@sanzoffc/baileys 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +1 -1
  2. package/WAProto/WAProto.proto +769 -233
  3. package/WAProto/index.js +65801 -141371
  4. package/lib/Defaults/index.js +117 -114
  5. package/lib/Defaults/index.js.bak +123 -0
  6. package/lib/KeyDB/BinarySearch.js +20 -0
  7. package/lib/KeyDB/KeyedDB.js +167 -0
  8. package/lib/KeyDB/index.js +4 -0
  9. package/lib/Signal/Group/ciphertext-message.js +12 -14
  10. package/lib/Signal/Group/group-session-builder.js +10 -42
  11. package/lib/Signal/Group/group_cipher.js +75 -87
  12. package/lib/Signal/Group/index.js +13 -57
  13. package/lib/Signal/Group/keyhelper.js +17 -52
  14. package/lib/Signal/Group/sender-chain-key.js +27 -33
  15. package/lib/Signal/Group/sender-key-distribution-message.js +62 -63
  16. package/lib/Signal/Group/sender-key-message.js +65 -66
  17. package/lib/Signal/Group/sender-key-name.js +45 -44
  18. package/lib/Signal/Group/sender-key-record.js +39 -49
  19. package/lib/Signal/Group/sender-key-state.js +80 -93
  20. package/lib/Signal/Group/sender-message-key.js +27 -28
  21. package/lib/Signal/libsignal.js +313 -163
  22. package/lib/Signal/lid-mapping.js +155 -0
  23. package/lib/Socket/Client/index.js +4 -18
  24. package/lib/Socket/Client/types.js +12 -12
  25. package/lib/Socket/Client/websocket.js +51 -71
  26. package/lib/Socket/Client/websocket.js.bak +53 -0
  27. package/lib/Socket/business.js +359 -242
  28. package/lib/Socket/chats.js +858 -945
  29. package/lib/Socket/communities.js +413 -0
  30. package/lib/Socket/groups.js +304 -324
  31. package/lib/Socket/index.js +15 -9
  32. package/lib/Socket/messages-recv.js +1105 -1046
  33. package/lib/Socket/messages-send.js +615 -389
  34. package/lib/Socket/mex.js +45 -0
  35. package/lib/Socket/newsletter.js +224 -227
  36. package/lib/Socket/socket.js +795 -621
  37. package/lib/Store/index.js +6 -8
  38. package/lib/Store/make-cache-manager-store.js +75 -0
  39. package/lib/Store/make-in-memory-store.js +286 -435
  40. package/lib/Store/make-ordered-dictionary.js +77 -79
  41. package/lib/Store/object-repository.js +24 -26
  42. package/lib/Types/Auth.js +3 -2
  43. package/lib/Types/Bussines.js +3 -0
  44. package/lib/Types/Call.js +3 -2
  45. package/lib/Types/Chat.js +9 -4
  46. package/lib/Types/Contact.js +3 -2
  47. package/lib/Types/Events.js +3 -2
  48. package/lib/Types/GroupMetadata.js +3 -2
  49. package/lib/Types/Label.js +24 -26
  50. package/lib/Types/LabelAssociation.js +6 -8
  51. package/lib/Types/Message.js +12 -7
  52. package/lib/Types/Newsletter.js +32 -17
  53. package/lib/Types/Newsletter.js.bak +33 -0
  54. package/lib/Types/Product.js +3 -2
  55. package/lib/Types/Signal.js +3 -2
  56. package/lib/Types/Socket.js +4 -2
  57. package/lib/Types/State.js +11 -2
  58. package/lib/Types/USync.js +3 -2
  59. package/lib/Types/index.js +27 -41
  60. package/lib/Utils/auth-utils.js +211 -191
  61. package/lib/Utils/baileys-event-stream.js +44 -0
  62. package/lib/Utils/browser-utils.js +21 -31
  63. package/lib/Utils/business.js +213 -214
  64. package/lib/Utils/chat-utils.js +711 -689
  65. package/lib/Utils/crypto.js +112 -175
  66. package/lib/Utils/decode-wa-message.js +254 -194
  67. package/lib/Utils/event-buffer.js +510 -500
  68. package/lib/Utils/generics.js +318 -430
  69. package/lib/Utils/history.js +83 -90
  70. package/lib/Utils/index.js +21 -35
  71. package/lib/Utils/link-preview.js +71 -116
  72. package/lib/Utils/logger.js +5 -7
  73. package/lib/Utils/lt-hash.js +40 -46
  74. package/lib/Utils/make-mutex.js +34 -41
  75. package/lib/Utils/message-retry-manager.js +33 -48
  76. package/lib/Utils/messages-media.js +573 -825
  77. package/lib/Utils/messages.js +349 -489
  78. package/lib/Utils/noise-handler.js +138 -144
  79. package/lib/Utils/pre-key-manager.js +85 -0
  80. package/lib/Utils/process-message.js +321 -384
  81. package/lib/Utils/signal.js +147 -139
  82. package/lib/Utils/use-multi-file-auth-state.js +95 -109
  83. package/lib/Utils/validate-connection.js +183 -212
  84. package/lib/WABinary/constants.js +1298 -1298
  85. package/lib/WABinary/decode.js +231 -256
  86. package/lib/WABinary/encode.js +207 -239
  87. package/lib/WABinary/generic-utils.js +119 -40
  88. package/lib/WABinary/index.js +7 -21
  89. package/lib/WABinary/jid-utils.js +87 -79
  90. package/lib/WABinary/types.js +3 -2
  91. package/lib/WAM/BinaryInfo.js +10 -12
  92. package/lib/WAM/constants.js +22851 -15348
  93. package/lib/WAM/encode.js +135 -136
  94. package/lib/WAM/index.js +5 -19
  95. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -30
  96. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +49 -53
  97. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -28
  98. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +36 -39
  99. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
  100. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +26 -20
  101. package/lib/WAUSync/Protocols/index.js +6 -20
  102. package/lib/WAUSync/USyncQuery.js +86 -85
  103. package/lib/WAUSync/USyncUser.js +23 -25
  104. package/lib/WAUSync/index.js +5 -19
  105. package/lib/index.js +18 -49
  106. package/package.json +65 -78
  107. package/README.MD +0 -1295
  108. package/WAProto/GenerateStatics.sh +0 -4
  109. package/WAProto/p.html +0 -1
  110. package/engine-requirements.js +0 -10
  111. package/lib/Defaults/wileys-version.json +0 -3
  112. package/lib/Signal/Group/queue-job.js +0 -57
  113. package/lib/Socket/usync.js +0 -70
  114. package/lib/Utils/wileys-event-stream.js +0 -63
