@badzz88/baileys 8.3.0 → 8.4.1

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 (213) hide show
  1. package/README.md +27 -27
  2. package/WAProto/GenerateStatics.sh +3 -0
  3. package/WAProto/WAProto.proto +6902 -0
  4. package/WAProto/fix-import.js +29 -0
  5. package/WAProto/fix-imports.js +85 -0
  6. package/WAProto/index.d.ts +79257 -0
  7. package/WAProto/index.js +204305 -131020
  8. package/engine-requirements.js +3 -4
  9. package/lib/Defaults/baileys-version.json +1 -1
  10. package/lib/Defaults/index.js +119 -136
  11. package/lib/Signal/Group/ciphertext-message.js +2 -5
  12. package/lib/Signal/Group/group-session-builder.js +7 -41
  13. package/lib/Signal/Group/group_cipher.js +37 -51
  14. package/lib/Signal/Group/index.js +12 -57
  15. package/lib/Signal/Group/keyhelper.js +7 -44
  16. package/lib/Signal/Group/sender-chain-key.js +7 -15
  17. package/lib/Signal/Group/sender-key-distribution-message.js +8 -11
  18. package/lib/Signal/Group/sender-key-message.js +9 -12
  19. package/lib/Signal/Group/sender-key-name.js +2 -5
  20. package/lib/Signal/Group/sender-key-record.js +9 -21
  21. package/lib/Signal/Group/sender-key-state.js +27 -42
  22. package/lib/Signal/Group/sender-message-key.js +4 -7
  23. package/lib/Signal/libsignal.js +347 -90
  24. package/lib/Signal/lid-mapping.js +277 -0
  25. package/lib/Socket/Client/index.js +3 -19
  26. package/lib/Socket/Client/types.js +11 -0
  27. package/lib/Socket/Client/websocket.js +54 -0
  28. package/lib/Socket/business.js +162 -43
  29. package/lib/Socket/chats.js +627 -404
  30. package/lib/Socket/communities.js +431 -0
  31. package/lib/Socket/community.js +392 -0
  32. package/lib/Socket/dugong.js +161 -8
  33. package/lib/Socket/groups.js +154 -160
  34. package/lib/Socket/index.js +11 -10
  35. package/lib/Socket/luxu.js +387 -0
  36. package/lib/Socket/messages-recv.js +1421 -615
  37. package/lib/Socket/messages-send.js +1150 -624
  38. package/lib/Socket/mex.js +42 -0
  39. package/lib/Socket/newsletter.js +139 -314
  40. package/lib/Socket/registration.js +2 -1
  41. package/lib/Socket/socket.js +544 -242
  42. package/lib/Store/index.js +10 -10
  43. package/lib/Store/keyed-db.js +108 -0
  44. package/lib/Store/make-cache-manager-store.js +43 -41
  45. package/lib/Store/make-in-memory-store.js +112 -341
  46. package/lib/Store/make-ordered-dictionary.js +14 -20
  47. package/lib/Store/object-repository.js +11 -6
  48. package/lib/Types/Auth.js +2 -2
  49. package/lib/Types/Bussines.js +2 -0
  50. package/lib/Types/Call.js +2 -2
  51. package/lib/Types/Chat.js +8 -4
  52. package/lib/Types/Contact.js +2 -2
  53. package/lib/Types/Events.js +2 -2
  54. package/lib/Types/GroupMetadata.js +2 -2
  55. package/lib/Types/Label.js +3 -5
  56. package/lib/Types/LabelAssociation.js +3 -5
  57. package/lib/Types/Message.js +11 -9
  58. package/lib/Types/Mex.js +37 -0
  59. package/lib/Types/Product.js +2 -2
  60. package/lib/Types/Signal.js +2 -2
  61. package/lib/Types/Socket.js +3 -2
  62. package/lib/Types/State.js +56 -2
  63. package/lib/Types/USync.js +2 -2
  64. package/lib/Types/index.js +15 -31
  65. package/lib/Utils/auth-utils.js +239 -143
  66. package/lib/Utils/browser-utils.js +48 -0
  67. package/lib/Utils/business.js +66 -69
  68. package/lib/Utils/chat-utils.js +396 -253
  69. package/lib/Utils/companion-reg-client-utils.js +35 -0
  70. package/lib/Utils/crypto.js +57 -90
  71. package/lib/Utils/decode-wa-message.js +236 -84
  72. package/lib/Utils/event-buffer.js +185 -77
  73. package/lib/Utils/generics.js +188 -283
  74. package/lib/Utils/history.js +93 -55
  75. package/lib/Utils/identity-change-handler.js +50 -0
  76. package/lib/Utils/index.js +23 -33
  77. package/lib/Utils/link-preview.js +16 -24
  78. package/lib/Utils/logger.js +3 -7
  79. package/lib/Utils/lt-hash.js +3 -46
  80. package/lib/Utils/make-mutex.js +24 -34
  81. package/lib/Utils/message-composer.js +273 -0
  82. package/lib/Utils/message-retry-manager.js +265 -0
  83. package/lib/Utils/messages-media.js +451 -482
  84. package/lib/Utils/messages.js +796 -359
  85. package/lib/Utils/noise-handler.js +145 -99
  86. package/lib/Utils/offline-node-processor.js +40 -0
  87. package/lib/Utils/pre-key-manager.js +106 -0
  88. package/lib/Utils/process-message.js +459 -150
  89. package/lib/Utils/reporting-utils.js +258 -0
  90. package/lib/Utils/signal.js +120 -72
  91. package/lib/Utils/stanza-ack.js +38 -0
  92. package/lib/Utils/sync-action-utils.js +49 -0
  93. package/lib/Utils/tc-token-utils.js +163 -0
  94. package/lib/Utils/use-multi-file-auth-state.js +29 -27
  95. package/lib/Utils/validate-connection.js +73 -99
  96. package/lib/WABinary/constants.js +1281 -20
  97. package/lib/WABinary/decode.js +52 -42
  98. package/lib/WABinary/encode.js +110 -155
  99. package/lib/WABinary/generic-utils.js +55 -49
  100. package/lib/WABinary/index.js +6 -21
  101. package/lib/WABinary/jid-utils.js +76 -40
  102. package/lib/WABinary/types.js +2 -2
  103. package/lib/WAM/BinaryInfo.js +2 -5
  104. package/lib/WAM/constants.js +19071 -11568
  105. package/lib/WAM/encode.js +17 -22
  106. package/lib/WAM/index.js +4 -19
  107. package/lib/WAUSync/Protocols/USyncContactProtocol.js +33 -13
  108. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +11 -14
  109. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +9 -12
  110. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +9 -13
  111. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  112. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +20 -22
  113. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +13 -8
  114. package/lib/WAUSync/Protocols/index.js +6 -20
  115. package/lib/WAUSync/USyncQuery.js +44 -35
  116. package/lib/WAUSync/USyncUser.js +10 -5
  117. package/lib/WAUSync/index.js +4 -19
  118. package/lib/index.js +39 -53
  119. package/package.json +134 -101
  120. package/lib/Defaults/index.d.ts +0 -53
  121. package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
  122. package/lib/Signal/Group/group-session-builder.d.ts +0 -14
  123. package/lib/Signal/Group/group_cipher.d.ts +0 -17
  124. package/lib/Signal/Group/index.d.ts +0 -11
  125. package/lib/Signal/Group/keyhelper.d.ts +0 -10
  126. package/lib/Signal/Group/queue-job.d.ts +0 -1
  127. package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
  128. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
  129. package/lib/Signal/Group/sender-key-message.d.ts +0 -18
  130. package/lib/Signal/Group/sender-key-name.d.ts +0 -17
  131. package/lib/Signal/Group/sender-key-record.d.ts +0 -30
  132. package/lib/Signal/Group/sender-key-state.d.ts +0 -38
  133. package/lib/Signal/Group/sender-message-key.d.ts +0 -11
  134. package/lib/Signal/libsignal.d.ts +0 -3
  135. package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
  136. package/lib/Socket/Client/index.d.ts +0 -3
  137. package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
  138. package/lib/Socket/Client/web-socket-client.d.ts +0 -12
  139. package/lib/Socket/business.d.ts +0 -171
  140. package/lib/Socket/chats.d.ts +0 -267
  141. package/lib/Socket/dugong.d.ts +0 -254
  142. package/lib/Socket/groups.d.ts +0 -115
  143. package/lib/Socket/index.d.ts +0 -173
  144. package/lib/Socket/messages-recv.d.ts +0 -161
  145. package/lib/Socket/messages-send.d.ts +0 -149
  146. package/lib/Socket/newsletter.d.ts +0 -134
  147. package/lib/Socket/registration.d.ts +0 -267
  148. package/lib/Socket/socket.d.ts +0 -43
  149. package/lib/Socket/usync.d.ts +0 -36
  150. package/lib/Store/index.d.ts +0 -3
  151. package/lib/Store/make-cache-manager-store.d.ts +0 -13
  152. package/lib/Store/make-in-memory-store.d.ts +0 -118
  153. package/lib/Store/make-ordered-dictionary.d.ts +0 -13
  154. package/lib/Store/object-repository.d.ts +0 -10
  155. package/lib/Types/Auth.d.ts +0 -110
  156. package/lib/Types/Call.d.ts +0 -13
  157. package/lib/Types/Chat.d.ts +0 -102
  158. package/lib/Types/Contact.d.ts +0 -19
  159. package/lib/Types/Events.d.ts +0 -157
  160. package/lib/Types/GroupMetadata.d.ts +0 -55
  161. package/lib/Types/Label.d.ts +0 -35
  162. package/lib/Types/LabelAssociation.d.ts +0 -29
  163. package/lib/Types/Message.d.ts +0 -273
  164. package/lib/Types/Newsletter.d.ts +0 -103
  165. package/lib/Types/Product.d.ts +0 -78
  166. package/lib/Types/Signal.d.ts +0 -57
  167. package/lib/Types/Socket.d.ts +0 -111
  168. package/lib/Types/State.d.ts +0 -27
  169. package/lib/Types/USync.d.ts +0 -25
  170. package/lib/Types/index.d.ts +0 -57
  171. package/lib/Utils/auth-utils.d.ts +0 -18
  172. package/lib/Utils/baileys-event-stream.d.ts +0 -16
  173. package/lib/Utils/business.d.ts +0 -22
  174. package/lib/Utils/chat-utils.d.ts +0 -71
  175. package/lib/Utils/crypto.d.ts +0 -41
  176. package/lib/Utils/decode-wa-message.d.ts +0 -19
  177. package/lib/Utils/event-buffer.d.ts +0 -35
  178. package/lib/Utils/generics.d.ts +0 -92
  179. package/lib/Utils/history.d.ts +0 -15
  180. package/lib/Utils/index.d.ts +0 -17
  181. package/lib/Utils/link-preview.d.ts +0 -21
  182. package/lib/Utils/logger.d.ts +0 -4
  183. package/lib/Utils/lt-hash.d.ts +0 -12
  184. package/lib/Utils/make-mutex.d.ts +0 -7
  185. package/lib/Utils/messages-media.d.ts +0 -116
  186. package/lib/Utils/messages.d.ts +0 -77
  187. package/lib/Utils/noise-handler.d.ts +0 -21
  188. package/lib/Utils/process-message.d.ts +0 -41
  189. package/lib/Utils/signal.d.ts +0 -32
  190. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  191. package/lib/Utils/validate-connection.d.ts +0 -11
  192. package/lib/WABinary/constants.d.ts +0 -30
  193. package/lib/WABinary/decode.d.ts +0 -7
  194. package/lib/WABinary/encode.d.ts +0 -3
  195. package/lib/WABinary/generic-utils.d.ts +0 -17
  196. package/lib/WABinary/index.d.ts +0 -5
  197. package/lib/WABinary/jid-utils.d.ts +0 -31
  198. package/lib/WABinary/types.d.ts +0 -18
  199. package/lib/WAM/BinaryInfo.d.ts +0 -17
  200. package/lib/WAM/constants.d.ts +0 -38
  201. package/lib/WAM/encode.d.ts +0 -3
  202. package/lib/WAM/index.d.ts +0 -3
  203. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
  204. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
  205. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
  206. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
  207. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
  208. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
  209. package/lib/WAUSync/Protocols/index.d.ts +0 -4
  210. package/lib/WAUSync/USyncQuery.d.ts +0 -28
  211. package/lib/WAUSync/USyncUser.d.ts +0 -12
  212. package/lib/WAUSync/index.d.ts +0 -3
  213. package/lib/index.d.ts +0 -12
