@ubedsockets/baileys 1.6.3 → 1.6.5

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 (177) hide show
  1. package/WAProto/GenerateStatics.sh +3 -4
  2. package/WAProto/WAProto.proto +1215 -511
  3. package/WAProto/fix-imports.js +85 -0
  4. package/WAProto/index.d.ts +14017 -0
  5. package/WAProto/index.js +65475 -137445
  6. package/lib/Socket/Client/index.d.ts +3 -0
  7. package/lib/Socket/Client/index.d.ts.map +1 -0
  8. package/lib/Socket/Client/index.js +3 -18
  9. package/lib/Socket/Client/index.js.map +1 -0
  10. package/lib/Socket/Client/types.d.ts +16 -0
  11. package/lib/Socket/Client/types.d.ts.map +1 -0
  12. package/lib/Socket/Client/types.js +4 -6
  13. package/lib/Socket/Client/types.js.map +1 -0
  14. package/lib/Socket/Client/websocket.d.ts +13 -0
  15. package/lib/Socket/Client/websocket.d.ts.map +1 -0
  16. package/lib/Socket/Client/websocket.js +21 -78
  17. package/lib/Socket/Client/websocket.js.map +1 -0
  18. package/lib/Socket/business.d.ts +217 -0
  19. package/lib/Socket/business.d.ts.map +1 -0
  20. package/lib/Socket/business.js +162 -43
  21. package/lib/Socket/business.js.map +1 -0
  22. package/lib/Socket/chats.d.ts +124 -0
  23. package/lib/Socket/chats.d.ts.map +1 -0
  24. package/lib/Socket/chats.js +571 -249
  25. package/lib/Socket/chats.js.map +1 -0
  26. package/lib/Socket/communities.d.ts +273 -0
  27. package/lib/Socket/communities.d.ts.map +1 -0
  28. package/lib/Socket/communities.js +431 -0
  29. package/lib/Socket/communities.js.map +1 -0
  30. package/lib/Socket/groups.d.ts +161 -0
  31. package/lib/Socket/groups.d.ts.map +1 -0
  32. package/lib/Socket/groups.js +114 -99
  33. package/lib/Socket/groups.js.map +1 -0
  34. package/lib/Socket/index.d.ts +260 -0
  35. package/lib/Socket/index.d.ts.map +1 -0
  36. package/lib/Socket/index.js +11 -9
  37. package/lib/Socket/index.js.map +1 -0
  38. package/lib/Socket/messages-recv.d.ts +203 -0
  39. package/lib/Socket/messages-recv.d.ts.map +1 -0
  40. package/lib/Socket/messages-recv.js +1277 -559
  41. package/lib/Socket/messages-recv.js.map +1 -0
  42. package/lib/Socket/messages-send.d.ts +199 -0
  43. package/lib/Socket/messages-send.d.ts.map +1 -0
  44. package/lib/Socket/messages-send.js +674 -669
  45. package/lib/Socket/messages-send.js.map +1 -0
  46. package/lib/Socket/mex.d.ts +3 -0
  47. package/lib/Socket/mex.d.ts.map +1 -0
  48. package/lib/Socket/mex.js +42 -0
  49. package/lib/Socket/mex.js.map +1 -0
  50. package/lib/Socket/newsletter.d.ts +170 -0
  51. package/lib/Socket/newsletter.d.ts.map +1 -0
  52. package/lib/Socket/newsletter.js +149 -286
  53. package/lib/Socket/newsletter.js.map +1 -0
  54. package/lib/Socket/socket.d.ts +59 -0
  55. package/lib/Socket/socket.d.ts.map +1 -0
  56. package/lib/Socket/socket.js.map +1 -0
  57. package/lib/Utils/auth-utils.d.ts +24 -0
  58. package/lib/Utils/auth-utils.d.ts.map +1 -0
  59. package/lib/Utils/auth-utils.js +239 -136
  60. package/lib/Utils/auth-utils.js.map +1 -0
  61. package/lib/Utils/browser-utils.d.ts +4 -0
  62. package/lib/Utils/browser-utils.d.ts.map +1 -0
  63. package/lib/Utils/browser-utils.js +28 -0
  64. package/lib/Utils/browser-utils.js.map +1 -0
  65. package/lib/Utils/business.d.ts +23 -0
  66. package/lib/Utils/business.d.ts.map +1 -0
  67. package/lib/Utils/business.js +66 -69
  68. package/lib/Utils/business.js.map +1 -0
  69. package/lib/Utils/chat-utils.d.ts +100 -0
  70. package/lib/Utils/chat-utils.d.ts.map +1 -0
  71. package/lib/Utils/chat-utils.js +380 -238
  72. package/lib/Utils/chat-utils.js.map +1 -0
  73. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  74. package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
  75. package/lib/Utils/companion-reg-client-utils.js +35 -0
  76. package/lib/Utils/companion-reg-client-utils.js.map +1 -0
  77. package/lib/Utils/crypto.d.ts +37 -0
  78. package/lib/Utils/crypto.d.ts.map +1 -0
  79. package/lib/Utils/crypto.js +56 -127
  80. package/lib/Utils/crypto.js.map +1 -0
  81. package/lib/Utils/decode-wa-message.d.ts +66 -0
  82. package/lib/Utils/decode-wa-message.d.ts.map +1 -0
  83. package/lib/Utils/decode-wa-message.js +173 -63
  84. package/lib/Utils/decode-wa-message.js.map +1 -0
  85. package/lib/Utils/event-buffer.d.ts +36 -0
  86. package/lib/Utils/event-buffer.d.ts.map +1 -0
  87. package/lib/Utils/event-buffer.js +174 -70
  88. package/lib/Utils/event-buffer.js.map +1 -0
  89. package/lib/Utils/generics.d.ts +91 -0
  90. package/lib/Utils/generics.d.ts.map +1 -0
  91. package/lib/Utils/generics.js +159 -207
  92. package/lib/Utils/generics.js.map +1 -0
  93. package/lib/Utils/history.d.ts +24 -0
  94. package/lib/Utils/history.d.ts.map +1 -0
  95. package/lib/Utils/history.js +93 -53
  96. package/lib/Utils/history.js.map +1 -0
  97. package/lib/Utils/identity-change-handler.d.ts +44 -0
  98. package/lib/Utils/identity-change-handler.d.ts.map +1 -0
  99. package/lib/Utils/identity-change-handler.js +50 -0
  100. package/lib/Utils/identity-change-handler.js.map +1 -0
  101. package/lib/Utils/index.d.ts +22 -0
  102. package/lib/Utils/index.d.ts.map +1 -0
  103. package/lib/Utils/index.js +22 -33
  104. package/lib/Utils/index.js.map +1 -0
  105. package/lib/Utils/link-preview.d.ts +21 -0
  106. package/lib/Utils/link-preview.d.ts.map +1 -0
  107. package/lib/Utils/link-preview.js +17 -58
  108. package/lib/Utils/link-preview.js.map +1 -0
  109. package/lib/Utils/logger.d.ts +12 -0
  110. package/lib/Utils/logger.d.ts.map +1 -0
  111. package/lib/Utils/logger.js +3 -7
  112. package/lib/Utils/logger.js.map +1 -0
  113. package/lib/Utils/lt-hash.d.ts +8 -0
  114. package/lib/Utils/lt-hash.d.ts.map +1 -0
  115. package/lib/Utils/lt-hash.js +3 -46
  116. package/lib/Utils/lt-hash.js.map +1 -0
  117. package/lib/Utils/make-mutex.d.ts +9 -0
  118. package/lib/Utils/make-mutex.d.ts.map +1 -0
  119. package/lib/Utils/make-mutex.js +24 -34
  120. package/lib/Utils/make-mutex.js.map +1 -0
  121. package/lib/Utils/message-retry-manager.d.ts +115 -0
  122. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  123. package/lib/Utils/message-retry-manager.js +265 -0
  124. package/lib/Utils/message-retry-manager.js.map +1 -0
  125. package/lib/Utils/messages-media.d.ts +133 -0
  126. package/lib/Utils/messages-media.d.ts.map +1 -0
  127. package/lib/Utils/messages-media.js +483 -744
  128. package/lib/Utils/messages-media.js.map +1 -0
  129. package/lib/Utils/messages.d.ts +91 -0
  130. package/lib/Utils/messages.d.ts.map +1 -0
  131. package/lib/Utils/messages.js +411 -621
  132. package/lib/Utils/messages.js.map +1 -0
  133. package/lib/Utils/noise-handler.d.ts +20 -0
  134. package/lib/Utils/noise-handler.d.ts.map +1 -0
  135. package/lib/Utils/noise-handler.js +149 -99
  136. package/lib/Utils/noise-handler.js.map +1 -0
  137. package/lib/Utils/offline-node-processor.d.ts +17 -0
  138. package/lib/Utils/offline-node-processor.d.ts.map +1 -0
  139. package/lib/Utils/offline-node-processor.js +40 -0
  140. package/lib/Utils/offline-node-processor.js.map +1 -0
  141. package/lib/Utils/pre-key-manager.d.ts +28 -0
  142. package/lib/Utils/pre-key-manager.d.ts.map +1 -0
  143. package/lib/Utils/pre-key-manager.js +106 -0
  144. package/lib/Utils/pre-key-manager.js.map +1 -0
  145. package/lib/Utils/process-message.d.ts +60 -0
  146. package/lib/Utils/process-message.d.ts.map +1 -0
  147. package/lib/Utils/process-message.js +459 -150
  148. package/lib/Utils/process-message.js.map +1 -0
  149. package/lib/Utils/reporting-utils.d.ts +11 -0
  150. package/lib/Utils/reporting-utils.d.ts.map +1 -0
  151. package/lib/Utils/reporting-utils.js +258 -0
  152. package/lib/Utils/reporting-utils.js.map +1 -0
  153. package/lib/Utils/signal.d.ts +47 -0
  154. package/lib/Utils/signal.d.ts.map +1 -0
  155. package/lib/Utils/signal.js +112 -73
  156. package/lib/Utils/signal.js.map +1 -0
  157. package/lib/Utils/stanza-ack.d.ts +11 -0
  158. package/lib/Utils/stanza-ack.d.ts.map +1 -0
  159. package/lib/Utils/stanza-ack.js +38 -0
  160. package/lib/Utils/stanza-ack.js.map +1 -0
  161. package/lib/Utils/sync-action-utils.d.ts +19 -0
  162. package/lib/Utils/sync-action-utils.d.ts.map +1 -0
  163. package/lib/Utils/sync-action-utils.js +49 -0
  164. package/lib/Utils/sync-action-utils.js.map +1 -0
  165. package/lib/Utils/tc-token-utils.d.ts +37 -0
  166. package/lib/Utils/tc-token-utils.d.ts.map +1 -0
  167. package/lib/Utils/tc-token-utils.js +163 -0
  168. package/lib/Utils/tc-token-utils.js.map +1 -0
  169. package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
  170. package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
  171. package/lib/Utils/use-multi-file-auth-state.js +23 -27
  172. package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
  173. package/lib/Utils/validate-connection.d.ts +11 -0
  174. package/lib/Utils/validate-connection.d.ts.map +1 -0
  175. package/lib/Utils/validate-connection.js +74 -104
  176. package/lib/Utils/validate-connection.js.map +1 -0
  177. package/package.json +1 -1
