mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Added ability to log messages
This commit is contained in:
@@ -33,7 +33,6 @@ const list = wsMessages.map ((item, i) => {
|
||||
try {
|
||||
const [tag, json, binaryTags] = decrypt (buffer, item.type === 'send')
|
||||
|
||||
if (json && json[1] && json[1].add) return
|
||||
return {tag, json: json && JSON.stringify(json), binaryTags}
|
||||
} catch (error) {
|
||||
return { error: error.message, data: buffer.toString('utf-8') }
|
||||
|
||||
@@ -20,10 +20,10 @@ export async function sendAndRetreiveMessage(conn: WAConnection, content, type:
|
||||
export const WAConnectionTest = (name: string, func: (conn: WAConnection) => void) => (
|
||||
describe(name, () => {
|
||||
const conn = new WAConnection()
|
||||
conn.logLevel = MessageLogLevel.info
|
||||
conn.connectOptions.maxIdleTimeMs = 30_000
|
||||
conn.logLevel = MessageLogLevel.unhandled
|
||||
|
||||
before(async () => {
|
||||
//conn.logLevel = MessageLogLevel.unhandled
|
||||
const file = './auth_info.json'
|
||||
await conn.loadAuthInfo(file).connect()
|
||||
await fs.writeFile(file, JSON.stringify(conn.base64EncodedAuthInfo(), null, '\t'))
|
||||
|
||||
@@ -30,7 +30,7 @@ import { STATUS_CODES, Agent } from 'http'
|
||||
|
||||
export class WAConnection extends EventEmitter {
|
||||
/** The version of WhatsApp Web we're telling the servers we are */
|
||||
version: [number, number, number] = [2, 2039, 6]
|
||||
version: [number, number, number] = [2, 2039, 9]
|
||||
/** The Browser we're telling the WhatsApp Web servers we are */
|
||||
browserDescription: [string, string, string] = Utils.Browsers.baileys ('Chrome')
|
||||
/** Metadata like WhatsApp id, name set on WhatsApp etc. */
|
||||
@@ -57,6 +57,10 @@ export class WAConnection extends EventEmitter {
|
||||
/** key to use to order chats */
|
||||
chatOrderingKey = Utils.WA_CHAT_KEY
|
||||
|
||||
/** log messages */
|
||||
shouldLogMessages = false
|
||||
messageLog: { tag: string, json: string, fromMe: boolean, binaryTags?: any[] }[] = []
|
||||
|
||||
maxCachedMessages = 50
|
||||
|
||||
chats: KeyedDB<WAChat> = new KeyedDB (Utils.WA_CHAT_KEY, value => value.jid)
|
||||
@@ -74,6 +78,7 @@ export class WAConnection extends EventEmitter {
|
||||
protected encoder = new Encoder()
|
||||
protected decoder = new Decoder()
|
||||
protected pendingRequests: {resolve: () => void, reject: (error) => void}[] = []
|
||||
|
||||
protected referenceDate = new Date () // used for generating tags
|
||||
protected lastSeen: Date = null // last keep alive received
|
||||
protected qrTimeout: NodeJS.Timeout
|
||||
@@ -208,15 +213,15 @@ export class WAConnection extends EventEmitter {
|
||||
* @param json query that was sent
|
||||
* @param timeoutMs timeout after which the promise will reject
|
||||
*/
|
||||
async waitForMessage(tag: string, json: Object = null, timeoutMs: number = null) {
|
||||
let promise = Utils.promiseTimeout(timeoutMs,
|
||||
(resolve, reject) => (this.callbacks[tag] = { queryJSON: json, callback: resolve, errCallback: reject }),
|
||||
)
|
||||
.catch((err) => {
|
||||
async waitForMessage(tag: string, json?: Object, timeoutMs?: number) {
|
||||
try {
|
||||
const result = await Utils.promiseTimeout(timeoutMs,
|
||||
(resolve, reject) => (this.callbacks[tag] = { queryJSON: json, callback: resolve, errCallback: reject }),
|
||||
)
|
||||
return result as any
|
||||
} finally {
|
||||
delete this.callbacks[tag]
|
||||
throw err
|
||||
})
|
||||
return promise as Promise<any>
|
||||
}
|
||||
}
|
||||
/** Generic function for action, set queries */
|
||||
async setQuery (nodes: WANode[], binaryTags: WATag = [WAMetric.group, WAFlag.ignore], tag?: string) {
|
||||
@@ -230,16 +235,18 @@ export class WAConnection extends EventEmitter {
|
||||
* @param binaryTags the tags to attach if the query is supposed to be sent encoded in binary
|
||||
* @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
|
||||
* recieved JSON
|
||||
*/
|
||||
async query({json, binaryTags, tag, timeoutMs, expect200, waitForOpen, longTag}: WAQuery) {
|
||||
waitForOpen = typeof waitForOpen === 'undefined' ? true : waitForOpen
|
||||
if (waitForOpen) await this.waitForConnection ()
|
||||
|
||||
waitForOpen = waitForOpen !== false
|
||||
if (waitForOpen) await this.waitForConnection()
|
||||
|
||||
tag = tag || this.generateMessageTag (longTag)
|
||||
const promise = this.waitForMessage(tag, json, timeoutMs)
|
||||
|
||||
if (binaryTags) tag = await this.sendBinary(json as WANode, binaryTags, tag, longTag)
|
||||
else tag = await this.sendJSON(json, tag, longTag)
|
||||
|
||||
const response = await this.waitForMessage(tag, json, timeoutMs)
|
||||
|
||||
const response = await promise
|
||||
if (expect200 && response.status && Math.floor(+response.status / 100) !== 2) {
|
||||
// read here: http://getstatuscode.com/599
|
||||
if (response.status === 599) {
|
||||
@@ -262,43 +269,48 @@ export class WAConnection extends EventEmitter {
|
||||
* @param tag the tag to attach to the message
|
||||
* @return the message tag
|
||||
*/
|
||||
protected sendBinary(json: WANode, tags: WATag, tag: string = null, longTag: boolean = false) {
|
||||
protected async sendBinary(json: WANode, tags: WATag, tag: string = null, longTag: boolean = false) {
|
||||
const binary = this.encoder.write(json) // encode the JSON to the WhatsApp binary format
|
||||
|
||||
let buff = Utils.aesEncrypt(binary, this.authInfo.encKey) // encrypt it using AES and our encKey
|
||||
const sign = Utils.hmacSign(buff, this.authInfo.macKey) // sign the message using HMAC and our macKey
|
||||
tag = tag || this.generateMessageTag(longTag)
|
||||
|
||||
if (this.shouldLogMessages) this.messageLog.push ({ tag, json: JSON.stringify(json), fromMe: true, binaryTags: tags })
|
||||
|
||||
buff = Buffer.concat([
|
||||
Buffer.from(tag + ','), // generate & prefix the message tag
|
||||
Buffer.from(tags), // prefix some bytes that tell whatsapp what the message is about
|
||||
sign, // the HMAC sign of the message
|
||||
buff, // the actual encrypted buffer
|
||||
])
|
||||
this.send(buff) // send it off
|
||||
await this.send(buff) // send it off
|
||||
return tag
|
||||
}
|
||||
/**
|
||||
* Send a plain JSON message to the WhatsApp servers
|
||||
* @param json the message to send
|
||||
* @param tag the tag to attach to the message
|
||||
* @return the message tag
|
||||
* @returns the message tag
|
||||
*/
|
||||
protected sendJSON(json: any[] | WANode, tag: string = null, longTag: boolean = false) {
|
||||
protected async sendJSON(json: any[] | WANode, tag: string = null, longTag: boolean = false) {
|
||||
tag = tag || this.generateMessageTag(longTag)
|
||||
this.send(`${tag},${JSON.stringify(json)}`)
|
||||
if (this.shouldLogMessages) this.messageLog.push ({ tag, json: JSON.stringify(json), fromMe: true })
|
||||
await this.send(`${tag},${JSON.stringify(json)}`)
|
||||
return tag
|
||||
}
|
||||
/** Send some message to the WhatsApp servers */
|
||||
protected send(m) {
|
||||
protected async send(m) {
|
||||
this.msgCount += 1 // increment message count, it makes the 'epoch' field when sending binary messages
|
||||
return this.conn.send(m)
|
||||
this.conn.send(m)
|
||||
}
|
||||
protected async waitForConnection () {
|
||||
if (this.state === 'open') return
|
||||
|
||||
await Utils.promiseTimeout (
|
||||
this.pendingRequestTimeoutMs,
|
||||
(resolve, reject) => this.pendingRequests.push({resolve, reject}))
|
||||
(resolve, reject) => this.pendingRequests.push({resolve, reject})
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Disconnect from the phone. Your auth credentials become invalid after sending a disconnect request.
|
||||
@@ -325,7 +337,6 @@ export class WAConnection extends EventEmitter {
|
||||
this.debounceTimeout && clearTimeout (this.debounceTimeout)
|
||||
|
||||
this.state = 'close'
|
||||
this.msgCount = 0
|
||||
this.phoneConnected = false
|
||||
this.lastDisconnectReason = reason
|
||||
this.lastDisconnectTime = new Date ()
|
||||
@@ -344,13 +355,14 @@ export class WAConnection extends EventEmitter {
|
||||
this.conn?.removeAllListeners ('error')
|
||||
this.conn?.removeAllListeners ('open')
|
||||
this.conn?.removeAllListeners ('message')
|
||||
//this.conn?.close ()
|
||||
|
||||
this.conn?.terminate()
|
||||
this.conn = null
|
||||
this.lastSeen = null
|
||||
this.msgCount = 0
|
||||
|
||||
Object.keys(this.callbacks).forEach(key => {
|
||||
if (!key.includes('function:')) {
|
||||
if (!key.startsWith('function:')) {
|
||||
this.log (`cancelling message wait: ${key}`, MessageLogLevel.info)
|
||||
this.callbacks[key].errCallback(new Error('close'))
|
||||
delete this.callbacks[key]
|
||||
|
||||
@@ -267,6 +267,7 @@ export class WAConnection extends Base {
|
||||
resolveTask = resolve
|
||||
cancelChats = () => reject (CancelledError())
|
||||
})
|
||||
console.log ('resolved task')
|
||||
|
||||
const oldChats = this.chats
|
||||
const updatedChats: { [k: string]: Partial<WAChat> } = {}
|
||||
@@ -307,12 +308,14 @@ export class WAConnection extends Base {
|
||||
this.emit ('received-pong')
|
||||
} else {
|
||||
const [messageTag, json] = Utils.decryptWA (message, this.authInfo?.macKey, this.authInfo?.encKey, new Decoder())
|
||||
if (!json) return
|
||||
if (this.shouldLogMessages) this.messageLog.push ({ tag: messageTag, json: JSON.stringify(json), fromMe: false })
|
||||
|
||||
if (!json) {return}
|
||||
|
||||
if (this.logLevel === MessageLogLevel.all) {
|
||||
this.log(messageTag + ', ' + JSON.stringify(json), MessageLogLevel.all)
|
||||
}
|
||||
if (!this.phoneConnected) {
|
||||
if (!this.phoneConnected && this.state === 'open') {
|
||||
this.phoneConnected = true
|
||||
this.emit ('connection-phone-change', { connected: true })
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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, WAOpenResult, Presence } from './Constants'
|
||||
import { whatsappID, unixTimestampSeconds, isGroupID, toNumber, GET_MESSAGE_ID, WA_MESSAGE_ID, WA_MESSAGE_KEY } from './Utils'
|
||||
import KeyedDB from '@adiwajshing/keyed-db'
|
||||
import { Mutex } from './Mutex'
|
||||
|
||||
export class WAConnection extends Base {
|
||||
|
||||
@@ -165,6 +166,7 @@ export class WAConnection extends Base {
|
||||
this.on ('qr', qr => QR.generate(qr, { small: true }))
|
||||
}
|
||||
/** Get the URL to download the profile picture of a person/group */
|
||||
@Mutex (jid => jid)
|
||||
async getProfilePicture(jid: string | null) {
|
||||
const response = await this.query({ json: ['query', 'ProfilePicThumb', jid || this.user.jid], expect200: true })
|
||||
return response.eurl as string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {WAConnection as Base} from './4.Events'
|
||||
import { Presence, WABroadcastListInfo, WAProfilePictureChange, WAChat, ChatModification } from './Constants'
|
||||
import { Presence, WABroadcastListInfo, WAProfilePictureChange, WAChat, ChatModification, WALoadChatOptions } from './Constants'
|
||||
import {
|
||||
WAMessage,
|
||||
WANode,
|
||||
@@ -96,17 +96,19 @@ export class WAConnection extends Base {
|
||||
* @param searchString optionally search for users
|
||||
* @returns the chats & the cursor to fetch the next page
|
||||
*/
|
||||
async loadChats (count: number, before: number | null, filters?: {searchString?: string, custom?: (c: WAChat) => boolean}) {
|
||||
const chats = this.chats.paginated (before, count, filters && (chat => (
|
||||
(typeof filters?.custom !== 'function' || filters?.custom(chat)) &&
|
||||
(typeof filters?.searchString === 'undefined' || chat.name?.includes (filters.searchString) || chat.jid?.startsWith(filters.searchString))
|
||||
async loadChats (count: number, before: number | null, options: WALoadChatOptions = {}) {
|
||||
const chats = this.chats.paginated (before, count, options && (chat => (
|
||||
(typeof options?.custom !== 'function' || options?.custom(chat)) &&
|
||||
(typeof options?.searchString === 'undefined' || chat.name?.includes (options.searchString) || chat.jid?.startsWith(options.searchString))
|
||||
)))
|
||||
await Promise.all (
|
||||
chats.map (async chat => (
|
||||
chat.imgUrl === undefined && await this.setProfilePicture (chat)
|
||||
))
|
||||
)
|
||||
const cursor = (chats[chats.length-1] && chats.length >= count) ? WA_CHAT_KEY (chats[chats.length-1]) : null
|
||||
if (options.loadProfilePicture !== false) {
|
||||
await Promise.all (
|
||||
chats.map (async chat => (
|
||||
typeof chat.imgUrl === 'undefined' && await this.setProfilePicture (chat)
|
||||
))
|
||||
)
|
||||
}
|
||||
const cursor = (chats[chats.length-1] && chats.length >= count) && this.chatOrderingKey (chats[chats.length-1])
|
||||
return { chats, cursor }
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -93,7 +93,7 @@ export class WAConnection extends Base {
|
||||
* @param cursor the data for which message to offset the query by
|
||||
* @param mostRecentFirst retreive the most recent message first or retreive from the converation start
|
||||
*/
|
||||
@Mutex ((jid, _, before, mostRecentFirst) => jid + (before?.id || '') + mostRecentFirst)
|
||||
@Mutex ()
|
||||
async loadMessages (
|
||||
jid: string,
|
||||
count: number,
|
||||
|
||||
@@ -65,6 +65,11 @@ export enum ReconnectMode {
|
||||
/** reconnects on all disconnects, including take overs */
|
||||
onAllErrors = 2
|
||||
}
|
||||
export type WALoadChatOptions = {
|
||||
searchString?: string
|
||||
custom?: (c: WAChat) => boolean
|
||||
loadProfilePicture?: boolean
|
||||
}
|
||||
export type WAConnectOptions = {
|
||||
/** New QR generation interval, set to null if you don't want to regenerate */
|
||||
regenerateQRIntervalMs?: number
|
||||
@@ -78,7 +83,6 @@ export type WAConnectOptions = {
|
||||
waitOnlyForLastMessage?: boolean
|
||||
/** max time for the phone to respond to a connectivity test */
|
||||
phoneResponseTime?: number
|
||||
|
||||
connectCooldownMs?: number
|
||||
/** agent which can be used for proxying connections */
|
||||
agent?: Agent
|
||||
|
||||
Reference in New Issue
Block a user