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

puki

v1.0.9

Published

A modified version of baileys - WebSockets library for interacting with WhatsApp Web

Downloads

3

Readme

Puki


Features

🚀 Core Capabilities

  • Multi-Authentication Support: QR code, pairing code, and persistent session management
  • Comprehensive Message Types: Text, media, interactive buttons, polls, location, contacts, and more
  • Advanced Media Handling: Upload/download with encryption, thumbnail generation, and metadata extraction
  • Group Management: Full CRUD operations, participant management, and settings control
  • Real-time Events: WebSocket-based real-time message and status updates
  • Business Integration: Business profiles, product catalogs, and commercial features

🔧 Technical Features

  • TypeScript First: Comprehensive type definitions and full IntelliSense support
  • Signal Protocol: End-to-end encryption using WhatsApp's Signal implementation
  • Performance Optimized: Intelligent caching, connection pooling, and efficient data handling
  • Event-Driven Architecture: Robust event system for handling all WhatsApp interactions
  • Error Recovery: Automatic reconnection, retry mechanisms, and graceful error handling
  • Extensible Design: Plugin architecture and customizable middleware support

📱 WhatsApp Features

  • Interactive Messages: Buttons, quick replies, lists, and rich media interactions
  • Status Management: Read receipts, typing indicators, and presence updates
  • Chat Operations: Message clearing, forwarding, reactions, and advanced chat controls
  • Privacy Controls: Ephemeral messages, view-once media, and privacy settings
  • History Sync: Message history synchronization and backup capabilities
  • WAM Integration: WhatsApp Analytics and metrics collection

Installation

Prerequisites

  • Node.js: >= 20.0.0 (LTS recommended)
  • Package Manager: npm, yarn, or pnpm
  • TypeScript: >= 5.0.0 (for TypeScript projects)

Install via npm

npm install puki

Install via yarn

yarn add puki

Install via pnpm

pnpm add puki

Optional Dependencies

For enhanced functionality, install these optional peer dependencies:

# For advanced image processing
npm install sharp

# Alternative image processing (fallback)
npm install jimp

# For link previews
npm install link-preview-js

# For audio processing
npm install audio-decode

Development Installation

# Clone the repository
git clone https://github.com/mehebub648/puki.git
cd puki

# Install dependencies
npm install

# Build the project
npm run build:all

# Run tests
npm test

Quick Start

Basic Bot Setup

const { default: makeWASocket, DisconnectReason, useMultiFileAuthState, downloadMediaMessage, Browsers } = require('puki')
const { Boom } = require('@hapi/boom')
const pino = require('pino')
const qrcode = require('qrcode-terminal')

async function startBot() {
    // Initialize authentication state
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys')
    
    // Create a proper Pino logger
    const logger = pino({ 
        timestamp: () => `,"time":"${new Date().toJSON()}"`,
        level: 'silent' // Set to 'info' or 'debug' if you want to see logs
    })
    
    // Create WhatsApp socket
    const sock = makeWASocket({
        auth: state,
        logger: logger,
        browser: Browsers.macOS('Chrome'),
    })

    // Handle connection updates and QR code generation to log in the console
    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update
        
        if (connection === 'close') {
            const shouldReconnect = (lastDisconnect?.error instanceof Boom)
                ? lastDisconnect.error.output?.statusCode !== DisconnectReason.loggedOut
                : false
            console.log('Connection closed:', lastDisconnect?.error, 'Reconnecting:', shouldReconnect)
            
            if (shouldReconnect) {
                startBot()
            }
        } else if (connection === 'open') {
            console.log('Connected to WhatsApp!')
        }

        // Handle QR code generation
        if (update.qr) {
            qrcode.generate(update.qr, { small: true }, (qrcode) => {
                console.log('QR Code generated:')
                console.log(qrcode)
            })
        }
    })

    // Save credentials
    sock.ev.on('creds.update', saveCreds)

    // Handle incoming messages
    sock.ev.on('messages.received', async (m) => {
        const msg = m.messages[0]
        if (!msg.key.fromMe && m.type === 'notify') {
            const messageText = msg.message?.conversation || 
                              msg.message?.extendedTextMessage?.text
            
            console.log('Received:', messageText)
            
            // Simple echo bot
            if (messageText === '/hello') {
                await sock.sendMessage(msg.key.remoteJid, { 
                    text: 'Hello! I am Puki bot.' 
                })
            }
        }
    })

    return sock
}

startBot()

Authentication

Puki supports multiple authentication methods to establish a WhatsApp Web session.

QR Code Authentication

The default and most common method:

const makeWASocket = require('puki').default
const { useMultiFileAuthState } = require('puki')
const qrcode = require('qrcode-terminal')

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

const sock = makeWASocket({
    auth: state,
    browser: ['Puki', 'Chrome', '1.0.0']
})

// Handle QR code manually
sock.ev.on('connection.update', (update) => {
    if (update.qr) {
        qrcode.generate(update.qr, { small: true }, (qrcode) => {
            console.log('Please scan this QR code:')
            console.log(qrcode)
        })
    }
})

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

Pairing Code Authentication

Alternative to QR code for programmatic authentication:

const makeWASocket = require('puki').default
const { useMultiFileAuthState } = require('puki')

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

const sock = makeWASocket({
    auth: state,
    browser: ['Puki', 'Chrome', '1.0.0']
})

// Request pairing code
if (!state.creds.registered) {
    const phoneNumber = '+1234567890' // Include country code
    const pairingCode = await sock.requestPairingCode(phoneNumber)
    console.log('Pairing code:', pairingCode)
}

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

Multi-File Auth State

Persistent authentication using file-based storage:

const { useMultiFileAuthState } = require('puki')

// Creates a folder to store authentication data
const { state, saveCreds, clearState } = await useMultiFileAuthState('./auth_info')

// Use the state
const sock = makeWASocket({ auth: state })

// Always save credentials when updated
sock.ev.on('creds.update', saveCreds)

// Clear authentication (logout)
// await clearState()

Custom Auth State Implementation

Implement your own authentication storage:

const { AuthenticationState, BufferJSON } = require('puki')

const customAuthState = {
    creds: {
        // Load from your database/storage
    },
    keys: {
        get: async (type, ids) => {
            // Retrieve keys from your storage
            return {}
        },
        set: async (data) => {
            // Save keys to your storage
        }
    }
}

const sock = makeWASocket({ auth: customAuthState })

Authentication Events

Handle authentication-related events:

const { Boom } = require('@hapi/boom')
const qrcode = require('qrcode-terminal')

sock.ev.on('connection.update', (update) => {
    const { connection, lastDisconnect, qr } = update
    
    if (connection === 'close') {
        const statusCode = (lastDisconnect?.error instanceof Boom) 
            ? lastDisconnect.error.output?.statusCode 
            : null
        
        if (statusCode === DisconnectReason.loggedOut) {
            console.log('Device logged out, scan QR code again')
            // Clear auth state and restart
        } else if (statusCode === DisconnectReason.restartRequired) {
            console.log('Server restarting, reconnecting...')
            startBot()
        }
    } else if (connection === 'open') {
        console.log('Connected successfully!')
    }
    
    if (qr) {
        console.log('Scan this QR code:')
        qrcode.generate(qr, { small: true }, (qrcode) => {
            console.log(qrcode)
        })
        // You can also generate QR code image here
    }
})

