UNPKG

n8n-nodes-wuzapi

Version:

n8n community nodes for Wuzapi - WhatsApp Multi-Device REST API

1,066 lines (1,065 loc) 52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WuzapiTrigger = void 0; class WuzapiTrigger { constructor() { this.description = { displayName: 'Wuzapi Trigger', name: 'wuzapiTrigger', icon: 'file:wuzapi.svg', group: ['trigger'], version: 1, subtitle: '={{$parameter["events"].join(", ")}}', description: 'Receive WhatsApp events from Wuzapi with complete event mapping', defaults: { name: 'Wuzapi Trigger', }, inputs: [], outputs: ["main" /* NodeConnectionType.Main */], credentials: [ { name: 'wuzapiApi', required: false, }, ], webhooks: [ { name: 'default', httpMethod: 'POST', responseMode: 'onReceived', path: 'wuzapi', }, ], properties: [ { displayName: 'Events', name: 'events', type: 'multiOptions', default: ['Message'], options: [ { name: 'All', value: 'All', description: 'Subscribe to all event types', }, { name: 'App State', value: 'AppState', description: 'Application state changes', }, { name: 'App State Sync Complete', value: 'AppStateSyncComplete', description: 'App state synchronization completed', }, { name: 'Blocklist', value: 'Blocklist', description: 'Full blocklist updates', }, { name: 'Blocklist Change', value: 'BlocklistChange', description: 'Contact block/unblock events', }, { name: 'Call Accept', value: 'CallAccept', description: 'Call accepted events', }, { name: 'Call Offer', value: 'CallOffer', description: 'Incoming call notifications', }, { name: 'Call Offer Notice', value: 'CallOfferNotice', description: 'Call offer notifications', }, { name: 'Call Relay Latency', value: 'CallRelayLatency', description: 'Call connection latency updates', }, { name: 'Call Terminate', value: 'CallTerminate', description: 'Call ended events', }, { name: 'CAT Refresh Error', value: 'CATRefreshError', description: 'Crypto auth token refresh errors', }, { name: 'Chat Presence', value: 'ChatPresence', description: 'Typing indicators and chat presence', }, { name: 'Client Outdated', value: 'ClientOutdated', description: 'WhatsApp client version outdated', }, { name: 'Connect Failure', value: 'ConnectFailure', description: 'Connection attempt failed', }, { name: 'Connected', value: 'Connected', description: 'WhatsApp connection established', }, { name: 'Disconnected', value: 'Disconnected', description: 'WhatsApp connection lost', }, { name: 'FB Ads Conversion', value: 'FBAdsConversion', description: 'Facebook Ads conversion events', }, { name: 'FB Message', value: 'FBMessage', description: 'Facebook bridge messages', }, { name: 'Group Info', value: 'GroupInfo', description: 'Group information updates', }, { name: 'History Sync', value: 'HistorySync', description: 'Message history synchronization', }, { name: 'Identity Change', value: 'IdentityChange', description: 'Contact security identity changes', }, { name: 'Joined Group', value: 'JoinedGroup', description: 'Group join notifications', }, { name: 'Keep Alive Restored', value: 'KeepAliveRestored', description: 'Connection keep-alive restored', }, { name: 'Keep Alive Timeout', value: 'KeepAliveTimeout', description: 'Connection keep-alive timeout', }, { name: 'Logged Out', value: 'LoggedOut', description: 'User logged out event', }, { name: 'Media Retry', value: 'MediaRetry', description: 'Media download retry events', }, { name: 'Message', value: 'Message', description: 'Incoming messages', }, { name: 'Newsletter Join', value: 'NewsletterJoin', description: 'Channel join notifications', }, { name: 'Newsletter Leave', value: 'NewsletterLeave', description: 'Channel leave notifications', }, { name: 'Newsletter Live Update', value: 'NewsletterLiveUpdate', description: 'Live updates from channels', }, { name: 'Newsletter Mute Change', value: 'NewsletterMuteChange', description: 'Channel mute status changes', }, { name: 'Offline Sync Completed', value: 'OfflineSyncCompleted', description: 'Offline message sync completed', }, { name: 'Offline Sync Preview', value: 'OfflineSyncPreview', description: 'Preview of offline messages to sync', }, { name: 'Pair Error', value: 'PairError', description: 'Device pairing failed', }, { name: 'Pair Success', value: 'PairSuccess', description: 'Device pairing successful', }, { name: 'Picture', value: 'Picture', description: 'Profile picture updates', }, { name: 'Presence', value: 'Presence', description: 'User online/offline status', }, { name: 'Privacy Settings', value: 'PrivacySettings', description: 'Privacy settings updates', }, { name: 'Push Name Setting', value: 'PushNameSetting', description: 'Push notification name changes', }, { name: 'QR', value: 'QR', description: 'QR code generated for pairing', }, { name: 'QR Scanned Without Multidevice', value: 'QRScannedWithoutMultidevice', description: 'QR code scanned without multidevice support', }, { name: 'Read Receipt', value: 'ReadReceipt', description: 'Message read confirmations', }, { name: 'Receipt', value: 'Receipt', description: 'Message delivery receipts', }, { name: 'Stream Error', value: 'StreamError', description: 'Connection stream error', }, { name: 'Stream Replaced', value: 'StreamReplaced', description: 'Connection stream replaced', }, { name: 'Temporary Ban', value: 'TemporaryBan', description: 'Account temporarily banned', }, { name: 'Undecryptable Message', value: 'UndecryptableMessage', description: 'Messages that could not be decrypted', }, { name: 'User About', value: 'UserAbout', description: 'User status/about updates', }, ], description: 'Events to listen for', }, { displayName: 'Filters', name: 'filters', type: 'collection', placeholder: 'Add Filter', default: {}, options: [ { displayName: 'From Phone (Sender)', name: 'fromPhone', type: 'string', default: '', placeholder: 'e.g. 5521971532700@s.whatsapp.net', description: 'Only process messages from this phone number (full JID format)', }, { displayName: 'Chat ID', name: 'chatId', type: 'string', default: '', placeholder: 'e.g. 120363312246943103@g.us or 5521971532700@s.whatsapp.net', description: 'Only process messages from this specific chat (group or direct)', }, { displayName: 'Message Type', name: 'messageType', type: 'options', default: '', options: [ { name: 'All Types', value: '', }, { name: 'Text', value: 'text', }, { name: 'URL/Link Preview', value: 'url', }, { name: 'Facebook Ads', value: 'ads', }, { name: 'Audio', value: 'audio', }, { name: 'Voice Note (PTT)', value: 'ptt', }, { name: 'Image', value: 'image', }, { name: 'Video', value: 'video', }, { name: 'Document', value: 'document', }, { name: 'Sticker', value: 'sticker', }, { name: 'Location', value: 'location', }, { name: 'Contact', value: 'contact', }, { name: 'Contacts Array', value: 'contacts', }, { name: 'Buttons', value: 'buttons', }, { name: 'List', value: 'list', }, { name: 'Template', value: 'template', }, { name: 'Interactive Response', value: 'interactiveResponse', }, { name: 'Interactive', value: 'interactive', }, { name: 'Poll', value: 'poll', }, { name: 'Event', value: 'event', }, { name: 'Product', value: 'product', }, { name: 'Order', value: 'order', }, { name: 'Unknown/Other', value: 'unknown', }, ], description: 'Only process specific message types based on content', }, { displayName: 'Contains Text', name: 'containsText', type: 'string', default: '', placeholder: 'e.g. hello', description: 'Only process messages containing this text (case insensitive)', }, { displayName: 'Is Group', name: 'isGroup', type: 'options', default: 'any', options: [ { name: 'Any', value: 'any', description: 'Process all messages', }, { name: 'Groups Only', value: 'true', description: 'Only process group messages', }, { name: 'Direct Only', value: 'false', description: 'Only process direct messages', }, ], description: 'Filter by message source (based on IsGroup field)', }, { displayName: 'Is From Me', name: 'isFromMe', type: 'options', default: 'any', options: [ { name: 'Any', value: 'any', description: 'Process all messages', }, { name: 'From Me Only', value: 'true', description: 'Only process messages I sent', }, { name: 'From Others Only', value: 'false', description: 'Only process messages from others', }, ], description: 'Filter by message sender (based on IsFromMe field)', }, { displayName: 'Token Filter', name: 'tokenFilter', type: 'string', typeOptions: { password: true, }, default: '', placeholder: 'e.g. setupautomatizado', description: 'Only process events from this specific token', }, ], }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Include Raw Data', name: 'includeRawData', type: 'boolean', default: false, description: 'Whether to include the complete raw webhook data for debugging', }, { displayName: 'Simplify Output', name: 'simplifyOutput', type: 'boolean', default: true, description: 'Whether to simplify the output structure for easier use', }, { displayName: 'Include Media Data', name: 'includeMediaData', type: 'boolean', default: true, description: 'Whether to include base64 and S3 media data in the output', }, { displayName: 'Parse Message Content', name: 'parseMessageContent', type: 'boolean', default: true, description: 'Whether to parse and extract message content from complex message types', }, { displayName: 'Allow Unknown Events', name: 'allowUnknownEvents', type: 'boolean', default: false, description: 'Whether to process unknown event types not listed in the Events selection. Useful for new event types like custom integrations, ads conversions, or other events that may be added in the future.', }, ], }, ], }; } static extractMessageContent(message, options) { const content = {}; if (!options.parseMessageContent) { return content; } // Text message if (message.conversation) { content.messageType = 'text'; content.text = message.conversation; } // Extended text message (URLs, links, ads) else if (message.extendedTextMessage) { const extMsg = message.extendedTextMessage; // Check if this is a Facebook Ads conversion if (extMsg.contextInfo?.conversionSource === 'FB_Ads') { content.messageType = 'ads'; content.text = extMsg.text; content.conversionSource = extMsg.contextInfo.conversionSource; content.conversionData = extMsg.contextInfo.conversionData; // Extract external ad reply info if present if (extMsg.contextInfo.externalAdReply) { content.adInfo = { title: extMsg.contextInfo.externalAdReply.title, body: extMsg.contextInfo.externalAdReply.body, mediaType: extMsg.contextInfo.externalAdReply.mediaType, thumbnailUrl: extMsg.contextInfo.externalAdReply.thumbnailUrl, mediaUrl: extMsg.contextInfo.externalAdReply.mediaUrl, sourceUrl: extMsg.contextInfo.externalAdReply.sourceUrl, sourceType: extMsg.contextInfo.externalAdReply.sourceType, sourceId: extMsg.contextInfo.externalAdReply.sourceId }; } } else { // Regular URL/link message content.messageType = 'url'; content.text = extMsg.text; if (extMsg.title) content.linkTitle = extMsg.title; if (extMsg.description) content.linkDescription = extMsg.description; if (extMsg.matchedText) content.linkUrl = extMsg.matchedText; if (extMsg.JPEGThumbnail) content.linkThumbnail = extMsg.JPEGThumbnail; } // Always include contextInfo if present for debugging if (extMsg.contextInfo) { content.contextInfo = extMsg.contextInfo; } } // Audio message (voice note) else if (message.audioMessage) { content.messageType = message.audioMessage.PTT ? 'ptt' : 'audio'; content.audioUrl = message.audioMessage.URL; content.duration = message.audioMessage.seconds; content.mimeType = message.audioMessage.mimetype; } // Image message else if (message.imageMessage) { content.messageType = 'image'; if (message.imageMessage.caption) content.caption = message.imageMessage.caption; if (message.imageMessage.URL) content.imageUrl = message.imageMessage.URL; if (message.imageMessage.mimetype) content.mimeType = message.imageMessage.mimetype; if (message.imageMessage.JPEGThumbnail) content.thumbnail = message.imageMessage.JPEGThumbnail; } // Video message else if (message.videoMessage) { content.messageType = 'video'; if (message.videoMessage.caption) content.caption = message.videoMessage.caption; if (message.videoMessage.URL) content.videoUrl = message.videoMessage.URL; if (message.videoMessage.mimetype) content.mimeType = message.videoMessage.mimetype; if (message.videoMessage.seconds) content.duration = message.videoMessage.seconds; if (message.videoMessage.JPEGThumbnail) content.thumbnail = message.videoMessage.JPEGThumbnail; } // Document message else if (message.documentMessage) { content.messageType = 'document'; if (message.documentMessage.fileName) content.fileName = message.documentMessage.fileName; if (message.documentMessage.mimetype) content.mimeType = message.documentMessage.mimetype; if (message.documentMessage.URL) content.documentUrl = message.documentMessage.URL; if (message.documentMessage.title) content.title = message.documentMessage.title; } // Sticker message else if (message.stickerMessage) { content.messageType = 'sticker'; if (message.stickerMessage.URL) content.stickerUrl = message.stickerMessage.URL; if (message.stickerMessage.mimetype) content.mimeType = message.stickerMessage.mimetype; } // Location message else if (message.locationMessage) { content.messageType = 'location'; content.latitude = message.locationMessage.degreesLatitude; content.longitude = message.locationMessage.degreesLongitude; if (message.locationMessage.name) content.locationName = message.locationMessage.name; if (message.locationMessage.address) content.locationAddress = message.locationMessage.address; if (message.locationMessage.JPEGThumbnail) content.thumbnail = message.locationMessage.JPEGThumbnail; } // Contact message else if (message.contactMessage) { content.messageType = 'contact'; content.contactName = message.contactMessage.displayName; content.vcard = message.contactMessage.vcard; } // Contacts array message else if (message.contactsArrayMessage) { content.messageType = 'contacts'; content.contactName = message.contactsArrayMessage.displayName; content.contacts = message.contactsArrayMessage.contacts; } // Buttons message else if (message.buttonsMessage) { content.messageType = 'buttons'; content.text = message.buttonsMessage.contentText; content.headerText = message.buttonsMessage.headerText; content.footerText = message.buttonsMessage.footerText; content.buttons = message.buttonsMessage.buttons?.map((btn) => ({ id: btn.buttonID, text: btn.buttonText?.displayText, type: btn.type })); } // List message else if (message.listMessage) { content.messageType = 'list'; content.title = message.listMessage.title; content.text = message.listMessage.description; content.buttonText = message.listMessage.buttonText; content.footerText = message.listMessage.footerText; content.sections = message.listMessage.sections?.map((section) => ({ title: section.title, rows: section.rows?.map((row) => ({ id: row.rowID, title: row.title, description: row.description })) })); } // Template message else if (message.templateMessage) { content.messageType = 'template'; content.templateContent = message.templateMessage; } // Interactive response message else if (message.interactiveResponseMessage) { content.messageType = 'interactiveResponse'; content.responseType = message.interactiveResponseMessage.body?.text; content.responseData = message.interactiveResponseMessage; } // Interactive message (new type from examples) else if (message.interactiveMessage) { content.messageType = 'interactive'; content.interactiveData = message.interactiveMessage; // Extract payment info if present if (message.interactiveMessage.InteractiveMessage?.NativeFlowMessage?.buttons) { const paymentButton = message.interactiveMessage.InteractiveMessage.NativeFlowMessage.buttons.find((btn) => btn.name === 'payment_info'); if (paymentButton?.buttonParamsJSON) { try { content.paymentInfo = JSON.parse(paymentButton.buttonParamsJSON); } catch (e) { content.paymentInfoRaw = paymentButton.buttonParamsJSON; } } } } // Poll message (all versions) else if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) { content.messageType = 'poll'; const pollData = message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3; content.pollName = pollData.name; content.options = pollData.options?.map((opt) => opt.optionName || opt); content.selectableCount = pollData.selectableOptionsCount; content.pollData = pollData; } // Event message (new type from examples) else if (message.eventMessage) { content.messageType = 'event'; content.eventName = message.eventMessage.name; content.eventDescription = message.eventMessage.description; content.startTime = message.eventMessage.startTime; content.endTime = message.eventMessage.endTime; content.isCanceled = message.eventMessage.isCanceled; content.extraGuestsAllowed = message.eventMessage.extraGuestsAllowed; if (message.eventMessage.joinLink) content.joinLink = message.eventMessage.joinLink; if (message.eventMessage.location) { content.eventLocation = { name: message.eventMessage.location.name, latitude: message.eventMessage.location.degreesLatitude, longitude: message.eventMessage.location.degreesLongitude }; } } // Product message (new type from examples) else if (message.productMessage) { content.messageType = 'product'; content.businessOwner = message.productMessage.businessOwnerJID; if (message.productMessage.product) { const product = message.productMessage.product; content.productId = product.productID; content.productTitle = product.title; content.productDescription = product.description; content.currency = product.currencyCode; content.price = product.priceAmount1000 / 1000; // Convert from 1000-based content.retailerId = product.retailerID; content.productUrl = product.URL; if (product.signedURL) content.signedUrl = product.signedURL; if (product.productImage) { content.productImage = { url: product.productImage.URL, thumbnail: product.productImage.JPEGThumbnail }; } } } // Order message else if (message.orderMessage) { content.messageType = 'order'; content.orderId = message.orderMessage.orderID || message.orderMessage.orderId; content.orderStatus = message.orderMessage.orderStatus || message.orderMessage.status; content.orderData = message.orderMessage; } // For any other message type not handled above else { // Try to detect message type from available fields const messageKeys = Object.keys(message); if (messageKeys.length > 0) { // Use the first key that ends with "Message" as the type const messageTypeKey = messageKeys.find(key => key.endsWith('Message')); if (messageTypeKey) { content.messageType = messageTypeKey.replace('Message', ''); content.messageData = message[messageTypeKey]; } else { // If no standard message type found, mark as unknown but include data content.messageType = 'unknown'; content.messageData = message; } } } // Always include message context info if present if (message.messageContextInfo) { content.contextInfo = message.messageContextInfo; } return content; } async webhook() { const bodyData = this.getBodyData(); const events = this.getNodeParameter('events'); const filters = this.getNodeParameter('filters'); const options = this.getNodeParameter('options'); // Check if the event type matches const eventType = String(bodyData.type || ''); const allowUnknown = options.allowUnknownEvents; // Process event if: // 1. Event type is in selected events list // 2. "All" is selected in events // 3. allowUnknownEvents is true if (!events.includes(eventType) && !events.includes('All') && !allowUnknown) { return { workflowData: [], }; } // Apply token filter first if (filters.tokenFilter && bodyData.token !== filters.tokenFilter) { return { workflowData: [] }; } // Get event info for message events const eventInfo = bodyData.event?.Info; // Apply filters for Message events if (eventType === 'Message' && eventInfo) { // Phone number filter (Sender) if (filters.fromPhone && eventInfo.Sender !== filters.fromPhone) { return { workflowData: [] }; } // Chat filter if (filters.chatId && eventInfo.Chat !== filters.chatId) { return { workflowData: [] }; } // Message type filter if (filters.messageType) { // First check MediaType field const mediaType = eventInfo.MediaType || eventInfo.Type || ''; // For complex message types, we need to check the actual message content const message = bodyData.event?.Message; let actualMessageType = mediaType; // Detect actual message type from content if (message) { if (message.conversation) actualMessageType = 'text'; else if (message.extendedTextMessage) { // Check if it's a Facebook Ads conversion if (message.extendedTextMessage.contextInfo?.conversionSource === 'FB_Ads') { actualMessageType = 'ads'; } else { actualMessageType = 'url'; } } else if (message.audioMessage?.PTT) actualMessageType = 'ptt'; else if (message.audioMessage) actualMessageType = 'audio'; else if (message.imageMessage) actualMessageType = 'image'; else if (message.videoMessage) actualMessageType = 'video'; else if (message.documentMessage) actualMessageType = 'document'; else if (message.stickerMessage) actualMessageType = 'sticker'; else if (message.locationMessage) actualMessageType = 'location'; else if (message.contactMessage) actualMessageType = 'contact'; else if (message.contactsArrayMessage) actualMessageType = 'contacts'; else if (message.buttonsMessage) actualMessageType = 'buttons'; else if (message.listMessage) actualMessageType = 'list'; else if (message.templateMessage) actualMessageType = 'template'; else if (message.interactiveResponseMessage) actualMessageType = 'interactiveResponse'; else if (message.interactiveMessage) actualMessageType = 'interactive'; else if (message.pollCreationMessage || message.pollCreationMessageV2 || message.pollCreationMessageV3) actualMessageType = 'poll'; else if (message.eventMessage) actualMessageType = 'event'; else if (message.productMessage) actualMessageType = 'product'; else if (message.orderMessage) actualMessageType = 'order'; else if (mediaType === 'location') actualMessageType = 'location'; else if (mediaType === 'product') actualMessageType = 'product'; } if (filters.messageType !== actualMessageType) { return { workflowData: [] }; } } // Text content filter if (filters.containsText) { const message = bodyData.event?.Message; let textContent = ''; // Extract text from different message types if (message?.conversation) { textContent = message.conversation; } else if (message?.extendedTextMessage?.text) { textContent = message.extendedTextMessage.text; } else if (message?.buttonsMessage?.contentText) { textContent = message.buttonsMessage.contentText; } else if (message?.listMessage) { textContent = `${message.listMessage.title || ''} ${message.listMessage.description || ''}`; } else if (message?.templateMessage?.hydratedTemplate?.hydratedContentText) { textContent = message.templateMessage.hydratedTemplate.hydratedContentText; } if (!textContent.toLowerCase().includes(String(filters.containsText).toLowerCase())) { return { workflowData: [] }; } } // Group/Direct filter if (filters.isGroup !== 'any') { const isGroup = eventInfo.IsGroup; if (filters.isGroup === 'true' && !isGroup) { return { workflowData: [] }; } if (filters.isGroup === 'false' && isGroup) { return { workflowData: [] }; } } // From me filter if (filters.isFromMe !== 'any') { const isFromMe = eventInfo.IsFromMe; if (filters.isFromMe === 'true' && !isFromMe) { return { workflowData: [] }; } if (filters.isFromMe === 'false' && isFromMe) { return { workflowData: [] }; } } } // Process the data let outputData = {}; if (options.simplifyOutput) { // Create simplified output structure outputData = { eventType: eventType, token: bodyData.token, timestamp: new Date().toISOString(), }; // Handle Message events if (eventType === 'Message' && eventInfo) { outputData.messageId = eventInfo.ID; outputData.chat = eventInfo.Chat; outputData.sender = eventInfo.Sender; outputData.timestamp = eventInfo.Timestamp; outputData.messageType = eventInfo.Type; outputData.mediaType = eventInfo.MediaType; outputData.isFromMe = eventInfo.IsFromMe; outputData.isGroup = eventInfo.IsGroup; outputData.pushName = eventInfo.PushName; if (eventInfo.VerifiedName) { outputData.verifiedName = eventInfo.VerifiedName.Details?.verifiedName; } // Extract message content const messageContent = WuzapiTrigger.extractMessageContent(bodyData.event?.Message, options); Object.assign(outputData, messageContent); // Special handling for Facebook Ads conversions if (messageContent.messageType === 'ads') { outputData.isAdsConversion = true; outputData.adsSource = messageContent.conversionSource; // Try to parse conversion data if it's JSON if (messageContent.conversionData) { try { outputData.adsConversionData = JSON.parse(String(messageContent.conversionData)); } catch (e) { outputData.adsConversionDataRaw = messageContent.conversionData; } } } // Include media data if present and requested if (options.includeMediaData) { if (bodyData.base64) outputData.mediaBase64 = bodyData.base64; if (bodyData.mimeType) outputData.mediaMimeType = bodyData.mimeType; if (bodyData.fileName) outputData.mediaFileName = bodyData.fileName; if (bodyData.s3) outputData.s3Data = bodyData.s3; } } // Handle Facebook Ads Conversion events else if (eventType === 'FBAdsConversion') { const adsEvent = bodyData.event; outputData.conversionSource = adsEvent?.conversionSource; outputData.conversionData = adsEvent?.conversionData; outputData.chat = adsEvent?.Chat; outputData.sender = adsEvent?.Sender; outputData.timestamp = adsEvent?.Timestamp; // Try to parse conversion data if (adsEvent?.conversionData) { try { outputData.parsedConversionData = JSON.parse(adsEvent.conversionData); } catch (e) { outputData.rawConversionData = adsEvent.conversionData; } } } // Handle ReadReceipt events else if (eventType === 'ReadReceipt') { const receiptEvent = bodyData.event; outputData.state = bodyData.state; outputData.chat = receiptEvent?.Chat; outputData.sender = receiptEvent?.Sender; outputData.timestamp = receiptEvent?.Timestamp; outputData.messageIds = receiptEvent?.MessageIDs; outputData.messageSender = receiptEvent?.MessageSender; } // Handle ChatPresence events else if (eventType === 'ChatPresence') { const presenceEvent = bodyData.event; outputData.chat = presenceEvent?.Chat; outputData.sender = presenceEvent?.Sender; outputData.state = presenceEvent?.State; outputData.media = presenceEvent?.Media; } // Handle Presence events else if (eventType === 'Presence') { const presenceEvent = bodyData.event; outputData.from = presenceEvent?.From; outputData.unavailable = presenceEvent?.Unavailable; outputData.lastSeen = presenceEvent?.LastSeen; } // Handle Order events else if (eventType === 'Order') { const orderEvent = bodyData.event; outputData.orderId = orderEvent?.ID; outputData.orderStatus = orderEvent?.Status; outputData.orderDetails = orderEvent; } // Handle Interaction events else if (eventType === 'Interaction') { const interactionEvent = bodyData.event; outputData.interactionType = interactionEvent?.Type; outputData.interactionData = interactionEvent; } // Handle Reaction events else if (eventType === 'Reaction') { const reactionEvent = bodyData.event; outputData.reaction = reactionEvent?.Reaction; outputData.messageId = reactionEvent?.MessageID; outputData.reactionData = reactionEvent; } // Handle Call events else if (eventType === 'Call') { const callEvent = bodyData.event; outputData.callType = callEvent?.Type; outputData.callStatus = callEvent?.Status; outputData.callData = callEvent; } // Handle unknown/other event types else { // For unknown events, include all available data outputData.unknownEventType = eventType; outputData.isUnknownEvent = true; // Copy event data directly for other types if (bodyData.event) { outputData.eventData = bodyData.event; // Try to extract common fields from unknown events const event = bodyData.event; if (event.Info) { outputData.chat = event.Info.Chat;