@openclaw/whatsapp 2026.9.1-beta.1 → 2026.9.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-BX2fZUyR.js → accounts-CiKHiuEe.js} +10 -27
- package/dist/{action-runtime-C075e6OP.js → action-runtime-CRZLhN3f.js} +2 -2
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/agent-tools-api.js +3 -3
- package/dist/api.js +10 -10
- package/dist/{approval-handler.runtime-CljxfPJz.js → approval-handler.runtime-C2cpkbpY.js} +8 -4
- package/dist/{approval-reactions-Jyr1JaAK.js → approval-reactions-DrZe-NmN.js} +7 -5
- package/dist/auth-presence.js +1 -1
- package/dist/{auth-store-CtpBqH5S.js → auth-store-ZWaclAbd.js} +8 -6
- package/dist/channel-config-api.js +1 -1
- package/dist/{channel-C4FMOZTE.js → channel-dwfgnadr.js} +56 -66
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-react-action-Bhp2BYRr.js → channel-react-action-CHeJSG9F.js} +3 -3
- package/dist/{channel.runtime-CrahUvcb.js → channel.runtime-Bn3K5Ygb.js} +4 -4
- package/dist/{channel.setup-CGcBvnpP.js → channel.setup-DckQm9ma.js} +1 -1
- package/dist/{config-schema-BQP5YCeK.js → config-schema-BYA6e_Ii.js} +6 -4
- package/dist/{connection-controller-CIYuBJrs.js → connection-controller-DNp6U1NN.js} +2 -2
- package/dist/{constants-HU41RHGI.js → constants-C7m81BNe.js} +1 -1
- package/dist/{creds-files-W6B3EMZ8.js → creds-files-Drg3usj1.js} +1 -1
- package/dist/{directory-config-P7_3U73Z.js → directory-config-CoWFw1zn.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-BPb-SjdE.js +65 -0
- package/dist/doctor-contract-api.js +21 -3
- package/dist/{group-session-key-DmFc_Cqi.js → group-session-key-BmVDSPr1.js} +11 -12
- package/dist/light-runtime-api.js +1 -1
- package/dist/{login-BkpKpndm.js → login-6j2iP9nR.js} +6 -5
- package/dist/{login-qr-DY4VFgOq.js → login-qr-DRFgI6k1.js} +9 -7
- package/dist/login-qr-runtime.js +1 -1
- package/dist/{monitor-C_Nw23PZ.js → monitor-Bc6DC5jl.js} +189 -156
- package/dist/{question-reactions-D3eAeB0i.js → question-reactions-bbe0Qnjn.js} +1 -1
- package/dist/runtime-api.js +9 -9
- package/dist/{send-DLMUc2uZ.js → send-BDDGPxav.js} +53 -28
- package/dist/{send-api-BtK45TmZ.js → send-api-C_Zn4Ij-.js} +48 -48
- package/dist/{setup-finalize-C1nij8NS.js → setup-finalize-gLmc3FTV.js} +7 -6
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-CjjqIxQP.js → setup-surface-twIi4TdN.js} +3 -3
- package/dist/{shared-BekHi69d.js → shared-ByeKDmh0.js} +13 -8
- package/dist/{socket-close-2T0_jZTN.js → socket-close-B3X9uerq.js} +10 -7
- package/dist/{targets-runtime-TMk0lGuy.js → targets-runtime-RBjx3pg_.js} +1 -2
- package/dist/{text-runtime-CaKLhowd.js → text-runtime-DyfmcATV.js} +1 -1
- package/node_modules/baileys/node_modules/process-warning/.github/dependabot.yml +42 -2
- package/node_modules/baileys/node_modules/process-warning/.github/workflows/ci.yml +12 -1
- package/node_modules/baileys/node_modules/process-warning/.github/workflows/lock-threads.yml +19 -0
- package/node_modules/baileys/node_modules/process-warning/LICENSE +1 -1
- package/node_modules/baileys/node_modules/process-warning/README.md +41 -2
- package/node_modules/baileys/node_modules/process-warning/index.js +85 -17
- package/node_modules/baileys/node_modules/process-warning/package.json +6 -7
- package/node_modules/baileys/node_modules/process-warning/test/emit-once-only.test.js +3 -3
- package/node_modules/baileys/node_modules/process-warning/test/emit-reset.test.js +4 -4
- package/node_modules/baileys/node_modules/process-warning/test/emit-unlimited.test.js +2 -2
- package/node_modules/baileys/node_modules/process-warning/test/spy-warning.test.js +143 -0
- package/node_modules/baileys/node_modules/process-warning/types/index.d.ts +15 -1
- package/node_modules/baileys/node_modules/process-warning/types/index.tst.ts +48 -0
- package/node_modules/baileys/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/baileys/node_modules/ws/package.json +1 -1
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
- package/node_modules/typebox/build/system/memory/create.mjs +3 -4
- package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
- package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
- package/node_modules/typebox/build/system/memory/update.mjs +3 -2
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +0 -1
- package/package.json +7 -6
- package/dist/doctor-CwzVXKoE.js +0 -10
- package/node_modules/baileys/node_modules/process-warning/types/index.test-d.ts +0 -36
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-
|
|
2
|
-
import { c as normalizeWhatsAppPayloadTextPreservingIndentation, d as listWhatsAppSendResultMessageIds, f as mergeWhatsAppAcceptedSendError, g as withWhatsAppLogicalDeliveryActivity, h as rememberWhatsAppPartialSend, l as prepareWhatsAppOutboundMedia, m as rememberWhatsAppAcceptedSend, o as normalizeWhatsAppOutboundPayload, p as normalizeWhatsAppSendResult, r as sendReactionWhatsApp, v as resolveWhatsAppReactionLevel } from "./send-
|
|
1
|
+
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-CiKHiuEe.js";
|
|
2
|
+
import { c as normalizeWhatsAppPayloadTextPreservingIndentation, d as listWhatsAppSendResultMessageIds, f as mergeWhatsAppAcceptedSendError, g as withWhatsAppLogicalDeliveryActivity, h as rememberWhatsAppPartialSend, l as prepareWhatsAppOutboundMedia, m as rememberWhatsAppAcceptedSend, o as normalizeWhatsAppOutboundPayload, p as normalizeWhatsAppSendResult, r as sendReactionWhatsApp, v as resolveWhatsAppReactionLevel } from "./send-BDDGPxav.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 { 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-
|
|
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-RBjx3pg_.js";
|
|
6
|
+
import { t as normalizeE164 } from "./text-runtime-DyfmcATV.js";
|
|
7
|
+
import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-BmVDSPr1.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-ZWaclAbd.js";
|
|
9
9
|
import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
|
|
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-
|
|
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-B3X9uerq.js";
|
|
11
11
|
import { n as getStatusCode, t as formatError } from "./session-errors-JuazczrA.js";
|
|
12
12
|
import { t as BufferJSON } from "./session.runtime-Nsqaehbd.js";
|
|
13
|
-
import { _ as resolveInboundMediaMimetype, a as addWhatsAppImagePreviewFields, c as extractContextInfo, d as extractMediaKind, f as extractMentionedJids, g as projectWhatsAppInboundMessage, 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-
|
|
13
|
+
import { _ as resolveInboundMediaMimetype, a as addWhatsAppImagePreviewFields, c as extractContextInfo, d as extractMediaKind, f as extractMentionedJids, g as projectWhatsAppInboundMessage, 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-C_Zn4Ij-.js";
|
|
14
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-
|
|
16
|
-
import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-
|
|
17
|
-
import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-
|
|
18
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString
|
|
19
|
-
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
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-DNp6U1NN.js";
|
|
16
|
+
import { n as maybeResolveWhatsAppApprovalReaction } from "./approval-reactions-DrZe-NmN.js";
|
|
17
|
+
import { t as maybeResolveWhatsAppQuestionReaction } from "./question-reactions-bbe0Qnjn.js";
|
|
18
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
20
19
|
import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
|
|
20
|
+
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
21
21
|
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
|
22
22
|
import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
|
|
23
23
|
import { createSubsystemLogger, getChildLogger, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
@@ -26,7 +26,6 @@ import { createSubsystemLogger as createSubsystemLogger$1, defaultRuntime, forma
|
|
|
26
26
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
27
27
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
28
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";
|
|
29
|
-
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
30
29
|
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
31
30
|
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
32
31
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -35,17 +34,18 @@ import { DisconnectReason, downloadMediaMessage, isHostedLidUser, isHostedPnUser
|
|
|
35
34
|
import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
36
35
|
import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
37
36
|
import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
37
|
+
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
38
38
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
39
39
|
import { createHash } from "node:crypto";
|
|
40
40
|
import { resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
41
41
|
import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
42
42
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
43
|
-
import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
|
|
44
43
|
import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
|
|
45
44
|
import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
46
45
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
47
46
|
import { fanInChannelIngressLifecycles, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
48
47
|
import { getSessionEntry, patchSessionEntry, resolveGroupSessionKey, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
48
|
+
import { createInboundDebouncer, resolveInboundDebounceMs } from "openclaw/plugin-sdk/channel-inbound-debounce";
|
|
49
49
|
import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
50
50
|
import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
51
51
|
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
@@ -357,7 +357,7 @@ function createWhatsAppIngressMonitor(params) {
|
|
|
357
357
|
deliver: (admission, lifecycle) => params.dispatch(admission, lifecycle),
|
|
358
358
|
pollIntervalMs: params.pollIntervalMs,
|
|
359
359
|
retention: {
|
|
360
|
-
completedTtlMs:
|
|
360
|
+
completedTtlMs: 6048e5,
|
|
361
361
|
completedMaxEntries: 5e3,
|
|
362
362
|
failedMaxEntries: 450
|
|
363
363
|
},
|
|
@@ -381,7 +381,7 @@ function createWhatsAppIngressMonitor(params) {
|
|
|
381
381
|
}
|
|
382
382
|
//#endregion
|
|
383
383
|
//#region extensions/whatsapp/src/inbound/group-metadata-cache.ts
|
|
384
|
-
const GROUP_META_TTL_MS =
|
|
384
|
+
const GROUP_META_TTL_MS = 3e5;
|
|
385
385
|
const WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES = 500;
|
|
386
386
|
function resolveGroupMetadataExpiresAt(nowRaw = Date.now()) {
|
|
387
387
|
const now = asDateTimestampMs(nowRaw);
|
|
@@ -498,10 +498,11 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
498
498
|
text,
|
|
499
499
|
mentionedJids: []
|
|
500
500
|
};
|
|
501
|
+
const meta = await get(jid);
|
|
501
502
|
return resolveWhatsAppOutboundMentions({
|
|
502
503
|
chatJid: jid,
|
|
503
504
|
text,
|
|
504
|
-
participants:
|
|
505
|
+
participants: meta.mentionParticipants
|
|
505
506
|
});
|
|
506
507
|
};
|
|
507
508
|
const applyOutboundMentions = async (jid, content) => {
|
|
@@ -528,10 +529,10 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
528
529
|
const listen = (event, listener) => {
|
|
529
530
|
detachListeners.push(params.listen(event, listener));
|
|
530
531
|
};
|
|
531
|
-
listen("groups.upsert", (
|
|
532
|
+
listen("groups.upsert", (groups) => {
|
|
532
533
|
for (const group of groups) if (group.id) rememberFullUpdate(group.id, group);
|
|
533
|
-
})
|
|
534
|
-
listen("groups.update", (
|
|
534
|
+
});
|
|
535
|
+
listen("groups.update", (updates) => {
|
|
535
536
|
for (const update of updates) {
|
|
536
537
|
if (!update.id) continue;
|
|
537
538
|
if (typeof update.subject === "string" && Array.isArray(update.participants)) {
|
|
@@ -540,10 +541,10 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
540
541
|
}
|
|
541
542
|
forgetFullMetadata(update.id);
|
|
542
543
|
}
|
|
543
|
-
})
|
|
544
|
-
listen("group-participants.update", (
|
|
544
|
+
});
|
|
545
|
+
listen("group-participants.update", (update) => {
|
|
545
546
|
forgetFullMetadata(update.id);
|
|
546
|
-
})
|
|
547
|
+
});
|
|
547
548
|
(async () => {
|
|
548
549
|
try {
|
|
549
550
|
const groups = await params.sock.groupFetchAllParticipating();
|
|
@@ -577,13 +578,7 @@ function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
|
577
578
|
//#region extensions/whatsapp/src/inbound/lifecycle.ts
|
|
578
579
|
function attachEmitterListener(emitter, event, listener) {
|
|
579
580
|
emitter.on(event, listener);
|
|
580
|
-
return () =>
|
|
581
|
-
if (typeof emitter.off === "function") {
|
|
582
|
-
emitter.off(event, listener);
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
if (typeof emitter.removeListener === "function") emitter.removeListener(event, listener);
|
|
586
|
-
};
|
|
581
|
+
return () => emitter.off(event, listener);
|
|
587
582
|
}
|
|
588
583
|
function closeInboundMonitorSocket(sock) {
|
|
589
584
|
if (typeof sock.end === "function") {
|
|
@@ -605,7 +600,6 @@ function resolveWhatsAppIngressLifecycle(message) {
|
|
|
605
600
|
//#endregion
|
|
606
601
|
//#region extensions/whatsapp/src/inbound/message-debounce.ts
|
|
607
602
|
function createWhatsAppInboundMessageDebouncer(options) {
|
|
608
|
-
const debounceMs = Math.max(0, Math.trunc(options.debounceMs ?? 0));
|
|
609
603
|
const pendingKeys = /* @__PURE__ */ new Map();
|
|
610
604
|
const activeFlushes = /* @__PURE__ */ new Set();
|
|
611
605
|
const workWaiters = /* @__PURE__ */ new Set();
|
|
@@ -635,7 +629,8 @@ function createWhatsAppInboundMessageDebouncer(options) {
|
|
|
635
629
|
return timestampDiff !== 0 ? timestampDiff : (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
|
|
636
630
|
});
|
|
637
631
|
const debouncer = createInboundDebouncer({
|
|
638
|
-
debounceMs,
|
|
632
|
+
debounceMs: options.resolveDebounceMs(),
|
|
633
|
+
resolveDebounceMs: (entry) => entry.debounceMs,
|
|
639
634
|
buildKey: (msg) => msg.debounceKey ?? buildKey(msg),
|
|
640
635
|
shouldDebounce,
|
|
641
636
|
onFlush: (entries, createFlush) => {
|
|
@@ -701,11 +696,15 @@ function createWhatsAppInboundMessageDebouncer(options) {
|
|
|
701
696
|
},
|
|
702
697
|
onError: options.onError
|
|
703
698
|
});
|
|
704
|
-
const enqueue = async (
|
|
699
|
+
const enqueue = async (input) => {
|
|
700
|
+
const message = {
|
|
701
|
+
...input,
|
|
702
|
+
debounceMs: options.resolveDebounceMs()
|
|
703
|
+
};
|
|
705
704
|
const key = buildKey(message);
|
|
706
705
|
if (key) {
|
|
707
706
|
message.debounceKey = key;
|
|
708
|
-
if (debounceMs > 0 && shouldDebounce(message)) {
|
|
707
|
+
if (message.debounceMs > 0 && shouldDebounce(message)) {
|
|
709
708
|
message.debounceKeyTracked = true;
|
|
710
709
|
trackKey(key);
|
|
711
710
|
options.onPendingWorkChanged();
|
|
@@ -748,26 +747,25 @@ function createWhatsAppInboundMessageDebouncer(options) {
|
|
|
748
747
|
function unwrapMessage(message) {
|
|
749
748
|
return normalizeMessageContent$1(message);
|
|
750
749
|
}
|
|
751
|
-
async function downloadInboundMedia(msg, sock, maxBytes =
|
|
750
|
+
async function downloadInboundMedia(msg, sock, maxBytes = 52428800, normalizedMessage) {
|
|
752
751
|
const message = normalizedMessage ?? unwrapMessage(msg.message);
|
|
753
752
|
if (!message) return;
|
|
754
753
|
const mimetype = resolveInboundMediaMimetype(message);
|
|
755
754
|
const fileName = message.documentMessage?.fileName ?? void 0;
|
|
756
755
|
if (!message.imageMessage && !message.videoMessage && !message.ptvMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
|
|
756
|
+
const stream = await downloadMediaMessage(msg, "stream", {}, {
|
|
757
|
+
reuploadRequest: sock.updateMediaMessage,
|
|
758
|
+
logger: sock.logger
|
|
759
|
+
});
|
|
757
760
|
return {
|
|
758
|
-
saved: await saveMediaStream(
|
|
759
|
-
reuploadRequest: sock.updateMediaMessage,
|
|
760
|
-
logger: sock.logger
|
|
761
|
-
}), mimetype, "inbound", maxBytes, fileName).catch((err) => {
|
|
762
|
-
if (err instanceof Error && /Media exceeds/i.test(err.message)) throw new Error(`Media exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
|
|
763
|
-
throw err;
|
|
764
|
-
}),
|
|
761
|
+
saved: await saveMediaStream(stream, mimetype, "inbound", maxBytes, fileName),
|
|
765
762
|
mimetype,
|
|
766
763
|
fileName
|
|
767
764
|
};
|
|
768
765
|
}
|
|
769
|
-
async function downloadQuotedInboundMedia(msg, sock, maxBytes =
|
|
770
|
-
const
|
|
766
|
+
async function downloadQuotedInboundMedia(msg, sock, maxBytes = 52428800) {
|
|
767
|
+
const message = unwrapMessage(msg.message);
|
|
768
|
+
const contextInfo = extractContextInfo(message);
|
|
771
769
|
if (!contextInfo?.quotedMessage) return;
|
|
772
770
|
const quotedMessage = contextInfo.quotedMessage;
|
|
773
771
|
const self = sock.user;
|
|
@@ -792,7 +790,9 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
|
|
|
792
790
|
const inboundMediaLogger = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
793
791
|
const MAX_MEDIA_ERROR_MESSAGE_CHARS = 256;
|
|
794
792
|
function sanitizeMediaErrorMessage(error) {
|
|
795
|
-
|
|
793
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
794
|
+
const redacted = redactToolPayloadText(rawMessage).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]");
|
|
795
|
+
return truncateUtf16Safe(redacted.split("\n", 1)[0]?.trim() || "unknown error", MAX_MEDIA_ERROR_MESSAGE_CHARS);
|
|
796
796
|
}
|
|
797
797
|
function logMediaMaterializationFailure(params) {
|
|
798
798
|
const errorClass = params.error instanceof Error && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u.test(params.error.name) ? params.error.name : "Error";
|
|
@@ -1001,7 +1001,7 @@ async function checkInboundAccessControl(params) {
|
|
|
1001
1001
|
//#endregion
|
|
1002
1002
|
//#region extensions/whatsapp/src/inbound/dedupe.ts
|
|
1003
1003
|
const recentOutboundMessages = createDedupeCache({
|
|
1004
|
-
ttlMs:
|
|
1004
|
+
ttlMs: 12e5,
|
|
1005
1005
|
maxSize: 5e3
|
|
1006
1006
|
});
|
|
1007
1007
|
function buildMessageKey(params) {
|
|
@@ -1182,7 +1182,11 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1182
1182
|
await maybeMarkInboundAsRead(target);
|
|
1183
1183
|
};
|
|
1184
1184
|
const messageDebouncer = createWhatsAppInboundMessageDebouncer({
|
|
1185
|
-
|
|
1185
|
+
resolveDebounceMs: () => resolveInboundDebounceMs({
|
|
1186
|
+
cfg: options.loadConfig?.() ?? options.cfg,
|
|
1187
|
+
channel: "whatsapp",
|
|
1188
|
+
overrideMs: options.debounceMs
|
|
1189
|
+
}),
|
|
1186
1190
|
onMessage: options.onMessage,
|
|
1187
1191
|
shouldDebounce: options.shouldDebounce,
|
|
1188
1192
|
markRead: maybeMarkInboundAsRead,
|
|
@@ -1215,11 +1219,13 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1215
1219
|
};
|
|
1216
1220
|
const reply = async (text, optionsResult) => {
|
|
1217
1221
|
const resolved = await groupMetadata.resolveOutboundMentions(chatJid, text);
|
|
1218
|
-
|
|
1222
|
+
const result = await sendTrackedMessage(chatJid, addWhatsAppOutboundMentionsToContent({ text: resolved.text }, resolved.mentionedJids), optionsResult);
|
|
1223
|
+
return normalizeWhatsAppSendResult(result, "text");
|
|
1219
1224
|
};
|
|
1220
1225
|
const sendMedia = async (payload, optionsValue) => {
|
|
1221
1226
|
const previewPayload = await addWhatsAppImagePreviewFields(payload);
|
|
1222
|
-
|
|
1227
|
+
const result = await sendTrackedMessage(chatJid, await groupMetadata.applyOutboundMentions(chatJid, previewPayload), optionsValue);
|
|
1228
|
+
return normalizeWhatsAppSendResult(result, "media");
|
|
1223
1229
|
};
|
|
1224
1230
|
const timestamp = inbound.messageTimestampMs;
|
|
1225
1231
|
const mentionedJids = enriched.mentionedJids;
|
|
@@ -1439,14 +1445,15 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1439
1445
|
finishPreparation(void 0);
|
|
1440
1446
|
const inbound = await normalizeInboundMessage(msg);
|
|
1441
1447
|
if (inbound) await maybeMarkNonSelfChatReadReceipt(inbound, buildReadReceiptTarget(inbound));
|
|
1442
|
-
} else if (result.kind === "durable" && result.queueResult.kind === "accepted")
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1448
|
+
} else if (result.kind === "durable" && result.queueResult.kind === "accepted") {
|
|
1449
|
+
if (skipRecentOutboundEcho) finishPreparation(null);
|
|
1450
|
+
else try {
|
|
1451
|
+
finishPreparation(await normalizeInboundMessage(msg), true);
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
finishPreparation(void 0);
|
|
1454
|
+
inboundLogger.warn({ error: formatError(error) }, "failed preparing WhatsApp inbound identity; durable drain will normalize again");
|
|
1455
|
+
}
|
|
1456
|
+
} else finishPreparation(void 0);
|
|
1450
1457
|
}
|
|
1451
1458
|
};
|
|
1452
1459
|
const handleMessagesUpsertEvent = (upsert) => {
|
|
@@ -1514,7 +1521,7 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1514
1521
|
//#region extensions/whatsapp/src/inbound/socket-session.ts
|
|
1515
1522
|
const LOGGED_OUT_STATUS = DisconnectReason.loggedOut;
|
|
1516
1523
|
const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
|
|
1517
|
-
const BAILEYS_MESSAGE_TTL_MS =
|
|
1524
|
+
const BAILEYS_MESSAGE_TTL_MS = 6e5;
|
|
1518
1525
|
function isDirectUserJid(jid) {
|
|
1519
1526
|
return /^(\d+)(?::\d+)?@(s\.whatsapp\.net|c\.us|lid|hosted|hosted\.lid)$/i.test(jid.trim());
|
|
1520
1527
|
}
|
|
@@ -2410,7 +2417,11 @@ async function applyGroupGating(params) {
|
|
|
2410
2417
|
const conversationGroupPolicy = inboundPolicy.resolveConversationGroupPolicy(conversationId);
|
|
2411
2418
|
if (conversationGroupPolicy.allowlistEnabled && !conversationGroupPolicy.allowed) {
|
|
2412
2419
|
const accountId = inboundPolicy.account.accountId;
|
|
2413
|
-
if (shouldWarnForGroupDrop(
|
|
2420
|
+
if (shouldWarnForGroupDrop(JSON.stringify([
|
|
2421
|
+
accountId,
|
|
2422
|
+
conversationId,
|
|
2423
|
+
"group registry"
|
|
2424
|
+
]))) {
|
|
2414
2425
|
const groupsPath = resolveWhatsAppGroupsConfigPath({
|
|
2415
2426
|
cfg: params.cfg,
|
|
2416
2427
|
accountId
|
|
@@ -2470,11 +2481,13 @@ async function applyGroupGating(params) {
|
|
|
2470
2481
|
conversationId
|
|
2471
2482
|
}) !== "always";
|
|
2472
2483
|
const replyContext = getReplyContext(params.msg, params.authDir);
|
|
2484
|
+
const implicitReplyToSelf = params.selfChatMode === true && identitiesOverlap(self, sender);
|
|
2485
|
+
const implicitMentionKinds = implicitMentionKindWhen("quoted_bot", !implicitReplyToSelf && identitiesOverlap(self, replyContext?.sender));
|
|
2473
2486
|
const mentionDecision = resolveInboundMentionDecision({
|
|
2474
2487
|
facts: {
|
|
2475
2488
|
canDetectMention: true,
|
|
2476
2489
|
wasMentioned,
|
|
2477
|
-
implicitMentionKinds
|
|
2490
|
+
implicitMentionKinds
|
|
2478
2491
|
},
|
|
2479
2492
|
policy: {
|
|
2480
2493
|
isGroup: true,
|
|
@@ -2497,6 +2510,22 @@ async function applyGroupGating(params) {
|
|
|
2497
2510
|
needsMentionText: true
|
|
2498
2511
|
};
|
|
2499
2512
|
}
|
|
2513
|
+
const accountId = inboundPolicy.account.accountId;
|
|
2514
|
+
if (shouldWarnForGroupDrop(JSON.stringify([
|
|
2515
|
+
accountId,
|
|
2516
|
+
conversationId,
|
|
2517
|
+
"no mention"
|
|
2518
|
+
]))) {
|
|
2519
|
+
const groupsPath = resolveWhatsAppGroupsConfigPath({
|
|
2520
|
+
cfg: params.cfg,
|
|
2521
|
+
accountId
|
|
2522
|
+
});
|
|
2523
|
+
params.replyLogger.warn({
|
|
2524
|
+
conversationId,
|
|
2525
|
+
accountId,
|
|
2526
|
+
groupsPath
|
|
2527
|
+
}, `WhatsApp group ${conversationId}: skipping messages without a mention. Mention patterns can be derived from the agent identity name. Use /activation always for this session, or set ${groupsPath}[${JSON.stringify(conversationId)}].requireMention=false for the default. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.`);
|
|
2528
|
+
}
|
|
2500
2529
|
const pendingHistoryBody = params.mentionText === void 0 ? void 0 : formatAudioTranscriptForAgent(params.mentionText);
|
|
2501
2530
|
return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${conversationId}: ${mentionMsg.payload.body}`, pendingHistoryBody);
|
|
2502
2531
|
}
|
|
@@ -2652,7 +2681,8 @@ function isLikelyWhatsAppCryptoError(reason) {
|
|
|
2652
2681
|
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
|
|
2653
2682
|
return Object.prototype.toString.call(value);
|
|
2654
2683
|
};
|
|
2655
|
-
const
|
|
2684
|
+
const raw = reason instanceof Error ? `${reason.message}\n${reason.stack ?? ""}` : formatReason(reason);
|
|
2685
|
+
const haystack = normalizeLowercaseStringOrEmpty(raw);
|
|
2656
2686
|
if (!(haystack.includes("unsupported state or unable to authenticate data") || haystack.includes("bad mac"))) return false;
|
|
2657
2687
|
return haystack.includes("baileys") || haystack.includes("noise-handler") || haystack.includes("aesdecryptgcm");
|
|
2658
2688
|
}
|
|
@@ -2717,6 +2747,11 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2717
2747
|
const isGroupConversation = transport.conversationKind === "group";
|
|
2718
2748
|
const replyStarted = Date.now();
|
|
2719
2749
|
const sendResults = [];
|
|
2750
|
+
const acceptedMediaUrls = /* @__PURE__ */ new Set();
|
|
2751
|
+
const recordMediaAccepted = (mediaUrl) => {
|
|
2752
|
+
acceptedMediaUrls.add(mediaUrl);
|
|
2753
|
+
params.onMediaAccepted?.(mediaUrl);
|
|
2754
|
+
};
|
|
2720
2755
|
const finishDelivery = () => {
|
|
2721
2756
|
const receipt = createWhatsAppReplyDeliveryReceipt(sendResults);
|
|
2722
2757
|
return {
|
|
@@ -2725,20 +2760,22 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2725
2760
|
providerAccepted: sendResults.some((result) => result.providerAccepted)
|
|
2726
2761
|
};
|
|
2727
2762
|
};
|
|
2728
|
-
const preserveAcceptedDeliveryError = (error, kind) => {
|
|
2763
|
+
const preserveAcceptedDeliveryError = (error, kind, mediaUrl) => {
|
|
2729
2764
|
const sendKind = kind ?? sendResults[0]?.kind ?? "text";
|
|
2730
|
-
if (isChannelPartialDeliveryError(error))
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2765
|
+
if (isChannelPartialDeliveryError(error)) {
|
|
2766
|
+
if (rememberWhatsAppPartialSend({
|
|
2767
|
+
error,
|
|
2768
|
+
kind: sendKind,
|
|
2769
|
+
results: sendResults
|
|
2770
|
+
}) && mediaUrl) recordMediaAccepted(mediaUrl);
|
|
2771
|
+
}
|
|
2735
2772
|
return mergeWhatsAppAcceptedSendError({
|
|
2736
2773
|
error,
|
|
2737
2774
|
kind: sendKind,
|
|
2738
2775
|
results: sendResults
|
|
2739
2776
|
});
|
|
2740
2777
|
};
|
|
2741
|
-
const rememberSendResult = (result) => {
|
|
2778
|
+
const rememberSendResult = (result, mediaUrl) => {
|
|
2742
2779
|
if (!result) return;
|
|
2743
2780
|
try {
|
|
2744
2781
|
rememberWhatsAppAcceptedSend({
|
|
@@ -2749,6 +2786,8 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2749
2786
|
} catch (error) {
|
|
2750
2787
|
if (sendResults.some((accepted) => accepted.providerAccepted)) throw preserveAcceptedDeliveryError(error, result.kind);
|
|
2751
2788
|
throw error;
|
|
2789
|
+
} finally {
|
|
2790
|
+
if (mediaUrl && sendResults.includes(result)) recordMediaAccepted(mediaUrl);
|
|
2752
2791
|
}
|
|
2753
2792
|
};
|
|
2754
2793
|
if (isReasoningReplyPayload(replyResult)) {
|
|
@@ -2773,7 +2812,7 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2773
2812
|
media: cached?.media
|
|
2774
2813
|
});
|
|
2775
2814
|
};
|
|
2776
|
-
const sendWithRetry = async (fn, label, kind) => {
|
|
2815
|
+
const sendWithRetry = async (fn, label, kind, mediaUrl) => {
|
|
2777
2816
|
try {
|
|
2778
2817
|
return await sendWhatsAppOutboundWithRetry({
|
|
2779
2818
|
send: fn,
|
|
@@ -2782,7 +2821,7 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2782
2821
|
}
|
|
2783
2822
|
});
|
|
2784
2823
|
} catch (error) {
|
|
2785
|
-
if (isChannelPartialDeliveryError(error) || sendResults.some((result) => result.providerAccepted)) throw preserveAcceptedDeliveryError(error, kind);
|
|
2824
|
+
if (isChannelPartialDeliveryError(error) || sendResults.some((result) => result.providerAccepted)) throw preserveAcceptedDeliveryError(error, kind, mediaUrl);
|
|
2786
2825
|
throw error;
|
|
2787
2826
|
}
|
|
2788
2827
|
};
|
|
@@ -2815,52 +2854,39 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2815
2854
|
}
|
|
2816
2855
|
const remainingText = [...textChunks];
|
|
2817
2856
|
const leadingCaption = remainingText.shift() || "";
|
|
2818
|
-
let acceptedIdsBeforeCurrentMedia = /* @__PURE__ */ new Set();
|
|
2819
2857
|
await sendMediaWithLeadingCaption({
|
|
2820
2858
|
mediaUrls: mediaList,
|
|
2821
2859
|
caption: leadingCaption,
|
|
2822
2860
|
send: async ({ mediaUrl, caption }) => {
|
|
2823
|
-
acceptedIdsBeforeCurrentMedia = new Set(sendResults.flatMap(listWhatsAppSendResultMessageIds));
|
|
2824
2861
|
const media = await prepareWhatsAppOutboundMedia(await loadWebMedia(mediaUrl, {
|
|
2825
2862
|
maxBytes: maxMediaBytes,
|
|
2826
2863
|
localRoots: params.mediaLocalRoots
|
|
2827
2864
|
}), mediaUrl);
|
|
2828
2865
|
if (shouldLogVerbose()) {
|
|
2829
|
-
logVerbose(`Web auto-reply media size: ${(media.buffer.length /
|
|
2866
|
+
logVerbose(`Web auto-reply media size: ${(media.buffer.length / 1048576).toFixed(2)}MB`);
|
|
2830
2867
|
logVerbose(`Web auto-reply media source: ${mediaUrl} (kind ${media.kind})`);
|
|
2831
2868
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
}, quote), "media:video", "media"));
|
|
2854
|
-
} else {
|
|
2855
|
-
const quote = getQuote();
|
|
2856
|
-
rememberSendResult(await sendWithRetry(() => transport.sendMedia({
|
|
2857
|
-
document: media.buffer,
|
|
2858
|
-
fileName: media.fileName,
|
|
2859
|
-
caption,
|
|
2860
|
-
mimetype: media.mimetype
|
|
2861
|
-
}, quote), "media:document", "media"));
|
|
2862
|
-
}
|
|
2863
|
-
whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
2869
|
+
const quote = getQuote();
|
|
2870
|
+
const mediaContent = media.kind === "image" ? {
|
|
2871
|
+
image: media.buffer,
|
|
2872
|
+
caption
|
|
2873
|
+
} : media.kind === "audio" ? {
|
|
2874
|
+
audio: media.buffer,
|
|
2875
|
+
ptt: true
|
|
2876
|
+
} : media.kind === "video" ? {
|
|
2877
|
+
video: media.buffer,
|
|
2878
|
+
caption
|
|
2879
|
+
} : {
|
|
2880
|
+
document: media.buffer,
|
|
2881
|
+
fileName: media.fileName,
|
|
2882
|
+
caption
|
|
2883
|
+
};
|
|
2884
|
+
rememberSendResult(await sendWithRetry(() => transport.sendMedia({
|
|
2885
|
+
...mediaContent,
|
|
2886
|
+
mimetype: media.mimetype
|
|
2887
|
+
}, quote), `media:${media.kind}`, "media", mediaUrl), mediaUrl);
|
|
2888
|
+
if (media.kind === "audio" && caption) rememberSendResult(await sendWithRetry(() => transport.reply(caption, quote), "media:audio-text", "text"));
|
|
2889
|
+
whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / 1048576).toFixed(2)}MB)`);
|
|
2864
2890
|
replyLogger.info({
|
|
2865
2891
|
correlationId: transport.correlationId ?? newConnectionId(),
|
|
2866
2892
|
connectionId: connectionId ?? null,
|
|
@@ -2874,7 +2900,7 @@ async function deliverWebReplyInActivityScope(params) {
|
|
|
2874
2900
|
}, "auto-reply sent (media)");
|
|
2875
2901
|
},
|
|
2876
2902
|
onError: async ({ error, mediaUrl, caption, isFirst }) => {
|
|
2877
|
-
if (
|
|
2903
|
+
if (acceptedMediaUrls.has(mediaUrl)) throw preserveAcceptedDeliveryError(error);
|
|
2878
2904
|
whatsappOutboundLog.error(`Failed sending web media to ${conversationId}: ${formatError(error)}`);
|
|
2879
2905
|
replyLogger.warn({
|
|
2880
2906
|
err: error,
|
|
@@ -3067,7 +3093,7 @@ function resolveWhatsAppDurableReplyToId(params) {
|
|
|
3067
3093
|
}
|
|
3068
3094
|
function resolveWhatsAppDeliverablePayload(payload, info) {
|
|
3069
3095
|
if (payload.isReasoning === true || payload.isCompactionNotice === true) return null;
|
|
3070
|
-
if (payload.isError === true) return null;
|
|
3096
|
+
if (payload.isError === true && info.kind !== "final") return null;
|
|
3071
3097
|
if (info.kind === "tool") {
|
|
3072
3098
|
if (!resolveSendableOutboundReplyParts(payload).hasMedia) return null;
|
|
3073
3099
|
return {
|
|
@@ -3077,9 +3103,6 @@ function resolveWhatsAppDeliverablePayload(payload, info) {
|
|
|
3077
3103
|
}
|
|
3078
3104
|
return payload;
|
|
3079
3105
|
}
|
|
3080
|
-
function getWhatsAppPayloadMediaUrls(payload) {
|
|
3081
|
-
return new Set(normalizeStringEntries([...Array.isArray(payload.mediaUrls) ? payload.mediaUrls : [], ...typeof payload.mediaUrl === "string" ? [payload.mediaUrl] : []]));
|
|
3082
|
-
}
|
|
3083
3106
|
function hasWhatsAppMediaUrlOverlap(left, right) {
|
|
3084
3107
|
for (const url of left) if (right.has(url)) return true;
|
|
3085
3108
|
return false;
|
|
@@ -3127,16 +3150,28 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
|
|
|
3127
3150
|
return finalization;
|
|
3128
3151
|
},
|
|
3129
3152
|
flushNonDuplicateMedia: (mediaUrls) => flushWhere((pending) => !hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)),
|
|
3130
|
-
|
|
3153
|
+
supersedeMedia(mediaUrl) {
|
|
3131
3154
|
const flushResult = {
|
|
3132
3155
|
delivered: 0,
|
|
3133
3156
|
droppedDuplicateMedia: 0
|
|
3134
3157
|
};
|
|
3135
3158
|
const retained = [];
|
|
3136
|
-
for (const pending of pendingMediaOnlyPayloads.splice(0))
|
|
3137
|
-
pending.
|
|
3138
|
-
|
|
3139
|
-
|
|
3159
|
+
for (const pending of pendingMediaOnlyPayloads.splice(0)) {
|
|
3160
|
+
if (pending.mediaUrls.delete(mediaUrl)) {
|
|
3161
|
+
flushResult.droppedDuplicateMedia += 1;
|
|
3162
|
+
const mediaUrls = [...pending.mediaUrls];
|
|
3163
|
+
pending.payload = {
|
|
3164
|
+
...pending.payload,
|
|
3165
|
+
mediaUrl: mediaUrls[0],
|
|
3166
|
+
mediaUrls
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
if (pending.mediaUrls.size === 0) {
|
|
3170
|
+
pending.resolveFinalization(whatsAppReplyDeliveryVisibility(false));
|
|
3171
|
+
continue;
|
|
3172
|
+
}
|
|
3173
|
+
retained.push(pending);
|
|
3174
|
+
}
|
|
3140
3175
|
pendingMediaOnlyPayloads.push(...retained);
|
|
3141
3176
|
return flushResult;
|
|
3142
3177
|
},
|
|
@@ -3145,7 +3180,7 @@ function createWhatsAppMediaOnlyReplyCoalescer(params) {
|
|
|
3145
3180
|
}
|
|
3146
3181
|
function logWhatsAppMediaOnlyFlushResult(result) {
|
|
3147
3182
|
if (!shouldLogVerbose$1()) return;
|
|
3148
|
-
if (result.droppedDuplicateMedia > 0) logVerbose$1(`
|
|
3183
|
+
if (result.droppedDuplicateMedia > 0) logVerbose$1(`Superseded ${result.droppedDuplicateMedia} deferred WhatsApp attachment(s) with accepted replacement media`);
|
|
3149
3184
|
if (result.delivered > 0) logVerbose$1(`Flushed ${result.delivered} deferred media-only WhatsApp reply payload(s)`);
|
|
3150
3185
|
}
|
|
3151
3186
|
function resolveWhatsAppResponsePrefix(params) {
|
|
@@ -3207,7 +3242,8 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3207
3242
|
from: conversationId,
|
|
3208
3243
|
sender: {
|
|
3209
3244
|
id: params.sender.id ?? params.sender.e164,
|
|
3210
|
-
name: params.sender.name
|
|
3245
|
+
name: params.sender.name,
|
|
3246
|
+
isSelf: params.msg.platform.fromMe === true
|
|
3211
3247
|
},
|
|
3212
3248
|
conversation: {
|
|
3213
3249
|
kind: conversationKind,
|
|
@@ -3316,7 +3352,8 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3316
3352
|
didSendReply = true;
|
|
3317
3353
|
if (shouldLogVerbose$1()) {
|
|
3318
3354
|
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3319
|
-
|
|
3355
|
+
const preview = payload.text != null ? reply.text : "<media>";
|
|
3356
|
+
logVerbose$1(`Reply body: ${preview}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
|
|
3320
3357
|
}
|
|
3321
3358
|
};
|
|
3322
3359
|
const deliverNormalizedPayload = async (normalizedDeliveryPayload, info, options) => {
|
|
@@ -3335,7 +3372,8 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3335
3372
|
replyLogger: params.replyLogger,
|
|
3336
3373
|
connectionId: params.connectionId,
|
|
3337
3374
|
skipLog: false,
|
|
3338
|
-
tableMode
|
|
3375
|
+
tableMode,
|
|
3376
|
+
onMediaAccepted: options?.onMediaAccepted
|
|
3339
3377
|
});
|
|
3340
3378
|
} catch (error) {
|
|
3341
3379
|
if (isWhatsAppVisibleDeliveryError(error) && !isChannelPartialDeliveryError(error)) throw createChannelPartialDeliveryError(error, {
|
|
@@ -3397,7 +3435,7 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3397
3435
|
} : normalizedOutboundPayload;
|
|
3398
3436
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3399
3437
|
if (!reply.hasMedia && !reply.text.trim()) return normalizedDeliveryPayload;
|
|
3400
|
-
const mediaUrls =
|
|
3438
|
+
const mediaUrls = new Set(normalizedDeliveryPayload.mediaUrls);
|
|
3401
3439
|
logWhatsAppMediaOnlyFlushResult(reply.hasMedia ? shouldDeferWhatsAppMediaOnlyPayload({
|
|
3402
3440
|
info,
|
|
3403
3441
|
mediaUrls,
|
|
@@ -3431,7 +3469,7 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3431
3469
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3432
3470
|
if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
|
|
3433
3471
|
if (!reply.hasMedia) return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { recordDelivery: false });
|
|
3434
|
-
const mediaUrls =
|
|
3472
|
+
const mediaUrls = new Set(normalizedDeliveryPayload.mediaUrls);
|
|
3435
3473
|
if (shouldDeferWhatsAppMediaOnlyPayload({
|
|
3436
3474
|
info,
|
|
3437
3475
|
mediaUrls,
|
|
@@ -3444,14 +3482,10 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3444
3482
|
payload: normalizedDeliveryPayload
|
|
3445
3483
|
})
|
|
3446
3484
|
};
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
} catch (error) {
|
|
3452
|
-
if (isWhatsAppVisibleDeliveryError(error)) logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.dropDuplicateMedia(mediaUrls));
|
|
3453
|
-
throw error;
|
|
3454
|
-
}
|
|
3485
|
+
return await deliverNormalizedPayload(normalizedDeliveryPayload, info, { onMediaAccepted: (mediaUrl) => {
|
|
3486
|
+
didSendReply = true;
|
|
3487
|
+
logWhatsAppMediaOnlyFlushResult(mediaOnlyCoalescer.supersedeMedia(mediaUrl));
|
|
3488
|
+
} });
|
|
3455
3489
|
},
|
|
3456
3490
|
onDelivered: (payload, _info, result) => {
|
|
3457
3491
|
if (!resolveSendableOutboundReplyParts(payload).hasMedia && result?.visibleReplySent === true) recordDeliveredPayload(payload);
|
|
@@ -3716,27 +3750,30 @@ async function processMessage(params) {
|
|
|
3716
3750
|
}) : void 0;
|
|
3717
3751
|
if (conversationKind === "group") {
|
|
3718
3752
|
const history = visibleGroupHistory ?? [];
|
|
3719
|
-
if (history.length > 0)
|
|
3720
|
-
|
|
3753
|
+
if (history.length > 0) {
|
|
3754
|
+
const historyEntries = history.map((m) => ({
|
|
3721
3755
|
sender: m.sender,
|
|
3722
3756
|
body: m.body,
|
|
3723
3757
|
timestamp: m.timestamp,
|
|
3724
3758
|
media: m.media
|
|
3725
|
-
}))
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3759
|
+
}));
|
|
3760
|
+
combinedBody = buildHistoryContextFromEntries({
|
|
3761
|
+
entries: historyEntries,
|
|
3762
|
+
currentMessage: combinedBody,
|
|
3763
|
+
excludeLast: false,
|
|
3764
|
+
formatEntry: (entry) => {
|
|
3765
|
+
return formatInboundEnvelope$1({
|
|
3766
|
+
channel: "WhatsApp",
|
|
3767
|
+
from: conversationId,
|
|
3768
|
+
timestamp: entry.timestamp,
|
|
3769
|
+
body: [entry.body, formatMediaPlaceholderText(entry.media ?? [])].filter(Boolean).join("\n"),
|
|
3770
|
+
chatType: "group",
|
|
3771
|
+
senderLabel: entry.sender,
|
|
3772
|
+
envelope: envelopeOptions
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3775
|
+
});
|
|
3776
|
+
}
|
|
3740
3777
|
shouldClearGroupHistory = !(params.suppressGroupHistoryClear ?? false);
|
|
3741
3778
|
}
|
|
3742
3779
|
const statusReactionController = params.statusReactionController ?? (params.cfg.messages?.statusReactions?.enabled === true && !params.ackAlreadySent ? await createWhatsAppStatusReactionController({
|
|
@@ -4263,8 +4300,8 @@ function normalizeReconnectAccountId(accountId) {
|
|
|
4263
4300
|
function isRetryableAuthUnstableError(error) {
|
|
4264
4301
|
return error instanceof WhatsAppAuthUnstableError || typeof error === "object" && error !== null && "code" in error && error.code === "whatsapp-auth-unstable";
|
|
4265
4302
|
}
|
|
4266
|
-
const DEFAULT_TRANSPORT_TIMEOUT_MS =
|
|
4267
|
-
const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS =
|
|
4303
|
+
const DEFAULT_TRANSPORT_TIMEOUT_MS = 3e5;
|
|
4304
|
+
const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS = 12e5;
|
|
4268
4305
|
async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSocket, keepAlive = true, replyResolver, runtime = defaultRuntime, abortSignal, tuning = {}) {
|
|
4269
4306
|
const activeReplyResolver = replyResolver ?? (await loadReplyResolverRuntime()).getReplyFromConfig;
|
|
4270
4307
|
const runId = newConnectionId();
|
|
@@ -4309,9 +4346,9 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4309
4346
|
};
|
|
4310
4347
|
process.once("SIGINT", handleSigint);
|
|
4311
4348
|
const transportTimeoutMs = tuning.transportTimeoutMs ?? DEFAULT_TRANSPORT_TIMEOUT_MS;
|
|
4312
|
-
const messageTimeoutMs = tuning.messageTimeoutMs ??
|
|
4349
|
+
const messageTimeoutMs = tuning.messageTimeoutMs ?? 18e5;
|
|
4313
4350
|
const reconnectCatchUpWindowMs = Math.min(Math.max(messageTimeoutMs, 6e4), WHATSAPP_RECONNECT_CATCH_UP_MAX_MS);
|
|
4314
|
-
const watchdogCheckMs = tuning.watchdogCheckMs ??
|
|
4351
|
+
const watchdogCheckMs = tuning.watchdogCheckMs ?? 6e4;
|
|
4315
4352
|
const controller = new WhatsAppConnectionController({
|
|
4316
4353
|
accountId: account.accountId,
|
|
4317
4354
|
authDir: account.authDir,
|
|
@@ -4333,10 +4370,6 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4333
4370
|
while (true) {
|
|
4334
4371
|
if (stopRequested()) break;
|
|
4335
4372
|
const connectionId = newConnectionId();
|
|
4336
|
-
const inboundDebounceMs = resolveInboundDebounceMs({
|
|
4337
|
-
cfg,
|
|
4338
|
-
channel: "whatsapp"
|
|
4339
|
-
});
|
|
4340
4373
|
const shouldDebounce = (msg) => shouldDebounceTextInbound({
|
|
4341
4374
|
text: msg.payload.commandBody ?? msg.payload.body,
|
|
4342
4375
|
cfg,
|
|
@@ -4381,7 +4414,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4381
4414
|
selfChatMode: account.selfChatMode,
|
|
4382
4415
|
sendReadReceipts: account.sendReadReceipts,
|
|
4383
4416
|
socketTiming,
|
|
4384
|
-
debounceMs:
|
|
4417
|
+
debounceMs: tuning.debounceMs,
|
|
4385
4418
|
appendReplyWindow: connectionLocal.openedAfterRecentInbound ? {
|
|
4386
4419
|
afterMs: connectionLocal.startedAt - reconnectCatchUpWindowMs,
|
|
4387
4420
|
untilMs: connectionLocal.startedAt + reconnectCatchUpWindowMs,
|