@atom8n/n8n-nodes-base 2.5.5 → 2.5.7

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.
@@ -2615,7 +2615,8 @@
2615
2615
  "sourcePath": "dist/credentials/TelegramApi.credentials.js",
2616
2616
  "supportedNodes": [
2617
2617
  "telegram",
2618
- "telegramTrigger"
2618
+ "telegramTrigger",
2619
+ "telegramPollingTrigger"
2619
2620
  ]
2620
2621
  },
2621
2622
  "theHiveProjectApi": {
@@ -1499,6 +1499,10 @@
1499
1499
  "className": "TelegramTrigger",
1500
1500
  "sourcePath": "dist/nodes/Telegram/TelegramTrigger.node.js"
1501
1501
  },
1502
+ "telegramPollingTrigger": {
1503
+ "className": "TelegramPollingTrigger",
1504
+ "sourcePath": "dist/nodes/Telegram/TelegramPollingTrigger.node.js"
1505
+ },
1502
1506
  "theHiveProject": {
1503
1507
  "className": "TheHiveProject",
1504
1508
  "sourcePath": "dist/nodes/TheHiveProject/TheHiveProject.node.js"
@@ -16,14 +16,14 @@ export declare const isPrimary: (field: FieldWithPrimaryField) => boolean;
16
16
  declare function adjustLocation(allFields: AllFieldsUi): AllFieldsUi | {
17
17
  location: IDataObject;
18
18
  languages_spoken: import("./types").LanguageCodes;
19
- phone_numbers: {
20
- phone_numbers_fields: import("./types").PhoneNumberField[];
21
- };
19
+ email_addresses: import("./types").EmailAddressUi;
22
20
  postal_addresses: {
23
21
  postal_addresses_fields: import("./types").PostalAddressField[];
24
22
  };
23
+ phone_numbers: {
24
+ phone_numbers_fields: import("./types").PhoneNumberField[];
25
+ };
25
26
  target: string;
26
- email_addresses: import("./types").EmailAddressUi;
27
27
  };
28
28
  declare function adjustTargets(allFields: AllFieldsUi): AllFieldsUi | {
29
29
  target: {
@@ -33,13 +33,13 @@ declare function adjustTargets(allFields: AllFieldsUi): AllFieldsUi | {
33
33
  postal_addresses_fields: import("./types").PostalAddressField;
34
34
  };
35
35
  languages_spoken: import("./types").LanguageCodes;
36
- phone_numbers: {
37
- phone_numbers_fields: import("./types").PhoneNumberField[];
38
- };
36
+ email_addresses: import("./types").EmailAddressUi;
39
37
  postal_addresses: {
40
38
  postal_addresses_fields: import("./types").PostalAddressField[];
41
39
  };
42
- email_addresses: import("./types").EmailAddressUi;
40
+ phone_numbers: {
41
+ phone_numbers_fields: import("./types").PhoneNumberField[];
42
+ };
43
43
  };
44
44
  export declare const adjustPersonPayload: (...args: any[]) => any;
45
45
  export declare const adjustPetitionPayload: typeof adjustTargets;
@@ -58,18 +58,18 @@ export declare const simplifyResponse: (response: Response, resource: Resource)
58
58
  language_spoken: any;
59
59
  postal_address: {
60
60
  address_lines: string;
61
- region: string;
62
- primary: boolean;
63
- locality: string;
64
- postal_code: string;
65
- country: string;
66
- language: import("./types").LanguageCodes;
67
61
  location: {
68
62
  location_fields: {
69
63
  latitude: string;
70
64
  longitude: string;
71
65
  };
72
66
  };
67
+ country: string;
68
+ region: string;
69
+ primary: boolean;
70
+ locality: string;
71
+ postal_code: string;
72
+ language: import("./types").LanguageCodes;
73
73
  };
74
74
  phone_number: string;
75
75
  email_address: string;
@@ -79,18 +79,18 @@ export declare const simplifyResponse: (response: Response, resource: Resource)
79
79
  language_spoken: any;
80
80
  postal_address: {
81
81
  address_lines: string;
82
- region: string;
83
- primary: boolean;
84
- locality: string;
85
- postal_code: string;
86
- country: string;
87
- language: import("./types").LanguageCodes;
88
82
  location: {
89
83
  location_fields: {
90
84
  latitude: string;
91
85
  longitude: string;
92
86
  };
93
87
  };
88
+ country: string;
89
+ region: string;
90
+ primary: boolean;
91
+ locality: string;
92
+ postal_code: string;
93
+ language: import("./types").LanguageCodes;
94
94
  };
95
95
  phone_number: string;
96
96
  email_address: string;
@@ -93,7 +93,7 @@ class ExecuteWorkflowTrigger {
93
93
  description: 'Use all incoming data from the parent workflow',
94
94
  },
95
95
  ],
96
- default: constants_1.WORKFLOW_INPUTS,
96
+ default: constants_1.PASSTHROUGH,
97
97
  noDataExpression: true,
98
98
  displayOptions: {
99
99
  show: { '@version': [{ _cnd: { gte: 1.1 } }] },
@@ -2,7 +2,10 @@ import type { IDataObject, IExecuteFunctions, INode, INodeExecutionData, IPaired
2
2
  import { NodeOperationError } from 'n8n-workflow';
3
3
  import type { Mysql2Pool, QueryValues, QueryWithValues, SortRule, WhereClause } from './interfaces';
4
4
  export declare function escapeSqlIdentifier(identifier: string): string;
5
- export declare const prepareQueryAndReplacements: (rawQuery: string, nodeVersion: number, replacements?: QueryValues) => QueryWithValues;
5
+ export declare const prepareQueryAndReplacements: (rawQuery: string, nodeVersion: number, replacements?: QueryValues) => {
6
+ query: string;
7
+ values: QueryValues;
8
+ };
6
9
  export declare const prepareQueryLegacy: (rawQuery: string, replacements: QueryValues) => QueryWithValues;
7
10
  export declare function prepareErrorItem(item: IDataObject, error: IDataObject | NodeOperationError | Error, index: number): INodeExecutionData;
8
11
  export declare function parseMySqlError(this: IExecuteFunctions, error: any, itemIndex?: number, queries?: string[]): NodeOperationError;
@@ -1,4 +1,4 @@
1
- import type { IDataObject, IExecuteFunctions, IHookFunctions, IHttpRequestMethods, ILoadOptionsFunctions, IWebhookFunctions } from 'n8n-workflow';
1
+ import type { IDataObject, IExecuteFunctions, IHookFunctions, IHttpRequestMethods, ILoadOptionsFunctions, IPollFunctions, ITriggerFunctions, IWebhookFunctions } from 'n8n-workflow';
2
2
  export interface IMarkupKeyboard {
3
3
  rows?: IMarkupKeyboardRow[];
4
4
  }
@@ -45,7 +45,7 @@ export declare function addAdditionalFields(this: IExecuteFunctions, body: IData
45
45
  * Make an API request to Telegram
46
46
  *
47
47
  */
48
- export declare function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: IHttpRequestMethods, endpoint: string, body: IDataObject, query?: IDataObject, option?: IDataObject): Promise<any>;
48
+ export declare function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions | ITriggerFunctions | IWebhookFunctions, method: IHttpRequestMethods, endpoint: string, body: IDataObject, query?: IDataObject, option?: IDataObject): Promise<any>;
49
49
  export declare function getImageBySize(photos: IDataObject[], size: string): IDataObject | undefined;
50
50
  export declare function getPropertyName(operation: string): string;
51
51
  export declare function getSecretToken(this: IHookFunctions | IWebhookFunctions): string;
@@ -0,0 +1,15 @@
1
+ import type { ITriggerFunctions, INodeType, INodeTypeDescription, ITriggerResponse } from 'n8n-workflow';
2
+ /**
3
+ * Telegram Polling Trigger — uses getUpdates (true long-polling) instead of webhooks.
4
+ *
5
+ * This lets the trigger work on local / HTTP-only n8n instances without
6
+ * requiring an HTTPS URL or a tunnelling service like ngrok.
7
+ *
8
+ * Design mirrors openclaw's approach: continuous loop calling getUpdates with
9
+ * a 30-second long-poll timeout, filtering by allowed update types, and
10
+ * persisting the offset across restarts.
11
+ */
12
+ export declare class TelegramPollingTrigger implements INodeType {
13
+ description: INodeTypeDescription;
14
+ trigger(this: ITriggerFunctions): Promise<ITriggerResponse>;
15
+ }
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TelegramPollingTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const GenericFunctions_1 = require("./GenericFunctions");
6
+ /**
7
+ * Telegram Polling Trigger — uses getUpdates (true long-polling) instead of webhooks.
8
+ *
9
+ * This lets the trigger work on local / HTTP-only n8n instances without
10
+ * requiring an HTTPS URL or a tunnelling service like ngrok.
11
+ *
12
+ * Design mirrors openclaw's approach: continuous loop calling getUpdates with
13
+ * a 30-second long-poll timeout, filtering by allowed update types, and
14
+ * persisting the offset across restarts.
15
+ */
16
+ class TelegramPollingTrigger {
17
+ description = {
18
+ displayName: 'Telegram Polling Trigger',
19
+ name: 'telegramPollingTrigger',
20
+ icon: 'file:telegram.svg',
21
+ group: ['trigger'],
22
+ version: 1,
23
+ subtitle: '=Updates: {{$parameter["updates"].join(", ")}}',
24
+ description: 'Starts the workflow on a Telegram update using long-polling (no HTTPS required, works locally)',
25
+ defaults: {
26
+ name: 'Telegram Polling Trigger',
27
+ },
28
+ inputs: [],
29
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
30
+ credentials: [
31
+ {
32
+ name: 'telegramApi',
33
+ required: true,
34
+ },
35
+ ],
36
+ triggerPanel: {
37
+ header: '',
38
+ executionsHelp: {
39
+ inactive: "<b>While building your workflow</b>, click the 'execute step' button, then send a message to your Telegram bot. This will trigger an execution, which will show up in this editor.<br /><br /><b>Once you're happy with your workflow</b>, publish it. Then every time a message arrives, the workflow will execute. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor.",
40
+ active: "<b>While building your workflow</b>, click the 'execute step' button, then send a message to your Telegram bot. This will trigger an execution, which will show up in this editor.<br /><br /><b>Your workflow will also execute automatically</b>, since it's activated. Every time a message arrives, this node will trigger an execution. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor.",
41
+ },
42
+ activationHint: "Once you've finished building your workflow, publish it to have it also listen continuously (you just won't see those executions here).",
43
+ },
44
+ properties: [
45
+ {
46
+ displayName: 'This trigger uses long-polling (getUpdates) instead of webhooks, so it works locally without HTTPS. Any existing webhook for this bot will be removed when the trigger starts.',
47
+ name: 'pollingNotice',
48
+ type: 'notice',
49
+ default: '',
50
+ },
51
+ {
52
+ displayName: 'Due to Telegram API limitations, you can use just one Telegram trigger for each bot at a time',
53
+ name: 'telegramTriggerNotice',
54
+ type: 'notice',
55
+ default: '',
56
+ },
57
+ {
58
+ displayName: 'Trigger On',
59
+ name: 'updates',
60
+ type: 'multiOptions',
61
+ options: [
62
+ {
63
+ name: '*',
64
+ value: '*',
65
+ description: 'All updates',
66
+ },
67
+ {
68
+ name: 'Callback Query',
69
+ value: 'callback_query',
70
+ description: 'Trigger on new incoming callback query',
71
+ },
72
+ {
73
+ name: 'Channel Post',
74
+ value: 'channel_post',
75
+ description: 'Trigger on new incoming channel post of any kind — text, photo, sticker, etc',
76
+ },
77
+ {
78
+ name: 'Edited Channel Post',
79
+ value: 'edited_channel_post',
80
+ description: 'Trigger on new version of a channel post that is known to the bot and was edited',
81
+ },
82
+ {
83
+ name: 'Edited Message',
84
+ value: 'edited_message',
85
+ description: 'Trigger on new version of a message that is known to the bot and was edited',
86
+ },
87
+ {
88
+ name: 'Inline Query',
89
+ value: 'inline_query',
90
+ description: 'Trigger on new incoming inline query',
91
+ },
92
+ {
93
+ name: 'Message',
94
+ value: 'message',
95
+ description: 'Trigger on new incoming message of any kind — text, photo, sticker, etc',
96
+ },
97
+ {
98
+ name: 'Poll',
99
+ value: 'poll',
100
+ action: 'On Poll Change',
101
+ description: 'Trigger on new poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot.',
102
+ },
103
+ {
104
+ name: 'Pre-Checkout Query',
105
+ value: 'pre_checkout_query',
106
+ description: 'Trigger on new incoming pre-checkout query. Contains full information about checkout.',
107
+ },
108
+ {
109
+ name: 'Shipping Query',
110
+ value: 'shipping_query',
111
+ description: 'Trigger on new incoming shipping query. Only for invoices with flexible price.',
112
+ },
113
+ ],
114
+ required: true,
115
+ default: [],
116
+ },
117
+ {
118
+ displayName: 'Additional Fields',
119
+ name: 'additionalFields',
120
+ type: 'collection',
121
+ placeholder: 'Add Field',
122
+ default: {},
123
+ options: [
124
+ {
125
+ displayName: 'Restrict to Chat IDs',
126
+ name: 'chatIds',
127
+ type: 'string',
128
+ default: '',
129
+ description: 'The chat IDs to restrict the trigger to. Multiple can be defined separated by comma.',
130
+ },
131
+ {
132
+ displayName: 'Restrict to User IDs',
133
+ name: 'userIds',
134
+ type: 'string',
135
+ default: '',
136
+ description: 'The user IDs to restrict the trigger to. Multiple can be defined separated by comma.',
137
+ },
138
+ {
139
+ displayName: 'Send Typing Indicator',
140
+ name: 'sendTyping',
141
+ type: 'boolean',
142
+ default: true,
143
+ description: 'Whether to show "typing..." in Telegram instantly when a message is received (like openclaw). This gives the user immediate feedback while the workflow processes.',
144
+ },
145
+ ],
146
+ },
147
+ ],
148
+ };
149
+ async trigger() {
150
+ const allowedUpdates = this.getNodeParameter('updates');
151
+ const additionalFields = this.getNodeParameter('additionalFields');
152
+ const nodeStaticData = this.getWorkflowStaticData('node');
153
+ const sendTypingEnabled = additionalFields.sendTyping !== false; // defaults to true
154
+ // Extract chat_id from any update type for sending typing indicator
155
+ const extractChatId = (update) => {
156
+ for (const key of ['message', 'edited_message', 'channel_post', 'edited_channel_post']) {
157
+ const msg = update[key];
158
+ if (msg?.chat) {
159
+ return msg.chat.id;
160
+ }
161
+ }
162
+ // callback_query has message.chat
163
+ const callbackQuery = update.callback_query;
164
+ if (callbackQuery?.message) {
165
+ const cbMsg = callbackQuery.message;
166
+ if (cbMsg.chat) {
167
+ return cbMsg.chat.id;
168
+ }
169
+ }
170
+ return undefined;
171
+ };
172
+ // Send "typing..." indicator — fire-and-forget (don't delay workflow)
173
+ const sendTypingIndicator = (update) => {
174
+ if (!sendTypingEnabled) {
175
+ return;
176
+ }
177
+ const chatId = extractChatId(update);
178
+ if (chatId === undefined) {
179
+ return;
180
+ }
181
+ void GenericFunctions_1.apiRequest
182
+ .call(this, 'POST', 'sendChatAction', {
183
+ chat_id: chatId,
184
+ action: 'typing',
185
+ })
186
+ .catch(() => {
187
+ // Ignore — typing indicator is best-effort
188
+ });
189
+ };
190
+ let aborted = false;
191
+ // Clear any existing webhook so getUpdates works.
192
+ // Telegram rejects getUpdates when a webhook is active.
193
+ try {
194
+ await GenericFunctions_1.apiRequest.call(this, 'POST', 'deleteWebhook', {});
195
+ }
196
+ catch {
197
+ // Ignore — webhook may not have been set
198
+ }
199
+ // Build allowed_updates array for getUpdates
200
+ const allowedUpdateTypes = allowedUpdates.length > 0 && !allowedUpdates.includes('*') ? allowedUpdates : undefined;
201
+ // Backoff state for error recovery (mirrors openclaw's TELEGRAM_POLL_RESTART_POLICY)
202
+ const BACKOFF = { initialMs: 2000, maxMs: 30_000, factor: 1.8, jitter: 0.25 };
203
+ let consecutiveErrors = 0;
204
+ const pollLoop = async () => {
205
+ while (!aborted) {
206
+ try {
207
+ // Build getUpdates request — true long-polling with 30s timeout (like openclaw)
208
+ const body = {
209
+ timeout: 30,
210
+ };
211
+ // Resume from last known update_id
212
+ const lastUpdateId = nodeStaticData.lastUpdateId;
213
+ if (lastUpdateId !== undefined) {
214
+ body.offset = lastUpdateId + 1;
215
+ }
216
+ if (allowedUpdateTypes) {
217
+ body.allowed_updates = JSON.stringify(allowedUpdateTypes);
218
+ }
219
+ const responseData = await GenericFunctions_1.apiRequest.call(this, 'POST', 'getUpdates', body);
220
+ consecutiveErrors = 0; // Reset on success
221
+ const updates = responseData.result;
222
+ if (!Array.isArray(updates) || updates.length === 0) {
223
+ continue;
224
+ }
225
+ // Track the highest update_id
226
+ let maxUpdateId = nodeStaticData.lastUpdateId ?? 0;
227
+ for (const update of updates) {
228
+ const updateId = update.update_id;
229
+ if (updateId > maxUpdateId) {
230
+ maxUpdateId = updateId;
231
+ }
232
+ // Filter by update type
233
+ if (allowedUpdateTypes && allowedUpdateTypes.length > 0) {
234
+ const updateType = Object.keys(update).find((key) => key !== 'update_id');
235
+ if (updateType && !allowedUpdateTypes.includes(updateType)) {
236
+ continue;
237
+ }
238
+ }
239
+ // Filter by chat ID
240
+ if (additionalFields.chatIds) {
241
+ const chatIds = additionalFields.chatIds
242
+ .split(',')
243
+ .map((id) => id.trim());
244
+ const messageChatId = String(update.message?.chat
245
+ ? update.message.chat.id
246
+ : '');
247
+ if (messageChatId && !chatIds.includes(messageChatId)) {
248
+ continue;
249
+ }
250
+ }
251
+ // Filter by user ID
252
+ if (additionalFields.userIds) {
253
+ const userIds = additionalFields.userIds
254
+ .split(',')
255
+ .map((id) => id.trim());
256
+ const messageUserId = String(update.message?.from
257
+ ? update.message.from.id
258
+ : '');
259
+ if (messageUserId && !userIds.includes(messageUserId)) {
260
+ continue;
261
+ }
262
+ }
263
+ // Send typing indicator instantly (like openclaw)
264
+ sendTypingIndicator(update);
265
+ // Emit each matched update as a separate workflow execution
266
+ this.emit([this.helpers.returnJsonArray([update])]);
267
+ }
268
+ // Persist offset even if no updates matched filters — we consumed them
269
+ nodeStaticData.lastUpdateId = maxUpdateId;
270
+ }
271
+ catch (error) {
272
+ if (aborted) {
273
+ break;
274
+ }
275
+ // On 409 conflict, another instance is polling — back off
276
+ const errorCode = error.statusCode ??
277
+ error.error_code;
278
+ if (errorCode === 409) {
279
+ // getUpdates conflict — another bot instance is polling
280
+ consecutiveErrors++;
281
+ }
282
+ else {
283
+ consecutiveErrors++;
284
+ }
285
+ // Exponential backoff with jitter (same algorithm as openclaw)
286
+ const baseDelay = Math.min(BACKOFF.maxMs, BACKOFF.initialMs * Math.pow(BACKOFF.factor, consecutiveErrors - 1));
287
+ const jitter = 1 + (Math.random() * 2 - 1) * BACKOFF.jitter;
288
+ const delayMs = Math.min(BACKOFF.maxMs, Math.round(baseDelay * jitter));
289
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
290
+ }
291
+ }
292
+ };
293
+ // Manual trigger mode: wait for the first message then return
294
+ const manualTriggerFunction = async () => {
295
+ try {
296
+ await GenericFunctions_1.apiRequest.call(this, 'POST', 'deleteWebhook', {});
297
+ }
298
+ catch {
299
+ // Ignore
300
+ }
301
+ const body = {
302
+ timeout: 30,
303
+ };
304
+ const lastUpdateId = nodeStaticData.lastUpdateId;
305
+ if (lastUpdateId !== undefined) {
306
+ body.offset = lastUpdateId + 1;
307
+ }
308
+ if (allowedUpdateTypes) {
309
+ body.allowed_updates = JSON.stringify(allowedUpdateTypes);
310
+ }
311
+ // Keep polling until we get at least one update
312
+ while (!aborted) {
313
+ const responseData = await GenericFunctions_1.apiRequest.call(this, 'POST', 'getUpdates', body);
314
+ const updates = responseData.result;
315
+ if (Array.isArray(updates) && updates.length > 0) {
316
+ let maxUpdateId = nodeStaticData.lastUpdateId ?? 0;
317
+ for (const update of updates) {
318
+ const updateId = update.update_id;
319
+ if (updateId > maxUpdateId) {
320
+ maxUpdateId = updateId;
321
+ }
322
+ }
323
+ nodeStaticData.lastUpdateId = maxUpdateId;
324
+ // Send typing indicator for the first update
325
+ if (updates.length > 0) {
326
+ sendTypingIndicator(updates[0]);
327
+ }
328
+ this.emit([this.helpers.returnJsonArray(updates)]);
329
+ return;
330
+ }
331
+ // No updates yet, loop again with long-poll
332
+ }
333
+ };
334
+ // In production trigger mode, start the continuous loop
335
+ if (this.getMode() === 'trigger') {
336
+ void pollLoop();
337
+ }
338
+ async function closeFunction() {
339
+ aborted = true;
340
+ }
341
+ return {
342
+ closeFunction,
343
+ manualTriggerFunction,
344
+ };
345
+ }
346
+ }
347
+ exports.TelegramPollingTrigger = TelegramPollingTrigger;
@@ -78,8 +78,8 @@ export declare const adjustDealPayload: (allFields: AllFields) => {
78
78
  name: string;
79
79
  };
80
80
  };
81
- accountId?: string | undefined;
82
81
  contactId?: string | undefined;
82
+ accountId?: string | undefined;
83
83
  dealId?: string | undefined;
84
84
  Product_Details?: ProductDetails;
85
85
  };
@@ -129,8 +129,8 @@ export declare const adjustProductPayload: (allFields: AllFields) => {
129
129
  name: string;
130
130
  };
131
131
  };
132
- accountId?: string | undefined;
133
132
  contactId?: string | undefined;
133
+ accountId?: string | undefined;
134
134
  dealId?: string | undefined;
135
135
  Product_Details?: ProductDetails;
136
136
  };