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

merajah-sockets

v1.0.3

Published

WhatsApp Web Multi-Device Protocol Engine (Merajah Sockets) with Native Signal E2EE and Pure WebSocket Transport

Readme

Merajah Sockets

NPM Version NPM Downloads GitHub Repository Node License Protocol Security

Merajah Sockets adalah library client native WhatsApp Web Multi-Device berbasis WebSocket murni dengan protokol E2EE Signal asli, dirancang dari nol (from scratch) dengan desain API modern, bebas, dan fleksibel tanpa konvensi usang library lama.

🚀 Bukan wrapper Baileys / WhatsApp-Web.js: Memiliki arsitektur event-driven yang bersih, sintaks pengiriman yang manusiawi, dan performa tinggi dengan konsumsi RAM di bawah 50MB.


📑 Daftar Isi

  1. Fitur Unggulan
  2. Instalasi
  3. Panduan Memulai (Quick Start)
  4. Panduan Koneksi & Sesi
  5. Sistem Event & Event Bus (client.ev / client.on)
  6. Pesan Mentah & Format Sendiri (Userland Serialization)
  7. Fungsi Mengirim Pesan (client.send...)
  8. Panduan Cache & MessageVault
  9. Penanganan Grup WhatsApp & Manajemen Utilitas
  10. Saluran WhatsApp (Newsletter / Channels)
  11. Pengendalian Panggilan Masuk (VoIP Calls)
  12. Pengaturan Privasi & Pemblokiran Akun
  13. Profil & Status Bio
  14. Uji Coba & Protokol
  15. Lisensi

🌟 Fitur Unggulan

  • ⚡ Native WebSocket Engine: Beroperasi langsung di atas ws RFC6455 dengan Noise XX 25519 Handshake. Zero browser di produksi.
  • 🔒 Real Signal E2EE: Enkripsi Curve25519, AES-CBC-256, HMAC-SHA256 dengan auto-repair session saat terjadi Bad MAC.
  • 💬 CreativeMessage Abstraction: Pesan masuk otomatis di-parse: teks, command, argumen, mention, quote, tombol, dan media langsung siap pakai.
  • 🛡️ Anti-Delete Bawaan: Event client.on('message:delete') langsung menyediakan salinan pesan asli yang ditarik pengirim.
  • 👁️ Anti-ViewOnce Bawaan: Event client.on('message:view_once') dan unwrap otomatis pesan sekali lihat.
  • 🎨 Meta Developers Cloud API Interactive Buttons: Tombol interaktif (URL, Webview, Salin Kode, Quick Reply) dan Single-Select List Menu tanpa tagihan iklan atau peringatan disclaimer.
  • 📢 Throttled Broadcast: Siaran pesan massal dengan jeda interval aman dan callback pemantauan progres.

📦 Instalasi

# Node.js v18.0.0 atau lebih baru dibutuhkan
npm install ./merajah-sockets

🚀 Panduan Memulai (Quick Start)

Hanya butuh beberapa baris untuk membuat bot WhatsApp modern:

const {
  createMerajahClient,
  useFileSession,
} = require('merajah-sockets');

async function main() {
  // 1. Inisialisasi sesi penyimpanan lokal
  const session = await useFileSession('./auth_info');

  // 2. Buat client Merajah Sockets
  const client = createMerajahClient({
    session,
    printQR: true,
  });

  // 3. Tangani event siap
  client.on('ready', (user) => {
    console.log(`✅ Bot Aktif sebagai: ${user.id}`);
  });

  // 4. Tangani pesan masuk (gaya modern, bersih, intuitif)
  client.on('message', async (msg) => {
    if (msg.fromMe) return;

    if (msg.command === 'ping') {
      await msg.reply('🏓 Pong!');
    }

    if (msg.command === 'halo') {
      await msg.reply(`Halo @${msg.sender.split('@')[0]}!`, {
        mentions: [msg.sender],
      });
    }
  });

  // 5. Hubungkan ke WhatsApp
  await client.connect();
}

main().catch(console.error);

🔌 Panduan Koneksi & Sesi

1. useFileSession(sessionFolder)

Menyimpan kredensial sesi multi-device, kunci enkripsi pre-key, dan identitas ke dalam folder disk secara modular.

const { useFileSession } = require('merajah-sockets');

// Membuat atau memuat sesi dari folder ./auth_info
const session = await useFileSession('./auth_info');

2. createMerajahClient(options)

Fungsi factory untuk membuat instance client WhatsApp baru.

Opsi Konfigurasi:

  • session (Wajib): Hasil dari useFileSession.
  • vault (Opsional): Instance MessageVault untuk caching pesan. Jika tidak diisi, vault in-memory default akan otomatis dibuat.
  • printQR (Opsional, default: true): Menampilkan QR code di terminal.
  • connectTimeoutMs (Opsional, default: 20000): Timeout koneksi dalam milidetik.
  • keepAliveIntervalMs (Opsional, default: 25000): Interval heartbeat ping WhatsApp.
  • browser (Opsional): Identitas browser platform (default: ['Ubuntu', 'Chrome', '22.04.4']).

Mode 1: QR Code Terminal

const client = createMerajahClient({
  session,
  printQR: true,
});