// Save authentication credentials
sock.ev.on('creds.update', saveCreds)

Authentication Configuration

Advanced authentication options:

const pino = require('pino')
const { Browsers } = require('puki')

const sock = makeWASocket({
    auth: state,
    
    // Browser information (affects QR code display)
    browser: Browsers.macOS('Chrome'),
    
    // Custom QR generation
    qrGenerate: (qr) => {
        // Custom QR code handling
        console.log('QR Code:', qr)
    },
    
    // Authentication timeout
    qrTimeout: 60000, // 60 seconds
    
    // Custom logger for auth events
    logger: pino({ 
        level: 'debug',
        timestamp: () => `,"time":"${new Date().toJSON()}"` 
    })
})

Message Handling

Puki supports all WhatsApp message types with comprehensive sending and receiving capabilities.

Text Messages

Basic Text

// Simple text message
await sock.sendMessage(jid, { text: 'Hello World!' })

// Text with formatting
await sock.sendMessage(jid, { 
    text: '*Bold* _Italic_ ~Strikethrough~ ```Monospace```' 
})

Extended Text Messages

// Text with context info
await sock.sendMessage(jid, {
    text: 'Reply to this message',
    contextInfo: {
        mentionedJid: ['[email protected]'],
        quotedMessage: previousMessage,
        isForwarded: false
    }
})

// Text with mentions
await sock.sendMessage(jid, {
    text: 'Hello @1234567890, how are you?',
    mentions: ['[email protected]']
})

Interactive Messages

Traditional Buttons

await sock.sendMessage(jid, {
    text: 'Choose an option:',
    footer: 'Powered by Puki',
    buttons: [
        { 
            buttonId: 'option1', 
            buttonText: { displayText: 'Option 1' }, 
            type: 1 
        },
        { 
            buttonId: 'option2', 
            buttonText: { displayText: 'Option 2' }, 
            type: 1 
        }
    ],
    headerType: 1
})

Interactive Buttons (New Format)

await sock.sendMessage(jid, {
    text: 'Modern interactive buttons',
    interactiveButtons: [
        {
            name: 'quick_reply',
            buttonParamsJson: JSON.stringify({
                display_text: 'Quick Reply',
                id: 'quick_reply_1'
            })
        },
        {
            name: 'cta_url',
            buttonParamsJson: JSON.stringify({
                display_text: 'Visit Website',
                url: 'https://example.com',
                merchant_url: 'https://example.com'
            })
        }
    ]
})

List Messages

const listMessage = {
    text: 'Please select an option:',
    footer: 'Choose wisely',
    title: 'Selection Menu',
    buttonText: 'View Options',
    listType: 1,
    sections: [
        {
            title: 'Main Options',
            rows: [
                {
                    title: 'Option 1',
                    description: 'Description for option 1',
                    rowId: 'option_1'
                },
                {
                    title: 'Option 2', 
                    description: 'Description for option 2',
                    rowId: 'option_2'
                }
            ]
        }
    ]
}

await sock.sendMessage(jid, listMessage)

Media Messages

Images

// Send image from file
await sock.sendMessage(jid, {
    image: { url: './path/to/image.jpg' },
    caption: 'Beautiful sunset!',
    jpegThumbnail: thumbnailBuffer // Optional thumbnail
})

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

// Send image from buffer
await sock.sendMessage(jid, {
    image: imageBuffer,
    caption: 'Image from buffer'
})

Videos

await sock.sendMessage(jid, {
    video: { url: './video.mp4' },
    caption: 'Check out this video!',
    gifPlayback: false, // Set true for GIF-like playback
    ptv: false, // Profile picture video
    jpegThumbnail: thumbnailBuffer
})

Audio

// Audio message
await sock.sendMessage(jid, {
    audio: { url: './audio.mp3' },
    mimetype: 'audio/mp4',
    ptt: true, // Push to talk (voice message)
    waveform: waveformBuffer // Optional waveform data
})

// Voice note
await sock.sendMessage(jid, {
    audio: { url: './voice.ogg' },
    mimetype: 'audio/ogg; codecs=opus',
    ptt: true,
    seconds: 30 // Duration in seconds
})

Documents

await sock.sendMessage(jid, {
    document: { url: './document.pdf' },
    mimetype: 'application/pdf',
    fileName: 'important_document.pdf',
    caption: 'Please review this document',
    pageCount: 10, // Optional page count
    jpegThumbnail: thumbnailBuffer
})

Stickers

await sock.sendMessage(jid, {
    sticker: { url: './sticker.webp' },
    packname: 'My Sticker Pack',
    author: 'Puki Bot'
})

Location Messages

// Current location
await sock.sendMessage(jid, {
    location: {
        degreesLatitude: 37.7749,
        degreesLongitude: -122.4194,
        name: 'San Francisco',
        address: 'San Francisco, CA, USA'
    }
})

// Live location
await sock.sendMessage(jid, {
    liveLocation: {
        degreesLatitude: 37.7749,
        degreesLongitude: -122.4194,
        caption: 'My live location',
        sequenceNumber: 1,
        timeOffset: 0,
        jpegThumbnail: thumbnailBuffer
    }
})

Contact Messages

// Single contact
await sock.sendMessage(jid, {
    contact: {
        displayName: 'John Doe',
        vcard: `BEGIN:VCARD
VERSION:3.0
FN:John Doe
ORG:Company Name
TEL;type=CELL;type=VOICE;waid=1234567890:+1 234 567 8900
END:VCARD`
    }
})

// Multiple contacts
await sock.sendMessage(jid, {
    contacts: {
        displayName: 'My Contacts',
        contacts: [
            {
                displayName: 'Contact 1',
                vcard: 'BEGIN:VCARD...'
            }
        ]
    }
})

Special Message Types

Ephemeral Messages

await sock.sendMessage(jid, {
    text: 'This message will disappear',
    ephemeralExpiration: 604800 // 7 days in seconds
})

View Once Messages

await sock.sendMessage(jid, {
    viewOnce: true,
    image: { url: './private_image.jpg' },
    caption: 'This image can only be viewed once'
})

Reply Messages

await sock.sendMessage(jid, {
    text: 'This is a reply',
    quotedMessage: {
        key: originalMessage.key,
        message: originalMessage.message
    }
})

Message Status and Reactions

Send Reactions

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

Update Message Status

// Mark as read
await sock.readMessages([messageKey])

// Send delivery receipt
await sock.sendReceipt(jid, participant, [messageKey.id], 'read')

Message Events

Handle different message events:

