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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nuiisweety/baileys

v0.1.19

Published

A WebSockets library for interacting with WhatsApp Web — forked STRICTLY from @whiskeysockets/baileys only, NOT from any other baileys fork. Modified by NuiiS4TORU.

Readme

📋 Daftar Isi

Instalasi  ·  QR Code  ·  Pairing Code  ·  Browser Identity  ·  Opsi Koneksi

Terima Pesan  ·  Handle Grup

Teks  ·  Gambar  ·  Video  ·  Audio & Voice Note  ·  Dokumen  ·  Sticker  ·  Lokasi  ·  Kontak

Reaksi  ·  Poll  ·  Poll Result  ·  Poll Update  ·  Forward  ·  Hapus Pesan  ·  Edit Pesan  ·  Pin Pesan

Album  ·  Event  ·  Group Status / Story  ·  Status Mention  ·  Channel (Newsletter)  ·  Flow Reply  ·  Button Reply  ·  Keep In Chat  ·  Scheduled Call  ·  Group Invite  ·  Product

View Once  ·  View Once V2  ·  Ephemeral  ·  Spoiler  ·  Group Status Wrap  ·  Lottie Sticker  ·  AI Icon  ·  Secure Meta Label

Buttons Location Header  ·  Buttons  ·  List Message  ·  Template Buttons  ·  Native Flow  ·  Carousel

List Reply  ·  Sticker Pack  ·  External Ad Reply  ·  Request Payment  ·  Invoice  ·  Order  ·  Disappearing Messages  ·  Raw Message

addText  ·  addCode  ·  addTable  ·  addImage  ·  addVideo  ·  addSource  ·  addReels  ·  addProduct  ·  addPost  ·  addTip  ·  addSuggest  ·  Map  ·  LaTeX  ·  Grid Image  ·  Inline Image  ·  Dynamic/GIF  ·  Content Items  ·  Rich Response  ·  Terima & Decode

📦 Instalasi

// CommonJS
const { default: makeWASocket } = require('@nuiisweety/baileys')

// ESM
import makeWASocket from '@nuiisweety/baileys'

📱 Koneksi QR Code

const {
  default: makeWASocket,
  useMultiFileAuthState,
  DisconnectReason,
  fetchLatestBaileysVersion
} = require('@nuiisweety/baileys')

async function start() {
  const { state, saveCreds } = await useMultiFileAuthState('auth_info')
  const { version } = await fetchLatestBaileysVersion()

  const sock = makeWASocket({
    version,
    auth: state,
    printQRInTerminal: true
  })

  sock.ev.on('creds.update', saveCreds)
  sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
    if (connection === 'close') {
      const code = lastDisconnect?.error?.output?.statusCode
      if (code !== DisconnectReason.loggedOut) start()
    } else if (connection === 'open') {
      console.log('Connected!')
    }
  })
}
start()

🔑 Koneksi Pairing Code

Cocok untuk server/VPS tanpa tampilan terminal.

const sock = makeWASocket({ version, auth: state, printQRInTerminal: false })

sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
  if (connection === 'close') {
    if (lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut) start()
  }
})

// Nomor HP format internasional tanpa +
if (!sock.authState.creds.registered) {
  const code = await sock.requestPairingCode('628123456789')
  console.log('Pairing code:', code)
}

// Custom pairing code (8 karakter)
const code = await sock.requestPairingCode('628123456789', 'NUYY2822')

🌐 Browser Identity

const { Browsers } = require('@nuiisweety/baileys')

makeWASocket({
  browser: Browsers.macOS('Chrome'),    // Mac OS — Chrome
  browser: Browsers.macOS('Firefox'),   // Mac OS — Firefox
  browser: Browsers.windows('Chrome'),  // Windows — Chrome
  browser: Browsers.ubuntu('Chrome'),   // Ubuntu — Chrome
  browser: Browsers.baileys('Chrome'),  // Baileys — Chrome
  browser: ['MyBot', 'Chrome', '1.0.0'] // custom
})

⚙️ Opsi Koneksi

makeWASocket({
  version,
  auth: state,
  printQRInTerminal: true,
  browser: Browsers.macOS('Chrome'),
  connectTimeoutMs: 20000,
  keepAliveIntervalMs: 30000,
  markOnlineOnConnect: true,
  syncFullHistory: false,
  generateHighQualityLinkPreview: false,
  emitOwnEvents: true,
  getMessage: async key => store?.messages?.[key.remoteJid]?.get(key.id)?.message
})

📬 Terima Pesan

sock.ev.on('messages.upsert', async ({ messages, type }) => {
  if (type !== 'notify') return

  for (const m of messages) {
    if (!m.message) continue
    if (m.key.fromMe) continue

    const jid = m.key.remoteJid
    const isGroup = jid.endsWith('@g.us')
    const sender = isGroup ? m.key.participant : jid

    const text =
      m.message.conversation ||
      m.message.extendedTextMessage?.text ||
      m.message.imageMessage?.caption ||
      m.message.videoMessage?.caption || ''

    await sock.readMessages([m.key])
    await sock.sendPresenceUpdate('composing', jid)
  }
})

👥 Handle Grup

// Anggota masuk/keluar
sock.ev.on('group-participants.update', ({ id, participants, action }) => {
  // action: 'add' | 'remove' | 'promote' | 'demote'
})

// Info grup diupdate
sock.ev.on('groups.update', updates => {})

// Metadata grup
const meta = await sock.groupMetadata(groupJid)
console.log(meta.subject, meta.participants)

