feat(telegram): add sendPoll support (#16193) (#16209)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: b58492cfed
Co-authored-by: robbyczgw-cla <239660374+robbyczgw-cla@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
This commit is contained in:
Robby
2026-02-14 18:34:30 +01:00
committed by GitHub
parent fc5d147d1b
commit 8e5689a84d
21 changed files with 364 additions and 11 deletions

View File

@@ -568,11 +568,36 @@ async function handlePollAction(ctx: ResolvedActionContext): Promise<MessageActi
if (options.length < 2) {
throw new Error("pollOption requires at least two values");
}
const silent = readBooleanParam(params, "silent");
const allowMultiselect = readBooleanParam(params, "pollMulti") ?? false;
const pollAnonymous = readBooleanParam(params, "pollAnonymous");
const pollPublic = readBooleanParam(params, "pollPublic");
if (pollAnonymous && pollPublic) {
throw new Error("pollAnonymous and pollPublic are mutually exclusive");
}
const isAnonymous = pollAnonymous ? true : pollPublic ? false : undefined;
const durationHours = readNumberParam(params, "pollDurationHours", {
integer: true,
});
const durationSeconds = readNumberParam(params, "pollDurationSeconds", {
integer: true,
});
const maxSelections = allowMultiselect ? Math.max(2, options.length) : 1;
const threadId = readStringParam(params, "threadId");
const slackAutoThreadId =
channel === "slack" && !threadId
? resolveSlackAutoThreadId({ to, toolContext: input.toolContext })
: undefined;
const telegramAutoThreadId =
channel === "telegram" && !threadId
? resolveTelegramAutoThreadId({ to, toolContext: input.toolContext })
: undefined;
const resolvedThreadId = threadId ?? slackAutoThreadId ?? telegramAutoThreadId;
if (resolvedThreadId && !params.threadId) {
params.threadId = resolvedThreadId;
}
const base = typeof params.message === "string" ? params.message : "";
await maybeApplyCrossContextMarker({
cfg,
@@ -595,12 +620,16 @@ async function handlePollAction(ctx: ResolvedActionContext): Promise<MessageActi
gateway,
toolContext: input.toolContext,
dryRun,
silent: silent ?? undefined,
},
to,
question,
options,
maxSelections,
durationSeconds: durationSeconds ?? undefined,
durationHours: durationHours ?? undefined,
threadId: resolvedThreadId ?? undefined,
isAnonymous,
});
return {

View File

@@ -69,8 +69,13 @@ type MessagePollParams = {
question: string;
options: string[];
maxSelections?: number;
durationSeconds?: number;
durationHours?: number;
channel?: string;
accountId?: string;
threadId?: string;
silent?: boolean;
isAnonymous?: boolean;
dryRun?: boolean;
cfg?: OpenClawConfig;
gateway?: MessageGatewayOptions;
@@ -83,6 +88,7 @@ export type MessagePollResult = {
question: string;
options: string[];
maxSelections: number;
durationSeconds: number | null;
durationHours: number | null;
via: "gateway";
result?: {
@@ -239,6 +245,7 @@ export async function sendPoll(params: MessagePollParams): Promise<MessagePollRe
question: params.question,
options: params.options,
maxSelections: params.maxSelections,
durationSeconds: params.durationSeconds,
durationHours: params.durationHours,
};
const plugin = getChannelPlugin(channel);
@@ -257,6 +264,7 @@ export async function sendPoll(params: MessagePollParams): Promise<MessagePollRe
question: normalized.question,
options: normalized.options,
maxSelections: normalized.maxSelections,
durationSeconds: normalized.durationSeconds ?? null,
durationHours: normalized.durationHours ?? null,
via: "gateway",
dryRun: true,
@@ -279,8 +287,13 @@ export async function sendPoll(params: MessagePollParams): Promise<MessagePollRe
question: normalized.question,
options: normalized.options,
maxSelections: normalized.maxSelections,
durationSeconds: normalized.durationSeconds,
durationHours: normalized.durationHours,
threadId: params.threadId,
silent: params.silent,
isAnonymous: params.isAnonymous,
channel,
accountId: params.accountId,
idempotencyKey: params.idempotencyKey ?? randomIdempotencyKey(),
},
timeoutMs: gateway.timeoutMs,
@@ -295,6 +308,7 @@ export async function sendPoll(params: MessagePollParams): Promise<MessagePollRe
question: normalized.question,
options: normalized.options,
maxSelections: normalized.maxSelections,
durationSeconds: normalized.durationSeconds ?? null,
durationHours: normalized.durationHours ?? null,
via: "gateway",
result,

View File

@@ -145,7 +145,10 @@ export async function executePollAction(params: {
question: string;
options: string[];
maxSelections: number;
durationSeconds?: number;
durationHours?: number;
threadId?: string;
isAnonymous?: boolean;
}): Promise<{
handledBy: "plugin" | "core";
payload: unknown;
@@ -178,8 +181,13 @@ export async function executePollAction(params: {
question: params.question,
options: params.options,
maxSelections: params.maxSelections,
durationSeconds: params.durationSeconds ?? undefined,
durationHours: params.durationHours ?? undefined,
channel: params.ctx.channel,
accountId: params.ctx.accountId ?? undefined,
threadId: params.threadId ?? undefined,
silent: params.ctx.silent ?? undefined,
isAnonymous: params.isAnonymous ?? undefined,
dryRun: params.ctx.dryRun,
gateway: params.ctx.gateway,
});