// Handle all messages (sent and received)
sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        if (m.type !== 'notify') continue
        
        const messageType = Object.keys(msg.message || {})[0]
        
        switch (messageType) {
            case 'conversation':
            case 'extendedTextMessage':
                // Handle text messages
                break
            case 'imageMessage':
                // Handle image messages
                break
            case 'videoMessage':
                // Handle video messages
                break
            case 'audioMessage':
                // Handle audio messages
                break
            case 'documentMessage':
                // Handle document messages
                break
            case 'contactMessage':
                // Handle contact messages
                break
            case 'locationMessage':
                // Handle location messages
                break
            case 'buttonsResponseMessage':
                // Handle button responses
                break
            case 'listResponseMessage':
                // Handle list responses
                break
        }
    }
})

// Handle only received messages (from other users)
sock.ev.on('messages.received', async (m) => {
    for (const msg of m.messages) {
        const messageText = msg.message?.conversation || 
                          msg.message?.extendedTextMessage?.text
        
        console.log('Received message from:', msg.key.remoteJid, messageText)
        
        // Process only incoming messages here
        if (messageText === '/help') {
            await sock.sendMessage(msg.key.remoteJid, { 
                text: 'Available commands: /hello, /help' 
            })
        }
    }
})

// Handle message updates (read receipts, delivery, etc.)
sock.ev.on('messages.update', (updates) => {
    for (const update of updates) {
        if (update.update.status) {
            console.log(`Message ${update.key.id} status: ${update.update.status}`)
        }
    }
})

Media Handling

Puki provides comprehensive media processing capabilities with encryption, thumbnail generation, and metadata extraction.

Downloading Media

Basic Media Download

const { downloadMediaMessage } = require('puki')

// Download media from a message
const buffer = await downloadMediaMessage(
    message, // The message containing media
    'buffer', // Return type: 'buffer' | 'stream'
    {}, // Options
    {
        logger: console,
        reuploadRequest: sock.updateMediaMessage
    }
)

// Save to file
const fs = require('fs')
if (buffer) {
    fs.writeFileSync('./downloaded_media.jpg', buffer)
}

Advanced Download Options

const downloadOptions = {
    // Custom media key (for decryption)
    mediaKey: customMediaKey,
    
    // Transform stream while downloading
    transform: (readable) => {
        return readable.pipe(someTransformStream)
    },
    
    // Custom options
    options: {
        hostname: 'custom.hostname.com',
        auth: 'custom-auth-token'
    }
}

const buffer = await downloadMediaMessage(message, 'buffer', downloadOptions)

Uploading Media

Upload with Automatic Processing

// Upload image with thumbnail generation
const uploadResult = await sock.sendMessage(jid, {
    image: { url: './image.jpg' },
    caption: 'Auto-processed image'
    // Thumbnail is automatically generated
})

Manual Media Upload

import { getWAUploadToServer } from 'puki'

// Custom upload function
const uploadMedia = async (buffer: Buffer, mediaType: 'image' | 'video' | 'document' | 'audio') => {
    const { upload } = await sock.refreshMediaConn(false)
    
    return await upload(buffer, { mediaType })
}

// Use custom upload
const { url, directPath, mediaKey, fileEncSha256, fileSha256 } = await uploadMedia(imageBuffer, 'image')

await sock.sendMessage(jid, {
    image: {
        url,
        directPath,
        mediaKey,
        fileEncSha256,
        fileSha256
    },
    caption: 'Manually uploaded image'
})

Image Processing

Thumbnail Generation

import { generateThumbnail } from 'puki'

// Generate thumbnail for image
const thumbnail = await generateThumbnail(imageBuffer, {
    width: 150,
    height: 150,
    format: 'jpeg',
    quality: 80
})

await sock.sendMessage(jid, {
    image: { url: './image.jpg' },
    jpegThumbnail: thumbnail
})

Image Manipulation with Sharp

import sharp from 'sharp'

// Resize and optimize image
const processedImage = await sharp(imageBuffer)
    .resize(1920, 1080, { fit: 'inside' })
    .jpeg({ quality: 85 })
    .toBuffer()

await sock.sendMessage(jid, {
    image: processedImage,
    caption: 'Processed with Sharp'
})

Image Manipulation with Jimp (Fallback)

import Jimp from 'jimp'

// Process with Jimp
const image = await Jimp.read(imageBuffer)
await image
    .resize(800, 600)
    .quality(80)
    .greyscale()

const processedBuffer = await image.getBufferAsync(Jimp.MIME_JPEG)

await sock.sendMessage(jid, {
    image: processedBuffer,
    caption: 'Processed with Jimp'
})

Audio Processing

Audio Metadata Extraction

import { parseFile } from 'music-metadata'

// Extract audio metadata
const metadata = await parseFile('./audio.mp3')

await sock.sendMessage(jid, {
    audio: { url: './audio.mp3' },
    mimetype: 'audio/mp4',
    title: metadata.common.title,
    performer: metadata.common.artist,
    duration: metadata.format.duration,
    ptt: false
})

Voice Message Processing

// Convert audio to voice message format
const voiceMessage = {
    audio: { url: './voice.ogg' },
    mimetype: 'audio/ogg; codecs=opus',
    ptt: true, // Push to talk
    seconds: 30,
    waveform: generateWaveform(audioBuffer) // Custom waveform
}

await sock.sendMessage(jid, voiceMessage)

Video Processing

Video Thumbnail Generation

import ffmpeg from 'fluent-ffmpeg'

// Generate video thumbnail
const generateVideoThumbnail = (videoPath: string): Promise<Buffer> => {
    return new Promise((resolve, reject) => {
        ffmpeg(videoPath)
            .screenshots({
                count: 1,
                timemarks: ['00:00:01.000'],
                size: '320x240'
            })
            .on('end', (files) => {
                const thumbnail = fs.readFileSync(files[0])
                resolve(thumbnail)
            })
            .on('error', reject)
    })
}

const thumbnail = await generateVideoThumbnail('./video.mp4')

await sock.sendMessage(jid, {
    video: { url: './video.mp4' },
    caption: 'Video with custom thumbnail',
    jpegThumbnail: thumbnail
})

Document Processing

Document with Thumbnail

// Generate document thumbnail
const generateDocumentThumbnail = async (documentPath: string) => {
    // Use appropriate library based on document type
    // PDF: pdf2pic, DOCX: officegen, etc.
    return thumbnailBuffer
}

await sock.sendMessage(jid, {
    document: { url: './document.pdf' },
    mimetype: 'application/pdf',
    fileName: 'document.pdf',
    pageCount: 15,
    jpegThumbnail: await generateDocumentThumbnail('./document.pdf')
})

Media Encryption/Decryption

Manual Media Encryption

import { encryptMedia, decryptMedia } from 'puki'

// Encrypt media
const { ciphertext, mediaKey, macKey, iv } = await encryptMedia(mediaBuffer, 'image')

// Decrypt media
const decryptedBuffer = await decryptMedia(ciphertext, mediaKey, 'image', macKey, iv)

Media Key Management

