@openclaw/whatsapp 2026.5.2 → 2026.5.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-control-BQ3tCkH8.js +288 -0
- package/dist/account-config-CNQ33oav.js +47 -0
- package/dist/account-ids-DG18xddX.js +5 -0
- package/dist/accounts-BDqgpFEB.js +116 -0
- package/dist/action-runtime-DTGNgG-i.js +74 -0
- package/dist/action-runtime-api.js +2 -0
- package/dist/action-runtime.runtime.js +2 -0
- package/dist/active-listener-N7GFKsuN.js +11 -0
- package/dist/agent-tools-login-DpRxLNRB.js +89 -0
- package/dist/api.js +148 -0
- package/dist/audio-preflight.runtime-Bcdsl_ri.js +7 -0
- package/dist/auth-presence.js +32 -0
- package/dist/auth-store-BjCb50Ry.js +381 -0
- package/dist/channel-DQ-5Xoj9.js +542 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel-react-action-Df20qAy9.js +42 -0
- package/dist/channel.runtime-DKXRnA8x.js +55 -0
- package/dist/channel.setup-DOHR2BGp.js +20 -0
- package/dist/command-policy-O8sRnqUV.js +8 -0
- package/dist/config-schema-Dmo66Ubb.js +27 -0
- package/dist/connection-controller-BiHAsTM8.js +596 -0
- package/dist/connection-controller-registry-D_iIGz5m.js +22 -0
- package/dist/constants-DLGCIbfl.js +4 -0
- package/dist/constants.js +2 -0
- package/dist/contract-api.js +19 -0
- package/dist/creds-files-_2V8jzQa.js +19 -0
- package/dist/directory-config-DVs4ffXu.js +37 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/doctor-A9tXm9Om.js +48 -0
- package/dist/doctor-contract-api.js +10 -0
- package/dist/group-session-contract-B8QBn0I2.js +15 -0
- package/dist/identity-QHAlV7Fu.js +75 -0
- package/dist/index.js +18 -0
- package/dist/legacy-session-surface-api.js +8 -0
- package/dist/legacy-state-migrations-api.js +2 -0
- package/dist/light-runtime-api.js +5 -0
- package/dist/login-DymdrKca.js +52 -0
- package/dist/login-qr-DIS0Iv-7.js +378 -0
- package/dist/login-qr-api.js +2 -0
- package/dist/login-qr-runtime.js +16 -0
- package/dist/monitor-C4Fhg_lD.js +3349 -0
- package/dist/normalize-CWYiGkXw.js +2 -0
- package/dist/normalize-target-BPVHNsM5.js +74 -0
- package/dist/outbound-adapter-BssVnZdz.js +29 -0
- package/dist/outbound-base-Bq1UYEK5.js +119 -0
- package/dist/outbound-media-contract-CGss_QHy.js +163 -0
- package/dist/outbound-payload-test-api.js +2 -0
- package/dist/quoted-message-TENQzyxV.js +108 -0
- package/dist/reaction-level-CRVEVAfi.js +16 -0
- package/dist/reply-resolver.runtime-BRCgoO4C.js +2 -0
- package/dist/resolve-outbound-target-CN-Wo6IY.js +39 -0
- package/dist/rolldown-runtime-RkAeH_Qm.js +27 -0
- package/dist/runtime-6fZ67abR.js +8 -0
- package/dist/runtime-api.js +15 -0
- package/dist/runtime-group-policy-Gp1lQ0at.js +11 -0
- package/dist/secret-contract-api.js +5 -0
- package/dist/security-contract-DqH8JDX6.js +26 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-CIyLFxdZ.js +234 -0
- package/dist/session-contract-B0efQf9P.js +27 -0
- package/dist/session-errors-CSuKZPub.js +63 -0
- package/dist/session.runtime-C1eSE_KK.js +13 -0
- package/dist/setup-core-DZKvpzbH.js +46 -0
- package/dist/setup-entry.js +23 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-ieFnIW4g.js +344 -0
- package/dist/shared-D_wzSVar.js +307 -0
- package/dist/state-migrations-DnCdvoYD.js +39 -0
- package/dist/targets.js +2 -0
- package/dist/test-api.js +3 -0
- package/dist/text-runtime-C1zNiZeO.js +116 -0
- package/package.json +14 -6
- package/action-runtime-api.ts +0 -1
- package/action-runtime.runtime.ts +0 -1
- package/api.ts +0 -213
- package/auth-presence.ts +0 -80
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -3
- package/config-api.ts +0 -4
- package/constants.ts +0 -1
- package/contract-api.ts +0 -29
- package/directory-contract-api.ts +0 -4
- package/doctor-contract-api.ts +0 -8
- package/index.test.ts +0 -13
- package/index.ts +0 -16
- package/legacy-session-surface-api.ts +0 -6
- package/legacy-state-migrations-api.ts +0 -1
- package/light-runtime-api.ts +0 -12
- package/login-qr-api.ts +0 -1
- package/login-qr-runtime.ts +0 -23
- package/outbound-payload-test-api.ts +0 -1
- package/runtime-api.ts +0 -84
- package/secret-contract-api.ts +0 -4
- package/security-contract-api.ts +0 -4
- package/setup-entry.test.ts +0 -21
- package/setup-entry.ts +0 -21
- package/setup-plugin-api.ts +0 -3
- package/src/account-config.ts +0 -77
- package/src/account-ids.ts +0 -13
- package/src/account-types.ts +0 -5
- package/src/accounts.test.ts +0 -182
- package/src/accounts.ts +0 -176
- package/src/accounts.whatsapp-auth.test.ts +0 -59
- package/src/action-runtime-target-auth.ts +0 -27
- package/src/action-runtime.test.ts +0 -330
- package/src/action-runtime.ts +0 -76
- package/src/active-listener.test.ts +0 -65
- package/src/active-listener.ts +0 -17
- package/src/agent-tools-login.test.ts +0 -81
- package/src/agent-tools-login.ts +0 -113
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -27
- package/src/auth-store.runtime.ts +0 -1
- package/src/auth-store.test.ts +0 -311
- package/src/auth-store.ts +0 -502
- package/src/auto-reply/config.runtime.ts +0 -16
- package/src/auto-reply/constants.ts +0 -1
- package/src/auto-reply/deliver-reply.test.ts +0 -843
- package/src/auto-reply/deliver-reply.ts +0 -279
- package/src/auto-reply/loggers.ts +0 -6
- package/src/auto-reply/mentions.ts +0 -131
- package/src/auto-reply/monitor/ack-reaction.test.ts +0 -170
- package/src/auto-reply/monitor/ack-reaction.ts +0 -99
- package/src/auto-reply/monitor/audio-preflight.runtime.ts +0 -9
- package/src/auto-reply/monitor/broadcast.ts +0 -153
- package/src/auto-reply/monitor/commands.ts +0 -19
- package/src/auto-reply/monitor/echo.ts +0 -64
- package/src/auto-reply/monitor/group-activation.runtime.ts +0 -1
- package/src/auto-reply/monitor/group-activation.test.ts +0 -189
- package/src/auto-reply/monitor/group-activation.ts +0 -73
- package/src/auto-reply/monitor/group-gating.audio-preflight.test.ts +0 -103
- package/src/auto-reply/monitor/group-gating.runtime.ts +0 -8
- package/src/auto-reply/monitor/group-gating.ts +0 -217
- package/src/auto-reply/monitor/group-members.test.ts +0 -56
- package/src/auto-reply/monitor/group-members.ts +0 -65
- package/src/auto-reply/monitor/inbound-context.test.ts +0 -97
- package/src/auto-reply/monitor/inbound-context.ts +0 -92
- package/src/auto-reply/monitor/inbound-dispatch.runtime.ts +0 -21
- package/src/auto-reply/monitor/inbound-dispatch.test.ts +0 -895
- package/src/auto-reply/monitor/inbound-dispatch.ts +0 -422
- package/src/auto-reply/monitor/last-route.test.ts +0 -37
- package/src/auto-reply/monitor/last-route.ts +0 -61
- package/src/auto-reply/monitor/message-line.runtime.ts +0 -38
- package/src/auto-reply/monitor/message-line.ts +0 -54
- package/src/auto-reply/monitor/on-message.audio-preflight.test.ts +0 -354
- package/src/auto-reply/monitor/on-message.ts +0 -291
- package/src/auto-reply/monitor/peer.ts +0 -17
- package/src/auto-reply/monitor/process-message.audio-preflight.test.ts +0 -420
- package/src/auto-reply/monitor/process-message.test.ts +0 -355
- package/src/auto-reply/monitor/process-message.ts +0 -531
- package/src/auto-reply/monitor/runtime-api.ts +0 -30
- package/src/auto-reply/monitor-state.test.ts +0 -64
- package/src/auto-reply/monitor-state.ts +0 -106
- package/src/auto-reply/monitor.ts +0 -600
- package/src/auto-reply/reply-resolver.runtime.ts +0 -1
- package/src/auto-reply/types.ts +0 -48
- package/src/auto-reply/util.ts +0 -63
- package/src/auto-reply/web-auto-reply-monitor.test.ts +0 -687
- package/src/auto-reply/web-auto-reply-utils.test.ts +0 -392
- package/src/auto-reply.broadcast-groups.combined.test.ts +0 -246
- package/src/auto-reply.broadcast-groups.test-harness.ts +0 -47
- package/src/auto-reply.impl.ts +0 -6
- package/src/auto-reply.test-harness.ts +0 -421
- package/src/auto-reply.ts +0 -1
- package/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts +0 -427
- package/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +0 -908
- package/src/auto-reply.web-auto-reply.last-route.test.ts +0 -184
- package/src/channel-actions.runtime.ts +0 -7
- package/src/channel-actions.test.ts +0 -216
- package/src/channel-actions.ts +0 -84
- package/src/channel-outbound.test.ts +0 -131
- package/src/channel-outbound.ts +0 -36
- package/src/channel-react-action.runtime.ts +0 -7
- package/src/channel-react-action.test.ts +0 -294
- package/src/channel-react-action.ts +0 -84
- package/src/channel.runtime.ts +0 -117
- package/src/channel.setup.test.ts +0 -461
- package/src/channel.setup.ts +0 -28
- package/src/channel.ts +0 -339
- package/src/command-policy.ts +0 -7
- package/src/config-accessors.test.ts +0 -34
- package/src/config-accessors.ts +0 -22
- package/src/config-schema.test.ts +0 -121
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -24
- package/src/connection-controller-registry.test.ts +0 -27
- package/src/connection-controller-registry.ts +0 -49
- package/src/connection-controller.test.ts +0 -282
- package/src/connection-controller.ts +0 -679
- package/src/creds-files.ts +0 -19
- package/src/creds-persistence.ts +0 -102
- package/src/directory-config.test.ts +0 -60
- package/src/directory-config.ts +0 -40
- package/src/directory-contract.test.ts +0 -56
- package/src/doctor-contract.ts +0 -11
- package/src/doctor.test.ts +0 -74
- package/src/doctor.ts +0 -56
- package/src/group-intro.ts +0 -15
- package/src/group-policy.test.ts +0 -36
- package/src/group-policy.ts +0 -40
- package/src/group-session-contract.ts +0 -20
- package/src/group-session-key.test.ts +0 -53
- package/src/group-session-key.ts +0 -41
- package/src/heartbeat.ts +0 -34
- package/src/identity.ts +0 -164
- package/src/inbound/access-control.test-harness.ts +0 -37
- package/src/inbound/access-control.test.ts +0 -375
- package/src/inbound/access-control.ts +0 -237
- package/src/inbound/dedupe.ts +0 -132
- package/src/inbound/extract.test.ts +0 -282
- package/src/inbound/extract.ts +0 -488
- package/src/inbound/lifecycle.ts +0 -39
- package/src/inbound/media.node.test.ts +0 -83
- package/src/inbound/media.ts +0 -102
- package/src/inbound/monitor.ts +0 -890
- package/src/inbound/runtime-api.ts +0 -7
- package/src/inbound/save-media.runtime.ts +0 -1
- package/src/inbound/send-api.test.ts +0 -319
- package/src/inbound/send-api.ts +0 -145
- package/src/inbound/send-result.ts +0 -61
- package/src/inbound/types.ts +0 -106
- package/src/inbound-context.contract.test.ts +0 -32
- package/src/inbound-policy.ts +0 -231
- package/src/inbound.media.test.ts +0 -320
- package/src/inbound.test.ts +0 -290
- package/src/inbound.ts +0 -9
- package/src/login-qr.test.ts +0 -483
- package/src/login-qr.ts +0 -542
- package/src/login.coverage.test.ts +0 -133
- package/src/login.test.ts +0 -89
- package/src/login.ts +0 -70
- package/src/logout.test.ts +0 -162
- package/src/media.test.ts +0 -480
- package/src/media.ts +0 -10
- package/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test-support.ts +0 -400
- package/src/monitor-inbox.append-upsert.test-support.ts +0 -133
- package/src/monitor-inbox.behavior.test.ts +0 -5
- package/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test-support.ts +0 -362
- package/src/monitor-inbox.captures-media-path-image-messages.test-support.ts +0 -306
- package/src/monitor-inbox.streams-inbound-messages.test-support.ts +0 -729
- package/src/monitor-inbox.test-harness.ts +0 -304
- package/src/normalize-target.ts +0 -123
- package/src/normalize.ts +0 -7
- package/src/outbound-adapter.poll.test.ts +0 -62
- package/src/outbound-adapter.sendpayload.test.ts +0 -195
- package/src/outbound-adapter.ts +0 -36
- package/src/outbound-base.test.ts +0 -560
- package/src/outbound-base.ts +0 -244
- package/src/outbound-media-contract.ts +0 -278
- package/src/outbound-media.runtime.ts +0 -36
- package/src/outbound-payload.contract.test.ts +0 -61
- package/src/outbound-send-deps.ts +0 -1
- package/src/outbound-test-support.ts +0 -16
- package/src/pairing-security.test-harness.ts +0 -59
- package/src/qr-image.ts +0 -1
- package/src/qr-terminal.ts +0 -1
- package/src/quoted-message.test.ts +0 -83
- package/src/quoted-message.ts +0 -184
- package/src/reaction-level.test.ts +0 -94
- package/src/reaction-level.ts +0 -21
- package/src/reconnect.test.ts +0 -51
- package/src/reconnect.ts +0 -55
- package/src/resolve-outbound-target.test.ts +0 -288
- package/src/resolve-outbound-target.ts +0 -58
- package/src/resolve-target.test.ts +0 -120
- package/src/runtime-api.ts +0 -59
- package/src/runtime-group-policy.ts +0 -16
- package/src/runtime.ts +0 -9
- package/src/security-contract.ts +0 -47
- package/src/security-fix.ts +0 -71
- package/src/send.test.ts +0 -534
- package/src/send.ts +0 -292
- package/src/session-contract.test.ts +0 -31
- package/src/session-contract.ts +0 -43
- package/src/session-errors.ts +0 -125
- package/src/session-route.test.ts +0 -41
- package/src/session-route.ts +0 -32
- package/src/session.runtime.ts +0 -8
- package/src/session.test.ts +0 -559
- package/src/session.ts +0 -330
- package/src/setup-core.ts +0 -52
- package/src/setup-finalize.ts +0 -446
- package/src/setup-surface.test.ts +0 -367
- package/src/setup-surface.ts +0 -69
- package/src/setup-test-helpers.ts +0 -216
- package/src/shared.ts +0 -291
- package/src/socket-timing.test.ts +0 -49
- package/src/socket-timing.ts +0 -38
- package/src/state-migrations.ts +0 -54
- package/src/status-issues.test.ts +0 -137
- package/src/status-issues.ts +0 -185
- package/src/system-prompt.test.ts +0 -199
- package/src/system-prompt.ts +0 -31
- package/src/targets-runtime.ts +0 -180
- package/src/test-helpers.ts +0 -690
- package/src/text-runtime.test.ts +0 -162
- package/src/text-runtime.ts +0 -11
- package/src/vcard.ts +0 -84
- package/targets.ts +0 -5
- package/test-api.ts +0 -2
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./account-ids-DG18xddX.js";
|
|
2
|
+
import { a as resolveWhatsAppAccount, o as resolveWhatsAppAuthDir } from "./accounts-BDqgpFEB.js";
|
|
3
|
+
import { i as formatWhatsAppWebAuthStatusState, m as readWebAuthState } from "./auth-store-BjCb50Ry.js";
|
|
4
|
+
import { t as whatsappSetupAdapter } from "./setup-core-DZKvpzbH.js";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAllowFromEntries, normalizeE164, pathExists, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
7
|
+
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
8
|
+
//#region extensions/whatsapp/src/setup-finalize.ts
|
|
9
|
+
function trimPromptText(value) {
|
|
10
|
+
return value?.trim() ?? "";
|
|
11
|
+
}
|
|
12
|
+
function isDefaultWhatsAppAccountKey(accountId) {
|
|
13
|
+
return accountId.trim().toLowerCase() === DEFAULT_ACCOUNT_ID;
|
|
14
|
+
}
|
|
15
|
+
function shouldWriteDefaultWhatsAppAccountConfigAtAccountScope(cfg) {
|
|
16
|
+
const accounts = cfg.channels?.whatsapp?.accounts;
|
|
17
|
+
if (!accounts) return false;
|
|
18
|
+
if (accounts.default) return true;
|
|
19
|
+
return Object.keys(accounts).some((accountId) => !isDefaultWhatsAppAccountKey(accountId));
|
|
20
|
+
}
|
|
21
|
+
function resolveDefaultWhatsAppAccountWriteKey(cfg) {
|
|
22
|
+
const accounts = cfg.channels?.whatsapp?.accounts;
|
|
23
|
+
if (!accounts) return DEFAULT_ACCOUNT_ID;
|
|
24
|
+
return Object.keys(accounts).find((accountId) => isDefaultWhatsAppAccountKey(accountId)) ?? DEFAULT_ACCOUNT_ID;
|
|
25
|
+
}
|
|
26
|
+
function resolveWhatsAppConfigPathPrefix(cfg, accountId) {
|
|
27
|
+
if (accountId === DEFAULT_ACCOUNT_ID && shouldWriteDefaultWhatsAppAccountConfigAtAccountScope(cfg)) return `channels.whatsapp.accounts.${resolveDefaultWhatsAppAccountWriteKey(cfg)}`;
|
|
28
|
+
return accountId === DEFAULT_ACCOUNT_ID ? "channels.whatsapp" : `channels.whatsapp.accounts.${accountId}`;
|
|
29
|
+
}
|
|
30
|
+
function mergeWhatsAppConfig(cfg, accountId, patch, options) {
|
|
31
|
+
const channelConfig = { ...cfg.channels?.whatsapp };
|
|
32
|
+
const mutableChannelConfig = channelConfig;
|
|
33
|
+
if (resolveWhatsAppConfigPathPrefix(cfg, accountId) === "channels.whatsapp") {
|
|
34
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
35
|
+
if (value === void 0) {
|
|
36
|
+
if (options?.unsetOnUndefined?.includes(key)) delete mutableChannelConfig[key];
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
mutableChannelConfig[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
...cfg,
|
|
43
|
+
channels: {
|
|
44
|
+
...cfg.channels,
|
|
45
|
+
whatsapp: channelConfig
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const accounts = { ...channelConfig.accounts };
|
|
50
|
+
const targetAccountId = accountId === DEFAULT_ACCOUNT_ID ? resolveDefaultWhatsAppAccountWriteKey(cfg) : accountId;
|
|
51
|
+
const lowerDefaultAccount = accountId === DEFAULT_ACCOUNT_ID && targetAccountId !== DEFAULT_ACCOUNT_ID ? accounts[DEFAULT_ACCOUNT_ID] : void 0;
|
|
52
|
+
const nextAccount = {
|
|
53
|
+
...accounts[targetAccountId],
|
|
54
|
+
...lowerDefaultAccount
|
|
55
|
+
};
|
|
56
|
+
const mutableNextAccount = nextAccount;
|
|
57
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
58
|
+
if (value === void 0) {
|
|
59
|
+
if (options?.unsetOnUndefined?.includes(key)) delete mutableNextAccount[key];
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
mutableNextAccount[key] = value;
|
|
63
|
+
}
|
|
64
|
+
accounts[targetAccountId] = nextAccount;
|
|
65
|
+
if (lowerDefaultAccount) delete accounts[DEFAULT_ACCOUNT_ID];
|
|
66
|
+
return {
|
|
67
|
+
...cfg,
|
|
68
|
+
channels: {
|
|
69
|
+
...cfg.channels,
|
|
70
|
+
whatsapp: {
|
|
71
|
+
...channelConfig,
|
|
72
|
+
accounts
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function setWhatsAppDmPolicy(cfg, accountId, dmPolicy) {
|
|
78
|
+
return mergeWhatsAppConfig(cfg, accountId, { dmPolicy });
|
|
79
|
+
}
|
|
80
|
+
function setWhatsAppAllowFrom(cfg, accountId, allowFrom) {
|
|
81
|
+
return mergeWhatsAppConfig(cfg, accountId, { allowFrom }, { unsetOnUndefined: ["allowFrom"] });
|
|
82
|
+
}
|
|
83
|
+
function setWhatsAppSelfChatMode(cfg, accountId, selfChatMode) {
|
|
84
|
+
return mergeWhatsAppConfig(cfg, accountId, { selfChatMode });
|
|
85
|
+
}
|
|
86
|
+
async function detectWhatsAppLinked(cfg, accountId) {
|
|
87
|
+
const { authDir } = resolveWhatsAppAuthDir({
|
|
88
|
+
cfg,
|
|
89
|
+
accountId
|
|
90
|
+
});
|
|
91
|
+
return await pathExists(path.join(authDir, "creds.json"));
|
|
92
|
+
}
|
|
93
|
+
async function promptWhatsAppOwnerAllowFrom(params) {
|
|
94
|
+
const { prompter, existingAllowFrom } = params;
|
|
95
|
+
await prompter.note("We need the sender/owner number so OpenClaw can allowlist you.", "WhatsApp number");
|
|
96
|
+
const normalized = normalizeE164(trimPromptText(await prompter.text({
|
|
97
|
+
message: "Your personal WhatsApp number (the phone you will message from)",
|
|
98
|
+
placeholder: "+15555550123",
|
|
99
|
+
initialValue: existingAllowFrom[0],
|
|
100
|
+
validate: (value) => {
|
|
101
|
+
const raw = trimPromptText(value);
|
|
102
|
+
if (!raw) return "Required";
|
|
103
|
+
if (!normalizeE164(raw)) return `Invalid number: ${raw}`;
|
|
104
|
+
}
|
|
105
|
+
})));
|
|
106
|
+
if (!normalized) throw new Error("Invalid WhatsApp owner number (expected E.164 after validation).");
|
|
107
|
+
return {
|
|
108
|
+
normalized,
|
|
109
|
+
allowFrom: normalizeAllowFromEntries([...existingAllowFrom.filter((item) => item !== "*"), normalized], normalizeE164)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function applyWhatsAppOwnerAllowlist(params) {
|
|
113
|
+
const { normalized, allowFrom } = await promptWhatsAppOwnerAllowFrom({
|
|
114
|
+
prompter: params.prompter,
|
|
115
|
+
existingAllowFrom: params.existingAllowFrom
|
|
116
|
+
});
|
|
117
|
+
let next = setWhatsAppSelfChatMode(params.cfg, params.accountId, true);
|
|
118
|
+
next = setWhatsAppDmPolicy(next, params.accountId, "allowlist");
|
|
119
|
+
next = setWhatsAppAllowFrom(next, params.accountId, allowFrom);
|
|
120
|
+
await params.prompter.note([...params.messageLines, `- allowFrom includes ${normalized}`].join("\n"), params.title);
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
function parseWhatsAppAllowFromEntries(raw) {
|
|
124
|
+
const parts = splitSetupEntries(raw);
|
|
125
|
+
if (parts.length === 0) return { entries: [] };
|
|
126
|
+
const entries = [];
|
|
127
|
+
for (const part of parts) {
|
|
128
|
+
if (part === "*") {
|
|
129
|
+
entries.push("*");
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const normalized = normalizeE164(part);
|
|
133
|
+
if (!normalized) return {
|
|
134
|
+
entries: [],
|
|
135
|
+
invalidEntry: part
|
|
136
|
+
};
|
|
137
|
+
entries.push(normalized);
|
|
138
|
+
}
|
|
139
|
+
return { entries: normalizeAllowFromEntries(entries, normalizeE164) };
|
|
140
|
+
}
|
|
141
|
+
async function promptWhatsAppDmAccess(params) {
|
|
142
|
+
const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;
|
|
143
|
+
const account = resolveWhatsAppAccount({
|
|
144
|
+
cfg: params.cfg,
|
|
145
|
+
accountId
|
|
146
|
+
});
|
|
147
|
+
const existingPolicy = account.dmPolicy ?? "pairing";
|
|
148
|
+
const existingAllowFrom = account.allowFrom ?? [];
|
|
149
|
+
const existingLabel = existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
|
|
150
|
+
const configPathPrefix = resolveWhatsAppConfigPathPrefix(params.cfg, accountId);
|
|
151
|
+
const policyKey = `${configPathPrefix}.dmPolicy`;
|
|
152
|
+
const allowFromKey = `${configPathPrefix}.allowFrom`;
|
|
153
|
+
if (params.forceAllowFrom) return await applyWhatsAppOwnerAllowlist({
|
|
154
|
+
cfg: params.cfg,
|
|
155
|
+
accountId,
|
|
156
|
+
prompter: params.prompter,
|
|
157
|
+
existingAllowFrom,
|
|
158
|
+
title: "WhatsApp allowlist",
|
|
159
|
+
messageLines: ["Allowlist mode enabled."]
|
|
160
|
+
});
|
|
161
|
+
await params.prompter.note([
|
|
162
|
+
`WhatsApp direct chats are gated by \`${policyKey}\` + \`${allowFromKey}\`.`,
|
|
163
|
+
"- pairing (default): unknown senders get a pairing code; owner approves",
|
|
164
|
+
"- allowlist: unknown senders are blocked",
|
|
165
|
+
"- open: public inbound DMs (requires allowFrom to include \"*\")",
|
|
166
|
+
"- disabled: ignore WhatsApp DMs",
|
|
167
|
+
"",
|
|
168
|
+
`Current: dmPolicy=${existingPolicy}, allowFrom=${existingLabel}`,
|
|
169
|
+
`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`
|
|
170
|
+
].join("\n"), "WhatsApp DM access");
|
|
171
|
+
if (await params.prompter.select({
|
|
172
|
+
message: "WhatsApp phone setup",
|
|
173
|
+
options: [{
|
|
174
|
+
value: "personal",
|
|
175
|
+
label: "This is my personal phone number"
|
|
176
|
+
}, {
|
|
177
|
+
value: "separate",
|
|
178
|
+
label: "Separate phone just for OpenClaw"
|
|
179
|
+
}]
|
|
180
|
+
}) === "personal") return await applyWhatsAppOwnerAllowlist({
|
|
181
|
+
cfg: params.cfg,
|
|
182
|
+
accountId,
|
|
183
|
+
prompter: params.prompter,
|
|
184
|
+
existingAllowFrom,
|
|
185
|
+
title: "WhatsApp personal phone",
|
|
186
|
+
messageLines: ["Personal phone mode enabled.", "- dmPolicy set to allowlist (pairing skipped)"]
|
|
187
|
+
});
|
|
188
|
+
const policy = await params.prompter.select({
|
|
189
|
+
message: "WhatsApp DM policy",
|
|
190
|
+
options: [
|
|
191
|
+
{
|
|
192
|
+
value: "pairing",
|
|
193
|
+
label: "Pairing (recommended)"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
value: "allowlist",
|
|
197
|
+
label: "Allowlist only (block unknown senders)"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
value: "open",
|
|
201
|
+
label: "Open (public inbound DMs)"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
value: "disabled",
|
|
205
|
+
label: "Disabled (ignore WhatsApp DMs)"
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
});
|
|
209
|
+
let next = setWhatsAppSelfChatMode(params.cfg, accountId, false);
|
|
210
|
+
next = setWhatsAppDmPolicy(next, accountId, policy);
|
|
211
|
+
if (policy === "open") {
|
|
212
|
+
const allowFrom = normalizeAllowFromEntries(["*", ...existingAllowFrom], normalizeE164);
|
|
213
|
+
next = setWhatsAppAllowFrom(next, accountId, allowFrom.length > 0 ? allowFrom : ["*"]);
|
|
214
|
+
return next;
|
|
215
|
+
}
|
|
216
|
+
if (policy === "disabled") return next;
|
|
217
|
+
const allowOptions = existingAllowFrom.length > 0 ? [
|
|
218
|
+
{
|
|
219
|
+
value: "keep",
|
|
220
|
+
label: "Keep current allowFrom"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
value: "unset",
|
|
224
|
+
label: "Unset allowFrom (use pairing approvals only)"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
value: "list",
|
|
228
|
+
label: "Set allowFrom to specific numbers"
|
|
229
|
+
}
|
|
230
|
+
] : [{
|
|
231
|
+
value: "unset",
|
|
232
|
+
label: "Unset allowFrom (default)"
|
|
233
|
+
}, {
|
|
234
|
+
value: "list",
|
|
235
|
+
label: "Set allowFrom to specific numbers"
|
|
236
|
+
}];
|
|
237
|
+
const mode = await params.prompter.select({
|
|
238
|
+
message: "WhatsApp allowFrom (optional pre-allowlist)",
|
|
239
|
+
options: allowOptions.map((opt) => ({
|
|
240
|
+
value: opt.value,
|
|
241
|
+
label: opt.label
|
|
242
|
+
}))
|
|
243
|
+
});
|
|
244
|
+
if (mode === "keep") return next;
|
|
245
|
+
if (mode === "unset") return setWhatsAppAllowFrom(next, accountId, void 0);
|
|
246
|
+
const parsed = parseWhatsAppAllowFromEntries(trimPromptText(await params.prompter.text({
|
|
247
|
+
message: "Allowed sender numbers (comma-separated, E.164)",
|
|
248
|
+
placeholder: "+15555550123, +447700900123",
|
|
249
|
+
validate: (value) => {
|
|
250
|
+
const raw = trimPromptText(value);
|
|
251
|
+
if (!raw) return "Required";
|
|
252
|
+
const parsed = parseWhatsAppAllowFromEntries(raw);
|
|
253
|
+
if (parsed.entries.length === 0 && !parsed.invalidEntry) return "Required";
|
|
254
|
+
if (parsed.invalidEntry) return `Invalid number: ${parsed.invalidEntry}`;
|
|
255
|
+
}
|
|
256
|
+
})));
|
|
257
|
+
if (parsed.invalidEntry) throw new Error(`Invalid number: ${parsed.invalidEntry}`);
|
|
258
|
+
if (parsed.entries.length === 0) throw new Error("Invalid WhatsApp allowFrom list (expected at least one E.164 number).");
|
|
259
|
+
return setWhatsAppAllowFrom(next, accountId, parsed.entries);
|
|
260
|
+
}
|
|
261
|
+
async function finalizeWhatsAppSetup(params) {
|
|
262
|
+
const accountId = params.accountId.trim() || resolveDefaultWhatsAppAccountId(params.cfg);
|
|
263
|
+
let next = accountId === DEFAULT_ACCOUNT_ID ? params.cfg : whatsappSetupAdapter.applyAccountConfig({
|
|
264
|
+
cfg: params.cfg,
|
|
265
|
+
accountId,
|
|
266
|
+
input: {}
|
|
267
|
+
});
|
|
268
|
+
const linked = await detectWhatsAppLinked(next, accountId);
|
|
269
|
+
const { authDir } = resolveWhatsAppAuthDir({
|
|
270
|
+
cfg: next,
|
|
271
|
+
accountId
|
|
272
|
+
});
|
|
273
|
+
if (!linked) await params.prompter.note([
|
|
274
|
+
"Scan the QR with WhatsApp on your phone.",
|
|
275
|
+
`Credentials are stored under ${authDir}/ for future runs.`,
|
|
276
|
+
`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`
|
|
277
|
+
].join("\n"), "WhatsApp linking");
|
|
278
|
+
if (await params.prompter.confirm({
|
|
279
|
+
message: linked ? "WhatsApp already linked. Re-link now?" : "Link WhatsApp now (QR)?",
|
|
280
|
+
initialValue: !linked
|
|
281
|
+
})) try {
|
|
282
|
+
const { loginWeb } = await import("./login-DymdrKca.js").then((n) => n.n);
|
|
283
|
+
await loginWeb(false, void 0, params.runtime, accountId);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
params.runtime.error(`WhatsApp login failed: ${String(error)}`);
|
|
286
|
+
await params.prompter.note(`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`, "WhatsApp help");
|
|
287
|
+
}
|
|
288
|
+
else if (!linked) await params.prompter.note(`Run \`${formatCliCommand("openclaw channels login")}\` later to link WhatsApp.`, "WhatsApp");
|
|
289
|
+
next = await promptWhatsAppDmAccess({
|
|
290
|
+
cfg: next,
|
|
291
|
+
accountId,
|
|
292
|
+
forceAllowFrom: params.forceAllowFrom,
|
|
293
|
+
prompter: params.prompter
|
|
294
|
+
});
|
|
295
|
+
return { cfg: next };
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region extensions/whatsapp/src/setup-surface.ts
|
|
299
|
+
const channel = "whatsapp";
|
|
300
|
+
async function readWhatsAppSetupLinkState(cfg, accountId) {
|
|
301
|
+
const { authDir } = resolveWhatsAppAuthDir({
|
|
302
|
+
cfg,
|
|
303
|
+
accountId
|
|
304
|
+
});
|
|
305
|
+
return await readWebAuthState(authDir);
|
|
306
|
+
}
|
|
307
|
+
const whatsappSetupWizard = {
|
|
308
|
+
channel,
|
|
309
|
+
status: {
|
|
310
|
+
configuredLabel: "linked",
|
|
311
|
+
unconfiguredLabel: "not linked",
|
|
312
|
+
configuredHint: "linked",
|
|
313
|
+
unconfiguredHint: "not linked",
|
|
314
|
+
configuredScore: 5,
|
|
315
|
+
unconfiguredScore: 4,
|
|
316
|
+
resolveConfigured: async ({ cfg, accountId }) => {
|
|
317
|
+
for (const resolvedAccountId of accountId ? [accountId] : listAccountIds(cfg)) if (await readWhatsAppSetupLinkState(cfg, resolvedAccountId) === "linked") return true;
|
|
318
|
+
return false;
|
|
319
|
+
},
|
|
320
|
+
resolveStatusLines: async ({ cfg, accountId, configured }) => {
|
|
321
|
+
const linkedAccountId = (await Promise.all((accountId ? [accountId] : listAccountIds(cfg)).map(async (resolvedAccountId) => ({
|
|
322
|
+
accountId: resolvedAccountId,
|
|
323
|
+
state: await readWhatsAppSetupLinkState(cfg, resolvedAccountId)
|
|
324
|
+
})))).find((entry) => entry.state === "linked" || entry.state === "unstable");
|
|
325
|
+
const labelAccountId = accountId ?? linkedAccountId?.accountId;
|
|
326
|
+
return [`${labelAccountId ? `WhatsApp (${labelAccountId === DEFAULT_ACCOUNT_ID ? "default" : labelAccountId})` : "WhatsApp"}: ${configured ? formatWhatsAppWebAuthStatusState("linked") : formatWhatsAppWebAuthStatusState(linkedAccountId?.state ?? "not-linked")}`];
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
resolveShouldPromptAccountIds: ({ shouldPromptAccountIds }) => shouldPromptAccountIds,
|
|
330
|
+
credentials: [],
|
|
331
|
+
finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime }) => await finalizeWhatsAppSetup({
|
|
332
|
+
cfg,
|
|
333
|
+
accountId,
|
|
334
|
+
forceAllowFrom,
|
|
335
|
+
prompter,
|
|
336
|
+
runtime
|
|
337
|
+
}),
|
|
338
|
+
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
|
|
339
|
+
onAccountRecorded: (accountId, options) => {
|
|
340
|
+
options?.onAccountId?.(channel, accountId);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
//#endregion
|
|
344
|
+
export { whatsappSetupWizard };
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./account-ids-DG18xddX.js";
|
|
2
|
+
import { a as resolveWhatsAppAccount, n as hasAnyWhatsAppAuth } from "./accounts-BDqgpFEB.js";
|
|
3
|
+
import { a as normalizeWhatsAppAllowFromEntries } from "./normalize-target-BPVHNsM5.js";
|
|
4
|
+
import { t as WhatsAppChannelConfigSchema } from "./config-schema-Dmo66Ubb.js";
|
|
5
|
+
import { n as whatsappDoctor } from "./doctor-A9tXm9Om.js";
|
|
6
|
+
import { t as resolveLegacyGroupSessionKey } from "./group-session-contract-B8QBn0I2.js";
|
|
7
|
+
import { n as unsupportedSecretRefSurfacePatterns, t as collectUnsupportedSecretRefConfigCandidates } from "./security-contract-DqH8JDX6.js";
|
|
8
|
+
import { n as deriveLegacySessionChatType, r as isLegacyGroupSessionKey, t as canonicalizeLegacySessionKey } from "./session-contract-B0efQf9P.js";
|
|
9
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-core";
|
|
10
|
+
import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
|
|
11
|
+
import { createChannelPluginBase, getChatChannelMeta } from "openclaw/plugin-sdk/core";
|
|
12
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/account-id";
|
|
13
|
+
import { collectOpenGroupPolicyRouteAllowlistWarnings, createAllowlistProviderGroupPolicyWarningCollector, resolveChannelGroupRequireMention, resolveChannelGroupToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
14
|
+
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
15
|
+
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
16
|
+
import { createDelegatedSetupWizardProxy } from "openclaw/plugin-sdk/setup-runtime";
|
|
17
|
+
import { readChannelAllowFromStore } from "openclaw/plugin-sdk/channel-pairing";
|
|
18
|
+
//#region extensions/whatsapp/src/config-accessors.ts
|
|
19
|
+
function formatWhatsAppConfigAllowFromEntries(allowFrom) {
|
|
20
|
+
return normalizeWhatsAppAllowFromEntries(allowFrom);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region extensions/whatsapp/src/group-intro.ts
|
|
24
|
+
const WHATSAPP_GROUP_INTRO_HINT = "WhatsApp IDs: SenderId is the participant JID (group participant id).";
|
|
25
|
+
function resolveWhatsAppGroupIntroHint() {
|
|
26
|
+
return WHATSAPP_GROUP_INTRO_HINT;
|
|
27
|
+
}
|
|
28
|
+
function resolveWhatsAppMentionStripRegexes(ctx) {
|
|
29
|
+
const selfE164 = (ctx.To ?? "").replace(/^whatsapp:/i, "");
|
|
30
|
+
if (!selfE164) return [];
|
|
31
|
+
const escaped = selfE164.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
32
|
+
return [new RegExp(escaped, "g"), new RegExp(`@${escaped}`, "g")];
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region extensions/whatsapp/src/group-policy.ts
|
|
36
|
+
function resolveWhatsAppGroupRequireMention(params) {
|
|
37
|
+
return resolveChannelGroupRequireMention({
|
|
38
|
+
cfg: params.cfg,
|
|
39
|
+
channel: "whatsapp",
|
|
40
|
+
groupId: params.groupId,
|
|
41
|
+
accountId: params.accountId
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function resolveWhatsAppGroupToolPolicy(params) {
|
|
45
|
+
return resolveChannelGroupToolsPolicy({
|
|
46
|
+
cfg: params.cfg,
|
|
47
|
+
channel: "whatsapp",
|
|
48
|
+
groupId: params.groupId,
|
|
49
|
+
accountId: params.accountId,
|
|
50
|
+
senderId: params.senderId,
|
|
51
|
+
senderName: params.senderName,
|
|
52
|
+
senderUsername: params.senderUsername,
|
|
53
|
+
senderE164: params.senderE164
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region extensions/whatsapp/src/security-fix.ts
|
|
58
|
+
function applyGroupAllowFromFromStore(params) {
|
|
59
|
+
const next = structuredClone(params.cfg ?? {});
|
|
60
|
+
const section = next.channels?.whatsapp;
|
|
61
|
+
if (!section || typeof section !== "object" || params.storeAllowFrom.length === 0) return params.cfg;
|
|
62
|
+
let changed = false;
|
|
63
|
+
const maybeApply = (prefix, holder) => {
|
|
64
|
+
if (holder.groupPolicy !== "allowlist") return;
|
|
65
|
+
const allowFrom = Array.isArray(holder.allowFrom) ? holder.allowFrom : [];
|
|
66
|
+
const groupAllowFrom = Array.isArray(holder.groupAllowFrom) ? holder.groupAllowFrom : [];
|
|
67
|
+
if (allowFrom.length > 0 || groupAllowFrom.length > 0) return;
|
|
68
|
+
holder.groupAllowFrom = params.storeAllowFrom;
|
|
69
|
+
params.changes.push(`${prefix}groupAllowFrom=pairing-store`);
|
|
70
|
+
changed = true;
|
|
71
|
+
};
|
|
72
|
+
maybeApply("channels.whatsapp.", section);
|
|
73
|
+
const accounts = section.accounts;
|
|
74
|
+
if (accounts && typeof accounts === "object") for (const [accountId, accountValue] of Object.entries(accounts)) {
|
|
75
|
+
if (!accountValue || typeof accountValue !== "object") continue;
|
|
76
|
+
maybeApply(`channels.whatsapp.accounts.${accountId}.`, accountValue);
|
|
77
|
+
}
|
|
78
|
+
return changed ? next : params.cfg;
|
|
79
|
+
}
|
|
80
|
+
async function applyWhatsAppSecurityConfigFixes(params) {
|
|
81
|
+
const fromStore = await readChannelAllowFromStore("whatsapp", params.env, DEFAULT_ACCOUNT_ID$1).catch(() => []);
|
|
82
|
+
const normalized = Array.from(new Set(fromStore.map((entry) => entry.trim()))).filter(Boolean);
|
|
83
|
+
if (normalized.length === 0) return {
|
|
84
|
+
config: params.cfg,
|
|
85
|
+
changes: []
|
|
86
|
+
};
|
|
87
|
+
const changes = [];
|
|
88
|
+
return {
|
|
89
|
+
config: applyGroupAllowFromFromStore({
|
|
90
|
+
cfg: params.cfg,
|
|
91
|
+
storeAllowFrom: normalized,
|
|
92
|
+
changes
|
|
93
|
+
}),
|
|
94
|
+
changes
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region extensions/whatsapp/src/shared.ts
|
|
99
|
+
const WHATSAPP_CHANNEL = "whatsapp";
|
|
100
|
+
const WHATSAPP_GROUP_SCOPE_FIELDS = [
|
|
101
|
+
"groupPolicy",
|
|
102
|
+
"groupAllowFrom",
|
|
103
|
+
"groups"
|
|
104
|
+
];
|
|
105
|
+
function resolveWhatsAppAccountKey(accounts, accountId) {
|
|
106
|
+
if (!accounts) return;
|
|
107
|
+
if (Object.hasOwn(accounts, accountId)) return accountId;
|
|
108
|
+
const normalizedAccountId = accountId.trim().toLowerCase();
|
|
109
|
+
return Object.keys(accounts).find((key) => key.trim().toLowerCase() === normalizedAccountId);
|
|
110
|
+
}
|
|
111
|
+
function resolveWhatsAppGroupScopeBasePath(params) {
|
|
112
|
+
const accountId = typeof params.accountId === "string" ? params.accountId.trim() || DEFAULT_ACCOUNT_ID : DEFAULT_ACCOUNT_ID;
|
|
113
|
+
const accounts = params.cfg.channels?.whatsapp?.accounts;
|
|
114
|
+
const accountKey = resolveWhatsAppAccountKey(accounts, accountId);
|
|
115
|
+
const defaultAccountKey = resolveWhatsAppAccountKey(accounts, DEFAULT_ACCOUNT_ID);
|
|
116
|
+
const accountConfig = accountKey ? accounts?.[accountKey] : void 0;
|
|
117
|
+
const defaultAccountConfig = defaultAccountKey ? accounts?.[defaultAccountKey] : void 0;
|
|
118
|
+
const matchesAnyGroupScopeField = (config) => WHATSAPP_GROUP_SCOPE_FIELDS.some((field) => config?.[field] !== void 0);
|
|
119
|
+
if (matchesAnyGroupScopeField(accountConfig)) return `channels.whatsapp.accounts.${accountKey}`;
|
|
120
|
+
if (accountId !== DEFAULT_ACCOUNT_ID && matchesAnyGroupScopeField(defaultAccountConfig)) return `channels.whatsapp.accounts.${defaultAccountKey}`;
|
|
121
|
+
return "channels.whatsapp";
|
|
122
|
+
}
|
|
123
|
+
function resolveWhatsAppConfigPath(params) {
|
|
124
|
+
return `${resolveWhatsAppGroupScopeBasePath(params)}.${params.field}`;
|
|
125
|
+
}
|
|
126
|
+
async function loadWhatsAppChannelRuntime() {
|
|
127
|
+
return await import("./channel.runtime-DKXRnA8x.js");
|
|
128
|
+
}
|
|
129
|
+
async function loadWhatsAppSetupSurface() {
|
|
130
|
+
return await import("./setup-surface-ieFnIW4g.js");
|
|
131
|
+
}
|
|
132
|
+
const whatsappSetupWizardProxy = createWhatsAppSetupWizardProxy(async () => (await loadWhatsAppSetupSurface()).whatsappSetupWizard);
|
|
133
|
+
const whatsappConfigAdapter = createScopedChannelConfigAdapter({
|
|
134
|
+
sectionKey: WHATSAPP_CHANNEL,
|
|
135
|
+
listAccountIds,
|
|
136
|
+
resolveAccount: adaptScopedAccountAccessor(resolveWhatsAppAccount),
|
|
137
|
+
defaultAccountId: resolveDefaultWhatsAppAccountId,
|
|
138
|
+
clearBaseFields: [],
|
|
139
|
+
allowTopLevel: false,
|
|
140
|
+
resolveAllowFrom: (account) => account.allowFrom,
|
|
141
|
+
formatAllowFrom: (allowFrom) => formatWhatsAppConfigAllowFromEntries(allowFrom),
|
|
142
|
+
resolveDefaultTo: (account) => account.defaultTo
|
|
143
|
+
});
|
|
144
|
+
const whatsappResolveDmPolicy = createScopedDmSecurityResolver({
|
|
145
|
+
channelKey: WHATSAPP_CHANNEL,
|
|
146
|
+
resolvePolicy: (account) => account.dmPolicy,
|
|
147
|
+
resolveAllowFrom: (account) => account.allowFrom,
|
|
148
|
+
policyPathSuffix: "dmPolicy",
|
|
149
|
+
normalizeEntry: (raw) => normalizeE164(raw),
|
|
150
|
+
inheritSharedDefaultsFromDefaultAccount: true
|
|
151
|
+
});
|
|
152
|
+
function createWhatsAppSetupWizardProxy(loadWizard) {
|
|
153
|
+
return createDelegatedSetupWizardProxy({
|
|
154
|
+
channel: WHATSAPP_CHANNEL,
|
|
155
|
+
loadWizard,
|
|
156
|
+
status: {
|
|
157
|
+
configuredLabel: "linked",
|
|
158
|
+
unconfiguredLabel: "not linked",
|
|
159
|
+
configuredHint: "linked",
|
|
160
|
+
unconfiguredHint: "not linked",
|
|
161
|
+
configuredScore: 5,
|
|
162
|
+
unconfiguredScore: 4
|
|
163
|
+
},
|
|
164
|
+
resolveShouldPromptAccountIds: (params) => params.shouldPromptAccountIds,
|
|
165
|
+
credentials: [],
|
|
166
|
+
delegateFinalize: true,
|
|
167
|
+
disable: (cfg) => ({
|
|
168
|
+
...cfg,
|
|
169
|
+
channels: {
|
|
170
|
+
...cfg.channels,
|
|
171
|
+
whatsapp: {
|
|
172
|
+
...cfg.channels?.whatsapp,
|
|
173
|
+
enabled: false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}),
|
|
177
|
+
onAccountRecorded: (accountId, options) => {
|
|
178
|
+
options?.onAccountId?.(WHATSAPP_CHANNEL, accountId);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function createWhatsAppPluginBase(params) {
|
|
183
|
+
const collectWhatsAppSecurityWarnings = createAllowlistProviderGroupPolicyWarningCollector({
|
|
184
|
+
providerConfigPresent: (cfg) => cfg.channels?.whatsapp !== void 0,
|
|
185
|
+
resolveGroupPolicy: ({ account }) => account.groupPolicy,
|
|
186
|
+
collect: ({ account, accountId, cfg, groupPolicy }) => collectOpenGroupPolicyRouteAllowlistWarnings({
|
|
187
|
+
groupPolicy,
|
|
188
|
+
routeAllowlistConfigured: Boolean(account.groups) && Object.keys(account.groups ?? {}).length > 0,
|
|
189
|
+
restrictSenders: {
|
|
190
|
+
surface: "WhatsApp groups",
|
|
191
|
+
openScope: "any member in allowed groups",
|
|
192
|
+
groupPolicyPath: resolveWhatsAppConfigPath({
|
|
193
|
+
cfg,
|
|
194
|
+
accountId,
|
|
195
|
+
field: "groupPolicy"
|
|
196
|
+
}),
|
|
197
|
+
groupAllowFromPath: resolveWhatsAppConfigPath({
|
|
198
|
+
cfg,
|
|
199
|
+
accountId,
|
|
200
|
+
field: "groupAllowFrom"
|
|
201
|
+
})
|
|
202
|
+
},
|
|
203
|
+
noRouteAllowlist: {
|
|
204
|
+
surface: "WhatsApp groups",
|
|
205
|
+
routeAllowlistPath: resolveWhatsAppConfigPath({
|
|
206
|
+
cfg,
|
|
207
|
+
accountId,
|
|
208
|
+
field: "groups"
|
|
209
|
+
}),
|
|
210
|
+
routeScope: "group",
|
|
211
|
+
groupPolicyPath: resolveWhatsAppConfigPath({
|
|
212
|
+
cfg,
|
|
213
|
+
accountId,
|
|
214
|
+
field: "groupPolicy"
|
|
215
|
+
}),
|
|
216
|
+
groupAllowFromPath: resolveWhatsAppConfigPath({
|
|
217
|
+
cfg,
|
|
218
|
+
accountId,
|
|
219
|
+
field: "groupAllowFrom"
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
const base = createChannelPluginBase({
|
|
225
|
+
id: WHATSAPP_CHANNEL,
|
|
226
|
+
meta: {
|
|
227
|
+
...getChatChannelMeta(WHATSAPP_CHANNEL),
|
|
228
|
+
showConfigured: false,
|
|
229
|
+
quickstartAllowFrom: true,
|
|
230
|
+
forceAccountBinding: true,
|
|
231
|
+
preferSessionLookupForAnnounceTarget: true
|
|
232
|
+
},
|
|
233
|
+
setupWizard: params.setupWizard,
|
|
234
|
+
capabilities: {
|
|
235
|
+
chatTypes: [
|
|
236
|
+
"direct",
|
|
237
|
+
"group",
|
|
238
|
+
"channel"
|
|
239
|
+
],
|
|
240
|
+
polls: true,
|
|
241
|
+
reactions: true,
|
|
242
|
+
media: true,
|
|
243
|
+
tts: { voice: {
|
|
244
|
+
synthesisTarget: "voice-note",
|
|
245
|
+
transcodesAudio: true
|
|
246
|
+
} }
|
|
247
|
+
},
|
|
248
|
+
reload: {
|
|
249
|
+
configPrefixes: ["web"],
|
|
250
|
+
noopPrefixes: ["channels.whatsapp"]
|
|
251
|
+
},
|
|
252
|
+
gatewayMethods: ["web.login.start", "web.login.wait"],
|
|
253
|
+
configSchema: WhatsAppChannelConfigSchema,
|
|
254
|
+
config: {
|
|
255
|
+
...whatsappConfigAdapter,
|
|
256
|
+
isEnabled: (account, cfg) => account.enabled && cfg.web?.enabled !== false,
|
|
257
|
+
disabledReason: () => "disabled",
|
|
258
|
+
isConfigured: params.isConfigured,
|
|
259
|
+
hasPersistedAuthState: ({ cfg }) => hasAnyWhatsAppAuth(cfg),
|
|
260
|
+
unconfiguredReason: () => "not linked",
|
|
261
|
+
describeAccount: (account) => describeAccountSnapshot({
|
|
262
|
+
account,
|
|
263
|
+
configured: Boolean(account.authDir),
|
|
264
|
+
extra: {
|
|
265
|
+
linked: Boolean(account.authDir),
|
|
266
|
+
dmPolicy: account.dmPolicy,
|
|
267
|
+
allowFrom: account.allowFrom
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
},
|
|
271
|
+
security: {
|
|
272
|
+
applyConfigFixes: applyWhatsAppSecurityConfigFixes,
|
|
273
|
+
resolveDmPolicy: whatsappResolveDmPolicy,
|
|
274
|
+
collectWarnings: collectWhatsAppSecurityWarnings
|
|
275
|
+
},
|
|
276
|
+
doctor: whatsappDoctor,
|
|
277
|
+
setup: params.setup,
|
|
278
|
+
groups: params.groups
|
|
279
|
+
});
|
|
280
|
+
return {
|
|
281
|
+
...base,
|
|
282
|
+
setupWizard: base.setupWizard,
|
|
283
|
+
capabilities: base.capabilities,
|
|
284
|
+
reload: base.reload,
|
|
285
|
+
gatewayMethods: base.gatewayMethods,
|
|
286
|
+
configSchema: base.configSchema,
|
|
287
|
+
config: base.config,
|
|
288
|
+
messaging: {
|
|
289
|
+
defaultMarkdownTableMode: "bullets",
|
|
290
|
+
deriveLegacySessionChatType,
|
|
291
|
+
resolveLegacyGroupSessionKey,
|
|
292
|
+
isLegacyGroupSessionKey,
|
|
293
|
+
canonicalizeLegacySessionKey: (params) => canonicalizeLegacySessionKey({
|
|
294
|
+
key: params.key,
|
|
295
|
+
agentId: params.agentId
|
|
296
|
+
})
|
|
297
|
+
},
|
|
298
|
+
secrets: {
|
|
299
|
+
unsupportedSecretRefSurfacePatterns,
|
|
300
|
+
collectUnsupportedSecretRefConfigCandidates
|
|
301
|
+
},
|
|
302
|
+
security: base.security,
|
|
303
|
+
groups: base.groups
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
export { resolveWhatsAppGroupToolPolicy as a, formatWhatsAppConfigAllowFromEntries as c, resolveWhatsAppGroupRequireMention as i, loadWhatsAppChannelRuntime as n, resolveWhatsAppGroupIntroHint as o, whatsappSetupWizardProxy as r, resolveWhatsAppMentionStripRegexes as s, createWhatsAppPluginBase as t };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
4
|
+
//#region extensions/whatsapp/src/state-migrations.ts
|
|
5
|
+
function fileExists(pathValue) {
|
|
6
|
+
try {
|
|
7
|
+
return fs.existsSync(pathValue) && fs.statSync(pathValue).isFile();
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function isLegacyWhatsAppAuthFile(name) {
|
|
13
|
+
if (name === "creds.json" || name === "creds.json.bak") return true;
|
|
14
|
+
if (!name.endsWith(".json")) return false;
|
|
15
|
+
return /^(app-state-sync|session|sender-key|pre-key)-/.test(name);
|
|
16
|
+
}
|
|
17
|
+
function detectWhatsAppLegacyStateMigrations(params) {
|
|
18
|
+
const targetDir = path.join(params.oauthDir, "whatsapp", DEFAULT_ACCOUNT_ID);
|
|
19
|
+
return (() => {
|
|
20
|
+
try {
|
|
21
|
+
return fs.readdirSync(params.oauthDir, { withFileTypes: true });
|
|
22
|
+
} catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
})().flatMap((entry) => {
|
|
26
|
+
if (!entry.isFile() || entry.name === "oauth.json" || !isLegacyWhatsAppAuthFile(entry.name)) return [];
|
|
27
|
+
const sourcePath = path.join(params.oauthDir, entry.name);
|
|
28
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
29
|
+
if (fileExists(targetPath)) return [];
|
|
30
|
+
return [{
|
|
31
|
+
kind: "move",
|
|
32
|
+
label: `WhatsApp auth ${entry.name}`,
|
|
33
|
+
sourcePath,
|
|
34
|
+
targetPath
|
|
35
|
+
}];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { detectWhatsAppLegacyStateMigrations as t };
|
package/dist/targets.js
ADDED
package/dist/test-api.js
ADDED