@sixcore/baileys 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.
Files changed (278) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +606 -0
  3. package/WAProto/GenerateStatics.sh +4 -0
  4. package/WAProto/WAProto.proto +4357 -0
  5. package/WAProto/index.d.ts +50383 -0
  6. package/WAProto/index.js +155693 -0
  7. package/WASignalGroup/GroupProtocol.js +1697 -0
  8. package/WASignalGroup/ciphertext_message.js +16 -0
  9. package/WASignalGroup/generate-proto.sh +1 -0
  10. package/WASignalGroup/group.proto +42 -0
  11. package/WASignalGroup/group_cipher.js +120 -0
  12. package/WASignalGroup/group_session_builder.js +46 -0
  13. package/WASignalGroup/index.js +5 -0
  14. package/WASignalGroup/keyhelper.js +21 -0
  15. package/WASignalGroup/protobufs.js +3 -0
  16. package/WASignalGroup/queue_job.js +69 -0
  17. package/WASignalGroup/sender_chain_key.js +50 -0
  18. package/WASignalGroup/sender_key_distribution_message.js +78 -0
  19. package/WASignalGroup/sender_key_message.js +92 -0
  20. package/WASignalGroup/sender_key_name.js +70 -0
  21. package/WASignalGroup/sender_key_record.js +56 -0
  22. package/WASignalGroup/sender_key_state.js +129 -0
  23. package/WASignalGroup/sender_message_key.js +39 -0
  24. package/lib/Defaults/baileys-version.json +3 -0
  25. package/lib/Defaults/index.d.ts +53 -0
  26. package/lib/Defaults/index.js +108 -0
  27. package/lib/Signal/libsignal.d.ts +3 -0
  28. package/lib/Signal/libsignal.js +152 -0
  29. package/lib/Socket/Client/abstract-socket-client.d.ts +17 -0
  30. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  31. package/lib/Socket/Client/index.d.ts +3 -0
  32. package/lib/Socket/Client/index.js +19 -0
  33. package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
  34. package/lib/Socket/Client/mobile-socket-client.js +65 -0
  35. package/lib/Socket/Client/web-socket-client.d.ts +12 -0
  36. package/lib/Socket/Client/web-socket-client.js +62 -0
  37. package/lib/Socket/business.d.ts +170 -0
  38. package/lib/Socket/business.js +260 -0
  39. package/lib/Socket/chats.d.ts +81 -0
  40. package/lib/Socket/chats.js +950 -0
  41. package/lib/Socket/groups.d.ts +115 -0
  42. package/lib/Socket/groups.js +315 -0
  43. package/lib/Socket/index.d.ts +172 -0
  44. package/lib/Socket/index.js +10 -0
  45. package/lib/Socket/messages-recv.d.ts +158 -0
  46. package/lib/Socket/messages-recv.js +972 -0
  47. package/lib/Socket/messages-send.d.ts +155 -0
  48. package/lib/Socket/messages-send.js +1087 -0
  49. package/lib/Socket/newsletter.d.ts +132 -0
  50. package/lib/Socket/newsletter.js +236 -0
  51. package/lib/Socket/registration.d.ts +264 -0
  52. package/lib/Socket/registration.js +166 -0
  53. package/lib/Socket/socket.d.ts +44 -0
  54. package/lib/Socket/socket.js +643 -0
  55. package/lib/Socket/usync.d.ts +37 -0
  56. package/lib/Socket/usync.js +70 -0
  57. package/lib/Store/index.d.ts +3 -0
  58. package/lib/Store/index.js +10 -0
  59. package/lib/Store/make-cache-manager-store.d.ts +14 -0
  60. package/lib/Store/make-cache-manager-store.js +83 -0
  61. package/lib/Store/make-in-memory-store.d.ts +118 -0
  62. package/lib/Store/make-in-memory-store.js +431 -0
  63. package/lib/Store/make-ordered-dictionary.d.ts +13 -0
  64. package/lib/Store/make-ordered-dictionary.js +81 -0
  65. package/lib/Store/object-repository.d.ts +10 -0
  66. package/lib/Store/object-repository.js +27 -0
  67. package/lib/Types/Auth.d.ts +109 -0
  68. package/lib/Types/Auth.js +2 -0
  69. package/lib/Types/Call.d.ts +13 -0
  70. package/lib/Types/Call.js +2 -0
  71. package/lib/Types/Chat.d.ts +107 -0
  72. package/lib/Types/Chat.js +4 -0
  73. package/lib/Types/Contact.d.ts +19 -0
  74. package/lib/Types/Contact.js +2 -0
  75. package/lib/Types/Events.d.ts +172 -0
  76. package/lib/Types/Events.js +2 -0
  77. package/lib/Types/GroupMetadata.d.ts +56 -0
  78. package/lib/Types/GroupMetadata.js +2 -0
  79. package/lib/Types/Label.d.ts +46 -0
  80. package/lib/Types/Label.js +27 -0
  81. package/lib/Types/LabelAssociation.d.ts +29 -0
  82. package/lib/Types/LabelAssociation.js +9 -0
  83. package/lib/Types/Message.d.ts +433 -0
  84. package/lib/Types/Message.js +9 -0
  85. package/lib/Types/Newsletter.d.ts +92 -0
  86. package/lib/Types/Newsletter.js +32 -0
  87. package/lib/Types/Product.d.ts +78 -0
  88. package/lib/Types/Product.js +2 -0
  89. package/lib/Types/Signal.d.ts +57 -0
  90. package/lib/Types/Signal.js +2 -0
  91. package/lib/Types/Socket.d.ts +116 -0
  92. package/lib/Types/Socket.js +2 -0
  93. package/lib/Types/State.d.ts +27 -0
  94. package/lib/Types/State.js +2 -0
  95. package/lib/Types/USync.d.ts +25 -0
  96. package/lib/Types/USync.js +2 -0
  97. package/lib/Types/index.d.ts +66 -0
  98. package/lib/Types/index.js +42 -0
  99. package/lib/Utils/auth-utils.d.ts +18 -0
  100. package/lib/Utils/auth-utils.js +227 -0
  101. package/lib/Utils/baileys-event-stream.d.ts +16 -0
  102. package/lib/Utils/baileys-event-stream.js +63 -0
  103. package/lib/Utils/business.d.ts +22 -0
  104. package/lib/Utils/business.js +234 -0
  105. package/lib/Utils/chat-utils.d.ts +70 -0
  106. package/lib/Utils/chat-utils.js +745 -0
  107. package/lib/Utils/crypto.d.ts +40 -0
  108. package/lib/Utils/crypto.js +199 -0
  109. package/lib/Utils/decode-wa-message.d.ts +36 -0
  110. package/lib/Utils/decode-wa-message.js +234 -0
  111. package/lib/Utils/event-buffer.d.ts +35 -0
  112. package/lib/Utils/event-buffer.js +517 -0
  113. package/lib/Utils/generics.d.ts +88 -0
  114. package/lib/Utils/generics.js +402 -0
  115. package/lib/Utils/history.d.ts +19 -0
  116. package/lib/Utils/history.js +94 -0
  117. package/lib/Utils/index.d.ts +17 -0
  118. package/lib/Utils/index.js +33 -0
  119. package/lib/Utils/link-preview.d.ts +21 -0
  120. package/lib/Utils/link-preview.js +93 -0
  121. package/lib/Utils/logger.d.ts +2 -0
  122. package/lib/Utils/logger.js +7 -0
  123. package/lib/Utils/lt-hash.d.ts +12 -0
  124. package/lib/Utils/lt-hash.js +51 -0
  125. package/lib/Utils/make-mutex.d.ts +7 -0
  126. package/lib/Utils/make-mutex.js +43 -0
  127. package/lib/Utils/messages-media.d.ts +113 -0
  128. package/lib/Utils/messages-media.js +643 -0
  129. package/lib/Utils/messages.d.ts +77 -0
  130. package/lib/Utils/messages.js +1221 -0
  131. package/lib/Utils/noise-handler.d.ts +20 -0
  132. package/lib/Utils/noise-handler.js +160 -0
  133. package/lib/Utils/process-message.d.ts +41 -0
  134. package/lib/Utils/process-message.js +373 -0
  135. package/lib/Utils/signal.d.ts +33 -0
  136. package/lib/Utils/signal.js +159 -0
  137. package/lib/Utils/use-multi-file-auth-state.d.ts +12 -0
  138. package/lib/Utils/use-multi-file-auth-state.js +295 -0
  139. package/lib/Utils/use-single-file-auth-state.d.ts +12 -0
  140. package/lib/Utils/use-single-file-auth-state.js +75 -0
  141. package/lib/Utils/validate-connection.d.ts +11 -0
  142. package/lib/Utils/validate-connection.js +205 -0
  143. package/lib/WABinary/constants.d.ts +27 -0
  144. package/lib/WABinary/constants.js +40 -0
  145. package/lib/WABinary/decode.d.ts +6 -0
  146. package/lib/WABinary/decode.js +264 -0
  147. package/lib/WABinary/encode.d.ts +2 -0
  148. package/lib/WABinary/encode.js +252 -0
  149. package/lib/WABinary/generic-utils.d.ts +14 -0
  150. package/lib/WABinary/generic-utils.js +110 -0
  151. package/lib/WABinary/index.d.ts +5 -0
  152. package/lib/WABinary/index.js +21 -0
  153. package/lib/WABinary/jid-utils.d.ts +31 -0
  154. package/lib/WABinary/jid-utils.js +62 -0
  155. package/lib/WABinary/types.d.ts +18 -0
  156. package/lib/WABinary/types.js +2 -0
  157. package/lib/WAM/BinaryInfo.d.ts +8 -0
  158. package/lib/WAM/BinaryInfo.js +13 -0
  159. package/lib/WAM/constants.d.ts +38 -0
  160. package/lib/WAM/constants.js +15350 -0
  161. package/lib/WAM/encode.d.ts +2 -0
  162. package/lib/WAM/encode.js +155 -0
  163. package/lib/WAM/index.d.ts +3 -0
  164. package/lib/WAM/index.js +19 -0
  165. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -0
  166. package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
  167. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  168. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  169. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  170. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  171. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  172. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  173. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +25 -0
  174. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
  175. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +8 -0
  176. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  177. package/lib/WAUSync/Protocols/index.d.ts +4 -0
  178. package/lib/WAUSync/Protocols/index.js +20 -0
  179. package/lib/WAUSync/USyncQuery.d.ts +28 -0
  180. package/lib/WAUSync/USyncQuery.js +89 -0
  181. package/lib/WAUSync/USyncUser.d.ts +10 -0
  182. package/lib/WAUSync/USyncUser.js +26 -0
  183. package/lib/WAUSync/index.d.ts +3 -0
  184. package/lib/WAUSync/index.js +19 -0
  185. package/lib/index.js +31 -0
  186. package/package.json +51 -0
  187. package/src/Defaults/baileys-version.json +3 -0
  188. package/src/Defaults/index.ts +133 -0
  189. package/src/Signal/Group/ciphertext-message.ts +9 -0
  190. package/src/Signal/Group/group-session-builder.ts +56 -0
  191. package/src/Signal/Group/group_cipher.ts +117 -0
  192. package/src/Signal/Group/index.ts +11 -0
  193. package/src/Signal/Group/keyhelper.ts +28 -0
  194. package/src/Signal/Group/sender-chain-key.ts +34 -0
  195. package/src/Signal/Group/sender-key-distribution-message.ts +95 -0
  196. package/src/Signal/Group/sender-key-message.ts +96 -0
  197. package/src/Signal/Group/sender-key-name.ts +66 -0
  198. package/src/Signal/Group/sender-key-record.ts +69 -0
  199. package/src/Signal/Group/sender-key-state.ts +134 -0
  200. package/src/Signal/Group/sender-message-key.ts +36 -0
  201. package/src/Signal/libsignal.ts +447 -0
  202. package/src/Signal/lid-mapping.ts +209 -0
  203. package/src/Socket/Client/index.ts +2 -0
  204. package/src/Socket/Client/types.ts +22 -0
  205. package/src/Socket/Client/websocket.ts +56 -0
  206. package/src/Socket/business.ts +421 -0
  207. package/src/Socket/chats.ts +1223 -0
  208. package/src/Socket/communities.ts +477 -0
  209. package/src/Socket/groups.ts +361 -0
  210. package/src/Socket/index.ts +22 -0
  211. package/src/Socket/messages-recv.ts +1563 -0
  212. package/src/Socket/messages-send.ts +1210 -0
  213. package/src/Socket/mex.ts +58 -0
  214. package/src/Socket/newsletter.ts +229 -0
  215. package/src/Socket/socket.ts +1072 -0
  216. package/src/Types/Auth.ts +115 -0
  217. package/src/Types/Bussines.ts +20 -0
  218. package/src/Types/Call.ts +14 -0
  219. package/src/Types/Chat.ts +138 -0
  220. package/src/Types/Contact.ts +24 -0
  221. package/src/Types/Events.ts +132 -0
  222. package/src/Types/GroupMetadata.ts +70 -0
  223. package/src/Types/Label.ts +48 -0
  224. package/src/Types/LabelAssociation.ts +35 -0
  225. package/src/Types/Message.ts +424 -0
  226. package/src/Types/Newsletter.ts +98 -0
  227. package/src/Types/Product.ts +85 -0
  228. package/src/Types/Signal.ts +76 -0
  229. package/src/Types/Socket.ts +150 -0
  230. package/src/Types/State.ts +43 -0
  231. package/src/Types/USync.ts +27 -0
  232. package/src/Types/globals.d.ts +8 -0
  233. package/src/Types/index.ts +67 -0
  234. package/src/Utils/auth-utils.ts +331 -0
  235. package/src/Utils/browser-utils.ts +31 -0
  236. package/src/Utils/business.ts +286 -0
  237. package/src/Utils/chat-utils.ts +933 -0
  238. package/src/Utils/crypto.ts +184 -0
  239. package/src/Utils/decode-wa-message.ts +355 -0
  240. package/src/Utils/event-buffer.ts +662 -0
  241. package/src/Utils/generics.ts +470 -0
  242. package/src/Utils/history.ts +114 -0
  243. package/src/Utils/index.ts +18 -0
  244. package/src/Utils/link-preview.ts +111 -0
  245. package/src/Utils/logger.ts +13 -0
  246. package/src/Utils/lt-hash.ts +65 -0
  247. package/src/Utils/make-mutex.ts +45 -0
  248. package/src/Utils/message-retry-manager.ts +229 -0
  249. package/src/Utils/messages-media.ts +820 -0
  250. package/src/Utils/messages.ts +1137 -0
  251. package/src/Utils/noise-handler.ts +192 -0
  252. package/src/Utils/pre-key-manager.ts +126 -0
  253. package/src/Utils/process-message.ts +622 -0
  254. package/src/Utils/signal.ts +214 -0
  255. package/src/Utils/use-multi-file-auth-state.ts +136 -0
  256. package/src/Utils/validate-connection.ts +253 -0
  257. package/src/WABinary/constants.ts +1305 -0
  258. package/src/WABinary/decode.ts +281 -0
  259. package/src/WABinary/encode.ts +253 -0
  260. package/src/WABinary/generic-utils.ts +127 -0
  261. package/src/WABinary/index.ts +5 -0
  262. package/src/WABinary/jid-utils.ts +128 -0
  263. package/src/WABinary/types.ts +17 -0
  264. package/src/WAM/BinaryInfo.ts +12 -0
  265. package/src/WAM/constants.ts +22889 -0
  266. package/src/WAM/encode.ts +169 -0
  267. package/src/WAM/index.ts +3 -0
  268. package/src/WAUSync/Protocols/USyncContactProtocol.ts +32 -0
  269. package/src/WAUSync/Protocols/USyncDeviceProtocol.ts +78 -0
  270. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.ts +35 -0
  271. package/src/WAUSync/Protocols/USyncStatusProtocol.ts +44 -0
  272. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.ts +76 -0
  273. package/src/WAUSync/Protocols/UsyncLIDProtocol.ts +33 -0
  274. package/src/WAUSync/Protocols/index.ts +4 -0
  275. package/src/WAUSync/USyncQuery.ts +133 -0
  276. package/src/WAUSync/USyncUser.ts +32 -0
  277. package/src/WAUSync/index.ts +3 -0
  278. package/src/index.ts +13 -0
