diff --git a/example.js b/example.js index 0ed0e00..6e46f26 100644 --- a/example.js +++ b/example.js @@ -81,6 +81,14 @@ client.on('message', async msg => { } 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 == '!mediainfo' && msg.hasMedia) { + const attachmentData = await msg.downloadMedia(); + msg.reply(` + *Media info* + MimeType: ${attachmentData.mimetype} + Filename: ${attachmentData.filename} + Data (length): ${attachmentData.data.length} + `); } }); diff --git a/src/Client.js b/src/Client.js index ebf317e..ea223a8 100644 --- a/src/Client.js +++ b/src/Client.js @@ -6,7 +6,7 @@ const moduleRaid = require('moduleraid/moduleraid'); const Util = require('./util/Util'); const { WhatsWebURL, UserAgent, DefaultOptions, Events, WAState } = require('./util/Constants'); -const { ExposeStore, LoadCustomSerializers } = require('./util/Injected'); +const { ExposeStore, LoadUtils } = require('./util/Injected'); const ChatFactory = require('./factories/ChatFactory'); const Chat = require('./structures/Chat'); const Message = require('./structures/Message'); @@ -96,8 +96,8 @@ class Client extends EventEmitter { // Check Store Injection await page.waitForFunction('window.Store != undefined'); - //Load custom serializers - await page.evaluate(LoadCustomSerializers); + //Load util functions (serializers, helper functions) + await page.evaluate(LoadUtils); // Register events await page.exposeFunction('onAddMessageEvent', msg => { diff --git a/src/structures/Message.js b/src/structures/Message.js index fb98efe..b6249ab 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -15,7 +15,8 @@ class Message extends Base { _patch(data) { this.id = data.id; - this.body = data.body; + this.hasMedia = data.clientUrl ? true : false; + this.body = this.hasMedia ? data.caption || '' : data.body || ''; this.type = data.type; this.timestamp = data.t; this.from = data.from; @@ -60,6 +61,26 @@ class Message extends Base { }, chatId, this.id._serialized, message); } + + async downloadMedia() { + if (!this.hasMedia) { + return undefined; + } + + return await this.client.pupPage.evaluate(async (msgId) => { + const msg = Store.Msg.get(msgId); + const buffer = await WWebJS.downloadBuffer(msg.clientUrl); + const decrypted = await Store.CryptoLib.decryptE2EMedia(msg.type, buffer, msg.mediaKey, msg.mimetype); + const data = await WWebJS.readBlobAsync(decrypted._blob); + + return { + data, + mimetype: msg.mimetype, + filename: msg.filename + } + + }, this.id._serialized); + } } module.exports = Message; \ No newline at end of file diff --git a/src/util/Injected.js b/src/util/Injected.js index 433d9bd..fc6eb77 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -8,12 +8,12 @@ exports.ExposeStore = (moduleRaidStr) => { window.mR = moduleRaid(); window.Store = window.mR.findModule("Chat")[1].default; window.Store.AppState = window.mR.findModule("STREAM")[0].default; - + window.Store.CryptoLib = window.mR.findModule("decryptE2EMedia")[0]; window.Store.genId = window.mR.findModule((module) => module.default && typeof module.default === 'function' && module.default.toString().match(/crypto/))[0].default; window.Store.SendMessage = window.mR.findModule("sendTextMsgToChat")[0].sendTextMsgToChat; } -exports.LoadCustomSerializers = () => { +exports.LoadUtils = () => { window.WWebJS = {}; window.WWebJS.getChatModel = chat => { @@ -37,6 +37,45 @@ exports.LoadCustomSerializers = () => { const chats = Store.Chat.models; return chats.map(chat => WWebJS.getChatModel(chat)); } + + window.WWebJS.downloadBuffer = (url) => { + return new Promise(function(resolve, reject) { + let xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.responseType = 'arraybuffer'; + xhr.onload = function () { + if (xhr.status == 200) { + resolve(xhr.response); + } else { + reject({ + status: this.status, + statusText: xhr.statusText + }); + } + }; + xhr.onerror = function () { + reject({ + status: this.status, + statusText: xhr.statusText + }); + }; + xhr.send(null); + }); + } + + window.WWebJS.readBlobAsync = (blob) => { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + + reader.onload = () => { + resolve(reader.result); + }; + + reader.onerror = reject; + + reader.readAsDataURL(blob); + }) + } } exports.MarkAllRead = () => {