@nexustechpro/baileys 2.0.2 → 2.0.5

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 (102) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +924 -1299
  3. package/lib/Defaults/baileys-version.json +6 -2
  4. package/lib/Defaults/index.js +172 -172
  5. package/lib/Signal/libsignal.js +380 -292
  6. package/lib/Signal/lid-mapping.js +264 -171
  7. package/lib/Socket/Client/index.js +2 -2
  8. package/lib/Socket/Client/types.js +10 -10
  9. package/lib/Socket/Client/websocket.js +45 -310
  10. package/lib/Socket/business.js +375 -375
  11. package/lib/Socket/chats.js +909 -963
  12. package/lib/Socket/communities.js +430 -430
  13. package/lib/Socket/groups.js +342 -342
  14. package/lib/Socket/index.js +22 -22
  15. package/lib/Socket/messages-recv.js +777 -743
  16. package/lib/Socket/messages-send.js +295 -305
  17. package/lib/Socket/mex.js +50 -50
  18. package/lib/Socket/newsletter.js +148 -148
  19. package/lib/Socket/nexus-handler.js +75 -261
  20. package/lib/Socket/socket.js +709 -1201
  21. package/lib/Store/index.js +5 -5
  22. package/lib/Store/make-cache-manager-store.js +81 -81
  23. package/lib/Store/make-in-memory-store.js +416 -416
  24. package/lib/Store/make-ordered-dictionary.js +81 -81
  25. package/lib/Store/object-repository.js +30 -30
  26. package/lib/Types/Auth.js +1 -1
  27. package/lib/Types/Bussines.js +1 -1
  28. package/lib/Types/Call.js +1 -1
  29. package/lib/Types/Chat.js +7 -7
  30. package/lib/Types/Contact.js +1 -1
  31. package/lib/Types/Events.js +1 -1
  32. package/lib/Types/GroupMetadata.js +1 -1
  33. package/lib/Types/Label.js +24 -24
  34. package/lib/Types/LabelAssociation.js +6 -6
  35. package/lib/Types/Message.js +10 -10
  36. package/lib/Types/Newsletter.js +28 -28
  37. package/lib/Types/Product.js +1 -1
  38. package/lib/Types/Signal.js +1 -1
  39. package/lib/Types/Socket.js +2 -2
  40. package/lib/Types/State.js +12 -12
  41. package/lib/Types/USync.js +1 -1
  42. package/lib/Types/index.js +25 -25
  43. package/lib/Utils/auth-utils.js +264 -256
  44. package/lib/Utils/baileys-event-stream.js +55 -55
  45. package/lib/Utils/browser-utils.js +27 -27
  46. package/lib/Utils/business.js +228 -230
  47. package/lib/Utils/chat-utils.js +694 -764
  48. package/lib/Utils/crypto.js +109 -135
  49. package/lib/Utils/decode-wa-message.js +310 -314
  50. package/lib/Utils/event-buffer.js +547 -547
  51. package/lib/Utils/generics.js +297 -297
  52. package/lib/Utils/history.js +91 -83
  53. package/lib/Utils/index.js +21 -20
  54. package/lib/Utils/key-store.js +17 -0
  55. package/lib/Utils/link-preview.js +97 -98
  56. package/lib/Utils/logger.js +2 -2
  57. package/lib/Utils/lt-hash.js +47 -47
  58. package/lib/Utils/make-mutex.js +39 -39
  59. package/lib/Utils/message-retry-manager.js +148 -148
  60. package/lib/Utils/messages-media.js +534 -534
  61. package/lib/Utils/messages.js +705 -705
  62. package/lib/Utils/noise-handler.js +255 -255
  63. package/lib/Utils/pre-key-manager.js +105 -105
  64. package/lib/Utils/process-message.js +412 -412
  65. package/lib/Utils/signal.js +160 -158
  66. package/lib/Utils/use-multi-file-auth-state.js +120 -120
  67. package/lib/Utils/validate-connection.js +194 -194
  68. package/lib/WABinary/constants.js +1300 -1300
  69. package/lib/WABinary/decode.js +237 -237
  70. package/lib/WABinary/encode.js +232 -232
  71. package/lib/WABinary/generic-utils.js +252 -211
  72. package/lib/WABinary/index.js +5 -5
  73. package/lib/WABinary/jid-utils.js +279 -95
  74. package/lib/WABinary/types.js +1 -1
  75. package/lib/WAM/BinaryInfo.js +9 -9
  76. package/lib/WAM/constants.js +22852 -22852
  77. package/lib/WAM/encode.js +149 -149
  78. package/lib/WAM/index.js +3 -3
  79. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -28
  80. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -53
  81. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -26
  82. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -37
  83. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
  84. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -28
  85. package/lib/WAUSync/Protocols/index.js +4 -4
  86. package/lib/WAUSync/USyncQuery.js +93 -93
  87. package/lib/WAUSync/USyncUser.js +22 -22
  88. package/lib/WAUSync/index.js +3 -3
  89. package/lib/index.js +66 -66
  90. package/package.json +171 -144
  91. package/lib/Signal/Group/ciphertext-message.js +0 -12
  92. package/lib/Signal/Group/group-session-builder.js +0 -30
  93. package/lib/Signal/Group/group_cipher.js +0 -100
  94. package/lib/Signal/Group/index.js +0 -12
  95. package/lib/Signal/Group/keyhelper.js +0 -18
  96. package/lib/Signal/Group/sender-chain-key.js +0 -26
  97. package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
  98. package/lib/Signal/Group/sender-key-message.js +0 -66
  99. package/lib/Signal/Group/sender-key-name.js +0 -48
  100. package/lib/Signal/Group/sender-key-record.js +0 -41
  101. package/lib/Signal/Group/sender-key-state.js +0 -84
  102. package/lib/Signal/Group/sender-message-key.js +0 -26
