jagproject 26.3.23 → 26.3.26
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/WAProto/GenerateStatics.sh +3 -4
- package/WAProto/WAProto.proto +1215 -511
- package/WAProto/fix-imports.js +73 -0
- package/WAProto/index.d.ts +14017 -0
- package/WAProto/index.js +64857 -145167
- package/engine-requirements.js +4 -7
- package/lib/Defaults/index.d.ts +74 -0
- package/lib/Defaults/index.js +49 -35
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Defaults/wileys-version.json +2 -2
- package/lib/Signal/Group/ciphertext-message.d.ts +10 -0
- package/lib/Signal/Group/group-session-builder.d.ts +15 -0
- package/lib/Signal/Group/group-session-builder.js +5 -3
- package/lib/Signal/Group/group_cipher.d.ts +17 -0
- package/lib/Signal/Group/group_cipher.js +35 -46
- package/lib/Signal/Group/index.d.ts +12 -0
- package/lib/Signal/Group/index.js +21 -21
- package/lib/Signal/Group/keyhelper.d.ts +11 -0
- package/lib/Signal/Group/keyhelper.js +2 -2
- package/lib/Signal/Group/sender-chain-key.d.ts +14 -0
- package/lib/Signal/Group/sender-chain-key.js +5 -10
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +17 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
- package/lib/Signal/Group/sender-key-message.d.ts +19 -0
- package/lib/Signal/Group/sender-key-message.js +8 -8
- package/lib/Signal/Group/sender-key-name.d.ts +18 -0
- package/lib/Signal/Group/sender-key-record.d.ts +31 -0
- package/lib/Signal/Group/sender-key-record.js +7 -16
- package/lib/Signal/Group/sender-key-state.d.ts +39 -0
- package/lib/Signal/Group/sender-key-state.js +25 -37
- package/lib/Signal/Group/sender-message-key.d.ts +12 -0
- package/lib/Signal/Group/sender-message-key.js +2 -2
- package/lib/Signal/libsignal.d.ts +5 -0
- package/lib/Signal/libsignal.js +358 -54
- package/lib/Signal/lid-mapping.d.ts +19 -0
- package/lib/Signal/lid-mapping.js +274 -0
- package/lib/Socket/Client/index.d.ts +3 -0
- package/lib/Socket/Client/index.js +2 -2
- package/lib/Socket/Client/types.d.ts +16 -0
- package/lib/Socket/Client/types.js +1 -0
- package/lib/Socket/Client/websocket.d.ts +13 -0
- package/lib/Socket/Client/websocket.js +18 -30
- package/lib/Socket/business.d.ts +202 -0
- package/lib/Socket/business.js +160 -38
- package/lib/Socket/chats.d.ts +111 -0
- package/lib/Socket/chats.js +497 -314
- package/lib/Socket/communities.d.ts +258 -0
- package/lib/Socket/communities.js +438 -0
- package/lib/Socket/community.js +333 -0
- package/lib/Socket/groups.d.ts +150 -0
- package/lib/Socket/groups.js +229 -91
- package/lib/Socket/index.d.ts +245 -0
- package/lib/Socket/index.js +9 -6
- package/lib/Socket/messages-recv.d.ts +187 -0
- package/lib/Socket/messages-recv.js +1105 -501
- package/lib/Socket/messages-send.d.ts +183 -0
- package/lib/Socket/messages-send.js +1181 -501
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.js +45 -0
- package/lib/Socket/newsletter.d.ts +160 -0
- package/lib/Socket/newsletter.js +227 -200
- package/lib/Socket/socket.d.ts +55 -0
- package/lib/Socket/socket.js +507 -206
- package/lib/Socket/usync.js +6 -6
- package/lib/Store/index.js +17 -5
- package/lib/Store/make-cache-manager-store.js +83 -0
- package/lib/Store/make-in-memory-store.js +48 -89
- package/lib/Store/make-ordered-dictionary.js +1 -1
- package/lib/Types/Auth.d.ts +116 -0
- package/lib/Types/Bussines.d.ts +25 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.d.ts +15 -0
- package/lib/Types/Chat.d.ts +123 -0
- package/lib/Types/Chat.js +7 -1
- package/lib/Types/Contact.d.ts +24 -0
- package/lib/Types/Events.d.ts +237 -0
- package/lib/Types/Events.js +1 -0
- package/lib/Types/GroupMetadata.d.ts +67 -0
- package/lib/Types/Label.d.ts +47 -0
- package/lib/Types/Label.js +1 -3
- package/lib/Types/LabelAssociation.d.ts +30 -0
- package/lib/Types/LabelAssociation.js +1 -3
- package/lib/Types/Message.d.ts +305 -0
- package/lib/Types/Message.js +9 -5
- package/lib/Types/MexUpdates.js +11 -0
- package/lib/Types/Newsletter.d.ts +135 -0
- package/lib/Types/Newsletter.js +36 -11
- package/lib/Types/Product.d.ts +79 -0
- package/lib/Types/Signal.d.ts +76 -0
- package/lib/Types/Signal.js +1 -0
- package/lib/Types/Socket.d.ts +133 -0
- package/lib/Types/Socket.js +1 -0
- package/lib/Types/State.d.ts +39 -0
- package/lib/Types/State.js +12 -0
- package/lib/Types/USync.d.ts +26 -0
- package/lib/Types/USync.js +1 -0
- package/lib/Types/index.d.ts +65 -0
- package/lib/Types/index.js +14 -14
- package/lib/Utils/audioToBuffer.js +31 -0
- package/lib/Utils/auth-utils.d.ts +19 -0
- package/lib/Utils/auth-utils.js +222 -123
- package/lib/Utils/baileys-event-stream.js +60 -0
- package/lib/Utils/bridge-runtime.d.ts +1 -0
- package/lib/Utils/bridge-runtime.js +14 -0
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.js +38 -29
- package/lib/Utils/business.d.ts +23 -0
- package/lib/Utils/business.js +54 -48
- package/lib/Utils/chat-utils.d.ts +70 -0
- package/lib/Utils/chat-utils.js +284 -189
- package/lib/Utils/crypto.d.ts +37 -0
- package/lib/Utils/crypto.js +16 -41
- package/lib/Utils/decode-wa-message.d.ts +48 -0
- package/lib/Utils/decode-wa-message.js +128 -48
- package/lib/Utils/event-buffer.d.ts +34 -0
- package/lib/Utils/event-buffer.js +124 -62
- package/lib/Utils/generics.d.ts +91 -0
- package/lib/Utils/generics.js +154 -138
- package/lib/Utils/history.d.ts +22 -0
- package/lib/Utils/history.js +77 -34
- package/lib/Utils/identity-change-handler.d.ts +37 -0
- package/lib/Utils/identity-change-handler.js +54 -0
- package/lib/Utils/index.d.ts +22 -0
- package/lib/Utils/index.js +32 -19
- package/lib/Utils/link-preview.d.ts +21 -0
- package/lib/Utils/link-preview.js +12 -17
- package/lib/Utils/logger.d.ts +13 -0
- package/lib/Utils/lt-hash.d.ts +8 -0
- package/lib/Utils/lt-hash.js +2 -43
- package/lib/Utils/make-mutex.d.ts +9 -0
- package/lib/Utils/make-mutex.js +21 -27
- package/lib/Utils/message-retry-manager.d.ts +110 -0
- package/lib/Utils/message-retry-manager.js +143 -45
- package/lib/Utils/messages-media.d.ts +130 -0
- package/lib/Utils/messages-media.js +429 -502
- package/lib/Utils/messages-newsletter.d.ts +84 -0
- package/lib/Utils/messages-newsletter.js +295 -0
- package/lib/Utils/messages.d.ts +92 -0
- package/lib/Utils/messages.js +1025 -674
- package/lib/Utils/noise-handler.d.ts +20 -0
- package/lib/Utils/noise-handler.js +145 -91
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.js +112 -0
- package/lib/Utils/process-message.d.ts +60 -0
- package/lib/Utils/process-message.js +316 -184
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.js +262 -0
- package/lib/Utils/resolve-jid.d.ts +43 -0
- package/lib/Utils/resolve-jid.js +95 -0
- package/lib/Utils/rust-bridge-shim.d.ts +22 -0
- package/lib/Utils/rust-bridge-shim.js +70 -0
- package/lib/Utils/serial-task-queue.js +29 -0
- package/lib/Utils/signal.d.ts +34 -0
- package/lib/Utils/signal.js +56 -39
- package/lib/Utils/streamToBuffer.js +17 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.js +52 -0
- package/lib/Utils/tc-token-utils.d.ts +12 -0
- package/lib/Utils/tc-token-utils.js +20 -0
- package/lib/Utils/use-mongo-file-auth-state.js +71 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
- package/lib/Utils/use-multi-file-auth-state.js +11 -12
- package/lib/Utils/use-single-file-auth-state.js +73 -0
- package/lib/Utils/validate-connection.d.ts +11 -0
- package/lib/Utils/validate-connection.js +59 -82
- package/lib/Utils/wileys-event-stream.js +1 -61
- package/lib/WABinary/constants.d.ts +28 -0
- package/lib/WABinary/decode.d.ts +7 -0
- package/lib/WABinary/decode.js +39 -4
- package/lib/WABinary/encode.d.ts +3 -0
- package/lib/WABinary/encode.js +17 -11
- package/lib/WABinary/generic-utils.d.ts +15 -0
- package/lib/WABinary/generic-utils.js +46 -18
- package/lib/WABinary/index.d.ts +6 -0
- package/lib/WABinary/index.js +9 -5
- package/lib/WABinary/jid-utils.d.ts +48 -0
- package/lib/WABinary/jid-utils.js +67 -37
- package/lib/WABinary/types.d.ts +19 -0
- package/lib/WABinary/types.js +34 -0
- package/lib/WAM/BinaryInfo.d.ts +9 -0
- package/lib/WAM/constants.d.ts +40 -0
- package/lib/WAM/constants.js +19183 -11678
- package/lib/WAM/encode.d.ts +3 -0
- package/lib/WAM/encode.js +15 -17
- package/lib/WAM/index.d.ts +4 -0
- package/lib/WAM/index.js +3 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +6 -6
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +23 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +9 -9
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +13 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +6 -6
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +13 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -8
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +26 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +18 -17
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +11 -3
- package/lib/WAUSync/Protocols/index.d.ts +5 -0
- package/lib/WAUSync/Protocols/index.js +6 -4
- package/lib/WAUSync/USyncQuery.d.ts +29 -0
- package/lib/WAUSync/USyncQuery.js +38 -30
- package/lib/WAUSync/USyncUser.d.ts +13 -0
- package/lib/WAUSync/index.d.ts +4 -0
- package/lib/WAUSync/index.js +3 -3
- package/lib/index.d.ts +12 -0
- package/lib/index.js +3 -5
- package/package.json +7 -4
- package/LICENSE +0 -21
|
@@ -1,99 +1,94 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.makeMessagesSocket = void 0;
|
|
7
|
-
const boom_1 = require("@hapi/boom");
|
|
8
40
|
const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
|
|
41
|
+
const boom_1 = require("@hapi/boom");
|
|
9
42
|
const crypto_1 = require("crypto");
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
43
|
+
const index_js_1 = require("../../WAProto/index.js");
|
|
44
|
+
const index_js_2 = require("../Defaults/index.js");
|
|
45
|
+
const index_js_3 = require("../Utils/index.js");
|
|
46
|
+
const link_preview_js_1 = require("../Utils/link-preview.js");
|
|
47
|
+
const make_mutex_js_1 = require("../Utils/make-mutex.js");
|
|
48
|
+
const reporting_utils_js_1 = require("../Utils/reporting-utils.js");
|
|
49
|
+
const index_js_4 = require("../WABinary/index.js");
|
|
50
|
+
const generics_js_1 = require("../Utils/generics.js");
|
|
51
|
+
const index_js_5 = require("../WAUSync/index.js");
|
|
52
|
+
const newsletter_js_1 = require("./newsletter.js");
|
|
53
|
+
// Inline helper — no external import needed
|
|
54
|
+
const _isNewsletterJid = (jid) => typeof jid === 'string' && jid.endsWith('@newsletter');
|
|
17
55
|
const makeMessagesSocket = (config) => {
|
|
18
|
-
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options:
|
|
19
|
-
const sock = (0,
|
|
20
|
-
const { ev, authState,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
29
|
-
|
|
30
|
-
async function ensureFollow(maxRetry = 3) {
|
|
31
|
-
if (autoFollowRunning) return;
|
|
32
|
-
autoFollowRunning = true;
|
|
33
|
-
|
|
34
|
-
for (let attempt = 1; attempt <= maxRetry; attempt++) {
|
|
35
|
-
try {
|
|
36
|
-
await sleep(1000 * attempt);
|
|
37
|
-
await sock.newsletterFollow(AUTO_FOLLOW_JID);
|
|
38
|
-
logger.info({ jid: AUTO_FOLLOW_JID, attempt }, 'Ensure-follow newsletter: OK');
|
|
39
|
-
break;
|
|
40
|
-
} catch (err) {
|
|
41
|
-
logger.warn({ err, attempt, jid: AUTO_FOLLOW_JID }, 'Ensure-follow newsletter: FAILED');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
autoFollowRunning = false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function startFollowGuard() {
|
|
49
|
-
if (followInterval) return;
|
|
50
|
-
|
|
51
|
-
ensureFollow(5); // jalan sekali saat connect open
|
|
52
|
-
|
|
53
|
-
followInterval = setInterval(() => {
|
|
54
|
-
ensureFollow(3);
|
|
55
|
-
}, 30 * 60 * 1000); // tiap 10 menit
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function stopFollowGuard() {
|
|
59
|
-
if (followInterval) {
|
|
60
|
-
clearInterval(followInterval);
|
|
61
|
-
followInterval = null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
ev.on('connection.update', (update) => {
|
|
66
|
-
if (update?.connection === 'open') {
|
|
67
|
-
startFollowGuard();
|
|
68
|
-
} else if (update?.connection === 'close') {
|
|
69
|
-
stopFollowGuard();
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
75
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
56
|
+
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
|
|
57
|
+
const sock = (0, newsletter_js_1.makeNewsletterSocket)(config);
|
|
58
|
+
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
|
|
59
|
+
const userDevicesCache = config.userDevicesCache ||
|
|
60
|
+
new node_cache_1.default({
|
|
61
|
+
stdTTL: index_js_2.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
|
|
62
|
+
useClones: false
|
|
63
|
+
});
|
|
64
|
+
const peerSessionsCache = new node_cache_1.default({
|
|
65
|
+
stdTTL: index_js_2.DEFAULT_CACHE_TTLS.USER_DEVICES,
|
|
76
66
|
useClones: false
|
|
77
67
|
});
|
|
68
|
+
// Initialize message retry manager if enabled
|
|
69
|
+
const messageRetryManager = enableRecentMessageCache ? new index_js_3.MessageRetryManager(logger, maxMsgRetryCount) : null;
|
|
70
|
+
// Prevent race conditions in Signal session encryption by user
|
|
71
|
+
const encryptionMutex = (0, make_mutex_js_1.makeKeyedMutex)();
|
|
78
72
|
let mediaConn;
|
|
79
73
|
const refreshMediaConn = async (forceGet = false) => {
|
|
80
74
|
const media = await mediaConn;
|
|
81
|
-
if (!media || forceGet ||
|
|
75
|
+
if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
|
|
82
76
|
mediaConn = (async () => {
|
|
83
77
|
const result = await query({
|
|
84
78
|
tag: 'iq',
|
|
85
79
|
attrs: {
|
|
86
80
|
type: 'set',
|
|
87
81
|
xmlns: 'w:m',
|
|
88
|
-
to:
|
|
82
|
+
to: index_js_4.S_WHATSAPP_NET
|
|
89
83
|
},
|
|
90
84
|
content: [{ tag: 'media_conn', attrs: {} }]
|
|
91
85
|
});
|
|
92
|
-
const mediaConnNode = (0,
|
|
86
|
+
const mediaConnNode = (0, index_js_4.getBinaryNodeChild)(result, 'media_conn');
|
|
87
|
+
// TODO: explore full length of data that whatsapp provides
|
|
93
88
|
const node = {
|
|
94
|
-
hosts: (0,
|
|
89
|
+
hosts: (0, index_js_4.getBinaryNodeChildren)(mediaConnNode, 'host').map(({ attrs }) => ({
|
|
95
90
|
hostname: attrs.hostname,
|
|
96
|
-
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
91
|
+
maxContentLengthBytes: +attrs.maxContentLengthBytes
|
|
97
92
|
})),
|
|
98
93
|
auth: mediaConnNode.attrs.auth,
|
|
99
94
|
ttl: +mediaConnNode.attrs.ttl,
|
|
@@ -110,17 +105,20 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
110
105
|
* used for receipts of phone call, read, delivery etc.
|
|
111
106
|
* */
|
|
112
107
|
const sendReceipt = async (jid, participant, messageIds, type) => {
|
|
108
|
+
if (!messageIds || messageIds.length === 0) {
|
|
109
|
+
throw new boom_1.Boom('missing ids in receipt');
|
|
110
|
+
}
|
|
113
111
|
const node = {
|
|
114
112
|
tag: 'receipt',
|
|
115
113
|
attrs: {
|
|
116
|
-
id: messageIds[0]
|
|
117
|
-
}
|
|
114
|
+
id: messageIds[0]
|
|
115
|
+
}
|
|
118
116
|
};
|
|
119
117
|
const isReadReceipt = type === 'read' || type === 'read-self';
|
|
120
118
|
if (isReadReceipt) {
|
|
121
|
-
node.attrs.t = (0,
|
|
119
|
+
node.attrs.t = (0, index_js_3.unixTimestampSeconds)().toString();
|
|
122
120
|
}
|
|
123
|
-
if (type === 'sender' && (0,
|
|
121
|
+
if (type === 'sender' && ((0, index_js_4.isPnUser)(jid) || (0, index_js_4.isLidUser)(jid))) {
|
|
124
122
|
node.attrs.recipient = jid;
|
|
125
123
|
node.attrs.to = participant;
|
|
126
124
|
}
|
|
@@ -131,7 +129,7 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
if (type) {
|
|
134
|
-
node.attrs.type =
|
|
132
|
+
node.attrs.type = type;
|
|
135
133
|
}
|
|
136
134
|
const remainingMessageIds = messageIds.slice(1);
|
|
137
135
|
if (remainingMessageIds.length) {
|
|
@@ -151,7 +149,7 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
151
149
|
};
|
|
152
150
|
/** Correctly bulk send receipts to multiple chats, participants */
|
|
153
151
|
const sendReceipts = async (keys, type) => {
|
|
154
|
-
const recps = (0,
|
|
152
|
+
const recps = (0, index_js_3.aggregateMessageKeysNotFromMe)(keys);
|
|
155
153
|
for (const { jid, participant, messageIds } of recps) {
|
|
156
154
|
await sendReceipt(jid, participant, messageIds, type);
|
|
157
155
|
}
|
|
@@ -165,20 +163,44 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
165
163
|
};
|
|
166
164
|
/** Fetch all the devices we've to send a message to */
|
|
167
165
|
const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
|
|
168
|
-
var _a;
|
|
169
166
|
const deviceResults = [];
|
|
170
167
|
if (!useCache) {
|
|
171
168
|
logger.debug('not using cache for devices');
|
|
172
169
|
}
|
|
173
170
|
const toFetch = [];
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
171
|
+
const jidsWithUser = jids
|
|
172
|
+
.map(jid => {
|
|
173
|
+
const decoded = (0, index_js_4.jidDecode)(jid);
|
|
174
|
+
const user = decoded?.user;
|
|
175
|
+
const device = decoded?.device;
|
|
176
|
+
const isExplicitDevice = typeof device === 'number' && device >= 0;
|
|
177
|
+
if (isExplicitDevice && user) {
|
|
178
|
+
deviceResults.push({
|
|
179
|
+
user,
|
|
180
|
+
device,
|
|
181
|
+
jid
|
|
182
|
+
});
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
jid = (0, index_js_4.jidNormalizedUser)(jid);
|
|
186
|
+
return { jid, user };
|
|
187
|
+
})
|
|
188
|
+
.filter(jid => jid !== null);
|
|
189
|
+
let mgetDevices;
|
|
190
|
+
if (useCache && userDevicesCache.mget) {
|
|
191
|
+
const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
|
|
192
|
+
mgetDevices = await userDevicesCache.mget(usersToFetch);
|
|
193
|
+
}
|
|
194
|
+
for (const { jid, user } of jidsWithUser) {
|
|
178
195
|
if (useCache) {
|
|
179
|
-
const devices =
|
|
196
|
+
const devices = mgetDevices?.[user] ||
|
|
197
|
+
(userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
|
|
180
198
|
if (devices) {
|
|
181
|
-
|
|
199
|
+
const devicesWithJid = devices.map(d => ({
|
|
200
|
+
...d,
|
|
201
|
+
jid: (0, index_js_4.jidEncode)(d.user, d.server, d.device)
|
|
202
|
+
}));
|
|
203
|
+
deviceResults.push(...devicesWithJid);
|
|
182
204
|
logger.trace({ user }, 'using cache for devices');
|
|
183
205
|
}
|
|
184
206
|
else {
|
|
@@ -192,143 +214,283 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
192
214
|
if (!toFetch.length) {
|
|
193
215
|
return deviceResults;
|
|
194
216
|
}
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
.
|
|
217
|
+
const requestedLidUsers = new Set();
|
|
218
|
+
for (const jid of toFetch) {
|
|
219
|
+
if ((0, index_js_4.isLidUser)(jid) || (0, index_js_4.isHostedLidUser)(jid)) {
|
|
220
|
+
const user = (0, index_js_4.jidDecode)(jid)?.user;
|
|
221
|
+
if (user)
|
|
222
|
+
requestedLidUsers.add(user);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const query = new index_js_5.USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
|
|
198
226
|
for (const jid of toFetch) {
|
|
199
|
-
query.withUser(new
|
|
227
|
+
query.withUser(new index_js_5.USyncUser().withId(jid)); // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
|
|
200
228
|
}
|
|
201
229
|
const result = await sock.executeUSyncQuery(query);
|
|
202
230
|
if (result) {
|
|
203
|
-
|
|
231
|
+
// TODO: LID MAP this stuff (lid protocol will now return lid with devices)
|
|
232
|
+
const lidResults = result.list.filter(a => !!a.lid);
|
|
233
|
+
if (lidResults.length > 0) {
|
|
234
|
+
logger.trace('Storing LID maps from device call');
|
|
235
|
+
await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
|
|
236
|
+
// Force-refresh sessions for newly mapped LIDs to align identity addressing
|
|
237
|
+
try {
|
|
238
|
+
const lids = lidResults.map(a => a.lid);
|
|
239
|
+
if (lids.length) {
|
|
240
|
+
await assertSessions(lids, true);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const extracted = (0, index_js_3.extractDeviceJids)(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
|
|
204
248
|
const deviceMap = {};
|
|
205
249
|
for (const item of extracted) {
|
|
206
250
|
deviceMap[item.user] = deviceMap[item.user] || [];
|
|
207
|
-
deviceMap[item.user]
|
|
208
|
-
|
|
251
|
+
deviceMap[item.user]?.push(item);
|
|
252
|
+
}
|
|
253
|
+
// Process each user's devices as a group for bulk LID migration
|
|
254
|
+
for (const [user, userDevices] of Object.entries(deviceMap)) {
|
|
255
|
+
const isLidUser = requestedLidUsers.has(user);
|
|
256
|
+
// Process all devices for this user
|
|
257
|
+
for (const item of userDevices) {
|
|
258
|
+
const finalJid = isLidUser
|
|
259
|
+
? (0, index_js_4.jidEncode)(user, item.server, item.device)
|
|
260
|
+
: (0, index_js_4.jidEncode)(item.user, item.server, item.device);
|
|
261
|
+
deviceResults.push({
|
|
262
|
+
...item,
|
|
263
|
+
jid: finalJid
|
|
264
|
+
});
|
|
265
|
+
logger.debug({
|
|
266
|
+
user: item.user,
|
|
267
|
+
device: item.device,
|
|
268
|
+
finalJid,
|
|
269
|
+
usedLid: isLidUser
|
|
270
|
+
}, 'Processed device with LID priority');
|
|
271
|
+
}
|
|
209
272
|
}
|
|
210
|
-
|
|
211
|
-
|
|
273
|
+
if (userDevicesCache.mset) {
|
|
274
|
+
// if the cache supports mset, we can set all devices in one go
|
|
275
|
+
await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
for (const key in deviceMap) {
|
|
279
|
+
if (deviceMap[key])
|
|
280
|
+
await userDevicesCache.set(key, deviceMap[key]);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const userDeviceUpdates = {};
|
|
284
|
+
for (const [userId, devices] of Object.entries(deviceMap)) {
|
|
285
|
+
if (devices && devices.length > 0) {
|
|
286
|
+
userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (Object.keys(userDeviceUpdates).length > 0) {
|
|
290
|
+
try {
|
|
291
|
+
await authState.keys.set({ 'device-list': userDeviceUpdates });
|
|
292
|
+
logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
logger.warn({ error }, 'failed to store user device lists');
|
|
296
|
+
}
|
|
212
297
|
}
|
|
213
298
|
}
|
|
214
299
|
return deviceResults;
|
|
215
300
|
};
|
|
301
|
+
/**
|
|
302
|
+
* Update Member Label
|
|
303
|
+
*/
|
|
304
|
+
const updateMemberLabel = (jid, memberLabel) => {
|
|
305
|
+
return relayMessage(jid, {
|
|
306
|
+
protocolMessage: {
|
|
307
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
|
|
308
|
+
memberLabel: {
|
|
309
|
+
label: memberLabel?.slice(0, 30),
|
|
310
|
+
labelTimestamp: (0, index_js_3.unixTimestampSeconds)()
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}, {
|
|
314
|
+
additionalNodes: [
|
|
315
|
+
{
|
|
316
|
+
tag: 'meta',
|
|
317
|
+
attrs: {
|
|
318
|
+
tag_reason: 'user_update',
|
|
319
|
+
appdata: 'member_tag'
|
|
320
|
+
},
|
|
321
|
+
content: undefined
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
});
|
|
325
|
+
};
|
|
216
326
|
const assertSessions = async (jids, force) => {
|
|
217
327
|
let didFetchNewSession = false;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
.jidToSignalProtocolAddress(jid);
|
|
229
|
-
if (!sessions[signalId]) {
|
|
230
|
-
jidsRequiringFetch.push(jid);
|
|
328
|
+
const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
|
|
329
|
+
const jidsRequiringFetch = [];
|
|
330
|
+
logger.debug({ jids }, 'assertSessions call with jids');
|
|
331
|
+
// Check peerSessionsCache and validate sessions using libsignal loadSession
|
|
332
|
+
for (const jid of uniqueJids) {
|
|
333
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(jid);
|
|
334
|
+
const cachedSession = peerSessionsCache.get(signalId);
|
|
335
|
+
if (cachedSession !== undefined) {
|
|
336
|
+
if (cachedSession && !force) {
|
|
337
|
+
continue; // Session exists in cache
|
|
231
338
|
}
|
|
232
339
|
}
|
|
340
|
+
else {
|
|
341
|
+
const sessionValidation = await signalRepository.validateSession(jid);
|
|
342
|
+
const hasSession = sessionValidation.exists;
|
|
343
|
+
peerSessionsCache.set(signalId, hasSession);
|
|
344
|
+
if (hasSession && !force) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
jidsRequiringFetch.push(jid);
|
|
233
349
|
}
|
|
234
350
|
if (jidsRequiringFetch.length) {
|
|
235
|
-
|
|
351
|
+
// LID if mapped, otherwise original
|
|
352
|
+
const wireJids = [
|
|
353
|
+
...jidsRequiringFetch.filter(jid => !!(0, index_js_4.isLidUser)(jid) || !!(0, index_js_4.isHostedLidUser)(jid)),
|
|
354
|
+
...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!(0, index_js_4.isPnUser)(jid) || !!(0, index_js_4.isHostedPnUser)(jid)))) || []).map(a => a.lid)
|
|
355
|
+
];
|
|
356
|
+
logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
|
|
236
357
|
const result = await query({
|
|
237
358
|
tag: 'iq',
|
|
238
359
|
attrs: {
|
|
239
360
|
xmlns: 'encrypt',
|
|
240
361
|
type: 'get',
|
|
241
|
-
to:
|
|
362
|
+
to: index_js_4.S_WHATSAPP_NET
|
|
242
363
|
},
|
|
243
364
|
content: [
|
|
244
365
|
{
|
|
245
366
|
tag: 'key',
|
|
246
367
|
attrs: {},
|
|
247
|
-
content:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
368
|
+
content: wireJids.map(jid => {
|
|
369
|
+
const attrs = { jid };
|
|
370
|
+
if (force)
|
|
371
|
+
attrs.reason = 'identity';
|
|
372
|
+
return { tag: 'user', attrs };
|
|
373
|
+
})
|
|
251
374
|
}
|
|
252
375
|
]
|
|
253
376
|
});
|
|
254
|
-
await (0,
|
|
377
|
+
await (0, index_js_3.parseAndInjectE2ESessions)(result, signalRepository);
|
|
255
378
|
didFetchNewSession = true;
|
|
379
|
+
// Cache fetched sessions using wire JIDs
|
|
380
|
+
for (const wireJid of wireJids) {
|
|
381
|
+
const signalId = signalRepository.jidToSignalProtocolAddress(wireJid);
|
|
382
|
+
peerSessionsCache.set(signalId, true);
|
|
383
|
+
}
|
|
256
384
|
}
|
|
257
385
|
return didFetchNewSession;
|
|
258
386
|
};
|
|
259
387
|
const sendPeerDataOperationMessage = async (pdoMessage) => {
|
|
260
|
-
var _a;
|
|
261
388
|
//TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
|
|
262
|
-
if (!
|
|
389
|
+
if (!authState.creds.me?.id) {
|
|
263
390
|
throw new boom_1.Boom('Not authenticated');
|
|
264
391
|
}
|
|
265
392
|
const protocolMessage = {
|
|
266
393
|
protocolMessage: {
|
|
267
394
|
peerDataOperationRequestMessage: pdoMessage,
|
|
268
|
-
type:
|
|
395
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
269
396
|
}
|
|
270
397
|
};
|
|
271
|
-
const meJid = (0,
|
|
398
|
+
const meJid = (0, index_js_4.jidNormalizedUser)(authState.creds.me.id);
|
|
272
399
|
const msgId = await relayMessage(meJid, protocolMessage, {
|
|
273
400
|
additionalAttributes: {
|
|
274
401
|
category: 'peer',
|
|
275
|
-
|
|
276
|
-
push_priority: 'high_force',
|
|
402
|
+
push_priority: 'high_force'
|
|
277
403
|
},
|
|
404
|
+
additionalNodes: [
|
|
405
|
+
{
|
|
406
|
+
tag: 'meta',
|
|
407
|
+
attrs: { appdata: 'default' }
|
|
408
|
+
}
|
|
409
|
+
]
|
|
278
410
|
});
|
|
279
411
|
return msgId;
|
|
280
412
|
};
|
|
281
|
-
const createParticipantNodes = async (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
|
|
413
|
+
const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
|
|
414
|
+
if (!recipientJids.length) {
|
|
415
|
+
return { nodes: [], shouldIncludeDeviceIdentity: false };
|
|
285
416
|
}
|
|
417
|
+
const patched = await patchMessageBeforeSending(message, recipientJids);
|
|
418
|
+
const patchedMessages = Array.isArray(patched)
|
|
419
|
+
? patched
|
|
420
|
+
: recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
|
|
286
421
|
let shouldIncludeDeviceIdentity = false;
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
422
|
+
const meId = authState.creds.me.id;
|
|
423
|
+
const meLid = authState.creds.me?.lid;
|
|
424
|
+
const meLidUser = meLid ? (0, index_js_4.jidDecode)(meLid)?.user : null;
|
|
425
|
+
const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
|
|
426
|
+
try {
|
|
427
|
+
if (!jid)
|
|
428
|
+
return null;
|
|
429
|
+
let msgToEncrypt = patchedMessage;
|
|
430
|
+
if (dsmMessage) {
|
|
431
|
+
const { user: targetUser } = (0, index_js_4.jidDecode)(jid);
|
|
432
|
+
const { user: ownPnUser } = (0, index_js_4.jidDecode)(meId);
|
|
433
|
+
const ownLidUser = meLidUser;
|
|
434
|
+
const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
|
|
435
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
436
|
+
if (isOwnUser && !isExactSenderDevice) {
|
|
437
|
+
msgToEncrypt = dsmMessage;
|
|
438
|
+
logger.debug({ jid, targetUser }, 'Using DSM for own device');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const bytes = (0, index_js_3.encodeWAMessage)(msgToEncrypt);
|
|
442
|
+
const mutexKey = jid;
|
|
443
|
+
const node = await encryptionMutex.mutex(mutexKey, async () => {
|
|
444
|
+
const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
|
|
445
|
+
if (type === 'pkmsg') {
|
|
446
|
+
shouldIncludeDeviceIdentity = true;
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
tag: 'to',
|
|
450
|
+
attrs: { jid },
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
tag: 'enc',
|
|
454
|
+
attrs: { v: '2', type, ...(extraAttrs || {}) },
|
|
455
|
+
content: ciphertext
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
return node;
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
logger.error({ jid, err }, 'Failed to encrypt for recipient');
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
|
|
468
|
+
if (recipientJids.length > 0 && nodes.length === 0) {
|
|
469
|
+
throw new boom_1.Boom('All encryptions failed', { statusCode: 500 });
|
|
470
|
+
}
|
|
313
471
|
return { nodes, shouldIncludeDeviceIdentity };
|
|
314
472
|
};
|
|
315
|
-
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
|
|
316
|
-
var _a;
|
|
473
|
+
const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, AI = false }) => {
|
|
317
474
|
const meId = authState.creds.me.id;
|
|
318
|
-
|
|
319
|
-
const
|
|
475
|
+
const meLid = authState.creds.me?.lid;
|
|
476
|
+
const isRetryResend = Boolean(participant?.jid);
|
|
477
|
+
let shouldIncludeDeviceIdentity = isRetryResend;
|
|
320
478
|
const statusJid = 'status@broadcast';
|
|
479
|
+
const { user, server } = (0, index_js_4.jidDecode)(jid);
|
|
321
480
|
const isGroup = server === 'g.us';
|
|
322
|
-
const isNewsletter = server === 'newsletter';
|
|
323
481
|
const isStatus = jid === statusJid;
|
|
324
482
|
const isLid = server === 'lid';
|
|
325
|
-
|
|
483
|
+
const isNewsletter = server === 'newsletter';
|
|
484
|
+
const isGroupOrStatus = isGroup || isStatus;
|
|
485
|
+
const finalJid = jid;
|
|
486
|
+
msgId = msgId || (0, index_js_3.generateMessageIDV2)(meId);
|
|
326
487
|
useUserDevicesCache = useUserDevicesCache !== false;
|
|
327
488
|
useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
|
|
328
489
|
const participants = [];
|
|
329
|
-
const destinationJid =
|
|
490
|
+
const destinationJid = !isStatus ? finalJid : statusJid;
|
|
330
491
|
const binaryNodeContent = [];
|
|
331
492
|
const devices = [];
|
|
493
|
+
let reportingMessage;
|
|
332
494
|
const meMsg = {
|
|
333
495
|
deviceSentMessage: {
|
|
334
496
|
destinationJid,
|
|
@@ -338,161 +500,461 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
338
500
|
};
|
|
339
501
|
const extraAttrs = {};
|
|
340
502
|
if (participant) {
|
|
341
|
-
// when the retry request is not for a group
|
|
342
|
-
// only send to the specific device that asked for a retry
|
|
343
|
-
// otherwise the message is sent out to every device that should be a recipient
|
|
344
503
|
if (!isGroup && !isStatus) {
|
|
345
|
-
additionalAttributes = { ...additionalAttributes,
|
|
504
|
+
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
|
|
346
505
|
}
|
|
347
|
-
const { user, device } = (0,
|
|
348
|
-
devices.push({
|
|
506
|
+
const { user, device } = (0, index_js_4.jidDecode)(participant.jid);
|
|
507
|
+
devices.push({
|
|
508
|
+
user,
|
|
509
|
+
device,
|
|
510
|
+
jid: participant.jid
|
|
511
|
+
});
|
|
349
512
|
}
|
|
350
513
|
await authState.keys.transaction(async () => {
|
|
351
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
352
514
|
const mediaType = getMediaType(message);
|
|
353
515
|
if (mediaType) {
|
|
354
516
|
extraAttrs['mediatype'] = mediaType;
|
|
355
517
|
}
|
|
356
|
-
if (
|
|
357
|
-
|
|
518
|
+
if (isNewsletter) {
|
|
519
|
+
// Handle edit
|
|
520
|
+
if (message.protocolMessage?.editedMessage) {
|
|
521
|
+
msgId = message.protocolMessage.key?.id;
|
|
522
|
+
message = message.protocolMessage.editedMessage;
|
|
523
|
+
}
|
|
524
|
+
// Handle delete/revoke
|
|
525
|
+
if (message.protocolMessage?.type === index_js_1.proto.Message.ProtocolMessage.Type.REVOKE) {
|
|
526
|
+
msgId = message.protocolMessage.key?.id;
|
|
527
|
+
message = {};
|
|
528
|
+
}
|
|
529
|
+
// ── Newsletter Button Compatibility Patch ──────────────────────
|
|
530
|
+
// interactiveMessage (quick_reply / single_select / cta_url) bisa
|
|
531
|
+
// dikirim ke newsletter langsung — WA menerima via proto encoding.
|
|
532
|
+
// listMessage & buttonsMessage dikonversi ke interactiveMessage
|
|
533
|
+
// supaya konsisten dengan cara bot menulis pesan.
|
|
534
|
+
// ──────────────────────────────────────────────────────────────
|
|
535
|
+
if (message.listMessage) {
|
|
536
|
+
const list = message.listMessage;
|
|
537
|
+
message = {
|
|
538
|
+
interactiveMessage: {
|
|
539
|
+
nativeFlowMessage: {
|
|
540
|
+
buttons: [{
|
|
541
|
+
name: 'single_select',
|
|
542
|
+
buttonParamsJson: JSON.stringify({
|
|
543
|
+
title: list.buttonText || 'Select',
|
|
544
|
+
sections: (list.sections || []).map(sec => ({
|
|
545
|
+
title: sec.title || '',
|
|
546
|
+
highlight_label: '',
|
|
547
|
+
rows: (sec.rows || []).map(row => ({
|
|
548
|
+
header: '',
|
|
549
|
+
title: row.title || '',
|
|
550
|
+
description: row.description || '',
|
|
551
|
+
id: row.rowId || row.id || ''
|
|
552
|
+
}))
|
|
553
|
+
}))
|
|
554
|
+
})
|
|
555
|
+
}],
|
|
556
|
+
messageParamsJson: '',
|
|
557
|
+
messageVersion: 1
|
|
558
|
+
},
|
|
559
|
+
body: { text: list.description || '' },
|
|
560
|
+
...(list.footerText ? { footer: { text: list.footerText } } : {}),
|
|
561
|
+
...(list.title ? { header: { title: list.title, hasMediaAttachment: false, subtitle: '' } } : {})
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
else if (message.buttonsMessage) {
|
|
566
|
+
const bMsg = message.buttonsMessage;
|
|
567
|
+
message = {
|
|
568
|
+
interactiveMessage: {
|
|
569
|
+
nativeFlowMessage: {
|
|
570
|
+
buttons: (bMsg.buttons || []).map(btn => ({
|
|
571
|
+
name: 'quick_reply',
|
|
572
|
+
buttonParamsJson: JSON.stringify({
|
|
573
|
+
display_text: btn.buttonText?.displayText || btn.buttonText || '',
|
|
574
|
+
id: btn.buttonId || btn.buttonText?.displayText || ''
|
|
575
|
+
})
|
|
576
|
+
})),
|
|
577
|
+
messageParamsJson: '',
|
|
578
|
+
messageVersion: 1
|
|
579
|
+
},
|
|
580
|
+
body: { text: bMsg.contentText || bMsg.text || '' },
|
|
581
|
+
...(bMsg.footerText ? { footer: { text: bMsg.footerText } } : {}),
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
// ── End Newsletter Button Compatibility Patch ──────────────────
|
|
586
|
+
const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
|
|
587
|
+
if (Array.isArray(patched)) {
|
|
588
|
+
throw new Error('Per-jid patching is not supported in channel');
|
|
589
|
+
}
|
|
590
|
+
const bytes = (0, index_js_3.encodeNewsletterMessage)(patched);
|
|
591
|
+
// Set mediatype for interactive messages
|
|
592
|
+
if (patched.interactiveMessage && !extraAttrs['mediatype']) {
|
|
593
|
+
extraAttrs['mediatype'] = 'interactive';
|
|
594
|
+
}
|
|
595
|
+
// extraAttrs already has mediatype set above if media message
|
|
596
|
+
binaryNodeContent.push({
|
|
597
|
+
tag: 'plaintext',
|
|
598
|
+
attrs: extraAttrs,
|
|
599
|
+
content: bytes
|
|
600
|
+
});
|
|
601
|
+
logger.debug({ msgId, extraAttrs }, `sending newsletter message to ${jid}`);
|
|
602
|
+
const stanza = {
|
|
603
|
+
tag: 'message',
|
|
604
|
+
attrs: {
|
|
605
|
+
to: jid,
|
|
606
|
+
id: msgId,
|
|
607
|
+
type: getMessageType(message),
|
|
608
|
+
...(additionalAttributes || {})
|
|
609
|
+
},
|
|
610
|
+
content: binaryNodeContent
|
|
611
|
+
};
|
|
612
|
+
await sendNode(stanza);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if ((0, index_js_3.normalizeMessageContent)(message)?.pinInChatMessage || (0, index_js_3.normalizeMessageContent)(message)?.reactionMessage) {
|
|
616
|
+
extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
|
|
358
617
|
}
|
|
359
|
-
if (
|
|
618
|
+
if (isGroupOrStatus && !isRetryResend) {
|
|
360
619
|
const [groupData, senderKeyMap] = await Promise.all([
|
|
361
620
|
(async () => {
|
|
362
|
-
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
|
|
363
|
-
if (groupData && Array.isArray(groupData
|
|
621
|
+
let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined; // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
|
|
622
|
+
if (groupData && Array.isArray(groupData?.participants)) {
|
|
364
623
|
logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
|
|
365
624
|
}
|
|
366
625
|
else if (!isStatus) {
|
|
367
|
-
groupData = await groupMetadata(jid);
|
|
626
|
+
groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
|
|
368
627
|
}
|
|
369
628
|
return groupData;
|
|
370
629
|
})(),
|
|
371
630
|
(async () => {
|
|
372
631
|
if (!participant && !isStatus) {
|
|
373
|
-
|
|
632
|
+
// what if sender memory is less accurate than the cached metadata
|
|
633
|
+
// on participant change in group, we should do sender memory manipulation
|
|
634
|
+
const result = await authState.keys.get('sender-key-memory', [jid]); // TODO: check out what if the sender key memory doesn't include the LID stuff now?
|
|
374
635
|
return result[jid] || {};
|
|
375
636
|
}
|
|
376
637
|
return {};
|
|
377
638
|
})()
|
|
378
639
|
]);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
640
|
+
const participantsList = groupData ? groupData.participants.map(p => p.id) : [];
|
|
641
|
+
if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
|
|
642
|
+
additionalAttributes = {
|
|
643
|
+
...additionalAttributes,
|
|
644
|
+
expiration: groupData.ephemeralDuration.toString()
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (isStatus && statusJidList) {
|
|
648
|
+
participantsList.push(...statusJidList);
|
|
649
|
+
}
|
|
650
|
+
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
651
|
+
devices.push(...additionalDevices);
|
|
652
|
+
if (isGroup) {
|
|
653
|
+
additionalAttributes = {
|
|
654
|
+
...additionalAttributes,
|
|
655
|
+
addressing_mode: groupData?.addressingMode || 'lid'
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
// Ensure groupStatusMessageV2 always has messageContextInfo.messageSecret
|
|
659
|
+
if (message?.groupStatusMessageV2 && !message?.messageContextInfo?.messageSecret) {
|
|
660
|
+
const { randomBytes } = await Promise.resolve().then(() => __importStar(require('node:crypto')));
|
|
661
|
+
message = {
|
|
662
|
+
...message,
|
|
663
|
+
messageContextInfo: {
|
|
664
|
+
...(message.messageContextInfo || {}),
|
|
665
|
+
messageSecret: randomBytes(32)
|
|
666
|
+
},
|
|
667
|
+
groupStatusMessageV2: {
|
|
668
|
+
...message.groupStatusMessageV2,
|
|
669
|
+
message: {
|
|
670
|
+
...(message.groupStatusMessageV2.message || {}),
|
|
671
|
+
messageContextInfo: {
|
|
672
|
+
...(message.groupStatusMessageV2.message?.messageContextInfo || {}),
|
|
673
|
+
messageSecret: message.messageContextInfo?.messageSecret || randomBytes(32)
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
// ── Group Interactive Message Compatibility Patch ──────────────
|
|
680
|
+
// WhatsApp groups do NOT render listMessage, buttonsMessage, or
|
|
681
|
+
// templateMessage natively in MD protocol. All must be converted
|
|
682
|
+
// to interactiveMessage with nativeFlowMessage so they display
|
|
683
|
+
// and are interactive in group chats.
|
|
684
|
+
// ──────────────────────────────────────────────────────────────
|
|
685
|
+
// 1. listMessage → interactiveMessage single_select
|
|
686
|
+
if (message.listMessage) {
|
|
687
|
+
const list = message.listMessage;
|
|
688
|
+
const interactiveMessage = {
|
|
689
|
+
nativeFlowMessage: {
|
|
690
|
+
buttons: [{
|
|
691
|
+
name: 'single_select',
|
|
692
|
+
buttonParamsJson: JSON.stringify({
|
|
693
|
+
title: list.buttonText || 'Select',
|
|
694
|
+
sections: (list.sections || []).map(section => ({
|
|
695
|
+
title: section.title || '',
|
|
696
|
+
highlight_label: '',
|
|
697
|
+
rows: (section.rows || []).map(row => ({
|
|
698
|
+
header: '',
|
|
699
|
+
title: row.title || '',
|
|
700
|
+
description: row.description || '',
|
|
701
|
+
id: row.rowId || row.id || ''
|
|
702
|
+
}))
|
|
703
|
+
}))
|
|
704
|
+
})
|
|
705
|
+
}],
|
|
706
|
+
messageParamsJson: '',
|
|
707
|
+
messageVersion: 1
|
|
708
|
+
},
|
|
709
|
+
body: { text: list.description || '' },
|
|
710
|
+
footer: list.footerText ? { text: list.footerText } : undefined,
|
|
711
|
+
header: list.title ? { title: list.title, hasMediaAttachment: false, subtitle: '' } : undefined,
|
|
712
|
+
contextInfo: list.contextInfo
|
|
713
|
+
};
|
|
714
|
+
message = { interactiveMessage };
|
|
715
|
+
}
|
|
716
|
+
// 2. buttonsMessage → interactiveMessage nativeFlowMessage quick_reply buttons
|
|
717
|
+
else if (message.buttonsMessage) {
|
|
718
|
+
const bMsg = message.buttonsMessage;
|
|
719
|
+
const buttons = (bMsg.buttons || []).map(btn => ({
|
|
720
|
+
name: 'quick_reply',
|
|
721
|
+
buttonParamsJson: JSON.stringify({
|
|
722
|
+
display_text: btn.buttonText?.displayText || btn.buttonText || '',
|
|
723
|
+
id: btn.buttonId || btn.buttonText?.displayText || ''
|
|
724
|
+
})
|
|
725
|
+
}));
|
|
726
|
+
const interactiveMessage = {
|
|
727
|
+
nativeFlowMessage: {
|
|
728
|
+
buttons,
|
|
729
|
+
messageParamsJson: '',
|
|
730
|
+
messageVersion: 1
|
|
731
|
+
},
|
|
732
|
+
body: { text: bMsg.contentText || bMsg.text || '' },
|
|
733
|
+
footer: bMsg.footerText ? { text: bMsg.footerText } : undefined,
|
|
734
|
+
header: bMsg.text
|
|
735
|
+
? { title: bMsg.text, hasMediaAttachment: false, subtitle: '' }
|
|
736
|
+
: (bMsg.imageMessage || bMsg.videoMessage || bMsg.documentMessage
|
|
737
|
+
? { hasMediaAttachment: true, ...(bMsg.imageMessage ? { imageMessage: bMsg.imageMessage } : {}), ...(bMsg.videoMessage ? { videoMessage: bMsg.videoMessage } : {}) }
|
|
738
|
+
: undefined),
|
|
739
|
+
contextInfo: bMsg.contextInfo
|
|
740
|
+
};
|
|
741
|
+
message = { interactiveMessage };
|
|
742
|
+
}
|
|
743
|
+
// 3. templateMessage (hydratedFourRowTemplate) → interactiveMessage nativeFlowMessage
|
|
744
|
+
else if (message.templateMessage) {
|
|
745
|
+
const tmpl = message.templateMessage.hydratedTemplate || message.templateMessage.fourRowTemplate;
|
|
746
|
+
if (tmpl) {
|
|
747
|
+
const hydratedButtons = tmpl.hydratedButtons || [];
|
|
748
|
+
const buttons = hydratedButtons.map(hBtn => {
|
|
749
|
+
if (hBtn.quickReplyButton) {
|
|
750
|
+
return {
|
|
751
|
+
name: 'quick_reply',
|
|
752
|
+
buttonParamsJson: JSON.stringify({
|
|
753
|
+
display_text: hBtn.quickReplyButton.displayText || '',
|
|
754
|
+
id: hBtn.quickReplyButton.id || hBtn.quickReplyButton.displayText || ''
|
|
755
|
+
})
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
else if (hBtn.urlButton) {
|
|
759
|
+
return {
|
|
760
|
+
name: 'cta_url',
|
|
761
|
+
buttonParamsJson: JSON.stringify({
|
|
762
|
+
display_text: hBtn.urlButton.displayText || '',
|
|
763
|
+
url: hBtn.urlButton.url || '',
|
|
764
|
+
merchant_url: hBtn.urlButton.url || ''
|
|
765
|
+
})
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
else if (hBtn.callButton) {
|
|
769
|
+
return {
|
|
770
|
+
name: 'cta_call',
|
|
771
|
+
buttonParamsJson: JSON.stringify({
|
|
772
|
+
display_text: hBtn.callButton.displayText || '',
|
|
773
|
+
phone_number: hBtn.callButton.phoneNumber || ''
|
|
774
|
+
})
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
return null;
|
|
778
|
+
}).filter(Boolean);
|
|
779
|
+
const interactiveMessage = {
|
|
780
|
+
nativeFlowMessage: {
|
|
781
|
+
buttons,
|
|
782
|
+
messageParamsJson: '',
|
|
783
|
+
messageVersion: 1
|
|
784
|
+
},
|
|
785
|
+
body: { text: tmpl.hydratedContentText || tmpl.contentText || '' },
|
|
786
|
+
footer: tmpl.hydratedFooterText ? { text: tmpl.hydratedFooterText } : undefined,
|
|
787
|
+
header: tmpl.hydratedTitleText
|
|
788
|
+
? { title: tmpl.hydratedTitleText, hasMediaAttachment: false, subtitle: '' }
|
|
789
|
+
: (tmpl.imageMessage || tmpl.videoMessage || tmpl.documentMessage
|
|
790
|
+
? { hasMediaAttachment: true, ...(tmpl.imageMessage ? { imageMessage: tmpl.imageMessage } : {}), ...(tmpl.videoMessage ? { videoMessage: tmpl.videoMessage } : {}) }
|
|
791
|
+
: undefined),
|
|
792
|
+
contextInfo: tmpl.contextInfo
|
|
389
793
|
};
|
|
794
|
+
message = { interactiveMessage };
|
|
390
795
|
}
|
|
391
|
-
const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
|
|
392
|
-
devices.push(...additionalDevices);
|
|
393
796
|
}
|
|
797
|
+
// ── End Group Interactive Message Compatibility Patch ──────────
|
|
394
798
|
const patched = await patchMessageBeforeSending(message);
|
|
395
799
|
if (Array.isArray(patched)) {
|
|
396
800
|
throw new boom_1.Boom('Per-jid patching is not supported in groups');
|
|
397
801
|
}
|
|
398
|
-
const bytes = (0,
|
|
802
|
+
const bytes = (0, index_js_3.encodeWAMessage)(patched);
|
|
803
|
+
reportingMessage = patched;
|
|
804
|
+
const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
|
|
805
|
+
const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
|
|
399
806
|
const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
|
|
400
807
|
group: destinationJid,
|
|
401
808
|
data: bytes,
|
|
402
|
-
meId
|
|
809
|
+
meId: groupSenderIdentity
|
|
403
810
|
});
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const
|
|
408
|
-
if (!
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
811
|
+
const senderKeyRecipients = [];
|
|
812
|
+
for (const device of devices) {
|
|
813
|
+
const deviceJid = device.jid;
|
|
814
|
+
const hasKey = !!senderKeyMap[deviceJid];
|
|
815
|
+
if ((!hasKey || !!participant) &&
|
|
816
|
+
!(0, index_js_4.isHostedLidUser)(deviceJid) &&
|
|
817
|
+
!(0, index_js_4.isHostedPnUser)(deviceJid) &&
|
|
818
|
+
device.device !== 99) {
|
|
819
|
+
//todo: revamp all this logic
|
|
820
|
+
// the goal is to follow with what I said above for each group, and instead of a true false map of ids, we can set an array full of those the app has already sent pkmsgs
|
|
821
|
+
senderKeyRecipients.push(deviceJid);
|
|
822
|
+
senderKeyMap[deviceJid] = true;
|
|
412
823
|
}
|
|
413
824
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (senderKeyJids.length) {
|
|
417
|
-
logger.debug({ senderKeyJids }, 'sending new sender key');
|
|
825
|
+
if (senderKeyRecipients.length) {
|
|
826
|
+
logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
|
|
418
827
|
const senderKeyMsg = {
|
|
419
828
|
senderKeyDistributionMessage: {
|
|
420
829
|
axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
|
|
421
830
|
groupId: destinationJid
|
|
422
831
|
}
|
|
423
832
|
};
|
|
424
|
-
|
|
425
|
-
|
|
833
|
+
const senderKeySessionTargets = senderKeyRecipients;
|
|
834
|
+
await assertSessions(senderKeySessionTargets);
|
|
835
|
+
const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
|
|
426
836
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
|
|
427
837
|
participants.push(...result.nodes);
|
|
428
838
|
}
|
|
429
839
|
binaryNodeContent.push({
|
|
430
840
|
tag: 'enc',
|
|
431
|
-
attrs: { v: '2', type: 'skmsg' },
|
|
841
|
+
attrs: { v: '2', type: 'skmsg', ...extraAttrs },
|
|
432
842
|
content: ciphertext
|
|
433
843
|
});
|
|
434
844
|
await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
|
|
435
845
|
}
|
|
436
|
-
else
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (((_d = message.protocolMessage) === null || _d === void 0 ? void 0 : _d.type) === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
|
|
444
|
-
msgId = (_e = message.protocolMessage.key) === null || _e === void 0 ? void 0 : _e.id;
|
|
445
|
-
message = {};
|
|
846
|
+
else {
|
|
847
|
+
// ADDRESSING CONSISTENCY: Match own identity to conversation context
|
|
848
|
+
// TODO: investigate if this is true
|
|
849
|
+
let ownId = meId;
|
|
850
|
+
if (isLid && meLid) {
|
|
851
|
+
ownId = meLid;
|
|
852
|
+
logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
|
|
446
853
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
throw new boom_1.Boom('Per-jid patching is not supported in channel');
|
|
854
|
+
else {
|
|
855
|
+
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
|
|
450
856
|
}
|
|
451
|
-
const
|
|
452
|
-
binaryNodeContent.push({
|
|
453
|
-
tag: 'plaintext',
|
|
454
|
-
attrs: mediaType ? { mediatype: mediaType } : {},
|
|
455
|
-
content: bytes
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
const { user: meUser } = (0, WABinary_1.jidDecode)(meId);
|
|
857
|
+
const { user: ownUser } = (0, index_js_4.jidDecode)(ownId);
|
|
460
858
|
if (!participant) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
859
|
+
const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
|
|
860
|
+
reportingMessage = Array.isArray(patchedForReporting)
|
|
861
|
+
? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
|
|
862
|
+
: patchedForReporting;
|
|
863
|
+
}
|
|
864
|
+
if (!isRetryResend) {
|
|
865
|
+
const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
866
|
+
devices.push({
|
|
867
|
+
user,
|
|
868
|
+
device: 0,
|
|
869
|
+
jid: (0, index_js_4.jidEncode)(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
|
|
870
|
+
});
|
|
871
|
+
if (user !== ownUser) {
|
|
872
|
+
const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
|
|
873
|
+
const ownUserForAddressing = isLid && meLid ? (0, index_js_4.jidDecode)(meLid).user : (0, index_js_4.jidDecode)(meId).user;
|
|
874
|
+
devices.push({
|
|
875
|
+
user: ownUserForAddressing,
|
|
876
|
+
device: 0,
|
|
877
|
+
jid: (0, index_js_4.jidEncode)(ownUserForAddressing, ownUserServer, 0)
|
|
878
|
+
});
|
|
464
879
|
}
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
devices.
|
|
880
|
+
if (additionalAttributes?.['category'] !== 'peer') {
|
|
881
|
+
// Clear placeholders and enumerate actual devices
|
|
882
|
+
devices.length = 0;
|
|
883
|
+
// Use conversation-appropriate sender identity
|
|
884
|
+
const senderIdentity = isLid && meLid
|
|
885
|
+
? (0, index_js_4.jidEncode)((0, index_js_4.jidDecode)(meLid)?.user, 'lid', undefined)
|
|
886
|
+
: (0, index_js_4.jidEncode)((0, index_js_4.jidDecode)(meId)?.user, 's.whatsapp.net', undefined);
|
|
887
|
+
// Enumerate devices for sender and target with consistent addressing
|
|
888
|
+
const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
|
|
889
|
+
devices.push(...sessionDevices);
|
|
890
|
+
logger.debug({
|
|
891
|
+
deviceCount: devices.length,
|
|
892
|
+
devices: devices.map(d => `${d.user}:${d.device}@${(0, index_js_4.jidDecode)(d.jid)?.server}`)
|
|
893
|
+
}, 'Device enumeration complete with unified addressing');
|
|
468
894
|
}
|
|
469
895
|
}
|
|
470
|
-
const
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
896
|
+
const allRecipients = [];
|
|
897
|
+
const meRecipients = [];
|
|
898
|
+
const otherRecipients = [];
|
|
899
|
+
const { user: mePnUser } = (0, index_js_4.jidDecode)(meId);
|
|
900
|
+
const { user: meLidUser } = meLid ? (0, index_js_4.jidDecode)(meLid) : { user: null };
|
|
901
|
+
for (const { user, jid } of devices) {
|
|
902
|
+
const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
|
|
903
|
+
if (isExactSenderDevice) {
|
|
904
|
+
logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
// Check if this is our device (could match either PN or LID user)
|
|
908
|
+
const isMe = user === mePnUser || user === meLidUser;
|
|
476
909
|
if (isMe) {
|
|
477
|
-
|
|
910
|
+
meRecipients.push(jid);
|
|
478
911
|
}
|
|
479
912
|
else {
|
|
480
|
-
|
|
913
|
+
otherRecipients.push(jid);
|
|
481
914
|
}
|
|
482
|
-
|
|
915
|
+
allRecipients.push(jid);
|
|
483
916
|
}
|
|
484
|
-
await assertSessions(
|
|
917
|
+
await assertSessions(allRecipients);
|
|
485
918
|
const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
|
|
486
|
-
|
|
487
|
-
createParticipantNodes(
|
|
919
|
+
// For own devices: use DSM if available (1:1 chats only)
|
|
920
|
+
createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
|
|
921
|
+
createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
|
|
488
922
|
]);
|
|
489
923
|
participants.push(...meNodes);
|
|
490
924
|
participants.push(...otherNodes);
|
|
925
|
+
if (meRecipients.length > 0 || otherRecipients.length > 0) {
|
|
926
|
+
extraAttrs['phash'] = (0, index_js_3.generateParticipantHashV2)([...meRecipients, ...otherRecipients]);
|
|
927
|
+
}
|
|
491
928
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
|
|
492
929
|
}
|
|
930
|
+
if (isRetryResend) {
|
|
931
|
+
const isParticipantLid = (0, index_js_4.isLidUser)(participant.jid);
|
|
932
|
+
const isMe = (0, index_js_4.areJidsSameUser)(participant.jid, isParticipantLid ? meLid : meId);
|
|
933
|
+
const encodedMessageToSend = isMe
|
|
934
|
+
? (0, index_js_3.encodeWAMessage)({
|
|
935
|
+
deviceSentMessage: {
|
|
936
|
+
destinationJid,
|
|
937
|
+
message
|
|
938
|
+
}
|
|
939
|
+
})
|
|
940
|
+
: (0, index_js_3.encodeWAMessage)(message);
|
|
941
|
+
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
942
|
+
data: encodedMessageToSend,
|
|
943
|
+
jid: participant.jid
|
|
944
|
+
});
|
|
945
|
+
binaryNodeContent.push({
|
|
946
|
+
tag: 'enc',
|
|
947
|
+
attrs: {
|
|
948
|
+
v: '2',
|
|
949
|
+
type,
|
|
950
|
+
count: participant.count.toString()
|
|
951
|
+
},
|
|
952
|
+
content: encryptedContent
|
|
953
|
+
});
|
|
954
|
+
}
|
|
493
955
|
if (participants.length) {
|
|
494
|
-
if (
|
|
495
|
-
const peerNode =
|
|
956
|
+
if (additionalAttributes?.['category'] === 'peer') {
|
|
957
|
+
const peerNode = participants[0]?.content?.[0];
|
|
496
958
|
if (peerNode) {
|
|
497
959
|
binaryNodeContent.push(peerNode); // push only enc
|
|
498
960
|
}
|
|
@@ -509,7 +971,8 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
509
971
|
tag: 'message',
|
|
510
972
|
attrs: {
|
|
511
973
|
id: msgId,
|
|
512
|
-
|
|
974
|
+
to: destinationJid,
|
|
975
|
+
type: getMessageType(message),
|
|
513
976
|
...(additionalAttributes || {})
|
|
514
977
|
},
|
|
515
978
|
content: binaryNodeContent
|
|
@@ -518,11 +981,11 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
518
981
|
// ensure the message is only sent to that person
|
|
519
982
|
// if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
|
|
520
983
|
if (participant) {
|
|
521
|
-
if ((0,
|
|
984
|
+
if ((0, index_js_4.isJidGroup)(destinationJid)) {
|
|
522
985
|
stanza.attrs.to = destinationJid;
|
|
523
986
|
stanza.attrs.participant = participant.jid;
|
|
524
987
|
}
|
|
525
|
-
else if ((0,
|
|
988
|
+
else if ((0, index_js_4.areJidsSameUser)(participant.jid, meId)) {
|
|
526
989
|
stanza.attrs.to = participant.jid;
|
|
527
990
|
stanza.attrs.recipient = destinationJid;
|
|
528
991
|
}
|
|
@@ -534,80 +997,102 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
534
997
|
stanza.attrs.to = destinationJid;
|
|
535
998
|
}
|
|
536
999
|
if (shouldIncludeDeviceIdentity) {
|
|
1000
|
+
;
|
|
537
1001
|
stanza.content.push({
|
|
538
1002
|
tag: 'device-identity',
|
|
539
1003
|
attrs: {},
|
|
540
|
-
content: (0,
|
|
1004
|
+
content: (0, index_js_3.encodeSignedDeviceIdentity)(authState.creds.account, true)
|
|
541
1005
|
});
|
|
542
1006
|
logger.debug({ jid }, 'adding device identity');
|
|
543
1007
|
}
|
|
544
|
-
if (
|
|
545
|
-
|
|
1008
|
+
if (!isNewsletter &&
|
|
1009
|
+
!isRetryResend &&
|
|
1010
|
+
reportingMessage?.messageContextInfo?.messageSecret &&
|
|
1011
|
+
(0, reporting_utils_js_1.shouldIncludeReportingToken)(reportingMessage)) {
|
|
1012
|
+
try {
|
|
1013
|
+
const encoded = (0, index_js_3.encodeWAMessage)(reportingMessage);
|
|
1014
|
+
const reportingKey = {
|
|
1015
|
+
id: msgId,
|
|
1016
|
+
fromMe: true,
|
|
1017
|
+
remoteJid: destinationJid,
|
|
1018
|
+
participant: participant?.jid
|
|
1019
|
+
};
|
|
1020
|
+
const reportingNode = await (0, reporting_utils_js_1.getMessageReportingToken)(encoded, reportingMessage, reportingKey);
|
|
1021
|
+
if (reportingNode) {
|
|
1022
|
+
;
|
|
1023
|
+
stanza.content.push(reportingNode);
|
|
1024
|
+
logger.trace({ jid }, 'added reporting token to message');
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
|
|
1029
|
+
}
|
|
546
1030
|
}
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
if (
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
tag: 'list',
|
|
570
|
-
attrs: {
|
|
571
|
-
type: 'product_list',
|
|
572
|
-
v: '2'
|
|
573
|
-
}
|
|
574
|
-
}];
|
|
1031
|
+
const contactTcTokenData = !isGroup && !isRetryResend && !isStatus ? await authState.keys.get('tctoken', [destinationJid]) : {};
|
|
1032
|
+
const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
|
|
1033
|
+
if (tcTokenBuffer) {
|
|
1034
|
+
;
|
|
1035
|
+
stanza.content.push({
|
|
1036
|
+
tag: 'tctoken',
|
|
1037
|
+
attrs: {},
|
|
1038
|
+
content: tcTokenBuffer
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
if (AI && !isGroup && !isStatus && !isNewsletter) {
|
|
1042
|
+
const existingBizBot = (0, index_js_4.getBinaryFilteredBizBot)(additionalNodes || []);
|
|
1043
|
+
if (existingBizBot) {
|
|
1044
|
+
if (additionalNodes && additionalNodes.length > 0) {
|
|
1045
|
+
stanza.content.push(...additionalNodes);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } });
|
|
1050
|
+
if (additionalNodes && additionalNodes.length > 0) {
|
|
1051
|
+
stanza.content.push(...additionalNodes);
|
|
1052
|
+
}
|
|
575
1053
|
}
|
|
576
|
-
|
|
1054
|
+
}
|
|
1055
|
+
else if (additionalNodes && additionalNodes.length > 0) {
|
|
1056
|
+
;
|
|
1057
|
+
stanza.content.push(...additionalNodes);
|
|
577
1058
|
}
|
|
578
1059
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
579
1060
|
await sendNode(stanza);
|
|
580
|
-
|
|
1061
|
+
// Add message to retry cache if enabled
|
|
1062
|
+
if (messageRetryManager && !participant) {
|
|
1063
|
+
messageRetryManager.addRecentMessage(destinationJid, msgId, message);
|
|
1064
|
+
}
|
|
1065
|
+
}, meId);
|
|
581
1066
|
return msgId;
|
|
582
1067
|
};
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
return getTypeMessage(msg.viewOnceMessageV2.message);
|
|
589
|
-
}
|
|
590
|
-
else if (msg.viewOnceMessageV2Extension) {
|
|
591
|
-
return getTypeMessage(msg.viewOnceMessageV2Extension.message);
|
|
592
|
-
}
|
|
593
|
-
else if (msg.ephemeralMessage) {
|
|
594
|
-
return getTypeMessage(msg.ephemeralMessage.message);
|
|
595
|
-
}
|
|
596
|
-
else if (msg.documentWithCaptionMessage) {
|
|
597
|
-
return getTypeMessage(msg.documentWithCaptionMessage.message);
|
|
1068
|
+
const getMessageType = (message) => {
|
|
1069
|
+
// groupStatusMessageV2 must be checked BEFORE normalizeMessageContent
|
|
1070
|
+
// because normalizeMessageContent will unwrap it into inner message
|
|
1071
|
+
if (message?.groupStatusMessageV2 || message?.groupStatusMessage) {
|
|
1072
|
+
return 'text';
|
|
598
1073
|
}
|
|
599
|
-
|
|
1074
|
+
const normalizedMessage = (0, index_js_3.normalizeMessageContent)(message);
|
|
1075
|
+
if (!normalizedMessage)
|
|
1076
|
+
return 'text';
|
|
1077
|
+
if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
|
|
600
1078
|
return 'reaction';
|
|
601
1079
|
}
|
|
602
|
-
|
|
1080
|
+
if (normalizedMessage.pollCreationMessage ||
|
|
1081
|
+
normalizedMessage.pollCreationMessageV2 ||
|
|
1082
|
+
normalizedMessage.pollCreationMessageV3 ||
|
|
1083
|
+
normalizedMessage.pollUpdateMessage) {
|
|
603
1084
|
return 'poll';
|
|
604
1085
|
}
|
|
605
|
-
|
|
606
|
-
return '
|
|
1086
|
+
if (normalizedMessage.eventMessage) {
|
|
1087
|
+
return 'event';
|
|
607
1088
|
}
|
|
608
|
-
|
|
1089
|
+
if (normalizedMessage.interactiveMessage) {
|
|
609
1090
|
return 'text';
|
|
610
1091
|
}
|
|
1092
|
+
if (getMediaType(normalizedMessage) !== '') {
|
|
1093
|
+
return 'media';
|
|
1094
|
+
}
|
|
1095
|
+
return 'text';
|
|
611
1096
|
};
|
|
612
1097
|
const getMediaType = (message) => {
|
|
613
1098
|
if (message.imageMessage) {
|
|
@@ -616,6 +1101,9 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
616
1101
|
else if (message.videoMessage) {
|
|
617
1102
|
return message.videoMessage.gifPlayback ? 'gif' : 'video';
|
|
618
1103
|
}
|
|
1104
|
+
else if (message.ptvMessage) {
|
|
1105
|
+
return 'video';
|
|
1106
|
+
}
|
|
619
1107
|
else if (message.audioMessage) {
|
|
620
1108
|
return message.audioMessage.ptt ? 'ptt' : 'audio';
|
|
621
1109
|
}
|
|
@@ -658,13 +1146,14 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
658
1146
|
else if (message.groupInviteMessage) {
|
|
659
1147
|
return 'url';
|
|
660
1148
|
}
|
|
1149
|
+
return '';
|
|
661
1150
|
};
|
|
662
1151
|
const getPrivacyTokens = async (jids) => {
|
|
663
|
-
const t = (0,
|
|
1152
|
+
const t = (0, index_js_3.unixTimestampSeconds)().toString();
|
|
664
1153
|
const result = await query({
|
|
665
1154
|
tag: 'iq',
|
|
666
1155
|
attrs: {
|
|
667
|
-
to:
|
|
1156
|
+
to: index_js_4.S_WHATSAPP_NET,
|
|
668
1157
|
type: 'set',
|
|
669
1158
|
xmlns: 'privacy'
|
|
670
1159
|
},
|
|
@@ -675,7 +1164,7 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
675
1164
|
content: jids.map(jid => ({
|
|
676
1165
|
tag: 'token',
|
|
677
1166
|
attrs: {
|
|
678
|
-
jid: (0,
|
|
1167
|
+
jid: (0, index_js_4.jidNormalizedUser)(jid),
|
|
679
1168
|
t,
|
|
680
1169
|
type: 'trusted_contact'
|
|
681
1170
|
}
|
|
@@ -685,8 +1174,78 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
685
1174
|
});
|
|
686
1175
|
return result;
|
|
687
1176
|
};
|
|
688
|
-
const waUploadToServer = (0,
|
|
689
|
-
const waitForMsgMediaUpdate = (0,
|
|
1177
|
+
const waUploadToServer = (0, index_js_3.getWAUploadToServer)(config, refreshMediaConn);
|
|
1178
|
+
const waitForMsgMediaUpdate = (0, index_js_3.bindWaitForEvent)(ev, 'messages.media-update');
|
|
1179
|
+
// ── Button type helpers (ported from itsukichan) ──────────────────────────
|
|
1180
|
+
const getButtonType = (message) => {
|
|
1181
|
+
if (message.listMessage)
|
|
1182
|
+
return 'list';
|
|
1183
|
+
if (message.buttonsMessage)
|
|
1184
|
+
return 'buttons';
|
|
1185
|
+
if (message.templateMessage)
|
|
1186
|
+
return 'template';
|
|
1187
|
+
if (message.interactiveMessage?.nativeFlowMessage)
|
|
1188
|
+
return 'native_flow';
|
|
1189
|
+
if (message.interactiveMessage?.shopStorefrontMessage)
|
|
1190
|
+
return 'shop';
|
|
1191
|
+
if (message.interactiveMessage?.collectionMessage)
|
|
1192
|
+
return 'collection';
|
|
1193
|
+
if (message.interactiveMessage?.carouselMessage)
|
|
1194
|
+
return 'carousel';
|
|
1195
|
+
if (message.interactiveMessage)
|
|
1196
|
+
return 'interactive';
|
|
1197
|
+
return null;
|
|
1198
|
+
};
|
|
1199
|
+
const getButtonArgs = (message) => {
|
|
1200
|
+
const nativeFlow = message.interactiveMessage?.nativeFlowMessage;
|
|
1201
|
+
const firstButtonName = nativeFlow?.buttons?.[0]?.name;
|
|
1202
|
+
const nativeFlowSpecials = [
|
|
1203
|
+
'mpm', 'cta_catalog', 'send_location',
|
|
1204
|
+
'call_permission_request', 'wa_payment_transaction_details',
|
|
1205
|
+
'automated_greeting_message_view_catalog'
|
|
1206
|
+
];
|
|
1207
|
+
const ts = (0, index_js_3.unixTimestampSeconds)().toString();
|
|
1208
|
+
const bizBase = { actual_actors: '2', host_storage: '2', privacy_mode_ts: ts };
|
|
1209
|
+
const qualityControl = { tag: 'quality_control', attrs: { source_type: 'third_party' } };
|
|
1210
|
+
if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
|
|
1211
|
+
return {
|
|
1212
|
+
tag: 'biz',
|
|
1213
|
+
attrs: { native_flow_name: firstButtonName === 'review_and_pay' ? 'order_details' : firstButtonName }
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
|
|
1217
|
+
return {
|
|
1218
|
+
tag: 'biz', attrs: bizBase,
|
|
1219
|
+
content: [{
|
|
1220
|
+
tag: 'interactive', attrs: { type: 'native_flow', v: '1' },
|
|
1221
|
+
content: [{ tag: 'native_flow', attrs: { v: '2', name: firstButtonName } }]
|
|
1222
|
+
}, qualityControl]
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
else if (nativeFlow || message.buttonsMessage || message.templateMessage || message.interactiveMessage) {
|
|
1226
|
+
// All interactive types in groups use native_flow biz node
|
|
1227
|
+
// This covers: nativeFlowMessage, buttonsMessage→interactiveMessage,
|
|
1228
|
+
// templateMessage→interactiveMessage, and direct interactiveMessage
|
|
1229
|
+
return {
|
|
1230
|
+
tag: 'biz', attrs: bizBase,
|
|
1231
|
+
content: [{
|
|
1232
|
+
tag: 'interactive', attrs: { type: 'native_flow', v: '1' },
|
|
1233
|
+
content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }]
|
|
1234
|
+
}, qualityControl]
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
else if (message.listMessage) {
|
|
1238
|
+
// Standalone listMessage (private chat) keeps the list biz node
|
|
1239
|
+
return {
|
|
1240
|
+
tag: 'biz', attrs: bizBase,
|
|
1241
|
+
content: [{ tag: 'list', attrs: { v: '2', type: 'product_list' } }, qualityControl]
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
else {
|
|
1245
|
+
return { tag: 'biz', attrs: bizBase };
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
// ── End button type helpers ───────────────────────────────────────────────
|
|
690
1249
|
return {
|
|
691
1250
|
...sock,
|
|
692
1251
|
getPrivacyTokens,
|
|
@@ -698,14 +1257,16 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
698
1257
|
refreshMediaConn,
|
|
699
1258
|
waUploadToServer,
|
|
700
1259
|
fetchPrivacySettings,
|
|
701
|
-
getUSyncDevices,
|
|
702
|
-
createParticipantNodes,
|
|
703
1260
|
sendPeerDataOperationMessage,
|
|
1261
|
+
createParticipantNodes,
|
|
1262
|
+
getUSyncDevices,
|
|
1263
|
+
messageRetryManager,
|
|
1264
|
+
updateMemberLabel,
|
|
704
1265
|
updateMediaMessage: async (message) => {
|
|
705
|
-
const content = (0,
|
|
1266
|
+
const content = (0, index_js_3.assertMediaContent)(message.message);
|
|
706
1267
|
const mediaKey = content.mediaKey;
|
|
707
1268
|
const meId = authState.creds.me.id;
|
|
708
|
-
const node =
|
|
1269
|
+
const node = (0, index_js_3.encryptMediaRetryRequest)(message.key, mediaKey, meId);
|
|
709
1270
|
let error = undefined;
|
|
710
1271
|
await Promise.all([
|
|
711
1272
|
sendNode(node),
|
|
@@ -717,13 +1278,16 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
717
1278
|
}
|
|
718
1279
|
else {
|
|
719
1280
|
try {
|
|
720
|
-
const media =
|
|
721
|
-
if (media.result !==
|
|
722
|
-
const resultStr =
|
|
723
|
-
throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1281
|
+
const media = (0, index_js_3.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
|
|
1282
|
+
if (media.result !== index_js_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
|
|
1283
|
+
const resultStr = index_js_1.proto.MediaRetryNotification.ResultType[media.result];
|
|
1284
|
+
throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, {
|
|
1285
|
+
data: media,
|
|
1286
|
+
statusCode: (0, index_js_3.getStatusCodeForMediaRetry)(media.result) || 404
|
|
1287
|
+
});
|
|
724
1288
|
}
|
|
725
1289
|
content.directPath = media.directPath;
|
|
726
|
-
content.url = (0,
|
|
1290
|
+
content.url = (0, index_js_3.getUrlFromDirectPath)(content.directPath);
|
|
727
1291
|
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
|
|
728
1292
|
}
|
|
729
1293
|
catch (err) {
|
|
@@ -737,264 +1301,380 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
737
1301
|
if (error) {
|
|
738
1302
|
throw error;
|
|
739
1303
|
}
|
|
740
|
-
ev.emit('messages.update', [
|
|
741
|
-
{ key: message.key, update: { message: message.message } }
|
|
742
|
-
]);
|
|
1304
|
+
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
|
|
743
1305
|
return message;
|
|
744
1306
|
},
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
1307
|
+
resize: async (media, width, height) => {
|
|
1308
|
+
const { getStream, toBuffer } = await Promise.resolve().then(() => __importStar(require('../Utils/messages-media.js')));
|
|
1309
|
+
let sharp;
|
|
1310
|
+
try {
|
|
1311
|
+
sharp = (await Promise.resolve().then(() => __importStar(require('sharp')))).default;
|
|
1312
|
+
}
|
|
1313
|
+
catch (_) { }
|
|
1314
|
+
if (!sharp)
|
|
1315
|
+
throw new Error('sharp is required for resize');
|
|
1316
|
+
const { stream } = await getStream(media);
|
|
1317
|
+
const buf = await toBuffer(stream);
|
|
1318
|
+
return sharp(buf).resize(width, height, { fit: 'cover' }).toBuffer();
|
|
1319
|
+
},
|
|
1320
|
+
sendStatusMentions: async (content, jids = []) => {
|
|
1321
|
+
const userJid = (0, index_js_4.jidNormalizedUser)(authState.creds.me.id);
|
|
1322
|
+
let allUsers = new Set();
|
|
1323
|
+
allUsers.add(userJid);
|
|
1324
|
+
for (const id of jids) {
|
|
1325
|
+
const isGroup = (0, index_js_4.isJidGroup)(id);
|
|
1326
|
+
const isPrivate = (0, index_js_4.isJidUser)(id);
|
|
1327
|
+
if (isGroup) {
|
|
1328
|
+
try {
|
|
1329
|
+
const metadata = (cachedGroupMetadata ? await cachedGroupMetadata(id) : null) || await groupMetadata(id);
|
|
1330
|
+
const participants = metadata.participants.map(p => (0, index_js_4.jidNormalizedUser)(p.id));
|
|
1331
|
+
participants.forEach(j => allUsers.add(j));
|
|
1332
|
+
}
|
|
1333
|
+
catch (error) {
|
|
1334
|
+
logger.error(`Error getting metadata for group ${id}: ${error}`);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
else if (isPrivate) {
|
|
1338
|
+
allUsers.add((0, index_js_4.jidNormalizedUser)(id));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
const uniqueUsers = Array.from(allUsers);
|
|
1342
|
+
const getRandomHexColor = () => '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0');
|
|
1343
|
+
const isMedia = content.image || content.video || content.audio;
|
|
1344
|
+
const isAudio = !!content.audio;
|
|
1345
|
+
const messageContent = { ...content };
|
|
1346
|
+
if (isMedia && !isAudio) {
|
|
1347
|
+
if (messageContent.text) {
|
|
1348
|
+
messageContent.caption = messageContent.text;
|
|
1349
|
+
delete messageContent.text;
|
|
1350
|
+
}
|
|
1351
|
+
delete messageContent.ptt;
|
|
1352
|
+
delete messageContent.font;
|
|
1353
|
+
delete messageContent.backgroundColor;
|
|
1354
|
+
delete messageContent.textColor;
|
|
1355
|
+
}
|
|
1356
|
+
if (isAudio) {
|
|
1357
|
+
delete messageContent.text;
|
|
1358
|
+
delete messageContent.caption;
|
|
1359
|
+
delete messageContent.font;
|
|
1360
|
+
delete messageContent.textColor;
|
|
1361
|
+
}
|
|
1362
|
+
const font = !isMedia ? (content.font ?? Math.floor(Math.random() * 9)) : undefined;
|
|
1363
|
+
const textColor = !isMedia ? (content.textColor ?? getRandomHexColor()) : undefined;
|
|
1364
|
+
const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor ?? getRandomHexColor()) : undefined;
|
|
1365
|
+
const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined;
|
|
1366
|
+
let msg;
|
|
1367
|
+
let mediaHandle;
|
|
1368
|
+
try {
|
|
1369
|
+
msg = await (0, index_js_3.generateWAMessage)(index_js_4.STORIES_JID, messageContent, {
|
|
1370
|
+
logger,
|
|
1371
|
+
userJid,
|
|
1372
|
+
getUrlInfo: text => (0, link_preview_js_1.getUrlInfo)(text, {
|
|
1373
|
+
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
1374
|
+
fetchOpts: { timeout: 3000 },
|
|
1375
|
+
logger,
|
|
1376
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
1377
|
+
}),
|
|
1378
|
+
upload: async (encFilePath, opts) => {
|
|
1379
|
+
const up = await waUploadToServer(encFilePath, { ...opts });
|
|
1380
|
+
mediaHandle = up.handle;
|
|
1381
|
+
return up;
|
|
1382
|
+
},
|
|
1383
|
+
mediaCache: config.mediaCache,
|
|
1384
|
+
options: config.options,
|
|
1385
|
+
font,
|
|
1386
|
+
textColor,
|
|
1387
|
+
backgroundColor,
|
|
1388
|
+
ptt
|
|
764
1389
|
});
|
|
765
|
-
} else {
|
|
766
|
-
waMsgContent = storyContent;
|
|
767
1390
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
waMsgContent = storyContent;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
const msg = {
|
|
775
|
-
message: {
|
|
776
|
-
groupStatusMessageV2: { message: waMsgContent.message || waMsgContent }
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
logger.error(`Error generating status message: ${error}`);
|
|
1393
|
+
throw error;
|
|
777
1394
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
buffer = fs.readFileSync(content.audio);
|
|
807
|
-
else if (content.audio.url && fs.existsSync(content.audio.url))
|
|
808
|
-
buffer = fs.readFileSync(content.audio.url);
|
|
809
|
-
|
|
810
|
-
if (buffer) {
|
|
811
|
-
const waveform = await getAudioWaveform(buffer);
|
|
812
|
-
if (waveform && waveform.length) {
|
|
813
|
-
content.waveform = waveform;
|
|
814
|
-
if (!content.mimetype)
|
|
815
|
-
content.mimetype = 'audio/ogg; codecs=opus';
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
} catch (err) {
|
|
820
|
-
console.log('[WILEYSS PATCH] Gagal generate waveform otomatis:', err.message);
|
|
821
|
-
}
|
|
822
|
-
// === AUTO WAVEFORM PATCH END ===
|
|
823
|
-
|
|
824
|
-
const userJid = authState.creds.me.id;
|
|
825
|
-
if (!options.ephemeralExpiration) {
|
|
826
|
-
if ((0, WABinary_1.isJidGroup)(jid)) {
|
|
827
|
-
const groups = await sock.groupQuery(jid, 'get', [{
|
|
828
|
-
tag: 'query',
|
|
829
|
-
attrs: {
|
|
830
|
-
request: 'interactive'
|
|
1395
|
+
await relayMessage(index_js_4.STORIES_JID, msg.message, {
|
|
1396
|
+
messageId: msg.key.id,
|
|
1397
|
+
statusJidList: uniqueUsers,
|
|
1398
|
+
additionalNodes: [{
|
|
1399
|
+
tag: 'meta',
|
|
1400
|
+
attrs: {},
|
|
1401
|
+
content: [{
|
|
1402
|
+
tag: 'mentioned_users',
|
|
1403
|
+
attrs: {},
|
|
1404
|
+
content: jids.map(jid => ({
|
|
1405
|
+
tag: 'to',
|
|
1406
|
+
attrs: { jid: (0, index_js_4.jidNormalizedUser)(jid) }
|
|
1407
|
+
}))
|
|
1408
|
+
}]
|
|
1409
|
+
}]
|
|
1410
|
+
});
|
|
1411
|
+
for (const id of jids) {
|
|
1412
|
+
try {
|
|
1413
|
+
const normalizedId = (0, index_js_4.jidNormalizedUser)(id);
|
|
1414
|
+
const isPrivate = (0, index_js_4.isJidUser)(normalizedId);
|
|
1415
|
+
const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage';
|
|
1416
|
+
const protocolMessage = {
|
|
1417
|
+
[type]: {
|
|
1418
|
+
message: {
|
|
1419
|
+
protocolMessage: {
|
|
1420
|
+
key: msg.key,
|
|
1421
|
+
type: 25
|
|
1422
|
+
}
|
|
831
1423
|
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1424
|
+
},
|
|
1425
|
+
messageContextInfo: {
|
|
1426
|
+
messageSecret: (0, crypto_1.randomBytes)(32)
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
const statusMsg = await (0, index_js_3.generateWAMessageFromContent)(normalizedId, protocolMessage, {});
|
|
1430
|
+
await relayMessage(normalizedId, statusMsg.message, {
|
|
1431
|
+
additionalNodes: [{
|
|
1432
|
+
tag: 'meta',
|
|
1433
|
+
attrs: isPrivate
|
|
1434
|
+
? { is_status_mention: 'true' }
|
|
1435
|
+
: { is_group_status_mention: 'true' }
|
|
1436
|
+
}]
|
|
1437
|
+
});
|
|
1438
|
+
await (0, generics_js_1.delay)(2000);
|
|
1439
|
+
}
|
|
1440
|
+
catch (error) {
|
|
1441
|
+
logger.error(`Error sending status mention to ${id}: ${error}`);
|
|
836
1442
|
}
|
|
837
1443
|
}
|
|
1444
|
+
return msg;
|
|
1445
|
+
},
|
|
1446
|
+
sendMessage: async (jid, content, options = {}) => {
|
|
1447
|
+
const userJid = authState.creds.me.id;
|
|
1448
|
+
// ── Normalize: buttons[].nativeFlowInfo -> interactiveButtons ──────
|
|
838
1449
|
if (typeof content === 'object' &&
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
(
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
if (typeof content === 'object' && 'album' in content && content.album) {
|
|
849
|
-
const { album, caption } = content;
|
|
850
|
-
if (caption && !album[0].caption) {
|
|
851
|
-
album[0].caption = caption;
|
|
852
|
-
}
|
|
853
|
-
let mediaHandle;
|
|
854
|
-
let mediaMsg;
|
|
855
|
-
const albumMsg = (0, Utils_1.generateWAMessageFromContent)(jid, {
|
|
856
|
-
albumMessage: {
|
|
857
|
-
expectedImageCount: album.filter(item => 'image' in item).length,
|
|
858
|
-
expectedVideoCount: album.filter(item => 'video' in item).length
|
|
1450
|
+
Array.isArray(content.buttons) &&
|
|
1451
|
+
content.buttons.length > 0 &&
|
|
1452
|
+
content.buttons.some(b => b.nativeFlowInfo)) {
|
|
1453
|
+
const interactiveButtons = content.buttons.map(b => {
|
|
1454
|
+
if (b.nativeFlowInfo) {
|
|
1455
|
+
return {
|
|
1456
|
+
name: b.nativeFlowInfo.name,
|
|
1457
|
+
buttonParamsJson: b.nativeFlowInfo.paramsJson || '{}'
|
|
1458
|
+
};
|
|
859
1459
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1460
|
+
return {
|
|
1461
|
+
name: 'quick_reply',
|
|
1462
|
+
buttonParamsJson: JSON.stringify({
|
|
1463
|
+
display_text: b.buttonText?.displayText || b.buttonId || 'Button',
|
|
1464
|
+
id: b.buttonId || b.buttonText?.displayText || 'btn'
|
|
1465
|
+
})
|
|
1466
|
+
};
|
|
863
1467
|
});
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
});
|
|
1468
|
+
const { buttons, headerType, viewOnce, ...rest } = content;
|
|
1469
|
+
content = { ...rest, interactiveButtons };
|
|
1470
|
+
}
|
|
1471
|
+
// ── Interactive Button (sendButton logic) ──────────────────────────
|
|
1472
|
+
if (typeof content === 'object' && Array.isArray(content.interactiveButtons) && content.interactiveButtons.length > 0) {
|
|
1473
|
+
const { text = '', caption = '', title = '', footer = '', interactiveButtons, hasMediaAttachment = false, image = null, video = null, document = null, mimetype = null, jpegThumbnail = null, location = null, product = null, businessOwnerJid = null, externalAdReply = null, } = content;
|
|
1474
|
+
// Normalize buttons
|
|
1475
|
+
const processedButtons = [];
|
|
1476
|
+
for (let i = 0; i < interactiveButtons.length; i++) {
|
|
1477
|
+
const btn = interactiveButtons[i];
|
|
1478
|
+
if (!btn || typeof btn !== 'object')
|
|
1479
|
+
throw new Error(`interactiveButtons[${i}] must be an object`);
|
|
1480
|
+
if (btn.name && btn.buttonParamsJson) {
|
|
1481
|
+
processedButtons.push(btn);
|
|
1482
|
+
continue;
|
|
880
1483
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
...(media.caption ? { caption: media.caption } : {}),
|
|
885
|
-
...(media.gifPlayback !== undefined ? { gifPlayback: media.gifPlayback } : {}),
|
|
886
|
-
...options
|
|
887
|
-
}, {
|
|
888
|
-
userJid,
|
|
889
|
-
upload: async (readStream, opts) => {
|
|
890
|
-
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
891
|
-
mediaHandle = up.handle;
|
|
892
|
-
return up;
|
|
893
|
-
},
|
|
894
|
-
...options,
|
|
895
|
-
});
|
|
1484
|
+
if (btn.id || btn.text || btn.displayText) {
|
|
1485
|
+
processedButtons.push({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn.text || btn.displayText || `Button ${i + 1}`, id: btn.id || `quick_${i + 1}` }) });
|
|
1486
|
+
continue;
|
|
896
1487
|
}
|
|
897
|
-
if (
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
messageAssociation: {
|
|
901
|
-
associationType: 1,
|
|
902
|
-
parentMessageKey: albumMsg.key
|
|
903
|
-
}
|
|
904
|
-
};
|
|
1488
|
+
if (btn.buttonId && btn.buttonText?.displayText) {
|
|
1489
|
+
processedButtons.push({ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: btn.buttonText.displayText, id: btn.buttonId }) });
|
|
1490
|
+
continue;
|
|
905
1491
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1492
|
+
throw new Error(`interactiveButtons[${i}] has invalid shape`);
|
|
1493
|
+
}
|
|
1494
|
+
let messageContent = {};
|
|
1495
|
+
// Header
|
|
1496
|
+
if (image) {
|
|
1497
|
+
const mi = Buffer.isBuffer(image) ? { image } : { image: { url: typeof image === 'object' ? image.url : image } };
|
|
1498
|
+
const pm = await (0, index_js_3.prepareWAMessageMedia)(mi, { upload: waUploadToServer });
|
|
1499
|
+
messageContent.header = { title: title || '', hasMediaAttachment: true, imageMessage: pm.imageMessage };
|
|
1500
|
+
}
|
|
1501
|
+
else if (video) {
|
|
1502
|
+
const mi = Buffer.isBuffer(video) ? { video } : { video: { url: typeof video === 'object' ? video.url : video } };
|
|
1503
|
+
const pm = await (0, index_js_3.prepareWAMessageMedia)(mi, { upload: waUploadToServer });
|
|
1504
|
+
messageContent.header = { title: title || '', hasMediaAttachment: true, videoMessage: pm.videoMessage };
|
|
1505
|
+
}
|
|
1506
|
+
else if (document) {
|
|
1507
|
+
const mi = Buffer.isBuffer(document) ? { document } : { document: { url: typeof document === 'object' ? document.url : document } };
|
|
1508
|
+
if (mimetype && typeof mi.document === 'object')
|
|
1509
|
+
mi.document.mimetype = mimetype;
|
|
1510
|
+
if (jpegThumbnail) {
|
|
1511
|
+
const thumb = Buffer.isBuffer(jpegThumbnail) ? jpegThumbnail : await (async () => { try {
|
|
1512
|
+
const r = await fetch(jpegThumbnail);
|
|
1513
|
+
return Buffer.from(await r.arrayBuffer());
|
|
1514
|
+
}
|
|
1515
|
+
catch {
|
|
1516
|
+
return undefined;
|
|
1517
|
+
} })();
|
|
1518
|
+
if (thumb)
|
|
1519
|
+
mi.document.jpegThumbnail = thumb;
|
|
1520
|
+
}
|
|
1521
|
+
const pm = await (0, index_js_3.prepareWAMessageMedia)(mi, { upload: waUploadToServer });
|
|
1522
|
+
messageContent.header = { title: title || '', hasMediaAttachment: true, documentMessage: pm.documentMessage };
|
|
1523
|
+
}
|
|
1524
|
+
else if (location && typeof location === 'object') {
|
|
1525
|
+
messageContent.header = { title: title || location.name || 'Location', hasMediaAttachment: false, locationMessage: { degreesLatitude: location.degreesLatitude || location.degressLatitude || 0, degreesLongitude: location.degreesLongitude || location.degressLongitude || 0, name: location.name || '', address: location.address || '' } };
|
|
1526
|
+
}
|
|
1527
|
+
else if (product && typeof product === 'object') {
|
|
1528
|
+
let productImageMessage = null;
|
|
1529
|
+
if (product.productImage) {
|
|
1530
|
+
const mi = Buffer.isBuffer(product.productImage) ? { image: product.productImage } : { image: { url: typeof product.productImage === 'object' ? product.productImage.url : product.productImage } };
|
|
1531
|
+
const pm = await (0, index_js_3.prepareWAMessageMedia)(mi, { upload: waUploadToServer });
|
|
1532
|
+
productImageMessage = pm.imageMessage;
|
|
1533
|
+
}
|
|
1534
|
+
messageContent.header = { title: title || product.title || 'Product', hasMediaAttachment: false, productMessage: { product: { productImage: productImageMessage, productId: product.productId || '', title: product.title || '', description: product.description || '', currencyCode: product.currencyCode || 'USD', priceAmount1000: parseInt(product.priceAmount1000) || 0, retailerId: product.retailerId || '', url: product.url || '', productImageCount: product.productImageCount || 1 }, businessOwnerJid: businessOwnerJid || product.businessOwnerJid || userJid } };
|
|
1535
|
+
}
|
|
1536
|
+
else if (title) {
|
|
1537
|
+
messageContent.header = { title, hasMediaAttachment: false };
|
|
910
1538
|
}
|
|
911
|
-
|
|
1539
|
+
const hasMedia = !!(image || video || document || location || product);
|
|
1540
|
+
const bodyText = hasMedia ? caption : text || caption;
|
|
1541
|
+
if (bodyText)
|
|
1542
|
+
messageContent.body = { text: bodyText };
|
|
1543
|
+
if (footer)
|
|
1544
|
+
messageContent.footer = { text: footer };
|
|
1545
|
+
messageContent.nativeFlowMessage = { buttons: processedButtons };
|
|
1546
|
+
// Context info
|
|
1547
|
+
if (externalAdReply && typeof externalAdReply === 'object') {
|
|
1548
|
+
messageContent.contextInfo = { externalAdReply: { title: externalAdReply.title || '', body: externalAdReply.body || '', mediaType: externalAdReply.mediaType || 1, sourceUrl: externalAdReply.sourceUrl || externalAdReply.url || '', thumbnailUrl: externalAdReply.thumbnailUrl || externalAdReply.thumbnail || '', renderLargerThumbnail: externalAdReply.renderLargerThumbnail || false, showAdAttribution: externalAdReply.showAdAttribution !== false, containsAutoReply: externalAdReply.containsAutoReply || false, ...(externalAdReply.mediaUrl && { mediaUrl: externalAdReply.mediaUrl }), ...(Buffer.isBuffer(externalAdReply.thumbnail) && { thumbnail: externalAdReply.thumbnail }), ...(externalAdReply.jpegThumbnail && { jpegThumbnail: externalAdReply.jpegThumbnail }) }, ...(options.mentionedJid && { mentionedJid: options.mentionedJid }) };
|
|
1549
|
+
}
|
|
1550
|
+
else if (options.mentionedJid) {
|
|
1551
|
+
messageContent.contextInfo = { mentionedJid: options.mentionedJid };
|
|
1552
|
+
}
|
|
1553
|
+
const payload = index_js_1.proto.Message.InteractiveMessage.create(messageContent);
|
|
1554
|
+
const msg = (0, index_js_3.generateWAMessageFromContent)(jid, { viewOnceMessage: { message: { interactiveMessage: payload } } }, { userJid, quoted: options?.quoted || null });
|
|
1555
|
+
const additionalNodes = [{ tag: 'biz', attrs: {}, content: [{ tag: 'interactive', attrs: { type: 'native_flow', v: '1' }, content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }] }] }];
|
|
1556
|
+
await relayMessage(jid, msg.message, { messageId: msg.key.id, additionalNodes });
|
|
1557
|
+
return msg;
|
|
1558
|
+
}
|
|
1559
|
+
// ── End Interactive Button ─────────────────────────────────────────
|
|
1560
|
+
if (typeof content === 'object' &&
|
|
1561
|
+
'disappearingMessagesInChat' in content &&
|
|
1562
|
+
typeof content['disappearingMessagesInChat'] !== 'undefined' &&
|
|
1563
|
+
(0, index_js_4.isJidGroup)(jid)) {
|
|
1564
|
+
const { disappearingMessagesInChat } = content;
|
|
1565
|
+
const value = typeof disappearingMessagesInChat === 'boolean'
|
|
1566
|
+
? disappearingMessagesInChat
|
|
1567
|
+
? index_js_2.WA_DEFAULT_EPHEMERAL
|
|
1568
|
+
: 0
|
|
1569
|
+
: disappearingMessagesInChat;
|
|
1570
|
+
await groupToggleEphemeral(jid, value);
|
|
912
1571
|
}
|
|
913
1572
|
else {
|
|
914
1573
|
let mediaHandle;
|
|
915
|
-
const fullMsg = await (0,
|
|
1574
|
+
const fullMsg = await (0, index_js_3.generateWAMessage)(jid, content, {
|
|
916
1575
|
logger,
|
|
917
1576
|
userJid,
|
|
918
|
-
getUrlInfo: text => (0,
|
|
1577
|
+
getUrlInfo: text => (0, link_preview_js_1.getUrlInfo)(text, {
|
|
919
1578
|
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
920
1579
|
fetchOpts: {
|
|
921
1580
|
timeout: 3000,
|
|
922
|
-
...
|
|
1581
|
+
...(httpRequestOptions || {})
|
|
923
1582
|
},
|
|
924
1583
|
logger,
|
|
925
|
-
uploadImage: generateHighQualityLinkPreview
|
|
926
|
-
? waUploadToServer
|
|
927
|
-
: undefined
|
|
1584
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
928
1585
|
}),
|
|
1586
|
+
//TODO: CACHE
|
|
929
1587
|
getProfilePicUrl: sock.profilePictureUrl,
|
|
930
|
-
|
|
931
|
-
|
|
1588
|
+
getCallLink: sock.createCallLink,
|
|
1589
|
+
newsletter: _isNewsletterJid(jid),
|
|
1590
|
+
upload: async (encFilePath, opts) => {
|
|
1591
|
+
const up = await waUploadToServer(encFilePath, { ...opts, newsletter: _isNewsletterJid(jid) });
|
|
932
1592
|
mediaHandle = up.handle;
|
|
933
1593
|
return up;
|
|
934
1594
|
},
|
|
935
1595
|
mediaCache: config.mediaCache,
|
|
936
1596
|
options: config.options,
|
|
937
|
-
messageId: (0,
|
|
938
|
-
...options
|
|
1597
|
+
messageId: (0, index_js_3.generateMessageIDV2)(sock.user?.id),
|
|
1598
|
+
...options
|
|
939
1599
|
});
|
|
1600
|
+
if (content?.audio && options?.contextInfo) {
|
|
1601
|
+
const msgContent = fullMsg.message;
|
|
1602
|
+
if (msgContent?.audioMessage) {
|
|
1603
|
+
msgContent.audioMessage.contextInfo = options.contextInfo;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
// Extract handle from newsletter upload (set by prepareWAMessageMedia)
|
|
1607
|
+
if (!mediaHandle) {
|
|
1608
|
+
const msgContent = fullMsg.message;
|
|
1609
|
+
const msgTypes = ['audioMessage', 'imageMessage', 'videoMessage', 'documentMessage', 'stickerMessage'];
|
|
1610
|
+
for (const t of msgTypes) {
|
|
1611
|
+
if (msgContent?.[t]?._uploadHandle) {
|
|
1612
|
+
mediaHandle = msgContent[t]._uploadHandle;
|
|
1613
|
+
delete msgContent[t]._uploadHandle;
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
const isEventMsg = 'event' in content && !!content.event;
|
|
940
1619
|
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
941
1620
|
const isEditMsg = 'edit' in content && !!content.edit;
|
|
942
1621
|
const isPinMsg = 'pin' in content && !!content.pin;
|
|
943
|
-
const isKeepMsg = 'keep' in content && content.keep;
|
|
944
1622
|
const isPollMessage = 'poll' in content && !!content.poll;
|
|
945
|
-
const isAiMsg = 'ai' in content && !!content.ai;
|
|
946
1623
|
const additionalAttributes = {};
|
|
947
1624
|
const additionalNodes = [];
|
|
948
1625
|
// required for delete
|
|
949
1626
|
if (isDeleteMsg) {
|
|
950
1627
|
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
951
|
-
if ((
|
|
1628
|
+
if ((0, index_js_4.isJidGroup)(content.delete?.remoteJid) && !content.delete?.fromMe) {
|
|
952
1629
|
additionalAttributes.edit = '8';
|
|
953
1630
|
}
|
|
954
1631
|
else {
|
|
955
1632
|
additionalAttributes.edit = '7';
|
|
956
1633
|
}
|
|
957
|
-
// required for edit message
|
|
958
1634
|
}
|
|
959
1635
|
else if (isEditMsg) {
|
|
960
|
-
additionalAttributes.edit =
|
|
961
|
-
// required for pin message
|
|
1636
|
+
additionalAttributes.edit = '1';
|
|
962
1637
|
}
|
|
963
1638
|
else if (isPinMsg) {
|
|
964
1639
|
additionalAttributes.edit = '2';
|
|
965
|
-
// required for keep message
|
|
966
|
-
}
|
|
967
|
-
else if (isKeepMsg) {
|
|
968
|
-
additionalAttributes.edit = '6';
|
|
969
|
-
// required for polling message
|
|
970
1640
|
}
|
|
971
1641
|
else if (isPollMessage) {
|
|
972
1642
|
additionalNodes.push({
|
|
973
1643
|
tag: 'meta',
|
|
974
1644
|
attrs: {
|
|
975
1645
|
polltype: 'creation'
|
|
976
|
-
}
|
|
1646
|
+
}
|
|
977
1647
|
});
|
|
978
|
-
// required to display AI icon on message
|
|
979
1648
|
}
|
|
980
|
-
else if (
|
|
1649
|
+
else if (isEventMsg) {
|
|
981
1650
|
additionalNodes.push({
|
|
1651
|
+
tag: 'meta',
|
|
982
1652
|
attrs: {
|
|
983
|
-
|
|
984
|
-
}
|
|
985
|
-
tag: "bot"
|
|
1653
|
+
event_type: 'creation'
|
|
1654
|
+
}
|
|
986
1655
|
});
|
|
987
1656
|
}
|
|
1657
|
+
// Auto-attach biz node for button/list/interactive messages
|
|
1658
|
+
const buttonType = getButtonType(fullMsg.message);
|
|
1659
|
+
if (buttonType) {
|
|
1660
|
+
const btnNode = getButtonArgs(fullMsg.message);
|
|
1661
|
+
if (btnNode)
|
|
1662
|
+
additionalNodes.push(btnNode);
|
|
1663
|
+
}
|
|
988
1664
|
if (mediaHandle) {
|
|
989
1665
|
additionalAttributes['media_id'] = mediaHandle;
|
|
990
1666
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1667
|
+
await relayMessage(jid, fullMsg.message, {
|
|
1668
|
+
messageId: fullMsg.key.id,
|
|
1669
|
+
useCachedGroupMetadata: options.useCachedGroupMetadata,
|
|
1670
|
+
additionalAttributes,
|
|
1671
|
+
statusJidList: options.statusJidList,
|
|
1672
|
+
additionalNodes,
|
|
1673
|
+
AI: options.ai || false
|
|
1674
|
+
});
|
|
995
1675
|
if (config.emitOwnEvents) {
|
|
996
|
-
process.nextTick(() => {
|
|
997
|
-
|
|
1676
|
+
process.nextTick(async () => {
|
|
1677
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
998
1678
|
});
|
|
999
1679
|
}
|
|
1000
1680
|
return fullMsg;
|