@hostwebhook/node-types 1.52.9 → 1.52.11

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/index.d.ts CHANGED
@@ -11,8 +11,10 @@ export { GOOGLE_CALENDAR_OPERATIONS, isGoogleCalendarOperation, } from './calend
11
11
  export type { GoogleCalendarOperation } from './calendar-operations';
12
12
  export { DRIVE_OPERATIONS, isDriveOperation, } from './drive-operations';
13
13
  export type { DriveOperation } from './drive-operations';
14
- export { TELEGRAM_OPERATIONS, isTelegramOperation, } from './telegram-operations';
15
- export type { TelegramOperation } from './telegram-operations';
14
+ export { TELEGRAM_OPERATIONS, TELEGRAM_OPERATION_SPECS, isTelegramOperation, } from './telegram-operations';
15
+ export type { TelegramOperation, TelegramParamSpec, TelegramOperationSpec, } from './telegram-operations';
16
+ export { TELEGRAM_TOOLKIT_SPECS, TELEGRAM_TOOLKIT_BY_TOOL_NAME, } from './telegram-toolkit';
17
+ export type { TelegramToolkitSpec, TelegramToolkitParameter, } from './telegram-toolkit';
16
18
  export { WHATSAPP_OPERATIONS, isWhatsAppOperation, } from './whatsapp-operations';
17
19
  export type { WhatsAppOperation } from './whatsapp-operations';
18
20
  export { DISCORD_OPERATIONS, DISCORD_OPERATION_SPECS, isDiscordOperation, } from './discord-operations';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.isTelegramOperation = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
- exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = void 0;
3
+ exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.isDriveOperation = exports.DRIVE_OPERATIONS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
+ exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = void 0;
5
5
  var types_1 = require("./types");
6
6
  Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
7
7
  Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