// Extract media keys from message
const extractMediaKeys = (message: any) => {
    const media = message.imageMessage || message.videoMessage || message.audioMessage || message.documentMessage
    
    return {
        mediaKey: media?.mediaKey,
        directPath: media?.directPath,
        url: media?.url,
        fileEncSha256: media?.fileEncSha256,
        fileSha256: media?.fileSha256
    }
}

Media Utilities

Get Media Type

const getMediaType = (message: any): string | null => {
    if (message.imageMessage) return 'image'
    if (message.videoMessage) return 'video'
    if (message.audioMessage) return 'audio'
    if (message.documentMessage) return 'document'
    if (message.stickerMessage) return 'sticker'
    return null
}

Media Size Validation

const validateMediaSize = (buffer: Buffer, type: string): boolean => {
    const limits = {
        image: 16 * 1024 * 1024, // 16MB
        video: 64 * 1024 * 1024, // 64MB
        audio: 16 * 1024 * 1024, // 16MB
        document: 100 * 1024 * 1024 // 100MB
    }
    
    return buffer.length <= (limits[type] || limits.document)
}

Media Streaming

Stream Large Files

import { createReadStream } from 'fs'

// Stream large video file
const videoStream = createReadStream('./large_video.mp4')

await sock.sendMessage(jid, {
    video: { stream: videoStream },
    caption: 'Streaming large video file'
})

Progress Tracking

const sendMediaWithProgress = async (filePath: string, jid: string) => {
    const stats = fs.statSync(filePath)
    const totalSize = stats.size
    let uploadedSize = 0
    
    const stream = createReadStream(filePath)
    
    stream.on('data', (chunk) => {
        uploadedSize += chunk.length
        const progress = (uploadedSize / totalSize) * 100
        console.log(`Upload progress: ${progress.toFixed(2)}%`)
    })
    
    await sock.sendMessage(jid, {
        video: { stream },
        caption: 'Video with progress tracking'
    })
}

Group Management

Puki provides comprehensive group management capabilities including creation, participant management, and settings control.

Group Operations

Create a Group

// Create a new group
const groupData = await sock.groupCreate(
    'My Awesome Group', // Group name
    ['[email protected]', '[email protected]'] // Initial participants
)

console.log('Group created:', groupData.id)
console.log('Group invite code:', groupData.inviteCode)

Get Group Metadata

// Get complete group information
const groupMetadata = await sock.groupMetadata('[email protected]')

console.log('Group info:', {
    id: groupMetadata.id,
    subject: groupMetadata.subject,
    desc: groupMetadata.desc,
    descId: groupMetadata.descId,
    descTime: groupMetadata.descTime,
    descOwner: groupMetadata.descOwner,
    creation: groupMetadata.creation,
    owner: groupMetadata.owner,
    participants: groupMetadata.participants,
    size: groupMetadata.size,
    announcement: groupMetadata.announcement,
    restrict: groupMetadata.restrict,
    ephemeralDuration: groupMetadata.ephemeralDuration
})

Update Group Settings

// Update group name
await sock.groupUpdateSubject('[email protected]', 'New Group Name')

// Update group description
await sock.groupUpdateDescription('[email protected]', 'New group description')

// Update group picture
await sock.updateProfilePicture('[email protected]', { url: './new_group_pic.jpg' })

// Toggle group announcement mode (only admins can send messages)
await sock.groupSettingUpdate('[email protected]', 'announcement')

// Toggle group restriction (only admins can edit group info)
await sock.groupSettingUpdate('[email protected]', 'locked')

// Set ephemeral messages (disappearing messages)
await sock.groupToggleEphemeral('[email protected]', 86400) // 1 day in seconds

Participant Management

Add Participants

// Add single participant
await sock.groupParticipantsUpdate(
    '[email protected]',
    ['[email protected]'],
    'add'
)

// Add multiple participants
await sock.groupParticipantsUpdate(
    '[email protected]',
    ['[email protected]', '[email protected]'],
    'add'
)

Remove Participants

// Remove participants
await sock.groupParticipantsUpdate(
    '[email protected]',
    ['[email protected]'],
    'remove'
)

Promote/Demote Admins

// Promote to admin
await sock.groupParticipantsUpdate(
    '[email protected]',
    ['[email protected]'],
    'promote'
)

// Demote from admin
await sock.groupParticipantsUpdate(
    '[email protected]',
    ['[email protected]'],
    'demote'
)

Group Invites

Generate Invite Link

// Create group invite code
const inviteCode = await sock.groupInviteCode('[email protected]')
const inviteLink = `https://chat.whatsapp.com/${inviteCode}`

console.log('Group invite link:', inviteLink)

Revoke Invite Link

// Revoke current invite link and generate new one
const newInviteCode = await sock.groupRevokeInvite('[email protected]')
console.log('New invite code:', newInviteCode)

Join via Invite Code

// Accept group invite
const groupInfo = await sock.groupAcceptInvite('INVITE_CODE')
console.log('Joined group:', groupInfo)

Get Invite Info

// Get group info from invite code without joining
const inviteInfo = await sock.groupGetInviteInfo('INVITE_CODE')
console.log('Group invite info:', {
    id: inviteInfo.id,
    subject: inviteInfo.subject,
    size: inviteInfo.size,
    participants: inviteInfo.participants
})

Advanced Group Features

Group Participant Permissions

// Check if user is admin
const isAdmin = (groupMetadata: any, userJid: string): boolean => {
    const participant = groupMetadata.participants.find(p => p.id === userJid)
    return participant?.admin === 'admin' || participant?.admin === 'superadmin'
}

// Get all admins
const getGroupAdmins = (groupMetadata: any): string[] => {
    return groupMetadata.participants
        .filter(p => p.admin === 'admin' || p.admin === 'superadmin')
        .map(p => p.id)
}

// Get group owner
const getGroupOwner = (groupMetadata: any): string => {
    return groupMetadata.owner || 
           groupMetadata.participants.find(p => p.admin === 'superadmin')?.id
}

Bulk Operations

// Bulk add participants with error handling
const bulkAddParticipants = async (groupJid: string, participants: string[]) => {
    const results = []
    
    for (const participant of participants) {
        try {
            await sock.groupParticipantsUpdate(groupJid, [participant], 'add')
            results.push({ participant, status: 'success' })
        } catch (error) {
            results.push({ participant, status: 'error', error: error.message })
        }
    }
    
    return results
}

Group Activity Monitoring

// Monitor group events
sock.ev.on('groups.upsert', (groups) => {
    for (const group of groups) {
        console.log('Group updated:', group.id)
    }
})

sock.ev.on('group-participants.update', (update) => {
    console.log('Participant update:', {
        groupJid: update.id,
        participants: update.participants,
        action: update.action, // 'add' | 'remove' | 'promote' | 'demote'
        author: update.author
    })
})

Group Message Management

Send Group Messages

// Send message to group
await sock.sendMessage('[email protected]', {
    text: 'Hello everyone! 👋'
})

// Send message with mentions
await sock.sendMessage('[email protected]', {
    text: 'Hello @1234567890 and @0987654321!',
    mentions: ['[email protected]', '[email protected]']
})

