@nexustechpro/baileys 1.0.1

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 (101) hide show
  1. package/README.md +1573 -0
  2. package/WAProto/fix-imports.js +29 -0
  3. package/WAProto/index.js +169659 -0
  4. package/lib/Defaults/index.js +115 -0
  5. package/lib/Signal/Group/ciphertext-message.js +12 -0
  6. package/lib/Signal/Group/group-session-builder.js +30 -0
  7. package/lib/Signal/Group/group_cipher.js +82 -0
  8. package/lib/Signal/Group/index.js +12 -0
  9. package/lib/Signal/Group/keyhelper.js +18 -0
  10. package/lib/Signal/Group/sender-chain-key.js +26 -0
  11. package/lib/Signal/Group/sender-key-distribution-message.js +63 -0
  12. package/lib/Signal/Group/sender-key-message.js +66 -0
  13. package/lib/Signal/Group/sender-key-name.js +48 -0
  14. package/lib/Signal/Group/sender-key-record.js +41 -0
  15. package/lib/Signal/Group/sender-key-state.js +84 -0
  16. package/lib/Signal/Group/sender-message-key.js +26 -0
  17. package/lib/Signal/libsignal.js +342 -0
  18. package/lib/Signal/lid-mapping.js +171 -0
  19. package/lib/Socket/Client/index.js +3 -0
  20. package/lib/Socket/Client/types.js +11 -0
  21. package/lib/Socket/Client/websocket.js +91 -0
  22. package/lib/Socket/business.js +376 -0
  23. package/lib/Socket/chats.js +963 -0
  24. package/lib/Socket/communities.js +431 -0
  25. package/lib/Socket/groups.js +328 -0
  26. package/lib/Socket/index.js +19 -0
  27. package/lib/Socket/messages-recv.js +1240 -0
  28. package/lib/Socket/messages-send.js +1370 -0
  29. package/lib/Socket/mex.js +42 -0
  30. package/lib/Socket/newsletter.js +202 -0
  31. package/lib/Socket/nexus-handler.js +667 -0
  32. package/lib/Socket/socket.js +871 -0
  33. package/lib/Store/index.js +4 -0
  34. package/lib/Store/make-cache-manager-store.js +81 -0
  35. package/lib/Store/make-in-memory-store.js +416 -0
  36. package/lib/Store/make-ordered-dictionary.js +82 -0
  37. package/lib/Store/object-repository.js +31 -0
  38. package/lib/Types/Auth.js +2 -0
  39. package/lib/Types/Bussines.js +2 -0
  40. package/lib/Types/Call.js +2 -0
  41. package/lib/Types/Chat.js +8 -0
  42. package/lib/Types/Contact.js +2 -0
  43. package/lib/Types/Events.js +2 -0
  44. package/lib/Types/GroupMetadata.js +2 -0
  45. package/lib/Types/Label.js +25 -0
  46. package/lib/Types/LabelAssociation.js +7 -0
  47. package/lib/Types/Message.js +11 -0
  48. package/lib/Types/Newsletter.js +31 -0
  49. package/lib/Types/Product.js +2 -0
  50. package/lib/Types/Signal.js +2 -0
  51. package/lib/Types/Socket.js +3 -0
  52. package/lib/Types/State.js +13 -0
  53. package/lib/Types/USync.js +2 -0
  54. package/lib/Types/index.js +26 -0
  55. package/lib/Utils/auth-utils.js +257 -0
  56. package/lib/Utils/baileys-event-stream.js +56 -0
  57. package/lib/Utils/browser-utils.js +28 -0
  58. package/lib/Utils/business.js +231 -0
  59. package/lib/Utils/chat-utils.js +763 -0
  60. package/lib/Utils/crypto.js +142 -0
  61. package/lib/Utils/decode-wa-message.js +279 -0
  62. package/lib/Utils/event-buffer.js +548 -0
  63. package/lib/Utils/generics.js +381 -0
  64. package/lib/Utils/history.js +84 -0
  65. package/lib/Utils/index.js +20 -0
  66. package/lib/Utils/link-preview.js +85 -0
  67. package/lib/Utils/logger.js +3 -0
  68. package/lib/Utils/lt-hash.js +48 -0
  69. package/lib/Utils/make-mutex.js +40 -0
  70. package/lib/Utils/message-retry-manager.js +149 -0
  71. package/lib/Utils/messages-media.js +685 -0
  72. package/lib/Utils/messages.js +820 -0
  73. package/lib/Utils/noise-handler.js +147 -0
  74. package/lib/Utils/pre-key-manager.js +106 -0
  75. package/lib/Utils/process-message.js +413 -0
  76. package/lib/Utils/signal.js +159 -0
  77. package/lib/Utils/use-multi-file-auth-state.js +121 -0
  78. package/lib/Utils/validate-connection.js +195 -0
  79. package/lib/WABinary/constants.js +1301 -0
  80. package/lib/WABinary/decode.js +238 -0
  81. package/lib/WABinary/encode.js +216 -0
  82. package/lib/WABinary/generic-utils.js +111 -0
  83. package/lib/WABinary/index.js +6 -0
  84. package/lib/WABinary/jid-utils.js +96 -0
  85. package/lib/WABinary/types.js +2 -0
  86. package/lib/WAM/BinaryInfo.js +10 -0
  87. package/lib/WAM/constants.js +22853 -0
  88. package/lib/WAM/encode.js +150 -0
  89. package/lib/WAM/index.js +4 -0
  90. package/lib/WAUSync/Protocols/USyncContactProtocol.js +29 -0
  91. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +54 -0
  92. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +27 -0
  93. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -0
  94. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +51 -0
  95. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +29 -0
  96. package/lib/WAUSync/Protocols/index.js +5 -0
  97. package/lib/WAUSync/USyncQuery.js +94 -0
  98. package/lib/WAUSync/USyncUser.js +23 -0
  99. package/lib/WAUSync/index.js +4 -0
  100. package/lib/index.js +24 -0
  101. package/package.json +113 -0
