@badzz88/baileys 8.4.7 → 8.4.9

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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1055 -337
  3. package/WAProto/WAProto.proto +347 -6
  4. package/WAProto/fix-import.js +38 -0
  5. package/WAProto/fix-imports.js +86 -85
  6. package/WAProto/index.d.ts +4913 -25
  7. package/WAProto/index.js +14074 -98
  8. package/package.json +50 -90
  9. package/src/Defaults/index.js +201 -0
  10. package/src/Defaults/phonenumber-mcc.json +223 -0
  11. package/src/Signal/Group/ciphertext-message.js +15 -0
  12. package/src/Signal/Group/group-session-builder.js +92 -0
  13. package/src/Signal/Group/group_cipher.js +89 -0
  14. package/src/Signal/Group/index.js +136 -0
  15. package/src/Signal/Group/keyhelper.js +73 -0
  16. package/src/Signal/Group/sender-chain-key.js +32 -0
  17. package/src/Signal/Group/sender-key-distribution-message.js +66 -0
  18. package/src/Signal/Group/sender-key-message.js +69 -0
  19. package/src/Signal/Group/sender-key-name.js +50 -0
  20. package/src/Signal/Group/sender-key-record.js +44 -0
  21. package/src/Signal/Group/sender-key-state.js +97 -0
  22. package/src/Signal/Group/sender-message-key.js +30 -0
  23. package/src/Signal/libsignal.js +470 -0
  24. package/src/Signal/lid-mapping.js +262 -0
  25. package/src/Socket/Client/index.js +30 -0
  26. package/src/Socket/Client/types.js +13 -0
  27. package/src/Socket/Client/websocket.js +62 -0
  28. package/src/Socket/aigroups.js +240 -0
  29. package/src/Socket/business.js +422 -0
  30. package/src/Socket/chats.js +2374 -0
  31. package/src/Socket/communities.js +580 -0
  32. package/src/Socket/graphql.js +915 -0
  33. package/src/Socket/groups.js +812 -0
  34. package/src/Socket/index.js +37 -0
  35. package/src/Socket/interactive-handler.js +579 -0
  36. package/src/Socket/interop.js +566 -0
  37. package/src/Socket/managed-account.js +214 -0
  38. package/src/Socket/messages-recv.js +3012 -0
  39. package/src/Socket/messages-send.js +2163 -0
  40. package/{lib → src}/Socket/mex.js +11 -5
  41. package/src/Socket/newsletter.js +1057 -0
  42. package/src/Socket/privacy.js +452 -0
  43. package/src/Socket/registration.js +434 -0
  44. package/src/Socket/socket.js +1079 -0
  45. package/src/Socket/text-router.js +67 -0
  46. package/src/Socket/username.js +234 -0
  47. package/src/Store/index.js +36 -0
  48. package/src/Store/make-cache-manager-store.js +90 -0
  49. package/src/Store/make-in-memory-store.js +506 -0
  50. package/src/Store/make-ordered-dictionary.js +81 -0
  51. package/src/Store/object-repository.js +29 -0
  52. package/src/Types/Auth.js +38 -0
  53. package/src/Types/Bussines.js +2 -0
  54. package/src/Types/Call.js +2 -0
  55. package/src/Types/Chat.js +4 -0
  56. package/src/Types/Contact.js +2 -0
  57. package/src/Types/Events.js +2 -0
  58. package/src/Types/GroupMetadata.js +2 -0
  59. package/src/Types/Label.js +27 -0
  60. package/src/Types/LabelAssociation.js +9 -0
  61. package/src/Types/Message.js +95 -0
  62. package/src/Types/Newsletter.js +152 -0
  63. package/src/Types/Product.js +2 -0
  64. package/src/Types/Signal.js +2 -0
  65. package/src/Types/Socket.js +2 -0
  66. package/src/Types/State.js +70 -0
  67. package/src/Types/USync.js +2 -0
  68. package/src/Types/index.js +54 -0
  69. package/src/Utils/auth-utils.js +306 -0
  70. package/src/Utils/browser-utils.js +114 -0
  71. package/src/Utils/business.js +247 -0
  72. package/src/Utils/chat-utils.js +1272 -0
  73. package/src/Utils/consumer-application.js +107 -0
  74. package/src/Utils/crypto.js +125 -0
  75. package/src/Utils/decode-wa-message.js +808 -0
  76. package/src/Utils/event-buffer.js +586 -0
  77. package/src/Utils/generics.js +640 -0
  78. package/src/Utils/group-history.js +60 -0
  79. package/src/Utils/history.js +244 -0
  80. package/src/Utils/identity-change-handler.js +52 -0
  81. package/src/Utils/index.js +53 -0
  82. package/src/Utils/jid-display-normalization.js +218 -0
  83. package/src/Utils/link-preview.js +143 -0
  84. package/src/Utils/logger.js +9 -0
  85. package/src/Utils/lt-hash.js +10 -0
  86. package/src/Utils/make-mutex.js +36 -0
  87. package/src/Utils/message-composer.js +479 -0
  88. package/src/Utils/message-inspect.js +400 -0
  89. package/src/Utils/message-retry-manager.js +231 -0
  90. package/src/Utils/messages-media.js +943 -0
  91. package/src/Utils/messages.js +2490 -0
  92. package/src/Utils/meta-ai-msmsg.js +133 -0
  93. package/src/Utils/noise-handler.js +194 -0
  94. package/src/Utils/offline-node-processor.js +42 -0
  95. package/src/Utils/pre-key-manager.js +107 -0
  96. package/src/Utils/process-message.js +1047 -0
  97. package/src/Utils/reporting-utils.js +262 -0
  98. package/src/Utils/signal.js +192 -0
  99. package/src/Utils/stanza-ack.js +74 -0
  100. package/src/Utils/sync-action-utils.js +54 -0
  101. package/src/Utils/tc-token-utils.js +161 -0
  102. package/src/Utils/use-multi-file-auth-state.js +121 -0
  103. package/src/Utils/validate-connection.js +248 -0
  104. package/src/Utils/voip-rekey.js +22 -0
  105. package/src/WABinary/constants.js +1304 -0
  106. package/src/WABinary/decode.js +377 -0
  107. package/src/WABinary/encode.js +58 -0
  108. package/src/WABinary/generic-utils.js +148 -0
  109. package/src/WABinary/index.js +33 -0
  110. package/src/WABinary/jid-utils.js +374 -0
  111. package/src/WABinary/types.js +2 -0
  112. package/src/WAM/BinaryInfo.js +13 -0
  113. package/src/WAM/constants.js +39486 -0
  114. package/src/WAM/encode.js +142 -0
  115. package/src/WAM/index.js +31 -0
  116. package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
  117. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +100 -0
  118. package/src/WAUSync/Protocols/USyncContactProtocol.js +60 -0
  119. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +65 -0
  120. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
  121. package/src/WAUSync/Protocols/USyncFeatureProtocol.js +74 -0
  122. package/src/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
  123. package/src/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  124. package/src/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  125. package/src/WAUSync/Protocols/USyncStatusProtocol.js +44 -0
  126. package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  127. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +28 -0
  128. package/src/WAUSync/Protocols/index.js +40 -0
  129. package/src/WAUSync/USyncBackoff.js +31 -0
  130. package/src/WAUSync/USyncQuery.js +204 -0
  131. package/src/WAUSync/USyncUser.js +58 -0
  132. package/src/WAUSync/index.js +32 -0
  133. package/src/antiban.js +4726 -0
  134. package/{lib → src}/index.js +48 -15
  135. package/lib/Defaults/index.js +0 -130
  136. package/lib/Signal/Group/ciphertext-message.js +0 -12
  137. package/lib/Signal/Group/group-session-builder.js +0 -30
  138. package/lib/Signal/Group/group_cipher.js +0 -82
  139. package/lib/Signal/Group/index.js +0 -12
  140. package/lib/Signal/Group/keyhelper.js +0 -18
  141. package/lib/Signal/Group/sender-chain-key.js +0 -26
  142. package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
  143. package/lib/Signal/Group/sender-key-message.js +0 -66
  144. package/lib/Signal/Group/sender-key-name.js +0 -48
  145. package/lib/Signal/Group/sender-key-record.js +0 -41
  146. package/lib/Signal/Group/sender-key-state.js +0 -84
  147. package/lib/Signal/Group/sender-message-key.js +0 -26
  148. package/lib/Signal/libsignal.js +0 -431
  149. package/lib/Signal/lid-mapping.js +0 -277
  150. package/lib/Socket/Client/index.js +0 -3
  151. package/lib/Socket/Client/types.js +0 -11
  152. package/lib/Socket/Client/websocket.js +0 -54
  153. package/lib/Socket/business.js +0 -379
  154. package/lib/Socket/chats.js +0 -1193
  155. package/lib/Socket/communities.js +0 -431
  156. package/lib/Socket/groups.js +0 -374
  157. package/lib/Socket/index.js +0 -12
  158. package/lib/Socket/luxu.js +0 -386
  159. package/lib/Socket/messages-recv.js +0 -1916
  160. package/lib/Socket/messages-send.js +0 -1453
  161. package/lib/Socket/newsletter.js +0 -251
  162. package/lib/Socket/socket.js +0 -980
  163. package/lib/Socket/username.js +0 -146
  164. package/lib/Store/index.js +0 -10
  165. package/lib/Store/keyed-db.js +0 -108
  166. package/lib/Store/make-cache-manager-store.js +0 -85
  167. package/lib/Store/make-in-memory-store.js +0 -198
  168. package/lib/Store/make-ordered-dictionary.js +0 -75
  169. package/lib/Store/object-repository.js +0 -32
  170. package/lib/Types/Auth.js +0 -2
  171. package/lib/Types/Bussines.js +0 -2
  172. package/lib/Types/Call.js +0 -2
  173. package/lib/Types/Chat.js +0 -8
  174. package/lib/Types/Contact.js +0 -2
  175. package/lib/Types/Events.js +0 -2
  176. package/lib/Types/GroupMetadata.js +0 -2
  177. package/lib/Types/Label.js +0 -25
  178. package/lib/Types/LabelAssociation.js +0 -7
  179. package/lib/Types/Message.js +0 -11
  180. package/lib/Types/Mex.js +0 -37
  181. package/lib/Types/Product.js +0 -2
  182. package/lib/Types/Signal.js +0 -2
  183. package/lib/Types/Socket.js +0 -3
  184. package/lib/Types/State.js +0 -56
  185. package/lib/Types/USync.js +0 -2
  186. package/lib/Types/index.js +0 -26
  187. package/lib/Utils/auth-utils.js +0 -302
  188. package/lib/Utils/browser-utils.js +0 -49
  189. package/lib/Utils/business.js +0 -231
  190. package/lib/Utils/chat-utils.js +0 -872
  191. package/lib/Utils/companion-reg-client-utils.js +0 -35
  192. package/lib/Utils/crypto.js +0 -118
  193. package/lib/Utils/decode-wa-message.js +0 -350
  194. package/lib/Utils/event-buffer.js +0 -622
  195. package/lib/Utils/generics.js +0 -403
  196. package/lib/Utils/history.js +0 -134
  197. package/lib/Utils/identity-change-handler.js +0 -50
  198. package/lib/Utils/index.js +0 -23
  199. package/lib/Utils/link-preview.js +0 -85
  200. package/lib/Utils/logger.js +0 -3
  201. package/lib/Utils/lt-hash.js +0 -8
  202. package/lib/Utils/make-mutex.js +0 -33
  203. package/lib/Utils/message-composer.js +0 -273
  204. package/lib/Utils/message-retry-manager.js +0 -265
  205. package/lib/Utils/messages-media.js +0 -788
  206. package/lib/Utils/messages.js +0 -1260
  207. package/lib/Utils/noise-handler.js +0 -201
  208. package/lib/Utils/offline-node-processor.js +0 -40
  209. package/lib/Utils/pre-key-manager.js +0 -106
  210. package/lib/Utils/process-message.js +0 -630
  211. package/lib/Utils/reporting-utils.js +0 -258
  212. package/lib/Utils/signal.js +0 -201
  213. package/lib/Utils/stanza-ack.js +0 -38
  214. package/lib/Utils/sync-action-utils.js +0 -49
  215. package/lib/Utils/tc-token-utils.js +0 -163
  216. package/lib/Utils/use-multi-file-auth-state.js +0 -121
  217. package/lib/Utils/validate-connection.js +0 -203
  218. package/lib/WABinary/constants.js +0 -1301
  219. package/lib/WABinary/decode.js +0 -262
  220. package/lib/WABinary/encode.js +0 -220
  221. package/lib/WABinary/generic-utils.js +0 -204
  222. package/lib/WABinary/index.js +0 -6
  223. package/lib/WABinary/jid-utils.js +0 -98
  224. package/lib/WABinary/types.js +0 -2
  225. package/lib/WAM/BinaryInfo.js +0 -10
  226. package/lib/WAM/constants.js +0 -22853
  227. package/lib/WAM/encode.js +0 -150
  228. package/lib/WAM/index.js +0 -4
  229. package/lib/WAUSync/Protocols/USyncContactProtocol.js +0 -52
  230. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +0 -54
  231. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +0 -27
  232. package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -263
  233. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +0 -38
  234. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
  235. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
  236. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
  237. package/lib/WAUSync/Protocols/index.js +0 -6
  238. package/lib/WAUSync/USyncQuery.js +0 -98
  239. package/lib/WAUSync/USyncUser.js +0 -31
  240. package/lib/WAUSync/index.js +0 -4
