Add group chat functions, slight refactoring

This commit is contained in:
Pedro Lopez
2019-02-20 02:42:19 -04:00
parent 594a65d5e8
commit 1b7376885d
12 changed files with 316 additions and 37 deletions

View File

@@ -1,4 +1,4 @@
const { Client } = require('./src')
const { Client } = require('./index')
const client = new Client({puppeteer: {headless: false}});
@@ -16,16 +16,60 @@ client.on('ready', () => {
console.log('READY');
});
client.on('message', (msg) => {
client.on('message', async msg => {
console.log('MESSAGE RECEIVED', msg);
if (msg.body == 'ping reply') {
if (msg.body == '!ping reply') {
// Send a new message as a reply to the current one
msg.reply('pong');
} else if (msg.body == 'ping') {
} else if (msg.body == '!ping') {
// Send a new message to the same chat
client.sendMessage(msg.from, 'pong');
} 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('!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 == '!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!');
}
}
});

13
index.js Normal file
View File

@@ -0,0 +1,13 @@
'use strict';
module.exports = {
Client: require('./src/Client'),
version: require('./package.json').version,
// Structures
Chat: require('./src/structures/Chat'),
PrivateChat: require('./src/structures/PrivateChat'),
GroupChat: require('./src/structures/GroupChat'),
Message: require('./src/structures/Message')
};

View File

@@ -2,7 +2,7 @@
"name": "whatsapp-web.js",
"version": "0.0.1",
"description": "Library for interacting with the WhatsApp Web API ",
"main": "./src/index.js",
"main": "./index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},

View File

@@ -2,11 +2,12 @@
const EventEmitter = require('events');
const puppeteer = require('puppeteer');
const Util = require('../util/Util');
const { WhatsWebURL, UserAgent, DefaultOptions, Events } = require('../util/Constants');
const { ExposeStore, LoadExtraProps } = require('../util/Injected');
const Chat = require('../models/Chat');
const Message = require('../models/Message')
const Util = require('./util/Util');
const { WhatsWebURL, UserAgent, DefaultOptions, Events } = require('./util/Constants');
const { ExposeStore, LoadExtraProps, LoadCustomSerializers } = require('./util/Injected');
const ChatFactory = require('./factories/ChatFactory');
const Chat = require('./structures/Chat');
const Message = require('./structures/Message')
/**
* Starting point for interacting with the WhatsApp Web API
@@ -22,6 +23,9 @@ class Client extends EventEmitter {
this.pupPage = null;
}
/**
* Sets up events and requirements, kicks off authentication request
*/
async initialize() {
const browser = await puppeteer.launch(this.options.puppeteer);
const page = await browser.newPage();
@@ -43,12 +47,14 @@ class Client extends EventEmitter {
// Check Store Injection
await page.waitForFunction('window.Store != undefined');
// Load extra serialized props
const models = [Chat, Message];
//Load extra serialized props
const models = [Message];
for (let model of models) {
await page.evaluate(LoadExtraProps, model.WAppModel, model.extraFields);
}
await page.evaluate(LoadCustomSerializers);
// Register events
await page.exposeFunction('onAddMessageEvent', msg => {
if (msg.id.fromMe || !msg.isNewMsg) return;
@@ -77,26 +83,39 @@ class Client extends EventEmitter {
await this.pupBrowser.close();
}
/**
* Send a message to a specific chatId
* @param {string} chatId
* @param {string} message
*/
async sendMessage(chatId, message) {
await this.pupPage.evaluate((chatId, message) => {
Store.Chat.get(chatId).sendMessage(message);
}, chatId, message)
}
/**
* Get all current chat instances
*/
async getChats() {
let chats = await this.pupPage.evaluate(() => {
return Store.Chat.serialize()
});
// let chats = await this.pupPage.evaluate(() => {
// return Store.Chat.serialize()
// });
return chats.map(chatData => new Chat(this, chatData));
// return chats.map(chatData => ChatFactory.create(this, chatData));
throw new Error('NOT IMPLEMENTED')
}
/**
* Get chat instance by ID
* @param {string} chatId
*/
async getChatById(chatId) {
let chat = await this.pupPage.evaluate(chatId => {
return Store.Chat.get(chatId).serialize();
return WWebJS.getChat(chatId);
}, chatId);
return new Chat(this, chat);
return ChatFactory.create(this, chat);
}

View File

@@ -0,0 +1,16 @@
'use strict';
const PrivateChat = require('../structures/PrivateChat');
const GroupChat = require('../structures/GroupChat');
class ChatFactory {
static create(client, data) {
if(data.isGroup) {
return new GroupChat(client, data);
}
return new PrivateChat(client, data);
}
}
module.exports = ChatFactory;

View File

@@ -1,10 +0,0 @@
'use strict';
module.exports = {
Client: require('./client/Client'),
version: require('../package.json').version,
// Models
Chat: require('./models/Chat'),
Message: require('./models/Message')
};

View File

@@ -1,7 +1,7 @@
'use strict';
/**
* Represents a data model
* Represents a WhatsApp data structure
*/
class Base {
constructor(client) {

View File

@@ -16,23 +16,18 @@ class Chat extends Base {
_patch(data) {
this.id = data.id;
this.name = data.formattedTitle;
this.isGroup = data.isGroup;
this.isReadOnly = data.isReadOnly;
this.name = data.name;
this.unreadCount = data.unreadCount;
this.timestamp = data.t;
return super._patch(data);
}
sendMessage(message) {
return this.client.sendMessage(this.id._serialized, message);
}
static get extraFields() {
return [
'formattedTitle',
'isGroup'
];
}
}
module.exports = Chat;

166
src/structures/GroupChat.js Normal file
View File

@@ -0,0 +1,166 @@
'use strict';
const Chat = require('./Chat');
/**
* Represents a Group Chat on WhatsApp
* @extends {Chat}
*/
class GroupChat extends Chat {
_patch(data) {
this.groupMetadata = data.groupMetadata;
return super._patch(data);
}
/**
* Gets the group owner
*/
get owner() {
return this.groupMetadata.owner;
}
/**
* Gets the date at which the group was created
*/
get createdAt() {
return new Date(this.groupMetadata.creation * 1000);
}
/**
* Gets the group description
*/
get description() {
return this.groupMetadata.desc;
}
/**
* Gets the group participants
*/
get participants() {
return this.groupMetadata.participants;
}
/**
* Adds a list of participants by ID to the group
* @param {Array[string]} participantIds
*/
async addParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
return Store.Wap.addParticipants(chatId, participantIds);
}, this.id._serialized, participantIds);
}
/**
* Removes a list of participants by ID to the group
* @param {Array[string]} participantIds
*/
async removeParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
return Store.Wap.removeParticipants(chatId, participantIds);
}, this.id._serialized, participantIds);
}
/**
* Promotes participants by IDs to admins
* @param {Array[string]} participantIds
*/
async promoteParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
return Store.Wap.promoteParticipants(chatId, participantIds);
}, this.id._serialized, participantIds);
}
/**
* Demotes participants by IDs to regular users
* @param {Array[string]} participantIds
*/
async demoteParticipants(participantIds) {
return await this.client.pupPage.evaluate((chatId, participantIds) => {
return Store.Wap.demoteParticipants(chatId, participantIds);
}, this.id._serialized, participantIds);
}
/**
* Updates the group subject
* @param {string} subject
*/
async setSubject(subject) {
let res = await this.client.pupPage.evaluate((chatId, subject) => {
return Store.Chat.get(chatId).setSubject(subject);
}, this.id._serialized, subject);
if(res.status == 200) {
this.name = subject;
}
}
/**
* Updates the group description
* @param {string} description
*/
async setDescription(description) {
let res = await this.client.pupPage.evaluate((chatId, description) => {
return Store.Chat.get(chatId).setGroupDesc(description);
}, this.id._serialized, description);
if (res.status == 200) {
this.groupMetadata.desc = description;
}
}
/**
* Gets the invite code for a specific group
*/
async getInviteCode() {
let res = await this.client.pupPage.evaluate(chatId => {
return Store.Wap.groupInviteCode(chatId);
}, this.id._serialized);
if (res.status == 200) {
return res.code;
}
throw new Error('Not authorized')
}
/**
* Invalidates the current group invite code and generates a new one
*/
async revokeInvite() {
return await this.client.pupPage.evaluate(chatId => {
return Store.Wap.revokeGroupInvite(chatId);
}, chatId);
}
/**
* Returns an object with information about the invite code's group
* @param {string} inviteCode
*/
static async getInviteInfo(inviteCode) {
return await this.client.pupPage.evaluate(inviteCode => {
return Store.Wap.groupInviteInfo(inviteCode);
}, inviteCode);
}
/**
* Joins a group with an invite code
* @param {string} inviteCode
*/
static async join(inviteCode) {
return await this.client.pupPage.evaluate(inviteCode => {
return Store.Wap.acceptGroupInvite(inviteCode);
}, inviteCode);
}
/**
* Makes the bot leave the group
*/
async leave() {
return await this.client.pupPage.evaluate(chatId => {
return Store.Wap.leaveGroup(chatId);
}, this.id._serialized);
}
}
module.exports = GroupChat;

View File

@@ -23,6 +23,8 @@ class Message extends Base {
this.author = data.author;
this.isForwarded = data.isForwarded;
this.broadcast = data.broadcast;
return super._patch(data);
}
/**

View File

@@ -0,0 +1,13 @@
'use strict';
const Chat = require('./Chat');
/**
* Represents a Private Chat on WhatsApp
* @extends {Chat}
*/
class PrivateChat extends Chat {
}
module.exports = PrivateChat;

View File

@@ -56,6 +56,27 @@ exports.LoadExtraProps = (model, props) => {
Store[model].models[0].__props = Store[model].models[0].__props.concat(props);
}
exports.LoadCustomSerializers = () => {
window.WWebJS = {};
window.WWebJS.getChatModel = chat => {
let res = chat.serialize();
res.isGroup = chat.isGroup;
res.formattedTitle = chat.formattedTitle;
if(chat.groupMetadata) {
res.groupMetadata = chat.groupMetadata.serialize();
}
return res;
}
window.WWebJS.getChat = chatId => {
const chat = Store.Chat.get(chatId);
return WWebJS.getChatModel(chat);
}
}
exports.MarkAllRead = () => {
let Chats = Store.Chat.models;