#!/usr/bin/env node import { google } from 'googleapis'; import minimist from 'minimist'; import { getAuthClient, parseAccountArg } from './auth/auth-utils.js'; import { logAction } from './action-logger.js'; function decodeBody(body) { if (!body.data) return ''; return Buffer.from(body.data, 'base64').toString('utf-8'); } function extractBody(payload) { if (payload.body && payload.body.data) { return decodeBody(payload.body); } if (payload.parts) { // Try to find text/plain first, fall back to text/html const textPart = payload.parts.find(part => part.mimeType === 'text/plain'); if (textPart && textPart.body.data) { return decodeBody(textPart.body); } const htmlPart = payload.parts.find(part => part.mimeType === 'text/html'); if (htmlPart && htmlPart.body.data) { return decodeBody(htmlPart.body); } // Recursively search nested parts for (const part of payload.parts) { if (part.parts) { const body = extractBody(part); if (body) return body; } } } return ''; } async function readMessage(args) { if (!args.id) { throw new Error('Missing required argument: --id (message ID)'); } // Get authenticated client for specified account (or default) const accountId = parseAccountArg(args); const oauth2Client = await getAuthClient(accountId); const gmail = google.gmail({ version: 'v1', auth: oauth2Client }); // Get message details const message = await gmail.users.messages.get({ userId: 'me', id: args.id, format: 'full' }); const headers = message.data.payload.headers; const getHeader = (name) => headers.find(h => h.name === name)?.value || ''; const result = { success: true, id: message.data.id, threadId: message.data.threadId, from: getHeader('From'), to: getHeader('To'), cc: getHeader('Cc'), bcc: getHeader('Bcc'), subject: getHeader('Subject'), date: getHeader('Date'), labels: message.data.labelIds || [], snippet: message.data.snippet, body: extractBody(message.data.payload), account: accountId || 'default' }; // Optionally get thread if requested if (args.thread) { const thread = await gmail.users.threads.get({ userId: 'me', id: message.data.threadId }); result.thread = { id: thread.data.id, messageCount: thread.data.messages.length, messages: thread.data.messages.map(msg => ({ id: msg.id, snippet: msg.snippet, from: msg.payload.headers.find(h => h.name === 'From')?.value, date: msg.payload.headers.find(h => h.name === 'Date')?.value })) }; } return result; } // Main execution const args = minimist(process.argv.slice(2)); const startTime = Date.now(); readMessage(args) .then(result => { // Log the action logAction('read', args, result, { account: result.account, duration: Date.now() - startTime }); console.log(JSON.stringify(result, null, 2)); }) .catch(error => { const errorResult = { success: false, error: error.message }; // Log the failed action logAction('read', args, errorResult, { account: args.account || 'default', duration: Date.now() - startTime, failed: true }); console.error(JSON.stringify(errorResult, null, 2)); process.exit(1); });