@sixcore/baileys 1.0.0 → 1.0.2

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