mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
feat: add bulk "process" capability to BaileysBufferableEventEmitter
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import EventEmitter from 'events'
|
||||
import { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { AuthenticationCreds, BaileysEvent, BaileysEventEmitter, BaileysEventMap, BufferedEventData, Chat, Contact, WAMessage, WAMessageStatus } from '../Types'
|
||||
@@ -20,9 +21,20 @@ const BUFFERABLE_EVENT = [
|
||||
|
||||
type BufferableEvent = typeof BUFFERABLE_EVENT[number]
|
||||
|
||||
/**
|
||||
* A map that contains a list of all events that have been triggered
|
||||
*
|
||||
* Note, this can contain different type of events
|
||||
* this can make processing events extremely efficient -- since everything
|
||||
* can be done in a single transaction
|
||||
*/
|
||||
type BaileysEventData = Partial<BaileysEventMap<AuthenticationCreds>>
|
||||
|
||||
const BUFFERABLE_EVENT_SET = new Set<BaileysEvent>(BUFFERABLE_EVENT)
|
||||
|
||||
type BaileysBufferableEventEmitter = BaileysEventEmitter & {
|
||||
/** Use to process events in a batch */
|
||||
process(handler: (events: BaileysEventData) => void | Promise<void>): (() => void)
|
||||
/**
|
||||
* starts buffering events, call flush() to release them
|
||||
* @returns true if buffering just started, false if it was already buffering
|
||||
@@ -39,24 +51,39 @@ type BaileysBufferableEventEmitter = BaileysEventEmitter & {
|
||||
* making the data processing more efficient.
|
||||
* @param ev the baileys event emitter
|
||||
*/
|
||||
export const makeEventBuffer = (
|
||||
ev: BaileysEventEmitter,
|
||||
logger: Logger
|
||||
): BaileysBufferableEventEmitter => {
|
||||
export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter => {
|
||||
const ev = new EventEmitter()
|
||||
|
||||
let data = makeBufferData()
|
||||
let isBuffering = false
|
||||
|
||||
let preBufferTask: Promise<any> = Promise.resolve()
|
||||
|
||||
// take the generic event and fire it as a baileys event
|
||||
ev.on('event', (map: BaileysEventData) => {
|
||||
for(const event in map) {
|
||||
ev.emit(event, map[event])
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
process(handler) {
|
||||
const listener = (map: BaileysEventData) => {
|
||||
handler(map)
|
||||
}
|
||||
|
||||
ev.on('event', listener)
|
||||
return () => {
|
||||
ev.off('event', listener)
|
||||
}
|
||||
},
|
||||
emit<T extends BaileysEvent>(event: BaileysEvent, evData: BaileysEventMap<AuthenticationCreds>[T]) {
|
||||
if(isBuffering && BUFFERABLE_EVENT_SET.has(event)) {
|
||||
append(data, event as any, evData, logger)
|
||||
return true
|
||||
}
|
||||
|
||||
return ev.emit(event, evData)
|
||||
return ev.emit('event', { [event]: evData })
|
||||
},
|
||||
processInBuffer(task) {
|
||||
if(isBuffering) {
|
||||
@@ -79,7 +106,7 @@ export const makeEventBuffer = (
|
||||
await preBufferTask
|
||||
|
||||
isBuffering = false
|
||||
flush(data, ev)
|
||||
ev.emit('event', consolidateEvents(data))
|
||||
data = makeBufferData()
|
||||
|
||||
logger.trace('released buffered events')
|
||||
@@ -300,58 +327,73 @@ function append<E extends BufferableEvent>(
|
||||
}
|
||||
}
|
||||
|
||||
function flush(data: BufferedEventData, ev: BaileysEventEmitter) {
|
||||
function consolidateEvents(data: BufferedEventData) {
|
||||
const map: BaileysEventData = { }
|
||||
|
||||
const chatUpsertList = Object.values(data.chatUpserts)
|
||||
chatUpsertList.length && ev.emit('chats.upsert', chatUpsertList)
|
||||
if(chatUpsertList.length) {
|
||||
map['chats.upsert'] = chatUpsertList
|
||||
}
|
||||
|
||||
const chatUpdateList = Object.values(data.chatUpdates)
|
||||
chatUpdateList.length && ev.emit('chats.update', chatUpdateList)
|
||||
if(chatUpdateList.length) {
|
||||
map['chats.update'] = chatUpdateList
|
||||
}
|
||||
|
||||
const chatDeleteList = Array.from(data.chatDeletes)
|
||||
chatDeleteList.length && ev.emit('chats.delete', chatDeleteList)
|
||||
if(chatDeleteList.length) {
|
||||
map['chats.delete'] = chatDeleteList
|
||||
}
|
||||
|
||||
const messageUpsertList = Object.values(data.messageUpserts)
|
||||
if(messageUpsertList.length) {
|
||||
const appends: WAMessage[] = []
|
||||
const notifys: WAMessage[] = []
|
||||
for(const { message, type } of messageUpsertList) {
|
||||
const arr = type === 'append' ? appends : notifys
|
||||
arr.push(message)
|
||||
}
|
||||
|
||||
if(appends.length) {
|
||||
ev.emit('messages.upsert', { type: 'append', messages: appends })
|
||||
}
|
||||
|
||||
if(notifys.length) {
|
||||
ev.emit('messages.upsert', { type: 'notify', messages: notifys })
|
||||
const type = messageUpsertList[0].type
|
||||
map['messages.upsert'] = {
|
||||
messages: messageUpsertList.map(m => m.message),
|
||||
type
|
||||
}
|
||||
}
|
||||
|
||||
const messageUpdateList = Object.values(data.messageUpdates)
|
||||
messageUpdateList.length && ev.emit('messages.update', messageUpdateList)
|
||||
if(messageUpdateList.length) {
|
||||
map['messages.update'] = messageUpdateList
|
||||
}
|
||||
|
||||
const messageDeleteList = Object.values(data.messageDeletes)
|
||||
messageDeleteList.length && ev.emit('messages.delete', { keys: messageDeleteList })
|
||||
if(messageDeleteList.length) {
|
||||
map['messages.delete'] = { keys: messageDeleteList }
|
||||
}
|
||||
|
||||
const messageReactionList = Object.values(data.messageReactions).flatMap(
|
||||
({ key, reactions }) => reactions.flatMap(reaction => ({ key, reaction }))
|
||||
)
|
||||
messageReactionList.length && ev.emit('messages.reaction', messageReactionList)
|
||||
if(messageReactionList.length) {
|
||||
map['messages.reaction'] = messageReactionList
|
||||
}
|
||||
|
||||
const messageReceiptList = Object.values(data.messageReceipts).flatMap(
|
||||
({ key, userReceipt }) => userReceipt.flatMap(receipt => ({ key, receipt }))
|
||||
)
|
||||
messageReceiptList.length && ev.emit('message-receipt.update', messageReceiptList)
|
||||
if(messageReceiptList.length) {
|
||||
map['message-receipt.update'] = messageReceiptList
|
||||
}
|
||||
|
||||
const contactUpsertList = Object.values(data.contactUpserts)
|
||||
contactUpsertList.length && ev.emit('contacts.upsert', contactUpsertList)
|
||||
if(contactUpsertList.length) {
|
||||
map['contacts.upsert'] = contactUpsertList
|
||||
}
|
||||
|
||||
const contactUpdateList = Object.values(data.contactUpdates)
|
||||
contactUpdateList.length && ev.emit('contacts.update', contactUpdateList)
|
||||
if(contactUpdateList.length) {
|
||||
map['contacts.update'] = contactUpdateList
|
||||
}
|
||||
|
||||
const groupUpdateList = Object.values(data.groupUpdates)
|
||||
groupUpdateList.length && ev.emit('groups.update', groupUpdateList)
|
||||
if(groupUpdateList.length) {
|
||||
map['groups.update'] = groupUpdateList
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
function concatChats<C extends Partial<Chat>>(a: C, b: C) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { AuthenticationCreds, BaileysEventMap, Chat, GroupMetadata, InitialReceivedChatsState, ParticipantAction, SignalKeyStoreWithTransaction, WAMessageStubType } from '../Types'
|
||||
import { AuthenticationCreds, BaileysEventEmitter, Chat, GroupMetadata, InitialReceivedChatsState, ParticipantAction, SignalKeyStoreWithTransaction, WAMessageStubType } from '../Types'
|
||||
import { downloadAndProcessHistorySyncNotification, normalizeMessageContent, toNumber } from '../Utils'
|
||||
import { areJidsSameUser, jidNormalizedUser } from '../WABinary'
|
||||
|
||||
@@ -10,6 +10,7 @@ type ProcessMessageContext = {
|
||||
downloadHistory: boolean
|
||||
creds: AuthenticationCreds
|
||||
keyStore: SignalKeyStoreWithTransaction
|
||||
ev: BaileysEventEmitter
|
||||
logger?: Logger
|
||||
treatCiphertextMessagesAsReal?: boolean
|
||||
}
|
||||
@@ -55,11 +56,10 @@ export const shouldIncrementChatUnread = (message: proto.IWebMessageInfo) => (
|
||||
|
||||
const processMessage = async(
|
||||
message: proto.IWebMessageInfo,
|
||||
{ downloadHistory, historyCache, recvChats, creds, keyStore, logger, treatCiphertextMessagesAsReal }: ProcessMessageContext
|
||||
{ downloadHistory, ev, historyCache, recvChats, creds, keyStore, logger, treatCiphertextMessagesAsReal }: ProcessMessageContext
|
||||
) => {
|
||||
const meId = creds.me!.id
|
||||
const { accountSettings } = creds
|
||||
const map: Partial<BaileysEventMap<any>> = { }
|
||||
|
||||
const chat: Partial<Chat> = { id: jidNormalizedUser(message.key.remoteJid) }
|
||||
|
||||
@@ -90,25 +90,24 @@ const processMessage = async(
|
||||
const isLatest = historyCache.size === 0 && !creds.processedHistoryMessages?.length
|
||||
|
||||
if(chats.length) {
|
||||
map['chats.set'] = { chats, isLatest }
|
||||
ev.emit('chats.set', { chats, isLatest })
|
||||
}
|
||||
|
||||
if(messages.length) {
|
||||
map['messages.set'] = { messages, isLatest }
|
||||
ev.emit('messages.set', { messages, isLatest })
|
||||
}
|
||||
|
||||
if(contacts.length) {
|
||||
map['contacts.set'] = { contacts, isLatest }
|
||||
ev.emit('contacts.set', { contacts, isLatest })
|
||||
}
|
||||
|
||||
if(didProcess) {
|
||||
map['creds.update'] = {
|
||||
...(map['creds.update'] || {}),
|
||||
ev.emit('creds.update', {
|
||||
processedHistoryMessages: [
|
||||
...(creds.processedHistoryMessages || []),
|
||||
{ key: message.key, timestamp: message.messageTimestamp }
|
||||
{ key: message.key, messageTimestamp: message.messageTimestamp }
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,14 +129,14 @@ const processMessage = async(
|
||||
}
|
||||
)
|
||||
|
||||
map['creds.update'] = { myAppStateKeyId: newAppStateSyncKeyId }
|
||||
ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId })
|
||||
} else {
|
||||
logger?.info({ protocolMsg }, 'recv app state sync with 0 keys')
|
||||
}
|
||||
|
||||
break
|
||||
case proto.ProtocolMessage.ProtocolMessageType.REVOKE:
|
||||
map['messages.update'] = [
|
||||
ev.emit('messages.update', [
|
||||
{
|
||||
key: {
|
||||
...message.key,
|
||||
@@ -145,7 +144,7 @@ const processMessage = async(
|
||||
},
|
||||
update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
|
||||
}
|
||||
]
|
||||
])
|
||||
break
|
||||
case proto.ProtocolMessage.ProtocolMessageType.EPHEMERAL_SETTING:
|
||||
Object.assign(chat, {
|
||||
@@ -159,19 +158,19 @@ const processMessage = async(
|
||||
...content.reactionMessage,
|
||||
key: message.key,
|
||||
}
|
||||
map['messages.reaction'] = [{
|
||||
ev.emit('messages.reaction', [{
|
||||
reaction,
|
||||
key: content.reactionMessage!.key!,
|
||||
}]
|
||||
}])
|
||||
} else if(message.messageStubType) {
|
||||
const jid = message.key!.remoteJid!
|
||||
//let actor = whatsappID (message.participant)
|
||||
let participants: string[]
|
||||
const emitParticipantsUpdate = (action: ParticipantAction) => (
|
||||
map['group-participants.update'] = { id: jid, participants, action }
|
||||
ev.emit('group-participants.update', { id: jid, participants, action })
|
||||
)
|
||||
const emitGroupUpdate = (update: Partial<GroupMetadata>) => {
|
||||
map['groups.update'] = [ { id: jid, ...update } ]
|
||||
ev.emit('groups.update', [{ id: jid, ...update }])
|
||||
}
|
||||
|
||||
const participantsIncludesMe = () => participants.find(jid => areJidsSameUser(meId, jid))
|
||||
@@ -222,10 +221,8 @@ const processMessage = async(
|
||||
}
|
||||
|
||||
if(Object.keys(chat).length > 1) {
|
||||
map['chats.update'] = [chat]
|
||||
ev.emit('chats.update', [chat])
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
export default processMessage
|
||||
Reference in New Issue
Block a user