import { DISPLAY_NAMES } from '../config/configuration'; import { UmamiStats } from '../umami/umami.service'; export function formatNumber(num: number): string { return num.toLocaleString('de-DE'); } export function formatChange(change: number): string { if (change === 0) return 'β†’'; const sign = change > 0 ? '+' : ''; return `${sign}${Math.round(change)}%`; } export function formatChangeEmoji(change: number): string { if (change > 10) return 'πŸ“ˆ'; if (change > 0) return 'β†—'; if (change < -10) return 'πŸ“‰'; if (change < 0) return 'β†˜'; return 'β†’'; } export function getDisplayName(websiteKey: string): string { return DISPLAY_NAMES[websiteKey] || websiteKey; } export function formatDate(date: Date, format: 'short' | 'long' = 'short'): string { const options: Intl.DateTimeFormatOptions = format === 'short' ? { day: 'numeric', month: 'numeric', year: 'numeric' } : { day: 'numeric', month: 'long', year: 'numeric' }; return date.toLocaleDateString('de-DE', options); } export function formatWeekNumber(date: Date): string { const startOfYear = new Date(date.getFullYear(), 0, 1); const days = Math.floor((date.getTime() - startOfYear.getTime()) / (24 * 60 * 60 * 1000)); const weekNumber = Math.ceil((days + startOfYear.getDay() + 1) / 7); return `KW ${weekNumber}`; } export function formatDailyReport(stats: Map, date: Date): string { const lines: string[] = [ 'πŸ“Š ManaCore Daily Report', '━━━━━━━━━━━━━━━━━━━━', '', `πŸ“… ${formatDate(date, 'long')}`, '', 'πŸ“ˆ Besucher heute:', ]; // Sort by visitors (descending) const sortedStats = Array.from(stats.entries()) .filter(([key]) => key.endsWith('-webapp')) .sort((a, b) => b[1].visitors.value - a[1].visitors.value); let totalVisitors = 0; let totalPageviews = 0; for (const [key, stat] of sortedStats) { const name = getDisplayName(key).padEnd(12); const visitors = stat.visitors.value; const change = formatChange(stat.visitors.change); const emoji = formatChangeEmoji(stat.visitors.change); totalVisitors += visitors; totalPageviews += stat.pageviews.value; lines.push(` ${name}: ${formatNumber(visitors)} (${change}) ${emoji}`); } lines.push(''); lines.push(`πŸ“„ Pageviews: ${formatNumber(totalPageviews)}`); lines.push(`πŸ‘₯ Besucher gesamt: ${formatNumber(totalVisitors)}`); return lines.join('\n'); } export function formatWeeklyReport( stats: Map, weekStart: Date, weekEnd: Date, prevStats?: Map ): string { const lines: string[] = [ 'πŸ“Š ManaCore Weekly Report', '━━━━━━━━━━━━━━━━━━━━', '', `πŸ“… ${formatWeekNumber(weekStart)} (${formatDate(weekStart)} - ${formatDate(weekEnd)})`, '', ' Besucher Pageviews', ]; // Sort by visitors (descending) const sortedStats = Array.from(stats.entries()) .filter(([key]) => key.endsWith('-webapp')) .sort((a, b) => b[1].visitors.value - a[1].visitors.value); let totalVisitors = 0; let totalPageviews = 0; for (const [key, stat] of sortedStats) { const name = getDisplayName(key).padEnd(12); const visitors = formatNumber(stat.visitors.value).padStart(6); const pageviews = formatNumber(stat.pageviews.value).padStart(9); totalVisitors += stat.visitors.value; totalPageviews += stat.pageviews.value; lines.push(`${name}: ${visitors} ${pageviews}`); } lines.push('────────────────────────────'); lines.push( `Total: ${formatNumber(totalVisitors).padStart(6)} ${formatNumber(totalPageviews).padStart(9)}` ); // Calculate week-over-week change if previous stats available if (prevStats) { let prevTotal = 0; for (const [key, stat] of prevStats.entries()) { if (key.endsWith('-webapp')) { prevTotal += stat.visitors.value; } } if (prevTotal > 0) { const change = ((totalVisitors - prevTotal) / prevTotal) * 100; lines.push(''); lines.push(`πŸ“Š vs. Vorwoche: ${formatChange(change)} ${formatChangeEmoji(change)}`); } } return lines.join('\n'); } export function formatRealtimeReport(activeVisitors: Map): string { const lines: string[] = ['πŸ”΄ Realtime - Aktive Besucher', '━━━━━━━━━━━━━━━━━━━━', '']; // Sort by active visitors (descending) const sortedVisitors = Array.from(activeVisitors.entries()) .filter(([key]) => key.endsWith('-webapp')) .sort((a, b) => b[1] - a[1]); let total = 0; for (const [key, count] of sortedVisitors) { const name = getDisplayName(key).padEnd(12); total += count; const indicator = count > 0 ? '🟒' : 'βšͺ'; lines.push(`${indicator} ${name}: ${count}`); } lines.push(''); lines.push(`πŸ‘₯ Gesamt aktiv: ${total}`); return lines.join('\n'); } export function formatStatsOverview(stats: Map): string { const lines: string[] = ['πŸ“Š ManaCore Stats Übersicht', '━━━━━━━━━━━━━━━━━━━━', '']; // Group by type const webapps = Array.from(stats.entries()) .filter(([key]) => key.endsWith('-webapp')) .sort((a, b) => b[1].visitors.value - a[1].visitors.value); const landings = Array.from(stats.entries()) .filter(([key]) => key.endsWith('-landing')) .sort((a, b) => b[1].visitors.value - a[1].visitors.value); lines.push('🌐 Web Apps:'); for (const [key, stat] of webapps) { const name = getDisplayName(key).padEnd(12); lines.push(` ${name}: ${formatNumber(stat.visitors.value)} visitors`); } if (landings.length > 0) { lines.push(''); lines.push('🏠 Landing Pages:'); for (const [key, stat] of landings) { const name = getDisplayName(key).padEnd(12); lines.push(` ${name}: ${formatNumber(stat.visitors.value)} visitors`); } } return lines.join('\n'); } export function formatHelpMessage(): string { return `πŸ€– ManaCore Stats Bot ━━━━━━━━━━━━━━━━━━━━ VerfΓΌgbare Befehle: /stats - Übersicht aller Apps /today - Heutige Statistiken /week - Wochenstatistiken /realtime - Aktive Besucher jetzt /users - Registrierte User /help - Diese Hilfe anzeigen πŸ“… Automatische Reports: β€’ Daily: Jeden Tag um 9:00 β€’ Weekly: Jeden Montag um 9:00`; } export interface DailyRegistration { date: string; count: number; } export interface UserStats { totalUsers: number; verifiedUsers: number; todayNewUsers: number; yesterdayNewUsers: number; weekNewUsers: number; lastWeekNewUsers: number; monthNewUsers: number; dailyRegistrations: DailyRegistration[]; } function createMiniBarChart(dailyRegistrations: DailyRegistration[]): string[] { if (dailyRegistrations.length === 0) return []; const maxCount = Math.max(...dailyRegistrations.map((d) => d.count), 1); const barChars = ['▁', 'β–‚', 'β–ƒ', 'β–„', 'β–…', 'β–†', 'β–‡', 'β–ˆ']; // Fill in missing days and sort const last7Days: DailyRegistration[] = []; for (let i = 6; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); const dateStr = date.toISOString().split('T')[0]; const found = dailyRegistrations.find((d) => d.date === dateStr); last7Days.push({ date: dateStr, count: found?.count || 0 }); } const bars = last7Days.map((d) => { const index = Math.floor((d.count / maxCount) * (barChars.length - 1)); return barChars[Math.max(0, index)]; }); const dayLabels = last7Days.map((d) => { const date = new Date(d.date); return ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'][date.getDay()]; }); return [`${bars.join('')}`, `${dayLabels.join('')}`]; } export function formatUsersReport(stats: UserStats): string { const verificationRate = stats.totalUsers > 0 ? Math.round((stats.verifiedUsers / stats.totalUsers) * 100) : 0; // Calculate trends const dailyTrend = stats.yesterdayNewUsers > 0 ? ((stats.todayNewUsers - stats.yesterdayNewUsers) / stats.yesterdayNewUsers) * 100 : stats.todayNewUsers > 0 ? 100 : 0; const weeklyTrend = stats.lastWeekNewUsers > 0 ? ((stats.weekNewUsers - stats.lastWeekNewUsers) / stats.lastWeekNewUsers) * 100 : stats.weekNewUsers > 0 ? 100 : 0; const lines: string[] = [ 'πŸ‘₯ ManaCore User Statistics', '━━━━━━━━━━━━━━━━━━━━', '', 'πŸ“Š Übersicht', ` πŸ‘€ Gesamt: ${formatNumber(stats.totalUsers)}`, ` βœ… Verifiziert: ${formatNumber(stats.verifiedUsers)} (${verificationRate}%)`, '', 'πŸ“ˆ Neue Registrierungen', ` Heute: +${formatNumber(stats.todayNewUsers)} ${formatChangeEmoji(dailyTrend)}`, ` Gestern: +${formatNumber(stats.yesterdayNewUsers)}`, ` Diese Woche: +${formatNumber(stats.weekNewUsers)} ${formatChange(weeklyTrend)} ${formatChangeEmoji(weeklyTrend)}`, ` Dieser Monat: +${formatNumber(stats.monthNewUsers)}`, ]; // Add mini bar chart for last 7 days if (stats.dailyRegistrations.length > 0) { lines.push(''); lines.push('πŸ“… Letzte 7 Tage'); lines.push(...createMiniBarChart(stats.dailyRegistrations)); } return lines.join('\n'); } export function formatUsersReportCompact(stats: UserStats): string { const verificationRate = stats.totalUsers > 0 ? Math.round((stats.verifiedUsers / stats.totalUsers) * 100) : 0; return [ '', 'πŸ‘₯ Registrierte User', ` Gesamt: ${formatNumber(stats.totalUsers)} (${verificationRate}% verifiziert)`, ` Heute: +${formatNumber(stats.todayNewUsers)} | Woche: +${formatNumber(stats.weekNewUsers)} | Monat: +${formatNumber(stats.monthNewUsers)}`, ].join('\n'); }