@@ -1,9 +1,3 @@
1
- /**
2
- * @nexus/baileys - Advanced Message Handler
3
- * Handles: PAYMENT, PRODUCT, INTERACTIVE, ALBUM, EVENT, POLL_RESULT,
4
- * STATUS_MENTION, ORDER, GROUP_STATUS, CAROUSEL, CAROUSEL_PROTO, STICKER_PACK
5
- */
6
-
7
1
  import axios from 'axios'
8
2
  import crypto from 'crypto'
9
3
 
@@ -14,8 +8,6 @@ class NexusHandler {
14
8
  this.upload = waUploadToServer
15
9
  this.opts = options
16
10
  this.user = options.user || null
17
-
18
- // Auto-routing map for message types
19
11
  this.handlers = {
20
12
  PAYMENT: this.handlePayment.bind(this),
21
13
  PRODUCT: this.handleProduct.bind(this),
@@ -32,41 +24,29 @@ class NexusHandler {
32
24
  }
33
25
  }
34
26
 
35
- // ========== TYPE DETECTION ==========
27
+ // ─── TYPE DETECTION ───────────────────────────────────────────────────────
36
28
  detectType(content) {
29
+ if (content.carouselMessage || content.carousel) return 'CAROUSEL'
37
30
  const types = {
38
- requestPaymentMessage: 'PAYMENT',
39
- productMessage: 'PRODUCT',
40
- interactiveMessage: 'INTERACTIVE',
41
- interactive: 'INTERACTIVE', // Also detect 'interactive' key
42
- albumMessage: 'ALBUM',
43
- eventMessage: 'EVENT',
44
- pollResultMessage: 'POLL_RESULT',
45
- statusMentionMessage: 'STATUS_MENTION',
46
- orderMessage: 'ORDER',
47
- stickerPack: 'STICKER_PACK',
48
- groupStatus: 'GROUP_STATUS',
49
- carouselProto: 'CAROUSEL_PROTO'
31
+ requestPaymentMessage: 'PAYMENT', productMessage: 'PRODUCT',
32
+ interactiveMessage: 'INTERACTIVE', interactive: 'INTERACTIVE',
33
+ albumMessage: 'ALBUM', eventMessage: 'EVENT', pollResultMessage: 'POLL_RESULT',
34
+ statusMentionMessage: 'STATUS_MENTION', orderMessage: 'ORDER',
35
+ stickerPack: 'STICKER_PACK', groupStatus: 'GROUP_STATUS', carouselProto: 'CAROUSEL_PROTO'
50
36
  }
51
-
52
- if (content.carouselMessage || content.carousel) return 'CAROUSEL'
53
37
  return types[Object.keys(types).find(k => content[k])] || null
54
38
  }
55
39
 
56
- // ========== UNIFIED PROCESSOR ==========
40
+ // ─── UNIFIED PROCESSOR ────────────────────────────────────────────────────
57
41
  async processMessage(content, jid, quoted) {
58
42
  const messageType = this.detectType(content)
59
43
  if (!messageType) throw new Error('Unknown message type')
60
-
61
44
  const handler = this.handlers[messageType]
62
45
  if (!handler) throw new Error(`No handler for: ${messageType}`)
63
-
64
- return messageType === 'STICKER_PACK'
65
- ? await handler(content.stickerPack, jid, quoted)
66
- : await handler(content, jid, quoted)
46
+ return messageType === 'STICKER_PACK' ? await handler(content.stickerPack, jid, quoted) : await handler(content, jid, quoted)
67
47
  }
68
48
 
69
- // ========== HELPERS ==========
49
+ // ─── HELPERS ──────────────────────────────────────────────────────────────
70
50
  async prepMedia(data, type) {
71
51
  if (!data) return null
72
52
  const payload = typeof data === 'object' && data.url ? { [type]: { url: data.url } } : { [type]: data }
@@ -74,101 +54,72 @@ class NexusHandler {
74
54
  }
75
55
 
76
56
  async genContent(jid, content, opts = {}) {
77
- return await this.utils.generateWAMessage(jid, content, { ...opts, upload: this.upload })
57
+ return await this.utils.generateWAMessage(jid, content, {
58
+ ...opts,
59
+ upload: this.upload,
60
+ userJid: opts.userJid || this.user?.id,
61
+ getUrlInfo: opts.getUrlInfo || this.opts.getUrlInfo,
62
+ logger: opts.logger || this.opts.logger
63
+ })
78
64
  }
79
65
 
80
66
  async sendMsg(jid, message, opts = {}) {
81
- await this.relay(jid, message, opts)
67
+ return await this.relay(jid, message, opts)
82
68
  }
83
69
 
84
70
  buildCtx(quoted, sender) {
85
- return {
86
- stanzaId: quoted?.key?.id,
87
- participant: quoted?.key?.participant || sender,
88
- quotedMessage: quoted?.message
89
- }
71
+ return { stanzaId: quoted?.key?.id, participant: quoted?.key?.participant || sender, quotedMessage: quoted?.message }
90
72
  }
91
73
 
92
74
  buildFullCtx(ctx, adReply) {
93
75
  const final = ctx ? { mentionedJid: ctx.mentionedJid || [], forwardingScore: ctx.forwardingScore || 0, isForwarded: ctx.isForwarded || false, ...ctx } : {}
94
- if (adReply) {
95
- final.externalAdReply = { title: adReply.title || '', body: adReply.body || '', mediaType: adReply.mediaType || 1,
96
- thumbnailUrl: adReply.thumbnailUrl || '', mediaUrl: adReply.mediaUrl || '', sourceUrl: adReply.sourceUrl || '',
97
- showAdAttribution: adReply.showAdAttribution || false, renderLargerThumbnail: adReply.renderLargerThumbnail || false, ...adReply }
98
- }
76
+ if (adReply) final.externalAdReply = { title: adReply.title || '', body: adReply.body || '', mediaType: adReply.mediaType || 1, thumbnailUrl: adReply.thumbnailUrl || '', mediaUrl: adReply.mediaUrl || '', sourceUrl: adReply.sourceUrl || '', showAdAttribution: adReply.showAdAttribution || false, renderLargerThumbnail: adReply.renderLargerThumbnail || false, ...adReply }
99
77
  return final
100
78
  }
101
79
 
102
- genJid() {
103
- return this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net'
104
- }
105
-
106
- parseTime(val, def) {
107
- return typeof val === 'string' ? parseInt(val) : val || def
108
- }
109
-
110
- delay(ms) {
111
- return new Promise(resolve => setTimeout(resolve, ms))
112
- }
113
-
114
- // ========== MESSAGE HANDLERS ==========
80
+ genJid() { return this.utils.generateMessageIDV2().split('@')[0] + '@s.whatsapp.net' }
81
+ parseTime(val, def) { return typeof val === 'string' ? parseInt(val) : val || def }
82
+ delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)) }
115
83
 
