MessageBuilderV4.3.js Developer Nixel released MessageBuilderV4.3.js, an advanced WhatsApp interactive message builder that creates buttons, carousels, native flows, and AI-rich response payloads using the Baileys library. The library features fluent chaining, flexible payload customization, and scalable architecture for modern bot development, with permission granted for personal or commercial use but prohibiting reuploading or reselling as a standalone product. | / | | NIXCODE - Advanced WhatsApp Interactive Message Builder | | Built for creating buttons, carousels, native flows, | | and AI rich response payloads using Baileys with | | fluent chaining, flexible payload customization, | | and scalable architecture for modern bot development. | | | | Created by Nixel | | wa.me/6282139672290 | | | | Copyright c 2026 Nixel | | | | Permission is granted to use and modify this library | | for personal or commercial projects. | | | | Reuploading, reselling, relicensing, or redistributing | | this library as a standalone product is prohibited. | | | | Do not claim this project as your own original work. | | / | | | | const VERSION = '4.3'; | | | | import { generateWAMessageFromContent, prepareWAMessageMedia } from 'baileys'; | | import crypto from 'crypto'; | | import sharp from 'sharp'; | | | | function extractHyperlink text { | | let hyperlink = , | | stack = , | | result = '', | | last = 0, | | index = 1, | | entity = 0; | | for let i = 0; i < text.length; i++ { | | if text i == ' ' && text i - 1 = '\\' { | | stack.push i ; | | } else if text i == ' ' && text i + 1 == ' ' { | | let start = stack.pop ; | | if start == null continue; | | let end = i + 2, | | depth = 1; | | while end < text.length && depth { | | if text end == ' ' && text end - 1 = '\\' depth++; | | else if text end == ' ' && text end - 1 = '\\' depth--; | | end++; | | } | | if depth continue; | | let txt = text.slice start + 1, i .trim , | | url = text.slice i + 2, end - 1 , | | reference id = txt ? 0 : index++, | | key = IE ${entity++} , | | tag = {{${key}}}${txt || 'Nixel'}{{/${key}}} ; | | result += text.slice last, start + tag; | | last = end; | | hyperlink.push { | | reference id, | | key, | | text: txt, | | url, | | } ; | | i = end - 1; | | } | | } | | result += text.slice last ; | | return { | | text: result, | | hyperlink, | | }; | | } | | | | async function fetchBuffer url, options = {}, config = {} { | | try { | | let response = await fetch url, options ; | | if response.ok throw Error HTTP ${response.status} ; | | return Buffer.from await response.arrayBuffer ; | | } catch error { | | if config.silent return Buffer.alloc 0 ; | | throw error; | | } | | } | | | | class BaseBuilder { | | constructor { | | this. title = ''; | | this. subtitle = ''; | | this. body = ''; | | this. footer = ''; | | this. contextInfo = {}; | | this. extraPayload = {}; | | } | | | | setTitle title { | | this. title = title; | | return this; | | } | | | | setSubtitle subtitle { | | this. subtitle = subtitle; | | return this; | | } | | | | setBody body { | | this. body = body; | | return this; | | } | | | | setFooter footer { | | this. footer = footer; | | return this; | | } | | | | setContextInfo obj { | | if typeof obj == 'object' || obj === null || Array.isArray obj { | | throw new TypeError 'ContextInfo must be a plain object' ; | | } | | | | this. contextInfo = obj; | | return this; | | } | | | | addPayload obj { | | if typeof obj == 'object' || obj === null || Array.isArray obj { | | throw new TypeError 'Payload must be a plain object' ; | | } | | | | Object.assign this. extraPayload, obj ; | | | | return this; | | } | | } | | | | class Button extends BaseBuilder { | | client; | | | | constructor client { | | super ; | | if client { | | throw new Error 'Socket is required' ; | | } | | this. client = client; | | | | this. buttons = ; | | this. data; | | this. currentSelectionIndex = -1; | | this. currentSectionIndex = -1; | | this. params = {}; | | } | | | | setVideo path, options = {} { | | if path throw new Error 'Url or buffer needed' ; | | Buffer.isBuffer path ? this. data = { video: path, ...options } : this. data = { video: { url: path }, ...options } ; | | return this; | | } | | | | setImage path, options = {} { | | if path throw new Error 'Url or buffer needed' ; | | Buffer.isBuffer path ? this. data = { image: path, ...options } : this. data = { image: { url: path }, ...options } ; | | return this; | | } | | | | setDocument path, options = {} { | | if path throw new Error 'Url or buffer needed' ; | | Buffer.isBuffer path ? this. data = { document: path, ...options } : this. data = { document: { url: path }, ...options } ; | | return this; | | } | | | | setMedia obj { | | if typeof obj == 'object' || obj === null || Array.isArray obj { | | throw new TypeError 'Media must be a plain object' ; | | } | | | | this. data = obj; | | return this; | | } | | | | clearButtons { | | this. buttons = ; | | return this; | | } | | | | setParams obj { | | this. params = obj; | | return this; | | } | | | | addButton name, params { | | this. buttons.push { | | name, | | buttonParamsJson: typeof params === 'string' ? params : JSON.stringify params , | | } ; | | | | return this; | | } | | | | makeRow header = '', title = '', description = '', id = crypto.randomUUID { | | if this. currentSelectionIndex === -1 || this. currentSectionIndex === -1 { | | throw new Error 'You need to create a selection and a section first' ; | | } | | const buttonParams = JSON.parse this. buttons this. currentSelectionIndex .buttonParamsJson ; | | buttonParams.sections this. currentSectionIndex .rows.push { header, title, description, id } ; | | this. buttons this. currentSelectionIndex .buttonParamsJson = JSON.stringify buttonParams ; | | return this; | | } | | | | makeSections title = '', highlight label = '' { | | if this. currentSelectionIndex === -1 { | | throw new Error 'You need to create a selection first' ; | | } | | const buttonParams = JSON.parse this. buttons this. currentSelectionIndex .buttonParamsJson ; | | buttonParams.sections.push { title, highlight label, rows: } ; | | this. currentSectionIndex = buttonParams.sections.length - 1; | | this. buttons this. currentSelectionIndex .buttonParamsJson = JSON.stringify buttonParams ; | | return this; | | } | | | | addSelection title { | | this. buttons.push { name: 'single select', buttonParamsJson: JSON.stringify { title, sections: } } ; | | this. currentSelectionIndex = this. buttons.length - 1; | | this. currentSectionIndex = -1; | | return this; | | } | | | | addReply display text = '', id = crypto.randomUUID { | | this. buttons.push { name: 'quick reply', buttonParamsJson: JSON.stringify { display text, id } } ; | | return this; | | } | | | | addCall display text = '', id = crypto.randomUUID { | | this. buttons.push { | | name: 'cta call', | | buttonParamsJson: JSON.stringify { | | display text, | | id, | | } , | | } ; | | return this; | | } | | | | addReminder display text = '', id = crypto.randomUUID { | | this. buttons.push { | | name: 'cta reminder', | | buttonParamsJson: JSON.stringify { | | display text, | | id, | | } , | | } ; | | return this; | | } | | | | addCancelReminder display text = '', id = crypto.randomUUID { | | this. buttons.push { | | name: 'cta cancel reminder', | | buttonParamsJson: JSON.stringify { | | display text, | | id, | | } , | | } ; | | return this; | | } | | | | addAddress display text = '', id = crypto.randomUUID { | | this. buttons.push { | | name: 'address message', | | buttonParamsJson: JSON.stringify { | | display text, | | id, | | } , | | } ; | | return this; | | } | | | | addLocation { | | this. buttons.push { | | name: 'send location', | | buttonParamsJson: '', | | } ; | | return this; | | } | | | | addUrl display text = '', url = '', webview interaction = false { | | this. buttons.push { | | name: 'cta url', | | buttonParamsJson: JSON.stringify { | | display text, | | url, | | webview interaction, | | } , | | } ; | | return this; | | } | | | | addCopy display text = '', copy code = '', id = crypto.randomUUID { | | this. buttons.push { | | name: 'cta copy', | | buttonParamsJson: JSON.stringify { | | display text, | | copy code, | | id, | | } , | | } ; | | return this; | | } | | | | static paramsList = { | | limited time offer: { | | text: 'string', | | url: 'string', | | copy code: 'string', | | expiration time: 'number', | | }, | | bottom sheet: { | | in thread buttons limit: 'number', | | divider indices: 'number' , | | list title: 'string', | | button title: 'string', | | }, | | tap target configuration: { | | title: 'string', | | description: 'string', | | canonical url: 'string', | | domain: 'string', | | buttonIndex: 'number', | | }, | | }; | | | | async toCard { | | return { | | body: { | | text: this. body, | | }, | | footer: { | | text: this. footer, | | }, | | header: { | | title: this. title, | | subtitle: this. subtitle, | | hasMediaAttachment: this. data, | | ... this. data ? await prepareWAMessageMedia this. data, { upload: this. client.waUploadToServer } : {} , | | }, | | nativeFlowMessage: { | | messageParamsJson: JSON.stringify this. params , | | buttons: this. buttons, | | }, | | }; | | } | | | | async build jid, { ...options } = {} { | | const message = await this.toCard ; | | | | return generateWAMessageFromContent | | jid, | | { | | ...this. extraPayload, | | interactiveMessage: { | | ...message, | | contextInfo: this. contextInfo, | | }, | | }, | | { ...options } | | ; | | } | | | | async send jid, { ...options } = {} { | | const msg = await this.build jid, options ; | | | | await this. client.relayMessage msg.key.remoteJid, msg.message, { | | messageId: msg.key.id, | | additionalNodes: | | { | | tag: 'biz', | | attrs: {}, | | content: | | { | | tag: 'interactive', | | attrs: { type: 'native flow', v: '1' }, | | content: { tag: 'native flow', attrs: { v: '9', name: 'mixed' } } , | | }, | | , | | }, | | , | | ...options, | | } ; | | return msg; | | } | | } | | | | class ButtonV2 extends BaseBuilder { | | client; | | | | constructor client { | | super ; | | if client { | | throw new Error 'Socket is required' ; | | } | | | | this. client = client; | | this. image; | | this. data; | | this. buttons = ; | | } | | | | addButton displayText = '', buttonId = crypto.randomUUID { | | this. buttons.push { buttonId, buttonText: { displayText }, type: 1 } ; | | return this; | | } | | | | addRawButton obj { | | if typeof obj == 'object' || obj === null || Array.isArray obj { | | throw new TypeError 'Buttons must be a plain object' ; | | } | | | | this. buttons.push obj ; | | return this; | | } | | | | setThumbnail path { | | if path throw new Error 'Url or buffer needed' ; | | this. image = path; | | return this; | | } | | | | setMedia obj { | | if typeof obj == 'object' || obj === null || Array.isArray obj { | | throw new TypeError 'Media must be a plain object' ; | | } | | | | this. data = obj; | | return this; | | } | | | | async build jid, { ...options } = {} { | | let thumbnail = this. image | | ? await sharp Buffer.isBuffer this. image ? this. image : await fetchBuffer this. image, {}, { silent: true } | | .resize 300, 300 | | .png | | .toBuffer | | : null; | | const msg = generateWAMessageFromContent | | jid, | | { | | ...this. extraPayload, | | buttonsMessage: { | | contentText: this. body, | | footerText: this. footer, | | ... this. data | | ? this. data | | : { | | headerType: 6, | | locationMessage: { | | jpegThumbnail: thumbnail, | | }, | | } , | | viewOnce: true, | | contextInfo: this. contextInfo, | | buttons: ...this. buttons , | | }, | | }, | | { ...options } | | ; | | return msg; | | } | | | | async send jid, { ...options } = {} { | | const msg = await this.build jid, options ; | | | | await this. client.relayMessage msg.key.remoteJid, msg.message, { | | messageId: msg.key.id, | | additionalNodes: | | { | | tag: 'biz', | | attrs: {}, | | content: | | { | | tag: 'interactive', | | attrs: { type: 'native flow', v: '1' }, | | content: { tag: 'native flow', attrs: { v: '9', name: 'mixed' } } , | | }, | | , | | }, | | , | | ...options, | | } ; | | return msg; | | } | | } | | | | class Carousel extends BaseBuilder { | | client; | | constructor client { | | super ; | | if client { | | throw new Error 'Socket is required' ; | | } | | | | this. client = client; | | this. cards = ; | | } | | | | addCard card { | | if Array.isArray card { | | this. cards.push ...card ; | | } else { | | this. cards.push card ; | | } | | | | return this; | | } | | | | build jid, { ...options } { | | return generateWAMessageFromContent | | jid, | | { | | ...this. extraPayload, | | interactiveMessage: { | | header: { | | hasMediaAttachment: false, | | }, | | body: { text: this. body }, | | footer: { text: this. footer }, | | contextInfo: this. contextInfo, | | carouselMessage: { | | cards: this. cards, | | }, | | }, | | }, | | { ...options } | | ; | | } | | | | async send jid, { ...options } = {} { | | const msg = this.build jid, options ; | | | | await this. client.relayMessage msg.key.remoteJid, msg.message, { | | messageId: msg.key.id, | | additionalNodes: | | { | | tag: 'biz', | | attrs: {}, | | content: | | { | | tag: 'interactive', | | attrs: { type: 'native flow', v: '1' }, | | content: { tag: 'native flow', attrs: { v: '9', name: 'mixed' } } , | | }, | | , | | }, | | , | | ...options, | | } ; | | return msg; | | } | | } | | | | class AIRich { | | client; | | constructor client { | | if client { | | throw new Error 'Socket is required' ; | | } | | | | this. client = client; | | this. submessages = ; | | this. sections = ; | | this. richResponseSources = ; | | } | | | | addText text, { hyperlink = true } = {} { | | const extractedHyperlink = hyperlink | | ? extractHyperlink text | | : { | | text, | | hyperlink: , | | }; | | | | this. submessages.push { | | messageType: 2, | | messageText: hyperlink ? extractedHyperlink.text : text, | | } ; | | | | if extractedHyperlink.hyperlink.length && hyperlink { | | this. sections.push { | | view model: { | | primitive: { | | text: extractedHyperlink.text, | | inline entities: extractedHyperlink.hyperlink.map { reference id, key, text, url } = { | | key, | | metadata: text?.trim | | ? { | | display name: text, | | is trusted: true, | | url, | | typename: 'GenAIInlineLinkItem', | | } | | : { | | reference id, | | reference url: url, | | reference title: url, | | reference display name: url, | | sources: , | | typename: 'GenAISearchCitationItem', | | }, | | } , | | typename: 'GenAIMarkdownTextUXPrimitive', | | }, | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | } else { | | this. sections.push { | | view model: { | | primitive: { | | text, | | typename: 'GenAIMarkdownTextUXPrimitive', | | }, | | | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | } | | | | return this; | | } | | | | addCode language, code { | | const meta = AIRich.tokenizer code, language ; | | | | this. submessages.push { | | messageType: 5, | | codeMetadata: { | | codeLanguage: language, | | codeBlocks: meta.codeBlock, | | }, | | } ; | | | | this. sections.push { | | view model: { | | primitive: { | | language, | | code blocks: meta.unified codeBlock, | | typename: 'GenAICodeUXPrimitive', | | }, | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | | | return this; | | } | | | | addTable table { | | const meta = AIRich.toTableMetadata table ; | | | | this. submessages.push { | | messageType: 4, | | tableMetadata: { | | title: meta.title, | | rows: meta.rows, | | }, | | } ; | | | | this. sections.push { | | view model: { | | primitive: { | | rows: meta.unified rows, | | typename: 'GenATableUXPrimitive', | | }, | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | | | return this; | | } | | | | addSource sources = { | | const source = sources.map profile url, url, text = { | | source type: 'THIRD PARTY', | | source display name: text, | | source subtitle: 'AI', | | source url: url, | | favicon: { | | url: profile url, | | mime type: 'image/jpeg', | | width: 16, | | height: 16, | | }, | | } ; | | | | this. sections.push { | | view model: { | | primitive: { | | sources: source, | | typename: 'GenAISearchResultPrimitive', | | }, | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | | | return this; | | } | | | | addReels reelsItems = { | | this. submessages.push { | | messageType: 9, | | contentItemsMetadata: { | | contentType: 1, | | itemsMetadata: reelsItems.map item = { | | reelItem: { | | title: item.title, | | profileIconUrl: item.profileIconUrl, | | thumbnailUrl: item.thumbnailUrl, | | videoUrl: item.videoUrl, | | }, | | } , | | }, | | } ; | | | | reelsItems.forEach item, idx = { | | this. richResponseSources.push { | | provider: 'UNKNOWN', | | thumbnailCDNURL: item.thumbnailUrl, | | sourceProviderURL: item.videoUrl, | | sourceQuery: '', | | faviconCDNURL: item.profileIconUrl, | | citationNumber: idx + 1, | | sourceTitle: item.title, | | } ; | | } ; | | | | this. sections.push { | | view model: { | | primitives: reelsItems.map item = { | | reels url: item.videoUrl, | | thumbnail url: item.thumbnailUrl, | | creator: item.title, | | avatar url: item.profileIconUrl, | | reels title: item.reels title, | | likes count: 0, | | shares count: 0, | | view count: 0, | | reel source: 'IG', | | is verified: item.is verified, | | typename: 'GenAIReelPrimitive', | | } , | | typename: 'GenAIHScrollLayoutViewModel', | | }, | | } ; | | | | return this; | | } | | | | addImage imageUrl { | | const imageUrls = Array.isArray imageUrl | | ? imageUrl.map url = { | | imagePreviewUrl: url, | | imageHighResUrl: url, | | sourceUrl: 'https://google.com', | | } | | : | | { | | imagePreviewUrl: imageUrl, | | imageHighResUrl: imageUrl, | | sourceUrl: 'https://google.com', | | }, | | ; | | | | this. submessages.push { | | messageType: 1, | | gridImageMetadata: { | | gridImageUrl: { | | imagePreviewUrl: Array.isArray imageUrl ? imageUrl 0 : imageUrl, | | }, | | imageUrls, | | }, | | } ; | | | | imageUrls.forEach { imagePreviewUrl } = { | | this. sections.push { | | view model: { | | primitive: { | | media: { | | url: imagePreviewUrl, | | mime type: 'image/jpeg', | | }, | | imagine type: 3, | | status: { | | status: 'READY', | | }, | | typename: 'GenAIImaginePrimitive', | | }, | | typename: 'GenAISingleLayoutViewModel', | | }, | | } ; | | } ; | | | | return this; | | } | | | | build { forwarded = true, includesUnifiedResponse = true, ...options } = {} { | | const contextInfo = forwarded | | ? { | | forwardingScore: 1, | | isForwarded: true, | | forwardedAiBotMessageInfo: { botJid: '0@bot' }, | | forwardOrigin: 4, | | } | | : {}; | | | | return { | | messageContextInfo: { | | deviceListMetadata: {}, | | deviceListMetadataVersion: 2, | | botMetadata: { | | pluginMetadata: {}, | | richResponseSourcesMetadata: { sources: this. richResponseSources }, | | }, | | }, | | botForwardedMessage: { | | message: { | | richResponseMessage: { | | messageType: 1, | | submessages: this. submessages, | | unifiedResponse: { | | data: includesUnifiedResponse ? Buffer.from JSON.stringify { response id: crypto.randomUUID , sections: this. sections } .toString 'base64' : '', | | }, | | contextInfo, | | }, | | }, | | }, | | }; | | } | | | | async send jid, { forwarded, includesUnifiedResponse, ...options } = {} { | | const msg = this.build { forwarded, includesUnifiedResponse, ...options } ; | | | | return await this. client.relayMessage jid, msg, { ...options } ; | | } | | | | static tokenizer code, lang = 'javascript' { | | const keywordsMap = { | | 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', | | , | | }; | | | | const TYPE MAP = { | | 0: 'DEFAULT', | | 1: 'KEYWORD', | | 2: 'METHOD', | | 3: 'STR', | | 4: 'NUMBER', | | 5: 'COMMENT', | | }; | | | | const keywords = keywordsMap lang || new Set ; | | const tokens = ; | | | | let i = 0; | | | | const push = content, type = { | | if content return; | | const last = tokens tokens.length - 1 ; | | if last && last.highlightType === type last.codeContent += content; | | else tokens.push { codeContent: content, highlightType: type } ; | | }; | | | | while i < code.length { | | const c = code i ; | | | | if /\s/.test c { | | let s = i; | | while i < code.length && /\s/.test code i i++; | | push code.slice s, i , 0 ; | | continue; | | } | | | | if c === '/' && code i + 1 === '/' { | | let s = i; | | i += 2; | | while i < code.length && code i == '\n' i++; | | push code.slice s, i , 5 ; | | continue; | | } | | | | if c === '"' || c === "'" || c === ' ' { | | let s = i; | | const q = c; | | i++; | | while i < code.length { | | if code i === '\\' && i + 1 < code.length i += 2; | | else if code i === q { | | i++; | | break; | | } else i++; | | } | | push code.slice s, i , 3 ; | | continue; | | } | | | | if / 0-9 /.test c { | | let s = i; | | while i < code.length && / 0-9. /.test code i i++; | | push code.slice s, i , 4 ; | | continue; | | } | | | | if / a-zA-Z $ /.test c { | | let s = i; | | while i < code.length && / a-zA-Z0-9 $ /.test code i i++; | | const word = code.slice s, i ; | | | | let type = 0; | | if keywords.has word type = 1; | | else { | | let j = i; | | while j < code.length && /\s/.test code j j++; | | if code j === ' ' type = 2; | | } | | | | push word, type ; | | continue; | | } | | | | push c, 0 ; | | i++; | | } | | | | return { | | codeBlock: tokens, | | unified codeBlock: tokens.map t = { | | content: t.codeContent, | | type: TYPE MAP t.highlightType , | | } , | | }; | | } | | | | static toTableMetadata arr { | | if Array.isArray arr || arr.length < 2 throw new Error 'Format tabel ngawur' ; | | | | const header, ...rows = arr; | | | | const maxLen = Math.max header.length, ...rows.map r = r.length ; | | | | const normalize = r = ...r, ...Array maxLen - r.length .fill '' ; | | | | const unified rows = | | { | | is header: true, | | cells: normalize header , | | }, | | ...rows.map r = { | | is header: false, | | cells: normalize r , | | } , | | ; | | | | const rowsMeta = unified rows.map r = { | | items: r.cells, | | ... r.is header ? { isHeading: true } : {} , | | } ; | | | | return { | | title: '', | | rows: rowsMeta, | | unified rows, | | }; | | } | | } | | | | export { VERSION, Button, ButtonV2, Carousel, AIRich }; |