New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more β†’
Get Started

astrabail

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

astrabail

Professional Whatsapp Web API Library

latest
Source
npmnpm
Version
1.2.4
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
Β 
Created
Source

AstraBail

Professional WhatsApp Web API Library β€” Built for the Next Generation of Bots
Fork-optimized from Baileys Β· Powered by Astra-Libsignal Β· Maintained by Danimaru-ze

npm version downloads license github WhatsApp Channel Astra Libsignal

✨ Tentang AstraBail (Klik untuk membuka)

✨ Tentang AstraBail

AstraBail adalah library WhatsApp Web API profesional yang dioptimalkan dari basis Baileys. Dikelola oleh Danimaru-ze, library ini dirancang untuk performa tinggi, stabilitas maksimal, dan kemudahan integrasi ke berbagai proyek bot WhatsApp modern. Kini ditenagai penuh oleh astra-libsignal untuk proses enkripsi tingkat tinggi.

πŸš€ Fitur Unggulan

FiturKeterangan
πŸ”€ Multi-DeviceDukungan penuh sistem multi-device WhatsApp terbaru
πŸ” End-to-End EncryptionEnkripsi pesan standar industri
⚑ Turbo HandshakeInisialisasi koneksi yang sangat cepat
🧠 Memory OptimizedGarbage Collector Otomatis (Anti-RAM Bocor)
πŸ’Ύ Anti-Crash I/ORead-Cache & Write-Debounce pencegah file sesi rusak (Corrupt)
πŸš€ Buffer UploadUpload media via in-memory buffer (Anti Timeout)
πŸ“¦ Modern FeaturesDukungan Penuh Saluran (Newsletter), Chat AI, dan ChatLock
🌟 Semua Jenis PesanTeks, Media, Button, Poll, Sticker, dan lainnya
πŸ†” LID & JID MappingPemetaan @lid dan @jid lengkap
πŸ› οΈ Event StreamHook custom dengan astrabail-event-stream
πŸ“¦ Instalasi (Klik untuk membuka)

πŸ“¦ Instalasi

npm install astrabail
# atau
yarn add astrabail
⚑ Mulai Cepat (Klik untuk membuka)

⚑ Mulai Cepat

Instalasi & Koneksi Dasar

const { default: makeWASocket, useMultiFileAuthState } = require("astrabail")

async function startBot() {
  const { state, saveCreds } = await useMultiFileAuthState('./session')

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

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

  sock.ev.on('connection.update', ({ connection }) => {
    if (connection === 'open') console.log('βœ… AstraBail: Bot Connected!')
  })
}

startBot()
Kirim Sticker Pack (Klik untuk membuka)

Kirim Sticker Pack

await sock.sendMessage('6281234567890@s.whatsapp.net', {
  stickerPack: {
    name: 'AstraBail Sticker',
    publisher: 'AstraBail',
    description: 'Sticker pack custom dari AstraBail',
    cover: { url: './assets/cover.png' },
    stickers: [
      { sticker: { url: './assets/sticker-1.webp' }, emojis: ['πŸ˜€'] },
      { sticker: { url: './assets/sticker-2.webp' }, emojis: ['πŸ”₯'] },
      { sticker: { url: './assets/sticker-3.png' }, emojis: ['πŸš€'] }
    ]
  }
})

Format object stickerPack

  • name: nama pack sticker
  • publisher: nama author/publisher
  • description: deskripsi pack
  • cover: cover/tray icon, bisa file path, buffer, stream, atau URL
  • stickers: array isi sticker, minimal 1 maksimal 60
  • sticker: media tiap sticker, bisa WebP/PNG/JPG. Jika bukan WebP akan dicoba dikonversi otomatis
  • emojis: emoji untuk sticker terkait
  • accessibilityLabel: label opsional
  • packId: opsional, jika tidak diisi akan dibuat otomatis

Catatan

  • Sangat disarankan install sharp agar konversi gambar ke WebP lebih stabil.
  • Jika sharp tidak ada, library akan mencoba memakai jimp.
  • Ukuran tiap sticker maksimal 1 MB.
  • Jumlah sticker maksimal 60 per pack.

Documentation

πŸ”— Menghubungkan Akun (Klik untuk membuka)

πŸ”— Menghubungkan Akun

WhatsApp provides a multi-device API that allows AstraBail to be authenticated as a second WhatsApp client by scanning a QR code or Pairing Code with WhatsApp on your phone.

πŸ”Ή Memulai Socket dengan Kode QR

[!TIP] You can customize browser name if you connect with QR-CODE, with Browser constant, we have some browsers config, see here

const { default: makeWASocket } = require("astrabail")


const sock = makeWASocket({
    // can provide additional config here
    browser: Browsers.ubuntu('My App'),
    printQRInTerminal: true
})

If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!

πŸ”Ή Memulai Socket dengan Kode Pairing

[!IMPORTANT] Pairing Code isn't Mobile API, it's a method to connect Whatsapp Web without QR-CODE, you can connect only with one device, see here

The phone number can't have + or () or -, only numbers, you must provide country code

const { default: makeWASocket } = require("astrabail")

const sock = makeWASocket({
    // can provide additional config here
    printQRInTerminal: false //need to be false
})

- Normal Pairing
if (!sock.authState.creds.registered) {
    const number = 'XXXXXXXXXXX'
    const code = await sock.requestPairingCode(number)
    console.log(code)
}

- Costum Pairing
if (!sock.authState.creds.registered) {
    const pair = "12345678" // only 8 digit numbers or letters (no more or less)
    const number = 'XXXXXXXXXXX'
    const code = await sock.requestPairingCode(number, pair)
    console.log(code)
}

πŸ”Ή Menerima Riwayat Penuh

  • Set syncFullHistory as true
  • AstraBail, by default, use chrome browser config
    • If you'd like to emulate a desktop connection (and receive more message history), this browser setting to your Socket config:
const sock = makeWASocket({
    ...otherOpts,
    // can use Windows, Ubuntu here too
    browser: Browsers.macOS('Desktop'),
    syncFullHistory: true
})

βš™οΈ Catatan Penting tentang Konfigurasi Socket (Klik untuk membuka)

βš™οΈ Catatan Penting tentang Konfigurasi Socket

🧠 Caching Metadata Grup (Direkomendasikan)

  • If you use AstraBail for groups, we recommend you to set cachedGroupMetadata in socket config, you need to implement a cache like this:

    const groupCache = new NodeCache({stdTTL: 5 * 60, useClones: false})
    
    const sock = makeWASocket({
        cachedGroupMetadata: async (jid) => groupCache.get(jid)
    })
    
    sock.ev.on('groups.update', async ([event]) => {
        const metadata = await sock.groupMetadata(event.id)
        groupCache.set(event.id, metadata)
    })
    
    sock.ev.on('group-participants.update', async (event) => {
        const metadata = await sock.groupMetadata(event.id)
        groupCache.set(event.id, metadata)
    })
    

πŸ” Perbaiki Sistem Retry & Dekripsi Polling

  • If you want to improve sending message, retrying when error occurs and decrypt poll votes, you need to have a store and set getMessage config in socket like this:
    const sock = makeWASocket({
        getMessage: async (key) => await getMessageFromStore(key)
    })
    

πŸ”” Menerima Notifikasi di Aplikasi WhatsApp

  • If you want to receive notifications in whatsapp app, set markOnlineOnConnect to false
    const sock = makeWASocket({
        markOnlineOnConnect: false
    })
    

πŸ“¦ Menyimpan & Mengembalikan Sesi (Klik untuk membuka)

πŸ“¦ Menyimpan & Mengembalikan Sesi

You obviously don't want to keep scanning the QR code every time you want to connect.

So, you can load the credentials to log back in:

const makeWASocket = require("astrabail").default;
const { useMultiFileAuthState } = require("astrabail");

const { state, saveCreds } = await useMultiFileAuthState('auth_info_AstraBail')

// will use the given state to connect
// so if valid credentials are available -- it'll connect without QR
const sock = makeWASocket({ auth: state })

// this will be called as soon as the credentials are updated
sock.ev.on('creds.update', saveCreds)

[!IMPORTANT] useMultiFileAuthState is a utility function to help save the auth state in a single folder, this function serves as a good guide to help write auth & key states for SQL/no-SQL databases, which I would recommend in any production grade system.

[!NOTE] When a message is received/sent, due to signal sessions needing updating, the auth keys (authState.keys) will update. Whenever that happens, you must save the updated keys (authState.keys.set() is called). Not doing so will prevent your messages from reaching the recipient & cause other unexpected consequences. The useMultiFileAuthState function automatically takes care of that, but for any other serious implementation -- you will need to be very careful with the key state management.

πŸ“‘ Penanganan Event (Klik untuk membuka)

