@langitdeveloper/baileys 2.0.4 → 2.0.6

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.
@@ -1,923 +1,1468 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
1
+ "use strict";
5
2
  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("node-cache"));
9
- const WAProto_1 = require("../../WAProto");
10
- const Defaults_1 = require("../Defaults");
11
- const axios_1 = require("axios")
12
- const Types_1 = require("../Types")
13
- const Utils_1 = require("../Utils");
14
- const link_preview_1 = require("../Utils/link-preview");
15
- const WABinary_1 = require("../WABinary");
16
- const newsletter_1 = require("./newsletter");
17
- const WAUSync_1 = require("../WAUSync")
18
- const kikyy = require('./dugong');
19
- var ListType = WAProto_1.proto.Message.ListMessage.ListType;
3
+ const { default: NodeCache } = require("@cacheable/node-cache");
4
+ const { Boom: Boom } = require("@hapi/boom");
5
+ const { randomBytes: randomBytes } = require("crypto");
6
+ const { proto: proto } = require("../../WAProto");
7
+ const {
8
+ DEFAULT_CACHE_TTLS: DEFAULT_CACHE_TTLS,
9
+ WA_DEFAULT_EPHEMERAL: WA_DEFAULT_EPHEMERAL,
10
+ } = require("../Defaults");
11
+ const {
12
+ delay: delay,
13
+ assertMediaContent: assertMediaContent,
14
+ bindWaitForEvent: bindWaitForEvent,
15
+ decryptMediaRetryData: decryptMediaRetryData,
16
+ encodeNewsletterMessage: encodeNewsletterMessage,
17
+ encodeSignedDeviceIdentity: encodeSignedDeviceIdentity,
18
+ encodeWAMessage: encodeWAMessage,
19
+ encryptMediaRetryRequest: encryptMediaRetryRequest,
20
+ extractDeviceJids: extractDeviceJids,
21
+ generateMessageID: generateMessageID,
22
+ generateParticipantHashV2: generateParticipantHashV2,
23
+ generateWAMessage: generateWAMessage,
24
+ generateWAMessageFromContent: generateWAMessageFromContent,
25
+ getStatusCodeForMediaRetry: getStatusCodeForMediaRetry,
26
+ getUrlFromDirectPath: getUrlFromDirectPath,
27
+ getWAUploadToServer: getWAUploadToServer,
28
+ MessageRetryManager: MessageRetryManager,
29
+ normalizeMessageContent: normalizeMessageContent,
30
+ parseAndInjectE2ESessions: parseAndInjectE2ESessions,
31
+ unixTimestampSeconds: unixTimestampSeconds,
32
+ prepareAlbumMessageContent: prepareAlbumMessageContent,
33
+ aggregateMessageKeysNotFromMe: aggregateMessageKeysNotFromMe,
34
+ } = require("../Utils");
35
+ const { WAMessageAddressingMode: WAMessageAddressingMode } = require("../Types");
36
+ const {
37
+ areJidsSameUser: areJidsSameUser,
38
+ getBinaryNodeChild: getBinaryNodeChild,
39
+ getBinaryNodeChildren: getBinaryNodeChildren,
40
+ getBinaryFilteredBizBot: getBinaryFilteredBizBot,
41
+ getBinaryFilteredButtons: getBinaryFilteredButtons,
42
+ isHostedLidUser: isHostedLidUser,
43
+ isHostedPnUser: isHostedPnUser,
44
+ isJidNewsletter: isJidNewsletter,
45
+ isJidGroup: isJidGroup,
46
+ isLidUser: isLidUser,
47
+ isPnUser: isPnUser,
48
+ jidDecode: jidDecode,
49
+ jidEncode: jidEncode,
50
+ jidNormalizedUser: jidNormalizedUser,
51
+ STORIES_JID: STORIES_JID,
52
+ S_WHATSAPP_NET: S_WHATSAPP_NET,
53
+ } = require("../WABinary");
54
+ const { USyncUser: USyncUser, USyncQuery: USyncQuery } = require("../WAUSync");
55
+ const { makeNewsletterSocket: makeNewsletterSocket } = require("./newsletter");
56
+ const { getUrlInfo: getUrlInfo } = require("../Utils/link-preview");
57
+ const { makeKeyedMutex: makeKeyedMutex } = require("../Utils/make-mutex");
20
58
  const makeMessagesSocket = (config) => {
21
59
  const {
22
- logger,
23
- linkPreviewImageThumbnailWidth,
24
- generateHighQualityLinkPreview,
25
- options: axiosOptions,
26
- patchMessageBeforeSending
60
+ logger: logger,
61
+ linkPreviewImageThumbnailWidth: linkPreviewImageThumbnailWidth,
62
+ generateHighQualityLinkPreview: generateHighQualityLinkPreview,
63
+ options: httpRequestOptions,
64
+ patchMessageBeforeSending: patchMessageBeforeSending,
65
+ cachedGroupMetadata: cachedGroupMetadata,
66
+ enableRecentMessageCache: enableRecentMessageCache,
67
+ maxMsgRetryCount: maxMsgRetryCount,
27
68
  } = config;
28
- const sock = (0, newsletter_1.makeNewsletterSocket)(config);
69
+ const conn = makeNewsletterSocket(config);
29
70
  const {
30
- ev,
31
- authState,
32
- processingMutex,
33
- signalRepository,
34
- upsertMessage,
35
- query,
36
- fetchPrivacySettings,
37
- generateMessageTag,
38
- sendNode,
39
- groupMetadata,
40
- groupToggleEphemeral,
41
- executeUSyncQuery
42
- } = sock;
43
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
44
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
45
- useClones: false
71
+ ev: ev,
72
+ authState: authState,
73
+ messageMutex: messageMutex,
74
+ signalRepository: signalRepository,
75
+ upsertMessage: upsertMessage,
76
+ createCallLink: createCallLink,
77
+ query: query,
78
+ fetchPrivacySettings: fetchPrivacySettings,
79
+ sendNode: sendNode,
80
+ groupQuery: groupQuery,
81
+ groupMetadata: groupMetadata,
82
+ groupToggleEphemeral: groupToggleEphemeral,
83
+ executeUSyncQuery: executeUSyncQuery,
84
+ newsletterMetadata: newsletterMetadata,
85
+ } = conn;
86
+ const userDevicesCache =
87
+ config.userDevicesCache ||
88
+ new NodeCache({
89
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
90
+ useClones: false,
91
+ });
92
+ const peerSessionsCache = new NodeCache({
93
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES,
94
+ useClones: false,
46
95
  });
96
+ const messageRetryManager = enableRecentMessageCache
97
+ ? new MessageRetryManager(logger, maxMsgRetryCount)
98
+ : null;
99
+ const encryptionMutex = makeKeyedMutex();
47
100
  let mediaConn;
48
101
  const refreshMediaConn = async (forceGet = false) => {
49
102
  const media = await mediaConn;
50
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
103
+ if (
104
+ !media ||
105
+ forceGet ||
106
+ new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1e3
107
+ ) {
51
108
  mediaConn = (async () => {
52
109
  const result = await query({
53
- tag: 'iq',
54
- attrs: {
55
- type: 'set',
56
- xmlns: 'w:m',
57
- to: WABinary_1.S_WHATSAPP_NET,
58
- },
59
- content: [{ tag: 'media_conn', attrs: {} }]
110
+ tag: "iq",
111
+ attrs: { type: "set", xmlns: "w:m", to: S_WHATSAPP_NET },
112
+ content: [{ tag: "media_conn", attrs: {} }],
60
113
  });
61
- const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn');
114
+ const mediaConnNode = getBinaryNodeChild(result, "media_conn");
62
115
  const node = {
63
- hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
116
+ hosts: getBinaryNodeChildren(mediaConnNode, "host").map(({ attrs: attrs }) => ({
64
117
  hostname: attrs.hostname,
65
118
  maxContentLengthBytes: +attrs.maxContentLengthBytes,
66
119
  })),
67
120
  auth: mediaConnNode.attrs.auth,
68
121
  ttl: +mediaConnNode.attrs.ttl,
69
- fetchDate: new Date()
122
+ fetchDate: new Date(),
70
123
  };
71
- logger.debug('fetched media conn');
124
+ logger.debug("fetched media conn");
72
125
  return node;
73
126
  })();
74
127
  }
75
128
  return mediaConn;
76
129
  };
77
- /**
78
- * generic send receipt function
79
- * used for receipts of phone call, read, delivery etc.
80
- * */
81
130
  const sendReceipt = async (jid, participant, messageIds, type) => {
82
- const node = {
83
- tag: 'receipt',
84
- attrs: {
85
- id: messageIds[0],
86
- },
87
- };
88
- const isReadReceipt = type === 'read' || type === 'read-self';
131
+ if (!messageIds || messageIds.length === 0) {
132
+ throw new Boom("missing ids in receipt");
133
+ }
134
+ const node = { tag: "receipt", attrs: { id: messageIds[0] } };
135
+ const isReadReceipt = type === "read" || type === "read-self";
89
136
  if (isReadReceipt) {
90
- node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString();
137
+ node.attrs.t = unixTimestampSeconds().toString();
91
138
  }
92
- if (type === 'sender' && WABinary_1.isJidUser(jid)) {
139
+ if (type === "sender" && (isPnUser(jid) || isLidUser(jid))) {
93
140
  node.attrs.recipient = jid;
94
141
  node.attrs.to = participant;
95
- }
96
- else {
142
+ } else {
97
143
  node.attrs.to = jid;
98
144
  if (participant) {
99
145
  node.attrs.participant = participant;
100
146
  }
101
147
  }
102
148
  if (type) {
103
- node.attrs.type = WABinary_1.isJidNewsLetter(jid) ? 'read-self' : type;
149
+ node.attrs.type = type;
104
150
  }
105
151
  const remainingMessageIds = messageIds.slice(1);
106
152
  if (remainingMessageIds.length) {
107
153
  node.content = [
108
154
  {
109
- tag: 'list',
155
+ tag: "list",
110
156
  attrs: {},
111
- content: remainingMessageIds.map(id => ({
112
- tag: 'item',
113
- attrs: { id }
114
- }))
115
- }
157
+ content: remainingMessageIds.map((id) => ({
158
+ tag: "item",
159
+ attrs: { id: id },
160
+ })),
161
+ },
116
162
  ];
117
163
  }
118
- logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');
164
+ logger.debug({ attrs: node.attrs, messageIds: messageIds }, "sending receipt for messages");
119
165
  await sendNode(node);
120
166
  };
