innovators-bot2
Version:
1,098 lines (999 loc) ⢠97.8 kB
JavaScript
const { WhatsAppClient,
STATUS_BACKGROUNDS,
STATUS_FONTS,
renderLatexToPng,
uploadUnencryptedToWA,
RichSubMessageType
} = require('./index')
const qrcode = require('qrcode-terminal')
const fs = require('fs');
const readline = require('readline');
const path = require('path');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
String.prototype.toTitleCase = function () {
return this.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
};
const question = (text) => new Promise((resolve) => rl.question(text, resolve));
async function start() {
const sessionDir = ".Sessions";
const hasSession = fs.existsSync(path.join(sessionDir, 'creds.json'));
let authMethod = 'qr';
let pairingPhoneNumber = null;
if (!hasSession) {
console.log('\nš± WhatsApp Bot Setup');
console.log('-------------------');
console.log('1. QR Code');
console.log('2. Pairing Code');
const choice = await question('\nChoose authentication method (1 or 2): ');
authMethod = choice === '2' ? 'pairing' : 'qr';
if (authMethod === 'pairing') {
pairingPhoneNumber = await question('Enter phone number for pairing (e.g. 923224559543): ');
if (!pairingPhoneNumber) {
console.log('ā Phone number is required for pairing method.');
process.exit(1);
}
}
} else {
console.log('\nš Existing session found! Skipping setup...');
}
rl.close();
const client = new WhatsAppClient({
sessionName: sessionDir,
authmethod: authMethod,
pairingPhoneNumber: pairingPhoneNumber,
ai: true, // Enable/Disable AI flag for outgoing messages (default: true)
// Message store persistence configuration
messageStoreFilePath: path.join(sessionDir, 'message-store.json'),
autoSaveInterval: 5 * 60 * 1000, // Auto-save every 5 minutes
maxMessagesPerChat: 1000, // Keep last 1000 messages per chat
messageTTL: 24 * 60 * 60 * 1000 // Messages expire after 24 hours
});
console.log(`\nš Initializing with ${authMethod} method...`);
// Handle QR Code
client.on('qr', qr => {
console.log('\nā
QR Code received. Scan it with WhatsApp:')
qrcode.generate(qr, { small: true })
})
client.on('pairing-code', (code) => {
console.log(`\nā
Pairing code for ${pairingPhoneNumber}: ${code}`)
})
// Handle connection events
client.on('connecting', (message) => {
console.log('ā³ Client status:', message)
})
client.on('connected', (user) => {
console.log('\n⨠Client is ready!')
console.log('User:', user.name)
console.log('Phone:', user.phone)
console.log('Plateform:', user.platform)
console.log('isOnline:', user.isOnline)
})
// Handle LID mapping updates
client.on('lid-mapping-update', (update) => {
console.log('š¦ New LID/PN mappings received')
})
// Handle Contact Events
client.on('contacts-received', (contacts) => {
console.log(`\nš„ History Sync: Received ${contacts.length} contacts`);
})
client.on('contacts-upsert', (contacts) => {
console.log(`\nš„ New Contacts: ${contacts.length} contacts added/updated`);
})
client.on('contacts-update', (updates) => {
console.log(`\nš„ Contact Updates: ${updates.length} contacts modified`);
})
// Handle Anti-Delete system
client.on('message-deleted', async (data) => {
console.log(`\nš”ļø Message from ${data.jid} was deleted!`)
await client.sendMessage(data.jid, 'ā ļø I saw you deleted that message! I have it saved in my memory. š', { quoted: data.originalMessage });
})
// Example of listening to the new events
client.on('message-stored', (messages) => {
//console.log(`${messages.length} messages were just cached in the store.`);
});
client.on('store-loaded', (info) => {
console.log(`\nš¾ Message store loaded: ${info.messageCount} messages restored from file`);
});
client.on('store-cleared', () => {
console.log('Main message store has been purged.');
});
// Handle message reactions
client.on('message-reaction', async (reaction) => {
console.log('\nš Message Reaction Received!')
console.log('Chat:', reaction.from)
console.log('Sender:', reaction.sender)
console.log('Emoji:', reaction.emoji || '(removed)')
console.log('Is Removed:', reaction.isRemoved)
console.log('Message ID:', reaction.messageKey.id)
})
client.on('poll-votes-update', async (data) => {
console.log('\nš Poll Votes Updated!');
console.log('Chat:', data.jid);
console.log('Voter:', data.voter);
// 1. Extract Poll Creation Message (Question, Options, etc.)
const pollCreation = data.pollCreationMessage;
if (pollCreation && pollCreation.message) {
const pollMessage = pollCreation.message.pollCreationMessage ||
pollCreation.message.pollCreationMessageV2 ||
pollCreation.message.pollCreationMessageV3;
if (pollMessage) {
console.log('\nš Poll Creation Details:');
console.log('Question:', pollMessage.name);
console.log('Options:', pollMessage.options?.map(o => o.optionName) || []);
}
}
// 2. Extract voters array from pollUpdate
console.log('\nš³ļø Vote Breakdown:');
let totalVotesCount = 0;
data.pollUpdate.forEach((option) => {
console.log(`--> ${option.name}: ${option.voters.length} vote(s) ${JSON.stringify(option.voters)}`);
totalVotesCount += option.voters.length;
});
console.log(`--> Total Votes Cast: ${totalVotesCount}`);
const winner = data.pollUpdate.reduce((prev, current) =>
prev.voters.length > current.voters.length ? prev : current);
console.log(`--> The Winner Is ${winner.name} With ${winner.voters.length} votes`);
});
client.on('call', (call) => {
const callData = call[0]; // Get the first call object from the array
if (callData.status !== 'offer') return;
console.log('\nš Call Received!')
console.log('Chat ID:', callData.chatId)
console.log('From:', callData.from)
console.log('Call ID:', callData.id)
console.log('Date:', callData.date)
console.log('Offline:', callData.offline)
console.log('Status:', callData.status)
console.log('Is Video:', callData.isVideo)
console.log('Is Group:', callData.isGroup)
console.log('Phone Number:', callData.phoneNumber)
})
client.on('disconnected', (error) => {
console.log('ā Client disconnected')
})
client.on('group-left', (info) => {
console.log(`Left group ${info.id}: ${info.reason}`);
});
// Connect to WhatsApp
client.connect()
client.on('status', async status => {
console.log('Status Received');
console.log('Number:', status.from);
console.log('Sender:', status.raw.pushName);
console.log('Message:', status.body);
console.log('Has Media:', status.hasMedia);
// Mark status as read using the complete message key
//await client.readMessage(status.key);
//Reply With Emoji
//await status.reply('Liked Your Status! ā¤ļø');
await status.like('ā¤ļø');
console.log('Status Seen! and Replied With Emoji')
});
// Listen for incoming messages
let lastOutgoingCallId = null;
let lastOutgoingCallJid = null;
let autoCancelCallTimer = null;
client.on('message', async msg => {
if (msg.body === '') {
return
}
console.log('Message Received');
isGroupMsg = msg.isGroup
if (isGroupMsg) {
msgFrom = msg.from
} else {
msgFrom = msg.sender
}
console.log('Msg From:', msg.from);
console.log('Msg Sender:', msg.sender);
console.log('Sender Name:', msg.raw.pushName);
console.log('Message:', msg.body);
console.log('Is Group:', msg.isGroup);
// Mark the message as read
await client.readMessage(msg.raw.key)
const command = msg.body.split(' ')[0].toLowerCase()
const args = msg.body.split(' ').slice(1).join(' ')
switch (command) {
case '!ping':
await client.sendMessage(msgFrom, 'Hello Pong! š')
break
case '!poll':
await client.sendMessage(msgFrom, '', {
poll: {
name: 'Which programming language do you like most?',
options: ['JavaScript', 'Python', 'C++', 'Java'],
selectableOptionsCount: 1,
messageId: 'poll1'
}
});
break
case '!echo':
if (args) {
await client.sendMessage(msgFrom, args)
} else {
await client.sendMessage(msgFrom, 'Please provide text to echo')
}
break
case '!mention':
const number = msg.sender.split('@')[0]
await client.sendMessage(msgFrom, {
type: 'text',
text: `Hey @${number}! How are you?`,
mentions: [number]
})
break
case '!mentionall':
if (!isGroupMsg) {
await client.sendMessage(msgFrom, 'This command is only for groups')
return
}
await client.sendMessage(msgFrom, {
type: 'text',
text: `Hey @all! How are you?`,
mentions: ['@all']
})
break
case '!reply':
await msg.reply('This is a reply message')
break
case '!location':
await client.sendMessage(msgFrom, {
type: 'location',
latitude: 24.121231,
longitude: 55.1121221
})
break
case '!contact':
await client.sendMessage(msgFrom, {
type: 'contact',
fullName: 'John Doe',
organization: 'Example Corp',
phoneNumber: '1234567890'
})
break
case '!react':
await client.sendMessage(
msgFrom,
{
type: 'reaction',
emoji: 'š',
message: { key: msg.raw.key }
}
)
break
case '!media':
if (fs.existsSync('./example.jpg')) {
await client.sendMedia(msgFrom, './example.jpg', {
caption: 'Check out this image!'
})
} else {
await client.sendMessage(msgFrom, 'Example image not found')
}
break
case '!urlimage':
try {
const integrationFormula = '\\dpi{900}\\int\\frac{1}{x}dx=\\ln\\left|x\\right|+C';
const mediaurl = `https://latex.codecogs.com/png.image?${encodeURIComponent(integrationFormula)}`;
await client.sendMedia(msgFrom, mediaurl, {
caption: 'Check out this image!'
})
} catch (error) {
await client.sendMessage(msgFrom, `Failed to send image from URL: ${error.message}`)
}
break
case '!doc':
if (fs.existsSync('./example.pdf')) {
await client.sendDocument(msgFrom, './example.pdf', 'Check out this document!')
} else {
await client.sendMessage(msgFrom, 'Example document not found')
}
break
case '!list':
await client.SendList(msgFrom, {
text: 'Please select an option from the list below:',
title: 'Comprehensive Menu',
buttonText: 'View All Options',
footer: 'Scroll to see more options',
sections: [
{
title: 'Main Options',
rows: [
{ title: 'Account Settings', id: 'account_settings', description: 'Manage your account preferences' },
{ title: 'Profile', id: 'profile', description: 'View and edit your profile' },
{ title: 'Notifications', id: 'notifications', description: 'Configure notification settings' },
{ title: 'Privacy', id: 'privacy', description: 'Privacy and security settings' },
{ title: 'Security', id: 'security', description: 'Security and login options' },
{ title: 'Payments', id: 'payments', description: 'Manage payment methods' },
{ title: 'Subscriptions', id: 'subscriptions', description: 'View your subscriptions' },
]
},
{
title: 'More Options',
rows: [
{ title: 'Themes', id: 'themes', description: 'Change app appearance' },
{ title: 'Font Size', id: 'font_size', description: 'Adjust text size' },
{ title: 'Dark Mode', id: 'dark_mode', description: 'Toggle dark theme' },
{ title: 'Offline Mode', id: 'offline', description: 'Use without internet' },
{ title: 'Data Saver', id: 'data_saver', description: 'Reduce data usage' },
{ title: 'Storage', id: 'storage', description: 'Manage local storage' },
{ title: 'Cache', id: 'cache', description: 'Clear cached data' },
]
}
]
})
break
case '!buttons':
// Example: Send a text interactive message (modern Baileys format)
await client.sendButtons(msgFrom, {
text: 'Do you like this bot?',
title: 'Feedback',
subtitle: 'Let us know!',
footer: 'Powered by Baileys',
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'ā
Yes',
id: 'text_yes'
})
},
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'ā No',
id: 'text_no'
})
}
]
});
// Example: Send an image interactive message (modern Baileys format)
await client.sendButtons(msgFrom, {
imagePath: './example.jpg',
caption: 'here is captions of image\nwith linebreaks', // Keep it short and concise
title: 'Image Title', // Max 24 chars
subtitle: 'Image Subtitle (but optional)', // Optional, appears below title
footer: 'Image Footer',
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'š Like',
id: 'img_like'
})
},
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'š Dislike',
id: 'img_dislike'
})
},
{
name: 'cta_call',
buttonParamsJson: JSON.stringify({
display_text: 'š Call Us',
phone_number: '+1234567890'
})
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({
display_text: 'š Visit Website',
url: 'https://example.com',
merchant_url: 'https://example.com'
})
},
{
name: 'cta_copy',
buttonParamsJson: JSON.stringify({
display_text: 'š Copy Link',
copy_code: 'https://example.com/copied'
})
}
]
});
break
case '!quickreplyv2':
await client.sendQuickReplyV2(msgFrom, 'Please select an option below:', [
{ id: 'btn-1', displayText: 'ā
Accept' },
{ id: 'btn-2', displayText: 'ā Reject' },
{ id: 'btn-3', displayText: 'š Contact Support' }
], { footer: 'Powered by Innovators Soft' });
break
case '!urlbuttonv2':
await client.sendUrlButtonV2(msgFrom, 'Visit our website for more info', [
{ displayText: 'š Open Website', url: 'https://example.com' }
], { title: 'Product Info', footer: 'Click to open' });
break
case '!copycodev2':
await client.sendCopyCodeV2(msgFrom, 'Your OTP Code is:', '123456', 'š Copy Code');
break
case '!combinedv2':
await client.sendCombinedButtonsV2(msgFrom, 'Choose an action:', [
{ type: 'reply', displayText: 'š Order Now', id: 'order' },
{ type: 'url', displayText: 'š Website', url: 'https://example.com' },
{ type: 'call', displayText: 'š Phone', phoneNumber: '+923224559543' },
{ type: 'copy', displayText: 'š Copy Promo', copyCode: 'PROMO2024' }
], { title: 'Main Menu', footer: 'Innovators Soft' });
break
case '!listv2':
await client.sendListV2(msgFrom, {
title: 'š Product Menu',
buttonText: 'View Menu',
description: 'Please select a product',
footer: 'Powered by Innovators Soft',
sections: [
{
title: 'Food',
rows: [
{ rowId: 'nasi-goreng', title: 'Fried Rice', description: '$2.50' },
{ rowId: 'mie-goreng', title: 'Fried Noodles', description: '$2.00' }
]
},
{
title: 'Beverages',
rows: [
{ rowId: 'es-teh', title: 'Ice Tea', description: '$0.50' },
{ rowId: 'kopi', title: 'Coffee', description: '$1.00' }
]
}
]
});
break
case '!cards':
if (fs.existsSync('./example.jpg')) {
const imageBuffer = fs.readFileSync('./example.jpg');
const videoBuffer = fs.readFileSync('./example.mp4');
await client.sendcards(msgFrom, {
text: 'Body Message',
title: 'Title Message',
subtile: 'Subtitle Message',
footer: 'Footer Message',
cards: [
{
image: imageBuffer, // use local buffer
title: 'Title Cards 1',
body: 'Body Cards 1',
footer: 'Footer Cards 1',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
id: 'ID'
})
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
url: 'https://www.example.com'
})
}
]
},
{
video: { url: 'https://files.inqscribe.com/samples/IS_Intro.mp4' },//videoBuffer, // use same local buffer for second card
title: 'Title Cards 2',
body: 'Body Cards 2',
footer: 'Video URL',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
id: 'ID2'
})
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
url: 'https://www.example.com'
})
}
]
},
{
video: videoBuffer, // use same local buffer for second card
title: 'Title Cards 3',
body: 'Body Cards 3',
footer: 'Video Buffer',
buttons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
id: 'ID3'
})
},
{
name: 'cta_url',
buttonParamsJson: JSON.stringify({
display_text: 'Display Button',
url: 'https://www.example.com'
})
}
]
}
]
});
} else {
await client.sendMessage(msgFrom, 'Example image (example.jpg) not found for cards demonstration.');
}
break
case '!call':
try {
if (autoCancelCallTimer) {
clearTimeout(autoCancelCallTimer);
autoCancelCallTimer = null;
}
const result = await client.initiateCall(msgFrom);
lastOutgoingCallId = result?.callId || null;
lastOutgoingCallJid = msgFrom;
await client.sendMessage(msgFrom, `Calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
if (lastOutgoingCallId) {
autoCancelCallTimer = setTimeout(async () => {
try {
await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
} catch (error) {
console.error('Error auto-canceling call:', error);
} finally {
lastOutgoingCallId = null;
lastOutgoingCallJid = null;
autoCancelCallTimer = null;
}
}, 10000);
}
} catch (error) {
console.error('Error initiating voice call:', error);
await client.sendMessage(msgFrom, 'Failed to initiate call');
}
break
case '!videocall':
try {
if (autoCancelCallTimer) {
clearTimeout(autoCancelCallTimer);
autoCancelCallTimer = null;
}
const result = await client.initiateCall(msgFrom, { isVideo: true });
lastOutgoingCallId = result?.callId || null;
lastOutgoingCallJid = msgFrom;
await client.sendMessage(msgFrom, `Video calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
if (lastOutgoingCallId) {
autoCancelCallTimer = setTimeout(async () => {
try {
await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
} catch (error) {
console.error('Error auto-canceling video call:', error);
} finally {
lastOutgoingCallId = null;
lastOutgoingCallJid = null;
autoCancelCallTimer = null;
}
}, 10000);
}
} catch (error) {
console.error('Error initiating video call:', error);
await client.sendMessage(msgFrom, 'Failed to initiate video call');
}
break
case '!cancelcall':
try {
if (autoCancelCallTimer) {
clearTimeout(autoCancelCallTimer);
autoCancelCallTimer = null;
}
if (!lastOutgoingCallId) {
await client.sendMessage(msgFrom, 'No outgoing call to cancel');
break;
}
await client.cancelCall(lastOutgoingCallId, msgFrom);
await client.sendMessage(msgFrom, `Canceled call: ${lastOutgoingCallId}`);
lastOutgoingCallId = null;
lastOutgoingCallJid = null;
} catch (error) {
console.error('Error canceling call:', error);
await client.sendMessage(msgFrom, 'Failed to cancel call');
}
break
case '!help':
const help = `*š Available Commands List*\n\n` +
`*š¹ Basic Commands*\n` +
`⢠!ping - Check if bot is alive\n` +
`⢠!echo <text> - Echo back your text\n` +
`⢠!help - Show this command list\n\n` +
`*š¬ Messaging*\n` +
`⢠!mention - Mention you in a message\n` +
`⢠!reply - Reply to your message\n` +
`⢠!react - React to your message with ā¤ļø\n` +
` (Note: Reactions are auto-detected!)\n` +
`*š¼ļø Media & Content*\n` +
`⢠!media - Send an example image\n` +
`⢠!doc - Send an example document\n` +
`⢠!location - Send a location\n` +
`⢠!contact - Send a contact card\n` +
`⢠!sticker - Send an example sticker\n\n` +
`*š„ Group Management*\n` +
`⢠!groups - List all your groups\n` +
`⢠!add <number> - Add participant\n` +
`⢠!invite <number> - Send group invite link\n` +
`⢠!remove <number> - Remove participant\n` +
`⢠!promote <number> - Make admin\n` +
`⢠!demote <number> - Remove admin\n` +
`⢠!creategroup <name> - Create a new group\n` +
`⢠!groupsubject <name> - Change group name\n` +
`⢠!groupdesc <text> - Change group description\n` +
`⢠!groupsetting <setting> - Change group settings\n` +
`⢠!invitecode - Get group invite code\n` +
`⢠!revokeinvite - Revoke group invite code\n` +
`⢠!leavegroup - Leave the group\n` +
`⢠!joingroup <code> - Join group by invite code\n` +
`⢠!groupinfo [jid|code] - Full group details with participants\n` +
`⢠!joinrequests - List pending join requests\n` +
`⢠!approvejoin <number> - Approve join request\n` +
`⢠!rejectjoin <number> - Reject join request\n` +
`⢠!ephemeral <seconds> - Toggle disappearing msgs\n` +
`⢠!addmode <mode> - Change who can add members\n\n` +
`*š Privacy*\n` +
`⢠!block <number> - Block a user\n` +
`⢠!unblock <number> - Unblock a user\n` +
`⢠!privacy - Get privacy settings\n` +
`⢠!blocklist - Get blocked contacts\n` +
`⢠!lastseenprivacy <value> - Update last seen\n` +
`⢠!onlineprivacy <value> - Update online status\n` +
`⢠!pfpprivacy <value> - Update profile pic privacy\n` +
`⢠!statusprivacy <value> - Update status privacy\n` +
`⢠!readreceiptprivacy <value> - Update read receipts\n` +
`⢠!groupaddprivacy <value> - Who can add to groups\n` +
`⢠!disappearing <seconds> - Default disappearing mode\n` +
`⢠!updatestatus <text> - Update profile status\n` +
`⢠!updatename <text> - Update profile name\n\n` +
`*š¤ Rich AI Messaging*\n` +
`⢠!table - Send a formatted table\n` +
`⢠!richlist - Send a bulleted list\n` +
`⢠!codeblock - Send a syntax-highlighted code snippet\n` +
`⢠!latex - Send LaTeX text\n` +
`⢠!lateximage - Send LaTeX image\n` +
`⢠!latexinlineimage - Send LaTeX inline image\n` +
`⢠!rich - Send demo rich message\n` +
`⢠!markdown - Send native markdown message\n` +
`⢠!richresponse - Send rich text with code block\n\n` +
`*šļø Templates & Buttons*\n` +
`⢠!buttons - Button template\n` +
`⢠!list - Scrollable list\n\n` +
`⢠!quickreplyv2 - Quick reply buttons V2\n` +
`⢠!urlbuttonv2 - URL button V2\n` +
`⢠!copycodev2 - Copy code button V2\n` +
`⢠!combinedv2 - Mixed buttons V2\n` +
`⢠!listv2 - Interactive list V2\n` +
`⢠!cards - Interactive cards message\n\n` +
`*š¢ Status*\n` +
`⢠!statustext - Post a text status\n` +
`⢠!statusimage - Post an image status\n` +
`⢠!statusvideo - Post a video status\n` +
`⢠!statusvoice - Post a voice note status\n` +
`⢠!groupstatus - Post a status directly inside a group (@g.us)\n\n` +
`*š Calls*\n` +
`⢠!call - Initiate a voice call\n` +
`⢠!videocall - Initiate a video call\n` +
`⢠!cancelcall - Cancel last outgoing call\n\n` +
`*ļæ½ Message Store*\n` +
`⢠!messages - Get stored messages for this chat\n` +
`⢠!allmessages - Get statistics for all stored chats\n` +
`⢠!message - Get a specific message by ID\n` +
`⢠!stats - Get store capacity statistics\n\n` +
`*ļæ½š LID/PN/JID Management*\n` +
`⢠!lid - Get your LID\n` +
`⢠!pn <lid> - Get PN from LID\n` +
`⢠!parse <jid> - Parse JID info\n` +
`⢠!normalize <phone> - Normalize to JID\n\n` +
`*š”ļø Protection*\n` +
`⢠Anti-Delete: Automatically active\n\n` +
`*āļø Admin Commands*\n` +
`⢠!read - Mark as read\n` +
`⢠!typing - Show typing indicator\n` +
`⢠!recording - Show recording indicator\n` +
`⢠!paused - Clear typing or recording indicator\n` +
`⢠!typing_simulate - Simulate typing for 5s then send msg\n` +
`⢠!typing_start - Start typing with auto-pause\n` +
`⢠!typing_stop - Stop typing indicator\n` +
`⢠!recording_start - Start recording indicator\n` +
`⢠!logout - End session\n\n` +
`*š Note*:\nReplace <number> with phone number\n(without + or spaces)`
await client.sendMessage(msgFrom, help)
break
case '!table':
await client.sendTable(
msgFrom,
'Price List',
['Item', 'Qty', 'Price'],
[
['Apple', '3', '$1.50'],
['Banana', '6', '$0.90'],
['Cherry', '1', '$3.00']
],
msg.raw,
{ headerText: 'Here is your order summary:', footer: 'Thank you!' }
);
break;
case '!richlist':
await client.sendRichList(
msgFrom,
'Available Commands',
['!help', '!ping', '!menu', '!info'],
msg.raw,
{ headerText: 'Bot commands:', footer: 'Type any command to use it.' }
);
break;
case '!codeblock':
await client.sendCodeBlock(
msgFrom,
`async function fetchData(url) {\n const res = await fetch(url)\n return res.json()\n}`,
msg.raw,
{ title: 'š¦ Example ā fetch helper', language: 'javascript', footer: 'Copy and paste into your project.' }
);
break;
case '!latex':
await client.sendLatex(
msgFrom,
{ text: 'Quadratic formula:', expressions: [{ latexExpression: 'x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}' }] }
);
break;
case '!lateximage':
try {
await client.sendLatexImage(
msgFrom,
{
formula: 'E=mc^2',
caption: 'Mass-Energy Equivalence (DPI 600)'
}
);
} catch (error) {
return (error)
}
break;
case '!latexinlineimage':
try {
await client.sendLatexInlineImage(
msgFrom,
{
expressions: [
{ latexExpression: 'e^{i\\pi} + 1 = 0' },
{ latexExpression: '\\int_a^b x^2 \\, dx = \\frac{b^3 - a^3}{3}' },
{ latexExpression: 'f(x) = \\sum_{n=0}^{\\infty} \\frac{f^{(n)}(a)}{n!} (x-a)^n' }
],
caption: true // Use each LaTeX expression as the caption for its respective image in the album
}
);
} catch (error) {
return (error)
}
break;
case '!rich':
const richLatexExpr = 'E = mc^2';
const richPngBuf = await renderLatexToPng(richLatexExpr);
const richImageUrl = (await uploadUnencryptedToWA(richPngBuf.buffer, client.sock.waUploadToServer)).url;
await client.sendRichMessage(msgFrom, [
{
messageType: RichSubMessageType.TEXT,
messageText: '# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6\n\n___\n\n> To use a horizontal line, you need to have two "\\n" above and below the "___"\n==Highlighted text==\n# By the way, ^you^ can _mix_ ==multiple markdowns== for a **richer response**\n###### Try different combinations...'
},
{
messageType: RichSubMessageType.TABLE,
tableMetadata: {
title: 'Product Prices',
rows: [
{ items: ['Product', 'Price', 'Stock'], isHeading: true },
{ items: ['Innovators Baileys Pro', '$49.99', 'In Stock'] },
{ items: ['Rust WASM Plugin', '$19.99', 'Low Stock'] }
]
}
},
{
messageType: RichSubMessageType.TEXT,
messageText: 'LaTeX Formula:'
},
{
messageType: RichSubMessageType.INLINE_IMAGE,
imageMetadata: {
imageUrl: {
imagePreviewUrl: richImageUrl,
imageHighResUrl: richImageUrl
},
imageText: richLatexExpr,
alignment: 2
}
},
{
messageType: RichSubMessageType.CODE,
codeMetadata: {
codeLanguage: 'javascript',
codeBlocks: [
{ highlightType: 1, codeContent: 'const ' },
{ highlightType: 0, codeContent: 'price = ' },
{ highlightType: 4, codeContent: '49.99' },
{ highlightType: 0, codeContent: ';\n' },
{ highlightType: 1, codeContent: 'if ' },
{ highlightType: 0, codeContent: '(price > ' },
{ highlightType: 4, codeContent: '20' },
{ highlightType: 0, codeContent: ') {\n console.log(' },
{ highlightType: 3, codeContent: '"Premium tier"' },
{ highlightType: 0, codeContent: ');\n}' }
]
}
}
], null, { useMarkdown: true });
break;
case '!markdown':
await client.sendMarkdown(
msgFrom,
'# Markdown Demo\n## Headers work\n==Highlighted text==\n_Italics_ and **Bold** are supported!',
msg.raw
);
break;
case '!richresponse':
// Demonstrate that sendMessage can now natively accept an array of rich submessages
await client.sendMessage(msgFrom, {
richResponse: [
{
messageType: 2,
messageText: '# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6\n\n___\n\n> To use a horizontal line, you need to have two "\\n" above and below the "___"\n==Highlighted text==\n# By the way, ^you^ can _mix_ ==multiple markdowns== for a **richer response**\n###### Try different combinations...'
},
{
messageType: 2,
messageText: 'And here is a syntax-highlighted code block natively passed:'
},
{
messageType: 5,
codeMetadata: {
codeLanguage: 'javascript',
codeBlocks: [
{ highlightType: 1, codeContent: 'const ' },
{ highlightType: 0, codeContent: 'greet = (name) => {\n console.log(' },
{ highlightType: 3, codeContent: '"Hello, "' },
{ highlightType: 0, codeContent: ' + name)\n}\n' },
{ highlightType: 0, codeContent: 'greet(' },
{ highlightType: 3, codeContent: '"World"' },
{ highlightType: 0, codeContent: ')' }
]
}
}
]
},
{ markdown: true });
break;
case '!groups':
try {
const groups = await client.getAllGroups()
if (groups && groups.length > 0) {
let groupList = '*Your Groups:*\n\n'
groups.forEach((group, index) => {
groupList += `${index + 1}. *${group.subject}*\n`
groupList += ` ID: ${group.id}\n`
if (group.notify) groupList += ` Notify: ${group.notify}\n`
groupList += ` Members: ${group.participants.length}\n`
if (group.desc) groupList += ` Description: ${group.desc}\n`
groupList += '\n'
})
await client.sendMessage(msgFrom, groupList)
} else {
await client.sendMessage(msgFrom, 'You are not in any groups')
}
} catch (error) {
console.error('Error fetching groups:', error)
await client.sendMessage(msgFrom, 'Failed to fetch groups')
}
break
case '!groupinfo':
try {
// Use provided group JID or current group
const groupJid = args.trim() || msg.raw.key.remoteJid
if (!groupJid || !groupJid.endsWith('@g.us')) {
await client.sendMessage(msgFrom, 'ā Please provide a group JID or use this command in a group.\nUsage: !groupinfo <groupJid>')
break
}
const groupInfo = await client.getGroupMetadata(groupJid)
if (groupInfo) {
let groupList = `*Group Info:*\n\n` +
`ID: ${groupInfo.id}\n` +
`Notify: ${groupInfo.notify || 'N/A'}\n` +
`Subject: ${groupInfo.subject}\n` +
`Owner: ${groupInfo.owner || 'N/A'}\n` +
`Created: ${new Date(groupInfo.creation * 1000).toLocaleString()}\n` +
`Members: ${groupInfo.participants.length}\n` +
`Description: ${groupInfo.desc || 'N/A'}\n\n` +
`*š„ Participants:*\n\n`
groupInfo.participants.forEach((p, i) => {
const role = p.admin === 'superadmin' ? 'š Super Admin'
: p.admin === 'admin' ? 'š”ļø Admin'
: 'š¤ Member'
groupList += `${i + 1}. ${p.id}\n`
groupList += ` Role: ${role}\n`
if (p.notify) groupList += ` Name: ${p.notify}\n`
groupList += '\n'
})
await client.sendMessage(msgFrom, groupList)
} else {
await client.sendMessage(msgFrom, 'Group not found')
}
} catch (error) {
console.error('Error fetching group info:', error)
await client.sendMessage(msgFrom, 'Failed to fetch group info')
}
break
case '!logout':
// Ask for confirmation before logging out
await client.sendButtons(msgFrom, {
text: 'Are you sure you want to logout?',
title: 'Logout Confirmation',
footer: 'Choose Yes to logout or No to cancel',
interactiveButtons: [
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'Yes',
id: 'logout_yes'
})
},
{
name: 'quick_reply',
buttonParamsJson: JSON.stringify({
display_text: 'No',
id: 'logout_no'
})
}
]
});
break;
// Handle logout confirmation
case 'Yes':
case 'yes':
case 'logout_yes':
await client.sendMessage(msgFrom, 'You have been logged out.');
await client.logout();
break;
case 'No':
case 'no':
case 'logout_no':
await client.sendMessage(msgFrom, 'Logout cancelled.');
break;
case '!lid':
// Get LID for the user's phone number
try {
const lid = await client.getLIDForPN(msgFrom);
if (lid) {
await client.sendMessage(msgFrom, `Your LID: ${lid}\nYour PN: ${msgFrom}`);
} else {
await client.sendMessage(msgFrom, `No LID found for ${msgFrom}. You might be using a PN-only session.`);
}
} catch (error) {
console.error('Error getting LID:', error);
await client.sendMessage(msgFrom, 'Failed to get LID.');
}
break;
case '!pn':
// Get PN from LID
try {
const lidToCheck = args.trim();
if (!lidToCheck) {
await client.sendMessage(msgFrom, 'Please provide a LID. Example: !pn 123456@lid');
break;
}
const pn = await client.getPNForLID(lidToCheck);
if (pn) {
await client.sendMessage(msgFrom, `Phone Number for ${lidToCheck}: ${pn}`);
} else {
await client.sendMessage(msgFrom, `No phone number found for LID: ${lidToCheck}`);
}
} catch (error) {
console.error('Error getting PN from LID:', error);
await client.sendMessage(msgFrom, 'Failed to get phone number.');
}
break;
case '!ad':
await client.sendAdReply(
ms