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

elynn-baileys

v2.3.0

Published

Unified WhatsApp, Telegram Bot API/MTProto userbot, and Discord toolkit with modern rich messages and encrypted deployment-bound auth state.

Downloads

1,279

Readme

✦ ELYNN BAILEYS

WhatsApp Rich Messaging · Telegram · Discord

Rich AI Response dijadikan fitur utama — bukan helper tersembunyi.

npm Node License Rich Response Platforms

Fork Baileys berorientasi protokol dengan bridge Telegraf dan Oceanic.js.

[!IMPORTANT] elynn-baileys memakai protokol WhatsApp Web yang tidak resmi. Sebuah payload dapat berhasil dibuat dan direlay, tetapi render akhirnya tetap dipengaruhi versi WhatsApp, rollout server, jenis akun, platform penerima, dan perubahan protokol. Gunakan akun uji sebelum menerapkan message surface baru pada produksi.


✨ Rilis 2.3.0 — WA Motion Photo · Telegram Bot/Userbot · Discord Components V2

Rilis 2.3.0 menyatukan tiga engine tanpa mencampur autentikasinya:

  • WhatsApp: helper pasangan Motion Photo, experimental Poll Add Option, dan raw Group Message History surfaces;
  • Telegram Bot API: Rich Message, streaming rich draft, Live Photo, media-poll builder, styled buttons, guest/managed-bot methods;
  • Telegram userbot: adapter MTProto berbasis optional peer telegram (GramJS), login nomor/OTP/2FA, multi-session, dan penyimpanan session AES-256-GCM;
  • Discord: Components V2 builders, poll builder, dan perbaikan boundary CommonJS/ESM pada konektor Discord;
  • validasi: syntax, smoke, TypeScript, lisensi, dependency audit, package tarball, ESM/CJS consumer.

[!WARNING] Motion Photo, Poll Add Option, dan Group Message History adalah surface protokol WhatsApp yang render/relay akhirnya tetap bergantung pada server dan versi client. Group history pada 2.3.0 menerima bundle yang sudah diunggah; library belum mengekstrak bundle terenkripsi dari client resmi.

Telegram Bot vs userbot

BotFather token  → Telegram Bot API
api_id/api_hash + nomor/OTP/2FA → Telegram MTProto user client

Userbot tidak memakai token BotFather. Buat api_id dan api_hash milik aplikasi sendiri dan patuhi Terms of Service Telegram; session userbot setara dengan akses akun.


✨ Rilis 2.2.0 — Sender-Hidden Delivery + Session Guard v2

Rilis ini mempertahankan Rich AI Response sebagai fitur utama dan menambahkan dua kemampuan yang tidak tersedia sebagai API publik standar Baileys:

  • sender-hidden direct message: pesan tetap dikirim ke pengguna, tetapi sinkronisasi ke perangkat tertaut milik akun bot dapat dihilangkan;
  • Session Guard v2: auth state terenkripsi, terikat deployment/machine/lokasi, memiliki clone lock, dan dapat memakai KMS serta remote single-active lease.

Perubahan utama 2.2.0:

  • selfSync: 'omit', suppressSelfSync, dan hideFromSender untuk chat pribadi;
  • helper sendSenderHiddenMessage();
  • sender-device filtering dilakukan pada daftar recipient Signal, bukan dengan memanfaatkan bug LID;
  • grup, status, newsletter, dan self-chat ditolak dalam mode sender-hidden agar tidak memberi jaminan palsu;
  • useGuardedMultiFileAuthState() sebagai penyimpanan auth terenkripsi;
  • AES-256-GCM, scrypt, HKDF-SHA-256, authenticated context, dan manifest HMAC;
  • binding ke session, deployment, machine, lokasi folder, dan nama file;
  • local single-instance lease yang terikat langsung ke folder session serta optional remote lease claim/renew/release;
  • strict key policy minimal 32 byte dan optional keyProvider untuk KMS, Vault, HSM, TPM agent, atau workload identity;
  • optional remote monotonic write-counter anchor untuk mendeteksi rollback/snapshot session lama;
  • migrasi otomatis auth state plaintext ke *.eg2;
  • penghapusan ketergantungan keamanan pada public IP;
  • dokumentasi threat model yang menyatakan root/process-memory compromise tidak dapat dihentikan oleh library lokal.

Rich Response rebuild dari 2.1.0 tetap lengkap: object API, array API, raw submessages, code, citation, table, image/grid, dynamic media, map, LaTeX, content items, custom bot metadata, dan incoming decoder.


Platforms

| Platform | Basis | Cakupan | |---|---|---| | WhatsApp | Baileys-compatible Web protocol | Core messages, rich response, interactive/native-flow, channel, protocol messages | | Telegram | Bot API + optional GramJS MTProto | BotFather bot, Rich Message/Live Photo, dan user client nomor/OTP/2FA | | Discord | Oceanic.js line | Gateway, REST, interactions, sharding, polls, Components V2; voice opsional |

[!NOTE] Modul Discord menggunakan Oceanic.js, bukan discord.js. Paket @discordjs/voice hanya optional dependency untuk voice.


Installation

npm install elynn-baileys

Persyaratan:

Node.js >= 20.18.1

Optional native/media features:

npm install sharp better-sqlite3 canvas @discordjs/voice telegram

WhatsApp quick start

import makeWASocket, {
  DisconnectReason,
  useMultiFileAuthState
} from 'elynn-baileys'

async function start() {
  const { state, saveCreds } = await useMultiFileAuthState('./session')
  const sock = makeWASocket({ auth: state })

  sock.ev.on('creds.update', saveCreds)

  sock.ev.on('connection.update', ({ connection, lastDisconnect, qr }) => {
    if (qr) console.log('QR tersedia')

    if (connection === 'close') {
      const status = lastDisconnect?.error?.output?.statusCode
      if (status !== DisconnectReason.loggedOut) start()
    }
  })

  return sock
}

const sock = await start()

WhatsApp Motion Photo dan history surfaces

const sent = await sock.sendMessage(jid, {
  motionPhoto: {
    image: imageBuffer,
    video: videoBuffer,
    caption: 'Motion Photo',
    presentationOffsetMs: 350
  }
})

await sock.sendMessage(jid, {
  pollAddOption: {
    key: pollMessageKey,
    optionName: 'Pilihan baru'
  }
})

messageHistoryNotice dan messageHistoryBundle tersedia sebagai raw proto surface. Bundle harus sudah memiliki directPath, mediaKey, fileSha256, dan fileEncSha256 yang valid.

Telegram Bot API modern

import {
  ElynnTelegraf,
  richMarkdown,
  streamRichMessage,
  telegramStyledButton
} from 'elynn-baileys/telegram'

const bot = new ElynnTelegraf(process.env.TELEGRAM_BOT_TOKEN)