121
- /** Correctly bulk send receipts to multiple chats, participants */
122
167
  const sendReceipts = async (keys, type) => {
123
- const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys);
124
- for (const { jid, participant, messageIds } of recps) {
168
+ const recps = aggregateMessageKeysNotFromMe(keys);
169
+ for (const { jid: jid, participant: participant, messageIds: messageIds } of recps) {
125
170
  await sendReceipt(jid, participant, messageIds, type);
126
171
  }
127
172
  };
128
- /** Bulk read messages. Keys can be from different chats & participants */
129
173
  const readMessages = async (keys) => {
130
174
  const privacySettings = await fetchPrivacySettings();
131
- // based on privacy settings, we have to change the read type
132
- const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
175
+ const readType = privacySettings.readreceipts === "all" ? "read" : "read-self";
133
176
  await sendReceipts(keys, readType);
134
177
  };
135
- /** Fetch all the devices we've to send a message to */
136
178
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
137
- const deviceResults = []
138
-
179
+ const deviceResults = [];
139
180
  if (!useCache) {
140
- logger.debug('not using cache for devices')
181
+ logger.debug("not using cache for devices");
141
182
  }
142
-
143
- const toFetch = []
144
-
145
- jids = Array.from(new Set(jids))
146
-
147
- for (let jid of jids) {
148
- const user = WABinary_1.jidDecode(jid)?.user
149
-
150
- jid = WABinary_1.jidNormalizedUser(jid)
151
-
183
+ const toFetch = [];
184
+ const jidsWithUser = jids
185
+ .map((jid) => {
186
+ const decoded = jidDecode(jid);
187
+ const user = decoded?.user;
188
+ const device = decoded?.device;
189
+ const isExplicitDevice = typeof device === "number" && device >= 0;
190
+ if (isExplicitDevice && user) {
191
+ deviceResults.push({ user: user, device: device, jid: jid });
192
+ return null;
193
+ }
194
+ jid = jidNormalizedUser(jid);
195
+ return { jid: jid, user: user };
196
+ })
197
+ .filter((jid) => jid !== null);
198
+ let mgetDevices;
199
+ if (useCache && userDevicesCache.mget) {
200
+ const usersToFetch = jidsWithUser.map((j) => j?.user).filter(Boolean);
201
+ mgetDevices = await userDevicesCache.mget(usersToFetch);
202
+ }
203
+ for (const { jid: jid, user: user } of jidsWithUser) {
152
204
  if (useCache) {
153
- const devices = userDevicesCache.get(user)
154
-
205
+ const devices =
206
+ mgetDevices?.[user] ||
207
+ (userDevicesCache.mget ? undefined : await userDevicesCache.get(user));
155
208
  if (devices) {
156
- deviceResults.push(...devices)
157
- logger.trace({ user }, 'using cache for devices')
158
- }
159
-
160
- else {
161
- toFetch.push(jid)
209
+ const devicesWithJid = devices.map((d) => ({
210
+ ...d,
211
+ jid: jidEncode(d.user, d.server, d.device),
212
+ }));
213
+ deviceResults.push(...devicesWithJid);
214
+ logger.trace({ user: user }, "using cache for devices");
215
+ } else {
216
+ toFetch.push(jid);
162
217
  }
163
- }
164
-
165
- else {
166
- toFetch.push(jid)
218
+ } else {
219
+ toFetch.push(jid);
167
220
  }
168
221
  }
169
-
170
222
  if (!toFetch.length) {
171
- return deviceResults
223
+ return deviceResults;
172
224
  }
173
-
174
- const query = new WAUSync_1.USyncQuery()
175
- .withContext('message')
225
+ const requestedLidUsers = new Set();
226
+ for (const jid of toFetch) {
227
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
228
+ const user = jidDecode(jid)?.user;
229
+ if (user) requestedLidUsers.add(user);
230
+ }
231
+ }
232
+ const query = new USyncQuery()
233
+ .withContext("message")
176
234
  .withDeviceProtocol()
177
-
235
+ .withLIDProtocol();
178
236
  for (const jid of toFetch) {
179
- query.withUser(new WAUSync_1.USyncUser().withId(jid))
237
+ query.withUser(new USyncUser().withId(jid));
180
238
  }
181
-
182
- const result = await executeUSyncQuery(query)
183
-
239
+ const result = await executeUSyncQuery(query);
184
240
  if (result) {
185
- const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
186
- const deviceMap = {}
187
-
241
+ const lidResults = result.list.filter((a) => !!a.lid);
242
+ if (lidResults.length > 0) {
243
+ logger.trace("Storing LID maps from device call");
244
+ await signalRepository.lidMapping.storeLIDPNMappings(
245
+ lidResults.map((a) => ({ lid: a.lid, pn: a.id }))
246
+ );
247
+ try {
248
+ const lids = lidResults.map((a) => a.lid);
249
+ if (lids.length) {
250
+ await assertSessions(lids, true);
251
+ }
252
+ } catch (e) {
253
+ logger.warn(
254
+ { e: e, count: lidResults.length },
255
+ "failed to assert sessions for newly mapped LIDs"
256
+ );
257
+ }
258
+ }
259
+ const extracted = extractDeviceJids(
260
+ result?.list,
261
+ authState.creds.me.id,
262
+ authState.creds.me.lid,
263
+ ignoreZeroDevices
264
+ );
265
+ const deviceMap = {};
188
266
  for (const item of extracted) {
189
- deviceMap[item.user] = deviceMap[item.user] || []
190
- deviceMap[item.user].push(item)
191
- deviceResults.push(item)
267
+ deviceMap[item.user] = deviceMap[item.user] || [];
268
+ deviceMap[item.user]?.push(item);
192
269
  }
193
-
194
- for (const key in deviceMap) {
195
- userDevicesCache.set(key, deviceMap[key])
270
+ for (const [user, userDevices] of Object.entries(deviceMap)) {
271
+ const isLidUser = requestedLidUsers.has(user);
272
+ for (const item of userDevices) {
273
+ const finalJid = isLidUser
274
+ ? jidEncode(user, item.server, item.device)
275
+ : jidEncode(item.user, item.server, item.device);
276
+ deviceResults.push({ ...item, jid: finalJid });
277
+ logger.debug(
278
+ {
279
+ user: item.user,
280
+ device: item.device,
281
+ finalJid: finalJid,
282
+ usedLid: isLidUser,
283
+ },
284
+ "Processed device with LID priority"
285
+ );
286
+ }
287
+ }
288
+ if (userDevicesCache.mset) {
289
+ await userDevicesCache.mset(
290
+ Object.entries(deviceMap).map(([key, value]) => ({
291
+ key: key,
292
+ value: value,
293
+ }))
294
+ );
295
+ } else {
296
+ for (const key in deviceMap) {
297
+ if (deviceMap[key]) await userDevicesCache.set(key, deviceMap[key]);
298
+ }
299
+ }
300
+ const userDeviceUpdates = {};
301
+ for (const [userId, devices] of Object.entries(deviceMap)) {
302
+ if (devices && devices.length > 0) {
303
+ userDeviceUpdates[userId] = devices.map((d) => d.device?.toString() || "0");
304
+ }
305
+ }
306
+ if (Object.keys(userDeviceUpdates).length > 0) {
307
+ try {
308
+ await authState.keys.set({ "device-list": userDeviceUpdates });
309
+ logger.debug(
310
+ { userCount: Object.keys(userDeviceUpdates).length },
311
+ "stored user device lists for bulk migration"
312
+ );
313
+ } catch (error) {
314
+ logger.warn({ error: error }, "failed to store user device lists");
315
+ }
196
316
  }
197
317
  }
198
-
199
- return deviceResults
200
- }
318
+ return deviceResults;
319
+ };
320
+ const updateMemberLabel = (jid, memberLabel) => {
321
+ if (!isJidGroup(jid)) {
322
+ throw new Error("Jid must a group!");
323
+ }
324
+ const protocolMessage = {
325
+ protocolMessage: {
326
+ type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
327
+ memberLabel: {
328
+ label: memberLabel?.slice(0, 30),
329
+ labelTimestamp: unixTimestampSeconds(),
330
+ },
331
+ },
332
+ };
333
+ return relayMessage(jid, protocolMessage, {
334
+ additionalNodes: [
335
+ {
336
+ tag: "meta",
337
+ attrs: { tag_reason: "user_update", appdata: "member_tag" },
338
+ },
339
+ ],
340
+ });
341
+ };
201
342
  const assertSessions = async (jids, force) => {
202
343
  let didFetchNewSession = false;
203
- let jidsRequiringFetch = [];
204
- if (force) {
205
- jidsRequiringFetch = jids;
206
- }
207
- else {
208
- const addrs = jids.map(jid => (signalRepository
209
- .jidToSignalProtocolAddress(jid)));
210
- const sessions = await authState.keys.get('session', addrs);
211
- for (const jid of jids) {
212
- const signalId = signalRepository
213
- .jidToSignalProtocolAddress(jid);
214
- if (!sessions[signalId]) {
215
- jidsRequiringFetch.push(jid);
344
+ const uniqueJids = [...new Set(jids)];
345
+ const jidsRequiringFetch = [];
346
+ logger.debug({ jids: jids }, "assertSessions call with jids");
347
+ for (const jid of uniqueJids) {
348
+ const signalId = signalRepository.jidToSignalProtocolAddress(jid);
349
+ const cachedSession = peerSessionsCache.get(signalId);
350
+ if (cachedSession !== undefined) {
351
+ if (cachedSession && !force) {
352
+ continue;
353
+ }
354
+ } else {
355
+ const sessionValidation = await signalRepository.validateSession(jid);
356
+ const hasSession = sessionValidation.exists;
357
+ peerSessionsCache.set(signalId, hasSession);
358
+ if (hasSession && !force) {
359
+ continue;
216
360
  }
217
361
  }
362
+ jidsRequiringFetch.push(jid);
218
363
  }
219
364
  if (jidsRequiringFetch.length) {
220
- logger.debug({ jidsRequiringFetch }, 'fetching sessions');
365
+ const wireJids = [
366
+ ...jidsRequiringFetch.filter((jid) => !!isLidUser(jid) || !!isHostedLidUser(jid)),
367
+ ...(
368
+ (await signalRepository.lidMapping.getLIDsForPNs(
369
+ jidsRequiringFetch.filter((jid) => !!isPnUser(jid) || !!isHostedPnUser(jid))
370
+ )) || []
371
+ ).map((a) => a.lid),
372
+ ];
373
+ logger.debug(
374
+ { jidsRequiringFetch: jidsRequiringFetch, wireJids: wireJids },
375
+ "fetching sessions"
376
+ );
221
377
  const result = await query({
222
- tag: 'iq',
223
- attrs: {
224
- xmlns: 'encrypt',
225
- type: 'get',
226
- to: WABinary_1.S_WHATSAPP_NET,
227
- },
378
+ tag: "iq",
379
+ attrs: { xmlns: "encrypt", type: "get", to: S_WHATSAPP_NET },
228
380
  content: [
229
381
  {
230
- tag: 'key',
382
+ tag: "key",
231
383
  attrs: {},
232
- content: jidsRequiringFetch.map(jid => ({
233
- tag: 'user',
234
- attrs: { jid },
235
- }))
236
- }
237
- ]
384
+ content: wireJids.map((jid) => {
385
+ const attrs = { jid: jid };
386
+ if (force) attrs.reason = "identity";
387
+ return { tag: "user", attrs: attrs };
388
+ }),
389
+ },
390
+ ],
238
391
  });
239
- await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
392
+ await parseAndInjectE2ESessions(result, signalRepository);
240
393
  didFetchNewSession = true;
394
+ for (const wireJid of wireJids) {
395
+ const signalId = signalRepository.jidToSignalProtocolAddress(wireJid);
396
+ peerSessionsCache.set(signalId, true);
397
+ }
241
398
  }
242
399
  return didFetchNewSession;
243
400
  };
244
-
245
-
246
401
  const sendPeerDataOperationMessage = async (pdoMessage) => {
247
402
  if (!authState.creds.me?.id) {
248
- throw new boom_1.Boom('Not authenticated')
403
+ throw new Boom("Not authenticated");
249
404
  }
250
-
251
405
  const protocolMessage = {
252
406
  protocolMessage: {
253
407
  peerDataOperationRequestMessage: pdoMessage,
254
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
255
- }
408
+ type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE,
409
+ },
256
410
  };
257
- const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id);
411
+ const meJid = jidNormalizedUser(authState.creds.me.id);
258
412
  const msgId = await relayMessage(meJid, protocolMessage, {
259
- additionalAttributes: {
260
- category: 'peer',
261
- // eslint-disable-next-line camelcase
262
- push_priority: 'high_force',
263
- },
413
+ additionalAttributes: { category: "peer", push_priority: "high_force" },
414
+ additionalNodes: [{ tag: "meta", attrs: { appdata: "default" } }],
264
415
  });
