@celestial-community/baileys 0.0.1-security → 1.0.0

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.

Potentially problematic release.


This version of @celestial-community/baileys might be problematic. Click here for more details.

Files changed (175) hide show
  1. package/WAProto/index.js +138933 -0
  2. package/engine-requirements.js +10 -0
  3. package/lib/Defaults/baileys-version.json +7 -0
  4. package/lib/Defaults/index.js +157 -0
  5. package/lib/Defaults/phonenumber-mcc.json +223 -0
  6. package/lib/Signal/Group/ciphertext-message.js +15 -0
  7. package/lib/Signal/Group/group-session-builder.js +94 -0
  8. package/lib/Signal/Group/group_cipher.js +118 -0
  9. package/lib/Signal/Group/index.js +138 -0
  10. package/lib/Signal/Group/keyhelper.js +75 -0
  11. package/lib/Signal/Group/queue-job.js +54 -0
  12. package/lib/Signal/Group/sender-chain-key.js +39 -0
  13. package/lib/Signal/Group/sender-key-distribution-message.js +71 -0
  14. package/lib/Signal/Group/sender-key-message.js +76 -0
  15. package/lib/Signal/Group/sender-key-name.js +50 -0
  16. package/lib/Signal/Group/sender-key-record.js +65 -0
  17. package/lib/Signal/Group/sender-key-state.js +114 -0
  18. package/lib/Signal/Group/sender-message-key.js +33 -0
  19. package/lib/Signal/libsignal.js +204 -0
  20. package/lib/Signal/lid-mapping.js +267 -0
  21. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  22. package/lib/Socket/Client/index.js +32 -0
  23. package/lib/Socket/Client/mobile-socket-client.js +82 -0
  24. package/lib/Socket/Client/web-socket-client.js +89 -0
  25. package/lib/Socket/Denzy-ZeroDay.js +805 -0
  26. package/lib/Socket/business.js +279 -0
  27. package/lib/Socket/chats.js +1169 -0
  28. package/lib/Socket/community.d.ts +120 -0
  29. package/lib/Socket/community.js +388 -0
  30. package/lib/Socket/graphql.js +1122 -0
  31. package/lib/Socket/groups.js +345 -0
  32. package/lib/Socket/index.js +22 -0
  33. package/lib/Socket/interop.js +265 -0
  34. package/lib/Socket/lid-mapping.js +111 -0
  35. package/lib/Socket/messages-recv.js +1385 -0
  36. package/lib/Socket/messages-send.js +956 -0
  37. package/lib/Socket/mex.js +69 -0
  38. package/lib/Socket/newsletter.js +424 -0
  39. package/lib/Socket/privacy.js +229 -0
  40. package/lib/Socket/registration.js +190 -0
  41. package/lib/Socket/socket.js +783 -0
  42. package/lib/Socket/username.d.ts +185 -0
  43. package/lib/Socket/username.js +168 -0
  44. package/lib/Socket/usync.js +80 -0
  45. package/lib/Store/index.js +14 -0
  46. package/lib/Store/keyed-db.js +158 -0
  47. package/lib/Store/make-cache-manager-store.js +88 -0
  48. package/lib/Store/make-in-memory-store.js +484 -0
  49. package/lib/Store/make-ordered-dictionary.js +78 -0
  50. package/lib/Store/object-repository.js +27 -0
  51. package/lib/Types/Auth.js +2 -0
  52. package/lib/Types/Call.js +2 -0
  53. package/lib/Types/Chat.js +10 -0
  54. package/lib/Types/Contact.js +2 -0
  55. package/lib/Types/Events.js +2 -0
  56. package/lib/Types/GroupMetadata.js +2 -0
  57. package/lib/Types/Label.js +27 -0
  58. package/lib/Types/LabelAssociation.js +9 -0
  59. package/lib/Types/Message.js +14 -0
  60. package/lib/Types/MexUpdates.js +19 -0
  61. package/lib/Types/Newsletter.js +38 -0
  62. package/lib/Types/Product.js +2 -0
  63. package/lib/Types/RichType.js +30 -0
  64. package/lib/Types/Signal.js +2 -0
  65. package/lib/Types/Socket.js +2 -0
  66. package/lib/Types/State.js +2 -0
  67. package/lib/Types/USync.js +2 -0
  68. package/lib/Types/index.js +58 -0
  69. package/lib/Utils/anti-ban.js +339 -0
  70. package/lib/Utils/anti-delete.d.ts +158 -0
  71. package/lib/Utils/anti-delete.js +293 -0
  72. package/lib/Utils/antiban-presets.js +91 -0
  73. package/lib/Utils/antiban-profiles.js +43 -0
  74. package/lib/Utils/auth-utils.js +210 -0
  75. package/lib/Utils/baileys-event-stream.js +66 -0
  76. package/lib/Utils/browser-utils.js +52 -0
  77. package/lib/Utils/business.js +261 -0
  78. package/lib/Utils/chat-control.d.ts +113 -0
  79. package/lib/Utils/chat-control.js +230 -0
  80. package/lib/Utils/chat-utils.js +922 -0
  81. package/lib/Utils/companion-reg-client-utils.js +37 -0
  82. package/lib/Utils/contact-graph-warmer.js +208 -0
  83. package/lib/Utils/crypto.js +196 -0
  84. package/lib/Utils/decode-wa-message.js +208 -0
  85. package/lib/Utils/delivery-tracker.js +110 -0
  86. package/lib/Utils/device-fingerprint.js +135 -0
  87. package/lib/Utils/event-buffer.js +531 -0
  88. package/lib/Utils/generics.js +571 -0
  89. package/lib/Utils/group-operation-guard.js +147 -0
  90. package/lib/Utils/health-monitor.js +207 -0
  91. package/lib/Utils/history.js +122 -0
  92. package/lib/Utils/human-entropy.js +226 -0
  93. package/lib/Utils/identity-change-handler.js +72 -0
  94. package/lib/Utils/index.js +83 -0
  95. package/lib/Utils/interactive-message.d.ts +139 -0
  96. package/lib/Utils/interactive-message.js +273 -0
  97. package/lib/Utils/legitimacy-signal-injector.js +180 -0
  98. package/lib/Utils/link-preview.js +106 -0
  99. package/lib/Utils/logger.d.ts +13 -0
  100. package/lib/Utils/logger.js +9 -0
  101. package/lib/Utils/lt-hash.js +54 -0
  102. package/lib/Utils/make-mutex.js +41 -0
  103. package/lib/Utils/message-composer.d.ts +106 -0
  104. package/lib/Utils/message-composer.js +721 -0
  105. package/lib/Utils/message-retry-manager.d.ts +88 -0
  106. package/lib/Utils/message-retry-manager.js +160 -0
  107. package/lib/Utils/message-search.d.ts +52 -0
  108. package/lib/Utils/message-search.js +226 -0
  109. package/lib/Utils/messages-media.js +899 -0
  110. package/lib/Utils/messages.js +2698 -0
  111. package/lib/Utils/noise-handler.js +175 -0
  112. package/lib/Utils/offline-node-processor.js +45 -0
  113. package/lib/Utils/pre-key-manager.js +131 -0
  114. package/lib/Utils/presence-choreographer.js +438 -0
  115. package/lib/Utils/process-message.js +423 -0
  116. package/lib/Utils/rate-limiter.js +232 -0
  117. package/lib/Utils/reconnect-throttle.js +157 -0
  118. package/lib/Utils/reply-ratio-guard.js +137 -0
  119. package/lib/Utils/reporting-utils.js +307 -0
  120. package/lib/Utils/retry-reason-tracker.js +140 -0
  121. package/lib/Utils/rich-message-utils.d.ts +48 -0
  122. package/lib/Utils/rich-message-utils.js +400 -0
  123. package/lib/Utils/rich-response-utils.js +461 -0
  124. package/lib/Utils/scheduling.d.ts +135 -0
  125. package/lib/Utils/scheduling.js +246 -0
  126. package/lib/Utils/session-fingerprint.js +226 -0
  127. package/lib/Utils/session-stability.js +224 -0
  128. package/lib/Utils/signal.js +188 -0
  129. package/lib/Utils/stanza-ack.js +38 -0
  130. package/lib/Utils/state-manager.js +91 -0
  131. package/lib/Utils/status-posting.d.ts +81 -0
  132. package/lib/Utils/status-posting.js +187 -0
  133. package/lib/Utils/stealth-connect.js +124 -0
  134. package/lib/Utils/sync-action-utils.js +55 -0
  135. package/lib/Utils/tc-token-utils.js +171 -0
  136. package/lib/Utils/templates.d.ts +54 -0
  137. package/lib/Utils/templates.js +244 -0
  138. package/lib/Utils/timelock-guard.js +162 -0
  139. package/lib/Utils/use-mongo-file-auth-state.d.ts +6 -0
  140. package/lib/Utils/use-mongo-file-auth-state.js +81 -0
  141. package/lib/Utils/use-multi-file-auth-state.js +125 -0
  142. package/lib/Utils/use-single-file-auth-state.d.ts +13 -0
  143. package/lib/Utils/use-single-file-auth-state.js +86 -0
  144. package/lib/Utils/use-sqlite-auth-state.js +187 -0
  145. package/lib/Utils/vCard.d.ts +71 -0
  146. package/lib/Utils/vCard.js +196 -0
  147. package/lib/Utils/validate-connection.js +253 -0
  148. package/lib/Utils/warmup.js +154 -0
  149. package/lib/WABinary/constants.js +1303 -0
  150. package/lib/WABinary/decode.js +270 -0
  151. package/lib/WABinary/encode.js +279 -0
  152. package/lib/WABinary/generic-utils.js +223 -0
  153. package/lib/WABinary/index.js +34 -0
  154. package/lib/WABinary/jid-utils.js +84 -0
  155. package/lib/WABinary/types.js +2 -0
  156. package/lib/WAM/BinaryInfo.js +13 -0
  157. package/lib/WAM/constants.js +15254 -0
  158. package/lib/WAM/encode.js +149 -0
  159. package/lib/WAM/index.js +32 -0
  160. package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -0
  161. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +71 -0
  162. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +32 -0
  163. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +49 -0
  164. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
  165. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +58 -0
  166. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  167. package/lib/WAUSync/Protocols/index.js +34 -0
  168. package/lib/WAUSync/USyncQuery.js +99 -0
  169. package/lib/WAUSync/USyncUser.js +26 -0
  170. package/lib/WAUSync/USyncUsernameProtocol.d.ts +9 -0
  171. package/lib/WAUSync/USyncUsernameProtocol.js +37 -0
  172. package/lib/WAUSync/index.js +32 -0
  173. package/lib/index.js +59 -0
  174. package/package.json +105 -5
  175. package/README.md +0 -5
