whatsapp-cloud-bot
v1.2.7
Published
A TypeScript/JavaScript client library for building WhatsApp bots using the WhatsApp Business Cloud API
Downloads
919
Maintainers
Readme
WhatsApp Bot Client
A powerful, fully-typed TypeScript/JavaScript library for building WhatsApp bots using the WhatsApp Business Cloud API.
Features
✨ Fully Typed - Complete TypeScript support with comprehensive type definitions 🚀 Async/Await - Modern async JavaScript architecture built from the ground up 📦 Dual Package - Works with both ESM and CommonJS 🎯 Event-Driven - Elegant handler-based message routing 💬 Interactive Messages - Support for buttons, lists, and location requests 📸 Media Support - Send and receive images, videos, audio, documents, and stickers 🔄 Conversation Flow - Built-in context management and next-step handlers 🧪 Well Tested - Comprehensive test suite included 📚 Great Documentation - Detailed guides and API reference
Table of Contents
- Installation
- Quick Start
- Getting WhatsApp Credentials
- Usage Examples
- API Reference
- TypeScript Usage
- Contributing
- License
Installation
npm install whatsapp-cloud-bot
# or with yarn
yarn add whatsapp-cloud-bot
# or with pnpm
pnpm add whatsapp-cloud-botQuick Start
You can initialize the client in two ways.
Method 1: Initialize with Handlers (Recommended)
Pass your handlers directly to the constructor for a cleaner setup.
import { WhatsApp, MessageHandler } from 'whatsapp-cloud-bot';
const messageHandler = new MessageHandler(async (update) => {
await update.replyMessage('Hello!');
});
const client = new WhatsApp({
numberId: 'YOUR_PHONE_NUMBER_ID',
token: 'YOUR_ACCESS_TOKEN',
handlers: {
messageHandler,
}
});Method 2: Register Handlers Separately
Initialize the client first, then register handlers.
import { WhatsApp } from 'whatsapp-cloud-bot';
const client = new WhatsApp({
numberId: 'YOUR_PHONE_NUMBER_ID',
token: 'YOUR_ACCESS_TOKEN',
});
// Register a message handler
client.onMessage(async (update, context) => {
console.log(`Message from ${update.userDisplayName}: ${update.messageText}`);
await update.replyMessage('Hello! Thanks for your message.');
});Webhook Integration
// Process incoming webhook (in your Express/HTTP server)
app.post('/webhook', async (req, res) => {
await client.processUpdate(req.body);
res.sendStatus(200);
});Getting WhatsApp Credentials
To use this library, you need to obtain a Phone Number ID and Access Token from the Facebook Developer Portal.
Step-by-Step Guide:
Create a Meta/Facebook Developer Account
- Go to developers.facebook.com
- Create an account or log in
Create a New App
- Click "Create App"
- Select "Business" as the app type
- Fill in the required details
Add WhatsApp Product
- In your app dashboard, click "Add Product"
- Find "WhatsApp" and click "Set Up"
Get Your Credentials
- Phone Number ID: Found in the WhatsApp > Getting Started section
- Temporary Token: Also in the Getting Started section (valid for 24 hours)
- Permanent Token: Follow this guide to create a permanent token
Set Up Webhook (for receiving messages)
- In WhatsApp > Configuration
- Add your webhook URL (must be HTTPS)
- Subscribe to message events
- Verify the webhook
Testing Your Setup
WhatsApp provides a test phone number you can use immediately. For production, you'll need to:
- Add a real phone number
- Complete business verification
- Get your app approved
Usage Examples
Sending Different Types of Messages
Text Messages
// Simple text message
await client.sendMessage('1234567890', 'Hello, World!');
// With web preview
await client.sendMessage('1234567890', 'Check this out: https://example.com', {
webPagePreview: true
});Interactive Messages with Buttons
import { InlineKeyboard } from 'whatsapp-cloud-bot';
await client.sendMessage(
'1234567890',
'Choose an option:',
{
replyMarkup: new InlineKeyboard(['Option 1', 'Option 2', 'Option 3'])
}
);Interactive Lists
import { InlineList, ListItem, ListSection } from 'whatsapp-cloud-bot';
// Simple list
const list = new InlineList('Select item', [
new ListItem('Item 1', 'item_1'),
new ListItem('Item 2', 'item_2')
]);
// List with sections
const sectionedList = new InlineList('Choose category', [
new ListSection('Fruits', ['Apple', 'Banana', 'Orange']),
new ListSection('Vegetables', ['Carrot', 'Broccoli', 'Spinach'])
]);
await client.sendMessage('1234567890', 'Here are your options:', {
replyMarkup: sectionedList
});Media Messages
// Send image
await client.sendImage('1234567890', 'https://example.com/image.jpg', 'Check this out!');
// Send video
await client.sendVideo('1234567890', 'https://example.com/video.mp4');
// Send document
await client.sendDocument('1234567890', 'https://example.com/doc.pdf', 'Here is the file');
// Send audio
await client.sendAudio('1234567890', 'https://example.com/audio.mp3');Location Messages
await client.sendLocation(
'1234567890',
37.7749, // latitude
-122.4194, // longitude
'San Francisco',
'123 Market St, San Francisco, CA'
);Template Messages
await client.sendTemplateMessage(
'1234567890',
'hello_world', // template name
[], // components
'en_US' // language code
);Handling Incoming Messages
Basic Message Handler
client.onMessage(async (update, context) => {
console.log(`Received: ${update.messageText}`);
await update.replyMessage('Got your message!');
});With Regex Filter
// Only respond to messages matching regex
client.onMessage(
async (update, context) => {
await update.replyMessage('You said hello!');
},
{ regex: /^(hi|hello|hey)/i }
);With Custom Filter
client.onMessage(
async (update, context) => {
await update.replyMessage('Processing your order...');
},
{
filter: (text) => text.toLowerCase().includes('order')
}
);Interactive Message Handler
client.onInteractiveMessage(async (update, context) => {
if (update.messageText === 'option_1') {
await update.replyMessage('You selected Option 1');
}
});Media Handlers
// Handle images
client.onImageMessage(async (update, context) => {
console.log(`Image caption: ${update.messageText}`);
console.log(`Image ID: ${update.mediaFileId}`);
// Download the image
const filePath = await client.downloadMedia(update.mediaFileId);
console.log(`Image saved to: ${filePath}`);
});
// Handle videos
client.onVideoMessage(async (update, context) => {
await update.replyMessage('Thanks for the video!');
});
// Handle audio
client.onAudioMessage(async (update, context) => {
if (update.mediaVoice) {
await update.replyMessage('Got your voice message!');
}
});
// Handle documents
client.onDocumentMessage(async (update, context) => {
const buffer = await client.downloadMediaData(update.mediaFileId);
// Process document...
});Location Handler
client.onLocationMessage(async (update, context) => {
const { locLatitude, locLongitude, locName } = update;
await update.replyMessage(
`Got your location: ${locName || 'Unknown'} (${locLatitude}, ${locLongitude})`
);
});Conversation Flow Management
Using Context
client.onMessage(async (update, context) => {
if (!context.has('name')) {
context.set('name', update.messageText);
await update.replyMessage('Nice to meet you! How old are you?');
} else if (!context.has('age')) {
context.set('age', parseInt(update.messageText));
const name = context.get('name');
const age = context.get('age');
await update.replyMessage(`Hello ${name}, you are ${age} years old!`);
context.clear(); // Reset conversation
}
});Using Next Step Handler
import { MessageHandler } from 'whatsapp-cloud-bot';
client.onMessage(async (update, context) => {
if (update.messageText === 'start survey') {
await update.replyMessage('What is your name?');
// Set next handler for this user only
client.setNextStep(
update,
new MessageHandler(async (nextUpdate, nextContext) => {
const name = nextUpdate.messageText;
await nextUpdate.replyMessage(`Hello ${name}! Survey complete.`);
})
);
}
});API Reference
WhatsApp Client
Constructor
const client = new WhatsApp(config: WhatsAppConfig);Configuration Options:
numberId(required): Your WhatsApp Phone Number IDtoken(required): Your WhatsApp Access TokenmarkAsRead(optional, default:true): Auto-mark messages as readversion(optional, default:21): WhatsApp API version
Methods
Sending Messages:
sendMessage(phoneNumber, text, options?)- Send text messagesendTemplateMessage(phoneNumber, templateName, components?, languageCode?)- Send templatesendImage(phoneNumber, imagePath, caption?)- Send imagesendVideo(phoneNumber, videoPath, caption?)- Send videosendAudio(phoneNumber, audioPath)- Send audiosendDocument(phoneNumber, docPath, caption?)- Send documentsendLocation(phoneNumber, latitude, longitude, name?, address?)- Send location
Media Management:
getMediaUrl(mediaId)- Get media URL from media IDdownloadMedia(mediaId, filePath?)- Download media to filedownloadMediaData(mediaId)- Download media as Buffer
Handler Registration:
onMessage(action, options?)- Register text message handleronInteractiveMessage(action, options?)- Register interactive handleronImageMessage(action, options?)- Register image handleronAudioMessage(action, options?)- Register audio handleronVideoMessage(action, options?)- Register video handleronDocumentMessage(action, options?)- Register document handleronStickerMessage(action, options?)- Register sticker handleronLocationMessage(action, options?)- Register location handler
Flow Control:
setNextStep(update, handler, fallback?, fallbackRegex?)- Set next step handlerclearNextStep(phoneNumber)- Clear next step handler
Webhook Processing:
processUpdate(webhookPayload)- Process incoming webhook
Update Object
Available in all handler functions as the first parameter:
interface Update {
bot: WhatsApp;
userDisplayName: string;
userPhoneNumber: string;
messageId: string;
messageText?: string;
message: WhatsAppMessage;
// Media properties (when applicable)
mediaMimeType?: string;
mediaFileId?: string;
mediaHash?: string;
mediaVoice?: boolean;
// Location properties (when applicable)
locLatitude?: number;
locLongitude?: number;
locName?: string;
locAddress?: string;
// Methods
replyMessage(text, options?): Promise<AxiosResponse>;
replyMedia(mediaPath, options?): Promise<AxiosResponse>;
replyTemplate(templateName, components?, languageCode?): Promise<AxiosResponse>;
}UserContext Object
Available in all handler functions as the second parameter:
interface UserContext {
userData: Record<string, any>;
set(key: string, value: any): void;
get<T>(key: string, defaultValue?: T): T | undefined;
has(key: string): boolean;
delete(key: string): void;
clear(): void;
keys(): string[];
size(): number;
}TypeScript Usage
The library is written in TypeScript and provides full type definitions:
import {
WhatsApp,
Update,
UserContext,
InlineKeyboard,
HandlerFunction,
} from 'whatsapp-cloud-bot';
// Type-safe handler
const myHandler: HandlerFunction = async (
update: Update,
context?: UserContext
) => {
// Full autocomplete and type checking
const text: string | undefined = update.messageText;
await update.replyMessage('Hello!');
};
client.onMessage(myHandler);Setting Up a Webhook Server
Express.js Example
import express from 'express';
import { WhatsApp } from 'whatsapp-cloud-bot';
const app = express();
const client = new WhatsApp({
numberId: process.env.WA_PHONE_NUMBER_ID!,
token: process.env.WA_TOKEN!,
});
// Webhook verification (GET)
app.get('/webhook', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.WEBHOOK_VERIFY_TOKEN) {
res.status(200).send(challenge);
} else {
res.sendStatus(403);
}
});
// Webhook handler (POST)
app.post('/webhook', express.json(), async (req, res) => {
try {
await client.processUpdate(req.body);
res.sendStatus(200);
} catch (error) {
console.error('Webhook error:', error);
res.sendStatus(500);
}
});
// Register handlers
client.onMessage(async (update) => {
await update.replyMessage('Hello from Express!');
});
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});Best Practices
Use Environment Variables for sensitive data:
const client = new WhatsApp({ numberId: process.env.WA_PHONE_NUMBER_ID!, token: process.env.WA_TOKEN!, });Handle Errors Gracefully:
client.onMessage(async (update) => { try { await update.replyMessage('Hello!'); } catch (error) { console.error('Failed to send message:', error); } });Use Context for Stateful Conversations:
client.onMessage(async (update, context) => { if (!context.has('started')) { context.set('started', true); await update.replyMessage('Welcome! What is your name?'); } });Format Phone Numbers Consistently:
// Library handles formatting, but ensure you include country code await client.sendMessage('1234567890', 'Hello'); // Good
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT © Radi
Links
Support
If you find this library useful, please consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs
- 💡 Suggesting new features
- 📖 Improving documentation
- 🤝 Contributing code
Made with ❤️ by the community