@@ -1,74 +1,71 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.makeMessagesSocket = void 0;
7
- const boom_1 = require("@hapi/boom");
8
- const node_cache_1 = __importDefault(require("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;
20
- const makeMessagesSocket = (config) => {
21
- const {
22
- logger,
23
- linkPreviewImageThumbnailWidth,
24
- generateHighQualityLinkPreview,
25
- options: axiosOptions,
26
- patchMessageBeforeSending
27
- } = config;
28
- const sock = (0, newsletter_1.makeNewsletterSocket)(config);
29
- 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
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, setBotMessageSecret } 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, getAdditionalNode, getBinaryNodeFilter, getBinaryFilteredBizBot, isInteropUser } from '../WABinary/index.js';
11
+ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
12
+ import { makeNewsletterSocket } from './newsletter.js';
13
+ import imup from './luxu.js';
14
+ import * as Utils_1 from '../Utils/index.js';
15
+ import { randomBytes } from 'crypto';
16
+ export const makeMessagesSocket = (config) => {
17
+ const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount, aiLabel } = config;
18
+ const sock = makeNewsletterSocket(config);
19
+ const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
20
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
21
+ /**
22
+ * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
23
+ * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
24
+ * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
25
+ */
26
+ const inFlightTcTokenIssuance = new Set();
27
+ const userDevicesCache = config.userDevicesCache ||
28
+ new NodeCache({
29
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
30
+ useClones: false
31
+ });
32
+ /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
33
+ const devicesMutex = makeMutex();
34
+ // Initialize message retry manager if enabled
35
+ const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
36
+ // Prevent race conditions in Signal session encryption by user
37
+ const encryptionMutex = makeKeyedMutex();
47
38
  let mediaConn;
39
+ /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
40
+ let mediaHost = DEF_MEDIA_HOST;
48
41
  const refreshMediaConn = async (forceGet = false) => {
49
42
  const media = await mediaConn;
50
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
43
+ if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
51
44
  mediaConn = (async () => {
52
45
  const result = await query({
53
46
  tag: 'iq',
54
47
  attrs: {
55
48
  type: 'set',
56
49
  xmlns: 'w:m',
57
- to: WABinary_1.S_WHATSAPP_NET,
50
+ to: S_WHATSAPP_NET
58
51
  },
59
52
  content: [{ tag: 'media_conn', attrs: {} }]
60
53
  });
61
- const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn');
54
+ const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
55
+ // TODO: explore full length of data that whatsapp provides
62
56
  const node = {
63
- hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
57
+ hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
64
58
  hostname: attrs.hostname,
65
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
59
+ maxContentLengthBytes: +attrs.maxContentLengthBytes
66
60
  })),
67
61
  auth: mediaConnNode.attrs.auth,
68
62
  ttl: +mediaConnNode.attrs.ttl,
69
63
  fetchDate: new Date()
70
64
  };
71
65
  logger.debug('fetched media conn');
66
+ if (node.hosts[0]) {
67
+ mediaHost = node.hosts[0].hostname;
68
+ }
72
69
  return node;
73
70
  })();
74
71
  }
@@ -79,17 +76,20 @@ const makeMessagesSocket = (config) => {
79
76
  * used for receipts of phone call, read, delivery etc.
80
77
  * */
81
78
  const sendReceipt = async (jid, participant, messageIds, type) => {
79
+ if (!messageIds || messageIds.length === 0) {
80
+ throw new Boom('missing ids in receipt');
81
+ }
82
82
  const node = {
83
83
  tag: 'receipt',
84
84
  attrs: {
85
- id: messageIds[0],
86
- },
85
+ id: messageIds[0]
86
+ }
87
87
  };
88
88
  const isReadReceipt = type === 'read' || type === 'read-self';
89
89
  if (isReadReceipt) {
90
- node.attrs.t = (0, Utils_1.unixTimestampSeconds)().toString();
90
+ node.attrs.t = unixTimestampSeconds().toString();
91
91
  }
92
- if (type === 'sender' && WABinary_1.isJidUser(jid)) {
92
+ if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
93
93
  node.attrs.recipient = jid;
94
94
  node.attrs.to = participant;
95
95
  }
@@ -100,7 +100,7 @@ const makeMessagesSocket = (config) => {
100
100
  }
101
101
  }
102
102
  if (type) {
103
- node.attrs.type = WABinary_1.isJidNewsLetter(jid) ? 'read-self' : type;
103
+ node.attrs.type = type;
104
104
  }
105
105
  const remainingMessageIds = messageIds.slice(1);
