← back to AbramsOS
lib/gmail-fetcher.js
72 lines
const { google } = require('googleapis');
const { clientFor } = require('./google-oauth');
const RECEIPT_QUERY = 'newer_than:90d (receipt OR order OR invoice OR confirmation OR shipped OR delivered OR purchase) -category:promotions';
function gmailClient(refreshToken) {
return google.gmail({ version: 'v1', auth: clientFor(refreshToken) });
}
async function listReceiptIds(refreshToken, { query = RECEIPT_QUERY, max = 200 } = {}) {
const gmail = gmailClient(refreshToken);
const ids = [];
let pageToken;
do {
const resp = await gmail.users.messages.list({
userId: 'me',
q: query,
maxResults: Math.min(100, max - ids.length),
pageToken,
});
for (const m of resp.data.messages || []) ids.push(m.id);
pageToken = resp.data.nextPageToken;
} while (pageToken && ids.length < max);
return ids;
}
async function getMessage(refreshToken, id) {
const gmail = gmailClient(refreshToken);
const resp = await gmail.users.messages.get({ userId: 'me', id, format: 'full' });
return resp.data;
}
function header(payload, name) {
const h = (payload.headers || []).find((x) => x.name.toLowerCase() === name.toLowerCase());
return h ? h.value : null;
}
function decodeBase64Url(data) {
if (!data) return '';
return Buffer.from(data.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
}
function walkParts(payload, out = { text: '', html: '', attachments: [] }) {
if (!payload) return out;
const mime = payload.mimeType || '';
if (mime === 'text/plain' && payload.body?.data) out.text += decodeBase64Url(payload.body.data);
else if (mime === 'text/html' && payload.body?.data) out.html += decodeBase64Url(payload.body.data);
else if (payload.body?.attachmentId && payload.filename) {
out.attachments.push({
attachmentId: payload.body.attachmentId,
filename: payload.filename,
mime,
size: payload.body.size || 0,
});
}
for (const p of payload.parts || []) walkParts(p, out);
return out;
}
function summarize(message) {
const headers = {
subject: header(message.payload, 'Subject'),
from: header(message.payload, 'From'),
to: header(message.payload, 'To'),
date: header(message.payload, 'Date'),
};
const body = walkParts(message.payload);
return { id: message.id, threadId: message.threadId, headers, body, raw: message };
}
module.exports = { listReceiptIds, getMessage, summarize, RECEIPT_QUERY };