// Semua grup
const groups = await sock.groupFetchAllParticipating()

// Tambah / keluarkan / promote / demote
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'add')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'remove')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'promote')
await sock.groupParticipantsUpdate(groupJid, ['[email protected]'], 'demote')

// Ubah nama / deskripsi
await sock.groupUpdateSubject(groupJid, 'Nama Baru')
await sock.groupUpdateDescription(groupJid, 'Deskripsi baru')

// Kunci / buka grup
await sock.groupSettingUpdate(groupJid, 'announcement')
await sock.groupSettingUpdate(groupJid, 'not_announcement')

// Mention semua anggota
const meta2 = await sock.groupMetadata(groupJid)
const members = meta2.participants.map(p => p.id)
await sock.sendMessage(groupJid, {
  text: members.map(m => `@${m.split('@')[0]}`).join(' '),
  mentions: members
})

💬 Teks

await sock.sendMessage(jid, { text: 'Halo!' })

// Dengan mention
await sock.sendMessage(jid, {
  text: '@628xxx halo!',
  mentions: ['[email protected]']
}, { quoted: m })

🖼️ Gambar

// URL
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Keterangan'
})

// Buffer
await sock.sendMessage(jid, {
  image: fs.readFileSync('image.jpg'),
  caption: 'Keterangan'
})

🎬 Video

await sock.sendMessage(jid, {
  video: { url: 'https://example.com/video.mp4' },
  caption: 'Keterangan',
  gifPlayback: false  // true = GIF looping
})

// Video note (bubble)
await sock.sendMessage(jid, {
  video: fs.readFileSync('video.mp4'),
  ptv: true
})

🎙️ Audio & Voice Note

// Audio biasa
await sock.sendMessage(jid, {
  audio: { url: 'https://example.com/audio.mp3' },
  mimetype: 'audio/mpeg'
})

// Voice note (PTT)
await sock.sendMessage(jid, {
  audio: fs.readFileSync('voice.ogg'),
  mimetype: 'audio/ogg; codecs=opus',
  ptt: true
})

📄 Dokumen

await sock.sendMessage(jid, {
  document: fs.readFileSync('file.pdf'),
  mimetype: 'application/pdf',
  fileName: 'dokumen.pdf',
  caption: 'File ini'
})

🎴 Sticker

await sock.sendMessage(jid, { sticker: fs.readFileSync('sticker.webp') })

📍 Lokasi

await sock.sendMessage(jid, {
  location: {
    degreesLatitude: -6.2088,
    degreesLongitude: 106.8456,
    name: 'Monas',
    address: 'Jakarta Pusat'
  }
})

👤 Kontak

await sock.sendMessage(jid, {
  contacts: {
    displayName: 'Nama',
    contacts: [{
      vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:Nama\nTEL:+62812345678\nEND:VCARD'
    }]
  }
})

❤️ Reaksi

await sock.sendMessage(jid, { react: { text: '❤️', key: m.key } })

// Hapus reaksi
await sock.sendMessage(jid, { react: { text: '', key: m.key } })

📊 Poll

await sock.sendMessage(jid, {
  poll: {
    name: 'Pilih salah satu',
    values: ['Opsi A', 'Opsi B', 'Opsi C'],
    selectableCount: 1  // 0 = boleh pilih banyak
  }
})

// Poll dengan fitur tambahan
await sock.sendMessage(jid, {
  poll: {
    name: 'Nama Poll',
    values: ['Opsi A', 'Opsi B'],
    selectableCount: 1,
    endDate: new Date('2025-12-31'),
    hideVoter: true,
    canAddOption: false,
    toAnnouncementGroup: false
  }
})

// Quiz (Poll dengan jawaban benar)
await sock.sendMessage(jid, {
  poll: {
    name: 'Ibu kota Indonesia?',
    values: ['Jakarta', 'Surabaya', 'Bandung'],
    selectableCount: 1,
    pollType: 1,           // 1 = QUIZ
    correctAnswer: 'Jakarta'
  }
})

📈 Poll Result

await sock.sendMessage(jid, {
  pollResult: {
    name: 'Nama Poll',
    votes: [
      { name: 'Opsi A', voteCount: 10 },
      { name: 'Opsi B', voteCount: 5 }
    ],
    pollType: 0  // 0 = POLL, 1 = QUIZ
  }
})

🔄 Poll Update

await sock.sendMessage(jid, {
  pollUpdate: {
    key: pollMessage.key,
    vote: encryptedVotePayload,
    metadata: optionalMetadata
  }
})

↗️ Forward

await sock.sendMessage(jid, {
  forward: targetMessage,
  force: true  // paksa tampil sebagai forwarded
})

🗑️ Hapus Pesan

await sock.sendMessage(jid, { delete: m.key })

✏️ Edit Pesan

await sock.sendMessage(jid, {
  text: 'Teks yang sudah diedit',
  edit: m.key
})

📌 Pin Pesan

// Pin
await sock.sendMessage(jid, {
  pin: m.key,
  type: 1,
  time: 86400  // 86400=1hr | 604800=7hr | 2592000=30hr
})

// Unpin
await sock.sendMessage(jid, { pin: m.key, type: 2 })

🗂️ Album

await sock.sendMessage(jid, {
  album: [
    { image: { url: 'https://example.com/1.jpg' }, caption: 'Foto 1' },
    { image: { url: 'https://example.com/2.jpg' }, caption: 'Foto 2' },
    { video: { url: 'https://example.com/vid.mp4' }, caption: 'Video' }
  ]
})
// minimal 2 media

