From eace0c1795bc484732d36c865c50d182f57e6ef1 Mon Sep 17 00:00:00 2001 From: Adhiraj Singh Date: Fri, 13 Nov 2020 16:07:08 +0530 Subject: [PATCH] Deprecated message-new & message-update + fix presence update for groups The chat-update event is fired now when a message is received/updated --- Example/example.ts | 7 +++- README.md | 10 ++--- src/WAConnection/3.Connect.ts | 2 +- src/WAConnection/4.Events.ts | 70 +++++++++++++++++++++-------------- src/WAConnection/Constants.ts | 2 - src/WAConnection/Utils.ts | 7 ++++ 6 files changed, 59 insertions(+), 39 deletions(-) diff --git a/Example/example.ts b/Example/example.ts index 15204e8..6356464 100644 --- a/Example/example.ts +++ b/Example/example.ts @@ -47,8 +47,11 @@ async function example() { const participant = json.participant ? ' (' + json.participant + ')' : '' // participant exists when the message is from a group console.log(`${json.to}${participant} acknlowledged message(s) ${json.ids} as ${json.type}`) }) - // set to false to NOT relay your own sent messages - conn.on('message-new', async (m) => { + conn.on('chat-update', async chat => { + // only do something when a new message is received; i.e. the unread count is updated + if (!chat.count) return + + const m = chat.messages.all()[0] // pull the new message from the update const messageStubType = WA_MESSAGE_STUB_TYPES[m.messageStubType] || 'MESSAGE' console.log('got notification of type: ' + messageStubType) diff --git a/README.md b/README.md index f4f9afd..b92abb6 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,8 @@ Also, these events are fired regardless of whether they are initiated by the Bai on (event: 'open', listener: (result: WAOpenResult) => void): this /** when the connection is opening */ on (event: 'connecting', listener: () => void): this +/** when the connection has been validated */ +on (event: 'connection-validated', listener: (user: WAUser) => void): this /** when the connection has closed */ on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this /** when the socket is closed */ @@ -198,12 +200,8 @@ on (event: 'user-presence-update', listener: (update: PresenceUpdate) => void): on (event: 'user-status-update', listener: (update: {jid: string, status?: string}) => void): this /** when a new chat is added */ on (event: 'chat-new', listener: (chat: WAChat) => void): this -/** when a chat is updated (archived, deleted, pinned) */ -on (event: 'chat-update', listener: (chat: Partial & { jid: string }) => void): this -/** when a new message is relayed */ -on (event: 'message-new', listener: (message: WAMessage) => void): this -/** when a message object itself is updated (receives its media info or is deleted) */ -on (event: 'message-update', listener: (message: WAMessage) => void): this +/** when a chat is updated (new message, updated message, deleted, pinned, etc) */ +on (event: 'chat-update', listener: (chat: WAChatUpdate) => void): this /** when a message's status is updated (deleted, delivered, read, sent etc.) */ on (event: 'message-status-update', listener: (message: WAMessageStatusUpdate) => void): this /** when participants are added to a group */ diff --git a/src/WAConnection/3.Connect.ts b/src/WAConnection/3.Connect.ts index 1805e7f..2cc4ae6 100644 --- a/src/WAConnection/3.Connect.ts +++ b/src/WAConnection/3.Connect.ts @@ -189,7 +189,7 @@ export class WAConnection extends Base { chat.jid = Utils.whatsappID (chat.jid) chat.t = +chat.t chat.count = +chat.count - chat.messages = new KeyedDB (Utils.waMessageKey, Utils.WA_MESSAGE_ID) + chat.messages = Utils.newMessagesDB() // chats data (log json to see what it looks like) !chats.get (chat.jid) && chats.insert (chat) diff --git a/src/WAConnection/4.Events.ts b/src/WAConnection/4.Events.ts index 5134515..425f967 100644 --- a/src/WAConnection/4.Events.ts +++ b/src/WAConnection/4.Events.ts @@ -1,7 +1,7 @@ import * as QR from 'qrcode-terminal' import { WAConnection as Base } from './3.Connect' -import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, PresenceUpdate, BaileysEvent, DisconnectReason, WANode, WAOpenResult, Presence, AuthenticationCredentials, WAParticipantAction, WAGroupMetadata } from './Constants' -import { whatsappID, unixTimestampSeconds, isGroupID, GET_MESSAGE_ID, WA_MESSAGE_ID, waMessageKey } from './Utils' +import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, PresenceUpdate, BaileysEvent, DisconnectReason, WAOpenResult, Presence, AuthenticationCredentials, WAParticipantAction, WAGroupMetadata, WAUser } from './Constants' +import { whatsappID, unixTimestampSeconds, isGroupID, GET_MESSAGE_ID, WA_MESSAGE_ID, waMessageKey, newMessagesDB } from './Utils' import KeyedDB from '@adiwajshing/keyed-db' import { Mutex } from './Mutex' @@ -41,8 +41,8 @@ export class WAConnection extends Base { // presence updates this.on('CB:Presence', json => { const update = json[1] as PresenceUpdate + const jid = whatsappID(update.participant || update.id) - const jid = whatsappID(update.id) const contact = this.contacts[jid] if (contact && jid.endsWith('@s.whatsapp.net')) { // if its a single chat if (update.t) contact.lastSeen = +update.t @@ -66,6 +66,10 @@ export class WAConnection extends Base { message['epoch'] = oldMessage['epoch'] chat.messages.delete (oldMessage) chat.messages.insert (message) + + const chatUpdate: Partial = { jid, messages: newMessagesDB([ message ]) } + this.emit ('chat-update', chatUpdate) + // emit deprecated this.emit ('message-update', message) } else { this.logger.debug ({ unhandled: true }, 'received message update for non-present message from ' + jid) @@ -236,15 +240,21 @@ export class WAConnection extends Base { this.chatAddMessage (message, chat) } protected chatAddMessage (message: WAMessage, chat: WAChat) { + // store updates in this + const chatUpdate: Partial & { jid: string } = { jid: chat.jid } + // add to count if the message isn't from me & there exists a message if (!message.key.fromMe && message.message) { chat.count += 1 - const contact = this.contacts[chat.jid] + chatUpdate.count = chat.count + const contact = this.contacts[message.participant || chat.jid] if (contact && contact.lastKnownPresence === Presence.composing) { // update presence contact.lastKnownPresence = Presence.available // emit change this.emit ('user-presence-update', { id: chat.jid, presence: Presence.available, participant: message.participant }) } } + + const messages = chat.messages const protocolMessage = message.message?.protocolMessage // if it's a message to delete another message if (protocolMessage) { @@ -256,17 +266,16 @@ export class WAConnection extends Base { found.messageStubType = WA_MESSAGE_STUB_TYPE.REVOKE delete found.message - this.emit ('message-update', found) + chatUpdate.messages = newMessagesDB([ found ]) + // emit deprecated + this.emit('message-update', found) } break default: break } - } else { - const messages = chat.messages - // if the message is already there - if (messages.get(WA_MESSAGE_ID(message))) return - + } else if (!messages.get(WA_MESSAGE_ID(message))) { // if the message is not already there + const last = messages.all().slice(-1) const lastEpoch = ((last && last[0]) && last[0]['epoch']) || 0 message['epoch'] = lastEpoch+1 @@ -277,9 +286,13 @@ export class WAConnection extends Base { messages.delete (messages.all()[0]) // delete oldest messages } // only update if it's an actual message - if (message.message) this.chatUpdateTime (chat) - - this.emit ('message-new', message) + if (message.message) { + this.chatUpdateTime (chat) + chatUpdate.t = chat.t + } + chatUpdate.messages = newMessagesDB([ message ]) + // emit deprecated + this.emit('message-new', message) // check if the message is an action if (message.messageStubType) { @@ -297,7 +310,7 @@ export class WAConnection extends Base { // mark the chat read only if you left the group if (participants.includes(this.user.jid)) { chat.read_only = 'true' - this.emit ('chat-update', { jid, read_only: chat.read_only }) + chatUpdate.read_only = 'true' } break case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_ADD: @@ -306,7 +319,7 @@ export class WAConnection extends Base { participants = message.messageStubParameters.map (whatsappID) if (participants.includes(this.user.jid) && chat.read_only === 'true') { delete chat.read_only - this.emit ('chat-update', { jid, read_only: 'false' }) + chatUpdate.read_only = 'false' } emitParticipantsUpdate('add') break @@ -325,20 +338,13 @@ export class WAConnection extends Base { case WA_MESSAGE_STUB_TYPE.GROUP_CHANGE_SUBJECT: case WA_MESSAGE_STUB_TYPE.GROUP_CREATE: chat.name = message.messageStubParameters[0] - this.emit ('chat-update', { jid, name: chat.name }) - break - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_PROMOTE: - case WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_DEMOTE: - participants = message.messageStubParameters.map (whatsappID) - emitParticipantsUpdate( - WA_MESSAGE_STUB_TYPE.GROUP_PARTICIPANT_PROMOTE ? - 'promote' : - 'demote' - ) + chatUpdate.name = chat.name break } } } + + this.emit('chat-update', chatUpdate) } protected chatUpdatedMessage (messageIDs: string[], status: WA_MESSAGE_STATUS_TYPE, chat: WAChat) { for (let id of messageIDs) { @@ -362,6 +368,8 @@ export class WAConnection extends Base { on (event: 'open', listener: (result: WAOpenResult) => void): this /** when the connection is opening */ on (event: 'connecting', listener: () => void): this + /** when the connection has been validated */ + on (event: 'connection-validated', listener: (user: WAUser) => void): this /** when the connection has closed */ on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this /** when the socket is closed */ @@ -378,11 +386,17 @@ export class WAConnection extends Base { on (event: 'user-status-update', listener: (update: {jid: string, status?: string}) => void): this /** when a new chat is added */ on (event: 'chat-new', listener: (chat: WAChat) => void): this - /** when a chat is updated (archived, deleted, pinned) */ + /** when a chat is updated (new message, updated message, deleted, pinned, etc) */ on (event: 'chat-update', listener: (chat: Partial & { jid: string }) => void): this - /** when a new message is relayed */ + /** + * when a new message is relayed + * @deprecated use `chat-update` + * */ on (event: 'message-new', listener: (message: WAMessage) => void): this - /** when a message object itself is updated (receives its media info or is deleted) */ + /** + * when a message object itself is updated (receives its media info or is deleted) + * @deprecated use `chat-update` + * */ on (event: 'message-update', listener: (message: WAMessage) => void): this /** when a message's status is updated (deleted, delivered, read, sent etc.) */ on (event: 'message-status-update', listener: (message: WAMessageStatusUpdate) => void): this diff --git a/src/WAConnection/Constants.ts b/src/WAConnection/Constants.ts index a3a8225..3e02973 100644 --- a/src/WAConnection/Constants.ts +++ b/src/WAConnection/Constants.ts @@ -425,8 +425,6 @@ export type BaileysEvent = 'user-status-update' | 'chat-new' | 'chat-update' | - 'message-new' | - 'message-update' | 'message-status-update' | 'group-participants-update' | 'group-update' | diff --git a/src/WAConnection/Utils.ts b/src/WAConnection/Utils.ts index d4faeeb..b4db866 100644 --- a/src/WAConnection/Utils.ts +++ b/src/WAConnection/Utils.ts @@ -9,6 +9,7 @@ import { URL } from 'url' import { Agent } from 'https' import Decoder from '../Binary/Decoder' import { MessageType, HKDFInfoKeys, MessageOptions, WAChat, WAMessageContent, BaileysError, WAMessageProto, TimedOutError, CancelledError, WAGenericMediaMessage, WAMessage, WAMessageKey } from './Constants' +import KeyedDB from '@adiwajshing/keyed-db' const platformMap = { 'aix': 'AIX', @@ -38,6 +39,12 @@ export const GET_MESSAGE_ID = (key: WAMessageKey) => `${key.id}|${key.fromMe ? 1 export const whatsappID = (jid: string) => jid?.replace ('@c.us', '@s.whatsapp.net') export const isGroupID = (jid: string) => jid?.endsWith ('@g.us') +export const newMessagesDB = (messages: WAMessage[] = []) => { + const db = new KeyedDB(waMessageKey, WA_MESSAGE_ID) + messages.forEach(m => db.insert(m)) + return db +} + export function shallowChanges (old: T, current: T): Partial { let changes: Partial = {} for (let key in current) {