@pontalabs/baileys 1.0.4 → 1.0.6

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 (447) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +267 -839
  3. package/WAProto/GenerateStatics.sh +3 -4
  4. package/WAProto/WAProto.proto +1215 -511
  5. package/WAProto/fix-imports.js +85 -0
  6. package/WAProto/index.d.ts +14017 -55
  7. package/WAProto/index.js +97689 -29
  8. package/engine-requirements.js +10 -0
  9. package/lib/Defaults/index.d.ts +72 -74
  10. package/lib/Defaults/index.d.ts.map +1 -0
  11. package/lib/Defaults/index.js +94 -108
  12. package/lib/Defaults/index.js.map +1 -0
  13. package/lib/Signal/Group/ciphertext-message.d.ts +8 -7
  14. package/lib/Signal/Group/ciphertext-message.d.ts.map +1 -0
  15. package/lib/Signal/Group/ciphertext-message.js +9 -16
  16. package/lib/Signal/Group/ciphertext-message.js.map +1 -0
  17. package/lib/Signal/Group/group-session-builder.d.ts +11 -13
  18. package/lib/Signal/Group/group-session-builder.d.ts.map +1 -0
  19. package/lib/Signal/Group/group-session-builder.js +19 -61
  20. package/lib/Signal/Group/group-session-builder.js.map +1 -0
  21. package/lib/Signal/Group/group_cipher.d.ts +14 -16
  22. package/lib/Signal/Group/group_cipher.d.ts.map +1 -0
  23. package/lib/Signal/Group/group_cipher.js +41 -70
  24. package/lib/Signal/Group/group_cipher.js.map +1 -0
  25. package/lib/Signal/Group/index.d.ts +12 -11
  26. package/lib/Signal/Group/index.d.ts.map +1 -0
  27. package/lib/Signal/Group/index.js +12 -61
  28. package/lib/Signal/Group/index.js.map +1 -0
  29. package/lib/Signal/Group/keyhelper.d.ts +9 -14
  30. package/lib/Signal/Group/keyhelper.d.ts.map +1 -0
  31. package/lib/Signal/Group/keyhelper.js +10 -58
  32. package/lib/Signal/Group/keyhelper.js.map +1 -0
  33. package/lib/Signal/Group/sender-chain-key.d.ts +13 -13
  34. package/lib/Signal/Group/sender-chain-key.d.ts.map +1 -0
  35. package/lib/Signal/Group/sender-chain-key.js +13 -34
  36. package/lib/Signal/Group/sender-chain-key.js.map +1 -0
  37. package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -16
  38. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +1 -0
  39. package/lib/Signal/Group/sender-key-distribution-message.js +28 -36
  40. package/lib/Signal/Group/sender-key-distribution-message.js.map +1 -0
  41. package/lib/Signal/Group/sender-key-message.d.ts +18 -18
  42. package/lib/Signal/Group/sender-key-message.d.ts.map +1 -0
  43. package/lib/Signal/Group/sender-key-message.js +38 -45
  44. package/lib/Signal/Group/sender-key-message.js.map +1 -0
  45. package/lib/Signal/Group/sender-key-name.d.ts +14 -15
  46. package/lib/Signal/Group/sender-key-name.d.ts.map +1 -0
  47. package/lib/Signal/Group/sender-key-name.js +20 -31
  48. package/lib/Signal/Group/sender-key-name.js.map +1 -0
  49. package/lib/Signal/Group/sender-key-record.d.ts +24 -25
  50. package/lib/Signal/Group/sender-key-record.d.ts.map +1 -0
  51. package/lib/Signal/Group/sender-key-record.js +18 -35
  52. package/lib/Signal/Group/sender-key-record.js.map +1 -0
  53. package/lib/Signal/Group/sender-key-state.d.ts +29 -34
  54. package/lib/Signal/Group/sender-key-state.d.ts.map +1 -0
  55. package/lib/Signal/Group/sender-key-state.js +37 -100
  56. package/lib/Signal/Group/sender-key-state.js.map +1 -0
  57. package/lib/Signal/Group/sender-message-key.d.ts +11 -10
  58. package/lib/Signal/Group/sender-message-key.d.ts.map +1 -0
  59. package/lib/Signal/Group/sender-message-key.js +15 -22
  60. package/lib/Signal/Group/sender-message-key.js.map +1 -0
  61. package/lib/Signal/libsignal.d.ts +5 -8
  62. package/lib/Signal/libsignal.d.ts.map +1 -0
  63. package/lib/Signal/libsignal.js +279 -121
  64. package/lib/Signal/libsignal.js.map +1 -0
  65. package/lib/Signal/lid-mapping.d.ts +23 -28
  66. package/lib/Signal/lid-mapping.d.ts.map +1 -0
  67. package/lib/Signal/lid-mapping.js +277 -184
  68. package/lib/Signal/lid-mapping.js.map +1 -0
  69. package/lib/Socket/Client/index.d.ts +3 -2
  70. package/lib/Socket/Client/index.d.ts.map +1 -0
  71. package/lib/Socket/Client/index.js +3 -22
  72. package/lib/Socket/Client/index.js.map +1 -0
  73. package/lib/Socket/Client/types.d.ts +15 -15
  74. package/lib/Socket/Client/types.d.ts.map +1 -0
  75. package/lib/Socket/Client/types.js +8 -15
  76. package/lib/Socket/Client/types.js.map +1 -0
  77. package/lib/Socket/Client/websocket.d.ts +12 -12
  78. package/lib/Socket/Client/websocket.d.ts.map +1 -0
  79. package/lib/Socket/Client/websocket.js +29 -37
  80. package/lib/Socket/Client/websocket.js.map +1 -0
  81. package/lib/Socket/business.d.ts +208 -178
  82. package/lib/Socket/business.d.ts.map +1 -0
  83. package/lib/Socket/business.js +91 -127
  84. package/lib/Socket/business.js.map +1 -0
  85. package/lib/Socket/chats.d.ts +120 -93
  86. package/lib/Socket/chats.d.ts.map +1 -0
  87. package/lib/Socket/chats.js +727 -652
  88. package/lib/Socket/chats.js.map +1 -0
  89. package/lib/Socket/communities.d.ts +273 -0
  90. package/lib/Socket/communities.d.ts.map +1 -0
  91. package/lib/Socket/communities.js +431 -0
  92. package/lib/Socket/communities.js.map +1 -0
  93. package/lib/Socket/dugong.js +261 -0
  94. package/lib/Socket/dugong.js.bak +204 -0
  95. package/lib/Socket/groups.d.ts +143 -111
  96. package/lib/Socket/groups.d.ts.map +1 -0
  97. package/lib/Socket/groups.js +165 -177
  98. package/lib/Socket/groups.js.map +1 -0
  99. package/lib/Socket/index.d.ts +252 -183
  100. package/lib/Socket/index.d.ts.map +1 -0
  101. package/lib/Socket/index.js +38 -18
  102. package/lib/Socket/index.js.bak +44 -0
  103. package/lib/Socket/index.js.map +1 -0
  104. package/lib/Socket/messages-recv.d.ts +196 -167
  105. package/lib/Socket/messages-recv.d.ts.map +1 -0
  106. package/lib/Socket/messages-recv.js +1443 -1563
  107. package/lib/Socket/messages-recv.js.map +1 -0
  108. package/lib/Socket/messages-send.d.ts +192 -158
  109. package/lib/Socket/messages-send.d.ts.map +1 -0
  110. package/lib/Socket/messages-send.js +1102 -1208
  111. package/lib/Socket/messages-send.js.bak +1400 -0
  112. package/lib/Socket/messages-send.js.map +1 -0
  113. package/lib/Socket/mex.d.ts +3 -0
  114. package/lib/Socket/mex.d.ts.map +1 -0
  115. package/lib/Socket/mex.js +42 -0
  116. package/lib/Socket/mex.js.map +1 -0
  117. package/lib/Socket/newsletter.d.ts +163 -138
  118. package/lib/Socket/newsletter.d.ts.map +1 -0
  119. package/lib/Socket/newsletter.js +153 -249
  120. package/lib/Socket/newsletter.js.map +1 -0
  121. package/lib/Socket/socket.d.ts +50 -36
  122. package/lib/Socket/socket.d.ts.map +1 -0
  123. package/lib/Socket/socket.js +749 -820
  124. package/lib/Socket/socket.js.map +1 -0
  125. package/lib/Store/index.d.ts +4 -4
  126. package/lib/Store/index.d.ts.map +1 -0
  127. package/lib/Store/index.js +4 -24
  128. package/lib/Store/index.js.map +1 -0
  129. package/lib/Store/make-in-memory-store.d.ts +44 -104
  130. package/lib/Store/make-in-memory-store.d.ts.map +1 -0
  131. package/lib/Store/make-in-memory-store.js +199 -213
  132. package/lib/Store/make-in-memory-store.js.map +1 -0
  133. package/lib/Store/make-ordered-dictionary.d.ts +13 -12
  134. package/lib/Store/make-ordered-dictionary.d.ts.map +1 -0
  135. package/lib/Store/make-ordered-dictionary.js +38 -45
  136. package/lib/Store/make-ordered-dictionary.js.map +1 -0
  137. package/lib/Store/object-repository.d.ts +11 -10
  138. package/lib/Store/object-repository.d.ts.map +1 -0
  139. package/lib/Store/object-repository.js +9 -16
  140. package/lib/Store/object-repository.js.map +1 -0
  141. package/lib/Types/Auth.d.ts +93 -97
  142. package/lib/Types/Auth.d.ts.map +1 -0
  143. package/lib/Types/Auth.js +2 -3
  144. package/lib/Types/Auth.js.map +1 -0
  145. package/lib/Types/Bussines.d.ts +25 -0
  146. package/lib/Types/Bussines.d.ts.map +1 -0
  147. package/lib/Types/Bussines.js +2 -3
  148. package/lib/Types/Bussines.js.map +1 -0
  149. package/lib/Types/Call.d.ts +14 -13
  150. package/lib/Types/Call.d.ts.map +1 -0
  151. package/lib/Types/Call.js +2 -3
  152. package/lib/Types/Call.js.map +1 -0
  153. package/lib/Types/Chat.d.ts +78 -97
  154. package/lib/Types/Chat.d.ts.map +1 -0
  155. package/lib/Types/Chat.js +8 -9
  156. package/lib/Types/Chat.js.map +1 -0
  157. package/lib/Types/Contact.d.ts +12 -9
  158. package/lib/Types/Contact.d.ts.map +1 -0
  159. package/lib/Types/Contact.js +2 -3
  160. package/lib/Types/Contact.js.map +1 -0
  161. package/lib/Types/Events.d.ts +206 -176
  162. package/lib/Types/Events.d.ts.map +1 -0
  163. package/lib/Types/Events.js +2 -3
  164. package/lib/Types/Events.js.map +1 -0
  165. package/lib/Types/GroupMetadata.d.ts +53 -48
  166. package/lib/Types/GroupMetadata.d.ts.map +1 -0
  167. package/lib/Types/GroupMetadata.js +2 -3
  168. package/lib/Types/GroupMetadata.js.map +1 -0
  169. package/lib/Types/Label.d.ts +13 -14
  170. package/lib/Types/Label.d.ts.map +1 -0
  171. package/lib/Types/Label.js +24 -30
  172. package/lib/Types/Label.js.map +1 -0
  173. package/lib/Types/LabelAssociation.d.ts +15 -20
  174. package/lib/Types/LabelAssociation.d.ts.map +1 -0
  175. package/lib/Types/LabelAssociation.js +6 -12
  176. package/lib/Types/LabelAssociation.js.map +1 -0
  177. package/lib/Types/Message.d.ts +248 -412
  178. package/lib/Types/Message.d.ts.map +1 -0
  179. package/lib/Types/Message.js +17 -19
  180. package/lib/Types/Message.js.map +1 -0
  181. package/lib/Types/Mex.d.ts +141 -0
  182. package/lib/Types/Mex.d.ts.map +1 -0
  183. package/lib/Types/Mex.js +37 -0
  184. package/lib/Types/Mex.js.map +1 -0
  185. package/lib/Types/Product.d.ts +58 -71
  186. package/lib/Types/Product.d.ts.map +1 -0
  187. package/lib/Types/Product.js +2 -3
  188. package/lib/Types/Product.js.map +1 -0
  189. package/lib/Types/RichType.d.ts +3 -0
  190. package/lib/Types/RichType.d.ts.map +1 -0
  191. package/lib/Types/RichType.js +23 -0
  192. package/lib/Types/RichType.js.bak +23 -0
  193. package/lib/Types/RichType.js.map +1 -0
  194. package/lib/Types/Signal.d.ts +71 -82
  195. package/lib/Types/Signal.d.ts.map +1 -0
  196. package/lib/Types/Signal.js +2 -3
  197. package/lib/Types/Signal.js.map +1 -0
  198. package/lib/Types/Socket.d.ts +76 -81
  199. package/lib/Types/Socket.d.ts.map +1 -0
  200. package/lib/Types/Socket.js +3 -3
  201. package/lib/Types/Socket.js.map +1 -0
  202. package/lib/Types/State.d.ts +75 -19
  203. package/lib/Types/State.d.ts.map +1 -0
  204. package/lib/Types/State.js +56 -14
  205. package/lib/Types/State.js.map +1 -0
  206. package/lib/Types/USync.d.ts +8 -8
  207. package/lib/Types/USync.d.ts.map +1 -0
  208. package/lib/Types/USync.js +2 -3
  209. package/lib/Types/USync.js.map +1 -0
  210. package/lib/Types/index.d.ts +47 -62
  211. package/lib/Types/index.d.ts.map +1 -0
  212. package/lib/Types/index.js +26 -50
  213. package/lib/Types/index.js.map +1 -0
  214. package/lib/Utils/auth-utils.d.ts +13 -10
  215. package/lib/Utils/auth-utils.d.ts.map +1 -0
  216. package/lib/Utils/auth-utils.js +213 -454
  217. package/lib/Utils/auth-utils.js.map +1 -0
  218. package/lib/Utils/browser-utils.d.ts +4 -0
  219. package/lib/Utils/browser-utils.d.ts.map +1 -0
  220. package/lib/Utils/browser-utils.js +28 -0
  221. package/lib/Utils/browser-utils.js.map +1 -0
  222. package/lib/Utils/business.d.ts +14 -20
  223. package/lib/Utils/business.d.ts.map +1 -0
  224. package/lib/Utils/business.js +110 -134
  225. package/lib/Utils/business.js.map +1 -0
  226. package/lib/Utils/chat-utils.d.ts +88 -70
  227. package/lib/Utils/chat-utils.d.ts.map +1 -0
  228. package/lib/Utils/chat-utils.js +466 -403
  229. package/lib/Utils/chat-utils.js.map +1 -0
  230. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  231. package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
  232. package/lib/Utils/companion-reg-client-utils.js +35 -0
  233. package/lib/Utils/companion-reg-client-utils.js.map +1 -0
  234. package/lib/Utils/crypto.d.ts +27 -46
  235. package/lib/Utils/crypto.d.ts.map +1 -0
  236. package/lib/Utils/crypto.js +117 -188
  237. package/lib/Utils/crypto.js.map +1 -0
  238. package/lib/Utils/decode-wa-message.d.ts +58 -45
  239. package/lib/Utils/decode-wa-message.d.ts.map +1 -0
  240. package/lib/Utils/decode-wa-message.js +213 -303
  241. package/lib/Utils/decode-wa-message.js.map +1 -0
  242. package/lib/Utils/event-buffer.d.ts +14 -17
  243. package/lib/Utils/event-buffer.d.ts.map +1 -0
  244. package/lib/Utils/event-buffer.js +321 -296
  245. package/lib/Utils/event-buffer.js.map +1 -0
  246. package/lib/Utils/generics.d.ts +66 -92
  247. package/lib/Utils/generics.d.ts.map +1 -0
  248. package/lib/Utils/generics.js +270 -436
  249. package/lib/Utils/generics.js.map +1 -0
  250. package/lib/Utils/history.d.ts +24 -23
  251. package/lib/Utils/history.d.ts.map +1 -0
  252. package/lib/Utils/history.js +106 -76
  253. package/lib/Utils/history.js.map +1 -0
  254. package/lib/Utils/identity-change-handler.d.ts +44 -0
  255. package/lib/Utils/identity-change-handler.d.ts.map +1 -0
  256. package/lib/Utils/identity-change-handler.js +50 -0
  257. package/lib/Utils/identity-change-handler.js.map +1 -0
  258. package/lib/Utils/index.d.ts +24 -21
  259. package/lib/Utils/index.d.ts.map +1 -0
  260. package/lib/Utils/index.js +24 -41
  261. package/lib/Utils/index.js.map +1 -0
  262. package/lib/Utils/link-preview.d.ts +12 -14
  263. package/lib/Utils/link-preview.d.ts.map +1 -0
  264. package/lib/Utils/link-preview.js +40 -75
  265. package/lib/Utils/link-preview.js.map +1 -0
  266. package/lib/Utils/logger.d.ts +10 -11
  267. package/lib/Utils/logger.d.ts.map +1 -0
  268. package/lib/Utils/logger.js +3 -7
  269. package/lib/Utils/logger.js.map +1 -0
  270. package/lib/Utils/lt-hash.d.ts +8 -14
  271. package/lib/Utils/lt-hash.d.ts.map +1 -0
  272. package/lib/Utils/lt-hash.js +3 -53
  273. package/lib/Utils/lt-hash.js.map +1 -0
  274. package/lib/Utils/make-mutex.d.ts +7 -7
  275. package/lib/Utils/make-mutex.d.ts.map +1 -0
  276. package/lib/Utils/make-mutex.js +28 -44
  277. package/lib/Utils/make-mutex.js.map +1 -0
  278. package/lib/Utils/message-retry-manager.d.ts +115 -88
  279. package/lib/Utils/message-retry-manager.d.ts.map +1 -0
  280. package/lib/Utils/message-retry-manager.js +265 -160
  281. package/lib/Utils/message-retry-manager.js.map +1 -0
  282. package/lib/Utils/messages-media.d.ts +105 -107
  283. package/lib/Utils/messages-media.d.ts.map +1 -0
  284. package/lib/Utils/messages-media.js +548 -619
  285. package/lib/Utils/messages-media.js.bak +786 -0
  286. package/lib/Utils/messages-media.js.map +1 -0
  287. package/lib/Utils/messages.d.ts +52 -66
  288. package/lib/Utils/messages.d.ts.map +1 -0
  289. package/lib/Utils/messages.js +1730 -1558
  290. package/lib/Utils/messages.js.backup +1768 -0
  291. package/lib/Utils/messages.js.bak +1809 -0
  292. package/lib/Utils/messages.js.bak2 +893 -0
  293. package/lib/Utils/messages.js.bak3 +1183 -0
  294. package/lib/Utils/messages.js.map +1 -0
  295. package/lib/Utils/noise-handler.d.ts +18 -18
  296. package/lib/Utils/noise-handler.d.ts.map +1 -0
  297. package/lib/Utils/noise-handler.js +176 -130
  298. package/lib/Utils/noise-handler.js.map +1 -0
  299. package/lib/Utils/offline-node-processor.d.ts +17 -0
  300. package/lib/Utils/offline-node-processor.d.ts.map +1 -0
  301. package/lib/Utils/offline-node-processor.js +40 -0
  302. package/lib/Utils/offline-node-processor.js.map +1 -0
  303. package/lib/Utils/pre-key-manager.d.ts +28 -0
  304. package/lib/Utils/pre-key-manager.d.ts.map +1 -0
  305. package/lib/Utils/pre-key-manager.js +106 -0
  306. package/lib/Utils/pre-key-manager.js.map +1 -0
  307. package/lib/Utils/process-message.d.ts +43 -32
  308. package/lib/Utils/process-message.d.ts.map +1 -0
  309. package/lib/Utils/process-message.js +484 -282
  310. package/lib/Utils/process-message.js.map +1 -0
  311. package/lib/Utils/reporting-utils.d.ts +11 -0
  312. package/lib/Utils/reporting-utils.d.ts.map +1 -0
  313. package/lib/Utils/reporting-utils.js +258 -0
  314. package/lib/Utils/reporting-utils.js.map +1 -0
  315. package/lib/Utils/rich-message-utils.d.ts +49 -0
  316. package/lib/Utils/rich-message-utils.d.ts.map +1 -0
  317. package/lib/Utils/rich-message-utils.js +1220 -724
  318. package/lib/Utils/rich-message-utils.js.backup +706 -0
  319. package/lib/Utils/rich-message-utils.js.bak +246 -0
  320. package/lib/Utils/rich-message-utils.js.map +1 -0
  321. package/lib/Utils/rich-message-utils.js.original +1332 -0
  322. package/lib/Utils/signal.d.ts +37 -32
  323. package/lib/Utils/signal.d.ts.map +1 -0
  324. package/lib/Utils/signal.js +140 -105
  325. package/lib/Utils/signal.js.map +1 -0
  326. package/lib/Utils/stanza-ack.d.ts +11 -0
  327. package/lib/Utils/stanza-ack.d.ts.map +1 -0
  328. package/lib/Utils/stanza-ack.js +38 -0
  329. package/lib/Utils/stanza-ack.js.map +1 -0
  330. package/lib/Utils/sync-action-utils.d.ts +19 -0
  331. package/lib/Utils/sync-action-utils.d.ts.map +1 -0
  332. package/lib/Utils/sync-action-utils.js +49 -0
  333. package/lib/Utils/sync-action-utils.js.map +1 -0
  334. package/lib/Utils/tc-token-utils.d.ts +37 -0
  335. package/lib/Utils/tc-token-utils.d.ts.map +1 -0
  336. package/lib/Utils/tc-token-utils.js +163 -0
  337. package/lib/Utils/tc-token-utils.js.map +1 -0
  338. package/lib/Utils/use-multi-file-auth-state.d.ts +5 -5
  339. package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
  340. package/lib/Utils/use-multi-file-auth-state.js +70 -120
  341. package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
  342. package/lib/Utils/use-single-file-auth-state.d.ts +11 -13
  343. package/lib/Utils/use-single-file-auth-state.d.ts.map +1 -0
  344. package/lib/Utils/use-single-file-auth-state.js +92 -66
  345. package/lib/Utils/use-single-file-auth-state.js.map +1 -0
  346. package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
  347. package/lib/Utils/use-sqlite-auth-state.d.ts.map +1 -0
  348. package/lib/Utils/use-sqlite-auth-state.js +109 -0
  349. package/lib/Utils/use-sqlite-auth-state.js.map +1 -0
  350. package/lib/Utils/validate-connection.d.ts +11 -13
  351. package/lib/Utils/validate-connection.d.ts.map +1 -0
  352. package/lib/Utils/validate-connection.js +128 -113
  353. package/lib/Utils/validate-connection.js.map +1 -0
  354. package/lib/WABinary/constants.d.ts +25 -27
  355. package/lib/WABinary/constants.d.ts.map +1 -0
  356. package/lib/WABinary/constants.js +1451 -1292
  357. package/lib/WABinary/constants.js.bak +1373 -0
  358. package/lib/WABinary/constants.js.map +1 -0
  359. package/lib/WABinary/decode.d.ts +7 -9
  360. package/lib/WABinary/decode.d.ts.map +1 -0
  361. package/lib/WABinary/decode.js +163 -189
  362. package/lib/WABinary/decode.js.map +1 -0
  363. package/lib/WABinary/encode.d.ts +3 -3
  364. package/lib/WABinary/encode.d.ts.map +1 -0
  365. package/lib/WABinary/encode.js +110 -155
  366. package/lib/WABinary/encode.js.map +1 -0
  367. package/lib/WABinary/generic-utils.d.ts +14 -27
  368. package/lib/WABinary/generic-utils.d.ts.map +1 -0
  369. package/lib/WABinary/generic-utils.js +177 -105
  370. package/lib/WABinary/generic-utils.js.map +1 -0
  371. package/lib/WABinary/index.d.ts +6 -5
  372. package/lib/WABinary/index.d.ts.map +1 -0
  373. package/lib/WABinary/index.js +6 -25
  374. package/lib/WABinary/index.js.map +1 -0
  375. package/lib/WABinary/jid-utils.d.ts +41 -51
  376. package/lib/WABinary/jid-utils.d.ts.map +1 -0
  377. package/lib/WABinary/jid-utils.js +83 -120
  378. package/lib/WABinary/jid-utils.js.map +1 -0
  379. package/lib/WABinary/types.d.ts +10 -13
  380. package/lib/WABinary/types.d.ts.map +1 -0
  381. package/lib/WABinary/types.js +2 -3
  382. package/lib/WABinary/types.js.map +1 -0
  383. package/lib/WAM/BinaryInfo.d.ts +8 -15
  384. package/lib/WAM/BinaryInfo.d.ts.map +1 -0
  385. package/lib/WAM/BinaryInfo.js +7 -14
  386. package/lib/WAM/BinaryInfo.js.map +1 -0
  387. package/lib/WAM/constants.d.ts +30 -37
  388. package/lib/WAM/constants.d.ts.map +1 -0
  389. package/lib/WAM/constants.js +19193 -11711
  390. package/lib/WAM/constants.js.map +1 -0
  391. package/lib/WAM/encode.d.ts +3 -3
  392. package/lib/WAM/encode.d.ts.map +1 -0
  393. package/lib/WAM/encode.js +95 -109
  394. package/lib/WAM/encode.js.map +1 -0
  395. package/lib/WAM/index.d.ts +4 -3
  396. package/lib/WAM/index.d.ts.map +1 -0
  397. package/lib/WAM/index.js +4 -23
  398. package/lib/WAM/index.js.map +1 -0
  399. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -9
  400. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -0
  401. package/lib/WAUSync/Protocols/USyncContactProtocol.js +37 -21
  402. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -0
  403. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +19 -22
  404. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +1 -0
  405. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +18 -26
  406. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +1 -0
  407. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +11 -12
  408. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +1 -0
  409. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -20
  410. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +1 -0
  411. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +11 -12
  412. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +1 -0
  413. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +16 -24
  414. package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +1 -0
  415. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  416. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
  417. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  418. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
  419. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +26 -0
  420. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +1 -0
  421. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
  422. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +1 -0
  423. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +10 -0
  424. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +1 -0
  425. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
  426. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +1 -0
  427. package/lib/WAUSync/Protocols/index.d.ts +6 -6
  428. package/lib/WAUSync/Protocols/index.d.ts.map +1 -0
  429. package/lib/WAUSync/Protocols/index.js +6 -26
  430. package/lib/WAUSync/Protocols/index.js.map +1 -0
  431. package/lib/WAUSync/USyncQuery.d.ts +27 -28
  432. package/lib/WAUSync/USyncQuery.d.ts.map +1 -0
  433. package/lib/WAUSync/USyncQuery.js +68 -62
  434. package/lib/WAUSync/USyncQuery.js.map +1 -0
  435. package/lib/WAUSync/USyncUser.d.ts +15 -10
  436. package/lib/WAUSync/USyncUser.d.ts.map +1 -0
  437. package/lib/WAUSync/USyncUser.js +20 -19
  438. package/lib/WAUSync/USyncUser.js.map +1 -0
  439. package/lib/WAUSync/index.d.ts +4 -3
  440. package/lib/WAUSync/index.d.ts.map +1 -0
  441. package/lib/WAUSync/index.js +4 -23
  442. package/lib/WAUSync/index.js.map +1 -0
  443. package/lib/index.d.ts +11 -12
  444. package/lib/index.d.ts.map +1 -0
  445. package/lib/index.js +12 -39
  446. package/lib/index.js.map +1 -0
  447. package/package.json +86 -42
