UNPKG

rashi-discord-bot-lib

Version:

πŸš€ Powerful Discord bot framework with built-in database, event handling, and utilities

363 lines β€’ 17.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotStatistics = void 0; const cli_table3_1 = __importDefault(require("cli-table3")); const Logger_1 = require("./Logger"); async function loadChalk() { try { const imp = new Function('m', 'return import(m)'); const mod = await imp('chalk'); const chalk = mod.default ?? mod; return { bold: { blue: (s) => chalk.bold.blue(s), green: (s) => chalk.bold.green(s), magenta: (s) => chalk.bold.magenta(s), yellow: (s) => chalk.bold.yellow(s), cyan: (s) => chalk.bold.cyan(s), red: (s) => chalk.bold.red(s), }, blue: (s) => chalk.blue(s), green: (s) => chalk.green(s), magenta: (s) => chalk.magenta(s), cyan: (s) => chalk.cyan(s), yellow: (s) => chalk.yellow(s), red: (s) => chalk.red(s), white: (s) => chalk.white(s), }; } catch { const id = (x) => x; return { bold: { blue: id, green: id, magenta: id, yellow: id, cyan: id, red: id }, blue: id, green: id, magenta: id, cyan: id, yellow: id, red: id, white: id, }; } } /* -------------------------------------------------------------------------- */ /* CLASS */ /* -------------------------------------------------------------------------- */ class BotStatistics { musicStats; logger; startTime; commandUsage; eventTriggers; printedOnce = false; // ⬅️ nowoΕ›Δ‡ totalCommandsExecuted = 0; totalEventsTriggered = 0; responseTimes = []; errorCount = 0; totalRequests = 0; // Liczniki zaΕ‚adowanych elementΓ³w (nie wykonanych) loadedSlashCommands = 0; loadedPrefixCommands = 0; loadedEvents = 0; // Pojedyncze staty bazy (wsteczna kompatybilnoΕ›Δ‡) dbStats; // NOWE: lista store’ów (Verse + Mongo + co chcesz) dbStores = []; constructor(logger) { this.logger = logger ?? new Logger_1.Logger(); this.startTime = new Date(); this.commandUsage = new Map(); this.eventTriggers = new Map(); } /* ------------ DB: SETTERY ------------- */ // Stare API – dla zgodnoΕ›ci setDatabaseStats(stats) { this.dbStats = stats; } setMusicStats(stats) { this.musicStats = stats; } // Nowe API – wiele store’ów setDatabaseStores(stores) { this.dbStores = stores ?? []; } resetPrintedFlag() { this.printedOnce = false; } // Wygodny helper dla VerseDB async loadDatabaseStatsFrom(db) { if (!db) { this.dbStats = undefined; return; } try { const stats = await db.getStats?.(); if (!stats) { this.dbStats = undefined; return; } this.dbStats = { keys: stats.keys ?? 0, size: stats.size ?? 0, adapter: stats.adapter ?? 'unknown', }; } catch { this.dbStats = undefined; } } /* ------------- LIFECYCLE -------------- */ initialize(client) { this.setupEventTracking(client); } setLoadedCommands(slashCount, prefixCount) { this.loadedSlashCommands = slashCount; this.loadedPrefixCommands = prefixCount; } setLoadedEvents(eventCount) { this.loadedEvents = eventCount; } /* ------------- TRACKING --------------- */ trackCommand(commandName, type, responseTime) { const current = this.commandUsage.get(commandName) ?? { count: 0, type }; this.commandUsage.set(commandName, { count: current.count + 1, type }); this.totalCommandsExecuted++; this.totalRequests++; if (typeof responseTime === 'number') { this.responseTimes.push(responseTime); if (this.responseTimes.length > 100) this.responseTimes.shift(); } } trackEvent(eventName) { const current = this.eventTriggers.get(eventName) ?? 0; this.eventTriggers.set(eventName, current + 1); this.totalEventsTriggered++; } trackError() { this.errorCount++; this.totalRequests++; } /* ------------- RENDER ----------------- */ async generateTableStats(client, options, databaseStats) { if (this.printedOnce) return; // ⬅️ blokada this.printedOnce = true; // ⬅️ ustaw po wejΕ›ciu const chalk = await loadChalk(); const dbStats = databaseStats ?? this.dbStats; // fallback (single) try { const botInfoTable = new cli_table3_1.default({ head: [chalk.bold.blue('Bot Information'), chalk.bold.green('Value')], colWidths: [25, 40], style: { head: ['cyan'], border: ['grey'] }, }); const statsTable = new cli_table3_1.default({ head: [chalk.bold.magenta('Statistics'), chalk.bold.cyan('Count')], colWidths: [25, 40], style: { head: ['magenta'], border: ['grey'] }, }); // NOWE: tabela wielu store’ów const databaseTable = new cli_table3_1.default({ head: [chalk.bold.green('Store'), chalk.bold.blue('Adapter'), chalk.bold.cyan('Details')], colWidths: [18, 18, 58], style: { head: ['green'], border: ['grey'] }, }); const uptime = this.formatUptime(Date.now() - this.startTime.getTime()); const mem = process.memoryUsage(); const rawPing = Math.round(client.ws.ping); const pingText = rawPing >= 0 ? `${rawPing}ms` : 'N/A'; // 🎢 MUSIC table const musicTable = new cli_table3_1.default({ head: [chalk.bold.green('Music'), chalk.bold.blue('Value')], colWidths: [25, 55], style: { head: ['green'], border: ['grey'] }, }); // πŸ”§ PATCH: MusicManager integration for Shoukaku (fixes "Disabled / Not initialized") const music = client.music; if (!this.musicStats && music?.getNodesStatus) { try { const nodes = music.getNodesStatus(); const connected = nodes.filter((n) => n.state === 'CONNECTED').length; const total = music?.config?.nodes?.length ?? 0; this.setMusicStats({ enabled: true, library: music.kazagumo ? 'Kazagumo + Shoukaku' : 'Shoukaku', nodesConnected: connected, nodesTotal: total, defaultSearch: music?.config?.defaultSearchEngine ?? 'unknown', }); } catch (err) { this.setMusicStats({ enabled: true, library: 'Shoukaku', }); } } if (this.musicStats) { const ms = this.musicStats; musicTable.push([chalk.green('Status'), chalk.white(ms.enabled ? 'Enabled' : 'Disabled')], ...(ms.library ? [[chalk.green('Library'), chalk.cyan(ms.library)]] : []), ...(ms.defaultSearch ? [[chalk.green('Default Search'), chalk.cyan(ms.defaultSearch)]] : []), ...((ms.nodesConnected != null || ms.nodesTotal != null) ? [[chalk.green('Nodes'), chalk.white(`${ms.nodesConnected ?? 0}/${ms.nodesTotal ?? 0}`)]] : []), ...(ms.players != null ? [[chalk.green('Players'), chalk.white(String(ms.players))]] : []), ...(ms.playing != null ? [[chalk.green('Playing'), chalk.white(String(ms.playing))]] : [])); } else { musicTable.push([chalk.green('Status'), chalk.white('Disabled / Not initialized')]); } botInfoTable.push([chalk.cyan('Name'), chalk.white(client.user?.username ?? 'Unknown')], [chalk.cyan('ID'), chalk.white(client.user?.id ?? 'Unknown')], [chalk.cyan('Tag'), chalk.white(client.user?.tag ?? 'Unknown')], [chalk.cyan('Uptime'), chalk.green(uptime)], [chalk.cyan('Started At'), chalk.white(this.startTime.toLocaleString())], [chalk.cyan('Node.js'), chalk.white(process.version)]); statsTable.push([chalk.magenta('Guilds'), chalk.yellow(String(client.guilds.cache.size))], [chalk.magenta('Users'), chalk.yellow(String(client.users.cache.size))], [chalk.magenta('Channels'), chalk.yellow(String(client.channels.cache.size))], [chalk.magenta('Events Loaded'), chalk.green(String(this.loadedEvents))], [chalk.magenta('Events Triggered'), chalk.blue(String(this.totalEventsTriggered))], [chalk.magenta('Slash Commands'), chalk.cyan(String(this.loadedSlashCommands))], [chalk.magenta('Prefix Commands'), chalk.cyan(String(this.loadedPrefixCommands))], [chalk.magenta('Commands Executed'), chalk.red(String(this.totalCommandsExecuted))]); // Render listy store’ów (preferowane) if (this.dbStores.length) { for (const s of this.dbStores) { const details = [ `status: ${s.status === 'connected' ? 'Connected' : 'Down'}`, s.keys != null ? `keys: ${s.keys}` : null, s.size != null ? `size: ${s.size} bytes` : null, s.info ?? null, ].filter(Boolean).join(' β€’ '); databaseTable.push([chalk.white(s.name), chalk.cyan(s.adapter), chalk.white(details)]); } } else if (dbStats) { // Fallback: pojedyncze staty (stare API) const details = [ 'status: Connected', dbStats.keys != null ? `keys: ${dbStats.keys}` : null, dbStats.size != null ? `size: ${dbStats.size} bytes` : null, ].filter(Boolean).join(' β€’ '); databaseTable.push([chalk.white('Database'), chalk.cyan(dbStats.adapter), chalk.white(details)]); } else { databaseTable.push([chalk.white('Database'), '-', chalk.red('status: Not connected')]); } if (options?.BotInfo) { const settingsTable = new cli_table3_1.default({ head: [chalk.bold.green('Settings'), chalk.bold.blue('Value')], colWidths: [25, 55], style: { head: ['green'], border: ['grey'] }, }); const bi = options.BotInfo; if (bi.ownerId) settingsTable.push([chalk.green('Bot Owner'), chalk.white(`<@${bi.ownerId}>`)]); if (bi.botInvite) settingsTable.push([chalk.green('Bot Invite'), chalk.blue(bi.botInvite)]); if (bi.serverInvite) settingsTable.push([chalk.green('Server Invite'), chalk.blue(bi.serverInvite)]); if (dbStats) settingsTable.push([chalk.green('Database'), chalk.cyan(`${dbStats.adapter} (${dbStats.keys} keys)`)]); if (databaseStats) settingsTable.push([chalk.green('Database'), chalk.cyan(`${databaseStats.adapter} (${databaseStats.keys} keys)`)]); console.log(chalk.green('\nβš™οΈ Bot Settings & Configuration:\n')); console.log(settingsTable.toString()); } const mostUsedCommand = this.getMostUsedCommand(); if (mostUsedCommand) { const commandTable = new cli_table3_1.default({ head: [chalk.bold.cyan('Most Used Command'), chalk.bold.yellow('Details')], colWidths: [25, 40], style: { head: ['cyan'], border: ['grey'] }, }); commandTable.push([chalk.cyan('Command Name'), chalk.white(mostUsedCommand.name)], [chalk.cyan('Type'), chalk.yellow(mostUsedCommand.type)], [chalk.cyan('Uses'), chalk.red(String(mostUsedCommand.uses))]); console.log(chalk.cyan('\nπŸ† Command Champion:\n')); console.log(commandTable.toString()); } console.log(chalk.blue('\nπŸ€– Bot Information:\n')); console.log(botInfoTable.toString()); console.log(chalk.magenta('\nπŸ“Š Statistics Overview:\n')); console.log(statsTable.toString()); console.log(chalk.cyan('\n🎢 Music:\n')); console.log(musicTable.toString()); const i18nTable = new cli_table3_1.default({ head: [chalk.bold.blue('🌐 i18n (Translation System)'), chalk.bold.green('Value')], colWidths: [30, 50], style: { head: ['blue'], border: ['grey'] }, }); console.log(chalk.blue('\n🌐 Translation System (i18n):\n')); console.log(i18nTable.toString()); console.log(chalk.green('\nπŸ—„οΈ Database:\n')); console.log(databaseTable.toString()); } catch (err) { this.logger.warn('cli-table3/chalk not available, falling back to simple display'); console.log(await this.generateStatsDisplay(client)); } } async generateStatsDisplay(client) { const uptime = this.formatUptime(Date.now() - this.startTime.getTime()); const mem = process.memoryUsage(); return ` ╔══════════════════════════════════════╗ β•‘ BOT STATISTICS β•‘ ╠══════════════════════════════════════╣ β•‘ Bot Information β•‘ β•‘ β€’ Name: ${(client.user?.username ?? 'Unknown').padEnd(28)}β•‘ β•‘ β€’ Uptime: ${uptime.padEnd(25)}β•‘ β•‘ β€’ Guilds: ${String(client.guilds.cache.size).padEnd(26)}β•‘ β•‘ β€’ Users: ${String(client.users.cache.size).padEnd(27)}β•‘ β•‘ β€’ Channels: ${String(client.channels.cache.size).padEnd(24)}β•‘ ╠══════════════════════════════════════╣ β•‘ Commands & Events β•‘ β•‘ β€’ Slash Commands: ${String(this.loadedSlashCommands).padEnd(18)}β•‘ β•‘ β€’ Prefix Commands: ${String(this.loadedPrefixCommands).padEnd(17)}β•‘ β•‘ β€’ Events Loaded: ${String(this.loadedEvents).padEnd(19)}β•‘ β•‘ β€’ Commands Executed: ${String(this.totalCommandsExecuted).padEnd(15)}β•‘ ╠══════════════════════════════════════╣ β•‘ Performance β•‘ β•‘ β€’ Memory: ${this.formatBytes(mem.heapUsed).padEnd(23)}β•‘ β•‘ β€’ API Latency: ${String(Math.round(client.ws.ping)).padEnd(19)}msβ•‘ β•‘ β€’ Error Rate: ${this.getErrorRate().toFixed(1).padEnd(20)}%β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• `.trim(); } /* ------------- HELPERY ---------------- */ get loadedEventsCount() { return this.loadedEvents; } get loadedSlashCommandsCount() { return this.loadedSlashCommands; } get loadedPrefixCommandsCount() { return this.loadedPrefixCommands; } setupEventTracking(client) { const originalEmit = client.emit.bind(client); client.emit = (event, ...args) => { this.trackEvent(event); return originalEmit(event, ...args); }; } getMostUsedCommand() { let mostUsed = null; let maxUses = 0; this.commandUsage.forEach((data, name) => { if (data.count > maxUses) { maxUses = data.count; mostUsed = { name, uses: data.count, type: data.type }; } }); return mostUsed; } getAverageResponseTime() { if (this.responseTimes.length === 0) return 0; const sum = this.responseTimes.reduce((a, b) => a + b, 0); return Math.round(sum / this.responseTimes.length); } getErrorRate() { if (this.totalRequests === 0) return 0; return (this.errorCount / this.totalRequests) * 100; } formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); const val = (bytes / Math.pow(k, i)).toFixed(2); return `${val} ${sizes[i]}`; } formatUptime(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`; if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`; if (minutes > 0) return `${minutes}m ${seconds % 60}s`; return `${seconds}s`; } } exports.BotStatistics = BotStatistics; //# sourceMappingURL=BotStatistics.js.map