Documentation
Complete reference for Socketon v1.8.26
Configuration
javascript
const { makeWASocket, useMultiFileAuthState, Browsers } = require('socketon');
const pino = require('pino');
const sock = makeWASocket({
// Required
auth: authState,
// Optional
logger: pino({ level: 'silent' }),
printQRInTerminal: true,
browser: Browsers('Chrome'),
version: [2, 3000, 1033105955],
connectTimeoutMs: 60000,
keepAliveIntervalMs: 30000,
defaultQueryTimeoutMs: 60000,
retryRequestDelayMs: 250,
maxMsgRetryCount: 5,
fireInitQueries: true,
markOnlineOnConnect: true,
syncFullHistory: false,
printQRInTerminal: true,
emitOwnEvents: true,
customUploadHosts: [],
getMessage: async () => {},
cachedGroupMetadata: async () => {},
shouldIgnoreJid: () => false,
shouldSyncHistoryMessage: () => true,
patchMessageBeforeSending: (msg) => msg,
linkPreviewImageThumbnailWidth: 192,
generateHighQualityLinkPreview: false,
mediaCache: new NodeCache(),
options: {},
mediaUploadTimeoutMs: 60000,
});
Authentication Methods
javascript
// Multi-file auth (recommended)
const { useMultiFileAuthState } = require('socketon');
const { state, saveCreds } = await useMultiFileAuthState('./auth');
// Single file auth
const { useSingleFileAuthState } = require('socketon');
const { state, saveCreds } = await useSingleFileAuthState('./auth.json');
sendMessage Types
javascript
// Text message
await sock.sendMessage(jid, { text: 'Hello!' });
// Image from URL
await sock.sendMessage(jid, {
image: { url: 'https://example.com/image.jpg' },
caption: 'Caption'
});
// Image from Buffer
await sock.sendMessage(jid, {
image: Buffer.from(fs.readFileSync('image.jpg')),
caption: 'Caption'
});
// Video from URL
await sock.sendMessage(jid, {
video: { url: 'https://example.com/video.mp4' },
caption: 'Caption'
});
// Audio from URL
await sock.sendMessage(jid, {
audio: { url: 'https://example.com/audio.mp3' }
});
// Voice note (PTT)
await sock.sendMessage(jid, {
audio: { url: 'https://example.com/audio.mp3' },
ptv: true
});
// Document
await sock.sendMessage(jid, {
document: { url: 'https://example.com/file.pdf' },
fileName: 'document.pdf',
mimeType: 'application/pdf'
});
// Sticker
await sock.sendMessage(jid, {
sticker: { url: 'https://example.com/sticker.webp' }
});
// Sticker from buffer
await sock.sendMessage(jid, {
sticker: fs.readFileSync('sticker.webp')
});
// Location
await sock.sendMessage(jid, {
location: {
degreesLatitude: -6.2088,
degreesLongitude: 106.8456,
name: 'Jakarta',
address: 'Indonesia'
}
});
// Contact
await sock.sendMessage(jid, {
contacts: {
contacts: [{
displayName: 'John Doe',
contacts: [{
waid: '6281234567890',
phone: '+6281234567890',
fullName: 'John Doe'
}]
}]
}
});
// vCard Contact
await sock.sendMessage(jid, {
contacts: {
firstName: 'John',
lastName: 'Doe',
phone: '+6281234567890',
email: 'john@example.com',
organization: 'Company'
}
});
sendMessage Options
javascript
// Reply to message
await sock.sendMessage(jid, { text: 'Reply!' }, { quoted: msg });
// Mention user
await sock.sendMessage(jid, { text: 'Hello @user!' }, { mentions: ['628xxxx@s.whatsapp.net'] });
// Link preview options
await sock.sendMessage(jid, { text: 'Check https://example.com' }, {
linkPreview: {
canonicalUrl: 'https://example.com',
matchedText: 'https://example.com',
title: 'Example',
description: 'Example website',
jpegThumbnail: buffer
}
});
// Disable link preview
await sock.sendMessage(jid, { text: 'No preview' }, { linkPreview: null });
// View once
await sock.sendMessage(jid, { image: buffer }, { viewOnce: true });
// Disappearing message (groups)
await sock.sendMessage(jid, { text: 'Disappearing' }, { ephemeralExpiration: 86400 });
// Edit message
await sock.sendMessage(jid, { text: 'New text' }, { edit: msg.key });
// Delete message
await sock.sendMessage(jid, { delete: msg.key });
// Forward
await sock.sendMessage(jid, { forward: msg });
// Forward without quote
await sock.sendMessage(jid, { forward: msg }, { contextInfo: { forwardingScore: 1 } });
// Background color (audio)
await sock.sendMessage(jid, { audio: buffer }, { backgroundColor: '#FF0000' });
// Font
await sock.sendMessage(jid, { text: 'Styled' }, { font: 0 }); // 0=sans-serif, 1=serif, 2=monospace
Buttons Message
javascript
// With URL button
await sock.sendMessage(jid, {
text: 'Click below',
buttons: [
{ buttonId: 'id1', buttonText: { displayText: 'Button 1' }, type: 1 },
{ buttonId: 'id2', buttonText: { displayText: 'Button 2' }, type: 1 },
{ buttonId: 'url_btn', buttonText: { displayText: 'Open URL' }, type: 2, url: 'https://example.com' }
],
footer: 'Footer text'
});
// With reply button
await sock.sendMessage(jid, {
text: 'Choose option',
buttons: [
{ buttonId: 'opt1', buttonText: { displayText: 'Option 1' }, type: 1 },
{ buttonId: 'opt2', buttonText: { displayText: 'Option 2' }, type: 1 }
],
headerType: 1 // 1=image, 2=video, 3=gif
});
List Message
javascript
await sock.sendMessage(jid, {
text: 'Select option',
sections: [
{
title: 'Section 1',
rows: [
{ id: 'row1', title: 'Row 1', description: 'Description 1' },
{ id: 'row2', title: 'Row 2', description: 'Description 2' }
]
},
{
title: 'Section 2',
rows: [
{ id: 'row3', title: 'Row 3' }
]
}
],
buttonText: 'Select',
title: 'Title',
footer: 'Footer'
});
Template Buttons
javascript
await sock.sendMessage(jid, {
text: 'Template message',
templateButtons: [
{ index: 1, urlButton: { displayText: 'Visit', url: 'https://example.com' } },
{ index: 2, callButton: { displayText: 'Call', phoneNumber: '+6281234567890' } },
{ index: 3, quickReplyButton: { displayText: 'Option 1', id: 'opt1' } },
{ index: 4, quickReplyButton: { displayText: 'Option 2', id: 'opt2' } },
{ index: 5, copyButton: { displayText: 'Copy', couponCode: 'PROMO123' } }
]
});
Poll Message
javascript
await sock.sendMessage(jid, {
poll: {
name: 'Vote for best',
values: ['Option A', 'Option B', 'Option C'],
selectableCount: 1 // 1=single choice, 2+=multiple
}
});
// With options
await sock.sendMessage(jid, {
poll: {
name: 'Survey',
values: ['Yes', 'No', 'Maybe'],
selectableCount: 1,
contextInfo: {
forwardingScore: 1,
isForwarded: true
}
}
});
Interactive Message
javascript
// Native Flow (payment)
await sock.sendMessage(jid, {
interactive: {
header: {
type: 'image',
image: { url: 'image.jpg' },
hasMediaAttachment: true
},
body: { text: 'Body text' },
footer: { text: 'Footer' },
nativeFlowMessage: {
buttons: [
{ name: 'review_and_pay', buttonParamsJson: JSON.stringify({ amount: '10000', currency: 'IDR' }) }
],
messageParamsJson: '{}'
}
}
});
Message Types Reference
| Type | Description |
|---|---|
text | Plain text message |
image | Image message |
video | Video message |
audio | Audio message |
document | Document message |
sticker | Sticker message |
location | Location message |
contacts | Contact message |
buttons | Buttons message |
list | List message |
templateButtons | Template buttons |
poll | Poll message |
react | Reaction |
forward | Forwarded message |
edit | Edited message |
delete | Deleted message |
disappearingMessagesInChat | Disappearing mode |
Group Methods
| Method | Description |
|---|---|
groupCreate(subject, participants) | Create new group |
groupLeave(id) | Leave group |
groupUpdateSubject(jid, subject) | Update subject |
groupUpdateDescription(jid, description) | Update description |
groupParticipantsUpdate(jid, participants, action) | Action: add, remove, promote, demote |
groupInviteCode(jid) | Get invite code |
groupRevokeInvite(jid) | Revoke invite code |
groupAcceptInvite(code) | Join via code |
groupAcceptInviteV4(key, inviteMessage) | Accept invite v4 |
groupGetInviteInfo(code) | Get invite info |
groupToggleEphemeral(jid, expiration) | Set disappearing |
groupSettingUpdate(jid, setting) | Setting: announcement, locked, not_announcement, unlocked |
groupMemberAddMode(jid, mode) | Set member mode |
groupJoinApprovalMode(jid, mode) | Set approval mode |
groupRequestParticipantsList(jid) | Get request list |
groupRequestParticipantsUpdate(jid, participants, action) | Approve/reject |
groupMetadata(jid) | Get metadata |
groupFetchAllParticipating() | Get all groups |
Newsletter Methods
| Method | Description |
|---|---|
newsletterFetchAllSubscribe() | Get all subscribed |
subscribeNewsletterUpdates(jid) | Subscribe updates |
newsletterId(url) | Get ID from URL |
newsletterFollow(jid) | Follow |
newsletterUnfollow(jid) | Unfollow |
newsletterMute(jid) | Mute |
newsletterUnmute(jid) | Unmute |
newsletterUpdateName(jid, name) | Update name |
newsletterUpdateDescription(jid, description) | Update description |
newsletterUpdatePicture(jid, content) | Update picture |
newsletterRemovePicture(jid) | Remove picture |
newsletterReactionMode(jid, mode) | Set reaction mode |
newsletterReactMessage(jid, serverId, code) | React to message |
newsletterAutoReact(newsletterId, messageId, reactions) | Auto react |
newsletterFetchMessages(type, key, count, after) | Fetch messages |
newsletterFetchUpdates(jid, count, after, since) | Fetch updates |
newsletterMetadata(type, key, role) | Get metadata |
newsletterAdminCount(jid) | Get admin count |
newsletterChangeOwner(jid, user) | Change owner |
newsletterDemote(jid, user) | Demote admin |
newsletterDelete(jid) | Delete |
newsletterCreate(name, description, reaction_codes) | Create new |
Profile Methods
| Method | Description |
|---|---|
updateProfileName(name) | Update name |
updateProfilePicture(jid, content) | Update picture |
removeProfilePicture(jid) | Remove picture |
updateProfileStatus(status) | Update status |
profilePictureUrl(jid, type, timeout) | Get picture URL |
updateBlockStatus(jid, action) | Block/unblock |
fetchBlocklist() | Get blocklist |
getBusinessProfile(jid) | Get business profile |
Privacy Methods
| Method | Description |
|---|---|
fetchPrivacySettings(force) | Get settings |
updateLastSeenPrivacy(value) | all, contacts, none |
updateOnlinePrivacy(value) | all, contacts, none |
updateProfilePicturePrivacy(value) | all, contacts, none |
updateStatusPrivacy(value) | all, contacts, none |
updateReadReceiptsPrivacy(value) | all, none |
updateGroupsAddPrivacy(value) | all, contacts, none |
getPrivacyTokens(jids) | Get tokens |
Chat Methods
| Method | Description |
|---|---|
chatModify(mod, jid) | Modify chat |
star(jid, messages, star) | Star/unstar |
addChatLabel(jid, labelId) | Add label |
removeChatLabel(jid, labelId) | Remove label |
addMessageLabel(jid, messageId, labelId) | Add message label |
removeMessageLabel(jid, messageId, labelId) | Remove message label |
updateDefaultDisappearingMode(duration) | Set default disappearing |
Presence Methods
| Method | Description |
|---|---|
sendPresenceUpdate(type, toJid) | Update presence |
presenceSubscribe(toJid, tcToken) | Subscribe |
Media Methods
| Method | Description |
|---|---|
refreshMediaConn(forceGet) | Refresh media conn |
downloadMediaMessage(msg, type, options, ctx) | Download media |
updateMediaMessage(message) | Update media |
generateLinkPreviewIfRequired(text, getUrlInfo, logger) | Generate preview |
Business Methods
| Method | Description |
|---|---|
getCatalog({jid, limit, cursor}) | Get catalog |
getCollections(jid, limit) | Get collections |
getOrderDetails(orderId, tokenBase64) | Get order |
productCreate(create) | Create product |
productUpdate(productId, update) | Update product |
productDelete(productIds) | Delete product |
Registration Methods
| Method | Description |
|---|---|
register(code) | Register with code |
requestRegistrationCode(options) | Request code |
mobileRegister(params, fetchOptions) | Mobile register |
mobileRegisterCode(params, fetchOptions) | Mobile request code |
mobileRegisterExists(params, fetchOptions) | Check registered |
Call Methods
| Method | Description |
|---|---|
offerCall(toJid, isVideo) | Make call |
rejectCall(callId, callFrom) | Reject call |
Utility Functions
| Function | Description |
|---|---|
generateMessageID() | Generate message ID |
generateWAMessage(jid, content, options) | Generate WA message |
generateWAMessageFromContent(jid, message, options) | Generate from content |
getContentType(content) | Get content type |
extractMessageContent(content) | Extract content |
| Normalize content | |
extractUrlFromText(text) | Extract URL |
getDevice(id) | Get device type |
Events
| Event | Data |
|---|---|
connection.update | { connection, qr, lastDisconnect, isOnline, receivedPendingNotifications } |
messages.upsert | { messages, type } |
messages.update | [{ key, update }] |
messages.media-update | [{ key, error, media }] |
presence.update | { id, presences } |
chats.upsert | [chats] |
chats.update | [updates] |
contacts.upsert | [contacts] |
contacts.update | [updates] |
groups.upsert | [groups] |
groups.update | [updates] |
call | [calls] |
creds.update | creds |
newsletter.reaction | { id, server_id, reaction } |
newsletter.view | { id, server_id, count } |
newsletter-participants.update | { id, author, user, new_role, action } |
newsletter-settings.update | { id, update } |
blocklist.update | { blocklist, type } |
message-receipt.update | [{ key, receipt }] |
Message Key
javascript
// msg.key structure
{
remoteJid: '628xxxx@s.whatsapp.net',
fromMe: true,
id: 'ABCDEF1234567890',
participant: '628xxxx@s.whatsapp.net' // for groups
}
// msg.message structure
{
conversation: 'text message',
extendedTextMessage: {
text: 'text',
contextInfo: {
quotedMessage: {},
mentionedJid: [],
forwardingScore: 0,
isForwarded: false
}
},
imageMessage: {
url: 'https://...',
mimetype: 'image/jpeg',
fileLength: '12345',
height: 100,
width: 100,
mediaKey: '...',
caption: 'caption'
},
videoMessage: { ... },
audioMessage: { ... },
documentMessage: { ... },
stickerMessage: { ... },
locationMessage: { ... },
contactMessage: { ... }
}
JID Formats
| Type | Format |
|---|---|
| Personal | 628xxxx@s.whatsapp.net |
| Group | group@s.whatsapp.net |
| Status | status@broadcast |
| Newsletter | id@newsletter |
| LID | xxxx@lid |
Error Handling
javascript
const { DisconnectReason } = require('socketon');
const Boom = require('@hapi/boom');
sock.ev.on('connection.update', async (update) => {
const { connection, lastDisconnect } = update;
if (connection === 'close') {
const statusCode = lastDisconnect?.error?.output?.statusCode;
switch (statusCode) {
case DisconnectReason.loggedOut:
// Session invalid, re-authenticate
console.log('Logged out');
break;
case DisconnectReason.connectionClosed:
// Connection closed, reconnect
console.log('Connection closed');
break;
case DisconnectReason.connectionLost:
// Connection lost, reconnect
console.log('Connection lost');
break;
case DisconnectReason.restartRequired:
// Restart required
console.log('Restart required');
break;
case DisconnectReason.badSession:
// Bad session, clear and restart
console.log('Bad session');
break;
}
}
});
// Handle specific errors
try {
await sock.sendMessage(jid, { text: 'Hello' });
} catch (error) {
if (Boom.isBoom(error)) {
console.log(error.output.statusCode);
console.log(error.message);
}
}
Complete Example
javascript
const { makeWASocket, useMultiFileAuthState, DisconnectReason, Browsers } = require('socketon');
const pino = require('pino');
const fs = require('fs');
class Bot {
constructor() {
this sock = null;
this.prefix = '!';
}
async start() {
const { state, saveCreds } = await useMultiFileAuthState('./auth');
this.sock = makeWASocket({
logger: pino({ level: 'info' }),
auth: state,
printQRInTerminal: true,
browser: Browsers('Chrome'),
connectTimeoutMs: 60000,
keepAliveIntervalMs: 30000
});
this.sock.ev.on('connection.update', this.handleConnectionUpdate.bind(this));
this.sock.ev.on('messages.upsert', this.handleMessages.bind(this));
this.sock.ev.on('creds.update', saveCreds);
}
handleConnectionUpdate(update) {
const { connection, lastDisconnect, qr } = update;
if (qr) {
console.log('Scan QR:', qr);
}
if (connection === 'close') {
const reason = lastDisconnect?.error?.output?.statusCode;
const shouldReconnect = reason !== DisconnectReason.loggedOut;
if (shouldReconnect) {
console.log('Reconnecting...');
this.start();
}
} else if (connection === 'open') {
console.log('Connected!');
}
}
async handleMessages({ messages, type }) {
if (type !== 'notify') return;
for (const msg of messages) {
if (!msg.message || msg.key.fromMe) continue;
const text = msg.message.conversation || msg.message.extendedTextMessage?.text;
if (!text) continue;
console.log(`From ${msg.key.remoteJid}: ${text}`);
// Command handler
if (text.startsWith(this.prefix)) {
const [cmd, ...args] = text.slice(1).split(' ');
await this.handleCommand(msg, cmd.toLowerCase(), args);
}
}
}
async handleCommand(msg, cmd, args) {
const jid = msg.key.remoteJid;
switch (cmd) {
case 'ping':
await this.sock.sendMessage(jid, { text: 'Pong!' }, { quoted: msg });
break;
case 'help':
await this.sock.sendMessage(jid, {
text: `Available commands:
- !ping
- !help
- !about
- !broadcast [text]`
}, { quoted: msg });
break;
case 'about':
await this.sock.sendMessage(jid, {
text: 'Socketon Bot v1.8.26\nDeveloper: Ibra Decode'
}, { quoted: msg });
break;
case 'broadcast':
// Broadcast to all (requires group list)
const broadcastText = args.join(' ');
await this.sock.sendMessage(jid, { text: `Broadcast: ${broadcastText}` });
break;
default:
await this.sock.sendMessage(jid, {
text: `Unknown command: ${cmd}`
}, { quoted: msg });
}
}
}
const bot = new Bot();
bot.start().catch(console.error);
Group Metadata Structure
javascript
// Group metadata response
{
id: 'group@s.whatsapp.net',
addressingMode: 'gid',
subject: 'Group Name',
subjectOwner: '628xxxx@s.whatsapp.net',
subjectTime: 1234567890,
creation: 1234567890,
owner: '628xxxx@s.whatsapp.net',
desc: 'Group description',
descId: 'ABC123',
descOwner: '628xxxx@s.whatsapp.net',
descTime: 1234567890,
restrict: false,
announce: false,
isCommunity: false,
isCommunityAnnounce: false,
joinApprovalMode: false,
memberAddMode: 'all_member_add',
size: 50,
participants: [
{
id: '628xxxx@s.whatsapp.net',
jid: '628xxxx@s.whatsapp.net',
admin: 'admin' // 'admin' | 'superadmin' | null
}
],
ephemeralDuration: 604800 // seconds
}
Group Participant Actions
javascript
const groupJid = '1234567890@g.us';
// Add participant
const addResult = await sock.groupParticipantsUpdate(groupJid, [
'6281111111111@s.whatsapp.net',
'6282222222222@s.whatsapp.net'
], 'add');
console.log(addResult);
// [{ jid: '...', status: '200' }]
// Remove participant
await sock.groupParticipantsUpdate(groupJid, [
'6281111111111@s.whatsapp.net'
], 'remove');
// Promote to admin
await sock.groupParticipantsUpdate(groupJid, [
'6281111111111@s.whatsapp.net'
], 'promote');
// Demote to member
await sock.groupParticipantsUpdate(groupJid, [
'6281111111111@s.whatsapp.net'
], 'demote');
// Handle result
const result = await sock.groupParticipantsUpdate(groupJid, participants, 'add');
result.forEach(r => {
if (r.status === '200') {
console.log(`Success: ${r.jid}`);
} else {
console.log(`Error: ${r.jid} - ${r.status}`);
}
});
Group Settings
javascript
const groupJid = '1234567890@g.us';
// Enable announcement mode (only admins can send)
await sock.groupSettingUpdate(groupJid, 'announcement');
// Disable announcement mode
await sock.groupSettingUpdate(groupJid, 'not_announcement');
// Lock group (only admins can modify settings)
await sock.groupSettingUpdate(groupJid, 'locked');
// Unlock group
await sock.groupSettingUpdate(groupJid, 'unlocked');
// Set disappearing messages
await sock.groupToggleEphemeral(groupJid, 604800); // 7 days in seconds
// Disable disappearing messages
await sock.groupToggleEphemeral(groupJid, 0);
// Set member add mode
await sock.groupMemberAddMode(groupJid, 'all_member_add'); // anyone can add
await sock.groupMemberAddMode(groupJid, 'admin_add_member_only'); // only admins
// Set join approval mode
await sock.groupJoinApprovalMode(groupJid, 'on'); // require approval
await sock.groupJoinApprovalMode(groupJid, 'off'); // no approval needed
Group Invite Management
javascript
const groupJid = '1234567890@g.us';
// Get invite code
const inviteCode = await sock.groupInviteCode(groupJid);
console.log('Invite link: https://chat.whatsapp.com/' + inviteCode);
// Revoke invite code (generates new code)
const newCode = await sock.groupRevokeInvite(groupJid);
console.log('New link: https://chat.whatsapp.com/' + newCode);
// Join via code
const joinedJid = await sock.groupAcceptInvite(inviteCode);
console.log('Joined:', joinedJid);
// Get invite info before joining
const inviteInfo = await sock.groupGetInviteInfo(inviteCode);
console.log(inviteInfo);
// {
// id: 'group@g.us',
// subject: 'Group Name',
// creation: 1234567890,
// owner: 'owner@g.us',
// size: 50
// }
// Accept invite from message
const key = { remoteJid: inviterJid, id: messageId };
await sock.groupAcceptInviteV4(key, inviteMessage);
Newsletter Advanced
javascript
// Get ID from URL
const nlId = await sock.newsletterId('https://newsletter.com/channel/name');
// or
const nlId = await sock.newsletterId('newsletter.com/channel/name');
// Follow newsletter
await sock.newsletterFollow(nlId);
// Get metadata
const meta = await sock.newsletterMetadata('id', nlId);
console.log({
name: meta.name,
description: meta.description,
subscribers: meta.subscribers,
threads: meta.threads,
pictureUrl: meta.pictureUrl
});
// Fetch messages
const messages = await sock.newsletterFetchMessages('id', nlId, 20);
for (const msg of messages.messages) {
console.log({
id: msg.key.id,
content: msg.message?.conversation || msg.message?.extendedTextMessage?.text
});
}
// Fetch updates
const updates = await sock.newsletterFetchUpdates(nlId, 10);
console.log(updates);
// Subscribe to live updates
await sock.subscribeNewsletterUpdates(nlId);
// Create newsletter
const newNL = await sock.newsletterCreate({
name: 'My Channel',
description: 'My newsletter channel',
reaction_codes: ['👍', '❤️', '🎉']
});
console.log('Created:', newNL);
Newsletter Auto React
javascript
// Manual reaction
await sock.newsletterReactMessage(newsletterId, messageServerId, '👍');
// Auto react setup (handled internally via config)
const reactions = ['👍', '❤️', '🔥', '🎉', '👏'];
const randomReaction = reactions[Math.floor(Math.random() * reactions.length)];
await sock.newsletterReactMessage(nlId, msgId, randomReaction);
// Auto react function
async function autoReact(newsletterId, messageId) {
const reactions = ['👍', '❤️', '🔥', '🎉', '👏', '😄', '🤩'];
const reaction = reactions[Math.floor(Math.random() * reactions.length)];
await sock.newsletterReactMessage(newsletterId, messageId, reaction);
}
// Listen to newsletter messages
sock.ev.on('messages.upsert', async ({ messages, type }) => {
if (type !== 'notify') return;
for (const msg of messages) {
if (msg.key.remoteJid.includes('@newsletter')) {
// Auto react
await autoReact(msg.key.remoteJid, msg.key.id);
// Or reply
await sock.sendMessage(msg.key.remoteJid, {
text: 'Thanks for the update!'
});
}
}
});
Media Upload
javascript
const fs = require('fs');
const path = require('path');
// Upload file to WhatsApp server
const fileBuffer = fs.readFileSync('./image.jpg');
const mediaUrl = await sock.waUploadToServer(fileBuffer, {
mediaType: 'image',
fileName: 'image.jpg'
});
console.log('Uploaded:', mediaUrl.url);
// Send with uploaded URL
await sock.sendMessage(jid, {
image: { url: mediaUrl.url },
caption: 'Using uploaded URL'
});
// Refresh media connection
await sock.refreshMediaConn(true);
// Generate thumbnail
const { generateThumbnail } = require('socketon').Utils;
const thumbnail = await generateThumbnail('./image.jpg', 'image', {
logger: pino({ level: 'silent' })
});
// Download media
const { downloadMediaMessage, extractMessageContent } = require('socketon').WAProto;
const mediaBuffer = await downloadMediaMessage(msg, 'buffer', {}, {
sock: sock,
logger: pino({ level: 'silent' })
});
fs.writeFileSync('downloaded.jpg', mediaBuffer);
Link Preview
javascript
// Automatic link preview (default)
await sock.sendMessage(jid, {
text: 'Check https://example.com'
});
// Custom link preview
await sock.sendMessage(jid, {
text: 'Visit our site',
linkPreview: {
canonicalUrl: 'https://example.com',
matchedText: 'https://example.com',
title: 'Example Website',
description: 'This is an example website',
jpegThumbnail: thumbnailBuffer
}
});
// Disable link preview
await sock.sendMessage(jid, {
text: 'No preview for this',
linkPreview: null
});
// Generate link preview manually
const { getUrlInfo } = require('socketon').Utils;
const urlInfo = await getUrlInfo('https://example.com', {
thumbnailWidth: 192,
fetchOpts: { timeout: 3000 },
logger: pino({ level: 'silent' }),
uploadImage: sock.waUploadToServer
});
await sock.sendMessage(jid, {
text: 'https://example.com',
linkPreview: urlInfo
});
Message Edit & Delete
javascript
// Edit message (within 24 hours)
await sock.sendMessage(jid, { text: 'Original' }, { edit: originalMsg.key });
// Delete message (revoke for everyone)
await sock.sendMessage(jid, { delete: msg.key });
// Delete for me only
// Note: Not directly supported, use chatModify
// Edit message response
sock.ev.on('messages.update', async ({ updates }) => {
for (const update of updates) {
if (update.update?.editedMessage) {
console.log('Message edited:', update.key.id);
}
if (update.update?.message) {
console.log('Message deleted/revoked:', update.key.id);
}
}
});
Message Reactions
javascript
// Send reaction
await sock.sendMessage(jid, {
react: {
key: targetMsg.key,
text: '👍'
}
});
// Remove reaction
await sock.sendMessage(jid, {
react: {
key: targetMsg.key,
text: ''
}
});
// Listen for reactions
sock.ev.on('messages.update', async ({ updates }) => {
for (const update of updates) {
if (update.update?.reactions) {
console.log('Reactions updated:', update.key.id);
console.log(update.update.reactions);
}
}
});
// Reaction types (emoji)
const reactions = ['👍', '👎', '❤️', '😄', '😢', '😮', '😱', '🔥', '🎉', '👏'];
Typing & Recording
javascript
// Send typing (composing)
await sock.sendPresenceUpdate('composing', jid);
// Send recording
await sock.sendPresenceUpdate('recording', jid);
// Pause typing
await sock.sendPresenceUpdate('paused', jid);
// Available
await sock.sendPresenceUpdate('available');
// Unavailable
await sock.sendPresenceUpdate('unavailable');
// Subscribe to presence
sock.presenceSubscribe(jid);
// Listen to presence
sock.ev.on('presence.update', ({ id, presences }) => {
for (const [participant, data] of Object.entries(presences)) {
console.log(participant, data.lastKnownPresence);
if (data.lastSeen) {
console.log('Last seen:', new Date(data.lastSeen * 1000));
}
}
});
Business Catalog
javascript
// Get business profile
const bizProfile = await sock.getBusinessProfile('628xxxx@s.whatsapp.net');
console.log({
wid: bizProfile.wid,
address: bizProfile.address,
description: bizProfile.description,
website: bizProfile.website,
email: bizProfile.email,
category: bizProfile.category,
businessHours: bizProfile.business_hours
});
// Get catalog
const catalog = await sock.getCatalog({
jid: '628xxxx@s.whatsapp.net',
limit: 20,
cursor: null
});
console.log('Products:', catalog.products);
console.log('Next cursor:', catalog.cursor);
// Get collections
const collections = await sock.getCollections('628xxxx@s.whatsapp.net', 20);
console.log(collections);
// Get order details
const order = await sock.getOrderDetails(orderId, tokenBase64);
console.log({
orderId: order.orderId,
status: order.status,
items: order.items,
total: order.total
});
Story (Status)
javascript
// Send text status
await sock.sendMessage('status@broadcast', {
text: 'My status!'
});
// Send image status
await sock.sendMessage('status@broadcast', {
image: { url: 'https://example.com/image.jpg' },
caption: 'My image status'
});
// Send video status
await sock.sendMessage('status@broadcast', {
video: { url: 'https://example.com/video.mp4' }
});
// Delete status (own status only)
await sock.sendMessage('status@broadcast', {
delete: statusMsg.key
});
// View status from contacts
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (msg.key.remoteJid === 'status@broadcast' && !msg.key.fromMe) {
console.log('Status from:', msg.key.participant);
// Download status media
if (msg.message?.imageMessage) {
const buffer = await sock.downloadMediaMessage(msg);
}
}
}
});
Label Management
javascript
// Note: Labels are managed via app state patches
// Add label to chat
await sock.addChatLabel(jid, labelId);
// Remove label from chat
await sock.removeChatLabel(jid, labelId);
// Add label to message
await sock.addMessageLabel(jid, messageId, labelId);
// Remove label from message
await sock.removeMessageLabel(jid, messageId, labelId);
// Note: Label IDs need to be obtained from existing labels
// Use appPatch or resyncAppState to fetch labels
Advanced: Custom Handlers
javascript
// Socketon Advanced Handler for complex messages
const handler = sock.socketonHandler;
// Detect message type
const type = handler.detectType(messageContent);
console.log(type);
// 'PAYMENT', 'PRODUCT', 'INTERACTIVE', 'ALBUM', 'EVENT',
// 'POLL_RESULT', 'GROUP_STORY', 'STICKER_PACK'
// Handle payment
const paymentMsg = await handler.handlePayment(content, quoted);
// Handle product
const productMsg = await handler.handleProduct(content, jid, quoted);
// Handle interactive
const interactiveMsg = await handler.handleInteractive(content, jid, quoted);
// Handle album
const albumMsgs = await handler.handleAlbum(content, jid, quoted);
// Handle event
const eventMsg = await handler.handleEvent(content, jid, quoted);
// Handle poll result
const pollResult = await handler.handlePollResult(content, jid, quoted);
// Handle group story
const groupStory = await handler.handleGroupStory(content, jid, quoted);
// Handle sticker pack
const stickerPack = await handler.handleStickerPack(content, jid, quoted);
Advanced: Signal Encryption
javascript
// Signal repository for E2E encryption
const signalRepo = sock.signalRepository;
// Encrypt message for group
const { ciphertext, senderKeyDistributionMessage } = await signalRepo.encryptGroupMessage({
group: groupJid,
data: messageBytes,
meId: myJid
});
// Encrypt message for user
const { type, ciphertext } = await signalRepo.encryptMessage({
jid: userJid,
data: messageBytes
});
// Decrypt message
const decrypted = await signalRepo.decryptMessage({
jid: userJid,
id: messageId,
type: encType,
ciphertext: encBytes
});
// Get sender key
const senderKey = await signalRepo.getSenderKey(groupJid, userJid);
// Store sender key
await signalRepo.storeSenderKey(groupJid, userJid, senderKey);
Advanced: Session Management
javascript
// Multi-device session management
const { state, saveCreds } = await useMultiFileAuthState('./auth');
// Auth state structure
console.log(state.creds);
// {
// me: { id: 'user@g.us', name: 'Name', lid: 'lid' },
// pairedMe: { ... },
// phoneNumber: '+628...',
// deviceId: '...',
// pairedDeviceId: '...',
// registrationId: 12345,
// backupToken: '...',
// signedDeviceIdentity: { ... },
// keys: { ... },
// account: { ... },
// session: { ... }
// }
// Check if authenticated
if (state.creds.me) {
console.log('Authenticated as:', state.creds.me.name);
}
// Get device info
console.log('My JID:', state.creds.me.id);
console.log('LID:', state.creds.me.lid);
console.log('Device ID:', state.creds.deviceId);
Advanced: Query Custom
javascript
// Custom IQ query
const result = await sock.query({
tag: 'iq',
attrs: {
type: 'get',
to: 'jabber.org',
xmlns: 'jabber:iq:roster'
}
});
// Query with timeout
const result2 = await sock.query({
tag: 'iq',
attrs: { type: 'get', to: 'recipient@g.us' },
content: [{ tag: 'query', attrs: {} }]
}, 30000); // 30 second timeout
// Send raw node
await sock.sendNode({
tag: 'presence',
attrs: { type: 'available' }
});
TypeScript Types
typescript
import { makeWASocket, DisconnectReason, WAMessage, WAMessageKey } from 'socketon';
interface BotConfig {
auth: AuthState;
logger?: Pino;
printQRInTerminal?: boolean;
}
interface MessageHandler {
messages: WAMessage[];
type: 'notify' | 'replace';
}
interface GroupMetadata {
id: string;
subject: string;
owner: string;
participants: Participant[];
announce: boolean;
restrict: boolean;
}
interface Participant {
id: string;
admin: 'admin' | 'superadmin' | null;
}
// Type-safe message handling
function handleMessage(msg: WAMessage) {
const key: WAMessageKey = msg.key;
const message = msg.message;
if (message?.conversation) {
console.log('Text:', message.conversation);
}
if (message?.imageMessage) {
console.log('Image:', message.imageMessage.caption);
}
}
Troubleshooting
javascript
// Common errors and solutions
// 1. QR Code not showing
// Solution: Ensure terminal supports ANSI codes
const sock = makeWASocket({
printQRInTerminal: true,
logger: pino({ level: 'debug' })
});
// 2. Session expired
// Solution: Clear auth and re-authenticate
const { state, saveCreds } = await useMultiFileAuthState('./auth');
// Delete ./auth folder if needed and restart
// 3. Connection timeout
// Solution: Increase timeout
const sock = makeWASocket({
connectTimeoutMs: 120000,
defaultQueryTimeoutMs: 120000
});
// 4. Rate limiting
// Solution: Add delays between messages
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
for (const jid of jids) {
await sock.sendMessage(jid, { text: 'Message' });
await delay(1000); // 1 second delay
}
// 5. Media upload failed
// Solution: Check file size (max 16MB for images, 64MB for videos)
const stats = fs.statSync('./file.jpg');
if (stats.size > 16 * 1024 * 1024) {
console.log('File too large');
}
// 6. Message not sending
// Solution: Check if valid JID
if (!isJidUser(jid) && !isJidGroup(jid) && !isJidBroadcast(jid)) {
console.log('Invalid JID');
}
Best Practices
javascript
// 1. Use proper error handling
try {
await sock.sendMessage(jid, { text: 'Hello' });
} catch (error) {
if (Boom.isBoom(error)) {
console.log(error.output.statusCode);
}
}
// 2. Save credentials regularly
sock.ev.on('creds.update', saveCreds);
// 3. Use connection handlers properly
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
if (connection === 'close') {
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
if (shouldReconnect) {
// Reconnect logic
}
}
});
// 4. Don't spam messages
// Use rate limiting and delays
// 5. Handle media properly
// Use streaming for large files
// 6. Use appropriate logger levels
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
// 7. Cache group metadata
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => {
// Your caching logic
return cachedData;
}
});
// 8. Clean up on shutdown
process.on('SIGINT', async () => {
await sock.end();
});
Deployment
package.json
{
"name": "socketon-bot",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"dependencies": {
"socketon": "^1.8.26",
"pino": "^8.0.0",
"pino-pretty": "^10.0.0"
},
"engines": {
"node": ">=20.0.0"
}
}
PM2 Process Manager
bash
# Install PM2
npm install -g pm2
# Start bot
pm2 start index.js --name socketon-bot
# Restart
pm2 restart socketon-bot
# Stop
pm2 stop socketon-bot
# View logs
pm2 logs socketon-bot
# Auto-restart on crash
pm2 start index.js --name socketon-bot --exp-backoff-restart-delay=100
# Auto-restart on file change (development)
pm2 start index.js --name socketon-bot --watch
Docker
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "index.js"]
docker-compose.yml
version: '3.8'
services:
bot:
build: .
volumes:
- ./auth:/app/auth
restart: unless-stopped
environment:
- NODE_ENV=production
Environment Variables
.env
# Bot Configuration
LOG_LEVEL=info
BOT_PREFIX=!
# Optional: Custom auth directory
AUTH_DIR=./auth
# Connection
CONNECT_TIMEOUT=60000
KEEP_ALIVE_INTERVAL=30000
Version History
| Version | Changes |
|---|---|
| 1.8.26 | Enhanced encryption, newsletter improvements, auto features |
| 1.8.25 | Bug fixes, performance improvements |
| 1.8.20 | Added advanced message handlers |
| 1.8.15 | Initial stable release |
Credits
Phone Number Verification
javascript
// Check if number is on WhatsApp
const result = await sock.onWhatsApp('6281234567890');
console.log(result);
// { jid: '6281234567890@s.whatsapp.net', exists: true, lid: '...' }
// Check multiple numbers
const results = await sock.onWhatsApp(
'6281111111111',
'6282222222222',
'6283333333333'
);
results.forEach(r => {
console.log(`${r.jid}: ${r.exists ? 'Available' : 'Not found'}`);
});
// Check with business account
const result = await sock.onWhatsApp('6281234567890');
if (result.exists && result.status === 'business') {
console.log('Business account:', result.businessInfo);
}
Contact Status
javascript
// Get contact status (about)
const status = await sock.fetchStatus('628xxxx@s.whatsapp.net');
console.log({
status: status.status, // "Hey there! I am using WhatsApp"
setAt: status.setAt // Date object
});
// Profile picture
const url = await sock.profilePictureUrl('628xxxx@s.whatsapp.net');
console.log('Profile URL:', url);
// High res: await sock.profilePictureUrl('628xxxx@s.whatsapp.net', 'image', 10000)
Contact Presence
javascript
// Subscribe to contact presence
await sock.presenceSubscribe('628xxxx@s.whatsapp.net');
// Listen for presence
sock.ev.on('presence.update', ({ id, presences }) => {
// id = phone number
// presences = { 'jid': { lastKnownPresence, lastSeen } }
for (const [jid, data] of Object.entries(presences)) {
console.log(`${jid} is ${data.lastKnownPresence}`);
if (data.lastSeen) {
console.log(`Last seen: ${new Date(data.lastSeen * 1000)}`);
}
}
});
// Presence types:
// - 'available' - Online
// - 'unavailable' - Offline
// - 'composing' - Typing
// - 'recording' - Recording voice
// - 'paused' - Stopped typing
Archive & Pin Chats
javascript
// Archive chat
await sock.chatModify({ archive: true }, jid);
// Unarchive chat
await sock.chatModify({ archive: false }, jid);
// Pin chat
await sock.chatModify({ pin: true }, jid);
// Unpin chat
await sock.chatModify({ pin: false }, jid);
// Mute chat (24 hours = 86400 seconds)
await sock.chatModify({ mute: 86400 }, jid);
// Mute for 1 week
await sock.chatModify({ mute: 604800 }, jid);
// Unmute
await sock.chatModify({ mute: null }, jid);
// Delete chat
await sock.chatModify({ delete: true }, jid);
// Mark as unread
await sock.chatModify({ markRead: false }, jid);
Star Messages
javascript
// Star message
await sock.star(jid, [messageId], true);
// Unstar message
await sock.star(jid, [messageId], false);
// Star multiple messages
await sock.star(jid, [msgId1, msgId2, msgId3], true);
Disappearing Messages
javascript
// Set disappearing mode (chat)
await sock.chatModify({ disappearingMessagesInChat: true }, jid);
// Disable
await sock.chatModify({ disappearingMessagesInChat: false }, jid);
// Set duration (seconds)
// 0 = off
// 86400 = 24 hours
// 604800 = 7 days
await sock.chatModify({ disappearingMessagesInChat: 604800 }, jid);
// Set group disappearing
await sock.groupToggleEphemeral(groupJid, 604800);
// Disable group disappearing
await sock.groupToggleEphemeral(groupJid, 0);
// Set default for new chats
await sock.updateDefaultDisappearingMode(86400);
App State Sync
javascript
// Resync app state
await sock.resyncAppState(['contacts', 'chats', 'groups'], false);
// Full sync
await sock.resyncAppState(['contacts', 'chats', 'groups', 'profile', 'privacy'], true);
// Handle app state mutations
sock.ev.on('contacts.upsert', (contacts) => {
console.log('New contacts:', contacts);
});
sock.ev.on('chats.update', (updates) => {
for (const update of updates) {
console.log('Chat updated:', update.id, update);
}
});
Message Processing
javascript
// Different message types handling
sock.ev.on('messages.upsert', async ({ messages, type }) => {
for (const msg of messages) {
if (!msg.message) continue;
const message = msg.message;
// Text
if (message.conversation) {
console.log('Text:', message.conversation);
}
// Extended text (with link preview)
if (message.extendedTextMessage) {
console.log('Extended:', message.extendedTextMessage.text);
console.log('Matched:', message.extendedTextMessage.matchedText);
}
// Image
if (message.imageMessage) {
console.log('Image caption:', message.imageMessage.caption);
console.log('Mimetype:', message.imageMessage.mimetype);
}
// Video
if (message.videoMessage) {
console.log('Video:', message.videoMessage.caption);
}
// Audio
if (message.audioMessage) {
console.log('PTT:', message.audioMessage.ptt); // voice note
}
// Document
if (message.documentMessage) {
console.log('File:', message.documentMessage.fileName);
}
// Sticker
if (message.stickerMessage) {
console.log('Emoji:', message.stickerMessage.firstScanEmoji);
}
// Location
if (message.locationMessage) {
console.log('Location:', message.locationMessage.degreesLatitude, message.locationMessage.degreesLongitude);
}
// Contact
if (message.contactMessage || message.contactsArrayMessage) {
console.log('Contact received');
}
// Buttons response
if (message.buttonsResponseMessage) {
console.log('Button:', message.buttonsResponseMessage.selectedButtonId);
}
// List response
if (message.listResponseMessage) {
console.log('List:', message.listResponseMessage.singleSelectReply.selectedRowId);
}
// Poll
if (message.pollCreationMessage) {
console.log('Poll:', message.pollCreationMessage.name);
}
// Group invite
if (message.groupInviteMessage) {
console.log('Invite:', message.groupInviteMessage.inviteCode);
}
}
});
Message Context Info
javascript
// When receiving a message with contextInfo
const msg = messages[0];
const context = msg.message?.extendedTextMessage?.contextInfo;
// Quoted message
if (context?.quotedMessage) {
const quoted = context.quotedMessage;
console.log('Quoted from:', context.participant);
console.log('Quoted ID:', context.stanzaId);
}
// Mentioned users
if (context?.mentionedJid) {
console.log('Mentions:', context.mentionedJid);
}
// Forward info
if (context?.forwardingScore) {
console.log('Forwarded', context.forwardingScore, 'times');
}
// Send message with context
await sock.sendMessage(jid, {
text: 'Hello @user!',
mentions: ['628xxxx@s.whatsapp.net'],
contextInfo: {
stanzaId: quotedMsg.key.id,
participant: quotedMsg.key.remoteJid,
quotedMessage: quotedMsg.message,
forwardingScore: 1,
isForwarded: true
}
});
Call Handling
javascript
// Handle incoming calls
sock.ev.on('call', async (calls) => {
for (const call of calls) {
console.log('Call from:', call.from);
console.log('ID:', call.id);
console.log('Status:', call.status);
console.log('Is Video:', call.isVideo);
console.log('Is Group:', call.isGroup);
// Reject call
if (call.status === 'offer') {
await sock.rejectCall(call.id, call.from);
}
// Make call
const result = await sock.offerCall(call.from, false); // false = voice, true = video
console.log('Call ID:', result.callId);
}
});
// Call status types: 'offer', 'ringing', 'accept', 'reject', 'terminate', 'timeout'
Registration (Phone Auth)
javascript
// Request registration code
const result = await sock.requestRegistrationCode({
phoneNumber: '+6281234567890',
phoneNumberCountryCode: '62',
phoneNumberNationalNumber: '81234567890',
phoneNumberMobileCountryCode: '510',
method: 'sms' // or 'voice'
});
console.log(result);
// Register with code
await sock.register('123456');
// Mobile registration
const regResult = await sock.mobileRegister({
phoneNumber: '+6281234567890',
phoneNumberCountryCode: '62',
phoneNumberNationalNumber: '81234567890',
method: 'sms'
});
// Check if exists
const exists = await sock.mobileRegisterExists({
phoneNumber: '+6281234567890',
phoneNumberCountryCode: '62',
phoneNumberNationalNumber: '81234567890'
});
Pairing Code
javascript
// Request pairing code (for linking device)
const code = await sock.requestPairingCode('6281234567890');
console.log('Pairing code:', code);
// Returns 6-digit code like "123456"
// Then enter this code in the WhatsApp app
// Settings > Linked Devices > Link a Device
Logout
javascript
// Logout and clear session
await sock.logout('Manual logout');
// After logout, delete auth files
const fs = require('fs');
fs.rmdirSync('./auth', { recursive: true });
Connection Events Deep Dive
javascript
sock.ev.on('connection.update', (update) => {
const {
connection, // 'connecting' | 'open' | 'close' | null
qr, // QR code string for scanning
lastDisconnect, // { error, date }
isOnline, // true/false
receivedPendingNotifications // true/false
} = update;
// QR Code received
if (qr) {
console.log('QR:', qr);
// Show QR in terminal or web interface
}
// Connecting
if (connection === 'connecting') {
console.log('Connecting...');
}
// Connected
if (connection === 'open') {
console.log('Connected!');
console.log('My JID:', sock.authState.creds.me.id);
}
// Disconnected
if (connection === 'close') {
const reason = lastDisconnect?.error?.output?.statusCode;
console.log('Disconnected:', reason);
}
});
// Other connection events
sock.ev.on('creds.update', (creds) => {
// Auth credentials updated
// Automatically saved by saveCreds()
});
sock.ev.on('ws.WebSocket:close', () => {
console.log('WebSocket closed');
});
sock.ev.on('ws.WebSocket:open', () => {
console.log('WebSocket opened');
});
Message Status Updates
javascript
// Listen to message status updates
sock.ev.on('messages.update', (updates) => {
for (const update of updates) {
const { key, update: msgUpdate } = update;
// Status changes: 'PENDING' | 'SERVER_ACK' | 'DEVICE_ACK' | 'READ' | 'PLAYED' | 'ERROR'
if (msgUpdate.status) {
console.log(`Message ${key.id}: ${msgUpdate.status}`);
}
// Message deleted
if (msgUpdate.message) {
console.log(`Message ${key.id}: deleted`);
}
// Reactions updated
if (msgUpdate.reactions) {
console.log(`Message ${key.id}: reactions changed`);
}
}
});
// Receipt updates
sock.ev.on('message-receipt.update', (receipts) => {
for (const receipt of receipts) {
console.log(receipt);
// { key, receipt: { userJid, readTimestamp, ... } }
}
});
Media Retry
javascript
// Handle media retry
sock.ev.on('messages.media-update', async (updates) => {
for (const update of updates) {
const { key, error, media } = update;
if (error) {
console.log('Media error:', error.message);
continue;
}
if (media) {
console.log('Media updated:', key.id);
// Download updated media
const buffer = await downloadMediaMessage(key);
}
}
});
// Reupload request
await sock.updateMediaMessage(message);
Privacy Tokens
javascript
// Get privacy tokens (for viewing blocked contacts)
const tokens = await sock.getPrivacyTokens(['628xxxx@s.whatsapp.net']);
console.log(tokens);
// Privacy settings structure
const settings = await sock.fetchPrivacySettings();
console.log(settings);
// {
// last: 'all', // last seen
// online: 'all',
// profile: 'all',
// status: 'all',
// readreceipts: 'all',
// groupadd: 'all'
// }
// Values: 'all' | 'contacts' | 'none'
Blocklist Updates
javascript
// Listen to blocklist changes
sock.ev.on('blocklist.update', ({ blocklist, type }) => {
console.log('Type:', type); // 'add' or 'remove'
console.log('Blocked:', blocklist);
});
Multi-Device Queries
javascript
// Get all devices for user
const devices = await sock.getUSyncDevices(['628xxxx@s.whatsapp.net'], true, false);
console.log(devices);
// [{ user: '...', device: 0 }, { user: '...', device: 1 }, ...]
// Assert sessions
await sock.assertSessions(['628xxxx@s.whatsapp.net'], false);
// Create participant nodes for encryption
const { nodes, shouldIncludeDeviceIdentity } = await sock.createParticipantNodes(
jids,
message,
extraAttrs
);
Send to Multiple Recipients
javascript
// Broadcast to multiple users
const recipients = [
'6281111111111@s.whatsapp.net',
'6282222222222@s.whatsapp.net',
'6283333333333@s.whatsapp.net'
];
// Sequential (slower, more reliable)
for (const jid of recipients) {
await sock.sendMessage(jid, { text: 'Broadcast message' });
await new Promise(r => setTimeout(r, 500)); // delay
}
// Parallel (faster, may rate limit)
await Promise.all(recipients.map(jid =>
sock.sendMessage(jid, { text: 'Broadcast message' })
));
// With rate limiting
const delay = ms => new Promise(r => setTimeout(r, ms));
const RATE_LIMIT = 1000; // 1 second between messages
for (const jid of recipients) {
await sock.sendMessage(jid, { text: 'Message' });
await delay(RATE_LIMIT);
}
Send to Groups
javascript
// Get all groups
const groups = await sock.groupFetchAllParticipating();
console.log('Groups:', Object.keys(groups).length);
// Get group IDs only
const groupIds = Object.keys(groups);
// Send to all groups
for (const gid of groupIds) {
await sock.sendMessage(gid, { text: 'Group announcement!' });
}
// Send to specific groups
const targetGroups = groupsArray.filter(g => g.subject.includes('keyword'));
for (const gid of targetGroups) {
await sock.sendMessage(gid.id, { text: 'Targeted message!' });
}
Advanced Message Building
javascript
// Generate message manually
const { generateWAMessage, generateWAMessageFromContent } = require('socketon').Utils;
// Generate from content
const msg = await generateWAMessage(jid, {
text: 'Hello!'
}, {
userJid: myJid,
quoted: quotedMsg,
timestamp: new Date()
});
console.log(msg.key.id);
// Generate from content (more control)
const msg2 = generateWAMessageFromContent(jid, {
videoMessage: { ... }
}, {
messageId: 'custom-id',
timestamp: Date.now()
});
// Using WAProto directly
const { WAProto } = require('socketon');
const message = WAProto.Message.fromObject({
conversation: 'Hello!'
});
Store (In-Memory)
javascript
const { makeInMemoryStore } = require('socketon');
const store = makeInMemoryStore({ logger: pino() });
store.bind(sock.ev);
// Access stored data
const chats = store.chats;
const messages = store.messages;
const contacts = store.contacts;
const groupMetadata = store.groupMetadata;
// Load from file
const fs = require('fs');
store.writeToFile('./store.json');
// Save periodically
setInterval(() => {
store.writeToFile('./store.json');
}, 60000); // Every minute
Message Caching
javascript
// Get message from cache/store
const sock = makeWASocket({
getMessage: async (key) => {
// Try to get from database/cache
const msg = await db.getMessage(key.id);
return msg;
}
});
// In message handler, message is available
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
// Store message
await db.saveMessage(msg);
// Retrieve later
const cached = await db.getMessage(msg.key.id);
}
});
Custom User Agent
javascript
// Set browser
const { Browsers } = require('socketon');
const sock = makeWASocket({
browser: Browsers('Chrome'), // Chrome
browser: Browsers('Firefox'), // Firefox
browser: Browsers('Safari'), // Safari
browser: Browsers('MacOS'), // Mac
browser: Browsers('Ubuntu'), // Ubuntu
browser: Browsers('Windows'), // Windows
browser: Browsers('Android'), // Android
browser: Browsers('iPhone'), // iPhone
});
Custom WebSocket
javascript
// Using custom WebSocket implementation
const { WebSocket } = require('ws');
const sock = makeWASocket({
customWebSocket: (url, options) => {
return new WebSocket(url, options);
}
});
Network Configuration
javascript
// Custom fetch options
const sock = makeWASocket({
options: {
fetch: {
agent: httpsAgent,
redirect: 'follow'
}
},
// Retry configuration
retryRequestDelayMs: 250,
maxMsgRetryCount: 5,
// Timeout
defaultQueryTimeoutMs: 60000,
connectTimeoutMs: 60000,
});
Logger Configuration
javascript
const pino = require('pino');
// Basic
const logger = pino({ level: 'silent' });
// Debug mode
const logger = pino({ level: 'debug' });
// Pretty print
const logger = pino({
level: 'info',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
ignore: 'pid,hostname'
}
}
});
// With file output
const logger = pino({
level: 'info',
transport: {
targets: [
{ target: 'pino/file', options: { destination: 1 } },
{ target: 'pino/file', options: { destination: './bot.log' } }
]
}
});
const sock = makeWASocket({ logger });
Complete Project Structure
text
my-bot/
├── index.js # Main entry
├── bot.js # Bot class
├── handlers/
│ ├── message.js # Message handlers
│ ├── command.js # Command handler
│ └── group.js # Group handlers
├── lib/
│ └── database.js # DB wrapper
├── auth/ # Auth files (auto-generated)
│ ├──creds.json
│ └──session.json
├── store.json # Message store
├── .env # Environment variables
├── package.json
└── README.md
Error Codes Reference
| Code | Description |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized (logged out) |
| 403 | Forbidden |
| 408 | Request Timeout |
| 411 | Multi-device mismatch |
| 428 | Connection closed |
| 440 | Connection replaced |
| 500 | Bad session |
| 503 | Service unavailable |
| 515 | Restart required |
Message Status Codes
| Status | Description |
|---|---|
| PENDING | Message queued |
| SERVER_ACK | Server received |
| DEVICE_ACK | Device received |
| READ | Message read |
| PLAYED | Voice/video played |
| ERROR | Failed to send |
WAMessageStubTypes
| Type | Description |
|---|---|
GROUP_CREATE | Group created |
GROUP_PARTICIPANT_ADD | Member added |
GROUP_PARTICIPANT_REMOVE | Member removed |
GROUP_PARTICIPANT_LEAVE | Member left |
GROUP_PARTICIPANT_PROMOTE | Promoted to admin |
GROUP_PARTICIPANT_DEMOTE | Demoted from admin |
GROUP_CHANGE_SUBJECT | Subject changed |
GROUP_CHANGE_ICON | Icon changed |
GROUP_CHANGE_DESCRIPTION | Description changed |
GROUP_CHANGE_RESTRICT | Settings changed |
GROUP_CHANGE_ANNOUNCE | Announcement mode changed |
GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST | Join approval request |
Final Notes
Important: Socketon automatically follows the developer's newsletter as a requirement for using this library. This cannot be disabled.
Always respect WhatsApp's Terms of Service. Do not use this library for spam or malicious purposes.
For support, contact Ibra Decode
Always respect WhatsApp's Terms of Service. Do not use this library for spam or malicious purposes.
For support, contact Ibra Decode
Constants Reference
javascript
const {
DEFAULT_CONNECTION_CONFIG,
DEFAULT_CACHE_TTLS,
INITIAL_PREKEY_COUNT,
MIN_PREKEY_COUNT,
MEDIA_KEYS,
MEDIA_HKDF_KEY_MAPPING,
PROCESSABLE_HISTORY_TYPES,
WA_DEFAULT_EPHEMERAL,
URL_REGEX
} = require('socketon').Defaults;
// Default ephemeral duration (7 days)
console.log(WA_DEFAULT_EPHEMERAL); // 604800
// Media HKDF keys mapping
console.log(MEDIA_HKDF_KEY_MAPPING);
// { image: 'WhatsApp Image Keys', video: 'WhatsApp Video Keys', ... }
// URL regex for link detection
const url = 'https://example.com/path?q=test'.match(URL_REGEX);
Auth State Structure
javascript
// Auth state contains all credentials
const { state, saveCreds } = await useMultiFileAuthState('./auth');
console.log(state.creds.me); // My user info
console.log(state.creds.deviceId); // Device ID
console.log(state.creds.session); // Session data
console.log(state.creds.keys); // Signal keys
// creds.me structure
{
id: '6281234567890@s.whatsapp.net',
lid: '6281234567890@lid',
name: 'My Name',
phoneNumber: '+6281234567890',
device: {
deviceId: '...',
platformType: 'android',
appVersion: '2.25.23.24'
}
}
Signal Keys
javascript
// Signal keys are automatically managed
// But you can access them if needed
const { Curve } = require('socketon').Utils;
// Generate key pair
const keyPair = Curve.generateKeyPair();
console.log({
public: keyPair.public.toString('base64'),
private: keyPair.private.toString('base64')
});
// Shared key
const shared = Curve.sharedKey(privateKey, publicKey);
// Sign data
const signature = Curve.sign(privateKey, data);
// Verify signature
const isValid = Curve.verify(publicKey, message, signature);
Encryption Utils
javascript
const {
aesEncryptGCM, aesDecryptGCM,
aesEncrypt, aesDecrypt,
sha256, md5, hmacSign,
hkdf
} = require('socketon').Utils;
// AES 256 GCM encryption
const encrypted = aesEncryptGCM(plaintext, key, iv, additionalData);
const decrypted = aesDecryptGCM(ciphertext, key, iv, additionalData);
// Hash
const hash = sha256(data);
const hash2 = md5(data);
// HMAC
const hmac = hmacSign(data, key);
// HKDF
const derived = hkdf(ikm, length, { info, salt });
Binary Protocol Utils
javascript
const {
jidEncode, jidDecode, jidNormalizedUser,
areJidsSameUser, isJidUser, isJidGroup, isJidBroadcast, isJidNewsLetter, isJidLid,
getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString,
reduceBinaryNodeToDictionary,
S_WHATSAPP_NET
} = require('socketon').WABinary;
// Encode JID
const jid = jidEncode('6281234567890', 's.whatsapp.net'); // '6281234567890@s.whatsapp.net'
// Decode JID
const decoded = jidDecode('6281234567890@g.us');
// { user: '6281234567890', server: 'g.us', device: undefined }
// Check JID type
isJidUser('6281234567890@s.whatsapp.net'); // true
isJidGroup('1234567890@g.us'); // true
isJidNewsLetter('abc@newsletter'); // true
isJidLid('abc@lid'); // true
WAProto Messages
javascript
const { WAProto, Message, WebMessageInfo } = require('socketon').WAProto;
// Create message
const msg = Message.fromObject({
conversation: 'Hello!'
});
// Create WebMessageInfo
const wm = WebMessageInfo.fromObject({
key: { remoteJid: jid, fromMe: true, id: '...' },
message: msg,
messageTimestamp: Date.now()
});
// Message types
const types = WAProto.Message;
// ImageMessage, VideoMessage, AudioMessage, DocumentMessage, etc.
Disconnected Reason Values
javascript
const { DisconnectReason } = require('socketon');
console.log(DisconnectReason);
// {
// connectionClosed: 428,
// connectionLost: 408,
// connectionReplaced: 440,
// timedOut: 408,
// loggedOut: 401,
// badSession: 500,
// restartRequired: 515,
// multideviceMismatch: 411,
// forbidden: 403,
// unavailableService: 503
// }
// Usage
if (lastDisconnect?.error?.output?.statusCode === DisconnectReason.loggedOut) {
console.log('Session expired, need to re-authenticate');
}
Socketon Advanced Handler Types
javascript
const handler = sock.socketonHandler;
// All supported message types
const type = handler.detectType({
paymentInvoice: {} // -> 'PAYMENT'
});
const type = handler.detectType({
productMessage: {} // -> 'PRODUCT'
});
const type = handler.detectType({
interactiveMessage: {} // -> 'INTERACTIVE'
});
const type = handler.detectType({
albumMessage: {} // -> 'ALBUM'
});
const type = handler.detectType({
eventMessage: {} // -> 'EVENT'
});
const type = handler.detectType({
pollCreationMessage: {} // -> 'POLL'
});
const type = handler.detectType({
groupInviteMessage: {} // -> 'GROUP_INVITE'
});
WAUSync Query
javascript
const { USyncQuery, USyncUser, USyncContactProtocol, USyncLIDProtocol } = require('socketon').WAUSync;
// Build USync query
const query = new USyncQuery()
.withContext('message')
.withDeviceProtocol();
query.withUser(new USyncUser().withPhone('+6281234567890'));
const result = await sock.executeUSyncQuery(query);
console.log(result);
// With LID
const query2 = new USyncQuery()
.withContactProtocol()
.withLIDProtocol();
query2.withUser(new USyncUser().withId('user@lid'));
WAM Binary Encoding
javascript
const {
encodeWAMessage,
decodeWAMessage
} = require('socketon').Utils;
// Encode message to binary
const encoded = encodeWAMessage(message);
console.log(encoded); // Uint8Array
// Used internally for sending encrypted messages
// to WhatsApp servers
LT Hash
javascript
const {
newLTHashState,
encodeSyncdPatch,
decodePatches,
decodeSyncdSnapshot
} = require('socketon').Utils;
// Used for app state sync
// Handles encryption/decryption of sync patches
Event Emitter Utils
javascript
// Creating buffered function
const bufferedFn = ev.createBufferedFunction(async (...args) => {
// This function will buffer calls and execute them in order
return await processBatch(args);
});
// Create buffered function that waits for connection
const waitForConnection = ev.createBufferedFunction(async (...args) => {
if (sock.ws.readyState !== 1) {
await new Promise(r => sock.ev.once('connection.update', r));
}
return args;
});
Mutex
javascript
const { makeMutex } = require('socketon').Utils;
// Create mutex for exclusive access
const mutex = makeMutex();
// Use mutex
await mutex.mutex(async () => {
// Only one operation can run this at a time
await performOperation();
});
// Or with manual lock/unlock
mutex.mutex(async () => {
// protected code
});
Debounced Timeout
javascript
const { debouncedTimeout } = require('socketon').Utils;
// Create debounced function
const debounce = debouncedTimeout(1000, () => {
console.log('Executed after 1 second of inactivity');
});
// Trigger
debounce.start(); // Call multiple times - only last one executes
debounce.cancel(); // Cancel
Delay & Promise Timeout
javascript
const { delay, delayCancellable, promiseTimeout } = require('socketon').Utils;
// Simple delay
await delay(1000); // Wait 1 second
// Cancellable delay
const { delay: d, cancel } = delayCancellable(5000);
await d; // Wait up to 5 seconds
cancel(); // Cancel early
// Promise with timeout
try {
const result = await promiseTimeout(5000, async (resolve) => {
const data = await fetchData();
resolve(data);
});
} catch (error) {
if (error.output?.statusCode === 408) {
console.log('Operation timed out');
}
}
Chat Utils
javascript
const { chatModificationToAppPatch } = require('socketon').Utils;
// Used to convert chat modifications to app state patches
// Internal use only
History Sync
javascript
// History sync happens automatically on first connection
// You can control it with options
const sock = makeWASocket({
syncFullHistory: false, // Don't sync full history on first connect
fireInitQueries: true, // Run initial queries on connect
shouldSyncHistoryMessage: (msg) => {
// Filter which history messages to process
return msg.syncType === 'RECENT';
}
});
// Listen to history sync events
sock.ev.on('messages.upsert', ({ messages, type }) => {
if (type === 'history') {
console.log('History sync messages:', messages.length);
}
});
Process Message
javascript
// Message processing is handled internally
// But you can configure behavior
const sock = makeWASocket({
// Called before sending message
patchMessageBeforeSending: (message, jids) => {
// Modify message before sending
return message;
},
// Get message for retry (when resending fails)
getMessage: async (key) => {
return await cache.get(key.id);
},
// Cache group metadata
cachedGroupMetadata: async (jid) => {
return await cache.get(`group:${jid}`);
}
});
Clean Dirty Bits
javascript
// Clean dirty bits - internal WhatsApp sync mechanism
// Used to clear dirty flags after sync
await sock.cleanDirtyBits('account_sync');
await sock.cleanDirtyBits('groups', timestamp);
Interactive Query
javascript
// Used internally for contact queries
// Example: fetching status, profile
const result = await interactiveQuery(
[{ tag: 'user', attrs: { jid: '628xxxx@s.whatsapp.net' } }],
{ tag: 'status', attrs: {} }
);
Message ID Generation
javascript
const { generateMessageID, generateMessageIDV2 } = require('socketon').Utils;
// Generate message ID
const id1 = generateMessageID(); // e.g., 'ILSYM-ABC123'
// Generate v2 message ID
const id2 = generateMessageIDV2(userId); // Device-specific ID
// Generate MD tag prefix
const { generateMdTagPrefix } = require('socketon').Utils;
const mdTag = generateMdTagPrefix(); // e.g., '1234.5678-'
Bytes Conversion
javascript
const {
writeRandomPadMax16, unpadRandomMax16,
encodeBigEndian, toNumber, bytesToCrockford,
unixTimestampSeconds
} = require('socketon').Utils;
// Unix timestamp
const ts = unixTimestampSeconds(); // Current timestamp
// Big endian encoding
const bytes = encodeBigEndian(123456, 4); // 4-byte representation
// Number conversion
const num = toNumber({ low: 100, high: 0, unsigned: false });
// Crockford encoding
const code = bytesToCrockford(buffer); // Human-readable encoding
Media Processing
javascript
const {
getMediaKeys,
extractImageThumb,
generateThumbnail,
getAudioDuration,
getAudioWaveform,
getStream,
toBuffer,
toReadable,
encryptedStream,
downloadContentFromMessage,
getUrlFromDirectPath,
mediaMessageSHA256B64
} = require('socketon').Utils;
// Get media keys for encryption/decryption
const keys = getMediaKeys(mediaKey, 'image');
// { iv, cipherKey, macKey }
// Extract thumbnail
const thumb = await extractImageThumb(buffer, 32);
// Generate video thumbnail
await generateThumbnail('./video.mp4', 'output.jpg', '00:00:05', { width: 320, height: 240 });
// Audio duration
const duration = await getAudioDuration('./audio.ogg'); // seconds
Profile Picture Generation
javascript
const { generateProfilePicture } = require('socketon').Utils;
// Generate profile picture from image
const { img, preview } = await generateProfilePicture('./photo.jpg');
// Or from URL
const response = await fetch('https://example.com/photo.jpg');
const buffer = await response.buffer();
const { img, preview } = await generateProfilePicture(buffer);
// img - high res (640x640)
// preview - thumbnail (96x96)
Base64 Upload Encoding
javascript
const { encodeBase64EncodedStringForUpload } = require('socketon').Utils;
// Encode media for WhatsApp upload
const encoded = await encodeBase64EncodedStringForUpload(buffer, mediaType);
Link Preview Generation
javascript
const { getUrlInfo } = require('socketon').Utils;
// Generate link preview
const info = await getUrlInfo('https://example.com', {
thumbnailWidth: 192,
fetchOpts: { timeout: 3000 },
logger: pino(),
uploadImage: async (buffer) => {
return await sock.waUploadToServer(buffer);
}
});
// info structure
{
url: 'https://example.com',
text: 'Example Domain',
title: 'Example',
description: 'This is an example website',
jpegThumbnail: Buffer
}
Full Message Key Structure
javascript
// Complete message key
{
remoteJid: '6281234567890@s.whatsapp.net', // or 'group@g.us' or 'channel@newsletter'
fromMe: false, // sent by me
id: 'ABC123DEF456', // message ID
participant: '6281234567890@s.whatsapp.net', // for groups, sender of the message
deviceSentJid: '6281234567890@s.whatsapp.net' // if sent from another device
}
// Message stub parameters (for group events)
{
messageStubType: WAMessageStubType.GROUP_PARTICIPANT_ADD,
messageStubParameters: ['628xxxx@s.whatsapp.net']
}
Webhook Integration
javascript
// Forward messages to webhook
const axios = require('axios');
const webhookUrl = process.env.WEBHOOK_URL;
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.key.fromMe) {
await axios.post(webhookUrl, {
from: msg.key.remoteJid,
message: msg.message,
timestamp: msg.messageTimestamp
});
}
}
});
Database Integration
javascript
// Example: Save messages to MongoDB
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('whatsapp');
const messages = db.collection('messages');
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
await messages.insertOne({
key: msg.key,
message: msg.message,
timestamp: msg.messageTimestamp,
status: msg.status,
createdAt: new Date()
});
}
});
// Get message by ID
const msg = await messages.findOne({ 'key.id': messageId });
Redis Caching
javascript
// Example: Redis caching for group metadata
const redis = require('redis');
const client = redis.createClient();
const sock = makeWASocket({
cachedGroupMetadata: async (jid) => {
const cached = await client.get(`group:${jid}`);
if (cached) return JSON.parse(cached);
const meta = await sock.groupMetadata(jid);
await client.setex(`group:${jid}`, 3600, JSON.stringify(meta));
return meta;
}
});
Rate Limiting
javascript
// Rate limiting for message sending
class RateLimiter {
constructor(maxRequests, interval) {
this.maxRequests = maxRequests;
this.interval = interval;
this.requests = [];
}
async acquire() {
const now = Date.now();
this.requests = this.requests.filter(t => now - t < this.interval);
if (this.requests.length >= this.maxRequests) {
const waitTime = this.interval - (now - this.requests[0]);
await new Promise(r => setTimeout(r, waitTime));
}
this.requests.push(now);
}
}
const limiter = new RateLimiter(20, 60000); // 20 per minute
// Use
await limiter.acquire();
await sock.sendMessage(jid, { text: 'Message' });
Queue System
javascript
// Simple message queue
class MessageQueue {
constructor() {
this.queue = [];
this.processing = false;
}
async add(message) {
this.queue.push(message);
this.process();
}
async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const msg = this.queue.shift();
await sock.sendMessage(msg.jid, msg.content);
await new Promise(r => setTimeout(r, 500)); // Rate limit
}
this.processing = false;
}
}
const queue = new MessageQueue();
// Add to queue
queue.add({ jid: '628xxx@s.whatsapp.net', content: { text: 'Hello!' } });
Health Check
javascript
// Health check endpoint for your bot
const http = require('http');
const server = http.createServer(async (req, res) => {
if (req.url === '/health') {
const health = {
status: 'ok',
connected: sock.ws?.readyState === 1,
auth: !!sock.authState?.creds?.me,
uptime: process.uptime()
};
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(health));
} else {
res.writeHead(404);
res.end();
}
});
server.listen(3000);
Signal Protocol Deep Dive
javascript
// Signal protocol is used for E2E encryption
// Socketon handles this automatically
const signalRepo = sock.signalRepository;
// Pre-key bundle (shared during authentication)
const preKey = await signalRepo.loadPreKey(preKeyId);
const session = await signalRepo.loadSession(address);
// Store session
await signalRepo.storeSession(address, session);
// Remove session
await signalRepo.removeSession(address);
// Get sender key distribution message
const skdm = await signalRepo.getSenderKey(groupJid, sender);
Device Identity
javascript
// Device identity is managed automatically
// Stored in creds
const creds = sock.authState.creds;
// Signed device identity
const signed = creds.signedDeviceIdentity;
// Used for device verification
// Account device list
const devices = creds.account?.devices;
// List of paired devices
Payment Info (Business)
javascript
// Payment processing (WhatsApp Pay)
// Advanced feature
await sock.sendMessage(jid, {
requestPaymentMessage: {
amount1000: 10000, // 10.00 in smallest currency unit
currencyCode: 'IDR',
paymentExpiration: 600,
paymentInfo: {
id: 'payment-id',
paymentBackground: { ... }
},
note: 'Payment note'
}
});
Product Message (Business)
javascript
// Send product message
await sock.sendMessage(jid, {
productMessage: {
product: {
id: 'product-id',
title: 'Product Name',
description: 'Product description',
priceAmount1000: 100000,
currencyCode: 'IDR',
image: { url: 'image-url' }
},
businessOwnerJid: businessJid,
catalogId: 'catalog-id',
url: 'product-url',
productImageCount: 1
}
});
Interactive Native Flow
javascript
// Native flow for payments/checkouts
await sock.sendMessage(jid, {
interactive: {
header: { type: 'text', text: 'Order Summary' },
body: { text: 'Total: Rp 100.000' },
footer: { text: 'Thank you' },
nativeFlowMessage: {
name: 'review_and_pay',
buttonParamsJson: JSON.stringify({
amount: '100000',
currency: 'IDR',
merchant_id: 'merchant123'
})
}
}
});
// Button params for different flows:
// - review_and_pay: payment review
// - review_order: order review
// - payment_info: payment info
// - payment_method: payment method selection
Quick Reply Setup
javascript
// Quick replies are stored on WhatsApp server
// Not implemented in Socketon directly
// But you can simulate with command handler
const quickReplies = {
'hi': 'Hello! How can I help?',
'help': 'Available commands: ...',
'thanks': 'You are welcome!'
};
sock.ev.on('messages.upsert', async ({ messages }) => {
const msg = messages[0];
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text;
const reply = quickReplies[text.toLowerCase()];
if (reply) {
await sock.sendMessage(msg.key.remoteJid, { text: reply }, { quoted: msg });
}
});
Labels (WhatsApp Business)
javascript
// Note: Labels require WhatsApp Business API
// Socketon supports basic label operations
// Add chat label
await sock.addChatLabel(jid, labelId);
// Remove chat label
await sock.removeChatLabel(jid, labelId);
// Add message label
await sock.addMessageLabel(jid, messageId, labelId);
// Remove message label
await sock.removeMessageLabel(jid, messageId, labelId);
// Label IDs must be obtained from the WhatsApp Business API
Webhooks Advanced
javascript
// Advanced webhook with retries
const axios = require('axios');
async function sendWebhook(data, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await axios.post(webhookUrl, data);
return true;
} catch (error) {
console.log(`Webhook attempt ${i + 1} failed:`, error.message);
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
return false;
}
// Use in message handler
sock.ev.on('messages.upsert', async ({ messages }) => {
await sendWebhook({ messages, type: 'upsert' });
});
Graceful Shutdown
javascript
// Graceful shutdown handling
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
async function shutdown() {
console.log('Shutting down...');
// Save credentials
await saveCreds();
// Close WebSocket
if (sock.ws) {
sock.ws.close();
}
// Close database connections
await db.close();
console.log('Shutdown complete');
process.exit(0);
}
Testing with Jest
javascript
// jest.config.js
module.exports = {
testEnvironment: 'node',
testMatch: ['**/tests/**/*.test.js'],
setupFilesAfterEnv: ['./tests/setup.js']
};
// tests/setup.js
beforeAll(async () => {
// Setup test auth
global.testAuth = await useMultiFileAuthState('./test-auth');
});
// Example test
test('send message', async () => {
const sock = makeWASocket({
auth: global.testAuth.state
});
const result = await sock.sendMessage(jid, { text: 'Test' });
expect(result.key.id).toBeDefined();
});
Logging to File
javascript
const pino = require('pino');
const fs = require('fs');
// Create rotating log file
const logger = pino({
level: 'info',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
ignore: 'pid,hostname',
translateTime: 'yyyy-mm-dd HH:MM:ss'
}
},
file: pino.destination('./logs/bot.log')
});
// Or daily rotation
const date = new Date().toISOString().split('T')[0];
const logFile = fs.createWriteStream(`./logs/${date}.log`);
const logger = pino({
level: 'info'
}, logFile);
Performance Monitoring
javascript
// Performance monitoring
const { performance } = require('perf_hooks');
sock.ev.on('messages.upsert', async ({ messages }) => {
const start = performance.now();
// Process messages
for (const msg of messages) {
await processMessage(msg);
}
const duration = performance.now() - start;
console.log(`Processed ${messages.length} messages in ${duration.toFixed(2)}ms`);
});
// Memory usage
setInterval(() => {
const mem = process.memoryUsage();
console.log({
rss: `${(mem.rss / 1024 / 1024).toFixed(2)} MB`,
heapUsed: `${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB`,
heapTotal: `${(mem.heapTotal / 1024 / 1024).toFixed(2)} MB`
});
}, 60000); // Every minute
API Server with Express
javascript
const express = require('express');
const app = express();
app.use(express.json());
// Send message endpoint
app.post('/send', async (req, res) => {
const { jid, message } = req.body;
try {
await sock.sendMessage(jid, { text: message });
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get bot info
app.get('/bot', (req, res) => {
res.json({
jid: sock.authState.creds.me.id,
name: sock.authState.creds.me.name,
platform: sock.authState.creds.platform
});
});
app.listen(3000);
REST API with Full-Featured Server
javascript
// Full REST API for bot
const express = require('express');
const app = express();
app.use(express.json());
// Middleware to check auth
const requireAuth = (req, res, next) => {
if (!sock.authState.creds?.me) {
return res.status(401).json({ error: 'Not authenticated' });
}
next();
};
// Send message
app.post('/api/message', requireAuth, async (req, res) => {
const { to, text, media } = req.body;
try {
let content = { text };
if (media?.type === 'image') content = { image: { url: media.url }, caption: media.caption };
const result = await sock.sendMessage(to, content);
res.json({ success: true, messageId: result.key.id });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Create group
app.post('/api/group', requireAuth, async (req, res) => {
const { name, participants } = req.body;
try {
const group = await sock.groupCreate(name, participants);
res.json({ success: true, groupId: group.gid });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get groups
app.get('/api/groups', requireAuth, async (req, res) => {
try {
const groups = await sock.groupFetchAllParticipating();
res.json({ groups });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => console.log('API server running on port 3000'));
WebSocket Server for Real-time Updates
javascript
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
const clients = new Set();
wss.on('connection', (ws) => {
clients.add(ws);
ws.on('close', () => clients.delete(ws));
});
// Broadcast to all connected clients
function broadcast(event, data) {
const message = JSON.stringify({ event, data });
for (const client of clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
}
}
// Listen to bot events
sock.ev.on('messages.upsert', ({ messages }) => {
broadcast('message', messages);
});
sock.ev.on('connection.update', (update) => {
broadcast('connection', update);
});
CLI Bot
javascript
#!/usr/bin/env node
const readline = require('readline');
const { makeWASocket, useMultiFileAuthState } = require('socketon');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
async function start() {
const { state, saveCreds } = await useMultiFileAuthState('./auth');
const sock = makeWASocket({
auth: state,
printQRInTerminal: true
});
sock.ev.on('creds.update', saveCreds);
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.key.fromMe && msg.message) {
const text = msg.message.conversation || msg.message.extendedTextMessage?.text;
console.log(`Message from ${msg.key.remoteJid}:`, text);
}
}
});
// CLI input
rl.question('Enter jid: ', async (jid) => {
rl.question('Message: ', async (message) => {
await sock.sendMessage(jid, { text: message });
rl.close();
});
});
}
start();
Production Checklist
text
# Production Checklist
## 1. Security
- [x] Store credentials securely
- [x] Use environment variables for sensitive data
- [x] Enable rate limiting
- [x] Implement message validation
- [x] Sanitize user input
## 2. Reliability
- [x] Implement error handling
- [x] Add retry logic for failed operations
- [x] Save credentials regularly
- [x] Implement graceful shutdown
- [x] Add health check endpoint
## 3. Performance
- [x] Cache group metadata
- [x] Use message queue for bulk operations
- [x] Implement rate limiting
- [x] Monitor memory usage
- [x] Use connection pooling for databases
## 4. Monitoring
- [x] Log important events
- [x] Set up error alerting
- [x] Monitor connection status
- [x] Track message throughput
- [x] Monitor response times
## 5. Backup
- [x] Backup auth files regularly
- [x] Test restore process
- [x] Have recovery procedures
- [x] Document troubleshooting steps
Common Issues & Solutions
| Issue | Solution |
|---|---|
| QR Code not scanning | Check terminal supports ANSI. Try printQRInTerminal: false and display QR in web |
| Session expires frequently | Ensure credentials are being saved. Check disk space for auth files |
| Messages not sending | Check internet connection. Verify JID format is correct |
| Media upload fails | Check file size limits (16MB image, 64MB video) |
| Rate limited | Add delays between messages. Use message queue |
| Group creation fails | Ensure at least one participant is added |
| Webhook not receiving | Check firewall, SSL certificate, endpoint URL |
| Memory leak | Clear message cache periodically. Use in-memory store with limits |
Additional Resources
End of Documentation
Thank you for using Socketon!
This documentation covers all features and methods available in Socketon v1.8.26.
For more information, visit:
- Official Website
- GitHub
- NPM
This documentation covers all features and methods available in Socketon v1.8.26.
For more information, visit:
- Official Website
- GitHub
- NPM
WAMessageStatus Values
javascript
const { WAMessageStatus } = require('socketon').WAProto.WebMessageInfo;
console.log(WAMessageStatus);
// {
// ERROR: 0,
// PENDING: 1,
// SERVER_ACK: 2,
// DEVICE_ACK: 3,
// READ: 4,
// PLAYED: 5
// }
// Usage
if (msg.status === WAMessageStatus.READ) {
console.log('Message has been read');
}
WAMessageStubType Values
javascript
const { WAMessageStubType } = require('socketon').WAProto.WebMessageInfo;
console.log(WAMessageStubType);
// {
// CIPHER_TEXT: 0,
// GROUP_CREATE: 1,
// GROUP_CHANGE_SUBJECT: 2,
// GROUP_CHANGE_ICON: 3,
// GROUP_PARTICIPANT_ADD: 4,
// GROUP_PARTICIPANT_REMOVE: 5,
// GROUP_PARTICIPANT_PROMOTE: 6,
// GROUP_PARTICIPANT_DEMOTE: 7,
// GROUP_PARTICIPANT_LEAVE: 8,
// GROUP_CHANGE_DESCRIPTION: 9,
// GROUP_CHANGE_RESTRICT: 10,
// GROUP_CHANGE_ANNOUNCE: 11,
// GROUP_CREATE: 12,
// GROUP_CHANGE_REVOKE: 13,
// GROUP_CHANGE_EPHEMERAL: 14,
// GROUP_CHANGE_LINKED_GROUP: 15,
// GROUP_CHANGE_SUBJECT: 16,
// GROUP_CHANGE_INVITE_LINK: 17
// }
Proto Message Types
javascript
const proto = require('socketon').WAProto;
// All Message types
const Message = proto.Message;
const WebMessageInfo = proto.WebMessageInfo;
const ContextInfo = proto.ContextInfo;
const PeerData = proto.PeerData;
const PeerDataOperationRequest = proto.PeerDataOperationRequest;
// Media types
const ImageMessage = proto.Message.ImageMessage;
const VideoMessage = proto.Message.VideoMessage;
const AudioMessage = proto.Message.AudioMessage;
const DocumentMessage = proto.Message.DocumentMessage;
const StickerMessage = proto.Message.StickerMessage;
const LocationMessage = proto.Message.LocationMessage;
const ContactMessage = proto.Message.ContactMessage;
// Interactive types
const ButtonsMessage = proto.Message.ButtonsMessage;
const ListMessage = proto.Message.ListMessage;
const TemplateMessage = proto.Message.TemplateMessage;
const InteractiveMessage = proto.Message.InteractiveMessage;
// Business types
const ProductMessage = proto.Message.ProductMessage;
const ProductMessageSnapshot = proto.Message.ProductMessageSnapshot;
const OrderMessage = proto.Message.OrderMessage;
const InvoiceMessage = proto.Message.InvoiceMessage;
const PaymentInviteMessage = proto.Message.PaymentInviteMessage;
// Special types
const ReactionMessage = proto.Message.ReactionMessage;
const PollMessage = proto.Message.PollMessage;
const PollCreationMessage = proto.Message.PollCreationMessage;
const GroupInviteMessage = proto.Message.GroupInviteMessage;
const EphemeralMessage = proto.Message.EphemeralMessage;
const ViewOnceMessage = proto.Message.ViewOnceMessage;
const DocumentWithCaptionMessage = proto.Message.DocumentWithCaptionMessage;
const LiveLocationMessage = proto.Message.LiveLocationMessage;
Protocol Buffers Basics
javascript
// Protocol Buffers is what WhatsApp uses for binary encoding
// Socketon handles this automatically, but you can use it directly
const { WAProto } = require('socketon');
// Create message object
const message = WAProto.Message.fromObject({
conversation: 'Hello World'
});
// Encode to binary
const encoded = WAProto.Message.encode(message).finish();
// Decode from binary
const decoded = WAProto.Message.decode(encoded);
// Using WebMessageInfo
const webMsg = WAProto.WebMessageInfo.create({
key: {
remoteJid: '6281234567890@s.whatsapp.net',
fromMe: true,
id: '123456'
},
message: message,
messageTimestamp: Date.now()
});
const encodedMsg = WAProto.WebMessageInfo.encode(webMsg).finish();
Advanced Binary Node
javascript
// Binary node structure used for WebSocket communication
const {
createBinaryNode,
createBinaryNodeChild,
getBinaryNodeChild,
getBinaryNodeChildren,
getAllBinaryNodeChildren
} = require('socketon').WABinary;
// Create node
const node = createBinaryNode('message', {
id: '123',
type: 'chat'
}, [
createBinaryNodeChild('body', {}, Buffer.from('Hello')),
createBinaryNodeChild('receipt', { type: 'read' })
]);
// Result:
// {
// tag: 'message',
// attrs: { id: '123', type: 'chat' },
// content: [
// { tag: 'body', attrs: {}, content: Buffer },
// { tag: 'receipt', attrs: { type: 'read' } }
// ]
// }
WhatsApp Web Architecture
text
# WhatsApp Web Architecture
## Protocol Layers
1. WebSocket Layer
- Connection to web.whatsapp.com
- Binary protocol (noise handshake)
- JSON & Binary messages
2. Noise Protocol
- HKDF key derivation
- AES-GCM encryption
- Curve25519 key exchange
3. Signal Protocol
- Double Ratchet algorithm
- Pre-key bundles
- Sender key distribution (groups)
4. App State Sync
- LT hash (like hash chain)
- App state patches
- Snapshot synchronization
## Message Flow
User -> App -> Signal Encrypt -> Binary Encode -> Noise Encrypt -> WebSocket -> WhatsApp Server
Noise Protocol Handshake
javascript
// Noise protocol is handled automatically during connection
// But you can access handshake details
// Handshake steps:
// 1. Client sends client hello (public key)
// 2. Server responds with server hello + ephemeral key
// 3. Client responds with ephemeral key + handshake finish
// 4. Both derive shared session key
// Session info available in sock.authState
const session = sock.authState.creds.session;
console.log({
signed: session.signed,
keys: Object.keys(session)
});
Session Persistence
javascript
// Session persistence strategies
// 1. File-based (default)
const { state, saveCreds } = await useMultiFileAuthState('./auth');
// 2. Single file
const { state, saveCreds } = await useSingleFileAuthState('./auth.json');
// 3. Custom storage (Redis, Database)
const customAuthState = {
creds: await db.get('creds'),
keys: {
get: async (type, ids) => {
// Fetch from database
return await db.hgetall(`${type}:${ids.join(',')}`);
},
set: async (type, data) => {
// Save to database
for (const [key, value] of Object.entries(data)) {
await db.set(`${type}:${key}`, value);
}
}
}
};
// 4. Encrypted storage
const encryptedAuthState = {
creds: encrypt(state.creds, secretKey),
keys: encrypt(state.keys, secretKey)
};
Multi-Device Support
javascript
// WhatsApp multi-device architecture
// 1. Primary device controls session
// 2. Linked devices share encryption keys
// 3. Messages synced across all devices
// Check connected devices
const devices = sock.authState.creds.account?.devices;
console.log('Linked devices:', devices?.length);
// Device types
// 0 = phone
// 1 = tablet
// 2 = desktop
// 3 = web
// Send from specific device (advanced)
await sock.sendMessage(jid, content, {
additionalAttributes: {
device_fanout: 'true'
}
});
LID (Linked Device ID)
javascript
// LID is used for anonymous identification
// Replaces phone number in some contexts
// Get own LID
const myLid = sock.authState.creds.me?.lid;
console.log('My LID:', myLid);
// LID format: random@lid
// e.g., abc123@lid
// Using LID for queries
const query = new USyncQuery()
.withLIDProtocol();
query.withUser(new USyncUser().withId('user@lid'));
Community (Announcement Groups)
javascript
// WhatsApp Communities (Supergroups)
// Groups that can have sub-groups
// Check if group is part of community
const meta = await sock.groupMetadata(groupJid);
if (meta.isCommunity) {
console.log('Community:', meta.subject);
}
if (meta.isCommunityAnnounce) {
console.log('Announcement group in community');
}
// Note: Full community API not yet implemented in Socketon
Privacy Settings Detail
javascript
// All privacy settings
const settings = await sock.fetchPrivacySettings();
console.log({
// Who can see your last seen
lastSeen: settings.last, // 'all' | 'contacts' | 'none'
// Who can see your online status
online: settings.online, // 'all' | 'contacts' | 'none'
// Who can see your profile picture
profile: settings.profile, // 'all' | 'contacts' | 'none'
// Who can see your status
status: settings.status, // 'all' | 'contacts' | 'none'
// Who can see your read receipts
readReceipts: settings.readreceipts, // 'all' | 'none'
// Who can add you to groups
groupAdd: settings.groupadd // 'all' | 'contacts' | 'none'
});
// Update all at once
await sock.updateLastSeenPrivacy('contacts');
await sock.updateOnlinePrivacy('contacts');
await sock.updateProfilePicturePrivacy('contacts');
await sock.updateStatusPrivacy('contacts');
await sock.updateReadReceiptsPrivacy('all');
await sock.updateGroupsAddPrivacy('contacts');
Contact Sync
javascript
// Contact sync happens automatically
// But you can handle it manually
sock.ev.on('contacts.upsert', (contacts) => {
for (const contact of contacts) {
console.log('New/updated contact:', {
id: contact.id,
name: contact.name,
notify: contact.notify,
verifiedName: contact.verifiedName
});
}
});
sock.ev.on('contacts.update', (updates) => {
for (const update of updates) {
console.log('Contact updated:', update);
}
});
// Force resync
await sock.resyncAppState(['contacts'], true);
Chat Sync
javascript
// Chat sync events
sock.ev.on('chats.upsert', (chats) => {
for (const chat of chats) {
console.log('New chat:', {
id: chat.id,
name: chat.name,
unreadCount: chat.unreadCount,
archived: chat.archive
});
}
});
sock.ev.on('chats.update', (updates) => {
for (const update of updates) {
console.log('Chat update:', {
id: update.id,
changes: Object.keys(update)
});
}
});
// Chat structure
{
id: '6281234567890@s.whatsapp.net',
name: 'Contact Name',
unreadCount: 5,
unreadCountMarkedAsCal: 3,
archive: false,
markAsUnread: false,
pin: null,
mute: null,
spam: false,
journal: false,
lang: 'en'
}
Group Sync
javascript
// Group sync events
sock.ev.on('groups.upsert', (groups) => {
console.log('New groups:', groups.length);
});
sock.ev.on('groups.update', (updates) => {
for (const update of updates) {
console.log('Group updated:', {
id: update.id,
changes: update
});
}
});
// Get all groups
const allGroups = await sock.groupFetchAllParticipating();
console.log('Total groups:', Object.keys(allGroups).length);
// Group metadata structure
{
id: 'group@g.us',
subject: 'Group Name',
description: 'Description',
participants: [
{ id: 'user@g.us', admin: 'admin' },
{ id: 'user2@g.us', admin: null }
],
size: 50,
creation: 1234567890,
owner: 'owner@g.us',
restrict: false,
announce: false
}
Newsletter Sync
javascript
// Newsletter events
sock.ev.on('newsletter.reaction', (reaction) => {
console.log({
id: reaction.id, // newsletter jid
serverId: reaction.server_id, // message server ID
reaction: reaction.reaction
});
});
sock.ev.on('newsletter.view', (view) => {
console.log({
id: view.id,
serverId: view.server_id,
views: view.count
});
});
sock.ev.on('newsletter-participants.update', (update) => {
console.log({
id: update.id, // newsletter jid
author: update.author, // who made the change
user: update.user, // affected user (LID)
newRole: update.new_role,
action: update.action // 'add', 'remove'
});
});
sock.ev.on('newsletter-settings.update', (update) => {
console.log({
id: update.id,
settings: update.update
});
});
App State Sync Events
javascript
// App state includes privacy, settings, labels, etc.
// Types of app state collections
const COLLECTIONS = [
'contact', // Contact list
'chat', // Chat list
'group', // Groups
'starred', // Starred messages
'payment', // Payment settings
'privacy', // Privacy settings
'label', // Labels
'mute', // Muted chats
'archive', // Archived chats
'disappearing', // Disappearing settings
'security', // Security settings
'wallpaper' // Wallpaper settings
];
// Resync specific collection
await sock.resyncAppState(['privacy'], false);
// Full resync
await sock.resyncAppState(COLLECTIONS, true);
Message Encryption Flow
text
# E2E Encryption Flow
## 1. For Individual Chats
1. Generate message key from ratchet
2. Derive encryption keys (HMAC key, IV)
3. Encrypt message with AES-GCM
4. Sign with HMAC
5. Send to server
## 2. For Group Chats
1. Generate sender key for group
2. Distribute to all participants
3. Create sender key message
4. Encrypt with sender key
5. Send to server
## 3. Verification
1. Receiver gets encrypted message
2. Generate same message key
3. Decrypt with AES-GCM
4. Verify HMAC signature
5. Display message
## Key Storage
- Pre-keys: Used once per conversation
- Session keys: Derived from pre-keys
- Sender keys: Group-specific
Webhook Events Mapping
javascript
// Map Socketon events to webhook events
const webhookEvents = {
'message': 'messages.upsert',
'message_update': 'messages.update',
'message_deleted': 'messages.update',
'message_read': 'messages.update',
'typing_start': 'presence.update',
'typing_stop': 'presence.update',
'user_online': 'presence.update',
'user_offline': 'presence.update',
'group_created': 'groups.upsert',
'group_updated': 'groups.update',
'member_joined': 'messages.upsert (stub)',
'member_left': 'messages.upsert (stub)',
'member_promoted': 'messages.upsert (stub)',
'contact_created': 'contacts.upsert',
'contact_updated': 'contacts.update',
'account_changed': 'creds.update',
'call_incoming': 'call',
'call_missed': 'call'
};
Message Filtering
javascript
// Filter which messages to process
// 1. By message type
const allowedTypes = ['conversation', 'extendedTextMessage'];
const isAllowed = msg => {
const keys = Object.keys(msg.message || {});
return keys.some(k => allowedTypes.includes(k));
};
// 2. By sender
const allowedSenders = ['628xxxx@s.whatsapp.net', '1234567890@g.us'];
const isFromAllowed = msg => {
return allowedSenders.includes(msg.key.remoteJid);
};
// 3. By group
const groupFilter = async msg => {
if (!msg.key.remoteJid.endsWith('@g.us')) return false;
const meta = await sock.groupMetadata(msg.key.remoteJid);
return meta.participants.length > 10;
};
// Apply filter
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
if (!msg.key.fromMe && isAllowed(msg) && isFromAllowed(msg)) {
await processMessage(msg);
}
}
});
Message Deduplication
javascript
// Prevent processing same message twice
class MessageDeduplicator {
constructor(maxSize = 1000) {
this.seen = new Map();
this.maxSize = maxSize;
}
isDuplicate(key) {
const msgId = key.id;
if (this.seen.has(msgId)) {
return true;
}
this.seen.set(msgId, Date.now());
// Clean old entries
if (this.seen.size > this.maxSize) {
const now = Date.now();
for (const [id, time] of this.seen) {
if (now - time > 60000) { // 1 minute
this.seen.delete(id);
}
}
}
return false;
}
}
const dedup = new MessageDeduplicator();
sock.ev.on('messages.upsert', ({ messages }) => {
for (const msg of messages) {
if (!dedup.isDuplicate(msg.key)) {
processMessage(msg);
}
}
});
Message Priority Queue
javascript
// Priority-based message queue
class PriorityQueue {
constructor() {
this.queue = [];
}
add(item, priority = 0) {
this.queue.push({ item, priority });
this.queue.sort((a, b) => b.priority - a.priority);
}
async process() {
while (this.queue.length > 0) {
const { item } = this.queue.shift();
await processItem(item);
}
}
}
// Usage
const queue = new PriorityQueue();
// Add messages with priority
queue.add({ jid: 'group@g.us', text: 'Urgent!' }, 10); // High priority
queue.add({ jid: 'user@g.us', text: 'Hello' }, 5); // Normal
queue.add({ jid: 'user2@g.us', text: 'Update' }, 1); // Low
// Process
await queue.process();
Bulk Operations
javascript
// Bulk message sending with batching
async function bulkSend(messages, batchSize = 10, delay = 1000) {
const results = [];
for (let i = 0; i < messages.length; i += batchSize) {
const batch = messages.slice(i, i + batchSize);
const batchResults = await Promise.allSettled(
batch.map(msg => sock.sendMessage(msg.jid, msg.content))
);
results.push(...batchResults);
if (i + batchSize < messages.length) {
await new Promise(r => setTimeout(r, delay));
}
}
return results;
}
// Usage
const messages = [
{ jid: 'user1@g.us', content: { text: 'Hello 1' } },
{ jid: 'user2@g.us', content: { text: 'Hello 2' } },
// ... more messages
];
const results = await bulkSend(messages, 5, 2000);
Scheduled Messages
javascript
// Schedule messages for later sending
class MessageScheduler {
constructor(sock) {
this.sock = sock;
this.scheduled = new Map();
}
schedule(message, sendAt) {
const delay = sendAt - Date.now();
if (delay <= 0) {
throw new Error('Scheduled time must be in the future');
}
const id = `msg_${Date.now()}_${Math.random()}`;
const timeout = setTimeout(async () => {
await this.sock.sendMessage(message.jid, message.content);
this.scheduled.delete(id);
}, delay);
this.scheduled.set(id, { timeout, message, sendAt });
return id;
}
cancel(id) {
const scheduled = this.scheduled.get(id);
if (scheduled) {
clearTimeout(scheduled.timeout);
this.scheduled.delete(id);
return true;
}
return false;
}
cancelAll() {
for (const [id, scheduled] of this.scheduled) {
clearTimeout(scheduled.timeout);
}
this.scheduled.clear();
}
}
const scheduler = new MessageScheduler(sock);
// Schedule
const id = scheduler.schedule(
{ jid: 'user@g.us', content: { text: 'Scheduled!' } },
new Date('2026-01-01T00:00:00')
);
// Cancel
scheduler.cancel(id);
Auto-Reply System
javascript
// Complete auto-reply system
class AutoReply {
constructor(sock) {
this.sock = sock;
this.rules = [];
}
addRule(pattern, response, options = {}) {
this.rules.push({
pattern: new RegExp(pattern, options.flags || 'i'),
response,
priority: options.priority || 0,
enabled: true
});
}
async handle(msg) {
if (msg.key.fromMe) return;
const text = msg.message?.conversation ||
msg.message?.extendedTextMessage?.text;
if (!text) return;
// Sort by priority
const matchedRules = this.rules
.filter(r => r.enabled && r.pattern.test(text))
.sort((a, b) => b.priority - a.priority);
for (const rule of matchedRules) {
const response = typeof rule.response === 'function'
? await rule.response(msg, text)
: rule.response;
await this.sock.sendMessage(msg.key.remoteJid,
{ text: response },
{ quoted: msg }
);
return true; // Stop after first match
}
return false;
}
}
const bot = new AutoReply(sock);
// Add rules
bot.addRule('^hello$', 'Hi there! 👋');
bot.addRule('^help$', 'Available commands: help, info, status');
bot.addRule(/^echo (.+)/, (msg, match) => match[1]); // Echo
// Use
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
await bot.handle(msg);
}
});
Conversation Flow
javascript
// Handle multi-step conversations
class Conversation {
constructor(sock) {
this.sock = sock;
this.conversations = new Map();
}
start(jid, handler) {
this.conversations.set(jid, {
step: 0,
data: {},
handler
});
}
async continue(jid, input) {
const conv = this.conversations.get(jid);
if (!conv) return null;
const response = await conv.handler(conv.step, conv.data, input);
if (response.done) {
this.conversations.delete(jid);
} else {
conv.step++;
if (response.data) {
Object.assign(conv.data, response.data);
}
}
return response;
}
end(jid) {
this.conversations.delete(jid);
}
}
const conv = new Conversation(sock);
// Example: Ask for name
conv.start('user@g.us', async (step, data, input) => {
switch (step) {
case 0:
await sock.sendMessage('user@g.us', { text: 'What is your name?' });
return { done: false };
case 1:
return {
done: true,
data: { name: input }
};
}
});
Command System
javascript
// Full command system with permissions
class CommandSystem {
constructor(sock, prefix = '!') {
this.sock = sock;
this.prefix = prefix;
this.commands = new Map();
}
register(name, handler, options = {}) {
this.commands.set(name, {
handler,
description: options.description || '',
usage: options.usage || '',
aliases: options.aliases || [],
permission: options.permission || 'all', // 'all', 'admin', 'owner'
cooldown: options.cooldown || 0
});
}
async handle(msg) {
const text = msg.message?.conversation ||
msg.message?.extendedTextMessage?.text;
if (!text.startsWith(this.prefix)) return;
const [cmdName, ...args] = text.slice(1).split(' ');
const cmd = this.commands.get(cmdName.toLowerCase());
if (!cmd) return;
// Check permission
if (cmd.permission === 'admin') {
const isAdmin = await this.checkAdmin(msg.key.remoteJid);
if (!isAdmin) {
await this.reply(msg, 'Admin only!');
return;
}
}
// Execute
try {
await cmd.handler(msg, args);
} catch (error) {
await this.reply(msg, `Error: ${error.message}`);
}
}
async reply(msg, text) {
await this.sock.sendMessage(msg.key.remoteJid,
{ text }, { quoted: msg }
);
}
async checkAdmin(jid) {
const meta = await this.sock.groupMetadata(jid);
const participant = meta.participants.find(p =>
p.id === this.sock.authState.creds.me.id
);
return participant?.admin !== null;
}
}
const cmds = new CommandSystem(sock);
// Register commands
cmds.register('ping', (msg) => cmds.reply(msg, 'Pong!'), {
description: 'Check bot is running'
});
cmds.register('help', (msg, args) => {
const helpText = Array.from(cmds.commands.values())
.map(c => `• ${c.description}`)
.join('\n');
cmds.reply(msg, helpText);
});
// Use
sock.ev.on('messages.upsert', ({ messages }) => {
for (const msg of messages) {
cmds.handle(msg);
}
});
Middleware System
javascript
// Message middleware for processing pipeline
class Middleware {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
}
async process(msg, context = {}) {
let index = 0;
const next = async () => {
if (index >= this.middlewares.length) {
return context;
}
const fn = this.middlewares[index++];
return await fn(msg, context, next);
};
return await next();
}
}
const mw = new Middleware();
// Logging middleware
mw.use(async (msg, ctx, next) => {
console.log('Processing:', msg.key.id);
await next();
console.log('Done:', msg.key.id);
});
// Rate limiting middleware
const userLimits = new Map();
mw.use(async (msg, ctx, next) => {
const user = msg.key.remoteJid;
const now = Date.now();
const limit = userLimits.get(user) || { count: 0, reset: now + 60000 };
if (now > limit.reset) {
limit.count = 0;
limit.reset = now + 60000;
}
if (limit.count > 20) {
throw new Error('Rate limited');
}
limit.count++;
userLimits.set(user, limit);
await next();
});
// Spam filter middleware
mw.use(async (msg, ctx, next) => {
const text = msg.message?.conversation;
if (text && text.length > 500) {
throw new Error('Message too long');
}
await next();
});
// Use
sock.ev.on('messages.upsert', async ({ messages }) => {
for (const msg of messages) {
await mw.process(msg);
}
});
Plugin System
javascript
// Simple plugin system
class PluginManager {
constructor(sock) {
this.sock = sock;
this.plugins = new Map();
}
register(name, plugin) {
if (this.plugins.has(name)) {
throw new Error(`Plugin ${name} already registered`);
}
// Initialize plugin
if (plugin.init) {
plugin.init(this.sock);
}
// Register event handlers
if (plugin.events) {
for (const [event, handler] of Object.entries(plugin.events)) {
this.sock.ev.on(event, handler);
}
}
this.plugins.set(name, plugin);
}
unregister(name) {
const plugin = this.plugins.get(name);
if (!plugin) return;
if (plugin.events) {
for (const [event, handler] of Object.entries(plugin.events)) {
this.sock.ev.off(event, handler);
}
}
if (plugin.cleanup) {
plugin.cleanup();
}
this.plugins.delete(name);
}
}
const plugins = new PluginManager(sock);
// Example plugin
plugins.register('auto-react', {
init(sock) {
this.reactions = ['👍', '❤️', '🔥'];
},
events: {
'messages.upsert': async ({ messages }) => {
for (const msg of messages) {
if (msg.key.remoteJid.includes('@newsletter')) {
const reaction = this.reactions[Math.floor(Math.random() * this.reactions.length)];
await sock.newsletterReactMessage(msg.key.remoteJid, msg.key.id, reaction);
}
}
}
}
});
State Management
javascript
// Persistent state management
class BotState {
constructor(persistPath) {
this.data = {};
this.path = persistPath;
this.load();
}
load() {
try {
const file = require('fs').readFileSync(this.path, 'utf8');
this.data = JSON.parse(file);
} catch {
this.data = {};
}
}
save() {
require('fs').writeFileSync(this.path, JSON.stringify(this.data, null, 2));
}
get(key, defaultValue) {
return this.data[key] ?? defaultValue;
}
set(key, value) {
this.data[key] = value;
this.save();
}
delete(key) {
delete this.data[key];
this.save();
}
increment(key, amount = 1) {
this.set(key, this.get(key, 0) + amount);
}
toggle(key) {
this.set(key, !this.get(key, false));
}
}
const state = new BotState('./state.json');
// Usage
state.set('greeting', 'Hello!');
state.increment('messageCount');
console.log(state.get('messageCount'));
Configuration Management
javascript
// Bot configuration management
const defaultConfig = {
prefix: '!',
admins: [],
cooldown: 1000,
maxLength: 4096,
allowedGroups: [],
blockedUsers: [],
greeting: null,
greetingDelay: 5000,
autoReply: true,
logLevel: 'info'
};
class Config {
constructor(path) {
this.path = path;
this.config = { ...defaultConfig };
this.load();
}
load() {
try {
const loaded = JSON.parse(require('fs').readFileSync(this.path));
this.config = { ...defaultConfig, ...loaded };
} catch {}
}
save() {
require('fs').writeFileSync(this.path, JSON.stringify(this.config, null, 2));
}
get(key) {
return this.config[key];
}
set(key, value) {
this.config[key] = value;
this.save();
}
reset() {
this.config = { ...defaultConfig };
this.save();
}
}
const config = new Config('./config.json');
// Usage
if (config.get('admins').includes(jid)) {
// User is admin
}
Statistics Tracking
javascript
// Bot statistics
class Statistics {
constructor() {
this.stats = {
messagesReceived: 0,
messagesSent: 0,
commandsExecuted: 0,
errors: 0,
startTime: Date.now()
};
}
increment(key) {
this.stats[key] = (this.stats[key] || 0) + 1;
}
get(key) {
return this.stats[key];
}
getAll() {
return {
...this.stats,
uptime: Date.now() - this.stats.startTime,
messagesPerMinute: this.stats.messagesReceived /
((Date.now() - this.stats.startTime) / 60000)
};
}
reset() {
this.stats = {
messagesReceived: 0,
messagesSent: 0,
commandsExecuted: 0,
errors: 0,
startTime: Date.now()
};
}
}
const stats = new Statistics();
// Track in handlers
sock.ev.on('messages.upsert', () => stats.increment('messagesReceived'));
sock.ev.on('messages.update', () => stats.increment('messagesSent'));
// Get stats
console.log(stats.getAll());
Final Checklist
text
# Socketon v1.8.26 Complete Feature Checklist
## Core Features
- [x] Connection Management
- [x] Send/Receive Messages
- [x] Media Handling
- [x] Group Management
- [x] Newsletter Support
- [x] Profile Management
- [x] Privacy Settings
- [x] Presence
- [x] Authentication
## Advanced Features
- [x] E2E Encryption
- [x] Multi-Device
- [x] Auto React
- [x] Auto Follow
- [x] Call Handling
- [x] Business API
- [x] Registration
## Infrastructure
- [x] Event System
- [x] Session Management
- [x] Message Queue
- [x] Rate Limiting
- [x] Error Handling
- [x] Logging
## Documentation
- [x] API Reference
- [x] Examples
- [x] Troubleshooting
- [x] Best Practices
- [x] Production Guide
## Support
- [x] GitHub Issues
- [x] NPM Package
- [x] Website
- [x] WhatsApp Support