await bot.telegram.sendRichMessage(chatId, richMarkdown(`# Elynn\n\n**Online**`))

await streamRichMessage(bot.telegram, chatId, ['Draft 1', 'Draft 2'], {
  draftId: 7
})

const button = telegramStyledButton('Run', {
  callbackData: 'run',
  style: 'success'
})

Telegram MTProto userbot

import {
  createTelegramUserbot,
  EncryptedTelegramSessionStore
} from 'elynn-baileys/telegram/userbot'

const store = new EncryptedTelegramSessionStore(
  './sessions/account-1.eg1',
  process.env.TELEGRAM_SESSION_SECRET
)

const user = await createTelegramUserbot({
  apiId: Number(process.env.TELEGRAM_API_ID),
  apiHash: process.env.TELEGRAM_API_HASH,
  phoneNumber: process.env.TELEGRAM_PHONE,
  sessionStore: store
})

await user.login({
  phoneCode: async () => readOtpFromOperator(),
  password: async () => readTwoFactorPassword()
})

Discord Components V2

import {
  discordContainer,
  discordTextDisplay,
  discordActionRow,
  discordButton,
  buildDiscordComponentsV2Message
} from 'elynn-baileys/discord'

const payload = buildDiscordComponentsV2Message([
  discordContainer([
    discordTextDisplay('## Elynn Control'),
    discordActionRow([
      discordButton('Run', { customId: 'run', style: 3 })
    ])
  ], { accentColor: '#00ffaa' })
])

Sender-hidden direct message

Mode ini cocok untuk menu bot yang harus terlihat oleh penerima, tetapi tidak ingin muncul sebagai outgoing message pada perangkat WhatsApp yang tertaut ke akun bot.

await sock.sendMessage(
  '[email protected]',
  {
    text: 'Pilih menu:\n1. Profil\n2. Saldo\n3. Bantuan'
  },
  {
    selfSync: 'omit',
    emitOwnEvent: false
  }
)

Atau gunakan helper:

import { sendSenderHiddenMessage } from 'elynn-baileys'

await sendSenderHiddenMessage(
  sock,
  '[email protected]',
  { text: 'Menu utama' }
)

Alias yang diterima:

{ suppressSelfSync: true }
{ hideFromSender: true }
{ selfSync: false }

[!IMPORTANT] Fitur ini hanya diterapkan untuk chat pribadi PN/LID. Paket menolak pemakaian pada grup, status, newsletter, dan self-chat. Ia tidak menyembunyikan pesan dari penerima atau server WhatsApp; ia hanya menghilangkan sender-owned linked devices dari daftar enkripsi direct-message dan mematikan local own-event echo. Perubahan fan-out server WhatsApp tetap dapat memengaruhi hasil, sehingga uji Android/iOS/Web diperlukan sebelum produksi.


WhatsApp message matrix

Core dan protocol surface

| Message | Input utama | Status paket | |---|---|---| | Text, mention, link preview | text, mentions, linkPreview | Core | | Image, video, audio, document, sticker | media field | Core; container/MIME harus valid | | Contact, location | contacts, location | Core | | Reaction, edit, delete, forward | field terkait | Core dengan batas server | | Poll, event, album, product | field terkait | Supported; context-dependent | | View-once media | viewOnce | Supported; client-dependent | | Native-flow | nativeFlow | Protocol-forward; rollout-dependent | | Carousel interactive | cards | Protocol-forward; 2–10 cards | | Legacy button/list/template | explicit legacy flags | Deprecated; tidak disarankan | | Payment, order, invoice | field terkait | Regional/account-dependent | | Rich AI Response | richResponse, submessages | First-class package support; render client-dependent | | Sender-hidden direct message | option selfSync: 'omit' | Private chat only; live-device dependent |

Rich response submessages

| Tipe | Input helper | Proto | |---|---|---| | Markdown text | { text } | AI_RICH_RESPONSE_TEXT | | Syntax-highlighted code | { code, language } | AI_RICH_RESPONSE_CODE | | Citation / inline entities | { links } | Text + unified inline entities | | Table | { table, title } | AI_RICH_RESPONSE_TABLE | | Inline image | { inlineImage } | AI_RICH_RESPONSE_INLINE_IMAGE | | Grid image | { gridImages } | AI_RICH_RESPONSE_GRID_IMAGE | | Dynamic image/GIF | { dynamic } / { inlineVideo } | AI_RICH_RESPONSE_DYNAMIC | | Map | { map } | AI_RICH_RESPONSE_MAP | | LaTeX | { latex } | AI_RICH_RESPONSE_LATEX | | Reel/content carousel | { items } | AI_RICH_RESPONSE_CONTENT_ITEMS | | Raw proto | { submessages } | Direct submessage passthrough |


✨ Rich AI Response

Rich response menggunakan struktur:

botForwardedMessage
└── richResponseMessage
    ├── submessages[]
    ├── unifiedResponse.data (JSON bytes)
    └── contextInfo.forwardedAiBotMessageInfo

unifiedResponse.data dibuat otomatis agar primitive seperti code, table, citation, inline image, LaTeX, dan content items mempunyai model render yang sesuai.

1. Object API — paling ringkas

await sock.sendMessage(jid, {
  richResponse: {
    text: 'Berikut hasil proses:',
    code: `const result = await buildProject()
console.log(result)`,
    language: 'javascript',
    botJid: '259786046210223@bot',
    disclaimerText: 'Generated by Elynn'
  }
})

Field botJid tidak lagi hard-coded. Bila tidak diberikan, package memakai default compatibility bot JID.

2. Array API — urutan submessage penuh

await sock.sendMessage(jid, {
  disclaimerText: 'Rich response example',
  richResponse: [
    { text: '# Build Report' },
    {
      code: 'const status = "success"',
      language: 'javascript'
    },
    {
      links: [
        {
          text: 'Dokumentasi',
          title: 'Elynn Docs',
          url: 'https://example.com/docs',
          sources: [
            {
              displayName: 'Official docs',
              subtitle: 'Reference',
              url: 'https://example.com/docs'
            }
          ]
        }
      ]
    },
    {
      title: 'Runtime comparison',
      table: [
        ['Runtime', 'Status', 'Score'],
        ['Node.js', 'Stable', '5/5'],
        ['Bun', 'Fast', '5/5']
      ]
    }
  ]
})

Code string ditokenisasi otomatis. Untuk token manual:

import { tokenizeCode } from 'elynn-baileys'

const blocks = tokenizeCode(
  'const value = 42\nconsole.log(value)',
  'javascript'
)

await sock.sendMessage(jid, {
  richResponse: [
    { text: 'Token manual:' },
    { code: blocks, language: 'javascript' }
  ]
})

3. Image, grid, GIF/dynamic, map, LaTeX, dan content items

