* Tests: add fresh module import helper * Process: share command queue runtime state * Agents: share embedded run runtime state * Reply: share followup queue runtime state * Reply: share followup drain callback state * Reply: share queued message dedupe state * Reply: share inbound dedupe state * Tests: cover shared command queue runtime state * Tests: cover shared embedded run runtime state * Tests: cover shared followup queue runtime state * Tests: cover shared inbound dedupe state * Tests: cover shared Slack thread participation state * Slack: share sent thread participation state * Tests: document fresh import helper * Telegram: share draft stream runtime state * Tests: cover shared Telegram draft stream state * Telegram: share sent message cache state * Tests: cover shared Telegram sent message cache * Telegram: share thread binding runtime state * Tests: cover shared Telegram thread binding state * Tests: avoid duplicate shared queue reset * refactor(runtime): centralize global singleton access * refactor(runtime): preserve undefined global singleton values * test(runtime): cover undefined global singleton values --------- Co-authored-by: Nimrod Gutman <nimrod.gutman@gmail.com>
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
import { resolveGlobalMap } from "../shared/global-singleton.js";
|
|
|
|
/**
|
|
* In-memory cache of sent message IDs per chat.
|
|
* Used to identify bot's own messages for reaction filtering ("own" mode).
|
|
*/
|
|
|
|
const TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
|
|
type CacheEntry = {
|
|
timestamps: Map<number, number>;
|
|
};
|
|
|
|
/**
|
|
* Keep sent-message tracking shared across bundled chunks so Telegram reaction
|
|
* filters see the same sent-message history regardless of which chunk recorded it.
|
|
*/
|
|
const TELEGRAM_SENT_MESSAGES_KEY = Symbol.for("openclaw.telegramSentMessages");
|
|
|
|
const sentMessages = resolveGlobalMap<string, CacheEntry>(TELEGRAM_SENT_MESSAGES_KEY);
|
|
|
|
function getChatKey(chatId: number | string): string {
|
|
return String(chatId);
|
|
}
|
|
|
|
function cleanupExpired(entry: CacheEntry): void {
|
|
const now = Date.now();
|
|
for (const [msgId, timestamp] of entry.timestamps) {
|
|
if (now - timestamp > TTL_MS) {
|
|
entry.timestamps.delete(msgId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record a message ID as sent by the bot.
|
|
*/
|
|
export function recordSentMessage(chatId: number | string, messageId: number): void {
|
|
const key = getChatKey(chatId);
|
|
let entry = sentMessages.get(key);
|
|
if (!entry) {
|
|
entry = { timestamps: new Map() };
|
|
sentMessages.set(key, entry);
|
|
}
|
|
entry.timestamps.set(messageId, Date.now());
|
|
// Periodic cleanup
|
|
if (entry.timestamps.size > 100) {
|
|
cleanupExpired(entry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a message was sent by the bot.
|
|
*/
|
|
export function wasSentByBot(chatId: number | string, messageId: number): boolean {
|
|
const key = getChatKey(chatId);
|
|
const entry = sentMessages.get(key);
|
|
if (!entry) {
|
|
return false;
|
|
}
|
|
// Clean up expired entries on read
|
|
cleanupExpired(entry);
|
|
return entry.timestamps.has(messageId);
|
|
}
|
|
|
|
/**
|
|
* Clear all cached entries (for testing).
|
|
*/
|
|
export function clearSentMessageCache(): void {
|
|
sentMessages.clear();
|
|
}
|