Files
openclaw/src/slack/sent-thread-cache.ts
Luis Conde bd78a74298 feat(slack): track thread participation for auto-reply without @mention (#29165)
* feat(slack): track thread participation for auto-reply without @mention

* fix(slack): scope thread participation cache by accountId and capture actual reply thread ts

* fix(slack): capture reply thread ts from all delivery paths and only after success

* Slack: add changelog for thread participation cache behavior

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 10:42:12 -06:00

62 lines
1.6 KiB
TypeScript

/**
* In-memory cache of Slack threads the bot has participated in.
* Used to auto-respond in threads without requiring @mention after the first reply.
* Follows a similar TTL pattern to the MS Teams and Telegram sent-message caches.
*/
const TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
const MAX_ENTRIES = 5000;
const threadParticipation = new Map<string, number>();
function makeKey(accountId: string, channelId: string, threadTs: string): string {
return `${accountId}:${channelId}:${threadTs}`;
}
function evictExpired(): void {
const now = Date.now();
for (const [key, timestamp] of threadParticipation) {
if (now - timestamp > TTL_MS) {
threadParticipation.delete(key);
}
}
}
export function recordSlackThreadParticipation(
accountId: string,
channelId: string,
threadTs: string,
): void {
if (!accountId || !channelId || !threadTs) {
return;
}
if (threadParticipation.size >= MAX_ENTRIES) {
evictExpired();
}
threadParticipation.set(makeKey(accountId, channelId, threadTs), Date.now());
}
export function hasSlackThreadParticipation(
accountId: string,
channelId: string,
threadTs: string,
): boolean {
if (!accountId || !channelId || !threadTs) {
return false;
}
const key = makeKey(accountId, channelId, threadTs);
const timestamp = threadParticipation.get(key);
if (timestamp == null) {
return false;
}
if (Date.now() - timestamp > TTL_MS) {
threadParticipation.delete(key);
return false;
}
return true;
}
export function clearSlackThreadParticipationCache(): void {
threadParticipation.clear();
}