// Mention all participants
const groupMetadata = await sock.groupMetadata('[email protected]')
const allParticipants = groupMetadata.participants.map(p => p.id)

await sock.sendMessage('[email protected]', {
    text: 'Important announcement for everyone!',
    mentions: allParticipants
})

Group Message Filtering

sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        const isGroup = msg.key.remoteJid?.endsWith('@g.us')
        
        if (isGroup) {
            const groupJid = msg.key.remoteJid
            const senderJid = msg.key.participant || msg.key.remoteJid
            
            // Get group metadata
            const groupMetadata = await sock.groupMetadata(groupJid)
            
            // Check if sender is admin
            const senderIsAdmin = isAdmin(groupMetadata, senderJid)
            
            // Handle group-specific logic
            console.log(`Message in ${groupMetadata.subject} from ${senderJid} (Admin: ${senderIsAdmin})`)
        }
    }
})

Group Statistics

Get Group Analytics

const getGroupStats = async (groupJid: string) => {
    const metadata = await sock.groupMetadata(groupJid)
    
    return {
        name: metadata.subject,
        description: metadata.desc,
        totalParticipants: metadata.participants.length,
        adminCount: metadata.participants.filter(p => p.admin).length,
        creationDate: new Date(metadata.creation * 1000),
        isAnnouncement: metadata.announcement,
        isRestricted: metadata.restrict,
        ephemeralDuration: metadata.ephemeralDuration
    }
}

Group Utilities

Search Groups

// Get all groups the bot is in
const getAllGroups = async () => {
    const chats = await sock.chatsFetchAll()
    return chats.filter(chat => chat.id.endsWith('@g.us'))
}

// Find group by name
const findGroupByName = async (name: string) => {
    const groups = await getAllGroups()
    
    for (const group of groups) {
        const metadata = await sock.groupMetadata(group.id)
        if (metadata.subject.toLowerCase().includes(name.toLowerCase())) {
            return metadata
        }
    }
    
    return null
}

Group Backup

// Export group data
const exportGroupData = async (groupJid: string) => {
    const metadata = await sock.groupMetadata(groupJid)
    const messages = await sock.fetchMessageHistory(groupJid, 1000)
    
    return {
        metadata,
        messages,
        exportDate: new Date().toISOString()
    }
}

Business Features

Puki supports WhatsApp Business features for commercial use cases.

Business Profile Management

Get Business Profile

// Get business profile information
const businessProfile = await sock.getBusinessProfile('[email protected]')

console.log('Business info:', {
    name: businessProfile.name,
    category: businessProfile.category,
    description: businessProfile.description,
    email: businessProfile.email,
    website: businessProfile.website,
    address: businessProfile.address,
    hours: businessProfile.hours,
    isVerified: businessProfile.isVerified
})

Update Business Profile

// Update business profile (if you own the business account)
await sock.updateBusinessProfile({
    name: 'My Business',
    category: 'Retail',
    description: 'We sell amazing products',
    email: '[email protected]',
    website: 'https://business.com',
    address: '123 Business St, City, Country',
    hours: 'Mon-Fri 9AM-5PM'
})

Product Catalog

Send Product Message

// Send a product from catalog
await sock.sendMessage(jid, {
    product: {
        productImage: { url: './product.jpg' },
        productId: 'PRODUCT_ID',
        title: 'Amazing Product',
        description: 'This product will change your life',
        currencyCode: 'USD',
        priceAmount1000: 99900, // $99.90 in units of 1000
        retailerId: 'RETAILER_ID',
        url: 'https://shop.com/product',
        productImageCount: 1
    }
})

Send Product List

// Send multiple products
await sock.sendMessage(jid, {
    productList: {
        productSections: [
            {
                title: 'Featured Products',
                products: [
                    {
                        productId: 'PRODUCT_1',
                        title: 'Product 1',
                        description: 'First product',
                        currencyCode: 'USD',
                        priceAmount1000: 50000,
                        retailerId: 'RETAILER_ID',
                        productImage: { url: './product1.jpg' }
                    },
                    {
                        productId: 'PRODUCT_2',
                        title: 'Product 2',
                        description: 'Second product',
                        currencyCode: 'USD',
                        priceAmount1000: 75000,
                        retailerId: 'RETAILER_ID',
                        productImage: { url: './product2.jpg' }
                    }
                ]
            }
        ],
        headerText: 'Our Products',
        footerText: 'Powered by Puki'
    }
})

Order Management

Handle Order Messages

sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        if (msg.message?.orderMessage) {
            const order = msg.message.orderMessage
            
            console.log('New order received:', {
                orderId: order.orderId,
                thumbnail: order.thumbnail,
                itemCount: order.itemCount,
                status: order.status,
                surface: order.surface,
                message: order.message,
                orderTitle: order.orderTitle,
                sellerJid: order.sellerJid,
                token: order.token
            })
            
            // Process the order
            await processOrder(order)
        }
    }
})

const processOrder = async (order: any) => {
    // Implement your order processing logic
    console.log('Processing order:', order.orderId)
    
    // Send confirmation
    await sock.sendMessage(order.sellerJid, {
        text: `Order ${order.orderId} has been received and is being processed.`
    })
}

Payment Integration

Send Payment Request

// Send payment request (requires business account)
await sock.sendMessage(jid, {
    requestPayment: {
        currencyCodeIso4217: 'USD',
        amount1000: 100000, // $100.00
        requestFrom: jid,
        noteMessage: {
            extendedTextMessage: {
                text: 'Payment for services rendered'
            }
        }
    }
})

Handle Payment Updates

sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        if (msg.message?.paymentInviteMessage) {
            const payment = msg.message.paymentInviteMessage
            
            console.log('Payment invite:', {
                serviceType: payment.serviceType,
                expiryTimestamp: payment.expiryTimestamp
            })
        }
        
        if (msg.message?.sendPaymentMessage) {
            const payment = msg.message.sendPaymentMessage
            
            console.log('Payment sent:', {
                noteMessage: payment.noteMessage,
                requestMessageKey: payment.requestMessageKey
            })
        }
    }
})

Customer Support Features

Auto-Reply Setup

const businessResponses = {
    'hello': 'Hi! Welcome to our business. How can we help you today?',
    'hours': 'Our business hours are Monday-Friday 9AM-5PM',
    'contact': 'You can reach us at [email protected] or +1234567890',
    'location': 'We are located at 123 Business St, City, Country'
}

sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        if (!msg.key.fromMe && m.type === 'notify') {
            const text = msg.message?.conversation?.toLowerCase() ||
                        msg.message?.extendedTextMessage?.text?.toLowerCase()
            
            if (text && businessResponses[text]) {
                await sock.sendMessage(msg.key.remoteJid!, {
                    text: businessResponses[text]
                })
            }
        }
    }
})

Customer Information Management

// Store customer information
const customerDatabase = new Map()

const addCustomer = (jid: string, info: any) => {
    customerDatabase.set(jid, {
        ...info,
        firstContact: new Date(),
        lastContact: new Date(),
        messageCount: 0
    })
}