@@ -1,536 +1,386 @@
1
- "use strict"
2
-
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k
5
- var desc = Object.getOwnPropertyDescriptor(m, k)
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k] } }
8
- }
9
- Object.defineProperty(o, k2, desc)
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k
12
- o[k2] = m[k]
13
- }))
14
-
15
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
- Object.defineProperty(o, "default", { enumerable: true, value: v })
17
- }) : function(o, v) {
18
- o["default"] = v
19
- })
20
-
21
- var __importStar = (this && this.__importStar) || function (mod) {
22
- if (mod && mod.__esModule) return mod
23
- var result = {}
24
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k)
25
- __setModuleDefault(result, mod)
26
- return result
27
- }
28
-
29
- var __importDefault = (this && this.__importDefault) || function (mod) {
30
- return (mod && mod.__esModule) ? mod : { "default": mod }
31
- }
32
-
33
- Object.defineProperty(exports, "__esModule", { value: true })
34
-
35
- const boom_1 = require("@hapi/boom")
36
- const axios_1 = __importDefault(require("axios"))
37
- const child_process_1 = require("child_process")
38
- const Crypto = __importStar(require("crypto"))
39
- const events_1 = require("events")
40
- const fs_1 = require("fs")
41
- const os_1 = require("os")
42
- const path_1 = require("path")
43
- const stream_1 = require("stream")
44
- const WAProto_1 = require("../../WAProto")
45
- const Defaults_1 = require("../Defaults")
46
- const WABinary_1 = require("../WABinary")
47
- const crypto_1 = require("./crypto")
48
- const generics_1 = require("./generics")
49
-
50
- const getImageProcessingLibrary = async () => {
51
- const [_jimp, sharp] = await Promise.all([
52
- (async () => {
53
- const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }))
54
- return jimp
55
- })(),
56
- (async () => {
57
- const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }))
58
- return sharp
59
- })()
60
- ])
1
+ import { Boom } from '@hapi/boom';
2
+ import { exec } from 'child_process';
3
+ import * as Crypto from 'crypto';
4
+ import { once } from 'events';
5
+ import { createReadStream, createWriteStream, promises as fs, WriteStream } from 'fs';
6
+ import { tmpdir } from 'os';
7
+ import { join } from 'path';
8
+ import { Readable, Transform } from 'stream';
9
+ import { URL } from 'url';
10
+ import { proto } from '../../WAProto/index.js';
11
+ import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults/index.js';
12
+ import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
13
+ import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
14
+ import { generateMessageIDV2 } from './generics.js';
15
+ const getTmpFilesDirectory = () => tmpdir();
16
+ export const getImageProcessingLibrary = async () => {
17
+ //@ts-ignore
18
+ const [jimp, sharp] = await Promise.all([import('jimp').catch(() => { }), import('sharp').catch(() => { })]);
61
19
  if (sharp) {
62
- return { sharp }
20
+ return { sharp };
63
21
  }
64
- // Jimp v1 pakai named export (export { Jimp }), beda sama v0 yang default
65
- // export. Coba named export dulu, fallback ke .default buat jaga-jaga versi lama.
66
- const jimp = _jimp?.Jimp || _jimp?.default || _jimp
67
22
  if (jimp) {
68
- return { jimp, jimpMime: _jimp?.JimpMime }
69
- }
70
- throw new boom_1.Boom('No image processing library available')
71
- }
72
-
73
- const hkdfInfoKey = (type) => {
74
- const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type]
75
- return `WhatsApp ${hkdfInfo} Keys`
76
- }
77
-
78
- const getRawMediaUploadData = async (media, mediaType, logger) => {
79
- const { stream } = await getStream(media)
80
-
81
- logger?.debug('got stream for raw upload')
82
-
83
- const hasher = Crypto.createHash('sha256')
84
- const filePath = path_1.join(os_1.tmpdir(), mediaType + generics_1.generateMessageID())
85
- const fileWriteStream = fs_1.createWriteStream(filePath)
86
-
87
- let fileLength = 0
88
-
23
+ return { jimp };
24
+ }
25
+ throw new Boom('No image processing library available');
26
+ };
27
+ export const hkdfInfoKey = (type) => {
28
+ const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type];
29
+ return `WhatsApp ${hkdfInfo} Keys`;
30
+ };
31
+ export const getRawMediaUploadData = async (media, mediaType, logger) => {
32
+ const { stream } = await getStream(media);
33
+ logger?.debug('got stream for raw upload');
34
+ const hasher = Crypto.createHash('sha256');
35
+ const filePath = join(tmpdir(), mediaType + generateMessageIDV2());
36
+ const fileWriteStream = createWriteStream(filePath);
37
+ let fileLength = 0;
89
38
  try {
90
39
  for await (const data of stream) {
91
- fileLength += data.length
92
- hasher.update(data)
93
-
40
+ fileLength += data.length;
41
+ hasher.update(data);
94
42
  if (!fileWriteStream.write(data)) {
95
- await events_1.once(fileWriteStream, 'drain')
43
+ await once(fileWriteStream, 'drain');
96
44
  }
97
45
  }
98
-
99
- fileWriteStream.end()
100
- await events_1.once(fileWriteStream, 'finish')
101
- stream.destroy()
102
-
103
- const fileSha256 = hasher.digest()
104
-
105
- logger?.debug('hashed data for raw upload')
106
-
46
+ fileWriteStream.end();
47
+ await once(fileWriteStream, 'finish');
48
+ stream.destroy();
49
+ const fileSha256 = hasher.digest();
50
+ logger?.debug('hashed data for raw upload');
107
51
  return {
108
52
  filePath: filePath,
109
53
  fileSha256,
110
54
  fileLength
111
- }
55
+ };
112
56
  }
