@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.
Files changed (302) hide show
  1. package/dist/access-control-BQ3tCkH8.js +288 -0
  2. package/dist/account-config-CNQ33oav.js +47 -0
  3. package/dist/account-ids-DG18xddX.js +5 -0
  4. package/dist/accounts-BDqgpFEB.js +116 -0
  5. package/dist/action-runtime-DTGNgG-i.js +74 -0
  6. package/dist/action-runtime-api.js +2 -0
  7. package/dist/action-runtime.runtime.js +2 -0
  8. package/dist/active-listener-N7GFKsuN.js +11 -0
  9. package/dist/agent-tools-login-DpRxLNRB.js +89 -0
  10. package/dist/api.js +148 -0
  11. package/dist/audio-preflight.runtime-Bcdsl_ri.js +7 -0
  12. package/dist/auth-presence.js +32 -0
  13. package/dist/auth-store-BjCb50Ry.js +381 -0
  14. package/dist/channel-DQ-5Xoj9.js +542 -0
  15. package/dist/channel-config-api.js +2 -0
  16. package/dist/channel-plugin-api.js +2 -0
  17. package/dist/channel-react-action-Df20qAy9.js +42 -0
  18. package/dist/channel.runtime-DKXRnA8x.js +55 -0
  19. package/dist/channel.setup-DOHR2BGp.js +20 -0
  20. package/dist/command-policy-O8sRnqUV.js +8 -0
  21. package/dist/config-schema-Dmo66Ubb.js +27 -0
  22. package/dist/connection-controller-BiHAsTM8.js +596 -0
  23. package/dist/connection-controller-registry-D_iIGz5m.js +22 -0
  24. package/dist/constants-DLGCIbfl.js +4 -0
  25. package/dist/constants.js +2 -0
  26. package/dist/contract-api.js +19 -0
  27. package/dist/creds-files-_2V8jzQa.js +19 -0
  28. package/dist/directory-config-DVs4ffXu.js +37 -0
  29. package/dist/directory-contract-api.js +2 -0
  30. package/dist/doctor-A9tXm9Om.js +48 -0
  31. package/dist/doctor-contract-api.js +10 -0
  32. package/dist/group-session-contract-B8QBn0I2.js +15 -0
  33. package/dist/identity-QHAlV7Fu.js +75 -0
  34. package/dist/index.js +18 -0
  35. package/dist/legacy-session-surface-api.js +8 -0
  36. package/dist/legacy-state-migrations-api.js +2 -0
  37. package/dist/light-runtime-api.js +5 -0
  38. package/dist/login-DymdrKca.js +52 -0
  39. package/dist/login-qr-DIS0Iv-7.js +378 -0
  40. package/dist/login-qr-api.js +2 -0
  41. package/dist/login-qr-runtime.js +16 -0
  42. package/dist/monitor-C4Fhg_lD.js +3349 -0
  43. package/dist/normalize-CWYiGkXw.js +2 -0
  44. package/dist/normalize-target-BPVHNsM5.js +74 -0
  45. package/dist/outbound-adapter-BssVnZdz.js +29 -0
  46. package/dist/outbound-base-Bq1UYEK5.js +119 -0
  47. package/dist/outbound-media-contract-CGss_QHy.js +163 -0
  48. package/dist/outbound-payload-test-api.js +2 -0
  49. package/dist/quoted-message-TENQzyxV.js +108 -0
  50. package/dist/reaction-level-CRVEVAfi.js +16 -0
  51. package/dist/reply-resolver.runtime-BRCgoO4C.js +2 -0
  52. package/dist/resolve-outbound-target-CN-Wo6IY.js +39 -0
  53. package/dist/rolldown-runtime-RkAeH_Qm.js +27 -0
  54. package/dist/runtime-6fZ67abR.js +8 -0
  55. package/dist/runtime-api.js +15 -0
  56. package/dist/runtime-group-policy-Gp1lQ0at.js +11 -0
  57. package/dist/secret-contract-api.js +5 -0
  58. package/dist/security-contract-DqH8JDX6.js +26 -0
  59. package/dist/security-contract-api.js +2 -0
  60. package/dist/send-CIyLFxdZ.js +234 -0
  61. package/dist/session-contract-B0efQf9P.js +27 -0
  62. package/dist/session-errors-CSuKZPub.js +63 -0
  63. package/dist/session.runtime-C1eSE_KK.js +13 -0
  64. package/dist/setup-core-DZKvpzbH.js +46 -0
  65. package/dist/setup-entry.js +23 -0
  66. package/dist/setup-plugin-api.js +2 -0
  67. package/dist/setup-surface-ieFnIW4g.js +344 -0
  68. package/dist/shared-D_wzSVar.js +307 -0
  69. package/dist/state-migrations-DnCdvoYD.js +39 -0
  70. package/dist/targets.js +2 -0
  71. package/dist/test-api.js +3 -0
  72. package/dist/text-runtime-C1zNiZeO.js +116 -0
  73. package/package.json +14 -6
  74. package/action-runtime-api.ts +0 -1
  75. package/action-runtime.runtime.ts +0 -1
  76. package/api.ts +0 -213
  77. package/auth-presence.ts +0 -80
  78. package/channel-config-api.ts +0 -1
  79. package/channel-plugin-api.ts +0 -3
  80. package/config-api.ts +0 -4
  81. package/constants.ts +0 -1
  82. package/contract-api.ts +0 -29
  83. package/directory-contract-api.ts +0 -4
  84. package/doctor-contract-api.ts +0 -8
  85. package/index.test.ts +0 -13
  86. package/index.ts +0 -16
  87. package/legacy-session-surface-api.ts +0 -6
  88. package/legacy-state-migrations-api.ts +0 -1
  89. package/light-runtime-api.ts +0 -12
  90. package/login-qr-api.ts +0 -1
  91. package/login-qr-runtime.ts +0 -23
  92. package/outbound-payload-test-api.ts +0 -1
  93. package/runtime-api.ts +0 -84
  94. package/secret-contract-api.ts +0 -4
  95. package/security-contract-api.ts +0 -4
  96. package/setup-entry.test.ts +0 -21
  97. package/setup-entry.ts +0 -21
  98. package/setup-plugin-api.ts +0 -3
  99. package/src/account-config.ts +0 -77
  100. package/src/account-ids.ts +0 -13
  101. package/src/account-types.ts +0 -5
  102. package/src/accounts.test.ts +0 -182
  103. package/src/accounts.ts +0 -176
  104. package/src/accounts.whatsapp-auth.test.ts +0 -59
  105. package/src/action-runtime-target-auth.ts +0 -27
  106. package/src/action-runtime.test.ts +0 -330
  107. package/src/action-runtime.ts +0 -76
  108. package/src/active-listener.test.ts +0 -65
  109. package/src/active-listener.ts +0 -17
  110. package/src/agent-tools-login.test.ts +0 -81
  111. package/src/agent-tools-login.ts +0 -113
  112. package/src/approval-auth.test.ts +0 -24
  113. package/src/approval-auth.ts +0 -27
  114. package/src/auth-store.runtime.ts +0 -1
  115. package/src/auth-store.test.ts +0 -311
  116. package/src/auth-store.ts +0 -502
  117. package/src/auto-reply/config.runtime.ts +0 -16
  118. package/src/auto-reply/constants.ts +0 -1
  119. package/src/auto-reply/deliver-reply.test.ts +0 -843
  120. package/src/auto-reply/deliver-reply.ts +0 -279
  121. package/src/auto-reply/loggers.ts +0 -6
  122. package/src/auto-reply/mentions.ts +0 -131
  123. package/src/auto-reply/monitor/ack-reaction.test.ts +0 -170
  124. package/src/auto-reply/monitor/ack-reaction.ts +0 -99
  125. package/src/auto-reply/monitor/audio-preflight.runtime.ts +0 -9
  126. package/src/auto-reply/monitor/broadcast.ts +0 -153
  127. package/src/auto-reply/monitor/commands.ts +0 -19
  128. package/src/auto-reply/monitor/echo.ts +0 -64
  129. package/src/auto-reply/monitor/group-activation.runtime.ts +0 -1
  130. package/src/auto-reply/monitor/group-activation.test.ts +0 -189
  131. package/src/auto-reply/monitor/group-activation.ts +0 -73
  132. package/src/auto-reply/monitor/group-gating.audio-preflight.test.ts +0 -103
  133. package/src/auto-reply/monitor/group-gating.runtime.ts +0 -8
  134. package/src/auto-reply/monitor/group-gating.ts +0 -217
  135. package/src/auto-reply/monitor/group-members.test.ts +0 -56
  136. package/src/auto-reply/monitor/group-members.ts +0 -65
  137. package/src/auto-reply/monitor/inbound-context.test.ts +0 -97
  138. package/src/auto-reply/monitor/inbound-context.ts +0 -92
  139. package/src/auto-reply/monitor/inbound-dispatch.runtime.ts +0 -21
  140. package/src/auto-reply/monitor/inbound-dispatch.test.ts +0 -895
  141. package/src/auto-reply/monitor/inbound-dispatch.ts +0 -422
  142. package/src/auto-reply/monitor/last-route.test.ts +0 -37
  143. package/src/auto-reply/monitor/last-route.ts +0 -61
  144. package/src/auto-reply/monitor/message-line.runtime.ts +0 -38
  145. package/src/auto-reply/monitor/message-line.ts +0 -54
  146. package/src/auto-reply/monitor/on-message.audio-preflight.test.ts +0 -354
  147. package/src/auto-reply/monitor/on-message.ts +0 -291
  148. package/src/auto-reply/monitor/peer.ts +0 -17
  149. package/src/auto-reply/monitor/process-message.audio-preflight.test.ts +0 -420
  150. package/src/auto-reply/monitor/process-message.test.ts +0 -355
  151. package/src/auto-reply/monitor/process-message.ts +0 -531
  152. package/src/auto-reply/monitor/runtime-api.ts +0 -30
  153. package/src/auto-reply/monitor-state.test.ts +0 -64
  154. package/src/auto-reply/monitor-state.ts +0 -106
  155. package/src/auto-reply/monitor.ts +0 -600
  156. package/src/auto-reply/reply-resolver.runtime.ts +0 -1
  157. package/src/auto-reply/types.ts +0 -48
  158. package/src/auto-reply/util.ts +0 -63
  159. package/src/auto-reply/web-auto-reply-monitor.test.ts +0 -687
  160. package/src/auto-reply/web-auto-reply-utils.test.ts +0 -392
  161. package/src/auto-reply.broadcast-groups.combined.test.ts +0 -246
  162. package/src/auto-reply.broadcast-groups.test-harness.ts +0 -47
  163. package/src/auto-reply.impl.ts +0 -6
  164. package/src/auto-reply.test-harness.ts +0 -421
  165. package/src/auto-reply.ts +0 -1
  166. package/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts +0 -427
  167. package/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +0 -908
  168. package/src/auto-reply.web-auto-reply.last-route.test.ts +0 -184
  169. package/src/channel-actions.runtime.ts +0 -7
  170. package/src/channel-actions.test.ts +0 -216
  171. package/src/channel-actions.ts +0 -84
  172. package/src/channel-outbound.test.ts +0 -131
  173. package/src/channel-outbound.ts +0 -36
  174. package/src/channel-react-action.runtime.ts +0 -7
  175. package/src/channel-react-action.test.ts +0 -294
  176. package/src/channel-react-action.ts +0 -84
  177. package/src/channel.runtime.ts +0 -117
  178. package/src/channel.setup.test.ts +0 -461
  179. package/src/channel.setup.ts +0 -28
  180. package/src/channel.ts +0 -339
  181. package/src/command-policy.ts +0 -7
  182. package/src/config-accessors.test.ts +0 -34
  183. package/src/config-accessors.ts +0 -22
  184. package/src/config-schema.test.ts +0 -121
  185. package/src/config-schema.ts +0 -6
  186. package/src/config-ui-hints.ts +0 -24
  187. package/src/connection-controller-registry.test.ts +0 -27
  188. package/src/connection-controller-registry.ts +0 -49
  189. package/src/connection-controller.test.ts +0 -282
  190. package/src/connection-controller.ts +0 -679
  191. package/src/creds-files.ts +0 -19
  192. package/src/creds-persistence.ts +0 -102
  193. package/src/directory-config.test.ts +0 -60
  194. package/src/directory-config.ts +0 -40
  195. package/src/directory-contract.test.ts +0 -56
  196. package/src/doctor-contract.ts +0 -11
  197. package/src/doctor.test.ts +0 -74
  198. package/src/doctor.ts +0 -56
  199. package/src/group-intro.ts +0 -15
  200. package/src/group-policy.test.ts +0 -36
  201. package/src/group-policy.ts +0 -40
  202. package/src/group-session-contract.ts +0 -20
  203. package/src/group-session-key.test.ts +0 -53
  204. package/src/group-session-key.ts +0 -41
  205. package/src/heartbeat.ts +0 -34
  206. package/src/identity.ts +0 -164
  207. package/src/inbound/access-control.test-harness.ts +0 -37
  208. package/src/inbound/access-control.test.ts +0 -375
  209. package/src/inbound/access-control.ts +0 -237
  210. package/src/inbound/dedupe.ts +0 -132
  211. package/src/inbound/extract.test.ts +0 -282
  212. package/src/inbound/extract.ts +0 -488
  213. package/src/inbound/lifecycle.ts +0 -39
  214. package/src/inbound/media.node.test.ts +0 -83
  215. package/src/inbound/media.ts +0 -102
  216. package/src/inbound/monitor.ts +0 -890
  217. package/src/inbound/runtime-api.ts +0 -7
  218. package/src/inbound/save-media.runtime.ts +0 -1
  219. package/src/inbound/send-api.test.ts +0 -319
  220. package/src/inbound/send-api.ts +0 -145
  221. package/src/inbound/send-result.ts +0 -61
  222. package/src/inbound/types.ts +0 -106
  223. package/src/inbound-context.contract.test.ts +0 -32
  224. package/src/inbound-policy.ts +0 -231
  225. package/src/inbound.media.test.ts +0 -320
  226. package/src/inbound.test.ts +0 -290
  227. package/src/inbound.ts +0 -9
  228. package/src/login-qr.test.ts +0 -483
  229. package/src/login-qr.ts +0 -542
  230. package/src/login.coverage.test.ts +0 -133
  231. package/src/login.test.ts +0 -89
  232. package/src/login.ts +0 -70
  233. package/src/logout.test.ts +0 -162
  234. package/src/media.test.ts +0 -480
  235. package/src/media.ts +0 -10
  236. package/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test-support.ts +0 -400
  237. package/src/monitor-inbox.append-upsert.test-support.ts +0 -133
  238. package/src/monitor-inbox.behavior.test.ts +0 -5
  239. package/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test-support.ts +0 -362
  240. package/src/monitor-inbox.captures-media-path-image-messages.test-support.ts +0 -306
  241. package/src/monitor-inbox.streams-inbound-messages.test-support.ts +0 -729
  242. package/src/monitor-inbox.test-harness.ts +0 -304
  243. package/src/normalize-target.ts +0 -123
  244. package/src/normalize.ts +0 -7
  245. package/src/outbound-adapter.poll.test.ts +0 -62
  246. package/src/outbound-adapter.sendpayload.test.ts +0 -195
  247. package/src/outbound-adapter.ts +0 -36
  248. package/src/outbound-base.test.ts +0 -560
  249. package/src/outbound-base.ts +0 -244
  250. package/src/outbound-media-contract.ts +0 -278
  251. package/src/outbound-media.runtime.ts +0 -36
  252. package/src/outbound-payload.contract.test.ts +0 -61
  253. package/src/outbound-send-deps.ts +0 -1
  254. package/src/outbound-test-support.ts +0 -16
  255. package/src/pairing-security.test-harness.ts +0 -59
  256. package/src/qr-image.ts +0 -1
  257. package/src/qr-terminal.ts +0 -1
  258. package/src/quoted-message.test.ts +0 -83
  259. package/src/quoted-message.ts +0 -184
  260. package/src/reaction-level.test.ts +0 -94
  261. package/src/reaction-level.ts +0 -21
  262. package/src/reconnect.test.ts +0 -51
  263. package/src/reconnect.ts +0 -55
  264. package/src/resolve-outbound-target.test.ts +0 -288
  265. package/src/resolve-outbound-target.ts +0 -58
  266. package/src/resolve-target.test.ts +0 -120
  267. package/src/runtime-api.ts +0 -59
  268. package/src/runtime-group-policy.ts +0 -16
  269. package/src/runtime.ts +0 -9
  270. package/src/security-contract.ts +0 -47
  271. package/src/security-fix.ts +0 -71
  272. package/src/send.test.ts +0 -534
  273. package/src/send.ts +0 -292
  274. package/src/session-contract.test.ts +0 -31
  275. package/src/session-contract.ts +0 -43
  276. package/src/session-errors.ts +0 -125
  277. package/src/session-route.test.ts +0 -41
  278. package/src/session-route.ts +0 -32
  279. package/src/session.runtime.ts +0 -8
  280. package/src/session.test.ts +0 -559
  281. package/src/session.ts +0 -330
  282. package/src/setup-core.ts +0 -52
  283. package/src/setup-finalize.ts +0 -446
  284. package/src/setup-surface.test.ts +0 -367
  285. package/src/setup-surface.ts +0 -69
  286. package/src/setup-test-helpers.ts +0 -216
  287. package/src/shared.ts +0 -291
  288. package/src/socket-timing.test.ts +0 -49
  289. package/src/socket-timing.ts +0 -38
  290. package/src/state-migrations.ts +0 -54
  291. package/src/status-issues.test.ts +0 -137
  292. package/src/status-issues.ts +0 -185
  293. package/src/system-prompt.test.ts +0 -199
  294. package/src/system-prompt.ts +0 -31
  295. package/src/targets-runtime.ts +0 -180
  296. package/src/test-helpers.ts +0 -690
  297. package/src/text-runtime.test.ts +0 -162
  298. package/src/text-runtime.ts +0 -11
  299. package/src/vcard.ts +0 -84
  300. package/targets.ts +0 -5
  301. package/test-api.ts +0 -2
  302. package/tsconfig.json +0 -16