116
- async handlePayment(content, quoted) {
84
+ // ─── PAYMENT ──────────────────────────────────────────────────────────────
85
+ async handlePayment(content, jid, quoted) {
117
86
  const d = content.requestPaymentMessage
118
- const notes = d.sticker?.stickerMessage ? {
119
- stickerMessage: { ...d.sticker.stickerMessage, contextInfo: this.buildCtx(quoted, content.sender) }
120
- } : d.note ? {
121
- extendedTextMessage: { text: d.note, contextInfo: this.buildCtx(quoted, content.sender) }
122
- } : {}
123
-
124
- const msg = await this.genContent(content.jid, {
125
- requestPaymentMessage: this.utils.WAProto.proto.Message.RequestPaymentMessage.fromObject({
126
- expiryTimestamp: d.expiry || 0, amount1000: d.amount || 0, currencyCodeIso4217: d.currency || 'IDR',
127
- requestFrom: d.from || '0@s.whatsapp.net', noteMessage: notes, background: d.background ?? { id: 'DEFAULT', placeholderArgb: 0xfff0f0f0 }
128
- })
87
+ const notes = d.sticker?.stickerMessage
88
+ ? { stickerMessage: { ...d.sticker.stickerMessage, contextInfo: this.buildCtx(quoted, content.sender) } }
89
+ : d.note ? { extendedTextMessage: { text: d.note, contextInfo: this.buildCtx(quoted, content.sender) } } : {}
90
+ const targetJid = jid || content.jid
91
+ const msg = await this.genContent(targetJid, {
92
+ requestPaymentMessage: {
93
+ expiryTimestamp: d.expiry || 0, amount1000: d.amount || 0,
94
+ currencyCodeIso4217: d.currency || 'IDR', requestFrom: d.from || '0@s.whatsapp.net',
95
+ noteMessage: notes, background: d.background ?? { id: 'DEFAULT', placeholderArgb: 0xfff0f0f0 }
96
+ }
129
97
  }, { quoted })
130
-
131
- await this.sendMsg(content.jid, msg.message, { messageId: msg.key.id })
98
+ await this.sendMsg(targetJid, msg.message, { messageId: msg.key.id })
132
99
  return msg
133
100
  }
134
101
 
102
+ // ─── PRODUCT ──────────────────────────────────────────────────────────────
135
103
  async handleProduct(content, jid, quoted) {
136
104
  const p = content.productMessage || {}
137
105
  let prodImg = null
138
-
139
106
  if (p.thumbnail) {
140
107
  const imgContent = Buffer.isBuffer(p.thumbnail) ? { image: p.thumbnail } : { image: { url: p.thumbnail.url || p.thumbnail } }
141
108
  const res = await this.utils.generateWAMessageContent(imgContent, { upload: this.upload })
142
109
  prodImg = res?.imageMessage || res?.message?.imageMessage
143
110
  }
144
-
145
- const product = {
146
- productId: p.productId, title: p.title || '', description: p.description || '', currencyCode: p.currencyCode || 'IDR',
147
- priceAmount1000: p.priceAmount1000, retailerId: p.retailerId, url: p.url, productImageCount: prodImg ? 1 : 0,
148
- ...(prodImg && { productImage: prodImg })
149
- }
150
-
111
+ const product = { productId: p.productId, title: p.title || '', description: p.description || '', currencyCode: p.currencyCode || 'IDR', priceAmount1000: p.priceAmount1000, retailerId: p.retailerId, url: p.url, productImageCount: prodImg ? 1 : 0, ...(prodImg && { productImage: prodImg }) }
151
112
  const msg = await this.genContent(jid, {
152
- viewOnceMessage: {
153
- message: {
154
- interactiveMessage: {
155
- body: { text: p.body || '' }, footer: { text: p.footer || '' },
156
- header: { title: p.title, hasMediaAttachment: !!prodImg, productMessage: { product, businessOwnerJid: '0@s.whatsapp.net' } },
157
- nativeFlowMessage: { buttons: p.buttons || [] }
158
- }
159
- }
160
- }
113
+ viewOnceMessage: { message: { interactiveMessage: { body: { text: p.body || '' }, footer: { text: p.footer || '' }, header: { title: p.title, hasMediaAttachment: !!prodImg, productMessage: { product, businessOwnerJid: '0@s.whatsapp.net' } }, nativeFlowMessage: { buttons: p.buttons || [] } } } }
161
114
  }, { quoted })
162
-
163
115
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
164
116
  return msg
165
117
  }
166
118
 
119
+ // ─── INTERACTIVE ──────────────────────────────────────────────────────────
167
120
  async handleInteractive(content, jid, quoted) {
168
- // Handle both 'interactiveMessage' and 'interactive' keys
169
121
  const i = content.interactiveMessage || content.interactive || {}
170
122
  let media = null
171
-
172
123
  if (i.thumbnail) media = await this.prepMedia({ url: i.thumbnail }, 'image')
173
124
  else if (i.image) media = await this.prepMedia(i.image, 'image')
174
125
  else if (i.video) media = await this.prepMedia(i.video, 'video')
@@ -178,286 +129,150 @@ class NexusHandler {
178
129
  if (i.fileName) media.documentMessage.fileName = i.fileName
179
130
  if (i.mimetype) media.documentMessage.mimetype = i.mimetype
180
131
  }
181
-
182
- // Handle both formats: { title, footer } and { body: {text}, footer: {text} }
183
132
  const bodyText = i.body?.text || i.title || ''
184
133
  const footerText = i.footer?.text || i.footer || ''
185
134
  const interactive = { body: { text: bodyText }, footer: { text: footerText } }
186
-
187
135
  if (i.buttons?.length || i.nativeFlowMessage) {
188
- // Build nativeFlowMessage with proper structure
189
136
  const nativeFlow = i.nativeFlowMessage || {}
190
- // Only include buttons, don't add empty messageParamsJson
191
- interactive.nativeFlowMessage = {
192
- buttons: i.buttons || nativeFlow.buttons || []
193
- }
194
- // Only add messageParamsJson if explicitly provided
195
- if (nativeFlow.messageParamsJson) {
196
- interactive.nativeFlowMessage.messageParamsJson = nativeFlow.messageParamsJson
197
- }
137
+ interactive.nativeFlowMessage = { buttons: i.buttons || nativeFlow.buttons || [], messageParamsJson: nativeFlow.messageParamsJson || '' }
198
138
  }
199
-
200
139
  if (media) {
201
140
  const headerMedia = {}
202
141
  if (media.imageMessage) headerMedia.imageMessage = media.imageMessage
203
142
  if (media.videoMessage) headerMedia.videoMessage = media.videoMessage
204
143
  if (media.documentMessage) headerMedia.documentMessage = media.documentMessage
205
- // Handle both plain title string and full header object
206
144
  const headerTitle = typeof i.header === 'string' ? i.header : i.header?.title || ''
207
145
  interactive.header = { title: headerTitle, hasMediaAttachment: true, ...headerMedia }
208
146
  } else {
209
- // Handle both plain title string and full header object
210
147
  const headerTitle = typeof i.header === 'string' ? i.header : i.header?.title || ''
211
148
  interactive.header = { title: headerTitle, hasMediaAttachment: false }
212
149
  }
213
-
214
150
  const ctx = this.buildFullCtx(i.contextInfo, i.externalAdReply)
215
151
  if (Object.keys(ctx).length) interactive.contextInfo = ctx
216
-
217
- // Return interactiveMessage directly without wrapping for native flows
218
- // This matches fadzzz404's approach which works correctly
219
- const messageContent = { interactiveMessage: interactive }
220
-
221
- const msg = await this.genContent(jid, messageContent, { quoted })
152
+ const msg = await this.genContent(jid, { interactiveMessage: interactive }, { quoted })
222
153
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
223
154
  return msg
224
155
  }
225
156
 
157
+ // ─── ALBUM ────────────────────────────────────────────────────────────────
226
158
  async handleAlbum(content, jid, quoted) {
227
159
  const arr = Array.isArray(content.albumMessage) ? content.albumMessage : []
228
160
  if (!arr.length) throw new Error('albumMessage must contain media items')
229
-
230
161
  const album = await this.genContent(jid, {
231
162
  messageContextInfo: { messageSecret: crypto.randomBytes(32) },
232
163
  albumMessage: { expectedImageCount: arr.filter(a => a.image).length, expectedVideoCount: arr.filter(a => a.video).length }
233
164
  }, { userJid: this.genJid(), quoted })
234
-
235
165
  await this.sendMsg(jid, album.message, { messageId: album.key.id })
236
-
237
166
  for (const item of arr) {
238
167
  const img = await this.utils.generateWAMessage(jid, item, { upload: this.upload })
239
-
240
- img.message.messageContextInfo = {
241
- messageSecret: crypto.randomBytes(32), messageAssociation: { associationType: 1, parentMessageKey: album.key },
242
- participant: '0@s.whatsapp.net', remoteJid: 'status@broadcast', forwardingScore: 99999, isForwarded: true,
243
- mentionedJid: [jid], starred: true, labels: ['Y', 'Important'], isHighlighted: true,
244
- businessMessageForwardInfo: { businessOwnerJid: jid }, dataSharingContext: { showMmDisclosure: true }
245
- }
246
-
247
- img.message.forwardedNewsletterMessageInfo = {
248
- newsletterJid: '0@newsletter', serverMessageId: 1, newsletterName: 'WhatsApp', contentType: 'UPDATE_CARD',
249
- timestamp: new Date().toISOString(), senderName: 'Nexus', priority: 'high', status: 'sent'
250
- }
251
-
252
- img.message.disappearingMode = {
253
- initiator: 3, trigger: 4, initiatorDeviceJid: jid, initiatedByExternalService: true, initiatedByUserDevice: true,
254
- initiatedBySystem: true, initiatedByServer: true, initiatedByAdmin: true, initiatedByUser: true, initiatedByApp: true,
255
- initiatedByBot: true, initiatedByMe: true
256
- }
257
-
258
- await this.sendMsg(jid, img.message, {
259
- messageId: img.key.id,
260
- quoted: { key: { ...album.key, fromMe: true, participant: this.genJid() }, message: album.message }
261
- })
168
+ img.message.messageContextInfo = { messageSecret: crypto.randomBytes(32), messageAssociation: { associationType: 1, parentMessageKey: album.key }, participant: '0@s.whatsapp.net', remoteJid: 'status@broadcast', forwardingScore: 99999, isForwarded: true, mentionedJid: [jid], starred: true, labels: ['Y', 'Important'], isHighlighted: true, businessMessageForwardInfo: { businessOwnerJid: jid }, dataSharingContext: { showMmDisclosure: true } }
169
+ img.message.forwardedNewsletterMessageInfo = { newsletterJid: '0@newsletter', serverMessageId: 1, newsletterName: 'WhatsApp', contentType: 'UPDATE_CARD', timestamp: new Date().toISOString(), senderName: 'Nexus', priority: 'high', status: 'sent' }
170
+ img.message.disappearingMode = { initiator: 3, trigger: 4, initiatorDeviceJid: jid, initiatedByExternalService: true, initiatedByUserDevice: true, initiatedBySystem: true, initiatedByServer: true, initiatedByAdmin: true, initiatedByUser: true, initiatedByApp: true, initiatedByBot: true, initiatedByMe: true }
171
+ await this.sendMsg(jid, img.message, { messageId: img.key.id, quoted: { key: { ...album.key, fromMe: true, participant: this.genJid() }, message: album.message } })
262
172
  }
263
-
264
173
  return album
265
174
  }
266
175
 
176
+ // ─── EVENT ────────────────────────────────────────────────────────────────
267
177
  async handleEvent(content, jid, quoted) {
268
178
  const e = content.eventMessage
269
-
270
179
  const msg = await this.genContent(jid, {
271
- viewOnceMessage: {
272
- message: {
273
- messageContextInfo: {
274
- deviceListMetadata: {}, deviceListMetadataVersion: 2, messageSecret: crypto.randomBytes(32),
275
- supportPayload: JSON.stringify({ version: 2, is_ai_message: true, should_show_system_message: true, ticket_id: crypto.randomBytes(16).toString('hex') })
276
- },
277
- eventMessage: {
278
- contextInfo: {
279
- mentionedJid: [jid], participant: jid, remoteJid: 'status@broadcast',
280
- forwardedNewsletterMessageInfo: { newsletterName: 'Nexus Events', newsletterJid: '120363422827915475@newsletter', serverMessageId: 1 }
281
- },
282
- isCanceled: e.isCanceled || false, name: e.name, description: e.description,
283
- location: e.location || { degreesLatitude: 0, degreesLongitude: 0, name: 'Location' }, joinLink: e.joinLink || '',
284
- startTime: this.parseTime(e.startTime, Date.now()), endTime: this.parseTime(e.endTime, Date.now() + 3600000),
285
- extraGuestsAllowed: e.extraGuestsAllowed !== false
286
- }
287
- }
288
- }
180
+ viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2, messageSecret: crypto.randomBytes(32), supportPayload: JSON.stringify({ version: 2, is_ai_message: true, should_show_system_message: true, ticket_id: crypto.randomBytes(16).toString('hex') }) }, eventMessage: { contextInfo: { mentionedJid: [jid], participant: jid, remoteJid: 'status@broadcast', forwardedNewsletterMessageInfo: { newsletterName: 'Nexus Events', newsletterJid: '120363422827915475@newsletter', serverMessageId: 1 } }, isCanceled: e.isCanceled || false, name: e.name, description: e.description, location: e.location || { degreesLatitude: 0, degreesLongitude: 0, name: 'Location' }, joinLink: e.joinLink || '', startTime: this.parseTime(e.startTime, Date.now()), endTime: this.parseTime(e.endTime, Date.now() + 3600000), extraGuestsAllowed: e.extraGuestsAllowed !== false } } }
289
181
  }, { quoted })
290
-
291
182
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
292
183
  return msg
293
184
  }