await sock.sendMessage(jid, {
  richResponse: [
    {
      inlineImage: 'https://example.com/preview.jpg',
      imageText: 'Single preview',
      tapLinkUrl: 'https://example.com'
    },
    {
      gridImages: [
        'https://example.com/one.jpg',
        'https://example.com/two.jpg'
      ]
    },
    {
      dynamic: {
        type: 'gif',
        url: 'https://example.com/demo.gif',
        loopCount: 0,
        version: 1
      }
    },
    {
      map: {
        centerLatitude: -6.2,
        centerLongitude: 106.8,
        latitudeDelta: 0.05,
        longitudeDelta: 0.05,
        showInfoList: true,
        annotations: [
          {
            annotationNumber: 1,
            latitude: -6.2,
            longitude: 106.8,
            title: 'Jakarta',
            body: 'Example location'
          }
        ]
      }
    },
    {
      text: 'Einstein equation',
      latex: [
        { latexExpression: 'E = mc^2' }
      ]
    },
    {
      items: [
        {
          title: 'Demo reel',
          profileIconUrl: 'https://example.com/avatar.jpg',
          thumbnailUrl: 'https://example.com/thumb.jpg',
          videoUrl: 'https://example.com/video.mp4'
        }
      ]
    }
  ]
})

Compatibility alias inlineVideo sekarang dibentuk sebagai dynamic GIF/media metadata; versi lama hanya mengirim literal text INLINE_VIDEO.

4. Raw submessages[]

Helper tidak wajib digunakan. Raw proto tetap tersedia:

import { proto } from 'elynn-baileys'

await sock.sendMessage(jid, {
  submessages: [
    {
      messageType:
        proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TEXT,
      messageText: 'Raw proto submessage'
    },
    {
      messageType:
        proto.AIRichResponseSubMessageType.AI_RICH_RESPONSE_TABLE,
      tableMetadata: {
        title: 'Raw table',
        rows: [
          { isHeading: true, items: ['Key', 'Value'] },
          { isHeading: false, items: ['status', 'ok'] }
        ]
      }
    }
  ]
})

Raw complete AIRichResponseMessage juga diterima:

await sock.sendMessage(jid, {
  richResponseMessage: {
    messageType: 1,
    submessages: [
      { messageType: 2, messageText: 'Direct AIRichResponseMessage' }
    ]
  },
  responseId: 'custom-response-id'
})

5. Custom metadata

await sock.sendMessage(jid, {
  richResponse: [{ text: 'Custom metadata' }],
  botJid: '259786046210223@bot',
  responseId: crypto.randomUUID(),
  disclaimerText: 'AI generated response',
  forwardingScore: 1,
  forwardOrigin: 4,
  includeVerificationMetadata: true,
  botMetadata: {
    // additional compatible bot metadata
  }
})

Untuk tes lokal yang tidak memerlukan synthetic verification metadata:

await sock.sendMessage(jid, {
  richResponse: [{ text: 'No generated proof metadata' }],
  includeVerificationMetadata: false
})

6. Membaca rich response masuk

import {
  extractRichResponseMessage,
  decodeUnifiedResponse
} from 'elynn-baileys'

sock.ev.on('messages.upsert', ({ messages }) => {
  for (const message of messages) {
    const rich = extractRichResponseMessage(message)
    if (!rich) continue

    const unified = decodeUnifiedResponse(message)

    console.log('Submessages:', rich.submessages)
    console.log('Unified:', unified)
  }
})

Extractor membuka wrapper umum seperti botForwardedMessage, view-once, ephemeral, document-with-caption, dan group-status wrapper.

7. API exports

import {
  RICH_RESPONSE_DEFAULT_BOT_JID,
  tokenizeCode,
  toUnified,
  normalizeRichResponseContent,
  prepareRichResponseMessage,
  wrapToBotForwardedMessage,
  extractRichResponseMessage,
  decodeUnifiedResponse
} from 'elynn-baileys'

[!WARNING] Package memvalidasi struktur dan encoding secara offline, tetapi tidak dapat menjamin semua primitive dirender identik di Android, iOS, Web, grup, channel, atau setiap versi WhatsApp. Jalankan device matrix test menggunakan akun uji.


Text and media

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

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

await sock.sendMessage(jid, {
  audio: voiceOggBuffer,
  mimetype: 'audio/ogg; codecs=opus',
  ptt: true
})

ptt: true tidak mengonversi MP3 menjadi Opus. File voice note harus benar-benar berformat OGG/Opus.


Native-flow

await sock.sendMessage(jid, {
  text: 'Pilih tindakan',
  footer: 'Elynn',
  nativeFlow: {
    messageVersion: 1,
    buttons: [
      { id: 'confirm', text: 'Konfirmasi' },
      { copy: 'ELYNN2026', text: 'Salin kode' },
      { url: 'https://example.com', text: 'Buka situs' },
      { call: '+6281234567890', text: 'Telepon' },
      {
        text: 'Buka daftar',
        sections: [
          {
            title: 'Menu',
            rows: [
              { id: 'menu_a', title: 'Menu A', description: 'Pilihan pertama' }
            ]
          }
        ]
      }
    ]
  }
})

Input legacy sederhana dimigrasikan ke native-flow secara default:

await sock.sendMessage(jid, {
  text: 'Converted automatically',
  buttons: [
    { buttonId: 'ok', buttonText: { displayText: 'OK' } }
  ]
})

Untuk memaksa raw legacy message—tidak direkomendasikan:

await sock.sendMessage(jid, {
  text: 'Legacy',
  legacyInteractive: true,
  buttons: [
    { buttonId: 'ok', buttonText: { displayText: 'OK' }, type: 1 }
  ]
})

interactiveAsTemplate ditolak. Approved template resmi adalah surface WhatsApp Business Platform, bukan wrapper raw legacy personal Baileys.


Carousel interactive

await sock.sendMessage(jid, {
  text: 'Produk pilihan',
  cards: [
    {
      image: { url: 'https://example.com/a.jpg' },
      caption: 'Produk A',
      nativeFlow: {
        buttons: [{ id: 'buy_a', text: 'Beli A' }]
      }
    },
    {
      image: { url: 'https://example.com/b.jpg' },
      caption: 'Produk B',
      nativeFlow: {
        buttons: [{ id: 'buy_b', text: 'Beli B' }]
      }
    }
  ]
})

Carousel wajib berisi 2–10 cards. Card tanpa button tidak lagi menyebabkan crash.


Parse interactive response

function readSelection(message) {
  const content = message.message ?? {}
  const native = content.interactiveResponseMessage?.nativeFlowResponseMessage

  if (native?.paramsJson) {
    try {
      return {
        type: 'native_flow',
        name: native.name,
        data: JSON.parse(native.paramsJson)
      }
    } catch {
      return {
        type: 'native_flow',
        name: native.name,
        data: native.paramsJson
      }
    }
  }

  return content.buttonsResponseMessage?.selectedButtonId
    ?? content.listResponseMessage?.singleSelectReply?.selectedRowId
    ?? null
}

