npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

gifted-btns

v1.0.0

Published

This is a plugin to make baileys more interactive(send buttons)

Readme

Gifted Buttons

Installation

bun install gifted-btns
npm install gifted-btns
yarn add gifted-btns

Problem Statement

By default, Baileys cannot send interactive buttons. The root cause is that Baileys lacks the required binary node wrappers (biz, interactive, native_flow) that WhatsApp expects for interactive messages.

Solution

The enhanced functionality provided by this gifted-btns package provides the missing functionality by:

  1. Detecting button messages
  2. Converting Baileys interactiveButtons format to the proper protobuf structure
  3. Adding missing binary nodes (biz, interactive, native_flow, bot) via additionalNodes
  4. Automatically handling private vs group chat requirements

Key Features

  • No modifications to Baileys folders
  • Template functionality removed as requested
  • Automatic binary node injection for button messages
  • Private chat support (adds bot node with biz_bot: '1')
  • Group chat support (adds only biz node)
  • Backward compatibility (regular messages pass through unchanged)

Compatibility

  • Gifted-Baileys 2.0.0+
  • Baileys 7.0.0-rc.2+
  • ✅ Forks from Baileys 7.0.0-rc.2+
  • ✅ Node.js 20+
  • ✅ All button types supported by WhatsApp
  • ✅ Private and group chats

Quick Start (Most Common Case)

const { sendButtons } = require('gifted-btns');

await sendButtons(sock, jid, {
  title: 'Header Title',            // optional header
  text: 'Pick one option below',    // body
  footer: 'Footer text',            // optional footer
  buttons: [
    {
   id: 'quick_1', text: 'Quick Reply' },       // legacy simple shape auto‑converted
    {
      name: 'cta_url',
      buttonParamsJson: JSON.stringify({
        display_text: 'Open Site',
        url: 'https://example.com'
      })
    }
  ]
});

For full control (multiple advanced button kinds in one message) use sendInteractiveMessage with interactiveButtons directly.

const { sendInteractiveMessage } = require('gifted-btns');

await sendInteractiveMessage(sock, jid, {
  text: 'Advanced native flow demo',
  footer: 'All the things',
  interactiveButtons: [
    // Quick reply (explicit form)
    {
      name: 'quick_reply',
      buttonParamsJson: JSON.stringify({ display_text: 'Reply A', id: 'reply_a' })
    },
    // Single select picker (list inside a button)
    {
      name: 'single_select',
      buttonParamsJson: JSON.stringify({
        title: 'Pick One',
        sections: [{
          title: 'Choices',
          rows: [
            { header: 'H', title: 'Hello', description: 'Says hi', id: 'opt_hello' },
            { header: 'B', title: 'Bye', description: 'Says bye', id: 'opt_bye' }
          ]
        }]
      })
    }
  ]
});

Supported Button Types (Native Flow Names)

Below are the most common & observed name values for nativeFlowMessage.buttons[] along with their required JSON keys. You can mix several in one interactiveButtons array (WhatsApp will decide layout).

| Name | Purpose | buttonParamsJson (required keys) | |------|---------|----------------------------------| | quick_reply | Simple reply that sends its id back | { display_text, id } | | single_select | In‑button picker list | { title, sections:[{ title?, rows:[{ id, title, description?, header? }] }] } | | cta_url | Open URL | { display_text, url, merchant_url? } | | cta_copy | Copy text to clipboard | { display_text, copy_code } | | cta_call | Tap to dial | { display_text, phone_number } | | cta_catalog | Open business catalog | { display_text? } (WA may ignore extra keys) | | send_location | Request user location (special flow) | { display_text? } | | review_and_pay | Order / payment summary (special) | Payment structured payload (server‑validated) | | payment_info | Payment info flow | Payment structured payload | | mpm | Multi product message (catalog) | Vendor internal structure | | wa_payment_transaction_details | Show transaction | Transaction reference keys | | automated_greeting_message_view_catalog | Greeting -> catalog | (Minimal / internal) |

Not all special names are guaranteed to render outside official / business clients; unsupported ones are simply ignored by WhatsApp. Core stable ones for bots are: quick_reply, single_select, cta_url, cta_copy, cta_call.

Example: URL, Copy & Call Together

