@@ -3281,7 +3281,7 @@ WAState diff --git a/docs/structures_Base.js.html b/docs/structures_Base.js.html index ecd596e423..9298998739 100644 --- a/docs/structures_Base.js.html +++ b/docs/structures_Base.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Base.js + whatsapp-web.js 1.19.5 » Source: structures/Base.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -60,7 +60,7 @@ Source: structures/Base.js diff --git a/docs/structures_BusinessContact.js.html b/docs/structures_BusinessContact.js.html index 9a97e69582..3b5feb76e9 100644 --- a/docs/structures_BusinessContact.js.html +++ b/docs/structures_BusinessContact.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/BusinessContact.js + whatsapp-web.js 1.19.5 » Source: structures/BusinessContact.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -59,7 +59,7 @@ Source: structures/BusinessContact.js diff --git a/docs/structures_Buttons.js.html b/docs/structures_Buttons.js.html index 2ea9add92f..7637c6ca05 100644 --- a/docs/structures_Buttons.js.html +++ b/docs/structures_Buttons.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Buttons.js + whatsapp-web.js 1.19.5 » Source: structures/Buttons.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -120,7 +120,7 @@ Source: structures/Buttons.js diff --git a/docs/structures_Call.js.html b/docs/structures_Call.js.html index 9d9593b7eb..09504d6944 100644 --- a/docs/structures_Call.js.html +++ b/docs/structures_Call.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Call.js + whatsapp-web.js 1.19.5 » Source: structures/Call.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -114,7 +114,7 @@ Source: structures/Call.js diff --git a/docs/structures_Chat.js.html b/docs/structures_Chat.js.html index 70eb9e2709..e470070c97 100644 --- a/docs/structures_Chat.js.html +++ b/docs/structures_Chat.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Chat.js + whatsapp-web.js 1.19.5 » Source: structures/Chat.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -299,7 +299,7 @@ Source: structures/Chat.js diff --git a/docs/structures_ClientInfo.js.html b/docs/structures_ClientInfo.js.html index 790e591e59..5c59f8448d 100644 --- a/docs/structures_ClientInfo.js.html +++ b/docs/structures_ClientInfo.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/ClientInfo.js + whatsapp-web.js 1.19.5 » Source: structures/ClientInfo.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -109,7 +109,7 @@ Source: structures/ClientInfo.js diff --git a/docs/structures_Contact.js.html b/docs/structures_Contact.js.html index 96f2323c03..9659220f02 100644 --- a/docs/structures_Contact.js.html +++ b/docs/structures_Contact.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Contact.js + whatsapp-web.js 1.19.5 » Source: structures/Contact.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -245,7 +245,7 @@ Source: structures/Contact.js diff --git a/docs/structures_GroupChat.js.html b/docs/structures_GroupChat.js.html index badebae3fe..03ef7ee6ac 100644 --- a/docs/structures_GroupChat.js.html +++ b/docs/structures_GroupChat.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/GroupChat.js + whatsapp-web.js 1.19.5 » Source: structures/GroupChat.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -294,7 +294,7 @@ Source: structures/GroupChat.js diff --git a/docs/structures_GroupNotification.js.html b/docs/structures_GroupNotification.js.html index 9ef074dd61..8885d690d3 100644 --- a/docs/structures_GroupNotification.js.html +++ b/docs/structures_GroupNotification.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/GroupNotification.js + whatsapp-web.js 1.19.5 » Source: structures/GroupNotification.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -143,7 +143,7 @@ Source: structures/GroupNotification.js diff --git a/docs/structures_Label.js.html b/docs/structures_Label.js.html index 01aed26829..0085387f54 100644 --- a/docs/structures_Label.js.html +++ b/docs/structures_Label.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Label.js + whatsapp-web.js 1.19.5 » Source: structures/Label.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -88,7 +88,7 @@ Source: structures/Label.js diff --git a/docs/structures_List.js.html b/docs/structures_List.js.html index e18dfd4503..4939fa15bd 100644 --- a/docs/structures_List.js.html +++ b/docs/structures_List.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/List.js + whatsapp-web.js 1.19.5 » Source: structures/List.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -118,7 +118,7 @@ Source: structures/List.js diff --git a/docs/structures_Location.js.html b/docs/structures_Location.js.html index 9707f6b2ae..2e263b664b 100644 --- a/docs/structures_Location.js.html +++ b/docs/structures_Location.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Location.js + whatsapp-web.js 1.19.5 » Source: structures/Location.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -71,7 +71,7 @@ Source: structures/Location.js diff --git a/docs/structures_Message.js.html b/docs/structures_Message.js.html index e5676a233c..42962cb01a 100644 --- a/docs/structures_Message.js.html +++ b/docs/structures_Message.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Message.js + whatsapp-web.js 1.19.5 » Source: structures/Message.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -434,7 +434,7 @@ Source: structures/Message.js } try { - const decryptedMedia = await window.Store.DownloadManager.downloadAndDecrypt({ + const decryptedMedia = await window.Store.DownloadManager.downloadAndMaybeDecrypt({ directPath: msg.directPath, encFilehash: msg.encFilehash, filehash: msg.filehash, @@ -573,7 +573,7 @@ Source: structures/Message.js diff --git a/docs/structures_MessageMedia.js.html b/docs/structures_MessageMedia.js.html index d52fd3b860..5648e76c25 100644 --- a/docs/structures_MessageMedia.js.html +++ b/docs/structures_MessageMedia.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/MessageMedia.js + whatsapp-web.js 1.19.5 » Source: structures/MessageMedia.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -150,7 +150,7 @@ Source: structures/MessageMedia.js diff --git a/docs/structures_Order.js.html b/docs/structures_Order.js.html index f2d76fb4c7..3da4befad2 100644 --- a/docs/structures_Order.js.html +++ b/docs/structures_Order.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Order.js + whatsapp-web.js 1.19.5 » Source: structures/Order.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -90,7 +90,7 @@ Source: structures/Order.js diff --git a/docs/structures_Payment.js.html b/docs/structures_Payment.js.html index 0d56ba40d6..2392de0997 100644 --- a/docs/structures_Payment.js.html +++ b/docs/structures_Payment.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Payment.js + whatsapp-web.js 1.19.5 » Source: structures/Payment.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -118,7 +118,7 @@ Source: structures/Payment.js diff --git a/docs/structures_PrivateChat.js.html b/docs/structures_PrivateChat.js.html index ff74a37c2c..46d5c2ee8e 100644 --- a/docs/structures_PrivateChat.js.html +++ b/docs/structures_PrivateChat.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/PrivateChat.js + whatsapp-web.js 1.19.5 » Source: structures/PrivateChat.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -51,7 +51,7 @@ Source: structures/PrivateChat.js diff --git a/docs/structures_PrivateContact.js.html b/docs/structures_PrivateContact.js.html index 7f14e299f2..0bf52689eb 100644 --- a/docs/structures_PrivateContact.js.html +++ b/docs/structures_PrivateContact.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/PrivateContact.js + whatsapp-web.js 1.19.5 » Source: structures/PrivateContact.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -51,7 +51,7 @@ Source: structures/PrivateContact.js diff --git a/docs/structures_Product.js.html b/docs/structures_Product.js.html index 882d704b1e..6a42dbf231 100644 --- a/docs/structures_Product.js.html +++ b/docs/structures_Product.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Product.js + whatsapp-web.js 1.19.5 » Source: structures/Product.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -106,7 +106,7 @@ Source: structures/Product.js diff --git a/docs/structures_ProductMetadata.js.html b/docs/structures_ProductMetadata.js.html index c4a071d26a..be6fa76ff5 100644 --- a/docs/structures_ProductMetadata.js.html +++ b/docs/structures_ProductMetadata.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/ProductMetadata.js + whatsapp-web.js 1.19.5 » Source: structures/ProductMetadata.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -63,7 +63,7 @@ Source: structures/ProductMetadata.js diff --git a/docs/structures_Reaction.js.html b/docs/structures_Reaction.js.html index 1dfda612b2..e4e4b009a0 100644 --- a/docs/structures_Reaction.js.html +++ b/docs/structures_Reaction.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: structures/Reaction.js + whatsapp-web.js 1.19.5 » Source: structures/Reaction.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -107,7 +107,7 @@ Source: structures/Reaction.js diff --git a/docs/util_Constants.js.html b/docs/util_Constants.js.html index 7b66690cfa..9389185d78 100644 --- a/docs/util_Constants.js.html +++ b/docs/util_Constants.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: util/Constants.js + whatsapp-web.js 1.19.5 » Source: util/Constants.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -200,7 +200,7 @@ Source: util/Constants.js diff --git a/docs/util_InterfaceController.js.html b/docs/util_InterfaceController.js.html index cc963e9331..17abf6d599 100644 --- a/docs/util_InterfaceController.js.html +++ b/docs/util_InterfaceController.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: util/InterfaceController.js + whatsapp-web.js 1.19.5 » Source: util/InterfaceController.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -164,7 +164,7 @@ Source: util/InterfaceController.js diff --git a/docs/util_Util.js.html b/docs/util_Util.js.html index 539ac5d694..ee2b4b4c50 100644 --- a/docs/util_Util.js.html +++ b/docs/util_Util.js.html @@ -2,9 +2,9 @@ - + - whatsapp-web.js 1.19.4 » Source: util/Util.js + whatsapp-web.js 1.19.5 » Source: util/Util.js @@ -15,7 +15,7 @@ - whatsapp-web.js 1.19.4 + whatsapp-web.js 1.19.5 @@ -225,7 +225,7 @@ Source: util/Util.js diff --git a/example.js b/example.js index 9e5eec59c8..f00b1303b6 100644 --- a/example.js +++ b/example.js @@ -1,4 +1,4 @@ -const { Client, Location, List, Buttons, LocalAuth} = require('./index'); +const { Client, Location, List, Buttons, LocalAuth } = require('./index'); const client = new Client({ authStrategy: new LocalAuth(), @@ -189,11 +189,11 @@ client.on('message', async msg => { client.interface.openChatWindowAt(quotedMsg.id._serialized); } } else if (msg.body === '!buttons') { - let button = new Buttons('Button body',[{body:'bt1'},{body:'bt2'},{body:'bt3'}],'title','footer'); + let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer'); client.sendMessage(msg.from, button); } else if (msg.body === '!list') { - let sections = [{title:'sectionTitle',rows:[{title:'ListItem1', description: 'desc'},{title:'ListItem2'}]}]; - let list = new List('List body','btnText',sections,'Title','footer'); + let sections = [{ title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }]; + let list = new List('List body', 'btnText', sections, 'Title', 'footer'); client.sendMessage(msg.from, list); } else if (msg.body === '!reaction') { msg.react('👍'); @@ -231,7 +231,7 @@ client.on('message_ack', (msg, ack) => { ACK_PLAYED: 4 */ - if(ack == 3) { + if (ack == 3) { // The message was read } }); @@ -254,7 +254,7 @@ client.on('group_update', (notification) => { }); client.on('change_state', state => { - console.log('CHANGE STATE', state ); + console.log('CHANGE STATE', state); }); // Change to false if you don't want to reject incoming calls @@ -270,3 +270,51 @@ client.on('disconnected', (reason) => { console.log('Client was logged out', reason); }); +client.on('contact_changed', async (message, oldId, newId, isContact) => { + /** The time the event occurred. */ + const eventTime = (new Date(message.timestamp * 1000)).toLocaleString(); + + console.log( + `The contact ${oldId.slice(0, -5)}` + + `${!isContact ? ' that participates in group ' + + `${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` + + `changed their phone number\nat ${eventTime}.\n` + + `Their new phone number is ${newId.slice(0, -5)}.\n`); + + /** + * Information about the {@name message}: + * + * 1. If a notification was emitted due to a group participant changing their phone number: + * {@name message.author} is a participant's id before the change. + * {@name message.recipients[0]} is a participant's id after the change (a new one). + * + * 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change: + * {@name message.to} is a group chat id the event was emitted in. + * {@name message.from} is a current user's id that got an notification message in the group. + * Also the {@name message.fromMe} is TRUE. + * + * 1.2 Otherwise: + * {@name message.from} is a group chat id the event was emitted in. + * {@name message.to} is @type {undefined}. + * Also {@name message.fromMe} is FALSE. + * + * 2. If a notification was emitted due to a contact changing their phone number: + * {@name message.templateParams} is an array of two user's ids: + * the old (before the change) and a new one, stored in alphabetical order. + * {@name message.from} is a current user's id that has a chat with a user, + * whos phone number was changed. + * {@name message.to} is a user's id (after the change), the current user has a chat with. + */ +}); + +client.on('group_admin_changed', (notification) => { + if (notification.type === 'promote') { + /** + * Emitted when a current user is promoted to an admin. + * {@link notification.author} is a user who performs the action of promoting/demoting the current user. + */ + console.log(`You were promoted by ${notification.author}`); + } else if (notification.type === 'demote') + /** Emitted when a current user is demoted to a regular user. */ + console.log(`You were demoted by ${notification.author}`); +}); diff --git a/index.d.ts b/index.d.ts index 6abef660cd..23ba35cd45 100644 --- a/index.d.ts +++ b/index.d.ts @@ -148,6 +148,12 @@ declare namespace WAWebJS { /** Unmutes the Chat */ unmuteChat(chatId: string): Promise + /** Sets the current user's profile picture */ + setProfilePicture(media: MessageMedia): Promise + + /** Deletes the current user's profile picture */ + deleteProfilePicture(): Promise + /** Generic event */ on(event: string, listener: (...args: any) => void): this @@ -192,12 +198,30 @@ declare namespace WAWebJS { notification: GroupNotification ) => void): this + /** Emitted when a current user is promoted to an admin or demoted to a regular user */ + on(event: 'group_admin_changed', listener: ( + /** GroupNotification with more information about the action */ + notification: GroupNotification + ) => void): this + /** Emitted when group settings are updated, such as subject, description or picture */ on(event: 'group_update', listener: ( /** GroupNotification with more information about the action */ notification: GroupNotification ) => void): this + /** Emitted when a contact or a group participant changed their phone number. */ + on(event: 'contact_changed', listener: ( + /** Message with more information about the event. */ + message: Message, + /** Old user's id. */ + oldId : String, + /** New user's id. */ + newId : String, + /** Indicates if a contact or a group participant changed their phone number. */ + isContact : Boolean + ) => void): this + /** Emitted when media has been uploaded for a message sent by the client */ on(event: 'media_uploaded', listener: ( /** The message with media that was uploaded */ @@ -217,6 +241,12 @@ declare namespace WAWebJS { /** The new ACK value */ ack: MessageAck ) => void): this + + /** Emitted when a chat unread count changes */ + on(event: 'unread_count', listener: ( + /** The chat that was affected */ + chat: Chat + ) => void): this /** Emitted when a new message is created, which may include the current user's own messages */ on(event: 'message_create', listener: ( @@ -247,6 +277,22 @@ declare namespace WAWebJS { reaction: Reaction ) => void): this + /** Emitted when a chat is removed */ + on(event: 'chat_removed', listener: ( + /** The chat that was removed */ + chat: Chat + ) => void): this + + /** Emitted when a chat is archived/unarchived */ + on(event: 'chat_archived', listener: ( + /** The chat that was archived/unarchived */ + chat: Chat, + /** State the chat is currently in */ + currState: boolean, + /** State the chat was previously in */ + prevState: boolean + ) => void): this + /** Emitted when loading screen is appearing */ on(event: 'loading_screen', listener: (percent: string, message: string) => void): this @@ -498,8 +544,10 @@ declare namespace WAWebJS { MESSAGE_REVOKED_ME = 'message_revoke_me', MESSAGE_ACK = 'message_ack', MEDIA_UPLOADED = 'media_uploaded', + CONTACT_CHANGED = 'contact_changed', GROUP_JOIN = 'group_join', GROUP_LEAVE = 'group_leave', + GROUP_ADMIN_CHANGED = 'group_admin_changed', GROUP_UPDATE = 'group_update', QR_RECEIVED = 'qr', LOADING_SCREEN = 'loading_screen', @@ -637,6 +685,7 @@ declare namespace WAWebJS { * broadcast: false, * fromMe: false, * hasQuotedMsg: false, + * hasReaction: false, * location: undefined, * mentionedIds: [] * } @@ -666,6 +715,8 @@ declare namespace WAWebJS { hasMedia: boolean, /** Indicates if the message was sent as a reply to another message */ hasQuotedMsg: boolean, + /** Indicates whether there are reactions to the message */ + hasReaction: boolean, /** Indicates the duration of the message in seconds */ duration: string, /** ID that represents the message */ @@ -767,6 +818,10 @@ declare namespace WAWebJS { * Gets the payment details associated with a given message */ getPayment: () => Promise, + /** + * Gets the reactions associated with the given message + */ + getReactions: () => Promise, } /** ID that represents a message */ @@ -1019,6 +1074,8 @@ declare namespace WAWebJS { timestamp: number, /** Amount of messages unread */ unreadCount: number, + /** Last message fo chat */ + lastMessage: Message, /** Archives this chat */ archive: () => Promise, @@ -1162,6 +1219,10 @@ declare namespace WAWebJS { revokeInvite: () => Promise; /** Makes the bot leave the group */ leave: () => Promise; + /** Sets the group's picture.*/ + setPicture: (media: MessageMedia) => Promise; + /** Deletes the group's picture */ + deletePicture: () => Promise; } /** @@ -1365,6 +1426,13 @@ declare namespace WAWebJS { senderId: string ack?: number } + + export type ReactionList = { + id: string, + aggregateEmoji: string, + hasReactionByMe: boolean, + senders: Array + } } export = WAWebJS diff --git a/package.json b/package.json index 0ad861d8fc..0aa50dcbca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { - "name": "whatsapp-web-electron.js-vgo", - "version": "1.19.4", - "description": "Library for interacting with the WhatsApp Web API through electron", + +"name": "whatsapp-web-electron.js-vgo", +"version": "1.19.5", + "description": "Library for interacting with the WhatsApp Web API ", "main": "./index.js", "typings": "./index.d.ts", "scripts": { diff --git a/src/Client.js b/src/Client.js index 017c89e91f..281d0c968a 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, Reaction } = require('./structures'); +const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction, Chat } = require('./structures'); const LegacySessionAuth = require('./authStrategies/LegacySessionAuth'); const NoAuth = require('./authStrategies/NoAuth'); @@ -47,6 +47,8 @@ const NoAuth = require('./authStrategies/NoAuth'); * @fires Client#group_update * @fires Client#disconnected * @fires Client#change_state + * @fires Client#contact_changed + * @fires Client#group_admin_changed */ class Client extends EventEmitter { constructor(puppeteerBrowser, browserWindow, options = {}) { @@ -317,6 +319,13 @@ class Client extends EventEmitter { * @param {GroupNotification} notification GroupNotification with more information about the action */ this.emit(Events.GROUP_LEAVE, notification); + } else if (msg.subtype === 'promote' || msg.subtype === 'demote') { + /** + * Emitted when a current user is promoted to an admin or demoted to a regular user. + * @event Client#group_admin_changed + * @param {GroupNotification} notification GroupNotification with more information about the action + */ + this.emit(Events.GROUP_ADMIN_CHANGED, notification); } else { /** * Emitted when group settings are updated, such as subject, description or picture. @@ -376,6 +385,36 @@ class Client extends EventEmitter { last_message = msg; } + /** + * The event notification that is received when one of + * the group participants changes thier phone number. + */ + const isParticipant = msg.type === 'gp2' && msg.subtype === 'modify'; + + /** + * The event notification that is received when one of + * the contacts changes thier phone number. + */ + const isContact = msg.type === 'notification_template' && msg.subtype === 'change_number'; + + if (isParticipant || isContact) { + /** {@link GroupNotification} object does not provide enough information about this event, so a {@link Message} object is used. */ + const message = new Message(this, msg); + + const newId = isParticipant ? msg.recipients[0] : msg.to; + const oldId = isParticipant ? msg.author : msg.templateParams.find(id => id !== newId); + + /** + * Emitted when a contact or a group participant changes their phone number. + * @event Client#contact_changed + * @param {Message} message Message with more information about the event. + * @param {String} oldId The user's id (an old one) who changed their phone number + * and who triggered the notification. + * @param {String} newId The user's new id after the change. + * @param {Boolean} isContact Indicates if a contact or a group participant changed their phone number. + */ + this.emit(Events.CONTACT_CHANGED, message, oldId, newId, isContact); + } }); await page.exposeFunction('onRemoveMessageEvent', (msg) => { @@ -407,6 +446,15 @@ class Client extends EventEmitter { }); + await page.exposeFunction('onChatUnreadCountEvent', async (data) =>{ + const chat = await this.getChatById(data.id); + + /** + * Emitted when the chat unread count changes + */ + this.emit(Events.UNREAD_COUNT, chat); + }); + await page.exposeFunction('onMessageMediaUploadedEvent', (msg) => { const message = new Message(this, msg); @@ -507,6 +555,26 @@ class Client extends EventEmitter { } }); + await page.exposeFunction('onRemoveChatEvent', (chat) => { + /** + * Emitted when a chat is removed + * @event Client#chat_removed + * @param {Chat} chat + */ + this.emit(Events.CHAT_REMOVED, new Chat(this, chat)); + }); + + await page.exposeFunction('onArchiveChatEvent', (chat, currState, prevState) => { + /** + * Emitted when a chat is archived/unarchived + * @event Client#chat_archived + * @param {Chat} chat + * @param {boolean} currState + * @param {boolean} prevState + */ + this.emit(Events.CHAT_ARCHIVED, new Chat(this, chat), currState, prevState); + }); + 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)); }); @@ -516,6 +584,8 @@ class Client extends EventEmitter { window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); }); window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); }); window.Store.Call.on('add', (call) => { window.onIncomingCall(call); }); + window.Store.Chat.on('remove', async (chat) => { window.onRemoveChatEvent(await window.WWebJS.getChatModel(chat)); }); + window.Store.Chat.on('change:archive', async (chat, currState, prevState) => { window.onArchiveChatEvent(await window.WWebJS.getChatModel(chat), currState, prevState); }); window.Store.Msg.on('add', (msg) => { if (msg.isNewMsg) { if(msg.type === 'ciphertext') { @@ -526,7 +596,8 @@ class Client extends EventEmitter { } } }); - + window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);}); + { const module = window.Store.createOrUpdateReactionsModule; const ogMethod = module.createOrUpdateReactions; @@ -1178,6 +1249,31 @@ class Client extends EventEmitter { return blockedContacts.map(contact => ContactFactory.create(this.client, contact)); } + + /** + * Sets the current user's profile picture. + * @param {MessageMedia} media + * @returns {Promise} Returns true if the picture was properly updated. + */ + async setProfilePicture(media) { + const success = await this.pupPage.evaluate((chatid, media) => { + return window.WWebJS.setPicture(chatid, media); + }, this.info.wid._serialized, media); + + return success; + } + + /** + * Deletes the current user's profile picture. + * @returns {Promise} Returns true if the picture was properly deleted. + */ + async deleteProfilePicture() { + const success = await this.pupPage.evaluate((chatid) => { + return window.WWebJS.deletePicture(chatid); + }, this.info.wid._serialized); + + return success; + } } module.exports = Client; diff --git a/src/structures/Chat.js b/src/structures/Chat.js index 241d297d92..0e0ae78d83 100644 --- a/src/structures/Chat.js +++ b/src/structures/Chat.js @@ -75,6 +75,12 @@ class Chat extends Base { */ this.muteExpiration = data.muteExpiration; + /** + * Last message fo chat + * @type {Message} + */ + this.lastMessage = data.lastMessage ? new Message(super.client, data.lastMessage) : undefined; + return super._patch(data); } diff --git a/src/structures/GroupChat.js b/src/structures/GroupChat.js index 0be6b62e10..51769a560b 100644 --- a/src/structures/GroupChat.js +++ b/src/structures/GroupChat.js @@ -213,6 +213,31 @@ class GroupChat extends Chat { return true; } + /** + * Deletes the group's picture. + * @returns {Promise} Returns true if the picture was properly deleted. This can return false if the user does not have the necessary permissions. + */ + async deletePicture() { + const success = await this.client.pupPage.evaluate((chatid) => { + return window.WWebJS.deletePicture(chatid); + }, this.id._serialized); + + return success; + } + + /** + * Sets the group's picture. + * @param {MessageMedia} media + * @returns {Promise} Returns true if the picture was properly updated. This can return false if the user does not have the necessary permissions. + */ + async setPicture(media) { + const success = await this.client.pupPage.evaluate((chatid, media) => { + return window.WWebJS.setPicture(chatid, media); + }, this.id._serialized, media); + + return success; + } + /** * Gets the invite code for a specific group * @returns {Promise} Group's invite code diff --git a/src/structures/Message.js b/src/structures/Message.js index 867bd888ee..87d716311d 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -5,7 +5,8 @@ const MessageMedia = require('./MessageMedia'); const Location = require('./Location'); const Order = require('./Order'); const Payment = require('./Payment'); -const { MessageTypes } = require('../util/Constants'); +const Reaction = require('./Reaction'); +const {MessageTypes} = require('../util/Constants'); /** * Represents a Message on WhatsApp @@ -134,6 +135,12 @@ class Message extends Base { */ this.hasQuotedMsg = data.quotedMsg ? true : false; + /** + * Indicates whether there are reactions to the message + * @type {boolean} + */ + this.hasReaction = data.hasReaction ? true : false; + /** * Indicates the duration of the message in seconds * @type {string} @@ -403,7 +410,7 @@ class Message extends Base { } try { - const decryptedMedia = await window.Store.DownloadManager.downloadAndDecrypt({ + const decryptedMedia = await window.Store.DownloadManager.downloadAndMaybeDecrypt({ directPath: msg.directPath, encFilehash: msg.encFilehash, filehash: msg.filehash, @@ -529,6 +536,44 @@ class Message extends Base { } return undefined; } + + + /** + * Reaction List + * @typedef {Object} ReactionList + * @property {string} id Original emoji + * @property {string} aggregateEmoji aggregate emoji + * @property {boolean} hasReactionByMe Flag who sent the reaction + * @property {Array} senders Reaction senders, to this message + */ + + /** + * Gets the reactions associated with the given message + * @return {Promise} + */ + async getReactions() { + if (!this.hasReaction) { + return undefined; + } + + const reactions = await this.client.pupPage.evaluate(async (msgId) => { + const msgReactions = await window.Store.Reactions.find(msgId); + if (!msgReactions || !msgReactions.reactions.length) return null; + return msgReactions.reactions.serialize(); + }, this.id._serialized); + + if (!reactions) { + return undefined; + } + + return reactions.map(reaction => { + reaction.senders = reaction.senders.map(sender => { + sender.timestamp = Math.round(sender.timestamp / 1000); + return new Reaction(this.client, sender); + }); + return reaction; + }); + } } module.exports = Message; diff --git a/src/util/Constants.js b/src/util/Constants.js index 4c22fa138d..97512c1b4d 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -36,15 +36,20 @@ exports.Events = { AUTHENTICATED: 'authenticated', AUTHENTICATION_FAILURE: 'auth_failure', READY: 'ready', + CHAT_REMOVED: 'chat_removed', + CHAT_ARCHIVED: 'chat_archived', MESSAGE_RECEIVED: 'message', MESSAGE_CREATE: 'message_create', MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone', MESSAGE_REVOKED_ME: 'message_revoke_me', MESSAGE_ACK: 'message_ack', + UNREAD_COUNT: 'unread_count', MESSAGE_REACTION: 'message_reaction', MEDIA_UPLOADED: 'media_uploaded', + CONTACT_CHANGED: 'contact_changed', GROUP_JOIN: 'group_join', GROUP_LEAVE: 'group_leave', + GROUP_ADMIN_CHANGED: 'group_admin_changed', GROUP_UPDATE: 'group_update', QR_RECEIVED: 'qr', LOADING_SCREEN: 'loading_screen', diff --git a/src/util/Injected.js b/src/util/Injected.js index 28cc069a66..cf812a9f98 100644 --- a/src/util/Injected.js +++ b/src/util/Injected.js @@ -17,7 +17,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.Invite = window.mR.findModule('resetGroupInviteCode')[0]; window.Store.InviteInfo = window.mR.findModule('queryGroupInvite')[0]; window.Store.Label = window.mR.findModule('LabelCollection')[0].LabelCollection; - window.Store.MediaPrep = window.mR.findModule('MediaPrep')[0]; + window.Store.MediaPrep = window.mR.findModule('prepRawMedia')[0]; window.Store.MediaObject = window.mR.findModule('getOrCreateMediaObject')[0]; window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0]; window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0]; @@ -41,7 +41,7 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0]; window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0]; window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0]; - window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[1]; + window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[0]; window.Store.JoinInviteV4 = window.mR.findModule('sendJoinGroupViaInviteV4')[0]; window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups; window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0]; @@ -62,7 +62,8 @@ exports.ExposeStore = (moduleRaidStr) => { window.Store.GroupUtils = { ...window.mR.findModule('createGroup')[0], ...window.mR.findModule('setGroupDescription')[0], - ...window.mR.findModule('sendExitGroup')[0] + ...window.mR.findModule('sendExitGroup')[0], + ...window.mR.findModule('sendSetPicture')[0] }; if (!window.Store.Chat._find) { @@ -271,6 +272,7 @@ exports.LoadUtils = () => { ...ephemeralFields, ...locationOptions, ...attOptions, + ...(attOptions.toJSON ? attOptions.toJSON() : {}), ...quotedMsgOptions, ...vcardOptions, ...buttonOptions, @@ -426,7 +428,15 @@ exports.LoadUtils = () => { await window.Store.GroupMetadata.update(chatWid); res.groupMetadata = chat.groupMetadata.serialize(); } - + + res.lastMessage = null; + if (res.msgs && res.msgs.length) { + const lastMessage = window.Store.Msg.get(chat.lastReceivedKey._serialized); + if (lastMessage) { + res.lastMessage = window.WWebJS.getMessageModel(lastMessage); + } + } + delete res.msgs; delete res.msgUnsyncedButtonReplyMsgs; delete res.unsyncedButtonReplies; @@ -625,4 +635,73 @@ exports.LoadUtils = () => { ]); await window.Store.Socket.deprecatedCastStanza(stanza); }; + + window.WWebJS.cropAndResizeImage = async (media, options = {}) => { + if (!media.mimetype.includes('image')) + throw new Error('Media is not an image'); + + if (options.mimetype && !options.mimetype.includes('image')) + delete options.mimetype; + + options = Object.assign({ size: 640, mimetype: media.mimetype, quality: .75, asDataUrl: false }, options); + + const img = await new Promise ((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = reject; + img.src = `data:${media.mimetype};base64,${media.data}`; + }); + + const sl = Math.min(img.width, img.height); + const sx = Math.floor((img.width - sl) / 2); + const sy = Math.floor((img.height - sl) / 2); + + const canvas = document.createElement('canvas'); + canvas.width = options.size; + canvas.height = options.size; + + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, sx, sy, sl, sl, 0, 0, options.size, options.size); + + const dataUrl = canvas.toDataURL(options.mimetype, options.quality); + + if (options.asDataUrl) + return dataUrl; + + return Object.assign(media, { + mimetype: options.mimeType, + data: dataUrl.replace(`data:${options.mimeType};base64,`, '') + }); + }; + + window.WWebJS.setPicture = async (chatid, media) => { + const thumbnail = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 96 }); + const profilePic = await window.WWebJS.cropAndResizeImage(media, { asDataUrl: true, mimetype: 'image/jpeg', size: 640 }); + + const chatWid = window.Store.WidFactory.createWid(chatid); + try { + const collection = window.Store.ProfilePicThumb.get(chatid); + if (!collection.canSet()) return; + + const res = await window.Store.GroupUtils.sendSetPicture(chatWid, thumbnail, profilePic); + return res ? res.status === 200 : false; + } catch (err) { + if(err.name === 'ServerStatusCodeError') return false; + throw err; + } + }; + + window.WWebJS.deletePicture = async (chatid) => { + const chatWid = window.Store.WidFactory.createWid(chatid); + try { + const collection = window.Store.ProfilePicThumb.get(chatid); + if (!collection.canDelete()) return; + + const res = await window.Store.GroupUtils.requestDeletePicture(chatWid); + return res ? res.status === 200 : false; + } catch (err) { + if(err.name === 'ServerStatusCodeError') return false; + throw err; + } + }; };