294
185
 
186
+ // ─── POLL RESULT ──────────────────────────────────────────────────────────
295
187
  async handlePollResult(content, jid, quoted) {
296
188
  const p = content.pollResultMessage
297
-
298
189
  const msg = await this.genContent(jid, {
299
- pollResultSnapshotMessage: {
300
- name: p.name,
301
- pollVotes: (p.pollVotes || []).map(v => ({
302
- optionName: v.optionName,
303
- optionVoteCount: typeof v.optionVoteCount === 'number' ? v.optionVoteCount.toString() : v.optionVoteCount
304
- })),
305
- contextInfo: {
306
- isForwarded: true, forwardingScore: 1,
307
- forwardedNewsletterMessageInfo: {
308
- newsletterName: p.newsletter?.newsletterName || 'Newsletter',
309
- newsletterJid: p.newsletter?.newsletterJid || '120363399602691477@newsletter',
310
- serverMessageId: 1000, contentType: 'UPDATE'
311
- }
312
- }
313
- }
190
+ pollResultSnapshotMessage: { name: p.name, pollVotes: (p.pollVotes || []).map(v => ({ optionName: v.optionName, optionVoteCount: typeof v.optionVoteCount === 'number' ? v.optionVoteCount.toString() : v.optionVoteCount })), contextInfo: { isForwarded: true, forwardingScore: 1, forwardedNewsletterMessageInfo: { newsletterName: p.newsletter?.newsletterName || 'Newsletter', newsletterJid: p.newsletter?.newsletterJid || '120363399602691477@newsletter', serverMessageId: 1000, contentType: 'UPDATE' } } }
314
191
  }, { userJid: this.genJid(), quoted })
315
-
316
192
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
317
193
  return msg
318
194
  }
