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
Maintainers
Keywords
Readme
✦ ELYNN BAILEYS
WhatsApp Rich Messaging · Telegram · Discord
Rich AI Response dijadikan fitur utama — bukan helper tersembunyi.
Fork Baileys berorientasi protokol dengan bridge Telegraf dan Oceanic.js.
[!IMPORTANT]
elynn-baileysmemakai 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 clientUserbot 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, danhideFromSenderuntuk 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
keyProvideruntuk 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/voicehanya optional dependency untuk voice.
Installation
npm install elynn-baileysPersyaratan:
Node.js >= 20.18.1Optional native/media features:
npm install sharp better-sqlite3 canvas @discordjs/voice telegramWhatsApp 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.forwardedAiBotMessageInfounifiedResponse.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=1Session 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.eg2Kontrol 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
0700dan file0600; - 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
writeCounteruntuk menolak snapshot/rollback lama; - strict mode mewajibkan key material minimal 32 byte;
keyProvideruntuk 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-runPemeriksaan 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:
- LICENSE
- bagian Third-Party Notices
- bagian Rich Response Research
- bagian Session Guard
- bagian Security and Stealth Audit
lib/Telegram/LICENSElib/Discord/LICENSE
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
- Elynn Plugins
- Rich Response Research
- Session Guard Security
- Security and Stealth Audit
- Rich Response Re-audit
- Third-Party Notices
Changelog
2.3.0 — 2026-07-11
- Added
generateWAMotionPhotoMessages()andsendMessage({ motionPhoto })parent/child relay support. - Added experimental high-level
pollAddOptiongeneration withPOLL_ADD_OPTIONassociation metadata. - Added raw
messageHistoryNoticeand uploadedmessageHistoryBundlegeneration 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-zerodraft_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/userbotpackage export.
Discord
- Fixed the invalid ESM syntax in the CommonJS
lib/Discord/connect-dc.jsboundary. - 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-v2ESM/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, andselfSync: 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, andreleaseactions. - Added optional external
keyProviderintegration 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
guardRootcannot create a second independent local lock. - Added strong remote lease response validation and optional monotonic
writeCounterrollback 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
Telegrafas a compatibility alias forElynnTelegrafin 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-stylerichResponse: [...], and rawsubmessagesAPIs. - Added support for all bundled
AIRichResponseSubMessageTypesurfaces: text, code, inline image, grid image, table, dynamic media, map, LaTeX, and content items. - Replaced the
INLINE_VIDEOtext placeholder withAIRichResponseDynamicMetadata. - 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()anddecodeUnifiedResponse()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
interactiveAsTemplatewrapper 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
NativeFlowMessagetype 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
canvasout of the mandatory install path. - Removed unused direct dependencies and shipped
@types/wsfor 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.secretorELYNN_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.eg2The 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.jsonand key files are migrated when first read unlessmigratePlaintext: falseis 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:
- sender-hidden direct messages that are delivered to the recipient but omitted from the sender account's linked-device synchronization list;
- 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
guardRootcannot bypass it; - optional external key provider;
- optional remote single-active lease provider with required lease ID/expiry validation;
- optional remote monotonic
writeCounteranchor 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
richResponseexisted but was nearly undocumented in the rebuilt README.CONTENT_ITEMSandLATEXgenerated empty unified sections.- inline video became a plain
INLINE_VIDEOtext 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 completeAIRichResponseMessageinput were not promoted. - no incoming-message extraction or unified-response decoder was available.
- declarations used
anythroughout the rich helper. - smoke tests only checked a malformed link and did not test generated structures.
Repairs performed
Input APIs
The package now accepts:
- object API:
{ richResponse: { text, code, language, botJid } }- ordered array API:
{ richResponse: [{ text }, { code }, { table }] }- raw proto API:
{ submessages: [{ messageType, ... }] }- 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, optionalsharp: Apache-2.0, optional peer dependencysandwich-stream: Apache-2.0@hapi/boom: BSD-3-Clauseprotobufjs: BSD-3-Clauselru-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.