Message test suite

const report = await testMessage(sock, jid, {
  quoted: message,
  includeInteractive: true,
  media: {
    image: imageBuffer,
    voice: oggOpusBuffer
  }
})

console.table(report.results)

testMessage() menghasilkan status per skenario dan tidak mengklaim seluruh raw proto pasti dirender.


Telegram

import {
  ElynnTelegraf,
  TelegramMarkup
} from 'elynn-baileys'

const bot = new ElynnTelegraf(process.env.TELEGRAM_BOT_TOKEN)

bot.start(ctx => ctx.reply('Bot aktif'))

bot.command('menu', ctx => ctx.reply(
  'Pilih:',
  TelegramMarkup.inlineKeyboard([
    TelegramMarkup.button.callback('OK', 'ok')
  ])
))

await bot.launch()

Health check tanpa polling:

import { connectTelegram } from 'elynn-baileys'

const me = await connectTelegram(token, {
  timeoutMs: 10_000
})

Method Bot API baru tetap dapat dipanggil melalui:

await bot.telegram.callApi(method, payload)

Discord

import { Client } from 'elynn-baileys/discord'

const client = new Client({
  auth: `Bot ${process.env.DISCORD_BOT_TOKEN}`,
  gateway: { intents: 0 }
})

client.on('ready', () => {
  console.log(`Ready as ${client.user.username}`)
})

await client.connect()

Health check:

import { connectDiscord } from 'elynn-baileys'

const me = await connectDiscord(token, {
  timeoutMs: 15_000
})

Voice membutuhkan @discordjs/voice; text-sticker membutuhkan canvas.


Banner

Package tidak mencetak banner ketika di-import.

import { printBootBanner } from 'elynn-baileys'

printBootBanner()

Atau set:

ELYNN_BANNER=1

Session Guard v2

Gunakan useGuardedMultiFileAuthState() untuk mengganti penyimpanan auth plaintext:

import makeWASocket, {
  useGuardedMultiFileAuthState
} from 'elynn-baileys'

const auth = await useGuardedMultiFileAuthState('./session', {
  securityLevel: 'strict',
  deploymentId: 'wa-bot-production-01',
  secret: process.env.ELYNN_SESSION_MASTER_KEY,
  requireRemoteLease: true,
  requireRemoteAnchor: true,
  leaseProvider: sessionLeaseProvider,
  onBlock(error) {
    console.error(error.code, error.message)
  }
})

const sock = makeWASocket({ auth: auth.state })
sock.ev.on('creds.update', auth.saveCreds)

process.once('SIGTERM', async () => {
  await auth.close()
  process.exit(0)
})

Auth folder berubah menjadi ciphertext:

session/
├── .elynn-guard-v2.json
├── .elynn-guard-v2.lease       # hanya saat instance aktif
├── creds.json.eg2
├── app-state-sync-key-....json.eg2
└── sender-key-....json.eg2

Kontrol yang tersedia:

  • AES-256-GCM untuk confidentiality dan integrity;
  • scrypt + HKDF-SHA-256 untuk key derivation;
  • authenticated binding ke session, deployment, machine, lokasi folder, dan filename;
  • manifest HMAC serta atomic write;
  • permission folder 0700 dan file 0600;
  • local lease autentik di folder session untuk memblokir dua proses dan mencegah bypass lewat perubahan guardRoot;
  • remote lease untuk mendeteksi clone lintas server/container;
  • remote monotonic anchor berbasis writeCounter untuk menolak snapshot/rollback lama;
  • strict mode mewajibkan key material minimal 32 byte;
  • keyProvider untuk KMS, Vault, HSM, TPM agent, atau workload identity;
  • migrasi plaintext otomatis, lalu file plaintext dihapus;
  • tidak memakai IP publik sebagai identitas keamanan.

Threat model

| Kondisi | Hasil | |---|---| | Admin panel hanya mencuri folder proyek/session | Tidak dapat membuka ciphertext tanpa key eksternal/local key di luar proyek | | Copy dipakai pada IP yang sama | Tetap diblokir oleh key, context binding, dan lease; IP tidak relevan | | Copy dipindahkan ke folder proyek lain | Ditolak oleh location binding | | Dua proses memakai session yang sama | Local lease mendeteksi clone | | Clone lintas host membawa key yang sama | Gunakan remote lease untuk single-active enforcement | | Ciphertext atau manifest diedit/rusak/hilang | Fail closed; manifest tidak dibuat ulang di atas ciphertext lama | | Snapshot lama direstorasi | Dapat ditolak oleh requireRemoteAnchor + monotonic writeCounter | | Root/admin membaca memory atau mengganti source | Tidak dapat dijamin oleh library lokal |

[!WARNING] Baileys memerlukan key WhatsApp/Signal dalam memory untuk bekerja. Pihak yang menguasai root, debugger, process memory, runtime injection, atau source deployment dapat melewati kontrol lokal. Untuk ancaman tersebut gunakan isolation, dedicated service account, KMS/Vault berbasis workload identity, dan remote lease authority di luar trust domain admin panel.

Dokumentasi lengkap: bagian Session Guard.


Package exports

import makeWASocket from 'elynn-baileys'
import { Telegraf, ElynnTelegraf } from 'elynn-baileys/telegram'
import { Client } from 'elynn-baileys/discord'
import { proto } from 'elynn-baileys/WAProto'

| Subpath | Format | |---|---| | elynn-baileys | ESM root | | elynn-baileys/telegram | ESM + CommonJS bridge | | elynn-baileys/discord | ESM + CommonJS bridge | | elynn-baileys/WAProto | Generated protobuf | | elynn-baileys/elynn | Elynn plugins | | elynn-baileys/plugins | Elynn plugins alias |


Validation

npm test
npm pack --dry-run

Pemeriksaan bawaan mencakup:

  • syntax seluruh .js, .mjs, dan .cjs;
  • root import dan subpath Telegram/Discord;
  • text, native-flow, legacy migration, carousel;
  • Rich Response object API;
  • Rich Response array API;
  • code tokenizer dan unified code primitive;
  • citation, table, inline image, grid image;
  • dynamic media, map, LaTeX, content items;
  • raw submessages;
  • custom bot JID;
  • verification metadata opt-out;
  • incoming rich extraction dan unified decoding;
  • TypeScript consumer compilation;
  • mandatory third-party license notices;
  • sender-device partition and sender-hidden helper;
  • encrypted auth roundtrip, clone lock, location binding, ciphertext/manifest tamper detection;
  • strict 32-byte key enforcement, authenticated local-lease tamper rejection, strong remote-lease validation, and rollback-anchor rejection;
  • clean-package ESM, CommonJS bridge, and strict downstream TypeScript compilation.

Local validation vs live validation