@@ -44,7 +44,11 @@ Object.defineProperty(exports, "DRIVE_OPERATIONS", { enumerable: true, get: func
44
44
  Object.defineProperty(exports, "isDriveOperation", { enumerable: true, get: function () { return drive_operations_1.isDriveOperation; } });
45
45
  var telegram_operations_1 = require("./telegram-operations");
46
46
  Object.defineProperty(exports, "TELEGRAM_OPERATIONS", { enumerable: true, get: function () { return telegram_operations_1.TELEGRAM_OPERATIONS; } });
47
+ Object.defineProperty(exports, "TELEGRAM_OPERATION_SPECS", { enumerable: true, get: function () { return telegram_operations_1.TELEGRAM_OPERATION_SPECS; } });
47
48
  Object.defineProperty(exports, "isTelegramOperation", { enumerable: true, get: function () { return telegram_operations_1.isTelegramOperation; } });
49
+ var telegram_toolkit_1 = require("./telegram-toolkit");
50
+ Object.defineProperty(exports, "TELEGRAM_TOOLKIT_SPECS", { enumerable: true, get: function () { return telegram_toolkit_1.TELEGRAM_TOOLKIT_SPECS; } });
51
+ Object.defineProperty(exports, "TELEGRAM_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return telegram_toolkit_1.TELEGRAM_TOOLKIT_BY_TOOL_NAME; } });
48
52
  var whatsapp_operations_1 = require("./whatsapp-operations");
49
53
  Object.defineProperty(exports, "WHATSAPP_OPERATIONS", { enumerable: true, get: function () { return whatsapp_operations_1.WHATSAPP_OPERATIONS; } });
50
54
  Object.defineProperty(exports, "isWhatsAppOperation", { enumerable: true, get: function () { return whatsapp_operations_1.isWhatsAppOperation; } });
@@ -96,11 +96,19 @@ exports.SHEETS_TOOLKIT_SPECS = [
96
96
  operation: 'createSpreadsheet',
97
97
  label: 'Create spreadsheet',
98
98
  toolName: 'create_spreadsheet',
99
- description: 'Create a new, empty Google Sheets document and return its id and URL. It writes no data: to fill it, call append_row afterwards with the id it returns.',
100
- parameters: [
101
- p('title', 'Name of the new document.'),
102
- p('folderId', 'Id of the Drive folder to create it in. Left out, it lands in the root of My Drive.', false),
103
- ],
99
+ description: "Create a new, empty Google Sheets document and return its id and URL. It writes no data: to fill it, call append_row afterwards with the id it returns. It lands wherever the node's Save Location says — you do not choose that.",
100
+ /*
101
+ * Sin `folderId` a propósito, y esta es la excepción a «defecto del nodo que
102
+ * el modelo puede cambiar».
103
+ *
104
+ * La hoja sí se le ofrece porque el modelo tiene de dónde sacar un id: se lo
105
+ * dice quien escribe, o lo devuelve un get_rows. Un id de carpeta de Drive
106
+ * no: no hay ninguna herramienta aquí que las liste, así que sólo podría
107
+ * inventárselo, y un id inventado falla mientras uno real acertado por
108
+ * casualidad escribe en la carpeta de otro. La carpeta la elige la persona
109
+ * en el nodo, y la api la aplica.
110
+ */
111
+ parameters: [p('title', 'Name of the new document.')],
104
112
  destructive: true,
105
113
  },
106
114
  ];
@@ -15,3 +15,90 @@ export declare const TELEGRAM_OPERATIONS: readonly ["sendMessage", "replyToMessa
15
15
  export type TelegramOperation = (typeof TELEGRAM_OPERATIONS)[number];
16
16
  /** Type guard — useful when validating untrusted input (DTOs, tool calls). */
17
17
  export declare function isTelegramOperation(value: unknown): value is TelegramOperation;
18
+ /**
19
+ * Per-operation form schema, same idea as SLACK_OPERATION_SPECS and
20
+ * DISCORD_OPERATION_SPECS: the detail page renders it with one loop instead of
21
+ * a branch per operation. It replaces 12 branches and a local `OPERATIONS`
22
+ * array of 11 entries whose count had already drifted from the truth — the
23
+ * mode chip read "Choose 1 of 10 ops" while the page painted 11 cards.
24
+ *
25
+ * Two things about this node's schema are worth knowing before writing a
26
+ * renderer for it:
27
+ *
28
+ * 1. **`chatId` is per-operation, not global.** The page used to hoist one
29
+ * shared field to the top of every form, but `answerCallbackQuery` has no
30
+ * chat at all (it answers a callback id) and `forwardMessage` needs two
31
+ * chats with their own labels — "Destination" and "Source". So it is
32
+ * declared op by op, and where it repeats it repeats on purpose.
33
+ * 2. **Three booleans default to ON.** See the note on `default` below; a
34
+ * renderer that coerces with `!!config[name]` silently turns them off.
35
+ */
36
+ export interface TelegramParamSpec {
37
+ /** Field key — the property name inside `operationConfig`. */
38
+ name: string;
39
+ /** UI label shown above the control. */
40
+ label: string;
41
+ /**
42
+ * Param type for form rendering.
43
+ *
44
+ * - `string` — one-line input with `{{payload.x}}` templating.
45
+ * - `text` — the same input, multiline; `rows` sets its height.
46
+ * - `select` — fixed option list (only `parseMode` today).
47
+ * - `boolean` — a Switch. Read it as `config[name] ?? default`, never as
48
+ * `!!config[name]`: see `default`.
49
+ * - `number` — numeric input clamped to `min`/`max` as it is typed.
50
+ * - `linesList` — **stored as an array of strings, edited as one per
51
+ * line.** Telegram's poll options. The cousin of Contacts' `csvList`,
52
+ * split on newlines instead of commas. A renderer that treats it as text
53
+ * turns the array into a string on the first save and `sendPoll` starts
54
+ * failing.
55
+ */
56
+ type: 'string' | 'text' | 'select' | 'boolean' | 'number' | 'linesList';
57
+ /** Required by the operation. Metadata — the form does not gate on it. */
58
+ required?: boolean;
59
+ /** Help text under the control. Absent where the form shows none. */
60
+ description?: string;
61
+ /** Hint shown inside the input. May contain newlines for `linesList`. */
62
+ placeholder?: string;
63
+ /**
64
+ * Value to show when the stored one is `undefined`.
65
+ *
66
+ * `includeDisapprove`, `isAnonymous` and `parseMode` all have one, and the
67
+ * first two default to **true**: the stored config only ever holds `false`
68
+ * when the user turned them off. `config[name] ?? default` reproduces that
69
+ * exactly; `!!config[name]` does not, and getting it wrong strips the
70
+ * Disapprove button off every approval flow that already exists.
71
+ */
72
+ default?: string | number | boolean;
73
+ /** `select` only — the fixed option list, in display order. */
74
+ options?: string[];
75
+ /** `number` only — the input clamps to these while typing. */
76
+ min?: number;
77
+ max?: number;
78
+ /** `text` only — height of the textarea. */
79
+ rows?: number;
80
+ /**
81
+ * Renders side by side with the next field in a two-column row. Only the
82
+ * Approve / Disapprove button labels use it — they are one decision split
83
+ * in two, and stacking them reads as two unrelated fields.
84
+ */
85
+ pairedWithNext?: boolean;
86
+ }
87
+ export interface TelegramOperationSpec {
88
+ /** UI label for the operation picker card. */
89
+ label: string;
90
+ /** Sub-line under the label in the picker. */
91
+ description: string;
92
+ /** Bot API method the operation maps to (core.telegram.org/bots/api). */
93
+ apiMethod: string;
94
+ /**
95
+ * The op accepts `reply_markup`, so the page offers the inline-keyboard
96
+ * builder for it. Used to live as a hardcoded array of five strings in the
97
+ * detail page. `sendAndWaitForResponse` is deliberately absent: it builds
98
+ * its own Approve / Disapprove keyboard and a second one would collide.
99
+ */
100
+ supportsInlineKeyboard?: boolean;
101
+ /** Parameter schema. Order matters for form rendering. */
102
+ params: TelegramParamSpec[];
103
+ }
104
+ export declare const TELEGRAM_OPERATION_SPECS: Record<TelegramOperation, TelegramOperationSpec>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TELEGRAM_OPERATIONS = void 0;
3
+ exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = void 0;
4
4
  exports.isTelegramOperation = isTelegramOperation;
5
5
  /**
6
6
  * Telegram Bot API operation enum — single source of truth across the API,
@@ -43,3 +43,313 @@ function isTelegramOperation(value) {
43
43
  return (typeof value === 'string' &&
44
44
  exports.TELEGRAM_OPERATIONS.includes(value));
45
45
  }
46
+ /* Los campos que se repiten entre operaciones. Fábricas y no constantes
47
+ compartidas: cada op recibe su propia copia y puede cambiarle la etiqueta o
48
+ la ayuda sin tocar a las demás. */
49
+ const chatId = () => ({
50
+ name: 'chatId',
51
+ label: 'Chat ID',
52
+ type: 'string',
53
+ required: true,
54
+ description: 'Numeric chat id (from a Telegram trigger payload — usually {{payload.chatId}}) or @channelusername.',
55
+ placeholder: '{{payload.chatId}}',
56
+ });
57
+ const parseMode = (description) => ({
58
+ name: 'parseMode',
59
+ label: 'Parse mode',
60
+ type: 'select',
61
+ options: ['HTML', 'MarkdownV2', 'none'],
62
+ // Se enseña como VALOR, no como placeholder: el campo sale relleno con HTML
63
+ // aunque nunca se haya guardado nada.
64
+ default: 'HTML',
65
+ ...(description ? { description } : {}),
66
+ });
67
+ const messageId = (label, description, placeholder = '{{payload.messageId}}') => ({
68
+ name: 'messageId',
69
+ label,
70
+ type: 'string',
71
+ required: true,
72
+ placeholder,
73
+ ...(description ? { description } : {}),
74
+ });
75
+ const caption = (description, rows) => ({
76
+ name: 'caption',
77
+ label: 'Caption',
78
+ type: 'text',
79
+ rows,
80
+ description,
81
+ placeholder: rows > 2 ? 'Optional caption' : 'Optional',
82
+ });
83
+ exports.TELEGRAM_OPERATION_SPECS = {
84
+ sendMessage: {
85
+ label: 'Send message',
86
+ description: 'Text reply with optional inline keyboard',
87
+ apiMethod: 'POST /sendMessage',
88
+ supportsInlineKeyboard: true,
89
+ params: [
90
+ chatId(),
91
+ {
92
+ name: 'text',
93
+ label: 'Text',
94
+ type: 'text',
95
+ required: true,
96
+ rows: 4,
97
+ description: 'Message body. HTML by default — escape <, >, & in user-supplied content.',
98
+ placeholder: 'Hi {{payload.fromUsername}}, thanks for your message!',
99
+ },
100
+ parseMode('HTML / MarkdownV2 / none.'),
101
+ {
102
+ name: 'replyToMessageId',
103
+ label: 'Reply to message ID',
104
+ type: 'string',
105
+ description: "Optional. Numeric id of the message you're replying to. For a fully-required quote-reply, use the dedicated 'Reply to message' op instead.",
106
+ placeholder: '{{payload.messageId}}',
107
+ },
108
+ ],
109
+ },
110
+ replyToMessage: {
111
+ label: 'Reply to message',
112
+ description: "Quote-reply with the threaded indicator above the bot's message",
113
+ apiMethod: 'POST /sendMessage (reply_to_message_id)',
114
+ supportsInlineKeyboard: true,
115
+ params: [
116
+ chatId(),
117
+ messageId('Message ID to reply to', "Numeric id of the user message you're quote-replying to. From a Telegram trigger this is typically {{payload.message.message_id}}.", '{{payload.message.message_id}}'),
118
+ {
119
+ name: 'text',
120
+ label: 'Text',
121
+ type: 'text',
122
+ required: true,
123
+ rows: 4,
124
+ description: 'Reply body. HTML by default — escape <, >, & in user-supplied content.',
125
+ placeholder: 'Yes, I understood your question about {{payload.text}}',
126
+ },
127
+ parseMode('HTML / MarkdownV2 / none.'),
128
+ ],
129
+ },
130
+ sendPhoto: {
131
+ label: 'Send photo',
132
+ description: 'Image by URL or _file ref',
133
+ apiMethod: 'POST /sendPhoto',
134
+ supportsInlineKeyboard: true,
135
+ params: [
136
+ chatId(),
137
+ {
138
+ name: 'photo',
139
+ label: 'Photo URL or file_id',
140
+ type: 'string',
141
+ required: true,
142
+ description: 'Public URL Telegram can fetch, or a file_id from a prior send.',
143
+ placeholder: '{{payload.attachment._file.downloadUrl}}',
144
+ },
145
+ caption('Optional. HTML supported. Max 1024 chars.', 3),
146
+ ],
147
+ },
148
+ sendDocument: {
149
+ label: 'Send document',
150
+ description: 'File of any type by URL',
151
+ apiMethod: 'POST /sendDocument',
152
+ supportsInlineKeyboard: true,
153
+ params: [
154
+ chatId(),
155
+ {
156
+ name: 'document',
157
+ label: 'Document URL or file_id',
158
+ type: 'string',
159
+ required: true,
160
+ description: 'Public URL or file_id. Max 50MB when fetching by URL.',
161
+ placeholder: '{{payload.file._file.downloadUrl}}',
162
+ },
163
+ caption('Optional. HTML supported.', 2),
164
+ ],
165
+ },
166
+ sendVoice: {
167
+ label: 'Send voice',
168
+ description: 'Send a voice bubble — pass a URL / file_id (or chain from the AI Node synthesize_voice tool)',
169
+ apiMethod: 'POST /sendVoice',
170
+ params: [
171
+ chatId(),
172
+ {
173
+ name: 'voice',
174
+ label: 'Voice URL or file_id',
175
+ type: 'string',
176
+ required: true,
177
+ description: 'OGG/OPUS audio URL Telegram can fetch, or a file_id from a prior /sendVoice for cheap re-sends. To synthesize from text, add the AI Node `synthesize_voice` tool (Tools tab → + Add → Voice synthesis) and pass its result here via `{{payload.voice}}` or chain through the LLM.',
178
+ placeholder: 'https://… .ogg or {{payload.voice}}',
179
+ },
180
+ ],
181
+ },
182
+ sendAndWaitForResponse: {
183
+ label: 'Send & wait for response',
184
+ description: 'Sends a message with Approve / Disapprove buttons (and optional free-text reply) — pipeline pauses until the user responds',
185
+ apiMethod: 'POST /sendMessage + PendingApproval',
186
+ params: [
187
+ chatId(),
188
+ {
189
+ name: 'text',
190
+ label: 'Message',
191
+ type: 'text',
192
+ required: true,
193
+ rows: 4,
194
+ description: 'Body of the prompt shown above the Approve / Disapprove buttons. HTML supported by default.',
195
+ placeholder: 'Approve the ${{payload.amount}} payment to {{payload.vendor}}?',
196
+ },
197
+ parseMode('HTML / MarkdownV2 / none.'),
198
+ {
199
+ name: 'approveLabel',
200
+ label: 'Approve button label',
201
+ type: 'string',
202
+ description: 'Tap = approved.',
203
+ placeholder: 'Approve',
204
+ pairedWithNext: true,
205
+ },
206
+ {
207
+ name: 'disapproveLabel',
208
+ label: 'Disapprove button label',
209
+ type: 'string',
210
+ description: "Tap = rejected. Hidden when 'Include disapprove' is off.",
211
+ placeholder: 'Reject',
212
+ },
213
+ {
214
+ name: 'includeDisapprove',
215
+ label: 'Include disapprove button',
216
+ type: 'boolean',
217
+ default: true,
218
+ description: 'Off = approval-only (single button). Default on.',
219
+ },
220
+ {
221
+ name: 'allowFreeText',
222
+ label: 'Allow free-text reply',
223
+ type: 'boolean',
224
+ default: false,
225
+ description: "When on, a plain text reply in the same chat also resolves the wait — _waitResponse.responseText carries the typed message. Useful for capturing context ('approved with comment'). Default off.",
226
+ },
227
+ {
228
+ name: 'timeoutMinutes',
229
+ label: 'Timeout (minutes)',
230
+ type: 'number',
231
+ min: 1,
232
+ max: 20160,
233
+ default: 1440,
234
+ description: 'Pipeline auto-resumes (rejected) after this many minutes if no response. Default 1440 (24h).',
235
+ },
236
+ ],
237
+ },
238
+ editMessageText: {
239
+ label: 'Edit message',
240
+ description: 'Replace text of a previously-sent message',
241
+ apiMethod: 'POST /editMessageText',
242
+ supportsInlineKeyboard: true,
243
+ params: [
244
+ chatId(),
245
+ messageId('Message ID', 'Id of the message to edit (from a prior sendMessage result).'),
246
+ {
247
+ name: 'text',
248
+ label: 'New text',
249
+ type: 'text',
250
+ required: true,
251
+ rows: 3,
252
+ description: 'Replacement text for the message.',
253
+ placeholder: 'Updated content',
254
+ },
255
+ // Sin ayuda a propósito: es la única op donde el formulario nunca la ha
256
+ // enseñado, y no me invento copy nueva al mudar el campo.
257
+ parseMode(),
258
+ ],
259
+ },
260
+ deleteMessage: {
261
+ label: 'Delete message',
262
+ description: 'Remove a message from a chat',
263
+ apiMethod: 'POST /deleteMessage',
264
+ params: [
265
+ chatId(),
266
+ messageId('Message ID', 'Id of the message to delete.'),
267
+ ],
268
+ },
269
+ answerCallbackQuery: {
270
+ label: 'Answer callback',
271
+ description: 'Acknowledge an inline-button press (REQUIRED after callback_query)',
272
+ apiMethod: 'POST /answerCallbackQuery',
273
+ params: [
274
+ {
275
+ name: 'callbackQueryId',
276
+ label: 'Callback query ID',
277
+ type: 'string',
278
+ required: true,
279
+ description: 'From the trigger payload — usually {{payload.callbackQueryId}} when responding to an inline-button press.',
280
+ placeholder: '{{payload.callbackQueryId}}',
281
+ },
282
+ {
283
+ name: 'text',
284
+ label: 'Text',
285
+ type: 'string',
286
+ description: 'Optional. Toast (or alert if showAlert is on). Max 200 chars.',
287
+ placeholder: 'Saved!',
288
+ },
289
+ {
290
+ name: 'showAlert',
291
+ label: 'Show as alert',
292
+ type: 'boolean',
293
+ default: false,
294
+ description: 'When on, shows a modal alert above the chat instead of a toast.',
295
+ },
296
+ ],
297
+ },
298
+ forwardMessage: {
299
+ label: 'Forward message',
300
+ description: 'Forward an existing message to another chat',
301
+ apiMethod: 'POST /forwardMessage',
302
+ // Las tres sin ayuda: las etiquetas ya dicen cuál es cuál, y así estaba.
303
+ params: [
304
+ {
305
+ name: 'chatId',
306
+ label: 'Destination chat ID',
307
+ type: 'string',
308
+ required: true,
309
+ placeholder: '{{payload.targetChatId}}',
310
+ },
311
+ {
312
+ name: 'fromChatId',
313
+ label: 'Source chat ID',
314
+ type: 'string',
315
+ required: true,
316
+ placeholder: '{{payload.chatId}}',
317
+ },
318
+ messageId('Source message ID'),
319
+ ],
320
+ },
321
+ sendPoll: {
322
+ label: 'Send poll',
323
+ description: 'Multi-option poll (regular or quiz)',
324
+ apiMethod: 'POST /sendPoll',
325
+ params: [
326
+ chatId(),
327
+ {
328
+ name: 'question',
329
+ label: 'Question',
330
+ type: 'text',
331
+ required: true,
332
+ rows: 2,
333
+ description: 'Max 300 chars.',
334
+ placeholder: 'Which option?',
335
+ },
336
+ {
337
+ name: 'options',
338
+ label: 'Options (2-10)',
339
+ type: 'linesList',
340
+ required: true,
341
+ rows: 5,
342
+ description: 'One per line. Max 100 chars each.',
343
+ placeholder: 'Option A\nOption B\nOption C',
344
+ },
345
+ {
346
+ name: 'isAnonymous',
347
+ label: 'Anonymous',
348
+ type: 'boolean',
349
+ // Defecto ENCENDIDO, como includeDisapprove.
350
+ default: true,
351
+ description: 'Default true. Set false to expose voter identities.',
352
+ },
353
+ ],
354
+ },
355
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Telegram AI Toolkit — las herramientas que un telegramAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * **Se escribe una sola vez**, como el de Sheets. Antes vivía dos veces: una
6
+ * copia en `dashboard/components/telegram-actions/telegram-operations-schemas.ts`
7
+ * y otra en `api/src/mcp-servers/toolkit-specs.ts`, las dos a mano, las dos con
8
+ * el mismo TODO escrito pidiendo justo esto. Y ya habían derivado: el
9
+ * `messageId` de `reply_telegram_message` era `number` en el dashboard y
10
+ * `string` en la api, mientras en las otras tres ops que lo llevan era `number`
11
+ * en las dos. Aquí es `number` y punto.
12
+ *
13
+ * Hay una TERCERA copia que también desaparece: la página de detalle pintaba la
14
+ * lista de las diez herramientas como diez `<li>` escritos a mano. Ahora sale de
15
+ * aquí, contada con `.length`, que es lo que evita que un día anuncie diez y
16
+ * exponga otra cosa.
17
+ *
18
+ * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para que pueda
19
+ * usarse sin adaptador. Los textos van en inglés porque los lee el modelo y
20
+ * quien mire la lista de herramientas del servidor MCP.
21
+ */
22
+ import type { TelegramOperation } from './telegram-operations';
23
+ export interface TelegramToolkitParameter {
24
+ name: string;
25
+ type: 'string' | 'number' | 'boolean';
26
+ description: string;
27
+ required: boolean;
28
+ }
29
+ export interface TelegramToolkitSpec {
30
+ operation: TelegramOperation;
31
+ /** Etiqueta corta de la fila en la lista de herramientas. */
32
+ label: string;
33
+ /**
34
+ * Nombre con el que el LLM llama a la herramienta. Lleva `_telegram_` para
35
+ * no chocar con el `send_email` de Gmail ni el `create_event` de Calendar
36
+ * cuando un mismo AI Node tiene varios toolkits encendidos.
37
+ */
38
+ toolName: string;
39
+ /** Descripción (más reglas de uso) que ve el LLM. */
40
+ description: string;
41
+ parameters: TelegramToolkitParameter[];
42
+ /**
43
+ * Encabezado de grupo para la lista de la página de detalle. Las filas
44
+ * consecutivas que comparten grupo se pintan bajo un único título, así que el
45
+ * ORDEN de este array es el que agrupa. Es el mismo que enseñaba el JSX que
46
+ * sustituye.
47
+ */
48
+ group?: string;
49
+ }
50
+ export declare const TELEGRAM_TOOLKIT_SPECS: TelegramToolkitSpec[];
51
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
52
+ export declare const TELEGRAM_TOOLKIT_BY_TOOL_NAME: Record<string, TelegramToolkitSpec>;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * Telegram AI Toolkit — las herramientas que un telegramAction expone cuando
4
+ * `aiEnabled` está encendido.
5
+ *
6
+ * **Se escribe una sola vez**, como el de Sheets. Antes vivía dos veces: una
7
+ * copia en `dashboard/components/telegram-actions/telegram-operations-schemas.ts`
8
+ * y otra en `api/src/mcp-servers/toolkit-specs.ts`, las dos a mano, las dos con
9
+ * el mismo TODO escrito pidiendo justo esto. Y ya habían derivado: el
10
+ * `messageId` de `reply_telegram_message` era `number` en el dashboard y
11
+ * `string` en la api, mientras en las otras tres ops que lo llevan era `number`
12
+ * en las dos. Aquí es `number` y punto.
13
+ *
14
+ * Hay una TERCERA copia que también desaparece: la página de detalle pintaba la
15
+ * lista de las diez herramientas como diez `<li>` escritos a mano. Ahora sale de
16
+ * aquí, contada con `.length`, que es lo que evita que un día anuncie diez y
17
+ * exponga otra cosa.
18
+ *
19
+ * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para que pueda
20
+ * usarse sin adaptador. Los textos van en inglés porque los lee el modelo y
21
+ * quien mire la lista de herramientas del servidor MCP.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = void 0;
25
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
26
+ /*
27
+ * `sendAndWaitForResponse` NO está aquí, y es una decisión, no un olvido: la op
28
+ * para el pipeline durante minutos u horas esperando que alguien pulse un botón,
29
+ * y por la vía de una herramienta no espera — el modelo recibe algo inmediato y
30
+ * acaba diciendo que ya se aprobó. Se usa en modo single-op, con un Conditional
31
+ * detrás leyendo `_waitResponse.approved`. La espera se resuelve por
32
+ * `service-triggers.processTelegramPush`, donde el LLM no participa.
33
+ *
34
+ * Ninguna lleva `destructive`. `delete_telegram_message` lo merecería —el
35
+ * toolkit de Drive marca sus borrados— pero hoy la spec de Telegram no tiene ese
36
+ * campo en ninguna de las dos capas, y añadirlo cambiaría cuándo el AI Node pide
37
+ * confirmación. Eso es otra decisión, no parte de mudar el esquema de sitio.
38
+ */
39
+ exports.TELEGRAM_TOOLKIT_SPECS = [
40
+ {
41
+ operation: 'sendMessage',
42
+ label: 'Send message',
43
+ toolName: 'send_telegram_message',
44
+ group: 'Sending',
45
+ description: 'Send a text message via the Telegram bot to a chat. Use for replies, notifications, status updates, or any text-only delivery. ' +
46
+ 'Supports HTML formatting (default parse mode). For inline buttons, supply the replyMarkup arg as a 2D array of buttons. ' +
47
+ 'USAGE RULES: ' +
48
+ '(1) chatId is REQUIRED — when replying inside a flow triggered by a Telegram message, this is normally `{{payload.chatId}}` from the trigger payload. ' +
49
+ '(2) Telegram caps text at 4096 chars. Split longer messages or use sendDocument with a text file. ' +
50
+ '(3) For a quote-style reply that points at a SPECIFIC user message, prefer `reply_telegram_message` so Telegram renders the threaded indicator — this op sends a standalone message.',
51
+ parameters: [
52
+ p('chatId', 'Target chat id (number) or @channelusername (string).'),
53
+ p('text', 'Message text. HTML by default — escape <, >, & in user content.'),
54
+ p('parseMode', 'Optional. "HTML" (default), "MarkdownV2", or "none".', false),
55
+ p('replyToMessageId', 'Optional. Reply to a specific message id.', false),
56
+ ],
57
+ },
58
+ {
59
+ operation: 'replyToMessage',
60
+ label: 'Reply to message',
61
+ toolName: 'reply_telegram_message',
62
+ group: 'Sending',
63
+ description: 'Reply to a SPECIFIC user message with the quote indicator visible. Telegram renders the bot\'s reply with a small "replying to..." card above it pointing back at the quoted message. ' +
64
+ 'USAGE RULES: ' +
65
+ '(1) Use this in group chats where multiple users post in parallel — the indicator makes it clear which user the bot is answering. In 1:1 DMs only use it when context disambiguates between several recent questions. ' +
66
+ '(2) messageId is REQUIRED — pass the id of the user\'s message you\'re responding to (typically `{{payload.message.message_id}}` or `{{payload.message_id}}` from the trigger). ' +
67
+ '(3) chatId is REQUIRED and must match the chat the original message lives in. ' +
68
+ '(4) Same Telegram /sendMessage webhook underneath — text limit (4096 chars) and parse modes (HTML / MarkdownV2 / none) work the same.',
69
+ parameters: [
70
+ p('chatId', 'Target chat id (number) or @channelusername — must match the chat of the quoted message.'),
71
+ // `number`, como en las otras tres ops que llevan messageId. La api lo
72
+ // declaraba `string` por un cuarto argumento que se quedó sin escribir.
73
+ p('messageId', 'Numeric id of the message to quote-reply to. Telegram renders the threaded indicator from this.', true, 'number'),
74
+ p('text', 'Reply text. HTML by default.'),
75
+ p('parseMode', 'Optional. "HTML" (default), "MarkdownV2", or "none".', false),
76
+ ],
77
+ },
78
+ {
79
+ operation: 'sendPhoto',
80
+ label: 'Send photo',
81
+ toolName: 'send_telegram_photo',
82
+ group: 'Sending',
83
+ description: 'Send a photo to a chat. The photo arg accepts a public URL (Telegram fetches it) or a Telegram file_id from a previously-sent photo (cheap re-send). ' +
84
+ 'For HW-stored files use the `_file.downloadUrl` template. Max 10MB photo size when fetching by URL. Caption is optional and supports HTML.',
85
+ parameters: [
86
+ p('chatId', 'Target chat id or @channelusername.'),
87
+ p('photo', "Public URL OR file_id from a prior send. URLs must be reachable from Telegram's servers."),
88
+ p('caption', 'Optional caption (max 1024 chars, HTML supported).', false),
89
+ p('parseMode', 'Optional caption parse mode: HTML / MarkdownV2 / none.', false),
90
+ ],
91
+ },
92
+ {
93
+ operation: 'sendDocument',
94
+ label: 'Send document',
95
+ toolName: 'send_telegram_document',
96
+ group: 'Sending',
97
+ description: 'Send a document (any file type) to a chat. Same URL-or-file_id contract as sendPhoto. Max 50MB when Telegram fetches by URL.',
98
+ parameters: [
99
+ p('chatId', 'Target chat id or @channelusername.'),
100
+ p('document', 'Public URL OR file_id.'),
101
+ p('caption', 'Optional caption (HTML supported).', false),
102
+ p('parseMode', 'Optional caption parse mode.', false),
103
+ ],
104
+ },
105
+ {
106
+ operation: 'sendVoice',
107
+ label: 'Send voice',
108
+ toolName: 'send_telegram_voice',
109
+ group: 'Sending',
110
+ description: 'Send a voice message — Telegram renders it as a voice bubble with waveform. ' +
111
+ 'USAGE RULES: ' +
112
+ '(1) chatId is REQUIRED — usually `{{payload.chatId}}` from the trigger. ' +
113
+ '(2) voice is REQUIRED — pass an OGG/OPUS audio URL Telegram can fetch, OR a `file_id` from a prior /sendVoice for cheap re-sends. ' +
114
+ '(3) To synthesize from text, FIRST call the `synthesize_voice` tool — it returns a JSON `_file` ref; pass its `downloadUrl` string here as `voice`. NEVER pass the raw `_file` object; Telegram needs a string. ' +
115
+ '(4) Use this when the user sent voice and wants voice back, or when the operator framed the bot as voice-first. For text replies prefer send_telegram_message.',
116
+ parameters: [
117
+ p('chatId', 'Target chat id (number) or @channelusername.'),
118
+ p('voice', 'OGG/OPUS audio URL or Telegram file_id. Use the `downloadUrl` returned by synthesize_voice when chaining from text.'),
119
+ ],
120
+ },
121
+ {
122
+ operation: 'sendPoll',
123
+ label: 'Send poll',
124
+ toolName: 'send_telegram_poll',
125
+ group: 'Sending',
126
+ description: 'Send a poll to a chat. The options arg is an array of strings (2-10 options). For quiz-style polls (single correct answer), pass type="quiz" and a numeric correctOptionId. Polls require group/supergroup/channel chats — DMs reject them.',
127
+ parameters: [
128
+ p('chatId', 'Target chat id.'),
129
+ p('question', 'Poll question (max 300 chars).'),
130
+ p('options', 'Array of 2-10 answer strings. Pass as a JSON array: ["Option A","Option B"]. Each option max 100 chars.'),
131
+ p('isAnonymous', 'Optional. Default true. Set false to expose voter identities.', false, 'boolean'),
132
+ p('type', 'Optional. "regular" (default) or "quiz".', false),
133
+ p('correctOptionId', 'Required when type="quiz" — zero-indexed correct answer.', false, 'number'),
134
+ ],
135
+ },
136
+ {
137
+ operation: 'forwardMessage',
138
+ label: 'Forward message',
139
+ toolName: 'forward_telegram_message',
140
+ group: 'Sending',
141
+ description: 'Forward an existing message from one chat to another. Preserves the original sender attribution. Use for announcements, escalations, or copying a message between groups the bot is in.',
142
+ parameters: [
143
+ p('chatId', 'Destination chat id.'),
144
+ p('fromChatId', 'Source chat id.'),
145
+ p('messageId', 'Source message id.', true, 'number'),
146
+ ],
147
+ },
148
+ {
149
+ operation: 'editMessageText',
150
+ label: 'Edit message text',
151
+ toolName: 'edit_telegram_message',
152
+ group: 'Modify / acknowledge',
153
+ description: 'Replace the text of a previously-sent message. Use for progress updates, fixing typos, removing inline buttons after a press. The bot can only edit messages it sent within the last 48h.',
154
+ parameters: [
155
+ p('chatId', 'Chat id of the message.'),
156
+ p('messageId', 'Message id to edit (returned from a prior sendMessage).', true, 'number'),
157
+ p('text', 'New text for the message.'),
158
+ p('parseMode', 'Optional parse mode.', false),
159
+ ],
160
+ },
161
+ {
162
+ operation: 'deleteMessage',
163
+ label: 'Delete message',
164
+ toolName: 'delete_telegram_message',
165
+ group: 'Modify / acknowledge',
166
+ description: 'Delete a message. Bots can delete their own messages anytime; messages by users only when the bot has Delete Messages admin permission in that chat. Within 48h for bot-own messages in private chats.',
167
+ parameters: [
168
+ p('chatId', 'Chat id where the message lives.'),
169
+ p('messageId', 'Message id to delete.', true, 'number'),
170
+ ],
171
+ },
172
+ {
173
+ operation: 'answerCallbackQuery',
174
+ label: 'Answer callback query',
175
+ toolName: 'answer_telegram_callback',
176
+ group: 'Modify / acknowledge',
177
+ description: 'REQUIRED after a callback_query trigger fires (an inline-keyboard button press) — without this, the user sees a forever-spinner on the button. The text optionally shows as a toast or alert above the chat.',
178
+ parameters: [
179
+ p('callbackQueryId', 'The callback_query.id from the trigger payload — usually `{{payload.callbackQueryId}}`.'),
180
+ p('text', 'Optional toast/alert text shown to the user (max 200 chars).', false),
181
+ p('showAlert', 'Optional. true = modal alert; false (default) = toast notification.', false, 'boolean'),
182
+ ],
183
+ },
184
+ ];
185
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
186
+ exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.TELEGRAM_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.52.9",
3
+ "version": "1.52.11",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",