import fs from 'node:fs/promises' import path from 'node:path' export interface BotLogEntry { id: string timestamp: string platform: 'discord' | 'dingtalk' | 'lark' | 'telegram' | 'slack' eventType: 'connection' | 'disconnection' | 'message_received' | 'message_sent' | 'error' severity: 'info' | 'warning' | 'error' message: string details?: Record } interface LogQueryOptions { limit?: number offset?: number platform?: 'discord' | 'dingtalk' | 'lark' | 'telegram' | 'slack' eventType?: string severity?: 'info' | 'warning' | 'error' } export class BotLogger { private static logs: BotLogEntry[] = [] private static maxLogs = 1000 private static logDir = path.join(process.cwd(), 'remote-control', 'data', 'logs') private static idCounter = 0 static log(entry: Omit): void { const logEntry: BotLogEntry = { id: `log_${++this.idCounter}_${Date.now()}`, timestamp: new Date().toISOString(), ...entry, } this.logs.unshift(logEntry) if (this.logs.length > this.maxLogs) { this.logs = this.logs.slice(0, this.maxLogs) } this.writeToFile(logEntry).catch(() => {}) } static getLogs(options: LogQueryOptions = {}): { logs: BotLogEntry[]; total: number } { let filtered = [...this.logs] if (options.platform) { filtered = filtered.filter(l => l.platform === options.platform) } if (options.eventType) { filtered = filtered.filter(l => l.eventType === options.eventType) } if (options.severity) { filtered = filtered.filter(l => l.severity === options.severity) } const total = filtered.length const offset = options.offset || 0 const limit = options.limit || 100 const logs = filtered.slice(offset, offset + limit) return { logs, total } } static clear(): void { this.logs = [] } private static async writeToFile(entry: BotLogEntry): Promise { await fs.mkdir(this.logDir, { recursive: true }) const date = new Date().toISOString().split('T')[0] const logFile = path.join(this.logDir, `bot-${date}.log`) const line = JSON.stringify(entry) + '\n' await fs.appendFile(logFile, line, 'utf-8') } }