@nexustechpro/baileys 2.1.2 → 2.1.6
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/README.md +471 -10
- package/lib/Defaults/index.js +0 -4
- package/lib/Signal/libsignal.js +138 -249
- package/lib/Signal/lid-mapping.js +23 -4
- package/lib/Socket/chats.js +1 -0
- package/lib/Socket/messages-recv.js +30 -18
- package/lib/Socket/messages-send.js +57 -37
- package/lib/Socket/newsletter.js +22 -17
- package/lib/Socket/nexus-handler.js +222 -328
- package/lib/Socket/socket.js +27 -18
- package/lib/Utils/browser-utils.js +12 -3
- package/lib/Utils/chat-utils.js +2 -2
- package/lib/Utils/key-store.js +1 -15
- package/lib/Utils/lt-hash.js +2 -43
- package/lib/Utils/messages-media.js +21 -16
- package/lib/Utils/messages.js +4 -2
- package/lib/Utils/process-message.js +234 -215
- package/lib/Utils/tc-token-utils.js +71 -71
- package/lib/Utils/use-multi-file-auth-state.js +211 -63
- package/lib/Utils/validate-connection.js +15 -3
- package/lib/WABinary/constants.js +14 -0
- package/lib/index.js +1 -1
- package/package.json +27 -12
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
import { proto } from '../../WAProto/index.js'
|
|
2
2
|
import axios from 'axios'
|
|
3
3
|
import crypto from 'crypto'
|
|
4
|
+
import { createRequire } from 'module'
|
|
5
|
+
const _require = createRequire(import.meta.url)
|
|
6
|
+
const Prism = _require('prismjs')
|
|
7
|
+
const loadPrismLanguages = _require('prismjs/components/index')
|
|
8
|
+
|
|
9
|
+
let _prismReady = false
|
|
10
|
+
const ensurePrism = () => { if (_prismReady) return; _prismReady = true; try { loadPrismLanguages() } catch { } }
|
|
11
|
+
|
|
12
|
+
const PRISM_TYPE_MAP = {
|
|
13
|
+
keyword: 1, 'keyword control': 1, builtin: 1, important: 1, bold: 1,
|
|
14
|
+
function: 2, 'function-definition': 2, method: 2, 'method-definition': 2,
|
|
15
|
+
string: 3, 'template-string': 3, 'string interpolation': 3, char: 3, regex: 3,
|
|
16
|
+
number: 4, constant: 4, boolean: 4, unit: 4,
|
|
17
|
+
comment: 5, 'block-comment': 5, prolog: 5, doctype: 5,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const flattenPrism = (tokens, out = []) => {
|
|
21
|
+
for (const t of tokens) {
|
|
22
|
+
if (typeof t === 'string') { if (t) out.push({ codeContent: t, highlightType: 0 }); continue }
|
|
23
|
+
const type = PRISM_TYPE_MAP[t.type] ?? 0
|
|
24
|
+
const content = Array.isArray(t.content) ? flattenPrism(t.content, []).map(x => x.codeContent).join('') : String(t.content)
|
|
25
|
+
if (content) out.push({ codeContent: content, highlightType: type })
|
|
26
|
+
}
|
|
27
|
+
return out
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tokenizeCode = (code, language = 'javascript') => {
|
|
31
|
+
ensurePrism()
|
|
32
|
+
const lang = language.toLowerCase()
|
|
33
|
+
const grammar = Prism.languages[lang] ?? Prism.languages.javascript
|
|
34
|
+
const raw = flattenPrism(Prism.tokenize(code, grammar))
|
|
35
|
+
const merged = []
|
|
36
|
+
for (const t of raw) {
|
|
37
|
+
const prev = merged[merged.length - 1]
|
|
38
|
+
if (prev && prev.highlightType === t.highlightType) prev.codeContent += t.codeContent
|
|
39
|
+
else merged.push({ ...t })
|
|
40
|
+
}
|
|
41
|
+
const TYPE_NAMES = ['DEFAULT', 'KEYWORD', 'METHOD', 'STR', 'NUMBER', 'COMMENT']
|
|
42
|
+
return { codeBlocks: merged, unified_codeBlock: merged.map(t => ({ content: t.codeContent, type: TYPE_NAMES[t.highlightType] ?? 'DEFAULT' })) }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const extractHyperlinks = (text) => {
|
|
46
|
+
const hyperlinks = []
|
|
47
|
+
let cleaned = text
|
|
48
|
+
const IE_RE = /\{\{IE_(\d+)\}\}([\s\S]*?)\{\{\/IE_\1\}\}/g
|
|
49
|
+
const URL_RE = /https?:\/\/[^\s\])"]+/g
|
|
50
|
+
let match
|
|
51
|
+
while ((match = IE_RE.exec(text)) !== null) { hyperlinks.push({ key: `IE_${match[1]}`, reference_id: parseInt(match[1]) + 1, text: match[2], url: '' }); cleaned = cleaned.replace(match[0], match[2]) }
|
|
52
|
+
if (!hyperlinks.length) { while ((match = URL_RE.exec(text)) !== null) hyperlinks.push({ key: `IE_${hyperlinks.length}`, reference_id: hyperlinks.length + 1, text: '', url: match[0] }) }
|
|
53
|
+
return { text: cleaned, hyperlinks }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const parseTableArray = (table) => {
|
|
57
|
+
const [title, headerStr, ...rest] = table
|
|
58
|
+
const splitCols = s => typeof s === 'string' ? (s.includes('|') ? s.split('|') : s.split(',')).map(x => x.trim()) : (Array.isArray(s) ? s.map(String) : [])
|
|
59
|
+
const headers = splitCols(headerStr)
|
|
60
|
+
const rows = rest.flatMap(r => typeof r === 'string' ? r.split(';;').map(splitCols) : [Array.isArray(r) ? r.map(String) : [String(r)]])
|
|
61
|
+
const maxLen = Math.max(headers.length, ...rows.map(r => r.length))
|
|
62
|
+
const pad = arr => [...arr, ...Array(maxLen - arr.length).fill('')]
|
|
63
|
+
return { title: String(title ?? ''), rows: [{ items: pad(headers), isHeading: true }, ...rows.map(r => ({ items: pad(r) }))], unified_rows: [{ is_header: true, cells: pad(headers) }, ...rows.map(r => ({ is_header: false, cells: pad(r) }))] }
|
|
64
|
+
}
|
|
4
65
|
|
|
5
66
|
class NexusHandler {
|
|
6
67
|
constructor(utils, waUploadToServer, relayMessageFn, options = {}) {
|
|
@@ -21,12 +82,14 @@ class NexusHandler {
|
|
|
21
82
|
STICKER_PACK: this.handleStickerPack.bind(this),
|
|
22
83
|
GROUP_STATUS: this.handleGroupStory.bind(this),
|
|
23
84
|
CAROUSEL: this.handleCarousel.bind(this),
|
|
24
|
-
CAROUSEL_PROTO: this.handleCarouselProto.bind(this)
|
|
85
|
+
CAROUSEL_PROTO: this.handleCarouselProto.bind(this),
|
|
86
|
+
AI_RICH: this.handleAiRich.bind(this)
|
|
25
87
|
}
|
|
26
88
|
}
|
|
27
89
|
|
|
28
90
|
// ─── TYPE DETECTION ───────────────────────────────────────────────────────
|
|
29
91
|
detectType(content) {
|
|
92
|
+
if (content.aiRich || content.airich || content.richResponse || content.richResponseMessage || content.AIRich) return 'AI_RICH'
|
|
30
93
|
if (content.carouselMessage || content.carousel) return 'CAROUSEL'
|
|
31
94
|
if (content.carouselProto) return 'CAROUSEL_PROTO'
|
|
32
95
|
const map = {
|
|
@@ -56,82 +119,162 @@ class NexusHandler {
|
|
|
56
119
|
}
|
|
57
120
|
|
|
58
121
|
async genMsg(jid, content, opts = {}) {
|
|
59
|
-
return await this.utils.generateWAMessage(jid, content, {
|
|
60
|
-
...opts,
|
|
61
|
-
upload: this.upload,
|
|
62
|
-
userJid: opts.userJid || this.user?.id,
|
|
63
|
-
getUrlInfo: opts.getUrlInfo || this.opts.getUrlInfo,
|
|
64
|
-
logger: opts.logger || this.opts.logger
|
|
65
|
-
})
|
|
122
|
+
return await this.utils.generateWAMessage(jid, content, { ...opts, upload: this.upload, userJid: opts.userJid || this.user?.id, getUrlInfo: opts.getUrlInfo || this.opts.getUrlInfo, logger: opts.logger || this.opts.logger })
|
|
66
123
|
}
|
|
67
124
|
|
|
68
125
|
async genFromContent(jid, content, opts = {}) {
|
|
69
|
-
return await this.utils.generateWAMessageFromContent(jid, content, {
|
|
70
|
-
...opts,
|
|
71
|
-
userJid: opts.userJid || this.user?.id
|
|
72
|
-
})
|
|
126
|
+
return await this.utils.generateWAMessageFromContent(jid, content, { ...opts, userJid: opts.userJid || this.user?.id })
|
|
73
127
|
}
|
|
74
128
|
|
|
75
|
-
async sendMsg(jid, message, opts = {}) {
|
|
76
|
-
return await this.relay(jid, message, opts)
|
|
77
|
-
}
|
|
129
|
+
async sendMsg(jid, message, opts = {}) { return await this.relay(jid, message, opts) }
|
|
78
130
|
|
|
79
|
-
buildCtx(quoted, sender) {
|
|
80
|
-
return {
|
|
81
|
-
stanzaId: quoted?.key?.id,
|
|
82
|
-
participant: quoted?.key?.participant || sender,
|
|
83
|
-
quotedMessage: quoted?.message
|
|
84
|
-
}
|
|
85
|
-
}
|
|
131
|
+
buildCtx(quoted, sender) { return { stanzaId: quoted?.key?.id, participant: quoted?.key?.participant || sender, quotedMessage: quoted?.message } }
|
|
86
132
|
|
|
87
133
|
buildFullCtx(ctx, adReply) {
|
|
88
134
|
const allowed = ['title', 'body', 'mediaType', 'thumbnailUrl', 'mediaUrl', 'sourceUrl', 'showAdAttribution', 'renderLargerThumbnail', 'thumbnail']
|
|
89
135
|
const final = ctx ? { mentionedJid: ctx.mentionedJid || [], forwardingScore: ctx.forwardingScore || 0, isForwarded: ctx.isForwarded || false, ...ctx } : {}
|
|
90
|
-
if (adReply) {
|
|
91
|
-
final.externalAdReply = {}
|
|
92
|
-
for (const k of allowed) if (adReply[k] !== undefined) final.externalAdReply[k] = adReply[k]
|
|
93
|
-
final.externalAdReply = { mediaType: 1, showAdAttribution: false, renderLargerThumbnail: false, ...final.externalAdReply }
|
|
94
|
-
}
|
|
136
|
+
if (adReply) { final.externalAdReply = {}; for (const k of allowed) if (adReply[k] !== undefined) final.externalAdReply[k] = adReply[k]; final.externalAdReply = { mediaType: 1, showAdAttribution: false, renderLargerThumbnail: false, ...final.externalAdReply } }
|
|
95
137
|
return final
|
|
96
138
|
}
|
|
97
139
|
|
|
98
|
-
genJid() {
|
|
99
|
-
const id = this.utils.generateMessageIDV2?.() || this.utils.generateMessageID?.() || crypto.randomBytes(10).toString('hex')
|
|
100
|
-
return id.includes('@') ? id : `${id}@s.whatsapp.net`
|
|
101
|
-
}
|
|
102
|
-
|
|
140
|
+
genJid() { const id = this.utils.generateMessageIDV2?.() || this.utils.generateMessageID?.() || crypto.randomBytes(10).toString('hex'); return id.includes('@') ? id : `${id}@s.whatsapp.net` }
|
|
103
141
|
parseTime(val, def) { return typeof val === 'string' ? parseInt(val) : (val || def) }
|
|
104
142
|
delay(ms) { return new Promise(r => setTimeout(r, ms)) }
|
|
143
|
+
genMsgId() { return this.utils.generateMessageIDV2?.() || crypto.randomBytes(10).toString('hex').toUpperCase() }
|
|
105
144
|
|
|
106
145
|
async downloadBuffer(urlOrBuffer) {
|
|
107
146
|
if (Buffer.isBuffer(urlOrBuffer)) return urlOrBuffer
|
|
108
|
-
if (typeof urlOrBuffer === 'string') {
|
|
109
|
-
try {
|
|
110
|
-
const res = await axios.get(urlOrBuffer, { responseType: 'arraybuffer' })
|
|
111
|
-
return Buffer.from(res.data)
|
|
112
|
-
} catch { this.opts.logger?.warn('Failed to download buffer from URL') }
|
|
113
|
-
}
|
|
147
|
+
if (typeof urlOrBuffer === 'string') { try { const res = await axios.get(urlOrBuffer, { responseType: 'arraybuffer' }); return Buffer.from(res.data) } catch { this.opts.logger?.warn('Failed to download buffer from URL') } }
|
|
114
148
|
return null
|
|
115
149
|
}
|
|
116
150
|
|
|
151
|
+
// ─── AI RICH MESSAGES ─────────────────────────────────────────────────────
|
|
152
|
+
_buildAiRichPayload(data = {}) {
|
|
153
|
+
const submessages = [], sections = [], richResponseSources = []
|
|
154
|
+
|
|
155
|
+
const addText = (text = '') => {
|
|
156
|
+
const str = String(text ?? '')
|
|
157
|
+
const { text: cleaned, hyperlinks } = extractHyperlinks(str)
|
|
158
|
+
submessages.push({ messageType: 2, messageText: cleaned })
|
|
159
|
+
sections.push({ view_model: { primitive: hyperlinks.length ? { text: cleaned, inline_entities: hyperlinks.map(h => ({ key: h.key, metadata: h.text?.trim() ? { display_name: h.text, is_trusted: true, url: h.url, __typename: 'GenAIInlineLinkItem' } : { reference_id: h.reference_id, reference_url: h.url, reference_title: h.url, reference_display_name: h.url, sources: [], __typename: 'GenAISearchCitationItem' } })), __typename: 'GenAIMarkdownTextUXPrimitive' } : { text: str, __typename: 'GenAIMarkdownTextUXPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const addCode = (language = 'javascript', code = '') => {
|
|
163
|
+
const { codeBlocks, unified_codeBlock } = tokenizeCode(String(code ?? ''), language)
|
|
164
|
+
submessages.push({ messageType: 5, codeMetadata: { codeLanguage: language, codeBlocks } })
|
|
165
|
+
sections.push({ view_model: { primitive: { language, code_blocks: unified_codeBlock, __typename: 'GenAICodeUXPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } })
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const addTable = (table = []) => {
|
|
169
|
+
const isRaw = Array.isArray(table[0])
|
|
170
|
+
let meta
|
|
171
|
+
if (isRaw) {
|
|
172
|
+
const headers = table[0].map(String), rows = table.slice(1).map(r => r.map(String))
|
|
173
|
+
const maxLen = Math.max(headers.length, ...rows.map(r => r.length))
|
|
174
|
+
const pad = arr => [...arr, ...Array(maxLen - arr.length).fill('')]
|
|
175
|
+
meta = { title: '', rows: [{ items: pad(headers), isHeading: true }, ...rows.map(r => ({ items: pad(r) }))], unified_rows: [{ is_header: true, cells: pad(headers) }, ...rows.map(r => ({ is_header: false, cells: pad(r) }))] }
|
|
176
|
+
} else { meta = parseTableArray(table) }
|
|
177
|
+
submessages.push({ messageType: 4, tableMetadata: { title: meta.title, rows: meta.rows } })
|
|
178
|
+
sections.push({ view_model: { primitive: { rows: meta.unified_rows, ...(meta.title ? { title: meta.title } : {}), __typename: 'GenATableUXPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const addImages = (images = []) => {
|
|
182
|
+
const list = (Array.isArray(images) ? images : [images]).filter(Boolean).map(item => { const url = typeof item === 'string' ? item : item.url || item.imageUrl || item.imagePreviewUrl; return { imagePreviewUrl: url, imageHighResUrl: item?.imageHighResUrl || item?.highResUrl || url, sourceUrl: item?.sourceUrl || data.sourceUrl || 'https://google.com' } }).filter(x => x.imagePreviewUrl)
|
|
183
|
+
if (!list.length) return
|
|
184
|
+
submessages.push({ messageType: 1, gridImageMetadata: { gridImageUrl: { imagePreviewUrl: list[0].imagePreviewUrl }, imageUrls: list } })
|
|
185
|
+
list.forEach(({ imagePreviewUrl }) => sections.push({ view_model: { primitive: { media: { url: imagePreviewUrl, mime_type: 'image/jpeg' }, imagine_type: 3, status: { status: 'READY' }, __typename: 'GenAIImaginePrimitive' }, __typename: 'GenAISingleLayoutViewModel' } }))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const addSources = (sources = []) => {
|
|
189
|
+
if (!Array.isArray(sources) || !sources.length) return
|
|
190
|
+
sections.push({ view_model: { primitive: { sources: sources.map(s => { const arr = Array.isArray(s) ? s : null; return { source_type: 'THIRD_PARTY', source_display_name: arr ? arr[2] : s.displayName || s.title || s.sourceTitle || 'Source', source_subtitle: s.subtitle || 'AI', source_url: arr ? arr[1] : s.url || s.sourceUrl || '', favicon: { url: arr ? arr[0] : s.profileIconUrl || s.faviconUrl || s.thumbnailUrl || '', mime_type: 'image/jpeg', width: 16, height: 16 } } }), search_engine: data.searchEngine || 'MAME', __typename: 'GenAISearchResultPrimitive' }, __typename: 'GenAISingleLayoutViewModel' } })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const addReels = (reels = []) => {
|
|
194
|
+
if (!Array.isArray(reels) || !reels.length) return
|
|
195
|
+
submessages.push({ messageType: 9, contentItemsMetadata: { contentType: 1, itemsMetadata: reels.map(r => ({ reelItem: { title: r.title || r.creator || '', profileIconUrl: r.profileIconUrl || r.avatar_url || '', thumbnailUrl: r.thumbnailUrl || r.thumbnail_url || '', videoUrl: r.videoUrl || r.reels_url || r.url || '' } })) } })
|
|
196
|
+
reels.forEach((r, idx) => richResponseSources.push({ provider: r.provider || 'UNKNOWN', thumbnailCDNURL: r.thumbnailUrl || '', sourceProviderURL: r.videoUrl || r.url || '', sourceQuery: '', faviconCDNURL: r.profileIconUrl || '', citationNumber: idx + 1, sourceTitle: r.title || r.creator || `Reel ${idx + 1}` }))
|
|
197
|
+
sections.push({ view_model: { primitives: reels.map(r => ({ reels_url: r.videoUrl || r.url || '', thumbnail_url: r.thumbnailUrl || r.thumbnail_url || '', creator: r.title || r.creator || '', avatar_url: r.profileIconUrl || r.avatar_url || '', reels_title: r.reels_title || r.title || '', likes_count: r.likes_count || 0, shares_count: r.shares_count || 0, view_count: r.view_count || 0, reel_source: r.reel_source || 'IG', is_verified: !!(r.is_verified ?? r.isVerified), __typename: 'GenAIReelPrimitive' })), __typename: 'GenAIHScrollLayoutViewModel' } })
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const addLatex = (expressions = [], text = '') => {
|
|
201
|
+
submessages.push({ messageType: 8, latexMetadata: { text, expressions: expressions.map(e => ({ latexExpression: e.expression || e.latexExpression || '', url: e.url || '', width: e.width || 0, height: e.height || 0, ...(e.fontHeight !== undefined ? { fontHeight: e.fontHeight } : {}), ...(e.imageTopPadding !== undefined ? { imageTopPadding: e.imageTopPadding } : {}), ...(e.imageLeadingPadding !== undefined ? { imageLeadingPadding: e.imageLeadingPadding } : {}), ...(e.imageBottomPadding !== undefined ? { imageBottomPadding: e.imageBottomPadding } : {}), ...(e.imageTrailingPadding !== undefined ? { imageTrailingPadding: e.imageTrailingPadding } : {}) })) } })
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// process in declaration order for predictable output
|
|
205
|
+
if (data.header) addText(data.header)
|
|
206
|
+
if (data.text) addText(data.text)
|
|
207
|
+
if (Array.isArray(data.texts)) data.texts.forEach(t => addText(t))
|
|
208
|
+
if (data.code) { typeof data.code === 'string' ? addCode(data.language || 'javascript', data.code) : addCode(data.code.language || data.language || 'javascript', data.code.content || data.code.code || '') }
|
|
209
|
+
if (Array.isArray(data.codes)) data.codes.forEach(c => typeof c === 'string' ? addCode(data.language || 'javascript', c) : addCode(c.language || data.language || 'javascript', c.content || c.code || ''))
|
|
210
|
+
if (data.table) { Array.isArray(data.table[0]) ? addTable(data.table) : Array.isArray(data.table) ? addTable(data.table) : null }
|
|
211
|
+
if (data.headers && data.rows) addTable([[...(data.title ? [data.title] : ['']), data.headers, ...data.rows].flat(0)])
|
|
212
|
+
if (data.image || data.images || data.gridImage) addImages(data.images || data.gridImage || data.image)
|
|
213
|
+
if (data.sources) addSources(data.sources)
|
|
214
|
+
if (data.reels || data.reel) addReels(data.reels || data.reel)
|
|
215
|
+
if (data.latex) addLatex(Array.isArray(data.latex) ? data.latex : [data.latex], data.latexText || '')
|
|
216
|
+
if (Array.isArray(data.parts)) {
|
|
217
|
+
for (const p of data.parts) {
|
|
218
|
+
if (p.type === 'text') addText(p.content)
|
|
219
|
+
else if (p.type === 'code') addCode(p.language || 'javascript', p.content)
|
|
220
|
+
else if (p.type === 'table') { if (Array.isArray(p.table)) addTable(p.table); else if (p.headers && p.rows) addTable([p.title || '', p.headers, ...p.rows]) }
|
|
221
|
+
else if (p.type === 'images') addImages(p.images || p.image)
|
|
222
|
+
else if (p.type === 'sources') addSources(p.sources)
|
|
223
|
+
else if (p.type === 'reels') addReels(p.reels)
|
|
224
|
+
else if (p.type === 'latex') addLatex(Array.isArray(p.expressions) ? p.expressions : [p.expressions], p.text || '')
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (data.footer) addText(data.footer)
|
|
228
|
+
|
|
229
|
+
const forwarded = data.forwarded !== false
|
|
230
|
+
const includesUnifiedResponse = data.includesUnifiedResponse !== false
|
|
231
|
+
const botJid = data.botJid || '259786046210223@bot'
|
|
232
|
+
const allSources = data.richResponseSources || richResponseSources
|
|
233
|
+
const ctxInfo = forwarded ? { forwardingScore: data.forwardingScore || 2, isForwarded: true, forwardedAiBotMessageInfo: { botJid }, forwardOrigin: data.forwardOrigin || 4, botMessageSharingInfo: { botEntryPointOrigin: 1, forwardScore: data.forwardingScore || 2 }, mentionedJid: [], groupMentions: [], ...(data.quoted?.key ? { stanzaId: data.quoted.key.id, participant: data.quoted.key.participant ?? data.quoted.sender ?? data.quoted.key.remoteJid, quotedMessage: data.quoted.message } : {}), ...(data.contextInfo || {}) } : (data.contextInfo || {})
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
messageContextInfo: {
|
|
237
|
+
deviceListMetadata: { senderKeyIndexes: [], recipientKeyIndexes: [], recipientKeyHash: '', recipientTimestamp: Math.floor(Date.now() / 1000) },
|
|
238
|
+
deviceListMetadataVersion: 2,
|
|
239
|
+
messageSecret: crypto.randomBytes(32),
|
|
240
|
+
botMetadata: { messageDisclaimerText: data.disclaimerText || data.messageDisclaimerText || '', pluginMetadata: {}, richResponseSourcesMetadata: { sources: allSources } }
|
|
241
|
+
},
|
|
242
|
+
botForwardedMessage: { message: { richResponseMessage: { messageType: data.messageType || 1, submessages, unifiedResponse: { data: includesUnifiedResponse ? Buffer.from(JSON.stringify({ response_id: data.responseId || crypto.randomUUID(), sections })).toString('base64') : '' }, contextInfo: ctxInfo } } }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async handleAiRich(content, jid, quoted) {
|
|
247
|
+
const raw = content.aiRich || content.airich || content.richResponse || content.richResponseMessage || content.AIRich || content
|
|
248
|
+
const data = { ...raw, quoted: raw.quoted || quoted }
|
|
249
|
+
const message = this._buildAiRichPayload(data)
|
|
250
|
+
const messageId = raw.messageId || this.genMsgId()
|
|
251
|
+
await this.sendMsg(jid, message, { messageId })
|
|
252
|
+
return { message, messageId }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
captureAiRich(msg) {
|
|
256
|
+
const rich = msg?.botForwardedMessage?.message?.richResponseMessage
|
|
257
|
+
if (!rich?.unifiedResponse?.data) return null
|
|
258
|
+
return { submessages: rich.submessages || [], sections: JSON.parse(Buffer.from(rich.unifiedResponse.data, 'base64').toString()), contextInfo: rich.contextInfo || {}, messageType: rich.messageType || 1 }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async relayAiRich(jid, captured, quoted) {
|
|
262
|
+
const data = { responseId: captured.sections?.response_id, forwarded: true, quoted, includesUnifiedResponse: true, messageType: captured.messageType }
|
|
263
|
+
const message = this._buildAiRichPayload(data)
|
|
264
|
+
message.botForwardedMessage.message.richResponseMessage.submessages = captured.submessages
|
|
265
|
+
message.botForwardedMessage.message.richResponseMessage.unifiedResponse.data = Buffer.from(JSON.stringify(captured.sections)).toString('base64')
|
|
266
|
+
const messageId = this.genMsgId()
|
|
267
|
+
await this.sendMsg(jid, message, { messageId })
|
|
268
|
+
return { message, messageId }
|
|
269
|
+
}
|
|
270
|
+
|
|
117
271
|
// ─── PAYMENT ──────────────────────────────────────────────────────────────
|
|
118
272
|
async handlePayment(content, jid, quoted) {
|
|
119
273
|
const d = content.requestPaymentMessage
|
|
120
274
|
const ctx = this.buildCtx(quoted, content.sender)
|
|
121
|
-
const notes = d.sticker?.stickerMessage
|
|
122
|
-
? { stickerMessage: { ...d.sticker.stickerMessage, contextInfo: ctx } }
|
|
123
|
-
: d.note ? { extendedTextMessage: { text: d.note, contextInfo: ctx } } : {}
|
|
275
|
+
const notes = d.sticker?.stickerMessage ? { stickerMessage: { ...d.sticker.stickerMessage, contextInfo: ctx } } : d.note ? { extendedTextMessage: { text: d.note, contextInfo: ctx } } : {}
|
|
124
276
|
const targetJid = jid || content.jid
|
|
125
|
-
const msg = await this.genFromContent(targetJid, {
|
|
126
|
-
requestPaymentMessage: proto.Message.RequestPaymentMessage.fromObject({
|
|
127
|
-
expiryTimestamp: d.expiry || 0,
|
|
128
|
-
amount1000: d.amount || 0,
|
|
129
|
-
currencyCodeIso4217: d.currency || 'IDR',
|
|
130
|
-
requestFrom: d.from || '0@s.whatsapp.net',
|
|
131
|
-
noteMessage: notes,
|
|
132
|
-
background: d.background ?? { id: 'DEFAULT', placeholderArgb: 0xfff0f0f0 }
|
|
133
|
-
})
|
|
134
|
-
}, { quoted })
|
|
277
|
+
const msg = await this.genFromContent(targetJid, { requestPaymentMessage: proto.Message.RequestPaymentMessage.fromObject({ expiryTimestamp: d.expiry || 0, amount1000: d.amount || 0, currencyCodeIso4217: d.currency || 'IDR', requestFrom: d.from || '0@s.whatsapp.net', noteMessage: notes, background: d.background ?? { id: 'DEFAULT', placeholderArgb: 0xfff0f0f0 } }) }, { quoted })
|
|
135
278
|
await this.sendMsg(targetJid, msg.message, { messageId: msg.key.id })
|
|
136
279
|
return msg
|
|
137
280
|
}
|
|
@@ -140,38 +283,9 @@ class NexusHandler {
|
|
|
140
283
|
async handleProduct(content, jid, quoted) {
|
|
141
284
|
const p = content.productMessage || {}
|
|
142
285
|
let prodImg = null
|
|
143
|
-
if (p.thumbnail) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
prodImg = res?.imageMessage || res?.message?.imageMessage
|
|
147
|
-
}
|
|
148
|
-
const product = proto.Message.ProductMessage.ProductSnapshot.create({
|
|
149
|
-
productId: p.productId,
|
|
150
|
-
title: p.title || '',
|
|
151
|
-
description: p.description || '',
|
|
152
|
-
currencyCode: p.currencyCode || 'IDR',
|
|
153
|
-
priceAmount1000: p.priceAmount1000,
|
|
154
|
-
retailerId: p.retailerId,
|
|
155
|
-
url: p.url,
|
|
156
|
-
productImageCount: prodImg ? 1 : 0,
|
|
157
|
-
...(prodImg && { productImage: prodImg })
|
|
158
|
-
})
|
|
159
|
-
const msg = await this.genFromContent(jid, {
|
|
160
|
-
viewOnceMessage: {
|
|
161
|
-
message: {
|
|
162
|
-
interactiveMessage: proto.Message.InteractiveMessage.create({
|
|
163
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: p.body || '' }),
|
|
164
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: p.footer || '' }),
|
|
165
|
-
header: proto.Message.InteractiveMessage.Header.create({
|
|
166
|
-
title: p.title || '',
|
|
167
|
-
hasMediaAttachment: !!prodImg,
|
|
168
|
-
productMessage: proto.Message.ProductMessage.create({ product, businessOwnerJid: '0@s.whatsapp.net' })
|
|
169
|
-
}),
|
|
170
|
-
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: p.buttons || [] })
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}, { quoted })
|
|
286
|
+
if (p.thumbnail) { const src = Buffer.isBuffer(p.thumbnail) ? { image: p.thumbnail } : { image: { url: p.thumbnail.url || p.thumbnail } }; const res = await this.utils.generateWAMessageContent(src, { upload: this.upload }); prodImg = res?.imageMessage || res?.message?.imageMessage }
|
|
287
|
+
const product = proto.Message.ProductMessage.ProductSnapshot.create({ 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 }) })
|
|
288
|
+
const msg = await this.genFromContent(jid, { viewOnceMessage: { message: { interactiveMessage: proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.create({ text: p.body || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: p.footer || '' }), header: proto.Message.InteractiveMessage.Header.create({ title: p.title || '', hasMediaAttachment: !!prodImg, productMessage: proto.Message.ProductMessage.create({ product, businessOwnerJid: '0@s.whatsapp.net' }) }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: p.buttons || [] }) }) } } }, { quoted })
|
|
175
289
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
176
290
|
return msg
|
|
177
291
|
}
|
|
@@ -183,33 +297,17 @@ class NexusHandler {
|
|
|
183
297
|
if (i.thumbnail) media = await this.prepMedia({ url: i.thumbnail }, 'image')
|
|
184
298
|
else if (i.image) media = await this.prepMedia(i.image, 'image')
|
|
185
299
|
else if (i.video) media = await this.prepMedia(i.video, 'video')
|
|
186
|
-
else if (i.document) {
|
|
187
|
-
media = await this.prepMedia(i.document, 'document')
|
|
188
|
-
if (i.jpegThumbnail) media.documentMessage.jpegThumbnail = typeof i.jpegThumbnail === 'object' && i.jpegThumbnail.url ? { url: i.jpegThumbnail.url } : i.jpegThumbnail
|
|
189
|
-
if (i.fileName) media.documentMessage.fileName = i.fileName
|
|
190
|
-
if (i.mimetype) media.documentMessage.mimetype = i.mimetype
|
|
191
|
-
}
|
|
300
|
+
else if (i.document) { media = await this.prepMedia(i.document, 'document'); if (i.jpegThumbnail) media.documentMessage.jpegThumbnail = typeof i.jpegThumbnail === 'object' && i.jpegThumbnail.url ? { url: i.jpegThumbnail.url } : i.jpegThumbnail; if (i.fileName) media.documentMessage.fileName = i.fileName; if (i.mimetype) media.documentMessage.mimetype = i.mimetype }
|
|
192
301
|
const bodyText = i.body?.text || i.title || ''
|
|
193
302
|
const footerText = i.footer?.text || (typeof i.footer === 'string' ? i.footer : '') || ''
|
|
194
303
|
const headerTitle = typeof i.header === 'string' ? i.header : i.header?.title || ''
|
|
195
304
|
let nativeFlow = null
|
|
196
|
-
if (i.buttons?.length || i.nativeFlowMessage) {
|
|
197
|
-
const nfm = i.nativeFlowMessage || {}
|
|
198
|
-
nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.create({
|
|
199
|
-
buttons: i.buttons || nfm.buttons || [],
|
|
200
|
-
messageParamsJson: nfm.messageParamsJson || ''
|
|
201
|
-
})
|
|
202
|
-
}
|
|
305
|
+
if (i.buttons?.length || i.nativeFlowMessage) { const nfm = i.nativeFlowMessage || {}; nativeFlow = proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: i.buttons || nfm.buttons || [], messageParamsJson: nfm.messageParamsJson || '' }) }
|
|
203
306
|
const headerMedia = {}
|
|
204
307
|
if (media?.imageMessage) headerMedia.imageMessage = media.imageMessage
|
|
205
308
|
if (media?.videoMessage) headerMedia.videoMessage = media.videoMessage
|
|
206
309
|
if (media?.documentMessage) headerMedia.documentMessage = media.documentMessage
|
|
207
|
-
const interactive = proto.Message.InteractiveMessage.create({
|
|
208
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: bodyText }),
|
|
209
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: footerText }),
|
|
210
|
-
header: proto.Message.InteractiveMessage.Header.create({ title: headerTitle, hasMediaAttachment: !!media, ...headerMedia }),
|
|
211
|
-
...(nativeFlow && { nativeFlowMessage: nativeFlow })
|
|
212
|
-
})
|
|
310
|
+
const interactive = proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.create({ text: bodyText }), footer: proto.Message.InteractiveMessage.Footer.create({ text: footerText }), header: proto.Message.InteractiveMessage.Header.create({ title: headerTitle, hasMediaAttachment: !!media, ...headerMedia }), ...(nativeFlow && { nativeFlowMessage: nativeFlow }) })
|
|
213
311
|
const ctx = this.buildFullCtx(i.contextInfo, i.externalAdReply)
|
|
214
312
|
if (Object.keys(ctx).length) interactive.contextInfo = ctx
|
|
215
313
|
const msg = await this.genFromContent(jid, { interactiveMessage: interactive }, { quoted })
|
|
@@ -221,51 +319,14 @@ class NexusHandler {
|
|
|
221
319
|
async handleAlbum(content, jid, quoted) {
|
|
222
320
|
const arr = Array.isArray(content.albumMessage) ? content.albumMessage : []
|
|
223
321
|
if (!arr.length) throw new Error('albumMessage must contain media items')
|
|
224
|
-
const album = await this.genFromContent(jid, {
|
|
225
|
-
messageContextInfo: proto.MessageContextInfo.create({ messageSecret: crypto.randomBytes(32) }),
|
|
226
|
-
albumMessage: proto.Message.AlbumMessage.create({
|
|
227
|
-
expectedImageCount: arr.filter(a => a.image).length,
|
|
228
|
-
expectedVideoCount: arr.filter(a => a.video).length
|
|
229
|
-
})
|
|
230
|
-
}, { userJid: this.genJid(), quoted })
|
|
322
|
+
const album = await this.genFromContent(jid, { messageContextInfo: proto.MessageContextInfo.create({ messageSecret: crypto.randomBytes(32) }), albumMessage: proto.Message.AlbumMessage.create({ expectedImageCount: arr.filter(a => a.image).length, expectedVideoCount: arr.filter(a => a.video).length }) }, { userJid: this.genJid(), quoted })
|
|
231
323
|
await this.sendMsg(jid, album.message, { messageId: album.key.id })
|
|
232
324
|
for (const item of arr) {
|
|
233
325
|
const img = await this.genMsg(jid, item, {})
|
|
234
|
-
img.message.messageContextInfo = proto.MessageContextInfo.create({
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
remoteJid: 'status@broadcast',
|
|
239
|
-
forwardingScore: 99999,
|
|
240
|
-
isForwarded: true,
|
|
241
|
-
mentionedJid: [jid],
|
|
242
|
-
starred: true,
|
|
243
|
-
labels: ['Y', 'Important'],
|
|
244
|
-
isHighlighted: true,
|
|
245
|
-
businessMessageForwardInfo: proto.ContextInfo.BusinessMessageForwardInfo.create({ businessOwnerJid: jid }),
|
|
246
|
-
dataSharingContext: proto.ContextInfo.DataSharingContext.create({ showMmDisclosure: true })
|
|
247
|
-
})
|
|
248
|
-
img.message.forwardedNewsletterMessageInfo = proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
249
|
-
newsletterJid: '0@newsletter',
|
|
250
|
-
serverMessageId: 1,
|
|
251
|
-
newsletterName: 'WhatsApp',
|
|
252
|
-
contentType: 'UPDATE_CARD',
|
|
253
|
-
timestamp: new Date().toISOString(),
|
|
254
|
-
senderName: 'Nexus',
|
|
255
|
-
priority: 'high',
|
|
256
|
-
status: 'sent'
|
|
257
|
-
})
|
|
258
|
-
img.message.disappearingMode = proto.DisappearingMode.create({
|
|
259
|
-
initiator: 3, trigger: 4, initiatorDeviceJid: jid,
|
|
260
|
-
initiatedByExternalService: true, initiatedByUserDevice: true,
|
|
261
|
-
initiatedBySystem: true, initiatedByServer: true,
|
|
262
|
-
initiatedByAdmin: true, initiatedByUser: true,
|
|
263
|
-
initiatedByApp: true, initiatedByBot: true, initiatedByMe: true
|
|
264
|
-
})
|
|
265
|
-
await this.sendMsg(jid, img.message, {
|
|
266
|
-
messageId: img.key.id,
|
|
267
|
-
quoted: { key: { ...album.key, fromMe: true, participant: this.genJid() }, message: album.message }
|
|
268
|
-
})
|
|
326
|
+
img.message.messageContextInfo = proto.MessageContextInfo.create({ messageSecret: crypto.randomBytes(32), messageAssociation: proto.MessageAssociation.create({ 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: proto.ContextInfo.BusinessMessageForwardInfo.create({ businessOwnerJid: jid }), dataSharingContext: proto.ContextInfo.DataSharingContext.create({ showMmDisclosure: true }) })
|
|
327
|
+
img.message.forwardedNewsletterMessageInfo = proto.ContextInfo.ForwardedNewsletterMessageInfo.create({ newsletterJid: '0@newsletter', serverMessageId: 1, newsletterName: 'WhatsApp', contentType: 'UPDATE_CARD', timestamp: new Date().toISOString(), senderName: 'Nexus', priority: 'high', status: 'sent' })
|
|
328
|
+
img.message.disappearingMode = proto.DisappearingMode.create({ initiator: 3, trigger: 4, initiatorDeviceJid: jid, initiatedByExternalService: true, initiatedByUserDevice: true, initiatedBySystem: true, initiatedByServer: true, initiatedByAdmin: true, initiatedByUser: true, initiatedByApp: true, initiatedByBot: true, initiatedByMe: true })
|
|
329
|
+
await this.sendMsg(jid, img.message, { messageId: img.key.id, quoted: { key: { ...album.key, fromMe: true, participant: this.genJid() }, message: album.message } })
|
|
269
330
|
}
|
|
270
331
|
return album
|
|
271
332
|
}
|
|
@@ -273,34 +334,7 @@ class NexusHandler {
|
|
|
273
334
|
// ─── EVENT ────────────────────────────────────────────────────────────────
|
|
274
335
|
async handleEvent(content, jid, quoted) {
|
|
275
336
|
const e = content.eventMessage
|
|
276
|
-
const msg = await this.genFromContent(jid, {
|
|
277
|
-
messageContextInfo: proto.MessageContextInfo.create({
|
|
278
|
-
deviceListMetadata: {},
|
|
279
|
-
deviceListMetadataVersion: 2,
|
|
280
|
-
messageSecret: crypto.randomBytes(32),
|
|
281
|
-
supportPayload: JSON.stringify({ version: 2, is_ai_message: true, should_show_system_message: true, ticket_id: crypto.randomBytes(16).toString('hex') })
|
|
282
|
-
}),
|
|
283
|
-
eventMessage: proto.Message.EventMessage.create({
|
|
284
|
-
contextInfo: proto.ContextInfo.create({
|
|
285
|
-
mentionedJid: [jid],
|
|
286
|
-
participant: jid,
|
|
287
|
-
remoteJid: 'status@broadcast',
|
|
288
|
-
forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
289
|
-
newsletterName: 'Nexus Events',
|
|
290
|
-
newsletterJid: '120363422827915475@newsletter',
|
|
291
|
-
serverMessageId: 1
|
|
292
|
-
})
|
|
293
|
-
}),
|
|
294
|
-
isCanceled: e.isCanceled || false,
|
|
295
|
-
name: e.name,
|
|
296
|
-
description: e.description,
|
|
297
|
-
location: e.location || { degreesLatitude: 0, degreesLongitude: 0, name: 'Location' },
|
|
298
|
-
joinLink: e.joinLink || '',
|
|
299
|
-
startTime: this.parseTime(e.startTime, Date.now()),
|
|
300
|
-
endTime: this.parseTime(e.endTime, Date.now() + 3600000),
|
|
301
|
-
extraGuestsAllowed: e.extraGuestsAllowed !== false
|
|
302
|
-
})
|
|
303
|
-
}, { quoted })
|
|
337
|
+
const msg = await this.genFromContent(jid, { messageContextInfo: proto.MessageContextInfo.create({ 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: proto.Message.EventMessage.create({ contextInfo: proto.ContextInfo.create({ mentionedJid: [jid], participant: jid, remoteJid: 'status@broadcast', forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({ 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 }) }, { quoted })
|
|
304
338
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
305
339
|
return msg
|
|
306
340
|
}
|
|
@@ -308,25 +342,7 @@ class NexusHandler {
|
|
|
308
342
|
// ─── POLL RESULT ──────────────────────────────────────────────────────────
|
|
309
343
|
async handlePollResult(content, jid, quoted) {
|
|
310
344
|
const p = content.pollResultMessage
|
|
311
|
-
const msg = await this.genFromContent(jid, {
|
|
312
|
-
pollResultSnapshotMessage: proto.Message.PollResultSnapshotMessage.create({
|
|
313
|
-
name: p.name,
|
|
314
|
-
pollVotes: (p.pollVotes || []).map(v => proto.Message.PollResultSnapshotMessage.PollVote.create({
|
|
315
|
-
optionName: v.optionName,
|
|
316
|
-
optionVoteCount: typeof v.optionVoteCount === 'number' ? v.optionVoteCount.toString() : v.optionVoteCount
|
|
317
|
-
})),
|
|
318
|
-
contextInfo: proto.ContextInfo.create({
|
|
319
|
-
isForwarded: true,
|
|
320
|
-
forwardingScore: 1,
|
|
321
|
-
forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({
|
|
322
|
-
newsletterName: p.newsletter?.newsletterName || 'Newsletter',
|
|
323
|
-
newsletterJid: p.newsletter?.newsletterJid || '120363399602691477@newsletter',
|
|
324
|
-
serverMessageId: 1000,
|
|
325
|
-
contentType: 'UPDATE'
|
|
326
|
-
})
|
|
327
|
-
})
|
|
328
|
-
})
|
|
329
|
-
}, { userJid: this.genJid(), quoted })
|
|
345
|
+
const msg = await this.genFromContent(jid, { pollResultSnapshotMessage: proto.Message.PollResultSnapshotMessage.create({ name: p.name, pollVotes: (p.pollVotes || []).map(v => proto.Message.PollResultSnapshotMessage.PollVote.create({ optionName: v.optionName, optionVoteCount: typeof v.optionVoteCount === 'number' ? v.optionVoteCount.toString() : v.optionVoteCount })), contextInfo: proto.ContextInfo.create({ isForwarded: true, forwardingScore: 1, forwardedNewsletterMessageInfo: proto.ContextInfo.ForwardedNewsletterMessageInfo.create({ newsletterName: p.newsletter?.newsletterName || 'Newsletter', newsletterJid: p.newsletter?.newsletterJid || '120363399602691477@newsletter', serverMessageId: 1000, contentType: 'UPDATE' }) }) }) }, { userJid: this.genJid(), quoted })
|
|
330
346
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
331
347
|
return msg
|
|
332
348
|
}
|
|
@@ -336,23 +352,8 @@ class NexusHandler {
|
|
|
336
352
|
const d = content.statusMentionMessage
|
|
337
353
|
const mediaType = d.image ? 'image' : 'video'
|
|
338
354
|
const media = await this.prepMedia(d.image || d.video, mediaType)
|
|
339
|
-
const statusMsg = await this.relay('status@broadcast', { ...media }, {
|
|
340
|
-
|
|
341
|
-
additionalNodes: [{
|
|
342
|
-
tag: 'meta', attrs: {},
|
|
343
|
-
content: [{ tag: 'mentioned_users', attrs: {}, content: [{ tag: 'to', attrs: { jid: d.mentions }, content: undefined }] }]
|
|
344
|
-
}]
|
|
345
|
-
})
|
|
346
|
-
const mentionMsg = await this.genFromContent(jid, {
|
|
347
|
-
statusMentionMessage: proto.Message.StatusMentionMessage.create({
|
|
348
|
-
message: {
|
|
349
|
-
protocolMessage: proto.Message.ProtocolMessage.create({
|
|
350
|
-
messageId: statusMsg?.key?.id || d.mentions,
|
|
351
|
-
type: proto.Message.ProtocolMessage.Type.STATUS_MENTION_MESSAGE
|
|
352
|
-
})
|
|
353
|
-
}
|
|
354
|
-
})
|
|
355
|
-
}, { additionalNodes: [{ tag: 'meta', attrs: { is_status_mention: 'true' }, content: undefined }] })
|
|
355
|
+
const statusMsg = await this.relay('status@broadcast', { ...media }, { statusJidList: [d.mentions, this.user?.id].filter(Boolean), additionalNodes: [{ tag: 'meta', attrs: {}, content: [{ tag: 'mentioned_users', attrs: {}, content: [{ tag: 'to', attrs: { jid: d.mentions }, content: undefined }] }] }] })
|
|
356
|
+
const mentionMsg = await this.genFromContent(jid, { statusMentionMessage: proto.Message.StatusMentionMessage.create({ message: { protocolMessage: proto.Message.ProtocolMessage.create({ messageId: statusMsg?.key?.id || d.mentions, type: proto.Message.ProtocolMessage.Type.STATUS_MENTION_MESSAGE }) } }) }, { additionalNodes: [{ tag: 'meta', attrs: { is_status_mention: 'true' }, content: undefined }] })
|
|
356
357
|
await this.sendMsg(jid, mentionMsg.message, { messageId: mentionMsg.key.id })
|
|
357
358
|
return mentionMsg
|
|
358
359
|
}
|
|
@@ -361,28 +362,9 @@ class NexusHandler {
|
|
|
361
362
|
async handleOrderMessage(content, jid, quoted) {
|
|
362
363
|
const o = content.orderMessage
|
|
363
364
|
const thumb = await this.downloadBuffer(o.thumbnail)
|
|
364
|
-
const cleanJid = (id)
|
|
365
|
-
if (!id) return null
|
|
366
|
-
const [user] = id.split(':')
|
|
367
|
-
return user.includes('@') ? user : `${user}@s.whatsapp.net`
|
|
368
|
-
}
|
|
365
|
+
const cleanJid = id => { if (!id) return null; const [user] = id.split(':'); return user.includes('@') ? user : `${user}@s.whatsapp.net` }
|
|
369
366
|
const seller = cleanJid(o.sellerJid) || cleanJid(this.user?.id) || cleanJid(jid) || '0@s.whatsapp.net'
|
|
370
|
-
const msg = await this.genFromContent(jid, {
|
|
371
|
-
orderMessage: proto.Message.OrderMessage.create({
|
|
372
|
-
orderId: o.orderId || '7NEXUS25022008',
|
|
373
|
-
thumbnail: thumb,
|
|
374
|
-
itemCount: o.itemCount || 0,
|
|
375
|
-
status: 2, // ACCEPTED
|
|
376
|
-
surface: 1, // CATALOG
|
|
377
|
-
message: o.message,
|
|
378
|
-
orderTitle: o.orderTitle,
|
|
379
|
-
sellerJid: seller,
|
|
380
|
-
token: o.token || 'NEXUS_EXAMPLE_TOKEN',
|
|
381
|
-
totalAmount1000: o.totalAmount1000 || 0,
|
|
382
|
-
totalCurrencyCode: o.totalCurrencyCode || 'IDR',
|
|
383
|
-
messageVersion: 2
|
|
384
|
-
})
|
|
385
|
-
}, { quoted })
|
|
367
|
+
const msg = await this.genFromContent(jid, { orderMessage: proto.Message.OrderMessage.create({ orderId: o.orderId || '7NEXUS25022008', thumbnail: thumb, itemCount: o.itemCount || 0, status: 2, surface: 1, message: o.message, orderTitle: o.orderTitle, sellerJid: seller, token: o.token || 'NEXUS_EXAMPLE_TOKEN', totalAmount1000: o.totalAmount1000 || 0, totalCurrencyCode: o.totalCurrencyCode || 'IDR', messageVersion: 2 }) }, { quoted })
|
|
386
368
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
387
369
|
return msg
|
|
388
370
|
}
|
|
@@ -390,112 +372,35 @@ class NexusHandler {
|
|
|
390
372
|
// ─── GROUP STATUS ─────────────────────────────────────────────────────────
|
|
391
373
|
async handleGroupStory(content, jid, quoted) {
|
|
392
374
|
const s = content.groupStatus
|
|
393
|
-
const mediaContent = await this.utils.generateWAMessageContent(s, {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
logger: this.opts.logger
|
|
397
|
-
})
|
|
398
|
-
const msg = await this.genFromContent(jid, {
|
|
399
|
-
groupStatusMessageV2: proto.Message.FutureProofMessage.create({ message: proto.Message.fromObject(mediaContent) })
|
|
400
|
-
}, { userJid: jid })
|
|
401
|
-
return await this.sendMsg(jid, msg.message, {
|
|
402
|
-
messageId: msg.key.id,
|
|
403
|
-
additionalNodes: [{ tag: 'meta', attrs: { is_group_status: 'true' }, content: undefined }]
|
|
404
|
-
})
|
|
375
|
+
const mediaContent = await this.utils.generateWAMessageContent(s, { upload: this.upload, getUrlInfo: this.opts.getUrlInfo, logger: this.opts.logger })
|
|
376
|
+
const msg = await this.genFromContent(jid, { groupStatusMessageV2: proto.Message.FutureProofMessage.create({ message: proto.Message.fromObject(mediaContent) }) }, { userJid: jid })
|
|
377
|
+
return await this.sendMsg(jid, msg.message, { messageId: msg.key.id, additionalNodes: [{ tag: 'meta', attrs: { is_group_status: 'true' }, content: undefined }] })
|
|
405
378
|
}
|
|
406
379
|
|
|
407
380
|
// ─── CAROUSEL ─────────────────────────────────────────────────────────────
|
|
408
381
|
async handleCarousel(content, jid, quoted) {
|
|
409
382
|
const c = content.carouselMessage || content.carousel || {}
|
|
410
383
|
const cards = await Promise.all((c.cards || []).map(card => this.buildCard(card)))
|
|
411
|
-
const msg = await this.genFromContent(jid, {
|
|
412
|
-
viewOnceMessage: {
|
|
413
|
-
message: {
|
|
414
|
-
interactiveMessage: proto.Message.InteractiveMessage.create({
|
|
415
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: c.caption || c.body || '' }),
|
|
416
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }),
|
|
417
|
-
carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards, messageVersion: 1 })
|
|
418
|
-
})
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}, { quoted })
|
|
384
|
+
const msg = await this.genFromContent(jid, { viewOnceMessage: { message: { interactiveMessage: proto.Message.InteractiveMessage.create({ body: proto.Message.InteractiveMessage.Body.create({ text: c.caption || c.body || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }), carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards, messageVersion: 1 }) }) } } }, { quoted })
|
|
422
385
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
423
386
|
return msg
|
|
424
387
|
}
|
|
425
388
|
|
|
426
389
|
async buildCard(card) {
|
|
427
|
-
const buttons = (card.buttons || []).map(btn => ({
|
|
428
|
-
name: btn.name,
|
|
429
|
-
buttonParamsJson: JSON.stringify(btn.params || {})
|
|
430
|
-
}))
|
|
390
|
+
const buttons = (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) }))
|
|
431
391
|
if (card.productTitle) {
|
|
432
392
|
const imgMedia = await this.prepMedia({ url: card.imageUrl }, 'image')
|
|
433
|
-
return {
|
|
434
|
-
header: proto.Message.InteractiveMessage.Header.create({
|
|
435
|
-
title: card.headerTitle || '',
|
|
436
|
-
subtitle: card.headerSubtitle || '',
|
|
437
|
-
hasMediaAttachment: false,
|
|
438
|
-
productMessage: proto.Message.ProductMessage.create({
|
|
439
|
-
product: proto.Message.ProductMessage.ProductSnapshot.create({
|
|
440
|
-
productImage: imgMedia?.imageMessage,
|
|
441
|
-
productId: card.productId || '123456',
|
|
442
|
-
title: card.productTitle,
|
|
443
|
-
description: card.productDescription || '',
|
|
444
|
-
currencyCode: card.currencyCode || 'IDR',
|
|
445
|
-
priceAmount1000: card.priceAmount1000 || '100000',
|
|
446
|
-
retailerId: card.retailerId || 'Retailer',
|
|
447
|
-
url: card.url || '',
|
|
448
|
-
productImageCount: 1
|
|
449
|
-
}),
|
|
450
|
-
businessOwnerJid: card.businessOwnerJid || '0@s.whatsapp.net'
|
|
451
|
-
})
|
|
452
|
-
}),
|
|
453
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }),
|
|
454
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }),
|
|
455
|
-
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons })
|
|
456
|
-
}
|
|
393
|
+
return { header: proto.Message.InteractiveMessage.Header.create({ title: card.headerTitle || '', subtitle: card.headerSubtitle || '', hasMediaAttachment: false, productMessage: proto.Message.ProductMessage.create({ product: proto.Message.ProductMessage.ProductSnapshot.create({ productImage: imgMedia?.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' }) }), body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons }) }
|
|
457
394
|
}
|
|
458
395
|
const imgMedia = card.imageUrl ? await this.prepMedia({ url: card.imageUrl }, 'image') : {}
|
|
459
|
-
return {
|
|
460
|
-
header: proto.Message.InteractiveMessage.Header.create({
|
|
461
|
-
title: card.headerTitle || '',
|
|
462
|
-
subtitle: card.headerSubtitle || '',
|
|
463
|
-
hasMediaAttachment: !!card.imageUrl,
|
|
464
|
-
...imgMedia
|
|
465
|
-
}),
|
|
466
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }),
|
|
467
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }),
|
|
468
|
-
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons })
|
|
469
|
-
}
|
|
396
|
+
return { header: proto.Message.InteractiveMessage.Header.create({ title: card.headerTitle || '', subtitle: card.headerSubtitle || '', hasMediaAttachment: !!card.imageUrl, ...imgMedia }), body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons }) }
|
|
470
397
|
}
|
|
471
398
|
|
|
472
399
|
// ─── CAROUSEL PROTO ───────────────────────────────────────────────────────
|
|
473
400
|
async handleCarouselProto(content, jid, quoted) {
|
|
474
401
|
const c = content.carouselProto
|
|
475
|
-
const cards = await Promise.all((c.cards || []).map(async card => ({
|
|
476
|
-
|
|
477
|
-
title: card.title?.substring(0, 60) || '',
|
|
478
|
-
subtitle: card.subtitle || '',
|
|
479
|
-
hasMediaAttachment: false
|
|
480
|
-
}),
|
|
481
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }),
|
|
482
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }),
|
|
483
|
-
nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
|
|
484
|
-
buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) }))
|
|
485
|
-
})
|
|
486
|
-
})))
|
|
487
|
-
const msg = await this.genFromContent(jid, {
|
|
488
|
-
viewOnceMessage: {
|
|
489
|
-
message: {
|
|
490
|
-
messageContextInfo: proto.MessageContextInfo.create({ deviceListMetadata: {}, deviceListMetadataVersion: 2 }),
|
|
491
|
-
interactiveMessage: proto.Message.InteractiveMessage.create({
|
|
492
|
-
body: proto.Message.InteractiveMessage.Body.create({ text: c.body || '' }),
|
|
493
|
-
footer: proto.Message.InteractiveMessage.Footer.create({ text: c.footer || '' }),
|
|
494
|
-
carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.create({ cards, messageVersion: 1 })
|
|
495
|
-
})
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}, { quoted })
|
|
402
|
+
const cards = await Promise.all((c.cards || []).map(async card => ({ header: proto.Message.InteractiveMessage.Header.create({ title: card.title?.substring(0, 60) || '', subtitle: card.subtitle || '', hasMediaAttachment: false }), body: proto.Message.InteractiveMessage.Body.create({ text: card.bodyText || '' }), footer: proto.Message.InteractiveMessage.Footer.create({ text: card.footerText || '' }), nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({ buttons: (card.buttons || []).map(btn => ({ name: btn.name, buttonParamsJson: JSON.stringify(btn.params || {}) })) }) })))
|
|
403
|
+
const msg = await this.genFromContent(jid, { viewOnceMessage: { message: { messageContextInfo: proto.MessageContextInfo.create({ 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, messageVersion: 1 }) }) } } }, { quoted })
|
|
499
404
|
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
500
405
|
return msg
|
|
501
406
|
}
|
|
@@ -503,21 +408,10 @@ class NexusHandler {
|
|
|
503
408
|
// ─── STICKER PACK ─────────────────────────────────────────────────────────
|
|
504
409
|
async handleStickerPack(content, jid, quoted) {
|
|
505
410
|
const stickerPack = content.stickerPack
|
|
506
|
-
const result = await this.utils.prepareStickerPackMessage(stickerPack, {
|
|
507
|
-
logger: this.opts?.logger,
|
|
508
|
-
upload: this.upload,
|
|
509
|
-
mediaCache: this.opts?.mediaCache,
|
|
510
|
-
options: this.opts,
|
|
511
|
-
mediaUploadTimeoutMs: this.opts?.mediaUploadTimeoutMs
|
|
512
|
-
})
|
|
411
|
+
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 })
|
|
513
412
|
if (result.isBatched) {
|
|
514
413
|
const sent = []
|
|
515
|
-
for (let i = 0; i < result.stickerPackMessage.length; i++) {
|
|
516
|
-
const msg = await this.genFromContent(jid, { stickerPackMessage: result.stickerPackMessage[i] }, { quoted })
|
|
517
|
-
await this.sendMsg(jid, msg.message, { messageId: msg.key.id })
|
|
518
|
-
sent.push(msg)
|
|
519
|
-
if (i < result.stickerPackMessage.length - 1) await this.delay(2000)
|
|
520
|
-
}
|
|
414
|
+
for (let i = 0; i < result.stickerPackMessage.length; i++) { const msg = await this.genFromContent(jid, { stickerPackMessage: result.stickerPackMessage[i] }, { quoted }); await this.sendMsg(jid, msg.message, { messageId: msg.key.id }); sent.push(msg); if (i < result.stickerPackMessage.length - 1) await this.delay(2000) }
|
|
521
415
|
return sent[sent.length - 1]
|
|
522
416
|
}
|
|
523
417
|
const msg = await this.genFromContent(jid, { stickerPackMessage: result.stickerPackMessage }, { quoted })
|