📅 Event

await sock.sendMessage(jid, {
  event: {
    name: 'Nama Acara',
    description: 'Deskripsi',
    startDate: new Date('2025-12-01T10:00:00'),
    endDate: new Date('2025-12-01T12:00:00'),
    location: {
      degreesLatitude: -6.2088,
      degreesLongitude: 106.8456,
      name: 'Monas, Jakarta'
    },
    extraGuestsAllowed: true
  }
})

// Event dengan scheduled call
await sock.sendMessage(jid, {
  event: {
    name: 'Meeting Online',
    startDate: new Date('2025-12-01T10:00:00'),
    call: 'audio',   // 'audio' | 'video'
    isScheduleCall: true
  }
})

📖 Group Status / Group Story

// Teks
await sock.sendMessage(groupJid, {
  groupStatusMessage: { text: 'Status teks' }
})

// Gambar
await sock.sendMessage(groupJid, {
  groupStatusMessage: {
    image: { url: 'https://example.com/image.jpg' },
    caption: 'Keterangan'
  }
})

// Video
await sock.sendMessage(groupJid, {
  groupStatusMessage: {
    video: { url: 'https://example.com/video.mp4' },
    caption: 'Keterangan'
  }
})

// Audio
await sock.sendMessage(groupJid, {
  groupStatusMessage: {
    audio: { url: 'https://example.com/audio.mp3' },
    mimetype: 'audio/mpeg'
  }
})

// Audio Voice Note (PTT)
await sock.sendMessage(groupJid, {
  groupStatusMessage: {
    audio: fs.readFileSync('voice.ogg'),
    mimetype: 'audio/ogg; codecs=opus',
    ptt: true
  }
})

Catatan audio: Saat audio dikirim sebagai group status (groupStatusMessageV2), pesan dikirim dengan type="text" dan tanpa mediatype attribute — perilaku sama dengan Baileys upstream. Berbeda dengan media lain (gambar/video) yang dikirim dengan type="media".

🏷️ Status Mention

// Kirim ke array JID untuk trigger status mention
await sock.sendMessage(['[email protected]', '[email protected]'], {
  text: 'Hei kamu!',
  backgroundColor: '#ff6b9d'
})

// Gambar dengan mention
await sock.sendMessage(['[email protected]'], {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Untuk kamu'
})

Untuk grup, otomatis pakai groupStatusMentionMessage.

📡 Channel (Newsletter)

Fix v0.1.16 — Pengiriman media ke channel sebelumnya tidak berfungsi karena dua bug: mediatype attribute tidak dikirim ke server dan thumbnail tidak digenerate. Keduanya sudah diperbaiki di versi ini.

Hanya admin/owner channel yang bisa posting. JID channel menggunakan format @newsletter.

const channelJid = '120363425154682710@newsletter'

// Teks
await sock.sendMessage(channelJid, { text: 'Halo dari channel!' })

// Gambar dari URL
await sock.sendMessage(channelJid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Keterangan gambar'
})

// Gambar dari file lokal
await sock.sendMessage(channelJid, {
  image: { url: './gambar.jpg' },
  caption: 'Dari file lokal'
})

// Gambar dari Buffer
await sock.sendMessage(channelJid, {
  image: fs.readFileSync('gambar.jpg'),
  caption: 'Dari buffer'
})

// Video
await sock.sendMessage(channelJid, {
  video: { url: 'https://example.com/video.mp4' },
  caption: 'Keterangan video'
})

// Audio
await sock.sendMessage(channelJid, {
  audio: { url: 'https://example.com/audio.mp3' },
  mimetype: 'audio/mpeg'
})

// Dokumen
await sock.sendMessage(channelJid, {
  document: { url: 'https://example.com/file.pdf' },
  mimetype: 'application/pdf',
  fileName: 'dokumen.pdf'
})

// Poll
await sock.sendMessage(channelJid, {
  poll: {
    name: 'Pertanyaan untuk subscriber',
    values: ['Opsi A', 'Opsi B', 'Opsi C'],
    selectableCount: 1
  }
})

// Edit pesan channel
await sock.sendMessage(channelJid, {
  text: 'Teks yang sudah diedit',
  edit: pesanLama.key
})

// Hapus pesan channel
await sock.sendMessage(channelJid, { delete: pesanLama.key })

Catatan: Channel tidak mendukung enkripsi E2E — media di-upload sebagai file mentah (unencrypted), berbeda dengan chat biasa.

🔁 Flow Reply

await sock.sendMessage(jid, {
  flowReply: {
    name: 'quick_reply',
    paramsJson: '{"id":"btn1"}',
    text: 'Teks balasan',
    format: 1,
    version: 1
  }
})

🔘 Button Reply

// Template button
await sock.sendMessage(jid, {
  buttonReply: { displayText: 'Opsi yang dipilih', id: 'btn_id', index: 0 },
  type: 'template'
})

// Plain button
await sock.sendMessage(jid, {
  buttonReply: { displayText: 'Opsi yang dipilih', id: 'btn_id' },
  type: 'plain'
})

📎 Keep In Chat

await sock.sendMessage(jid, {
  keep: m.key,
  keepType: 1  // 1 = keep, 2 = unkeep
})

📞 Scheduled Call

await sock.sendMessage(jid, {
  call: {
    time: Date.now() + 3600000,
    type: 1,  // 1 = voice, 2 = video
    title: 'Meeting'
  }
})

🔗 Group Invite

