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]