πŸ“‘ Penanganan Event

  • AstraBail uses the EventEmitter syntax for events. They're all nicely typed up, so you shouldn't have any issues with an Intellisense editor like VS Code.

[!IMPORTANT] The events are these, it's important you see all events

You can listen to these events like this:

const sock = makeWASocket()
sock.ev.on('messages.upsert', ({ messages }) => {
    console.log('got messages', messages)
})

πŸ› οΈ Contoh Awal

[!NOTE] This example includes basic auth storage too

const makeWASocket = require("astrabail").default;
const { DisconnectReason, useMultiFileAuthState } = require("astrabail");
const Boom = require('@hapi/boom');

async function connectToWhatsApp () {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_AstraBail')
    const sock = makeWASocket({
        // can provide additional config here
        auth: state,
        printQRInTerminal: true
    })
    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update
        if(connection === 'close') {
            const shouldReconnect = (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut
            console.log('connection closed due to ', lastDisconnect.error, ', reconnecting ', shouldReconnect)
            // reconnect if not logged out
            if(shouldReconnect) {
                connectToWhatsApp()
            }
        } else if(connection === 'open') {
            console.log('opened connection')
        }
    })
    sock.ev.on('messages.upsert', event => {
        for (const m of event.messages) {
            console.log(JSON.stringify(m, undefined, 2))

            console.log('replying to', m.key.remoteJid)
            await sock.sendMessage(m.key.remoteJid!, { text: 'Hello Word' })
        }
    })

    // to storage creds (session info) when it updates
    sock.ev.on('creds.update', saveCreds)
}
// run in main file
connectToWhatsApp()

[!IMPORTANT] In messages.upsert it's recommended to use a loop like for (const message of event.messages) to handle all messages in array

πŸ” Dekripsi Pilihan Polling

  • By default poll votes are encrypted and handled in messages.update
  • That's a simple example
sock.ev.on('messages.update', event => {
    for(const { key, update } of event) {
        if(update.pollUpdates) {
            const pollCreation = await getMessage(key)
            if(pollCreation) {
                console.log(
                    'got poll update, aggregation: ',
                    getAggregateVotesInPollMessage({
                        message: pollCreation,
                        pollUpdates: update.pollUpdates,
                    })
                )
            }
        }
    }
})
  • getMessage is a store implementation (in your end)

πŸ”Ž Ringkasan Event pada Koneksi Pertama

  • When you connect first time, connection.update will be fired requesting you to restart sock
  • Then, history messages will be received in messaging.history-set

πŸ’Ύ Mengimplementasikan Data Store (Klik untuk membuka)

πŸ’Ύ Mengimplementasikan Data Store

  • AstraBail does not come with a defacto storage for chats, contacts, or messages. However, a simple in-memory implementation has been provided. The store listens for chat updates, new messages, message updates, etc., to always have an up-to-date version of the data.

[!IMPORTANT] I highly recommend building your own data store, as storing someone's entire chat history in memory is a terrible waste of RAM.

It can be used as follows:

const makeWASocket = require("astrabail").default;
const { makeInMemoryStore } = require("astrabail");
// the store maintains the data of the WA connection in memory
// can be written out to a file & read from it
const store = makeInMemoryStore({ })
// can be read from a file
store.readFromFile('./AstraBail_store.json')
// saves the state to a file every 10s
setInterval(() => {
    store.writeToFile('./AstraBail_store.json')
}, 10_000)

const sock = makeWASocket({ })
// will listen from this socket
// the store can listen from a new socket once the current socket outlives its lifetime
store.bind(sock.ev)

sock.ev.on('chats.upsert', () => {
    // can use 'store.chats' however you want, even after the socket dies out
    // 'chats' => a KeyedDB instance
    console.log('got chats', store.chats.all())
})

sock.ev.on('contacts.upsert', () => {
    console.log('got contacts', Object.values(store.contacts))
})

The store also provides some simple functions such as loadMessages that utilize the store to speed up data retrieval.

πŸ†” Penjelasan ID WhatsApp (Klik untuk membuka)

πŸ†” Penjelasan ID WhatsApp

  • id is the WhatsApp ID, called jid too, of the person or group you're sending the message to.
    • It must be in the format [country code][phone number]@s.whatsapp.net - Example for people: +19999999999@s.whatsapp.net. - For groups, it must be in the format 123456789-123345@g.us.
    • For broadcast lists, it's [timestamp of creation]@broadcast.
    • For stories, the ID is status@broadcast.

🧰 Fungsi Utilitas (Klik untuk membuka)

🧰 Fungsi Utilitas

  • getContentType, returns the content type for any message
  • getDevice, returns the device from message
  • makeCacheableSignalKeyStore, make auth store more fast
  • downloadContentFromMessage, download content from any message

πŸ’« Status Mention Grup (upswgc) (Klik untuk membuka)

πŸ’« Status Mention Grup (upswgc)

Digunakan untuk mengirim status WhatsApp (story) Grup baik berupa teks maupun media ke status@broadcast.

[!NOTE] Fungsi ini sudah otomatis mendeteksi tipe konten (teks, gambar, video, audio). Jika teks dan media digabung, hanya satu yang akan dikirim tergantung prioritas konten.

Kirim teks ke status WhatsApp

await conn.upswgc(m.chat, { text: "Halo dari upswgc versi final!" })

Kirim gambar ke status WhatsApp

await conn.upswgc(m.chat, {
        image: { url: "https://m.media-amazon.com/images/S/pv-target-images/f75db71efd62ea2eff81bd7cc01c44a9344b4ac18615ab71a80f58459ddf8791.jpg" },
        caption : "apasi"
Β Β Β Β Β Β });

Kirim video ke status WhatsApp

await conn.upswgc(m.chat, {
        video: { url: "https://m.media-amazon.com/images/S/pv-target-images/f75db71efd62ea2eff81bd7cc01c44a9344b4ac18615ab71a80f58459ddf8791.jpg" },
        caption : "apasi"
Β Β Β Β Β Β });

Kirim audio (voice note) ke status WhatsApp

await conn.upswgc(m.chat, {
        audio: { url: "https://raw.githubusercontent.com/Danimaru-ze/AstraBail/main/assets/sample.opus" },
        caption : "apasi"
Β Β Β Β Β Β });

[!TIP] Jika kamu ingin mengirim ke story + broadcast sekaligus, gunakan fungsi sendMessage() dengan broadcast: true seperti dijelaskan pada bagian sebelumnya.

πŸ’¬ Mengirim Pesan (Klik untuk membuka)

πŸ’¬ Mengirim Pesan

  • Send all types of messages with a single function

    • Here you can see all message contents supported, like text message
    • Here you can see all options supported, like quote message
    const jid: string
    const content: AnyMessageContent
    const options: MiscMessageGenerationOptions
    
    sock.sendMessage(jid, content, options)
    

βœ‰οΈ Pesan Non-Media

πŸ”˜ Pesan Tombol (Buttons)

// send a buttons message!
sock.sendMessage(jid, {
     text: "Hello World !",
     footer: "AstraBail - 2026",
     buttons: [
     {
     buttonId: `πŸš€`, 
     buttonText: {
     displayText: 'πŸ—Ώ'
     },
     type: 1 
     }
     ],
     headerType: 1,
     viewOnce: true
 },{ quoted: null })

πŸ” Alur Tombol

sock.sendMessage(jid, {
  text: "Hello Wolrd !;", 
  footer: "Β© Danimaru-ze Dev",
  buttons: [
  {
    buttonId: '.tes',
    buttonText: {
      displayText: 'TESTING BOT'
    },
    type: 1,
  },
  {
    buttonId: ' ',
    buttonText: {
      displayText: 'PRIVATE SCRIPT'
    },
    type: 1,
  },
  {
    buttonId: 'action',
    buttonText: {
      displayText: 'ini pesan interactiveMeta'
    },
    type: 4,
    nativeFlowInfo: {
      name: 'single_select',
      paramsJson: JSON.stringify({
        title: 'message',
        sections: [
          {
            title: 'AstraBail - 2026',
            highlight_label: '😜',
            rows: [
              {
                header: 'HEADER',
                title: 'TITLE',
                description: 'DESCRIPTION',
                id: 'YOUR ID',
              },
              {
                header: 'HEADER',
                title: 'TITLE',
                description: 'DESCRIPTION',
                id: 'YOUR ID',
              },
            ],
          },
        ],
      }),
    },
  },
  ],
  headerType: 1,
  viewOnce: true
}, { quoted: m });

🧩 Pesan Interaktif