await sendInteractiveMessage(sock, jid, {
  text: 'Contact actions',
  interactiveButtons: [
    { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Docs', url: 'https://example.com' }) },
    { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copy Code', copy_code: 'ABC-123' }) },
    { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: 'Call Support', phone_number: '+1234567890' }) }
  ]
});

Example: Mixed Quick Replies + Catalog

await sendInteractiveMessage(sock, jid, {
  text: 'Explore products or reply',
  interactiveButtons: [
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hello', id: 'hi' }) },
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Pricing', id: 'pricing' }) },
    { name: 'cta_catalog', buttonParamsJson: JSON.stringify({}) }
  ]
});

Example: Location Request (Experimental)

await sendInteractiveMessage(sock, jid, {
  text: 'Please share your location',
  interactiveButtons: [
    { name: 'send_location', buttonParamsJson: JSON.stringify({ display_text: 'Share Location' }) }
  ]
});

Example: Single Select Menu

await sendInteractiveMessage(sock, jid, {
  text: 'Choose one item',
  interactiveButtons: [
    { name: 'single_select', buttonParamsJson: JSON.stringify({
        title: 'Menu',
        sections: [{
          title: 'Main',
          rows: [
            { id: 'it_1', title: 'First', description: 'First choice' },
            { id: 'it_2', title: 'Second', description: 'Second choice' }
          ]
        }]
    }) }
  ]
});

Tip: Legacy simple objects like { id: 'x', text: 'Label' } passed to sendButtons auto‑convert to quick_reply.

Technical Details

Binary Node Structure (What The Wrapper Injects)

Private chat: adds biz + interactive/native_flow + bot (biz_bot=1).

Group chat: adds only biz + interactive/native_flow.

When special first button names (review_and_pay, payment_info, mpm, etc.) are detected, version/name attributes change to match official client traffic so WhatsApp enables those flows.

Button Type Detection

The wrapper detects button types using the same logic as itsukichan:

  • listMessage → 'list'
  • buttonsMessage → 'buttons'
  • interactiveMessage.nativeFlowMessage → 'native_flow'

Content Conversion Flow

Authoring (you write):

{ text, footer, interactiveButtons: [{ name, buttonParamsJson }, ...] }

Wrapper builds (sent to WA):

{ interactiveMessage: { nativeFlowMessage: { buttons: [...] }, body:{ text }, footer:{ text } } }

Files Modified

Detailed API Reference: sendInteractiveMessage

Low‑level power helper used by all higher level wrappers. Use this when you need to:

  • Mix several advanced button kinds in one message (e.g. quick_reply + single_select + cta_url).
  • Provide pre‑built interactiveMessage content (after internal transformation) while still benefiting from automatic binary node injection.
  • Attach custom relay options (statusJidList, additionalAttributes, experimental fields) or manually append extra additionalNodes.

Signature

async function sendInteractiveMessage(sock, jid, content, options = {})

Parameters

  • sock: Active Baileys socket (must expose relayMessage, logger, authState or user).
  • jid: Destination WhatsApp JID (user or group). Auto‑detects group via WABinary.isJidGroup.
  • content: High‑level authoring object. Accepts either a regular Baileys message shape or the enhanced authoring shape:
    • text (string) Body text (mapped to interactiveMessage.body.text).
    • footer (string) Footer (mapped to interactiveMessage.footer.text).
    • title / subtitle (string) Optional header title (mapped to interactiveMessage.header.title).
    • interactiveButtons (Array) Array of button descriptors. Each item should be either:
      • { name: '<native_flow_name>', buttonParamsJson: JSON.stringify({...}) } (already normalized), or
      • A legacy quick reply shape { id, text } / { buttonId, buttonText: { displayText } } which is auto‑normalized to a quick_reply.
    • Any other Baileys message keys (e.g. contextInfo) pass through unchanged.
  • options: (Optional) Extra relay + generation options:
    • All fields accepted by generateWAMessageFromContent (e.g. custom timestamp).
    • additionalNodes (Array) Prepend your own binary nodes (the function appends required interactive nodes after detection).
    • additionalAttributes (Object) Extra attributes for the root relay stanza.
    • statusJidList, useCachedGroupMetadata (advanced Baileys relay options).

