@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,1514 +1,1408 @@
1
- "use strict"
2
-
3
- var __importDefault = (this && this.__importDefault) || function(mod) {
4
- return (mod && mod.__esModule) ? mod : {
5
- "default": mod
6
- }
7
- }
8
-
9
- Object.defineProperty(exports, "__esModule", {
10
- value: true
11
- })
12
-
13
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
14
- const boom_1 = require("@hapi/boom")
15
- const crypto_1 = require("crypto")
16
- const WAProto_1 = require("../../WAProto")
17
- const Defaults_1 = require("../Defaults")
18
- const Utils_1 = require("../Utils")
19
- const Types_1 = require("../Types")
20
- const WABinary_1 = require("../WABinary")
21
- const WAUSync_1 = require("../WAUSync")
22
- const newsletter_1 = require("./newsletter")
23
- const link_preview_1 = require("../Utils/link-preview")
24
- const make_keyed_mutex_1 = require("../Utils/make-mutex")
25
-
26
- const makeMessagesSocket = (config) => {
27
- const {
28
- logger,
29
- maxMsgRetryCount,
30
- linkPreviewImageThumbnailWidth,
31
- generateHighQualityLinkPreview,
32
- options: axiosOptions,
33
- patchMessageBeforeSending,
34
- cachedGroupMetadata,
35
- enableRecentMessageCache
36
- } = config
37
- const sock = newsletter_1.makeNewsletterSocket(config)
38
- const {
39
- ev,
40
- authState,
41
- processingMutex,
42
- signalRepository,
43
- upsertMessage,
44
- createCallLink,
45
- query,
46
- fetchPrivacySettings,
47
- sendNode,
48
- groupQuery,
49
- groupMetadata,
50
- groupToggleEphemeral,
51
- newsletterWMexQuery,
52
- executeUSyncQuery
53
- } = sock
54
-
55
- const userDevicesCache = config.userDevicesCache || new node_cache_1.default({
56
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.USER_DEVICES,
57
- useClones: false
58
- })
59
-
60
- const messageRetryManager = enableRecentMessageCache ? new Utils_1.MessageRetryManager(logger, maxMsgRetryCount) : null
61
- const encryptionMutex = make_keyed_mutex_1.makeKeyedMutex()
62
- const devicesMutex = make_keyed_mutex_1.makeMutex()
63
- let mediaConn
64
-
1
+ import NodeCache from '@cacheable/node-cache';
2
+ import { Boom } from '@hapi/boom';
3
+ import { randomBytes } from 'crypto';
4
+ import { proto } from '../../WAProto/index.js';
5
+ import { BIZ_BOT_SUPPORT_PAYLOAD, DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
6
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, delay, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, generateWAMessageContent, generateWAMessageFromContent, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, hasValidAlbumMedia, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds } from '../Utils/index.js';
7
+ import { AssociationType } from '../Types/index.js';
8
+ import { getUrlInfo } from '../Utils/link-preview.js';
9
+ import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
10
+ import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
11
+ import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
12
+ import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinaryNode, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isJidNewsletter, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET } from '../WABinary/index.js';
13
+ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
14
+ import { makeNewsletterSocket } from './newsletter.js';
15
+ import kikyy from './dugong.js';
16
+ import { generateTableContent, generateTableContentV2, generateListContent, generateCodeBlockContent, generateCodeBlockContentV2, generateLinkContent, generateLinkContentV2, generateRichMessageContent, generateUnifiedResponseContent, captureUnifiedResponse, generateLatexContent, generateLatexImageContent, generateLatexInlineImageContent } from '../Utils/rich-message-utils.js';
17
+ export const makeMessagesSocket = (config) => {
18
+ const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
19
+ const sock = makeNewsletterSocket(config);
20
+ const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
21
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
22
+ /**
23
+ * Set of tctoken storage JIDs with a fire-and-forget `issuePrivacyTokens` IQ in flight.
24
+ * Prevents duplicate IQs from rapid back-to-back sends before `senderTimestamp` persists.
25
+ * Entries are always removed in `.finally()`, so the set is bounded by concurrency.
26
+ */
27
+ const inFlightTcTokenIssuance = new Set();
28
+ const userDevicesCache = config.userDevicesCache ||
29
+ new NodeCache({
30
+ stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
31
+ useClones: false
32
+ });
33
+ /** Serializes writes to userDevicesCache across USync refresh and device-notification handling. */
34
+ const devicesMutex = makeMutex();
35
+ // Initialize message retry manager if enabled
36
+ const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
37
+ // Prevent race conditions in Signal session encryption by user
38
+ const encryptionMutex = makeKeyedMutex();
39
+ let mediaConn;
40
+ /** Per-socket media host; updated whenever media_conn is fetched. Defaults to the public WhatsApp host. */
41
+ let mediaHost = DEF_MEDIA_HOST;
65
42
  const refreshMediaConn = async (forceGet = false) => {
66
- const media = await mediaConn
67
- if (!media || forceGet || (new Date().getTime() - media.fetchDate.getTime()) > media.ttl * 1000) {
43
+ const media = await mediaConn;
44
+ if (!media || forceGet || new Date().getTime() - media.fetchDate.getTime() > media.ttl * 1000) {
68
45
  mediaConn = (async () => {
69
46
  const result = await query({
70
47
  tag: 'iq',
71
48
  attrs: {
72
49
  type: 'set',
73
50
  xmlns: 'w:m',
74
- to: WABinary_1.S_WHATSAPP_NET,
51
+ to: S_WHATSAPP_NET
75
52
  },
76
- content: [{
77
- tag: 'media_conn',
78
- attrs: {}
79
- }]
80
- })
81
- const mediaConnNode = WABinary_1.getBinaryNodeChild(result, 'media_conn')
53
+ content: [{ tag: 'media_conn', attrs: {} }]
54
+ });
55
+ const mediaConnNode = getBinaryNodeChild(result, 'media_conn');
56
+ // TODO: explore full length of data that whatsapp provides
82
57
  const node = {
83
- hosts: WABinary_1.getBinaryNodeChildren(mediaConnNode, 'host').map(({
84
- attrs
85
- }) => ({
58
+ hosts: getBinaryNodeChildren(mediaConnNode, 'host').map(({ attrs }) => ({
86
59
  hostname: attrs.hostname,
87
- maxContentLengthBytes: +attrs.maxContentLengthBytes,
60
+ maxContentLengthBytes: +attrs.maxContentLengthBytes
88
61
  })),
89
62
  auth: mediaConnNode.attrs.auth,
90
63
  ttl: +mediaConnNode.attrs.ttl,
91
64
  fetchDate: new Date()
65
+ };
66
+ logger.debug('fetched media conn');
67
+ if (node.hosts[0]) {
68
+ mediaHost = node.hosts[0].hostname;
92
69
  }
93
- logger.debug('fetched media conn')
94
- return node
95
- })()
70
+ return node;
71
+ })();
96
72
  }
97
- return mediaConn
98
- }
99
-
73
+ return mediaConn;
74
+ };
75
+ /**
76
+ * generic send receipt function
77
+ * used for receipts of phone call, read, delivery etc.
78
+ * */
100
79
  const sendReceipt = async (jid, participant, messageIds, type) => {
80
+ if (!messageIds || messageIds.length === 0) {
81
+ throw new Boom('missing ids in receipt');
82
+ }
101
83
  const node = {
102
84
  tag: 'receipt',
103
85
  attrs: {
104
- id: messageIds[0],
105
- },
106
- }
107
- const isReadReceipt = type === 'read' || type === 'read-self'
86
+ id: messageIds[0]
87
+ }
88
+ };
89
+ const isReadReceipt = type === 'read' || type === 'read-self';
108
90
  if (isReadReceipt) {
109
- node.attrs.t = Utils_1.unixTimestampSeconds().toString()
91
+ node.attrs.t = unixTimestampSeconds().toString();
92
+ }
93
+ if (type === 'sender' && (isPnUser(jid) || isLidUser(jid))) {
94
+ node.attrs.recipient = jid;
95
+ node.attrs.to = participant;
110
96
  }
111
- if (type === 'sender' && WABinary_1.isJidUser(jid)) {
112
- node.attrs.recipient = jid
113
- node.attrs.to = participant
114
- } else {
115
- node.attrs.to = jid
97
+ else {
98
+ node.attrs.to = jid;
116
99
  if (participant) {
117
- node.attrs.participant = participant
100
+ node.attrs.participant = participant;
118
101
  }
119
102
  }
120
103
  if (type) {
121
- node.attrs.type = WABinary_1.isJidNewsletter(jid) ? 'read-self' : type
104
+ node.attrs.type = type;
122
105
  }
123
- const remainingMessageIds = messageIds.slice(1)
106
+ const remainingMessageIds = messageIds.slice(1);
124
107
  if (remainingMessageIds.length) {
125
- node.content = [{
126
- tag: 'list',
127
- attrs: {},
128
- content: remainingMessageIds.map(id => ({
129
- tag: 'item',
130
- attrs: {
131
- id
132
- }
133
- }))
134
- }]
108
+ node.content = [
109
+ {
110
+ tag: 'list',
111
+ attrs: {},
112
+ content: remainingMessageIds.map(id => ({
113
+ tag: 'item',
114
+ attrs: { id }
115
+ }))
116
+ }
117
+ ];
135
118
  }
136
- logger.debug({
137
- attrs: node.attrs,
138
- messageIds
139
- }, 'sending receipt for messages')
140
- await sendNode(node)
141
- }
142
-
119
+ logger.debug({ attrs: node.attrs, messageIds }, 'sending receipt for messages');
120
+ await sendNode(node);
121
+ };
122
+ /** Correctly bulk send receipts to multiple chats, participants */
143
123
  const sendReceipts = async (keys, type) => {
144
- const recps = Utils_1.aggregateMessageKeysNotFromMe(keys)
124
+ const recps = aggregateMessageKeysNotFromMe(keys);
145
125
  for (const { jid, participant, messageIds } of recps) {
146
- await sendReceipt(jid, participant, messageIds, type)
126
+ await sendReceipt(jid, participant, messageIds, type);
147
127
  }
148
- }
149
-
128
+ };
129
+ /** Bulk read messages. Keys can be from different chats & participants */
150
130
  const readMessages = async (keys) => {
151
- const privacySettings = await fetchPrivacySettings()
152
- const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self'
153
- await sendReceipts(keys, readType)
154
- }
155
-
156
- const deduplicateLidPnJids = (jids) => {
157
- const lidUsers = new Set()
158
- const filteredJids = []
159
-
160
- for (const jid of jids) {
161
- if (WABinary_1.isLidUser(jid)) {
162
- const user = WABinary_1.jidDecode(jid)?.user
163
- if (user) lidUsers.add(user)
164
- }
165
- }
166
-
167
- for (const jid of jids) {
168
- if (WABinary_1.isJidUser(jid)) {
169
- const user = WABinary_1.jidDecode(jid)?.user
170
- if (user && lidUsers.has(user)) {
171
- logger.debug({
172
- jid
173
- }, 'Skipping PN - LID version exists')
174
- continue
175
- }
176
- }
177
- filteredJids.push(jid)
178
- }
179
- return filteredJids
180
- }
181
-
182
- const profilePictureUrl = async (jid) => {
183
- if (WABinary_1.isJidNewsletter(jid)) {
184
- let node = await newsletterWMexQuery(undefined, Types_1.QueryIds.METADATA, {
185
- input: {
186
- key: jid,
187
- type: 'JID',
188
- view_role: 'GUEST'
189
- },
190
- fetch_viewer_metadata: true,
191
- fetch_full_image: true,
192
- fetch_creation_time: true
193
- })
194
- let result = WABinary_1.getBinaryNodeChild(node, 'result')?.content?.toString()
195
- let metadata = JSON.parse(result).data[Types_1.XWAPaths.NEWSLETTER]
196
- return Utils_1.getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
197
- } else {
198
- const result = await query({
199
- tag: 'iq',
200
- attrs: {
201
- target: WABinary_1.jidNormalizedUser(jid),
202
- to: WABinary_1.S_WHATSAPP_NET,
203
- type: 'get',
204
- xmlns: 'w:profile:picture'
205
- },
206
- content: [{
207
- tag: 'picture',
208
- attrs: {
209
- type: 'image',
210
- query: 'url'
211
- }
212
- }]
213
- })
214
- const child = WABinary_1.getBinaryNodeChild(result, 'picture')
215
- return child?.attrs?.url || null
216
- }
217
- }
218
-
131
+ const privacySettings = await fetchPrivacySettings();
132
+ // based on privacy settings, we have to change the read type
133
+ const readType = privacySettings.readreceipts === 'all' ? 'read' : 'read-self';
134
+ await sendReceipts(keys, readType);
135
+ };
136
+ /** Fetch all the devices we've to send a message to */
219
137
  const getUSyncDevices = async (jids, useCache, ignoreZeroDevices) => {
220
- const deviceResults = []
221
-
138
+ const deviceResults = [];
222
139
  if (!useCache) {
223
- logger.debug('not using cache for devices')
140
+ logger.debug('not using cache for devices');
224
141
  }
225
-
226
- const toFetch = []
227
-
228
- jids = deduplicateLidPnJids(Array.from(new Set(jids)))
142
+ const toFetch = [];
229
143
  const jidsWithUser = jids
230
144
  .map(jid => {
231
- const decoded = WABinary_1.jidDecode(jid)
232
- const user = decoded?.user
233
- const device = decoded?.device
234
- const isExplicitDevice = typeof device === 'number' && device >= 0
235
-
236
- if (isExplicitDevice && user) {
237
- deviceResults.push({
238
- user,
239
- device,
240
- wireJid: jid
241
- });
242
- return null
243
- }
244
-
245
- jid = WABinary_1.jidNormalizedUser(jid)
246
- return {
247
- jid,
248
- user
249
- }
250
- })
251
- .filter(jid => jid !== null)
252
-
253
- let mgetDevices
145
+ const decoded = jidDecode(jid);
146
+ const user = decoded?.user;
147
+ const device = decoded?.device;
148
+ const isExplicitDevice = typeof device === 'number' && device >= 0;
149
+ if (isExplicitDevice && user) {
150
+ deviceResults.push({
151
+ user,
152
+ device,
153
+ jid
154
+ });
155
+ return null;
156
+ }
157
+ jid = jidNormalizedUser(jid);
158
+ return { jid, user };
159
+ })
160
+ .filter(jid => jid !== null);
161
+ let mgetDevices;
254
162
  if (useCache && userDevicesCache.mget) {
255
- const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean)
256
- mgetDevices = await userDevicesCache.mget(usersToFetch)
163
+ const usersToFetch = jidsWithUser.map(j => j?.user).filter(Boolean);
164
+ mgetDevices = await userDevicesCache.mget(usersToFetch);
257
165
  }