let msg = generateWAMessageFromContent(m.chat, {
 viewOnceMessage: {
   message: {
       "messageContextInfo": {
         "deviceListMetadata": {},
         "deviceListMetadataVersion": 2
       },
       interactiveMessage: proto.Message.InteractiveMessage.create({
         body: proto.Message.InteractiveMessage.Body.create({
           text: "Danimaru-ze"
         }),
         footer: proto.Message.InteractiveMessage.Footer.create({
           text: "Bot"
         }),
         header: proto.Message.InteractiveMessage.Header.create({
           title: "Igna",
           subtitle: "test",
           hasMediaAttachment: false
         }),
         nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.create({
           buttons: [
             {
               "name": "single_select",
               "buttonParamsJson": "{\"title\":\"title\",\"sections\":[{\".menu\":\".play dj webito\",\"highlight_label\":\"label\",\"rows\":[{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"},{\"header\":\"header\",\"title\":\"title\",\"description\":\"description\",\"id\":\"id\"}]}]}"
             },
             {
               "name": "cta_reply",
               "buttonParamsJson": "{\"display_text\":\"quick_reply\",\"id\":\"message\"}"
             },
             {
                "name": "cta_url",
                "buttonParamsJson": "{\"display_text\":\"url\",\"url\":\"https://www.google.com\",\"merchant_url\":\"https://www.google.com\"}"
             },
             {
                "name": "cta_call",
                "buttonParamsJson": "{\"display_text\":\"call\",\"id\":\"message\"}"
             },
             {
                "name": "cta_copy",
                "buttonParamsJson": "{\"display_text\":\"copy\",\"id\":\"123456789\",\"copy_code\":\"message\"}"
             },
             {
                "name": "cta_reminder",
                "buttonParamsJson": "{\"display_text\":\"Recordatorio\",\"id\":\"message\"}"
             },
             {
                "name": "cta_cancel_reminder",
                "buttonParamsJson": "{\"display_text\":\"cta_cancel_reminder\",\"id\":\"message\"}"
             },
             {
                "name": "address_message",
                "buttonParamsJson": "{\"display_text\":\"address_message\",\"id\":\"message\"}"
             },
             {
                "name": "send_location",
                "buttonParamsJson": ""
             }
          ],
         })
       })
   }
 }
}, {})

return sock.relayMessage(msg.key.remoteJid, msg.message, { messageId: msg.key.id })

πŸ“ Pesan Teks

await sock.sendMessage(jid, { text: 'hello word' })

❝ Pesan Quote (bekerja untuk semua tipe)

await sock.sendMessage(jid, { text: 'hello word' }, { quoted: message })

🏷️ Mention Pengguna (bekerja di kebanyakan tipe)

  • @number is to mention in text, it's optional
await sock.sendMessage(
    jid,
    {
        text: '@12345678901',
        mentions: ['12345678901@s.whatsapp.net']
    }
)

πŸ“£ Mention Status

  • [ jid ] If the Jid Group and Jid Private Chat are included in the JID list, try to make the JID group first starting from the Jid Private Chat or Jid Private Chat in the middle between the group Jid
await sock.StatusMentions(
     {
        text: "Hello", // or image / video / audio ( url or buffer )
     },
     [
      "123456789123456789@g.us",
      "123456789@s.whatsapp.net",
      // Enter jid chat here
     ] 
)  

πŸ“Š Hasil Poll dari Newsletter

await client.sendMessage(
    jid,
    {
        pollResult: {
            name: "Text poll",
            votes: [["Options 1", 10], ["Options 2", 10]], // 10 For Fake Polling Count Results
        }
    }, { quoted : message }
)

πŸ–ΌοΈ Mengirim Pesan Album

  • url or buffer ( image or video )
await sock.sendAlbumMessage(
    jid,
    [
       {
          image: { url: "https://example.jpg" }, // or buffer
          caption: "Hello World",
       },
       {
          video: { url: "https://example.mp4" }, // or buffer
          caption: "Hello World",
       },
    ],
    { 
       quoted : message, 
       delay : 2000 // number in seconds
    }
)

πŸ”” Respon Interaktif

await client.sendMessage(
    jid, 
    {
        buttonReply: {
             text: 'Text',
             nativeFlow: { 
                version: 3,
             },
        },
        type: 'interactive',
        ephemeral: true,
    }
)

πŸ’³ Permintaan Pembayaran

- Example non media sticker
await client.sendMessage(
    jid,
    {
        requestPayment: {      
           currency: "IDR",
           amount: "10000000",
           from: "123456@s.whatsapp.net",
           note: "Hai Guys",
           background: { ...background of the message }
        }
    },
    { quoted : message }
)

- with media sticker buffer
await client.sendMessage(
    jid,
    {
        requestPayment: {      
           currency: "IDR",
           amount: "10000000",
           from: "123456@s.whatsapp.net",
           sticker: Buffer,
           background: { ...background of the message }
        }
    },
    { quoted : message }
)

- with media sticker url
await client.sendMessage(
    jid,
    {
        requestPayment: {      
           currency: "IDR",
           amount: "10000000",
           from: "123456@s.whatsapp.net",
           sticker: { url: Sticker Url },
           background: { ...background of the message }
        }
    },
    { quoted : message }
)

πŸ“† Pesan Event

await client.sendMessage(
   jid, 
   { 
       event: {
           isCanceled: false, // or true for cancel event 
           name: "Name Event", 
           description: "Description Event",
           location: { 
               degressLatitude: -0, 
               degressLongitude: - 0 
           },
           link: Call Link,
           startTime: m.messageTimestamp.low,
           endTime: m.messageTimestamp.low + 86400, // 86400 is day in seconds
           extraGuestsAllowed: true // or false
       }
   },
   { quoted : message }
)

πŸ”— Interaktif

- Example non header media
await client.sendMessage(
    jid,
    {
        text: "Description Of Messages", //Additional information
        title: "Title Of Messages",
        subtitle: "Subtitle Message",
        footer: "Footer Messages",
        interactiveButtons: [
             {
                name: "quick_reply",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     id: "ID"
                })
             },
             {
                name: "cta_url",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     url: "https://www.example.com"
                })
             }
        ]
    },
  {
    quoted : message
  }
)

- Example with media
await client.sendMessage(
    jid,
    {
        image: { url : "https://example.jpg" }, // Can buffer
        caption: "Description Of Messages", //Additional information
        title: "Title Of Messages",
        subtitle: "Subtile Message",
        footer: "Footer Messages",
        media: true,
        interactiveButtons: [
             {
                name: "quick_reply",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     id: "ID"
                })
             },
             {
                name: "cta_url",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     url: "https://www.example.com"
                })
             }
        ]
    },
  {
    quoted : message
  }
)

- Example with header product
await client.sendMessage(
    jid,
    {
        product: {
            productImage: { url: "https://example.jpg }, //or buffer
            productImageCount: 1,
            title: "Title Product",
            description: "Description Product",
            priceAmount1000: 20000 * 1000,
            currencyCode: "IDR",
            retailerId: "Retail",
            url: "https://example.com",            
        },
        businessOwnerJid: "1234@s.whatsapp.net",
        caption: "Description Of Messages", //Additional information
        title: "Title Of Messages",
        footer: "Footer Messages",
        media: true,
        interactiveButtons: [
             {
                name: "quick_reply",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     id: "ID"
                })
             },
             {
                name: "cta_url",
                buttonParamsJson: JSON.stringify({
                     display_text: "Display Button",
                     url: "https://www.example.com"
                })
             }
        ]
    },
  {
    quoted : message
  }
)

πŸ“€ Meneruskan Pesan

  • You need to have message object, can be retrieved from store or use a message object
const msg = getMessageFromStore() // implement this on your end
await sock.sendMessage(jid, { forward: msg }) // WA forward the message!

πŸ“ Pesan Lokasi

await sock.sendMessage(
    jid,
    {
        location: {
            degreesLatitude: 24.121231,
            degreesLongitude: 55.1121221
        }
    }
)

πŸ‘€ Pesan Kontak

const vcard = 'BEGIN:VCARD\n' // metadata of the contact card
            + 'VERSION:3.0\n'
            + 'FN:Jeff Singh\n' // full name
            + 'ORG:Ashoka Uni;\n' // the organization of the contact
            + 'TEL;type=CELL;type=VOICE;waid=911234567890:+91 12345 67890\n' // WhatsApp ID + phone number
            + 'END:VCARD'

await sock.sendMessage(
    id,
    {
        contacts: {
            displayName: 'Jeff',
            contacts: [{ vcard }]
        }
    }
)

❀️ Pesan Reaksi

  • You need to pass the key of message, you can retrieve from store or use a key object
await sock.sendMessage(
    jid,
    {
        react: {
            text: 'πŸ’–', // use an empty string to remove the reaction
            key: message.key
        }
    }
)

πŸ“Œ Pesan Pin

  • You need to pass the key of message, you can retrieve from store or use a key object

  • Time can be:

TimeSeconds
24h86.400
7d604.800
30d2.592.000
await sock.sendMessage(
    jid,
    {
        pin: {
            type: 1, // 0 to remove
            time: 86400
            key: message.key
        }
    }
)

