UNPKG

trash-cleaner

Version:

Finds and deletes trash email in the mailbox

225 lines (198 loc) 6.79 kB
import ora, { Ora } from 'ora'; import chalk from 'chalk'; import { ProgressReporter } from './progress-reporter.js'; import { Email } from '../client/email-client.js'; /** * A progress reporter that prints the progress on console. */ class ConsoleProgressReporter extends ProgressReporter { private _quiet: boolean; private _spinner: Ora | undefined; private _dryRun: boolean = false; private _trashEmails: Email[] = []; private _unreadEmailCount: number = 0; /** * Creates an instance of ConsoleProgressReporter. */ constructor(cliMode: boolean, quiet: boolean = false) { super(); this._quiet = quiet; if (cliMode) { this._spinner = ora({ interval: 250 }); } } /** * An event that fires when cleaning has started. */ onStart(dryRun: boolean): void { if (this._spinner) { this._spinner.start('Starting cleaning...'); } this._dryRun = dryRun; this._trashEmails = []; this._unreadEmailCount = 0; } /** * An event that fires when unread emails are being retrieved. */ onRetrievingUnreadEmails(): void { this._update('Retrieving emails...'); } /** * An event that fires when unread emails are retrieved. */ onUnreadEmailsRetrieved(emails: Email[]): void { this._unreadEmailCount = emails.length; this._update(`Retrieved ${emails.length} emails.`); } /** * An event that fires when trash emails are identified. */ onTrashEmailsIdentified(emails: Email[]): void { this._trashEmails = emails; this._update(`Found ${emails.length} trash emails.`); } /** * An event that fires when evaluating an email against rules. */ onEvaluatingEmail(current: number, total: number): void { this._update(`Evaluating emails... (${current}/${total})`); } /** * An event that fires when starting batched LLM evaluation. */ onEvaluatingLlm(count: number): void { this._update(`Classifying ${count} email(s) with LLM...`); } /** * An event that fires when trash emails are being deleted. */ onDeletingTrash(): void { this._update('Deleting trash emails...'); } /** * An event that fires when trash emails are deleted. */ onTrashDeleted(): void { this._update(`Trash emails${this._dryRun ? ' not' : ''} deleted.`); } /** * An event that fires when processing an action on emails. */ onProcessingAction(action: string, count: number): void { const verb = action === 'delete' ? 'Deleting' : action === 'archive' ? 'Archiving' : 'Marking as read'; this._update(`${verb} ${count} email(s)...`); } /** * An event that fires when an action is complete. */ onActionComplete(action: string, count: number): void { const verb = action === 'delete' ? 'Deleted' : action === 'archive' ? 'Archived' : 'Marked as read'; const status = this._dryRun ? ` (dry-run, not ${action === 'mark-as-read' ? 'marked' : action + 'd'})` : ''; this._update(`${verb} ${count} email(s)${status}.`); } /** * An event that fires when cleaning has stopped. */ onStop(): void { if (this._spinner) { this._spinner.stop(); } this._logTrashEmails(); this._printSummary(); } /** * Stops the spinner without printing summary output. */ onStopSpinner(): void { if (this._spinner) { this._spinner.stop(); } } /** * Prints the summary of the cleanup operation. */ private _printSummary(): void { if (this._quiet) { if (this._trashEmails.length > 0) { this._log(`Processed ${this._trashEmails.length} trash emails out of ${this._unreadEmailCount} unread${this._dryRun ? ' (dry-run)' : ''}`); } return; } this._log(''); this._log(`Total unread emails: ${this._unreadEmailCount}`); this._log(`Total trash emails: ${this._trashEmails.length}`); if (this._trashEmails.length > 0) { const actionCounts: Record<string, number> = {}; for (const email of this._trashEmails) { const action = email._action || 'delete'; actionCounts[action] = (actionCounts[action] || 0) + 1; } this._log(''); this._log(chalk.bold('Breakdown by action:')); for (const [action, count] of Object.entries(actionCounts)) { const verb = this._dryRun ? 'would be ' : ''; const label = action === 'delete' ? `${verb}deleted` : action === 'archive' ? `${verb}archived` : `${verb}marked as read`; this._log(` ${this._colorAction(action)}: ${count} ${label}`); } } if (this._dryRun) { this._log(''); this._log('Dry-run mode: no actions were performed.'); } } /** * Logs the trash emails to console. */ private _logTrashEmails(): void { if (this._quiet || this._trashEmails.length === 0) { return; } this._trashEmails.forEach(this._logEmail.bind(this)); } /** * Shows an update message. */ private _update(message: string): void { if (this._spinner) { this._spinner.text = message; } } /** * Logs the key properties of an email to the console. */ private _logEmail(email: Email): void { const action = email._action || 'delete'; this._log(`${chalk.bold('Action:')} ${this._colorAction(action)}`); if (email._rule) { this._log(`${chalk.bold('Rule:')} ${email._rule}`); } this._log(`${chalk.bold('From:')} ${email.from}`); this._log(`${chalk.bold('Labels:')} ${email.labels}`); this._log(`${chalk.bold('Subject:')} ${email.subject}`); this._log(`${chalk.dim('Snippet:')} ${chalk.dim(email.snippet)}`); this._log(chalk.gray('-'.repeat(60))); } /** * Returns a color-coded action label. */ private _colorAction(action: string): string { switch (action) { case 'delete': return chalk.red(action); case 'archive': return chalk.yellow(action); case 'mark-as-read': return chalk.blue(action); default: return action; } } /** * Logs the message to console. */ private _log(message: string): void { console.log(message); } } export { ConsoleProgressReporter };