@@ -0,0 +1,667 @@
1
+ /**
2
+ * @nexus/baileys - Advanced Message Handler
3
+ * Combines WhiskeySockets foundation with Kelvdra's enhanced message types
4
+ *
5
+ * Supported message types:
6
+ * - PAYMENT (Request Payment)
7
+ * - PRODUCT (Product Messages)
8
+ * - INTERACTIVE (Buttons, Lists, Native Flows)
9
+ * - ALBUM (Photo Albums)
10
+ * - EVENT (Events/Invitations)
11
+ * - POLL_RESULT (Poll Results)
12
+ * - STATUS_MENTION (Status Mentions)
13
+ * - ORDER (Order Messages)
14
+ * - GROUP_STATUS (Group Stories)
15
+ * - CAROUSEL (Product Carousels)
16
+ */
17
+
18
+ import axios from 'axios'
19
+ import crypto from 'crypto'
20
+
21
+ class NexusHandler {
22
+ constructor(utils, waUploadToServer, relayMessageFn) {
23
+ this.utils = utils
24
+ this.relayMessage = relayMessageFn
25
+ this.waUploadToServer = waUploadToServer
26
+ }
27
+
28
+ /**
29
+ * Detect message type from content
30
+ */
31
+ detectType(content) {
32
+ if (content.requestPaymentMessage) return 'PAYMENT'
33
+ if (content.productMessage) return 'PRODUCT'
34
+ if (content.interactiveMessage) return 'INTERACTIVE'
35
+ if (content.albumMessage) return 'ALBUM'
36
+ if (content.eventMessage) return 'EVENT'
37
+ if (content.pollResultMessage) return 'POLL_RESULT'
38
+ if (content.statusMentionMessage) return 'STATUS_MENTION'
39
+ if (content.orderMessage) return 'ORDER'
40
+ if (content.groupStatus) return 'GROUP_STATUS'
41
+ if (content.carouselMessage || content.carousel) return 'CAROUSEL'
42
+ return null
43
+ }
44
+
45
+ /**
46
+ * Handle Payment Messages
47
+ */
48
+ async handlePayment(content, quoted) {
49
+ const data = content.requestPaymentMessage
50
+ let notes = {}
51
+
52
+ if (data.sticker?.stickerMessage) {
53
+ notes = {
54
+ stickerMessage: {
55
+ ...data.sticker.stickerMessage,
56
+ contextInfo: {
57
+ stanzaId: quoted?.key?.id,
58
+ participant: quoted?.key?.participant || content.sender,
59
+ quotedMessage: quoted?.message
60
+ }
61
+ }
62
+ }
63
+ } else if (data.note) {
64
+ notes = {
65
+ extendedTextMessage: {
66
+ text: data.note,
67
+ contextInfo: {
68
+ stanzaId: quoted?.key?.id,
69
+ participant: quoted?.key?.participant || content.sender,
70
+ quotedMessage: quoted?.message
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ const WAProto = this.utils.WAProto?.proto
77
+ return {
78
+ requestPaymentMessage: WAProto.Message.RequestPaymentMessage.fromObject({
79
+ expiryTimestamp: data.expiry || 0,
80
+ amount1000: data.amount || 0,
81
+ currencyCodeIso4217: data.currency || 'IDR',
82
+ requestFrom: data.from || '0@s.whatsapp.net',
83
+ noteMessage: notes,
84
+ background: data.background ?? {
85
+ id: 'DEFAULT',
86
+ placeholderArgb: 0xfff0f0f0
87
+ }
88
+ })
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Handle Product Messages
94
+ */
95
+ async handleProduct(content, jid, quoted) {
96
+ const {
97
+ title = '',
98
+ description = '',
99
+ thumbnail,
100
+ productId,
101
+ retailerId,
102
+ url,
103
+ body = '',
104
+ footer = '',
105
+ buttons = [],
106
+ priceAmount1000 = null,
107
+ currencyCode = 'IDR'
108
+ } = content.productMessage || {}
109
+
110
+ let productImage = null
111
+ if (thumbnail) {
112
+ try {
113
+ if (Buffer.isBuffer(thumbnail)) {
114
+ const res = await this.utils.generateWAMessageContent(
115
+ { image: thumbnail },
116
+ { upload: this.waUploadToServer }
117
+ )
118
+ productImage = res?.imageMessage || res?.message?.imageMessage || null
119
+ } else if (typeof thumbnail === 'object' && thumbnail.url) {
120
+ const res = await this.utils.generateWAMessageContent(
121
+ { image: { url: thumbnail.url } },
122
+ { upload: this.waUploadToServer }
123
+ )
124
+ productImage = res?.imageMessage || res?.message?.imageMessage || null
125
+ }
126
+ } catch (e) {
127
+ console.error('Error processing product thumbnail:', e)
128
+ }
129
+ }
130
+
131
+ const product = {
132
+ productId,
133
+ title,
134
+ description,
135
+ currencyCode,
136
+ priceAmount1000,
137
+ retailerId,
138
+ url,
139
+ productImageCount: productImage ? 1 : 0
140
+ }
141
+ if (productImage) product.productImage = productImage
142
+
143
+ return {
144
+ viewOnceMessage: {
145
+ message: {
146
+ interactiveMessage: {
147
+ body: { text: body },
148
+ footer: { text: footer },
149
+ header: {
150
+ title,
151
+ hasMediaAttachment: !!productImage,
152
+ productMessage: {
153
+ product,
154
+ businessOwnerJid: '0@s.whatsapp.net'
155
+ }
156
+ },
157
+ nativeFlowMessage: { buttons }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Handle Interactive Messages (Buttons, Lists, Native Flows)
166
+ */
167
+ async handleInteractive(content, jid, quoted) {
168
+ const {
169
+ title,
170
+ footer,
171
+ thumbnail,
172
+ image,
173
+ video,
174
+ document,
175
+ mimetype,
176
+ fileName,
177
+ jpegThumbnail,
178
+ contextInfo,
179
+ externalAdReply,
180
+ buttons = [],
181
+ nativeFlowMessage,
182
+ header
183
+ } = content.interactiveMessage || {}
184
+
185
+ let media = null
186
+
187
+ if (thumbnail) {
188
+ media = await this.utils.prepareWAMessageMedia(
189
+ { image: { url: thumbnail } },
190
+ { upload: this.waUploadToServer }
191
+ )
192
+ } else if (image) {
193
+ media = await this.utils.prepareWAMessageMedia(
194
+ typeof image === 'object' && image.url ? { image: { url: image.url } } : { image },
195
+ { upload: this.waUploadToServer }
196
+ )
197
+ } else if (video) {
198
+ media = await this.utils.prepareWAMessageMedia(
199
+ typeof video === 'object' && video.url ? { video: { url: video.url } } : { video },
200
+ { upload: this.waUploadToServer }
201
+ )
202
+ } else if (document) {
203
+ const docPayload = { document }
204
+ if (jpegThumbnail) {
205
+ docPayload.jpegThumbnail =
206
+ typeof jpegThumbnail === 'object' && jpegThumbnail.url
207
+ ? { url: jpegThumbnail.url }
208
+ : jpegThumbnail
209
+ }
210
+ media = await this.utils.prepareWAMessageMedia(docPayload, { upload: this.waUploadToServer })
211
+ if (fileName) media.documentMessage.fileName = fileName
212
+ if (mimetype) media.documentMessage.mimetype = mimetype
213
+ }
214
+
215
+ const interactiveMessage = {
216
+ body: { text: title || '' },
217
+ footer: { text: footer || '' }
218
+ }
219
+
220
+ if (buttons.length > 0) {
221
+ interactiveMessage.nativeFlowMessage = { buttons }
222
+ if (nativeFlowMessage) Object.assign(interactiveMessage.nativeFlowMessage, nativeFlowMessage)
223
+ } else if (nativeFlowMessage) {
224
+ interactiveMessage.nativeFlowMessage = nativeFlowMessage
225
+ }
226
+
227
+ if (media) {
228
+ const headerMedia = {}
229
+ if (media.imageMessage) headerMedia.imageMessage = media.imageMessage
230
+ if (media.videoMessage) headerMedia.videoMessage = media.videoMessage
231
+ if (media.documentMessage) headerMedia.documentMessage = media.documentMessage
232
+ interactiveMessage.header = { title: header || '', hasMediaAttachment: true, ...headerMedia }
233
+ } else {
234
+ interactiveMessage.header = { title: header || '', hasMediaAttachment: false }
235
+ }
236
+
237
+ let finalContextInfo = {}
238
+ if (contextInfo) {
239
+ finalContextInfo = {
240
+ mentionedJid: contextInfo.mentionedJid || [],
241
+ forwardingScore: contextInfo.forwardingScore || 0,
242
+ isForwarded: contextInfo.isForwarded || false,
243
+ ...contextInfo
244
+ }
245
+ }
246
+
247
+ if (externalAdReply) {
248
+ finalContextInfo.externalAdReply = {
249
+ title: externalAdReply.title || '',
250
+ body: externalAdReply.body || '',
251
+ mediaType: externalAdReply.mediaType || 1,
252
+ thumbnailUrl: externalAdReply.thumbnailUrl || '',
253
+ mediaUrl: externalAdReply.mediaUrl || '',
254
+ sourceUrl: externalAdReply.sourceUrl || '',
255
+ showAdAttribution: externalAdReply.showAdAttribution || false,
256
+ renderLargerThumbnail: externalAdReply.renderLargerThumbnail || false,
257
+ ...externalAdReply
258
+ }
259
+ }
260
+
261
+ if (Object.keys(finalContextInfo).length > 0) {
262
+ interactiveMessage.contextInfo = finalContextInfo
263
+ }
264
+
265
+ return { interactiveMessage }
266
+ }
267
+
268
+ /**
269
+ * Handle Album Messages
270
+ */
271
+ async handleAlbum(content, jid, quoted) {
272
+ const array = Array.isArray(content.albumMessage) ? content.albumMessage : []
273
+
274
+ if (array.length === 0) {
275
+ throw new Error('albumMessage must be an array with media items')
276
+ }
277
+
278
+ const album = await this.utils.generateWAMessageFromContent(
279
+ jid,
280
+ {
281
+ messageContextInfo: { messageSecret: crypto.randomBytes(32) },
282
+ albumMessage: {
283
+ expectedImageCount: array.filter(a => a.image).length,
284
+ expectedVideoCount: array.filter(a => a.video).length
285
+ }
286
+ },
287
+ {
288
+ userJid: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net',
289
+ quoted,
290
+ upload: this.waUploadToServer
291
+ }
292
+ )
293
+
294
+ await this.relayMessage(jid, album.message, { messageId: album.key.id })
295
+
296
+ for (const item of array) {
297
+ const img = await this.utils.generateWAMessage(jid, item, { upload: this.waUploadToServer })
298
+ img.message.messageContextInfo = {
299
+ messageSecret: crypto.randomBytes(32),
300
+ messageAssociation: {
301
+ associationType: 1,
302
+ parentMessageKey: album.key
303
+ },
304
+ participant: '0@s.whatsapp.net',
305
+ remoteJid: 'status@broadcast',
306
+ forwardingScore: 99999,
307
+ isForwarded: true,
308
+ mentionedJid: [jid],
309
+ starred: true,
310
+ labels: ['Y', 'Important'],
311
+ isHighlighted: true,
312
+ businessMessageForwardInfo: { businessOwnerJid: jid },
313
+ dataSharingContext: { showMmDisclosure: true }
314
+ }
315
+
316
+ await this.relayMessage(jid, img.message, {
317
+ messageId: img.key.id,
318
+ quoted: {
319
+ key: {
320
+ remoteJid: album.key.remoteJid,
321
+ id: album.key.id,
322
+ fromMe: true,
323
+ participant: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net'
324
+ },
325
+ message: album.message
326
+ }
327
+ })
328
+ }
329
+
330
+ return album
331
+ }
332
+
333
+ /**
334
+ * Handle Event Messages
335
+ */
336
+ async handleEvent(content, jid, quoted) {
337
+ const eventData = content.eventMessage
338
+
339
+ const msg = await this.utils.generateWAMessageFromContent(
340
+ jid,
341
+ {
342
+ viewOnceMessage: {
343
+ message: {
344
+ messageContextInfo: {
345
+ deviceListMetadata: {},
346
+ deviceListMetadataVersion: 2,
347
+ messageSecret: crypto.randomBytes(32)
348
+ },
349
+ eventMessage: {
350
+ isCanceled: eventData.isCanceled || false,
351
+ name: eventData.name,
352
+ description: eventData.description,
353
+ location: eventData.location || {
354
+ degreesLatitude: 0,
355
+ degreesLongitude: 0,
356
+ name: 'Location'
357
+ },
358
+ joinLink: eventData.joinLink || '',
359
+ startTime:
360
+ typeof eventData.startTime === 'string'
361
+ ? parseInt(eventData.startTime)
362
+ : eventData.startTime || Date.now(),
363
+ endTime:
364
+ typeof eventData.endTime === 'string'
365
+ ? parseInt(eventData.endTime)
366
+ : eventData.endTime || Date.now() + 3600000,
367
+ extraGuestsAllowed: eventData.extraGuestsAllowed !== false
368
+ }
369
+ }
370
+ }
371
+ },
372
+ { quoted }
373
+ )
374
+
375
+ await this.relayMessage(jid, msg.message, { messageId: msg.key.id })
376
+ return msg
377
+ }
378
+
379
+ /**
380
+ * Handle Poll Results
381
+ */
382
+ async handlePollResult(content, jid, quoted) {
383
+ const pollData = content.pollResultMessage
384
+
385
+ const msg = await this.utils.generateWAMessageFromContent(
386
+ jid,
387
+ {
388
+ pollResultSnapshotMessage: {
389
+ name: pollData.name,
390
+ pollVotes: (pollData.pollVotes || []).map(vote => ({
391
+ optionName: vote.optionName,
392
+ optionVoteCount:
393
+ typeof vote.optionVoteCount === 'number'
394
+ ? vote.optionVoteCount.toString()
395
+ : vote.optionVoteCount
396
+ }))
397
+ }
398
+ },
399
+ {
400
+ userJid: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net',
401
+ quoted
402
+ }
403
+ )
404
+
405
+ await this.relayMessage(jid, msg.message, { messageId: msg.key.id })
406
+ return msg
407
+ }
408
+
409
+ /**
410
+ * Handle Status Mentions
411
+ */
412
+ async handleStMention(content, jid, quoted) {
413
+ const data = content.statusMentionMessage
414
+
415
+ let media = null
416
+
417
+ if (data.image) {
418
+ media = await this.utils.prepareWAMessageMedia(
419
+ typeof data.image === 'object' && data.image.url
420
+ ? { image: { url: data.image.url } }
421
+ : { image: data.image },
422
+ { upload: this.waUploadToServer }
423
+ )
424
+ } else if (data.video) {
425
+ media = await this.utils.prepareWAMessageMedia(
426
+ typeof data.video === 'object' && data.video.url
427
+ ? { video: { url: data.video.url } }
428
+ : { video: data.video },
429
+ { upload: this.waUploadToServer }
430
+ )
431
+ }
432
+
433
+ const msg = await this.relayMessage('status@broadcast', { ...media }, {
434
+ statusJidList: [data.mentions, this.user?.id],
435
+ additionalNodes: [
436
+ {
437
+ tag: 'meta',
438
+ attrs: {},
439
+ content: [
440
+ {
441
+ tag: 'mentioned_users',
442
+ attrs: {},
443
+ content: [
444
+ {
445
+ tag: 'to',
446
+ attrs: { jid: data.mentions },
447
+ content: undefined
448
+ }
449
+ ]
450
+ }
451
+ ]
452
+ }
453
+ ]
454
+ })
455
+
456
+ const xontols = await this.utils.generateWAMessageFromContent(
457
+ jid,
458
+ {
459
+ statusMentionMessage: {
460
+ message: {
461
+ protocolMessage: {
462
+ messageId: msg.key,
463
+ type: 'STATUS_MENTION_MESSAGE'
464
+ }
465
+ }
466
+ }
467
+ },
468
+ {
469
+ additionalNodes: [
470
+ {
471
+ tag: 'meta',
472
+ attrs: { is_status_mention: true },
473
+ content: undefined
474
+ }
475
+ ]
476
+ }
477
+ )
478
+
479
+ await this.relayMessage(jid, xontols.message, { messageId: xontols.key.id })
480
+ return xontols
481
+ }
482
+
483
+ /**
484
+ * Handle Order Messages
485
+ */
486
+ async handleOrderMessage(content, jid, quoted) {
487
+ const orderData = content.orderMessage
488
+
489
+ let thumbnail = null
490
+ if (orderData.thumbnail) {
491
+ if (Buffer.isBuffer(orderData.thumbnail)) {
492
+ thumbnail = orderData.thumbnail
493
+ } else if (typeof orderData.thumbnail === 'string') {
494
+ try {
495
+ const res = await axios.get(orderData.thumbnail, { responseType: 'arraybuffer' })
496
+ thumbnail = Buffer.from(res.data)
497
+ } catch (e) {
498
+ console.error('Error downloading thumbnail:', e)
499
+ thumbnail = null
500
+ }
501
+ }
502
+ }
503
+
504
+ const msg = await this.utils.generateWAMessageFromContent(
505
+ jid,
506
+ {
507
+ orderMessage: {
508
+ orderId: '7NEXUS25022008',
509
+ thumbnail,
510
+ itemCount: orderData.itemCount || 0,
511
+ status: 'ACCEPTED',
512
+ surface: 'CATALOG',
513
+ message: orderData.message,
514
+ orderTitle: orderData.orderTitle,
515
+ sellerJid: '0@whatsapp.net',
516
+ token: 'NEXUS_EXAMPLE_TOKEN',
517
+ totalAmount1000: orderData.totalAmount1000 || 0,
518
+ totalCurrencyCode: orderData.totalCurrencyCode || 'IDR',
519
+ messageVersion: 2
520
+ }
521
+ },
522
+ { quoted }
523
+ )
524
+
525
+ await this.relayMessage(jid, msg.message, {})
526
+ return msg
527
+ }
528
+
529
+ /**
530
+ * Handle Group Stories
531
+ */
532
+ async handleGroupStory(content, jid, quoted) {
533
+ const storyData = content.groupStatus
534
+ let messageContent
535
+
536
+ if (storyData.message) {
537
+ messageContent = storyData
538
+ } else {
539
+ try {
540
+ messageContent = await this.utils.generateWAMessageContent(storyData, {
541
+ upload: this.waUploadToServer
542
+ })
543
+ } catch (e) {
544
+ console.error('Error generating group story content:', e)
545
+ messageContent = { text: 'Group story' }
546
+ }
547
+ }
548
+
549
+ const msg = {
550
+ message: {
551
+ groupStatusMessageV2: {
552
+ message: messageContent.message || messageContent
553
+ }
554
+ }
555
+ }
556
+
557
+ return await this.relayMessage(jid, msg.message, { messageId: this.utils.generateMessageID() })
558
+ }
559
+
560
+ /**
561
+ * Handle Carousel Messages (Product/Image Carousels)
562
+ */
563
+ async handleCarousel(content, jid, quoted) {
564
+ // Support carouselMessage (native) & carousel (wrapper)
565
+ const root = content.carouselMessage || content.carousel || {}
566
+ const { caption = '', footer = '', cards = [] } = root
567
+
568
+ const carouselCards = await Promise.all(
569
+ cards.map(async (card) => {
570
+ if (card.productTitle) {
571
+ // Mode Product
572
+ return {
573
+ header: {
574
+ title: card.headerTitle || '',
575
+ subtitle: card.headerSubtitle || '',
576
+ productMessage: {
577
+ product: {
578
+ productImage: (
579
+ await this.utils.prepareWAMessageMedia(
580
+ { image: { url: card.imageUrl } },
581
+ { upload: this.waUploadToServer }
582
+ )
583
+ ).imageMessage,
584
+ productId: card.productId || '123456',
585
+ title: card.productTitle,
586
+ description: card.productDescription || '',
587
+ currencyCode: card.currencyCode || 'IDR',
588
+ priceAmount1000: card.priceAmount1000 || '100000',
589
+ retailerId: card.retailerId || 'Retailer',
590
+ url: card.url || '',
591
+ productImageCount: 1
592
+ },
593
+ businessOwnerJid: card.businessOwnerJid || '0@s.whatsapp.net'
594
+ },
595
+ hasMediaAttachment: false
596
+ },
597
+ body: {
598
+ text: card.bodyText || ''
599
+ },
600
+ footer: {
601
+ text: card.footerText || ''
602
+ },
603
+ nativeFlowMessage: {
604
+ buttons: (card.buttons || []).map((btn) => ({
605
+ name: btn.name,
606
+ buttonParamsJson: JSON.stringify(btn.params || {})
607
+ }))
608
+ }
609
+ }
610
+ } else {
611
+ // Mode Image
612
+ const imageMedia = card.imageUrl
613
+ ? await this.utils.prepareWAMessageMedia(
614
+ { image: { url: card.imageUrl } },
615
+ { upload: this.waUploadToServer }
616
+ )
617
+ : {}
618
+
619
+ return {
620
+ header: {
621
+ title: card.headerTitle || '',
622
+ subtitle: card.headerSubtitle || '',
623
+ hasMediaAttachment: !!card.imageUrl,
624
+ ...imageMedia
625
+ },
626
+ body: {
627
+ text: card.bodyText || ''
628
+ },
629
+ footer: {
630
+ text: card.footerText || ''
631
+ },
632
+ nativeFlowMessage: {
633
+ buttons: (card.buttons || []).map((btn) => ({
634
+ name: btn.name,
635
+ buttonParamsJson: JSON.stringify(btn.params || {})
636
+ }))
637
+ }
638
+ }
639
+ }
640
+ })
641
+ )
642
+
643
+ const msg = await this.utils.generateWAMessageFromContent(
644
+ jid,
645
+ {
646
+ viewOnceMessage: {
647
+ message: {
648
+ interactiveMessage: {
649
+ body: { text: caption },
650
+ footer: { text: footer },
651
+ carouselMessage: {
652
+ cards: carouselCards,
653
+ messageVersion: 1
654
+ }
655
+ }
656
+ }
657
+ }
658
+ },
659
+ { quoted }
660
+ )
661
+
662
+ await this.relayMessage(jid, msg.message, { messageId: msg.key.id })
663
+ return msg
664
+ }
665
+ }
666
+
667
+ export default NexusHandler