style: format code for improved readability and consistency across multiple files
This commit is contained in:
@@ -107,7 +107,9 @@ async function skipAgeRestrictedMessages(
|
|||||||
getModerationBroadcaster()?.messageAnalyzed(row);
|
getModerationBroadcaster()?.messageAnalyzed(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
const skippedIds = new Set(ageRestrictedMessages.map((message) => message.id));
|
const skippedIds = new Set(
|
||||||
|
ageRestrictedMessages.map((message) => message.id),
|
||||||
|
);
|
||||||
return messages.filter((message) => !skippedIds.has(message.id));
|
return messages.filter((message) => !skippedIds.has(message.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,7 +800,10 @@ export async function queueMessageAnalysis(messageId: string): Promise<void> {
|
|||||||
if (updated) {
|
if (updated) {
|
||||||
getModerationBroadcaster()?.messageAnalyzed(updated);
|
getModerationBroadcaster()?.messageAnalyzed(updated);
|
||||||
}
|
}
|
||||||
logger.info({ messageId }, "Skipped AI analysis for age-restricted message");
|
logger.info(
|
||||||
|
{ messageId },
|
||||||
|
"Skipped AI analysis for age-restricted message",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -902,7 +907,8 @@ export function startPendingAIAnalysisWorker(client?: Client): void {
|
|||||||
config.AI_ANALYSIS_INDIVIDUAL_MAX_CONCURRENT,
|
config.AI_ANALYSIS_INDIVIDUAL_MAX_CONCURRENT,
|
||||||
)
|
)
|
||||||
.then(async (msgs) => {
|
.then(async (msgs) => {
|
||||||
const processableMessages = await skipAgeRestrictedMessages(msgs);
|
const processableMessages =
|
||||||
|
await skipAgeRestrictedMessages(msgs);
|
||||||
return processableMessages;
|
return processableMessages;
|
||||||
})
|
})
|
||||||
.then((msgs) => {
|
.then((msgs) => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Client, PermissionString } from "discord.js-selfbot-v13";
|
import type { Client, PermissionString } from "discord.js-selfbot-v13";
|
||||||
import { config } from "../config.js";
|
import { config } from "../config.js";
|
||||||
import { createChildLogger } from "../logger.js";
|
import { createChildLogger } from "../logger.js";
|
||||||
import type { MessageRecord } from "./types.js";
|
|
||||||
import { createModerationAction } from "./messageStore.js";
|
import { createModerationAction } from "./messageStore.js";
|
||||||
|
import type { MessageRecord } from "./types.js";
|
||||||
|
|
||||||
const logger = createChildLogger("auto-delete-manager");
|
const logger = createChildLogger("auto-delete-manager");
|
||||||
|
|
||||||
@@ -183,9 +183,7 @@ function isAlreadyDeletedError(error: unknown): boolean {
|
|||||||
return code === 10008 || code === 404 || code === "10008" || code === "404";
|
return code === 10008 || code === 404 || code === "10008" || code === "404";
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasChannelMessagesApi(
|
function hasChannelMessagesApi(channel: unknown): channel is {
|
||||||
channel: unknown,
|
|
||||||
): channel is {
|
|
||||||
messages: {
|
messages: {
|
||||||
fetch: (id: string) => Promise<{ delete: () => Promise<unknown> }>;
|
fetch: (id: string) => Promise<{ delete: () => Promise<unknown> }>;
|
||||||
};
|
};
|
||||||
@@ -200,9 +198,7 @@ function hasChannelMessagesApi(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPermissionApi(
|
function hasPermissionApi(channel: unknown): channel is {
|
||||||
channel: unknown,
|
|
||||||
): channel is {
|
|
||||||
permissionsFor: (
|
permissionsFor: (
|
||||||
member: unknown,
|
member: unknown,
|
||||||
) => { has: (permission: string) => boolean } | null;
|
) => { has: (permission: string) => boolean } | null;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import OpenAI from "openai";
|
import OpenAI from "openai";
|
||||||
import { config } from "../config.js";
|
import { config } from "../config.js";
|
||||||
import { INDONESIAN_SLANG_LEXICON } from "./resources/indonesianSlangLexicon.js";
|
|
||||||
import { createChildLogger } from "../logger.js";
|
import { createChildLogger } from "../logger.js";
|
||||||
import { retryWithBackoff } from "../retry.js";
|
import { retryWithBackoff } from "../retry.js";
|
||||||
|
import { INDONESIAN_SLANG_LEXICON } from "./resources/indonesianSlangLexicon.js";
|
||||||
|
|
||||||
const log = createChildLogger("indonesianTextNormalizer");
|
const log = createChildLogger("indonesianTextNormalizer");
|
||||||
|
|
||||||
@@ -259,7 +259,10 @@ function getPrimaryModerationClient(): OpenAI | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizePrimaryAiFlag(value: string): string | null {
|
function normalizePrimaryAiFlag(value: string): string | null {
|
||||||
const lower = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
const lower = value
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[\s-]+/g, "_");
|
||||||
if (!lower) return null;
|
if (!lower) return null;
|
||||||
|
|
||||||
if (VALID_PRIMARY_AI_FLAGS.has(lower)) {
|
if (VALID_PRIMARY_AI_FLAGS.has(lower)) {
|
||||||
@@ -337,7 +340,7 @@ async function callPrimaryAiModeration(text: string): Promise<string[]> {
|
|||||||
role: "user",
|
role: "user",
|
||||||
content:
|
content:
|
||||||
"Deteksi kata kasar / pelanggaran ringan dari teks Indonesia berikut. " +
|
"Deteksi kata kasar / pelanggaran ringan dari teks Indonesia berikut. " +
|
||||||
"Balas hanya JSON object dengan format {\"flags\":[...]} dan gunakan hanya flag valid ini: " +
|
'Balas hanya JSON object dengan format {"flags":[...]} dan gunakan hanya flag valid ini: ' +
|
||||||
Array.from(VALID_PRIMARY_AI_FLAGS).join(", ") +
|
Array.from(VALID_PRIMARY_AI_FLAGS).join(", ") +
|
||||||
". Jika tidak ada pelanggaran, flags harus array kosong. Teks: " +
|
". Jika tidak ada pelanggaran, flags harus array kosong. Teks: " +
|
||||||
text,
|
text,
|
||||||
@@ -478,9 +481,12 @@ export async function detectIndonesianBadwords(
|
|||||||
hits.add(hit);
|
hits.add(hit);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const status = axios.isAxiosError(error) ? error.response?.status : null;
|
const status = axios.isAxiosError(error)
|
||||||
|
? error.response?.status
|
||||||
|
: null;
|
||||||
if (status === 429) {
|
if (status === 429) {
|
||||||
nemotronUnavailableUntil = Date.now() + NEMOTRON_RATE_LIMIT_COOLDOWN_MS;
|
nemotronUnavailableUntil =
|
||||||
|
Date.now() + NEMOTRON_RATE_LIMIT_COOLDOWN_MS;
|
||||||
}
|
}
|
||||||
log.warn(
|
log.warn(
|
||||||
{ error },
|
{ error },
|
||||||
@@ -497,7 +503,9 @@ export async function detectIndonesianBadwords(
|
|||||||
hits.add(hit);
|
hits.add(hit);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const status = axios.isAxiosError(error) ? error.response?.status : null;
|
const status = axios.isAxiosError(error)
|
||||||
|
? error.response?.status
|
||||||
|
: null;
|
||||||
if (status === 429) {
|
if (status === 429) {
|
||||||
primaryAiUnavailableUntil =
|
primaryAiUnavailableUntil =
|
||||||
Date.now() + PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS;
|
Date.now() + PRIMARY_AI_RATE_LIMIT_COOLDOWN_MS;
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import { createChildLogger } from "../logger.js";
|
|||||||
import { retryWithBackoff } from "../retry.js";
|
import { retryWithBackoff } from "../retry.js";
|
||||||
import { formatModerationTextEvidenceForPrompt } from "./indonesianTextNormalizer.js";
|
import { formatModerationTextEvidenceForPrompt } from "./indonesianTextNormalizer.js";
|
||||||
import { extractMessageMediaEvidence } from "./messageMetadata.js";
|
import { extractMessageMediaEvidence } from "./messageMetadata.js";
|
||||||
import {
|
|
||||||
buildStickerTextOnlyWarning,
|
|
||||||
buildStickerVisionPrompt,
|
|
||||||
} from "./stickerPrompt.js";
|
|
||||||
import {
|
import {
|
||||||
getStickerFromCache,
|
getStickerFromCache,
|
||||||
initStickerCache,
|
initStickerCache,
|
||||||
isStickerCacheReady,
|
isStickerCacheReady,
|
||||||
setStickerInCache,
|
setStickerInCache,
|
||||||
} from "./stickerCache.js";
|
} from "./stickerCache.js";
|
||||||
|
import {
|
||||||
|
buildStickerTextOnlyWarning,
|
||||||
|
buildStickerVisionPrompt,
|
||||||
|
} from "./stickerPrompt.js";
|
||||||
import type {
|
import type {
|
||||||
AnalysisResult,
|
AnalysisResult,
|
||||||
AttachmentRecord,
|
AttachmentRecord,
|
||||||
@@ -244,7 +244,12 @@ export function parseModerationResponse(
|
|||||||
return (
|
return (
|
||||||
Array.isArray(val) &&
|
Array.isArray(val) &&
|
||||||
val.length > 0 &&
|
val.length > 0 &&
|
||||||
val.every((item: unknown) => typeof item === "object" && item !== null && "message_id" in (item as any))
|
val.every(
|
||||||
|
(item: unknown) =>
|
||||||
|
typeof item === "object" &&
|
||||||
|
item !== null &&
|
||||||
|
"message_id" in (item as any),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if (arrayKey) {
|
if (arrayKey) {
|
||||||
@@ -311,7 +316,7 @@ export function parseModerationResponse(
|
|||||||
flags: flags ?? [],
|
flags: flags ?? [],
|
||||||
score: normalizedScore,
|
score: normalizedScore,
|
||||||
analysis: coalescedAnalysis,
|
analysis: coalescedAnalysis,
|
||||||
categories: categories ?? (flags ?? []),
|
categories: categories ?? flags ?? [],
|
||||||
severity: normalizedSeverity,
|
severity: normalizedSeverity,
|
||||||
confidence: normalizedConfidence,
|
confidence: normalizedConfidence,
|
||||||
recommendedAction:
|
recommendedAction:
|
||||||
|
|||||||
@@ -87,7 +87,10 @@ export function getMessageLocation(message: Message): MessageLocation {
|
|||||||
threadId: null,
|
threadId: null,
|
||||||
threadName: null,
|
threadName: null,
|
||||||
channelName: "name" in channel ? channel.name : null,
|
channelName: "name" in channel ? channel.name : null,
|
||||||
nsfw: typeof safetyChannel.nsfw === "boolean" ? safetyChannel.nsfw : undefined,
|
nsfw:
|
||||||
|
typeof safetyChannel.nsfw === "boolean"
|
||||||
|
? safetyChannel.nsfw
|
||||||
|
: undefined,
|
||||||
nsfwLevel:
|
nsfwLevel:
|
||||||
typeof safetyChannel.nsfwLevel === "string"
|
typeof safetyChannel.nsfwLevel === "string"
|
||||||
? safetyChannel.nsfwLevel
|
? safetyChannel.nsfwLevel
|
||||||
@@ -104,7 +107,8 @@ export function getMessageLocation(message: Message): MessageLocation {
|
|||||||
threadId: channel.id,
|
threadId: channel.id,
|
||||||
threadName: channel.name,
|
threadName: channel.name,
|
||||||
channelName: channel.parent?.name ?? null,
|
channelName: channel.parent?.name ?? null,
|
||||||
nsfw: typeof safetyChannel.nsfw === "boolean" ? safetyChannel.nsfw : undefined,
|
nsfw:
|
||||||
|
typeof safetyChannel.nsfw === "boolean" ? safetyChannel.nsfw : undefined,
|
||||||
nsfwLevel:
|
nsfwLevel:
|
||||||
typeof safetyChannel.nsfwLevel === "string"
|
typeof safetyChannel.nsfwLevel === "string"
|
||||||
? safetyChannel.nsfwLevel
|
? safetyChannel.nsfwLevel
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { and, eq, isNull, lt } from "drizzle-orm";
|
||||||
import { getDatabase } from "../database/drizzle.js";
|
import { getDatabase } from "../database/drizzle.js";
|
||||||
import {
|
import {
|
||||||
attachmentsTable,
|
attachmentsTable,
|
||||||
@@ -6,9 +7,8 @@ import {
|
|||||||
voiceRecordingsTable,
|
voiceRecordingsTable,
|
||||||
} from "../database/schema.js";
|
} from "../database/schema.js";
|
||||||
import { createChildLogger } from "../logger.js";
|
import { createChildLogger } from "../logger.js";
|
||||||
import { getExpiredMessages, getRetentionPolicy } from "./messageStore.js";
|
import { getRetentionPolicy } from "./messageStore.js";
|
||||||
import type { RetentionPolicy } from "./types.js";
|
import type { RetentionPolicy } from "./types.js";
|
||||||
import { and, eq, isNull, lt, sql } from "drizzle-orm";
|
|
||||||
|
|
||||||
const logger = createChildLogger("retention-manager");
|
const logger = createChildLogger("retention-manager");
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { mkdir, readFile, writeFile, unlink } from "node:fs/promises";
|
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { createChildLogger } from "../logger.js";
|
import { createChildLogger } from "../logger.js";
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import {
|
|||||||
getAnalysisQueueStatus,
|
getAnalysisQueueStatus,
|
||||||
queueMessageAnalysis,
|
queueMessageAnalysis,
|
||||||
} from "../moderation/aiAnalyzer.js";
|
} from "../moderation/aiAnalyzer.js";
|
||||||
import { searchMessages, updateMessageAIAnalysis } from "../moderation/messageStore.js";
|
import {
|
||||||
|
searchMessages,
|
||||||
|
updateMessageAIAnalysis,
|
||||||
|
} from "../moderation/messageStore.js";
|
||||||
import type { MessageRecord } from "../moderation/types.js";
|
import type { MessageRecord } from "../moderation/types.js";
|
||||||
|
|
||||||
export function createAnalysisRoutes(): Router {
|
export function createAnalysisRoutes(): Router {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import {
|
import { Pool } from "pg";
|
||||||
getDatabase,
|
import { getDatabase, initializeDatabase } from "../../src/database/drizzle";
|
||||||
initializeDatabase,
|
|
||||||
} from "../../src/database/drizzle";
|
|
||||||
|
|
||||||
interface RunnableDatabase {
|
interface RunnableDatabase {
|
||||||
run(sql: string): Promise<unknown>;
|
run(sql: string): Promise<unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SAFE_TEST_DATABASE_NAME = /(^|[_-])(test|testing)([_-]|$)|gmw_test/i;
|
const SAFE_TEST_DATABASE_NAME = /(^|[_-])(test|testing)([_-]|$)|gmw_test/i;
|
||||||
|
const DEFAULT_TEST_SCHEMA = "gmw_test";
|
||||||
|
const SAFE_TEST_SCHEMA_NAME =
|
||||||
|
/^[a-zA-Z_][a-zA-Z0-9_]*(test|testing)[a-zA-Z0-9_]*$/i;
|
||||||
|
|
||||||
function getDatabaseNameFromUrl(databaseUrl: string): string {
|
function getDatabaseNameFromUrl(databaseUrl: string): string {
|
||||||
try {
|
try {
|
||||||
@@ -26,6 +27,41 @@ function getConfiguredDatabaseName(): string {
|
|||||||
return process.env.POSTGRES_DB ?? "";
|
return process.env.POSTGRES_DB ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTestSchemaName(): string {
|
||||||
|
const schemaName = process.env.TEST_DATABASE_SCHEMA ?? DEFAULT_TEST_SCHEMA;
|
||||||
|
if (!SAFE_TEST_SCHEMA_NAME.test(schemaName)) {
|
||||||
|
throw new Error(
|
||||||
|
`Refusing to use unsafe test schema "${schemaName}". Schema name must contain "test" and use identifier-safe characters only.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return schemaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function quoteIdentifier(identifier: string): string {
|
||||||
|
return `"${identifier.replace(/"/g, '""')}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureTestSchemaExists(): Promise<void> {
|
||||||
|
assertSafeTestDatabaseUrl();
|
||||||
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||||
|
const schemaName = getTestSchemaName();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pool.query(
|
||||||
|
`CREATE SCHEMA IF NOT EXISTS ${quoteIdentifier(schemaName)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function configureTestSearchPath(): Promise<void> {
|
||||||
|
const db = getTestDatabase();
|
||||||
|
await db.run(
|
||||||
|
`SET search_path TO ${quoteIdentifier(getTestSchemaName())}, public`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function assertSafeTestDatabaseUrl(): void {
|
export function assertSafeTestDatabaseUrl(): void {
|
||||||
if (process.env.NODE_ENV !== "test") {
|
if (process.env.NODE_ENV !== "test") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -38,16 +74,24 @@ export function assertSafeTestDatabaseUrl(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const databaseName = getConfiguredDatabaseName();
|
const databaseName = getConfiguredDatabaseName();
|
||||||
if (!SAFE_TEST_DATABASE_NAME.test(databaseName)) {
|
const hasSafeSchema = Boolean(process.env.TEST_DATABASE_SCHEMA);
|
||||||
|
if (!SAFE_TEST_DATABASE_NAME.test(databaseName) && !hasSafeSchema) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Refusing to run destructive database test against non-test database "${databaseName || "unknown"}". Set TEST_DATABASE_URL or DATABASE_URL to a database whose name contains "test" (for example hub_test).`,
|
`Refusing to run destructive database test against non-test database "${databaseName || "unknown"}" without TEST_DATABASE_SCHEMA. Set TEST_DATABASE_SCHEMA to a safe test schema name or use a database whose name contains "test" (for example hub_test).`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasSafeSchema) {
|
||||||
|
getTestSchemaName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initializeTestDatabase() {
|
export async function initializeTestDatabase() {
|
||||||
assertSafeTestDatabaseUrl();
|
assertSafeTestDatabaseUrl();
|
||||||
return initializeDatabase();
|
await ensureTestSchemaExists();
|
||||||
|
const database = await initializeDatabase();
|
||||||
|
await configureTestSearchPath();
|
||||||
|
return database;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTestDatabase(): RunnableDatabase {
|
export function getTestDatabase(): RunnableDatabase {
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
vi.mock("../../src/moderation/indonesianTextNormalizer.js", async (importOriginal) => {
|
vi.mock(
|
||||||
const actual = await importOriginal<typeof import("../../src/moderation/indonesianTextNormalizer.js")>();
|
"../../src/moderation/indonesianTextNormalizer.js",
|
||||||
return {
|
async (importOriginal) => {
|
||||||
...actual,
|
const actual =
|
||||||
formatModerationTextEvidenceForPrompt: vi.fn(async (content: string) => {
|
await importOriginal<
|
||||||
// Deterministic mock evidence — length tuned for the "tight budget" test:
|
typeof import("../../src/moderation/indonesianTextNormalizer.js")
|
||||||
// maxTokens=300, target ~88, c3 ~108, c2 ~108, c1 ~108
|
>();
|
||||||
// Expectation: target+c3 fits (196), target+c3+c2 overflows (304)
|
return {
|
||||||
return "[text_evidence] categories=[\"offensive\",\"profanity\",\"sexual_violence\"] severity=high confidence=0.92 language=id detected=badword normalized=false metadata_v2=true context=true";
|
...actual,
|
||||||
}),
|
formatModerationTextEvidenceForPrompt: vi.fn(async (content: string) => {
|
||||||
};
|
// Deterministic mock evidence — length tuned for the "tight budget" test:
|
||||||
});
|
// maxTokens=300, target ~88, c3 ~108, c2 ~108, c1 ~108
|
||||||
|
// Expectation: target+c3 fits (196), target+c3+c2 overflows (304)
|
||||||
|
return '[text_evidence] categories=["offensive","profanity","sexual_violence"] severity=high confidence=0.92 language=id detected=badword normalized=false metadata_v2=true context=true';
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildConversationContext,
|
buildConversationContext,
|
||||||
|
|||||||
@@ -65,4 +65,4 @@ describe("detectIndonesianBadwords remote fallback", () => {
|
|||||||
expect(mocks.axiosPost).toHaveBeenCalledTimes(1);
|
expect(mocks.axiosPost).toHaveBeenCalledTimes(1);
|
||||||
expect(mocks.openaiCreate).toHaveBeenCalledTimes(1);
|
expect(mocks.openaiCreate).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
||||||
|
import { config } from "../../src/config";
|
||||||
import {
|
import {
|
||||||
buildModerationTextEvidence,
|
buildModerationTextEvidence,
|
||||||
detectIndonesianBadwords,
|
detectIndonesianBadwords,
|
||||||
@@ -6,7 +7,6 @@ import {
|
|||||||
normalizeDiscordCustomEmoji,
|
normalizeDiscordCustomEmoji,
|
||||||
normalizeIndonesianSlang,
|
normalizeIndonesianSlang,
|
||||||
} from "../../src/moderation/indonesianTextNormalizer";
|
} from "../../src/moderation/indonesianTextNormalizer";
|
||||||
import { config } from "../../src/config";
|
|
||||||
|
|
||||||
const originalNemotronKey = config.NVIDIA_NEMOTRON_API_KEY;
|
const originalNemotronKey = config.NVIDIA_NEMOTRON_API_KEY;
|
||||||
const originalPrimaryAiKey = config.AI_LLM_API_KEY;
|
const originalPrimaryAiKey = config.AI_LLM_API_KEY;
|
||||||
@@ -105,7 +105,9 @@ describe("formatModerationTextEvidenceForPrompt", () => {
|
|||||||
expect(formatted).toContain("[emoji:hadeh]");
|
expect(formatted).toContain("[emoji:hadeh]");
|
||||||
expect(formatted).toContain("[normalization_notes:");
|
expect(formatted).toContain("[normalization_notes:");
|
||||||
// The NVIDIA API may or may not detect badwords for this input
|
// The NVIDIA API may or may not detect badwords for this input
|
||||||
expect(formatted).toMatch(/no Indonesian badword detected|Indonesian badword detected/);
|
expect(formatted).toMatch(
|
||||||
|
/no Indonesian badword detected|Indonesian badword detected/,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes normalized text even for clean input", async () => {
|
it("includes normalized text even for clean input", async () => {
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
vi,
|
vi,
|
||||||
} from "vitest";
|
} from "vitest";
|
||||||
import { closeDatabase } from "../../src/database/drizzle";
|
import { closeDatabase } from "../../src/database/drizzle";
|
||||||
|
import { captureMessage } from "../../src/moderation/messageCapture";
|
||||||
|
import type { ModerationBroadcaster } from "../../src/moderation/types";
|
||||||
import {
|
import {
|
||||||
clearTestTables,
|
clearTestTables,
|
||||||
getTestDatabase,
|
getTestDatabase,
|
||||||
initializeTestDatabase,
|
initializeTestDatabase,
|
||||||
} from "../helpers/testDatabase";
|
} from "../helpers/testDatabase";
|
||||||
import { captureMessage } from "../../src/moderation/messageCapture";
|
|
||||||
import type { ModerationBroadcaster } from "../../src/moderation/types";
|
|
||||||
|
|
||||||
const queueMessageAnalysis = vi.fn();
|
const queueMessageAnalysis = vi.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { closeDatabase } from "../../src/database/drizzle";
|
import { closeDatabase } from "../../src/database/drizzle";
|
||||||
import {
|
|
||||||
clearTestTables,
|
|
||||||
getTestDatabase,
|
|
||||||
initializeTestDatabase,
|
|
||||||
} from "../helpers/testDatabase";
|
|
||||||
import { createChildLogger } from "../../src/logger";
|
import { createChildLogger } from "../../src/logger";
|
||||||
import {
|
import {
|
||||||
decodeCursor,
|
decodeCursor,
|
||||||
@@ -18,6 +13,11 @@ import {
|
|||||||
updateMessageAsEdited,
|
updateMessageAsEdited,
|
||||||
} from "../../src/moderation/messageStore";
|
} from "../../src/moderation/messageStore";
|
||||||
import type { MessageRecord } from "../../src/moderation/types";
|
import type { MessageRecord } from "../../src/moderation/types";
|
||||||
|
import {
|
||||||
|
clearTestTables,
|
||||||
|
getTestDatabase,
|
||||||
|
initializeTestDatabase,
|
||||||
|
} from "../helpers/testDatabase";
|
||||||
|
|
||||||
const logger = createChildLogger("messageStoreQueries.test");
|
const logger = createChildLogger("messageStoreQueries.test");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user