@atom8n/n8n-nodes-base 2.5.6 → 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.
- package/dist/known/credentials.json +2 -1
- package/dist/known/nodes.json +4 -0
- package/dist/nodes/ActionNetwork/GenericFunctions.d.ts +20 -20
- package/dist/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.js +1 -1
- package/dist/nodes/MySql/v2/helpers/utils.d.ts +4 -1
- package/dist/nodes/Telegram/GenericFunctions.d.ts +2 -2
- package/dist/nodes/Telegram/TelegramPollingTrigger.node.d.ts +15 -0
- package/dist/nodes/Telegram/TelegramPollingTrigger.node.js +347 -0
- package/dist/nodes/Zoho/GenericFunctions.d.ts +2 -2
- package/dist/typecheck.tsbuildinfo +1 -1
- package/dist/types/credentials.json +1 -1
- package/dist/types/nodes.json +9 -8
- package/dist/utils/descriptions.js +1 -1
- package/package.json +10 -9
package/dist/known/nodes.json
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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) =>
|
|
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
|
};
|