const updateCustomer = (jid: string, updates: any) => {
    const existing = customerDatabase.get(jid) || {}
    customerDatabase.set(jid, {
        ...existing,
        ...updates,
        lastContact: new Date(),
        messageCount: (existing.messageCount || 0) + 1
    })
}

// Track customer interactions
sock.ev.on('messages.upsert', async (m) => {
    for (const msg of m.messages) {
        if (!msg.key.fromMe) {
            const customerJid = msg.key.remoteJid
            updateCustomer(customerJid, {
                lastMessage: msg.message?.conversation || 'media'
            })
        }
    }
})

Business Analytics

Message Analytics

const analytics = {
    totalMessages: 0,
    incomingMessages: 0,
    outgoingMessages: 0,
    customerCount: 0,
    dailyStats: new Map()
}

const updateAnalytics = (isIncoming: boolean) => {
    analytics.totalMessages++
    
    if (isIncoming) {
        analytics.incomingMessages++
    } else {
        analytics.outgoingMessages++
    }
    
    const today = new Date().toDateString()
    const dailyStat = analytics.dailyStats.get(today) || { incoming: 0, outgoing: 0 }
    
    if (isIncoming) {
        dailyStat.incoming++
    } else {
        dailyStat.outgoing++
    }
    
    analytics.dailyStats.set(today, dailyStat)
}

// Generate business report
const generateBusinessReport = () => {
    return {
        totalMessages: analytics.totalMessages,
        incomingMessages: analytics.incomingMessages,
        outgoingMessages: analytics.outgoingMessages,
        responseRate: (analytics.outgoingMessages / analytics.incomingMessages * 100).toFixed(2) + '%',
        activeCustomers: customerDatabase.size,
        dailyStats: Object.fromEntries(analytics.dailyStats)
    }
}

WhatsApp Business API Features

Broadcast Lists

// Send broadcast message to multiple customers
const broadcastMessage = async (recipients: string[], message: any) => {
    for (const recipient of recipients) {
        try {
            await sock.sendMessage(recipient, message)
            await new Promise(resolve => setTimeout(resolve, 1000)) // Rate limiting
        } catch (error) {
            console.error(`Failed to send to ${recipient}:`, error)
        }
    }
}

// Send promotional broadcast
await broadcastMessage(
    ['[email protected]', '[email protected]'],
    {
        text: '🎉 Special offer! Get 20% off on all products this week!',
        footer: 'Terms and conditions apply'
    }
)

Template Messages

// Send template message (requires approved templates)
const sendTemplateMessage = async (jid: string, templateName: string, parameters: string[]) => {
    await sock.sendMessage(jid, {
        templateMessage: {
            hydratedTemplate: {
                hydratedContentText: templateName,
                hydratedButtons: [
                    {
                        quickReplyButton: {
                            displayText: 'Yes',
                            id: 'yes'
                        }
                    },
                    {
                        quickReplyButton: {
                            displayText: 'No',
                            id: 'no'
                        }
                    }
                ]
            }
        }
    })
}

API Reference

Core Functions

makeWASocket(config: SocketConfig): WASocket

Creates a new WhatsApp Web socket connection with the specified configuration.

Parameters:

interface SocketConfig {
    // Authentication
    auth: AuthenticationState
    
    // Connection settings
    waWebSocketUrl?: string | URL
    connectTimeoutMs?: number // Default: 20000
    defaultQueryTimeoutMs?: number // Default: 60000
    keepAliveIntervalMs?: number // Default: 25000
    
    // Browser and version
    version?: WAVersion // [major, minor, patch]
    browser?: WABrowserDescription // [name, version, os]
    
    // Features
    printQRInTerminal?: boolean // Default: false
    generateHighQualityLinkPreview?: boolean // Default: false
    syncFullHistory?: boolean // Default: false
    markOnlineOnConnect?: boolean // Default: true
    
    // Media settings
    linkPreviewImageThumbnailWidth?: number // Default: 192
    
    // Networking
    agent?: Agent // HTTP agent for requests
    fetchAgent?: Agent // Agent for media uploads/downloads
    
    // Callbacks and hooks
    getMessage?: (key: WAMessageKey) => Promise<WAMessageContent | undefined>
    patchMessageBeforeSending?: (msg: proto.Message) => proto.Message
    shouldIgnoreJid?: (jid: string) => boolean
    
    // Caching
    userDevicesCache?: CacheStore
    msgRetryCounterCache?: CacheStore
    cachedGroupMetadata?: (jid: string) => Promise<GroupMetadata | undefined>
    
    // Logging
    logger?: ILogger
}

Returns: WASocket - The socket instance with all available methods.

useMultiFileAuthState(folder: string): Promise<AuthState>

Creates file-based authentication state management for persistent sessions.

Parameters:

  • folder (string): Directory path to store authentication files

Returns:

{
    state: AuthenticationState,
    saveCreds: () => Promise<void>,
    clearState: () => Promise<void>
}

Socket Methods

Core Communication

sendMessage(jid: string, content: AnyMessageContent, options?: MessageGenerationOptions): Promise<WAMessage>

Send any type of message to a chat or group.

Parameters:

  • jid: Target JID (chat ID)
  • content: Message content (text, media, buttons, etc.)
  • options: Additional options

Options:

interface MessageGenerationOptions {
    timestamp?: number
    quoted?: WAMessage
    ephemeralExpiration?: number
    disappearingMessagesInChat?: boolean
    isViewOnce?: boolean
    backgroundColor?: string
    font?: number
    textArgb?: number
    cachedGroupMetadata?: (jid: string) => Promise<GroupMetadata>
}
relayMessage(jid: string, message: WAMessage, options?: MessageRelayOptions): Promise<WAMessage>

Relay a pre-constructed message.

sendReceipt(jid: string, participant: string | undefined, messageIds: string[], type: MessageReceiptType): Promise<void>

Send delivery/read receipts.

readMessages(keys: WAMessageKey[]): Promise<void>

Mark messages as read.

sendPresenceUpdate(type: WAPresence, jid?: string): Promise<void>

Update presence status (typing, online, etc.).

Group Management

groupCreate(subject: string, participants: string[]): Promise<{ id: string, gid: string, inviteCode?: string }>

Create a new group with specified participants.

groupMetadata(jid: string): Promise<GroupMetadata>

Get comprehensive group information.

groupParticipantsUpdate(jid: string, participants: string[], action: ParticipantAction): Promise<ParticipantUpdateResult[]>

Add, remove, promote, or demote group participants.

groupUpdateSubject(jid: string, subject: string): Promise<void>

Update group name/subject.

groupUpdateDescription(jid: string, description?: string): Promise<void>

Update group description (pass undefined to remove).

groupInviteCode(jid: string): Promise<string>

Get group invite code.

groupRevokeInvite(jid: string): Promise<string>

Revoke current invite and generate new one.

groupAcceptInvite(code: string): Promise<string>

Join group using invite code.