| Pemeriksaan | Bisa dilakukan offline | Memerlukan akun/device | |---|---:|---:| | Syntax, import, TypeScript | ✅ | — | | Protobuf structure and encoding | ✅ | — | | unifiedResponse JSON | ✅ | — | | Message generation | ✅ | — | | Server acceptance | — | ✅ | | Android/iOS/Web render | — | ✅ | | Account/region-specific payment | — | ✅ |


License and attribution

Package utama menggunakan MIT. Kode turunan mempertahankan notice dari:

  • WhiskeySockets/Baileys;
  • Telegraf contributors;
  • Donovan Daniels/Oceanic.js;
  • Lia untuk implementasi rich response helper yang menjadi basis pengembangan.

Lihat:

Discord, Telegram, WhatsApp, Meta, Oceanic.js, Telegraf, dan Baileys merupakan nama atau merek pihak masing-masing. Package ini tidak berafiliasi resmi dengan platform tersebut.


Dokumentasi gabungan

Seluruh dokumentasi proyek disatukan di file ini agar paket hanya memiliki satu file Markdown.

Daftar bagian


Changelog

2.3.0 — 2026-07-11

WhatsApp
  • Added generateWAMotionPhotoMessages() and sendMessage({ motionPhoto }) parent/child relay support.
  • Added experimental high-level pollAddOption generation with POLL_ADD_OPTION association metadata.
  • Added raw messageHistoryNotice and uploaded messageHistoryBundle generation surfaces.
  • Added regression tests for Motion Photo pairing, history metadata, and poll-option payloads.
Telegram
  • Added Bot API rich-message builders and streamRichMessage() draft-to-final flow.
  • Corrected sendLivePhoto() to require both motion video and static photo.
  • Corrected sendRichMessageDraft() to include a non-zero draft_id.
  • Added styled-button and media-poll builders.
  • Added optional GramJS MTProto user client with phone/OTP/2FA login, raw API access, dialogs/history helpers, and encrypted AES-256-GCM session storage.
  • Added ./telegram/userbot package export.
Discord
  • Fixed the invalid ESM syntax in the CommonJS lib/Discord/connect-dc.js boundary.
  • Added Components V2 builders for text display, thumbnail, media gallery, file, separator, section, container, action rows, and buttons.
  • Added Components V2 message flag handling and native Discord poll builder.
  • Added ./discord/components-v2 ESM/CommonJS package export.
Validation and packaging
  • Updated package version, declarations, root exports, optional peer metadata, README, and package lock.
  • Added smoke and strict TypeScript coverage for all new public helpers.
  • Passed syntax, smoke, TypeScript, license, and dependency-vulnerability checks.

2.2.0 — 2026-07-10

Sender-hidden direct messages
  • Added selfSync: 'omit' for direct PN/LID chats.
  • Added aliases suppressSelfSync, hideFromSender, and selfSync: false.
  • Added sendSenderHiddenMessage() convenience helper.
  • Excluded sender-owned linked devices from direct-message encryption recipients while preserving recipient devices.
  • Disabled the local own-message echo by default in omit mode.
  • Rejected unsupported group, status, newsletter, and self-chat use instead of silently claiming success.
  • Added structured sender-visibility TypeScript declarations and smoke tests.
Session Guard v2
  • Replaced public-IP binding with encrypted deployment-bound auth state.
  • Added useGuardedMultiFileAuthState().
  • Added AES-256-GCM encryption, scrypt, HKDF-SHA-256, manifest HMAC, and authenticated context.
  • Bound encrypted files to session ID, deployment, machine identity, folder location, and logical filename.
  • Added permission hardening, atomic writes, and plaintext auth migration.
  • Added an authenticated local single-instance lease with TTL and heartbeat.
  • Added optional remote lease provider with claim, renew, and release actions.
  • Added optional external keyProvider integration for KMS, Vault, HSM, TPM agents, and workload identity.
  • Added clone, copied-folder, tamper, wrong-key, lease-loss, malformed/missing manifest, forged lease, and short-key failure modes.
  • Bound the local lease to the session folder so overriding guardRoot cannot create a second independent local lock.
  • Added strong remote lease response validation and optional monotonic writeCounter rollback anchoring.
  • Added key zeroization on derivation/acquisition failures.
  • Added explicit threat-model documentation: malicious root/process-memory access remains outside local-library protection.
Documentation and validation
  • Added bagian Session Guard di README.md.
  • Added bagian Security and Stealth Audit di README.md.
  • Updated README, package metadata, banner version, exports, and TypeScript declarations.
  • Added Telegraf as a compatibility alias for ElynnTelegraf in root, ESM subpath, and CommonJS subpath exports.
  • Added encrypted auth roundtrip, clone lock, location binding, rollback anchor, lease/manifest tamper, strict key-strength, and sender-device partition tests.
  • Validated the packed tarball from a clean ESM/CommonJS/TypeScript consumer project.

2.1.0 — 2026-07-10

Rich response rebuild
  • Promoted WhatsApp Rich AI Response to a first-class public feature.
  • Added object-style richResponse: { ... }, array-style richResponse: [...], and raw submessages APIs.
  • Added support for all bundled AIRichResponseSubMessageType surfaces: text, code, inline image, grid image, table, dynamic media, map, LaTeX, and content items.
  • Replaced the INLINE_VIDEO text placeholder with AIRichResponseDynamicMetadata.
  • Added non-empty unified response sections for inline image, content items, and LaTeX, plus raw-safe sections for grid, dynamic, and map payloads.
  • Added citation generation with globally unique inline-entity keys.
  • Added configurable botJid, response ID, disclaimer, forwarded context, verification metadata, and bot metadata.
  • Added extractRichResponseMessage() and decodeUnifiedResponse() for incoming messages.
  • Added structured TypeScript declarations for rich-response inputs and outputs.
  • Added runtime smoke tests for every rich-response family and raw proto passthrough.
  • Preserved Lia Wynn / itsliaaa rich-response attribution in source and third-party notices.
  • Rebuilt the README around rich messaging with complete examples and an offline-vs-live validation matrix.
Corrected from 2.0.0
  • Removed the inaccurate grouping of rich response with generic payment/invoice experimental helpers.
  • Clarified that package-level support is complete while final WhatsApp rendering remains client/server dependent.

2.0.0 — 2026-07-10

Fixed
  • Removed automatic newsletter annotations from image and video payloads.
  • Restored the standard Baileys message ID generator.
  • Corrected fetchLatestBaileysVersion() to use the official upstream source.
  • Added native-flow validation, messageVersion, compatibility aliases, and carousel limits.
  • Fixed carousel cards without buttons.
  • Rejected the obsolete interactiveAsTemplate wrapper with migration guidance.
  • Rebuilt testMessage() as a per-case report and corrected forward/audio behavior.
  • Fixed Discord connection health-check listener ordering.
  • Removed import-time banner side effects.
  • Removed injected package branding from products, orders, sticker packs, external previews, and rich-response citations.
  • Stopped writing raw pairing codes to application logs.
  • Fixed TypeScript declarations, default/named exports, and CommonJS/ESM boundaries for Telegram and Discord.
  • Fixed Discord EventEmitter declaration compatibility for strict downstream TypeScript consumers.
  • Added the public NativeFlowMessage type alias.
  • Added full Telegraf and Oceanic.js attribution.