What It Does Internally

  1. Calls convertToInteractiveMessage(content) if interactiveButtons exist, producing:
    { interactiveMessage: { nativeFlowMessage: { buttons: [...] }, header?, body?, footer? } }
  2. Imports Baileys internal helpers (generateWAMessageFromContent, normalizeMessageContent, isJidGroup, generateMessageIDV2). Throws if unavailable.
  3. Builds a raw WAMessage bypassing normal send validation (lets unsupported interactive types through).
  4. Normalizes and determines button type via getButtonType then derives binary node tree with getButtonArgs.
  5. Injects required binary nodes:
    • Always a biz node (with nested interactive/native_flow/... for buttons and lists) when interactive.
    • Adds { tag: 'bot', attrs: { biz_bot: '1' } } automatically for private (1:1) chats enabling rendering of interactive flows.
  6. Relays the message using relayMessage with additionalNodes.
  7. Optionally emits the message locally (sock.upsertMessage) for private chats if sock.config.emitOwnEvents is set (groups are skipped to avoid duplicates).

Return Value

Resolves with the full constructed WAMessage object ({ key, message, messageTimestamp, ... }) so you can log/store/await acks exactly like a standard sock.sendMessage call.

Error Handling

  • Throws Socket is required if sock is null/undefined.
  • Throws WhiskeySockets functions not available if internal modules cannot be loaded (e.g. path changes). In such a case you may fall back to plain sock.sendMessage for non‑interactive messages.

Choosing Between Helpers

  • Use sendButtons / sendInteractiveButtonsBasic for simple quick replies + common CTA cases.
  • Use sendInteractiveMessage for any combination including single_select, special native flow names, or when you need to attach custom nodes.

Advanced Example: Mixed Buttons + List + Custom Node

const { sendInteractiveMessage } = require('gifted-btns');

await sendInteractiveMessage(sock, jid, {
  text: 'Pick or explore',
  footer: 'Advanced demo',
  interactiveButtons: [
    { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) },
    { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Docs', url: 'https://example.com' }) },
    { name: 'single_select', buttonParamsJson: JSON.stringify({
        title: 'Menu',
        sections: [{
          title: 'Options',
          rows: [
            { id: 'a', title: 'Alpha', description: 'First item' },
            { id: 'b', title: 'Beta', description: 'Second item' }
          ]
        }]
    }) }
  ]
}, {
  additionalNodes: [ { tag: 'biz', attrs: { experimental_flag: '1' } } ] // will be merged before auto interactive nodes
});

Special Native Flow Names & Effects

| First Button Name | Injected Node Variant | Notes | |-------------------|-----------------------|-------| | review_and_pay | biz with native_flow_name=order_details | Payment/order style flow | | payment_info | biz with native_flow_name=payment_info | Payment info flow | | mpm, cta_catalog, send_location, call_permission_request, wa_payment_transaction_details, automated_greeting_message_view_catalog | biz > interactive(native_flow v=1) > native_flow(v=2,name=<name>) | Specialized (may require official client) | | Anything else / mixed | biz > interactive(native_flow v=1) > native_flow(v=9,name=mixed) | Generic path covering standard quick replies, lists, CTAs |

Performance / Throughput

Cost is roughly equivalent to a standard sendMessage call; extra overhead is a small synchronous transformation + node injection. Suitable for high‑volume bots. Consider standard Baileys concurrency limits for large broadcast scenarios.

Debugging Tips

  • Temporary console log emitted: Interactive send: { type, nodes, private } – remove or redirect if noisy.
  • If buttons do not render: ensure first binary node injected is biz and private chats include the bot node.
  • Confirm each button's buttonParamsJson is valid JSON string (catch JSON.stringify mistakes early).

Common Mistakes

  • Forgetting to JSON.stringify buttonParamsJson payloads.
  • Using sendInteractiveMessage without a socket that includes relayMessage (e.g., passing a partially constructed object).
  • Adding your own bot node for private chats (not needed; auto added).
  • Expecting unsupported special flows (payments/catalog) to render in a non‑business account—WhatsApp may silently ignore them.

Minimal Raw Usage

If you already built a correct interactiveMessage object you can call:

await sendInteractiveMessage(sock, jid, {
  interactiveMessage: {
    nativeFlowMessage: {
      buttons: [ { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Hi', id: 'hi' }) } ]
    },
    body: { text: 'Direct native flow' }
  }
});

Result

You can now send all mainstream interactive button variants (quick replies, URL / copy / call CTAs, single select lists) plus experimental special flows from Baileys exactly like the official client, with automatic handling for groups vs private chats and without editing fork source.