mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-04 01:04:07 +00:00
fix(slack): bound thread starter cache clocks
This commit is contained in:
@@ -61,6 +61,45 @@ describe("resolveSlackThreadStarter cache", () => {
|
||||
expect(replies).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("drops cached thread starters when the current clock is not a valid date timestamp", async () => {
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_700_000_000_000);
|
||||
const { replies, client } = createThreadStarterRepliesClient();
|
||||
|
||||
const first = await resolveSlackThreadStarter({
|
||||
channelId: "C1",
|
||||
threadTs: "1000.1",
|
||||
client,
|
||||
});
|
||||
nowSpy.mockReturnValue(Number.NaN);
|
||||
const second = await resolveSlackThreadStarter({
|
||||
channelId: "C1",
|
||||
threadTs: "1000.1",
|
||||
client,
|
||||
});
|
||||
|
||||
expect(first).toEqual(second);
|
||||
expect(replies).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("does not cache thread starters when the expiry timestamp would exceed the valid date range", async () => {
|
||||
vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
|
||||
const { replies, client } = createThreadStarterRepliesClient();
|
||||
|
||||
const first = await resolveSlackThreadStarter({
|
||||
channelId: "C1",
|
||||
threadTs: "1000.1",
|
||||
client,
|
||||
});
|
||||
const second = await resolveSlackThreadStarter({
|
||||
channelId: "C1",
|
||||
threadTs: "1000.1",
|
||||
client,
|
||||
});
|
||||
|
||||
expect(first).toEqual(second);
|
||||
expect(replies).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("does not cache empty starter text", async () => {
|
||||
const { replies, client } = createThreadStarterRepliesClient({
|
||||
messages: [{ text: " ", user: "U1", ts: "1000.1" }],
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { WebClient as SlackWebClient } from "@slack/web-api";
|
||||
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import {
|
||||
asDateTimestampMs,
|
||||
resolveExpiresAtMsFromDurationMs,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { formatSlackFileReferenceList } from "../file-reference.js";
|
||||
import type { SlackFile } from "../types.js";
|
||||
import { logVerbose } from "./thread.runtime.js";
|
||||
@@ -15,7 +19,7 @@ export type SlackThreadStarter = {
|
||||
|
||||
type SlackThreadStarterCacheEntry = {
|
||||
value: SlackThreadStarter;
|
||||
cachedAt: number;
|
||||
expiresAt: number;
|
||||
};
|
||||
|
||||
const THREAD_STARTER_CACHE = new Map<string, SlackThreadStarterCacheEntry>();
|
||||
@@ -23,9 +27,13 @@ const THREAD_STARTER_CACHE_TTL_MS = 6 * 60 * 60_000;
|
||||
const THREAD_STARTER_CACHE_MAX = 2000;
|
||||
|
||||
function evictThreadStarterCache(): void {
|
||||
const now = Date.now();
|
||||
const now = asDateTimestampMs(Date.now());
|
||||
if (now === undefined) {
|
||||
THREAD_STARTER_CACHE.clear();
|
||||
return;
|
||||
}
|
||||
for (const [cacheKey, entry] of THREAD_STARTER_CACHE.entries()) {
|
||||
if (now - entry.cachedAt > THREAD_STARTER_CACHE_TTL_MS) {
|
||||
if (asDateTimestampMs(entry.expiresAt) === undefined || entry.expiresAt <= now) {
|
||||
THREAD_STARTER_CACHE.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
@@ -44,10 +52,11 @@ export async function resolveSlackThreadStarter(params: {
|
||||
evictThreadStarterCache();
|
||||
const cacheKey = `${params.channelId}:${params.threadTs}`;
|
||||
const cached = THREAD_STARTER_CACHE.get(cacheKey);
|
||||
if (cached && Date.now() - cached.cachedAt <= THREAD_STARTER_CACHE_TTL_MS) {
|
||||
return cached.value;
|
||||
}
|
||||
if (cached) {
|
||||
const now = asDateTimestampMs(Date.now());
|
||||
if (now !== undefined && cached.expiresAt > now) {
|
||||
return cached.value;
|
||||
}
|
||||
THREAD_STARTER_CACHE.delete(cacheKey);
|
||||
}
|
||||
try {
|
||||
@@ -78,14 +87,17 @@ export async function resolveSlackThreadStarter(params: {
|
||||
ts: message.ts,
|
||||
files,
|
||||
};
|
||||
if (THREAD_STARTER_CACHE.has(cacheKey)) {
|
||||
THREAD_STARTER_CACHE.delete(cacheKey);
|
||||
const expiresAt = resolveExpiresAtMsFromDurationMs(THREAD_STARTER_CACHE_TTL_MS);
|
||||
if (expiresAt !== undefined) {
|
||||
if (THREAD_STARTER_CACHE.has(cacheKey)) {
|
||||
THREAD_STARTER_CACHE.delete(cacheKey);
|
||||
}
|
||||
THREAD_STARTER_CACHE.set(cacheKey, {
|
||||
value: starter,
|
||||
expiresAt,
|
||||
});
|
||||
evictThreadStarterCache();
|
||||
}
|
||||
THREAD_STARTER_CACHE.set(cacheKey, {
|
||||
value: starter,
|
||||
cachedAt: Date.now(),
|
||||
});
|
||||
evictThreadStarterCache();
|
||||
return starter;
|
||||
} catch (err) {
|
||||
logVerbose(
|
||||
|
||||
Reference in New Issue
Block a user