113
57
  catch (error) {
114
- fileWriteStream.destroy()
115
- stream.destroy()
116
-
58
+ fileWriteStream.destroy();
59
+ stream.destroy();
117
60
  try {
118
- await fs_1.promises.unlink(filePath)
61
+ await fs.unlink(filePath);
119
62
  }
120
63
  catch {
121
64
  //
122
65
  }
123
- throw error
66
+ throw error;
124
67
  }
125
- }
126
-
68
+ };
127
69
  /** generates all the keys required to encrypt/decrypt & sign a media message */
128
- async function getMediaKeys(buffer, mediaType) {
70
+ export async function getMediaKeys(buffer, mediaType) {
129
71
  if (!buffer) {
130
- throw new boom_1.Boom('Cannot derive from empty media key')
72
+ throw new Boom('Cannot derive from empty media key');
131
73
  }
132
74
  if (typeof buffer === 'string') {
133
- buffer = Buffer.from(buffer.replace('data:base64,', ''), 'base64')
75
+ buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
134
76
  }
135
77
  // expand using HKDF to 112 bytes, also pass in the relevant app info
136
- const expandedMediaKey = await crypto_1.hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
78
+ const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) });
137
79
  return {
138
80
  iv: expandedMediaKey.slice(0, 16),
139
81
  cipherKey: expandedMediaKey.slice(16, 48),
140
- macKey: expandedMediaKey.slice(48, 80),
141
- }
82
+ macKey: expandedMediaKey.slice(48, 80)
83
+ };
142
84
  }
