feat: add spam detection lane (tower.classify.spam) with content-aware filtering

- Add SpamJobData + sync missing job types (DncJobData, ClassifyJobData, P1JobData, etc.) from dist back to source in @tower/types
- spam-detector.ts: emoji-only, short-greeting, link-only detection + duplicate-hash (1h window)
- spam.queue.ts / spam.processor.ts: BullMQ tower.classify.spam lane
- classify.processor.ts: spam check (Step 0) before rule engine, accepts spamQueue param
- main.ts: wire spamQueue + spamWorker; spam pre-filter before ingestQueue.add
- dnc.processor.ts: labelled reason logging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 16:00:48 +05:30
parent 566690bd04
commit a685f8b4d5
7 changed files with 249 additions and 5 deletions
+68
View File
@@ -91,4 +91,72 @@ export interface IndexJobData {
tags: string[];
platform: string;
approvedAt: string; // ISO 8601 — converted to Unix ms in the index processor
traceId?: string;
}
export interface DncJobData {
tenantId: string;
sourceGroupId: string;
accountId: string;
reason: 'rule_skip' | 'no_rule_match' | 'spam_detected' | 'content_filtered';
timestamp: string;
policyVersion: string;
traceId?: string;
}
export interface ClassifyJobData {
messageId: string;
tenantId: string;
sourceGroupId: string;
sourceGroupJid: string;
senderJid: string;
accountId: string;
traceId?: string;
}
export interface ThreadResolveJobData {
messageId: string;
tenantId: string;
sourceGroupId: string;
quotedPlatformMsgId?: string;
traceId?: string;
}
export interface DigestJobData {
tenantId: string;
digestDate: string;
triggeredBy: 'schedule' | 'manual';
traceId?: string;
}
export interface ReviewJobData {
tenantId: string;
messageId: string;
sourceGroupId: string;
tags: string[];
reason: string;
policyVersion: string;
traceId?: string;
}
export interface P1JobData {
tenantId: string;
messageId: string;
content: string;
sourceGroupId: string;
senderJid: string;
senderName?: string;
tags: string[];
policyVersion: string;
traceId?: string;
}
export interface SpamJobData {
tenantId: string;
messageId: string;
sourceGroupId: string;
senderJid: string;
reason: 'forward_flood' | 'duplicate_hash' | 'emoji_only' | 'short_greeting' | 'link_only';
contentHash: string;
traceId?: string;
}