πŸ—³οΈ Pesan Poll

await sock.sendMessage(
    jid,
    {
        poll: {
            name: 'My Poll',
            values: ['Option 1', 'Option 2', ...],
            selectableCount: 1,
            toAnnouncementGroup: false // or true
        }
    }
)

  • By default, wa does not have link generation when sent from the web
  • AstraBail has a function to generate the content for these link previews
  • To enable this function's usage, add link-preview-js as a dependency to your project with yarn add link-preview-js
  • Send a link:
await sock.sendMessage(
    jid,
    {
        text: 'Hi, this was sent using https://github.com/whiskeysockets/AstraBail'
    }
)

🎞️ Pesan Media

Sending media (video, stickers, images) is easier & more efficient than ever.

[!NOTE] In media messages, you can pass { stream: Stream } or { url: Url } or Buffer directly, you can see more here

  • When specifying a media url, AstraBail never loads the entire buffer into memory; it even encrypts the media as a readable stream.

[!TIP] It's recommended to use Stream or Url to save memory

🎬 Pesan GIF

  • Whatsapp doesn't support .gif files, that's why we send gifs as common .mp4 video with gifPlayback flag
await sock.sendMessage(
    jid,
    {
        video: fs.readFileSync('Media/ma_gif.mp4'),
        caption: 'hello word',
        gifPlayback: true
    }
)

πŸ“Ή Pesan Video

await sock.sendMessage(
    id,
    {
        video: {
            url: './Media/ma_gif.mp4'
        },
        caption: 'hello word',
            ptv: false // if set to true, will send as a `video note`
    }
)

πŸ”Š Pesan Audio

  • To audio message work in all devices you need to convert with some tool like ffmpeg with this flags:
        codec: libopus //ogg file
        ac: 1 //one channel
        avoid_negative_ts
        make_zero
    
    • Example:
    ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
    
await sock.sendMessage(
    jid,
    {
        audio: {
            url: './Media/audio.mp3'
        },
        mimetype: 'audio/mp4'
    }
)

πŸ–ΌοΈ Pesan Gambar

await sock.sendMessage(
    id,
    {
        image: {
            url: './Media/ma_img.png'
        },
        caption: 'hello word'
    }
)

πŸ‘οΈ View Once (Sekali Lihat)

  • You can send all messages above as viewOnce, you only need to pass viewOnce: true in content object
await sock.sendMessage(
    id,
    {
        image: {
            url: './Media/ma_img.png'
        },
        viewOnce: true, //works with video, audio too
        caption: 'hello word'
    }
)

✏️ Memodifikasi Pesan (Klik untuk membuka)

✏️ Memodifikasi Pesan

πŸ—‘οΈ Menghapus Pesan (untuk semua)

const msg = await sock.sendMessage(jid, { text: 'hello word' })
await sock.sendMessage(jid, { delete: msg.key })

Note: deleting for oneself is supported via chatModify, see in this section

πŸ–ŠοΈ Mengedit Pesan

  • You can pass all editable contents here
await sock.sendMessage(jid, {
      text: 'updated text goes here',
      edit: response.key,
    });

πŸ› οΈ Manipulasi Pesan Media (Klik untuk membuka)

πŸ› οΈ Manipulasi Pesan Media

πŸ–ΌοΈ Thumbnail pada Pesan Media

  • For media messages, the thumbnail can be generated automatically for images & stickers provided you add jimp or sharp as a dependency in your project using yarn add jimp or yarn add sharp.
  • Thumbnails for videos can also be generated automatically, though, you need to have ffmpeg installed on your system.

⬇️ Mengunduh Pesan Media

If you want to save the media you received

const { createWriteStream } = require('fs');
const { downloadMediaMessage, getContentType } = require("astrabail");

sock.ev.on('messages.upsert', async ({ [m] }) => {
    if (!m.message) return // if there is no text or media message
    const messageType = getContentType(m) // get what type of message it is (text, image, video...)

    // if the message is an image
    if (messageType === 'imageMessage') {
        // download the message
        const stream = await downloadMediaMessage(
            m,
            'stream', // can be 'buffer' too
            { },
            {
                logger,
                // pass this so that AstraBail can request a reupload of media
                // that has been deleted
                reuploadRequest: sock.updateMediaMessage
            }
        )
        // save to file
        const writeStream = createWriteStream('./my-download.jpeg')
        stream.pipe(writeStream)
    }
}

πŸ” Mengunggah Ulang Pesan Media ke WhatsApp

  • WhatsApp automatically removes old media from their servers. For the device to access said media -- a re-upload is required by another device that has it. This can be accomplished using:
await sock.updateMediaMessage(msg)

🚫 Menolak Panggilan (Klik untuk membuka)

🚫 Menolak Panggilan

  • You can obtain callId and callFrom from call event
await sock.rejectCall(callId, callFrom)

πŸ“‘ Status Pengiriman di Chat (Klik untuk membuka)

πŸ“‘ Status Pengiriman di Chat

πŸ“– Membaca Pesan

  • A set of message keys must be explicitly marked read now.
  • You cannot mark an entire 'chat' read as it were with AstraBail Web. This means you have to keep track of unread messages.
const key: WAMessageKey
// can pass multiple keys to read multiple messages as well
await sock.readMessages([key])

The message ID is the unique identifier of the message that you are marking as read. On a WAMessage, the messageID can be accessed using messageID = message.key.id.

🟒 Memperbarui Presence

  • presence can be one of these
  • The presence expires after about 10 seconds.
  • This lets the person/group with jid know whether you're online, offline, typing etc.
await sock.sendPresenceUpdate('available', jid)

[!NOTE] If a desktop client is active, WA doesn't send push notifications to the device. If you would like to receive said notifications -- mark your AstraBail client offline using sock.sendPresenceUpdate('unavailable')

πŸ—‚οΈ Memodifikasi Chat (Klik untuk membuka)

πŸ—‚οΈ Memodifikasi Chat

WA uses an encrypted form of communication to send chat/app updates. This has been implemented mostly and you can send the following updates:

[!IMPORTANT] If you mess up one of your updates, WA can log you out of all your devices and you'll have to log in again.

πŸ—ƒοΈ Arsipkan Chat

const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)

πŸ”• Mute/Unmute Chat

  • Supported times:
TimeMiliseconds
Removenull
8h86.400.000
7d604.800.000
// mute for 8 hours
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
// unmute
await sock.chatModify({ mute: null }, jid)

βœ… Tandai Chat Baca/Belum Baca

const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
// mark it unread
await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)

🧹 Hapus Pesan untuk Saya

await sock.chatModify(
    {
        clear: {
            messages: [
                {
                    id: 'ATWYHDNNWU81732J',
                    fromMe: true,
                    timestamp: '1654823909'
                }
            ]
        }
    },
    jid
)

❌ Hapus Chat

const lastMsgInChat = await getLastMessageInChat(jid) // implement this on your end
await sock.chatModify({
        delete: true,
        lastMessages: [
            {
                key: lastMsgInChat.key,
                messageTimestamp: lastMsgInChat.messageTimestamp
            }
        ]
    },
    jid
)

πŸ“Œ Pin/Unpin Chat

await sock.chatModify({
        pin: true // or `false` to unpin
    },
    jid
)

⭐ Star/Unstar Pesan

await sock.chatModify({
        star: {
            messages: [
                {
                    id: 'messageID',
                    fromMe: true // or `false`
                }
            ],
            star: true // - true: Star Message; false: Unstar Message
        }
    },
    jid
)

πŸ•’ Pesan yang Menghilang (Disappearing)

  • Ephemeral can be:
TimeSeconds
Remove0
24h86.400
7d604.800
90d7.776.000
  • You need to pass in Seconds, default is 7 days
// turn on disappearing messages
await sock.sendMessage(
    jid,
    // this is 1 week in seconds -- how long you want messages to appear for
    { disappearingMessagesInChat: WA_DEFAULT_EPHEMERAL }
)

// will send as a disappearing message
await sock.sendMessage(jid, { text: 'hello' }, { ephemeralExpiration: WA_DEFAULT_EPHEMERAL })

// turn off disappearing messages
await sock.sendMessage(
    jid,
    { disappearingMessagesInChat: false }
)

πŸ”Ž Query Pengguna (Klik untuk membuka)

πŸ”Ž Query Pengguna

πŸ” Cek Jika ID Ada di WhatsApp

const [result] = await sock.onWhatsApp(jid)
if (result.exists) console.log (`${jid} exists on WhatsApp, as jid: ${result.jid}`)

πŸ“œ Query Riwayat Chat (termasuk grup)

  • You need to have oldest message in chat
const msg = await getOldestMessageInChat(jid)
await sock.fetchMessageHistory(
    50, //quantity (max: 50 per query)
    msg.key,
    msg.messageTimestamp
)
  • Messages will be received in messaging.history-set event