143
-
144
85
  /** Extracts video thumb using FFMPEG */
145
- const extractVideoThumb = (videoPath, time = '00:00:00', size = { width: 320 }) => {
146
- return new Promise((resolve, reject) => {
147
- const args = [
148
- '-ss', time,
149
- '-i', videoPath,
150
- '-y',
151
- '-vf', `scale=${size.width}:-1`,
152
- '-vframes', '1',
153
- '-f', 'image2',
154
- '-vcodec', 'mjpeg',
155
- 'pipe:1'
156
- ]
157
-
158
- const ffmpeg = child_process_1.spawn('ffmpeg', args)
159
- const chunks = []
160
- let errorOutput = ''
161
-
162
- ffmpeg.stdout.on('data', chunk => chunks.push(chunk))
163
- ffmpeg.stderr.on('data', data => {
164
- errorOutput += data.toString()
165
- })
166
- ffmpeg.on('error', reject)
167
- ffmpeg.on('close', code => {
168
- if (code === 0) return resolve(Buffer.concat(chunks))
169
- reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`))
170
- })
171
- })
172
- }
173
-
174
- const extractImageThumb = async (bufferOrFilePath, width = 32, quality = 50) => {
175
- if (typeof bufferOrFilePath === "string" && bufferOrFilePath.startsWith("http")) {
176
- const response = await axios_1.default.get(bufferOrFilePath, { responseType: "arraybuffer" })
177
- bufferOrFilePath = Buffer.from(response.data)
178
- }
179
- if (bufferOrFilePath instanceof stream_1.Readable) {
180
- bufferOrFilePath = await toBuffer(bufferOrFilePath)
181
- }
182
- const lib = await getImageProcessingLibrary()
86
+ const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
87
+ const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
88
+ exec(cmd, err => {
89
+ if (err) {
90
+ reject(err);
91
+ }
92
+ else {
93
+ resolve();
94
+ }
95
+ });
96
+ });
97
+ export const extractImageThumb = async (bufferOrFilePath, width = 32) => {
98
+ // TODO: Move entirely to sharp, removing jimp as it supports readable streams
99
+ // This will have positive speed and performance impacts as well as minimizing RAM usage.
100
+ if (bufferOrFilePath instanceof Readable) {
101
+ bufferOrFilePath = await toBuffer(bufferOrFilePath);
102
+ }
103
+ const lib = await getImageProcessingLibrary();
183
104
  if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
184
- const img = lib.sharp.default(bufferOrFilePath)
185
- const dimensions = await img.metadata()
186
- const buffer = await img
187
- .resize({
188
- width,
189
- height: width,
190
- fit: 'contain',
191
- background: { r: 255, g: 255, b: 255, alpha: 0 }
192
- })
193
- .jpeg({ quality })
194
- .toBuffer()
105
+ const img = lib.sharp.default(bufferOrFilePath);
106
+ const dimensions = await img.metadata();
107
+ const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
195
108
  return {
196
109
  buffer,
197
110
  original: {
198
111
  width: dimensions.width,
199
- height: dimensions.height,
200
- },
201
- }
112
+ height: dimensions.height
113
+ }
114
+ };
202
115
  }
203
- else if ('jimp' in lib && typeof lib.jimp?.read === 'function') {
204
- const Jimp = lib.jimp
205
- const JimpMime = lib.jimpMime
206
- const jimp = await Jimp.read(bufferOrFilePath)
116
+ else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
117
+ const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
207
118
  const dimensions = {
208
119
  width: jimp.width,
209
120
  height: jimp.height
210
- }
121
+ };
211
122
  const buffer = await jimp
212
- .resize({ w: width })
213
- .getBuffer(JimpMime?.jpeg || 'image/jpeg', { quality })
123
+ .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
124
+ .getBuffer('image/jpeg', { quality: 50 });
214
125
  return {
215
126
  buffer,
216
127
  original: dimensions
217
- }
128
+ };
218
129
  }
219
130
  else {
220
- throw new boom_1.Boom('No image processing library available')
131
+ throw new Boom('No image processing library available');
221
132
  }
222
- }
223
-
224
- const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
225
- .replace(/\+/g, '-')
226
- .replace(/\//g, '_')
227
- .replace(/\=+$/, '')))
228
-
229
- const generateProfilePicture = async (mediaUpload) => {
230
- let bufferOrFilePath
133
+ };
134
+ export const encodeBase64EncodedStringForUpload = (b64) => encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''));
135
+ export const generateProfilePicture = async (mediaUpload, dimensions) => {
136
+ let buffer;
137
+ const { width: w = 640, height: h = 640 } = dimensions || {};
231
138
  if (Buffer.isBuffer(mediaUpload)) {
232
- bufferOrFilePath = mediaUpload
233
- }
234
- else if ('url' in mediaUpload) {
235
- bufferOrFilePath = mediaUpload.url.toString()
139
+ buffer = mediaUpload;
236
140
  }
237
141
  else {
238
- bufferOrFilePath = await toBuffer(mediaUpload.stream)
142
+ // Use getStream to handle all WAMediaUpload types (Buffer, Stream, URL)
143
+ const { stream } = await getStream(mediaUpload);
144
+ // Convert the resulting stream to a buffer
145
+ buffer = await toBuffer(stream);
239
146
  }
240
- const lib = await getImageProcessingLibrary()
241
- let img
147
+ const lib = await getImageProcessingLibrary();
148
+ let img;
242
149
  if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
243
- img = await lib.sharp.default(bufferOrFilePath)
244
- .resize(720, 720, {
245
- fit: 'inside',
150
+ img = lib.sharp
151
+ .default(buffer)
152
+ .resize(w, h)
153
+ .jpeg({
154
+ quality: 50
246
155
  })
247
- .jpeg({ quality: 50 })
248
- .toBuffer()
249
- }
250
- else if ('jimp' in lib && typeof lib.jimp?.read === 'function') {
251
- const Jimp = lib.jimp
252
- const JimpMime = lib.jimpMime
253
- const image = await Jimp.read(bufferOrFilePath)
254
- const min = image.width
255
- const max = image.height
256
- const cropped = image.crop({ x: 0, y: 0, w: min, h: max })
257
- img = await cropped.scaleToFit({ w: 720, h: 720 }).getBuffer(JimpMime?.jpeg || 'image/jpeg')
156
+ .toBuffer();
157
+ }
158
+ else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'function') {
159
+ const jimp = await lib.jimp.Jimp.read(buffer);
160
+ const min = Math.min(jimp.width, jimp.height);
161
+ const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min });
162
+ img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
258
163
  }
259
164
  else {
260
- throw new boom_1.Boom('No image processing library available')
165
+ throw new Boom('No image processing library available');
261
166
  }
262
167
  return {
263
- img: await img,
264
- }
265
- }
266
-
267
-
168
+ img: await img
169
+ };
170
+ };
268
171
  /** gets the SHA256 of the given media message */
269
- const mediaMessageSHA256B64 = (message) => {
270
- const media = Object.values(message)[0]
271
- return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64')
272
- }
273
-
274
- async function getAudioDuration(buffer) {
275
- const musicMetadata = await Promise.resolve().then(() => __importStar(require('music-metadata')))
172
+ export const mediaMessageSHA256B64 = (message) => {
173
+ const media = Object.values(message)[0];
174
+ return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
175
+ };
176
+ export async function getAudioDuration(buffer) {
177
+ const musicMetadata = await import('music-metadata');
178
+ let metadata;
276
179
  const options = {
277
- duration: true
278
- }
279
-
280
- let metadata
281
-
180
+ duration: true
181
+ };
282
182
  if (Buffer.isBuffer(buffer)) {
283
- metadata = await musicMetadata.parseBuffer(buffer, undefined, options)
183
+ metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
284
184
  }
285
185
  else if (typeof buffer === 'string') {
286
- metadata = await musicMetadata.parseFile(buffer, options)
186
+ metadata = await musicMetadata.parseFile(buffer, options);
287
187
  }
288
188
  else {
289
- metadata = await musicMetadata.parseStream(buffer, undefined, options)
189
+ metadata = await musicMetadata.parseStream(buffer, undefined, options);
290
190
  }
291
- return metadata.format.duration
191
+ return metadata.format.duration;
292
192
  }
293
-
294
193
  /**
295
194
  referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
296
195
  */
297
- async function getAudioWaveform(buffer, logger) {
196
+ export async function getAudioWaveform(buffer, logger) {
298
197
  try {
299
- const { default: decoder } = await eval('import(\'audio-decode\')')
300
- let audioData
198
+ // @ts-ignore
199
+ const { default: decoder } = await import('audio-decode');
200
+ let audioData;
301
201
  if (Buffer.isBuffer(buffer)) {
302
- audioData = buffer
202
+ audioData = buffer;
303
203
  }
304
204
  else if (typeof buffer === 'string') {
305
- const rStream = fs_1.createReadStream(buffer)
306
- audioData = await toBuffer(rStream)
205
+ const rStream = createReadStream(buffer);
206
+ audioData = await toBuffer(rStream);
307
207
  }
308
208
  else {
309
- audioData = await toBuffer(buffer)
209
+ audioData = await toBuffer(buffer);
310
210
  }
311
- const audioBuffer = await decoder(audioData)
312
- const rawData = audioBuffer.getChannelData(0) // We only need to work with one channel of data
313
- const samples = 64 // Number of samples we want to have in our final data set
314
- const blockSize = Math.floor(rawData.length / samples) // the number of samples in each subdivision
315
- const filteredData = []
211
+ const audioBuffer = await decoder(audioData);
212
+ const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
213
+ const samples = 64; // Number of samples we want to have in our final data set
214
+ const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
215
+ const filteredData = [];
316
216
  for (let i = 0; i < samples; i++) {
317
- const blockStart = blockSize * i // the location of the first sample in the block
318
- let sum = 0
217
+ const blockStart = blockSize * i; // the location of the first sample in the block
218
+ let sum = 0;
319
219
  for (let j = 0; j < blockSize; j++) {
320
- sum = sum + Math.abs(rawData[blockStart + j]) // find the sum of all the samples in the block
220
+ sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
321
221
  }
322
- filteredData.push(sum / blockSize) // divide the sum by the block size to get the average
222
+ filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
323
223
  }
324
224
  // This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
325
- const multiplier = Math.pow(Math.max(...filteredData), -1)
326
- const normalizedData = filteredData.map((n) => n * multiplier)
225
+ const multiplier = Math.pow(Math.max(...filteredData), -1);
226
+ const normalizedData = filteredData.map(n => n * multiplier);
327
227
  // Generate waveform like WhatsApp
328
- const waveform = new Uint8Array(normalizedData.map((n) => Math.floor(100 * n)))
329
- return waveform
228
+ const waveform = new Uint8Array(normalizedData.map(n => Math.floor(100 * n)));
229
+ return waveform;
330
230
  }
331
231
  catch (e) {
332
- logger?.debug('Failed to generate waveform: ' + e)
232
+ logger?.debug('Failed to generate waveform: ' + e);
333
233
  }
334
234
  }
335
-
336
- const toReadable = (buffer) => {
337
- const readable = new stream_1.Readable({ read: () => { } })
338
- readable.push(buffer)
339
- readable.push(null)
340
- return readable
341
- }
342
-
343
- const toBuffer = async (stream) => {
344
- const chunks = []
235
+ export const toReadable = (buffer) => {
236
+ const readable = new Readable({ read: () => { } });
237
+ readable.push(buffer);
238
+ readable.push(null);
239
+ return readable;
240
+ };
241
+ export const toBuffer = async (stream) => {
242
+ const chunks = [];
345
243
  for await (const chunk of stream) {
346
- chunks.push(chunk)
244
+ chunks.push(chunk);
347
245
  }
348
- stream.destroy()
349
- return Buffer.concat(chunks)
350
- }
351
-
352
- const getStream = async (item, opts) => {
246
+ stream.destroy();
247
+ return Buffer.concat(chunks);
248
+ };
249
+ export const getStream = async (item, opts) => {
353
250
  if (Buffer.isBuffer(item)) {
354
- return { stream: toReadable(item), type: 'buffer' }
251
+ return { stream: toReadable(item), type: 'buffer' };
355
252
  }
356
-
357
253
  if ('stream' in item) {
358
- return { stream: item.stream, type: 'readable' }
254
+ return { stream: item.stream, type: 'readable' };
359
255
  }
360
-
361
- const urlStr = item.url.toString()
362
-
256
+ const urlStr = item.url.toString();
363
257
  if (urlStr.startsWith('data:')) {
364
- const buffer = Buffer.from(urlStr.split(',')[1], 'base64')
365
- return { stream: await toReadable(buffer), type: 'buffer' }
258
+ const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
259
+ return { stream: toReadable(buffer), type: 'buffer' };
366
260
  }
367
-
368
261
  if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
369
- return { stream: await getHttpStream(item.url, opts), type: 'remote' }
262
+ return { stream: await getHttpStream(item.url, opts), type: 'remote' };
370
263
  }
371
-
372
- return { stream: fs_1.createReadStream(item.url), type: 'file' }
373
- }
374
-
264
+ return { stream: createReadStream(item.url), type: 'file' };
265
+ };
375
266
  /** generates a thumbnail for a given media, if required */
376
- async function generateThumbnail(file, mediaType, options) {
377
- let thumbnail
378
- let originalImageDimensions
267
+ export async function generateThumbnail(file, mediaType, options) {
268
+ let thumbnail;
269
+ let originalImageDimensions;
379
270
  if (mediaType === 'image') {
380
- const { buffer, original } = await extractImageThumb(file, 256, 95)
381
- thumbnail = buffer.toString('base64')
271
+ const { buffer, original } = await extractImageThumb(file);
272
+ thumbnail = buffer.toString('base64');
382
273
  if (original.width && original.height) {
383
274
  originalImageDimensions = {
384
275
  width: original.width,
385
- height: original.height,
386
- }
276
+ height: original.height
277
+ };
387
278
  }
388
279
  }
389
280
  else if (mediaType === 'video') {
281
+ const imgFilename = join(getTmpFilesDirectory(), generateMessageIDV2() + '.jpg');
390
282
  try {
391
- const buff = await extractVideoThumb(file, '00:00:00', { width: 32, height: 32 })
392
- thumbnail = buff.toString('base64')
283
+ await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
284
+ const buff = await fs.readFile(imgFilename);
285
+ thumbnail = buff.toString('base64');
286
+ await fs.unlink(imgFilename);
393
287
  }
394
288
  catch (err) {
395
- options?.logger?.debug('could not generate video thumb: ' + err)
289
+ options.logger?.debug('could not generate video thumb: ' + err);
396
290
  }
397
291
  }
398
292
  return {
399
293
  thumbnail,
400
294
  originalImageDimensions
401
- }
402
- }
403
-
404
- const getHttpStream = async (url, options = {}) => {
405
- const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' })
406
- return fetched.data
295
+ };
407
296
  }
408
-
409
- const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
410
- const { stream, type } = await getStream(media, opts)
411
- logger?.debug('fetched media stream')
412
-
413
- const encFilePath = path_1.join(os_1.tmpdir(), mediaType + generics_1.generateMessageID() + '-plain')
414
- const encFileWriteStream = fs_1.createWriteStream(encFilePath)
415
-
416
- let originalFilePath
417
- let originalFileStream
418
-
419
- if (type === 'file') {
420
- originalFilePath = media.url.toString()
421
- } else if (saveOriginalFileIfRequired) {
422
- originalFilePath = path_1.join(os_1.tmpdir(), mediaType + generics_1.generateMessageID() + '-original')
423
- originalFileStream = fs_1.createWriteStream(originalFilePath)
424
- }
425
-
426
- let fileLength = 0
427
- const sha256 = Crypto.createHash('sha256')
428
-
297
+ export const getHttpStream = async (url, options = {}) => {
298
+ const urlStr = url.toString();
299
+ // Auto-inject Referer for domains that require it (e.g. tikwm.com)
300
+ let autoHeaders = {};
429
301
  try {
430
- for await (const data of stream) {
431
- fileLength += data.length
432
-
433
- if (type === 'remote'
434
- && opts?.maxContentLength
435
- && fileLength + data.length > opts.maxContentLength) {
436
- throw new boom_1.Boom(`content length exceeded when preparing "${type}"`, {
437
- data: { media, type }
438
- })
439
- }
440
-
441
- sha256.update(data)
442
- encFileWriteStream.write(data)
443
-
444
- if (originalFileStream && !originalFileStream.write(data)) {
445
- await events_1.once(originalFileStream, 'drain')
446
- }
447
- }
448
-
449
- const fileSha256 = sha256.digest()
450
- encFileWriteStream.end()
451
- originalFileStream?.end?.call(originalFileStream)
452
- stream.destroy()
453
-
454
- logger?.debug('prepared plain stream successfully')
455
-
456
- return {
457
- mediaKey: undefined,
458
- originalFilePath,
459
- encFilePath,
460
- mac: undefined,
461
- fileEncSha256: undefined,
462
- fileSha256,
463
- fileLength
464
- }
465
- }
466
- catch (error) {
467
- encFileWriteStream.destroy()
468
- originalFileStream?.destroy?.call(originalFileStream)
469
- sha256.destroy()
470
- stream.destroy()
471
- try {
472
- await fs_1.promises.unlink(encFilePath)
473
- if (originalFilePath && didSaveToTmpPath) {
474
- await fs_1.promises.unlink(originalFilePath)
475
- }
476
- } catch (err) {
477
- logger?.error({ err }, 'failed deleting tmp files')
478
- }
479
- throw error
480
- }
481
- }
482
-
483
- const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
484
- const { stream, type } = await getStream(media, opts)
485
- logger?.debug('fetched media stream')
486
- const mediaKey = Crypto.randomBytes(32)
487
- const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
488
- const encFilePath = path_1.join(os_1.tmpdir(), mediaType + generics_1.generateMessageID() + '-enc')
489
- const encFileWriteStream = fs_1.createWriteStream(encFilePath)
490
- let originalFileStream
491
- let originalFilePath
302
+ const parsedUrl = new URL(urlStr);
303
+ const origin = parsedUrl.origin;
304
+ autoHeaders = {
305
+ 'Referer': origin + '/',
306
+ 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Mobile Safari/537.36'
307
+ };
308
+ } catch (_) {}
309
+ const mergedHeaders = {
310
+ ...autoHeaders,
311
+ ...(options.headers || {})
312
+ };
313
+ const response = await fetch(urlStr, {
314
+ dispatcher: options.dispatcher,
315
+ method: 'GET',
316
+ headers: mergedHeaders
317
+ });
318
+ if (!response.ok) {
319
+ throw new Boom(`Failed to fetch stream from ${url}`, { statusCode: response.status, data: { url } });
320
+ }
321
+ // @ts-ignore Node18+ Readable.fromWeb exists
322
+ return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
323
+ };
324
+ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
325
+ const { stream, type } = await getStream(media, opts);
326
+ logger?.debug('fetched media stream');
327
+ const mediaKey = Crypto.randomBytes(32);
328
+ const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
329
+ const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
330
+ const encFileWriteStream = createWriteStream(encFilePath);
331
+ let originalFileStream;
332
+ let originalFilePath;
492
333
  if (saveOriginalFileIfRequired) {
493
- originalFilePath = path_1.join(os_1.tmpdir(), mediaType + generics_1.generateMessageID() + '-original')
494
- originalFileStream = fs_1.createWriteStream(originalFilePath)
495
- }
496
- let fileLength = 0
497
- const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
498
- const hmac = Crypto.createHmac('sha256', macKey).update(iv)
499
- const sha256Plain = Crypto.createHash('sha256')
500
- const sha256Enc = Crypto.createHash('sha256')
501
- const onChunk = (buff) => {
502
- sha256Enc.update(buff)
503
- hmac.update(buff)
504
- encFileWriteStream.write(buff)
505
- }
334
+ originalFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-original');
335
+ originalFileStream = createWriteStream(originalFilePath);
336
+ }
337
+ let fileLength = 0;
338
+ const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
339
+ const hmac = Crypto.createHmac('sha256', macKey).update(iv);
340
+ const sha256Plain = Crypto.createHash('sha256');
341
+ const sha256Enc = Crypto.createHash('sha256');
342
+ const onChunk = async (buff) => {
343
+ sha256Enc.update(buff);
344
+ hmac.update(buff);
345
+ // Handle backpressure: if write returns false, wait for drain
346
+ if (!encFileWriteStream.write(buff)) {
347
+ await once(encFileWriteStream, 'drain');
348
+ }
349
+ };
506
350
  try {
507
351
  for await (const data of stream) {
508
- fileLength += data.length
509
- if (type === 'remote'
510
- && opts?.maxContentLength
511
- && fileLength + data.length > opts.maxContentLength) {
512
- throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
352
+ fileLength += data.length;
353
+ if (type === 'remote' &&
354
+ opts?.maxContentLength &&
355
+ fileLength + data.length > opts.maxContentLength) {
356
+ throw new Boom(`content length exceeded when encrypting "${type}"`, {
513
357
  data: { media, type }
514
- })
358
+ });
515
359
  }
516
360
  if (originalFileStream) {
517
361
  if (!originalFileStream.write(data)) {
518
- await events_1.once(originalFileStream, 'drain')
362
+ await once(originalFileStream, 'drain');
519
363
  }
520
364
  }
521
- sha256Plain.update(data)
522
- onChunk(aes.update(data))
365
+ sha256Plain.update(data);
366
+ await onChunk(aes.update(data));
523
367
  }
524
- onChunk(aes.final())
525
- const mac = hmac.digest().slice(0, 10)
526
- sha256Enc.update(mac)
527
- const fileSha256 = sha256Plain.digest()
528
- const fileEncSha256 = sha256Enc.digest()
529
- encFileWriteStream.write(mac)
530
- encFileWriteStream.end()
531
- originalFileStream?.end?.call(originalFileStream)
532
- stream.destroy()
533
- logger?.debug('encrypted data successfully')
368
+ await onChunk(aes.final());
369
+ const mac = hmac.digest().slice(0, 10);
370
+ sha256Enc.update(mac);
371
+ const fileSha256 = sha256Plain.digest();
372
+ const fileEncSha256 = sha256Enc.digest();
373
+ encFileWriteStream.write(mac);
374
+ const encFinishPromise = once(encFileWriteStream, 'finish');
375
+ const originalFinishPromise = originalFileStream ? once(originalFileStream, 'finish') : Promise.resolve();
376
+ encFileWriteStream.end();
377
+ originalFileStream?.end?.();
378
+ stream.destroy();
379
+ // Wait for write streams to fully flush to disk
380
+ // This helps reduce memory pressure by allowing OS to release buffers
381
+ await encFinishPromise;
382
+ await originalFinishPromise;
383
+ logger?.debug('encrypted data successfully');
534
384
  return {
535
385
  mediaKey,
536
386
  originalFilePath,
@@ -539,235 +389,345 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
539
389
  fileEncSha256,
540
390
  fileSha256,
541
391
  fileLength
542
- }
392
+ };
543
393
  }
544
394
  catch (error) {
545
395
  // destroy all streams with error
546
- encFileWriteStream.destroy()
547
- originalFileStream?.destroy?.call(originalFileStream)
548
- aes.destroy()
549
- hmac.destroy()
550
- sha256Plain.destroy()
551
- sha256Enc.destroy()
552
- stream.destroy()
396
+ encFileWriteStream.destroy();
397
+ originalFileStream?.destroy?.();
398
+ aes.destroy();
399
+ hmac.destroy();
400
+ sha256Plain.destroy();
401
+ sha256Enc.destroy();
402
+ stream.destroy();
553
403
  try {
554
- await fs_1.promises.unlink(encFilePath)
404
+ await fs.unlink(encFilePath);
555
405
  if (originalFilePath) {
556
- await fs_1.promises.unlink(originalFilePath)
406
+ await fs.unlink(originalFilePath);
557
407
  }
558
408
  }
559
409
  catch (err) {
560
- logger?.error({ err }, 'failed deleting tmp files')
410
+ logger?.error({ err }, 'failed deleting tmp files');
561
411
  }
562
- throw error
412
+ throw error;
563
413
  }
564
- }
565
-
566
- const DEF_HOST = 'mmg.whatsapp.net'
567
-
568
- const AES_CHUNK_SIZE = 16
569
-
414
+ };
415
+ export const DEF_MEDIA_HOST = 'mmg.whatsapp.net';
416
+ const AES_CHUNK_SIZE = 16;
570
417
  const toSmallestChunkSize = (num) => {
571
- return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE
572
- }
573
- const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`
574
-
575
- const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
576
- const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/')
577
- const downloadUrl = isValidMediaUrl ? url : getUrlFromDirectPath(directPath)
578
-
418
+ return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
419
+ };
420
+ export const getUrlFromDirectPath = (directPath, host = DEF_MEDIA_HOST) => `https://${host}${directPath}`;
421
+ const extractHost = (url) => {
422
+ if (!url)
423
+ return undefined;
424
+ try {
425
+ return new URL(url).host;
426
+ }
427
+ catch {
428
+ return undefined;
429
+ }
430
+ };
431
+ export const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
432
+ // Fallback host: explicit opt > host parsed from `url` > DEF_MEDIA_HOST.
433
+ // Lets us honor a non-default host carried by the proto without forcing callers to thread it through.
434
+ const fallbackHost = opts.host ?? extractHost(url);
435
+ const downloadUrl = directPath ? getUrlFromDirectPath(directPath, fallbackHost) : url;
579
436
  if (!downloadUrl) {
580
- throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 })
437
+ throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 });
581
438
  }
