n8n-nodes-walichat
Version:
n8n plugin for WaliChat
636 lines (635 loc) • 22.2 kB
JavaScript
;
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;