forked from syntaxbullet/AuroraBot-discord
159 lines
4.9 KiB
TypeScript
159 lines
4.9 KiB
TypeScript
import { moderationCases } from "@/db/schema";
|
|
import { eq, and, desc } from "drizzle-orm";
|
|
import { DrizzleClient } from "@/lib/DrizzleClient";
|
|
import type { CreateCaseOptions, ClearCaseOptions, SearchCasesFilter, CaseType } from "./moderation.types";
|
|
|
|
export class ModerationService {
|
|
/**
|
|
* Generate the next sequential case ID
|
|
*/
|
|
private static async getNextCaseId(): Promise<string> {
|
|
const latestCase = await DrizzleClient.query.moderationCases.findFirst({
|
|
orderBy: [desc(moderationCases.id)],
|
|
});
|
|
|
|
if (!latestCase) {
|
|
return "CASE-0001";
|
|
}
|
|
|
|
// Extract number from case ID (e.g., "CASE-0042" -> 42)
|
|
const match = latestCase.caseId.match(/CASE-(\d+)/);
|
|
if (!match) {
|
|
return "CASE-0001";
|
|
}
|
|
|
|
const nextNumber = parseInt(match[1], 10) + 1;
|
|
return `CASE-${nextNumber.toString().padStart(4, '0')}`;
|
|
}
|
|
|
|
/**
|
|
* Create a new moderation case
|
|
*/
|
|
static async createCase(options: CreateCaseOptions) {
|
|
const caseId = await this.getNextCaseId();
|
|
|
|
const [newCase] = await DrizzleClient.insert(moderationCases).values({
|
|
caseId,
|
|
type: options.type,
|
|
userId: BigInt(options.userId),
|
|
username: options.username,
|
|
moderatorId: BigInt(options.moderatorId),
|
|
moderatorName: options.moderatorName,
|
|
reason: options.reason,
|
|
metadata: options.metadata || {},
|
|
active: options.type === 'warn' ? true : false, // Only warnings are "active" by default
|
|
}).returning();
|
|
|
|
return newCase;
|
|
}
|
|
|
|
/**
|
|
* Get a case by its case ID
|
|
*/
|
|
static async getCaseById(caseId: string) {
|
|
return await DrizzleClient.query.moderationCases.findFirst({
|
|
where: eq(moderationCases.caseId, caseId),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get all cases for a specific user
|
|
*/
|
|
static async getUserCases(userId: string, activeOnly: boolean = false) {
|
|
const conditions = [eq(moderationCases.userId, BigInt(userId))];
|
|
|
|
if (activeOnly) {
|
|
conditions.push(eq(moderationCases.active, true));
|
|
}
|
|
|
|
return await DrizzleClient.query.moderationCases.findMany({
|
|
where: and(...conditions),
|
|
orderBy: [desc(moderationCases.createdAt)],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get active warnings for a user
|
|
*/
|
|
static async getUserWarnings(userId: string) {
|
|
return await DrizzleClient.query.moderationCases.findMany({
|
|
where: and(
|
|
eq(moderationCases.userId, BigInt(userId)),
|
|
eq(moderationCases.type, 'warn'),
|
|
eq(moderationCases.active, true)
|
|
),
|
|
orderBy: [desc(moderationCases.createdAt)],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get all notes for a user
|
|
*/
|
|
static async getUserNotes(userId: string) {
|
|
return await DrizzleClient.query.moderationCases.findMany({
|
|
where: and(
|
|
eq(moderationCases.userId, BigInt(userId)),
|
|
eq(moderationCases.type, 'note')
|
|
),
|
|
orderBy: [desc(moderationCases.createdAt)],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Clear/resolve a warning
|
|
*/
|
|
static async clearCase(options: ClearCaseOptions) {
|
|
const [updatedCase] = await DrizzleClient.update(moderationCases)
|
|
.set({
|
|
active: false,
|
|
resolvedAt: new Date(),
|
|
resolvedBy: BigInt(options.clearedBy),
|
|
resolvedReason: options.reason || 'Manually cleared',
|
|
})
|
|
.where(eq(moderationCases.caseId, options.caseId))
|
|
.returning();
|
|
|
|
return updatedCase;
|
|
}
|
|
|
|
/**
|
|
* Search cases with various filters
|
|
*/
|
|
static async searchCases(filter: SearchCasesFilter) {
|
|
const conditions = [];
|
|
|
|
if (filter.userId) {
|
|
conditions.push(eq(moderationCases.userId, BigInt(filter.userId)));
|
|
}
|
|
|
|
if (filter.moderatorId) {
|
|
conditions.push(eq(moderationCases.moderatorId, BigInt(filter.moderatorId)));
|
|
}
|
|
|
|
if (filter.type) {
|
|
conditions.push(eq(moderationCases.type, filter.type));
|
|
}
|
|
|
|
if (filter.active !== undefined) {
|
|
conditions.push(eq(moderationCases.active, filter.active));
|
|
}
|
|
|
|
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
|
|
return await DrizzleClient.query.moderationCases.findMany({
|
|
where: whereClause,
|
|
orderBy: [desc(moderationCases.createdAt)],
|
|
limit: filter.limit || 50,
|
|
offset: filter.offset || 0,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get total count of active warnings for a user (useful for auto-timeout)
|
|
*/
|
|
static async getActiveWarningCount(userId: string): Promise<number> {
|
|
const warnings = await this.getUserWarnings(userId);
|
|
return warnings.length;
|
|
}
|
|
}
|