582
-
583
- const keys = await getMediaKeys(mediaKey, type)
584
- return downloadEncryptedContent(downloadUrl, keys, opts)
585
- }
586
-
439
+ const keys = await getMediaKeys(mediaKey, type);
440
+ return downloadEncryptedContent(downloadUrl, keys, opts);
441
+ };
587
442
  /**
588
443
  * Decrypts and downloads an AES256-CBC encrypted file given the keys.
589
444
  * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
590
445
  * */
591
- const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
592
- let bytesFetched = 0
593
- let startChunk = 0
594
- let firstBlockIsIV = false
446
+ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
447
+ let bytesFetched = 0;
448
+ let startChunk = 0;
449
+ let firstBlockIsIV = false;
595
450
  // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
596
451
  if (startByte) {
597
- const chunk = toSmallestChunkSize(startByte || 0)
452
+ const chunk = toSmallestChunkSize(startByte || 0);
598
453
  if (chunk) {
599
- startChunk = chunk - AES_CHUNK_SIZE
600
- bytesFetched = chunk
601
- firstBlockIsIV = true
454
+ startChunk = chunk - AES_CHUNK_SIZE;
455
+ bytesFetched = chunk;
456
+ firstBlockIsIV = true;
602
457
  }
603
458
  }
604
- const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined
459
+ const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
460
+ const headersInit = options?.headers ? options.headers : undefined;
605
461
  const headers = {
606
- ...(options?.headers) || {},
607
- Origin: Defaults_1.DEFAULT_ORIGIN,
608
- }
462
+ ...(headersInit
463
+ ? Array.isArray(headersInit)
464
+ ? Object.fromEntries(headersInit)
465
+ : headersInit
466
+ : {}),
467
+ Origin: DEFAULT_ORIGIN
468
+ };
609
469
  if (startChunk || endChunk) {
610
- headers.Range = `bytes=${startChunk}-`
470
+ headers.Range = `bytes=${startChunk}-`;
611
471
  if (endChunk) {
612
- headers.Range += endChunk
472
+ headers.Range += endChunk;
613
473
  }
614
474
  }
