import type { EmailDraft } from "./types.js"; /** * Convert a draft to RFC 5322 EML format with MIME multipart/alternative. */ export function draftToEml(draft: EmailDraft, from: string): string { const boundary = `----=_Part_${Date.now()}_${Math.random().toString(36).slice(2)}`; const date = new Date().toUTCString(); const messageId = `<${Date.now()}.${Math.random().toString(36).slice(2)}@outreach.local>`; const toHeader = draft.recipient_name ? `"${draft.recipient_name}" <${draft.recipient_email}>` : draft.recipient_email; const headers = [ `From: ${from}`, `To: ${toHeader}`, `Subject: ${encodeSubject(draft.subject)}`, `Date: ${date}`, `Message-ID: ${messageId}`, `MIME-Version: 1.0`, `Content-Type: multipart/alternative; boundary="${boundary}"`, `X-Mailer: email-export-skill`, ]; const textPart = [ `--${boundary}`, `Content-Type: text/plain; charset="utf-8"`, `Content-Transfer-Encoding: quoted-printable`, ``, quotedPrintableEncode(draft.body_text), ].join("\r\n"); const htmlPart = [ `--${boundary}`, `Content-Type: text/html; charset="utf-8"`, `Content-Transfer-Encoding: quoted-printable`, ``, quotedPrintableEncode(draft.body_html), ].join("\r\n"); return [ headers.join("\r\n"), ``, textPart, ``, htmlPart, ``, `--${boundary}--`, ``, ].join("\r\n"); } /** RFC 2047 encoded-word for non-ASCII subjects */ function encodeSubject(subject: string): string { if (/^[\x20-\x7E]*$/.test(subject)) return subject; const encoded = Buffer.from(subject, "utf-8").toString("base64"); return `=?UTF-8?B?${encoded}?=`; } /** Simple quoted-printable encoding */ function quotedPrintableEncode(text: string): string { const lines: string[] = []; let current = ""; for (const char of text) { const code = char.charCodeAt(0); let encoded: string; if (char === "\r" || char === "\n") { lines.push(current); current = ""; continue; } else if (code === 9 || (code >= 32 && code <= 126 && char !== "=")) { encoded = char; } else { const buf = Buffer.from(char, "utf-8"); encoded = Array.from(buf).map(b => `=${b.toString(16).toUpperCase().padStart(2, "0")}`).join(""); } if (current.length + encoded.length > 75) { lines.push(current + "="); current = encoded; } else { current += encoded; } } if (current) lines.push(current); return lines.join("\r\n"); } /** Generate a safe filename from draft */ export function emlFilename(draft: EmailDraft, index: number): string { const domain = draft.recipient_email.split("@")[1] || "unknown"; const safe = domain.replace(/[^a-zA-Z0-9.-]/g, "_"); return `${String(index + 1).padStart(3, "0")}_${safe}.eml`; }