function decodeBase64Url(data) { if (!data) return ""; const pad = data.length % 4 === 0 ? "" : "=".repeat(4 - (data.length % 4)); return Buffer.from( data.replace(/-/g, "+").replace(/_/g, "/") + pad, "base64", ).toString("utf8"); } function stripHtml(html) { return html .replace(//gi, "") .replace(//gi, "") .replace(//gi, "\n") .replace(/<\/p>/gi, "\n\n") .replace(/<\/div>/gi, "\n") .replace(/<\/tr>/gi, "\n") .replace(/<[^>]+>/g, "") .replace(/ /g, " ") .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, '"') .replace(/'/g, "'") .replace(/'/gi, "'") .replace(/\n{3,}/g, "\n\n") .trim(); } function walkParts(payload, acc = { text: "", html: "" }) { if (!payload) return acc; const mime = payload.mimeType || ""; const data = payload.body?.data; if (data) { if (mime === "text/plain") acc.text += decodeBase64Url(data); else if (mime === "text/html") acc.html += decodeBase64Url(data); } for (const part of payload.parts || []) { walkParts(part, acc); } return acc; } export function extractBody(payload) { const { text, html } = walkParts(payload); if (text.trim()) return text.trim(); if (html.trim()) return stripHtml(html); return ""; } export function header(payload, name) { const match = payload?.headers?.find( (h) => h.name?.toLowerCase() === name.toLowerCase(), ); return match?.value || ""; } export function summarizeMessage(message) { const payload = message.payload || {}; return { id: message.id, threadId: message.threadId, from: header(payload, "From"), to: header(payload, "To"), subject: header(payload, "Subject") || "(no subject)", date: header(payload, "Date"), snippet: message.snippet || "", }; }