258
-
259
166
  for (const { jid, user } of jidsWithUser) {
260
167
  if (useCache) {
261
- const devices = mgetDevices?.[user] || (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)))
168
+ const devices = mgetDevices?.[user] ||
169
+ (userDevicesCache.mget ? undefined : (await userDevicesCache.get(user)));
262
170
  if (devices) {
263
- const isLidJid = WABinary_1.isLidUser(jid)
264
- const devicesWithWire = devices.map(d => ({
171
+ const devicesWithJid = devices.map(d => ({
265
172
  ...d,
266
- wireJid: isLidJid ? WABinary_1.jidEncode(d.user, 'lid', d.device) : WABinary_1.jidEncode(d.user, 's.whatsapp.net', d.device)
267
- }))
268
- deviceResults.push(...devicesWithWire)
269
- logger.trace({
270
- user
271
- }, 'using cache for devices')
272
- } else {
273
- toFetch.push(jid)
173
+ jid: jidEncode(d.user, d.server, d.device)
174
+ }));
175
+ deviceResults.push(...devicesWithJid);
176
+ logger.trace({ user }, 'using cache for devices');
274
177
  }
275
- } else {
276
- toFetch.push(jid)
178
+ else {
179
+ toFetch.push(jid);
180
+ }
181
+ }
182
+ else {
183
+ toFetch.push(jid);
277
184
  }
278
185
  }
279
-
280
186
  if (!toFetch.length) {
281
- return deviceResults
187
+ return deviceResults;
282
188
  }
283
-
284
- const requestedLidUsers = new Set()
189
+ const requestedLidUsers = new Set();
285
190
  for (const jid of toFetch) {
286
- if (WABinary_1.isLidUser(jid)) {
287
- const user = WABinary_1.jidDecode(jid)?.user
288
- if (user) requestedLidUsers.add(user)
191
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
192
+ const user = jidDecode(jid)?.user;
193
+ if (user)
194
+ requestedLidUsers.add(user);
289
195
  }
290
196
  }
291
-
292
- const query = new WAUSync_1.USyncQuery().withContext('message').withDeviceProtocol()
197
+ const query = new USyncQuery().withContext('message').withDeviceProtocol().withLIDProtocol();
293
198
  for (const jid of toFetch) {
294
- query.withUser(new WAUSync_1.USyncUser().withId(jid))
199
+ query.withUser(new USyncUser().withId(jid)); // todo: investigate - the idea here is that <user> should have an inline lid field with the lid being the pn equivalent
295
200
  }
296
-
297
- const result = await executeUSyncQuery(query)
201
+ const result = await sock.executeUSyncQuery(query);
298
202
  if (result) {
299
- // FIX: pontalabs sebelumnya gak pernah nyimpen mapping LID<->PN
300
- // dari hasil USync device query ini (nuiisweety nyimpen + langsung
301
- // force-refresh session buat LID yang baru ketemu). Tanpa ini,
302
- // pengiriman ke kontak yang butuh LID addressing bisa tetep pakai
303
- // address PN yang salah/basi -> penerima gak bisa decrypt balasan
304
- // bot ("Menunggu pesan ini...").
305
- const lidResults = (result.list || []).filter(item => !!item.lid)
203
+ // TODO: LID MAP this stuff (lid protocol will now return lid with devices)
204
+ const lidResults = result.list.filter(a => !!a.lid);
306
205
  if (lidResults.length > 0) {
206
+ logger.trace('Storing LID maps from device call');
207
+ await signalRepository.lidMapping.storeLIDPNMappings(lidResults.map(a => ({ lid: a.lid, pn: a.id })));
208
+ // Force-refresh sessions for newly mapped LIDs to align identity addressing
307
209
  try {
308
- await signalRepository.lidMapping.storeLIDPNMappings(
309
- lidResults.map(item => ({ lid: item.lid, pn: item.id }))
310
- )
311
- const lids = lidResults.map(item => item.lid)
312
- await assertSessions(lids, true)
313
- } catch (error) {
314
- logger.warn({ error, count: lidResults.length }, 'failed to store/refresh LID mappings from device sync')
210
+ const lids = lidResults.map(a => a.lid);
211
+ if (lids.length) {
212
+ await assertSessions(lids, true);
213
+ }
214
+ }
215
+ catch (e) {
216
+ logger.warn({ e, count: lidResults.length }, 'failed to assert sessions for newly mapped LIDs');
315
217
  }
316
218
  }
317
- const extracted = Utils_1.extractDeviceJids(result?.list, authState.creds.me.id, ignoreZeroDevices)
318
- const deviceMap = {}
219
+ const extracted = extractDeviceJids(result?.list, authState.creds.me.id, authState.creds.me.lid, ignoreZeroDevices);
220
+ const deviceMap = {};
319
221
  for (const item of extracted) {
320
- deviceMap[item.user] = deviceMap[item.user] || []
321
- deviceMap[item.user]?.push(item)
222
+ deviceMap[item.user] = deviceMap[item.user] || [];
223
+ deviceMap[item.user]?.push(item);
322
224
  }
225
+ // Process each user's devices as a group for bulk LID migration
323
226
  for (const [user, userDevices] of Object.entries(deviceMap)) {
324
- const isLidUser = requestedLidUsers.has(user)
227
+ const isLidUser = requestedLidUsers.has(user);
228
+ // Process all devices for this user
325
229
  for (const item of userDevices) {
326
- const finalWireJid = isLidUser ? WABinary_1.jidEncode(user, 'lid', item.device) : WABinary_1.jidEncode(item.user, 's.whatsapp.net', item.device)
230
+ const finalJid = isLidUser
231
+ ? jidEncode(user, item.server, item.device)
232
+ : jidEncode(item.user, item.server, item.device);
327
233
  deviceResults.push({
328
234
  ...item,
329
- wireJid: finalWireJid
235
+ jid: finalJid
330
236
  });
331
237
  logger.debug({
332
238
  user: item.user,
333
239
  device: item.device,
334
- finalWireJid,
240
+ finalJid,
335
241
  usedLid: isLidUser
336
- }, 'Processed device with LID priority')
242
+ }, 'Processed device with LID priority');
337
243
  }
338
244
  }
339
-
340
245
  await devicesMutex.mutex(async () => {
341
246
  if (userDevicesCache.mset) {
342
- await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({
343
- key,
344
- value
345
- })))
346
- } else {
247
+ // if the cache supports mset, we can set all devices in one go
248
+ await userDevicesCache.mset(Object.entries(deviceMap).map(([key, value]) => ({ key, value })));
249
+ }
250
+ else {
347
251
  for (const key in deviceMap) {
348
- if (deviceMap[key]) await userDevicesCache.set(key, deviceMap[key])
252
+ if (deviceMap[key])
253
+ await userDevicesCache.set(key, deviceMap[key]);
349
254
  }
350
255
  }
351
- })
256
+ });
257
+ const userDeviceUpdates = {};
258
+ for (const [userId, devices] of Object.entries(deviceMap)) {
259
+ if (devices && devices.length > 0) {
260
+ userDeviceUpdates[userId] = devices.map(d => d.device?.toString() || '0');
261
+ }
262
+ }
263
+ if (Object.keys(userDeviceUpdates).length > 0) {
264
+ try {
265
+ await authState.keys.set({ 'device-list': userDeviceUpdates });
266
+ logger.debug({ userCount: Object.keys(userDeviceUpdates).length }, 'stored user device lists for bulk migration');
267
+ }
268
+ catch (error) {
269
+ logger.warn({ error }, 'failed to store user device lists');
270
+ }
271
+ }
352
272
  }
