Files
wapp/test/complete.js
2025-12-06 18:58:29 +01:00

699 lines
25 KiB
JavaScript

const { Client, Location, Poll, List, Buttons, LocalAuth } = require('whatsapp-web.js');
const client = new Client({
authStrategy: new LocalAuth(),
// proxyAuthentication: { username: 'username', password: 'password' },
/**
* This option changes the browser name from defined in user agent to custom.
*/
// deviceName: 'Your custom name',
/**
* This option changes browser type from defined in user agent to yours. It affects the browser icon
* that is displayed in 'linked devices' section.
* Valid value are: 'Chrome' | 'Firefox' | 'IE' | 'Opera' | 'Safari' | 'Edge'.
* If another value is provided, the browser icon in 'linked devices' section will be gray.
*/
// browserName: 'Firefox',
puppeteer: {
// args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
headless: false,
},
// pairWithPhoneNumber: {
// phoneNumber: '96170100100' // Pair with phone number (format: <COUNTRY_CODE><PHONE_NUMBER>)
// showNotification: true,
// intervalMs: 180000 // Time to renew pairing code in milliseconds, defaults to 3 minutes
// }
});
// client initialize does not finish at ready now.
client.initialize();
client.on('loading_screen', (percent, message) => {
console.log('LOADING SCREEN', percent, message);
});
client.on('qr', async (qr) => {
// NOTE: This event will not be fired if a session is specified.
console.log('QR RECEIVED', qr);
});
client.on('code', (code) => {
console.log('Pairing code:',code);
});
client.on('authenticated', () => {
console.log('AUTHENTICATED');
});
client.on('auth_failure', msg => {
// Fired if session restore was unsuccessful
console.error('AUTHENTICATION FAILURE', msg);
});
client.on('ready', async () => {
console.log('READY');
const debugWWebVersion = await client.getWWebVersion();
console.log(`WWebVersion = ${debugWWebVersion}`);
client.pupPage.on('pageerror', function(err) {
console.log('Page error: ' + err.toString());
});
client.pupPage.on('error', function(err) {
console.log('Page error: ' + err.toString());
});
});
client.on('message', async msg => {
console.log('MESSAGE RECEIVED', msg);
if (msg.body === '!ping reply') {
// Send a new message as a reply to the current one
msg.reply('pong');
} else if (msg.body === '!ping') {
// Send a new message to the same chat
client.sendMessage(msg.from, 'pong');
} else if (msg.body.startsWith('!sendto ')) {
// Direct send a new message to specific id
let number = msg.body.split(' ')[1];
let messageIndex = msg.body.indexOf(number) + number.length;
let message = msg.body.slice(messageIndex, msg.body.length);
number = number.includes('@c.us') ? number : `${number}@c.us`;
let chat = await msg.getChat();
chat.sendSeen();
client.sendMessage(number, message);
} else if (msg.body.startsWith('!subject ')) {
// Change the group subject
let chat = await msg.getChat();
if (chat.isGroup) {
let newSubject = msg.body.slice(9);
chat.setSubject(newSubject);
} else {
msg.reply('This command can only be used in a group!');
}
} else if (msg.body.startsWith('!echo ')) {
// Replies with the same message
msg.reply(msg.body.slice(6));
} else if (msg.body.startsWith('!preview ')) {
const text = msg.body.slice(9);
msg.reply(text, null, { linkPreview: true });
} else if (msg.body.startsWith('!desc ')) {
// Change the group description
let chat = await msg.getChat();
if (chat.isGroup) {
let newDescription = msg.body.slice(6);
chat.setDescription(newDescription);
} else {
msg.reply('This command can only be used in a group!');
}
} else if (msg.body === '!leave') {
// Leave the group
let chat = await msg.getChat();
if (chat.isGroup) {
chat.leave();
} else {
msg.reply('This command can only be used in a group!');
}
} else if (msg.body.startsWith('!join ')) {
const inviteCode = msg.body.split(' ')[1];
try {
await client.acceptInvite(inviteCode);
msg.reply('Joined the group!');
} catch (e) {
msg.reply('That invite code seems to be invalid.');
}
} else if (msg.body.startsWith('!addmembers')) {
const group = await msg.getChat();
const result = await group.addParticipants(['number1@c.us', 'number2@c.us', 'number3@c.us']);
/**
* The example of the {@link result} output:
*
* {
* 'number1@c.us': {
* code: 200,
* message: 'The participant was added successfully',
* isInviteV4Sent: false
* },
* 'number2@c.us': {
* code: 403,
* message: 'The participant can be added by sending private invitation only',
* isInviteV4Sent: true
* },
* 'number3@c.us': {
* code: 404,
* message: 'The phone number is not registered on WhatsApp',
* isInviteV4Sent: false
* }
* }
*
* For more usage examples:
* @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example1
*/
console.log(result);
} else if (msg.body === '!creategroup') {
const partitipantsToAdd = ['number1@c.us', 'number2@c.us', 'number3@c.us'];
const result = await client.createGroup('Group Title', partitipantsToAdd);
/**
* The example of the {@link result} output:
* {
* title: 'Group Title',
* gid: {
* server: 'g.us',
* user: '1111111111',
* _serialized: '1111111111@g.us'
* },
* participants: {
* 'botNumber@c.us': {
* statusCode: 200,
* message: 'The participant was added successfully',
* isGroupCreator: true,
* isInviteV4Sent: false
* },
* 'number1@c.us': {
* statusCode: 200,
* message: 'The participant was added successfully',
* isGroupCreator: false,
* isInviteV4Sent: false
* },
* 'number2@c.us': {
* statusCode: 403,
* message: 'The participant can be added by sending private invitation only',
* isGroupCreator: false,
* isInviteV4Sent: true
* },
* 'number3@c.us': {
* statusCode: 404,
* message: 'The phone number is not registered on WhatsApp',
* isGroupCreator: false,
* isInviteV4Sent: false
* }
* }
* }
*
* For more usage examples:
* @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example2
*/
console.log(result);
} else if (msg.body === '!groupinfo') {
let chat = await msg.getChat();
if (chat.isGroup) {
msg.reply(`
*Group Details*
Name: ${chat.name}
Description: ${chat.description}
Created At: ${chat.createdAt.toString()}
Created By: ${chat.owner.user}
Participant count: ${chat.participants.length}
`);
} else {
msg.reply('This command can only be used in a group!');
}
} else if (msg.body === '!chats') {
const chats = await client.getChats();
client.sendMessage(msg.from, `The bot has ${chats.length} chats open.`);
} else if (msg.body === '!info') {
let info = client.info;
client.sendMessage(msg.from, `
*Connection info*
User name: ${info.pushname}
My number: ${info.wid.user}
Platform: ${info.platform}
`);
} else if (msg.body === '!mediainfo' && msg.hasMedia) {
const attachmentData = await msg.downloadMedia();
msg.reply(`
*Media info*
MimeType: ${attachmentData.mimetype}
Filename: ${attachmentData.filename}
Data (length): ${attachmentData.data.length}
`);
} else if (msg.body === '!quoteinfo' && msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
quotedMsg.reply(`
ID: ${quotedMsg.id._serialized}
Type: ${quotedMsg.type}
Author: ${quotedMsg.author || quotedMsg.from}
Timestamp: ${quotedMsg.timestamp}
Has Media? ${quotedMsg.hasMedia}
`);
} else if (msg.body === '!resendmedia' && msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.hasMedia) {
const attachmentData = await quotedMsg.downloadMedia();
client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
}
if (quotedMsg.hasMedia && quotedMsg.type === 'audio') {
const audio = await quotedMsg.downloadMedia();
await client.sendMessage(msg.from, audio, { sendAudioAsVoice: true });
}
} else if (msg.body === '!isviewonce' && msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.hasMedia) {
const media = await quotedMsg.downloadMedia();
await client.sendMessage(msg.from, media, { isViewOnce: true });
}
} else if (msg.body === '!location') {
// only latitude and longitude
await msg.reply(new Location(37.422, -122.084));
// location with name only
await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex' }));
// location with address only
await msg.reply(new Location(37.422, -122.084, { address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA' }));
// location with name, address and url
await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex', address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA', url: 'https://google.com' }));
} else if (msg.location) {
msg.reply(msg.location);
} else if (msg.body.startsWith('!status ')) {
const newStatus = msg.body.split(' ')[1];
await client.setStatus(newStatus);
msg.reply(`Status was updated to *${newStatus}*`);
} else if (msg.body === '!mentionUsers') {
const chat = await msg.getChat();
const userNumber = 'XXXXXXXXXX';
/**
* To mention one user you can pass user's ID to 'mentions' property as is,
* without wrapping it in Array, and a user's phone number to the message body:
*/
await chat.sendMessage(`Hi @${userNumber}`, {
mentions: userNumber + '@c.us'
});
// To mention a list of users:
await chat.sendMessage(`Hi @${userNumber}, @${userNumber}`, {
mentions: [userNumber + '@c.us', userNumber + '@c.us']
});
} else if (msg.body === '!mentionGroups') {
const chat = await msg.getChat();
const groupId = 'YYYYYYYYYY@g.us';
/**
* Sends clickable group mentions, the same as user mentions.
* When the mentions are clicked, it opens a chat with the mentioned group.
* The 'groupMentions.subject' can be custom
*
* @note The user that does not participate in the mentioned group,
* will not be able to click on that mentioned group, the same if the group does not exist
*
* To mention one group:
*/
await chat.sendMessage(`Check the last message here: @${groupId}`, {
groupMentions: { subject: 'GroupSubject', id: groupId }
});
// To mention a list of groups:
await chat.sendMessage(`Check the last message in these groups: @${groupId}, @${groupId}`, {
groupMentions: [
{ subject: 'FirstGroup', id: groupId },
{ subject: 'SecondGroup', id: groupId }
]
});
} else if (msg.body === '!getGroupMentions') {
// To get group mentions from a message:
const groupId = 'ZZZZZZZZZZ@g.us';
const msg = await client.sendMessage('chatId', `Check the last message here: @${groupId}`, {
groupMentions: { subject: 'GroupSubject', id: groupId }
});
/** {@link groupMentions} is an array of `GroupChat` */
const groupMentions = await msg.getGroupMentions();
console.log(groupMentions);
} else if (msg.body === '!delete') {
if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.fromMe) {
quotedMsg.delete(true);
} else {
msg.reply('I can only delete my own messages');
}
}
} else if (msg.body === '!pin') {
const chat = await msg.getChat();
await chat.pin();
} else if (msg.body === '!archive') {
const chat = await msg.getChat();
await chat.archive();
} else if (msg.body === '!mute') {
const chat = await msg.getChat();
// mute the chat for 20 seconds
const unmuteDate = new Date();
unmuteDate.setSeconds(unmuteDate.getSeconds() + 20);
await chat.mute(unmuteDate);
} else if (msg.body === '!typing') {
const chat = await msg.getChat();
// simulates typing in the chat
chat.sendStateTyping();
} else if (msg.body === '!recording') {
const chat = await msg.getChat();
// simulates recording audio in the chat
chat.sendStateRecording();
} else if (msg.body === '!clearstate') {
const chat = await msg.getChat();
// stops typing or recording in the chat
chat.clearState();
} else if (msg.body === '!jumpto') {
if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
client.interface.openChatWindowAt(quotedMsg.id._serialized);
}
} else if (msg.body === '!buttons') {
let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer');
client.sendMessage(msg.from, button);
} else if (msg.body === '!list') {
let sections = [
{ title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }
];
let list = new List('List body', 'btnText', sections, 'Title', 'footer');
client.sendMessage(msg.from, list);
} else if (msg.body === '!reaction') {
await msg.react('👍');
} else if (msg.body === '!sendpoll') {
/** By default the poll is created as a single choice poll: */
await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer']));
/** If you want to provide a multiple choice poll, add allowMultipleAnswers as true: */
await msg.reply(new Poll('Cats or Dogs?', ['Cats', 'Dogs'], { allowMultipleAnswers: true }));
/**
* You can provide a custom message secret, it can be used as a poll ID:
* @note It has to be a unique vector with a length of 32
*/
await msg.reply(
new Poll('Cats or Dogs?', ['Cats', 'Dogs'], {
messageSecret: [
1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
})
);
} else if (msg.body === '!vote') {
if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.type === 'poll_creation') {
await quotedMsg.vote(msg.body.replace('!vote', ''));
} else {
msg.reply('Can only be used on poll messages');
}
}
} else if (msg.body === '!edit') {
if (msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.fromMe) {
await quotedMsg.edit(msg.body.replace('!edit', ''));
} else {
msg.reply('I can only edit my own messages');
}
}
} else if (msg.body === '!updatelabels') {
const chat = await msg.getChat();
await chat.changeLabels([0, 1]);
} else if (msg.body === '!addlabels') {
const chat = await msg.getChat();
let labels = (await chat.getLabels()).map((l) => l.id);
labels.push('0');
labels.push('1');
await chat.changeLabels(labels);
} else if (msg.body === '!removelabels') {
const chat = await msg.getChat();
await chat.changeLabels([]);
} else if (msg.body === '!approverequest') {
/**
* Presented an example for membership request approvals, the same examples are for the request rejections.
* To approve the membership request from a specific user:
*/
await client.approveGroupMembershipRequests(msg.from, { requesterIds: 'number@c.us' });
/** The same for execution on group object (no need to provide the group ID): */
const group = await msg.getChat();
await group.approveGroupMembershipRequests({ requesterIds: 'number@c.us' });
/** To approve several membership requests: */
const approval = await client.approveGroupMembershipRequests(msg.from, {
requesterIds: ['number1@c.us', 'number2@c.us']
});
/**
* The example of the {@link approval} output:
* [
* {
* requesterId: 'number1@c.us',
* message: 'Rejected successfully'
* },
* {
* requesterId: 'number2@c.us',
* error: 404,
* message: 'ParticipantRequestNotFoundError'
* }
* ]
*
*/
console.log(approval);
/** To approve all the existing membership requests (simply don't provide any user IDs): */
await client.approveGroupMembershipRequests(msg.from);
/** To change the sleep value to 300 ms: */
await client.approveGroupMembershipRequests(msg.from, {
requesterIds: ['number1@c.us', 'number2@c.us'],
sleep: 300
});
/** To change the sleep value to random value between 100 and 300 ms: */
await client.approveGroupMembershipRequests(msg.from, {
requesterIds: ['number1@c.us', 'number2@c.us'],
sleep: [100, 300]
});
/** To explicitly disable the sleep: */
await client.approveGroupMembershipRequests(msg.from, {
requesterIds: ['number1@c.us', 'number2@c.us'],
sleep: null
});
} else if (msg.body === '!pinmsg') {
/**
* Pins a message in a chat, a method takes a number in seconds for the message to be pinned.
* WhatsApp default values for duration to pass to the method are:
* 1. 86400 for 24 hours
* 2. 604800 for 7 days
* 3. 2592000 for 30 days
* You can pass your own value:
*/
const result = await msg.pin(60); // Will pin a message for 1 minute
console.log(result); // True if the operation completed successfully, false otherwise
} else if (msg.body === '!howManyConnections') {
/**
* Get user device count by ID
* Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
* So for a non-enterprise user with one WaWeb connection it should return "2"
*/
let deviceCount = await client.getContactDeviceCount(msg.from);
await msg.reply(`You have *${deviceCount}* devices connected`);
} else if (msg.body === '!syncHistory') {
const isSynced = await client.syncHistory(msg.from);
// Or through the Chat object:
// const chat = await client.getChatById(msg.from);
// const isSynced = await chat.syncHistory();
await msg.reply(isSynced ? 'Historical chat is syncing..' : 'There is no historical chat to sync.');
} else if (msg.body === '!statuses') {
const statuses = await client.getBroadcasts();
console.log(statuses);
const chat = await statuses[0]?.getChat(); // Get user chat of a first status
console.log(chat);
} else if (msg.body === '!sendMediaHD' && msg.hasQuotedMsg) {
const quotedMsg = await msg.getQuotedMessage();
if (quotedMsg.hasMedia) {
const media = await quotedMsg.downloadMedia();
await client.sendMessage(msg.from, media, { sendMediaAsHd: true });
}
} else if (msg.body === '!parseVCard') {
const vCard =
'BEGIN:VCARD\n' +
'VERSION:3.0\n' +
'FN:John Doe\n' +
'ORG:Microsoft;\n' +
'EMAIL;type=INTERNET:john.doe@gmail.com\n' +
'URL:www.johndoe.com\n' +
'TEL;type=CELL;type=VOICE;waid=18006427676:+1 (800) 642 7676\n' +
'END:VCARD';
const vCardExtended =
'BEGIN:VCARD\n' +
'VERSION:3.0\n' +
'FN:John Doe\n' +
'ORG:Microsoft;\n' +
'item1.TEL:+1 (800) 642 7676\n' +
'item1.X-ABLabel:USA Customer Service\n' +
'item2.TEL:+55 11 4706 0900\n' +
'item2.X-ABLabel:Brazil Customer Service\n' +
'PHOTO;BASE64:here you can paste a binary data of a contact photo in Base64 encoding\n' +
'END:VCARD';
const userId = 'XXXXXXXXXX@c.us';
await client.sendMessage(userId, vCard);
await client.sendMessage(userId, vCardExtended);
} else if (msg.body === '!changeSync') {
// NOTE: this action will take effect after you restart the client.
const backgroundSync = await client.setBackgroundSync(true);
console.log(backgroundSync);
}
});
client.on('message_create', async (msg) => {
// Fired on all message creations, including your own
if (msg.fromMe) {
// do stuff here
}
// Unpins a message
if (msg.fromMe && msg.body.startsWith('!unpin')) {
const pinnedMsg = await msg.getQuotedMessage();
if (pinnedMsg) {
// Will unpin a message
const result = await pinnedMsg.unpin();
console.log(result); // True if the operation completed successfully, false otherwise
}
}
});
client.on('message_ciphertext', (msg) => {
// Receiving new incoming messages that have been encrypted
// msg.type === 'ciphertext'
msg.body = 'Waiting for this message. Check your phone.';
// do stuff here
});
client.on('message_revoke_everyone', async (after, before) => {
// Fired whenever a message is deleted by anyone (including you)
console.log(after); // message after it was deleted.
if (before) {
console.log(before); // message before it was deleted.
}
});
client.on('message_revoke_me', async (msg) => {
// Fired whenever a message is only deleted in your own view.
console.log(msg.body); // message before it was deleted.
});
client.on('message_ack', (msg, ack) => {
/*
== ACK VALUES ==
ACK_ERROR: -1
ACK_PENDING: 0
ACK_SERVER: 1
ACK_DEVICE: 2
ACK_READ: 3
ACK_PLAYED: 4
*/
if (ack == 3) {
// The message was read
}
});
client.on('group_join', (notification) => {
// User has joined or been added to the group.
console.log('join', notification);
notification.reply('User joined.');
});
client.on('group_leave', (notification) => {
// User has left or been kicked from the group.
console.log('leave', notification);
notification.reply('User left.');
});
client.on('group_update', (notification) => {
// Group picture, subject or description has been updated.
console.log('update', notification);
});
client.on('change_state', state => {
console.log('CHANGE STATE', state);
});
// Change to false if you don't want to reject incoming calls
let rejectCalls = true;
client.on('call', async (call) => {
console.log('Call received, rejecting. GOTO Line 261 to disable', call);
if (rejectCalls) await call.reject();
await client.sendMessage(call.from, `[${call.fromMe ? 'Outgoing' : 'Incoming'}] Phone call from ${call.from}, type ${call.isGroup ? 'group' : ''} ${call.isVideo ? 'video' : 'audio'} call. ${rejectCalls ? 'This call was automatically rejected by the script.' : ''}`);
});
client.on('disconnected', (reason) => {
console.log('Client was logged out', reason);
});
client.on('contact_changed', async (message, oldId, newId, isContact) => {
/** The time the event occurred. */
const eventTime = (new Date(message.timestamp * 1000)).toLocaleString();
console.log(
`The contact ${oldId.slice(0, -5)}` +
`${!isContact ? ' that participates in group ' +
`${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` +
`changed their phone number\nat ${eventTime}.\n` +
`Their new phone number is ${newId.slice(0, -5)}.\n`);
/**
* Information about the @param {message}:
*
* 1. If a notification was emitted due to a group participant changing their phone number:
* @param {message.author} is a participant's id before the change.
* @param {message.recipients[0]} is a participant's id after the change (a new one).
*
* 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
* @param {message.to} is a group chat id the event was emitted in.
* @param {message.from} is a current user's id that got an notification message in the group.
* Also the @param {message.fromMe} is TRUE.
*
* 1.2 Otherwise:
* @param {message.from} is a group chat id the event was emitted in.
* @param {message.to} is @type {undefined}.
* Also @param {message.fromMe} is FALSE.
*
* 2. If a notification was emitted due to a contact changing their phone number:
* @param {message.templateParams} is an array of two user's ids:
* the old (before the change) and a new one, stored in alphabetical order.
* @param {message.from} is a current user's id that has a chat with a user,
* whos phone number was changed.
* @param {message.to} is a user's id (after the change), the current user has a chat with.
*/
});
client.on('group_admin_changed', (notification) => {
if (notification.type === 'promote') {
/**
* Emitted when a current user is promoted to an admin.
* {@link notification.author} is a user who performs the action of promoting/demoting the current user.
*/
console.log(`You were promoted by ${notification.author}`);
} else if (notification.type === 'demote')
/** Emitted when a current user is demoted to a regular user. */
console.log(`You were demoted by ${notification.author}`);
});
client.on('group_membership_request', async (notification) => {
/**
* The example of the {@link notification} output:
* {
* id: {
* fromMe: false,
* remote: 'groupId@g.us',
* id: '123123123132132132',
* participant: 'number@c.us',
* _serialized: 'false_groupId@g.us_123123123132132132_number@c.us'
* },
* body: '',
* type: 'created_membership_requests',
* timestamp: 1694456538,
* chatId: 'groupId@g.us',
* author: 'number@c.us',
* recipientIds: []
* }
*
*/
console.log(notification);
/** You can approve or reject the newly appeared membership request: */
await client.approveGroupMembershipRequestss(notification.chatId, notification.author);
await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
});
client.on('message_reaction', async (reaction) => {
console.log('REACTION RECEIVED', reaction);
});
client.on('vote_update', (vote) => {
/** The vote that was affected: */
console.log(vote);
});