106
106
  if (remainingMessageIds.length) {
@@ -120,7 +120,7 @@ const makeMessagesSocket = (config) => {
120
120
  };
121
121
  /** Correctly bulk send receipts to multiple chats, participants */
122
122
  const sendReceipts = async (keys, type) => {
123
- const recps = (0, Utils_1.aggregateMessageKeysNotFromMe)(keys);
123
+ const recps = aggregateMessageKeysNotFromMe(keys);
124
124
  for (const { jid, participant, messageIds } of recps) {
125
125
  await sendReceipt(jid, participant, messageIds, type);
126
126
  }
@@ -134,540 +134,829 @@ const makeMessagesSocket = (config) => {
134
134
  };
135
135
  /** Fetch all the devices we've to send a message to */
136
136
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
137
- const deviceResults = []
138
-
137
+ const deviceResults = [];
139
138
  if (!useCache) {
140
- logger.debug('not using cache for devices')
139
+ logger.debug('not using cache for devices');
141
140
  }
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
-
141
+ const toFetch = [];
142
+ const jidsWithUser = jids
143
+ .map(jid => {
144
+ const decoded = jidDecode(jid);
145
+ const user = decoded?.user;
146
+ const device = decoded?.device;
147
+ const isExplicitDevice = typeof device === 'number' && device >= 0;
148
+ if (isExplicitDevice && user) {
149
+ deviceResults.push({
150
+ user,
151
+ device,
152
+ jid
153
+ });
154
+ return null;
155
+ }
156
+ jid = jidNormalizedUser(jid);
157
+ return { jid, user };
158
+ })
159
+ .filter(jid => jid !== null);
160
+ let mgetDevices;
161
+ if (useCache && userDevicesCache.mget) {
162
+ const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
163
+ mgetDevices = await userDevicesCache.mget(usersToFetch);
164
+ }
165
+ for (const { jid, user } of jidsWithUser) {
152
166
  if (useCache) {
153
- const devices = userDevicesCache.get(user)
154
-
167
+ const devices = mgetDevices?.[user] ||
168
+ (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
155
169
  if (devices) {
156
- deviceResults.push(...devices)
157
- logger.trace({ user }, 'using cache for devices')
170
+ const devicesWithJid = devices.map(d => ({
171
+ ...d,
172
+ jid: jidEncode(d.user, d.server, d.device)
173
+ }));
174
+ deviceResults.push(...devicesWithJid);
175
+ logger.trace({ user }, 'using cache for devices');
158
176
  }
159
-
160
177
  else {
161
- toFetch.push(jid)
178
+ toFetch.push(jid);
162
179
  }
163
180
  }
164
-
165
181
  else {
166
- toFetch.push(jid)
182
+ toFetch.push(jid);
167
183
  }
168
184
  }
169
-
170
185
  if (!toFetch.length) {
171
- return deviceResults
186
+ return deviceResults;
172
187
  }
173
-
174
- const query = new WAUSync_1.USyncQuery()
175
- .withContext('message')
176
- .withDeviceProtocol()
177
-
188
+ const requestedLidUsers = new Set();
178
189
  for (const jid of toFetch) {
179
- query.withUser(new WAUSync_1.USyncUser().withId(jid))
190
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
191
+ const user = jidDecode(jid)?.user;
192
+ if (user)
193
+ requestedLidUsers.add(user);
194
+ }
180
195
  }
181
-
182
- const result = await executeUSyncQuery(query)
183
-
196
+ const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
197
+ for (const jid of toFetch) {
198
+ 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
199
+ }
200
+ const result = await sock.executeUSyncQuery(query);
184
201
  if (result) {
185
- const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
186
- const deviceMap = {}
187
-
202
+ // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
203
+ const lidResults = result.list.filter(a => !!a.lid);
204
+ if (lidResults.length > 0) {
205
+ logger.trace('Storing LID maps from device call');
206
+ await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
207
+ // Force-refresh sessions for newly mapped LIDs to align identity addressing
208
+ try {
209
+ const lids = lidResults.map(a => a.lid);
210
+ if (lids.length) {
211
+ await assertSessions(lids, true);
212
+ }
213
+ }
214
+ catch (e) {
215
+ logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs');
216
+ }
217
+ }
218
+ const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
219
+ const deviceMap = {};
188
220
  for (const item of extracted) {
189
- deviceMap[item.user] = deviceMap[item.user] || []
190
- deviceMap[item.user].push(item)
191
- deviceResults.push(item)
221
+ deviceMap[item.user] = deviceMap[item.user] || [];
222
+ deviceMap[item.user]?.push(item);
192
223
  }
193
-
194
- for (const key in deviceMap) {
195
- userDevicesCache.set(key, deviceMap[key])
224
+ // Process each user's devices as a group for bulk LID migration
225
+ for (const [user, userDevices] of Object.entries(deviceMap)) {
226
+ const isLidUser = requestedLidUsers.has(user);
227
+ // Process all devices for this user
228
+ for (const item of userDevices) {
229
+ const finalJid = isLidUser
230
+ ? jidEncode(user, item.server, item.device)
231
+ : jidEncode(item.user, item.server, item.device);
232
+ deviceResults.push({
233
+ ...item,
234
+ jid: finalJid
235
+ });
236
+ logger.debug({
237
+ user: item.user,
238
+ device: item.device,
239
+ finalJid,
240
+ usedLid: isLidUser
241
+ }, 'Processed device with LID priority');
242
+ }
243
+ }
244
+ await devicesMutex.mutex(async () => {
245
+ if (userDevicesCache.mset) {
246
+ // if the cache supports mset, we can set all devices in one go
247
+ await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
248
+ }
249
+ else {
250
+ for (const key in deviceMap) {
251
+ if (deviceMap[key])
252
+ await userDevicesCache.set(key, deviceMap[key]);
253
+ }
254
+ }
255
+ });
256
+ const userDeviceUpdates = {};
257
+ for (const [userId, devices] of Object.entries(deviceMap)) {
258
+ if (devices && devices.length > 0) {
259
+ userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
260
+ }
261
+ }
262
+ if (Object.keys(userDeviceUpdates).length > 0) {
263
+ try {
264
+ await authState.keys.set({ 'device-list': userDeviceUpdates });
265
+ logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
266
+ }
267
+ catch (error) {
268
+ logger.warn({ error }, 'failed to store user device lists');
269
+ }
196
270
  }
197
271
  }
198
-
199
- return deviceResults
200
- }
272
+ return deviceResults;
273
+ };
274
+ /**
275
+ * Update Member Label
276
+ */
277
+ const updateMemberLabel = (jid, memberLabel) => {
278
+ return relayMessage(jid, {
279
+ protocolMessage: {
280
+ type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
281
+ memberLabel: {
282
+ label: memberLabel?.slice(0, 30),
283
+ labelTimestamp: unixTimestampSeconds()
284
+ }
285
+ }
286
+ }, {
287
+ additionalNodes: [
288
+ {
289
+ tag: 'meta',
290
+ attrs: {
291
+ tag_reason: 'user_update',
292
+ appdata: 'member_tag'
293
+ },
294
+ content: undefined
295
+ }
296
+ ]
297
+ });
298
+ };
201
299
  const assertSessions = async (jids, force) => {
202
300
  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);
301
+ const uniqueJids = [...new Set(jids)];
302
+ const jidsRequiringFetch = [];
303
+ logger.debug({ jids }, 'assertSessions call with jids');
304
+ for (const jid of uniqueJids) {
305
+ if (!force) {
306
+ const sessionValidation = await signalRepository.validateSession(jid);
307
+ if (sessionValidation.exists) {
308
+ continue;
216
309
  }
217
310
  }
311
+ jidsRequiringFetch.push(jid);
218
312
  }
219
313
  if (jidsRequiringFetch.length) {
220
- logger.debug({ jidsRequiringFetch }, 'fetching sessions');
314
+ // LID if mapped, otherwise original
315
+ const wireJids = [
316
+ ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
317
+ ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
318
+ ];
319
+ logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
221
320
  const result = await query({
222
321
  tag: 'iq',
223
322
  attrs: {
224
323
  xmlns: 'encrypt',
225
324
  type: 'get',
226
- to: WABinary_1.S_WHATSAPP_NET,
325
+ to: S_WHATSAPP_NET
227
326
  },
228
327
  content: [
229
328
  {
230
329
  tag: 'key',
231
330
  attrs: {},
232
- content: jidsRequiringFetch.map(jid => ({
233
- tag: 'user',
234
- attrs: { jid },
235
- }))
331
+ content: wireJids.map(jid => {
332
+ const attrs = { jid };
333
+ if (force)
334
+ attrs.reason = 'identity';
335
+ return { tag: 'user', attrs };
336
+ })
236
337
  }
237
338
  ]
238
339
  });
239
- await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
340
+ await parseAndInjectE2ESessions(result, signalRepository);
240
341
  didFetchNewSession = true;
241
342
  }
242
343
  return didFetchNewSession;
243
344
  };
244
-
245
-
246
345
  const sendPeerDataOperationMessage = async (pdoMessage) => {
346
+ //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
247
347
  if (!authState.creds.me?.id) {
248
- throw new boom_1.Boom('Not authenticated')
348
+ throw new Boom('Not authenticated');
249
349
  }
250
-
251
350
  const protocolMessage = {
252
351
  protocolMessage: {
253
352
  peerDataOperationRequestMessage: pdoMessage,
254
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
353
+ type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
255
354
  }
256
355
  };
257
- const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id);
356
+ const meJid = jidNormalizedUser(authState.creds.me.id);
258
357
  const msgId = await relayMessage(meJid, protocolMessage, {
259
358
  additionalAttributes: {
260
359
  category: 'peer',
261
- // eslint-disable-next-line camelcase
262
- push_priority: 'high_force',
360
+ push_priority: 'high_force'
263
361
  },
362
+ additionalNodes: [
363
+ {
364
+ tag: 'meta',
365
+ attrs: { appdata: 'default' }
366
+ }
367
+ ]
264
368
  });
265
369
  return msgId;
266
370
  };
267
- const createParticipantNodes = async (jids, message, extraAttrs) => {
268
- const patched = await patchMessageBeforeSending(message, jids);
269
- const bytes = (0, Utils_1.encodeWAMessage)(patched);
371
+ const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
372
+ if (!recipientJids.length) {
373
+ return { nodes: [], shouldIncludeDeviceIdentity: false };
374
+ }
375
+ const patched = await patchMessageBeforeSending(message, recipientJids);
376
+ const patchedMessages = Array.isArray(patched)
377
+ ? patched
378
+ : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
270
379
  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
380
  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';
301
- 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)();
306
- useUserDevicesCache = useUserDevicesCache !== false;
307
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
308
- const participants = [];
309
- const destinationJid = (!isStatus) ? WABinary_1.jidEncode(user, isLid ? 'lid' : isGroup ? 'g.us' : isNewsletter ? 'newsletter' : 's.whatsapp.net') : statusJid;
310
- const binaryNodeContent = [];
311
- const devices = [];
312
- const meMsg = {
313
- deviceSentMessage: {
314
- destinationJid,
315
- message
316
- }
317
- };
318
- const extraAttrs = {}
319
- const messages = Utils_1.normalizeMessageContent(message)
320
- const buttonType = getButtonType(messages);
321
- 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
- if (!isGroup && !isStatus) {
326
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' };
327
- }
328
- const { user, device } = WABinary_1.jidDecode(participant.jid);
329
- devices.push({ user, device });
330
- }
331
- await authState.keys.transaction(async () => {
332
- const mediaType = getMediaType(messages);
333
-
334
- if (mediaType) {
335
- extraAttrs['mediatype'] = mediaType
336
- }
337
-
338
- if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
339
- extraAttrs['decrypt-fail'] = 'hide'
340
- }
341
-
342
- if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
343
- extraAttrs['native_flow_name'] = messages.interactiveResponseMessage?.nativeFlowResponseMessage.name
344
- }
345
-
346
- if (isGroup || isStatus) {
347
- const [groupData, senderKeyMap] = await Promise.all([
348
- (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');
352
- }
353
-
354
- else if (!isStatus) {
355
- groupData = await groupMetadata(jid)
356
- }
357
-
358
- return groupData;
359
- })(),
360
- (async () => {
361
- if (!participant && !isStatus) {
362
- const result = await authState.keys.get('sender-key-memory', [jid])
363
- return result[jid] || {}
364
- }
365
-
366
- return {}
367
-
368
- })()
369
- ]);
370
- if (!participant) {
371
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
372
-
373
- if (isStatus && statusJidList) {
374
- participantsList.push(...statusJidList)
381
+ const meLid = authState.creds.me?.lid;
382
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
383
+ const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
384
+ try {
385
+ if (!jid)
386
+ return null;
387
+ let msgToEncrypt = patchedMessage;
388
+ if (dsmMessage) {
389
+ const { user: targetUser } = jidDecode(jid);
390
+ const { user: ownPnUser } = jidDecode(meId);
391
+ const ownLidUser = meLidUser;
392
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
393
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
394
+ if (isOwnUser && !isExactSenderDevice) {
395
+ msgToEncrypt = dsmMessage;
396
+ logger.debug({ jid, targetUser }, 'Using DSM for own device');
375
397
  }
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
398
  }
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({
394
- group: destinationJid,
395
- data: bytes,
396
- meId,
397
- });
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;
399
+ const bytes = encodeWAMessage(msgToEncrypt);
400
+ const mutexKey = jid;
401
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
402
+ const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
403
+ if (type === 'pkmsg') {
404
+ shouldIncludeDeviceIdentity = true;
406
405
  }
407
- }
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');
412
- const senderKeyMsg = {
413
- senderKeyDistributionMessage: {
414
- axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
415
- groupId: destinationJid
416
- }
406
+ return {
407
+ tag: 'to',
408
+ attrs: { jid },
409
+ content: [
410
+ {
411
+ tag: 'enc',
412
+ attrs: { v: '2', type, ...(extraAttrs || {}) },
413
+ content: ciphertext
414
+ }
415
+ ]
417
416
  };
418
- await assertSessions(senderKeyJids, false);
419
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
420
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
421
- participants.push(...result.nodes);
422
- }
423
- binaryNodeContent.push({
424
- tag: 'enc',
425
- attrs: { v: '2', type: 'skmsg', ...extraAttrs },
426
- content: ciphertext
427
417
  });
428
- await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
418
+ return node;
429
419
  }
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 = {}
441
- }
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
- })
420
+ catch (err) {
421
+ logger.error({ jid, err }, 'Failed to encrypt for recipient');
422
+ return null;
451
423
  }
