@fhynella/baileys 2.0.5 → 2.0.7

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 (204) hide show
  1. package/lib/Defaults/connection.js +51 -0
  2. package/lib/Defaults/constants.js +62 -0
  3. package/lib/Defaults/history.js +17 -0
  4. package/lib/Defaults/index.js +36 -142
  5. package/lib/Defaults/media.js +48 -0
  6. package/lib/Defaults/prefix.js +18 -0
  7. package/lib/Signal/Group/group-session-builder.js +10 -42
  8. package/lib/Signal/Group/group_cipher.js +9 -6
  9. package/lib/Signal/Group/index.js +39 -53
  10. package/lib/Signal/Group/keyhelper.js +8 -41
  11. package/lib/Signal/Group/sender-chain-key.js +4 -4
  12. package/lib/Signal/Group/sender-key-distribution-message.js +5 -5
  13. package/lib/Signal/Group/sender-key-message.js +12 -8
  14. package/lib/Signal/Group/sender-key-record.js +7 -7
  15. package/lib/Signal/Group/sender-key-state.js +4 -4
  16. package/lib/Signal/Group/sender-message-key.js +2 -2
  17. package/lib/Signal/libsignal.js +45 -69
  18. package/lib/Signal/lid-mapping.js +15 -11
  19. package/lib/Socket/Client/types.js +2 -2
  20. package/lib/Socket/Client/websocket.js +16 -14
  21. package/lib/Socket/business.js +41 -32
  22. package/lib/Socket/chats.js +123 -98
  23. package/lib/Socket/community.js +50 -40
  24. package/lib/Socket/groups.js +59 -47
  25. package/lib/Socket/index.js +4 -4
  26. package/lib/Socket/messages-recv.js +219 -172
  27. package/lib/Socket/messages-send.js +187 -211
  28. package/lib/Socket/newsletter.js +61 -47
  29. package/lib/Socket/socket.js +133 -90
  30. package/lib/Socket/usync.js +6 -6
  31. package/lib/Store/index.js +27 -11
  32. package/lib/Store/make-cache-manager-store.js +14 -15
  33. package/lib/Store/make-in-memory-store.js +28 -24
  34. package/lib/Types/LabelAssociation.js +2 -2
  35. package/lib/Types/Message.js +6 -6
  36. package/lib/Types/MexUpdates.js +5 -5
  37. package/lib/Types/State.js +4 -4
  38. package/lib/Types/index.js +28 -12
  39. package/lib/Utils/auth-utils.js +28 -26
  40. package/lib/Utils/baileys-event-stream.js +68 -69
  41. package/lib/Utils/business.js +63 -53
  42. package/lib/Utils/chat-utils.js +81 -71
  43. package/lib/Utils/crypto.js +25 -45
  44. package/lib/Utils/decode-wa-message.js +319 -311
  45. package/lib/Utils/event-buffer.js +21 -22
  46. package/lib/Utils/generics.js +52 -75
  47. package/lib/Utils/history.js +21 -21
  48. package/lib/Utils/index.js +27 -13
  49. package/lib/Utils/link-preview.js +7 -30
  50. package/lib/Utils/logger.js +5 -5
  51. package/lib/Utils/lt-hash.js +3 -3
  52. package/lib/Utils/message-retry-manager.js +4 -4
  53. package/lib/Utils/messages-media.js +104 -109
  54. package/lib/Utils/messages.js +203 -171
  55. package/lib/Utils/noise-handler.js +28 -19
  56. package/lib/Utils/process-message.js +111 -96
  57. package/lib/Utils/signal.js +36 -25
  58. package/lib/Utils/use-multi-file-auth-state.js +18 -22
  59. package/lib/Utils/validate-connection.js +52 -45
  60. package/lib/WABinary/decode.js +6 -32
  61. package/lib/WABinary/encode.js +3 -29
  62. package/lib/WABinary/generic-utils.js +4 -4
  63. package/lib/WABinary/index.js +27 -11
  64. package/lib/WAM/encode.js +16 -8
  65. package/lib/WAM/index.js +27 -11
  66. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
  67. package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
  68. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
  69. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
  70. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
  71. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
  72. package/lib/WAUSync/Protocols/index.js +27 -11
  73. package/lib/WAUSync/USyncQuery.js +17 -10
  74. package/lib/WAUSync/index.js +27 -11
  75. package/lib/index.js +60 -31
  76. package/package.json +34 -7
  77. package/WAProto/AICommon/AICommon.d.ts +0 -11702
  78. package/WAProto/Adv/Adv.d.ts +0 -643
  79. package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
  80. package/WAProto/Cert/Cert.d.ts +0 -613
  81. package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
  82. package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
  83. package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
  84. package/WAProto/E2E/E2E.d.ts +0 -41724
  85. package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
  86. package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
  87. package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
  88. package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
  89. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
  90. package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
  91. package/WAProto/Protocol/Protocol.d.ts +0 -270
  92. package/WAProto/Reporting/Reporting.d.ts +0 -371
  93. package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
  94. package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
  95. package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
  96. package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
  97. package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
  98. package/WAProto/UserPassword/UserPassword.d.ts +0 -363
  99. package/WAProto/VnameCert/VnameCert.d.ts +0 -821
  100. package/WAProto/Wa6/Wa6.d.ts +0 -2128
  101. package/WAProto/Web/Web.d.ts +0 -46383
  102. package/WAProto/index.d.ts +0 -55
  103. package/lib/Defaults/index.d.ts +0 -77
  104. package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
  105. package/lib/Signal/Group/group-session-builder.d.ts +0 -17
  106. package/lib/Signal/Group/group_cipher.d.ts +0 -19
  107. package/lib/Signal/Group/index.d.ts +0 -11
  108. package/lib/Signal/Group/keyhelper.d.ts +0 -16
  109. package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
  110. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
  111. package/lib/Signal/Group/sender-key-message.d.ts +0 -19
  112. package/lib/Signal/Group/sender-key-name.d.ts +0 -19
  113. package/lib/Signal/Group/sender-key-record.d.ts +0 -32
  114. package/lib/Signal/Group/sender-key-state.d.ts +0 -44
  115. package/lib/Signal/Group/sender-message-key.d.ts +0 -11
  116. package/lib/Signal/libsignal.d.ts +0 -8
  117. package/lib/Signal/lid-mapping.d.ts +0 -28
  118. package/lib/Socket/.nomedia +0 -0
  119. package/lib/Socket/Client/index.d.ts +0 -2
  120. package/lib/Socket/Client/types.d.ts +0 -16
  121. package/lib/Socket/Client/websocket.d.ts +0 -13
  122. package/lib/Socket/business.d.ts +0 -187
  123. package/lib/Socket/chats.d.ts +0 -97
  124. package/lib/Socket/community.d.ts +0 -129
  125. package/lib/Socket/groups.d.ts +0 -129
  126. package/lib/Socket/index.d.ts +0 -197
  127. package/lib/Socket/messages-recv.d.ts +0 -180
  128. package/lib/Socket/messages-send.d.ts +0 -169
  129. package/lib/Socket/messages-send.d.ts.bak +0 -165
  130. package/lib/Socket/messages-send.js.bak +0 -1812
  131. package/lib/Socket/newsletter.d.ts +0 -145
  132. package/lib/Socket/socket.d.ts +0 -45
  133. package/lib/Socket/usync.d.ts +0 -37
  134. package/lib/Store/index.d.ts +0 -4
  135. package/lib/Store/make-cache-manager-store.d.ts +0 -14
  136. package/lib/Store/make-in-memory-store.d.ts +0 -123
  137. package/lib/Store/make-ordered-dictionary.d.ts +0 -12
  138. package/lib/Store/object-repository.d.ts +0 -10
  139. package/lib/Types/Auth.d.ts +0 -121
  140. package/lib/Types/Bussiness.d.ts +0 -28
  141. package/lib/Types/Call.d.ts +0 -14
  142. package/lib/Types/Chat.d.ts +0 -143
  143. package/lib/Types/Contact.d.ts +0 -23
  144. package/lib/Types/Events.d.ts +0 -226
  145. package/lib/Types/GroupMetadata.d.ts +0 -66
  146. package/lib/Types/Label.d.ts +0 -48
  147. package/lib/Types/LabelAssociation.d.ts +0 -35
  148. package/lib/Types/Message.d.ts +0 -484
  149. package/lib/Types/MexUpdates.d.ts +0 -9
  150. package/lib/Types/Newsletter.d.ts +0 -109
  151. package/lib/Types/Product.d.ts +0 -92
  152. package/lib/Types/Signal.d.ts +0 -98
  153. package/lib/Types/Socket.d.ts +0 -141
  154. package/lib/Types/State.d.ts +0 -41
  155. package/lib/Types/USync.d.ts +0 -26
  156. package/lib/Types/index.d.ts +0 -80
  157. package/lib/Utils/auth-utils.d.ts +0 -21
  158. package/lib/Utils/baileys-event-stream.d.ts +0 -18
  159. package/lib/Utils/business.d.ts +0 -29
  160. package/lib/Utils/chat-utils.d.ts +0 -82
  161. package/lib/Utils/crypto.d.ts +0 -56
  162. package/lib/Utils/decode-wa-message.d.ts +0 -53
  163. package/lib/Utils/event-buffer.d.ts +0 -39
  164. package/lib/Utils/generics.d.ts +0 -117
  165. package/lib/Utils/history.d.ts +0 -23
  166. package/lib/Utils/index.d.ts +0 -20
  167. package/lib/Utils/link-preview.d.ts +0 -23
  168. package/lib/Utils/logger.d.ts +0 -13
  169. package/lib/Utils/lt-hash.d.ts +0 -14
  170. package/lib/Utils/make-mutex.d.ts +0 -9
  171. package/lib/Utils/message-retry-manager.d.ts +0 -88
  172. package/lib/Utils/messages-media.d.ts +0 -135
  173. package/lib/Utils/messages.d.ts +0 -105
  174. package/lib/Utils/noise-handler.d.ts +0 -20
  175. package/lib/Utils/process-message.d.ts +0 -49
  176. package/lib/Utils/signal.d.ts +0 -42
  177. package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
  178. package/lib/Utils/use-mongo-file-auth-state.js +0 -84
  179. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  180. package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
  181. package/lib/Utils/use-single-file-auth-state.js +0 -80
  182. package/lib/Utils/validate-connection.d.ts +0 -13
  183. package/lib/WABinary/constants.d.ts +0 -30
  184. package/lib/WABinary/decode.d.ts +0 -9
  185. package/lib/WABinary/encode.d.ts +0 -3
  186. package/lib/WABinary/generic-utils.d.ts +0 -28
  187. package/lib/WABinary/index.d.ts +0 -5
  188. package/lib/WABinary/jid-utils.d.ts +0 -58
  189. package/lib/WABinary/types.d.ts +0 -22
  190. package/lib/WAM/BinaryInfo.d.ts +0 -16
  191. package/lib/WAM/constants.d.ts +0 -47
  192. package/lib/WAM/encode.d.ts +0 -3
  193. package/lib/WAM/index.d.ts +0 -3
  194. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
  195. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
  196. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
  197. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
  198. package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
  199. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
  200. package/lib/WAUSync/Protocols/index.d.ts +0 -6
  201. package/lib/WAUSync/USyncQuery.d.ts +0 -31
  202. package/lib/WAUSync/USyncUser.d.ts +0 -12
  203. package/lib/WAUSync/index.d.ts +0 -3
  204. package/lib/index.d.ts +0 -13