353
- return deviceResults
354
- }
355
-
273
+ return deviceResults;
274
+ };
275
+ /**
276
+ * Update Member Label
277
+ */
278
+ const updateMemberLabel = (jid, memberLabel) => {
279
+ return relayMessage(jid, {
280
+ protocolMessage: {
281
+ type: proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE,
282
+ memberLabel: {
283
+ label: memberLabel?.slice(0, 30),
284
+ labelTimestamp: unixTimestampSeconds()
285
+ }
286
+ }
287
+ }, {
288
+ additionalNodes: [
289
+ {
290
+ tag: 'meta',
291
+ attrs: {
292
+ tag_reason: 'user_update',
293
+ appdata: 'member_tag'
294
+ },
295
+ content: undefined
296
+ }
297
+ ]
298
+ });
299
+ };
356
300
  const assertSessions = async (jids, force) => {
357
301
  let didFetchNewSession = false;
358
- let jidsRequiringFetch = [];
359
-
360
- if (force) {
361
- jidsRequiringFetch = jids;
362
- } else {
363
- // FIX: sebelumnya cuma ngecek "ada record tersimpan atau enggak"
364
- // (truthy check di authState.keys.get('session', ...)). Record
365
- // yang kosong/stale/rusak (misal device grup yang session-nya
366
- // kebentuk tapi gak pernah kepakai/keisi ratchet) tetep truthy,
367
- // jadi dianggap "udah punya sesi" padahal libsignal-nya beneran
368
- // kosong -> pas encrypt muncul "SessionError: No sessions".
369
- // validateSession ngecek session-nya beneran ada & "open" enggak,
370
- // bukan cuma record-nya ada.
371
- for (const jid of jids) {
302
+ const uniqueJids = [...new Set(jids)];
303
+ const jidsRequiringFetch = [];
304
+ logger.debug({ jids }, 'assertSessions call with jids');
305
+ for (const jid of uniqueJids) {
306
+ if (!force) {
372
307
  const sessionValidation = await signalRepository.validateSession(jid);
373
- if (!sessionValidation.exists) {
374
- jidsRequiringFetch.push(jid);
308
+ if (sessionValidation.exists) {
309
+ continue;
375
310
  }
376
311
  }
312
+ jidsRequiringFetch.push(jid);
377
313
  }
378
-
379
314
  if (jidsRequiringFetch.length) {
380
- logger.debug({ jidsRequiringFetch }, 'fetching sessions');
381
-
315
+ // LID if mapped, otherwise original
316
+ const wireJids = [
317
+ ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
318
+ ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
319
+ ];
320
+ logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions');
382
321
  const result = await query({
383
322
  tag: 'iq',
384
323
  attrs: {
385
324
  xmlns: 'encrypt',
386
325
  type: 'get',
387
- to: WABinary_1.S_WHATSAPP_NET,
326
+ to: S_WHATSAPP_NET
388
327
  },
389
328
  content: [
390
329
  {
391
330
  tag: 'key',
392
331
  attrs: {},
393
- content: jidsRequiringFetch.map(jid => ({
394
- tag: 'user',
395
- attrs: { jid },
396
- }))
332
+ content: wireJids.map(jid => {
333
+ const attrs = { jid };
334
+ if (force)
335
+ attrs.reason = 'identity';
336
+ return { tag: 'user', attrs };
337
+ })
397
338
  }
398
339
  ]
399
340
  });
400
- await (0, Utils_1.parseAndInjectE2ESessions)(result, signalRepository);
341
+ await parseAndInjectE2ESessions(result, signalRepository);
401
342
  didFetchNewSession = true;
402
343
  }
403
344
  return didFetchNewSession;
404
345
  };
405
-
406
346
  const sendPeerDataOperationMessage = async (pdoMessage) => {
347
+ //TODO: for later, abstract the logic to send a Peer Message instead of just PDO - useful for App State Key Resync with phone
407
348
  if (!authState.creds.me?.id) {
408
- throw new boom_1.Boom('Not authenticated')
349
+ throw new Boom('Not authenticated');
409
350
  }
410
-
411
351
  const protocolMessage = {
412
352
  protocolMessage: {
413
353
  peerDataOperationRequestMessage: pdoMessage,
414
- type: WAProto_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
354
+ type: proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
415
355
  }
416
- }
417
-
418
- const meJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
356
+ };
357
+ const meJid = jidNormalizedUser(authState.creds.me.id);
419
358
  const msgId = await relayMessage(meJid, protocolMessage, {
420
359
  additionalAttributes: {
421
360
  category: 'peer',
422
- push_priority: 'high_force',
361
+ push_priority: 'high_force'
423
362
  },
424
- })
425
- return msgId
426
- }
427
-
428
- const createParticipantNodes = async (jids, message, extraAttrs) => {
429
- let patched = await patchMessageBeforeSending(message, jids);
430
-
431
- if (!Array.isArray(patched)) {
432
- patched = jids ? jids.map(jid => ({ recipientJid: jid, ...patched })) : [patched];
363
+ additionalNodes: [
364
+ {
365
+ tag: 'meta',
366
+ attrs: { appdata: 'default' }
367
+ }
368
+ ]
369
+ });
370
+ return msgId;
371
+ };
372
+ const createParticipantNodes = async (recipientJids, message, extraAttrs, dsmMessage) => {
373
+ if (!recipientJids.length) {
374
+ return { nodes: [], shouldIncludeDeviceIdentity: false };
433
375
  }
434
-
376
+ const patched = await patchMessageBeforeSending(message, recipientJids);
377
+ const patchedMessages = Array.isArray(patched)
378
+ ? patched
379
+ : recipientJids.map(jid => ({ recipientJid: jid, message: patched }));
435
380
  let shouldIncludeDeviceIdentity = false;
436
- const nodes = await Promise.all(patched.map(async (patchedMessageWithJid) => {
437
- const { recipientJid: jid, ...patchedMessage } = patchedMessageWithJid;
438
- if (!jid) {
439
- return {};
440
- }
441
-
442
- const bytes = (0, Utils_1.encodeWAMessage)(patchedMessage);
443
- // FIX: encryptMessage sebelumnya dipanggil tanpa mutex, padahal
444
- // encryptionMutex sudah ada tapi gak dipakai. Kalau ada 2 pesan
445
- // dikirim hampir bersamaan ke jid yang sama (misal "processing..."
446
- // lalu hasil), encryptMessage bisa race saat baca-ubah-tulis session
447
- // Signal, merusak ratchet -> salah satu pesan jadi gak bisa
448
- // didekripsi penerima ("Menunggu pesan ini..." nyangkut selamanya).
449
- const node = await encryptionMutex.mutex(jid, async () => {
450
- const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
451
- if (type === 'pkmsg') {
452
- shouldIncludeDeviceIdentity = true;
381
+ const meId = authState.creds.me.id;
382
+ const meLid = authState.creds.me?.lid;
383
+ const meLidUser = meLid ? jidDecode(meLid)?.user : null;
384
+ const encryptionPromises = patchedMessages.map(async ({ recipientJid: jid, message: patchedMessage }) => {
385
+ try {
386
+ if (!jid)
387
+ return null;
388
+ let msgToEncrypt = patchedMessage;
389
+ if (dsmMessage) {
390
+ const { user: targetUser } = jidDecode(jid);
391
+ const { user: ownPnUser } = jidDecode(meId);
392
+ const ownLidUser = meLidUser;
393
+ const isOwnUser = targetUser === ownPnUser || (ownLidUser && targetUser === ownLidUser);
394
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
395
+ if (isOwnUser && !isExactSenderDevice) {
396
+ msgToEncrypt = dsmMessage;
397
+ logger.debug({ jid, targetUser }, 'Using DSM for own device');
398
+ }
453
399
  }
454
- return {
455
- tag: 'to',
456
- attrs: { jid },
457
- content: [{
458
- tag: 'enc',
459
- attrs: {
460
- v: '2',
461
- type,
462
- ...extraAttrs || {}
463
- },
464
- content: ciphertext
465
- }]
466
- };
467
- });
468
- return node;
469
- }));
400
+ const bytes = encodeWAMessage(msgToEncrypt);
401
+ const mutexKey = jid;
402
+ const node = await encryptionMutex.mutex(mutexKey, async () => {
403
+ const { type, ciphertext } = await signalRepository.encryptMessage({ jid, data: bytes });
404
+ if (type === 'pkmsg') {
405
+ shouldIncludeDeviceIdentity = true;
406
+ }
407
+ return {
408
+ tag: 'to',
409
+ attrs: { jid },
410
+ content: [
411
+ {
412
+ tag: 'enc',
413
+ attrs: { v: '2', type, ...(extraAttrs || {}) },
414
+ content: ciphertext
415
+ }
416
+ ]
417
+ };
418
+ });
419
+ return node;
420
+ }
421
+ catch (err) {
422
+ logger.error({ jid, err }, 'Failed to encrypt for recipient');
423
+ return null;
424
+ }
425
+ });
426
+ const nodes = (await Promise.all(encryptionPromises)).filter(node => node !== null);
427
+ if (recipientJids.length > 0 && nodes.length === 0) {
428
+ throw new Boom('All encryptions failed', { statusCode: 500 });
429
+ }
470
430
  return { nodes, shouldIncludeDeviceIdentity };
471
431
  };
472
-
473
- const relayMessage = async (jid, message, {
474
- messageId: msgId,
475
- participant,
476
- additionalAttributes,
477
- useUserDevicesCache,
478
- useCachedGroupMetadata,
479
- statusJidList,
480
- additionalNodes,
481
- ai = false
482
- }) => {
483
- const meId = authState.creds.me.id
484
- const meLid = authState.creds.me?.lid
485
-
486
- let additionalAlready = false
487
- let shouldIncludeDeviceIdentity = false
488
-
489
- const {
490
- user,
491
- server
492
- } = WABinary_1.jidDecode(jid)
493
-
494
- const statusJid = 'status@broadcast'
495
- const isGroup = server === 'g.us'
496
- const isPrivate = server === 's.whatsapp.net'
497
- const isNewsletter = server == 'newsletter'
498
- const isStatus = jid === statusJid
499
- const isLid = server === 'lid'
500
-
501
- const finalJid = jid
502
-
503
- let ownId = meId
504
-
505
- if (isLid && meLid) {
506
- ownId = meLid
507
- logger.debug({
508
- to: jid,
509
- ownId
510
- }, 'Using LID identity for @lid conversation')
511
- } else {
512
- logger.debug({
513
- to: jid,
514
- ownId
515
- }, 'Using PN identity for @s.whatsapp.net conversation')
516
- }
517
-
518
- msgId = msgId || Utils_1.generateMessageID(authState.creds.me.id)
519
- useUserDevicesCache = useUserDevicesCache !== false
520
- useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus
521
-
522
- const participants = []
523
- const destinationJid = !isStatus ? finalJid : statusJid
524
- const binaryNodeContent = []
525
- const devices = []
526
-
432
+ const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, additionalNodes, useUserDevicesCache, useCachedGroupMetadata, statusJidList }) => {
433
+ const meId = assertMeId(authState.creds);
434
+ const meLid = authState.creds.me?.lid;
435
+ const isRetryResend = Boolean(participant?.jid);
436
+ let shouldIncludeDeviceIdentity = isRetryResend;
437
+ const statusJid = 'status@broadcast';
438
+ const { user, server } = jidDecode(jid);
439
+ const isGroup = server === 'g.us';
440
+ const isStatus = jid === statusJid;
441
+ const isLid = server === 'lid';
442
+ const isNewsletter = server === 'newsletter';
443
+ const isGroupOrStatus = isGroup || isStatus;
444
+ const finalJid = jid;
445
+ msgId = msgId || generateMessageIDV2(meId);
446
+ useUserDevicesCache = useUserDevicesCache !== false;
447
+ useCachedGroupMetadata = useCachedGroupMetadata !== false && !isStatus;
448
+ const participants = [];
449
+ const destinationJid = !isStatus ? finalJid : statusJid;
450
+ const binaryNodeContent = [];
451
+ const devices = [];
452
+ let reportingMessage;
527
453
  const meMsg = {
528
454
  deviceSentMessage: {
529
455
  destinationJid,
530
456
  message
531
457
  },
532
- messageContextInfo: message.messageContextInfo || {}
533
- }
534
-
535
- const extraAttrs = {}
536
- const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
537
- const messages = Utils_1.normalizeMessageContent(message)
538
- const buttonType = getButtonType(messages)
539
- const pollMessage = messages.pollCreationMessage || messages.pollCreationMessageV2 || messages.pollCreationMessageV3
540
-
458
+ messageContextInfo: message.messageContextInfo
459
+ };
460
+ const extraAttrs = {};
541
461
  if (participant) {
542
462
  if (!isGroup && !isStatus) {
543
- additionalAttributes = {
544
- ...additionalAttributes,
545
- 'device_fanout': 'false'
546
- }
463
+ additionalAttributes = { ...additionalAttributes, device_fanout: 'false' };
547
464
  }
548
-
549
- const {
550
- user,
551
- device
552
- } = WABinary_1.jidDecode(participant.jid)
553
-
465
+ const { user, device } = jidDecode(participant.jid);
554
466
  devices.push({
555
467
  user,
556
468
  device,
557
- wireJid: participant.jid
558
- })
469
+ jid: participant.jid
470
+ });
559
471
  }
