@openclaw/whatsapp 2026.5.7 → 2026.5.9-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.
Files changed (37) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{action-runtime-BqYZmBX_.js → action-runtime-flTHnBac.js} +1 -1
  3. package/dist/action-runtime.runtime.js +1 -1
  4. package/dist/api.js +115 -7
  5. package/dist/{auth-store-BNZmNP-s.js → auth-store-CG0eSZ0D.js} +17 -39
  6. package/dist/{channel-CTr3YjO8.js → channel-Dnv-QeNE.js} +34 -7
  7. package/dist/channel-plugin-api.js +1 -1
  8. package/dist/{channel-react-action-BuSTzmDX.js → channel-react-action-LOuxBqX6.js} +1 -1
  9. package/dist/{channel.runtime-C3i9Svfj.js → channel.runtime-CCZpOzbU.js} +5 -5
  10. package/dist/{channel.setup-BYtkDwHe.js → channel.setup-CV0OjAwl.js} +3 -3
  11. package/dist/{connection-controller-BCIuChXD.js → connection-controller-d8D2iUBj.js} +6 -197
  12. package/dist/contract-api.js +1 -1
  13. package/dist/legacy-state-migrations-api.js +1 -1
  14. package/dist/light-runtime-api.js +2 -2
  15. package/dist/{login-DSUTCcJ7.js → login-B_u009k0.js} +3 -2
  16. package/dist/{login-qr-dfs8tfGo.js → login-qr-C0b7I7hu.js} +3 -2
  17. package/dist/login-qr-runtime.js +1 -1
  18. package/dist/{monitor-C5_C_RGJ.js → monitor-5u0dVXnB.js} +93 -683
  19. package/dist/{outbound-adapter-CbR17Ehj.js → outbound-adapter-BoHBD2S7.js} +3 -3
  20. package/dist/{outbound-base-BmzhutvF.js → outbound-base-CCx7atwc.js} +6 -1
  21. package/dist/{outbound-media-contract-BPdOq7Hb.js → outbound-media-contract-Dzi1ILTd.js} +36 -40
  22. package/dist/outbound-payload-test-api.js +1 -1
  23. package/dist/runtime-api.js +8 -7
  24. package/dist/{send-CxctcFGT.js → send-CLBN7TZv.js} +1 -1
  25. package/dist/send-api-DoycpOvC.js +697 -0
  26. package/dist/session-BXC3R43P.js +198 -0
  27. package/dist/setup-plugin-api.js +1 -1
  28. package/dist/{setup-surface-CF3l3gEZ.js → setup-surface-RbOpXjSR.js} +2 -2
  29. package/dist/{shared-CTSTwprn.js → shared-h4cwNeYT.js} +2 -2
  30. package/dist/{state-migrations-DnCdvoYD.js → state-migrations-Bm0S67yH.js} +2 -1
  31. package/dist/test-api.js +1 -1
  32. package/package.json +5 -5
  33. package/dist/{access-control-CTj7P7WP.js → access-control-hFVqAWSd.js} +1 -1
  34. /package/dist/{active-listener-N7GFKsuN.js → active-listener-DTbKS-j-.js} +0 -0
  35. /package/dist/{audio-preflight.runtime-Bcdsl_ri.js → audio-preflight.runtime-MmkB-pgX.js} +0 -0
  36. /package/dist/{reply-resolver.runtime-BRCgoO4C.js → reply-resolver.runtime-rIVWohXr.js} +0 -0
  37. /package/dist/{session.runtime-C1eSE_KK.js → session.runtime-CVhtXjTk.js} +0 -0
