@queenanya/baileys 9.2.4 → 9.4.3

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 (301) hide show
  1. package/README.md +329 -1237
  2. package/WAProto/fix-imports.js +22 -18
  3. package/WAProto/index.js +22 -18
  4. package/lib/Defaults/index.d.ts +17 -0
  5. package/lib/Defaults/index.d.ts.map +1 -1
  6. package/lib/Defaults/index.js +29 -3
  7. package/lib/Defaults/index.js.map +1 -1
  8. package/lib/Signal/libsignal.d.ts.map +1 -1
  9. package/lib/Signal/libsignal.js +61 -2
  10. package/lib/Signal/libsignal.js.map +1 -1
  11. package/lib/Signal/lid-mapping.d.ts +5 -9
  12. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  13. package/lib/Signal/lid-mapping.js +170 -70
  14. package/lib/Signal/lid-mapping.js.map +1 -1
  15. package/lib/Socket/business.d.ts +114 -2
  16. package/lib/Socket/business.d.ts.map +1 -1
  17. package/lib/Socket/chats.d.ts +14 -1
  18. package/lib/Socket/chats.d.ts.map +1 -1
  19. package/lib/Socket/chats.js +253 -38
  20. package/lib/Socket/chats.js.map +1 -1
  21. package/lib/Socket/communities.d.ts +114 -2
  22. package/lib/Socket/communities.d.ts.map +1 -1
  23. package/lib/Socket/groups.d.ts +9 -0
  24. package/lib/Socket/groups.d.ts.map +1 -1
  25. package/lib/Socket/groups.js +6 -0
  26. package/lib/Socket/groups.js.map +1 -1
  27. package/lib/Socket/index.d.ts +114 -2
  28. package/lib/Socket/index.d.ts.map +1 -1
  29. package/lib/Socket/index.js +0 -6
  30. package/lib/Socket/index.js.map +1 -1
  31. package/lib/Socket/messages-recv.d.ts +115 -3
  32. package/lib/Socket/messages-recv.d.ts.map +1 -1
  33. package/lib/Socket/messages-recv.js +739 -150
  34. package/lib/Socket/messages-recv.js.map +1 -1
  35. package/lib/Socket/messages-send.d.ts +118 -4
  36. package/lib/Socket/messages-send.d.ts.map +1 -1
  37. package/lib/Socket/messages-send.js +328 -86
  38. package/lib/Socket/messages-send.js.map +1 -1
  39. package/lib/Socket/newsletter.d.ts +9 -0
  40. package/lib/Socket/newsletter.d.ts.map +1 -1
  41. package/lib/Socket/newsletter.js +2 -2
  42. package/lib/Socket/newsletter.js.map +1 -1
  43. package/lib/Socket/socket.d.ts +2 -0
  44. package/lib/Socket/socket.d.ts.map +1 -1
  45. package/lib/Socket/socket.js +143 -17
  46. package/lib/Socket/socket.js.map +1 -1
  47. package/lib/Types/Auth.d.ts +2 -0
  48. package/lib/Types/Auth.d.ts.map +1 -1
  49. package/lib/Types/Call.d.ts +10 -1
  50. package/lib/Types/Call.d.ts.map +1 -1
  51. package/lib/Types/Contact.d.ts +2 -0
  52. package/lib/Types/Contact.d.ts.map +1 -1
  53. package/lib/Types/Events.d.ts +21 -1
  54. package/lib/Types/Events.d.ts.map +1 -1
  55. package/lib/Types/GroupMetadata.d.ts +4 -0
  56. package/lib/Types/GroupMetadata.d.ts.map +1 -1
  57. package/lib/Types/Message.d.ts +530 -16
  58. package/lib/Types/Message.d.ts.map +1 -1
  59. package/lib/Types/Message.js.map +1 -1
  60. package/lib/Types/Newsletter.d.ts +37 -31
  61. package/lib/Types/Newsletter.d.ts.map +1 -1
  62. package/lib/Types/Newsletter.js +27 -23
  63. package/lib/Types/Newsletter.js.map +1 -1
  64. package/lib/Types/State.d.ts +54 -0
  65. package/lib/Types/State.d.ts.map +1 -1
  66. package/lib/Types/State.js +42 -0
  67. package/lib/Types/State.js.map +1 -1
  68. package/lib/Types/index.d.ts +9 -0
  69. package/lib/Types/index.d.ts.map +1 -1
  70. package/lib/Types/index.js.map +1 -1
  71. package/lib/Utils/browser-utils.d.ts +13 -0
  72. package/lib/Utils/browser-utils.d.ts.map +1 -1
  73. package/lib/Utils/browser-utils.js +90 -10
  74. package/lib/Utils/browser-utils.js.map +1 -1
  75. package/lib/Utils/chat-utils.d.ts +30 -0
  76. package/lib/Utils/chat-utils.d.ts.map +1 -1
  77. package/lib/Utils/chat-utils.js +81 -52
  78. package/lib/Utils/chat-utils.js.map +1 -1
  79. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  80. package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
  81. package/lib/Utils/companion-reg-client-utils.js +34 -0
  82. package/lib/Utils/companion-reg-client-utils.js.map +1 -0
  83. package/lib/Utils/crypto.d.ts +4 -8
  84. package/lib/Utils/crypto.d.ts.map +1 -1
  85. package/lib/Utils/crypto.js +2 -26
  86. package/lib/Utils/crypto.js.map +1 -1
  87. package/lib/Utils/decode-wa-message.d.ts +12 -0
  88. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  89. package/lib/Utils/decode-wa-message.js +16 -0
  90. package/lib/Utils/decode-wa-message.js.map +1 -1
  91. package/lib/Utils/event-buffer.js +10 -1
  92. package/lib/Utils/event-buffer.js.map +1 -1
  93. package/lib/Utils/generics.d.ts +3 -1
  94. package/lib/Utils/generics.d.ts.map +1 -1
  95. package/lib/Utils/generics.js +16 -3
  96. package/lib/Utils/generics.js.map +1 -1
  97. package/lib/Utils/history.d.ts +5 -2
  98. package/lib/Utils/history.d.ts.map +1 -1
  99. package/lib/Utils/history.js +53 -17
  100. package/lib/Utils/history.js.map +1 -1
  101. package/lib/Utils/identity-change-handler.d.ts +44 -0
  102. package/lib/Utils/identity-change-handler.d.ts.map +1 -0
  103. package/lib/Utils/identity-change-handler.js +50 -0
  104. package/lib/Utils/identity-change-handler.js.map +1 -0
  105. package/lib/Utils/index.d.ts +6 -0
  106. package/lib/Utils/index.d.ts.map +1 -1
  107. package/lib/Utils/index.js +6 -0
  108. package/lib/Utils/index.js.map +1 -1
  109. package/lib/Utils/interactive-message.d.ts +201 -0
  110. package/lib/Utils/interactive-message.d.ts.map +1 -0
  111. package/lib/Utils/interactive-message.js +256 -0
  112. package/lib/Utils/interactive-message.js.map +1 -0
  113. package/lib/Utils/lt-hash.d.ts +7 -12
  114. package/lib/Utils/lt-hash.d.ts.map +1 -1
  115. package/lib/Utils/lt-hash.js +2 -42
  116. package/lib/Utils/lt-hash.js.map +1 -1
  117. package/lib/Utils/message-composer.d.ts +5 -0
  118. package/lib/Utils/message-composer.d.ts.map +1 -0
  119. package/lib/Utils/message-composer.js +5 -0
  120. package/lib/Utils/message-composer.js.map +1 -0
  121. package/lib/Utils/message-retry-manager.d.ts +30 -2
  122. package/lib/Utils/message-retry-manager.d.ts.map +1 -1
  123. package/lib/Utils/message-retry-manager.js +59 -2
  124. package/lib/Utils/message-retry-manager.js.map +1 -1
  125. package/lib/Utils/messages-media.d.ts +19 -5
  126. package/lib/Utils/messages-media.d.ts.map +1 -1
  127. package/lib/Utils/messages-media.js +26 -17
  128. package/lib/Utils/messages-media.js.map +1 -1
  129. package/lib/Utils/messages.d.ts.map +1 -1
  130. package/lib/Utils/messages.js +433 -13
  131. package/lib/Utils/messages.js.map +1 -1
  132. package/lib/Utils/noise-handler.d.ts +2 -2
  133. package/lib/Utils/noise-handler.d.ts.map +1 -1
  134. package/lib/Utils/noise-handler.js +10 -10
  135. package/lib/Utils/noise-handler.js.map +1 -1
  136. package/lib/Utils/offline-node-processor.d.ts +17 -0
  137. package/lib/Utils/offline-node-processor.d.ts.map +1 -0
  138. package/lib/Utils/offline-node-processor.js +40 -0
  139. package/lib/Utils/offline-node-processor.js.map +1 -0
  140. package/lib/Utils/process-message.d.ts.map +1 -1
  141. package/lib/Utils/process-message.js +96 -16
  142. package/lib/Utils/process-message.js.map +1 -1
  143. package/lib/Utils/reporting-utils.js +2 -2
  144. package/lib/Utils/reporting-utils.js.map +1 -1
  145. package/lib/Utils/stanza-ack.d.ts +11 -0
  146. package/lib/Utils/stanza-ack.d.ts.map +1 -0
  147. package/lib/Utils/stanza-ack.js +38 -0
  148. package/lib/Utils/stanza-ack.js.map +1 -0
  149. package/lib/Utils/sync-action-utils.d.ts.map +1 -1
  150. package/lib/Utils/sync-action-utils.js +2 -1
  151. package/lib/Utils/sync-action-utils.js.map +1 -1
  152. package/lib/Utils/tc-token-utils.d.ts +26 -1
  153. package/lib/Utils/tc-token-utils.d.ts.map +1 -1
  154. package/lib/Utils/tc-token-utils.js +149 -4
  155. package/lib/Utils/tc-token-utils.js.map +1 -1
  156. package/lib/Utils/use-mongo-file-auth-state.d.ts +16 -0
  157. package/lib/Utils/use-mongo-file-auth-state.d.ts.map +1 -0
  158. package/lib/Utils/use-mongo-file-auth-state.js +60 -0
  159. package/lib/Utils/use-mongo-file-auth-state.js.map +1 -0
  160. package/lib/Utils/use-multi-file-auth-state.js +1 -1
  161. package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
  162. package/lib/Utils/validate-connection.d.ts.map +1 -1
  163. package/lib/Utils/validate-connection.js +11 -1
  164. package/lib/Utils/validate-connection.js.map +1 -1
  165. package/lib/WABinary/generic-utils.d.ts +9 -0
  166. package/lib/WABinary/generic-utils.d.ts.map +1 -1
  167. package/lib/WABinary/generic-utils.js +23 -0
  168. package/lib/WABinary/generic-utils.js.map +1 -1
  169. package/lib/WABinary/jid-utils.js.map +1 -1
  170. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +1 -1
  171. package/lib/WAUSync/Protocols/USyncContactProtocol.js +26 -3
  172. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +1 -1
  173. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  174. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +1 -0
  175. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  176. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +1 -0
  177. package/lib/WAUSync/Protocols/index.d.ts +1 -0
  178. package/lib/WAUSync/Protocols/index.d.ts.map +1 -1
  179. package/lib/WAUSync/Protocols/index.js +1 -0
  180. package/lib/WAUSync/Protocols/index.js.map +1 -1
  181. package/lib/WAUSync/USyncQuery.d.ts +1 -0
  182. package/lib/WAUSync/USyncQuery.d.ts.map +1 -1
  183. package/lib/WAUSync/USyncQuery.js +5 -1
  184. package/lib/WAUSync/USyncQuery.js.map +1 -1
  185. package/lib/WAUSync/USyncUser.d.ts +4 -0
  186. package/lib/WAUSync/USyncUser.d.ts.map +1 -1
  187. package/lib/WAUSync/USyncUser.js +8 -0
  188. package/lib/WAUSync/USyncUser.js.map +1 -1
  189. package/lib/addons/anti-delete.d.ts +72 -0
  190. package/lib/addons/anti-delete.d.ts.map +1 -0
  191. package/lib/addons/anti-delete.js +165 -0
  192. package/lib/addons/anti-delete.js.map +1 -0
  193. package/lib/addons/auto-reply.d.ts +67 -0
  194. package/lib/addons/auto-reply.d.ts.map +1 -0
  195. package/lib/addons/auto-reply.js +145 -0
  196. package/lib/addons/auto-reply.js.map +1 -0
  197. package/lib/addons/browser-presets.d.ts +16 -0
  198. package/lib/addons/browser-presets.d.ts.map +1 -0
  199. package/lib/addons/browser-presets.js +24 -0
  200. package/lib/addons/browser-presets.js.map +1 -0
  201. package/lib/addons/button-sender.d.ts +260 -0
  202. package/lib/addons/button-sender.d.ts.map +1 -0
  203. package/lib/addons/button-sender.js +771 -0
  204. package/lib/addons/button-sender.js.map +1 -0
  205. package/lib/addons/call-handler.d.ts +79 -0
  206. package/lib/addons/call-handler.d.ts.map +1 -0
  207. package/lib/addons/call-handler.js +342 -0
  208. package/lib/addons/call-handler.js.map +1 -0
  209. package/lib/addons/from-chats.d.ts +30 -0
  210. package/lib/addons/from-chats.d.ts.map +1 -0
  211. package/lib/addons/from-chats.js +38 -0
  212. package/lib/addons/from-chats.js.map +1 -0
  213. package/lib/addons/from-messages-recv.d.ts +59 -0
  214. package/lib/addons/from-messages-recv.d.ts.map +1 -0
  215. package/lib/addons/from-messages-recv.js +326 -0
  216. package/lib/addons/from-messages-recv.js.map +1 -0
  217. package/lib/addons/from-messages-send.d.ts +50 -0
  218. package/lib/addons/from-messages-send.d.ts.map +1 -0
  219. package/lib/addons/from-messages-send.js +148 -0
  220. package/lib/addons/from-messages-send.js.map +1 -0
  221. package/lib/addons/from-messages.d.ts +52 -0
  222. package/lib/addons/from-messages.d.ts.map +1 -0
  223. package/lib/addons/from-messages.js +304 -0
  224. package/lib/addons/from-messages.js.map +1 -0
  225. package/lib/addons/index.d.ts +67 -0
  226. package/lib/addons/index.d.ts.map +1 -0
  227. package/lib/addons/index.js +86 -0
  228. package/lib/addons/index.js.map +1 -0
  229. package/lib/addons/interactive-message.d.ts +201 -0
  230. package/lib/addons/interactive-message.d.ts.map +1 -0
  231. package/lib/addons/interactive-message.js +256 -0
  232. package/lib/addons/interactive-message.js.map +1 -0
  233. package/lib/addons/jid-plot.d.ts +49 -0
  234. package/lib/addons/jid-plot.d.ts.map +1 -0
  235. package/lib/addons/jid-plot.js +84 -0
  236. package/lib/addons/jid-plot.js.map +1 -0
  237. package/lib/addons/jid-plotting.d.ts +54 -0
  238. package/lib/addons/jid-plotting.d.ts.map +1 -0
  239. package/lib/addons/jid-plotting.js +150 -0
  240. package/lib/addons/jid-plotting.js.map +1 -0
  241. package/lib/addons/lid-support.d.ts +41 -0
  242. package/lib/addons/lid-support.d.ts.map +1 -0
  243. package/lib/addons/lid-support.js +42 -0
  244. package/lib/addons/lid-support.js.map +1 -0
  245. package/lib/addons/message-composer.d.ts +142 -0
  246. package/lib/addons/message-composer.d.ts.map +1 -0
  247. package/lib/addons/message-composer.js +377 -0
  248. package/lib/addons/message-composer.js.map +1 -0
  249. package/lib/addons/message-scheduler.d.ts +77 -0
  250. package/lib/addons/message-scheduler.d.ts.map +1 -0
  251. package/lib/addons/message-scheduler.js +108 -0
  252. package/lib/addons/message-scheduler.js.map +1 -0
  253. package/lib/addons/message-search.d.ts +51 -0
  254. package/lib/addons/message-search.d.ts.map +1 -0
  255. package/lib/addons/message-search.js +171 -0
  256. package/lib/addons/message-search.js.map +1 -0
  257. package/lib/addons/message-utils.d.ts +88 -0
  258. package/lib/addons/message-utils.d.ts.map +1 -0
  259. package/lib/addons/message-utils.js +292 -0
  260. package/lib/addons/message-utils.js.map +1 -0
  261. package/lib/addons/outgoing-calls.d.ts +64 -0
  262. package/lib/addons/outgoing-calls.d.ts.map +1 -0
  263. package/lib/addons/outgoing-calls.js +139 -0
  264. package/lib/addons/outgoing-calls.js.map +1 -0
  265. package/lib/addons/pairing-fix.d.ts +31 -0
  266. package/lib/addons/pairing-fix.d.ts.map +1 -0
  267. package/lib/addons/pairing-fix.js +74 -0
  268. package/lib/addons/pairing-fix.js.map +1 -0
  269. package/lib/addons/past-participants.d.ts +42 -0
  270. package/lib/addons/past-participants.d.ts.map +1 -0
  271. package/lib/addons/past-participants.js +41 -0
  272. package/lib/addons/past-participants.js.map +1 -0
  273. package/lib/addons/rich-response.d.ts +111 -0
  274. package/lib/addons/rich-response.d.ts.map +1 -0
  275. package/lib/addons/rich-response.js +152 -0
  276. package/lib/addons/rich-response.js.map +1 -0
  277. package/lib/addons/scheduling.d.ts +41 -0
  278. package/lib/addons/scheduling.d.ts.map +1 -0
  279. package/lib/addons/scheduling.js +110 -0
  280. package/lib/addons/scheduling.js.map +1 -0
  281. package/lib/addons/status-posting.d.ts +177 -0
  282. package/lib/addons/status-posting.d.ts.map +1 -0
  283. package/lib/addons/status-posting.js +240 -0
  284. package/lib/addons/status-posting.js.map +1 -0
  285. package/lib/addons/stickerpack.d.ts +37 -0
  286. package/lib/addons/stickerpack.d.ts.map +1 -0
  287. package/lib/addons/stickerpack.js +39 -0
  288. package/lib/addons/stickerpack.js.map +1 -0
  289. package/lib/addons/templates.d.ts +72 -0
  290. package/lib/addons/templates.d.ts.map +1 -0
  291. package/lib/addons/templates.js +145 -0
  292. package/lib/addons/templates.js.map +1 -0
  293. package/lib/addons/vcard.d.ts +59 -0
  294. package/lib/addons/vcard.d.ts.map +1 -0
  295. package/lib/addons/vcard.js +88 -0
  296. package/lib/addons/vcard.js.map +1 -0
  297. package/lib/index.d.ts +1 -0
  298. package/lib/index.d.ts.map +1 -1
  299. package/lib/index.js +1 -0
  300. package/lib/index.js.map +1 -1
  301. package/package.json +5 -3