await sock.sendMessage(jid, {
  groupInvite: {
    jid: '[email protected]',
    inviteCode: 'kode',
    inviteExpiration: Date.now() + 86400000,
    subject: 'Nama Grup',
    text: 'Bergabunglah bersama kami'
  }
})

🛍️ Product

await sock.sendMessage(jid, {
  product: {
    productImage: fs.readFileSync('produk.jpg'),
    productId: 'prod_123',
    title: 'Nama Produk',
    description: 'Deskripsi produk',
    currencyCode: 'IDR',
    priceAmount1000: 50000000,
    retailerId: 'sku_001',
    url: 'https://toko.example.com/produk'
  },
  businessOwnerJid: '[email protected]'  // wajib diisi
})

👁️ View Once

await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  viewOnce: true
})

👁️ View Once V2

await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  viewOnceV2: true
})

// viewOnceV2Extension
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  viewOnceV2Extension: true
})

⏳ Ephemeral

await sock.sendMessage(jid, {
  text: 'Pesan ini ephemeral',
  ephemeral: true
})

🙈 Spoiler

await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Spoiler!',
  spoiler: true
})

🎭 Group Status Wrap

Wrapper groupStatus: true membungkus pesan apapun ke dalam groupStatusMessageV2 secara otomatis.

// Teks
await sock.sendMessage(jid, {
  text: 'Status di grup ini',
  groupStatus: true
})

// Gambar
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Keterangan',
  groupStatus: true
})

// Audio
await sock.sendMessage(jid, {
  audio: { url: 'https://example.com/audio.mp3' },
  mimetype: 'audio/mpeg',
  groupStatus: true
})

Catatan audio: Pesan audio yang dibungkus groupStatus: true dikirim dengan type="text" dan tanpa mediatype (perilaku Baileys upstream). Ini hanya berlaku untuk audio — gambar dan video tetap dikirim sebagai type="media".

🎞️ Lottie Sticker

await sock.sendMessage(jid, {
  sticker: fs.readFileSync('sticker.webp'),
  isLottie: true
})

🤖 AI Icon

// Hanya untuk private chat (bukan grup)
await sock.sendMessage(jid, { text: 'Respons dari AI', ai: true })

await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Dihasilkan oleh AI',
  ai: true
})

🔒 Secure Meta Service Label

await sock.sendMessage(jid, {
  text: 'Pesan layanan resmi',
  secureMetaServiceLabel: true
})

📍 Buttons dengan Location Header

Kirim tombol menggunakan buttonsMessage secara langsung — berguna untuk header tipe Location (headerType: 6) yang tidak bisa dibuat lewat shorthand buttons.

// Tombol dengan header lokasi (headerType 6)
await sock.sendMessage(jid, {
  buttonsMessage: {
    locationMessage: {
      degreesLatitude: 0,
      degreesLongitude: 0,
      name: 'NuiiSweety',
      address: 'NuiiSweety Bot',
      jpegThumbnail: './src/img/menu.jpg'  // path file, URL, atau Buffer
    },
    contentText: 'Pilih menu di bawah ini',
    footerText: 'Powered by @nuiisweety/baileys',
    buttons: [
      { buttonId: 'allmenu', buttonText: { displayText: 'All Menu' }, type: 1 }
    ],
    headerType: 6
  }
})

// Header kosong (teks saja) — headerType 1
await sock.sendMessage(jid, {
  buttonsMessage: {
    contentText: 'Pilih opsi',
    footerText: 'Bot Footer',
    buttons: [
      { buttonId: 'yes', buttonText: { displayText: 'Ya' },    type: 1 },
      { buttonId: 'no',  buttonText: { displayText: 'Tidak' }, type: 1 }
    ],
    headerType: 1
  }
})

Nilai headerType:

| Nilai | Tipe Header | |:-----:|-------------| | 1 | Teks (contentText) | | 2 | Gambar (imageMessage) | | 3 | Video (videoMessage) | | 4 | Dokumen (documentMessage) | | 6 | Lokasi (locationMessage) |

jpegThumbnail pada locationMessage bisa berupa path file lokal, URL, atau Buffer — otomatis diproses oleh Baileys.

🎛️ Buttons

// Teks dengan tombol (quick reply)
await sock.sendMessage(jid, {
  text: 'Pilih salah satu',
  footer: 'Footer pesan',
  buttons: [
    { id: 'btn1', text: 'Tombol 1' },
    { id: 'btn2', text: 'Tombol 2' },
    { id: 'btn3', text: 'Tombol 3' }
  ]
})

// Gambar sebagai header
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Isi pesan',
  footer: 'Footer',
  buttons: [
    { id: 'btn1', text: 'Pilih A' },
    { id: 'btn2', text: 'Pilih B' }
  ]
})

// Shorthand: sections langsung di button (otomatis jadi single_select)
await sock.sendMessage(jid, {
  text: 'Pilih menu',
  buttons: [{
    text: 'Buka Menu',
    sections: [{
      title: 'Kategori A',
      rows: [
        { id: 'row1', title: 'Item 1', description: 'Deskripsi' },
        { id: 'row2', title: 'Item 2' }
      ]
    }]
  }]
})

Field tombol: gunakan text atau buttonText untuk label. Quick reply pakai id/buttonId. Native flow pakai name + paramsJson.

📋 List Message

