From 6a4fca0a778ee3fd9b2629a80bd771df9d8e77de Mon Sep 17 00:00:00 2001 From: Nowbie S <33182389+NowDev@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:24:43 -0300 Subject: [PATCH 1/7] updating forward documentation. (#1624) * updating forward documentation. * Update Message.js * Update index.d.ts * Update docs/Message.html Co-authored-by: Rajeh Taher --- index.d.ts | 2 +- src/structures/Message.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 7450208..8759f5c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -708,7 +708,7 @@ declare namespace WAWebJS { /** React to this message with an emoji*/ react: (reaction: string) => Promise, /** - * Forwards this message to another chat + * Forwards this message to another chat (that you chatted before, otherwise it will fail) */ forward: (chat: Chat | string) => Promise, /** Star this message */ diff --git a/src/structures/Message.js b/src/structures/Message.js index 94893cc..77a8e89 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -356,7 +356,7 @@ class Message extends Base { } /** - * Forwards this message to another chat + * Forwards this message to another chat (that you chatted before, otherwise it will fail) * * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded * @returns {Promise} From 56343497e9e5b737975afcbe25f1b9f2a1b412d2 Mon Sep 17 00:00:00 2001 From: Ruvian S <12111730+matricce@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:25:53 -0300 Subject: [PATCH 2/7] fix: `star` Error: Evaluation failed: TypeError: msg.chat.sendStarMsgs is not a function (#1598) Co-authored-by: Rajeh Taher --- docs/structures_Message.js.html | 4 ++-- src/structures/Message.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/structures_Message.js.html b/docs/structures_Message.js.html index 125c4f3..d937dd5 100644 --- a/docs/structures_Message.js.html +++ b/docs/structures_Message.js.html @@ -480,7 +480,7 @@ class Message extends Base { let msg = window.Store.Msg.get(msgId); if (msg.canStar()) { - return msg.chat.sendStarMsgs([msg], true); + return window.Store.Cmd.sendStarMsgs(msg.chat, [msg], false); } }, this.id._serialized); } @@ -493,7 +493,7 @@ class Message extends Base { let msg = window.Store.Msg.get(msgId); if (msg.canStar()) { - return msg.chat.sendStarMsgs([msg], false); + return window.Store.Cmd.sendUnstarMsgs(msg.chat, [msg], false); } }, this.id._serialized); } diff --git a/src/structures/Message.js b/src/structures/Message.js index 77a8e89..f66af99 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -449,7 +449,7 @@ class Message extends Base { let msg = window.Store.Msg.get(msgId); if (msg.canStar()) { - return msg.chat.sendStarMsgs([msg], true); + return window.Store.Cmd.sendStarMsgs(msg.chat, [msg], false); } }, this.id._serialized); } @@ -462,7 +462,7 @@ class Message extends Base { let msg = window.Store.Msg.get(msgId); if (msg.canStar()) { - return msg.chat.sendStarMsgs([msg], false); + return window.Store.Cmd.sendUnstarMsgs(msg.chat, [msg], false); } }, this.id._serialized); } From 6e047cb9bee7eeb43943e1b900b4401cd14c7c78 Mon Sep 17 00:00:00 2001 From: Yehuda Eisenberg <32451776+YehudaEi@users.noreply.github.com> Date: Tue, 9 Aug 2022 18:26:45 +0300 Subject: [PATCH 3/7] Update User agent (#1470) I encountered errors because of this (it says that the chrome version needs to be updated) Co-authored-by: Rajeh Taher --- src/util/Constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/Constants.js b/src/util/Constants.js index 83a030f..c3d9dfd 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -11,7 +11,7 @@ exports.DefaultOptions = { qrMaxRetries: 0, takeoverOnConflict: false, takeoverTimeoutMs: 0, - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36', + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36', ffmpegPath: 'ffmpeg', bypassCSP: false }; From c5c705a55399204d761f11f971e35eaeab78482e Mon Sep 17 00:00:00 2001 From: tonbotfy <106827778+tonbotfy@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:27:35 -0300 Subject: [PATCH 4/7] feat: [Updated] Loading screen listener with percent and message (#1563) * last update * eslint fix * headless fix * Update index.d.ts Co-authored-by: stefanfuchs * Update index.d.ts - Add 'LOADING_SCREEN' type to Enum Co-authored-by: stefanfuchs Co-authored-by: Rajeh Taher --- example.js | 4 ++++ index.d.ts | 4 ++++ src/Client.js | 46 +++++++++++++++++++++++++++++++++++++++++++ src/util/Constants.js | 1 + 4 files changed, 55 insertions(+) diff --git a/example.js b/example.js index 3c84bab..99dc7ea 100644 --- a/example.js +++ b/example.js @@ -7,6 +7,10 @@ const client = new Client({ client.initialize(); +client.on('loading_screen', (percent, message) => { + console.log('LOADING SCREEN', percent, message); +}); + client.on('qr', (qr) => { // NOTE: This event will not be fired if a session is specified. console.log('QR RECEIVED', qr); diff --git a/index.d.ts b/index.d.ts index 8759f5c..d4289d7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -241,6 +241,9 @@ declare namespace WAWebJS { message: Message ) => void): this + /** Emitted when loading screen is appearing */ + on(event: 'loading_screen', listener: (percent: string, message: string) => void): this + /** Emitted when the QR code is received */ on(event: 'qr', listener: ( /** qr code string @@ -463,6 +466,7 @@ declare namespace WAWebJS { GROUP_LEAVE = 'group_leave', GROUP_UPDATE = 'group_update', QR_RECEIVED = 'qr', + LOADING_SCREEN = 'loading_screen', DISCONNECTED = 'disconnected', STATE_CHANGED = 'change_state', BATTERY_CHANGED = 'change_battery', diff --git a/src/Client.js b/src/Client.js index d30390b..01d9527 100644 --- a/src/Client.js +++ b/src/Client.js @@ -115,6 +115,52 @@ class Client extends EventEmitter { referer: 'https://whatsapp.com/' }); + await page.evaluate(`function getElementByXpath(path) { + return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + }`); + + let lastPercent = null, + lastPercentMessage = null; + + await page.exposeFunction('loadingScreen', async (percent, message) => { + if (lastPercent !== percent || lastPercentMessage !== message) { + this.emit(Events.LOADING_SCREEN, percent, message); + lastPercent = percent; + lastPercentMessage = message; + } + }); + + await page.evaluate( + async function (selectors) { + var observer = new MutationObserver(function () { + let progressBar = window.getElementByXpath( + selectors.PROGRESS + ); + let progressMessage = window.getElementByXpath( + selectors.PROGRESS_MESSAGE + ); + + if (progressBar) { + window.loadingScreen( + progressBar.value, + progressMessage.innerText + ); + } + }); + + observer.observe(document, { + attributes: true, + childList: true, + characterData: true, + subtree: true, + }); + }, + { + PROGRESS: '//*[@id=\'app\']/div/div/div[2]/progress', + PROGRESS_MESSAGE: '//*[@id=\'app\']/div/div/div[3]', + } + ); + const INTRO_IMG_SELECTOR = '[data-testid="intro-md-beta-logo-dark"], [data-testid="intro-md-beta-logo-light"], [data-asset-intro-image-light="true"], [data-asset-intro-image-dark="true"]'; const INTRO_QRCODE_SELECTOR = 'div[data-ref] canvas'; diff --git a/src/util/Constants.js b/src/util/Constants.js index c3d9dfd..009ae5e 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -46,6 +46,7 @@ exports.Events = { GROUP_LEAVE: 'group_leave', GROUP_UPDATE: 'group_update', QR_RECEIVED: 'qr', + LOADING_SCREEN: 'loading_screen', DISCONNECTED: 'disconnected', STATE_CHANGED: 'change_state', BATTERY_CHANGED: 'change_battery', From bd553f75d35a882b4d4144e94687b8d32baf52ba Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Tue, 9 Aug 2022 18:29:59 +0300 Subject: [PATCH 5/7] feat: Adding file size by bytes to MessageMedia (#1273) * Update index.d.ts * Update Message.js * Update Message.js * Update MessageMedia.js * Update MessageMedia.js --- index.d.ts | 5 ++++- src/structures/Message.js | 5 +++-- src/structures/MessageMedia.js | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/index.d.ts b/index.d.ts index d4289d7..eabafb7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -809,13 +809,16 @@ declare namespace WAWebJS { data: string /** Document file name. Value can be null */ filename?: string | null + /** Document file size in bytes. Value can be null. */ + filesize?: number | null /** * @param {string} mimetype MIME type of the attachment * @param {string} data Base64-encoded data of the file * @param {?string} filename Document file name. Value can be null + * @param {?number} filesize Document file size in bytes. Value can be null. */ - constructor(mimetype: string, data: string, filename?: string | null) + constructor(mimetype: string, data: string, filename?: string | null, filesize?: number | null) /** Creates a MessageMedia instance from a local file path */ static fromFilePath: (filePath: string) => MessageMedia diff --git a/src/structures/Message.js b/src/structures/Message.js index f66af99..899241b 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -413,7 +413,8 @@ class Message extends Base { return { data, mimetype: msg.mimetype, - filename: msg.filename + filename: msg.filename, + filesize: msg.size }; } catch (e) { if(e.status && e.status === 404) return undefined; @@ -422,7 +423,7 @@ class Message extends Base { }, this.id._serialized); if (!result) return undefined; - return new MessageMedia(result.mimetype, result.data, result.filename); + return new MessageMedia(result.mimetype, result.data, result.filename, result.filesize); } /** diff --git a/src/structures/MessageMedia.js b/src/structures/MessageMedia.js index 450f78b..9be15be 100644 --- a/src/structures/MessageMedia.js +++ b/src/structures/MessageMedia.js @@ -10,10 +10,11 @@ const { URL } = require('url'); * Media attached to a message * @param {string} mimetype MIME type of the attachment * @param {string} data Base64-encoded data of the file - * @param {?string} filename Document file name + * @param {?string} filename Document file name. Value can be null + * @param {?number} filesize Document file size in bytes. Value can be null */ class MessageMedia { - constructor(mimetype, data, filename) { + constructor(mimetype, data, filename, filesize) { /** * MIME type of the attachment * @type {string} @@ -27,10 +28,16 @@ class MessageMedia { this.data = data; /** - * Name of the file (for documents) + * Document file name. Value can be null * @type {?string} */ this.filename = filename; + + /** + * Document file size in bytes. Value can be null + * @type {?number} + */ + this.filesize = filesize; } /** @@ -68,6 +75,7 @@ class MessageMedia { const reqOptions = Object.assign({ headers: { accept: 'image/* video/* text/* audio/*' } }, options); const response = await fetch(url, reqOptions); const mime = response.headers.get('Content-Type'); + const size = response.headers.get('Content-Length'); const contentDisposition = response.headers.get('Content-Disposition'); const name = contentDisposition ? contentDisposition.match(/((?<=filename=")(.*)(?="))/) : null; @@ -83,7 +91,7 @@ class MessageMedia { data = btoa(data); } - return { data, mime, name }; + return { data, mime, name, size }; } const res = options.client @@ -96,7 +104,7 @@ class MessageMedia { if (!mimetype) mimetype = res.mime; - return new MessageMedia(mimetype, res.data, filename); + return new MessageMedia(mimetype, res.data, filename, res.size || null); } } From fd368361df9627d48d3046fd712c7f6330d53476 Mon Sep 17 00:00:00 2001 From: Jeremy Andes <73316325+jeremyandes@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:30:56 -0300 Subject: [PATCH 6/7] Fix: Cannot read properties of undefined (reading 'id') (#1604) This change fix `react` evaluation: ``` Error: Evaluation failed: TypeError: Cannot read properties of undefined (reading 'id') at Object. (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:103021) at Generator.next () at t (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66483) at s (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66694) at https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66753 at Y (https://web.whatsapp.com/bootstrap_qr.f74b98c729dd38392a5f.js:37:128505) at new y (https://web.whatsapp.com/bootstrap_qr.f74b98c729dd38392a5f.js:37:121072) at Object. (https://web.whatsapp.com/vendor1~bootstrap_qr.5922e52928d864c0918c.js:2:66634) at Object.k (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:105511) at Object.t.sendReactionToMsg (https://web.whatsapp.com/bootstrap_main.44dc3fdf06d9bb8b053d.js:2:102647) at ExecutionContext._evaluateInternal (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:221:19) at runMicrotasks () at processTicksAndRejections (node:internal/process/task_queues:96:5) at async ExecutionContext.evaluate (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:110:16) at async Message.react (/app/node_modules/whatsapp-web.js/src/structures/Message.js:344:9) ``` Co-authored-by: Rajeh Taher --- src/structures/Message.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/structures/Message.js b/src/structures/Message.js index 899241b..7c1d3d0 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -342,6 +342,8 @@ class Message extends Base { */ async react(reaction){ await this.client.pupPage.evaluate(async (messageId, reaction) => { + if (!messageId) { return undefined; } + const msg = await window.Store.Msg.get(messageId); await window.Store.sendReactionToMsg(msg, reaction); }, this.id._serialized, reaction); From f2ec77f969c03b8d3f21d3bbb19f04320747b4ed Mon Sep 17 00:00:00 2001 From: Wictor Nogueira <57378387+wictornogueira@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:09:16 -0700 Subject: [PATCH 7/7] Feat: add message_reaction event (#1619) * Add 'message_reaction' event --- index.d.ts | 19 +++++++++++ src/Client.js | 39 ++++++++++++++++++++- src/structures/Reaction.js | 69 ++++++++++++++++++++++++++++++++++++++ src/structures/index.js | 1 + src/util/Constants.js | 1 + src/util/Injected.js | 1 + 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/structures/Reaction.js diff --git a/index.d.ts b/index.d.ts index eabafb7..710d404 100644 --- a/index.d.ts +++ b/index.d.ts @@ -241,6 +241,12 @@ declare namespace WAWebJS { message: Message ) => void): this + /** Emitted when a reaction is sent, received, updated or removed */ + on(event: 'message_reaction', listener: ( + /** The reaction object */ + reaction: Reaction + ) => void): this + /** Emitted when loading screen is appearing */ on(event: 'loading_screen', listener: (percent: string, message: string) => void): this @@ -1307,6 +1313,19 @@ declare namespace WAWebJS { constructor(body: string, buttons: Array<{ id?: string; body: string }>, title?: string | null, footer?: string | null) } + + /** Message type Reaction */ + export class Reaction { + id: MessageId + orphan: number + orphanReason?: string + timestamp: number + reaction: string + read: boolean + msgId: MessageId + senderId: string + ack?: number + } } export = WAWebJS diff --git a/src/Client.js b/src/Client.js index 01d9527..2368ef2 100644 --- a/src/Client.js +++ b/src/Client.js @@ -10,7 +10,7 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constan const { ExposeStore, LoadUtils } = require('./util/Injected'); const ChatFactory = require('./factories/ChatFactory'); const ContactFactory = require('./factories/ContactFactory'); -const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List} = require('./structures'); +const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures'); const LegacySessionAuth = require('./authStrategies/LegacySessionAuth'); const NoAuth = require('./authStrategies/NoAuth'); @@ -485,6 +485,27 @@ class Client extends EventEmitter { this.emit(Events.INCOMING_CALL, cll); }); + await page.exposeFunction('onReaction', (reactions) => { + for (const reaction of reactions) { + /** + * Emitted when a reaction is sent, received, updated or removed + * @event Client#message_reaction + * @param {object} reaction + * @param {object} reaction.id - Reaction id + * @param {number} reaction.orphan - Orphan + * @param {?string} reaction.orphanReason - Orphan reason + * @param {number} reaction.timestamp - Timestamp + * @param {string} reaction.reaction - Reaction + * @param {boolean} reaction.read - Read + * @param {object} reaction.msgId - Parent message id + * @param {string} reaction.senderId - Sender id + * @param {?number} reaction.ack - Ack + */ + + this.emit(Events.MESSAGE_REACTION, new Reaction(this, reaction)); + } + }); + await page.evaluate(() => { window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); }); window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); }); @@ -504,6 +525,22 @@ class Client extends EventEmitter { } } }); + + { + const module = window.Store.createOrUpdateReactionsModule; + const ogMethod = module.createOrUpdateReactions; + module.createOrUpdateReactions = ((...args) => { + window.onReaction(args[0].map(reaction => { + const msgKey = window.Store.MsgKey.fromString(reaction.msgKey); + const parentMsgKey = window.Store.MsgKey.fromString(reaction.parentMsgKey); + const timestamp = reaction.timestamp / 1000; + + return {...reaction, msgKey, parentMsgKey, timestamp }; + })); + + return ogMethod(...args); + }).bind(module); + } }); /** diff --git a/src/structures/Reaction.js b/src/structures/Reaction.js new file mode 100644 index 0000000..bb30881 --- /dev/null +++ b/src/structures/Reaction.js @@ -0,0 +1,69 @@ +'use strict'; + +const Base = require('./Base'); + +/** + * Represents a Reaction on WhatsApp + * @extends {Base} + */ +class Reaction extends Base { + constructor(client, data) { + super(client); + + if (data) this._patch(data); + } + + _patch(data) { + /** + * Reaction ID + * @type {object} + */ + this.id = data.msgKey; + /** + * Orphan + * @type {number} + */ + this.orphan = data.orphan; + /** + * Orphan reason + * @type {?string} + */ + this.orphanReason = data.orphanReason; + /** + * Unix timestamp for when the reaction was created + * @type {number} + */ + this.timestamp = data.timestamp; + /** + * Reaction + * @type {string} + */ + this.reaction = data.reactionText; + /** + * Read + * @type {boolean} + */ + this.read = data.read; + /** + * Message ID + * @type {object} + */ + this.msgId = data.parentMsgKey; + /** + * Sender ID + * @type {string} + */ + this.senderId = data.senderUserJid; + /** + * ACK + * @type {?number} + */ + this.ack = data.ack; + + + return super._patch(data); + } + +} + +module.exports = Reaction; \ No newline at end of file diff --git a/src/structures/index.js b/src/structures/index.js index fc5b212..ec449f0 100644 --- a/src/structures/index.js +++ b/src/structures/index.js @@ -18,4 +18,5 @@ module.exports = { Buttons: require('./Buttons'), List: require('./List'), Payment: require('./Payment'), + Reaction: require('./Reaction'), }; diff --git a/src/util/Constants.js b/src/util/Constants.js index 009ae5e..a85b8d8 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -41,6 +41,7 @@ exports.Events = { MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone', MESSAGE_REVOKED_ME: 'message_revoke_me', MESSAGE_ACK: 'message_ack', + MESSAGE_REACTION: 'message_reaction', MEDIA_UPLOADED: 'media_uploaded', GROUP_JOIN: 'group_join', GROUP_LEAVE: 'group_leave', diff --git a/src/util/Injected.js b/src/util/Injected.js index 7eea752..a5eeae3 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -50,6 +50,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0]; window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0]; window.Store.sendReactionToMsg = window.mR.findModule('sendReactionToMsg')[0].sendReactionToMsg; + window.Store.createOrUpdateReactionsModule = window.mR.findModule('createOrUpdateReactions')[0]; window.Store.StickerTools = { ...window.mR.findModule('toWebpSticker')[0], ...window.mR.findModule('addWebpMetadata')[0]