452
- else {
453
- const { user: meUser } = WABinary_1.jidDecode(meId);
454
- if (!participant) {
455
- devices.push({ user })
456
- if (user !== meUser) {
457
- devices.push({ user: meUser })
458
- }
459
-
460
- if (additionalAttributes?.['category'] !== 'peer') {
461
- const additionalDevices = await getUSyncDevices([meId, jid], !!useUserDevicesCache, true)
462
-
463
- devices.push(...additionalDevices)
464
- }
465
- }
466
- const allJids = [];
467
- const meJids = [];
468
- const otherJids = [];
469
- for (const { user, device } of devices) {
470
- const isMe = user === meUser
471
- const jid = WABinary_1.jidEncode(isMe && isLid ? authState.creds?.me?.lid?.split(':')[0] || user : user, isLid ? 'lid' : 's.whatsapp.net', device)
472
-
473
- if (isMe) {
474
- meJids.push(jid)
475
- }
424
+ });
425
+ const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
426
+ if (recipientJids.length > 0 && nodes.length === 0) {
427
+ throw new Boom('All encryptions failed', { statusCode: 500 });
428
+ }
429
+ return { nodes, shouldIncludeDeviceIdentity };
430
+ };
431
+ const relayMessage = async (
432
+ jid,
433
+ message,
434
+ {
435
+ messageId: msgId,
436
+ participant = false,
437
+ additionalAttributes,
438
+ additionalNodes,
439
+ useUserDevicesCache,
440
+ useCachedGroupMetadata,
441
+ statusJidList
442
+ }
443
+ ) => {
444
+ const meId = authState.creds.me.id
445
+ const meLid = authState.creds.me?.lid
446
+ const isRetryResend = Boolean(participant?.jid)
447
+ let shouldIncludeDeviceIdentity = isRetryResend
448
+ const statusJid = 'status@broadcast'
449
+ const { user, server } = jidDecode(jid)
450
+ const isGroup = server === 'g.us'
451
+ const isStatus = jid === statusJid
452
+ const isLid = server === 'lid'
453
+ const isNewsletter = server === 'newsletter'
454
+ const isInterop = isInteropUser(jid)
455
+ const isGroupOrStatus = isGroup || isStatus
456
+ const finalJid = jid
457
+ msgId = msgId || generateMessageIDV2(meId)
458
+ useUserDevicesCache = useUserDevicesCache!== false
459
+ useCachedGroupMetadata = useCachedGroupMetadata!== false &&!isStatus
460
+ const participants = []
461
+ const destinationJid =!isStatus? finalJid : statusJid
462
+ const binaryNodeContent = []
463
+ const devices = []
464
+ let reportingMessage
465
+ const meMsg = {
466
+ deviceSentMessage: { destinationJid, message },
467
+ messageContextInfo: message.messageContextInfo
468
+ }
469
+ const extraAttrs = {}
470
+ const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
471
+ const messages = normalizeMessageContent(message)
472
+ const buttonType = getButtonType(messages)
473
+ const pollMessage =
474
+ messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
475
+ await authState.keys.transaction(async () => {
476
+ const mediaType = getMediaType(message)
477
+ if (mediaType) extraAttrs.mediatype = mediaType
478
+ if (isNewsletter) {
479
+ const patched = patchMessageBeforeSending? await patchMessageBeforeSending(message, []) : message
480
+ const bytes = encodeNewsletterMessage(patched)
481
+ binaryNodeContent.push({ tag: 'plaintext', attrs: {}, content: bytes })
482
+ const stanza = {
483
+ tag: 'message',
484
+ attrs: {
485
+ to: jid,
486
+ id: msgId,
487
+ type: getMessageType(message),
488
+ ...(additionalAttributes || {})
489
+ },
490
+ content: binaryNodeContent
491
+ }
492
+ logger.debug({ msgId }, `sending newsletter message to ${jid}`)
493
+ await sendNode(stanza)
494
+ return
495
+ }
496
+ if (normalizeMessageContent(message)?.pinInChatMessage || normalizeMessageContent(message)?.reactionMessage) {
497
+ extraAttrs['decrypt-fail'] = 'hide'
498
+ }
499
+ if (isGroupOrStatus &&!isRetryResend) {
500
+ const [groupData, senderKeyMap] = await Promise.all([
501
+ (async () => {
502
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata? await cachedGroupMetadata(jid) : undefined
503
+ if (groupData && Array.isArray(groupData?.participants)) {
504
+ logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
505
+ } else if (!isStatus) {
506
+ groupData = await groupMetadata(jid)
507
+ }
508
+ return groupData
509
+ })(),
510
+ (async () => {
511
+ if (!participant &&!isStatus) {
512
+ const result = await authState.keys.get('sender-key-memory', [jid])
513
+ return result[jid] || {}
514
+ }
515
+ return {}
516
+ })()
517
+ ])
518
+ const participantsList = groupData? groupData.participants.map(p => p.id) : []
519
+ if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
520
+ additionalAttributes = {...additionalAttributes, expiration: groupData.ephemeralDuration.toString() }
521
+ }
522
+ if (isStatus && statusJidList) participantsList.push(...statusJidList)
523
+ const additionalDevices = await getUSyncDevices(participantsList,!!useUserDevicesCache, false)
524
+ devices.push(...additionalDevices)
525
+ if (isGroup) {
526
+ additionalAttributes = {
527
+ ...additionalAttributes,
528
+ addressing_mode: groupData?.addressingMode || 'lid'
529
+ }
530
+ }
531
+ if (message?.groupStatusMessageV2 &&!message?.messageContextInfo?.messageSecret) {
532
+ message = {
533
+ ...message,
534
+ messageContextInfo: {
535
+ ...(message.messageContextInfo || {}),
536
+ messageSecret: randomBytes(32)
537
+ },
538
+ groupStatusMessageV2: {
539
+ ...message.groupStatusMessageV2,
540
+ message: {
541
+ ...(message.groupStatusMessageV2.message || {}),
542
+ messageContextInfo: {
543
+ ...(message.groupStatusMessageV2.message?.messageContextInfo || {}),
544
+ messageSecret: message.messageContextInfo?.messageSecret || randomBytes(32)
545
+ }
546
+ }
547
+ }
548
+ }
549
+ }
550
+ // list/buttons/template -> interactiveMessage
551
+ if (message.listMessage) {
552
+ const list = message.listMessage
553
+ message = {
554
+ interactiveMessage: {
555
+ nativeFlowMessage: {
556
+ buttons: [
557
+ {
558
+ name: 'single_select',
559
+ buttonParamsJson: JSON.stringify({
560
+ title: list.buttonText || 'Select',
561
+ sections: (list.sections || []).map(section => ({
562
+ title: section.title || '',
563
+ highlight_label: '',
564
+ rows: (section.rows || []).map(row => ({
565
+ header: '',
566
+ title: row.title || '',
567
+ description: row.description || '',
568
+ id: row.rowId || row.id || ''
569
+ }))
570
+ }))
571
+ })
572
+ }
573
+ ],
574
+ messageParamsJson: '',
575
+ messageVersion: 1
576
+ },
577
+ body: { text: list.description || '' },
578
+ footer: list.footerText? { text: list.footerText } : undefined,
579
+ header: list.title? { title: list.title, hasMediaAttachment: false, subtitle: '' } : undefined,
580
+ contextInfo: list.contextInfo
581
+ }
582
+ }
583
+ } else if (message.buttonsMessage) {
584
+ const bMsg = message.buttonsMessage
585
+ const buttons = (bMsg.buttons || []).map(btn => ({
586
+ name: 'quick_reply',
587
+ buttonParamsJson: JSON.stringify({
588
+ display_text: btn.buttonText?.displayText || btn.buttonText || '',
589
+ id: btn.buttonId || btn.buttonText?.displayText || ''
590
+ })
591
+ }))
592
+ message = {
593
+ interactiveMessage: {
594
+ nativeFlowMessage: { buttons, messageParamsJson: '', messageVersion: 1 },
595
+ body: { text: bMsg.contentText || bMsg.text || '' },
596
+ footer: bMsg.footerText? { text: bMsg.footerText } : undefined,
597
+ header: bMsg.text
598
+ ? { title: bMsg.text, hasMediaAttachment: false, subtitle: '' }
599
+ : bMsg.imageMessage || bMsg.videoMessage || bMsg.documentMessage
600
+ ? { hasMediaAttachment: true,...(bMsg.imageMessage? { imageMessage: bMsg.imageMessage } : {}),...(bMsg.videoMessage? { videoMessage: bMsg.videoMessage } : {}) }
601
+ : undefined,
602
+ contextInfo: bMsg.contextInfo
603
+ }
604
+ }
605
+ } else if (message.templateMessage) {
606
+ const tmpl = message.templateMessage.hydratedTemplate || message.templateMessage.fourRowTemplate
607
+ if (tmpl) {
608
+ const buttons = (tmpl.hydratedButtons || [])
609
+ .map(hBtn => {
610
+ if (hBtn.quickReplyButton) {
611
+ return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: hBtn.quickReplyButton.displayText || '', id: hBtn.quickReplyButton.id || hBtn.quickReplyButton.displayText || '' }) }
612
+ } else if (hBtn.urlButton) {
613
+ return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: hBtn.urlButton.displayText || '', url: hBtn.urlButton.url || '', merchant_url: hBtn.urlButton.url || '' }) }
614
+ } else if (hBtn.callButton) {
615
+ return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: hBtn.callButton.displayText || '', phone_number: hBtn.callButton.phoneNumber || '' }) }
616
+ }
617
+ return null
618
+ })
619
+ .filter(Boolean)
620
+ message = {
621
+ interactiveMessage: {
622
+ nativeFlowMessage: { buttons, messageParamsJson: '', messageVersion: 1 },
623
+ body: { text: tmpl.hydratedContentText || tmpl.contentText || '' },
624
+ footer: tmpl.hydratedFooterText? { text: tmpl.hydratedFooterText } : undefined,
625
+ header: tmpl.hydratedTitleText
626
+ ? { title: tmpl.hydratedTitleText, hasMediaAttachment: false, subtitle: '' }
627
+ : tmpl.imageMessage || tmpl.videoMessage || tmpl.documentMessage
628
+ ? { hasMediaAttachment: true,...(tmpl.imageMessage? { imageMessage: tmpl.imageMessage } : {}),...(tmpl.videoMessage? { videoMessage: tmpl.videoMessage } : {}) }
629
+ : undefined,
630
+ contextInfo: tmpl.contextInfo
631
+ }
632
+ }
633
+ }
634
+ }
476
635
 