await sock.sendMessage(jid, {
  text: 'Silakan pilih',       // → description (isi pesan)
  title: 'Judul List',         // → title (judul di atas list)
  buttonText: 'Buka Daftar',   // → teks tombol pembuka
  footer: 'Footer pesan',      // → footerText
  sections: [
    {
      title: 'Bagian 1',
      rows: [
        { id: 'row1', title: 'Pilihan A', description: 'Deskripsi A' },
        { id: 'row2', title: 'Pilihan B', description: 'Deskripsi B' }
      ]
    },
    {
      title: 'Bagian 2',
      rows: [
        { id: 'row3', title: 'Pilihan C' }
      ]
    }
  ]
})

Trigger key: sections. listType otomatis di-set ke SINGLE_SELECT. Field text di-map ke description — gunakan title untuk judul list.

🗃️ Template Buttons

// Teks
await sock.sendMessage(jid, {
  text: 'Pesan template',
  footer: 'Footer',
  id: 'template-unik',  // opsional, auto-generate jika tidak diisi
  templateButtons: [
    { id: 'btn1', text: 'Quick Reply' },
    { url: 'https://example.com', text: 'Kunjungi Website' },
    { call: '+62812345678', text: 'Hubungi Kami' }
  ]
})

// Gambar sebagai header
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/img.jpg' },
  caption: 'Isi pesan',
  title: 'Judul',
  footer: 'Footer',
  templateButtons: [
    { id: 'btn1', text: 'Klik Sini' },
    { url: 'https://example.com', text: 'Buka Link' }
  ]
})

Tipe tombol: { id, text } → quick reply  ·  { url, text } → URL button  ·  { call, text } → call button.

🌊 Native Flow

// Quick reply
await sock.sendMessage(jid, {
  text: 'Pilih aksi',
  footer: 'Footer pesan',
  nativeFlow: [
    { id: '1', text: 'Opsi 1' },
    { id: '2', text: 'Opsi 2' }
  ]
})

// URL button
await sock.sendMessage(jid, {
  text: 'Kunjungi kami',
  nativeFlow: [
    { url: 'https://example.com', text: 'Buka Website' }
  ]
})

// Copy code button
await sock.sendMessage(jid, {
  text: 'Kode promo kamu',
  nativeFlow: [
    { copy: 'PROMO2025', text: 'Salin Kode' }
  ]
})

// Call button
await sock.sendMessage(jid, {
  text: 'Hubungi kami',
  nativeFlow: [
    { call: '+62812345678', text: 'Telepon Sekarang' }
  ]
})

// Single select (list dalam button)
await sock.sendMessage(jid, {
  text: 'Pilih menu',
  nativeFlow: [
    {
      sections: [{
        title: 'Kategori A',
        rows: [
          { id: 'row1', title: 'Item 1', description: 'Deskripsi' },
          { id: 'row2', title: 'Item 2' }
        ]
      }],
      text: 'Buka Daftar'
    }
  ]
})

// Gambar sebagai header
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/img.jpg' },
  caption: 'Isi pesan',
  title: 'Judul',
  subtitle: 'Subjudul',
  footer: 'Footer',
  nativeFlow: [
    { id: '1', text: 'Tombol 1' }
  ]
})

// offerText — limited time offer banner
await sock.sendMessage(jid, {
  text: 'Penawaran terbatas!',
  offerText: 'Diskon 50%',
  offerUrl: 'https://example.com/promo',
  offerCode: 'DISKON50',
  offerExpiration: 1800000000,
  nativeFlow: [
    { id: 'claim', text: 'Klaim Sekarang' }
  ]
})

// optionText — tombol masuk ke bottom sheet
await sock.sendMessage(jid, {
  text: 'Pilih opsi',
  optionText: 'Lihat Semua Opsi',
  optionTitle: 'Daftar Pilihan',
  nativeFlow: [
    { id: '1', text: 'Opsi A' },
    { id: '2', text: 'Opsi B' }
  ]
})

Tipe tombol ditentukan otomatis: id → quick_reply  ·  url → cta_url  ·  copy → cta_copy  ·  call → cta_call  ·  sections → single_select. Field icon opsional (contoh: 'CHECK', 'LINK').

🎠 Carousel

await sock.sendMessage(jid, {
  text: 'Carousel utama',
  footer: 'Footer utama',
  cards: [
    {
      image: { url: 'https://example.com/1.jpg' },
      title: 'Kartu 1',
      caption: 'Deskripsi kartu 1',
      footer: 'Footer kartu 1',
      nativeFlow: [
        { name: 'quick_reply', paramsJson: '{"id":"c1"}', text: 'Pilih Ini' }
      ]
    },
    {
      image: { url: 'https://example.com/2.jpg' },
      title: 'Kartu 2',
      caption: 'Deskripsi kartu 2',
      nativeFlow: [
        { name: 'quick_reply', paramsJson: '{"id":"c2"}', text: 'Pilih Itu' }
      ]
    }
  ]
})

🤖 Rich Message (AI)

Rich message menggunakan format AIRichResponseMessage yang tampil seperti respons AI di WhatsApp. Semua tipe bisa dipakai secara flat (field langsung) atau dikombinasikan via richResponse: [...].

⚠️ Penting: contentText, headerText, footerText, dan disclaimerText bukan trigger key. Jika hanya field itu yang dikirim, pesan tidak akan diproses sebagai rich message dan menghasilkan Error: Invalid media type. Selalu sertakan minimal satu trigger key (code, table, richImage, richVideo, richResponse, dll).

✍️ addText

