{"slug": "messagebuilderv4-7-js", "title": "MessageBuilderV4.7.js", "summary": "Nixel released MessageBuilderV4.7.js, an advanced WhatsApp interactive message builder for Baileys that supports buttons, carousels, native flows, and AI rich response payloads. The library includes a custom extractIE function for parsing hyperlinks, citations, and LaTeX expressions, and is available for personal or commercial use with restrictions on redistribution.", "body_md": "|\n/** |\n|\n* Do not remove this watermark. |\n|\n* |\n|\n* NIXCODE - Advanced WhatsApp Interactive Message Builder |\n|\n* Built for creating buttons, carousels, native flows, |\n|\n* and AI rich response payloads using Baileys with |\n|\n* fluent chaining, flexible payload customization, |\n|\n* and scalable architecture for modern bot development. |\n|\n* |\n|\n* Runtime: |\n|\n* - Baileys: @whiskeysockets/baileys (latest) |\n|\n* |\n|\n* Created by Nixel |\n|\n* Contributors: ~ Ahmad tumbuh kembang |\n|\n* |\n|\n* WhatsApp: wa.me/6285188349341 |\n|\n* Channel: https://whatsapp.com/channel/0029VbCV1ck8fewpdNb2TY2k |\n|\n* |\n|\n* Copyright (c) 2026 Nixel |\n|\n* |\n|\n* Permission is granted to use and modify this library |\n|\n* for personal or commercial projects. |\n|\n* |\n|\n* Reuploading, reselling, relicensing, or redistributing |\n|\n* this library as a standalone product is prohibited. |\n|\n* |\n|\n* Do not claim this project as your own original work. |\n|\n*/ |\n|\n|\n|\n'use strict'; |\n|\n|\n|\nconst VERSION = '4.7'; |\n|\n|\n|\nimport { generateWAMessageFromContent, prepareWAMessageMedia, generateMessageIDV2 } from 'baileys'; |\n|\nimport crypto from 'crypto'; |\n|\nimport sharp from 'sharp'; |\n|\nimport ffmpeg from 'fluent-ffmpeg'; |\n|\nimport { PassThrough, Readable } from 'stream'; |\n|\n|\n|\nfunction extractIE(text, { extract = true, hyperlink = true, citation = true, latex = true } = {}) { |\n|\nif (!extract) { |\n|\nreturn { |\n|\ntext, |\n|\nie: [], |\n|\ninline_entities: [], |\n|\n}; |\n|\n} |\n|\n|\n|\nconst createIE = (type, ie) => { |\n|\nif (type == 'hyperlink') { |\n|\nreturn { |\n|\nkey: ie.key, |\n|\nmetadata: { |\n|\ndisplay_name: ie.text, |\n|\nis_trusted: ie.is_trusted, |\n|\nurl: ie.url, |\n|\n__typename: 'GenAIInlineLinkItem', |\n|\n}, |\n|\n}; |\n|\n} |\n|\n|\n|\nif (type == 'citation') { |\n|\nreturn { |\n|\nkey: ie.key, |\n|\nmetadata: { |\n|\nreference_id: ie.reference_id, |\n|\nreference_url: ie.url, |\n|\nreference_title: ie.url, |\n|\nreference_display_name: ie.url, |\n|\nsources: [], |\n|\n__typename: 'GenAISearchCitationItem', |\n|\n}, |\n|\n}; |\n|\n} |\n|\n|\n|\nif (type == 'latex') { |\n|\nreturn { |\n|\nkey: ie.key, |\n|\nmetadata: { |\n|\nlatex_expression: ie.text, |\n|\nlatex_image: { |\n|\nurl: ie.url, |\n|\nwidth: Number(ie.width) || 100, |\n|\nheight: Number(ie.height) || 100, |\n|\n}, |\n|\nfont_height: Number(ie.font_height) || 83.333333333333, |\n|\npadding: Number(ie.padding) || 15, |\n|\n__typename: 'GenAILatexItem', |\n|\n}, |\n|\n}; |\n|\n} |\n|\n}; |\n|\n|\n|\nlet ie = []; |\n|\nlet inline_entities = []; |\n|\nlet result = ''; |\n|\nlet last = 0; |\n|\nlet citation_index = 1; |\n|\nlet hyperlink_index = 0; |\n|\nlet latex_index = 0; |\n|\nlet stack = []; |\n|\n|\n|\nfor (let i = 0; i < text.length; i++) { |\n|\nif (text[i] == '[' && text[i - 1] != '\\\\') { |\n|\nstack.push(i); |\n|\n} else if (text[i] == ']' && (text[i + 1] == '(' || text[i + 1] == '<')) { |\n|\nlet start = stack.pop(); |\n|\n|\n|\nif (start == null) continue; |\n|\n|\n|\nlet open = text[i + 1]; |\n|\nlet close = open == '(' ? ')' : '>'; |\n|\nlet type = open == '(' ? 'link' : 'latex'; |\n|\nlet end = i + 2; |\n|\nlet depth = 1; |\n|\n|\n|\nwhile (end < text.length && depth) { |\n|\nif (text[end] == open && text[end - 1] != '\\\\') depth++; |\n|\nelse if (text[end] == close && text[end - 1] != '\\\\') depth--; |\n|\nend++; |\n|\n} |\n|\n|\n|\nif (depth) continue; |\n|\n|\n|\nlet raw = text.slice(start + 1, i).trim(); |\n|\nlet url = text.slice(i + 2, end - 1).trim(); |\n|\n|\n|\nlet key; |\n|\nlet tag; |\n|\nlet data; |\n|\n|\n|\nif (type == 'latex') { |\n|\nif (!latex) continue; |\n|\n|\n|\nlet [txt = '', width = null, height = null, font_height = null, padding = null] = raw.split('|'); |\n|\n|\n|\nkey = `\\u004E\\u0049\\u0058\\u0045\\u004C_LATEX_${latex_index++}`; |\n|\ntag = `{{${key}}}${txt || 'image'}{{/${key}}}`; |\n|\n|\n|\ndata = { |\n|\ntype: 'latex', |\n|\nie: { |\n|\nkey, |\n|\ntext: txt, |\n|\nurl, |\n|\nwidth, |\n|\nheight, |\n|\nfont_height, |\n|\npadding, |\n|\n}, |\n|\n}; |\n|\n} else if (raw) { |\n|\nif (!hyperlink) continue; |\n|\n|\n|\nconst trusted = !url.startsWith('!'); |\n|\n|\n|\nif (!trusted) { |\n|\nurl = url.slice(1); |\n|\n} |\n|\n|\n|\nkey = `\\u004E\\u0049\\u0058\\u0045\\u004C_HYPERLINK_${hyperlink_index++}`; |\n|\ntag = `{{${key}}}${url}{{/${key}}}`; |\n|\n|\n|\ndata = { |\n|\ntype: 'hyperlink', |\n|\nie: { |\n|\nkey, |\n|\ntext: raw, |\n|\nurl, |\n|\nis_trusted: trusted, |\n|\n}, |\n|\n}; |\n|\n} else { |\n|\nif (!citation) continue; |\n|\n|\n|\nkey = `\\u004E\\u0049\\u0058\\u0045\\u004C_CITATION_${citation_index - 1}`; |\n|\ntag = `{{${key}}}${url}{{/${key}}}`; |\n|\n|\n|\ndata = { |\n|\ntype: 'citation', |\n|\nie: { |\n|\nreference_id: citation_index++, |\n|\nkey, |\n|\ntext: '', |\n|\nurl, |\n|\n}, |\n|\n}; |\n|\n} |\n|\n|\n|\nresult += text.slice(last, start) + tag; |\n|\nlast = end; |\n|\n|\n|\nie.push(data); |\n|\n|\n|\nconst entity = createIE(data.type, data.ie); |\n|\n|\n|\nif (entity) { |\n|\ninline_entities.push(entity); |\n|\n} |\n|\n|\n|\ni = end - 1; |\n|\n} |\n|\n} |\n|\n|\n|\nresult += text.slice(last); |\n|\n|\n|\nreturn { |\n|\ntext: result, |\n|\nie, |\n|\ninline_entities, |\n|\n}; |\n|\n} |\n|\n|\n|\nasync function waitAllPromises(input) { |\n|\nconst isPromise = (v) => v && typeof v.then === 'function'; |\n|\nconst isObject = (v) => v && typeof v === 'object'; |\n|\n|\n|\nconst deep = async (v) => { |\n|\nif (isPromise(v)) return deep(await v); |\n|\nif (Array.isArray(v)) return Promise.all(v.map(deep)); |\n|\nif (isObject(v)) { |\n|\nconst entries = await Promise.all(Object.entries(v).map(async ([k, val]) => [k, await deep(val)])); |\n|\nreturn Object.fromEntries(entries); |\n|\n} |\n|\nreturn v; |\n|\n}; |\n|\n|\n|\nreturn deep(await input); |\n|\n} |\n|\n|\n|\nclass AIRichError extends Error { |\n|\nconstructor(message, code, meta = {}) { |\n|\nsuper(message); |\n|\nthis.name = 'AIRichError'; |\n|\nthis.code = code; |\n|\nObject.assign(this, meta); |\n|\n} |\n|\n} |\n|\n|\n|\nclass ItemNotFoundError extends AIRichError { |\n|\nconstructor(id, availableIds = []) { |\n|\nsuper(`Item id \"${id}\" not found${availableIds.length ? ` (available: ${availableIds.join(', ')})` : ' (no items have an id yet)'}`, 'ITEM_NOT_FOUND', { id, availableIds }); |\n|\nthis.name = 'ItemNotFoundError'; |\n|\n} |\n|\n} |\n|\n|\n|\nclass DuplicateIdError extends AIRichError { |\n|\nconstructor(id) { |\n|\nsuper(`Item id \"${id}\" already exists`, 'DUPLICATE_ID', { id }); |\n|\nthis.name = 'DuplicateIdError'; |\n|\n} |\n|\n} |\n|\n|\n|\nclass InvalidTargetError extends AIRichError { |\n|\nconstructor(message, meta = {}) { |\n|\nsuper(message, 'INVALID_TARGET', meta); |\n|\nthis.name = 'InvalidTargetError'; |\n|\n} |\n|\n} |\n|\n|\n|\nclass ContentValidationError extends AIRichError { |\n|\nconstructor(message, meta = {}) { |\n|\nsuper(message, 'CONTENT_VALIDATION', meta); |\n|\nthis.name = 'ContentValidationError'; |\n|\n} |\n|\n} |\n|\n|\n|\nclass Toolkit { |\n|\nconstructor() {} |\n|\n|\n|\nstatic extractIE(text, { extract = true, hyperlink = true, citation = true, latex = true } = {}) { |\n|\nreturn extractIE(text, { extract, hyperlink, citation, latex }); |\n|\n} |\n|\n|\n|\nstatic async resize(buffer, x, y, fit = 'cover') { |\n|\nreturn await sharp(buffer) |\n|\n.resize(x, y, { |\n|\nfit, |\n|\nposition: 'center', |\n|\nbackground: { r: 0, g: 0, b: 0, alpha: 0 }, |\n|\n}) |\n|\n.png() |\n|\n.toBuffer(); |\n|\n} |\n|\n|\n|\nstatic async waitAllPromises(input) { |\n|\nreturn await waitAllPromises(input); |\n|\n} |\n|\n|\n|\nstatic async fetchBuffer(url, options = {}, { silent = true } = {}) { |\n|\ntry { |\n|\nlet response = await fetch(url, options); |\n|\nif (!response.ok) throw Error(`HTTP ${response.status}`); |\n|\nreturn Buffer.from(await response.arrayBuffer()); |\n|\n} catch (error) { |\n|\nif (silent) return Buffer.alloc(0); |\n|\nthrow error; |\n|\n} |\n|\n} |\n|\n|\n|\nstatic async toUrl(_client, path, mediaType = 'document') { |\n|\nif (!path) throw new Error('Url or buffer needed'); |\n|\n|\n|\nconst media = await prepareWAMessageMedia( |\n|\n{ |\n|\n[mediaType]: Buffer.isBuffer(path) ? path : { url: path }, |\n|\n}, |\n|\n{ |\n|\nupload: _client.waUploadToServer, |\n|\njid: '\\u0040\\u006e\\u0065\\u0077\\u0073\\u006c\\u0065\\u0074\\u0074\\u0065\\u0072', |\n|\n} |\n|\n); |\n|\n|\n|\nreturn Object.values(media)[0]?.url; |\n|\n} |\n|\n|\n|\nstatic async resolveMedia(_client, media, mediaType = 'image', { resolveUrl = false, resolveWAUrl = false, result = 'url', resize = false, width = 300, height = 300 } = {}) { |\n|\nconst isUrl = (str) => /^https?:\\/\\/.+/i.test(str); |\n|\n|\n|\nconst isWAUrl = (str) => /^https?:\\/\\/[^/]*\\.whatsapp\\.net\\//i.test(str); |\n|\n|\n|\nif (Array.isArray(media)) { |\n|\nreturn Promise.all( |\n|\nmedia.map((item) => |\n|\nToolkit.resolveMedia(_client, item, mediaType, { |\n|\nresolveUrl, |\n|\nresolveWAUrl, |\n|\nresult, |\n|\nresize, |\n|\nwidth, |\n|\nheight, |\n|\n}) |\n|\n) |\n|\n); |\n|\n} |\n|\n|\n|\nconst originalIsBuffer = Buffer.isBuffer(media); |\n|\n|\n|\nif (typeof media === 'string' && isUrl(media)) { |\n|\nif (isWAUrl(media)) { |\n|\nif (resolveWAUrl) { |\n|\nmedia = await Toolkit.fetchBuffer(media, {}, { silent: true }); |\n|\n} else if (!resolveUrl) { |\n|\nif (result === 'url') return media; |\n|\n|\n|\nmedia = await Toolkit.fetchBuffer(media, {}, { silent: true }); |\n|\n} |\n|\n} else { |\n|\nif (!resolveUrl) { |\n|\nif (result === 'url') return media; |\n|\n|\n|\nmedia = await Toolkit.fetchBuffer(media, {}, { silent: true }); |\n|\n} else { |\n|\nmedia = await Toolkit.fetchBuffer(media, {}, { silent: true }); |\n|\n} |\n|\n} |\n|\n} |\n|\n|\n|\nif (typeof media === 'string' && !isUrl(media)) { |\n|\nmedia = Buffer.from(media, 'base64'); |\n|\n} |\n|\n|\n|\nif (!Buffer.isBuffer(media) || !media.length) { |\n|\nreturn; |\n|\n} |\n|\n|\n|\nif (resize && Buffer.isBuffer(media)) { |\n|\nmedia = await Toolkit.resize(media, width, height); |\n|\n} |\n|\n|\n|\nif (result === 'buffer') { |\n|\nreturn media; |\n|\n} |\n|\n|\n|\nif (result === 'base64') { |\n|\nreturn media.toString('base64'); |\n|\n} |\n|\n|\n|\nif (originalIsBuffer) { |\n|\nreturn Toolkit.toUrl(_client, media, mediaType); |\n|\n} |\n|\n|\n|\nreturn Toolkit.toUrl(_client, media, mediaType); |\n|\n} |\n|\n|\n|\nstatic getMp4Duration(buffer, { silent = true } = {}) { |\n|\ntry { |\n|\nif (!Buffer.isBuffer(buffer) || buffer.length < 8) { |\n|\nif (silent) return 0; |\n|\nthrow new Error('Invalid buffer'); |\n|\n} |\n|\n|\n|\nlet offset = 0; |\n|\n|\n|\nwhile (offset < buffer.length - 8) { |\n|\nconst size = buffer.readUInt32BE(offset); |\n|\n|\n|\nif (size < 8 || offset + size > buffer.length) { |\n|\nif (silent) return 0; |\n|\nthrow new Error('Invalid atom size'); |\n|\n} |\n|\n|\n|\nconst type = buffer.toString('ascii', offset + 4, offset + 8); |\n|\n|\n|\nif (type === 'moov') { |\n|\nlet moovOffset = offset + 8; |\n|\nconst moovEnd = offset + size; |\n|\n|\n|\nwhile (moovOffset < moovEnd - 8) { |\n|\nconst childSize = buffer.readUInt32BE(moovOffset); |\n|\n|\n|\nif (childSize < 8 || moovOffset + childSize > moovEnd) { |\n|\nif (silent) return 0; |\n|\nthrow new Error('Invalid child atom size'); |\n|\n} |\n|\n|\n|\nconst childType = buffer.toString('ascii', moovOffset + 4, moovOffset + 8); |\n|\n|\n|\nif (childType === 'mvhd') { |\n|\nconst version = buffer.readUInt8(moovOffset + 8); |\n|\n|\n|\nif (version === 0) { |\n|\nconst timescale = buffer.readUInt32BE(moovOffset + 20); |\n|\nconst duration = buffer.readUInt32BE(moovOffset + 24); |\n|\n|\n|\nif (!timescale) { |\n|\nif (silent) return 0; |\n|\nthrow new Error('Invalid timescale'); |\n|\n} |\n|\n|\n|\nreturn duration / timescale; |\n|\n} |\n|\n|\n|\nif (version === 1) { |\n|\nconst timescale = buffer.readUInt32BE(moovOffset + 32); |\n|\nconst duration = Number(buffer.readBigUInt64BE(moovOffset + 36)); |\n|\n|\n|\nif (!timescale) { |\n|\nif (silent) return 0; |\n|\nthrow new Error('Invalid timescale'); |\n|\n} |\n|\n|\n|\nreturn duration / timescale; |\n|\n} |\n|\n} |\n|\n|\n|\nmoovOffset += childSize; |\n|\n} |\n|\n} |\n|\n|\n|\noffset += size; |\n|\n} |\n|\n|\n|\nif (silent) return 0; |\n|\n|\n|\nthrow new Error('No mvhd found!'); |\n|\n} catch (err) { |\n|\nif (silent) return 0; |\n|\nthrow err; |\n|\n} |\n|\n} |\n|\n|\n|\nstatic getMp4Preview(videoBuffer, { time, result = 'buffer', resize = true, width = 300, height = 300, silent = true } = {}) { |\n|\nreturn new Promise((resolve, reject) => { |\n|\nconst fail = (err) => { |\n|\nif (silent) { |\n|\nreturn resolve(result === 'base64' ? '' : Buffer.alloc(0)); |\n|\n} |\n|\nreturn reject(err); |\n|\n}; |\n|\n|\n|\ntry { |\n|\nif (!Buffer.isBuffer(videoBuffer) || !videoBuffer.length) { |\n|\nreturn fail(new Error('videoBuffer tidak valid atau kosong')); |\n|\n} |\n|\n|\n|\nconst inputStream = new Readable({ read() {} }); |\n|\ninputStream.push(videoBuffer); |\n|\ninputStream.push(null); |\n|\n|\n|\nconst outputStream = new PassThrough(); |\n|\nconst chunks = []; |\n|\n|\n|\noutputStream.on('data', (chunk) => chunks.push(chunk)); |\n|\n|\n|\noutputStream.on('end', async () => { |\n|\ntry { |\n|\nlet output = Buffer.concat(chunks); |\n|\n|\n|\nif (!output.length) { |\n|\nreturn fail(new Error('Output kosong — cek format atau timestamp video')); |\n|\n} |\n|\n|\n|\nif (resize) { |\n|\noutput = await Toolkit.resize(output, width, height); |\n|\n} |\n|\n|\n|\nreturn resolve(result === 'base64' ? output.toString('base64') : output); |\n|\n} catch (err) { |\n|\nreturn fail(err); |\n|\n} |\n|\n}); |\n|\n|\n|\noutputStream.on('error', fail); |\n|\n|\n|\ntime = time ?? Math.min(Toolkit.getMp4Duration(videoBuffer) * 0.2, 10); |\n|\n|\n|\nffmpeg(inputStream) |\n|\n.outputOptions([`-ss ${time}`, '-vframes 1', '-vcodec png', '-f image2pipe']) |\n|\n.on('error', (err) => fail(new Error(`ffmpeg error: ${err.message}`))) |\n|\n.pipe(outputStream, { end: true }); |\n|\n} catch (err) { |\n|\nreturn fail(err); |\n|\n} |\n|\n}); |\n|\n} |\n|\n|\n|\nstatic stringifyEscaped(obj) { |\n|\nreturn JSON.stringify(obj).replace(/[\\u007f-\\uffff]/g, (c) => '\\\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')); |\n|\n} |\n|\n} |\n|\n|\n|\nclass BaseBuilder { |\n|\nconstructor() { |\n|\nthis._title = ''; |\n|\nthis._subtitle = ''; |\n|\nthis._body = ''; |\n|\nthis._footer = ''; |\n|\nthis._contextInfo = {}; |\n|\nthis._extraPayload = {}; |\n|\n} |\n|\n|\n|\nsetTitle(title) { |\n|\nif (typeof title !== 'string') { |\n|\nthrow new TypeError('Title must be a string'); |\n|\n} |\n|\nthis._title = title; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetSubtitle(subtitle) { |\n|\nif (typeof subtitle !== 'string') { |\n|\nthrow new TypeError('Subtitle must be a string'); |\n|\n} |\n|\nthis._subtitle = subtitle; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetBody(body) { |\n|\nif (typeof body !== 'string') { |\n|\nthrow new TypeError('Body must be a string'); |\n|\n} |\n|\nthis._body = body; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetFooter(footer) { |\n|\nif (typeof footer !== 'string') { |\n|\nthrow new TypeError('Footer must be a string'); |\n|\n} |\n|\nthis._footer = footer; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetContextInfo(obj) { |\n|\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { |\n|\nthrow new TypeError('ContextInfo must be a plain object'); |\n|\n} |\n|\n|\n|\nthis._contextInfo = obj; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddPayload(obj) { |\n|\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { |\n|\nthrow new TypeError('Payload must be a plain object'); |\n|\n} |\n|\n|\n|\nObject.assign(this._extraPayload, obj); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n} |\n|\n|\n|\nclass Button extends BaseBuilder { |\n|\n#client; |\n|\n|\n|\nconstructor(client) { |\n|\nsuper(); |\n|\nif (!client) { |\n|\nthrow new Error('Socket is required'); |\n|\n} |\n|\nthis.#client = client; |\n|\n|\n|\nthis._buttons = []; |\n|\nthis._data; |\n|\nthis._currentSelectionIndex = -1; |\n|\nthis._currentSectionIndex = -1; |\n|\nthis._params = {}; |\n|\n} |\n|\n|\n|\nloadFrom(msg) { |\n|\nif (!msg) throw new Error('interactiveMessage needed'); |\n|\nif (!msg.interactiveMessage) throw new Error('interactiveMessage not found'); |\n|\n|\n|\nconst { interactiveMessage, ...extraPayload } = msg; |\n|\nconst iM = interactiveMessage; |\n|\nconst header = iM.header || {}; |\n|\nconst nativeFlow = iM.nativeFlowMessage || {}; |\n|\n|\n|\nthis._title = header.title || ''; |\n|\nthis._subtitle = header.subtitle || ''; |\n|\nthis._body = iM.body?.text || ''; |\n|\nthis._footer = iM.footer?.text || ''; |\n|\nthis._contextInfo = iM.contextInfo || {}; |\n|\nthis._extraPayload = extraPayload; |\n|\n|\n|\nthis._buttons = Array.isArray(nativeFlow.buttons) |\n|\n? nativeFlow.buttons.map((button) => ({ |\n|\n...button, |\n|\nbuttonParamsJson: typeof button.buttonParamsJson === 'string' ? button.buttonParamsJson : JSON.stringify(button.buttonParamsJson || {}), |\n|\n})) |\n|\n: []; |\n|\n|\n|\nthis._data = header.imageMessage |\n|\n? { imageMessage: header.imageMessage } |\n|\n: header.videoMessage |\n|\n? { videoMessage: header.videoMessage } |\n|\n: header.documentMessage |\n|\n? { documentMessage: header.documentMessage } |\n|\n: header.productMessage |\n|\n? { productMessage: header.productMessage } |\n|\n: undefined; |\n|\n|\n|\nthis._params = {}; |\n|\n|\n|\nif (typeof nativeFlow.messageParamsJson === 'string') { |\n|\ntry { |\n|\nthis._params = JSON.parse(nativeFlow.messageParamsJson || '{}'); |\n|\n} catch { |\n|\nthis._params = {}; |\n|\n} |\n|\n} else if (nativeFlow.messageParamsJson && typeof nativeFlow.messageParamsJson === 'object') { |\n|\nthis._params = { ...nativeFlow.messageParamsJson }; |\n|\n} |\n|\n|\n|\nthis._currentSelectionIndex = this._buttons.findLastIndex((button) => button.name === 'single_select'); |\n|\n|\n|\nthis._currentSectionIndex = -1; |\n|\n|\n|\nif (this._currentSelectionIndex !== -1) { |\n|\ntry { |\n|\nconst button = this._buttons[this._currentSelectionIndex]; |\n|\nconst params = JSON.parse(button.buttonParamsJson || '{}'); |\n|\n|\n|\nif (Array.isArray(params.sections) && params.sections.length) { |\n|\nthis._currentSectionIndex = params.sections.length - 1; |\n|\n} |\n|\n} catch { |\n|\nthis._currentSelectionIndex = -1; |\n|\nthis._currentSectionIndex = -1; |\n|\n} |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetImage(path, options = {}) { |\n|\nif (!path) throw new Error('Url or buffer needed'); |\n|\nBuffer.isBuffer(path) ? (this._data = { image: path, ...options }) : (this._data = { image: { url: path }, ...options }); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetDocument(path, options = {}) { |\n|\nif (!path) throw new Error('Url or buffer needed'); |\n|\nBuffer.isBuffer(path) ? (this._data = { document: path, ...options }) : (this._data = { document: { url: path }, ...options }); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetMedia(obj) { |\n|\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { |\n|\nthrow new TypeError('Media must be a plain object'); |\n|\n} |\n|\n|\n|\nthis._data = obj; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nclearButtons() { |\n|\nthis._buttons = []; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetParams(obj) { |\n|\nthis._params = obj; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddButton(name, params) { |\n|\nthis._buttons.push({ |\n|\nname, |\n|\nbuttonParamsJson: typeof params === 'string' ? params : JSON.stringify(params), |\n|\n}); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nmakeRow(header = '', title = '', description = '', id = '') { |\n|\nif (this._currentSelectionIndex === -1 || this._currentSectionIndex === -1) { |\n|\nthrow new Error('You need to create a selection and a section first'); |\n|\n} |\n|\nconst buttonParams = JSON.parse(this._buttons[this._currentSelectionIndex].buttonParamsJson); |\n|\nbuttonParams.sections[this._currentSectionIndex].rows.push({ header, title, description, id }); |\n|\nthis._buttons[this._currentSelectionIndex].buttonParamsJson = JSON.stringify(buttonParams); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nmakeSection(title = '', highlight_label = '') { |\n|\nif (this._currentSelectionIndex === -1) { |\n|\nthrow new Error('You need to create a selection first'); |\n|\n} |\n|\nconst buttonParams = JSON.parse(this._buttons[this._currentSelectionIndex].buttonParamsJson); |\n|\nbuttonParams.sections.push({ title, highlight_label, rows: [] }); |\n|\nthis._currentSectionIndex = buttonParams.sections.length - 1; |\n|\nthis._buttons[this._currentSelectionIndex].buttonParamsJson = JSON.stringify(buttonParams); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddSelection(title, options = {}) { |\n|\nthis._buttons.push({ name: 'single_select', buttonParamsJson: JSON.stringify({ title, sections: [], ...options }) }); |\n|\nthis._currentSelectionIndex = this._buttons.length - 1; |\n|\nthis._currentSectionIndex = -1; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddReply(display_text = '', id = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'quick_reply', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nid, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddCall(display_text = '', id = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'cta_call', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nid, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddReminder(display_text = '', id = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'cta_reminder', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nid, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddCancelReminder(display_text = '', id = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'cta_cancel_reminder', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nid, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddAddress(display_text = '', id = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'address_message', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nid, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddLocation(options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'send_location', |\n|\nbuttonParamsJson: JSON.stringify(options), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddUrl(display_text = '', url = '', webview_interaction = false, options = {}) { |\n|\nthis._buttons.push({ |\n|\n...options, |\n|\nname: 'cta_url', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\nurl, |\n|\nwebview_interaction, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddCopy(display_text = '', copy_code = '', options = {}) { |\n|\nthis._buttons.push({ |\n|\nname: 'cta_copy', |\n|\nbuttonParamsJson: JSON.stringify({ |\n|\ndisplay_text, |\n|\ncopy_code, |\n|\n...options, |\n|\n}), |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nstatic paramsList = { |\n|\nlimited_time_offer: { |\n|\ntext: 'string', |\n|\nurl: 'string', |\n|\ncopy_code: 'string', |\n|\nexpiration_time: 'number', |\n|\n}, |\n|\nbottom_sheet: { |\n|\nin_thread_buttons_limit: 'number', |\n|\ndivider_indices: ['number'], |\n|\nlist_title: 'string', |\n|\nbutton_title: 'string', |\n|\n}, |\n|\ntap_target_configuration: { |\n|\ntitle: 'string', |\n|\ndescription: 'string', |\n|\ncanonical_url: 'string', |\n|\ndomain: 'string', |\n|\nbuttonIndex: 'number', |\n|\n}, |\n|\n}; |\n|\n|\n|\nasync toCard() { |\n|\nreturn { |\n|\nbody: { |\n|\ntext: this._body, |\n|\n}, |\n|\nfooter: { |\n|\ntext: this._footer, |\n|\n}, |\n|\nheader: { |\n|\ntitle: this._title, |\n|\nsubtitle: this._subtitle, |\n|\nhasMediaAttachment: !!this._data, |\n|\n...(this._data |\n|\n? await prepareWAMessageMedia(this._data, { upload: this.#client.waUploadToServer }).catch((e) => { |\n|\nif (String(e).includes('Invalid media type')) return this._data; |\n|\nthrow e; |\n|\n}) |\n|\n: {}), |\n|\n}, |\n|\nnativeFlowMessage: { |\n|\nmessageParamsJson: JSON.stringify(this._params), |\n|\nbuttons: this._buttons, |\n|\n}, |\n|\n}; |\n|\n} |\n|\n|\n|\nasync build(jid, { messageId, ...options } = {}) { |\n|\nconst message = await this.toCard(); |\n|\n|\n|\nreturn generateWAMessageFromContent( |\n|\njid, |\n|\n{ |\n|\n...this._extraPayload, |\n|\ninteractiveMessage: { |\n|\n...message, |\n|\ncontextInfo: this._contextInfo, |\n|\n}, |\n|\n}, |\n|\n{ messageId: messageId || generateMessageIDV2(), ...options } |\n|\n); |\n|\n} |\n|\n|\n|\nasync send(jid, { messageId, additionalNodes = [], ...options } = {}) { |\n|\nconst msg = await this.build(jid, { messageId, ...options }); |\n|\n|\n|\nawait this.#client.relayMessage(msg.key.remoteJid, msg.message, { |\n|\nmessageId: msg.key.id, |\n|\nadditionalNodes: [ |\n|\n{ |\n|\ntag: 'biz', |\n|\nattrs: {}, |\n|\ncontent: [ |\n|\n{ |\n|\ntag: 'interactive', |\n|\nattrs: { type: 'native_flow', v: '1' }, |\n|\ncontent: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }], |\n|\n}, |\n|\n], |\n|\n}, |\n|\n...additionalNodes, |\n|\n], |\n|\n...options, |\n|\n}); |\n|\nreturn msg; |\n|\n} |\n|\n} |\n|\n|\n|\nclass ButtonV2 extends BaseBuilder { |\n|\n#client; |\n|\n|\n|\nconstructor(client) { |\n|\nsuper(); |\n|\nif (!client) { |\n|\nthrow new Error('Socket is required'); |\n|\n} |\n|\n|\n|\nthis.#client = client; |\n|\nthis._image; |\n|\nthis._data; |\n|\nthis._buttons = []; |\n|\n} |\n|\n|\n|\nloadFrom(msg) { |\n|\nif (!msg) throw new Error('buttonsMessage needed'); |\n|\nif (!msg.buttonsMessage) throw new Error('buttonsMessage not found'); |\n|\n|\n|\nconst { buttonsMessage, ...extraPayload } = msg; |\n|\nconst bM = buttonsMessage; |\n|\nconst location = bM.locationMessage || {}; |\n|\n|\n|\nthis._title = location.name || ''; |\n|\nthis._subtitle = location.address || ''; |\n|\nthis._body = bM.contentText || ''; |\n|\nthis._footer = bM.footerText || ''; |\n|\nthis._contextInfo = bM.contextInfo || {}; |\n|\nthis._extraPayload = extraPayload; |\n|\n|\n|\nthis._buttons = Array.isArray(bM.buttons) |\n|\n? bM.buttons.map((button) => ({ |\n|\n...button, |\n|\n...(button.nativeFlowInfo |\n|\n? { |\n|\nnativeFlowInfo: { |\n|\n...button.nativeFlowInfo, |\n|\nparamsJson: typeof button.nativeFlowInfo.paramsJson === 'string' ? button.nativeFlowInfo.paramsJson : JSON.stringify(button.nativeFlowInfo.paramsJson || {}), |\n|\n}, |\n|\n} |\n|\n: {}), |\n|\n})) |\n|\n: []; |\n|\n|\n|\nthis._image = location.jpegThumbnail || undefined; |\n|\n|\n|\nif (!this._image && bM.locationMessage) { |\n|\nthis._image = undefined; |\n|\n} |\n|\n|\n|\nif (!bM.locationMessage && bM.headerType === 6) { |\n|\nthis._image = undefined; |\n|\n} |\n|\n|\n|\nthis._data = Object.keys(bM).reduce((data, key) => { |\n|\nif (!['contentText', 'footerText', 'contextInfo', 'buttons', 'headerType', 'locationMessage', 'viewOnce'].includes(key)) { |\n|\ndata[key] = bM[key]; |\n|\n} |\n|\nreturn data; |\n|\n}, {}); |\n|\n|\n|\nif (!Object.keys(this._data).length) { |\n|\nthis._data = undefined; |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddButton(displayText = '', buttonId = crypto.randomUUID()) { |\n|\nthis._buttons.push({ |\n|\nbuttonId, |\n|\nbuttonText: { displayText }, |\n|\ntype: 1, |\n|\n}); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddRawButton(obj) { |\n|\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { |\n|\nthrow new TypeError('Buttons must be a plain object'); |\n|\n} |\n|\n|\n|\nthis._buttons.push(obj); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetRawThumbnail(thumbnail) { |\n|\nif (!thumbnail) throw new Error('Thumbnail needed'); |\n|\nthis._image = { base64: thumbnail, is_raw: true }; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetThumbnail(path) { |\n|\nif (!path) throw new Error('Url or buffer needed'); |\n|\nthis._image = path; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetMedia(obj) { |\n|\nif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { |\n|\nthrow new TypeError('Media must be a plain object'); |\n|\n} |\n|\n|\n|\nthis._data = obj; |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nasync build(jid, { messageId, ...options } = {}) { |\n|\nconst _thumbnail = this._image?.is_raw |\n|\n? this._image.base64 |\n|\n: this._image |\n|\n? await Toolkit.resize(Buffer.isBuffer(this._image) ? this._image : await Toolkit.fetchBuffer(this._image, {}, { silent: true }), 300, 300) |\n|\n: null; |\n|\nconst msg = generateWAMessageFromContent( |\n|\njid, |\n|\n{ |\n|\n...this._extraPayload, |\n|\nbuttonsMessage: { |\n|\ncontentText: this._body, |\n|\nfooterText: this._footer, |\n|\n...(this._data |\n|\n? this._data |\n|\n: { |\n|\nheaderType: 6, |\n|\nlocationMessage: { |\n|\ndegreesLatitude: 0, |\n|\ndegreesLongitude: 0, |\n|\nname: this._title, |\n|\naddress: this._subtitle, |\n|\njpegThumbnail: _thumbnail, |\n|\n}, |\n|\n}), |\n|\nviewOnce: true, |\n|\ncontextInfo: this._contextInfo, |\n|\nbuttons: [...this._buttons], |\n|\n}, |\n|\n}, |\n|\n{ messageId: messageId || generateMessageIDV2(), ...options } |\n|\n); |\n|\nreturn msg; |\n|\n} |\n|\n|\n|\nasync send(jid, { messageId, additionalNodes = [], ...options } = {}) { |\n|\nif (this._buttons.length < 1) throw new Error('ButtonV2 requires at least one button'); |\n|\nconst msg = await this.build(jid, { messageId, ...options }); |\n|\n|\n|\nawait this.#client.relayMessage(msg.key.remoteJid, msg.message, { |\n|\nmessageId: msg.key.id, |\n|\nadditionalNodes: [ |\n|\n{ |\n|\ntag: 'biz', |\n|\nattrs: {}, |\n|\ncontent: [ |\n|\n{ |\n|\ntag: 'interactive', |\n|\nattrs: { type: 'native_flow', v: '1' }, |\n|\ncontent: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }], |\n|\n}, |\n|\n], |\n|\n}, |\n|\n...additionalNodes, |\n|\n], |\n|\n...options, |\n|\n}); |\n|\nreturn msg; |\n|\n} |\n|\n} |\n|\n|\n|\nclass Carousel extends BaseBuilder { |\n|\n#client; |\n|\n|\n|\nconstructor(client) { |\n|\nsuper(); |\n|\nif (!client) { |\n|\nthrow new Error('Socket is required'); |\n|\n} |\n|\n|\n|\nthis.#client = client; |\n|\nthis._cards = []; |\n|\n} |\n|\n|\n|\nloadFrom(msg) { |\n|\nif (!msg) throw new Error('interactiveMessage needed'); |\n|\nif (!msg.interactiveMessage) throw new Error('interactiveMessage not found'); |\n|\n|\n|\nconst { interactiveMessage, ...extraPayload } = msg; |\n|\nconst iM = interactiveMessage; |\n|\nconst carousel = iM.carouselMessage || {}; |\n|\n|\n|\nthis._body = iM.body?.text || ''; |\n|\nthis._footer = iM.footer?.text || ''; |\n|\nthis._contextInfo = iM.contextInfo || {}; |\n|\nthis._extraPayload = extraPayload; |\n|\n|\n|\nthis._cards = Array.isArray(carousel.cards) |\n|\n? carousel.cards.map((card) => ({ |\n|\n...card, |\n|\nheader: { |\n|\n...(card.header || {}), |\n|\nhasMediaAttachment: !!card.header?.hasMediaAttachment, |\n|\n...(card.header?.imageMessage ? { imageMessage: card.header.imageMessage } : {}), |\n|\n...(card.header?.videoMessage ? { videoMessage: card.header.videoMessage } : {}), |\n|\n}, |\n|\nbody: { |\n|\ntext: card.body?.text || '', |\n|\n}, |\n|\nfooter: { |\n|\ntext: card.footer?.text || '', |\n|\n}, |\n|\nnativeFlowMessage: { |\n|\n...(card.nativeFlowMessage || {}), |\n|\nbuttons: Array.isArray(card.nativeFlowMessage?.buttons) |\n|\n? card.nativeFlowMessage.buttons.map((button) => ({ |\n|\n...button, |\n|\nbuttonParamsJson: typeof button.buttonParamsJson === 'string' ? button.buttonParamsJson : JSON.stringify(button.buttonParamsJson || {}), |\n|\n})) |\n|\n: [], |\n|\nmessageParamsJson: |\n|\ntypeof card.nativeFlowMessage?.messageParamsJson === 'string' ? card.nativeFlowMessage.messageParamsJson : JSON.stringify(card.nativeFlowMessage?.messageParamsJson || {}), |\n|\n}, |\n|\n})) |\n|\n: []; |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddCard(card) { |\n|\nconst cards = Array.isArray(card) ? card : [card]; |\n|\nconst baseIndex = this._cards.length; |\n|\n|\n|\nfor (const [index, c] of cards.entries()) { |\n|\nif (!c?.header?.hasMediaAttachment) { |\n|\nthrow new Error(`Card [${baseIndex + index}] must include an image or video in header`); |\n|\n} |\n|\n} |\n|\n|\n|\nthis._cards.push(...cards); |\n|\nreturn this; |\n|\n} |\n|\n|\n|\nbuild(jid, { messageId, ...options } = {}) { |\n|\nreturn generateWAMessageFromContent( |\n|\njid, |\n|\n{ |\n|\n...this._extraPayload, |\n|\ninteractiveMessage: { |\n|\nheader: { |\n|\nhasMediaAttachment: false, |\n|\n}, |\n|\nbody: { text: this._body }, |\n|\nfooter: { text: this._footer }, |\n|\ncontextInfo: this._contextInfo, |\n|\ncarouselMessage: { |\n|\ncards: this._cards, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n{ messageId: messageId || generateMessageIDV2(), ...options } |\n|\n); |\n|\n} |\n|\n|\n|\nasync send(jid, { messageId, additionalNodes = [], ...options } = {}) { |\n|\nconst msg = this.build(jid, { messageId, ...options }); |\n|\n|\n|\nawait this.#client.relayMessage(msg.key.remoteJid, msg.message, { |\n|\nmessageId: msg.key.id, |\n|\nadditionalNodes: [ |\n|\n{ |\n|\ntag: 'biz', |\n|\nattrs: {}, |\n|\ncontent: [ |\n|\n{ |\n|\ntag: 'interactive', |\n|\nattrs: { type: 'native_flow', v: '1' }, |\n|\ncontent: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }], |\n|\n}, |\n|\n], |\n|\n}, |\n|\n...additionalNodes, |\n|\n], |\n|\n...options, |\n|\n}); |\n|\nreturn msg; |\n|\n} |\n|\n} |\n|\n|\n|\nclass AIRich extends BaseBuilder { |\n|\n#client; |\n|\n|\n|\nconstructor(client, { dynamic = true, unsupportedTypeAlert = true } = {}) { |\n|\nif (!client) { |\n|\nthrow new Error('Socket is required'); |\n|\n} |\n|\n|\n|\nsuper(); |\n|\nthis.#client = client; |\n|\nthis._contextInfo = {}; |\n|\nthis._nodes = []; |\n|\nthis._idIndex = new Map(); |\n|\nthis._unsupportedTypeAlert = !!unsupportedTypeAlert; |\n|\nthis._dynamic = !!dynamic; |\n|\nthis._responseId = crypto.randomUUID(); |\n|\nthis._botResponseId = crypto.randomUUID(); |\n|\nthis._lastMessageKey = null; |\n|\n} |\n|\n|\n|\nloadFrom(msg) { |\n|\nif (!msg) throw new Error('AI Rich message needed'); |\n|\n|\n|\nconst message = msg.message ?? msg; |\n|\n|\n|\nlet richResponseMessage = message?.botForwardedMessage?.message?.richResponseMessage; |\n|\n|\n|\nif (!richResponseMessage) { |\n|\nrichResponseMessage = message?.botForwardedMessage?.richResponseMessage; |\n|\n} |\n|\n|\n|\nif (!richResponseMessage) { |\n|\nrichResponseMessage = message?.richResponseMessage; |\n|\n} |\n|\n|\n|\nif (!richResponseMessage) { |\n|\nthrow new Error('richResponseMessage not found'); |\n|\n} |\n|\n|\n|\nconst messageContextInfo = message?.messageContextInfo ?? {}; |\n|\nconst botMetadata = messageContextInfo?.botMetadata ?? {}; |\n|\n|\n|\nthis._title = botMetadata?.messageDisclaimerText ?? ''; |\n|\n|\n|\nthis._contextInfo = structuredClone(richResponseMessage?.contextInfo ?? {}); |\n|\n|\n|\nconst loadedSubmessages = Array.isArray(richResponseMessage?.submessages) ? structuredClone(richResponseMessage.submessages) : []; |\n|\n|\n|\nlet loadedSections = []; |\n|\n|\n|\nconst unifiedData = richResponseMessage?.unifiedResponse?.data; |\n|\n|\n|\nif (unifiedData) { |\n|\ntry { |\n|\nconst decoded = Buffer.from(unifiedData, 'base64').toString('utf8'); |\n|\nconst unifiedResponse = JSON.parse(decoded); |\n|\n|\n|\nif (Array.isArray(unifiedResponse?.sections)) { |\n|\nloadedSections = structuredClone(unifiedResponse.sections); |\n|\n} |\n|\n} catch {} |\n|\n} |\n|\n|\n|\nthis._nodes = []; |\n|\nthis._idIndex = new Map(); |\n|\n|\n|\nconst maxLength = Math.max(loadedSections.length, loadedSubmessages.length); |\n|\n|\n|\nfor (let i = 0; i < maxLength; i++) { |\n|\nthis._nodes.push({ |\n|\nid: null, |\n|\nsection: loadedSections[i] ?? null, |\n|\nsubmessage: loadedSubmessages[i] ?? null, |\n|\n}); |\n|\n} |\n|\n|\n|\nthis._extraPayload = {}; |\n|\n|\n|\nfor (const [key, value] of Object.entries(message)) { |\n|\nif (key !== 'messageContextInfo' && key !== 'botForwardedMessage' && key !== 'richResponseMessage') { |\n|\nthis._extraPayload[key] = structuredClone(value); |\n|\n} |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetResponseId(id) { |\n|\nif (typeof id !== 'string') { |\n|\nthrow new TypeError('ID must be a string'); |\n|\n} |\n|\nthis._responseId = id; |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nrefreshResponseId() { |\n|\nthis._responseId = crypto.randomUUID(); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nsetBotResponseId(id) { |\n|\nif (typeof id !== 'string') { |\n|\nthrow new TypeError('ID must be a string'); |\n|\n} |\n|\nthis._botResponseId = id; |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nrefreshBotResponseId() { |\n|\nthis._botResponseId = crypto.randomUUID(); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\ncreateAlert(type) { |\n|\nif (this._unsupportedTypeAlert) { |\n|\nreturn { |\n|\nmessageType: 2, |\n|\nmessageText: `[ UNSUPPORTED_TYPE - ${type}]`, |\n|\n}; |\n|\n} |\n|\n|\n|\nreturn undefined; |\n|\n} |\n|\n|\n|\naddText(text, { hyperlink = true, citation = true, latex = true, id, replace, insertAt } = {}) { |\n|\nif (typeof text !== 'string') { |\n|\nthrow new TypeError('Text must be a string'); |\n|\n} |\n|\n|\n|\nconst { text: extractedText, inline_entities } = extractIE(text, { |\n|\nhyperlink, |\n|\ncitation, |\n|\nlatex, |\n|\n}); |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\ntext: extractedText, |\n|\n...(inline_entities.length && { inline_entities }), |\n|\n__typename: 'GenAIMarkdownTextUXPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessages = [ |\n|\n{ |\n|\nmessageType: 2, |\n|\nmessageText: text, |\n|\n}, |\n|\n].filter(Boolean); |\n|\n|\n|\nreturn this._addContent(section, submessages, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddFOAText(text, { id, replace, insertAt } = {}) { |\n|\nif (typeof text !== 'string') { |\n|\nthrow new TypeError('Text must be a string'); |\n|\n} |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\ntext, |\n|\n__typename: 'FOATextPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessages = [ |\n|\n{ |\n|\nmessageType: 2, |\n|\nmessageText: text, |\n|\n}, |\n|\n]; |\n|\n|\n|\nreturn this._addContent(section, submessages, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddCode(language, code, { id, replace, insertAt } = {}) { |\n|\nif (typeof language !== 'string' || typeof code !== 'string') { |\n|\nthrow new TypeError('Language and code must be a string'); |\n|\n} |\n|\n|\n|\nconst meta = AIRich.tokenizer(code, language); |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\nlanguage, |\n|\ncode_blocks: meta.unified_codeBlock, |\n|\n__typename: 'GenAICodeUXPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessages = [ |\n|\n{ |\n|\nmessageType: 5, |\n|\ncodeMetadata: { |\n|\ncodeLanguage: language, |\n|\ncodeBlocks: meta.codeBlock, |\n|\n}, |\n|\n}, |\n|\n]; |\n|\n|\n|\nreturn this._addContent(section, submessages, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddTable(table, { hyperlink = true, citation = true, latex = true, id, replace, insertAt } = {}) { |\n|\nif (!Array.isArray(table)) { |\n|\nthrow new TypeError('Table must be an array'); |\n|\n} |\n|\n|\n|\nconst meta = AIRich.toTableMetadata(table, { |\n|\nhyperlink, |\n|\ncitation, |\n|\nlatex, |\n|\n}); |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\nrows: meta.unified_rows, |\n|\n__typename: 'GenATableUXPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessages = [ |\n|\n{ |\n|\nmessageType: 4, |\n|\ntableMetadata: { |\n|\ntitle: meta.title, |\n|\nrows: meta.rows, |\n|\n}, |\n|\n}, |\n|\n]; |\n|\n|\n|\nreturn this._addContent(section, submessages, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddSource(sources = [], { id, replace, insertAt } = {}) { |\n|\nif (!Array.isArray(sources)) { |\n|\nthrow new TypeError('Sources must be an array of strings, arrays, or objects'); |\n|\n} |\n|\n|\n|\nconst isStringArray = sources.every((item) => typeof item === 'string'); |\n|\n|\n|\nconst isArrayFormat = sources.every((item) => Array.isArray(item) && item.every((value) => typeof value === 'string')); |\n|\n|\n|\nconst isObjectFormat = sources.every((item) => item && typeof item === 'object' && !Array.isArray(item)); |\n|\n|\n|\nif (!isStringArray && !isArrayFormat && !isObjectFormat) { |\n|\nthrow new TypeError('Sources must be a string array, array of string arrays, or array of objects'); |\n|\n} |\n|\n|\n|\nif (isStringArray) { |\n|\nsources = [sources]; |\n|\n} |\n|\n|\n|\nconst normalizedSources = sources.map((source) => { |\n|\nif (Array.isArray(source)) { |\n|\nconst [icon, url, title, subtitle] = source; |\n|\n|\n|\nreturn { |\n|\nicon, |\n|\nurl, |\n|\ntitle, |\n|\nsubtitle, |\n|\n}; |\n|\n} |\n|\n|\n|\nreturn { |\n|\nicon: source.favicon ?? source.icon ?? '', |\n|\nurl: source.url ?? '', |\n|\ntitle: source.title ?? '', |\n|\nsubtitle: source.subtitle ?? '', |\n|\n}; |\n|\n}); |\n|\n|\n|\nconst source = normalizedSources.map(({ icon, url, title, subtitle }) => ({ |\n|\nsource_type: 'THIRD_PARTY', |\n|\nsource_display_name: title, |\n|\nsource_subtitle: subtitle, |\n|\nsource_url: url, |\n|\nfavicon: { |\n|\nurl: Toolkit.resolveMedia(this.#client, icon, 'image'), |\n|\nmime_type: 'image/jpeg', |\n|\nwidth: 16, |\n|\nheight: 16, |\n|\n}, |\n|\n})); |\n|\n|\n|\nconst submessage = this.createAlert('GenAISearchResultPrimitive'); |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\nsources: source, |\n|\n__typename: 'GenAISearchResultPrimitive', |\n|\n}); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddReels(reelsItems = [], { id, replace, insertAt } = {}) { |\n|\nif ( |\n|\n!( |\n|\n(reelsItems && typeof reelsItems === 'object' && !Array.isArray(reelsItems)) || |\n|\n(Array.isArray(reelsItems) && reelsItems.every((item) => item && typeof item === 'object' && !Array.isArray(item))) |\n|\n) |\n|\n) { |\n|\nthrow new TypeError('Reels items must be an object or an array of objects'); |\n|\n} |\n|\n|\n|\nconst items = Array.isArray(reelsItems) ? reelsItems : [reelsItems]; |\n|\n|\n|\nconst reels = items.map((item) => ({ |\n|\n...item, |\n|\n_avatar: Toolkit.resolveMedia(this.#client, item.profileIconUrl ?? item.profile_url ?? item.profile ?? '', 'image'), |\n|\n_thumbnail: Toolkit.resolveMedia(this.#client, item.thumbnailUrl ?? item.thumbnail ?? '', 'image'), |\n|\n})); |\n|\n|\n|\nconst section = AIRich.newLayout( |\n|\n'HScroll', |\n|\nreels.map((item) => ({ |\n|\nreels_url: item.videoUrl ?? item.url ?? '', |\n|\nthumbnail_url: item._thumbnail, |\n|\ncreator: item.username ?? item.title ?? '', |\n|\navatar_url: item._avatar, |\n|\nreels_title: item.reels_title ?? item.title ?? '', |\n|\nlikes_count: item.likes_count ?? item.like ?? 0, |\n|\nshares_count: item.shares_count ?? item.share ?? 0, |\n|\nview_count: item.view_count ?? item.view ?? 0, |\n|\nreel_source: item.reel_source ?? item.source ?? 'IG', |\n|\nis_verified: !!(item.is_verified || item.verified), |\n|\n__typename: 'GenAIReelPrimitive', |\n|\n})) |\n|\n); |\n|\n|\n|\nconst submessages = [ |\n|\n{ |\n|\nmessageType: 9, |\n|\ncontentItemsMetadata: { |\n|\ncontentType: 1, |\n|\nitemsMetadata: reels.map((item) => ({ |\n|\nreelItem: { |\n|\ntitle: item.username ?? '', |\n|\nprofileIconUrl: item._avatar, |\n|\nthumbnailUrl: item._thumbnail, |\n|\nvideoUrl: item.videoUrl ?? item.url ?? '', |\n|\n}, |\n|\n})), |\n|\n}, |\n|\n}, |\n|\n]; |\n|\n|\n|\nreturn this._addContent(section, submessages, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddImage(imageUrl, { width, height, status = 'READY', update_text, resolveUrl = false, id, replace, insertAt } = {}) { |\n|\nif (!(typeof imageUrl === 'string' || Buffer.isBuffer(imageUrl) || (Array.isArray(imageUrl) && imageUrl.every((v) => typeof v === 'string' || Buffer.isBuffer(v))))) { |\n|\nthrow new TypeError('imageUrl must be string | buffer | array of string/buffer'); |\n|\n} |\n|\n|\n|\nconst list = Array.isArray(imageUrl) |\n|\n? imageUrl.map((v) => { |\n|\nconst url = Toolkit.resolveMedia(this.#client, v, 'image', { resolveUrl }); |\n|\n|\n|\nreturn { |\n|\nimagePreviewUrl: url, |\n|\nimageHighResUrl: url, |\n|\nsourceUrl: url, |\n|\n}; |\n|\n}) |\n|\n: (() => { |\n|\nconst url = Toolkit.resolveMedia(this.#client, imageUrl, 'image', { resolveUrl }); |\n|\n|\n|\nreturn [ |\n|\n{ |\n|\nimagePreviewUrl: url, |\n|\nimageHighResUrl: url, |\n|\nsourceUrl: url, |\n|\n}, |\n|\n]; |\n|\n})(); |\n|\n|\n|\nconst sections = list.map(({ imagePreviewUrl }) => |\n|\nAIRich.newLayout('Single', { |\n|\nmedia: { |\n|\nurl: imagePreviewUrl, |\n|\nmime_type: 'image/png', |\n|\nwidth, |\n|\nheight, |\n|\n}, |\n|\nimagine_type: 'IMAGE', |\n|\nstatus: { |\n|\nstatus, |\n|\nupdate_text, |\n|\n}, |\n|\n__typename: 'GenAIImaginePrimitive', |\n|\n}) |\n|\n); |\n|\n|\n|\nconst submessage = { |\n|\nmessageType: 1, |\n|\ngridImageMetadata: { |\n|\ngridImageUrl: { |\n|\nimagePreviewUrl: list[0]?.imagePreviewUrl, |\n|\n}, |\n|\nimageUrls: list, |\n|\n}, |\n|\n}; |\n|\n|\n|\nif (id && sections.length !== 1) { |\n|\nthrow new Error('Cannot assign one id to multiple image sections'); |\n|\n} |\n|\n|\n|\nreturn this._addContent(sections, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddVideo(videoUrl, { autoFill = true, status = 'READY', estimatedTime, id, replace, insertAt } = {}) { |\n|\nconst isObjectVideo = (v) => v && typeof v === 'object' && !Array.isArray(v) && v.url; |\n|\n|\n|\nconst isValidPrimitive = |\n|\ntypeof videoUrl === 'string' || |\n|\nBuffer.isBuffer(videoUrl) || |\n|\nisObjectVideo(videoUrl) || |\n|\n(Array.isArray(videoUrl) && videoUrl.every((v) => typeof v === 'string' || Buffer.isBuffer(v) || isObjectVideo(v))); |\n|\n|\n|\nif (!isValidPrimitive) { |\n|\nthrow new TypeError('videoUrl must be string | buffer | object | array'); |\n|\n} |\n|\n|\n|\nconst items = Array.isArray(videoUrl) ? videoUrl : [videoUrl]; |\n|\n|\n|\nconst alert = this.createAlert('GenAIImaginePrimitive (ANIMATE)'); |\n|\n|\n|\nconst sections = []; |\n|\nconst submessages = []; |\n|\n|\n|\nfor (const item of items) { |\n|\nconst isObject = isObjectVideo(item); |\n|\n|\n|\nconst url = isObject ? Toolkit.resolveMedia(this.#client, item.url ?? '', 'video') : Toolkit.resolveMedia(this.#client, item, 'video'); |\n|\n|\n|\nconst bufferPromise = autoFill ? Promise.resolve(url).then((u) => Toolkit.fetchBuffer(u)) : null; |\n|\n|\n|\nconst file_length = isObject && item.file_length != null ? item.file_length : autoFill ? bufferPromise.then((b) => b?.length ?? 0) : 0; |\n|\n|\n|\nconst duration = |\n|\nisObject && item.duration != null |\n|\n? item.duration |\n|\n: autoFill |\n|\n? bufferPromise.then((b) => |\n|\nToolkit.getMp4Duration(b, { |\n|\nsilent: true, |\n|\n}) |\n|\n) |\n|\n: 0; |\n|\n|\n|\nconst thumbnail = |\n|\nisObject && item.thumbnail |\n|\n? Toolkit.resolveMedia(this.#client, item.thumbnail, 'image', { |\n|\nresult: 'base64', |\n|\nresize: true, |\n|\nwidth: 300, |\n|\nheight: 300, |\n|\n}) |\n|\n: autoFill |\n|\n? bufferPromise?.then((b) => |\n|\nToolkit.getMp4Preview(b, { |\n|\ntime: 0, |\n|\nresult: 'base64', |\n|\n}) |\n|\n) |\n|\n: null; |\n|\n|\n|\nsections.push( |\n|\nAIRich.newLayout('Single', { |\n|\nmedia: { |\n|\nurl, |\n|\nmime_type: isObject ? (item.mime_type ?? 'video/mp4') : 'video/mp4', |\n|\nfile_length, |\n|\nduration, |\n|\n}, |\n|\nimagine_type: 'ANIMATE', |\n|\nstatus: { |\n|\nstatus, |\n|\nestimated_completion_time: estimatedTime != null ? Math.floor((Date.now() + estimatedTime) / 1000) : undefined, |\n|\n}, |\n|\nthumbnail: { |\n|\nraw_media: thumbnail, |\n|\n}, |\n|\n__typename: 'GenAIImaginePrimitive', |\n|\n}) |\n|\n); |\n|\n} |\n|\n|\n|\nif (alert !== undefined) { |\n|\nsubmessages.push(alert); |\n|\n} |\n|\n|\n|\nif (submessages.length > 1) { |\n|\nthrow new Error('Video content can only have one submessage'); |\n|\n} |\n|\n|\n|\nreturn this._addContent(sections, submessages[0], { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddProduct(data = {}, { id, replace, insertAt } = {}) { |\n|\nif (!((data && typeof data === 'object' && !Array.isArray(data)) || (Array.isArray(data) && data.every((item) => item && typeof item === 'object' && !Array.isArray(item))))) { |\n|\nthrow new TypeError('Product items must be an object or an array of objects'); |\n|\n} |\n|\n|\n|\nconst items = Array.isArray(data) ? data : [data]; |\n|\n|\n|\nconst product = items.map((item) => ({ |\n|\ntitle: item.title, |\n|\nbrand: item.brand, |\n|\nprice: item.price, |\n|\nsale_price: item.sale_price, |\n|\nproduct_url: item.product_url ?? item.url, |\n|\nimage: { |\n|\nurl: Toolkit.resolveMedia(this.#client, item.image_url ?? item.image, 'image'), |\n|\n}, |\n|\nadditional_images: [ |\n|\n{ |\n|\nurl: Toolkit.resolveMedia(this.#client, item.icon_url ?? item.icon, 'image'), |\n|\n}, |\n|\n], |\n|\n__typename: 'GenAIProductItemCardPrimitive', |\n|\n})); |\n|\n|\n|\nconst section = AIRich.newLayout(Array.isArray(data) ? 'HScroll' : 'Single', Array.isArray(data) ? product : product[0]); |\n|\n|\n|\nconst submessage = this.createAlert('GenAIProductItemCardPrimitive'); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddPost(data = {}, { id, replace, insertAt } = {}) { |\n|\nif (!((data && typeof data === 'object' && !Array.isArray(data)) || (Array.isArray(data) && data.every((item) => item && typeof item === 'object' && !Array.isArray(item))))) { |\n|\nthrow new TypeError('Post items must be an object or an array of objects'); |\n|\n} |\n|\n|\n|\nconst posts = Array.isArray(data) ? data : [data]; |\n|\n|\n|\nconst primitives = posts.map((p) => ({ |\n|\ntitle: p.title ?? '', |\n|\nsubtitle: p.subtitle ?? '', |\n|\nusername: p.username ?? '', |\n|\nprofile_picture_url: Toolkit.resolveMedia(this.#client, p.profile_picture_url ?? p.profile_url ?? p.profile ?? '', 'image'), |\n|\nis_verified: !!(p.is_verified || p.verified), |\n|\nthumbnail_url: Toolkit.resolveMedia(this.#client, p.thumbnail_url ?? p.thumbnail ?? '', 'image'), |\n|\npost_caption: p.post_caption ?? p.caption ?? '', |\n|\nlikes_count: p.likes_count ?? p.like ?? 0, |\n|\ncomments_count: p.comments_count ?? p.comment ?? 0, |\n|\nshares_count: p.shares_count ?? p.share ?? 0, |\n|\npost_url: p.post_url ?? p.url ?? '', |\n|\npost_deeplink: p.post_deeplink ?? p.deeplink ?? '', |\n|\nsource_app: p.source_app || p.source || 'INSTAGRAM', |\n|\nfooter_label: p.footer_label ?? p.footer ?? '', |\n|\nfooter_icon: Toolkit.resolveMedia(this.#client, p.footer_icon ?? p.icon ?? '', 'image'), |\n|\nis_carousel: posts.length > 1, |\n|\norientation: p.orientation ?? 'LANDSCAPE', |\n|\npost_type: p.post_type ?? 'VIDEO', |\n|\n__typename: 'GenAIPostPrimitive', |\n|\n})); |\n|\n|\n|\nconst section = AIRich.newLayout('HScroll', primitives); |\n|\n|\n|\nconst submessage = this.createAlert('GenAIPostPrimitive'); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddMetadata(text, { id, replace, insertAt } = {}) { |\n|\nif (typeof text !== 'string') { |\n|\nthrow new TypeError('Text must be a string'); |\n|\n} |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\ntext, |\n|\n__typename: 'GenAIMetadataTextPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessage = { |\n|\nmessageType: 2, |\n|\nmessageText: text, |\n|\n}; |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddTip(text, { id, replace, insertAt } = {}) { |\n|\nif (typeof text !== 'string') { |\n|\nthrow new TypeError('Text must be a string'); |\n|\n} |\n|\n|\n|\nconst section = AIRich.newLayout('Single', { |\n|\ntext: 'ⓘ ' + text, |\n|\n__typename: 'GenAIMetadataTextPrimitive', |\n|\n}); |\n|\n|\n|\nconst submessage = { |\n|\nmessageType: 2, |\n|\nmessageText: text, |\n|\n}; |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddWidget(data, { layout, id, replace, insertAt, ...options } = {}) { |\n|\nif (!((data && typeof data === 'object' && !Array.isArray(data)) || (Array.isArray(data) && data.every((item) => item && typeof item === 'object' && !Array.isArray(item))))) { |\n|\nthrow new TypeError('Widget must be an object or an array of objects'); |\n|\n} |\n|\n|\n|\nconst isArray = Array.isArray(data); |\n|\n|\n|\nconst items = isArray ? data : [data]; |\n|\n|\n|\nconst widgets = items.map((item) => ({ |\n|\n__typename: 'GenAI3PExtWidgetPrimitive', |\n|\n|\n|\nheader: { |\n|\n__typename: 'GenAI3PExtWidgetStandardHeader', |\n|\ntitle: item.title ?? '', |\n|\n...(item.header ?? {}), |\n|\n}, |\n|\n|\n|\nbody: { |\n|\n__typename: 'GenAI3PExtCalendarEventList', |\n|\nsections: item.sections ?? [], |\n|\n|\n|\nctas: (item.actions ?? []).map((action) => ({ |\n|\n__typename: 'GenAI3PExtWidgetCTA', |\n|\nlabel: action.label ?? '', |\n|\nstate: action.state ?? 'PENDING', |\n|\nkind: action.kind ?? 'OTHER', |\n|\ntool_call_id: action.tool_call_id ?? action.id ?? '', |\n|\n|\n|\n...(action.toast && { |\n|\ntoast: { |\n|\n__typename: 'GenAI3PExtWidgetToast', |\n|\nlabel: action.toast.label ?? action.label ?? '', |\n|\n}, |\n|\n}), |\n|\n})), |\n|\n|\n|\n...(item.body ?? {}), |\n|\n}, |\n|\n})); |\n|\n|\n|\nconst section = AIRich.newLayout(layout ?? (isArray ? 'HScroll' : 'Single'), isArray ? widgets : widgets[0], options); |\n|\n|\n|\nconst submessage = this.createAlert('GenAI3PExtWidgetStandardHeader'); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddFooterAction(data, { layout, id, replace, insertAt, ...options } = {}) { |\n|\nif (!((data && typeof data === 'object' && !Array.isArray(data)) || (Array.isArray(data) && data.every((item) => item && typeof item === 'object' && !Array.isArray(item))))) { |\n|\nthrow new TypeError('Footer action must be an object or an array of objects'); |\n|\n} |\n|\n|\n|\nconst isArray = Array.isArray(data); |\n|\n|\n|\nconst items = isArray ? data : [data]; |\n|\n|\n|\nconst actions = items.map((item) => ({ |\n|\n__typename: 'GenAIFooterActionPrimitive', |\n|\n|\n|\ncta_text: item.text ?? item.cta_text ?? '', |\n|\n|\n|\ncta_type: item.type ?? item.cta_type ?? 'OPEN_URL', |\n|\n|\n|\ncta_url: item.url ?? item.cta_url ?? '', |\n|\n})); |\n|\n|\n|\nconst section = AIRich.newLayout(layout ?? (isArray ? 'HScroll' : 'Single'), isArray ? actions : actions[0], options); |\n|\n|\n|\nconst submessage = this.createAlert('GenAIFooterActionPrimitive'); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\naddSuggest(suggestion, { scroll = true, layout, id, replace, insertAt } = {}) { |\n|\nif (!(typeof suggestion === 'string' || (Array.isArray(suggestion) && suggestion.every((v) => typeof v === 'string')))) { |\n|\nthrow new TypeError('Suggestion must be a string or array of strings'); |\n|\n} |\n|\n|\n|\nconst suggest = Array.isArray(suggestion) |\n|\n? suggestion.map((text) => ({ |\n|\nprompt_text: text, |\n|\nprompt_type: 'SUGGESTED_PROMPT', |\n|\n__typename: 'GenAIFollowUpSuggestionPillPrimitive', |\n|\n})) |\n|\n: [ |\n|\n{ |\n|\nprompt_text: suggestion, |\n|\nprompt_type: 'SUGGESTED_PROMPT', |\n|\n__typename: 'GenAIFollowUpSuggestionPillPrimitive', |\n|\n}, |\n|\n]; |\n|\n|\n|\nconst type = layout ?? (suggest.length === 1 ? 'Single' : scroll ? 'HScroll' : 'ActionRow'); |\n|\n|\n|\nconst section = AIRich.newLayout(type, type === 'Single' ? suggest[0] : suggest, { |\n|\n__typename: 'GenAIUnifiedResponseSection', |\n|\n}); |\n|\n|\n|\nconst submessage = this.createAlert('GenAIFollowUpSuggestionPillPrimitive'); |\n|\n|\n|\nreturn this._addContent(section, submessage, { |\n|\nid, |\n|\nreplace, |\n|\ninsertAt, |\n|\n}); |\n|\n} |\n|\n|\n|\nasync build( |\n|\njid, |\n|\n{ bypassDownload = true, forwarded = true, notification = false, includesUnifiedResponse = true, includesSubmessages = true, quoted, quotedParticipant, messageId, ...options } = {} |\n|\n) { |\n|\nconst forward = forwarded |\n|\n? { |\n|\nforwardingScore: 1, |\n|\nisForwarded: true, |\n|\nforwardedAiBotMessageInfo: { botJid: '867051314767696@bot' }, |\n|\nforwardOrigin: 4, |\n|\n} |\n|\n: {}; |\n|\n|\n|\nconst notif = notification |\n|\n? { |\n|\nsessionTransparencyMetadata: { |\n|\ndisclaimerText: '~ Ahmad tumbuh kembang', |\n|\nhcaId: `hca_${Date.now()}`, |\n|\nsessionTransparencyType: 1, |\n|\n}, |\n|\n} |\n|\n: {}; |\n|\n|\n|\nconst qObj = quoted |\n|\n? { |\n|\nstanzaId: quoted?.key?.id || quoted?.id, |\n|\nparticipant: quotedParticipant || quoted?.key?.participant || quoted?.participant || quoted?.key?.remoteJid, |\n|\nquotedType: 0, |\n|\nquotedMessage: typeof quoted === 'object' && quoted !== null ? (quoted.message ?? quoted) : undefined, |\n|\n} |\n|\n: {}; |\n|\n|\n|\nconst sections = this._footer |\n|\n? [ |\n|\n...(await waitAllPromises(this._sections)), |\n|\nAIRich.newLayout('Single', { |\n|\ntext: this._footer, |\n|\n__typename: 'GenAIMetadataTextPrimitive', |\n|\n}), |\n|\n] |\n|\n: [...(await waitAllPromises(this._sections))]; |\n|\n|\n|\nif (this._dynamic) { |\n|\nthis.refreshResponseId(); |\n|\nthis.refreshBotResponseId(); |\n|\n} |\n|\n|\n|\nreturn generateWAMessageFromContent( |\n|\njid, |\n|\n{ |\n|\nmessageContextInfo: { |\n|\ndeviceListMetadata: {}, |\n|\ndeviceListMetadataVersion: 2, |\n|\nbotMetadata: { |\n|\nmessageDisclaimerText: this._title, |\n|\n...notif, |\n|\nverificationMetadata: AIRich.generateVerificationMetadata(), |\n|\nbotResponseId: this._botResponseId, |\n|\n}, |\n|\n}, |\n|\n...this._extraPayload, |\n|\nbotForwardedMessage: { |\n|\nmessage: { |\n|\nrichResponseMessage: { |\n|\nmessageType: 1, |\n|\nsubmessages: includesSubmessages ? await waitAllPromises(this._submessages) : [], |\n|\nunifiedResponse: { |\n|\ndata: includesUnifiedResponse ? Buffer.from(Toolkit.stringifyEscaped({ response_id: this._responseId, sections })).toString('base64') : '', |\n|\n}, |\n|\ncontextInfo: { |\n|\n...forward, |\n|\n...qObj, |\n|\n...this._contextInfo, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n{ messageId: messageId || generateMessageIDV2(), ...options } |\n|\n); |\n|\n} |\n|\n|\n|\nasync buildEdit(targetJid, targetId, { msg, messageId, ...options } = {}) { |\n|\nif (!msg) { |\n|\nmsg = (await this.build(targetJid, options)).message; |\n|\n} |\n|\n|\n|\nconst editedMessage = msg; |\n|\n|\n|\nif (!editedMessage) { |\n|\nthrow new Error('buildEdit: msg does not contain botForwardedMessage'); |\n|\n} |\n|\n|\n|\nreturn generateWAMessageFromContent( |\n|\ntargetJid, |\n|\n{ |\n|\nbotForwardedMessage: { |\n|\nmessage: { |\n|\nprotocolMessage: { |\n|\nkey: { |\n|\nremoteJid: targetJid, |\n|\nfromMe: true, |\n|\nid: targetId, |\n|\n}, |\n|\ntype: 14, |\n|\neditedMessage, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n}, |\n|\n{ messageId: messageId || generateMessageIDV2(), ...options } |\n|\n); |\n|\n} |\n|\n|\n|\nasync sendEdit(jid, id, { msg, messageId, additionalNodes = [], ...options } = {}) { |\n|\njid = jid ?? this._lastMessageKey?.remoteJid; |\n|\nid = id ?? this._lastMessageKey?.id; |\n|\n|\n|\nif (!jid) { |\n|\nthrow new Error('JID is required'); |\n|\n} |\n|\n|\n|\nif (!id) { |\n|\nthrow new Error('Message id is required'); |\n|\n} |\n|\n|\n|\nconst msgEdit = await this.buildEdit(jid, id, { |\n|\nmsg, |\n|\nmessageId: messageId || generateMessageIDV2(), |\n|\n...options, |\n|\n}); |\n|\n|\n|\nawait this.#client.relayMessage(jid, msgEdit.message, { |\n|\nmessageId: msgEdit.key.id, |\n|\nadditionalNodes, |\n|\n}); |\n|\n|\n|\nreturn msgEdit; |\n|\n} |\n|\n|\n|\nasync send(jid, { bypassDownload = true, forwarded = true, notification = false, includesUnifiedResponse = true, includesSubmessages = true, messageId, additionalNodes = [], ...options } = {}) { |\n|\nconst msg = await this.build(jid, { |\n|\nforwarded, |\n|\nnotification, |\n|\nincludesUnifiedResponse, |\n|\nincludesSubmessages, |\n|\nmessageId, |\n|\n...options, |\n|\n}); |\n|\n|\n|\nawait this.#client.relayMessage(msg.key.remoteJid, msg.message, { |\n|\nmessageId: msg.key.id, |\n|\nadditionalNodes, |\n|\n...options, |\n|\n}); |\n|\n|\n|\nif (includesUnifiedResponse && bypassDownload) { |\n|\nawait this.sendEdit(jid, msg.key.id, { |\n|\nmsg: msg.message, |\n|\n}); |\n|\n} |\n|\n|\n|\nthis._lastMessageKey = msg.key; |\n|\n|\n|\nreturn msg; |\n|\n} |\n|\n|\n|\nstatic tokenizer(code, lang = 'javascript') { |\n|\nconst keywordsMap = { |\n|\njavascript: new Set([ |\n|\n'break', |\n|\n'case', |\n|\n'catch', |\n|\n'continue', |\n|\n'debugger', |\n|\n'delete', |\n|\n'do', |\n|\n'else', |\n|\n'finally', |\n|\n'for', |\n|\n'function', |\n|\n'if', |\n|\n'in', |\n|\n'instanceof', |\n|\n'new', |\n|\n'return', |\n|\n'switch', |\n|\n'this', |\n|\n'throw', |\n|\n'try', |\n|\n'typeof', |\n|\n'var', |\n|\n'void', |\n|\n'while', |\n|\n'with', |\n|\n'true', |\n|\n'false', |\n|\n'null', |\n|\n'undefined', |\n|\n'class', |\n|\n'const', |\n|\n'let', |\n|\n'super', |\n|\n'extends', |\n|\n'export', |\n|\n'import', |\n|\n'yield', |\n|\n'static', |\n|\n'constructor', |\n|\n'async', |\n|\n'await', |\n|\n'get', |\n|\n'set', |\n|\n]), |\n|\n|\n|\ntypescript: new Set([ |\n|\n'abstract', |\n|\n'any', |\n|\n'as', |\n|\n'asserts', |\n|\n'bigint', |\n|\n'boolean', |\n|\n'declare', |\n|\n'enum', |\n|\n'implements', |\n|\n'infer', |\n|\n'interface', |\n|\n'is', |\n|\n'keyof', |\n|\n'module', |\n|\n'namespace', |\n|\n'never', |\n|\n'readonly', |\n|\n'require', |\n|\n'number', |\n|\n'object', |\n|\n'override', |\n|\n'private', |\n|\n'protected', |\n|\n'public', |\n|\n'satisfies', |\n|\n'string', |\n|\n'symbol', |\n|\n'type', |\n|\n'unknown', |\n|\n'using', |\n|\n'from', |\n|\n'break', |\n|\n'case', |\n|\n'catch', |\n|\n'continue', |\n|\n'do', |\n|\n'else', |\n|\n'finally', |\n|\n'for', |\n|\n'function', |\n|\n'if', |\n|\n'new', |\n|\n'return', |\n|\n'switch', |\n|\n'this', |\n|\n'throw', |\n|\n'try', |\n|\n'var', |\n|\n'void', |\n|\n'while', |\n|\n'class', |\n|\n'const', |\n|\n'let', |\n|\n'extends', |\n|\n'import', |\n|\n'export', |\n|\n'async', |\n|\n'await', |\n|\n]), |\n|\n|\n|\npython: new Set([ |\n|\n'False', |\n|\n'None', |\n|\n'True', |\n|\n'and', |\n|\n'as', |\n|\n'assert', |\n|\n'async', |\n|\n'await', |\n|\n'break', |\n|\n'class', |\n|\n'continue', |\n|\n'def', |\n|\n'del', |\n|\n'elif', |\n|\n'else', |\n|\n'except', |\n|\n'finally', |\n|\n'for', |\n|\n'from', |\n|\n'global', |\n|\n'if', |\n|\n'import', |\n|\n'in', |\n|\n'is', |\n|\n'lambda', |\n|\n'nonlocal', |\n|\n'not', |\n|\n'or', |\n|\n'pass', |\n|\n'raise', |\n|\n'return', |\n|\n'try', |\n|\n'while', |\n|\n'with', |\n|\n'yield', |\n|\n]), |\n|\n|\n|\njava: new Set([ |\n|\n'abstract', |\n|\n'assert', |\n|\n'boolean', |\n|\n'break', |\n|\n'byte', |\n|\n'case', |\n|\n'catch', |\n|\n'char', |\n|\n'class', |\n|\n'const', |\n|\n'continue', |\n|\n'default', |\n|\n'do', |\n|\n'double', |\n|\n'else', |\n|\n'enum', |\n|\n'extends', |\n|\n'final', |\n|\n'finally', |\n|\n'float', |\n|\n'for', |\n|\n'goto', |\n|\n'if', |\n|\n'implements', |\n|\n'import', |\n|\n'instanceof', |\n|\n'int', |\n|\n'interface', |\n|\n'long', |\n|\n'native', |\n|\n'new', |\n|\n'package', |\n|\n'private', |\n|\n'protected', |\n|\n'public', |\n|\n'return', |\n|\n'short', |\n|\n'static', |\n|\n'strictfp', |\n|\n'super', |\n|\n'switch', |\n|\n'synchronized', |\n|\n'this', |\n|\n'throw', |\n|\n'throws', |\n|\n'transient', |\n|\n'try', |\n|\n'void', |\n|\n'volatile', |\n|\n'while', |\n|\n]), |\n|\n|\n|\ngolang: new Set([ |\n|\n'break', |\n|\n'case', |\n|\n'chan', |\n|\n'const', |\n|\n'continue', |\n|\n'default', |\n|\n'defer', |\n|\n'else', |\n|\n'fallthrough', |\n|\n'for', |\n|\n'func', |\n|\n'go', |\n|\n'goto', |\n|\n'if', |\n|\n'import', |\n|\n'interface', |\n|\n'map', |\n|\n'package', |\n|\n'range', |\n|\n'return', |\n|\n'select', |\n|\n'struct', |\n|\n'switch', |\n|\n'type', |\n|\n'var', |\n|\n]), |\n|\n|\n|\nc: new Set([ |\n|\n'auto', |\n|\n'break', |\n|\n'case', |\n|\n'char', |\n|\n'const', |\n|\n'continue', |\n|\n'default', |\n|\n'do', |\n|\n'double', |\n|\n'else', |\n|\n'enum', |\n|\n'extern', |\n|\n'float', |\n|\n'for', |\n|\n'goto', |\n|\n'if', |\n|\n'int', |\n|\n'long', |\n|\n'register', |\n|\n'return', |\n|\n'short', |\n|\n'signed', |\n|\n'sizeof', |\n|\n'static', |\n|\n'struct', |\n|\n'switch', |\n|\n'typedef', |\n|\n'union', |\n|\n'unsigned', |\n|\n'void', |\n|\n'volatile', |\n|\n'while', |\n|\n]), |\n|\n|\n|\ncpp: new Set([ |\n|\n'alignas', |\n|\n'alignof', |\n|\n'and', |\n|\n'auto', |\n|\n'bool', |\n|\n'break', |\n|\n'case', |\n|\n'catch', |\n|\n'class', |\n|\n'const', |\n|\n'constexpr', |\n|\n'continue', |\n|\n'delete', |\n|\n'do', |\n|\n'double', |\n|\n'else', |\n|\n'enum', |\n|\n'explicit', |\n|\n'export', |\n|\n'extern', |\n|\n'false', |\n|\n'float', |\n|\n'for', |\n|\n'friend', |\n|\n'if', |\n|\n'inline', |\n|\n'int', |\n|\n'long', |\n|\n'mutable', |\n|\n'namespace', |\n|\n'new', |\n|\n'noexcept', |\n|\n'nullptr', |\n|\n'operator', |\n|\n'private', |\n|\n'protected', |\n|\n'public', |\n|\n'return', |\n|\n'short', |\n|\n'signed', |\n|\n'sizeof', |\n|\n'static', |\n|\n'struct', |\n|\n'switch', |\n|\n'template', |\n|\n'this', |\n|\n'throw', |\n|\n'true', |\n|\n'try', |\n|\n'typedef', |\n|\n'typename', |\n|\n'union', |\n|\n'unsigned', |\n|\n'using', |\n|\n'virtual', |\n|\n'void', |\n|\n'while', |\n|\n]), |\n|\n|\n|\nphp: new Set([ |\n|\n'abstract', |\n|\n'and', |\n|\n'array', |\n|\n'as', |\n|\n'break', |\n|\n'callable', |\n|\n'case', |\n|\n'catch', |\n|\n'class', |\n|\n'clone', |\n|\n'const', |\n|\n'continue', |\n|\n'declare', |\n|\n'default', |\n|\n'do', |\n|\n'echo', |\n|\n'else', |\n|\n'elseif', |\n|\n'empty', |\n|\n'enddeclare', |\n|\n'endfor', |\n|\n'endforeach', |\n|\n'endif', |\n|\n'endswitch', |\n|\n'endwhile', |\n|\n'extends', |\n|\n'final', |\n|\n'finally', |\n|\n'fn', |\n|\n'for', |\n|\n'foreach', |\n|\n'function', |\n|\n'global', |\n|\n'goto', |\n|\n'if', |\n|\n'implements', |\n|\n'include', |\n|\n'include_once', |\n|\n'instanceof', |\n|\n'interface', |\n|\n'match', |\n|\n'namespace', |\n|\n'new', |\n|\n'null', |\n|\n'or', |\n|\n'private', |\n|\n'protected', |\n|\n'public', |\n|\n'require', |\n|\n'require_once', |\n|\n'return', |\n|\n'static', |\n|\n'switch', |\n|\n'throw', |\n|\n'trait', |\n|\n'try', |\n|\n'use', |\n|\n'var', |\n|\n'while', |\n|\n'yield', |\n|\n]), |\n|\n|\n|\nrust: new Set([ |\n|\n'as', |\n|\n'break', |\n|\n'const', |\n|\n'continue', |\n|\n'crate', |\n|\n'else', |\n|\n'enum', |\n|\n'extern', |\n|\n'false', |\n|\n'fn', |\n|\n'for', |\n|\n'if', |\n|\n'impl', |\n|\n'in', |\n|\n'let', |\n|\n'loop', |\n|\n'match', |\n|\n'mod', |\n|\n'move', |\n|\n'mut', |\n|\n'pub', |\n|\n'ref', |\n|\n'return', |\n|\n'self', |\n|\n'Self', |\n|\n'static', |\n|\n'struct', |\n|\n'super', |\n|\n'trait', |\n|\n'true', |\n|\n'type', |\n|\n'unsafe', |\n|\n'use', |\n|\n'where', |\n|\n'while', |\n|\n]), |\n|\n|\n|\nhtml: new Set([ |\n|\n'html', |\n|\n'head', |\n|\n'body', |\n|\n'div', |\n|\n'span', |\n|\n'p', |\n|\n'a', |\n|\n'img', |\n|\n'video', |\n|\n'audio', |\n|\n'script', |\n|\n'style', |\n|\n'link', |\n|\n'meta', |\n|\n'form', |\n|\n'input', |\n|\n'button', |\n|\n'table', |\n|\n'tr', |\n|\n'td', |\n|\n'th', |\n|\n'ul', |\n|\n'ol', |\n|\n'li', |\n|\n'section', |\n|\n'article', |\n|\n'header', |\n|\n'footer', |\n|\n'nav', |\n|\n'main', |\n|\n]), |\n|\n|\n|\nbash: new Set([ |\n|\n'if', |\n|\n'then', |\n|\n'else', |\n|\n'elif', |\n|\n'fi', |\n|\n'for', |\n|\n'while', |\n|\n'do', |\n|\n'done', |\n|\n'case', |\n|\n'esac', |\n|\n'function', |\n|\n'in', |\n|\n'select', |\n|\n'until', |\n|\n'break', |\n|\n'continue', |\n|\n'return', |\n|\n'export', |\n|\n'readonly', |\n|\n'local', |\n|\n'declare', |\n|\n]), |\n|\n|\n|\nmarkdown: new Set(['#', '##', '###', '####', '#####', '######']), |\n|\n}; |\n|\n|\n|\nif (!lang || lang === 'txt' || lang === 'text' || lang === 'plaintext') { |\n|\nreturn { |\n|\ncodeBlock: [ |\n|\n{ |\n|\ncodeContent: code, |\n|\nhighlightType: 0, |\n|\n}, |\n|\n], |\n|\nunified_codeBlock: [ |\n|\n{ |\n|\ncontent: code, |\n|\ntype: 'DEFAULT', |\n|\n}, |\n|\n], |\n|\n}; |\n|\n} |\n|\n|\n|\nconst TYPE_MAP = { |\n|\n0: 'DEFAULT', |\n|\n1: 'KEYWORD', |\n|\n2: 'METHOD', |\n|\n3: 'STR', |\n|\n4: 'NUMBER', |\n|\n5: 'COMMENT', |\n|\n}; |\n|\n|\n|\nconst keywords = keywordsMap[lang.toLowerCase()] || new Set(); |\n|\nconst tokens = []; |\n|\n|\n|\nlet i = 0; |\n|\n|\n|\nconst push = (content, type) => { |\n|\nif (!content) return; |\n|\n|\n|\nconst last = tokens[tokens.length - 1]; |\n|\n|\n|\nif (last && last.highlightType === type) { |\n|\nlast.codeContent += content; |\n|\n} else { |\n|\ntokens.push({ |\n|\ncodeContent: content, |\n|\nhighlightType: type, |\n|\n}); |\n|\n} |\n|\n}; |\n|\n|\n|\nconst isIdentifier = (char) => { |\n|\nswitch (lang.toLowerCase()) { |\n|\ncase 'css': |\n|\nreturn /[a-zA-Z0-9_$-]/.test(char); |\n|\n|\n|\ncase 'html': |\n|\nreturn /[a-zA-Z0-9_$:-]/.test(char); |\n|\n|\n|\ndefault: |\n|\nreturn /[a-zA-Z0-9_$]/.test(char); |\n|\n} |\n|\n}; |\n|\n|\n|\nwhile (i < code.length) { |\n|\nconst c = code[i]; |\n|\n|\n|\nif (/\\s/.test(c)) { |\n|\nlet s = i; |\n|\n|\n|\nwhile (i < code.length && /\\s/.test(code[i])) { |\n|\ni++; |\n|\n} |\n|\n|\n|\npush(code.slice(s, i), 0); |\n|\ncontinue; |\n|\n} |\n|\n|\n|\nif ((c === '/' && code[i + 1] === '/') || (c === '#' && ['python', 'bash'].includes(lang))) { |\n|\nlet s = i; |\n|\n|\n|\nwhile (i < code.length && code[i] !== '\\n') { |\n|\ni++; |\n|\n} |\n|\n|\n|\npush(code.slice(s, i), 5); |\n|\ncontinue; |\n|\n} |\n|\n|\n|\nif (c === '\"' || c === \"'\" || c === '`') { |\n|\nlet s = i; |\n|\nconst q = c; |\n|\n|\n|\ni++; |\n|\n|\n|\nwhile (i < code.length) { |\n|\nif (code[i] === '\\\\' && i + 1 < code.length) { |\n|\ni += 2; |\n|\n} else if (code[i] === q) { |\n|\ni++; |\n|\nbreak; |\n|\n} else { |\n|\ni++; |\n|\n} |\n|\n} |\n|\n|\n|\npush(code.slice(s, i), 3); |\n|\ncontinue; |\n|\n} |\n|\n|\n|\nif (/[0-9]/.test(c)) { |\n|\nlet s = i; |\n|\n|\n|\nwhile (i < code.length && /[0-9._]/.test(code[i])) { |\n|\ni++; |\n|\n} |\n|\n|\n|\npush(code.slice(s, i), 4); |\n|\ncontinue; |\n|\n} |\n|\n|\n|\nif (/[a-zA-Z_$]/.test(c)) { |\n|\nlet s = i; |\n|\n|\n|\nwhile (i < code.length && isIdentifier(code[i])) { |\n|\ni++; |\n|\n} |\n|\n|\n|\nconst word = code.slice(s, i); |\n|\n|\n|\nlet type = 0; |\n|\n|\n|\nif (keywords.has(word)) { |\n|\ntype = 1; |\n|\n} else if (lang === 'css') { |\n|\nlet j = i; |\n|\n|\n|\nwhile (j < code.length && /\\s/.test(code[j])) { |\n|\nj++; |\n|\n} |\n|\n|\n|\nif (code[j] === ':') { |\n|\ntype = 1; |\n|\n} |\n|\n} else if (lang === 'html') { |\n|\nlet p = s - 1; |\n|\n|\n|\nwhile (p >= 0 && /\\s/.test(code[p])) { |\n|\np--; |\n|\n} |\n|\n|\n|\nif (code[p] === '<' || (code[p] === '/' && code[p - 1] === '<')) { |\n|\ntype = 1; |\n|\n} |\n|\n} |\n|\n|\n|\nif (type === 0) { |\n|\nlet j = i; |\n|\n|\n|\nwhile (j < code.length && /\\s/.test(code[j])) { |\n|\nj++; |\n|\n} |\n|\n|\n|\nif (code[j] === '(') { |\n|\ntype = 2; |\n|\n} |\n|\n} |\n|\n|\n|\npush(word, type); |\n|\ncontinue; |\n|\n} |\n|\n|\n|\npush(c, 0); |\n|\ni++; |\n|\n} |\n|\n|\n|\nreturn { |\n|\ncodeBlock: tokens, |\n|\nunified_codeBlock: tokens.map((t) => ({ |\n|\ncontent: t.codeContent, |\n|\ntype: TYPE_MAP[t.highlightType], |\n|\n})), |\n|\n}; |\n|\n} |\n|\n|\n|\nstatic toTableMetadata(arr, { hyperlink = true, citation = true, latex = true } = {}) { |\n|\nif (!Array.isArray(arr) || !arr.every((row) => Array.isArray(row) && row.every((cell) => typeof cell === 'string'))) { |\n|\nthrow new TypeError('Table must be a nested array of strings'); |\n|\n} |\n|\n|\n|\nconst [header, ...rows] = arr; |\n|\n|\n|\nconst maxLen = Math.max(header.length, ...rows.map((r) => r.length)); |\n|\n|\n|\nconst normalize = (r) => [...r, ...Array(maxLen - r.length).fill('')]; |\n|\n|\n|\nconst unified_rows = [ |\n|\n{ |\n|\nis_header: true, |\n|\ncells: normalize(header), |\n|\n}, |\n|\n...rows.map((r) => ({ |\n|\nis_header: false, |\n|\ncells: normalize(r), |\n|\n})), |\n|\n].map((row) => { |\n|\nconst markdown_cells = row.cells.map((cell) => { |\n|\nconst extracted = extractIE(cell, { hyperlink, citation, latex }); |\n|\n|\n|\nreturn { |\n|\ntext: extracted.text, |\n|\n...(extracted.inline_entities.length ? { inline_entities: extracted.inline_entities } : {}), |\n|\n}; |\n|\n}); |\n|\n|\n|\nreturn { |\n|\n...row, |\n|\n...(markdown_cells.some((c) => c.inline_entities?.length) ? { markdown_cells } : {}), |\n|\n}; |\n|\n}); |\n|\n|\n|\nconst rowsMeta = unified_rows.map((r) => ({ |\n|\nitems: r.cells, |\n|\n...(r.is_header ? { isHeading: true } : {}), |\n|\n})); |\n|\n|\n|\nreturn { |\n|\ntitle: '', |\n|\nrows: rowsMeta, |\n|\nunified_rows, |\n|\n}; |\n|\n} |\n|\n|\n|\nstatic generateVerificationMetadata() { |\n|\nconst signatureMaterial = Buffer.from( |\n|\n`\\u004E\\u0049\\u0058\\u0045\\u004C\\u002E\\u004D\\u0065\\u0073\\u0073\\u0061\\u0067\\u0065\\u0042\\u0075\\u0069\\u006C\\u0064\\u0065\\u0072\\u0056${VERSION}\\u002D\\u0056\\u0065\\u0072\\u0069\\u0066\\u0069\\u0063\\u0061\\u0074\\u0069\\u006F\\u006E\\u0053\\u0069\\u0067\\u006E\\u0061\\u0074\\u0075\\u0072\\u0065\\u002E\\u004D\\u0065\\u0074\\u0061\\u0064\\u0061\\u0074\\u0061` |\n|\n); |\n|\n|\n|\nconst certificateMaterial = Buffer.from( |\n|\n`\\u004E\\u0049\\u0058\\u0045\\u004C\\u002E\\u004D\\u0065\\u0073\\u0073\\u0061\\u0067\\u0065\\u0042\\u0075\\u0069\\u006C\\u0064\\u0065\\u0072\\u0056${VERSION}\\u002D\\u0043\\u0065\\u0072\\u0074\\u0069\\u0066\\u0069\\u0063\\u0061\\u0074\\u0065\\u0043\\u0068\\u0061\\u0069\\u006E\\u002E\\u004D\\u0065\\u0074\\u0061\\u0064\\u0061\\u0074\\u0061` |\n|\n); |\n|\n|\n|\nconst signature = Buffer.concat([signatureMaterial, crypto.randomBytes(64 - signatureMaterial.length)]).toString('base64'); |\n|\n|\n|\nconst certificateChain = [ |\n|\nBuffer.concat([certificateMaterial, crypto.randomBytes(684 - certificateMaterial.length)]).toString('base64'), |\n|\n|\n|\nBuffer.concat([certificateMaterial, crypto.randomBytes(892 - certificateMaterial.length)]).toString('base64'), |\n|\n]; |\n|\n|\n|\nreturn { |\n|\nproofs: [ |\n|\n{ |\n|\nversion: 1, |\n|\nuseCase: 1, |\n|\nsignature, |\n|\ncertificateChain, |\n|\n}, |\n|\n], |\n|\n}; |\n|\n} |\n|\n|\n|\nstatic newLayout(name, data, extra = {}) { |\n|\nreturn { |\n|\n...extra, |\n|\nview_model: { |\n|\n[Array.isArray(data) ? 'primitives' : 'primitive']: data, |\n|\n__typename: `GenAI${name}LayoutViewModel`, |\n|\n}, |\n|\n}; |\n|\n} |\n|\n|\n|\n_makeNode(id, section, submessage) { |\n|\nreturn { id: id ?? null, section: section ?? null, submessage: submessage ?? null }; |\n|\n} |\n|\n|\n|\n_registerId(node, id) { |\n|\nif (id === undefined || id === null || id === '') return; |\n|\n|\n|\nif (typeof id !== 'string') { |\n|\nthrow new ContentValidationError('Item id must be a string', { id }); |\n|\n} |\n|\n|\n|\nif (this._idIndex.has(id)) { |\n|\nthrow new DuplicateIdError(id); |\n|\n} |\n|\n|\n|\nnode.id = id; |\n|\nthis._idIndex.set(id, node); |\n|\n} |\n|\n|\n|\n_unregisterId(node) { |\n|\nif (node.id && this._idIndex.get(node.id) === node) { |\n|\nthis._idIndex.delete(node.id); |\n|\n} |\n|\n} |\n|\n|\n|\nhasId(id) { |\n|\nreturn typeof id === 'string' && this._idIndex.has(id); |\n|\n} |\n|\n|\n|\ngetIds() { |\n|\nreturn [...this._idIndex.keys()]; |\n|\n} |\n|\n|\n|\npeek(id) { |\n|\nconst node = this._idIndex.get(id); |\n|\n|\n|\nif (!node) return null; |\n|\n|\n|\nreturn { |\n|\nid: node.id, |\n|\nsection: node.section, |\n|\nsubmessage: node.submessage, |\n|\n}; |\n|\n} |\n|\n|\n|\nassignId(index, id) { |\n|\nif (!Number.isInteger(index) || index < 0 || index >= this._nodes.length) { |\n|\nthrow new InvalidTargetError(`Node index ${index} is out of range (0-${this._nodes.length - 1})`, { index }); |\n|\n} |\n|\n|\n|\nconst node = this._nodes[index]; |\n|\n|\n|\nif (node.id) { |\n|\nthrow new AIRichError(`Node at index ${index} already has id \"${node.id}\"`, 'ALREADY_HAS_ID', { index, id: node.id }); |\n|\n} |\n|\n|\n|\nthis._registerId(node, id); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\n_getNode(id) { |\n|\nif (typeof id !== 'string' || !id) { |\n|\nthrow new ContentValidationError('Item id must be a non-empty string', { id }); |\n|\n} |\n|\n|\n|\nconst node = this._idIndex.get(id); |\n|\n|\n|\nif (!node) { |\n|\nthrow new ItemNotFoundError(id, this.getIds()); |\n|\n} |\n|\n|\n|\nreturn node; |\n|\n} |\n|\n|\n|\n_resolveTarget(target) { |\n|\nif (Array.isArray(target)) { |\n|\nif (target.length < 1 || target.length > 2) { |\n|\nthrow new ContentValidationError('Target must be id or [id, offset]', { target }); |\n|\n} |\n|\n|\n|\nconst [id, offset = 0] = target; |\n|\n|\n|\nif (typeof id !== 'string' || !id) { |\n|\nthrow new ContentValidationError('Target id must be a non-empty string', { target }); |\n|\n} |\n|\n|\n|\nif (!Number.isInteger(offset)) { |\n|\nthrow new ContentValidationError('Offset must be an integer', { target }); |\n|\n} |\n|\n|\n|\nreturn { id, offset }; |\n|\n} |\n|\n|\n|\nif (typeof target !== 'string' || !target) { |\n|\nthrow new ContentValidationError('Target must be a non-empty id or [id, offset]', { target }); |\n|\n} |\n|\n|\n|\nreturn { id: target, offset: 0 }; |\n|\n} |\n|\n|\n|\n_resolveNodeIndex(target) { |\n|\nconst { id, offset } = this._resolveTarget(target); |\n|\nconst node = this._getNode(id); |\n|\nconst baseIndex = this._nodes.indexOf(node); |\n|\n|\n|\nif (baseIndex === -1) { |\n|\nthrow new InvalidTargetError(`Item id \"${id}\" is registered but not present in the node list (internal desync)`, { id }); |\n|\n} |\n|\n|\n|\nconst index = baseIndex + offset; |\n|\n|\n|\nif (index < 0 || index >= this._nodes.length) { |\n|\nthrow new InvalidTargetError(`Target \"${id}\" with offset ${offset} resolves to index ${index}, which is out of range (0-${this._nodes.length - 1})`, { id, offset, index }); |\n|\n} |\n|\n|\n|\nreturn { id, offset, baseIndex, index }; |\n|\n} |\n|\n|\n|\n_validateSections(section) { |\n|\nconst items = Array.isArray(section) ? section : [section]; |\n|\n|\n|\nif (!items.length) { |\n|\nthrow new ContentValidationError('At least one section is required'); |\n|\n} |\n|\n|\n|\nfor (const item of items) { |\n|\nif (!item || typeof item !== 'object' || Array.isArray(item)) { |\n|\nthrow new ContentValidationError('Sections must be plain objects'); |\n|\n} |\n|\n} |\n|\n|\n|\nreturn items; |\n|\n} |\n|\n|\n|\n_validateSubmessages(submessage) { |\n|\nif (submessage === undefined || submessage === null) { |\n|\nreturn []; |\n|\n} |\n|\n|\n|\nconst items = Array.isArray(submessage) ? submessage : [submessage]; |\n|\n|\n|\nfor (const item of items) { |\n|\nif (!item || typeof item !== 'object' || Array.isArray(item)) { |\n|\nthrow new ContentValidationError('Submessages must be plain objects'); |\n|\n} |\n|\n} |\n|\n|\n|\nreturn items; |\n|\n} |\n|\n|\n|\n_pairSubmessages(sections, submessages) { |\n|\nconst n = sections.length; |\n|\nconst m = submessages.length; |\n|\n|\n|\nif (m === 0) return sections.map(() => null); |\n|\nif (m === 1) return sections.map((_, i) => (i === 0 ? submessages[0] : null)); |\n|\nif (m === n) return submessages; |\n|\n|\n|\nthrow new ContentValidationError(`Cannot pair ${m} submessage(s) with ${n} section(s): expected 0, 1, or ${n}`, { sectionCount: n, submessageCount: m }); |\n|\n} |\n|\n|\n|\n_addContent(section, submessage, { id, replace, insertAt } = {}) { |\n|\nconst hasReplace = replace !== undefined && replace !== null && replace !== ''; |\n|\n|\n|\nconst hasInsertAt = insertAt !== undefined && insertAt !== null && insertAt !== ''; |\n|\n|\n|\nif (hasReplace && hasInsertAt) { |\n|\nthrow new ContentValidationError('replace and insertAt cannot be used together'); |\n|\n} |\n|\n|\n|\nconst sections = this._validateSections(section); |\n|\nconst submessages = this._validateSubmessages(submessage); |\n|\n|\n|\nif (!sections.length) { |\n|\nthrow new ContentValidationError('At least one section is required'); |\n|\n} |\n|\n|\n|\nif (id !== undefined && id !== null && id !== '' && sections.length !== 1) { |\n|\nthrow new ContentValidationError('One id can only be assigned to one node', { |\n|\nid, |\n|\nsectionCount: sections.length, |\n|\n}); |\n|\n} |\n|\n|\n|\nif (submessages.length && submessages.length !== sections.length && submessages.length !== 1) { |\n|\nthrow new ContentValidationError('Section and submessage count must match'); |\n|\n} |\n|\n|\n|\nconst pairedSubmessages = sections.map((_, index) => { |\n|\nif (!submessages.length) return undefined; |\n|\n|\n|\nreturn submessages.length === 1 ? submessages[0] : submessages[index]; |\n|\n}); |\n|\n|\n|\nif (id && this._idIndex.has(id) && !(hasReplace && this._resolveTarget(replace)?.id === id)) { |\n|\nthrow new DuplicateIdError(id); |\n|\n} |\n|\n|\n|\nconst newNodes = sections.map((currentSection, index) => { |\n|\nreturn this._makeNode(index === 0 ? id : null, currentSection, pairedSubmessages[index]); |\n|\n}); |\n|\n|\n|\nif (hasReplace) { |\n|\nif (newNodes.length !== 1) { |\n|\nthrow new ContentValidationError('replace only supports adding exactly one node'); |\n|\n} |\n|\n|\n|\nconst target = this._resolveNodeIndex(replace); |\n|\n|\n|\nif (!target) { |\n|\nthrow new ContentValidationError('Target node could not be resolved'); |\n|\n} |\n|\n|\n|\nconst oldNode = this._nodes[target.index]; |\n|\nconst newNode = newNodes[0]; |\n|\n|\n|\nif (!newNode.id && oldNode?.id) { |\n|\nnewNode.id = oldNode.id; |\n|\n} |\n|\n|\n|\nthis._unregisterId(oldNode); |\n|\n|\n|\nthis._nodes.splice(target.index, 1, newNode); |\n|\n|\n|\nif (newNode.id) { |\n|\nthis._idIndex.set(newNode.id, newNode); |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nif (hasInsertAt) { |\n|\nconst target = this._resolveNodeIndex(insertAt); |\n|\n|\n|\nif (!target) { |\n|\nthrow new ContentValidationError('Target node could not be resolved'); |\n|\n} |\n|\n|\n|\nconst insertIndex = target.offset < 0 ? target.index : target.index + 1; |\n|\n|\n|\nthis._nodes.splice(insertIndex, 0, ...newNodes); |\n|\n|\n|\nfor (const node of newNodes) { |\n|\nif (node.id) { |\n|\nthis._idIndex.set(node.id, node); |\n|\n} |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nthis._nodes.push(...newNodes); |\n|\n|\n|\nfor (const node of newNodes) { |\n|\nif (node.id) { |\n|\nthis._idIndex.set(node.id, node); |\n|\n} |\n|\n} |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\naddSection(section, options = {}) { |\n|\nreturn this._addContent(section, undefined, options); |\n|\n} |\n|\n|\n|\naddSubmessage(submessage, options = {}) { |\n|\nconst items = this._validateSubmessages(submessage); |\n|\n|\n|\nif (!items.length) { |\n|\nthrow new ContentValidationError('At least one submessage is required'); |\n|\n} |\n|\n|\n|\nreturn this._addContent(undefined, items, options); |\n|\n} |\n|\n|\n|\ndelete(target) { |\n|\nconst { index } = this._resolveNodeIndex(target); |\n|\nconst [oldNode] = this._nodes.splice(index, 1); |\n|\n|\n|\nthis._unregisterId(oldNode); |\n|\n|\n|\nreturn this; |\n|\n} |\n|\n|\n|\nget _sections() { |\n|\nreturn this._nodes.filter((n) => n.section !== null).map((n) => n.section); |\n|\n} |\n|\n|\n|\nget _submessages() { |\n|\nreturn this._nodes.filter((n) => n.submessage !== null).map((n) => n.submessage); |\n|\n} |\n|\n|\n|\nget sections() { |\n|\nreturn this._sections; |\n|\n} |\n|\n|\n|\nget items() { |\n|\nreturn this._sections.flatMap((section) => { |\n|\nconst vm = section?.view_model; |\n|\n|\n|\nif (Array.isArray(vm?.primitives)) { |\n|\nreturn vm.primitives; |\n|\n} |\n|\n|\n|\nif (vm?.primitive) { |\n|\nreturn [vm.primitive]; |\n|\n} |\n|\n|\n|\nreturn []; |\n|\n}); |\n|\n} |\n|\n} |\n|\n|\n|\nexport { VERSION, Button, ButtonV2, Carousel, AIRich, Toolkit }; |", "url": "https://wpnews.pro/news/messagebuilderv4-7-js", "canonical_source": "https://gist.github.com/ValdazGT/adc8b767a082d18d12ff3e7f01b78651", "published_at": "2026-08-24 01:15:54+00:00", "updated_at": "2026-09-02 05:52:36.524281+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Nixel", "Baileys", "WhatsApp", "MessageBuilderV4.7.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/messagebuilderv4-7-js", "markdown": "https://wpnews.pro/news/messagebuilderv4-7-js.md", "text": "https://wpnews.pro/news/messagebuilderv4-7-js.txt", "jsonld": "https://wpnews.pro/news/messagebuilderv4-7-js.jsonld"}}