265
416
  return msgId;
266
417
  };
267
- const createParticipantNodes = async (jids, message, extraAttrs) => {
268
- const patched = await patchMessageBeforeSending(message, jids);
269
- const bytes = (0, Utils_1.encodeWAMessage)(patched);
418
+ const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
419
+ if (!recipientJids.length) {
420
+ return { nodes: [], shouldIncludeDeviceIdentity: false };
421
+ }
422
+ const patched = await patchMessageBeforeSending(message, recipientJids);
423
+ const patchedMessages = Array.isArray(patched)
424
+ ? patched
425
+ : recipientJids.map((jid) => ({ recipientJid: jid, message: patched }));
270
426
  let shouldIncludeDeviceIdentity = false;
271
- const nodes = await Promise.all(jids.map(async (jid) => {
272
- const { type, ciphertext } = await signalRepository
273
- .encryptMessage({ jid, data: bytes });
274
- if (type === 'pkmsg') {
275
- shouldIncludeDeviceIdentity = true;
276
- }
277
- const node = {
278
- tag: 'to',
279
- attrs: { jid },
280
- content: [{
281
- tag: 'enc',
282
- attrs: {
283
- v: '2',
284
- type,
285
- ...extraAttrs || {}
286
- },
287
- content: ciphertext
288
- }]
289
- };
290
- return node;
291
- }));
292
- return { nodes, shouldIncludeDeviceIdentity };
293
- }; //apela
294
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, cachedGroupMetadata, useCachedGroupMetadata, statusJidList, AI = true }) => {
295
427
  const meId = authState.creds.me.id;
296
- let shouldIncludeDeviceIdentity = false;
297
- let didPushAdditional = false
298
- const { user, server } = WABinary_1.jidDecode(jid);
299
- const statusJid = 'status@broadcast';
300
- const isGroup = server === 'g.us';
428
+ const meLid = authState.creds.me?.lid;
429
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
430
+ const encryptionPromises = patchedMessages.map(
431
+ async ({ recipientJid: jid, message: patchedMessage }) => {
432
+ if (!jid) return null;
433
+ let msgToEncrypt = patchedMessage;
434
+ if (dsmMessage) {
435
+ const { user: targetUser } = jidDecode(jid);
436
+ const { user: ownPnUser } = jidDecode(meId);
437
+ const ownLidUser = meLidUser;
438
+ const isOwnUser =
439
+ targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
440
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
441
+ if (isOwnUser && !isExactSenderDevice) {
442
+ msgToEncrypt = dsmMessage;
443
+ logger.debug(
444
+ { jid: jid, targetUser: targetUser },
445
+ "Using DSM for own device"
446
+ );
447
+ }
448
+ }
449
+ const bytes = encodeWAMessage(msgToEncrypt);
450
+ const mutexKey = jid;
451
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
452
+ const { type: type, ciphertext: ciphertext } =
453
+ await signalRepository.encryptMessage({ jid: jid, data: bytes });
454
+ if (type === "pkmsg") {
455
+ shouldIncludeDeviceIdentity = true;
456
+ }
457
+ return {
458
+ tag: "to",
459
+ attrs: { jid: jid },
460
+ content: [
461
+ {
462
+ tag: "enc",
463
+ attrs: { v: "2", type: type, ...(extraAttrs || {}) },
464
+ content: ciphertext,
465
+ },
466
+ ],
467
+ };
468
+ });
469
+ return node;
470
+ }
471
+ );
472
+ const nodes = (await Promise.all(encryptionPromises)).filter((node) => node !== null);
473
+ return {
474
+ nodes: nodes,
475
+ shouldIncludeDeviceIdentity: shouldIncludeDeviceIdentity,
476
+ };
477
+ };
478
+ const profilePictureUrl = async (jid) => {
479
+ if (isJidNewsletter(jid)) {
480
+ const metadata = await conn.newsletterMetadata("JID", jid);
481
+ return getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || "");
482
+ } else {
483
+ const result = await query({
484
+ tag: "iq",
485
+ attrs: {
486
+ target: jidNormalizedUser(jid),
487
+ to: S_WHATSAPP_NET,
488
+ type: "get",
489
+ xmlns: "w:profile:picture",
490
+ },
491
+ content: [{ tag: "picture", attrs: { type: "image", query: "url" } }],
492
+ });
493
+ const child = getBinaryNodeChild(result, "picture");
494
+ return child?.attrs?.url || null;
495
+ }
496
+ };
497
+ const relayMessage = async (
498
+ jid,
499
+ message,
500
+ {
501
+ messageId: msgId,
502
+ participant: participant,
503
+ additionalAttributes: additionalAttributes,
504
+ useUserDevicesCache: useUserDevicesCache,
505
+ useCachedGroupMetadata: useCachedGroupMetadata,
506
+ statusJidList: statusJidList,
507
+ additionalNodes: additionalNodes,
508
+ AI: AI = false,
509
+ }
510
+ ) => {
511
+ const meId = authState.creds.me.id;
512
+ const meLid = authState.creds.me?.lid;
513
+ const isRetryResend = Boolean(participant?.jid);
514
+ let shouldIncludeDeviceIdentity = isRetryResend;
515
+ let didPushAdditional = false;
516
+ const statusJid = "status@broadcast";
517
+ const { user: user, server: server } = jidDecode(jid);
518
+ const isGroup = server === "g.us";
301
519
  const isStatus = jid === statusJid;
302
- const isLid = server === 'lid';
303
- const isPrivate = server === 's.whatsapp.net'
304
- const isNewsletter = server === 'newsletter';
305
- msgId = msgId || (0, Utils_1.generateMessageID)();
520
+ const isLid = server === "lid";
521
+ const isNewsletter = server === "newsletter";
522
+ const isGroupOrStatus = isGroup || isStatus;
523
+ const finalJid = jid;
524
+ msgId = msgId || generateMessageID(meId);
306
525
  useUserDevicesCache = useUserDevicesCache !== false;
307
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
526
+ useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
308
527
  const participants = [];
309
- const destinationJid = (!isStatus) ? WABinary_1.jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid;
528
+ const destinationJid = !isStatus ? finalJid : statusJid;
310
529
  const binaryNodeContent = [];
311
530
  const devices = [];
312
531
  const meMsg = {
313
- deviceSentMessage: {
314
- destinationJid,
315
- message
316
- }
532
+ deviceSentMessage: { destinationJid: destinationJid, message: message },
533
+ messageContextInfo: message.messageContextInfo,
317
534
  };
318
- const extraAttrs = {}
319
- const messages = Utils_1.normalizeMessageContent(message)
535
+ const extraAttrs = {};
536
+ const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/;
537
+ const messages = normalizeMessageContent(message);
320
538
  const buttonType = getButtonType(messages);
539
+ const pollMessage =
540
+ messages.pollCreationMessage ||
541
+ messages.pollCreationMessageV2 ||
542
+ messages.pollCreationMessageV3;
321
543
  if (participant) {
322
- // when the retry request is not for a group
323
- // only send to the specific device that asked for a retry
324
- // otherwise the message is sent out to every device that should be a recipient
325
544
  if (!isGroup && !isStatus) {
326
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' };
545
+ additionalAttributes = {
546
+ ...additionalAttributes,
547
+ device_fanout: "false",
548
+ };
327
549
  }
328
- const { user, device } = WABinary_1.jidDecode(participant.jid);
329
- devices.push({ user, device });
550
+ const { user: user, device: device } = jidDecode(participant.jid);
551
+ devices.push({ user: user, device: device, jid: participant.jid });
330
552
  }
331
553
  await authState.keys.transaction(async () => {
332
- const mediaType = getMediaType(messages);
333
-
554
+ const mediaType = getMediaType(message);
334
555
  if (mediaType) {
335
- extraAttrs['mediatype'] = mediaType
556
+ extraAttrs["mediatype"] = mediaType;
557
+ }
558
+ if (isNewsletter) {
559
+
560
+ const patched = patchMessageBeforeSending
561
+ ? await patchMessageBeforeSending(message, [])
562
+ : message
563
+
564
+ const mediaType = getMediaType(patched)
565
+
566
+ if (mediaType) {
567
+ extraAttrs["mediatype"] = mediaType
568
+ }
569
+
570
+ if (
571
+ patched.interactiveMessage &&
572
+ !extraAttrs["mediatype"]
573
+ ) {
574
+ extraAttrs["mediatype"] = "interactive"
575
+ }
576
+
577
+ const bytes = encodeNewsletterMessage(patched)
578
+
579
+ binaryNodeContent.push({
580
+ tag: "plaintext",
581
+ attrs: extraAttrs,
582
+ content: bytes
583
+ })
584
+
585
+ const stanza = {
586
+ tag: "message",
587
+ attrs: {
588
+ to: jid,
589
+ id: msgId,
590
+ type: getTypeMessage(patched),
591
+ ...(additionalAttributes || {}),
592
+ },
593
+ content: binaryNodeContent,
594
+ }
595
+
596
+ logger.debug(
597
+ {
598
+ msgId,
599
+ mediaType,
600
+ extraAttrs
601
+ },
602
+ `sending newsletter message to ${jid}`
603
+ )
604
+
605
+ await sendNode(stanza)
606
+
607
+ return
608
+ }
609
+ if (
610
+ messages.pinInChatMessage ||
611
+ messages.keepInChatMessage ||
612
+ message.reactionMessage ||
613
+ message.protocolMessage?.editedMessage
614
+ ) {
615
+ extraAttrs["decrypt-fail"] = "hide";
336
616
  }
337
-
338
- if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
339
- extraAttrs['decrypt-fail'] = 'hide'
340
- }
341
-
342
617
  if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
343
- extraAttrs['native_flow_name'] = messages.interactiveResponseMessage?.nativeFlowResponseMessage.name
618
+ extraAttrs["native_flow_name"] =
619
+ messages.interactiveResponseMessage.nativeFlowResponseMessage?.name ||
620
+ "menu_options";
344
621
  }
345
-
346
- if (isGroup || isStatus) {
622
+ if (isGroupOrStatus && !isRetryResend) {
347
623
  const [groupData, senderKeyMap] = await Promise.all([
348
624
  (async () => {
349
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
350
- if (groupData) {
351
- logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
625
+ let groupData =
626
+ useCachedGroupMetadata && cachedGroupMetadata
627
+ ? await cachedGroupMetadata(jid)
628
+ : undefined;
629
+ if (groupData && Array.isArray(groupData?.participants)) {
630
+ logger.trace(
631
+ { jid: jid, participants: groupData.participants.length },
632
+ "using cached group metadata"
633
+ );
634
+ } else if (!isStatus) {
635
+ groupData = await groupMetadata(jid);
352
636
  }
353
-
354
- else if (!isStatus) {
355
- groupData = await groupMetadata(jid)
356
- }
357
-
358
637
  return groupData;
359
638
  })(),
360
639
  (async () => {
361
640
  if (!participant && !isStatus) {
362
- const result = await authState.keys.get('sender-key-memory', [jid])
363
- return result[jid] || {}
641
+ const result = await authState.keys.get("sender-key-memory", [jid]);
642
+ return result[jid] || {};
364
643
  }
365
-
366
- return {}
367
-
368
- })()
644
+ return {};
645
+ })(),
369
646
  ]);
370
- if (!participant) {
371
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
372
-
373
- if (isStatus && statusJidList) {
374
- participantsList.push(...statusJidList)
375
- }
376
-
377
- // if (!isStatus) {
378
- // const expiration = await getEphemeralGroup(jid)
379
- // additionalAttributes = {
380
- // ...additionalAttributes,
381
- // addressing_mode: 'pn',
382
- // ...expiration ? { expiration: expiration.toString() } : null
383
- // }
384
- // }
385
-
386
- const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
387
- devices.push(...additionalDevices)
388
- }
389
-
390
- const patched = await patchMessageBeforeSending(message, devices.map(d => WABinary_1.jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)));
391
- const bytes = Utils_1.encodeWAMessage(patched);
392
-
393
- const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
647
+ const participantsList = groupData ? groupData.participants.map((p) => p.id) : [];
648
+ if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
649
+ additionalAttributes = {
650
+ ...additionalAttributes,
651
+ expiration: groupData.ephemeralDuration.toString(),
652
+ };
653
+ }
654
+ if (isStatus && statusJidList) {
655
+ participantsList.push(...statusJidList);
656
+ }
657
+ const additionalDevices = await getUSyncDevices(
658
+ participantsList,
659
+ !!useUserDevicesCache,
660
+ false
661
+ );
662
+ devices.push(...additionalDevices);
663
+ if (isGroup) {
664
+ additionalAttributes = {
665
+ ...additionalAttributes,
666
+ addressing_mode: groupData?.addressingMode || "lid",
667
+ };
668
+ }
669
+ const patched = await patchMessageBeforeSending(message);
670
+ if (Array.isArray(patched)) {
671
+ throw new Boom("Per-jid patching is not supported in groups");
672
+ }
673
+ const bytes = encodeWAMessage(patched);
674
+ const groupAddressingMode =
675
+ additionalAttributes?.["addressing_mode"] || groupData?.addressingMode || "lid";
676
+ const groupSenderIdentity = groupAddressingMode === "lid" && meLid ? meLid : meId;
677
+ const {
678
+ ciphertext: ciphertext,
679
+ senderKeyDistributionMessage: senderKeyDistributionMessage,
680
+ } = await signalRepository.encryptGroupMessage({
394
681
  group: destinationJid,
395
682
  data: bytes,
396
- meId,
683
+ meId: groupSenderIdentity,
397
684
  });