560
-
561
472
  await authState.keys.transaction(async () => {
562
- const mediaType = getMediaType(messages)
473
+ const normalizedMsg = normalizeMessageContent(message);
474
+ const isGroupStatusAudio = !!(message?.groupStatusMessageV2?.message?.audioMessage);
475
+ const mediaType = isGroupStatusAudio ? undefined : getMediaType(normalizedMsg || message);
563
476
  if (mediaType) {
564
- extraAttrs['mediatype'] = mediaType
477
+ extraAttrs['mediatype'] = mediaType;
565
478
  }
566
-
567
- if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
568
- extraAttrs['decrypt-fail'] = 'hide'
479
+ if (isNewsletter) {
480
+ const patched = patchMessageBeforeSending ? await patchMessageBeforeSending(message, []) : message;
481
+ // Evaluate mediaType from patched message (after patchMessageBeforeSending),
482
+ // and place it in plaintext attrs — NOT in stanza attrs (WA server requires it there)
483
+ const newsletterMediaType = getMediaType(normalizeMessageContent(patched) || patched);
484
+ if (newsletterMediaType) {
485
+ extraAttrs['mediatype'] = newsletterMediaType;
486
+ }
487
+ if (patched.interactiveMessage && !extraAttrs['mediatype']) {
488
+ extraAttrs['mediatype'] = 'interactive';
489
+ }
490
+ const bytes = encodeNewsletterMessage(patched);
491
+ binaryNodeContent.push({
492
+ tag: 'plaintext',
493
+ attrs: extraAttrs,
494
+ content: bytes
495
+ });
496
+ const stanza = {
497
+ tag: 'message',
498
+ attrs: {
499
+ to: jid,
500
+ id: msgId,
501
+ type: getMessageType(patched),
502
+ ...(additionalAttributes || {})
503
+ },
504
+ content: binaryNodeContent
505
+ };
506
+ logger.debug({ msgId, mediaType: newsletterMediaType, extraAttrs }, `sending newsletter message to ${jid}`);
507
+ await sendNode(stanza);
508
+ return;
569
509
  }
570
-
571
- if (messages.interactiveResponseMessage?.nativeFlowResponseMessage) {
572
- extraAttrs['native_flow_name'] = messages.interactiveResponseMessage.nativeFlowResponseMessage?.name || 'menu_options'
510
+ if (normalizedMsg?.pinInChatMessage || normalizedMsg?.reactionMessage) {
511
+ extraAttrs['decrypt-fail'] = 'hide'; // todo: expand for reactions and other types
573
512
  }
574
-
575
- if (isGroup || isStatus) {
513
+ if (isGroupOrStatus && !isRetryResend) {
576
514
  const [groupData, senderKeyMap] = await Promise.all([
577
515
  (async () => {
578
- let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined
516
+ let groupData = useCachedGroupMetadata && cachedGroupMetadata ? await cachedGroupMetadata(jid) : undefined; // todo: should we rely on the cache specially if the cache is outdated and the metadata has new fields?
579
517
  if (groupData && Array.isArray(groupData?.participants)) {
580
- logger.trace({
581
- jid,
582
- participants: groupData.participants.length
583
- }, 'using cached group metadata')
584
- } else if (!isStatus) {
585
- groupData = await global.groupMetadataCache(jid)
518
+ logger.trace({ jid, participants: groupData.participants.length }, 'using cached group metadata');
586
519
  }
587
- return groupData
520
+ else if (!isStatus) {
521
+ groupData = await groupMetadata(jid); // TODO: start storing group participant list + addr mode in Signal & stop relying on this
522
+ }
523
+ return groupData;
588
524
  })(),
589
525
  (async () => {
590
526
  if (!participant && !isStatus) {
591
- const result = await authState.keys.get('sender-key-memory', [jid])
592
- return result[jid] || {}
527
+ // what if sender memory is less accurate than the cached metadata
528
+ // on participant change in group, we should do sender memory manipulation
529
+ const result = await authState.keys.get('sender-key-memory', [jid]); // TODO: check out what if the sender key memory doesn't include the LID stuff now?
530
+ return result[jid] || {};
593
531
  }
594
- return {}
532
+ return {};
595
533
  })()
596
- ])
597
-
598
- if (!participant) {
599
- const participantsList = (groupData && !isStatus) ? groupData.participants.map(p => p.id) : []
600
- if (isStatus && statusJidList) {
601
- participantsList.push(...statusJidList)
602
- }
603
- if (!isStatus) {
604
- const groupAddressingMode = groupData?.addressingMode || (isLid ? Types_1.WAMessageAddressingMode.LID : Types_1.WAMessageAddressingMode.PN)
605
- additionalAttributes = {
606
- ...additionalAttributes,
607
- addressing_mode: groupAddressingMode
608
- }
609
- }
610
- const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false)
611
- devices.push(...additionalDevices)
534
+ ]);
535
+ const participantsList = groupData ? groupData.participants.map(p => p.id) : [];
536
+ if (groupData?.ephemeralDuration && groupData.ephemeralDuration > 0) {
537
+ additionalAttributes = {
538
+ ...additionalAttributes,
539
+ expiration: groupData.ephemeralDuration.toString()
540
+ };
541
+ }
542
+ if (isStatus && statusJidList) {
543
+ participantsList.push(...statusJidList);
544
+ }
545
+ const additionalDevices = await getUSyncDevices(participantsList, !!useUserDevicesCache, false);
546
+ devices.push(...additionalDevices);
547
+ if (isGroup) {
548
+ additionalAttributes = {
549
+ ...additionalAttributes,
550
+ addressing_mode: groupData?.addressingMode || 'lid'
551
+ };
552
+ }
553
+ const patched = await patchMessageBeforeSending(message);
554
+ if (Array.isArray(patched)) {
555
+ throw new Boom('Per-jid patching is not supported in groups');
612
556
  }
613
-
614
- const patched = await patchMessageBeforeSending(message, devices.map(d => WABinary_1.jidEncode(d.user, isLid ? 'lid' : 's.whatsapp.net', d.device)))
615
- const bytes = Utils_1.encodeWAMessage(patched)
616
-
617
- const groupAddressingMode = groupData?.addressingMode || (isLid ? 'lid' : 'pn')
618
- const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId
619
-
620
- const {
621
- ciphertext,
622
- senderKeyDistributionMessage
623
- } = await signalRepository.encryptGroupMessage({
557
+ const bytes = encodeWAMessage(patched);
558
+ reportingMessage = patched;
559
+ const groupAddressingMode = additionalAttributes?.['addressing_mode'] || groupData?.addressingMode || 'lid';
560
+ const groupSenderIdentity = groupAddressingMode === 'lid' && meLid ? meLid : meId;
561
+ const { ciphertext, senderKeyDistributionMessage } = await signalRepository.encryptGroupMessage({
624
562
  group: destinationJid,
625
563
  data: bytes,
626
564
  meId: groupSenderIdentity
627
- })
628
-
629
- const senderKeyJids = []
630
-
565
+ });
566
+ const senderKeyRecipients = [];
631
567
  for (const device of devices) {
632
- const deviceJid = device.wireJid
633
- const hasKey = !!senderKeyMap[deviceJid]
634
- if (!hasKey || !!participant) {
635
- senderKeyJids.push(deviceJid)
636
- senderKeyMap[deviceJid] = true
568
+ const deviceJid = device.jid;
569
+ const hasKey = !!senderKeyMap[deviceJid];
570
+ if ((!hasKey || !!participant) &&
571
+ !isHostedLidUser(deviceJid) &&
572
+ !isHostedPnUser(deviceJid) &&
573
+ device.device !== 99) {
574
+ //todo: revamp all this logic
575
+ // the goal is to follow with what I said above for each group, and instead of a true false map of ids, we can set an array full of those the app has already sent pkmsgs
576
+ senderKeyRecipients.push(deviceJid);
577
+ senderKeyMap[deviceJid] = true;
637
578
  }
638
579
  }
639
-
640
- if (senderKeyJids.length) {
641
- logger.debug({
642
- senderKeyJids
643
- }, 'sending new sender key')
580
+ if (senderKeyRecipients.length) {
581
+ logger.debug({ senderKeyJids: senderKeyRecipients }, 'sending new sender key');
644
582
  const senderKeyMsg = {
645
583
  senderKeyDistributionMessage: {
646
584
  axolotlSenderKeyDistributionMessage: senderKeyDistributionMessage,
647
585
  groupId: destinationJid
648
586
  }
649
- }
650
-
651
- await assertSessions(senderKeyJids, false)
652
- const result = await createParticipantNodes(senderKeyJids, senderKeyMsg, extraAttrs)
653
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity
654
- participants.push(...result.nodes)
587
+ };
588
+ const senderKeySessionTargets = senderKeyRecipients;
589
+ await assertSessions(senderKeySessionTargets);
590
+ const result = await createParticipantNodes(senderKeyRecipients, senderKeyMsg, extraAttrs);
591
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || result.shouldIncludeDeviceIdentity;
592
+ participants.push(...result.nodes);
655
593
  }
656
-
657
594
  binaryNodeContent.push({
658
595
  tag: 'enc',
659
- attrs: {
660
- v: '2',
661
- type: 'skmsg',
662
- ...extraAttrs
663
- },
596
+ attrs: { v: '2', type: 'skmsg', ...extraAttrs },
664
597
  content: ciphertext
665
- })
666
-
667
- await authState.keys.set({
668
- 'sender-key-memory': {
669
- [jid]: senderKeyMap
670
- }
671
- })
672
- } else if (isNewsletter) {
673
- if (message.protocolMessage?.editedMessage) {
674
- msgId = message.protocolMessage.key?.id
675
- message = message.protocolMessage.editedMessage
598
+ });
599
+ await authState.keys.set({ 'sender-key-memory': { [jid]: senderKeyMap } });
600
+ }
601
+ else {
602
+ // ADDRESSING CONSISTENCY: Match own identity to conversation context
603
+ // TODO: investigate if this is true
604
+ let ownId = meId;
605
+ if (isLid && meLid) {
606
+ ownId = meLid;
607
+ logger.debug({ to: jid, ownId }, 'Using LID identity for @lid conversation');
676
608
  }
677
-
678
- if (message.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE) {
679
- msgId = message.protocolMessage.key?.id
680
- message = {}
609
+ else {
610
+ logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation');
681
611
  }
682
-
683
- const patched = await patchMessageBeforeSending(message, [])
684
- const bytes = Utils_1.encodeNewsletterMessage(patched)
685
-
686
- binaryNodeContent.push({
687
- tag: 'plaintext',
688
- attrs: extraAttrs,
689
- content: bytes
690
- })
691
- } else {
692
- const {
693
- user: ownUser
694
- } = WABinary_1.jidDecode(ownId)
695
-
612
+ const { user: ownUser } = jidDecode(ownId);
696
613
  if (!participant) {
697
- const targetUserServer = isLid ? 'lid' : 's.whatsapp.net'
614
+ const patchedForReporting = await patchMessageBeforeSending(message, [jid]);
615
+ reportingMessage = Array.isArray(patchedForReporting)
616
+ ? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
617
+ : patchedForReporting;
618
+ }
619
+ if (!isRetryResend) {
620
+ const targetUserServer = isLid ? 'lid' : 's.whatsapp.net';
698
621
  devices.push({
699
622
  user,
700
623
  device: 0,
701
- wireJid: WABinary_1.jidEncode(user, targetUserServer, 0)
702
- })
703
-
624
+ jid: jidEncode(user, targetUserServer, 0) // rajeh, todo: this entire logic is convoluted and weird.
625
+ });
704
626
  if (user !== ownUser) {
705
627
  const ownUserServer = isLid ? 'lid' : 's.whatsapp.net';
706
- const ownUserForAddressing = isLid && meLid ? WABinary_1.jidDecode(meLid).user : WABinary_1.jidDecode(meId).user
628
+ const ownUserForAddressing = isLid && meLid ? jidDecode(meLid).user : jidDecode(meId).user;
707
629
  devices.push({
708
630
  user: ownUserForAddressing,
709
631
  device: 0,
710
- wireJid: WABinary_1.jidEncode(ownUserForAddressing, ownUserServer, 0)
711
- })
632
+ jid: jidEncode(ownUserForAddressing, ownUserServer, 0)
633
+ });
712
634
  }
713
-
714
635
  if (additionalAttributes?.['category'] !== 'peer') {
715
- devices.length = 0
716
- const senderIdentity = isLid && meLid ? WABinary_1.jidEncode(WABinary_1.jidDecode(meLid)?.user, 'lid', undefined) : WABinary_1.jidEncode(WABinary_1.jidDecode(meId)?.user, 's.whatsapp.net', undefined)
717
- const sessionDevices = await getUSyncDevices([senderIdentity, jid], false, false)
718
- devices.push(...sessionDevices)
636
+ // Clear placeholders and enumerate actual devices
637
+ devices.length = 0;
638
+ // Use conversation-appropriate sender identity
639
+ const senderIdentity = isLid && meLid
640
+ ? jidEncode(jidDecode(meLid)?.user, 'lid', undefined)
641
+ : jidEncode(jidDecode(meId)?.user, 's.whatsapp.net', undefined);
642
+ // Enumerate devices for sender and target with consistent addressing
643
+ const sessionDevices = await getUSyncDevices([senderIdentity, jid], true, false);
644
+ devices.push(...sessionDevices);
719
645
  logger.debug({
720
646
  deviceCount: devices.length,
721
- devices: devices.map(d => `${d.user}:${d.device}@${WABinary_1.jidDecode(d.wireJid)?.server}`)
722
- }, 'Device enumeration complete with unified addressing')
647
+ devices: devices.map(d => `${d.user}:${d.device}@${jidDecode(d.jid)?.server}`)
648
+ }, 'Device enumeration complete with unified addressing');
723
649
  }
724
650
  }
725
-
726
- const allJids = []
727
- const meJids = []
728
- const otherJids = []
729
-
730
- const {
731
- user: mePnUser
732
- } = WABinary_1.jidDecode(meId)
733
- const {
734
- user: meLidUser
735
- } = meLid ? WABinary_1.jidDecode(meLid) : {
736
- user: null
737
- }
738
-
739
- for (const { user, wireJid } of devices) {
740
- const isExactSenderDevice = wireJid === meId || (meLid && wireJid === meLid)
651
+ const allRecipients = [];
652
+ const meRecipients = [];
653
+ const otherRecipients = [];
654
+ const { user: mePnUser } = jidDecode(meId);
655
+ const { user: meLidUser } = meLid ? jidDecode(meLid) : { user: null };
656
+ for (const { user, jid } of devices) {
657
+ const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
741
658
  if (isExactSenderDevice) {
742
- logger.debug({
743
- wireJid,
744
- meId,
745
- meLid
746
- }, 'Skipping exact sender device (whatsmeow pattern)')
747
- continue
659
+ logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
660
+ continue;
748
661
  }
749
- const isMe = user === mePnUser || (meLidUser && user === meLidUser)
750
- const jid = wireJid
662
+ // Check if this is our device (could match either PN or LID user)
663
+ const isMe = user === mePnUser || user === meLidUser;
751
664
  if (isMe) {
752
- meJids.push(jid)
753
- } else {
754
- otherJids.push(jid)
665
+ meRecipients.push(jid);
755
666
  }
756
- allJids.push(jid)
667
+ else {
668
+ otherRecipients.push(jid);
669
+ }
670
+ allRecipients.push(jid);
757
671
  }
758
-
759
- await assertSessions([...otherJids, ...meJids], false)
760
- const [{
761
- nodes: meNodes,
762
- shouldIncludeDeviceIdentity: s1
763
- }, {
764
- nodes: otherNodes,
765
- shouldIncludeDeviceIdentity: s2
766
- }] = await Promise.all([
767
- createParticipantNodes(meJids, meMsg || message, extraAttrs),
768
- createParticipantNodes(otherJids, message, extraAttrs, meMsg)
769
- ])
770
-
771
- participants.push(...meNodes)
772
- participants.push(...otherNodes)
773
- if (meJids.length > 0 || otherJids.length > 0) {
774
- extraAttrs['phash'] = Utils_1.generateParticipantHashV2([...meJids, ...otherJids])
672
+ await assertSessions(allRecipients);
673
+ const [{ nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 }] = await Promise.all([
674
+ // For own devices: use DSM if available (1:1 chats only)
675
+ createParticipantNodes(meRecipients, meMsg || message, extraAttrs),
676
+ createParticipantNodes(otherRecipients, message, extraAttrs, meMsg)
677
+ ]);
678
+ participants.push(...meNodes);
679
+ participants.push(...otherNodes);
680
+ if (meRecipients.length > 0 || otherRecipients.length > 0) {
681
+ extraAttrs['phash'] = generateParticipantHashV2([...meRecipients, ...otherRecipients]);
775
682
  }
776
- shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
683
+ shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2;
684
+ }
685
+ if (isRetryResend) {
686
+ const isParticipantLid = isLidUser(participant.jid);
687
+ const isMe = areJidsSameUser(participant.jid, isParticipantLid ? meLid : meId);
688
+ let messageToSend = message;
689
+ if (isGroupOrStatus) {
690
+ let groupSenderIdentity;
691
+ if (meLid && (await signalRepository.hasSenderKey({ group: destinationJid, meId: meLid }))) {
692
+ groupSenderIdentity = meLid;
693
+ }
694
+ else if (await signalRepository.hasSenderKey({ group: destinationJid, meId })) {
695
+ groupSenderIdentity = meId;
696
+ }
697
+ if (groupSenderIdentity) {
698
+ try {
699
+ const skdm = await signalRepository.getSenderKeyDistributionMessage({
700
+ group: destinationJid,
701
+ meId: groupSenderIdentity
702
+ });
703
+ messageToSend = {
704
+ ...message,
705
+ senderKeyDistributionMessage: {
706
+ groupId: destinationJid,
707
+ axolotlSenderKeyDistributionMessage: skdm
708
+ }
709
+ };
710
+ }
711
+ catch (err) {
712
+ logger.warn({ err, jid: destinationJid }, 'failed to build SKDM for retry, sending without it');
713
+ }
714
+ }
715
+ }
716
+ const encodedMessageToSend = isMe
717
+ ? encodeWAMessage({
718
+ deviceSentMessage: {
719
+ destinationJid,
720
+ message: messageToSend
721
+ }
722
+ })
723
+ : encodeWAMessage(messageToSend);
724
+ const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
725
+ data: encodedMessageToSend,
726
+ jid: participant.jid
727
+ });
728
+ binaryNodeContent.push({
729
+ tag: 'enc',
730
+ attrs: {
731
+ v: '2',
732
+ type,
733
+ count: participant.count.toString()
734
+ },
735
+ content: encryptedContent
736
+ });
777
737
  }