@@ -1,362 +0,0 @@
1
- import "./monitor-inbox.test-harness.js";
2
- import { describe, expect, it, vi } from "vitest";
3
- import {
4
- DEFAULT_ACCOUNT_ID,
5
- expectPairingPromptSent,
6
- getAuthDir,
7
- getMonitorWebInbox,
8
- getSock,
9
- installWebMonitorInboxUnitTestHooks,
10
- mockLoadConfig,
11
- settleInboundWork,
12
- } from "./monitor-inbox.test-harness.js";
13
-
14
- const nowSeconds = (offsetMs = 0) => Math.floor((Date.now() + offsetMs) / 1000);
15
- const DEFAULT_MESSAGES_CFG = {
16
- messagePrefix: undefined,
17
- responsePrefix: undefined,
18
- } as const;
19
- const TIMESTAMP_OFF_MESSAGES_CFG = {
20
- ...DEFAULT_MESSAGES_CFG,
21
- timestampPrefix: false,
22
- } as const;
23
-
24
- const createNotifyUpsert = (message: Record<string, unknown>) => ({
25
- type: "notify",
26
- messages: [message],
27
- });
28
-
29
- const createDmMessage = (params: { id: string; remoteJid: string; conversation: string }) => ({
30
- key: {
31
- id: params.id,
32
- fromMe: false,
33
- remoteJid: params.remoteJid,
34
- },
35
- message: { conversation: params.conversation },
36
- messageTimestamp: nowSeconds(),
37
- });
38
-
39
- const createGroupMessage = (params: {
40
- id: string;
41
- remoteJid?: string;
42
- participant: string;
43
- conversation: string;
44
- }) => ({
45
- key: {
46
- id: params.id,
47
- fromMe: false,
48
- remoteJid: params.remoteJid ?? "11111@g.us",
49
- participant: params.participant,
50
- },
51
- message: { conversation: params.conversation },
52
- messageTimestamp: nowSeconds(),
53
- });
54
-
55
- async function startWebInboxMonitor(params: {
56
- config?: Record<string, unknown>;
57
- sendReadReceipts?: boolean;
58
- }) {
59
- const monitorWebInbox = getMonitorWebInbox();
60
- if (params.config) {
61
- mockLoadConfig.mockReturnValue(params.config);
62
- }
63
- const onMessage = vi.fn();
64
- const base = {
65
- cfg: (params.config ?? mockLoadConfig()) as never,
66
- verbose: false,
67
- accountId: DEFAULT_ACCOUNT_ID,
68
- authDir: getAuthDir(),
69
- onMessage,
70
- };
71
- const listener = await monitorWebInbox(
72
- params.sendReadReceipts === undefined
73
- ? base
74
- : {
75
- ...base,
76
- sendReadReceipts: params.sendReadReceipts,
77
- },
78
- );
79
- return { onMessage, listener, sock: getSock() };
80
- }
81
-
82
- describe("web monitor inbox", () => {
83
- installWebMonitorInboxUnitTestHooks();
84
-
85
- it("blocks messages from unauthorized senders not in allowFrom", async () => {
86
- // Test for auto-recovery fix: early allowFrom filtering prevents Bad MAC errors
87
- // from unauthorized senders corrupting sessions
88
- const config = {
89
- channels: {
90
- whatsapp: {
91
- // Only allow +111
92
- allowFrom: ["+111"],
93
- },
94
- },
95
- messages: DEFAULT_MESSAGES_CFG,
96
- };
97
-
98
- const { onMessage, listener, sock } = await startWebInboxMonitor({
99
- config,
100
- });
101
-
102
- // Message from unauthorized sender +999 (not in allowFrom)
103
- sock.ev.emit(
104
- "messages.upsert",
105
- createNotifyUpsert(
106
- createDmMessage({
107
- id: "unauth1",
108
- remoteJid: "999@s.whatsapp.net",
109
- conversation: "unauthorized message",
110
- }),
111
- ),
112
- );
113
- await settleInboundWork();
114
-
115
- // Should NOT call onMessage for unauthorized senders
116
- expect(onMessage).not.toHaveBeenCalled();
117
- // Should NOT send read receipts for blocked senders (privacy + avoids Baileys Bad MAC churn).
118
- expect(sock.readMessages).not.toHaveBeenCalled();
119
- expectPairingPromptSent(sock, "999@s.whatsapp.net", "+999");
120
-
121
- await listener.close();
122
- });
123
-
124
- it("skips read receipts in self-chat mode", async () => {
125
- const config = {
126
- channels: {
127
- whatsapp: {
128
- // Self-chat heuristic: allowFrom includes selfE164 (+123).
129
- allowFrom: ["+123"],
130
- },
131
- },
132
- messages: DEFAULT_MESSAGES_CFG,
133
- };
134
-
135
- const { onMessage, listener, sock } = await startWebInboxMonitor({
136
- config,
137
- });
138
-
139
- sock.ev.emit(
140
- "messages.upsert",
141
- createNotifyUpsert(
142
- createDmMessage({
143
- id: "self1",
144
- remoteJid: "123@s.whatsapp.net",
145
- conversation: "self ping",
146
- }),
147
- ),
148
- );
149
- await settleInboundWork();
150
-
151
- expect(onMessage).toHaveBeenCalledTimes(1);
152
- expect(onMessage).toHaveBeenCalledWith(
153
- expect.objectContaining({
154
- from: "+123",
155
- to: "+123",
156
- body: "self ping",
157
- accessControlPassed: true,
158
- }),
159
- );
160
- expect(sock.readMessages).not.toHaveBeenCalled();
161
-
162
- await listener.close();
163
- });
164
-
165
- it("skips read receipts when disabled", async () => {
166
- const { onMessage, listener, sock } = await startWebInboxMonitor({
167
- sendReadReceipts: false,
168
- });
169
- sock.ev.emit(
170
- "messages.upsert",
171
- createNotifyUpsert(
172
- createDmMessage({
173
- id: "rr-off-1",
174
- remoteJid: "222@s.whatsapp.net",
175
- conversation: "read receipts off",
176
- }),
177
- ),
178
- );
179
- await settleInboundWork();
180
-
181
- expect(onMessage).toHaveBeenCalledTimes(1);
182
- expect(sock.readMessages).not.toHaveBeenCalled();
183
-
184
- await listener.close();
185
- });
186
-
187
- it("lets group messages through even when sender not in allowFrom", async () => {
188
- const { onMessage, listener, sock } = await startWebInboxMonitor({
189
- config: {
190
- channels: { whatsapp: { allowFrom: ["+1234"], groupPolicy: "open" } },
191
- messages: DEFAULT_MESSAGES_CFG,
192
- },
193
- });
194
- sock.ev.emit(
195
- "messages.upsert",
196
- createNotifyUpsert(
197
- createGroupMessage({
198
- id: "grp3",
199
- participant: "999@s.whatsapp.net",
200
- conversation: "unauthorized group message",
201
- }),
202
- ),
203
- );
204
- await settleInboundWork();
205
-
206
- expect(onMessage).toHaveBeenCalledTimes(1);
207
- const payload = onMessage.mock.calls[0][0];
208
- expect(payload.chatType).toBe("group");
209
- expect(payload.senderE164).toBe("+999");
210
-
211
- await listener.close();
212
- });
213
-
214
- it("blocks all group messages when groupPolicy is 'disabled'", async () => {
215
- const { onMessage, listener, sock } = await startWebInboxMonitor({
216
- config: {
217
- channels: { whatsapp: { allowFrom: ["+1234"], groupPolicy: "disabled" } },
218
- messages: TIMESTAMP_OFF_MESSAGES_CFG,
219
- },
220
- });
221
- sock.ev.emit(
222
- "messages.upsert",
223
- createNotifyUpsert(
224
- createGroupMessage({
225
- id: "grp-disabled",
226
- participant: "999@s.whatsapp.net",
227
- conversation: "group message should be blocked",
228
- }),
229
- ),
230
- );
231
- await settleInboundWork();
232
-
233
- // Should NOT call onMessage because groupPolicy is disabled
234
- expect(onMessage).not.toHaveBeenCalled();
235
-
236
- await listener.close();
237
- });
238
-
239
- it("blocks group messages from senders not in groupAllowFrom when groupPolicy is 'allowlist'", async () => {
240
- const { onMessage, listener, sock } = await startWebInboxMonitor({
241
- config: {
242
- channels: {
243
- whatsapp: {
244
- groupAllowFrom: ["+1234"], // Does not include +999
245
- groupPolicy: "allowlist",
246
- },
247
- },
248
- messages: TIMESTAMP_OFF_MESSAGES_CFG,
249
- },
250
- });
251
- sock.ev.emit(
252
- "messages.upsert",
253
- createNotifyUpsert(
254
- createGroupMessage({
255
- id: "grp-allowlist-blocked",
256
- participant: "999@s.whatsapp.net",
257
- conversation: "unauthorized group sender",
258
- }),
259
- ),
260
- );
261
- await settleInboundWork();
262
-
263
- // Should NOT call onMessage because sender +999 not in groupAllowFrom
264
- expect(onMessage).not.toHaveBeenCalled();
265
-
266
- await listener.close();
267
- });
268
-
269
- it("allows group messages from senders in groupAllowFrom when groupPolicy is 'allowlist'", async () => {
270
- const { onMessage, listener, sock } = await startWebInboxMonitor({
271
- config: {
272
- channels: {
273
- whatsapp: {
274
- groupAllowFrom: ["+15551234567"], // Includes the sender
275
- groupPolicy: "allowlist",
276
- },
277
- },
278
- messages: TIMESTAMP_OFF_MESSAGES_CFG,
279
- },
280
- });
281
- sock.ev.emit(
282
- "messages.upsert",
283
- createNotifyUpsert(
284
- createGroupMessage({
285
- id: "grp-allowlist-allowed",
286
- participant: "15551234567@s.whatsapp.net",
287
- conversation: "authorized group sender",
288
- }),
289
- ),
290
- );
291
- await settleInboundWork();
292
-
293
- // Should call onMessage because sender is in groupAllowFrom
294
- expect(onMessage).toHaveBeenCalledTimes(1);
295
- const payload = onMessage.mock.calls[0][0];
296
- expect(payload.chatType).toBe("group");
297
- expect(payload.senderE164).toBe("+15551234567");
298
-
299
- await listener.close();
300
- });
301
-
302
- it("allows all group senders with wildcard in groupPolicy allowlist", async () => {
303
- const { onMessage, listener, sock } = await startWebInboxMonitor({
304
- config: {
305
- channels: {
306
- whatsapp: {
307
- groupAllowFrom: ["*"], // Wildcard allows everyone
308
- groupPolicy: "allowlist",
309
- },
310
- },
311
- messages: TIMESTAMP_OFF_MESSAGES_CFG,
312
- },
313
- });
314
- sock.ev.emit(
315
- "messages.upsert",
316
- createNotifyUpsert(
317
- createGroupMessage({
318
- id: "grp-wildcard-test",
319
- remoteJid: "22222@g.us",
320
- participant: "9999999999@s.whatsapp.net",
321
- conversation: "wildcard group sender",
322
- }),
323
- ),
324
- );
325
- await settleInboundWork();
326
-
327
- // Should call onMessage because wildcard allows all senders
328
- expect(onMessage).toHaveBeenCalledTimes(1);
329
- const payload = onMessage.mock.calls[0][0];
330
- expect(payload.chatType).toBe("group");
331
-
332
- await listener.close();
333
- });
334
-
335
- it("blocks group messages when groupPolicy allowlist has no groupAllowFrom", async () => {
336
- const { onMessage, listener, sock } = await startWebInboxMonitor({
337
- config: {
338
- channels: {
339
- whatsapp: {
340
- groupPolicy: "allowlist",
341
- },
342
- },
343
- messages: TIMESTAMP_OFF_MESSAGES_CFG,
344
- },
345
- });
346
- sock.ev.emit(
347
- "messages.upsert",
348
- createNotifyUpsert(
349
- createGroupMessage({
350
- id: "grp-allowlist-empty",
351
- participant: "999@s.whatsapp.net",
352
- conversation: "blocked by empty allowlist",
353
- }),
354
- ),
355
- );
356
- await settleInboundWork();
357
-
358
- expect(onMessage).not.toHaveBeenCalled();
359
-
360
- await listener.close();
361
- });
362
- });
@@ -1,306 +0,0 @@
1
- import "./monitor-inbox.test-harness.js";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import {
4
- DEFAULT_ACCOUNT_ID,
5
- getAuthDir,
6
- getMonitorWebInbox,
7
- getSock,
8
- installWebMonitorInboxUnitTestHooks,
9
- mockLoadConfig,
10
- } from "./monitor-inbox.test-harness.js";
11
- let monitorWebInbox: typeof import("./inbound.js").monitorWebInbox;
12
- const inboundLoggerInfoMock = vi.hoisted(() => vi.fn());
13
-
14
- vi.mock("openclaw/plugin-sdk/text-runtime", async () => {
15
- const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/text-runtime")>(
16
- "openclaw/plugin-sdk/text-runtime",
17
- );
18
- return {
19
- ...actual,
20
- getChildLogger: () => ({
21
- info: inboundLoggerInfoMock,
22
- warn: vi.fn(),
23
- error: vi.fn(),
24
- debug: vi.fn(),
25
- }),
26
- };
27
- });
28
-
29
- describe("web monitor inbox", () => {
30
- installWebMonitorInboxUnitTestHooks();
31
-
32
- beforeEach(() => {
33
- inboundLoggerInfoMock.mockReset();
34
- monitorWebInbox = getMonitorWebInbox();
35
- });
36
-
37
- async function openMonitor(onMessage = vi.fn()) {
38
- return await monitorWebInbox({
39
- cfg: mockLoadConfig() as never,
40
- verbose: false,
41
- accountId: DEFAULT_ACCOUNT_ID,
42
- authDir: getAuthDir(),
43
- onMessage,
44
- });
45
- }
46
-
47
- async function runSingleUpsertAndCapture(upsert: unknown) {
48
- const onMessage = vi.fn();
49
- const listener = await openMonitor(onMessage);
50
- const sock = getSock();
51
- sock.ev.emit("messages.upsert", upsert);
52
- await new Promise((resolve) => setTimeout(resolve, 25));
53
- return { onMessage, listener, sock };
54
- }
55
-
56
- function expectSingleGroupMessage(
57
- onMessage: ReturnType<typeof vi.fn>,
58
- expected: Record<string, unknown>,
59
- ) {
60
- expect(onMessage).toHaveBeenCalledTimes(1);
61
- expect(onMessage).toHaveBeenCalledWith(expect.objectContaining(expected));
62
- }
63
-
64
- it("captures media path for image messages", async () => {
65
- const { onMessage, listener, sock } = await runSingleUpsertAndCapture({
66
- type: "notify",
67
- messages: [
68
- {
69
- key: { id: "med1", fromMe: false, remoteJid: "888@s.whatsapp.net" },
70
- message: { imageMessage: { mimetype: "image/jpeg" } },
71
- messageTimestamp: 1_700_000_100,
72
- },
73
- ],
74
- });
75
-
76
- expect(onMessage).toHaveBeenCalledWith(
77
- expect.objectContaining({
78
- body: "<media:image>",
79
- }),
80
- );
81
- expect(sock.readMessages).toHaveBeenCalledWith([
82
- {
83
- remoteJid: "888@s.whatsapp.net",
84
- id: "med1",
85
- participant: undefined,
86
- fromMe: false,
87
- },
88
- ]);
89
- expect(sock.sendPresenceUpdate).toHaveBeenNthCalledWith(1, "available");
90
- await listener.close();
91
- });
92
-
93
- it("sets gifPlayback on outbound video payloads when requested", async () => {
94
- const onMessage = vi.fn();
95
- const listener = await openMonitor(onMessage);
96
- const sock = getSock();
97
- const buf = Buffer.from("gifvid");
98
-
99
- await listener.sendMessage("+1555", "gif", buf, "video/mp4", {
100
- gifPlayback: true,
101
- });
102
-
103
- expect(sock.sendMessage).toHaveBeenCalledWith("1555@s.whatsapp.net", {
104
- video: buf,
105
- caption: "gif",
106
- mimetype: "video/mp4",
107
- gifPlayback: true,
108
- });
109
-
110
- await listener.close();
111
- });
112
-
113
- it("resolves onClose when the socket closes", async () => {
114
- const listener = await openMonitor(vi.fn());
115
- const sock = getSock();
116
- const reasonPromise = listener.onClose;
117
- sock.ev.emit("connection.update", {
118
- connection: "close",
119
- lastDisconnect: { error: { output: { statusCode: 500 } } },
120
- });
121
- await expect(reasonPromise).resolves.toEqual(
122
- expect.objectContaining({ status: 500, isLoggedOut: false }),
123
- );
124
- await listener.close();
125
- });
126
-
127
- it("detaches inbound listeners and ends the socket on close()", async () => {
128
- const listener = await openMonitor(vi.fn());
129
- const sock = getSock();
130
-
131
- expect(sock.ev.listenerCount("messages.upsert")).toBeGreaterThan(0);
132
- expect(sock.ev.listenerCount("connection.update")).toBeGreaterThan(0);
133
-
134
- await listener.close();
135
-
136
- expect(sock.ev.listenerCount("messages.upsert")).toBe(0);
137
- expect(sock.ev.listenerCount("connection.update")).toBe(0);
138
- expect(sock.end).toHaveBeenCalledTimes(1);
139
- expect(sock.end).toHaveBeenCalledWith(expect.any(Error));
140
- expect(sock.ws.close).not.toHaveBeenCalled();
141
- });
142
-
143
- it("logs inbound bodies through the inbound child logger", async () => {
144
- const { listener } = await runSingleUpsertAndCapture({
145
- type: "notify",
146
- messages: [
147
- {
148
- key: { id: "abc", fromMe: false, remoteJid: "999@s.whatsapp.net" },
149
- message: { conversation: "ping" },
150
- messageTimestamp: 1_700_000_000,
151
- pushName: "Tester",
152
- },
153
- ],
154
- });
155
-
156
- expect(inboundLoggerInfoMock).toHaveBeenCalledWith(
157
- expect.objectContaining({
158
- body: "ping",
159
- from: "+999",
160
- }),
161
- "inbound message",
162
- );
163
- await listener.close();
164
- });
165
-
166
- it("includes participant when marking group messages read", async () => {
167
- const { listener, sock } = await runSingleUpsertAndCapture({
168
- type: "notify",
169
- messages: [
170
- {
171
- key: {
172
- id: "grp1",
173
- fromMe: false,
174
- remoteJid: "12345-67890@g.us",
175
- participant: "111@s.whatsapp.net",
176
- },
177
- message: { conversation: "group ping" },
178
- },
179
- ],
180
- });
181
-
182
- expect(sock.readMessages).toHaveBeenCalledWith([
183
- {
184
- remoteJid: "12345-67890@g.us",
185
- id: "grp1",
186
- participant: "111@s.whatsapp.net",
187
- fromMe: false,
188
- },
189
- ]);
190
- await listener.close();
191
- });
192
-
193
- it("passes through group messages with participant metadata", async () => {
194
- const { onMessage, listener } = await runSingleUpsertAndCapture({
195
- type: "notify",
196
- messages: [
197
- {
198
- key: {
199
- id: "grp2",
200
- fromMe: false,
201
- remoteJid: "99999@g.us",
202
- participant: "777@s.whatsapp.net",
203
- },
204
- pushName: "Alice",
205
- message: {
206
- extendedTextMessage: {
207
- text: "@bot ping",
208
- contextInfo: { mentionedJid: ["123@s.whatsapp.net"] },
209
- },
210
- },
211
- messageTimestamp: 1_700_000_000,
212
- },
213
- ],
214
- });
215
-
216
- expect(onMessage).toHaveBeenCalledWith(
217
- expect.objectContaining({
218
- chatType: "group",
219
- conversationId: "99999@g.us",
220
- senderE164: "+777",
221
- mentionedJids: ["123@s.whatsapp.net"],
222
- }),
223
- );
224
- await listener.close();
225
- });
226
-
227
- it("unwraps ephemeral messages, preserves mentions, and still delivers group pings", async () => {
228
- const { onMessage, listener } = await runSingleUpsertAndCapture({
229
- type: "notify",
230
- messages: [
231
- {
232
- key: {
233
- id: "grp-ephem",
234
- fromMe: false,
235
- remoteJid: "424242@g.us",
236
- participant: "888@s.whatsapp.net",
237
- },
238
- message: {
239
- ephemeralMessage: {
240
- message: {
241
- extendedTextMessage: {
242
- text: "oh hey @Clawd UK !",
243
- contextInfo: { mentionedJid: ["123@s.whatsapp.net"] },
244
- },
245
- },
246
- },
247
- },
248
- },
249
- ],
250
- });
251
- expectSingleGroupMessage(onMessage, {
252
- chatType: "group",
253
- conversationId: "424242@g.us",
254
- body: "oh hey @Clawd UK !",
255
- mentionedJids: ["123@s.whatsapp.net"],
256
- senderE164: "+888",
257
- });
258
- await listener.close();
259
- });
260
-
261
- it("still forwards group messages (with sender info) even when allowFrom is restrictive", async () => {
262
- mockLoadConfig.mockReturnValue({
263
- channels: {
264
- whatsapp: {
265
- // does not include +777
266
- allowFrom: ["+111"],
267
- groupPolicy: "open",
268
- },
269
- },
270
- messages: {
271
- messagePrefix: undefined,
272
- responsePrefix: undefined,
273
- },
274
- });
275
-
276
- const { onMessage, listener } = await runSingleUpsertAndCapture({
277
- type: "notify",
278
- messages: [
279
- {
280
- key: {
281
- id: "grp-allow",
282
- fromMe: false,
283
- remoteJid: "55555@g.us",
284
- participant: "777@s.whatsapp.net",
285
- },
286
- message: {
287
- extendedTextMessage: {
288
- text: "@bot hi",
289
- contextInfo: { mentionedJid: ["123@s.whatsapp.net"] },
290
- },
291
- },
292
- },
293
- ],
294
- });
295
- expectSingleGroupMessage(onMessage, {
296
- chatType: "group",
297
- from: "55555@g.us",
298
- senderE164: "+777",
299
- senderJid: "777@s.whatsapp.net",
300
- mentionedJids: ["123@s.whatsapp.net"],
301
- selfE164: "+123",
302
- selfJid: "123@s.whatsapp.net",
303
- });
304
- await listener.close();
305
- });
306
- });