@openclaw/whatsapp 2026.7.2-beta.7 → 2026.7.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{access-control-pZcxTmd5.js → .setup/access-control-C0jumQuO.mjs} +5 -4
- package/dist/.setup/account-config-6PTOSfaI.mjs +47 -0
- package/dist/{accounts-CSqLvCY5.js → .setup/accounts-Dw16pdNl.mjs} +9 -47
- package/dist/{action-runtime-BzfDFPbH.js → .setup/action-runtime-DLK_9v4r.mjs} +3 -3
- package/dist/{active-listener-BEjWsCNM.js → .setup/active-listener-CloPBx0S.mjs} +3 -3
- package/dist/{agent-tools-login-DE9MU98T.js → .setup/agent-tools-login-DINqgaYo.mjs} +19 -7
- package/dist/{approval-handler.runtime-DrA59QSA.js → .setup/approval-handler.runtime-DtJZbu3y.mjs} +14 -20
- package/dist/{approval-resolver-DoeBBt22.js → .setup/approval-resolver-DF-lu8k5.mjs} +1 -2
- package/dist/{auth-store-DcEpIOmE.js → .setup/auth-store-BZKGa0tW.mjs} +25 -29
- package/dist/{channel-Dg62olhy.js → .setup/channel-9WGjBznd.mjs} +117 -132
- package/dist/{channel-react-action-J_YtyMGX.js → .setup/channel-react-action-mvIbAFhn.mjs} +15 -12
- package/dist/{channel.runtime-BP0xNmLi.js → .setup/channel.runtime-DmmMgRJQ.mjs} +6 -6
- package/dist/.setup/channel.setup-ByBYSeLd.mjs +22 -0
- package/dist/.setup/config-schema-mBX2nmn1.mjs +47 -0
- package/dist/{connection-controller-BOB2MVKY.js → .setup/connection-controller-Dt62u6fI.mjs} +29 -226
- package/dist/.setup/connection-controller-registry-TSX_udJp.mjs +22 -0
- package/dist/{constants-HU41RHGI.js → .setup/constants-C7m81BNe.mjs} +1 -1
- package/dist/.setup/directory-config-B7Obxrq2.mjs +37 -0
- package/dist/.setup/doctor-By3SB4lm.mjs +48 -0
- package/dist/{group-session-key-DVMZlO3x.js → .setup/group-session-key-CEwPRRRK.mjs} +5 -38
- package/dist/{identity-CPrT5aEU.js → .setup/identity-CDB1bT-p.mjs} +2 -2
- package/dist/.setup/legacy-session-surface-api-B2szjq4r.mjs +2 -0
- package/dist/.setup/legacy-state-migrations-api-DXCxufZz.mjs +3 -0
- package/dist/{login-CvY2Dhch.js → .setup/login-7b3M0iBp.mjs} +12 -51
- package/dist/{login-qr-Bncpb6js.js → .setup/login-qr-CbyA4QX5.mjs} +47 -23
- package/dist/{monitor-CfkaUSL2.js → .setup/monitor-Dh6Di_YO.mjs} +1451 -1843
- package/dist/.setup/normalize-eddWWsNV.mjs +2 -0
- package/dist/{normalize-target-CugRyONv.js → .setup/normalize-target-bVWjgftN.mjs} +10 -18
- package/dist/{resolve-outbound-target-1GqbT1uX.js → .setup/resolve-outbound-target-oI7VpU4w.mjs} +1 -1
- package/dist/.setup/runtime-BfAdAEYT.mjs +8 -0
- package/dist/.setup/send-BQjMC41n.mjs +777 -0
- package/dist/{send-api-7wk6eQyW.js → .setup/send-api-BiAkPLr3.mjs} +155 -105
- package/dist/{session-errors-JuazczrA.js → .setup/session-errors-BAj9D2La.mjs} +1 -2
- package/dist/.setup/session-vUI41Jel.mjs +252 -0
- package/dist/.setup/session.runtime-B-V3rXP6.mjs +12 -0
- package/dist/{shared-CAxd15bp.js → .setup/setup-core--FfzntMG.mjs} +94 -63
- package/dist/{setup-finalize-BZucx24W.js → .setup/setup-finalize-DCruIciL.mjs} +17 -46
- package/dist/.setup/setup-plugin-api-B6ck_20D.mjs +3 -0
- package/dist/{setup-surface-BkVcd769.js → .setup/setup-surface-Zvj2cLzz.mjs} +4 -4
- package/dist/.setup/socket-timing-DhbVFjah.mjs +77 -0
- package/dist/{state-migrations-CAMTrs2U.js → .setup/state-migrations-D_BmQUR9.mjs} +8 -1
- package/dist/.setup/targets-runtime-C-GiVn6Y.mjs +188 -0
- package/dist/.setup/text-runtime-CUrq1mFq.mjs +4 -0
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/{call-tool-api.js → agent-tools-api.js} +26 -11
- package/dist/api.js +27 -17
- package/dist/auth-presence.js +1 -1
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/constants.js +2 -0
- package/dist/contract-api.js +5 -5
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +5 -46
- package/dist/index.js +4 -4
- package/dist/legacy-session-surface-api.js +1 -1
- package/dist/legacy-state-migrations-api.js +1 -1
- package/dist/light-runtime-api.js +4 -4
- package/dist/login-qr-runtime.js +1 -1
- package/dist/runtime-api.js +14 -14
- package/dist/runtime-setter-api.js +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/setup-entry.js +4 -3
- package/dist/setup-plugin-api.js +1 -1
- package/dist/targets.js +2 -0
- package/node_modules/audio-decode/node_modules/@thi.ng/bitstream/package.json +4 -4
- package/node_modules/audio-decode/node_modules/@thi.ng/errors/package.json +4 -4
- package/node_modules/audio-decode/node_modules/audio-type/package.json +2 -3
- package/node_modules/baileys/node_modules/@protobufjs/aspromise/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/aspromise/README.md +13 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/aspromise.d.ts → @protobufjs/aspromise/index.d.ts} +13 -13
- package/node_modules/baileys/node_modules/{protobufjs/src/util/aspromise.js → @protobufjs/aspromise/index.js} +52 -52
- package/node_modules/baileys/node_modules/@protobufjs/aspromise/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/aspromise/tests/index.js +130 -0
- package/node_modules/baileys/node_modules/@protobufjs/base64/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/base64/README.md +19 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/base64.d.ts → @protobufjs/base64/index.d.ts} +32 -32
- package/node_modules/baileys/node_modules/{protobufjs/src/util/base64.js → @protobufjs/base64/index.js} +139 -146
- package/node_modules/baileys/node_modules/@protobufjs/base64/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/base64/tests/index.js +46 -0
- package/node_modules/baileys/node_modules/@protobufjs/codegen/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/codegen/README.md +49 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/codegen.d.ts → @protobufjs/codegen/index.d.ts} +31 -31
- package/node_modules/baileys/node_modules/{protobufjs/src/util/codegen.js → @protobufjs/codegen/index.js} +112 -118
- package/node_modules/baileys/node_modules/@protobufjs/codegen/package.json +13 -0
- package/node_modules/baileys/node_modules/@protobufjs/codegen/tests/index.js +13 -0
- package/node_modules/baileys/node_modules/@protobufjs/eventemitter/CHANGELOG.md +8 -0
- package/node_modules/baileys/node_modules/@protobufjs/eventemitter/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/eventemitter/README.md +22 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/eventemitter.d.ts → @protobufjs/eventemitter/index.d.ts} +45 -45
- package/node_modules/baileys/node_modules/{protobufjs/src/util/eventemitter.js → @protobufjs/eventemitter/index.js} +86 -86
- package/node_modules/baileys/node_modules/@protobufjs/eventemitter/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/eventemitter/tests/index.js +83 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/README.md +13 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/fetch.d.ts → @protobufjs/fetch/index.d.ts} +56 -56
- package/node_modules/baileys/node_modules/{protobufjs/src/util/fetch.js → @protobufjs/fetch/index.js} +112 -112
- package/node_modules/baileys/node_modules/@protobufjs/fetch/package.json +27 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/tests/index.js +158 -0
- package/node_modules/baileys/node_modules/@protobufjs/fetch/util/fs.js +11 -0
- package/node_modules/baileys/node_modules/@protobufjs/float/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/float/README.md +102 -0
- package/node_modules/baileys/node_modules/@protobufjs/float/bench/index.js +87 -0
- package/node_modules/baileys/node_modules/@protobufjs/float/bench/suite.js +46 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/float.js → @protobufjs/float/index.js} +335 -335
- package/node_modules/baileys/node_modules/@protobufjs/float/package.json +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/float/tests/index.js +100 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/README.md +13 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/index.d.ts +10 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/index.js +38 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/tests/data/array.js +1 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/tests/data/emptyArray.js +1 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/tests/data/emptyObject.js +1 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/tests/data/object.js +1 -0
- package/node_modules/baileys/node_modules/@protobufjs/inquire/tests/index.js +20 -0
- package/node_modules/baileys/node_modules/@protobufjs/path/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/path/README.md +19 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/path.d.ts → @protobufjs/path/index.d.ts} +22 -22
- package/node_modules/baileys/node_modules/{protobufjs/src/util/path.js → @protobufjs/path/index.js} +65 -100
- package/node_modules/baileys/node_modules/@protobufjs/path/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/path/tests/index.js +60 -0
- package/node_modules/baileys/node_modules/@protobufjs/pool/.npmignore +3 -0
- package/node_modules/baileys/node_modules/@protobufjs/pool/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/pool/README.md +13 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/pool.d.ts → @protobufjs/pool/index.d.ts} +32 -32
- package/node_modules/baileys/node_modules/{protobufjs/src/util/pool.js → @protobufjs/pool/index.js} +48 -48
- package/node_modules/baileys/node_modules/@protobufjs/pool/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/pool/tests/index.js +33 -0
- package/node_modules/baileys/node_modules/@protobufjs/utf8/LICENSE +26 -0
- package/node_modules/baileys/node_modules/@protobufjs/utf8/README.md +20 -0
- package/node_modules/baileys/node_modules/{protobufjs/src/util/utf8.d.ts → @protobufjs/utf8/index.d.ts} +24 -24
- package/node_modules/baileys/node_modules/{protobufjs/src/util/utf8.js → @protobufjs/utf8/index.js} +104 -190
- package/node_modules/baileys/node_modules/@protobufjs/utf8/package.json +21 -0
- package/node_modules/baileys/node_modules/@protobufjs/utf8/tests/data/surrogate_pair_bug.txt +207 -0
- package/node_modules/baileys/node_modules/@protobufjs/utf8/tests/data/utf8.txt +216 -0
- package/node_modules/baileys/node_modules/@protobufjs/utf8/tests/index.js +74 -0
- package/node_modules/baileys/node_modules/@types/node/LICENSE +21 -0
- package/node_modules/baileys/node_modules/@types/node/README.md +15 -0
- package/node_modules/baileys/node_modules/@types/node/assert/strict.d.ts +68 -0
- package/node_modules/baileys/node_modules/@types/node/assert.d.ts +1077 -0
- package/node_modules/baileys/node_modules/@types/node/async_hooks.d.ts +711 -0
- package/node_modules/baileys/node_modules/@types/node/buffer.buffer.d.ts +466 -0
- package/node_modules/baileys/node_modules/@types/node/buffer.d.ts +1799 -0
- package/node_modules/baileys/node_modules/@types/node/child_process.d.ts +1362 -0
- package/node_modules/baileys/node_modules/@types/node/cluster.d.ts +432 -0
- package/node_modules/baileys/node_modules/@types/node/console.d.ts +93 -0
- package/node_modules/baileys/node_modules/@types/node/constants.d.ts +14 -0
- package/node_modules/baileys/node_modules/@types/node/crypto.d.ts +3954 -0
- package/node_modules/baileys/node_modules/@types/node/dgram.d.ts +537 -0
- package/node_modules/baileys/node_modules/@types/node/diagnostics_channel.d.ts +794 -0
- package/node_modules/baileys/node_modules/@types/node/dns/promises.d.ts +497 -0
- package/node_modules/baileys/node_modules/@types/node/dns.d.ts +876 -0
- package/node_modules/baileys/node_modules/@types/node/domain.d.ts +150 -0
- package/node_modules/baileys/node_modules/@types/node/events.d.ts +1008 -0
- package/node_modules/baileys/node_modules/@types/node/ffi.d.ts +486 -0
- package/node_modules/baileys/node_modules/@types/node/fs/promises.d.ts +1471 -0
- package/node_modules/baileys/node_modules/@types/node/fs.d.ts +4786 -0
- package/node_modules/baileys/node_modules/@types/node/globals.d.ts +150 -0
- package/node_modules/baileys/node_modules/@types/node/globals.typedarray.d.ts +101 -0
- package/node_modules/baileys/node_modules/@types/node/http.d.ts +2172 -0
- package/node_modules/baileys/node_modules/@types/node/http2.d.ts +2489 -0
- package/node_modules/baileys/node_modules/@types/node/https.d.ts +400 -0
- package/node_modules/baileys/node_modules/@types/node/index.d.ts +115 -0
- package/node_modules/baileys/node_modules/@types/node/inspector/promises.d.ts +35 -0
- package/node_modules/baileys/node_modules/@types/node/inspector.d.ts +264 -0
- package/node_modules/baileys/node_modules/@types/node/inspector.generated.d.ts +4406 -0
- package/node_modules/baileys/node_modules/@types/node/module.d.ts +726 -0
- package/node_modules/baileys/node_modules/@types/node/net.d.ts +970 -0
- package/node_modules/baileys/node_modules/@types/node/os.d.ts +498 -0
- package/node_modules/baileys/node_modules/@types/node/package.json +155 -0
- package/node_modules/baileys/node_modules/@types/node/path/posix.d.ts +8 -0
- package/node_modules/baileys/node_modules/@types/node/path/win32.d.ts +8 -0
- package/node_modules/baileys/node_modules/@types/node/path.d.ts +178 -0
- package/node_modules/baileys/node_modules/@types/node/perf_hooks.d.ts +604 -0
- package/node_modules/baileys/node_modules/@types/node/process.d.ts +2217 -0
- package/node_modules/baileys/node_modules/@types/node/punycode.d.ts +89 -0
- package/node_modules/baileys/node_modules/@types/node/querystring.d.ts +139 -0
- package/node_modules/baileys/node_modules/@types/node/quic.d.ts +978 -0
- package/node_modules/baileys/node_modules/@types/node/readline/promises.d.ts +158 -0
- package/node_modules/baileys/node_modules/@types/node/readline.d.ts +508 -0
- package/node_modules/baileys/node_modules/@types/node/repl.d.ts +420 -0
- package/node_modules/baileys/node_modules/@types/node/sea.d.ts +47 -0
- package/node_modules/baileys/node_modules/@types/node/sqlite.d.ts +1123 -0
- package/node_modules/baileys/node_modules/@types/node/stream/consumers.d.ts +114 -0
- package/node_modules/baileys/node_modules/@types/node/stream/iter.d.ts +451 -0
- package/node_modules/baileys/node_modules/@types/node/stream/promises.d.ts +211 -0
- package/node_modules/baileys/node_modules/@types/node/stream/web.d.ts +300 -0
- package/node_modules/baileys/node_modules/@types/node/stream.d.ts +1806 -0
- package/node_modules/baileys/node_modules/@types/node/string_decoder.d.ts +27 -0
- package/node_modules/baileys/node_modules/@types/node/test/reporters.d.ts +59 -0
- package/node_modules/baileys/node_modules/@types/node/test.d.ts +2389 -0
- package/node_modules/baileys/node_modules/@types/node/timers/promises.d.ts +93 -0
- package/node_modules/baileys/node_modules/@types/node/timers.d.ts +149 -0
- package/node_modules/baileys/node_modules/@types/node/tls.d.ts +1193 -0
- package/node_modules/baileys/node_modules/@types/node/trace_events.d.ts +103 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.6/index.d.ts +117 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
- package/node_modules/baileys/node_modules/@types/node/ts5.7/index.d.ts +117 -0
- package/node_modules/baileys/node_modules/@types/node/tty.d.ts +225 -0
- package/node_modules/baileys/node_modules/@types/node/url.d.ts +556 -0
- package/node_modules/baileys/node_modules/@types/node/util/types.d.ts +558 -0
- package/node_modules/baileys/node_modules/@types/node/util.d.ts +1677 -0
- package/node_modules/baileys/node_modules/@types/node/v8.d.ts +1062 -0
- package/node_modules/baileys/node_modules/@types/node/vm.d.ts +1136 -0
- package/node_modules/baileys/node_modules/@types/node/wasi.d.ts +131 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/blob.d.ts +23 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/console.d.ts +9 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/domexception.d.ts +83 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/events.d.ts +106 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/performance.d.ts +45 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/storage.d.ts +24 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/streams.d.ts +115 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/timers.d.ts +44 -0
- package/node_modules/baileys/node_modules/@types/node/web-globals/url.d.ts +24 -0
- package/node_modules/baileys/node_modules/@types/node/worker_threads.d.ts +669 -0
- package/node_modules/baileys/node_modules/@types/node/zlib/iter.d.ts +131 -0
- package/node_modules/baileys/node_modules/@types/node/zlib.d.ts +589 -0
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/browser/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/browser/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/node/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/node/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/browser/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/browser/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/browser/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/node/index.js +4 -11
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/node/index.js.map +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/node/index.min.js +1 -1
- package/node_modules/baileys/node_modules/lru-cache/dist/esm/node/index.min.js.map +3 -3
- package/node_modules/baileys/node_modules/lru-cache/package.json +1 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/common/Util.d.ts +1 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/common/Util.js +1 -7
- package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameHeader.js +3 -23
- package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameParser.js +2 -2
- package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2Token.js +1 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/mp4/AtomToken.js +13 -38
- package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/MpegParser.js +1 -1
- package/node_modules/baileys/node_modules/music-metadata/package.json +6 -6
- package/node_modules/baileys/node_modules/p-queue/dist/index.js +27 -37
- package/node_modules/baileys/node_modules/p-queue/package.json +1 -1
- package/node_modules/baileys/node_modules/protobufjs/README.md +553 -266
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.js +1985 -3177
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.js +1244 -1966
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.js +2005 -3330
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor/README.md +70 -3
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor/index.d.ts +193 -0
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor/index.js +1185 -1
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor/test.js +54 -0
- package/node_modules/baileys/node_modules/protobufjs/google/api/annotations.json +1 -3
- package/node_modules/baileys/node_modules/protobufjs/google/api/http.json +1 -2
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/api.json +6 -12
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/descriptor.json +87 -170
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/descriptor.proto +8 -0
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/source_context.json +1 -2
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/type.json +7 -14
- package/node_modules/baileys/node_modules/protobufjs/index.d.ts +703 -1066
- package/node_modules/baileys/node_modules/protobufjs/package.json +52 -25
- package/node_modules/baileys/node_modules/protobufjs/scripts/postinstall.js +32 -0
- package/node_modules/baileys/node_modules/protobufjs/src/common.js +8 -14
- package/node_modules/baileys/node_modules/protobufjs/src/converter.js +51 -85
- package/node_modules/baileys/node_modules/protobufjs/src/decoder.js +61 -205
- package/node_modules/baileys/node_modules/protobufjs/src/encoder.js +7 -27
- package/node_modules/baileys/node_modules/protobufjs/src/enum.js +5 -18
- package/node_modules/baileys/node_modules/protobufjs/src/field.js +19 -71
- package/node_modules/baileys/node_modules/protobufjs/src/index-light.js +29 -29
- package/node_modules/baileys/node_modules/protobufjs/src/index-minimal.js +13 -12
- package/node_modules/baileys/node_modules/protobufjs/src/index.js +6 -6
- package/node_modules/baileys/node_modules/protobufjs/src/mapfield.js +8 -26
- package/node_modules/baileys/node_modules/protobufjs/src/message.js +11 -5
- package/node_modules/baileys/node_modules/protobufjs/src/method.js +7 -30
- package/node_modules/baileys/node_modules/protobufjs/src/namespace.js +10 -26
- package/node_modules/baileys/node_modules/protobufjs/src/object.js +31 -41
- package/node_modules/baileys/node_modules/protobufjs/src/oneof.js +1 -12
- package/node_modules/baileys/node_modules/protobufjs/src/parse.js +47 -169
- package/node_modules/baileys/node_modules/protobufjs/src/reader.js +50 -412
- package/node_modules/baileys/node_modules/protobufjs/src/reader_buffer.js +5 -31
- package/node_modules/baileys/node_modules/protobufjs/src/root.js +4 -16
- package/node_modules/baileys/node_modules/protobufjs/src/rpc/service.js +5 -18
- package/node_modules/baileys/node_modules/protobufjs/src/service.js +2 -15
- package/node_modules/baileys/node_modules/protobufjs/src/tokenize.js +1 -6
- package/node_modules/baileys/node_modules/protobufjs/src/type.js +36 -80
- package/node_modules/baileys/node_modules/protobufjs/src/typescript.jsdoc +15 -0
- package/node_modules/baileys/node_modules/protobufjs/src/util/longbits.js +6 -10
- package/node_modules/baileys/node_modules/protobufjs/src/util/minimal.js +54 -71
- package/node_modules/baileys/node_modules/protobufjs/src/util.js +17 -51
- package/node_modules/baileys/node_modules/protobufjs/src/verifier.js +14 -19
- package/node_modules/baileys/node_modules/protobufjs/src/wrappers.js +3 -2
- package/node_modules/baileys/node_modules/protobufjs/src/writer.js +191 -424
- package/node_modules/baileys/node_modules/protobufjs/src/writer_buffer.js +26 -41
- package/node_modules/baileys/node_modules/protobufjs/tsconfig.json +5 -3
- package/node_modules/baileys/node_modules/undici-types/LICENSE +21 -0
- package/node_modules/baileys/node_modules/undici-types/README.md +6 -0
- package/node_modules/baileys/node_modules/undici-types/agent.d.ts +30 -0
- package/node_modules/baileys/node_modules/undici-types/api.d.ts +43 -0
- package/node_modules/baileys/node_modules/undici-types/balanced-pool.d.ts +30 -0
- package/node_modules/baileys/node_modules/undici-types/cache-interceptor.d.ts +179 -0
- package/node_modules/baileys/node_modules/undici-types/cache.d.ts +36 -0
- package/node_modules/baileys/node_modules/undici-types/client-stats.d.ts +15 -0
- package/node_modules/baileys/node_modules/undici-types/client.d.ts +129 -0
- package/node_modules/baileys/node_modules/undici-types/connector.d.ts +36 -0
- package/node_modules/baileys/node_modules/undici-types/content-type.d.ts +21 -0
- package/node_modules/baileys/node_modules/undici-types/cookies.d.ts +30 -0
- package/node_modules/baileys/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
- package/node_modules/baileys/node_modules/undici-types/dispatcher.d.ts +253 -0
- package/node_modules/baileys/node_modules/undici-types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/baileys/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
- package/node_modules/baileys/node_modules/undici-types/errors.d.ts +177 -0
- package/node_modules/baileys/node_modules/undici-types/eventsource.d.ts +66 -0
- package/node_modules/baileys/node_modules/undici-types/fetch.d.ts +231 -0
- package/node_modules/baileys/node_modules/undici-types/formdata.d.ts +108 -0
- package/node_modules/baileys/node_modules/undici-types/global-dispatcher.d.ts +9 -0
- package/node_modules/baileys/node_modules/undici-types/global-origin.d.ts +7 -0
- package/node_modules/baileys/node_modules/undici-types/h2c-client.d.ts +73 -0
- package/node_modules/baileys/node_modules/undici-types/handlers.d.ts +14 -0
- package/node_modules/baileys/node_modules/undici-types/header.d.ts +165 -0
- package/node_modules/baileys/node_modules/undici-types/index.d.ts +93 -0
- package/node_modules/baileys/node_modules/undici-types/interceptors.d.ts +80 -0
- package/node_modules/baileys/node_modules/undici-types/mock-agent.d.ts +68 -0
- package/node_modules/baileys/node_modules/undici-types/mock-call-history.d.ts +111 -0
- package/node_modules/baileys/node_modules/undici-types/mock-client.d.ts +27 -0
- package/node_modules/baileys/node_modules/undici-types/mock-errors.d.ts +12 -0
- package/node_modules/baileys/node_modules/undici-types/mock-interceptor.d.ts +94 -0
- package/node_modules/baileys/node_modules/undici-types/mock-pool.d.ts +27 -0
- package/node_modules/baileys/node_modules/undici-types/package.json +55 -0
- package/node_modules/baileys/node_modules/undici-types/patch.d.ts +29 -0
- package/node_modules/baileys/node_modules/undici-types/pool-stats.d.ts +19 -0
- package/node_modules/baileys/node_modules/undici-types/pool.d.ts +39 -0
- package/node_modules/baileys/node_modules/undici-types/proxy-agent.d.ts +29 -0
- package/node_modules/baileys/node_modules/undici-types/readable.d.ts +68 -0
- package/node_modules/baileys/node_modules/undici-types/retry-agent.d.ts +8 -0
- package/node_modules/baileys/node_modules/undici-types/retry-handler.d.ts +125 -0
- package/node_modules/baileys/node_modules/undici-types/round-robin-pool.d.ts +39 -0
- package/node_modules/baileys/node_modules/undici-types/snapshot-agent.d.ts +113 -0
- package/node_modules/baileys/node_modules/undici-types/socks5-proxy-agent.d.ts +25 -0
- package/node_modules/baileys/node_modules/undici-types/util.d.ts +18 -0
- package/node_modules/baileys/node_modules/undici-types/utility.d.ts +7 -0
- package/node_modules/baileys/node_modules/undici-types/webidl.d.ts +346 -0
- package/node_modules/baileys/node_modules/undici-types/websocket.d.ts +188 -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/compile/validator.d.mts +4 -1
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +2 -5
- package/node_modules/typebox/build/guard/string.mjs +4 -16
- package/node_modules/typebox/build/schema/engine/_functions.mjs +15 -34
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +0 -5
- package/node_modules/typebox/build/type/action/readonly_object.mjs +0 -8
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +32 -29
- package/openclaw.plugin.json +239 -214
- package/package.json +11 -21
- package/dist/approval-reactions-Cu66aYdq.js +0 -373
- package/dist/channel.setup-pMwNVkCA.js +0 -19
- package/dist/config-schema-BQP5YCeK.js +0 -50
- package/dist/connection-controller-runtime-context-DG0tuYuw.js +0 -23
- package/dist/directory-config-DalGrfGN.js +0 -196
- package/dist/doctor-CwzVXKoE.js +0 -10
- package/dist/normalize-C7Hkc_vE.js +0 -2
- package/dist/question-reactions-CRsnyoMK.js +0 -142
- package/dist/runtime-BLlToOi6.js +0 -21
- package/dist/send-D37ddk22.js +0 -436
- package/dist/session.runtime-Nsqaehbd.js +0 -16
- package/dist/socket-close-DridEuzz.js +0 -622
- package/dist/targets-runtime-TMk0lGuy.js +0 -299
- package/dist/text-runtime-CaKLhowd.js +0 -4
- package/node_modules/baileys/node_modules/protobufjs/ext/README.md +0 -70
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor.d.ts +0 -87
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor.generated.d.ts +0 -417
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor.js +0 -1356
- package/node_modules/baileys/node_modules/protobufjs/ext/protojson.LICENSE +0 -201
- package/node_modules/baileys/node_modules/protobufjs/ext/protojson.d.ts +0 -20
- package/node_modules/baileys/node_modules/protobufjs/ext/protojson.generated.d.ts +0 -49
- package/node_modules/baileys/node_modules/protobufjs/ext/protojson.js +0 -951
- package/node_modules/baileys/node_modules/protobufjs/ext/textformat.d.ts +0 -19
- package/node_modules/baileys/node_modules/protobufjs/ext/textformat.generated.d.ts +0 -33
- package/node_modules/baileys/node_modules/protobufjs/ext/textformat.js +0 -1276
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/compiler/plugin.json +0 -126
- package/node_modules/baileys/node_modules/protobufjs/google/protobuf/compiler/plugin.proto +0 -47
- package/node_modules/baileys/node_modules/protobufjs/src/typescript.js +0 -25
- /package/dist/{account-ids-CB5SOWjc.js → .setup/account-ids-CB5SOWjc.mjs} +0 -0
- /package/dist/{audio-preflight.runtime-C_glQhZY.js → .setup/audio-preflight.runtime-C_glQhZY.mjs} +0 -0
- /package/dist/{command-policy-BIOSHySD.js → .setup/command-policy-BIOSHySD.mjs} +0 -0
- /package/dist/{creds-files-B1kSWtBg.js → .setup/creds-files-B1kSWtBg.mjs} +0 -0
- /package/dist/{group-config-path-BGyzT9Lg.js → .setup/group-config-path-BGyzT9Lg.mjs} +0 -0
- /package/dist/{group-session-contract-DDnZSsJ1.js → .setup/group-session-contract-DDnZSsJ1.mjs} +0 -0
- /package/dist/{reply-resolver.runtime-Ded8D58Y.js → .setup/reply-resolver.runtime-Ded8D58Y.mjs} +0 -0
- /package/dist/{rolldown-runtime-D7D4PA-g.js → .setup/rolldown-runtime-D7D4PA-g.mjs} +0 -0
- /package/dist/{security-contract-nAzD945y.js → .setup/security-contract-nAzD945y.mjs} +0 -0
- /package/dist/{session-contract-DMTm6Q_L.js → .setup/session-contract-DMTm6Q_L.mjs} +0 -0
- /package/node_modules/baileys/node_modules/{protobufjs/src/util/float.d.ts → @protobufjs/float/index.d.ts} +0 -0
|
@@ -1,87 +1,149 @@
|
|
|
1
|
-
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-
|
|
2
|
-
import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import { a as
|
|
10
|
-
import {
|
|
11
|
-
import { a as
|
|
12
|
-
import { n as
|
|
13
|
-
import { t as
|
|
14
|
-
import { c as
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { n as
|
|
18
|
-
import {
|
|
19
|
-
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
1
|
+
import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-Dw16pdNl.mjs";
|
|
2
|
+
import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, d as maybeResolveWhatsAppApprovalReaction, g as resolveWhatsAppReactionLevel, l as sendWhatsAppOutboundWithRetry, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation } from "./send-BQjMC41n.mjs";
|
|
3
|
+
import { n as getWhatsAppRuntime } from "./runtime-BfAdAEYT.mjs";
|
|
4
|
+
import { t as getRegisteredWhatsAppConnectionController } from "./connection-controller-registry-TSX_udJp.mjs";
|
|
5
|
+
import { n as getStatusCode, t as formatError } from "./session-errors-BAj9D2La.mjs";
|
|
6
|
+
import { a as resolveWhatsAppSocketTiming, i as resolveWhatsAppSocketOperationTimeoutMs, n as createWhatsAppSocketOperationTimeoutAdapter, o as withWhatsAppSocketOperationTimeout, r as isWhatsAppSocketOperationTimeoutError } from "./socket-timing-DhbVFjah.mjs";
|
|
7
|
+
import { a as resolveEquivalentWhatsAppDirectChatJids, c as toWhatsappJidWithLid, i as markdownToWhatsApp, n as isSelfChatMode, o as resolveJidToE164, r as jidToE164, s as toWhatsappJid } from "./targets-runtime-C-GiVn6Y.mjs";
|
|
8
|
+
import { n as normalizeE164, t as convertMarkdownTables$1 } from "./text-runtime-CUrq1mFq.mjs";
|
|
9
|
+
import { a as cacheInboundMessageMeta, i as buildQuotedMessageOptions, n as resolveWhatsAppGroupSessionRoute, o as lookupInboundMessageMeta, r as resolveWhatsAppLegacyGroupSessionKey } from "./group-session-key-CEwPRRRK.mjs";
|
|
10
|
+
import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-BZKGa0tW.mjs";
|
|
11
|
+
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-CDB1bT-p.mjs";
|
|
12
|
+
import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.mjs";
|
|
13
|
+
import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-C0jumQuO.mjs";
|
|
14
|
+
import { _ as extractLocationData, a as mayContainWhatsAppOutboundMention, b as extractText, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractExternalAdReplyContext, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMediaPlaceholder, x as hasInboundUserContent, y as extractMentionedJids } from "./send-api-BiAkPLr3.mjs";
|
|
15
|
+
import { r as waitForWaConnection, t as createWaSocket } from "./session-vUI41Jel.mjs";
|
|
16
|
+
import { t as BufferJSON } from "./session.runtime-B-V3rXP6.mjs";
|
|
17
|
+
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-Dt62u6fI.mjs";
|
|
18
|
+
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
|
|
19
|
+
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
20
|
+
import { createChannelMessageReplyPipeline, createDurableInboundReceiveJournalFromQueue, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-outbound";
|
|
20
21
|
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
21
|
-
import { createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
|
|
22
|
+
import { DEFAULT_TIMING, createAckReactionHandle, createStatusReactionController, logAckFailure, removeAckReactionHandleAfterReply, shouldAckReactionForWhatsApp } from "openclaw/plugin-sdk/channel-feedback";
|
|
22
23
|
import { formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
|
23
24
|
import { getChildLogger } from "openclaw/plugin-sdk/logging-core";
|
|
24
25
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
25
26
|
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";
|
|
26
|
-
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
27
|
-
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
28
|
-
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
29
|
-
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
30
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
31
27
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
32
|
-
import {
|
|
33
|
-
import { DEFAULT_ACCOUNT_ID, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
28
|
+
import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
|
|
34
29
|
import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
30
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
31
|
+
import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia as loadWebMedia$1, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
|
|
32
|
+
import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
|
|
33
|
+
import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
34
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId as normalizeAccountId$1, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
35
|
+
import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
|
|
37
36
|
import { getSessionEntry, patchSessionEntry, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
37
|
+
import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatInboundMediaUnavailableText, formatLocationText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, runChannelInboundEvent, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
38
|
+
import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
38
39
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
39
|
-
import {
|
|
40
|
+
import { createHash } from "node:crypto";
|
|
41
|
+
import { dispatchReplyWithBufferedBlockDispatcher, finalizeInboundContext, 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";
|
|
44
|
+
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
45
|
+
import { hasControlCommand, isControlCommandMessage, isControlCommandMessage as isControlCommandMessage$1, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
|
|
42
46
|
import { drainPendingDeliveries } from "openclaw/plugin-sdk/delivery-queue-runtime";
|
|
43
47
|
import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
44
48
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import { getRuntimeConfig as getRuntimeConfig$1 } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
49
|
+
import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
50
|
+
import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
51
|
+
import { getRuntimeConfig as getRuntimeConfig$1, getRuntimeConfigSourceSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
48
52
|
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
49
53
|
import { buildMentionRegexes, implicitMentionKindWhen, normalizeMentionText, resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
|
|
50
54
|
import { createConnectedChannelStatusPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
51
55
|
import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute } from "openclaw/plugin-sdk/conversation-binding-runtime";
|
|
52
56
|
import { resolveAgentIdentity, resolveIdentityNamePrefix as resolveIdentityNamePrefix$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
53
57
|
import { normalizeGroupActivation, parseActivationCommand } from "openclaw/plugin-sdk/group-activation";
|
|
54
|
-
import { hasControlCommand, isControlCommandMessage, shouldComputeCommandAuthorized } from "openclaw/plugin-sdk/command-detection";
|
|
55
58
|
import { createInternalHookEvent, deriveInboundMessageHookContext, fireAndForgetBoundedHook, toInternalMessageReceivedContext, toPluginMessageContext, toPluginMessageReceivedEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
|
|
56
59
|
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
57
60
|
import { resolveBatchedReplyThreadingPolicy } from "openclaw/plugin-sdk/reply-reference";
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
61
|
+
//#region extensions/whatsapp/src/inbound/dedupe.ts
|
|
62
|
+
const WHATSAPP_INBOUND_DEDUPE_TTL_MS = 12e5;
|
|
63
|
+
const RECENT_WEB_MESSAGE_MAX = 5e3;
|
|
64
|
+
const RECENT_OUTBOUND_MESSAGE_TTL_MS = 12e5;
|
|
65
|
+
const RECENT_OUTBOUND_MESSAGE_MAX = 5e3;
|
|
66
|
+
const claimableInboundMessages = createClaimableDedupe({
|
|
67
|
+
ttlMs: WHATSAPP_INBOUND_DEDUPE_TTL_MS,
|
|
68
|
+
memoryMaxSize: RECENT_WEB_MESSAGE_MAX
|
|
69
|
+
});
|
|
70
|
+
const recentOutboundMessages = createRecentMessageCache({
|
|
71
|
+
ttlMs: RECENT_OUTBOUND_MESSAGE_TTL_MS,
|
|
72
|
+
maxSize: RECENT_OUTBOUND_MESSAGE_MAX
|
|
73
|
+
});
|
|
74
|
+
function createRecentMessageCache(options) {
|
|
75
|
+
const ttlMs = Math.max(0, options.ttlMs);
|
|
76
|
+
const maxSize = Math.max(0, Math.floor(options.maxSize));
|
|
77
|
+
const cache = /* @__PURE__ */ new Map();
|
|
78
|
+
const prune = (now) => {
|
|
79
|
+
if (ttlMs > 0) {
|
|
80
|
+
const cutoff = now - ttlMs;
|
|
81
|
+
for (const [key, timestamp] of cache) if (timestamp < cutoff) cache.delete(key);
|
|
82
|
+
}
|
|
83
|
+
while (cache.size > maxSize) {
|
|
84
|
+
const oldest = cache.keys().next().value;
|
|
85
|
+
if (!oldest) break;
|
|
86
|
+
cache.delete(oldest);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const peek = (key, now = Date.now()) => {
|
|
90
|
+
if (!key) return false;
|
|
91
|
+
const timestamp = cache.get(key);
|
|
92
|
+
if (timestamp === void 0) return false;
|
|
93
|
+
if (ttlMs > 0 && now - timestamp >= ttlMs) {
|
|
94
|
+
cache.delete(key);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
check: (key, now = Date.now()) => {
|
|
101
|
+
if (!key) return false;
|
|
102
|
+
const existed = peek(key, now);
|
|
103
|
+
cache.delete(key);
|
|
104
|
+
cache.set(key, now);
|
|
105
|
+
prune(now);
|
|
106
|
+
return existed;
|
|
107
|
+
},
|
|
108
|
+
peek,
|
|
109
|
+
clear: () => cache.clear()
|
|
110
|
+
};
|
|
74
111
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
cache.delete(key);
|
|
80
|
-
return;
|
|
112
|
+
var WhatsAppRetryableInboundError = class extends Error {
|
|
113
|
+
constructor(message, options) {
|
|
114
|
+
super(message, options);
|
|
115
|
+
this.name = "WhatsAppRetryableInboundError";
|
|
81
116
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
};
|
|
118
|
+
function buildMessageKey(params) {
|
|
119
|
+
const accountId = params.accountId.trim();
|
|
120
|
+
const remoteJid = params.remoteJid.trim();
|
|
121
|
+
const messageId = params.messageId.trim();
|
|
122
|
+
if (!accountId || !remoteJid || !messageId || messageId === "unknown") return null;
|
|
123
|
+
return `${accountId}:${remoteJid}:${messageId}`;
|
|
124
|
+
}
|
|
125
|
+
function resetWebInboundDedupe() {
|
|
126
|
+
claimableInboundMessages.clearMemory();
|
|
127
|
+
recentOutboundMessages.clear();
|
|
128
|
+
}
|
|
129
|
+
async function claimRecentInboundMessageDelivery(key) {
|
|
130
|
+
return (await claimableInboundMessages.claim(key)).kind;
|
|
131
|
+
}
|
|
132
|
+
async function commitRecentInboundMessage(key) {
|
|
133
|
+
await claimableInboundMessages.commit(key);
|
|
134
|
+
}
|
|
135
|
+
function releaseRecentInboundMessage(key, error) {
|
|
136
|
+
claimableInboundMessages.release(key, { error });
|
|
137
|
+
}
|
|
138
|
+
function rememberRecentOutboundMessage(params) {
|
|
139
|
+
const key = buildMessageKey(params);
|
|
140
|
+
if (!key) return;
|
|
141
|
+
recentOutboundMessages.check(key);
|
|
142
|
+
}
|
|
143
|
+
function isRecentOutboundMessage(params) {
|
|
144
|
+
const key = buildMessageKey(params);
|
|
145
|
+
if (!key) return false;
|
|
146
|
+
return recentOutboundMessages.peek(key);
|
|
85
147
|
}
|
|
86
148
|
//#endregion
|
|
87
149
|
//#region extensions/whatsapp/src/inbound/message-aliases.ts
|
|
@@ -154,18 +216,6 @@ function defineDeprecatedAliasAccessors(msg, descriptors) {
|
|
|
154
216
|
defineDeprecatedAccessors(msg, descriptors);
|
|
155
217
|
return msg;
|
|
156
218
|
}
|
|
157
|
-
function defineDeprecatedStructuredContextPayloadAlias(msg) {
|
|
158
|
-
const channelStructuredContext = msg.payload.channelStructuredContext ?? msg.payload.untrustedStructuredContext;
|
|
159
|
-
if (channelStructuredContext !== void 0) msg.payload.channelStructuredContext = channelStructuredContext;
|
|
160
|
-
Object.defineProperty(msg.payload, "untrustedStructuredContext", {
|
|
161
|
-
configurable: true,
|
|
162
|
-
enumerable: true,
|
|
163
|
-
get: () => msg.payload.channelStructuredContext,
|
|
164
|
-
set: (value) => {
|
|
165
|
-
msg.payload.channelStructuredContext = value;
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
219
|
function defineDeprecatedAdmissionTopLevelAccessors(msg) {
|
|
170
220
|
let fallbackConversationId = msg.conversationId || msg.from;
|
|
171
221
|
let fallbackAccountId = msg.accountId;
|
|
@@ -201,12 +251,9 @@ function defineDeprecatedAdmissionTopLevelAccessors(msg) {
|
|
|
201
251
|
}
|
|
202
252
|
},
|
|
203
253
|
accessControlPassed: {
|
|
204
|
-
get: () =>
|
|
205
|
-
if (msg.admission?.ingress.decisiveGateId === "legacy-flat-compat") return fallbackAccessControlPassed;
|
|
206
|
-
return msg.admission ? msg.admission.ingress.decision === "allow" : fallbackAccessControlPassed;
|
|
207
|
-
},
|
|
254
|
+
get: () => msg.admission ? msg.admission.ingress.decision === "allow" : fallbackAccessControlPassed,
|
|
208
255
|
set: (value) => {
|
|
209
|
-
|
|
256
|
+
fallbackAccessControlPassed = value;
|
|
210
257
|
}
|
|
211
258
|
},
|
|
212
259
|
chatType: {
|
|
@@ -220,7 +267,6 @@ function defineDeprecatedAdmissionTopLevelAccessors(msg) {
|
|
|
220
267
|
});
|
|
221
268
|
}
|
|
222
269
|
function withDeprecatedWebInboundMessageFlatAliases(msg) {
|
|
223
|
-
defineDeprecatedStructuredContextPayloadAlias(msg);
|
|
224
270
|
return defineDeprecatedAliasAccessors(defineDeprecatedAdmissionTopLevelAccessors(msg), {
|
|
225
271
|
id: {
|
|
226
272
|
get: () => msg.event.id,
|
|
@@ -366,13 +412,9 @@ function withDeprecatedWebInboundMessageFlatAliases(msg) {
|
|
|
366
412
|
get: () => msg.payload.media?.url,
|
|
367
413
|
set: (value) => setMediaField(msg, "url", value)
|
|
368
414
|
},
|
|
369
|
-
channelStructuredContext: {
|
|
370
|
-
get: () => msg.payload.channelStructuredContext,
|
|
371
|
-
set: (value) => msg.payload.channelStructuredContext = value
|
|
372
|
-
},
|
|
373
415
|
untrustedStructuredContext: {
|
|
374
|
-
get: () => msg.payload.
|
|
375
|
-
set: (value) => msg.payload.
|
|
416
|
+
get: () => msg.payload.untrustedStructuredContext,
|
|
417
|
+
set: (value) => msg.payload.untrustedStructuredContext = value
|
|
376
418
|
},
|
|
377
419
|
isBatched: {
|
|
378
420
|
get: () => msg.event.isBatched,
|
|
@@ -399,7 +441,7 @@ function normalizeLegacyFlatWebInboundMessage(msg) {
|
|
|
399
441
|
body: msg.body,
|
|
400
442
|
media,
|
|
401
443
|
location: msg.location,
|
|
402
|
-
|
|
444
|
+
untrustedStructuredContext: msg.untrustedStructuredContext
|
|
403
445
|
},
|
|
404
446
|
platform: {
|
|
405
447
|
chatJid: msg.chatId,
|
|
@@ -427,304 +469,42 @@ function normalizeWebInboundMessage(msg) {
|
|
|
427
469
|
if (msg.event || msg.payload || msg.platform || msg.quote || msg.group) throw new Error("WhatsApp inbound messages must be either legacy flat or canonical nested; partial nested contexts are not supported.");
|
|
428
470
|
return normalizeLegacyFlatWebInboundMessage(msg);
|
|
429
471
|
}
|
|
430
|
-
function normalizeAdmittedWebInboundMessage(msg) {
|
|
431
|
-
return requireAdmittedWhatsAppInboundMessage(normalizeWebInboundMessage(msg));
|
|
432
|
-
}
|
|
433
|
-
//#endregion
|
|
434
|
-
//#region extensions/whatsapp/src/inbound/durable-payload.ts
|
|
435
|
-
var WhatsAppIngressPermanentError = class extends Error {
|
|
436
|
-
constructor(reason, message, options) {
|
|
437
|
-
super(message, options);
|
|
438
|
-
this.reason = reason;
|
|
439
|
-
this.name = "WhatsAppIngressPermanentError";
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
function serializeWhatsAppDurableInboundMessage(message) {
|
|
443
|
-
const timestamp = message.messageTimestamp;
|
|
444
|
-
let serializedMessage = message;
|
|
445
|
-
if (timestamp != null && typeof timestamp === "object") try {
|
|
446
|
-
const numericTimestamp = Number(timestamp);
|
|
447
|
-
if (Number.isFinite(numericTimestamp)) serializedMessage = {
|
|
448
|
-
...message,
|
|
449
|
-
messageTimestamp: numericTimestamp
|
|
450
|
-
};
|
|
451
|
-
} catch {}
|
|
452
|
-
return JSON.parse(JSON.stringify(serializedMessage, BufferJSON.replacer));
|
|
453
|
-
}
|
|
454
|
-
function deserializeWhatsAppDurableInboundMessage(message) {
|
|
455
|
-
try {
|
|
456
|
-
return JSON.parse(JSON.stringify(message), BufferJSON.reviver);
|
|
457
|
-
} catch (error) {
|
|
458
|
-
throw new WhatsAppIngressPermanentError("invalid-payload", "WhatsApp ingress row contains an invalid serialized message", { cause: error });
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
472
|
//#endregion
|
|
462
473
|
//#region extensions/whatsapp/src/inbound/durable-receive.ts
|
|
463
|
-
const
|
|
474
|
+
const WHATSAPP_DURABLE_INBOUND_PENDING_MAX_ENTRIES = 450;
|
|
475
|
+
const WHATSAPP_DURABLE_INBOUND_COMPLETED_MAX_ENTRIES = 450;
|
|
476
|
+
const WHATSAPP_DURABLE_INBOUND_PENDING_TTL_MS = 2592e6;
|
|
477
|
+
const WHATSAPP_DURABLE_INBOUND_COMPLETED_TTL_MS = 6048e5;
|
|
464
478
|
function hashNamespacePart(value) {
|
|
465
479
|
return createHash("sha256").update(value).digest("hex").slice(0, 24);
|
|
466
480
|
}
|
|
467
481
|
function createWhatsAppDurableInboundMessageId(params) {
|
|
468
482
|
return createHash("sha256").update(`${params.remoteJid}\n${params.id}`).digest("hex");
|
|
469
483
|
}
|
|
470
|
-
function
|
|
471
|
-
|
|
472
|
-
const id = message.key?.id?.trim();
|
|
473
|
-
if (!remoteJid || !id) throw new WhatsAppIngressPermanentError("missing-message-key", "WhatsApp ingress message is missing key.remoteJid or key.id");
|
|
474
|
-
return {
|
|
475
|
-
eventId: createWhatsAppDurableInboundMessageId({
|
|
476
|
-
remoteJid,
|
|
477
|
-
id
|
|
478
|
-
}),
|
|
479
|
-
laneKey: remoteJid
|
|
480
|
-
};
|
|
484
|
+
function serializeWhatsAppDurableInboundMessage(message) {
|
|
485
|
+
return JSON.parse(JSON.stringify(message, BufferJSON.replacer));
|
|
481
486
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
+
function deserializeWhatsAppDurableInboundMessage(message) {
|
|
488
|
+
return JSON.parse(JSON.stringify(message), BufferJSON.reviver);
|
|
489
|
+
}
|
|
490
|
+
function createWhatsAppDurableInboundReceiveJournal(accountId) {
|
|
491
|
+
const accountPart = hashNamespacePart(accountId);
|
|
492
|
+
const runtime = getWhatsAppRuntime();
|
|
493
|
+
const queue = runtime.state.openChannelIngressQueue({
|
|
494
|
+
accountId: accountPart,
|
|
495
|
+
stateDir: runtime.state.resolveStateDir()
|
|
487
496
|
});
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
return error instanceof WhatsAppIngressPermanentError ? {
|
|
491
|
-
reason: error.reason,
|
|
492
|
-
message: error.message
|
|
493
|
-
} : null;
|
|
494
|
-
}
|
|
495
|
-
/** Shared monitor with per-conversation lanes and completion at reply-lane adoption. */
|
|
496
|
-
function createWhatsAppIngressMonitor(params) {
|
|
497
|
-
return createChannelIngressMonitor({
|
|
498
|
-
queue: params.queue,
|
|
499
|
-
inspect: (admission) => inspectWhatsAppIngressMessage(admission.message),
|
|
500
|
-
payload: {
|
|
501
|
-
version: WHATSAPP_DURABLE_INBOUND_PAYLOAD_VERSION,
|
|
502
|
-
serialize: (admission, { receivedAt }) => ({
|
|
503
|
-
...admission,
|
|
504
|
-
message: serializeWhatsAppDurableInboundMessage(admission.message),
|
|
505
|
-
receivedAt
|
|
506
|
-
}),
|
|
507
|
-
deserialize: (payload) => ({
|
|
508
|
-
...payload,
|
|
509
|
-
message: deserializeWhatsAppDurableInboundMessage(payload.message)
|
|
510
|
-
}),
|
|
511
|
-
encode: ({ body }) => body,
|
|
512
|
-
decode: (payload) => ({
|
|
513
|
-
version: WHATSAPP_DURABLE_INBOUND_PAYLOAD_VERSION,
|
|
514
|
-
body: payload
|
|
515
|
-
}),
|
|
516
|
-
createClaimError: (kind) => new WhatsAppIngressPermanentError(kind === "invalid-version" ? "invalid-payload" : "event-id-mismatch", kind === "invalid-version" ? "WhatsApp ingress row has an invalid payload version" : "WhatsApp ingress row identity does not match its transport message key")
|
|
517
|
-
},
|
|
518
|
-
deliver: (admission, lifecycle) => params.dispatch(admission, lifecycle),
|
|
519
|
-
pollIntervalMs: params.pollIntervalMs,
|
|
497
|
+
return createDurableInboundReceiveJournalFromQueue({
|
|
498
|
+
queue,
|
|
520
499
|
retention: {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
deriveLaneKey: (record) => {
|
|
528
|
-
try {
|
|
529
|
-
return inspectWhatsAppIngressMessage(deserializeWhatsAppDurableInboundMessage(record.payload.message)).laneKey;
|
|
530
|
-
} catch {
|
|
531
|
-
return record.id;
|
|
532
|
-
}
|
|
533
|
-
},
|
|
534
|
-
...params.onLog ? { onLog: params.onLog } : {}
|
|
535
|
-
},
|
|
536
|
-
...params.abortSignal ? { abortSignal: params.abortSignal } : {},
|
|
537
|
-
admissionMode: "while-running",
|
|
538
|
-
createStoppedError: () => /* @__PURE__ */ new Error("WhatsApp ingress monitor is stopped."),
|
|
539
|
-
...params.onError ? { onError: params.onError } : {},
|
|
540
|
-
...params.onActivityChange ? { onActivityChange: params.onActivityChange } : {}
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
//#endregion
|
|
544
|
-
//#region extensions/whatsapp/src/inbound/group-metadata-cache.ts
|
|
545
|
-
const GROUP_META_TTL_MS = 300 * 1e3;
|
|
546
|
-
const WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES = 500;
|
|
547
|
-
function resolveGroupMetadataExpiresAt(nowRaw = Date.now()) {
|
|
548
|
-
const now = asDateTimestampMs(nowRaw);
|
|
549
|
-
return now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(GROUP_META_TTL_MS, { nowMs: now });
|
|
550
|
-
}
|
|
551
|
-
function rememberGroupMetadataCacheEntry(cache, jid, entry) {
|
|
552
|
-
if (asDateTimestampMs(entry.expires) === void 0) {
|
|
553
|
-
cache.delete(jid);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
if (cache.has(jid)) cache.delete(jid);
|
|
557
|
-
cache.set(jid, entry);
|
|
558
|
-
while (cache.size > WHATSAPP_GROUP_METADATA_CACHE_MAX_ENTRIES) {
|
|
559
|
-
const oldest = cache.keys().next();
|
|
560
|
-
if (oldest.done) break;
|
|
561
|
-
cache.delete(oldest.value);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
function readGroupMetadataCacheEntry(cache, jid) {
|
|
565
|
-
const entry = cache.get(jid);
|
|
566
|
-
if (!entry) return null;
|
|
567
|
-
const now = asDateTimestampMs(Date.now());
|
|
568
|
-
const expires = asDateTimestampMs(entry.expires);
|
|
569
|
-
if (now === void 0 || expires === void 0 || expires <= now) {
|
|
570
|
-
cache.delete(jid);
|
|
571
|
-
return null;
|
|
572
|
-
}
|
|
573
|
-
cache.delete(jid);
|
|
574
|
-
cache.set(jid, entry);
|
|
575
|
-
return entry;
|
|
576
|
-
}
|
|
577
|
-
function createWhatsAppGroupMetadataCacheOwner(params) {
|
|
578
|
-
const reconnectCache = params.reconnectCache ?? /* @__PURE__ */ new Map();
|
|
579
|
-
const localCache = /* @__PURE__ */ new Map();
|
|
580
|
-
const publishedJids = /* @__PURE__ */ new Set();
|
|
581
|
-
const invalidatedJids = /* @__PURE__ */ new Set();
|
|
582
|
-
const detachListeners = [];
|
|
583
|
-
let closed = false;
|
|
584
|
-
let started = false;
|
|
585
|
-
const summarize = async (meta) => {
|
|
586
|
-
const participantEntries = await Promise.all(meta.participants?.map(async (participant) => {
|
|
587
|
-
const mapped = await params.resolveInboundJid(participant.id);
|
|
588
|
-
return {
|
|
589
|
-
display: mapped ?? participant.id,
|
|
590
|
-
mention: {
|
|
591
|
-
id: participant.id,
|
|
592
|
-
lid: participant.lid,
|
|
593
|
-
phoneNumber: participant.phoneNumber,
|
|
594
|
-
e164: mapped
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
}) ?? []);
|
|
598
|
-
return {
|
|
599
|
-
subject: meta.subject,
|
|
600
|
-
participants: participantEntries.map((entry) => entry.display).filter(Boolean),
|
|
601
|
-
mentionParticipants: participantEntries.map((entry) => entry.mention),
|
|
602
|
-
expires: resolveGroupMetadataExpiresAt() ?? 0
|
|
603
|
-
};
|
|
604
|
-
};
|
|
605
|
-
const summarizeForReconnect = (meta) => ({
|
|
606
|
-
subject: meta.subject,
|
|
607
|
-
expires: resolveGroupMetadataExpiresAt() ?? NaN
|
|
608
|
-
});
|
|
609
|
-
const rememberFullUpdate = (jid, meta) => {
|
|
610
|
-
if (closed) return;
|
|
611
|
-
rememberWhatsAppBaileysCacheEntry(params.baileysCache, jid, meta, GROUP_META_TTL_MS);
|
|
612
|
-
publishedJids.add(jid);
|
|
613
|
-
invalidatedJids.delete(jid);
|
|
614
|
-
rememberGroupMetadataCacheEntry(reconnectCache, jid, summarizeForReconnect(meta));
|
|
615
|
-
localCache.delete(jid);
|
|
616
|
-
};
|
|
617
|
-
const forgetFullMetadata = (jid) => {
|
|
618
|
-
params.baileysCache?.delete(jid);
|
|
619
|
-
reconnectCache.delete(jid);
|
|
620
|
-
localCache.delete(jid);
|
|
621
|
-
publishedJids.delete(jid);
|
|
622
|
-
invalidatedJids.add(jid);
|
|
623
|
-
};
|
|
624
|
-
const get = async (jid) => {
|
|
625
|
-
const cached = readGroupMetadataCacheEntry(localCache, jid);
|
|
626
|
-
if (cached) return cached;
|
|
627
|
-
try {
|
|
628
|
-
const meta = await (params.getCurrentSock() ?? params.sock).groupMetadata(jid);
|
|
629
|
-
rememberWhatsAppBaileysCacheEntry(params.baileysCache, jid, meta, GROUP_META_TTL_MS);
|
|
630
|
-
publishedJids.add(jid);
|
|
631
|
-
const entry = await summarize(meta);
|
|
632
|
-
rememberGroupMetadataCacheEntry(reconnectCache, jid, {
|
|
633
|
-
subject: entry.subject,
|
|
634
|
-
expires: entry.expires
|
|
635
|
-
});
|
|
636
|
-
rememberGroupMetadataCacheEntry(localCache, jid, entry);
|
|
637
|
-
return entry;
|
|
638
|
-
} catch (error) {
|
|
639
|
-
const hydrated = readGroupMetadataCacheEntry(reconnectCache, jid);
|
|
640
|
-
if (hydrated) {
|
|
641
|
-
rememberGroupMetadataCacheEntry(localCache, jid, hydrated);
|
|
642
|
-
params.logVerbose(`Using cached group metadata for ${jid} after fetch failure: ${String(error)}`);
|
|
643
|
-
return hydrated;
|
|
644
|
-
}
|
|
645
|
-
params.logVerbose(`Failed to fetch group metadata for ${jid}: ${String(error)}`);
|
|
646
|
-
return { expires: resolveGroupMetadataExpiresAt() ?? 0 };
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
const resolveOutboundMentions = async (jid, text) => {
|
|
650
|
-
if (isJidGroup(jid) !== true || !mayContainWhatsAppOutboundMention(text)) return {
|
|
651
|
-
text,
|
|
652
|
-
mentionedJids: []
|
|
653
|
-
};
|
|
654
|
-
return resolveWhatsAppOutboundMentions({
|
|
655
|
-
chatJid: jid,
|
|
656
|
-
text,
|
|
657
|
-
participants: (await get(jid)).mentionParticipants
|
|
658
|
-
});
|
|
659
|
-
};
|
|
660
|
-
const applyOutboundMentions = async (jid, content) => {
|
|
661
|
-
if ("text" in content && typeof content.text === "string") {
|
|
662
|
-
const resolved = await resolveOutboundMentions(jid, content.text);
|
|
663
|
-
return addWhatsAppOutboundMentionsToContent({
|
|
664
|
-
...content,
|
|
665
|
-
text: resolved.text
|
|
666
|
-
}, resolved.mentionedJids);
|
|
667
|
-
}
|
|
668
|
-
const caption = content.caption;
|
|
669
|
-
if (typeof caption === "string") {
|
|
670
|
-
const resolved = await resolveOutboundMentions(jid, caption);
|
|
671
|
-
return addWhatsAppOutboundMentionsToContent({
|
|
672
|
-
...content,
|
|
673
|
-
caption: resolved.text
|
|
674
|
-
}, resolved.mentionedJids);
|
|
500
|
+
pendingTtlMs: WHATSAPP_DURABLE_INBOUND_PENDING_TTL_MS,
|
|
501
|
+
completedTtlMs: WHATSAPP_DURABLE_INBOUND_COMPLETED_TTL_MS,
|
|
502
|
+
failedTtlMs: WHATSAPP_DURABLE_INBOUND_PENDING_TTL_MS,
|
|
503
|
+
pendingMaxEntries: WHATSAPP_DURABLE_INBOUND_PENDING_MAX_ENTRIES,
|
|
504
|
+
completedMaxEntries: WHATSAPP_DURABLE_INBOUND_COMPLETED_MAX_ENTRIES,
|
|
505
|
+
failedMaxEntries: WHATSAPP_DURABLE_INBOUND_PENDING_MAX_ENTRIES
|
|
675
506
|
}
|
|
676
|
-
|
|
677
|
-
};
|
|
678
|
-
const start = () => {
|
|
679
|
-
if (started || closed) return;
|
|
680
|
-
started = true;
|
|
681
|
-
const listen = (event, listener) => {
|
|
682
|
-
detachListeners.push(params.listen(event, listener));
|
|
683
|
-
};
|
|
684
|
-
listen("groups.upsert", ((groups) => {
|
|
685
|
-
for (const group of groups) if (group.id) rememberFullUpdate(group.id, group);
|
|
686
|
-
}));
|
|
687
|
-
listen("groups.update", ((updates) => {
|
|
688
|
-
for (const update of updates) {
|
|
689
|
-
if (!update.id) continue;
|
|
690
|
-
if (typeof update.subject === "string" && Array.isArray(update.participants)) {
|
|
691
|
-
rememberFullUpdate(update.id, update);
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
forgetFullMetadata(update.id);
|
|
695
|
-
}
|
|
696
|
-
}));
|
|
697
|
-
listen("group-participants.update", ((update) => {
|
|
698
|
-
forgetFullMetadata(update.id);
|
|
699
|
-
}));
|
|
700
|
-
(async () => {
|
|
701
|
-
try {
|
|
702
|
-
const groups = await params.sock.groupFetchAllParticipating();
|
|
703
|
-
if (closed) return;
|
|
704
|
-
for (const [jid, meta] of Object.entries(groups ?? {})) if (meta && !publishedJids.has(jid) && !invalidatedJids.has(jid)) {
|
|
705
|
-
rememberGroupMetadataCacheEntry(reconnectCache, jid, summarizeForReconnect(meta));
|
|
706
|
-
rememberWhatsAppBaileysCacheEntry(params.baileysCache, jid, meta, GROUP_META_TTL_MS);
|
|
707
|
-
publishedJids.add(jid);
|
|
708
|
-
}
|
|
709
|
-
params.logVerbose(`Hydrated ${Object.keys(groups ?? {}).length} participating groups on connect`);
|
|
710
|
-
} catch (error) {
|
|
711
|
-
const formatted = String(error);
|
|
712
|
-
params.logHydrationWarning(formatted);
|
|
713
|
-
params.logVerbose(`Failed to hydrate participating groups on connect: ${formatted}`);
|
|
714
|
-
}
|
|
715
|
-
})();
|
|
716
|
-
};
|
|
717
|
-
const close = () => {
|
|
718
|
-
closed = true;
|
|
719
|
-
for (const detach of detachListeners.splice(0)) detach();
|
|
720
|
-
};
|
|
721
|
-
return {
|
|
722
|
-
start,
|
|
723
|
-
close,
|
|
724
|
-
get,
|
|
725
|
-
resolveOutboundMentions,
|
|
726
|
-
applyOutboundMentions
|
|
727
|
-
};
|
|
507
|
+
});
|
|
728
508
|
}
|
|
729
509
|
//#endregion
|
|
730
510
|
//#region extensions/whatsapp/src/inbound/lifecycle.ts
|
|
@@ -746,178 +526,28 @@ function closeInboundMonitorSocket(sock) {
|
|
|
746
526
|
sock.ws?.close?.();
|
|
747
527
|
}
|
|
748
528
|
//#endregion
|
|
749
|
-
//#region extensions/whatsapp/src/inbound/ingress-lifecycle.ts
|
|
750
|
-
const ingressLifecycleKey = Symbol("whatsappIngressLifecycle");
|
|
751
|
-
function attachWhatsAppIngressLifecycle(message, lifecycle) {
|
|
752
|
-
if (lifecycle) message[ingressLifecycleKey] = lifecycle;
|
|
753
|
-
return message;
|
|
754
|
-
}
|
|
755
|
-
function resolveWhatsAppIngressLifecycle(message) {
|
|
756
|
-
return message[ingressLifecycleKey];
|
|
757
|
-
}
|
|
758
|
-
//#endregion
|
|
759
|
-
//#region extensions/whatsapp/src/inbound/message-debounce.ts
|
|
760
|
-
function createWhatsAppInboundMessageDebouncer(options) {
|
|
761
|
-
const debounceMs = Math.max(0, Math.trunc(options.debounceMs ?? 0));
|
|
762
|
-
const pendingKeys = /* @__PURE__ */ new Map();
|
|
763
|
-
const activeFlushes = /* @__PURE__ */ new Set();
|
|
764
|
-
const workWaiters = /* @__PURE__ */ new Set();
|
|
765
|
-
const notifyWork = () => {
|
|
766
|
-
const waiters = [...workWaiters];
|
|
767
|
-
workWaiters.clear();
|
|
768
|
-
for (const resolve of waiters) resolve();
|
|
769
|
-
};
|
|
770
|
-
const buildKey = (msg) => {
|
|
771
|
-
const admission = requireWhatsAppInboundAdmission(msg);
|
|
772
|
-
const sender = msg.platform.sender;
|
|
773
|
-
const senderKey = admission.conversation.kind === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.platform.senderJid ?? msg.platform.senderE164 ?? msg.platform.senderName ?? admission.sender.id : admission.conversation.id;
|
|
774
|
-
return senderKey ? `${admission.accountId}:${admission.conversation.id}:${senderKey}` : null;
|
|
775
|
-
};
|
|
776
|
-
const shouldDebounce = (msg) => options.shouldDebounce?.(msg) ?? true;
|
|
777
|
-
const trackKey = (key) => {
|
|
778
|
-
pendingKeys.set(key, (pendingKeys.get(key) ?? 0) + 1);
|
|
779
|
-
};
|
|
780
|
-
const releaseKey = (entry) => {
|
|
781
|
-
if (!entry.debounceKey || entry.debounceKeyTracked !== true) return;
|
|
782
|
-
const remaining = (pendingKeys.get(entry.debounceKey) ?? 0) - 1;
|
|
783
|
-
if (remaining > 0) pendingKeys.set(entry.debounceKey, remaining);
|
|
784
|
-
else pendingKeys.delete(entry.debounceKey);
|
|
785
|
-
};
|
|
786
|
-
const orderEntries = (entries) => entries.toSorted((a, b) => {
|
|
787
|
-
const timestampDiff = (a.event.timestamp ?? 0) - (b.event.timestamp ?? 0);
|
|
788
|
-
return timestampDiff !== 0 ? timestampDiff : (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
|
|
789
|
-
});
|
|
790
|
-
const debouncer = createInboundDebouncer({
|
|
791
|
-
debounceMs,
|
|
792
|
-
buildKey: (msg) => msg.debounceKey ?? buildKey(msg),
|
|
793
|
-
shouldDebounce,
|
|
794
|
-
onFlush: (entries, createFlush) => {
|
|
795
|
-
for (const entry of entries) releaseKey(entry);
|
|
796
|
-
const orderedEntries = orderEntries(entries);
|
|
797
|
-
const { lifecycle, settle, abandon } = fanInChannelIngressLifecycles(orderedEntries.map((entry) => entry.turnAdoptionLifecycle));
|
|
798
|
-
const flush = createFlush({
|
|
799
|
-
lifecycle,
|
|
800
|
-
dispatch: async (admissionLifecycle) => {
|
|
801
|
-
const last = orderedEntries.at(-1);
|
|
802
|
-
if (!last) return;
|
|
803
|
-
try {
|
|
804
|
-
if (orderedEntries.length === 1) {
|
|
805
|
-
await options.onMessage(attachWhatsAppIngressLifecycle(last, admissionLifecycle));
|
|
806
|
-
await settle();
|
|
807
|
-
await Promise.all(orderedEntries.map((entry) => options.markRead(entry.readReceipt)));
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
const mentioned = /* @__PURE__ */ new Set();
|
|
811
|
-
for (const entry of orderedEntries) for (const jid of entry.group?.mentions?.jids ?? []) mentioned.add(jid);
|
|
812
|
-
const combinedBody = orderedEntries.map((entry) => entry.payload.body).filter(Boolean).join("\n");
|
|
813
|
-
const combinedCommandBody = orderedEntries.map((entry) => entry.payload.commandBody ?? entry.payload.body).filter(Boolean).join("\n");
|
|
814
|
-
const combinedMentions = mentioned.size > 0 ? {
|
|
815
|
-
...last.group?.mentions,
|
|
816
|
-
jids: Array.from(mentioned)
|
|
817
|
-
} : last.group?.mentions;
|
|
818
|
-
const combinedGroup = last.group || combinedMentions ? {
|
|
819
|
-
...last.group,
|
|
820
|
-
mentions: combinedMentions
|
|
821
|
-
} : void 0;
|
|
822
|
-
const combinedMessage = attachWhatsAppIngressLifecycle(withDeprecatedWebInboundMessageFlatAliases({
|
|
823
|
-
...last,
|
|
824
|
-
turnAdoptionLifecycle: admissionLifecycle,
|
|
825
|
-
payload: {
|
|
826
|
-
...last.payload,
|
|
827
|
-
body: combinedBody,
|
|
828
|
-
commandBody: combinedCommandBody
|
|
829
|
-
},
|
|
830
|
-
group: combinedGroup,
|
|
831
|
-
event: {
|
|
832
|
-
...last.event,
|
|
833
|
-
isBatched: true
|
|
834
|
-
}
|
|
835
|
-
}), admissionLifecycle);
|
|
836
|
-
await options.onMessage(combinedMessage);
|
|
837
|
-
await settle();
|
|
838
|
-
await Promise.all(orderedEntries.map((entry) => options.markRead(entry.readReceipt)));
|
|
839
|
-
} catch (error) {
|
|
840
|
-
await abandon();
|
|
841
|
-
throw error;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
activeFlushes.add(flush.completion);
|
|
846
|
-
options.onPendingWorkChanged();
|
|
847
|
-
notifyWork();
|
|
848
|
-
const cleanup = () => {
|
|
849
|
-
activeFlushes.delete(flush.completion);
|
|
850
|
-
options.onPendingWorkChanged();
|
|
851
|
-
};
|
|
852
|
-
flush.completion.then(cleanup, cleanup);
|
|
853
|
-
return flush;
|
|
854
|
-
},
|
|
855
|
-
onError: options.onError
|
|
856
|
-
});
|
|
857
|
-
const enqueue = async (message) => {
|
|
858
|
-
const key = buildKey(message);
|
|
859
|
-
if (key) {
|
|
860
|
-
message.debounceKey = key;
|
|
861
|
-
if (debounceMs > 0 && shouldDebounce(message)) {
|
|
862
|
-
message.debounceKeyTracked = true;
|
|
863
|
-
trackKey(key);
|
|
864
|
-
options.onPendingWorkChanged();
|
|
865
|
-
notifyWork();
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
await debouncer.enqueue(message);
|
|
869
|
-
};
|
|
870
|
-
const hasPendingWork = () => pendingKeys.size > 0 || activeFlushes.size > 0;
|
|
871
|
-
const pendingWorkCount = () => pendingKeys.size + activeFlushes.size;
|
|
872
|
-
const waitForWorkOrIdle = (handlers, handlersIdle) => {
|
|
873
|
-
if (hasPendingWork() || handlersIdle) return Promise.resolve();
|
|
874
|
-
return new Promise((resolve) => {
|
|
875
|
-
const finish = () => {
|
|
876
|
-
workWaiters.delete(finish);
|
|
877
|
-
resolve();
|
|
878
|
-
};
|
|
879
|
-
workWaiters.add(finish);
|
|
880
|
-
Promise.allSettled(handlers).then(finish);
|
|
881
|
-
});
|
|
882
|
-
};
|
|
883
|
-
const drain = async () => {
|
|
884
|
-
while (hasPendingWork()) {
|
|
885
|
-
const keys = Array.from(pendingKeys.keys());
|
|
886
|
-
if (keys.length > 0) await Promise.all(keys.map((key) => debouncer.flushKey(key)));
|
|
887
|
-
await debouncer.drain();
|
|
888
|
-
await Promise.resolve();
|
|
889
|
-
}
|
|
890
|
-
};
|
|
891
|
-
return {
|
|
892
|
-
enqueue,
|
|
893
|
-
drain,
|
|
894
|
-
hasPendingWork,
|
|
895
|
-
pendingWorkCount,
|
|
896
|
-
waitForWorkOrIdle
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
//#endregion
|
|
900
529
|
//#region extensions/whatsapp/src/inbound/media.ts
|
|
901
530
|
var WhatsAppInboundMediaLimitExceededError = class extends Error {
|
|
902
531
|
constructor(maxBytes) {
|
|
903
|
-
super(`Media exceeds ${Math.round(maxBytes /
|
|
532
|
+
super(`Media exceeds ${Math.round(maxBytes / 1048576)}MB limit`);
|
|
904
533
|
this.name = "WhatsAppInboundMediaLimitExceededError";
|
|
905
534
|
}
|
|
906
535
|
};
|
|
907
536
|
function unwrapMessage(message) {
|
|
908
537
|
return normalizeMessageContent(message);
|
|
909
538
|
}
|
|
910
|
-
async function downloadInboundMedia(msg, sock, maxBytes =
|
|
539
|
+
async function downloadInboundMedia(msg, sock, maxBytes = 52428800) {
|
|
911
540
|
const message = unwrapMessage(msg.message);
|
|
912
541
|
if (!message) return;
|
|
913
542
|
const mimetype = resolveInboundMediaMimetype(message);
|
|
914
543
|
const fileName = message.documentMessage?.fileName ?? void 0;
|
|
915
544
|
if (!message.imageMessage && !message.videoMessage && !message.documentMessage && !message.audioMessage && !message.stickerMessage) return;
|
|
545
|
+
const stream = await downloadMediaMessage(msg, "stream", {}, {
|
|
546
|
+
reuploadRequest: sock.updateMediaMessage,
|
|
547
|
+
logger: sock.logger
|
|
548
|
+
});
|
|
916
549
|
return {
|
|
917
|
-
saved: await saveMediaStream(
|
|
918
|
-
reuploadRequest: sock.updateMediaMessage,
|
|
919
|
-
logger: sock.logger
|
|
920
|
-
}), mimetype, "inbound", maxBytes, fileName).catch((err) => {
|
|
550
|
+
saved: await saveMediaStream(stream, mimetype, "inbound", maxBytes, fileName).catch((err) => {
|
|
921
551
|
if (err instanceof Error && /Media exceeds/i.test(err.message)) throw new WhatsAppInboundMediaLimitExceededError(maxBytes);
|
|
922
552
|
throw err;
|
|
923
553
|
}),
|
|
@@ -925,8 +555,9 @@ async function downloadInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024) {
|
|
|
925
555
|
fileName
|
|
926
556
|
};
|
|
927
557
|
}
|
|
928
|
-
async function downloadQuotedInboundMedia(msg, sock, maxBytes =
|
|
929
|
-
const
|
|
558
|
+
async function downloadQuotedInboundMedia(msg, sock, maxBytes = 52428800) {
|
|
559
|
+
const message = unwrapMessage(msg.message);
|
|
560
|
+
const contextInfo = extractContextInfo(message);
|
|
930
561
|
if (!contextInfo?.quotedMessage) return;
|
|
931
562
|
const quotedMessage = contextInfo.quotedMessage;
|
|
932
563
|
return downloadInboundMedia({
|
|
@@ -941,143 +572,497 @@ async function downloadQuotedInboundMedia(msg, sock, maxBytes = 50 * 1024 * 1024
|
|
|
941
572
|
}, sock, maxBytes);
|
|
942
573
|
}
|
|
943
574
|
//#endregion
|
|
944
|
-
//#region extensions/whatsapp/src/inbound/
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (
|
|
954
|
-
|
|
955
|
-
body = body ?? "";
|
|
956
|
-
const commandBody = body;
|
|
957
|
-
const replyContext = describeReplyContext(msg.message);
|
|
958
|
-
let mediaPath;
|
|
959
|
-
let mediaType = mediaKind ? resolveInboundMediaMimetype(msg.message) : void 0;
|
|
960
|
-
const nativeMedia = mediaKind ? {
|
|
961
|
-
contentType: mediaType,
|
|
962
|
-
kind: mediaKind
|
|
963
|
-
} : void 0;
|
|
964
|
-
let mediaFileName;
|
|
965
|
-
const maxBytes = (typeof params.mediaMaxMb === "number" && params.mediaMaxMb > 0 ? params.mediaMaxMb : 50) * 1024 * 1024;
|
|
966
|
-
const saveInboundMedia = async (inboundMedia) => {
|
|
967
|
-
if (!inboundMedia) return;
|
|
968
|
-
mediaPath = inboundMedia.saved.path;
|
|
969
|
-
mediaType = inboundMedia.mimetype;
|
|
970
|
-
mediaFileName = inboundMedia.fileName;
|
|
971
|
-
};
|
|
972
|
-
try {
|
|
973
|
-
await saveInboundMedia(await downloadInboundMedia(msg, sock, maxBytes));
|
|
974
|
-
} catch (error) {
|
|
975
|
-
params.logVerbose(`Inbound media download failed: ${String(error)}`);
|
|
976
|
-
body = formatInboundMediaUnavailableText({
|
|
977
|
-
body,
|
|
978
|
-
notice: "[whatsapp attachment unavailable]"
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
if (!mediaPath && !mediaKind && replyContext?.media) try {
|
|
982
|
-
await saveInboundMedia(await downloadQuotedInboundMedia(msg, sock, maxBytes));
|
|
983
|
-
mediaKind = replyContext.media.kind ?? void 0;
|
|
984
|
-
mediaType = mediaType ?? replyContext.media.contentType ?? void 0;
|
|
985
|
-
} catch (error) {
|
|
986
|
-
params.logVerbose(`Quoted media download failed: ${String(error)}`);
|
|
987
|
-
body = formatInboundMediaUnavailableText({
|
|
988
|
-
body,
|
|
989
|
-
notice: "[whatsapp quoted attachment unavailable]"
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
return {
|
|
993
|
-
body,
|
|
994
|
-
commandBody,
|
|
995
|
-
location: location ?? void 0,
|
|
996
|
-
contactContext,
|
|
997
|
-
externalAdReplyContext,
|
|
998
|
-
replyContext,
|
|
999
|
-
mediaPath,
|
|
1000
|
-
mediaType,
|
|
1001
|
-
mediaFileName,
|
|
1002
|
-
mediaKind,
|
|
1003
|
-
nativeMedia
|
|
1004
|
-
};
|
|
575
|
+
//#region extensions/whatsapp/src/inbound/monitor.ts
|
|
576
|
+
const LOGGED_OUT_STATUS = DisconnectReason?.loggedOut ?? 401;
|
|
577
|
+
const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
|
|
578
|
+
const GROUP_META_TTL_MS = 3e5;
|
|
579
|
+
const BAILEYS_MESSAGE_TTL_MS = 6e5;
|
|
580
|
+
const INBOUND_CLOSE_DRAIN_TIMEOUT_MS = 5e3;
|
|
581
|
+
const REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE = /reply session initialization conflicted for \S+/u;
|
|
582
|
+
function resolveRetryableWhatsAppInboundError(error) {
|
|
583
|
+
if (error instanceof WhatsAppRetryableInboundError) return error;
|
|
584
|
+
if (!collectErrorGraphCandidates(error, (current) => [current.cause, current.error]).some((candidate) => REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE.test(formatErrorMessage(candidate)))) return null;
|
|
585
|
+
return new WhatsAppRetryableInboundError(formatErrorMessage(error), { cause: error });
|
|
1005
586
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
ttlMs: 20 * 6e4,
|
|
1010
|
-
maxSize: 5e3
|
|
1011
|
-
});
|
|
1012
|
-
function buildMessageKey(params) {
|
|
1013
|
-
const accountId = params.accountId.trim();
|
|
1014
|
-
const remoteJid = params.remoteJid.trim();
|
|
1015
|
-
const messageId = params.messageId.trim();
|
|
1016
|
-
if (!accountId || !remoteJid || !messageId || messageId === "unknown") return null;
|
|
1017
|
-
return `${accountId}:${remoteJid}:${messageId}`;
|
|
587
|
+
function resolveGroupMetadataExpiresAt(nowRaw = Date.now()) {
|
|
588
|
+
const now = asDateTimestampMs(nowRaw);
|
|
589
|
+
return now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(GROUP_META_TTL_MS, { nowMs: now });
|
|
1018
590
|
}
|
|
1019
|
-
function
|
|
1020
|
-
|
|
591
|
+
function parseWhatsAppTimestampSeconds(value) {
|
|
592
|
+
if (value == null) return;
|
|
593
|
+
if (typeof value === "string") return parseStrictFiniteNumber(value);
|
|
594
|
+
const parsed = Number(value);
|
|
595
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1021
596
|
}
|
|
1022
|
-
function
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
597
|
+
function rememberGroupMetadataCacheEntry(cache, jid, entry) {
|
|
598
|
+
if (asDateTimestampMs(entry.expires) === void 0) {
|
|
599
|
+
cache.delete(jid);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (cache.has(jid)) cache.delete(jid);
|
|
603
|
+
cache.set(jid, entry);
|
|
604
|
+
while (cache.size > 500) {
|
|
605
|
+
const oldest = cache.keys().next();
|
|
606
|
+
if (oldest.done) break;
|
|
607
|
+
cache.delete(oldest.value);
|
|
608
|
+
}
|
|
1026
609
|
}
|
|
1027
|
-
function
|
|
1028
|
-
const
|
|
1029
|
-
if (!
|
|
1030
|
-
|
|
610
|
+
function readGroupMetadataCacheEntry(cache, jid) {
|
|
611
|
+
const entry = cache.get(jid);
|
|
612
|
+
if (!entry) return null;
|
|
613
|
+
const now = asDateTimestampMs(Date.now());
|
|
614
|
+
const expires = asDateTimestampMs(entry.expires);
|
|
615
|
+
if (now === void 0 || expires === void 0 || expires <= now) {
|
|
616
|
+
cache.delete(jid);
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
cache.delete(jid);
|
|
620
|
+
cache.set(jid, entry);
|
|
621
|
+
return entry;
|
|
1031
622
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
623
|
+
function rememberWhatsAppBaileysCacheEntry(cache, key, value, ttlMs) {
|
|
624
|
+
if (!cache) return;
|
|
625
|
+
if (cache.has(key)) cache.delete(key);
|
|
626
|
+
cache.set(key, {
|
|
627
|
+
expiresAt: Date.now() + ttlMs,
|
|
628
|
+
value
|
|
629
|
+
});
|
|
630
|
+
while (cache.size > 500) {
|
|
631
|
+
const oldest = cache.keys().next();
|
|
632
|
+
if (oldest.done) break;
|
|
633
|
+
cache.delete(oldest.value);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function readWhatsAppBaileysCacheEntry(cache, key) {
|
|
637
|
+
const entry = cache.get(key);
|
|
638
|
+
if (!entry) return;
|
|
639
|
+
if (entry.expiresAt <= Date.now()) {
|
|
640
|
+
cache.delete(key);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
cache.delete(key);
|
|
644
|
+
cache.set(key, entry);
|
|
645
|
+
return entry.value;
|
|
646
|
+
}
|
|
647
|
+
function logWhatsAppVerbose(enabled, message) {
|
|
648
|
+
if (!enabled) return;
|
|
649
|
+
defaultRuntime.log(message);
|
|
650
|
+
}
|
|
651
|
+
function isGroupJid(jid) {
|
|
652
|
+
return (typeof isJidGroup === "function" ? isJidGroup(jid) : jid.endsWith("@g.us")) === true;
|
|
653
|
+
}
|
|
654
|
+
function isDirectUserJid(jid) {
|
|
655
|
+
return /^(\d+)(?::\d+)?@(s\.whatsapp\.net|c\.us|lid|hosted|hosted\.lid)$/i.test(jid.trim());
|
|
656
|
+
}
|
|
657
|
+
function getActiveReachoutTimelock(state) {
|
|
658
|
+
if (state?.isActive !== true) return;
|
|
659
|
+
const endsAt = state.timeEnforcementEnds?.getTime();
|
|
660
|
+
return endsAt === void 0 || !Number.isFinite(endsAt) || endsAt > Date.now() ? state : void 0;
|
|
661
|
+
}
|
|
662
|
+
function formatReachoutTimelockError(state) {
|
|
663
|
+
const details = [state.enforcementType ? `type=${state.enforcementType}` : void 0, state.timeEnforcementEnds instanceof Date && Number.isFinite(state.timeEnforcementEnds.getTime()) ? `until=${state.timeEnforcementEnds.toISOString()}` : void 0].filter(Boolean);
|
|
664
|
+
return `WhatsApp reachout timelock is active; direct messages are temporarily blocked${details.length ? ` (${details.join(", ")})` : ""}`;
|
|
665
|
+
}
|
|
666
|
+
function recordAcceptedInboundActivity(accountId) {
|
|
667
|
+
recordChannelActivity({
|
|
668
|
+
channel: "whatsapp",
|
|
669
|
+
accountId,
|
|
670
|
+
direction: "inbound"
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
function isRetryableSendDisconnectError(err) {
|
|
674
|
+
if (isWhatsAppSocketOperationTimeoutError(err)) return false;
|
|
675
|
+
return /closed|reset|timed\s*out|disconnect|no active socket/i.test(formatError(err));
|
|
676
|
+
}
|
|
677
|
+
function shouldClearSocketRefAfterSendFailure(err) {
|
|
678
|
+
return /closed|reset|disconnect|no active socket/i.test(formatError(err));
|
|
679
|
+
}
|
|
680
|
+
function isNonEmptyString(value) {
|
|
681
|
+
return Boolean(value);
|
|
682
|
+
}
|
|
683
|
+
async function attachWebInboxToSocket(options) {
|
|
684
|
+
const inboundLogger = getChildLogger({ module: "web-inbound" });
|
|
685
|
+
const inboundConsoleLog = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
686
|
+
const sock = options.sock;
|
|
687
|
+
const connectedAtMs = Date.now();
|
|
688
|
+
if (options.socketRef) options.socketRef.current = sock;
|
|
689
|
+
const shouldRetryDisconnect = () => options.shouldRetryDisconnect?.() === true;
|
|
690
|
+
const disconnectRetryPolicy = options.disconnectRetryPolicy ?? DEFAULT_RECONNECT_POLICY;
|
|
691
|
+
const sendRetryMaxAttempts = disconnectRetryPolicy.maxAttempts > 0 ? disconnectRetryPolicy.maxAttempts : DEFAULT_RECONNECT_POLICY.maxAttempts;
|
|
692
|
+
const sendOperationTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(options.socketTiming.defaultQueryTimeoutMs);
|
|
693
|
+
let onCloseResolve = null;
|
|
694
|
+
const onClose = new Promise((resolve) => {
|
|
695
|
+
onCloseResolve = resolve;
|
|
696
|
+
});
|
|
697
|
+
const resolveClose = (reason) => {
|
|
698
|
+
if (!onCloseResolve) return;
|
|
699
|
+
const resolver = onCloseResolve;
|
|
700
|
+
onCloseResolve = null;
|
|
701
|
+
resolver(reason);
|
|
702
|
+
};
|
|
703
|
+
const presence = options.selfChatMode ? "unavailable" : "available";
|
|
704
|
+
try {
|
|
705
|
+
await createWhatsAppSocketOperationTimeoutAdapter(sock, sendOperationTimeoutMs).sendPresenceUpdate(presence);
|
|
706
|
+
logWhatsAppVerbose(options.verbose, `Sent global '${presence}' presence on connect`);
|
|
707
|
+
} catch (err) {
|
|
708
|
+
logWhatsAppVerbose(options.verbose, `Failed to send '${presence}' presence on connect: ${String(err)}`);
|
|
709
|
+
}
|
|
710
|
+
const selfIdentity = await readWebSelfIdentityForDecision(options.authDir, sock.user);
|
|
711
|
+
if (selfIdentity.outcome === "unstable") throw new WhatsAppAuthUnstableError("WhatsApp auth state is still stabilizing; retrying inbox attach.");
|
|
712
|
+
const self = selfIdentity.identity;
|
|
713
|
+
const getCurrentSock = () => {
|
|
714
|
+
if (!options.socketRef) return sock;
|
|
715
|
+
if (options.socketRef.current) return options.socketRef.current;
|
|
716
|
+
if (!self.e164 && !self.jid && !self.lid) return null;
|
|
717
|
+
const successor = getRegisteredWhatsAppConnectionController(options.accountId);
|
|
718
|
+
if (!successor) return null;
|
|
719
|
+
const successorIdentity = successor.getSelfIdentity();
|
|
720
|
+
if (!successorIdentity || !identitiesOverlap(self, successorIdentity)) return null;
|
|
721
|
+
return successor.getCurrentSock();
|
|
722
|
+
};
|
|
723
|
+
const durableInboundJournal = createWhatsAppDurableInboundReceiveJournal(options.accountId);
|
|
724
|
+
const inboundDebounceMs = Math.max(0, Math.trunc(options.debounceMs ?? 0));
|
|
725
|
+
const pendingDebounceKeys = /* @__PURE__ */ new Set();
|
|
726
|
+
const activeInboundFlushes = /* @__PURE__ */ new Set();
|
|
727
|
+
const pendingMessageHandlers = /* @__PURE__ */ new Set();
|
|
728
|
+
let nextReceiveOrder = 0;
|
|
729
|
+
const publishPendingWorkState = (at = Date.now()) => {
|
|
730
|
+
options.onPendingWorkChanged?.(pendingMessageHandlers.size + pendingDebounceKeys.size + activeInboundFlushes.size, at);
|
|
731
|
+
};
|
|
732
|
+
const buildInboundDebounceKey = (msg) => {
|
|
733
|
+
const admission = requireWhatsAppInboundAdmission(msg);
|
|
734
|
+
const sender = msg.platform.sender;
|
|
735
|
+
const senderKey = admission.conversation.kind === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.platform.senderJid ?? msg.platform.senderE164 ?? msg.platform.senderName ?? admission.sender.id : admission.conversation.id;
|
|
736
|
+
if (!senderKey) return null;
|
|
737
|
+
return `${admission.accountId}:${admission.conversation.id}:${senderKey}`;
|
|
738
|
+
};
|
|
739
|
+
const shouldDebounceInboundMessage = (msg) => options.shouldDebounce?.(msg) ?? true;
|
|
740
|
+
const orderDebouncedInboundEntries = (entries) => entries.toSorted((a, b) => {
|
|
741
|
+
const timestampDiff = (a.event.timestamp ?? 0) - (b.event.timestamp ?? 0);
|
|
742
|
+
if (timestampDiff !== 0) return timestampDiff;
|
|
743
|
+
return (a.receiveOrder ?? 0) - (b.receiveOrder ?? 0);
|
|
744
|
+
});
|
|
745
|
+
const finalizeInboundDelivery = async (entries, error) => {
|
|
746
|
+
const dedupeKeys = uniqueStrings(entries.map((entry) => entry.dedupeKey).filter(isNonEmptyString));
|
|
747
|
+
const durableEntries = entries.filter((entry) => isNonEmptyString(entry.durableId));
|
|
748
|
+
const readReceiptEntries = entries.filter((entry) => Boolean(entry.readReceipt));
|
|
749
|
+
const retryableError = resolveRetryableWhatsAppInboundError(error);
|
|
750
|
+
if (retryableError) {
|
|
751
|
+
dedupeKeys.forEach((dedupeKey) => releaseRecentInboundMessage(dedupeKey, retryableError));
|
|
752
|
+
await Promise.all(durableEntries.map((entry) => durableInboundJournal.release(entry.durableId, { lastError: formatError(retryableError) })));
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
await Promise.all([...dedupeKeys.map((dedupeKey) => commitRecentInboundMessage(dedupeKey)), ...durableEntries.map((entry) => durableInboundJournal.complete(entry.durableId, entry.readReceipt ? { metadata: { readReceipt: entry.readReceipt } } : void 0))]);
|
|
756
|
+
await Promise.all(readReceiptEntries.map((entry) => maybeMarkInboundAsRead(entry.readReceipt)));
|
|
757
|
+
};
|
|
758
|
+
const debouncer = createInboundDebouncer({
|
|
759
|
+
debounceMs: inboundDebounceMs,
|
|
760
|
+
buildKey: (msg) => msg.debounceKey ?? buildInboundDebounceKey(msg),
|
|
761
|
+
shouldDebounce: shouldDebounceInboundMessage,
|
|
762
|
+
onFlush: async (entries) => {
|
|
763
|
+
let finishFlush;
|
|
764
|
+
const flushTask = new Promise((resolve) => {
|
|
765
|
+
finishFlush = resolve;
|
|
766
|
+
});
|
|
767
|
+
activeInboundFlushes.add(flushTask);
|
|
768
|
+
publishPendingWorkState();
|
|
769
|
+
try {
|
|
770
|
+
const orderedEntries = orderDebouncedInboundEntries(entries);
|
|
771
|
+
const last = orderedEntries.at(-1);
|
|
772
|
+
if (!last) return;
|
|
773
|
+
try {
|
|
774
|
+
if (orderedEntries.length === 1) {
|
|
775
|
+
await options.onMessage(last);
|
|
776
|
+
await finalizeInboundDelivery(orderedEntries);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const mentioned = /* @__PURE__ */ new Set();
|
|
780
|
+
for (const entry of orderedEntries) for (const jid of entry.group?.mentions?.jids ?? []) mentioned.add(jid);
|
|
781
|
+
const combinedBody = orderedEntries.map((entry) => entry.payload.body).filter(Boolean).join("\n");
|
|
782
|
+
const combinedCommandBody = orderedEntries.map((entry) => entry.payload.commandBody ?? entry.payload.body).filter(Boolean).join("\n");
|
|
783
|
+
const combinedMentions = mentioned.size > 0 ? {
|
|
784
|
+
...last.group?.mentions,
|
|
785
|
+
jids: Array.from(mentioned)
|
|
786
|
+
} : last.group?.mentions;
|
|
787
|
+
const combinedGroup = last.group || combinedMentions ? {
|
|
788
|
+
...last.group,
|
|
789
|
+
mentions: combinedMentions
|
|
790
|
+
} : void 0;
|
|
791
|
+
const combinedMessage = withDeprecatedWebInboundMessageFlatAliases({
|
|
792
|
+
...last,
|
|
793
|
+
payload: {
|
|
794
|
+
...last.payload,
|
|
795
|
+
body: combinedBody,
|
|
796
|
+
commandBody: combinedCommandBody
|
|
797
|
+
},
|
|
798
|
+
group: combinedGroup,
|
|
799
|
+
event: {
|
|
800
|
+
...last.event,
|
|
801
|
+
isBatched: true
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
await options.onMessage(combinedMessage);
|
|
805
|
+
await finalizeInboundDelivery(orderedEntries);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
await finalizeInboundDelivery(orderedEntries, error);
|
|
808
|
+
throw error;
|
|
809
|
+
}
|
|
810
|
+
} finally {
|
|
811
|
+
for (const entry of entries) if (entry.debounceKey) pendingDebounceKeys.delete(entry.debounceKey);
|
|
812
|
+
activeInboundFlushes.delete(flushTask);
|
|
813
|
+
finishFlush();
|
|
814
|
+
publishPendingWorkState();
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
onError: (err) => {
|
|
818
|
+
inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
|
|
819
|
+
inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
const groupMetadataCache = options.groupMetadataCache ?? /* @__PURE__ */ new Map();
|
|
823
|
+
const groupMetaCache = /* @__PURE__ */ new Map();
|
|
824
|
+
const lidLookup = sock.signalRepository?.lidMapping;
|
|
825
|
+
const publishedGroupMetadataJids = /* @__PURE__ */ new Set();
|
|
826
|
+
const invalidatedGroupMetadataJids = /* @__PURE__ */ new Set();
|
|
827
|
+
let groupMetadataCacheClosed = false;
|
|
828
|
+
const resolveInboundJid = async (jid) => resolveJidToE164(jid, {
|
|
829
|
+
authDir: options.authDir,
|
|
830
|
+
lidLookup
|
|
831
|
+
});
|
|
832
|
+
const resolveReactionTargetJids = async (jid) => resolveEquivalentWhatsAppDirectChatJids(jid, {
|
|
833
|
+
authDir: options.authDir,
|
|
834
|
+
lidLookup
|
|
835
|
+
});
|
|
836
|
+
const rememberBaileysMessage = (remoteJid, messageId, message) => {
|
|
837
|
+
if (!options.recentMessageKeys || !remoteJid || !messageId || !message) return;
|
|
838
|
+
rememberWhatsAppBaileysCacheEntry(options.recentMessageKeys, `${remoteJid}:${messageId}`, message, BAILEYS_MESSAGE_TTL_MS);
|
|
839
|
+
};
|
|
840
|
+
const rememberOutboundMessage = (remoteJid, result) => {
|
|
841
|
+
const messageId = typeof result === "object" && result && "key" in result ? result.key?.id ?? "" : "";
|
|
842
|
+
if (!messageId) return;
|
|
843
|
+
rememberRecentOutboundMessage({
|
|
1040
844
|
accountId: options.accountId,
|
|
1041
845
|
remoteJid,
|
|
1042
|
-
messageId
|
|
1043
|
-
})
|
|
1044
|
-
|
|
846
|
+
messageId
|
|
847
|
+
});
|
|
848
|
+
const message = typeof result === "object" && result && "message" in result ? result.message : void 0;
|
|
849
|
+
rememberBaileysMessage(remoteJid, messageId, message);
|
|
850
|
+
cacheInboundMessageMeta(options.accountId, remoteJid, messageId, {
|
|
851
|
+
fromMe: true,
|
|
852
|
+
body: extractText(message ?? void 0)
|
|
853
|
+
});
|
|
854
|
+
};
|
|
855
|
+
const trackLateAcceptedSend = (jid, promise) => {
|
|
856
|
+
promise.then((result) => {
|
|
857
|
+
rememberOutboundMessage(jid, result);
|
|
858
|
+
}, () => {});
|
|
859
|
+
};
|
|
860
|
+
let reachoutTimeLock;
|
|
861
|
+
let reachoutTimeLockFetch;
|
|
862
|
+
let reachoutTimeLockVersion = 0;
|
|
863
|
+
let verifiedSendReady;
|
|
864
|
+
const rememberReachoutTimeLock = (state) => {
|
|
865
|
+
reachoutTimeLock = state;
|
|
866
|
+
reachoutTimeLockVersion += 1;
|
|
867
|
+
verifiedSendReady = void 0;
|
|
868
|
+
};
|
|
869
|
+
const fetchReachoutTimeLock = async (currentSock) => {
|
|
870
|
+
if (typeof currentSock.fetchAccountReachoutTimelock !== "function") return;
|
|
871
|
+
if (!reachoutTimeLockFetch) reachoutTimeLockFetch = currentSock.fetchAccountReachoutTimelock().then((state) => {
|
|
872
|
+
rememberReachoutTimeLock(state);
|
|
873
|
+
return state;
|
|
874
|
+
}).catch((err) => {
|
|
875
|
+
logWhatsAppVerbose(options.verbose, `Failed fetching WhatsApp reachout timelock before send: ${formatError(err)}`);
|
|
876
|
+
}).finally(() => {
|
|
877
|
+
reachoutTimeLockFetch = void 0;
|
|
878
|
+
});
|
|
879
|
+
return await reachoutTimeLockFetch;
|
|
880
|
+
};
|
|
881
|
+
const rememberVerifiedSendReady = (jid, currentSock) => {
|
|
882
|
+
verifiedSendReady = {
|
|
883
|
+
jid,
|
|
884
|
+
sock: currentSock,
|
|
885
|
+
reachoutTimeLockVersion
|
|
886
|
+
};
|
|
887
|
+
};
|
|
888
|
+
const consumeVerifiedSendReady = (jid, currentSock) => {
|
|
889
|
+
if (verifiedSendReady?.jid !== jid || verifiedSendReady.sock !== currentSock || verifiedSendReady.reachoutTimeLockVersion !== reachoutTimeLockVersion) return false;
|
|
890
|
+
verifiedSendReady = void 0;
|
|
1045
891
|
return true;
|
|
1046
892
|
};
|
|
1047
|
-
const
|
|
893
|
+
const assertCanSendToJid = async (jid, currentSock, readinessOptions) => {
|
|
894
|
+
if (!isDirectUserJid(jid)) return;
|
|
895
|
+
if (readinessOptions?.useVerifiedReady && consumeVerifiedSendReady(jid, currentSock)) return;
|
|
896
|
+
const state = getActiveReachoutTimelock(reachoutTimeLock) ?? await fetchReachoutTimeLock(currentSock);
|
|
897
|
+
const activeState = getActiveReachoutTimelock(state);
|
|
898
|
+
if (activeState) throw new Error(formatReachoutTimelockError(activeState));
|
|
899
|
+
if (readinessOptions?.rememberReady && state) rememberVerifiedSendReady(jid, currentSock);
|
|
900
|
+
};
|
|
901
|
+
const assertCanSendTo = async (to) => {
|
|
902
|
+
const currentSock = getCurrentSock();
|
|
903
|
+
if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
904
|
+
const jid = options.authDir ? toWhatsappJidWithLid(to, { authDir: options.authDir }) : toWhatsappJid(to);
|
|
905
|
+
await assertCanSendToJid(jid, currentSock, { rememberReady: true });
|
|
906
|
+
};
|
|
907
|
+
const sendTrackedMessage = async (jid, content, sendOptions) => {
|
|
908
|
+
let lastErr = /* @__PURE__ */ new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
909
|
+
for (let attempt = 1;; attempt++) {
|
|
910
|
+
const currentSock = getCurrentSock();
|
|
911
|
+
if (currentSock) try {
|
|
912
|
+
await assertCanSendToJid(jid, currentSock, { useVerifiedReady: true });
|
|
913
|
+
const result = await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs, { onSendMessageTimeout: ({ jid: timedOutJid, promise }) => {
|
|
914
|
+
trackLateAcceptedSend(timedOutJid, promise);
|
|
915
|
+
} }).sendMessage(jid, content, sendOptions);
|
|
916
|
+
rememberOutboundMessage(jid, result);
|
|
917
|
+
return result;
|
|
918
|
+
} catch (err) {
|
|
919
|
+
if (!shouldRetryDisconnect() || !isRetryableSendDisconnectError(err)) throw err;
|
|
920
|
+
lastErr = err;
|
|
921
|
+
if (shouldClearSocketRefAfterSendFailure(err) && options.socketRef?.current === currentSock) options.socketRef.current = null;
|
|
922
|
+
}
|
|
923
|
+
else if (!shouldRetryDisconnect()) throw lastErr;
|
|
924
|
+
if (attempt >= sendRetryMaxAttempts) throw lastErr;
|
|
925
|
+
const delayMs = computeBackoff(disconnectRetryPolicy, attempt);
|
|
926
|
+
logWhatsAppVerbose(options.verbose, `Waiting ${delayMs}ms for WhatsApp reconnect before retrying send to ${jid}: ${formatError(lastErr)}`);
|
|
927
|
+
try {
|
|
928
|
+
await sleepWithAbort(delayMs, options.disconnectRetryAbortSignal);
|
|
929
|
+
} catch {
|
|
930
|
+
throw lastErr;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
const sendApiSocketOperations = {
|
|
935
|
+
sendMessage: (jid, content, sendOptions) => sendTrackedMessage(jid, content, sendOptions),
|
|
936
|
+
sendPresenceUpdate: async (presenceLocal, jid) => {
|
|
937
|
+
const currentSock = getCurrentSock();
|
|
938
|
+
if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
939
|
+
return await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs).sendPresenceUpdate(presenceLocal, jid);
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
const summarizeGroupMeta = async (meta) => {
|
|
943
|
+
const participantEntries = await Promise.all(meta.participants?.map(async (p) => {
|
|
944
|
+
const mapped = await resolveInboundJid(p.id);
|
|
945
|
+
return {
|
|
946
|
+
display: mapped ?? p.id,
|
|
947
|
+
mention: {
|
|
948
|
+
id: p.id,
|
|
949
|
+
lid: p.lid,
|
|
950
|
+
phoneNumber: p.phoneNumber,
|
|
951
|
+
e164: mapped
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
}) ?? []);
|
|
955
|
+
const participants = participantEntries.map((entry) => entry.display).filter(Boolean);
|
|
956
|
+
const mentionParticipants = participantEntries.map((entry) => entry.mention);
|
|
957
|
+
return {
|
|
958
|
+
subject: meta.subject,
|
|
959
|
+
participants,
|
|
960
|
+
mentionParticipants,
|
|
961
|
+
expires: resolveGroupMetadataExpiresAt() ?? 0
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
const summarizeGroupMetaForReconnectCache = (meta) => ({
|
|
965
|
+
subject: meta.subject,
|
|
966
|
+
expires: resolveGroupMetadataExpiresAt() ?? NaN
|
|
967
|
+
});
|
|
968
|
+
const getGroupMeta = async (jid) => {
|
|
969
|
+
const cached = readGroupMetadataCacheEntry(groupMetaCache, jid);
|
|
970
|
+
if (cached) return cached;
|
|
971
|
+
try {
|
|
972
|
+
const meta = await (getCurrentSock() ?? sock).groupMetadata(jid);
|
|
973
|
+
rememberWhatsAppBaileysCacheEntry(options.baileysGroupMetaCache, jid, meta, GROUP_META_TTL_MS);
|
|
974
|
+
publishedGroupMetadataJids.add(jid);
|
|
975
|
+
const entry = await summarizeGroupMeta(meta);
|
|
976
|
+
rememberGroupMetadataCacheEntry(groupMetadataCache, jid, {
|
|
977
|
+
subject: entry.subject,
|
|
978
|
+
expires: entry.expires
|
|
979
|
+
});
|
|
980
|
+
rememberGroupMetadataCacheEntry(groupMetaCache, jid, entry);
|
|
981
|
+
return entry;
|
|
982
|
+
} catch (err) {
|
|
983
|
+
const hydrated = readGroupMetadataCacheEntry(groupMetadataCache, jid);
|
|
984
|
+
if (hydrated) {
|
|
985
|
+
rememberGroupMetadataCacheEntry(groupMetaCache, jid, hydrated);
|
|
986
|
+
logWhatsAppVerbose(options.verbose, `Using cached group metadata for ${jid} after fetch failure: ${String(err)}`);
|
|
987
|
+
return hydrated;
|
|
988
|
+
}
|
|
989
|
+
logWhatsAppVerbose(options.verbose, `Failed to fetch group metadata for ${jid}: ${String(err)}`);
|
|
990
|
+
return { expires: resolveGroupMetadataExpiresAt() ?? 0 };
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
const resolveOutboundMentionsForGroup = async (jid, text) => {
|
|
994
|
+
if (!isGroupJid(jid) || !mayContainWhatsAppOutboundMention(text)) return {
|
|
995
|
+
text,
|
|
996
|
+
mentionedJids: []
|
|
997
|
+
};
|
|
998
|
+
const meta = await getGroupMeta(jid);
|
|
999
|
+
return resolveWhatsAppOutboundMentions({
|
|
1000
|
+
chatJid: jid,
|
|
1001
|
+
text,
|
|
1002
|
+
participants: meta.mentionParticipants
|
|
1003
|
+
});
|
|
1004
|
+
};
|
|
1005
|
+
const applyOutboundMentionsToContent = async (jid, content) => {
|
|
1006
|
+
if ("text" in content && typeof content.text === "string") {
|
|
1007
|
+
const resolved = await resolveOutboundMentionsForGroup(jid, content.text);
|
|
1008
|
+
return addWhatsAppOutboundMentionsToContent({
|
|
1009
|
+
...content,
|
|
1010
|
+
text: resolved.text
|
|
1011
|
+
}, resolved.mentionedJids);
|
|
1012
|
+
}
|
|
1013
|
+
const caption = content.caption;
|
|
1014
|
+
if (typeof caption === "string") {
|
|
1015
|
+
const resolved = await resolveOutboundMentionsForGroup(jid, caption);
|
|
1016
|
+
return addWhatsAppOutboundMentionsToContent({
|
|
1017
|
+
...content,
|
|
1018
|
+
caption: resolved.text
|
|
1019
|
+
}, resolved.mentionedJids);
|
|
1020
|
+
}
|
|
1021
|
+
return content;
|
|
1022
|
+
};
|
|
1023
|
+
const normalizeInboundMessage = async (msg) => {
|
|
1048
1024
|
const id = msg.key?.id ?? void 0;
|
|
1049
1025
|
const remoteJid = msg.key?.remoteJid;
|
|
1050
|
-
if (!remoteJid
|
|
1051
|
-
|
|
1052
|
-
|
|
1026
|
+
if (!remoteJid) return null;
|
|
1027
|
+
if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast")) return null;
|
|
1028
|
+
const group = isGroupJid(remoteJid);
|
|
1029
|
+
if (Boolean(msg.key?.fromMe) && id && isRecentOutboundMessage({
|
|
1030
|
+
accountId: options.accountId,
|
|
1031
|
+
remoteJid,
|
|
1032
|
+
messageId: id
|
|
1033
|
+
})) {
|
|
1034
|
+
logWhatsAppVerbose(options.verbose, `Skipping recent outbound WhatsApp echo ${id} for ${remoteJid}`);
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1053
1037
|
if (!hasInboundUserContent(msg.message ?? void 0)) return null;
|
|
1054
1038
|
const participantJid = msg.key?.participant ?? void 0;
|
|
1055
|
-
const from = group ? remoteJid : await
|
|
1039
|
+
const from = group ? remoteJid : await resolveInboundJid(remoteJid);
|
|
1056
1040
|
if (!from) return null;
|
|
1057
|
-
const senderE164 = group ? participantJid ? await
|
|
1041
|
+
const senderE164 = group ? participantJid ? await resolveInboundJid(participantJid) : null : from;
|
|
1058
1042
|
let groupSubject;
|
|
1059
1043
|
let groupParticipants;
|
|
1060
1044
|
if (group) {
|
|
1061
|
-
const meta = await
|
|
1045
|
+
const meta = await getGroupMeta(remoteJid);
|
|
1062
1046
|
groupSubject = meta.subject;
|
|
1063
1047
|
groupParticipants = meta.participants;
|
|
1064
1048
|
}
|
|
1065
|
-
const messageTimestampSeconds =
|
|
1049
|
+
const messageTimestampSeconds = parseWhatsAppTimestampSeconds(msg.messageTimestamp);
|
|
1066
1050
|
const messageTimestampMs = messageTimestampSeconds !== void 0 ? messageTimestampSeconds * 1e3 : void 0;
|
|
1051
|
+
const accessCfg = options.loadConfig?.() ?? options.cfg;
|
|
1067
1052
|
const access = await checkInboundAccessControl({
|
|
1068
|
-
cfg:
|
|
1053
|
+
cfg: accessCfg,
|
|
1069
1054
|
accountId: options.accountId,
|
|
1070
1055
|
from,
|
|
1071
|
-
selfE164:
|
|
1056
|
+
selfE164: self.e164 ?? null,
|
|
1072
1057
|
senderE164,
|
|
1073
1058
|
senderJid: participantJid,
|
|
1074
1059
|
group,
|
|
1075
1060
|
pushName: msg.pushName ?? void 0,
|
|
1076
1061
|
isFromMe: Boolean(msg.key?.fromMe),
|
|
1077
1062
|
messageTimestampMs,
|
|
1078
|
-
connectedAtMs
|
|
1063
|
+
connectedAtMs,
|
|
1079
1064
|
verbose: options.verbose,
|
|
1080
|
-
sock: { sendMessage: (jid, content) =>
|
|
1065
|
+
sock: { sendMessage: (jid, content) => sendTrackedMessage(jid, content) },
|
|
1081
1066
|
remoteJid
|
|
1082
1067
|
});
|
|
1083
1068
|
if (!access.allowed) return null;
|
|
@@ -1094,58 +1079,6 @@ function createWhatsAppInboundMessageNormalizer(options) {
|
|
|
1094
1079
|
access
|
|
1095
1080
|
};
|
|
1096
1081
|
};
|
|
1097
|
-
return {
|
|
1098
|
-
normalize,
|
|
1099
|
-
shouldSkipRecentOutboundEcho
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
//#endregion
|
|
1103
|
-
//#region extensions/whatsapp/src/inbound/message-delivery.ts
|
|
1104
|
-
const INBOUND_CLOSE_DRAIN_TIMEOUT_MS = 5e3;
|
|
1105
|
-
const WHATSAPP_INGRESS_DRAIN_INTERVAL_MS = 1e3;
|
|
1106
|
-
function parseWhatsAppTimestampSeconds(value) {
|
|
1107
|
-
if (value == null) return;
|
|
1108
|
-
if (typeof value === "string") return parseStrictFiniteNumber(value);
|
|
1109
|
-
const parsed = Number(value);
|
|
1110
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1111
|
-
}
|
|
1112
|
-
function logWhatsAppVerbose$1(enabled, message) {
|
|
1113
|
-
if (!enabled) return;
|
|
1114
|
-
defaultRuntime.log(message);
|
|
1115
|
-
}
|
|
1116
|
-
function recordAcceptedInboundActivity(accountId) {
|
|
1117
|
-
recordChannelActivity({
|
|
1118
|
-
channel: "whatsapp",
|
|
1119
|
-
accountId,
|
|
1120
|
-
direction: "inbound"
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
|
-
function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
1124
|
-
const inboundLogger = getChildLogger({ module: "web-inbound" });
|
|
1125
|
-
const inboundConsoleLog = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
|
|
1126
|
-
const sock = options.sock;
|
|
1127
|
-
const socketSession = options.socketSession;
|
|
1128
|
-
const groupMetadata = options.groupMetadata;
|
|
1129
|
-
const { connectedAtMs, self, getCurrentSock, resolveInboundJid, resolveReactionTargetJids, rememberBaileysMessage, assertCanSendToJid, sendTrackedMessage, socketOperations } = socketSession;
|
|
1130
|
-
const durableInboundQueue = options.durableInboundQueue ?? createWhatsAppDurableInboundQueue(options.accountId);
|
|
1131
|
-
const pendingMessageHandlers = /* @__PURE__ */ new Set();
|
|
1132
|
-
let durableIngressActive = false;
|
|
1133
|
-
let nextReceiveOrder = 0;
|
|
1134
|
-
const publishPendingWorkState = (at = Date.now()) => {
|
|
1135
|
-
options.onPendingWorkChanged?.(pendingMessageHandlers.size + messageDebouncer.pendingWorkCount() + (durableIngressActive ? 1 : 0), at);
|
|
1136
|
-
};
|
|
1137
|
-
const messageNormalizer = createWhatsAppInboundMessageNormalizer({
|
|
1138
|
-
cfg: options.cfg,
|
|
1139
|
-
loadConfig: options.loadConfig,
|
|
1140
|
-
accountId: options.accountId,
|
|
1141
|
-
verbose: options.verbose,
|
|
1142
|
-
socketSession,
|
|
1143
|
-
groupMetadata,
|
|
1144
|
-
parseTimestampSeconds: parseWhatsAppTimestampSeconds,
|
|
1145
|
-
logVerbose: (message) => logWhatsAppVerbose$1(options.verbose, message)
|
|
1146
|
-
});
|
|
1147
|
-
const normalizeInboundMessage = messageNormalizer.normalize;
|
|
1148
|
-
const shouldSkipRecentOutboundEcho = messageNormalizer.shouldSkipRecentOutboundEcho;
|
|
1149
1082
|
const buildReadReceiptTarget = (inbound) => inbound.id ? {
|
|
1150
1083
|
remoteJid: inbound.remoteJid,
|
|
1151
1084
|
id: inbound.id,
|
|
@@ -1155,15 +1088,20 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1155
1088
|
if (!target || options.sendReadReceipts === false) return;
|
|
1156
1089
|
const { id, remoteJid, participant } = target;
|
|
1157
1090
|
try {
|
|
1158
|
-
await
|
|
1091
|
+
await withWhatsAppSocketOperationTimeout("readMessages", (getCurrentSock() ?? sock).readMessages([{
|
|
1092
|
+
remoteJid,
|
|
1093
|
+
id,
|
|
1094
|
+
participant,
|
|
1095
|
+
fromMe: false
|
|
1096
|
+
}]), sendOperationTimeoutMs);
|
|
1159
1097
|
const suffix = participant ? ` (participant ${participant})` : "";
|
|
1160
|
-
logWhatsAppVerbose
|
|
1098
|
+
logWhatsAppVerbose(options.verbose, `Marked message ${id} as read for ${remoteJid}${suffix}`);
|
|
1161
1099
|
} catch (err) {
|
|
1162
|
-
logWhatsAppVerbose
|
|
1100
|
+
logWhatsAppVerbose(options.verbose, `Failed to mark message ${id} read: ${String(err)}`);
|
|
1163
1101
|
}
|
|
1164
1102
|
};
|
|
1165
1103
|
const maybeLogSkippedSelfChatReadReceipt = (inbound, target) => {
|
|
1166
|
-
if (target?.id && inbound.access.isSelfChat && options.verbose) logWhatsAppVerbose
|
|
1104
|
+
if (target?.id && inbound.access.isSelfChat && options.verbose) logWhatsAppVerbose(options.verbose, `Self-chat mode: skipping read receipt for ${target.id}`);
|
|
1167
1105
|
};
|
|
1168
1106
|
const maybeMarkNonSelfChatReadReceipt = async (inbound, target) => {
|
|
1169
1107
|
if (inbound.access.isSelfChat) {
|
|
@@ -1172,16 +1110,14 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1172
1110
|
}
|
|
1173
1111
|
await maybeMarkInboundAsRead(target);
|
|
1174
1112
|
};
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
inboundConsoleLog.error(`Failed handling inbound web message: ${String(error)}`);
|
|
1184
|
-
}
|
|
1113
|
+
const completeUndeliverableDurableInbound = async (durableId, metadata) => {
|
|
1114
|
+
if (!durableId) return;
|
|
1115
|
+
await durableInboundJournal.complete(durableId, metadata?.readReceipt ? { metadata: { readReceipt: metadata.readReceipt } } : void 0);
|
|
1116
|
+
};
|
|
1117
|
+
const buildDurableInboundPayload = (msg, upsertType) => ({
|
|
1118
|
+
message: serializeWhatsAppDurableInboundMessage(msg),
|
|
1119
|
+
...upsertType ? { upsertType } : {},
|
|
1120
|
+
receivedAt: Date.now()
|
|
1185
1121
|
});
|
|
1186
1122
|
const shouldSkipStaleAppend = (msg, upsertType) => {
|
|
1187
1123
|
if (upsertType !== "append") return false;
|
|
@@ -1191,7 +1127,124 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1191
1127
|
const nowMs = Date.now();
|
|
1192
1128
|
return msgTsMs < (options.appendReplyWindow && nowMs <= options.appendReplyWindow.untilMs ? Math.max(options.appendReplyWindow.afterMs, nowMs - options.appendReplyWindow.maxAgeMs) : connectedAtMs - APPEND_RECENT_GRACE_MS);
|
|
1193
1129
|
};
|
|
1194
|
-
const
|
|
1130
|
+
const processDurableInboundMessage = async (msg, upsertType, receiveOrder, stored) => {
|
|
1131
|
+
const inbound = await normalizeInboundMessage(msg);
|
|
1132
|
+
if (!inbound) {
|
|
1133
|
+
if (stored) await completeUndeliverableDurableInbound(stored.id, stored.metadata);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const readReceipt = stored?.metadata?.readReceipt ?? buildReadReceiptTarget(inbound);
|
|
1137
|
+
const deliveryReadReceipt = inbound.access.isSelfChat ? void 0 : readReceipt;
|
|
1138
|
+
if (!stored && shouldSkipStaleAppend(msg, upsertType)) {
|
|
1139
|
+
await maybeMarkNonSelfChatReadReceipt(inbound, readReceipt);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
let durableId = stored?.id ?? (inbound.id ? createWhatsAppDurableInboundMessageId({
|
|
1143
|
+
remoteJid: inbound.remoteJid,
|
|
1144
|
+
id: inbound.id
|
|
1145
|
+
}) : void 0);
|
|
1146
|
+
const durableMetadata = deliveryReadReceipt ? { readReceipt: deliveryReadReceipt } : void 0;
|
|
1147
|
+
if (durableId && !stored) try {
|
|
1148
|
+
const accepted = await durableInboundJournal.accept(durableId, buildDurableInboundPayload(msg, upsertType), {
|
|
1149
|
+
metadata: durableMetadata,
|
|
1150
|
+
receivedAt: inbound.messageTimestampMs
|
|
1151
|
+
});
|
|
1152
|
+
if (accepted.kind === "completed") {
|
|
1153
|
+
await maybeMarkNonSelfChatReadReceipt(inbound, accepted.record.metadata?.readReceipt ?? deliveryReadReceipt);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (accepted.kind === "pending" && accepted.record.attempts === 0) return;
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
durableId = void 0;
|
|
1159
|
+
const error = formatError(err);
|
|
1160
|
+
inboundLogger.warn({ error }, "failed persisting durable WhatsApp inbound; delivering live");
|
|
1161
|
+
inboundConsoleLog.warn(`Failed persisting durable WhatsApp inbound; delivering live: ${error}`);
|
|
1162
|
+
}
|
|
1163
|
+
const enriched = await enrichInboundMessage(msg);
|
|
1164
|
+
if (!enriched) {
|
|
1165
|
+
await completeUndeliverableDurableInbound(durableId, durableMetadata);
|
|
1166
|
+
await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
const dedupeKey = inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : "";
|
|
1170
|
+
const dedupeClaim = dedupeKey ? await claimRecentInboundMessageDelivery(dedupeKey) : "claimed";
|
|
1171
|
+
if (dedupeClaim !== "claimed") {
|
|
1172
|
+
if (dedupeClaim === "duplicate") {
|
|
1173
|
+
await completeUndeliverableDurableInbound(durableId, durableMetadata);
|
|
1174
|
+
await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
|
|
1175
|
+
}
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
recordAcceptedInboundActivity(options.accountId);
|
|
1179
|
+
await enqueueInboundMessage(msg, inbound, enriched, {
|
|
1180
|
+
durableId,
|
|
1181
|
+
readReceipt: deliveryReadReceipt,
|
|
1182
|
+
receiveOrder
|
|
1183
|
+
});
|
|
1184
|
+
};
|
|
1185
|
+
const replayPendingDurableInboundMessages = async () => {
|
|
1186
|
+
const pending = await durableInboundJournal.pending();
|
|
1187
|
+
for (const record of pending) await processDurableInboundMessage(deserializeWhatsAppDurableInboundMessage(record.payload.message), record.payload.upsertType, record.payload.receivedAt, {
|
|
1188
|
+
id: record.id,
|
|
1189
|
+
payload: record.payload,
|
|
1190
|
+
metadata: record.metadata
|
|
1191
|
+
});
|
|
1192
|
+
};
|
|
1193
|
+
const enrichInboundMessage = async (msg) => {
|
|
1194
|
+
const location = extractLocationData(msg.message ?? void 0);
|
|
1195
|
+
const locationText = location ? formatLocationText(location) : void 0;
|
|
1196
|
+
const contactContext = extractContactContext(msg.message ?? void 0);
|
|
1197
|
+
const externalAdReplyContext = extractExternalAdReplyContext(msg.message ?? void 0);
|
|
1198
|
+
const mediaPlaceholder = extractMediaPlaceholder(msg.message ?? void 0);
|
|
1199
|
+
let body = extractText(msg.message ?? void 0);
|
|
1200
|
+
if (locationText) body = [body, locationText].filter(Boolean).join("\n").trim();
|
|
1201
|
+
if (!body) {
|
|
1202
|
+
body = mediaPlaceholder;
|
|
1203
|
+
if (!body) return null;
|
|
1204
|
+
}
|
|
1205
|
+
const commandBody = body;
|
|
1206
|
+
const replyContext = describeReplyContext(msg.message);
|
|
1207
|
+
let mediaPath;
|
|
1208
|
+
let mediaType;
|
|
1209
|
+
let mediaFileName;
|
|
1210
|
+
const maxBytes = (typeof options.mediaMaxMb === "number" && options.mediaMaxMb > 0 ? options.mediaMaxMb : 50) * 1024 * 1024;
|
|
1211
|
+
const saveInboundMedia = async (inboundMedia) => {
|
|
1212
|
+
if (!inboundMedia) return;
|
|
1213
|
+
mediaPath = inboundMedia.saved.path;
|
|
1214
|
+
mediaType = inboundMedia.mimetype;
|
|
1215
|
+
mediaFileName = inboundMedia.fileName;
|
|
1216
|
+
};
|
|
1217
|
+
try {
|
|
1218
|
+
await saveInboundMedia(await downloadInboundMedia(msg, sock, maxBytes));
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
logWhatsAppVerbose(options.verbose, `Inbound media download failed: ${String(err)}`);
|
|
1221
|
+
body = formatInboundMediaUnavailableText({
|
|
1222
|
+
body,
|
|
1223
|
+
mediaPlaceholder,
|
|
1224
|
+
notice: "[whatsapp attachment unavailable]"
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
if (!mediaPath && replyContext) try {
|
|
1228
|
+
await saveInboundMedia(await downloadQuotedInboundMedia(msg, sock, maxBytes));
|
|
1229
|
+
} catch (err) {
|
|
1230
|
+
logWhatsAppVerbose(options.verbose, `Quoted media download failed: ${String(err)}`);
|
|
1231
|
+
body = formatInboundMediaUnavailableText({
|
|
1232
|
+
body,
|
|
1233
|
+
notice: "[whatsapp quoted attachment unavailable]"
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
return {
|
|
1237
|
+
body,
|
|
1238
|
+
commandBody,
|
|
1239
|
+
location: location ?? void 0,
|
|
1240
|
+
contactContext,
|
|
1241
|
+
externalAdReplyContext,
|
|
1242
|
+
replyContext,
|
|
1243
|
+
mediaPath,
|
|
1244
|
+
mediaType,
|
|
1245
|
+
mediaFileName
|
|
1246
|
+
};
|
|
1247
|
+
};
|
|
1195
1248
|
const enqueueInboundMessage = async (msg, inbound, enriched, durable) => {
|
|
1196
1249
|
const chatJid = inbound.remoteJid;
|
|
1197
1250
|
const sendComposing = async () => {
|
|
@@ -1199,18 +1252,20 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1199
1252
|
if (!currentSock) return;
|
|
1200
1253
|
try {
|
|
1201
1254
|
await assertCanSendToJid(chatJid, currentSock);
|
|
1202
|
-
await
|
|
1255
|
+
await sendApiSocketOperations.sendPresenceUpdate("composing", chatJid);
|
|
1203
1256
|
} catch (err) {
|
|
1204
|
-
logWhatsAppVerbose
|
|
1257
|
+
logWhatsAppVerbose(options.verbose, `Presence update failed: ${String(err)}`);
|
|
1205
1258
|
}
|
|
1206
1259
|
};
|
|
1207
1260
|
const reply = async (text, optionsResult) => {
|
|
1208
|
-
const resolved = await
|
|
1209
|
-
|
|
1261
|
+
const resolved = await resolveOutboundMentionsForGroup(chatJid, text);
|
|
1262
|
+
const result = await sendTrackedMessage(chatJid, addWhatsAppOutboundMentionsToContent({ text: resolved.text }, resolved.mentionedJids), optionsResult);
|
|
1263
|
+
return normalizeWhatsAppSendResult(result, "text");
|
|
1210
1264
|
};
|
|
1211
1265
|
const sendMedia = async (payload, optionsValue) => {
|
|
1212
1266
|
const previewPayload = await addWhatsAppImagePreviewFields(payload);
|
|
1213
|
-
|
|
1267
|
+
const result = await sendTrackedMessage(chatJid, await applyOutboundMentionsToContent(chatJid, previewPayload), optionsValue);
|
|
1268
|
+
return normalizeWhatsAppSendResult(result, "media");
|
|
1214
1269
|
};
|
|
1215
1270
|
const timestamp = inbound.messageTimestampMs;
|
|
1216
1271
|
const mentionedJids = extractMentionedJids(msg.message);
|
|
@@ -1224,11 +1279,10 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1224
1279
|
mediaFileName: enriched.mediaFileName,
|
|
1225
1280
|
timestamp
|
|
1226
1281
|
}, "inbound message");
|
|
1227
|
-
const media = enriched.mediaPath || enriched.mediaType || enriched.mediaFileName
|
|
1282
|
+
const media = enriched.mediaPath || enriched.mediaType || enriched.mediaFileName ? {
|
|
1228
1283
|
path: enriched.mediaPath,
|
|
1229
1284
|
type: enriched.mediaType,
|
|
1230
|
-
fileName: enriched.mediaFileName
|
|
1231
|
-
kind: enriched.mediaKind
|
|
1285
|
+
fileName: enriched.mediaFileName
|
|
1232
1286
|
} : void 0;
|
|
1233
1287
|
const groupMentions = mentionedJids ? { jids: mentionedJids } : void 0;
|
|
1234
1288
|
const group = inbound.group && (inbound.groupSubject || inbound.groupParticipants?.length || groupMentions) ? {
|
|
@@ -1236,26 +1290,17 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1236
1290
|
participants: inbound.groupParticipants,
|
|
1237
1291
|
mentions: groupMentions
|
|
1238
1292
|
} : void 0;
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
payload: enriched.contactContext
|
|
1251
|
-
}] : [],
|
|
1252
|
-
...enriched.externalAdReplyContext ? [{
|
|
1253
|
-
label: "WhatsApp external ad reply",
|
|
1254
|
-
source: "whatsapp",
|
|
1255
|
-
type: "external_ad_reply",
|
|
1256
|
-
payload: enriched.externalAdReplyContext
|
|
1257
|
-
}] : []
|
|
1258
|
-
];
|
|
1293
|
+
const untrustedStructuredContext = [...enriched.contactContext ? [{
|
|
1294
|
+
label: "WhatsApp contact",
|
|
1295
|
+
source: "whatsapp",
|
|
1296
|
+
type: enriched.contactContext.kind,
|
|
1297
|
+
payload: enriched.contactContext
|
|
1298
|
+
}] : [], ...enriched.externalAdReplyContext ? [{
|
|
1299
|
+
label: "WhatsApp external ad reply",
|
|
1300
|
+
source: "whatsapp",
|
|
1301
|
+
type: "external_ad_reply",
|
|
1302
|
+
payload: enriched.externalAdReplyContext
|
|
1303
|
+
}] : []];
|
|
1259
1304
|
const inboundMessage = withDeprecatedWebInboundMessageFlatAliases({
|
|
1260
1305
|
admission: inbound.access.admission,
|
|
1261
1306
|
event: {
|
|
@@ -1266,7 +1311,7 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1266
1311
|
body: enriched.body,
|
|
1267
1312
|
commandBody: enriched.commandBody,
|
|
1268
1313
|
location: enriched.location ?? void 0,
|
|
1269
|
-
|
|
1314
|
+
untrustedStructuredContext: untrustedStructuredContext.length > 0 ? untrustedStructuredContext : void 0,
|
|
1270
1315
|
media
|
|
1271
1316
|
},
|
|
1272
1317
|
platform: {
|
|
@@ -1294,7 +1339,6 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1294
1339
|
context: enriched.replyContext,
|
|
1295
1340
|
id: enriched.replyContext.id,
|
|
1296
1341
|
body: enriched.replyContext.body,
|
|
1297
|
-
media: enriched.replyContext.media,
|
|
1298
1342
|
sender: {
|
|
1299
1343
|
displayName: enriched.replyContext.sender?.label ?? void 0,
|
|
1300
1344
|
jid: enriched.replyContext.sender?.jid ?? void 0,
|
|
@@ -1302,78 +1346,38 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1302
1346
|
}
|
|
1303
1347
|
} : void 0,
|
|
1304
1348
|
group,
|
|
1305
|
-
|
|
1349
|
+
dedupeKey: inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : void 0,
|
|
1350
|
+
durableId: durable.durableId,
|
|
1306
1351
|
readReceipt: durable.readReceipt,
|
|
1307
1352
|
receiveOrder: durable.receiveOrder
|
|
1308
1353
|
});
|
|
1354
|
+
const debounceKey = buildInboundDebounceKey(inboundMessage);
|
|
1355
|
+
if (debounceKey) {
|
|
1356
|
+
inboundMessage.debounceKey = debounceKey;
|
|
1357
|
+
if (inboundDebounceMs > 0 && shouldDebounceInboundMessage(inboundMessage)) {
|
|
1358
|
+
pendingDebounceKeys.add(debounceKey);
|
|
1359
|
+
publishPendingWorkState();
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1309
1362
|
if (inboundMessage.event.id) {
|
|
1310
1363
|
const admission = requireWhatsAppInboundAdmission(inboundMessage);
|
|
1311
1364
|
cacheInboundMessageMeta(admission.accountId, inboundMessage.platform.chatJid, inboundMessage.event.id, {
|
|
1312
1365
|
participant: inboundMessage.platform.senderJid,
|
|
1313
1366
|
participantE164: admission.conversation.kind === "direct" ? inboundMessage.platform.senderE164 : void 0,
|
|
1314
1367
|
body: inboundMessage.payload.body,
|
|
1315
|
-
media: enriched.nativeMedia,
|
|
1316
1368
|
fromMe: inboundMessage.platform.fromMe
|
|
1317
1369
|
});
|
|
1318
1370
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const preparation = durableId ? preparedInboundByDurableId.get(durableId) : void 0;
|
|
1328
|
-
if (durableId) preparedInboundByDurableId.delete(durableId);
|
|
1329
|
-
if (context.skipRecentOutboundEcho === true) return "completed";
|
|
1330
|
-
const prepared = await preparation;
|
|
1331
|
-
if (prepared === null) return "completed";
|
|
1332
|
-
const inbound = prepared ?? await normalizeInboundMessage(msg);
|
|
1333
|
-
if (!inbound) return "completed";
|
|
1334
|
-
if (await maybeResolveWhatsAppQuestionReaction({
|
|
1335
|
-
cfg: options.loadConfig?.() ?? options.cfg,
|
|
1336
|
-
accountId: options.accountId,
|
|
1337
|
-
msg,
|
|
1338
|
-
senderId: inbound.senderE164 ?? inbound.from,
|
|
1339
|
-
resolveReactionTargetJids,
|
|
1340
|
-
logDebug: (message) => logWhatsAppVerbose$1(options.verbose, message)
|
|
1341
|
-
})) return "completed";
|
|
1342
|
-
const readReceipt = buildReadReceiptTarget(inbound);
|
|
1343
|
-
const deliveryReadReceipt = inbound.access.isSelfChat ? void 0 : readReceipt;
|
|
1344
|
-
if (context.skipStaleAppend === true) {
|
|
1345
|
-
await maybeMarkNonSelfChatReadReceipt(inbound, readReceipt);
|
|
1346
|
-
return "completed";
|
|
1347
|
-
}
|
|
1348
|
-
const enriched = await enrichWhatsAppInboundMessage({
|
|
1349
|
-
msg,
|
|
1350
|
-
sock,
|
|
1351
|
-
mediaMaxMb: options.mediaMaxMb,
|
|
1352
|
-
logVerbose: (message) => logWhatsAppVerbose$1(options.verbose, message)
|
|
1353
|
-
});
|
|
1354
|
-
if (!enriched) {
|
|
1355
|
-
await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
|
|
1356
|
-
return "completed";
|
|
1371
|
+
try {
|
|
1372
|
+
Promise.resolve(debouncer.enqueue(inboundMessage)).catch((err) => {
|
|
1373
|
+
inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
|
|
1374
|
+
inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`);
|
|
1375
|
+
});
|
|
1376
|
+
} catch (err) {
|
|
1377
|
+
inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
|
|
1378
|
+
inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`);
|
|
1357
1379
|
}
|
|
1358
|
-
recordAcceptedInboundActivity(options.accountId);
|
|
1359
|
-
await enqueueInboundMessage(msg, inbound, enriched, {
|
|
1360
|
-
readReceipt: deliveryReadReceipt,
|
|
1361
|
-
receiveOrder: context.receiveOrder ?? context.receivedAt,
|
|
1362
|
-
turnAdoptionLifecycle: lifecycle
|
|
1363
|
-
});
|
|
1364
|
-
return "deferred";
|
|
1365
1380
|
};
|
|
1366
|
-
const durableInboundMonitor = createWhatsAppIngressMonitor({
|
|
1367
|
-
queue: durableInboundQueue,
|
|
1368
|
-
dispatch: async (admission, lifecycle) => ({ kind: await processDurableInboundMessage(admission, lifecycle) }),
|
|
1369
|
-
pollIntervalMs: WHATSAPP_INGRESS_DRAIN_INTERVAL_MS,
|
|
1370
|
-
onLog: (message) => inboundLogger.warn({ message }, "whatsapp ingress drain"),
|
|
1371
|
-
onError: (error) => inboundLogger.error({ error: formatError(error) }, "whatsapp durable inbound drain failed"),
|
|
1372
|
-
onActivityChange: (active) => {
|
|
1373
|
-
durableIngressActive = active;
|
|
1374
|
-
publishPendingWorkState();
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
1381
|
const handleMessagesUpsert = async (upsert) => {
|
|
1378
1382
|
if (upsert.type !== "notify" && upsert.type !== "append") return;
|
|
1379
1383
|
for (const msg of upsert.messages ?? []) {
|
|
@@ -1387,57 +1391,9 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1387
1391
|
selfLid: self.lid,
|
|
1388
1392
|
resolveInboundJid,
|
|
1389
1393
|
resolveReactionTargetJids,
|
|
1390
|
-
logVerboseMessage: (message) => logWhatsAppVerbose
|
|
1394
|
+
logVerboseMessage: (message) => logWhatsAppVerbose(options.verbose, message)
|
|
1391
1395
|
})) continue;
|
|
1392
|
-
|
|
1393
|
-
const skipStaleAppend = shouldSkipStaleAppend(msg, upsert.type);
|
|
1394
|
-
const skipRecentOutboundEcho = shouldSkipRecentOutboundEcho(msg);
|
|
1395
|
-
const remoteJid = msg.key?.remoteJid;
|
|
1396
|
-
const id = msg.key?.id;
|
|
1397
|
-
const durableId = remoteJid && id ? createHash("sha256").update(`${remoteJid}\n${id}`).digest("hex") : void 0;
|
|
1398
|
-
let resolvePrepared;
|
|
1399
|
-
if (durableId && !preparedInboundByDurableId.has(durableId)) {
|
|
1400
|
-
if (preparedInboundByDurableId.size >= 1e3) {
|
|
1401
|
-
const oldest = preparedInboundByDurableId.keys().next().value;
|
|
1402
|
-
if (oldest !== void 0) preparedInboundByDurableId.delete(oldest);
|
|
1403
|
-
}
|
|
1404
|
-
preparedInboundByDurableId.set(durableId, new Promise((resolve) => {
|
|
1405
|
-
resolvePrepared = resolve;
|
|
1406
|
-
}));
|
|
1407
|
-
}
|
|
1408
|
-
const finishPreparation = (inbound, keepForDrain = false) => {
|
|
1409
|
-
resolvePrepared?.(inbound);
|
|
1410
|
-
if (!keepForDrain && durableId && resolvePrepared) preparedInboundByDurableId.delete(durableId);
|
|
1411
|
-
};
|
|
1412
|
-
let result;
|
|
1413
|
-
try {
|
|
1414
|
-
result = await durableInboundMonitor.admit({
|
|
1415
|
-
message: msg,
|
|
1416
|
-
upsertType: upsert.type,
|
|
1417
|
-
skipStaleAppend,
|
|
1418
|
-
skipRecentOutboundEcho,
|
|
1419
|
-
receivedAt,
|
|
1420
|
-
receiveOrder
|
|
1421
|
-
}, { receivedAt });
|
|
1422
|
-
} catch (error) {
|
|
1423
|
-
finishPreparation(void 0);
|
|
1424
|
-
const formattedError = formatError(error);
|
|
1425
|
-
inboundLogger.error({ error: formattedError }, "failed persisting durable WhatsApp inbound after retries; message dropped");
|
|
1426
|
-
inboundConsoleLog.error(`Failed persisting durable WhatsApp inbound after retries; message dropped: ${formattedError}`);
|
|
1427
|
-
continue;
|
|
1428
|
-
}
|
|
1429
|
-
if (result.kind === "durable" && result.queueResult.kind === "completed") {
|
|
1430
|
-
finishPreparation(void 0);
|
|
1431
|
-
const inbound = await normalizeInboundMessage(msg);
|
|
1432
|
-
if (inbound) await maybeMarkNonSelfChatReadReceipt(inbound, buildReadReceiptTarget(inbound));
|
|
1433
|
-
} else if (result.kind === "durable" && result.queueResult.kind === "accepted") if (skipRecentOutboundEcho) finishPreparation(null);
|
|
1434
|
-
else try {
|
|
1435
|
-
finishPreparation(await normalizeInboundMessage(msg), true);
|
|
1436
|
-
} catch (error) {
|
|
1437
|
-
finishPreparation(void 0);
|
|
1438
|
-
inboundLogger.warn({ error: formatError(error) }, "failed preparing WhatsApp inbound identity; durable drain will normalize again");
|
|
1439
|
-
}
|
|
1440
|
-
else finishPreparation(void 0);
|
|
1396
|
+
await processDurableInboundMessage(msg, upsert.type, receiveOrder);
|
|
1441
1397
|
}
|
|
1442
1398
|
};
|
|
1443
1399
|
const handleMessagesUpsertEvent = (upsert) => {
|
|
@@ -1452,24 +1408,22 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1452
1408
|
publishPendingWorkState();
|
|
1453
1409
|
});
|
|
1454
1410
|
};
|
|
1411
|
+
const waitForPendingMessageHandlers = async () => {
|
|
1412
|
+
while (pendingMessageHandlers.size > 0) await Promise.all(Array.from(pendingMessageHandlers));
|
|
1413
|
+
};
|
|
1455
1414
|
const drainDebouncedInboundMessages = async () => {
|
|
1456
|
-
|
|
1415
|
+
while (pendingDebounceKeys.size > 0 || activeInboundFlushes.size > 0) {
|
|
1416
|
+
const debounceKeys = Array.from(pendingDebounceKeys);
|
|
1417
|
+
if (debounceKeys.length > 0) await Promise.all(debounceKeys.map((key) => debouncer.flushKey(key)));
|
|
1418
|
+
const flushes = Array.from(activeInboundFlushes);
|
|
1419
|
+
if (flushes.length > 0) await Promise.allSettled(flushes);
|
|
1420
|
+
await Promise.resolve();
|
|
1421
|
+
}
|
|
1457
1422
|
};
|
|
1458
1423
|
const drainInboundBeforeSocketClose = async () => {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
if (pendingMessageHandlers.size === 0) break;
|
|
1462
|
-
const handlers = Array.from(pendingMessageHandlers);
|
|
1463
|
-
await Promise.race([Promise.allSettled(handlers), messageDebouncer.waitForWorkOrIdle(handlers, pendingMessageHandlers.size === 0)]);
|
|
1464
|
-
if (pendingMessageHandlers.size === 0 && !messageDebouncer.hasPendingWork()) break;
|
|
1465
|
-
}
|
|
1424
|
+
groupMetadataCacheClosed = true;
|
|
1425
|
+
await waitForPendingMessageHandlers();
|
|
1466
1426
|
await drainDebouncedInboundMessages();
|
|
1467
|
-
for (;;) {
|
|
1468
|
-
await durableInboundMonitor.waitForIdle();
|
|
1469
|
-
if (!messageDebouncer.hasPendingWork()) break;
|
|
1470
|
-
await drainDebouncedInboundMessages();
|
|
1471
|
-
}
|
|
1472
|
-
await durableInboundMonitor.stop();
|
|
1473
1427
|
};
|
|
1474
1428
|
const drainInboundBeforeSocketCloseWithTimeout = async () => {
|
|
1475
1429
|
let timeout = null;
|
|
@@ -1482,212 +1436,8 @@ function createWhatsAppMessageDeliveryCoordinator(options) {
|
|
|
1482
1436
|
})]);
|
|
1483
1437
|
} finally {
|
|
1484
1438
|
if (timeout) clearTimeout(timeout);
|
|
1485
|
-
durableInboundMonitor.stop();
|
|
1486
|
-
}
|
|
1487
|
-
};
|
|
1488
|
-
let detachMessagesUpsert;
|
|
1489
|
-
const start = () => {
|
|
1490
|
-
if (detachMessagesUpsert) return;
|
|
1491
|
-
detachMessagesUpsert = socketSession.listen("messages.upsert", handleMessagesUpsertEvent);
|
|
1492
|
-
durableInboundMonitor.start();
|
|
1493
|
-
};
|
|
1494
|
-
const stopIntake = () => {
|
|
1495
|
-
detachMessagesUpsert?.();
|
|
1496
|
-
detachMessagesUpsert = void 0;
|
|
1497
|
-
};
|
|
1498
|
-
return {
|
|
1499
|
-
start,
|
|
1500
|
-
stopIntake,
|
|
1501
|
-
drain: drainInboundBeforeSocketCloseWithTimeout
|
|
1502
|
-
};
|
|
1503
|
-
}
|
|
1504
|
-
//#endregion
|
|
1505
|
-
//#region extensions/whatsapp/src/inbound/socket-session.ts
|
|
1506
|
-
const LOGGED_OUT_STATUS = DisconnectReason.loggedOut;
|
|
1507
|
-
const RECONNECT_IN_PROGRESS_ERROR = "no active socket - reconnection in progress";
|
|
1508
|
-
const BAILEYS_MESSAGE_TTL_MS = 600 * 1e3;
|
|
1509
|
-
function isDirectUserJid(jid) {
|
|
1510
|
-
return /^(\d+)(?::\d+)?@(s\.whatsapp\.net|c\.us|lid|hosted|hosted\.lid)$/i.test(jid.trim());
|
|
1511
|
-
}
|
|
1512
|
-
function getActiveReachoutTimelock(state) {
|
|
1513
|
-
if (state?.isActive !== true) return;
|
|
1514
|
-
const endsAt = state.timeEnforcementEnds?.getTime();
|
|
1515
|
-
return endsAt === void 0 || !Number.isFinite(endsAt) || endsAt > Date.now() ? state : void 0;
|
|
1516
|
-
}
|
|
1517
|
-
function formatReachoutTimelockError(state) {
|
|
1518
|
-
const details = [state.enforcementType ? `type=${state.enforcementType}` : void 0, state.timeEnforcementEnds instanceof Date && Number.isFinite(state.timeEnforcementEnds.getTime()) ? `until=${state.timeEnforcementEnds.toISOString()}` : void 0].filter(Boolean);
|
|
1519
|
-
return `WhatsApp reachout timelock is active; direct messages are temporarily blocked${details.length ? ` (${details.join(", ")})` : ""}`;
|
|
1520
|
-
}
|
|
1521
|
-
function isRetryableSendDisconnectError(error) {
|
|
1522
|
-
if (isWhatsAppSocketOperationTimeoutError(error)) return false;
|
|
1523
|
-
return /closed|reset|timed\s*out|disconnect|no active socket/i.test(formatError(error));
|
|
1524
|
-
}
|
|
1525
|
-
function shouldClearSocketRefAfterSendFailure(error) {
|
|
1526
|
-
return /closed|reset|disconnect|no active socket/i.test(formatError(error));
|
|
1527
|
-
}
|
|
1528
|
-
async function createWhatsAppAttachedSocketSession(options) {
|
|
1529
|
-
const { sock } = options;
|
|
1530
|
-
const connectedAtMs = Date.now();
|
|
1531
|
-
if (options.socketRef) options.socketRef.current = sock;
|
|
1532
|
-
const shouldRetryDisconnect = () => options.shouldRetryDisconnect?.() === true;
|
|
1533
|
-
const disconnectRetryPolicy = options.disconnectRetryPolicy ?? DEFAULT_RECONNECT_POLICY;
|
|
1534
|
-
const sendRetryMaxAttempts = disconnectRetryPolicy.maxAttempts > 0 ? disconnectRetryPolicy.maxAttempts : DEFAULT_RECONNECT_POLICY.maxAttempts;
|
|
1535
|
-
const sendOperationTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(options.socketTiming.defaultQueryTimeoutMs);
|
|
1536
|
-
let onCloseResolve = null;
|
|
1537
|
-
const onClose = new Promise((resolve) => {
|
|
1538
|
-
onCloseResolve = resolve;
|
|
1539
|
-
});
|
|
1540
|
-
const resolveClose = (reason) => {
|
|
1541
|
-
if (!onCloseResolve) return;
|
|
1542
|
-
const resolver = onCloseResolve;
|
|
1543
|
-
onCloseResolve = null;
|
|
1544
|
-
resolver(reason);
|
|
1545
|
-
};
|
|
1546
|
-
const presence = options.selfChatMode ? "unavailable" : "available";
|
|
1547
|
-
try {
|
|
1548
|
-
await createWhatsAppSocketOperationTimeoutAdapter(sock, sendOperationTimeoutMs).sendPresenceUpdate(presence);
|
|
1549
|
-
options.logVerbose(`Sent global '${presence}' presence on connect`);
|
|
1550
|
-
} catch (error) {
|
|
1551
|
-
options.logVerbose(`Failed to send '${presence}' presence on connect: ${String(error)}`);
|
|
1552
|
-
}
|
|
1553
|
-
const selfIdentity = await readWebSelfIdentityForDecision(options.authDir, sock.user);
|
|
1554
|
-
if (selfIdentity.outcome === "unstable") throw new WhatsAppAuthUnstableError("WhatsApp auth state is still stabilizing; retrying inbox attach.");
|
|
1555
|
-
const self = selfIdentity.identity;
|
|
1556
|
-
const getCurrentSock = () => {
|
|
1557
|
-
if (!options.socketRef) return sock;
|
|
1558
|
-
if (options.socketRef.current) return options.socketRef.current;
|
|
1559
|
-
if (!self.e164 && !self.jid && !self.lid) return null;
|
|
1560
|
-
const successor = getWhatsAppConnectionController(options.accountId);
|
|
1561
|
-
if (!successor) return null;
|
|
1562
|
-
const successorIdentity = successor.getSelfIdentity();
|
|
1563
|
-
if (!successorIdentity || !identitiesOverlap(self, successorIdentity)) return null;
|
|
1564
|
-
return successor.getCurrentSock();
|
|
1565
|
-
};
|
|
1566
|
-
const lidLookup = sock.signalRepository?.lidMapping;
|
|
1567
|
-
const resolveInboundJid = async (jid) => resolveJidToE164(jid, {
|
|
1568
|
-
authDir: options.authDir,
|
|
1569
|
-
lidLookup
|
|
1570
|
-
});
|
|
1571
|
-
const resolveReactionTargetJids = async (jid) => resolveEquivalentWhatsAppDirectChatJids(jid, {
|
|
1572
|
-
authDir: options.authDir,
|
|
1573
|
-
lidLookup
|
|
1574
|
-
});
|
|
1575
|
-
const rememberBaileysMessage = (remoteJid, messageId, message) => {
|
|
1576
|
-
if (!options.recentMessageKeys || !remoteJid || !messageId || !message) return;
|
|
1577
|
-
rememberWhatsAppBaileysCacheEntry(options.recentMessageKeys, `${remoteJid}:${messageId}`, message, BAILEYS_MESSAGE_TTL_MS);
|
|
1578
|
-
};
|
|
1579
|
-
const rememberOutboundMessage = (remoteJid, result) => {
|
|
1580
|
-
const messageId = typeof result === "object" && result && "key" in result ? result.key?.id ?? "" : "";
|
|
1581
|
-
if (!messageId) return;
|
|
1582
|
-
rememberRecentOutboundMessage({
|
|
1583
|
-
accountId: options.accountId,
|
|
1584
|
-
remoteJid,
|
|
1585
|
-
messageId
|
|
1586
|
-
});
|
|
1587
|
-
const message = typeof result === "object" && result && "message" in result ? result.message : void 0;
|
|
1588
|
-
rememberBaileysMessage(remoteJid, messageId, message);
|
|
1589
|
-
cacheInboundMessageMeta(options.accountId, remoteJid, messageId, {
|
|
1590
|
-
fromMe: true,
|
|
1591
|
-
body: extractText(message ?? void 0)
|
|
1592
|
-
});
|
|
1593
|
-
};
|
|
1594
|
-
const trackLateAcceptedSend = (jid, promise) => {
|
|
1595
|
-
promise.then((result) => {
|
|
1596
|
-
rememberOutboundMessage(jid, result);
|
|
1597
|
-
}, () => {});
|
|
1598
|
-
};
|
|
1599
|
-
let reachoutTimeLock;
|
|
1600
|
-
let reachoutTimeLockFetch;
|
|
1601
|
-
let reachoutTimeLockVersion = 0;
|
|
1602
|
-
let verifiedSendReady;
|
|
1603
|
-
const rememberReachoutTimeLock = (state) => {
|
|
1604
|
-
reachoutTimeLock = state;
|
|
1605
|
-
reachoutTimeLockVersion += 1;
|
|
1606
|
-
verifiedSendReady = void 0;
|
|
1607
|
-
};
|
|
1608
|
-
const fetchReachoutTimeLock = async (currentSock) => {
|
|
1609
|
-
if (typeof currentSock.fetchAccountReachoutTimelock !== "function") return;
|
|
1610
|
-
if (!reachoutTimeLockFetch) reachoutTimeLockFetch = currentSock.fetchAccountReachoutTimelock().then((state) => {
|
|
1611
|
-
rememberReachoutTimeLock(state);
|
|
1612
|
-
return state;
|
|
1613
|
-
}).catch((error) => {
|
|
1614
|
-
options.logVerbose(`Failed fetching WhatsApp reachout timelock before send: ${formatError(error)}`);
|
|
1615
|
-
}).finally(() => {
|
|
1616
|
-
reachoutTimeLockFetch = void 0;
|
|
1617
|
-
});
|
|
1618
|
-
return await reachoutTimeLockFetch;
|
|
1619
|
-
};
|
|
1620
|
-
const rememberVerifiedSendReady = (jid, currentSock) => {
|
|
1621
|
-
verifiedSendReady = {
|
|
1622
|
-
jid,
|
|
1623
|
-
sock: currentSock,
|
|
1624
|
-
reachoutTimeLockVersion
|
|
1625
|
-
};
|
|
1626
|
-
};
|
|
1627
|
-
const consumeVerifiedSendReady = (jid, currentSock) => {
|
|
1628
|
-
if (verifiedSendReady?.jid !== jid || verifiedSendReady.sock !== currentSock || verifiedSendReady.reachoutTimeLockVersion !== reachoutTimeLockVersion) return false;
|
|
1629
|
-
verifiedSendReady = void 0;
|
|
1630
|
-
return true;
|
|
1631
|
-
};
|
|
1632
|
-
const assertCanSendToJid = async (jid, currentSock, readinessOptions) => {
|
|
1633
|
-
if (!isDirectUserJid(jid)) return;
|
|
1634
|
-
if (readinessOptions?.useVerifiedReady && consumeVerifiedSendReady(jid, currentSock)) return;
|
|
1635
|
-
const state = getActiveReachoutTimelock(reachoutTimeLock) ?? await fetchReachoutTimeLock(currentSock);
|
|
1636
|
-
const activeState = getActiveReachoutTimelock(state);
|
|
1637
|
-
if (activeState) throw new Error(formatReachoutTimelockError(activeState));
|
|
1638
|
-
if (readinessOptions?.rememberReady && state) rememberVerifiedSendReady(jid, currentSock);
|
|
1639
|
-
};
|
|
1640
|
-
const assertSendReady = async (to) => {
|
|
1641
|
-
const currentSock = getCurrentSock();
|
|
1642
|
-
if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
1643
|
-
const jid = options.authDir ? toWhatsappJidWithLid(to, { authDir: options.authDir }) : toWhatsappJid(to);
|
|
1644
|
-
await assertCanSendToJid(jid, currentSock, { rememberReady: true });
|
|
1645
|
-
};
|
|
1646
|
-
const sendTrackedMessage = async (jid, content, sendOptions) => {
|
|
1647
|
-
let lastError = /* @__PURE__ */ new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
1648
|
-
for (let attempt = 1;; attempt += 1) {
|
|
1649
|
-
const currentSock = getCurrentSock();
|
|
1650
|
-
if (currentSock) try {
|
|
1651
|
-
await assertCanSendToJid(jid, currentSock, { useVerifiedReady: true });
|
|
1652
|
-
const result = await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs, { onSendMessageTimeout: ({ jid: timedOutJid, promise }) => {
|
|
1653
|
-
trackLateAcceptedSend(timedOutJid, promise);
|
|
1654
|
-
} }).sendMessage(jid, content, sendOptions);
|
|
1655
|
-
rememberOutboundMessage(jid, result);
|
|
1656
|
-
return result;
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
if (!shouldRetryDisconnect() || !isRetryableSendDisconnectError(error)) throw error;
|
|
1659
|
-
lastError = error;
|
|
1660
|
-
if (shouldClearSocketRefAfterSendFailure(error) && options.socketRef?.current === currentSock) options.socketRef.current = null;
|
|
1661
|
-
}
|
|
1662
|
-
else if (!shouldRetryDisconnect()) throw lastError;
|
|
1663
|
-
if (attempt >= sendRetryMaxAttempts) throw lastError;
|
|
1664
|
-
const delayMs = computeBackoff(disconnectRetryPolicy, attempt);
|
|
1665
|
-
options.logVerbose(`Waiting ${delayMs}ms for WhatsApp reconnect before retrying send to ${jid}: ${formatError(lastError)}`);
|
|
1666
|
-
try {
|
|
1667
|
-
await sleepWithAbort(delayMs, options.disconnectRetryAbortSignal);
|
|
1668
|
-
} catch {
|
|
1669
|
-
throw lastError;
|
|
1670
|
-
}
|
|
1671
1439
|
}
|
|
1672
1440
|
};
|
|
1673
|
-
const socketOperations = {
|
|
1674
|
-
sendMessage: (jid, content, sendOptions) => sendTrackedMessage(jid, content, sendOptions),
|
|
1675
|
-
sendPresenceUpdate: async (presenceLocal, jid) => {
|
|
1676
|
-
const currentSock = getCurrentSock();
|
|
1677
|
-
if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
|
|
1678
|
-
return await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs).sendPresenceUpdate(presenceLocal, jid);
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
const markRead = async (target) => {
|
|
1682
|
-
const { id, remoteJid, participant } = target;
|
|
1683
|
-
await withWhatsAppSocketOperationTimeout("readMessages", (getCurrentSock() ?? sock).readMessages([{
|
|
1684
|
-
remoteJid,
|
|
1685
|
-
id,
|
|
1686
|
-
participant,
|
|
1687
|
-
fromMe: false
|
|
1688
|
-
}]), sendOperationTimeoutMs);
|
|
1689
|
-
};
|
|
1690
|
-
const attachSockListener = (event, listener) => attachEmitterListener(sock.ev, event, listener);
|
|
1691
1441
|
const handleConnectionUpdate = (update) => {
|
|
1692
1442
|
try {
|
|
1693
1443
|
if ("reachoutTimeLock" in update) rememberReachoutTimeLock(update.reachoutTimeLock);
|
|
@@ -1700,135 +1450,110 @@ async function createWhatsAppAttachedSocketSession(options) {
|
|
|
1700
1450
|
error: update.lastDisconnect?.error
|
|
1701
1451
|
});
|
|
1702
1452
|
}
|
|
1703
|
-
} catch (
|
|
1704
|
-
|
|
1453
|
+
} catch (err) {
|
|
1454
|
+
inboundLogger.error({ error: String(err) }, "connection.update handler error");
|
|
1705
1455
|
resolveClose({
|
|
1706
1456
|
status: void 0,
|
|
1707
1457
|
isLoggedOut: false,
|
|
1708
|
-
error
|
|
1458
|
+
error: err
|
|
1709
1459
|
});
|
|
1710
1460
|
}
|
|
1711
1461
|
};
|
|
1712
|
-
|
|
1713
|
-
const
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
} catch (error) {
|
|
1724
|
-
options.logVerbose(`Socket close failed: ${String(error)}`);
|
|
1725
|
-
}
|
|
1462
|
+
const attachSockListener = (event, listener) => attachEmitterListener(sock.ev, event, listener);
|
|
1463
|
+
const detachMessagesUpsert = attachSockListener("messages.upsert", handleMessagesUpsertEvent);
|
|
1464
|
+
const detachConnectionUpdate = attachSockListener("connection.update", handleConnectionUpdate);
|
|
1465
|
+
const isFullGroupMetadataUpdate = (update) => typeof update.id === "string" && typeof update.subject === "string" && Array.isArray(update.participants);
|
|
1466
|
+
const rememberFullGroupMetadataUpdate = (jid, meta) => {
|
|
1467
|
+
if (groupMetadataCacheClosed) return;
|
|
1468
|
+
rememberWhatsAppBaileysCacheEntry(options.baileysGroupMetaCache, jid, meta, GROUP_META_TTL_MS);
|
|
1469
|
+
publishedGroupMetadataJids.add(jid);
|
|
1470
|
+
invalidatedGroupMetadataJids.delete(jid);
|
|
1471
|
+
rememberGroupMetadataCacheEntry(groupMetadataCache, jid, summarizeGroupMetaForReconnectCache(meta));
|
|
1472
|
+
groupMetaCache.delete(jid);
|
|
1726
1473
|
};
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
status: void 0,
|
|
1734
|
-
isLoggedOut: false,
|
|
1735
|
-
error: "closed"
|
|
1736
|
-
});
|
|
1737
|
-
},
|
|
1738
|
-
start,
|
|
1739
|
-
stop,
|
|
1740
|
-
closeSocket,
|
|
1741
|
-
listen: attachSockListener,
|
|
1742
|
-
getCurrentSock,
|
|
1743
|
-
resolveInboundJid,
|
|
1744
|
-
resolveReactionTargetJids,
|
|
1745
|
-
rememberBaileysMessage,
|
|
1746
|
-
assertCanSendToJid,
|
|
1747
|
-
assertSendReady,
|
|
1748
|
-
sendTrackedMessage,
|
|
1749
|
-
socketOperations,
|
|
1750
|
-
markRead
|
|
1474
|
+
const forgetFullGroupMetadata = (jid) => {
|
|
1475
|
+
options.baileysGroupMetaCache?.delete(jid);
|
|
1476
|
+
groupMetadataCache.delete(jid);
|
|
1477
|
+
groupMetaCache.delete(jid);
|
|
1478
|
+
publishedGroupMetadataJids.delete(jid);
|
|
1479
|
+
invalidatedGroupMetadataJids.add(jid);
|
|
1751
1480
|
};
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
socketRef: options.socketRef,
|
|
1764
|
-
accountId: options.accountId,
|
|
1765
|
-
authDir: options.authDir,
|
|
1766
|
-
selfChatMode: options.selfChatMode,
|
|
1767
|
-
socketTiming: options.socketTiming,
|
|
1768
|
-
shouldRetryDisconnect: options.shouldRetryDisconnect,
|
|
1769
|
-
disconnectRetryPolicy: options.disconnectRetryPolicy,
|
|
1770
|
-
disconnectRetryAbortSignal: options.disconnectRetryAbortSignal,
|
|
1771
|
-
recentMessageKeys: options.recentMessageKeys,
|
|
1772
|
-
logVerbose: (message) => logWhatsAppVerbose(options.verbose, message),
|
|
1773
|
-
logConnectionError: (error) => {
|
|
1774
|
-
inboundLogger.error({ error: String(error) }, "connection.update handler error");
|
|
1481
|
+
const detachGroupsUpsert = attachSockListener("groups.upsert", ((groups) => {
|
|
1482
|
+
for (const group of groups) if (group.id) rememberFullGroupMetadataUpdate(group.id, group);
|
|
1483
|
+
}));
|
|
1484
|
+
const detachGroupsUpdate = attachSockListener("groups.update", ((updates) => {
|
|
1485
|
+
for (const update of updates) {
|
|
1486
|
+
if (!update.id) continue;
|
|
1487
|
+
if (isFullGroupMetadataUpdate(update)) {
|
|
1488
|
+
rememberFullGroupMetadataUpdate(update.id, update);
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
forgetFullGroupMetadata(update.id);
|
|
1775
1492
|
}
|
|
1493
|
+
}));
|
|
1494
|
+
const detachGroupParticipantsUpdate = attachSockListener("group-participants.update", ((update) => {
|
|
1495
|
+
forgetFullGroupMetadata(update.id);
|
|
1496
|
+
}));
|
|
1497
|
+
const replayTask = replayPendingDurableInboundMessages().catch((err) => {
|
|
1498
|
+
inboundLogger.error({ error: String(err) }, "failed replaying durable WhatsApp inbound");
|
|
1499
|
+
inboundConsoleLog.error(`Failed replaying durable WhatsApp inbound: ${String(err)}`);
|
|
1500
|
+
});
|
|
1501
|
+
pendingMessageHandlers.add(replayTask);
|
|
1502
|
+
publishPendingWorkState();
|
|
1503
|
+
replayTask.finally(() => {
|
|
1504
|
+
pendingMessageHandlers.delete(replayTask);
|
|
1505
|
+
publishPendingWorkState();
|
|
1776
1506
|
});
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1507
|
+
(async () => {
|
|
1508
|
+
try {
|
|
1509
|
+
const groups = await sock.groupFetchAllParticipating();
|
|
1510
|
+
if (groupMetadataCacheClosed) return;
|
|
1511
|
+
for (const [jid, meta] of Object.entries(groups ?? {})) if (meta && !publishedGroupMetadataJids.has(jid) && !invalidatedGroupMetadataJids.has(jid)) {
|
|
1512
|
+
rememberGroupMetadataCacheEntry(groupMetadataCache, jid, summarizeGroupMetaForReconnectCache(meta));
|
|
1513
|
+
rememberWhatsAppBaileysCacheEntry(options.baileysGroupMetaCache, jid, meta, GROUP_META_TTL_MS);
|
|
1514
|
+
publishedGroupMetadataJids.add(jid);
|
|
1515
|
+
}
|
|
1516
|
+
logWhatsAppVerbose(options.verbose, `Hydrated ${Object.keys(groups ?? {}).length} participating groups on connect`);
|
|
1517
|
+
} catch (err) {
|
|
1518
|
+
const error = String(err);
|
|
1786
1519
|
inboundLogger.warn({ error }, "failed hydrating participating groups on connect");
|
|
1787
1520
|
inboundConsoleLog.warn(`Failed hydrating participating groups on connect: ${error}`);
|
|
1521
|
+
logWhatsAppVerbose(options.verbose, `Failed to hydrate participating groups on connect: ${error}`);
|
|
1788
1522
|
}
|
|
1789
|
-
});
|
|
1790
|
-
const delivery = createWhatsAppMessageDeliveryCoordinator({
|
|
1791
|
-
cfg: options.cfg,
|
|
1792
|
-
loadConfig: options.loadConfig,
|
|
1793
|
-
verbose: options.verbose,
|
|
1794
|
-
accountId: options.accountId,
|
|
1795
|
-
sock: options.sock,
|
|
1796
|
-
socketSession,
|
|
1797
|
-
groupMetadata,
|
|
1798
|
-
onMessage: options.onMessage,
|
|
1799
|
-
mediaMaxMb: options.mediaMaxMb,
|
|
1800
|
-
sendReadReceipts: options.sendReadReceipts,
|
|
1801
|
-
debounceMs: options.debounceMs,
|
|
1802
|
-
appendReplyWindow: options.appendReplyWindow,
|
|
1803
|
-
shouldDebounce: options.shouldDebounce,
|
|
1804
|
-
onPendingWorkChanged: options.onPendingWorkChanged,
|
|
1805
|
-
durableInboundQueue: options.durableInboundQueue ?? createWhatsAppDurableInboundQueue(options.accountId)
|
|
1806
|
-
});
|
|
1523
|
+
})();
|
|
1807
1524
|
const sendApi = createWebSendApi({
|
|
1808
|
-
sock:
|
|
1525
|
+
sock: sendApiSocketOperations,
|
|
1809
1526
|
defaultAccountId: options.accountId,
|
|
1810
|
-
resolveOutboundMentions: ({ jid, text }) =>
|
|
1527
|
+
resolveOutboundMentions: ({ jid, text }) => resolveOutboundMentionsForGroup(jid, text),
|
|
1811
1528
|
authDir: options.authDir
|
|
1812
1529
|
});
|
|
1813
|
-
delivery.start();
|
|
1814
|
-
socketSession.start();
|
|
1815
|
-
groupMetadata.start();
|
|
1816
1530
|
return {
|
|
1817
1531
|
close: async () => {
|
|
1818
|
-
delivery.stopIntake();
|
|
1819
|
-
socketSession.stop();
|
|
1820
|
-
groupMetadata.close();
|
|
1821
1532
|
try {
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1533
|
+
detachMessagesUpsert();
|
|
1534
|
+
detachConnectionUpdate();
|
|
1535
|
+
detachGroupsUpsert();
|
|
1536
|
+
detachGroupsUpdate();
|
|
1537
|
+
detachGroupParticipantsUpdate();
|
|
1538
|
+
await drainInboundBeforeSocketCloseWithTimeout();
|
|
1539
|
+
} catch (err) {
|
|
1540
|
+
logWhatsAppVerbose(options.verbose, `Inbound close drain failed: ${String(err)}`);
|
|
1827
1541
|
}
|
|
1542
|
+
try {
|
|
1543
|
+
closeInboundMonitorSocket(sock);
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
logWhatsAppVerbose(options.verbose, `Socket close failed: ${String(err)}`);
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1548
|
+
onClose,
|
|
1549
|
+
signalClose: (reason) => {
|
|
1550
|
+
resolveClose(reason ?? {
|
|
1551
|
+
status: void 0,
|
|
1552
|
+
isLoggedOut: false,
|
|
1553
|
+
error: "closed"
|
|
1554
|
+
});
|
|
1828
1555
|
},
|
|
1829
|
-
|
|
1830
|
-
signalClose: socketSession.signalClose,
|
|
1831
|
-
assertSendReady: socketSession.assertSendReady,
|
|
1556
|
+
assertSendReady: assertCanSendTo,
|
|
1832
1557
|
sendComposingTo: sendApi.sendComposingTo,
|
|
1833
1558
|
sendMessage: sendApi.sendMessage,
|
|
1834
1559
|
sendPoll: sendApi.sendPoll,
|
|
@@ -1836,7 +1561,7 @@ async function attachWebInboxToSocket(options) {
|
|
|
1836
1561
|
};
|
|
1837
1562
|
}
|
|
1838
1563
|
async function monitorWebInbox(options) {
|
|
1839
|
-
const socketTiming = options.socketTiming ?? resolveWhatsAppSocketTiming();
|
|
1564
|
+
const socketTiming = options.socketTiming ?? resolveWhatsAppSocketTiming(options.cfg);
|
|
1840
1565
|
const recentMessageKeys = options.recentMessageKeys ?? /* @__PURE__ */ new Map();
|
|
1841
1566
|
const baileysGroupMetaCache = options.baileysGroupMetaCache ?? /* @__PURE__ */ new Map();
|
|
1842
1567
|
const sock = await createWaSocket(false, options.verbose, {
|
|
@@ -1850,16 +1575,18 @@ async function monitorWebInbox(options) {
|
|
|
1850
1575
|
});
|
|
1851
1576
|
try {
|
|
1852
1577
|
await waitForWaConnection(sock, { timeoutMs: socketTiming.connectTimeoutMs });
|
|
1853
|
-
} catch (
|
|
1578
|
+
} catch (err) {
|
|
1854
1579
|
closeInboundMonitorSocket(sock);
|
|
1855
|
-
throw
|
|
1580
|
+
throw err;
|
|
1856
1581
|
}
|
|
1582
|
+
const shouldDebounce = options.shouldDebounce;
|
|
1583
|
+
const normalizeAdmittedWebInboundMessage = (msg) => requireAdmittedWhatsAppInboundMessage(normalizeWebInboundMessage(msg));
|
|
1857
1584
|
return attachWebInboxToSocket({
|
|
1858
1585
|
...options,
|
|
1859
1586
|
onMessage: async (msg) => {
|
|
1860
1587
|
await options.onMessage(normalizeAdmittedWebInboundMessage(msg));
|
|
1861
1588
|
},
|
|
1862
|
-
shouldDebounce:
|
|
1589
|
+
shouldDebounce: shouldDebounce ? (msg) => shouldDebounce(normalizeAdmittedWebInboundMessage(msg)) : void 0,
|
|
1863
1590
|
socketTiming,
|
|
1864
1591
|
sock,
|
|
1865
1592
|
recentMessageKeys,
|
|
@@ -1892,13 +1619,12 @@ function isBotMentionedFromTargets(msg, mentionCfg, targets) {
|
|
|
1892
1619
|
const isGroupConversation = requireWhatsAppInboundAdmission(msg).conversation.kind === "group";
|
|
1893
1620
|
const isSelfChat = explicitSelfChatOverride ? Boolean(mentionCfg.isSelfChat) : isSelfChatMode(targets.self.e164, mentionCfg.allowFrom) && !isGroupConversation;
|
|
1894
1621
|
const hasMentions = targets.normalizedMentions.length > 0;
|
|
1895
|
-
|
|
1896
|
-
if (hasNativeMentionsOutsideSelfChat) {
|
|
1622
|
+
if (hasMentions && !isSelfChat) {
|
|
1897
1623
|
for (const mention of targets.normalizedMentions) if (identitiesOverlap(targets.self, mention)) return true;
|
|
1624
|
+
return false;
|
|
1898
1625
|
} else if (hasMentions && isSelfChat) {}
|
|
1899
1626
|
const bodyClean = clean(msg.payload.body);
|
|
1900
1627
|
if (mentionCfg.mentionRegexes.some((re) => re.test(bodyClean))) return true;
|
|
1901
|
-
if (hasNativeMentionsOutsideSelfChat) return false;
|
|
1902
1628
|
if (targets.self.e164) {
|
|
1903
1629
|
const selfDigits = targets.self.e164.replace(/\D/g, "");
|
|
1904
1630
|
if (selfDigits) {
|
|
@@ -2037,7 +1763,6 @@ function createEchoTracker(params) {
|
|
|
2037
1763
|
const recentlySent = /* @__PURE__ */ new Set();
|
|
2038
1764
|
const maxItems = Math.max(1, params.maxItems ?? 100);
|
|
2039
1765
|
const buildCombinedKey = (p) => `combined:${p.sessionKey}:${p.combinedBody}`;
|
|
2040
|
-
const buildTextKey = (text, conversationId) => conversationId ? `${conversationId}\0${text}` : text;
|
|
2041
1766
|
const trim = () => {
|
|
2042
1767
|
while (recentlySent.size > maxItems) {
|
|
2043
1768
|
const firstKey = recentlySent.values().next().value;
|
|
@@ -2047,19 +1772,19 @@ function createEchoTracker(params) {
|
|
|
2047
1772
|
};
|
|
2048
1773
|
const rememberText = (text, opts) => {
|
|
2049
1774
|
if (!text) return;
|
|
2050
|
-
recentlySent.add(
|
|
1775
|
+
recentlySent.add(text);
|
|
2051
1776
|
if (opts.combinedBody && opts.combinedBodySessionKey) recentlySent.add(buildCombinedKey({
|
|
2052
1777
|
sessionKey: opts.combinedBodySessionKey,
|
|
2053
1778
|
combinedBody: opts.combinedBody
|
|
2054
1779
|
}));
|
|
2055
|
-
if (opts.logVerboseMessage) params.logVerbose?.(`Added to echo detection set (size now: ${recentlySent.size}): ${
|
|
1780
|
+
if (opts.logVerboseMessage) params.logVerbose?.(`Added to echo detection set (size now: ${recentlySent.size}): ${text.slice(0, 50)}...`);
|
|
2056
1781
|
trim();
|
|
2057
1782
|
};
|
|
2058
1783
|
return {
|
|
2059
1784
|
rememberText,
|
|
2060
|
-
has: (key
|
|
2061
|
-
forget: (key
|
|
2062
|
-
recentlySent.delete(
|
|
1785
|
+
has: (key) => recentlySent.has(key),
|
|
1786
|
+
forget: (key) => {
|
|
1787
|
+
recentlySent.delete(key);
|
|
2063
1788
|
},
|
|
2064
1789
|
buildCombinedKey
|
|
2065
1790
|
};
|
|
@@ -2080,7 +1805,8 @@ function formatWhatsAppInboundListeningLog(account) {
|
|
|
2080
1805
|
const DEFAULT_WHATSAPP_ACK_REACTION = "👀";
|
|
2081
1806
|
function resolveWhatsAppAckEmoji(params) {
|
|
2082
1807
|
if (!params.ackConfig) return "";
|
|
2083
|
-
|
|
1808
|
+
if (params.ackConfig.emoji !== void 0) return params.ackConfig.emoji.trim();
|
|
1809
|
+
return resolveAgentIdentityEmoji(params.cfg, params.agentId) ?? DEFAULT_WHATSAPP_ACK_REACTION;
|
|
2084
1810
|
}
|
|
2085
1811
|
function resolveAgentIdentityEmoji(cfg, agentId) {
|
|
2086
1812
|
return resolveAgentIdentity(cfg, agentId)?.emoji?.trim() || void 0;
|
|
@@ -2088,7 +1814,7 @@ function resolveAgentIdentityEmoji(cfg, agentId) {
|
|
|
2088
1814
|
//#endregion
|
|
2089
1815
|
//#region extensions/whatsapp/src/auto-reply/monitor/group-activation.ts
|
|
2090
1816
|
function hasNamedWhatsAppAccounts(cfg) {
|
|
2091
|
-
return Object.keys(cfg.channels?.whatsapp?.accounts ?? {}).some((accountId) => normalizeAccountId(accountId) !== DEFAULT_ACCOUNT_ID);
|
|
1817
|
+
return Object.keys(cfg.channels?.whatsapp?.accounts ?? {}).some((accountId) => normalizeAccountId$1(accountId) !== DEFAULT_ACCOUNT_ID$1);
|
|
2092
1818
|
}
|
|
2093
1819
|
function isActivationOnlyEntry(entry) {
|
|
2094
1820
|
return entry?.groupActivation !== void 0 && typeof entry?.sessionId !== "string" && typeof entry?.updatedAt !== "number";
|
|
@@ -2111,21 +1837,20 @@ async function resolveGroupActivationFor(params) {
|
|
|
2111
1837
|
...sessionScope,
|
|
2112
1838
|
sessionKey: params.sessionKey
|
|
2113
1839
|
});
|
|
2114
|
-
const activation = (normalizeAccountId(params.accountId) === DEFAULT_ACCOUNT_ID && hasNamedWhatsAppAccounts(params.cfg) && isActivationOnlyEntry(scopedEntry) ? void 0 : scopedEntry?.groupActivation) ?? legacyEntry?.groupActivation;
|
|
2115
|
-
if (activation !== void 0 && scopedEntry?.groupActivation === void 0) {
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
}
|
|
1840
|
+
const activation = (normalizeAccountId$1(params.accountId) === DEFAULT_ACCOUNT_ID$1 && hasNamedWhatsAppAccounts(params.cfg) && isActivationOnlyEntry(scopedEntry) ? void 0 : scopedEntry?.groupActivation) ?? legacyEntry?.groupActivation;
|
|
1841
|
+
if (activation !== void 0 && scopedEntry?.groupActivation === void 0) await patchSessionEntry({
|
|
1842
|
+
...sessionScope,
|
|
1843
|
+
sessionKey: params.sessionKey,
|
|
1844
|
+
fallbackEntry: {},
|
|
1845
|
+
replaceEntry: true,
|
|
1846
|
+
update: (entry) => {
|
|
1847
|
+
if (entry.groupActivation !== void 0) return null;
|
|
1848
|
+
return {
|
|
1849
|
+
...entry,
|
|
1850
|
+
groupActivation: activation
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
2129
1854
|
const defaultActivation = !resolveWhatsAppInboundPolicy({
|
|
2130
1855
|
cfg: params.cfg,
|
|
2131
1856
|
accountId: params.accountId
|
|
@@ -2133,41 +1858,33 @@ async function resolveGroupActivationFor(params) {
|
|
|
2133
1858
|
return normalizeGroupActivation(activation) ?? defaultActivation;
|
|
2134
1859
|
}
|
|
2135
1860
|
//#endregion
|
|
2136
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/reaction
|
|
2137
|
-
function
|
|
2138
|
-
|
|
2139
|
-
return sender.jid ?? sender.lid ?? void 0;
|
|
2140
|
-
}
|
|
2141
|
-
//#endregion
|
|
2142
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/reaction-eligibility.ts
|
|
2143
|
-
const DISABLED_REACTION = { status: "disabled" };
|
|
2144
|
-
async function resolveWhatsAppReactionEligibility(params) {
|
|
2145
|
-
const messageId = params.msg.event.id;
|
|
2146
|
-
if (!messageId) return DISABLED_REACTION;
|
|
1861
|
+
//#region extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts
|
|
1862
|
+
async function maybeSendAckReaction(params) {
|
|
1863
|
+
if (!params.msg.event.id) return null;
|
|
2147
1864
|
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
2148
1865
|
const accountId = admission.accountId;
|
|
2149
1866
|
if (resolveWhatsAppReactionLevel({
|
|
2150
1867
|
cfg: params.cfg,
|
|
2151
1868
|
accountId
|
|
2152
|
-
}).level === "off") return
|
|
2153
|
-
const
|
|
2154
|
-
if (scope === "off" || scope === "none") return DISABLED_REACTION;
|
|
1869
|
+
}).level === "off") return null;
|
|
1870
|
+
const ackConfig = params.cfg.channels?.whatsapp?.ackReaction;
|
|
2155
1871
|
const emoji = resolveWhatsAppAckEmoji({
|
|
2156
1872
|
cfg: params.cfg,
|
|
2157
1873
|
agentId: params.agentId,
|
|
2158
|
-
ackConfig
|
|
1874
|
+
ackConfig
|
|
2159
1875
|
});
|
|
2160
|
-
const directEnabled =
|
|
2161
|
-
const groupMode =
|
|
1876
|
+
const directEnabled = ackConfig?.direct ?? true;
|
|
1877
|
+
const groupMode = ackConfig?.group ?? "mentions";
|
|
2162
1878
|
const isGroup = admission.conversation.kind === "group";
|
|
1879
|
+
const conversationIdForCheck = admission.conversation.id;
|
|
2163
1880
|
const activation = isGroup ? await resolveGroupActivationFor({
|
|
2164
1881
|
cfg: params.cfg,
|
|
2165
1882
|
accountId,
|
|
2166
1883
|
agentId: params.agentId,
|
|
2167
1884
|
sessionKey: params.sessionKey,
|
|
2168
|
-
conversationId:
|
|
1885
|
+
conversationId: conversationIdForCheck
|
|
2169
1886
|
}) : null;
|
|
2170
|
-
|
|
1887
|
+
const shouldSendReaction = () => shouldAckReactionForWhatsApp({
|
|
2171
1888
|
emoji,
|
|
2172
1889
|
isDirect: admission.conversation.kind === "direct",
|
|
2173
1890
|
isGroup,
|
|
@@ -2175,50 +1892,32 @@ async function resolveWhatsAppReactionEligibility(params) {
|
|
|
2175
1892
|
groupMode,
|
|
2176
1893
|
wasMentioned: (params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned) === true,
|
|
2177
1894
|
groupActivated: activation === "always"
|
|
2178
|
-
})) return DISABLED_REACTION;
|
|
2179
|
-
const participant = resolveReactionParticipant(params.msg);
|
|
2180
|
-
return {
|
|
2181
|
-
status: "prepared",
|
|
2182
|
-
chatId: params.msg.platform.chatJid,
|
|
2183
|
-
messageId,
|
|
2184
|
-
emoji,
|
|
2185
|
-
reactionOptions: {
|
|
2186
|
-
verbose: params.verbose,
|
|
2187
|
-
fromMe: false,
|
|
2188
|
-
...participant ? { participant } : {},
|
|
2189
|
-
accountId,
|
|
2190
|
-
cfg: params.cfg
|
|
2191
|
-
}
|
|
2192
|
-
};
|
|
2193
|
-
}
|
|
2194
|
-
//#endregion
|
|
2195
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts
|
|
2196
|
-
async function maybeSendAckReaction(params) {
|
|
2197
|
-
const eligibility = await resolveWhatsAppReactionEligibility({
|
|
2198
|
-
cfg: params.cfg,
|
|
2199
|
-
msg: params.msg,
|
|
2200
|
-
agentId: params.agentId,
|
|
2201
|
-
sessionKey: params.sessionKey,
|
|
2202
|
-
verbose: params.verbose
|
|
2203
1895
|
});
|
|
2204
|
-
if (
|
|
2205
|
-
const { chatId, messageId, emoji, reactionOptions } = eligibility;
|
|
1896
|
+
if (!shouldSendReaction()) return null;
|
|
2206
1897
|
params.info({
|
|
2207
|
-
chatId,
|
|
2208
|
-
messageId,
|
|
1898
|
+
chatId: params.msg.platform.chatJid,
|
|
1899
|
+
messageId: params.msg.event.id,
|
|
2209
1900
|
emoji
|
|
2210
1901
|
}, "sending ack reaction");
|
|
1902
|
+
const sender = getSenderIdentity(params.msg);
|
|
1903
|
+
const reactionOptions = {
|
|
1904
|
+
verbose: params.verbose,
|
|
1905
|
+
fromMe: false,
|
|
1906
|
+
...sender.jid ? { participant: sender.jid } : {},
|
|
1907
|
+
accountId,
|
|
1908
|
+
cfg: params.cfg
|
|
1909
|
+
};
|
|
2211
1910
|
return createAckReactionHandle({
|
|
2212
1911
|
ackReactionValue: emoji,
|
|
2213
|
-
send: () => sendReactionWhatsApp(
|
|
2214
|
-
remove: () => sendReactionWhatsApp(
|
|
1912
|
+
send: () => sendReactionWhatsApp(params.msg.platform.chatJid, params.msg.event.id, emoji, reactionOptions),
|
|
1913
|
+
remove: () => sendReactionWhatsApp(params.msg.platform.chatJid, params.msg.event.id, "", reactionOptions),
|
|
2215
1914
|
onSendError: (err) => {
|
|
2216
1915
|
params.warn({
|
|
2217
1916
|
error: formatError(err),
|
|
2218
|
-
chatId,
|
|
2219
|
-
messageId
|
|
1917
|
+
chatId: params.msg.platform.chatJid,
|
|
1918
|
+
messageId: params.msg.event.id
|
|
2220
1919
|
}, "failed to send ack reaction");
|
|
2221
|
-
logVerbose(`WhatsApp ack reaction failed for chat ${
|
|
1920
|
+
logVerbose(`WhatsApp ack reaction failed for chat ${params.msg.platform.chatJid}: ${formatError(err)}`);
|
|
2222
1921
|
}
|
|
2223
1922
|
});
|
|
2224
1923
|
}
|
|
@@ -2351,12 +2050,17 @@ function formatGroupMembers(params) {
|
|
|
2351
2050
|
}
|
|
2352
2051
|
//#endregion
|
|
2353
2052
|
//#region extensions/whatsapp/src/auto-reply/monitor/group-gating.ts
|
|
2354
|
-
const
|
|
2355
|
-
|
|
2356
|
-
maxSize: 100
|
|
2357
|
-
});
|
|
2053
|
+
const MAX_GROUP_DROP_WARNINGS = 100;
|
|
2054
|
+
const groupDropWarned = /* @__PURE__ */ new Set();
|
|
2358
2055
|
function shouldWarnForGroupDrop(warnKey) {
|
|
2359
|
-
|
|
2056
|
+
if (groupDropWarned.has(warnKey)) return false;
|
|
2057
|
+
groupDropWarned.add(warnKey);
|
|
2058
|
+
while (groupDropWarned.size > MAX_GROUP_DROP_WARNINGS) {
|
|
2059
|
+
const oldest = groupDropWarned.values().next().value;
|
|
2060
|
+
if (!oldest) break;
|
|
2061
|
+
groupDropWarned.delete(oldest);
|
|
2062
|
+
}
|
|
2063
|
+
return true;
|
|
2360
2064
|
}
|
|
2361
2065
|
function isOwnerSender(baseMentionConfig, msg, authDir) {
|
|
2362
2066
|
const sender = normalizeE164(getSenderIdentity(msg, authDir).e164 ?? "");
|
|
@@ -2374,13 +2078,7 @@ function recordPendingGroupHistoryEntry(params) {
|
|
|
2374
2078
|
body: params.body ?? params.msg.payload.body,
|
|
2375
2079
|
timestamp: params.msg.event.timestamp,
|
|
2376
2080
|
id: params.msg.event.id,
|
|
2377
|
-
senderJid: senderIdentity.jid ?? params.msg.platform.senderJid
|
|
2378
|
-
...params.body === void 0 && params.msg.payload.media ? { media: [{
|
|
2379
|
-
path: params.msg.payload.media.path,
|
|
2380
|
-
url: params.msg.payload.media.url ?? params.msg.payload.media.path,
|
|
2381
|
-
contentType: params.msg.payload.media.type,
|
|
2382
|
-
kind: params.msg.payload.media.kind ?? void 0
|
|
2383
|
-
}] } : {}
|
|
2081
|
+
senderJid: senderIdentity.jid ?? params.msg.platform.senderJid
|
|
2384
2082
|
}
|
|
2385
2083
|
});
|
|
2386
2084
|
}
|
|
@@ -2468,11 +2166,13 @@ async function applyGroupGating(params) {
|
|
|
2468
2166
|
conversationId
|
|
2469
2167
|
}) !== "always";
|
|
2470
2168
|
const replyContext = getReplyContext(params.msg, params.authDir);
|
|
2169
|
+
const implicitReplyToSelf = params.selfChatMode === true && identitiesOverlap(self, sender);
|
|
2170
|
+
const implicitMentionKinds = implicitMentionKindWhen("quoted_bot", !implicitReplyToSelf && identitiesOverlap(self, replyContext?.sender));
|
|
2471
2171
|
const mentionDecision = resolveInboundMentionDecision({
|
|
2472
2172
|
facts: {
|
|
2473
2173
|
canDetectMention: true,
|
|
2474
2174
|
wasMentioned,
|
|
2475
|
-
implicitMentionKinds
|
|
2175
|
+
implicitMentionKinds
|
|
2476
2176
|
},
|
|
2477
2177
|
policy: {
|
|
2478
2178
|
isGroup: true,
|
|
@@ -2559,57 +2259,6 @@ function resolveWhatsAppDirectSystemPrompt(params) {
|
|
|
2559
2259
|
return wildcard != null ? wildcard.trim() || void 0 : void 0;
|
|
2560
2260
|
}
|
|
2561
2261
|
//#endregion
|
|
2562
|
-
//#region extensions/whatsapp/src/outbound-retry.ts
|
|
2563
|
-
const WHATSAPP_OUTBOUND_MAX_ATTEMPTS = 3;
|
|
2564
|
-
const WHATSAPP_OUTBOUND_MIN_DELAY_MS = 500;
|
|
2565
|
-
const WHATSAPP_OUTBOUND_MAX_DELAY_MS = 1e3;
|
|
2566
|
-
const WHATSAPP_RETRYABLE_OUTBOUND_ERROR_PATTERN = /closed|reset|timed\s*out|disconnect/i;
|
|
2567
|
-
var WhatsAppOutboundRetryError = class extends Error {
|
|
2568
|
-
constructor(original) {
|
|
2569
|
-
super(formatError(original), { cause: original });
|
|
2570
|
-
this.original = original;
|
|
2571
|
-
}
|
|
2572
|
-
};
|
|
2573
|
-
function isRetryableWhatsAppOutboundError(error) {
|
|
2574
|
-
if (isWhatsAppSocketOperationTimeoutError(error)) return false;
|
|
2575
|
-
return WHATSAPP_RETRYABLE_OUTBOUND_ERROR_PATTERN.test(formatError(error));
|
|
2576
|
-
}
|
|
2577
|
-
async function sendWhatsAppOutboundWithRetry(params) {
|
|
2578
|
-
const runWithRetry = createChannelApiRetryRunner({
|
|
2579
|
-
retry: {
|
|
2580
|
-
attempts: WHATSAPP_OUTBOUND_MAX_ATTEMPTS,
|
|
2581
|
-
minDelayMs: WHATSAPP_OUTBOUND_MIN_DELAY_MS,
|
|
2582
|
-
maxDelayMs: WHATSAPP_OUTBOUND_MAX_DELAY_MS,
|
|
2583
|
-
jitter: 0
|
|
2584
|
-
},
|
|
2585
|
-
strictShouldRetry: true,
|
|
2586
|
-
retryAfterMs: () => void 0,
|
|
2587
|
-
shouldRetry: (error, attempt) => {
|
|
2588
|
-
if (!(error instanceof WhatsAppOutboundRetryError) || !isRetryableWhatsAppOutboundError(error.original)) return false;
|
|
2589
|
-
params.onRetry?.({
|
|
2590
|
-
attempt,
|
|
2591
|
-
maxAttempts: WHATSAPP_OUTBOUND_MAX_ATTEMPTS,
|
|
2592
|
-
backoffMs: Math.min(WHATSAPP_OUTBOUND_MIN_DELAY_MS * 2 ** (attempt - 1), WHATSAPP_OUTBOUND_MAX_DELAY_MS),
|
|
2593
|
-
error: error.original,
|
|
2594
|
-
errorText: formatError(error.original)
|
|
2595
|
-
});
|
|
2596
|
-
return true;
|
|
2597
|
-
}
|
|
2598
|
-
});
|
|
2599
|
-
try {
|
|
2600
|
-
return await runWithRetry(async () => {
|
|
2601
|
-
try {
|
|
2602
|
-
return await params.send();
|
|
2603
|
-
} catch (error) {
|
|
2604
|
-
throw new WhatsAppOutboundRetryError(error);
|
|
2605
|
-
}
|
|
2606
|
-
});
|
|
2607
|
-
} catch (error) {
|
|
2608
|
-
if (error instanceof WhatsAppOutboundRetryError) throw error.original;
|
|
2609
|
-
throw error;
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
//#endregion
|
|
2613
2262
|
//#region extensions/whatsapp/src/auto-reply/util.ts
|
|
2614
2263
|
function elide(text, limit = 400) {
|
|
2615
2264
|
if (!text) return text;
|
|
@@ -2649,26 +2298,13 @@ function isLikelyWhatsAppCryptoError(reason) {
|
|
|
2649
2298
|
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
|
|
2650
2299
|
return Object.prototype.toString.call(value);
|
|
2651
2300
|
};
|
|
2652
|
-
const
|
|
2301
|
+
const raw = reason instanceof Error ? `${reason.message}\n${reason.stack ?? ""}` : formatReason(reason);
|
|
2302
|
+
const haystack = normalizeLowercaseStringOrEmpty(raw);
|
|
2653
2303
|
if (!(haystack.includes("unsupported state or unable to authenticate data") || haystack.includes("bad mac"))) return false;
|
|
2654
2304
|
return haystack.includes("baileys") || haystack.includes("noise-handler") || haystack.includes("aesdecryptgcm");
|
|
2655
2305
|
}
|
|
2656
2306
|
//#endregion
|
|
2657
2307
|
//#region extensions/whatsapp/src/auto-reply/deliver-reply.ts
|
|
2658
|
-
function createWhatsAppReplyTransportContext(msg) {
|
|
2659
|
-
const admission = requireWhatsAppInboundAdmission(msg);
|
|
2660
|
-
return {
|
|
2661
|
-
accountId: admission.accountId,
|
|
2662
|
-
conversationId: admission.conversation.id,
|
|
2663
|
-
conversationKind: admission.conversation.kind,
|
|
2664
|
-
chatJid: msg.platform.chatJid,
|
|
2665
|
-
senderJid: msg.platform.senderJid,
|
|
2666
|
-
recipientJid: msg.platform.recipientJid,
|
|
2667
|
-
correlationId: msg.event.id,
|
|
2668
|
-
reply: msg.platform.reply,
|
|
2669
|
-
sendMedia: msg.platform.sendMedia
|
|
2670
|
-
};
|
|
2671
|
-
}
|
|
2672
2308
|
function resolveWhatsAppReceiptKind(results) {
|
|
2673
2309
|
if (results.length > 0 && results.every((result) => result.kind === "text")) return "text";
|
|
2674
2310
|
if (results.length > 0 && results.every((result) => result.kind === "media")) return "media";
|
|
@@ -2706,9 +2342,10 @@ function createWhatsAppReplyDeliveryReceipt(results) {
|
|
|
2706
2342
|
});
|
|
2707
2343
|
}
|
|
2708
2344
|
async function deliverWebReply(params) {
|
|
2709
|
-
const { replyResult,
|
|
2710
|
-
const
|
|
2711
|
-
const
|
|
2345
|
+
const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
|
|
2346
|
+
const admission = requireWhatsAppInboundAdmission(msg);
|
|
2347
|
+
const conversationId = admission.conversation.id;
|
|
2348
|
+
const isGroupConversation = admission.conversation.kind === "group";
|
|
2712
2349
|
const replyStarted = Date.now();
|
|
2713
2350
|
const sendResults = [];
|
|
2714
2351
|
const rememberSendResult = (result) => {
|
|
@@ -2729,24 +2366,25 @@ async function deliverWebReply(params) {
|
|
|
2729
2366
|
const tableMode = params.tableMode ?? "code";
|
|
2730
2367
|
const chunkMode = params.chunkMode ?? "length";
|
|
2731
2368
|
const normalizedReply = params.normalizedReplyResult ?? normalizeWhatsAppOutboundPayload(replyResult, { normalizeText: normalizeWhatsAppPayloadTextPreservingIndentation });
|
|
2732
|
-
const
|
|
2369
|
+
const convertedText = markdownToWhatsApp(convertMarkdownTables$1(normalizedReply.text ?? "", tableMode));
|
|
2370
|
+
const textChunks = chunkMarkdownTextWithMode(convertedText, textLimit, chunkMode);
|
|
2733
2371
|
const mediaList = normalizedReply.mediaUrls ?? [];
|
|
2734
2372
|
const getQuote = () => {
|
|
2735
2373
|
if (!replyResult.replyToId) return;
|
|
2736
|
-
const cached = lookupInboundMessageMeta(
|
|
2374
|
+
const cached = lookupInboundMessageMeta(admission.accountId, msg.platform.chatJid, replyResult.replyToId);
|
|
2737
2375
|
return buildQuotedMessageOptions({
|
|
2738
2376
|
messageId: replyResult.replyToId,
|
|
2739
|
-
remoteJid:
|
|
2377
|
+
remoteJid: msg.platform.chatJid,
|
|
2740
2378
|
fromMe: cached?.fromMe ?? false,
|
|
2741
|
-
participant: cached?.participant ?? (isGroupConversation ?
|
|
2742
|
-
messageText: cached?.body ?? ""
|
|
2743
|
-
media: cached?.media
|
|
2379
|
+
participant: cached?.participant ?? (isGroupConversation ? msg.platform.senderJid : void 0),
|
|
2380
|
+
messageText: cached?.body ?? ""
|
|
2744
2381
|
});
|
|
2745
2382
|
};
|
|
2746
|
-
const sendWithRetry = async (fn, label) => {
|
|
2383
|
+
const sendWithRetry = async (fn, label, maxAttempts = 3) => {
|
|
2747
2384
|
try {
|
|
2748
2385
|
return await sendWhatsAppOutboundWithRetry({
|
|
2749
2386
|
send: fn,
|
|
2387
|
+
maxAttempts,
|
|
2750
2388
|
onRetry: ({ attempt, maxAttempts: retryMaxAttempts, backoffMs, errorText }) => {
|
|
2751
2389
|
logVerbose(`Retrying ${label} to ${conversationId} after failure (${attempt}/${retryMaxAttempts - 1}) in ${backoffMs}ms: ${errorText}`);
|
|
2752
2390
|
}
|
|
@@ -2761,7 +2399,7 @@ async function deliverWebReply(params) {
|
|
|
2761
2399
|
for (const [index, chunk] of textChunks.entries()) {
|
|
2762
2400
|
const chunkStarted = Date.now();
|
|
2763
2401
|
const quote = getQuote();
|
|
2764
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2402
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.reply(chunk, quote), "text"));
|
|
2765
2403
|
if (!skipLog) {
|
|
2766
2404
|
const durationMs = Date.now() - chunkStarted;
|
|
2767
2405
|
whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${conversationId} (${durationMs.toFixed(0)}ms)`);
|
|
@@ -2769,10 +2407,10 @@ async function deliverWebReply(params) {
|
|
|
2769
2407
|
}
|
|
2770
2408
|
const delivery = finishDelivery();
|
|
2771
2409
|
const logPayload = {
|
|
2772
|
-
correlationId:
|
|
2410
|
+
correlationId: msg.event.id ?? newConnectionId(),
|
|
2773
2411
|
connectionId: connectionId ?? null,
|
|
2774
2412
|
to: conversationId,
|
|
2775
|
-
from:
|
|
2413
|
+
from: msg.platform.recipientJid,
|
|
2776
2414
|
text: elide(replyResult.text, 240),
|
|
2777
2415
|
mediaUrl: null,
|
|
2778
2416
|
mediaSizeBytes: null,
|
|
@@ -2784,55 +2422,56 @@ async function deliverWebReply(params) {
|
|
|
2784
2422
|
return delivery;
|
|
2785
2423
|
}
|
|
2786
2424
|
const remainingText = [...textChunks];
|
|
2425
|
+
const leadingCaption = remainingText.shift() || "";
|
|
2787
2426
|
await sendMediaWithLeadingCaption({
|
|
2788
2427
|
mediaUrls: mediaList,
|
|
2789
|
-
caption:
|
|
2428
|
+
caption: leadingCaption,
|
|
2790
2429
|
send: async ({ mediaUrl, caption }) => {
|
|
2791
|
-
const media = await prepareWhatsAppOutboundMedia(await loadWebMedia(mediaUrl, {
|
|
2430
|
+
const media = await prepareWhatsAppOutboundMedia(await loadWebMedia$1(mediaUrl, {
|
|
2792
2431
|
maxBytes: maxMediaBytes,
|
|
2793
2432
|
localRoots: params.mediaLocalRoots
|
|
2794
2433
|
}), mediaUrl);
|
|
2795
2434
|
if (shouldLogVerbose()) {
|
|
2796
|
-
logVerbose(`Web auto-reply media size: ${(media.buffer.length /
|
|
2435
|
+
logVerbose(`Web auto-reply media size: ${(media.buffer.length / 1048576).toFixed(2)}MB`);
|
|
2797
2436
|
logVerbose(`Web auto-reply media source: ${mediaUrl} (kind ${media.kind})`);
|
|
2798
2437
|
}
|
|
2799
2438
|
if (media.kind === "image") {
|
|
2800
2439
|
const quote = getQuote();
|
|
2801
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2440
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
|
|
2802
2441
|
image: media.buffer,
|
|
2803
2442
|
caption,
|
|
2804
2443
|
mimetype: media.mimetype
|
|
2805
2444
|
}, quote), "media:image"));
|
|
2806
2445
|
} else if (media.kind === "audio") {
|
|
2807
2446
|
const quote = getQuote();
|
|
2808
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2447
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
|
|
2809
2448
|
audio: media.buffer,
|
|
2810
2449
|
ptt: true,
|
|
2811
2450
|
mimetype: media.mimetype
|
|
2812
2451
|
}, quote), "media:audio"));
|
|
2813
|
-
if (caption) rememberSendResult(await sendWithRetry(() =>
|
|
2452
|
+
if (caption) rememberSendResult(await sendWithRetry(() => msg.platform.reply(caption, quote), "media:audio-text"));
|
|
2814
2453
|
} else if (media.kind === "video") {
|
|
2815
2454
|
const quote = getQuote();
|
|
2816
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2455
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
|
|
2817
2456
|
video: media.buffer,
|
|
2818
2457
|
caption,
|
|
2819
2458
|
mimetype: media.mimetype
|
|
2820
2459
|
}, quote), "media:video"));
|
|
2821
2460
|
} else {
|
|
2822
2461
|
const quote = getQuote();
|
|
2823
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2462
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.sendMedia({
|
|
2824
2463
|
document: media.buffer,
|
|
2825
2464
|
fileName: media.fileName,
|
|
2826
2465
|
caption,
|
|
2827
2466
|
mimetype: media.mimetype
|
|
2828
2467
|
}, quote), "media:document"));
|
|
2829
2468
|
}
|
|
2830
|
-
whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length /
|
|
2469
|
+
whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / 1048576).toFixed(2)}MB)`);
|
|
2831
2470
|
replyLogger.info({
|
|
2832
|
-
correlationId:
|
|
2471
|
+
correlationId: msg.event.id ?? newConnectionId(),
|
|
2833
2472
|
connectionId: connectionId ?? null,
|
|
2834
2473
|
to: conversationId,
|
|
2835
|
-
from:
|
|
2474
|
+
from: msg.platform.recipientJid,
|
|
2836
2475
|
text: caption ?? null,
|
|
2837
2476
|
mediaUrl,
|
|
2838
2477
|
mediaSizeBytes: media.buffer.length,
|
|
@@ -2848,16 +2487,16 @@ async function deliverWebReply(params) {
|
|
|
2848
2487
|
}, "failed to send web media reply");
|
|
2849
2488
|
if (!isFirst) {
|
|
2850
2489
|
whatsappOutboundLog.warn(`Trailing media failed; sent warning to ${conversationId}`);
|
|
2851
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2490
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.reply("⚠️ Media unavailable.", getQuote()), "media:fallback-unavailable"));
|
|
2852
2491
|
return;
|
|
2853
2492
|
}
|
|
2854
2493
|
const fallbackText = [caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
|
|
2855
2494
|
if (!fallbackText) return;
|
|
2856
2495
|
whatsappOutboundLog.warn(`Media skipped; sent text-only to ${conversationId}`);
|
|
2857
|
-
rememberSendResult(await sendWithRetry(() =>
|
|
2496
|
+
rememberSendResult(await sendWithRetry(() => msg.platform.reply(fallbackText, getQuote()), "media:fallback-text"));
|
|
2858
2497
|
}
|
|
2859
2498
|
});
|
|
2860
|
-
for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() =>
|
|
2499
|
+
for (const chunk of remainingText) rememberSendResult(await sendWithRetry(() => msg.platform.reply(chunk, getQuote()), "media:text"));
|
|
2861
2500
|
return finishDelivery();
|
|
2862
2501
|
}
|
|
2863
2502
|
//#endregion
|
|
@@ -2875,106 +2514,32 @@ function isWhatsAppSupplementalSenderAllowed(params) {
|
|
|
2875
2514
|
return false;
|
|
2876
2515
|
}
|
|
2877
2516
|
function resolveVisibleWhatsAppGroupHistory(params) {
|
|
2517
|
+
if (params.groupPolicy !== "allowlist") return params.history;
|
|
2878
2518
|
return filterSupplementalContextItems({
|
|
2879
2519
|
items: params.history,
|
|
2880
2520
|
mode: params.mode,
|
|
2881
2521
|
kind: "history",
|
|
2882
|
-
isSenderAllowed: (entry) =>
|
|
2883
|
-
isGroup: true,
|
|
2884
|
-
groupPolicy: params.groupPolicy,
|
|
2522
|
+
isSenderAllowed: (entry) => isWhatsAppSupplementalSenderAllowed({
|
|
2885
2523
|
allowFrom: params.groupAllowFrom,
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
authDir: params.authDir,
|
|
2889
|
-
sender: entry.senderJid ? { jid: entry.senderJid } : null
|
|
2890
|
-
})
|
|
2524
|
+
authDir: params.authDir,
|
|
2525
|
+
sender: entry.senderJid ? { jid: entry.senderJid } : null
|
|
2891
2526
|
})
|
|
2892
2527
|
}).items;
|
|
2893
2528
|
}
|
|
2894
2529
|
function resolveVisibleWhatsAppReplyContext(params) {
|
|
2895
2530
|
const replyTo = getReplyContext(params.msg, params.authDir);
|
|
2896
2531
|
if (!replyTo) return null;
|
|
2897
|
-
const
|
|
2898
|
-
const previewBody = [replyTo.body, formatMediaPlaceholderText(replyTo.media ? [replyTo.media] : [])].filter(Boolean).join("\n");
|
|
2899
|
-
const senderAllowed = resolveInboundSupplementalSenderAllowed({
|
|
2900
|
-
isGroup: admission.conversation.kind === "group",
|
|
2901
|
-
groupPolicy: params.groupPolicy,
|
|
2532
|
+
const senderAllowed = requireWhatsAppInboundAdmission(params.msg).conversation.kind !== "group" || params.groupPolicy !== "allowlist" ? true : isWhatsAppSupplementalSenderAllowed({
|
|
2902
2533
|
allowFrom: params.groupAllowFrom,
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
authDir: params.authDir,
|
|
2906
|
-
sender: replyTo.sender
|
|
2907
|
-
})
|
|
2534
|
+
authDir: params.authDir,
|
|
2535
|
+
sender: replyTo.sender
|
|
2908
2536
|
});
|
|
2909
|
-
|
|
2537
|
+
return filterChannelInboundQuoteContext(params.mode, {
|
|
2910
2538
|
id: replyTo.id,
|
|
2911
|
-
body:
|
|
2539
|
+
body: replyTo.body,
|
|
2912
2540
|
sender: replyTo.sender?.label ?? void 0,
|
|
2913
2541
|
senderAllowed
|
|
2914
|
-
});
|
|
2915
|
-
return visible ? {
|
|
2916
|
-
...replyTo,
|
|
2917
|
-
body: visible.body ?? ""
|
|
2918
|
-
} : null;
|
|
2919
|
-
}
|
|
2920
|
-
//#endregion
|
|
2921
|
-
//#region extensions/whatsapp/src/auto-reply/monitor/prepared-inbound.ts
|
|
2922
|
-
function resolvePreparedCommandFacts(command) {
|
|
2923
|
-
if (!command) return;
|
|
2924
|
-
const { authorization, ...facts } = command;
|
|
2925
|
-
return {
|
|
2926
|
-
...facts,
|
|
2927
|
-
...authorization.kind === "not_checked" ? {} : { authorized: authorization.kind === "authorized" }
|
|
2928
|
-
};
|
|
2929
|
-
}
|
|
2930
|
-
function projectPreparedChannelInbound(params) {
|
|
2931
|
-
const { inbound, control } = params;
|
|
2932
|
-
const command = resolvePreparedCommandFacts(inbound.command);
|
|
2933
|
-
const commandAuthorization = inbound.command?.authorization;
|
|
2934
|
-
const commandAccess = commandAuthorization && commandAuthorization.kind !== "not_checked" ? { authorized: commandAuthorization.kind === "authorized" } : void 0;
|
|
2935
|
-
return {
|
|
2936
|
-
input: {
|
|
2937
|
-
id: inbound.event.id,
|
|
2938
|
-
timestamp: inbound.event.timestamp,
|
|
2939
|
-
rawText: inbound.message.rawBody,
|
|
2940
|
-
textForAgent: inbound.message.bodyForAgent,
|
|
2941
|
-
textForCommands: inbound.message.commandBody,
|
|
2942
|
-
raw: inbound
|
|
2943
|
-
},
|
|
2944
|
-
context: buildChannelInboundEventContext({
|
|
2945
|
-
...inbound,
|
|
2946
|
-
messageId: inbound.event.id,
|
|
2947
|
-
messageIdFull: inbound.event.fullId,
|
|
2948
|
-
timestamp: inbound.event.timestamp,
|
|
2949
|
-
command,
|
|
2950
|
-
access: inbound.mentions || commandAccess ? {
|
|
2951
|
-
mentions: inbound.mentions,
|
|
2952
|
-
commands: commandAccess
|
|
2953
|
-
} : void 0,
|
|
2954
|
-
extra: {
|
|
2955
|
-
Transcript: inbound.context?.transcript,
|
|
2956
|
-
...inbound.context && "groupSubject" in inbound.context ? { GroupSubject: inbound.context.groupSubject ?? void 0 } : {},
|
|
2957
|
-
GroupMembers: inbound.context?.groupMembers,
|
|
2958
|
-
SenderE164: inbound.context?.senderE164,
|
|
2959
|
-
ReplyThreading: inbound.context?.replyThreading,
|
|
2960
|
-
SuppressMessageReceivedHooks: control.messageReceivedHooks === "channel",
|
|
2961
|
-
...inbound.context?.location ? toLocationContext(inbound.context.location) : {}
|
|
2962
|
-
}
|
|
2963
|
-
})
|
|
2964
|
-
};
|
|
2965
|
-
}
|
|
2966
|
-
/** Keeps WhatsApp reply-policy mapping beside the transport that applies it. */
|
|
2967
|
-
function resolveWhatsAppInboundReplyPolicy(params) {
|
|
2968
|
-
const sourceReplyDeliveryMode = params.ctx.ChatType === "group" || params.ctx.ChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
|
|
2969
|
-
cfg: params.cfg,
|
|
2970
|
-
ctx: params.ctx
|
|
2971
|
-
}) : void 0;
|
|
2972
|
-
const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
|
|
2973
|
-
return {
|
|
2974
|
-
sourceReplyDeliveryMode,
|
|
2975
|
-
disableBlockStreaming: sourceRepliesAreToolOnly ? true : typeof params.blockStreamingEnabled === "boolean" ? !params.blockStreamingEnabled : void 0,
|
|
2976
|
-
suppressTyping: sourceRepliesAreToolOnly && params.ctx.ChatType === "group" && params.ctx.WasMentioned !== true
|
|
2977
|
-
};
|
|
2542
|
+
}) ? replyTo : null;
|
|
2978
2543
|
}
|
|
2979
2544
|
//#endregion
|
|
2980
2545
|
//#region extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.ts
|
|
@@ -2990,36 +2555,26 @@ function normalizeErrForLog(err) {
|
|
|
2990
2555
|
function whatsAppReplyDeliveryVisibility(visibleReplySent) {
|
|
2991
2556
|
return { visibleReplySent };
|
|
2992
2557
|
}
|
|
2993
|
-
function
|
|
2994
|
-
|
|
2995
|
-
return {
|
|
2996
|
-
receipt: params.delivery.receipt,
|
|
2997
|
-
...messageIds.length > 0 ? { messageIds } : {},
|
|
2998
|
-
content: params.content,
|
|
2999
|
-
visibleReplySent: params.delivery.providerAccepted
|
|
3000
|
-
};
|
|
3001
|
-
}
|
|
3002
|
-
function isWhatsAppVisibleDeliveryError(error) {
|
|
3003
|
-
return typeof error === "object" && error !== null && !Array.isArray(error) && error.visibleReplySent === true || isChannelPartialDeliveryError(error) && error.deliveryResult.visibleReplySent;
|
|
2558
|
+
function whatsAppReplyDeliveryVisibilityFromDurableResult(result) {
|
|
2559
|
+
return whatsAppReplyDeliveryVisibility(result.visibleReplySent === true);
|
|
3004
2560
|
}
|
|
3005
2561
|
function readTrimmedString(value) {
|
|
3006
2562
|
return typeof value === "string" ? value.trim() : "";
|
|
3007
2563
|
}
|
|
3008
2564
|
function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
|
|
3009
|
-
|
|
3010
|
-
if (isWhatsAppVisibleDeliveryError(error)) return error;
|
|
3011
|
-
return markWhatsAppVisibleDeliveryError(new Error("deferred WhatsApp media delivery failed after an earlier visible send", { cause: error }));
|
|
2565
|
+
return flushResult.delivered > 0 ? markWhatsAppVisibleDeliveryError(error) : error;
|
|
3012
2566
|
}
|
|
3013
2567
|
function logWhatsAppReplyDeliveryError(params) {
|
|
2568
|
+
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
3014
2569
|
params.replyLogger.error({
|
|
3015
2570
|
err: normalizeErrForLog(params.err),
|
|
3016
2571
|
replyKind: params.info.kind,
|
|
3017
|
-
correlationId: params.
|
|
2572
|
+
correlationId: params.msg.event.id ?? null,
|
|
3018
2573
|
connectionId: params.connectionId,
|
|
3019
|
-
conversationId:
|
|
3020
|
-
chatId: params.
|
|
3021
|
-
to:
|
|
3022
|
-
from: params.
|
|
2574
|
+
conversationId: admission.conversation.id,
|
|
2575
|
+
chatId: params.msg.platform.chatJid ?? null,
|
|
2576
|
+
to: admission.conversation.id,
|
|
2577
|
+
from: params.msg.platform.recipientJid ?? null
|
|
3023
2578
|
}, "auto-reply delivery failed");
|
|
3024
2579
|
}
|
|
3025
2580
|
function resolveWhatsAppDurableReplyToId(params) {
|
|
@@ -3027,10 +2582,14 @@ function resolveWhatsAppDurableReplyToId(params) {
|
|
|
3027
2582
|
const explicitPayloadReplyToId = readTrimmedString(params.payload.replyToId);
|
|
3028
2583
|
if (explicitPayloadReplyToId) return explicitPayloadReplyToId;
|
|
3029
2584
|
const hasVisibleInboundReplyTarget = Boolean(readTrimmedString(params.context.ReplyToId)) || Boolean(readTrimmedString(params.context.ReplyToIdFull));
|
|
3030
|
-
const currentInboundMessageId = readTrimmedString(params.
|
|
2585
|
+
const currentInboundMessageId = readTrimmedString(params.msg.event.id);
|
|
3031
2586
|
if (params.info.kind === "final" && hasVisibleInboundReplyTarget && currentInboundMessageId) return currentInboundMessageId;
|
|
3032
2587
|
return null;
|
|
3033
2588
|
}
|
|
2589
|
+
function resolveWhatsAppDisableBlockStreaming(cfg) {
|
|
2590
|
+
if (typeof cfg.channels?.whatsapp?.blockStreaming !== "boolean") return;
|
|
2591
|
+
return !cfg.channels.whatsapp.blockStreaming;
|
|
2592
|
+
}
|
|
3034
2593
|
function resolveWhatsAppDeliverablePayload(payload, info) {
|
|
3035
2594
|
if (payload.isReasoning === true || payload.isCompactionNotice === true) return null;
|
|
3036
2595
|
if (payload.isError === true) return null;
|
|
@@ -3055,58 +2614,31 @@ function shouldDeferWhatsAppMediaOnlyPayload(params) {
|
|
|
3055
2614
|
}
|
|
3056
2615
|
function createWhatsAppMediaOnlyReplyCoalescer(params) {
|
|
3057
2616
|
const pendingMediaOnlyPayloads = [];
|
|
3058
|
-
const
|
|
2617
|
+
const flushExceptDuplicateMedia = async (mediaUrls) => {
|
|
3059
2618
|
const flushResult = {
|
|
3060
2619
|
delivered: 0,
|
|
3061
2620
|
droppedDuplicateMedia: 0
|
|
3062
2621
|
};
|
|
3063
|
-
const
|
|
3064
|
-
const
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
candidate.rejectFinalization(error);
|
|
3075
|
-
for (const remaining of candidates.slice(index + 1)) remaining.rejectFinalization(new Error("deferred WhatsApp media delivery was not attempted", { cause: error }));
|
|
3076
|
-
throw visibleError;
|
|
2622
|
+
const pending = pendingMediaOnlyPayloads.splice(0);
|
|
2623
|
+
for (const candidate of pending) {
|
|
2624
|
+
if (mediaUrls && hasWhatsAppMediaUrlOverlap(candidate.mediaUrls, mediaUrls)) {
|
|
2625
|
+
flushResult.droppedDuplicateMedia += 1;
|
|
2626
|
+
continue;
|
|
2627
|
+
}
|
|
2628
|
+
try {
|
|
2629
|
+
if ((await params.deliver(candidate)).visibleReplySent) flushResult.delivered += 1;
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
throw markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
|
|
2632
|
+
}
|
|
3077
2633
|
}
|
|
3078
2634
|
return flushResult;
|
|
3079
2635
|
};
|
|
3080
2636
|
return {
|
|
3081
2637
|
defer(pending) {
|
|
3082
|
-
|
|
3083
|
-
let rejectFinalization;
|
|
3084
|
-
const finalization = new Promise((resolve, reject) => {
|
|
3085
|
-
resolveFinalization = resolve;
|
|
3086
|
-
rejectFinalization = reject;
|
|
3087
|
-
});
|
|
3088
|
-
pendingMediaOnlyPayloads.push({
|
|
3089
|
-
...pending,
|
|
3090
|
-
resolveFinalization,
|
|
3091
|
-
rejectFinalization
|
|
3092
|
-
});
|
|
3093
|
-
return finalization;
|
|
3094
|
-
},
|
|
3095
|
-
flushNonDuplicateMedia: (mediaUrls) => flushWhere((pending) => !hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)),
|
|
3096
|
-
dropDuplicateMedia(mediaUrls) {
|
|
3097
|
-
const flushResult = {
|
|
3098
|
-
delivered: 0,
|
|
3099
|
-
droppedDuplicateMedia: 0
|
|
3100
|
-
};
|
|
3101
|
-
const retained = [];
|
|
3102
|
-
for (const pending of pendingMediaOnlyPayloads.splice(0)) if (hasWhatsAppMediaUrlOverlap(pending.mediaUrls, mediaUrls)) {
|
|
3103
|
-
pending.resolveFinalization(whatsAppReplyDeliveryVisibility(false));
|
|
3104
|
-
flushResult.droppedDuplicateMedia += 1;
|
|
3105
|
-
} else retained.push(pending);
|
|
3106
|
-
pendingMediaOnlyPayloads.push(...retained);
|
|
3107
|
-
return flushResult;
|
|
2638
|
+
pendingMediaOnlyPayloads.push(pending);
|
|
3108
2639
|
},
|
|
3109
|
-
|
|
2640
|
+
flushExceptDuplicateMedia,
|
|
2641
|
+
flushAll: () => flushExceptDuplicateMedia()
|
|
3110
2642
|
};
|
|
3111
2643
|
}
|
|
3112
2644
|
function logWhatsAppMediaOnlyFlushResult(result) {
|
|
@@ -3116,15 +2648,9 @@ function logWhatsAppMediaOnlyFlushResult(result) {
|
|
|
3116
2648
|
}
|
|
3117
2649
|
function resolveWhatsAppResponsePrefix(params) {
|
|
3118
2650
|
const configuredResponsePrefix = params.cfg.messages?.responsePrefix;
|
|
3119
|
-
return params.pipelineResponsePrefix ?? (configuredResponsePrefix ===
|
|
3120
|
-
}
|
|
3121
|
-
function buildWhatsAppInboundTransportContext(msg) {
|
|
3122
|
-
return {
|
|
3123
|
-
...createWhatsAppReplyTransportContext(msg),
|
|
3124
|
-
sendComposing: msg.platform.sendComposing
|
|
3125
|
-
};
|
|
2651
|
+
return params.pipelineResponsePrefix ?? (configuredResponsePrefix === void 0 && params.isSelfChat ? resolveIdentityNamePrefix$1(params.cfg, params.agentId) : void 0);
|
|
3126
2652
|
}
|
|
3127
|
-
async function
|
|
2653
|
+
async function buildWhatsAppInboundContext(params) {
|
|
3128
2654
|
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
3129
2655
|
const conversationId = admission.conversation.id;
|
|
3130
2656
|
const conversationKind = admission.conversation.kind;
|
|
@@ -3134,20 +2660,18 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3134
2660
|
sender: entry.sender,
|
|
3135
2661
|
body: entry.body,
|
|
3136
2662
|
timestamp: entry.timestamp,
|
|
3137
|
-
messageId: entry.id
|
|
3138
|
-
media: entry.media
|
|
2663
|
+
messageId: entry.id
|
|
3139
2664
|
})),
|
|
3140
2665
|
limit: params.groupHistory?.length ?? 1
|
|
3141
2666
|
}) : void 0;
|
|
3142
|
-
const media =
|
|
2667
|
+
const media = toInboundMediaFacts(params.msg.payload.media?.path || params.msg.payload.media?.url ? [{
|
|
3143
2668
|
path: params.msg.payload.media?.path,
|
|
3144
2669
|
url: params.msg.payload.media?.url ?? params.msg.payload.media?.path,
|
|
3145
|
-
contentType: params.msg.payload.media?.type
|
|
3146
|
-
kind: params.msg.payload.media?.kind
|
|
2670
|
+
contentType: params.msg.payload.media?.type
|
|
3147
2671
|
}] : void 0, { transcribed: (_entry, index) => params.mediaTranscribedIndexes?.includes(index) === true });
|
|
3148
|
-
|
|
3149
|
-
const inbound = {
|
|
2672
|
+
return buildChannelInboundEventContext({
|
|
3150
2673
|
channel: "whatsapp",
|
|
2674
|
+
finalize: finalizeInboundContext,
|
|
3151
2675
|
supplemental: {
|
|
3152
2676
|
quote: params.visibleReplyTo ? {
|
|
3153
2677
|
id: params.visibleReplyTo.id,
|
|
@@ -3155,13 +2679,11 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3155
2679
|
sender: params.visibleReplyTo.sender?.label ?? void 0
|
|
3156
2680
|
} : void 0,
|
|
3157
2681
|
groupSystemPrompt: params.groupSystemPrompt,
|
|
3158
|
-
|
|
2682
|
+
untrustedContext: params.msg.payload.untrustedStructuredContext
|
|
3159
2683
|
},
|
|
3160
2684
|
media,
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
timestamp: params.msg.event.timestamp
|
|
3164
|
-
},
|
|
2685
|
+
messageId: params.msg.event.id,
|
|
2686
|
+
timestamp: params.msg.event.timestamp,
|
|
3165
2687
|
from: conversationId,
|
|
3166
2688
|
sender: {
|
|
3167
2689
|
id: params.sender.id ?? params.sender.e164,
|
|
@@ -3174,51 +2696,70 @@ async function prepareWhatsAppInboundContext(params) {
|
|
|
3174
2696
|
},
|
|
3175
2697
|
route: {
|
|
3176
2698
|
agentId: params.route.agentId,
|
|
3177
|
-
dmScope: params.route.dmScope,
|
|
3178
2699
|
accountId: params.route.accountId,
|
|
3179
2700
|
routeSessionKey: params.route.sessionKey
|
|
3180
2701
|
},
|
|
3181
2702
|
reply: {
|
|
3182
2703
|
to: params.msg.platform.recipientJid,
|
|
3183
|
-
originatingTo: conversationId
|
|
3184
|
-
replyToId: params.visibleReplyTo?.id
|
|
2704
|
+
originatingTo: conversationId
|
|
3185
2705
|
},
|
|
3186
2706
|
message: {
|
|
3187
2707
|
body: params.combinedBody,
|
|
3188
2708
|
bodyForAgent: params.bodyForAgent ?? params.msg.payload.body,
|
|
3189
2709
|
inboundHistory,
|
|
3190
2710
|
rawBody: params.rawBody ?? params.msg.payload.body,
|
|
3191
|
-
commandBody: params.
|
|
2711
|
+
commandBody: params.commandBody ?? params.msg.payload.body
|
|
3192
2712
|
},
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
2713
|
+
access: {
|
|
2714
|
+
...wasMentioned !== void 0 ? { mentions: {
|
|
2715
|
+
canDetectMention: conversationKind === "group",
|
|
2716
|
+
wasMentioned,
|
|
2717
|
+
requireMention: params.msg.groupMention?.requireMention
|
|
2718
|
+
} } : {},
|
|
2719
|
+
commands: { authorized: params.commandAuthorized }
|
|
2720
|
+
},
|
|
2721
|
+
commandTurn: params.commandTurn,
|
|
2722
|
+
extra: {
|
|
2723
|
+
Transcript: params.transcript,
|
|
2724
|
+
GroupSubject: params.msg.group?.subject,
|
|
2725
|
+
GroupMembers: formatGroupMembers({
|
|
3204
2726
|
participants: params.msg.group?.participants,
|
|
3205
2727
|
roster: params.groupMemberRoster,
|
|
3206
2728
|
fallbackE164: params.sender.e164
|
|
3207
2729
|
}),
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
2730
|
+
SenderE164: params.sender.e164,
|
|
2731
|
+
CommandSource: params.commandSource ?? (params.commandTurn?.source === "native" || params.commandTurn?.source === "text" ? params.commandTurn.source : void 0),
|
|
2732
|
+
ReplyThreading: params.replyThreading,
|
|
2733
|
+
SuppressMessageReceivedHooks: params.suppressMessageReceivedHooks,
|
|
2734
|
+
...params.msg.payload.location ? toLocationContext(params.msg.payload.location) : {}
|
|
3211
2735
|
}
|
|
3212
|
-
};
|
|
3213
|
-
const projected = projectPreparedChannelInbound({
|
|
3214
|
-
inbound,
|
|
3215
|
-
control
|
|
3216
2736
|
});
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
2737
|
+
}
|
|
2738
|
+
function normalizeCommandTurnFromContext(value) {
|
|
2739
|
+
if (!value || typeof value !== "object") return;
|
|
2740
|
+
const record = value;
|
|
2741
|
+
const kind = record.kind;
|
|
2742
|
+
const source = record.source;
|
|
2743
|
+
if (kind === "native" && source === "native" && typeof record.authorized === "boolean") return {
|
|
2744
|
+
kind: "native",
|
|
2745
|
+
source: "native",
|
|
2746
|
+
authorized: record.authorized,
|
|
2747
|
+
commandName: typeof record.commandName === "string" ? record.commandName : void 0,
|
|
2748
|
+
body: typeof record.body === "string" ? record.body : void 0
|
|
2749
|
+
};
|
|
2750
|
+
if (kind === "text-slash" && source === "text" && typeof record.authorized === "boolean") return {
|
|
2751
|
+
kind: "text-slash",
|
|
2752
|
+
source: "text",
|
|
2753
|
+
authorized: record.authorized,
|
|
2754
|
+
commandName: typeof record.commandName === "string" ? record.commandName : void 0,
|
|
2755
|
+
body: typeof record.body === "string" ? record.body : void 0
|
|
2756
|
+
};
|
|
2757
|
+
if (kind === "normal" && source === "message") return {
|
|
2758
|
+
kind: "normal",
|
|
2759
|
+
source: "message",
|
|
2760
|
+
authorized: false,
|
|
2761
|
+
commandName: typeof record.commandName === "string" ? record.commandName : void 0,
|
|
2762
|
+
body: typeof record.body === "string" ? record.body : void 0
|
|
3222
2763
|
};
|
|
3223
2764
|
}
|
|
3224
2765
|
function resolveWhatsAppDmRouteTarget(params) {
|
|
@@ -3251,9 +2792,16 @@ function updateWhatsAppMainLastRoute(params) {
|
|
|
3251
2792
|
}
|
|
3252
2793
|
if (params.dmRouteTarget && inboundLastRouteSessionKey === params.route.mainSessionKey && params.pinnedMainDmRecipient) logVerbose$1(`Skipping main-session last route update for ${params.dmRouteTarget} (pinned owner ${params.pinnedMainDmRecipient})`);
|
|
3253
2794
|
}
|
|
3254
|
-
function
|
|
3255
|
-
const
|
|
2795
|
+
async function dispatchWhatsAppBufferedReply(params) {
|
|
2796
|
+
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
2797
|
+
const conversationId = admission.conversation.id;
|
|
2798
|
+
const conversationKind = admission.conversation.kind;
|
|
3256
2799
|
const statusReactionController = params.statusReactionController ?? null;
|
|
2800
|
+
const statusReactionTiming = {
|
|
2801
|
+
...DEFAULT_TIMING,
|
|
2802
|
+
...params.cfg.messages?.statusReactions?.timing
|
|
2803
|
+
};
|
|
2804
|
+
const removeAckAfterReply = params.cfg.messages?.removeAckAfterReply ?? false;
|
|
3257
2805
|
const textLimit = params.maxMediaTextChunkLimit ?? resolveTextChunkLimit(params.cfg, "whatsapp");
|
|
3258
2806
|
const chunkMode = resolveChunkMode(params.cfg, "whatsapp", params.route.accountId);
|
|
3259
2807
|
const tableMode = resolveMarkdownTableMode$1({
|
|
@@ -3262,85 +2810,72 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3262
2810
|
accountId: params.route.accountId
|
|
3263
2811
|
});
|
|
3264
2812
|
const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
|
|
3265
|
-
const
|
|
2813
|
+
const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : conversationKind;
|
|
2814
|
+
const sourceReplyCommandSource = params.context.CommandSource === "native" || params.context.CommandSource === "text" ? params.context.CommandSource : void 0;
|
|
2815
|
+
const sourceReplyCommandTurn = normalizeCommandTurnFromContext(params.context.CommandTurn);
|
|
2816
|
+
const sourceReplyCommandAuthorized = typeof params.context.CommandAuthorized === "boolean" ? params.context.CommandAuthorized : void 0;
|
|
2817
|
+
const sourceReplyDeliveryMode = sourceReplyChatType === "group" || sourceReplyChatType === "channel" ? resolveChannelMessageSourceReplyDeliveryMode({
|
|
3266
2818
|
cfg: params.cfg,
|
|
3267
|
-
ctx:
|
|
3268
|
-
|
|
3269
|
-
|
|
2819
|
+
ctx: {
|
|
2820
|
+
ChatType: sourceReplyChatType,
|
|
2821
|
+
CommandTurn: sourceReplyCommandTurn,
|
|
2822
|
+
CommandSource: sourceReplyCommandSource,
|
|
2823
|
+
CommandAuthorized: sourceReplyCommandAuthorized
|
|
2824
|
+
}
|
|
2825
|
+
}) : void 0;
|
|
2826
|
+
const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
|
|
2827
|
+
const disableBlockStreaming = sourceRepliesAreToolOnly ? true : resolveWhatsAppDisableBlockStreaming(params.cfg);
|
|
3270
2828
|
let didSendReply = false;
|
|
3271
2829
|
let didLogHeartbeatStrip = false;
|
|
3272
|
-
const
|
|
3273
|
-
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
|
-
if (shouldLogVerbose$1()) {
|
|
3282
|
-
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3283
|
-
logVerbose$1(`Reply body: ${payload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${conversationId}`);
|
|
3284
|
-
}
|
|
3285
|
-
};
|
|
3286
|
-
const deliverNormalizedPayload = async (normalizedDeliveryPayload, info, options) => {
|
|
2830
|
+
const deliverNormalizedPayload = async (normalizedDeliveryPayload, info) => {
|
|
3287
2831
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3288
2832
|
if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
tableMode
|
|
3303
|
-
});
|
|
3304
|
-
} catch (error) {
|
|
3305
|
-
if (isWhatsAppVisibleDeliveryError(error) && !isChannelPartialDeliveryError(error)) throw createChannelPartialDeliveryError(error, {
|
|
3306
|
-
content: reply.text,
|
|
3307
|
-
visibleReplySent: true
|
|
3308
|
-
});
|
|
3309
|
-
throw error;
|
|
3310
|
-
}
|
|
3311
|
-
const result = createWhatsAppChannelDeliveryResult({
|
|
3312
|
-
content: reply.text,
|
|
3313
|
-
delivery
|
|
3314
|
-
});
|
|
3315
|
-
if (!result.visibleReplySent) {
|
|
2833
|
+
if (!(await params.deliverReply({
|
|
2834
|
+
replyResult: normalizedDeliveryPayload,
|
|
2835
|
+
normalizedReplyResult: normalizedDeliveryPayload,
|
|
2836
|
+
msg: params.msg,
|
|
2837
|
+
mediaLocalRoots,
|
|
2838
|
+
maxMediaBytes: params.maxMediaBytes,
|
|
2839
|
+
textLimit,
|
|
2840
|
+
chunkMode,
|
|
2841
|
+
replyLogger: params.replyLogger,
|
|
2842
|
+
connectionId: params.connectionId,
|
|
2843
|
+
skipLog: false,
|
|
2844
|
+
tableMode
|
|
2845
|
+
})).providerAccepted) {
|
|
3316
2846
|
params.replyLogger.warn({
|
|
3317
|
-
correlationId: params.
|
|
2847
|
+
correlationId: params.msg.event.id ?? null,
|
|
3318
2848
|
connectionId: params.connectionId,
|
|
3319
2849
|
conversationId,
|
|
3320
|
-
chatId: params.
|
|
2850
|
+
chatId: params.msg.platform.chatJid,
|
|
3321
2851
|
to: conversationId,
|
|
3322
|
-
from: params.
|
|
2852
|
+
from: params.msg.platform.recipientJid,
|
|
3323
2853
|
replyKind: info.kind
|
|
3324
2854
|
}, "auto-reply was not accepted by WhatsApp provider");
|
|
3325
|
-
return
|
|
2855
|
+
return whatsAppReplyDeliveryVisibility(false);
|
|
3326
2856
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
2857
|
+
didSendReply = true;
|
|
2858
|
+
const shouldLog = normalizedDeliveryPayload.text ? true : void 0;
|
|
2859
|
+
params.rememberSentText(normalizedDeliveryPayload.text, {
|
|
2860
|
+
combinedBody: params.context.Body,
|
|
2861
|
+
combinedBodySessionKey: params.route.sessionKey,
|
|
2862
|
+
logVerboseMessage: shouldLog
|
|
2863
|
+
});
|
|
2864
|
+
const fromDisplay = conversationId;
|
|
2865
|
+
if (shouldLogVerbose$1()) {
|
|
2866
|
+
const preview = normalizedDeliveryPayload.text != null ? reply.text : "<media>";
|
|
2867
|
+
logVerbose$1(`Reply body: ${preview}${reply.hasMedia ? " (media)" : ""} -> ${fromDisplay}`);
|
|
3334
2868
|
}
|
|
3335
|
-
return
|
|
2869
|
+
return whatsAppReplyDeliveryVisibility(true);
|
|
3336
2870
|
};
|
|
3337
2871
|
const mediaOnlyCoalescer = createWhatsAppMediaOnlyReplyCoalescer({ deliver: async (pending) => {
|
|
3338
2872
|
return await deliverNormalizedPayload(pending.payload, pending.info);
|
|
3339
2873
|
} });
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
2874
|
+
if (statusReactionController) statusReactionController.setThinking();
|
|
2875
|
+
const dispatchResult = await dispatchReplyWithBufferedBlockDispatcher({
|
|
2876
|
+
ctx: params.context,
|
|
2877
|
+
cfg: params.cfg,
|
|
2878
|
+
replyResolver: params.replyResolver,
|
|
3344
2879
|
dispatcherOptions: {
|
|
3345
2880
|
...params.replyPipeline,
|
|
3346
2881
|
onHeartbeatStrip: () => {
|
|
@@ -3349,140 +2884,168 @@ function createWhatsAppReplyPlan(params) {
|
|
|
3349
2884
|
logVerbose$1("Stripped stray HEARTBEAT_OK token from web reply");
|
|
3350
2885
|
}
|
|
3351
2886
|
},
|
|
3352
|
-
|
|
3353
|
-
const flushResult = await mediaOnlyCoalescer.flushAll();
|
|
3354
|
-
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
3355
|
-
return whatsAppReplyDeliveryVisibility(didSendReply || flushResult.delivered > 0);
|
|
3356
|
-
},
|
|
3357
|
-
onReplyStart: params.transport.sendComposing
|
|
3358
|
-
},
|
|
3359
|
-
delivery: {
|
|
3360
|
-
observeMessageSent: true,
|
|
3361
|
-
preparePayload: async (payload, info) => {
|
|
2887
|
+
deliver: async (payload, info) => {
|
|
3362
2888
|
const deliveryPayload = resolveWhatsAppDeliverablePayload(payload, info);
|
|
3363
|
-
if (!deliveryPayload) return
|
|
2889
|
+
if (!deliveryPayload) return whatsAppReplyDeliveryVisibility(false);
|
|
3364
2890
|
const normalizedOutboundPayload = normalizeWhatsAppOutboundPayload(deliveryPayload, { normalizeText: normalizeWhatsAppPayloadTextPreservingIndentation });
|
|
3365
2891
|
const normalizedDeliveryPayload = deliveryPayload.text === void 0 ? {
|
|
3366
2892
|
...normalizedOutboundPayload,
|
|
3367
2893
|
text: void 0
|
|
3368
2894
|
} : normalizedOutboundPayload;
|
|
3369
2895
|
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3370
|
-
if (!reply.hasMedia && !reply.text.trim()) return normalizedDeliveryPayload;
|
|
3371
|
-
const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
|
|
3372
|
-
logWhatsAppMediaOnlyFlushResult(reply.hasMedia ? shouldDeferWhatsAppMediaOnlyPayload({
|
|
3373
|
-
info,
|
|
3374
|
-
mediaUrls,
|
|
3375
|
-
reply
|
|
3376
|
-
}) ? {
|
|
3377
|
-
delivered: 0,
|
|
3378
|
-
droppedDuplicateMedia: 0
|
|
3379
|
-
} : await mediaOnlyCoalescer.flushNonDuplicateMedia(mediaUrls) : await mediaOnlyCoalescer.flushAll());
|
|
3380
|
-
return normalizedDeliveryPayload;
|
|
3381
|
-
},
|
|
3382
|
-
durable: (payload, info) => {
|
|
3383
|
-
const reply = resolveSendableOutboundReplyParts(payload);
|
|
3384
|
-
if (reply.hasMedia || !reply.text.trim()) return false;
|
|
3385
|
-
return {
|
|
3386
|
-
to: conversationId,
|
|
3387
|
-
replyToId: resolveWhatsAppDurableReplyToId({
|
|
3388
|
-
context: params.context,
|
|
3389
|
-
info,
|
|
3390
|
-
currentMessageId: params.transport.correlationId,
|
|
3391
|
-
payload
|
|
3392
|
-
}),
|
|
3393
|
-
formatting: {
|
|
3394
|
-
textLimit,
|
|
3395
|
-
tableMode,
|
|
3396
|
-
chunkMode
|
|
3397
|
-
}
|
|
3398
|
-
};
|
|
3399
|
-
},
|
|
3400
|
-
deliver: async (payload, info) => {
|
|
3401
|
-
const normalizedDeliveryPayload = payload;
|
|
3402
|
-
const reply = resolveSendableOutboundReplyParts(normalizedDeliveryPayload);
|
|
3403
2896
|
if (!reply.hasMedia && !reply.text.trim()) return whatsAppReplyDeliveryVisibility(false);
|
|
3404
|
-
if (!reply.hasMedia)
|
|
2897
|
+
if (!reply.hasMedia) {
|
|
2898
|
+
const flushResult = await mediaOnlyCoalescer.flushAll();
|
|
2899
|
+
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
2900
|
+
try {
|
|
2901
|
+
const durable = await deliverInboundReplyWithMessageSendContext({
|
|
2902
|
+
cfg: params.cfg,
|
|
2903
|
+
channel: "whatsapp",
|
|
2904
|
+
accountId: params.route.accountId,
|
|
2905
|
+
agentId: params.route.agentId,
|
|
2906
|
+
ctxPayload: params.context,
|
|
2907
|
+
payload: normalizedDeliveryPayload,
|
|
2908
|
+
info,
|
|
2909
|
+
to: conversationId,
|
|
2910
|
+
replyToId: resolveWhatsAppDurableReplyToId({
|
|
2911
|
+
context: params.context,
|
|
2912
|
+
info,
|
|
2913
|
+
msg: params.msg,
|
|
2914
|
+
payload: normalizedDeliveryPayload
|
|
2915
|
+
}),
|
|
2916
|
+
formatting: {
|
|
2917
|
+
textLimit,
|
|
2918
|
+
tableMode,
|
|
2919
|
+
chunkMode
|
|
2920
|
+
}
|
|
2921
|
+
});
|
|
2922
|
+
if (durable.status === "failed") {
|
|
2923
|
+
if (durable.sentBeforeError === true) throw markWhatsAppVisibleDeliveryError(durable.error);
|
|
2924
|
+
throw durable.error;
|
|
2925
|
+
}
|
|
2926
|
+
if (durable.status === "handled_visible") {
|
|
2927
|
+
didSendReply = true;
|
|
2928
|
+
const shouldLog = normalizedDeliveryPayload.text ? true : void 0;
|
|
2929
|
+
params.rememberSentText(normalizedDeliveryPayload.text, {
|
|
2930
|
+
combinedBody: params.context.Body,
|
|
2931
|
+
combinedBodySessionKey: params.route.sessionKey,
|
|
2932
|
+
logVerboseMessage: shouldLog
|
|
2933
|
+
});
|
|
2934
|
+
return whatsAppReplyDeliveryVisibilityFromDurableResult(durable.delivery);
|
|
2935
|
+
}
|
|
2936
|
+
if (durable.status === "handled_no_send") return flushResult.delivered > 0 ? whatsAppReplyDeliveryVisibility(true) : whatsAppReplyDeliveryVisibilityFromDurableResult(durable.delivery);
|
|
2937
|
+
const delivery = await deliverNormalizedPayload(normalizedDeliveryPayload, info);
|
|
2938
|
+
return flushResult.delivered > 0 && !delivery.visibleReplySent ? whatsAppReplyDeliveryVisibility(true) : delivery;
|
|
2939
|
+
} catch (error) {
|
|
2940
|
+
throw markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
3405
2943
|
const mediaUrls = getWhatsAppPayloadMediaUrls(normalizedDeliveryPayload);
|
|
3406
2944
|
if (shouldDeferWhatsAppMediaOnlyPayload({
|
|
3407
2945
|
info,
|
|
3408
2946
|
mediaUrls,
|
|
3409
2947
|
reply
|
|
3410
|
-
}))
|
|
3411
|
-
|
|
3412
|
-
finalization: mediaOnlyCoalescer.defer({
|
|
2948
|
+
})) {
|
|
2949
|
+
mediaOnlyCoalescer.defer({
|
|
3413
2950
|
info,
|
|
3414
2951
|
mediaUrls,
|
|
3415
2952
|
payload: normalizedDeliveryPayload
|
|
3416
|
-
})
|
|
3417
|
-
|
|
2953
|
+
});
|
|
2954
|
+
return whatsAppReplyDeliveryVisibility(false);
|
|
2955
|
+
}
|
|
2956
|
+
const flushResult = await mediaOnlyCoalescer.flushExceptDuplicateMedia(mediaUrls);
|
|
2957
|
+
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
3418
2958
|
try {
|
|
3419
|
-
const
|
|
3420
|
-
|
|
3421
|
-
return result;
|
|
2959
|
+
const delivery = await deliverNormalizedPayload(normalizedDeliveryPayload, info);
|
|
2960
|
+
return flushResult.delivered > 0 && !delivery.visibleReplySent ? whatsAppReplyDeliveryVisibility(true) : delivery;
|
|
3422
2961
|
} catch (error) {
|
|
3423
|
-
|
|
3424
|
-
throw error;
|
|
2962
|
+
throw markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult);
|
|
3425
2963
|
}
|
|
3426
2964
|
},
|
|
3427
|
-
|
|
3428
|
-
|
|
2965
|
+
onSettled: async () => {
|
|
2966
|
+
const flushResult = await mediaOnlyCoalescer.flushAll();
|
|
2967
|
+
logWhatsAppMediaOnlyFlushResult(flushResult);
|
|
2968
|
+
return whatsAppReplyDeliveryVisibility(flushResult.delivered > 0);
|
|
3429
2969
|
},
|
|
2970
|
+
onReplyStart: params.msg.platform.sendComposing,
|
|
2971
|
+
...statusReactionController ? {
|
|
2972
|
+
onCompactionStart: async () => {
|
|
2973
|
+
await statusReactionController.setCompacting();
|
|
2974
|
+
},
|
|
2975
|
+
onCompactionEnd: async () => {
|
|
2976
|
+
statusReactionController.cancelPending();
|
|
2977
|
+
await statusReactionController.setThinking();
|
|
2978
|
+
}
|
|
2979
|
+
} : {},
|
|
3430
2980
|
onError: (err, info) => {
|
|
3431
|
-
if (didSendReply) markWhatsAppVisibleDeliveryError(err);
|
|
3432
2981
|
logWhatsAppReplyDeliveryError({
|
|
3433
2982
|
err,
|
|
3434
2983
|
info,
|
|
3435
2984
|
connectionId: params.connectionId,
|
|
3436
|
-
|
|
2985
|
+
msg: params.msg,
|
|
3437
2986
|
replyLogger: params.replyLogger
|
|
3438
2987
|
});
|
|
3439
2988
|
}
|
|
3440
2989
|
},
|
|
3441
2990
|
replyOptions: {
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
...replyPolicy.sourceReplyDeliveryMode ? { sourceReplyDeliveryMode: replyPolicy.sourceReplyDeliveryMode } : {},
|
|
2991
|
+
suppressTyping: sourceRepliesAreToolOnly && conversationKind === "group" && !(params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned),
|
|
2992
|
+
disableBlockStreaming,
|
|
2993
|
+
...sourceReplyDeliveryMode ? { sourceReplyDeliveryMode } : {},
|
|
3446
2994
|
onModelSelected: params.onModelSelected,
|
|
3447
|
-
...statusReactionController ? {
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
},
|
|
3452
|
-
onCompactionStart: async () => {
|
|
3453
|
-
await statusReactionController.setCompacting();
|
|
3454
|
-
},
|
|
3455
|
-
onCompactionEnd: async () => {
|
|
3456
|
-
statusReactionController.cancelPending();
|
|
3457
|
-
await statusReactionController.setThinking();
|
|
3458
|
-
}
|
|
3459
|
-
} : {}
|
|
3460
|
-
},
|
|
3461
|
-
replyResolver: params.replyResolver,
|
|
3462
|
-
finalize: (dispatchResult) => {
|
|
3463
|
-
const didQueueVisibleReply = hasVisibleInboundReplyDispatch(dispatchResult);
|
|
3464
|
-
const didDeliverVisibleReply = didSendReply || dispatchResult.observedReplyDelivery === true;
|
|
3465
|
-
if (!didQueueVisibleReply && !didDeliverVisibleReply) {
|
|
3466
|
-
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3467
|
-
controller: statusReactionController,
|
|
3468
|
-
outcome: "error"
|
|
3469
|
-
});
|
|
3470
|
-
if (params.shouldClearGroupHistory) params.groupHistories.set(params.groupHistoryKey, []);
|
|
3471
|
-
logVerbose$1("Skipping auto-reply: silent token or no text/media returned from resolver");
|
|
3472
|
-
return false;
|
|
3473
|
-
}
|
|
3474
|
-
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3475
|
-
controller: statusReactionController,
|
|
3476
|
-
outcome: didDeliverVisibleReply ? "done" : "error"
|
|
3477
|
-
});
|
|
3478
|
-
if (params.shouldClearGroupHistory) params.groupHistories.set(params.groupHistoryKey, []);
|
|
3479
|
-
return didDeliverVisibleReply;
|
|
2995
|
+
...statusReactionController ? { onToolStart: async (payload) => {
|
|
2996
|
+
const toolName = payload.name?.trim();
|
|
2997
|
+
if (toolName) await statusReactionController.setTool(toolName);
|
|
2998
|
+
} } : {}
|
|
3480
2999
|
}
|
|
3481
|
-
};
|
|
3000
|
+
});
|
|
3001
|
+
const didQueueVisibleReply = hasVisibleInboundReplyDispatch(dispatchResult);
|
|
3002
|
+
const didDeliverVisibleReply = didSendReply || dispatchResult.observedReplyDelivery === true;
|
|
3003
|
+
if (!didQueueVisibleReply) {
|
|
3004
|
+
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3005
|
+
controller: statusReactionController,
|
|
3006
|
+
outcome: "error",
|
|
3007
|
+
hasFinalResponse: false,
|
|
3008
|
+
removeAckAfterReply,
|
|
3009
|
+
timing: statusReactionTiming
|
|
3010
|
+
});
|
|
3011
|
+
if (params.shouldClearGroupHistory) params.groupHistories.set(params.groupHistoryKey, []);
|
|
3012
|
+
logVerbose$1("Skipping auto-reply: silent token or no text/media returned from resolver");
|
|
3013
|
+
return false;
|
|
3014
|
+
}
|
|
3015
|
+
if (statusReactionController) finalizeWhatsAppStatusReaction({
|
|
3016
|
+
controller: statusReactionController,
|
|
3017
|
+
outcome: didDeliverVisibleReply ? "done" : "error",
|
|
3018
|
+
hasFinalResponse: didDeliverVisibleReply,
|
|
3019
|
+
removeAckAfterReply,
|
|
3020
|
+
timing: statusReactionTiming
|
|
3021
|
+
});
|
|
3022
|
+
if (params.shouldClearGroupHistory) params.groupHistories.set(params.groupHistoryKey, []);
|
|
3023
|
+
return didDeliverVisibleReply;
|
|
3482
3024
|
}
|
|
3483
3025
|
async function finalizeWhatsAppStatusReaction(params) {
|
|
3484
|
-
if (params.outcome === "done")
|
|
3485
|
-
|
|
3026
|
+
if (params.outcome === "done") {
|
|
3027
|
+
await params.controller.setDone();
|
|
3028
|
+
if (params.removeAckAfterReply) {
|
|
3029
|
+
await new Promise((resolve) => {
|
|
3030
|
+
setTimeout(resolve, params.timing.doneHoldMs);
|
|
3031
|
+
});
|
|
3032
|
+
await params.controller.clear();
|
|
3033
|
+
} else await params.controller.restoreInitial();
|
|
3034
|
+
return;
|
|
3035
|
+
}
|
|
3036
|
+
await params.controller.setError();
|
|
3037
|
+
if (params.hasFinalResponse) {
|
|
3038
|
+
if (params.removeAckAfterReply) {
|
|
3039
|
+
await new Promise((resolve) => {
|
|
3040
|
+
setTimeout(resolve, params.timing.errorHoldMs);
|
|
3041
|
+
});
|
|
3042
|
+
await params.controller.clear();
|
|
3043
|
+
} else await params.controller.restoreInitial();
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
if (params.removeAckAfterReply) await new Promise((resolve) => {
|
|
3047
|
+
setTimeout(resolve, params.timing.errorHoldMs);
|
|
3048
|
+
});
|
|
3486
3049
|
await params.controller.restoreInitial();
|
|
3487
3050
|
}
|
|
3488
3051
|
//#endregion
|
|
@@ -3496,7 +3059,7 @@ function resolveIdentityNamePrefix(cfg, agentId) {
|
|
|
3496
3059
|
return identityName ? `[${identityName}]` : void 0;
|
|
3497
3060
|
}
|
|
3498
3061
|
function resolveMessagePrefix(cfg, agentId, opts) {
|
|
3499
|
-
const configured = opts?.configured;
|
|
3062
|
+
const configured = opts?.configured ?? cfg.messages?.messagePrefix;
|
|
3500
3063
|
if (configured !== void 0) return configured;
|
|
3501
3064
|
if (opts?.hasAllowFrom === true) return "";
|
|
3502
3065
|
return resolveIdentityNamePrefix(cfg, agentId) ?? opts?.fallback ?? "[openclaw]";
|
|
@@ -3513,7 +3076,7 @@ function formatReplyContext(msg) {
|
|
|
3513
3076
|
function buildInboundLine(params) {
|
|
3514
3077
|
const { cfg, msg, agentId, previousTimestamp, envelope } = params;
|
|
3515
3078
|
const messagePrefix = resolveMessagePrefix(cfg, agentId, {
|
|
3516
|
-
configured: cfg.channels?.whatsapp?.
|
|
3079
|
+
configured: cfg.channels?.whatsapp?.messagePrefix,
|
|
3517
3080
|
hasAllowFrom: (cfg.channels?.whatsapp?.allowFrom?.length ?? 0) > 0
|
|
3518
3081
|
});
|
|
3519
3082
|
const admission = requireWhatsAppInboundAdmission(msg);
|
|
@@ -3543,30 +3106,66 @@ function buildInboundLine(params) {
|
|
|
3543
3106
|
//#region extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts
|
|
3544
3107
|
async function createWhatsAppStatusReactionController(params) {
|
|
3545
3108
|
if (!params.msg.event.id) return null;
|
|
3546
|
-
|
|
3547
|
-
|
|
3109
|
+
const statusReactionsConfig = params.cfg.messages?.statusReactions;
|
|
3110
|
+
if (statusReactionsConfig?.enabled !== true) return null;
|
|
3111
|
+
const admission = requireWhatsAppInboundAdmission(params.msg);
|
|
3112
|
+
const accountId = admission.accountId;
|
|
3113
|
+
if (resolveWhatsAppReactionLevel({
|
|
3114
|
+
cfg: params.cfg,
|
|
3115
|
+
accountId
|
|
3116
|
+
}).level === "off") return null;
|
|
3117
|
+
const ackConfig = params.cfg.channels?.whatsapp?.ackReaction;
|
|
3118
|
+
const ackEmoji = resolveWhatsAppAckEmoji({
|
|
3548
3119
|
cfg: params.cfg,
|
|
3549
|
-
msg: params.msg,
|
|
3550
3120
|
agentId: params.agentId,
|
|
3551
|
-
|
|
3552
|
-
verbose: params.verbose
|
|
3121
|
+
ackConfig
|
|
3553
3122
|
});
|
|
3554
|
-
if (
|
|
3555
|
-
const
|
|
3123
|
+
if (!ackEmoji) return null;
|
|
3124
|
+
const directEnabled = ackConfig?.direct ?? true;
|
|
3125
|
+
const groupMode = ackConfig?.group ?? "mentions";
|
|
3126
|
+
const isGroup = admission.conversation.kind === "group";
|
|
3127
|
+
const conversationIdForCheck = admission.conversation.id;
|
|
3128
|
+
const activation = isGroup ? await resolveGroupActivationFor({
|
|
3129
|
+
cfg: params.cfg,
|
|
3130
|
+
accountId,
|
|
3131
|
+
agentId: params.agentId,
|
|
3132
|
+
sessionKey: params.sessionKey,
|
|
3133
|
+
conversationId: conversationIdForCheck
|
|
3134
|
+
}) : null;
|
|
3135
|
+
if (!shouldAckReactionForWhatsApp({
|
|
3136
|
+
emoji: ackEmoji,
|
|
3137
|
+
isDirect: admission.conversation.kind === "direct",
|
|
3138
|
+
isGroup,
|
|
3139
|
+
directEnabled,
|
|
3140
|
+
groupMode,
|
|
3141
|
+
wasMentioned: (params.msg.groupMention?.wasMentioned ?? params.msg.wasMentioned) === true,
|
|
3142
|
+
groupActivated: activation === "always"
|
|
3143
|
+
})) return null;
|
|
3144
|
+
const sender = getSenderIdentity(params.msg);
|
|
3145
|
+
const reactionOptions = {
|
|
3146
|
+
verbose: params.verbose,
|
|
3147
|
+
fromMe: false,
|
|
3148
|
+
...sender.jid ? { participant: sender.jid } : {},
|
|
3149
|
+
accountId,
|
|
3150
|
+
cfg: params.cfg
|
|
3151
|
+
};
|
|
3152
|
+
const chatId = params.msg.platform.chatJid;
|
|
3153
|
+
const msgId = params.msg.event.id;
|
|
3556
3154
|
return createStatusReactionController({
|
|
3557
3155
|
enabled: true,
|
|
3558
3156
|
adapter: {
|
|
3559
3157
|
setReaction: async (emoji) => {
|
|
3560
|
-
await sendReactionWhatsApp(chatId,
|
|
3158
|
+
await sendReactionWhatsApp(chatId, msgId, emoji, reactionOptions);
|
|
3561
3159
|
},
|
|
3562
3160
|
clearReaction: async () => {
|
|
3563
|
-
await sendReactionWhatsApp(chatId,
|
|
3161
|
+
await sendReactionWhatsApp(chatId, msgId, "", reactionOptions);
|
|
3564
3162
|
}
|
|
3565
3163
|
},
|
|
3566
|
-
initialEmoji,
|
|
3567
|
-
emojis:
|
|
3164
|
+
initialEmoji: ackEmoji,
|
|
3165
|
+
emojis: statusReactionsConfig.emojis,
|
|
3166
|
+
timing: statusReactionsConfig.timing,
|
|
3568
3167
|
onError: (err) => {
|
|
3569
|
-
logVerbose(`WhatsApp status-reaction error for chat ${chatId}/${
|
|
3168
|
+
logVerbose(`WhatsApp status-reaction error for chat ${chatId}/${msgId}: ${String(err)}`);
|
|
3570
3169
|
}
|
|
3571
3170
|
});
|
|
3572
3171
|
}
|
|
@@ -3633,16 +3232,13 @@ async function processMessage(params) {
|
|
|
3633
3232
|
sessionKey: params.route.sessionKey
|
|
3634
3233
|
});
|
|
3635
3234
|
let audioTranscript = params.preflightAudioTranscript ?? void 0;
|
|
3636
|
-
const hasAudioBody =
|
|
3235
|
+
const hasAudioBody = params.msg.payload.media?.type?.startsWith("audio/") === true && params.msg.payload.body === "<media:audio>";
|
|
3637
3236
|
if (params.preflightAudioTranscript === void 0 && hasAudioBody && params.msg.payload.media?.path) try {
|
|
3638
|
-
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.
|
|
3237
|
+
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.mjs");
|
|
3639
3238
|
audioTranscript = await transcribeFirstAudio({
|
|
3640
3239
|
ctx: {
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
contentType: params.msg.payload.media.type,
|
|
3644
|
-
kind: params.msg.payload.media.kind ?? void 0
|
|
3645
|
-
}],
|
|
3240
|
+
MediaPaths: [params.msg.payload.media?.path],
|
|
3241
|
+
MediaTypes: params.msg.payload.media?.type ? [params.msg.payload.media?.type] : void 0,
|
|
3646
3242
|
From: conversationId,
|
|
3647
3243
|
To: params.msg.platform.recipientJid,
|
|
3648
3244
|
Provider: "whatsapp",
|
|
@@ -3654,7 +3250,7 @@ async function processMessage(params) {
|
|
|
3654
3250
|
cfg: params.cfg
|
|
3655
3251
|
});
|
|
3656
3252
|
} catch {
|
|
3657
|
-
if (shouldLogVerbose$1()) logVerbose$1("whatsapp: audio preflight transcription failed,
|
|
3253
|
+
if (shouldLogVerbose$1()) logVerbose$1("whatsapp: audio preflight transcription failed, using placeholder");
|
|
3658
3254
|
}
|
|
3659
3255
|
const msgForAgent = audioTranscript !== void 0 ? {
|
|
3660
3256
|
...params.msg,
|
|
@@ -3688,27 +3284,29 @@ async function processMessage(params) {
|
|
|
3688
3284
|
}) : void 0;
|
|
3689
3285
|
if (conversationKind === "group") {
|
|
3690
3286
|
const history = visibleGroupHistory ?? [];
|
|
3691
|
-
if (history.length > 0)
|
|
3692
|
-
|
|
3287
|
+
if (history.length > 0) {
|
|
3288
|
+
const historyEntries = history.map((m) => ({
|
|
3693
3289
|
sender: m.sender,
|
|
3694
3290
|
body: m.body,
|
|
3695
|
-
timestamp: m.timestamp
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3291
|
+
timestamp: m.timestamp
|
|
3292
|
+
}));
|
|
3293
|
+
combinedBody = buildHistoryContextFromEntries({
|
|
3294
|
+
entries: historyEntries,
|
|
3295
|
+
currentMessage: combinedBody,
|
|
3296
|
+
excludeLast: false,
|
|
3297
|
+
formatEntry: (entry) => {
|
|
3298
|
+
return formatInboundEnvelope$1({
|
|
3299
|
+
channel: "WhatsApp",
|
|
3300
|
+
from: conversationId,
|
|
3301
|
+
timestamp: entry.timestamp,
|
|
3302
|
+
body: entry.body,
|
|
3303
|
+
chatType: "group",
|
|
3304
|
+
senderLabel: entry.sender,
|
|
3305
|
+
envelope: envelopeOptions
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3308
|
+
});
|
|
3309
|
+
}
|
|
3712
3310
|
shouldClearGroupHistory = !(params.suppressGroupHistoryClear ?? false);
|
|
3713
3311
|
}
|
|
3714
3312
|
const combinedEchoKey = params.buildCombinedEchoKey({
|
|
@@ -3760,13 +3358,24 @@ async function processMessage(params) {
|
|
|
3760
3358
|
normalizeE164
|
|
3761
3359
|
});
|
|
3762
3360
|
const shouldCheckCommandAuth = shouldComputeCommandAuthorized(commandBody, params.cfg);
|
|
3763
|
-
const isTextCommand = isControlCommandMessage(commandBody, params.cfg);
|
|
3361
|
+
const isTextCommand = isControlCommandMessage$1(commandBody, params.cfg);
|
|
3764
3362
|
const commandAuthorized = shouldCheckCommandAuth ? await resolveWhatsAppCommandAuthorized({
|
|
3765
3363
|
cfg: params.cfg,
|
|
3766
3364
|
msg: params.msg,
|
|
3767
3365
|
policy: inboundPolicy,
|
|
3768
3366
|
authDir: account.authDir
|
|
3769
3367
|
}) : void 0;
|
|
3368
|
+
const commandTurn = isTextCommand ? {
|
|
3369
|
+
kind: "text-slash",
|
|
3370
|
+
source: "text",
|
|
3371
|
+
authorized: Boolean(commandAuthorized),
|
|
3372
|
+
body: commandBody
|
|
3373
|
+
} : {
|
|
3374
|
+
kind: "normal",
|
|
3375
|
+
source: "message",
|
|
3376
|
+
authorized: false,
|
|
3377
|
+
body: commandBody
|
|
3378
|
+
};
|
|
3770
3379
|
const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
3771
3380
|
cfg: params.cfg,
|
|
3772
3381
|
agentId: params.route.agentId,
|
|
@@ -3787,17 +3396,13 @@ async function processMessage(params) {
|
|
|
3787
3396
|
accountConfig: account,
|
|
3788
3397
|
peerId: dmRouteTarget ?? conversationId
|
|
3789
3398
|
});
|
|
3790
|
-
const
|
|
3791
|
-
const { inbound, turnInput, ctxPayload } = await prepareWhatsAppInboundContext({
|
|
3399
|
+
const ctxPayload = await buildWhatsAppInboundContext({
|
|
3792
3400
|
bodyForAgent: msgForAgent.payload.body,
|
|
3793
3401
|
combinedBody,
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
authorization: commandAuthorization
|
|
3798
|
-
},
|
|
3402
|
+
commandBody,
|
|
3403
|
+
commandAuthorized,
|
|
3404
|
+
commandTurn,
|
|
3799
3405
|
groupHistory: visibleGroupHistory,
|
|
3800
|
-
groupHistoryLimit: params.groupHistoryLimit,
|
|
3801
3406
|
groupMemberRoster: params.groupMemberNames.get(params.groupHistoryKey),
|
|
3802
3407
|
groupSystemPrompt: conversationSystemPrompt,
|
|
3803
3408
|
msg: params.msg,
|
|
@@ -3814,9 +3419,6 @@ async function processMessage(params) {
|
|
|
3814
3419
|
visibleReplyTo: visibleReplyTo ?? void 0,
|
|
3815
3420
|
suppressMessageReceivedHooks: true
|
|
3816
3421
|
});
|
|
3817
|
-
const transport = buildWhatsAppInboundTransportContext(params.msg);
|
|
3818
|
-
const ingressLifecycle = resolveWhatsAppIngressLifecycle(params.msg);
|
|
3819
|
-
const turnAdoptionLifecycle = ingressLifecycle ? bindIngressLifecycleToReplyOptions(ingressLifecycle).turnAdoptionLifecycle : void 0;
|
|
3820
3422
|
emitWhatsAppMessageReceivedHooksIfEnabled({
|
|
3821
3423
|
cfg: params.cfg,
|
|
3822
3424
|
ctx: ctxPayload,
|
|
@@ -3837,14 +3439,19 @@ async function processMessage(params) {
|
|
|
3837
3439
|
updateLastRoute: updateLastRouteInBackground,
|
|
3838
3440
|
warn: params.replyLogger.warn.bind(params.replyLogger)
|
|
3839
3441
|
});
|
|
3840
|
-
let finalizeReply;
|
|
3841
3442
|
const turnResult = await runChannelInboundEvent({
|
|
3842
3443
|
channel: "whatsapp",
|
|
3843
3444
|
accountId: params.route.accountId,
|
|
3844
|
-
raw:
|
|
3845
|
-
...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
|
|
3445
|
+
raw: params.msg,
|
|
3846
3446
|
adapter: {
|
|
3847
|
-
ingest: () =>
|
|
3447
|
+
ingest: () => ({
|
|
3448
|
+
id: params.msg.event.id ?? `${conversationId}:${Date.now()}`,
|
|
3449
|
+
timestamp: params.msg.event.timestamp,
|
|
3450
|
+
rawText: ctxPayload.RawBody ?? "",
|
|
3451
|
+
textForAgent: ctxPayload.BodyForAgent,
|
|
3452
|
+
textForCommands: ctxPayload.CommandBody,
|
|
3453
|
+
raw: params.msg
|
|
3454
|
+
}),
|
|
3848
3455
|
preflight: () => {
|
|
3849
3456
|
const reason = admission.ingress.reasonCode;
|
|
3850
3457
|
if (admission.ingress.admission === "dispatch") return { admission: {
|
|
@@ -3865,8 +3472,26 @@ async function processMessage(params) {
|
|
|
3865
3472
|
recordHistory: false
|
|
3866
3473
|
} };
|
|
3867
3474
|
},
|
|
3868
|
-
resolveTurn: () => {
|
|
3869
|
-
|
|
3475
|
+
resolveTurn: () => ({
|
|
3476
|
+
channel: "whatsapp",
|
|
3477
|
+
accountId: params.route.accountId,
|
|
3478
|
+
routeSessionKey: params.route.sessionKey,
|
|
3479
|
+
storePath,
|
|
3480
|
+
ctxPayload,
|
|
3481
|
+
recordInboundSession,
|
|
3482
|
+
record: {
|
|
3483
|
+
onRecordError: (err) => {
|
|
3484
|
+
params.replyLogger.warn({
|
|
3485
|
+
error: formatError(err),
|
|
3486
|
+
storePath,
|
|
3487
|
+
sessionKey: params.route.sessionKey
|
|
3488
|
+
}, "failed updating session meta");
|
|
3489
|
+
},
|
|
3490
|
+
trackSessionMetaTask: (task) => {
|
|
3491
|
+
trackBackgroundTask(params.backgroundTasks, task);
|
|
3492
|
+
}
|
|
3493
|
+
},
|
|
3494
|
+
runDispatch: () => dispatchWhatsAppBufferedReply({
|
|
3870
3495
|
cfg: params.cfg,
|
|
3871
3496
|
connectionId: params.connectionId,
|
|
3872
3497
|
context: ctxPayload,
|
|
@@ -3875,7 +3500,7 @@ async function processMessage(params) {
|
|
|
3875
3500
|
groupHistoryKey: params.groupHistoryKey,
|
|
3876
3501
|
maxMediaBytes: params.maxMediaBytes,
|
|
3877
3502
|
maxMediaTextChunkLimit: params.maxMediaTextChunkLimit,
|
|
3878
|
-
|
|
3503
|
+
msg: params.msg,
|
|
3879
3504
|
onModelSelected,
|
|
3880
3505
|
rememberSentText: params.rememberSentText,
|
|
3881
3506
|
replyLogger: params.replyLogger,
|
|
@@ -3886,40 +3511,14 @@ async function processMessage(params) {
|
|
|
3886
3511
|
replyResolver: params.replyResolver,
|
|
3887
3512
|
route: params.route,
|
|
3888
3513
|
shouldClearGroupHistory,
|
|
3889
|
-
statusReactionController
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
});
|
|
3893
|
-
finalizeReply = finalize;
|
|
3894
|
-
return {
|
|
3895
|
-
cfg: params.cfg,
|
|
3896
|
-
channel: "whatsapp",
|
|
3897
|
-
accountId: params.route.accountId,
|
|
3898
|
-
route: {
|
|
3899
|
-
agentId: params.route.agentId,
|
|
3900
|
-
sessionKey: params.route.sessionKey
|
|
3901
|
-
},
|
|
3902
|
-
ctxPayload,
|
|
3903
|
-
record: {
|
|
3904
|
-
onRecordError: (err) => {
|
|
3905
|
-
params.replyLogger.warn({
|
|
3906
|
-
error: formatError(err),
|
|
3907
|
-
storePath,
|
|
3908
|
-
sessionKey: params.route.sessionKey
|
|
3909
|
-
}, "failed updating session meta");
|
|
3910
|
-
},
|
|
3911
|
-
trackSessionMetaTask: (task) => {
|
|
3912
|
-
trackBackgroundTask(params.backgroundTasks, task);
|
|
3913
|
-
}
|
|
3914
|
-
},
|
|
3915
|
-
...replyPlan
|
|
3916
|
-
};
|
|
3917
|
-
}
|
|
3514
|
+
statusReactionController
|
|
3515
|
+
})
|
|
3516
|
+
})
|
|
3918
3517
|
}
|
|
3919
3518
|
});
|
|
3920
|
-
const didSendReply = turnResult.dispatched ?
|
|
3519
|
+
const didSendReply = turnResult.dispatched ? turnResult.dispatchResult : false;
|
|
3921
3520
|
removeAckReactionHandleAfterReply({
|
|
3922
|
-
removeAfterReply:
|
|
3521
|
+
removeAfterReply: Boolean(params.cfg.messages?.removeAckAfterReply && didSendReply),
|
|
3923
3522
|
ackReaction,
|
|
3924
3523
|
onError: (err) => {
|
|
3925
3524
|
logAckFailure({
|
|
@@ -3934,14 +3533,8 @@ async function processMessage(params) {
|
|
|
3934
3533
|
}
|
|
3935
3534
|
//#endregion
|
|
3936
3535
|
//#region extensions/whatsapp/src/auto-reply/monitor/on-message.ts
|
|
3937
|
-
function readDeprecatedAccessControlPassed(msg) {
|
|
3938
|
-
return msg.accessControlPassed;
|
|
3939
|
-
}
|
|
3940
3536
|
function createWebOnMessageHandler(params) {
|
|
3941
|
-
const hasExplicitlyPassedInboundAccess = (msg) =>
|
|
3942
|
-
if (msg.admission.ingress.decisiveGateId === "legacy-flat-compat") return readDeprecatedAccessControlPassed(msg) === true;
|
|
3943
|
-
return msg.admission.ingress.decision === "allow";
|
|
3944
|
-
};
|
|
3537
|
+
const hasExplicitlyPassedInboundAccess = (msg) => msg.admission ? msg.admission.ingress.decision === "allow" : msg.accessControlPassed === true;
|
|
3945
3538
|
const withDirectSenderPeer = (msg, peerId) => {
|
|
3946
3539
|
if (requireWhatsAppInboundAdmission(msg).conversation.kind === "group" || msg.platform.sender?.e164 || msg.platform.senderE164 || !peerId.startsWith("+")) return msg;
|
|
3947
3540
|
const normalized = normalizeE164(peerId);
|
|
@@ -3965,7 +3558,6 @@ function createWebOnMessageHandler(params) {
|
|
|
3965
3558
|
route,
|
|
3966
3559
|
groupHistoryKey,
|
|
3967
3560
|
groupHistories: params.groupHistories,
|
|
3968
|
-
groupHistoryLimit: params.groupHistoryLimit,
|
|
3969
3561
|
groupMemberNames: params.groupMemberNames,
|
|
3970
3562
|
connectionId: params.connectionId,
|
|
3971
3563
|
verbose: params.verbose,
|
|
@@ -3986,8 +3578,9 @@ function createWebOnMessageHandler(params) {
|
|
|
3986
3578
|
if (opts?.statusReactionController !== void 0) processParams.statusReactionController = opts.statusReactionController;
|
|
3987
3579
|
return processMessage(processParams);
|
|
3988
3580
|
};
|
|
3989
|
-
return async (
|
|
3990
|
-
const canRunDirectEarlyAudioPreflight = hasExplicitlyPassedInboundAccess(
|
|
3581
|
+
return async (rawMsg) => {
|
|
3582
|
+
const canRunDirectEarlyAudioPreflight = hasExplicitlyPassedInboundAccess(rawMsg);
|
|
3583
|
+
const normalizedMsg = requireAdmittedWhatsAppInboundMessage(normalizeWebInboundMessage(rawMsg));
|
|
3991
3584
|
const cfg = params.loadConfig?.() ?? params.cfg;
|
|
3992
3585
|
const peerId = resolvePeerId(normalizedMsg);
|
|
3993
3586
|
const msg = withDirectSenderPeer(normalizedMsg, peerId);
|
|
@@ -4012,9 +3605,9 @@ function createWebOnMessageHandler(params) {
|
|
|
4012
3605
|
});
|
|
4013
3606
|
const baseMentionConfig = buildMentionConfig(cfg);
|
|
4014
3607
|
if (conversationId === msg.platform.recipientJid) logVerbose(`📱 Same-phone mode detected (from === to: ${conversationId})`);
|
|
4015
|
-
if (params.echoTracker.has(msg.payload.body
|
|
3608
|
+
if (params.echoTracker.has(msg.payload.body)) {
|
|
4016
3609
|
logVerbose("Skipping auto-reply: detected echo (message matches recently sent text)");
|
|
4017
|
-
params.echoTracker.forget(msg.payload.body
|
|
3610
|
+
params.echoTracker.forget(msg.payload.body);
|
|
4018
3611
|
return;
|
|
4019
3612
|
}
|
|
4020
3613
|
const configuredRoute = resolveConfiguredBindingRoute({
|
|
@@ -4032,7 +3625,7 @@ function createWebOnMessageHandler(params) {
|
|
|
4032
3625
|
peerId
|
|
4033
3626
|
}) : route.sessionKey;
|
|
4034
3627
|
let preflightAudioTranscript;
|
|
4035
|
-
const hasAudioBody =
|
|
3628
|
+
const hasAudioBody = msg.payload.media?.type?.startsWith("audio/") === true && msg.payload.body === "<media:audio>";
|
|
4036
3629
|
const canRunEarlyAudioPreflight = conversationKind === "group" || canRunDirectEarlyAudioPreflight;
|
|
4037
3630
|
let ackAlreadySent = false;
|
|
4038
3631
|
let ackReaction = null;
|
|
@@ -4055,14 +3648,11 @@ function createWebOnMessageHandler(params) {
|
|
|
4055
3648
|
const transcribeAudioOnce = async () => {
|
|
4056
3649
|
if (preflightAudioTranscript !== void 0 || !hasAudioBody || !msg.payload.media?.path) return;
|
|
4057
3650
|
try {
|
|
4058
|
-
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.
|
|
3651
|
+
const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.mjs");
|
|
4059
3652
|
preflightAudioTranscript = await transcribeFirstAudio({
|
|
4060
3653
|
ctx: {
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
contentType: msg.payload.media.type,
|
|
4064
|
-
kind: msg.payload.media.kind ?? void 0
|
|
4065
|
-
}],
|
|
3654
|
+
MediaPaths: [msg.payload.media?.path],
|
|
3655
|
+
MediaTypes: msg.payload.media?.type ? [msg.payload.media?.type] : void 0,
|
|
4066
3656
|
From: conversationId,
|
|
4067
3657
|
To: msg.platform.recipientJid,
|
|
4068
3658
|
Provider: "whatsapp",
|
|
@@ -4220,7 +3810,7 @@ function createWebOnMessageHandler(params) {
|
|
|
4220
3810
|
function isNonRetryableWebCloseStatus(statusCode) {
|
|
4221
3811
|
return statusCode === 440;
|
|
4222
3812
|
}
|
|
4223
|
-
const loadReplyResolverRuntime = createLazyRuntimeModule(() => import("./reply-resolver.runtime-Ded8D58Y.
|
|
3813
|
+
const loadReplyResolverRuntime = createLazyRuntimeModule(() => import("./reply-resolver.runtime-Ded8D58Y.mjs"));
|
|
4224
3814
|
function resolveWebMonitorConfigSnapshot(params) {
|
|
4225
3815
|
const account = resolveWhatsAppAccount({
|
|
4226
3816
|
cfg: params.cfg,
|
|
@@ -4233,13 +3823,15 @@ function resolveWebMonitorConfigSnapshot(params) {
|
|
|
4233
3823
|
...params.cfg.channels,
|
|
4234
3824
|
whatsapp: {
|
|
4235
3825
|
...params.cfg.channels?.whatsapp,
|
|
4236
|
-
|
|
3826
|
+
ackReaction: account.ackReaction,
|
|
3827
|
+
messagePrefix: account.messagePrefix,
|
|
4237
3828
|
allowFrom: account.allowFrom,
|
|
4238
3829
|
groupAllowFrom: account.groupAllowFrom,
|
|
4239
3830
|
groupPolicy: account.groupPolicy,
|
|
4240
3831
|
textChunkLimit: account.textChunkLimit,
|
|
4241
|
-
|
|
3832
|
+
chunkMode: account.chunkMode,
|
|
4242
3833
|
mediaMaxMb: account.mediaMaxMb,
|
|
3834
|
+
blockStreaming: account.blockStreaming,
|
|
4243
3835
|
groups: account.groups
|
|
4244
3836
|
}
|
|
4245
3837
|
}
|
|
@@ -4247,17 +3839,28 @@ function resolveWebMonitorConfigSnapshot(params) {
|
|
|
4247
3839
|
account
|
|
4248
3840
|
};
|
|
4249
3841
|
}
|
|
3842
|
+
function normalizeReconnectAccountId(accountId) {
|
|
3843
|
+
return (accountId ?? "").trim() || "default";
|
|
3844
|
+
}
|
|
4250
3845
|
function isNoListenerReconnectError(lastError) {
|
|
4251
3846
|
return typeof lastError === "string" && /No active WhatsApp Web listener/i.test(lastError);
|
|
4252
3847
|
}
|
|
4253
|
-
function
|
|
4254
|
-
|
|
3848
|
+
function resolveExplicitWhatsAppDebounceOverride(params) {
|
|
3849
|
+
const channel = params.sourceCfg?.channels?.whatsapp;
|
|
3850
|
+
if (!channel) return;
|
|
3851
|
+
const accountId = normalizeReconnectAccountId(params.accountId);
|
|
3852
|
+
const accountDebounce = resolveAccountEntry(channel.accounts, accountId)?.debounceMs;
|
|
3853
|
+
if (accountDebounce !== void 0) return accountDebounce;
|
|
3854
|
+
if (accountId !== "default") {
|
|
3855
|
+
const defaultAccountDebounce = resolveAccountEntry(channel.accounts, "default")?.debounceMs;
|
|
3856
|
+
if (defaultAccountDebounce !== void 0) return defaultAccountDebounce;
|
|
3857
|
+
}
|
|
3858
|
+
return channel.debounceMs;
|
|
4255
3859
|
}
|
|
4256
3860
|
function isRetryableAuthUnstableError(error) {
|
|
4257
3861
|
return error instanceof WhatsAppAuthUnstableError || typeof error === "object" && error !== null && "code" in error && error.code === "whatsapp-auth-unstable";
|
|
4258
3862
|
}
|
|
4259
|
-
const DEFAULT_TRANSPORT_TIMEOUT_MS =
|
|
4260
|
-
const WHATSAPP_RECONNECT_CATCH_UP_MAX_MS = 20 * 6e4;
|
|
3863
|
+
const DEFAULT_TRANSPORT_TIMEOUT_MS = 3e5;
|
|
4261
3864
|
async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSocket, keepAlive = true, replyResolver, runtime = defaultRuntime, abortSignal, tuning = {}) {
|
|
4262
3865
|
const activeReplyResolver = replyResolver ?? (await loadReplyResolverRuntime()).getReplyFromConfig;
|
|
4263
3866
|
const runId = newConnectionId();
|
|
@@ -4273,8 +3876,10 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4273
3876
|
module: "web-reconnect",
|
|
4274
3877
|
runId
|
|
4275
3878
|
});
|
|
3879
|
+
const baseCfg = getRuntimeConfig$1();
|
|
3880
|
+
const sourceCfg = getRuntimeConfigSourceSnapshot();
|
|
4276
3881
|
const { cfg, account } = resolveWebMonitorConfigSnapshot({
|
|
4277
|
-
cfg:
|
|
3882
|
+
cfg: baseCfg,
|
|
4278
3883
|
accountId: tuning.accountId
|
|
4279
3884
|
});
|
|
4280
3885
|
const loadCurrentMonitorConfig = () => resolveWebMonitorConfigSnapshot({
|
|
@@ -4284,7 +3889,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4284
3889
|
const maxMediaBytes = resolveWhatsAppMediaMaxBytes(account);
|
|
4285
3890
|
const heartbeatSeconds = resolveHeartbeatSeconds(cfg, tuning.heartbeatSeconds);
|
|
4286
3891
|
const reconnectPolicy = resolveReconnectPolicy(cfg, tuning.reconnect);
|
|
4287
|
-
const socketTiming = resolveWhatsAppSocketTiming(tuning.socketTiming);
|
|
3892
|
+
const socketTiming = resolveWhatsAppSocketTiming(cfg, tuning.socketTiming);
|
|
4288
3893
|
const baseMentionConfig = buildMentionConfig(cfg);
|
|
4289
3894
|
const groupHistoryLimit = account.historyLimit ?? cfg.channels?.whatsapp?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT;
|
|
4290
3895
|
const groupHistories = /* @__PURE__ */ new Map();
|
|
@@ -4306,9 +3911,9 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4306
3911
|
};
|
|
4307
3912
|
process.once("SIGINT", handleSigint);
|
|
4308
3913
|
const transportTimeoutMs = tuning.transportTimeoutMs ?? DEFAULT_TRANSPORT_TIMEOUT_MS;
|
|
4309
|
-
const messageTimeoutMs = tuning.messageTimeoutMs ??
|
|
4310
|
-
const reconnectCatchUpWindowMs = Math.min(Math.max(messageTimeoutMs, 6e4),
|
|
4311
|
-
const watchdogCheckMs = tuning.watchdogCheckMs ??
|
|
3914
|
+
const messageTimeoutMs = tuning.messageTimeoutMs ?? 18e5;
|
|
3915
|
+
const reconnectCatchUpWindowMs = Math.min(Math.max(messageTimeoutMs, 6e4), WHATSAPP_INBOUND_DEDUPE_TTL_MS);
|
|
3916
|
+
const watchdogCheckMs = tuning.watchdogCheckMs ?? 6e4;
|
|
4312
3917
|
const controller = new WhatsAppConnectionController({
|
|
4313
3918
|
accountId: account.accountId,
|
|
4314
3919
|
authDir: account.authDir,
|
|
@@ -4332,16 +3937,19 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4332
3937
|
const connectionId = newConnectionId();
|
|
4333
3938
|
const inboundDebounceMs = resolveInboundDebounceMs({
|
|
4334
3939
|
cfg,
|
|
4335
|
-
channel: "whatsapp"
|
|
3940
|
+
channel: "whatsapp",
|
|
3941
|
+
overrideMs: resolveExplicitWhatsAppDebounceOverride({
|
|
3942
|
+
cfg,
|
|
3943
|
+
sourceCfg,
|
|
3944
|
+
accountId: account.accountId
|
|
3945
|
+
})
|
|
4336
3946
|
});
|
|
4337
3947
|
const shouldDebounce = (msg) => {
|
|
4338
|
-
const
|
|
4339
|
-
return
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
allowDebounce: !(admitted.payload.location || admitted.quote?.id || admitted.quote?.body)
|
|
4344
|
-
});
|
|
3948
|
+
const normalized = normalizeWebInboundMessage(msg);
|
|
3949
|
+
if (normalized.payload.media?.path || normalized.payload.media?.type) return false;
|
|
3950
|
+
if (normalized.payload.location) return false;
|
|
3951
|
+
if (normalized.quote?.id || normalized.quote?.body) return false;
|
|
3952
|
+
return !isControlCommandMessage(normalized.payload.commandBody ?? normalized.payload.body, cfg);
|
|
4345
3953
|
};
|
|
4346
3954
|
let connection;
|
|
4347
3955
|
try {
|
|
@@ -4394,11 +4002,11 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4394
4002
|
recentMessageKeys,
|
|
4395
4003
|
baileysGroupMetaCache,
|
|
4396
4004
|
onMessage: async (msg) => {
|
|
4397
|
-
const
|
|
4005
|
+
const normalized = normalizeWebInboundMessage(msg);
|
|
4398
4006
|
const inboundAt = Date.now();
|
|
4399
4007
|
controller.noteInbound(inboundAt);
|
|
4400
4008
|
statusController.noteInbound(inboundAt);
|
|
4401
|
-
await onMessage(
|
|
4009
|
+
await onMessage(normalized);
|
|
4402
4010
|
},
|
|
4403
4011
|
onPendingWorkChanged: (pendingWorkCount, at) => {
|
|
4404
4012
|
statusController.noteBusy(pendingWorkCount > 0, at);
|
|
@@ -4678,4 +4286,4 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
|
|
|
4678
4286
|
}
|
|
4679
4287
|
}
|
|
4680
4288
|
//#endregion
|
|
4681
|
-
export { loadWebMediaRaw as a, monitorWebInbox as c, loadWebMedia as i, resetWebInboundDedupe as l, LocalMediaAccessError as n, optimizeImageToJpeg as o, getDefaultLocalRoots as r, optimizeImageToPng as s, monitorWebChannel as t };
|
|
4289
|
+
export { loadWebMediaRaw as a, monitorWebInbox as c, loadWebMedia$1 as i, resetWebInboundDedupe as l, LocalMediaAccessError as n, optimizeImageToJpeg as o, getDefaultLocalRoots as r, optimizeImageToPng as s, monitorWebChannel as t };
|