// ✅ Cara benar — pakai richResponse array untuk teks murni
await sock.sendMessage(jid, {
  richResponse: [
    { text: 'Ini teks dari AI dengan *markdown* dan **bold**.' }
  ],
  headerText: 'Judul',
  footerText: 'Footer',
  disclaimerText: 'Generated by AI'
})
// ❌ SALAH — contentText saja tidak trigger rich message
await sock.sendMessage(jid, {
  contentText: 'halooo',
  headerText: 'Judul'
  // → Error: Invalid media type
})

💻 addCode

await sock.sendMessage(jid, {
  code: 'console.log("Hello World!")',
  language: 'javascript',  // default: 'javascript'
  headerText: 'Contoh kode:',
  footerText: 'Semoga membantu',
  disclaimerText: 'Generated by AI'
})

Bahasa yang didukung:

| Bahasa | Key | |--------|-----| | JavaScript | javascript, js | | TypeScript | typescript, ts | | Python | python, py | | Go | go, golang | | C++ | cpp, c++ | | Rust | rust, rs | | Java | java | | PHP | php | | Ruby | ruby, rb | | Kotlin | kotlin, kt | | Swift | swift | | C | c | | SQL/MySQL/PostgreSQL | sql, mysql, postgresql, sqlite | | Bash/Shell | bash, sh, shell | | HTML | html | | CSS | css | | JSON | json | | YAML | yaml, yml |

📊 addTable

await sock.sendMessage(jid, {
  table: [
    ['Nama', 'Usia', 'Kota'],   // baris pertama = header
    ['Hana', '20', 'Jakarta'],
    ['Risa', '22', 'Bandung'],
    ['Yuki', '21', 'Surabaya']
  ],
  title: 'Data Anggota',
  headerText: 'Berikut datanya',
  footerText: 'Data per Juni 2025'
})

// Tanpa baris header
await sock.sendMessage(jid, {
  table: [
    ['Item A', 'Rp 10.000'],
    ['Item B', 'Rp 20.000']
  ],
  noHeading: true,
  title: 'Daftar Harga'
})

🖼️ addImage

// Satu gambar
await sock.sendMessage(jid, {
  richImage: 'https://example.com/photo.jpg',
  headerText: 'Gambar untuk kamu'
})

// Beberapa gambar (grid)
await sock.sendMessage(jid, {
  richImage: [
    'https://example.com/photo1.jpg',
    'https://example.com/photo2.jpg',
    'https://example.com/photo3.jpg'
  ],
  headerText: 'Koleksi gambar'
})

🎬 addVideo

// Satu video
await sock.sendMessage(jid, {
  richVideo: 'https://example.com/video.mp4',
  headerText: 'Video untuk kamu'
})

// Dengan durasi (format: 'url|durasi_detik')
await sock.sendMessage(jid, {
  richVideo: 'https://example.com/video.mp4|120',
  headerText: 'Video berdurasi 2 menit'
})

// Beberapa video
await sock.sendMessage(jid, {
  richVideo: [
    'https://example.com/video1.mp4|60',
    'https://example.com/video2.mp4|90'
  ],
  headerText: 'Playlist video'
})

🔗 addSource

await sock.sendMessage(jid, {
  source: [
    {
      url: 'https://example.com/artikel',
      title: 'Judul Artikel',
      display_name: 'Example.com',
      subtitle: 'Sumber terpercaya',
      favicon: 'https://example.com/favicon.ico'
    },
    {
      url: 'https://docs.example.com/api',
      title: 'Dokumentasi API',
      display_name: 'Docs',
      subtitle: 'Referensi lengkap'
    }
  ],
  headerText: 'Sumber referensi'
})

| Field | Tipe | Keterangan | |-------|------|------------| | url | string | URL tujuan (wajib) | | title | string | Judul sumber | | display_name | string | Nama tampilan | | subtitle | string | Subjudul/deskripsi singkat | | favicon | string | URL favicon (opsional) | | source_type | string | Default 'THIRD_PARTY' |

🎥 addReels

await sock.sendMessage(jid, {
  reels: [
    {
      username: 'creator1',
      videoUrl: 'https://example.com/reel1.mp4',
      thumbnailUrl: 'https://example.com/thumb1.jpg',
      profileIconUrl: 'https://example.com/avatar1.jpg',
      reels_title: 'Judul Reel 1',
      likes_count: 1200,
      view_count: 50000,
      reel_source: 'IG',
      is_verified: true
    }
  ],
  headerText: 'Rekomendasi video'
})

| Field | Tipe | Keterangan | |-------|------|------------| | username / title | string | Nama kreator | | videoUrl / url | string | URL video | | thumbnailUrl / thumbnail | string | URL thumbnail | | profileIconUrl / profile_url | string | URL foto profil | | reels_title | string | Judul reel | | likes_count / like | number | Jumlah like | | view_count / view | number | Jumlah views | | reel_source / source | string | 'IG', 'TT', dll | | is_verified / verified | boolean | Akun terverifikasi |

🛍️ addProduct

// Satu produk (Single layout)
await sock.sendMessage(jid, {
  richProduct: {
    title: 'Nama Produk',
    brand: 'Nama Brand',
    price: 'Rp 150.000',
    sale_price: 'Rp 120.000',
    product_url: 'https://toko.example.com/produk',
    image_url: 'https://example.com/produk.jpg'
  },
  headerText: 'Rekomendasi produk untukmu'
})

// Beberapa produk (HScroll layout)
await sock.sendMessage(jid, {
  richProduct: [
    {
      title: 'Produk A',
      price: 'Rp 50.000',
      product_url: 'https://toko.example.com/a',
      image_url: 'https://example.com/a.jpg'
    },
    {
      title: 'Produk B',
      price: 'Rp 75.000',
      sale_price: 'Rp 60.000',
      product_url: 'https://toko.example.com/b',
      image_url: 'https://example.com/b.jpg'
    }
  ],
  headerText: 'Produk pilihan hari ini'
})

