mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
compute chat deltas with connect
This commit is contained in:
11
README.md
11
README.md
@@ -126,13 +126,12 @@ They're all nicely typed up, so you shouldn't have any issues with an Intellisen
|
|||||||
Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.
|
Also, these events are fired regardless of whether they are initiated by the Baileys client or are relayed from your phone.
|
||||||
|
|
||||||
``` ts
|
``` ts
|
||||||
|
|
||||||
/** when the connection has opened successfully */
|
/** when the connection has opened successfully */
|
||||||
on (event: 'open', listener: () => void): this
|
on (event: 'open', listener: (result: WAOpenResult) => void): this
|
||||||
/** when the connection is opening */
|
/** when the connection is opening */
|
||||||
on (event: 'connecting', listener: () => void): this
|
on (event: 'connecting', listener: () => void): this
|
||||||
/** when the connection has closed */
|
/** when the connection has closed */
|
||||||
on (event: 'close', listener: (err: {reason?: string, isReconnecting: boolean}) => void): this
|
on (event: 'close', listener: (err: {reason?: DisconnectReason | string, isReconnecting: boolean}) => void): this
|
||||||
/** when a new QR is generated, ready for scanning */
|
/** when a new QR is generated, ready for scanning */
|
||||||
on (event: 'qr', listener: (qr: string) => void): this
|
on (event: 'qr', listener: (qr: string) => void): this
|
||||||
/** when the connection to the phone changes */
|
/** when the connection to the phone changes */
|
||||||
@@ -143,12 +142,14 @@ on (event: 'user-presence-update', listener: (update: PresenceUpdate) => void):
|
|||||||
on (event: 'user-status-update', listener: (update: {jid: string, status?: string}) => void): this
|
on (event: 'user-status-update', listener: (update: {jid: string, status?: string}) => void): this
|
||||||
/** when a new chat is added */
|
/** when a new chat is added */
|
||||||
on (event: 'chat-new', listener: (chat: WAChat) => void): this
|
on (event: 'chat-new', listener: (chat: WAChat) => void): this
|
||||||
/** when a chat is updated (archived, deleted, pinned, read, unread, name changed) */
|
/** when a chat is updated (archived, deleted, pinned) */
|
||||||
on (event: 'chat-update', listener: (chat: Partial<WAChat> & { jid: string }) => void): this
|
on (event: 'chat-update', listener: (chat: Partial<WAChat> & { jid: string }) => void): this
|
||||||
/** when a new message is relayed */
|
/** when a new message is relayed */
|
||||||
on (event: 'message-new', listener: (message: WAMessage) => void): this
|
on (event: 'message-new', listener: (message: WAMessage) => void): this
|
||||||
/** when a message is updated (deleted, delivered, read) */
|
/** when a message object itself is updated (receives its media info or is deleted) */
|
||||||
on (event: 'message-update', listener: (message: WAMessage) => void): this
|
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
|
||||||
/** when participants are added to a group */
|
/** when participants are added to a group */
|
||||||
on (event: 'group-participants-add', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
|
on (event: 'group-participants-add', listener: (update: {jid: string, participants: string[], actor?: string}) => void): this
|
||||||
/** when participants are removed or leave from a group */
|
/** when participants are removed or leave from a group */
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@adiwajshing/baileys",
|
"name": "@adiwajshing/baileys",
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"description": "WhatsApp Web API",
|
"description": "WhatsApp Web API",
|
||||||
"homepage": "https://github.com/adiwajshing/Baileys",
|
"homepage": "https://github.com/adiwajshing/Baileys",
|
||||||
"main": "lib/WAConnection/WAConnection.js",
|
"main": "lib/WAConnection/WAConnection.js",
|
||||||
|
|||||||
@@ -47,33 +47,21 @@ describe('Test Connect', () => {
|
|||||||
const conn = new WAConnection()
|
const conn = new WAConnection()
|
||||||
conn.connectOptions.timeoutMs = 20*1000
|
conn.connectOptions.timeoutMs = 20*1000
|
||||||
|
|
||||||
await conn
|
await conn.loadAuthInfo (auth).connect ()
|
||||||
.loadAuthInfo (auth)
|
assert.ok(conn.user)
|
||||||
.connect ()
|
assert.ok(conn.user.jid)
|
||||||
.then (conn => {
|
|
||||||
assert.ok(conn.user)
|
|
||||||
assert.ok(conn.user.jid)
|
|
||||||
|
|
||||||
const chatArray = conn.chats.all()
|
assertChatDBIntegrity (conn)
|
||||||
if (chatArray.length > 0) {
|
await conn.logout()
|
||||||
assert.ok(chatArray[0].jid)
|
conn.loadAuthInfo(auth)
|
||||||
assert.ok(chatArray[0].count !== null)
|
|
||||||
if (chatArray[0].messages.length > 0) {
|
await conn.connect()
|
||||||
assert.ok(chatArray[0].messages[0])
|
.then (() => assert.fail('should not have reconnected'))
|
||||||
}
|
.catch (err => {
|
||||||
}
|
assert.ok (err instanceof BaileysError)
|
||||||
})
|
assert.ok ((err as BaileysError).status >= 400)
|
||||||
.then (() => conn.logout())
|
})
|
||||||
.then (() => conn.loadAuthInfo(auth))
|
conn.close()
|
||||||
.then (() => (
|
|
||||||
conn.connect()
|
|
||||||
.then (() => assert.fail('should not have reconnected'))
|
|
||||||
.catch (err => {
|
|
||||||
assert.ok (err instanceof BaileysError)
|
|
||||||
assert.ok ((err as BaileysError).status >= 400)
|
|
||||||
})
|
|
||||||
))
|
|
||||||
.finally (() => conn.close())
|
|
||||||
})
|
})
|
||||||
it ('should disconnect & reconnect phone', async () => {
|
it ('should disconnect & reconnect phone', async () => {
|
||||||
const conn = new WAConnection ()
|
const conn = new WAConnection ()
|
||||||
@@ -237,6 +225,31 @@ describe ('Reconnects', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe ('Pending Requests', () => {
|
describe ('Pending Requests', () => {
|
||||||
|
it ('should correctly send updates', async () => {
|
||||||
|
const conn = new WAConnection ()
|
||||||
|
conn.connectOptions.timeoutMs = 20*1000
|
||||||
|
conn.pendingRequestTimeoutMs = null
|
||||||
|
|
||||||
|
conn.loadAuthInfo('./auth_info.json')
|
||||||
|
await conn.connect ()
|
||||||
|
|
||||||
|
conn.close ()
|
||||||
|
|
||||||
|
const oldChat = conn.chats.all()[0]
|
||||||
|
oldChat.archive = 'true' // mark the first chat as archived
|
||||||
|
oldChat.modify_tag = '1234' // change modify tag to detect change
|
||||||
|
|
||||||
|
const result = await conn.connect ()
|
||||||
|
assert.ok (!result.newConnection)
|
||||||
|
|
||||||
|
const chat = result.updatedChats[oldChat.jid]
|
||||||
|
assert.ok (chat)
|
||||||
|
|
||||||
|
assert.ok ('archive' in chat)
|
||||||
|
assert.equal (Object.keys(chat).length, 2)
|
||||||
|
|
||||||
|
conn.close ()
|
||||||
|
})
|
||||||
it('should queue requests when closed', async () => {
|
it('should queue requests when closed', async () => {
|
||||||
const conn = new WAConnection ()
|
const conn = new WAConnection ()
|
||||||
conn.pendingRequestTimeoutMs = null
|
conn.pendingRequestTimeoutMs = null
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
user: WAUser
|
user: WAUser
|
||||||
/** What level of messages to log to the console */
|
/** What level of messages to log to the console */
|
||||||
logLevel: MessageLogLevel = MessageLogLevel.info
|
logLevel: MessageLogLevel = MessageLogLevel.info
|
||||||
/** Should requests be queued when the connection breaks in between; if false, then an error will be thrown */
|
/** Should requests be queued when the connection breaks in between; if 0, then an error will be thrown */
|
||||||
pendingRequestTimeoutMs: number = null
|
pendingRequestTimeoutMs: number = null
|
||||||
/** The connection state */
|
/** The connection state */
|
||||||
state: WAConnectionState = 'close'
|
state: WAConnectionState = 'close'
|
||||||
@@ -43,7 +43,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
timeoutMs: 60*1000,
|
timeoutMs: 60*1000,
|
||||||
waitForChats: true,
|
waitForChats: true,
|
||||||
maxRetries: 5,
|
maxRetries: 5,
|
||||||
connectCooldownMs: 5000
|
connectCooldownMs: 2250
|
||||||
}
|
}
|
||||||
/** When to auto-reconnect */
|
/** When to auto-reconnect */
|
||||||
autoReconnect = ReconnectMode.onConnectionLost
|
autoReconnect = ReconnectMode.onConnectionLost
|
||||||
@@ -71,7 +71,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
protected lastSeen: Date = null // last keep alive received
|
protected lastSeen: Date = null // last keep alive received
|
||||||
protected qrTimeout: NodeJS.Timeout
|
protected qrTimeout: NodeJS.Timeout
|
||||||
|
|
||||||
protected lastConnectTime: Date = null
|
protected lastDisconnectTime: Date = null
|
||||||
protected lastDisconnectReason: DisconnectReason
|
protected lastDisconnectReason: DisconnectReason
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -85,7 +85,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
* @param options the connect options
|
* @param options the connect options
|
||||||
*/
|
*/
|
||||||
async connect() {
|
async connect() {
|
||||||
return this
|
return null
|
||||||
}
|
}
|
||||||
async unexpectedDisconnect (error: DisconnectReason) {
|
async unexpectedDisconnect (error: DisconnectReason) {
|
||||||
const willReconnect =
|
const willReconnect =
|
||||||
@@ -313,6 +313,7 @@ export class WAConnection extends EventEmitter {
|
|||||||
this.msgCount = 0
|
this.msgCount = 0
|
||||||
this.phoneConnected = false
|
this.phoneConnected = false
|
||||||
this.lastDisconnectReason = reason
|
this.lastDisconnectReason = reason
|
||||||
|
this.lastDisconnectTime = new Date ()
|
||||||
|
|
||||||
this.endConnection ()
|
this.endConnection ()
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,36 @@
|
|||||||
import * as Utils from './Utils'
|
import * as Utils from './Utils'
|
||||||
import { WAMessage, WAChat, MessageLogLevel, WANode, KEEP_ALIVE_INTERVAL_MS, BaileysError, WAConnectOptions, DisconnectReason, UNAUTHORIZED_CODES, WAContact, TimedOutError, CancelledError } from './Constants'
|
import { WAMessage, WAChat, MessageLogLevel, WANode, KEEP_ALIVE_INTERVAL_MS, BaileysError, WAConnectOptions, DisconnectReason, UNAUTHORIZED_CODES, WAContact, TimedOutError, CancelledError, WAOpenResult } from './Constants'
|
||||||
import {WAConnection as Base} from './1.Validation'
|
import {WAConnection as Base} from './1.Validation'
|
||||||
import Decoder from '../Binary/Decoder'
|
import Decoder from '../Binary/Decoder'
|
||||||
|
|
||||||
export class WAConnection extends Base {
|
export class WAConnection extends Base {
|
||||||
/** Connect to WhatsApp Web */
|
/** Connect to WhatsApp Web */
|
||||||
async connect() {
|
async connect () {
|
||||||
// if we're already connected, throw an error
|
// if we're already connected, throw an error
|
||||||
if (this.state !== 'close') throw new Error('cannot connect when state=' + this.state)
|
if (this.state !== 'close') throw new Error('cannot connect when state=' + this.state)
|
||||||
|
|
||||||
const options = this.connectOptions
|
const options = this.connectOptions
|
||||||
|
const newConnection = !this.authInfo
|
||||||
|
|
||||||
this.state = 'connecting'
|
this.state = 'connecting'
|
||||||
this.emit ('connecting')
|
this.emit ('connecting')
|
||||||
|
|
||||||
let tries = 0
|
let tries = 0
|
||||||
|
let lastConnect = this.lastDisconnectTime
|
||||||
|
var updates
|
||||||
while (this.state === 'connecting') {
|
while (this.state === 'connecting') {
|
||||||
tries += 1
|
tries += 1
|
||||||
try {
|
try {
|
||||||
const diff = this.lastConnectTime ? new Date().getTime()-this.lastConnectTime.getTime() : Infinity
|
const diff = lastConnect ? new Date().getTime()-lastConnect.getTime() : Infinity
|
||||||
await this.connectInternal (
|
updates = await this.connectInternal (
|
||||||
options,
|
options,
|
||||||
diff > this.connectOptions.connectCooldownMs ? 0 : this.connectOptions.connectCooldownMs
|
diff > this.connectOptions.connectCooldownMs ? 0 : this.connectOptions.connectCooldownMs
|
||||||
)
|
)
|
||||||
this.phoneConnected = true
|
this.phoneConnected = true
|
||||||
this.state = 'open'
|
this.state = 'open'
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
lastConnect = new Date()
|
||||||
|
|
||||||
const loggedOut = error instanceof BaileysError && UNAUTHORIZED_CODES.includes(error.status)
|
const loggedOut = error instanceof BaileysError && UNAUTHORIZED_CODES.includes(error.status)
|
||||||
const willReconnect = !loggedOut && (tries <= (options?.maxRetries || 5)) && this.state === 'connecting'
|
const willReconnect = !loggedOut && (tries <= (options?.maxRetries || 5)) && this.state === 'connecting'
|
||||||
|
|
||||||
@@ -36,12 +41,12 @@ export class WAConnection extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!willReconnect) throw error
|
if (!willReconnect) throw error
|
||||||
} finally {
|
|
||||||
this.lastConnectTime = new Date()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit ('open')
|
const updatedChats = !!this.lastDisconnectTime && updates
|
||||||
|
const result: WAOpenResult = { newConnection, updatedChats }
|
||||||
|
this.emit ('open', result)
|
||||||
|
|
||||||
this.releasePendingRequests ()
|
this.releasePendingRequests ()
|
||||||
this.startKeepAliveRequest()
|
this.startKeepAliveRequest()
|
||||||
@@ -50,8 +55,7 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
this.conn.on ('close', () => this.unexpectedDisconnect (DisconnectReason.close))
|
this.conn.on ('close', () => this.unexpectedDisconnect (DisconnectReason.close))
|
||||||
|
|
||||||
return this
|
return result
|
||||||
|
|
||||||
}
|
}
|
||||||
/** Meat of the connect logic */
|
/** Meat of the connect logic */
|
||||||
protected async connectInternal (options: WAConnectOptions, delayMs?: number) {
|
protected async connectInternal (options: WAConnectOptions, delayMs?: number) {
|
||||||
@@ -61,7 +65,7 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
const { ws, cancel } = Utils.openWebSocketConnection (5000, false)
|
const { ws, cancel } = Utils.openWebSocketConnection (5000, false)
|
||||||
|
|
||||||
let task = ws
|
let task: Promise<void | { [k: string]: Partial<WAChat> }> = ws
|
||||||
.then (conn => this.conn = conn)
|
.then (conn => this.conn = conn)
|
||||||
.then (() => (
|
.then (() => (
|
||||||
this.conn.on('message', data => this.onMessageRecieved(data as any))
|
this.conn.on('message', data => this.onMessageRecieved(data as any))
|
||||||
@@ -78,16 +82,17 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
let cancelTask: () => void
|
let cancelTask: () => void
|
||||||
if (typeof options?.waitForChats === 'undefined' ? true : options?.waitForChats) {
|
if (typeof options?.waitForChats === 'undefined' ? true : options?.waitForChats) {
|
||||||
const {waitForChats, cancelChats} = this.receiveChatsAndContacts(true)
|
const {waitForChats, cancelChats} = this.receiveChatsAndContacts()
|
||||||
|
|
||||||
task = Promise.all ([task, waitForChats]).then (() => {})
|
task = Promise.all ([task, waitForChats]).then (([_, updates]) => updates)
|
||||||
cancelTask = () => { cancelChats(); cancel() }
|
cancelTask = () => { cancelChats(); cancel() }
|
||||||
} else cancelTask = cancel
|
} else cancelTask = cancel
|
||||||
|
|
||||||
// determine whether reconnect should be used or not
|
// determine whether reconnect should be used or not
|
||||||
const shouldUseReconnect = this.lastDisconnectReason !== DisconnectReason.replaced &&
|
const shouldUseReconnect = this.lastDisconnectReason !== DisconnectReason.replaced &&
|
||||||
this.lastDisconnectReason !== DisconnectReason.unknown &&
|
this.lastDisconnectReason !== DisconnectReason.unknown &&
|
||||||
this.lastDisconnectReason !== DisconnectReason.intentional && this.user?.jid
|
this.lastDisconnectReason !== DisconnectReason.intentional &&
|
||||||
|
this.user?.jid
|
||||||
const reconnectID = shouldUseReconnect ? this.user.jid.replace ('@s.whatsapp.net', '@c.us') : null
|
const reconnectID = shouldUseReconnect ? this.user.jid.replace ('@s.whatsapp.net', '@c.us') : null
|
||||||
|
|
||||||
const promise = Utils.promiseTimeout(timeoutMs, (resolve, reject) => (
|
const promise = Utils.promiseTimeout(timeoutMs, (resolve, reject) => (
|
||||||
@@ -96,7 +101,7 @@ export class WAConnection extends Base {
|
|||||||
.catch (err => {
|
.catch (err => {
|
||||||
this.endConnection ()
|
this.endConnection ()
|
||||||
throw err
|
throw err
|
||||||
}) as Promise<void>
|
}) as Promise<void | { [k: string]: Partial<WAChat> }>
|
||||||
|
|
||||||
return { promise, cancel: cancelTask }
|
return { promise, cancel: cancelTask }
|
||||||
}
|
}
|
||||||
@@ -114,23 +119,27 @@ export class WAConnection extends Base {
|
|||||||
cancellations.push (cancel)
|
cancellations.push (cancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise
|
try {
|
||||||
.then (() => {
|
await promise
|
||||||
const {promise, cancel} = connect ()
|
|
||||||
cancellations.push (cancel)
|
const result = connect ()
|
||||||
return promise
|
cancellations.push (result.cancel)
|
||||||
})
|
|
||||||
.finally (() => {
|
const final = await result.promise
|
||||||
cancel()
|
return final
|
||||||
this.off('close', cancel)
|
} finally {
|
||||||
})
|
cancel()
|
||||||
|
this.off('close', cancel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets up callbacks to receive chats, contacts & messages.
|
* Sets up callbacks to receive chats, contacts & messages.
|
||||||
* Must be called immediately after connect
|
* Must be called immediately after connect
|
||||||
* @returns [chats, contacts]
|
* @returns [chats, contacts]
|
||||||
*/
|
*/
|
||||||
protected receiveChatsAndContacts(stopAfterMostRecentMessage: boolean=false) {
|
protected receiveChatsAndContacts() {
|
||||||
|
const oldChats: {[k: string]: WAChat} = this.chats['dict']
|
||||||
|
|
||||||
this.contacts = {}
|
this.contacts = {}
|
||||||
this.chats.clear ()
|
this.chats.clear ()
|
||||||
|
|
||||||
@@ -141,21 +150,19 @@ export class WAConnection extends Base {
|
|||||||
const deregisterCallbacks = () => {
|
const deregisterCallbacks = () => {
|
||||||
// wait for actual messages to load, "last" is the most recent message, "before" contains prior messages
|
// wait for actual messages to load, "last" is the most recent message, "before" contains prior messages
|
||||||
this.deregisterCallback(['action', 'add:last'])
|
this.deregisterCallback(['action', 'add:last'])
|
||||||
if (!stopAfterMostRecentMessage) {
|
this.deregisterCallback(['action', 'add:before'])
|
||||||
this.deregisterCallback(['action', 'add:before'])
|
this.deregisterCallback(['action', 'add:unread'])
|
||||||
this.deregisterCallback(['action', 'add:unread'])
|
|
||||||
}
|
|
||||||
this.deregisterCallback(['response', 'type:chat'])
|
this.deregisterCallback(['response', 'type:chat'])
|
||||||
this.deregisterCallback(['response', 'type:contacts'])
|
this.deregisterCallback(['response', 'type:contacts'])
|
||||||
}
|
}
|
||||||
const checkForResolution = () => {
|
const checkForResolution = () => receivedContacts && receivedMessages && resolveTask ()
|
||||||
if (receivedContacts && receivedMessages) resolveTask ()
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for messages to load
|
// wait for messages to load
|
||||||
const chatUpdate = json => {
|
const chatUpdate = json => {
|
||||||
receivedMessages = true
|
receivedMessages = true
|
||||||
const isLast = json[1].last || stopAfterMostRecentMessage
|
|
||||||
|
const isLast = json[1].last
|
||||||
const messages = json[2] as WANode[]
|
const messages = json[2] as WANode[]
|
||||||
|
|
||||||
if (messages) {
|
if (messages) {
|
||||||
@@ -171,10 +178,9 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
// wait for actual messages to load, "last" is the most recent message, "before" contains prior messages
|
// wait for actual messages to load, "last" is the most recent message, "before" contains prior messages
|
||||||
this.registerCallback(['action', 'add:last'], chatUpdate)
|
this.registerCallback(['action', 'add:last'], chatUpdate)
|
||||||
if (!stopAfterMostRecentMessage) {
|
this.registerCallback(['action', 'add:before'], chatUpdate)
|
||||||
this.registerCallback(['action', 'add:before'], chatUpdate)
|
this.registerCallback(['action', 'add:unread'], chatUpdate)
|
||||||
this.registerCallback(['action', 'add:unread'], chatUpdate)
|
|
||||||
}
|
|
||||||
// get chats
|
// get chats
|
||||||
this.registerCallback(['response', 'type:chat'], json => {
|
this.registerCallback(['response', 'type:chat'], json => {
|
||||||
if (json[1].duplicate || !json[2]) return
|
if (json[1].duplicate || !json[2]) return
|
||||||
@@ -185,6 +191,7 @@ export class WAConnection extends Base {
|
|||||||
this.log (`unexpectedly got null chat: ${item}, ${chat}`, MessageLogLevel.info)
|
this.log (`unexpectedly got null chat: ${item}, ${chat}`, MessageLogLevel.info)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.jid = Utils.whatsappID (chat.jid)
|
chat.jid = Utils.whatsappID (chat.jid)
|
||||||
chat.t = +chat.t
|
chat.t = +chat.t
|
||||||
chat.count = +chat.count
|
chat.count = +chat.count
|
||||||
@@ -220,21 +227,33 @@ export class WAConnection extends Base {
|
|||||||
|
|
||||||
// wait for the chats & contacts to load
|
// wait for the chats & contacts to load
|
||||||
let cancelChats: () => void
|
let cancelChats: () => void
|
||||||
const waitForChats = new Promise ((resolve, reject) => {
|
const waitForChats = async () => {
|
||||||
resolveTask = resolve
|
try {
|
||||||
cancelChats = () => reject (CancelledError())
|
await new Promise ((resolve, reject) => {
|
||||||
})
|
resolveTask = resolve
|
||||||
.then (() => (
|
cancelChats = () => reject (CancelledError())
|
||||||
this.chats
|
})
|
||||||
.all ()
|
|
||||||
.forEach (chat => {
|
const updatedChats: { [k: string]: Partial<WAChat> } = {}
|
||||||
|
for (let chat of this.chats.all()) {
|
||||||
const respectiveContact = this.contacts[chat.jid]
|
const respectiveContact = this.contacts[chat.jid]
|
||||||
chat.name = respectiveContact?.name || respectiveContact?.notify || chat.name
|
chat.name = respectiveContact?.name || respectiveContact?.notify || chat.name
|
||||||
})
|
|
||||||
))
|
|
||||||
.finally (deregisterCallbacks)
|
|
||||||
|
|
||||||
return { waitForChats, cancelChats }
|
if (!oldChats[chat.jid]) {
|
||||||
|
updatedChats[chat.jid] = chat
|
||||||
|
} else if (oldChats[chat.jid].t < chat.t || oldChats[chat.jid].modify_tag !== chat.modify_tag) {
|
||||||
|
const changes = Utils.shallowChanges (oldChats[chat.jid], chat)
|
||||||
|
delete changes.messages
|
||||||
|
|
||||||
|
updatedChats[chat.jid] = changes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedChats
|
||||||
|
} finally {
|
||||||
|
deregisterCallbacks ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { waitForChats: waitForChats (), cancelChats }
|
||||||
}
|
}
|
||||||
private releasePendingRequests () {
|
private releasePendingRequests () {
|
||||||
this.pendingRequests.forEach (({resolve}) => resolve()) // send off all pending request
|
this.pendingRequests.forEach (({resolve}) => resolve()) // send off all pending request
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as QR from 'qrcode-terminal'
|
import * as QR from 'qrcode-terminal'
|
||||||
import { WAConnection as Base } from './3.Connect'
|
import { WAConnection as Base } from './3.Connect'
|
||||||
import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, MessageLogLevel, PresenceUpdate, BaileysEvent, DisconnectReason, WANode } from './Constants'
|
import { WAMessageStatusUpdate, WAMessage, WAContact, WAChat, WAMessageProto, WA_MESSAGE_STUB_TYPE, WA_MESSAGE_STATUS_TYPE, MessageLogLevel, PresenceUpdate, BaileysEvent, DisconnectReason, WANode, WAOpenResult } from './Constants'
|
||||||
import { whatsappID, unixTimestampSeconds, isGroupID, toNumber } from './Utils'
|
import { whatsappID, unixTimestampSeconds, isGroupID, toNumber } from './Utils'
|
||||||
|
|
||||||
export class WAConnection extends Base {
|
export class WAConnection extends Base {
|
||||||
@@ -143,17 +143,6 @@ export class WAConnection extends Base {
|
|||||||
}
|
}
|
||||||
this.registerCallback('Msg', func)
|
this.registerCallback('Msg', func)
|
||||||
this.registerCallback('MsgInfo', func)
|
this.registerCallback('MsgInfo', func)
|
||||||
/*// genetic chat action
|
|
||||||
this.registerCallback (['Chat', 'cmd:action'], json => {
|
|
||||||
const data = json[1].data as WANode
|
|
||||||
if (!data) return
|
|
||||||
|
|
||||||
this.log (data, MessageLogLevel.info)
|
|
||||||
|
|
||||||
if (data[0] === 'create') {
|
|
||||||
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
|
|
||||||
this.on ('qr', qr => QR.generate(qr, { small: true }))
|
this.on ('qr', qr => QR.generate(qr, { small: true }))
|
||||||
}
|
}
|
||||||
@@ -300,7 +289,7 @@ export class WAConnection extends Base {
|
|||||||
// Add all event types
|
// Add all event types
|
||||||
|
|
||||||
/** when the connection has opened successfully */
|
/** when the connection has opened successfully */
|
||||||
on (event: 'open', listener: () => void): this
|
on (event: 'open', listener: (result: WAOpenResult) => void): this
|
||||||
/** when the connection is opening */
|
/** when the connection is opening */
|
||||||
on (event: 'connecting', listener: () => void): this
|
on (event: 'connecting', listener: () => void): this
|
||||||
/** when the connection has closed */
|
/** when the connection has closed */
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
WAUrlInfo,
|
WAUrlInfo,
|
||||||
WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto, BaileysError, MessageLogLevel, WA_MESSAGE_STATUS_TYPE
|
WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto, BaileysError, MessageLogLevel, WA_MESSAGE_STATUS_TYPE
|
||||||
} from './Constants'
|
} from './Constants'
|
||||||
import { whatsappID } from './Utils'
|
import { whatsappID, delay } from './Utils'
|
||||||
|
|
||||||
export class WAConnection extends Base {
|
export class WAConnection extends Base {
|
||||||
|
|
||||||
@@ -173,6 +173,27 @@ export class WAConnection extends Base {
|
|||||||
}
|
}
|
||||||
return loadMessage() as Promise<void>
|
return loadMessage() as Promise<void>
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Find a message in a given conversation
|
||||||
|
* @param chunkSize the number of messages to load in a single request
|
||||||
|
* @param onMessage callback for every message retreived, if return true -- the loop will break
|
||||||
|
*/
|
||||||
|
async findMessage (jid: string, chunkSize: number, onMessage: (m: WAMessage) => boolean) {
|
||||||
|
const chat = this.chats.get (whatsappID(jid))
|
||||||
|
let count = chat?.messages?.length || chunkSize
|
||||||
|
let offsetID
|
||||||
|
while (true) {
|
||||||
|
const {messages, cursor} = await this.loadMessages(jid, count, offsetID, true)
|
||||||
|
// callback with most recent message first (descending order of date)
|
||||||
|
for (let i = messages.length - 1; i >= 0; i--) {
|
||||||
|
if (onMessage(messages[i])) return
|
||||||
|
}
|
||||||
|
if (messages.length === 0) return
|
||||||
|
// if there are more messages
|
||||||
|
offsetID = cursor
|
||||||
|
await delay (200)
|
||||||
|
}
|
||||||
|
}
|
||||||
/** Load a single message specified by the ID */
|
/** Load a single message specified by the ID */
|
||||||
async loadMessage (jid: string, messageID: string) {
|
async loadMessage (jid: string, messageID: string) {
|
||||||
// load the message before the given message
|
// load the message before the given message
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ export enum WAFlag {
|
|||||||
}
|
}
|
||||||
/** Tag used with binary queries */
|
/** Tag used with binary queries */
|
||||||
export type WATag = [WAMetric, WAFlag]
|
export type WATag = [WAMetric, WAFlag]
|
||||||
|
|
||||||
/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */
|
/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */
|
||||||
export enum Presence {
|
export enum Presence {
|
||||||
available = 'available', // "online"
|
available = 'available', // "online"
|
||||||
@@ -310,6 +309,15 @@ export interface WAMessageStatusUpdate {
|
|||||||
/** Status of the Message IDs */
|
/** Status of the Message IDs */
|
||||||
type: WA_MESSAGE_STATUS_TYPE
|
type: WA_MESSAGE_STATUS_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WAOpenResult {
|
||||||
|
/** Was this connection opened via a QR scan */
|
||||||
|
newConnection: boolean
|
||||||
|
updatedChats?: {
|
||||||
|
[k: string]: Partial<WAChat>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export enum GroupSettingChange {
|
export enum GroupSettingChange {
|
||||||
messageSend = 'announcement',
|
messageSend = 'announcement',
|
||||||
settingsChange = 'locked',
|
settingsChange = 'locked',
|
||||||
|
|||||||
@@ -33,6 +33,21 @@ export const waChatUniqueKey = (c: WAChat) => ((c.t*100000) + (hashCode(c.jid)%1
|
|||||||
export const whatsappID = (jid: string) => jid?.replace ('@c.us', '@s.whatsapp.net')
|
export const whatsappID = (jid: string) => jid?.replace ('@c.us', '@s.whatsapp.net')
|
||||||
export const isGroupID = (jid: string) => jid?.includes ('@g.us')
|
export const isGroupID = (jid: string) => jid?.includes ('@g.us')
|
||||||
|
|
||||||
|
export function shallowChanges <T> (old: T, current: T): Partial<T> {
|
||||||
|
let changes: Partial<T> = {}
|
||||||
|
for (let key in current) {
|
||||||
|
if (old[key] !== current[key]) {
|
||||||
|
changes[key] = current[key] || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key in old) {
|
||||||
|
if (!changes[key] && old[key] !== current[key]) {
|
||||||
|
changes[key] = current[key] || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
/** decrypt AES 256 CBC; where the IV is prefixed to the buffer */
|
/** decrypt AES 256 CBC; where the IV is prefixed to the buffer */
|
||||||
export function aesDecrypt(buffer: Buffer, key: Buffer) {
|
export function aesDecrypt(buffer: Buffer, key: Buffer) {
|
||||||
return aesDecryptWithIV(buffer.slice(16, buffer.length), key, buffer.slice(0, 16))
|
return aesDecryptWithIV(buffer.slice(16, buffer.length), key, buffer.slice(0, 16))
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -373,14 +373,14 @@
|
|||||||
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==
|
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@^14.6.2":
|
"@types/node@*", "@types/node@^14.6.2":
|
||||||
version "14.6.2"
|
version "14.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.2.tgz#264b44c5a28dfa80198fc2f7b6d3c8a054b9491f"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.3.tgz#cc4f979548ca4d8e7b90bc0180052ab99ee64224"
|
||||||
integrity sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==
|
integrity sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww==
|
||||||
|
|
||||||
"@types/node@^13.7.0":
|
"@types/node@^13.7.0":
|
||||||
version "13.13.15"
|
version "13.13.16"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.16.tgz#66f2177047b61131eaac18c47eb25d6f1317070a"
|
||||||
integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw==
|
integrity sha512-dJ9vXxJ8MEwzNn4GkoAGauejhXoKuJyYKegsA6Af25ZpEDXomeVXt5HUWUNVHk5UN7+U0f6ghC6otwt+7PdSDg==
|
||||||
|
|
||||||
"@types/strip-bom@^3.0.0":
|
"@types/strip-bom@^3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
@@ -1878,9 +1878,9 @@ trim-newlines@^1.0.0:
|
|||||||
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
|
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
|
||||||
|
|
||||||
ts-node-dev@^1.0.0-pre.61:
|
ts-node-dev@^1.0.0-pre.61:
|
||||||
version "1.0.0-pre.61"
|
version "1.0.0-pre.62"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.61.tgz#e3ecd7388cdf4ebdebb9e754017550ed58cd4447"
|
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz#835644c43669b659a880379b9d06df86cef665ad"
|
||||||
integrity sha512-BZTR7mk7K3fJmoD5cdGho4jYZNd0bbQegJs6UXpfOB9qgVqMyis8p76VlFlMBoNhGPR5ojmE0zgk2qPAH7Ac2Q==
|
integrity sha512-hfsEuCqUZOVnZ86l7A3icxD1nFt1HEmLVbx4YOHCkrbSHPBNWcw+IczAPZo3zz7YiOm9vs0xG6OENNrkgm89tQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar "^3.4.0"
|
chokidar "^3.4.0"
|
||||||
dateformat "~1.0.4-1.2.3"
|
dateformat "~1.0.4-1.2.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user