319
195
 
196
+ // ─── STATUS MENTION ───────────────────────────────────────────────────────
320
197
  async handleStMention(content, jid, quoted) {
321
198
  const d = content.statusMentionMessage
322
199
  const media = await this.prepMedia(d.image || d.video, d.image ? 'image' : 'video')
323
-
324
- const msg = await this.relay('status@broadcast', { ...media }, {
325
- statusJidList: [d.mentions, this.user?.id],
326
- additionalNodes: [{
327
- tag: 'meta', attrs: {},
328
- content: [{ tag: 'mentioned_users', attrs: {}, content: [{ tag: 'to', attrs: { jid: d.mentions }, content: undefined }] }]
329
- }]
200
+ await this.relay('status@broadcast', { ...media }, {
201
+ statusJidList: [d.mentions, this.user?.id].filter(Boolean),
202
+ additionalNodes: [{ tag: 'meta', attrs: {}, content: [{ tag: 'mentioned_users', attrs: {}, content: [{ tag: 'to', attrs: { jid: d.mentions }, content: undefined }] }] }]
330
203
  })
331
-
332
204
  const xontols = await this.genContent(jid, {
333
- statusMentionMessage: { message: { protocolMessage: { messageId: msg.key, type: 'STATUS_MENTION_MESSAGE' } } }
334
- }, { additionalNodes: [{ tag: 'meta', attrs: { is_status_mention: true }, content: undefined }] })
335
-
205
+ statusMentionMessage: { message: { protocolMessage: { messageId: d.mentions, type: 'STATUS_MENTION_MESSAGE' } } }
206
+ }, { additionalNodes: [{ tag: 'meta', attrs: { is_status_mention: 'true' }, content: undefined }] })
336
207
  await this.sendMsg(jid, xontols.message, { messageId: xontols.key.id })
337
208
  return xontols
338
209
  }
