diff --git a/extensions/telegram/src/channel-actions.test.ts b/extensions/telegram/src/channel-actions.test.ts index 0e5170431b1..0addd92af78 100644 --- a/extensions/telegram/src/channel-actions.test.ts +++ b/extensions/telegram/src/channel-actions.test.ts @@ -1,12 +1,8 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { telegramMessageActions, telegramMessageActionRuntime } from "./channel-actions.js"; const handleTelegramActionMock = vi.hoisted(() => vi.fn()); - -vi.mock("../../../src/agents/tools/telegram-actions.js", () => ({ - handleTelegramAction: (...args: unknown[]) => handleTelegramActionMock(...args), -})); - -import { telegramMessageActions } from "./channel-actions.js"; +const originalHandleTelegramAction = telegramMessageActionRuntime.handleTelegramAction; describe("telegramMessageActions", () => { beforeEach(() => { @@ -15,6 +11,12 @@ describe("telegramMessageActions", () => { content: [], details: {}, }); + telegramMessageActionRuntime.handleTelegramAction = (...args) => + handleTelegramActionMock(...args); + }); + + afterEach(() => { + telegramMessageActionRuntime.handleTelegramAction = originalHandleTelegramAction; }); it("allows interactive-only sends", async () => { diff --git a/extensions/telegram/src/channel-actions.ts b/extensions/telegram/src/channel-actions.ts index 50c472ea600..c81b7e1aec6 100644 --- a/extensions/telegram/src/channel-actions.ts +++ b/extensions/telegram/src/channel-actions.ts @@ -8,6 +8,8 @@ import { handleTelegramAction } from "openclaw/plugin-sdk/agent-runtime"; import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param"; import { resolveReactionMessageId } from "openclaw/plugin-sdk/channel-runtime"; import { + createMessageToolButtonsSchema, + createTelegramPollExtraToolSchemas, createUnionActionGate, listTokenSourcedAccounts, } from "openclaw/plugin-sdk/channel-runtime"; @@ -28,6 +30,10 @@ import { isTelegramInlineButtonsEnabled } from "./inline-buttons.js"; const providerId = "telegram"; +export const telegramMessageActionRuntime = { + handleTelegramAction, +}; + function readTelegramSendParams(params: Record) { const to = readStringParam(params, "to", { required: true }); const mediaUrl = readStringParam(params, "media", { trim: false }); @@ -138,13 +144,44 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { ); return buttonsEnabled ? (["interactive", "buttons"] as const) : []; }, + getToolSchema: ({ cfg }) => { + const accounts = listTokenSourcedAccounts(listEnabledTelegramAccounts(cfg)); + if (accounts.length === 0) { + return null; + } + const buttonsEnabled = accounts.some((account) => + isTelegramInlineButtonsEnabled({ cfg, accountId: account.accountId }), + ); + const pollEnabledForAnyAccount = accounts.some((account) => { + const accountGate = createTelegramActionGate({ + cfg, + accountId: account.accountId, + }); + return resolveTelegramPollActionGateState(accountGate).enabled; + }); + const entries = []; + if (buttonsEnabled) { + entries.push({ + properties: { + buttons: createMessageToolButtonsSchema(), + }, + }); + } + if (pollEnabledForAnyAccount) { + entries.push({ + properties: createTelegramPollExtraToolSchemas(), + visibility: "all-configured" as const, + }); + } + return entries.length > 0 ? entries : null; + }, extractToolSend: ({ args }) => { return extractToolSend(args, "sendMessage"); }, handleAction: async ({ action, params, cfg, accountId, mediaLocalRoots, toolContext }) => { if (action === "send") { const sendParams = readTelegramSendParams(params); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "sendMessage", ...sendParams, @@ -159,7 +196,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const messageId = resolveReactionMessageId({ args: params, toolContext }); const emoji = readStringParam(params, "emoji", { allowEmpty: true }); const remove = readBooleanParam(params, "remove"); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "react", chatId: readTelegramChatIdParam(params), @@ -192,7 +229,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const pollPublic = readBooleanParam(params, "pollPublic"); const isAnonymous = resolveTelegramPollVisibility({ pollAnonymous, pollPublic }); const silent = readBooleanParam(params, "silent"); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "poll", to, @@ -215,7 +252,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { if (action === "delete") { const chatId = readTelegramChatIdParam(params); const messageId = readTelegramMessageIdParam(params); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "deleteMessage", chatId, @@ -232,7 +269,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const messageId = readTelegramMessageIdParam(params); const message = readStringParam(params, "message", { required: true, allowEmpty: false }); const buttons = params.buttons; - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "editMessage", chatId, @@ -254,7 +291,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const fileId = stickerIds?.[0] ?? readStringParam(params, "fileId", { required: true }); const replyToMessageId = readNumberParam(params, "replyTo", { integer: true }); const messageThreadId = readNumberParam(params, "threadId", { integer: true }); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "sendSticker", to, @@ -271,7 +308,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { if (action === "sticker-search") { const query = readStringParam(params, "query", { required: true }); const limit = readNumberParam(params, "limit", { integer: true }); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "searchSticker", query, @@ -288,7 +325,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { const name = readStringParam(params, "name", { required: true }); const iconColor = readNumberParam(params, "iconColor", { integer: true }); const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId"); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "createForumTopic", chatId, @@ -312,7 +349,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = { } const name = readStringParam(params, "name"); const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId"); - return await handleTelegramAction( + return await telegramMessageActionRuntime.handleTelegramAction( { action: "editForumTopic", chatId, diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index d89d74da289..3e120e567af 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -252,6 +252,8 @@ const telegramMessageActions: ChannelMessageActionAdapter = { getTelegramRuntime().channel.telegram.messageActions?.listActions?.(ctx) ?? [], getCapabilities: (ctx) => getTelegramRuntime().channel.telegram.messageActions?.getCapabilities?.(ctx) ?? [], + getToolSchema: (ctx) => + getTelegramRuntime().channel.telegram.messageActions?.getToolSchema?.(ctx) ?? null, extractToolSend: (ctx) => getTelegramRuntime().channel.telegram.messageActions?.extractToolSend?.(ctx) ?? null, handleAction: async (ctx) => {