jagproject 26.3.22 → 26.3.25
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 +51 -33
- 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 +1184 -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/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 +1099 -400
- 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/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 +10 -6
- package/readme.md +97 -0
- 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
|
-
.withContext('message')
|
|
197
|
-
.withDeviceProtocol();
|
|
217
|
+
const requestedLidUsers = new Set();
|
|
198
218
|
for (const jid of toFetch) {
|
|
199
|
-
|
|
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();
|
|
226
|
+
for (const jid of toFetch) {
|
|
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
|
+
}
|
|
272
|
+
}
|
|
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 })));
|
|
209
276
|
}
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
338
|
+
}
|
|
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;
|
|
231
346
|
}
|
|
232
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
|
}
|
|
@@ -634,6 +1122,9 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
634
1122
|
else if (message.stickerMessage) {
|
|
635
1123
|
return 'sticker';
|
|
636
1124
|
}
|
|
1125
|
+
else if (message.stickerPackMessage) {
|
|
1126
|
+
return 'sticker_pack';
|
|
1127
|
+
}
|
|
637
1128
|
else if (message.listMessage) {
|
|
638
1129
|
return 'list';
|
|
639
1130
|
}
|
|
@@ -655,13 +1146,14 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
655
1146
|
else if (message.groupInviteMessage) {
|
|
656
1147
|
return 'url';
|
|
657
1148
|
}
|
|
1149
|
+
return '';
|
|
658
1150
|
};
|
|
659
1151
|
const getPrivacyTokens = async (jids) => {
|
|
660
|
-
const t = (0,
|
|
1152
|
+
const t = (0, index_js_3.unixTimestampSeconds)().toString();
|
|
661
1153
|
const result = await query({
|
|
662
1154
|
tag: 'iq',
|
|
663
1155
|
attrs: {
|
|
664
|
-
to:
|
|
1156
|
+
to: index_js_4.S_WHATSAPP_NET,
|
|
665
1157
|
type: 'set',
|
|
666
1158
|
xmlns: 'privacy'
|
|
667
1159
|
},
|
|
@@ -672,7 +1164,7 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
672
1164
|
content: jids.map(jid => ({
|
|
673
1165
|
tag: 'token',
|
|
674
1166
|
attrs: {
|
|
675
|
-
jid: (0,
|
|
1167
|
+
jid: (0, index_js_4.jidNormalizedUser)(jid),
|
|
676
1168
|
t,
|
|
677
1169
|
type: 'trusted_contact'
|
|
678
1170
|
}
|
|
@@ -682,8 +1174,78 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
682
1174
|
});
|
|
683
1175
|
return result;
|
|
684
1176
|
};
|
|
685
|
-
const waUploadToServer = (0,
|
|
686
|
-
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 ───────────────────────────────────────────────
|
|
687
1249
|
return {
|
|
688
1250
|
...sock,
|
|
689
1251
|
getPrivacyTokens,
|
|
@@ -695,14 +1257,16 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
695
1257
|
refreshMediaConn,
|
|
696
1258
|
waUploadToServer,
|
|
697
1259
|
fetchPrivacySettings,
|
|
698
|
-
getUSyncDevices,
|
|
699
|
-
createParticipantNodes,
|
|
700
1260
|
sendPeerDataOperationMessage,
|
|
1261
|
+
createParticipantNodes,
|
|
1262
|
+
getUSyncDevices,
|
|
1263
|
+
messageRetryManager,
|
|
1264
|
+
updateMemberLabel,
|
|
701
1265
|
updateMediaMessage: async (message) => {
|
|
702
|
-
const content = (0,
|
|
1266
|
+
const content = (0, index_js_3.assertMediaContent)(message.message);
|
|
703
1267
|
const mediaKey = content.mediaKey;
|
|
704
1268
|
const meId = authState.creds.me.id;
|
|
705
|
-
const node =
|
|
1269
|
+
const node = (0, index_js_3.encryptMediaRetryRequest)(message.key, mediaKey, meId);
|
|
706
1270
|
let error = undefined;
|
|
707
1271
|
await Promise.all([
|
|
708
1272
|
sendNode(node),
|
|
@@ -714,13 +1278,16 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
714
1278
|
}
|
|
715
1279
|
else {
|
|
716
1280
|
try {
|
|
717
|
-
const media =
|
|
718
|
-
if (media.result !==
|
|
719
|
-
const resultStr =
|
|
720
|
-
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
|
+
});
|
|
721
1288
|
}
|
|
722
1289
|
content.directPath = media.directPath;
|
|
723
|
-
content.url = (0,
|
|
1290
|
+
content.url = (0, index_js_3.getUrlFromDirectPath)(content.directPath);
|
|
724
1291
|
logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
|
|
725
1292
|
}
|
|
726
1293
|
catch (err) {
|
|
@@ -734,264 +1301,380 @@ const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
|
|
|
734
1301
|
if (error) {
|
|
735
1302
|
throw error;
|
|
736
1303
|
}
|
|
737
|
-
ev.emit('messages.update', [
|
|
738
|
-
{ key: message.key, update: { message: message.message } }
|
|
739
|
-
]);
|
|
1304
|
+
ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
|
|
740
1305
|
return message;
|
|
741
1306
|
},
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
|
761
1389
|
});
|
|
762
|
-
} else {
|
|
763
|
-
waMsgContent = storyContent;
|
|
764
1390
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
waMsgContent = storyContent;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
const msg = {
|
|
772
|
-
message: {
|
|
773
|
-
groupStatusMessageV2: { message: waMsgContent.message || waMsgContent }
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
logger.error(`Error generating status message: ${error}`);
|
|
1393
|
+
throw error;
|
|
774
1394
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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
|
-
buffer = fs.readFileSync(content.audio);
|
|
804
|
-
else if (content.audio.url && fs.existsSync(content.audio.url))
|
|
805
|
-
buffer = fs.readFileSync(content.audio.url);
|
|
806
|
-
|
|
807
|
-
if (buffer) {
|
|
808
|
-
const waveform = await getAudioWaveform(buffer);
|
|
809
|
-
if (waveform && waveform.length) {
|
|
810
|
-
content.waveform = waveform;
|
|
811
|
-
if (!content.mimetype)
|
|
812
|
-
content.mimetype = 'audio/ogg; codecs=opus';
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
} catch (err) {
|
|
817
|
-
console.log('[WILEYSS PATCH] Gagal generate waveform otomatis:', err.message);
|
|
818
|
-
}
|
|
819
|
-
// === AUTO WAVEFORM PATCH END ===
|
|
820
|
-
|
|
821
|
-
const userJid = authState.creds.me.id;
|
|
822
|
-
if (!options.ephemeralExpiration) {
|
|
823
|
-
if ((0, WABinary_1.isJidGroup)(jid)) {
|
|
824
|
-
const groups = await sock.groupQuery(jid, 'get', [{
|
|
825
|
-
tag: 'query',
|
|
826
|
-
attrs: {
|
|
827
|
-
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
|
+
}
|
|
828
1423
|
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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}`);
|
|
833
1442
|
}
|
|
834
1443
|
}
|
|
1444
|
+
return msg;
|
|
1445
|
+
},
|
|
1446
|
+
sendMessage: async (jid, content, options = {}) => {
|
|
1447
|
+
const userJid = authState.creds.me.id;
|
|
1448
|
+
// ── Normalize: buttons[].nativeFlowInfo -> interactiveButtons ──────
|
|
835
1449
|
if (typeof content === 'object' &&
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
(
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if (typeof content === 'object' && 'album' in content && content.album) {
|
|
846
|
-
const { album, caption } = content;
|
|
847
|
-
if (caption && !album[0].caption) {
|
|
848
|
-
album[0].caption = caption;
|
|
849
|
-
}
|
|
850
|
-
let mediaHandle;
|
|
851
|
-
let mediaMsg;
|
|
852
|
-
const albumMsg = (0, Utils_1.generateWAMessageFromContent)(jid, {
|
|
853
|
-
albumMessage: {
|
|
854
|
-
expectedImageCount: album.filter(item => 'image' in item).length,
|
|
855
|
-
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
|
+
};
|
|
856
1459
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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
|
+
};
|
|
860
1467
|
});
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
});
|
|
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;
|
|
877
1483
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
...(media.caption ? { caption: media.caption } : {}),
|
|
882
|
-
...(media.gifPlayback !== undefined ? { gifPlayback: media.gifPlayback } : {}),
|
|
883
|
-
...options
|
|
884
|
-
}, {
|
|
885
|
-
userJid,
|
|
886
|
-
upload: async (readStream, opts) => {
|
|
887
|
-
const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
|
|
888
|
-
mediaHandle = up.handle;
|
|
889
|
-
return up;
|
|
890
|
-
},
|
|
891
|
-
...options,
|
|
892
|
-
});
|
|
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;
|
|
893
1487
|
}
|
|
894
|
-
if (
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
messageAssociation: {
|
|
898
|
-
associationType: 1,
|
|
899
|
-
parentMessageKey: albumMsg.key
|
|
900
|
-
}
|
|
901
|
-
};
|
|
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;
|
|
902
1491
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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 };
|
|
907
1538
|
}
|
|
908
|
-
|
|
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);
|
|
909
1571
|
}
|
|
910
1572
|
else {
|
|
911
1573
|
let mediaHandle;
|
|
912
|
-
const fullMsg = await (0,
|
|
1574
|
+
const fullMsg = await (0, index_js_3.generateWAMessage)(jid, content, {
|
|
913
1575
|
logger,
|
|
914
1576
|
userJid,
|
|
915
|
-
getUrlInfo: text => (0,
|
|
1577
|
+
getUrlInfo: text => (0, link_preview_js_1.getUrlInfo)(text, {
|
|
916
1578
|
thumbnailWidth: linkPreviewImageThumbnailWidth,
|
|
917
1579
|
fetchOpts: {
|
|
918
1580
|
timeout: 3000,
|
|
919
|
-
...
|
|
1581
|
+
...(httpRequestOptions || {})
|
|
920
1582
|
},
|
|
921
1583
|
logger,
|
|
922
|
-
uploadImage: generateHighQualityLinkPreview
|
|
923
|
-
? waUploadToServer
|
|
924
|
-
: undefined
|
|
1584
|
+
uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
|
|
925
1585
|
}),
|
|
1586
|
+
//TODO: CACHE
|
|
926
1587
|
getProfilePicUrl: sock.profilePictureUrl,
|
|
927
|
-
|
|
928
|
-
|
|
1588
|
+
getCallLink: sock.createCallLink,
|
|
1589
|
+
newsletter: _isNewsletterJid(jid),
|
|
1590
|
+
upload: async (encFilePath, opts) => {
|
|
1591
|
+
const up = await waUploadToServer(encFilePath, { ...opts, newsletter: _isNewsletterJid(jid) });
|
|
929
1592
|
mediaHandle = up.handle;
|
|
930
1593
|
return up;
|
|
931
1594
|
},
|
|
932
1595
|
mediaCache: config.mediaCache,
|
|
933
1596
|
options: config.options,
|
|
934
|
-
messageId: (0,
|
|
935
|
-
...options
|
|
1597
|
+
messageId: (0, index_js_3.generateMessageIDV2)(sock.user?.id),
|
|
1598
|
+
...options
|
|
936
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;
|
|
937
1619
|
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
938
1620
|
const isEditMsg = 'edit' in content && !!content.edit;
|
|
939
1621
|
const isPinMsg = 'pin' in content && !!content.pin;
|
|
940
|
-
const isKeepMsg = 'keep' in content && content.keep;
|
|
941
1622
|
const isPollMessage = 'poll' in content && !!content.poll;
|
|
942
|
-
const isAiMsg = 'ai' in content && !!content.ai;
|
|
943
1623
|
const additionalAttributes = {};
|
|
944
1624
|
const additionalNodes = [];
|
|
945
1625
|
// required for delete
|
|
946
1626
|
if (isDeleteMsg) {
|
|
947
1627
|
// if the chat is a group, and I am not the author, then delete the message as an admin
|
|
948
|
-
if ((
|
|
1628
|
+
if ((0, index_js_4.isJidGroup)(content.delete?.remoteJid) && !content.delete?.fromMe) {
|
|
949
1629
|
additionalAttributes.edit = '8';
|
|
950
1630
|
}
|
|
951
1631
|
else {
|
|
952
1632
|
additionalAttributes.edit = '7';
|
|
953
1633
|
}
|
|
954
|
-
// required for edit message
|
|
955
1634
|
}
|
|
956
1635
|
else if (isEditMsg) {
|
|
957
|
-
additionalAttributes.edit =
|
|
958
|
-
// required for pin message
|
|
1636
|
+
additionalAttributes.edit = '1';
|
|
959
1637
|
}
|
|
960
1638
|
else if (isPinMsg) {
|
|
961
1639
|
additionalAttributes.edit = '2';
|
|
962
|
-
// required for keep message
|
|
963
|
-
}
|
|
964
|
-
else if (isKeepMsg) {
|
|
965
|
-
additionalAttributes.edit = '6';
|
|
966
|
-
// required for polling message
|
|
967
1640
|
}
|
|
968
1641
|
else if (isPollMessage) {
|
|
969
1642
|
additionalNodes.push({
|
|
970
1643
|
tag: 'meta',
|
|
971
1644
|
attrs: {
|
|
972
1645
|
polltype: 'creation'
|
|
973
|
-
}
|
|
1646
|
+
}
|
|
974
1647
|
});
|
|
975
|
-
// required to display AI icon on message
|
|
976
1648
|
}
|
|
977
|
-
else if (
|
|
1649
|
+
else if (isEventMsg) {
|
|
978
1650
|
additionalNodes.push({
|
|
1651
|
+
tag: 'meta',
|
|
979
1652
|
attrs: {
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
tag: "bot"
|
|
1653
|
+
event_type: 'creation'
|
|
1654
|
+
}
|
|
983
1655
|
});
|
|
984
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
|
+
}
|
|
985
1664
|
if (mediaHandle) {
|
|
986
1665
|
additionalAttributes['media_id'] = mediaHandle;
|
|
987
1666
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
+
});
|
|
992
1675
|
if (config.emitOwnEvents) {
|
|
993
|
-
process.nextTick(() => {
|
|
994
|
-
|
|
1676
|
+
process.nextTick(async () => {
|
|
1677
|
+
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
995
1678
|
});
|
|
996
1679
|
}
|
|
997
1680
|
return fullMsg;
|