Changed
  • Minimum Node.js version is 20.18.1.
  • Legacy buttons, lists, and hydrated template inputs migrate to native-flow by default.
  • Added explicit Telegram and Discord package subpath exports.
  • Updated compatible runtime dependencies while retaining versions required by bundled APIs.
  • Moved native-only optional features such as canvas out of the mandatory install path.
  • Removed unused direct dependencies and shipped @types/ws for Discord declarations.

Elynn Plugins

Plugin aktif:

| Key | Path | Desc | Output | |-----------|------------------------|------------------------|----------| | brat | maker/brat.js | Brat meme text-to-img | sticker | | bratvid | maker/bratvid.js | Brat meme text-to-vid | sticker |

Usage di bot

import { plugins } from 'elynn-baileys'

// Brat → kirim sebagai stiker
const res = await plugins.brat({ text: 'teks lo' })
if (res.ok) {
  await sock.sendMessage(jid, {
    sticker: { url: res.result }
  })
}

Semua plugin brat return { ok, type: 'sticker', result: <url> }.


Rich Response Research

Date checked: 2026-07-10

Why this feature remains in Elynn Baileys

WhatsApp Rich AI Response is not an obsolete template surface. It is represented by current WAProto messages and is actively implemented or documented in several Baileys lines.

Sources compared

WhiskeySockets/Baileys

Current v7 release direction explicitly lists richResponseMessage among the features planned for broader Baileys support.

  • Repository: https://github.com/WhiskeySockets/Baileys
  • Release notes: https://github.com/WhiskeySockets/Baileys/releases
  • Proto docs: https://baileys.wiki/docs/api/namespaces/proto/

The proto namespace exposes AIRichResponseMessage, unified response, submessages, code, table, inline image, grid image, dynamic metadata, map, LaTeX, and content items.

itsliaaa/baileys

This fork provides the original helper line used by this package:

  • ordered richResponse[] entries;
  • direct raw submessages[] support;
  • code tokenizer;
  • code, inline entities, and table examples;
  • bot-forwarded wrapper and unified JSON bytes.

Repository: https://github.com/itsliaaa/baileys

The original Lia Wynn source credit remains in lib/Utils/rich-message-utils.js.

yemo-dev/baileys

This fork documents the compact object API:

{
  richResponse: {
    text,
    code,
    language,
    botJid
  }
}

Repository: https://github.com/yemo-dev/baileys

Elynn 2.1.0 accepts this form in addition to the array API.

realvare/based

This fork documents a raw message-shaped form:

{
  ai: true,
  richResponse: {
    messageType,
    submessages,
    contextInfo
  }
}

Repository: https://github.com/realvare/based

Elynn 2.1.0 accepts this form and preserves rich contextInfo.

Elynn compatibility matrix

| Input style | Supported | |---|---:| | richResponse: { text, code } | Yes | | richResponse: [{ text }, { code }] | Yes | | richResponse: { messageType, submessages, contextInfo } | Yes | | top-level submessages | Yes | | complete richResponseMessage | Yes | | custom botJid | Yes | | custom/disabled verification metadata | Yes | | incoming wrapper extraction | Yes | | unified JSON decoding | Yes |

Submessage coverage

| Proto type | Elynn helper | |---|---| | AI_RICH_RESPONSE_GRID_IMAGE | gridImage / gridImages | | AI_RICH_RESPONSE_TEXT | text, links | | AI_RICH_RESPONSE_INLINE_IMAGE | inlineImage | | AI_RICH_RESPONSE_TABLE | table | | AI_RICH_RESPONSE_CODE | code | | AI_RICH_RESPONSE_DYNAMIC | dynamic / inlineVideo | | AI_RICH_RESPONSE_MAP | map / mapMetadata | | AI_RICH_RESPONSE_LATEX | latex | | AI_RICH_RESPONSE_CONTENT_ITEMS | items / contentItems |

Validation boundary

Offline tests prove syntax, types, protobuf encoding, decoding, wrapper structure, and unified JSON. Live rendering remains dependent on WhatsApp server and client versions. That boundary is documented rather than used to remove the feature.


Session Guard Security

Session Guard v2 protects Baileys authentication state from ordinary project-folder theft, accidental copying, tampering, and duplicate execution. It deliberately does not claim that a JavaScript process can defeat a malicious host administrator with root access.

Security model

| Threat | Protection | Notes | |---|---:|---| | Session folder copied from a hosting panel | Strong when the key is outside the project or supplied by KMS | Auth files are AES-256-GCM ciphertext. | | Copy reused on the same public IP | Yes | Public IP is not used as identity. | | Copy moved to another project path | Yes by default | Folder location is authenticated. | | Copy run concurrently on the same host | Yes | HMAC-protected local lease blocks a second active instance. | | Copy run on another host with the same secret | Machine binding blocks it by default | Use a remote lease for stronger clone detection. | | Ciphertext or manifest edited, malformed, or removed | Yes | AES-GCM/HMAC fail closed; an invalid manifest is never silently replaced. | | Old valid snapshot restored | Yes with remote anchor | requireRemoteAnchor binds a monotonic writeCounter to an external authority. | | Plaintext legacy auth state | Migrated automatically by default | Plaintext file is removed after encrypted write succeeds. | | Malicious root/admin reads process memory | No | Baileys must use decrypted Signal keys in memory. | | Malicious root/admin modifies library or runtime | No | Host control defeats local enforcement. Use isolated infrastructure and external policy. |

Recommended strict configuration

import makeWASocket, {
  useGuardedMultiFileAuthState
} from 'elynn-baileys'

const auth = await useGuardedMultiFileAuthState('./session', {
  securityLevel: 'strict',
  deploymentId: 'bot-production-01',
  secret: process.env.ELYNN_SESSION_MASTER_KEY,
  requireRemoteLease: true,
  requireRemoteAnchor: true,
  leaseProvider: sessionLeaseProvider,
  onBlock(error) {
    console.error('[SESSION BLOCKED]', error.code, error.message)
  }
})

const sock = makeWASocket({ auth: auth.state })
sock.ev.on('creds.update', auth.saveCreds)

process.once('SIGTERM', async () => {
  await auth.close()
  process.exit(0)
})

Use at least one of these key sources:

  • keyProvider: preferred for KMS, Vault, HSM, TPM-backed agents, or workload identity.
  • masterKey: at least 32 bytes, supplied outside the project directory.
  • secret or ELYNN_SESSION_MASTER_KEY: at least 32 bytes in strict mode, then transformed with scrypt and HKDF.
  • local key file: standard-mode fallback only; it is stored outside the project directory.

