Refactor giveaway job processing and update environment documentation

- Improved the `giveawayCreate` job handling in the bot's job processing, ensuring consistent functionality with existing commands.
- Enhanced the `.env.example` file to provide clearer instructions on the usage of `BOT_TOKEN` for both the bot and WebUI.
- Added the `bullmq` dependency to the WebUI package for better job management capabilities.
This commit is contained in:
smueller
2026-07-22 15:22:56 +02:00
parent 677142672f
commit 429f974bfc
11 changed files with 669 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
import type {
TicketCategoryDashboard,
TicketCategoryDashboardCreate,
TicketCategoryDashboardUpdate,
TicketConfigDashboard,
TicketConfigDashboardPatch
} from '@nexumi/shared';
import type { TicketCategory } from '@prisma/client';
import { prisma } from '../prisma';
async function ensureTicketConfig(guildId: string) {
await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
return prisma.ticketConfig.upsert({ where: { guildId }, update: {}, create: { guildId } });
}
function toConfigDashboard(config: Awaited<ReturnType<typeof ensureTicketConfig>>): TicketConfigDashboard {
return {
enabled: config.enabled,
mode: config.mode as TicketConfigDashboard['mode'],
categoryId: config.categoryId ?? '',
logChannelId: config.logChannelId ?? '',
transcriptDm: config.transcriptDm,
inactivityHours: config.inactivityHours,
ratingEnabled: config.ratingEnabled
};
}
export async function getTicketConfigDashboard(guildId: string): Promise<TicketConfigDashboard> {
const config = await ensureTicketConfig(guildId);
return toConfigDashboard(config);
}
export async function updateTicketConfigDashboard(
guildId: string,
patch: TicketConfigDashboardPatch
): Promise<TicketConfigDashboard> {
await ensureTicketConfig(guildId);
const { categoryId, logChannelId, ...rest } = patch;
const updated = await prisma.ticketConfig.update({
where: { guildId },
data: {
...rest,
...(categoryId !== undefined ? { categoryId: categoryId === '' ? null : categoryId } : {}),
...(logChannelId !== undefined ? { logChannelId: logChannelId === '' ? null : logChannelId } : {})
}
});
return toConfigDashboard(updated);
}
function toCategoryDashboard(category: TicketCategory): TicketCategoryDashboard {
return {
id: category.id,
name: category.name,
description: category.description,
emoji: category.emoji,
supportRoleIds: category.supportRoleIds
};
}
export async function listTicketCategories(guildId: string): Promise<TicketCategoryDashboard[]> {
const categories = await prisma.ticketCategory.findMany({
where: { guildId },
orderBy: { createdAt: 'asc' }
});
return categories.map(toCategoryDashboard);
}
export class TicketCategoryConflictError extends Error {
constructor() {
super('A ticket category with this name already exists');
}
}
export async function createTicketCategory(
guildId: string,
input: TicketCategoryDashboardCreate
): Promise<TicketCategoryDashboard> {
await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
try {
const category = await prisma.ticketCategory.create({
data: {
guildId,
name: input.name,
description: input.description ?? null,
emoji: input.emoji ?? null,
supportRoleIds: input.supportRoleIds
}
});
return toCategoryDashboard(category);
} catch (error) {
if (error && typeof error === 'object' && 'code' in error && error.code === 'P2002') {
throw new TicketCategoryConflictError();
}
throw error;
}
}
export async function updateTicketCategory(
guildId: string,
categoryId: string,
patch: TicketCategoryDashboardUpdate
): Promise<TicketCategoryDashboard | null> {
const existing = await prisma.ticketCategory.findFirst({ where: { id: categoryId, guildId } });
if (!existing) {
return null;
}
try {
const updated = await prisma.ticketCategory.update({
where: { id: categoryId },
data: {
...(patch.name !== undefined ? { name: patch.name } : {}),
...(patch.description !== undefined ? { description: patch.description } : {}),
...(patch.emoji !== undefined ? { emoji: patch.emoji } : {}),
...(patch.supportRoleIds !== undefined ? { supportRoleIds: patch.supportRoleIds } : {})
}
});
return toCategoryDashboard(updated);
} catch (error) {
if (error && typeof error === 'object' && 'code' in error && error.code === 'P2002') {
throw new TicketCategoryConflictError();
}
throw error;
}
}
export async function deleteTicketCategory(guildId: string, categoryId: string): Promise<boolean> {
const result = await prisma.ticketCategory.deleteMany({ where: { id: categoryId, guildId } });
return result.count > 0;
}