339
210
 
211
+ // ─── ORDER ────────────────────────────────────────────────────────────────
340
212
  async handleOrderMessage(content, jid, quoted) {
341
213
  const o = content.orderMessage
342
214
  let thumb = null
343
-
344
215
  if (o.thumbnail) {
345
- if (Buffer.isBuffer(o.thumbnail)) {
346
- thumb = o.thumbnail
347
- } else if (typeof o.thumbnail === 'string') {
348
- try {
349
- const res = await axios.get(o.thumbnail, { responseType: 'arraybuffer' })
350
- thumb = Buffer.from(res.data)
351
- } catch (err) {
352
- console.error('Failed to download thumbnail:', err)
353
- }
216
+ if (Buffer.isBuffer(o.thumbnail)) thumb = o.thumbnail
217
+ else if (typeof o.thumbnail === 'string') {
218
+ try { const res = await axios.get(o.thumbnail, { responseType: 'arraybuffer' }); thumb = Buffer.from(res.data) } catch (err) { this.opts.logger?.warn('Failed to download order thumbnail') }
354
219
  }
355
220
  }
356
-
357
221
  const msg = await this.genContent(jid, {
358
- orderMessage: {
359
- orderId: '7NEXUS25022008', thumbnail: thumb, itemCount: o.itemCount || 0, status: 'ACCEPTED', surface: 'CATALOG',
360
- message: o.message, orderTitle: o.orderTitle, sellerJid: '0@whatsapp.net', token: 'NEXUS_EXAMPLE_TOKEN',
361
- totalAmount1000: o.totalAmount1000 || 0, totalCurrencyCode: o.totalCurrencyCode || 'IDR', messageVersion: 2
362
- }
222
+ orderMessage: { orderId: '7NEXUS25022008', thumbnail: thumb, itemCount: o.itemCount || 0, status: 'ACCEPTED', surface: 'CATALOG', message: o.message, orderTitle: o.orderTitle, sellerJid: '0@whatsapp.net', token: 'NEXUS_EXAMPLE_TOKEN', totalAmount1000: o.totalAmount1000 || 0, totalCurrencyCode: o.totalCurrencyCode || 'IDR', messageVersion: 2 }
363
223
  }, { quoted })
364
-
365
224
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
366
225
  return msg
367
226
  }
368
227
 
228
+ // ─── GROUP STATUS ─────────────────────────────────────────────────────────
369
229
  async handleGroupStory(content, jid, quoted) {
370
230
  const s = content.groupStatus
371
- const msgContent = s.message || await this.utils.generateWAMessageContent(s, { upload: this.upload })
372
- const msg = { message: { groupStatusMessageV2: { message: msgContent.message || msgContent } } }
373
- return await this.relay(jid, msg.message, { messageId: this.utils.generateMessageID() })
231
+ const mediaContent = await this.utils.generateWAMessageContent(s, { upload: this.upload, getUrlInfo: this.opts.getUrlInfo, logger: this.opts.logger })
232
+ const msg = await this.utils.generateWAMessageFromContent(jid, { groupStatusMessageV2: { message: mediaContent } }, { userJid: jid })
233
+ return await this.sendMsg(jid, msg.message, { messageId: msg.key.id, additionalNodes: [{ tag: 'meta', attrs: { is_group_status: 'true' }, content: undefined }] })
374
234
  }
375
235
 
236
+ // ─── CAROUSEL ─────────────────────────────────────────────────────────────
376
237
  async handleCarousel(content, jid, quoted) {
377
238
  const c = content.carouselMessage || content.carousel || {}
378
239
  const cards = await Promise.all((c.cards || []).map(card => this.buildCard(card)))
379
-
380
240
  const msg = await this.genContent(jid, {
381
- viewOnceMessage: {
382
- message: {
383
- interactiveMessage: {
384
- body: { text: c.caption || c.body || '' }, footer: { text: c.footer || '' },
385
- carouselMessage: { cards, messageVersion: 1 }
386
- }
387
- }
388
- }
241
+ viewOnceMessage: { message: { interactiveMessage: { body: { text: c.caption || c.body || '' }, footer: { text: c.footer || '' }, carouselMessage: { cards, messageVersion: 1 } } } }
389
242
  }, { quoted })
390
-
391
243
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
392
244
  return msg
393
245
  }
394
246
 
395
247
  async buildCard(card) {
396
248
  if (card.productTitle) {
397
- return {
398
- header: {
399
- title: card.headerTitle || '', subtitle: card.headerSubtitle || '',
400
- productMessage: {
401
- product: {
402
- productImage: (await this.prepMedia({ url: card.imageUrl }, 'image')).imageMessage,
403
- productId: card.productId || '123456', title: card.productTitle, description: card.productDescription || '',
404
- currencyCode: card.currencyCode || 'IDR', priceAmount1000: card.priceAmount1000 || '100000',
405
- retailerId: card.retailerId || 'Retailer', url: card.url || '', productImageCount: 1
406
- },
407
- businessOwnerJid: card.businessOwnerJid || '0@s.whatsapp.net'
408
- },
409
- hasMediaAttachment: false
410
- },
411
- body: { text: card.bodyText || '' }, footer: { text: card.footerText || '' },
412
- nativeFlowMessage: { buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) }
413
- }
249
+ return { header: { title: card.headerTitle || '', subtitle: card.headerSubtitle || '', productMessage: { product: { productImage: (await this.prepMedia({ url: card.imageUrl }, 'image')).imageMessage, productId: card.productId || '123456', title: card.productTitle, description: card.productDescription || '', currencyCode: card.currencyCode || 'IDR', priceAmount1000: card.priceAmount1000 || '100000', retailerId: card.retailerId || 'Retailer', url: card.url || '', productImageCount: 1 }, businessOwnerJid: card.businessOwnerJid || '0@s.whatsapp.net' }, hasMediaAttachment: false }, body: { text: card.bodyText || '' }, footer: { text: card.footerText || '' }, nativeFlowMessage: { buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) } }
414
250
  }
