refactor: remove Feishu runtime dedupe JSON fallback

This commit is contained in:
Peter Steinberger
2026-06-04 08:20:28 -07:00
committed by GitHub
parent 0a351cdf7f
commit 3a335c6df1
3 changed files with 9 additions and 81 deletions

View File

@@ -12,7 +12,7 @@ import {
hasProcessedFeishuMessage,
testingHooks,
tryRecordMessagePersistent,
warmupDedupFromDisk,
warmupDedupFromPluginState,
} from "./dedup.js";
import { setFeishuRuntime } from "./runtime.js";
@@ -59,7 +59,7 @@ describe("Feishu persistent dedupe", () => {
await expect(tryRecordMessagePersistent("msg-2", "account-a")).resolves.toBe(true);
testingHooks.resetFeishuDedupMemoryForTests();
await expect(warmupDedupFromDisk("account-a")).resolves.toBe(1);
await expect(warmupDedupFromPluginState("account-a")).resolves.toBe(1);
await expect(tryRecordMessagePersistent("msg-2", "account-a")).resolves.toBe(false);
});
@@ -73,7 +73,7 @@ describe("Feishu persistent dedupe", () => {
await expect(hasProcessedFeishuMessage("msg-3", "account-a")).resolves.toBe(false);
});
it("imports legacy JSON dedupe entries before checking plugin state", async () => {
it("ignores legacy JSON dedupe files at runtime", async () => {
vi.useFakeTimers();
vi.setSystemTime(2_000);
const legacyPath = path.join(tempDir as string, "feishu", "dedup", "account-a.json");
@@ -87,8 +87,8 @@ describe("Feishu persistent dedupe", () => {
"utf8",
);
await expect(hasProcessedFeishuMessage("msg-legacy", "account-a")).resolves.toBe(true);
await expect(tryRecordMessagePersistent("msg-legacy", "account-a")).resolves.toBe(false);
await expect(hasProcessedFeishuMessage("msg-legacy", "account-a")).resolves.toBe(false);
await expect(tryRecordMessagePersistent("msg-legacy", "account-a")).resolves.toBe(true);
await expect(hasProcessedFeishuMessage("msg-expired", "account-a")).resolves.toBe(false);
});
});

View File

@@ -1,7 +1,4 @@
import { createHash } from "node:crypto";
import os from "node:os";
import path from "node:path";
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
import type { PluginStateSyncKeyedStore } from "openclaw/plugin-sdk/plugin-state-runtime";
import {
releaseFeishuMessageProcessing,
@@ -20,11 +17,8 @@ type FeishuDedupStoreEntry = {
};
const memory = new Map<string, number>();
const importedLegacyNamespaces = new Set<string>();
const cachedDedupStores = new Map<string, PluginStateSyncKeyedStore<FeishuDedupStoreEntry>>();
type LegacyDedupeData = Record<string, number>;
function normalizeMessageId(messageId: string | undefined | null): string | null {
const trimmed = messageId?.trim();
return trimmed ? trimmed : null;
@@ -34,22 +28,6 @@ function normalizeNamespace(namespace?: string): string {
return namespace?.trim() || "global";
}
function resolveLegacyStateDir(env: NodeJS.ProcessEnv = process.env): string {
const stateOverride = env.OPENCLAW_STATE_DIR?.trim();
if (stateOverride) {
return stateOverride;
}
if (env.VITEST || env.NODE_ENV === "test") {
return path.join(os.tmpdir(), ["openclaw-vitest", String(process.pid)].join("-"));
}
return path.join(os.homedir(), ".openclaw");
}
function resolveLegacyNamespaceFilePath(namespace: string): string {
const safe = namespace.replace(/[^a-zA-Z0-9_-]/g, "_");
return path.join(resolveLegacyStateDir(), "feishu", "dedup", `${safe}.json`);
}
function pluginStateNamespace(namespace: string): string {
return `dedup.${namespace.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
}
@@ -116,52 +94,6 @@ function hasMemory(namespace: string, messageId: string, now = Date.now()): bool
return false;
}
function sanitizeLegacyDedupeData(value: unknown): LegacyDedupeData {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return {};
}
const out: LegacyDedupeData = {};
for (const [key, seenAt] of Object.entries(value as Record<string, unknown>)) {
if (typeof seenAt === "number" && Number.isFinite(seenAt) && seenAt > 0) {
out[key] = seenAt;
}
}
return out;
}
function importLegacyDedupNamespace(
namespace: string,
now = Date.now(),
log?: (...args: unknown[]) => void,
): void {
if (importedLegacyNamespaces.has(namespace)) {
return;
}
try {
const data = sanitizeLegacyDedupeData(loadJsonFile(resolveLegacyNamespaceFilePath(namespace)));
const store = openDedupStore(namespace);
for (const [messageId, seenAt] of Object.entries(data)) {
if (!isRecent(seenAt, now)) {
continue;
}
const key = dedupeStoreKey(namespace, messageId);
if (store.lookup(key) != null) {
continue;
}
store.register(
key,
{ namespace, messageId, seenAt },
{ ttlMs: Math.max(1, DEDUP_TTL_MS - (now - seenAt)) },
);
}
importedLegacyNamespaces.add(namespace);
} catch (error) {
importedLegacyNamespaces.delete(namespace);
log?.(`feishu-dedup: legacy state import failed: ${String(error)}`);
}
}
export { releaseFeishuMessageProcessing, tryBeginFeishuMessageProcessing };
export async function claimUnprocessedFeishuMessage(params: {
@@ -259,7 +191,6 @@ export async function tryRecordMessagePersistent(
return true;
}
const now = Date.now();
importLegacyDedupNamespace(normalizedNamespace, now, log);
if (hasMemory(normalizedNamespace, normalizedMessageId, now)) {
return false;
}
@@ -318,7 +249,6 @@ async function hasRecordedMessagePersistent(
return false;
}
const now = Date.now();
importLegacyDedupNamespace(normalizedNamespace, now, log);
if (hasMemory(normalizedNamespace, normalizedMessageId, now)) {
return true;
}
@@ -337,7 +267,7 @@ async function hasRecordedMessagePersistent(
}
}
export async function warmupDedupFromDisk(
export async function warmupDedupFromPluginState(
namespace: string,
log?: (...args: unknown[]) => void,
): Promise<number> {
@@ -345,7 +275,6 @@ export async function warmupDedupFromDisk(
try {
let loaded = 0;
const now = Date.now();
importLegacyDedupNamespace(normalizedNamespace, now, log);
for (const entry of openDedupStore(normalizedNamespace).entries()) {
if (entry.value.namespace !== normalizedNamespace || !isRecent(entry.value.seenAt, now)) {
continue;
@@ -363,7 +292,6 @@ export async function warmupDedupFromDisk(
export const testingHooks = {
resetFeishuDedupForTests() {
memory.clear();
importedLegacyNamespaces.clear();
for (const store of cachedDedupStores.values()) {
store.clear();
}

View File

@@ -14,7 +14,7 @@ import { isRecord, readString } from "./comment-shared.js";
import {
hasProcessedFeishuMessage,
recordProcessedFeishuMessage,
warmupDedupFromDisk,
warmupDedupFromPluginState,
} from "./dedup.js";
import { applyBotIdentityState, startBotIdentityRecovery } from "./monitor.bot-identity.js";
import { createFeishuBotMenuHandler } from "./monitor.bot-menu-handler.js";
@@ -469,9 +469,9 @@ export async function monitorSingleAccount(params: MonitorSingleAccountParams):
throw new Error(`Feishu account "${accountId}" webhook mode requires encryptKey`);
}
const warmupCount = await warmupDedupFromDisk(accountId, log);
const warmupCount = await warmupDedupFromPluginState(accountId, log);
if (warmupCount > 0) {
log(`feishu[${accountId}]: dedup warmup loaded ${warmupCount} entries from disk`);
log(`feishu[${accountId}]: dedup warmup loaded ${warmupCount} entries from plugin state`);
}
let threadBindingManager: ReturnType<typeof createFeishuThreadBindingManager> | null | undefined;