await client.connect();

Mode 2: 8-Digit Pairing Code

Bagi server VPS tanpa tampilan scan kamera, hubungkan langsung menggunakan kode pairing 8-digit:

const client = createMerajahClient({
  session,
  printQR: false,
});

await client.connect();

// Cek apakah akun belum terdaftar
if (!session.state.creds.registered && !session.state.creds.me) {
  const pairingCode = await client.requestPairingCode('6285123456789');
  console.log('🔑 MASUKKAN KODE PAIRING INI DI WHATSAPP:', pairingCode);
  // Buka WhatsApp di HP: Perangkat Tertaut > Tautkan Perangkat > Tautkan dengan nomor telepon
}

Penanganan Lifecycle Koneksi

// Saat bot berhasil terhubung dan siap memproses perintah
client.on('ready', (user) => {
  console.log('🟢 Bot online! JID:', user.id);
});

// Saat proses jabat tangan (handshake) sedang berlangsung
client.on('connecting', () => {
  console.log('🔄 Menghubungkan ke server WhatsApp Web...');
});

// Saat QR code baru dihasilkan
client.on('qr', (qrString) => {
  console.log('QR Code diperbarui.');
});

// Saat koneksi terputus
client.on('disconnected', ({ error, statusCode }) => {
  console.log(`⚠️ Terputus: ${error?.message} (Status: ${statusCode})`);

  if (statusCode === 401) {
    console.error('❌ Sesi logout (401). Hapus folder sesi lalu pairing ulang.');
    process.exit(1);
  } else {
    // Reconnect otomatis
    setTimeout(() => client.connect(), 3000);
  }
});

📡 Sistem Event Modern (client.on)

Merajah Sockets meninggalkan penamaan event lama seperti messages.upsert dan menggantikannya dengan event-event tingkat tinggi:

Event: ready / connected

Dipanggil saat koneksi WebSocket dan sesi E2EE selesai diautentikasi.

client.on('ready', (user) => {
  console.log('User aktif:', user.id, user.name);
});

Event: messages.upsert (Standar Baileys Murni)

Dipanggil setiap kali ada pesan masuk dari server WhatsApp dalam format standar Baileys:

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

  for (const rawMsg of messages) {
    // rawMsg adalah objek pesan mentah (raw WAMessage / proto.IWebMessageInfo)
    console.log('[Upsert Raw ID]:', rawMsg.key.id);
  }
});

Event: message (Pesan Masuk Mentah)

Dipanggil untuk setiap pesan masuk individu dalam bentuk mentah (raw). Tidak ada pemaksaan serializer internal sehingga Anda bebas memformat objek pesan sesuai kebutuhan bot Anda:

client.on('message', async (rawMsg) => {
  console.log('[Pesan Mentah]:', rawMsg.key.remoteJid, rawMsg.message);
});

Event: message:delete (Anti-Delete)

Dipanggil secara otomatis ketika seseorang menarik/menghapus pesan (Delete for Everyone). Menyediakan salinan isi pesan asli yang tersimpan di memori cache vault:

client.on('message:delete', ({ chat, sender, targetKey, originalMessage, revokedAt }) => {
  console.log(`🗑️ Pesan ${targetKey.id} ditarik oleh ${sender}`);
  if (originalMessage) {
    console.log('Isi pesan asli yang dihapus:', originalMessage.message);
  }
});

Event: message:view_once (Anti-ViewOnce)

Dipanggil saat menerima pesan sekali lihat (View-Once). Payload media langsung di-decode dan siap diinspeksi.

client.on('message:view_once', ({ msg, payload }) => {
  console.log('👁️ Pesan sekali lihat diterima dari:', msg.key.participant || msg.key.remoteJid);
  console.log('Tipe media sekali lihat:', Object.keys(payload));
});

💬 Pesan Mentah & Format Sendiri (Userland Serialization)

1. Konsep Murni Baileys: Tanpa Internal Serialize

Sesuai dengan filosofi arsitektur Baileys, Merajah Sockets adalah library client/socket protokol murni:

  • Library tidak menyuntikkan serializer opini pribadi (tidak memaksakan format msg.command, msg.prefix, atau mengubah struktur objek pesan asli).
  • Semua pesan yang dipancarkan oleh event messages.upsert maupun message adalah objek pesan mentah (raw WAMessage).
  • Developer memiliki kendali penuh 100% untuk memformat, mengekstrak command, prefix, argumen, dan fungsi pembantu (helper) di dalam kode bot masing-masing (userland).

2. Struktur Objek Pesan Mentah (WAMessage):

{
  key: {
    remoteJid: '[email protected]', // JID chat / grup
    fromMe: false,                        // Apakah dikirim oleh bot sendiri
    id: '3EB07A8F9B1234567890',           // ID unik pesan WhatsApp
    participant: '[email protected]' // Pengirim (jika di grup)
  },
  messageTimestamp: 1789268340,           // Waktu pesan (unix timestamp)
  pushName: 'Alex',                       // Nama WhatsApp pengirim
  message: {                              // Objek Protobuf asli
    conversation: '!ping',
    // atau extendedTextMessage, imageMessage, interactiveMessage, dll.
  }
}