@@ -1,46 +1,89 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.makeMessagesSocket = void 0;
7
- const boom_1 = require("@hapi/boom");
8
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
9
- const crypto_1 = require("crypto");
10
- const WAProto_1 = require("../../WAProto");
11
- const Defaults_1 = require("../Defaults");
12
- const Utils_1 = require("../Utils");
13
- const link_preview_1 = require("../Utils/link-preview");
14
- const WABinary_1 = require("../WABinary");
15
- const WAUSync_1 = require("../WAUSync");
16
- const newsletter_1 = require("./newsletter");
17
- const makeMessagesSocket = (config) => {
18
- const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: axiosOptions, patchMessageBeforeSending, cachedGroupMetadata, albumMessageItemDelayMs = 0, } = config;
19
- const sock = (0, newsletter_1.makeNewsletterSocket)(config);
20
- const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, } = sock;
21
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
22
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
1
+ import NodeCache from '@cacheable/node-cache';
2
+ import { Boom } from '@hapi/boom';
3
+ import { proto } from '../../WAProto/index.js';
4
+ import {
5
+ DEFAULT_CACHE_TTLS,
6
+ WA_DEFAULT_EPHEMERAL
7
+ } from '../Defaults/index.js';
8
+ import {
9
+ aggregateMessageKeysNotFromMe,
10
+ assertMediaContent,
11
+ bindWaitForEvent,
12
+ decryptMediaRetryData,
13
+ encodeNewsletterMessage,
14
+ encodeSignedDeviceIdentity,
15
+ encodeWAMessage,
16
+ encryptMediaRetryRequest,
17
+ extractDeviceJids,
18
+ generateMessageIDV2,
19
+ generateParticipantHashV2,
20
+ generateWAMessage,
21
+ getStatusCodeForMediaRetry,
22
+ getUrlFromDirectPath,
23
+ getWAUploadToServer,
24
+ MessageRetryManager,
25
+ normalizeMessageContent,
26
+ parseAndInjectE2ESessions,
27
+ unixTimestampSeconds
28
+ } from '../Utils/index.js';
29
+ import {
30
+ areJidsSameUser,
31
+ getBinaryNodeChild,
32
+ getBinaryNodeChildren,
33
+ getAdditionalNode,
34
+ getBinaryNodeFilter,
35
+ isHostedLidUser,
36
+ isHostedPnUser,
37
+ isJidGroup,
38
+ isLidUser,
39
+ isPnUser,
40
+ jidDecode,
41
+ jidEncode,
42
+ isJidNewsletter,
43
+ jidNormalizedUser,
44
+ S_WHATSAPP_NET
45
+ } from '../WABinary/index.js';
46
+ import { getUrlInfo } from '../Utils/link-preview.js';
47
+ import { makeKeyedMutex } from '../Utils/make-mutex.js';
48
+ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
49
+ import { makeNewsletterSocket } from './newsletter.js';
50
+ export const makeMessagesSocket = (config) => {
51
+ const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
52
+ const sock = makeNewsletterSocket(config);
53
+ const { ev, authState, processingMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
54
+ const userDevicesCache = config.userDevicesCache ||
55
+ new NodeCache({
56
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
57
+ useClones: false
58
+ });
59
+ const peerSessionsCache = new NodeCache({
60
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
23
61
  useClones: false
24
62
  });
63
+ // Initialize message retry manager if enabled
64
+ const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
65
+ // Prevent race conditions in Signal session encryption by user
66
+ const encryptionMutex = makeKeyedMutex();
25
67
  let mediaConn;
26
68
  const refreshMediaConn = async (forceGet = false) => {
27
69
  const media = await mediaConn;
28
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
70
+ if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
29
71
  mediaConn = (async () => {
30
72
  const result = await query({
31
73
  tag: 'iq',
32
74
  attrs: {
33
75
  type: 'set',
34
76
  xmlns: 'w:m',
35
- to: WABinary_1.S_WHATSAPP_NET,
77
+ to: S_WHATSAPP_NET
36
78
  },
37
79
  content: [{ tag: 'media_conn', attrs: {} }]
38
80
  });
39
- const mediaConnNode = (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn');
81
+ const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
82
+ // TODO: explore full length of data that whatsapp provides
40
83
  const node = {
41
- hosts: (0, WABinary_1.getBinaryNodeChildren)(mediaConnNode, 'host').map(({ attrs }) => ({
84
+ hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
42
85
  hostname: attrs.hostname,
43
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
86
+ maxContentLengthBytes: +attrs.maxContentLengthBytes
44
87
  })),
45
88
  auth: mediaConnNode.attrs.auth,
46
89
  ttl: +mediaConnNode.attrs.ttl,
@@ -57,17 +100,20 @@ const makeMessagesSocket = (config) => {
57
100
  * used for receipts of phone call, read, delivery etc.
58
101
  * */
59
102
  const sendReceipt = async (jid, participant, messageIds, type) => {
103
+ if (!messageIds || messageIds.length === 0) {
104
+ throw new Boom('missing ids in receipt');
105
+ }
60
106
  const node = {
61
107
  tag: 'receipt',
62
108
  attrs: {
63
- id: messageIds[0],
64
- },
109
+ id: messageIds[0]
110
+ }
65
111
  };
66
112
  const isReadReceipt = type === 'read' || type === 'read-self';
67
113
  if (isReadReceipt) {
68
- node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString();
114
+ node.attrs.t = unixTimestampSeconds().toString();
69
115
  }
70
- if (type === 'sender' && (0, WABinary_1.isJidUser)(jid)) {
116
+ if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
71
117
  node.attrs.recipient = jid;
72
118
  node.attrs.to = participant;
73
119
  }
@@ -78,7 +124,7 @@ const makeMessagesSocket = (config) => {
78
124
  }
79
125
  }
80
126
  if (type) {
81
- node.attrs.type = (0, WABinary_1.isJidNewsletter)(jid) ? 'read-self' : type;
127
+ node.attrs.type = type;
82
128
  }
83
129
  const remainingMessageIds = messageIds.slice(1);
84
130
  if (remainingMessageIds.length) {
@@ -98,7 +144,7 @@ const makeMessagesSocket = (config) => {
98
144
  };
99
145
  /** Correctly bulk send receipts to multiple chats, participants */
100
146
  const sendReceipts = async (keys, type) => {
101
- const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys);
147
+ const recps = aggregateMessageKeysNotFromMe(keys);
102
148
  for (const { jid, participant, messageIds } of recps) {
103
149
  await sendReceipt(jid, participant, messageIds, type);
104
150
  }
@@ -112,20 +158,44 @@ const makeMessagesSocket = (config) => {
112
158
  };
113
159
  /** Fetch all the devices we've to send a message to */
114
160
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
115
- var _a;
116
161
  const deviceResults = [];
117
162
  if (!useCache) {
118
163
  logger.debug('not using cache for devices');
119
164
  }
120
165
  const toFetch = [];
121
- jids = Array.from(new Set(jids));
122
- for (let jid of jids) {
123
- const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
124
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
166
+ const jidsWithUser = jids
167
+ .map(jid => {
168
+ const decoded = jidDecode(jid);
169
+ const user = decoded?.user;
170
+ const device = decoded?.device;
171
+ const isExplicitDevice = typeof device === 'number' && device >= 0;
172
+ if (isExplicitDevice && user) {
173
+ deviceResults.push({
174
+ user,
175
+ device,
176
+ jid
177
+ });
178
+ return null;
179
+ }
180
+ jid = jidNormalizedUser(jid);
181
+ return { jid, user };
182
+ })
183
+ .filter(jid => jid !== null);
184
+ let mgetDevices;
185
+ if (useCache && userDevicesCache.mget) {
186
+ const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
187
+ mgetDevices = await userDevicesCache.mget(usersToFetch);
188
+ }
189
+ for (const { jid, user } of jidsWithUser) {
125
190
  if (useCache) {
126
- const devices = userDevicesCache.get(user);
191
+ const devices = mgetDevices?.[user] ||
192
+ (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
127
193
  if (devices) {
128
- deviceResults.push(...devices);
194
+ const devicesWithJid = devices.map(d => ({
195
+ ...d,
196
+ jid: jidEncode(d.user, d.server, d.device)
197
+ }));
198
+ deviceResults.push(...devicesWithJid);
129
199
  logger.trace({ user }, 'using cache for devices');
130
200
  }
131
201
  else {
@@ -139,141 +209,243 @@ const makeMessagesSocket = (config) => {
139
209
  if (!toFetch.length) {
140
210
  return deviceResults;
141
211
  }
142
- const query = new WAUSync_1.USyncQuery()
143
- .withContext('message')
144
- .withDeviceProtocol();
212
+ const requestedLidUsers = new Set();
213
+ for (const jid of toFetch) {
214
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
215
+ const user = jidDecode(jid)?.user;
216
+ if (user)
217
+ requestedLidUsers.add(user);
218
+ }
219
+ }
220
+ const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
145
221
  for (const jid of toFetch) {
146
- query.withUser(new WAUSync_1.USyncUser().withId(jid));
222
+ query.withUser(new USyncUser().withId(jid)); // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
147
223
  }
148
224
  const result = await sock.executeUSyncQuery(query);
149
225
  if (result) {
150
- const extracted = (0, Utils_1.extractDeviceJids)(result === null || result === void 0 ? void 0 : result.list, authState.creds.me.id, ignoreZeroDevices);
226
+ // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
227
+ const lidResults = result.list.filter(a => !!a.lid);
228
+ if (lidResults.length > 0) {
229
+ logger.trace('Storing LID maps from device call');
230
+ await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
231
+ }
232
+ const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
151
233
  const deviceMap = {};
152
234
  for (const item of extracted) {
153
235
  deviceMap[item.user] = deviceMap[item.user] || [];
154
- deviceMap[item.user].push(item);
155
- deviceResults.push(item);
236
+ deviceMap[item.user]?.push(item);
237
+ }
238
+ // Process each user's devices as a group for bulk LID migration
239
+ for (const [user, userDevices] of Object.entries(deviceMap)) {
240
+ const isLidUser = requestedLidUsers.has(user);
241
+ // Process all devices for this user
242
+ for (const item of userDevices) {
243
+ const finalJid = isLidUser
244
+ ? jidEncode(user, item.server, item.device)
245
+ : jidEncode(item.user, item.server, item.device);
246
+ deviceResults.push({
247
+ ...item,
248
+ jid: finalJid
249
+ });
250
+ logger.debug({
251
+ user: item.user,
252
+ device: item.device,
253
+ finalJid,
254
+ usedLid: isLidUser
255
+ }, 'Processed device with LID priority');
256
+ }
257
+ }
258
+ if (userDevicesCache.mset) {
259
+ // if the cache supports mset, we can set all devices in one go
260
+ await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
261
+ }
262
+ else {
263
+ for (const key in deviceMap) {
264
+ if (deviceMap[key])
265
+ await userDevicesCache.set(key, deviceMap[key]);
266
+ }
267
+ }
268
+ const userDeviceUpdates = {};
269
+ for (const [userId, devices] of Object.entries(deviceMap)) {
270
+ if (devices && devices.length > 0) {
271
+ userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
272
+ }
156
273
  }
157
- for (const key in deviceMap) {
158
- userDevicesCache.set(key, deviceMap[key]);
274
+ if (Object.keys(userDeviceUpdates).length > 0) {
275
+ try {
276
+ await authState.keys.set({ 'device-list': userDeviceUpdates });
277
+ logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
278
+ }
279
+ catch (error) {
280
+ logger.warn({ error }, 'failed to store user device lists');
281
+ }
159
282
  }
160
283
  }
161
284
  return deviceResults;
162
285
  };
163
- const assertSessions = async (jids, force) => {
286
+ const assertSessions = async (jids) => {
164
287
  let didFetchNewSession = false;
165
- let jidsRequiringFetch = [];
166
- if (force) {
167
- jidsRequiringFetch = jids;
168
- }
169
- else {
170
- const addrs = jids.map(jid => (signalRepository
171
- .jidToSignalProtocolAddress(jid)));
172
- const sessions = await authState.keys.get('session', addrs);
173
- for (const jid of jids) {
174
- const signalId = signalRepository
175
- .jidToSignalProtocolAddress(jid);
176
- if (!sessions[signalId]) {
177
- jidsRequiringFetch.push(jid);
288
+ const uniqueJids = [...new Set(jids)]; // Deduplicate JIDs
289
+ const jidsRequiringFetch = [];
290
+ logger.debug({ jids }, 'assertSessions call with jids');
291
+ // Check peerSessionsCache and validate sessions using libsignal loadSession
292
+ for (const jid of uniqueJids) {
293
+ const signalId = signalRepository.jidToSignalProtocolAddress(jid);
294
+ const cachedSession = peerSessionsCache.get(signalId);
295
+ if (cachedSession !== undefined) {
296
+ if (cachedSession) {
297
+ continue; // Session exists in cache
178
298
  }
179
299
  }
300
+ else {
301
+ const sessionValidation = await signalRepository.validateSession(jid);
302
+ const hasSession = sessionValidation.exists;
303
+ peerSessionsCache.set(signalId, hasSession);
304
+ if (hasSession) {
305
+ continue;
306
+ }
307
+ }
308
+ jidsRequiringFetch.push(jid);
180
309
  }
181
310
  if (jidsRequiringFetch.length) {
182
- logger.debug({ jidsRequiringFetch }, 'fetching sessions');
311
+ // LID if mapped, otherwise original
312
+ const wireJids = [
313
+ ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
314
+ ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
315
+ ];
316
+ logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
183
317
  const result = await query({
184
318
  tag: 'iq',
185
319
  attrs: {
186
320
  xmlns: 'encrypt',
187
321
  type: 'get',
188
- to: WABinary_1.S_WHATSAPP_NET,
322
+ to: S_WHATSAPP_NET
189
323
  },
190
324
  content: [
191
325
  {
192
326
  tag: 'key',
193
327
  attrs: {},
194
- content: jidsRequiringFetch.map(jid => ({
328
+ content: wireJids.map(jid => ({
195
329
  tag: 'user',
196
- attrs: { jid },
330
+ attrs: { jid }
197
331
  }))
198
332
  }
199
333
  ]
200
334
  });
201
- await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
335
+ await parseAndInjectE2ESessions(result, signalRepository);
202
336
  didFetchNewSession = true;
337
+ // Cache fetched sessions using wire JIDs
338
+ for (const wireJid of wireJids) {
339
+ const signalId = signalRepository.jidToSignalProtocolAddress(wireJid);
340
+ peerSessionsCache.set(signalId, true);
341
+ }
203
342
  }
204
343
  return didFetchNewSession;
205
344
  };
206
345
  const sendPeerDataOperationMessage = async (pdoMessage) => {
207
- var _a;
208
346
  //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
209
- if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
210
- throw new boom_1.Boom('Not authenticated');
347
+ if (!authState.creds.me?.id) {
348
+ throw new Boom('Not authenticated');
211
349
  }
212
350
  const protocolMessage = {
213
351
  protocolMessage: {
214
352
  peerDataOperationRequestMessage: pdoMessage,
215
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
353
+ type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
216
354
  }
217
355
  };
218
- const meJid = (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id);
356
+ const meJid = jidNormalizedUser(authState.creds.me.id);
219
357
  const msgId = await relayMessage(meJid, protocolMessage, {
220
358
  additionalAttributes: {
221
359
  category: 'peer',
222
- // eslint-disable-next-line camelcase
223
- push_priority: 'high_force',
360
+ push_priority: 'high_force'
224
361
  },
362
+ additionalNodes: [
363
+ {
364
+ tag: 'meta',
365
+ attrs: { appdata: 'default' }
366
+ }
367
+ ]
225
368
  });
226
369
  return msgId;
227
370
  };
228
- const createParticipantNodes = async (jids, message, extraAttrs) => {
229
- let patched = await patchMessageBeforeSending(message, jids);
230
- if (!Array.isArray(patched)) {
231
- patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
371
+ const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
372
+ if (!recipientJids.length) {
373
+ return { nodes: [], shouldIncludeDeviceIdentity: false };
232
374
  }
375
+ const patched = await patchMessageBeforeSending(message, recipientJids);
376
+ const patchedMessages = Array.isArray(patched)
377
+ ? patched
378
+ : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
233
379
  let shouldIncludeDeviceIdentity = false;
234
- const nodes = await Promise.all(patched.map(async (patchedMessageWithJid) => {
235
- const { recipientJid: jid, ...patchedMessage } = patchedMessageWithJid;
236
- if (!jid) {
237
- return {};
238
- }
239
- const bytes = (0, Utils_1.encodeWAMessage)(patchedMessage);
240
- const { type, ciphertext } = await signalRepository
241
- .encryptMessage({ jid, data: bytes });
242
- if (type === 'pkmsg') {
243
- shouldIncludeDeviceIdentity = true;
244
- }
245
- const node = {
246
- tag: 'to',
247
- attrs: { jid },
248
- content: [{
249
- tag: 'enc',
250
- attrs: {
251
- v: '2',
252
- type,
253
- ...extraAttrs || {}
254
- },
255
- content: ciphertext
256
- }]
257
- };
380
+ const meId = authState.creds.me.id;
381
+ const meLid = authState.creds.me?.lid;
382
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
383
+ const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
384
+ if (!jid)
385
+ return null;
386
+ let msgToEncrypt = patchedMessage;
387
+ if (dsmMessage) {
388
+ const { user: targetUser } = jidDecode(jid);
389
+ const { user: ownPnUser } = jidDecode(meId);
390
+ const ownLidUser = meLidUser;
391
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
392
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
393
+ if (isOwnUser && !isExactSenderDevice) {
394
+ msgToEncrypt = dsmMessage;
395
+ logger.debug({ jid, targetUser }, 'Using DSM for own device');
396
+ }
397
+ }
398
+ const bytes = encodeWAMessage(msgToEncrypt);
399
+ const mutexKey = jid;
400
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
401
+ const { type, ciphertext } = await signalRepository.encryptMessage({
402
+ jid,
403
+ data: bytes
404
+ });
405
+ if (type === 'pkmsg') {
406
+ shouldIncludeDeviceIdentity = true;
407
+ }
408
+ return {
409
+ tag: 'to',
410
+ attrs: { jid },
411
+ content: [
412
+ {
413
+ tag: 'enc',
414
+ attrs: {
415
+ v: '2',
416
+ type,
417
+ ...(extraAttrs || {})
418
+ },
419
+ content: ciphertext
420
+ }
421
+ ]
422
+ };
423
+ });
258
424
  return node;
259
- }));
425
+ });
426
+ const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
260
427
  return { nodes, shouldIncludeDeviceIdentity };
261
428
  };
262
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
263
- var _a;
429
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, AI = false }) => {
430
+ // let shouldIncludeDeviceIdentity = false;
431
+ let didPushAdditional = false
264
432
  const meId = authState.creds.me.id;
265
- let shouldIncludeDeviceIdentity = false;
266
- const { user, server } = (0, WABinary_1.jidDecode)(jid);
433
+ const meLid = authState.creds.me?.lid;
434
+ const isRetryResend = Boolean(participant?.jid);
435
+ let shouldIncludeDeviceIdentity = isRetryResend;
267
436
  const statusJid = 'status@broadcast';
437
+ const { user, server } = jidDecode(jid);
268
438
  const isGroup = server === 'g.us';
269
- const isNewsletter = server === 'newsletter';
270
439
  const isStatus = jid === statusJid;
271
440
  const isLid = server === 'lid';
272
- msgId = msgId || (0, Utils_1.generateMessageIDV2)((_a = sock.user) === null || _a === void 0 ? void 0 : _a.id);
441
+ const isNewsletter = server === 'newsletter';
442
+ const isPrivate = server === 's.whatsapp.net'
443
+ const finalJid = jid;
444
+ msgId = msgId || generateMessageIDV2(meId);
273
445
  useUserDevicesCache = useUserDevicesCache !== false;
274
446
  useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
275
447
  const participants = [];
276
- const destinationJid = (!isStatus) ? (0, WABinary_1.jidEncode)(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid;
448
+ const destinationJid = !isStatus ? finalJid : statusJid;
277
449
  const binaryNodeContent = [];
278
450
  const devices = [];
279
451
  const meMsg = {
@@ -284,162 +456,253 @@ const makeMessagesSocket = (config) => {
284
456
  messageContextInfo: message.messageContextInfo
285
457
  };
286
458
  const extraAttrs = {};
459
+ const messages = normalizeMessageContent(message)
460
+ const buttonType = getButtonType(messages);
287
461
  if (participant) {
288
- // when the retry request is not for a group
289
- // only send to the specific device that asked for a retry
290
- // otherwise the message is sent out to every device that should be a recipient
291
462
  if (!isGroup && !isStatus) {
292
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' };
463
+ additionalAttributes = {
464
+ ...additionalAttributes,
465
+ device_fanout: 'false'
466
+ };
293
467
  }
294
- const { user, device } = (0, WABinary_1.jidDecode)(participant.jid);
295
- devices.push({ user, device });
468
+ const { user, device } = jidDecode(participant.jid);
469
+ devices.push({
470
+ user,
471
+ device,
472
+ jid: participant.jid
473
+ });
296
474
  }
297
475
  await authState.keys.transaction(async () => {
298
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
299
476
  const mediaType = getMediaType(message);
300
477
  if (mediaType) {
301
478
  extraAttrs['mediatype'] = mediaType;
302
479
  }
303
- if ((_a = (0, Utils_1.normalizeMessageContent)(message)) === null || _a === void 0 ? void 0 : _a.pinInChatMessage) {
304
- extraAttrs['decrypt-fail'] = 'hide';
480
+
481
+ if (isNewsletter) {
482
+ const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
483
+ const bytes = encodeNewsletterMessage(patched);
484
+ binaryNodeContent.push({
485
+ tag: "plaintext",
486
+ attrs: mediaType ? { mediatype: mediaType } : {},
487
+ content: bytes
488
+ });
489
+ const stanza = {
490
+ tag: "message",
491
+ attrs: {
492
+ to: jid,
493
+ id: msgId,
494
+ type: getTypeMessage(message),
495
+ ...(additionalAttributes || {})
496
+ },
497
+ content: binaryNodeContent
498
+ };
499
+ logger.debug({ msgId }, `sending newsletter message to ${jid}`);
500
+ await sendNode(stanza);
501
+ return;
502
+ }
503
+
504
+ if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
505
+ extraAttrs['decrypt-fail'] = 'hide'
506
+ }
507
+
508
+ if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
509
+ extraAttrs['native_flow_name'] = messages.interactiveResponseMessage?.nativeFlowResponseMessage.name
305
510
  }
511
+
306
512
  if (isGroup || isStatus) {
307
513
  const [groupData, senderKeyMap] = await Promise.all([
308
514
  (async () => {
309
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
310
- if (groupData && Array.isArray(groupData === null || groupData === void 0 ? void 0 : groupData.participants)) {
311
- logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
515
+ 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?
516
+ if (groupData && Array.isArray(groupData?.participants)) {
517
+ logger.trace({
518
+ jid,
519
+ participants: groupData.participants.length
520
+ }, 'using cached group metadata');
312
521
  }
313
522
  else if (!isStatus) {
314
- groupData = await groupMetadata(jid);
523
+ groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
315
524
  }
316
525
  return groupData;
317
526
  })(),
318
527
  (async () => {
319
528
  if (!participant && !isStatus) {
320
- const result = await authState.keys.get('sender-key-memory', [jid]);
529
+ // what if sender memory is less accurate than the cached metadata
530
+ // on participant change in group, we should do sender memory manipulation
531
+ 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?
321
532
  return result[jid] || {};
322
533
  }
323
534
  return {};
324
535
  })()
325
536
  ]);
326
537
  if (!participant) {
327
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : [];
538
+ const participantsList = groupData && !isStatus ? groupData.participants.map(p => p.id) : [];
328
539
  if (isStatus && statusJidList) {
329
540
  participantsList.push(...statusJidList);
330
541
  }
331
- if (!isStatus) {
332
- additionalAttributes = {
333
- ...additionalAttributes,
334
- // eslint-disable-next-line camelcase
335
- addressing_mode: (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) || 'pn'
336
- };
337
- }
542
+ // if (!isStatus) {
543
+ // additionalAttributes = {
544
+ // ...additionalAttributes,
545
+ // addressing_mode: groupData?.addressingMode || 'pn'
546
+ // };
547
+ // }
338
548
  const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
339
549
  devices.push(...additionalDevices);
340
550
  }
551
+ if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
552
+ additionalAttributes = {
553
+ ...additionalAttributes,
554
+ expiration: groupData.ephemeralDuration.toString()
555
+ };
556
+ }
341
557
  const patched = await patchMessageBeforeSending(message);
342
558
  if (Array.isArray(patched)) {
343
- throw new boom_1.Boom('Per-jid patching is not supported in groups');
559
+ throw new Boom('Per-jid patching is not supported in groups');
344
560
  }
345
- const bytes = (0, Utils_1.encodeWAMessage)(patched);
561
+ const bytes = encodeWAMessage(patched);
562
+ const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
563
+ const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
346
564
  const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
347
565
  group: destinationJid,
348
566
  data: bytes,
349
- meId,
567
+ meId: groupSenderIdentity
350
568
  });
351
- const senderKeyJids = [];
352
- // ensure a connection is established with every device
353
- for (const { user, device } of devices) {
354
- const jid = (0, WABinary_1.jidEncode)(user, (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) === 'lid' ? 'lid' : 's.whatsapp.net', device);
355
- if (!senderKeyMap[jid] || !!participant) {
356
- senderKeyJids.push(jid);
357
- // store that this person has had the sender keys sent to them
358
- senderKeyMap[jid] = true;
569
+ const senderKeyRecipients = [];
570
+ for (const device of devices) {
571
+ const deviceJid = device.jid;
572
+ const hasKey = !!senderKeyMap[deviceJid];
573
+ if ((!hasKey || !!participant) &&
574
+ !isHostedLidUser(deviceJid) &&
575
+ !isHostedPnUser(deviceJid) &&
576
+ device.device !== 99) {
577
+ //todo: revamp all this logic
578
+ // 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
579
+ senderKeyRecipients.push(deviceJid);
580
+ senderKeyMap[deviceJid] = true;
359
581
  }
360
582
  }
361
- // if there are some participants with whom the session has not been established
362
- // if there are, we re-send the senderkey
363
- if (senderKeyJids.length) {
364
- logger.debug({ senderKeyJids }, 'sending new sender key');
583
+ if (senderKeyRecipients.length) {
584
+ logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
365
585
  const senderKeyMsg = {
366
586
  senderKeyDistributionMessage: {
367
587
  axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
368
588
  groupId: destinationJid
369
589
  }
370
590
  };
371
- await assertSessions(senderKeyJids, false);
372
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs);
591
+ const senderKeySessionTargets = senderKeyRecipients;
592
+ await assertSessions(senderKeySessionTargets);
593
+ const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
373
594
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
374
595
  participants.push(...result.nodes);
375
596
  }
376
- binaryNodeContent.push({
377
- tag: 'enc',
378
- attrs: { v: '2', type: 'skmsg' },
379
- content: ciphertext
380
- });
381
- await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
382
- }
383
- else if (isNewsletter) {
384
- // Message edit
385
- if ((_b = message.protocolMessage) === null || _b === void 0 ? void 0 : _b.editedMessage) {
386
- msgId = (_c = message.protocolMessage.key) === null || _c === void 0 ? void 0 : _c.id;
387
- message = message.protocolMessage.editedMessage;
388
- }
389
- // Message delete
390
- if (((_d = message.protocolMessage) === null || _d === void 0 ? void 0 : _d.type) === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
391
- msgId = (_e = message.protocolMessage.key) === null || _e === void 0 ? void 0 : _e.id;
392
- message = {};
597
+ if (isRetryResend) {
598
+ const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
599
+ data: bytes,
600
+ jid: participant?.jid
601
+ });
602
+ binaryNodeContent.push({
603
+ tag: 'enc',
604
+ attrs: {
605
+ v: '2',
606
+ type,
607
+ count: participant.count.toString()
608
+ },
609
+ content: encryptedContent
610
+ });
393
611
  }
394
- const patched = await patchMessageBeforeSending(message, []);
395
- if (Array.isArray(patched)) {
396
- throw new boom_1.Boom('Per-jid patching is not supported in channel');
612
+ else {
613
+ binaryNodeContent.push({
614
+ tag: 'enc',
615
+ attrs: {
616
+ v: '2',
617
+ type: 'skmsg',
618
+ ...extraAttrs
619
+ },
620
+ content: ciphertext
621
+ });
622
+ await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
397
623
  }
398
- const bytes = (0, Utils_1.encodeNewsletterMessage)(patched);
399
- binaryNodeContent.push({
400
- tag: 'plaintext',
401
- attrs: mediaType ? { mediatype: mediaType } : {},
402
- content: bytes
403
- });
404
624
  }
405
625
  else {
406
- const { user: meUser } = (0, WABinary_1.jidDecode)(meId);
626
+ // ADDRESSING CONSISTENCY: Match own identity to conversation context
627
+ // TODO: investigate if this is true
628
+ let ownId = meId;
629
+ if (isLid && meLid) {
630
+ ownId = meLid;
631
+ logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
632
+ }
633
+ else {
634
+ logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
635
+ }
636
+ const { user: ownUser } = jidDecode(ownId);
407
637
  if (!participant) {
408
- devices.push({ user });
409
- if (user !== meUser) {
410
- devices.push({ user: meUser });
638
+ const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
639
+ devices.push({
640
+ user,
641
+ device: 0,
642
+ jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
643
+ });
644
+ if (user !== ownUser) {
645
+ const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
646
+ const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
647
+ devices.push({
648
+ user: ownUserForAddressing,
649
+ device: 0,
650
+ jid: jidEncode(ownUserForAddressing, ownUserServer, 0)
651
+ });
411
652
  }
412
- if ((additionalAttributes === null || additionalAttributes === void 0 ? void 0 : additionalAttributes['category']) !== 'peer') {
413
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true);
414
- devices.push(...additionalDevices);
653
+ if (additionalAttributes?.['category'] !== 'peer') {
654
+ // Clear placeholders and enumerate actual devices
655
+ devices.length = 0;
656
+ // Use conversation-appropriate sender identity
657
+ const senderIdentity = isLid && meLid
658
+ ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
659
+ : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
660
+ // Enumerate devices for sender and target with consistent addressing
661
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
662
+ devices.push(...sessionDevices);
663
+ logger.debug({
664
+ deviceCount: devices.length,
665
+ devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`)
666
+ }, 'Device enumeration complete with unified addressing');
415
667
  }
416
668
  }
417
- const allJids = [];
418
- const meJids = [];
419
- const otherJids = [];
420
- for (const { user, device } of devices) {
421
- const isMe = user === meUser;
422
- const jid = (0, WABinary_1.jidEncode)(isMe && isLid ? ((_g = (_f = authState.creds) === null || _f === void 0 ? void 0 : _f.me) === null || _g === void 0 ? void 0 : _g.lid.split(':')[0]) || user : user, isLid ? 'lid' : 's.whatsapp.net', device);
669
+ const allRecipients = [];
670
+ const meRecipients = [];
671
+ const otherRecipients = [];
672
+ const { user: mePnUser } = jidDecode(meId);
673
+ const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
674
+ for (const { user, jid } of devices) {
675
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
676
+ if (isExactSenderDevice) {
677
+ logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
678
+ continue;
679
+ }
680
+ // Check if this is our device (could match either PN or LID user)
681
+ const isMe = user === mePnUser || user === meLidUser;
423
682
  if (isMe) {
424
- meJids.push(jid);
683
+ meRecipients.push(jid);
425
684
  }
426
685
  else {
427
- otherJids.push(jid);
686
+ otherRecipients.push(jid);
428
687
  }
429
- allJids.push(jid);
688
+ allRecipients.push(jid);
430
689
  }
431
- await assertSessions(allJids, false);
690
+ await assertSessions(allRecipients);
432
691
  const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
433
- createParticipantNodes(meJids, meMsg, extraAttrs),
434
- createParticipantNodes(otherJids, message, extraAttrs)
692
+ // For own devices: use DSM if available (1:1 chats only)
693
+ createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
694
+ createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
435
695
  ]);
436
696
  participants.push(...meNodes);
437
697
  participants.push(...otherNodes);
698
+ /* if (meRecipients.length > 0 || otherRecipients.length > 0) {
699
+ extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients]);
700
+ }*/
438
701
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
439
702
  }
440
703
  if (participants.length) {
441
- if ((additionalAttributes === null || additionalAttributes === void 0 ? void 0 : additionalAttributes['category']) === 'peer') {
442
- const peerNode = (_j = (_h = participants[0]) === null || _h === void 0 ? void 0 : _h.content) === null || _j === void 0 ? void 0 : _j[0];
704
+ if (additionalAttributes?.['category'] === 'peer') {
705
+ const peerNode = participants[0]?.content?.[0];
443
706
  if (peerNode) {
444
707
  binaryNodeContent.push(peerNode); // push only enc
445
708
  }
@@ -456,7 +719,8 @@ const makeMessagesSocket = (config) => {
456
719
  tag: 'message',
457
720
  attrs: {
458
721
  id: msgId,
459
- type: isNewsletter ? getTypeMessage(message) : 'text',
722
+ to: destinationJid,
723
+ type: getTypeMessage(messages),
460
724
  ...(additionalAttributes || {})
461
725
  },
462
726
  content: binaryNodeContent
@@ -465,11 +729,11 @@ const makeMessagesSocket = (config) => {
465
729
  // ensure the message is only sent to that person
466
730
  // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
467
731
  if (participant) {
468
- if ((0, WABinary_1.isJidGroup)(destinationJid)) {
732
+ if (isJidGroup(destinationJid)) {
469
733
  stanza.attrs.to = destinationJid;
470
734
  stanza.attrs.participant = participant.jid;
471
735
  }
472
- else if ((0, WABinary_1.areJidsSameUser)(participant.jid, meId)) {
736
+ else if (areJidsSameUser(participant.jid, meId)) {
473
737
  stanza.attrs.to = participant.jid;
474
738
  stanza.attrs.recipient = destinationJid;
475
739
  }
@@ -481,50 +745,58 @@ const makeMessagesSocket = (config) => {
481
745
  stanza.attrs.to = destinationJid;
482
746
  }
483
747
  if (shouldIncludeDeviceIdentity) {
748
+ ;
484
749
  stanza.content.push({
485
750
  tag: 'device-identity',
486
751
  attrs: {},
487
- content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
752
+ content: encodeSignedDeviceIdentity(authState.creds.account, true)
488
753
  });
489
754
  logger.debug({ jid }, 'adding device identity');
490
755
  }
491
- if (additionalNodes && additionalNodes.length > 0) {
492
- stanza.content.push(...additionalNodes);
756
+ if (AI && isPrivate) {
757
+ const botNode = {
758
+ tag: 'bot',
759
+ attrs: {
760
+ biz_bot: '1'
761
+ }
762
+ }
763
+
764
+ const filteredBizBot = getBinaryNodeFilter(additionalNodes ? additionalNodes : [])
765
+
766
+ if (filteredBizBot) {
767
+ stanza.content.push(...additionalNodes)
768
+ didPushAdditional = true
769
+ }
770
+
771
+ else {
772
+ stanza.content.push(botNode)
773
+ }
493
774
  }
494
- const content = (0, Utils_1.normalizeMessageContent)(message);
495
- const contentType = (0, Utils_1.getContentType)(content);
496
- if (((0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidUser)(jid)) && (contentType === 'interactiveMessage' ||
497
- contentType === 'buttonsMessage' ||
498
- contentType === 'listMessage')) {
499
- const bizNode = { tag: 'biz', attrs: {} };
500
- if ((((_l = (_k = message === null || message === void 0 ? void 0 : message.viewOnceMessage) === null || _k === void 0 ? void 0 : _k.message) === null || _l === void 0 ? void 0 : _l.interactiveMessage) || ((_o = (_m = message === null || message === void 0 ? void 0 : message.viewOnceMessageV2) === null || _m === void 0 ? void 0 : _m.message) === null || _o === void 0 ? void 0 : _o.interactiveMessage) || ((_q = (_p = message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension) === null || _p === void 0 ? void 0 : _p.message) === null || _q === void 0 ? void 0 : _q.interactiveMessage) || (message === null || message === void 0 ? void 0 : message.interactiveMessage)) || (((_s = (_r = message === null || message === void 0 ? void 0 : message.viewOnceMessage) === null || _r === void 0 ? void 0 : _r.message) === null || _s === void 0 ? void 0 : _s.buttonsMessage) || ((_u = (_t = message === null || message === void 0 ? void 0 : message.viewOnceMessageV2) === null || _t === void 0 ? void 0 : _t.message) === null || _u === void 0 ? void 0 : _u.buttonsMessage) || ((_w = (_v = message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension) === null || _v === void 0 ? void 0 : _v.message) === null || _w === void 0 ? void 0 : _w.buttonsMessage) || (message === null || message === void 0 ? void 0 : message.buttonsMessage))) {
501
- bizNode.content = [{
502
- tag: 'interactive',
503
- attrs: {
504
- type: 'native_flow',
505
- v: '1'
506
- },
507
- content: [{
508
- tag: 'native_flow',
509
- attrs: { v: '9', name: 'mixed' }
510
- }]
511
- }];
512
- }
513
- else if (message === null || message === void 0 ? void 0 : message.listMessage) {
514
- // list message only support in private chat
515
- bizNode.content = [{
516
- tag: 'list',
517
- attrs: {
518
- type: 'product_list',
519
- v: '2'
520
- }
521
- }];
775
+
776
+ if(buttonType && !isStatus) {
777
+ const content = getAdditionalNode(buttonType)
778
+ const filteredNode = getBinaryNodeFilter(additionalNodes)
779
+
780
+ if (filteredNode) {
781
+ didPushAdditional = true
782
+ stanza.content.push(...additionalNodes)
783
+ }
784
+ else {
785
+ stanza.content.push(...content)
522
786
  }
523
- stanza.content.push(bizNode);
787
+ logger.debug({ jid }, 'adding business node')
788
+ }
789
+
790
+ if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
791
+ stanza.content.push(...additionalNodes);
524
792
  }
525
793
  logger.debug({ msgId }, `sending message to ${participants.length} devices`);
526
794
  await sendNode(stanza);
527
- });
795
+ // Add message to retry cache if enabled
796
+ if (messageRetryManager && !participant) {
797
+ messageRetryManager.addRecentMessage(destinationJid, msgId, message);
798
+ }
799
+ }, meId);
528
800
  return msgId;
529
801
  };
530
802
  const getTypeMessage = (msg) => {
@@ -558,57 +830,89 @@ const makeMessagesSocket = (config) => {
558
830
  };
559
831
  const getMediaType = (message) => {
560
832
  if (message.imageMessage) {
561
- return 'image';
833
+ return 'image'
562
834
  }
563
835
  else if (message.videoMessage) {
564
- return message.videoMessage.gifPlayback ? 'gif' : 'video';
836
+ return message.videoMessage.gifPlayback ? 'gif' : 'video'
565
837
  }
566
838
  else if (message.audioMessage) {
567
- return message.audioMessage.ptt ? 'ptt' : 'audio';
839
+ return message.audioMessage.ptt ? 'ptt' : 'audio'
568
840
  }
569
841
  else if (message.contactMessage) {
570
- return 'vcard';
842
+ return 'vcard'
571
843
  }
572
844
  else if (message.documentMessage) {
573
- return 'document';
845
+ return 'document'
574
846
  }
575
847
  else if (message.contactsArrayMessage) {
576
- return 'contact_array';
848
+ return 'contact_array'
577
849
  }
578
850
  else if (message.liveLocationMessage) {
579
- return 'livelocation';
851
+ return 'livelocation'
580
852
  }
581
853
  else if (message.stickerMessage) {
582
- return 'sticker';
854
+ return 'sticker'
583
855
  }
584
856
  else if (message.listMessage) {
585
- return 'list';
857
+ return 'list'
586
858
  }
587
859
  else if (message.listResponseMessage) {
588
- return 'list_response';
860
+ return 'list_response'
589
861
  }
590
862
  else if (message.buttonsResponseMessage) {
591
- return 'buttons_response';
863
+ return 'buttons_response'
592
864
  }
593
865
  else if (message.orderMessage) {
594
- return 'order';
866
+ return 'order'
595
867
  }
596
868
  else if (message.productMessage) {
597
- return 'product';
869
+ return 'product'
598
870
  }
599
871
  else if (message.interactiveResponseMessage) {
600
- return 'native_flow_response';
872
+ return 'native_flow_response'
601
873
  }
602
874
  else if (message.groupInviteMessage) {
603
- return 'url';
875
+ return 'url'
604
876
  }
605
- };
877
+ else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
878
+ return 'productlink'
879
+ }
880
+ }
881
+ const getButtonType = (message) => {
882
+ if (message.listMessage) {
883
+ return 'list'
884
+ }
885
+ else if (message.buttonsMessage) {
886
+ return 'buttons'
887
+ }
888
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_and_pay') {
889
+ return 'review_and_pay'
890
+ }
891
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_order') {
892
+ return 'review_order'
893
+ }
894
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
895
+ return 'payment_info'
896
+ }
897
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
898
+ return 'payment_status'
899
+ }
900
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
901
+ return 'payment_method'
902
+ }
903
+ else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
904
+ return 'interactive'
905
+ }
906
+ else if (message.interactiveMessage?.nativeFlowMessage) {
907
+ return 'native_flow'
908
+ }
909
+ }
606
910
  const getPrivacyTokens = async (jids) => {
607
- const t = (0, Utils_1.unixTimestampSeconds)().toString();
911
+ const t = unixTimestampSeconds().toString();
608
912
  const result = await query({
609
913
  tag: 'iq',
610
914
  attrs: {
611
- to: WABinary_1.S_WHATSAPP_NET,
915
+ to: S_WHATSAPP_NET,
612
916
  type: 'set',
613
917
  xmlns: 'privacy'
614
918
  },
@@ -619,7 +923,7 @@ const makeMessagesSocket = (config) => {
619
923
  content: jids.map(jid => ({
620
924
  tag: 'token',
621
925
  attrs: {
622
- jid: (0, WABinary_1.jidNormalizedUser)(jid),
926
+ jid: jidNormalizedUser(jid),
623
927
  t,
624
928
  type: 'trusted_contact'
625
929
  }
@@ -629,8 +933,8 @@ const makeMessagesSocket = (config) => {
629
933
  });
630
934
  return result;
631
935
  };
632
- const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);
633
- const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');
936
+ const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
937
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
634
938
  return {
635
939
  ...sock,
636
940
  getPrivacyTokens,
@@ -642,14 +946,15 @@ const makeMessagesSocket = (config) => {
642
946
  refreshMediaConn,
643
947
  waUploadToServer,
644
948
  fetchPrivacySettings,
645
- getUSyncDevices,
646
- createParticipantNodes,
647
949
  sendPeerDataOperationMessage,
950
+ createParticipantNodes,
951
+ getUSyncDevices,
952
+ messageRetryManager,
648
953
  updateMediaMessage: async (message) => {
649
- const content = (0, Utils_1.assertMediaContent)(message.message);
954
+ const content = assertMediaContent(message.message);
650
955
  const mediaKey = content.mediaKey;
651
956
  const meId = authState.creds.me.id;
652
- const node = await (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId);
957
+ const node = await encryptMediaRetryRequest(message.key, mediaKey, meId);
653
958
  let error = undefined;
654
959
  await Promise.all([
655
960
  sendNode(node),
@@ -661,13 +966,16 @@ const makeMessagesSocket = (config) => {
661
966
  }
662
967
  else {
663
968
  try {
664
- const media = await (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
665
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
666
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];
667
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });
969
+ const media = await decryptMediaRetryData(result.media, mediaKey, result.key.id);
970
+ if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
971
+ const resultStr = proto.MediaRetryNotification.ResultType[media.result];
972
+ throw new Boom(`Media re-upload failed by device (${resultStr})`, {
973
+ data: media,
974
+ statusCode: getStatusCodeForMediaRetry(media.result) || 404
975
+ });
668
976
  }
669
977
  content.directPath = media.directPath;
670
- content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);
978
+ content.url = getUrlFromDirectPath(content.directPath);
671
979
  logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
672
980
  }
673
981
  catch (err) {
@@ -681,194 +989,112 @@ const makeMessagesSocket = (config) => {
681
989
  if (error) {
682
990
  throw error;
683
991
  }
684
- ev.emit('messages.update', [
685
- { key: message.key, update: { message: message.message } }
686
- ]);
992
+ ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
687
993
  return message;
688
994
  },
689
995
  sendMessage: async (jid, content, options = {}) => {
690
- var _a, _b, _c;
691
996
  const userJid = authState.creds.me.id;
692
- if (!options.ephemeralExpiration) {
693
- if ((0, WABinary_1.isJidGroup)(jid)) {
694
- const groups = await sock.groupQuery(jid, 'get', [{
695
- tag: 'query',
696
- attrs: {
697
- request: 'interactive'
698
- }
699
- }]);
700
- const metadata = (0, WABinary_1.getBinaryNodeChild)(groups, 'group');
701
- const expiration = ((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(metadata, 'ephemeral')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.expiration) || 0;
702
- options.ephemeralExpiration = expiration;
703
- }
704
- }
705
997
  if (typeof content === 'object' &&
706
998
  'disappearingMessagesInChat' in content &&
707
999
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
708
- (0, WABinary_1.isJidGroup)(jid)) {
1000
+ isJidGroup(jid)) {
709
1001
  const { disappearingMessagesInChat } = content;
710
- const value = typeof disappearingMessagesInChat === 'boolean' ?
711
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
712
- disappearingMessagesInChat;
1002
+ const value = typeof disappearingMessagesInChat === 'boolean'
1003
+ ? disappearingMessagesInChat
1004
+ ? WA_DEFAULT_EPHEMERAL
1005
+ : 0
1006
+ : disappearingMessagesInChat;
713
1007
  await groupToggleEphemeral(jid, value);
714
1008
  }
715
- if (typeof content === 'object' && 'album' in content && content.album) {
716
- const { album, caption } = content;
717
- if (caption && !album[0].caption) {
718
- album[0].caption = caption;
719
- }
720
- let mediaHandle;
721
- let mediaMsg;
722
- const albumMsg = (0, Utils_1.generateWAMessageFromContent)(jid, {
723
- albumMessage: {
724
- expectedImageCount: album.filter(item => 'image' in item).length,
725
- expectedVideoCount: album.filter(item => 'video' in item).length
726
- }
727
- }, { userJid, ...options });
728
- await relayMessage(jid, albumMsg.message, {
729
- messageId: albumMsg.key.id
730
- });
731
- for (const i in album) {
732
- const media = album[i];
733
- if ('image' in media) {
734
- mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
735
- image: media.image,
736
- ...(media.caption ? { caption: media.caption } : {}),
737
- ...options
738
- }, {
739
- userJid,
740
- upload: async (readStream, opts) => {
741
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
742
- mediaHandle = up.handle;
743
- return up;
744
- },
745
- ...options,
746
- });
747
- }
748
- else if ('video' in media) {
749
- mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
750
- video: media.video,
751
- ...(media.caption ? { caption: media.caption } : {}),
752
- ...(media.gifPlayback !== undefined ? { gifPlayback: media.gifPlayback } : {}),
753
- ...options
754
- }, {
755
- userJid,
756
- upload: async (readStream, opts) => {
757
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
758
- mediaHandle = up.handle;
759
- return up;
760
- },
761
- ...options,
762
- });
763
- }
764
- if (mediaMsg) {
765
- mediaMsg.message.messageContextInfo = {
766
- messageSecret: (0, crypto_1.randomBytes)(32),
767
- messageAssociation: {
768
- associationType: 1,
769
- parentMessageKey: albumMsg.key
770
- }
771
- };
772
- }
773
- await relayMessage(jid, mediaMsg.message, {
774
- messageId: mediaMsg.key.id
775
- });
776
- if (albumMessageItemDelayMs > 0) {
777
- await new Promise(resolve => setTimeout(resolve, albumMessageItemDelayMs));
778
- }
779
- }
780
- return albumMsg;
781
- }
782
1009
  else {
783
- let mediaHandle;
784
- const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
1010
+ const fullMsg = await generateWAMessage(jid, content, {
785
1011
  logger,
786
1012
  userJid,
787
- getUrlInfo: text => (0, link_preview_1.getUrlInfo)(text, {
1013
+ getUrlInfo: text => getUrlInfo(text, {
788
1014
  thumbnailWidth: linkPreviewImageThumbnailWidth,
789
1015
  fetchOpts: {
790
1016
  timeout: 3000,
791
- ...axiosOptions || {}
1017
+ ...(httpRequestOptions || {})
792
1018
  },
793
1019
  logger,
794
- uploadImage: generateHighQualityLinkPreview
795
- ? waUploadToServer
796
- : undefined
1020
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
797
1021
  }),
1022
+ //TODO: CACHE
798
1023
  getProfilePicUrl: sock.profilePictureUrl,
1024
+ getCallLink: sock.createCallLink,
799
1025
  upload: async (readStream, opts) => {
800
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
801
- mediaHandle = up.handle;
1026
+ const up = await waUploadToServer(readStream, {
1027
+ ...opts,
1028
+ newsletter: isJidNewsletter(jid)
1029
+ });
802
1030
  return up;
803
1031
  },
804
1032
  mediaCache: config.mediaCache,
805
1033
  options: config.options,
806
- messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
807
- ...options,
1034
+ messageId: generateMessageIDV2(sock.user?.id),
1035
+ ...options
808
1036
  });
1037
+ const isAiMsg = 'ai' in content && !!content.ai;
1038
+ const isEventMsg = 'event' in content && !!content.event;
809
1039
  const isDeleteMsg = 'delete' in content && !!content.delete;
810
1040
  const isEditMsg = 'edit' in content && !!content.edit;
811
1041
  const isPinMsg = 'pin' in content && !!content.pin;
812
- const isKeepMsg = 'keep' in content && content.keep;
813
1042
  const isPollMessage = 'poll' in content && !!content.poll;
814
- const isAiMsg = 'ai' in content && !!content.ai;
815
1043
  const additionalAttributes = {};
816
1044
  const additionalNodes = [];
817
1045
  // required for delete
818
1046
  if (isDeleteMsg) {
819
1047
  // if the chat is a group, and I am not the author, then delete the message as an admin
820
- if (((0, WABinary_1.isJidGroup)(content.delete.remoteJid) && !content.delete.fromMe) || (0, WABinary_1.isJidNewsletter)(jid)) {
1048
+ if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
821
1049
  additionalAttributes.edit = '8';
822
1050
  }
823
1051
  else {
824
1052
  additionalAttributes.edit = '7';
825
1053
  }
826
- // required for edit message
827
1054
  }
828
1055
  else if (isEditMsg) {
829
- additionalAttributes.edit = (0, WABinary_1.isJidNewsletter)(jid) ? '3' : '1';
830
- // required for pin message
1056
+ additionalAttributes.edit = '1';
1057
+ }
1058
+ else if (isAiMsg) {
1059
+ additionalNodes.push({
1060
+ attrs: {
1061
+ biz_bot: '1'
1062
+ }, tag: "bot"
1063
+ });
831
1064
  }
832
1065
  else if (isPinMsg) {
833
1066
  additionalAttributes.edit = '2';
834
- // required for keep message
835
- }
836
- else if (isKeepMsg) {
837
- additionalAttributes.edit = '6';
838
- // required for polling message
839
1067
  }
840
1068
  else if (isPollMessage) {
841
1069
  additionalNodes.push({
842
1070
  tag: 'meta',
843
1071
  attrs: {
844
1072
  polltype: 'creation'
845
- },
1073
+ }
846
1074
  });
847
- // required to display AI icon on message
848
1075
  }
849
- else if (isAiMsg) {
1076
+ else if (isEventMsg) {
850
1077
  additionalNodes.push({
1078
+ tag: 'meta',
851
1079
  attrs: {
852
- biz_bot: '1'
853
- },
854
- tag: "bot"
1080
+ event_type: 'creation'
1081
+ }
855
1082
  });
856
1083
  }
857
- if (mediaHandle) {
858
- additionalAttributes['media_id'] = mediaHandle;
859
- }
860
- if ('cachedGroupMetadata' in options) {
861
- console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
862
- }
863
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
1084
+ await relayMessage(jid, fullMsg.message, {
1085
+ messageId: fullMsg.key.id,
1086
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1087
+ additionalAttributes,
1088
+ statusJidList: options.statusJidList,
1089
+ additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes
1090
+ });
864
1091
  if (config.emitOwnEvents) {
865
1092
  process.nextTick(() => {
866
- processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
1093
+ processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
867
1094
  });
868
1095
  }
869
1096
  return fullMsg;
870
1097
  }
871
1098
  }
872
1099
  };
873
- };
874
- exports.makeMessagesSocket = makeMessagesSocket;
1100
+ };