feat(telegram): add sticker support with vision caching
Add support for receiving and sending Telegram stickers: Inbound: - Receive static WEBP stickers (skip animated/video) - Process stickers through dedicated vision call for descriptions - Cache vision descriptions to avoid repeated API calls - Graceful error handling for fetch failures Outbound: - Add sticker action to send stickers by fileId - Add sticker-search action to find cached stickers by query - Accept stickerId from shared schema, convert to fileId Cache: - Store sticker metadata (fileId, emoji, setName, description) - Fuzzy search by description, emoji, and set name - Persist to ~/.clawdbot/telegram/sticker-cache.json Config: - Single `channels.telegram.actions.sticker` option enables both send and search actions 🤖 AI-assisted: Built with Claude Code (claude-opus-4-5) Testing: Fully tested - unit tests pass, live tested on dev gateway The contributor understands and has reviewed all code changes. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,9 @@ import {
|
||||
editMessageTelegram,
|
||||
reactMessageTelegram,
|
||||
sendMessageTelegram,
|
||||
sendStickerTelegram,
|
||||
} from "../../telegram/send.js";
|
||||
import { getCacheStats, searchStickers } from "../../telegram/sticker-cache.js";
|
||||
import { resolveTelegramToken } from "../../telegram/token.js";
|
||||
import {
|
||||
resolveTelegramInlineButtonsScope,
|
||||
@@ -255,5 +257,64 @@ export async function handleTelegramAction(
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "sendSticker") {
|
||||
if (!isActionEnabled("sticker")) {
|
||||
throw new Error(
|
||||
"Telegram sticker actions are disabled. Set channels.telegram.actions.sticker to true.",
|
||||
);
|
||||
}
|
||||
const to = readStringParam(params, "to", { required: true });
|
||||
const fileId = readStringParam(params, "fileId", { required: true });
|
||||
const replyToMessageId = readNumberParam(params, "replyToMessageId", {
|
||||
integer: true,
|
||||
});
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
integer: true,
|
||||
});
|
||||
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||
);
|
||||
}
|
||||
const result = await sendStickerTelegram(to, fileId, {
|
||||
token,
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId: replyToMessageId ?? undefined,
|
||||
messageThreadId: messageThreadId ?? undefined,
|
||||
});
|
||||
return jsonResult({
|
||||
ok: true,
|
||||
messageId: result.messageId,
|
||||
chatId: result.chatId,
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "searchSticker") {
|
||||
if (!isActionEnabled("sticker")) {
|
||||
throw new Error(
|
||||
"Telegram sticker actions are disabled. Set channels.telegram.actions.sticker to true.",
|
||||
);
|
||||
}
|
||||
const query = readStringParam(params, "query", { required: true });
|
||||
const limit = readNumberParam(params, "limit", { integer: true }) ?? 5;
|
||||
const results = searchStickers(query, limit);
|
||||
return jsonResult({
|
||||
ok: true,
|
||||
count: results.length,
|
||||
stickers: results.map((s) => ({
|
||||
fileId: s.fileId,
|
||||
emoji: s.emoji,
|
||||
description: s.description,
|
||||
setName: s.setName,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "stickerCacheStats") {
|
||||
const stats = getCacheStats();
|
||||
return jsonResult({ ok: true, ...stats });
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported Telegram action: ${action}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user