Object tunggal → Single layout  ·  Array → HScroll layout

📝 addPost

// Satu post
await sock.sendMessage(jid, {
  richPost: {
    username: 'namauser',
    profile_picture_url: 'https://example.com/avatar.jpg',
    is_verified: true,
    thumbnail_url: 'https://example.com/thumb.jpg',
    post_caption: 'Caption post ini',
    likes_count: 5000,
    post_url: 'https://instagram.com/p/xxx',
    source_app: 'INSTAGRAM',
    orientation: 'LANDSCAPE'
  },
  headerText: 'Post terpopuler'
})

// Beberapa post (carousel horizontal)
await sock.sendMessage(jid, {
  richPost: [
    {
      username: 'user1',
      thumbnail_url: 'https://example.com/thumb1.jpg',
      likes_count: 3000,
      post_url: 'https://instagram.com/p/aaa',
      source_app: 'INSTAGRAM'
    },
    {
      username: 'user2',
      thumbnail_url: 'https://example.com/thumb2.jpg',
      likes_count: 1500,
      post_url: 'https://tiktok.com/@user2/video/yyy',
      source_app: 'TIKTOK'
    }
  ],
  headerText: 'Post trending hari ini'
})

💡 addTip

await sock.sendMessage(jid, {
  tip: 'Informasi ini dibuat oleh AI berdasarkan data terbaru.',
  headerText: 'Ringkasan'
})

🔮 addSuggest

// Beberapa saran (tampil sebagai pill button)
await sock.sendMessage(jid, {
  contentText: 'Apakah ada yang ingin kamu tanyakan?',
  suggest: [
    'Contoh kodenya?',
    'Apa perbedaannya?',
    'Kapan sebaiknya digunakan?',
    'Ada alternatif lain?'
  ]
})

🗺️ Map (Rich)

await sock.sendMessage(jid, {
  map: {
    centerLatitude: -6.2088,
    centerLongitude: 106.8456,
    latitudeDelta: 0.05,
    longitudeDelta: 0.05,
    showInfoList: true,
    annotations: [
      {
        number: 1,
        latitude: -6.2088,
        longitude: 106.8456,
        title: 'Jakarta Pusat',
        body: 'Ibu kota Indonesia'
      }
    ]
  },
  headerText: 'Lokasi yang kamu cari'
})

🧮 LaTeX (Rich)

url di setiap expression akan otomatis di-generate dari latex.codecogs.com jika tidak diisi.

await sock.sendMessage(jid, {
  latex: {
    text: 'Rumus-rumus fisika dasar:',
    expressions: [
      { expression: 'F = ma', width: 80, height: 35 },
      { expression: 'v = u + at', width: 100, height: 35 },
      { expression: 's = ut + \\frac{1}{2}at^2', width: 160, height: 50 }
    ]
  },
  headerText: 'Hukum Newton'
})

🖼️ Grid Image (Rich)

await sock.sendMessage(jid, {
  gridImage: {
    gridImageUrl: 'https://picsum.photos/id/10/300/300',
    imageUrls: ['https://picsum.photos/id/11/300/300']
  },
  headerText: 'Hasil pencarian gambar'
})

📸 Inline Image (Rich)

await sock.sendMessage(jid, {
  inlineImage: {
    imageUrl: 'https://picsum.photos/id/237/300/300',
    imageText: 'Ilustrasi gambar',
    alignment: 2,    // 0 = leading, 1 = trailing, 2 = center
    tapLinkUrl: 'https://picsum.photos'
  },
  headerText: 'Berikut ilustrasinya'
})

🎞️ Dynamic / GIF (Rich)

await sock.sendMessage(jid, {
  dynamic: {
    url: 'https://example.com/animation.gif',
    type: 2,      // 1 = IMAGE, 2 = GIF
    version: 1,
    loopCount: 0  // 0 = loop selamanya
  },
  headerText: 'Animasi untuk kamu'
})

📦 Content Items (Rich)

await sock.sendMessage(jid, {
  contentItems: {
    contentType: 1,  // 0 = DEFAULT, 1 = CAROUSEL
    items: [
      {
        kind: 'reel',
        title: 'Tutorial Coding JavaScript',
        profileIconUrl: 'https://example.com/avatar1.jpg',
        thumbnailUrl: 'https://example.com/thumb1.jpg',
        videoUrl: 'https://example.com/video1.mp4'
      }
    ]
  },
  headerText: 'Rekomendasi video'
})

🎁 Rich Response (Gabungan)

await sock.sendMessage(jid, {
  richResponse: [
    { text: 'Penjelasan singkat:' },
    { code: 'const x = 1 + 1', language: 'javascript' },
    {
      table: [['Kolom A', 'Kolom B'], ['Nilai 1', 'Nilai 2']],
      title: 'Hasil'
    },
    { richImage: ['https://example.com/img1.jpg', 'https://example.com/img2.jpg'] },
    { source: [{ url: 'https://example.com', title: 'Sumber Data' }] },
    { tip: 'Informasi ini dihasilkan secara otomatis.' },
    { suggest: ['Tanya lebih lanjut', 'Lihat contoh lain'] }
  ],
  disclaimerText: 'Generated by AI'
})

📥 Terima & Decode Rich Message

const { normalizeMessageContent, parseRichMessage } = require('@nuiisweety/baileys')

