Fetch Flight Email Details
Fetch full body content for specific email IDs
Source Code
import fs from "fs";
import path from "path";
const [idsPath, outputPath = "session/flight-emails.json"] =
process.argv.slice(2);
if (!idsPath) {
console.error("Error: idsPath is required");
process.exit(1);
}
// Read the IDs file
let emailIds;
try {
const idsContent = fs.readFileSync(idsPath, "utf-8");
const idsData = JSON.parse(idsContent);
emailIds = Array.isArray(idsData) ? idsData : idsData.ids || [];
} catch (e) {
console.error(`Error reading IDs file: ${e.message}`);
process.exit(1);
}
if (emailIds.length === 0) {
console.log("No email IDs to fetch");
const dir = path.dirname(outputPath);
if (dir && dir !== ".") fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(
outputPath,
JSON.stringify({ count: 0, emails: [] }, null, 2)
);
console.log(JSON.stringify({ success: true, outputPath, count: 0 }));
process.exit(0);
}
console.log(`Fetching full details for ${emailIds.length} emails...`);
/**
* Extract body text from MIME payload
*/
function extractBodyText(payload) {
if (payload.body?.data) {
return Buffer.from(payload.body.data, "base64").toString("utf-8");
}
if (payload.parts) {
for (const part of payload.parts) {
if (part.mimeType === "text/plain" && part.body?.data) {
return Buffer.from(part.body.data, "base64").toString("utf-8");
}
if (part.parts) {
for (const nested of part.parts) {
if (nested.mimeType === "text/plain" && nested.body?.data) {
return Buffer.from(nested.body.data, "base64").toString("utf-8");
}
}
}
}
// Try HTML if no plain text
for (const part of payload.parts) {
if (part.mimeType === "text/html" && part.body?.data) {
return Buffer.from(part.body.data, "base64").toString("utf-8");
}
}
}
return "";
}
function getHeader(msg, name) {
const h = msg.payload?.headers?.find(
(h) => h.name.toLowerCase() === name.toLowerCase()
);
return h ? h.value : "";
}
try {
const CONCURRENCY = 20;
const emails = [];
for (let i = 0; i < emailIds.length; i += CONCURRENCY) {
const batch = emailIds.slice(i, i + CONCURRENCY);
const fetched = await Promise.all(
batch.map(async (id) => {
const url = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${id}?format=full`;
const res = await fetch(url, {
headers: { Authorization: "Bearer PLACEHOLDER_TOKEN" },
});
if (!res.ok) return null;
return res.json();
})
);
for (const msg of fetched.filter(Boolean)) {
const bodyText = extractBodyText(msg.payload || {});
emails.push({
id: msg.id,
threadId: msg.threadId,
subject: getHeader(msg, "Subject"),
from: getHeader(msg, "From"),
to: getHeader(msg, "To"),
date: getHeader(msg, "Date"),
snippet: msg.snippet,
body: bodyText.slice(0, 5000), // Limit body to 5KB
});
}
console.log(
` Fetched ${Math.min(i + CONCURRENCY, emailIds.length)}/${emailIds.length}...`
);
}
// Sort by date
emails.sort((a, b) => new Date(a.date) - new Date(b.date));
// Write output
const dir = path.dirname(outputPath);
if (dir && dir !== ".") fs.mkdirSync(dir, { recursive: true });
const output = {
fetchedAt: new Date().toISOString(),
count: emails.length,
emails,
};
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
console.log(`\n✓ Fetched ${emails.length} email details`);
console.log(` Written to: ${outputPath}`);
console.log(
JSON.stringify({
success: true,
outputPath,
count: emails.length,
})
);
} catch (error) {
console.error("Error fetching emails:", error.message);
throw error;
}