415
-
416
251
  const imgMedia = card.imageUrl ? await this.prepMedia({ url: card.imageUrl }, 'image') : {}
417
- return {
418
- header: { title: card.headerTitle || '', subtitle: card.headerSubtitle || '', hasMediaAttachment: !!card.imageUrl, ...imgMedia },
419
- body: { text: card.bodyText || '' }, footer: { text: card.footerText || '' },
420
- nativeFlowMessage: { buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) }
421
- }
252
+ return { header: { title: card.headerTitle || '', subtitle: card.headerSubtitle || '', hasMediaAttachment: !!card.imageUrl, ...imgMedia }, body: { text: card.bodyText || '' }, footer: { text: card.footerText || '' }, nativeFlowMessage: { buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) } }
422
253
  }
423
254
 
255
+ // ─── CAROUSEL PROTO ───────────────────────────────────────────────────────
424
256
  async handleCarouselProto(content, jid, quoted) {
425
257
  const c = content.carouselProto
426
258
  const proto = this.utils.WAProto?.proto
427
259
  if (!proto) throw new Error('WAProto not available')
428
-
429
260
  const cards = await Promise.all((c.cards || []).map(async card => ({
430
261
  header: proto.Message.InteractiveMessage.Header.create({ title: card.title?.substring(0, 60) || '', subtitle: card.subtitle || '', hasMediaAttachment: false }),
431
262
  body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }),
432
263
  footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }),
433
- nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
434
- buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) }))
435
- })
264
+ nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) })
436
265
  })))
