@awangxcode/qiro-baileys 7.0.0-rc20 → 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.
@@ -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