πŸ“£ Ambil Status

const status = await sock.fetchStatus(jid)
console.log('status: ' + status)

πŸ–ΌοΈ Ambil Foto Profil (termasuk grup)

  • To get the display picture of some person/group
// for low res picture
const ppUrl = await sock.profilePictureUrl(jid)
console.log(ppUrl)

// for high res picture
const ppUrl = await sock.profilePictureUrl(jid, 'image')

🏷️ Ambil Profil Bisnis (deskripsi/kategori)

const profile = await sock.getBusinessProfile(jid)
console.log('business description: ' + profile.description + ', category: ' + profile.category)

πŸ‘€ Ambil Presence Seseorang (sedang mengetik/online)

// the presence update is fetched and called here
sock.ev.on('presence.update', console.log)

// request updates for a chat
await sock.presenceSubscribe(jid)

πŸ§‘β€πŸ’Ό Mengubah Profil (Klik untuk membuka)

πŸ§‘β€πŸ’Ό Mengubah Profil

πŸ§‘β€πŸ’Ό Mengubah Profil Status

await sock.updateProfileStatus('Hello World!')

πŸ§‘β€πŸ’Ό Mengubah Profil Name

await sock.updateProfileName('My name')

πŸ–ΌοΈ Ubah Foto Profil (termasuk grup)

  • To change your display picture or a group's

[!NOTE] Like media messages, you can pass { stream: Stream } or { url: Url } or Buffer directly, you can see more here

await sock.updateProfilePicture(jid, { url: './new-profile-picture.jpeg' })

πŸ—‘οΈ Hapus Foto Profil (termasuk grup)

await sock.removeProfilePicture(jid)

πŸ‘₯ Grup (Klik untuk membuka)

πŸ‘₯ Grup

  • To change group properties you need to be admin

βž• Buat Grup

// title & participants
const group = await sock.groupCreate('My Fab Group', ['1234@s.whatsapp.net', '4564@s.whatsapp.net'])
console.log('created group with id: ' + group.gid)
await sock.sendMessage(group.id, { text: 'hello there' }) // say hello to everyone on the group

βž•/βž– Tambah/Hapus atau Turunkan/Naikkan Status

