Files
openclaw/extensions/telegram/src/sticker-cache-store.ts
2026-04-10 23:12:59 +01:00

134 lines
3.5 KiB
TypeScript

import path from "node:path";
import { loadJsonFile, saveJsonFile } from "openclaw/plugin-sdk/json-store";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
const CACHE_VERSION = 1;
export interface CachedSticker {
fileId: string;
fileUniqueId: string;
emoji?: string;
setName?: string;
description: string;
cachedAt: string;
receivedFrom?: string;
}
interface StickerCache {
version: number;
stickers: Record<string, CachedSticker>;
}
function getCacheFile(): string {
return path.join(resolveStateDir(), "telegram", "sticker-cache.json");
}
function loadCache(): StickerCache {
const data = loadJsonFile(getCacheFile());
if (!data || typeof data !== "object") {
return { version: CACHE_VERSION, stickers: {} };
}
const cache = data as StickerCache;
if (cache.version !== CACHE_VERSION) {
// Future: handle migration if needed
return { version: CACHE_VERSION, stickers: {} };
}
return cache;
}
function saveCache(cache: StickerCache): void {
saveJsonFile(getCacheFile(), cache);
}
/**
* Get a cached sticker by its unique ID.
*/
export function getCachedSticker(fileUniqueId: string): CachedSticker | null {
const cache = loadCache();
return cache.stickers[fileUniqueId] ?? null;
}
/**
* Add or update a sticker in the cache.
*/
export function cacheSticker(sticker: CachedSticker): void {
const cache = loadCache();
cache.stickers[sticker.fileUniqueId] = sticker;
saveCache(cache);
}
/**
* Search cached stickers by text query (fuzzy match on description + emoji + setName).
*/
export function searchStickers(query: string, limit = 10): CachedSticker[] {
const cache = loadCache();
const queryLower = normalizeLowercaseStringOrEmpty(query);
const results: Array<{ sticker: CachedSticker; score: number }> = [];
for (const sticker of Object.values(cache.stickers)) {
let score = 0;
const descLower = normalizeLowercaseStringOrEmpty(sticker.description);
// Exact substring match in description
if (descLower.includes(queryLower)) {
score += 10;
}
// Word-level matching
const queryWords = queryLower.split(/\s+/).filter(Boolean);
const descWords = descLower.split(/\s+/);
for (const qWord of queryWords) {
if (descWords.some((dWord) => dWord.includes(qWord))) {
score += 5;
}
}
// Emoji match
if (sticker.emoji && query.includes(sticker.emoji)) {
score += 8;
}
// Set name match
if (normalizeLowercaseStringOrEmpty(sticker.setName).includes(queryLower)) {
score += 3;
}
if (score > 0) {
results.push({ sticker, score });
}
}
return results
.toSorted((a, b) => b.score - a.score)
.slice(0, limit)
.map((r) => r.sticker);
}
/**
* Get all cached stickers (for debugging/listing).
*/
export function getAllCachedStickers(): CachedSticker[] {
const cache = loadCache();
return Object.values(cache.stickers);
}
/**
* Get cache statistics.
*/
export function getCacheStats(): { count: number; oldestAt?: string; newestAt?: string } {
const cache = loadCache();
const stickers = Object.values(cache.stickers);
if (stickers.length === 0) {
return { count: 0 };
}
const sorted = [...stickers].toSorted(
(a, b) => new Date(a.cachedAt).getTime() - new Date(b.cachedAt).getTime(),
);
return {
count: stickers.length,
oldestAt: sorted[0]?.cachedAt,
newestAt: sorted[sorted.length - 1]?.cachedAt,
};
}