53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
import { logVerbose, shouldLogVerbose } from "../../globals.js";
|
|
import { createDedupeCache, type DedupeCache } from "../../infra/dedupe.js";
|
|
import type { MsgContext } from "../templating.js";
|
|
|
|
const DEFAULT_INBOUND_DEDUPE_TTL_MS = 20 * 60_000;
|
|
const DEFAULT_INBOUND_DEDUPE_MAX = 5000;
|
|
|
|
const inboundDedupeCache = createDedupeCache({
|
|
ttlMs: DEFAULT_INBOUND_DEDUPE_TTL_MS,
|
|
maxSize: DEFAULT_INBOUND_DEDUPE_MAX,
|
|
});
|
|
|
|
const normalizeProvider = (value?: string | null) =>
|
|
value?.trim().toLowerCase() || "";
|
|
|
|
const resolveInboundPeerId = (ctx: MsgContext) =>
|
|
ctx.OriginatingTo ?? ctx.To ?? ctx.From ?? ctx.SessionKey;
|
|
|
|
export function buildInboundDedupeKey(ctx: MsgContext): string | null {
|
|
const provider = normalizeProvider(
|
|
ctx.OriginatingChannel ?? ctx.Provider ?? ctx.Surface,
|
|
);
|
|
const messageId = ctx.MessageSid?.trim();
|
|
if (!provider || !messageId) return null;
|
|
const peerId = resolveInboundPeerId(ctx);
|
|
if (!peerId) return null;
|
|
const sessionKey = ctx.SessionKey?.trim() ?? "";
|
|
const accountId = ctx.AccountId?.trim() ?? "";
|
|
const threadId =
|
|
typeof ctx.MessageThreadId === "number" ? String(ctx.MessageThreadId) : "";
|
|
return [provider, accountId, sessionKey, peerId, threadId, messageId]
|
|
.filter(Boolean)
|
|
.join("|");
|
|
}
|
|
|
|
export function shouldSkipDuplicateInbound(
|
|
ctx: MsgContext,
|
|
opts?: { cache?: DedupeCache; now?: number },
|
|
): boolean {
|
|
const key = buildInboundDedupeKey(ctx);
|
|
if (!key) return false;
|
|
const cache = opts?.cache ?? inboundDedupeCache;
|
|
const skipped = cache.check(key, opts?.now);
|
|
if (skipped && shouldLogVerbose()) {
|
|
logVerbose(`inbound dedupe: skipped ${key}`);
|
|
}
|
|
return skipped;
|
|
}
|
|
|
|
export function resetInboundDedupe(): void {
|
|
inboundDedupeCache.clear();
|
|
}
|