778
-
779
738
  if (participants.length) {
780
739
  if (additionalAttributes?.['category'] === 'peer') {
781
- const peerNode = participants[0]?.content?.[0]
740
+ const peerNode = participants[0]?.content?.[0];
782
741
  if (peerNode) {
783
- binaryNodeContent.push(peerNode)
742
+ binaryNodeContent.push(peerNode); // push only enc
784
743
  }
785
- } else {
744
+ }
745
+ else {
786
746
  binaryNodeContent.push({
787
747
  tag: 'participants',
788
748
  attrs: {},
789
749
  content: participants
790
- })
750
+ });
791
751
  }
792
752
  }
793
-
794
753
  const stanza = {
795
754
  tag: 'message',
796
755
  attrs: {
797
- to: destinationJid,
798
756
  id: msgId,
799
- type: getTypeMessage(message),
757
+ to: destinationJid,
758
+ type: getMessageType(message),
800
759
  ...(additionalAttributes || {})
801
760
  },
802
761
  content: binaryNodeContent
803
- }
804
-
762
+ };
763
+ // if the participant to send to is explicitly specified (generally retry recp)
764
+ // ensure the message is only sent to that person
765
+ // if a retry receipt is sent to everyone -- it'll fail decryption for everyone else who received the msg
805
766
  if (participant) {
806
- if (WABinary_1.isJidGroup(destinationJid)) {
807
- stanza.attrs.to = destinationJid
808
- stanza.attrs.participant = participant.jid
809
- } else if (WABinary_1.areJidsSameUser(participant.jid, meId)) {
810
- stanza.attrs.to = participant.jid
811
- stanza.attrs.recipient = destinationJid
812
- } else {
813
- stanza.attrs.to = participant.jid
767
+ if (isJidGroup(destinationJid)) {
768
+ stanza.attrs.to = destinationJid;
769
+ stanza.attrs.participant = participant.jid;
770
+ }
771
+ else if (areJidsSameUser(participant.jid, meId)) {
772
+ stanza.attrs.to = participant.jid;
773
+ stanza.attrs.recipient = destinationJid;
814
774
  }
815
- } else {
816
- stanza.attrs.to = destinationJid
775
+ else {
776
+ stanza.attrs.to = participant.jid;
777
+ }
778
+ }
779
+ else {
780
+ stanza.attrs.to = destinationJid;
817
781
  }
818
-
819
782
  if (shouldIncludeDeviceIdentity) {
783
+ ;
820
784
  stanza.content.push({
821
785
  tag: 'device-identity',
822
786
  attrs: {},
823
- content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
824
- })
825
- logger.debug({
826
- jid
827
- }, 'adding device identity')
828
- }
829
-
830
- if (isGroup && regexGroupOld.test(jid) && !message.reactionMessage) {
831
- stanza.content.push({
832
- tag: 'multicast',
833
- attrs: {}
834
- })
787
+ content: encodeSignedDeviceIdentity(authState.creds.account, true)
788
+ });
789
+ logger.debug({ jid }, 'adding device identity');
835
790
  }
836
-
837
- if (pollMessage || messages.eventMessage) {
838
- let attrs = {};
839
- if (messages.eventMessage) {
840
- attrs.event_type = 'creation';
841
- } else {
842
- attrs.polltype = 'creation';
843
- if (isNewsletter) {
844
- attrs.contenttype = (pollMessage && pollMessage.pollContentType === 2) ? 'image' : 'text';
791
+ if (!isNewsletter &&
792
+ !isRetryResend &&
793
+ reportingMessage?.messageContextInfo?.messageSecret &&
794
+ shouldIncludeReportingToken(reportingMessage)) {
795
+ try {
796
+ const encoded = encodeWAMessage(reportingMessage);
797
+ const reportingKey = {
798
+ id: msgId,
799
+ fromMe: true,
800
+ remoteJid: destinationJid,
801
+ participant: participant?.jid
802
+ };
803
+ const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey);
804
+ if (reportingNode) {
805
+ ;
806
+ stanza.content.push(reportingNode);
807
+ logger.trace({ jid }, 'added reporting token to message');
845
808
  }
846
809
  }
810
+ catch (error) {
811
+ logger.warn({ jid, trace: error?.stack }, 'failed to attach reporting token');
812
+ }
813
+ }
814
+ // WA Web never attaches tctoken to peer (AppStateSync) messages — server rejects with 479
815
+ const isPeerMessage = additionalAttributes?.['category'] === 'peer';
816
+ const is1on1Send = !isGroup && !isRetryResend && !isStatus && !isNewsletter && !isPeerMessage;
817
+ // Resolve destination to LID for tctoken storage — matches Signal session key pattern
818
+ const tcTokenJid = is1on1Send ? await resolveTcTokenJid(destinationJid, getLIDForPN) : destinationJid;
819
+ const contactTcTokenData = is1on1Send ? await authState.keys.get('tctoken', [tcTokenJid]) : {};
820
+ const existingTokenEntry = contactTcTokenData[tcTokenJid];
821
+ let tcTokenBuffer = existingTokenEntry?.token;
822
+ // Treat expired tokens the same as missing — clear from cache
823
+ if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
824
+ logger.debug({ jid: destinationJid, timestamp: existingTokenEntry?.timestamp }, 'tctoken expired, clearing');
825
+ tcTokenBuffer = undefined;
826
+ // Preserve senderTimestamp so the fire-and-forget issuance dedupe survives cleanup.
827
+ const cleared = existingTokenEntry?.senderTimestamp !== undefined
828
+ ? { token: Buffer.alloc(0), senderTimestamp: existingTokenEntry.senderTimestamp }
829
+ : null;
830
+ try {
831
+ await authState.keys.set({ tctoken: { [tcTokenJid]: cleared } });
832
+ }
833
+ catch (err) {
834
+ logger.debug({ jid: destinationJid, err: err?.message }, 'failed to persist tctoken expiry cleanup');
835
+ }
836
+ }
837
+ if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
838
+ ;
847
839
  stanza.content.push({
848
- tag: 'meta',
849
- attrs: attrs
840
+ tag: 'tctoken',
841
+ attrs: {},
842
+ content: tcTokenBuffer
850
843
  });
851
844
  }
