-Removed unread messages while connecting
-Added KeyedDB integration
-pdf mimetype fix + mimetype can be string now
This commit is contained in:
Adhiraj
2020-07-31 17:32:15 +05:30
parent 44bf842724
commit b06f53401f
8 changed files with 85 additions and 66 deletions

View File

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

View File

@@ -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] === '!') {

View File

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

View File

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