@openclaw/whatsapp 2026.7.2-beta.4 → 2026.7.2-beta.5
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/README.md +1 -1
- package/dist/{access-control-CRz_v6bR.js → access-control-pZcxTmd5.js} +4 -4
- package/dist/{accounts-CxPuEFi-.js → accounts-CSqLvCY5.js} +0 -1
- package/dist/{action-runtime-CHU7xb6o.js → action-runtime-B2bNDUwE.js} +3 -3
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/api.js +13 -19
- package/dist/{approval-handler.runtime-C4ddB7Kc.js → approval-handler.runtime-CmtqgLSQ.js} +5 -5
- package/dist/{approval-reactions-P2HXjmln.js → approval-reactions-Cu66aYdq.js} +3 -3
- package/dist/{auth-store-DirYMTbo.js → auth-store-DcEpIOmE.js} +2 -2
- package/dist/call-tool-api.js +2 -2
- package/dist/{channel-B9xjrOGG.js → channel-DcWBc3Hb.js} +23 -60
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-react-action-CBW-PA8q.js → channel-react-action-DUIkjl6w.js} +5 -5
- package/dist/{channel.runtime-DdKuhlNu.js → channel.runtime-Q5fJ2I2b.js} +4 -4
- package/dist/{channel.setup-DXUCPHel.js → channel.setup-C_1u6HN6.js} +1 -1
- package/dist/{config-schema-C6IeKIC0.js → config-schema-DARep0Nn.js} +1 -5
- package/dist/{connection-controller-BVRExqrB.js → connection-controller-BOB2MVKY.js} +3 -3
- package/dist/contract-api.js +2 -2
- package/dist/{directory-config-BTbJkV1W.js → directory-config-DalGrfGN.js} +5 -5
- package/dist/directory-contract-api.js +1 -1
- package/dist/{group-session-key-DJSdYfnb.js → group-session-key-DVMZlO3x.js} +5 -5
- package/dist/{identity-BM4_wZeM.js → identity-CPrT5aEU.js} +2 -2
- package/dist/light-runtime-api.js +1 -1
- package/dist/{login-BKUm7KSk.js → login-CvY2Dhch.js} +4 -4
- package/dist/{login-qr-DagJvMbg.js → login-qr-Bncpb6js.js} +4 -4
- package/dist/login-qr-runtime.js +1 -1
- package/dist/{monitor-CBVNJ2lb.js → monitor-B7phZF9w.js} +258 -330
- package/dist/normalize-C7Hkc_vE.js +2 -0
- package/dist/{normalize-target-DSZnHkKF.js → normalize-target-CugRyONv.js} +6 -2
- package/dist/{question-reactions-CaOrSYzU.js → question-reactions-CRsnyoMK.js} +1 -1
- package/dist/{resolve-outbound-target-BR97fAbB.js → resolve-outbound-target-1GqbT1uX.js} +1 -1
- package/dist/runtime-api.js +7 -7
- package/dist/{send-DhJVN30g.js → send-BTXJAyK4.js} +5 -5
- package/dist/{send-api-CpHwbor_.js → send-api-BY_AkSiB.js} +7 -7
- package/dist/{setup-finalize-9XzKdFq6.js → setup-finalize-C-m-vCl9.js} +4 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-Kv3mPki2.js → setup-surface-BjvUbR6M.js} +3 -3
- package/dist/{shared-CuqHFnKd.js → shared-UcAwtJaq.js} +6 -6
- package/dist/{socket-close-BoSHhQNJ.js → socket-close-DridEuzz.js} +1 -1
- package/dist/{targets-runtime-CKFrhoX4.js → targets-runtime-TMk0lGuy.js} +4 -13
- package/dist/{text-runtime-CU_IlbK5.js → text-runtime-CaKLhowd.js} +1 -1
- package/openclaw.plugin.json +2 -18
- package/package.json +4 -5
- package/dist/normalize-C-Z16y7Q.js +0 -2
- package/npm-shrinkwrap.json +0 -954
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-
|
|
2
|
-
import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation, u as resolveWhatsAppReactionLevel } from "./send-
|
|
1
|
+
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CSqLvCY5.js";
|
|
2
|
+
import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation, u as resolveWhatsAppReactionLevel } from "./send-BTXJAyK4.js";
|
|
3
3
|
import { i as getWhatsAppRuntime } from "./runtime-BLlToOi6.js";
|
|
4
4
|
import { r as getWhatsAppConnectionController } from "./connection-controller-runtime-context-DG0tuYuw.js";
|
|
5
|
-
import { a as markdownToWhatsAppChunks, c as toWhatsappJid, l as toWhatsappJidWithLid, n as isSelfChatMode, o as resolveEquivalentWhatsAppDirectChatJids, r as jidToE164, s as resolveJidToE164 } from "./targets-runtime-
|
|
6
|
-
import { t as normalizeE164 } from "./text-runtime-
|
|
7
|
-
import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-
|
|
8
|
-
import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-
|
|
9
|
-
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-
|
|
5
|
+
import { a as markdownToWhatsAppChunks, c as toWhatsappJid, l as toWhatsappJidWithLid, n as isSelfChatMode, o as resolveEquivalentWhatsAppDirectChatJids, r as jidToE164, s as resolveJidToE164 } from "./targets-runtime-TMk0lGuy.js";
|
|
6
|
+
import { t as normalizeE164 } from "./text-runtime-CaKLhowd.js";
|
|
7
|
+
import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-DVMZlO3x.js";
|
|
8
|
+
import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-DcEpIOmE.js";
|
|
9
|
+
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-CPrT5aEU.js";
|
|
10
10
|
import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
|
|
11
|
-
import { a as waitForWaConnection, c as isWhatsAppSocketOperationTimeoutError, d as withWhatsAppSocketOperationTimeout, l as resolveWhatsAppSocketOperationTimeoutMs, r as createWaSocket, s as createWhatsAppSocketOperationTimeoutAdapter, u as resolveWhatsAppSocketTiming } from "./socket-close-
|
|
11
|
+
import { a as waitForWaConnection, c as isWhatsAppSocketOperationTimeoutError, d as withWhatsAppSocketOperationTimeout, l as resolveWhatsAppSocketOperationTimeoutMs, r as createWaSocket, s as createWhatsAppSocketOperationTimeoutAdapter, u as resolveWhatsAppSocketTiming } from "./socket-close-DridEuzz.js";
|
|
12
12
|
import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
|
|
13
13
|
import { t as BufferJSON } from "./session.runtime-Nsqaehbd.js";
|
|
14
|
-
import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-
|
|
15
|
-
import { _ as extractMediaKind, a as mayContainWhatsAppOutboundMention, b as hasInboundUserContent, c as DisconnectReason, d as normalizeMessageContent, f as describeReplyContext, g as extractLocationData, h as extractExternalAdReplyContext, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContextInfo, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as extractContactContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMentionedJids, x as resolveInboundMediaMimetype, y as extractText } from "./send-api-
|
|
16
|
-
import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-
|
|
17
|
-
import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-
|
|
18
|
-
import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-
|
|
19
|
-
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
|
|
14
|
+
import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-pZcxTmd5.js";
|
|
15
|
+
import { _ as extractMediaKind, a as mayContainWhatsAppOutboundMention, b as hasInboundUserContent, c as DisconnectReason, d as normalizeMessageContent, f as describeReplyContext, g as extractLocationData, h as extractExternalAdReplyContext, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContextInfo, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as extractContactContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMentionedJids, x as resolveInboundMediaMimetype, y as extractText } from "./send-api-BY_AkSiB.js";
|
|
16
|
+
import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-BOB2MVKY.js";
|
|
17
|
+
import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-Cu66aYdq.js";
|
|
18
|
+
import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-CRsnyoMK.js";
|
|
20
19
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
21
20
|
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
22
21
|
import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
|
|
@@ -25,15 +24,16 @@ import { getChildLogger } from "openclaw/plugin-sdk/logging-core";
|
|
|
25
24
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
26
25
|
import { createSubsystemLogger, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
27
26
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
28
|
-
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults,
|
|
27
|
+
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
29
28
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
30
29
|
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
31
30
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
32
31
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
33
|
-
import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
34
|
-
import { DEFAULT_ACCOUNT_ID
|
|
32
|
+
import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, resolveInboundSessionEnvelopeContext, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
33
|
+
import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
35
34
|
import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
36
35
|
import { createHash } from "node:crypto";
|
|
36
|
+
import { fanInChannelIngressLifecycles } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
37
37
|
import { getSessionEntry, patchSessionEntry, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
38
38
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
39
39
|
import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
@@ -44,7 +44,7 @@ import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInbou
|
|
|
44
44
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
45
45
|
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
46
46
|
import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
47
|
-
import { getRuntimeConfig as getRuntimeConfig$1
|
|
47
|
+
import { getRuntimeConfig as getRuntimeConfig$1 } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
48
48
|
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
49
49
|
import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
|
|
50
50
|
import { createConnectedChannelStatusPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
@@ -127,6 +127,18 @@ function defineDeprecatedAliasAccessors(msg, descriptors) {
|
|
|
127
127
|
defineDeprecatedAccessors(msg, descriptors);
|
|
128
128
|
return msg;
|
|
129
129
|
}
|
|
130
|
+
function defineDeprecatedStructuredContextPayloadAlias(msg) {
|
|
131
|
+
const channelStructuredContext = msg.payload.channelStructuredContext ?? msg.payload.untrustedStructuredContext;
|
|
132
|
+
if (channelStructuredContext !== void 0) msg.payload.channelStructuredContext = channelStructuredContext;
|
|
133
|
+
Object.defineProperty(msg.payload, "untrustedStructuredContext", {
|
|
134
|
+
configurable: true,
|
|
135
|
+
enumerable: true,
|
|
136
|
+
get: () => msg.payload.channelStructuredContext,
|
|
137
|
+
set: (value) => {
|
|
138
|
+
msg.payload.channelStructuredContext = value;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
130
142
|
function defineDeprecatedAdmissionTopLevelAccessors(msg) {
|
|
131
143
|
let fallbackConversationId = msg.conversationId || msg.from;
|
|
132
144
|
let fallbackAccountId = msg.accountId;
|
|
@@ -178,6 +190,7 @@ function defineDeprecatedAdmissionTopLevelAccessors(msg) {
|
|
|
178
190
|
});
|
|
179
191
|
}
|
|
180
192
|
function withDeprecatedWebInboundMessageFlatAliases(msg) {
|
|
193
|
+
defineDeprecatedStructuredContextPayloadAlias(msg);
|
|
181
194
|
return defineDeprecatedAliasAccessors(defineDeprecatedAdmissionTopLevelAccessors(msg), {
|
|
182
195
|
id: {
|
|
183
196
|
get: () => msg.event.id,
|
|
@@ -323,9 +336,13 @@ function withDeprecatedWebInboundMessageFlatAliases(msg) {
|
|
|
323
336
|
get: () => msg.payload.media?.url,
|
|
324
337
|
set: (value) => setMediaField(msg, "url", value)
|
|
325
338
|
},
|
|
339
|
+
channelStructuredContext: {
|
|
340
|
+
get: () => msg.payload.channelStructuredContext,
|
|
341
|
+
set: (value) => msg.payload.channelStructuredContext = value
|
|
342
|
+
},
|
|
326
343
|
untrustedStructuredContext: {
|
|
327
|
-
get: () => msg.payload.
|
|
328
|
-
set: (value) => msg.payload.
|
|
344
|
+
get: () => msg.payload.channelStructuredContext,
|
|
345
|
+
set: (value) => msg.payload.channelStructuredContext = value
|
|
329
346
|
},
|
|
330
347
|
isBatched: {
|
|
331
348
|
get: () => msg.event.isBatched,
|
|
@@ -352,7 +369,7 @@ function normalizeLegacyFlatWebInboundMessage(msg) {
|
|
|
352
369
|
body: msg.body,
|
|
353
370
|
media,
|
|
354
371
|
location: msg.location,
|
|
355
|
-
|
|
372
|
+
channelStructuredContext: msg.channelStructuredContext ?? msg.untrustedStructuredContext
|
|
356
373
|
},
|
|
357
374
|
platform: {
|
|
358
375
|
chatJid: msg.chatId,
|
|
@@ -467,22 +484,6 @@ function createWhatsAppDurableInboundQueue(accountId) {
|
|
|
467
484
|
stateDir: getWhatsAppRuntime().state.resolveStateDir()
|
|
468
485
|
});
|
|
469
486
|
}
|
|
470
|
-
/** Raw receive chokepoint: append first, then let the drain normalize and dispatch. */
|
|
471
|
-
async function enqueueWhatsAppDurableInbound(params) {
|
|
472
|
-
const facts = inspectWhatsAppIngressMessage(params.message);
|
|
473
|
-
const receivedAt = params.receivedAt ?? Date.now();
|
|
474
|
-
return await params.queue.enqueue(facts.eventId, {
|
|
475
|
-
message: serializeWhatsAppDurableInboundMessage(params.message),
|
|
476
|
-
upsertType: params.upsertType,
|
|
477
|
-
...params.skipStaleAppend === void 0 ? {} : { skipStaleAppend: params.skipStaleAppend },
|
|
478
|
-
...params.skipRecentOutboundEcho === void 0 ? {} : { skipRecentOutboundEcho: params.skipRecentOutboundEcho },
|
|
479
|
-
receivedAt,
|
|
480
|
-
...params.receiveOrder === void 0 ? {} : { receiveOrder: params.receiveOrder }
|
|
481
|
-
}, {
|
|
482
|
-
receivedAt,
|
|
483
|
-
laneKey: facts.laneKey
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
487
|
function resolveWhatsAppIngressNonRetryableFailure(error) {
|
|
487
488
|
return error instanceof WhatsAppIngressPermanentError ? {
|
|
488
489
|
reason: error.reason,
|
|
@@ -493,14 +494,18 @@ function resolveWhatsAppIngressNonRetryableFailure(error) {
|
|
|
493
494
|
function createWhatsAppIngressMonitor(params) {
|
|
494
495
|
return createChannelIngressMonitor({
|
|
495
496
|
queue: params.queue,
|
|
496
|
-
inspect: (
|
|
497
|
+
inspect: (admission) => inspectWhatsAppIngressMessage(admission.message),
|
|
497
498
|
payload: {
|
|
498
499
|
version: WHATSAPP_DURABLE_INBOUND_PAYLOAD_VERSION,
|
|
499
|
-
serialize: (
|
|
500
|
-
|
|
500
|
+
serialize: (admission, { receivedAt }) => ({
|
|
501
|
+
...admission,
|
|
502
|
+
message: serializeWhatsAppDurableInboundMessage(admission.message),
|
|
501
503
|
receivedAt
|
|
502
504
|
}),
|
|
503
|
-
deserialize: (payload) =>
|
|
505
|
+
deserialize: (payload) => ({
|
|
506
|
+
...payload,
|
|
507
|
+
message: deserializeWhatsAppDurableInboundMessage(payload.message)
|
|
508
|
+
}),
|
|
504
509
|
encode: ({ body }) => body,
|
|
505
510
|
decode: (payload) => ({
|
|
506
511
|
version: WHATSAPP_DURABLE_INBOUND_PAYLOAD_VERSION,
|
|
@@ -508,7 +513,7 @@ function createWhatsAppIngressMonitor(params) {
|
|
|
508
513
|
}),
|
|
509
514
|
createClaimError: (kind) => new WhatsAppIngressPermanentError(kind === "invalid-version" ? "invalid-payload" : "event-id-mismatch", kind === "invalid-version" ? "WhatsApp ingress row has an invalid payload version" : "WhatsApp ingress row identity does not match its transport message key")
|
|
510
515
|
},
|
|
511
|
-
deliver: (
|
|
516
|
+
deliver: (admission, lifecycle) => params.dispatch(admission, lifecycle),
|
|
512
517
|
pollIntervalMs: params.pollIntervalMs,
|
|
513
518
|
retention: {
|
|
514
519
|
pruneIntervalMs: WHATSAPP_DURABLE_INBOUND_PRUNE_INTERVAL_MS,
|
|
@@ -791,48 +796,6 @@ async function attachWebInboxToSocket(options) {
|
|
|
791
796
|
if (timestampDiff !== 0) return timestampDiff;
|
|
792
797
|
return (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
|
|
793
798
|
});
|
|
794
|
-
const buildFlushIngressLifecycle = (entries) => {
|
|
795
|
-
const lifecycles = entries.map((entry) => entry.turnAdoptionLifecycle).filter((lifecycle) => lifecycle !== void 0);
|
|
796
|
-
const [firstLifecycle] = lifecycles;
|
|
797
|
-
if (!firstLifecycle) return {
|
|
798
|
-
lifecycle: void 0,
|
|
799
|
-
settle: async () => {},
|
|
800
|
-
abandon: async () => {}
|
|
801
|
-
};
|
|
802
|
-
let handedOff = false;
|
|
803
|
-
const adoptAll = async () => {
|
|
804
|
-
for (const lifecycle of lifecycles) await lifecycle.onAdopted();
|
|
805
|
-
};
|
|
806
|
-
return {
|
|
807
|
-
lifecycle: {
|
|
808
|
-
abortSignal: lifecycles.length === 1 ? firstLifecycle.abortSignal : AbortSignal.any(lifecycles.map((lifecycle) => lifecycle.abortSignal)),
|
|
809
|
-
onAdopted: async () => {
|
|
810
|
-
handedOff = true;
|
|
811
|
-
await adoptAll();
|
|
812
|
-
},
|
|
813
|
-
onDeferred: () => {
|
|
814
|
-
handedOff = true;
|
|
815
|
-
for (const lifecycle of lifecycles) lifecycle.onDeferred();
|
|
816
|
-
},
|
|
817
|
-
onAdoptionFinalizing: () => {
|
|
818
|
-
for (const lifecycle of lifecycles) lifecycle.onAdoptionFinalizing();
|
|
819
|
-
},
|
|
820
|
-
onAbandoned: async () => {
|
|
821
|
-
handedOff = true;
|
|
822
|
-
await Promise.all(lifecycles.map((lifecycle) => Promise.resolve(lifecycle.onAbandoned())));
|
|
823
|
-
}
|
|
824
|
-
},
|
|
825
|
-
settle: async () => {
|
|
826
|
-
if (!handedOff) await adoptAll();
|
|
827
|
-
},
|
|
828
|
-
abandon: async () => {
|
|
829
|
-
if (!handedOff) {
|
|
830
|
-
handedOff = true;
|
|
831
|
-
await Promise.all(lifecycles.map((lifecycle) => Promise.resolve(lifecycle.onAbandoned())));
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
};
|
|
836
799
|
const debouncer = createInboundDebouncer({
|
|
837
800
|
debounceMs: inboundDebounceMs,
|
|
838
801
|
buildKey: (msg) => msg.debounceKey ?? buildInboundDebounceKey(msg),
|
|
@@ -849,7 +812,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
849
812
|
const orderedEntries = orderDebouncedInboundEntries(entries);
|
|
850
813
|
const last = orderedEntries.at(-1);
|
|
851
814
|
if (!last) return;
|
|
852
|
-
const { lifecycle, settle, abandon } =
|
|
815
|
+
const { lifecycle, settle, abandon } = fanInChannelIngressLifecycles(orderedEntries.map((entry) => entry.turnAdoptionLifecycle));
|
|
853
816
|
try {
|
|
854
817
|
if (orderedEntries.length === 1) {
|
|
855
818
|
await options.onMessage(attachWhatsAppIngressLifecycle(last, lifecycle));
|
|
@@ -1308,7 +1271,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
1308
1271
|
participants: inbound.groupParticipants,
|
|
1309
1272
|
mentions: groupMentions
|
|
1310
1273
|
} : void 0;
|
|
1311
|
-
const
|
|
1274
|
+
const channelStructuredContext = [
|
|
1312
1275
|
...enriched.nativeMedia ? [{
|
|
1313
1276
|
label: "WhatsApp media",
|
|
1314
1277
|
source: "whatsapp",
|
|
@@ -1338,7 +1301,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
1338
1301
|
body: enriched.body,
|
|
1339
1302
|
commandBody: enriched.commandBody,
|
|
1340
1303
|
location: enriched.location ?? void 0,
|
|
1341
|
-
|
|
1304
|
+
channelStructuredContext: channelStructuredContext.length > 0 ? channelStructuredContext : void 0,
|
|
1342
1305
|
media
|
|
1343
1306
|
},
|
|
1344
1307
|
platform: {
|
|
@@ -1399,11 +1362,15 @@ async function attachWebInboxToSocket(options) {
|
|
|
1399
1362
|
}
|
|
1400
1363
|
await debouncer.enqueue(inboundMessage);
|
|
1401
1364
|
};
|
|
1402
|
-
const processDurableInboundMessage = async (
|
|
1365
|
+
const processDurableInboundMessage = async (admission, lifecycle) => {
|
|
1366
|
+
const { message: msg, ...context } = admission;
|
|
1403
1367
|
rememberBaileysMessage(msg.key?.remoteJid, msg.key?.id, msg.message);
|
|
1368
|
+
const remoteJid = msg.key?.remoteJid;
|
|
1369
|
+
const id = msg.key?.id;
|
|
1370
|
+
const durableId = remoteJid && id ? createHash("sha256").update(`${remoteJid}\n${id}`).digest("hex") : void 0;
|
|
1371
|
+
const preparation = durableId ? preparedInboundByDurableId.get(durableId) : void 0;
|
|
1372
|
+
if (durableId) preparedInboundByDurableId.delete(durableId);
|
|
1404
1373
|
if (context.skipRecentOutboundEcho === true) return "completed";
|
|
1405
|
-
const preparation = context.eventId ? preparedInboundByDurableId.get(context.eventId) : void 0;
|
|
1406
|
-
if (context.eventId) preparedInboundByDurableId.delete(context.eventId);
|
|
1407
1374
|
const prepared = await preparation;
|
|
1408
1375
|
if (prepared === null) return "completed";
|
|
1409
1376
|
const inbound = prepared ?? await normalizeInboundMessage(msg);
|
|
@@ -1431,24 +1398,13 @@ async function attachWebInboxToSocket(options) {
|
|
|
1431
1398
|
await enqueueInboundMessage(msg, inbound, enriched, {
|
|
1432
1399
|
readReceipt: deliveryReadReceipt,
|
|
1433
1400
|
receiveOrder: context.receiveOrder ?? context.receivedAt,
|
|
1434
|
-
turnAdoptionLifecycle:
|
|
1401
|
+
turnAdoptionLifecycle: lifecycle
|
|
1435
1402
|
});
|
|
1436
1403
|
return "deferred";
|
|
1437
1404
|
};
|
|
1438
1405
|
const durableInboundMonitor = createWhatsAppIngressMonitor({
|
|
1439
1406
|
queue: durableInboundQueue,
|
|
1440
|
-
dispatch: async (
|
|
1441
|
-
const remoteJid = msg.key?.remoteJid;
|
|
1442
|
-
const id = msg.key?.id;
|
|
1443
|
-
return { kind: await processDurableInboundMessage(msg, {
|
|
1444
|
-
...payload,
|
|
1445
|
-
...remoteJid && id ? { eventId: createWhatsAppDurableInboundMessageId({
|
|
1446
|
-
remoteJid,
|
|
1447
|
-
id
|
|
1448
|
-
}) } : {},
|
|
1449
|
-
lifecycle
|
|
1450
|
-
}) };
|
|
1451
|
-
},
|
|
1407
|
+
dispatch: async (admission, lifecycle) => ({ kind: await processDurableInboundMessage(admission, lifecycle) }),
|
|
1452
1408
|
pollIntervalMs: WHATSAPP_INGRESS_DRAIN_INTERVAL_MS,
|
|
1453
1409
|
onLog: (message) => inboundLogger.warn({ message }, "whatsapp ingress drain"),
|
|
1454
1410
|
onError: (error) => inboundLogger.error({ error: formatError(error) }, "whatsapp durable inbound drain failed"),
|
|
@@ -1477,10 +1433,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
1477
1433
|
const skipRecentOutboundEcho = shouldSkipRecentOutboundEcho(msg);
|
|
1478
1434
|
const remoteJid = msg.key?.remoteJid;
|
|
1479
1435
|
const id = msg.key?.id;
|
|
1480
|
-
const durableId = remoteJid && id ?
|
|
1481
|
-
remoteJid,
|
|
1482
|
-
id
|
|
1483
|
-
}) : void 0;
|
|
1436
|
+
const durableId = remoteJid && id ? createHash("sha256").update(`${remoteJid}\n${id}`).digest("hex") : void 0;
|
|
1484
1437
|
let resolvePrepared;
|
|
1485
1438
|
if (durableId && !preparedInboundByDurableId.has(durableId)) {
|
|
1486
1439
|
if (preparedInboundByDurableId.size >= 1e3) {
|
|
@@ -1496,52 +1449,34 @@ async function attachWebInboxToSocket(options) {
|
|
|
1496
1449
|
if (!keepForDrain && durableId && resolvePrepared) preparedInboundByDurableId.delete(durableId);
|
|
1497
1450
|
};
|
|
1498
1451
|
let result;
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
});
|
|
1508
|
-
|
|
1509
|
-
result = await enqueueWhatsAppDurableInbound({
|
|
1510
|
-
queue: durableInboundQueue,
|
|
1511
|
-
message: msg,
|
|
1512
|
-
upsertType: upsert.type,
|
|
1513
|
-
skipStaleAppend,
|
|
1514
|
-
skipRecentOutboundEcho,
|
|
1515
|
-
receivedAt,
|
|
1516
|
-
receiveOrder
|
|
1517
|
-
});
|
|
1518
|
-
appendError = void 0;
|
|
1519
|
-
break;
|
|
1520
|
-
} catch (error) {
|
|
1521
|
-
appendError = error;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
if (result === void 0) {
|
|
1452
|
+
try {
|
|
1453
|
+
result = await durableInboundMonitor.admit({
|
|
1454
|
+
message: msg,
|
|
1455
|
+
upsertType: upsert.type,
|
|
1456
|
+
skipStaleAppend,
|
|
1457
|
+
skipRecentOutboundEcho,
|
|
1458
|
+
receivedAt,
|
|
1459
|
+
receiveOrder
|
|
1460
|
+
}, { receivedAt });
|
|
1461
|
+
} catch (error) {
|
|
1525
1462
|
finishPreparation(void 0);
|
|
1526
|
-
const formattedError = formatError(
|
|
1463
|
+
const formattedError = formatError(error);
|
|
1527
1464
|
inboundLogger.error({ error: formattedError }, "failed persisting durable WhatsApp inbound after retries; message dropped");
|
|
1528
1465
|
inboundConsoleLog.error(`Failed persisting durable WhatsApp inbound after retries; message dropped: ${formattedError}`);
|
|
1529
1466
|
continue;
|
|
1530
1467
|
}
|
|
1531
|
-
if (result.kind === "completed") {
|
|
1468
|
+
if (result.kind === "durable" && result.queueResult.kind === "completed") {
|
|
1532
1469
|
finishPreparation(void 0);
|
|
1533
1470
|
const inbound = await normalizeInboundMessage(msg);
|
|
1534
1471
|
if (inbound) await maybeMarkNonSelfChatReadReceipt(inbound, buildReadReceiptTarget(inbound));
|
|
1535
|
-
} else
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
}
|
|
1542
|
-
else finishPreparation(void 0);
|
|
1543
|
-
durableInboundMonitor.requestDrain();
|
|
1472
|
+
} else if (result.kind === "durable" && result.queueResult.kind === "accepted") if (skipRecentOutboundEcho) finishPreparation(null);
|
|
1473
|
+
else try {
|
|
1474
|
+
finishPreparation(await normalizeInboundMessage(msg), true);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
finishPreparation(void 0);
|
|
1477
|
+
inboundLogger.warn({ error: formatError(error) }, "failed preparing WhatsApp inbound identity; durable drain will normalize again");
|
|
1544
1478
|
}
|
|
1479
|
+
else finishPreparation(void 0);
|
|
1545
1480
|
}
|
|
1546
1481
|
};
|
|
1547
1482
|
const handleMessagesUpsertEvent = (upsert) => {
|
|
@@ -1963,7 +1898,7 @@ function resolveAgentIdentityEmoji(cfg, agentId) {
|
|
|
1963
1898
|
//#endregion
|
|
1964
1899
|
//#region extensions/whatsapp/src/auto-reply/monitor/group-activation.ts
|
|
1965
1900
|
function hasNamedWhatsAppAccounts(cfg) {
|
|
1966
|
-
return Object.keys(cfg.channels?.whatsapp?.accounts ?? {}).some((accountId) => normalizeAccountId
|
|
1901
|
+
return Object.keys(cfg.channels?.whatsapp?.accounts ?? {}).some((accountId) => normalizeAccountId(accountId) !== DEFAULT_ACCOUNT_ID);
|
|
1967
1902
|
}
|
|
1968
1903
|
function isActivationOnlyEntry(entry) {
|
|
1969
1904
|
return entry?.groupActivation !== void 0 && typeof entry?.sessionId !== "string" && typeof entry?.updatedAt !== "number";
|
|
@@ -1986,7 +1921,7 @@ async function resolveGroupActivationFor(params) {
|
|
|
1986
1921
|
...sessionScope,
|
|
1987
1922
|
sessionKey: params.sessionKey
|
|
1988
1923
|
});
|
|
1989
|
-
const activation = (normalizeAccountId
|
|
1924
|
+
const activation = (normalizeAccountId(params.accountId) === DEFAULT_ACCOUNT_ID && hasNamedWhatsAppAccounts(params.cfg) && isActivationOnlyEntry(scopedEntry) ? void 0 : scopedEntry?.groupActivation) ?? legacyEntry?.groupActivation;
|
|
1990
1925
|
if (activation !== void 0 && scopedEntry?.groupActivation === void 0) {
|
|
1991
1926
|
if (scopedEntry) await patchSessionEntry({
|
|
1992
1927
|
...sessionScope,
|
|
@@ -2014,35 +1949,35 @@ function resolveReactionParticipant(msg) {
|
|
|
2014
1949
|
return sender.jid ?? sender.lid ?? void 0;
|
|
2015
1950
|
}
|
|
2016
1951
|
//#endregion
|
|
2017
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/
|
|
2018
|
-
|
|
2019
|
-
|
|
1952
|
+
//#region extensions/whatsapp/src/auto-reply/monitor/reaction-eligibility.ts
|
|
1953
|
+
const DISABLED_REACTION = { status: "disabled" };
|
|
1954
|
+
async function resolveWhatsAppReactionEligibility(params) {
|
|
1955
|
+
const messageId = params.msg.event.id;
|
|
1956
|
+
if (!messageId) return DISABLED_REACTION;
|
|
2020
1957
|
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
2021
1958
|
const accountId = admission.accountId;
|
|
2022
1959
|
if (resolveWhatsAppReactionLevel({
|
|
2023
1960
|
cfg: params.cfg,
|
|
2024
1961
|
accountId
|
|
2025
|
-
}).level === "off") return
|
|
2026
|
-
const ackConfig = params.cfg.messages?.ackReaction;
|
|
1962
|
+
}).level === "off") return DISABLED_REACTION;
|
|
2027
1963
|
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
|
2028
|
-
if (scope === "off" || scope === "none") return
|
|
1964
|
+
if (scope === "off" || scope === "none") return DISABLED_REACTION;
|
|
2029
1965
|
const emoji = resolveWhatsAppAckEmoji({
|
|
2030
1966
|
cfg: params.cfg,
|
|
2031
1967
|
agentId: params.agentId,
|
|
2032
|
-
ackConfig
|
|
1968
|
+
ackConfig: params.cfg.messages?.ackReaction
|
|
2033
1969
|
});
|
|
2034
1970
|
const directEnabled = scope === "all" || scope === "direct";
|
|
2035
1971
|
const groupMode = scope === "all" || scope === "group-all" ? "always" : scope === "direct" ? "never" : "mentions";
|
|
2036
1972
|
const isGroup = admission.conversation.kind === "group";
|
|
2037
|
-
const conversationIdForCheck = admission.conversation.id;
|
|
2038
1973
|
const activation = isGroup ? await resolveGroupActivationFor({
|
|
2039
1974
|
cfg: params.cfg,
|
|
2040
1975
|
accountId,
|
|
2041
1976
|
agentId: params.agentId,
|
|
2042
1977
|
sessionKey: params.sessionKey,
|
|
2043
|
-
conversationId:
|
|
1978
|
+
conversationId: admission.conversation.id
|
|
2044
1979
|
}) : null;
|
|
2045
|
-
|
|
1980
|
+
if (!shouldAckReactionForWhatsApp({
|
|
2046
1981
|
emoji,
|
|
2047
1982
|
isDirect: admission.conversation.kind === "direct",
|
|
2048
1983
|
isGroup,
|
|
@@ -2050,32 +1985,50 @@ async function maybeSendAckReaction(params) {
|
|
|
2050
1985
|
groupMode,
|
|
2051
1986
|
wasMentioned: (params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned) === true,
|
|
2052
1987
|
groupActivated: activation === "always"
|
|
1988
|
+
})) return DISABLED_REACTION;
|
|
1989
|
+
const participant = resolveReactionParticipant(params.msg);
|
|
1990
|
+
return {
|
|
1991
|
+
status: "prepared",
|
|
1992
|
+
chatId: params.msg.platform.chatJid,
|
|
1993
|
+
messageId,
|
|
1994
|
+
emoji,
|
|
1995
|
+
reactionOptions: {
|
|
1996
|
+
verbose: params.verbose,
|
|
1997
|
+
fromMe: false,
|
|
1998
|
+
...participant ? { participant } : {},
|
|
1999
|
+
accountId,
|
|
2000
|
+
cfg: params.cfg
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
//#endregion
|
|
2005
|
+
//#region extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts
|
|
2006
|
+
async function maybeSendAckReaction(params) {
|
|
2007
|
+
const eligibility = await resolveWhatsAppReactionEligibility({
|
|
2008
|
+
cfg: params.cfg,
|
|
2009
|
+
msg: params.msg,
|
|
2010
|
+
agentId: params.agentId,
|
|
2011
|
+
sessionKey: params.sessionKey,
|
|
2012
|
+
verbose: params.verbose
|
|
2053
2013
|
});
|
|
2054
|
-
if (
|
|
2014
|
+
if (eligibility.status === "disabled") return null;
|
|
2015
|
+
const { chatId, messageId, emoji, reactionOptions } = eligibility;
|
|
2055
2016
|
params.info({
|
|
2056
|
-
chatId
|
|
2057
|
-
messageId
|
|
2017
|
+
chatId,
|
|
2018
|
+
messageId,
|
|
2058
2019
|
emoji
|
|
2059
2020
|
}, "sending ack reaction");
|
|
2060
|
-
const participant = resolveReactionParticipant(params.msg);
|
|
2061
|
-
const reactionOptions = {
|
|
2062
|
-
verbose: params.verbose,
|
|
2063
|
-
fromMe: false,
|
|
2064
|
-
...participant ? { participant } : {},
|
|
2065
|
-
accountId,
|
|
2066
|
-
cfg: params.cfg
|
|
2067
|
-
};
|
|
2068
2021
|
return createAckReactionHandle({
|
|
2069
2022
|
ackReactionValue: emoji,
|
|
2070
|
-
send: () => sendReactionWhatsApp(
|
|
2071
|
-
remove: () => sendReactionWhatsApp(
|
|
2023
|
+
send: () => sendReactionWhatsApp(chatId, messageId, emoji, reactionOptions),
|
|
2024
|
+
remove: () => sendReactionWhatsApp(chatId, messageId, "", reactionOptions),
|
|
2072
2025
|
onSendError: (err) => {
|
|
2073
2026
|
params.warn({
|
|
2074
2027
|
error: formatError(err),
|
|
2075
|
-
chatId
|
|
2076
|
-
messageId
|
|
2028
|
+
chatId,
|
|
2029
|
+
messageId
|
|
2077
2030
|
}, "failed to send ack reaction");
|
|
2078
|
-
logVerbose(`WhatsApp ack reaction failed for chat ${
|
|
2031
|
+
logVerbose(`WhatsApp ack reaction failed for chat ${chatId}: ${formatError(err)}`);
|
|
2079
2032
|
}
|
|
2080
2033
|
});
|
|
2081
2034
|
}
|
|
@@ -2775,14 +2728,16 @@ function normalizeErrForLog(err) {
|
|
|
2775
2728
|
function whatsAppReplyDeliveryVisibility(visibleReplySent) {
|
|
2776
2729
|
return { visibleReplySent };
|
|
2777
2730
|
}
|
|
2778
|
-
function
|
|
2779
|
-
return
|
|
2731
|
+
function isWhatsAppVisibleDeliveryError(error) {
|
|
2732
|
+
return typeof error === "object" && error !== null && !Array.isArray(error) && error.visibleReplySent === true || isChannelPartialDeliveryError(error) && error.deliveryResult.visibleReplySent;
|
|
2780
2733
|
}
|
|
2781
2734
|
function readTrimmedString(value) {
|
|
2782
2735
|
return typeof value === "string" ? value.trim() : "";
|
|
2783
2736
|
}
|
|
2784
2737
|
function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
|
|
2785
|
-
|
|
2738
|
+
if (flushResult.delivered === 0) return error;
|
|
2739
|
+
if (isWhatsAppVisibleDeliveryError(error)) return error;
|
|
2740
|
+
return markWhatsAppVisibleDeliveryError(new Error("deferred WhatsApp media delivery failed after an earlier visible send", { cause: error }));
|
|
2786
2741
|
}
|
|
2787
2742
|
function logWhatsAppReplyDeliveryError(params) {
|
|
2788
2743
|
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
@@ -2834,31 +2789,58 @@ function shouldDeferWhatsAppMediaOnlyPayload(params) {
|
|
|
2834
2789
|
}
|
|
2835
2790
|
function createWhatsAppMediaOnlyReplyCoalescer(params) {
|
|
2836
2791
|
const pendingMediaOnlyPayloads = [];
|
|
2837
|
-
const
|
|
2792
|
+
const flushWhere = async (shouldFlush) => {
|
|
2838
2793
|
const flushResult = {
|
|
2839
2794
|
delivered: 0,
|
|
2840
2795
|
droppedDuplicateMedia: 0
|
|
2841
2796
|
};
|
|
2842
|
-
const
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2797
|
+
const candidates = [];
|
|
2798
|
+
const retained = [];
|
|
2799
|
+
for (const pending of pendingMediaOnlyPayloads.splice(0)) if (shouldFlush(pending)) candidates.push(pending);
|
|
2800
|
+
else retained.push(pending);
|
|
2801
|
+
pendingMediaOnlyPayloads.push(...retained);
|
|
2802
|
+
for (const [index, candidate] of candidates.entries()) try {
|
|
2803
|
+
const delivery = await params.deliver(candidate);
|
|
2804
|
+
candidate.resolveFinalization(delivery);
|
|
2805
|
+
if (delivery.visibleReplySent) flushResult.delivered += 1;
|
|
2806
|
+
} catch (error) {
|
|
2807
|
+
const visibleError = markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
|
|
2808
|
+
candidate.rejectFinalization(error);
|
|
2809
|
+
for (const remaining of candidates.slice(index + 1)) remaining.rejectFinalization(new Error("deferred WhatsApp media delivery was not attempted", { cause: error }));
|
|
2810
|
+
throw visibleError;
|
|
2853
2811
|
}
|
|
2854
2812
|
return flushResult;
|
|
2855
2813
|
};
|
|
2856
2814
|
return {
|
|
2857
2815
|
defer(pending) {
|
|
2858
|
-
|
|
2816
|
+
let resolveFinalization;
|
|
2817
|
+
let rejectFinalization;
|
|
2818
|
+
const finalization = new Promise((resolve, reject) => {
|
|
2819
|
+
resolveFinalization = resolve;
|
|
2820
|
+
rejectFinalization = reject;
|
|
2821
|
+
});
|
|
2822
|
+
pendingMediaOnlyPayloads.push({
|
|
2823
|
+
...pending,
|
|
2824
|
+
resolveFinalization,
|
|
2825
|
+
rejectFinalization
|
|
2826
|
+
});
|
|
2827
|
+
return finalization;
|
|
2859
2828
|
},
|
|
2860
|
-
|
|
2861
|
-
|
|
2829
|
+
flushNonDuplicateMedia: (mediaUrls) => flushWhere((pending) => !hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)),
|
|
2830
|
+
dropDuplicateMedia(mediaUrls) {
|
|
2831
|
+
const flushResult = {
|
|
2832
|
+
delivered: 0,
|
|
2833
|
+
droppedDuplicateMedia: 0
|
|
2834
|
+
};
|
|
2835
|
+
const retained = [];
|
|
2836
|
+
for (const pending of pendingMediaOnlyPayloads.splice(0)) if (hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)) {
|
|
2837
|
+
pending.resolveFinalization(whatsAppReplyDeliveryVisibility(false));
|
|
2838
|
+
flushResult.droppedDuplicateMedia += 1;
|
|
2839
|
+
} else retained.push(pending);
|
|
2840
|
+
pendingMediaOnlyPayloads.push(...retained);
|
|
2841
|
+
return flushResult;
|
|
2842
|
+
},
|
|
2843
|
+
flushAll: () => flushWhere(() => true)
|
|
2862
2844
|
};
|
|
2863
2845
|
}
|
|
2864
2846
|
function logWhatsAppMediaOnlyFlushResult(result) {
|
|
@@ -2900,7 +2882,7 @@ async function buildWhatsAppInboundContext(params) {
|
|
|
2900
2882
|
sender: params.visibleReplyTo.sender?.label ?? void 0
|
|
2901
2883
|
} : void 0,
|
|
2902
2884
|
groupSystemPrompt: params.groupSystemPrompt,
|
|
2903
|
-
|
|
2885
|
+
channelStructuredContext: params.msg.payload.channelStructuredContext
|
|
2904
2886
|
},
|
|
2905
2887
|
media,
|
|
2906
2888
|
messageId: params.msg.event.id,
|
|
@@ -3009,7 +2991,20 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3009
2991
|
const disableBlockStreaming = sourceRepliesAreToolOnly ? true : resolveWhatsAppDisableBlockStreaming(params.cfg);
|
|
3010
2992
|
let didSendReply = false;
|
|
3011
2993
|
let didLogHeartbeatStrip = false;
|
|
3012
|
-
const
|
|
2994
|
+
const recordDeliveredPayload = (payload) => {
|
|
2995
|
+
didSendReply = true;
|
|
2996
|
+
const shouldLog = payload.text ? true : void 0;
|
|
2997
|
+
params.rememberSentText(payload.text, {
|
|
2998
|
+
combinedBody: params.context.Body,
|
|
2999
|
+
combinedBodySessionKey: params.route.sessionKey,
|
|
3000
|
+
logVerboseMessage: shouldLog
|
|
3001
|
+
});
|
|
3002
|
+
if (shouldLogVerbose$1()) {
|
|
3003
|
+
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3004
|
+
logVerbose$1(`Reply body: ${payload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
|
|
3005
|
+
}
|
|
3006
|
+
};
|
|
3007
|
+
const deliverNormalizedPayload = async (normalizedDeliveryPayload, info, options) => {
|
|
3013
3008
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3014
3009
|
if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
|
|
3015
3010
|
if (!(await params.deliverReply({
|
|
@@ -3036,15 +3031,11 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3036
3031
|
}, "auto-reply was not accepted by WhatsApp provider");
|
|
3037
3032
|
return whatsAppReplyDeliveryVisibility(false);
|
|
3038
3033
|
}
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
logVerboseMessage: shouldLog
|
|
3045
|
-
});
|
|
3046
|
-
const fromDisplay = conversationId;
|
|
3047
|
-
if (shouldLogVerbose$1()) logVerbose$1(`Reply body: ${normalizedDeliveryPayload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${fromDisplay}`);
|
|
3034
|
+
if (options?.recordDelivery !== false) try {
|
|
3035
|
+
recordDeliveredPayload(normalizedDeliveryPayload);
|
|
3036
|
+
} catch (error) {
|
|
3037
|
+
throw markWhatsAppVisibleDeliveryError(error);
|
|
3038
|
+
}
|
|
3048
3039
|
return whatsAppReplyDeliveryVisibility(true);
|
|
3049
3040
|
};
|
|
3050
3041
|
const mediaOnlyCoalescer = createWhatsAppMediaOnlyReplyCoalescer({ deliver: async (pending) => {
|
|
@@ -3065,90 +3056,82 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3065
3056
|
onSettled: async () => {
|
|
3066
3057
|
const flushResult = await mediaOnlyCoalescer.flushAll();
|
|
3067
3058
|
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
3068
|
-
return whatsAppReplyDeliveryVisibility(flushResult.delivered > 0);
|
|
3059
|
+
return whatsAppReplyDeliveryVisibility(didSendReply || flushResult.delivered > 0);
|
|
3069
3060
|
},
|
|
3070
3061
|
onReplyStart: params.msg.platform.sendComposing
|
|
3071
3062
|
},
|
|
3072
3063
|
delivery: {
|
|
3073
|
-
|
|
3064
|
+
preparePayload: async (payload, info) => {
|
|
3074
3065
|
const deliveryPayload = resolveWhatsAppDeliverablePayload(payload, info);
|
|
3075
|
-
if (!deliveryPayload) return
|
|
3066
|
+
if (!deliveryPayload) return null;
|
|
3076
3067
|
const normalizedOutboundPayload = normalizeWhatsAppOutboundPayload(deliveryPayload, { normalizeText: normalizeWhatsAppPayloadTextPreservingIndentation });
|
|
3077
3068
|
const normalizedDeliveryPayload = deliveryPayload.text === void 0 ? {
|
|
3078
3069
|
...normalizedOutboundPayload,
|
|
3079
3070
|
text: void 0
|
|
3080
3071
|
} : normalizedOutboundPayload;
|
|
3081
3072
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3082
|
-
if (!reply.hasMedia && !reply.text.trim()) return
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
if (durable.sentBeforeError === true) throw markWhatsAppVisibleDeliveryError(durable.error);
|
|
3110
|
-
throw durable.error;
|
|
3111
|
-
}
|
|
3112
|
-
if (durable.status === "handled_visible") {
|
|
3113
|
-
didSendReply = true;
|
|
3114
|
-
const shouldLog = normalizedDeliveryPayload.text ? true : void 0;
|
|
3115
|
-
params.rememberSentText(normalizedDeliveryPayload.text, {
|
|
3116
|
-
combinedBody: params.context.Body,
|
|
3117
|
-
combinedBodySessionKey: params.route.sessionKey,
|
|
3118
|
-
logVerboseMessage: shouldLog
|
|
3119
|
-
});
|
|
3120
|
-
return whatsAppReplyDeliveryVisibilityFromDurableResult(durable.delivery);
|
|
3121
|
-
}
|
|
3122
|
-
if (durable.status === "handled_no_send") return flushResult.delivered > 0 ? whatsAppReplyDeliveryVisibility(true) : whatsAppReplyDeliveryVisibilityFromDurableResult(durable.delivery);
|
|
3123
|
-
const deliveryResult = await deliverNormalizedPayload(normalizedDeliveryPayload, info);
|
|
3124
|
-
return flushResult.delivered > 0 && !deliveryResult.visibleReplySent ? whatsAppReplyDeliveryVisibility(true) : deliveryResult;
|
|
3125
|
-
} catch (error) {
|
|
3126
|
-
throw markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
|
|
3073
|
+
if (!reply.hasMedia && !reply.text.trim()) return normalizedDeliveryPayload;
|
|
3074
|
+
const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
|
|
3075
|
+
logWhatsAppMediaOnlyFlushResult(reply.hasMedia ? shouldDeferWhatsAppMediaOnlyPayload({
|
|
3076
|
+
info,
|
|
3077
|
+
mediaUrls,
|
|
3078
|
+
reply
|
|
3079
|
+
}) ? {
|
|
3080
|
+
delivered: 0,
|
|
3081
|
+
droppedDuplicateMedia: 0
|
|
3082
|
+
} : await mediaOnlyCoalescer.flushNonDuplicateMedia(mediaUrls) : await mediaOnlyCoalescer.flushAll());
|
|
3083
|
+
return normalizedDeliveryPayload;
|
|
3084
|
+
},
|
|
3085
|
+
durable: (payload, info) => {
|
|
3086
|
+
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3087
|
+
if (reply.hasMedia || !reply.text.trim()) return false;
|
|
3088
|
+
return {
|
|
3089
|
+
to: conversationId,
|
|
3090
|
+
replyToId: resolveWhatsAppDurableReplyToId({
|
|
3091
|
+
context: params.context,
|
|
3092
|
+
info,
|
|
3093
|
+
msg: params.msg,
|
|
3094
|
+
payload
|
|
3095
|
+
}),
|
|
3096
|
+
formatting: {
|
|
3097
|
+
textLimit,
|
|
3098
|
+
tableMode,
|
|
3099
|
+
chunkMode
|
|
3127
3100
|
}
|
|
3128
|
-
}
|
|
3101
|
+
};
|
|
3102
|
+
},
|
|
3103
|
+
deliver: async (payload, info) => {
|
|
3104
|
+
const normalizedDeliveryPayload = payload;
|
|
3105
|
+
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3106
|
+
if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
|
|
3107
|
+
if (!reply.hasMedia) return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { recordDelivery: false });
|
|
3129
3108
|
const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
|
|
3130
3109
|
if (shouldDeferWhatsAppMediaOnlyPayload({
|
|
3131
3110
|
info,
|
|
3132
3111
|
mediaUrls,
|
|
3133
3112
|
reply
|
|
3134
|
-
})) {
|
|
3135
|
-
|
|
3113
|
+
})) return {
|
|
3114
|
+
visibleReplySent: false,
|
|
3115
|
+
finalization: mediaOnlyCoalescer.defer({
|
|
3136
3116
|
info,
|
|
3137
3117
|
mediaUrls,
|
|
3138
3118
|
payload: normalizedDeliveryPayload
|
|
3139
|
-
})
|
|
3140
|
-
|
|
3141
|
-
}
|
|
3142
|
-
const flushResult = await mediaOnlyCoalescer.flushExceptDuplicateMedia(mediaUrls);
|
|
3143
|
-
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
3119
|
+
})
|
|
3120
|
+
};
|
|
3144
3121
|
try {
|
|
3145
|
-
const
|
|
3146
|
-
|
|
3122
|
+
const result = await deliverNormalizedPayload(normalizedDeliveryPayload, info);
|
|
3123
|
+
if (result.visibleReplySent) logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.dropDuplicateMedia(mediaUrls));
|
|
3124
|
+
return result;
|
|
3147
3125
|
} catch (error) {
|
|
3148
|
-
|
|
3126
|
+
if (isWhatsAppVisibleDeliveryError(error)) logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.dropDuplicateMedia(mediaUrls));
|
|
3127
|
+
throw error;
|
|
3149
3128
|
}
|
|
3150
3129
|
},
|
|
3130
|
+
onDelivered: (payload, _info, result) => {
|
|
3131
|
+
if (!resolveSendableOutboundReplyParts(payload).hasMedia && result?.visibleReplySent === true) recordDeliveredPayload(payload);
|
|
3132
|
+
},
|
|
3151
3133
|
onError: (err, info) => {
|
|
3134
|
+
if (didSendReply) markWhatsAppVisibleDeliveryError(err);
|
|
3152
3135
|
logWhatsAppReplyDeliveryError({
|
|
3153
3136
|
err,
|
|
3154
3137
|
info,
|
|
@@ -3182,7 +3165,7 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3182
3165
|
finalize: (dispatchResult) => {
|
|
3183
3166
|
const didQueueVisibleReply = hasVisibleInboundReplyDispatch(dispatchResult);
|
|
3184
3167
|
const didDeliverVisibleReply = didSendReply || dispatchResult.observedReplyDelivery === true;
|
|
3185
|
-
if (!didQueueVisibleReply) {
|
|
3168
|
+
if (!didQueueVisibleReply && !didDeliverVisibleReply) {
|
|
3186
3169
|
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3187
3170
|
controller: statusReactionController,
|
|
3188
3171
|
outcome: "error"
|
|
@@ -3264,65 +3247,29 @@ function buildInboundLine(params) {
|
|
|
3264
3247
|
async function createWhatsAppStatusReactionController(params) {
|
|
3265
3248
|
if (!params.msg.event.id) return null;
|
|
3266
3249
|
if ((params.cfg.messages?.statusReactions)?.enabled !== true) return null;
|
|
3267
|
-
const
|
|
3268
|
-
const accountId = admission.accountId;
|
|
3269
|
-
if (resolveWhatsAppReactionLevel({
|
|
3270
|
-
cfg: params.cfg,
|
|
3271
|
-
accountId
|
|
3272
|
-
}).level === "off") return null;
|
|
3273
|
-
const ackConfig = params.cfg.messages?.ackReaction;
|
|
3274
|
-
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
|
3275
|
-
if (scope === "off" || scope === "none") return null;
|
|
3276
|
-
const ackEmoji = resolveWhatsAppAckEmoji({
|
|
3277
|
-
cfg: params.cfg,
|
|
3278
|
-
agentId: params.agentId,
|
|
3279
|
-
ackConfig
|
|
3280
|
-
});
|
|
3281
|
-
if (!ackEmoji) return null;
|
|
3282
|
-
const directEnabled = scope === "all" || scope === "direct";
|
|
3283
|
-
const groupMode = scope === "all" || scope === "group-all" ? "always" : scope === "direct" ? "never" : "mentions";
|
|
3284
|
-
const isGroup = admission.conversation.kind === "group";
|
|
3285
|
-
const conversationIdForCheck = admission.conversation.id;
|
|
3286
|
-
const activation = isGroup ? await resolveGroupActivationFor({
|
|
3250
|
+
const eligibility = await resolveWhatsAppReactionEligibility({
|
|
3287
3251
|
cfg: params.cfg,
|
|
3288
|
-
|
|
3252
|
+
msg: params.msg,
|
|
3289
3253
|
agentId: params.agentId,
|
|
3290
3254
|
sessionKey: params.sessionKey,
|
|
3291
|
-
|
|
3292
|
-
})
|
|
3293
|
-
if (
|
|
3294
|
-
|
|
3295
|
-
isDirect: admission.conversation.kind === "direct",
|
|
3296
|
-
isGroup,
|
|
3297
|
-
directEnabled,
|
|
3298
|
-
groupMode,
|
|
3299
|
-
wasMentioned: (params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned) === true,
|
|
3300
|
-
groupActivated: activation === "always"
|
|
3301
|
-
})) return null;
|
|
3302
|
-
const participant = resolveReactionParticipant(params.msg);
|
|
3303
|
-
const reactionOptions = {
|
|
3304
|
-
verbose: params.verbose,
|
|
3305
|
-
fromMe: false,
|
|
3306
|
-
...participant ? { participant } : {},
|
|
3307
|
-
accountId,
|
|
3308
|
-
cfg: params.cfg
|
|
3309
|
-
};
|
|
3310
|
-
const chatId = params.msg.platform.chatJid;
|
|
3311
|
-
const msgId = params.msg.event.id;
|
|
3255
|
+
verbose: params.verbose
|
|
3256
|
+
});
|
|
3257
|
+
if (eligibility.status === "disabled") return null;
|
|
3258
|
+
const { chatId, messageId, emoji: initialEmoji, reactionOptions } = eligibility;
|
|
3312
3259
|
return createStatusReactionController({
|
|
3313
3260
|
enabled: true,
|
|
3314
3261
|
adapter: {
|
|
3315
3262
|
setReaction: async (emoji) => {
|
|
3316
|
-
await sendReactionWhatsApp(chatId,
|
|
3263
|
+
await sendReactionWhatsApp(chatId, messageId, emoji, reactionOptions);
|
|
3317
3264
|
},
|
|
3318
3265
|
clearReaction: async () => {
|
|
3319
|
-
await sendReactionWhatsApp(chatId,
|
|
3266
|
+
await sendReactionWhatsApp(chatId, messageId, "", reactionOptions);
|
|
3320
3267
|
}
|
|
3321
3268
|
},
|
|
3322
|
-
initialEmoji
|
|
3269
|
+
initialEmoji,
|
|
3323
3270
|
emojis: void 0,
|
|
3324
3271
|
onError: (err) => {
|
|
3325
|
-
logVerbose(`WhatsApp status-reaction error for chat ${chatId}/${
|
|
3272
|
+
logVerbose(`WhatsApp status-reaction error for chat ${chatId}/${messageId}: ${String(err)}`);
|
|
3326
3273
|
}
|
|
3327
3274
|
});
|
|
3328
3275
|
}
|
|
@@ -3998,23 +3945,11 @@ function resolveWebMonitorConfigSnapshot(params) {
|
|
|
3998
3945
|
account
|
|
3999
3946
|
};
|
|
4000
3947
|
}
|
|
4001
|
-
function normalizeReconnectAccountId(accountId) {
|
|
4002
|
-
return (accountId ?? "").trim() || "default";
|
|
4003
|
-
}
|
|
4004
3948
|
function isNoListenerReconnectError(lastError) {
|
|
4005
3949
|
return typeof lastError === "string" && /No active WhatsApp Web listener/i.test(lastError);
|
|
4006
3950
|
}
|
|
4007
|
-
function
|
|
4008
|
-
|
|
4009
|
-
if (!channel) return;
|
|
4010
|
-
const accountId = normalizeReconnectAccountId(params.accountId);
|
|
4011
|
-
const accountDebounce = resolveAccountEntry(channel.accounts, accountId)?.debounceMs;
|
|
4012
|
-
if (accountDebounce !== void 0) return accountDebounce;
|
|
4013
|
-
if (accountId !== "default") {
|
|
4014
|
-
const defaultAccountDebounce = resolveAccountEntry(channel.accounts, "default")?.debounceMs;
|
|
4015
|
-
if (defaultAccountDebounce !== void 0) return defaultAccountDebounce;
|
|
4016
|
-
}
|
|
4017
|
-
return channel.debounceMs;
|
|
3951
|
+
function normalizeReconnectAccountId(accountId) {
|
|
3952
|
+
return (accountId ?? "").trim() || "default";
|
|
4018
3953
|
}
|
|
4019
3954
|
function isRetryableAuthUnstableError(error) {
|
|
4020
3955
|
return error instanceof WhatsAppAuthUnstableError || typeof error === "object" && error !== null && "code" in error && error.code === "whatsapp-auth-unstable";
|
|
@@ -4036,10 +3971,8 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4036
3971
|
module: "web-reconnect",
|
|
4037
3972
|
runId
|
|
4038
3973
|
});
|
|
4039
|
-
const baseCfg = getRuntimeConfig$1();
|
|
4040
|
-
const sourceCfg = getRuntimeConfigSourceSnapshot();
|
|
4041
3974
|
const { cfg, account } = resolveWebMonitorConfigSnapshot({
|
|
4042
|
-
cfg:
|
|
3975
|
+
cfg: getRuntimeConfig$1(),
|
|
4043
3976
|
accountId: tuning.accountId
|
|
4044
3977
|
});
|
|
4045
3978
|
const loadCurrentMonitorConfig = () => resolveWebMonitorConfigSnapshot({
|
|
@@ -4097,12 +4030,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4097
4030
|
const connectionId = newConnectionId();
|
|
4098
4031
|
const inboundDebounceMs = resolveInboundDebounceMs({
|
|
4099
4032
|
cfg,
|
|
4100
|
-
channel: "whatsapp"
|
|
4101
|
-
overrideMs: resolveExplicitWhatsAppDebounceOverride({
|
|
4102
|
-
cfg,
|
|
4103
|
-
sourceCfg,
|
|
4104
|
-
accountId: account.accountId
|
|
4105
|
-
})
|
|
4033
|
+
channel: "whatsapp"
|
|
4106
4034
|
});
|
|
4107
4035
|
const shouldDebounce = (msg) => {
|
|
4108
4036
|
const normalized = normalizeWebInboundMessage(msg);
|