@awangxcode/qiro-baileys 7.0.0-rc19 → 7.0.0-rc21
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/QIRO-BAILEYS-RC20.md +14 -0
- package/QIRO-BAILEYS-RC21.md +19 -0
- package/README.md +17 -2
- package/lib/Socket/business.d.ts +1 -1
- package/lib/Socket/chats.d.ts +1 -1
- package/lib/Socket/communities.d.ts +1 -1
- package/lib/Socket/groups.d.ts +1 -1
- package/lib/Socket/index.d.ts +1 -1
- package/lib/Socket/messages-recv.d.ts +1 -1
- package/lib/Socket/messages-send.d.ts +1 -1
- package/lib/Socket/newsletter.d.ts +1 -1
- package/lib/Socket/socket.d.ts +1 -1
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +7 -2
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Utils/terminal-welcome.d.ts.map +1 -1
- package/lib/Utils/terminal-welcome.js +2 -1
- package/lib/Utils/terminal-welcome.js.map +1 -1
- package/lib/extensions/awang-MessageBuilder.js +3257 -0
- package/lib/extensions/awang-ai-rich.js +658 -0
- package/lib/extensions/awang-carousel.js +275 -0
- package/package.json +7 -3
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
/*
|
|
2
|
+
⌬═════════════════════⌬
|
|
3
|
+
┇
|
|
4
|
+
┇ ▬▬▬▬▬▬▬▬ ஐஐ ▬▬▬▬▬▬▬▬
|
|
5
|
+
┇ ----- 🥳 𝗪𝗘𝗟𝗖𝗢𝗠𝗘 𝗕𝗥𝗢𝗞 🙌 -----
|
|
6
|
+
┇ ▬▬▬▬▬▬▬▬ ஐஐ ▬▬▬▬▬▬▬▬
|
|
7
|
+
┇
|
|
8
|
+
┇
|
|
9
|
+
┇ █▀█ █ █▀█ █▀█ ▄▀█ █
|
|
10
|
+
┇ █ █ █ █▀▄ █▄█ █▀█ █
|
|
11
|
+
┇ ▀█ --𝗤--𝗜--𝗥--𝗢------𝗔--𝗜----
|
|
12
|
+
┇
|
|
13
|
+
┇ ❏ ───────────────── ❏
|
|
14
|
+
┇ ✧・゚ 💬 𝗜𝗡𝗙𝗢𝗥𝗠𝗔𝗧𝗜𝗢𝗡 💬 ゚・✧
|
|
15
|
+
┇ ❏ ───────────────── ❏
|
|
16
|
+
┇ › ୨୧ Developer : AXCODE iD - AwangXcode
|
|
17
|
+
┇ › ୨୧ Website : awangofc.my.id
|
|
18
|
+
┇ › ୨୧ WhatsApp : wa.me/556184127506
|
|
19
|
+
┇
|
|
20
|
+
┇ ✦ ───────────────── ✦
|
|
21
|
+
┇ ✧・゚ ⛔ 𝗗𝗜𝗟𝗔𝗥𝗔𝗡𝗚 𝗞𝗘𝗥𝗔𝗦 ⛔ ゚・✧
|
|
22
|
+
┇ ✦ ───────────────── ✦
|
|
23
|
+
┇ › ✎ Dilarang Hapus Creadit SC
|
|
24
|
+
┇ › ✎ Dilarang Jual-Beli Script Ini
|
|
25
|
+
┇ › ✎ Dilarang Ngaku Sebagai Dev Qiro
|
|
26
|
+
┇ › ✎ Jangan Ganti Baileys ya Brok!!
|
|
27
|
+
┇
|
|
28
|
+
┇ ♡ ───────────────── ♡
|
|
29
|
+
┇ ✧・゚ 🌸 𝗜𝗭𝗜𝗡 & 𝗦𝗬𝗔𝗥𝗔𝗧 🌸 ゚・✧
|
|
30
|
+
┇ ♡ ───────────────── ♡
|
|
31
|
+
┇ › ○ Bebas Kembangin dan Recode SC
|
|
32
|
+
┇ › ○ Bebas Modif dan Tambah Fitur
|
|
33
|
+
┇ › ○ Bebas Share Ke Teman / Group
|
|
34
|
+
┇ › ○ Wajib Izin & Cantumin Creadit
|
|
35
|
+
┇ › ○ Hargai Karya Orang Anjing!!!!
|
|
36
|
+
┇
|
|
37
|
+
┇ ⚠ ───────────────── ⚠
|
|
38
|
+
┇ ✧・゚ 📢 𝗖𝗔𝗧𝗔𝗧𝗔𝗡 & 𝗟𝗔𝗣𝗢𝗥𝗔𝗡 📢 ゚・✧
|
|
39
|
+
┇ ⚠ ───────────────── ⚠
|
|
40
|
+
┇ › Jika menemukan yang melanggar
|
|
41
|
+
┇ › aturan di atas, segera laporkan
|
|
42
|
+
┇ › via WhatsApp yang tertera
|
|
43
|
+
┇
|
|
44
|
+
┇ › sertakan bukti Screenshot / Link
|
|
45
|
+
┇ › agar cepat kami tindak lanjuti!!
|
|
46
|
+
┇ ╺╺╺╺╺╺╺╺╺╺╺╺╺╺╺✧˖°.
|
|
47
|
+
┇
|
|
48
|
+
┇ ║▌│█║▌│ █║▌│█│║▌
|
|
49
|
+
┇ ♡ 𝗤𝗜𝗥𝗢 - 𝗔𝗜 | 𝟮𝟬𝟮𝟲 ♡
|
|
50
|
+
┇
|
|
51
|
+
⌬═════════════════════⌬
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
import crypto from 'node:crypto'
|
|
55
|
+
|
|
56
|
+
const TYPE_MAP = Object.freeze({
|
|
57
|
+
0: 'DEFAULT',
|
|
58
|
+
1: 'KEYWORD',
|
|
59
|
+
2: 'METHOD',
|
|
60
|
+
3: 'STR',
|
|
61
|
+
4: 'NUMBER',
|
|
62
|
+
5: 'COMMENT',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const KEYWORDS = {
|
|
66
|
+
javascript: new Set('break case catch continue debugger delete do else finally for function if in instanceof new return switch this throw try typeof var void while with true false null undefined class const let super extends export import yield static constructor async await get set'.split(/\s+/)),
|
|
67
|
+
typescript: new Set('abstract any as asserts bigint boolean declare enum implements infer interface is keyof module namespace never readonly require number object override private protected public satisfies string symbol type unknown using from break case catch continue do else finally for function if new return switch this throw try var void while class const let extends import export async await'.split(/\s+/)),
|
|
68
|
+
python: new Set('False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield'.split(/\s+/)),
|
|
69
|
+
java: new Set('abstract assert boolean break byte case char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while'.split(/\s+/)),
|
|
70
|
+
golang: new Set('break case chan const continue default defer else fallthrough for func go goto if import interface map package range return select struct switch type var'.split(/\s+/)),
|
|
71
|
+
c: new Set('auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while'.split(/\s+/)),
|
|
72
|
+
cpp: new Set('alignas alignof and auto bool break case catch class const constexpr continue delete do double else enum explicit export extern false float for friend if inline int long mutable namespace new noexcept nullptr operator private protected public return short signed sizeof static struct switch template this throw true try typedef typename union unsigned using virtual void while'.split(/\s+/)),
|
|
73
|
+
php: new Set('abstract and array as break callable case catch class clone const continue declare default do echo else elseif empty enddeclare endfor endforeach endif endswitch endwhile extends final finally fn for foreach function global goto implements include include_once instanceof interface match namespace new null or private protected public require require_once return static switch throw trait try use var while yield'.split(/\s+/)),
|
|
74
|
+
rust: new Set('as break const continue crate else enum extern false fn for if impl in let loop match mod move mut pub ref return self Self static struct super trait true type unsafe use where while'.split(/\s+/)),
|
|
75
|
+
html: new Set('html head body div span p a img video audio script style link meta form input button table tr td th ul ol li section article header footer nav main'.split(/\s+/)),
|
|
76
|
+
bash: new Set('if then else elif fi for while do done case esac function in select until break continue return export readonly local declare'.split(/\s+/)),
|
|
77
|
+
markdown: new Set('# ## ### #### ##### ######'.split(/\s+/)),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeLanguage(language = 'text') {
|
|
81
|
+
const value = String(language || 'text').trim().toLowerCase()
|
|
82
|
+
const aliases = {
|
|
83
|
+
js: 'javascript',
|
|
84
|
+
jsx: 'javascript',
|
|
85
|
+
ts: 'typescript',
|
|
86
|
+
py: 'python',
|
|
87
|
+
go: 'golang',
|
|
88
|
+
golang: 'golang',
|
|
89
|
+
cxx: 'cpp',
|
|
90
|
+
h: 'c',
|
|
91
|
+
hpp: 'cpp',
|
|
92
|
+
sh: 'bash',
|
|
93
|
+
shell: 'bash',
|
|
94
|
+
txt: 'text',
|
|
95
|
+
plaintext: 'text',
|
|
96
|
+
}
|
|
97
|
+
return aliases[value] || value || 'text'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function tokenize(code, language = 'javascript') {
|
|
101
|
+
const lang = normalizeLanguage(language)
|
|
102
|
+
if (lang === 'text') {
|
|
103
|
+
return {
|
|
104
|
+
codeBlock: [{ codeContent: code, highlightType: 0 }],
|
|
105
|
+
unifiedCodeBlock: [{ content: code, type: 'DEFAULT' }],
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const keywords = KEYWORDS[lang] || new Set()
|
|
110
|
+
const tokens = []
|
|
111
|
+
const push = (content, type) => {
|
|
112
|
+
if (!content) return
|
|
113
|
+
const last = tokens[tokens.length - 1]
|
|
114
|
+
if (last && last.highlightType === type) {
|
|
115
|
+
last.codeContent += content
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
tokens.push({ codeContent: content, highlightType: type })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const identifierPattern = lang === 'css'
|
|
122
|
+
? /[a-zA-Z0-9_$-]/
|
|
123
|
+
: lang === 'html'
|
|
124
|
+
? /[a-zA-Z0-9_$:-]/
|
|
125
|
+
: /[a-zA-Z0-9_$]/
|
|
126
|
+
|
|
127
|
+
let i = 0
|
|
128
|
+
while (i < code.length) {
|
|
129
|
+
const c = code[i]
|
|
130
|
+
|
|
131
|
+
if (/\s/.test(c)) {
|
|
132
|
+
const start = i
|
|
133
|
+
while (i < code.length && /\s/.test(code[i])) i++
|
|
134
|
+
push(code.slice(start, i), 0)
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if ((c === '/' && code[i + 1] === '/') || (c === '#' && ['python', 'bash'].includes(lang))) {
|
|
139
|
+
const start = i
|
|
140
|
+
while (i < code.length && code[i] !== '\n') i++
|
|
141
|
+
push(code.slice(start, i), 5)
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (c === '/' && code[i + 1] === '*') {
|
|
146
|
+
const start = i
|
|
147
|
+
i += 2
|
|
148
|
+
while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) i++
|
|
149
|
+
if (i < code.length) i += 2
|
|
150
|
+
push(code.slice(start, i), 5)
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (c === '"' || c === "'" || c === '`') {
|
|
155
|
+
const start = i
|
|
156
|
+
const quote = c
|
|
157
|
+
i++
|
|
158
|
+
while (i < code.length) {
|
|
159
|
+
if (code[i] === '\\' && i + 1 < code.length) {
|
|
160
|
+
i += 2
|
|
161
|
+
} else if (code[i] === quote) {
|
|
162
|
+
i++
|
|
163
|
+
break
|
|
164
|
+
} else {
|
|
165
|
+
i++
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
push(code.slice(start, i), 3)
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (/[0-9]/.test(c)) {
|
|
173
|
+
const start = i
|
|
174
|
+
while (i < code.length && /[0-9._]/.test(code[i])) i++
|
|
175
|
+
push(code.slice(start, i), 4)
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (/[a-zA-Z_$]/.test(c)) {
|
|
180
|
+
const start = i
|
|
181
|
+
while (i < code.length && identifierPattern.test(code[i])) i++
|
|
182
|
+
const word = code.slice(start, i)
|
|
183
|
+
let type = 0
|
|
184
|
+
|
|
185
|
+
if (keywords.has(word)) {
|
|
186
|
+
type = 1
|
|
187
|
+
} else if (lang === 'css') {
|
|
188
|
+
let j = i
|
|
189
|
+
while (j < code.length && /\s/.test(code[j])) j++
|
|
190
|
+
if (code[j] === ':') type = 1
|
|
191
|
+
} else if (lang === 'html') {
|
|
192
|
+
let j = start - 1
|
|
193
|
+
while (j >= 0 && /\s/.test(code[j])) j--
|
|
194
|
+
if (code[j] === '<' || (code[j] === '/' && code[j - 1] === '<')) type = 1
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (type === 0) {
|
|
198
|
+
let j = i
|
|
199
|
+
while (j < code.length && /\s/.test(code[j])) j++
|
|
200
|
+
if (code[j] === '(') type = 2
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
push(word, type)
|
|
204
|
+
continue
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
push(c, 0)
|
|
208
|
+
i++
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
codeBlock: tokens,
|
|
213
|
+
unifiedCodeBlock: tokens.map(token => ({
|
|
214
|
+
content: token.codeContent,
|
|
215
|
+
type: TYPE_MAP[token.highlightType],
|
|
216
|
+
})),
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function extractInlineEntities(text) {
|
|
221
|
+
const source = String(text || '')
|
|
222
|
+
const pattern = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g
|
|
223
|
+
const inlineEntities = []
|
|
224
|
+
let result = ''
|
|
225
|
+
let lastIndex = 0
|
|
226
|
+
let index = 0
|
|
227
|
+
let match
|
|
228
|
+
|
|
229
|
+
while ((match = pattern.exec(source))) {
|
|
230
|
+
const [raw, displayName, url] = match
|
|
231
|
+
const key = `QIRO_HYPERLINK_${index++}`
|
|
232
|
+
result += source.slice(lastIndex, match.index)
|
|
233
|
+
result += `{{${key}}}${url}{{/${key}}}`
|
|
234
|
+
inlineEntities.push({
|
|
235
|
+
key,
|
|
236
|
+
metadata: {
|
|
237
|
+
display_name: displayName,
|
|
238
|
+
is_trusted: true,
|
|
239
|
+
url,
|
|
240
|
+
__typename: 'GenAIInlineLinkItem',
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
lastIndex = match.index + raw.length
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
result += source.slice(lastIndex)
|
|
247
|
+
return { text: result, inlineEntities }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeInlineMarkdown(text) {
|
|
251
|
+
const boldToken = 'QIRO_BOLD_TOKEN_7F3A9C'
|
|
252
|
+
return String(text || '')
|
|
253
|
+
.replace(/\*{3}([^*\n]+)\*{3}/g, `${boldToken}$1${boldToken}`)
|
|
254
|
+
.replace(/\*{2}([^*\n]+)\*{2}/g, `${boldToken}$1${boldToken}`)
|
|
255
|
+
.replace(/_{2}([^_\n]+)_{2}/g, `${boldToken}$1${boldToken}`)
|
|
256
|
+
.replace(/!\[([^\]]*)\]\((https?:\/\/[^)]+)\)/g, '$1')
|
|
257
|
+
.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '$1 ($2)')
|
|
258
|
+
.replace(/(^|\s)_([^_\n]+)_(?=\s|$)/g, '$1$2')
|
|
259
|
+
.replace(/(^|\s)~([^~\n]+)~(?=\s|$)/g, '$1$2')
|
|
260
|
+
.replace(/~~([^~\n]+)~~/g, '$1')
|
|
261
|
+
.replace(/`([^`\n]+)`/g, '$1')
|
|
262
|
+
.replace(/\*([^*\n]+)\*/g, '$1')
|
|
263
|
+
.replace(new RegExp(boldToken, 'g'), '*')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function cleanTableCell(value) {
|
|
267
|
+
return String(value ?? '')
|
|
268
|
+
.replace(/!\[([^\]]*)\]\((https?:\/\/[^)]+)\)/g, '$1')
|
|
269
|
+
.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '$1')
|
|
270
|
+
.replace(/\*{3}([^*\n]+)\*{3}/g, '$1')
|
|
271
|
+
.replace(/\*{2}([^*\n]+)\*{2}/g, '$1')
|
|
272
|
+
.replace(/\*([^*\n]+)\*/g, '$1')
|
|
273
|
+
.replace(/(^|\s)_([^_\n]+)_(?=\s|$)/g, '$1$2')
|
|
274
|
+
.replace(/(^|\s)~([^~\n]+)~(?=\s|$)/g, '$1$2')
|
|
275
|
+
.replace(/~~([^~\n]+)~~/g, '$1')
|
|
276
|
+
.replace(/`([^`\n]+)`/g, '$1')
|
|
277
|
+
.replace(/\s{2,}/g, ' ')
|
|
278
|
+
.trim()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function getTextLineKind(line) {
|
|
282
|
+
const value = String(line || '').trim()
|
|
283
|
+
if (!value) return 'blank'
|
|
284
|
+
if (/^#{1,6}\s+/.test(value)) return 'heading'
|
|
285
|
+
if (/^[-*+]\s+/.test(value) || /^\d+[.)]\s+/.test(value)) return 'list'
|
|
286
|
+
if (/^>\s?/.test(value)) return 'quote'
|
|
287
|
+
if (/^(?:[-*_]){3,}$/.test(value)) return 'divider'
|
|
288
|
+
return 'paragraph'
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function normalizeTextAtom(value, { preserveHeading = false } = {}) {
|
|
292
|
+
let line = String(value || '').replace(/[ \t]+/g, ' ').trim()
|
|
293
|
+
if (!line) return ''
|
|
294
|
+
const heading = line.match(/^\s{0,3}#{1,6}\s+(.+?)\s*$/)
|
|
295
|
+
if (heading) {
|
|
296
|
+
const headingText = normalizeInlineMarkdown(heading[1]).replace(/[*_~`]+/g, '').trim()
|
|
297
|
+
return preserveHeading && headingText ? `*${headingText}*` : headingText
|
|
298
|
+
}
|
|
299
|
+
return normalizeInlineMarkdown(line).trim()
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function buildTextUnits(text) {
|
|
303
|
+
const lines = String(text || '').replace(/\r/g, '').split('\n')
|
|
304
|
+
const units = []
|
|
305
|
+
let buffer = []
|
|
306
|
+
let explicitDivider = false
|
|
307
|
+
let previousKind = null
|
|
308
|
+
|
|
309
|
+
const pushParagraph = () => {
|
|
310
|
+
if (!buffer.length) return
|
|
311
|
+
const normalizedLines = buffer.map(line => normalizeTextAtom(line)).filter(Boolean)
|
|
312
|
+
if (!normalizedLines.length) {
|
|
313
|
+
buffer = []
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
units.push({ type: 'paragraph', lines: normalizedLines })
|
|
317
|
+
buffer = []
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const pushList = (kind) => {
|
|
321
|
+
const normalizedLines = buffer.map(line => {
|
|
322
|
+
const source = String(line || '').trim()
|
|
323
|
+
const unordered = source.match(/^[-*+]\s+(.+)$/)
|
|
324
|
+
const ordered = source.match(/^(\d+)[.)]\s+(.+)$/)
|
|
325
|
+
if (kind === 'unordered' && unordered) return `• ${normalizeTextAtom(unordered[1])}`
|
|
326
|
+
if (kind === 'ordered' && ordered) return `${ordered[1]}. ${normalizeTextAtom(ordered[2])}`
|
|
327
|
+
return normalizeTextAtom(source)
|
|
328
|
+
}).filter(Boolean)
|
|
329
|
+
if (normalizedLines.length) units.push({ type: 'list', lines: normalizedLines })
|
|
330
|
+
buffer = []
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const pushQuote = () => {
|
|
334
|
+
const normalizedLines = buffer.map(line => normalizeTextAtom(String(line || '').replace(/^>\s?/, ''))).filter(Boolean)
|
|
335
|
+
if (normalizedLines.length) units.push({ type: 'quote', lines: normalizedLines.map(line => `│ ${line}`) })
|
|
336
|
+
buffer = []
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const pushByKind = kind => {
|
|
340
|
+
if (kind === 'unordered' || kind === 'ordered') pushList(kind)
|
|
341
|
+
else if (kind === 'quote') pushQuote()
|
|
342
|
+
else pushParagraph()
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (const rawLine of lines) {
|
|
346
|
+
const line = String(rawLine || '').replace(/[ \t]+$/g, '').trim()
|
|
347
|
+
if (!line) {
|
|
348
|
+
pushByKind(previousKind === 'unordered' || previousKind === 'ordered' || previousKind === 'quote' ? previousKind : 'paragraph')
|
|
349
|
+
previousKind = null
|
|
350
|
+
continue
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const kind = getTextLineKind(line)
|
|
354
|
+
if (kind === 'divider') {
|
|
355
|
+
pushByKind(previousKind === 'unordered' || previousKind === 'ordered' || previousKind === 'quote' ? previousKind : 'paragraph')
|
|
356
|
+
explicitDivider = true
|
|
357
|
+
previousKind = null
|
|
358
|
+
continue
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (kind === 'heading') {
|
|
362
|
+
pushByKind(previousKind === 'unordered' || previousKind === 'ordered' || previousKind === 'quote' ? previousKind : 'paragraph')
|
|
363
|
+
const headingText = normalizeTextAtom(line, { preserveHeading: true })
|
|
364
|
+
if (headingText) units.push({ type: 'heading', lines: [headingText] })
|
|
365
|
+
previousKind = null
|
|
366
|
+
continue
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (kind === 'list') {
|
|
370
|
+
const listKind = /^\d+[.)]\s+/.test(line) ? 'ordered' : 'unordered'
|
|
371
|
+
if (previousKind && previousKind !== listKind) pushByKind(previousKind)
|
|
372
|
+
buffer.push(line)
|
|
373
|
+
previousKind = listKind
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (kind === 'quote') {
|
|
378
|
+
if (previousKind && previousKind !== 'quote') pushByKind(previousKind)
|
|
379
|
+
buffer.push(line)
|
|
380
|
+
previousKind = 'quote'
|
|
381
|
+
continue
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (previousKind && previousKind !== 'paragraph') pushByKind(previousKind)
|
|
385
|
+
buffer.push(line)
|
|
386
|
+
previousKind = 'paragraph'
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
pushByKind(previousKind === 'unordered' || previousKind === 'ordered' || previousKind === 'quote' ? previousKind : 'paragraph')
|
|
390
|
+
return { units: compactTextUnits(units), explicitDivider }
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function compactTextUnits(units) {
|
|
394
|
+
const result = []
|
|
395
|
+
for (let index = 0; index < units.length; index++) {
|
|
396
|
+
const current = units[index]
|
|
397
|
+
const next = units[index + 1]
|
|
398
|
+
if (!next) {
|
|
399
|
+
result.push(current)
|
|
400
|
+
continue
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (current.type === 'heading' && ['paragraph', 'list', 'quote'].includes(next.type)) {
|
|
404
|
+
result.push({ type: 'section', lines: [...current.lines, ...next.lines] })
|
|
405
|
+
index++
|
|
406
|
+
continue
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const singleOrdered = current.type === 'list' && current.lines.length === 1 && /^\d+\.\s+/.test(current.lines[0])
|
|
410
|
+
if (singleOrdered && next.type === 'paragraph') {
|
|
411
|
+
result.push({ type: 'section', lines: [...current.lines, ...next.lines] })
|
|
412
|
+
index++
|
|
413
|
+
continue
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (current.type === 'paragraph' && next.type === 'list' && /[:;]$/.test(current.lines[current.lines.length - 1])) {
|
|
417
|
+
result.push({ type: 'section', lines: [...current.lines, ...next.lines] })
|
|
418
|
+
index++
|
|
419
|
+
continue
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
result.push(current)
|
|
423
|
+
}
|
|
424
|
+
return result
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function chooseDivider() {
|
|
428
|
+
const options = ['────────────', '┈┈┈┈┈┈┈┈┈┈', '╭──────────╮']
|
|
429
|
+
return options[crypto.randomInt(0, options.length)]
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function shouldInsertDivider(units, explicitDivider) {
|
|
433
|
+
if (units.length < 3) return false
|
|
434
|
+
const chance = explicitDivider ? 34 : 16
|
|
435
|
+
return crypto.randomInt(1, 101) <= chance
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function renderTextUnits(units, explicitDivider) {
|
|
439
|
+
if (!units.length) return ''
|
|
440
|
+
const dividerEnabled = shouldInsertDivider(units, explicitDivider)
|
|
441
|
+
let dividerIndex = -1
|
|
442
|
+
if (dividerEnabled) {
|
|
443
|
+
const candidates = units
|
|
444
|
+
.map((unit, index) => ({ unit, index }))
|
|
445
|
+
.filter(({ unit, index }) => index > 0 && (unit.type === 'heading' || unit.type === 'list' || unit.type === 'quote'))
|
|
446
|
+
.map(item => item.index)
|
|
447
|
+
const fallback = units.map((_, index) => index).filter(index => index > 0)
|
|
448
|
+
const pool = candidates.length ? candidates : fallback
|
|
449
|
+
dividerIndex = pool[crypto.randomInt(0, pool.length)]
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const rendered = []
|
|
453
|
+
units.forEach((unit, index) => {
|
|
454
|
+
const content = unit.lines.join('\n').trim()
|
|
455
|
+
if (!content) return
|
|
456
|
+
if (dividerIndex === index) rendered.push(chooseDivider())
|
|
457
|
+
rendered.push(content)
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
return rendered.join('\n\n').replace(/(?:\n[ \t]*){3,}/g, '\n\n').trim()
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function normalizeTextLayout(text) {
|
|
464
|
+
return renderTextUnits(...(() => {
|
|
465
|
+
const result = buildTextUnits(text)
|
|
466
|
+
return [result.units, result.explicitDivider]
|
|
467
|
+
})())
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function composeTextBlocks(text) {
|
|
471
|
+
const unitsData = buildTextUnits(text)
|
|
472
|
+
if (!unitsData.units.length) return []
|
|
473
|
+
const rendered = renderTextUnits(unitsData.units, unitsData.explicitDivider)
|
|
474
|
+
if (!rendered) return []
|
|
475
|
+
return [{ type: 'paragraph', text: rendered }]
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function normalizeTable(table) {
|
|
479
|
+
if (!Array.isArray(table) || !table.length || !table.every(row => Array.isArray(row))) {
|
|
480
|
+
throw new TypeError('Table harus berupa array berisi array')
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const maxColumns = Math.max(1, ...table.map(row => row.length))
|
|
484
|
+
const rows = table.map(row => Array.from({ length: maxColumns }, (_, index) => cleanTableCell(row[index] ?? '')))
|
|
485
|
+
const [header, ...body] = rows
|
|
486
|
+
return [
|
|
487
|
+
{ is_header: true, cells: header },
|
|
488
|
+
...body.map(cells => ({ is_header: false, cells })),
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function buildLayout(primitive) {
|
|
493
|
+
return {
|
|
494
|
+
view_model: {
|
|
495
|
+
primitive,
|
|
496
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
497
|
+
},
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function buildTextSection(text) {
|
|
502
|
+
const extracted = extractInlineEntities(text)
|
|
503
|
+
return buildLayout({
|
|
504
|
+
text: extracted.text,
|
|
505
|
+
...(extracted.inlineEntities.length ? { inline_entities: extracted.inlineEntities } : {}),
|
|
506
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function buildCodeSection(language, code) {
|
|
511
|
+
const meta = tokenize(code, language)
|
|
512
|
+
return buildLayout({
|
|
513
|
+
language: String(language || 'text'),
|
|
514
|
+
code_blocks: meta.unifiedCodeBlock,
|
|
515
|
+
__typename: 'GenAICodeUXPrimitive',
|
|
516
|
+
})
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function buildTableSection(table) {
|
|
520
|
+
return buildLayout({
|
|
521
|
+
rows: normalizeTable(table),
|
|
522
|
+
__typename: 'GenATableUXPrimitive',
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function normalizeMarkdownForUnifiedText(text) {
|
|
527
|
+
return String(text || '')
|
|
528
|
+
.replace(/\*{4,}([^\n*]+)\*{4,}/g, '**$1**')
|
|
529
|
+
.replace(/_{4,}([^\n_]+)_{4,}/g, '__$1__')
|
|
530
|
+
.replace(/~{3,}([^\n~]+)~{3,}/g, '~~$1~~')
|
|
531
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
532
|
+
.trim()
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function buildQuotedContext(quoted) {
|
|
536
|
+
if (!quoted?.key) return {}
|
|
537
|
+
return {
|
|
538
|
+
stanzaId: quoted.key.id || quoted.id,
|
|
539
|
+
participant: quoted.key.participant || quoted.key.remoteJid || quoted.sender,
|
|
540
|
+
quotedType: 0,
|
|
541
|
+
quotedMessage: quoted.message ?? quoted,
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
class QiroAIRich {
|
|
546
|
+
constructor(client, { title = 'Qiro AI' } = {}) {
|
|
547
|
+
if (!client?.relayMessage) throw new TypeError('Connection relayMessage tidak tersedia')
|
|
548
|
+
this.client = client
|
|
549
|
+
this.title = String(title || 'Qiro AI')
|
|
550
|
+
this.submessages = []
|
|
551
|
+
this.sections = []
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
addText(text) {
|
|
555
|
+
if (typeof text !== 'string') throw new TypeError('Text harus berupa string')
|
|
556
|
+
const blocks = composeTextBlocks(text)
|
|
557
|
+
if (!blocks.length) return this
|
|
558
|
+
|
|
559
|
+
const messageText = blocks
|
|
560
|
+
.map(block => block.text.trim())
|
|
561
|
+
.filter(Boolean)
|
|
562
|
+
.join('\n')
|
|
563
|
+
.trim()
|
|
564
|
+
|
|
565
|
+
if (!messageText) return this
|
|
566
|
+
|
|
567
|
+
this.submessages.push({
|
|
568
|
+
messageType: 2,
|
|
569
|
+
messageText,
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
const extracted = extractInlineEntities(messageText)
|
|
573
|
+
this.sections.push(buildLayout({
|
|
574
|
+
text: extracted.text,
|
|
575
|
+
...(extracted.inlineEntities.length ? { inline_entities: extracted.inlineEntities } : {}),
|
|
576
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
577
|
+
}))
|
|
578
|
+
|
|
579
|
+
return this
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
addCode(language, code) {
|
|
583
|
+
if (typeof language !== 'string' || typeof code !== 'string') {
|
|
584
|
+
throw new TypeError('Language dan code harus berupa string')
|
|
585
|
+
}
|
|
586
|
+
const normalizedLanguage = normalizeLanguage(language)
|
|
587
|
+
const sourceCode = String(code)
|
|
588
|
+
if (!sourceCode.length) return this
|
|
589
|
+
const meta = tokenize(sourceCode, normalizedLanguage)
|
|
590
|
+
this.submessages.push({
|
|
591
|
+
messageType: 5,
|
|
592
|
+
codeMetadata: {
|
|
593
|
+
codeLanguage: normalizedLanguage,
|
|
594
|
+
codeBlocks: meta.codeBlock,
|
|
595
|
+
},
|
|
596
|
+
})
|
|
597
|
+
this.sections.push(buildCodeSection(normalizedLanguage, sourceCode))
|
|
598
|
+
return this
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
addTable(table) {
|
|
602
|
+
const unifiedRows = normalizeTable(table)
|
|
603
|
+
this.submessages.push({
|
|
604
|
+
messageType: 4,
|
|
605
|
+
tableMetadata: {
|
|
606
|
+
title: '',
|
|
607
|
+
rows: unifiedRows.map(row => ({
|
|
608
|
+
items: row.cells,
|
|
609
|
+
...(row.is_header ? { isHeading: true } : {}),
|
|
610
|
+
})),
|
|
611
|
+
},
|
|
612
|
+
})
|
|
613
|
+
this.sections.push(buildLayout({
|
|
614
|
+
rows: unifiedRows,
|
|
615
|
+
__typename: 'GenATableUXPrimitive',
|
|
616
|
+
}))
|
|
617
|
+
return this
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async send(jid, { quoted = null, forwarded = true, ...options } = {}) {
|
|
621
|
+
if (!this.submessages.length) {
|
|
622
|
+
throw new Error('Rich response tidak memiliki konten')
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const contextInfo = {
|
|
626
|
+
...(forwarded
|
|
627
|
+
? {
|
|
628
|
+
forwardingScore: 1,
|
|
629
|
+
isForwarded: true,
|
|
630
|
+
forwardedAiBotMessageInfo: {
|
|
631
|
+
botJid: '867051314767696@bot',
|
|
632
|
+
},
|
|
633
|
+
forwardOrigin: 4,
|
|
634
|
+
}
|
|
635
|
+
: {}),
|
|
636
|
+
...buildQuotedContext(quoted),
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const responseId = `3EB0${crypto.randomBytes(18).toString('hex').toUpperCase()}`
|
|
640
|
+
const message = {
|
|
641
|
+
botForwardedMessage: {
|
|
642
|
+
message: {
|
|
643
|
+
richResponseMessage: {
|
|
644
|
+
messageType: 1,
|
|
645
|
+
submessages: this.submessages,
|
|
646
|
+
contextInfo,
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
await this.client.relayMessage(jid, message, { messageId: responseId, ...options })
|
|
653
|
+
return { message, responseId }
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export { QiroAIRich, tokenize, normalizeTable, buildTextSection, buildCodeSection, buildTableSection }
|
|
658
|
+
export default QiroAIRich
|