852
-
853
- if (!isNewsletter && buttonType) {
854
- const buttonsNode = getButtonArgs(messages)
855
- const filteredButtons = WABinary_1.getBinaryFilteredButtons(additionalNodes ? additionalNodes : [])
856
- if (filteredButtons) {
857
- stanza.content.push(...additionalNodes)
858
- additionalAlready = true
859
- } else {
860
- stanza.content.push(buttonsNode)
861
- }
845
+ if (additionalNodes && additionalNodes.length > 0) {
846
+ ;
847
+ stanza.content.push(...additionalNodes);
862
848
  }
863
-
864
- if (ai && isPrivate) {
865
- const botNode = {
866
- tag: 'bot',
867
- attrs: {
868
- biz_bot: '1'
869
- }
870
- }
871
- const filteredBizBot = WABinary_1.getBinaryFilteredBizBot(additionalNodes ? additionalNodes : [])
872
- if (filteredBizBot) {
873
- stanza.content.push(...additionalNodes)
874
- additionalAlready = true
875
- } else {
876
- stanza.content.push(botNode)
849
+ // Inject <biz> node required for buttons/interactive/list messages to render on WA
850
+ const _bizContent = normalizeMessageContent(message);
851
+ const hasExistingBizNode = additionalNodes && additionalNodes.some(node => node.tag === 'biz');
852
+ if (!hasExistingBizNode && (isGroup || !isGroup && !isStatus && !isNewsletter)) {
853
+ const bizNode = getBizBinaryNode(_bizContent || message);
854
+ if (bizNode) {
855
+ stanza.content.push(bizNode);
877
856
  }
878
857
  }
879
-
880
- if (!additionalAlready && additionalNodes && additionalNodes.length > 0) {
881
- stanza.content.push(...additionalNodes)
858
+ logger.debug({ msgId }, `sending message to ${participants.length} devices`);
859
+ await sendNode(stanza);
860
+ // Fire-and-forget: issue our token to the contact AFTER message send.
861
+ // WA Web skips protocol messages and PSA/bot contacts (TcTokenChatAction: isRegularUser)
862
+ const isProtocolMsg = !!normalizeMessageContent(message)?.protocolMessage;
863
+ const isBotOrPSA = destinationJid === PSA_WID || isJidBot(destinationJid) || isJidMetaAI(destinationJid);
864
+ if (is1on1Send &&
865
+ !isProtocolMsg &&
866
+ !isBotOrPSA &&
867
+ shouldSendNewTcToken(existingTokenEntry?.senderTimestamp) &&
868
+ !inFlightTcTokenIssuance.has(tcTokenJid)) {
869
+ inFlightTcTokenIssuance.add(tcTokenJid);
870
+ const issueTimestamp = unixTimestampSeconds();
871
+ const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
872
+ resolveIssuanceJid(destinationJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID)
873
+ .then(issueJid => issuePrivacyTokens([issueJid], issueTimestamp))
874
+ .then(async (result) => {
875
+ await storeTcTokensFromIqResult({
876
+ result,
877
+ fallbackJid: tcTokenJid,
878
+ keys: authState.keys,
879
+ getLIDForPN
880
+ });
881
+ const currentData = await authState.keys.get('tctoken', [tcTokenJid]);
882
+ const currentEntry = currentData[tcTokenJid];
883
+ const indexWrite = await buildMergedTcTokenIndexWrite(authState.keys, [tcTokenJid]);
884
+ await authState.keys.set({
885
+ tctoken: {
886
+ [tcTokenJid]: {
887
+ token: Buffer.alloc(0),
888
+ ...currentEntry,
889
+ senderTimestamp: issueTimestamp
890
+ },
891
+ ...indexWrite
892
+ }
893
+ });
894
+ })
895
+ .catch(err => {
896
+ logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed');
897
+ })
898
+ .finally(() => {
899
+ inFlightTcTokenIssuance.delete(tcTokenJid);
900
+ });
882
901
  }
883
-
884
- logger.debug({
885
- msgId
886
- }, `sending message to ${participants.length} devices`)
887
-
888
- await sendNode(stanza)
902
+ // Add message to retry cache if enabled
889
903
  if (messageRetryManager && !participant) {
890
- messageRetryManager.addRecentMessage(destinationJid, msgId, message)
904
+ messageRetryManager.addRecentMessage(destinationJid, msgId, message);
891
905
  }
892
- }, meId)
893
- return msgId
894
- }
895
-
896
- const getTypeMessage = (msg) => {
897
- const message = Utils_1.normalizeMessageContent(msg)
898
- if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) {
899
- return 'poll'
900
- } else if (message.reactionMessage) {
901
- return 'reaction'
902
- } else if (message.eventMessage) {
903
- return 'event'
904
- } else if (getMediaType(message)) {
905
- return 'media'
906
- } else {
907
- return 'text'
906
+ }, meId);
907
+ return msgId;
908
+ };
909
+ const getMessageType = (message) => {
910
+ const normalizedMessage = normalizeMessageContent(message);
911
+ if (!normalizedMessage)
912
+ return 'text';
913
+ if (normalizedMessage.reactionMessage || normalizedMessage.encReactionMessage) {
914
+ return 'reaction';
915
+ }
916
+ if (normalizedMessage.pollCreationMessage ||
917
+ normalizedMessage.pollCreationMessageV2 ||
918
+ normalizedMessage.pollCreationMessageV3 ||
919
+ normalizedMessage.pollUpdateMessage) {
920
+ return 'poll';
921
+ }
922
+ if (normalizedMessage.eventMessage) {
923
+ return 'event';
908
924
  }
909
- }
910
-
925
+ // Audio wrapped in groupStatusMessageV2 → type "text" (Baileys behavior)
926
+ const isGroupStatusAudio = !!(message?.groupStatusMessageV2?.message?.audioMessage);
927
+ if (!isGroupStatusAudio && getMediaType(normalizedMessage) !== '') {
928
+ return 'media';
929
+ }
930
+ return 'text';
931
+ };
911
932
  const getMediaType = (message) => {
912
933
  if (message.imageMessage) {
913
- return 'image'
914
- } else if (message.stickerMessage) {
915
- return message.stickerMessage.isLottie ? '1p_sticker' : message.stickerMessage.isAvatar ? 'avatar_sticker' : 'sticker'
916
- } else if (message.videoMessage) {
917
- return message.videoMessage.gifPlayback ? 'gif' : 'video'
918
- } else if (message.audioMessage) {
919
- return message.audioMessage.ptt ? 'ptt' : 'audio'
920
- } else if (message.ptvMessage) {
921
- return 'ptv'
922
- } else if (message.albumMessage) {
923
- return 'collection'
924
- } else if (message.contactMessage) {
925
- return 'vcard'
926
- } else if (message.documentMessage) {
927
- return 'document'
928
- } else if (message.stickerPackMessage) {
929
- return 'sticker_pack'
930
- } else if (message.contactsArrayMessage) {
931
- return 'contact_array'
932
- } else if (message.locationMessage) {
933
- return 'location'
934
- } else if (message.liveLocationMessage) {
935
- return 'livelocation'
936
- } else if (message.listMessage) {
937
- return 'list'
938
- } else if (message.listResponseMessage) {
939
- return 'list_response'
940
- } else if (message.buttonsResponseMessage) {
941
- return 'buttons_response'
942
- } else if (message.orderMessage) {
943
- return 'order'
944
- } else if (message.productMessage) {
945
- return 'product'
946
- } else if (message.interactiveResponseMessage) {
947
- return 'native_flow_response'
948
- } else if (/https:\/\/wa\.me\/c\/\d+/.test(message.extendedTextMessage?.text)) {
949
- return 'cataloglink'
950
- } else if (/https:\/\/wa\.me\/p\/\d+\/\d+/.test(message.extendedTextMessage?.text)) {
951
- return 'productlink'
952
- } else if (message.extendedTextMessage?.matchedText || message.groupInviteMessage) {
953
- return 'url'
934
+ return 'image';
935
+ }
936
+ else if (message.videoMessage) {
937
+ return message.videoMessage.gifPlayback ? 'gif' : 'video';
938
+ }
939
+ else if (message.audioMessage) {
940
+ return message.audioMessage.ptt ? 'ptt' : 'audio';
941
+ }
942
+ else if (message.contactMessage) {
943
+ return 'vcard';
944
+ }
945
+ else if (message.documentMessage) {
946
+ return 'document';
954
947
  }
955
- }
956
-
957
- const getButtonType = (message) => {
958
- const message_content = message.viewOnceMessage?.message || message;
959
- if (message_content.listMessage) {
948
+ else if (message.contactsArrayMessage) {
949
+ return 'contact_array';
950
+ }
951
+ else if (message.liveLocationMessage) {
952
+ return 'livelocation';
953
+ }
954
+ else if (message.stickerMessage) {
955
+ return 'sticker';
956
+ }
957
+ else if (message.listMessage) {
960
958
  return 'list';
961
- } else if (message_content.buttonsMessage) {
962
- return 'buttons';
963
- } else if (message_content.interactiveMessage?.nativeFlowMessage) {
964
- return 'native_flow';
965
959
  }
966
- };
967
-
968
- const getButtonArgs = (message) => {
969
- const message_content = message.viewOnceMessage?.message || message;
970
- const message_flow = message_content.interactiveMessage?.nativeFlowMessage;
971
- const flow_buttons_first = message_flow?.buttons?.[0]?.name;
972
- const flow_buttons_special = [ 'mpm', 'cta_catalog', 'send_location', 'call_permission_request', 'wa_payment_transaction_details', 'automated_greeting_message_view_catalog' ];
973
- const baseArgs = {
974
- tag: 'biz',
975
- attrs: {
976
- actual_actors: '2',
977
- host_storage: '2',
978
- privacy_mode_ts: Utils_1.unixTimestampSeconds().toString()
979
- }
980
- };
981
- if (message_flow && (flow_buttons_first === 'review_and_pay' || flow_buttons_first === 'payment_info')) {
982
- return {
983
- tag: 'biz',
984
- attrs: {
985
- native_flow_name: flow_buttons_first === 'review_and_pay' ? 'order_details' : flow_buttons_first
986
- }
987
- };
960
+ else if (message.listResponseMessage) {
961
+ return 'list_response';
988
962
  }
989
- if (message_flow && flow_buttons_special.includes(flow_buttons_first)) {
990
- return {
991
- ...baseArgs,
992
- content: [
993
- {
994
- tag: 'interactive',
995
- attrs: { type: 'native_flow', v: '1' },
996
- content: [{
997
- tag: 'native_flow',
998
- attrs: { v: '2', name: flow_buttons_first }
999
- }]
1000
- },
1001
- {
1002
- tag: 'quality_control',
1003
- attrs: { source_type: 'third_party' }
1004
- }
1005
- ]
1006
- };
963
+ else if (message.buttonsResponseMessage) {
964
+ return 'buttons_response';
1007
965
  }
1008
- if (message_flow || message_content.buttonsMessage) {
1009
- return {
1010
- ...baseArgs,
1011
- content: [
1012
- {
1013
- tag: 'interactive',
1014
- attrs: { type: 'native_flow', v: '1' },
1015
- content: [{
1016
- tag: 'native_flow',
1017
- attrs: { v: '9', name: 'mixed' }
1018
- }]
1019
- },
1020
- {
1021
- tag: 'quality_control',
1022
- attrs: { source_type: 'third_party' }
1023
- }
1024
- ]
1025
- };
966
+ else if (message.orderMessage) {
967
+ return 'order';
1026
968
  }
1027
- if (message_content.listMessage) {
1028
- return {
1029
- ...baseArgs,
1030
- content: [
1031
- {
1032
- tag: 'list',
1033
- attrs: { v: '2', type: 'product_list' }
1034
- },
1035
- {
1036
- tag: 'quality_control',
1037
- attrs: { source_type: 'third_party' }
1038
- }
1039
- ]
1040
- };
969
+ else if (message.productMessage) {
970
+ return 'product';
971
+ }
972
+ else if (message.interactiveResponseMessage) {
973
+ return 'native_flow_response';
1041
974
  }
1042
- return baseArgs;
975
+ else if (message.groupInviteMessage) {
976
+ return 'url';
977
+ }
978
+ return '';
1043
979
  };
1044
-
1045
- const getPrivacyTokens = async (jids) => {
1046
- const t = Utils_1.unixTimestampSeconds().toString()
1047
-
980
+ const issuePrivacyTokens = async (jids, timestamp) => {
981
+ const t = (timestamp ?? unixTimestampSeconds()).toString();
1048
982
  const result = await query({
1049
983
  tag: 'iq',
1050
984
  attrs: {
1051
- to: WABinary_1.S_WHATSAPP_NET,
985
+ to: S_WHATSAPP_NET,
1052
986
  type: 'set',
1053
987
  xmlns: 'privacy'
1054
988
  },
1055
- content: [{
1056
- tag: 'tokens',
1057
- attrs: {},
1058
- content: jids.map(jid => ({
1059
- tag: 'token',
1060
- attrs: {
1061
- jid: WABinary_1.jidNormalizedUser(jid),
1062
- t,
1063
- type: 'trusted_contact'
1064
- }
1065
- }))
1066
- }]
1067
- })
1068
-
1069
- return result
1070
- }
1071
-
1072
- const getEphemeralGroup = (jid) => {
1073
- if (!WABinary_1.isJidGroup(jid)) throw new TypeError("Jid should originate from a group!")
1074
-
1075
- return groupQuery(jid, 'get', [{
1076
- tag: 'query',
1077
- attrs: {
1078
- request: 'interactive'
989
+ content: [
990
+ {
991
+ tag: 'tokens',
992
+ attrs: {},
993
+ content: jids.map(jid => ({
994
+ tag: 'token',
995
+ attrs: {
996
+ jid: jidNormalizedUser(jid),
997
+ t,
998
+ type: 'trusted_contact'
999
+ }
1000
+ }))
1079
1001
  }
1080
- }])
1081
- .then((groups) => WABinary_1.getBinaryNodeChild(groups, 'group'))
1082
- .then((metadata) => WABinary_1.getBinaryNodeChild(metadata, 'ephemeral')?.attrs?.expiration || 0)
1083
- }
1084
-
1085
- const waUploadToServer = Utils_1.getWAUploadToServer(config, refreshMediaConn)
1086
-
1087
- const waitForMsgMediaUpdate = Utils_1.bindWaitForEvent(ev, 'messages.media-update')
1088
-
1002
+ ]
1003
+ });
1004
+ return result;
1005
+ };
1006
+ const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
1007
+ const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
1008
+ // Initialize kikyy (group story / extended message handler)
1009
+ const rahmi = new kikyy({ generateWAMessageContent, generateWAMessageFromContent }, waUploadToServer, relayMessage, authState.creds.me?.id);
1010
+ registerSocketEndHandler(() => {
1011
+ if (!config.userDevicesCache && userDevicesCache.close) {
1012
+ userDevicesCache.close();
1013
+ }
1014
+ mediaConn = undefined;
1015
+ if (messageRetryManager) {
1016
+ messageRetryManager.clear();
1017
+ }
1018
+ });
1089
1019
  return {
1090
1020
  ...sock,
1091
- getPrivacyTokens,
1021
+ userDevicesCache,
1022
+ devicesMutex,
1023
+ issuePrivacyTokens,
1092
1024
  assertSessions,
1093
1025
  relayMessage,
1094
1026
  sendReceipt,
1095
1027
  sendReceipts,
1096
1028
  readMessages,
1097
- profilePictureUrl,
1098
- getUSyncDevices,
1099
1029
  refreshMediaConn,
1030
+ // Function (not getter) so the spread in chats.ts preserves the live closure binding.
1031
+ getMediaHost: () => mediaHost,
1100
1032
  waUploadToServer,
1101
- getEphemeralGroup,
1102
1033
  fetchPrivacySettings,
1103
- messageRetryManager,
1104
- createParticipantNodes,
1105
1034
  sendPeerDataOperationMessage,
1035
+ createParticipantNodes,
1036
+ getUSyncDevices,
1037
+ messageRetryManager,
1038
+ updateMemberLabel,
1106
1039
  updateMediaMessage: async (message) => {
1107
- const content = Utils_1.assertMediaContent(message.message)
1108
- const mediaKey = content.mediaKey
1109
- const meId = authState.creds.me.id
1110
- const node = await Utils_1.encryptMediaRetryRequest(message.key, mediaKey, meId)
1111
- let error = undefined
1112
-
1040
+ const content = assertMediaContent(message.message);
1041
+ const mediaKey = content.mediaKey;
1042
+ const meId = authState.creds.me.id;
1043
+ const node = encryptMediaRetryRequest(message.key, mediaKey, meId);
1044
+ let error = undefined;
1113
1045
  await Promise.all([
1114
1046
  sendNode(node),
1115
1047
  waitForMsgMediaUpdate(async (update) => {
1116
- const result = update.find(c => c.key.id === message.key.id)
1048
+ const result = update.find(c => c.key.id === message.key.id);
1117
1049
  if (result) {
1118
1050
  if (result.error) {
1119
- error = result.error
1120
- } else {
1051
+ error = result.error;
1052
+ }
1053
+ else {
1121
1054
  try {
1122
- const media = await Utils_1.decryptMediaRetryData(result.media, mediaKey, result.key.id)
1123
-
1124
- if (media.result !== WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS) {
1125
- const resultStr = WAProto_1.proto.MediaRetryNotification.ResultType[media.result]
1126
-
1127
- throw new boom_1.Boom(`Media re-upload failed by device (${resultStr})`, {
1055
+ const media = decryptMediaRetryData(result.media, mediaKey, result.key.id);
1056
+ if (media.result !== proto.MediaRetryNotification.ResultType.SUCCESS) {
1057
+ const resultStr = proto.MediaRetryNotification.ResultType[media.result];
1058
+ throw new Boom(`Media re-upload failed by device (${resultStr})`, {
1128
1059
  data: media,
1129
- statusCode: Utils_1.getStatusCodeForMediaRetry(media.result) || 404
1130
- })
1060
+ statusCode: getStatusCodeForMediaRetry(media.result) || 404
1061
+ });
1131
1062
  }
1132
-
1133
- content.directPath = media.directPath
1134
-
1135
- content.url = Utils_1.getUrlFromDirectPath(content.directPath)
1136
-
1137
- logger.debug({
1138
- directPath: media.directPath,
1139
- key: result.key
1140
- }, 'media update successful')
1141
- } catch (err) {
1142
- error = err
1063
+ content.directPath = media.directPath;
1064
+ content.url = getUrlFromDirectPath(content.directPath, mediaHost);
1065
+ logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful');
1066
+ }
1067
+ catch (err) {
1068
+ error = err;
1143
1069
  }
1144
1070
  }
1145
-
1146
- return true
1071
+ return true;
1147
1072
  }
1148
1073
  })
1149
- ])
1150
-
1074
+ ]);
1151
1075
  if (error) {
1152
- throw error
1076
+ throw error;
1153
1077
  }
1154
-
1155
- ev.emit('messages.update', [{
1156
- key: message.key,
1157
- update: {
1158
- message: message.message
1159
- }
1160
- }])
1161
-
1162
- return message
1078
+ ev.emit('messages.update', [{ key: message.key, update: { message: message.message } }]);
1079
+ return message;
1163
1080
  },
1164
- sendStatusMentions: async (content, jids = []) => {
1165
- const MAX_STATUS_MENTIONS = 5
1166
- if (jids.length > MAX_STATUS_MENTIONS) {
1167
- logger.warn(
1168
- { requested: jids.length, max: MAX_STATUS_MENTIONS },
1169
- `sendStatusMentions: max ${MAX_STATUS_MENTIONS} mentions per status, truncating`
1170
- )
1171
- jids = jids.slice(0, MAX_STATUS_MENTIONS)
1172
- }
1173
- const userJid = WABinary_1.jidNormalizedUser(authState.creds.me.id)
1174
- let allUsers = new Set()
1175
- allUsers.add(userJid)
1176
-
1177
- for (const id of jids) {
1178
- const isGroup = WABinary_1.isJidGroup(id)
1179
- const isPrivate = WABinary_1.isJidUser(id)
1180
-
1181
- if (isGroup) {
1182
- try {
1183
- const metadata = await cachedGroupMetadata(id) || await global.groupMetadataCache(id)
1184
- const participants = metadata.participants.map(p => WABinary_1.jidNormalizedUser(p.id))
1185
- participants.forEach(jid => allUsers.add(jid))
1186
- } catch (error) {
1187
- logger.error(`Error getting metadata for group ${id}: ${error}`)
1188
- }
1189
- } else if (isPrivate) {
1190
- allUsers.add(WABinary_1.jidNormalizedUser(id))
1191
- }
1192
- }
1193
-
1194
- const uniqueUsers = Array.from(allUsers)
1195
- const getRandomHexColor = () => "#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")
1196
-
1197
- const isMedia = content.image || content.video || content.audio
1198
- const isAudio = !!content.audio
1199
-
1200
- const messageContent = {
1201
- ...content
1202
- }
1203
-
1204
- if (isMedia && !isAudio) {
1205
- if (messageContent.text) {
1206
- messageContent.caption = messageContent.text
1207
-
1208
- delete messageContent.text
1209
- }
1210
-
1211
- delete messageContent.ptt
1212
- delete messageContent.font
1213
- delete messageContent.backgroundColor
1214
- delete messageContent.textColor
1215
- }
1216
-
1217
- if (isAudio) {
1218
- delete messageContent.text
1219
- delete messageContent.caption
1220
- delete messageContent.font
1221
- delete messageContent.textColor
1222
- }
1223
-
1224
- const font = !isMedia ? (content.font || Math.floor(Math.random() * 9)) : undefined
1225
- const textColor = !isMedia ? (content.textColor || getRandomHexColor()) : undefined
1226
- const backgroundColor = (!isMedia || isAudio) ? (content.backgroundColor || getRandomHexColor()) : undefined
1227
- const ptt = isAudio ? (typeof content.ptt === 'boolean' ? content.ptt : true) : undefined
1228
-
1229
- let msg
1230
- let mediaHandle
1231
- try {
1232
- msg = await Utils_1.generateWAMessage(WABinary_1.STORIES_JID, messageContent, {
1081
+ sendMessage: async (jid, content, options = {}) => {
1082
+ const userJid = authState.creds.me.id;
1083
+ // Update rahmi's userJid in case it changed after login
1084
+ if (rahmi && userJid) rahmi.userJid = userJid;
1085
+ if (Array.isArray(jid)) {
1086
+ const { delayMs = 1500 } = options;
1087
+ const allUsers = new Set();
1088
+ const fullMsg = await generateWAMessage('status@broadcast', content, {
1233
1089
  logger,
1234
1090
  userJid,
1235
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1236
- thumbnailWidth: linkPreviewImageThumbnailWidth,
1237
- fetchOpts: {
1238
- timeout: 3000,
1239
- ...axiosOptions || {}
1240
- },
1241
- logger,
1242
- uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1243
- }),
1244
- upload: async (encFilePath, opts) => {
1245
- const up = await waUploadToServer(encFilePath, {
1246
- ...opts
1247
- })
1248
- mediaHandle = up.handle
1249
- return up
1250
- },
1091
+ upload: waUploadToServer,
1251
1092
  mediaCache: config.mediaCache,
1252
1093
  options: config.options,
1253
- font,
1254
- textColor,
1255
- backgroundColor,
1256
- ptt
1257
- })
1258
- } catch (error) {
1259
- logger.error(`Error generating message: ${error}`)
1260
- throw error
1261
- }
1262
-
1263
- await relayMessage(WABinary_1.STORIES_JID, msg.message, {
1264
- messageId: msg.key.id,
1265
- statusJidList: uniqueUsers,
1266
- additionalNodes: [{
1267
- tag: 'meta',
1268
- attrs: {},
1269
- content: [{
1270
- tag: 'mentioned_users',
1271
- attrs: {},
1272
- content: jids.map(jid => ({
1273
- tag: 'to',
1274
- attrs: {
1275
- jid: WABinary_1.jidNormalizedUser(jid)
1094
+ ...options,
1095
+ messageId: generateMessageIDV2(userJid)
1096
+ });
1097
+ for (const id of jid) {
1098
+ if (isJidGroup(id)) {
1099
+ try {
1100
+ const groupData = (cachedGroupMetadata ? await cachedGroupMetadata(id) : null) || await groupMetadata(id);
1101
+ for (const participant of groupData.participants) {
1102
+ if (allUsers.has(participant.id))
1103
+ continue;
1104
+ allUsers.add(participant.id);
1276
1105
  }
1277
- }))
1278
- }]
1279
- }]
1280
- })
1281
-
1282
- for (const id of jids) {
1283
- try {
1284
- const normalizedId = WABinary_1.jidNormalizedUser(id)
1285
- const isPrivate = WABinary_1.isJidUser(normalizedId)
1286
- const type = isPrivate ? 'statusMentionMessage' : 'groupStatusMentionMessage'
1287
-
1288
- const protocolMessage = {
1289
- [type]: {
1106
+ }
1107
+ catch (error) {
1108
+ logger.error(`Error getting metadata group from ${id}: ${error}`);
1109
+ }
1110
+ }
1111
+ else if (!allUsers.has(id)) {
1112
+ allUsers.add(id);
1113
+ }
1114
+ }
1115
+ await relayMessage('status@broadcast', fullMsg.message, {
1116
+ messageId: fullMsg.key.id,
1117
+ statusJidList: Array.from(allUsers),
1118
+ additionalNodes: [
1119
+ {
1120
+ tag: 'meta',
1121
+ attrs: {},
1122
+ content: [
1123
+ {
1124
+ tag: 'mentioned_users',
1125
+ attrs: {},
1126
+ content: jid.map(id => ({
1127
+ tag: 'to',
1128
+ attrs: { jid: id },
1129
+ content: undefined
1130
+ }))
1131
+ }
1132
+ ]
1133
+ }
1134
+ ]
1135
+ });
1136
+ if (config.emitOwnEvents) {
1137
+ process.nextTick(async () => {
1138
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1139
+ });
1140
+ }
1141
+ for (const id of jid) {
1142
+ const isGroup = isJidGroup(id);
1143
+ const sendType = isGroup ? 'groupStatusMentionMessage' : 'statusMentionMessage';
1144
+ const mentionMsg = generateWAMessageFromContent(id, {
1145
+ messageContextInfo: {
1146
+ messageSecret: randomBytes(32)
1147
+ },
1148
+ [sendType]: {
1290
1149
  message: {
1291
1150
  protocolMessage: {
1292
- key: msg.key,
1151
+ key: fullMsg.key,
1293
1152
  type: 25
1294
1153
  }
1295
1154
  }
1296
- },
1297
- messageContextInfo: {
1298
- messageSecret: crypto_1.randomBytes(32)
1299
1155
  }
1300
- }
1301
-
1302
- const statusMsg = await Utils_1.generateWAMessageFromContent(normalizedId,
1303
- protocolMessage, {}
1304
- )
1305
-
1306
- await relayMessage(
1307
- normalizedId,
1308
- statusMsg.message, {
1309
- additionalNodes: [{
1156
+ }, {
1157
+ userJid
1158
+ });
1159
+ await relayMessage(id, mentionMsg.message, {
1160
+ additionalNodes: [
1161
+ {
1310
1162
  tag: 'meta',
1311
- attrs: isPrivate ? {
1312
- is_status_mention: 'true'
1313
- } : {
1314
- is_group_status_mention: 'true'
1315
- }
1316
- }]
1317
- }
1318
- )
1319
-
1320
- await Utils_1.delay(2000)
1321
- } catch (error) {
1322
- logger.error(`Error sending to ${id}: ${error}`)
1163
+ attrs: isGroup ?
1164
+ { is_group_status_mention: 'true' } :
1165
+ { is_status_mention: 'true' },
1166
+ content: undefined
1167
+ }
1168
+ ]
1169
+ });
1170
+ if (config.emitOwnEvents) {
1171
+ process.nextTick(async () => {
1172
+ await messageMutex.mutex(() => upsertMessage(mentionMsg, 'append'));
1173
+ });
1174
+ }
1175
+ await delay(delayMs);
1323
1176
  }
1177
+ return fullMsg;
1324
1178
  }
1325
-
1326
- return msg
1327
- },
1328
- sendAlbumMessage: async (jid, medias, options = {}) => {
1329
- const userJid = authState.creds.me.id
1330
- for (const media of medias) {
1331
- if (!media.image && !media.video) throw new TypeError(`medias[i] must have image or video property`)
1179
+ else if (typeof content === 'object' &&
1180
+ 'disappearingMessagesInChat' in content &&
1181
+ typeof content['disappearingMessagesInChat'] !== 'undefined' &&
1182
+ isJidGroup(jid)) {
1183
+ const { disappearingMessagesInChat } = content;
1184
+ const value = typeof disappearingMessagesInChat === 'boolean'
1185
+ ? disappearingMessagesInChat
1186
+ ? WA_DEFAULT_EPHEMERAL
1187
+ : 0
1188
+ : disappearingMessagesInChat;
1189
+ await groupToggleEphemeral(jid, value);
1332
1190
  }
1333
- if (medias.length < 2) throw new RangeError("Minimum 2 media")
1334
- const time = options.delay || 500
1335
- delete options.delay
1336
- const album = await Utils_1.generateWAMessageFromContent(jid, {
1337
- albumMessage: {
1338
- expectedImageCount: medias.filter(media => media.image).length,
1339
- expectedVideoCount: medias.filter(media => media.video).length,
1340
- ...options
1191
+ else {
1192
+ // Handle GROUP_STORY (group status/story) via kikyy
1193
+ const messageType = rahmi.detectType(content);
1194
+ if (messageType === 'GROUP_STORY') {
1195
+ return await rahmi.handleGroupStory(content, jid, options.quoted);
1341
1196
  }
1342
- }, {
1343
- userJid,
1344
- ...options
1345
- })
1346
- await relayMessage(jid, album.message, {
1347
- messageId: album.key.id
1348
- })
1349
- let mediaHandle
1350
- let msg
1351
- for (const i in medias) {
1352
- const media = medias[i]
1353
- if (media.image) {
1354
- msg = await Utils_1.generateWAMessage(jid, {
1355
- image: media.image,
1356
- ...media,
1357
- ...options
1358
- }, {
1359
- userJid,
1360
- upload: async (readStream, opts) => {
1361
- const up = await waUploadToServer(readStream, {
1362
- ...opts,
1363
- newsletter: WABinary_1.isJidNewsletter(jid)
1364
- })
1365
- mediaHandle = up.handle
1366
- return up
1367
- },
1368
- ...options
1369
- })
1370
- } else if (media.video) {
1371
- msg = await Utils_1.generateWAMessage(jid, {
1372
- video: media.video,
1373
- ...media,
1374
- ...options
1375
- }, {
1376
- userJid,
1377
- upload: async (readStream, opts) => {
1378
- const up = await waUploadToServer(readStream, {
1379
- ...opts,
1380
- newsletter: WABinary_1.isJidNewsletter(jid)
1381
- })
1382
- mediaHandle = up.handle
1383
- return up
1384
- },
1385
- ...options,
1386
- })
1197
+ else if (messageType === 'ALBUM') {
1198
+ return await rahmi.handleAlbum(content, jid, options.quoted);
1387
1199
  }
1388
- if (msg) {
1389
- msg.message.messageContextInfo = {
1390
- messageSecret: crypto_1.randomBytes(32),
1391
- messageAssociation: {
1392
- associationType: 1,
1393
- parentMessageKey: album.key
1394
- }
1395
- }
1200
+ else if (messageType === 'EVENT') {
1201
+ return await rahmi.handleEvent(content, jid, options.quoted);
1396
1202
  }
1397
- await relayMessage(jid, msg.message, {
1398
- messageId: msg.key.id
1399
- })
1400
- await Utils_1.delay(time)
1401
- }
1402
- return album
1403
- },
1404
- sendMessage: async (jid, content, options = {}) => {
1405
- const userJid = authState.creds.me.id
1406
- const additionalAttributes = {}
1407
-
1408
- if (typeof content === 'object' && 'disappearingMessagesInChat' in content && typeof content['disappearingMessagesInChat'] !== 'undefined' && WABinary_1.isJidGroup(jid)) {
1409
- const { disappearingMessagesInChat } = content
1410
- const value = typeof disappearingMessagesInChat === 'boolean' ? (disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) : disappearingMessagesInChat
1411
- await groupToggleEphemeral(jid, value)
1412
- } else if (typeof content === 'object' && 'album' in content && content.album) {
1413
- const albumMsg = await Utils_1.prepareAlbumMessageContent(jid, content.album, {
1414
- sock: {
1415
- relayMessage,
1416
- waUploadToServer
1417
- },
1418
- userJid: userJid,
1419
- ...options
1420
- })
1421
-
1422
- for (const media of albumMsg) {
1423
- await Utils_1.delay(options.delay || 500)
1424
- await relayMessage(jid, media.message, {
1425
- messageId: media.key.id,
1426
- useCachedGroupMetadata: options.useCachedGroupMetadata,
1427
- additionalAttributes,
1428
- statusJidList: options.statusJidList,
1429
- additionalNodes: options.additionalNodes,
1430
- ai: options.ai
1431
- })
1203
+ else if (messageType === 'POLL_RESULT') {
1204
+ return await rahmi.handlePollResult(content, jid, options.quoted);
1432
1205
  }
1433
-
1434
- return albumMsg
1435
- } else {
1436
- let mediaHandle
1437
-
1438
- const fullMsg = await Utils_1.generateWAMessage(jid, content, {
1206
+ const fullMsg = await generateWAMessage(jid, content, {
1439
1207
  logger,
1440
1208
  userJid,
1441
- getUrlInfo: text => link_preview_1.getUrlInfo(text, {
1209
+ getUrlInfo: text => getUrlInfo(text, {
1442
1210
  thumbnailWidth: linkPreviewImageThumbnailWidth,
1443
1211
  fetchOpts: {
1444
1212
  timeout: 3000,
1445
- ...axiosOptions || {}
1213
+ ...(httpRequestOptions || {})
1446
1214
  },
1447
1215
  logger,
1448
1216
  uploadImage: generateHighQualityLinkPreview ? waUploadToServer : undefined
1449
1217
  }),
1450
- getProfilePicUrl: profilePictureUrl,
1451
- getCallLink: createCallLink,
1452
- upload: async (encFilePath, opts) => {
1453
- const up = await waUploadToServer(encFilePath, {
1454
- ...opts,
1455
- newsletter: WABinary_1.isJidNewsletter(jid)
1456
- })
1457
- mediaHandle = up.handle
1458
- return up
1459
- },
1218
+ //TODO: CACHE
1219
+ getProfilePicUrl: sock.profilePictureUrl,
1220
+ getCallLink: sock.createCallLink,
1221
+ upload: waUploadToServer,
1460
1222
  mediaCache: config.mediaCache,
1461
1223
  options: config.options,
1462
- messageId: Utils_1.generateMessageID(userJid),
1463
- ...options,
1464
- })
1465
-
1466
- const isPin = 'pin' in content && !!content.pin
1467
- const isEdit = 'edit' in content && !!content.edit
1468
- const isDelete = 'delete' in content && !!content.delete
1469
- const isKeep = 'keep' in content && !!content.keep && content.keep?.type === 2
1470
-
1471
- if (isDelete || isKeep) {
1472
- if (WABinary_1.isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe || WABinary_1.isJidNewsletter(jid)) {
1473
- additionalAttributes.edit = '8'
1474
- } else {
1475
- additionalAttributes.edit = '7'
1224
+ messageId: generateMessageIDV2(sock.user?.id),
1225
+ ...options
1226
+ });
1227
+ const isNewsletter = isJidNewsletter(jid);
1228
+ const isEventMsg = 'event' in content && !!content.event;
1229
+ const isDeleteMsg = 'delete' in content && !!content.delete;
1230
+ const isEditMsg = 'edit' in content && !!content.edit;
1231
+ const isPinMsg = 'pin' in content && !!content.pin;
1232
+ const isKeepMsg = 'keep' in content && !!content.keep;
1233
+ const isPollMsg = 'poll' in content && !!content.poll;
1234
+ const isQuizMsg = 'poll' in content && !!content.poll.pollType;
1235
+ const isAiMsg = 'ai' in content && !!content.ai;
1236
+ const isNeedBizAttrs = 'secureMetaServiceLabel' in content && !!content.secureMetaServiceLabel;
1237
+ const additionalAttributes = options.additionalAttributes || {};
1238
+ const additionalNodes = options.additionalNodes || [];
1239
+ // required for delete
1240
+ if (isDeleteMsg || isKeepMsg) {
1241
+ // if the chat is a group, and I am not the author, then delete the message as an admin
1242
+ if (isJidGroup(content.delete?.remoteJid) && !content.delete?.fromMe) {
1243
+ additionalAttributes.edit = '8';
1244
+ }
1245
+ else {
1246
+ additionalAttributes.edit = '7';
1476
1247
  }
1477
- } else if (isEdit) {
1478
- additionalAttributes.edit = WABinary_1.isJidNewsletter(jid) ? '3' : '1'
1479
- } else if (isPin) {
1480
- additionalAttributes.edit = '2'
1481
1248
  }
1482
-
1483
- if (mediaHandle) {
1484
- additionalAttributes['media_id'] = mediaHandle
1249
+ else if (isEditMsg) {
1250
+ additionalAttributes.edit = isNewsletter ? '3' : '1';
1251
+ }
1252
+ else if (isPinMsg) {
1253
+ additionalAttributes.edit = '2';
1485
1254
  }
1486
-
1487
- if ('cachedGroupMetadata' in options) {
1488
- console.warn('cachedGroupMetadata in sendMessage are deprecated, now cachedGroupMetadata is part of the socket config.')
1255
+ else if (isPollMsg) {
1256
+ if (!isNewsletter && isQuizMsg) {
1257
+ throw new Boom('Quiz are only allowed for newsletter', { statusCode: 400 });
1258
+ }
1259
+ additionalNodes.push({
1260
+ tag: 'meta',
1261
+ attrs: {
1262
+ polltype: isQuizMsg ? 'quiz_creation' : 'creation',
1263
+ contenttype: isPollMsg && isNewsletter ? 'text' : undefined
1264
+ },
1265
+ content: undefined
1266
+ });
1267
+ }
1268
+ else if (isEventMsg) {
1269
+ additionalNodes.push({
1270
+ tag: 'meta',
1271
+ attrs: {
1272
+ event_type: 'creation'
1273
+ },
1274
+ content: undefined
1275
+ });
1276
+ }
1277
+ else if (isAiMsg) {
1278
+ if (!(isPnUser(jid) || isLidUser(jid))) {
1279
+ throw new Boom('AI icon on message are only allowed in private chat', { statusCode: 400 });
1280
+ }
1281
+ if ('messageContextInfo' in fullMsg.message && !!fullMsg.message.messageContextInfo) {
1282
+ fullMsg.message.messageContextInfo.supportPayload = BIZ_BOT_SUPPORT_PAYLOAD;
1283
+ }
1284
+ additionalNodes.push({
1285
+ tag: 'bot',
1286
+ attrs: {
1287
+ biz_bot: '1'
1288
+ },
1289
+ content: undefined
1290
+ });
1291
+ delete content.ai;
1489
1292
  }
1490
-
1491
1293
  await relayMessage(jid, fullMsg.message, {
1492
1294
  messageId: fullMsg.key.id,
1493
1295
  useCachedGroupMetadata: options.useCachedGroupMetadata,
1494
- additionalAttributes,
1296
+ addBizAttributes: isNeedBizAttrs,
1495
1297
  statusJidList: options.statusJidList,
1496
- additionalNodes: options.additionalNodes,
1497
- ai: options.ai
1498
- })
1499
-
1298
+ additionalAttributes,
1299
+ additionalNodes
1300
+ });
1500
1301
  if (config.emitOwnEvents) {
1501
- process.nextTick(() => {
1502
- processingMutex.mutex(() => (upsertMessage(fullMsg, 'append')))
1503
- })
1302
+ process.nextTick(async () => {
1303
+ await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
1304
+ });
1504
1305
  }
1505
-
1506
- return fullMsg
1306
+ if ('album' in content) {
1307
+ const { delayMs = 1500 } = options;
1308
+ for (const albumMedia of content.album) {
1309
+ const albumMsg = await generateWAMessage(jid, albumMedia, {
1310
+ logger,
1311
+ userJid,
1312
+ upload: waUploadToServer,
1313
+ mediaCache: config.mediaCache,
1314
+ options: config.options,
1315
+ ...options,
1316
+ messageId: generateMessageIDV2(userJid)
1317
+ });
1318
+ if (!hasValidAlbumMedia(normalizeMessageContent(albumMsg.message))) {
1319
+ throw new Boom('Invalid message type for album', { statusCode: 400 });
1320
+ }
1321
+ albumMsg.message.messageContextInfo ||= {};
1322
+ albumMsg.message.messageContextInfo.messageAssociation = {
1323
+ parentMessageKey: fullMsg.key,
1324
+ associationType: AssociationType.MEDIA_ALBUM
1325
+ };
1326
+ await relayMessage(jid, albumMsg.message, {
1327
+ messageId: albumMsg.key.id,
1328
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
1329
+ addBizAttributes: isNeedBizAttrs,
1330
+ statusJidList: options.statusJidList,
1331
+ additionalAttributes,
1332
+ additionalNodes
1333
+ });
1334
+ if (config.emitOwnEvents) {
1335
+ process.nextTick(async () => {
1336
+ await messageMutex.mutex(() => upsertMessage(albumMsg, 'append'));
1337
+ });
1338
+ }
1339
+ await delay(delayMs);
1340
+ }
1341
+ }
1342
+ return fullMsg;
1507
1343
  }
1508
- }
1509
- }
1510
- }
1511
-
1512
- module.exports = {
1513
- makeMessagesSocket
1514
- }
1344
+ },
1345
+ sendTable: async (jid, title, headers, rows, quoted, options = {}) => {
1346
+ const { message, messageId } = generateTableContent(title, headers, rows, quoted, options);
1347
+ await relayMessage(jid, message, { messageId });
1348
+ return { message, messageId };
1349
+ },
1350
+ sendTableV2: async (jid, table, quoted, options = {}) => {
1351
+ const { message, messageId } = generateTableContentV2(table, quoted, options);
1352
+ await relayMessage(jid, message, { messageId });
1353
+ return { message, messageId };
1354
+ },
1355
+ sendList: async (jid, title, items, quoted, options = {}) => {
1356
+ const { message, messageId } = generateListContent(title, items, quoted, options);
1357
+ await relayMessage(jid, message, { messageId });
1358
+ return { message, messageId };
1359
+ },
1360
+ sendCodeBlock: async (jid, code, quoted, options = {}) => {
1361
+ const { message, messageId } = generateCodeBlockContent(code, quoted, options);
1362
+ await relayMessage(jid, message, { messageId });
1363
+ return { message, messageId };
1364
+ },
1365
+ sendCodeBlockV2: async (jid, code, quoted, options = {}) => {
1366
+ const { message, messageId } = generateCodeBlockContentV2(code, quoted, options);
1367
+ await relayMessage(jid, message, { messageId });
1368
+ return { message, messageId };
1369
+ },
1370
+ sendLink: async (jid, text, links, quoted, options = {}) => {
1371
+ const { message, messageId } = generateLinkContent(text, links, quoted, options);
1372
+ await relayMessage(jid, message, { messageId });
1373
+ return { message, messageId };
1374
+ },
1375
+ sendLinkV2: async (jid, text, links, quoted, options = {}) => {
1376
+ const { message, messageId } = generateLinkContentV2(text, links, quoted, options);
1377
+ await relayMessage(jid, message, { messageId });
1378
+ return { message, messageId };
1379
+ },
1380
+ sendLatex: async (jid, quoted, options) => {
1381
+ const { message, messageId } = generateLatexContent(quoted, options);
1382
+ await relayMessage(jid, message, { messageId });
1383
+ return { message, messageId };
1384
+ },
1385
+ sendLatexImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1386
+ const { message, messageId } = await generateLatexImageContent(quoted, options, uploadFn, renderLatexToPng);
1387
+ await relayMessage(jid, message, { messageId });
1388
+ return { message, messageId };
1389
+ },
1390
+ sendLatexInlineImage: async (jid, quoted, options, renderLatexToPng, uploadFn) => {
1391
+ const { message, messageId } = await generateLatexInlineImageContent(quoted, options, uploadFn, renderLatexToPng);
1392
+ await relayMessage(jid, message, { messageId });
1393
+ return { message, messageId };
1394
+ },
1395
+ sendRichMessage: async (jid, submessages, quoted, options = {}) => {
1396
+ const { message, messageId } = generateRichMessageContent(submessages, quoted);
1397
+ await relayMessage(jid, message, { messageId });
1398
+ return { message, messageId };
1399
+ },
1400
+ sendUnifiedResponse: async (jid, quoted, captured) => {
1401
+ const { message, messageId } = generateUnifiedResponseContent(quoted, captured);
1402
+ await relayMessage(jid, message, { messageId });
1403
+ return { message, messageId };
1404
+ },
1405
+ captureUnifiedResponse,
1406
+ };
1407
+ };
1408
+ //# sourceMappingURL=messages-send.js.map