From 6e859902d0297d64c8a608ffae614751bec747b4 Mon Sep 17 00:00:00 2001 From: LightZirconite Date: Sat, 8 Nov 2025 12:24:43 +0100 Subject: [PATCH] feature: Integrate the SummaryReporter module to improve results reporting --- src/index.ts | 169 ++++++--------------------------------------------- 1 file changed, 18 insertions(+), 151 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8652f2f..07cae34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,8 +42,8 @@ import { Workers } from './functions/Workers' import { DesktopFlow } from './flows/DesktopFlow' import { MobileFlow } from './flows/MobileFlow' -// import { SummaryReporter } from './flows/SummaryReporter' // TODO: Integrate in Phase 3 -// import { BuyModeHandler } from './flows/BuyModeHandler' // TODO: Integrate in Phase 3 +import { SummaryReporter, type AccountResult } from './flows/SummaryReporter' +// import { BuyModeHandler } from './flows/BuyModeHandler' // TODO: Integrate later import { DISCORD, TIMEOUTS } from './constants' import { Account } from './interface/Account' @@ -896,145 +896,25 @@ export class MicrosoftRewardsBot { } private async sendConclusion(summaries: AccountSummary[]) { - const { ConclusionWebhook } = await import('./util/ConclusionWebhook') - const cfg = this.config + if (summaries.length === 0) return - const conclusionWebhookEnabled = !!(cfg.conclusionWebhook && cfg.conclusionWebhook.enabled) - const ntfyEnabled = !!(cfg.ntfy && cfg.ntfy.enabled) - const webhookEnabled = !!(cfg.webhook && cfg.webhook.enabled) + // Convert AccountSummary to AccountResult format + const accountResults: AccountResult[] = summaries.map(s => ({ + email: s.email, + pointsEarned: s.totalCollected, + runDuration: s.durationMs, + errors: s.errors.length > 0 ? s.errors : undefined + })) - const totalAccounts = summaries.length - if (totalAccounts === 0) return - - let totalCollected = 0 - let totalInitial = 0 - let totalEnd = 0 - let totalDuration = 0 - let accountsWithErrors = 0 - let successes = 0 - - // Calculate summary statistics - for (const s of summaries) { - totalCollected += s.totalCollected - totalInitial += s.initialTotal - totalEnd += s.endTotal - totalDuration += s.durationMs - if (s.errors.length) accountsWithErrors++ - else successes++ - } - - const avgDuration = totalDuration / totalAccounts - const avgPointsPerAccount = Math.round(totalCollected / totalAccounts) - - // Read package version - let version = 'unknown' - try { - const pkgPath = path.join(process.cwd(), 'package.json') - if (fs.existsSync(pkgPath)) { - const raw = fs.readFileSync(pkgPath, 'utf-8') - const pkg = JSON.parse(raw) - version = pkg.version || version - } - } catch { /* ignore */ } - - const formatNumber = (value: number) => value.toLocaleString() - const formatSigned = (value: number) => `${value >= 0 ? '+' : ''}${formatNumber(value)}` - const padText = (value: string, length: number) => { - if (value.length >= length) { - if (length <= 1) return value.slice(0, length) - return value.length === length ? value : `${value.slice(0, length - 1)}…` - } - return value + ' '.repeat(length - value.length) - } - const buildAccountLine = (summary: AccountSummary): string => { - const statusIcon = summary.banned?.status ? '🚫' : (summary.errors.length ? '⚠️' : '✅') - const email = padText(summary.email, 24) - const total = padText(`${formatSigned(summary.totalCollected)} pts`, 13) - const desktop = padText(`D:${formatSigned(summary.desktopCollected)}`, 13) - const mobile = padText(`M:${formatSigned(summary.mobileCollected)}`, 13) - const totals = padText(`${formatNumber(summary.initialTotal)}→${formatNumber(summary.endTotal)}`, 21) - const duration = padText(formatDuration(summary.durationMs), 10) - const extras: string[] = [] - if (summary.banned?.status) extras.push(`BAN:${summary.banned.reason || 'detected'}`) - if (summary.errors.length) extras.push(`ERR:${summary.errors.slice(0, 1).join(' | ')}`) - const tail = extras.length ? ` | ${extras.join(' • ')}` : '' - return `${statusIcon} ${email} ${total} ${desktop}${mobile} ${totals} ${duration}${tail}` - } - const chunkLines = (lines: string[], maxLen = 900): string[][] => { - const chunks: string[][] = [] - let current: string[] = [] - let currentLen = 0 - for (const line of lines) { - const nextLen = line.length + 1 - if (current.length > 0 && currentLen + nextLen > maxLen) { - chunks.push(current) - current = [] - currentLen = 0 - } - current.push(line) - currentLen += nextLen - } - if (current.length) chunks.push(current) - return chunks - } - - const accountLines = summaries.map(buildAccountLine) - const accountChunks = chunkLines(accountLines) - - const globalLines = [ - `Total points: **${formatNumber(totalInitial)}** → **${formatNumber(totalEnd)}** (${formatSigned(totalCollected)} pts)`, - `Accounts: ✅ ${successes}${accountsWithErrors > 0 ? ` • ⚠️ ${accountsWithErrors}` : ''} (${totalAccounts} total)`, - `Average per account: **${formatSigned(avgPointsPerAccount)} pts** • **${formatDuration(avgDuration)}**`, - `Runtime: **${formatDuration(totalDuration)}**` - ] - - const globalStatsValue = globalLines.join('\n') - - const fields: { name: string; value: string; inline?: boolean }[] = [ - { name: '📊 Summary', value: globalStatsValue, inline: false } - ] - - if (accountChunks.length === 0) { - fields.push({ name: '📋 Accounts', value: '_No results recorded_', inline: false }) - } else { - accountChunks.forEach((chunk, index) => { - const name = accountChunks.length === 1 ? '📋 Accounts' : `📋 Accounts (${index + 1}/${accountChunks.length})` - fields.push({ name, value: ['```', ...chunk, '```'].join('\n'), inline: false }) - }) - } - - // Send webhook - if (conclusionWebhookEnabled || ntfyEnabled || webhookEnabled) { - await ConclusionWebhook( - cfg, - '✅ Daily Run Complete', - `**v${version}** • ${this.runId}`, - fields, - accountsWithErrors > 0 ? DISCORD.COLOR_ORANGE : DISCORD.COLOR_GREEN - ) - } - - // Write local JSON report - try { - const fs = await import('fs') - const path = await import('path') - const now = new Date() - const day = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}` - const baseDir = path.join(process.cwd(), 'reports', day) - if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true }) - const file = path.join(baseDir, `summary_${this.runId}.json`) - const payload = { - runId: this.runId, - timestamp: now.toISOString(), - totals: { totalCollected, totalInitial, totalEnd, totalDuration, totalAccounts, accountsWithErrors }, - perAccount: summaries - } - fs.writeFileSync(file, JSON.stringify(payload, null, 2), 'utf-8') - log('main','REPORT',`Saved report to ${file}`) - } catch (e) { - log('main','REPORT',`Failed to save report: ${e instanceof Error ? e.message : e}`,'warn') - } + const startTime = new Date(Date.now() - summaries.reduce((sum, s) => sum + s.durationMs, 0)) + const endTime = new Date() + // Use SummaryReporter for modern reporting + const reporter = new SummaryReporter(this.config) + const summary = reporter.createSummary(accountResults, startTime, endTime) + + // Generate console output and send notifications (webhooks, ntfy, job state) + await reporter.generateReport(summary) } // Run optional auto-update script based on configuration flags. @@ -1134,19 +1014,6 @@ function formatFullError(label: string, e: unknown, verbose: boolean): string { return `${label}:${base}` } -function formatDuration(ms: number): string { - if (!ms || ms < 1000) return `${ms}ms` - const sec = Math.floor(ms / 1000) - const h = Math.floor(sec / 3600) - const m = Math.floor((sec % 3600) / 60) - const s = sec % 60 - const parts: string[] = [] - if (h) parts.push(`${h}h`) - if (m) parts.push(`${m}m`) - if (s) parts.push(`${s}s`) - return parts.join(' ') || `${ms}ms` -} - async function main(): Promise { // Check for dashboard mode flag (standalone dashboard) if (process.argv.includes('-dashboard')) {