398
- const senderKeyJids = [];
399
-
400
- for (const { user, device } of devices) {
401
- const jid = WABinary_1.jidEncode(user, (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) === 'lid' ? 'lid' : 's.whatsapp.net', device);
402
- if (!senderKeyMap[jid] || !!participant) {
403
- senderKeyJids.push(jid);
404
- // store that this person has had the sender keys sent to them
405
- senderKeyMap[jid] = true;
685
+ const senderKeyRecipients = [];
686
+ for (const device of devices) {
687
+ const deviceJid = device.jid;
688
+ const hasKey = !!senderKeyMap[deviceJid];
689
+ if (
690
+ (!hasKey || !!participant) &&
691
+ !isHostedLidUser(deviceJid) &&
692
+ !isHostedPnUser(deviceJid) &&
693
+ device.device !== 99
694
+ ) {
695
+ senderKeyRecipients.push(deviceJid);
696
+ senderKeyMap[deviceJid] = true;
406
697
  }
407
698
  }
408
- // if there are some participants with whom the session has not been established
409
- // if there are, we re-send the senderkey
410
- if (senderKeyJids.length) {
411
- logger.debug({ senderKeyJids }, 'sending new sender key');
699
+ if (senderKeyRecipients.length) {
700
+ logger.debug({ senderKeyJids: senderKeyRecipients }, "sending new sender key");
412
701
  const senderKeyMsg = {
413
702
  senderKeyDistributionMessage: {
414
703
  axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
415
- groupId: destinationJid
416
- }
704
+ groupId: destinationJid,
705
+ },
417
706
  };
418
- await assertSessions(senderKeyJids, false);
419
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
420
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
707
+ const senderKeySessionTargets = senderKeyRecipients;
708
+ await assertSessions(senderKeySessionTargets);
709
+ const result = await createParticipantNodes(
710
+ senderKeyRecipients,
711
+ senderKeyMsg,
712
+ extraAttrs
713
+ );
714
+ shouldIncludeDeviceIdentity =
715
+ shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
421
716
  participants.push(...result.nodes);
422
717
  }
423
718
  binaryNodeContent.push({
424
- tag: 'enc',
425
- attrs: { v: '2', type: 'skmsg', ...extraAttrs },
426
- content: ciphertext
719
+ tag: "enc",
720
+ attrs: { v: "2", type: "skmsg", ...extraAttrs },
721
+ content: ciphertext,
427
722
  });
428
- await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
429
- }
430
- else if (isNewsletter) {
431
- // Message edit
432
- if (message.protocolMessage?.editedMessage) {
433
- msgId = message.protocolMessage.key?.id
434
- message = message.protocolMessage.editedMessage
435
- }
436
-
437
- // Message delete
438
- if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
439
- msgId = message.protocolMessage.key?.id
440
- message = {}
723
+ await authState.keys.set({
724
+ "sender-key-memory": { [jid]: senderKeyMap },
725
+ });
726
+ } else {
727
+ let ownId = meId;
728
+ if (isLid && meLid) {
729
+ ownId = meLid;
730
+ logger.debug(
731
+ { to: jid, ownId: ownId },
732
+ "Using LID identity for @lid conversation"
733
+ );
734
+ } else {
735
+ logger.debug(
736
+ { to: jid, ownId: ownId },
737
+ "Using PN identity for @s.whatsapp.net conversation"
738
+ );
441
739
  }
442
-
443
- const patched = await patchMessageBeforeSending(message, [])
444
- const bytes = Utils_1.encodeNewsletterMessage(patched)
445
-
446
- binaryNodeContent.push({
447
- tag: 'plaintext',
448
- attrs: extraAttrs ? extraAttrs : {},
449
- content: bytes
450
- })
451
- }
452
- else {
453
- const { user: meUser } = WABinary_1.jidDecode(meId);
454
- const meLid = authState.creds?.me?.lid ? WABinary_1.jidEncode(authState.creds.me.lid.split(':')[0], 'lid') : undefined;
455
- if (!participant) {
456
- devices.push({ user })
457
- if (user !== meUser) {
458
- devices.push({ user: meUser })
740
+ const { user: ownUser } = jidDecode(ownId);
741
+ if (!isRetryResend) {
742
+ const targetUserServer = isLid ? "lid" : "s.whatsapp.net";
743
+ devices.push({
744
+ user: user,
745
+ device: 0,
746
+ jid: jidEncode(user, targetUserServer, 0),
747
+ });
748
+ if (user !== ownUser) {
749
+ const ownUserServer = isLid ? "lid" : "s.whatsapp.net";
750
+ const ownUserForAddressing =
751
+ isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
752
+ devices.push({
753
+ user: ownUserForAddressing,
754
+ device: 0,
755
+ jid: jidEncode(ownUserForAddressing, ownUserServer, 0),
756
+ });
459
757
  }
460
-
461
- if (additionalAttributes?.['category'] !== 'peer') {
462
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true)
463
-
464
- devices.push(...additionalDevices)
758
+ if (additionalAttributes?.["category"] !== "peer") {
759
+ devices.length = 0;
760
+ const senderIdentity =
761
+ isLid && meLid
762
+ ? jidEncode(jidDecode(meLid)?.user, "lid", undefined)
763
+ : jidEncode(jidDecode(meId)?.user, "s.whatsapp.net", undefined);
764
+ const sessionDevices = await getUSyncDevices(
765
+ [senderIdentity, jid],
766
+ true,
767
+ false
768
+ );
769
+ devices.push(...sessionDevices);
770
+ logger.debug(
771
+ {
772
+ deviceCount: devices.length,
773
+ devices: devices.map(
774
+ (d) => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`
775
+ ),
776
+ },
777
+ "Device enumeration complete with unified addressing"
778
+ );
465
779
  }
466
780
  }
467
- const allRecipients = []
468
- const meRecipients = []
469
- const otherRecipients = []
470
- const { user: mePnUser } = WABinary_1.jidDecode(meId)
471
- const { user: meLidUser } = meLid ? WABinary_1.jidDecode(meLid) : { user: null }
472
- for (const { user, device } of devices) {
473
- const encodedJid = WABinary_1.jidEncode(user, isLid ? 'lid' : 's.whatsapp.net', device)
474
- const isExactSenderDevice = encodedJid === meId || (meLid && encodedJid === meLid)
781
+ const allRecipients = [];
782
+ const meRecipients = [];
783
+ const otherRecipients = [];
784
+ const { user: mePnUser } = jidDecode(meId);
785
+ const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
786
+ for (const { user: user, jid: jid } of devices) {
787
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
475
788
  if (isExactSenderDevice) {
476
- logger.debug({ jid: encodedJid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)')
477
- continue
789
+ logger.debug(
790
+ { jid: jid, meId: meId, meLid: meLid },
791
+ "Skipping exact sender device (whatsmeow pattern)"
792
+ );
793
+ continue;
478
794
  }
479
- const isMe = user === mePnUser || user === meLidUser
480
- let ptcp = false
481
- if (participant) {
482
- if (!WABinary_1.isJidGroup(encodedJid) && !isStatus) {
483
- if (!(!isMe)) ptcp = true
484
- } else {
485
- ptcp = false
486
- }
487
- }
488
- if (!ptcp) {
489
- if (isMe) {
490
- meRecipients.push(encodedJid)
491
- } else {
492
- otherRecipients.push(encodedJid)
493
- }
494
- allRecipients.push(encodedJid)
795
+ const isMe = user === mePnUser || user === meLidUser;
796
+ if (isMe) {
797
+ meRecipients.push(jid);
798
+ } else {
799
+ otherRecipients.push(jid);
495
800
  }
801
+ allRecipients.push(jid);
496
802
  }
497
- await assertSessions(allRecipients, false);
498
- const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
499
- createParticipantNodes(meRecipients, meMsg, extraAttrs),
500
- createParticipantNodes(otherRecipients, message, extraAttrs)
501
- ])
803
+ await assertSessions(allRecipients);
804
+ const [
805
+ { nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
806
+ { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 },
807
+ ] = await Promise.all([
808
+ createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
809
+ createParticipantNodes(otherRecipients, message, extraAttrs, meMsg),
810
+ ]);
502
811
  participants.push(...meNodes);
503
812
  participants.push(...otherNodes);
813
+ if (meRecipients.length > 0 || otherRecipients.length > 0) {
814
+ extraAttrs["phash"] = generateParticipantHashV2([
815
+ ...meRecipients,
816
+ ...otherRecipients,
817
+ ]);
818
+ }
504
819
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
505
820
  }
821
+ if (isRetryResend) {
822
+ const isParticipantLid = isLidUser(participant.jid);
823
+ const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId);
824
+ const encodedMessageToSend = isMe
825
+ ? encodeWAMessage({
826
+ deviceSentMessage: {
827
+ destinationJid: destinationJid,
828
+ message: message,
829
+ },
830
+ })
831
+ : encodeWAMessage(message);
832
+ const { type: type, ciphertext: encryptedContent } =
833
+ await signalRepository.encryptMessage({
834
+ data: encodedMessageToSend,
835
+ jid: participant.jid,
836
+ });
837
+ binaryNodeContent.push({
838
+ tag: "enc",
839
+ attrs: { v: "2", type: type, count: participant.count.toString() },
840
+ content: encryptedContent,
841
+ });
842
+ }
506
843
  if (participants.length) {
507
- if (additionalAttributes?.['category'] === 'peer') {
508
- const peerNode = participants[0]?.content?.[0]
509
-
844
+ if (additionalAttributes?.["category"] === "peer") {
845
+ const peerNode = participants[0]?.content?.[0];
510
846
  if (peerNode) {
511
- binaryNodeContent.push(peerNode) // push only enc
847
+ binaryNodeContent.push(peerNode);
512
848
  }
513
- }
514
-
515
- else {
849
+ } else {
516
850
  binaryNodeContent.push({
517
- tag: 'participants',
851
+ tag: "participants",
518
852
  attrs: {},
519
- content: participants
520
- })
853
+ content: participants,
854
+ });
521
855
  }
522
856
  }
523
-
524
857
  const stanza = {
525
- tag: 'message',
858
+ tag: "message",
526
859
  attrs: {
527
860
  id: msgId,
528
- type: getTypeMessage(messages),
529
- ...(additionalAttributes || {})
861
+ to: destinationJid,
862
+ type: getTypeMessage(message),
863
+ ...(additionalAttributes || {}),
530
864
  },
531
- content: binaryNodeContent
532
- }
533
- // if the participant to send to is explicitly specified (generally retry recp)
534
- // ensure the message is only sent to that person
535
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
865
+ content: binaryNodeContent,
866
+ };
536
867
  if (participant) {
537
- if (WABinary_1.isJidGroup(destinationJid)) {
868
+ if (isJidGroup(destinationJid)) {
538
869
  stanza.attrs.to = destinationJid;
539
870
  stanza.attrs.participant = participant.jid;
540
- }
541
- else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
871
+ } else if (areJidsSameUser(participant.jid, meId)) {
542
872
  stanza.attrs.to = participant.jid;
543
873
  stanza.attrs.recipient = destinationJid;
544
- }
545
- else {
874
+ } else {
546
875
  stanza.attrs.to = participant.jid;
547
876
  }
548
- }
549
- else {
877
+ } else {
550
878
  stanza.attrs.to = destinationJid;
551
879
  }
552
880
  if (shouldIncludeDeviceIdentity) {
553
881
  stanza.content.push({
554
- tag: 'device-identity',
882
+ tag: "device-identity",
555
883
  attrs: {},
556
- content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
884
+ content: encodeSignedDeviceIdentity(authState.creds.account, true),
557
885
  });
558
- logger.debug({ jid }, 'adding device identity');
886
+ logger.debug({ jid: jid }, "adding device identity");
559
887
  }
560
-
561
- if (AI && isPrivate) {
562
- const botNode = {
563
- tag: 'bot',
564
- attrs: {
565
- biz_bot: '1'
566
- }
888
+ const contactTcTokenData =
889
+ !isGroup && !isRetryResend && !isStatus
890
+ ? await authState.keys.get("tctoken", [destinationJid])
891
+ : {};
892
+ const tcTokenBuffer = contactTcTokenData[destinationJid]?.token;
893
+ if (tcTokenBuffer) {
894
+ stanza.content.push({
895
+ tag: "tctoken",
896
+ attrs: {},
897
+ content: tcTokenBuffer,
898
+ });
899
+ }
900
+ if (isGroup && regexGroupOld.test(jid) && !message.reactionMessage) {
901
+ stanza.content.push({ tag: "multicast", attrs: {} });
902
+ }
903
+ if (pollMessage || messages.eventMessage) {
904
+ stanza.content.push({
905
+ tag: "meta",
906
+ attrs: messages.eventMessage
907
+ ? { event_type: "creation" }
908
+ : isNewsletter
909
+ ? {
910
+ polltype: "creation",
911
+ contenttype: pollMessage?.pollContentType === 2 ? "image" : "text",
912
+ }
913
+ : { polltype: "creation" },
914
+ });
915
+ }
916
+ if (!isNewsletter && buttonType) {
917
+ const buttonsNode = getButtonArgs(messages);
918
+ const filteredButtons = getBinaryFilteredButtons(
919
+ additionalNodes ? additionalNodes : []
920
+ );
921
+ if (filteredButtons) {
922
+ stanza.content.push(...additionalNodes);
923
+ didPushAdditional = true;
924
+ } else {
925
+ stanza.content.push(buttonsNode);
567
926
  }
568
-
569
- const filteredBizBot = WABinary_1.getBinaryNodeFilter(additionalNodes ? additionalNodes : [])
570
-
927
+ }
928
+ if (AI && isPrivate) {
929
+ const botNode = { tag: "bot", attrs: { biz_bot: "1" } };
930
+ const filteredBizBot = getBinaryFilteredBizBot(
931
+ additionalNodes ? additionalNodes : []
932
+ );
571
933
  if (filteredBizBot) {
572
- stanza.content.push(...additionalNodes)
573
- didPushAdditional = true
574
- }
575
-
576
- else {
577
- stanza.content.push(botNode)
934
+ stanza.content.push(...additionalNodes);
935
+ didPushAdditional = true;
936
+ } else {
937
+ stanza.content.push(botNode);
578
938
  }
579
939
  }
580
-
581
- if(!isNewsletter && buttonType && !isStatus) {
582
- const content = WABinary_1.getAdditionalNode(buttonType)
583
- const filteredNode = WABinary_1.getBinaryNodeFilter(additionalNodes)
584
-
585
- if (filteredNode) {
586
- didPushAdditional = true
587
- stanza.content.push(...additionalNodes)
588
- }
589
- else {
590
- stanza.content.push(...content)
591
- }
592
- logger.debug({ jid }, 'adding business node')
593
- }
594
-
595
940
  if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
596
941
  stanza.content.push(...additionalNodes);
597
942
  }
598
-
599
- logger.debug({ msgId }, `sending message to ${participants.length} devices`);
943
+ logger.debug({ msgId: msgId }, `sending message to ${participants.length} devices`);
600
944
  await sendNode(stanza);
601
- });
602
-
603
- message = Types_1.WAProto.Message.fromObject(message)
604
-
605
- const messageJSON = {
606
- key: {
607
- remoteJid: jid,
608
- fromMe: true,
609
- id: msgId
610
- },
611
- message: message,
612
- messageTimestamp: Utils_1.unixTimestampSeconds(new Date()),
613
- messageStubParameters: [],
614
- participant: WABinary_1.isJidGroup(jid) || WABinary_1.isJidStatusBroadcast(jid) ? meId : undefined,
615
- status: Types_1.WAMessageStatus.PENDING
616
- }
617
-
618
- return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON)
619
- // return msgId;
945
+ if (messageRetryManager && !participant) {
946
+ messageRetryManager.addRecentMessage(destinationJid, msgId, message);
947
+ }
948
+ }, meId);
949
+ return msgId;
620
950
  };
621
951
  const getTypeMessage = (msg) => {
622
- const message = Utils_1.normalizeMessageContent(msg)
623
- if (message.reactionMessage) {
624
- return 'reaction'
625
- }
626
- else if (getMediaType(message)) {
627
- return 'media'
628
- }
629
- else {
630
- return 'text'
952
+ const message = normalizeMessageContent(msg);
953
+ if (
954
+ message.pollCreationMessage ||
955
+ message.pollCreationMessageV2 ||
956
+ message.pollCreationMessageV3
957
+ ) {
958
+ return "poll";
959
+ } else if (message.reactionMessage) {
960
+ return "reaction";
961
+ } else if (message.eventMessage) {
962
+ return "event";
963
+ } else if (getMediaType(message)) {
964
+ return "media";
965
+ } else {
966
+ return "text";
631
967
  }
632
- }
633
-
968
+ };
634
969
  const getMediaType = (message) => {
635
970
  if (message.imageMessage) {
636
- return 'image'
637
- }
638
- else if (message.videoMessage) {
639
- return message.videoMessage.gifPlayback ? 'gif' : 'video'
640
- }
641
- else if (message.audioMessage) {
642
- return message.audioMessage.ptt ? 'ptt' : 'audio'
643
- }
644
- else if (message.contactMessage) {
645
- return 'vcard'
971
+ return "image";
972
+ } else if (message.stickerMessage) {
973
+ return message.stickerMessage.isLottie
974
+ ? "1p_sticker"
975
+ : message.stickerMessage.isAvatar
976
+ ? "avatar_sticker"
977
+ : "sticker";
978
+ } else if (message.videoMessage) {
979
+ return message.videoMessage.gifPlayback ? "gif" : "video";
980
+ } else if (message.audioMessage) {
981
+ return message.audioMessage.ptt ? "ptt" : "audio";
982
+ } else if (message.ptvMessage) {
983
+ return "ptv";
984
+ } else if (message.albumMessage) {
985
+ return "collection";
986
+ } else if (message.contactMessage) {
987
+ return "vcard";
988
+ } else if (message.documentMessage) {
989
+ return "document";
990
+ } else if (message.stickerPackMessage) {
991
+ return "sticker_pack";
992
+ } else if (message.contactsArrayMessage) {
993
+ return "contact_array";
994
+ } else if (message.locationMessage) {
995
+ return "location";
996
+ } else if (message.liveLocationMessage) {
997
+ return "livelocation";
998
+ } else if (message.listMessage) {
999
+ return "list";
1000
+ } else if (message.listResponseMessage) {
1001
+ return "list_response";
1002
+ } else if (message.buttonsResponseMessage) {
1003
+ return "buttons_response";
1004
+ } else if (message.orderMessage) {
1005
+ return "order";
1006
+ } else if (message.productMessage) {
1007
+ return "product";
1008
+ } else if (message.interactiveResponseMessage) {
1009
+ return "native_flow_response";
1010
+ } else if (/https:\/\/wa\.me\/c\/\d+/.test(message.extendedTextMessage?.text)) {
1011
+ return "cataloglink";
1012
+ } else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
1013
+ return "productlink";
1014
+ } else if (message.extendedTextMessage?.matchedText || message.groupInviteMessage) {
1015
+ return "url";
646
1016
  }
647
- else if (message.documentMessage) {
648
- return 'document'
649
- }
650
- else if (message.contactsArrayMessage) {
651
- return 'contact_array'
652
- }
653
- else if (message.liveLocationMessage) {
654
- return 'livelocation'
655
- }
656
- else if (message.stickerMessage) {
657
- return 'sticker'
658
- }
659
- else if (message.listMessage) {
660
- return 'list'
661
- }
662
- else if (message.listResponseMessage) {
663
- return 'list_response'
664
- }
665
- else if (message.buttonsResponseMessage) {
666
- return 'buttons_response'
667
- }
668
- else if (message.orderMessage) {
669
- return 'order'
670
- }
671
- else if (message.productMessage) {
672
- return 'product'
673
- }
674
- else if (message.interactiveResponseMessage) {
675
- return 'native_flow_response'
676
- }
677
- else if (message.groupInviteMessage) {
678
- return 'url'
679
- }
680
- else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
681
- return 'productlink'
682
- }
683
- }
684
-
1017
+ };
685
1018
  const getButtonType = (message) => {
686
1019
  if (message.listMessage) {
687
- return 'list'
688
- }
689
- else if (message.buttonsMessage) {
690
- return 'buttons'
691
- }
692
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_and_pay') {
693
- return 'review_and_pay'
694
- }
695
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'review_order') {
696
- return 'review_order'
697
- }
698
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
699
- return 'payment_info'
700
- }
701
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
702
- return 'payment_status'
1020
+ return "list";
1021
+ } else if (message.buttonsMessage) {
1022
+ return "buttons";
1023
+ } else if (message.interactiveMessage?.nativeFlowMessage) {
1024
+ return "native_flow";
703
1025
  }
704
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
705
- return 'payment_method'
706
- }
707
- else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
708
- return 'interactive'
709
- }
710
- else if (message.interactiveMessage?.nativeFlowMessage) {
711
- return 'native_flow'
1026
+ };
1027
+ const getButtonArgs = (message) => {
1028
+ const nativeFlow = message.interactiveMessage?.nativeFlowMessage;
1029
+ const firstButtonName = nativeFlow?.buttons?.[0]?.name;
1030
+ const nativeFlowSpecials = [
1031
+ "mpm",
1032
+ "cta_catalog",
1033
+ "send_location",
1034
+ "call_permission_request",
1035
+ "wa_payment_transaction_details",
1036
+ "automated_greeting_message_view_catalog",
1037
+ ];
1038
+ if (
1039
+ nativeFlow &&
1040
+ (firstButtonName === "review_and_pay" || firstButtonName === "payment_info")
1041
+ ) {
1042
+ return {
1043
+ tag: "biz",
1044
+ attrs: {
1045
+ native_flow_name:
1046
+ firstButtonName === "review_and_pay" ? "order_details" : firstButtonName,
1047
+ },
1048
+ };
1049
+ } else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
1050
+ return {
1051
+ tag: "biz",
1052
+ attrs: {
1053
+ actual_actors: "2",
1054
+ host_storage: "2",
1055
+ privacy_mode_ts: unixTimestampSeconds().toString(),
1056
+ },
1057
+ content: [
1058
+ {
1059
+ tag: "interactive",
1060
+ attrs: { type: "native_flow", v: "1" },
1061
+ content: [{ tag: "native_flow", attrs: { v: "2", name: firstButtonName } }],
1062
+ },
1063
+ { tag: "quality_control", attrs: { source_type: "third_party" } },
1064
+ ],
1065
+ };
1066
+ } else if (nativeFlow || message.buttonsMessage) {
1067
+ return {
1068
+ tag: "biz",
1069
+ attrs: {
1070
+ actual_actors: "2",
1071
+ host_storage: "2",
1072
+ privacy_mode_ts: unixTimestampSeconds().toString(),
1073
+ },
1074
+ content: [
1075
+ {
1076
+ tag: "interactive",
1077
+ attrs: { type: "native_flow", v: "1" },
1078
+ content: [{ tag: "native_flow", attrs: { v: "9", name: "mixed" } }],
1079
+ },
1080
+ { tag: "quality_control", attrs: { source_type: "third_party" } },
1081
+ ],
1082
+ };
1083
+ } else if (message.listMessage) {
1084
+ return {
1085
+ tag: "biz",
1086
+ attrs: {
1087
+ actual_actors: "2",
1088
+ host_storage: "2",
1089
+ privacy_mode_ts: unixTimestampSeconds().toString(),
1090
+ },
1091
+ content: [
1092
+ { tag: "list", attrs: { v: "2", type: "product_list" } },
1093
+ { tag: "quality_control", attrs: { source_type: "third_party" } },
1094
+ ],
1095
+ };
1096
+ } else {
1097
+ return {
1098
+ tag: "biz",
1099
+ attrs: {
1100
+ actual_actors: "2",
1101
+ host_storage: "2",
1102
+ privacy_mode_ts: unixTimestampSeconds().toString(),
1103
+ },
1104
+ };
712
1105
  }
713
- }
1106
+ };
714
1107
  const getPrivacyTokens = async (jids) => {
715
- const t = Utils_1.unixTimestampSeconds().toString();
1108
+ const t = unixTimestampSeconds().toString();
716
1109
  const result = await query({
717
- tag: 'iq',
718
- attrs: {
719
- to: WABinary_1.S_WHATSAPP_NET,
720
- type: 'set',
721
- xmlns: 'privacy'
722
- },
1110
+ tag: "iq",
1111
+ attrs: { to: S_WHATSAPP_NET, type: "set", xmlns: "privacy" },
723
1112
  content: [
724
1113
  {
725
- tag: 'tokens',
1114
+ tag: "tokens",
726
1115
  attrs: {},
727
- content: jids.map(jid => ({
728
- tag: 'token',
1116
+ content: jids.map((jid) => ({
1117
+ tag: "token",
729
1118
  attrs: {
730
- jid: WABinary_1.jidNormalizedUser(jid),
731
- t,
732
- type: 'trusted_contact'
733
- }
734
- }))
735
- }
736
- ]
1119
+ jid: jidNormalizedUser(jid),
1120
+ t: t,
1121
+ type: "trusted_contact",
1122
+ },
1123
+ })),
1124
+ },
1125
+ ],
737
1126
  });
738
1127
  return result;
739
- }
740
- const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);
741
- const rahmi = new kikyy(Utils_1, waUploadToServer, relayMessage);
742
- const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');
1128
+ };
1129
+ const getEphemeralGroup = (jid) => {
1130
+ if (!isJidGroup(jid)) throw new TypeError("Jid should originate from a group!");
1131
+ return groupQuery(jid, "get", [{ tag: "query", attrs: { request: "interactive" } }])
1132
+ .then((groups) => getBinaryNodeChild(groups, "group"))
1133
+ .then((metadata) => getBinaryNodeChild(metadata, "ephemeral")?.attrs?.expiration || 0);
1134
+ };
1135
+ const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
1136
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, "messages.media-update");
743
1137
  return {
744
- ...sock,
745
- getPrivacyTokens,
746
- assertSessions,
747
- relayMessage,
748
- sendReceipt,
749
- sendReceipts,
750
- rahmi,
751
- readMessages,
752
- refreshMediaConn,
753
- getUSyncDevices,
754
- createParticipantNodes,
755
- waUploadToServer,
756
- sendPeerDataOperationMessage,
757
- fetchPrivacySettings,
1138
+ ...conn,
1139
+ getPrivacyTokens: getPrivacyTokens,
1140
+ assertSessions: assertSessions,
1141
+ profilePictureUrl: profilePictureUrl,
1142
+ relayMessage: relayMessage,
1143
+ sendReceipt: sendReceipt,
1144
+ sendReceipts: sendReceipts,
1145
+ readMessages: readMessages,
1146
+ getUSyncDevices: getUSyncDevices,
1147
+ refreshMediaConn: refreshMediaConn,
1148
+ waUploadToServer: waUploadToServer,
1149
+ getEphemeralGroup: getEphemeralGroup,
1150
+ fetchPrivacySettings: fetchPrivacySettings,
1151
+ messageRetryManager: messageRetryManager,
1152
+ createParticipantNodes: createParticipantNodes,
1153
+ sendPeerDataOperationMessage: sendPeerDataOperationMessage,
1154
+ updateMemberLabel: updateMemberLabel,
758
1155
  updateMediaMessage: async (message) => {
759
- const content = (0, Utils_1.assertMediaContent)(message.message);
1156
+ const content = assertMediaContent(message.message);
760
1157
  const mediaKey = content.mediaKey;
761
1158
  const meId = authState.creds.me.id;
762
- const node = (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId);
1159
+ const node = await encryptMediaRetryRequest(message.key, mediaKey, meId);
763
1160
  let error = undefined;
764
1161
  await Promise.all([
765
1162
  sendNode(node),
766
- waitForMsgMediaUpdate(update => {
767
- const result = update.find(c => c.key.id === message.key.id);
1163
+ waitForMsgMediaUpdate(async (update) => {
1164
+ const result = update.find((c) => c.key.id === message.key.id);
768
1165
  if (result) {
769
1166
  if (result.error) {
770
1167
  error = result.error;
771
- }
772
- else {
1168
+ } else {
773
1169
  try {
774
- const media = (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
775
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
776
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];
777
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });
1170
+ const media = await decryptMediaRetryData(
1171
+ result.media,
1172
+ mediaKey,
1173
+ result.key.id
1174
+ );
1175
+ if (
1176
+ media.result !== proto.MediaRetryNotification.ResultType.SUCCESS
1177
+ ) {
1178
+ const resultStr =
1179
+ proto.MediaRetryNotification.ResultType[media.result];
1180
+ throw new Boom(
1181
+ `Media re-upload failed by device (${resultStr})`,
1182
+ {
1183
+ data: media,
1184
+ statusCode:
1185
+ getStatusCodeForMediaRetry(media.result) || 404,
1186
+ }
1187
+ );
778
1188
  }
779
1189
  content.directPath = media.directPath;
780
- content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);
781
- logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
782
- }
783
- catch (err) {
1190
+ content.url = getUrlFromDirectPath(content.directPath);
1191
+ logger.debug(
1192
+ { directPath: media.directPath, key: result.key },
1193
+ "media update successful"
1194
+ );
1195
+ } catch (err) {
784
1196
  error = err;
785
1197
  }
786
1198
  }
787
1199
  return true;
788
1200
  }
789
- })
1201
+ }),
790
1202
  ]);
791
1203
  if (error) {
792
1204
  throw error;
793
1205
  }
794
- ev.emit('messages.update', [
795
- {
796
- key: message.key,
797
- update: {
798
- message: message.message
799
- }
800
- }
1206
+ ev.emit("messages.update", [
1207
+ { key: message.key, update: { message: message.message } },
801
1208
  ]);
802
1209
  return message;
803
1210
  },
1211
+ sendStatusMentions: async (content, jids = []) => {
1212
+ const userJid = jidNormalizedUser(authState.creds.me.id);
1213
+ let allUsers = new Set();
1214
+ allUsers.add(userJid);
1215
+ for (const id of jids) {
1216
+ const isGroup = isJidGroup(id);
1217
+ const isPrivate = isPnUser(id);
1218
+ if (isGroup) {
1219
+ try {
1220
+ const metadata =
1221
+ (await cachedGroupMetadata(id)) || (await groupMetadata(id));
1222
+ const participants = metadata.participants.map((p) =>
1223
+ jidNormalizedUser(p.id)
1224
+ );
1225
+ participants.forEach((jid) => allUsers.add(jid));
1226
+ } catch (error) {
1227
+ logger.error(`Error getting metadata for group ${id}: ${error}`);
1228
+ }
1229
+ } else if (isPrivate) {
1230
+ allUsers.add(jidNormalizedUser(id));
1231
+ }
1232
+ }
1233
+ const uniqueUsers = Array.from(allUsers);
1234
+ const getRandomHexColor = () =>
1235
+ "#" +
1236
+ Math.floor(Math.random() * 16777215)
1237
+ .toString(16)
1238
+ .padStart(6, "0");
1239
+ const isMedia = content.image || content.video || content.audio;
1240
+ const isAudio = !!content.audio;
1241
+ const messageContent = { ...content };
1242
+ if (isMedia && !isAudio) {
1243
+ if (messageContent.text) {
1244
+ messageContent.caption = messageContent.text;
1245
+ delete messageContent.text;
1246
+ }
1247
+ delete messageContent.ptt;
1248
+ delete messageContent.font;
1249
+ delete messageContent.backgroundColor;
1250
+ delete messageContent.textColor;
1251
+ }
1252
+ if (isAudio) {
1253
+ delete messageContent.text;
1254
+ delete messageContent.caption;
1255
+ delete messageContent.font;
1256
+ delete messageContent.textColor;
1257
+ }
1258
+ const font = !isMedia ? content.font || Math.floor(Math.random() * 9) : undefined;
1259
+ const textColor = !isMedia ? content.textColor || getRandomHexColor() : undefined;
1260
+ const backgroundColor =
1261
+ !isMedia || isAudio ? content.backgroundColor || getRandomHexColor() : undefined;
1262
+ const ptt = isAudio
1263
+ ? typeof content.ptt === "boolean"
1264
+ ? content.ptt
1265
+ : true
1266
+ : undefined;
1267
+ let msg;
1268
+ let mediaHandle;
1269
+ try {
1270
+ msg = await generateWAMessage(STORIES_JID, messageContent, {
1271
+ logger: logger,
1272
+ userJid: userJid,
1273
+ getUrlInfo: (text) =>
1274
+ getUrlInfo(text, {
1275
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1276
+ fetchOpts: { timeout: 3e3, ...(httpRequestOptions || {}) },
1277
+ logger: logger,
1278
+ uploadImage: generateHighQualityLinkPreview
1279
+ ? waUploadToServer
1280
+ : undefined,
1281
+ }),
1282
+ upload: async (encFilePath, opts) => {
1283
+ const up = await waUploadToServer(encFilePath, { ...opts });
1284
+ mediaHandle = up.handle;
1285
+ return up;
1286
+ },
1287
+ mediaCache: config.mediaCache,
1288
+ options: config.options,
1289
+ font: font,
1290
+ textColor: textColor,
1291
+ backgroundColor: backgroundColor,
1292
+ ptt: ptt,
1293
+ });
1294
+ } catch (error) {
1295
+ logger.error(`Error generating message: ${error}`);
1296
+ throw error;
1297
+ }
1298
+ await relayMessage(STORIES_JID, msg.message, {
1299
+ messageId: msg.key.id,
1300
+ statusJidList: uniqueUsers,
1301
+ additionalNodes: [
1302
+ {
1303
+ tag: "meta",
1304
+ attrs: {},
1305
+ content: [
1306
+ {
1307
+ tag: "mentioned_users",
1308
+ attrs: {},
1309
+ content: jids.map((jid) => ({
1310
+ tag: "to",
1311
+ attrs: { jid: jidNormalizedUser(jid) },
1312
+ })),
1313
+ },
1314
+ ],
1315
+ },
1316
+ ],
1317
+ });
1318
+ for (const id of jids) {
1319
+ try {
1320
+ const normalizedId = jidNormalizedUser(id);
1321
+ const isPrivate = isPnUser(normalizedId);
1322
+ const type = isPrivate ? "statusMentionMessage" : "groupStatusMentionMessage";
1323
+ const protocolMessage = {
1324
+ [type]: {
1325
+ message: { protocolMessage: { key: msg.key, type: 25 } },
1326
+ },
1327
+ messageContextInfo: { messageSecret: randomBytes(32) },
1328
+ };
1329
+ const statusMsg = await generateWAMessageFromContent(
1330
+ normalizedId,
1331
+ protocolMessage,
1332
+ {}
1333
+ );
1334
+ await relayMessage(normalizedId, statusMsg.message, {
1335
+ additionalNodes: [
1336
+ {
1337
+ tag: "meta",
1338
+ attrs: isPrivate
1339
+ ? { is_status_mention: "true" }
1340
+ : { is_group_status_mention: "true" },
1341
+ },
1342
+ ],
1343
+ });
1344
+ await delay(2e3);
1345
+ } catch (error) {
1346
+ logger.error(`Error sending to ${id}: ${error}`);
1347
+ }
1348
+ }
1349
+ return msg;
1350
+ },
804
1351
  sendMessage: async (jid, content, options = {}) => {
805
1352
  const userJid = authState.creds.me.id;
806
- delete options.ephemeralExpiration
807
- const { filter = false, quoted } = options;
808
- const getParticipantAttr = () => filter ? { participant: { jid } } : {};
809
- const messageType = rahmi.detectType(content);
810
- if (typeof content === 'object' && 'disappearingMessagesInChat' in content &&
811
- typeof content['disappearingMessagesInChat'] !== 'undefined' && WABinary_1.isJidGroup(jid)) {
812
- const { disappearingMessagesInChat } = content
813
-
814
- const value = typeof disappearingMessagesInChat === 'boolean' ?
815
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
816
- disappearingMessagesInChat
817
-
818
- await groupToggleEphemeral(jid, value)
1353
+ const additionalAttributes = {};
1354
+ if (!options.ephemeralExpiration) {
1355
+ if (isJidGroup(jid)) {
1356
+ const expiration = await getEphemeralGroup(jid);
1357
+ options.ephemeralExpiration = expiration;
1358
+ }
819
1359
  }
820
-
821
- else {
822
- let mediaHandle
823
-
824
-
825
- if (messageType) {
826
- switch(messageType) {
827
- case 'PAYMENT':
828
- const paymentContent = await rahmi.handlePayment(content, quoted);
829
- return await relayMessage(jid, paymentContent, {
830
- messageId: Utils_1.generateMessageID(),
831
- ...getParticipantAttr()
832
- });
833
-
834
- case 'PRODUCT':
835
- const productContent = await rahmi.handleProduct(content, jid, quoted);
836
- const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
837
- return await relayMessage(jid, productMsg.message, {
838
- messageId: productMsg.key.id,
839
- ...getParticipantAttr()
840
- });
841
-
842
- case 'INTERACTIVE':
843
- const interactiveContent = await rahmi.handleInteractive(content, jid, quoted);
844
- const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
845
- return await relayMessage(jid, interactiveMsg.message, {
846
- messageId: interactiveMsg.key.id,
847
- ...getParticipantAttr()
848
- });
849
- case 'ALBUM':
850
- return await rahmi.handleAlbum(content, jid, quoted)
851
- case 'EVENT':
852
- return await rahmi.handleEvent(content, jid, quoted)
853
- case 'POLL_RESULT':
854
- return await rahmi.handlePollResult(content, jid, quoted)
855
- case 'GROUP_STORY':
856
- return await rahmi.handleGroupStory(content, jid, quoted)
857
- }
858
- }
859
- const fullMsg = await Utils_1.generateWAMessage(jid, content, {
860
- logger,
861
- userJid,
862
- quoted,
863
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
864
- thumbnailWidth: linkPreviewImageThumbnailWidth,
865
- fetchOpts: {
866
- timeout: 3000,
867
- ...axiosOptions || {}
1360
+ if (
1361
+ typeof content === "object" &&
1362
+ "disappearingMessagesInChat" in content &&
1363
+ typeof content["disappearingMessagesInChat"] !== "undefined" &&
1364
+ isJidGroup(jid)
1365
+ ) {
1366
+ const { disappearingMessagesInChat: disappearingMessagesInChat } = content;
1367
+ const value =
1368
+ typeof disappearingMessagesInChat === "boolean"
1369
+ ? disappearingMessagesInChat
1370
+ ? WA_DEFAULT_EPHEMERAL
1371
+ : 0
1372
+ : disappearingMessagesInChat;
1373
+ await groupToggleEphemeral(jid, value);
1374
+ } else if (typeof content === "object" && "album" in content && content.album) {
1375
+ const albumMsg = await prepareAlbumMessageContent(jid, content.album, {
1376
+ conn: {
1377
+ relayMessage: relayMessage,
1378
+ waUploadToServer: waUploadToServer,
868
1379
  },
869
- logger,
870
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
871
- }),
872
- upload: async (readStream, opts) => {
873
- const up = await waUploadToServer(readStream, {
874
- ...opts,
875
- newsletter: WABinary_1.isJidNewsLetter(jid)
1380
+ userJid: userJid,
1381
+ ...options,
1382
+ });
1383
+ for (const media of albumMsg) {
1384
+ await delay(options.delay || 500);
1385
+ await relayMessage(jid, media.message, {
1386
+ messageId: media.key.id,
1387
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1388
+ additionalAttributes: additionalAttributes,
1389
+ statusJidList: options.statusJidList,
1390
+ additionalNodes: options.additionalNodes,
1391
+ AI: options.ai,
876
1392
  });
877
- return up;
878
- },
879
- mediaCache: config.mediaCache,
880
- options: config.options,
881
- ...options
882
- });
883
-
884
- const isDeleteMsg = 'delete' in content && !!content.delete;
885
- const isEditMsg = 'edit' in content && !!content.edit;
886
- const isAiMsg = 'ai' in content && !!content.ai;
887
-
888
- const additionalAttributes = {};
889
- const additionalNodes = [];
890
-
891
- if (isDeleteMsg) {
892
- const fromMe = content.delete?.fromMe;
893
- const isGroup = WABinary_1.isJidGroup(content.delete?.remoteJid);
894
- additionalAttributes.edit = (isGroup && !fromMe) || WABinary_1.isJidNewsLetter(jid) ? '8' : '7';
895
- } else if (isEditMsg) {
896
- additionalAttributes.edit = WABinary_1.isJidNewsLetter(jid) ? '3' : '1';
897
- } else if (isAiMsg) {
898
- additionalNodes.push({
899
- attrs: {
900
- biz_bot: '1'
901
- }, tag: "bot"
1393
+ }
1394
+ return albumMsg;
1395
+ } else {
1396
+ let mediaHandle;
1397
+ const fullMsg = await generateWAMessage(jid, content, {
1398
+ logger: logger,
1399
+ userJid: userJid,
1400
+ getUrlInfo: (text) =>
1401
+ getUrlInfo(text, {
1402
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1403
+ fetchOpts: { timeout: 3e3, ...(httpRequestOptions || {}) },
1404
+ logger: logger,
1405
+ uploadImage: generateHighQualityLinkPreview
1406
+ ? waUploadToServer
1407
+ : undefined,
1408
+ }),
1409
+ getProfilePicUrl: profilePictureUrl,
1410
+ getCallLink: createCallLink,
1411
+ upload: async (encFilePath, opts) => {
1412
+ const up = await waUploadToServer(encFilePath, {
1413
+ ...opts,
1414
+ newsletter: isJidNewsletter(jid),
1415
+ });
1416
+ mediaHandle = up.handle;
1417
+ return up;
1418
+ },
1419
+ mediaCache: config.mediaCache,
1420
+ options: config.options,
1421
+ messageId: generateMessageID(userJid),
1422
+ ...options,
902
1423
  });
903
- }
904
-
905
- await relayMessage(jid, fullMsg.message, {
906
- messageId: fullMsg.key.id,
907
- cachedGroupMetadata: options.cachedGroupMetadata,
908
- additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes,
909
- additionalAttributes,
910
- statusJidList: options.statusJidList
911
- });
912
-
913
- if (config.emitOwnEvents) {
914
- process.nextTick(() => {
915
- processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1424
+ const isPin = "pin" in content && !!content.pin;
1425
+ const isEdit = "edit" in content && !!content.edit;
1426
+ const isDelete = "delete" in content && !!content.delete;
1427
+ const isKeep = "keep" in content && !!content.keep && content.keep?.type === 2;
1428
+ if (isDelete || isKeep) {
1429
+ if (
1430
+ (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) ||
1431
+ isJidNewsletter(jid)
1432
+ ) {
1433
+ additionalAttributes.edit = "8";
1434
+ } else {
1435
+ additionalAttributes.edit = "7";
1436
+ }
1437
+ } else if (isEdit) {
1438
+ additionalAttributes.edit = isJidNewsletter(jid) ? "3" : "1";
1439
+ } else if (isPin) {
1440
+ additionalAttributes.edit = "2";
1441
+ }
1442
+ if (mediaHandle) {
1443
+ additionalAttributes["media_id"] = mediaHandle;
1444
+ }
1445
+ if ("cachedGroupMetadata" in options) {
1446
+ console.warn(
1447
+ "cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config."
1448
+ );
1449
+ }
1450
+ await relayMessage(jid, fullMsg.message, {
1451
+ messageId: fullMsg.key.id,
1452
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1453
+ additionalAttributes: additionalAttributes,
1454
+ statusJidList: options.statusJidList,
1455
+ additionalNodes: options.additionalNodes,
1456
+ AI: options.ai,
916
1457
  });
1458
+ if (config.emitOwnEvents) {
1459
+ process.nextTick(async () => {
1460
+ await messageMutex.mutex(() => upsertMessage(fullMsg, "append"));
1461
+ });
1462
+ }
1463
+ return fullMsg;
917
1464
  }
918
- return fullMsg;
919
- }
920
- }
921
- }
1465
+ },
1466
+ };
922
1467
  };
923
- exports.makeMessagesSocket = makeMessagesSocket;
1468
+ module.exports = { makeMessagesSocket: makeMessagesSocket };