mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:38:28 +00:00
feat: Add WhatsApp poll support (#248)
Implements issue #123 - WhatsApp Poll Support ## Gateway Protocol - Add `poll` RPC method with params: to, question, options (2-12), selectableCount ## ActiveWebListener - Add `sendPoll(to, poll)` method to interface - Implementation uses Baileys poll message type ## CLI Command - `clawdbot poll --to <jid> -q <question> -o <opt1> -o <opt2> [-s count]` - Supports --dry-run, --json, --verbose flags - Validates 2-12 options ## Changes - src/gateway/protocol/schema.ts: Add PollParamsSchema - src/gateway/protocol/index.ts: Export validator and types - src/web/active-listener.ts: Add sendPoll to interface - src/web/inbound.ts: Implement sendPoll using Baileys - src/web/outbound.ts: Add sendPollWhatsApp function - src/gateway/server-methods/send.ts: Add poll handler - src/commands/poll.ts: New CLI command - src/cli/program.ts: Register poll command Closes #123
This commit is contained in:
@@ -79,6 +79,8 @@ import {
|
||||
type ResponseFrame,
|
||||
ResponseFrameSchema,
|
||||
SendParamsSchema,
|
||||
type PollParams,
|
||||
PollParamsSchema,
|
||||
type SessionsCompactParams,
|
||||
SessionsCompactParamsSchema,
|
||||
type SessionsDeleteParams,
|
||||
@@ -147,6 +149,7 @@ export const validateResponseFrame =
|
||||
ajv.compile<ResponseFrame>(ResponseFrameSchema);
|
||||
export const validateEventFrame = ajv.compile<EventFrame>(EventFrameSchema);
|
||||
export const validateSendParams = ajv.compile(SendParamsSchema);
|
||||
export const validatePollParams = ajv.compile<PollParams>(PollParamsSchema);
|
||||
export const validateAgentParams = ajv.compile(AgentParamsSchema);
|
||||
export const validateAgentWaitParams = ajv.compile<AgentWaitParams>(
|
||||
AgentWaitParamsSchema,
|
||||
@@ -282,6 +285,7 @@ export {
|
||||
AgentEventSchema,
|
||||
ChatEventSchema,
|
||||
SendParamsSchema,
|
||||
PollParamsSchema,
|
||||
AgentParamsSchema,
|
||||
WakeParamsSchema,
|
||||
NodePairRequestParamsSchema,
|
||||
@@ -390,4 +394,5 @@ export type {
|
||||
CronRunParams,
|
||||
CronRunsParams,
|
||||
CronRunLogEntry,
|
||||
PollParams,
|
||||
};
|
||||
|
||||
@@ -198,6 +198,17 @@ export const SendParamsSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
export const PollParamsSchema = Type.Object(
|
||||
{
|
||||
to: NonEmptyString,
|
||||
question: NonEmptyString,
|
||||
options: Type.Array(NonEmptyString, { minItems: 2, maxItems: 12 }),
|
||||
selectableCount: Type.Optional(Type.Integer({ minimum: 1, maximum: 12 })),
|
||||
idempotencyKey: NonEmptyString,
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
export const AgentParamsSchema = Type.Object(
|
||||
{
|
||||
message: NonEmptyString,
|
||||
@@ -831,6 +842,7 @@ export const ProtocolSchemas: Record<string, TSchema> = {
|
||||
ErrorShape: ErrorShapeSchema,
|
||||
AgentEvent: AgentEventSchema,
|
||||
SendParams: SendParamsSchema,
|
||||
PollParams: PollParamsSchema,
|
||||
AgentParams: AgentParamsSchema,
|
||||
AgentWaitParams: AgentWaitParamsSchema,
|
||||
WakeParams: WakeParamsSchema,
|
||||
@@ -900,6 +912,7 @@ export type PresenceEntry = Static<typeof PresenceEntrySchema>;
|
||||
export type ErrorShape = Static<typeof ErrorShapeSchema>;
|
||||
export type StateVersion = Static<typeof StateVersionSchema>;
|
||||
export type AgentEvent = Static<typeof AgentEventSchema>;
|
||||
export type PollParams = Static<typeof PollParamsSchema>;
|
||||
export type AgentWaitParams = Static<typeof AgentWaitParamsSchema>;
|
||||
export type WakeParams = Static<typeof WakeParamsSchema>;
|
||||
export type NodePairRequestParams = Static<typeof NodePairRequestParamsSchema>;
|
||||
|
||||
@@ -6,11 +6,12 @@ import { sendMessageSignal } from "../../signal/index.js";
|
||||
import { sendMessageSlack } from "../../slack/send.js";
|
||||
import { sendMessageTelegram } from "../../telegram/send.js";
|
||||
import { resolveTelegramToken } from "../../telegram/token.js";
|
||||
import { sendMessageWhatsApp } from "../../web/outbound.js";
|
||||
import { sendMessageWhatsApp, sendPollWhatsApp } from "../../web/outbound.js";
|
||||
import {
|
||||
ErrorCodes,
|
||||
errorShape,
|
||||
formatValidationErrors,
|
||||
validatePollParams,
|
||||
validateSendParams,
|
||||
} from "../protocol/index.js";
|
||||
import { formatForLog } from "../ws-log.js";
|
||||
@@ -178,4 +179,69 @@ export const sendHandlers: GatewayRequestHandlers = {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
poll: async ({ params, respond, context }) => {
|
||||
const p = params as Record<string, unknown>;
|
||||
if (!validatePollParams(p)) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(
|
||||
ErrorCodes.INVALID_REQUEST,
|
||||
`invalid poll params: ${formatValidationErrors(validatePollParams.errors)}`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const request = p as {
|
||||
to: string;
|
||||
question: string;
|
||||
options: string[];
|
||||
selectableCount?: number;
|
||||
idempotencyKey: string;
|
||||
};
|
||||
const idem = request.idempotencyKey;
|
||||
const cached = context.dedupe.get(`poll:${idem}`);
|
||||
if (cached) {
|
||||
respond(cached.ok, cached.payload, cached.error, {
|
||||
cached: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const to = request.to.trim();
|
||||
const question = request.question.trim();
|
||||
const options = request.options.map((o) => o.trim());
|
||||
const selectableCount = request.selectableCount ?? 1;
|
||||
|
||||
try {
|
||||
const result = await sendPollWhatsApp(
|
||||
to,
|
||||
{ question, options, selectableCount },
|
||||
{ verbose: shouldLogVerbose() },
|
||||
);
|
||||
const payload = {
|
||||
runId: idem,
|
||||
messageId: result.messageId,
|
||||
toJid: result.toJid ?? `${to}@s.whatsapp.net`,
|
||||
provider: "whatsapp",
|
||||
};
|
||||
context.dedupe.set(`poll:${idem}`, {
|
||||
ts: Date.now(),
|
||||
ok: true,
|
||||
payload,
|
||||
});
|
||||
respond(true, payload, undefined, { provider: "whatsapp" });
|
||||
} catch (err) {
|
||||
const error = errorShape(ErrorCodes.UNAVAILABLE, String(err));
|
||||
context.dedupe.set(`poll:${idem}`, {
|
||||
ts: Date.now(),
|
||||
ok: false,
|
||||
error,
|
||||
});
|
||||
respond(false, undefined, error, {
|
||||
provider: "whatsapp",
|
||||
error: formatForLog(err),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user