Ship the receipt pipeline (Livin/Grab/BRI), PocketBase spendings, PM2 ecosystem, and a proper .gitignore that excludes secrets, SQLite data, and media dumps. Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
1.9 KiB
JavaScript
70 lines
1.9 KiB
JavaScript
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(/<style[\s\S]*?<\/style>/gi, "")
|
|
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
.replace(/<br\s*\/?>/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 || "",
|
|
};
|
|
}
|