477
- else {
478
- otherJids.push(jid)
636
+ const patched = await patchMessageBeforeSending(message)
637
+ if (Array.isArray(patched)) throw new Boom('Per-jid patching is not supported in groups')
638
+ const bytes = encodeWAMessage(patched)
639
+ reportingMessage = patched
640
+ const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid'
641
+ const groupSenderIdentity = groupAddressingMode === 'lid' && meLid? meLid : meId
642
+ const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
643
+ group: destinationJid,
644
+ data: bytes,
645
+ meId: groupSenderIdentity
646
+ })
647
+ const senderKeyRecipients = []
648
+ for (const device of devices) {
649
+ const deviceJid = device.jid
650
+ const hasKey =!!senderKeyMap[deviceJid]
651
+ if (!hasKey ||!!participant &&!isHostedLidUser(deviceJid) &&!isHostedPnUser(deviceJid) && device.device!== 99) {
652
+ senderKeyRecipients.push(deviceJid)
653
+ senderKeyMap[deviceJid] = true
654
+ }
655
+ }
656
+ if (senderKeyRecipients.length) {
657
+ logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key')
658
+ const senderKeyMsg = {
659
+ senderKeyDistributionMessage: {
660
+ axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
661
+ groupId: destinationJid
662
+ }
663
+ }
664
+ await assertSessions(senderKeyRecipients)
665
+ const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs)
666
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
667
+ participants.push(...result.nodes)
668
+ }
669
+ binaryNodeContent.push({ tag: 'enc', attrs: { v: '2', type: 'skmsg',...extraAttrs }, content: ciphertext })
670
+ await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
671
+ } else {
672
+ let ownId = meId
673
+ if (isLid && meLid) {
674
+ ownId = meLid
675
+ logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation')
676
+ } else {
677
+ logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
678
+ }
679
+ const { user: ownUser } = jidDecode(ownId)
680
+ if (!participant) {
681
+ const patchedForReporting = await patchMessageBeforeSending(message, [jid])
682
+ reportingMessage = Array.isArray(patchedForReporting)
683
+ ? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
684
+ : patchedForReporting
685
+ }
686
+ if (!isRetryResend) {
687
+ const targetUserServer = isLid? 'lid' : isInterop? 'interop' : 's.whatsapp.net'
688
+ devices.push({ user, device: 0, jid: jidEncode(user, targetUserServer, 0) })
689
+ if (user!== ownUser &&!isInterop) {
690
+ const ownUserServer = isLid? 'lid' : 's.whatsapp.net'
691
+ const ownUserForAddressing = isLid && meLid? jidDecode(meLid).user : jidDecode(meId).user
692
+ devices.push({ user: ownUserForAddressing, device: 0, jid: jidEncode(ownUserForAddressing, ownUserServer, 0) })
693
+ }
694
+ if (additionalAttributes?.['category']!== 'peer' &&!isInterop) {
695
+ devices.length = 0
696
+ const senderIdentity = isLid && meLid
697
+ ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
698
+ : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined)
699
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false)
700
+ devices.push(...sessionDevices)
701
+ logger.debug({ deviceCount: devices.length, devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`) }, 'Device enumeration complete with unified addressing')
702
+ }
703
+ }
704
+ const allRecipients = []
705
+ const meRecipients = []
706
+ const otherRecipients = []
707
+ const { user: mePnUser } = jidDecode(meId)
708
+ const { user: meLidUser } = meLid? jidDecode(meLid) : { user: null }
709
+ for (const { user, jid } of devices) {
710
+ /** participant method by Xzc || Tsm, who's delete the credits = love BBC */
711
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid)
712
+ if (isExactSenderDevice) {
713
+ logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)')
714
+ continue
479
715
  }
480
-
481
- allJids.push(jid)
482
- }
483
- await assertSessions(allJids, false);
484
- const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
485
- createParticipantNodes(meJids, meMsg, extraAttrs),
486
- createParticipantNodes(otherJids, message, extraAttrs)
487
- ])
488
- participants.push(...meNodes);
489
- participants.push(...otherNodes);
490
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
491
- }
492
- if (participants.length) {
493
- if (additionalAttributes?.['category'] === 'peer') {
494
- const peerNode = participants[0]?.content?.[0]
495
-
496
- if (peerNode) {
497
- binaryNodeContent.push(peerNode) // push only enc
716
+ const isMe = user === mePnUser || user === meLidUser
717
+ let ptcp = false
718
+ if (participant) {
719
+ if (!isJidGroup(jid) && !isStatus) {
720
+ if (!(!isMe)) ptcp = true
721
+ } else {
722
+ ptcp = false
723
+ }
498
724
  }
499
- }
500
-
501
- else {
502
- binaryNodeContent.push({
503
- tag: 'participants',
504
- attrs: {},
505
- content: participants
506
- })
507
- }
508
- }
509
-
510
- const stanza = {
511
- tag: 'message',
512
- attrs: {
513
- id: msgId,
514
- type: getTypeMessage(messages),
515
- ...(additionalAttributes || {})
516
- },
517
- content: binaryNodeContent
518
- }
519
- // if the participant to send to is explicitly specified (generally retry recp)
520
- // ensure the message is only sent to that person
521
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
522
- if (participant) {
523
- if (WABinary_1.isJidGroup(destinationJid)) {
524
- stanza.attrs.to = destinationJid;
525
- stanza.attrs.participant = participant.jid;
526
- }
527
- else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
528
- stanza.attrs.to = participant.jid;
529
- stanza.attrs.recipient = destinationJid;
530
- }
531
- else {
532
- stanza.attrs.to = participant.jid;
533
- }
534
- }
535
- else {
536
- stanza.attrs.to = destinationJid;
537
- }
538
- if (shouldIncludeDeviceIdentity) {
539
- stanza.content.push({
540
- tag: 'device-identity',
541
- attrs: {},
542
- content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
543
- });
544
- logger.debug({ jid }, 'adding device identity');
545
- }
546
-
547
- if (AI && isPrivate) {
548
- const botNode = {
549
- tag: 'bot',
550
- attrs: {
551
- biz_bot: '1'
725
+ if (!ptcp) {
726
+ if (isMe) {
727
+ meRecipients.push(jid)
728
+ } else {
729
+ otherRecipients.push(jid)
730
+ }
731
+ allRecipients.push(jid)
552
732
  }
553
733
  }
734
+ await assertSessions(allRecipients)
735
+ const [
736
+ { nodes: meNodes, shouldIncludeDeviceIdentity: s1 },
737
+ { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }
738
+ ] = await Promise.all([
739
+ createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
740
+ createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
741
+ ])
742
+ participants.push(...meNodes,...otherNodes)
743
+ if (meRecipients.length > 0 || otherRecipients.length > 0) {
744
+ extraAttrs.phash = generateParticipantHashV2([...meRecipients,...otherRecipients])
745
+ }
746
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
747
+ }
748
+ if (isRetryResend) {
749
+ const isParticipantLid = jidDecode(participant.jid).server === 'lid'
750
+ const isMe = areJidsSameUser(participant.jid, isParticipantLid? meLid : meId)
751
+ const encodedMessageToSend = isMe
752
+ ? encodeWAMessage({ deviceSentMessage: { destinationJid, message } })
753
+ : encodeWAMessage(message)
754
+ const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
755
+ data: encodedMessageToSend,
756
+ jid: participant.jid
757
+ })
758
+ binaryNodeContent.push({
759
+ tag: 'enc',
760
+ attrs: { v: '2', type, count: (participant.count?? 0).toString() },
761
+ content: encryptedContent
762
+ })
763
+ }
764
+ if (participants.length) {
765
+ if (additionalAttributes?.['category'] === 'peer') {
766
+ const peerNode = participants[0]?.content?.[0]
767
+ if (peerNode) binaryNodeContent.push(peerNode)
768
+ } else if (isInterop) {
769
+ const recipientNode = participants.find(p => isInteropUser(p?.attrs?.jid))
770
+ const encNode = (recipientNode?? participants[0])?.content?.[0]
771
+ if (encNode) binaryNodeContent.push(encNode)
772
+ } else {
773
+ binaryNodeContent.push({ tag: 'participants', attrs: {}, content: participants })
774
+ }
775
+ }
776
+ const stanza = {
777
+ tag: 'message',
778
+ attrs: { id: msgId, to: destinationJid, type: getMessageType(message),...(additionalAttributes || {}) },
779
+ content: binaryNodeContent
780
+ }
781
+ if (shouldIncludeDeviceIdentity) {
782
+ stanza.content.push({ tag: 'device-identity', attrs: {}, content: encodeSignedDeviceIdentity(authState.creds.account, true) })
783
+ logger.debug({ jid }, 'adding device identity')
784
+ }
554
785
 
555
- const filteredBizBot = WABinary_1.getBinaryNodeFilter(additionalNodes ? additionalNodes : [])
556
-
557
- if (filteredBizBot) {
558
- stanza.content.push(...additionalNodes)
559
- didPushAdditional = true
560
- }
561
-
562
- else {
563
- stanza.content.push(botNode)
564
- }
565
- }
566
-
567
- if(!isNewsletter && buttonType && !isStatus) {
568
- const content = WABinary_1.getAdditionalNode(buttonType)
569
- const filteredNode = WABinary_1.getBinaryNodeFilter(additionalNodes)
570
-
571
- if (filteredNode) {
572
- didPushAdditional = true
573
- stanza.content.push(...additionalNodes)
574
- }
575
- else {
576
- stanza.content.push(...content)
577
- }
578
- logger.debug({ jid }, 'adding business node')
579
- }
580
-
581
- if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
582
- stanza.content.push(...additionalNodes);
583
- }
584
-
585
- logger.debug({ msgId }, `sending message to ${participants.length} devices`);
586
- await sendNode(stanza);
587
- });
588
-
589
- message = Types_1.WAProto.Message.fromObject(message)
590
-
591
- const messageJSON = {
592
- key: {
593
- remoteJid: jid,
594
- fromMe: true,
595
- id: msgId
596
- },
597
- message: message,
598
- messageTimestamp: Utils_1.unixTimestampSeconds(new Date()),
599
- messageStubParameters: [],
600
- participant: WABinary_1.isJidGroup(jid) || WABinary_1.isJidStatusBroadcast(jid) ? meId : undefined,
601
- status: Types_1.WAMessageStatus.PENDING
786
+ if (isGroup && regexGroupOld.test(jid) &&!message.reactionMessage) {
787
+ stanza.content.push({ tag: 'multicast', attrs: {} })
788
+ }
789
+ if (pollMessage || messages.eventMessage) {
790
+ stanza.content.push({
791
+ tag: 'meta',
792
+ attrs: messages.eventMessage
793
+ ? { event_type: 'creation' }
794
+ : isNewsletter
795
+ ? { polltype: 'creation', contenttype: pollMessage?.pollContentType === 2? 'image' : 'text' }
796
+ : { polltype: 'creation' }
797
+ })
798
+ }
799
+ if (!isNewsletter &&!isRetryResend && reportingMessage?.messageContextInfo?.messageSecret && shouldIncludeReportingToken(reportingMessage)) {
800
+ try {
801
+ const encoded = encodeWAMessage(reportingMessage)
802
+ const reportingKey = { id: msgId, fromMe: true, remoteJid: destinationJid, participant: participant?.jid }
803
+ const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey)
804
+ if (reportingNode) {
805
+ stanza.content.push(reportingNode)
806
+ logger.trace({ jid }, 'added reporting token to message')
807
+ }
808
+ } catch (error) {
809
+ logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token')
810
+ }
811
+ }
812
+ let didPushAdditional = false
813
+ if (!isNewsletter && buttonType) {
814
+ const buttonsNode = getButtonArgs(messages)
815
+ const filteredButtons = getBinaryNodeFilter(additionalNodes? additionalNodes : [])
816
+ if (filteredButtons) {
817
+ stanza.content.push(...additionalNodes)
818
+ didPushAdditional = true
819
+ } else {
820
+ stanza.content.push(buttonsNode)
821
+ }
822
+ }
823
+ if (!aiLabel && isPnUser(destinationJid)) {
824
+ const alreadyHasBizBot = getBinaryFilteredBizBot(additionalNodes || []) || getBinaryFilteredBizBot(stanza.content)
825
+ if (!alreadyHasBizBot) stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
826
+ } else if (aiLabel &&!isGroup &&!isStatus &&!isNewsletter) {
827
+ const existingBizBot = getBinaryFilteredBizBot(additionalNodes || [])
828
+ if (!existingBizBot) stanza.content.push({ tag: 'bot', attrs: { biz_bot: '1' } })
829
+ }
830
+ const isPeerMessage = additionalAttributes?.['category'] === 'peer'
831
+ const is1on1Send =!isGroup &&!isRetryResend &&!isStatus &&!isNewsletter &&!isPeerMessage
832
+ const tcTokenJid = is1on1Send? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid
833
+ const contactTcTokenData = is1on1Send? await authState.keys.get('tctoken', [tcTokenJid]) : {}
834
+ const existingTokenEntry = contactTcTokenData[tcTokenJid]
835
+ let tcTokenBuffer = existingTokenEntry?.token
836
+ if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
837
+ logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing')
838
+ tcTokenBuffer = undefined
839
+ const cleared = existingTokenEntry?.senderTimestamp!== undefined? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp } : null
840
+ try {
841
+ await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } })
842
+ } catch (err) {
843
+ logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup')
844
+ }
845
+ }
846
+ if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
847
+ stanza.content.push({ tag: 'tctoken', attrs: {}, content: tcTokenBuffer })
848
+ }
849
+ if (additionalNodes && additionalNodes.length > 0 &&!didPushAdditional) {
850
+ stanza.content.push(...additionalNodes)
851
+ }
852
+ logger.debug({ msgId }, `sending message to ${participants.length} devices`)
853
+ await sendNode(stanza)
854
+ if (message.messageContextInfo?.messageSecret) {
855
+ setBotMessageSecret(msgId, message.messageContextInfo.messageSecret, destinationJid)
856
+ }
857
+ const isProtocolMsg =!!normalizeMessageContent(message)?.protocolMessage
858
+ const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid)
859
+ if (is1on1Send &&!isProtocolMsg &&!isBotOrPSA && shouldSendNewTcToken(existingTokenEntry?.senderTimestamp) &&!inFlightTcTokenIssuance.has(tcTokenJid)) {
860
+ inFlightTcTokenIssuance.add(tcTokenJid)
861
+ const issueTimestamp = unixTimestampSeconds()
862
+ const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping)
863
+ resolveIssuanceJid(destinationJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID)
864
+ .then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
865
+ .then(async result => {
866
+ await storeTcTokensFromIqResult({ result, fallbackJid: tcTokenJid, keys: authState.keys, getLIDForPN })
867
+ const currentData = await authState.keys.get('tctoken', [tcTokenJid])
868
+ const currentEntry = currentData[tcTokenJid]
869
+ const indexWrite = await buildMergedTcTokenIndexWrite(authState.keys, [tcTokenJid])
870
+ await authState.keys.set({
871
+ tctoken: {
872
+ [tcTokenJid]: { token: Buffer.alloc(0),...currentEntry, senderTimestamp: issueTimestamp },
873
+ ...indexWrite
874
+ }
875
+ })
876
+ })
877
+ .catch(err => logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed'))
878
+ .finally(() => inFlightTcTokenIssuance.delete(tcTokenJid))
879
+ }
880
+ if (messageRetryManager &&!participant) {
881
+ messageRetryManager.addRecentMessage(destinationJid, msgId, message)
882
+ }
883
+ if (isInterop &&!isRetryResend) {
884
+ await trustInteropContact(destinationJid).catch(err => logger.debug({ err, jid: destinationJid }, 'failed to trust interop contact'))
885
+ }
886
+ }, meId)
887
+ return msgId
888
+ }
889
+ const getMessageType = (message) => {
890
+ const normalizedMessage = normalizeMessageContent(message);
891
+ if (!normalizedMessage)
892
+ return 'text';
893
+ if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
894
+ return 'reaction';
602
895
  }
603
-
604
- return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON)
605
- // return msgId;
606
- };
607
- const getTypeMessage = (msg) => {
608
- const message = Utils_1.normalizeMessageContent(msg)
609
- if (message.reactionMessage) {
610
- return 'reaction'
611
- }
612
- else if (getMediaType(message)) {
613
- return 'media'
614
- }
615
- else {
616
- return 'text'
896
+ if (normalizedMessage.pollCreationMessage ||
897
+ normalizedMessage.pollCreationMessageV2 ||
898
+ normalizedMessage.pollCreationMessageV3 ||
899
+ normalizedMessage.pollCreationMessageV4 ||
900
+ normalizedMessage.pollCreationMessageV5 ||
901
+ normalizedMessage.pollUpdateMessage) {
902
+ return 'poll';
617
903
  }
618
- }
619
-
904
+ if (normalizedMessage.eventMessage) {
905
+ return 'event';
906
+ }
907
+ if (getMediaType(normalizedMessage) !== '') {
908
+ return 'media';
909
+ }
910
+ return 'text';
911
+ };
620
912
  const getMediaType = (message) => {
621
913
  if (message.imageMessage) {
622
- return 'image'
914
+ return 'image';
623
915
  }
624
916
  else if (message.videoMessage) {
625
- return message.videoMessage.gifPlayback ? 'gif' : 'video'
917
+ return message.videoMessage.gifPlayback ? 'gif' : 'video';
626
918
  }
627
919
  else if (message.audioMessage) {
628
- return message.audioMessage.ptt ? 'ptt' : 'audio'
920
+ return message.audioMessage.ptt ? 'ptt' : 'audio';
629
921
  }
630
922
  else if (message.contactMessage) {
631
- return 'vcard'
923
+ return 'vcard';
632
924
  }
633
925
  else if (message.documentMessage) {
634
- return 'document'
926
+ return 'document';
635
927
  }
636
928
  else if (message.contactsArrayMessage) {
637
- return 'contact_array'
929
+ return 'contact_array';
638
930
  }
639
931
  else if (message.liveLocationMessage) {
640
- return 'livelocation'
932
+ return 'livelocation';
641
933
  }
642
934
  else if (message.stickerMessage) {
643
- return 'sticker'
935
+ return 'sticker';
644
936
  }
645
937
  else if (message.listMessage) {
646
- return 'list'
938
+ return 'list';
647
939
  }
648
940
  else if (message.listResponseMessage) {
649
- return 'list_response'
941
+ return 'list_response';
650
942
  }
651
943
  else if (message.buttonsResponseMessage) {
652
- return 'buttons_response'
944
+ return 'buttons_response';
653
945
  }
654
946
  else if (message.orderMessage) {
655
- return 'order'
947
+ return 'order';
656
948
  }
657
949
  else if (message.productMessage) {
658
- return 'product'
950
+ return 'product';
659
951
  }
660
952
  else if (message.interactiveResponseMessage) {
661
- return 'native_flow_response'
953
+ return 'native_flow_response';
662
954
  }
663
955
  else if (message.groupInviteMessage) {
664
- return 'url'
956
+ return 'url';
665
957
  }
666
- else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
667
- return 'productlink'
668
- }
669
- }
670
-
958
+ return '';
959
+ };
671
960
  const getButtonType = (message) => {
672
961
  if (message.listMessage) {
673
962
  return 'list'
@@ -683,26 +972,152 @@ const makeMessagesSocket = (config) => {
683
972
  }
684
973
  else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_info') {
685
974
  return 'payment_info'
686
- }
687
- else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
975
+ } else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_key_info') {
976
+ return 'payment_key_info'
977
+ } else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_status') {
688
978
  return 'payment_status'
689
979
  }
690
980
  else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'payment_method') {
691
981
  return 'payment_method'
692
982
  }
983
+ else if (message.interactiveMessage?.nativeFlowMessage?.buttons?.[0]?.name === 'catalog_message') {
984
+ return 'catalog_message'
985
+ }
693
986
  else if (message.interactiveMessage && message.interactiveMessage?.nativeFlowMessage) {
694
987
  return 'interactive'
695
988
  }
696
989
  else if (message.interactiveMessage?.nativeFlowMessage) {
697
990
  return 'native_flow'
698
991
  }
699
- }
700
- const getPrivacyTokens = async (jids) => {
701
- const t = Utils_1.unixTimestampSeconds().toString();
992
+ };
993
+ const getButtonArgs = (message) => {
994
+ const nativeFlow = message.interactiveMessage?.nativeFlowMessage
995
+ const firstButtonName = nativeFlow?.buttons?.[0]?.name
996
+ const nativeFlowSpecials = [
997
+ 'mpm',
998
+ 'cta_catalog',
999
+ 'send_location',
1000
+ 'call_permission_request',
1001
+ 'wa_payment_transaction_details',
1002
+ 'automated_greeting_message_view_catalog'
1003
+ ]
1004
+
1005
+ if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
1006
+ return {
1007
+ tag: 'biz',
1008
+ attrs: {
1009
+ native_flow_name: firstButtonName === 'review_and_pay' ? 'order_details' : firstButtonName
1010
+ }
1011
+ }
1012
+ } else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
1013
+ // Only works for WhatsApp Original, not WhatsApp Business
1014
+ return {
1015
+ tag: 'biz',
1016
+ attrs: {
1017
+ actual_actors: '2',
1018
+ host_storage: '2',
1019
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1020
+ },
1021
+ content: [
1022
+ {
1023
+ tag: 'interactive',
1024
+ attrs: {
1025
+ type: 'native_flow',
1026
+ v: '1'
1027
+ },
1028
+ content: [
1029
+ {
1030
+ tag: 'native_flow',
1031
+ attrs: {
1032
+ v: '2',
1033
+ name: firstButtonName
1034
+ }
1035
+ }
1036
+ ]
1037
+ },
1038
+ {
1039
+ tag: 'quality_control',
1040
+ attrs: {
1041
+ source_type: 'third_party'
1042
+ }
1043
+ }
1044
+ ]
1045
+ }
1046
+ } else if (nativeFlow || message.buttonsMessage) {
1047
+ // It works for whatsapp original and whatsapp business
1048
+ return {
1049
+ tag: 'biz',
1050
+ attrs: {
1051
+ actual_actors: '2',
1052
+ host_storage: '2',
1053
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1054
+ },
1055
+ content: [
1056
+ {
1057
+ tag: 'interactive',
1058
+ attrs: {
1059
+ type: 'native_flow',
1060
+ v: '1'
1061
+ },
1062
+ content: [
1063
+ {
1064
+ tag: 'native_flow',
1065
+ attrs: {
1066
+ v: '9',
1067
+ name: 'mixed'
1068
+ }
1069
+ }
1070
+ ]
1071
+ },
1072
+ {
1073
+ tag: 'quality_control',
1074
+ attrs: {
1075
+ source_type: 'third_party'
1076
+ }
1077
+ }
1078
+ ]
1079
+ }
1080
+ } else if (message.listMessage) {
1081
+ return {
1082
+ tag: 'biz',
1083
+ attrs: {
1084
+ actual_actors: '2',
1085
+ host_storage: '2',
1086
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1087
+ },
1088
+ content: [
1089
+ {
1090
+ tag: 'list',
1091
+ attrs: {
1092
+ v: '2',
1093
+ type: 'product_list'
1094
+ }
1095
+ },
1096
+ {
1097
+ tag: 'quality_control',
1098
+ attrs: {
1099
+ source_type: 'third_party'
1100
+ }
1101
+ }
1102
+ ]
1103
+ }
1104
+ } else {
1105
+ return {
1106
+ tag: 'biz',
1107
+ attrs: {
1108
+ actual_actors: '2',
1109
+ host_storage: '2',
1110
+ privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1111
+ }
1112
+ }
1113
+ }
1114
+ }
1115
+ const issuePrivacyTokens = async (jids, timestamp) => {
1116
+ const t = (timestamp ?? unixTimestampSeconds()).toString();
702
1117
  const result = await query({
703
1118
  tag: 'iq',
704
1119
  attrs: {
705
- to: WABinary_1.S_WHATSAPP_NET,
1120
+ to: S_WHATSAPP_NET,
706
1121
  type: 'set',
707
1122
  xmlns: 'privacy'
708
1123
  },
@@ -713,7 +1128,7 @@ const makeMessagesSocket = (config) => {
713
1128
  content: jids.map(jid => ({
714
1129
  tag: 'token',
715
1130
  attrs: {
716
- jid: WABinary_1.jidNormalizedUser(jid),
1131
+ jid: jidNormalizedUser(jid),
717
1132
  t,
718
1133
  type: 'trusted_contact'
719
1134
  }
@@ -722,34 +1137,47 @@ const makeMessagesSocket = (config) => {
722
1137
  ]
723
1138
  });
724
1139
  return result;
725
- }
726
- const waUploadToServer = (0, Utils_1.getWAUploadToServer)(config, refreshMediaConn);
727
- const rahmi = new kikyy(Utils_1, waUploadToServer, relayMessage);
728
- const waitForMsgMediaUpdate = (0, Utils_1.bindWaitForEvent)(ev, 'messages.media-update');
1140
+ };
1141
+ const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
1142
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
1143
+ registerSocketEndHandler(() => {
1144
+ if (!config.userDevicesCache && userDevicesCache.close) {
1145
+ userDevicesCache.close();
1146
+ }
1147
+ mediaConn = undefined;
1148
+ if (messageRetryManager) {
1149
+ messageRetryManager.clear();
1150
+ }
1151
+ });
729
1152
  return {
730
1153
  ...sock,
731
- getPrivacyTokens,
1154
+ userDevicesCache,
1155
+ devicesMutex,
1156
+ issuePrivacyTokens,
732
1157
  assertSessions,
733
1158
  relayMessage,
734
1159
  sendReceipt,
735
1160
  sendReceipts,
736
- rahmi,
737
1161
  readMessages,
738
1162
  refreshMediaConn,
739
- getUSyncDevices,
740
- createParticipantNodes,
1163
+ // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1164
+ getMediaHost: () => mediaHost,
741
1165
  waUploadToServer,
742
- sendPeerDataOperationMessage,
743
1166
  fetchPrivacySettings,
1167
+ sendPeerDataOperationMessage,
1168
+ createParticipantNodes,
1169
+ getUSyncDevices,
1170
+ messageRetryManager,
1171
+ updateMemberLabel,
744
1172
  updateMediaMessage: async (message) => {
745
- const content = (0, Utils_1.assertMediaContent)(message.message);
1173
+ const content = assertMediaContent(message.message);
746
1174
  const mediaKey = content.mediaKey;
747
1175
  const meId = authState.creds.me.id;
748
- const node = (0, Utils_1.encryptMediaRetryRequest)(message.key, mediaKey, meId);
1176
+ const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
749
1177
  let error = undefined;
750
1178
  await Promise.all([
751
1179
  sendNode(node),
752
- waitForMsgMediaUpdate(update => {
1180
+ waitForMsgMediaUpdate(async (update) => {
753
1181
  const result = update.find(c => c.key.id === message.key.id);
754
1182
  if (result) {
755
1183
  if (result.error) {
@@ -757,13 +1185,16 @@ const makeMessagesSocket = (config) => {
757
1185
  }
758
1186
  else {
759
1187
  try {
760
- const media = (0, Utils_1.decryptMediaRetryData)(result.media, mediaKey, result.key.id);
761
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
762
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result];
763
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: (0, Utils_1.getStatusCodeForMediaRetry)(media.result) || 404 });
1188
+ const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
1189
+ if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
1190
+ const resultStr = proto.MediaRetryNotification.ResultType[media.result];
1191
+ throw new Boom(`Media re-upload failed by device (${resultStr})`, {
1192
+ data: media,
1193
+ statusCode: getStatusCodeForMediaRetry(media.result) || 404
1194
+ });
764
1195
  }
765
1196
  content.directPath = media.directPath;
766
- content.url = (0, Utils_1.getUrlFromDirectPath)(content.directPath);
1197
+ content.url = getUrlFromDirectPath(content.directPath, mediaHost);
767
1198
  logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
768
1199
  }
769
1200
  catch (err) {
@@ -777,133 +1208,228 @@ const makeMessagesSocket = (config) => {
777
1208
  if (error) {
778
1209
  throw error;
779
1210
  }
780
- ev.emit('messages.update', [
781
- {
782
- key: message.key,
783
- update: {
784
- message: message.message
785
- }
786
- }
787
- ]);
1211
+ ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
788
1212
  return message;
789
1213
  },
1214
+ sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
1215
+ const { message, messageId } = Utils_1.generateTableContent(title, headers, rows, quoted, options)
1216
+ await relayMessage(jid, message, { messageId })
1217
+ return { message, messageId }
1218
+ },
1219
+ sendList: async (jid, title, items, quoted, options = {}) => {
1220
+ const { message, messageId } = Utils_1.generateListContent(title, items, quoted, options)
1221
+ await relayMessage(jid, message, { messageId })
1222
+ return { message, messageId }
1223
+ },
1224
+ sendCodeBlock: async (jid, code, quoted, options = {}) => {
1225
+ const { message, messageId } = Utils_1.generateCodeBlockContent(code, quoted, options)
1226
+ await relayMessage(jid, message, { messageId })
1227
+ return { message, messageId }
1228
+ },
1229
+ sendLatex: async (jid, quoted, options) => {
1230
+ const { message, messageId } = Utils_1.generateLatexContent(quoted, options)
1231
+ await relayMessage(jid, message, { messageId })
1232
+ return { message, messageId }
1233
+ },
1234
+ sendLatexImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1235
+ const { message, messageId } = await Utils_1.generateLatexImageContent(
1236
+ quoted,
1237
+ options,
1238
+ uploadFn,
1239
+ renderLatexToPng
1240
+ )
1241
+ await relayMessage(jid, message, { messageId })
1242
+ return { message, messageId }
1243
+ },
1244
+ sendLatexInlineImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1245
+ const { message, messageId } = await Utils_1.generateLatexInlineImageContent(
1246
+ quoted,
1247
+ options,
1248
+ uploadFn,
1249
+ renderLatexToPng
1250
+ )
1251
+ await relayMessage(jid, message, { messageId })
1252
+ return { message, messageId }
1253
+ },
1254
+ captureUnifiedResponse: Utils_1.captureUnifiedResponse,
1255
+ sendUnifiedResponse: async (jid, quoted, captured) => {
1256
+ const { message, messageId } = Utils_1.generateUnifiedResponseContent(quoted, captured)
1257
+ await relayMessage(jid, message, { messageId })
1258
+ return { message, messageId }
1259
+ },
1260
+ sendRichMessage: async (jid, submessages, quoted, options = {}) => {
1261
+ const { message, messageId } = Utils_1.generateRichMessageContent(submessages, quoted, options)
1262
+ await relayMessage(jid, message, { messageId })
1263
+ return { message, messageId }
1264
+ },
790
1265
  sendMessage: async (jid, content, options = {}) => {
791
1266
  const userJid = authState.creds.me.id;
792
- delete options.ephemeralExpiration
793
- const { filter = false, quoted } = options;
794
- const getParticipantAttr = () => filter ? { participant: { jid } } : {};
795
- const messageType = rahmi.detectType(content);
796
- if (typeof content === 'object' && 'disappearingMessagesInChat' in content &&
797
- typeof content['disappearingMessagesInChat'] !== 'undefined' && WABinary_1.isJidGroup(jid)) {
798
- const { disappearingMessagesInChat } = content
799
-
800
- const value = typeof disappearingMessagesInChat === 'boolean' ?
801
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
802
- disappearingMessagesInChat
803
-
804
- await groupToggleEphemeral(jid, value)
1267
+ const luki = new imup(Utils_1, waUploadToServer, relayMessage)
1268
+ const { quoted, participant = false } = options;
1269
+ const messageType = luki.detectType(content);
1270
+ if (typeof content === 'object' &&
1271
+ 'disappearingMessagesInChat' in content &&
1272
+ typeof content['disappearingMessagesInChat'] !== 'undefined' &&
1273
+ isJidGroup(jid)) {
1274
+ const { disappearingMessagesInChat } = content;
1275
+ const value = typeof disappearingMessagesInChat === 'boolean'
1276
+ ? disappearingMessagesInChat
1277
+ ? WA_DEFAULT_EPHEMERAL
1278
+ : 0
1279
+ : disappearingMessagesInChat;
1280
+ await groupToggleEphemeral(jid, value);
805
1281
  }
806
-
807
1282
  else {
808
- let mediaHandle
809
-
810
-
811
- if (messageType) {
812
- switch(messageType) {
813
- case 'PAYMENT':
814
- const paymentContent = await rahmi.handlePayment(content, quoted);
815
- return await relayMessage(jid, paymentContent, {
816
- messageId: Utils_1.generateMessageID(),
817
- ...getParticipantAttr()
818
- });
819
-
820
- case 'PRODUCT':
821
- const productContent = await rahmi.handleProduct(content, jid, quoted);
822
- const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
823
- return await relayMessage(jid, productMsg.message, {
824
- messageId: productMsg.key.id,
825
- ...getParticipantAttr()
826
- });
827
-
828
- case 'INTERACTIVE':
829
- const interactiveContent = await rahmi.handleInteractive(content, jid, quoted);
830
- const interactiveMsg = await Utils_1.generateWAMessageFromContent(jid, interactiveContent, { quoted });
831
- return await relayMessage(jid, interactiveMsg.message, {
832
- messageId: interactiveMsg.key.id,
833
- ...getParticipantAttr()
834
- });
835
- case 'ALBUM':
836
- return await rahmi.handleAlbum(content, jid, quoted)
837
- case 'EVENT':
838
- return await rahmi.handleEvent(content, jid, quoted)
839
- case 'POLL_RESULT':
840
- return await rahmi.handlePollResult(content, jid, quoted)
841
- case 'GROUP_STORY':
842
- return await rahmi.handleGroupStory(content, jid, quoted)
1283
+ if (messageType) {
1284
+ switch(messageType) {
1285
+ case 'PAYMENT':
1286
+ const paymentContent = await luki.handlePayment(content, quoted);
1287
+ return await relayMessage(jid, paymentContent, {
1288
+ messageId: Utils_1.generateMessageID()
1289
+ });
1290
+ case 'PRODUCT':
1291
+ const productContent = await luki.handleProduct(content, jid, quoted);
1292
+ const productMsg = await Utils_1.generateWAMessageFromContent(jid, productContent, { quoted });
1293
+ return await relayMessage(jid, productMsg.message, {
1294
+ messageId: productMsg.key.id,
1295
+ });
1296
+
1297
+ case 'ALBUM':
1298
+ return await luki.handleAlbum(content, jid, quoted)
1299
+ case 'EVENT':
1300
+ return await luki.handleEvent(content, jid, quoted)
1301
+ case 'POLL_RESULT':
1302
+ return await luki.handlePollResult(content, jid, quoted)
1303
+ case 'ORDER':
1304
+ return await luki.handleOrderMessage(content, jid, quoted)
1305
+ case 'GROUP_STATUS':
1306
+ return await luki.handleGroupStory(content, jid, quoted)
1307
+ case 'GROUP_LABEL':
1308
+ return await luki.handleGbLabel(content, jid)
1309
+ }
843
1310
  }
844
- }
845
- const fullMsg = await Utils_1.generateWAMessage(jid, content, {
846
- logger,
847
- userJid,
848
- quoted,
849
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
850
- thumbnailWidth: linkPreviewImageThumbnailWidth,
851
- fetchOpts: {
852
- timeout: 3000,
853
- ...axiosOptions || {}
854
- },
1311
+ const fullMsg = await generateWAMessage(jid, content, {
855
1312
  logger,
856
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
857
- }),
858
- upload: async (readStream, opts) => {
859
- const up = await waUploadToServer(readStream, {
860
- ...opts,
861
- newsletter: WABinary_1.isJidNewsLetter(jid)
1313
+ userJid,
1314
+ getUrlInfo: text => getUrlInfo(text, {
1315
+ thumbnailWidth: linkPreviewImageThumbnailWidth,
1316
+ fetchOpts: {
1317
+ timeout: 3000,
1318
+ ...(httpRequestOptions || {})
1319
+ },
1320
+ logger,
1321
+ uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1322
+ }),
1323
+ //TODO: CACHE
1324
+ getProfilePicUrl: sock.profilePictureUrl,
1325
+ getCallLink: sock.createCallLink,
1326
+ upload: waUploadToServer,
1327
+ mediaCache: config.mediaCache,
1328
+ options: config.options,
1329
+ messageId: generateMessageIDV2(sock.user?.id),
1330
+ ...options
1331
+ });
1332
+ const isEventMsg = 'event' in content && !!content.event;
1333
+ const isDeleteMsg = 'delete' in content && !!content.delete;
1334
+ const isEditMsg = 'edit' in content && !!content.edit;
1335
+ const isPinMsg = 'pin' in content && !!content.pin;
1336
+ const isPollMessage = 'poll' in content && !!content.poll;
1337
+ const additionalAttributes = {};
1338
+ const additionalNodes = [];
1339
+ // required for delete
1340
+ if (isDeleteMsg) {
1341
+ // if the chat is a group, and I am not the author, then delete the message as an admin
1342
+ if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1343
+ additionalAttributes.edit = '8';
1344
+ }
1345
+ else {
1346
+ additionalAttributes.edit = '7';
1347
+ }
1348
+ }
1349
+ else if (isEditMsg) {
1350
+ additionalAttributes.edit = '1';
1351
+ }
1352
+ else if (isPinMsg) {
1353
+ additionalAttributes.edit = '2';
1354
+ }
1355
+ else if (isPollMessage) {
1356
+ additionalNodes.push({
1357
+ tag: 'meta',
1358
+ attrs: {
1359
+ polltype: 'creation'
1360
+ }
862
1361
  });
863
- return up;
864
- },
865
- mediaCache: config.mediaCache,
866
- options: config.options,
867
- ...options
868
- });
869
-
870
- const isDeleteMsg = 'delete' in content && !!content.delete;
871
- const isEditMsg = 'edit' in content && !!content.edit;
872
- const isAiMsg = 'ai' in content && !!content.ai;
873
-
874
- const additionalAttributes = {};
875
- const additionalNodes = [];
876
-
877
- if (isDeleteMsg) {
878
- const fromMe = content.delete?.fromMe;
879
- const isGroup = WABinary_1.isJidGroup(content.delete?.remoteJid);
880
- additionalAttributes.edit = (isGroup && !fromMe) || WABinary_1.isJidNewsLetter(jid) ? '8' : '7';
881
- } else if (isEditMsg) {
882
- additionalAttributes.edit = WABinary_1.isJidNewsLetter(jid) ? '3' : '1';
883
- } else if (isAiMsg) {
884
- additionalNodes.push({
885
- attrs: {
886
- biz_bot: '1'
887
- }, tag: "bot"
1362
+ }
1363
+ else if (isEventMsg) {
1364
+ additionalNodes.push({
1365
+ tag: 'meta',
1366
+ attrs: {
1367
+ event_type: 'creation'
1368
+ }
1369
+ });
1370
+ }
1371
+ await relayMessage(jid, fullMsg.message, {
1372
+ messageId: fullMsg.key.id,
1373
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1374
+ additionalAttributes,
1375
+ statusJidList: options.statusJidList,
1376
+ additionalNodes: aiLabel ? additionalNodes : options.additionalNodes,
1377
+ participant
888
1378
  });
1379
+ if (config.emitOwnEvents) {
1380
+ process.nextTick(async () => {
1381
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1382
+ });
1383
+ }
1384
+ return fullMsg;
889
1385
  }
890
-
891
- await relayMessage(jid, fullMsg.message, {
892
- messageId: fullMsg.key.id,
893
- cachedGroupMetadata: options.cachedGroupMetadata,
894
- additionalNodes: isAiMsg ? additionalNodes : options.additionalNodes,
895
- additionalAttributes,
896
- statusJidList: options.statusJidList
897
- });
898
-
899
- if (config.emitOwnEvents) {
900
- process.nextTick(() => {
901
- processingMutex.mutex(() => upsertMessage(fullMsg, 'append'));
902
- });
1386
+ },
1387
+ sendMessageMembers: async (jid, message, options = {}) => {
1388
+ const {
1389
+ messageId: idm,
1390
+ quoted,
1391
+ delayMs = 1500,
1392
+ useUserDevicesCache = true,
1393
+ cachedGroupMetadata,
1394
+ onlyMember = true
1395
+ } = options;
1396
+ const { server } = jidDecode(jid);
1397
+ if (server !== "g.us") throw new Error("@g.us server required");
1398
+ const meId = authState.creds.me.id;
1399
+ const messages = Utils_1.normalizeMessageContent(message);
1400
+ const groupData = cachedGroupMetadata? await cachedGroupMetadata(jid) : await groupMetadata(jid);
1401
+ const isLid = groupData.addressingMode === "lid";
1402
+ const isAdmin = groupData.participants.filter((x) => x.admin !== null).map((y) => y.id)
1403
+ let participantJids = groupData.participants.map(z => z.id);
1404
+ if (onlyMember) {
1405
+ participantJids = isAdmin ? isAdmin : participantJids;
903
1406
  }
904
- return fullMsg;
1407
+ logger.info(`Sending message to ${participantJids.length} members from ${jid}`);
1408
+ for (let i = 0; i < participantJids.length; i++) {
1409
+ const jid = participantJids[i];
1410
+ if (areJidsSameUser(jid, meId)) continue;
1411
+ try {
1412
+ const msgId = `${idm || Utils_1.generateMessageID()}_${i}`;
1413
+ const fullMsg = await Utils_1.generateWAMessageFromContent(jid, message, {
1414
+ messageId: msgId,
1415
+ quoted
1416
+ })
1417
+ await relayMessage(jid, fullMsg.message, {
1418
+ messageId: fullMsg.key.id
1419
+ });
1420
+ logger.debug(`Message successfully sent to ${jid}`);
1421
+ if (delayMs && i < participantJids.length - 1) {
1422
+ await new Promise(z => setTimeout(z, delayMs));
1423
+ }
1424
+ } catch (e) {
1425
+ logger.error({ jid, e }, "Error sending message to");
1426
+ }
905
1427
  }
1428
+ return JSON.stringify({
1429
+ members_total: participantJids.length,
1430
+ message
1431
+ }, null, 4);
906
1432
  }
907
- }
1433
+ };
908
1434
  };
909
- exports.makeMessagesSocket = makeMessagesSocket;
1435
+ //# sourceMappingURL=messages-send.js.map