Strict mode refuses to silently create a local key when no external secret is supplied and rejects key material shorter than 32 bytes.

KMS or external key provider

The provider receives non-secret context. It should release key material only to the expected workload identity.

const auth = await useGuardedMultiFileAuthState('./session', {
  securityLevel: 'strict',
  deploymentId: 'bot-production-01',
  keyProvider: async context => {
    const key = await myKmsClient.unwrapSessionKey({
      sessionId: context.sessionId,
      deploymentHash: context.deploymentHash,
      machineHash: context.machineHash,
      locationHash: context.locationHash
    })

    return {
      key,
      keyId: 'kms:elynn-wa-prod'
    }
  }
})

The context is bound to every encrypted file as authenticated data:

  • session ID;
  • deployment hash;
  • machine hash;
  • folder-location hash;
  • logical auth filename.

A file renamed, moved, copied into another deployment, or decrypted with the wrong context is rejected.

Remote single-active lease and rollback anchor

A remote authority is the recommended defense when the same encrypted session and key could be copied to another server/container. The provider must atomically enforce one active instanceId per sessionId.

With requireRemoteAnchor: true, the provider must also maintain a monotonic writeCounter. A request lower than the greatest accepted counter is a restored/rolled-back snapshot and must be rejected.

const counters = new Map()

async function sessionLeaseProvider(request) {
  if (request.action === 'release') {
    await leaseStore.release(request.sessionId, request.leaseId)
    return { allowed: true }
  }

  const previous = counters.get(request.sessionId) ?? -1
  if (request.writeCounter < previous) {
    return { allowed: false, reason: 'session rollback detected' }
  }

  const lease = request.action === 'claim'
    ? await leaseStore.claim(request)
    : await leaseStore.renew(request)

  if (!lease.allowed) return lease

  counters.set(request.sessionId, request.writeCounter)
  return {
    allowed: true,
    leaseId: lease.leaseId,
    expiresAt: lease.expiresAt,
    acceptedWriteCounter: request.writeCounter
  }
}

Strong response requirements:

{
  allowed: true,
  leaseId: 'opaque-lease-id',
  expiresAt: Date.now() + 30_000,
  acceptedWriteCounter: request.writeCounter
}

requireRemoteLease: true requires a non-empty leaseId and a future expiresAt. requireRemoteAnchor: true additionally requires acceptedWriteCounter to exactly match the local authenticated manifest counter. By default the anchor is synchronized after each manifest write. Set remoteAnchorOnWrite: false only when delayed anchoring is an intentional trade-off.

Rejecting a claim, renewal, or counter update blocks future auth-state reads and writes. Put this authority outside the hosting panel's trust domain.

Why public IP is not a security boundary

A public IP can be shared by multiple containers, users, reverse proxies, NAT clients, or hosting-panel workloads. It can also change without the machine changing. Session Guard v2 therefore does not use public IP as an authentication factor.

Files on disk

A guarded auth folder contains data such as:

session/
├── .elynn-guard-v2.json
├── .elynn-guard-v2.lease       # present only while active
├── creds.json.eg2
├── app-state-sync-key-....json.eg2
└── sender-key-....json.eg2

The manifest contains hashes, salts, identifiers, a monotonic write counter, and a MAC. It does not contain the master key or plaintext WhatsApp credentials.

Recovery and migration

  • Existing plaintext creds.json and key files are migrated when first read unless migratePlaintext: false is set.
  • Back up the encrypted folder and its external key separately.
  • Losing the external key makes the session unrecoverable.
  • resetGuard() only removes the obsolete v1 IP guard. It intentionally does not delete a v2 manifest, because the manifest is required to decrypt the encrypted auth files.
  • To move a protected session legitimately, perform an explicit export/re-encryption flow under operator control rather than deleting the manifest.

Operational hardening

  • Run the bot as a dedicated unprivileged user.
  • Keep the project directory unreadable to unrelated hosting-panel tenants.
  • Do not expose environment variables, process inspection, shell access, or arbitrary file download in the admin panel.
  • Use a KMS/Vault/workload identity key provider for production.
  • Use a remote lease and monotonic-counter store outside the compromised panel's trust domain.
  • Alert on lease rejection, manifest failure, repeated decrypt failures, and unexpected deployment changes.
  • Log identifiers and error codes, never decrypted credentials or key material.

Security and Stealth Audit

Scope

This audit covers two additions:

  1. sender-hidden direct messages that are delivered to the recipient but omitted from the sender account's linked-device synchronization list;
  2. Session Guard v2, an encrypted and deployment-bound replacement for plaintext multi-file auth state.

Sender-hidden direct messages

Implementation

sendMessage() accepts:

{
  selfSync: 'omit',
  emitOwnEvent: false
}

Aliases:

  • suppressSelfSync: true;
  • hideFromSender: true;
  • selfSync: false.

The direct-message encryption recipient list is partitioned into:

  • sender-owned linked devices;
  • recipient-owned devices.

In omit mode, sender-owned devices are excluded while recipient devices remain encrypted recipients. The exact Baileys device is skipped in both modes because it already owns the clear outgoing message.

Safety boundaries
  • Supported only for private PN/LID chats.
  • Rejected for groups, status, and newsletters.
  • Rejected when sending to the same WhatsApp account.
  • Does not hide the message from WhatsApp servers or recipients.
  • Does not delete an already synchronized message.
  • WhatsApp can change server fan-out behavior; live-device validation remains required.
Validation
  • policy aliases tested;
  • direct-only validation tested;
  • sender/recipient device partition tested;
  • convenience wrapper tested;
  • JavaScript syntax and TypeScript declarations tested.

A live WhatsApp account/device matrix was not available, so Android/iOS/Web rendering is not claimed as verified.

Session Guard v2

Previous weakness

The previous guard hashed a public IP and stored it beside the session. This did not stop:

  • two workloads on the same NAT/public IP;
  • an admin panel copying the guard file together with the session;
  • plaintext credential theft;
  • guard-file editing;
  • concurrent clones on the same host.
New controls
  • AES-256-GCM auth-state encryption;
  • scrypt and HKDF-SHA-256 key derivation;
  • authenticated deployment, machine, location, session, and filename context;
  • HMAC-authenticated manifest;
  • restrictive filesystem permissions;
  • atomic writes;
  • plaintext-to-encrypted migration;
  • local single-active lease with TTL and authenticated ownership, stored in the session folder so changing guardRoot cannot bypass it;
  • optional external key provider;
  • optional remote single-active lease provider with required lease ID/expiry validation;
  • optional remote monotonic writeCounter anchor for valid-snapshot rollback detection;
  • strict-mode minimum 32-byte key material;
  • fail-closed malformed/missing manifest and forged-lease handling;
  • fail-closed block state after lease loss;
  • explicit capability reporting and threat-model limitations.
