forked from syntaxbullet/aurorabot
refactor: convert ModerationService and PruneService from classes to singleton objects
- Convert ModerationService class to moderationService singleton - Convert PruneService class to pruneService singleton - Update all command files to use new singleton imports - Update web routes to use new singleton imports - Update tests for singleton pattern - Remove getNextCaseId from tests (now private module function)
This commit is contained in:
@@ -10,34 +10,56 @@ export interface ModerationCaseConfig {
|
||||
autoTimeoutThreshold?: number;
|
||||
}
|
||||
|
||||
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)],
|
||||
});
|
||||
/**
|
||||
* Generate the next sequential case ID
|
||||
*/
|
||||
async function 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 || !match[1]) {
|
||||
return "CASE-0001";
|
||||
}
|
||||
|
||||
const nextNumber = parseInt(match[1], 10) + 1;
|
||||
return `CASE-${nextNumber.toString().padStart(4, '0')}`;
|
||||
if (!latestCase) {
|
||||
return "CASE-0001";
|
||||
}
|
||||
|
||||
// Extract number from case ID (e.g., "CASE-0042" -> 42)
|
||||
const match = latestCase.caseId.match(/CASE-(\d+)/);
|
||||
if (!match || !match[1]) {
|
||||
return "CASE-0001";
|
||||
}
|
||||
|
||||
const nextNumber = parseInt(match[1], 10) + 1;
|
||||
return `CASE-${nextNumber.toString().padStart(4, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active warnings for a user
|
||||
*/
|
||||
async function getUserWarnings(userId: string) {
|
||||
return await DrizzleClient.query.moderationCases.findMany({
|
||||
where: and(
|
||||
eq(moderationCases.userId, BigInt(userId)),
|
||||
eq(moderationCases.type, CaseType.WARN),
|
||||
eq(moderationCases.active, true)
|
||||
),
|
||||
orderBy: [desc(moderationCases.createdAt)],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count of active warnings for a user (useful for auto-timeout)
|
||||
*/
|
||||
async function getActiveWarningCount(userId: string): Promise<number> {
|
||||
const warnings = await getUserWarnings(userId);
|
||||
return warnings.length;
|
||||
}
|
||||
|
||||
export const moderationService = {
|
||||
/**
|
||||
* Create a new moderation case
|
||||
*/
|
||||
static async createCase(options: CreateCaseOptions) {
|
||||
const caseId = await this.getNextCaseId();
|
||||
async createCase(options: CreateCaseOptions) {
|
||||
const caseId = await getNextCaseId();
|
||||
|
||||
const [newCase] = await DrizzleClient.insert(moderationCases).values({
|
||||
caseId,
|
||||
@@ -52,12 +74,12 @@ export class ModerationService {
|
||||
}).returning();
|
||||
|
||||
return newCase;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Issue a warning with DM and threshold logic
|
||||
*/
|
||||
static async issueWarning(options: {
|
||||
async issueWarning(options: {
|
||||
userId: string;
|
||||
username: string;
|
||||
moderatorId: string;
|
||||
@@ -81,7 +103,7 @@ export class ModerationService {
|
||||
throw new Error("Failed to create moderation case");
|
||||
}
|
||||
|
||||
const warningCount = await this.getActiveWarningCount(options.userId);
|
||||
const warningCount = await getActiveWarningCount(options.userId);
|
||||
const config = options.config ?? {};
|
||||
|
||||
// Try to DM the user if configured
|
||||
@@ -127,21 +149,21 @@ export class ModerationService {
|
||||
}
|
||||
|
||||
return { moderationCase, warningCount, autoTimeoutIssued };
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a case by its case ID
|
||||
*/
|
||||
static async getCaseById(caseId: string) {
|
||||
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) {
|
||||
async getUserCases(userId: string, activeOnly: boolean = false) {
|
||||
const conditions = [eq(moderationCases.userId, BigInt(userId))];
|
||||
|
||||
if (activeOnly) {
|
||||
@@ -152,26 +174,19 @@ export class ModerationService {
|
||||
where: and(...conditions),
|
||||
orderBy: [desc(moderationCases.createdAt)],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get active warnings for a user
|
||||
* Get active warnings for a user (public alias)
|
||||
*/
|
||||
static async getUserWarnings(userId: string) {
|
||||
return await DrizzleClient.query.moderationCases.findMany({
|
||||
where: and(
|
||||
eq(moderationCases.userId, BigInt(userId)),
|
||||
eq(moderationCases.type, CaseType.WARN),
|
||||
eq(moderationCases.active, true)
|
||||
),
|
||||
orderBy: [desc(moderationCases.createdAt)],
|
||||
});
|
||||
}
|
||||
async getUserWarnings(userId: string) {
|
||||
return await getUserWarnings(userId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all notes for a user
|
||||
*/
|
||||
static async getUserNotes(userId: string) {
|
||||
async getUserNotes(userId: string) {
|
||||
return await DrizzleClient.query.moderationCases.findMany({
|
||||
where: and(
|
||||
eq(moderationCases.userId, BigInt(userId)),
|
||||
@@ -179,12 +194,12 @@ export class ModerationService {
|
||||
),
|
||||
orderBy: [desc(moderationCases.createdAt)],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear/resolve a warning
|
||||
*/
|
||||
static async clearCase(options: ClearCaseOptions) {
|
||||
async clearCase(options: ClearCaseOptions) {
|
||||
const [updatedCase] = await DrizzleClient.update(moderationCases)
|
||||
.set({
|
||||
active: false,
|
||||
@@ -196,12 +211,12 @@ export class ModerationService {
|
||||
.returning();
|
||||
|
||||
return updatedCase;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Search cases with various filters
|
||||
*/
|
||||
static async searchCases(filter: SearchCasesFilter) {
|
||||
async searchCases(filter: SearchCasesFilter) {
|
||||
const conditions = [];
|
||||
|
||||
if (filter.userId) {
|
||||
@@ -228,13 +243,12 @@ export class ModerationService {
|
||||
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;
|
||||
}
|
||||
}
|
||||
async getActiveWarningCount(userId: string): Promise<number> {
|
||||
return await getActiveWarningCount(userId);
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user