From 0e3ab476ad73cab4fa6c593f23f885daccc5f1dd Mon Sep 17 00:00:00 2001 From: Muhammed Mukhthar CM Date: Fri, 23 Jan 2026 08:05:03 +0000 Subject: [PATCH] Telegram-user: add poll sending --- extensions/telegram-user/src/channel.ts | 8 ++++ extensions/telegram-user/src/send.ts | 63 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/extensions/telegram-user/src/channel.ts b/extensions/telegram-user/src/channel.ts index 797c79633..57c128d0e 100644 --- a/extensions/telegram-user/src/channel.ts +++ b/extensions/telegram-user/src/channel.ts @@ -27,6 +27,7 @@ import { normalizeTelegramUserMessagingTarget, sendMediaTelegramUser, sendMessageTelegramUser, + sendPollTelegramUser, } from "./send.js"; import { resolveTelegramUserSessionPath } from "./session.js"; import { getTelegramUserRuntime } from "./runtime.js"; @@ -155,6 +156,7 @@ export const telegramUserPlugin: ChannelPlugin = { chunker: (text, limit) => getTelegramUserRuntime().channel.text.chunkMarkdownText(text, limit), textChunkLimit: 4000, + pollMaxOptions: 10, sendText: async ({ to, text, accountId }) => { const result = await sendMessageTelegramUser(to, text, { accountId: accountId ?? undefined }); return { channel: "telegram-user", ...result }; @@ -166,6 +168,12 @@ export const telegramUserPlugin: ChannelPlugin = { }); return { channel: "telegram-user", ...result }; }, + sendPoll: async ({ to, poll, accountId }) => { + const result = await sendPollTelegramUser(to, poll, { + accountId: accountId ?? undefined, + }); + return { channel: "telegram-user", ...result }; + }, }, auth: { login: async ({ cfg, accountId, runtime }) => { diff --git a/extensions/telegram-user/src/send.ts b/extensions/telegram-user/src/send.ts index e52412d7f..9b26a8bab 100644 --- a/extensions/telegram-user/src/send.ts +++ b/extensions/telegram-user/src/send.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import type { TelegramClient } from "@mtcute/node"; import { InputMedia } from "@mtcute/core"; +import type { PollInput } from "clawdbot/plugin-sdk"; import { getTelegramUserRuntime } from "./runtime.js"; import { resolveTelegramUserAccount } from "./accounts.js"; @@ -13,6 +14,12 @@ export type TelegramUserSendResult = { chatId: string; }; +type NormalizedPollInput = { + question: string; + options: string[]; + maxSelections: number; +}; + export type TelegramUserSendOpts = { client?: TelegramClient; accountId?: string; @@ -47,6 +54,32 @@ function resolveTelegramUserPeer(target: string): number | string { return target; } +function normalizePollInput(input: PollInput): NormalizedPollInput { + const question = input.question.trim(); + if (!question) { + throw new Error("Poll question is required"); + } + const options = (input.options ?? []).map((option) => option.trim()).filter(Boolean); + if (options.length < 2) { + throw new Error("Poll requires at least 2 options"); + } + if (options.length > 10) { + throw new Error("Poll supports at most 10 options"); + } + const maxSelectionsRaw = input.maxSelections; + const maxSelections = + typeof maxSelectionsRaw === "number" && Number.isFinite(maxSelectionsRaw) + ? Math.floor(maxSelectionsRaw) + : 1; + if (maxSelections < 1) { + throw new Error("maxSelections must be at least 1"); + } + if (maxSelections > options.length) { + throw new Error("maxSelections cannot exceed option count"); + } + return { question, options, maxSelections }; +} + async function resolveClient(params: { client?: TelegramClient; cfg: CoreConfig; @@ -126,3 +159,33 @@ export async function sendMediaTelegramUser( } } } + +export async function sendPollTelegramUser( + to: string, + poll: PollInput, + opts: TelegramUserSendOpts = {}, +): Promise { + const cfg = getTelegramUserRuntime().config.loadConfig() as CoreConfig; + const { client, stopOnDone } = await resolveClient({ + client: opts.client, + cfg, + accountId: opts.accountId, + }); + try { + const target = resolveTelegramUserPeer(normalizeTarget(to)); + const normalized = normalizePollInput(poll); + const input = InputMedia.poll({ + question: normalized.question, + answers: normalized.options, + multiple: normalized.maxSelections > 1, + }); + const message = await client.sendMedia(target, input, { + ...(opts.replyToId ? { replyTo: opts.replyToId } : {}), + }); + return { messageId: String(message.id), chatId: String(target) }; + } finally { + if (stopOnDone) { + await client.destroy(); + } + } +}