feat(signal): add poll support
Wire up signal-cli's sendPollCreate to the message tool, enabling poll creation in Signal chats and groups. - Add sendPollSignal() function in src/signal/send.ts - Add sendPoll method to signal outbound adapter - Support 2-12 options, single or multi-select Closes #TBD
This commit is contained in:
parent
c9fe062824
commit
fd423fe90c
@ -1,5 +1,5 @@
|
|||||||
import { chunkText } from "../../../auto-reply/chunk.js";
|
import { chunkText } from "../../../auto-reply/chunk.js";
|
||||||
import { sendMessageSignal } from "../../../signal/send.js";
|
import { sendMessageSignal, sendPollSignal } from "../../../signal/send.js";
|
||||||
import { resolveChannelMediaMaxBytes } from "../media-limits.js";
|
import { resolveChannelMediaMaxBytes } from "../media-limits.js";
|
||||||
import type { ChannelOutboundAdapter } from "../types.js";
|
import type { ChannelOutboundAdapter } from "../types.js";
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ export const signalOutbound: ChannelOutboundAdapter = {
|
|||||||
chunker: chunkText,
|
chunker: chunkText,
|
||||||
chunkerMode: "text",
|
chunkerMode: "text",
|
||||||
textChunkLimit: 4000,
|
textChunkLimit: 4000,
|
||||||
|
pollMaxOptions: 12,
|
||||||
sendText: async ({ cfg, to, text, accountId, deps }) => {
|
sendText: async ({ cfg, to, text, accountId, deps }) => {
|
||||||
const send = deps?.sendSignal ?? sendMessageSignal;
|
const send = deps?.sendSignal ?? sendMessageSignal;
|
||||||
const maxBytes = resolveChannelMediaMaxBytes({
|
const maxBytes = resolveChannelMediaMaxBytes({
|
||||||
@ -37,4 +38,11 @@ export const signalOutbound: ChannelOutboundAdapter = {
|
|||||||
});
|
});
|
||||||
return { channel: "signal", ...result };
|
return { channel: "signal", ...result };
|
||||||
},
|
},
|
||||||
|
sendPoll: async ({ to, poll, accountId }) => {
|
||||||
|
const result = await sendPollSignal(to, poll.question, poll.options, {
|
||||||
|
multiSelect: (poll.maxSelections ?? 1) > 1,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
});
|
||||||
|
return { messageId: result.messageId };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -255,3 +255,69 @@ export async function sendReadReceiptSignal(
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SignalPollOpts = SignalRpcOpts & {
|
||||||
|
/** Allow multiple selections (default: true) */
|
||||||
|
multiSelect?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SignalPollResult = {
|
||||||
|
messageId: string;
|
||||||
|
timestamp?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and send a poll to a Signal recipient or group.
|
||||||
|
*/
|
||||||
|
export async function sendPollSignal(
|
||||||
|
to: string,
|
||||||
|
question: string,
|
||||||
|
options: string[],
|
||||||
|
opts: SignalPollOpts = {},
|
||||||
|
): Promise<SignalPollResult> {
|
||||||
|
if (!question?.trim()) {
|
||||||
|
throw new Error("Poll question is required");
|
||||||
|
}
|
||||||
|
if (!options || options.length < 2) {
|
||||||
|
throw new Error("Poll requires at least 2 options");
|
||||||
|
}
|
||||||
|
if (options.length > 12) {
|
||||||
|
throw new Error("Poll cannot have more than 12 options");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { baseUrl, account } = resolveSignalRpcContext(opts);
|
||||||
|
const target = parseTarget(to);
|
||||||
|
const targetParams = buildTargetParams(target, {
|
||||||
|
recipient: true,
|
||||||
|
group: true,
|
||||||
|
username: true,
|
||||||
|
});
|
||||||
|
if (!targetParams) {
|
||||||
|
throw new Error("Signal poll recipient is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const params: Record<string, unknown> = {
|
||||||
|
question: question.trim(),
|
||||||
|
option: options.map((o) => o.trim()).filter(Boolean),
|
||||||
|
...targetParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (account) params.account = account;
|
||||||
|
|
||||||
|
// signal-cli uses --no-multi to disable multi-select, so we invert the logic
|
||||||
|
// Default is multi-select allowed (true), pass multiSelect: false to restrict to single choice
|
||||||
|
if (opts.multiSelect === false) {
|
||||||
|
params.multiSelect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await signalRpcRequest<{ timestamp?: number }>("sendPollCreate", params, {
|
||||||
|
baseUrl,
|
||||||
|
timeoutMs: opts.timeoutMs ?? 15_000, // polls may take longer
|
||||||
|
});
|
||||||
|
|
||||||
|
const timestamp = result?.timestamp;
|
||||||
|
return {
|
||||||
|
messageId: timestamp ? String(timestamp) : "unknown",
|
||||||
|
timestamp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user