615
475
  // download the message
616
476
  const fetched = await getHttpStream(downloadUrl, {
617
- ...options || {},
618
- headers,
619
- maxBodyLength: Infinity,
620
- maxContentLength: Infinity,
621
- })
622
- let remainingBytes = Buffer.from([])
623
- let aes
477
+ ...(options || {}),
478
+ headers
479
+ });
480
+ let remainingBytes = Buffer.from([]);
481
+ let aes;
624
482
  const pushBytes = (bytes, push) => {
625
483
  if (startByte || endByte) {
626
- const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
627
- const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0)
628
- push(bytes.slice(start, end))
629
- bytesFetched += bytes.length
484
+ const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0);
485
+ const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0);
486
+ push(bytes.slice(start, end));
487
+ bytesFetched += bytes.length;
630
488
  }
631
489
  else {
632
- push(bytes)
490
+ push(bytes);
633
491
  }
634
- }
635
- const output = new stream_1.Transform({
492
+ };
493
+ const output = new Transform({
636
494
  transform(chunk, _, callback) {
637
- let data = Buffer.concat([remainingBytes, chunk])
638
- const decryptLength = toSmallestChunkSize(data.length)
639
- remainingBytes = data.slice(decryptLength)
640
- data = data.slice(0, decryptLength)
495
+ let data = remainingBytes.length ? Buffer.concat([remainingBytes, chunk]) : chunk;
496
+ const decryptLength = toSmallestChunkSize(data.length);
497
+ remainingBytes = data.slice(decryptLength);
498
+ data = data.slice(0, decryptLength);
641
499
  if (!aes) {
642
- let ivValue = iv
500
+ let ivValue = iv;
643
501
  if (firstBlockIsIV) {
644
- ivValue = data.slice(0, AES_CHUNK_SIZE)
645
- data = data.slice(AES_CHUNK_SIZE)
502
+ ivValue = data.slice(0, AES_CHUNK_SIZE);
503
+ data = data.slice(AES_CHUNK_SIZE);
646
504
  }
647
- aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue)
505
+ aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
648
506
  // if an end byte that is not EOF is specified
649
507
  // stop auto padding (PKCS7) -- otherwise throws an error for decryption
650
508
  if (endByte) {
651
- aes.setAutoPadding(false)
509
+ aes.setAutoPadding(false);
652
510
  }
653
511
  }
654
512
  try {
655
- pushBytes(aes.update(data), b => this.push(b))
656
- callback()
513
+ pushBytes(aes.update(data), b => this.push(b));
514
+ callback();
657
515
  }
658
516
  catch (error) {
659
- callback(error)
517
+ callback(error);
660
518
  }
661
519
  },
662
520
  final(callback) {
663
521
  try {
664
- pushBytes(aes.final(), b => this.push(b))
665
- callback()
522
+ pushBytes(aes.final(), b => this.push(b));
523
+ callback();
666
524
  }
667
525
  catch (error) {
668
- callback(error)
526
+ callback(error);
669
527
  }
670
- },
671
- })
672
- return fetched.pipe(output, { end: true })
673
- }
674
-
675
- function extensionForMediaMessage(message) {
676
- const getExtension = (mimetype) => mimetype.split('')[0].split('/')[1]
677
- const type = Object.keys(message)[0]
678
- let extension
679
- if (type === 'locationMessage' ||
680
- type === 'liveLocationMessage' ||
681
- type === 'productMessage') {
682
- extension = '.jpeg'
528
+ }
529
+ });
530
+ return fetched.pipe(output, { end: true });
531
+ };
532
+ export function extensionForMediaMessage(message) {
533
+ const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
534
+ const type = Object.keys(message)[0];
535
+ let extension;
536
+ if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
537
+ extension = '.jpeg';
683
538
  }