// id & people to add to the group (will throw error if it fails)
await sock.groupParticipantsUpdate(
    jid,
    ['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
    'add' // replace this parameter with 'remove' or 'demote' or 'promote'
)

✏️ Ubah Subjek (Nama)

await sock.groupUpdateSubject(jid, 'New Subject!')

πŸ“ Ubah Deskripsi

await sock.groupUpdateDescription(jid, 'New Description!')

βš™οΈ Ubah Pengaturan

// only allow admins to send messages
await sock.groupSettingUpdate(jid, 'announcement')
// allow everyone to send messages
await sock.groupSettingUpdate(jid, 'not_announcement')
// allow everyone to modify the group's settings -- like display picture etc.
await sock.groupSettingUpdate(jid, 'unlocked')
// only allow admins to modify the group's settings
await sock.groupSettingUpdate(jid, 'locked')

πŸšͺ Keluar Grup

// will throw error if it fails
await sock.groupLeave(jid)

πŸ” Dapatkan Kode Undangan

  • To create link with code use 'https://chat.whatsapp.com/' + code
const code = await sock.groupInviteCode(jid)
console.log('group code: ' + code)

πŸ”„ Cabut Kode Undangan

const code = await sock.groupRevokeInvite(jid)
console.log('New group code: ' + code)

➿ Bergabung Menggunakan Kode Undangan

  • Code can't have https://chat.whatsapp.com/, only code
const response = await sock.groupAcceptInvite(code)
console.log('joined to: ' + response)

ℹ️ Dapatkan Info Grup lewat Kode Undangan

const response = await sock.groupGetInviteInfo(code)
console.log('group information: ' + response)

πŸ”Ž Query Metadata (peserta, nama, deskripsi...)

const metadata = await sock.groupMetadata(jid)
console.log(metadata.id + ', title: ' + metadata.subject + ', description: ' + metadata.desc)

Join using groupInviteMessage

const response = await sock.groupAcceptInviteV4(jid, groupInviteMessage)
console.log('joined to: ' + response)

πŸ“₯ Dapatkan Daftar Permintaan Bergabung

const response = await sock.groupRequestParticipantsList(jid)
console.log(response)

βœ…/❌ Setuju/Tolak Permintaan Bergabung

const response = await sock.groupRequestParticipantsUpdate(
    jid, // group id
    ['abcd@s.whatsapp.net', 'efgh@s.whatsapp.net'],
    'approve' // or 'reject'
)
console.log(response)

πŸ“š Dapatkan Semua Metadata Grup yang Diikuti

const response = await sock.groupFetchAllParticipating()
console.log(response)

⏳ Toggle Ephemeral

  • Ephemeral can be:
TimeSeconds
Remove0
24h86.400
7d604.800
90d7.776.000
await sock.groupToggleEphemeral(jid, 86400)

πŸ” Ubah Mode Penambahan

await sock.groupMemberAddMode(
    jid,
    'all_member_add' // or 'admin_add'
)

πŸ”’ Privasi (Klik untuk membuka)

πŸ”’ Privasi

β›”/βœ… Blokir/Buka Blokir Pengguna

await sock.updateBlockStatus(jid, 'block') // Block user
await sock.updateBlockStatus(jid, 'unblock') // Unblock user

βš™οΈ Dapatkan Pengaturan Privasi

const privacySettings = await sock.fetchPrivacySettings(true)
console.log('privacy settings: ' + privacySettings)

πŸ“› Dapatkan Daftar Blokir

const response = await sock.fetchBlocklist()
console.log(response)

πŸ‘€ Update Privasi LastSeen

const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateLastSeenPrivacy(value)

🟒 Update Privasi Online

const value = 'all' // 'match_last_seen'
await sock.updateOnlinePrivacy(value)

πŸ–ΌοΈ Update Privasi Foto Profil

const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateProfilePicturePrivacy(value)

πŸ“£ Update Privasi Status

const value = 'all' // 'contacts' | 'contact_blacklist' | 'none'
await sock.updateStatusPrivacy(value)

βœ… Update Privasi Read Receipts

const value = 'all' // 'none'
await sock.updateReadReceiptsPrivacy(value)

πŸ‘₯ Update Privasi Tambah Grup

const value = 'all' // 'contacts' | 'contact_blacklist'
await sock.updateGroupsAddPrivacy(value)

πŸ•’ Update Mode Default Disappearing

  • Like this, ephemeral can be:
TimeSeconds
Remove0
24h86.400
7d604.800
90d7.776.000
const ephemeral = 86400
await sock.updateDefaultDisappearingMode(ephemeral)

πŸ“’ Broadcast & Story (Klik untuk membuka)

πŸ“’ Broadcast & Story

πŸ“€ Kirim Broadcast & Story

  • Messages can be sent to broadcasts & stories. You need to add the following message options in sendMessage, like this:
await sock.sendMessage(
    jid,
    {
        image: {
            url: url
        },
        caption: caption
    },
    {
        backgroundColor: backgroundColor,
        font: font,
        statusJidList: statusJidList,
        broadcast: true
    }
)
  • Message body can be a extendedTextMessage or imageMessage or videoMessage or voiceMessage, see here

  • You can add backgroundColor and other options in the message options, see here

  • broadcast: true enables broadcast mode

  • statusJidList: a list of people that you can get which you need to provide, which are the people who will get this status message.

  • You can send messages to broadcast lists the same way you send messages to groups & individual chats.

  • Right now, WA Web does not support creating broadcast lists, but you can still delete them.

  • Broadcast IDs are in the format 12345678@broadcast

πŸ”Ž Query Penerima & Nama Broadcast List

const bList = await sock.getBroadcastListInfo('1234@broadcast')
console.log (`list name: ${bList.name}, recps: ${bList.recipients}`)

✍️ Menulis Fungsionalitas Kustom (Klik untuk membuka)

✍️ Menulis Fungsionalitas Kustom

AstraBail is written with custom functionality in mind. Instead of forking the project & re-writing the internals, you can simply write your own extensions.

πŸ› Mengaktifkan Level Debug di Log AstraBail

First, enable the logging of unhandled messages from WhatsApp by setting:

const sock = makeWASocket({
    logger: P({ level: 'debug' }),
})

This will enable you to see all sorts of messages WhatsApp sends in the console.

πŸ”¬ Bagaimana WhatsApp Berkomunikasi dengan Kita

[!TIP] If you want to learn whatsapp protocol, we recommend to study about Libsignal Protocol and Noise Protocol

  • Example: Functionality to track the battery percentage of your phone. You enable logging and you'll see a message about your battery pop up in the console:
    {
        "level": 10,
        "fromMe": false,
        "frame": {
            "tag": "ib",
            "attrs": {
                "from": "@s.whatsapp.net"
            },
            "content": [
                {
                    "tag": "edge_routing",
                    "attrs": {},
                    "content": [
                        {
                            "tag": "routing_info",
                            "attrs": {},
                            "content": {
                                "type": "Buffer",
                                "data": [8,2,8,5]
                            }
                        }
                    ]
                }
            ]
        },
        "msg":"communication"
    }
    

The 'frame' is what the message received is, it has three components:

  • tag -- what this frame is about (eg. message will have 'message')
  • attrs -- a string key-value pair with some metadata (contains ID of the message usually)
  • content -- the actual data (eg. a message node will have the actual message content in it)
  • read more about this format here

πŸ” Mendaftarkan Callback untuk Event Websocket

[!TIP] Recommended to see onMessageReceived function in socket.ts file to understand how websockets events are fired

// for any message with tag 'edge_routing'
sock.ws.on('CB:edge_routing', (node: BinaryNode) => { })

// for any message with tag 'edge_routing' and id attribute = abcd
sock.ws.on('CB:edge_routing,id:abcd', (node: BinaryNode) => { })

// for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node: BinaryNode) => { })

[!NOTE] Also, this repo is now licenced under GPL 3 since it uses libsignal-node

⚠️ Catatan (Klik untuk membuka)

⚠️ Catatan

Proyek ini tidak berafiliasi dengan WhatsApp Inc.
Gunakan secara bertanggung jawab dan hindari aktivitas ilegal atau penyalahgunaan.

Lisensi (Klik untuk membuka)

Lisensi

πŸ“˜ Documentation powered by AstraBail
πŸ§‘β€πŸ’» Modified and Presented by AstraBail

Sticker Pack Message (Klik untuk membuka)

Sticker Pack Message

Gunakan field stickerPack pada sendMessage untuk mengirim satu paket sticker sekaligus.

Catatan penting: stickerPack sekarang bisa menerima campuran sticker statis dan video/animated (webp/png/jpg/jpeg/gif/webm/mp4) lalu mengonversinya ke WebP otomatis saat proses kirim, selama ffmpeg tersedia di server. Format tgs/Lottie tetap butuh preview/konverter tambahan di luar runtime ini.

await sock.sendMessage(jid, {
  stickerPack: {
    name: 'Nama Pack',
    publisher: 'Nama Publisher',
    description: 'Deskripsi singkat',
    cover: { url: './cover.png' },
    stickers: [
      { sticker: { url: './1.webp' }, emojis: ['πŸ™‚'] },
      { sticker: { url: './2.png' }, emojis: ['πŸŽ‰'] }
    ]
  }
})

cover dan setiap item sticker menerima format media yang sama seperti pengiriman media biasa: path lokal, URL, Buffer, atau stream.

πŸ›‘οΈ Panduan Stabilitas & Pencegahan Bot Freeze (Klik untuk membuka)

πŸ›‘οΈ Panduan Stabilitas & Pencegahan Bot Freeze

Bagian ini menjelaskan penyebab umum bot hang/freeze dan cara mencegahnya secara profesional.

⚠️ PERINGATAN PENTING: Bot Bisa Hang/Freeze Saat Ada User Join/Leave Grup (Klik untuk membuka)

⚠️ PERINGATAN PENTING: Bot Bisa Hang/Freeze Saat Ada User Join/Leave Grup

[!CAUTION] Ini adalah kesalahan paling umum yang dilakukan developer bot WhatsApp. Jika kamu mengirim pesan langsung (await sendMessage(...)) di dalam event group-participants.update tanpa delay, botmu akan hang (macet) dan tidak bisa memproses command apapun selama beberapa menit.

❓ Mengapa Bisa Terjadi?

Ketika ada user baru join ke grup, WhatsApp membutuhkan waktu untuk:

  • Menyinkronkan Sender Key β€” Kunci enkripsi baru harus dinegosiasikan antara bot dan member baru
  • Memperbarui Session Signal β€” Library perlu update session internal untuk anggota baru
  • Fetch data server β€” WhatsApp server membutuhkan sedikit waktu untuk menyebarkan informasi member baru

Jika kamu langsung memanggil sendMessage pada saat yang sama dengan proses di atas, bot akan "menunggu" respons server yang belum siap β€” menyebabkan seluruh event loop Node.js tersumbat (blocked).

πŸ“Š Ilustrasi Masalah

[User Join Grup]
       ↓
[group-participants.update fired]
       ↓
[Bot langsung await sendMessage()] ← ❌ MASALAH ADA DI SINI
       ↓
[Baileys menunggu Sender Key dari server...]
       ↓
[Event loop TERSUMBAT 30-60 detik]
       ↓
[Semua command lain DIABAIKAN selama menunggu]

βœ… Solusi yang Benar

Selalu gunakan setTimeout dengan delay minimal 3-5 detik sebelum mengirim pesan welcome/leave di dalam event group-participants.update:

// ❌ CARA SALAH β€” Bot akan hang!
sock.ev.on('group-participants.update', async ({ id, participants, action }) => {
    if (action === 'add') {
        await sock.sendMessage(id, { text: 'Selamat datang!' }) // ← JANGAN LAKUKAN INI
    }
})

// βœ… CARA BENAR β€” Bot tetap responsif!
sock.ev.on('group-participants.update', async ({ id, participants, action }) => {
    // Selesaikan event handler ini secepat mungkin (non-blocking)
    // Kirim pesan welcome di background setelah 5 detik
    setTimeout(async () => {
        try {
            if (action === 'add') {
                for (const participant of participants) {
                    await sock.sendMessage(id, {
                        text: `Selamat datang @${participant.id.split('@')[0]}!`,
                        mentions: [participant.id]
                    })
                }
            }
        } catch (e) {
            console.error('Welcome message error:', e.message)
        }
    }, 5000) // ← Delay 5 detik agar Baileys selesai negosiasi kunci enkripsi
})

[!TIP] Kenapa 5 detik? Itulah waktu rata-rata yang dibutuhkan WhatsApp server untuk menyebarkan Sender Key baru ke semua perangkat dalam grup. Kamu bisa mencoba 3 detik jika grupnya kecil (< 50 orang), tapi 5 detik lebih aman untuk grup besar.

🧠 Panduan Lengkap: Menangani Event Grup dengan Benar (Klik untuk membuka)

🧠 Panduan Lengkap: Menangani Event Grup dengan Benar

πŸ“‹ Template Lengkap Welcome & Leave Message

Berikut adalah template yang sudah teruji dan aman untuk menangani welcome/leave message:

const { default: makeWASocket, useMultiFileAuthState } = require('astrabail')
const fs = require('fs')

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

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

    // βœ… Event handler yang AMAN dan TIDAK menyebabkan hang
    sock.ev.on('group-participants.update', async ({ id, participants, action, author }) => {
        
        // βœ… STEP 1: Ambil data grup yang diperlukan SEBELUM setTimeout
        // Ini aman karena groupMetadata sudah ada di cache internal AstraBail
        let groupMeta
        try {
            groupMeta = await sock.groupMetadata(id)
        } catch (e) {
            console.error('Gagal ambil metadata grup:', e.message)
            return
        }

        // βœ… STEP 2: Kirim pesan di background dengan delay
        // Jangan await di sini β€” biarkan event handler selesai lebih dulu
        setTimeout(async () => {
            for (const participant of participants) {
                const userJid = participant.id || participant
                const userNumber = userJid.split('@')[0]

                try {
                    if (action === 'add') {
                        // Pesan Welcome
                        await sock.sendMessage(id, {
                            text: `πŸ‘‹ Selamat datang @${userNumber} di grup *${groupMeta.subject}*!`,
                            mentions: [userJid]
                        })
                    } else if (action === 'remove') {
                        // Pesan Leave
                        await sock.sendMessage(id, {
                            text: `πŸ‘‹ Sampai jumpa @${userNumber}, semoga ketemu lagi!`,
                            mentions: [userJid]
                        })
                    } else if (action === 'promote') {
                        // Pesan Promosi Admin
                        await sock.sendMessage(id, {
                            text: `🎊 Selamat @${userNumber}! Kamu sekarang menjadi *Admin* grup ini.`,
                            mentions: [userJid]
                        })
                    } else if (action === 'demote') {
                        // Pesan Copot Admin
                        await sock.sendMessage(id, {
                            text: `ℹ️ @${userNumber} tidak lagi menjadi Admin grup ini.`,
                            mentions: [userJid]
                        })
                    }
                } catch (e) {
                    console.error(`Gagal kirim pesan untuk ${userJid}:`, e.message)
                }
            }
        }, 5000) // ← Delay 5 detik (JANGAN dihapus!)
    })
}

startBot()
πŸ†” Panduan Lengkap: LID vs JID (Untuk Pemula) (Klik untuk membuka)