Tested failure cases
  • duplicate process using the same session is blocked;
  • correct key reopens the session after clean close;
  • copied folder at a different path is rejected;
  • altered ciphertext fails AES-GCM authentication;
  • malformed or missing manifests fail closed instead of being regenerated;
  • forged local leases are rejected rather than treated as stale;
  • weak remote lease responses are rejected;
  • remotely anchored old snapshots are rejected;
  • plaintext credentials are absent from encrypted files;
  • strict TypeScript consumer declarations compile;
  • packed-package ESM root imports, Telegram/Discord ESM, CommonJS bridges, encrypted auth roundtrip, and NodeNext TypeScript compile from a clean consumer project.
Residual risk

A malicious root or hosting administrator capable of reading process memory, injecting code, replacing the package, or controlling the external key service credentials can still obtain active session material. Baileys must hold usable Signal/WhatsApp keys in process memory. This is an infrastructure boundary, not a library bug.

Production deployments should combine this package with workload identity, KMS/Vault, a remote lease authority, least-privilege service accounts, and isolation from the hosting control panel.


Rich Response Re-audit

[!NOTE] This file records the 2.1.0 Rich Response re-audit. Sender-hidden delivery and Session Guard v2 are audited separately in bagian Security and Stealth Audit.

Date: 2026-07-10

Correction

Version 2.0.0 incorrectly documented WhatsApp rich response as a secondary experimental/internal helper. The source had not been deleted, but the status, documentation, tests, and implementation completeness were insufficient.

This re-audit treats rich response as a current protocol surface and compares the package against:

  • WhiskeySockets/Baileys current v7 direction;
  • itsliaaa/baileys rich-response helper and documentation;
  • yemo-dev/baileys object-style rich-response API;
  • the bundled WAProto definitions.

Findings before repair

  • richResponse existed but was nearly undocumented in the rebuilt README.
  • CONTENT_ITEMS and LATEX generated empty unified sections.
  • inline video became a plain INLINE_VIDEO text marker.
  • grid image, dynamic metadata, and map metadata had no high-level helper.
  • the array API handled links only after a local patch and citation keys restarted in each entry.
  • custom bot JID was not exposed; a fixed bot JID was embedded.
  • raw submessages[] and complete AIRichResponseMessage input were not promoted.
  • no incoming-message extraction or unified-response decoder was available.
  • declarations used any throughout the rich helper.
  • smoke tests only checked a malformed link and did not test generated structures.

Repairs performed

Input APIs

The package now accepts:

  1. object API:
{ richResponse: { text, code, language, botJid } }
  1. ordered array API:
{ richResponse: [{ text }, { code }, { table }] }
  1. raw proto API:
{ submessages: [{ messageType, ... }] }
  1. complete raw message API:
{ richResponseMessage: { messageType, submessages, unifiedResponse } }
Supported submessage families
  • text and Markdown unified primitive;
  • tokenized code and code unified primitive;
  • citations/inline entities;
  • tables;
  • inline image;
  • grid image;
  • dynamic image/GIF metadata;
  • map metadata;
  • LaTeX expressions;
  • content-items/reel carousel;
  • raw unknown/future submessage passthrough.
Metadata and parsing
  • custom bot JID;
  • custom response ID;
  • disclaimer text;
  • forwarded context overrides;
  • custom or disabled verification metadata;
  • additional bot metadata;
  • wrapper-aware rich-message extraction;
  • unified JSON decoding with explicit parse errors.
TypeScript

Added concrete interfaces for links, sources, image URLs, reels, dynamic media, LaTeX, tables, raw submessages, options, and unified responses.

Validation performed

  • syntax check over modified JavaScript;
  • full offline smoke suite;
  • strict TypeScript consumer compilation;
  • rich object API generation;
  • rich array API generation;
  • raw submessage generation;
  • custom bot JID propagation;
  • code tokenization;
  • citation URL validation;
  • table generation;
  • inline and grid image generation;
  • dynamic GIF metadata generation;
  • map generation;
  • LaTeX unified generation;
  • content-item unified generation;
  • verification metadata opt-out;
  • incoming rich extraction;
  • unified-response JSON decoding.

Validation boundary

The package can prove that it builds coherent protobuf objects and unified JSON. Only live account tests can prove server acceptance and visual rendering across specific WhatsApp Android, iOS, Desktop, and Web versions. This boundary applies to any unofficial Baileys fork and is not a reason to remove or hide current protocol support.

Other platform status

The Telegram/Telegraf and Discord/Oceanic.js fixes from v2.0.0 remain present, including ESM/CommonJS bridges, health checks, declarations, and third-party license notices.


Third-Party Notices

elynn-baileys includes or derives code from the projects below. Their names and trademarks remain the property of their respective owners.

Baileys

  • Project: WhiskeySockets/Baileys
  • Bundled basis: 7.0.0 release-candidate line
  • License: MIT
MIT License

Copyright (c) 2025 Rajeh Taher/WhiskeySockets

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Telegraf

  • Project: telegraf/telegraf
  • Bundled basis: 4.16.3, with Elynn compatibility additions
  • License: MIT
The MIT License (MIT)

Copyright (c) 2016-2019 Vitaly Domnikov
Copyright (c) 2020-2023 The Telegraf Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Oceanic.js

The code under lib/Discord is based on Oceanic.js, not discord.js.

  • Project: OceanicJS/Oceanic
  • Bundled basis: 1.14.0 line, with Elynn ESM bridge additions
  • License: MIT
MIT License

Copyright (c) 2023 Donovan Daniels

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Runtime dependencies

Runtime and optional dependencies keep their own licenses. Important examples:

  • libsignal: GPL-3.0
  • @discordjs/voice: Apache-2.0, optional
  • sharp: Apache-2.0, optional peer dependency
  • sandwich-stream: Apache-2.0
  • @hapi/boom: BSD-3-Clause
  • protobufjs: BSD-3-Clause
  • lru-cache: BlueOak-1.0.0

This notice is attribution, not legal advice. Distributors must preserve all applicable notices and review the dependency tree produced by their lockfile.

Rich response helper contributions

The rich-response helper in lib/Utils/rich-message-utils.js originated from work by Lia Wynn in itsliaaa/baileys and has been extended by Elynn. The original in-file attribution notice is preserved without modification.

  • Project: itsliaaa/baileys
  • Contributor/author attribution: Lia Wynn
  • License declared by the source package: MIT
  • Relevant work: rich response submessages, code tokenizer, table rendering, unified response payload, and bot-forwarded wrapper

Redistributors must preserve the original in-file credit notice together with applicable MIT notices.


ELYNN BAILEYS 2.3.0

Rich-first. Sender-aware. Deployment-bound.