684
539
  else {
685
- const messageContent = message[type]
686
- extension = getExtension(messageContent.mimetype)
540
+ const messageContent = message[type];
541
+ extension = getExtension(messageContent.mimetype);
687
542
  }
688
- return extension
543
+ return extension;
689
544
  }
690
-
691
- const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
692
- return async (filePath, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
545
+ const isNodeRuntime = () => {
546
+ return (typeof process !== 'undefined' &&
547
+ process.versions?.node !== null &&
548
+ typeof process.versions.bun === 'undefined' &&
549
+ typeof globalThis.Deno === 'undefined');
550
+ };
551
+ export const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, agent }, redirectCount = 0) => {
552
+ if (redirectCount > 5) {
553
+ throw new Error('Too many redirects');
554
+ }
555
+ const parsedUrl = new URL(url);
556
+ const httpModule = parsedUrl.protocol === 'https:' ? await import('https') : await import('http');
557
+ // Get file size for Content-Length header (required for Node.js streaming)
558
+ const fileStats = await fs.stat(filePath);
559
+ const fileSize = fileStats.size;
560
+ return new Promise((resolve, reject) => {
561
+ const req = httpModule.request({
562
+ hostname: parsedUrl.hostname,
563
+ port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
564
+ path: parsedUrl.pathname + parsedUrl.search,
565
+ method: 'POST',
566
+ headers: {
567
+ ...headers,
568
+ 'Content-Length': fileSize
569
+ },
570
+ agent,
571
+ timeout: timeoutMs
572
+ }, res => {
573
+ // Handle redirects (3xx)
574
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
575
+ res.resume(); // Consume response to free resources
576
+ const newUrl = new URL(res.headers.location, url).toString();
577
+ resolve(uploadWithNodeHttp({
578
+ url: newUrl,
579
+ filePath,
580
+ headers,
581
+ timeoutMs,
582
+ agent
583
+ }, redirectCount + 1));
584
+ return;
585
+ }
586
+ let body = '';
587
+ res.on('data', chunk => (body += chunk));
588
+ res.on('end', () => {
589
+ try {
590
+ resolve(JSON.parse(body));
591
+ }
592
+ catch {
593
+ resolve(undefined);
594
+ }
595
+ });
596
+ });
597
+ req.on('error', reject);
598
+ req.on('timeout', () => {
599
+ req.destroy();
600
+ reject(new Error('Upload timeout'));
601
+ });
602
+ const stream = createReadStream(filePath);
603
+ stream.pipe(req);
604
+ stream.on('error', err => {
605
+ req.destroy();
606
+ reject(err);
607
+ });
608
+ });
609
+ };
610
+ const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) => {
611
+ // Convert Node.js Readable to Web ReadableStream
612
+ const nodeStream = createReadStream(filePath);
613
+ const webStream = Readable.toWeb(nodeStream);
614
+ const response = await fetch(url, {
615
+ dispatcher: agent,
616
+ method: 'POST',
617
+ body: webStream,
618
+ headers,
619
+ duplex: 'half',
620
+ signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
621
+ });
622
+ try {
623
+ return (await response.json());
624
+ }
625
+ catch {
626
+ return undefined;
627
+ }
628
+ };
629
+ /**
630
+ * Uploads media to WhatsApp servers.
631
+ *
632
+ * ## Why we have two upload implementations:
633
+ *
634
+ * Node.js's native `fetch` (powered by undici) has a known bug where it buffers
635
+ * the entire request body in memory before sending, even when using streams.
636
+ * This causes memory issues with large files (e.g., 1GB file = 1GB+ memory usage).
637
+ * See: https://github.com/nodejs/undici/issues/4058
638
+ *
639
+ * Other runtimes (Bun, Deno, browsers) correctly stream the request body without
640
+ * buffering, so we can use the web-standard Fetch API there.
641
+ *
642
+ * ## Future considerations:
643
+ * Once the undici bug is fixed, we can simplify this to use only the Fetch API
644
+ * across all runtimes. Monitor the GitHub issue for updates.
645
+ */
646
+ const uploadMedia = async (params, logger) => {
647
+ if (isNodeRuntime()) {
648
+ logger?.debug('Using Node.js https module for upload (avoids undici buffering bug)');
649
+ return uploadWithNodeHttp(params);
650
+ }
651
+ else {
652
+ logger?.debug('Using web-standard Fetch API for upload');
653
+ return uploadWithFetch(params);
654
+ }
655
+ };
656
+ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
657
+ return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
693
658
  // send a query JSON to obtain the url & auth token to upload our media