groupGetInviteInfo(code: string): Promise<GroupMetadata>

Get group info from invite code without joining.

groupSettingUpdate(jid: string, setting: GroupSetting): Promise<void>

Update group settings (announcement, restriction modes).

groupToggleEphemeral(jid: string, ephemeralExpiration: number): Promise<void>

Toggle ephemeral (disappearing) messages.

Profile & Contact Management

updateProfilePicture(jid: string, content: WAMediaUpload): Promise<void>

Update profile or group picture.

updateProfileStatus(status: string): Promise<void>

Update WhatsApp status message.

updateProfileName(name: string): Promise<void>

Update profile display name.

getBusinessProfile(jid: string): Promise<BusinessProfile>

Get business account information.

fetchBlocklist(): Promise<string[]>

Get list of blocked contacts.

updateBlockStatus(jid: string, action: 'block' | 'unblock'): Promise<void>

Block or unblock a contact.

Chat Operations

chatModify(modification: ChatModification, jid: string, chatInfo?: Chat, lastMessages?: WAMessage[]): Promise<void>

Modify chat properties (archive, pin, mute, etc.).

Chat Modifications:

type ChatModification = 
    | { archive: boolean }
    | { pin: boolean }
    | { mute: number | null } // null to unmute, number for seconds
    | { clear: 'all' | { messages: WAMessageKey[] } }
    | { star: { messages: WAMessageKey[], star: boolean } }
    | { markRead: boolean }
    | { delete: boolean }
clearMessage(jid: string, key: WAMessageKey): Promise<WAMessage>

Clear/delete a specific message for everyone.

Media Operations

downloadMediaMessage(message: WAMessage, type?: 'buffer' | 'stream', options?: DownloadMediaOptions): Promise<Buffer | Transform | undefined>

Download media content from a message.

updateMediaMessage(message: WAMessage): Promise<WAMessage>

Re-upload failed media message.

generateThumbnail(buffer: Buffer, type: MediaType, options?: ThumbnailOptions): Promise<Buffer>

Generate thumbnail for media.

Advanced Operations

query(node: BinaryNode, timeoutMs?: number): Promise<BinaryNode>

Send raw binary node query to WhatsApp servers.

waitForMessage(jid: string, options?: WaitMessageOptions): Promise<WAMessage>

Wait for a specific message matching criteria.

fetchLatestBaileysVersion(): Promise<{ version: WAVersion, isLatest: boolean }>

Get latest supported WhatsApp Web version.

fetchPrivacySettings(force?: boolean): Promise<PrivacySettings>

Get privacy settings from WhatsApp.

Event System

The socket uses EventEmitter for real-time updates. All events are typed for better development experience.

Connection Events

// Connection status updates
sock.ev.on('connection.update', (update: Partial<ConnectionState>) => {
    // update.connection: 'connecting' | 'open' | 'close'
    // update.lastDisconnect: Error details if disconnected
    // update.qr: QR code for authentication
    // update.receivedPendingNotifications: boolean
})

// Credential updates (always save these)
sock.ev.on('creds.update', () => saveCreds())

Message Events

// New messages received
sock.ev.on('messages.upsert', (update: { messages: WAMessage[], type: MessageUpsertType }) => {
    // type: 'append' | 'notify' | 'prepend'
})

// Message status updates (read receipts, delivery, etc.)
sock.ev.on('messages.update', (updates: WAMessageUpdate[]) => {
    // Updates to existing messages
})

// Messages deleted
sock.ev.on('messages.delete', (item: { keys: WAMessageKey[] }) => {
    // Handle deleted messages
})

// Read receipt updates
sock.ev.on('message-receipt.update', (updates: MessageReceiptUpdate[]) => {
    // Read receipt status changes
})

Chat Events

// Chat updates
sock.ev.on('chats.upsert', (chats: Chat[]) => {
    // New or updated chats
})

sock.ev.on('chats.update', (updates: Partial<Chat>[]) => {
    // Chat property changes
})

sock.ev.on('chats.delete', (deletions: string[]) => {
    // Deleted chats
})

Contact & Presence Events

// Contact updates
sock.ev.on('contacts.upsert', (contacts: Contact[]) => {
    // New or updated contacts
})

sock.ev.on('contacts.update', (updates: Partial<Contact>[]) => {
    // Contact changes
})

// Presence updates (online/offline/typing)
sock.ev.on('presence.update', (update: PresenceData) => {
    // id: jid of user
    // presences: { [participantJid]: PresenceInfo }
})

Group Events

// Group metadata updates
sock.ev.on('groups.upsert', (groups: GroupMetadata[]) => {
    // New or updated groups
})

// Group participant changes
sock.ev.on('group-participants.update', (update: {
    id: string, // Group JID
    participants: string[], // Affected participants
    action: ParticipantAction, // 'add' | 'remove' | 'promote' | 'demote'
    author?: string // Who made the change
}) => {
    // Handle participant updates
})

Type Definitions

Core Types

// WhatsApp version tuple
type WAVersion = [number, number, number]

// Browser description
type WABrowserDescription = [string, string, string] // [name, version, os]

// Message key for identifying messages
interface WAMessageKey {
    remoteJid?: string // Chat/group JID
    fromMe?: boolean // If message is from current user
    id?: string // Message ID
    participant?: string // Sender JID in groups
}

// Presence types
type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused'

// Participant actions
type ParticipantAction = 'add' | 'remove' | 'promote' | 'demote'

// Message receipt types
type MessageReceiptType = 'read' | 'read-self' | 'hist_sync' | 'peer_msg' | 'sender' | 'inactive' | 'played'

// Disconnect reasons
enum DisconnectReason {
    connectionClosed = 428,
    connectionLost = 408,
    connectionReplaced = 440,
    timedOut = 408,
    loggedOut = 401,
    badSession = 500,
    restartRequired = 515,
    multideviceMismatch = 411
}

Message Content Types

// All possible message content
interface AnyMessageContent {
    // Text content
    text?: string
    
    // Media content
    image?: WAMediaUpload
    video?: WAMediaUpload
    audio?: WAMediaUpload
    document?: WAMediaUpload
    sticker?: WAMediaUpload
    
    // Location
    location?: LocationMessage
    liveLocation?: LiveLocationMessage
    
    // Contacts
    contact?: ContactMessage
    contacts?: ContactsArrayMessage
    
    // Interactive elements
    buttons?: proto.Message.ButtonsMessage.IButton[]
    interactiveButtons?: InteractiveButton[]
    listMessage?: ListMessage
    
    // Business
    product?: ProductMessage
    productList?: ProductListMessage
    order?: OrderMessage
    
    // Special
    poll?: PollCreationMessage
    reaction?: ReactionMessage
    
    // Message properties
    caption?: string
    footer?: string
    mentions?: string[]
    quotedMessage?: WAMessage
    ephemeralExpiration?: number
    viewOnce?: boolean
    backgroundColor?: string
    font?: number
}

// Media upload formats
type WAMediaUpload = Buffer | { url: string | URL } | { stream: Readable }