@@ -0,0 +1,771 @@
1
+ /**
2
+ * button-sender.ts
3
+ * Ported from @ryuu-reinzz/button-helper v2.2.5 → TypeScript/ESM for @queenanya/baileys
4
+ *
5
+ * Provides runtime helpers to send WhatsApp interactive / native-flow button
6
+ * messages via relayMessage directly, bypassing sendMessage's validation path
7
+ * that does not recognise interactiveMessage payloads.
8
+ *
9
+ * Visibility guarantee:
10
+ * All button types (quick_reply, cta_url, cta_copy, cta_call, list, template,
11
+ * carousel/cards, combined, single_select) render correctly on:
12
+ * • WhatsApp Messenger — Android & iOS
13
+ * • WhatsApp Business — Android & iOS
14
+ *
15
+ * What this file provides:
16
+ * 1. buildInteractiveButtons() — legacy → native_flow normaliser
17
+ * 2. validateAuthoringButtons() — permissive pre-send validation
18
+ * 3. validateSendButtonsPayload() — strict sendButtons payload check
19
+ * 4. validateSendInteractiveMessagePayload()— strict interactive payload check
20
+ * 5. validateInteractiveMessageContent() — post-convert check
21
+ * 6. convertToInteractiveMessage() — high-level authoring → proto shape
22
+ * 7. sendInteractiveMessage() — low-level power function
23
+ * 8. sendInteractiveMessageV2() — extended (thumbnail / externalAdReply)
24
+ * 9. sendButtons() — convenience quick-reply wrapper
25
+ * 10. InteractiveValidationError — structured error class
26
+ *
27
+ * getButtonType / getButtonArgs are imported from ./message-utils (already in
28
+ * this repo — no duplication).
29
+ *
30
+ * Usage:
31
+ * import { sendButtons, sendInteractiveMessage } from '@queenanya/baileys'
32
+ *
33
+ * await sendButtons(sock, jid, {
34
+ * text: 'Choose an option',
35
+ * footer: 'Bot footer',
36
+ * buttons: [
37
+ * { id: 'yes', text: 'Yes' },
38
+ * { id: 'no', text: 'No' },
39
+ * ],
40
+ * })
41
+ */
42
+ import { generateMessageIDV2 } from '../Utils/generics.js';
43
+ import { generateWAMessageFromContent, normalizeMessageContent } from '../Utils/messages.js';
44
+ import { isJidGroup } from '../WABinary/jid-utils.js';
45
+ import { getButtonArgs, getButtonType } from './message-utils.js';
46
+ // ─────────────────────────────────────────────────────────────────────────────
47
+ // Custom Error
48
+ // ─────────────────────────────────────────────────────────────────────────────
49
+ /**
50
+ * Thrown when button payload authoring validation fails.
51
+ * Provides structured errors/warnings + a canonical example so callers can
52
+ * surface actionable feedback to end-users or logs.
53
+ */
54
+ export class InteractiveValidationError extends Error {
55
+ constructor(message, { context, errors = [], warnings = [], example } = {}) {
56
+ super(message);
57
+ this.name = 'InteractiveValidationError';
58
+ this.context = context;
59
+ this.errors = errors;
60
+ this.warnings = warnings;
61
+ this.example = example;
62
+ }
63
+ toJSON() {
64
+ return {
65
+ name: this.name,
66
+ message: this.message,
67
+ context: this.context,
68
+ errors: this.errors,
69
+ warnings: this.warnings,
70
+ example: this.example
71
+ };
72
+ }
73
+ formatDetailed() {
74
+ const lines = [`[${this.name}] ${this.message}${this.context ? ' (' + this.context + ')' : ''}`];
75
+ if (this.errors.length) {
76
+ lines.push('Errors:');
77
+ this.errors.forEach(e => lines.push(' - ' + e));
78
+ }
79
+ if (this.warnings.length) {
80
+ lines.push('Warnings:');
81
+ this.warnings.forEach(w => lines.push(' - ' + w));
82
+ }
83
+ if (this.example) {
84
+ lines.push('Example payload:', JSON.stringify(this.example, null, 2));
85
+ }
86
+ return lines.join('\n');
87
+ }
88
+ }
89
+ // ─────────────────────────────────────────────────────────────────────────────
90
+ // Example payloads (embedded inside thrown errors for developer guidance)
91
+ // ─────────────────────────────────────────────────────────────────────────────
92
+ const EXAMPLE_PAYLOADS = {
93
+ sendButtons: {
94
+ text: 'Choose an option',
95
+ buttons: [
96
+ { id: 'opt1', text: 'Option 1' },
97
+ { id: 'opt2', text: 'Option 2' },
98
+ {
99
+ name: 'cta_url',
100
+ buttonParamsJson: JSON.stringify({
101
+ display_text: 'Visit Site',
102
+ url: 'https://example.com'
103
+ })
104
+ }
105
+ ],
106
+ footer: 'Footer text'
107
+ },
108
+ sendInteractiveMessage: {
109
+ text: 'Pick an action',
110
+ interactiveButtons: [
111
+ {
112
+ name: 'quick_reply',
113
+ buttonParamsJson: JSON.stringify({ display_text: 'Hello', id: 'hello' })
114
+ },
115
+ {
116
+ name: 'cta_copy',
117
+ buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', copy_code: 'ABC123' })
118
+ }
119
+ ],
120
+ footer: 'Footer'
121
+ }
122
+ };
123
+ // ─────────────────────────────────────────────────────────────────────────────
124
+ // Allowed button name sets
125
+ // ─────────────────────────────────────────────────────────────────────────────
126
+ /** Allowed complex button names in sendButtons */
127
+ const SEND_BUTTONS_ALLOWED_COMPLEX = new Set(['cta_url', 'cta_copy', 'cta_call']);
128
+ /** Full set of button names allowed in sendInteractiveMessage */
129
+ const INTERACTIVE_ALLOWED_NAMES = new Set([
130
+ 'quick_reply',
131
+ 'cta_url',
132
+ 'cta_copy',
133
+ 'cta_call',
134
+ 'cta_catalog',
135
+ 'cta_reminder',
136
+ 'cta_cancel_reminder',
137
+ 'address_message',
138
+ 'send_location',
139
+ 'open_webview',
140
+ 'mpm',
141
+ 'wa_payment_transaction_details',
142
+ 'automated_greeting_message_view_catalog',
143
+ 'galaxy_message',
144
+ 'single_select'
145
+ ]);
146
+ /** Minimum required fields per button name */
147
+ const REQUIRED_FIELDS_MAP = {
148
+ cta_url: ['display_text', 'url'],
149
+ cta_copy: ['display_text', 'copy_code'],
150
+ cta_call: ['display_text', 'phone_number'],
151
+ cta_catalog: ['business_phone_number'],
152
+ cta_reminder: ['display_text'],
153
+ cta_cancel_reminder: ['display_text'],
154
+ address_message: ['display_text'],
155
+ send_location: ['display_text'],
156
+ open_webview: ['title', 'link'],
157
+ mpm: ['product_id'],
158
+ wa_payment_transaction_details: ['transaction_id'],
159
+ automated_greeting_message_view_catalog: ['business_phone_number', 'catalog_product_id'],
160
+ galaxy_message: ['flow_token', 'flow_id'],
161
+ single_select: ['title', 'sections'],
162
+ quick_reply: ['display_text', 'id']
163
+ };
164
+ // ─────────────────────────────────────────────────────────────────────────────
165
+ // Internal helper
166
+ // ─────────────────────────────────────────────────────────────────────────────
167
+ function parseButtonParamsInternal(name, buttonParamsJson, errors, _warnings, index) {
168
+ let parsed;
169
+ try {
170
+ parsed = JSON.parse(buttonParamsJson);
171
+ }
172
+ catch (e) {
173
+ errors.push(`button[${index}] (${name}) invalid JSON: ${e.message}`);
174
+ return null;
175
+ }
176
+ const required = REQUIRED_FIELDS_MAP[name] ?? [];
177
+ for (const field of required) {
178
+ if (!(field in parsed)) {
179
+ errors.push(`button[${index}] (${name}) missing required field '${field}'`);
180
+ }
181
+ }
182
+ if (name === 'open_webview' && parsed.link) {
183
+ const link = parsed.link;
184
+ if (typeof link !== 'object' || !link.url) {
185
+ errors.push(`button[${index}] (open_webview) link.url required`);
186
+ }
187
+ }
188
+ if (name === 'single_select') {
189
+ if (!Array.isArray(parsed.sections) || parsed.sections.length === 0) {
190
+ errors.push(`button[${index}] (single_select) sections must be a non-empty array`);
191
+ }
192
+ }
193
+ return parsed;
194
+ }
195
+ // ─────────────────────────────────────────────────────────────────────────────
196
+ // Public: normalise + validate
197
+ // ─────────────────────────────────────────────────────────────────────────────
198
+ /**
199
+ * Normalise various legacy / upstream button shapes into the native-flow format
200
+ * { name, buttonParamsJson }.
201
+ *
202
+ * Accepted input shapes:
203
+ * 1. Already native_flow : { name: string, buttonParamsJson: string }
204
+ * 2. Simple legacy : { id?: string, text?: string, displayText?: string }
205
+ * 3. Old Baileys : { buttonId: string, buttonText: { displayText: string } }
206
+ * 4. Unknown : passed through verbatim
207
+ */
208
+ export function buildInteractiveButtons(buttons = []) {
209
+ return buttons.map((b, i) => {
210
+ const btn = b;
211
+ // 1. Already native shape
212
+ if (btn['name'] && btn['buttonParamsJson'])
213
+ return b;
214
+ // 2. Legacy quick-reply
215
+ if (btn['id'] || btn['text'] || btn['displayText']) {
216
+ return {
217
+ name: 'quick_reply',
218
+ buttonParamsJson: JSON.stringify({
219
+ display_text: (btn['text'] ?? btn['displayText'] ?? `Button ${i + 1}`),
220
+ id: (btn['id'] ?? `quick_${i + 1}`)
221
+ })
222
+ };
223
+ }
224
+ // 3. Old Baileys shape
225
+ const oldBt = btn['buttonText'];
226
+ if (btn['buttonId'] && oldBt?.['displayText']) {
227
+ return {
228
+ name: 'quick_reply',
229
+ buttonParamsJson: JSON.stringify({
230
+ display_text: oldBt['displayText'],
231
+ id: btn['buttonId']
232
+ })
233
+ };
234
+ }
235
+ // 4. Unknown — pass through
236
+ return b;
237
+ });
238
+ }
239
+ /**
240
+ * Validate raw button objects before conversion.
241
+ * Permissive: only blocks clearly malformed input.
242
+ */
243
+ export function validateAuthoringButtons(buttons) {
244
+ const errors = [];
245
+ const warnings = [];
246
+ if (buttons === null)
247
+ return { errors: [], warnings: [], valid: true, cleaned: [] };
248
+ if (!Array.isArray(buttons)) {
249
+ errors.push('buttons must be an array');
250
+ return { errors, warnings, valid: false, cleaned: [] };
251
+ }
252
+ const SOFT_CAP = 25;
253
+ if (buttons.length === 0) {
254
+ warnings.push('buttons array is empty');
255
+ }
256
+ else if (buttons.length > SOFT_CAP) {
257
+ warnings.push(`buttons count (${buttons.length}) exceeds soft cap of ${SOFT_CAP}; may be rejected by client`);
258
+ }
259
+ const cleaned = buttons.map((b, idx) => {
260
+ const btn = b;
261
+ if (b === null || typeof b !== 'object') {
262
+ errors.push(`button[${idx}] is not an object`);
263
+ return b;
264
+ }
265
+ if (btn['name'] && btn['buttonParamsJson']) {
266
+ if (typeof btn['buttonParamsJson'] !== 'string') {
267
+ errors.push(`button[${idx}] buttonParamsJson must be string`);
268
+ }
269
+ else {
270
+ try {
271
+ JSON.parse(btn['buttonParamsJson']);
272
+ }
273
+ catch (e) {
274
+ errors.push(`button[${idx}] buttonParamsJson is not valid JSON: ${e.message}`);
275
+ }
276
+ }
277
+ return b;
278
+ }
279
+ if (btn['id'] || btn['text'] || btn['displayText'])
280
+ return b;
281
+ const oldBt = btn['buttonText'];
282
+ if (btn['buttonId'] && oldBt?.['displayText'])
283
+ return b;
284
+ warnings.push(`button[${idx}] unrecognized shape; passing through unchanged`);
285
+ return b;
286
+ });
287
+ return { errors, warnings, valid: errors.length === 0, cleaned };
288
+ }
289
+ /**
290
+ * Strict validator for sendButtons() payload.
291
+ */
292
+ export function validateSendButtonsPayload(data) {
293
+ const errors = [];
294
+ const warnings = [];
295
+ if (!data || typeof data !== 'object') {
296
+ return { valid: false, errors: ['payload must be an object'], warnings };
297
+ }
298
+ const d = data;
299
+ if (!d.text || typeof d.text !== 'string') {
300
+ errors.push('text is mandatory and must be a string');
301
+ }
302
+ if (!Array.isArray(d.buttons) || d.buttons.length === 0) {
303
+ errors.push('buttons is mandatory and must be a non-empty array');
304
+ }
305
+ else {
306
+ ;
307
+ d.buttons.forEach((btn, i) => {
308
+ const b = btn;
309
+ if (!btn || typeof btn !== 'object') {
310
+ errors.push(`button[${i}] must be an object`);
311
+ return;
312
+ }
313
+ if (b['id'] && b['text']) {
314
+ if (typeof b['id'] !== 'string' || typeof b['text'] !== 'string') {
315
+ errors.push(`button[${i}] legacy quick reply id/text must be strings`);
316
+ }
317
+ return;
318
+ }
319
+ if (b['name'] && b['buttonParamsJson']) {
320
+ if (!SEND_BUTTONS_ALLOWED_COMPLEX.has(b['name'])) {
321
+ errors.push(`button[${i}] name '${b['name']}' not allowed in sendButtons (allowed: ${[...SEND_BUTTONS_ALLOWED_COMPLEX].join(', ')})`);
322
+ return;
323
+ }
324
+ if (typeof b['buttonParamsJson'] !== 'string') {
325
+ errors.push(`button[${i}] buttonParamsJson must be string`);
326
+ return;
327
+ }
328
+ parseButtonParamsInternal(b['name'], b['buttonParamsJson'], errors, warnings, i);
329
+ return;
330
+ }
331
+ errors.push(`button[${i}] invalid shape — expected {id, text} or {name, buttonParamsJson} with name in [${[...SEND_BUTTONS_ALLOWED_COMPLEX].join(', ')}]`);
332
+ });
333
+ }
334
+ return { valid: errors.length === 0, errors, warnings };
335
+ }
336
+ /**
337
+ * Strict validator for sendInteractiveMessage() authoring payload.
338
+ */
339
+ export function validateSendInteractiveMessagePayload(data) {
340
+ const errors = [];
341
+ const warnings = [];
342
+ if (!data || typeof data !== 'object') {
343
+ return { valid: false, errors: ['payload must be an object'], warnings };
344
+ }
345
+ const d = data;
346
+ if (!d.text || typeof d.text !== 'string') {
347
+ errors.push('text is mandatory and must be a string');
348
+ }
349
+ if (!Array.isArray(d.interactiveButtons) || d.interactiveButtons.length === 0) {
350
+ errors.push('interactiveButtons is mandatory and must be a non-empty array');
351
+ }
352
+ else {
353
+ ;
354
+ d.interactiveButtons.forEach((btn, i) => {
355
+ if (!btn || typeof btn !== 'object') {
356
+ errors.push(`interactiveButtons[${i}] must be an object`);
357
+ return;
358
+ }
359
+ if (!btn.name || typeof btn.name !== 'string') {
360
+ errors.push(`interactiveButtons[${i}] missing name`);
361
+ return;
362
+ }
363
+ if (!INTERACTIVE_ALLOWED_NAMES.has(btn.name)) {
364
+ errors.push(`interactiveButtons[${i}] name '${btn.name}' not allowed`);
365
+ return;
366
+ }
367
+ if (!btn.buttonParamsJson || typeof btn.buttonParamsJson !== 'string') {
368
+ errors.push(`interactiveButtons[${i}] buttonParamsJson must be a non-empty string`);
369
+ return;
370
+ }
371
+ parseButtonParamsInternal(btn.name, btn.buttonParamsJson, errors, warnings, i);
372
+ });
373
+ }
374
+ return { valid: errors.length === 0, errors, warnings };
375
+ }
376
+ /**
377
+ * Validate already-converted interactiveMessage content (just before WAMessage creation).
378
+ */
379
+ export function validateInteractiveMessageContent(content) {
380
+ const errors = [];
381
+ const warnings = [];
382
+ if (!content || typeof content !== 'object') {
383
+ return { errors: ['content must be an object'], warnings, valid: false };
384
+ }
385
+ const c = content;
386
+ const interactive = c.interactiveMessage;
387
+ // Non-interactive messages are fine — nothing to validate
388
+ if (!interactive)
389
+ return { errors, warnings, valid: true };
390
+ const nativeFlow = interactive.nativeFlowMessage;
391
+ if (!nativeFlow) {
392
+ errors.push('interactiveMessage.nativeFlowMessage missing');
393
+ return { errors, warnings, valid: false };
394
+ }
395
+ if (!Array.isArray(nativeFlow.buttons)) {
396
+ errors.push('nativeFlowMessage.buttons must be an array');
397
+ return { errors, warnings, valid: false };
398
+ }
399
+ if (nativeFlow.buttons.length === 0) {
400
+ warnings.push('nativeFlowMessage.buttons is empty');
401
+ }
402
+ ;
403
+ nativeFlow.buttons.forEach((btn, i) => {
404
+ if (!btn || typeof btn !== 'object') {
405
+ errors.push(`buttons[${i}] is not an object`);
406
+ return;
407
+ }
408
+ if (!btn.buttonParamsJson) {
409
+ warnings.push(`buttons[${i}] missing buttonParamsJson (may fail to render)`);
410
+ }
411
+ else if (typeof btn.buttonParamsJson !== 'string') {
412
+ errors.push(`buttons[${i}] buttonParamsJson must be string`);
413
+ }
414
+ else {
415
+ try {
416
+ JSON.parse(btn.buttonParamsJson);
417
+ }
418
+ catch (e) {
419
+ warnings.push(`buttons[${i}] buttonParamsJson invalid JSON (${e.message})`);
420
+ }
421
+ }
422
+ if (!btn.name) {
423
+ warnings.push(`buttons[${i}] missing name; defaulting to quick_reply`);
424
+ btn.name = 'quick_reply';
425
+ }
426
+ });
427
+ return { errors, warnings, valid: errors.length === 0 };
428
+ }
429
+ // ─────────────────────────────────────────────────────────────────────────────
430
+ // Content converter
431
+ // ─────────────────────────────────────────────────────────────────────────────
432
+ /**
433
+ * Convert the high-level authoring shape:
434
+ * { text, footer?, title?, subtitle?, interactiveButtons: [...] }
435
+ * into the exact structure @queenanya/baileys / WAProto expects:
436
+ * { interactiveMessage: { nativeFlowMessage: { buttons: [...] }, body?, header?, footer? } }
437
+ *
438
+ * Authoring-only fields are stripped so they don't leak into
439
+ * generateWAMessageFromContent.
440
+ */
441
+ export function convertToInteractiveMessage(content) {
442
+ const btns = content.interactiveButtons;
443
+ if (btns && btns.length > 0) {
444
+ const interactiveMessage = {
445
+ nativeFlowMessage: {
446
+ buttons: btns.map(btn => ({
447
+ name: btn.name ?? 'quick_reply',
448
+ buttonParamsJson: btn.buttonParamsJson
449
+ })),
450
+ messageParamsJson: ''
451
+ }
452
+ };
453
+ if (content.title || content.subtitle) {
454
+ interactiveMessage.header = {
455
+ title: (content.title ?? content.subtitle ?? '')
456
+ };
457
+ }
458
+ if (content.text) {
459
+ interactiveMessage.body = { text: content.text };
460
+ }
461
+ if (content.footer) {
462
+ interactiveMessage.footer = { text: content.footer };
463
+ }
464
+ // Strip authoring-only keys to avoid leaking into proto serialisation
465
+ const newContent = { ...content };
466
+ delete newContent.interactiveButtons;
467
+ delete newContent.title;
468
+ delete newContent.subtitle;
469
+ delete newContent.text;
470
+ delete newContent.footer;
471
+ return { ...newContent, interactiveMessage };
472
+ }
473
+ return content;
474
+ }
475
+ // ─────────────────────────────────────────────────────────────────────────────
476
+ // Core send helpers
477
+ // ─────────────────────────────────────────────────────────────────────────────
478
+ /**
479
+ * Low-level power function — sends any interactive message by:
480
+ *
481
+ * 1. Validate authoring payload (when interactiveButtons is provided).
482
+ * 2. Convert to interactiveMessage / nativeFlowMessage proto shape.
483
+ * 3. Build WAMessage via generateWAMessageFromContent (skips sendMessage
484
+ * validation that rejects interactiveMessage).
485
+ * 4. Derive & inject required binary nodes so buttons render on all platforms:
486
+ * - biz node { tag:'biz', attrs:{} }
487
+ * - interactive child { type:'native_flow', v:'1' }
488
+ * - native_flow child { v:'9', name:'mixed' }
489
+ * - bot node { tag:'bot', attrs:{ biz_bot:'1' } } (private chats only)
490
+ * 5. Relay via sock.relayMessage.
491
+ *
492
+ * iOS/Android + WA Messenger + WA Business compatibility:
493
+ * getButtonArgs() (message-utils.ts) already handles all button categories:
494
+ * single_select, payment flows, mpm/catalog/location specials, standard
495
+ * native_flow (mixed), list, carousel/cards — each with the correct node tree.
496
+ *
497
+ * @param sock Active @queenanya/baileys socket instance.
498
+ * @param jid Destination chat JID.
499
+ * @param content Authoring payload (may include interactiveButtons).
500
+ * @param options Pass-through relay options (additionalNodes, etc.).
501
+ */
502
+ export async function sendInteractiveMessage(sock, jid, content, options = {}) {
503
+ if (!sock) {
504
+ throw new InteractiveValidationError('Socket is required', {
505
+ context: 'sendInteractiveMessage'
506
+ });
507
+ }
508
+ // Step 1 — authoring validation
509
+ if (Array.isArray(content.interactiveButtons)) {
510
+ const strict = validateSendInteractiveMessagePayload(content);
511
+ if (!strict.valid) {
512
+ throw new InteractiveValidationError('Interactive authoring payload invalid', {
513
+ context: 'sendInteractiveMessage.validateSendInteractiveMessagePayload',
514
+ errors: strict.errors,
515
+ warnings: strict.warnings,
516
+ example: EXAMPLE_PAYLOADS.sendInteractiveMessage
517
+ });
518
+ }
519
+ if (strict.warnings.length) {
520
+ console.warn('[button-sender] sendInteractiveMessage warnings:', strict.warnings);
521
+ }
522
+ }
523
+ // Step 2 — convert to proto shape
524
+ const convertedContent = convertToInteractiveMessage(content);
525
+ const { errors: cErr, warnings: cWarn, valid: cValid } = validateInteractiveMessageContent(convertedContent);
526
+ if (!cValid) {
527
+ throw new InteractiveValidationError('Converted interactive content invalid', {
528
+ context: 'sendInteractiveMessage.validateInteractiveMessageContent',
529
+ errors: cErr,
530
+ warnings: cWarn,
531
+ example: convertToInteractiveMessage(EXAMPLE_PAYLOADS.sendInteractiveMessage)
532
+ });
533
+ }
534
+ if (cWarn.length)
535
+ console.warn('[button-sender] Interactive content warnings:', cWarn);
536
+ // Step 3 — build WAMessage (uses internal @queenanya/baileys helpers directly)
537
+ const userJid = sock.authState?.creds?.me?.id ?? sock.user?.id ?? '';
538
+ const fullMsg = generateWAMessageFromContent(jid, convertedContent, {
539
+ userJid,
540
+ messageId: generateMessageIDV2(userJid),
541
+ timestamp: new Date()
542
+ });
543
+ // Step 4 — derive binary nodes
544
+ // normalizeMessageContent unwraps view-once/ephemerals so getButtonType
545
+ // inspects the real inner message type correctly.
546
+ const normalizedContent = normalizeMessageContent(fullMsg.message);
547
+ const buttonType = normalizedContent ? getButtonType(normalizedContent) : undefined;
548
+ const additionalNodes = [...(options.additionalNodes ?? [])];
549
+ if (buttonType && normalizedContent) {
550
+ // biz > interactive > native_flow(v:9, mixed) — exact sendButton node structure
551
+ // Works on: Android, iOS, WA Messenger, WA Business
552
+ const bizNode = getButtonArgs(normalizedContent);
553
+ additionalNodes.push(bizNode);
554
+ // Private chats also need the bot node
555
+ if (!isJidGroup(jid)) {
556
+ additionalNodes.push({ tag: 'bot', attrs: { biz_bot: '1' } });
557
+ }
558
+ console.log('[button-sender] Interactive send:', {
559
+ type: buttonType,
560
+ nodes: additionalNodes.map(n => ({ tag: n.tag, attrs: n.attrs })),
561
+ private: !isJidGroup(jid)
562
+ });
563
+ }
564
+ // Step 5 — relay
565
+ await sock.relayMessage(jid, fullMsg.message, {
566
+ messageId: fullMsg.key.id,
567
+ useCachedGroupMetadata: options.useCachedGroupMetadata,
568
+ additionalAttributes: options.additionalAttributes ?? {},
569
+ statusJidList: options.statusJidList,
570
+ additionalNodes
571
+ });
572
+ // Step 6 (optional) — emit to local event stream
573
+ // Only for private chats to avoid duplicate processing in groups.
574
+ if (sock.config?.emitOwnEvents && !isJidGroup(jid)) {
575
+ process.nextTick(() => {
576
+ if (sock.processingMutex?.mutex && sock.upsertMessage) {
577
+ void sock.processingMutex.mutex(() => sock.upsertMessage(fullMsg, 'append'));
578
+ }
579
+ });
580
+ }
581
+ return fullMsg;
582
+ }
583
+ /**
584
+ * Extended send variant with thumbnail / externalAdReply patching.
585
+ * Merged best-of-both from button-helper v2.2.3 + v2.2.5.
586
+ *
587
+ * Dummy document priority (Patch 1):
588
+ * 1. content.filePath — load file from local disk path (v2.2.3)
589
+ * 2. content.fileUrl — fetch file buffer from a URL (v2.2.3)
590
+ * 3. content.thumbnailUrl — fetch jpegThumbnail buffer AND use as dummy (v2.2.5)
591
+ * 4. fallback — plain text dummy buffer (v2.2.3)
592
+ * mimetype is taken from content.mimetype if provided, else auto-detected.
593
+ *
594
+ * externalAdReply (Patch 2) — merged fields from both versions:
595
+ * - containsAutoReply : true (v2.2.3)
596
+ * - mediaUrl / thumbnailUrl (v2.2.5)
597
+ * - renderLargerThumbnail : true (v2.2.5)
598
+ * - jpegThumbnail buffer (v2.2.5 — WA trusts this most)
599
+ *
600
+ * axios is a soft peer dependency — errors are non-fatal (console warn only).
601
+ *
602
+ * @example — thumbnail from URL
603
+ * await sendInteractiveMessageV2(sock, jid, {
604
+ * text: 'Hello',
605
+ * thumbnailUrl: 'https://example.com/thumb.jpg',
606
+ * interactiveButtons: [{ name: 'quick_reply', buttonParamsJson: '{"display_text":"Hi","id":"hi"}' }],
607
+ * })
608
+ *
609
+ * @example — dummy file from local path
610
+ * await sendInteractiveMessageV2(sock, jid, {
611
+ * text: 'Hello',
612
+ * filePath: '/tmp/myfile.pdf',
613
+ * mimetype: 'application/pdf',
614
+ * interactiveButtons: [...],
615
+ * })
616
+ */
617
+ export async function sendInteractiveMessageV2(sock, jid, content, options = {}) {
618
+ if (!sock) {
619
+ throw new InteractiveValidationError('Socket is required', {
620
+ context: 'sendInteractiveMessageV2'
621
+ });
622
+ }
623
+ const hasThumb = !!content.thumbnailUrl;
624
+ const hasFilePath = !!content.filePath;
625
+ const hasFileUrl = !!content.fileUrl;
626
+ const shouldForce = options.forceExternalAdReply === true;
627
+ // ── Helper: fetch buffer from any URL (axios soft peer dep) ───────────────
628
+ async function bufferFromUrl(url) {
629
+ try {
630
+ // @ts-ignore — axios is an optional peer dependency
631
+ const { default: axios } = await import('axios');
632
+ const res = await axios.get(url, { responseType: 'arraybuffer' });
633
+ return Buffer.from(res.data);
634
+ }
635
+ catch (e) {
636
+ console.warn('[button-sender] ⚠️ Failed to fetch buffer from URL:', e);
637
+ return null;
638
+ }
639
+ }
640
+ // ── Patch 1 — build dummy document so externalAdReply renders thumbnail ───
641
+ // Only inject when no media already present in the payload.
642
+ if ((hasThumb || hasFilePath || hasFileUrl || shouldForce) && !content.document && !content.image && !content.video) {
643
+ try {
644
+ let fileBuffer;
645
+ let fileName = 'QueenAnya.pdf';
646
+ let mimeType = 'application/pdf';
647
+ if (hasFilePath) {
648
+ // v2.2.3: load from local disk
649
+ const { readFileSync } = await import('fs');
650
+ fileBuffer = readFileSync(content.filePath);
651
+ fileName = content.filePath.split('/').pop() ?? fileName;
652
+ mimeType = content.mimetype ?? 'application/octet-stream';
653
+ }
654
+ else if (hasFileUrl) {
655
+ // v2.2.3: fetch from remote URL
656
+ const buf = await bufferFromUrl(content.fileUrl);
657
+ fileBuffer = buf ?? Buffer.from('dummy', 'utf-8');
658
+ fileName = content.fileUrl.split('/').pop() ?? fileName;
659
+ mimeType = content.mimetype ?? 'application/octet-stream';
660
+ }
661
+ else {
662
+ // v2.2.5: simple hardcoded dummy PDF
663
+ fileBuffer = Buffer.from('dummy', 'utf-8');
664
+ // keep fileName / mimeType defaults above
665
+ }
666
+ content.document = fileBuffer;
667
+ content.fileName = content.fileName ?? fileName;
668
+ content.mimetype = content.mimetype ?? mimeType;
669
+ }
670
+ catch (e) {
671
+ console.warn('[button-sender] ⚠️ Failed to build dummy document:', e);
672
+ }
673
+ }
674
+ // ── Fetch jpegThumbnail buffer from thumbnailUrl (v2.2.5) ─────────────────
675
+ // WA trusts jpegThumbnail buffer more than a bare URL string.
676
+ let jpegThumb = null;
677
+ if (hasThumb)
678
+ jpegThumb = await bufferFromUrl(content.thumbnailUrl);
679
+ // ── Patch 2 — build externalAdReply contextInfo (merged v2.2.3 + v2.2.5) ─
680
+ if (hasThumb || hasFilePath || hasFileUrl || shouldForce) {
681
+ const thumbUrl = (content.thumbnailUrl ?? options.thumbnailUrl ?? '');
682
+ const existingCtx = (content.contextInfo ?? {});
683
+ const existingEar = (existingCtx.externalAdReply ?? {});
684
+ content.contextInfo = {
685
+ ...existingCtx,
686
+ externalAdReply: {
687
+ ...existingEar,
688
+ mediaType: 1,
689
+ // v2.2.3 field — marks this as an auto-reply card
690
+ containsAutoReply: true,
691
+ title: (existingEar.title ??
692
+ `© ${globalThis.ownername ?? 'QueenAnya'} - 2025`),
693
+ body: (existingEar.body ?? 'Virtual Assistant'),
694
+ sourceUrl: (existingEar.sourceUrl ?? 'https://example.com'),
695
+ // v2.2.5 fields — reliable thumbnail display on all clients
696
+ mediaUrl: thumbUrl,
697
+ thumbnailUrl: thumbUrl,
698
+ renderLargerThumbnail: true,
699
+ // jpegThumbnail buffer: WA Messenger + Business trust this most
700
+ ...(jpegThumb ? { jpegThumbnail: jpegThumb } : {})
701
+ }
702
+ };
703
+ }
704
+ return sendInteractiveMessage(sock, jid, content, options);
705
+ }
706
+ // ─────────────────────────────────────────────────────────────────────────────
707
+ // Public convenience wrapper
708
+ // ─────────────────────────────────────────────────────────────────────────────
709
+ /**
710
+ * Convenience wrapper for the most common quick-reply / CTA button use case.
711
+ *
712
+ * @example — quick-reply
713
+ * await sendButtons(sock, jid, {
714
+ * text: 'Are you sure?',
715
+ * footer: 'Bot v1',
716
+ * buttons: [
717
+ * { id: 'yes', text: 'Yes' },
718
+ * { id: 'no', text: 'No' },
719
+ * ],
720
+ * })
721
+ *
722
+ * @example — mixed quick-reply + CTA URL
723
+ * await sendButtons(sock, jid, {
724
+ * text: 'Check our site',
725
+ * buttons: [
726
+ * { id: 'info', text: 'More Info' },
727
+ * { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Open', url: 'https://example.com' }) },
728
+ * ],
729
+ * })
730
+ */
731
+ export async function sendButtons(sock, jid, data = { text: '', buttons: [] }, options = {}) {
732
+ if (!sock) {
733
+ throw new InteractiveValidationError('Socket is required', { context: 'sendButtons' });
734
+ }
735
+ const { text = '', footer = '', title, subtitle, buttons = [] } = data;
736
+ // Strict payload validation
737
+ const strict = validateSendButtonsPayload({ text, buttons, title, subtitle, footer });
738
+ if (!strict.valid) {
739
+ throw new InteractiveValidationError('Buttons payload invalid', {
740
+ context: 'sendButtons.validateSendButtonsPayload',
741
+ errors: strict.errors,
742
+ warnings: strict.warnings,
743
+ example: EXAMPLE_PAYLOADS.sendButtons
744
+ });
745
+ }
746
+ if (strict.warnings.length) {
747
+ console.warn('[button-sender] sendButtons warnings:', strict.warnings);
748
+ }
749
+ // Validate authoring buttons
750
+ const { errors, warnings, cleaned } = validateAuthoringButtons(buttons);
751
+ if (errors.length) {
752
+ throw new InteractiveValidationError('Authoring button objects invalid', {
753
+ context: 'sendButtons.validateAuthoringButtons',
754
+ errors,
755
+ warnings,
756
+ example: EXAMPLE_PAYLOADS.sendButtons.buttons
757
+ });
758
+ }
759
+ if (warnings.length) {
760
+ console.warn('[button-sender] Button validation warnings:', warnings);
761
+ }
762
+ // Normalise to native_flow format
763
+ const interactiveButtons = buildInteractiveButtons(cleaned);
764
+ const payload = { text, footer, interactiveButtons };
765
+ if (title)
766
+ payload.title = title;
767
+ if (subtitle)
768
+ payload.subtitle = subtitle;
769
+ return sendInteractiveMessage(sock, jid, payload, options);
770
+ }
771
+ //# sourceMappingURL=button-sender.js.map