@@ -1,74 +1,68 @@
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
-
8
- const boom_1 = require("@hapi/boom");
9
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
10
- const crypto_1 = require("crypto");
11
- const WAProto_1 = require("../../WAProto");
12
- const Defaults_1 = require("../Defaults");
13
- const Utils_1 = require("../Utils");
14
- const link_preview_1 = require("../Utils/link-preview");
15
- const WABinary_1 = require("../WABinary");
16
- const WAUSync_1 = require("../WAUSync");
17
- const newsletter_1 = require("./newsletter");
18
- const aveline = require("./setup");
19
-
20
- const makeMessagesSocket = (config) => {
21
- const { logger,
22
- linkPreviewImageThumbnailWidth,
23
- generateHighQualityLinkPreview,
24
- options: axiosOptions,
25
- patchMessageBeforeSending,
26
- cachedGroupMetadata,
27
- } = config;
28
-
29
- const sock = (0, newsletter_1.makeNewsletterSocket)(config);
30
- // ⒷⒶⒾⓁⒺⓎⓈ ⒷⓎ ⓋⓎⓏⒺⓃ
31
- const {
32
- ev,
33
- authState,
34
- processingMutex,
35
- signalRepository,
36
- upsertMessage,
37
- query,
38
- fetchPrivacySettings,
39
- sendNode,
40
- groupMetadata,
41
- groupToggleEphemeral,
42
- } = sock;
43
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
44
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
45
- useClones: false
46
- });
1
+ import NodeCache from '@cacheable/node-cache';
2
+ import { Boom } from '@hapi/boom';
3
+ import { proto } from '../../WAProto/index.js';
4
+ import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
6
+ import { getUrlInfo } from '../Utils/link-preview.js';
7
+ import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
8
+ import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
9
+ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
10
+ import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
11
+ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
12
+ import { makeNewsletterSocket } from './newsletter.js';
13
+ export const makeMessagesSocket = (config) => {
14
+ const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
15
+ const sock = makeNewsletterSocket(config);
16
+ const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
17
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
18
+ /**
19
+ * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
20
+ * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
21
+ * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
22
+ */
23
+ const inFlightTcTokenIssuance = new Set();
24
+ const userDevicesCache = config.userDevicesCache ||
25
+ new NodeCache({
26
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
27
+ useClones: false
28
+ });
29
+ /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
30
+ const devicesMutex = makeMutex();
31
+ // Initialize message retry manager if enabled
32
+ const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
33
+ // Prevent race conditions in Signal session encryption by user
34
+ const encryptionMutex = makeKeyedMutex();
47
35
  let mediaConn;
36
+ /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
37
+ let mediaHost = DEF_MEDIA_HOST;
48
38
  const refreshMediaConn = async (forceGet = false) => {
49
39
  const media = await mediaConn;
50
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
40
+ if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
51
41
  mediaConn = (async () => {
52
42
  const result = await query({
53
43
  tag: 'iq',
54
44
  attrs: {
55
45
  type: 'set',
56
46
  xmlns: 'w:m',
57
- to: WABinary_1.S_WHATSAPP_NET,
47
+ to: S_WHATSAPP_NET
58
48
  },
59
49
  content: [{ tag: 'media_conn', attrs: {} }]
60
50
  });
61
- const mediaConnNode = (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn');
51
+ const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
52
+ // TODO: explore full length of data that whatsapp provides
62
53
  const node = {
63
- hosts: (0, WABinary_1.getBinaryNodeChildren)(mediaConnNode, 'host').map(({ attrs }) => ({
54
+ hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
64
55
  hostname: attrs.hostname,
65
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
56
+ maxContentLengthBytes: +attrs.maxContentLengthBytes
66
57
  })),
67
58
  auth: mediaConnNode.attrs.auth,
68
59
  ttl: +mediaConnNode.attrs.ttl,
69
60
  fetchDate: new Date()
70
61
  };
71
62
  logger.debug('fetched media conn');
63
+ if (node.hosts[0]) {
64
+ mediaHost = node.hosts[0].hostname;
65
+ }
72
66
  return node;
73
67
  })();
74
68
  }
@@ -77,19 +71,22 @@ const makeMessagesSocket = (config) => {
77
71
  /**
78
72
  * generic send receipt function
79
73
  * used for receipts of phone call, read, delivery etc.
80
- */
74
+ * */
81
75
  const sendReceipt = async (jid, participant, messageIds, type) => {
76
+ if (!messageIds || messageIds.length === 0) {
77
+ throw new Boom('missing ids in receipt');
78
+ }
82
79
  const node = {
83
80
  tag: 'receipt',
84
81
  attrs: {
85
- id: messageIds[0],
86
- },
82
+ id: messageIds[0]
83
+ }
87
84
  };
88
85
  const isReadReceipt = type === 'read' || type === 'read-self';
89
86
  if (isReadReceipt) {
90
- node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString();
87
+ node.attrs.t = unixTimestampSeconds().toString();
91
88
  }
92
- if (type === 'sender' && (0, WABinary_1.isJidUser)(jid)) {
89
+ if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
93
90
  node.attrs.recipient = jid;
94
91
  node.attrs.to = participant;
95
92
  }
@@ -100,7 +97,7 @@ const makeMessagesSocket = (config) => {
100
97
  }
101
98
  }
102
99
  if (type) {
103
- node.attrs.type = (0, WABinary_1.isJidNewsletter)(jid) ? 'read-self' : type;
100
+ node.attrs.type = type;
104
101
  }
105
102
  const remainingMessageIds = messageIds.slice(1);
106
103
  if (remainingMessageIds.length) {
@@ -120,7 +117,7 @@ const makeMessagesSocket = (config) => {
120
117
  };
121
118
  /** Correctly bulk send receipts to multiple chats, participants */
122
119
  const sendReceipts = async (keys, type) => {
123
- const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys);
120
+ const recps = aggregateMessageKeysNotFromMe(keys);
124
121
  for (const { jid, participant, messageIds } of recps) {
125
122
  await sendReceipt(jid, participant, messageIds, type);
126
123
  }
@@ -134,20 +131,44 @@ const makeMessagesSocket = (config) => {
134
131
  };
135
132
  /** Fetch all the devices we've to send a message to */
136
133
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
137
- var _a;
138
134
  const deviceResults = [];
139
135
  if (!useCache) {
140
136
  logger.debug('not using cache for devices');
141
137
  }
142
138
  const toFetch = [];
143
- jids = Array.from(new Set(jids));
144
- for (let jid of jids) {
145
- const user = (_a = (0, WABinary_1.jidDecode)(jid)) === null || _a === void 0 ? void 0 : _a.user;
146
- jid = (0, WABinary_1.jidNormalizedUser)(jid);
139
+ const jidsWithUser = jids
140
+ .map(jid => {
141
+ const decoded = jidDecode(jid);
142
+ const user = decoded?.user;
143
+ const device = decoded?.device;
144
+ const isExplicitDevice = typeof device === 'number' && device >= 0;
145
+ if (isExplicitDevice && user) {
146
+ deviceResults.push({
147
+ user,
148
+ device,
149
+ jid
150
+ });
151
+ return null;
152
+ }
153
+ jid = jidNormalizedUser(jid);
154
+ return { jid, user };
155
+ })
156
+ .filter(jid => jid !== null);
157
+ let mgetDevices;
158
+ if (useCache && userDevicesCache.mget) {
159
+ const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
160
+ mgetDevices = await userDevicesCache.mget(usersToFetch);
161
+ }
162
+ for (const { jid, user } of jidsWithUser) {
147
163
  if (useCache) {
148
- const devices = userDevicesCache.get(user);
164
+ const devices = mgetDevices?.[user] ||
165
+ (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
149
166
  if (devices) {
150
- deviceResults.push(...devices);
167
+ const devicesWithJid = devices.map(d => ({
168
+ ...d,
169
+ jid: jidEncode(d.user, d.server, d.device)
170
+ }));
171
+ deviceResults.push(...devicesWithJid);
151
172
  logger.trace({ user }, 'using cache for devices');
152
173
  }
153
174
  else {
@@ -161,157 +182,270 @@ const makeMessagesSocket = (config) => {
161
182
  if (!toFetch.length) {
162
183
  return deviceResults;
163
184
  }
164
- const query = new WAUSync_1.USyncQuery()
165
- .withContext('message')
166
- .withDeviceProtocol();
185
+ const requestedLidUsers = new Set();
186
+ for (const jid of toFetch) {
187
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
188
+ const user = jidDecode(jid)?.user;
189
+ if (user)
190
+ requestedLidUsers.add(user);
191
+ }
192
+ }
193
+ const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
167
194
  for (const jid of toFetch) {
168
- query.withUser(new WAUSync_1.USyncUser().withId(jid));
195
+ 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
169
196
  }
170
197
  const result = await sock.executeUSyncQuery(query);
171
198
  if (result) {
172
- const extracted = (0, Utils_1.extractDeviceJids)(result === null || result === void 0 ? void 0 : result.list, authState.creds.me.id, ignoreZeroDevices);
199
+ // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
200
+ const lidResults = result.list.filter(a => !!a.lid);
201
+ if (lidResults.length > 0) {
202
+ logger.trace('Storing LID maps from device call');
203
+ await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
204
+ // Force-refresh sessions for newly mapped LIDs to align identity addressing
205
+ try {
206
+ const lids = lidResults.map(a => a.lid);
207
+ if (lids.length) {
208
+ await assertSessions(lids, true);
209
+ }
210
+ }
211
+ catch (e) {
212
+ logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs');
213
+ }
214
+ }
215
+ const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
173
216
  const deviceMap = {};
174
217
  for (const item of extracted) {
175
218
  deviceMap[item.user] = deviceMap[item.user] || [];
176
- deviceMap[item.user].push(item);
177
- deviceResults.push(item);
219
+ deviceMap[item.user]?.push(item);
220
+ }
221
+ // Process each user's devices as a group for bulk LID migration
222
+ for (const [user, userDevices] of Object.entries(deviceMap)) {
223
+ const isLidUser = requestedLidUsers.has(user);
224
+ // Process all devices for this user
225
+ for (const item of userDevices) {
226
+ const finalJid = isLidUser
227
+ ? jidEncode(user, item.server, item.device)
228
+ : jidEncode(item.user, item.server, item.device);
229
+ deviceResults.push({
230
+ ...item,
231
+ jid: finalJid
232
+ });
233
+ logger.debug({
234
+ user: item.user,
235
+ device: item.device,
236
+ finalJid,
237
+ usedLid: isLidUser
238
+ }, 'Processed device with LID priority');
239
+ }
240
+ }
241
+ await devicesMutex.mutex(async () => {
242
+ if (userDevicesCache.mset) {
243
+ // if the cache supports mset, we can set all devices in one go
244
+ await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
245
+ }
246
+ else {
247
+ for (const key in deviceMap) {
248
+ if (deviceMap[key])
249
+ await userDevicesCache.set(key, deviceMap[key]);
250
+ }
251
+ }
252
+ });
253
+ const userDeviceUpdates = {};
254
+ for (const [userId, devices] of Object.entries(deviceMap)) {
255
+ if (devices && devices.length > 0) {
256
+ userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
257
+ }
178
258
  }
179
- for (const key in deviceMap) {
180
- userDevicesCache.set(key, deviceMap[key]);
259
+ if (Object.keys(userDeviceUpdates).length > 0) {
260
+ try {
261
+ await authState.keys.set({ 'device-list': userDeviceUpdates });
262
+ logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
263
+ }
264
+ catch (error) {
265
+ logger.warn({ error }, 'failed to store user device lists');
266
+ }
181
267
  }
182
268
  }
183
269
  return deviceResults;
184
270
  };
271
+ /**
272
+ * Update Member Label
273
+ */
274
+ const updateMemberLabel = (jid, memberLabel) => {
275
+ return relayMessage(jid, {
276
+ protocolMessage: {
277
+ type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
278
+ memberLabel: {
279
+ label: memberLabel?.slice(0, 30),
280
+ labelTimestamp: unixTimestampSeconds()
281
+ }
282
+ }
283
+ }, {
284
+ additionalNodes: [
285
+ {
286
+ tag: 'meta',
287
+ attrs: {
288
+ tag_reason: 'user_update',
289
+ appdata: 'member_tag'
290
+ },
291
+ content: undefined
292
+ }
293
+ ]
294
+ });
295
+ };
185
296
  const assertSessions = async (jids, force) => {
186
297
  let didFetchNewSession = false;
187
- let jidsRequiringFetch = [];
188
- if (force) {
189
- jidsRequiringFetch = jids;
190
- }
191
- else {
192
- const addrs = jids.map(jid => (signalRepository
193
- .jidToSignalProtocolAddress(jid)));
194
- const sessions = await authState.keys.get('session', addrs);
195
- for (const jid of jids) {
196
- const signalId = signalRepository
197
- .jidToSignalProtocolAddress(jid);
198
- if (!sessions[signalId]) {
199
- jidsRequiringFetch.push(jid);
298
+ const uniqueJids = [...new Set(jids)];
299
+ const jidsRequiringFetch = [];
300
+ logger.debug({ jids }, 'assertSessions call with jids');
301
+ for (const jid of uniqueJids) {
302
+ if (!force) {
303
+ const sessionValidation = await signalRepository.validateSession(jid);
304
+ if (sessionValidation.exists) {
305
+ continue;
200
306
  }
201
307
  }
308
+ jidsRequiringFetch.push(jid);
202
309
  }
203
310
  if (jidsRequiringFetch.length) {
204
- 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');
205
317
  const result = await query({
206
318
  tag: 'iq',
207
319
  attrs: {
208
320
  xmlns: 'encrypt',
209
321
  type: 'get',
210
- to: WABinary_1.S_WHATSAPP_NET,
322
+ to: S_WHATSAPP_NET
211
323
  },
212
324
  content: [
213
325
  {
214
326
  tag: 'key',
215
327
  attrs: {},
216
- content: jidsRequiringFetch.map(jid => ({
217
- tag: 'user',
218
- attrs: { jid },
219
- }))
328
+ content: wireJids.map(jid => {
329
+ const attrs = { jid };
330
+ if (force)
331
+ attrs.reason = 'identity';
332
+ return { tag: 'user', attrs };
333
+ })
220
334
  }
221
335
  ]
222
336
  });
223
- await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
337
+ await parseAndInjectE2ESessions(result, signalRepository);
224
338
  didFetchNewSession = true;
225
339
  }
226
340
  return didFetchNewSession;
227
341
  };
228
342
  const sendPeerDataOperationMessage = async (pdoMessage) => {
229
- var _a;
230
343
  //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
231
- if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
232
- throw new boom_1.Boom('Not authenticated');
344
+ if (!authState.creds.me?.id) {
345
+ throw new Boom('Not authenticated');
233
346
  }
234
347
  const protocolMessage = {
235
348
  protocolMessage: {
236
349
  peerDataOperationRequestMessage: pdoMessage,
237
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
350
+ type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
238
351
  }
239
352
  };
240
- const meJid = (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id);
353
+ const meJid = jidNormalizedUser(authState.creds.me.id);
241
354
  const msgId = await relayMessage(meJid, protocolMessage, {
242
355
  additionalAttributes: {
243
356
  category: 'peer',
244
- // eslint-disable-next-line camelcase
245
- push_priority: 'high_force',
357
+ push_priority: 'high_force'
246
358
  },
359
+ additionalNodes: [
360
+ {
361
+ tag: 'meta',
362
+ attrs: { appdata: 'default' }
363
+ }
364
+ ]
247
365
  });
248
366
  return msgId;
249
367
  };
250
- const createParticipantNodes = async (jids, message, extraAttrs) => {
251
- let patched = await patchMessageBeforeSending(message, jids);
252
- if (!Array.isArray(patched)) {
253
- patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
368
+ const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
369
+ if (!recipientJids.length) {
370
+ return { nodes: [], shouldIncludeDeviceIdentity: false };
254
371
  }
372
+ const patched = await patchMessageBeforeSending(message, recipientJids);
373
+ const patchedMessages = Array.isArray(patched)
374
+ ? patched
375
+ : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
255
376
  let shouldIncludeDeviceIdentity = false;
256
- const nodes = await Promise.all(patched.map(async (patchedMessageWithJid) => {
257
- const { recipientJid: jid, ...patchedMessage } = patchedMessageWithJid;
258
- if (!jid) {
259
- return {};
377
+ const meId = authState.creds.me.id;
378
+ const meLid = authState.creds.me?.lid;
379
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
380
+ const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
381
+ try {
382
+ if (!jid)
383
+ return null;
384
+ let msgToEncrypt = patchedMessage;
385
+ if (dsmMessage) {
386
+ const { user: targetUser } = jidDecode(jid);
387
+ const { user: ownPnUser } = jidDecode(meId);
388
+ const ownLidUser = meLidUser;
389
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
390
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
391
+ if (isOwnUser && !isExactSenderDevice) {
392
+ msgToEncrypt = dsmMessage;
393
+ logger.debug({ jid, targetUser }, 'Using DSM for own device');
394
+ }
395
+ }
396
+ const bytes = encodeWAMessage(msgToEncrypt);
397
+ const mutexKey = jid;
398
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
399
+ const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
400
+ if (type === 'pkmsg') {
401
+ shouldIncludeDeviceIdentity = true;
402
+ }
403
+ return {
404
+ tag: 'to',
405
+ attrs: { jid },
406
+ content: [
407
+ {
408
+ tag: 'enc',
409
+ attrs: { v: '2', type, ...(extraAttrs || {}) },
410
+ content: ciphertext
411
+ }
412
+ ]
413
+ };
414
+ });
415
+ return node;
260
416
  }
261
- const bytes = (0, Utils_1.encodeWAMessage)(patchedMessage);
262
- const { type, ciphertext } = await signalRepository
263
- .encryptMessage({ jid, data: bytes });
264
- if (type === 'pkmsg') {
265
- shouldIncludeDeviceIdentity = true;
417
+ catch (err) {
418
+ logger.error({ jid, err }, 'Failed to encrypt for recipient');
419
+ return null;
266
420
  }
267
- const node = {
268
- tag: 'to',
269
- attrs: { jid },
270
- content: [{
271
- tag: 'enc',
272
- attrs: {
273
- v: '2',
274
- type,
275
- ...extraAttrs || {}
276
- },
277
- content: ciphertext
278
- }]
279
- };
280
- return node;
281
- }));
421
+ });
422
+ const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
423
+ if (recipientJids.length > 0 && nodes.length === 0) {
424
+ throw new Boom('All encryptions failed', { statusCode: 500 });
425
+ }
282
426
  return { nodes, shouldIncludeDeviceIdentity };
283
427
  };
284
- const relayMessage = async (
285
- jid,
286
- message,
287
- {
288
- messageId:
289
- msgId,
290
- participant,
291
- additionalAttributes,
292
- additionalNodes,
293
- useUserDevicesCache,
294
- useCachedGroupMetadata,
295
- statusJidList, AI = true }) => {
296
- var _a;
297
- const meId = authState.creds.me.id;
298
- let shouldIncludeDeviceIdentity = false;
299
- const { user, server } = (0, WABinary_1.jidDecode)(jid);
428
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
429
+ const meId = assertMeId(authState.creds);
430
+ const meLid = authState.creds.me?.lid;
431
+ const isRetryResend = Boolean(participant?.jid);
432
+ let shouldIncludeDeviceIdentity = isRetryResend;
300
433
  const statusJid = 'status@broadcast';
434
+ const { user, server } = jidDecode(jid);
301
435
  const isGroup = server === 'g.us';
302
- const isPrivatee = server === 's.whatsapp.net'
303
- const isNewsletter = server === 'newsletter';
304
436
  const isStatus = jid === statusJid;
305
437
  const isLid = server === 'lid';
306
- const messages = Utils_1.normalizeMessageContent(message)
307
- const pollMessage = messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
308
- msgId = msgId || (0, Utils_1.generateMessageIDV2)((_a = sock.user) === null || _a === void 0 ? void 0 : _a.id);
438
+ const isNewsletter = server === 'newsletter';
439
+ const isGroupOrStatus = isGroup || isStatus;
440
+ const finalJid = jid;
441
+ msgId = msgId || generateMessageIDV2(meId);
309
442
  useUserDevicesCache = useUserDevicesCache !== false;
310
443
  useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
311
444
  const participants = [];
312
- const destinationJid = (!isStatus) ? (0, WABinary_1.jidEncode)(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid;
445
+ const destinationJid = !isStatus ? finalJid : statusJid;
313
446
  const binaryNodeContent = [];
314
447
  const devices = [];
448
+ let reportingMessage;
315
449
  const meMsg = {
316
450
  deviceSentMessage: {
317
451
  destinationJid,
@@ -321,161 +455,274 @@ const makeMessagesSocket = (config) => {
321
455
  };
322
456
  const extraAttrs = {};
323
457
  if (participant) {
324
- // when the retry request is not for a group
325
- // only send to the specific device that asked for a retry
326
- // otherwise the message is sent out to every device that should be a recipient
327
458
  if (!isGroup && !isStatus) {
328
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' };
459
+ additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
329
460
  }
330
- const { user, device } = (0, WABinary_1.jidDecode)(participant.jid);
331
- devices.push({ user, device });
461
+ const { user, device } = jidDecode(participant.jid);
462
+ devices.push({
463
+ user,
464
+ device,
465
+ jid: participant.jid
466
+ });
332
467
  }
333
468
  await authState.keys.transaction(async () => {
334
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
335
469
  const mediaType = getMediaType(message);
336
470
  if (mediaType) {
337
471
  extraAttrs['mediatype'] = mediaType;
338
472
  }
339
- if ((_a = (0, Utils_1.normalizeMessageContent)(message)) === null || _a === void 0 ? void 0 : _a.pinInChatMessage) {
340
- extraAttrs['decrypt-fail'] = 'hide';
473
+ if (isNewsletter) {
474
+ const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
475
+ const bytes = encodeNewsletterMessage(patched);
476
+ binaryNodeContent.push({
477
+ tag: 'plaintext',
478
+ attrs: {},
479
+ content: bytes
480
+ });
481
+ const stanza = {
482
+ tag: 'message',
483
+ attrs: {
484
+ to: jid,
485
+ id: msgId,
486
+ type: getMessageType(message),
487
+ ...(additionalAttributes || {})
488
+ },
489
+ content: binaryNodeContent
490
+ };
491
+ logger.debug({ msgId }, `sending newsletter message to ${jid}`);
492
+ await sendNode(stanza);
493
+ return;
494
+ }
495
+ if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
496
+ extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
341
497
  }
342
- if (isGroup || isStatus) {
498
+ if (isGroupOrStatus && !isRetryResend) {
343
499
  const [groupData, senderKeyMap] = await Promise.all([
344
500
  (async () => {
345
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined;
346
- if (groupData && Array.isArray(groupData === null || groupData === void 0 ? void 0 : groupData.participants)) {
501
+ 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?
502
+ if (groupData && Array.isArray(groupData?.participants)) {
347
503
  logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
348
504
  }
349
505
  else if (!isStatus) {
350
- groupData = await groupMetadata(jid);
506
+ groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
351
507
  }
352
508
  return groupData;
353
509
  })(),
354
510
  (async () => {
355
511
  if (!participant && !isStatus) {
356
- const result = await authState.keys.get('sender-key-memory', [jid]);
512
+ // what if sender memory is less accurate than the cached metadata
513
+ // on participant change in group, we should do sender memory manipulation
514
+ 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?
357
515
  return result[jid] || {};
358
516
  }
359
517
  return {};
360
518
  })()
361
519
  ]);
362
- if (!participant) {
363
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : [];
364
- if (isStatus && statusJidList) {
365
- participantsList.push(...statusJidList);
366
- }
367
- if (!isStatus) {
368
- additionalAttributes = {
369
- ...additionalAttributes,
370
- // eslint-disable-next-line camelcase
371
- addressing_mode: (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) || 'pn'
372
- };
373
- }
374
- const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
375
- devices.push(...additionalDevices);
520
+ const participantsList = groupData ? groupData.participants.map(p => p.id) : [];
521
+ if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
522
+ additionalAttributes = {
523
+ ...additionalAttributes,
524
+ expiration: groupData.ephemeralDuration.toString()
525
+ };
526
+ }
527
+ if (isStatus && statusJidList) {
528
+ participantsList.push(...statusJidList);
529
+ }
530
+ const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
531
+ devices.push(...additionalDevices);
532
+ if (isGroup) {
533
+ additionalAttributes = {
534
+ ...additionalAttributes,
535
+ addressing_mode: groupData?.addressingMode || 'lid'
536
+ };
376
537
  }
377
538
  const patched = await patchMessageBeforeSending(message);
378
539
  if (Array.isArray(patched)) {
379
- throw new boom_1.Boom('Per-jid patching is not supported in groups');
540
+ throw new Boom('Per-jid patching is not supported in groups');
380
541
  }
381
- const bytes = (0, Utils_1.encodeWAMessage)(patched);
542
+ const bytes = encodeWAMessage(patched);
543
+ reportingMessage = patched;
544
+ const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
545
+ const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
382
546
  const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
383
547
  group: destinationJid,
384
548
  data: bytes,
385
- meId,
549
+ meId: groupSenderIdentity
386
550
  });
387
- const senderKeyJids = [];
388
- // ensure a connection is established with every device
389
- for (const { user, device } of devices) {
390
- const jid = (0, WABinary_1.jidEncode)(user, (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) === 'lid' ? 'lid' : 's.whatsapp.net', device);
391
- if (!senderKeyMap[jid] || !!participant) {
392
- senderKeyJids.push(jid);
393
- // store that this person has had the sender keys sent to them
394
- senderKeyMap[jid] = true;
551
+ const senderKeyRecipients = [];
552
+ for (const device of devices) {
553
+ const deviceJid = device.jid;
554
+ const hasKey = !!senderKeyMap[deviceJid];
555
+ if ((!hasKey || !!participant) &&
556
+ !isHostedLidUser(deviceJid) &&
557
+ !isHostedPnUser(deviceJid) &&
558
+ device.device !== 99) {
559
+ //todo: revamp all this logic
560
+ // 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
561
+ senderKeyRecipients.push(deviceJid);
562
+ senderKeyMap[deviceJid] = true;
395
563
  }
396
564
  }
397
- // if there are some participants with whom the session has not been established
398
- // if there are, we re-send the senderkey
399
- if (senderKeyJids.length) {
400
- logger.debug({ senderKeyJids }, 'sending new sender key');
565
+ if (senderKeyRecipients.length) {
566
+ logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
401
567
  const senderKeyMsg = {
402
568
  senderKeyDistributionMessage: {
403
569
  axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
404
570
  groupId: destinationJid
405
571
  }
406
572
  };
407
- await assertSessions(senderKeyJids, false);
408
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs);
573
+ const senderKeySessionTargets = senderKeyRecipients;
574
+ await assertSessions(senderKeySessionTargets);
575
+ const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
409
576
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
410
577
  participants.push(...result.nodes);
411
578
  }
412
579
  binaryNodeContent.push({
413
580
  tag: 'enc',
414
- attrs: { v: '2', type: 'skmsg' },
581
+ attrs: { v: '2', type: 'skmsg', ...extraAttrs },
415
582
  content: ciphertext
416
583
  });
417
584
  await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
418
585
  }
419
- else if (isNewsletter) {
420
- // Message edit
421
- if ((_b = message.protocolMessage) === null || _b === void 0 ? void 0 : _b.editedMessage) {
422
- msgId = (_c = message.protocolMessage.key) === null || _c === void 0 ? void 0 : _c.id;
423
- message = message.protocolMessage.editedMessage;
424
- }
425
- // Message delete
426
- if (((_d = message.protocolMessage) === null || _d === void 0 ? void 0 : _d.type) === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
427
- msgId = (_e = message.protocolMessage.key) === null || _e === void 0 ? void 0 : _e.id;
428
- message = {};
586
+ else {
587
+ // ADDRESSING CONSISTENCY: Match own identity to conversation context
588
+ // TODO: investigate if this is true
589
+ let ownId = meId;
590
+ if (isLid && meLid) {
591
+ ownId = meLid;
592
+ logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
429
593
  }
430
- const patched = await patchMessageBeforeSending(message, []);
431
- if (Array.isArray(patched)) {
432
- throw new boom_1.Boom('Per-jid patching is not supported in channel');
594
+ else {
595
+ logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
433
596
  }
434
- const bytes = (0, Utils_1.encodeNewsletterMessage)(patched);
435
- binaryNodeContent.push({
436
- tag: 'plaintext',
437
- attrs: mediaType ? { mediatype: mediaType } : {},
438
- content: bytes
439
- });
440
- }
441
- else {
442
- const { user: meUser } = (0, WABinary_1.jidDecode)(meId);
597
+ const { user: ownUser } = jidDecode(ownId);
443
598
  if (!participant) {
444
- devices.push({ user });
445
- if (user !== meUser) {
446
- devices.push({ user: meUser });
599
+ const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
600
+ reportingMessage = Array.isArray(patchedForReporting)
601
+ ? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
602
+ : patchedForReporting;
603
+ }
604
+ if (!isRetryResend) {
605
+ const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
606
+ devices.push({
607
+ user,
608
+ device: 0,
609
+ jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
610
+ });
611
+ if (user !== ownUser) {
612
+ const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
613
+ const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
614
+ devices.push({
615
+ user: ownUserForAddressing,
616
+ device: 0,
617
+ jid: jidEncode(ownUserForAddressing, ownUserServer, 0)
618
+ });
447
619
  }
448
- if ((additionalAttributes === null || additionalAttributes === void 0 ? void 0 : additionalAttributes['category']) !== 'peer') {
449
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true);
450
- devices.push(...additionalDevices);
620
+ if (additionalAttributes?.['category'] !== 'peer') {
621
+ // Clear placeholders and enumerate actual devices
622
+ devices.length = 0;
623
+ // Use conversation-appropriate sender identity
624
+ const senderIdentity = isLid && meLid
625
+ ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
626
+ : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
627
+ // Enumerate devices for sender and target with consistent addressing
628
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
629
+ devices.push(...sessionDevices);
630
+ logger.debug({
631
+ deviceCount: devices.length,
632
+ devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`)
633
+ }, 'Device enumeration complete with unified addressing');
451
634
  }
452
635
  }
453
- const allJids = [];
454
- const meJids = [];
455
- const otherJids = [];
456
- for (const { user, device } of devices) {
457
- const isMe = user === meUser;
458
- 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);
636
+ const allRecipients = [];
637
+ const meRecipients = [];
638
+ const otherRecipients = [];
639
+ const { user: mePnUser } = jidDecode(meId);
640
+ const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
641
+ for (const { user, jid } of devices) {
642
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
643
+ if (isExactSenderDevice) {
644
+ logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
645
+ continue;
646
+ }
647
+ // Check if this is our device (could match either PN or LID user)
648
+ const isMe = user === mePnUser || user === meLidUser;
459
649
  if (isMe) {
460
- meJids.push(jid);
650
+ meRecipients.push(jid);
461
651
  }
462
652
  else {
463
- otherJids.push(jid);
653
+ otherRecipients.push(jid);
464
654
  }
465
- allJids.push(jid);
655
+ allRecipients.push(jid);
466
656
  }
467
- await assertSessions(allJids, false);
657
+ await assertSessions(allRecipients);
468
658
  const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
469
- createParticipantNodes(meJids, meMsg, extraAttrs),
470
- createParticipantNodes(otherJids, message, extraAttrs)
659
+ // For own devices: use DSM if available (1:1 chats only)
660
+ createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
661
+ createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
471
662
  ]);
472
663
  participants.push(...meNodes);
473
664
  participants.push(...otherNodes);
665
+ if (meRecipients.length > 0 || otherRecipients.length > 0) {
666
+ extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients]);
667
+ }
474
668
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
475
669
  }
670
+ if (isRetryResend) {
671
+ const isParticipantLid = isLidUser(participant.jid);
672
+ const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId);
673
+ let messageToSend = message;
674
+ if (isGroupOrStatus) {
675
+ let groupSenderIdentity;
676
+ if (meLid && (await signalRepository.hasSenderKey({ group: destinationJid, meId: meLid }))) {
677
+ groupSenderIdentity = meLid;
678
+ }
679
+ else if (await signalRepository.hasSenderKey({ group: destinationJid, meId })) {
680
+ groupSenderIdentity = meId;
681
+ }
682
+ if (groupSenderIdentity) {
683
+ try {
684
+ const skdm = await signalRepository.getSenderKeyDistributionMessage({
685
+ group: destinationJid,
686
+ meId: groupSenderIdentity
687
+ });
688
+ messageToSend = {
689
+ ...message,
690
+ senderKeyDistributionMessage: {
691
+ groupId: destinationJid,
692
+ axolotlSenderKeyDistributionMessage: skdm
693
+ }
694
+ };
695
+ }
696
+ catch (err) {
697
+ logger.warn({ err, jid: destinationJid }, 'failed to build SKDM for retry, sending without it');
698
+ }
699
+ }
700
+ }
701
+ const encodedMessageToSend = isMe
702
+ ? encodeWAMessage({
703
+ deviceSentMessage: {
704
+ destinationJid,
705
+ message: messageToSend
706
+ }
707
+ })
708
+ : encodeWAMessage(messageToSend);
709
+ const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
710
+ data: encodedMessageToSend,
711
+ jid: participant.jid
712
+ });
713
+ binaryNodeContent.push({
714
+ tag: 'enc',
715
+ attrs: {
716
+ v: '2',
717
+ type,
718
+ count: participant.count.toString()
719
+ },
720
+ content: encryptedContent
721
+ });
722
+ }
476
723
  if (participants.length) {
477
- if ((additionalAttributes === null || additionalAttributes === void 0 ? void 0 : additionalAttributes['category']) === 'peer') {
478
- const peerNode = (_j = (_h = participants[0]) === null || _h === void 0 ? void 0 : _h.content) === null || _j === void 0 ? void 0 : _j[0];
724
+ if (additionalAttributes?.['category'] === 'peer') {
725
+ const peerNode = participants[0]?.content?.[0];
479
726
  if (peerNode) {
480
727
  binaryNodeContent.push(peerNode); // push only enc
481
728
  }
@@ -492,7 +739,8 @@ const makeMessagesSocket = (config) => {
492
739
  tag: 'message',
493
740
  attrs: {
494
741
  id: msgId,
495
- type: isNewsletter ? getTypeMessage(message) : 'text',
742
+ to: destinationJid,
743
+ type: getMessageType(message),
496
744
  ...(additionalAttributes || {})
497
745
  },
498
746
  content: binaryNodeContent
@@ -501,11 +749,11 @@ const makeMessagesSocket = (config) => {
501
749
  // ensure the message is only sent to that person
502
750
  // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
503
751
  if (participant) {
504
- if ((0, WABinary_1.isJidGroup)(destinationJid)) {
752
+ if (isJidGroup(destinationJid)) {
505
753
  stanza.attrs.to = destinationJid;
506
754
  stanza.attrs.participant = participant.jid;
507
755
  }
508
- else if ((0, WABinary_1.areJidsSameUser)(participant.jid, meId)) {
756
+ else if (areJidsSameUser(participant.jid, meId)) {
509
757
  stanza.attrs.to = participant.jid;
510
758
  stanza.attrs.recipient = destinationJid;
511
759
  }
@@ -517,116 +765,149 @@ const makeMessagesSocket = (config) => {
517
765
  stanza.attrs.to = destinationJid;
518
766
  }
519
767
  if (shouldIncludeDeviceIdentity) {
768
+ ;
520
769
  stanza.content.push({
521
770
  tag: 'device-identity',
522
771
  attrs: {},
523
- content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
772
+ content: encodeSignedDeviceIdentity(authState.creds.account, true)
524
773
  });
525
774
  logger.debug({ jid }, 'adding device identity');
526
775
  }
527
- if (pollMessage || messages.eventMessage) {
528
- let attrs = {};
529
- if (messages.eventMessage) {
530
- attrs.event_type = 'creation';
531
- } else {
532
- attrs.polltype = 'creation';
533
- if (isNewsletter) {
534
- attrs.contenttype = (pollMessage && pollMessage.pollContentType === 2) ? 'image' : 'text';
776
+ if (!isNewsletter &&
777
+ !isRetryResend &&
778
+ reportingMessage?.messageContextInfo?.messageSecret &&
779
+ shouldIncludeReportingToken(reportingMessage)) {
780
+ try {
781
+ const encoded = encodeWAMessage(reportingMessage);
782
+ const reportingKey = {
783
+ id: msgId,
784
+ fromMe: true,
785
+ remoteJid: destinationJid,
786
+ participant: participant?.jid
787
+ };
788
+ const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey);
789
+ if (reportingNode) {
790
+ ;
791
+ stanza.content.push(reportingNode);
792
+ logger.trace({ jid }, 'added reporting token to message');
535
793
  }
536
794
  }
795
+ catch (error) {
796
+ logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
797
+ }
798
+ }
799
+ // WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
800
+ const isPeerMessage = additionalAttributes?.['category'] === 'peer';
801
+ const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
802
+ // Resolve destination to LID for tctoken storage — matches Signal session key pattern
803
+ const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
804
+ const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
805
+ const existingTokenEntry = contactTcTokenData[tcTokenJid];
806
+ let tcTokenBuffer = existingTokenEntry?.token;
807
+ // Treat expired tokens the same as missing — clear from cache
808
+ if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
809
+ logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
810
+ tcTokenBuffer = undefined;
811
+ // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
812
+ const cleared = existingTokenEntry?.senderTimestamp !== undefined
813
+ ? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
814
+ : null;
815
+ try {
816
+ await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } });
817
+ }
818
+ catch (err) {
819
+ logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup');
820
+ }
821
+ }
822
+ if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
823
+ ;
537
824
  stanza.content.push({
538
- tag: 'meta',
539
- attrs: attrs
825
+ tag: 'tctoken',
826
+ attrs: {},
827
+ content: tcTokenBuffer
540
828
  });
541
829
  }
542
830
  if (additionalNodes && additionalNodes.length > 0) {
831
+ ;
543
832
  stanza.content.push(...additionalNodes);
544
833
  }
545
- if (AI && isPrivatee) {
546
- const botNode = {
547
- tag: 'bot',
548
- attrs: {
549
- biz_bot: '1'
550
- }
551
- }
552
- stanza.content.push(botNode);
553
- }
554
- const content = (0, Utils_1.normalizeMessageContent)(message);
555
- const contentType = (0, Utils_1.getContentType)(content);
556
- if (((0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidUser)(jid)) && (contentType === 'interactiveMessage' ||
557
- contentType === 'buttonsMessage' ||
558
- contentType === 'listMessage')) {
559
- const bizNode = { tag: 'biz', attrs: {} };
560
- 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))) {
561
- bizNode.content = [{
562
- tag: 'interactive',
563
- attrs: {
564
- type: 'native_flow',
565
- v: '1'
566
- },
567
- content: [{
568
- tag: 'native_flow',
569
- attrs: { v: '9', name: 'mixed' }
570
- }]
571
- }];
572
- }
573
- else if (message === null || message === void 0 ? void 0 : message.listMessage) {
574
- // list message only support in private chat
575
- bizNode.content = [{
576
- tag: 'list',
577
- attrs: {
578
- type: 'product_list',
579
- v: '2'
580
- }
581
- }];
582
- }
583
- stanza.content.push(bizNode);
584
- }
585
834
  logger.debug({ msgId }, `sending message to ${participants.length} devices`);
586
835
  await sendNode(stanza);
587
- });
836
+ // Fire-and-forget: issue our token to the contact AFTER message send.
837
+ // WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
838
+ const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
839
+ const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
840
+ if (is1on1Send &&
841
+ !isProtocolMsg &&
842
+ !isBotOrPSA &&
843
+ shouldSendNewTcToken(existingTokenEntry?.senderTimestamp) &&
844
+ !inFlightTcTokenIssuance.has(tcTokenJid)) {
845
+ inFlightTcTokenIssuance.add(tcTokenJid);
846
+ const issueTimestamp = unixTimestampSeconds();
847
+ const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
848
+ resolveIssuanceJid(destinationJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID)
849
+ .then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
850
+ .then(async (result) => {
851
+ await storeTcTokensFromIqResult({
852
+ result,
853
+ fallbackJid: tcTokenJid,
854
+ keys: authState.keys,
855
+ getLIDForPN
856
+ });
857
+ const currentData = await authState.keys.get('tctoken', [tcTokenJid]);
858
+ const currentEntry = currentData[tcTokenJid];
859
+ const indexWrite = await buildMergedTcTokenIndexWrite(authState.keys, [tcTokenJid]);
860
+ await authState.keys.set({
861
+ tctoken: {
862
+ [tcTokenJid]: {
863
+ token: Buffer.alloc(0),
864
+ ...currentEntry,
865
+ senderTimestamp: issueTimestamp
866
+ },
867
+ ...indexWrite
868
+ }
869
+ });
870
+ })
871
+ .catch(err => {
872
+ logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed');
873
+ })
874
+ .finally(() => {
875
+ inFlightTcTokenIssuance.delete(tcTokenJid);
876
+ });
877
+ }
878
+ // Add message to retry cache if enabled
879
+ if (messageRetryManager && !participant) {
880
+ messageRetryManager.addRecentMessage(destinationJid, msgId, message);
881
+ }
882
+ }, meId);
588
883
  return msgId;
589
884
  };
590
- const getTypeMessage = (msg) => {
591
- if (msg.viewOnceMessage) {
592
- return getTypeMessage(msg.viewOnceMessage.message);
593
- }
594
- else if (msg.viewOnceMessageV2) {
595
- return getTypeMessage(msg.viewOnceMessageV2.message);
596
- }
597
- else if (msg.viewOnceMessageV2Extension) {
598
- return getTypeMessage(msg.viewOnceMessageV2Extension.message);
599
- }
600
- else if (msg.ephemeralMessage) {
601
- return getTypeMessage(msg.ephemeralMessage.message);
602
- }
603
- else if (msg.documentWithCaptionMessage) {
604
- return getTypeMessage(msg.documentWithCaptionMessage.message);
605
- }
606
- else if (msg.reactionMessage) {
885
+ const getMessageType = (message) => {
886
+ const normalizedMessage = normalizeMessageContent(message);
887
+ if (!normalizedMessage)
888
+ return 'text';
889
+ if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
607
890
  return 'reaction';
608
891
  }
609
- else if (msg.pollCreationMessage || msg.pollCreationMessageV2 || msg.pollCreationMessageV3 || msg.pollUpdateMessage) {
892
+ if (normalizedMessage.pollCreationMessage ||
893
+ normalizedMessage.pollCreationMessageV2 ||
894
+ normalizedMessage.pollCreationMessageV3 ||
895
+ normalizedMessage.pollUpdateMessage) {
610
896
  return 'poll';
611
897
  }
612
- else if (getMediaType(msg)) {
613
- return 'media';
898
+ if (normalizedMessage.eventMessage) {
899
+ return 'event';
614
900
  }
615
- else {
616
- return 'text';
901
+ if (getMediaType(normalizedMessage) !== '') {
902
+ return 'media';
617
903
  }
904
+ return 'text';
618
905
  };
619
906
  const getMediaType = (message) => {
620
907
  if (message.imageMessage) {
621
908
  return 'image';
622
909
  }
623
- else if (message.ptvMessage) {
624
- return 'ptv';
625
- }
626
910
  else if (message.videoMessage) {
627
- if (message.videoMessage.ptv) {
628
- return 'ptv';
629
- }
630
911
  return message.videoMessage.gifPlayback ? 'gif' : 'video';
631
912
  }
632
913
  else if (message.audioMessage) {
@@ -665,19 +946,17 @@ const makeMessagesSocket = (config) => {
665
946
  else if (message.interactiveResponseMessage) {
666
947
  return 'native_flow_response';
667
948
  }
668
- else if (message.richResponseMessage) {
669
- return 'rich_response';
670
- }
671
949
  else if (message.groupInviteMessage) {
672
950
  return 'url';
673
951
  }
952
+ return '';
674
953
  };
675
- const getPrivacyTokens = async (jids) => {
676
- const t = (0, Utils_1.unixTimestampSeconds)().toString();
954
+ const issuePrivacyTokens = async (jids, timestamp) => {
955
+ const t = (timestamp ?? unixTimestampSeconds()).toString();
677
956
  const result = await query({
678
957
  tag: 'iq',
679
958
  attrs: {
680
- to: WABinary_1.S_WHATSAPP_NET,
959
+ to: S_WHATSAPP_NET,
681
960
  type: 'set',
682
961
  xmlns: 'privacy'
683
962
  },
@@ -688,7 +967,7 @@ const makeMessagesSocket = (config) => {
688
967
  content: jids.map(jid => ({
689
968
  tag: 'token',
690
969
  attrs: {
691
- jid: (0, WABinary_1.jidNormalizedUser)(jid),
970
+ jid: jidNormalizedUser(jid),
692
971
  t,
693
972
  type: 'trusted_contact'
694
973
  }
@@ -698,29 +977,42 @@ const makeMessagesSocket = (config) => {
698
977
  });
699
978
  return result;
700
979
  };
701
- const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);
702
- const avelinel = new aveline(Utils_1, waUploadToServer, relayMessage);
703
- const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');
980
+ const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
981
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
982
+ registerSocketEndHandler(() => {
983
+ if (!config.userDevicesCache && userDevicesCache.close) {
984
+ userDevicesCache.close();
985
+ }
986
+ mediaConn = undefined;
987
+ if (messageRetryManager) {
988
+ messageRetryManager.clear();
989
+ }
990
+ });
704
991
  return {
705
992
  ...sock,
706
- getPrivacyTokens,
993
+ userDevicesCache,
994
+ devicesMutex,
995
+ issuePrivacyTokens,
707
996
  assertSessions,
708
997
  relayMessage,
709
998
  sendReceipt,
710
999
  sendReceipts,
711
- avelinel,
712
1000
  readMessages,
713
1001
  refreshMediaConn,
1002
+ // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1003
+ getMediaHost: () => mediaHost,
714
1004
  waUploadToServer,
715
1005
  fetchPrivacySettings,
716
- getUSyncDevices,
717
- createParticipantNodes,
718
1006
  sendPeerDataOperationMessage,
1007
+ createParticipantNodes,
1008
+ getUSyncDevices,
1009
+ messageRetryManager,
1010
+ updateMemberLabel,
719
1011
  updateMediaMessage: async (message) => {
720
- const content = (0, Utils_1.assertMediaContent)(message.message);
1012
+ const content = assertMediaContent(message.message);
721
1013
  const mediaKey = content.mediaKey;
722
1014
  const meId = authState.creds.me.id;
723
- const node = await (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId);
1015
+ const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
724
1016
  let error = undefined;
725
1017
  await Promise.all([
726
1018
  sendNode(node),
@@ -732,13 +1024,16 @@ const makeMessagesSocket = (config) => {
732
1024
  }
733
1025
  else {
734
1026
  try {
735
- const media = await (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
736
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
737
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];
738
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });
1027
+ const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
1028
+ if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
1029
+ const resultStr = proto.MediaRetryNotification.ResultType[media.result];
1030
+ throw new Boom(`Media re-upload failed by device (${resultStr})`, {
1031
+ data: media,
1032
+ statusCode: getStatusCodeForMediaRetry(media.result) || 404
1033
+ });
739
1034
  }
740
1035
  content.directPath = media.directPath;
741
- content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);
1036
+ content.url = getUrlFromDirectPath(content.directPath, mediaHost);
742
1037
  logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
743
1038
  }
744
1039
  catch (err) {
@@ -752,384 +1047,94 @@ const makeMessagesSocket = (config) => {
752
1047
  if (error) {
753
1048
  throw error;
754
1049
  }
755
- ev.emit('messages.update', [
756
- { key: message.key, update: { message: message.message } }
757
- ]);
1050
+ ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
758
1051
  return message;
759
1052
  },
760
- sendStatusMentions: async (content, jids = []) => {
761
- const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
762
- let allUsers = new Set()
763
- allUsers.add(userJid)
764
-
765
- for (const id of jids) {
766
- const isGroup = WABinary_1.isJidGroup(id)
767
- const isPrivate = WABinary_1.isJidUser(id)
768
-
769
- if (isGroup) {
770
- try {
771
- const metadata = await cachedGroupMetadata(id) || await global.groupMetadataCache(id)
772
- const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
773
- participants.forEach(jid => allUsers.add(jid))
774
- } catch (error) {
775
- logger.error(`Error getting metadata for group ${id}: ${error}`)
776
- }
777
- } else if (isPrivate) {
778
- allUsers.add(WABinary_1.jidNormalizedUser(id))
779
- }
780
- }
781
-
782
- const uniqueUsers = Array.from(allUsers)
783
- const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
784
-
785
- const isMedia = content.image || content.video || content.audio
786
- const isAudio = !!content.audio
787
-
788
- const messageContent = {
789
- ...content
790
- }
791
-
792
- if (isMedia && !isAudio) {
793
- if (messageContent.text) {
794
- messageContent.caption = messageContent.text
795
-
796
- delete messageContent.text
797
- }
798
-
799
- delete messageContent.ptt
800
- delete messageContent.font
801
- delete messageContent.backgroundColor
802
- delete messageContent.textColor
803
- }
804
-
805
- if (isAudio) {
806
- delete messageContent.text
807
- delete messageContent.caption
808
- delete messageContent.font
809
- delete messageContent.textColor
810
- }
811
-
812
- const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
813
- const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
814
- const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
815
- const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
816
-
817
- let msg
818
- let mediaHandle
819
- try {
820
- msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
821
- logger,
822
- userJid,
823
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
824
- thumbnailWidth: linkPreviewImageThumbnailWidth,
825
- fetchOpts: {
826
- timeout: 3000,
827
- ...axiosOptions || {}
828
- },
829
- logger,
830
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
831
- }),
832
- upload: async (encFilePath, opts) => {
833
- const up = await waUploadToServer(encFilePath, {
834
- ...opts
835
- })
836
- mediaHandle = up.handle
837
- return up
838
- },
839
- mediaCache: config.mediaCache,
840
- options: config.options,
841
- font,
842
- textColor,
843
- backgroundColor,
844
- ptt
845
- })
846
- } catch (error) {
847
- logger.error(`Error generating message: ${error}`)
848
- throw error
849
- }
850
-
851
- await relayMessage(WABinary_1.STORIES_JID, msg.message, {
852
- messageId: msg.key.id,
853
- statusJidList: uniqueUsers,
854
- additionalNodes: [{
855
- tag: 'meta',
856
- attrs: {},
857
- content: [{
858
- tag: 'mentioned_users',
859
- attrs: {},
860
- content: jids.map(jid => ({
861
- tag: 'to',
862
- attrs: {
863
- jid: WABinary_1.jidNormalizedUser(jid)
864
- }
865
- }))
866
- }]
867
- }]
868
- })
869
-
870
- for (const id of jids) {
871
- try {
872
- const normalizedId = WABinary_1.jidNormalizedUser(id)
873
- const isPrivate = WABinary_1.isJidUser(normalizedId)
874
- const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
875
-
876
- const protocolMessage = {
877
- [type]: {
878
- message: {
879
- protocolMessage: {
880
- key: msg.key,
881
- type: 25
882
- }
883
- }
884
- },
885
- messageContextInfo: {
886
- messageSecret: crypto_1.randomBytes(32)
887
- }
888
- }
889
-
890
- const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
891
- protocolMessage, {}
892
- )
893
-
894
- await relayMessage(
895
- normalizedId,
896
- statusMsg.message, {
897
- additionalNodes: [{
898
- tag: 'meta',
899
- attrs: isPrivate ? {
900
- is_status_mention: 'true'
901
- } : {
902
- is_group_status_mention: 'true'
903
- }
904
- }]
905
- }
906
- )
907
-
908
- await Utils_1.delay(2000)
909
- } catch (error) {
910
- logger.error(`Error sending to ${id}: ${error}`)
911
- }
912
- }
913
-
914
- return msg
915
- },
916
1053
  sendMessage: async (jid, content, options = {}) => {
917
- var _a, _b, _c;
918
1054
  const userJid = authState.creds.me.id;
919
- const getParticipantAttr = () => filter ? { participant: { jid } } : {};
920
- const { filter = false, quoted } = options;
921
- const messageType = avelinel.detectType(content);
922
-
923
- if (messageType) {
924
- switch (messageType) {
925
- case 'PAYMENT':
926
- const paymentContent = await avelinel.handlePayment(content, quoted);
927
- return await relayMessage(jid, paymentContent, {
928
- messageId: Utils_1.generateMessageID(),
929
- ...getParticipantAttr()
930
- });
931
-
932
- case 'PRODUCT':
933
- const productContent = await avelinel.handleProduct(content, jid, quoted);
934
- const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
935
- return await relayMessage(jid, productMsg.message, {
936
- messageId: productMsg.key.id,
937
- ...getParticipantAttr()
938
- });
939
-
940
- case 'INTERACTIVE':
941
- const interactiveContent = await avelinel.handleInteractive(content, jid, quoted);
942
- const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
943
- return await relayMessage(jid, interactiveMsg.message, {
944
- messageId: interactiveMsg.key.id,
945
- ...getParticipantAttr()
946
- });
947
-
948
- case 'ALBUM':
949
- const albumContent = await avelinel.handleAlbum(content, jid, quoted);
950
- return albumContent;
951
-
952
- case 'EVENT':
953
- return await avelinel.handleEvent(content, jid, quoted);
954
-
955
- case 'POLL_RESULT':
956
- return await avelinel.handlePollResult(content, jid, quoted);
957
- }
958
- }
959
-
960
- if (!options.ephemeralExpiration) {
961
- if ((0, WABinary_1.isJidGroup)(jid)) {
962
- const groups = await sock.groupQuery(jid, 'get', [{
963
- tag: 'query',
964
- attrs: {
965
- request: 'interactive'
966
- }
967
- }]);
968
- const metadata = (0, WABinary_1.getBinaryNodeChild)(groups, 'group');
969
- 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;
970
- options.ephemeralExpiration = expiration;
971
- }
972
- }
973
-
974
1055
  if (typeof content === 'object' &&
975
1056
  'disappearingMessagesInChat' in content &&
976
1057
  typeof content['disappearingMessagesInChat'] !== 'undefined' &&
977
- (0, WABinary_1.isJidGroup)(jid)) {
1058
+ isJidGroup(jid)) {
978
1059
  const { disappearingMessagesInChat } = content;
979
- const value = typeof disappearingMessagesInChat === 'boolean' ?
980
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
981
- disappearingMessagesInChat;
1060
+ const value = typeof disappearingMessagesInChat === 'boolean'
1061
+ ? disappearingMessagesInChat
1062
+ ? WA_DEFAULT_EPHEMERAL
1063
+ : 0
1064
+ : disappearingMessagesInChat;
982
1065
  await groupToggleEphemeral(jid, value);
983
1066
  }
984
- if (typeof content === 'object' && 'album' in content && content.album) {
985
- const { album, caption } = content;
986
- if (caption && !album[0].caption) {
987
- album[0].caption = caption;
988
- }
989
- let mediaHandle;
990
- let mediaMsg;
991
- const albumMsg = (0, Utils_1.generateWAMessageFromContent)(jid, {
992
- albumMessage: {
993
- expectedImageCount: album.filter(item => 'image' in item).length,
994
- expectedVideoCount: album.filter(item => 'video' in item).length
995
- }
996
- }, { userJid, ...options });
997
- await relayMessage(jid, albumMsg.message, {
998
- messageId: albumMsg.key.id
999
- });
1000
- for (const i in album) {
1001
- const media = album[i];
1002
- if ('image' in media) {
1003
- mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
1004
- image: media.image,
1005
- ...(media.caption ? { caption: media.caption } : {}),
1006
- ...options
1007
- }, {
1008
- userJid,
1009
- upload: async (readStream, opts) => {
1010
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
1011
- mediaHandle = up.handle;
1012
- return up;
1013
- },
1014
- ...options,
1015
- });
1016
- }
1017
- else if ('video' in media) {
1018
- mediaMsg = await (0, Utils_1.generateWAMessage)(jid, {
1019
- video: media.video,
1020
- ...(media.caption ? { caption: media.caption } : {}),
1021
- ...(media.gifPlayback !== undefined ? { gifPlayback: media.gifPlayback } : {}),
1022
- ...options
1023
- }, {
1024
- userJid,
1025
- upload: async (readStream, opts) => {
1026
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
1027
- mediaHandle = up.handle;
1028
- return up;
1029
- },
1030
- ...options,
1031
- });
1032
- }
1033
- if (mediaMsg) {
1034
- mediaMsg.message.messageContextInfo = {
1035
- messageSecret: (0, crypto_1.randomBytes)(32),
1036
- messageAssociation: {
1037
- associationType: 1,
1038
- parentMessageKey: albumMsg.key
1039
- }
1040
- };
1041
- }
1042
- await relayMessage(jid, mediaMsg.message, {
1043
- messageId: mediaMsg.key.id
1044
- });
1045
- await new Promise(resolve => setTimeout(resolve, 800));
1046
- }
1047
- return albumMsg;
1048
- }
1049
1067
  else {
1050
- let mediaHandle;
1051
- const fullMsg = await (0, Utils_1.generateWAMessage)(jid, content, {
1068
+ const fullMsg = await generateWAMessage(jid, content, {
1052
1069
  logger,
1053
1070
  userJid,
1054
- getUrlInfo: text => (0, link_preview_1.getUrlInfo)(text, {
1071
+ getUrlInfo: text => getUrlInfo(text, {
1055
1072
  thumbnailWidth: linkPreviewImageThumbnailWidth,
1056
1073
  fetchOpts: {
1057
1074
  timeout: 3000,
1058
- ...axiosOptions || {}
1075
+ ...(httpRequestOptions || {})
1059
1076
  },
1060
1077
  logger,
1061
- uploadImage: generateHighQualityLinkPreview
1062
- ? waUploadToServer
1063
- : undefined
1078
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1064
1079
  }),
1080
+ //TODO: CACHE
1065
1081
  getProfilePicUrl: sock.profilePictureUrl,
1066
- upload: async (readStream, opts) => {
1067
- const up = await waUploadToServer(readStream, { ...opts, newsletter: (0, WABinary_1.isJidNewsletter)(jid) });
1068
- mediaHandle = up.handle;
1069
- return up;
1070
- },
1082
+ getCallLink: sock.createCallLink,
1083
+ upload: waUploadToServer,
1071
1084
  mediaCache: config.mediaCache,
1072
1085
  options: config.options,
1073
- messageId: (0, Utils_1.generateMessageIDV2)((_c = sock.user) === null || _c === void 0 ? void 0 : _c.id),
1074
- ...options,
1086
+ messageId: generateMessageIDV2(sock.user?.id),
1087
+ ...options
1075
1088
  });
1089
+ const isEventMsg = 'event' in content && !!content.event;
1076
1090
  const isDeleteMsg = 'delete' in content && !!content.delete;
1077
1091
  const isEditMsg = 'edit' in content && !!content.edit;
1078
1092
  const isPinMsg = 'pin' in content && !!content.pin;
1079
- const isKeepMsg = 'keep' in content && content.keep;
1080
1093
  const isPollMessage = 'poll' in content && !!content.poll;
1081
- const isAiMsg = 'ai' in content && !!content.ai;
1082
1094
  const additionalAttributes = {};
1083
1095
  const additionalNodes = [];
1084
1096
  // required for delete
1085
1097
  if (isDeleteMsg) {
1086
1098
  // if the chat is a group, and I am not the author, then delete the message as an admin
1087
- if (((0, WABinary_1.isJidGroup)(content.delete.remoteJid) && !content.delete.fromMe) || (0, WABinary_1.isJidNewsletter)(jid)) {
1099
+ if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1088
1100
  additionalAttributes.edit = '8';
1089
1101
  }
1090
1102
  else {
1091
1103
  additionalAttributes.edit = '7';
1092
1104
  }
1093
- // required for edit message
1094
1105
  }
1095
1106
  else if (isEditMsg) {
1096
- additionalAttributes.edit = (0, WABinary_1.isJidNewsletter)(jid) ? '3' : '1';
1097
- // required for pin message
1107
+ additionalAttributes.edit = '1';
1098
1108
  }
1099
1109
  else if (isPinMsg) {
1100
1110
  additionalAttributes.edit = '2';
1101
- // required for keep message
1102
- }
1103
- else if (isKeepMsg) {
1104
- additionalAttributes.edit = '6';
1105
- // required for polling message
1106
1111
  }
1107
1112
  else if (isPollMessage) {
1108
1113
  additionalNodes.push({
1109
1114
  tag: 'meta',
1110
1115
  attrs: {
1111
1116
  polltype: 'creation'
1112
- },
1117
+ }
1113
1118
  });
1114
- // required to display AI icon on message
1115
1119
  }
1116
- else if (isAiMsg) {
1120
+ else if (isEventMsg) {
1117
1121
  additionalNodes.push({
1118
- attrs: {
1119
- biz_bot: '1'
1120
- }, tag: "bot"
1121
- });
1122
- }
1123
- if (mediaHandle) {
1124
- additionalAttributes['media_id'] = mediaHandle;
1125
- }
1126
- if ('cachedGroupMetadata' in options) {
1127
- console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.');
1122
+ tag: 'meta',
1123
+ attrs: {
1124
+ event_type: 'creation'
1125
+ }
1126
+ });
1128
1127
  }
1129
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes, statusJidList: options.statusJidList });
1128
+ await relayMessage(jid, fullMsg.message, {
1129
+ messageId: fullMsg.key.id,
1130
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1131
+ additionalAttributes,
1132
+ statusJidList: options.statusJidList,
1133
+ additionalNodes
1134
+ });
1130
1135
  if (config.emitOwnEvents) {
1131
- process.nextTick(() => {
1132
- processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')));
1136
+ process.nextTick(async () => {
1137
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1133
1138
  });
1134
1139
  }
1135
1140
  return fullMsg;
@@ -1137,4 +1142,4 @@ const makeMessagesSocket = (config) => {
1137
1142
  }
1138
1143
  };
1139
1144
  };
1140
- exports.makeMessagesSocket = makeMessagesSocket;
1145
+ //# sourceMappingURL=messages-send.js.map