





















@@ -0,0 +1,148 @@
1+import { resolveGlobalDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
2+import { getOptionalSlackRuntime } from "../runtime.js";
3+import type { SlackMessageEvent } from "../types.js";
4+5+const TTL_MS = 24 * 60 * 60 * 1000;
6+const MAX_ENTRIES = 20_000;
7+const PERSISTENT_MAX_ENTRIES = 20_000;
8+const PERSISTENT_NAMESPACE = "slack.inbound-deliveries";
9+const SLACK_INBOUND_DELIVERIES_KEY = Symbol.for("openclaw.slackInboundDeliveries");
10+11+type SlackInboundDeliveryRecord = {
12+deliveredAt: number;
13+};
14+15+type SlackInboundDeliveryStore = {
16+register(
17+key: string,
18+value: SlackInboundDeliveryRecord,
19+opts?: { ttlMs?: number },
20+): Promise<void>;
21+lookup(key: string): Promise<SlackInboundDeliveryRecord | undefined>;
22+};
23+24+const deliveredMessages = resolveGlobalDedupeCache(SLACK_INBOUND_DELIVERIES_KEY, {
25+ttlMs: TTL_MS,
26+maxSize: MAX_ENTRIES,
27+});
28+29+let persistentStore: SlackInboundDeliveryStore | undefined;
30+let persistentStoreDisabled = false;
31+32+function makeKey(accountId: string, channelId: string, ts: string): string {
33+return `${accountId}:${channelId}:${ts}`;
34+}
35+36+function reportPersistentInboundDeliveryError(error: unknown): void {
37+try {
38+getOptionalSlackRuntime()
39+?.logging.getChildLogger({ plugin: "slack", feature: "inbound-delivery-state" })
40+.warn("Slack persistent inbound delivery state failed", { error: String(error) });
41+} catch {
42+// Best effort only: persistent state must never break Slack message handling.
43+}
44+}
45+46+function disablePersistentInboundDelivery(error: unknown): void {
47+persistentStoreDisabled = true;
48+persistentStore = undefined;
49+reportPersistentInboundDeliveryError(error);
50+}
51+52+function getPersistentInboundDeliveryStore(): SlackInboundDeliveryStore | undefined {
53+if (persistentStoreDisabled) {
54+return undefined;
55+}
56+if (persistentStore) {
57+return persistentStore;
58+}
59+const runtime = getOptionalSlackRuntime();
60+if (!runtime) {
61+return undefined;
62+}
63+try {
64+persistentStore = runtime.state.openKeyedStore<SlackInboundDeliveryRecord>({
65+namespace: PERSISTENT_NAMESPACE,
66+maxEntries: PERSISTENT_MAX_ENTRIES,
67+defaultTtlMs: TTL_MS,
68+});
69+return persistentStore;
70+} catch (error) {
71+disablePersistentInboundDelivery(error);
72+return undefined;
73+}
74+}
75+76+async function lookupPersistentInboundDelivery(key: string): Promise<boolean> {
77+const store = getPersistentInboundDeliveryStore();
78+if (!store) {
79+return false;
80+}
81+try {
82+return Boolean(await store.lookup(key));
83+} catch (error) {
84+disablePersistentInboundDelivery(error);
85+return false;
86+}
87+}
88+89+async function rememberPersistentInboundDelivery(key: string, deliveredAt: number): Promise<void> {
90+const store = getPersistentInboundDeliveryStore();
91+if (!store) {
92+return;
93+}
94+try {
95+await store.register(key, { deliveredAt });
96+} catch (error) {
97+disablePersistentInboundDelivery(error);
98+}
99+}
100+101+export async function hasSlackInboundMessageDelivery(params: {
102+accountId: string;
103+channelId: string | undefined;
104+ts: string | undefined;
105+}): Promise<boolean> {
106+if (!params.accountId || !params.channelId || !params.ts) {
107+return false;
108+}
109+const key = makeKey(params.accountId, params.channelId, params.ts);
110+if (deliveredMessages.peek(key)) {
111+return true;
112+}
113+const found = await lookupPersistentInboundDelivery(key);
114+if (found) {
115+deliveredMessages.check(key);
116+}
117+return found;
118+}
119+120+export async function recordSlackInboundMessageDeliveries(params: {
121+accountId: string;
122+messages: readonly SlackMessageEvent[];
123+}): Promise<void> {
124+if (!params.accountId || params.messages.length === 0) {
125+return;
126+}
127+const deliveredAt = Date.now();
128+const keys = new Set<string>();
129+for (const message of params.messages) {
130+if (!message.channel || !message.ts) {
131+continue;
132+}
133+keys.add(makeKey(params.accountId, message.channel, message.ts));
134+}
135+if (keys.size === 0) {
136+return;
137+}
138+for (const key of keys) {
139+deliveredMessages.check(key, deliveredAt);
140+}
141+await Promise.all(Array.from(keys, (key) => rememberPersistentInboundDelivery(key, deliveredAt)));
142+}
143+144+export function clearSlackInboundDeliveryStateForTest(): void {
145+deliveredMessages.clear();
146+persistentStore = undefined;
147+persistentStoreDisabled = false;
148+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。