πŸ†” Panduan Lengkap: LID vs JID (Untuk Pemula)

[!NOTE] Bagian ini menjelaskan sistem identifikasi pengguna WhatsApp yang baru. Penting untuk dipahami agar fitur mention, welcome, dan admin-check bekerja dengan benar.

Apa itu JID dan LID?

WhatsApp menggunakan dua sistem ID pengguna:

TipeFormatKeterangan
JID (Phone Number ID)628xxx@s.whatsapp.netID lama berbasis nomor telepon
LID (Linked ID)123456789@lidID baru sistem multi-device WhatsApp

Masalah yang Sering Terjadi

Pada grup yang sudah mengaktifkan sistem LID (mode addressing baru WhatsApp), event group-participants.update bisa mengirimkan participant dengan format @lid β€” bukan nomor telepon. Jika kamu tidak menanganinya, fitur mention tidak akan bekerja.

AstraBail sudah otomatis mengatasi ini. Library secara internal:

  • Mendeteksi apakah participant menggunakan LID
  • Jika LID tersedia dan phoneNumber ada, otomatis mengganti id dengan phoneNumber (PN format)
  • Menyimpan LID asli di field lid untuk referensi
// Contoh data participant yang dikirim AstraBail ke event handler kamu:
{
    id: '628123456789@s.whatsapp.net',  // ← Sudah dinormalisasi ke PN (nomor telepon)
    phoneNumber: '628123456789@s.whatsapp.net',
    lid: '272202813464634@lid',           // ← LID asli disimpan di sini
    admin: null                            // null = bukan admin, 'admin' = admin, 'superadmin' = owner
}

Cara Aman Mengambil Nomor Telepon dari Participant

// βœ… Cara yang AMAN β€” kompatibel dengan LID dan JID
function getRealJid(participant) {
    if (!participant) return null
    if (typeof participant === 'string') return participant
    
    // Prioritaskan phoneNumber (sudah dinormalisasi oleh AstraBail)
    if (participant.phoneNumber) return participant.phoneNumber
    
    // Fallback: gunakan id jika bukan @lid
    if (participant.id && !participant.id.endsWith('@lid')) return participant.id
    
    return null
}

// Penggunaan:
sock.ev.on('group-participants.update', async ({ participants, action, id }) => {
    setTimeout(async () => {
        for (const p of participants) {
            const userJid = getRealJid(p) // ← Selalu gunakan fungsi ini
            if (!userJid) continue
            
            const phoneNumber = userJid.split('@')[0] // Ambil nomor saja
            
            await sock.sendMessage(id, {
                text: `@${phoneNumber} bergabung!`,
                mentions: [userJid]
            })
        }
    }, 5000)
})
⚑ Tips Performa & Optimasi (Klik untuk membuka)

⚑ Tips Performa & Optimasi

1. Aktifkan Cache Metadata Grup (Sangat Direkomendasikan)

Tanpa cache, setiap kali bot memproses pesan dari grup, library akan melakukan request ke server WhatsApp untuk mengambil daftar peserta. Ini lambat dan bisa menyebabkan rate limit.

AstraBail sudah memiliki cache internal bawaan (5 menit TTL), tapi kamu bisa menambahkan cache eksternal untuk kontrol lebih lanjut:

// Instalasi: npm install node-cache
const NodeCache = require('node-cache')
const groupCache = new NodeCache({ stdTTL: 300, useClones: false }) // Cache 5 menit

const sock = makeWASocket({
    auth: state,
    // ← Ini memberi tahu AstraBail untuk pakai cache kamu dulu sebelum hit server
    cachedGroupMetadata: async (jid) => groupCache.get(jid)
})

// Update cache saat ada perubahan grup
sock.ev.on('groups.update', async (updates) => {
    for (const update of updates) {
        const meta = await sock.groupMetadata(update.id)
        groupCache.set(update.id, meta)
    }
})

sock.ev.on('group-participants.update', async ({ id }) => {
    // Invalidate cache saat ada perubahan peserta
    groupCache.del(id)
})

2. Gunakan makeCacheableSignalKeyStore untuk Performa Signal

const { makeCacheableSignalKeyStore } = require('astrabail')
const pino = require('pino')

const sock = makeWASocket({
    auth: {
        creds: state.creds,
        // ← Ini mempercepat proses enkripsi/dekripsi pesan secara signifikan
        keys: makeCacheableSignalKeyStore(state.keys, pino({ level: 'silent' }))
    }
})

3. Konfigurasi Socket yang Direkomendasikan untuk Bot Production

const sock = makeWASocket({
    auth: state,
    printQRInTerminal: false,
    
    // Sembunyikan log internal Baileys agar terminal bersih
    logger: pino({ level: 'silent' }),
    
    // Nonaktifkan sinkronisasi history penuh (hemat memori & waktu koneksi)
    syncFullHistory: false,
    shouldSyncHistoryMessage: () => false,
    
    // Timeout koneksi yang aman
    connectTimeoutMs: 60000,
    defaultQueryTimeoutMs: 0,        // 0 = tidak ada timeout (lebih stabil)
    keepAliveIntervalMs: 10000,
    
    // Bot tetap terlihat online
    markOnlineOnConnect: true,
    
    // Performa enkripsi yang lebih baik
    keys: makeCacheableSignalKeyStore(state.keys, pino({ level: 'silent' })),
    
    // Cache metadata grup
    cachedGroupMetadata: async (jid) => groupCache.get(jid)
})

4. Cegah Pemrosesan Pesan Duplikat

// Set untuk menyimpan ID pesan yang sudah diproses
const processedMessages = new Set()

sock.ev.on('messages.upsert', async ({ messages, type }) => {
    if (type !== 'notify') return
    
    for (const m of messages) {
        if (!m.message || !m.key?.id) continue
        
        // Lewati jika sudah diproses
        if (processedMessages.has(m.key.id)) continue
        processedMessages.add(m.key.id)
        
        // Hapus dari Set setelah 60 detik untuk hemat memori
        setTimeout(() => processedMessages.delete(m.key.id), 60000)
        
        // Proses pesan...
        // await handler(sock, m)
    }
})
πŸ”§ Panduan Troubleshooting (Untuk Pemula) (Klik untuk membuka)

πŸ”§ Panduan Troubleshooting (Untuk Pemula)

Bagian ini membantu kamu mendiagnosis dan memperbaiki masalah umum yang sering terjadi.

❗ Masalah: Bot Tidak Merespon Command Setelah Ada User Join/Leave

Gejala: Terminal menampilkan log MSG (pesan masuk) tapi tidak ada log CMD (command dieksekusi). Bot seperti "membeku" selama beberapa menit.

Penyebab: await sendMessage() dipanggil langsung di dalam event handler group-participants.update tanpa delay.

Solusi:

// Tambahkan setTimeout dengan delay 5000ms (5 detik) sebelum sendMessage
setTimeout(async () => {
    await sock.sendMessage(groupId, { text: 'Welcome!' })
}, 5000) // ← Ini yang menyelamatkan botmu

❗ Masalah: Mention Tidak Berfungsi / Nama Tidak Muncul

Gejala: Pesan terkirim tapi @nomortelpon tidak menjadi mention biru, atau nama kontak tidak muncul.

Penyebab: Format JID yang digunakan salah, atau field mentions tidak diisi.

Solusi:

// βœ… Cara yang benar
const userJid = '628123456789@s.whatsapp.net' // Harus pakai @s.whatsapp.net

await sock.sendMessage(groupId, {
    text: `Halo @628123456789!`,  // @ + nomor tanpa kode negara awal 0
    mentions: [userJid]            // ← WAJIB diisi agar mention berfungsi
})

❗ Masalah: Session Terhapus / QR Code Muncul Terus

Gejala: Setiap kali bot restart, QR code muncul lagi dan harus scan ulang.

Penyebab: Folder session tidak disimpan dengan benar, atau ada error saat menyimpan kredensial.

Solusi:

const { state, saveCreds } = await useMultiFileAuthState('./session')

const sock = makeWASocket({ auth: state })

// ← WAJIB: Pastikan ini ada agar session tersimpan
sock.ev.on('creds.update', saveCreds)

Pastikan folder ./session tidak dihapus dan bot punya izin menulis ke folder tersebut.

❗ Masalah: Bot Error "Connection Closed" / Sering Disconnect

Gejala: Bot sering disconnect dan perlu reconnect manual.

Solusi: Implementasikan auto-reconnect yang benar:

const { DisconnectReason } = require('astrabail')
const { Boom } = require('@hapi/boom')

sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
    if (connection === 'close') {
        const reason = new Boom(lastDisconnect?.error)?.output?.statusCode
        
        if (reason === DisconnectReason.loggedOut) {
            // Sesi expired atau logout β€” JANGAN reconnect, hapus session dulu
            console.log('❌ Bot ter-logout. Hapus folder session dan scan ulang QR.')
            process.exit(1)
        } else {
            // Error lain β€” aman untuk reconnect
            console.log('πŸ”„ Koneksi terputus, mencoba reconnect...')
            startBot() // Panggil ulang fungsi utama
        }
    } else if (connection === 'open') {
        console.log('βœ… Bot berhasil terhubung!')
    }
})

❗ Masalah: groupMetadata Error / Timeout saat Banyak Request

Gejala: Error seperti Error: Timed out atau Error: not-authorized saat memanggil groupMetadata.

Penyebab: Terlalu banyak request groupMetadata dalam waktu singkat (rate limit dari WhatsApp).

Solusi: Gunakan cache dan batasi frekuensi request:

// Map untuk menyimpan waktu terakhir request per grup
const lastFetchTime = new Map()

async function safeGroupMetadata(sock, groupJid) {
    const now = Date.now()
    const lastFetch = lastFetchTime.get(groupJid) || 0
    
    // Batasi request: minimal 30 detik sekali per grup
    if (now - lastFetch < 30000) {
        // Gunakan data dari file lokal jika ada
        const dbPath = `./database/group/${groupJid.split('@')[0]}.json`
        if (fs.existsSync(dbPath)) {
            return JSON.parse(fs.readFileSync(dbPath, 'utf-8'))
        }
        return null
    }
    
    try {
        const meta = await sock.groupMetadata(groupJid)
        lastFetchTime.set(groupJid, now)
        return meta
    } catch (e) {
        console.error('groupMetadata error:', e.message)
        return null
    }
}

❗ Masalah: Pesan Tidak Terenkripsi / Error Enkripsi

Gejala: Error seperti Error: No sessions, Error: bad mac, atau pesan gagal terkirim ke grup tertentu.

Penyebab: Session Signal untuk beberapa member belum tersedia atau sudah expired.

Solusi: Pastikan menggunakan makeCacheableSignalKeyStore dan jangan hapus folder session secara manual. Jika masalah persisten, coba:

// Reset cache session (hati-hati: ini akan reconnect)
// Hapus folder session, lalu scan QR ulang
πŸ“ Referensi Cepat: Format ID WhatsApp (Klik untuk membuka)

πŸ“ Referensi Cepat: Format ID WhatsApp

TipeFormatContoh
User (PN)[kode_negara][nomor]@s.whatsapp.net628123456789@s.whatsapp.net
User (LID)[angka_random]@lid272202813464634@lid
Grup[timestamp]-[random]@g.us120363186018390209@g.us
Broadcast[timestamp]@broadcast1234567890@broadcast
Status/Storystatus@broadcaststatus@broadcast
Newsletter[id]@newsletter123456789@newsletter

[!IMPORTANT] Selalu gunakan format @s.whatsapp.net (bukan @c.us) untuk user. Format @c.us sudah deprecated dan tidak didukung oleh WhatsApp multi-device.

πŸ“ Daftar Event Lengkap AstraBail (Klik untuk membuka)

πŸ“ Daftar Event Lengkap AstraBail

EventKapan DipanggilData yang Diterima
connection.updateStatus koneksi berubah{ connection, lastDisconnect, qr }
creds.updateKredensial session berubahData kredensial baru
messages.upsertPesan baru diterima/dikirim{ messages, type }
messages.updatePesan diperbarui (dibaca, dihapus, dll)Array update pesan
messages.reactionAda reaksi pada pesan[{ reaction, key }]
message-receipt.updateStatus pengiriman pesan berubahArray receipt update
messages.deletePesan dihapusKeys pesan yang dihapus
group-participants.updateMember grup berubah (join/leave/promote){ id, participants, action, author }
groups.updateInfo grup berubah (nama, deskripsi, dll)Array update grup
group.join-requestAda request join grup{ id, participant, action }
chats.updateData chat berubahArray update chat
chats.upsertChat baru munculArray chat baru
contacts.updateInfo kontak berubahArray update kontak
contacts.upsertKontak baruArray kontak baru
presence.updateStatus online/typing berubah{ id, presences }
messaging-history.setRiwayat pesan diterimaData history
callAda panggilan masukData panggilan
labels.associationLabel chat berubahData asosiasi label
🎯 Contoh Bot Lengkap yang Siap Produksi (Klik untuk membuka)

🎯 Contoh Bot Lengkap yang Siap Produksi

Berikut adalah contoh bot paling lengkap dan aman menggunakan AstraBail:

const { default: makeWASocket, useMultiFileAuthState, DisconnectReason, makeCacheableSignalKeyStore } = require('astrabail')
const { Boom } = require('@hapi/boom')
const pino = require('pino')
const NodeCache = require('node-cache')

// Cache untuk metadata grup (TTL 5 menit)
const groupCache = new NodeCache({ stdTTL: 300, useClones: false })

// Set untuk mencegah pesan duplikat
const processedMsgs = new Set()

async function startBot() {
    const { state, saveCreds } = await useMultiFileAuthState('./session')

    const sock = makeWASocket({
        auth: {
            creds: state.creds,
            keys: makeCacheableSignalKeyStore(state.keys, pino({ level: 'silent' }))
        },
        logger: pino({ level: 'silent' }),
        printQRInTerminal: false,
        syncFullHistory: false,
        connectTimeoutMs: 60000,
        defaultQueryTimeoutMs: 0,
        keepAliveIntervalMs: 10000,
        cachedGroupMetadata: async (jid) => groupCache.get(jid)
    })

    // ─── Simpan session ───────────────────────────────────────────────────
    sock.ev.on('creds.update', saveCreds)

    // ─── Auto reconnect ───────────────────────────────────────────────────
    sock.ev.on('connection.update', ({ connection, lastDisconnect, qr }) => {
        if (qr) console.log('Scan QR ini untuk login')
        if (connection === 'close') {
            const reason = new Boom(lastDisconnect?.error)?.output?.statusCode
            if (reason === DisconnectReason.loggedOut) {
                console.log('❌ Session expired. Hapus folder session dan restart.')
                process.exit(1)
            }
            console.log('πŸ”„ Reconnecting...')
            startBot()
        } else if (connection === 'open') {
            console.log('βœ… Bot Online!')
        }
    })

    // ─── Update cache saat grup berubah ──────────────────────────────────
    sock.ev.on('groups.update', async (updates) => {
        for (const update of updates) {
            try {
                const meta = await sock.groupMetadata(update.id)
                groupCache.set(update.id, meta)
            } catch (_) {}
        }
    })

    // ─── Welcome/Leave message yang AMAN ─────────────────────────────────
    sock.ev.on('group-participants.update', async ({ id, participants, action }) => {
        // Ambil metadata sebelum setTimeout (sudah ada di cache, aman)
        let meta
        try {
            meta = await sock.groupMetadata(id)
            groupCache.set(id, meta) // Update cache
        } catch (_) { return }

        // ← KUNCI: Delay 5 detik agar Baileys selesai sinkronisasi kunci enkripsi
        setTimeout(async () => {
            for (const p of participants) {
                const userJid = p.phoneNumber || (p.id?.endsWith('@lid') ? null : p.id) || p
                if (!userJid || typeof userJid !== 'string') continue
                const num = userJid.split('@')[0]

                try {
                    if (action === 'add') {
                        await sock.sendMessage(id, {
                            text: `πŸ‘‹ Selamat datang @${num} di grup *${meta.subject}*!`,
                            mentions: [userJid]
                        })
                    } else if (action === 'remove') {
                        await sock.sendMessage(id, {
                            text: `πŸ‘‹ Sampai jumpa @${num}!`,
                            mentions: [userJid]
                        })
                    }
                } catch (e) {
                    console.error('Pesan gagal:', e.message)
                }
            }
        }, 5000)
    })

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

        for (const m of messages) {
            if (!m.message || !m.key?.id) continue
            if (m.key.remoteJid === 'status@broadcast') continue

            // Cegah duplikat
            if (processedMsgs.has(m.key.id)) continue
            processedMsgs.add(m.key.id)
            setTimeout(() => processedMsgs.delete(m.key.id), 60000)

            // Proses pesan di sini...
            const text = m.message.conversation || m.message.extendedTextMessage?.text || ''
            if (text === '.ping') {
                await sock.sendMessage(m.key.remoteJid, { text: 'πŸ“ Pong!' }, { quoted: m })
            }
        }
    })
}

startBot()
πŸ’¬ Butuh Bantuan? (Klik untuk membuka)

πŸ’¬ Butuh Bantuan?

Jika kamu mengalami masalah yang tidak tercakup di dokumentasi ini:

WhatsApp Channel GitHub Issues

AstraBail β€” Built for stability. Designed for developers.

Keywords

astra

FAQs

Package last updated on 11 Aug 2026

Related posts