@openclaw/whatsapp 2026.5.2-beta.2 → 2026.5.3-beta.1
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 -334
- 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 -307
- package/src/inbound/send-api.ts +0 -141
- 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 -112
- package/src/normalize.ts +0 -6
- 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 -265
- package/src/resolve-outbound-target.ts +0 -54
- package/src/resolve-target.test.ts +0 -97
- 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 -515
- package/src/send.ts +0 -287
- 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.ts +0 -26
- 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
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import fsSync from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { resetLogger, setLoggerOverride } from "openclaw/plugin-sdk/runtime-env";
|
|
6
|
-
import { afterEach, beforeEach, expect, vi } from "vitest";
|
|
7
|
-
import {
|
|
8
|
-
loadConfigMock,
|
|
9
|
-
readAllowFromStoreMock as pairingReadAllowFromStoreMock,
|
|
10
|
-
resetPairingSecurityMocks,
|
|
11
|
-
upsertPairingRequestMock as pairingUpsertPairingRequestMock,
|
|
12
|
-
} from "./pairing-security.test-harness.js";
|
|
13
|
-
|
|
14
|
-
// Avoid exporting vitest mock types (TS2742 under pnpm + d.ts emit).
|
|
15
|
-
type AnyMockFn = any;
|
|
16
|
-
|
|
17
|
-
export const DEFAULT_ACCOUNT_ID = "default";
|
|
18
|
-
|
|
19
|
-
export const DEFAULT_WEB_INBOX_CONFIG = {
|
|
20
|
-
channels: {
|
|
21
|
-
whatsapp: {
|
|
22
|
-
// Allow all in tests by default.
|
|
23
|
-
allowFrom: ["*"],
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
messages: {
|
|
27
|
-
messagePrefix: undefined,
|
|
28
|
-
responsePrefix: undefined,
|
|
29
|
-
},
|
|
30
|
-
} as const;
|
|
31
|
-
export const mockLoadConfig: typeof loadConfigMock = loadConfigMock;
|
|
32
|
-
export const readAllowFromStoreMock = pairingReadAllowFromStoreMock;
|
|
33
|
-
export const upsertPairingRequestMock = pairingUpsertPairingRequestMock;
|
|
34
|
-
|
|
35
|
-
export type MockSock = {
|
|
36
|
-
ev: EventEmitter;
|
|
37
|
-
end: AnyMockFn;
|
|
38
|
-
ws: { close: AnyMockFn };
|
|
39
|
-
sendPresenceUpdate: AnyMockFn;
|
|
40
|
-
sendMessage: AnyMockFn;
|
|
41
|
-
readMessages: AnyMockFn;
|
|
42
|
-
groupMetadata: AnyMockFn;
|
|
43
|
-
groupFetchAllParticipating: AnyMockFn;
|
|
44
|
-
updateMediaMessage: AnyMockFn;
|
|
45
|
-
logger: Record<string, unknown>;
|
|
46
|
-
signalRepository: {
|
|
47
|
-
lidMapping: {
|
|
48
|
-
getPNForLID: AnyMockFn;
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
user: { id: string };
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const sessionState = vi.hoisted(() => ({
|
|
55
|
-
sock: undefined as MockSock | undefined,
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
const inboundRuntimeMocks = vi.hoisted(() => {
|
|
59
|
-
const wrapperKeys = [
|
|
60
|
-
"ephemeralMessage",
|
|
61
|
-
"viewOnceMessage",
|
|
62
|
-
"viewOnceMessageV2",
|
|
63
|
-
"viewOnceMessageV2Extension",
|
|
64
|
-
"documentWithCaptionMessage",
|
|
65
|
-
] as const;
|
|
66
|
-
|
|
67
|
-
function normalizeMessageContent(message: unknown): unknown {
|
|
68
|
-
let current = message;
|
|
69
|
-
while (current && typeof current === "object") {
|
|
70
|
-
const record = current as Record<string, unknown>;
|
|
71
|
-
const wrapper = wrapperKeys
|
|
72
|
-
.map((key) => record[key])
|
|
73
|
-
.find(
|
|
74
|
-
(candidate): candidate is { message: unknown } =>
|
|
75
|
-
Boolean(candidate) &&
|
|
76
|
-
typeof candidate === "object" &&
|
|
77
|
-
"message" in (candidate as Record<string, unknown>) &&
|
|
78
|
-
Boolean((candidate as { message?: unknown }).message),
|
|
79
|
-
);
|
|
80
|
-
if (!wrapper) {
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
current = wrapper.message;
|
|
84
|
-
}
|
|
85
|
-
return current;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
downloadMediaMessage: vi.fn().mockResolvedValue(Buffer.from("fake-media-data")),
|
|
90
|
-
isJidGroup: vi.fn((jid: string | undefined | null) =>
|
|
91
|
-
typeof jid === "string" ? jid.endsWith("@g.us") : false,
|
|
92
|
-
),
|
|
93
|
-
normalizeMessageContent: vi.fn(normalizeMessageContent),
|
|
94
|
-
saveMediaBuffer: vi.fn().mockResolvedValue({
|
|
95
|
-
id: "mid",
|
|
96
|
-
path: "/tmp/mid",
|
|
97
|
-
size: 1,
|
|
98
|
-
contentType: "image/jpeg",
|
|
99
|
-
}),
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
function createResolvedMock() {
|
|
104
|
-
return vi.fn().mockResolvedValue(undefined);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function createMockSock(): MockSock {
|
|
108
|
-
const ev = new EventEmitter();
|
|
109
|
-
return {
|
|
110
|
-
ev,
|
|
111
|
-
end: vi.fn(),
|
|
112
|
-
ws: { close: vi.fn() },
|
|
113
|
-
sendPresenceUpdate: createResolvedMock(),
|
|
114
|
-
sendMessage: createResolvedMock(),
|
|
115
|
-
readMessages: createResolvedMock(),
|
|
116
|
-
groupMetadata: vi.fn().mockImplementation(async (jid: string) => ({
|
|
117
|
-
id: jid,
|
|
118
|
-
subject: "Test Group",
|
|
119
|
-
owner: undefined,
|
|
120
|
-
participants: [],
|
|
121
|
-
})),
|
|
122
|
-
groupFetchAllParticipating: vi.fn().mockResolvedValue({}),
|
|
123
|
-
updateMediaMessage: vi.fn(),
|
|
124
|
-
logger: {},
|
|
125
|
-
signalRepository: {
|
|
126
|
-
lidMapping: {
|
|
127
|
-
getPNForLID: vi.fn().mockResolvedValue(null),
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
user: { id: "123@s.whatsapp.net" },
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
vi.mock("./inbound/runtime-api.js", () => {
|
|
135
|
-
return {
|
|
136
|
-
DisconnectReason: { loggedOut: 401 },
|
|
137
|
-
...inboundRuntimeMocks,
|
|
138
|
-
};
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
vi.mock("./session.js", async () => {
|
|
142
|
-
return {
|
|
143
|
-
createWaSocket: vi.fn().mockImplementation(async () => {
|
|
144
|
-
if (!sessionState.sock) {
|
|
145
|
-
throw new Error("mock WhatsApp socket not initialized");
|
|
146
|
-
}
|
|
147
|
-
return sessionState.sock;
|
|
148
|
-
}),
|
|
149
|
-
waitForWaConnection: vi.fn().mockResolvedValue(undefined),
|
|
150
|
-
getStatusCode: vi.fn(() => 500),
|
|
151
|
-
formatError: (err: unknown) => (err instanceof Error ? err.message : String(err)),
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
export function getSock(): MockSock {
|
|
156
|
-
if (!sessionState.sock) {
|
|
157
|
-
throw new Error("mock WhatsApp socket not initialized");
|
|
158
|
-
}
|
|
159
|
-
return sessionState.sock;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
type MonitorWebInbox = typeof import("./inbound.js").monitorWebInbox;
|
|
163
|
-
type ResetWebInboundDedupe = typeof import("./inbound.js").resetWebInboundDedupe;
|
|
164
|
-
export type InboxOnMessage = NonNullable<Parameters<MonitorWebInbox>[0]["onMessage"]>;
|
|
165
|
-
export type InboxMonitorOptions = Parameters<MonitorWebInbox>[0];
|
|
166
|
-
let monitorWebInbox: MonitorWebInbox;
|
|
167
|
-
let resetWebInboundDedupe: ResetWebInboundDedupe;
|
|
168
|
-
|
|
169
|
-
function expectInboxPairingReplyText(
|
|
170
|
-
text: string,
|
|
171
|
-
params: {
|
|
172
|
-
channel: string;
|
|
173
|
-
idLine: string;
|
|
174
|
-
code?: string;
|
|
175
|
-
},
|
|
176
|
-
): string {
|
|
177
|
-
const code = text.match(/Pairing code:\s*```[\r\n]+([A-Z2-9]{6,})/)?.[1];
|
|
178
|
-
expect(code).toBeDefined();
|
|
179
|
-
const resolvedCode = params.code ?? code ?? "";
|
|
180
|
-
expect(text).toContain("OpenClaw: access not configured.");
|
|
181
|
-
expect(text).toContain(params.idLine);
|
|
182
|
-
expect(text).toContain("Pairing code:");
|
|
183
|
-
expect(text).toContain(`\n\`\`\`\n${resolvedCode}\n\`\`\`\n`);
|
|
184
|
-
expect(text).toContain(`pairing approve ${params.channel} ${resolvedCode}`);
|
|
185
|
-
return resolvedCode;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function getMonitorWebInbox(): MonitorWebInbox {
|
|
189
|
-
if (!monitorWebInbox) {
|
|
190
|
-
throw new Error("monitorWebInbox not initialized");
|
|
191
|
-
}
|
|
192
|
-
return monitorWebInbox;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export async function settleInboundWork() {
|
|
196
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
197
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export async function waitForMessageCalls(onMessage: ReturnType<typeof vi.fn>, count: number) {
|
|
201
|
-
await vi.waitFor(
|
|
202
|
-
() => {
|
|
203
|
-
expect(onMessage).toHaveBeenCalledTimes(count);
|
|
204
|
-
},
|
|
205
|
-
// Channel-suite workers can be saturated under no-isolate CI runs.
|
|
206
|
-
{ timeout: 5_000, interval: 5 },
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export async function startInboxMonitor(
|
|
211
|
-
onMessage: InboxOnMessage,
|
|
212
|
-
extraOptions: Partial<InboxMonitorOptions> = {},
|
|
213
|
-
) {
|
|
214
|
-
if (!monitorWebInbox) {
|
|
215
|
-
({ monitorWebInbox } = await import("./inbound.js"));
|
|
216
|
-
}
|
|
217
|
-
const listener = await monitorWebInbox({
|
|
218
|
-
cfg: mockLoadConfig() as never,
|
|
219
|
-
verbose: false,
|
|
220
|
-
onMessage,
|
|
221
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
222
|
-
authDir: getAuthDir(),
|
|
223
|
-
...extraOptions,
|
|
224
|
-
});
|
|
225
|
-
return { listener, sock: getSock() };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function buildNotifyMessageUpsert(params: {
|
|
229
|
-
id: string;
|
|
230
|
-
remoteJid: string;
|
|
231
|
-
text: string;
|
|
232
|
-
timestamp: number;
|
|
233
|
-
pushName?: string;
|
|
234
|
-
participant?: string;
|
|
235
|
-
}) {
|
|
236
|
-
return {
|
|
237
|
-
type: "notify",
|
|
238
|
-
messages: [
|
|
239
|
-
{
|
|
240
|
-
key: {
|
|
241
|
-
id: params.id,
|
|
242
|
-
fromMe: false,
|
|
243
|
-
remoteJid: params.remoteJid,
|
|
244
|
-
participant: params.participant,
|
|
245
|
-
},
|
|
246
|
-
message: { conversation: params.text },
|
|
247
|
-
messageTimestamp: params.timestamp,
|
|
248
|
-
pushName: params.pushName,
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export function expectPairingPromptSent(sock: MockSock, jid: string, senderE164: string) {
|
|
255
|
-
expect(sock.sendMessage).toHaveBeenCalledTimes(1);
|
|
256
|
-
const sendCall = sock.sendMessage.mock.calls[0];
|
|
257
|
-
expect(sendCall?.[0]).toBe(jid);
|
|
258
|
-
expectInboxPairingReplyText((sendCall?.[1] as { text?: string } | undefined)?.text ?? "", {
|
|
259
|
-
channel: "whatsapp",
|
|
260
|
-
idLine: `Your WhatsApp phone number: ${senderE164}`,
|
|
261
|
-
code: "PAIRCODE",
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
let authDir: string | undefined;
|
|
266
|
-
|
|
267
|
-
export function getAuthDir(): string {
|
|
268
|
-
if (!authDir) {
|
|
269
|
-
throw new Error("authDir not initialized; call installWebMonitorInboxUnitTestHooks()");
|
|
270
|
-
}
|
|
271
|
-
return authDir;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export function installWebMonitorInboxUnitTestHooks(opts?: { authDir?: boolean }) {
|
|
275
|
-
const createAuthDir = opts?.authDir ?? true;
|
|
276
|
-
|
|
277
|
-
beforeEach(async () => {
|
|
278
|
-
vi.useRealTimers();
|
|
279
|
-
vi.clearAllMocks();
|
|
280
|
-
sessionState.sock = createMockSock();
|
|
281
|
-
resetPairingSecurityMocks(DEFAULT_WEB_INBOX_CONFIG);
|
|
282
|
-
if (!monitorWebInbox || !resetWebInboundDedupe) {
|
|
283
|
-
const inboundModule = await import("./inbound.js");
|
|
284
|
-
monitorWebInbox = inboundModule.monitorWebInbox;
|
|
285
|
-
resetWebInboundDedupe = inboundModule.resetWebInboundDedupe;
|
|
286
|
-
}
|
|
287
|
-
resetWebInboundDedupe();
|
|
288
|
-
if (createAuthDir) {
|
|
289
|
-
authDir = fsSync.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-"));
|
|
290
|
-
} else {
|
|
291
|
-
authDir = undefined;
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
afterEach(() => {
|
|
296
|
-
resetLogger();
|
|
297
|
-
setLoggerOverride(null);
|
|
298
|
-
vi.useRealTimers();
|
|
299
|
-
if (authDir) {
|
|
300
|
-
fsSync.rmSync(authDir, { recursive: true, force: true });
|
|
301
|
-
authDir = undefined;
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
}
|
package/src/normalize-target.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
|
|
2
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
3
|
-
|
|
4
|
-
const WHATSAPP_USER_JID_RE = /^(\d+)(?::\d+)?@s\.whatsapp\.net$/i;
|
|
5
|
-
const WHATSAPP_LEGACY_USER_JID_RE = /^(\d+)@c\.us$/i;
|
|
6
|
-
const WHATSAPP_LID_RE = /^(\d+)@lid$/i;
|
|
7
|
-
const NON_WHATSAPP_PROVIDER_PREFIX_RE = /^[a-z][a-z0-9-]*:/i;
|
|
8
|
-
|
|
9
|
-
function stripWhatsAppTargetPrefixes(value: string): string {
|
|
10
|
-
let candidate = value.trim();
|
|
11
|
-
for (;;) {
|
|
12
|
-
const before = candidate;
|
|
13
|
-
candidate = candidate.replace(/^whatsapp:/i, "").trim();
|
|
14
|
-
if (candidate === before) {
|
|
15
|
-
return candidate;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function isWhatsAppGroupJid(value: string): boolean {
|
|
21
|
-
const candidate = stripWhatsAppTargetPrefixes(value);
|
|
22
|
-
const lower = normalizeLowercaseStringOrEmpty(candidate);
|
|
23
|
-
if (!lower.endsWith("@g.us")) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const localPart = candidate.slice(0, candidate.length - "@g.us".length);
|
|
27
|
-
if (!localPart || localPart.includes("@")) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return /^[0-9]+(-[0-9]+)*$/.test(localPart);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function isWhatsAppUserTarget(value: string): boolean {
|
|
34
|
-
const candidate = stripWhatsAppTargetPrefixes(value);
|
|
35
|
-
return (
|
|
36
|
-
WHATSAPP_USER_JID_RE.test(candidate) ||
|
|
37
|
-
WHATSAPP_LEGACY_USER_JID_RE.test(candidate) ||
|
|
38
|
-
WHATSAPP_LID_RE.test(candidate)
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function extractUserJidPhone(jid: string): string | null {
|
|
43
|
-
const userMatch = jid.match(WHATSAPP_USER_JID_RE);
|
|
44
|
-
if (userMatch) {
|
|
45
|
-
return userMatch[1];
|
|
46
|
-
}
|
|
47
|
-
const legacyUserMatch = jid.match(WHATSAPP_LEGACY_USER_JID_RE);
|
|
48
|
-
if (legacyUserMatch) {
|
|
49
|
-
return legacyUserMatch[1];
|
|
50
|
-
}
|
|
51
|
-
const lidMatch = jid.match(WHATSAPP_LID_RE);
|
|
52
|
-
if (lidMatch) {
|
|
53
|
-
return lidMatch[1];
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function normalizeWhatsAppTarget(value: string): string | null {
|
|
59
|
-
const candidate = stripWhatsAppTargetPrefixes(value);
|
|
60
|
-
if (!candidate) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
if (isWhatsAppGroupJid(candidate)) {
|
|
64
|
-
const localPart = candidate.slice(0, candidate.length - "@g.us".length);
|
|
65
|
-
return `${localPart}@g.us`;
|
|
66
|
-
}
|
|
67
|
-
if (isWhatsAppUserTarget(candidate)) {
|
|
68
|
-
const phone = extractUserJidPhone(candidate);
|
|
69
|
-
if (!phone) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const normalized = normalizeE164(phone);
|
|
73
|
-
return normalized.length > 1 ? normalized : null;
|
|
74
|
-
}
|
|
75
|
-
if (candidate.includes("@")) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
if (NON_WHATSAPP_PROVIDER_PREFIX_RE.test(candidate)) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const normalized = normalizeE164(candidate);
|
|
82
|
-
return normalized.length > 1 ? normalized : null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function normalizeWhatsAppMessagingTarget(raw: string): string | undefined {
|
|
86
|
-
const trimmed = raw.trim();
|
|
87
|
-
if (!trimmed) {
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
return normalizeWhatsAppTarget(trimmed) ?? undefined;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function normalizeWhatsAppAllowFromEntries(allowFrom: Array<string | number>): string[] {
|
|
94
|
-
return allowFrom
|
|
95
|
-
.map((entry) => String(entry).trim())
|
|
96
|
-
.filter((entry): entry is string => Boolean(entry))
|
|
97
|
-
.map((entry) => (entry === "*" ? entry : normalizeWhatsAppTarget(entry)))
|
|
98
|
-
.filter((entry): entry is string => Boolean(entry));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function looksLikeWhatsAppTargetId(raw: string): boolean {
|
|
102
|
-
const trimmed = raw.trim();
|
|
103
|
-
if (!trimmed) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
return (
|
|
107
|
-
/^whatsapp:/i.test(trimmed) ||
|
|
108
|
-
isWhatsAppGroupJid(trimmed) ||
|
|
109
|
-
isWhatsAppUserTarget(trimmed) ||
|
|
110
|
-
normalizeWhatsAppTarget(trimmed) !== null
|
|
111
|
-
);
|
|
112
|
-
}
|
package/src/normalize.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
|
|
2
|
-
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
const hoisted = vi.hoisted(() => ({
|
|
5
|
-
sendPollWhatsApp: vi.fn(async () => ({ messageId: "poll-1", toJid: "1555@s.whatsapp.net" })),
|
|
6
|
-
sendReactionWhatsApp: vi.fn(async () => undefined),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
|
|
10
|
-
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/runtime-env")>(
|
|
11
|
-
"openclaw/plugin-sdk/runtime-env",
|
|
12
|
-
);
|
|
13
|
-
return {
|
|
14
|
-
...actual,
|
|
15
|
-
shouldLogVerbose: () => false,
|
|
16
|
-
};
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
vi.mock("./send.js", () => ({
|
|
20
|
-
sendPollWhatsApp: hoisted.sendPollWhatsApp,
|
|
21
|
-
sendReactionWhatsApp: hoisted.sendReactionWhatsApp,
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
let whatsappOutbound: typeof import("./outbound-adapter.js").whatsappOutbound;
|
|
25
|
-
|
|
26
|
-
describe("whatsappOutbound sendPoll", () => {
|
|
27
|
-
beforeAll(async () => {
|
|
28
|
-
({ whatsappOutbound } = await import("./outbound-adapter.js"));
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
hoisted.sendPollWhatsApp.mockClear();
|
|
33
|
-
hoisted.sendReactionWhatsApp.mockClear();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("threads cfg through poll send options", async () => {
|
|
37
|
-
const cfg = { marker: "resolved-cfg" } as OpenClawConfig;
|
|
38
|
-
const poll = {
|
|
39
|
-
question: "Lunch?",
|
|
40
|
-
options: ["Pizza", "Sushi"],
|
|
41
|
-
maxSelections: 1,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const result = await whatsappOutbound.sendPoll!({
|
|
45
|
-
cfg,
|
|
46
|
-
to: "+1555",
|
|
47
|
-
poll,
|
|
48
|
-
accountId: "work",
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
expect(hoisted.sendPollWhatsApp).toHaveBeenCalledWith("+1555", poll, {
|
|
52
|
-
verbose: false,
|
|
53
|
-
accountId: "work",
|
|
54
|
-
cfg,
|
|
55
|
-
});
|
|
56
|
-
expect(result).toEqual({
|
|
57
|
-
channel: "whatsapp",
|
|
58
|
-
messageId: "poll-1",
|
|
59
|
-
toJid: "1555@s.whatsapp.net",
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { whatsappOutbound } from "./outbound-adapter.js";
|
|
3
|
-
|
|
4
|
-
describe("whatsappOutbound sendPayload", () => {
|
|
5
|
-
it("trims leading whitespace for direct text sends", async () => {
|
|
6
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
7
|
-
|
|
8
|
-
await whatsappOutbound.sendText!({
|
|
9
|
-
cfg: {},
|
|
10
|
-
to: "5511999999999@c.us",
|
|
11
|
-
text: "\n \thello",
|
|
12
|
-
deps: { sendWhatsApp },
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
expect(sendWhatsApp).toHaveBeenCalledWith("5511999999999@c.us", "hello", {
|
|
16
|
-
verbose: false,
|
|
17
|
-
cfg: {},
|
|
18
|
-
accountId: undefined,
|
|
19
|
-
gifPlayback: undefined,
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("uses the same final sanitizer stack for direct text sends", async () => {
|
|
24
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
25
|
-
|
|
26
|
-
await whatsappOutbound.sendText!({
|
|
27
|
-
cfg: {},
|
|
28
|
-
to: "5511999999999@c.us",
|
|
29
|
-
text: [
|
|
30
|
-
"Before",
|
|
31
|
-
"<function_calls>",
|
|
32
|
-
' <invoke name="send_message">',
|
|
33
|
-
' <parameter name="text"><b>hidden</b></parameter>',
|
|
34
|
-
" </invoke>",
|
|
35
|
-
"</function_calls>",
|
|
36
|
-
"<div>After</div>",
|
|
37
|
-
].join("\n"),
|
|
38
|
-
deps: { sendWhatsApp },
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(sendWhatsApp).toHaveBeenCalledWith(
|
|
42
|
-
"5511999999999@c.us",
|
|
43
|
-
"Before\n\nAfter\n",
|
|
44
|
-
expect.objectContaining({
|
|
45
|
-
verbose: false,
|
|
46
|
-
cfg: {},
|
|
47
|
-
accountId: undefined,
|
|
48
|
-
gifPlayback: undefined,
|
|
49
|
-
}),
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("trims leading whitespace for direct media captions", async () => {
|
|
54
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
55
|
-
|
|
56
|
-
await whatsappOutbound.sendMedia!({
|
|
57
|
-
cfg: {},
|
|
58
|
-
to: "5511999999999@c.us",
|
|
59
|
-
text: "\n \tcaption",
|
|
60
|
-
mediaUrl: "/tmp/test.png",
|
|
61
|
-
deps: { sendWhatsApp },
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(sendWhatsApp).toHaveBeenCalledWith("5511999999999@c.us", "caption", {
|
|
65
|
-
verbose: false,
|
|
66
|
-
cfg: {},
|
|
67
|
-
mediaUrl: "/tmp/test.png",
|
|
68
|
-
mediaLocalRoots: undefined,
|
|
69
|
-
accountId: undefined,
|
|
70
|
-
gifPlayback: undefined,
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("trims leading whitespace for sendPayload text and caption delivery", async () => {
|
|
75
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
76
|
-
|
|
77
|
-
await whatsappOutbound.sendPayload!({
|
|
78
|
-
cfg: {},
|
|
79
|
-
to: "5511999999999@c.us",
|
|
80
|
-
text: "",
|
|
81
|
-
payload: { text: "\n\nhello" },
|
|
82
|
-
deps: { sendWhatsApp },
|
|
83
|
-
});
|
|
84
|
-
await whatsappOutbound.sendPayload!({
|
|
85
|
-
cfg: {},
|
|
86
|
-
to: "5511999999999@c.us",
|
|
87
|
-
text: "",
|
|
88
|
-
payload: { text: "\n\ncaption", mediaUrl: "/tmp/test.png" },
|
|
89
|
-
deps: { sendWhatsApp },
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
expect(sendWhatsApp).toHaveBeenNthCalledWith(1, "5511999999999@c.us", "hello", {
|
|
93
|
-
verbose: false,
|
|
94
|
-
cfg: {},
|
|
95
|
-
accountId: undefined,
|
|
96
|
-
gifPlayback: undefined,
|
|
97
|
-
});
|
|
98
|
-
expect(sendWhatsApp).toHaveBeenNthCalledWith(2, "5511999999999@c.us", "caption", {
|
|
99
|
-
verbose: false,
|
|
100
|
-
cfg: {},
|
|
101
|
-
mediaUrl: "/tmp/test.png",
|
|
102
|
-
mediaLocalRoots: undefined,
|
|
103
|
-
accountId: undefined,
|
|
104
|
-
gifPlayback: undefined,
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("preserves audioAsVoice from payload media sends", async () => {
|
|
109
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
110
|
-
|
|
111
|
-
await whatsappOutbound.sendPayload!({
|
|
112
|
-
cfg: {},
|
|
113
|
-
to: "5511999999999@c.us",
|
|
114
|
-
text: "",
|
|
115
|
-
payload: { text: "voice", mediaUrl: "/tmp/voice.ogg", audioAsVoice: true },
|
|
116
|
-
deps: { sendWhatsApp },
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
expect(sendWhatsApp).toHaveBeenCalledWith("5511999999999@c.us", "voice", {
|
|
120
|
-
verbose: false,
|
|
121
|
-
cfg: {},
|
|
122
|
-
mediaUrl: "/tmp/voice.ogg",
|
|
123
|
-
mediaLocalRoots: undefined,
|
|
124
|
-
audioAsVoice: true,
|
|
125
|
-
accountId: undefined,
|
|
126
|
-
gifPlayback: undefined,
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("drops blank mediaUrls before sending payload media", async () => {
|
|
131
|
-
const sendWhatsApp = vi.fn(async () => ({ messageId: "wa-1", toJid: "jid" }));
|
|
132
|
-
|
|
133
|
-
await whatsappOutbound.sendPayload!({
|
|
134
|
-
cfg: {},
|
|
135
|
-
to: "5511999999999@c.us",
|
|
136
|
-
text: "",
|
|
137
|
-
payload: {
|
|
138
|
-
text: "\n\ncaption",
|
|
139
|
-
mediaUrls: [" ", " /tmp/voice.ogg "],
|
|
140
|
-
},
|
|
141
|
-
deps: { sendWhatsApp },
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
expect(sendWhatsApp).toHaveBeenCalledTimes(1);
|
|
145
|
-
expect(sendWhatsApp).toHaveBeenCalledWith("5511999999999@c.us", "caption", {
|
|
146
|
-
verbose: false,
|
|
147
|
-
cfg: {},
|
|
148
|
-
mediaUrl: "/tmp/voice.ogg",
|
|
149
|
-
mediaLocalRoots: undefined,
|
|
150
|
-
accountId: undefined,
|
|
151
|
-
gifPlayback: undefined,
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("skips whitespace-only text payloads", async () => {
|
|
156
|
-
const sendWhatsApp = vi.fn();
|
|
157
|
-
|
|
158
|
-
const result = await whatsappOutbound.sendPayload!({
|
|
159
|
-
cfg: {},
|
|
160
|
-
to: "5511999999999@c.us",
|
|
161
|
-
text: "",
|
|
162
|
-
payload: { text: "\n \t" },
|
|
163
|
-
deps: { sendWhatsApp },
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
expect(result).toEqual({ channel: "whatsapp", messageId: "" });
|
|
167
|
-
expect(sendWhatsApp).not.toHaveBeenCalled();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("suppresses routed error payloads", async () => {
|
|
171
|
-
const sendWhatsApp = vi.fn();
|
|
172
|
-
|
|
173
|
-
const result = await whatsappOutbound.sendPayload!({
|
|
174
|
-
cfg: {},
|
|
175
|
-
to: "5511999999999@c.us",
|
|
176
|
-
text: "",
|
|
177
|
-
payload: { text: "provider exploded", isError: true },
|
|
178
|
-
deps: { sendWhatsApp },
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
expect(result).toEqual({ channel: "whatsapp", messageId: "" });
|
|
182
|
-
expect(sendWhatsApp).not.toHaveBeenCalled();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("sanitizes HTML-only text to whitespace-only payload", () => {
|
|
186
|
-
expect(
|
|
187
|
-
whatsappOutbound
|
|
188
|
-
.sanitizeText?.({
|
|
189
|
-
text: "<br><br>",
|
|
190
|
-
payload: { text: "<br><br>" },
|
|
191
|
-
})
|
|
192
|
-
?.trim(),
|
|
193
|
-
).toBe("");
|
|
194
|
-
});
|
|
195
|
-
});
|