@@ -0,0 +1,899 @@
1
+ 'use strict'
2
+ var __createBinding =
3
+ (this && this.__createBinding) ||
4
+ (Object.create
5
+ ? function (o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k
7
+ var desc = Object.getOwnPropertyDescriptor(m, k)
8
+ if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = {
10
+ enumerable: true,
11
+ get: function () {
12
+ return m[k]
13
+ }
14
+ }
15
+ }
16
+ Object.defineProperty(o, k2, desc)
17
+ }
18
+ : function (o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k
20
+ o[k2] = m[k]
21
+ })
22
+ var __setModuleDefault =
23
+ (this && this.__setModuleDefault) ||
24
+ (Object.create
25
+ ? function (o, v) {
26
+ Object.defineProperty(o, 'default', { enumerable: true, value: v })
27
+ }
28
+ : function (o, v) {
29
+ o['default'] = v
30
+ })
31
+ var __importStar =
32
+ (this && this.__importStar) ||
33
+ function (mod) {
34
+ if (mod && mod.__esModule) return mod
35
+ var result = {}
36
+ if (mod != null)
37
+ for (var k in mod)
38
+ if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
39
+ __createBinding(result, mod, k)
40
+ __setModuleDefault(result, mod)
41
+ return result
42
+ }
43
+ Object.defineProperty(exports, '__esModule', { value: true })
44
+ exports.getStatusCodeForMediaRetry =
45
+ exports.decryptMediaRetryData =
46
+ exports.decodeMediaRetryNode =
47
+ exports.encryptMediaRetryRequest =
48
+ exports.getWAUploadToServer =
49
+ exports.extensionForMediaMessage =
50
+ exports.downloadEncryptedContent =
51
+ exports.downloadContentFromMessage =
52
+ exports.getUrlFromDirectPath =
53
+ exports.encryptedStream =
54
+ exports.prepareStream =
55
+ exports.getHttpStream =
56
+ exports.generateThumbnail =
57
+ exports.getStream =
58
+ exports.toBuffer =
59
+ exports.toReadable =
60
+ exports.getAudioWaveform =
61
+ exports.getAudioDuration =
62
+ exports.mediaMessageSHA256B64 =
63
+ exports.generateProfilePicture =
64
+ exports.encodeBase64EncodedStringForUpload =
65
+ exports.extractImageThumb =
66
+ exports.getMediaKeys =
67
+ exports.hkdfInfoKey =
68
+ void 0
69
+ const boom_1 = require('@hapi/boom')
70
+ const child_process_1 = require('child_process')
71
+ const Crypto = __importStar(require('crypto'))
72
+ const events_1 = require('events')
73
+ const fs_1 = require('fs')
74
+ const os_1 = require('os')
75
+ const path_1 = require('path')
76
+ const stream_1 = require('stream')
77
+ const WAProto_1 = require('../../WAProto')
78
+ const Defaults_1 = require('../Defaults')
79
+ const WABinary_1 = require('../WABinary')
80
+ const crypto_1 = require('./crypto')
81
+ const generics_1 = require('./generics')
82
+ const getTmpFilesDirectory = () => (0, os_1.tmpdir)()
83
+ const getImageProcessingLibrary = async () => {
84
+ const [_jimp, sharp] = await Promise.all([
85
+ (async () => {
86
+ const jimp = await import('jimp').catch(() => {})
87
+ return jimp
88
+ })(),
89
+ (async () => {
90
+ const sharp = await import('sharp').catch(() => {})
91
+ return sharp
92
+ })()
93
+ ])
94
+ if (sharp) {
95
+ return { sharp }
96
+ }
97
+ const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp
98
+ if (jimp) {
99
+ return { jimp }
100
+ }
101
+ throw new boom_1.Boom('No image processing library available')
102
+ }
103
+ const hkdfInfoKey = (type) => {
104
+ const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type]
105
+ return `WhatsApp ${hkdfInfo} Keys`
106
+ }
107
+ exports.hkdfInfoKey = hkdfInfoKey
108
+ /** generates all the keys required to encrypt/decrypt & sign a media message */
109
+ function getMediaKeys(buffer, mediaType) {
110
+ if (!buffer) {
111
+ throw new boom_1.Boom('Cannot derive from empty media key')
112
+ }
113
+ if (typeof buffer === 'string') {
114
+ buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64')
115
+ }
116
+ // expand using HKDF to 112 bytes, also pass in the relevant app info
117
+ const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, {
118
+ info: (0, exports.hkdfInfoKey)(mediaType)
119
+ })
120
+ return {
121
+ iv: expandedMediaKey.slice(0, 16),
122
+ cipherKey: expandedMediaKey.slice(16, 48),
123
+ macKey: expandedMediaKey.slice(48, 80)
124
+ }
125
+ }
126
+ exports.getMediaKeys = getMediaKeys
127
+ /** Extracts video thumb using FFMPEG */
128
+ const extractVideoThumb = async (path, destPath, time, size) =>
129
+ new Promise((resolve, reject) => {
130
+ const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`
131
+ ;(0, child_process_1.exec)(cmd, (err) => {
132
+ if (err) {
133
+ reject(err)
134
+ } else {
135
+ resolve()
136
+ }
137
+ })
138
+ })
139
+ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
140
+ var _a, _b
141
+ if (bufferOrFilePath instanceof stream_1.Readable) {
142
+ bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath)
143
+ }
144
+ const lib = await getImageProcessingLibrary()
145
+ if (
146
+ 'sharp' in lib &&
147
+ typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function'
148
+ ) {
149
+ const img = lib.sharp.default(bufferOrFilePath)
150
+ const dimensions = await img.metadata()
151
+ const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer()
152
+ return {
153
+ buffer,
154
+ original: {
155
+ width: dimensions.width,
156
+ height: dimensions.height
157
+ }
158
+ }
159
+ } else if (
160
+ 'jimp' in lib &&
161
+ typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function'
162
+ ) {
163
+ const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp
164
+ const jimp = await read(bufferOrFilePath)
165
+ const dimensions = {
166
+ width: jimp.getWidth(),
167
+ height: jimp.getHeight()
168
+ }
169
+ const buffer = await jimp
170
+ .quality(50)
171
+ .resize(width, AUTO, RESIZE_BILINEAR)
172
+ .getBufferAsync(MIME_JPEG)
173
+ return {
174
+ buffer,
175
+ original: dimensions
176
+ }
177
+ } else {
178
+ throw new boom_1.Boom('No image processing library available')
179
+ }
180
+ }
181
+ exports.extractImageThumb = extractImageThumb
182
+ const encodeBase64EncodedStringForUpload = (b64) =>
183
+ encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''))
184
+ exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload
185
+ const generateProfilePicture = async (mediaUpload) => {
186
+ var _a, _b
187
+ let bufferOrFilePath
188
+ if (Buffer.isBuffer(mediaUpload)) {
189
+ bufferOrFilePath = mediaUpload
190
+ } else if ('url' in mediaUpload) {
191
+ bufferOrFilePath = mediaUpload.url.toString()
192
+ } else {
193
+ bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream)
194
+ }
195
+ const lib = await getImageProcessingLibrary()
196
+ let img
197
+ if (
198
+ 'sharp' in lib &&
199
+ typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function'
200
+ ) {
201
+ img = lib.sharp
202
+ .default(bufferOrFilePath)
203
+ .resize(640, 640)
204
+ .jpeg({
205
+ quality: 50
206
+ })
207
+ .toBuffer()
208
+ } else if (
209
+ 'jimp' in lib &&
210
+ typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function'
211
+ ) {
212
+ const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp
213
+ const jimp = await read(bufferOrFilePath)
214
+ const min = Math.min(jimp.getWidth(), jimp.getHeight())
215
+ const cropped = jimp.crop(0, 0, min, min)
216
+ img = cropped.quality(50).resize(640, 640, RESIZE_BILINEAR).getBufferAsync(MIME_JPEG)
217
+ } else {
218
+ throw new boom_1.Boom('No image processing library available')
219
+ }
220
+ return {
221
+ img: await img
222
+ }
223
+ }
224
+ exports.generateProfilePicture = generateProfilePicture
225
+ /** gets the SHA256 of the given media message */
226
+ const mediaMessageSHA256B64 = (message) => {
227
+ const media = Object.values(message)[0]
228
+ return (
229
+ (media === null || media === void 0 ? void 0 : media.fileSha256) &&
230
+ Buffer.from(media.fileSha256).toString('base64')
231
+ )
232
+ }
233
+ exports.mediaMessageSHA256B64 = mediaMessageSHA256B64
234
+ async function getAudioDuration(buffer) {
235
+ try {
236
+ const { PassThrough } = require('stream')
237
+ const ff = require('fluent-ffmpeg')
238
+
239
+ return await new Promise((resolve, reject) => {
240
+ const inputStream = new PassThrough()
241
+ inputStream.end(buffer)
242
+
243
+ ff(inputStream).ffprobe((err, data) => {
244
+ if (err) reject(err)
245
+ else resolve(data.format.duration)
246
+ })
247
+ })
248
+ } catch (error) {
249
+ const musicMetadata = await import('music-metadata')
250
+ let metadata
251
+ if (Buffer.isBuffer(buffer)) {
252
+ metadata = await musicMetadata.parseBuffer(buffer, undefined, {
253
+ duration: true
254
+ })
255
+ } else if (typeof buffer === 'string') {
256
+ const rStream = (0, fs_1.createReadStream)(buffer)
257
+ try {
258
+ metadata = await musicMetadata.parseStream(rStream, undefined, {
259
+ duration: true
260
+ })
261
+ } finally {
262
+ rStream.destroy()
263
+ }
264
+ } else {
265
+ metadata = await musicMetadata.parseStream(buffer, undefined, {
266
+ duration: true
267
+ })
268
+ }
269
+ return metadata.format.duration
270
+ }
271
+ }
272
+ exports.getAudioDuration = getAudioDuration
273
+ async function getAudioWaveform(buffer, logger) {
274
+ try {
275
+ const { PassThrough } = require('stream')
276
+ const ff = require('fluent-ffmpeg')
277
+
278
+ let audioData
279
+ if (Buffer.isBuffer(buffer)) {
280
+ audioData = buffer
281
+ } else if (typeof buffer === 'string') {
282
+ const rStream = require('fs').createReadStream(buffer)
283
+ audioData = await exports.toBuffer(rStream)
284
+ } else {
285
+ audioData = await exports.toBuffer(buffer)
286
+ }
287
+
288
+ return await new Promise((resolve, reject) => {
289
+ const inputStream = new PassThrough()
290
+ inputStream.end(audioData)
291
+ const chunks = []
292
+ const bars = 64
293
+
294
+ ff(inputStream)
295
+ .audioChannels(1)
296
+ .audioFrequency(16000)
297
+ .format('s16le')
298
+ .on('error', reject)
299
+ .on('end', () => {
300
+ const rawData = Buffer.concat(chunks)
301
+ const samples = rawData.length / 2
302
+ const amplitudes = []
303
+
304
+ for (let i = 0; i < samples; i++) {
305
+ amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768)
306
+ }
307
+
308
+ const blockSize = Math.floor(amplitudes.length / bars)
309
+ const avg = []
310
+ for (let i = 0; i < bars; i++) {
311
+ const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize)
312
+ avg.push(block.reduce((a, b) => a + b, 0) / block.length)
313
+ }
314
+
315
+ const max = Math.max(...avg)
316
+ const normalized = avg.map((v) => Math.floor((v / max) * 100))
317
+ resolve(new Uint8Array(normalized))
318
+ })
319
+ .pipe()
320
+ .on('data', (chunk) => chunks.push(chunk))
321
+ })
322
+ } catch (e) {
323
+ logger?.debug(e)
324
+ }
325
+ }
326
+ exports.getAudioWaveform = getAudioWaveform
327
+ async function convertToOpusBuffer(buffer, logger) {
328
+ try {
329
+ const { PassThrough } = require('stream')
330
+ const ff = require('fluent-ffmpeg')
331
+
332
+ return await new Promise((resolve, reject) => {
333
+ const inStream = new PassThrough()
334
+ const outStream = new PassThrough()
335
+ const chunks = []
336
+ inStream.end(buffer)
337
+
338
+ ff(inStream)
339
+ .noVideo()
340
+ .audioCodec('libopus')
341
+ .format('ogg')
342
+ .audioBitrate('48k')
343
+ .audioChannels(1)
344
+ .audioFrequency(48000)
345
+ .outputOptions([
346
+ '-vn',
347
+ '-b:a 64k',
348
+ '-ac 2',
349
+ '-ar 48000',
350
+ '-map_metadata',
351
+ '-1',
352
+ '-application',
353
+ 'voip'
354
+ ])
355
+ .on('error', reject)
356
+ .on('end', () => resolve(Buffer.concat(chunks)))
357
+ .pipe(outStream, {
358
+ end: true
359
+ })
360
+ outStream.on('data', (c) => chunks.push(c))
361
+ })
362
+ } catch (e) {
363
+ logger?.debug(e)
364
+ throw e
365
+ }
366
+ }
367
+ exports.convertToOpusBuffer = convertToOpusBuffer
368
+ const toReadable = (buffer) => {
369
+ const readable = new stream_1.Readable({ read: () => {} })
370
+ readable.push(buffer)
371
+ readable.push(null)
372
+ return readable
373
+ }
374
+ exports.toReadable = toReadable
375
+ const toBuffer = async (stream) => {
376
+ const chunks = []
377
+ for await (const chunk of stream) {
378
+ chunks.push(chunk)
379
+ }
380
+ stream.destroy()
381
+ return Buffer.concat(chunks)
382
+ }
383
+ exports.toBuffer = toBuffer
384
+ const getStream = async (item, opts) => {
385
+ if (Buffer.isBuffer(item)) {
386
+ return { stream: (0, exports.toReadable)(item), type: 'buffer' }
387
+ }
388
+ if ('stream' in item) {
389
+ return { stream: item.stream, type: 'readable' }
390
+ }
391
+ if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
392
+ return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' }
393
+ }
394
+ return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' }
395
+ }
396
+ exports.getStream = getStream
397
+ /** generates a thumbnail for a given media, if required */
398
+ async function generateThumbnail(file, mediaType, options) {
399
+ var _a
400
+ let thumbnail
401
+ let originalImageDimensions
402
+ if (mediaType === 'image') {
403
+ const { buffer, original } = await (0, exports.extractImageThumb)(file)
404
+ thumbnail = buffer.toString('base64')
405
+ if (original.width && original.height) {
406
+ originalImageDimensions = {
407
+ width: original.width,
408
+ height: original.height
409
+ }
410
+ }
411
+ } else if (mediaType === 'video') {
412
+ const imgFilename = (0, path_1.join)(
413
+ getTmpFilesDirectory(),
414
+ (0, generics_1.generateMessageID)() + '.jpg'
415
+ )
416
+ try {
417
+ await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 })
418
+ const buff = await fs_1.promises.readFile(imgFilename)
419
+ thumbnail = buff.toString('base64')
420
+ await fs_1.promises.unlink(imgFilename)
421
+ } catch (err) {
422
+ ;(_a = options.logger) === null || _a === void 0
423
+ ? void 0
424
+ : _a.debug('could not generate video thumb: ' + err)
425
+ }
426
+ }
427
+ return {
428
+ thumbnail,
429
+ originalImageDimensions
430
+ }
431
+ }
432
+ exports.generateThumbnail = generateThumbnail
433
+ const getHttpStream = async (url, options = {}) => {
434
+ const { default: axios } = await import('axios')
435
+ const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' })
436
+ return fetched.data
437
+ }
438
+ exports.getHttpStream = getHttpStream
439
+ const prepareStream = async (
440
+ media,
441
+ mediaType,
442
+ { logger, saveOriginalFileIfRequired, opts } = {}
443
+ ) => {
444
+ const { stream, type } = await (0, exports.getStream)(media, opts)
445
+ logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream')
446
+ let bodyPath
447
+ let didSaveToTmpPath = false
448
+ try {
449
+ const buffer = await (0, exports.toBuffer)(stream)
450
+ if (type === 'file') {
451
+ bodyPath = media.url
452
+ } else if (saveOriginalFileIfRequired) {
453
+ bodyPath = (0, path_1.join)(
454
+ getTmpFilesDirectory(),
455
+ mediaType + (0, generics_1.generateMessageID)()
456
+ )
457
+ ;(0, fs_1.writeFileSync)(bodyPath, buffer)
458
+ didSaveToTmpPath = true
459
+ }
460
+ const fileLength = buffer.length
461
+ const fileSha256 = Crypto.createHash('sha256').update(buffer).digest()
462
+ stream === null || stream === void 0 ? void 0 : stream.destroy()
463
+ logger === null || logger === void 0
464
+ ? void 0
465
+ : logger.debug('prepare stream data successfully')
466
+ return {
467
+ mediaKey: undefined,
468
+ encWriteStream: buffer,
469
+ fileLength,
470
+ fileSha256,
471
+ fileEncSha256: undefined,
472
+ bodyPath,
473
+ didSaveToTmpPath
474
+ }
475
+ } catch (error) {
476
+ // destroy all streams with error
477
+ stream.destroy()
478
+ if (didSaveToTmpPath) {
479
+ try {
480
+ await fs_1.promises.unlink(bodyPath)
481
+ } catch (err) {
482
+ logger === null || logger === void 0
483
+ ? void 0
484
+ : logger.error({ err }, 'failed to save to tmp path')
485
+ }
486
+ }
487
+ throw error
488
+ }
489
+ }
490
+ exports.prepareStream = prepareStream
491
+ const encryptedStream = async (
492
+ media,
493
+ mediaType,
494
+ { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}
495
+ ) => {
496
+ const { stream, type } = await (0, exports.getStream)(media, opts)
497
+
498
+ let finalStream = stream
499
+ if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
500
+ try {
501
+ const buffer = await (0, exports.toBuffer)(stream)
502
+ const opusBuffer = await exports.convertToOpusBuffer(buffer, logger)
503
+ finalStream = (0, exports.toReadable)(opusBuffer)
504
+ } catch (error) {
505
+ const { stream: newStream } = await (0, exports.getStream)(media, opts)
506
+ finalStream = newStream
507
+ }
508
+ }
509
+
510
+ const mediaKey = Crypto.randomBytes(32)
511
+ const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType)
512
+ const encWriteStream = new stream_1.Readable({ read: () => {} })
513
+ let bodyPath
514
+ let writeStream
515
+ let didSaveToTmpPath = false
516
+
517
+ if (type === 'file') {
518
+ bodyPath = media.url
519
+ } else if (saveOriginalFileIfRequired) {
520
+ bodyPath = (0, path_1.join)(
521
+ getTmpFilesDirectory(),
522
+ mediaType + (0, generics_1.generateMessageID)()
523
+ )
524
+ writeStream = (0, fs_1.createWriteStream)(bodyPath)
525
+ didSaveToTmpPath = true
526
+ }
527
+
528
+ let fileLength = 0
529
+ const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
530
+ let hmac = Crypto.createHmac('sha256', macKey).update(iv)
531
+ let sha256Plain = Crypto.createHash('sha256')
532
+ let sha256Enc = Crypto.createHash('sha256')
533
+
534
+ try {
535
+ for await (const data of finalStream) {
536
+ fileLength += data.length
537
+ if (
538
+ type === 'remote' &&
539
+ (opts === null || opts === void 0 ? void 0 : opts.maxContentLength) &&
540
+ fileLength + data.length > opts.maxContentLength
541
+ ) {
542
+ throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
543
+ data: { media, type }
544
+ })
545
+ }
546
+
547
+ sha256Plain = sha256Plain.update(data)
548
+ if (writeStream) {
549
+ if (!writeStream.write(data)) {
550
+ await (0, events_1.once)(writeStream, 'drain')
551
+ }
552
+ }
553
+ onChunk(aes.update(data))
554
+ }
555
+
556
+ onChunk(aes.final())
557
+ const mac = hmac.digest().slice(0, 10)
558
+ sha256Enc = sha256Enc.update(mac)
559
+ const fileSha256 = sha256Plain.digest()
560
+ const fileEncSha256 = sha256Enc.digest()
561
+
562
+ encWriteStream.push(mac)
563
+ encWriteStream.push(null)
564
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.end()
565
+ finalStream.destroy()
566
+
567
+ return {
568
+ mediaKey,
569
+ encWriteStream,
570
+ bodyPath,
571
+ mac,
572
+ fileEncSha256,
573
+ fileSha256,
574
+ fileLength,
575
+ didSaveToTmpPath
576
+ }
577
+ } catch (error) {
578
+ encWriteStream.destroy()
579
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy()
580
+ aes.destroy()
581
+ hmac.destroy()
582
+ sha256Plain.destroy()
583
+ sha256Enc.destroy()
584
+ finalStream.destroy()
585
+
586
+ if (didSaveToTmpPath) {
587
+ try {
588
+ await fs_1.promises.unlink(bodyPath)
589
+ } catch (err) {}
590
+ }
591
+ throw error
592
+ }
593
+
594
+ function onChunk(buff) {
595
+ sha256Enc = sha256Enc.update(buff)
596
+ hmac = hmac.update(buff)
597
+ encWriteStream.push(buff)
598
+ }
599
+ }
600
+ exports.encryptedStream = encryptedStream
601
+ const DEF_HOST = 'mmg.whatsapp.net'
602
+ const AES_CHUNK_SIZE = 16
603
+ const toSmallestChunkSize = (num) => {
604
+ return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE
605
+ }
606
+ const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`
607
+ exports.getUrlFromDirectPath = getUrlFromDirectPath
608
+ const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
609
+ const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath)
610
+ const keys = getMediaKeys(mediaKey, type)
611
+ return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts)
612
+ }
613
+ exports.downloadContentFromMessage = downloadContentFromMessage
614
+ /**
615
+ * Decrypts and downloads an AES256-CBC encrypted file given the keys.
616
+ * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
617
+ * */
618
+ const downloadEncryptedContent = async (
619
+ downloadUrl,
620
+ { cipherKey, iv },
621
+ { startByte, endByte, options } = {}
622
+ ) => {
623
+ let bytesFetched = 0
624
+ let startChunk = 0
625
+ let firstBlockIsIV = false
626
+ // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
627
+ if (startByte) {
628
+ const chunk = toSmallestChunkSize(startByte || 0)
629
+ if (chunk) {
630
+ startChunk = chunk - AES_CHUNK_SIZE
631
+ bytesFetched = chunk
632
+ firstBlockIsIV = true
633
+ }
634
+ }
635
+ const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined
636
+ const headers = {
637
+ ...((options === null || options === void 0 ? void 0 : options.headers) || {}),
638
+ Origin: Defaults_1.DEFAULT_ORIGIN
639
+ }
640
+ if (startChunk || endChunk) {
641
+ headers.Range = `bytes=${startChunk}-`
642
+ if (endChunk) {
643
+ headers.Range += endChunk
644
+ }
645
+ }
646
+ // download the message
647
+ const fetched = await (0, exports.getHttpStream)(downloadUrl, {
648
+ ...(options || {}),
649
+ headers,
650
+ maxBodyLength: Infinity,
651
+ maxContentLength: Infinity
652
+ })
653
+ let remainingBytes = Buffer.from([])
654
+ let aes
655
+ const pushBytes = (bytes, push) => {
656
+ if (startByte || endByte) {
657
+ const start =
658
+ bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
659
+ const end =
660
+ bytesFetched + bytes.length < endByte
661
+ ? undefined
662
+ : Math.max(endByte - bytesFetched, 0)
663
+ push(bytes.slice(start, end))
664
+ bytesFetched += bytes.length
665
+ } else {
666
+ push(bytes)
667
+ }
668
+ }
669
+ const output = new stream_1.Transform({
670
+ transform(chunk, _, callback) {
671
+ let data = Buffer.concat([remainingBytes, chunk])
672
+ const decryptLength = toSmallestChunkSize(data.length)
673
+ remainingBytes = data.slice(decryptLength)
674
+ data = data.slice(0, decryptLength)
675
+ if (!aes) {
676
+ let ivValue = iv
677
+ if (firstBlockIsIV) {
678
+ ivValue = data.slice(0, AES_CHUNK_SIZE)
679
+ data = data.slice(AES_CHUNK_SIZE)
680
+ }
681
+ aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue)
682
+ // if an end byte that is not EOF is specified
683
+ // stop auto padding (PKCS7) -- otherwise throws an error for decryption
684
+ if (endByte) {
685
+ aes.setAutoPadding(false)
686
+ }
687
+ }
688
+ try {
689
+ pushBytes(aes.update(data), (b) => this.push(b))
690
+ callback()
691
+ } catch (error) {
692
+ callback(error)
693
+ }
694
+ },
695
+ final(callback) {
696
+ try {
697
+ pushBytes(aes.final(), (b) => this.push(b))
698
+ callback()
699
+ } catch (error) {
700
+ callback(error)
701
+ }
702
+ }
703
+ })
704
+ return fetched.pipe(output, { end: true })
705
+ }
706
+ exports.downloadEncryptedContent = downloadEncryptedContent
707
+ function extensionForMediaMessage(message) {
708
+ const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1]
709
+ const type = Object.keys(message)[0]
710
+ let extension
711
+ if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
712
+ extension = '.jpeg'
713
+ } else {
714
+ const messageContent = message[type]
715
+ extension = getExtension(messageContent.mimetype)
716
+ }
717
+ return extension
718
+ }
719
+ exports.extensionForMediaMessage = extensionForMediaMessage
720
+ const getWAUploadToServer = (
721
+ { customUploadHosts, fetchAgent, logger, options },
722
+ refreshMediaConn
723
+ ) => {
724
+ return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
725
+ var _a, _b
726
+ const { default: axios } = await import('axios')
727
+ // send a query JSON to obtain the url & auth token to upload our media
728
+ let uploadInfo = await refreshMediaConn(false)
729
+ let urls
730
+ const hosts = [...customUploadHosts, ...uploadInfo.hosts]
731
+ const chunks = []
732
+ if (!Buffer.isBuffer(stream)) {
733
+ for await (const chunk of stream) {
734
+ chunks.push(chunk)
735
+ }
736
+ }
737
+ const reqBody = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks)
738
+ fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64)
739
+ let media = Defaults_1.MEDIA_PATH_MAP[mediaType]
740
+ if (newsletter) {
741
+ media =
742
+ media === null || media === void 0
743
+ ? void 0
744
+ : media.replace('/mms/', '/newsletter/newsletter-')
745
+ }
746
+ for (const { hostname, maxContentLengthBytes } of hosts) {
747
+ logger.debug(`uploading to "${hostname}"`)
748
+ const auth = encodeURIComponent(uploadInfo.auth) // the auth token
749
+ const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
750
+ let result
751
+ try {
752
+ if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
753
+ throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 })
754
+ }
755
+ const body = await axios.post(url, reqBody, {
756
+ ...options,
757
+ headers: {
758
+ ...(options.headers || {}),
759
+ 'Content-Type': 'application/octet-stream',
760
+ Origin: Defaults_1.DEFAULT_ORIGIN
761
+ },
762
+ httpsAgent: fetchAgent,
763
+ timeout: timeoutMs,
764
+ responseType: 'json',
765
+ maxBodyLength: Infinity,
766
+ maxContentLength: Infinity
767
+ })
768
+ result = body.data
769
+ if (
770
+ (result === null || result === void 0 ? void 0 : result.url) ||
771
+ (result === null || result === void 0 ? void 0 : result.directPath)
772
+ ) {
773
+ urls = {
774
+ mediaUrl: result.url,
775
+ directPath: result.direct_path,
776
+ handle: result.handle
777
+ }
778
+ break
779
+ } else {
780
+ uploadInfo = await refreshMediaConn(true)
781
+ throw new Error(`upload failed, reason: ${JSON.stringify(result)}`)
782
+ }
783
+ } catch (error) {
784
+ if (axios.isAxiosError(error)) {
785
+ result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data
786
+ }
787
+ const isLast =
788
+ hostname ===
789
+ ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0
790
+ ? void 0
791
+ : _b.hostname)
792
+ logger.warn(
793
+ { trace: error.stack, uploadResult: result },
794
+ `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`
795
+ )
796
+ }
797
+ }
798
+ if (!urls) {
799
+ throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 })
800
+ }
801
+ return urls
802
+ }
803
+ }
804
+ exports.getWAUploadToServer = getWAUploadToServer
805
+ const getMediaRetryKey = (mediaKey) => {
806
+ return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' })
807
+ }
808
+ /**
809
+ * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
810
+ */
811
+ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
812
+ const recp = { stanzaId: key.id }
813
+ const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish()
814
+ const iv = Crypto.randomBytes(12)
815
+ const retryKey = getMediaRetryKey(mediaKey)
816
+ const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id))
817
+ const req = {
818
+ tag: 'receipt',
819
+ attrs: {
820
+ id: key.id,
821
+ to: (0, WABinary_1.jidNormalizedUser)(meId),
822
+ type: 'server-error'
823
+ },
824
+ content: [
825
+ // this encrypt node is actually pretty useless
826
+ // the media is returned even without this node
827
+ // keeping it here to maintain parity with WA Web
828
+ {
829
+ tag: 'encrypt',
830
+ attrs: {},
831
+ content: [
832
+ { tag: 'enc_p', attrs: {}, content: ciphertext },
833
+ { tag: 'enc_iv', attrs: {}, content: iv }
834
+ ]
835
+ },
836
+ {
837
+ tag: 'rmr',
838
+ attrs: {
839
+ jid: key.remoteJid,
840
+ from_me: (!!key.fromMe).toString(),
841
+ // @ts-ignore
842
+ participant: key.participant || undefined
843
+ }
844
+ }
845
+ ]
846
+ }
847
+ return req
848
+ }
849
+ exports.encryptMediaRetryRequest = encryptMediaRetryRequest
850
+ const decodeMediaRetryNode = (node) => {
851
+ const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr')
852
+ const event = {
853
+ key: {
854
+ id: node.attrs.id,
855
+ remoteJid: rmrNode.attrs.jid,
856
+ fromMe: rmrNode.attrs.from_me === 'true',
857
+ participant: rmrNode.attrs.participant
858
+ }
859
+ }
860
+ const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error')
861
+ if (errorNode) {
862
+ const errorCode = +errorNode.attrs.code
863
+ event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, {
864
+ data: errorNode.attrs,
865
+ statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode)
866
+ })
867
+ } else {
868
+ const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt')
869
+ const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p')
870
+ const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv')
871
+ if (ciphertext && iv) {
872
+ event.media = { ciphertext, iv }
873
+ } else {
874
+ event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', {
875
+ statusCode: 404
876
+ })
877
+ }
878
+ }
879
+ return event
880
+ }
881
+ exports.decodeMediaRetryNode = decodeMediaRetryNode
882
+ const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
883
+ const retryKey = getMediaRetryKey(mediaKey)
884
+ const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId))
885
+ return WAProto_1.proto.MediaRetryNotification.decode(plaintext)
886
+ }
887
+ exports.decryptMediaRetryData = decryptMediaRetryData
888
+ const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code]
889
+ exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry
890
+ const MEDIA_RETRY_STATUS_MAP = {
891
+ [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
892
+ [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
893
+ [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
894
+ [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
895
+ }
896
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
897
+ function __importStar(arg0) {
898
+ throw new Error('Function not implemented.')
899
+ }