From 6ade08e814719bf099458879f7caa89193d6a6e5 Mon Sep 17 00:00:00 2001 From: sahalMoidu Date: Sat, 15 Feb 2020 03:23:28 +0530 Subject: [PATCH] Implemented changes mentioned in SimiPrambos PR#28 (#52) * Implemented changes mentioned in SimiPrambos PR#28 To reflect new code structure --- example.js | 53 ++++++++++++++++++++++++++------------------ src/Client.js | 35 ++++++++++++++++++++--------- src/util/Injected.js | 36 ++++++++++++++++-------------- 3 files changed, 74 insertions(+), 50 deletions(-) diff --git a/example.js b/example.js index 2aaff41..25a80f1 100644 --- a/example.js +++ b/example.js @@ -7,7 +7,7 @@ if (fs.existsSync(SESSION_FILE_PATH)) { sessionCfg = require(SESSION_FILE_PATH); } -const client = new Client({puppeteer: {headless: false}, session: sessionCfg}); +const client = new Client({ puppeteer: { headless: false }, session: sessionCfg }); // You can use an existing session and avoid scanning a QR code by adding a "session" object to the client options. // This object must include WABrowserId, WASecretBundle, WAToken1 and WAToken2. @@ -22,7 +22,7 @@ client.on('authenticated', (session) => { console.log('AUTHENTICATED', session); if (!fs.existsSync(SESSION_FILE_PATH)) { - fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function(err) { + fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function (err) { if (err) { console.error(err); } @@ -50,10 +50,19 @@ client.on('message', async msg => { // 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`; + + client.sendMessage(number, message); + } else if (msg.body.startsWith('!subject ')) { // Change the group subject let chat = await msg.getChat(); - if(chat.isGroup) { + if (chat.isGroup) { let newSubject = msg.body.slice(9); chat.setSubject(newSubject); } else { @@ -65,7 +74,7 @@ client.on('message', async msg => { } else if (msg.body.startsWith('!desc ')) { // Change the group description let chat = await msg.getChat(); - if(chat.isGroup) { + if (chat.isGroup) { let newDescription = msg.body.slice(6); chat.setDescription(newDescription); } else { @@ -74,22 +83,22 @@ client.on('message', async msg => { } else if (msg.body == '!leave') { // Leave the group let chat = await msg.getChat(); - if(chat.isGroup) { + if (chat.isGroup) { chat.leave(); } else { msg.reply('This command can only be used in a group!'); } - } else if(msg.body.startsWith('!join ')) { + } else if (msg.body.startsWith('!join ')) { const inviteCode = msg.body.split(' ')[1]; try { await client.acceptInvite(inviteCode); msg.reply('Joined the group!'); - } catch(e) { + } catch (e) { msg.reply('That invite code seems to be invalid.'); - } - } else if(msg.body == '!groupinfo') { + } + } else if (msg.body == '!groupinfo') { let chat = await msg.getChat(); - if(chat.isGroup) { + if (chat.isGroup) { msg.reply(` *Group Details* Name: ${chat.name} @@ -101,10 +110,10 @@ client.on('message', async msg => { } else { msg.reply('This command can only be used in a group!'); } - } else if(msg.body == '!chats') { + } 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') { + } else if (msg.body == '!info') { let info = client.info; client.sendMessage(msg.from, ` *Connection info* @@ -113,7 +122,7 @@ client.on('message', async msg => { Platform: ${info.platform} WhatsApp version: ${info.phone.wa_version} `); - } else if(msg.body == '!mediainfo' && msg.hasMedia) { + } else if (msg.body == '!mediainfo' && msg.hasMedia) { const attachmentData = await msg.downloadMedia(); msg.reply(` *Media info* @@ -121,7 +130,7 @@ client.on('message', async msg => { Filename: ${attachmentData.filename} Data (length): ${attachmentData.data.length} `); - } else if(msg.body == '!quoteinfo' && msg.hasQuotedMsg) { + } else if (msg.body == '!quoteinfo' && msg.hasQuotedMsg) { const quotedMsg = await msg.getQuotedMessage(); quotedMsg.reply(` @@ -131,18 +140,18 @@ client.on('message', async msg => { Timestamp: ${quotedMsg.timestamp} Has Media? ${quotedMsg.hasMedia} `); - } else if(msg.body == '!resendmedia' && msg.hasQuotedMsg) { + } else if (msg.body == '!resendmedia' && msg.hasQuotedMsg) { const quotedMsg = await msg.getQuotedMessage(); - if(quotedMsg.hasMedia) { + if (quotedMsg.hasMedia) { const attachmentData = await quotedMsg.downloadMedia(); - client.sendMessage(msg.from, attachmentData, {caption: 'Here\'s your requested media.'}); + client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' }); } - - } else if(msg.body == '!location') { + + } else if (msg.body == '!location') { msg.reply(new Location(37.422, -122.084, 'Googleplex\nGoogle Headquarters')); - } else if(msg.location) { + } else if (msg.location) { msg.reply(msg.location); - } else if(msg.body.startsWith('!status ')) { + } else if (msg.body.startsWith('!status ')) { const newStatus = msg.body.split(' ')[1]; await client.setStatus(newStatus); msg.reply(`Status was updated to *${newStatus}*`); @@ -157,7 +166,7 @@ client.on('message', async msg => { client.on('message_create', (msg) => { // Fired on all message creations, including your own - if(msg.fromMe) { + if (msg.fromMe) { // do stuff here } }); diff --git a/src/Client.js b/src/Client.js index 4cbe929..e139ae1 100644 --- a/src/Client.js +++ b/src/Client.js @@ -86,9 +86,9 @@ class Client extends EventEmitter { // Wait for QR Code const QR_CANVAS_SELECTOR = 'canvas'; await page.waitForSelector(QR_CANVAS_SELECTOR); - const qrImgData = await page.$eval(QR_CANVAS_SELECTOR, canvas => [].slice.call(canvas.getContext('2d').getImageData(0,0,264,264).data)); + const qrImgData = await page.$eval(QR_CANVAS_SELECTOR, canvas => [].slice.call(canvas.getContext('2d').getImageData(0, 0, 264, 264).data)); const qr = jsQR(qrImgData, 264, 264).data; - + /** * Emitted when the QR code is received * @event Client#qr @@ -162,7 +162,7 @@ class Client extends EventEmitter { if (msg.type === 'revoked') { const message = new Message(this, msg); let revoked_msg; - if(last_message && msg.id.id === last_message.id.id) { + if (last_message && msg.id.id === last_message.id.id) { revoked_msg = new Message(this, last_message); } @@ -175,11 +175,11 @@ class Client extends EventEmitter { */ this.emit(Events.MESSAGE_REVOKED_EVERYONE, message, revoked_msg); } - + }); await page.exposeFunction('onChangeMessageEvent', (msg) => { - + if (msg.type !== 'revoked') { last_message = msg; } @@ -254,32 +254,45 @@ class Client extends EventEmitter { * @param {object} options * @returns {Promise} Message that was just sent */ - async sendMessage(chatId, content, options={}) { + async sendMessage(chatId, content, options = {}) { let internalOptions = { caption: options.caption, quotedMessageId: options.quotedMessageId, - mentionedJidList: Array.isArray(options.mentions) ? options.mentions.map(contact => contact.id._serialized) : [] + mentionedJidList: Array.isArray(options.mentions) ? options.mentions.map(contact => contact.id._serialized) : [] }; - if(content instanceof MessageMedia) { + if (content instanceof MessageMedia) { internalOptions.attachment = content; content = ''; - } else if(options.media instanceof MessageMedia) { + } else if (options.media instanceof MessageMedia) { internalOptions.attachment = options.media; internalOptions.caption = content; - } else if(content instanceof Location) { + } else if (content instanceof Location) { internalOptions.location = content; content = ''; } const newMessage = await this.pupPage.evaluate(async (chatId, message, options) => { - const msg = await window.WWebJS.sendMessage(window.Store.Chat.get(chatId), message, options); + let chat = window.Store.Chat.get(chatId); + let msg; + if (!chat) { // The chat is not available in the previously chatted list + + //todo : Check if the number is a whatsapp enabled. Whatsapp web sends query exists via ws. + chat = window.Store.Chat.models[0]; //get the topmost chat object and assign the new chatId to it + let originalChatObjId = chat.id; + chat.id = typeof originalChatObjId === 'string' ? chatId : new window.Store.UserConstructor(chatId, { intentionallyUsePrivateConstructor: true }); + msg = await window.WWebJS.sendMessage(chat, message, options); + chat.id = originalChatObjId; //replace the chat with its original id + } + else + msg = await window.WWebJS.sendMessage(chat, message, options); return msg.serialize(); }, chatId, content, internalOptions); return new Message(this, newMessage); } + /** * Get all current chat instances * @returns {Promise>} diff --git a/src/util/Injected.js b/src/util/Injected.js index 706526d..07e303c 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -19,12 +19,14 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.MediaObject = window.mR.findModule('getOrCreateMediaObject')[0]; window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0]; window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0]; + window.Store.UserConstructor = window.mR.findModule((module) => (module.default && module.default.prototype && module.default.prototype.isServer && module.default.prototype.isUser) ? module.default : null)[0].default; + }; exports.LoadUtils = () => { window.WWebJS = {}; - window.WWebJS.sendMessage = async (chat, content, options={}) => { + window.WWebJS.sendMessage = async (chat, content, options = {}) => { let attOptions = {}; if (options.attachment) { attOptions = await window.WWebJS.processMediaData(options.attachment); @@ -34,27 +36,27 @@ exports.LoadUtils = () => { let quotedMsgOptions = {}; if (options.quotedMessageId) { let quotedMessage = window.Store.Msg.get(options.quotedMessageId); - if(quotedMessage.canReply()) { + if (quotedMessage.canReply()) { quotedMsgOptions = quotedMessage.msgContextInfo(chat); } delete options.quotedMessageId; } if (options.mentionedJidList) { - options.mentionedJidList = options.mentionedJidList.map(cId=> window.Store.Contact.get(cId).id); + options.mentionedJidList = options.mentionedJidList.map(cId => window.Store.Contact.get(cId).id); } let locationOptions = {}; if (options.location) { locationOptions = { type: 'location', - loc: options.location.description, - lat: options.location.latitude, + loc: options.location.description, + lat: options.location.latitude, lng: options.location.longitude }; delete options.location; } - + const newMsgId = new window.Store.MsgKey({ from: window.Store.Conn.me, to: chat.id, @@ -94,9 +96,9 @@ exports.LoadUtils = () => { isGif: mediaData.isGif }); - if(!(mediaData.mediaBlob instanceof window.Store.OpaqueData.default)) { + if (!(mediaData.mediaBlob instanceof window.Store.OpaqueData.default)) { mediaData.mediaBlob = await window.Store.OpaqueData.default.createFromData(mediaData.mediaBlob, mediaData.mediaBlob.type); - } + } mediaData.renderableUrl = mediaData.mediaBlob.url(); mediaObject.consolidate(mediaData.toJSON()); @@ -161,7 +163,7 @@ exports.LoadUtils = () => { return res; }; - + window.WWebJS.getContact = contactId => { const contact = window.Store.Contact.get(contactId); return window.WWebJS.getContactModel(contact); @@ -172,16 +174,16 @@ exports.LoadUtils = () => { return contacts.map(contact => window.WWebJS.getContactModel(contact)); }; - window.WWebJS.mediaInfoToFile = ({data, mimetype, filename}) => { + window.WWebJS.mediaInfoToFile = ({ data, mimetype, filename }) => { const binaryData = atob(data); - + const buffer = new ArrayBuffer(binaryData.length); const view = new Uint8Array(buffer); - for(let i=0; i < binaryData.length; i++) { + for (let i = 0; i < binaryData.length; i++) { view[i] = binaryData.charCodeAt(i); } - const blob = new Blob([buffer], {type: mimetype}); + const blob = new Blob([buffer], { type: mimetype }); return new File([blob], filename, { type: mimetype, lastModified: Date.now() @@ -189,7 +191,7 @@ exports.LoadUtils = () => { }; window.WWebJS.downloadBuffer = (url) => { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { let xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'arraybuffer'; @@ -216,13 +218,13 @@ exports.LoadUtils = () => { window.WWebJS.readBlobAsync = (blob) => { return new Promise((resolve, reject) => { let reader = new FileReader(); - + reader.onload = () => { resolve(reader.result); }; - + reader.onerror = reject; - + reader.readAsDataURL(blob); }); };