@openclaw/whatsapp 2026.7.2-beta.7 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accounts-CSqLvCY5.js → accounts-BX2fZUyR.js} +1 -1
- package/dist/{action-runtime-BzfDFPbH.js → action-runtime-B4e3XFOY.js} +2 -2
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/{agent-tools-login-DE9MU98T.js → agent-tools-login-BjRpD-JQ.js} +5 -4
- package/dist/api.js +69 -127
- package/dist/{approval-handler.runtime-DrA59QSA.js → approval-handler.runtime-fgGZhALv.js} +4 -4
- package/dist/{approval-reactions-Cu66aYdq.js → approval-reactions-CmtJCxe_.js} +25 -73
- package/dist/auth-presence.js +1 -1
- package/dist/{auth-store-DcEpIOmE.js → auth-store-B_DQIdzp.js} +79 -12
- package/dist/call-tool-api.js +1 -1
- package/dist/{channel-Dg62olhy.js → channel-DJVQHz_V.js} +56 -160
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-react-action-J_YtyMGX.js → channel-react-action-d6OacTNj.js} +3 -3
- package/dist/{channel.runtime-BP0xNmLi.js → channel.runtime-RxuNGZ7w.js} +4 -4
- package/dist/channel.setup-Cmvd967u.js +14 -0
- package/dist/{connection-controller-BOB2MVKY.js → connection-controller-CUyZgGpf.js} +2 -3
- package/dist/contract-api.js +2 -3
- package/dist/{creds-files-B1kSWtBg.js → creds-files-W6B3EMZ8.js} +17 -1
- package/dist/{directory-config-DalGrfGN.js → directory-config-Pc4f07wB.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +37 -2
- package/dist/{group-session-key-DVMZlO3x.js → group-session-key-DmFc_Cqi.js} +23 -4
- package/dist/light-runtime-api.js +2 -2
- package/dist/{login-CvY2Dhch.js → login-D6_rfxTZ.js} +4 -4
- package/dist/{login-qr-Bncpb6js.js → login-qr-B0VyVSqQ.js} +4 -4
- package/dist/login-qr-runtime.js +1 -1
- package/dist/{monitor-CfkaUSL2.js → monitor-BCe5Mm-1.js} +574 -163
- package/dist/{question-reactions-CRsnyoMK.js → question-reactions-D3eAeB0i.js} +29 -78
- package/dist/runtime-api.js +9 -9
- package/dist/runtime-group-policy-DeSRFXEV.js +11 -0
- package/dist/{send-api-7wk6eQyW.js → send-api-DimZST3E.js} +86 -107
- package/dist/{send-D37ddk22.js → send-prK8F7Vj.js} +277 -16
- package/dist/setup-entry.js +1 -8
- package/dist/{setup-finalize-BZucx24W.js → setup-finalize-D3n71Uyx.js} +4 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-BkVcd769.js → setup-surface-BCZszf3l.js} +3 -3
- package/dist/{shared-CAxd15bp.js → shared-Dp9M3ZPX.js} +5 -6
- package/dist/{socket-close-DridEuzz.js → socket-close-09FojDO2.js} +4 -10
- package/openclaw.plugin.json +4 -0
- package/package.json +4 -5
- package/skills/wacli/SKILL.md +8 -1
- package/dist/access-control-pZcxTmd5.js +0 -363
- package/dist/approval-resolver-DoeBBt22.js +0 -16
- package/dist/channel.setup-pMwNVkCA.js +0 -19
- package/dist/identity-CPrT5aEU.js +0 -76
- package/dist/legacy-state-migrations-api.js +0 -2
- package/dist/state-migrations-CAMTrs2U.js +0 -33
|
@@ -1,53 +1,58 @@
|
|
|
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
|
|
1
|
+
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-BX2fZUyR.js";
|
|
2
|
+
import { _ as resolveWhatsAppReactionLevel, a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, d as mergeWhatsAppAcceptedSendError, f as normalizeWhatsAppSendResult, h as withWhatsAppLogicalDeliveryActivity, m as rememberWhatsAppPartialSend, p as rememberWhatsAppAcceptedSend, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation, u as listWhatsAppSendResultMessageIds } from "./send-prK8F7Vj.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
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
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-
|
|
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-CPrT5aEU.js";
|
|
7
|
+
import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-DmFc_Cqi.js";
|
|
8
|
+
import { C as getMentionIdentities, D as getSenderIdentity, E as getSelfIdentity, O as identitiesOverlap, S as getComparableIdentityValues, T as getReplyContext, g as readWebSelfId, k as resolveComparableIdentity, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision, w as getPrimaryIdentityId } from "./auth-store-B_DQIdzp.js";
|
|
10
9
|
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-
|
|
10
|
+
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-09FojDO2.js";
|
|
12
11
|
import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
|
|
13
12
|
import { t as BufferJSON } from "./session.runtime-Nsqaehbd.js";
|
|
14
|
-
import { c as
|
|
15
|
-
import {
|
|
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 { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
|
+
import { a as addWhatsAppImagePreviewFields, c as extractContextInfo, d as extractMediaKind, f as extractMentionedJids, g as resolveInboundMediaMimetype, h as hasInboundUserContent, i as resolveWhatsAppOutboundMentions, l as extractExternalAdReplyContext, n as addWhatsAppOutboundMentionsToContent, o as describeReplyContext, p as extractText, r as mayContainWhatsAppOutboundMention, s as extractContactContext, t as createWebSendApi, u as extractLocationData } from "./send-api-DimZST3E.js";
|
|
14
|
+
import { t as resolveWhatsAppRuntimeGroupPolicy } from "./runtime-group-policy-DeSRFXEV.js";
|
|
15
|
+
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-CUyZgGpf.js";
|
|
16
|
+
import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-CmtJCxe_.js";
|
|
17
|
+
import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-D3eAeB0i.js";
|
|
18
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
20
19
|
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
21
|
-
import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply,
|
|
20
|
+
import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
|
|
22
21
|
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
|
23
|
-
import {
|
|
22
|
+
import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
|
|
23
|
+
import { createSubsystemLogger, getChildLogger, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
24
24
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
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";
|
|
25
|
+
import { createSubsystemLogger as createSubsystemLogger$1, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
26
26
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
27
|
+
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
28
|
+
import { buildChannelInboundEventContext, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, formatMediaPlaceholderText, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, readAgentRunTerminalOutcome, resolveInboundSessionEnvelopeContext, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
27
29
|
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
28
30
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
29
31
|
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
30
32
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
31
33
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
32
|
-
import {
|
|
34
|
+
import { DisconnectReason, downloadMediaMessage, isHostedLidUser, isHostedPnUser, isJidGroup, isJidGroup as isJidGroup$1, jidDecode, jidEncode, jidNormalizedUser, normalizeMessageContent as normalizeMessageContent$1 } from "baileys";
|
|
35
|
+
import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
33
36
|
import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
34
37
|
import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
38
|
+
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
35
39
|
import { createHash } from "node:crypto";
|
|
36
|
-
import {
|
|
37
|
-
import { getSessionEntry, patchSessionEntry, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
38
|
-
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
40
|
+
import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
39
41
|
import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
40
42
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
41
43
|
import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
|
|
42
44
|
import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
|
|
43
45
|
import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
44
46
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
47
|
+
import { fanInChannelIngressLifecycles, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
48
|
+
import { getSessionEntry, patchSessionEntry, resolveGroupSessionKey, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
45
49
|
import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
50
|
+
import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
46
51
|
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
47
52
|
import { getRuntimeConfig as getRuntimeConfig$1 } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
48
53
|
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
49
54
|
import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
|
|
50
|
-
import {
|
|
55
|
+
import { channelReadyPatch, channelStoppedPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
51
56
|
import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute } from "openclaw/plugin-sdk/conversation-binding-runtime";
|
|
52
57
|
import { resolveAgentIdentity, resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
53
58
|
import { normalizeGroupActivation, parseActivationCommand } from "openclaw/plugin-sdk/group-activation";
|
|
@@ -57,6 +62,241 @@ import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
|
57
62
|
import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
|
|
58
63
|
import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
|
|
59
64
|
import { createChannelApiRetryRunner } from "openclaw/plugin-sdk/retry-runtime";
|
|
65
|
+
//#region extensions/whatsapp/src/inbound/group-conversation.ts
|
|
66
|
+
function resolveWhatsAppGroupConversationId(conversationId) {
|
|
67
|
+
return resolveGroupSessionKey({
|
|
68
|
+
From: conversationId,
|
|
69
|
+
ChatType: "group",
|
|
70
|
+
Provider: "whatsapp"
|
|
71
|
+
})?.id ?? conversationId;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region extensions/whatsapp/src/inbound/admission.ts
|
|
75
|
+
const ingressResolverByAdmission = /* @__PURE__ */ new WeakMap();
|
|
76
|
+
function copyAccount(account) {
|
|
77
|
+
const copied = {
|
|
78
|
+
accountId: account.accountId,
|
|
79
|
+
enabled: account.enabled,
|
|
80
|
+
sendReadReceipts: account.sendReadReceipts
|
|
81
|
+
};
|
|
82
|
+
if (account.name) copied.name = account.name;
|
|
83
|
+
if (typeof account.selfChatMode === "boolean") copied.selfChatMode = account.selfChatMode;
|
|
84
|
+
if (account.replyToMode) copied.replyToMode = account.replyToMode;
|
|
85
|
+
return copied;
|
|
86
|
+
}
|
|
87
|
+
function buildWhatsAppInboundAdmission(params) {
|
|
88
|
+
const admission = {
|
|
89
|
+
channelIngress: params.channelIngress,
|
|
90
|
+
accountId: params.policy.account.accountId,
|
|
91
|
+
isSelfChat: params.policy.isSelfChat,
|
|
92
|
+
account: copyAccount(params.policy.account),
|
|
93
|
+
conversation: {
|
|
94
|
+
kind: params.isGroup ? "group" : "direct",
|
|
95
|
+
id: params.conversationId,
|
|
96
|
+
groupSessionId: resolveWhatsAppGroupConversationId(params.conversationId)
|
|
97
|
+
},
|
|
98
|
+
sender: {
|
|
99
|
+
id: params.senderId,
|
|
100
|
+
isSamePhone: params.policy.isSamePhone(params.senderId)
|
|
101
|
+
},
|
|
102
|
+
ingress: {
|
|
103
|
+
admission: params.access.ingress.admission,
|
|
104
|
+
decision: params.access.ingress.decision,
|
|
105
|
+
decisiveGateId: params.access.ingress.decisiveGateId,
|
|
106
|
+
reasonCode: params.access.ingress.reasonCode
|
|
107
|
+
},
|
|
108
|
+
senderAccess: {
|
|
109
|
+
allowed: params.access.senderAccess.allowed,
|
|
110
|
+
decision: params.access.senderAccess.decision,
|
|
111
|
+
reasonCode: params.access.senderAccess.reasonCode,
|
|
112
|
+
providerMissingFallbackApplied: params.access.senderAccess.providerMissingFallbackApplied
|
|
113
|
+
},
|
|
114
|
+
commandAccess: {
|
|
115
|
+
requested: params.access.commandAccess.requested,
|
|
116
|
+
authorized: params.access.commandAccess.authorized,
|
|
117
|
+
shouldBlockControlCommand: params.access.commandAccess.shouldBlockControlCommand,
|
|
118
|
+
reasonCode: params.access.commandAccess.reasonCode
|
|
119
|
+
},
|
|
120
|
+
activationAccess: {
|
|
121
|
+
ran: params.access.activationAccess.ran,
|
|
122
|
+
allowed: params.access.activationAccess.allowed,
|
|
123
|
+
shouldSkip: params.access.activationAccess.shouldSkip,
|
|
124
|
+
reasonCode: params.access.activationAccess.reasonCode
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
if (params.resolveChannelIngress) ingressResolverByAdmission.set(admission, params.resolveChannelIngress);
|
|
128
|
+
return admission;
|
|
129
|
+
}
|
|
130
|
+
async function resolveWhatsAppAdmissionChannelIngress(admission, contextBinding) {
|
|
131
|
+
return await ingressResolverByAdmission.get(admission)?.(contextBinding);
|
|
132
|
+
}
|
|
133
|
+
function buildDeprecatedFlatWhatsAppInboundAdmission(msg) {
|
|
134
|
+
const conversationId = msg.conversationId || msg.from;
|
|
135
|
+
if (!conversationId || !msg.accountId || !msg.chatType) throw new Error("WhatsApp legacy flat inbound messages must include deprecated top-level admission fields.");
|
|
136
|
+
const accountId = msg.accountId;
|
|
137
|
+
const admitted = msg.accessControlPassed !== false;
|
|
138
|
+
const platformSender = msg.platform?.sender;
|
|
139
|
+
const senderE164 = platformSender?.e164 ?? msg.platform?.senderE164 ?? msg.senderE164;
|
|
140
|
+
const senderJid = platformSender?.jid ?? msg.platform?.senderJid ?? msg.senderJid;
|
|
141
|
+
const senderName = platformSender?.name ?? msg.platform?.senderName ?? msg.senderName;
|
|
142
|
+
const senderId = msg.chatType === "group" ? senderE164 ?? senderJid ?? senderName ?? conversationId : senderE164 ?? conversationId;
|
|
143
|
+
const reasonCode = admitted ? msg.chatType === "group" ? "group_policy_allowed" : "dm_policy_allowlisted" : "no_policy_match";
|
|
144
|
+
return buildWhatsAppInboundAdmission({
|
|
145
|
+
policy: {
|
|
146
|
+
account: {
|
|
147
|
+
accountId,
|
|
148
|
+
enabled: true,
|
|
149
|
+
sendReadReceipts: true
|
|
150
|
+
},
|
|
151
|
+
isSelfChat: false,
|
|
152
|
+
isSamePhone: () => false
|
|
153
|
+
},
|
|
154
|
+
access: {
|
|
155
|
+
ingress: {
|
|
156
|
+
admission: admitted ? "dispatch" : "drop",
|
|
157
|
+
decision: admitted ? "allow" : "block",
|
|
158
|
+
decisiveGateId: "legacy-flat-compat",
|
|
159
|
+
reasonCode
|
|
160
|
+
},
|
|
161
|
+
senderAccess: {
|
|
162
|
+
allowed: admitted,
|
|
163
|
+
decision: admitted ? "allow" : "block",
|
|
164
|
+
reasonCode,
|
|
165
|
+
providerMissingFallbackApplied: false
|
|
166
|
+
},
|
|
167
|
+
commandAccess: {
|
|
168
|
+
requested: false,
|
|
169
|
+
authorized: false,
|
|
170
|
+
shouldBlockControlCommand: false,
|
|
171
|
+
reasonCode: "command_authorized"
|
|
172
|
+
},
|
|
173
|
+
activationAccess: {
|
|
174
|
+
ran: false,
|
|
175
|
+
allowed: admitted,
|
|
176
|
+
shouldSkip: !admitted,
|
|
177
|
+
reasonCode: admitted ? "activation_allowed" : "activation_skipped"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
isGroup: msg.chatType === "group",
|
|
181
|
+
conversationId,
|
|
182
|
+
senderId
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function requireWhatsAppInboundAdmission(params) {
|
|
186
|
+
if (!params.admission) throw new Error("WhatsApp inbound message is missing admission facts");
|
|
187
|
+
return params.admission;
|
|
188
|
+
}
|
|
189
|
+
function requireAdmittedWhatsAppInboundMessage(msg) {
|
|
190
|
+
requireWhatsAppInboundAdmission(msg);
|
|
191
|
+
return msg;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region extensions/whatsapp/src/inbound-policy.ts
|
|
195
|
+
function normalizeWhatsAppIngressPhone(value) {
|
|
196
|
+
const trimmed = value.trim();
|
|
197
|
+
if (!trimmed) return null;
|
|
198
|
+
return normalizeE164(trimmed);
|
|
199
|
+
}
|
|
200
|
+
function buildResolvedWhatsAppGroupConfig(params) {
|
|
201
|
+
return { channels: { whatsapp: {
|
|
202
|
+
groupPolicy: params.groupPolicy,
|
|
203
|
+
groups: params.groups
|
|
204
|
+
} } };
|
|
205
|
+
}
|
|
206
|
+
function resolveWhatsAppInboundPolicy(params) {
|
|
207
|
+
const account = resolveWhatsAppAccount({
|
|
208
|
+
cfg: params.cfg,
|
|
209
|
+
accountId: params.accountId
|
|
210
|
+
});
|
|
211
|
+
const configuredAllowFrom = account.allowFrom ?? [];
|
|
212
|
+
const dmPolicy = account.dmPolicy ?? "pairing";
|
|
213
|
+
const dmAllowFrom = configuredAllowFrom.length > 0 ? configuredAllowFrom : params.selfE164 ? [params.selfE164] : [];
|
|
214
|
+
const groupAllowFrom = (Array.isArray(account.groupAllowFrom) && account.groupAllowFrom.length > 0 ? account.groupAllowFrom : void 0) ?? (configuredAllowFrom.length > 0 ? configuredAllowFrom : void 0) ?? [];
|
|
215
|
+
const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg);
|
|
216
|
+
const { groupPolicy, providerMissingFallbackApplied } = resolveWhatsAppRuntimeGroupPolicy({
|
|
217
|
+
providerConfigPresent: params.cfg.channels?.whatsapp !== void 0,
|
|
218
|
+
groupPolicy: account.groupPolicy,
|
|
219
|
+
defaultGroupPolicy
|
|
220
|
+
});
|
|
221
|
+
const resolvedGroupCfg = buildResolvedWhatsAppGroupConfig({
|
|
222
|
+
groupPolicy,
|
|
223
|
+
groups: account.groups
|
|
224
|
+
});
|
|
225
|
+
const isSamePhone = (value) => typeof value === "string" && typeof params.selfE164 === "string" && value === params.selfE164;
|
|
226
|
+
return {
|
|
227
|
+
account,
|
|
228
|
+
dmPolicy,
|
|
229
|
+
groupPolicy,
|
|
230
|
+
configuredAllowFrom,
|
|
231
|
+
dmAllowFrom,
|
|
232
|
+
groupAllowFrom,
|
|
233
|
+
isSelfChat: account.selfChatMode ?? isSelfChatMode(params.selfE164, configuredAllowFrom),
|
|
234
|
+
providerMissingFallbackApplied,
|
|
235
|
+
isSamePhone,
|
|
236
|
+
resolveConversationGroupPolicy: (conversationId) => resolveChannelGroupPolicy({
|
|
237
|
+
cfg: resolvedGroupCfg,
|
|
238
|
+
channel: "whatsapp",
|
|
239
|
+
groupId: resolveWhatsAppGroupConversationId(conversationId),
|
|
240
|
+
hasGroupAllowFrom: groupAllowFrom.length > 0
|
|
241
|
+
}),
|
|
242
|
+
resolveConversationRequireMention: (conversationId) => resolveChannelGroupRequireMention({
|
|
243
|
+
cfg: resolvedGroupCfg,
|
|
244
|
+
channel: "whatsapp",
|
|
245
|
+
groupId: resolveWhatsAppGroupConversationId(conversationId)
|
|
246
|
+
})
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function resolveWhatsAppIngressAccess(params) {
|
|
250
|
+
return await resolveStableChannelMessageIngress({
|
|
251
|
+
channelId: "whatsapp",
|
|
252
|
+
accountId: params.policy.account.accountId,
|
|
253
|
+
identity: {
|
|
254
|
+
key: "whatsapp-sender-phone",
|
|
255
|
+
kind: "phone",
|
|
256
|
+
normalize: normalizeWhatsAppIngressPhone,
|
|
257
|
+
sensitivity: "pii",
|
|
258
|
+
entryIdPrefix: "whatsapp-entry"
|
|
259
|
+
},
|
|
260
|
+
cfg: params.cfg,
|
|
261
|
+
useDefaultPairingStore: true,
|
|
262
|
+
subject: { stableId: params.senderId ?? "" },
|
|
263
|
+
conversation: {
|
|
264
|
+
kind: params.isGroup ? "group" : "direct",
|
|
265
|
+
id: params.conversationId
|
|
266
|
+
},
|
|
267
|
+
contextBinding: params.contextBinding,
|
|
268
|
+
dmPolicy: params.policy.dmPolicy,
|
|
269
|
+
groupPolicy: params.policy.groupPolicy,
|
|
270
|
+
policy: { groupAllowFromFallbackToAllowFrom: false },
|
|
271
|
+
providerMissingFallbackApplied: params.policy.providerMissingFallbackApplied,
|
|
272
|
+
allowFrom: !params.isGroup && params.policy.account.selfChatMode !== false && params.senderId && params.policy.isSamePhone(params.senderId) ? [...params.policy.dmAllowFrom, params.senderId] : params.policy.dmAllowFrom,
|
|
273
|
+
groupAllowFrom: params.policy.groupAllowFrom,
|
|
274
|
+
command: params.includeCommand === true ? {} : void 0
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async function resolveWhatsAppCommandAuthorized(params) {
|
|
278
|
+
const self = getSelfIdentity(params.msg, params.authDir);
|
|
279
|
+
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
280
|
+
const policy = params.policy ?? resolveWhatsAppInboundPolicy({
|
|
281
|
+
cfg: params.cfg,
|
|
282
|
+
accountId: admission.accountId,
|
|
283
|
+
selfE164: self.e164 ?? null
|
|
284
|
+
});
|
|
285
|
+
const isGroup = admission.conversation.kind === "group";
|
|
286
|
+
const sender = getSenderIdentity(params.msg, params.authDir);
|
|
287
|
+
const dmSender = sender.e164 ?? admission.conversation.id;
|
|
288
|
+
const groupSender = sender.e164 ?? "";
|
|
289
|
+
if (!normalizeE164(isGroup ? groupSender : dmSender)) return false;
|
|
290
|
+
return (await resolveWhatsAppIngressAccess({
|
|
291
|
+
cfg: params.cfg,
|
|
292
|
+
policy,
|
|
293
|
+
isGroup,
|
|
294
|
+
conversationId: admission.conversation.id,
|
|
295
|
+
senderId: isGroup ? groupSender : dmSender,
|
|
296
|
+
includeCommand: true
|
|
297
|
+
})).commandAccess.authorized;
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
60
300
|
//#region extensions/whatsapp/src/inbound/baileys-cache.ts
|
|
61
301
|
const WHATSAPP_BAILEYS_CACHE_MAX_ENTRIES = 500;
|
|
62
302
|
function rememberWhatsAppBaileysCacheEntry(cache, key, value, ttlMs) {
|
|
@@ -625,10 +865,14 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
625
865
|
const cached = readGroupMetadataCacheEntry(localCache, jid);
|
|
626
866
|
if (cached) return cached;
|
|
627
867
|
try {
|
|
628
|
-
const
|
|
629
|
-
|
|
868
|
+
const hydratedEntry = params.baileysCache?.get(jid);
|
|
869
|
+
const providerMetadata = params.baileysCache ? readWhatsAppBaileysCacheEntry(params.baileysCache, jid) : void 0;
|
|
870
|
+
const hydratedMetadata = providerMetadata?.participants?.length ? providerMetadata : void 0;
|
|
871
|
+
const meta = hydratedMetadata ?? await (params.getCurrentSock() ?? params.sock).groupMetadata(jid);
|
|
872
|
+
if (!hydratedMetadata) rememberWhatsAppBaileysCacheEntry(params.baileysCache, jid, meta, GROUP_META_TTL_MS);
|
|
630
873
|
publishedJids.add(jid);
|
|
631
874
|
const entry = await summarize(meta);
|
|
875
|
+
if (hydratedMetadata && hydratedEntry) entry.expires = hydratedEntry.expiresAt;
|
|
632
876
|
rememberGroupMetadataCacheEntry(reconnectCache, jid, {
|
|
633
877
|
subject: entry.subject,
|
|
634
878
|
expires: entry.expires
|
|
@@ -647,7 +891,7 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
647
891
|
}
|
|
648
892
|
};
|
|
649
893
|
const resolveOutboundMentions = async (jid, text) => {
|
|
650
|
-
if (isJidGroup(jid) !== true || !mayContainWhatsAppOutboundMention(text)) return {
|
|
894
|
+
if (isJidGroup$1(jid) !== true || !mayContainWhatsAppOutboundMention(text)) return {
|
|
651
895
|
text,
|
|
652
896
|
mentionedJids: []
|
|
653
897
|
};
|
|
@@ -898,27 +1142,21 @@ function createWhatsAppInboundMessageDebouncer(options) {
|
|
|
898
1142
|
}
|
|
899
1143
|
//#endregion
|
|
900
1144
|
//#region extensions/whatsapp/src/inbound/media.ts
|
|
901
|
-
var WhatsAppInboundMediaLimitExceededError = class extends Error {
|
|
902
|
-
constructor(maxBytes) {
|
|
903
|
-
super(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
|
|
904
|
-
this.name = "WhatsAppInboundMediaLimitExceededError";
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
1145
|
function unwrapMessage(message) {
|
|
908
|
-
return normalizeMessageContent(message);
|
|
1146
|
+
return normalizeMessageContent$1(message);
|
|
909
1147
|
}
|
|
910
1148
|
async function downloadInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024) {
|
|
911
1149
|
const message = unwrapMessage(msg.message);
|
|
912
1150
|
if (!message) return;
|
|
913
1151
|
const mimetype = resolveInboundMediaMimetype(message);
|
|
914
1152
|
const fileName = message.documentMessage?.fileName ?? void 0;
|
|
915
|
-
if (!message.imageMessage && !message.videoMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
|
|
1153
|
+
if (!message.imageMessage && !message.videoMessage && !message.ptvMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
|
|
916
1154
|
return {
|
|
917
1155
|
saved: await saveMediaStream(await downloadMediaMessage(msg, "stream", {}, {
|
|
918
1156
|
reuploadRequest: sock.updateMediaMessage,
|
|
919
1157
|
logger: sock.logger
|
|
920
1158
|
}), mimetype, "inbound", maxBytes, fileName).catch((err) => {
|
|
921
|
-
if (err instanceof Error && /Media exceeds/i.test(err.message)) throw new
|
|
1159
|
+
if (err instanceof Error && /Media exceeds/i.test(err.message)) throw new Error(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
|
|
922
1160
|
throw err;
|
|
923
1161
|
}),
|
|
924
1162
|
mimetype,
|
|
@@ -929,12 +1167,18 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
|
|
|
929
1167
|
const contextInfo = extractContextInfo(unwrapMessage(msg.message));
|
|
930
1168
|
if (!contextInfo?.quotedMessage) return;
|
|
931
1169
|
const quotedMessage = contextInfo.quotedMessage;
|
|
1170
|
+
const self = sock.user;
|
|
1171
|
+
const quotedFromMe = identitiesOverlap({ jid: contextInfo.participant }, {
|
|
1172
|
+
jid: self?.id,
|
|
1173
|
+
lid: self?.lid,
|
|
1174
|
+
e164: self?.phoneNumber
|
|
1175
|
+
});
|
|
932
1176
|
return downloadInboundMedia({
|
|
933
1177
|
key: {
|
|
934
1178
|
id: contextInfo?.stanzaId || void 0,
|
|
935
1179
|
remoteJid: contextInfo.remoteJid ?? msg.key?.remoteJid ?? void 0,
|
|
936
1180
|
participant: contextInfo?.participant ?? void 0,
|
|
937
|
-
fromMe:
|
|
1181
|
+
fromMe: quotedFromMe
|
|
938
1182
|
},
|
|
939
1183
|
message: quotedMessage,
|
|
940
1184
|
messageTimestamp: msg.messageTimestamp
|
|
@@ -942,6 +1186,32 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
|
|
|
942
1186
|
}
|
|
943
1187
|
//#endregion
|
|
944
1188
|
//#region extensions/whatsapp/src/inbound/message-enrichment.ts
|
|
1189
|
+
const inboundMediaLogger = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
1190
|
+
const MAX_MEDIA_ERROR_MESSAGE_CHARS = 256;
|
|
1191
|
+
function sanitizeMediaErrorMessage(error) {
|
|
1192
|
+
return truncateUtf16Safe(redactToolPayloadText(error instanceof Error ? error.message : String(error)).replace(/https?:\/\/\S+/giu, "[redacted-url]").replace(/\b[^@\s]+@[a-z][a-z\d.-]*\b/giu, "[redacted-jid]").replace(/\+?\d[\d ().-]{6,}\d/gu, "[redacted-phone]").split("\n", 1)[0]?.trim() || "unknown error", MAX_MEDIA_ERROR_MESSAGE_CHARS);
|
|
1193
|
+
}
|
|
1194
|
+
function logMediaMaterializationFailure(params) {
|
|
1195
|
+
const errorClass = params.error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u.test(params.error.name) ? params.error.name : "Error";
|
|
1196
|
+
const statusCode = getStatusCode(params.error);
|
|
1197
|
+
const errorMessage = sanitizeMediaErrorMessage(params.error);
|
|
1198
|
+
const failureDetails = [
|
|
1199
|
+
`stage=${params.failureStage}`,
|
|
1200
|
+
...params.mediaKind ? [`kind=${params.mediaKind}`] : [],
|
|
1201
|
+
...typeof statusCode === "number" ? [`status=${statusCode}`] : []
|
|
1202
|
+
].join(" ");
|
|
1203
|
+
inboundMediaLogger.warn("WhatsApp inbound media materialization failed", {
|
|
1204
|
+
channel: "whatsapp",
|
|
1205
|
+
messageId: params.messageId ?? void 0,
|
|
1206
|
+
mediaKind: params.mediaKind,
|
|
1207
|
+
mimeType: params.mimeType,
|
|
1208
|
+
failureStage: params.failureStage,
|
|
1209
|
+
errorClass,
|
|
1210
|
+
...typeof statusCode === "number" ? { statusCode } : {},
|
|
1211
|
+
errorMessage,
|
|
1212
|
+
consoleMessage: `WhatsApp inbound media materialization failed (${failureDetails}): ${errorMessage}`
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
945
1215
|
async function enrichWhatsAppInboundMessage(params) {
|
|
946
1216
|
const { msg, sock } = params;
|
|
947
1217
|
const location = extractLocationData(msg.message ?? void 0);
|
|
@@ -972,6 +1242,13 @@ async function enrichWhatsAppInboundMessage(params) {
|
|
|
972
1242
|
try {
|
|
973
1243
|
await saveInboundMedia(await downloadInboundMedia(msg, sock, maxBytes));
|
|
974
1244
|
} catch (error) {
|
|
1245
|
+
logMediaMaterializationFailure({
|
|
1246
|
+
messageId: msg.key?.id,
|
|
1247
|
+
mediaKind,
|
|
1248
|
+
mimeType: mediaType,
|
|
1249
|
+
failureStage: "direct",
|
|
1250
|
+
error
|
|
1251
|
+
});
|
|
975
1252
|
params.logVerbose(`Inbound media download failed: ${String(error)}`);
|
|
976
1253
|
body = formatInboundMediaUnavailableText({
|
|
977
1254
|
body,
|
|
@@ -983,6 +1260,13 @@ async function enrichWhatsAppInboundMessage(params) {
|
|
|
983
1260
|
mediaKind = replyContext.media.kind ?? void 0;
|
|
984
1261
|
mediaType = mediaType ?? replyContext.media.contentType ?? void 0;
|
|
985
1262
|
} catch (error) {
|
|
1263
|
+
logMediaMaterializationFailure({
|
|
1264
|
+
messageId: msg.key?.id,
|
|
1265
|
+
mediaKind: replyContext.media.kind ?? void 0,
|
|
1266
|
+
mimeType: replyContext.media.contentType ?? void 0,
|
|
1267
|
+
failureStage: "quoted",
|
|
1268
|
+
error
|
|
1269
|
+
});
|
|
986
1270
|
params.logVerbose(`Quoted media download failed: ${String(error)}`);
|
|
987
1271
|
body = formatInboundMediaUnavailableText({
|
|
988
1272
|
body,
|
|
@@ -1004,6 +1288,112 @@ async function enrichWhatsAppInboundMessage(params) {
|
|
|
1004
1288
|
};
|
|
1005
1289
|
}
|
|
1006
1290
|
//#endregion
|
|
1291
|
+
//#region extensions/whatsapp/src/inbound/access-control.ts
|
|
1292
|
+
const PAIRING_REPLY_HISTORY_GRACE_MS = 3e4;
|
|
1293
|
+
function logWhatsAppVerbose$2(enabled, message) {
|
|
1294
|
+
if (!enabled) return;
|
|
1295
|
+
defaultRuntime.log(message);
|
|
1296
|
+
}
|
|
1297
|
+
function blockedInboundAccess(policy) {
|
|
1298
|
+
return {
|
|
1299
|
+
allowed: false,
|
|
1300
|
+
shouldMarkRead: false,
|
|
1301
|
+
isSelfChat: policy.isSelfChat,
|
|
1302
|
+
resolvedAccountId: policy.account.accountId
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
async function checkInboundAccessControl(params) {
|
|
1306
|
+
const policy = resolveWhatsAppInboundPolicy({
|
|
1307
|
+
cfg: params.cfg,
|
|
1308
|
+
accountId: params.accountId,
|
|
1309
|
+
selfE164: params.selfE164
|
|
1310
|
+
});
|
|
1311
|
+
const pairingGraceMs = typeof params.pairingGraceMs === "number" && params.pairingGraceMs > 0 ? params.pairingGraceMs : PAIRING_REPLY_HISTORY_GRACE_MS;
|
|
1312
|
+
const suppressPairingReply = typeof params.connectedAtMs === "number" && typeof params.messageTimestampMs === "number" && params.messageTimestampMs < params.connectedAtMs - pairingGraceMs;
|
|
1313
|
+
warnMissingProviderGroupPolicyFallbackOnce({
|
|
1314
|
+
providerMissingFallbackApplied: policy.providerMissingFallbackApplied,
|
|
1315
|
+
providerKey: "whatsapp",
|
|
1316
|
+
accountId: policy.account.accountId,
|
|
1317
|
+
log: (message) => logWhatsAppVerbose$2(params.verbose, message)
|
|
1318
|
+
});
|
|
1319
|
+
const conversationId = params.group ? params.remoteJid : params.from;
|
|
1320
|
+
const accessSenderId = params.group ? params.senderE164 : params.from;
|
|
1321
|
+
const admissionSenderId = params.group ? params.senderE164 ?? params.senderJid ?? params.from : params.from;
|
|
1322
|
+
const resolveChannelIngress = async (contextBinding) => await resolveWhatsAppIngressAccess({
|
|
1323
|
+
cfg: params.cfg,
|
|
1324
|
+
policy,
|
|
1325
|
+
isGroup: params.group,
|
|
1326
|
+
conversationId,
|
|
1327
|
+
senderId: accessSenderId,
|
|
1328
|
+
contextBinding
|
|
1329
|
+
});
|
|
1330
|
+
const access = await resolveChannelIngress();
|
|
1331
|
+
const { senderAccess } = access;
|
|
1332
|
+
if (params.group && senderAccess.decision !== "allow") {
|
|
1333
|
+
if (senderAccess.reasonCode === "group_policy_disabled") logWhatsAppVerbose$2(params.verbose, "Blocked group message (groupPolicy: disabled)");
|
|
1334
|
+
else if (senderAccess.reasonCode === "group_policy_empty_allowlist") logWhatsAppVerbose$2(params.verbose, "Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
|
|
1335
|
+
else logWhatsAppVerbose$2(params.verbose, `Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`);
|
|
1336
|
+
return blockedInboundAccess(policy);
|
|
1337
|
+
}
|
|
1338
|
+
if (!params.group) {
|
|
1339
|
+
if (params.isFromMe && (policy.account.selfChatMode === false || !policy.isSamePhone(params.from))) {
|
|
1340
|
+
logWhatsAppVerbose$2(params.verbose, "Skipping outbound DM (fromMe); no pairing reply needed.");
|
|
1341
|
+
return blockedInboundAccess(policy);
|
|
1342
|
+
}
|
|
1343
|
+
if (senderAccess.decision === "block" && senderAccess.reasonCode === "dm_policy_disabled") {
|
|
1344
|
+
logWhatsAppVerbose$2(params.verbose, "Blocked dm (dmPolicy: disabled)");
|
|
1345
|
+
return blockedInboundAccess(policy);
|
|
1346
|
+
}
|
|
1347
|
+
if (senderAccess.decision === "pairing" && !policy.isSamePhone(params.from)) {
|
|
1348
|
+
const candidate = params.from;
|
|
1349
|
+
if (suppressPairingReply) logWhatsAppVerbose$2(params.verbose, `Skipping pairing reply for historical DM from ${candidate}.`);
|
|
1350
|
+
else await createChannelPairingChallengeIssuer({
|
|
1351
|
+
channel: "whatsapp",
|
|
1352
|
+
accountId: policy.account.accountId,
|
|
1353
|
+
upsertPairingRequest: async ({ id, meta }) => await upsertChannelPairingRequest({
|
|
1354
|
+
channel: "whatsapp",
|
|
1355
|
+
id,
|
|
1356
|
+
accountId: policy.account.accountId,
|
|
1357
|
+
meta
|
|
1358
|
+
})
|
|
1359
|
+
})({
|
|
1360
|
+
senderId: candidate,
|
|
1361
|
+
senderIdLine: `Your WhatsApp phone number: ${candidate}`,
|
|
1362
|
+
meta: { name: (params.pushName ?? "").trim() || void 0 },
|
|
1363
|
+
onCreated: () => {
|
|
1364
|
+
logWhatsAppVerbose$2(params.verbose, `whatsapp pairing request sender=${candidate} name=${params.pushName ?? "unknown"}`);
|
|
1365
|
+
},
|
|
1366
|
+
sendPairingReply: async (text) => {
|
|
1367
|
+
await params.sock.sendMessage(params.remoteJid, { text });
|
|
1368
|
+
},
|
|
1369
|
+
onReplyError: (err) => {
|
|
1370
|
+
logWhatsAppVerbose$2(params.verbose, `whatsapp pairing reply failed for ${candidate}: ${String(err)}`);
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
return blockedInboundAccess(policy);
|
|
1374
|
+
}
|
|
1375
|
+
if (senderAccess.decision !== "allow") {
|
|
1376
|
+
logWhatsAppVerbose$2(params.verbose, `Blocked unauthorized sender ${params.from} (dmPolicy=${policy.dmPolicy})`);
|
|
1377
|
+
return blockedInboundAccess(policy);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
allowed: true,
|
|
1382
|
+
shouldMarkRead: true,
|
|
1383
|
+
isSelfChat: policy.isSelfChat,
|
|
1384
|
+
resolvedAccountId: policy.account.accountId,
|
|
1385
|
+
admission: buildWhatsAppInboundAdmission({
|
|
1386
|
+
policy,
|
|
1387
|
+
access,
|
|
1388
|
+
channelIngress: access,
|
|
1389
|
+
resolveChannelIngress,
|
|
1390
|
+
isGroup: params.group,
|
|
1391
|
+
conversationId,
|
|
1392
|
+
senderId: admissionSenderId
|
|
1393
|
+
})
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
//#endregion
|
|
1007
1397
|
//#region extensions/whatsapp/src/inbound/dedupe.ts
|
|
1008
1398
|
const recentOutboundMessages = createDedupeCache({
|
|
1009
1399
|
ttlMs: 20 * 6e4,
|
|
@@ -1011,7 +1401,14 @@ const recentOutboundMessages = createDedupeCache({
|
|
|
1011
1401
|
});
|
|
1012
1402
|
function buildMessageKey(params) {
|
|
1013
1403
|
const accountId = params.accountId.trim();
|
|
1014
|
-
const
|
|
1404
|
+
const rawRemoteJid = params.remoteJid.trim();
|
|
1405
|
+
const hostedPhone = isHostedPnUser(rawRemoteJid);
|
|
1406
|
+
let remoteJid;
|
|
1407
|
+
if (hostedPhone || isHostedLidUser(rawRemoteJid)) {
|
|
1408
|
+
const decodedJid = jidDecode(rawRemoteJid);
|
|
1409
|
+
if (!decodedJid) return null;
|
|
1410
|
+
remoteJid = jidEncode(decodedJid.user, hostedPhone ? "s.whatsapp.net" : "lid");
|
|
1411
|
+
} else remoteJid = jidNormalizedUser(rawRemoteJid);
|
|
1015
1412
|
const messageId = params.messageId.trim();
|
|
1016
1413
|
if (!accountId || !remoteJid || !messageId || messageId === "unknown") return null;
|
|
1017
1414
|
return `${accountId}:${remoteJid}:${messageId}`;
|
|
@@ -1027,7 +1424,13 @@ function rememberRecentOutboundMessage(params) {
|
|
|
1027
1424
|
function isRecentOutboundMessage(params) {
|
|
1028
1425
|
const key = buildMessageKey(params);
|
|
1029
1426
|
if (!key) return false;
|
|
1030
|
-
|
|
1427
|
+
if (recentOutboundMessages.peek(key)) return true;
|
|
1428
|
+
if (!params.alternateRemoteJid || isJidGroup(params.remoteJid)) return false;
|
|
1429
|
+
const alternateKey = buildMessageKey({
|
|
1430
|
+
...params,
|
|
1431
|
+
remoteJid: params.alternateRemoteJid
|
|
1432
|
+
});
|
|
1433
|
+
return alternateKey !== null && recentOutboundMessages.peek(alternateKey);
|
|
1031
1434
|
}
|
|
1032
1435
|
//#endregion
|
|
1033
1436
|
//#region extensions/whatsapp/src/inbound/message-normalization.ts
|
|
@@ -1039,6 +1442,7 @@ function createWhatsAppInboundMessageNormalizer(options) {
|
|
|
1039
1442
|
if (!msg.key?.fromMe || !id || !remoteJid || !isRecentOutboundMessage({
|
|
1040
1443
|
accountId: options.accountId,
|
|
1041
1444
|
remoteJid,
|
|
1445
|
+
alternateRemoteJid: msg.key?.remoteJidAlt,
|
|
1042
1446
|
messageId: id
|
|
1043
1447
|
})) return false;
|
|
1044
1448
|
options.logVerbose(`Skipping recent outbound WhatsApp echo ${id} for ${remoteJid}`);
|
|
@@ -1048,7 +1452,7 @@ function createWhatsAppInboundMessageNormalizer(options) {
|
|
|
1048
1452
|
const id = msg.key?.id ?? void 0;
|
|
1049
1453
|
const remoteJid = msg.key?.remoteJid;
|
|
1050
1454
|
if (!remoteJid || remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast")) return null;
|
|
1051
|
-
const group = isJidGroup(remoteJid) === true;
|
|
1455
|
+
const group = isJidGroup$1(remoteJid) === true;
|
|
1052
1456
|
if (shouldSkipRecentOutboundEcho(msg)) return null;
|
|
1053
1457
|
if (!hasInboundUserContent(msg.message ?? void 0)) return null;
|
|
1054
1458
|
const participantJid = msg.key?.participant ?? void 0;
|
|
@@ -1122,7 +1526,7 @@ function recordAcceptedInboundActivity(accountId) {
|
|
|
1122
1526
|
}
|
|
1123
1527
|
function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
1124
1528
|
const inboundLogger = getChildLogger({ module: "web-inbound" });
|
|
1125
|
-
const inboundConsoleLog = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
1529
|
+
const inboundConsoleLog = createSubsystemLogger$1("gateway/channels/whatsapp").child("inbound");
|
|
1126
1530
|
const sock = options.sock;
|
|
1127
1531
|
const socketSession = options.socketSession;
|
|
1128
1532
|
const groupMetadata = options.groupMetadata;
|
|
@@ -1634,7 +2038,10 @@ async function createWhatsAppAttachedSocketSession(options) {
|
|
|
1634
2038
|
if (readinessOptions?.useVerifiedReady && consumeVerifiedSendReady(jid, currentSock)) return;
|
|
1635
2039
|
const state = getActiveReachoutTimelock(reachoutTimeLock) ?? await fetchReachoutTimeLock(currentSock);
|
|
1636
2040
|
const activeState = getActiveReachoutTimelock(state);
|
|
1637
|
-
if (activeState)
|
|
2041
|
+
if (activeState) {
|
|
2042
|
+
const error = new Error(formatReachoutTimelockError(activeState));
|
|
2043
|
+
throw readinessOptions?.rememberReady ? new PlatformMessageNotDispatchedError(error.message, { cause: error }) : error;
|
|
2044
|
+
}
|
|
1638
2045
|
if (readinessOptions?.rememberReady && state) rememberVerifiedSendReady(jid, currentSock);
|
|
1639
2046
|
};
|
|
1640
2047
|
const assertSendReady = async (to) => {
|
|
@@ -1757,7 +2164,7 @@ function logWhatsAppVerbose(enabled, message) {
|
|
|
1757
2164
|
}
|
|
1758
2165
|
async function attachWebInboxToSocket(options) {
|
|
1759
2166
|
const inboundLogger = getChildLogger({ module: "web-inbound" });
|
|
1760
|
-
const inboundConsoleLog = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
2167
|
+
const inboundConsoleLog = createSubsystemLogger$1("gateway/channels/whatsapp").child("inbound");
|
|
1761
2168
|
const socketSession = await createWhatsAppAttachedSocketSession({
|
|
1762
2169
|
sock: options.sock,
|
|
1763
2170
|
socketRef: options.socketRef,
|
|
@@ -1868,7 +2275,7 @@ async function monitorWebInbox(options) {
|
|
|
1868
2275
|
}
|
|
1869
2276
|
//#endregion
|
|
1870
2277
|
//#region extensions/whatsapp/src/auto-reply/loggers.ts
|
|
1871
|
-
const whatsappLog = createSubsystemLogger("gateway/channels/whatsapp");
|
|
2278
|
+
const whatsappLog = createSubsystemLogger$1("gateway/channels/whatsapp");
|
|
1872
2279
|
const whatsappInboundLog = whatsappLog.child("inbound");
|
|
1873
2280
|
const whatsappOutboundLog = whatsappLog.child("outbound");
|
|
1874
2281
|
const whatsappHeartbeatLog = whatsappLog.child("heartbeat");
|
|
@@ -1932,6 +2339,15 @@ function resolveOwnerList(mentionCfg, selfE164) {
|
|
|
1932
2339
|
}
|
|
1933
2340
|
//#endregion
|
|
1934
2341
|
//#region extensions/whatsapp/src/auto-reply/monitor-state.ts
|
|
2342
|
+
const LIFECYCLE_BY_HEALTH_STATE = {
|
|
2343
|
+
starting: "starting",
|
|
2344
|
+
healthy: "ready",
|
|
2345
|
+
stale: "recovering",
|
|
2346
|
+
reconnecting: "recovering",
|
|
2347
|
+
conflict: "blocked",
|
|
2348
|
+
"logged-out": "blocked",
|
|
2349
|
+
stopped: "blocked"
|
|
2350
|
+
};
|
|
1935
2351
|
function cloneStatus(status) {
|
|
1936
2352
|
return {
|
|
1937
2353
|
...status,
|
|
@@ -1955,7 +2371,8 @@ function createWebChannelStatusController(statusSink) {
|
|
|
1955
2371
|
lastError: null,
|
|
1956
2372
|
busy: false,
|
|
1957
2373
|
lastRunActivityAt: null,
|
|
1958
|
-
healthState: "starting"
|
|
2374
|
+
healthState: "starting",
|
|
2375
|
+
lifecycle: "starting"
|
|
1959
2376
|
};
|
|
1960
2377
|
const emit = () => {
|
|
1961
2378
|
statusSink?.(cloneStatus(status));
|
|
@@ -1964,16 +2381,17 @@ function createWebChannelStatusController(statusSink) {
|
|
|
1964
2381
|
emit,
|
|
1965
2382
|
snapshot: () => status,
|
|
1966
2383
|
noteConnected(at = Date.now()) {
|
|
1967
|
-
Object.assign(status,
|
|
2384
|
+
Object.assign(status, channelReadyPatch({
|
|
2385
|
+
lastConnectedAt: at,
|
|
2386
|
+
lastEventAt: at
|
|
2387
|
+
}));
|
|
1968
2388
|
Object.assign(status, createTransportActivityStatusPatch(at));
|
|
1969
2389
|
if (lastDisconnectWasWatchdogRecovery) {
|
|
1970
2390
|
status.lastDisconnect = null;
|
|
1971
2391
|
status.reconnectAttempts = 0;
|
|
1972
2392
|
lastDisconnectWasWatchdogRecovery = false;
|
|
1973
2393
|
}
|
|
1974
|
-
status.lastError = null;
|
|
1975
2394
|
status.healthState = "healthy";
|
|
1976
|
-
status.terminalDisconnect = void 0;
|
|
1977
2395
|
emit();
|
|
1978
2396
|
},
|
|
1979
2397
|
noteInbound(at = Date.now()) {
|
|
@@ -1981,7 +2399,10 @@ function createWebChannelStatusController(statusSink) {
|
|
|
1981
2399
|
status.lastMessageAt = at;
|
|
1982
2400
|
status.lastEventAt = at;
|
|
1983
2401
|
Object.assign(status, createTransportActivityStatusPatch(at));
|
|
1984
|
-
if (status.connected)
|
|
2402
|
+
if (status.connected) {
|
|
2403
|
+
status.healthState = "healthy";
|
|
2404
|
+
status.lifecycle = "ready";
|
|
2405
|
+
}
|
|
1985
2406
|
emit();
|
|
1986
2407
|
},
|
|
1987
2408
|
noteTransportActivity(at = Date.now()) {
|
|
@@ -1993,12 +2414,18 @@ function createWebChannelStatusController(statusSink) {
|
|
|
1993
2414
|
if (status.busy === busy && status.lastRunActivityAt === at) return;
|
|
1994
2415
|
status.busy = busy;
|
|
1995
2416
|
status.lastRunActivityAt = at;
|
|
1996
|
-
if (status.connected && busy)
|
|
2417
|
+
if (status.connected && busy) {
|
|
2418
|
+
status.healthState = "healthy";
|
|
2419
|
+
status.lifecycle = "ready";
|
|
2420
|
+
}
|
|
1997
2421
|
emit();
|
|
1998
2422
|
},
|
|
1999
2423
|
noteWatchdogStale(at = Date.now()) {
|
|
2000
2424
|
status.lastEventAt = at;
|
|
2001
|
-
if (status.connected)
|
|
2425
|
+
if (status.connected) {
|
|
2426
|
+
status.healthState = "stale";
|
|
2427
|
+
status.lifecycle = "recovering";
|
|
2428
|
+
}
|
|
2002
2429
|
emit();
|
|
2003
2430
|
},
|
|
2004
2431
|
noteReconnectAttempts(reconnectAttempts) {
|
|
@@ -2019,52 +2446,28 @@ function createWebChannelStatusController(statusSink) {
|
|
|
2019
2446
|
status.lastError = params.error ?? null;
|
|
2020
2447
|
status.reconnectAttempts = params.reconnectAttempts;
|
|
2021
2448
|
status.healthState = params.healthState;
|
|
2449
|
+
status.lifecycle = LIFECYCLE_BY_HEALTH_STATE[params.healthState];
|
|
2022
2450
|
emit();
|
|
2023
2451
|
},
|
|
2024
2452
|
markStopped(at = Date.now()) {
|
|
2025
|
-
status.
|
|
2026
|
-
status.
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2453
|
+
const terminalDisconnect = status.lifecycle === "blocked";
|
|
2454
|
+
if (!isTerminalHealthState(status.healthState)) {
|
|
2455
|
+
Object.assign(status, channelStoppedPatch({
|
|
2456
|
+
lastEventAt: at,
|
|
2457
|
+
terminalDisconnect
|
|
2458
|
+
}));
|
|
2459
|
+
status.healthState = "stopped";
|
|
2460
|
+
} else Object.assign(status, {
|
|
2461
|
+
running: false,
|
|
2462
|
+
connected: false,
|
|
2463
|
+
lastEventAt: at,
|
|
2464
|
+
terminalDisconnect
|
|
2465
|
+
});
|
|
2030
2466
|
emit();
|
|
2031
2467
|
}
|
|
2032
2468
|
};
|
|
2033
2469
|
}
|
|
2034
2470
|
//#endregion
|
|
2035
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/echo.ts
|
|
2036
|
-
function createEchoTracker(params) {
|
|
2037
|
-
const recentlySent = /* @__PURE__ */ new Set();
|
|
2038
|
-
const maxItems = Math.max(1, params.maxItems ?? 100);
|
|
2039
|
-
const buildCombinedKey = (p) => `combined:${p.sessionKey}:${p.combinedBody}`;
|
|
2040
|
-
const buildTextKey = (text, conversationId) => conversationId ? `${conversationId}\0${text}` : text;
|
|
2041
|
-
const trim = () => {
|
|
2042
|
-
while (recentlySent.size > maxItems) {
|
|
2043
|
-
const firstKey = recentlySent.values().next().value;
|
|
2044
|
-
if (!firstKey) break;
|
|
2045
|
-
recentlySent.delete(firstKey);
|
|
2046
|
-
}
|
|
2047
|
-
};
|
|
2048
|
-
const rememberText = (text, opts) => {
|
|
2049
|
-
if (!text) return;
|
|
2050
|
-
recentlySent.add(buildTextKey(text, opts.conversationId));
|
|
2051
|
-
if (opts.combinedBody && opts.combinedBodySessionKey) recentlySent.add(buildCombinedKey({
|
|
2052
|
-
sessionKey: opts.combinedBodySessionKey,
|
|
2053
|
-
combinedBody: opts.combinedBody
|
|
2054
|
-
}));
|
|
2055
|
-
if (opts.logVerboseMessage) params.logVerbose?.(`Added to echo detection set (size now: ${recentlySent.size}): ${truncateUtf16Safe(text, 50)}...`);
|
|
2056
|
-
trim();
|
|
2057
|
-
};
|
|
2058
|
-
return {
|
|
2059
|
-
rememberText,
|
|
2060
|
-
has: (key, conversationId) => recentlySent.has(buildTextKey(key, conversationId)),
|
|
2061
|
-
forget: (key, conversationId) => {
|
|
2062
|
-
recentlySent.delete(buildTextKey(key, conversationId));
|
|
2063
|
-
},
|
|
2064
|
-
buildCombinedKey
|
|
2065
|
-
};
|
|
2066
|
-
}
|
|
2067
|
-
//#endregion
|
|
2068
2471
|
//#region extensions/whatsapp/src/auto-reply/monitor/listener-log.ts
|
|
2069
2472
|
function formatWhatsAppInboundListeningLog(account) {
|
|
2070
2473
|
if (account.groupPolicy === "disabled") return "Listening for WhatsApp inbound messages (DM + groups disabled by groupPolicy).";
|
|
@@ -2157,8 +2560,6 @@ async function resolveWhatsAppReactionEligibility(params) {
|
|
|
2157
2560
|
agentId: params.agentId,
|
|
2158
2561
|
ackConfig: params.cfg.messages?.ackReaction
|
|
2159
2562
|
});
|
|
2160
|
-
const directEnabled = scope === "all" || scope === "direct";
|
|
2161
|
-
const groupMode = scope === "all" || scope === "group-all" ? "always" : scope === "direct" ? "never" : "mentions";
|
|
2162
2563
|
const isGroup = admission.conversation.kind === "group";
|
|
2163
2564
|
const activation = isGroup ? await resolveGroupActivationFor({
|
|
2164
2565
|
cfg: params.cfg,
|
|
@@ -2167,14 +2568,14 @@ async function resolveWhatsAppReactionEligibility(params) {
|
|
|
2167
2568
|
sessionKey: params.sessionKey,
|
|
2168
2569
|
conversationId: admission.conversation.id
|
|
2169
2570
|
}) : null;
|
|
2170
|
-
if (!
|
|
2171
|
-
|
|
2571
|
+
if (!emoji || !shouldAckReaction({
|
|
2572
|
+
scope,
|
|
2172
2573
|
isDirect: admission.conversation.kind === "direct",
|
|
2173
2574
|
isGroup,
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2575
|
+
isMentionableGroup: isGroup,
|
|
2576
|
+
canDetectMention: isGroup,
|
|
2577
|
+
effectiveWasMentioned: (params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned) === true,
|
|
2578
|
+
shouldBypassMention: activation === "always"
|
|
2178
2579
|
})) return DISABLED_REACTION;
|
|
2179
2580
|
const participant = resolveReactionParticipant(params.msg);
|
|
2180
2581
|
return {
|
|
@@ -2184,7 +2585,7 @@ async function resolveWhatsAppReactionEligibility(params) {
|
|
|
2184
2585
|
emoji,
|
|
2185
2586
|
reactionOptions: {
|
|
2186
2587
|
verbose: params.verbose,
|
|
2187
|
-
fromMe:
|
|
2588
|
+
fromMe: params.msg.platform.fromMe === true,
|
|
2188
2589
|
...participant ? { participant } : {},
|
|
2189
2590
|
accountId,
|
|
2190
2591
|
cfg: params.cfg
|
|
@@ -2299,6 +2700,11 @@ async function maybeBroadcastMessage(params) {
|
|
|
2299
2700
|
return true;
|
|
2300
2701
|
}
|
|
2301
2702
|
//#endregion
|
|
2703
|
+
//#region extensions/whatsapp/src/auto-reply/monitor/audio-transcript.ts
|
|
2704
|
+
function formatWhatsAppAudioTranscriptForAgent(transcript) {
|
|
2705
|
+
return `[Audio transcript (machine-generated, untrusted)]: ${JSON.stringify(transcript)}`;
|
|
2706
|
+
}
|
|
2707
|
+
//#endregion
|
|
2302
2708
|
//#region extensions/whatsapp/src/auto-reply/monitor/commands.ts
|
|
2303
2709
|
function stripMentionsForCommand(text, mentionRegexes, selfE164) {
|
|
2304
2710
|
let result = text;
|
|
@@ -2375,7 +2781,7 @@ function recordPendingGroupHistoryEntry(params) {
|
|
|
2375
2781
|
timestamp: params.msg.event.timestamp,
|
|
2376
2782
|
id: params.msg.event.id,
|
|
2377
2783
|
senderJid: senderIdentity.jid ?? params.msg.platform.senderJid,
|
|
2378
|
-
...params.
|
|
2784
|
+
...params.msg.payload.media ? { media: [{
|
|
2379
2785
|
path: params.msg.payload.media.path,
|
|
2380
2786
|
url: params.msg.payload.media.url ?? params.msg.payload.media.path,
|
|
2381
2787
|
contentType: params.msg.payload.media.type,
|
|
@@ -2495,7 +2901,8 @@ async function applyGroupGating(params) {
|
|
|
2495
2901
|
needsMentionText: true
|
|
2496
2902
|
};
|
|
2497
2903
|
}
|
|
2498
|
-
|
|
2904
|
+
const pendingHistoryBody = params.mentionText === void 0 ? void 0 : formatWhatsAppAudioTranscriptForAgent(params.mentionText);
|
|
2905
|
+
return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${conversationId}: ${mentionMsg.payload.body}`, pendingHistoryBody);
|
|
2499
2906
|
}
|
|
2500
2907
|
return { shouldProcess: true };
|
|
2501
2908
|
}
|
|
@@ -2571,7 +2978,7 @@ var WhatsAppOutboundRetryError = class extends Error {
|
|
|
2571
2978
|
}
|
|
2572
2979
|
};
|
|
2573
2980
|
function isRetryableWhatsAppOutboundError(error) {
|
|
2574
|
-
if (isWhatsAppSocketOperationTimeoutError(error)) return false;
|
|
2981
|
+
if (isChannelPartialDeliveryError(error) || isWhatsAppSocketOperationTimeoutError(error)) return false;
|
|
2575
2982
|
return WHATSAPP_RETRYABLE_OUTBOUND_ERROR_PATTERN.test(formatError(error));
|
|
2576
2983
|
}
|
|
2577
2984
|
async function sendWhatsAppOutboundWithRetry(params) {
|
|
@@ -2706,14 +3113,14 @@ function createWhatsAppReplyDeliveryReceipt(results) {
|
|
|
2706
3113
|
});
|
|
2707
3114
|
}
|
|
2708
3115
|
async function deliverWebReply(params) {
|
|
3116
|
+
return await withWhatsAppLogicalDeliveryActivity(() => deliverWebReplyInActivityScope(params));
|
|
3117
|
+
}
|
|
3118
|
+
async function deliverWebReplyInActivityScope(params) {
|
|
2709
3119
|
const { replyResult, transport, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
|
|
2710
3120
|
const conversationId = transport.conversationId;
|
|
2711
3121
|
const isGroupConversation = transport.conversationKind === "group";
|
|
2712
3122
|
const replyStarted = Date.now();
|
|
2713
3123
|
const sendResults = [];
|
|
2714
|
-
const rememberSendResult = (result) => {
|
|
2715
|
-
if (result) sendResults.push(result);
|
|
2716
|
-
};
|
|
2717
3124
|
const finishDelivery = () => {
|
|
2718
3125
|
const receipt = createWhatsAppReplyDeliveryReceipt(sendResults);
|
|
2719
3126
|
return {
|
|
@@ -2722,6 +3129,32 @@ async function deliverWebReply(params) {
|
|
|
2722
3129
|
providerAccepted: sendResults.some((result) => result.providerAccepted)
|
|
2723
3130
|
};
|
|
2724
3131
|
};
|
|
3132
|
+
const preserveAcceptedDeliveryError = (error, kind) => {
|
|
3133
|
+
const sendKind = kind ?? sendResults[0]?.kind ?? "text";
|
|
3134
|
+
if (isChannelPartialDeliveryError(error)) rememberWhatsAppPartialSend({
|
|
3135
|
+
error,
|
|
3136
|
+
kind: sendKind,
|
|
3137
|
+
results: sendResults
|
|
3138
|
+
});
|
|
3139
|
+
return mergeWhatsAppAcceptedSendError({
|
|
3140
|
+
error,
|
|
3141
|
+
kind: sendKind,
|
|
3142
|
+
results: sendResults
|
|
3143
|
+
});
|
|
3144
|
+
};
|
|
3145
|
+
const rememberSendResult = (result) => {
|
|
3146
|
+
if (!result) return;
|
|
3147
|
+
try {
|
|
3148
|
+
rememberWhatsAppAcceptedSend({
|
|
3149
|
+
accountId: transport.accountId,
|
|
3150
|
+
result,
|
|
3151
|
+
results: sendResults
|
|
3152
|
+
});
|
|
3153
|
+
} catch (error) {
|
|
3154
|
+
if (sendResults.some((accepted) => accepted.providerAccepted)) throw preserveAcceptedDeliveryError(error, result.kind);
|
|
3155
|
+
throw error;
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
2725
3158
|
if (isReasoningReplyPayload(replyResult)) {
|
|
2726
3159
|
whatsappOutboundLog.debug(`Suppressed reasoning payload to ${conversationId}`);
|
|
2727
3160
|
return finishDelivery();
|
|
@@ -2743,7 +3176,7 @@ async function deliverWebReply(params) {
|
|
|
2743
3176
|
media: cached?.media
|
|
2744
3177
|
});
|
|
2745
3178
|
};
|
|
2746
|
-
const sendWithRetry = async (fn, label) => {
|
|
3179
|
+
const sendWithRetry = async (fn, label, kind) => {
|
|
2747
3180
|
try {
|
|
2748
3181
|
return await sendWhatsAppOutboundWithRetry({
|
|
2749
3182
|
send: fn,
|
|
@@ -2752,7 +3185,7 @@ async function deliverWebReply(params) {
|
|
|
2752
3185
|
}
|
|
2753
3186
|
});
|
|
2754
3187
|
} catch (error) {
|
|
2755
|
-
if (sendResults.some((result) => result.providerAccepted)) throw
|
|
3188
|
+
if (isChannelPartialDeliveryError(error) || sendResults.some((result) => result.providerAccepted)) throw preserveAcceptedDeliveryError(error, kind);
|
|
2756
3189
|
throw error;
|
|
2757
3190
|
}
|
|
2758
3191
|
};
|
|
@@ -2761,7 +3194,7 @@ async function deliverWebReply(params) {
|
|
|
2761
3194
|
for (const [index, chunk] of textChunks.entries()) {
|
|
2762
3195
|
const chunkStarted = Date.now();
|
|
2763
3196
|
const quote = getQuote();
|
|
2764
|
-
rememberSendResult(await sendWithRetry(() => transport.reply(chunk, quote), "text"));
|
|
3197
|
+
rememberSendResult(await sendWithRetry(() => transport.reply(chunk, quote), "text", "text"));
|
|
2765
3198
|
if (!skipLog) {
|
|
2766
3199
|
const durationMs = Date.now() - chunkStarted;
|
|
2767
3200
|
whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${conversationId} (${durationMs.toFixed(0)}ms)`);
|
|
@@ -2784,10 +3217,13 @@ async function deliverWebReply(params) {
|
|
|
2784
3217
|
return delivery;
|
|
2785
3218
|
}
|
|
2786
3219
|
const remainingText = [...textChunks];
|
|
3220
|
+
const leadingCaption = remainingText.shift() || "";
|
|
3221
|
+
let acceptedIdsBeforeCurrentMedia = /* @__PURE__ */ new Set();
|
|
2787
3222
|
await sendMediaWithLeadingCaption({
|
|
2788
3223
|
mediaUrls: mediaList,
|
|
2789
|
-
caption:
|
|
3224
|
+
caption: leadingCaption,
|
|
2790
3225
|
send: async ({ mediaUrl, caption }) => {
|
|
3226
|
+
acceptedIdsBeforeCurrentMedia = new Set(sendResults.flatMap(listWhatsAppSendResultMessageIds));
|
|
2791
3227
|
const media = await prepareWhatsAppOutboundMedia(await loadWebMedia(mediaUrl, {
|
|
2792
3228
|
maxBytes: maxMediaBytes,
|
|
2793
3229
|
localRoots: params.mediaLocalRoots
|
|
@@ -2802,22 +3238,22 @@ async function deliverWebReply(params) {
|
|
|
2802
3238
|
image: media.buffer,
|
|
2803
3239
|
caption,
|
|
2804
3240
|
mimetype: media.mimetype
|
|
2805
|
-
}, quote), "media:image"));
|
|
3241
|
+
}, quote), "media:image", "media"));
|
|
2806
3242
|
} else if (media.kind === "audio") {
|
|
2807
3243
|
const quote = getQuote();
|
|
2808
3244
|
rememberSendResult(await sendWithRetry(() => transport.sendMedia({
|
|
2809
3245
|
audio: media.buffer,
|
|
2810
3246
|
ptt: true,
|
|
2811
3247
|
mimetype: media.mimetype
|
|
2812
|
-
}, quote), "media:audio"));
|
|
2813
|
-
if (caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text"));
|
|
3248
|
+
}, quote), "media:audio", "media"));
|
|
3249
|
+
if (caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text", "text"));
|
|
2814
3250
|
} else if (media.kind === "video") {
|
|
2815
3251
|
const quote = getQuote();
|
|
2816
3252
|
rememberSendResult(await sendWithRetry(() => transport.sendMedia({
|
|
2817
3253
|
video: media.buffer,
|
|
2818
3254
|
caption,
|
|
2819
3255
|
mimetype: media.mimetype
|
|
2820
|
-
}, quote), "media:video"));
|
|
3256
|
+
}, quote), "media:video", "media"));
|
|
2821
3257
|
} else {
|
|
2822
3258
|
const quote = getQuote();
|
|
2823
3259
|
rememberSendResult(await sendWithRetry(() => transport.sendMedia({
|
|
@@ -2825,7 +3261,7 @@ async function deliverWebReply(params) {
|
|
|
2825
3261
|
fileName: media.fileName,
|
|
2826
3262
|
caption,
|
|
2827
3263
|
mimetype: media.mimetype
|
|
2828
|
-
}, quote), "media:document"));
|
|
3264
|
+
}, quote), "media:document", "media"));
|
|
2829
3265
|
}
|
|
2830
3266
|
whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
2831
3267
|
replyLogger.info({
|
|
@@ -2841,6 +3277,7 @@ async function deliverWebReply(params) {
|
|
|
2841
3277
|
}, "auto-reply sent (media)");
|
|
2842
3278
|
},
|
|
2843
3279
|
onError: async ({ error, mediaUrl, caption, isFirst }) => {
|
|
3280
|
+
if (sendResults.some((result) => listWhatsAppSendResultMessageIds(result).some((messageId) => !acceptedIdsBeforeCurrentMedia.has(messageId)))) throw preserveAcceptedDeliveryError(error);
|
|
2844
3281
|
whatsappOutboundLog.error(`Failed sending web media to ${conversationId}: ${formatError(error)}`);
|
|
2845
3282
|
replyLogger.warn({
|
|
2846
3283
|
err: error,
|
|
@@ -2848,16 +3285,16 @@ async function deliverWebReply(params) {
|
|
|
2848
3285
|
}, "failed to send web media reply");
|
|
2849
3286
|
if (!isFirst) {
|
|
2850
3287
|
whatsappOutboundLog.warn(`Trailing media failed; sent warning to ${conversationId}`);
|
|
2851
|
-
rememberSendResult(await sendWithRetry(() => transport.reply("⚠️ Media unavailable.", getQuote()), "media:fallback-unavailable"));
|
|
3288
|
+
rememberSendResult(await sendWithRetry(() => transport.reply("⚠️ Media unavailable.", getQuote()), "media:fallback-unavailable", "text"));
|
|
2852
3289
|
return;
|
|
2853
3290
|
}
|
|
2854
3291
|
const fallbackText = [caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
|
|
2855
3292
|
if (!fallbackText) return;
|
|
2856
3293
|
whatsappOutboundLog.warn(`Media skipped; sent text-only to ${conversationId}`);
|
|
2857
|
-
rememberSendResult(await sendWithRetry(() => transport.reply(fallbackText, getQuote()), "media:fallback-text"));
|
|
3294
|
+
rememberSendResult(await sendWithRetry(() => transport.reply(fallbackText, getQuote()), "media:fallback-text", "text"));
|
|
2858
3295
|
}
|
|
2859
3296
|
});
|
|
2860
|
-
for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => transport.reply(chunk, getQuote()), "media:text"));
|
|
3297
|
+
for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => transport.reply(chunk, getQuote()), "media:text", "text"));
|
|
2861
3298
|
return finishDelivery();
|
|
2862
3299
|
}
|
|
2863
3300
|
//#endregion
|
|
@@ -2941,7 +3378,7 @@ function projectPreparedChannelInbound(params) {
|
|
|
2941
3378
|
textForCommands: inbound.message.commandBody,
|
|
2942
3379
|
raw: inbound
|
|
2943
3380
|
},
|
|
2944
|
-
context:
|
|
3381
|
+
context: params.buildContext({
|
|
2945
3382
|
...inbound,
|
|
2946
3383
|
messageId: inbound.event.id,
|
|
2947
3384
|
messageIdFull: inbound.event.fullId,
|
|
@@ -3003,7 +3440,7 @@ function isWhatsAppVisibleDeliveryError(error) {
|
|
|
3003
3440
|
return typeof error === "object" && error !== null && !Array.isArray(error) && error.visibleReplySent === true || isChannelPartialDeliveryError(error) && error.deliveryResult.visibleReplySent;
|
|
3004
3441
|
}
|
|
3005
3442
|
function readTrimmedString(value) {
|
|
3006
|
-
return
|
|
3443
|
+
return normalizeOptionalString(value) ?? "";
|
|
3007
3444
|
}
|
|
3008
3445
|
function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
|
|
3009
3446
|
if (flushResult.delivered === 0) return error;
|
|
@@ -3128,6 +3565,13 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3128
3565
|
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
3129
3566
|
const conversationId = admission.conversation.id;
|
|
3130
3567
|
const conversationKind = admission.conversation.kind;
|
|
3568
|
+
const eventId = params.msg.event.id ?? `${conversationId}:${newConnectionId()}`;
|
|
3569
|
+
const channelIngress = await resolveWhatsAppAdmissionChannelIngress(admission, {
|
|
3570
|
+
agentId: params.route.agentId,
|
|
3571
|
+
sessionKey: params.route.sessionKey,
|
|
3572
|
+
messageId: eventId,
|
|
3573
|
+
inboundEventKind: "user_request"
|
|
3574
|
+
}) ?? admission.channelIngress;
|
|
3131
3575
|
const wasMentioned = params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned;
|
|
3132
3576
|
const inboundHistory = conversationKind === "group" ? buildInboundHistoryFromEntries({
|
|
3133
3577
|
entries: (params.groupHistory ?? []).map((entry) => ({
|
|
@@ -3147,6 +3591,7 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3147
3591
|
}] : void 0, { transcribed: (_entry, index) => params.mediaTranscribedIndexes?.includes(index) === true });
|
|
3148
3592
|
const control = { messageReceivedHooks: params.suppressMessageReceivedHooks ? "channel" : "core" };
|
|
3149
3593
|
const inbound = {
|
|
3594
|
+
channelIngress,
|
|
3150
3595
|
channel: "whatsapp",
|
|
3151
3596
|
supplemental: {
|
|
3152
3597
|
quote: params.visibleReplyTo ? {
|
|
@@ -3159,7 +3604,7 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3159
3604
|
},
|
|
3160
3605
|
media,
|
|
3161
3606
|
event: {
|
|
3162
|
-
id:
|
|
3607
|
+
id: eventId,
|
|
3163
3608
|
timestamp: params.msg.event.timestamp
|
|
3164
3609
|
},
|
|
3165
3610
|
from: conversationId,
|
|
@@ -3212,7 +3657,8 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3212
3657
|
};
|
|
3213
3658
|
const projected = projectPreparedChannelInbound({
|
|
3214
3659
|
inbound,
|
|
3215
|
-
control
|
|
3660
|
+
control,
|
|
3661
|
+
buildContext: params.buildContext ?? buildChannelInboundEventContext
|
|
3216
3662
|
});
|
|
3217
3663
|
return {
|
|
3218
3664
|
inbound,
|
|
@@ -3271,13 +3717,6 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3271
3717
|
let didLogHeartbeatStrip = false;
|
|
3272
3718
|
const recordDeliveredPayload = (payload) => {
|
|
3273
3719
|
didSendReply = true;
|
|
3274
|
-
const shouldLog = payload.text ? true : void 0;
|
|
3275
|
-
params.rememberSentText(payload.text, {
|
|
3276
|
-
combinedBody: params.context.Body,
|
|
3277
|
-
combinedBodySessionKey: params.route.sessionKey,
|
|
3278
|
-
conversationId,
|
|
3279
|
-
logVerboseMessage: shouldLog
|
|
3280
|
-
});
|
|
3281
3720
|
if (shouldLogVerbose$1()) {
|
|
3282
3721
|
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3283
3722
|
logVerbose$1(`Reply body: ${payload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
|
|
@@ -3324,14 +3763,7 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3324
3763
|
}, "auto-reply was not accepted by WhatsApp provider");
|
|
3325
3764
|
return result;
|
|
3326
3765
|
}
|
|
3327
|
-
if (options?.recordDelivery !== false)
|
|
3328
|
-
recordDeliveredPayload(normalizedDeliveryPayload);
|
|
3329
|
-
} catch (error) {
|
|
3330
|
-
throw createChannelPartialDeliveryError(error, {
|
|
3331
|
-
...result,
|
|
3332
|
-
visibleReplySent: true
|
|
3333
|
-
});
|
|
3334
|
-
}
|
|
3766
|
+
if (options?.recordDelivery !== false) recordDeliveredPayload(normalizedDeliveryPayload);
|
|
3335
3767
|
return result;
|
|
3336
3768
|
};
|
|
3337
3769
|
const mediaOnlyCoalescer = createWhatsAppMediaOnlyReplyCoalescer({ deliver: async (pending) => {
|
|
@@ -3448,13 +3880,16 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3448
3880
|
onToolStart: async (payload) => {
|
|
3449
3881
|
const toolName = payload.name?.trim();
|
|
3450
3882
|
if (toolName) await statusReactionController.setTool(toolName);
|
|
3883
|
+
return false;
|
|
3451
3884
|
},
|
|
3452
3885
|
onCompactionStart: async () => {
|
|
3453
3886
|
await statusReactionController.setCompacting();
|
|
3887
|
+
return false;
|
|
3454
3888
|
},
|
|
3455
3889
|
onCompactionEnd: async () => {
|
|
3456
3890
|
statusReactionController.cancelPending();
|
|
3457
3891
|
await statusReactionController.setThinking();
|
|
3892
|
+
return false;
|
|
3458
3893
|
}
|
|
3459
3894
|
} : {}
|
|
3460
3895
|
},
|
|
@@ -3473,7 +3908,7 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3473
3908
|
}
|
|
3474
3909
|
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3475
3910
|
controller: statusReactionController,
|
|
3476
|
-
outcome: didDeliverVisibleReply ? "
|
|
3911
|
+
outcome: readAgentRunTerminalOutcome(dispatchResult) === "failed" || !didDeliverVisibleReply ? "error" : "done"
|
|
3477
3912
|
});
|
|
3478
3913
|
if (params.shouldClearGroupHistory) params.groupHistories.set(params.groupHistoryKey, []);
|
|
3479
3914
|
return didDeliverVisibleReply;
|
|
@@ -3487,12 +3922,9 @@ async function finalizeWhatsAppStatusReaction(params) {
|
|
|
3487
3922
|
}
|
|
3488
3923
|
//#endregion
|
|
3489
3924
|
//#region extensions/whatsapp/src/auto-reply/monitor/message-line.runtime.ts
|
|
3490
|
-
function normalizeAgentId$1(agentId) {
|
|
3491
|
-
return agentId.trim().toLowerCase() || "main";
|
|
3492
|
-
}
|
|
3493
3925
|
function resolveIdentityNamePrefix(cfg, agentId) {
|
|
3494
|
-
const normalizedAgentId = normalizeAgentId
|
|
3495
|
-
const identityName = cfg.agents?.list?.find((agent) => normalizeAgentId
|
|
3926
|
+
const normalizedAgentId = normalizeAgentId(agentId);
|
|
3927
|
+
const identityName = cfg.agents?.list?.find((agent) => normalizeAgentId(agent.id ?? "") === normalizedAgentId)?.identity?.name?.trim();
|
|
3496
3928
|
return identityName ? `[${identityName}]` : void 0;
|
|
3497
3929
|
}
|
|
3498
3930
|
function resolveMessagePrefix(cfg, agentId, opts) {
|
|
@@ -3660,7 +4092,7 @@ async function processMessage(params) {
|
|
|
3660
4092
|
...params.msg,
|
|
3661
4093
|
payload: {
|
|
3662
4094
|
...params.msg.payload,
|
|
3663
|
-
body: audioTranscript
|
|
4095
|
+
body: formatWhatsAppAudioTranscriptForAgent(audioTranscript)
|
|
3664
4096
|
}
|
|
3665
4097
|
} : params.msg;
|
|
3666
4098
|
const visibleReplyTo = resolveVisibleWhatsAppReplyContext({
|
|
@@ -3711,15 +4143,6 @@ async function processMessage(params) {
|
|
|
3711
4143
|
});
|
|
3712
4144
|
shouldClearGroupHistory = !(params.suppressGroupHistoryClear ?? false);
|
|
3713
4145
|
}
|
|
3714
|
-
const combinedEchoKey = params.buildCombinedEchoKey({
|
|
3715
|
-
sessionKey: params.route.sessionKey,
|
|
3716
|
-
combinedBody
|
|
3717
|
-
});
|
|
3718
|
-
if (params.echoHas(combinedEchoKey)) {
|
|
3719
|
-
logVerbose$1("Skipping auto-reply: detected echo for combined message");
|
|
3720
|
-
params.echoForget(combinedEchoKey);
|
|
3721
|
-
return false;
|
|
3722
|
-
}
|
|
3723
4146
|
const statusReactionController = params.statusReactionController ?? (params.cfg.messages?.statusReactions?.enabled === true && !params.ackAlreadySent ? await createWhatsAppStatusReactionController({
|
|
3724
4147
|
cfg: params.cfg,
|
|
3725
4148
|
msg: params.msg,
|
|
@@ -3803,6 +4226,7 @@ async function processMessage(params) {
|
|
|
3803
4226
|
msg: params.msg,
|
|
3804
4227
|
rawBody: commandBody,
|
|
3805
4228
|
route: params.route,
|
|
4229
|
+
buildContext: params.buildContext,
|
|
3806
4230
|
sender: {
|
|
3807
4231
|
id: getPrimaryIdentityId(sender) ?? void 0,
|
|
3808
4232
|
name: sender.name ?? void 0,
|
|
@@ -3877,7 +4301,6 @@ async function processMessage(params) {
|
|
|
3877
4301
|
maxMediaTextChunkLimit: params.maxMediaTextChunkLimit,
|
|
3878
4302
|
inbound,
|
|
3879
4303
|
onModelSelected,
|
|
3880
|
-
rememberSentText: params.rememberSentText,
|
|
3881
4304
|
replyLogger: params.replyLogger,
|
|
3882
4305
|
replyPipeline: {
|
|
3883
4306
|
...replyPipeline,
|
|
@@ -3973,10 +4396,7 @@ function createWebOnMessageHandler(params) {
|
|
|
3973
4396
|
replyResolver: params.replyResolver,
|
|
3974
4397
|
replyLogger: params.replyLogger,
|
|
3975
4398
|
backgroundTasks: params.backgroundTasks,
|
|
3976
|
-
|
|
3977
|
-
echoHas: params.echoTracker.has,
|
|
3978
|
-
echoForget: params.echoTracker.forget,
|
|
3979
|
-
buildCombinedEchoKey: params.echoTracker.buildCombinedKey
|
|
4399
|
+
buildContext: params.buildContext
|
|
3980
4400
|
};
|
|
3981
4401
|
if (opts?.groupHistory !== void 0) processParams.groupHistory = opts.groupHistory;
|
|
3982
4402
|
if (opts?.suppressGroupHistoryClear !== void 0) processParams.suppressGroupHistoryClear = opts.suppressGroupHistoryClear;
|
|
@@ -4012,11 +4432,6 @@ function createWebOnMessageHandler(params) {
|
|
|
4012
4432
|
});
|
|
4013
4433
|
const baseMentionConfig = buildMentionConfig(cfg);
|
|
4014
4434
|
if (conversationId === msg.platform.recipientJid) logVerbose(`📱 Same-phone mode detected (from === to: ${conversationId})`);
|
|
4015
|
-
if (params.echoTracker.has(msg.payload.body, conversationId)) {
|
|
4016
|
-
logVerbose("Skipping auto-reply: detected echo (message matches recently sent text)");
|
|
4017
|
-
params.echoTracker.forget(msg.payload.body, conversationId);
|
|
4018
|
-
return;
|
|
4019
|
-
}
|
|
4020
4435
|
const configuredRoute = resolveConfiguredBindingRoute({
|
|
4021
4436
|
cfg,
|
|
4022
4437
|
route: baseConversationRoute,
|
|
@@ -4292,10 +4707,6 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4292
4707
|
const groupMetadataCache = /* @__PURE__ */ new Map();
|
|
4293
4708
|
const recentMessageKeys = /* @__PURE__ */ new Map();
|
|
4294
4709
|
const baileysGroupMetaCache = /* @__PURE__ */ new Map();
|
|
4295
|
-
const echoTracker = createEchoTracker({
|
|
4296
|
-
maxItems: 100,
|
|
4297
|
-
logVerbose
|
|
4298
|
-
});
|
|
4299
4710
|
const sleep = tuning.sleep ?? ((ms, signal) => sleepWithAbort(ms, signal ?? abortSignal));
|
|
4300
4711
|
const stopRequested = () => abortSignal?.aborted === true;
|
|
4301
4712
|
const currentMaxListeners = process.getMaxListeners?.() ?? 10;
|
|
@@ -4362,12 +4773,12 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4362
4773
|
groupHistoryLimit,
|
|
4363
4774
|
groupHistories,
|
|
4364
4775
|
groupMemberNames,
|
|
4365
|
-
echoTracker,
|
|
4366
4776
|
backgroundTasks: connectionLocal.backgroundTasks,
|
|
4367
4777
|
replyResolver: activeReplyResolver,
|
|
4368
4778
|
replyLogger,
|
|
4369
4779
|
baseMentionConfig,
|
|
4370
|
-
account
|
|
4780
|
+
account,
|
|
4781
|
+
buildContext: tuning.channelRuntime?.inbound.buildContext
|
|
4371
4782
|
});
|
|
4372
4783
|
return await (listenerFactory ?? attachWebInboxToSocket)({
|
|
4373
4784
|
cfg,
|