n8n-nodes-signal-cli-rest-api 0.2.1 → 0.2.3
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/README.md +1 -1
- package/dist/credentials/SignalApi.credentials.d.ts +9 -0
- package/dist/credentials/SignalApi.credentials.js +53 -0
- package/dist/nodes/Signal/Signal.node.d.ts +5 -0
- package/dist/nodes/Signal/Signal.node.js +324 -0
- package/dist/nodes/Signal/SignalTrigger.node.d.ts +5 -0
- package/dist/nodes/Signal/SignalTrigger.node.js +152 -0
- package/dist/nodes/Signal/contacts.d.ts +9 -0
- package/dist/nodes/Signal/contacts.js +44 -0
- package/dist/nodes/Signal/groups.d.ts +12 -0
- package/dist/nodes/Signal/groups.js +61 -0
- package/dist/nodes/Signal/messages.d.ts +15 -0
- package/dist/nodes/Signal/messages.js +82 -0
- package/dist/nodes/Signal/tools/messageHistory.d.ts +10 -0
- package/dist/nodes/Signal/tools/messageHistory.js +58 -0
- package/package.json +22 -11
- package/.editorconfig +0 -20
- package/.eslintrc.js +0 -146
- package/.eslintrc.prepublish.js +0 -16
- package/.prettierrc.js +0 -51
- package/CODE_OF_CONDUCT.md +0 -76
- /package/{credentials → dist/credentials}/signal.svg +0 -0
- /package/{nodes → dist/nodes}/Signal/signal.svg +0 -0
package/README.md
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ICredentialType, INodeProperties, ICredentialTestRequest, Icon } from 'n8n-workflow';
|
|
2
|
+
export declare class SignalApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: Icon;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SignalApi = void 0;
|
|
4
|
+
class SignalApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'signalApi';
|
|
7
|
+
this.displayName = 'Signal API';
|
|
8
|
+
this.icon = 'file:signal.svg';
|
|
9
|
+
this.documentationUrl = 'https://github.com/bbernhard/signal-cli-rest-api';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'API URL',
|
|
13
|
+
name: 'apiUrl',
|
|
14
|
+
type: 'string',
|
|
15
|
+
default: 'http://localhost:8080',
|
|
16
|
+
placeholder: 'http://your-truenas-ip:8085',
|
|
17
|
+
description: 'The URL of your signal-cli-rest-api instance',
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
displayName: 'API Token',
|
|
22
|
+
name: 'apiToken',
|
|
23
|
+
type: 'string',
|
|
24
|
+
typeOptions: { password: true },
|
|
25
|
+
default: '',
|
|
26
|
+
description: 'Optional API token for authentication (set in docker-compose)',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
displayName: 'Phone Number',
|
|
30
|
+
name: 'phoneNumber',
|
|
31
|
+
type: 'string',
|
|
32
|
+
default: '',
|
|
33
|
+
placeholder: '+1234567890',
|
|
34
|
+
description: 'Phone number registered with Signal (with country code)',
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
this.test = {
|
|
39
|
+
request: {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
url: '={{$credentials.apiUrl}}/v1/about',
|
|
42
|
+
headers: {
|
|
43
|
+
// The Authorization header uses n8n expression syntax for runtime interpolation
|
|
44
|
+
...(typeof '{{ $credentials.apiToken }}' === 'string' && '{{ $credentials.apiToken }}'
|
|
45
|
+
? { Authorization: 'Bearer {{ $credentials.apiToken }}' }
|
|
46
|
+
: {}),
|
|
47
|
+
},
|
|
48
|
+
timeout: 5000,
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.SignalApi = SignalApi;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Signal = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const messages_1 = require("./messages");
|
|
6
|
+
const groups_1 = require("./groups");
|
|
7
|
+
const contacts_1 = require("./contacts");
|
|
8
|
+
class Signal {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.description = {
|
|
11
|
+
displayName: 'Signal',
|
|
12
|
+
name: 'signal',
|
|
13
|
+
icon: 'file:signal.svg',
|
|
14
|
+
group: ['output'],
|
|
15
|
+
version: 1,
|
|
16
|
+
description: 'Interact with Signal via signal-cli-rest-api',
|
|
17
|
+
defaults: {
|
|
18
|
+
name: 'Signal',
|
|
19
|
+
},
|
|
20
|
+
inputs: ['main'],
|
|
21
|
+
outputs: ['main'],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'signalApi',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
properties: [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Operation',
|
|
31
|
+
name: 'operation',
|
|
32
|
+
type: 'options',
|
|
33
|
+
noDataExpression: true,
|
|
34
|
+
default: '',
|
|
35
|
+
options: [
|
|
36
|
+
{
|
|
37
|
+
name: 'Messages: Send Message',
|
|
38
|
+
value: 'sendMessage',
|
|
39
|
+
description: 'Send a text message to a contact or group',
|
|
40
|
+
action: 'Send a text message',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Messages: Send Attachment',
|
|
44
|
+
value: 'sendAttachment',
|
|
45
|
+
description: 'Send a file or image to a contact or group',
|
|
46
|
+
action: 'Send an attachment',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Messages: Send Reaction',
|
|
50
|
+
value: 'sendReaction',
|
|
51
|
+
description: 'Send a reaction (emoji) to a message',
|
|
52
|
+
action: 'Send a reaction',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Messages: Remove Reaction',
|
|
56
|
+
value: 'removeReaction',
|
|
57
|
+
description: 'Remove a reaction from a message',
|
|
58
|
+
action: 'Remove a reaction',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Contacts: Get Contacts',
|
|
62
|
+
value: 'getContacts',
|
|
63
|
+
description: 'Get the list of contacts for the account',
|
|
64
|
+
action: 'Get contacts',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Groups: Get Groups',
|
|
68
|
+
value: 'getGroups',
|
|
69
|
+
description: 'Get the list of groups for the account',
|
|
70
|
+
action: 'Get groups',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Groups: Create Group',
|
|
74
|
+
value: 'createGroup',
|
|
75
|
+
description: 'Create a new Signal group',
|
|
76
|
+
action: 'Create a group',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'Groups: Update Group',
|
|
80
|
+
value: 'updateGroup',
|
|
81
|
+
description: 'Update a Signal group’s name or members',
|
|
82
|
+
action: 'Update a group',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
displayName: 'Recipient',
|
|
88
|
+
name: 'recipient',
|
|
89
|
+
type: 'string',
|
|
90
|
+
default: '',
|
|
91
|
+
placeholder: '+1234567890 or groupId',
|
|
92
|
+
description: 'Phone number or group ID to send the message, attachment, or reaction to',
|
|
93
|
+
required: true,
|
|
94
|
+
displayOptions: {
|
|
95
|
+
show: {
|
|
96
|
+
operation: ['sendMessage', 'sendAttachment', 'sendReaction', 'removeReaction'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
displayName: 'Message',
|
|
102
|
+
name: 'message',
|
|
103
|
+
type: 'string',
|
|
104
|
+
default: '',
|
|
105
|
+
description: 'The text message to send (optional for attachments)',
|
|
106
|
+
displayOptions: {
|
|
107
|
+
show: {
|
|
108
|
+
operation: ['sendMessage', 'sendAttachment'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Attachment URL',
|
|
114
|
+
name: 'attachmentUrl',
|
|
115
|
+
type: 'string',
|
|
116
|
+
default: '',
|
|
117
|
+
placeholder: 'https://example.com/image.jpg',
|
|
118
|
+
description: 'URL of the file or image to send (e.g., PNG, JPG, PDF, MP3 for voice notes)',
|
|
119
|
+
required: true,
|
|
120
|
+
displayOptions: {
|
|
121
|
+
show: {
|
|
122
|
+
operation: ['sendAttachment'],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
displayName: 'Group ID',
|
|
128
|
+
name: 'groupId',
|
|
129
|
+
type: 'string',
|
|
130
|
+
default: '',
|
|
131
|
+
placeholder: 'group.XXXXXXXXXXXXXXXXXXXXXXXXXX==',
|
|
132
|
+
description: 'ID of the group to update',
|
|
133
|
+
required: true,
|
|
134
|
+
displayOptions: {
|
|
135
|
+
show: {
|
|
136
|
+
operation: ['updateGroup'],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
displayName: 'Group Name',
|
|
142
|
+
name: 'groupName',
|
|
143
|
+
type: 'string',
|
|
144
|
+
default: '',
|
|
145
|
+
description: 'Name of the new or updated group',
|
|
146
|
+
required: false,
|
|
147
|
+
displayOptions: {
|
|
148
|
+
show: {
|
|
149
|
+
operation: ['createGroup', 'updateGroup'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Group Members',
|
|
155
|
+
name: 'groupMembers',
|
|
156
|
+
type: 'string',
|
|
157
|
+
default: '',
|
|
158
|
+
placeholder: '+1234567890,+0987654321',
|
|
159
|
+
description: 'Comma-separated list of phone numbers to add to the group',
|
|
160
|
+
required: false,
|
|
161
|
+
displayOptions: {
|
|
162
|
+
show: {
|
|
163
|
+
operation: ['createGroup', 'updateGroup'],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Emoji',
|
|
169
|
+
name: 'emoji',
|
|
170
|
+
type: 'options',
|
|
171
|
+
default: '👍',
|
|
172
|
+
description: 'Emoji to send as a reaction (select or enter custom emoji)',
|
|
173
|
+
required: true,
|
|
174
|
+
typeOptions: {
|
|
175
|
+
allowCustom: true,
|
|
176
|
+
},
|
|
177
|
+
options: [
|
|
178
|
+
{
|
|
179
|
+
name: 'Thumbs Up',
|
|
180
|
+
value: '👍',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'Heart',
|
|
184
|
+
value: '❤️',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'Smile',
|
|
188
|
+
value: '😄',
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'Sad',
|
|
192
|
+
value: '😢',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'Angry',
|
|
196
|
+
value: '😣',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'Star',
|
|
200
|
+
value: '⭐',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'Fire',
|
|
204
|
+
value: '🔥',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'Plus',
|
|
208
|
+
value: '➕',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'Minus',
|
|
212
|
+
value: '➖',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'Handshake',
|
|
216
|
+
value: '🤝',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
displayOptions: {
|
|
220
|
+
show: {
|
|
221
|
+
operation: ['sendReaction'],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
displayName: 'Target Author',
|
|
227
|
+
name: 'targetAuthor',
|
|
228
|
+
type: 'string',
|
|
229
|
+
default: '',
|
|
230
|
+
placeholder: '+1234567890',
|
|
231
|
+
description: 'Phone number of the message author to react to',
|
|
232
|
+
required: true,
|
|
233
|
+
displayOptions: {
|
|
234
|
+
show: {
|
|
235
|
+
operation: ['sendReaction', 'removeReaction'],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
displayName: 'Target Message Timestamp',
|
|
241
|
+
name: 'targetSentTimestamp',
|
|
242
|
+
type: 'number',
|
|
243
|
+
default: 0,
|
|
244
|
+
description: 'Timestamp of the message to react to (in milliseconds)',
|
|
245
|
+
required: true,
|
|
246
|
+
displayOptions: {
|
|
247
|
+
show: {
|
|
248
|
+
operation: ['sendReaction', 'removeReaction'],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
displayName: 'Timeout (seconds)',
|
|
254
|
+
name: 'timeout',
|
|
255
|
+
type: 'number',
|
|
256
|
+
default: 60,
|
|
257
|
+
description: 'Request timeout in seconds (set higher for Get Groups, e.g., 300)',
|
|
258
|
+
displayOptions: {
|
|
259
|
+
show: {
|
|
260
|
+
operation: ['sendMessage', 'sendAttachment', 'sendReaction', 'removeReaction', 'getContacts', 'getGroups', 'createGroup', 'updateGroup'],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
typeOptions: {
|
|
264
|
+
minValue: 1,
|
|
265
|
+
maxValue: 600,
|
|
266
|
+
},
|
|
267
|
+
hint: 'Increase for slow operations like Get Groups (recommended: 300 for Get Groups)',
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
async execute() {
|
|
273
|
+
const items = this.getInputData();
|
|
274
|
+
const returnData = [];
|
|
275
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
276
|
+
const credentials = await this.getCredentials('signalApi');
|
|
277
|
+
const apiUrl = credentials.apiUrl;
|
|
278
|
+
const apiToken = credentials.apiToken;
|
|
279
|
+
const phoneNumber = credentials.phoneNumber;
|
|
280
|
+
this.logger.debug(`Signal: Starting execute for operation ${operation}, apiUrl: ${apiUrl}, items length: ${items.length}`);
|
|
281
|
+
for (let i = 0; i < items.length; i++) {
|
|
282
|
+
const timeout = this.getNodeParameter('timeout', i, operation === 'getGroups' ? 300 : 60) * 1000;
|
|
283
|
+
const params = {
|
|
284
|
+
recipient: this.getNodeParameter('recipient', i, ''),
|
|
285
|
+
message: this.getNodeParameter('message', i, ''),
|
|
286
|
+
attachmentUrl: this.getNodeParameter('attachmentUrl', i, ''),
|
|
287
|
+
groupId: this.getNodeParameter('groupId', i, ''),
|
|
288
|
+
groupName: this.getNodeParameter('groupName', i, ''),
|
|
289
|
+
groupMembers: this.getNodeParameter('groupMembers', i, ''),
|
|
290
|
+
emoji: this.getNodeParameter('emoji', i, ''),
|
|
291
|
+
targetAuthor: this.getNodeParameter('targetAuthor', i, ''),
|
|
292
|
+
targetSentTimestamp: this.getNodeParameter('targetSentTimestamp', i, 0),
|
|
293
|
+
timeout,
|
|
294
|
+
apiUrl,
|
|
295
|
+
apiToken,
|
|
296
|
+
phoneNumber,
|
|
297
|
+
};
|
|
298
|
+
try {
|
|
299
|
+
let result;
|
|
300
|
+
if (['sendMessage', 'sendAttachment', 'sendReaction', 'removeReaction'].includes(operation)) {
|
|
301
|
+
result = await messages_1.executeMessagesOperation.call(this, operation, i, params);
|
|
302
|
+
}
|
|
303
|
+
else if (['getGroups', 'createGroup', 'updateGroup'].includes(operation)) {
|
|
304
|
+
result = await groups_1.executeGroupsOperation.call(this, operation, i, params);
|
|
305
|
+
}
|
|
306
|
+
else if (operation === 'getContacts') {
|
|
307
|
+
result = await contacts_1.executeContactsOperation.call(this, operation, i, params);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Unknown operation' });
|
|
311
|
+
}
|
|
312
|
+
this.logger.info(`Signal: Operation ${operation} result for item ${i}: ${JSON.stringify(result.json, null, 2)}`);
|
|
313
|
+
returnData.push(result);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
this.logger.error(`Signal: Error in operation ${operation} for item ${i}`, { error });
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
this.logger.debug(`Signal: Returning data length: ${returnData.length}`);
|
|
321
|
+
return [returnData];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
exports.Signal = Signal;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SignalTrigger = void 0;
|
|
4
|
+
const ws_1 = require("ws");
|
|
5
|
+
class SignalTrigger {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'Signal Trigger',
|
|
9
|
+
name: 'signalTrigger',
|
|
10
|
+
icon: 'file:signal.svg',
|
|
11
|
+
group: ['trigger'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'Triggers on new Signal messages via signal-cli-rest-api WebSocket',
|
|
14
|
+
defaults: {
|
|
15
|
+
name: 'Signal Trigger',
|
|
16
|
+
},
|
|
17
|
+
inputs: [],
|
|
18
|
+
outputs: ['main'],
|
|
19
|
+
credentials: [
|
|
20
|
+
{
|
|
21
|
+
name: 'signalApi',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
properties: [
|
|
26
|
+
{
|
|
27
|
+
displayName: 'Reconnect Delay (seconds)',
|
|
28
|
+
name: 'reconnectDelay',
|
|
29
|
+
type: 'number',
|
|
30
|
+
default: 5,
|
|
31
|
+
description: 'Delay before reconnecting on close (in seconds)',
|
|
32
|
+
typeOptions: {
|
|
33
|
+
minValue: 1,
|
|
34
|
+
maxValue: 60,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: 'Only With Text',
|
|
39
|
+
name: 'onlyWithText',
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: true,
|
|
42
|
+
description: 'Retrieve only messages with text content',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: 'Only With Attachments',
|
|
46
|
+
name: 'onlyWithAttachments',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
default: false,
|
|
49
|
+
description: 'Retrieve only messages with attachments',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
displayName: 'Only With Reactions',
|
|
53
|
+
name: 'onlyWithReactions',
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
default: false,
|
|
56
|
+
description: 'Retrieve only messages with reactions',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async trigger() {
|
|
62
|
+
const credentials = await this.getCredentials('signalApi');
|
|
63
|
+
const apiUrl = credentials.apiUrl;
|
|
64
|
+
const apiToken = credentials.apiToken;
|
|
65
|
+
const phoneNumber = credentials.phoneNumber;
|
|
66
|
+
const reconnectDelay = this.getNodeParameter('reconnectDelay', 0) * 1000;
|
|
67
|
+
const onlyWithText = this.getNodeParameter('onlyWithText', 0);
|
|
68
|
+
const onlyWithAttachments = this.getNodeParameter('onlyWithAttachments', 0);
|
|
69
|
+
const onlyWithReactions = this.getNodeParameter('onlyWithReactions', 0);
|
|
70
|
+
const wsUrl = `${apiUrl.replace('http', 'ws')}/v1/receive/${phoneNumber}`;
|
|
71
|
+
const processedMessages = new Set();
|
|
72
|
+
const maxMessages = 1000;
|
|
73
|
+
const connectWebSocket = () => {
|
|
74
|
+
const ws = new ws_1.WebSocket(wsUrl, {
|
|
75
|
+
headers: apiToken ? { Authorization: `Bearer ${apiToken}` } : {},
|
|
76
|
+
});
|
|
77
|
+
ws.on('message', (data) => {
|
|
78
|
+
var _a, _b, _c, _d, _e;
|
|
79
|
+
try {
|
|
80
|
+
const message = JSON.parse(data.toString());
|
|
81
|
+
this.logger.debug(`SignalTrigger: Received message: ${JSON.stringify(message, null, 2)}`);
|
|
82
|
+
const timestamp = ((_a = message.envelope) === null || _a === void 0 ? void 0 : _a.timestamp) || 0;
|
|
83
|
+
if (processedMessages.has(timestamp)) {
|
|
84
|
+
this.logger.debug(`SignalTrigger: Skipping duplicate message with timestamp ${timestamp}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (processedMessages.size >= maxMessages) {
|
|
88
|
+
processedMessages.clear();
|
|
89
|
+
}
|
|
90
|
+
processedMessages.add(timestamp);
|
|
91
|
+
const dataMsg = ((_b = message.envelope) === null || _b === void 0 ? void 0 : _b.dataMessage) || ((_d = (_c = message.envelope) === null || _c === void 0 ? void 0 : _c.syncMessage) === null || _d === void 0 ? void 0 : _d.sentMessage) || {};
|
|
92
|
+
const processedMessage = {
|
|
93
|
+
messageText: dataMsg.message || '',
|
|
94
|
+
attachments: dataMsg.attachments || [],
|
|
95
|
+
reactions: dataMsg.reactions || [],
|
|
96
|
+
sourceNumber: ((_e = message.envelope) === null || _e === void 0 ? void 0 : _e.sourceNumber) || '',
|
|
97
|
+
timestamp: timestamp,
|
|
98
|
+
account: message.account || '',
|
|
99
|
+
};
|
|
100
|
+
// Ігнорувати події без вмісту
|
|
101
|
+
if (!processedMessage.messageText &&
|
|
102
|
+
processedMessage.attachments.length === 0 &&
|
|
103
|
+
processedMessage.reactions.length === 0) {
|
|
104
|
+
this.logger.debug(`SignalTrigger: Skipping empty message with timestamp ${timestamp}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Фільтрація за параметрами
|
|
108
|
+
if ((onlyWithText && !processedMessage.messageText) ||
|
|
109
|
+
(onlyWithAttachments && processedMessage.attachments.length === 0) ||
|
|
110
|
+
(onlyWithReactions && processedMessage.reactions.length === 0)) {
|
|
111
|
+
this.logger.debug(`SignalTrigger: Skipping filtered message with timestamp ${timestamp}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const returnData = {
|
|
115
|
+
json: processedMessage,
|
|
116
|
+
};
|
|
117
|
+
this.emit([this.helpers.returnJsonArray([returnData])]);
|
|
118
|
+
this.logger.debug(`SignalTrigger: Emitted message with timestamp ${timestamp}`);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.logger.error('SignalTrigger: Error parsing message', { error });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
ws.on('error', (error) => {
|
|
125
|
+
this.logger.error('SignalTrigger: WebSocket error', { error });
|
|
126
|
+
setTimeout(connectWebSocket, reconnectDelay);
|
|
127
|
+
});
|
|
128
|
+
ws.on('close', (code, reason) => {
|
|
129
|
+
this.logger.debug(`SignalTrigger: WebSocket closed with code ${code}, reason: ${reason.toString()}`);
|
|
130
|
+
setTimeout(connectWebSocket, reconnectDelay);
|
|
131
|
+
});
|
|
132
|
+
return ws;
|
|
133
|
+
};
|
|
134
|
+
const ws = connectWebSocket();
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
ws.on('open', () => {
|
|
137
|
+
this.logger.debug(`SignalTrigger: Connected to ${wsUrl}`);
|
|
138
|
+
resolve({
|
|
139
|
+
closeFunction: async () => {
|
|
140
|
+
ws.close();
|
|
141
|
+
this.logger.debug('SignalTrigger: WebSocket closed');
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
ws.on('error', (error) => {
|
|
146
|
+
this.logger.error('SignalTrigger: WebSocket connection failed', { error });
|
|
147
|
+
setTimeout(connectWebSocket, reconnectDelay);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.SignalTrigger = SignalTrigger;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
2
|
+
interface OperationParams {
|
|
3
|
+
timeout: number;
|
|
4
|
+
apiUrl: string;
|
|
5
|
+
apiToken: string;
|
|
6
|
+
phoneNumber: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function executeContactsOperation(this: IExecuteFunctions, operation: string, itemIndex: number, params: OperationParams): Promise<INodeExecutionData>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeContactsOperation = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
async function executeContactsOperation(operation, itemIndex, params) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const { timeout, apiUrl, apiToken, phoneNumber } = params;
|
|
12
|
+
const axiosConfig = {
|
|
13
|
+
headers: apiToken ? { Authorization: `Bearer ${apiToken}` } : {},
|
|
14
|
+
timeout,
|
|
15
|
+
};
|
|
16
|
+
const retryRequest = async (request, retries = 2, delay = 5000) => {
|
|
17
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
return await request();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (attempt === retries)
|
|
23
|
+
throw error;
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
if (operation === 'getContacts') {
|
|
30
|
+
const response = await retryRequest(() => axios_1.default.get(`${apiUrl}/v1/contacts/${phoneNumber}`, axiosConfig));
|
|
31
|
+
return { json: response.data, pairedItem: { item: itemIndex } };
|
|
32
|
+
}
|
|
33
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Unknown operation' });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const axiosError = error;
|
|
37
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
38
|
+
message: axiosError.message,
|
|
39
|
+
description: (((_b = (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) || axiosError.message),
|
|
40
|
+
httpCode: ((_d = (_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === null || _d === void 0 ? void 0 : _d.toString()) || 'unknown',
|
|
41
|
+
}, { itemIndex });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.executeContactsOperation = executeContactsOperation;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
2
|
+
interface OperationParams {
|
|
3
|
+
groupName?: string;
|
|
4
|
+
groupMembers?: string;
|
|
5
|
+
groupId?: string;
|
|
6
|
+
timeout: number;
|
|
7
|
+
apiUrl: string;
|
|
8
|
+
apiToken: string;
|
|
9
|
+
phoneNumber: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function executeGroupsOperation(this: IExecuteFunctions, operation: string, itemIndex: number, params: OperationParams): Promise<INodeExecutionData>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeGroupsOperation = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
async function executeGroupsOperation(operation, itemIndex, params) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const { groupName, groupMembers, groupId, timeout, apiUrl, apiToken, phoneNumber } = params;
|
|
12
|
+
const axiosConfig = {
|
|
13
|
+
headers: apiToken ? { Authorization: `Bearer ${apiToken}` } : {},
|
|
14
|
+
timeout,
|
|
15
|
+
};
|
|
16
|
+
const retryRequest = async (request, retries = 2, delay = 5000) => {
|
|
17
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
return await request();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (attempt === retries)
|
|
23
|
+
throw error;
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
if (operation === 'getGroups') {
|
|
30
|
+
const response = await retryRequest(() => axios_1.default.get(`${apiUrl}/v1/groups/${phoneNumber}`, axiosConfig));
|
|
31
|
+
return { json: response.data, pairedItem: { item: itemIndex } };
|
|
32
|
+
}
|
|
33
|
+
else if (operation === 'createGroup') {
|
|
34
|
+
const members = (groupMembers === null || groupMembers === void 0 ? void 0 : groupMembers.split(',').map(member => member.trim())) || [];
|
|
35
|
+
const response = await retryRequest(() => axios_1.default.post(`${apiUrl}/v1/groups/${phoneNumber}`, {
|
|
36
|
+
name: groupName,
|
|
37
|
+
members,
|
|
38
|
+
}, axiosConfig));
|
|
39
|
+
return { json: response.data, pairedItem: { item: itemIndex } };
|
|
40
|
+
}
|
|
41
|
+
else if (operation === 'updateGroup') {
|
|
42
|
+
const body = {};
|
|
43
|
+
if (groupName)
|
|
44
|
+
body.name = groupName;
|
|
45
|
+
if (groupMembers)
|
|
46
|
+
body.members = groupMembers.split(',').map(member => member.trim());
|
|
47
|
+
const response = await retryRequest(() => axios_1.default.put(`${apiUrl}/v1/groups/${phoneNumber}/${groupId}`, body, axiosConfig));
|
|
48
|
+
return { json: response.data, pairedItem: { item: itemIndex } };
|
|
49
|
+
}
|
|
50
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Unknown operation' });
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const axiosError = error;
|
|
54
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
55
|
+
message: axiosError.message,
|
|
56
|
+
description: (((_b = (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) || axiosError.message),
|
|
57
|
+
httpCode: ((_d = (_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === null || _d === void 0 ? void 0 : _d.toString()) || 'unknown',
|
|
58
|
+
}, { itemIndex });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.executeGroupsOperation = executeGroupsOperation;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
2
|
+
interface OperationParams {
|
|
3
|
+
recipient?: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
attachmentUrl?: string;
|
|
6
|
+
emoji?: string;
|
|
7
|
+
targetAuthor?: string;
|
|
8
|
+
targetSentTimestamp?: number;
|
|
9
|
+
timeout: number;
|
|
10
|
+
apiUrl: string;
|
|
11
|
+
apiToken: string;
|
|
12
|
+
phoneNumber: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function executeMessagesOperation(this: IExecuteFunctions, operation: string, itemIndex: number, params: OperationParams): Promise<INodeExecutionData>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeMessagesOperation = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
async function executeMessagesOperation(operation, itemIndex, params) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const { recipient, message, attachmentUrl, emoji, targetAuthor, targetSentTimestamp, timeout, apiUrl, apiToken, phoneNumber } = params;
|
|
12
|
+
const axiosConfig = {
|
|
13
|
+
headers: apiToken ? { Authorization: `Bearer ${apiToken}` } : {},
|
|
14
|
+
timeout,
|
|
15
|
+
};
|
|
16
|
+
const retryRequest = async (request, retries = 2, delay = 5000) => {
|
|
17
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
return await request();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (attempt === retries)
|
|
23
|
+
throw error;
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
let response;
|
|
30
|
+
if (operation === 'sendMessage') {
|
|
31
|
+
response = await retryRequest(() => axios_1.default.post(`${apiUrl}/v1/send`, {
|
|
32
|
+
message,
|
|
33
|
+
number: phoneNumber,
|
|
34
|
+
recipients: [recipient],
|
|
35
|
+
}, axiosConfig));
|
|
36
|
+
this.logger.debug(`Signal messages: sendMessage response: ${JSON.stringify(response.data, null, 2)}`);
|
|
37
|
+
return { json: response.data || { status: 'Message sent' }, pairedItem: { item: itemIndex } };
|
|
38
|
+
}
|
|
39
|
+
else if (operation === 'sendAttachment') {
|
|
40
|
+
response = await retryRequest(() => axios_1.default.post(`${apiUrl}/v1/send`, {
|
|
41
|
+
message,
|
|
42
|
+
number: phoneNumber,
|
|
43
|
+
recipients: [recipient],
|
|
44
|
+
attachments: [attachmentUrl],
|
|
45
|
+
}, axiosConfig));
|
|
46
|
+
this.logger.debug(`Signal messages: sendAttachment response: ${JSON.stringify(response.data, null, 2)}`);
|
|
47
|
+
return { json: response.data || { status: 'Attachment sent' }, pairedItem: { item: itemIndex } };
|
|
48
|
+
}
|
|
49
|
+
else if (operation === 'sendReaction') {
|
|
50
|
+
response = await retryRequest(() => axios_1.default.post(`${apiUrl}/v1/reactions/${phoneNumber}`, {
|
|
51
|
+
reaction: emoji,
|
|
52
|
+
recipient,
|
|
53
|
+
target_author: targetAuthor,
|
|
54
|
+
timestamp: targetSentTimestamp,
|
|
55
|
+
}, axiosConfig));
|
|
56
|
+
this.logger.debug(`Signal messages: sendReaction response: ${JSON.stringify(response.data, null, 2)}`);
|
|
57
|
+
return { json: response.data || { status: 'Reaction sent' }, pairedItem: { item: itemIndex } };
|
|
58
|
+
}
|
|
59
|
+
else if (operation === 'removeReaction') {
|
|
60
|
+
response = await retryRequest(() => axios_1.default.delete(`${apiUrl}/v1/reactions/${phoneNumber}`, {
|
|
61
|
+
...axiosConfig,
|
|
62
|
+
data: {
|
|
63
|
+
recipient,
|
|
64
|
+
target_author: targetAuthor,
|
|
65
|
+
timestamp: targetSentTimestamp,
|
|
66
|
+
},
|
|
67
|
+
}));
|
|
68
|
+
this.logger.debug(`Signal messages: removeReaction response: ${JSON.stringify(response.data, null, 2)}`);
|
|
69
|
+
return { json: response.data || { status: 'Reaction removed' }, pairedItem: { item: itemIndex } };
|
|
70
|
+
}
|
|
71
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Unknown operation' });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const axiosError = error;
|
|
75
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
76
|
+
message: axiosError.message,
|
|
77
|
+
description: (((_b = (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) || axiosError.message),
|
|
78
|
+
httpCode: ((_d = (_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === null || _d === void 0 ? void 0 : _d.toString()) || 'unknown',
|
|
79
|
+
}, { itemIndex });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.executeMessagesOperation = executeMessagesOperation;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
2
|
+
interface MessageHistoryParams {
|
|
3
|
+
timeout: number;
|
|
4
|
+
fullHistory: boolean;
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
apiToken: string;
|
|
7
|
+
phoneNumber: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function getMessageHistory(this: IExecuteFunctions, params: MessageHistoryParams): Promise<INodeExecutionData[]>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getMessageHistory = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
async function getMessageHistory(params) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const { timeout, fullHistory, apiUrl, apiToken, phoneNumber } = params;
|
|
12
|
+
const axiosConfig = {
|
|
13
|
+
headers: apiToken ? { Authorization: `Bearer ${apiToken}` } : {},
|
|
14
|
+
timeout,
|
|
15
|
+
};
|
|
16
|
+
const retryRequest = async (request, retries = 2, delay = 5000) => {
|
|
17
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
return await request();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (attempt === retries)
|
|
23
|
+
throw error;
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
try {
|
|
29
|
+
const url = fullHistory
|
|
30
|
+
? `${apiUrl}/v1/receive/${phoneNumber}`
|
|
31
|
+
: `${apiUrl}/v1/receive/${phoneNumber}?timeout=${timeout / 1000}`;
|
|
32
|
+
const response = await retryRequest(() => axios_1.default.get(url, axiosConfig));
|
|
33
|
+
const messages = Array.isArray(response.data) ? response.data : [];
|
|
34
|
+
return messages.map((message, index) => {
|
|
35
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
36
|
+
return ({
|
|
37
|
+
json: {
|
|
38
|
+
messageText: ((_b = (_a = message.envelope) === null || _a === void 0 ? void 0 : _a.dataMessage) === null || _b === void 0 ? void 0 : _b.message) || '',
|
|
39
|
+
attachments: ((_d = (_c = message.envelope) === null || _c === void 0 ? void 0 : _c.dataMessage) === null || _d === void 0 ? void 0 : _d.attachments) || [],
|
|
40
|
+
reactions: ((_f = (_e = message.envelope) === null || _e === void 0 ? void 0 : _e.dataMessage) === null || _f === void 0 ? void 0 : _f.reactions) || [],
|
|
41
|
+
sourceNumber: ((_g = message.envelope) === null || _g === void 0 ? void 0 : _g.sourceNumber) || '',
|
|
42
|
+
timestamp: ((_h = message.envelope) === null || _h === void 0 ? void 0 : _h.timestamp) || 0,
|
|
43
|
+
account: message.account || '',
|
|
44
|
+
},
|
|
45
|
+
pairedItem: { item: index },
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const axiosError = error;
|
|
51
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
52
|
+
message: axiosError.message,
|
|
53
|
+
description: (((_b = (_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error) || axiosError.message),
|
|
54
|
+
httpCode: ((_d = (_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === null || _d === void 0 ? void 0 : _d.toString()) || 'unknown',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.getMessageHistory = getMessageHistory;
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-signal-cli-rest-api",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Signal Node for n8n",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Signal Node for n8n using signal-cli-rest-api",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/ZBlaZe/n8n-nodes-signal.git"
|
|
8
8
|
},
|
|
9
|
-
"keywords": ["n8n-community", "signal", "messaging"],
|
|
9
|
+
"keywords": ["n8n-community-node-package", "signal", "messaging", "n8n", "automation"],
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"main": "index.js",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/**/*",
|
|
14
|
+
"package.json",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
12
18
|
"scripts": {
|
|
13
19
|
"build": "tsc && copy nodes\\Signal\\signal.svg dist\\nodes\\Signal\\signal.svg && copy credentials\\signal.svg dist\\credentials\\signal.svg",
|
|
14
|
-
"dev": "tsc --watch"
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
15
22
|
},
|
|
16
23
|
"n8n": {
|
|
17
24
|
"n8nNodesApiVersion": 1,
|
|
@@ -19,18 +26,22 @@
|
|
|
19
26
|
"dist/nodes/Signal/Signal.node.js",
|
|
20
27
|
"dist/nodes/Signal/SignalTrigger.node.js"
|
|
21
28
|
],
|
|
22
|
-
"credentials": ["
|
|
29
|
+
"credentials": ["dist/credentials/SignalApi.credentials.js"]
|
|
23
30
|
},
|
|
24
31
|
"dependencies": {
|
|
25
|
-
"axios": "1.7.2",
|
|
26
|
-
"n8n-core": "1.111.0",
|
|
27
|
-
"n8n-workflow": "1.109.0",
|
|
28
|
-
"@types/node": "20.12.12",
|
|
32
|
+
"axios": "^1.7.2",
|
|
29
33
|
"ws": "^8.18.0"
|
|
30
34
|
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"n8n-workflow": ">=1.109.0"
|
|
37
|
+
},
|
|
31
38
|
"devDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
39
|
+
"n8n-workflow": "1.109.0",
|
|
40
|
+
"typescript": "^5.4.5",
|
|
41
|
+
"@types/node": "^20.12.12",
|
|
34
42
|
"@types/ws": "^8.5.12"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
35
46
|
}
|
|
36
47
|
}
|
package/.editorconfig
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
root = true
|
|
2
|
-
|
|
3
|
-
[*]
|
|
4
|
-
charset = utf-8
|
|
5
|
-
indent_style = tab
|
|
6
|
-
indent_size = 2
|
|
7
|
-
end_of_line = lf
|
|
8
|
-
insert_final_newline = true
|
|
9
|
-
trim_trailing_whitespace = true
|
|
10
|
-
|
|
11
|
-
[package.json]
|
|
12
|
-
indent_style = space
|
|
13
|
-
indent_size = 2
|
|
14
|
-
|
|
15
|
-
[*.md]
|
|
16
|
-
trim_trailing_whitespace = false
|
|
17
|
-
|
|
18
|
-
[*.yml]
|
|
19
|
-
indent_style = space
|
|
20
|
-
indent_size = 2
|
package/.eslintrc.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @type {import('@types/eslint').ESLint.ConfigData}
|
|
3
|
-
*/
|
|
4
|
-
module.exports = {
|
|
5
|
-
root: true,
|
|
6
|
-
|
|
7
|
-
env: {
|
|
8
|
-
browser: true,
|
|
9
|
-
es6: true,
|
|
10
|
-
node: true,
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
parser: '@typescript-eslint/parser',
|
|
14
|
-
|
|
15
|
-
parserOptions: {
|
|
16
|
-
project: ['./tsconfig.json'],
|
|
17
|
-
sourceType: 'module',
|
|
18
|
-
extraFileExtensions: ['.json'],
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
ignorePatterns: ['.eslintrc.js', '**/*.js', '**/node_modules/**', '**/dist/**'],
|
|
22
|
-
|
|
23
|
-
overrides: [
|
|
24
|
-
{
|
|
25
|
-
files: ['package.json'],
|
|
26
|
-
plugins: ['eslint-plugin-n8n-nodes-base'],
|
|
27
|
-
extends: ['plugin:n8n-nodes-base/community'],
|
|
28
|
-
rules: {
|
|
29
|
-
'n8n-nodes-base/community-package-json-name-still-default': 'off',
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
files: ['./credentials/**/*.ts'],
|
|
34
|
-
plugins: ['eslint-plugin-n8n-nodes-base'],
|
|
35
|
-
extends: ['plugin:n8n-nodes-base/credentials'],
|
|
36
|
-
rules: {
|
|
37
|
-
'n8n-nodes-base/cred-class-field-authenticate-type-assertion': 'error',
|
|
38
|
-
'n8n-nodes-base/cred-class-field-display-name-missing-oauth2': 'error',
|
|
39
|
-
'n8n-nodes-base/cred-class-field-display-name-miscased': 'error',
|
|
40
|
-
'n8n-nodes-base/cred-class-field-documentation-url-missing': 'error',
|
|
41
|
-
'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off',
|
|
42
|
-
'n8n-nodes-base/cred-class-field-name-missing-oauth2': 'error',
|
|
43
|
-
'n8n-nodes-base/cred-class-field-name-unsuffixed': 'error',
|
|
44
|
-
'n8n-nodes-base/cred-class-field-name-uppercase-first-char': 'error',
|
|
45
|
-
'n8n-nodes-base/cred-class-field-properties-assertion': 'error',
|
|
46
|
-
'n8n-nodes-base/cred-class-field-type-options-password-missing': 'error',
|
|
47
|
-
'n8n-nodes-base/cred-class-name-missing-oauth2-suffix': 'error',
|
|
48
|
-
'n8n-nodes-base/cred-class-name-unsuffixed': 'error',
|
|
49
|
-
'n8n-nodes-base/cred-filename-against-convention': 'error',
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
files: ['./nodes/**/*.ts'],
|
|
54
|
-
plugins: ['eslint-plugin-n8n-nodes-base'],
|
|
55
|
-
extends: ['plugin:n8n-nodes-base/nodes'],
|
|
56
|
-
rules: {
|
|
57
|
-
'n8n-nodes-base/node-class-description-credentials-name-unsuffixed': 'error',
|
|
58
|
-
'n8n-nodes-base/node-class-description-display-name-unsuffixed-trigger-node': 'error',
|
|
59
|
-
'n8n-nodes-base/node-class-description-empty-string': 'error',
|
|
60
|
-
'n8n-nodes-base/node-class-description-icon-not-svg': 'error',
|
|
61
|
-
'n8n-nodes-base/node-class-description-inputs-wrong-regular-node': 'off',
|
|
62
|
-
'n8n-nodes-base/node-class-description-inputs-wrong-trigger-node': 'error',
|
|
63
|
-
'n8n-nodes-base/node-class-description-missing-subtitle': 'error',
|
|
64
|
-
'n8n-nodes-base/node-class-description-non-core-color-present': 'error',
|
|
65
|
-
'n8n-nodes-base/node-class-description-name-miscased': 'error',
|
|
66
|
-
'n8n-nodes-base/node-class-description-name-unsuffixed-trigger-node': 'error',
|
|
67
|
-
'n8n-nodes-base/node-class-description-outputs-wrong': 'off',
|
|
68
|
-
'n8n-nodes-base/node-dirname-against-convention': 'error',
|
|
69
|
-
'n8n-nodes-base/node-execute-block-double-assertion-for-items': 'error',
|
|
70
|
-
'n8n-nodes-base/node-execute-block-wrong-error-thrown': 'error',
|
|
71
|
-
'n8n-nodes-base/node-filename-against-convention': 'error',
|
|
72
|
-
'n8n-nodes-base/node-param-array-type-assertion': 'error',
|
|
73
|
-
'n8n-nodes-base/node-param-color-type-unused': 'error',
|
|
74
|
-
'n8n-nodes-base/node-param-default-missing': 'error',
|
|
75
|
-
'n8n-nodes-base/node-param-default-wrong-for-boolean': 'error',
|
|
76
|
-
'n8n-nodes-base/node-param-default-wrong-for-collection': 'error',
|
|
77
|
-
'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error',
|
|
78
|
-
'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error',
|
|
79
|
-
'n8n-nodes-base/node-param-default-wrong-for-multi-options': 'error',
|
|
80
|
-
'n8n-nodes-base/node-param-default-wrong-for-number': 'error',
|
|
81
|
-
'n8n-nodes-base/node-param-default-wrong-for-simplify': 'error',
|
|
82
|
-
'n8n-nodes-base/node-param-default-wrong-for-string': 'error',
|
|
83
|
-
'n8n-nodes-base/node-param-description-boolean-without-whether': 'error',
|
|
84
|
-
'n8n-nodes-base/node-param-description-comma-separated-hyphen': 'error',
|
|
85
|
-
'n8n-nodes-base/node-param-description-empty-string': 'error',
|
|
86
|
-
'n8n-nodes-base/node-param-description-excess-final-period': 'error',
|
|
87
|
-
'n8n-nodes-base/node-param-description-excess-inner-whitespace': 'error',
|
|
88
|
-
'n8n-nodes-base/node-param-description-identical-to-display-name': 'error',
|
|
89
|
-
'n8n-nodes-base/node-param-description-line-break-html-tag': 'error',
|
|
90
|
-
'n8n-nodes-base/node-param-description-lowercase-first-char': 'error',
|
|
91
|
-
'n8n-nodes-base/node-param-description-miscased-id': 'error',
|
|
92
|
-
'n8n-nodes-base/node-param-description-miscased-json': 'error',
|
|
93
|
-
'n8n-nodes-base/node-param-description-miscased-url': 'error',
|
|
94
|
-
'n8n-nodes-base/node-param-description-missing-final-period': 'error',
|
|
95
|
-
'n8n-nodes-base/node-param-description-missing-for-ignore-ssl-issues': 'error',
|
|
96
|
-
'n8n-nodes-base/node-param-description-missing-for-return-all': 'error',
|
|
97
|
-
'n8n-nodes-base/node-param-description-missing-for-simplify': 'error',
|
|
98
|
-
'n8n-nodes-base/node-param-description-missing-from-dynamic-multi-options': 'error',
|
|
99
|
-
'n8n-nodes-base/node-param-description-missing-from-dynamic-options': 'error',
|
|
100
|
-
'n8n-nodes-base/node-param-description-missing-from-limit': 'error',
|
|
101
|
-
'n8n-nodes-base/node-param-description-unencoded-angle-brackets': 'error',
|
|
102
|
-
'n8n-nodes-base/node-param-description-unneeded-backticks': 'error',
|
|
103
|
-
'n8n-nodes-base/node-param-description-untrimmed': 'error',
|
|
104
|
-
'n8n-nodes-base/node-param-description-url-missing-protocol': 'error',
|
|
105
|
-
'n8n-nodes-base/node-param-description-weak': 'error',
|
|
106
|
-
'n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options': 'error',
|
|
107
|
-
'n8n-nodes-base/node-param-description-wrong-for-dynamic-options': 'error',
|
|
108
|
-
'n8n-nodes-base/node-param-description-wrong-for-ignore-ssl-issues': 'error',
|
|
109
|
-
'n8n-nodes-base/node-param-description-wrong-for-limit': 'error',
|
|
110
|
-
'n8n-nodes-base/node-param-description-wrong-for-return-all': 'error',
|
|
111
|
-
'n8n-nodes-base/node-param-description-wrong-for-simplify': 'error',
|
|
112
|
-
'n8n-nodes-base/node-param-description-wrong-for-upsert': 'error',
|
|
113
|
-
'n8n-nodes-base/node-param-display-name-excess-inner-whitespace': 'error',
|
|
114
|
-
'n8n-nodes-base/node-param-display-name-miscased-id': 'error',
|
|
115
|
-
'n8n-nodes-base/node-param-display-name-miscased': 'error',
|
|
116
|
-
'n8n-nodes-base/node-param-display-name-not-first-position': 'error',
|
|
117
|
-
'n8n-nodes-base/node-param-display-name-untrimmed': 'error',
|
|
118
|
-
'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options': 'error',
|
|
119
|
-
'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options': 'error',
|
|
120
|
-
'n8n-nodes-base/node-param-display-name-wrong-for-simplify': 'error',
|
|
121
|
-
'n8n-nodes-base/node-param-display-name-wrong-for-update-fields': 'error',
|
|
122
|
-
'n8n-nodes-base/node-param-min-value-wrong-for-limit': 'error',
|
|
123
|
-
'n8n-nodes-base/node-param-multi-options-type-unsorted-items': 'error',
|
|
124
|
-
'n8n-nodes-base/node-param-name-untrimmed': 'error',
|
|
125
|
-
'n8n-nodes-base/node-param-operation-option-action-wrong-for-get-many': 'error',
|
|
126
|
-
'n8n-nodes-base/node-param-operation-option-description-wrong-for-get-many': 'error',
|
|
127
|
-
'n8n-nodes-base/node-param-operation-option-without-action': 'error',
|
|
128
|
-
'n8n-nodes-base/node-param-operation-without-no-data-expression': 'error',
|
|
129
|
-
'n8n-nodes-base/node-param-option-description-identical-to-name': 'error',
|
|
130
|
-
'n8n-nodes-base/node-param-option-name-containing-star': 'error',
|
|
131
|
-
'n8n-nodes-base/node-param-option-name-duplicate': 'error',
|
|
132
|
-
'n8n-nodes-base/node-param-option-name-wrong-for-get-many': 'error',
|
|
133
|
-
'n8n-nodes-base/node-param-option-name-wrong-for-upsert': 'error',
|
|
134
|
-
'n8n-nodes-base/node-param-option-value-duplicate': 'error',
|
|
135
|
-
'n8n-nodes-base/node-param-options-type-unsorted-items': 'error',
|
|
136
|
-
'n8n-nodes-base/node-param-placeholder-miscased-id': 'error',
|
|
137
|
-
'n8n-nodes-base/node-param-placeholder-missing-email': 'error',
|
|
138
|
-
'n8n-nodes-base/node-param-required-false': 'error',
|
|
139
|
-
'n8n-nodes-base/node-param-resource-with-plural-option': 'error',
|
|
140
|
-
'n8n-nodes-base/node-param-resource-without-no-data-expression': 'error',
|
|
141
|
-
'n8n-nodes-base/node-param-type-options-missing-from-limit': 'error',
|
|
142
|
-
'n8n-nodes-base/node-param-type-options-password-missing': 'error',
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
};
|
package/.eslintrc.prepublish.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @type {import('@types/eslint').ESLint.ConfigData}
|
|
3
|
-
*/
|
|
4
|
-
module.exports = {
|
|
5
|
-
extends: "./.eslintrc.js",
|
|
6
|
-
|
|
7
|
-
overrides: [
|
|
8
|
-
{
|
|
9
|
-
files: ['package.json'],
|
|
10
|
-
plugins: ['eslint-plugin-n8n-nodes-base'],
|
|
11
|
-
rules: {
|
|
12
|
-
'n8n-nodes-base/community-package-json-name-still-default': 'error',
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
};
|
package/.prettierrc.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
/**
|
|
3
|
-
* https://prettier.io/docs/en/options.html#semicolons
|
|
4
|
-
*/
|
|
5
|
-
semi: true,
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* https://prettier.io/docs/en/options.html#trailing-commas
|
|
9
|
-
*/
|
|
10
|
-
trailingComma: 'all',
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* https://prettier.io/docs/en/options.html#bracket-spacing
|
|
14
|
-
*/
|
|
15
|
-
bracketSpacing: true,
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* https://prettier.io/docs/en/options.html#tabs
|
|
19
|
-
*/
|
|
20
|
-
useTabs: true,
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* https://prettier.io/docs/en/options.html#tab-width
|
|
24
|
-
*/
|
|
25
|
-
tabWidth: 2,
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* https://prettier.io/docs/en/options.html#arrow-function-parentheses
|
|
29
|
-
*/
|
|
30
|
-
arrowParens: 'always',
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* https://prettier.io/docs/en/options.html#quotes
|
|
34
|
-
*/
|
|
35
|
-
singleQuote: true,
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* https://prettier.io/docs/en/options.html#quote-props
|
|
39
|
-
*/
|
|
40
|
-
quoteProps: 'as-needed',
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* https://prettier.io/docs/en/options.html#end-of-line
|
|
44
|
-
*/
|
|
45
|
-
endOfLine: 'lf',
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* https://prettier.io/docs/en/options.html#print-width
|
|
49
|
-
*/
|
|
50
|
-
printWidth: 100,
|
|
51
|
-
};
|
package/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
-
contributors and maintainers pledge to making participation in our project and
|
|
7
|
-
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
-
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
9
|
-
level of experience, education, socio-economic status, nationality, personal
|
|
10
|
-
appearance, race, religion, or sexual identity and orientation.
|
|
11
|
-
|
|
12
|
-
## Our Standards
|
|
13
|
-
|
|
14
|
-
Examples of behavior that contributes to creating a positive environment
|
|
15
|
-
include:
|
|
16
|
-
|
|
17
|
-
* Using welcoming and inclusive language
|
|
18
|
-
* Being respectful of differing viewpoints and experiences
|
|
19
|
-
* Gracefully accepting constructive criticism
|
|
20
|
-
* Focusing on what is best for the community
|
|
21
|
-
* Showing empathy towards other community members
|
|
22
|
-
|
|
23
|
-
Examples of unacceptable behavior by participants include:
|
|
24
|
-
|
|
25
|
-
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
-
advances
|
|
27
|
-
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
-
* Public or private harassment
|
|
29
|
-
* Publishing others' private information, such as a physical or electronic
|
|
30
|
-
address, without explicit permission
|
|
31
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
-
professional setting
|
|
33
|
-
|
|
34
|
-
## Our Responsibilities
|
|
35
|
-
|
|
36
|
-
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
-
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
-
response to any instances of unacceptable behavior.
|
|
39
|
-
|
|
40
|
-
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
-
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
-
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
-
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
-
threatening, offensive, or harmful.
|
|
45
|
-
|
|
46
|
-
## Scope
|
|
47
|
-
|
|
48
|
-
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
-
when an individual is representing the project or its community. Examples of
|
|
50
|
-
representing a project or community include using an official project e-mail
|
|
51
|
-
address, posting via an official social media account, or acting as an appointed
|
|
52
|
-
representative at an online or offline event. Representation of a project may be
|
|
53
|
-
further defined and clarified by project maintainers.
|
|
54
|
-
|
|
55
|
-
## Enforcement
|
|
56
|
-
|
|
57
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
-
reported by contacting the project team at jan@n8n.io. All
|
|
59
|
-
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
-
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
-
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
-
Further details of specific enforcement policies may be posted separately.
|
|
63
|
-
|
|
64
|
-
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
-
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
-
members of the project's leadership.
|
|
67
|
-
|
|
68
|
-
## Attribution
|
|
69
|
-
|
|
70
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
-
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
72
|
-
|
|
73
|
-
[homepage]: https://www.contributor-covenant.org
|
|
74
|
-
|
|
75
|
-
For answers to common questions about this code of conduct, see
|
|
76
|
-
https://www.contributor-covenant.org/faq
|
|
File without changes
|
|
File without changes
|