UNPKG

n8n-nodes-walichat

Version:

n8n plugin for WaliChat

636 lines (635 loc) 22.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WaliChatTrigger = void 0; const axios_1 = __importDefault(require("axios")); const request_1 = require("../request"); const clientRuntime = process.env.N8N_RUNTIME_CLIENT || 'n8n'; const sampleWebhookEvents = [ { id: 'inbound-text', type: 'text', name: 'On text message received', event: 'message:in:new' }, { id: 'outbound-text', type: 'text', name: 'On text message sent', event: 'message:out:new' }, { id: 'inbound-image', type: 'image', name: 'On image message received', event: 'message:in:new' }, { id: 'outbound-image', type: 'image', name: 'On image message sent', event: 'message:out:new' }, { id: 'inbound-video', type: 'video', name: 'On video message received', event: 'message:in:new' }, { id: 'outbound-video', type: 'video', name: 'On video message sent', event: 'message:out:new' }, { id: 'inbound-audio', type: 'audio', name: 'On audio message received', event: 'message:in:new' }, { id: 'outbound-audio', type: 'audio', name: 'On audio message sent', event: 'message:out:new' }, { id: 'inbound-document', type: 'document', name: 'On document message received', event: 'message:in:new' }, { id: 'outbound-document', type: 'document', name: 'On document message sent', event: 'message:out:new' }, { id: 'inbound-contacts', type: 'contacts', name: 'On contacts message received', event: 'message:in:new' }, { id: 'outbound-contacts', type: 'contacts', name: 'On contacts message sent', event: 'message:out:new' }, { id: 'inbound-location', type: 'location', name: 'On location message received', event: 'message:in:new' }, { id: 'outbound-location', type: 'location', name: 'On location message sent', event: 'message:out:new' }, { id: 'inbound-buttons', type: 'buttons', name: 'On buttons message received', event: 'message:in:new' }, { id: 'outbound-buttons', type: 'buttons', name: 'On buttons message sent', event: 'message:out:new' }, { id: 'inbound-buttons-response', type: 'buttons', subtype: 'response', name: 'On inbound buttons reply message', event: 'message:in:new' }, { id: 'inbound-list', type: 'list', name: 'On list message received', event: 'message:in:new' }, { id: 'outbound-list', type: 'list', name: 'On outbound message sent', event: 'message:out:new' }, { id: 'inbound-list-response', type: 'list', subtype: 'response', name: 'On inbound list response message', event: 'message:in:new' }, { id: 'inbound-product', type: 'catalog', name: 'On product catalog message received', event: 'message:in:new' }, { id: 'outbound-product', type: 'catalog', name: 'On product catalog message sent', event: 'message:out:new' }, { id: 'inbound-order', type: 'order', name: 'On order catalog message received', event: 'message:in:new' }, { id: 'outbound-order', type: 'order', name: 'On order catalog message sent', event: 'message:out:new' }, { id: 'inbound-reaction', type: 'reaction', name: 'On inbound message with reaction', event: 'message:reaction' }, { id: 'outbound-reaction', type: 'reaction', name: 'On outbound message with reaction', event: 'message:reaction' }, { id: 'inbound-reaction-deleted', type: 'reaction', name: 'Inbound message reaction was deleted', event: 'message:reaction' }, { id: 'inbound-poll', type: 'poll', name: 'On poll message received', event: 'message:in:new' }, { id: 'inbound-event', type: 'event', name: 'On group event message received', event: 'message:in:new' }, { id: 'inbound-link-preview', type: 'link-preview', name: 'On message with link preview received', event: 'message:in:new' }, { id: 'outbound-link-preview', type: 'link-preview', name: 'On message with link preview sent', event: 'message:out:new' }, { id: 'inbound-quoted', type: 'quoted', name: 'On quoted message received', event: 'message:in:new' }, { id: 'outbound-quoted', type: 'quoted', name: 'On quoted message sent', event: 'message:out:new' }, { id: 'outbound-read', type: 'text', name: 'When a sent message is read the user', event: 'message:out:ack' }, { id: 'outbound-played', type: 'audio', name: 'When a sent audio message is played the user', event: 'message:out:ack' }, { id: 'autoreply', type: 'text', name: 'Auto reply message is sent to the user', event: 'message:out:new' }, { id: 'group-inbound', type: 'group-inbound', name: 'On group message received', event: 'message:in:new' }, { id: 'group-outbound', type: 'group-outbound', name: 'On group message sent', event: 'message:out:new' }, { id: 'inbound-group-mentions', type: 'group-mentions', name: 'On group mentions message received', event: 'message:in:new' }, { id: 'outbound-failed', type: 'outbound-failed', name: 'Outbound messages failed', event: 'message:out:failed' }, { id: 'chat-update', type: 'chat', name: 'Chat is updated', event: 'chat:update' }, { id: 'contact-update', type: 'chat', name: 'Contact updated', event: 'contact:update' }, { id: 'message-update-poll', type: 'poll', name: 'Message update on new poll votes', event: 'message:update' }, { id: 'message-update-event', type: 'event', name: 'Message update on new event attendees', event: 'message:update' }, { id: 'message-update-edit', type: 'text', name: 'Message content is edited', event: 'message:update' }, { id: 'group-update-add', type: 'group-update', name: 'Group participants added', event: 'group:update', }, { id: 'group-update-remove', type: 'group-update', name: 'Group participants removed', event: 'group:update', }, { id: 'group-update-subject', type: 'group-update', name: 'Group subject updated', event: 'group:update', }, { id: 'group-update-restrict', type: 'group-update', name: 'Group edit permissions restricted', event: 'group:update', }, { id: 'group-update-restrict-off', type: 'group-update', name: 'Group edit permissions extended', event: 'group:update', }, { id: 'group-update-announce', type: 'group-update', name: 'Group send messages permissions restricted', event: 'group:update', }, { id: 'group-update-announce-off', type: 'group-update', name: 'Group edit permissions extended', event: 'group:update', }, { id: 'status-update', type: 'text', name: 'On user status with text', event: 'status:update' }, { id: 'status-update-image', type: 'image', name: 'On user status with image', event: 'status:update' }, { id: 'status-update-video', type: 'video', name: 'On user status with video', event: 'status:update' }, { id: 'channel-message', type: 'channel', name: 'WhatsApp Channel\'s new inbound image message', event: 'channel:in' }, { id: 'channel-image', type: 'image', name: 'WhatsApp Channel\'s new inbound image message', event: 'channel:in' }, { id: 'number-session', type: 'text', name: 'WhatsApp number session status update', event: 'number:session' }, ]; class WaliChatTrigger { constructor() { this.description = { displayName: 'WaliChat Trigger', name: 'walichatTrigger', icon: 'file:icon.png', group: ['trigger'], version: 1, subtitle: '={{$parameter["event"]}}', description: 'Starts the workflow when WaliChat events occur', defaults: { name: 'WaliChat Trigger', }, inputs: [], outputs: ['main'], credentials: [ { name: 'walichatApiKey', required: true, }, ], webhooks: [ { name: 'default', httpMethod: 'POST', responseMode: 'onReceived', path: 'webhook', }, ], properties: [ { displayName: 'Webhook name', name: 'webhookName', type: 'string', default: 'Automations', required: true, description: 'Webhook internal name for your reference (max 30 characters)', typeOptions: { maxLength: 30, }, }, { displayName: 'WhatsApp number', name: 'device', type: 'options', default: '', required: false, description: 'Restrict webhook events to a specific WhatsApp number (optional)', typeOptions: { loadOptionsMethod: 'getDevices', }, }, { displayName: 'Events', name: 'events', type: 'multiOptions', default: [], required: true, description: 'The events to subscribe to', options: [ { name: 'New Inbound Message Received', value: 'message:in:new' }, { name: 'New Outbound Message Delivered', value: 'message:out:new' }, { name: 'Message Read or Played by User', value: 'message:out:ack' }, { name: 'Message Delivery Failed', value: 'message:out:failed' }, { name: 'Group Updated', value: 'group:update' }, { name: 'Message Updated (Poll Votes, Meeting Events)', value: 'message:update' }, { name: 'Message Reaction Added/Removed', value: 'message:reaction' }, { name: 'New User Status Published', value: 'status:update' }, { name: 'New Channel Message Received', value: 'channel:in' }, { name: 'Chat Updated (Assigned, Resolved, etc.)', value: 'chat:update' }, { name: 'WhatsApp Session Changes', value: 'number:session' }, { name: 'Contact Information Updated', value: 'contact:update' }, ], }, { displayName: 'Sample event to notify', name: 'sampleEvent', type: 'options', required: false, default: '', description: 'Select a sample event that matches your use case to test and configure the webhook JSON data structure in the next steps of your workflow.', typeOptions: { loadOptionsMethod: 'getSampleEvents', loadOptionsDependsOn: ['events'], }, } ], }; this.methods = { loadOptions: { async getDevices() { const credentials = await this.getCredentials('walichatApiKey'); const apiKey = credentials.walichatApiKey; try { const response = await (0, request_1.rawRequest)({ url: '/devices?size=100' }, apiKey); if (Array.isArray(response.data) && response.data.length > 0) { const devices = response.data.map((device) => ({ name: `${device.alias || 'Unnamed'} (${device.phone || 'No phone yet'})`, value: device.id, })); if (clientRuntime) { return devices; } return [ { name: 'All WhatsApp numbers in your account', value: '', }, ...devices, ]; } if (clientRuntime) { return [ { name: 'No WhatsApp numbers available: connect one or upgrade plan ', value: 'invalid', } ]; } return [{ name: 'All WhatsApp numbers in your account', value: '' }]; } catch (error) { return [{ name: 'All WhatsApp numbers in your account', value: '' }]; } }, async getSampleEvents() { const selectedEvents = this.getNodeParameter('events', []); if (!selectedEvents || selectedEvents.length === 0) { return []; } // Filter sample events based on selected events const filteredSampleEvents = sampleWebhookEvents.filter(sample => selectedEvents.includes(sample.event)); return filteredSampleEvents.map(sample => ({ name: sample.name, value: sample.id, })); }, }, }; this.webhookMethods = { default: { async checkExists() { const webhookUrl = this.getNodeWebhookUrl('default'); const nodeData = this.getWorkflowStaticData('node'); const webhookId = nodeData.webhookId; if (!webhookId) { return false; } const credentials = await this.getCredentials('walichatApiKey'); const apiKey = credentials.walichatApiKey; const workflowId = this.getWorkflow().id; const sample = this.getNodeParameter('sampleEvent', '') || 'inbound-text'; try { const response = await (0, request_1.rawRequest)({ url: `/webhooks/${webhookId}?workflow=${workflowId}&sample=${sample}` }, apiKey); return response.data.url === webhookUrl; } catch (error) { // Delete all workflow specific webhooks try { await (0, request_1.rawRequest)({ url: `/webhooks/600f1c2a9b3d4e5f6a7b8c00?workflow=${workflowId}`, method: 'DELETE' }, apiKey); } catch (error) { console.error(`Error checking WaliChat webhook existence:`, error.message); } if (axios_1.default.isAxiosError(error) && error.response && error.response.status === 404) { return false; } throw error; } }, async create() { var _a; const webhookUrl = this.getNodeWebhookUrl('default'); const webhookName = this.getNodeParameter('webhookName'); const device = this.getNodeParameter('device', ''); const events = this.getNodeParameter('events', []); // Get the n8n workflow ID const workflowId = this.getWorkflow().id; if (!webhookUrl) { throw new Error('No webhook URL available for registration'); } if (events.length === 0) { throw new Error('At least one event must be selected'); } try { const credentials = await this.getCredentials('walichatApiKey'); const apiKey = credentials.walichatApiKey; const sample = this.getNodeParameter('sampleEvent', '') || 'inbound-text'; const url = `${webhookUrl}?workflow=${workflowId}`; const prefix = clientRuntime.replace('_', ''); const requestBody = { name: `${prefix}: ${webhookName}`, url, events, }; if (device) { requestBody.device = device; } const response = await (0, request_1.rawRequest)({ url: '/webhooks', method: 'POST', data: requestBody, params: { sample }, headers: { 'Content-Type': 'application/json', }, }, apiKey); if (response.data && response.data.id) { const nodeData = this.getWorkflowStaticData('node'); nodeData.webhookId = response.data.id; return true; } return false; } catch (error) { if (axios_1.default.isAxiosError(error) && error.response) { if (error.response.status === 409) { return true; } throw new Error(`WaliChat webhook registration failed: ${((_a = error.response.data) === null || _a === void 0 ? void 0 : _a.message) || error.message}`); } throw error; } }, async delete() { var _a; const nodeData = this.getWorkflowStaticData('node'); const webhookId = nodeData.webhookId; if (!webhookId) { return true; } try { const credentials = await this.getCredentials('walichatApiKey'); const apiKey = credentials.walichatApiKey; const workflowId = this.getWorkflow().id; await (0, request_1.rawRequest)({ url: `/webhooks/${webhookId}?workflow=${workflowId}`, method: 'DELETE', headers: { 'Content-Type': 'application/json' }, }, apiKey); return true; } catch (error) { if (axios_1.default.isAxiosError(error) && error.response && error.response.status === 404) { return true; } if (axios_1.default.isAxiosError(error) && error.response) { console.error(`WaliChat webhook deletion failed: ${((_a = error.response.data) === null || _a === void 0 ? void 0 : _a.message) || error.message}`); } else { console.error('Error deleting WaliChat webhook:', error); } return true; } }, }, }; } // This method is called when a webhook request is received async webhook() { const req = this.getRequestObject(); const body = req.body; // Return the data return { workflowData: [this.helpers.returnJsonArray(body)], }; } } exports.WaliChatTrigger = WaliChatTrigger;