n8n-nodes-chatflow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/credentials/ChatflowApi.credentials.ts +30 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/nodes/ChatflowMessage.node.d.ts +5 -0
- package/dist/nodes/ChatflowMessage.node.js +135 -0
- package/dist/nodes/chatflow.png +2 -0
- package/dist/nodes/chatflow.svg +11 -0
- package/index.ts +2 -0
- package/nodes/ChatflowMessage.node.ts +135 -0
- package/nodes/chatflow.png +0 -0
- package/package.json +39 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
|
|
3
|
+
export class ChatflowApi implements ICredentialType {
|
|
4
|
+
name = 'chatflowApi';
|
|
5
|
+
displayName = 'Chatflow API';
|
|
6
|
+
documentationUrl = '';
|
|
7
|
+
|
|
8
|
+
properties: INodeProperties[] = [
|
|
9
|
+
{
|
|
10
|
+
displayName: 'Token',
|
|
11
|
+
name: 'token',
|
|
12
|
+
type: 'string',
|
|
13
|
+
typeOptions: { password: true },
|
|
14
|
+
default: '',
|
|
15
|
+
required: true,
|
|
16
|
+
description: 'API token from Chatflow → Api access',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// Optional basic test just to ensure base URL is reachable
|
|
21
|
+
// Token не проверяем, так как для ping он не нужен
|
|
22
|
+
test = {
|
|
23
|
+
request: {
|
|
24
|
+
baseURL: 'https://app.chatflow.kz/',
|
|
25
|
+
url: 'ping',
|
|
26
|
+
method: 'GET',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class ChatflowMessage implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChatflowMessage = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class ChatflowMessage {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Chatflow',
|
|
9
|
+
name: 'chatflow',
|
|
10
|
+
icon: 'file:chatflow.png',
|
|
11
|
+
group: ['output'],
|
|
12
|
+
version: 1,
|
|
13
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
14
|
+
description: 'Send WhatsApp messages via Chatflow API',
|
|
15
|
+
defaults: { name: 'Chatflow Message' },
|
|
16
|
+
inputs: [
|
|
17
|
+
{ type: 'main' },
|
|
18
|
+
],
|
|
19
|
+
outputs: [
|
|
20
|
+
{ type: 'main' },
|
|
21
|
+
],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'chatflowApi',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
properties: [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Operation',
|
|
31
|
+
name: 'operation',
|
|
32
|
+
type: 'options',
|
|
33
|
+
options: [
|
|
34
|
+
{ name: 'Text', value: 'text' },
|
|
35
|
+
{ name: 'Audio', value: 'audio' },
|
|
36
|
+
{ name: 'Video', value: 'video' },
|
|
37
|
+
{ name: 'Document', value: 'document' },
|
|
38
|
+
{ name: 'Image', value: 'image' },
|
|
39
|
+
],
|
|
40
|
+
default: 'text',
|
|
41
|
+
},
|
|
42
|
+
{ displayName: 'Instance ID', name: 'instance_id', type: 'string', default: '', required: true },
|
|
43
|
+
{ displayName: 'JID', name: 'jid', type: 'string', default: '', required: true },
|
|
44
|
+
// Text
|
|
45
|
+
{ displayName: 'Message', name: 'msg', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['text'] } } },
|
|
46
|
+
// Audio
|
|
47
|
+
{ displayName: 'Audio URL', name: 'audiourl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['audio'] } } },
|
|
48
|
+
// Video
|
|
49
|
+
{ displayName: 'Video URL', name: 'videourl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['video'] } } },
|
|
50
|
+
{ displayName: 'Caption', name: 'caption', type: 'string', default: '', displayOptions: { show: { operation: ['video'] } } },
|
|
51
|
+
// Document
|
|
52
|
+
{ displayName: 'Document URL', name: 'docurl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['document'] } } },
|
|
53
|
+
{ displayName: 'Caption', name: 'captionDoc', type: 'string', default: '', displayOptions: { show: { operation: ['document'] } } },
|
|
54
|
+
// Image
|
|
55
|
+
{ displayName: 'Image URL', name: 'imageurl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['image'] } } },
|
|
56
|
+
{ displayName: 'Caption', name: 'captionImg', type: 'string', default: '', displayOptions: { show: { operation: ['image'] } } },
|
|
57
|
+
{
|
|
58
|
+
displayName: 'Continue On Fail',
|
|
59
|
+
name: 'continueOnFail',
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
default: true,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async execute() {
|
|
67
|
+
const items = this.getInputData();
|
|
68
|
+
const out = [];
|
|
69
|
+
const baseUrl = 'https://app.chatflow.kz/';
|
|
70
|
+
const credentials = await this.getCredentials('chatflowApi');
|
|
71
|
+
const token = credentials.token;
|
|
72
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
73
|
+
const continueOnFail = this.getNodeParameter('continueOnFail', 0);
|
|
74
|
+
const endpointMap = {
|
|
75
|
+
text: 'api/v1/send-text',
|
|
76
|
+
audio: 'api/v1/send-audio',
|
|
77
|
+
video: 'api/v1/send-video',
|
|
78
|
+
document: 'api/v1/send-doc',
|
|
79
|
+
image: 'api/v1/send-image',
|
|
80
|
+
};
|
|
81
|
+
const urlJoin = (a, b) => a.replace(/\/+$/, '') + '/' + b.replace(/^\/+/, '');
|
|
82
|
+
for (let i = 0; i < items.length; i++) {
|
|
83
|
+
try {
|
|
84
|
+
const instance_id = this.getNodeParameter('instance_id', i);
|
|
85
|
+
const jid = this.getNodeParameter('jid', i);
|
|
86
|
+
const qs = { token, instance_id, jid };
|
|
87
|
+
switch (operation) {
|
|
88
|
+
case 'text':
|
|
89
|
+
qs.msg = this.getNodeParameter('msg', i);
|
|
90
|
+
break;
|
|
91
|
+
case 'audio':
|
|
92
|
+
qs.audiourl = this.getNodeParameter('audiourl', i);
|
|
93
|
+
break;
|
|
94
|
+
case 'video':
|
|
95
|
+
qs.videourl = this.getNodeParameter('videourl', i);
|
|
96
|
+
const capV = this.getNodeParameter('caption', i, '');
|
|
97
|
+
if (capV)
|
|
98
|
+
qs.caption = capV;
|
|
99
|
+
break;
|
|
100
|
+
case 'document':
|
|
101
|
+
qs.docurl = this.getNodeParameter('docurl', i);
|
|
102
|
+
const capD = this.getNodeParameter('captionDoc', i, '');
|
|
103
|
+
if (capD)
|
|
104
|
+
qs.caption = capD;
|
|
105
|
+
break;
|
|
106
|
+
case 'image':
|
|
107
|
+
qs.imageurl = this.getNodeParameter('imageurl', i);
|
|
108
|
+
const capI = this.getNodeParameter('captionImg', i, '');
|
|
109
|
+
if (capI)
|
|
110
|
+
qs.caption = capI;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
const options = {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
url: urlJoin(baseUrl, endpointMap[operation]),
|
|
116
|
+
qs,
|
|
117
|
+
headers: { Accept: 'application/json' },
|
|
118
|
+
timeout: 10000,
|
|
119
|
+
retry: { times: 1, interval: 500 },
|
|
120
|
+
};
|
|
121
|
+
const responseData = await this.helpers.httpRequest(options);
|
|
122
|
+
out.push({ json: responseData });
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (continueOnFail) {
|
|
126
|
+
out.push({ json: { success: false, message: error.message || 'Request failed' } });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [out];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.ChatflowMessage = ChatflowMessage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<rect width="512" height="512" rx="32" fill="#111"/>
|
|
3
|
+
<g fill="#fff" transform="translate(76,140)">
|
|
4
|
+
<rect x="0" y="32" width="72" height="72" rx="8"/>
|
|
5
|
+
<rect x="96" y="128" width="72" height="72" rx="8"/>
|
|
6
|
+
<rect x="96" y="0" width="72" height="72" rx="8"/>
|
|
7
|
+
<rect x="192" y="96" width="72" height="72" rx="8"/>
|
|
8
|
+
<path d="M72 68h32v32H72zM168 36h32v32h-32zM168 164h32v32h-32zM240 132h32v32h-32z" fill="#111" opacity="0.35"/>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
11
|
+
|
package/index.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
import { NodeOperationError } from 'n8n-workflow';
|
|
3
|
+
|
|
4
|
+
type MessageType = 'text' | 'audio' | 'video' | 'document' | 'image';
|
|
5
|
+
|
|
6
|
+
export class ChatflowMessage implements INodeType {
|
|
7
|
+
description: INodeTypeDescription = {
|
|
8
|
+
displayName: 'Chatflow',
|
|
9
|
+
name: 'chatflow',
|
|
10
|
+
icon: 'file:chatflow.png',
|
|
11
|
+
group: ['output'],
|
|
12
|
+
version: 1,
|
|
13
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
14
|
+
description: 'Send WhatsApp messages via Chatflow API',
|
|
15
|
+
defaults: { name: 'Chatflow Message' },
|
|
16
|
+
inputs: [
|
|
17
|
+
{ type: 'main' as any },
|
|
18
|
+
],
|
|
19
|
+
outputs: [
|
|
20
|
+
{ type: 'main' as any },
|
|
21
|
+
],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'chatflowApi',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
properties: [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Operation',
|
|
31
|
+
name: 'operation',
|
|
32
|
+
type: 'options',
|
|
33
|
+
options: [
|
|
34
|
+
{ name: 'Text', value: 'text' },
|
|
35
|
+
{ name: 'Audio', value: 'audio' },
|
|
36
|
+
{ name: 'Video', value: 'video' },
|
|
37
|
+
{ name: 'Document', value: 'document' },
|
|
38
|
+
{ name: 'Image', value: 'image' },
|
|
39
|
+
],
|
|
40
|
+
default: 'text',
|
|
41
|
+
},
|
|
42
|
+
{ displayName: 'Instance ID', name: 'instance_id', type: 'string', default: '', required: true },
|
|
43
|
+
{ displayName: 'JID', name: 'jid', type: 'string', default: '', required: true },
|
|
44
|
+
|
|
45
|
+
// Text
|
|
46
|
+
{ displayName: 'Message', name: 'msg', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['text'] } } },
|
|
47
|
+
|
|
48
|
+
// Audio
|
|
49
|
+
{ displayName: 'Audio URL', name: 'audiourl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['audio'] } } },
|
|
50
|
+
|
|
51
|
+
// Video
|
|
52
|
+
{ displayName: 'Video URL', name: 'videourl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['video'] } } },
|
|
53
|
+
{ displayName: 'Caption', name: 'caption', type: 'string', default: '', displayOptions: { show: { operation: ['video'] } } },
|
|
54
|
+
|
|
55
|
+
// Document
|
|
56
|
+
{ displayName: 'Document URL', name: 'docurl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['document'] } } },
|
|
57
|
+
{ displayName: 'Caption', name: 'captionDoc', type: 'string', default: '', displayOptions: { show: { operation: ['document'] } } },
|
|
58
|
+
|
|
59
|
+
// Image
|
|
60
|
+
{ displayName: 'Image URL', name: 'imageurl', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['image'] } } },
|
|
61
|
+
{ displayName: 'Caption', name: 'captionImg', type: 'string', default: '', displayOptions: { show: { operation: ['image'] } } },
|
|
62
|
+
|
|
63
|
+
{
|
|
64
|
+
displayName: 'Continue On Fail',
|
|
65
|
+
name: 'continueOnFail',
|
|
66
|
+
type: 'boolean',
|
|
67
|
+
default: true,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
73
|
+
const items = this.getInputData();
|
|
74
|
+
const out: INodeExecutionData[] = [];
|
|
75
|
+
|
|
76
|
+
const baseUrl = 'https://app.chatflow.kz/';
|
|
77
|
+
const credentials = await this.getCredentials('chatflowApi');
|
|
78
|
+
const token = (credentials as any).token as string;
|
|
79
|
+
const operation = this.getNodeParameter('operation', 0) as MessageType;
|
|
80
|
+
const continueOnFail = this.getNodeParameter('continueOnFail', 0) as boolean;
|
|
81
|
+
|
|
82
|
+
const endpointMap: Record<MessageType, string> = {
|
|
83
|
+
text: 'api/v1/send-text',
|
|
84
|
+
audio: 'api/v1/send-audio',
|
|
85
|
+
video: 'api/v1/send-video',
|
|
86
|
+
document: 'api/v1/send-doc',
|
|
87
|
+
image: 'api/v1/send-image',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const urlJoin = (a: string, b: string) => a.replace(/\/+$/, '') + '/' + b.replace(/^\/+/, '');
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < items.length; i++) {
|
|
93
|
+
try {
|
|
94
|
+
const instance_id = this.getNodeParameter('instance_id', i) as string;
|
|
95
|
+
const jid = this.getNodeParameter('jid', i) as string;
|
|
96
|
+
|
|
97
|
+
const qs: Record<string, string> = { token, instance_id, jid };
|
|
98
|
+
switch (operation) {
|
|
99
|
+
case 'text': qs.msg = this.getNodeParameter('msg', i) as string; break;
|
|
100
|
+
case 'audio': qs.audiourl = this.getNodeParameter('audiourl', i) as string; break;
|
|
101
|
+
case 'video':
|
|
102
|
+
qs.videourl = this.getNodeParameter('videourl', i) as string;
|
|
103
|
+
const capV = this.getNodeParameter('caption', i, '') as string; if (capV) qs.caption = capV; break;
|
|
104
|
+
case 'document':
|
|
105
|
+
qs.docurl = this.getNodeParameter('docurl', i) as string;
|
|
106
|
+
const capD = this.getNodeParameter('captionDoc', i, '') as string; if (capD) qs.caption = capD; break;
|
|
107
|
+
case 'image':
|
|
108
|
+
qs.imageurl = this.getNodeParameter('imageurl', i) as string;
|
|
109
|
+
const capI = this.getNodeParameter('captionImg', i, '') as string; if (capI) qs.caption = capI; break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const options = {
|
|
113
|
+
method: 'GET',
|
|
114
|
+
url: urlJoin(baseUrl, endpointMap[operation]),
|
|
115
|
+
qs,
|
|
116
|
+
headers: { Accept: 'application/json' },
|
|
117
|
+
timeout: 10000,
|
|
118
|
+
retry: { times: 1, interval: 500 },
|
|
119
|
+
} as unknown as any;
|
|
120
|
+
|
|
121
|
+
const responseData = await this.helpers.httpRequest(options);
|
|
122
|
+
out.push({ json: responseData });
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (continueOnFail) {
|
|
125
|
+
out.push({ json: { success: false, message: (error as Error).message || 'Request failed' } });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
throw new NodeOperationError(this.getNode(), error as Error, { itemIndex: i });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return [out];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-chatflow",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n community node to send WhatsApp messages via Chatflow (text, audio, video, document, image)",
|
|
5
|
+
"author": "Chatflow",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"n8n-community-node-package",
|
|
9
|
+
"n8n-nodes",
|
|
10
|
+
"chatflow",
|
|
11
|
+
"whatsapp",
|
|
12
|
+
"messaging"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": ""
|
|
17
|
+
},
|
|
18
|
+
"n8n": {
|
|
19
|
+
"nodes": [
|
|
20
|
+
"dist/nodes/ChatflowMessage.node.js"
|
|
21
|
+
],
|
|
22
|
+
"credentials": [
|
|
23
|
+
"dist/credentials/ChatflowApi.credentials.js"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json && cpx \"nodes/*.{svg,png}\" dist/nodes || true",
|
|
30
|
+
"clean": "rimraf dist"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^18.19.34",
|
|
34
|
+
"cpx": "^1.5.0",
|
|
35
|
+
"n8n-workflow": "^1.42.0",
|
|
36
|
+
"rimraf": "^5.0.5",
|
|
37
|
+
"typescript": "^5.4.5"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2019",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": ".",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["nodes/**/*.ts", "index.ts"],
|
|
13
|
+
"exclude": ["dist", "node_modules"]
|
|
14
|
+
}
|
|
15
|
+
|