From 242328abdcf31fb92106fc2f765e4354b7f215e4 Mon Sep 17 00:00:00 2001 From: Adhiraj Singh Date: Tue, 24 Nov 2020 18:56:07 +0530 Subject: [PATCH] Presence fixes --- src/Tests/Tests.Messages.ts | 11 +++++++++++ src/Tests/Tests.Misc.ts | 17 ++++++++++------- src/WAConnection/0.Base.ts | 2 +- src/WAConnection/5.User.ts | 21 +++++++++------------ src/WAConnection/6.MessagesSend.ts | 24 +++++++++++++++++++----- src/WAConnection/7.MessagesExtra.ts | 25 ++++++++++++++++--------- src/WAConnection/Constants.ts | 12 ++++++++---- 7 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/Tests/Tests.Messages.ts b/src/Tests/Tests.Messages.ts index 003f10b..9b5420e 100644 --- a/src/Tests/Tests.Messages.ts +++ b/src/Tests/Tests.Messages.ts @@ -11,6 +11,17 @@ WAConnectionTest('Messages', conn => { const message = await sendAndRetreiveMessage(conn, 'hello fren', MessageType.text) assert.strictEqual(message.message.conversation || message.message.extendedTextMessage?.text, 'hello fren') }) + it('should send a pending message', async () => { + const message = await sendAndRetreiveMessage(conn, 'hello fren', MessageType.text, { waitForAck: false }) + + await new Promise(resolve => conn.once('message-status-update', update => { + if (update.ids.includes(message.key.id)) { + assert.strictEqual(update.type, WA_MESSAGE_STATUS_TYPE.SERVER_ACK) + resolve() + } + })) + + }) it('should forward a message', async () => { let {messages} = await conn.loadMessages (testJid, 1) await conn.forwardMessage (testJid, messages[0], true) diff --git a/src/Tests/Tests.Misc.ts b/src/Tests/Tests.Misc.ts index 698377c..2d35ead 100644 --- a/src/Tests/Tests.Misc.ts +++ b/src/Tests/Tests.Misc.ts @@ -136,14 +136,17 @@ WAConnectionTest('Misc', (conn) => { await delay (500) } }) + // open the other phone and look at the updates to really verify stuff + it('should send presence updates', async () => { + conn.shouldLogMessages = true + conn.requestPresenceUpdate(testJid) - it('should update presence', async () => { - const presences = Object.values(Presence) - for (const i in presences) { - const response = await conn.updatePresence(testJid, presences[i]) - assert.strictEqual(response.status, 200) - - await delay(1500) + const sequence = [ Presence.available, Presence.composing, Presence.paused, Presence.recording, Presence.paused, Presence.unavailable ] + for (const presence of sequence) { + await delay(5000) + await conn.updatePresence(presence !== Presence.unavailable ? testJid : null, presence) + //console.log(conn.messageLog.slice(-1)) + console.log('sent update ', presence) } }) it('should generate link previews correctly', async () => { diff --git a/src/WAConnection/0.Base.ts b/src/WAConnection/0.Base.ts index f8d946c..a745d0c 100644 --- a/src/WAConnection/0.Base.ts +++ b/src/WAConnection/0.Base.ts @@ -210,7 +210,7 @@ export class WAConnection extends EventEmitter { * @param timeoutMs timeout after which the query will be failed (set to null to disable a timeout) * @param tag the tag to attach to the message */ - async query(q: WAQuery) { + async query(q: WAQuery): Promise { let {json, binaryTags, tag, timeoutMs, expect200, waitForOpen, longTag, requiresPhoneConnection, startDebouncedTimeout} = q requiresPhoneConnection = requiresPhoneConnection !== false waitForOpen = waitForOpen !== false diff --git a/src/WAConnection/5.User.ts b/src/WAConnection/5.User.ts index c806a21..7b3f3e3 100644 --- a/src/WAConnection/5.User.ts +++ b/src/WAConnection/5.User.ts @@ -51,18 +51,15 @@ export class WAConnection extends Base { * @param jid the ID of the person/group who you are updating * @param type your presence */ - updatePresence = (jid: string | null, type: Presence) => - this.query( - { - json: [ - 'action', - { epoch: this.msgCount.toString(), type: 'set' }, - [['presence', { type: type, to: jid }, null]], - ], - binaryTags: [WAMetric.group, WAFlag.acknowledge], - expect200: true - } - ) as Promise<{status: number}> + updatePresence = (jid: string | null, type: Presence) => this.sendBinary( + [ 'action', + {epoch: this.msgCount.toString(), type: 'set'}, + [ ['presence', { type: type, to: jid }, null] ] + ], + [WAMetric.presence, WAFlag[type] ], // weird stuff WA does + undefined, + true + ) /** Request an update on the presence of a user */ requestPresenceUpdate = async (jid: string) => this.query({ json: ['action', 'presence', 'subscribe', jid] }) /** Query the status of the person (see groupMetadata() for groups) */ diff --git a/src/WAConnection/6.MessagesSend.ts b/src/WAConnection/6.MessagesSend.ts index c98bac8..eddd735 100644 --- a/src/WAConnection/6.MessagesSend.ts +++ b/src/WAConnection/6.MessagesSend.ts @@ -29,7 +29,7 @@ export class WAConnection extends Base { options: MessageOptions = {}, ) { const waMessage = await this.prepareMessage (id, message, type, options) - await this.relayWAMessage (waMessage) + await this.relayWAMessage (waMessage, { waitForAck: options.waitForAck !== false }) return waMessage } /** Prepares a message for sending via sendWAMessage () */ @@ -217,12 +217,26 @@ export class WAConnection extends Base { return WAMessageProto.WebMessageInfo.create (messageJSON) } /** Relay (send) a WAMessage; more advanced functionality to send a built WA Message, you may want to stick with sendMessage() */ - async relayWAMessage(message: WAMessage) { + async relayWAMessage(message: WAMessage, { waitForAck } = { waitForAck: true }) { const json = ['action', {epoch: this.msgCount.toString(), type: 'relay'}, [['message', null, message]]] const flag = message.key.remoteJid === this.user?.jid ? WAFlag.acknowledge : WAFlag.ignore // acknowledge when sending message to oneself - await this.query({json, binaryTags: [WAMetric.message, flag], tag: message.key.id, expect200: true}) - - message.status = WA_MESSAGE_STATUS_TYPE.SERVER_ACK + const mID = message.key.id + message.status = WA_MESSAGE_STATUS_TYPE.PENDING + const promise = this.query({ + json, + binaryTags: [WAMetric.message, flag], + tag: mID, + expect200: true + }) + .then(() => message.status = WA_MESSAGE_STATUS_TYPE.SERVER_ACK) + + if (waitForAck) { + await promise + } else { + promise + .then(() => this.emit('message-status-update', { ids: [ mID ], to: message.key.remoteJid, type: WA_MESSAGE_STATUS_TYPE.SERVER_ACK })) + .catch(() => this.emit('message-status-update', { ids: [ mID ], to: message.key.remoteJid, type: WA_MESSAGE_STATUS_TYPE.ERROR })) + } await this.chatAddMessageAppropriate (message) } /** diff --git a/src/WAConnection/7.MessagesExtra.ts b/src/WAConnection/7.MessagesExtra.ts index 68361be..18ff4e9 100644 --- a/src/WAConnection/7.MessagesExtra.ts +++ b/src/WAConnection/7.MessagesExtra.ts @@ -299,14 +299,14 @@ export class WAConnection extends Base { return waMessage } /** - * Forward a message like WA does - * @param id the id to forward the message to + * Generate forwarded message content like WA does * @param message the message to forward * @param forceForward will show the message as forwarded even if it is from you */ - async forwardMessage(id: string, message: WAMessage, forceForward: boolean=false) { - const content = message.message - if (!content) throw new Error ('no content in message') + generateForwardMessageContent (message: WAMessage, forceForward: boolean=false) { + let content = message.message + if (!content) throw new BaileysError ('no content in message', { status: 400 }) + content = JSON.parse(JSON.stringify(content)) // hacky copy let key = Object.keys(content)[0] @@ -320,13 +320,20 @@ export class WAConnection extends Base { } if (score > 0) content[key].contextInfo = { forwardingScore: score, isForwarded: true } else content[key].contextInfo = {} - - const waMessage = this.prepareMessageFromContent (id, content, {}) + return content + } + /** + * Forward a message like WA + * @param jid the chat ID to forward to + * @param message the message to forward + * @param forceForward will show the message as forwarded even if it is from you + */ + async forwardMessage(jid: string, message: WAMessage, forceForward: boolean=false) { + const content = this.generateForwardMessageContent(message, forceForward) + const waMessage = this.prepareMessageFromContent (jid, content, {}) await this.relayWAMessage (waMessage) return waMessage } - - /** * Modify a given chat (archive, pin etc.) * @param jid the ID of the person/group you are modifiying diff --git a/src/WAConnection/Constants.ts b/src/WAConnection/Constants.ts index 14fe3e3..6d6c85a 100644 --- a/src/WAConnection/Constants.ts +++ b/src/WAConnection/Constants.ts @@ -253,22 +253,24 @@ export enum WAMetric { export const STORIES_JID = 'status@broadcast' export enum WAFlag { + available = 160, ignore = 1 << 7, acknowledge = 1 << 6, - available = 1 << 5, unavailable = 1 << 4, expires = 1 << 3, - skipOffline = 1 << 2, + composing = 1 << 2, + recording = 1 << 2, + paused = 1 << 2 } /** Tag used with binary queries */ export type WATag = [WAMetric, WAFlag] /** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */ export enum Presence { - available = 'available', // "online" unavailable = 'unavailable', // "offline" + available = 'available', // "online" composing = 'composing', // "typing..." recording = 'recording', // "recording..." - paused = 'paused', // I have no clue + paused = 'paused', // stop typing } /** Set of message types that are supported by the library */ export enum MessageType { @@ -348,6 +350,8 @@ export interface MessageOptions { duration?: number /** Fetches new media options for every media file */ forceNewMediaOptions?: boolean + /** Wait for the message to be sent to the server (default true) */ + waitForAck?: boolean } export interface WABroadcastListInfo { status: number