compute chat deltas with connect

This commit is contained in:
Adhiraj
2020-09-03 18:25:43 +05:30
parent 33448690a4
commit 3d5b37fc44
10 changed files with 178 additions and 111 deletions

View File

@@ -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 */

View File

@@ -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",

View File

@@ -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

View File

@@ -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 ()

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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',

View File

@@ -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))

View File

@@ -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"