feat(discord-gateway): add perceptual hash helpers for image deduplication in ai-moderation

This commit is contained in:
MythEclipse
2026-06-02 22:49:37 +07:00
parent decfb8fb61
commit b0a879647e
2 changed files with 13 additions and 25 deletions
@@ -1,4 +1,5 @@
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import { availableParallelism } from "node:os";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { createChildLogger } from "@bete/shared/logger"; import { createChildLogger } from "@bete/shared/logger";
import { retryWithBackoff } from "@bete/shared/utils"; import { retryWithBackoff } from "@bete/shared/utils";
@@ -26,7 +27,7 @@ import type {
ModerationBroadcaster, ModerationBroadcaster,
} from "../message-capture/types.js"; } from "../message-capture/types.js";
import { attemptAutoDeleteFlaggedMessage } from "./autoDeleteManager.js"; import { attemptAutoDeleteFlaggedMessage } from "./autoDeleteManager.js";
import { buildConversationContext } from "./conversationContext.js"; import { buildConversationContext, estimateTokens } from "./conversationContext.js";
import { runModerationAnalysis } from "./llmModerationClient.js"; import { runModerationAnalysis } from "./llmModerationClient.js";
import { logModerationError } from "./responseLogger.js"; import { logModerationError } from "./responseLogger.js";
@@ -190,31 +190,18 @@ export async function captureMessage(
} }
if (!isBacklog) { if (!isBacklog) {
if (attachmentUploadTasks.length > 0) { // AI analysis starts immediately — attachment upload runs in parallel.
let analysisQueued = false; // Media analysis path downloads images directly from Discord CDN,
let fallbackTimer: NodeJS.Timeout | null = null; // so it does NOT depend on the upload completing first.
const queueAnalysisOnce = () => { queueMessageAnalysis(message.id);
if (analysisQueued) return;
analysisQueued = true;
if (fallbackTimer) {
clearTimeout(fallbackTimer);
fallbackTimer = null;
}
queueMessageAnalysis(message.id);
};
fallbackTimer = setTimeout(queueAnalysisOnce, 30000); if (attachmentUploadTasks.length > 0) {
Promise.allSettled(attachmentUploadTasks) Promise.allSettled(attachmentUploadTasks).catch((err: unknown) => {
.then(queueAnalysisOnce) logger.error(
.catch((err: unknown) => { { messageId: message.id, error: err },
logger.error( "Attachment upload tasks failed",
{ messageId: message.id, error: err }, );
"Failed to queue message analysis after attachment upload", });
);
queueAnalysisOnce();
});
} else {
queueMessageAnalysis(message.id);
} }
} }
} }