Group Types

interface GroupMetadata {
    id: string
    subject: string
    desc?: string
    descId?: string
    descTime?: number
    descOwner?: string
    creation?: number
    owner?: string
    participants: GroupParticipant[]
    size?: number
    announcement?: boolean
    restrict?: boolean
    ephemeralDuration?: number
    inviteCode?: string
}

interface GroupParticipant {
    id: string // JID
    admin?: 'admin' | 'superadmin' | null
}

Utility Functions

Message Utilities

// Generate message from content
generateWAMessageFromContent(
    jid: string, 
    content: WAMessageContent, 
    options: MessageGenerationOptions
): proto.WebMessageInfo

// Normalize message content
normalizeMessageContent(content: WAMessageContent): WAMessageContent

// Get message content type
getContentType(content: WAMessageContent): string | undefined

// Extract quoted message
extractMessageContent(message: WAMessage): WAMessageContent | undefined

// Check if message is a protocol message
isProtocolMessage(message: WAMessage): boolean

// Check if event payload contains protocol messages  
isProtocolMessageEvent(eventData: { messages?: WAMessage[] }): boolean

Protocol Message Detection

Protocol messages are system-level messages used by WhatsApp for various operations like ephemeral settings, read receipts, and other metadata updates. The isProtocolMessage utilities help distinguish these from regular user messages:

import { isProtocolMessage, isProtocolMessageEvent } from 'puki'

// Check individual messages
const message = await sock.sendMessage(jid, { text: 'Hello' })
console.log(isProtocolMessage(message)) // false

// Filter protocol messages from events
sock.ev.on('messages.sent', (data) => {
    if (isProtocolMessageEvent(data)) {
        console.log('Protocol messages sent')
        return
    }
    
    // Process only regular user messages
    data.messages.forEach(msg => {
        if (!isProtocolMessage(msg)) {
            console.log('Regular message:', msg.message?.conversation)
        }
    })
})

Note: The messages.sent and messages.received events automatically filter out protocol messages, so you'll primarily receive regular user messages.

Authentication Utilities

// Create cacheable key store
makeCacheableSignalKeyStore(
    store: SignalKeyStore, 
    logger?: ILogger, 
    cache?: CacheStore
): SignalKeyStore

// Initialize credentials
initAuthCreds(): AuthenticationCreds

// Generate registration ID
generateRegistrationId(): number

Crypto Utilities

// Generate message ID
generateMessageID(): string

// Generate key pairs
Curve.generateKeyPair(): KeyPair

// HKDF key derivation
hkdf(buffer: Buffer, expandedLength: number, info: string): Buffer

// AES encryption/decryption
aesEncryptGCM(plaintext: Buffer, key: Buffer, iv: Buffer, additionalData: Buffer): Buffer
aesDecryptGCM(ciphertext: Buffer, key: Buffer, iv: Buffer, additionalData: Buffer, authTag: Buffer): Buffer

Configuration Examples

Basic Configuration

const pino = require('pino')
const { Browsers } = require('puki')

const sock = makeWASocket({
    auth: state,
    logger: pino({ 
        level: 'info',
        timestamp: () => `,"time":"${new Date().toJSON()}"`
    }),
    browser: Browsers.macOS('Chrome')
})

Production Configuration

const NodeCache = require('@cacheable/node-cache')
const pino = require('pino')
const { Browsers } = require('puki')

const sock = makeWASocket({
    auth: state,
    
    // Connection
    connectTimeoutMs: 30000,
    defaultQueryTimeoutMs: 120000,
    keepAliveIntervalMs: 30000,
    
    // Features
    generateHighQualityLinkPreview: true,
    syncFullHistory: false,
    markOnlineOnConnect: true,
    
    // Performance
    userDevicesCache: new NodeCache({ stdTTL: 300 }),
    msgRetryCounterCache: new NodeCache({ stdTTL: 3600 }),
    
    // Browser configuration
    browser: Browsers.macOS('Chrome'),
    
    // Custom handlers
    getMessage: async (key) => {
        // Retrieve message from your database
        return getMessageFromDB(key)
    },
    
    shouldIgnoreJid: (jid) => {
        // Ignore broadcast messages
        return jid.includes('broadcast')
    },
    
    // Logging
    logger: pino({
        level: 'warn',
        timestamp: () => `,"time":"${new Date().toJSON()}"`,
        transport: {
            target: 'pino-pretty',
            options: { colorize: true }
        }
    })
})

Advanced Features

History Sync

Puki supports WhatsApp's history synchronization for retrieving message history across devices.

Enable History Sync

const sock = makeWASocket({
    auth: state,
    syncFullHistory: true, // Enable full history sync
    browser: ['Puki', 'Chrome', '1.0.0']
})

// Handle history sync notifications
sock.ev.on('messaging-history.set', async (data) => {
    const { chats, contacts, messages, isLatest } = data
    
    console.log('History sync received:', {
        chatsCount: chats.length,
        contactsCount: contacts.length,
        messagesCount: messages.length,
        isLatest
    })
    
    // Process historical data
    for (const message of messages) {
        await processHistoricalMessage(message)
    }
})

Download History Sync

import { downloadAndProcessHistorySyncNotification } from 'puki'

sock.ev.on('notifications.upsert', async (notifications) => {
    for (const notification of notifications) {
        if (notification.messageStubType === proto.WebMessageInfo.StubType.HISTORY_SYNC_NOTIFICATION) {
            try {
                const historyData = await downloadAndProcessHistorySyncNotification(
                    notification,
                    sock.authState.creds,
                    sock.authState.keys
                )
                
                console.log('Downloaded history:', historyData)
            } catch (error) {
                console.error('History sync error:', error)
            }
        }
    }
})

WAM (WhatsApp Analytics)

WhatsApp Analytics for tracking usage and performance metrics.

Basic WAM Usage

import { encodeWAM } from 'puki'

// Track message sent
const wamEvent = {
    eventType: 'message',
    messageType: 'text',
    timestamp: Date.now(),
    success: true
}

const encodedWAM = encodeWAM(wamEvent)
console.log('WAM encoded:', encodedWAM)

Custom Analytics Tracking

const trackingData = {
    messagesSent: 0,
    messagesReceived: 0,
    mediaUploaded: 0,
    groupsCreated: 0,
    errors: 0
}

sock.ev.on('messages.upsert', (m) => {
    trackingData.messagesReceived += m.messages.length
})

// Track when sending messages
const originalSendMessage = sock.sendMessage
sock.sendMessage = async function(jid, content, options) {
    try {
        const result = await originalSendMessage.call(this, jid, content, options)
        trackingData.messagesSent++
        
        // Track media uploads
        if (content.image || content.video || content.audio || content.document) {
            trackingData.mediaUploaded++
        }
        
        return result
    } catch (error) {
        trackingData.errors++
        throw error
    }
}

WebSocket Management

Advanced WebSocket connection handling and monitoring.

Connection Monitoring

let connectionAttempts = 0
const maxReconnectAttempts = 5