feat: unify poll support

Co-authored-by: DBH <5251425+dbhurley@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-06 04:43:35 +00:00
parent 1f4d9e83ff
commit 0b27964693
19 changed files with 360 additions and 118 deletions

View File

@@ -1,30 +1,53 @@
import type { CliDeps } from "../cli/deps.js";
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
import { success } from "../globals.js";
import { normalizePollInput, type PollInput } from "../polls.js";
import type { RuntimeEnv } from "../runtime.js";
function parseIntOption(value: unknown, label: string): number | undefined {
if (value === undefined || value === null) return undefined;
if (typeof value !== "string" || value.trim().length === 0) return undefined;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed)) {
throw new Error(`${label} must be a number`);
}
return parsed;
}
export async function pollCommand(
opts: {
to: string;
question: string;
options: string[];
selectableCount?: number;
option: string[];
maxSelections?: string;
durationHours?: string;
provider?: string;
json?: boolean;
dryRun?: boolean;
},
_deps: CliDeps,
runtime: RuntimeEnv,
) {
if (opts.options.length < 2) {
throw new Error("Poll requires at least 2 options");
}
if (opts.options.length > 12) {
throw new Error("Poll supports at most 12 options");
const provider = (opts.provider ?? "whatsapp").toLowerCase();
if (provider !== "whatsapp" && provider !== "discord") {
throw new Error(`Unsupported poll provider: ${provider}`);
}
const maxSelections = parseIntOption(opts.maxSelections, "max-selections");
const durationHours = parseIntOption(opts.durationHours, "duration-hours");
const pollInput: PollInput = {
question: opts.question,
options: opts.option,
maxSelections,
durationHours,
};
const maxOptions = provider === "discord" ? 10 : 12;
const normalized = normalizePollInput(pollInput, { maxOptions });
if (opts.dryRun) {
runtime.log(
`[dry-run] would send poll to ${opts.to}:\n Question: ${opts.question}\n Options: ${opts.options.join(", ")}\n Selectable: ${opts.selectableCount ?? 1}`,
`[dry-run] would send poll via ${provider} -> ${opts.to}:\n Question: ${normalized.question}\n Options: ${normalized.options.join(", ")}\n Max selections: ${normalized.maxSelections}`,
);
return;
}
@@ -32,14 +55,17 @@ export async function pollCommand(
const result = await callGateway<{
messageId: string;
toJid?: string;
channelId?: string;
}>({
url: "ws://127.0.0.1:18789",
method: "poll",
params: {
to: opts.to,
question: opts.question,
options: opts.options,
selectableCount: opts.selectableCount ?? 1,
question: normalized.question,
options: normalized.options,
maxSelections: normalized.maxSelections,
durationHours: normalized.durationHours,
provider,
idempotencyKey: randomIdempotencyKey(),
},
timeoutMs: 10_000,
@@ -49,21 +75,23 @@ export async function pollCommand(
runtime.log(
success(
`✅ Poll sent via gateway. Message ID: ${result.messageId ?? "unknown"}`,
`✅ Poll sent via gateway (${provider}). Message ID: ${result.messageId ?? "unknown"}`,
),
);
if (opts.json) {
runtime.log(
JSON.stringify(
{
provider: "whatsapp",
provider,
via: "gateway",
to: opts.to,
toJid: result.toJid,
toJid: result.toJid ?? null,
channelId: result.channelId ?? null,
messageId: result.messageId,
question: opts.question,
options: opts.options,
selectableCount: opts.selectableCount ?? 1,
question: normalized.question,
options: normalized.options,
maxSelections: normalized.maxSelections,
durationHours: normalized.durationHours ?? null,
},
null,
2,