UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

148 lines (147 loc) 6.14 kB
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js"; import { y as parseStrictNonNegativeInteger } from "./number-coercion-CJQ8TR--.js"; import { b as readStringParam, i as createActionGate, l as jsonResult } from "./common-C4yy9V-D.js"; import "./number-runtime-DBLVDypr.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import { t as resolveReactionLevel } from "./reaction-level-Dq0rYo3y.js"; import "./status-helpers-BevxX6Pu.js"; import { r as resolveReactionMessageId } from "./channel-actions-BOfvC-Gl.js"; import { i as resolveSignalAccount, t as listEnabledSignalAccounts } from "./accounts-ms-4J9iY.js"; import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-CgOWS03A.js"; //#region extensions/signal/src/reaction-level.ts /** * Resolve the effective reaction level and its implications for Signal. * * Levels: * - "off": No reactions at all * - "ack": Only automatic ack reactions (👀 when processing), no agent reactions * - "minimal": Agent can react, but sparingly (default) * - "extensive": Agent can react liberally */ function resolveSignalReactionLevel(params) { return resolveReactionLevel({ value: resolveSignalAccount({ cfg: params.cfg, accountId: params.accountId }).config.reactionLevel, defaultLevel: "minimal", invalidFallback: "minimal" }); } //#endregion //#region extensions/signal/src/message-actions.ts const providerId = "signal"; const GROUP_PREFIX = "group:"; function normalizeSignalReactionRecipient(raw) { const trimmed = raw.trim(); if (!trimmed) return trimmed; const withoutSignal = trimmed.replace(/^signal:/i, "").trim(); if (!withoutSignal) return withoutSignal; if (normalizeLowercaseStringOrEmpty(withoutSignal).startsWith("uuid:")) return withoutSignal.slice(5).trim(); return withoutSignal; } function resolveSignalReactionTarget(raw) { const trimmed = raw.trim(); if (!trimmed) return {}; const withoutSignal = trimmed.replace(/^signal:/i, "").trim(); if (!withoutSignal) return {}; if (normalizeLowercaseStringOrEmpty(withoutSignal).startsWith(GROUP_PREFIX)) { const groupId = withoutSignal.slice(6).trim(); return groupId ? { groupId } : {}; } return { recipient: normalizeSignalReactionRecipient(withoutSignal) }; } async function mutateSignalReaction(params) { const options = { cfg: params.cfg, accountId: params.accountId, groupId: params.target.groupId, targetAuthor: params.targetAuthor, targetAuthorUuid: params.targetAuthorUuid }; if (params.remove) { await removeReactionSignal(params.target.recipient ?? "", params.timestamp, params.emoji, options); return jsonResult({ ok: true, removed: params.emoji }); } await sendReactionSignal(params.target.recipient ?? "", params.timestamp, params.emoji, options); return jsonResult({ ok: true, added: params.emoji }); } const signalMessageActions = { describeMessageTool: ({ cfg, accountId }) => { const configuredAccounts = accountId ? [resolveSignalAccount({ cfg, accountId })].filter((account) => account.enabled && account.configured) : listEnabledSignalAccounts(cfg).filter((account) => account.configured); if (configuredAccounts.length === 0) return null; const actions = new Set(["send"]); if (configuredAccounts.some((account) => createActionGate(account.config.actions)("reactions"))) actions.add("react"); return { actions: Array.from(actions) }; }, supportsAction: ({ action }) => action !== "send", handleAction: async ({ action, params, cfg, accountId, toolContext }) => { if (action === "send") throw new Error("Send should be handled by outbound, not actions handler."); if (action === "react") { const reactionLevelInfo = resolveSignalReactionLevel({ cfg, accountId: accountId ?? void 0 }); if (!reactionLevelInfo.agentReactionsEnabled) throw new Error(`Signal agent reactions disabled (reactionLevel="${reactionLevelInfo.level}"). Set channels.signal.reactionLevel to "minimal" or "extensive" to enable.`); const actionConfig = resolveSignalAccount({ cfg, accountId }).config.actions; if (!createActionGate(actionConfig)("reactions")) throw new Error("Signal reactions are disabled via actions.reactions."); const target = resolveSignalReactionTarget(readStringParam(params, "recipient") ?? readStringParam(params, "to", { required: true, label: "recipient (UUID, phone number, or group)" })); if (!target.recipient && !target.groupId) throw new Error("recipient or group required"); const messageIdRaw = resolveReactionMessageId({ args: params, toolContext }); const messageId = messageIdRaw != null ? String(messageIdRaw) : void 0; if (!messageId) throw new Error("messageId (timestamp) required. Provide messageId explicitly or react to the current inbound message."); const targetAuthor = readStringParam(params, "targetAuthor"); const targetAuthorUuid = readStringParam(params, "targetAuthorUuid"); if (target.groupId && !targetAuthor && !targetAuthorUuid) throw new Error("targetAuthor or targetAuthorUuid required for group reactions."); const emoji = readStringParam(params, "emoji", { allowEmpty: true }); const remove = typeof params.remove === "boolean" ? params.remove : void 0; const timestamp = parseStrictNonNegativeInteger(messageId); if (timestamp === void 0) throw new Error(`Invalid messageId: ${messageId}. Expected numeric timestamp.`); if (remove) { if (!emoji) throw new Error("Emoji required to remove reaction."); return await mutateSignalReaction({ cfg, accountId: accountId ?? void 0, target, timestamp, emoji, remove: true, targetAuthor, targetAuthorUuid }); } if (!emoji) throw new Error("Emoji required to add reaction."); return await mutateSignalReaction({ cfg, accountId: accountId ?? void 0, target, timestamp, emoji, remove: false, targetAuthor, targetAuthorUuid }); } throw new Error(`Action ${action} not supported for ${providerId}.`); } }; //#endregion export { resolveSignalReactionLevel as n, signalMessageActions as t };