Refactor giveaway job processing and enhance environment documentation
- Improved handling of the `giveawayCreate` job in the bot's job processing for better consistency. - Updated `.env.example` to clarify the usage of `BOT_TOKEN` for both the bot and WebUI. - Added `bullmq` dependency to the WebUI package for enhanced job management capabilities.
This commit is contained in:
111
apps/webui/src/lib/module-configs/scheduler.ts
Normal file
111
apps/webui/src/lib/module-configs/scheduler.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { ScheduledMessageDashboard, ScheduledMessageDashboardCreate } from '@nexumi/shared';
|
||||
import type { ScheduledMessage } from '@prisma/client';
|
||||
import { prisma } from '../prisma';
|
||||
import { scheduleQueue } from '../queues';
|
||||
|
||||
export class SchedulerValidationError extends Error {}
|
||||
|
||||
function isValidCron(pattern: string): boolean {
|
||||
const parts = pattern.trim().split(/\s+/);
|
||||
return parts.length >= 5 && parts.length <= 6;
|
||||
}
|
||||
|
||||
function toDashboard(schedule: ScheduledMessage): ScheduledMessageDashboard {
|
||||
return {
|
||||
id: schedule.id,
|
||||
channelId: schedule.channelId,
|
||||
creatorId: schedule.creatorId,
|
||||
content: schedule.content,
|
||||
rolePingId: schedule.rolePingId,
|
||||
cron: schedule.cron,
|
||||
runAt: schedule.runAt?.toISOString() ?? null,
|
||||
enabled: schedule.enabled,
|
||||
createdAt: schedule.createdAt.toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
export async function listSchedules(guildId: string): Promise<ScheduledMessageDashboard[]> {
|
||||
const schedules = await prisma.scheduledMessage.findMany({
|
||||
where: { guildId, enabled: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
return schedules.map(toDashboard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scheduled message and enqueues the exact `scheduleSend` BullMQ
|
||||
* job the bot's `/schedule create` command would enqueue (same queue, job
|
||||
* name and delay/repeat semantics) - see
|
||||
* `apps/bot/src/modules/scheduler/service.ts#enqueueScheduleJob`. The bot's
|
||||
* existing `scheduleQueueName` worker sends the message, so no bot-side
|
||||
* changes are required.
|
||||
*/
|
||||
export async function createSchedule(
|
||||
guildId: string,
|
||||
creatorId: string,
|
||||
input: ScheduledMessageDashboardCreate
|
||||
): Promise<ScheduledMessageDashboard> {
|
||||
await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
|
||||
|
||||
const cron = input.cron?.trim() || null;
|
||||
if (cron && !isValidCron(cron)) {
|
||||
throw new SchedulerValidationError('Invalid cron expression (expects 5-6 space separated fields)');
|
||||
}
|
||||
|
||||
const runAt = !cron && input.runAt ? new Date(input.runAt) : null;
|
||||
if (!cron && runAt && runAt.getTime() <= Date.now()) {
|
||||
throw new SchedulerValidationError('runAt must be in the future');
|
||||
}
|
||||
|
||||
const schedule = await prisma.scheduledMessage.create({
|
||||
data: {
|
||||
guildId,
|
||||
channelId: input.channelId,
|
||||
creatorId,
|
||||
content: input.content?.trim() || null,
|
||||
rolePingId: input.rolePingId ? input.rolePingId : null,
|
||||
cron,
|
||||
runAt
|
||||
}
|
||||
});
|
||||
|
||||
const jobOptions: {
|
||||
jobId: string;
|
||||
removeOnComplete: boolean;
|
||||
removeOnFail: number;
|
||||
delay?: number;
|
||||
repeat?: { pattern: string };
|
||||
} = {
|
||||
jobId: `schedule-${schedule.id}`,
|
||||
removeOnComplete: true,
|
||||
removeOnFail: 50
|
||||
};
|
||||
if (cron) {
|
||||
jobOptions.repeat = { pattern: cron };
|
||||
} else if (runAt) {
|
||||
jobOptions.delay = Math.max(0, runAt.getTime() - Date.now());
|
||||
}
|
||||
|
||||
const job = await scheduleQueue.add('scheduleSend', { scheduleId: schedule.id }, jobOptions);
|
||||
const updated = await prisma.scheduledMessage.update({
|
||||
where: { id: schedule.id },
|
||||
data: { jobId: String(job.id) }
|
||||
});
|
||||
|
||||
return toDashboard(updated);
|
||||
}
|
||||
|
||||
export async function deleteSchedule(guildId: string, scheduleId: string): Promise<boolean> {
|
||||
const schedule = await prisma.scheduledMessage.findFirst({ where: { id: scheduleId, guildId } });
|
||||
if (!schedule) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (schedule.jobId) {
|
||||
const job = await scheduleQueue.getJob(schedule.jobId);
|
||||
await job?.remove();
|
||||
}
|
||||
|
||||
await prisma.scheduledMessage.delete({ where: { id: scheduleId } });
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user