@@ -0,0 +1,643 @@
1
+ "use strict";
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Lena-Baileys — Módulo de Mídia
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+
43
+ exports.getStatusCodeForMediaRetry =
44
+ exports.decryptMediaRetryData =
45
+ exports.decodeMediaRetryNode =
46
+ exports.encryptMediaRetryRequest =
47
+ exports.getWAUploadToServer =
48
+ exports.downloadEncryptedContent =
49
+ exports.downloadContentFromMessageV1 =
50
+ exports.clearDownloadCache =
51
+ exports.detectMimeType =
52
+ exports.downloadContentFromMessage =
53
+ exports.getUrlFromDirectPath =
54
+ exports.encryptedStream =
55
+ exports.prepareStream =
56
+ exports.getHttpStream =
57
+ exports.getStream =
58
+ exports.toBuffer =
59
+ exports.toReadable =
60
+ exports.mediaMessageSHA256B64 =
61
+ exports.generateProfilePicture =
62
+ exports.encodeBase64EncodedStringForUpload =
63
+ exports.extractImageThumb =
64
+ exports.hkdfInfoKey = void 0;
65
+
66
+ exports.getMediaKeys = getMediaKeys;
67
+ exports.getAudioDuration = getAudioDuration;
68
+ exports.getAudioWaveform = getAudioWaveform;
69
+ exports.generateThumbnail = generateThumbnail;
70
+ exports.extensionForMediaMessage = extensionForMediaMessage;
71
+
72
+ // ─── Dependências ─────────────────────────────────────────────────────────────
73
+ const { Boom } = require("@hapi/boom");
74
+ const { exec } = require("child_process");
75
+ const Crypto = __importStar(require("crypto"));
76
+ const { once } = require("events");
77
+ const { createReadStream, createWriteStream, writeFileSync, promises: fsp } = require("fs");
78
+ const { tmpdir } = require("os");
79
+ const { join } = require("path");
80
+ const { Readable, Transform } = require("stream");
81
+ const Proto = require("../../WAProto");
82
+ const Config = require("../Defaults");
83
+ const JidUtils = require("../WABinary");
84
+ const CryptoUtils = require("./crypto");
85
+ const Helpers = require("./generics");
86
+
87
+ // ─── Cache de downloads ───────────────────────────────────────────────────────
88
+ const DOWNLOAD_CACHE = new Map()
89
+ const MAX_RETRIES = 3
90
+ const RETRY_DELAY_MS = 1000
91
+
92
+ // ─── Helpers internos ─────────────────────────────────────────────────────────
93
+ const getTmpDir = () => tmpdir()
94
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms))
95
+
96
+ // ─── Detecta mime type pelo buffer ───────────────────────────────────────────
97
+ const detectMimeType = (buffer) => {
98
+ if (!buffer || buffer.length < 4) return 'application/octet-stream'
99
+ const hex = buffer.slice(0, 4).toString('hex')
100
+ if (hex.startsWith('ffd8ff')) return 'image/jpeg'
101
+ if (hex.startsWith('89504e47')) return 'image/png'
102
+ if (hex.startsWith('47494638')) return 'image/gif'
103
+ if (hex.startsWith('25504446')) return 'application/pdf'
104
+ if (hex.startsWith('494433') || hex.startsWith('fffb')) return 'audio/mpeg'
105
+ if (hex.startsWith('4f676753')) return 'audio/ogg'
106
+ if (hex.startsWith('1a45dfa3')) return 'video/webm'
107
+ if (buffer.slice(4, 8).toString('ascii') === 'ftyp') return 'video/mp4'
108
+ if (hex.startsWith('52494646')) return 'audio/wav'
109
+ if (hex.startsWith('504b0304')) return 'application/zip'
110
+ return 'application/octet-stream'
111
+ }
112
+ exports.detectMimeType = detectMimeType
113
+
114
+ // ─── Limpar cache de downloads ────────────────────────────────────────────────
115
+ const clearDownloadCache = () => DOWNLOAD_CACHE.clear()
116
+ exports.clearDownloadCache = clearDownloadCache
117
+
118
+ // ─── Biblioteca de processamento de imagem ────────────────────────────────────
119
+ const getImageProcessingLibrary = async () => {
120
+ const [_jimp, sharp] = await Promise.all([
121
+ import('jimp').catch(() => {}),
122
+ import('sharp').catch(() => {}),
123
+ ])
124
+ if (sharp) return { sharp }
125
+ const jimp = _jimp?.default || _jimp
126
+ if (jimp) return { jimp }
127
+ throw new Boom('Nenhuma biblioteca de imagem disponível')
128
+ }
129
+
130
+ // ─── Chave HKDF por tipo de mídia ────────────────────────────────────────────
131
+ const hkdfInfoKey = (type) => `WhatsApp ${Config.MEDIA_HKDF_KEY_MAPPING[type]} Keys`
132
+ exports.hkdfInfoKey = hkdfInfoKey
133
+
134
+ // ─── Deriva chaves de criptografia ───────────────────────────────────────────
135
+ async function getMediaKeys(buffer, mediaType) {
136
+ if (!buffer) throw new Boom('Chave de mídia vazia')
137
+ if (typeof buffer === 'string') buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64')
138
+ const expanded = await CryptoUtils.hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
139
+ return {
140
+ iv: expanded.slice(0, 16),
141
+ cipherKey: expanded.slice(16, 48),
142
+ macKey: expanded.slice(48, 80),
143
+ }
144
+ }
145
+
146
+ // ─── Extrai thumbnail de vídeo via ffmpeg ─────────────────────────────────────
147
+ const extractVideoThumb = (path, dest, time, size) => new Promise((resolve, reject) => {
148
+ const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${dest}`
149
+ exec(cmd, (err) => err ? reject(err) : resolve())
150
+ })
151
+
152
+ // ─── Extrai thumbnail de imagem ───────────────────────────────────────────────
153
+ const extractImageThumb = async (bufferOrPath, width = 32) => {
154
+ var _a, _b
155
+ if (bufferOrPath instanceof Readable) bufferOrPath = await toBuffer(bufferOrPath)
156
+ const lib = await getImageProcessingLibrary()
157
+ if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
158
+ const img = lib.sharp.default(bufferOrPath)
159
+ const meta = await img.metadata()
160
+ const buf = await img.resize(width).jpeg({ quality: 50 }).toBuffer()
161
+ return { buffer: buf, original: { width: meta.width, height: meta.height } }
162
+ } else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
163
+ const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp
164
+ const jimp = await read(bufferOrPath)
165
+ const buf = await jimp.quality(50).resize(width, AUTO, RESIZE_BILINEAR).getBufferAsync(MIME_JPEG)
166
+ return { buffer: buf, original: { width: jimp.getWidth(), height: jimp.getHeight() } }
167
+ }
168
+ throw new Boom('Nenhuma biblioteca de imagem disponível')
169
+ }
170
+ exports.extractImageThumb = extractImageThumb
171
+
172
+ // ─── Encode base64 URL-safe para upload ──────────────────────────────────────
173
+ const encodeBase64EncodedStringForUpload = (b64) =>
174
+ encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''))
175
+ exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload
176
+
177
+ // ─── Gera foto de perfil ──────────────────────────────────────────────────────
178
+ const generateProfilePicture = async (mediaUpload) => {
179
+ const input = Buffer.isBuffer(mediaUpload)
180
+ ? mediaUpload
181
+ : typeof mediaUpload === 'object' && 'url' in mediaUpload
182
+ ? mediaUpload.url.toString()
183
+ : await toBuffer(mediaUpload.stream)
184
+ const { read, MIME_JPEG, AUTO } = require('jimp')
185
+ const jimp = await read(input)
186
+ const cropped = jimp.crop(0, 0, jimp.getWidth(), jimp.getHeight())
187
+ return { img: await cropped.quality(100).scaleToFit(720, 720, AUTO).getBufferAsync(MIME_JPEG) }
188
+ }
189
+ exports.generateProfilePicture = generateProfilePicture
190
+
191
+ // ─── SHA256 de mensagem de mídia ──────────────────────────────────────────────
192
+ const mediaMessageSHA256B64 = (message) => {
193
+ const media = Object.values(message)[0]
194
+ return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64')
195
+ }
196
+ exports.mediaMessageSHA256B64 = mediaMessageSHA256B64
197
+
198
+ // ─── Duração de áudio ─────────────────────────────────────────────────────────
199
+ async function getAudioDuration(buffer) {
200
+ const mm = await import('music-metadata')
201
+ if (Buffer.isBuffer(buffer)) return (await mm.parseBuffer(buffer, undefined, { duration: true })).format.duration
202
+ if (typeof buffer === 'string') {
203
+ const rs = createReadStream(buffer)
204
+ try { return (await mm.parseStream(rs, undefined, { duration: true })).format.duration }
205
+ finally { rs.destroy() }
206
+ }
207
+ return (await mm.parseStream(buffer, undefined, { duration: true })).format.duration
208
+ }
209
+
210
+ // ─── Waveform de áudio ────────────────────────────────────────────────────────
211
+ async function getAudioWaveform(buffer, logger) {
212
+ try {
213
+ const audioDecode = (b) => import('audio-decode').then(({ default: d }) => d(b))
214
+ let data = Buffer.isBuffer(buffer) ? buffer
215
+ : typeof buffer === 'string' ? await toBuffer(createReadStream(buffer))
216
+ : await toBuffer(buffer)
217
+ const decoded = await audioDecode(data)
218
+ const raw = decoded.getChannelData(0)
219
+ const samples = 64
220
+ const blockSize = Math.floor(raw.length / samples)
221
+ const filtered = Array.from({ length: samples }, (_, i) => {
222
+ let sum = 0
223
+ for (let j = 0; j < blockSize; j++) sum += Math.abs(raw[i * blockSize + j])
224
+ return sum / blockSize
225
+ })
226
+ const mult = Math.pow(Math.max(...filtered), -1)
227
+ return new Uint8Array(filtered.map(n => Math.floor(100 * n * mult)))
228
+ } catch (e) {
229
+ logger?.debug('Falha ao gerar waveform: ' + e)
230
+ }
231
+ }
232
+
233
+ // ─── Buffer / Stream helpers ──────────────────────────────────────────────────
234
+ const toReadable = (buffer) => {
235
+ const r = new Readable({ read: () => {} })
236
+ r.push(buffer)
237
+ r.push(null)
238
+ return r
239
+ }
240
+ exports.toReadable = toReadable
241
+
242
+ const toBuffer = async (stream) => {
243
+ const chunks = []
244
+ for await (const chunk of stream) chunks.push(chunk)
245
+ stream.destroy()
246
+ return Buffer.concat(chunks)
247
+ }
248
+ exports.toBuffer = toBuffer
249
+
250
+ const getStream = async (item, opts) => {
251
+ if (Buffer.isBuffer(item)) return { stream: toReadable(item), type: 'buffer' }
252
+ if ('stream' in item) return { stream: item.stream, type: 'readable' }
253
+ const url = item.url.toString()
254
+ if (url.startsWith('http://') || url.startsWith('https://'))
255
+ return { stream: await getHttpStream(item.url, opts), type: 'remote' }
256
+ return { stream: createReadStream(item.url), type: 'file' }
257
+ }
258
+ exports.getStream = getStream
259
+
260
+ // ─── Gera thumbnail ───────────────────────────────────────────────────────────
261
+ async function generateThumbnail(file, mediaType, options) {
262
+ var _a
263
+ let thumbnail, originalImageDimensions
264
+ if (mediaType === 'image') {
265
+ const { buffer, original } = await extractImageThumb(file)
266
+ thumbnail = buffer.toString('base64')
267
+ if (original.width && original.height) originalImageDimensions = original
268
+ } else if (mediaType === 'video') {
269
+ const imgPath = join(getTmpDir(), Helpers.generateMessageID() + '.jpg')
270
+ try {
271
+ await extractVideoThumb(file, imgPath, '00:00:00', { width: 32, height: 32 })
272
+ thumbnail = (await fsp.readFile(imgPath)).toString('base64')
273
+ await fsp.unlink(imgPath)
274
+ } catch (err) {
275
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('Falha ao gerar thumbnail de vídeo: ' + err)
276
+ }
277
+ }
278
+ return { thumbnail, originalImageDimensions }
279
+ }
280
+
281
+ // ─── HTTP Stream ──────────────────────────────────────────────────────────────
282
+ const getHttpStream = async (url, options = {}) => {
283
+ const { default: axios } = await import('axios')
284
+ const res = await axios.get(url.toString(), { ...options, responseType: 'stream' })
285
+ return res.data
286
+ }
287
+ exports.getHttpStream = getHttpStream
288
+
289
+ // ─── Prepara stream (newsletter) ──────────────────────────────────────────────
290
+ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
291
+ const { stream, type } = await getStream(media, opts)
292
+ logger?.debug('stream de mídia obtido')
293
+ let bodyPath, didSaveToTmpPath = false
294
+ try {
295
+ const buffer = await toBuffer(stream)
296
+ if (type === 'file') { bodyPath = media.url }
297
+ else if (saveOriginalFileIfRequired) {
298
+ bodyPath = join(getTmpDir(), mediaType + Helpers.generateMessageID())
299
+ writeFileSync(bodyPath, buffer)
300
+ didSaveToTmpPath = true
301
+ }
302
+ const fileSha256 = Crypto.createHash('sha256').update(buffer).digest()
303
+ stream?.destroy()
304
+ return { mediaKey: undefined, encWriteStream: buffer, fileLength: buffer.length, fileSha256, fileEncSha256: undefined, bodyPath, didSaveToTmpPath }
305
+ } catch (error) {
306
+ stream.destroy()
307
+ if (didSaveToTmpPath) { try { await fsp.unlink(bodyPath) } catch (e) { logger?.error({ e }, 'falha ao remover tmp') } }
308
+ throw error
309
+ }
310
+ }
311
+ exports.prepareStream = prepareStream
312
+
313
+ // ─── Stream encriptado ────────────────────────────────────────────────────────
314
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
315
+ const { stream, type } = await getStream(media, opts)
316
+ logger?.debug('stream de mídia obtido')
317
+ const mediaKey = Crypto.randomBytes(32)
318
+ const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
319
+ const encWriteStream = new Readable({ read: () => {} })
320
+ let bodyPath, writeStream, didSaveToTmpPath = false
321
+ if (type === 'file') { bodyPath = media.url }
322
+ else if (saveOriginalFileIfRequired) {
323
+ bodyPath = join(getTmpDir(), mediaType + Helpers.generateMessageID())
324
+ writeStream = createWriteStream(bodyPath)
325
+ didSaveToTmpPath = true
326
+ }
327
+ let fileLength = 0
328
+ const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
329
+ let hmac = Crypto.createHmac('sha256', macKey).update(iv)
330
+ let sha256Plain = Crypto.createHash('sha256')
331
+ let sha256Enc = Crypto.createHash('sha256')
332
+
333
+ function onChunk(buff) {
334
+ sha256Enc = sha256Enc.update(buff)
335
+ hmac = hmac.update(buff)
336
+ encWriteStream.push(buff)
337
+ }
338
+
339
+ try {
340
+ for await (const data of stream) {
341
+ fileLength += data.length
342
+ if (type === 'remote' && opts?.maxContentLength && fileLength + data.length > opts.maxContentLength)
343
+ throw new Boom(`conteúdo excedeu o limite para "${type}"`, { data: { media, type } })
344
+ sha256Plain = sha256Plain.update(data)
345
+ if (writeStream) { if (!writeStream.write(data)) await once(writeStream, 'drain') }
346
+ onChunk(aes.update(data))
347
+ }
348
+ onChunk(aes.final())
349
+ const mac = hmac.digest().slice(0, 10)
350
+ sha256Enc = sha256Enc.update(mac)
351
+ const fileSha256 = sha256Plain.digest()
352
+ const fileEncSha256 = sha256Enc.digest()
353
+ encWriteStream.push(mac)
354
+ encWriteStream.push(null)
355
+ writeStream?.end()
356
+ stream.destroy()
357
+ logger?.debug('dados encriptados com sucesso')
358
+ return { mediaKey, encWriteStream, bodyPath, mac, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath }
359
+ } catch (error) {
360
+ encWriteStream.destroy(); writeStream?.destroy()
361
+ aes.destroy(); hmac.destroy(); sha256Plain.destroy(); sha256Enc.destroy(); stream.destroy()
362
+ if (didSaveToTmpPath) { try { await fsp.unlink(bodyPath) } catch (e) { logger?.error({ e }, 'falha ao remover tmp') } }
363
+ throw error
364
+ }
365
+ }
366
+ exports.encryptedStream = encryptedStream
367
+
368
+ // ─── URL de download ──────────────────────────────────────────────────────────
369
+ const DEF_HOST = 'mmg.whatsapp.net'
370
+ const AES_CHUNK_SIZE = 16
371
+ const toSmallestChunk = (num) => Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE
372
+ const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`
373
+ exports.getUrlFromDirectPath = getUrlFromDirectPath
374
+
375
+ // ─────────────────────────────────────────────────────────────────────────────
376
+ // downloadContentFromMessage — original
377
+ // ─────────────────────────────────────────────────────────────────────────────
378
+ const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
379
+ const downloadUrl = url || getUrlFromDirectPath(directPath)
380
+ const keys = await getMediaKeys(mediaKey, type)
381
+ return downloadEncryptedContent(downloadUrl, keys, opts)
382
+ }
383
+ exports.downloadContentFromMessage = downloadContentFromMessage
384
+
385
+ // ─────────────────────────────────────────────────────────────────────────────
386
+ // downloadContentFromMessageV1 — versão melhorada
387
+ // Melhorias: retry automático, progresso, cache, timeout, mime type, buffer, arquivo
388
+ // ─────────────────────────────────────────────────────────────────────────────
389
+ const downloadContentFromMessageV1 = async (
390
+ { mediaKey, directPath, url },
391
+ type,
392
+ opts = {}
393
+ ) => {
394
+ const {
395
+ onProgress = null, // ({ downloaded, total, percent }) => void
396
+ useCache = false, // cache em memória
397
+ saveToFile = null, // salvar direto em arquivo
398
+ timeoutMs = 60_000, // timeout em ms
399
+ retries = MAX_RETRIES,
400
+ asBuffer = false, // retornar buffer em vez de stream
401
+ } = opts
402
+
403
+ const downloadUrl = url || getUrlFromDirectPath(directPath)
404
+
405
+ // ── Cache ────────────────────────────────────────────────────────────────
406
+ if (useCache && DOWNLOAD_CACHE.has(downloadUrl)) {
407
+ const cached = DOWNLOAD_CACHE.get(downloadUrl)
408
+ return asBuffer ? cached : toReadable(cached)
409
+ }
410
+
411
+ const keys = await getMediaKeys(mediaKey, type)
412
+
413
+ // ── Retry automático ──────────────────────────────────────────────────────
414
+ let lastError
415
+ for (let attempt = 1; attempt <= retries; attempt++) {
416
+ try {
417
+ const result = await _executeDownload({ downloadUrl, keys, opts, onProgress, saveToFile, timeoutMs, asBuffer })
418
+ if (useCache && Buffer.isBuffer(result)) DOWNLOAD_CACHE.set(downloadUrl, result)
419
+ return result
420
+ } catch (err) {
421
+ lastError = err
422
+ if (attempt < retries) {
423
+ const delay = RETRY_DELAY_MS * attempt
424
+ console.warn(`[downloadV1] Tentativa ${attempt}/${retries} falhou. Retentando em ${delay}ms...`)
425
+ await sleep(delay)
426
+ }
427
+ }
428
+ }
429
+
430
+ throw new Error(`[downloadV1] Falhou após ${retries} tentativas: ${lastError?.message}`)
431
+ }
432
+ exports.downloadContentFromMessageV1 = downloadContentFromMessageV1
433
+
434
+ // ─── Executa download com progresso/timeout ───────────────────────────────────
435
+ const _executeDownload = async ({ downloadUrl, keys, opts, onProgress, saveToFile, timeoutMs, asBuffer }) => {
436
+ const controller = new AbortController()
437
+ const timer = setTimeout(() => controller.abort(), timeoutMs)
438
+ let fetchedStream
439
+ try {
440
+ fetchedStream = await downloadEncryptedContent(downloadUrl, keys, {
441
+ ...opts,
442
+ options: { ...(opts.options || {}), signal: controller.signal }
443
+ })
444
+ } finally {
445
+ clearTimeout(timer)
446
+ }
447
+
448
+ if (!onProgress && !saveToFile && !asBuffer) return fetchedStream
449
+
450
+ return new Promise((resolve, reject) => {
451
+ const chunks = []
452
+ let downloaded = 0
453
+ const total = parseInt(fetchedStream.headers?.['content-length'] || '0', 10)
454
+
455
+ fetchedStream.on('data', (chunk) => {
456
+ chunks.push(chunk)
457
+ downloaded += chunk.length
458
+ if (onProgress) onProgress({
459
+ downloaded,
460
+ total,
461
+ percent: total > 0 ? Math.round((downloaded / total) * 100) : null,
462
+ })
463
+ })
464
+
465
+ fetchedStream.on('end', async () => {
466
+ const buffer = Buffer.concat(chunks)
467
+ if (saveToFile) {
468
+ try { await fsp.writeFile(saveToFile, buffer); resolve(saveToFile) }
469
+ catch (err) { reject(err) }
470
+ return
471
+ }
472
+ resolve(asBuffer ? buffer : toReadable(buffer))
473
+ })
474
+
475
+ fetchedStream.on('error', reject)
476
+ })
477
+ }
478
+
479
+ // ─────────────────────────────────────────────────────────────────────────────
480
+ // downloadEncryptedContent — desencripta stream AES256-CBC
481
+ // ─────────────────────────────────────────────────────────────────────────────
482
+ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
483
+ let bytesFetched = 0, startChunk = 0, firstBlockIsIV = false
484
+ if (startByte) {
485
+ const chunk = toSmallestChunk(startByte || 0)
486
+ if (chunk) { startChunk = chunk - AES_CHUNK_SIZE; bytesFetched = chunk; firstBlockIsIV = true }
487
+ }
488
+ const endChunk = endByte ? toSmallestChunk(endByte || 0) + AES_CHUNK_SIZE : undefined
489
+ const headers = { ...(options?.headers || {}), Origin: Config.DEFAULT_ORIGIN }
490
+ if (startChunk || endChunk) {
491
+ headers.Range = `bytes=${startChunk}-`
492
+ if (endChunk) headers.Range += endChunk
493
+ }
494
+ const fetched = await getHttpStream(downloadUrl, { ...options || {}, headers, maxBodyLength: Infinity, maxContentLength: Infinity })
495
+ let remainingBytes = Buffer.from([])
496
+ let aes
497
+
498
+ const pushBytes = (bytes, push) => {
499
+ if (startByte || endByte) {
500
+ const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
501
+ const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0)
502
+ push(bytes.slice(start, end))
503
+ bytesFetched += bytes.length
504
+ } else {
505
+ push(bytes)
506
+ }
507
+ }
508
+
509
+ const output = new Transform({
510
+ transform(chunk, _, callback) {
511
+ let data = Buffer.concat([remainingBytes, chunk])
512
+ const decryptLen = toSmallestChunk(data.length)
513
+ remainingBytes = data.slice(decryptLen)
514
+ data = data.slice(0, decryptLen)
515
+ if (!aes) {
516
+ let ivValue = iv
517
+ if (firstBlockIsIV) { ivValue = data.slice(0, AES_CHUNK_SIZE); data = data.slice(AES_CHUNK_SIZE) }
518
+ aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue)
519
+ if (endByte) aes.setAutoPadding(false)
520
+ }
521
+ try { pushBytes(aes.update(data), b => this.push(b)); callback() }
522
+ catch (err) { callback(err) }
523
+ },
524
+ final(callback) {
525
+ try { pushBytes(aes.final(), b => this.push(b)); callback() }
526
+ catch (err) { callback(err) }
527
+ }
528
+ })
529
+
530
+ return fetched.pipe(output, { end: true })
531
+ }
532
+ exports.downloadEncryptedContent = downloadEncryptedContent
533
+
534
+ // ─── Extensão por tipo de mensagem ───────────────────────────────────────────
535
+ function extensionForMediaMessage(message) {
536
+ const type = Object.keys(message)[0]
537
+ if (['locationMessage', 'liveLocationMessage', 'productMessage'].includes(type)) return '.jpeg'
538
+ return '.' + message[type].mimetype.split(';')[0].split('/')[1]
539
+ }
540
+
541
+ // ─── Upload para servidor WA ──────────────────────────────────────────────────
542
+ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
543
+ return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
544
+ var _a, _b
545
+ const { default: axios } = await import('axios')
546
+ let uploadInfo = await refreshMediaConn(false)
547
+ let urls
548
+ const hosts = [...customUploadHosts, ...uploadInfo.hosts]
549
+ const chunks = []
550
+ if (!Buffer.isBuffer(stream)) { for await (const c of stream) chunks.push(c) }
551
+ const body = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks)
552
+ fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
553
+ let media = Config.MEDIA_PATH_MAP[mediaType]
554
+ if (newsletter) media = media?.replace('/mms/', '/newsletter/newsletter-')
555
+
556
+ for (const { hostname, maxContentLengthBytes } of hosts) {
557
+ logger.debug(`enviando para "${hostname}"`)
558
+ const auth = encodeURIComponent(uploadInfo.auth)
559
+ const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
560
+ let result
561
+ try {
562
+ if (maxContentLengthBytes && body.length > maxContentLengthBytes)
563
+ throw new Boom(`Body muito grande para "${hostname}"`, { statusCode: 413 })
564
+ const res = await axios.post(url, body, {
565
+ ...options,
566
+ headers: { ...options.headers || {}, 'Content-Type': 'application/octet-stream', 'Origin': Config.DEFAULT_ORIGIN },
567
+ httpsAgent: fetchAgent, timeout: timeoutMs, responseType: 'json',
568
+ maxBodyLength: Infinity, maxContentLength: Infinity,
569
+ })
570
+ result = res.data
571
+ if (result?.url || result?.directPath) {
572
+ urls = { mediaUrl: result.url, directPath: result.direct_path, handle: result.handle }
573
+ break
574
+ } else {
575
+ uploadInfo = await refreshMediaConn(true)
576
+ throw new Error(`upload falhou: ${JSON.stringify(result)}`)
577
+ }
578
+ } catch (error) {
579
+ if (axios.isAxiosError(error)) result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data
580
+ const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname)
581
+ logger.warn({ trace: error.stack, uploadResult: result }, `Erro ao enviar para ${hostname}${isLast ? '' : ', tentando próximo...'}`)
582
+ }
583
+ }
584
+ if (!urls) throw new Boom('Upload de mídia falhou em todos os hosts', { statusCode: 500 })
585
+ return urls
586
+ }
587
+ }
588
+ exports.getWAUploadToServer = getWAUploadToServer
589
+
590
+ // ─── Retry de mídia ───────────────────────────────────────────────────────────
591
+ const getMediaRetryKey = (mediaKey) => CryptoUtils.hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' })
592
+
593
+ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
594
+ const recp = Proto.proto.ServerErrorReceipt.encode({ stanzaId: key.id }).finish()
595
+ const iv = Crypto.randomBytes(12)
596
+ const retryKey = await getMediaRetryKey(mediaKey)
597
+ const ciphertext = CryptoUtils.aesEncryptGCM(recp, retryKey, iv, Buffer.from(key.id))
598
+ return {
599
+ tag: 'receipt',
600
+ attrs: { id: key.id, to: JidUtils.jidNormalizedUser(meId), type: 'server-error' },
601
+ content: [
602
+ { tag: 'encrypt', attrs: {}, content: [
603
+ { tag: 'enc_p', attrs: {}, content: ciphertext },
604
+ { tag: 'enc_iv', attrs: {}, content: iv }
605
+ ]},
606
+ { tag: 'rmr', attrs: { jid: key.remoteJid, 'from_me': (!!key.fromMe).toString(), participant: key.participant || undefined } }
607
+ ]
608
+ }
609
+ }
610
+ exports.encryptMediaRetryRequest = encryptMediaRetryRequest
611
+
612
+ const decodeMediaRetryNode = (node) => {
613
+ const rmr = JidUtils.getBinaryNodeChild(node, 'rmr')
614
+ const event = { key: { id: node.attrs.id, remoteJid: rmr.attrs.jid, fromMe: rmr.attrs.from_me === 'true', participant: rmr.attrs.participant } }
615
+ const err = JidUtils.getBinaryNodeChild(node, 'error')
616
+ if (err) {
617
+ const code = +err.attrs.code
618
+ event.error = new Boom(`Falha ao reenviar mídia (${code})`, { data: err.attrs, statusCode: getStatusCodeForMediaRetry(code) })
619
+ } else {
620
+ const enc = JidUtils.getBinaryNodeChild(node, 'encrypt')
621
+ const ciphertext = JidUtils.getBinaryNodeChildBuffer(enc, 'enc_p')
622
+ const iv = JidUtils.getBinaryNodeChildBuffer(enc, 'enc_iv')
623
+ event.media = (ciphertext && iv) ? { ciphertext, iv } : undefined
624
+ if (!event.media) event.error = new Boom('Falha ao reenviar mídia (sem ciphertext)', { statusCode: 404 })
625
+ }
626
+ return event
627
+ }
628
+ exports.decodeMediaRetryNode = decodeMediaRetryNode
629
+
630
+ const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
631
+ const retryKey = await getMediaRetryKey(mediaKey)
632
+ return Proto.proto.MediaRetryNotification.decode(CryptoUtils.aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId)))
633
+ }
634
+ exports.decryptMediaRetryData = decryptMediaRetryData
635
+
636
+ const MEDIA_RETRY_STATUS_MAP = {
637
+ [Proto.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
638
+ [Proto.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
639
+ [Proto.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
640
+ [Proto.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
641
+ }
642
+ const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code]
643
+ exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry