fix(telegram): wire sendPollTelegram into channel action handler (#16977)

The Telegram channel adapter listed no 'poll' action, so agents could
not create polls via the unified action interface. The underlying
sendPollTelegram function was already implemented but unreachable.

Changes:
- telegram.ts: add 'poll' to listActions (enabled by default via gate),
  add handleAction branch that reads pollQuestion/pollOption params and
  delegates to handleTelegramAction with action 'sendPoll'.
- telegram-actions.ts: add 'sendPoll' handler that validates question,
  options (≥2), and forwards to sendPollTelegram with threading, silent,
  and anonymous options.
- actions.test.ts: add test verifying poll action routes correctly.

Fixes #16977
This commit is contained in:
artale
2026-02-16 12:20:51 +01:00
committed by Peter Steinberger
parent 068b9c9749
commit 7bb9a7dcfc
4 changed files with 92 additions and 0 deletions

View File

@@ -402,5 +402,42 @@ export async function handleTelegramAction(
return jsonResult({ ok: true, ...stats });
}
if (action === "sendPoll") {
const to = readStringParam(params, "to", { required: true });
const question = readStringParam(params, "question") ?? readStringParam(params, "pollQuestion");
if (!question) {
throw new Error("sendPoll requires 'question'");
}
const options = (params.options ?? params.pollOption) as string[] | undefined;
if (!options || options.length < 2) {
throw new Error("sendPoll requires at least 2 options");
}
const maxSelections =
typeof params.maxSelections === "number" ? params.maxSelections : undefined;
const isAnonymous = typeof params.isAnonymous === "boolean" ? params.isAnonymous : undefined;
const silent = typeof params.silent === "boolean" ? params.silent : undefined;
const replyToMessageId = readNumberParam(params, "replyTo");
const messageThreadId = readNumberParam(params, "threadId");
const pollAccountId = readStringParam(params, "accountId");
const res = await sendPollTelegram(
to,
{ question, options, maxSelections },
{
accountId: pollAccountId?.trim() || undefined,
replyToMessageId,
messageThreadId,
isAnonymous,
silent,
},
);
return jsonResult({
ok: true,
messageId: res.messageId,
chatId: res.chatId,
pollId: res.pollId,
});
}
throw new Error(`Unsupported Telegram action: ${action}`);
}

View File

@@ -489,6 +489,31 @@ describe("telegramMessageActions", () => {
expect(String(call.messageId)).toBe("456");
expect(call.emoji).toBe("ok");
});
it("routes poll action to sendPoll with question and options", async () => {
const cfg = { channels: { telegram: { botToken: "tok" } } } as OpenClawConfig;
await telegramMessageActions.handleAction({
action: "poll",
params: {
to: "-100123",
pollQuestion: "Ready?",
pollOption: ["Yes", "No", "Maybe"],
},
cfg,
accountId: undefined,
});
expect(handleTelegramAction).toHaveBeenCalledWith(
expect.objectContaining({
action: "sendPoll",
to: "-100123",
question: "Ready?",
options: ["Yes", "No", "Maybe"],
}),
cfg,
);
});
});
describe("signalMessageActions", () => {

View File

@@ -52,6 +52,9 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
const gate = (key: keyof TelegramActionConfig, defaultValue = true) =>
gates.some((g) => g(key, defaultValue));
const actions = new Set<ChannelMessageActionName>(["send"]);
if (gate("poll")) {
actions.add("poll");
}
if (gate("reactions")) {
actions.add("react");
}
@@ -232,6 +235,31 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
);
}
if (action === "poll") {
const to = readStringParam(params, "to", { required: true });
const question =
readStringParam(params, "pollQuestion") ??
readStringParam(params, "question", { required: true });
const options =
readStringArrayParam(params, "pollOption") ?? readStringArrayParam(params, "options");
const threadId = readStringParam(params, "threadId");
const replyTo = readStringParam(params, "replyTo");
const silent = typeof params.silent === "boolean" ? params.silent : undefined;
return await handleTelegramAction(
{
action: "sendPoll",
to,
question,
options,
replyTo: replyTo != null ? Number(replyTo) : undefined,
threadId: threadId != null ? Number(threadId) : undefined,
silent,
accountId: accountId ?? undefined,
},
cfg,
);
}
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
},
};

View File

@@ -18,6 +18,8 @@ export type TelegramActionConfig = {
polls?: boolean;
deleteMessage?: boolean;
editMessage?: boolean;
/** Enable poll actions (sendPoll). */
poll?: boolean;
/** Enable sticker actions (send and search). */
sticker?: boolean;
};