@@ -0,0 +1,697 @@
1
+ import { n as isWhatsAppNewsletterJid } from "./normalize-target-nXxC_hxG.js";
2
+ import "./normalize-Y3eBdM8a.js";
3
+ import { c as toWhatsappJidWithLid, i as jidToE164, s as toWhatsappJid } from "./text-runtime-DFFwk5z6.js";
4
+ import { t as buildQuotedMessageOptions } from "./quoted-message-BXs55Teh.js";
5
+ import { c as resolveComparableIdentity } from "./identity-DhaC0MoD.js";
6
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
7
+ import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
8
+ import { createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-message";
9
+ import { extractMessageContent, getContentType, normalizeMessageContent } from "@whiskeysockets/baileys";
10
+ import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
11
+ import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
12
+ //#region extensions/whatsapp/src/vcard.ts
13
+ const ALLOWED_VCARD_KEYS = new Set([
14
+ "FN",
15
+ "N",
16
+ "TEL"
17
+ ]);
18
+ function parseVcard(vcard) {
19
+ if (!vcard) return { phones: [] };
20
+ const lines = vcard.split(/\r?\n/);
21
+ let nameFromN;
22
+ let nameFromFn;
23
+ const phones = [];
24
+ for (const rawLine of lines) {
25
+ const line = rawLine.trim();
26
+ if (!line) continue;
27
+ const colonIndex = line.indexOf(":");
28
+ if (colonIndex === -1) continue;
29
+ const key = line.slice(0, colonIndex).toUpperCase();
30
+ const rawValue = line.slice(colonIndex + 1).trim();
31
+ if (!rawValue) continue;
32
+ const baseKey = normalizeVcardKey(key);
33
+ if (!baseKey || !ALLOWED_VCARD_KEYS.has(baseKey)) continue;
34
+ const value = cleanVcardValue(rawValue);
35
+ if (!value) continue;
36
+ if (baseKey === "FN" && !nameFromFn) {
37
+ nameFromFn = normalizeVcardName(value);
38
+ continue;
39
+ }
40
+ if (baseKey === "N" && !nameFromN) {
41
+ nameFromN = normalizeVcardName(value);
42
+ continue;
43
+ }
44
+ if (baseKey === "TEL") {
45
+ const phone = normalizeVcardPhone(value);
46
+ if (phone) phones.push(phone);
47
+ }
48
+ }
49
+ return {
50
+ name: nameFromFn ?? nameFromN,
51
+ phones
52
+ };
53
+ }
54
+ function normalizeVcardKey(key) {
55
+ const [primary] = key.split(";");
56
+ if (!primary) return;
57
+ const segments = primary.split(".");
58
+ return segments[segments.length - 1] || void 0;
59
+ }
60
+ function cleanVcardValue(value) {
61
+ return value.replace(/\\n/gi, " ").replace(/\\,/g, ",").replace(/\\;/g, ";").trim();
62
+ }
63
+ function normalizeVcardName(value) {
64
+ return value.replace(/;/g, " ").replace(/\s+/g, " ").trim();
65
+ }
66
+ function normalizeVcardPhone(value) {
67
+ const trimmed = value.trim();
68
+ if (!trimmed) return "";
69
+ if (normalizeLowercaseStringOrEmpty(trimmed).startsWith("tel:")) return trimmed.slice(4).trim();
70
+ return trimmed;
71
+ }
72
+ //#endregion
73
+ //#region extensions/whatsapp/src/inbound/extract.ts
74
+ const MESSAGE_WRAPPER_KEYS = [
75
+ "botInvokeMessage",
76
+ "ephemeralMessage",
77
+ "viewOnceMessage",
78
+ "viewOnceMessageV2",
79
+ "viewOnceMessageV2Extension",
80
+ "documentWithCaptionMessage",
81
+ "groupMentionedMessage"
82
+ ];
83
+ const MESSAGE_CONTENT_KEYS = [
84
+ "conversation",
85
+ "extendedTextMessage",
86
+ "imageMessage",
87
+ "videoMessage",
88
+ "audioMessage",
89
+ "documentMessage",
90
+ "stickerMessage",
91
+ "locationMessage",
92
+ "liveLocationMessage",
93
+ "contactMessage",
94
+ "contactsArrayMessage",
95
+ "buttonsResponseMessage",
96
+ "listResponseMessage",
97
+ "templateButtonReplyMessage",
98
+ "interactiveResponseMessage",
99
+ "buttonsMessage",
100
+ "listMessage"
101
+ ];
102
+ function fallbackNormalizeMessageContent(message) {
103
+ let current = message;
104
+ while (current && typeof current === "object") {
105
+ let unwrapped = false;
106
+ for (const key of MESSAGE_WRAPPER_KEYS) {
107
+ const candidate = current[key];
108
+ if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message) {
109
+ current = candidate.message;
110
+ unwrapped = true;
111
+ break;
112
+ }
113
+ }
114
+ if (!unwrapped) break;
115
+ }
116
+ return current;
117
+ }
118
+ function normalizeMessage(message) {
119
+ if (typeof normalizeMessageContent === "function") return normalizeMessageContent(message);
120
+ return fallbackNormalizeMessageContent(message);
121
+ }
122
+ function fallbackGetContentType(message) {
123
+ const normalized = fallbackNormalizeMessageContent(message);
124
+ if (!normalized || typeof normalized !== "object") return;
125
+ for (const key of MESSAGE_CONTENT_KEYS) if (normalized[key] != null) return key;
126
+ }
127
+ function getMessageContentType(message) {
128
+ if (typeof getContentType === "function") return getContentType(message);
129
+ return fallbackGetContentType(message);
130
+ }
131
+ function extractMessage(message) {
132
+ if (typeof extractMessageContent === "function") return extractMessageContent(message);
133
+ const normalized = fallbackNormalizeMessageContent(message);
134
+ const contentType = fallbackGetContentType(normalized);
135
+ if (!normalized || !contentType || contentType === "conversation") return normalized;
136
+ const candidate = normalized[contentType];
137
+ return candidate && typeof candidate === "object" ? candidate : normalized;
138
+ }
139
+ function getFutureProofInnerMessage(message) {
140
+ const contentType = getMessageContentType(message);
141
+ const candidate = contentType ? message[contentType] : void 0;
142
+ if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message && typeof candidate.message === "object") {
143
+ const inner = normalizeMessage(candidate.message);
144
+ if (inner) {
145
+ const innerType = getMessageContentType(inner);
146
+ if (innerType && innerType !== contentType) return inner;
147
+ }
148
+ }
149
+ }
150
+ function buildMessageChain(message) {
151
+ const chain = [];
152
+ let current = normalizeMessage(message);
153
+ while (current && chain.length < 4) {
154
+ chain.push(current);
155
+ current = getFutureProofInnerMessage(current);
156
+ }
157
+ return chain;
158
+ }
159
+ function unwrapMessage(message) {
160
+ return buildMessageChain(message).at(-1);
161
+ }
162
+ function extractContextInfoFromMessage(message) {
163
+ const contentType = getMessageContentType(message);
164
+ const candidate = contentType ? message[contentType] : void 0;
165
+ const contextInfo = candidate && typeof candidate === "object" && "contextInfo" in candidate ? candidate.contextInfo : void 0;
166
+ if (contextInfo) return contextInfo;
167
+ const fallback = message.extendedTextMessage?.contextInfo ?? message.imageMessage?.contextInfo ?? message.videoMessage?.contextInfo ?? message.documentMessage?.contextInfo ?? message.audioMessage?.contextInfo ?? message.stickerMessage?.contextInfo ?? message.buttonsResponseMessage?.contextInfo ?? message.listResponseMessage?.contextInfo ?? message.templateButtonReplyMessage?.contextInfo ?? message.interactiveResponseMessage?.contextInfo ?? message.buttonsMessage?.contextInfo ?? message.listMessage?.contextInfo;
168
+ if (fallback) return fallback;
169
+ for (const value of Object.values(message)) {
170
+ if (!value || typeof value !== "object") continue;
171
+ if ("contextInfo" in value) {
172
+ const candidateContext = value.contextInfo;
173
+ if (candidateContext) return candidateContext;
174
+ }
175
+ if ("message" in value) {
176
+ const inner = value.message;
177
+ if (inner) {
178
+ const innerCtx = extractContextInfo(inner);
179
+ if (innerCtx) return innerCtx;
180
+ }
181
+ }
182
+ }
183
+ }
184
+ function extractContextInfo(message) {
185
+ for (const candidate of buildMessageChain(message)) {
186
+ const contextInfo = extractContextInfoFromMessage(candidate);
187
+ if (contextInfo) return contextInfo;
188
+ }
189
+ }
190
+ function extractMentionedJids(rawMessage) {
191
+ const message = unwrapMessage(rawMessage);
192
+ if (!message) return;
193
+ const flattened = [
194
+ message.extendedTextMessage?.contextInfo?.mentionedJid,
195
+ message.imageMessage?.contextInfo?.mentionedJid,
196
+ message.videoMessage?.contextInfo?.mentionedJid,
197
+ message.documentMessage?.contextInfo?.mentionedJid,
198
+ message.audioMessage?.contextInfo?.mentionedJid,
199
+ message.stickerMessage?.contextInfo?.mentionedJid,
200
+ message.buttonsResponseMessage?.contextInfo?.mentionedJid,
201
+ message.listResponseMessage?.contextInfo?.mentionedJid
202
+ ].flatMap((arr) => arr ?? []).filter(Boolean);
203
+ if (flattened.length === 0) return;
204
+ return Array.from(new Set(flattened));
205
+ }
206
+ function extractText(rawMessage) {
207
+ const message = unwrapMessage(rawMessage);
208
+ if (!message) return;
209
+ const extracted = extractMessage(message);
210
+ const candidates = [message, extracted && extracted !== message ? extracted : void 0];
211
+ for (const candidate of candidates) {
212
+ if (!candidate) continue;
213
+ if (typeof candidate.conversation === "string" && candidate.conversation.trim()) return candidate.conversation.trim();
214
+ const extended = candidate.extendedTextMessage?.text;
215
+ if (extended?.trim()) return extended.trim();
216
+ const caption = candidate.imageMessage?.caption ?? candidate.videoMessage?.caption ?? candidate.documentMessage?.caption;
217
+ if (caption?.trim()) return caption.trim();
218
+ }
219
+ const contactPlaceholder = extractContactPlaceholder(message) ?? (extracted && extracted !== message ? extractContactPlaceholder(extracted) : void 0);
220
+ if (contactPlaceholder) return contactPlaceholder;
221
+ }
222
+ function extractMediaPlaceholder(rawMessage) {
223
+ const message = unwrapMessage(rawMessage);
224
+ if (!message) return;
225
+ if (message.imageMessage) return "<media:image>";
226
+ if (message.videoMessage) return "<media:video>";
227
+ if (message.audioMessage) return "<media:audio>";
228
+ if (message.documentMessage) return "<media:document>";
229
+ if (message.stickerMessage) return "<media:sticker>";
230
+ }
231
+ function extractContactPlaceholder(rawMessage) {
232
+ const contactContext = extractContactContext(rawMessage);
233
+ if (!contactContext) return;
234
+ if (contactContext.kind === "contact") return "<contact>";
235
+ const suffix = contactContext.total === 1 ? "contact" : "contacts";
236
+ return `<contacts: ${contactContext.total} ${suffix}>`;
237
+ }
238
+ function extractContactContext(rawMessage) {
239
+ const message = unwrapMessage(rawMessage);
240
+ if (!message) return;
241
+ const contact = message.contactMessage ?? void 0;
242
+ if (contact) {
243
+ const { name, phones } = describeContact({
244
+ displayName: contact.displayName,
245
+ vcard: contact.vcard
246
+ });
247
+ return {
248
+ kind: "contact",
249
+ total: 1,
250
+ contacts: [{
251
+ name,
252
+ phones
253
+ }]
254
+ };
255
+ }
256
+ const contactsArray = message.contactsArrayMessage?.contacts ?? void 0;
257
+ if (!contactsArray || contactsArray.length === 0) return;
258
+ return {
259
+ kind: "contacts",
260
+ total: contactsArray.length,
261
+ contacts: contactsArray.map((entry) => describeContact({
262
+ displayName: entry.displayName,
263
+ vcard: entry.vcard
264
+ }))
265
+ };
266
+ }
267
+ function describeContact(input) {
268
+ const displayName = (input.displayName ?? "").trim();
269
+ const parsed = parseVcard(input.vcard ?? void 0);
270
+ return {
271
+ name: displayName || parsed.name,
272
+ phones: parsed.phones
273
+ };
274
+ }
275
+ function extractLocationData(rawMessage) {
276
+ const message = unwrapMessage(rawMessage);
277
+ if (!message) return null;
278
+ const live = message.liveLocationMessage ?? void 0;
279
+ if (live) {
280
+ const latitudeRaw = live.degreesLatitude;
281
+ const longitudeRaw = live.degreesLongitude;
282
+ if (latitudeRaw != null && longitudeRaw != null) {
283
+ const latitude = latitudeRaw;
284
+ const longitude = longitudeRaw;
285
+ if (Number.isFinite(latitude) && Number.isFinite(longitude)) return {
286
+ latitude,
287
+ longitude,
288
+ accuracy: live.accuracyInMeters ?? void 0,
289
+ caption: live.caption ?? void 0,
290
+ source: "live",
291
+ isLive: true
292
+ };
293
+ }
294
+ }
295
+ const location = message.locationMessage ?? void 0;
296
+ if (location) {
297
+ const latitudeRaw = location.degreesLatitude;
298
+ const longitudeRaw = location.degreesLongitude;
299
+ if (latitudeRaw != null && longitudeRaw != null) {
300
+ const latitude = latitudeRaw;
301
+ const longitude = longitudeRaw;
302
+ if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
303
+ const isLive = Boolean(location.isLive);
304
+ return {
305
+ latitude,
306
+ longitude,
307
+ accuracy: location.accuracyInMeters ?? void 0,
308
+ name: location.name ?? void 0,
309
+ address: location.address ?? void 0,
310
+ caption: location.comment ?? void 0,
311
+ source: isLive ? "live" : location.name || location.address ? "place" : "pin",
312
+ isLive
313
+ };
314
+ }
315
+ }
316
+ }
317
+ return null;
318
+ }
319
+ function describeReplyContext(rawMessage) {
320
+ const message = unwrapMessage(rawMessage);
321
+ if (!message) return null;
322
+ const contextInfo = extractContextInfo(message);
323
+ const quoted = normalizeMessage(contextInfo?.quotedMessage);
324
+ if (!quoted) return null;
325
+ const location = extractLocationData(quoted);
326
+ const locationText = location ? formatLocationText(location) : void 0;
327
+ let body = [extractText(quoted), locationText].filter(Boolean).join("\n").trim();
328
+ if (!body) body = extractMediaPlaceholder(quoted);
329
+ if (!body) {
330
+ const quotedType = quoted ? getMessageContentType(quoted) : void 0;
331
+ logVerbose(`Quoted message missing extractable body${quotedType ? ` (type ${quotedType})` : ""}`);
332
+ return null;
333
+ }
334
+ const senderJid = contextInfo?.participant ?? void 0;
335
+ const sender = resolveComparableIdentity({
336
+ jid: senderJid,
337
+ label: senderJid ? jidToE164(senderJid) ?? senderJid : "unknown sender"
338
+ });
339
+ return {
340
+ id: contextInfo?.stanzaId || void 0,
341
+ body,
342
+ sender
343
+ };
344
+ }
345
+ function hasInteractiveResponseContent(message) {
346
+ if (!message) return false;
347
+ return Boolean(message.buttonsResponseMessage || message.listResponseMessage || message.templateButtonReplyMessage || message.interactiveResponseMessage);
348
+ }
349
+ /**
350
+ * Fast check that a Baileys message carries user-visible inbound content
351
+ * (text, media, contact, location, button/list selection). Returns false for
352
+ * protocol/receipt/typing notifications that arrive on the same
353
+ * `messages.upsert` stream as real messages but should not trigger pairing
354
+ * access-control side effects.
355
+ */
356
+ function hasInboundUserContent(rawMessage) {
357
+ if (!rawMessage) return false;
358
+ if (extractText(rawMessage)) return true;
359
+ if (extractMediaPlaceholder(rawMessage)) return true;
360
+ if (extractLocationData(rawMessage)) return true;
361
+ for (const candidate of buildMessageChain(rawMessage)) if (hasInteractiveResponseContent(candidate)) return true;
362
+ return false;
363
+ }
364
+ //#endregion
365
+ //#region extensions/whatsapp/src/inbound/outbound-mentions.ts
366
+ const CODE_FENCE_RE = /```[\s\S]*?```/g;
367
+ const INLINE_CODE_RE = /`[^`\n]+`/g;
368
+ const OUTBOUND_MENTION_RE = /@(\+?\d+)/g;
369
+ const KNOWN_USER_JID_RE = /^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted|lid|hosted\.lid|c\.us)$/i;
370
+ const PHONE_JID_DOMAIN_RE = /^(s\.whatsapp\.net|hosted|c\.us)$/i;
371
+ const LID_JID_DOMAIN_RE = /^(lid|hosted\.lid)$/i;
372
+ function isWhatsAppGroupJid(jid) {
373
+ return jid.endsWith("@g.us");
374
+ }
375
+ function mayContainWhatsAppOutboundMention(text) {
376
+ return /@\+?\d/.test(text);
377
+ }
378
+ function collectCodeRanges(text) {
379
+ const ranges = [];
380
+ for (const match of text.matchAll(CODE_FENCE_RE)) ranges.push({
381
+ start: match.index,
382
+ end: match.index + match[0].length
383
+ });
384
+ for (const match of text.matchAll(INLINE_CODE_RE)) {
385
+ const start = match.index;
386
+ if (ranges.some((range) => start >= range.start && start < range.end)) continue;
387
+ ranges.push({
388
+ start,
389
+ end: start + match[0].length
390
+ });
391
+ }
392
+ return ranges.toSorted((a, b) => a.start - b.start);
393
+ }
394
+ function isInRange(index, ranges) {
395
+ return ranges.some((range) => index >= range.start && index < range.end);
396
+ }
397
+ function normalizeKnownUserJid(value) {
398
+ const trimmed = value.replace(/^whatsapp:/i, "").trim();
399
+ const jidMatch = trimmed.match(KNOWN_USER_JID_RE);
400
+ if (jidMatch) {
401
+ const domain = jidMatch[2].toLowerCase() === "c.us" ? "s.whatsapp.net" : jidMatch[2].toLowerCase();
402
+ return `${jidMatch[1]}@${domain}`;
403
+ }
404
+ const digits = trimmed.startsWith("+") ? trimmed.replace(/\D/g, "") : /^\d+$/.test(trimmed) ? trimmed : "";
405
+ return digits ? `${digits}@s.whatsapp.net` : null;
406
+ }
407
+ function extractKnownJidParts(value) {
408
+ const normalized = normalizeKnownUserJid(value);
409
+ if (!normalized) return null;
410
+ const match = normalized.match(/^(\d+)@(.+)$/);
411
+ return match ? {
412
+ user: match[1],
413
+ domain: match[2]
414
+ } : null;
415
+ }
416
+ function extractPhoneDigits(value) {
417
+ if (!value) return null;
418
+ const trimmed = value.replace(/^whatsapp:/i, "").trim();
419
+ if (trimmed.startsWith("+") || /^\d+$/.test(trimmed)) return trimmed.replace(/\D/g, "") || null;
420
+ const parts = extractKnownJidParts(trimmed);
421
+ return parts && PHONE_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
422
+ }
423
+ function extractLidDigits(value) {
424
+ if (!value) return null;
425
+ const parts = extractKnownJidParts(value);
426
+ return parts && LID_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
427
+ }
428
+ function isLidJid(jid) {
429
+ const parts = extractKnownJidParts(jid);
430
+ return Boolean(parts && LID_JID_DOMAIN_RE.test(parts.domain));
431
+ }
432
+ function lidReplacementText(jid) {
433
+ const parts = extractKnownJidParts(jid);
434
+ if (!parts || !LID_JID_DOMAIN_RE.test(parts.domain)) return;
435
+ return `@${parts.user}`;
436
+ }
437
+ function participantValues(participant) {
438
+ return typeof participant === "string" ? { id: participant } : participant;
439
+ }
440
+ function chooseMentionJid(participant) {
441
+ const values = participantValues(participant);
442
+ const idJid = normalizeKnownUserJid(values.id ?? "");
443
+ const lidJid = normalizeKnownUserJid(values.lid ?? "");
444
+ return (idJid && isLidJid(idJid) ? idJid : null) ?? (lidJid && isLidJid(lidJid) ? lidJid : null) ?? idJid ?? lidJid ?? normalizeKnownUserJid(values.phoneNumber ?? "") ?? normalizeKnownUserJid(values.e164 ?? "");
445
+ }
446
+ function buildMentionTargetMaps(participants) {
447
+ const byPhone = /* @__PURE__ */ new Map();
448
+ const byLid = /* @__PURE__ */ new Map();
449
+ for (const participant of participants) {
450
+ const mentionJid = chooseMentionJid(participant);
451
+ if (!mentionJid) continue;
452
+ const target = {
453
+ mentionJid,
454
+ ...isLidJid(mentionJid) ? { replacementText: lidReplacementText(mentionJid) } : {}
455
+ };
456
+ const values = participantValues(participant);
457
+ for (const value of [
458
+ values.id,
459
+ values.phoneNumber,
460
+ values.e164
461
+ ]) {
462
+ const digits = extractPhoneDigits(value);
463
+ if (digits && !byPhone.has(digits)) byPhone.set(digits, target);
464
+ }
465
+ for (const value of [values.id, values.lid]) {
466
+ const digits = extractLidDigits(value);
467
+ if (digits && !byLid.has(digits)) byLid.set(digits, target);
468
+ }
469
+ }
470
+ return {
471
+ byPhone,
472
+ byLid
473
+ };
474
+ }
475
+ function shouldSkipMentionAt(text, index, end, codeRanges) {
476
+ if (isInRange(index, codeRanges)) return true;
477
+ const previous = index > 0 ? text[index - 1] : "";
478
+ const next = text[end] ?? "";
479
+ return Boolean(previous && /[\w@]/.test(previous) || next && /[\w@]/.test(next));
480
+ }
481
+ function resolveWhatsAppOutboundMentions(params) {
482
+ if (!isWhatsAppGroupJid(params.chatJid) || !mayContainWhatsAppOutboundMention(params.text) || !params.participants?.length) return {
483
+ text: params.text,
484
+ mentionedJids: []
485
+ };
486
+ const { byPhone, byLid } = buildMentionTargetMaps(params.participants);
487
+ if (byPhone.size === 0 && byLid.size === 0) return {
488
+ text: params.text,
489
+ mentionedJids: []
490
+ };
491
+ const codeRanges = collectCodeRanges(params.text);
492
+ const replacements = [];
493
+ const mentionedJids = [];
494
+ const seenMentionJids = /* @__PURE__ */ new Set();
495
+ for (const match of params.text.matchAll(OUTBOUND_MENTION_RE)) {
496
+ const start = match.index;
497
+ const token = match[0];
498
+ if (shouldSkipMentionAt(params.text, start, start + token.length, codeRanges)) continue;
499
+ const digits = match[1].replace(/\D/g, "");
500
+ const target = token.startsWith("@+") ? byPhone.get(digits) ?? byLid.get(digits) : byLid.get(digits) ?? byPhone.get(digits);
501
+ if (!target) continue;
502
+ if (!seenMentionJids.has(target.mentionJid)) {
503
+ seenMentionJids.add(target.mentionJid);
504
+ mentionedJids.push(target.mentionJid);
505
+ }
506
+ if (target.replacementText && target.replacementText !== token) replacements.push({
507
+ start,
508
+ end: start + token.length,
509
+ text: target.replacementText
510
+ });
511
+ }
512
+ if (replacements.length === 0) return {
513
+ text: params.text,
514
+ mentionedJids
515
+ };
516
+ let text = "";
517
+ let cursor = 0;
518
+ for (const replacement of replacements) {
519
+ text += params.text.slice(cursor, replacement.start);
520
+ text += replacement.text;
521
+ cursor = replacement.end;
522
+ }
523
+ text += params.text.slice(cursor);
524
+ return {
525
+ text,
526
+ mentionedJids
527
+ };
528
+ }
529
+ function addWhatsAppOutboundMentionsToContent(content, mentionedJids) {
530
+ return mentionedJids.length > 0 ? {
531
+ ...content,
532
+ mentions: [...mentionedJids]
533
+ } : content;
534
+ }
535
+ //#endregion
536
+ //#region extensions/whatsapp/src/inbound/send-result.ts
537
+ function resolveWhatsAppReceiptKind(kind) {
538
+ if (kind === "media" || kind === "text") return kind;
539
+ return "unknown";
540
+ }
541
+ function toReceiptSourceResult(key) {
542
+ return {
543
+ channel: "whatsapp",
544
+ messageId: key.id,
545
+ ...key.remoteJid ? { toJid: key.remoteJid } : {},
546
+ meta: {
547
+ fromMe: key.fromMe,
548
+ participant: key.participant
549
+ }
550
+ };
551
+ }
552
+ function createWhatsAppSendReceipt(kind, keys) {
553
+ return createMessageReceiptFromOutboundResults({
554
+ kind: resolveWhatsAppReceiptKind(kind),
555
+ results: keys.map(toReceiptSourceResult)
556
+ });
557
+ }
558
+ function normalizeKey(key) {
559
+ const id = typeof key?.id === "string" ? key.id.trim() : "";
560
+ if (!id) return;
561
+ return {
562
+ id,
563
+ remoteJid: key?.remoteJid,
564
+ fromMe: key?.fromMe,
565
+ participant: key?.participant
566
+ };
567
+ }
568
+ function normalizeWhatsAppSendResult(result, kind) {
569
+ const key = normalizeKey(result?.key);
570
+ return {
571
+ kind,
572
+ messageId: key?.id ?? "unknown",
573
+ receipt: createWhatsAppSendReceipt(kind, key ? [key] : []),
574
+ keys: key ? [key] : [],
575
+ providerAccepted: Boolean(key)
576
+ };
577
+ }
578
+ function combineWhatsAppSendResults(kind, results) {
579
+ const messageIds = [...new Set(results.flatMap(listWhatsAppSendResultMessageIds))];
580
+ const keys = results.flatMap((result) => result.keys);
581
+ return {
582
+ kind,
583
+ messageId: messageIds[0] ?? "unknown",
584
+ receipt: createWhatsAppSendReceipt(kind, keys),
585
+ keys,
586
+ providerAccepted: results.some((result) => result.providerAccepted)
587
+ };
588
+ }
589
+ function listWhatsAppSendResultMessageIds(result) {
590
+ const receiptIds = result.receipt ? listMessageReceiptPlatformIds(result.receipt) : [];
591
+ if (receiptIds.length > 0) return receiptIds;
592
+ const keyIds = result.keys.map((key) => key.id.trim()).filter(Boolean);
593
+ if (keyIds.length > 0) return [...new Set(keyIds)];
594
+ return [];
595
+ }
596
+ //#endregion
597
+ //#region extensions/whatsapp/src/inbound/send-api.ts
598
+ function recordWhatsAppOutbound(accountId) {
599
+ recordChannelActivity({
600
+ channel: "whatsapp",
601
+ accountId,
602
+ direction: "outbound"
603
+ });
604
+ }
605
+ function createWebSendApi(params) {
606
+ const resolveOutboundJid = (recipient) => params.authDir ? toWhatsappJidWithLid(recipient, { authDir: params.authDir }) : toWhatsappJid(recipient);
607
+ const resolveMentions = async (jid, text) => params.resolveOutboundMentions ? await params.resolveOutboundMentions({
608
+ jid,
609
+ text
610
+ }) : {
611
+ text,
612
+ mentionedJids: []
613
+ };
614
+ return {
615
+ sendMessage: async (to, text, mediaBuffer, mediaType, sendOptions) => {
616
+ const jid = resolveOutboundJid(to);
617
+ let payload;
618
+ if (mediaBuffer) mediaType ??= "application/octet-stream";
619
+ const shouldSendAudioText = Boolean(mediaBuffer && mediaType?.startsWith("audio/") && text.trim());
620
+ const resolvedPayloadText = shouldSendAudioText ? {
621
+ text,
622
+ mentionedJids: []
623
+ } : await resolveMentions(jid, text);
624
+ if (mediaBuffer && mediaType) if (mediaType.startsWith("image/")) payload = {
625
+ image: mediaBuffer,
626
+ caption: resolvedPayloadText.text || void 0,
627
+ mimetype: mediaType
628
+ };
629
+ else if (mediaType.startsWith("audio/")) payload = {
630
+ audio: mediaBuffer,
631
+ ptt: true,
632
+ mimetype: mediaType
633
+ };
634
+ else if (mediaType.startsWith("video/")) {
635
+ const gifPlayback = sendOptions?.gifPlayback;
636
+ payload = {
637
+ video: mediaBuffer,
638
+ caption: resolvedPayloadText.text || void 0,
639
+ mimetype: mediaType,
640
+ ...gifPlayback ? { gifPlayback: true } : {}
641
+ };
642
+ } else payload = {
643
+ document: mediaBuffer,
644
+ fileName: sendOptions?.fileName?.trim() || "file",
645
+ caption: resolvedPayloadText.text || void 0,
646
+ mimetype: mediaType
647
+ };
648
+ else payload = { text: resolvedPayloadText.text };
649
+ payload = addWhatsAppOutboundMentionsToContent(payload, resolvedPayloadText.mentionedJids);
650
+ const quotedOpts = buildQuotedMessageOptions({
651
+ messageId: sendOptions?.quotedMessageKey?.id,
652
+ remoteJid: sendOptions?.quotedMessageKey?.remoteJid,
653
+ fromMe: sendOptions?.quotedMessageKey?.fromMe,
654
+ participant: sendOptions?.quotedMessageKey?.participant,
655
+ messageText: sendOptions?.quotedMessageKey?.messageText
656
+ });
657
+ const results = [normalizeWhatsAppSendResult(quotedOpts ? await params.sock.sendMessage(jid, payload, quotedOpts) : await params.sock.sendMessage(jid, payload), mediaBuffer ? "media" : "text")];
658
+ if (shouldSendAudioText) {
659
+ const resolvedAudioText = await resolveMentions(jid, text);
660
+ const textPayload = addWhatsAppOutboundMentionsToContent({ text: resolvedAudioText.text }, resolvedAudioText.mentionedJids);
661
+ const textResult = quotedOpts ? await params.sock.sendMessage(jid, textPayload, quotedOpts) : await params.sock.sendMessage(jid, textPayload);
662
+ results.push(normalizeWhatsAppSendResult(textResult, "text"));
663
+ }
664
+ recordWhatsAppOutbound(sendOptions?.accountId ?? params.defaultAccountId);
665
+ return combineWhatsAppSendResults(mediaBuffer ? "media" : "text", results);
666
+ },
667
+ sendPoll: async (to, poll) => {
668
+ const jid = resolveOutboundJid(to);
669
+ const result = await params.sock.sendMessage(jid, { poll: {
670
+ name: poll.question,
671
+ values: poll.options,
672
+ selectableCount: poll.maxSelections ?? 1
673
+ } });
674
+ recordWhatsAppOutbound(params.defaultAccountId);
675
+ return normalizeWhatsAppSendResult(result, "poll");
676
+ },
677
+ sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
678
+ const jid = toWhatsappJid(chatJid);
679
+ return normalizeWhatsAppSendResult(await params.sock.sendMessage(jid, { react: {
680
+ text: emoji,
681
+ key: {
682
+ remoteJid: jid,
683
+ id: messageId,
684
+ fromMe,
685
+ participant: participant ? toWhatsappJid(participant) : void 0
686
+ }
687
+ } }), "reaction");
688
+ },
689
+ sendComposingTo: async (to) => {
690
+ const jid = resolveOutboundJid(to);
691
+ if (isWhatsAppNewsletterJid(jid)) return;
692
+ await params.sock.sendPresenceUpdate("composing", jid);
693
+ }
694
+ };
695
+ }
696
+ //#endregion
697
+ export { mayContainWhatsAppOutboundMention as a, extractContactContext as c, extractMediaPlaceholder as d, extractMentionedJids as f, addWhatsAppOutboundMentionsToContent as i, extractContextInfo as l, hasInboundUserContent as m, listWhatsAppSendResultMessageIds as n, resolveWhatsAppOutboundMentions as o, extractText as p, normalizeWhatsAppSendResult as r, describeReplyContext as s, createWebSendApi as t, extractLocationData as u };