26247c7681
- Remove unused eslint-disable directives from multiple files - Fix floating promise in baileys-event-stream.ts by adding void operator - Remove unused import recordMessageRetry from messages-send.ts - Prefix unused variable beforeTime with underscore in test file - Remove incorrect eslint-disable comments that were causing prettier errors Progress: Reduced from 68 to 35 errors. Remaining errors are primarily max-depth issues. https://claude.ai/code/session_015R3U3kiprQiNTTNNt31Sg6
782 lines
17 KiB
TypeScript
782 lines
17 KiB
TypeScript
/**
|
|
* Baileys Event Stream Management
|
|
*
|
|
* Provides:
|
|
* - Event buffering with backpressure
|
|
* - Event transformation and filtering
|
|
* - Priority queues for events
|
|
* - Batch processing
|
|
* - Dead letter queue for failed events
|
|
* - Event replay
|
|
* - Logging and metrics integration
|
|
*
|
|
* @module Utils/baileys-event-stream
|
|
*/
|
|
|
|
import { EventEmitter } from 'events'
|
|
import type { BaileysLogCategory } from './baileys-logger.js'
|
|
import { metrics } from './prometheus-metrics.js'
|
|
|
|
/**
|
|
* Baileys event types
|
|
*/
|
|
export type BaileysEventType =
|
|
| 'connection.update'
|
|
| 'creds.update'
|
|
| 'messaging-history.set'
|
|
| 'chats.set'
|
|
| 'contacts.set'
|
|
| 'messages.upsert'
|
|
| 'messages.update'
|
|
| 'messages.delete'
|
|
| 'messages.reaction'
|
|
| 'message-receipt.update'
|
|
| 'groups.upsert'
|
|
| 'groups.update'
|
|
| 'group-participants.update'
|
|
| 'presence.update'
|
|
| 'chats.update'
|
|
| 'chats.delete'
|
|
| 'labels.edit'
|
|
| 'labels.association'
|
|
| 'call'
|
|
| 'blocklist.set'
|
|
| 'blocklist.update'
|
|
| string // For custom events
|
|
|
|
/**
|
|
* Event priority
|
|
*/
|
|
export type EventPriority = 'critical' | 'high' | 'normal' | 'low'
|
|
|
|
/**
|
|
* Numeric priority values
|
|
*/
|
|
const PRIORITY_VALUES: Record<EventPriority, number> = {
|
|
critical: 0,
|
|
high: 1,
|
|
normal: 2,
|
|
low: 3
|
|
}
|
|
|
|
/**
|
|
* Stream event
|
|
*/
|
|
export interface StreamEvent<T = unknown> {
|
|
id: string
|
|
type: BaileysEventType
|
|
data: T
|
|
timestamp: number
|
|
priority: EventPriority
|
|
category: BaileysLogCategory
|
|
metadata?: Record<string, unknown>
|
|
retryCount?: number
|
|
originalTimestamp?: number
|
|
}
|
|
|
|
/**
|
|
* Event Stream options
|
|
*/
|
|
export interface EventStreamOptions {
|
|
/** Maximum buffer size (default: 10000) */
|
|
maxBufferSize?: number
|
|
/** Whether to apply backpressure when buffer is full */
|
|
enableBackpressure?: boolean
|
|
/** High water mark limit for backpressure */
|
|
highWaterMark?: number
|
|
/** Low water mark limit to resume */
|
|
lowWaterMark?: number
|
|
/** Batch size for processing */
|
|
batchSize?: number
|
|
/** Flush interval in ms (0 = disabled) */
|
|
flushInterval?: number
|
|
/** Maximum retries for failed events */
|
|
maxRetries?: number
|
|
/** Dead letter queue size */
|
|
deadLetterQueueSize?: number
|
|
/** Whether to collect metrics */
|
|
collectMetrics?: boolean
|
|
/** Stream name for metrics */
|
|
streamName?: string
|
|
}
|
|
|
|
/**
|
|
* Event handler
|
|
*/
|
|
export type EventHandler<T = unknown> = (event: StreamEvent<T>) => void | Promise<void>
|
|
|
|
/**
|
|
* Event filter
|
|
*/
|
|
export type EventFilter<T = unknown> = (event: StreamEvent<T>) => boolean
|
|
|
|
/**
|
|
* Event transformer
|
|
*/
|
|
export type EventTransformer<T = unknown, R = unknown> = (event: StreamEvent<T>) => StreamEvent<R>
|
|
|
|
/**
|
|
* Batch processing result
|
|
*/
|
|
export interface BatchResult {
|
|
processed: number
|
|
failed: number
|
|
duration: number
|
|
}
|
|
|
|
/**
|
|
* Stream statistics
|
|
*/
|
|
export interface EventStreamStats {
|
|
bufferSize: number
|
|
totalReceived: number
|
|
totalProcessed: number
|
|
totalFailed: number
|
|
totalDropped: number
|
|
deadLetterQueueSize: number
|
|
isBackpressured: boolean
|
|
lastEventTimestamp?: number
|
|
eventsByType: Record<string, number>
|
|
eventsByPriority: Record<EventPriority, number>
|
|
}
|
|
|
|
/**
|
|
* Event type to category mapping
|
|
*/
|
|
const EVENT_CATEGORY_MAP: Record<string, BaileysLogCategory> = {
|
|
'connection.update': 'connection',
|
|
'creds.update': 'auth',
|
|
'messaging-history.set': 'sync',
|
|
'chats.set': 'sync',
|
|
'contacts.set': 'sync',
|
|
'messages.upsert': 'message',
|
|
'messages.update': 'message',
|
|
'messages.delete': 'message',
|
|
'messages.reaction': 'message',
|
|
'message-receipt.update': 'message',
|
|
'groups.upsert': 'group',
|
|
'groups.update': 'group',
|
|
'group-participants.update': 'group',
|
|
'presence.update': 'presence',
|
|
'chats.update': 'message',
|
|
'chats.delete': 'message',
|
|
call: 'call',
|
|
'blocklist.set': 'sync',
|
|
'blocklist.update': 'sync'
|
|
}
|
|
|
|
/**
|
|
* Default priority by event type
|
|
*/
|
|
const EVENT_PRIORITY_MAP: Partial<Record<BaileysEventType, EventPriority>> = {
|
|
'connection.update': 'critical',
|
|
'creds.update': 'critical',
|
|
'messages.upsert': 'high',
|
|
'messages.update': 'high',
|
|
call: 'high',
|
|
'presence.update': 'low',
|
|
'messaging-history.set': 'normal'
|
|
}
|
|
|
|
/**
|
|
* Generate unique event ID
|
|
*/
|
|
function generateEventId(): string {
|
|
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
|
|
}
|
|
|
|
/**
|
|
* Main Event Stream class
|
|
*/
|
|
export class BaileysEventStream extends EventEmitter {
|
|
private buffer: StreamEvent[] = []
|
|
private handlers: Map<BaileysEventType | '*', Set<EventHandler>> = new Map()
|
|
private filters: EventFilter[] = []
|
|
private transformers: EventTransformer[] = []
|
|
private deadLetterQueue: StreamEvent[] = []
|
|
private options: Required<EventStreamOptions>
|
|
private stats: EventStreamStats
|
|
private isProcessing = false
|
|
private flushTimer?: ReturnType<typeof setInterval>
|
|
private paused = false
|
|
|
|
constructor(options: EventStreamOptions = {}) {
|
|
super()
|
|
|
|
this.options = {
|
|
maxBufferSize: options.maxBufferSize ?? 10000,
|
|
enableBackpressure: options.enableBackpressure ?? true,
|
|
highWaterMark: options.highWaterMark ?? 8000,
|
|
lowWaterMark: options.lowWaterMark ?? 2000,
|
|
batchSize: options.batchSize ?? 100,
|
|
flushInterval: options.flushInterval ?? 0,
|
|
maxRetries: options.maxRetries ?? 3,
|
|
deadLetterQueueSize: options.deadLetterQueueSize ?? 1000,
|
|
collectMetrics: options.collectMetrics ?? true,
|
|
streamName: options.streamName ?? 'baileys'
|
|
}
|
|
|
|
this.stats = this.createInitialStats()
|
|
|
|
// Start periodic flush if configured
|
|
if (this.options.flushInterval > 0) {
|
|
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval)
|
|
}
|
|
}
|
|
|
|
private createInitialStats(): EventStreamStats {
|
|
return {
|
|
bufferSize: 0,
|
|
totalReceived: 0,
|
|
totalProcessed: 0,
|
|
totalFailed: 0,
|
|
totalDropped: 0,
|
|
deadLetterQueueSize: 0,
|
|
isBackpressured: false,
|
|
eventsByType: {},
|
|
eventsByPriority: {
|
|
critical: 0,
|
|
high: 0,
|
|
normal: 0,
|
|
low: 0
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add event to stream
|
|
*/
|
|
push<T>(
|
|
type: BaileysEventType,
|
|
data: T,
|
|
options?: { priority?: EventPriority; metadata?: Record<string, unknown> }
|
|
): boolean {
|
|
// Check backpressure
|
|
if (this.options.enableBackpressure && this.buffer.length >= this.options.highWaterMark) {
|
|
this.stats.isBackpressured = true
|
|
this.emit('backpressure', { bufferSize: this.buffer.length })
|
|
|
|
if (this.buffer.length >= this.options.maxBufferSize) {
|
|
this.stats.totalDropped++
|
|
this.emit('dropped', { type, reason: 'buffer_full' })
|
|
|
|
if (this.options.collectMetrics) {
|
|
metrics.errors.inc({ category: 'event_stream', code: 'dropped' })
|
|
}
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
const event: StreamEvent<T> = {
|
|
id: generateEventId(),
|
|
type,
|
|
data,
|
|
timestamp: Date.now(),
|
|
priority: options?.priority || EVENT_PRIORITY_MAP[type] || 'normal',
|
|
category: EVENT_CATEGORY_MAP[type] || 'unknown',
|
|
metadata: options?.metadata,
|
|
retryCount: 0
|
|
}
|
|
|
|
// Apply transformers
|
|
let transformedEvent: StreamEvent = event
|
|
for (const transformer of this.transformers) {
|
|
transformedEvent = transformer(transformedEvent)
|
|
}
|
|
|
|
// Apply filters
|
|
for (const filter of this.filters) {
|
|
if (!filter(transformedEvent)) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Add to buffer at correct position (by priority)
|
|
this.insertByPriority(transformedEvent)
|
|
|
|
// Update statistics
|
|
this.stats.totalReceived++
|
|
this.stats.bufferSize = this.buffer.length
|
|
this.stats.lastEventTimestamp = Date.now()
|
|
this.stats.eventsByType[type] = (this.stats.eventsByType[type] || 0) + 1
|
|
this.stats.eventsByPriority[event.priority]++
|
|
|
|
if (this.options.collectMetrics) {
|
|
metrics.socketEvents.inc({ event: type })
|
|
}
|
|
|
|
this.emit('event', transformedEvent)
|
|
|
|
// Process if not paused
|
|
if (!this.paused && !this.isProcessing) {
|
|
void this.processNext()
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Insert event in buffer by priority
|
|
*/
|
|
private insertByPriority(event: StreamEvent): void {
|
|
const eventPriorityValue = PRIORITY_VALUES[event.priority]
|
|
|
|
// Find correct position
|
|
let insertIndex = this.buffer.length
|
|
for (let i = 0; i < this.buffer.length; i++) {
|
|
const bufferEvent = this.buffer[i]
|
|
if (bufferEvent && PRIORITY_VALUES[bufferEvent.priority] > eventPriorityValue) {
|
|
insertIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
this.buffer.splice(insertIndex, 0, event)
|
|
}
|
|
|
|
/**
|
|
* Register handler for event type
|
|
*/
|
|
on<T = unknown>(
|
|
event: BaileysEventType | '*' | 'backpressure' | 'drain' | 'dropped' | 'batch-processed' | 'retry',
|
|
handler: EventHandler<T>
|
|
): this {
|
|
// For control events (backpressure, drain, etc), use native EventEmitter
|
|
if (
|
|
event === 'backpressure' ||
|
|
event === 'drain' ||
|
|
event === 'dropped' ||
|
|
event === 'batch-processed' ||
|
|
event === 'retry'
|
|
) {
|
|
super.on(event, handler as any)
|
|
return this
|
|
}
|
|
|
|
// For Baileys events, use custom handler system
|
|
if (!this.handlers.has(event)) {
|
|
this.handlers.set(event, new Set())
|
|
}
|
|
|
|
this.handlers.get(event)!.add(handler as EventHandler)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Remove handler
|
|
*/
|
|
off(
|
|
event: BaileysEventType | '*' | 'backpressure' | 'drain' | 'dropped' | 'batch-processed' | 'retry',
|
|
handler: EventHandler
|
|
): this {
|
|
// For control events, use native EventEmitter
|
|
if (
|
|
event === 'backpressure' ||
|
|
event === 'drain' ||
|
|
event === 'dropped' ||
|
|
event === 'batch-processed' ||
|
|
event === 'retry'
|
|
) {
|
|
super.off(event, handler as any)
|
|
return this
|
|
}
|
|
|
|
// For Baileys events, use custom handler system
|
|
const handlers = this.handlers.get(event)
|
|
if (handlers) {
|
|
handlers.delete(handler)
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Register single-use handler
|
|
*/
|
|
once<T = unknown>(event: BaileysEventType, handler: EventHandler<T>): this {
|
|
const wrappedHandler: EventHandler<T> = e => {
|
|
this.off(event, wrappedHandler as EventHandler)
|
|
return handler(e)
|
|
}
|
|
|
|
return this.on(event, wrappedHandler)
|
|
}
|
|
|
|
/**
|
|
* Add filter
|
|
*/
|
|
addFilter(filter: EventFilter): this {
|
|
this.filters.push(filter)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Remove filter
|
|
*/
|
|
removeFilter(filter: EventFilter): this {
|
|
const index = this.filters.indexOf(filter)
|
|
if (index !== -1) {
|
|
this.filters.splice(index, 1)
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Add transformer
|
|
*/
|
|
addTransformer(transformer: EventTransformer): this {
|
|
this.transformers.push(transformer)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Process next events
|
|
*/
|
|
private async processNext(): Promise<void> {
|
|
if (this.isProcessing || this.paused || this.buffer.length === 0) {
|
|
return
|
|
}
|
|
|
|
this.isProcessing = true
|
|
|
|
try {
|
|
// Get batch of events
|
|
const batch = this.buffer.splice(0, this.options.batchSize)
|
|
this.stats.bufferSize = this.buffer.length
|
|
|
|
// Check if exited backpressure
|
|
if (this.stats.isBackpressured && this.buffer.length <= this.options.lowWaterMark) {
|
|
this.stats.isBackpressured = false
|
|
this.emit('drain')
|
|
}
|
|
|
|
// Process batch
|
|
const startTime = Date.now()
|
|
let processed = 0
|
|
let failed = 0
|
|
|
|
for (const event of batch) {
|
|
try {
|
|
await this.processEvent(event)
|
|
processed++
|
|
this.stats.totalProcessed++
|
|
} catch (error) {
|
|
failed++
|
|
this.stats.totalFailed++
|
|
await this.handleFailedEvent(event, error as Error)
|
|
}
|
|
}
|
|
|
|
const duration = Date.now() - startTime
|
|
this.emit('batch-processed', { processed, failed, duration } as BatchResult)
|
|
|
|
// Continue processing if there are more
|
|
if (this.buffer.length > 0) {
|
|
setImmediate(() => this.processNext())
|
|
}
|
|
} finally {
|
|
this.isProcessing = false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a single event
|
|
*/
|
|
private async processEvent(event: StreamEvent): Promise<void> {
|
|
// Type-specific handlers
|
|
const typeHandlers = this.handlers.get(event.type)
|
|
if (typeHandlers) {
|
|
for (const handler of typeHandlers) {
|
|
await handler(event)
|
|
}
|
|
}
|
|
|
|
// Global handlers
|
|
const globalHandlers = this.handlers.get('*')
|
|
if (globalHandlers) {
|
|
for (const handler of globalHandlers) {
|
|
await handler(event)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle failed event
|
|
*/
|
|
private async handleFailedEvent(event: StreamEvent, error: Error): Promise<void> {
|
|
event.retryCount = (event.retryCount || 0) + 1
|
|
|
|
if (event.retryCount <= this.options.maxRetries) {
|
|
// Re-add to buffer for retry
|
|
event.originalTimestamp = event.originalTimestamp || event.timestamp
|
|
event.timestamp = Date.now()
|
|
this.buffer.push(event)
|
|
this.stats.bufferSize = this.buffer.length
|
|
|
|
this.emit('retry', { event, error, attempt: event.retryCount })
|
|
} else {
|
|
// Send to dead letter queue
|
|
this.addToDeadLetterQueue(event, error)
|
|
}
|
|
|
|
if (this.options.collectMetrics) {
|
|
metrics.errors.inc({ category: 'event_stream', code: 'processing_failed' })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add event to dead letter queue
|
|
*/
|
|
private addToDeadLetterQueue(event: StreamEvent, error: Error): void {
|
|
const dlqEvent = {
|
|
...event,
|
|
metadata: {
|
|
...event.metadata,
|
|
error: error.message,
|
|
errorStack: error.stack,
|
|
movedToDlqAt: Date.now()
|
|
}
|
|
}
|
|
|
|
this.deadLetterQueue.push(dlqEvent)
|
|
|
|
// Limit DLQ size
|
|
while (this.deadLetterQueue.length > this.options.deadLetterQueueSize) {
|
|
this.deadLetterQueue.shift()
|
|
}
|
|
|
|
this.stats.deadLetterQueueSize = this.deadLetterQueue.length
|
|
this.emit('dead-letter', dlqEvent)
|
|
}
|
|
|
|
/**
|
|
* Force flush the buffer
|
|
*/
|
|
async flush(): Promise<BatchResult> {
|
|
const startTime = Date.now()
|
|
let processed = 0
|
|
let failed = 0
|
|
|
|
while (this.buffer.length > 0 && !this.paused) {
|
|
const batch = this.buffer.splice(0, this.options.batchSize)
|
|
|
|
for (const event of batch) {
|
|
try {
|
|
await this.processEvent(event)
|
|
processed++
|
|
this.stats.totalProcessed++
|
|
} catch (error) {
|
|
failed++
|
|
this.stats.totalFailed++
|
|
await this.handleFailedEvent(event, error as Error)
|
|
}
|
|
}
|
|
}
|
|
|
|
this.stats.bufferSize = this.buffer.length
|
|
|
|
return {
|
|
processed,
|
|
failed,
|
|
duration: Date.now() - startTime
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pause processing
|
|
*/
|
|
pause(): void {
|
|
this.paused = true
|
|
this.emit('pause')
|
|
}
|
|
|
|
/**
|
|
* Resume processing
|
|
*/
|
|
resume(): void {
|
|
this.paused = false
|
|
this.emit('resume')
|
|
void this.processNext()
|
|
}
|
|
|
|
/**
|
|
* Check if paused
|
|
*/
|
|
isPaused(): boolean {
|
|
return this.paused
|
|
}
|
|
|
|
/**
|
|
* Clear the buffer
|
|
*/
|
|
clear(): void {
|
|
this.buffer = []
|
|
this.stats.bufferSize = 0
|
|
this.emit('clear')
|
|
}
|
|
|
|
/**
|
|
* Return dead letter queue events
|
|
*/
|
|
getDeadLetterQueue(): StreamEvent[] {
|
|
return [...this.deadLetterQueue]
|
|
}
|
|
|
|
/**
|
|
* Clear dead letter queue
|
|
*/
|
|
clearDeadLetterQueue(): void {
|
|
this.deadLetterQueue = []
|
|
this.stats.deadLetterQueueSize = 0
|
|
}
|
|
|
|
/**
|
|
* Replay dead letter queue events
|
|
*/
|
|
async replayDeadLetterQueue(): Promise<BatchResult> {
|
|
const events = this.deadLetterQueue.splice(0)
|
|
this.stats.deadLetterQueueSize = 0
|
|
|
|
let processed = 0
|
|
let failed = 0
|
|
const startTime = Date.now()
|
|
|
|
for (const event of events) {
|
|
// Reset retry count
|
|
event.retryCount = 0
|
|
delete event.metadata?.error
|
|
delete event.metadata?.errorStack
|
|
delete event.metadata?.movedToDlqAt
|
|
|
|
try {
|
|
await this.processEvent(event)
|
|
processed++
|
|
} catch (error) {
|
|
failed++
|
|
this.addToDeadLetterQueue(event, error as Error)
|
|
}
|
|
}
|
|
|
|
return {
|
|
processed,
|
|
failed,
|
|
duration: Date.now() - startTime
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return statistics
|
|
*/
|
|
getStats(): EventStreamStats {
|
|
return { ...this.stats }
|
|
}
|
|
|
|
/**
|
|
* Reset statistics
|
|
*/
|
|
resetStats(): void {
|
|
this.stats = this.createInitialStats()
|
|
this.stats.bufferSize = this.buffer.length
|
|
this.stats.deadLetterQueueSize = this.deadLetterQueue.length
|
|
}
|
|
|
|
/**
|
|
* Destroy and clean up resources
|
|
*/
|
|
destroy(): void {
|
|
if (this.flushTimer) {
|
|
clearInterval(this.flushTimer)
|
|
}
|
|
|
|
this.buffer = []
|
|
this.deadLetterQueue = []
|
|
this.handlers.clear()
|
|
this.filters = []
|
|
this.transformers = []
|
|
this.removeAllListeners()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory to create event stream
|
|
*/
|
|
export function createEventStream(options?: EventStreamOptions): BaileysEventStream {
|
|
return new BaileysEventStream(options)
|
|
}
|
|
|
|
/**
|
|
* Pre-defined filters
|
|
*/
|
|
export const eventFilters = {
|
|
/** Filter by event type */
|
|
byType:
|
|
(...types: BaileysEventType[]): EventFilter =>
|
|
event =>
|
|
types.includes(event.type),
|
|
|
|
/** Filter by category */
|
|
byCategory:
|
|
(...categories: BaileysLogCategory[]): EventFilter =>
|
|
event =>
|
|
categories.includes(event.category),
|
|
|
|
/** Filter by minimum priority */
|
|
byMinPriority:
|
|
(minPriority: EventPriority): EventFilter =>
|
|
event =>
|
|
PRIORITY_VALUES[event.priority] <= PRIORITY_VALUES[minPriority],
|
|
|
|
/** Filter recent events (within ms) */
|
|
recentOnly:
|
|
(maxAgeMs: number): EventFilter =>
|
|
event =>
|
|
Date.now() - event.timestamp <= maxAgeMs,
|
|
|
|
/** Combine filters with AND */
|
|
and:
|
|
(...filters: EventFilter[]): EventFilter =>
|
|
event =>
|
|
filters.every(f => f(event)),
|
|
|
|
/** Combine filters with OR */
|
|
or:
|
|
(...filters: EventFilter[]): EventFilter =>
|
|
event =>
|
|
filters.some(f => f(event))
|
|
}
|
|
|
|
/**
|
|
* Pre-defined transformers
|
|
*/
|
|
export const eventTransformers = {
|
|
/** Add processing timestamp */
|
|
addProcessingTimestamp: (): EventTransformer => event => ({
|
|
...event,
|
|
metadata: {
|
|
...event.metadata,
|
|
processingTimestamp: Date.now()
|
|
}
|
|
}),
|
|
|
|
/** Add trace ID */
|
|
addTraceId:
|
|
(traceIdGenerator: () => string): EventTransformer =>
|
|
event => ({
|
|
...event,
|
|
metadata: {
|
|
...event.metadata,
|
|
traceId: traceIdGenerator()
|
|
}
|
|
}),
|
|
|
|
/** Elevate priority based on condition */
|
|
elevatepriorityIf:
|
|
(condition: (event: StreamEvent) => boolean, newPriority: EventPriority): EventTransformer =>
|
|
event =>
|
|
condition(event) ? { ...event, priority: newPriority } : event
|
|
}
|
|
|
|
export default BaileysEventStream
|