sock.ev.on('messages.upsert', async ({ messages }) => {
  for (const msg of messages) {
    const inner = normalizeMessageContent(msg.message)
    const parsed = parseRichMessage(inner?.richResponseMessage)

    if (!parsed) continue

    for (const sub of parsed.submessages) {
      switch (sub.type) {
        case 'text':         console.log('Teks:', sub.text); break
        case 'code':         console.log('Kode:', sub.language, sub.raw); break
        case 'table':        console.log('Tabel:', sub.title, sub.rows); break
        case 'gridImage':    console.log('Grid Image:', sub.gridImageUrl); break
        case 'inlineImage':  console.log('Inline Image:', sub.imageUrl); break
        case 'dynamic':      console.log('Dynamic:', sub.url); break
        case 'map':          console.log('Map:', sub.centerLatitude, sub.centerLongitude); break
        case 'latex':        console.log('LaTeX:', sub.expressions); break
        case 'contentItems': console.log('Content Items:', sub.items); break
      }
    }
  }
})

parseRichMessage menerima null atau undefined dengan aman — return null jika tidak ada rich message.

Standalone Functions

// sendLatex
await sock.sendLatex(jid, m, {
  text: 'Rumus Pythagoras:',
  expressions: [
    { latexExpression: 'a^2 + b^2 = c^2', width: 150, height: 45 }
  ],
  headerText: 'Matematika'
})

// sendTable
await sock.sendTable(jid, 'Data Kota', ['Kota', 'Populasi'], [['Jakarta', '10jt'], ['Surabaya', '3jt']], m)

// sendList
await sock.sendList(jid, 'Daftar Belanja', ['Apel', 'Jeruk', 'Mangga'], m)

// sendCodeBlock
await sock.sendCodeBlock(jid, 'console.log("Hello")', m, { language: 'javascript', title: 'Contoh JS' })

// sendLink
await sock.sendLink(jid, 'Kunjungi dokumentasi', [{ url: 'https://docs.example.com', displayName: 'Docs' }], m)

// sendRichMessage
await sock.sendRichMessage(jid, [
  { messageType: 2, messageText: 'Teks pertama' },
  { messageType: 2, messageText: 'Teks kedua' }
], m)

📃 List Reply

await sock.sendMessage(jid, {
  listReply: {
    title: 'Pilihan Saya',
    description: 'Deskripsi pilihan',
    id: 'row1'  // selectedRowId dari baris yang dipilih
  }
})

🎴 Sticker Pack

await sock.sendMessage(jid, {
  cover: fs.readFileSync('cover.webp'),  // wajib
  name: 'Pack Manis',
  publisher: 'NuiiS4TORU',
  description: 'Sticker pack cantik',
  stickers: [
    {
      data: fs.readFileSync('sticker1.webp'),
      emojis: ['🌸', '💕'],
      accessibilityLabel: 'stiker manis'
    },
    {
      data: { url: 'https://example.com/sticker2.webp' },
      emojis: ['🌷']
    }
    // maksimal 60 sticker
  ]
})

📢 External Ad Reply

await sock.sendMessage(jid, {
  text: 'Cek produk ini',
  externalAdReply: {
    title: 'Judul Iklan',
    body: 'Deskripsi iklan',
    thumbnail: fs.readFileSync('thumbnail.jpg'),  // harus Buffer
    url: 'https://example.com/produk',
    mediaType: 1,
    largeThumbnail: false
  }
})

💳 Request Payment

await sock.sendMessage(jid, {
  text: 'Tolong transfer ya',
  requestPaymentFrom: '[email protected]',
  amount1000: 50000000,      // Rp 50.000 (unit 1/1000)
  currencyCodeIso4217: 'IDR',
  expiryTimestamp: Date.now() + 86400000
})

🧾 Invoice

// Dengan gambar
await sock.sendMessage(jid, {
  image: fs.readFileSync('invoice.jpg'),
  invoiceNote: 'Invoice pembelian\nTotal: Rp 150.000'
})

// Dengan PDF
await sock.sendMessage(jid, {
  document: fs.readFileSync('invoice.pdf'),
  mimetype: 'application/pdf',
  fileName: 'invoice.pdf',
  invoiceNote: 'Terima kasih atas pesananmu'
})

📦 Order

await sock.sendMessage(jid, {
  order: {
    orderId: 'order_001',
    thumbnail: fs.readFileSync('produk.jpg'),  // wajib
    itemCount: 3,
    status: 'PAYMENT_PENDING',
    surface: 'CATALOG',
    message: 'Pesanan kamu sedang diproses',
    orderTitle: 'Pesanan #001',
    sellerJid: '[email protected]',
    token: 'token_unik',
    totalAmount1000: 150000000,
    totalCurrencyCode: 'IDR'
  }
})

💨 Disappearing Messages

// Aktifkan (default 86400 detik = 24 jam)
await sock.sendMessage(groupJid, { disappearingMessagesInChat: true })

// Nonaktifkan
await sock.sendMessage(groupJid, { disappearingMessagesInChat: false })

// Durasi kustom (detik)
await sock.sendMessage(groupJid, { disappearingMessagesInChat: 604800 })  // 7 hari

Hanya berfungsi di grup (@g.us).

⚡ Raw Message

await sock.sendMessage(jid, {
  raw: true,
  extendedTextMessage: {
    text: 'Pesan raw langsung ke proto'
  }
})

Ketika raw: true ada, semua properti lain diteruskan langsung sebagai WAProto message tanpa pemrosesan tambahan.