@openclaw/whatsapp 2026.5.7-beta.1 → 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.
- package/dist/action-runtime-api.js +1 -1
- package/dist/{action-runtime-BqYZmBX_.js → action-runtime-flTHnBac.js} +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/api.js +115 -7
- package/dist/{auth-store-BNZmNP-s.js → auth-store-CG0eSZ0D.js} +17 -39
- package/dist/{channel-CTr3YjO8.js → channel-Dnv-QeNE.js} +34 -7
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-react-action-BuSTzmDX.js → channel-react-action-LOuxBqX6.js} +1 -1
- package/dist/{channel.runtime-C3i9Svfj.js → channel.runtime-CCZpOzbU.js} +5 -5
- package/dist/{channel.setup-BYtkDwHe.js → channel.setup-CV0OjAwl.js} +3 -3
- package/dist/{connection-controller-BCIuChXD.js → connection-controller-d8D2iUBj.js} +6 -197
- package/dist/contract-api.js +1 -1
- package/dist/legacy-state-migrations-api.js +1 -1
- package/dist/light-runtime-api.js +2 -2
- package/dist/{login-DSUTCcJ7.js → login-B_u009k0.js} +3 -2
- package/dist/{login-qr-dfs8tfGo.js → login-qr-C0b7I7hu.js} +3 -2
- package/dist/login-qr-runtime.js +1 -1
- package/dist/{monitor-C5_C_RGJ.js → monitor-5u0dVXnB.js} +93 -683
- package/dist/{outbound-adapter-CbR17Ehj.js → outbound-adapter-BoHBD2S7.js} +3 -3
- package/dist/{outbound-base-BmzhutvF.js → outbound-base-CCx7atwc.js} +6 -1
- package/dist/{outbound-media-contract-BPdOq7Hb.js → outbound-media-contract-Dzi1ILTd.js} +36 -40
- package/dist/outbound-payload-test-api.js +1 -1
- package/dist/runtime-api.js +8 -7
- package/dist/{send-CxctcFGT.js → send-CLBN7TZv.js} +1 -1
- package/dist/send-api-DoycpOvC.js +697 -0
- package/dist/session-BXC3R43P.js +198 -0
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-CF3l3gEZ.js → setup-surface-RbOpXjSR.js} +2 -2
- package/dist/{shared-CTSTwprn.js → shared-h4cwNeYT.js} +2 -2
- package/dist/{state-migrations-DnCdvoYD.js → state-migrations-Bm0S67yH.js} +2 -1
- package/dist/test-api.js +1 -1
- package/package.json +5 -5
- package/dist/{access-control-CTj7P7WP.js → access-control-hFVqAWSd.js} +1 -1
- /package/dist/{active-listener-N7GFKsuN.js → active-listener-DTbKS-j-.js} +0 -0
- /package/dist/{audio-preflight.runtime-Bcdsl_ri.js → audio-preflight.runtime-MmkB-pgX.js} +0 -0
- /package/dist/{reply-resolver.runtime-BRCgoO4C.js → reply-resolver.runtime-rIVWohXr.js} +0 -0
- /package/dist/{session.runtime-C1eSE_KK.js → session.runtime-CVhtXjTk.js} +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BDqgpFEB.js";
|
|
2
|
-
import {
|
|
2
|
+
import { t as isWhatsAppGroupJid } from "./normalize-target-nXxC_hxG.js";
|
|
3
3
|
import { t as resolveWhatsAppReactionLevel } from "./reaction-level-CRVEVAfi.js";
|
|
4
|
-
import "./normalize-Y3eBdM8a.js";
|
|
5
4
|
import { n as getStatusCode, t as formatError } from "./session-errors-CSuKZPub.js";
|
|
6
|
-
import { a as markdownToWhatsApp,
|
|
7
|
-
import { i as prepareWhatsAppOutboundMedia, o as sendWhatsAppOutboundWithRetry, r as normalizeWhatsAppPayloadTextPreservingIndentation, t as normalizeWhatsAppOutboundPayload } from "./outbound-media-contract-
|
|
8
|
-
import { r as sendReactionWhatsApp } from "./send-
|
|
5
|
+
import { a as markdownToWhatsApp, i as jidToE164, o as resolveJidToE164, r as isSelfChatMode, t as text_runtime_exports } from "./text-runtime-DFFwk5z6.js";
|
|
6
|
+
import { i as prepareWhatsAppOutboundMedia, o as sendWhatsAppOutboundWithRetry, r as normalizeWhatsAppPayloadTextPreservingIndentation, t as normalizeWhatsAppOutboundPayload } from "./outbound-media-contract-Dzi1ILTd.js";
|
|
7
|
+
import { r as sendReactionWhatsApp } from "./send-CLBN7TZv.js";
|
|
9
8
|
import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-BXs55Teh.js";
|
|
10
|
-
import { _ as readWebSelfIdentityForDecision, a as getWebAuthAgeMs, h as readWebSelfId, r as WhatsAppAuthUnstableError, s as logoutWeb } from "./auth-store-
|
|
9
|
+
import { _ as readWebSelfIdentityForDecision, a as getWebAuthAgeMs, h as readWebSelfId, r as WhatsAppAuthUnstableError, s as logoutWeb } from "./auth-store-CG0eSZ0D.js";
|
|
11
10
|
import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-DhaC0MoD.js";
|
|
12
|
-
import { i as resolveWhatsAppInboundPolicy, n as checkInboundAccessControl, r as resolveWhatsAppCommandAuthorized } from "./access-control-
|
|
13
|
-
import {
|
|
11
|
+
import { i as resolveWhatsAppInboundPolicy, n as checkInboundAccessControl, r as resolveWhatsAppCommandAuthorized } from "./access-control-hFVqAWSd.js";
|
|
12
|
+
import { a as mayContainWhatsAppOutboundMention, c as extractContactContext, d as extractMediaPlaceholder, f as extractMentionedJids, i as addWhatsAppOutboundMentionsToContent, l as extractContextInfo, m as hasInboundUserContent, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as extractText, r as normalizeWhatsAppSendResult, s as describeReplyContext, t as createWebSendApi, u as extractLocationData } from "./send-api-DoycpOvC.js";
|
|
13
|
+
import { i as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-BXC3R43P.js";
|
|
14
|
+
import { c as newConnectionId, d as sleepWithAbort, l as resolveHeartbeatSeconds, n as WhatsAppConnectionController, o as DEFAULT_RECONNECT_POLICY, s as computeBackoff, u as resolveReconnectPolicy } from "./connection-controller-d8D2iUBj.js";
|
|
14
15
|
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
|
|
15
16
|
import { getChildLogger, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
16
17
|
import { createAckReactionHandle, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
|
|
@@ -18,22 +19,23 @@ import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
|
|
18
19
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
19
20
|
import { createSubsystemLogger, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
|
|
20
21
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
22
|
+
import { evaluateSupplementalContextVisibility, filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
21
23
|
import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia as loadWebMedia$1, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
|
|
24
|
+
import { createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
|
|
22
25
|
import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
|
|
23
26
|
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
24
27
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId as normalizeAccountId$1, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
25
28
|
import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
|
|
26
|
-
import { evaluateSupplementalContextVisibility, filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
27
29
|
import { loadSessionStore, resolveStorePath, updateLastRoute, updateSessionStore } from "openclaw/plugin-sdk/session-store-runtime";
|
|
30
|
+
import { DisconnectReason as DisconnectReason$1, downloadMediaMessage, isJidGroup, normalizeMessageContent as normalizeMessageContent$1 } from "@whiskeysockets/baileys";
|
|
31
|
+
import { formatLocationText, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
32
|
+
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
28
33
|
import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContext, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
29
34
|
import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
|
|
30
35
|
import { hasControlCommand, hasControlCommand as hasControlCommand$1, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
|
|
31
36
|
import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
|
|
32
37
|
import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
|
|
33
38
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
34
|
-
import { DisconnectReason as DisconnectReason$1, downloadMediaMessage, extractMessageContent, getContentType, isJidGroup, normalizeMessageContent, normalizeMessageContent as normalizeMessageContent$1 } from "@whiskeysockets/baileys";
|
|
35
|
-
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
36
|
-
import { formatLocationText, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
37
39
|
import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
38
40
|
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
39
41
|
import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
@@ -48,7 +50,6 @@ import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-re
|
|
|
48
50
|
import { resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
49
51
|
import { formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1 } from "openclaw/plugin-sdk/channel-envelope";
|
|
50
52
|
import { toLocationContext } from "openclaw/plugin-sdk/channel-location";
|
|
51
|
-
import { createChannelReplyPipeline, resolveChannelSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
|
52
53
|
//#region extensions/whatsapp/src/inbound/dedupe.ts
|
|
53
54
|
const RECENT_WEB_MESSAGE_TTL_MS = 20 * 6e4;
|
|
54
55
|
const RECENT_WEB_MESSAGE_MAX = 5e3;
|
|
@@ -137,359 +138,6 @@ function isRecentOutboundMessage(params) {
|
|
|
137
138
|
return recentOutboundMessages.peek(key);
|
|
138
139
|
}
|
|
139
140
|
//#endregion
|
|
140
|
-
//#region extensions/whatsapp/src/vcard.ts
|
|
141
|
-
const ALLOWED_VCARD_KEYS = new Set([
|
|
142
|
-
"FN",
|
|
143
|
-
"N",
|
|
144
|
-
"TEL"
|
|
145
|
-
]);
|
|
146
|
-
function parseVcard(vcard) {
|
|
147
|
-
if (!vcard) return { phones: [] };
|
|
148
|
-
const lines = vcard.split(/\r?\n/);
|
|
149
|
-
let nameFromN;
|
|
150
|
-
let nameFromFn;
|
|
151
|
-
const phones = [];
|
|
152
|
-
for (const rawLine of lines) {
|
|
153
|
-
const line = rawLine.trim();
|
|
154
|
-
if (!line) continue;
|
|
155
|
-
const colonIndex = line.indexOf(":");
|
|
156
|
-
if (colonIndex === -1) continue;
|
|
157
|
-
const key = line.slice(0, colonIndex).toUpperCase();
|
|
158
|
-
const rawValue = line.slice(colonIndex + 1).trim();
|
|
159
|
-
if (!rawValue) continue;
|
|
160
|
-
const baseKey = normalizeVcardKey(key);
|
|
161
|
-
if (!baseKey || !ALLOWED_VCARD_KEYS.has(baseKey)) continue;
|
|
162
|
-
const value = cleanVcardValue(rawValue);
|
|
163
|
-
if (!value) continue;
|
|
164
|
-
if (baseKey === "FN" && !nameFromFn) {
|
|
165
|
-
nameFromFn = normalizeVcardName(value);
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (baseKey === "N" && !nameFromN) {
|
|
169
|
-
nameFromN = normalizeVcardName(value);
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
if (baseKey === "TEL") {
|
|
173
|
-
const phone = normalizeVcardPhone(value);
|
|
174
|
-
if (phone) phones.push(phone);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
name: nameFromFn ?? nameFromN,
|
|
179
|
-
phones
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function normalizeVcardKey(key) {
|
|
183
|
-
const [primary] = key.split(";");
|
|
184
|
-
if (!primary) return;
|
|
185
|
-
const segments = primary.split(".");
|
|
186
|
-
return segments[segments.length - 1] || void 0;
|
|
187
|
-
}
|
|
188
|
-
function cleanVcardValue(value) {
|
|
189
|
-
return value.replace(/\\n/gi, " ").replace(/\\,/g, ",").replace(/\\;/g, ";").trim();
|
|
190
|
-
}
|
|
191
|
-
function normalizeVcardName(value) {
|
|
192
|
-
return value.replace(/;/g, " ").replace(/\s+/g, " ").trim();
|
|
193
|
-
}
|
|
194
|
-
function normalizeVcardPhone(value) {
|
|
195
|
-
const trimmed = value.trim();
|
|
196
|
-
if (!trimmed) return "";
|
|
197
|
-
if (normalizeLowercaseStringOrEmpty(trimmed).startsWith("tel:")) return trimmed.slice(4).trim();
|
|
198
|
-
return trimmed;
|
|
199
|
-
}
|
|
200
|
-
//#endregion
|
|
201
|
-
//#region extensions/whatsapp/src/inbound/extract.ts
|
|
202
|
-
const MESSAGE_WRAPPER_KEYS = [
|
|
203
|
-
"botInvokeMessage",
|
|
204
|
-
"ephemeralMessage",
|
|
205
|
-
"viewOnceMessage",
|
|
206
|
-
"viewOnceMessageV2",
|
|
207
|
-
"viewOnceMessageV2Extension",
|
|
208
|
-
"documentWithCaptionMessage",
|
|
209
|
-
"groupMentionedMessage"
|
|
210
|
-
];
|
|
211
|
-
const MESSAGE_CONTENT_KEYS = [
|
|
212
|
-
"conversation",
|
|
213
|
-
"extendedTextMessage",
|
|
214
|
-
"imageMessage",
|
|
215
|
-
"videoMessage",
|
|
216
|
-
"audioMessage",
|
|
217
|
-
"documentMessage",
|
|
218
|
-
"stickerMessage",
|
|
219
|
-
"locationMessage",
|
|
220
|
-
"liveLocationMessage",
|
|
221
|
-
"contactMessage",
|
|
222
|
-
"contactsArrayMessage",
|
|
223
|
-
"buttonsResponseMessage",
|
|
224
|
-
"listResponseMessage",
|
|
225
|
-
"templateButtonReplyMessage",
|
|
226
|
-
"interactiveResponseMessage",
|
|
227
|
-
"buttonsMessage",
|
|
228
|
-
"listMessage"
|
|
229
|
-
];
|
|
230
|
-
function fallbackNormalizeMessageContent(message) {
|
|
231
|
-
let current = message;
|
|
232
|
-
while (current && typeof current === "object") {
|
|
233
|
-
let unwrapped = false;
|
|
234
|
-
for (const key of MESSAGE_WRAPPER_KEYS) {
|
|
235
|
-
const candidate = current[key];
|
|
236
|
-
if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message) {
|
|
237
|
-
current = candidate.message;
|
|
238
|
-
unwrapped = true;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (!unwrapped) break;
|
|
243
|
-
}
|
|
244
|
-
return current;
|
|
245
|
-
}
|
|
246
|
-
function normalizeMessage(message) {
|
|
247
|
-
if (typeof normalizeMessageContent === "function") return normalizeMessageContent(message);
|
|
248
|
-
return fallbackNormalizeMessageContent(message);
|
|
249
|
-
}
|
|
250
|
-
function fallbackGetContentType(message) {
|
|
251
|
-
const normalized = fallbackNormalizeMessageContent(message);
|
|
252
|
-
if (!normalized || typeof normalized !== "object") return;
|
|
253
|
-
for (const key of MESSAGE_CONTENT_KEYS) if (normalized[key] != null) return key;
|
|
254
|
-
}
|
|
255
|
-
function getMessageContentType(message) {
|
|
256
|
-
if (typeof getContentType === "function") return getContentType(message);
|
|
257
|
-
return fallbackGetContentType(message);
|
|
258
|
-
}
|
|
259
|
-
function extractMessage(message) {
|
|
260
|
-
if (typeof extractMessageContent === "function") return extractMessageContent(message);
|
|
261
|
-
const normalized = fallbackNormalizeMessageContent(message);
|
|
262
|
-
const contentType = fallbackGetContentType(normalized);
|
|
263
|
-
if (!normalized || !contentType || contentType === "conversation") return normalized;
|
|
264
|
-
const candidate = normalized[contentType];
|
|
265
|
-
return candidate && typeof candidate === "object" ? candidate : normalized;
|
|
266
|
-
}
|
|
267
|
-
function getFutureProofInnerMessage(message) {
|
|
268
|
-
const contentType = getMessageContentType(message);
|
|
269
|
-
const candidate = contentType ? message[contentType] : void 0;
|
|
270
|
-
if (candidate && typeof candidate === "object" && "message" in candidate && candidate.message && typeof candidate.message === "object") {
|
|
271
|
-
const inner = normalizeMessage(candidate.message);
|
|
272
|
-
if (inner) {
|
|
273
|
-
const innerType = getMessageContentType(inner);
|
|
274
|
-
if (innerType && innerType !== contentType) return inner;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
function buildMessageChain(message) {
|
|
279
|
-
const chain = [];
|
|
280
|
-
let current = normalizeMessage(message);
|
|
281
|
-
while (current && chain.length < 4) {
|
|
282
|
-
chain.push(current);
|
|
283
|
-
current = getFutureProofInnerMessage(current);
|
|
284
|
-
}
|
|
285
|
-
return chain;
|
|
286
|
-
}
|
|
287
|
-
function unwrapMessage$1(message) {
|
|
288
|
-
return buildMessageChain(message).at(-1);
|
|
289
|
-
}
|
|
290
|
-
function extractContextInfoFromMessage(message) {
|
|
291
|
-
const contentType = getMessageContentType(message);
|
|
292
|
-
const candidate = contentType ? message[contentType] : void 0;
|
|
293
|
-
const contextInfo = candidate && typeof candidate === "object" && "contextInfo" in candidate ? candidate.contextInfo : void 0;
|
|
294
|
-
if (contextInfo) return contextInfo;
|
|
295
|
-
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;
|
|
296
|
-
if (fallback) return fallback;
|
|
297
|
-
for (const value of Object.values(message)) {
|
|
298
|
-
if (!value || typeof value !== "object") continue;
|
|
299
|
-
if ("contextInfo" in value) {
|
|
300
|
-
const candidateContext = value.contextInfo;
|
|
301
|
-
if (candidateContext) return candidateContext;
|
|
302
|
-
}
|
|
303
|
-
if ("message" in value) {
|
|
304
|
-
const inner = value.message;
|
|
305
|
-
if (inner) {
|
|
306
|
-
const innerCtx = extractContextInfo(inner);
|
|
307
|
-
if (innerCtx) return innerCtx;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function extractContextInfo(message) {
|
|
313
|
-
for (const candidate of buildMessageChain(message)) {
|
|
314
|
-
const contextInfo = extractContextInfoFromMessage(candidate);
|
|
315
|
-
if (contextInfo) return contextInfo;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
function extractMentionedJids(rawMessage) {
|
|
319
|
-
const message = unwrapMessage$1(rawMessage);
|
|
320
|
-
if (!message) return;
|
|
321
|
-
const flattened = [
|
|
322
|
-
message.extendedTextMessage?.contextInfo?.mentionedJid,
|
|
323
|
-
message.imageMessage?.contextInfo?.mentionedJid,
|
|
324
|
-
message.videoMessage?.contextInfo?.mentionedJid,
|
|
325
|
-
message.documentMessage?.contextInfo?.mentionedJid,
|
|
326
|
-
message.audioMessage?.contextInfo?.mentionedJid,
|
|
327
|
-
message.stickerMessage?.contextInfo?.mentionedJid,
|
|
328
|
-
message.buttonsResponseMessage?.contextInfo?.mentionedJid,
|
|
329
|
-
message.listResponseMessage?.contextInfo?.mentionedJid
|
|
330
|
-
].flatMap((arr) => arr ?? []).filter(Boolean);
|
|
331
|
-
if (flattened.length === 0) return;
|
|
332
|
-
return Array.from(new Set(flattened));
|
|
333
|
-
}
|
|
334
|
-
function extractText(rawMessage) {
|
|
335
|
-
const message = unwrapMessage$1(rawMessage);
|
|
336
|
-
if (!message) return;
|
|
337
|
-
const extracted = extractMessage(message);
|
|
338
|
-
const candidates = [message, extracted && extracted !== message ? extracted : void 0];
|
|
339
|
-
for (const candidate of candidates) {
|
|
340
|
-
if (!candidate) continue;
|
|
341
|
-
if (typeof candidate.conversation === "string" && candidate.conversation.trim()) return candidate.conversation.trim();
|
|
342
|
-
const extended = candidate.extendedTextMessage?.text;
|
|
343
|
-
if (extended?.trim()) return extended.trim();
|
|
344
|
-
const caption = candidate.imageMessage?.caption ?? candidate.videoMessage?.caption ?? candidate.documentMessage?.caption;
|
|
345
|
-
if (caption?.trim()) return caption.trim();
|
|
346
|
-
}
|
|
347
|
-
const contactPlaceholder = extractContactPlaceholder(message) ?? (extracted && extracted !== message ? extractContactPlaceholder(extracted) : void 0);
|
|
348
|
-
if (contactPlaceholder) return contactPlaceholder;
|
|
349
|
-
}
|
|
350
|
-
function extractMediaPlaceholder(rawMessage) {
|
|
351
|
-
const message = unwrapMessage$1(rawMessage);
|
|
352
|
-
if (!message) return;
|
|
353
|
-
if (message.imageMessage) return "<media:image>";
|
|
354
|
-
if (message.videoMessage) return "<media:video>";
|
|
355
|
-
if (message.audioMessage) return "<media:audio>";
|
|
356
|
-
if (message.documentMessage) return "<media:document>";
|
|
357
|
-
if (message.stickerMessage) return "<media:sticker>";
|
|
358
|
-
}
|
|
359
|
-
function extractContactPlaceholder(rawMessage) {
|
|
360
|
-
const contactContext = extractContactContext(rawMessage);
|
|
361
|
-
if (!contactContext) return;
|
|
362
|
-
if (contactContext.kind === "contact") return "<contact>";
|
|
363
|
-
const suffix = contactContext.total === 1 ? "contact" : "contacts";
|
|
364
|
-
return `<contacts: ${contactContext.total} ${suffix}>`;
|
|
365
|
-
}
|
|
366
|
-
function extractContactContext(rawMessage) {
|
|
367
|
-
const message = unwrapMessage$1(rawMessage);
|
|
368
|
-
if (!message) return;
|
|
369
|
-
const contact = message.contactMessage ?? void 0;
|
|
370
|
-
if (contact) {
|
|
371
|
-
const { name, phones } = describeContact({
|
|
372
|
-
displayName: contact.displayName,
|
|
373
|
-
vcard: contact.vcard
|
|
374
|
-
});
|
|
375
|
-
return {
|
|
376
|
-
kind: "contact",
|
|
377
|
-
total: 1,
|
|
378
|
-
contacts: [{
|
|
379
|
-
name,
|
|
380
|
-
phones
|
|
381
|
-
}]
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
const contactsArray = message.contactsArrayMessage?.contacts ?? void 0;
|
|
385
|
-
if (!contactsArray || contactsArray.length === 0) return;
|
|
386
|
-
return {
|
|
387
|
-
kind: "contacts",
|
|
388
|
-
total: contactsArray.length,
|
|
389
|
-
contacts: contactsArray.map((entry) => describeContact({
|
|
390
|
-
displayName: entry.displayName,
|
|
391
|
-
vcard: entry.vcard
|
|
392
|
-
}))
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function describeContact(input) {
|
|
396
|
-
const displayName = (input.displayName ?? "").trim();
|
|
397
|
-
const parsed = parseVcard(input.vcard ?? void 0);
|
|
398
|
-
return {
|
|
399
|
-
name: displayName || parsed.name,
|
|
400
|
-
phones: parsed.phones
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
function extractLocationData(rawMessage) {
|
|
404
|
-
const message = unwrapMessage$1(rawMessage);
|
|
405
|
-
if (!message) return null;
|
|
406
|
-
const live = message.liveLocationMessage ?? void 0;
|
|
407
|
-
if (live) {
|
|
408
|
-
const latitudeRaw = live.degreesLatitude;
|
|
409
|
-
const longitudeRaw = live.degreesLongitude;
|
|
410
|
-
if (latitudeRaw != null && longitudeRaw != null) {
|
|
411
|
-
const latitude = latitudeRaw;
|
|
412
|
-
const longitude = longitudeRaw;
|
|
413
|
-
if (Number.isFinite(latitude) && Number.isFinite(longitude)) return {
|
|
414
|
-
latitude,
|
|
415
|
-
longitude,
|
|
416
|
-
accuracy: live.accuracyInMeters ?? void 0,
|
|
417
|
-
caption: live.caption ?? void 0,
|
|
418
|
-
source: "live",
|
|
419
|
-
isLive: true
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
const location = message.locationMessage ?? void 0;
|
|
424
|
-
if (location) {
|
|
425
|
-
const latitudeRaw = location.degreesLatitude;
|
|
426
|
-
const longitudeRaw = location.degreesLongitude;
|
|
427
|
-
if (latitudeRaw != null && longitudeRaw != null) {
|
|
428
|
-
const latitude = latitudeRaw;
|
|
429
|
-
const longitude = longitudeRaw;
|
|
430
|
-
if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
|
|
431
|
-
const isLive = Boolean(location.isLive);
|
|
432
|
-
return {
|
|
433
|
-
latitude,
|
|
434
|
-
longitude,
|
|
435
|
-
accuracy: location.accuracyInMeters ?? void 0,
|
|
436
|
-
name: location.name ?? void 0,
|
|
437
|
-
address: location.address ?? void 0,
|
|
438
|
-
caption: location.comment ?? void 0,
|
|
439
|
-
source: isLive ? "live" : location.name || location.address ? "place" : "pin",
|
|
440
|
-
isLive
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
function describeReplyContext(rawMessage) {
|
|
448
|
-
const message = unwrapMessage$1(rawMessage);
|
|
449
|
-
if (!message) return null;
|
|
450
|
-
const contextInfo = extractContextInfo(message);
|
|
451
|
-
const quoted = normalizeMessage(contextInfo?.quotedMessage);
|
|
452
|
-
if (!quoted) return null;
|
|
453
|
-
const location = extractLocationData(quoted);
|
|
454
|
-
const locationText = location ? formatLocationText(location) : void 0;
|
|
455
|
-
let body = [extractText(quoted), locationText].filter(Boolean).join("\n").trim();
|
|
456
|
-
if (!body) body = extractMediaPlaceholder(quoted);
|
|
457
|
-
if (!body) {
|
|
458
|
-
const quotedType = quoted ? getMessageContentType(quoted) : void 0;
|
|
459
|
-
logVerbose(`Quoted message missing extractable body${quotedType ? ` (type ${quotedType})` : ""}`);
|
|
460
|
-
return null;
|
|
461
|
-
}
|
|
462
|
-
const senderJid = contextInfo?.participant ?? void 0;
|
|
463
|
-
const sender = resolveComparableIdentity({
|
|
464
|
-
jid: senderJid,
|
|
465
|
-
label: senderJid ? jidToE164(senderJid) ?? senderJid : "unknown sender"
|
|
466
|
-
});
|
|
467
|
-
return {
|
|
468
|
-
id: contextInfo?.stanzaId || void 0,
|
|
469
|
-
body,
|
|
470
|
-
sender
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function hasInteractiveResponseContent(message) {
|
|
474
|
-
if (!message) return false;
|
|
475
|
-
return Boolean(message.buttonsResponseMessage || message.listResponseMessage || message.templateButtonReplyMessage || message.interactiveResponseMessage);
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Fast check that a Baileys message carries user-visible inbound content
|
|
479
|
-
* (text, media, contact, location, button/list selection). Returns false for
|
|
480
|
-
* protocol/receipt/typing notifications that arrive on the same
|
|
481
|
-
* `messages.upsert` stream as real messages but should not trigger pairing
|
|
482
|
-
* access-control side effects.
|
|
483
|
-
*/
|
|
484
|
-
function hasInboundUserContent(rawMessage) {
|
|
485
|
-
if (!rawMessage) return false;
|
|
486
|
-
if (extractText(rawMessage)) return true;
|
|
487
|
-
if (extractMediaPlaceholder(rawMessage)) return true;
|
|
488
|
-
if (extractLocationData(rawMessage)) return true;
|
|
489
|
-
for (const candidate of buildMessageChain(rawMessage)) if (hasInteractiveResponseContent(candidate)) return true;
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
//#endregion
|
|
493
141
|
//#region extensions/whatsapp/src/inbound/lifecycle.ts
|
|
494
142
|
function attachEmitterListener(emitter, event, listener) {
|
|
495
143
|
emitter.on(event, listener);
|
|
@@ -561,310 +209,6 @@ async function downloadQuotedInboundMedia(msg, sock) {
|
|
|
561
209
|
}, sock);
|
|
562
210
|
}
|
|
563
211
|
//#endregion
|
|
564
|
-
//#region extensions/whatsapp/src/inbound/outbound-mentions.ts
|
|
565
|
-
const CODE_FENCE_RE = /```[\s\S]*?```/g;
|
|
566
|
-
const INLINE_CODE_RE = /`[^`\n]+`/g;
|
|
567
|
-
const OUTBOUND_MENTION_RE = /@(\+?\d+)/g;
|
|
568
|
-
const KNOWN_USER_JID_RE = /^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted|lid|hosted\.lid|c\.us)$/i;
|
|
569
|
-
const PHONE_JID_DOMAIN_RE = /^(s\.whatsapp\.net|hosted|c\.us)$/i;
|
|
570
|
-
const LID_JID_DOMAIN_RE = /^(lid|hosted\.lid)$/i;
|
|
571
|
-
function isWhatsAppGroupJid(jid) {
|
|
572
|
-
return jid.endsWith("@g.us");
|
|
573
|
-
}
|
|
574
|
-
function mayContainWhatsAppOutboundMention(text) {
|
|
575
|
-
return /@\+?\d/.test(text);
|
|
576
|
-
}
|
|
577
|
-
function collectCodeRanges(text) {
|
|
578
|
-
const ranges = [];
|
|
579
|
-
for (const match of text.matchAll(CODE_FENCE_RE)) ranges.push({
|
|
580
|
-
start: match.index,
|
|
581
|
-
end: match.index + match[0].length
|
|
582
|
-
});
|
|
583
|
-
for (const match of text.matchAll(INLINE_CODE_RE)) {
|
|
584
|
-
const start = match.index;
|
|
585
|
-
if (ranges.some((range) => start >= range.start && start < range.end)) continue;
|
|
586
|
-
ranges.push({
|
|
587
|
-
start,
|
|
588
|
-
end: start + match[0].length
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
return ranges.toSorted((a, b) => a.start - b.start);
|
|
592
|
-
}
|
|
593
|
-
function isInRange(index, ranges) {
|
|
594
|
-
return ranges.some((range) => index >= range.start && index < range.end);
|
|
595
|
-
}
|
|
596
|
-
function normalizeKnownUserJid(value) {
|
|
597
|
-
const trimmed = value.replace(/^whatsapp:/i, "").trim();
|
|
598
|
-
const jidMatch = trimmed.match(KNOWN_USER_JID_RE);
|
|
599
|
-
if (jidMatch) {
|
|
600
|
-
const domain = jidMatch[2].toLowerCase() === "c.us" ? "s.whatsapp.net" : jidMatch[2].toLowerCase();
|
|
601
|
-
return `${jidMatch[1]}@${domain}`;
|
|
602
|
-
}
|
|
603
|
-
const digits = trimmed.startsWith("+") ? trimmed.replace(/\D/g, "") : /^\d+$/.test(trimmed) ? trimmed : "";
|
|
604
|
-
return digits ? `${digits}@s.whatsapp.net` : null;
|
|
605
|
-
}
|
|
606
|
-
function extractKnownJidParts(value) {
|
|
607
|
-
const normalized = normalizeKnownUserJid(value);
|
|
608
|
-
if (!normalized) return null;
|
|
609
|
-
const match = normalized.match(/^(\d+)@(.+)$/);
|
|
610
|
-
return match ? {
|
|
611
|
-
user: match[1],
|
|
612
|
-
domain: match[2]
|
|
613
|
-
} : null;
|
|
614
|
-
}
|
|
615
|
-
function extractPhoneDigits(value) {
|
|
616
|
-
if (!value) return null;
|
|
617
|
-
const trimmed = value.replace(/^whatsapp:/i, "").trim();
|
|
618
|
-
if (trimmed.startsWith("+") || /^\d+$/.test(trimmed)) return trimmed.replace(/\D/g, "") || null;
|
|
619
|
-
const parts = extractKnownJidParts(trimmed);
|
|
620
|
-
return parts && PHONE_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
|
|
621
|
-
}
|
|
622
|
-
function extractLidDigits(value) {
|
|
623
|
-
if (!value) return null;
|
|
624
|
-
const parts = extractKnownJidParts(value);
|
|
625
|
-
return parts && LID_JID_DOMAIN_RE.test(parts.domain) ? parts.user : null;
|
|
626
|
-
}
|
|
627
|
-
function isLidJid(jid) {
|
|
628
|
-
const parts = extractKnownJidParts(jid);
|
|
629
|
-
return Boolean(parts && LID_JID_DOMAIN_RE.test(parts.domain));
|
|
630
|
-
}
|
|
631
|
-
function lidReplacementText(jid) {
|
|
632
|
-
const parts = extractKnownJidParts(jid);
|
|
633
|
-
if (!parts || !LID_JID_DOMAIN_RE.test(parts.domain)) return;
|
|
634
|
-
return `@${parts.user}`;
|
|
635
|
-
}
|
|
636
|
-
function participantValues(participant) {
|
|
637
|
-
return typeof participant === "string" ? { id: participant } : participant;
|
|
638
|
-
}
|
|
639
|
-
function chooseMentionJid(participant) {
|
|
640
|
-
const values = participantValues(participant);
|
|
641
|
-
const idJid = normalizeKnownUserJid(values.id ?? "");
|
|
642
|
-
const lidJid = normalizeKnownUserJid(values.lid ?? "");
|
|
643
|
-
return (idJid && isLidJid(idJid) ? idJid : null) ?? (lidJid && isLidJid(lidJid) ? lidJid : null) ?? idJid ?? lidJid ?? normalizeKnownUserJid(values.phoneNumber ?? "") ?? normalizeKnownUserJid(values.e164 ?? "");
|
|
644
|
-
}
|
|
645
|
-
function buildMentionTargetMaps(participants) {
|
|
646
|
-
const byPhone = /* @__PURE__ */ new Map();
|
|
647
|
-
const byLid = /* @__PURE__ */ new Map();
|
|
648
|
-
for (const participant of participants) {
|
|
649
|
-
const mentionJid = chooseMentionJid(participant);
|
|
650
|
-
if (!mentionJid) continue;
|
|
651
|
-
const target = {
|
|
652
|
-
mentionJid,
|
|
653
|
-
...isLidJid(mentionJid) ? { replacementText: lidReplacementText(mentionJid) } : {}
|
|
654
|
-
};
|
|
655
|
-
const values = participantValues(participant);
|
|
656
|
-
for (const value of [
|
|
657
|
-
values.id,
|
|
658
|
-
values.phoneNumber,
|
|
659
|
-
values.e164
|
|
660
|
-
]) {
|
|
661
|
-
const digits = extractPhoneDigits(value);
|
|
662
|
-
if (digits && !byPhone.has(digits)) byPhone.set(digits, target);
|
|
663
|
-
}
|
|
664
|
-
for (const value of [values.id, values.lid]) {
|
|
665
|
-
const digits = extractLidDigits(value);
|
|
666
|
-
if (digits && !byLid.has(digits)) byLid.set(digits, target);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
return {
|
|
670
|
-
byPhone,
|
|
671
|
-
byLid
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
function shouldSkipMentionAt(text, index, end, codeRanges) {
|
|
675
|
-
if (isInRange(index, codeRanges)) return true;
|
|
676
|
-
const previous = index > 0 ? text[index - 1] : "";
|
|
677
|
-
const next = text[end] ?? "";
|
|
678
|
-
return Boolean(previous && /[\w@]/.test(previous) || next && /[\w@]/.test(next));
|
|
679
|
-
}
|
|
680
|
-
function resolveWhatsAppOutboundMentions(params) {
|
|
681
|
-
if (!isWhatsAppGroupJid(params.chatJid) || !mayContainWhatsAppOutboundMention(params.text) || !params.participants?.length) return {
|
|
682
|
-
text: params.text,
|
|
683
|
-
mentionedJids: []
|
|
684
|
-
};
|
|
685
|
-
const { byPhone, byLid } = buildMentionTargetMaps(params.participants);
|
|
686
|
-
if (byPhone.size === 0 && byLid.size === 0) return {
|
|
687
|
-
text: params.text,
|
|
688
|
-
mentionedJids: []
|
|
689
|
-
};
|
|
690
|
-
const codeRanges = collectCodeRanges(params.text);
|
|
691
|
-
const replacements = [];
|
|
692
|
-
const mentionedJids = [];
|
|
693
|
-
const seenMentionJids = /* @__PURE__ */ new Set();
|
|
694
|
-
for (const match of params.text.matchAll(OUTBOUND_MENTION_RE)) {
|
|
695
|
-
const start = match.index;
|
|
696
|
-
const token = match[0];
|
|
697
|
-
if (shouldSkipMentionAt(params.text, start, start + token.length, codeRanges)) continue;
|
|
698
|
-
const digits = match[1].replace(/\D/g, "");
|
|
699
|
-
const target = token.startsWith("@+") ? byPhone.get(digits) ?? byLid.get(digits) : byLid.get(digits) ?? byPhone.get(digits);
|
|
700
|
-
if (!target) continue;
|
|
701
|
-
if (!seenMentionJids.has(target.mentionJid)) {
|
|
702
|
-
seenMentionJids.add(target.mentionJid);
|
|
703
|
-
mentionedJids.push(target.mentionJid);
|
|
704
|
-
}
|
|
705
|
-
if (target.replacementText && target.replacementText !== token) replacements.push({
|
|
706
|
-
start,
|
|
707
|
-
end: start + token.length,
|
|
708
|
-
text: target.replacementText
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
if (replacements.length === 0) return {
|
|
712
|
-
text: params.text,
|
|
713
|
-
mentionedJids
|
|
714
|
-
};
|
|
715
|
-
let text = "";
|
|
716
|
-
let cursor = 0;
|
|
717
|
-
for (const replacement of replacements) {
|
|
718
|
-
text += params.text.slice(cursor, replacement.start);
|
|
719
|
-
text += replacement.text;
|
|
720
|
-
cursor = replacement.end;
|
|
721
|
-
}
|
|
722
|
-
text += params.text.slice(cursor);
|
|
723
|
-
return {
|
|
724
|
-
text,
|
|
725
|
-
mentionedJids
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
function addWhatsAppOutboundMentionsToContent(content, mentionedJids) {
|
|
729
|
-
return mentionedJids.length > 0 ? {
|
|
730
|
-
...content,
|
|
731
|
-
mentions: [...mentionedJids]
|
|
732
|
-
} : content;
|
|
733
|
-
}
|
|
734
|
-
//#endregion
|
|
735
|
-
//#region extensions/whatsapp/src/inbound/send-result.ts
|
|
736
|
-
function normalizeKey(key) {
|
|
737
|
-
const id = typeof key?.id === "string" ? key.id.trim() : "";
|
|
738
|
-
if (!id) return;
|
|
739
|
-
return {
|
|
740
|
-
id,
|
|
741
|
-
remoteJid: key?.remoteJid,
|
|
742
|
-
fromMe: key?.fromMe,
|
|
743
|
-
participant: key?.participant
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
function normalizeWhatsAppSendResult(result, kind) {
|
|
747
|
-
const key = normalizeKey(result?.key);
|
|
748
|
-
return {
|
|
749
|
-
kind,
|
|
750
|
-
messageId: key?.id ?? "unknown",
|
|
751
|
-
messageIds: key ? [key.id] : [],
|
|
752
|
-
keys: key ? [key] : [],
|
|
753
|
-
providerAccepted: Boolean(key)
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
function combineWhatsAppSendResults(kind, results) {
|
|
757
|
-
const messageIds = [...new Set(results.flatMap((result) => result.messageIds))];
|
|
758
|
-
const keys = results.flatMap((result) => result.keys);
|
|
759
|
-
return {
|
|
760
|
-
kind,
|
|
761
|
-
messageId: messageIds[0] ?? "unknown",
|
|
762
|
-
messageIds,
|
|
763
|
-
keys,
|
|
764
|
-
providerAccepted: results.some((result) => result.providerAccepted)
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
//#endregion
|
|
768
|
-
//#region extensions/whatsapp/src/inbound/send-api.ts
|
|
769
|
-
function recordWhatsAppOutbound(accountId) {
|
|
770
|
-
recordChannelActivity({
|
|
771
|
-
channel: "whatsapp",
|
|
772
|
-
accountId,
|
|
773
|
-
direction: "outbound"
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
function createWebSendApi(params) {
|
|
777
|
-
const resolveOutboundJid = (recipient) => params.authDir ? toWhatsappJidWithLid(recipient, { authDir: params.authDir }) : toWhatsappJid(recipient);
|
|
778
|
-
const resolveMentions = async (jid, text) => params.resolveOutboundMentions ? await params.resolveOutboundMentions({
|
|
779
|
-
jid,
|
|
780
|
-
text
|
|
781
|
-
}) : {
|
|
782
|
-
text,
|
|
783
|
-
mentionedJids: []
|
|
784
|
-
};
|
|
785
|
-
return {
|
|
786
|
-
sendMessage: async (to, text, mediaBuffer, mediaType, sendOptions) => {
|
|
787
|
-
const jid = resolveOutboundJid(to);
|
|
788
|
-
let payload;
|
|
789
|
-
if (mediaBuffer) mediaType ??= "application/octet-stream";
|
|
790
|
-
const shouldSendAudioText = Boolean(mediaBuffer && mediaType?.startsWith("audio/") && text.trim());
|
|
791
|
-
const resolvedPayloadText = shouldSendAudioText ? {
|
|
792
|
-
text,
|
|
793
|
-
mentionedJids: []
|
|
794
|
-
} : await resolveMentions(jid, text);
|
|
795
|
-
if (mediaBuffer && mediaType) if (mediaType.startsWith("image/")) payload = {
|
|
796
|
-
image: mediaBuffer,
|
|
797
|
-
caption: resolvedPayloadText.text || void 0,
|
|
798
|
-
mimetype: mediaType
|
|
799
|
-
};
|
|
800
|
-
else if (mediaType.startsWith("audio/")) payload = {
|
|
801
|
-
audio: mediaBuffer,
|
|
802
|
-
ptt: true,
|
|
803
|
-
mimetype: mediaType
|
|
804
|
-
};
|
|
805
|
-
else if (mediaType.startsWith("video/")) {
|
|
806
|
-
const gifPlayback = sendOptions?.gifPlayback;
|
|
807
|
-
payload = {
|
|
808
|
-
video: mediaBuffer,
|
|
809
|
-
caption: resolvedPayloadText.text || void 0,
|
|
810
|
-
mimetype: mediaType,
|
|
811
|
-
...gifPlayback ? { gifPlayback: true } : {}
|
|
812
|
-
};
|
|
813
|
-
} else payload = {
|
|
814
|
-
document: mediaBuffer,
|
|
815
|
-
fileName: sendOptions?.fileName?.trim() || "file",
|
|
816
|
-
caption: resolvedPayloadText.text || void 0,
|
|
817
|
-
mimetype: mediaType
|
|
818
|
-
};
|
|
819
|
-
else payload = { text: resolvedPayloadText.text };
|
|
820
|
-
payload = addWhatsAppOutboundMentionsToContent(payload, resolvedPayloadText.mentionedJids);
|
|
821
|
-
const quotedOpts = buildQuotedMessageOptions({
|
|
822
|
-
messageId: sendOptions?.quotedMessageKey?.id,
|
|
823
|
-
remoteJid: sendOptions?.quotedMessageKey?.remoteJid,
|
|
824
|
-
fromMe: sendOptions?.quotedMessageKey?.fromMe,
|
|
825
|
-
participant: sendOptions?.quotedMessageKey?.participant,
|
|
826
|
-
messageText: sendOptions?.quotedMessageKey?.messageText
|
|
827
|
-
});
|
|
828
|
-
const results = [normalizeWhatsAppSendResult(quotedOpts ? await params.sock.sendMessage(jid, payload, quotedOpts) : await params.sock.sendMessage(jid, payload), mediaBuffer ? "media" : "text")];
|
|
829
|
-
if (shouldSendAudioText) {
|
|
830
|
-
const resolvedAudioText = await resolveMentions(jid, text);
|
|
831
|
-
const textPayload = addWhatsAppOutboundMentionsToContent({ text: resolvedAudioText.text }, resolvedAudioText.mentionedJids);
|
|
832
|
-
const textResult = quotedOpts ? await params.sock.sendMessage(jid, textPayload, quotedOpts) : await params.sock.sendMessage(jid, textPayload);
|
|
833
|
-
results.push(normalizeWhatsAppSendResult(textResult, "text"));
|
|
834
|
-
}
|
|
835
|
-
recordWhatsAppOutbound(sendOptions?.accountId ?? params.defaultAccountId);
|
|
836
|
-
return combineWhatsAppSendResults(mediaBuffer ? "media" : "text", results);
|
|
837
|
-
},
|
|
838
|
-
sendPoll: async (to, poll) => {
|
|
839
|
-
const jid = resolveOutboundJid(to);
|
|
840
|
-
const result = await params.sock.sendMessage(jid, { poll: {
|
|
841
|
-
name: poll.question,
|
|
842
|
-
values: poll.options,
|
|
843
|
-
selectableCount: poll.maxSelections ?? 1
|
|
844
|
-
} });
|
|
845
|
-
recordWhatsAppOutbound(params.defaultAccountId);
|
|
846
|
-
return normalizeWhatsAppSendResult(result, "poll");
|
|
847
|
-
},
|
|
848
|
-
sendReaction: async (chatJid, messageId, emoji, fromMe, participant) => {
|
|
849
|
-
const jid = toWhatsappJid(chatJid);
|
|
850
|
-
return normalizeWhatsAppSendResult(await params.sock.sendMessage(jid, { react: {
|
|
851
|
-
text: emoji,
|
|
852
|
-
key: {
|
|
853
|
-
remoteJid: jid,
|
|
854
|
-
id: messageId,
|
|
855
|
-
fromMe,
|
|
856
|
-
participant: participant ? toWhatsappJid(participant) : void 0
|
|
857
|
-
}
|
|
858
|
-
} }), "reaction");
|
|
859
|
-
},
|
|
860
|
-
sendComposingTo: async (to) => {
|
|
861
|
-
const jid = resolveOutboundJid(to);
|
|
862
|
-
if (isWhatsAppNewsletterJid(jid)) return;
|
|
863
|
-
await params.sock.sendPresenceUpdate("composing", jid);
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
//#endregion
|
|
868
212
|
//#region extensions/whatsapp/src/inbound/monitor.ts
|
|
869
213
|
const LOGGED_OUT_STATUS = DisconnectReason$1?.loggedOut ?? 401;
|
|
870
214
|
const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
|
|
@@ -896,6 +240,13 @@ function logWhatsAppVerbose(enabled, message) {
|
|
|
896
240
|
function isGroupJid(jid) {
|
|
897
241
|
return (typeof isJidGroup === "function" ? isJidGroup(jid) : jid.endsWith("@g.us")) === true;
|
|
898
242
|
}
|
|
243
|
+
function recordAcceptedInboundActivity(accountId) {
|
|
244
|
+
recordChannelActivity({
|
|
245
|
+
channel: "whatsapp",
|
|
246
|
+
accountId,
|
|
247
|
+
direction: "inbound"
|
|
248
|
+
});
|
|
249
|
+
}
|
|
899
250
|
function isRetryableSendDisconnectError(err) {
|
|
900
251
|
return /closed|reset|timed\s*out|disconnect|no active socket/i.test(formatError(err));
|
|
901
252
|
}
|
|
@@ -1310,11 +661,6 @@ async function attachWebInboxToSocket(options) {
|
|
|
1310
661
|
const handleMessagesUpsert = async (upsert) => {
|
|
1311
662
|
if (upsert.type !== "notify" && upsert.type !== "append") return;
|
|
1312
663
|
for (const msg of upsert.messages ?? []) {
|
|
1313
|
-
recordChannelActivity({
|
|
1314
|
-
channel: "whatsapp",
|
|
1315
|
-
accountId: options.accountId,
|
|
1316
|
-
direction: "inbound"
|
|
1317
|
-
});
|
|
1318
664
|
const inbound = await normalizeInboundMessage(msg);
|
|
1319
665
|
if (!inbound) continue;
|
|
1320
666
|
await maybeMarkInboundAsRead(inbound);
|
|
@@ -1328,6 +674,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
1328
674
|
if (!enriched) continue;
|
|
1329
675
|
const dedupeKey = inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : "";
|
|
1330
676
|
if (dedupeKey && !await claimRecentInboundMessage(dedupeKey)) continue;
|
|
677
|
+
recordAcceptedInboundActivity(options.accountId);
|
|
1331
678
|
await enqueueInboundMessage(msg, inbound, enriched);
|
|
1332
679
|
}
|
|
1333
680
|
};
|
|
@@ -1432,7 +779,7 @@ function resolveMentionTargets(msg, authDir) {
|
|
|
1432
779
|
function isBotMentionedFromTargets(msg, mentionCfg, targets) {
|
|
1433
780
|
const clean = (text) => normalizeMentionText(text);
|
|
1434
781
|
const explicitSelfChatOverride = typeof mentionCfg.isSelfChat === "boolean";
|
|
1435
|
-
const isGroupConversation = isWhatsAppGroupJid
|
|
782
|
+
const isGroupConversation = isWhatsAppGroupJid(msg.from);
|
|
1436
783
|
const isSelfChat = explicitSelfChatOverride ? Boolean(mentionCfg.isSelfChat) : isSelfChatMode(targets.self.e164, mentionCfg.allowFrom) && !isGroupConversation;
|
|
1437
784
|
const hasMentions = targets.normalizedMentions.length > 0;
|
|
1438
785
|
if (hasMentions && !isSelfChat) {
|
|
@@ -2018,6 +1365,42 @@ function isLikelyWhatsAppCryptoError(reason) {
|
|
|
2018
1365
|
}
|
|
2019
1366
|
//#endregion
|
|
2020
1367
|
//#region extensions/whatsapp/src/auto-reply/deliver-reply.ts
|
|
1368
|
+
function resolveWhatsAppReceiptKind(results) {
|
|
1369
|
+
if (results.length > 0 && results.every((result) => result.kind === "text")) return "text";
|
|
1370
|
+
if (results.length > 0 && results.every((result) => result.kind === "media")) return "media";
|
|
1371
|
+
return "unknown";
|
|
1372
|
+
}
|
|
1373
|
+
function createWhatsAppReplyDeliveryReceipt(results) {
|
|
1374
|
+
const receiptResultsById = /* @__PURE__ */ new Map();
|
|
1375
|
+
for (const result of results) {
|
|
1376
|
+
if (result.receipt?.parts.length) {
|
|
1377
|
+
for (const part of result.receipt.parts) receiptResultsById.set(part.platformMessageId, {
|
|
1378
|
+
...part.raw ?? {
|
|
1379
|
+
channel: "whatsapp",
|
|
1380
|
+
messageId: part.platformMessageId
|
|
1381
|
+
},
|
|
1382
|
+
meta: {
|
|
1383
|
+
...part.raw?.meta,
|
|
1384
|
+
kind: result.kind,
|
|
1385
|
+
providerAccepted: result.providerAccepted
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
for (const messageId of listWhatsAppSendResultMessageIds(result)) receiptResultsById.set(messageId, {
|
|
1391
|
+
channel: "whatsapp",
|
|
1392
|
+
messageId,
|
|
1393
|
+
meta: {
|
|
1394
|
+
kind: result.kind,
|
|
1395
|
+
providerAccepted: result.providerAccepted
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
return createMessageReceiptFromOutboundResults({
|
|
1400
|
+
results: [...receiptResultsById.values()],
|
|
1401
|
+
kind: resolveWhatsAppReceiptKind(results)
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
2021
1404
|
async function deliverWebReply(params) {
|
|
2022
1405
|
const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
|
|
2023
1406
|
const replyStarted = Date.now();
|
|
@@ -2028,7 +1411,7 @@ async function deliverWebReply(params) {
|
|
|
2028
1411
|
const finishDelivery = () => {
|
|
2029
1412
|
return {
|
|
2030
1413
|
results: sendResults,
|
|
2031
|
-
|
|
1414
|
+
receipt: createWhatsAppReplyDeliveryReceipt(sendResults),
|
|
2032
1415
|
providerAccepted: sendResults.some((result) => result.providerAccepted)
|
|
2033
1416
|
};
|
|
2034
1417
|
};
|
|
@@ -2361,7 +1744,7 @@ async function dispatchWhatsAppBufferedReply(params) {
|
|
|
2361
1744
|
});
|
|
2362
1745
|
const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
|
|
2363
1746
|
const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : params.msg.chatType;
|
|
2364
|
-
const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ?
|
|
1747
|
+
const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
|
|
2365
1748
|
cfg: params.cfg,
|
|
2366
1749
|
ctx: {
|
|
2367
1750
|
ChatType: sourceReplyChatType,
|
|
@@ -2435,6 +1818,33 @@ async function dispatchWhatsAppBufferedReply(params) {
|
|
|
2435
1818
|
if (!reply.hasMedia && !reply.text.trim()) return;
|
|
2436
1819
|
if (!reply.hasMedia) {
|
|
2437
1820
|
logWhatsAppMediaOnlyFlushResult(await mediaOnlyCoalescer.flushAll());
|
|
1821
|
+
const durable = await deliverInboundReplyWithMessageSendContext({
|
|
1822
|
+
cfg: params.cfg,
|
|
1823
|
+
channel: "whatsapp",
|
|
1824
|
+
accountId: params.route.accountId,
|
|
1825
|
+
agentId: params.route.agentId,
|
|
1826
|
+
ctxPayload: params.context,
|
|
1827
|
+
payload: normalizedDeliveryPayload,
|
|
1828
|
+
info,
|
|
1829
|
+
to: params.msg.from,
|
|
1830
|
+
formatting: {
|
|
1831
|
+
textLimit,
|
|
1832
|
+
tableMode,
|
|
1833
|
+
chunkMode
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
if (durable.status === "failed") throw durable.error;
|
|
1837
|
+
if (durable.status === "handled_visible") {
|
|
1838
|
+
didSendReply = true;
|
|
1839
|
+
const shouldLog = normalizedDeliveryPayload.text ? true : void 0;
|
|
1840
|
+
params.rememberSentText(normalizedDeliveryPayload.text, {
|
|
1841
|
+
combinedBody: params.context.Body,
|
|
1842
|
+
combinedBodySessionKey: params.route.sessionKey,
|
|
1843
|
+
logVerboseMessage: shouldLog
|
|
1844
|
+
});
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
if (durable.status === "handled_no_send") return;
|
|
2438
1848
|
await deliverNormalizedPayload(normalizedDeliveryPayload, info);
|
|
2439
1849
|
return;
|
|
2440
1850
|
}
|
|
@@ -2593,7 +2003,7 @@ async function processMessage(params) {
|
|
|
2593
2003
|
let audioTranscript = params.preflightAudioTranscript ?? void 0;
|
|
2594
2004
|
const hasAudioBody = params.msg.mediaType?.startsWith("audio/") === true && params.msg.body === "<media:audio>";
|
|
2595
2005
|
if (params.preflightAudioTranscript === void 0 && hasAudioBody && params.msg.mediaPath) try {
|
|
2596
|
-
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-
|
|
2006
|
+
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-MmkB-pgX.js");
|
|
2597
2007
|
audioTranscript = await transcribeFirstAudio({
|
|
2598
2008
|
ctx: {
|
|
2599
2009
|
MediaPaths: [params.msg.mediaPath],
|
|
@@ -2699,7 +2109,7 @@ async function processMessage(params) {
|
|
|
2699
2109
|
msg: params.msg,
|
|
2700
2110
|
policy: inboundPolicy
|
|
2701
2111
|
}) : void 0;
|
|
2702
|
-
const { onModelSelected, ...replyPipeline } =
|
|
2112
|
+
const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
2703
2113
|
cfg: params.cfg,
|
|
2704
2114
|
agentId: params.route.agentId,
|
|
2705
2115
|
channel: "whatsapp",
|
|
@@ -2907,7 +2317,7 @@ function createWebOnMessageHandler(params) {
|
|
|
2907
2317
|
});
|
|
2908
2318
|
ackAlreadySent = ackReaction !== null;
|
|
2909
2319
|
try {
|
|
2910
|
-
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-
|
|
2320
|
+
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-MmkB-pgX.js");
|
|
2911
2321
|
preflightAudioTranscript = await transcribeFirstAudio({
|
|
2912
2322
|
ctx: {
|
|
2913
2323
|
MediaPaths: [msg.mediaPath],
|
|
@@ -3023,7 +2433,7 @@ function isNonRetryableWebCloseStatus(statusCode) {
|
|
|
3023
2433
|
}
|
|
3024
2434
|
let replyResolverRuntimePromise = null;
|
|
3025
2435
|
function loadReplyResolverRuntime() {
|
|
3026
|
-
replyResolverRuntimePromise ??= import("./reply-resolver.runtime-
|
|
2436
|
+
replyResolverRuntimePromise ??= import("./reply-resolver.runtime-rIVWohXr.js");
|
|
3027
2437
|
return replyResolverRuntimePromise;
|
|
3028
2438
|
}
|
|
3029
2439
|
function normalizeReconnectAccountId(accountId) {
|
|
@@ -3426,4 +2836,4 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
3426
2836
|
}
|
|
3427
2837
|
}
|
|
3428
2838
|
//#endregion
|
|
3429
|
-
export { loadWebMediaRaw as a, monitorWebInbox as c,
|
|
2839
|
+
export { loadWebMediaRaw as a, monitorWebInbox as c, loadWebMedia$1 as i, resetWebInboundDedupe as l, LocalMediaAccessError as n, optimizeImageToJpeg as o, getDefaultLocalRoots as r, optimizeImageToPng as s, monitorWebChannel as t };
|