mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
refactor: remove Feishu runtime dedupe JSON fallback
This commit is contained in:
committed by
GitHub
parent
0a351cdf7f
commit
3a335c6df1
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user