437
-
438
266
  const msg = await this.genContent(jid, {
439
- viewOnceMessage: {
440
- message: {
441
- messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 },
442
- interactiveMessage: proto.Message.InteractiveMessage.create({
443
- body: proto.Message.InteractiveMessage.Body.create({ text: c.body || '' }),
444
- footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }),
445
- carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards })
446
- })
447
- }
448
- }
267
+ viewOnceMessage: { message: { messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 }, interactiveMessage: proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.create({ text: c.body || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }), carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards }) }) } }
449
268
  }, { quoted })
450
-
451
269
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
452
270
  return msg
453
271
  }
454
272
 
273
+ // ─── STICKER PACK ─────────────────────────────────────────────────────────
455
274
  async handleStickerPack(stickerPack, jid, quoted) {
456
- const result = await this.utils.prepareStickerPackMessage(stickerPack, {
457
- logger: this.opts?.logger, upload: this.upload, mediaCache: this.opts?.mediaCache,
458
- options: this.opts, mediaUploadTimeoutMs: this.opts?.mediaUploadTimeoutMs
459
- })
460
-
275
+ const result = await this.utils.prepareStickerPackMessage(stickerPack, { logger: this.opts?.logger, upload: this.upload, mediaCache: this.opts?.mediaCache, options: this.opts, mediaUploadTimeoutMs: this.opts?.mediaUploadTimeoutMs })
461
276
  if (result.isBatched) {
462
277
  const sent = []
463
278
  for (let i = 0; i < result.stickerPackMessage.length; i++) {
@@ -468,7 +283,6 @@ class NexusHandler {
468
283
  }
469
284
  return sent[sent.length - 1]
470
285
  }
471
-
472
286
  const msg = await this.genContent(jid, { stickerPackMessage: result.stickerPackMessage }, { quoted })
473
287
  await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
474
288
  return msg