@@ -0,0 +1,2490 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.generateWAMessage =
4
+ exports.generateWAMessageFromContent =
5
+ exports.generateWAMessageContent =
6
+ exports.hasNonNullishProperty =
7
+ exports.generateForwardMessageContent =
8
+ exports.prepareDisappearingMessageSettingContent =
9
+ exports.prepareWAMessageMedia =
10
+ exports.generateLinkPreviewIfRequired =
11
+ exports.extractUrlFromText =
12
+ void 0
13
+ const boom_1 = require('@hapi/boom')
14
+ const crypto_1 = require('crypto')
15
+ const fs_1 = require('fs')
16
+ const lru_cache_1 = require('lru-cache')
17
+ const WAProto_1 = require('../../WAProto/index.js')
18
+ const Defaults_1 = require('../Defaults')
19
+ const Types_1 = require('../Types')
20
+ const WABinary_1 = require('../WABinary')
21
+ const crypto_2 = require('./crypto')
22
+ const generics_1 = require('./generics')
23
+ const messages_media_1 = require('./messages-media')
24
+ const reporting_utils_1 = require('./reporting-utils')
25
+ const jid_display_normalization_1 = require('./jid-display-normalization')
26
+ const message_inspect_1 = require('./message-inspect')
27
+ Object.assign(exports, message_inspect_1)
28
+ // Group-invite thumbnails are fetched (IQ + HTTP GET) per generateWAMessage call;
29
+ // caching the downloaded bytes avoids redownloading the same group picture when
30
+ // the same invite is sent to many recipients in a burst.
31
+ const groupInviteThumbnailCache = new lru_cache_1.LRUCache({ max: 100, ttl: 5 * 60 * 1000 })
32
+ const MIMETYPE_MAP = {
33
+ image: 'image/jpeg',
34
+ video: 'video/mp4',
35
+ document: 'application/pdf',
36
+ audio: 'audio/ogg; codecs=opus',
37
+ sticker: 'image/webp',
38
+ 'product-catalog-image': 'image/jpeg'
39
+ }
40
+ const MessageTypeProto = {
41
+ image: WAProto_1.proto.Message.ImageMessage,
42
+ video: WAProto_1.proto.Message.VideoMessage,
43
+ audio: WAProto_1.proto.Message.AudioMessage,
44
+ sticker: WAProto_1.proto.Message.StickerMessage,
45
+ document: WAProto_1.proto.Message.DocumentMessage
46
+ }
47
+
48
+ // Input payloads can carry protobuf media message keys (e.g. imageMessage).
49
+ const MEDIA_MESSAGE_TYPE_ALIASES = {
50
+ imageMessage: 'image',
51
+ videoMessage: 'video',
52
+ audioMessage: 'audio',
53
+ documentMessage: 'document',
54
+ stickerMessage: 'sticker',
55
+ // PTV = push-to-video (video note) payload.
56
+ ptvMessage: 'video'
57
+ }
58
+ const MEDIA_MESSAGE_TYPE_ALIAS_KEYS = Object.keys(MEDIA_MESSAGE_TYPE_ALIASES)
59
+ const hasMediaPayload = message =>
60
+ Defaults_1.MEDIA_KEYS.some(key => key in message) || MEDIA_MESSAGE_TYPE_ALIAS_KEYS.some(key => key in message)
61
+
62
+ const ButtonType = WAProto_1.proto.Message.ButtonsMessage.HeaderType
63
+
64
+ const RICH_RESPONSE_CODE_KEYWORDS = new Set([
65
+ 'break',
66
+ 'case',
67
+ 'catch',
68
+ 'continue',
69
+ 'debugger',
70
+ 'default',
71
+ 'delete',
72
+ 'do',
73
+ 'else',
74
+ 'finally',
75
+ 'for',
76
+ 'function',
77
+ 'if',
78
+ 'in',
79
+ 'instanceof',
80
+ 'new',
81
+ 'return',
82
+ 'switch',
83
+ 'this',
84
+ 'throw',
85
+ 'try',
86
+ 'typeof',
87
+ 'var',
88
+ 'void',
89
+ 'while',
90
+ 'with',
91
+ 'true',
92
+ 'false',
93
+ 'null',
94
+ 'undefined',
95
+ 'NaN',
96
+ 'Infinity',
97
+ 'class',
98
+ 'const',
99
+ 'let',
100
+ 'super',
101
+ 'extends',
102
+ 'export',
103
+ 'import',
104
+ 'yield',
105
+ 'static',
106
+ 'constructor',
107
+ 'of',
108
+ 'async',
109
+ 'await',
110
+ 'get',
111
+ 'set',
112
+ 'implements',
113
+ 'interface',
114
+ 'package',
115
+ 'private',
116
+ 'protected',
117
+ 'public',
118
+ 'enum',
119
+ 'throws',
120
+ 'transient'
121
+ ])
122
+ const tokenizeCode = code => {
123
+ const tokens = []
124
+ let i = 0
125
+ const len = code.length
126
+ while (i < len) {
127
+ if (/\s/.test(code[i])) {
128
+ const start = i
129
+ while (i < len && /\s/.test(code[i])) i++
130
+ tokens.push({ content: code.slice(start, i), type: 'DEFAULT' })
131
+ continue
132
+ }
133
+ if (code[i] === '"' || code[i] === "'" || code[i] === '`') {
134
+ const start = i
135
+ const quote = code[i]
136
+ i++
137
+ while (i < len && code[i] !== quote) {
138
+ if (code[i] === '\\') i++
139
+ i++
140
+ }
141
+ i++
142
+ tokens.push({ content: code.slice(start, i), type: 'STR' })
143
+ continue
144
+ }
145
+ if (code[i] === '/' && i + 1 < len && code[i + 1] === '/') {
146
+ const start = i
147
+ while (i < len && code[i] !== '\n') i++
148
+ tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
149
+ continue
150
+ }
151
+ if (code[i] === '/' && i + 1 < len && code[i + 1] === '*') {
152
+ const start = i
153
+ i += 2
154
+ while (i + 1 < len && !(code[i] === '*' && code[i + 1] === '/')) i++
155
+ i += 2
156
+ tokens.push({ content: code.slice(start, i), type: 'COMMENT' })
157
+ continue
158
+ }
159
+ if (/[0-9]/.test(code[i])) {
160
+ const start = i
161
+ while (i < len && /[0-9.]/.test(code[i])) i++
162
+ tokens.push({ content: code.slice(start, i), type: 'NUMBER' })
163
+ continue
164
+ }
165
+ if (/[a-zA-Z_$]/.test(code[i])) {
166
+ const start = i
167
+ while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) i++
168
+ const word = code.slice(start, i)
169
+ if (RICH_RESPONSE_CODE_KEYWORDS.has(word)) {
170
+ tokens.push({ content: word, type: 'KEYWORD' })
171
+ } else {
172
+ let j = i
173
+ while (j < len && /\s/.test(code[j])) j++
174
+ tokens.push({ content: word, type: j < len && code[j] === '(' ? 'METHOD' : 'DEFAULT' })
175
+ }
176
+ continue
177
+ }
178
+ tokens.push({ content: code[i], type: 'DEFAULT' })
179
+ i++
180
+ }
181
+ const merged = []
182
+ for (const t of tokens) {
183
+ if (merged.length && merged[merged.length - 1].type === 'DEFAULT' && t.type === 'DEFAULT') {
184
+ merged[merged.length - 1].content += t.content
185
+ } else {
186
+ merged.push(t)
187
+ }
188
+ }
189
+ return merged
190
+ }
191
+
192
+ /**
193
+ * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
194
+ * @param text eg. hello https://google.com
195
+ * @returns the URL, eg. https://google.com
196
+ */
197
+ const extractUrlFromText = text => text.match(Defaults_1.URL_REGEX)?.[0]
198
+ exports.extractUrlFromText = extractUrlFromText
199
+ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
200
+ const url = (0, exports.extractUrlFromText)(text)
201
+ if (!!getUrlInfo && url) {
202
+ try {
203
+ const urlInfo = await getUrlInfo(url)
204
+ return urlInfo
205
+ } catch (error) {
206
+ // ignore if fails
207
+ logger?.warn({ trace: error.stack }, 'url generation failed')
208
+ }
209
+ }
210
+ }
211
+ exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired
212
+ const assertColor = async color => {
213
+ let assertedColor
214
+ if (typeof color === 'number') {
215
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
216
+ } else {
217
+ let hex = color.trim().replace('#', '')
218
+ if (hex.length <= 6) {
219
+ hex = 'FF' + hex.padStart(6, '0')
220
+ }
221
+ assertedColor = parseInt(hex, 16)
222
+ return assertedColor
223
+ }
224
+ }
225
+ const prepareWAMessageMedia = async (message, options) => {
226
+ const logger = options.logger
227
+ // AB props for feature-flagged media paths (passed via options.mediaAbProps).
228
+ const mediaAbProps = options.mediaAbProps || null
229
+ const getMediaProp = propName => (0, messages_media_1.getMediaProp)(mediaAbProps, propName)
230
+ let mediaType
231
+ for (const key of Defaults_1.MEDIA_KEYS) {
232
+ if (key in message) {
233
+ mediaType = key
234
+ }
235
+ }
236
+ if (!mediaType) {
237
+ throw new boom_1.Boom('Invalid media type', { statusCode: 400 })
238
+ }
239
+ const uploadData = {
240
+ ...message,
241
+ media: message[mediaType]
242
+ }
243
+ delete uploadData[mediaType]
244
+
245
+ // Feature B: HEVC video — when hevc_video_dual_upload is enabled, allow HEVC mimetype
246
+ // through without forcing it to mp4. When the prop is absent we leave the existing
247
+ // mimetype as-is (defaults are set further below).
248
+ if (mediaType === 'video' && uploadData.mimetype) {
249
+ const isHevc = uploadData.mimetype.includes('hevc') || uploadData.mimetype.includes('x-matroska')
250
+ if (isHevc && !getMediaProp('hevc_video_dual_upload')) {
251
+ // Prop not enabled: normalise to mp4 so receivers can decode it.
252
+ uploadData.mimetype = MIMETYPE_MAP['video']
253
+ }
254
+ // If the prop is enabled we leave the caller-supplied HEVC mimetype intact.
255
+ }
256
+
257
+ // check if cacheable + generate cache key
258
+ const cacheableKey =
259
+ typeof uploadData.media === 'object' &&
260
+ 'url' in uploadData.media &&
261
+ !!uploadData.media.url &&
262
+ !!options.mediaCache &&
263
+ mediaType + ':' + uploadData.media.url.toString()
264
+ if (mediaType === 'document' && !uploadData.fileName) {
265
+ uploadData.fileName = 'file'
266
+ }
267
+ if (!uploadData.mimetype) {
268
+ uploadData.mimetype = MIMETYPE_MAP[mediaType]
269
+ }
270
+ if (cacheableKey) {
271
+ const mediaBuff = await options.mediaCache.get(cacheableKey)
272
+ if (mediaBuff) {
273
+ logger?.debug({ cacheableKey }, 'got media cache hit')
274
+ const obj = WAProto_1.proto.Message.decode(mediaBuff)
275
+ const key = `${mediaType}Message`
276
+ Object.assign(obj[key], { ...uploadData, media: undefined })
277
+ return obj
278
+ }
279
+ }
280
+ const isNewsletter = !!options.jid && (0, WABinary_1.isJidNewsletter)(options.jid)
281
+ if (isNewsletter) {
282
+ logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter')
283
+ const { filePath, fileSha256, fileLength } = await (0, messages_media_1.getRawMediaUploadData)(
284
+ uploadData.media,
285
+ options.mediaTypeOverride || mediaType,
286
+ logger
287
+ )
288
+ const fileSha256B64 = fileSha256.toString('base64')
289
+ const { mediaUrl, directPath } = await options.upload(filePath, {
290
+ fileEncSha256B64: fileSha256B64,
291
+ mediaType: mediaType,
292
+ timeoutMs: options.mediaUploadTimeoutMs
293
+ })
294
+ await fs_1.promises.unlink(filePath)
295
+ const obj = WAProto_1.proto.Message.fromObject({
296
+ // todo: add more support here
297
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
298
+ url: mediaUrl,
299
+ directPath,
300
+ fileSha256,
301
+ fileLength,
302
+ ...uploadData,
303
+ media: undefined
304
+ })
305
+ })
306
+ if (uploadData.ptv) {
307
+ obj.ptvMessage = obj.videoMessage
308
+ delete obj.videoMessage
309
+ }
310
+ if (obj.stickerMessage) {
311
+ obj.stickerMessage.stickerSentTs = Date.now()
312
+ }
313
+ if (cacheableKey) {
314
+ logger?.debug({ cacheableKey }, 'set cache')
315
+ await options.mediaCache.set(cacheableKey, WAProto_1.proto.Message.encode(obj).finish())
316
+ }
317
+ return obj
318
+ }
319
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
320
+ const requiresThumbnailComputation =
321
+ (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
322
+
323
+ const requiresWaveformProcessing =
324
+ mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined'
325
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
326
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
327
+ const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await (0,
328
+ messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
329
+ logger,
330
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
331
+ opts: options.options
332
+ })
333
+ const fileEncSha256B64 = fileEncSha256.toString('base64')
334
+ const [{ mediaUrl, directPath }] = await Promise.all([
335
+ (async () => {
336
+ const result = await options.upload(encFilePath, {
337
+ fileEncSha256B64,
338
+ mediaType,
339
+ timeoutMs: options.mediaUploadTimeoutMs
340
+ })
341
+ logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
342
+ return result
343
+ })(),
344
+ (async () => {
345
+ try {
346
+ if (requiresThumbnailComputation) {
347
+ const { thumbnail, originalImageDimensions } = await (0, messages_media_1.generateThumbnail)(
348
+ originalFilePath,
349
+ mediaType,
350
+ options
351
+ )
352
+ uploadData.jpegThumbnail = thumbnail
353
+ if (!uploadData.width && originalImageDimensions) {
354
+ uploadData.width = originalImageDimensions.width
355
+ uploadData.height = originalImageDimensions.height
356
+ logger?.debug('set dimensions')
357
+ }
358
+ logger?.debug('generated thumbnail')
359
+ }
360
+ if (requiresDurationComputation) {
361
+ uploadData.seconds = await (0, messages_media_1.getAudioDuration)(originalFilePath)
362
+ logger?.debug('computed audio duration')
363
+ }
364
+ if (requiresWaveformProcessing) {
365
+ uploadData.waveform = await (0, messages_media_1.getAudioWaveform)(originalFilePath, logger)
366
+ logger?.debug('processed waveform')
367
+ }
368
+ if (requiresAudioBackground) {
369
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor)
370
+ logger?.debug('computed backgroundColor audio status')
371
+ }
372
+ } catch (error) {
373
+ logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
374
+ }
375
+ })()
376
+ ]).finally(async () => {
377
+ try {
378
+ await fs_1.promises.unlink(encFilePath)
379
+ if (originalFilePath) {
380
+ await fs_1.promises.unlink(originalFilePath)
381
+ }
382
+ logger?.debug('removed tmp files')
383
+ } catch (error) {
384
+ logger?.warn('failed to remove tmp file')
385
+ }
386
+ })
387
+
388
+ // Feature A: HD image dual upload — set mediaHd flag when prop is enabled and caller
389
+ // explicitly requests HD quality via uploadData.hd === true.
390
+ const hdImageEnabled = mediaType === 'image' && getMediaProp('hd_image_dual_upload') && !!uploadData.hd
391
+ const hdVideoEnabled = mediaType === 'video' && getMediaProp('hd_video_dual_upload') && !!uploadData.hd
392
+ if (hdImageEnabled || hdVideoEnabled) {
393
+ uploadData.mediaHd = true
394
+ logger?.debug({ mediaType }, 'HD dual upload enabled, setting mediaHd flag')
395
+ }
396
+
397
+ // Feature C: Motion photo — propagate motionPhoto field for image messages when present.
398
+ // The caller sets message.motionPhoto = true (or a Buffer for the motion data).
399
+ if (mediaType === 'image' && uploadData.motionPhoto != null) {
400
+ // Keep motionPhoto in uploadData; it will be spread into the proto below.
401
+ // If it is a boolean we coerce to 1/0 for the proto field (uint32 in WA proto).
402
+ if (typeof uploadData.motionPhoto === 'boolean') {
403
+ uploadData.motionPhoto = uploadData.motionPhoto ? 1 : 0
404
+ }
405
+ logger?.debug('motion photo field set on image message')
406
+ }
407
+
408
+ const obj = WAProto_1.proto.Message.fromObject({
409
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
410
+ url: mediaUrl,
411
+ directPath,
412
+ mediaKey,
413
+ fileEncSha256,
414
+ fileSha256,
415
+ fileLength,
416
+ mediaKeyTimestamp: (0, generics_1.unixTimestampSeconds)(),
417
+ ...uploadData,
418
+ media: undefined
419
+ })
420
+ })
421
+ if (uploadData.ptv) {
422
+ obj.ptvMessage = obj.videoMessage
423
+ delete obj.videoMessage
424
+ }
425
+ if (cacheableKey) {
426
+ logger?.debug({ cacheableKey }, 'set cache')
427
+ await options.mediaCache.set(cacheableKey, WAProto_1.proto.Message.encode(obj).finish())
428
+ }
429
+ return obj
430
+ }
431
+ exports.prepareWAMessageMedia = prepareWAMessageMedia
432
+ const prepareDisappearingMessageSettingContent = ephemeralExpiration => {
433
+ ephemeralExpiration = ephemeralExpiration || 0
434
+ const content = {
435
+ ephemeralMessage: {
436
+ message: {
437
+ protocolMessage: {
438
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
439
+ ephemeralExpiration
440
+ }
441
+ }
442
+ }
443
+ }
444
+ return WAProto_1.proto.Message.fromObject(content)
445
+ }
446
+ exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent
447
+ /**
448
+ * Generate forwarded message content like WA does
449
+ * @param message the message to forward
450
+ * @param options.forceForward will show the message as forwarded even if it is from you
451
+ */
452
+ const generateForwardMessageContent = (message, forceForward) => {
453
+ let content = message.message
454
+ if (!content) {
455
+ throw new boom_1.Boom('no content in message', { statusCode: 400 })
456
+ }
457
+ // hacky copy
458
+ content = (0, exports.normalizeMessageContent)(content)
459
+ content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish())
460
+ let key = Object.keys(content)[0]
461
+ let score = content?.[key]?.contextInfo?.forwardingScore || 0
462
+ score += message.key.fromMe && !forceForward ? 0 : 1
463
+ if (key === 'conversation') {
464
+ content.extendedTextMessage = { text: content[key] }
465
+ delete content.conversation
466
+ key = 'extendedTextMessage'
467
+ }
468
+ const key_ = content?.[key]
469
+ if (score > 0) {
470
+ key_.contextInfo = { forwardingScore: score, isForwarded: true }
471
+ } else {
472
+ key_.contextInfo = {}
473
+ }
474
+ return content
475
+ }
476
+ exports.generateForwardMessageContent = generateForwardMessageContent
477
+ const hasNonNullishProperty = (message, key) => {
478
+ return (
479
+ typeof message === 'object' &&
480
+ message !== null &&
481
+ key in message &&
482
+ message[key] !== null &&
483
+ message[key] !== undefined
484
+ )
485
+ }
486
+ exports.hasNonNullishProperty = hasNonNullishProperty
487
+ function hasOptionalProperty(obj, key) {
488
+ return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null
489
+ }
490
+ const normalizeEarFields = ear => {
491
+ const result = { ...ear }
492
+ const applyAlias = (fromKey, toKey) => {
493
+ if (result[fromKey] !== undefined && result[toKey] === undefined) result[toKey] = result[fromKey]
494
+ }
495
+ applyAlias('thumbnail_url', 'thumbnailUrl')
496
+ applyAlias('thumbnailUrl', 'thumbnail')
497
+ applyAlias('source_url', 'sourceUrl')
498
+ applyAlias('media_type', 'mediaType')
499
+ applyAlias('show_ad_attribution', 'showAdAttribution')
500
+ applyAlias('render_larger_thumbnail', 'renderLargerThumbnail')
501
+ if (result.thumbnail && !result.jpegThumbnail) result.jpegThumbnail = result.thumbnail
502
+ if (result.largeThumbnail !== undefined && result.renderLargerThumbnail === undefined)
503
+ result.renderLargerThumbnail = result.largeThumbnail
504
+ if (result.url && !result.sourceUrl) result.sourceUrl = result.url
505
+ delete result.thumbnail
506
+ delete result.largeThumbnail
507
+ delete result.url
508
+ delete result.thumbnail_url
509
+ delete result.source_url
510
+ delete result.media_type
511
+ delete result.show_ad_attribution
512
+ delete result.render_larger_thumbnail
513
+ return result
514
+ }
515
+ const normalizeQuickReplyButton = button => {
516
+ var _a
517
+ if (button.name && typeof button.name === 'string') {
518
+ return {
519
+ name: button.name,
520
+ buttonParamsJson:
521
+ typeof button.buttonParamsJson === 'string'
522
+ ? button.buttonParamsJson
523
+ : JSON.stringify(button.buttonParamsJson || {})
524
+ }
525
+ }
526
+ if (button.type === 4 && button.nativeFlowInfo) {
527
+ const { name, paramsJson } = button.nativeFlowInfo
528
+ return {
529
+ name: name || 'quick_reply',
530
+ buttonParamsJson: typeof paramsJson === 'string' ? paramsJson : JSON.stringify(paramsJson || {})
531
+ }
532
+ }
533
+ const buttonTextObject = button.buttonText && typeof button.buttonText === 'object' ? button.buttonText : undefined
534
+ const displayTextCandidates = [
535
+ button.text,
536
+ button.displayText,
537
+ button.display_text,
538
+ typeof button.buttonText === 'string' ? button.buttonText : undefined,
539
+ buttonTextObject === null || buttonTextObject === void 0 ? void 0 : buttonTextObject.displayText,
540
+ buttonTextObject === null || buttonTextObject === void 0 ? void 0 : buttonTextObject.display_text
541
+ ]
542
+ const displayText = displayTextCandidates.find(value => typeof value === 'string' && value.length > 0) || ''
543
+ const id =
544
+ button.buttonId || button.id || ((_a = button.buttonParamsJson) === null || _a === void 0 ? void 0 : _a.id) || ''
545
+ return {
546
+ name: 'quick_reply',
547
+ buttonParamsJson: JSON.stringify({
548
+ display_text: displayText,
549
+ id
550
+ })
551
+ }
552
+ }
553
+ const asciiDecode = arr => arr.map(e => String.fromCharCode(e)).join('')
554
+
555
+ const applyContextInfoAndMentions = (interactiveMessage, message) => {
556
+ if ('contextInfo' in message && !!message.contextInfo) {
557
+ interactiveMessage.contextInfo = message.contextInfo
558
+ }
559
+ if ('mentions' in message && !!message.mentions) {
560
+ interactiveMessage.contextInfo = {
561
+ ...(interactiveMessage.contextInfo || {}),
562
+ mentionedJid: message.mentions
563
+ }
564
+ }
565
+ }
566
+ const buildPaymentNoteMessage = async (paymentPayload, options, fallbackText = '') => {
567
+ let notes
568
+ if (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.sticker) {
569
+ const stickerPrep = await (0, exports.prepareWAMessageMedia)({ sticker: paymentPayload.sticker }, options)
570
+ notes = {
571
+ stickerMessage: {
572
+ ...(stickerPrep === null || stickerPrep === void 0 ? void 0 : stickerPrep.stickerMessage),
573
+ contextInfo: paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.contextInfo
574
+ }
575
+ }
576
+ } else if (
577
+ typeof (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.note) === 'string'
578
+ ) {
579
+ notes = {
580
+ extendedTextMessage: {
581
+ text: paymentPayload.note,
582
+ contextInfo: paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.contextInfo
583
+ }
584
+ }
585
+ } else if (
586
+ (paymentPayload === null || paymentPayload === void 0 ? void 0 : paymentPayload.noteMessage) &&
587
+ typeof paymentPayload.noteMessage === 'object'
588
+ ) {
589
+ const noteKeys = Object.keys(paymentPayload.noteMessage)
590
+ const allowedNoteMessageKeys = ['extendedTextMessage', 'stickerMessage']
591
+ const hasOnlyAllowedKeys = noteKeys.length > 0 && noteKeys.every(key => allowedNoteMessageKeys.includes(key))
592
+ if (!noteKeys.length || !hasOnlyAllowedKeys) {
593
+ throw new boom_1.Boom('Invalid payment noteMessage', { statusCode: 400 })
594
+ }
595
+ notes = paymentPayload.noteMessage
596
+ } else {
597
+ notes = { extendedTextMessage: { text: fallbackText } }
598
+ }
599
+ return notes
600
+ }
601
+
602
+ const generateWAMessageContent = async (message, options) => {
603
+ var _a, _b
604
+ let m = {}
605
+ const hasCaptionWithoutMedia = 'caption' in message && !hasMediaPayload(message)
606
+ const hasCaptionContainer =
607
+ ('groupStatus' in message && !!message.groupStatus) || ('viewOnce' in message && !!message.viewOnce)
608
+ if ((0, exports.hasNonNullishProperty)(message, 'text')) {
609
+ const extContent = { text: message.text }
610
+ let urlInfo = message.linkPreview
611
+ if (typeof urlInfo === 'undefined') {
612
+ urlInfo = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger)
613
+ }
614
+ if (urlInfo) {
615
+ extContent.matchedText = urlInfo['matched-text']
616
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail
617
+ extContent.description = urlInfo.description
618
+ extContent.title = urlInfo.title
619
+ extContent.previewType = 0
620
+ const img = urlInfo.highQualityThumbnail
621
+ if (img) {
622
+ extContent.thumbnailDirectPath = img.directPath
623
+ extContent.mediaKey = img.mediaKey
624
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
625
+ extContent.thumbnailWidth = img.width
626
+ extContent.thumbnailHeight = img.height
627
+ extContent.thumbnailSha256 = img.fileSha256
628
+ extContent.thumbnailEncSha256 = img.fileEncSha256
629
+ }
630
+ }
631
+ if (options.backgroundColor) {
632
+ extContent.backgroundArgb = await assertColor(options.backgroundColor)
633
+ }
634
+ if (options.font) {
635
+ extContent.font = options.font
636
+ }
637
+ if (
638
+ options.jid &&
639
+ (0, WABinary_1.isInteropUser)(options.jid) &&
640
+ !options.backgroundColor &&
641
+ !options.font &&
642
+ !urlInfo
643
+ ) {
644
+ m.conversation = message.text
645
+ } else {
646
+ m.extendedTextMessage = extContent
647
+ }
648
+ } else if ((0, exports.hasNonNullishProperty)(message, 'contacts')) {
649
+ const contactLen = message.contacts.contacts.length
650
+ if (!contactLen) {
651
+ throw new boom_1.Boom('require atleast 1 contact', { statusCode: 400 })
652
+ }
653
+ if (contactLen === 1) {
654
+ m.contactMessage = WAProto_1.proto.Message.ContactMessage.create(message.contacts.contacts[0])
655
+ } else {
656
+ m.contactsArrayMessage = WAProto_1.proto.Message.ContactsArrayMessage.create(message.contacts)
657
+ }
658
+ } else if ((0, exports.hasNonNullishProperty)(message, 'location')) {
659
+ m.locationMessage = WAProto_1.proto.Message.LocationMessage.create(message.location)
660
+ } else if ((0, exports.hasNonNullishProperty)(message, 'react')) {
661
+ if (!message.react.senderTimestampMs) {
662
+ message.react.senderTimestampMs = Date.now()
663
+ }
664
+ m.reactionMessage = WAProto_1.proto.Message.ReactionMessage.create(message.react)
665
+ } else if ((0, exports.hasNonNullishProperty)(message, 'delete')) {
666
+ m.protocolMessage = {
667
+ key: message.delete,
668
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE
669
+ }
670
+ } else if ((0, exports.hasNonNullishProperty)(message, 'forward')) {
671
+ m = (0, exports.generateForwardMessageContent)(message.forward, message.force)
672
+ } else if ((0, exports.hasNonNullishProperty)(message, 'disappearingMessagesInChat')) {
673
+ const exp =
674
+ typeof message.disappearingMessagesInChat === 'boolean'
675
+ ? message.disappearingMessagesInChat
676
+ ? Defaults_1.WA_DEFAULT_EPHEMERAL
677
+ : 0
678
+ : message.disappearingMessagesInChat
679
+ m = (0, exports.prepareDisappearingMessageSettingContent)(exp)
680
+ } else if ((0, exports.hasNonNullishProperty)(message, 'groupInvite')) {
681
+ m.groupInviteMessage = {}
682
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode
683
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration
684
+ m.groupInviteMessage.caption = message.groupInvite.text
685
+ m.groupInviteMessage.groupJid = message.groupInvite.jid
686
+ m.groupInviteMessage.groupName = message.groupInvite.subject
687
+ if (options.getProfilePicUrl) {
688
+ const cached = groupInviteThumbnailCache.get(message.groupInvite.jid)
689
+ if (cached) {
690
+ m.groupInviteMessage.jpegThumbnail = cached
691
+ } else {
692
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview')
693
+ if (pfpUrl) {
694
+ const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
695
+ if (resp.ok) {
696
+ const buf = Buffer.from(await resp.arrayBuffer())
697
+ m.groupInviteMessage.jpegThumbnail = buf
698
+ groupInviteThumbnailCache.set(message.groupInvite.jid, buf)
699
+ }
700
+ }
701
+ }
702
+ }
703
+ } else if ((0, exports.hasNonNullishProperty)(message, 'pin')) {
704
+ m.pinInChatMessage = {}
705
+ m.messageContextInfo = {}
706
+ m.pinInChatMessage.key = message.pin
707
+ m.pinInChatMessage.type = message.type
708
+ m.pinInChatMessage.senderTimestampMs = Date.now()
709
+ m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0
710
+ } else if ((0, exports.hasNonNullishProperty)(message, 'buttonReply')) {
711
+ switch (message.type) {
712
+ case 'template':
713
+ m.templateButtonReplyMessage = {
714
+ selectedDisplayText: message.buttonReply.displayText,
715
+ selectedId: message.buttonReply.id,
716
+ selectedIndex: message.buttonReply.index
717
+ }
718
+ break
719
+ case 'plain':
720
+ m.buttonsResponseMessage = {
721
+ selectedButtonId: message.buttonReply.id,
722
+ selectedDisplayText: message.buttonReply.displayText,
723
+ type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
724
+ }
725
+ break
726
+ case 'interactive':
727
+ m.interactiveResponseMessage = {
728
+ body: {
729
+ text: message.buttonReply.displayText,
730
+ format: WAProto_1.proto.Message.InteractiveResponseMessage.Body.Format.EXTENSIONS_1
731
+ },
732
+ nativeFlowResponseMessage: {
733
+ name: message.buttonReply.nativeFlows.name,
734
+ paramsJson: message.buttonReply.nativeFlows.paramsJson,
735
+ version: message.buttonReply.nativeFlows.version
736
+ }
737
+ }
738
+ break
739
+ case 'list':
740
+ m.listResponseMessage = {
741
+ title: message.buttonReply.title,
742
+ description: message.buttonReply.description,
743
+ singleSelectReply: {
744
+ selectedRowId: message.buttonReply.rowId
745
+ },
746
+ listType: WAProto_1.proto.Message.ListResponseMessage.ListType.SINGLE_SELECT
747
+ }
748
+ break
749
+ }
750
+ } else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
751
+ const { videoMessage } = await (0, exports.prepareWAMessageMedia)({ video: message.video }, options)
752
+ m.ptvMessage = videoMessage
753
+ } else if ((0, exports.hasNonNullishProperty)(message, 'product')) {
754
+ const { imageMessage } = await (0, exports.prepareWAMessageMedia)({ image: message.product.productImage }, options)
755
+ m.productMessage = WAProto_1.proto.Message.ProductMessage.create({
756
+ ...message,
757
+ product: {
758
+ ...message.product,
759
+ productImage: imageMessage
760
+ }
761
+ })
762
+ } else if ((0, exports.hasNonNullishProperty)(message, 'listReply')) {
763
+ m.listResponseMessage = { ...message.listReply }
764
+ } else if ((0, exports.hasNonNullishProperty)(message, 'event')) {
765
+ m.eventMessage = {}
766
+ const startTime = Math.floor(message.event.startDate.getTime() / 1000)
767
+ if (message.event.call && options.getCallLink) {
768
+ const token = await options.getCallLink(message.event.call, { startTime })
769
+ m.eventMessage.joinLink =
770
+ (message.event.call === 'audio' ? Defaults_1.CALL_AUDIO_PREFIX : Defaults_1.CALL_VIDEO_PREFIX) + token
771
+ }
772
+ m.messageContextInfo = {
773
+ // encKey
774
+ messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32)
775
+ }
776
+ m.eventMessage.name = message.event.name
777
+ m.eventMessage.description = message.event.description
778
+ m.eventMessage.startTime = startTime
779
+ m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined
780
+ m.eventMessage.isCanceled = message.event.isCancelled ?? false
781
+ m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed
782
+ m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false
783
+ m.eventMessage.location = message.event.location
784
+ } else if ((0, exports.hasNonNullishProperty)(message, 'poll')) {
785
+ ;(_a = message.poll).selectableCount || (_a.selectableCount = 0)
786
+ ;(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false)
787
+ if (!Array.isArray(message.poll.values)) {
788
+ throw new boom_1.Boom('Invalid poll values', { statusCode: 400 })
789
+ }
790
+ if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
791
+ throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
792
+ statusCode: 400
793
+ })
794
+ }
795
+ m.messageContextInfo = {
796
+ messageSecret: message.poll.messageSecret || (0, crypto_1.randomBytes)(32)
797
+ }
798
+ const isQuiz = message.poll.type === 'quiz' || message.poll.pollType === WAProto_1.proto.Message.PollType.QUIZ
799
+ const pollTypeEnum = WAProto_1.proto.Message.PollType
800
+ const pollCreationMessage = {
801
+ name: message.poll.name,
802
+ selectableOptionsCount: message.poll.selectableCount,
803
+ options: message.poll.values.map(optionName => ({ optionName })),
804
+ pollType: isQuiz ? pollTypeEnum.QUIZ : pollTypeEnum.POLL,
805
+ ...(isQuiz && message.poll.correctAnswer ? { correctAnswer: { optionName: message.poll.correctAnswer } } : {}),
806
+ ...(message.poll.endTime
807
+ ? {
808
+ endTime:
809
+ message.poll.endTime instanceof Date
810
+ ? Math.floor(message.poll.endTime.getTime() / 1000)
811
+ : Number(message.poll.endTime)
812
+ }
813
+ : {}),
814
+ ...(message.poll.hideParticipantName !== undefined
815
+ ? { hideParticipantName: !!message.poll.hideParticipantName }
816
+ : {}),
817
+ ...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {}),
818
+ // Feature D: media_poll — allow poll messages to carry a media attachment when the
819
+ // AB prop is enabled and the caller supplies message.poll.mediaAttachment.
820
+ ...((0, messages_media_1.getMediaProp)(options.mediaAbProps, 'media_poll') && message.poll.mediaPoll != null
821
+ ? { mediaPoll: !!message.poll.mediaPoll }
822
+ : {})
823
+ }
824
+ if (message.poll.version === 6 || message.poll.v6) {
825
+ m.pollCreationMessageV6 = pollCreationMessage
826
+ } else if (message.poll.version === 5 || message.poll.v5) {
827
+ m.pollCreationMessageV5 = pollCreationMessage
828
+ } else if (message.poll.toAnnouncementGroup) {
829
+ // poll v2 is for community announcement groups (single select and multiple)
830
+ m.pollCreationMessageV2 = pollCreationMessage
831
+ } else {
832
+ if (message.poll.selectableCount === 1) {
833
+ //poll v3 is for single select polls
834
+ m.pollCreationMessageV3 = pollCreationMessage
835
+ } else {
836
+ // poll for multiple choice polls
837
+ m.pollCreationMessage = pollCreationMessage
838
+ }
839
+ }
840
+ } else if ('inviteAdmin' in message) {
841
+ m.newsletterAdminInviteMessage = {}
842
+ m.newsletterAdminInviteMessage.inviteExpiration = message.inviteAdmin.inviteExpiration
843
+ m.newsletterAdminInviteMessage.caption = message.inviteAdmin.text
844
+ m.newsletterAdminInviteMessage.newsletterJid = message.inviteAdmin.jid
845
+ m.newsletterAdminInviteMessage.newsletterName = message.inviteAdmin.subject
846
+ m.newsletterAdminInviteMessage.jpegThumbnail = message.inviteAdmin.thumbnail
847
+ } else if ('requestPayment' in message || 'requestPaymentMessage' in message) {
848
+ if ('requestPayment' in message && 'requestPaymentMessage' in message) {
849
+ throw new boom_1.Boom('Use either requestPayment or requestPaymentMessage, not both', { statusCode: 400 })
850
+ }
851
+ const requestPayment = message.requestPayment || message.requestPaymentMessage
852
+ const notes = await buildPaymentNoteMessage(requestPayment, options)
853
+ const amountValue = requestPayment.amount ?? requestPayment.amount1000
854
+ const amount1000Raw =
855
+ typeof (amountValue === null || amountValue === void 0 ? void 0 : amountValue.toNumber) === 'function'
856
+ ? amountValue.toNumber()
857
+ : Number(amountValue)
858
+ const amount1000 = Number.isFinite(amount1000Raw) ? Math.round(amount1000Raw) : amount1000Raw
859
+ const currencyCodeIso4217 = requestPayment.currency ?? requestPayment.currencyCodeIso4217
860
+ const requestFrom = requestPayment.from ?? requestPayment.requestFrom ?? options.recipientJid
861
+ const missingFields = []
862
+ if (amountValue === undefined) missingFields.push('amount/amount1000')
863
+ if (currencyCodeIso4217 === undefined) missingFields.push('currency/currencyCodeIso4217')
864
+ if (requestFrom === undefined) missingFields.push('from/requestFrom')
865
+ if (missingFields.length) {
866
+ throw new boom_1.Boom(`Invalid requestPayment fields: missing ${missingFields.join(', ')}`, { statusCode: 400 })
867
+ }
868
+ if (
869
+ typeof amount1000 !== 'number' ||
870
+ !Number.isFinite(amount1000) ||
871
+ !Number.isInteger(amount1000) ||
872
+ amount1000 <= 0
873
+ ) {
874
+ throw new boom_1.Boom('Invalid requestPayment fields: amount/amount1000 must be a positive integer', {
875
+ statusCode: 400
876
+ })
877
+ }
878
+ const bg = requestPayment.background
879
+ m.requestPaymentMessage = WAProto_1.proto.Message.RequestPaymentMessage.fromObject({
880
+ expiryTimestamp: requestPayment.expiry ?? requestPayment.expiryTimestamp,
881
+ amount1000,
882
+ currencyCodeIso4217,
883
+ requestFrom,
884
+ noteMessage: notes,
885
+ ...(bg != null ? { background: bg } : {})
886
+ })
887
+ } else if ('sendPayment' in message || 'sendPaymentMessage' in message) {
888
+ if ('sendPayment' in message && 'sendPaymentMessage' in message) {
889
+ throw new boom_1.Boom('Use either sendPayment or sendPaymentMessage, not both', { statusCode: 400 })
890
+ }
891
+ const sendPayment = message.sendPayment || message.sendPaymentMessage
892
+ const notes = await buildPaymentNoteMessage(sendPayment, options, message.text || '')
893
+ const requestMessageKey = sendPayment.requestMessageKey ?? sendPayment.requestKey ?? sendPayment.request
894
+ if (!requestMessageKey) {
895
+ throw new boom_1.Boom('Invalid sendPayment fields: missing requestMessageKey/requestKey/request', {
896
+ statusCode: 400
897
+ })
898
+ }
899
+ m.sendPaymentMessage = WAProto_1.proto.Message.SendPaymentMessage.fromObject({
900
+ noteMessage: notes,
901
+ requestMessageKey,
902
+ ...(sendPayment.background != null ? { background: sendPayment.background } : {}),
903
+ ...(sendPayment.transactionData != null ? { transactionData: sendPayment.transactionData } : {})
904
+ })
905
+ } else if ('declinePaymentRequest' in message || 'declinePaymentRequestMessage' in message) {
906
+ if ('declinePaymentRequest' in message && 'declinePaymentRequestMessage' in message) {
907
+ throw new boom_1.Boom('Use either declinePaymentRequest or declinePaymentRequestMessage, not both', {
908
+ statusCode: 400
909
+ })
910
+ }
911
+ const declinePayment = message.declinePaymentRequest || message.declinePaymentRequestMessage
912
+ const key = (declinePayment === null || declinePayment === void 0 ? void 0 : declinePayment.key) || declinePayment
913
+ if (!key) {
914
+ throw new boom_1.Boom('Invalid declinePaymentRequest fields: missing key', { statusCode: 400 })
915
+ }
916
+ m.declinePaymentRequestMessage = WAProto_1.proto.Message.DeclinePaymentRequestMessage.fromObject({ key })
917
+ } else if ('cancelPaymentRequest' in message || 'cancelPaymentRequestMessage' in message) {
918
+ if ('cancelPaymentRequest' in message && 'cancelPaymentRequestMessage' in message) {
919
+ throw new boom_1.Boom('Use either cancelPaymentRequest or cancelPaymentRequestMessage, not both', {
920
+ statusCode: 400
921
+ })
922
+ }
923
+ const cancelPayment = message.cancelPaymentRequest || message.cancelPaymentRequestMessage
924
+ const key = (cancelPayment === null || cancelPayment === void 0 ? void 0 : cancelPayment.key) || cancelPayment
925
+ if (!key) {
926
+ throw new boom_1.Boom('Invalid cancelPaymentRequest fields: missing key', { statusCode: 400 })
927
+ }
928
+ m.cancelPaymentRequestMessage = WAProto_1.proto.Message.CancelPaymentRequestMessage.fromObject({ key })
929
+ } else if ('requestPaymentFrom' in message && !!message.requestPaymentFrom) {
930
+ const noteText = message.text || ''
931
+ m.requestPaymentMessage = WAProto_1.proto.Message.RequestPaymentMessage.fromObject({
932
+ requestFrom: message.requestPaymentFrom,
933
+ noteMessage: { extendedTextMessage: { text: noteText } }
934
+ })
935
+ } else if ('invoiceNote' in message) {
936
+ const preparedInvoice = await (0, exports.prepareWAMessageMedia)(message, options)
937
+ const mediaType = Object.keys(preparedInvoice)[0]
938
+ const mediaMsg = preparedInvoice[mediaType] || {}
939
+ m.invoiceMessage = WAProto_1.proto.Message.InvoiceMessage.fromObject({
940
+ note: message.invoiceNote,
941
+ token: message.invoiceToken || '',
942
+ attachmentType: mediaType === 'imageMessage' ? 1 : 0,
943
+ attachmentMimetype: mediaMsg.mimetype,
944
+ attachmentMediaKey: mediaMsg.mediaKey,
945
+ attachmentMediaKeyTimestamp: mediaMsg.mediaKeyTimestamp,
946
+ attachmentFileSha256: mediaMsg.fileSha256,
947
+ attachmentFileEncSha256: mediaMsg.fileEncSha256,
948
+ attachmentDirectPath: mediaMsg.directPath,
949
+ attachmentJpegThumbnail: mediaMsg.jpegThumbnail
950
+ })
951
+ } else if ('orderText' in message) {
952
+ m.orderMessage = WAProto_1.proto.Message.OrderMessage.fromObject({
953
+ message: message.orderText,
954
+ thumbnail: message.thumbnail,
955
+ status: message.orderStatus || 1,
956
+ surface: message.orderSurface || 1
957
+ })
958
+ } else if ('paymentInviteServiceType' in message) {
959
+ m.paymentInviteMessage = {
960
+ serviceType: message.paymentInviteServiceType,
961
+ expiryTimestamp: message.paymentInviteExpiry
962
+ }
963
+ } else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
964
+ m.protocolMessage = {
965
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
966
+ }
967
+ } else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
968
+ m.requestPhoneNumberMessage = {}
969
+ } else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
970
+ m.protocolMessage = {
971
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
972
+ limitSharing: {
973
+ sharingLimited: message.limitSharing === true,
974
+ trigger: 1,
975
+ limitSharingSettingTimestamp: Date.now(),
976
+ initiatedByMe: true
977
+ }
978
+ }
979
+ } else if ('album' in message) {
980
+ const imageMessages = message.album.filter(item => 'image' in item)
981
+ const videoMessages = message.album.filter(item => 'video' in item)
982
+ m.albumMessage = WAProto_1.proto.Message.AlbumMessage.fromObject({
983
+ expectedImageCount: imageMessages.length,
984
+ expectedVideoCount: videoMessages.length
985
+ })
986
+ } else if ('pollResult' in message) {
987
+ if (!Array.isArray(message.pollResult.values)) {
988
+ throw new boom_1.Boom('Invalid pollResult values', { statusCode: 400 })
989
+ }
990
+ const isQuizResult =
991
+ message.pollResult.type === 'quiz' || message.pollResult.pollType === WAProto_1.proto.Message.PollType.QUIZ
992
+ const pollVotes = message.pollResult.values.map(([optionName, optionVoteCount]) => ({
993
+ optionName,
994
+ optionVoteCount
995
+ }))
996
+ const snapshotPayload = {
997
+ name: message.pollResult.name,
998
+ pollVotes,
999
+ pollType: isQuizResult ? WAProto_1.proto.Message.PollType.QUIZ : WAProto_1.proto.Message.PollType.POLL
1000
+ }
1001
+ if (message.pollResult.version === 3 || message.pollResult.v3) {
1002
+ m.pollResultSnapshotMessageV3 = WAProto_1.proto.Message.PollResultSnapshotMessage.fromObject(snapshotPayload)
1003
+ } else {
1004
+ m.pollResultSnapshotMessage = snapshotPayload
1005
+ }
1006
+ } else if ('stickerPack' in message || 'stickerPackMessage' in message) {
1007
+ if ('stickerPack' in message && 'stickerPackMessage' in message) {
1008
+ throw new boom_1.Boom('Cannot specify both stickerPack and stickerPackMessage; use only one property.', {
1009
+ statusCode: 400
1010
+ })
1011
+ }
1012
+ const stickerPackMessage = 'stickerPack' in message ? message.stickerPack : message.stickerPackMessage
1013
+ m.stickerPackMessage = WAProto_1.proto.Message.StickerPackMessage.fromObject(stickerPackMessage)
1014
+ } else if ('listMessage' in message) {
1015
+ const lm = { ...message.listMessage }
1016
+ if (lm.text !== undefined && lm.description === undefined) {
1017
+ lm.description = lm.text
1018
+ delete lm.text
1019
+ }
1020
+ m = { listMessage: lm }
1021
+ } else if ('buttonsMessage' in message) {
1022
+ m = {
1023
+ buttonsMessage: WAProto_1.proto.Message.ButtonsMessage.fromObject(message.buttonsMessage)
1024
+ }
1025
+ } else if ('interactiveMessage' in message) {
1026
+ m = { interactiveMessage: message.interactiveMessage }
1027
+ } else if ('richResponse' in message) {
1028
+ // handled in richResponse block below
1029
+ } else if ('groupStatusMessage' in message) {
1030
+ m = { groupStatusMessage: WAProto_1.proto.Message.GroupStatusMessage.fromObject(message.groupStatusMessage) }
1031
+ } else if (hasCaptionWithoutMedia && !hasCaptionContainer) {
1032
+ m.extendedTextMessage = { text: message.caption }
1033
+ } else if (hasCaptionWithoutMedia && hasCaptionContainer) {
1034
+ m = {}
1035
+ } else if (!hasCaptionWithoutMedia && hasMediaPayload(message)) {
1036
+ m = await (0, exports.prepareWAMessageMedia)(message, options)
1037
+ }
1038
+ if ('buttons' in message && !!message.buttons) {
1039
+ const interactiveMessage = {
1040
+ nativeFlowMessage: WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1041
+ buttons: message.buttons.map(normalizeQuickReplyButton)
1042
+ })
1043
+ }
1044
+ if ('text' in message) {
1045
+ interactiveMessage.body = { text: message.text }
1046
+ } else if ('caption' in message) {
1047
+ interactiveMessage.body = { text: message.caption }
1048
+ interactiveMessage.header = {
1049
+ title: message.title || '',
1050
+ subtitle: message.subtitle,
1051
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1052
+ }
1053
+ Object.assign(interactiveMessage.header, m)
1054
+ }
1055
+ if ('title' in message && !!message.title && !interactiveMessage.header) {
1056
+ interactiveMessage.header = {
1057
+ title: message.title,
1058
+ subtitle: message.subtitle,
1059
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1060
+ }
1061
+ } else if ('title' in message && !!message.title && interactiveMessage.header) {
1062
+ interactiveMessage.header.title = message.title
1063
+ if (message.subtitle !== undefined) {
1064
+ interactiveMessage.header.subtitle = message.subtitle
1065
+ }
1066
+ }
1067
+ if ('footer' in message && !!message.footer) {
1068
+ interactiveMessage.footer = { text: message.footer }
1069
+ }
1070
+ applyContextInfoAndMentions(interactiveMessage, message)
1071
+ m = { interactiveMessage }
1072
+ } else if ('templateButtons' in message && !!message.templateButtons) {
1073
+ const msg = {
1074
+ hydratedButtons: message.templateButtons
1075
+ }
1076
+ if ('text' in message) {
1077
+ msg.hydratedContentText = message.text
1078
+ } else {
1079
+ if ('caption' in message) {
1080
+ msg.hydratedContentText = message.caption
1081
+ }
1082
+ Object.assign(msg, m)
1083
+ }
1084
+ if ('footer' in message && !!message.footer) {
1085
+ msg.hydratedFooterText = message.footer
1086
+ }
1087
+ m = {
1088
+ templateMessage: {
1089
+ fourRowTemplate: msg,
1090
+ hydratedTemplate: msg
1091
+ }
1092
+ }
1093
+ }
1094
+ if ('sections' in message && !!message.sections) {
1095
+ const listMessage = {
1096
+ sections: message.sections,
1097
+ buttonText: message.buttonText,
1098
+ title: message.title,
1099
+ footerText: message.footer,
1100
+ description: message.text,
1101
+ listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
1102
+ }
1103
+ m = { listMessage }
1104
+ } else if ('productList' in message && !!message.productList) {
1105
+ if (
1106
+ !Array.isArray(message.productList) ||
1107
+ message.productList.length === 0 ||
1108
+ !Array.isArray(message.productList[0].products) ||
1109
+ message.productList[0].products.length === 0
1110
+ ) {
1111
+ throw new boom_1.Boom('Invalid productList: must contain at least one section with one product', {
1112
+ statusCode: 400
1113
+ })
1114
+ }
1115
+ m.listMessage = {
1116
+ title: message.title,
1117
+ buttonText: message.buttonText,
1118
+ footerText: message.footer,
1119
+ description: message.text,
1120
+ productListInfo: {
1121
+ productSections: message.productList,
1122
+ headerImage: {
1123
+ productId: message.productList[0].products[0].productId
1124
+ },
1125
+ businessOwnerJid: message.businessOwnerJid
1126
+ },
1127
+ listType: WAProto_1.proto.Message.ListMessage.ListType.PRODUCT_LIST
1128
+ }
1129
+ }
1130
+ if ('interactiveButtons' in message && !!message.interactiveButtons) {
1131
+ const interactiveMessage = {
1132
+ nativeFlowMessage: WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1133
+ buttons: message.interactiveButtons
1134
+ })
1135
+ }
1136
+ if ('text' in message) {
1137
+ interactiveMessage.body = {
1138
+ text: message.text
1139
+ }
1140
+ } else if ('caption' in message) {
1141
+ interactiveMessage.body = {
1142
+ text: message.caption
1143
+ }
1144
+ interactiveMessage.header = {
1145
+ title: message.title,
1146
+ subtitle: message.subtitle,
1147
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1148
+ }
1149
+ Object.assign(interactiveMessage.header, m)
1150
+ }
1151
+ if ('footer' in message && !!message.footer) {
1152
+ interactiveMessage.footer = {
1153
+ text: message.footer
1154
+ }
1155
+ }
1156
+ if ('title' in message && !!message.title) {
1157
+ interactiveMessage.header = {
1158
+ title: message.title,
1159
+ subtitle: message.subtitle,
1160
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1161
+ }
1162
+ Object.assign(interactiveMessage.header, m)
1163
+ }
1164
+ applyContextInfoAndMentions(interactiveMessage, message)
1165
+ m = { interactiveMessage }
1166
+ }
1167
+ if ('shop' in message && !!message.shop) {
1168
+ const interactiveMessage = {
1169
+ shopStorefrontMessage: WAProto_1.proto.Message.InteractiveMessage.ShopMessage.fromObject({
1170
+ surface: (_l = message.shop) === null || _l === void 0 ? void 0 : _l.surface,
1171
+ id: (_m = message.shop) === null || _m === void 0 ? void 0 : _m.id
1172
+ })
1173
+ }
1174
+ if ('text' in message) {
1175
+ interactiveMessage.body = {
1176
+ text: message.text
1177
+ }
1178
+ } else if ('caption' in message) {
1179
+ interactiveMessage.body = {
1180
+ text: message.caption
1181
+ }
1182
+ interactiveMessage.header = {
1183
+ title: message.title,
1184
+ subtitle: message.subtitle,
1185
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1186
+ }
1187
+ Object.assign(interactiveMessage.header, m)
1188
+ }
1189
+ if ('footer' in message && !!message.footer) {
1190
+ interactiveMessage.footer = {
1191
+ text: message.footer
1192
+ }
1193
+ }
1194
+ if ('title' in message && !!message.title) {
1195
+ interactiveMessage.header = {
1196
+ title: message.title,
1197
+ subtitle: message.subtitle,
1198
+ hasMediaAttachment: Boolean(message.hasMediaAttachment)
1199
+ }
1200
+ Object.assign(interactiveMessage.header, m)
1201
+ }
1202
+ applyContextInfoAndMentions(interactiveMessage, message)
1203
+ m = { interactiveMessage }
1204
+ if ('interactiveAsTemplate' in message && message.interactiveAsTemplate !== false) {
1205
+ m = { templateMessage: { interactiveMessageTemplate: interactiveMessage } }
1206
+ }
1207
+ }
1208
+ if ('richResponse' in message) {
1209
+ const {
1210
+ text,
1211
+ code,
1212
+ language = 'javascript',
1213
+ botJid = '259786046210223@bot',
1214
+ table,
1215
+ latex,
1216
+ map,
1217
+ imageUrl,
1218
+ imageUrls,
1219
+ responseId,
1220
+ messageSecret: richSecret
1221
+ } = message.richResponse
1222
+ const sections = []
1223
+ if (text) {
1224
+ sections.push({
1225
+ view_model: {
1226
+ primitive: { text, __typename: 'GenAIMarkdownTextUXPrimitive' },
1227
+ __typename: 'GenAISingleLayoutViewModel'
1228
+ }
1229
+ })
1230
+ }
1231
+ if (code) {
1232
+ sections.push({
1233
+ view_model: {
1234
+ primitive: {
1235
+ language,
1236
+ code_blocks: tokenizeCode(String(code)),
1237
+ __typename: 'GenAICodeUXPrimitive'
1238
+ },
1239
+ __typename: 'GenAISingleLayoutViewModel'
1240
+ }
1241
+ })
1242
+ }
1243
+ if (table && Array.isArray(table.rows)) {
1244
+ sections.push({
1245
+ view_model: {
1246
+ primitive: {
1247
+ rows: table.rows.map(row => ({
1248
+ cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells
1249
+ })),
1250
+ __typename: 'GenAITableUXPrimitive'
1251
+ },
1252
+ __typename: 'GenAISingleLayoutViewModel'
1253
+ }
1254
+ })
1255
+ }
1256
+ if (latex) {
1257
+ const expressions = Array.isArray(latex)
1258
+ ? latex.map(e => (typeof e === 'string' ? { expression: e } : e))
1259
+ : [{ expression: String(latex) }]
1260
+ sections.push({
1261
+ view_model: {
1262
+ primitive: { expressions, __typename: 'GenAILatexUXPrimitive' },
1263
+ __typename: 'GenAISingleLayoutViewModel'
1264
+ }
1265
+ })
1266
+ }
1267
+ if (map) {
1268
+ sections.push({
1269
+ view_model: {
1270
+ primitive: {
1271
+ latitude: map.latitude,
1272
+ longitude: map.longitude,
1273
+ zoom: map.zoom,
1274
+ title: map.title,
1275
+ annotations: map.annotations || [],
1276
+ __typename: 'GenAIMapUXPrimitive'
1277
+ },
1278
+ __typename: 'GenAISingleLayoutViewModel'
1279
+ }
1280
+ })
1281
+ }
1282
+ if (imageUrl) {
1283
+ sections.push({
1284
+ view_model: {
1285
+ primitive: { url: imageUrl, __typename: 'GenAIInlineImageUXPrimitive' },
1286
+ __typename: 'GenAISingleLayoutViewModel'
1287
+ }
1288
+ })
1289
+ }
1290
+ if (imageUrls && Array.isArray(imageUrls) && imageUrls.length > 0) {
1291
+ sections.push({
1292
+ view_model: {
1293
+ primitive: {
1294
+ urls: imageUrls.map(u => (typeof u === 'string' ? { url: u } : u)),
1295
+ __typename: 'GenAIGridImageUXPrimitive'
1296
+ },
1297
+ __typename: 'GenAISingleLayoutViewModel'
1298
+ }
1299
+ })
1300
+ }
1301
+ if (!sections.length && !text) {
1302
+ sections.push({
1303
+ view_model: {
1304
+ primitive: { text: '', __typename: 'GenAIMarkdownTextUXPrimitive' },
1305
+ __typename: 'GenAISingleLayoutViewModel'
1306
+ }
1307
+ })
1308
+ }
1309
+ const unifiedData = {
1310
+ response_id: responseId || (0, crypto_1.randomUUID)(),
1311
+ sections
1312
+ }
1313
+ return WAProto_1.proto.Message.fromObject({
1314
+ messageContextInfo: {
1315
+ deviceListMetadata: {},
1316
+ deviceListMetadataVersion: 2,
1317
+ messageSecret: richSecret || (0, crypto_1.randomBytes)(32)
1318
+ },
1319
+ botForwardedMessage: {
1320
+ message: {
1321
+ richResponseMessage: {
1322
+ submessages: [],
1323
+ messageType: 1,
1324
+ unifiedResponse: { data: Buffer.from(JSON.stringify(unifiedData)) },
1325
+ contextInfo: {
1326
+ forwardingScore: 2,
1327
+ isForwarded: true,
1328
+ forwardedAiBotMessageInfo: { botJid },
1329
+ botMessageSharingInfo: {
1330
+ botEntryPointOrigin: 1,
1331
+ forwardScore: 2
1332
+ }
1333
+ }
1334
+ }
1335
+ }
1336
+ }
1337
+ })
1338
+ }
1339
+ if ('statusNotification' in message || 'statusNotificationMessage' in message) {
1340
+ const notifData = 'statusNotification' in message ? message.statusNotification : message.statusNotificationMessage
1341
+ m = { statusNotificationMessage: WAProto_1.proto.Message.StatusNotificationMessage.fromObject(notifData) }
1342
+ } else if ('statusQuestionAnswer' in message || 'statusQuestionAnswerMessage' in message) {
1343
+ const qaData =
1344
+ 'statusQuestionAnswer' in message ? message.statusQuestionAnswer : message.statusQuestionAnswerMessage
1345
+ m = { statusQuestionAnswerMessage: WAProto_1.proto.Message.StatusQuestionAnswerMessage.fromObject(qaData) }
1346
+ } else if ('questionResponse' in message || 'questionResponseMessage' in message) {
1347
+ const qrData = 'questionResponse' in message ? message.questionResponse : message.questionResponseMessage
1348
+ m = { questionResponseMessage: WAProto_1.proto.Message.QuestionResponseMessage.fromObject(qrData) }
1349
+ } else if ('statusQuoted' in message || 'statusQuotedMessage' in message) {
1350
+ const sqData = 'statusQuoted' in message ? message.statusQuoted : message.statusQuotedMessage
1351
+ m = { statusQuotedMessage: WAProto_1.proto.Message.StatusQuotedMessage.fromObject(sqData) }
1352
+ } else if ('statusStickerInteraction' in message || 'statusStickerInteractionMessage' in message) {
1353
+ const ssiData =
1354
+ 'statusStickerInteraction' in message ? message.statusStickerInteraction : message.statusStickerInteractionMessage
1355
+ m = { statusStickerInteractionMessage: WAProto_1.proto.Message.StatusStickerInteractionMessage.fromObject(ssiData) }
1356
+ } else if ('newsletterFollowerInvite' in message || 'newsletterFollowerInviteMessageV2' in message) {
1357
+ const nfiData =
1358
+ 'newsletterFollowerInvite' in message
1359
+ ? message.newsletterFollowerInvite
1360
+ : message.newsletterFollowerInviteMessageV2
1361
+ m = {
1362
+ newsletterFollowerInviteMessageV2: WAProto_1.proto.Message.NewsletterFollowerInviteMessage.fromObject(nfiData)
1363
+ }
1364
+ } else if ('messageHistoryNotice' in message) {
1365
+ m = { messageHistoryNotice: WAProto_1.proto.Message.MessageHistoryNotice.fromObject(message.messageHistoryNotice) }
1366
+ } else if ('scheduledCall' in message || 'scheduledCallCreationMessage' in message) {
1367
+ if ('scheduledCall' in message && 'scheduledCallCreationMessage' in message) {
1368
+ throw new boom_1.Boom('Use either scheduledCall or scheduledCallCreationMessage, not both', { statusCode: 400 })
1369
+ }
1370
+ const sc = message.scheduledCall || message.scheduledCallCreationMessage
1371
+ const scheduledTs =
1372
+ sc.scheduledAt instanceof Date
1373
+ ? sc.scheduledAt.getTime()
1374
+ : sc.scheduledAt
1375
+ ? Number(sc.scheduledAt)
1376
+ : sc.scheduledTimestampMs
1377
+ ? Number(sc.scheduledTimestampMs)
1378
+ : Date.now() + 3600000
1379
+ const callTypeEnum = WAProto_1.proto.Message.ScheduledCallCreationMessage.CallType
1380
+ let callType = callTypeEnum.UNKNOWN
1381
+ if (sc.isVideo || sc.callType === 'video' || sc.callType === callTypeEnum.VIDEO) {
1382
+ callType = callTypeEnum.VIDEO
1383
+ } else if (sc.callType === 'voice' || sc.callType === callTypeEnum.VOICE || sc.callType === undefined) {
1384
+ callType = callTypeEnum.VOICE
1385
+ } else {
1386
+ callType = sc.callType ?? callTypeEnum.VOICE
1387
+ }
1388
+ m.scheduledCallCreationMessage = WAProto_1.proto.Message.ScheduledCallCreationMessage.fromObject({
1389
+ scheduledTimestampMs: scheduledTs,
1390
+ callType,
1391
+ title: sc.title || ''
1392
+ })
1393
+ } else if ('editScheduledCall' in message || 'scheduledCallEditMessage' in message) {
1394
+ if ('editScheduledCall' in message && 'scheduledCallEditMessage' in message) {
1395
+ throw new boom_1.Boom('Use either editScheduledCall or scheduledCallEditMessage, not both', { statusCode: 400 })
1396
+ }
1397
+ const esc = message.editScheduledCall || message.scheduledCallEditMessage
1398
+ const key = esc.key || esc
1399
+ if (!key || typeof key !== 'object' || !key.id) {
1400
+ throw new boom_1.Boom('editScheduledCall requires a valid message key with id', { statusCode: 400 })
1401
+ }
1402
+ m.scheduledCallEditMessage = WAProto_1.proto.Message.ScheduledCallEditMessage.fromObject({
1403
+ key,
1404
+ editType: WAProto_1.proto.Message.ScheduledCallEditMessage.EditType.CANCEL
1405
+ })
1406
+ } else if ('eventInvite' in message || 'eventInviteMessage' in message) {
1407
+ if ('eventInvite' in message && 'eventInviteMessage' in message) {
1408
+ throw new boom_1.Boom('Use either eventInvite or eventInviteMessage, not both', { statusCode: 400 })
1409
+ }
1410
+ const ei = message.eventInvite || message.eventInviteMessage
1411
+ const startTime =
1412
+ ei.startDate instanceof Date
1413
+ ? Math.floor(ei.startDate.getTime() / 1000)
1414
+ : ei.startTime
1415
+ ? Number(ei.startTime)
1416
+ : undefined
1417
+ const endTime =
1418
+ ei.endDate instanceof Date ? Math.floor(ei.endDate.getTime() / 1000) : ei.endTime ? Number(ei.endTime) : undefined
1419
+ if (!ei.eventId && !ei.id) {
1420
+ throw new boom_1.Boom('eventInvite requires an eventId', { statusCode: 400 })
1421
+ }
1422
+ m.eventInviteMessage = WAProto_1.proto.Message.EventInviteMessage.fromObject({
1423
+ eventId: ei.eventId || ei.id,
1424
+ eventTitle: ei.title || ei.eventTitle || '',
1425
+ caption: ei.text || ei.caption || '',
1426
+ startTime,
1427
+ endTime,
1428
+ isCanceled: ei.isCancelled ?? ei.isCanceled ?? false,
1429
+ jpegThumbnail: ei.thumbnail || ei.jpegThumbnail,
1430
+ contextInfo: ei.contextInfo
1431
+ })
1432
+ } else if ('comment' in message || 'commentMessage' in message) {
1433
+ if ('comment' in message && 'commentMessage' in message) {
1434
+ throw new boom_1.Boom('Use either comment or commentMessage, not both', { statusCode: 400 })
1435
+ }
1436
+ const cm = message.comment || message.commentMessage
1437
+ if (!cm.targetMessageKey && !cm.key) {
1438
+ throw new boom_1.Boom('comment requires a targetMessageKey', { statusCode: 400 })
1439
+ }
1440
+ m.commentMessage = WAProto_1.proto.Message.CommentMessage.fromObject({
1441
+ message: cm.message || cm.replyMessage,
1442
+ targetMessageKey: cm.targetMessageKey || cm.key
1443
+ })
1444
+ } else if ('splitPayment' in message || 'splitPaymentMessage' in message) {
1445
+ if ('splitPayment' in message && 'splitPaymentMessage' in message) {
1446
+ throw new boom_1.Boom('Use either splitPayment or splitPaymentMessage, not both', { statusCode: 400 })
1447
+ }
1448
+ const sp = message.splitPayment || message.splitPaymentMessage
1449
+ if (!sp.totalAmount || !sp.participants?.length) {
1450
+ throw new boom_1.Boom('splitPayment requires totalAmount and at least one participant', { statusCode: 400 })
1451
+ }
1452
+ m.splitPaymentMessage = WAProto_1.proto.Message.SplitPaymentMessage.fromObject({
1453
+ splitId: sp.splitId || (0, crypto_1.randomUUID)(),
1454
+ totalAmount: sp.totalAmount,
1455
+ description: sp.description || '',
1456
+ requesterJid: sp.requesterJid || options.userJid,
1457
+ participants: sp.participants.map(p => ({
1458
+ jid: p.jid,
1459
+ amount: p.amount,
1460
+ status: p.status ?? WAProto_1.proto.Message.SplitPaymentParticipant.SplitPaymentStatus.PENDING
1461
+ })),
1462
+ createdAtMs: sp.createdAtMs || Date.now(),
1463
+ contextInfo: sp.contextInfo
1464
+ })
1465
+ } else if ('p2pPaymentReminder' in message || 'p2PPaymentReminderNotification' in message) {
1466
+ if ('p2pPaymentReminder' in message && 'p2PPaymentReminderNotification' in message) {
1467
+ throw new boom_1.Boom('Use either p2pPaymentReminder or p2PPaymentReminderNotification, not both', {
1468
+ statusCode: 400
1469
+ })
1470
+ }
1471
+ const pr = message.p2pPaymentReminder || message.p2PPaymentReminderNotification
1472
+ const freqEnum = WAProto_1.proto.Message.P2PPaymentReminderNotification.ReminderFrequency
1473
+ const stateEnum = WAProto_1.proto.Message.P2PPaymentReminderNotification.ReminderState
1474
+ const freqMap = {
1475
+ weekly: freqEnum.WEEKLY,
1476
+ biweekly: freqEnum.BIWEEKLY,
1477
+ monthly: freqEnum.MONTHLY,
1478
+ custom: freqEnum.CUSTOM
1479
+ }
1480
+ const stateMap = {
1481
+ active: stateEnum.ACTIVE,
1482
+ paused: stateEnum.PAUSED,
1483
+ stopped: stateEnum.STOPPED,
1484
+ expired: stateEnum.EXPIRED,
1485
+ cancelled: stateEnum.CANCELLED
1486
+ }
1487
+ m.p2PPaymentReminderNotification = WAProto_1.proto.Message.P2PPaymentReminderNotification.fromObject({
1488
+ reminderId: pr.reminderId || (0, crypto_1.randomUUID)(),
1489
+ amount: pr.amount,
1490
+ frequency:
1491
+ typeof pr.frequency === 'string'
1492
+ ? (freqMap[pr.frequency.toLowerCase()] ?? freqEnum.UNKNOWN_FREQUENCY)
1493
+ : (pr.frequency ?? freqEnum.UNKNOWN_FREQUENCY),
1494
+ nextReminderTimestamp: pr.nextReminderTimestamp,
1495
+ expiryTimestamp: pr.expiryTimestamp,
1496
+ state:
1497
+ typeof pr.state === 'string'
1498
+ ? (stateMap[pr.state.toLowerCase()] ?? stateEnum.ACTIVE)
1499
+ : (pr.state ?? stateEnum.ACTIVE),
1500
+ description: pr.description || '',
1501
+ creatorJid: pr.creatorJid || options.userJid,
1502
+ receiverJid: pr.receiverJid,
1503
+ upiId: pr.upiId,
1504
+ createdTimestamp: pr.createdTimestamp || Date.now()
1505
+ })
1506
+ } else if ('conditionalReveal' in message || 'conditionalRevealMessage' in message) {
1507
+ if ('conditionalReveal' in message && 'conditionalRevealMessage' in message) {
1508
+ throw new boom_1.Boom('Use either conditionalReveal or conditionalRevealMessage, not both', { statusCode: 400 })
1509
+ }
1510
+ const cr = message.conditionalReveal || message.conditionalRevealMessage
1511
+ m.conditionalRevealMessage = WAProto_1.proto.Message.ConditionalRevealMessage.fromObject({
1512
+ conditionalRevealMessageType:
1513
+ WAProto_1.proto.Message.ConditionalRevealMessage.ConditionalRevealMessageType.SCHEDULED_MESSAGE,
1514
+ revealKeyId: cr.revealKeyId || (0, crypto_1.randomUUID)()
1515
+ })
1516
+ m.messageContextInfo = {
1517
+ messageSecret: cr.messageSecret || (0, crypto_1.randomBytes)(32)
1518
+ }
1519
+ } else if ('callLog' in message || 'callLogMessage' in message) {
1520
+ if ('callLog' in message && 'callLogMessage' in message) {
1521
+ throw new boom_1.Boom('Use either callLog or callLogMessage, not both', { statusCode: 400 })
1522
+ }
1523
+ const cl = message.callLog || message.callLogMessage
1524
+ const outcomeEnum = WAProto_1.proto.Message.CallLogMessage.CallOutcome
1525
+ const callTypeEnum = WAProto_1.proto.Message.CallLogMessage.CallType
1526
+ const outcomeMap = {
1527
+ connected: outcomeEnum.CONNECTED,
1528
+ missed: outcomeEnum.MISSED,
1529
+ failed: outcomeEnum.FAILED,
1530
+ rejected: outcomeEnum.REJECTED,
1531
+ accepted_elsewhere: outcomeEnum.ACCEPTED_ELSEWHERE,
1532
+ ongoing: outcomeEnum.ONGOING,
1533
+ silenced_by_dnd: outcomeEnum.SILENCED_BY_DND,
1534
+ silenced_unknown_caller: outcomeEnum.SILENCED_UNKNOWN_CALLER
1535
+ }
1536
+ const callTypeMap = {
1537
+ regular: callTypeEnum.REGULAR,
1538
+ scheduled_call: callTypeEnum.SCHEDULED_CALL,
1539
+ voice_chat: callTypeEnum.VOICE_CHAT
1540
+ }
1541
+ m.callLogMesssage = WAProto_1.proto.Message.CallLogMessage.fromObject({
1542
+ isVideo: cl.isVideo ?? false,
1543
+ callOutcome:
1544
+ typeof cl.outcome === 'string'
1545
+ ? (outcomeMap[cl.outcome.toLowerCase()] ?? outcomeEnum.CONNECTED)
1546
+ : (cl.outcome ?? outcomeEnum.CONNECTED),
1547
+ durationSecs: cl.durationSecs || 0,
1548
+ callType:
1549
+ typeof cl.callType === 'string'
1550
+ ? (callTypeMap[cl.callType.toLowerCase()] ?? callTypeEnum.REGULAR)
1551
+ : (cl.callType ?? callTypeEnum.REGULAR),
1552
+ participants: (cl.participants || []).map(p => ({
1553
+ jid: p.jid,
1554
+ callOutcome:
1555
+ typeof p.outcome === 'string'
1556
+ ? (outcomeMap[p.outcome.toLowerCase()] ?? outcomeEnum.CONNECTED)
1557
+ : (p.outcome ?? p.callOutcome ?? outcomeEnum.CONNECTED)
1558
+ }))
1559
+ })
1560
+ } else if ('statusMention' in message || 'statusMentionMsg' in message) {
1561
+ const sm = message.statusMention || message.statusMentionMsg
1562
+ if (!sm || typeof sm !== 'object') {
1563
+ throw new boom_1.Boom('statusMention must be an object with a message property', { statusCode: 400 })
1564
+ }
1565
+ m.statusMentionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1566
+ message: sm.message || sm
1567
+ })
1568
+ } else if ('question' in message || 'questionMessage' in message) {
1569
+ const q = message.question || message.questionMessage
1570
+ m.questionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1571
+ message: q.message || (typeof q === 'object' && 'conversation' in q ? q : { conversation: String(q) })
1572
+ })
1573
+ } else if ('questionReply' in message || 'questionReplyMessage' in message) {
1574
+ const qr = message.questionReply || message.questionReplyMessage
1575
+ m.questionReplyMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1576
+ message: qr.message || (typeof qr === 'object' && 'conversation' in qr ? qr : { conversation: String(qr) })
1577
+ })
1578
+ } else if ('statusAddYours' in message) {
1579
+ const say = message.statusAddYours
1580
+ m.statusAddYours = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1581
+ message: say.message || say
1582
+ })
1583
+ } else if ('eventCoverImage' in message) {
1584
+ const eci = message.eventCoverImage
1585
+ m.eventCoverImage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1586
+ message: eci.message || eci
1587
+ })
1588
+ } else if ('spoilerMessage' in message || 'spoiler' in message) {
1589
+ const sp = message.spoilerMessage || message.spoiler
1590
+ m.spoilerMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1591
+ message: sp.message || sp
1592
+ })
1593
+ } else if ('lottieStickerMessage' in message || 'lottieSticker' in message) {
1594
+ const ls = message.lottieStickerMessage || message.lottieSticker
1595
+ m.lottieStickerMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1596
+ message: ls.message || ls
1597
+ })
1598
+ } else if ('groupStatusV2' in message || 'groupStatusMessageV2' in message) {
1599
+ const gsv2 = message.groupStatusV2 || message.groupStatusMessageV2
1600
+ m.groupStatusMessageV2 = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1601
+ message: gsv2.message || gsv2
1602
+ })
1603
+ } else if ('newsletterAdminProfile' in message || 'newsletterAdminProfileMessage' in message) {
1604
+ const nap = message.newsletterAdminProfile || message.newsletterAdminProfileMessage
1605
+ m.newsletterAdminProfileMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1606
+ message: nap.message || nap
1607
+ })
1608
+ } else if ('newsletterAdminProfileV2' in message || 'newsletterAdminProfileMessageV2' in message) {
1609
+ const nap2 = message.newsletterAdminProfileV2 || message.newsletterAdminProfileMessageV2
1610
+ m.newsletterAdminProfileMessageV2 = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1611
+ message: nap2.message || nap2
1612
+ })
1613
+ } else if ('botTask' in message || 'botTaskMessage' in message) {
1614
+ const bt = message.botTask || message.botTaskMessage
1615
+ m.botTaskMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1616
+ message: bt.message || bt
1617
+ })
1618
+ } else if ('botInvoke' in message || 'botInvokeMessage' in message) {
1619
+ const bi = message.botInvoke || message.botInvokeMessage
1620
+ m.botInvokeMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1621
+ message: bi.message || bi
1622
+ })
1623
+ } else if ('associatedChild' in message || 'associatedChildMessage' in message) {
1624
+ const ac = message.associatedChild || message.associatedChildMessage
1625
+ m.associatedChildMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1626
+ message: ac.message || ac
1627
+ })
1628
+ } else if ('groupStatusMention' in message || 'groupStatusMentionMessage' in message) {
1629
+ const gsm = message.groupStatusMention || message.groupStatusMentionMessage
1630
+ m.groupStatusMentionMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1631
+ message: gsm.message || gsm
1632
+ })
1633
+ } else if ('pollCreationOptionImage' in message || 'pollCreationOptionImageMessage' in message) {
1634
+ const pcoi = message.pollCreationOptionImage || message.pollCreationOptionImageMessage
1635
+ m.pollCreationOptionImageMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1636
+ message: pcoi.message || pcoi
1637
+ })
1638
+ } else if ('newsletterAdminProfileStatus' in message || 'newsletterAdminProfileStatusMessage' in message) {
1639
+ const naps = message.newsletterAdminProfileStatus || message.newsletterAdminProfileStatusMessage
1640
+ m.newsletterAdminProfileStatusMessage = WAProto_1.proto.Message.FutureProofMessage.fromObject({
1641
+ message: naps.message || naps
1642
+ })
1643
+ } else if ('placeholder' in message || 'placeholderMessage' in message) {
1644
+ const ph = message.placeholder || message.placeholderMessage
1645
+ const phTypeEnum = WAProto_1.proto.Message.PlaceholderMessage.PlaceholderType
1646
+ const phTypeMap = {
1647
+ e2e_reenable: phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG,
1648
+ linked_devices: phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_LINKED_DEVICES_MESSAGE
1649
+ }
1650
+ m.placeholderMessage = WAProto_1.proto.Message.PlaceholderMessage.fromObject({
1651
+ type:
1652
+ typeof ph.type === 'string'
1653
+ ? (phTypeMap[ph.type.toLowerCase()] ?? phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG)
1654
+ : (ph.type ?? phTypeEnum.PLACEHOLDER_MESSAGE_TYPE_E2E_REENABLE_MSG)
1655
+ })
1656
+ } else if ('tapLink' in message || 'tapLinkMessage' in message) {
1657
+ const tl = message.tapLink || message.tapLinkMessage
1658
+ const inner = {
1659
+ extendedTextMessage: {
1660
+ text: tl.text || tl.title || '',
1661
+ matchedText: tl.url || tl.tapUrl,
1662
+ title: tl.title || tl.text || '',
1663
+ contextInfo: {
1664
+ actionLink: {
1665
+ url: tl.url || tl.tapUrl || '',
1666
+ buttonTitle: tl.buttonTitle || 'Open'
1667
+ }
1668
+ }
1669
+ }
1670
+ }
1671
+ Object.assign(m, inner)
1672
+ } else if ('citation' in message) {
1673
+ const cit = message.citation
1674
+ m.extendedTextMessage = {
1675
+ text: cit.text || cit.title || '',
1676
+ title: cit.title || '',
1677
+ description: cit.subtitle || cit.description || '',
1678
+ ...(cit.imageUrl ? { jpegThumbnail: undefined, previewType: 0 } : {})
1679
+ }
1680
+ } else if ('embeddedMusic' in message) {
1681
+ const em = message.embeddedMusic
1682
+ m.extendedTextMessage = {
1683
+ text: em.text || `🎵 ${em.title || ''} — ${em.author || ''}`,
1684
+ title: em.title || '',
1685
+ description: em.author || em.artistAttribution || '',
1686
+ previewType: 0
1687
+ }
1688
+ }
1689
+ if ('keepInChat' in message || 'keepInChatMessage' in message) {
1690
+ const kic = message.keepInChat || message.keepInChatMessage
1691
+ const keepTypeEnum = WAProto_1.proto.KeepType
1692
+ const keepTypeMap = { keep: keepTypeEnum.KEEP_FOR_ALL, undo: keepTypeEnum.UNDO_KEEP_FOR_ALL }
1693
+ m.keepInChatMessage = WAProto_1.proto.Message.KeepInChatMessage.fromObject({
1694
+ key: kic.key || kic,
1695
+ keepType:
1696
+ typeof kic.keepType === 'string'
1697
+ ? (keepTypeMap[kic.keepType.toLowerCase()] ?? keepTypeEnum.KEEP_FOR_ALL)
1698
+ : (kic.keepType ?? keepTypeEnum.KEEP_FOR_ALL),
1699
+ timestampMs: kic.timestampMs || Date.now()
1700
+ })
1701
+ }
1702
+ if ('botFeedback' in message || 'botFeedbackMessage' in message) {
1703
+ const bf = message.botFeedback || message.botFeedbackMessage
1704
+ const kindEnum = WAProto_1.proto.BotFeedbackMessage.BotFeedbackKind
1705
+ const kindMap = {
1706
+ positive: kindEnum.BOT_FEEDBACK_POSITIVE,
1707
+ negative: kindEnum.BOT_FEEDBACK_NEGATIVE,
1708
+ negative_generic: kindEnum.BOT_FEEDBACK_NEGATIVE_GENERIC,
1709
+ negative_helpful: kindEnum.BOT_FEEDBACK_NEGATIVE_HELPFUL,
1710
+ negative_interesting: kindEnum.BOT_FEEDBACK_NEGATIVE_INTERESTING,
1711
+ negative_accurate: kindEnum.BOT_FEEDBACK_NEGATIVE_ACCURATE,
1712
+ negative_safe: kindEnum.BOT_FEEDBACK_NEGATIVE_SAFE,
1713
+ negative_other: kindEnum.BOT_FEEDBACK_NEGATIVE_OTHER
1714
+ }
1715
+ m.protocolMessage = {
1716
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.BOT_FEEDBACK_MESSAGE,
1717
+ key: bf.key,
1718
+ botFeedbackMessage: WAProto_1.proto.BotFeedbackMessage.fromObject({
1719
+ feedbackId: bf.feedbackId || bf.key?.id || '',
1720
+ kind:
1721
+ typeof bf.kind === 'string'
1722
+ ? (kindMap[bf.kind.toLowerCase()] ?? kindEnum.BOT_FEEDBACK_POSITIVE)
1723
+ : (bf.kind ?? kindEnum.BOT_FEEDBACK_POSITIVE)
1724
+ })
1725
+ }
1726
+ }
1727
+ if ('pollAddOption' in message || 'pollAddOptionMessage' in message) {
1728
+ const pao = message.pollAddOption || message.pollAddOptionMessage
1729
+ if (!pao.pollCreationMessageKey && !pao.key) {
1730
+ throw new boom_1.Boom('pollAddOption requires a pollCreationMessageKey', { statusCode: 400 })
1731
+ }
1732
+ m.pollAddOptionMessage = WAProto_1.proto.Message.PollAddOptionMessage.fromObject({
1733
+ pollCreationMessageKey: pao.pollCreationMessageKey || pao.key,
1734
+ addOption: { optionName: pao.optionName || pao.option }
1735
+ })
1736
+ }
1737
+ if ('chatTheme' in message || 'chatThemeMessage' in message) {
1738
+ const ct = message.chatTheme || message.chatThemeMessage
1739
+ const themeSetting = {
1740
+ settingTimestampMs: Date.now(),
1741
+ clearTheme: ct.clear ?? false,
1742
+ colorSchemeId: ct.colorSchemeId
1743
+ }
1744
+ if (ct.solidColor) {
1745
+ const sc = typeof ct.solidColor === 'object' ? ct.solidColor : {}
1746
+ themeSetting.solidColor = WAProto_1.proto.Message.ChatSolidColorWallpaper.fromObject({
1747
+ colorLight: sc.colorLight || sc.color || '#FFFFFF',
1748
+ colorDark: sc.colorDark || sc.color || '#000000',
1749
+ isDoodleEnabled: sc.isDoodleEnabled ?? false
1750
+ })
1751
+ } else if (ct.stockImage) {
1752
+ const si = typeof ct.stockImage === 'object' ? ct.stockImage : {}
1753
+ themeSetting.stockImage = WAProto_1.proto.Message.ChatStockImageWallpaper.fromObject({
1754
+ stockImageId: si.id || si.stockImageId || String(ct.stockImage),
1755
+ dimLevel: (si.dimLevel ?? si.opacity != null) ? (si.opacity ?? 100) / 100 : 0
1756
+ })
1757
+ } else if (ct.defaultWallpaper) {
1758
+ themeSetting.defaultWallpaper = WAProto_1.proto.Message.ChatDefaultWallpaper.fromObject({
1759
+ isDoodleEnabled: ct.defaultWallpaper.isDoodleEnabled ?? false
1760
+ })
1761
+ } else if (ct.customImage) {
1762
+ themeSetting.customImage = WAProto_1.proto.Message.ChatCustomImageWallpaper.fromObject({
1763
+ directPath: ct.customImage.directPath,
1764
+ mediaKey: ct.customImage.mediaKey,
1765
+ fileEncSha256: ct.customImage.fileEncSha256,
1766
+ fileSha256: ct.customImage.fileSha256,
1767
+ dimLevel: ct.customImage.dimLevel ?? 0
1768
+ })
1769
+ }
1770
+ m.protocolMessage = {
1771
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.CHAT_THEME_SETTING,
1772
+ chatThemeSetting: WAProto_1.proto.Message.ChatThemeSetting.fromObject(themeSetting)
1773
+ }
1774
+ }
1775
+ if ('stopGeneration' in message) {
1776
+ const sg = message.stopGeneration
1777
+ m.protocolMessage = {
1778
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.STOP_GENERATION_MESSAGE,
1779
+ key: sg.key || sg
1780
+ }
1781
+ }
1782
+ if ('unscheduleMessage' in message) {
1783
+ const us = message.unscheduleMessage
1784
+ m.protocolMessage = {
1785
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.MESSAGE_UNSCHEDULE,
1786
+ key: us.key || us
1787
+ }
1788
+ }
1789
+ if ('bcall' in message || 'bcallMessage' in message) {
1790
+ const bc = message.bcall || message.bcallMessage
1791
+ const mediaTypeEnum = WAProto_1.proto.Message.BCallMessage.MediaType
1792
+ const mediaTypeMap = { audio: mediaTypeEnum.AUDIO, video: mediaTypeEnum.VIDEO }
1793
+ m.bcallMessage = WAProto_1.proto.Message.BCallMessage.fromObject({
1794
+ sessionId: bc.sessionId || (0, crypto_1.randomUUID)(),
1795
+ mediaType:
1796
+ typeof bc.mediaType === 'string'
1797
+ ? (mediaTypeMap[bc.mediaType.toLowerCase()] ?? mediaTypeEnum.AUDIO)
1798
+ : (bc.mediaType ?? mediaTypeEnum.AUDIO),
1799
+ masterKey: bc.masterKey || (0, crypto_1.randomBytes)(32),
1800
+ caption: bc.caption || ''
1801
+ })
1802
+ }
1803
+ if ('liveLocationUpdate' in message) {
1804
+ const llu = message.liveLocationUpdate
1805
+ m.liveLocationMessage = WAProto_1.proto.Message.LiveLocationMessage.fromObject({
1806
+ degreesLatitude: llu.latitude,
1807
+ degreesLongitude: llu.longitude,
1808
+ accuracyInMeters: llu.accuracy,
1809
+ speedInMps: llu.speed,
1810
+ degreesClockwiseFromMagneticNorth: llu.heading,
1811
+ sequenceNumber: llu.sequence || 1,
1812
+ timeOffset: llu.timeOffset || 0,
1813
+ jpegThumbnail: llu.thumbnail
1814
+ })
1815
+ }
1816
+ if ('stopLiveLocation' in message) {
1817
+ const sll = message.stopLiveLocation
1818
+ m.protocolMessage = {
1819
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.REVOKE,
1820
+ key: sll.key || sll
1821
+ }
1822
+ }
1823
+ if ('carousel' in message || 'carouselMessage' in message) {
1824
+ const c = message.carousel || message.carouselMessage
1825
+ if (!Array.isArray(c.cards) || !c.cards.length) {
1826
+ throw new boom_1.Boom('carousel requires at least one card', { statusCode: 400 })
1827
+ }
1828
+ const cardTypeEnum = WAProto_1.proto.Message.InteractiveMessage.CarouselMessage.CarouselCardType
1829
+ const cardTypeMap = {
1830
+ horizontal: cardTypeEnum.HSCROLL_CARDS,
1831
+ album: cardTypeEnum.ALBUM_IMAGE,
1832
+ hscroll: cardTypeEnum.HSCROLL_CARDS
1833
+ }
1834
+ m.interactiveMessage = {
1835
+ carouselMessage: WAProto_1.proto.Message.InteractiveMessage.CarouselMessage.fromObject({
1836
+ cards: c.cards.map(card => card.interactiveMessage || card),
1837
+ messageVersion: c.messageVersion || 1,
1838
+ carouselCardType:
1839
+ typeof c.cardType === 'string'
1840
+ ? (cardTypeMap[c.cardType.toLowerCase()] ?? cardTypeEnum.HSCROLL_CARDS)
1841
+ : (c.carouselCardType ?? cardTypeEnum.HSCROLL_CARDS)
1842
+ })
1843
+ }
1844
+ }
1845
+ if ('aiMediaCollection' in message || 'aiMediaCollectionMessage' in message) {
1846
+ const amc = message.aiMediaCollection || message.aiMediaCollectionMessage
1847
+ m.protocolMessage = {
1848
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.AI_MEDIA_COLLECTION_MESSAGE,
1849
+ aiMediaCollectionMessage: WAProto_1.proto.AIMediaCollectionMessage.fromObject({
1850
+ collectionId: amc.collectionId || (0, crypto_1.randomUUID)(),
1851
+ expectedMediaCount: amc.expectedMediaCount || amc.count || 1,
1852
+ hasGlobalCaption: amc.hasGlobalCaption ?? false
1853
+ })
1854
+ }
1855
+ }
1856
+ if ('botCapabilities' in message && Array.isArray(message.botCapabilities) && message.botCapabilities.length) {
1857
+ const capEnum = WAProto_1.proto.BotCapabilityMetadata.BotCapabilityType
1858
+ const capStrMap = Object.fromEntries(Object.entries(capEnum).map(([k, v]) => [k.toLowerCase(), v]))
1859
+ const caps = message.botCapabilities
1860
+ .map(c => (typeof c === 'string' ? (capStrMap[c.toLowerCase()] ?? capEnum.UNKNOWN) : c))
1861
+ .filter(c => typeof c === 'number' && c !== capEnum.UNKNOWN)
1862
+ m.messageContextInfo = m.messageContextInfo || {}
1863
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
1864
+ capabilityMetadata: { capabilities: caps }
1865
+ })
1866
+ }
1867
+ if ('botThreadInfo' in message && message.botThreadInfo) {
1868
+ const bt = message.botThreadInfo
1869
+ const threadTypeEnum = WAProto_1.proto.AIThreadInfo.AIThreadClientInfo.AIThreadType
1870
+ const threadTypeMap = Object.fromEntries(Object.entries(threadTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
1871
+ const existing = m.messageContextInfo?.botMetadata
1872
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
1873
+ : {}
1874
+ m.messageContextInfo = m.messageContextInfo || {}
1875
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
1876
+ ...existing,
1877
+ botThreadInfo: {
1878
+ clientInfo: {
1879
+ type:
1880
+ typeof bt.type === 'string'
1881
+ ? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
1882
+ : (bt.type ?? threadTypeEnum.UNKNOWN),
1883
+ sourceChatJid: bt.sourceChatJid || ''
1884
+ }
1885
+ }
1886
+ })
1887
+ }
1888
+ if ('messageAssociation' in message && message.messageAssociation) {
1889
+ const ma = message.messageAssociation
1890
+ const assocTypeEnum = WAProto_1.proto.MessageAssociation.AssociationType
1891
+ const assocTypeMap = Object.fromEntries(Object.entries(assocTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
1892
+ m.messageContextInfo = m.messageContextInfo || {}
1893
+ m.messageContextInfo.messageAssociation = WAProto_1.proto.MessageAssociation.fromObject({
1894
+ associationType:
1895
+ typeof ma.type === 'string'
1896
+ ? (assocTypeMap[ma.type.toLowerCase()] ?? assocTypeEnum.UNKNOWN)
1897
+ : (ma.associationType ?? ma.type ?? assocTypeEnum.UNKNOWN),
1898
+ parentMessageKey: ma.parentMessageKey || ma.parentKey,
1899
+ messageIndex: ma.messageIndex || 0
1900
+ })
1901
+ }
1902
+ if ('threadId' in message && message.threadId) {
1903
+ const tid = message.threadId
1904
+ const threadTypeEnum = WAProto_1.proto.ThreadID.ThreadType
1905
+ m.messageContextInfo = m.messageContextInfo || {}
1906
+ m.messageContextInfo.threadId = [
1907
+ WAProto_1.proto.ThreadID.fromObject({
1908
+ threadType:
1909
+ tid.type === 'ai'
1910
+ ? threadTypeEnum.AI_THREAD
1911
+ : tid.type === 'replies'
1912
+ ? threadTypeEnum.VIEW_REPLIES
1913
+ : (tid.threadType ?? threadTypeEnum.UNKNOWN),
1914
+ threadKey: tid.key || tid.threadKey
1915
+ })
1916
+ ]
1917
+ }
1918
+ if ('featureEligibilities' in message && message.featureEligibilities) {
1919
+ const fe = message.featureEligibilities
1920
+ const [msgType] = Object.keys(m)
1921
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1922
+ m[msgType].contextInfo = {
1923
+ ...(m[msgType].contextInfo || {}),
1924
+ featureEligibilities: {
1925
+ cannotBeReactedTo: fe.cannotBeReactedTo ?? false,
1926
+ cannotBeRanked: fe.cannotBeRanked ?? false,
1927
+ canRequestFeedback: fe.canRequestFeedback ?? false,
1928
+ canBeReshared: fe.canBeReshared ?? true,
1929
+ canReceiveMultiReact: fe.canReceiveMultiReact ?? true
1930
+ }
1931
+ }
1932
+ }
1933
+ }
1934
+ if ('forwardOrigin' in message) {
1935
+ const [msgType] = Object.keys(m)
1936
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1937
+ const foEnum = WAProto_1.proto.ContextInfo.ForwardOrigin
1938
+ const foMap = {
1939
+ chat: foEnum.CHAT,
1940
+ status: foEnum.STATUS,
1941
+ channels: foEnum.CHANNELS,
1942
+ meta_ai: foEnum.META_AI,
1943
+ ugc: foEnum.UGC
1944
+ }
1945
+ m[msgType].contextInfo = {
1946
+ ...(m[msgType].contextInfo || {}),
1947
+ forwardOrigin:
1948
+ typeof message.forwardOrigin === 'string'
1949
+ ? (foMap[message.forwardOrigin.toLowerCase()] ?? foEnum.CHAT)
1950
+ : (message.forwardOrigin ?? foEnum.CHAT)
1951
+ }
1952
+ }
1953
+ }
1954
+ if ('statusAudienceMetadata' in message && message.statusAudienceMetadata) {
1955
+ const [msgType] = Object.keys(m)
1956
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
1957
+ const audType = WAProto_1.proto.ContextInfo.StatusAudienceMetadata.AudienceType
1958
+ const sam = message.statusAudienceMetadata
1959
+ m[msgType].contextInfo = {
1960
+ ...(m[msgType].contextInfo || {}),
1961
+ statusAudienceMetadata: {
1962
+ audienceType: sam.closeFriends ? audType.CLOSE_FRIENDS : audType.UNKNOWN,
1963
+ listName: sam.listName,
1964
+ listEmoji: sam.listEmoji
1965
+ }
1966
+ }
1967
+ }
1968
+ }
1969
+ if ('aiGenerated' in message && message.aiGenerated === true) {
1970
+ if (m.videoMessage) {
1971
+ m.videoMessage.videoSourceType = WAProto_1.proto.Message.VideoMessage.VideoSourceType.AI_GENERATED
1972
+ }
1973
+ if (m.imageMessage) {
1974
+ m.imageMessage.imageSourceType = WAProto_1.proto.Message.ImageMessage.ImageSourceType.AI_GENERATED
1975
+ }
1976
+ }
1977
+ if ('aiModified' in message && message.aiModified === true) {
1978
+ if (m.imageMessage) {
1979
+ m.imageMessage.imageSourceType = WAProto_1.proto.Message.ImageMessage.ImageSourceType.AI_MODIFIED
1980
+ }
1981
+ }
1982
+ if ('imageSourceType' in message) {
1983
+ if (m.imageMessage) {
1984
+ const istEnum = WAProto_1.proto.Message.ImageMessage.ImageSourceType
1985
+ const istMap = {
1986
+ user: istEnum.USER_IMAGE,
1987
+ ai_generated: istEnum.AI_GENERATED,
1988
+ ai_modified: istEnum.AI_MODIFIED,
1989
+ rasterized: istEnum.RASTERIZED_TEXT_STATUS
1990
+ }
1991
+ m.imageMessage.imageSourceType =
1992
+ typeof message.imageSourceType === 'string'
1993
+ ? (istMap[message.imageSourceType.toLowerCase()] ?? istEnum.USER_IMAGE)
1994
+ : (message.imageSourceType ?? istEnum.USER_IMAGE)
1995
+ }
1996
+ }
1997
+ if ('qrUrl' in message && message.qrUrl && m.imageMessage) {
1998
+ m.imageMessage.qrUrl = message.qrUrl
1999
+ }
2000
+ if ('videoContentUrl' in message && message.videoContentUrl && m.extendedTextMessage) {
2001
+ m.extendedTextMessage.videoContentUrl = message.videoContentUrl
2002
+ }
2003
+ if ('endCardTiles' in message && Array.isArray(message.endCardTiles) && m.extendedTextMessage) {
2004
+ m.extendedTextMessage.endCardTiles = message.endCardTiles.map(tile =>
2005
+ WAProto_1.proto.Message.VideoEndCard.fromObject({
2006
+ username: tile.username || tile.user,
2007
+ caption: tile.caption || tile.text,
2008
+ thumbnailImageUrl: tile.thumbnailUrl || tile.thumbnail,
2009
+ profilePictureUrl: tile.profilePictureUrl || tile.avatar
2010
+ })
2011
+ )
2012
+ }
2013
+ if ('musicMetadata' in message && message.musicMetadata && m.extendedTextMessage) {
2014
+ const mm = message.musicMetadata
2015
+ m.extendedTextMessage.musicMetadata = WAProto_1.proto.EmbeddedMusic.fromObject({
2016
+ songId: mm.songId,
2017
+ author: mm.author || mm.artist,
2018
+ title: mm.title,
2019
+ artistAttribution: mm.artistAttribution,
2020
+ isExplicit: mm.isExplicit ?? false,
2021
+ musicSongStartTimeInMs: mm.startTimeMs || 0,
2022
+ derivedContentStartTimeInMs: mm.derivedStartTimeMs || 0,
2023
+ overlapDurationInMs: mm.overlapDurationMs || 0
2024
+ })
2025
+ }
2026
+ if ('businessInteractionPills' in message && message.businessInteractionPills) {
2027
+ const bip = message.businessInteractionPills
2028
+ const pillTypeEnum = WAProto_1.proto.ContextInfo.BusinessInteractionPills.PillType
2029
+ const entryEnum = WAProto_1.proto.ContextInfo.BusinessInteractionPills.EntryPoint
2030
+ const pillMap = Object.fromEntries(Object.entries(pillTypeEnum).map(([k, v]) => [k.toLowerCase(), v]))
2031
+ const entryMap = Object.fromEntries(Object.entries(entryEnum).map(([k, v]) => [k.toLowerCase(), v]))
2032
+ const [msgType] = Object.keys(m)
2033
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2034
+ m[msgType].contextInfo = {
2035
+ ...(m[msgType].contextInfo || {}),
2036
+ businessInteractionPills: WAProto_1.proto.ContextInfo.BusinessInteractionPills.fromObject({
2037
+ businessJid: bip.businessJid,
2038
+ entryPoint:
2039
+ typeof bip.entryPoint === 'string'
2040
+ ? (entryMap[bip.entryPoint.toLowerCase()] ?? entryEnum.ENTRY_POINT_UNKNOWN)
2041
+ : bip.entryPoint,
2042
+ pills: (bip.pills || []).map(p => ({
2043
+ pillType:
2044
+ typeof p.type === 'string'
2045
+ ? (pillMap[p.type.toLowerCase()] ?? pillTypeEnum.UNKNOWN)
2046
+ : (p.pillType ?? pillTypeEnum.UNKNOWN),
2047
+ actionUrl: p.url || p.actionUrl
2048
+ }))
2049
+ })
2050
+ }
2051
+ }
2052
+ }
2053
+ if ('dataSharingContext' in message && message.dataSharingContext) {
2054
+ const dsc = message.dataSharingContext
2055
+ const [msgType] = Object.keys(m)
2056
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2057
+ m[msgType].contextInfo = {
2058
+ ...(m[msgType].contextInfo || {}),
2059
+ dataSharingContext: WAProto_1.proto.ContextInfo.DataSharingContext.fromObject({
2060
+ showMmDisclosure: dsc.showMmDisclosure ?? false,
2061
+ encryptedSignalTokenConsented: dsc.encryptedToken,
2062
+ dataSharingFlags: dsc.flags || 0
2063
+ })
2064
+ }
2065
+ }
2066
+ }
2067
+ if ('botSession' in message && message.botSession) {
2068
+ const bs = message.botSession
2069
+ const srcEnum = WAProto_1.proto.BotSessionSource
2070
+ const srcMap = Object.fromEntries(Object.entries(srcEnum).map(([k, v]) => [k.toLowerCase(), v]))
2071
+ m.messageContextInfo = m.messageContextInfo || {}
2072
+ const existing = m.messageContextInfo.botMetadata
2073
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2074
+ : {}
2075
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2076
+ ...existing,
2077
+ sessionMetadata: {
2078
+ sessionId: bs.sessionId,
2079
+ sessionSource:
2080
+ typeof bs.source === 'string'
2081
+ ? (srcMap[bs.source.toLowerCase()] ?? srcEnum.NONE)
2082
+ : (bs.sessionSource ?? srcEnum.NONE)
2083
+ }
2084
+ })
2085
+ }
2086
+ if ('botReminder' in message && message.botReminder) {
2087
+ const br = message.botReminder
2088
+ const actionEnum = WAProto_1.proto.BotReminderMetadata.ReminderAction
2089
+ const freqEnum = WAProto_1.proto.BotReminderMetadata.ReminderFrequency
2090
+ const actionMap = {
2091
+ notify: actionEnum.NOTIFY,
2092
+ create: actionEnum.CREATE,
2093
+ delete: actionEnum.DELETE,
2094
+ update: actionEnum.UPDATE
2095
+ }
2096
+ const freqMap = {
2097
+ once: freqEnum.ONCE,
2098
+ daily: freqEnum.DAILY,
2099
+ weekly: freqEnum.WEEKLY,
2100
+ biweekly: freqEnum.BIWEEKLY,
2101
+ monthly: freqEnum.MONTHLY
2102
+ }
2103
+ m.messageContextInfo = m.messageContextInfo || {}
2104
+ const existing = m.messageContextInfo.botMetadata
2105
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2106
+ : {}
2107
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2108
+ ...existing,
2109
+ reminderMetadata: {
2110
+ requestMessageKey: br.requestMessageKey || br.key,
2111
+ action:
2112
+ typeof br.action === 'string'
2113
+ ? (actionMap[br.action.toLowerCase()] ?? actionEnum.NOTIFY)
2114
+ : (br.action ?? actionEnum.NOTIFY),
2115
+ name: br.name,
2116
+ nextTriggerTimestamp: br.nextTriggerTimestamp || br.timestamp,
2117
+ frequency:
2118
+ typeof br.frequency === 'string'
2119
+ ? (freqMap[br.frequency.toLowerCase()] ?? freqEnum.ONCE)
2120
+ : (br.frequency ?? freqEnum.ONCE)
2121
+ }
2122
+ })
2123
+ }
2124
+ if ('botPlugin' in message && message.botPlugin) {
2125
+ const bp = message.botPlugin
2126
+ const ptEnum = WAProto_1.proto.BotPluginMetadata.PluginType
2127
+ const spEnum = WAProto_1.proto.BotPluginMetadata.SearchProvider
2128
+ const ptMap = { reels: ptEnum.REELS, search: ptEnum.SEARCH }
2129
+ const spMap = { bing: spEnum.BING, google: spEnum.GOOGLE, support: spEnum.SUPPORT }
2130
+ m.messageContextInfo = m.messageContextInfo || {}
2131
+ const existing = m.messageContextInfo.botMetadata
2132
+ ? WAProto_1.proto.BotMetadata.toObject(m.messageContextInfo.botMetadata)
2133
+ : {}
2134
+ m.messageContextInfo.botMetadata = WAProto_1.proto.BotMetadata.fromObject({
2135
+ ...existing,
2136
+ pluginMetadata: WAProto_1.proto.BotPluginMetadata.fromObject({
2137
+ provider:
2138
+ typeof bp.provider === 'string'
2139
+ ? (spMap[bp.provider.toLowerCase()] ?? spEnum.UNKNOWN)
2140
+ : (bp.provider ?? spEnum.UNKNOWN),
2141
+ pluginType:
2142
+ typeof bp.pluginType === 'string'
2143
+ ? (ptMap[bp.pluginType.toLowerCase()] ?? ptEnum.UNKNOWN_PLUGIN)
2144
+ : (bp.pluginType ?? ptEnum.UNKNOWN_PLUGIN),
2145
+ searchProviderUrl: bp.searchProviderUrl,
2146
+ searchQuery: bp.searchQuery,
2147
+ thumbnailCdnUrl: bp.thumbnailCdnUrl,
2148
+ profilePhotoCdnUrl: bp.profilePhotoCdnUrl,
2149
+ expectedLinksCount: bp.expectedLinksCount || 0,
2150
+ referenceIndex: bp.referenceIndex || 0
2151
+ })
2152
+ })
2153
+ }
2154
+ if ('isSpoiler' in message && message.isSpoiler === true) {
2155
+ const [msgType] = Object.keys(m)
2156
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2157
+ m[msgType].contextInfo = {
2158
+ ...(m[msgType].contextInfo || {}),
2159
+ isSpoiler: true
2160
+ }
2161
+ }
2162
+ }
2163
+ if ('expiration' in message && typeof message.expiration === 'number') {
2164
+ const [msgType] = Object.keys(m)
2165
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2166
+ m[msgType].contextInfo = {
2167
+ ...(m[msgType].contextInfo || {}),
2168
+ expiration: message.expiration
2169
+ }
2170
+ }
2171
+ }
2172
+ if ('ephemeralSettingTimestamp' in message) {
2173
+ const [msgType] = Object.keys(m)
2174
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2175
+ m[msgType].contextInfo = {
2176
+ ...(m[msgType].contextInfo || {}),
2177
+ ephemeralSettingTimestamp: message.ephemeralSettingTimestamp
2178
+ }
2179
+ }
2180
+ }
2181
+ if ('groupSubject' in message && message.groupSubject) {
2182
+ const [msgType] = Object.keys(m)
2183
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2184
+ m[msgType].contextInfo = {
2185
+ ...(m[msgType].contextInfo || {}),
2186
+ groupSubject: message.groupSubject
2187
+ }
2188
+ }
2189
+ }
2190
+ if ('parentGroupJid' in message && message.parentGroupJid) {
2191
+ const [msgType] = Object.keys(m)
2192
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2193
+ m[msgType].contextInfo = {
2194
+ ...(m[msgType].contextInfo || {}),
2195
+ parentGroupJid: message.parentGroupJid
2196
+ }
2197
+ }
2198
+ }
2199
+ if ('memberLabel' in message && message.memberLabel) {
2200
+ const [msgType] = Object.keys(m)
2201
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2202
+ m[msgType].contextInfo = {
2203
+ ...(m[msgType].contextInfo || {}),
2204
+ memberLabel: message.memberLabel
2205
+ }
2206
+ }
2207
+ }
2208
+ if ('trustBanner' in message && message.trustBanner) {
2209
+ const tb = message.trustBanner
2210
+ const [msgType] = Object.keys(m)
2211
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2212
+ m[msgType].contextInfo = {
2213
+ ...(m[msgType].contextInfo || {}),
2214
+ ...(tb.type != null ? { trustBannerType: tb.type } : {}),
2215
+ ...(tb.action != null ? { trustBannerAction: tb.action } : {})
2216
+ }
2217
+ }
2218
+ }
2219
+ if ('entryPoint' in message && message.entryPoint) {
2220
+ const ep = message.entryPoint
2221
+ const [msgType] = Object.keys(m)
2222
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2223
+ m[msgType].contextInfo = {
2224
+ ...(m[msgType].contextInfo || {}),
2225
+ ...(ep.source ? { entryPointConversionSource: ep.source } : {}),
2226
+ ...(ep.app ? { entryPointConversionApp: ep.app } : {}),
2227
+ ...(ep.delaySecs != null ? { entryPointConversionDelaySeconds: ep.delaySecs } : {}),
2228
+ ...(ep.externalSource ? { entryPointConversionExternalSource: ep.externalSource } : {}),
2229
+ ...(ep.externalMedium ? { entryPointConversionExternalMedium: ep.externalMedium } : {})
2230
+ }
2231
+ }
2232
+ }
2233
+ if ('utm' in message && message.utm) {
2234
+ const [msgType] = Object.keys(m)
2235
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2236
+ m[msgType].contextInfo = {
2237
+ ...(m[msgType].contextInfo || {}),
2238
+ utm: WAProto_1.proto.ContextInfo.UTMInfo.fromObject({
2239
+ utmSource: message.utm.source,
2240
+ utmCampaign: message.utm.campaign
2241
+ })
2242
+ }
2243
+ }
2244
+ }
2245
+ if ('partiallySelectedContent' in message && message.partiallySelectedContent) {
2246
+ const [msgType] = Object.keys(m)
2247
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2248
+ m[msgType].contextInfo = {
2249
+ ...(m[msgType].contextInfo || {}),
2250
+ partiallySelectedContent: message.partiallySelectedContent
2251
+ }
2252
+ }
2253
+ }
2254
+ if ('crossAppSource' in message && message.crossAppSource) {
2255
+ const [msgType] = Object.keys(m)
2256
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2257
+ m[msgType].contextInfo = {
2258
+ ...(m[msgType].contextInfo || {}),
2259
+ crossAppSource: message.crossAppSource
2260
+ }
2261
+ }
2262
+ }
2263
+ if ('isQuestion' in message && message.isQuestion === true) {
2264
+ const [msgType] = Object.keys(m)
2265
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2266
+ m[msgType].contextInfo = {
2267
+ ...(m[msgType].contextInfo || {}),
2268
+ isQuestion: true
2269
+ }
2270
+ }
2271
+ }
2272
+ if ('afterReadDuration' in message && typeof message.afterReadDuration === 'number') {
2273
+ const [msgType] = Object.keys(m)
2274
+ if (msgType && m[msgType] && typeof m[msgType] === 'object') {
2275
+ m[msgType].contextInfo = {
2276
+ ...(m[msgType].contextInfo || {}),
2277
+ afterReadDuration: message.afterReadDuration
2278
+ }
2279
+ }
2280
+ m.messageContextInfo = {
2281
+ ...(m.messageContextInfo || {}),
2282
+ messageAddOnDurationInSecs: message.afterReadDuration,
2283
+ messageAddOnExpiryType: WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
2284
+ }
2285
+ }
2286
+ if ('raw' in message && !!message.raw) {
2287
+ const { raw: _, externalAdReply: _ear, ...rawMsg } = message
2288
+ if ('externalAdReply' in message && !!message.externalAdReply) {
2289
+ const ear = normalizeEarFields(message.externalAdReply)
2290
+ const [rawType] = Object.keys(rawMsg)
2291
+ if (rawType && rawMsg[rawType]) {
2292
+ rawMsg[rawType].contextInfo = {
2293
+ ...(rawMsg[rawType].contextInfo || {}),
2294
+ externalAdReply: ear
2295
+ }
2296
+ }
2297
+ }
2298
+ return WAProto_1.proto.Message.fromObject(rawMsg)
2299
+ } else if (Object.keys(m).length === 0) {
2300
+ m = await (0, exports.prepareWAMessageMedia)(message, options)
2301
+ }
2302
+
2303
+ if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
2304
+ const viewOnceVersion = message.viewOnceVersion || message.viewOnce
2305
+ if (viewOnceVersion === 'v2' || viewOnceVersion === 2) {
2306
+ m = { viewOnceMessageV2: { message: m } }
2307
+ } else if (viewOnceVersion === 'v2ext' || viewOnceVersion === 'v2extension') {
2308
+ m = { viewOnceMessageV2Extension: { message: m } }
2309
+ } else {
2310
+ m = { viewOnceMessage: { message: m } }
2311
+ }
2312
+ }
2313
+ if (hasOptionalProperty(message, 'documentWithCaption') && !!message.documentWithCaption) {
2314
+ m = { documentWithCaptionMessage: { message: m } }
2315
+ }
2316
+ if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
2317
+ m = { ephemeralMessage: { message: m } }
2318
+ }
2319
+ if (hasOptionalProperty(message, 'groupMentioned') && !!message.groupMentioned) {
2320
+ m = { groupMentionedMessage: { message: m } }
2321
+ }
2322
+ if ('groupStatus' in message && !!message.groupStatus) {
2323
+ m = { groupStatusMessage: { message: m } }
2324
+ }
2325
+ if (
2326
+ (hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
2327
+ (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)
2328
+ ) {
2329
+ const normalizedMentions = await (0, jid_display_normalization_1.normalizeMentionedJidsForSend)(
2330
+ message.mentions,
2331
+ options.groupData,
2332
+ options.signalRepository,
2333
+ options.logger
2334
+ )
2335
+ const messageType = Object.keys(m)[0]
2336
+ const key = m[messageType]
2337
+ if (key && 'contextInfo' in key) {
2338
+ key.contextInfo = key.contextInfo || {}
2339
+ if (normalizedMentions?.length) {
2340
+ key.contextInfo.mentionedJid = normalizedMentions
2341
+ }
2342
+ if (message.mentionAll) {
2343
+ key.contextInfo.nonJidMentions = 1
2344
+ } else if (!key) {
2345
+ key.contextInfo = {
2346
+ mentionedJid: normalizedMentions,
2347
+ nonJidMentions: message.mentionAll ? 1 : 0
2348
+ }
2349
+ }
2350
+ }
2351
+ }
2352
+ if (hasOptionalProperty(message, 'edit')) {
2353
+ m = {
2354
+ protocolMessage: {
2355
+ key: message.edit,
2356
+ editedMessage: m,
2357
+ timestampMs: Date.now(),
2358
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT
2359
+ }
2360
+ }
2361
+ }
2362
+ if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
2363
+ const messageType = Object.keys(m)[0]
2364
+ const key = m[messageType]
2365
+ if ('contextInfo' in key && !!key.contextInfo) {
2366
+ key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
2367
+ } else if (key) {
2368
+ key.contextInfo = message.contextInfo
2369
+ }
2370
+ }
2371
+ if ((0, reporting_utils_1.shouldIncludeReportingToken)(m) && !(0, WABinary_1.isInteropUser)(options.jid)) {
2372
+ m.messageContextInfo = m.messageContextInfo || {}
2373
+ if (!m.messageContextInfo.messageSecret) {
2374
+ m.messageContextInfo.messageSecret = (0, crypto_1.randomBytes)(32)
2375
+ }
2376
+ }
2377
+
2378
+ if ('externalAdReply' in message && !!message.externalAdReply) {
2379
+ const wrappers = [
2380
+ 'viewOnceMessage',
2381
+ 'viewOnceMessageV2',
2382
+ 'viewOnceMessageV2Extension',
2383
+ 'ephemeralMessage',
2384
+ 'groupStatusMessage',
2385
+ 'templateMessage'
2386
+ ]
2387
+ const [outerType] = Object.keys(m)
2388
+ const inner = wrappers.includes(outerType) ? m[outerType].message : m
2389
+ const [innerType] = Object.keys(inner)
2390
+ const innerPayload = innerType ? inner[innerType] : undefined
2391
+ if (innerType && innerType !== 'carouselMessage' && innerPayload && typeof innerPayload === 'object') {
2392
+ const ear = normalizeEarFields(message.externalAdReply)
2393
+ innerPayload.contextInfo = {
2394
+ ...(innerPayload.contextInfo || {}),
2395
+ externalAdReply: ear
2396
+ }
2397
+ }
2398
+ }
2399
+ if ('secureMetaServiceLabel' in message && !!message.secureMetaServiceLabel) {
2400
+ const [messageType] = Object.keys(m)
2401
+ m[messageType] = m[messageType] || {}
2402
+ m[messageType].contextInfo = {
2403
+ ...(m[messageType].contextInfo || {}),
2404
+ secureMetaServiceLabel: 1
2405
+ }
2406
+ }
2407
+
2408
+ return WAProto_1.proto.Message.create(m)
2409
+ }
2410
+ exports.generateWAMessageContent = generateWAMessageContent
2411
+ const generateWAMessageFromContent = (jid, message, options) => {
2412
+ // set timestamp to now
2413
+ // if not specified
2414
+ if (!options.timestamp) {
2415
+ options.timestamp = new Date()
2416
+ }
2417
+ const innerMessage = (0, exports.normalizeMessageContent)(message)
2418
+ const key = (0, exports.getContentType)(innerMessage)
2419
+ const timestamp = (0, generics_1.unixTimestampSeconds)(options.timestamp)
2420
+ const { quoted, userJid } = options
2421
+ if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
2422
+ const participant = quoted.key.fromMe
2423
+ ? userJid // TODO: Add support for LIDs
2424
+ : quoted.participant || quoted.key.participant || quoted.key.remoteJid
2425
+ let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message)
2426
+ const msgType = (0, exports.getContentType)(quotedMsg)
2427
+ // strip any redundant properties
2428
+ quotedMsg = WAProto_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
2429
+ const quotedContent = quotedMsg[msgType]
2430
+ if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
2431
+ delete quotedContent.contextInfo
2432
+ }
2433
+ const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {}
2434
+ contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant)
2435
+ contextInfo.stanzaId = quoted.key.id
2436
+ contextInfo.quotedMessage = quotedMsg
2437
+ // if a participant is quoted, then it must be a group
2438
+ // hence, remoteJid of group must also be entered
2439
+ if (jid !== quoted.key.remoteJid) {
2440
+ contextInfo.remoteJid = quoted.key.remoteJid
2441
+ }
2442
+ if (contextInfo && innerMessage[key]) {
2443
+ /* @ts-ignore */
2444
+ innerMessage[key].contextInfo = contextInfo
2445
+ }
2446
+ }
2447
+ if (
2448
+ // if we want to send a disappearing message
2449
+ !!options.ephemeralExpiration &&
2450
+ // and it's not a protocol message -- delete, toggle disappear message
2451
+ key !== 'protocolMessage' &&
2452
+ // already not converted to disappearing message
2453
+ key !== 'ephemeralMessage' &&
2454
+ // newsletters don't support ephemeral messages
2455
+ !(0, WABinary_1.isJidNewsletter)(jid)
2456
+ ) {
2457
+ /* @ts-ignore */
2458
+ innerMessage[key].contextInfo = {
2459
+ ...(innerMessage[key].contextInfo || {}),
2460
+ expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL
2461
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
2462
+ }
2463
+ }
2464
+ message = WAProto_1.proto.Message.create(message)
2465
+ const messageJSON = {
2466
+ key: {
2467
+ remoteJid: jid,
2468
+ fromMe: true,
2469
+ id: options?.messageId || (0, generics_1.generateMessageIDV2)()
2470
+ },
2471
+ message: message,
2472
+ messageTimestamp: timestamp,
2473
+ messageStubParameters: [],
2474
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined, // TODO: Add support for LIDs
2475
+ status: Types_1.WAMessageStatus.PENDING
2476
+ }
2477
+ return WAProto_1.proto.WebMessageInfo.fromObject(messageJSON)
2478
+ }
2479
+ exports.generateWAMessageFromContent = generateWAMessageFromContent
2480
+ const generateWAMessage = async (jid, content, options) => {
2481
+ // ensure msg ID is with every log
2482
+ options.logger = options?.logger?.child({ msgId: options.messageId })
2483
+ // Pass jid in the options to generateWAMessageContent
2484
+ return (0, exports.generateWAMessageFromContent)(
2485
+ jid,
2486
+ await (0, exports.generateWAMessageContent)(content, { ...options, jid }),
2487
+ options
2488
+ )
2489
+ }
2490
+ exports.generateWAMessage = generateWAMessage