@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.
- package/LICENSE +1 -1
- package/README.md +924 -1299
- package/lib/Defaults/baileys-version.json +6 -2
- package/lib/Defaults/index.js +172 -172
- package/lib/Signal/libsignal.js +380 -292
- package/lib/Signal/lid-mapping.js +264 -171
- package/lib/Socket/Client/index.js +2 -2
- package/lib/Socket/Client/types.js +10 -10
- package/lib/Socket/Client/websocket.js +45 -310
- package/lib/Socket/business.js +375 -375
- package/lib/Socket/chats.js +909 -963
- package/lib/Socket/communities.js +430 -430
- package/lib/Socket/groups.js +342 -342
- package/lib/Socket/index.js +22 -22
- package/lib/Socket/messages-recv.js +777 -743
- package/lib/Socket/messages-send.js +295 -305
- package/lib/Socket/mex.js +50 -50
- package/lib/Socket/newsletter.js +148 -148
- package/lib/Socket/nexus-handler.js +75 -261
- package/lib/Socket/socket.js +709 -1201
- package/lib/Store/index.js +5 -5
- package/lib/Store/make-cache-manager-store.js +81 -81
- package/lib/Store/make-in-memory-store.js +416 -416
- package/lib/Store/make-ordered-dictionary.js +81 -81
- package/lib/Store/object-repository.js +30 -30
- package/lib/Types/Auth.js +1 -1
- package/lib/Types/Bussines.js +1 -1
- package/lib/Types/Call.js +1 -1
- package/lib/Types/Chat.js +7 -7
- package/lib/Types/Contact.js +1 -1
- package/lib/Types/Events.js +1 -1
- package/lib/Types/GroupMetadata.js +1 -1
- package/lib/Types/Label.js +24 -24
- package/lib/Types/LabelAssociation.js +6 -6
- package/lib/Types/Message.js +10 -10
- package/lib/Types/Newsletter.js +28 -28
- package/lib/Types/Product.js +1 -1
- package/lib/Types/Signal.js +1 -1
- package/lib/Types/Socket.js +2 -2
- package/lib/Types/State.js +12 -12
- package/lib/Types/USync.js +1 -1
- package/lib/Types/index.js +25 -25
- package/lib/Utils/auth-utils.js +264 -256
- package/lib/Utils/baileys-event-stream.js +55 -55
- package/lib/Utils/browser-utils.js +27 -27
- package/lib/Utils/business.js +228 -230
- package/lib/Utils/chat-utils.js +694 -764
- package/lib/Utils/crypto.js +109 -135
- package/lib/Utils/decode-wa-message.js +310 -314
- package/lib/Utils/event-buffer.js +547 -547
- package/lib/Utils/generics.js +297 -297
- package/lib/Utils/history.js +91 -83
- package/lib/Utils/index.js +21 -20
- package/lib/Utils/key-store.js +17 -0
- package/lib/Utils/link-preview.js +97 -98
- package/lib/Utils/logger.js +2 -2
- package/lib/Utils/lt-hash.js +47 -47
- package/lib/Utils/make-mutex.js +39 -39
- package/lib/Utils/message-retry-manager.js +148 -148
- package/lib/Utils/messages-media.js +534 -534
- package/lib/Utils/messages.js +705 -705
- package/lib/Utils/noise-handler.js +255 -255
- package/lib/Utils/pre-key-manager.js +105 -105
- package/lib/Utils/process-message.js +412 -412
- package/lib/Utils/signal.js +160 -158
- package/lib/Utils/use-multi-file-auth-state.js +120 -120
- package/lib/Utils/validate-connection.js +194 -194
- package/lib/WABinary/constants.js +1300 -1300
- package/lib/WABinary/decode.js +237 -237
- package/lib/WABinary/encode.js +232 -232
- package/lib/WABinary/generic-utils.js +252 -211
- package/lib/WABinary/index.js +5 -5
- package/lib/WABinary/jid-utils.js +279 -95
- package/lib/WABinary/types.js +1 -1
- package/lib/WAM/BinaryInfo.js +9 -9
- package/lib/WAM/constants.js +22852 -22852
- package/lib/WAM/encode.js +149 -149
- package/lib/WAM/index.js +3 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -28
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -53
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -26
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -37
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -28
- package/lib/WAUSync/Protocols/index.js +4 -4
- package/lib/WAUSync/USyncQuery.js +93 -93
- package/lib/WAUSync/USyncUser.js +22 -22
- package/lib/WAUSync/index.js +3 -3
- package/lib/index.js +66 -66
- package/package.json +171 -144
- package/lib/Signal/Group/ciphertext-message.js +0 -12
- package/lib/Signal/Group/group-session-builder.js +0 -30
- package/lib/Signal/Group/group_cipher.js +0 -100
- package/lib/Signal/Group/index.js +0 -12
- package/lib/Signal/Group/keyhelper.js +0 -18
- package/lib/Signal/Group/sender-chain-key.js +0 -26
- package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
- package/lib/Signal/Group/sender-key-message.js +0 -66
- package/lib/Signal/Group/sender-key-name.js +0 -48
- package/lib/Signal/Group/sender-key-record.js +0 -41
- package/lib/Signal/Group/sender-key-state.js +0 -84
- 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
|
-
//
|
|
27
|
+
// ─── TYPE DETECTION ───────────────────────────────────────────────────────
|
|
36
28
|
detectType(content) {
|
|
29
|
+
if (content.carouselMessage || content.carousel) return 'CAROUSEL'
|
|
37
30
|
const types = {
|
|
38
|
-
requestPaymentMessage: 'PAYMENT',
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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, {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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.
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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:
|
|
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
|
-
|
|
347
|
-
|
|
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
|
|
372
|
-
const msg =
|
|
373
|
-
return await this.
|
|
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
|