mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
wip
This commit is contained in:
@@ -285,7 +285,7 @@ async function findAppModules() {
|
|||||||
const indentation = moduleIndentationMap[info.type]?.indentation
|
const indentation = moduleIndentationMap[info.type]?.indentation
|
||||||
let typeName = unnestName(info.type)
|
let typeName = unnestName(info.type)
|
||||||
if(indentation !== parentName && indentation) {
|
if(indentation !== parentName && indentation) {
|
||||||
typeName = `${indentation.replace(/\$/g, '.')}.${typeName}`
|
typeName = `${indentation.replaceAll('$', '.')}.${typeName}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(info.enumValues) {
|
// if(info.enumValues) {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": [2, 2244, 6]
|
"version": [2, 2243, 7]
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ProductCreate, ProductUpdate, SocketConfig } from '../Types'
|
import { GetCatalogOptions, ProductCreate, ProductUpdate, SocketConfig } from '../Types'
|
||||||
import { parseCatalogNode, parseCollectionsNode, parseOrderDetailsNode, parseProductNode, toProductNode, uploadingNecessaryImagesOfProduct } from '../Utils/business'
|
import { parseCatalogNode, parseCollectionsNode, parseOrderDetailsNode, parseProductNode, toProductNode, uploadingNecessaryImagesOfProduct } from '../Utils/business'
|
||||||
import { jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
import { BinaryNode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
||||||
import { getBinaryNodeChild } from '../WABinary/generic-utils'
|
import { getBinaryNodeChild } from '../WABinary/generic-utils'
|
||||||
import { makeMessagesRecvSocket } from './messages-recv'
|
import { makeMessagesRecvSocket } from './messages-recv'
|
||||||
|
|
||||||
@@ -12,9 +12,36 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
|||||||
waUploadToServer
|
waUploadToServer
|
||||||
} = sock
|
} = sock
|
||||||
|
|
||||||
const getCatalog = async(jid?: string, limit = 10) => {
|
const getCatalog = async({ jid, limit, cursor }: GetCatalogOptions) => {
|
||||||
jid = jid || authState.creds.me?.id
|
jid = jid || authState.creds.me?.id
|
||||||
jid = jidNormalizedUser(jid!)
|
jid = jidNormalizedUser(jid!)
|
||||||
|
|
||||||
|
const queryParamNodes: BinaryNode[] = [
|
||||||
|
{
|
||||||
|
tag: 'limit',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from((limit || 10).toString())
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'width',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from('100')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'height',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from('100')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if(cursor) {
|
||||||
|
queryParamNodes.push({
|
||||||
|
tag: 'after',
|
||||||
|
attrs: { },
|
||||||
|
content: cursor
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const result = await query({
|
const result = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -29,23 +56,7 @@ export const makeBusinessSocket = (config: SocketConfig) => {
|
|||||||
jid,
|
jid,
|
||||||
allow_shop_source: 'true'
|
allow_shop_source: 'true'
|
||||||
},
|
},
|
||||||
content: [
|
content: queryParamNodes
|
||||||
{
|
|
||||||
tag: 'limit',
|
|
||||||
attrs: { },
|
|
||||||
content: Buffer.from(limit.toString())
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'width',
|
|
||||||
attrs: { },
|
|
||||||
content: Buffer.from('100')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: 'height',
|
|
||||||
attrs: { },
|
|
||||||
content: Buffer.from('100')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -494,14 +494,27 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const presenceSubscribe = (toJid: string) => (
|
/**
|
||||||
|
* @param toJid the jid to subscribe to
|
||||||
|
* @param tcToken token for subscription, use if present
|
||||||
|
*/
|
||||||
|
const presenceSubscribe = (toJid: string, tcToken?: Buffer) => (
|
||||||
sendNode({
|
sendNode({
|
||||||
tag: 'presence',
|
tag: 'presence',
|
||||||
attrs: {
|
attrs: {
|
||||||
to: toJid,
|
to: toJid,
|
||||||
id: generateMessageTag(),
|
id: generateMessageTag(),
|
||||||
type: 'subscribe'
|
type: 'subscribe'
|
||||||
}
|
},
|
||||||
|
content: tcToken
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
tag: 'tctoken',
|
||||||
|
attrs: { },
|
||||||
|
content: tcToken
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: undefined
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -725,7 +738,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
|
|
||||||
if(shouldProcessHistoryMsg && !authState.creds.myAppStateKeyId) {
|
if(historyMsg && !authState.creds.myAppStateKeyId) {
|
||||||
logger.warn('skipping app state sync, as myAppStateKeyId is not set')
|
logger.warn('skipping app state sync, as myAppStateKeyId is not set')
|
||||||
pendingAppStateSync = true
|
pendingAppStateSync = true
|
||||||
}
|
}
|
||||||
@@ -733,7 +746,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
(async() => {
|
(async() => {
|
||||||
if(
|
if(
|
||||||
shouldProcessHistoryMsg
|
historyMsg
|
||||||
&& authState.creds.myAppStateKeyId
|
&& authState.creds.myAppStateKeyId
|
||||||
) {
|
) {
|
||||||
pendingAppStateSync = false
|
pendingAppStateSync = false
|
||||||
@@ -822,7 +835,8 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
// we keep buffering events until we finally have
|
// we keep buffering events until we finally have
|
||||||
// the key and can sync the messages
|
// the key and can sync the messages
|
||||||
if(!authState.creds?.myAppStateKeyId) {
|
if(!authState.creds?.myAppStateKeyId) {
|
||||||
needToFlushWithAppStateSync = ev.buffer()
|
ev.buffer()
|
||||||
|
needToFlushWithAppStateSync = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -268,6 +268,21 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
const from = jidNormalizedUser(node.attrs.from)
|
const from = jidNormalizedUser(node.attrs.from)
|
||||||
|
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
|
case 'privacy_token':
|
||||||
|
const tokenList = getBinaryNodeChildren(child, 'token')
|
||||||
|
for(const { attrs, content } of tokenList) {
|
||||||
|
const jid = attrs.jid
|
||||||
|
ev.emit('chats.update', [
|
||||||
|
{
|
||||||
|
id: jid,
|
||||||
|
tcToken: content as Buffer
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
logger.debug({ jid }, 'got privacy token update')
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
case 'w:gp2':
|
case 'w:gp2':
|
||||||
handleGroupNotification(node.attrs.participant, child, result)
|
handleGroupNotification(node.attrs.participant, child, result)
|
||||||
break
|
break
|
||||||
@@ -645,16 +660,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
identifier: string,
|
identifier: string,
|
||||||
exec: (node: BinaryNode) => Promise<any>
|
exec: (node: BinaryNode) => Promise<any>
|
||||||
) => {
|
) => {
|
||||||
const started = ev.buffer()
|
ev.buffer()
|
||||||
if(started) {
|
await execTask()
|
||||||
await execTask()
|
ev.flush()
|
||||||
if(started) {
|
|
||||||
await ev.flush()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const task = execTask()
|
|
||||||
ev.processInBuffer(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
function execTask() {
|
function execTask() {
|
||||||
return exec(node)
|
return exec(node)
|
||||||
@@ -662,17 +670,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called when all offline notifs are handled
|
|
||||||
ws.on('CB:ib,,offline', async(node: BinaryNode) => {
|
|
||||||
const child = getBinaryNodeChild(node, 'offline')
|
|
||||||
const offlineNotifs = +(child?.attrs.count || 0)
|
|
||||||
|
|
||||||
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
|
|
||||||
await ev.flush()
|
|
||||||
|
|
||||||
ev.emit('connection.update', { receivedPendingNotifications: true })
|
|
||||||
})
|
|
||||||
|
|
||||||
// recv a message
|
// recv a message
|
||||||
ws.on('CB:message', (node: BinaryNode) => {
|
ws.on('CB:message', (node: BinaryNode) => {
|
||||||
processNodeWithBuffer(node, 'processing message', handleMessage)
|
processNodeWithBuffer(node, 'processing message', handleMessage)
|
||||||
|
|||||||
@@ -532,11 +532,32 @@ export const makeSocket = ({
|
|||||||
end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }))
|
end(new Boom('Multi-device beta not joined', { statusCode: DisconnectReason.multideviceMismatch }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let didStartBuffer = false
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
// start buffering important events
|
if(creds.me?.id) {
|
||||||
ev.buffer()
|
// start buffering important events
|
||||||
|
// if we're logged in
|
||||||
|
ev.buffer()
|
||||||
|
didStartBuffer = true
|
||||||
|
}
|
||||||
|
|
||||||
ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined })
|
ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// called when all offline notifs are handled
|
||||||
|
ws.on('CB:ib,,offline', (node: BinaryNode) => {
|
||||||
|
const child = getBinaryNodeChild(node, 'offline')
|
||||||
|
const offlineNotifs = +(child?.attrs.count || 0)
|
||||||
|
|
||||||
|
logger.info(`handled ${offlineNotifs} offline messages/notifications`)
|
||||||
|
if(didStartBuffer) {
|
||||||
|
ev.flush()
|
||||||
|
logger.trace('flushed events for initial buffer')
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.emit('connection.update', { receivedPendingNotifications: true })
|
||||||
|
})
|
||||||
|
|
||||||
// update credentials when required
|
// update credentials when required
|
||||||
ev.on('creds.update', update => {
|
ev.on('creds.update', update => {
|
||||||
const name = update.me?.name
|
const name = update.me?.name
|
||||||
|
|||||||
@@ -22,16 +22,25 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.on('chats.update', () => fail('should not emit update event'))
|
ev.on('chats.update', () => fail('should not emit update event'))
|
||||||
|
|
||||||
ev.buffer()
|
ev.buffer()
|
||||||
ev.processInBuffer((async() => {
|
await Promise.all([
|
||||||
await delay(100)
|
(async() => {
|
||||||
ev.emit('chats.upsert', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
ev.buffer()
|
||||||
})())
|
await delay(100)
|
||||||
ev.processInBuffer((async() => {
|
ev.emit('chats.upsert', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
||||||
await delay(200)
|
const flushed = ev.flush()
|
||||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
expect(flushed).toBeFalsy()
|
||||||
})())
|
})(),
|
||||||
|
(async() => {
|
||||||
|
ev.buffer()
|
||||||
|
await delay(200)
|
||||||
|
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
||||||
|
const flushed = ev.flush()
|
||||||
|
expect(flushed).toBeFalsy()
|
||||||
|
})()
|
||||||
|
])
|
||||||
|
|
||||||
await ev.flush()
|
const flushed = ev.flush()
|
||||||
|
expect(flushed).toBeTruthy()
|
||||||
|
|
||||||
expect(chats).toHaveLength(1)
|
expect(chats).toHaveLength(1)
|
||||||
expect(chats[0].conversationTimestamp).toEqual(124)
|
expect(chats[0].conversationTimestamp).toEqual(124)
|
||||||
@@ -51,7 +60,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.emit('chats.delete', [chatId])
|
ev.emit('chats.delete', [chatId])
|
||||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chats).toHaveLength(1)
|
expect(chats).toHaveLength(1)
|
||||||
})
|
})
|
||||||
@@ -68,7 +77,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
||||||
ev.emit('chats.delete', [chatId])
|
ev.emit('chats.delete', [chatId])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chatsDeleted).toHaveLength(1)
|
expect(chatsDeleted).toHaveLength(1)
|
||||||
})
|
})
|
||||||
@@ -103,7 +112,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
ev.buffer()
|
ev.buffer()
|
||||||
ev.emit('chats.upsert', [{
|
ev.emit('chats.upsert', [{
|
||||||
@@ -123,7 +132,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
messages: [],
|
messages: [],
|
||||||
isLatest: false
|
isLatest: false
|
||||||
})
|
})
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chatsUpserted).toHaveLength(1)
|
expect(chatsUpserted).toHaveLength(1)
|
||||||
expect(chatsUpserted[0].id).toEqual(chatId)
|
expect(chatsUpserted[0].id).toEqual(chatId)
|
||||||
@@ -159,7 +168,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
muteEndTime: 123
|
muteEndTime: 123
|
||||||
}])
|
}])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chatsUpserted).toHaveLength(1)
|
expect(chatsUpserted).toHaveLength(1)
|
||||||
expect(chatsUpserted[0].archived).toBeUndefined()
|
expect(chatsUpserted[0].archived).toBeUndefined()
|
||||||
@@ -184,7 +193,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
})
|
})
|
||||||
ev.emit('chats.update', [{ id: chatId, archived: true }])
|
ev.emit('chats.update', [{ id: chatId, archived: true }])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chatRecv).toBeDefined()
|
expect(chatRecv).toBeDefined()
|
||||||
expect(chatRecv?.archived).toBeTruthy()
|
expect(chatRecv?.archived).toBeTruthy()
|
||||||
@@ -218,7 +227,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
||||||
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(msgs).toHaveLength(1)
|
expect(msgs).toHaveLength(1)
|
||||||
expect(msgs[0].message).toBeTruthy()
|
expect(msgs[0].message).toBeTruthy()
|
||||||
@@ -254,7 +263,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(msgs).toHaveLength(1)
|
expect(msgs).toHaveLength(1)
|
||||||
expect(msgs[0].userReceipt).toHaveLength(1)
|
expect(msgs[0].userReceipt).toHaveLength(1)
|
||||||
@@ -275,7 +284,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.DELIVERY_ACK } }])
|
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.DELIVERY_ACK } }])
|
||||||
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.READ } }])
|
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.READ } }])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(msgs).toHaveLength(1)
|
expect(msgs).toHaveLength(1)
|
||||||
expect(msgs[0].update.status).toEqual(WAMessageStatus.READ)
|
expect(msgs[0].update.status).toEqual(WAMessageStatus.READ)
|
||||||
@@ -303,7 +312,7 @@ describe('Event Buffer Tests', () => {
|
|||||||
ev.emit('chats.update', [{ id: msg.key.remoteJid!, unreadCount: 1, conversationTimestamp: msg.messageTimestamp }])
|
ev.emit('chats.update', [{ id: msg.key.remoteJid!, unreadCount: 1, conversationTimestamp: msg.messageTimestamp }])
|
||||||
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
||||||
|
|
||||||
await ev.flush()
|
ev.flush()
|
||||||
|
|
||||||
expect(chats[0].unreadCount).toBeUndefined()
|
expect(chats[0].unreadCount).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|||||||
37
src/Tests/test.messages.ts
Normal file
37
src/Tests/test.messages.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { WAMessageContent } from '../Types'
|
||||||
|
import { normalizeMessageContent } from '../Utils'
|
||||||
|
|
||||||
|
describe('Messages Tests', () => {
|
||||||
|
|
||||||
|
it('should correctly unwrap messages', () => {
|
||||||
|
const CONTENT = { imageMessage: { } }
|
||||||
|
expectRightContent(CONTENT)
|
||||||
|
expectRightContent({
|
||||||
|
ephemeralMessage: { message: CONTENT }
|
||||||
|
})
|
||||||
|
expectRightContent({
|
||||||
|
viewOnceMessage: {
|
||||||
|
message: {
|
||||||
|
ephemeralMessage: { message: CONTENT }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expectRightContent({
|
||||||
|
viewOnceMessage: {
|
||||||
|
message: {
|
||||||
|
viewOnceMessageV2: {
|
||||||
|
message: {
|
||||||
|
ephemeralMessage: { message: CONTENT }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function expectRightContent(content: WAMessageContent) {
|
||||||
|
expect(
|
||||||
|
normalizeMessageContent(content)
|
||||||
|
).toHaveProperty('imageMessage')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -70,4 +70,15 @@ export type OrderProduct = {
|
|||||||
export type OrderDetails = {
|
export type OrderDetails = {
|
||||||
price: OrderPrice
|
price: OrderPrice
|
||||||
products: OrderProduct[]
|
products: OrderProduct[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CatalogCursor = string
|
||||||
|
|
||||||
|
export type GetCatalogOptions = {
|
||||||
|
/** cursor to start from */
|
||||||
|
cursor?: CatalogCursor
|
||||||
|
/** number of products to fetch */
|
||||||
|
limit?: number
|
||||||
|
|
||||||
|
jid?: string
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,14 @@ import { getStream, getUrlFromDirectPath, toReadable } from './messages-media'
|
|||||||
export const parseCatalogNode = (node: BinaryNode) => {
|
export const parseCatalogNode = (node: BinaryNode) => {
|
||||||
const catalogNode = getBinaryNodeChild(node, 'product_catalog')
|
const catalogNode = getBinaryNodeChild(node, 'product_catalog')
|
||||||
const products = getBinaryNodeChildren(catalogNode, 'product').map(parseProductNode)
|
const products = getBinaryNodeChildren(catalogNode, 'product').map(parseProductNode)
|
||||||
return { products }
|
const paging = getBinaryNodeChild(catalogNode, 'paging')
|
||||||
|
|
||||||
|
return {
|
||||||
|
products,
|
||||||
|
nextPageCursor: paging
|
||||||
|
? getBinaryNodeChildString(paging, 'after')
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseCollectionsNode = (node: BinaryNode) => {
|
export const parseCollectionsNode = (node: BinaryNode) => {
|
||||||
|
|||||||
@@ -39,15 +39,16 @@ type BaileysBufferableEventEmitter = BaileysEventEmitter & {
|
|||||||
process(handler: (events: BaileysEventData) => void | Promise<void>): (() => void)
|
process(handler: (events: BaileysEventData) => void | Promise<void>): (() => void)
|
||||||
/**
|
/**
|
||||||
* starts buffering events, call flush() to release them
|
* starts buffering events, call flush() to release them
|
||||||
* @returns true if buffering just started, false if it was already buffering
|
|
||||||
* */
|
* */
|
||||||
buffer(): boolean
|
buffer(): void
|
||||||
/** buffers all events till the promise completes */
|
/** buffers all events till the promise completes */
|
||||||
createBufferedFunction<A extends any[], T>(work: (...args: A) => Promise<T>): ((...args: A) => Promise<T>)
|
createBufferedFunction<A extends any[], T>(work: (...args: A) => Promise<T>): ((...args: A) => Promise<T>)
|
||||||
/** flushes all buffered events */
|
/**
|
||||||
flush(): Promise<void>
|
* flushes all buffered events
|
||||||
/** waits for the task to complete, before releasing the buffer */
|
* @param force if true, will flush all data regardless of any pending buffers
|
||||||
processInBuffer(task: Promise<any>)
|
* @returns returns true if the flush actually happened, otherwise false
|
||||||
|
*/
|
||||||
|
flush(force?: boolean): boolean
|
||||||
/** is there an ongoing buffer */
|
/** is there an ongoing buffer */
|
||||||
isBuffering(): boolean
|
isBuffering(): boolean
|
||||||
}
|
}
|
||||||
@@ -62,9 +63,7 @@ export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter =
|
|||||||
const historyCache = new Set<string>()
|
const historyCache = new Set<string>()
|
||||||
|
|
||||||
let data = makeBufferData()
|
let data = makeBufferData()
|
||||||
let isBuffering = false
|
let buffersInProgress = 0
|
||||||
let preBufferTask: Promise<any> = Promise.resolve()
|
|
||||||
let preBufferTraces: string[] = []
|
|
||||||
|
|
||||||
// take the generic event and fire it as a baileys event
|
// take the generic event and fire it as a baileys event
|
||||||
ev.on('event', (map: BaileysEventData) => {
|
ev.on('event', (map: BaileysEventData) => {
|
||||||
@@ -74,25 +73,24 @@ export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter =
|
|||||||
})
|
})
|
||||||
|
|
||||||
function buffer() {
|
function buffer() {
|
||||||
if(!isBuffering) {
|
buffersInProgress += 1
|
||||||
logger.trace('buffering events')
|
|
||||||
isBuffering = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function flush() {
|
function flush(force = false) {
|
||||||
if(!isBuffering) {
|
// no buffer going on
|
||||||
return
|
if(!buffersInProgress) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace({ preBufferTraces }, 'releasing buffered events...')
|
if(!force) {
|
||||||
await preBufferTask
|
// reduce the number of buffers in progress
|
||||||
|
buffersInProgress -= 1
|
||||||
preBufferTraces = []
|
// if there are still some buffers going on
|
||||||
isBuffering = false
|
// then we don't flush now
|
||||||
|
if(buffersInProgress) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const newData = makeBufferData()
|
const newData = makeBufferData()
|
||||||
const chatUpdates = Object.values(data.chatUpdates)
|
const chatUpdates = Object.values(data.chatUpdates)
|
||||||
@@ -117,6 +115,8 @@ export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter =
|
|||||||
{ conditionalChatUpdatesLeft },
|
{ conditionalChatUpdatesLeft },
|
||||||
'released buffered events'
|
'released buffered events'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -131,34 +131,26 @@ export const makeEventBuffer = (logger: Logger): BaileysBufferableEventEmitter =
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emit<T extends BaileysEvent>(event: BaileysEvent, evData: BaileysEventMap[T]) {
|
emit<T extends BaileysEvent>(event: BaileysEvent, evData: BaileysEventMap[T]) {
|
||||||
if(isBuffering && BUFFERABLE_EVENT_SET.has(event)) {
|
if(buffersInProgress && BUFFERABLE_EVENT_SET.has(event)) {
|
||||||
append(data, historyCache, event as any, evData, logger)
|
append(data, historyCache, event as any, evData, logger)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return ev.emit('event', { [event]: evData })
|
return ev.emit('event', { [event]: evData })
|
||||||
},
|
},
|
||||||
processInBuffer(task) {
|
|
||||||
if(isBuffering) {
|
|
||||||
preBufferTask = Promise.allSettled([ preBufferTask, task ])
|
|
||||||
preBufferTraces.push(new Error('').stack!)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isBuffering() {
|
isBuffering() {
|
||||||
return isBuffering
|
return buffersInProgress > 0
|
||||||
},
|
},
|
||||||
buffer,
|
buffer,
|
||||||
flush,
|
flush,
|
||||||
createBufferedFunction(work) {
|
createBufferedFunction(work) {
|
||||||
return async(...args) => {
|
return async(...args) => {
|
||||||
const started = buffer()
|
buffer()
|
||||||
try {
|
try {
|
||||||
const result = await work(...args)
|
const result = await work(...args)
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
if(started) {
|
flush()
|
||||||
await flush()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
|
import logger from './logger'
|
||||||
|
|
||||||
|
const MUTEX_TIMEOUT_MS = 60_000
|
||||||
|
|
||||||
export const makeMutex = () => {
|
export const makeMutex = () => {
|
||||||
let task = Promise.resolve() as Promise<any>
|
let task = Promise.resolve() as Promise<any>
|
||||||
|
|
||||||
|
let taskTimeout: NodeJS.Timeout | undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mutex<T>(code: () => Promise<T> | T): Promise<T> {
|
mutex<T>(code: () => Promise<T> | T): Promise<T> {
|
||||||
task = (async() => {
|
task = (async() => {
|
||||||
|
const stack = new Error('mutex start').stack
|
||||||
|
let waitOver = false
|
||||||
|
taskTimeout = setTimeout(() => {
|
||||||
|
logger.warn({ stack, waitOver }, 'possible mutex deadlock')
|
||||||
|
}, MUTEX_TIMEOUT_MS)
|
||||||
// wait for the previous task to complete
|
// wait for the previous task to complete
|
||||||
// if there is an error, we swallow so as to not block the queue
|
// if there is an error, we swallow so as to not block the queue
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user