694
- let uploadInfo = await refreshMediaConn(false)
695
- let urls
696
- let media = Defaults_1.MEDIA_PATH_MAP[mediaType]
697
- const hosts = [...customUploadHosts, ...uploadInfo.hosts]
698
- fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
699
- if (newsletter) {
700
- media = media?.replace('/mms/', '/newsletter/newsletter-')
701
- }
659
+ let uploadInfo = await refreshMediaConn(false);
660
+ let urls;
661
+ const hosts = [...customUploadHosts, ...uploadInfo.hosts];
662
+ fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64);
663
+ // Prepare common headers
664
+ const customHeaders = (() => {
665
+ const hdrs = options?.headers;
666
+ if (!hdrs)
667
+ return {};
668
+ return Array.isArray(hdrs) ? Object.fromEntries(hdrs) : hdrs;
669
+ })();
670
+ const headers = {
671
+ ...customHeaders,
672
+ 'Content-Type': 'application/octet-stream',
673
+ Origin: DEFAULT_ORIGIN
674
+ };
702
675
  for (const { hostname } of hosts) {
703
- logger.debug(`uploading to "${hostname}"`)
704
- const auth = encodeURIComponent(uploadInfo.auth) // the auth token
705
- const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
706
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
707
- let result
676
+ logger.debug(`uploading to "${hostname}"`);
677
+ const auth = encodeURIComponent(uploadInfo.auth);
678
+ const url = `https://${hostname}${MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
679
+ let result;
708
680
  try {
709
- const body = await axios_1.default.post(url, fs_1.createReadStream(filePath), {
710
- ...options,
711
- maxRedirects: 0,
712
- headers: {
713
- ...options.headers || {},
714
- 'Content-Type': 'application/octet-stream',
715
- 'Origin': Defaults_1.DEFAULT_ORIGIN
716
- },
717
- httpsAgent: fetchAgent,
718
- timeout: timeoutMs,
719
- responseType: 'json',
720
- maxBodyLength: Infinity,
721
- maxContentLength: Infinity,
722
- })
723
- result = body.data
724
- if (result?.url || result?.directPath) {
681
+ result = await uploadMedia({
682
+ url,
683
+ filePath,
684
+ headers,
685
+ timeoutMs,
686
+ agent: fetchAgent
687
+ }, logger);
688
+ if (result?.url || result?.direct_path) {
725
689
  urls = {
726
690
  mediaUrl: result.url,
727
- directPath: result.direct_path,
691
+ directPath: result.direct_path,
728
692
  meta_hmac: result.meta_hmac,
729
693
  fbid: result.fbid,
730
694
  ts: result.ts
731
- }
732
- break
695
+ };
696
+ break;
733
697
  }
734
698
  else {
735
- uploadInfo = await refreshMediaConn(true)
736
- throw new Error(`upload failed, reason: ${JSON.stringify(result)}`)
699
+ uploadInfo = await refreshMediaConn(true);
700
+ throw new Error(`upload failed, reason: ${JSON.stringify(result)}`);
737
701
  }
738
702
  }
739
703
  catch (error) {
740
- if (axios_1.default.isAxiosError(error)) {
741
- result = error.response?.data
742
- }
743
- const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname
744
- logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`)
704
+ const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
705
+ logger.warn({ trace: error?.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
745
706
  }
746
707
  }
747
708
  if (!urls) {
748
- throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 })
709
+ throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
749
710
  }
750
- return urls
751
- }
752
- }
753
-
711
+ return urls;
712
+ };
713
+ };
754
714
  const getMediaRetryKey = (mediaKey) => {
755
- return crypto_1.hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' })
756
- }
715
+ return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
716
+ };
757
717
  /**
758
718
  * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
759
719
  */
760
- const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
761
- const recp = { stanzaId: key.id }
762
- const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish()
763
- const iv = Crypto.randomBytes(12)
764
- const retryKey = await getMediaRetryKey(mediaKey)
765
- const ciphertext = crypto_1.aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id))
720
+ export const encryptMediaRetryRequest = (key, mediaKey, meId) => {
721
+ const recp = { stanzaId: key.id };
722
+ const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish();
723
+ const iv = Crypto.randomBytes(12);
724
+ const retryKey = getMediaRetryKey(mediaKey);
725
+ const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id));
766
726
  const req = {
767
727
  tag: 'receipt',
768
728
  attrs: {
769
729
  id: key.id,
770
- to: WABinary_1.jidNormalizedUser(meId),
730
+ to: jidNormalizedUser(meId),
771
731
  type: 'server-error'
772
732
  },
773
733
  content: [
@@ -786,18 +746,17 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
786
746
  tag: 'rmr',
787
747
  attrs: {
788
748
  jid: key.remoteJid,
789
- 'from_me': (!!key.fromMe).toString(),
749
+ from_me: (!!key.fromMe).toString(),
790
750
  // @ts-ignore
791
751
  participant: key.participant || undefined
792
752
  }
793
753
  }
794
754
  ]
795
- }
796
- return req
797
- }
798
-
799
- const decodeMediaRetryNode = (node) => {
800
- const rmrNode = WABinary_1.getBinaryNodeChild(node, 'rmr')
755
+ };
756
+ return req;
757
+ };
758
+ export const decodeMediaRetryNode = (node) => {
759
+ const rmrNode = getBinaryNodeChild(node, 'rmr');
801
760
  const event = {
802
761
  key: {
803
762
  id: node.attrs.id,
@@ -805,68 +764,38 @@ const decodeMediaRetryNode = (node) => {
805
764
  fromMe: rmrNode.attrs.from_me === 'true',
806
765
  participant: rmrNode.attrs.participant
807
766
  }
808
- }
809
- const errorNode = WABinary_1.getBinaryNodeChild(node, 'error')
767
+ };
768
+ const errorNode = getBinaryNodeChild(node, 'error');
810
769
  if (errorNode) {
811
- const errorCode = +errorNode.attrs.code
812
- event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: getStatusCodeForMediaRetry(errorCode) })
770
+ const errorCode = +errorNode.attrs.code;
771
+ event.error = new Boom(`Failed to re-upload media (${errorCode})`, {
772
+ data: errorNode.attrs,
773
+ statusCode: getStatusCodeForMediaRetry(errorCode)
774
+ });
813
775
  }
814
776
  else {
815
- const encryptedInfoNode = WABinary_1.getBinaryNodeChild(node, 'encrypt')
816
- const ciphertext = WABinary_1.getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p')
817
- const iv = WABinary_1.getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv')
777
+ const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt');
778
+ const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p');
779
+ const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv');
818
780
  if (ciphertext && iv) {
819
- event.media = { ciphertext, iv }
781
+ event.media = { ciphertext, iv };
820
782
  }
821
783
  else {
822
- event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 })
784
+ event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
823
785
  }
824
786
  }
825
- return event
826
- }
827
-
828
- const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
829
- const retryKey = await getMediaRetryKey(mediaKey)
830
- const plaintext = crypto_1.aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId))
831
- return WAProto_1.proto.MediaRetryNotification.decode(plaintext)
832
- }
833
-
834
- const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code]
835
-
787
+ return event;
788
+ };
789
+ export const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
790
+ const retryKey = getMediaRetryKey(mediaKey);
791
+ const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId));
792
+ return proto.MediaRetryNotification.decode(plaintext);
793
+ };
794
+ export const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
836
795
  const MEDIA_RETRY_STATUS_MAP = {
837
- [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
838
- [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
839
- [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
840
- [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
841
- }
842
-
843
- module.exports = {
844
- hkdfInfoKey,
845
- getMediaKeys,
846
- extractVideoThumb,
847
- extractImageThumb,
848
- encodeBase64EncodedStringForUpload,
849
- generateProfilePicture,
850
- mediaMessageSHA256B64,
851
- getAudioDuration,
852
- getAudioWaveform,
853
- toReadable,
854
- toBuffer,
855
- getStream,
856
- generateThumbnail,
857
- getHttpStream,
858
- prepareStream,
859
- encryptedStream,
860
- getUrlFromDirectPath,
861
- downloadContentFromMessage,
862
- downloadEncryptedContent,
863
- extensionForMediaMessage,
864
- getRawMediaUploadData,
865
- getWAUploadToServer,
866
- getMediaRetryKey,
867
- encryptMediaRetryRequest,
868
- decodeMediaRetryNode,
869
- decryptMediaRetryData,
870
- getStatusCodeForMediaRetry,
871
- MEDIA_RETRY_STATUS_MAP
872
- }
796
+ [proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
797
+ [proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
798
+ [proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
799
+ [proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
800
+ };
801
+ //# sourceMappingURL=messages-media.js.map