mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Updates
-Removed unread messages while connecting -Added KeyedDB integration -pdf mimetype fix + mimetype can be string now
This commit is contained in:
@@ -17,11 +17,12 @@ async function example() {
|
||||
client.logLevel = MessageLogLevel.info // set to unhandled to see what kind of stuff you can implement
|
||||
|
||||
// connect or timeout in 20 seconds (loads the auth file credentials if present)
|
||||
const [user, chats, contacts, unread] = await client.connect('./auth_info.json', 20 * 1000)
|
||||
const [user, chats, contacts] = await client.connect('./auth_info.json', 20 * 1000)
|
||||
const unread = chats.all().flatMap (chat => chat.messages.slice(chat.messages.length-chat.count))
|
||||
|
||||
console.log('oh hello ' + user.name + ' (' + user.id + ')')
|
||||
console.log('you have ' + unread.length + ' unread messages')
|
||||
console.log('you have ' + chats.length + ' chats & ' + contacts.length + ' contacts')
|
||||
console.log('you have ' + chats.all().length + ' chats & ' + contacts.length + ' contacts')
|
||||
console.log ('you have ' + unread.length + ' unread messages')
|
||||
|
||||
const authInfo = client.base64EncodedAuthInfo() // get all the auth info we need to restore this session
|
||||
fs.writeFileSync('./auth_info.json', JSON.stringify(authInfo, null, '\t')) // save this info to a file
|
||||
|
||||
52
README.md
52
README.md
@@ -34,35 +34,53 @@ Set the phone number you can randomly send messages to in a `.env` file with `TE
|
||||
|
||||
## Connecting
|
||||
``` ts
|
||||
const client = new WAClient()
|
||||
import { WAClient } from '@adiwajshing/baileys'
|
||||
|
||||
client.connect()
|
||||
.then (([user, chats, contacts, unread]) => {
|
||||
async function connectToWhatsApp () {
|
||||
const client = new WAClient()
|
||||
const [user, chats, contacts] = await client.connect ()
|
||||
console.log ("oh hello " + user.name + " (" + user.id + ")")
|
||||
console.log ("you have " + unread.length + " unread messages")
|
||||
console.log ("you have " + chats.length + " chats")
|
||||
})
|
||||
.catch (err => console.log("unexpected error: " + err) )
|
||||
|
||||
// every chat object has a list of most recent messages
|
||||
// can use that to retreive all your pending unread messages
|
||||
// the 'count' property a chat object reflects the number of unread messages
|
||||
// the 'count' property is -1 if the entire thread has been marked unread
|
||||
const unread = chats.all().flatMap (chat => chat.messages.slice(chat.messages.length-chat.count))
|
||||
|
||||
console.log ("you have " + unread.length + " unread messages")
|
||||
}
|
||||
|
||||
// run in main file
|
||||
connectToWhatsApp ()
|
||||
.catch (err => console.log("unexpected error: " + err) ) // catch any errors
|
||||
```
|
||||
|
||||
If the connection is successful, you will see a QR code printed on your terminal screen, scan it with WhatsApp on your phone and you'll be logged in!
|
||||
|
||||
If you don't want to wait for WhatsApp to send all your chats while connecting, you can use the following function:
|
||||
|
||||
``` ts
|
||||
const client = new WAClient()
|
||||
client.connectSlim() // does not wait for chats & contacts
|
||||
.then (user => {
|
||||
import { WAClient } from '@adiwajshing/baileys'
|
||||
|
||||
async function connectToWhatsApp () {
|
||||
const client = new WAClient()
|
||||
const user = await client.connectSlim ()
|
||||
console.log ("oh hello " + user.name + " (" + user.id + ")")
|
||||
|
||||
|
||||
client.receiveChatsAndContacts () // wait for chats & contacts in the background
|
||||
.then (([chats, contacts, unread]) => {
|
||||
console.log ("you have " + unread.length + " unread messages")
|
||||
console.log ("you have " + chats.length + " chats")
|
||||
.then (([chats, contacts]) => {
|
||||
console.log ("you have " + chats.all().length + " chats and " + contacts.length + " contacts")
|
||||
})
|
||||
})
|
||||
.catch (err => console.log("unexpected error: " + err))
|
||||
}
|
||||
// run in main file
|
||||
connectToWhatsApp ()
|
||||
.catch (err => console.log("unexpected error: " + err) ) // catch any errors
|
||||
```
|
||||
|
||||
Do note, the `chats` object returned is now a [KeyedDB](https://github.com/adiwajshing/keyed-db). This is done for the following reasons:
|
||||
- Most applications require chats to be ordered in descending order of time. (`KeyedDB` does this in `log(N)` time)
|
||||
- Most applications require pagination of chats (Use `chats.paginated()`)
|
||||
- Most applications require **O(1)** access to chats via the chat ID. (Use `chats.get(jid)` with `KeyedDB`)
|
||||
|
||||
## Saving & Restoring Sessions
|
||||
|
||||
You obviously don't want to keep scanning the QR code every time you want to connect.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"url": "git@github.com:adiwajshing/baileys.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adiwajshing/keyed-db": "github:adiwajshing/keyed-db",
|
||||
"curve25519-js": "0.0.4",
|
||||
"futoin-hkdf": "^1.3.2",
|
||||
"jimp": "^0.14.0",
|
||||
|
||||
@@ -55,7 +55,7 @@ export enum Mimetype {
|
||||
png = 'image/png',
|
||||
mp4 = 'video/mp4',
|
||||
gif = 'video/gif',
|
||||
pdf = 'appliction/pdf',
|
||||
pdf = 'application/pdf',
|
||||
ogg = 'audio/ogg; codecs=opus',
|
||||
/** for stickers */
|
||||
webp = 'image/webp',
|
||||
@@ -66,7 +66,7 @@ export interface MessageOptions {
|
||||
timestamp?: Date
|
||||
caption?: string
|
||||
thumbnail?: string
|
||||
mimetype?: Mimetype
|
||||
mimetype?: Mimetype | string
|
||||
validateID?: boolean,
|
||||
filename?: string
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import WS from 'ws'
|
||||
import KeyedDB from '@adiwajshing/keyed-db'
|
||||
import * as Utils from './Utils'
|
||||
import { AuthenticationCredentialsBase64, UserMetaData, WAMessage, WAChat, WAContact, MessageLogLevel } from './Constants'
|
||||
import { AuthenticationCredentialsBase64, UserMetaData, WAMessage, WAChat, WAContact, MessageLogLevel, WANode } from './Constants'
|
||||
import WAConnectionValidator from './Validation'
|
||||
import Decoder from '../Binary/Decoder'
|
||||
|
||||
@@ -9,13 +10,13 @@ export default class WAConnectionConnector extends WAConnectionValidator {
|
||||
* Connect to WhatsAppWeb
|
||||
* @param [authInfo] credentials or path to credentials to log back in
|
||||
* @param [timeoutMs] timeout after which the connect will fail, set to null for an infinite timeout
|
||||
* @return returns [userMetaData, chats, contacts, unreadMessages]
|
||||
* @return returns [userMetaData, chats, contacts]
|
||||
*/
|
||||
async connect(authInfo: AuthenticationCredentialsBase64 | string = null, timeoutMs: number = null) {
|
||||
try {
|
||||
const userInfo = await this.connectSlim(authInfo, timeoutMs)
|
||||
const chats = await this.receiveChatsAndContacts(timeoutMs)
|
||||
return [userInfo, ...chats] as [UserMetaData, WAChat[], WAContact[], WAMessage[]]
|
||||
return [userInfo, ...chats] as [UserMetaData, KeyedDB<WAChat>, WAContact[]]
|
||||
} catch (error) {
|
||||
this.close ()
|
||||
throw error
|
||||
@@ -66,13 +67,11 @@ export default class WAConnectionConnector extends WAConnectionValidator {
|
||||
/**
|
||||
* Sets up callbacks to receive chats, contacts & unread messages.
|
||||
* Must be called immediately after connect
|
||||
* @returns [chats, contacts, unreadMessages]
|
||||
* @returns [chats, contacts]
|
||||
*/
|
||||
async receiveChatsAndContacts(timeoutMs: number = null) {
|
||||
let chats: Array<WAChat> = []
|
||||
let contacts: Array<WAContact> = []
|
||||
let unreadMessages: Array<WAMessage> = []
|
||||
let chatMap: Record<string, {index: number, count: number}> = {}
|
||||
let contacts: WAContact[] = []
|
||||
const chats: KeyedDB<WAChat> = new KeyedDB (Utils.waChatUniqueKey, value => value.jid)
|
||||
|
||||
let receivedContacts = false
|
||||
let receivedMessages = false
|
||||
@@ -91,25 +90,17 @@ export default class WAConnectionConnector extends WAConnectionValidator {
|
||||
const chatUpdate = json => {
|
||||
receivedMessages = true
|
||||
const isLast = json[1].last
|
||||
json = json[2]
|
||||
if (json) {
|
||||
for (let k = json.length - 1; k >= 0; k--) {
|
||||
const message = json[k][2]
|
||||
const messages = json[2] as WANode[]
|
||||
|
||||
if (messages) {
|
||||
messages.reverse().forEach (([, __, message]: ['message', null, WAMessage]) => {
|
||||
const jid = message.key.remoteJid.replace('@s.whatsapp.net', '@c.us')
|
||||
const index = chatMap[jid]
|
||||
if (!message.key.fromMe && index && index.count > 0) {
|
||||
// only forward if the message is from the sender
|
||||
unreadMessages.push(message)
|
||||
index.count -= 1 // reduce
|
||||
}
|
||||
if (index) {
|
||||
chats[index.index].messages.push (message)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isLast && receivedContacts) { // if received contacts before messages
|
||||
convoResolve ()
|
||||
const chat = chats.get(jid)
|
||||
chat?.messages.unshift (message)
|
||||
})
|
||||
}
|
||||
// if received contacts before messages
|
||||
if (isLast && receivedContacts) convoResolve ()
|
||||
}
|
||||
// wait for actual messages to load, "last" is the most recent message, "before" contains prior messages
|
||||
this.registerCallback(['action', 'add:last'], chatUpdate)
|
||||
@@ -120,14 +111,13 @@ export default class WAConnectionConnector extends WAConnectionValidator {
|
||||
let json = await this.registerCallbackOneTime(['response', 'type:chat'])
|
||||
if (json[1].duplicate) json = await this.registerCallbackOneTime (['response', 'type:chat'])
|
||||
|
||||
json[2].forEach(chat => {
|
||||
chat[1].count = parseInt(chat[1].count)
|
||||
chat[1].messages = []
|
||||
chats.push(chat[1]) // chats data (log json to see what it looks like)
|
||||
// store the number of unread messages for each sender
|
||||
chatMap[chat[1].jid] = {index: chats.length-1, count: chat[1].count} //chat[1].count
|
||||
json[2].forEach(([_, chat]: [any, WAChat]) => {
|
||||
chat.count = +chat.count
|
||||
chat.messages = []
|
||||
chats.insert (chat) // chats data (log json to see what it looks like)
|
||||
})
|
||||
if (chats.length > 0) return waitForConvos()
|
||||
|
||||
if (chats.all().length > 0) return waitForConvos()
|
||||
}
|
||||
const waitForContacts = async () => {
|
||||
let json = await this.registerCallbackOneTime(['response', 'type:contacts'])
|
||||
@@ -142,7 +132,8 @@ export default class WAConnectionConnector extends WAConnectionValidator {
|
||||
// wait for the chats & contacts to load
|
||||
const promise = Promise.all([waitForChats(), waitForContacts()])
|
||||
await Utils.promiseTimeout (timeoutMs, promise)
|
||||
return [chats, contacts, unreadMessages] as [WAChat[], WAContact[], WAMessage[]]
|
||||
|
||||
return [chats, contacts] as [KeyedDB<WAChat>, WAContact[]]
|
||||
}
|
||||
private onMessageRecieved(message) {
|
||||
if (message[0] === '!') {
|
||||
|
||||
@@ -50,25 +50,25 @@ describe('Test Connect', () => {
|
||||
})
|
||||
it('should reconnect', async () => {
|
||||
const conn = new WAConnection()
|
||||
const [user, chats, contacts, unread] = await conn.connect(auth, 20*1000)
|
||||
const [user, chats, contacts] = await conn.connect(auth, 20*1000)
|
||||
|
||||
assert.ok(user)
|
||||
assert.ok(user.id)
|
||||
|
||||
assert.ok(chats)
|
||||
if (chats.length > 0) {
|
||||
assert.ok(chats[0].jid)
|
||||
assert.ok(chats[0].count !== null)
|
||||
|
||||
const chatArray = chats.all()
|
||||
if (chatArray.length > 0) {
|
||||
assert.ok(chatArray[0].jid)
|
||||
assert.ok(chatArray[0].count !== null)
|
||||
if (chatArray[0].messages.length > 0) {
|
||||
assert.ok(chatArray[0].messages[0])
|
||||
}
|
||||
}
|
||||
assert.ok(contacts)
|
||||
if (contacts.length > 0) {
|
||||
assert.ok(contacts[0].jid)
|
||||
}
|
||||
assert.ok(unread)
|
||||
if (unread.length > 0) {
|
||||
assert.ok(unread[0].key)
|
||||
}
|
||||
|
||||
await conn.logout()
|
||||
await assert.rejects(async () => conn.connectSlim(auth), 'reconnect should have failed')
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as Crypto from 'crypto'
|
||||
import HKDF from 'futoin-hkdf'
|
||||
import Decoder from '../Binary/Decoder'
|
||||
import {platform, release} from 'os'
|
||||
import { BaileysError } from './Constants'
|
||||
import { BaileysError, WAChat } from './Constants'
|
||||
import UserAgent from 'user-agents'
|
||||
|
||||
const platformMap = {
|
||||
@@ -18,6 +18,13 @@ export const Browsers = {
|
||||
/** The appropriate browser based on your OS & release */
|
||||
appropriate: browser => [ platformMap [platform()] || 'Ubuntu', browser, release() ] as [string, string, string]
|
||||
}
|
||||
function hashCode(s: string) {
|
||||
for(var i = 0, h = 0; i < s.length; i++)
|
||||
h = Math.imul(31, h) + s.charCodeAt(i) | 0;
|
||||
return h;
|
||||
}
|
||||
export const waChatUniqueKey = (c: WAChat) => ((+c.t*1000) + (hashCode(c.jid)%1000))*-1 // -1 to sort descending
|
||||
|
||||
export function userAgentString (browser) {
|
||||
const agent = new UserAgent (new RegExp(browser))
|
||||
return agent.toString ()
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"noImplicitThis": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true
|
||||
"declaration": true,
|
||||
"lib": ["es2019", "esnext.array"]
|
||||
},
|
||||
"include": ["src/*/*.ts"],
|
||||
"exclude": ["node_modules", "src/*/Tests.ts"]
|
||||
|
||||
Reference in New Issue
Block a user