Add stats, feeds, scheduler, and guild backup features to the bot

- Introduced new models in the Prisma schema for statistics, social feeds, scheduled messages, and guild backups, enhancing the bot's functionality.
- Implemented command modules for managing stats channels, feeds, scheduling messages, and creating/restoring backups, improving user engagement and server management.
- Updated job handling to include dedicated workers for stats updates, feed polling, scheduling, and backup restoration, enhancing automation.
- Enhanced command localization for new features in both German and English.
- Documented the new features and their setup processes in PHASE-TRACKING.md for clarity on implementation steps.
This commit is contained in:
smueller
2026-07-22 13:36:39 +02:00
parent 52b10c9536
commit 12066befa8
36 changed files with 4032 additions and 42 deletions

View File

@@ -15,6 +15,10 @@ import { expireSelfRole } from './modules/selfroles/service.js';
import { endGiveaway } from './modules/giveaways/index.js';
import { closeInactiveTickets } from './modules/tickets/index.js';
import { expireBirthdayRole, runBirthdayCheck } from './modules/birthdays/index.js';
import { ensureStatsRecurringJobs, startStatsWorker } from './modules/stats/index.js';
import { pollAllFeeds } from './modules/feeds/index.js';
import { sendScheduledMessage } from './modules/scheduler/index.js';
import { runGuildBackupRestoreJob } from './modules/guildbackup/index.js';
import {
automodQueue,
automodQueueName,
@@ -22,9 +26,13 @@ import {
backupQueueName,
birthdayQueue,
birthdayQueueName,
feedsQueue,
feedsQueueName,
giveawayQueueName,
guildBackupQueueName,
moderationQueueName,
reminderQueueName,
scheduleQueueName,
ticketQueue,
ticketQueueName,
verificationQueueName
@@ -213,6 +221,50 @@ export function startWorkers(context: BotContext): Worker[] {
logger.error({ jobId: job?.id, error }, 'Birthday job failed');
});
const statsWorker = startStatsWorker(context);
const feedsWorker = new Worker(
feedsQueueName,
async (job) => {
if (job.name !== 'pollFeeds') {
return;
}
await pollAllFeeds(context);
},
{ connection: redis }
);
feedsWorker.on('failed', (job, error) => {
logger.error({ jobId: job?.id, error }, 'Feeds job failed');
});
const scheduleWorker = new Worker<{ scheduleId: string }>(
scheduleQueueName,
async (job) => {
if (job.name !== 'scheduleSend') {
return;
}
await sendScheduledMessage(context, job.data.scheduleId);
},
{ connection: redis }
);
scheduleWorker.on('failed', (job, error) => {
logger.error({ jobId: job?.id, error }, 'Schedule job failed');
});
const guildBackupWorker = new Worker<{ backupId: string; guildId: string }>(
guildBackupQueueName,
async (job) => {
if (job.name !== 'restoreGuildBackup') {
return;
}
await runGuildBackupRestoreJob(context, job.data.backupId, job.data.guildId);
},
{ connection: redis }
);
guildBackupWorker.on('failed', (job, error) => {
logger.error({ jobId: job?.id, error }, 'Guild backup restore job failed');
});
return [
moderationWorker,
backupWorker,
@@ -221,7 +273,11 @@ export function startWorkers(context: BotContext): Worker[] {
reminderWorker,
ticketWorker,
giveawayWorker,
birthdayWorker
birthdayWorker,
statsWorker,
feedsWorker,
scheduleWorker,
guildBackupWorker
];
}
@@ -267,6 +323,18 @@ export async function ensureRecurringJobs(): Promise<void> {
removeOnFail: 20
}
);
await ensureStatsRecurringJobs();
await feedsQueue.add(
'pollFeeds',
{},
{
repeat: { pattern: '*/5 * * * *' },
removeOnComplete: 20,
removeOnFail: 20
}
);
}
async function runPgDumpBackup(): Promise<void> {