mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 12:58:38 +00:00
Fix Telegram poll action wiring
This commit is contained in:
@@ -262,8 +262,11 @@ function buildPollSchema() {
|
|||||||
return {
|
return {
|
||||||
pollQuestion: Type.Optional(Type.String()),
|
pollQuestion: Type.Optional(Type.String()),
|
||||||
pollOption: Type.Optional(Type.Array(Type.String())),
|
pollOption: Type.Optional(Type.Array(Type.String())),
|
||||||
|
pollDurationSeconds: Type.Optional(Type.Number()),
|
||||||
pollDurationHours: Type.Optional(Type.Number()),
|
pollDurationHours: Type.Optional(Type.Number()),
|
||||||
pollMulti: Type.Optional(Type.Boolean()),
|
pollMulti: Type.Optional(Type.Boolean()),
|
||||||
|
pollAnonymous: Type.Optional(Type.Boolean()),
|
||||||
|
pollPublic: Type.Optional(Type.Boolean()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ const sendStickerTelegram = vi.fn(async () => ({
|
|||||||
messageId: "456",
|
messageId: "456",
|
||||||
chatId: "123",
|
chatId: "123",
|
||||||
}));
|
}));
|
||||||
|
const sendPollTelegram = vi.fn(async () => ({
|
||||||
|
messageId: "999",
|
||||||
|
chatId: "123",
|
||||||
|
pollId: "poll-1",
|
||||||
|
}));
|
||||||
const deleteMessageTelegram = vi.fn(async () => ({ ok: true }));
|
const deleteMessageTelegram = vi.fn(async () => ({ ok: true }));
|
||||||
const originalToken = process.env.TELEGRAM_BOT_TOKEN;
|
const originalToken = process.env.TELEGRAM_BOT_TOKEN;
|
||||||
|
|
||||||
@@ -18,6 +23,7 @@ vi.mock("../../telegram/send.js", () => ({
|
|||||||
reactMessageTelegram: (...args: unknown[]) => reactMessageTelegram(...args),
|
reactMessageTelegram: (...args: unknown[]) => reactMessageTelegram(...args),
|
||||||
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
|
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
|
||||||
sendStickerTelegram: (...args: unknown[]) => sendStickerTelegram(...args),
|
sendStickerTelegram: (...args: unknown[]) => sendStickerTelegram(...args),
|
||||||
|
sendPollTelegram: (...args: unknown[]) => sendPollTelegram(...args),
|
||||||
deleteMessageTelegram: (...args: unknown[]) => deleteMessageTelegram(...args),
|
deleteMessageTelegram: (...args: unknown[]) => deleteMessageTelegram(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -49,6 +55,7 @@ describe("handleTelegramAction", () => {
|
|||||||
reactMessageTelegram.mockClear();
|
reactMessageTelegram.mockClear();
|
||||||
sendMessageTelegram.mockClear();
|
sendMessageTelegram.mockClear();
|
||||||
sendStickerTelegram.mockClear();
|
sendStickerTelegram.mockClear();
|
||||||
|
sendPollTelegram.mockClear();
|
||||||
deleteMessageTelegram.mockClear();
|
deleteMessageTelegram.mockClear();
|
||||||
process.env.TELEGRAM_BOT_TOKEN = "tok";
|
process.env.TELEGRAM_BOT_TOKEN = "tok";
|
||||||
});
|
});
|
||||||
@@ -362,6 +369,30 @@ describe("handleTelegramAction", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sends a poll", async () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { telegram: { botToken: "tok" } },
|
||||||
|
} as OpenClawConfig;
|
||||||
|
await handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "poll",
|
||||||
|
to: "123",
|
||||||
|
question: "Ready?",
|
||||||
|
options: ["Yes", "No"],
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
expect(sendPollTelegram).toHaveBeenCalledWith(
|
||||||
|
"123",
|
||||||
|
expect.objectContaining({
|
||||||
|
question: "Ready?",
|
||||||
|
options: ["Yes", "No"],
|
||||||
|
maxSelections: 1,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({ token: "tok" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("respects deleteMessage gating", async () => {
|
it("respects deleteMessage gating", async () => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
channels: {
|
channels: {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
editMessageTelegram,
|
editMessageTelegram,
|
||||||
reactMessageTelegram,
|
reactMessageTelegram,
|
||||||
sendMessageTelegram,
|
sendMessageTelegram,
|
||||||
|
sendPollTelegram,
|
||||||
sendStickerTelegram,
|
sendStickerTelegram,
|
||||||
} from "../../telegram/send.js";
|
} from "../../telegram/send.js";
|
||||||
import { getCacheStats, searchStickers } from "../../telegram/sticker-cache.js";
|
import { getCacheStats, searchStickers } from "../../telegram/sticker-cache.js";
|
||||||
@@ -213,6 +214,66 @@ export async function handleTelegramAction(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === "poll") {
|
||||||
|
if (!isActionEnabled("polls")) {
|
||||||
|
throw new Error("Telegram polls are disabled.");
|
||||||
|
}
|
||||||
|
const to = readStringParam(params, "to", { required: true });
|
||||||
|
const question = readStringParam(params, "question", { required: true });
|
||||||
|
const options = (params.options ?? params.answers) as unknown;
|
||||||
|
if (!Array.isArray(options)) {
|
||||||
|
throw new Error("options must be an array of strings");
|
||||||
|
}
|
||||||
|
const pollOptions = options.filter((option): option is string => typeof option === "string");
|
||||||
|
if (pollOptions.length !== options.length) {
|
||||||
|
throw new Error("options must be an array of strings");
|
||||||
|
}
|
||||||
|
const durationSeconds = readNumberParam(params, "durationSeconds", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const durationHours = readNumberParam(params, "durationHours", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const replyToMessageId = readNumberParam(params, "replyToMessageId", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const maxSelections =
|
||||||
|
typeof params.allowMultiselect === "boolean" && params.allowMultiselect === true ? 2 : 1;
|
||||||
|
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 sendPollTelegram(
|
||||||
|
to,
|
||||||
|
{
|
||||||
|
question,
|
||||||
|
options: pollOptions,
|
||||||
|
maxSelections,
|
||||||
|
durationSeconds: durationSeconds ?? undefined,
|
||||||
|
durationHours: durationHours ?? undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
replyToMessageId: replyToMessageId ?? undefined,
|
||||||
|
messageThreadId: messageThreadId ?? undefined,
|
||||||
|
silent: typeof params.silent === "boolean" ? params.silent : undefined,
|
||||||
|
isAnonymous: typeof params.isAnonymous === "boolean" ? params.isAnonymous : undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return jsonResult({
|
||||||
|
ok: true,
|
||||||
|
messageId: result.messageId,
|
||||||
|
chatId: result.chatId,
|
||||||
|
pollId: result.pollId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (action === "deleteMessage") {
|
if (action === "deleteMessage") {
|
||||||
if (!isActionEnabled("deleteMessage")) {
|
if (!isActionEnabled("deleteMessage")) {
|
||||||
throw new Error("Telegram deleteMessage is disabled.");
|
throw new Error("Telegram deleteMessage is disabled.");
|
||||||
|
|||||||
@@ -128,6 +128,42 @@ describe("discord message actions", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("telegram message actions", () => {
|
||||||
|
it("lists poll action when telegram is configured", () => {
|
||||||
|
const cfg = { channels: { telegram: { botToken: "t0" } } } as OpenClawConfig;
|
||||||
|
const actions = telegramMessageActions.listActions?.({ cfg }) ?? [];
|
||||||
|
expect(actions).toContain("poll");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("routes poll with normalized params", async () => {
|
||||||
|
await telegramMessageActions.handleAction({
|
||||||
|
action: "poll",
|
||||||
|
params: {
|
||||||
|
to: "123",
|
||||||
|
pollQuestion: "Ready?",
|
||||||
|
pollOption: ["Yes", "No"],
|
||||||
|
pollMulti: true,
|
||||||
|
pollDurationSeconds: 60,
|
||||||
|
},
|
||||||
|
cfg: { channels: { telegram: { botToken: "tok" } } } as OpenClawConfig,
|
||||||
|
accountId: "ops",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
action: "poll",
|
||||||
|
to: "123",
|
||||||
|
question: "Ready?",
|
||||||
|
options: ["Yes", "No"],
|
||||||
|
allowMultiselect: true,
|
||||||
|
durationSeconds: 60,
|
||||||
|
accountId: "ops",
|
||||||
|
}),
|
||||||
|
expect.any(Object),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("handleDiscordMessageAction", () => {
|
describe("handleDiscordMessageAction", () => {
|
||||||
it("forwards context accountId for send", async () => {
|
it("forwards context accountId for send", async () => {
|
||||||
await handleDiscordMessageAction({
|
await handleDiscordMessageAction({
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
|
|||||||
if (gate("deleteMessage")) {
|
if (gate("deleteMessage")) {
|
||||||
actions.add("delete");
|
actions.add("delete");
|
||||||
}
|
}
|
||||||
|
if (gate("polls")) {
|
||||||
|
actions.add("poll");
|
||||||
|
}
|
||||||
if (gate("editMessage")) {
|
if (gate("editMessage")) {
|
||||||
actions.add("edit");
|
actions.add("edit");
|
||||||
}
|
}
|
||||||
@@ -116,6 +119,40 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === "poll") {
|
||||||
|
const to = readStringParam(params, "to", { required: true });
|
||||||
|
const question = readStringParam(params, "pollQuestion", { required: true });
|
||||||
|
const options = readStringArrayParam(params, "pollOption", { required: true }) ?? [];
|
||||||
|
const allowMultiselect = typeof params.pollMulti === "boolean" ? params.pollMulti : undefined;
|
||||||
|
const durationSeconds = readNumberParam(params, "pollDurationSeconds", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const durationHours = readNumberParam(params, "pollDurationHours", {
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const replyToMessageId = readNumberParam(params, "replyTo", { integer: true });
|
||||||
|
const messageThreadId = readNumberParam(params, "threadId", { integer: true });
|
||||||
|
const silent = typeof params.silent === "boolean" ? params.silent : undefined;
|
||||||
|
const isAnonymous = typeof params.isAnonymous === "boolean" ? params.isAnonymous : undefined;
|
||||||
|
return await handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "poll",
|
||||||
|
to,
|
||||||
|
question,
|
||||||
|
options,
|
||||||
|
allowMultiselect,
|
||||||
|
durationSeconds: durationSeconds ?? undefined,
|
||||||
|
durationHours: durationHours ?? undefined,
|
||||||
|
replyToMessageId: replyToMessageId ?? undefined,
|
||||||
|
messageThreadId: messageThreadId ?? undefined,
|
||||||
|
silent,
|
||||||
|
isAnonymous,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (action === "delete") {
|
if (action === "delete") {
|
||||||
const chatId =
|
const chatId =
|
||||||
readStringOrNumberParam(params, "chatId") ??
|
readStringOrNumberParam(params, "chatId") ??
|
||||||
|
|||||||
Reference in New Issue
Block a user