Files
d4rkbot/src/bot/weekly-recap.js
T
D4rkst3randClaude Opus 4.8 3c8f442d6c Wochen-Rückblick: sonntags 20:00 automatisch in den Devlog-Kanal
- Scheduler (5-Minuten-Check, dedupe über last_weekly_recap-Setting)
- Embed: Commits pro Tag als Balken-Grafik, Commit-/Devlog-Zahlen, Top-Projekte
- Setting weekly_recap_enabled + 'Rückblick jetzt testen'-Button auf der Setup-Seite
- TZ Europe/Berlin im Compose (Scheduler + Datumsformate)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:41:17 +02:00

88 lines
3.3 KiB
JavaScript

// Wochen-Rückblick: sonntags 20:00 (Container-TZ, siehe compose) postet der Bot
// eine Zusammenfassung der Woche in den Devlog-Kanal.
import { EmbedBuilder } from 'discord.js';
import { weeklyStats, getSetting, setSetting } from '../db.js';
import { devlogChannelId } from '../runtime-settings.js';
import { config } from '../config.js';
const BRAND_YELLOW = 0xf5c518;
const CHECK_INTERVAL_MS = 5 * 60 * 1000;
/** Balken-Zeile: `Mo 21.07 ████████ 12` */
function buildBars(perDay) {
const byDay = new Map(perDay.map((r) => [r.day, r.n]));
const rows = [];
let max = 1;
// Letzte 7 Tage, älteste zuerst
for (let i = 6; i >= 0; i--) {
const date = new Date(Date.now() - i * 86400000);
const key = date.toISOString().slice(0, 10);
const n = byDay.get(key) ?? 0;
max = Math.max(max, n);
rows.push({ date, n });
}
const dayFmt = new Intl.DateTimeFormat('de-DE', { weekday: 'short' });
const dateFmt = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit' });
return rows
.map((r) => {
const bar = '█'.repeat(Math.round((r.n / max) * 14)) || '·';
return `${dayFmt.format(r.date).padEnd(3)}${dateFmt.format(r.date)} ${bar} ${r.n}`;
})
.join('\n');
}
/** Rückblick-Embed bauen und in den Devlog-Kanal posten */
export async function postWeeklyRecap(client) {
const channelId = devlogChannelId();
if (!channelId) throw new Error('Kein Devlog-Kanal konfiguriert');
const channel = await client.channels.fetch(channelId);
if (!channel?.isTextBased()) throw new Error('Devlog-Kanal nicht gefunden');
const stats = weeklyStats();
const totalCommits = stats.perDay.reduce((sum, r) => sum + r.n, 0);
const repoLine = stats.topRepos
.map((r) => `**${r.repo.split('/').pop()}** (${r.n})`)
.join(' · ');
const embed = new EmbedBuilder()
.setColor(BRAND_YELLOW)
.setTitle('📊 Wochen-Rückblick')
.setURL(`${config.publicUrl}/devlogs`)
.setDescription(
`\`\`\`\n${buildBars(stats.perDay)}\n\`\`\``
)
.addFields(
{ name: 'Commits', value: String(totalCommits), inline: true },
{ name: 'Devlogs', value: String(stats.devlogs), inline: true },
...(repoLine ? [{ name: 'Aktive Projekte', value: repoLine, inline: false }] : [])
)
.setFooter({ text: 'D4RKST3R // WEEKLY' })
.setTimestamp();
await channel.send({ embeds: [embed] });
}
/** Scheduler starten: prüft alle 5 Minuten, ob Sonntag ≥ 20:00 und noch nicht gepostet */
export function scheduleWeeklyRecap(client) {
const check = async () => {
if (getSetting('weekly_recap_enabled') === '0') return;
const now = new Date();
if (now.getDay() !== 0 || now.getHours() < 20) return; // Sonntag ab 20:00 (lokale TZ)
const today = now.toISOString().slice(0, 10);
if (getSetting('last_weekly_recap') === today) return;
try {
await postWeeklyRecap(client);
setSetting('last_weekly_recap', today);
console.log('[weekly] Wochen-Rückblick gepostet');
} catch (error) {
console.error('[weekly] Rückblick fehlgeschlagen:', error);
}
};
setInterval(check, CHECK_INTERVAL_MS);
check();
}