3. Contoh Lengkap Fungsi serialize() di Userland:

Anda dapat menyalin fungsi serialize berikut ke file bot Anda (misal client.js atau lib/serialize.js):

/**
 * Fungsi Serialize di Userland Bot
 * @param {MerajahClient} client
 * @param {object} m - Pesan mentah dari event message / messages.upsert
 */
function serialize(client, m) {
  if (!m) return m;

  const key = m.key || {};
  const isGroup = Boolean(key.remoteJid && key.remoteJid.endsWith('@g.us'));
  const chat = key.remoteJid || '';
  const fromMe = Boolean(key.fromMe);
  const sender = fromMe
    ? (client.user?.id || '')
    : (isGroup ? (key.participant || '') : chat);

  // 1. Ekstrak pesan bagian dalam (unwrapping ephemeral / viewOnce)
  let msg = m.message;
  let isViewOnce = false;
  if (msg) {
    while (msg) {
      if (
        msg.viewOnceMessage ||
        msg.viewOnceMessageV2 ||
        msg.viewOnceMessageV2Extension ||
        msg.imageMessage?.viewOnce ||
        msg.videoMessage?.viewOnce
      ) {
        isViewOnce = true;
      }
      const next =
        msg.ephemeralMessage?.message ||
        msg.viewOnceMessage?.message ||
        msg.viewOnceMessageV2?.message ||
        msg.viewOnceMessageV2Extension?.message ||
        msg.documentWithCaptionMessage?.message ||
        msg.deviceSentMessage?.message;
      if (!next) break;
      msg = next;
    }
  }

  // 2. Ekstrak teks atau respons tombol interaktif
  let text = '';
  let buttonResponse = null;

  if (msg) {
    // Tombol Interactive Native Flow
    if (msg.interactiveResponseMessage?.nativeFlowResponseMessage) {
      try {
        const p = JSON.parse(msg.interactiveResponseMessage.nativeFlowResponseMessage.paramsJson || '{}');
        const btnId = p.id || p.selectedRowId || p.rowId || '';
        text = btnId;
        buttonResponse = { id: btnId, text: btnId };
      } catch {}
    }
    // List Menu Single Select
    if (!text && msg.listResponseMessage?.singleSelectReply?.selectedRowId) {
      const rowId = msg.listResponseMessage.singleSelectReply.selectedRowId;
      text = rowId;
      buttonResponse = { id: rowId, text: rowId };
    }
    // Buttons Quick Reply
    if (!text && msg.buttonsResponseMessage?.selectedButtonId) {
      const btnId = msg.buttonsResponseMessage.selectedButtonId;
      text = btnId;
      buttonResponse = { id: btnId, text: msg.buttonsResponseMessage.selectedDisplayText || btnId };
    }
    // Teks percakapan biasa atau caption media
    if (!text) {
      text =
        msg.conversation ||
        msg.extendedTextMessage?.text ||
        msg.imageMessage?.caption ||
        msg.videoMessage?.caption ||
        msg.documentMessage?.caption ||
        '';
    }
  }

  // 3. Ekstrak Quoted Context (Pesan yang dikutip)
  let quoted = null;
  const ctx =
    msg?.extendedTextMessage?.contextInfo ||
    msg?.imageMessage?.contextInfo ||
    msg?.videoMessage?.contextInfo ||
    msg?.documentMessage?.contextInfo ||
    msg?.audioMessage?.contextInfo ||
    msg?.interactiveMessage?.contextInfo;

  if (ctx && ctx.stanzaId) {
    quoted = {
      id: ctx.stanzaId,
      sender: ctx.participant || key.remoteJid || '',
      text: ctx.quotedMessage?.conversation || ctx.quotedMessage?.extendedTextMessage?.text || '',
      message: ctx.quotedMessage || null,
    };
  }

  // 4. Parsing prefix, command, dan arguments
  const trimmed = (text || '').trim();
  let prefix = '';
  let command = '';
  let args = [];

  const match = trimmed.match(/^([!./#$])(\S+)/);
  if (match) {
    prefix = match[1];
    command = match[2].toLowerCase();
    args = trimmed.slice(prefix.length + command.length).trim().split(/\s+/).filter(Boolean);
  } else {
    const parts = trimmed.split(/\s+/);
    command = (parts[0] || '').toLowerCase();
    args = parts.slice(1);
  }

  return {
    raw: m,
    key,
    id: key.id,
    chat,
    sender,
    fromMe,
    isGroup,
    pushName: m.pushName || '',
    timestamp: m.messageTimestamp || Math.floor(Date.now() / 1000),
    message: msg,
    text,
    body: text,
    prefix,
    command,
    args,
    quoted,
    isViewOnce,
    buttonResponse,
    // Method pembantu reply langsung
    reply: (content, options) => {
      const opts = { ...options };
      if (isGroup && !opts.quotedParticipant) {
        opts.quotedParticipant = sender;
      }
      return client.sendText(chat, typeof content === 'string' ? content : (content.text || ''), {
        ...opts,
        quoted: m,
      });
    },
  };
}

4. Contoh Handler Pesan di Bot:

// Menggunakan event 'message' atau 'messages.upsert'
client.on('message', async (rawMsg) => {
  // Format pesan mentah menggunakan fungsi serialize buatan sendiri
  const msg = serialize(client, rawMsg);

  if (msg.fromMe) return;

  console.log(`[Pesan] Dari ${msg.sender}: "${msg.text}" (Cmd: ${msg.command})`);

  if (msg.command === 'ping') {
    await msg.reply('🏓 Pong!');
  }
});

📤 Fungsi Mengirim Pesan (client.send...)

Tidak perlu lagi menulis payload proto yang rumit. Merajah Sockets menyediakan fungsi modular tingkat tinggi:

1. client.sendText(to, text, options)

Mengirim pesan teks dengan opsi mention, quoted, link preview, dan view-once.

await client.sendText(chat, 'Halo semua anggota!');

// Dengan mention dan kutipan
await client.sendText(chat, 'Halo @6285123456789', {
  mentions: ['[email protected]'],
  quoted: msg.raw,
});

// Pesan teks View-Once (Sekali Lihat)
await client.sendText(chat, 'Kode rahasia: 8899', {
  viewOnce: true,
});

2. client.sendImage(to, image, options)

Mengirim gambar dari Buffer memori atau path file lokal.

// Mengirim dari file lokal
await client.sendImage(chat, './banner.jpg', {
  caption: '🖼️ Banner Resmi Komunitas',
});

// Mengirim gambar View-Once (Sekali Lihat)
await client.sendImage(chat, fs.readFileSync('./foto.jpg'), {
  caption: 'Foto ini hanya bisa dibuka sekali!',
  viewOnce: true,
});

3. client.sendVideo(to, video, options)

Mengirim video atau animasi GIF.

await client.sendVideo(chat, './tutorial.mp4', {
  caption: '🎥 Video Tutorial Penggunaan Bot',
});

// Mengirim sebagai animasi GIF memutar otomatis
await client.sendVideo(chat, './animasi.mp4', {
  gifPlayback: true,
});

4. client.sendAudio(to, audio, options)

Mengirim file audio atau rekaman suara pesan suara (Voice Note / PTT).

// Mengirim sebagai Voice Note (VN / PTT)
await client.sendAudio(chat, './rekaman.ogg', {
  ptt: true,
});

// Mengirim sebagai lagu / file audio biasa
await client.sendAudio(chat, './musik.mp3', {
  ptt: false,
});

5. client.sendDocument(to, document, options)

Mengirim dokumen file apa pun (PDF, ZIP, DOCX, APK, dll).

await client.sendDocument(chat, './laporan.pdf', {
  fileName: 'Laporan_Tahunan_2026.pdf',
  caption: 'Silakan unduh dokumen laporan terlampir.',
  mimetype: 'application/pdf',
});

6. client.sendButton(to, options)

Mengirim tombol Call-To-Action (URL, Buka Mini Web, Salin Kode) atau Quick Reply Buttons.

// Mode 1: Call-To-Action (CTA) Resmi Meta Developers API
await client.sendButton(chat, {
  title: '✨ PUSAT BANTUAN CREATIVE',
  body: 'Pilih aksi yang Anda butuhkan melalui tombol di bawah:',
  footer: 'Merajah Sockets Native Engine',
  buttons: [
    { type: 'url', text: '🌐 Kunjungi Situs', url: 'https://google.com' },
    { type: 'copy', text: '📋 Salin Token', copy: 'TOKEN-CREATIVE-2026' },
    { type: 'reply', text: '🏓 Cek Status', id: 'ping' },
  ],
});

// Mode 2: Quick Reply Cepat
await client.sendButton(chat, {
  title: '⚡ PILIHAN CEPAT',
  body: 'Pilih salah satu:',
  footer: 'Pilih menu',
  buttons: [
    { text: 'Opsi 1', id: 'opsi_1' },
    { text: 'Opsi 2', id: 'opsi_2' },
  ],
});

7. client.sendList(to, options)

Mengirim Single-Select List Menu dengan judul seksi dan baris menu interaktif.

await client.sendList(chat, {
  title: '📋 DAFTAR MENU BOT',
  body: 'Pilih salah satu layanan dari daftar menu di bawah:',
  footer: 'Merajah Sockets Standard',
  buttonText: 'Buka Daftar Menu',
  sections: [
    {
      title: 'Layanan Utama',
      rows: [
        { id: 'ping', title: '🏓 Ping Pong', description: 'Uji latensi respon bot' },
        { id: 'status', title: '📊 Status Sistem', description: 'Informasi kesehatan server' },
      ],
    },
    {
      title: 'Bantuan & Komunitas',
      rows: [
        { id: 'group', title: '👥 Gabung Grup', description: 'Tautan resmi komunitas WhatsApp' },
        { id: 'owner', title: '👤 Kontak Admin', description: 'Hubungi pengembang langsung' },
      ],
    },
  ],
});

8. client.sendCard(to, options)

Mengirim kartu pratinjau tautan (ExternalAdReply Link Preview Card) dengan gambar banner besar nyata.

await client.sendCard(chat, {
  text: '🌟 Bergabunglah ke saluran resmi kami untuk mendapatkan update terbaru!',
  title: 'Komunitas Resmi Merajah Sockets',
  body: 'Grup diskusi WhatsApp Multi-Device',
  thumbnail: fs.readFileSync('./thumb.jpg'),
  url: 'https://chat.whatsapp.com/KcFAwyGTo8RBhPv03LvoHZ',
  renderLargerThumbnail: true,
});

9. client.revoke(to, key)

Menghapus pesan bagi semua orang (Delete for Everyone). Alias: client.deleteMessage(to, key).

const sent = await client.sendText(chat, 'Pesan ini akan dihapus...');

// Hapus pesan setelah 3 detik
setTimeout(async () => {
  await client.revoke(chat, sent.key);
}, 3000);

10. client.edit(to, key, newText)

Mengedit teks pesan yang telah terkirim.

const sent = await client.sendText(chat, 'Teks salah ketik');

await client.edit(chat, sent.key, 'Teks yang sudah diperbaiki ✅');

11. client.react(to, key, emoji)

Memberikan reaksi emoji pada pesan apa pun.

await client.react(chat, targetMessage.key, '🚀');

12. client.broadcast(recipients, content, options)

Menyiarkan pesan ke banyak penerima secara berurutan dengan penundaan (throttling delay) agar terhindar dari pemblokiran server WhatsApp.

const recipients = [
  '[email protected]',
  '[email protected]',
  '[email protected]',
];

const report = await client.broadcast(
  recipients,
  '📢 Pengumuman: Server sedang dalam pemeliharaan berkala.',
  {
    delayMs: 400, // Jeda 400ms antar pesan
    onProgress: ({ current, total, jid, success, error }) => {
      console.log(`[Progress ${current}/${total}] Mengirim ke ${jid}: ${success ? 'OK' : error}`);
    },
  }
);

console.log(`Broadcast selesai. Berhasil: ${report.sent.length}, Gagal: ${report.failed.length}`);

13. client.sendAlbum(to, options)

Mengirimkan 2 atau lebih media (gambar atau video) yang ditampilkan secara otomatis dalam tata letak kisi (grid album layout) khas WhatsApp, lengkap dengan expectedImageCount, expectedVideoCount, dan messageAssociation:

const fs = require('fs');

const foto1 = fs.readFileSync('./foto1.jpg');
const foto2 = fs.readFileSync('./foto2.jpg');

await client.sendAlbum(chat, {
  album: [
    { image: foto1, caption: 'Foto Produk 1' },
    { image: foto2, caption: 'Foto Produk 2' },
  ],
  caption: '📸 *Koleksi Katalog Produk Terbaru*',
  quoted: msg.raw,
});

14. client.sendPoll(to, poll, options)

Mengirim pesan undian (Polling) dengan pilihan ganda atau pilihan tunggal.

await client.sendPoll(chat, {
  name: '📊 Fitur apa yang paling Anda sukai di Merajah Sockets?',
  values: [
    '⚡ Pairing Code Cepat',
    '🔒 Real Signal E2EE',
    '🎨 Tombol Interaktif Resmi',
    '🛡️ Anti-Delete Bawaan'
  ],
  selectableCount: 1, // 1 untuk pilihan tunggal, > 1 untuk multi-choice
});

15. client.pin(to, key, durationSeconds)

Menyematkan (Pin Message) pesan penting dalam percakapan peribadi atau grup.

// Sematkan selama 24 jam (86400 detik)
await client.pin(chat, targetMessage.key, 86400);

// Durasi lain yang didukung:
// 604800 (7 hari)
// 2592000 (30 hari)
// 0 (Lepas sematan / Unpin)

16. client.sendStatus(content, options)

Menyiarkan status atau cerita (WhatsApp Story) ke status@broadcast.

// 1. Status teks
await client.sendStatus('🚀 Selamat datang di era baru Merajah Sockets!');

// 2. Status gambar
await client.sendStatus({
  image: fs.readFileSync('./story.jpg'),
  caption: 'Suasana pagi ini ☕',
});

// 3. Status video
await client.sendStatus({
  video: fs.readFileSync('./story.mp4'),
  caption: 'Video pendek perjalanan',
});

🧠 Panduan Cache & MessageVault

MessageVault bertindak sebagai brankas memori performa tinggi untuk mengelola pesan masuk, mendeteksi duplikasi, serta mendukung fitur Anti-Delete dan Anti-ViewOnce.

1. createMessageVault(options)

const { createMessageVault } = require('merajah-sockets');

const vault = createMessageVault({
  maxMessagesPerChat: 300, // Jumlah riwayat pesan yang disimpan per chat
  maxSeenIds: 5000,        // Batas cache deduplikasi pesan (LRU eviction)
});

2. Pencarian Pesan & Riwayat

// Mencari pesan spesifik berdasarkan ID
const msg = vault.findMessage(chatJid, '3EB012345');

// Mengambil 20 pesan terbaru dari suatu chat
const history = vault.getHistory(chatJid, 20);

// Memeriksa apakah pesan sudah pernah diproses sebelumnya
if (vault.hasSeen(messageId)) {
  console.log('Pesan sudah pernah diproses');
}

3. Anti-Delete di Vault

Ketika pesan ditarik oleh pengirim, MessageVault otomatis menyalinnya ke arsip pesan terhapus:

const deletedRecord = vault.findRevoked(chatJid, messageId);
if (deletedRecord) {
  console.log('Isi pesan asli:', deletedRecord.originalMessage.message);
  console.log('Waktu dihapus:', new Date(deletedRecord.revokedAt * 1000).toLocaleString());
}

4. Anti-ViewOnce di Vault

Setiap pesan sekali lihat yang masuk secara otomatis disimpan ke arsip khusus View-Once:

// Ambil pesan View-Once
const vo = vault.findViewOnce(chatJid, messageId);

// Dekode dan ekstrak payload media tanpa batasan
const payload = vault.decodeViewOnce(chatJid, messageId);
console.log('Media caption:', payload.imageMessage?.caption);

5. Backup & Restore Disk JSON

Simpan seluruh memori vault ke file JSON agar data chat tetap ada meskipun server restart:

// Pulihkan data saat bot mulai
vault.restore('./store_backup.json');

// Simpan data setiap 30 detik
setInterval(() => {
  vault.backup('./store_backup.json');
}, 30000);

👥 Penanganan Grup WhatsApp (Group Handling & Fanout)

Merajah Sockets v1.0-beta mendukung penuh interaksi di dalam WhatsApp Group (@g.us) secara native, cepat, dan stabil.

1. Arsitektur Multi-Device Group Fanout

Pada protokol resmi WhatsApp Multi-Device XMPP Binary:

  • Pesan keluar ke grup menggunakan stansa <message to="...g.us">.
  • Konten pesan dienkripsi secara individual untuk setiap perangkat anggota grup menggunakan Signal Protocol E2EE asli dan disusun ke dalam node <participants><to jid="user:dev@domain"><enc>...</enc></to></participants>.
  • Node <enc v="2" type="skmsg" /> disematkan untuk kompatibilitas protokol multi-device.
  • Sesi perangkat master/primer bot juga menerima salinan DSM (Device Sent Message) secara otomatis.
  • Bebas Error 479: Library secara otomatis melakukan batch pre-key assertion dan memvalidasi JID setiap peserta agar tidak terjadi penolakan stansa oleh server WhatsApp.

2. Membaca Metadata Grup (client.groupMetadata)

const groupJid = '[email protected]';

// Ambil metadata grup (dengan auto-caching memori 5 menit)
const meta = await client.groupMetadata(groupJid);

console.log('Subjek Grup :', meta.subject);
console.log('Pembuat Grup:', meta.owner);
console.log('Deskripsi   :', meta.desc);
console.log('Total Anggota:', meta.participants.length);

// Cek apakah pengirim pesan adalah admin
const isAdmin = meta.participants.some(
  (p) => (p.id === msg.sender || p.lid === msg.sender) && (p.admin === 'admin' || p.admin === 'superadmin')
);

3. Mengambil Tautan Undangan Grup (client.groupInviteCode)

const inviteCode = await client.groupInviteCode(groupJid);
console.log('Link Grup:', `https://chat.whatsapp.com/${inviteCode}`);

4. Membalas Pesan di Dalam Grup (msg.reply)

Di dalam event client.on('message'), properti msg.isGroup bernilai true jika pesan berasal dari grup:

client.on('message', async (msg) => {
  if (msg.fromMe) return;

  // msg.chat   = JID Grup (120363...g.us)
  // msg.sender = JID Pengguna pengirim ([email protected] atau xxx@lid)
  if (msg.command === 'ping') {
    // Membalas langsung ke dalam grup dan mengutip pesan pengirim
    await msg.reply('🏓 *Pong!*\nRespon cepat dari bot di dalam grup.');
  }
});

5. Mengirim Tombol Interaktif & List di Dalam Grup

Tombol Call-To-Action (URL, Salin Kode, Webview), Quick Reply, maupun Single-Select List Menu dapat dikirimkan langsung ke grup. Node <biz> native flow secara otomatis disematkan sehingga tombol tampil elegan di WhatsApp Android dan iOS:

// Kirim tombol Call-to-Action ke grup
await client.sendButton(groupJid, {
  title: '⚡ PENGUMUMAN GRUP',
  body: 'Halo semua anggota grup! Silakan akses tautan resmi atau salin kode promo di bawah:',
  footer: 'Merajah Sockets • Group System',
  buttons: [
    { type: 'url', text: '🌐 Kunjungi Website', url: 'https://example.com' },
    { type: 'copy', text: '📋 Salin Voucher', copy: 'MERAJAH-GROUP-2026' },
  ],
});

// Kirim Single-Select List Menu ke grup
await client.sendList(groupJid, {
  title: '📋 DAFTAR MENU GRUP',
  body: 'Pilih aksi atau bantuan dari daftar menu:',
  buttonText: 'Buka Menu',
  sections: [
    {
      title: 'Menu Utama',
      rows: [
        { id: 'rules', title: '📜 Peraturan Grup', description: 'Tata tertib grup' },
        { id: 'admin', title: '👮 Panggil Admin', description: 'Hubungi pengurus grup' },
      ],
    },
  ],
});

6. Menangani Klik Tombol di Grup (button:click)

Saat anggota grup menekan tombol Quick Reply atau memilih opsi List:

client.on('button:click', async ({ buttonId, sender, chat, msg, reply }) => {
  console.log(`[Button Click] ID: "${buttonId}" dari ${sender} di grup ${chat}`);

  if (buttonId === 'rules') {
    await reply('📜 *PERATURAN GRUP*\n1. Hormati sesama anggota\n2. Dilarang spam\n3. Patuhi arahan admin.');
  } else if (buttonId === 'admin') {
    await reply('👮 Notifikasi telah diteruskan kepada jajaran admin grup.');
  }
});

7. Pengelolaan Prefix di Grup

Agar bot tidak merespon setiap obrolan santai anggota grup, gunakan filter prefix yang disediakan oleh CreativeMessage:

client.on('message', async (msg) => {
  if (msg.fromMe) return;

  // Jika pesan di grup tidak memiliki prefix (! / . / # / $) dan bukan respon tombol, abaikan
  const hasPrefix = Boolean(msg.prefix);
  if (msg.isGroup && !hasPrefix && !msg.buttonResponse) {
    return;
  }

  // Proses perintah resmi
  if (msg.command === 'menu') {
    await msg.reply('📋 Menampilkan menu perintah grup...');
  }
});

8. Membuat Grup Baru (client.groupCreate)

Membuat grup WhatsApp baru dengan subjek dan daftar peserta awal:

const newGroup = await client.groupCreate('Komunitas Pengembang Node.js', [
  '[email protected]',
  '[email protected]',
]);
console.log('Grup berhasil dibuat dengan ID:', newGroup.id);

9. Mengelola Anggota Grup (client.groupParticipantsUpdate)

Menambah, menghapus, atau mengubah hak akses admin peserta grup:

const targetUser = '[email protected]';

// Menambahkan peserta baru
await client.groupParticipantsUpdate(groupJid, [targetUser], 'add');

// Menaikkan peserta menjadi Admin (Promote)
await client.groupParticipantsUpdate(groupJid, [targetUser], 'promote');

// Menurunkan Admin menjadi anggota biasa (Demote)
await client.groupParticipantsUpdate(groupJid, [targetUser], 'demote');

// Mengeluarkan peserta dari grup (Remove / Kick)
await client.groupParticipantsUpdate(groupJid, [targetUser], 'remove');

10. Mengubah Nama & Deskripsi Grup

// Mengubah nama/subjek grup
await client.groupUpdateSubject(groupJid, 'Komunitas Merajah Sockets Indonesia');

// Mengubah atau menghapus deskripsi grup
await client.groupUpdateDescription(groupJid, 'Grup resmi diskusi implementasi WhatsApp Web Multi-Device protokol native WebSocket.');

11. Pengaturan Izin & Pengumuman Grup (client.groupSettingUpdate)

Mengatur izin kirim pesan (hanya admin atau semua peserta) serta izin edit info grup:

// Mode Hanya Admin yang Boleh Mengirim Pesan (Tutup Grup)
await client.groupSettingUpdate(groupJid, 'announcement');

// Buka kembali grup agar semua anggota bisa mengirim pesan
await client.groupSettingUpdate(groupJid, 'not_announcement');

// Hanya Admin yang Boleh Mengedit Info Grup (Subjek, Foto, Deskripsi)
await client.groupSettingUpdate(groupJid, 'locked');

// Semua Anggota Boleh Mengedit Info Grup
await client.groupSettingUpdate(groupJid, 'unlocked');

12. Pesan Sementara Grup (client.groupToggleEphemeral)

Mengatur masa kadaluwarsa pesan menghilang otomatis (disappearing messages):

// Aktifkan pesan sementara: 24 jam (86400 detik), 7 hari (604800 detik), atau 90 hari (7776000 detik)
await client.groupToggleEphemeral(groupJid, 86400);

// Nonaktifkan pesan sementara (0 detik)
await client.groupToggleEphemeral(groupJid, 0);

13. Manajemen Tautan Undangan Grup

// Mengambil kode tautan undangan grup
const code = await client.groupInviteCode(groupJid);
console.log('Tautan Undangan:', `https://chat.whatsapp.com/${code}`);

// Mereset / mencabut kode undangan grup lama
const newCode = await client.groupRevokeInvite(groupJid);
console.log('Kode Undangan Baru:', newCode);

// Mengambil informasi grup dari kode undangan tanpa harus bergabung terlebih dahulu
const inviteInfo = await client.groupGetInviteInfo('KcFAwyGTo8RBhPv03LvoHZ');
console.log(`Grup: ${inviteInfo.subject}, Jumlah Anggota: ${inviteInfo.size}`);

// Bergabung ke grup menggunakan kode undangan
const joinedGroupJid = await client.groupAcceptInvite('KcFAwyGTo8RBhPv03LvoHZ');
console.log('Berhasil bergabung ke grup:', joinedGroupJid);

14. Keluar dari Grup (client.groupLeave)

await client.groupLeave(groupJid);
console.log('Bot telah keluar dari grup.');

📢 Saluran WhatsApp (Newsletter / Channels)

Merajah Sockets mendukung manajemen Saluran WhatsApp (WhatsApp Channels) secara penuh:

// 1. Membuat Saluran Baru
const channel = await client.newsletterCreate('Komunitas Merajah Sockets', 'Saluran informasi dan pembaruan');
console.log('Saluran dibuat:', channel);

// 2. Mengambil Metadata Saluran
const meta = await client.newsletterMetadata('123456789012345678@newsletter');
console.log('Nama Saluran:', meta);

// 3. Mengikuti (Follow) & Berhenti Mengikuti (Unfollow)
await client.newsletterFollow('123456789012345678@newsletter');
await client.newsletterUnfollow('123456789012345678@newsletter');

// 4. Membisukan (Mute) & Bunyikan (Unmute)
await client.newsletterMute('123456789012345678@newsletter');
await client.newsletterUnmute('123456789012345678@newsletter');

// 5. Mengubah Nama & Deskripsi Saluran
await client.newsletterUpdateName('123456789012345678@newsletter', 'Nama Saluran Baru');
await client.newsletterUpdateDescription('123456789012345678@newsletter', 'Deskripsi terbaru');

📞 Pengendalian Panggilan Masuk (VoIP Calls)

Tangani panggilan suara dan video secara otomatis:

// Mendengarkan event panggilan masuk
client.on('call', async (calls) => {
  for (const call of calls) {
    console.log(`[Panggilan] Dari: ${call.from}, Tipe: ${call.isVideo ? 'Video' : 'Suara'}, Status: ${call.status}`);

    // Tolak panggilan otomatis jika status = 'offer'
    if (call.status === 'offer') {
      await client.rejectCall(call.id, call.from);
      await client.sendText(call.from, '⚠️ Maaf, bot tidak menerima panggilan suara/video.');
    }
  }
});

🔐 Pengaturan Privasi & Pemblokiran Akun

Atur privasi akun WhatsApp secara terprogram:

// 1. Privasi Terakhir Dilihat (Last Seen)
// Nilai: 'all' | 'contacts' | 'contact_blacklist' | 'none'
await client.updateLastSeenPrivacy('contacts');

// 2. Privasi Status Online
// Nilai: 'all' | 'match_last_seen'
await client.updateOnlinePrivacy('match_last_seen');

// 3. Privasi Foto Profil
await client.updateProfilePicturePrivacy('contacts');

// 4. Privasi Laporan Dibaca (Read Receipts / Centang Biru)
// Nilai: 'all' | 'none'
await client.updateReadReceiptsPrivacy('all');

// 5. Blokir & Buka Blokir Pengguna
await client.updateBlockStatus('[email protected]', 'block');
await client.updateBlockStatus('[email protected]', 'unblock');

// 6. Mendapatkan Daftar Blokir
const blockedList = await client.fetchBlocklist();
console.log('Daftar JID yang diblokir:', blockedList);

👤 Profil & Status Bio

// 1. Mengubah Foto Profil (Avatar)
await client.updateProfilePicture(client.user.id, fs.readFileSync('./avatar.jpg'));

// 2. Menghapus Foto Profil
await client.removeProfilePicture(client.user.id);

// 3. Mengubah Status Bio (About)
await client.updateProfileStatus('⚡ Powered by Merajah Sockets');

// 4. Mengambil Status Bio Pengguna Lain
const bio = await client.fetchStatus('[email protected]');
console.log('Status Bio:', bio?.status, 'Diperbarui pada:', bio?.setAt);

🌐 Utiliti Versi WhatsApp Web Dinamis

Gunakan versi web dinamis agar selalu cocok dengan pelayan WhatsApp terbaru:

const { fetchLatestWaWebVersion, createMerajahClient, useFileSession } = require('merajah-sockets');

async function start() {
  const { version, isLatest } = await fetchLatestWaWebVersion();
  console.log(`Menggunakan WA Web Version: ${version.join('.')} (Terkini: ${isLatest})`);

  const session = await useFileSession('./auth_info');
  const client = createMerajahClient({
    session,
    version,
    printQR: true,
  });
}

🧪 Uji Coba & Protokol

Merajah Sockets dilengkapi dengan rangkaian uji protokol mandiri:

# Menjalankan seluruh test suite
npm test

Hasil verifikasi 12 test suite:

  • ✓ Protocol Framing (FrameCodec)
  • ✓ Binary Node Codec & Fixtures
  • ✓ Transport Layer & State Machine
  • ✓ Auth State & Session Restoration
  • ✓ JID Utilities & Normalization
  • ✓ Media Cryptography (AES-CBC-256 & HMAC)
  • ✓ Messaging Pipeline & Real ACK Generation
  • ✓ Signal Protocol E2EE Cipher Bridge
  • ✓ Privacy & Credential Redaction
  • ✓ Native InMemoryStore & Message Cache
  • ✓ Outbound Message Builders (Delete, Edit, ViewOnce)
  • ✓ High-Level MerajahClient & CreativeMessage API
  • ✅ ALL PROTOCOL & UNIT TESTS PASSED SUCCESSFULLY!

📄 Lisensi

Didistribusikan di bawah lisensi MIT. Bebas dikembangkan, dimodifikasi, dan digunakan untuk proyek komersial maupun personal.