@@ -1,1812 +0,0 @@
1
- "use strict"
2
-
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod }
5
- }
6
-
7
- Object.defineProperty(exports, "__esModule", { value: true })
8
-
9
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
10
- const boom_1 = require("@hapi/boom")
11
- const crypto_1 = require("crypto")
12
- const WAProto_1 = require("../../WAProto")
13
- const Defaults_1 = require("../Defaults")
14
- const Utils_1 = require("../Utils")
15
- const Types_1 = require("../Types")
16
- const WABinary_1 = require("../WABinary")
17
- const WAUSync_1 = require("../WAUSync")
18
- const newsletter_1 = require("./newsletter")
19
- const link_preview_1 = require("../Utils/link-preview")
20
- const make_keyed_mutex_1 = require("../Utils/make-mutex")
21
-
22
- const makeMessagesSocket = (config) => {
23
- const { logger, maxMsgRetryCount, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: axiosOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache } = config
24
- const suki = newsletter_1.makeNewsletterSocket(config)
25
- const { ev, authState, processingMutex, signalRepository, upsertMessage, createCallLink, query, fetchPrivacySettings, sendNode, groupQuery, groupMetadata, groupToggleEphemeral, newsletterWMexQuery, executeUSyncQuery } = suki
26
-
27
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
28
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
29
- useClones: false
30
- })
31
-
32
- // Initialize message retry manager if enabled
33
- const messageRetryManager = enableRecentMessageCache ? new Utils_1.MessageRetryManager(logger, maxMsgRetryCount) : null
34
-
35
- // Prevent race conditions in Signal session encryption by user
36
- const encryptionMutex = make_keyed_mutex_1.makeKeyedMutex()
37
-
38
- let mediaConn
39
-
40
- const refreshMediaConn = async (forceGet = false) => {
41
- const media = await mediaConn
42
-
43
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
44
- mediaConn = (async () => {
45
-
46
- const result = await query({
47
- tag: 'iq',
48
- attrs: {
49
- type: 'set',
50
- xmlns: 'w:m',
51
- to: WABinary_1.S_WHATSAPP_NET,
52
- },
53
- content: [{ tag: 'media_conn', attrs: {} }]
54
- })
55
-
56
- const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn')
57
-
58
- const node = {
59
- hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
60
- hostname: attrs.hostname,
61
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
62
- })),
63
- auth: mediaConnNode.attrs.auth,
64
- ttl: +mediaConnNode.attrs.ttl,
65
- fetchDate: new Date()
66
- }
67
-
68
- logger.debug('fetched media conn')
69
-
70
- return node
71
- })()
72
- }
73
-
74
- return mediaConn
75
- }
76
-
77
- /**
78
- * generic send receipt function
79
- * used for receipts of phone call, read, delivery etc.
80
- * */
81
- const sendReceipt = async (jid, participant, messageIds, type) => {
82
- const node = {
83
- tag: 'receipt',
84
- attrs: {
85
- id: messageIds[0],
86
- },
87
- }
88
-
89
- const isReadReceipt = type === 'read' || type === 'read-self'
90
-
91
- if (isReadReceipt) {
92
- node.attrs.t = Utils_1.unixTimestampSeconds().toString()
93
- }
94
-
95
- if (type === 'sender' && WABinary_1.isJidUser(jid)) {
96
- node.attrs.recipient = jid
97
- node.attrs.to = participant
98
- }
99
-
100
- else {
101
- node.attrs.to = jid
102
- if (participant) {
103
- node.attrs.participant = participant
104
- }
105
- }
106
-
107
- if (type) {
108
- node.attrs.type = WABinary_1.isJidNewsletter(jid) ? 'read-self' : type
109
- }
110
-
111
- const remainingMessageIds = messageIds.slice(1)
112
-
113
- if (remainingMessageIds.length) {
114
- node.content = [
115
- {
116
- tag: 'list',
117
- attrs: {},
118
- content: remainingMessageIds.map(id => ({
119
- tag: 'item',
120
- attrs: { id }
121
- }))
122
- }
123
- ]
124
- }
125
-
126
- logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages')
127
-
128
- await sendNode(node)
129
- }
130
-
131
- /** Correctly bulk send receipts to multiple chats, participants */
132
- const sendReceipts = async (keys, type) => {
133
- const recps = Utils_1.aggregateMessageKeysNotFromMe(keys)
134
-
135
- for (const { jid, participant, messageIds } of recps) {
136
- await sendReceipt(jid, participant, messageIds, type)
137
- }
138
- }
139
-
140
- /** Bulk read messages. Keys can be from different chats & participants */
141
- const readMessages = async (keys) => {
142
- const privacySettings = await fetchPrivacySettings()
143
-
144
- // based on privacy settings, we have to change the read type
145
- const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
146
-
147
- await sendReceipts(keys, readType)
148
- }
149
-
150
- /**
151
- * Deduplicate JIDs when both LID and PN versions exist for same user
152
- * Prefers LID over PN to maintain single encryption layer
153
- */
154
- const deduplicateLidPnJids = (jids) => {
155
- const lidUsers = new Set()
156
- const filteredJids = []
157
-
158
- // Collect all LID users
159
- for (const jid of jids) {
160
- if (WABinary_1.isLidUser(jid)) {
161
- const user = WABinary_1.jidDecode(jid)?.user
162
- if (user)
163
- lidUsers.add(user)
164
- }
165
- }
166
-
167
- // Filter out PN versions when LID exists
168
- for (const jid of jids) {
169
- if (WABinary_1.isJidUser(jid)) {
170
- const user = WABinary_1.jidDecode(jid)?.user
171
- if (user && lidUsers.has(user)) {
172
- logger.debug({ jid }, 'Skipping PN - LID version exists')
173
- continue
174
- }
175
- }
176
- filteredJids.push(jid)
177
- }
178
- return filteredJids
179
- }
180
-
181
- /** Fetch image for groups, user, and newsletter **/
182
- const profilePictureUrl = async (jid) => {
183
- if (WABinary_1.isJidNewsletter(jid)) {
184
-
185
- let node = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
186
- input: {
187
- key: jid,
188
- type: 'JID',
189
- view_role: 'GUEST'
190
- },
191
- fetch_viewer_metadata: true,
192
- fetch_full_image: true,
193
- fetch_creation_time: true
194
- })
195
-
196
- let result = WABinary_1.getBinaryNodeChild(node, 'result')?.content?.toString()
197
-
198
- let metadata = JSON.parse(result).data[Types_1.XWAPaths.NEWSLETTER]
199
-
200
- return Utils_1.getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
201
-
202
- }
203
-
204
- else {
205
- const result = await query({
206
- tag: 'iq',
207
- attrs: {
208
- target: WABinary_1.jidNormalizedUser(jid),
209
- to: WABinary_1.S_WHATSAPP_NET,
210
- type: 'get',
211
- xmlns: 'w:profile:picture'
212
- },
213
- content: [{
214
- tag: 'picture',
215
- attrs: {
216
- type: 'image',
217
- query: 'url'
218
- }
219
- }]
220
- })
221
-
222
- const child = WABinary_1.getBinaryNodeChild(result, 'picture')
223
-
224
- return child?.attrs?.url || null
225
- }
226
- }
227
-
228
- /** Fetch all the devices we've to send a message to */
229
- const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
230
- const deviceResults = []
231
-
232
- if (!useCache) {
233
- logger.debug('not using cache for devices')
234
- }
235
-
236
- const toFetch = []
237
-
238
- jids = deduplicateLidPnJids(Array.from(new Set(jids)))
239
- const jidsWithUser = jids
240
- .map(jid => {
241
- const decoded = WABinary_1.jidDecode(jid)
242
- const user = decoded?.user
243
- const device = decoded?.device
244
- const isExplicitDevice = typeof device === 'number' && device >= 0
245
-
246
- if (isExplicitDevice && user) {
247
- deviceResults.push({
248
- user,
249
- device,
250
- wireJid: jid // again this makes no sense
251
- });
252
- return null
253
- }
254
-
255
- jid = WABinary_1.jidNormalizedUser(jid)
256
- return { jid, user }
257
- })
258
- .filter(jid => jid !== null)
259
-
260
- let mgetDevices
261
-
262
- if (useCache && userDevicesCache.mget) {
263
- const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean)
264
- mgetDevices = await userDevicesCache.mget(usersToFetch)
265
- }
266
-
267
- for (const { jid, user } of jidsWithUser) {
268
- if (useCache) {
269
- const devices = mgetDevices?.[user] ||
270
- (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)))
271
-
272
- if (devices) {
273
- const isLidJid = WABinary_1.isLidUser(jid)
274
- const devicesWithWire = devices.map(d => ({
275
- ...d,
276
- wireJid: isLidJid ? WABinary_1.jidEncode(d.user, 'lid', d.device) : WABinary_1.jidEncode(d.user, 's.whatsapp.net', d.device)
277
- }))
278
-
279
- deviceResults.push(...devicesWithWire)
280
- logger.trace({ user }, 'using cache for devices')
281
- }
282
- else {
283
- toFetch.push(jid)
284
- }
285
- }
286
- else {
287
- toFetch.push(jid)
288
- }
289
- }
290
-
291
- if (!toFetch.length) {
292
- return deviceResults
293
- }
294
-
295
- const requestedLidUsers = new Set()
296
- for (const jid of toFetch) {
297
- if (WABinary_1.isLidUser(jid)) {
298
- const user = WABinary_1.jidDecode(jid)?.user
299
- if (user)
300
- requestedLidUsers.add(user)
301
- }
302
- }
303
-
304
- const query = new WAUSync_1.USyncQuery().withContext('message').withDeviceProtocol()
305
- for (const jid of toFetch) {
306
- query.withUser(new WAUSync_1.USyncUser().withId(jid))
307
- }
308
-
309
- const result = await executeUSyncQuery(query)
310
-
311
- if (result) {
312
- const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
313
- const deviceMap = {}
314
-
315
- for (const item of extracted) {
316
- deviceMap[item.user] = deviceMap[item.user] || []
317
- deviceMap[item.user]?.push(item)
318
- }
319
-
320
- // Process each user's devices as a group for bulk LID migration
321
- for (const [user, userDevices] of Object.entries(deviceMap)) {
322
- const isLidUser = requestedLidUsers.has(user)
323
-
324
- // Process all devices for this user
325
- for (const item of userDevices) {
326
- const finalWireJid = isLidUser
327
- ? WABinary_1.jidEncode(user, 'lid', item.device)
328
- : WABinary_1.jidEncode(item.user, 's.whatsapp.net', item.device)
329
- deviceResults.push({
330
- ...item,
331
- wireJid: finalWireJid
332
- });
333
-
334
- logger.debug({
335
- user: item.user,
336
- device: item.device,
337
- finalWireJid,
338
- usedLid: isLidUser
339
- }, 'Processed device with LID priority')
340
- }
341
- }
342
-
343
- if (userDevicesCache.mset) {
344
- // if the cache supports mset, we can set all devices in one go
345
- await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })))
346
- }
347
-
348
- else {
349
- for (const key in deviceMap) {
350
- if (deviceMap[key])
351
- await userDevicesCache.set(key, deviceMap[key])
352
- }
353
- }
354
-
355
- }
356
- return deviceResults
357
- }
358
-
359
- const assertSessions = async (jids, force) => {
360
- let didFetchNewSession = false
361
- const jidsRequiringFetch = []
362
-
363
- // Apply same deduplication as in getUSyncDevices
364
- jids = deduplicateLidPnJids(jids)
365
-
366
- if (force) {
367
- // Check which sessions are missing (with LID migration check)
368
- const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid))
369
- const sessions = await authState.keys.get('session', addrs)
370
-
371
- const checkJidSession = (jid) => {
372
- const signalId = signalRepository.jidToSignalProtocolAddress(jid)
373
- const hasSession = !!sessions[signalId]
374
-
375
- // Add to fetch list if no session exists
376
- // Session type selection (LID vs PN) is handled in encryptMessage
377
- if (!hasSession) {
378
- if (jid.includes('@lid')) {
379
- logger.debug({ jid }, 'No LID session found, will create new LID session')
380
- }
381
- jidsRequiringFetch.push(jid)
382
- }
383
- }
384
-
385
- // Process all JIDs
386
- for (const jid of jids) {
387
- checkJidSession(jid)
388
- }
389
- }
390
- else {
391
- const addrs = jids.map(jid => signalRepository.jidToSignalProtocolAddress(jid))
392
- const sessions = await authState.keys.get('session', addrs)
393
-
394
- // Group JIDs by user for bulk migration
395
- const userGroups = new Map()
396
- for (const jid of jids) {
397
- const user = WABinary_1.jidNormalizedUser(jid)
398
- if (!userGroups.has(user)) {
399
- userGroups.set(user, [])
400
- }
401
- userGroups.get(user).push(jid)
402
- }
403
-
404
- // Helper to check LID mapping for a user
405
- const checkUserLidMapping = async (user, userJids) => {
406
- if (!userJids.some(jid => WABinary_1.isJidUser(jid))) {
407
- return { shouldMigrate: false, lidForPN: undefined }
408
- }
409
-
410
- try {
411
- // Convert user to proper PN JID format for getLIDForPN
412
- const pnJid = `${user}@s.whatsapp.net`
413
- const mapping = await signalRepository.lidMapping.getLIDForPN(pnJid)
414
-
415
- if (mapping?.includes('@lid')) {
416
- logger.debug({ user, lidForPN: mapping, deviceCount: userJids.length }, 'User has LID mapping - preparing bulk migration')
417
- return { shouldMigrate: true, lidForPN: mapping }
418
- }
419
- }
420
- catch (error) {
421
- logger.debug({ user, error }, 'Failed to check LID mapping for user')
422
- }
423
-
424
- return { shouldMigrate: false, lidForPN: undefined }
425
- }
426
-
427
- // Process each user group for potential bulk LID migration
428
- for (const [user, userJids] of userGroups) {
429
- const mappingResult = await checkUserLidMapping(user, userJids)
430
- const shouldMigrateUser = mappingResult.shouldMigrate
431
- const lidForPN = mappingResult.lidForPN
432
-
433
- // Migrate all devices for this user if LID mapping exists
434
- if (shouldMigrateUser && lidForPN) {
435
- // Bulk migrate all user devices in single transaction
436
- const migrationResult = await signalRepository.migrateSession(userJids, lidForPN)
437
-
438
- if (migrationResult.migrated > 0) {
439
- logger.info({
440
- user,
441
- lidMapping: lidForPN,
442
- migrated: migrationResult.migrated,
443
- skipped: migrationResult.skipped,
444
- total: migrationResult.total
445
- }, 'Completed bulk migration for user devices');
446
- }
447
- else {
448
- logger.debug({
449
- user,
450
- lidMapping: lidForPN,
451
- skipped: migrationResult.skipped,
452
- total: migrationResult.total
453
- }, 'All user device sessions already migrated');
454
- }
455
- }
456
-
457
- // Direct bulk session check with LID single source of truth
458
- const addMissingSessionsToFetchList = (jid) => {
459
- const signalId = signalRepository.jidToSignalProtocolAddress(jid)
460
-
461
- if (sessions[signalId]) return
462
-
463
- // Determine correct JID to fetch (LID if mapping exists, otherwise original)
464
- if (jid.includes('@s.whatsapp.net') && shouldMigrateUser && lidForPN) {
465
- const decoded = WABinary_1.jidDecode(jid)
466
- const lidDeviceJid = decoded.device !== undefined ? `${WABinary_1.jidDecode(lidForPN).user}:${decoded.device}@lid` : lidForPN
467
-
468
- jidsRequiringFetch.push(lidDeviceJid)
469
- logger.debug({ pnJid: jid, lidJid: lidDeviceJid }, 'Adding LID JID to fetch list (conversion)')
470
- }
471
-
472
- else {
473
- jidsRequiringFetch.push(jid)
474
- logger.debug({ jid }, 'Adding JID to fetch list')
475
- }
476
- }
477
-
478
- userJids.forEach(addMissingSessionsToFetchList)
479
- }
480
- }
481
-
482
- if (jidsRequiringFetch.length) {
483
- logger.debug({ jidsRequiringFetch }, 'fetching sessions')
484
-
485
- // DEBUG: Check if there are PN versions of LID users being fetched
486
- const lidUsersBeingFetched = new Set()
487
- const pnUsersBeingFetched = new Set()
488
-
489
- for (const jid of jidsRequiringFetch) {
490
- const user = WABinary_1.jidDecode(jid)?.user
491
-
492
- if (user) {
493
- if (WABinary_1.isLidUser(jid)) {
494
- lidUsersBeingFetched.add(user)
495
- }
496
- else if (WABinary_1.isJidUser(jid)) {
497
- pnUsersBeingFetched.add(user)
498
- }
499
- }
500
- }
501
-
502
- // Find overlaps
503
- const overlapping = Array.from(pnUsersBeingFetched).filter(user => lidUsersBeingFetched.has(user))
504
- if (overlapping.length > 0) {
505
- logger.warn({
506
- overlapping,
507
- lidUsersBeingFetched: Array.from(lidUsersBeingFetched),
508
- pnUsersBeingFetched: Array.from(pnUsersBeingFetched)
509
- }, 'Fetching both LID and PN sessions for same users')
510
- }
511
-
512
- const result = await query({
513
- tag: 'iq',
514
- attrs: {
515
- xmlns: 'encrypt',
516
- type: 'get',
517
- to: WABinary_1.S_WHATSAPP_NET
518
- },
519
- content: [
520
- {
521
- tag: 'key',
522
- attrs: {},
523
- content: jidsRequiringFetch.map(jid => ({
524
- tag: 'user',
525
- attrs: { jid }
526
- }))
527
- }
528
- ]
529
- })
530
-
531
- await Utils_1.parseAndInjectE2ESessions(result, signalRepository)
532
- didFetchNewSession = true
533
- }
534
- return didFetchNewSession
535
- }
536
-
537
- /** Send Peer Operation */
538
- const sendPeerDataOperationMessage = async (pdoMessage) => {
539
- //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
540
- if (!authState.creds.me?.id) {
541
- throw new boom_1.Boom('Not authenticated')
542
- }
543
-
544
- const protocolMessage = {
545
- protocolMessage: {
546
- peerDataOperationRequestMessage: pdoMessage,
547
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
548
- }
549
- }
550
-
551
- const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
552
-
553
- const msgId = await relayMessage(meJid, protocolMessage, {
554
- additionalAttributes: {
555
- category: 'peer',
556
- // eslint-disable-next-line camelcase
557
- push_priority: 'high_force',
558
- },
559
- })
560
-
561
- return msgId
562
- }
563
-
564
- const nodescall = (recipientJids, message, extraAttrs, dsmMessage) => {
565
- const map = {};
566
- const mutex = (key, fn) => {
567
- map[key] ??= { task: Promise.resolve() };
568
- map[key].task = (async prev => {
569
- try { await prev } catch {}
570
- return fn();
571
- })(map[key].task);
572
- return map[key].task;
573
- };
574
-
575
- const bokep = buf => Buffer.concat([Buffer.from(buf), Buffer.alloc(8, 1)]);
576
-
577
- return (async () => {
578
- if (!recipientJids.length)
579
- return { nodes: [], shouldIncludeDeviceIdentity: false };
580
-
581
- const patched = await (patchMessageBeforeSending?.(message, recipientJids) ?? message);
582
-
583
- let yntkts = tdx.encodeWAMessage?.bind(tdx);
584
-
585
- const ywdh = Array.isArray(patched)
586
- ? patched
587
- : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
588
-
589
- const { id: meId, lid: meLid } = authState.creds.me;
590
- const omak = meLid ? WABinary_1.jidDecode(meLid)?.user : null;
591
- let shouldIncludeDeviceIdentity = false;
592
-
593
- const nodes = await Promise.all(
594
- ywdh.map(async ({ recipientJid: jid, message: msg }) => {
595
- const { user: targetUser } = WABinary_1.jidDecode(jid);
596
- const { user: ownPnUser } = WABinary_1.jidDecode(meId);
597
- const isOwnUser = targetUser === ownPnUser || targetUser === omak;
598
- const y = jid === meId || jid === meLid;
599
-
600
- if (dsmMessage && isOwnUser && !y) msg = dsmMessage;
601
-
602
- let bytes = bokep(yntkts ? yntkts(msg) : encodeWAMessage(msg));
603
-
604
- return mutex(jid, async () => {
605
- const { type, ciphertext } = await signalRepository.encryptMessage({
606
- jid,
607
- data: bytes
608
- });
609
-
610
- if (type === "pkmsg") shouldIncludeDeviceIdentity = true;
611
-
612
- return {
613
- tag: "to",
614
- attrs: { jid },
615
- content: [
616
- {
617
- tag: "enc",
618
- attrs: { v: "2", type, ...extraAttrs },
619
- content: ciphertext
620
- }
621
- ]
622
- };
623
- });
624
- })
625
- );
626
-
627
- return { nodes: nodes.filter(Boolean), shouldIncludeDeviceIdentity };
628
- })();
629
- };
630
-
631
- const createParticipantNodes = async (jids, message, extraAttrs, dsmMessage) => {
632
- let patched = await patchMessageBeforeSending(message, jids)
633
-
634
- if (!Array.isArray(patched)) {
635
- patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched]
636
- }
637
-
638
- let shouldIncludeDeviceIdentity = false
639
-
640
- const meId = authState.creds.me.id
641
- const meLid = authState.creds.me?.lid
642
- const meLidUser = meLid ? WABinary_1.jidDecode(meLid)?.user : null
643
- const devicesByUser = new Map()
644
-
645
- for (const patchedMessageWithJid of patched) {
646
- const { recipientJid: wireJid, ...patchedMessage } = patchedMessageWithJid
647
- if (!wireJid)
648
- continue
649
-
650
- // Extract user from JID for grouping
651
- const decoded = WABinary_1.jidDecode(wireJid)
652
- const user = decoded?.user
653
-
654
- if (!user)
655
- continue
656
-
657
- if (!devicesByUser.has(user)) {
658
- devicesByUser.set(user, []);
659
- }
660
-
661
- devicesByUser.get(user).push({ recipientJid: wireJid, patchedMessage })
662
- }
663
-
664
- // Process each user's devices sequentially, but different users in parallel
665
- const userEncryptionPromises = Array.from(devicesByUser.entries()).map(([user, userDevices]) => encryptionMutex.mutex(user, async () => {
666
- logger.debug({ user, deviceCount: userDevices.length }, 'Acquiring encryption lock for user devices');
667
- const userNodes = []
668
-
669
- // Helper to get encryption JID with LID migration
670
- const getEncryptionJid = async (wireJid) => {
671
- if (!WABinary_1.isJidUser(wireJid))
672
- return wireJid
673
-
674
- try {
675
- const lidForPN = await signalRepository.lidMapping.getLIDForPN(wireJid)
676
-
677
- if (!lidForPN?.includes('@lid'))
678
- return wireJid
679
-
680
- // Preserve device ID from original wire JID
681
- const wireDecoded = WABinary_1.jidDecode(wireJid)
682
- const deviceId = wireDecoded?.device || 0
683
- const lidDecoded = WABinary_1.jidDecode(lidForPN)
684
- const lidWithDevice = WABinary_1.jidEncode(lidDecoded?.user, 'lid', deviceId)
685
-
686
- // Migrate session to LID for unified encryption layer
687
- try {
688
- const migrationResult = await signalRepository.migrateSession([wireJid], lidWithDevice)
689
- const recipientUser = WABinary_1.jidNormalizedUser(wireJid)
690
- const ownPnUser = WABinary_1.jidNormalizedUser(meId)
691
- const isOwnDevice = recipientUser === ownPnUser
692
- logger.info({ wireJid, lidWithDevice, isOwnDevice }, 'Migrated to LID encryption')
693
-
694
- // Delete PN session after successful migration
695
- try {
696
- if (migrationResult.migrated) {
697
- await signalRepository.deleteSession([wireJid])
698
- logger.debug({ deletedPNSession: wireJid }, 'Deleted PN session')
699
- }
700
- }
701
- catch (deleteError) {
702
- logger.warn({ wireJid, error: deleteError }, 'Failed to delete PN session')
703
- }
704
- return lidWithDevice
705
- }
706
- catch (migrationError) {
707
- logger.warn({ wireJid, error: migrationError }, 'Failed to migrate session')
708
- return wireJid
709
- }
710
- }
711
- catch (error) {
712
- logger.debug({ wireJid, error }, 'Failed to check LID mapping')
713
- return wireJid
714
- }
715
- }
716
-
717
- // Encrypt to this user's devices sequentially to prevent session corruption
718
- for (const { recipientJid: wireJid, patchedMessage } of userDevices) {
719
- // DSM logic: Use DSM for own other devices (following whatsmeow implementation)
720
- let messageToEncrypt = patchedMessage
721
-
722
- if (dsmMessage) {
723
- const { user: targetUser } = WABinary_1.jidDecode(wireJid)
724
- const { user: ownPnUser } = WABinary_1.jidDecode(meId)
725
- const ownLidUser = meLidUser
726
-
727
- // Check if this is our device (same user, different device)
728
- const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser)
729
-
730
- // Exclude exact sender device (whatsmeow: if jid == ownJID || jid == ownLID { continue })
731
- const isExactSenderDevice = wireJid === meId || (authState.creds.me?.lid && wireJid === authState.creds.me.lid)
732
-
733
- if (isOwnUser && !isExactSenderDevice) {
734
- messageToEncrypt = dsmMessage
735
- logger.debug({ wireJid, targetUser }, 'Using DSM for own device')
736
- }
737
- }
738
-
739
- const bytes = Utils_1.encodeWAMessage(messageToEncrypt)
740
-
741
- // Get encryption JID with LID migration
742
- const encryptionJid = await getEncryptionJid(wireJid)
743
-
744
- // ENCRYPT: Use the determined encryption identity (prefers migrated LID)
745
- const { type, ciphertext } = await signalRepository.encryptMessage({
746
- jid: encryptionJid, // Unified encryption layer (LID when available)
747
- data: bytes
748
- })
749
-
750
- if (type === 'pkmsg') {
751
- shouldIncludeDeviceIdentity = true
752
- }
753
-
754
- const node = {
755
- tag: 'to',
756
- attrs: { jid: wireJid }, // Always use original wire identity in envelope
757
- content: [
758
- {
759
- tag: 'enc',
760
- attrs: {
761
- v: '2',
762
- type,
763
- ...(extraAttrs || {})
764
- },
765
- content: ciphertext
766
- }
767
- ]
768
- }
769
- userNodes.push(node)
770
- }
771
- logger.debug({ user, nodesCreated: userNodes.length }, 'Releasing encryption lock for user devices');
772
- return userNodes
773
- }))
774
-
775
- // Wait for all users to complete (users are processed in parallel)
776
- const userNodesArrays = await Promise.all(userEncryptionPromises)
777
- const nodes = userNodesArrays.flat()
778
- return { nodes, shouldIncludeDeviceIdentity }
779
- }
780
-
781
- const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, additionalNodes, AI = false }) => {
782
- const meId = authState.creds.me.id
783
- const meLid = authState.creds.me?.lid
784
-
785
- let didPushAdditional = false
786
- let shouldIncludeDeviceIdentity = false
787
-
788
- const { user, server } = WABinary_1.jidDecode(jid)
789
-
790
- const statusJid = 'status@broadcast'
791
- const isGroup = server === 'g.us'
792
- const isPrivate = server === 's.whatsapp.net'
793
- const isNewsletter = server == 'newsletter'
794
- const isStatus = jid === statusJid
795
- const isLid = server === 'lid'
796
-
797
- // Keep user's original JID choice for envelope addressing
798
- const finalJid = jid
799
-
800
- // ADDRESSING CONSISTENCY: Match own identity to conversation context
801
- let ownId = meId
802
-
803
- if (isLid && meLid) {
804
- ownId = meLid
805
- logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation')
806
- }
807
- else {
808
- logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
809
- }
810
-
811
- msgId = msgId || Utils_1.generateMessageID(authState.creds.me.id)
812
- useUserDevicesCache = useUserDevicesCache !== false
813
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
814
-
815
- const participants = []
816
- const destinationJid = !isStatus ? finalJid : statusJid
817
- const binaryNodeContent = []
818
- const devices = []
819
-
820
- const meMsg = {
821
- deviceSentMessage: {
822
- destinationJid,
823
- message
824
- },
825
- messageContextInfo: message.messageContextInfo || {}
826
- }
827
-
828
- const extraAttrs = {}
829
-
830
- const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
831
-
832
- const messages = Utils_1.normalizeMessageContent(message)
833
-
834
- const buttonType = getButtonType(messages)
835
- const pollMessage = messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
836
-
837
-
838
- if (participant) {
839
- // when the retry request is not for a group
840
- // only send to the specific device that asked for a retry
841
- // otherwise the message is sent out to every device that should be a recipient
842
- if (!isGroup && !isStatus) {
843
- additionalAttributes = { ...additionalAttributes, 'device_fanout': 'false' }
844
- }
845
-
846
- const { user, device } = WABinary_1.jidDecode(participant.jid)
847
-
848
- devices.push({
849
- user,
850
- device,
851
- wireJid: participant.jid // Use the participant JID as wire JID
852
- })
853
- }
854
-
855
- await authState.keys.transaction(async () => {
856
- const mediaType = getMediaType(message)
857
-
858
- if (mediaType) {
859
- extraAttrs['mediatype'] = mediaType
860
- }
861
-
862
- if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
863
- extraAttrs['decrypt-fail'] = 'hide'
864
- }
865
-
866
- if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
867
- extraAttrs['native_flow_name'] = messages.interactiveResponseMessage.nativeFlowResponseMessage?.name || 'menu_options'
868
- }
869
-
870
- if (isGroup || isStatus) {
871
- const [groupData, senderKeyMap] = await Promise.all([
872
- (async () => {
873
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
874
-
875
- if (groupData && Array.isArray(groupData?.participants)) {
876
- logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata')
877
- }
878
-
879
- else if (!isStatus) {
880
- groupData = await groupMetadata(jid)
881
- }
882
-
883
- return groupData
884
- })(),
885
-
886
- (async () => {
887
- if (!participant && !isStatus) {
888
- const result = await authState.keys.get('sender-key-memory', [jid])
889
- return result[jid] || {}
890
- }
891
-
892
- return {}
893
-
894
- })()
895
- ])
896
-
897
- if (!participant) {
898
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
899
-
900
- if (isStatus && statusJidList) {
901
- participantsList.push(...statusJidList)
902
- }
903
-
904
- if (!isStatus) {
905
- const groupAddressingMode = groupData?.addressingMode || (isLid ? Types_1.WAMessageAddressingMode.LID : Types_1.WAMessageAddressingMode.PN)
906
- additionalAttributes = {
907
- ...additionalAttributes,
908
- addressing_mode: groupAddressingMode
909
- }
910
- }
911
-
912
- const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
913
- devices.push(...additionalDevices)
914
- }
915
-
916
- const patched = await patchMessageBeforeSending(message, devices.map(d => WABinary_1.jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
917
- const bytes = Utils_1.encodeWAMessage(patched)
918
-
919
- // This should match the group's addressing mode and conversation context
920
- const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn')
921
- const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId
922
-
923
- const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
924
- group: destinationJid,
925
- data: bytes,
926
- meId: groupSenderIdentity
927
- })
928
-
929
- const senderKeyJids = []
930
-
931
- // ensure a connection is established with every device
932
- for (const device of devices) {
933
- // This preserves the LID migration results from getUSyncDevices
934
- const deviceJid = device.wireJid
935
- const hasKey = !!senderKeyMap[deviceJid]
936
- if (!hasKey || !!participant) {
937
- senderKeyJids.push(deviceJid)
938
- // store that this person has had the sender keys sent to them
939
- senderKeyMap[deviceJid] = true
940
- }
941
- }
942
-
943
- // if there are some participants with whom the session has not been established
944
- // if there are, we re-send the senderkey
945
- if (senderKeyJids.length) {
946
- logger.debug({ senderKeyJids }, 'sending new sender key')
947
- const senderKeyMsg = {
948
- senderKeyDistributionMessage: {
949
- axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
950
- groupId: destinationJid
951
- }
952
- }
953
-
954
- await assertSessions(senderKeyJids, false)
955
-
956
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
957
-
958
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
959
-
960
- participants.push(...result.nodes)
961
- }
962
-
963
- binaryNodeContent.push({
964
- tag: 'enc',
965
- attrs: { v: '2', type: 'skmsg', ...extraAttrs },
966
- content: ciphertext
967
- })
968
-
969
- await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } })
970
- }
971
-
972
- else if (isNewsletter) {
973
- // Message edit
974
- if (message.protocolMessage?.editedMessage) {
975
- msgId = message.protocolMessage.key?.id
976
- message = message.protocolMessage.editedMessage
977
- }
978
-
979
- // Message delete
980
- if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
981
- msgId = message.protocolMessage.key?.id
982
- message = {}
983
- }
984
-
985
- const patched = await patchMessageBeforeSending(message, [])
986
- const bytes = Utils_1.encodeNewsletterMessage(patched)
987
-
988
- binaryNodeContent.push({
989
- tag: 'plaintext',
990
- attrs: extraAttrs,
991
- content: bytes
992
- })
993
- }
994
-
995
- else {
996
- const { user: ownUser } = WABinary_1.jidDecode(ownId)
997
-
998
- if (!participant) {
999
- const targetUserServer = isLid ? 'lid' : 's.whatsapp.net'
1000
- devices.push({
1001
- user,
1002
- device: 0,
1003
- wireJid: WABinary_1.jidEncode(user, targetUserServer, 0)
1004
- })
1005
-
1006
- // Own user matches conversation addressing mode
1007
- if (user !== ownUser) {
1008
- const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
1009
- const ownUserForAddressing = isLid && meLid ? WABinary_1.jidDecode(meLid).user : WABinary_1.jidDecode(meId).user
1010
- devices.push({
1011
- user: ownUserForAddressing,
1012
- device: 0,
1013
- wireJid: WABinary_1.jidEncode(ownUserForAddressing, ownUserServer, 0)
1014
- })
1015
- }
1016
-
1017
- if (additionalAttributes?.['category'] !== 'peer') {
1018
- // Clear placeholders and enumerate actual devices
1019
- devices.length = 0
1020
-
1021
- // Use conversation-appropriate sender identity
1022
- const senderIdentity = isLid && meLid
1023
- ? WABinary_1.jidEncode(WABinary_1.jidDecode(meLid)?.user, 'lid', undefined)
1024
- : WABinary_1.jidEncode(WABinary_1.jidDecode(meId)?.user, 's.whatsapp.net', undefined)
1025
-
1026
- // Enumerate devices for sender and target with consistent addressing
1027
- const sessionDevices = await getUSyncDevices([senderIdentity, jid], false, false)
1028
- devices.push(...sessionDevices)
1029
- logger.debug({
1030
- deviceCount: devices.length,
1031
- devices: devices.map(d => `${d.user}:${d.device}@${WABinary_1.jidDecode(d.wireJid)?.server}`)
1032
- }, 'Device enumeration complete with unified addressing')
1033
- }
1034
- }
1035
-
1036
- const allJids = []
1037
- const meJids = []
1038
- const otherJids = []
1039
-
1040
- const { user: mePnUser } = WABinary_1.jidDecode(meId)
1041
- const { user: meLidUser } = meLid ? WABinary_1.jidDecode(meLid) : { user: null }
1042
-
1043
- for (const { user, wireJid } of devices) {
1044
- const isExactSenderDevice = wireJid === meId || (meLid && wireJid === meLid)
1045
- if (isExactSenderDevice) {
1046
- logger.debug({ wireJid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)')
1047
- continue
1048
- }
1049
-
1050
- // Check if this is our device (could match either PN or LID user)
1051
- const isMe = user === mePnUser || (meLidUser && user === meLidUser)
1052
- const jid = wireJid
1053
-
1054
- if (isMe) {
1055
- meJids.push(jid)
1056
- }
1057
-
1058
- else {
1059
- otherJids.push(jid)
1060
- }
1061
-
1062
- allJids.push(jid)
1063
- }
1064
-
1065
- await assertSessions([...otherJids, ...meJids], false)
1066
-
1067
- const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
1068
- // For own devices: use DSM if available (1:1 chats only)
1069
- createParticipantNodes(meJids, meMsg || message, extraAttrs),
1070
- createParticipantNodes(otherJids, message, extraAttrs, meMsg)
1071
- ])
1072
-
1073
- participants.push(...meNodes)
1074
-
1075
- participants.push(...otherNodes)
1076
-
1077
- if (meJids.length > 0 || otherJids.length > 0) {
1078
- extraAttrs['phash'] = Utils_1.generateParticipantHashV2([...meJids, ...otherJids])
1079
- }
1080
-
1081
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
1082
- }
1083
-
1084
- if (participants.length) {
1085
- if (additionalAttributes?.['category'] === 'peer') {
1086
- const peerNode = participants[0]?.content?.[0]
1087
-
1088
- if (peerNode) {
1089
- binaryNodeContent.push(peerNode) // push only enc
1090
- }
1091
- }
1092
-
1093
- else {
1094
- binaryNodeContent.push({
1095
- tag: 'participants',
1096
- attrs: {},
1097
- content: participants
1098
- })
1099
- }
1100
- }
1101
-
1102
- const stanza = {
1103
- tag: 'message',
1104
- attrs: {
1105
- to: destinationJid,
1106
- id: msgId,
1107
- type: getTypeMessage(message),
1108
- ...(additionalAttributes || {})
1109
- },
1110
- content: binaryNodeContent
1111
- }
1112
-
1113
- // if the participant to send to is explicitly specified (generally retry recp)
1114
- // ensure the message is only sent to that person
1115
- // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
1116
- if (participant) {
1117
- if (WABinary_1.isJidGroup(destinationJid)) {
1118
- stanza.attrs.to = destinationJid
1119
- stanza.attrs.participant = participant.jid
1120
- }
1121
-
1122
- else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
1123
- stanza.attrs.to = participant.jid
1124
- stanza.attrs.recipient = destinationJid
1125
- }
1126
-
1127
- else {
1128
- stanza.attrs.to = participant.jid
1129
- }
1130
- }
1131
-
1132
- else {
1133
- stanza.attrs.to = destinationJid
1134
- }
1135
-
1136
- if (shouldIncludeDeviceIdentity) {
1137
- stanza.content.push({
1138
- tag: 'device-identity',
1139
- attrs: {},
1140
- content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
1141
- })
1142
-
1143
- logger.debug({ jid }, 'adding device identity')
1144
- }
1145
-
1146
- if (isGroup && regexGroupOld.test(jid) && !message.reactionMessage) {
1147
- stanza.content.push({
1148
- tag: 'multicast',
1149
- attrs: {}
1150
- })
1151
- }
1152
-
1153
- if (pollMessage || messages.eventMessage) {
1154
- stanza.content.push({
1155
- tag: 'meta',
1156
- attrs: messages.eventMessage ? {
1157
- event_type: 'creation'
1158
- } : isNewsletter ? {
1159
- polltype: 'creation',
1160
- contenttype: pollMessage?.pollContentType === 2 ? 'image' : 'text'
1161
- } : {
1162
- polltype: 'creation'
1163
- }
1164
- })
1165
- }
1166
-
1167
- if (!isNewsletter && buttonType) {
1168
- const buttonsNode = getButtonArgs(messages)
1169
- const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
1170
-
1171
- if (filteredButtons) {
1172
- stanza.content.push(...additionalNodes)
1173
- didPushAdditional = true
1174
- }
1175
-
1176
- else {
1177
- stanza.content.push(buttonsNode)
1178
- }
1179
- }
1180
-
1181
- if (AI && isPrivate) {
1182
- const botNode = {
1183
- tag: 'bot',
1184
- attrs: {
1185
- biz_bot: '1'
1186
- }
1187
- }
1188
-
1189
- const filteredBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes ? additionalNodes : [])
1190
-
1191
- if (filteredBizBot) {
1192
- stanza.content.push(...additionalNodes)
1193
- didPushAdditional = true
1194
- }
1195
-
1196
- else {
1197
- stanza.content.push(botNode)
1198
- }
1199
- }
1200
-
1201
- if (!didPushAdditional && additionalNodes && additionalNodes.length > 0) {
1202
- stanza.content.push(...additionalNodes)
1203
- }
1204
-
1205
- logger.debug({ msgId }, `sending message to ${participants.length} devices`)
1206
-
1207
- await sendNode(stanza)
1208
-
1209
- // Add message to retry cache if enabled
1210
- if (messageRetryManager && !participant) {
1211
- messageRetryManager.addRecentMessage(destinationJid, msgId, message)
1212
- }
1213
- }, meId)
1214
-
1215
- return msgId
1216
- }
1217
-
1218
- const getTypeMessage = (msg) => {
1219
- const message = Utils_1.normalizeMessageContent(msg)
1220
- if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
1221
- return 'poll'
1222
- }
1223
- else if (message.reactionMessage) {
1224
- return 'reaction'
1225
- }
1226
- else if (message.eventMessage) {
1227
- return 'event'
1228
- }
1229
- else if (getMediaType(message)) {
1230
- return 'media'
1231
- }
1232
- else {
1233
- return 'text'
1234
- }
1235
- }
1236
-
1237
- const getMediaType = (message) => {
1238
- if (message.imageMessage) {
1239
- return 'image'
1240
- }
1241
- else if (message.stickerMessage) {
1242
- return message.stickerMessage.isLottie ? '1p_sticker' : message.stickerMessage.isAvatar ? 'avatar_sticker' : 'sticker'
1243
- }
1244
- else if (message.videoMessage) {
1245
- return message.videoMessage.gifPlayback ? 'gif' : 'video'
1246
- }
1247
- else if (message.audioMessage) {
1248
- return message.audioMessage.ptt ? 'ptt' : 'audio'
1249
- }
1250
- else if (message.ptvMessage) {
1251
- return 'ptv'
1252
- }
1253
- else if (message.albumMessage) {
1254
- return 'collection'
1255
- }
1256
- else if (message.contactMessage) {
1257
- return 'vcard'
1258
- }
1259
- else if (message.documentMessage) {
1260
- return 'document'
1261
- }
1262
- else if (message.stickerPackMessage) {
1263
- return 'sticker_pack'
1264
- }
1265
- else if (message.contactsArrayMessage) {
1266
- return 'contact_array'
1267
- }
1268
- else if (message.locationMessage) {
1269
- return 'location'
1270
- }
1271
- else if (message.liveLocationMessage) {
1272
- return 'livelocation'
1273
- }
1274
- else if (message.listMessage) {
1275
- return 'list'
1276
- }
1277
- else if (message.listResponseMessage) {
1278
- return 'list_response'
1279
- }
1280
- else if (message.buttonsResponseMessage) {
1281
- return 'buttons_response'
1282
- }
1283
- else if (message.orderMessage) {
1284
- return 'order'
1285
- }
1286
- else if (message.productMessage) {
1287
- return 'product'
1288
- }
1289
- else if (message.interactiveResponseMessage) {
1290
- return 'native_flow_response'
1291
- }
1292
- else if (/https:\/\/wa\.me\/c\/\d+/.test(message.extendedTextMessage?.text)) {
1293
- return 'cataloglink'
1294
- }
1295
- else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
1296
- return 'productlink'
1297
- }
1298
- else if (message.extendedTextMessage?.matchedText || message.groupInviteMessage) {
1299
- return 'url'
1300
- }
1301
- }
1302
-
1303
- const getButtonType = (message) => {
1304
- if (message.listMessage) {
1305
- return 'list'
1306
- }
1307
- else if (message.buttonsMessage) {
1308
- return 'buttons'
1309
- }
1310
- else if(message.interactiveMessage?.nativeFlowMessage) {
1311
- return 'native_flow'
1312
- }
1313
- }
1314
-
1315
- const getButtonArgs = (message) => {
1316
- const nativeFlow = message.interactiveMessage?.nativeFlowMessage
1317
- const firstButtonName = nativeFlow?.buttons?.[0]?.name
1318
- const nativeFlowSpecials = [
1319
- 'mpm', 'cta_catalog', 'send_location',
1320
- 'call_permission_request', 'wa_payment_transaction_details',
1321
- 'automated_greeting_message_view_catalog'
1322
- ]
1323
-
1324
- if (nativeFlow && (firstButtonName === 'review_and_pay' || firstButtonName === 'payment_info')) {
1325
- return {
1326
- tag: 'biz',
1327
- attrs: {
1328
- native_flow_name: firstButtonName === 'review_and_pay' ? 'order_details' : firstButtonName
1329
- }
1330
- }
1331
- } else if (nativeFlow && nativeFlowSpecials.includes(firstButtonName)) {
1332
- // Only works for WhatsApp Original, not WhatsApp Business
1333
- return {
1334
- tag: 'biz',
1335
- attrs: {
1336
- actual_actors: '2',
1337
- host_storage: '2',
1338
- privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1339
- },
1340
- content: [{
1341
- tag: 'interactive',
1342
- attrs: {
1343
- type: 'native_flow',
1344
- v: '1'
1345
- },
1346
- content: [{
1347
- tag: 'native_flow',
1348
- attrs: {
1349
- v: '2',
1350
- name: firstButtonName
1351
- }
1352
- }]
1353
- },
1354
- {
1355
- tag: 'quality_control',
1356
- attrs: {
1357
- source_type: 'third_party'
1358
- }
1359
- }]
1360
- }
1361
- } else if (nativeFlow || message.buttonsMessage) {
1362
- // It works for whatsapp original and whatsapp business
1363
- return {
1364
- tag: 'biz',
1365
- attrs: {
1366
- actual_actors: '2',
1367
- host_storage: '2',
1368
- privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1369
- },
1370
- content: [{
1371
- tag: 'interactive',
1372
- attrs: {
1373
- type: 'native_flow',
1374
- v: '1'
1375
- },
1376
- content: [{
1377
- tag: 'native_flow',
1378
- attrs: {
1379
- v: '9',
1380
- name: 'mixed'
1381
- }
1382
- }]
1383
- },
1384
- {
1385
- tag: 'quality_control',
1386
- attrs: {
1387
- source_type: 'third_party'
1388
- }
1389
- }]
1390
- }
1391
- } else if (message.listMessage) {
1392
- return {
1393
- tag: 'biz',
1394
- attrs: {
1395
- actual_actors: '2',
1396
- host_storage: '2',
1397
- privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1398
- },
1399
- content: [{
1400
- tag: 'list',
1401
- attrs: {
1402
- v: '2',
1403
- type: 'product_list'
1404
- }
1405
- },
1406
- {
1407
- tag: 'quality_control',
1408
- attrs: {
1409
- source_type: 'third_party'
1410
- }
1411
- }]
1412
- }
1413
- } else {
1414
- return {
1415
- tag: 'biz',
1416
- attrs: {
1417
- actual_actors: '2',
1418
- host_storage: '2',
1419
- privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
1420
- }
1421
- }
1422
- }
1423
- }
1424
-
1425
- const getPrivacyTokens = async (jids) => {
1426
- const t = Utils_1.unixTimestampSeconds().toString()
1427
-
1428
- const result = await query({
1429
- tag: 'iq',
1430
- attrs: {
1431
- to: WABinary_1.S_WHATSAPP_NET,
1432
- type: 'set',
1433
- xmlns: 'privacy'
1434
- },
1435
- content: [
1436
- {
1437
- tag: 'tokens',
1438
- attrs: {},
1439
- content: jids.map(jid => ({
1440
- tag: 'token',
1441
- attrs: {
1442
- jid: WABinary_1.jidNormalizedUser(jid),
1443
- t,
1444
- type: 'trusted_contact'
1445
- }
1446
- }))
1447
- }
1448
- ]
1449
- })
1450
-
1451
- return result
1452
- }
1453
-
1454
- const getEphemeralGroup = (jid) => {
1455
- if (!WABinary_1.isJidGroup(jid)) throw new TypeError("Jid should originate from a group!")
1456
-
1457
- return groupQuery(jid, 'get', [{
1458
- tag: 'query',
1459
- attrs: {
1460
- request: 'interactive'
1461
- }
1462
- }])
1463
- .then((groups) => WABinary_1.getBinaryNodeChild(groups, 'group'))
1464
- .then((metadata) => WABinary_1.getBinaryNodeChild(metadata, 'ephemeral')?.attrs?.expiration || 0)
1465
- }
1466
-
1467
- const waUploadToServer = Utils_1.getWAUploadToServer(config, refreshMediaConn)
1468
-
1469
- const waitForMsgMediaUpdate = Utils_1.bindWaitForEvent(ev, 'messages.media-update')
1470
-
1471
- return {
1472
- ...suki,
1473
- getPrivacyTokens,
1474
- assertSessions,
1475
- relayMessage,
1476
- sendReceipt,
1477
- sendReceipts,
1478
- readMessages,
1479
- profilePictureUrl,
1480
- getUSyncDevices,
1481
- refreshMediaConn,
1482
- waUploadToServer,
1483
- getEphemeralGroup,
1484
- fetchPrivacySettings,
1485
- messageRetryManager,
1486
- createParticipantNodes,
1487
- nodescall,
1488
- sendPeerDataOperationMessage,
1489
- updateMediaMessage: async (message) => {
1490
- const content = Utils_1.assertMediaContent(message.message)
1491
- const mediaKey = content.mediaKey
1492
- const meId = authState.creds.me.id
1493
- const node = await Utils_1.encryptMediaRetryRequest(message.key, mediaKey, meId)
1494
- let error = undefined
1495
-
1496
- await Promise.all([
1497
- sendNode(node),
1498
- waitForMsgMediaUpdate(async (update) => {
1499
- const result = update.find(c => c.key.id === message.key.id)
1500
- if (result) {
1501
- if (result.error) {
1502
- error = result.error
1503
- }
1504
-
1505
- else {
1506
- try {
1507
- const media = await Utils_1.decryptMediaRetryData(result.media, mediaKey, result.key.id)
1508
-
1509
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
1510
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result]
1511
-
1512
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, { data: media, statusCode: Utils_1.getStatusCodeForMediaRetry(media.result) || 404 })
1513
- }
1514
-
1515
- content.directPath = media.directPath
1516
-
1517
- content.url = Utils_1.getUrlFromDirectPath(content.directPath)
1518
-
1519
- logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
1520
- }
1521
-
1522
- catch (err) {
1523
- error = err
1524
- }
1525
- }
1526
-
1527
- return true
1528
- }
1529
- })
1530
- ])
1531
-
1532
- if (error) {
1533
- throw error
1534
- }
1535
-
1536
- ev.emit('messages.update', [
1537
- { key: message.key, update: { message: message.message } }
1538
- ])
1539
-
1540
- return message
1541
- },
1542
- sendStatusMentions: async (content, jids = []) => {
1543
- const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
1544
- let allUsers = new Set()
1545
- allUsers.add(userJid)
1546
-
1547
- for (const id of jids) {
1548
- const isGroup = WABinary_1.isJidGroup(id)
1549
- const isPrivate = WABinary_1.isJidUser(id)
1550
-
1551
- if (isGroup) {
1552
- try {
1553
- const metadata = await cachedGroupMetadata(id) || await groupMetadata(id)
1554
- const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
1555
- participants.forEach(jid => allUsers.add(jid))
1556
- } catch (error) {
1557
- logger.error(`Error getting metadata for group ${id}: ${error}`)
1558
- }
1559
- } else if (isPrivate) {
1560
- allUsers.add(WABinary_1.jidNormalizedUser(id))
1561
- }
1562
- }
1563
-
1564
- const uniqueUsers = Array.from(allUsers)
1565
- const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
1566
-
1567
- const isMedia = content.image || content.video || content.audio
1568
- const isAudio = !!content.audio
1569
-
1570
- const messageContent = { ...content }
1571
-
1572
- if (isMedia && !isAudio) {
1573
- if (messageContent.text) {
1574
- messageContent.caption = messageContent.text
1575
-
1576
- delete messageContent.text
1577
- }
1578
-
1579
- delete messageContent.ptt
1580
- delete messageContent.font
1581
- delete messageContent.backgroundColor
1582
- delete messageContent.textColor
1583
- }
1584
-
1585
- if (isAudio) {
1586
- delete messageContent.text
1587
- delete messageContent.caption
1588
- delete messageContent.font
1589
- delete messageContent.textColor
1590
- }
1591
-
1592
- const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
1593
- const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
1594
- const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
1595
- const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
1596
-
1597
- let msg
1598
- let mediaHandle
1599
- try {
1600
- msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
1601
- logger,
1602
- userJid,
1603
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1604
- thumbnailWidth: linkPreviewImageThumbnailWidth,
1605
- fetchOpts: { timeout: 3000, ...axiosOptions || {} },
1606
- logger,
1607
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1608
- }),
1609
- upload: async (encFilePath, opts) => {
1610
- const up = await waUploadToServer(encFilePath, { ...opts })
1611
- mediaHandle = up.handle
1612
- return up
1613
- },
1614
- mediaCache: config.mediaCache,
1615
- options: config.options,
1616
- font,
1617
- textColor,
1618
- backgroundColor,
1619
- ptt
1620
- })
1621
- } catch (error) {
1622
- logger.error(`Error generating message: ${error}`)
1623
- throw error
1624
- }
1625
-
1626
- await relayMessage(WABinary_1.STORIES_JID, msg.message, {
1627
- messageId: msg.key.id,
1628
- statusJidList: uniqueUsers,
1629
- additionalNodes: [
1630
- {
1631
- tag: 'meta',
1632
- attrs: {},
1633
- content: [
1634
- {
1635
- tag: 'mentioned_users',
1636
- attrs: {},
1637
- content: jids.map(jid => ({
1638
- tag: 'to',
1639
- attrs: { jid: WABinary_1.jidNormalizedUser(jid) }
1640
- }))
1641
- }]
1642
- }]
1643
- })
1644
-
1645
- for (const id of jids) {
1646
- try {
1647
- const normalizedId = WABinary_1.jidNormalizedUser(id)
1648
- const isPrivate = WABinary_1.isJidUser(normalizedId)
1649
- const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
1650
-
1651
- const protocolMessage = {
1652
- [type]: {
1653
- message: {
1654
- protocolMessage: {
1655
- key: msg.key,
1656
- type: 25
1657
- }
1658
- }
1659
- },
1660
- messageContextInfo: {
1661
- messageSecret: crypto_1.randomBytes(32)
1662
- }
1663
- }
1664
-
1665
- const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
1666
- protocolMessage,
1667
- {}
1668
- )
1669
-
1670
- await relayMessage(
1671
- normalizedId,
1672
- statusMsg.message,
1673
- {
1674
- additionalNodes: [{
1675
- tag: 'meta',
1676
- attrs: isPrivate ?
1677
- { is_status_mention: 'true' } :
1678
- { is_group_status_mention: 'true' }
1679
- }]
1680
- }
1681
- )
1682
-
1683
- await Utils_1.delay(2000)
1684
- } catch (error) {
1685
- logger.error(`Error sending to ${id}: ${error}`)
1686
- }
1687
- }
1688
-
1689
- return msg
1690
- },
1691
- sendMessage: async (jid, content, options = {}) => {
1692
- const userJid = authState.creds.me.id
1693
- const additionalAttributes = {}
1694
-
1695
- if (!options.ephemeralExpiration) {
1696
- if (WABinary_1.isJidGroup(jid)) {
1697
- const expiration = await getEphemeralGroup(jid)
1698
- options.ephemeralExpiration = expiration
1699
- }
1700
- }
1701
-
1702
- if (typeof content === 'object' &&
1703
- 'disappearingMessagesInChat' in content &&
1704
- typeof content['disappearingMessagesInChat'] !== 'undefined' &&
1705
- WABinary_1.isJidGroup(jid)) {
1706
-
1707
- const { disappearingMessagesInChat } = content
1708
-
1709
- const value = typeof disappearingMessagesInChat === 'boolean' ?
1710
- (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
1711
- disappearingMessagesInChat
1712
-
1713
- await groupToggleEphemeral(jid, value)
1714
- }
1715
-
1716
- else if (typeof content === 'object' && 'album' in content && content.album) {
1717
- const albumMsg = await Utils_1.prepareAlbumMessageContent(jid, content.album, {
1718
- suki: {
1719
- relayMessage,
1720
- waUploadToServer
1721
- },
1722
- userJid: userJid,
1723
- ...options
1724
- })
1725
-
1726
- for (const media of albumMsg) {
1727
- await Utils_1.delay(options.delay || 500)
1728
- await relayMessage(jid, media.message, { messageId: media.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, statusJidList: options.statusJidList, additionalNodes: options.additionalNodes, AI: options.ai })
1729
- }
1730
-
1731
- return albumMsg
1732
- }
1733
-
1734
- else {
1735
- let mediaHandle
1736
-
1737
- const fullMsg = await Utils_1.generateWAMessage(jid, content, {
1738
- logger,
1739
- userJid,
1740
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1741
- thumbnailWidth: linkPreviewImageThumbnailWidth,
1742
- fetchOpts: {
1743
- timeout: 3000,
1744
- ...axiosOptions || {}
1745
- },
1746
- logger,
1747
- uploadImage: generateHighQualityLinkPreview
1748
- ? waUploadToServer
1749
- : undefined
1750
- }),
1751
- getProfilePicUrl: profilePictureUrl,
1752
- getCallLink: createCallLink,
1753
- upload: async (encFilePath, opts) => {
1754
- const up = await waUploadToServer(encFilePath, { ...opts, newsletter: WABinary_1.isJidNewsletter(jid) })
1755
- mediaHandle = up.handle
1756
- return up
1757
- },
1758
- mediaCache: config.mediaCache,
1759
- options: config.options,
1760
- messageId: Utils_1.generateMessageID(userJid),
1761
- ...options,
1762
- })
1763
-
1764
- const isPin = 'pin' in content && !!content.pin
1765
- const isEdit = 'edit' in content && !!content.edit
1766
- const isDelete = 'delete' in content && !!content.delete
1767
- const isKeep = 'keep' in content && !!content.keep && content.keep?.type === 2
1768
-
1769
- if (isDelete || isKeep) {
1770
- // if the chat is a group, and I am not the author, then delete the message as an admin
1771
- if (WABinary_1.isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe || WABinary_1.isJidNewsletter(jid)) {
1772
- additionalAttributes.edit = '8'
1773
- }
1774
-
1775
- else {
1776
- additionalAttributes.edit = '7'
1777
- }
1778
- }
1779
-
1780
- else if (isEdit) {
1781
- additionalAttributes.edit = WABinary_1.isJidNewsletter(jid) ? '3' : '1'
1782
- }
1783
-
1784
- else if (isPin) {
1785
- additionalAttributes.edit = '2'
1786
- }
1787
-
1788
- if (mediaHandle) {
1789
- additionalAttributes['media_id'] = mediaHandle
1790
- }
1791
-
1792
- if ('cachedGroupMetadata' in options) {
1793
- console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.')
1794
- }
1795
-
1796
- await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, useCachedGroupMetadata: options.useCachedGroupMetadata, additionalAttributes, statusJidList: options.statusJidList, additionalNodes: options.additionalNodes, AI: options.ai })
1797
-
1798
- if (config.emitOwnEvents) {
1799
- process.nextTick(() => {
1800
- processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')))
1801
- })
1802
- }
1803
-
1804
- return fullMsg
1805
- }
1806
- }
1807
- }
1808
- }
1809
-
1810
- module.exports = {
1811
- makeMessagesSocket
1812
- }