Asistente de Telegram inteligente
Este es unAI Chatbot, Multimodal AIflujo de automatización del dominio deautomatización que contiene 36 nodos.Utiliza principalmente nodos como Set, Code, Switch, Postgres, Telegram. Construye un asistente de Telegram inteligente usando IA de Gemini, memoria de PostgreSQL y enrutamiento dinámico
- •Información de conexión de la base de datos PostgreSQL
- •Bot Token de Telegram
- •Clave de API de Google Gemini
Nodos utilizados (36)
Categoría
{
"meta": {
"instanceId": "50be75eaab016244f302e16f06394e6613d664bfc61e8cd41452474a0de6a3ee",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "fccc9f50-71fa-4e25-9b15-8fd540ddc2fa",
"name": "Selector de Modelo",
"type": "@n8n/n8n-nodes-langchain.modelSelector",
"position": [
800,
1536
],
"parameters": {
"rules": {
"rule": [
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "976d83bb-7e9e-4aab-9722-25a9e238164f",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.output.difficulty }}",
"rightValue": 1
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "1e68688d-73fe-47c1-9b35-a1e226220bcd",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.output.difficulty }}",
"rightValue": 2
}
]
},
"modelIndex": 2
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "61d58197-db59-4cd7-bc41-bbeaf5e7b069",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.output.difficulty }}",
"rightValue": 3
}
]
},
"modelIndex": 3
}
]
},
"numberInputs": 3
},
"typeVersion": 1
},
{
"id": "cf5b2ea0-78c5-47bf-a3c1-4c59c9a32f76",
"name": "Analizador de Salida Estructurada",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
336,
1504
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"difficulty\": {\n \"type\": \"integer\",\n \"enum\": [1, 2, 3]\n },\n \"context\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\"difficulty\", \"context\"]\n}\n"
},
"typeVersion": 1.3
},
{
"id": "3a60caa7-eb1a-4bbd-88fc-55d7a3ffae29",
"name": "Gemini 2.5 Flash Lite",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
720,
1728
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-flash-lite"
},
"credentials": {
"googlePalmApi": {
"id": "to92mdfNe3L6sBae",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "412d35ed-cd49-4d20-b425-2f556ef175b1",
"name": "Gemini 2.5 Flash",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
880,
1728
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"id": "to92mdfNe3L6sBae",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "2cc40e2c-a2be-435e-8540-9235efc41e08",
"name": "Gemini 2.5 Pro",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1024,
1728
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-pro"
},
"credentials": {
"googlePalmApi": {
"id": "to92mdfNe3L6sBae",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "35f56404-6998-4952-a7f3-8716712bf96a",
"name": "Obtener Memoria de Chat",
"type": "n8n-nodes-base.postgres",
"onError": "continueRegularOutput",
"position": [
-256,
1328
],
"parameters": {
"limit": 25,
"table": {
"__rl": true,
"mode": "list",
"value": "chat_memory",
"cachedResultName": "chat_memory"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"options": {},
"operation": "select"
},
"credentials": {
"postgres": {
"id": "eQR2NFRag48wov9g",
"name": "Postgres account"
}
},
"typeVersion": 2.6,
"alwaysOutputData": true
},
{
"id": "5e6058bc-a3b8-4827-954e-85e3a000a986",
"name": "MarkdownV2",
"type": "n8n-nodes-base.code",
"position": [
1136,
1264
],
"parameters": {
"jsCode": "/**\n * MarkdownV2-safe formatter + auto-chunker for Telegram (n8n Code node)\n * --------------------------------------------------------------------\n * - Allows: *bold*, _italic_, ||spoiler||, [label](url)\n * - Escapes everything else for Telegram MarkdownV2\n * - Validates/normalizes URLs\n * - Converts \"# Heading\" lines to bold titles\n * - Splits long messages into <= 4096-char chunks (uses a 4000-char budget)\n * - Outputs one item per chunk so the Telegram node sends all parts\n *\n * Recommended: Run this node in \"Run Once for All Items\".\n */\n\nconst MAX_TELEGRAM = 4096;\nconst SAFE_BUDGET = 4000; // small margin to avoid edge overflows\n\n// ============ MarkdownV2 helpers ============\nfunction escapeMarkdownV2(text) {\n if (!text) return '';\n return String(text).replace(/([\\\\_*[\\]()~`>#+\\-=|{}.!])/g, '\\\\$1');\n}\n\nfunction escapeForUrl(url) {\n return String(url).replace(/[)\\\\]/g, '\\\\$&');\n}\n\nfunction normalizeAndValidateUrl(url) {\n let raw = String(url || '').trim();\n try {\n const u = new URL(raw);\n return u.toString();\n } catch {}\n // Try https:// for bare domains\n const domainLike = /^[a-z0-9.-]+\\.[a-z]{2,}([/:?#].*)?$/i.test(raw);\n if (domainLike) {\n try {\n const u2 = new URL('https://' + raw);\n return u2.toString();\n } catch {}\n }\n return null;\n}\n\nfunction normalizeHeadings(text) {\n // Turn \"# Title\" → \"*Title*\"\n return text.replace(/^(#{1,6})\\s+(.*)$/gm, (m, hashes, title) => `*${title.trim()}*`);\n}\n\nfunction normalizeCommonMd(text) {\n return String(text)\n .replace(/\\*\\*([\\s\\S]*?)\\*\\*/g, '*$1*') // **bold** → *bold*\n .replace(/__([\\s\\S]*?)__/g, '_$1_'); // __italic__ → _italic_\n}\n\n/**\n * Convert incoming text to Telegram-safe MarkdownV2.\n */\nfunction processMarkdownV2Safe(inputText) {\n if (!inputText) return '';\n\n let text = normalizeCommonMd(String(inputText));\n text = normalizeHeadings(text);\n\n const placeholders = { links: [], bolds: [], italics: [], spoilers: [] };\n\n // Links: keep safe via placeholders during escaping\n text = text.replace(/\\[([^\\]\\n]+)\\]\\(([^)]+)\\)/g, (m, label, url) => {\n const normalizedUrl = normalizeAndValidateUrl(url);\n if (!normalizedUrl) return escapeMarkdownV2(label);\n const idx = placeholders.links.length;\n const ph = `⟬L${idx}⟭`;\n const safeLabel = escapeMarkdownV2(label);\n const safeUrl = escapeForUrl(normalizedUrl);\n placeholders.links.push(`[${safeLabel}](${safeUrl})`);\n return ph;\n });\n\n // Bold\n text = text.replace(/\\*([\\s\\S]+?)\\*/g, (m, inner) => {\n const idx = placeholders.bolds.length;\n const ph = `⟬B${idx}⟭`;\n placeholders.bolds.push(`*${escapeMarkdownV2(inner)}*`);\n return ph;\n });\n\n // Italic\n text = text.replace(/_([\\s\\S]+?)_/g, (m, inner) => {\n const idx = placeholders.italics.length;\n const ph = `⟬I${idx}⟭`;\n placeholders.italics.push(`_${escapeMarkdownV2(inner)}_`);\n return ph;\n });\n\n // Spoilers\n text = text.replace(/\\|\\|([\\s\\S]+?)\\|\\|/g, (m, inner) => {\n const idx = placeholders.spoilers.length;\n const ph = `⟬S${idx}⟭`;\n placeholders.spoilers.push(`||${escapeMarkdownV2(inner)}||`);\n return ph;\n });\n\n // Escape everything else\n text = escapeMarkdownV2(text);\n\n // Restore placeholders\n placeholders.links.forEach((md, i) => { text = text.replace(`⟬L${i}⟭`, md); });\n placeholders.bolds.forEach((md, i) => { text = text.replace(`⟬B${i}⟭`, md); });\n placeholders.italics.forEach((md, i) => { text = text.replace(`⟬I${i}⟭`, md); });\n placeholders.spoilers.forEach((md, i) => { text = text.replace(`⟬S${i}⟭`, md); });\n\n return text;\n}\n\n// ============ Chunking helpers ============\n/**\n * Split text into Telegram-safe chunks <= maxLen.\n * Prefers paragraph boundaries, then sentence boundaries, then words.\n * Falls back to hard cuts only when unavoidable (e.g., extremely long URL).\n */\nfunction chunkForTelegram(text, maxLen = SAFE_BUDGET) {\n if (!text || text.length <= maxLen) return [text || ''];\n\n const parts = [];\n let buffer = '';\n\n const flush = () => {\n if (buffer) {\n parts.push(buffer);\n buffer = '';\n }\n };\n\n // 1) Paragraph-level packing\n const paragraphs = text.split(/\\n{2,}/);\n for (const pRaw of paragraphs) {\n const p = pRaw; // keep paragraph as-is\n const candidate = buffer ? buffer + '\\n\\n' + p : p;\n if (candidate.length <= maxLen) {\n buffer = candidate;\n continue;\n }\n if (p.length <= maxLen) {\n flush();\n buffer = p;\n continue;\n }\n\n // 2) Sentence-level packing (paragraph is still too big)\n flush();\n const sentences = p.split(/(?<=[.!?…])\\s+(?=[^\\s])/u);\n let sBuf = '';\n for (const s of sentences) {\n const sCandidate = sBuf ? sBuf + ' ' + s : s;\n if (sCandidate.length <= maxLen) {\n sBuf = sCandidate;\n continue;\n }\n if (s.length <= maxLen) {\n if (sBuf) parts.push(sBuf);\n sBuf = s;\n continue;\n }\n\n // 3) Word-level packing (sentence is still too big)\n if (sBuf) { parts.push(sBuf); sBuf = ''; }\n let wBuf = '';\n const words = s.split(/\\s+/);\n for (const w of words) {\n const wCandidate = wBuf ? wBuf + ' ' + w : w;\n if (wCandidate.length <= maxLen) {\n wBuf = wCandidate;\n continue;\n }\n if (w.length <= maxLen) {\n if (wBuf) parts.push(wBuf);\n wBuf = w;\n continue;\n }\n // 4) Hard split (extremely long token, e.g., massive URL)\n if (wBuf) { parts.push(wBuf); wBuf = ''; }\n const re = new RegExp(`.{1,${maxLen}}`, 'g');\n const hardPieces = w.match(re) || [];\n parts.push(...hardPieces);\n }\n if (wBuf) parts.push(wBuf);\n }\n if (sBuf) parts.push(sBuf);\n }\n if (buffer) parts.push(buffer);\n\n // Final safety pass: trim chunks that might still exceed MAX_TELEGRAM\n return parts.flatMap(part => {\n if (part.length <= MAX_TELEGRAM) return [part];\n const re = new RegExp(`.{1,${SAFE_BUDGET}}`, 'g');\n return part.match(re) || [];\n });\n}\n\n// ============ Main ============\nconst inputItems = $input.all();\nconst out = [];\n\nfor (const item of inputItems) {\n const j = item.json || {};\n const raw =\n j.message ?? j.output ?? j.text ?? j.content ?? '';\n\n const formatted = processMarkdownV2Safe(raw);\n const chunks = chunkForTelegram(formatted, SAFE_BUDGET);\n\n chunks.forEach((chunk, idx) => {\n out.push({\n json: {\n ...j,\n message: chunk,\n message_part_index: idx + 1,\n message_parts_total: chunks.length,\n },\n binary: item.binary,\n });\n });\n}\n\nreturn out;\n"
},
"typeVersion": 2
},
{
"id": "6ce0f61c-03d1-4bef-827f-ac3c80f48939",
"name": "Enviar mensaje de texto",
"type": "n8n-nodes-base.telegram",
"position": [
1312,
1264
],
"webhookId": "1ad49e91-7894-4fb1-ba93-73ba3ec4e666",
"parameters": {
"text": "={{ $json.message }}",
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"additionalFields": {
"parse_mode": "MarkdownV2",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"id": "rzhkYoexl5hHvqnv",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "662e6beb-7498-42c2-ba0c-6530c100bf97",
"name": "Corregir tipo MIME",
"type": "n8n-nodes-base.code",
"position": [
-1184,
1328
],
"parameters": {
"jsCode": "// --- Mapa Extendido de Tipos MIME ---\n// Una lista completa para cubrir la mayoría de los formatos de archivo comunes.\nconst mimeMap = {\n // --- Formatos de Documentos ---\n 'pdf': 'application/pdf',\n 'txt': 'text/plain',\n 'rtf': 'application/rtf',\n 'csv': 'text/csv',\n 'html': 'text/html',\n 'htm': 'text/html',\n 'json': 'application/json',\n 'xml': 'application/xml', // 'text/xml' también es válido pero 'application/xml' es más común\n 'yaml': 'application/x-yaml',\n 'yml': 'application/x-yaml',\n\n // --- Formatos de Microsoft Office ---\n 'doc': 'application/msword',\n 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'xls': 'application/vnd.ms-excel',\n 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'ppt': 'application/vnd.ms-powerpoint',\n 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'pub': 'application/vnd.ms-publisher',\n\n // --- Formatos de OpenOffice / LibreOffice ---\n 'odt': 'application/vnd.oasis.opendocument.text',\n 'ods': 'application/vnd.oasis.opendocument.spreadsheet',\n 'odp': 'application/vnd.oasis.opendocument.presentation',\n 'odg': 'application/vnd.oasis.opendocument.graphics',\n\n // --- Formatos de Apple iWork ---\n 'pages': 'application/vnd.apple.pages',\n 'numbers': 'application/vnd.apple.numbers',\n 'key': 'application/vnd.apple.keynote',\n\n // --- Formatos de Imagen ---\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'gif': 'image/gif',\n 'webp': 'image/webp',\n 'svg': 'image/svg+xml',\n 'bmp': 'image/bmp',\n 'ico': 'image/vnd.microsoft.icon',\n 'tif': 'image/tiff',\n 'tiff': 'image/tiff',\n 'heic': 'image/heic',\n 'heif': 'image/heif',\n\n // --- Formatos de Audio ---\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'oga': 'audio/ogg',\n 'ogg': 'audio/ogg',\n 'flac': 'audio/flac',\n 'm4a': 'audio/mp4',\n 'aac': 'audio/aac',\n 'opus': 'audio/opus',\n 'wma': 'audio/x-ms-wma',\n 'mid': 'audio/midi',\n 'midi': 'audio/midi',\n\n // --- Formatos de Video ---\n 'mp4': 'video/mp4',\n 'mov': 'video/quicktime',\n 'webm': 'video/webm',\n 'mpeg': 'video/mpeg',\n 'mpg': 'video/mpeg',\n 'avi': 'video/x-msvideo',\n 'wmv': 'video/x-ms-wmv',\n 'flv': 'video/x-flv',\n 'mkv': 'video/x-matroska',\n\n // --- Formatos de Archivos y Compresión ---\n 'zip': 'application/zip',\n 'rar': 'application/vnd.rar',\n '7z': 'application/x-7z-compressed',\n 'tar': 'application/x-tar',\n 'gz': 'application/gzip',\n 'bz2': 'application/x-bzip2',\n\n // --- Otros Formatos ---\n 'epub': 'application/epub+zip',\n 'ics': 'text/calendar',\n 'vcf': 'text/vcard',\n 'js': 'text/javascript',\n 'css': 'text/css',\n 'sh': 'application/x-sh',\n 'py': 'text/x-python',\n};\n\n// --- Lógica de Procesamiento (sin cambios) ---\n\n// Obtenemos todos los items que llegan al nodo\nconst items = $input.all();\n\n// Iteramos sobre cada item para procesarlo\nfor (const item of items) {\n // Verificamos que el item tenga datos binarios para procesar\n if (item.binary && item.binary['data']) {\n // Obtenemos el nombre del archivo de forma segura\n const fileName = item.binary['data'].fileName || '';\n if (!fileName) {\n continue; // Si no hay nombre de archivo, pasamos al siguiente item\n }\n\n // Extraemos la extensión del archivo de forma robusta\n const extension = fileName.slice((fileName.lastIndexOf(\".\") - 1 >>> 0) + 2).toLowerCase();\n\n // Buscamos la extensión en nuestro mapa\n const newMimeType = mimeMap[extension];\n\n // Si encontramos una coincidencia en el mapa, actualizamos el mimeType\n if (newMimeType) {\n if(item.binary['data'].mimeType !== newMimeType) {\n console.log(`Cambiando mimeType para '${fileName}' de '${item.binary['data'].mimeType}' a '${newMimeType}'.`);\n item.binary['data'].mimeType = newMimeType;\n }\n }\n }\n}\n\n// Devolvemos todos los items, modificados o no\nreturn items;"
},
"typeVersion": 2
},
{
"id": "07281d83-6c94-4952-819e-288a4435da24",
"name": "Escribiendo…",
"type": "n8n-nodes-base.telegram",
"position": [
-1744,
1184
],
"webhookId": "b768e407-5f22-4b80-a8a9-2d255b9bf815",
"parameters": {
"chatId": "={{ $json.message.chat.id }}",
"operation": "sendChatAction"
},
"credentials": {
"telegramApi": {
"id": "rzhkYoexl5hHvqnv",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "e211d4af-9ebd-4524-84ef-934349b6b155",
"name": "obtener_mensaje (mensaje de Audio/Video)",
"type": "n8n-nodes-base.set",
"position": [
-848,
1328
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d8935452-fe20-469d-a68d-1aad056cb8dd",
"name": "message",
"type": "string",
"value": "=Voice message description:{{ $json.candidates?.[0]?.content?.parts?.[0]?.text || $json.content?.parts?.[0]?.text }}"
},
{
"id": "93f1bba1-1180-404a-93ca-c34cf1d1b7ac",
"name": "chat_id",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "9c28c37b-1d37-4f23-90a3-872f5e3870a7",
"name": "Analizar mensaje de voz",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
-1024,
1328
],
"parameters": {
"text": "What's in this audio message from telegram user?",
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-2.5-pro",
"cachedResultName": "models/gemini-2.5-pro"
},
"options": {},
"resource": "audio",
"inputType": "binary",
"operation": "analyze"
},
"credentials": {
"googlePalmApi": {
"id": "to92mdfNe3L6sBae",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "ea646e8c-e6d0-47ea-8743-45eade4c1c4d",
"name": "obtener_mensaje (texto)",
"type": "n8n-nodes-base.set",
"position": [
-1184,
1152
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "801ec600-22ad-4a94-a2b4-ae72eb271df0",
"name": "message",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.text }}"
},
{
"id": "263071fb-bcdf-42b0-bb46-71b75fa0bf2a",
"name": "chat_id",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1ca88076-5c56-4561-bb8a-fdf1db080d2a",
"name": "Enrutador de Mensajes de Entrada1",
"type": "n8n-nodes-base.switch",
"position": [
-1600,
1312
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Text",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "fcb767ee-565e-4b56-a54e-6f97f739fc24",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
"rightValue": ""
}
]
},
"renameOutput": true
},
{
"outputKey": "Voice Message",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1016c40-f8f2-4e08-8ec8-5cdb88f5c87a",
"operator": {
"type": "object",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Telegram Trigger').item.json.message.voice }}",
"rightValue": ""
}
]
},
"renameOutput": true
}
]
},
"options": {
"ignoreCase": false,
"fallbackOutput": "extra",
"allMatchingOutputs": true
}
},
"typeVersion": 3.2
},
{
"id": "298fa06c-d3fc-4d20-a2d4-fe879a01527c",
"name": "Descargar Mensaje de Voz",
"type": "n8n-nodes-base.telegram",
"position": [
-1360,
1328
],
"webhookId": "d28e2f59-d662-4e75-8bac-11fdc3fbb295",
"parameters": {
"fileId": "={{ $('Telegram Trigger').item.json.message.voice.file_id }}",
"resource": "file",
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "rzhkYoexl5hHvqnv",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "5a1dc986-6bfe-4400-bdbf-d4f5817e0f7e",
"name": "Telegram Disparador",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
-1888,
1328
],
"webhookId": "1aecee74-ba0f-4fe2-a302-578312187154",
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "rzhkYoexl5hHvqnv",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "0188bc12-4fc1-4149-8c80-edf8e80009ec",
"name": "Normalizar entrada",
"type": "n8n-nodes-base.set",
"position": [
-464,
1328
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3c2fa4f9-079c-4729-9737-66ce8f42029f",
"name": "message",
"type": "string",
"value": "={{ $json.message }}"
},
{
"id": "b6e57068-8ece-4725-b07d-1b00069943b0",
"name": "chat_id",
"type": "string",
"value": "={{ $json.chat_id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4db66ea5-0817-4326-9991-ab6a905ec0ee",
"name": "Agregador",
"type": "n8n-nodes-base.aggregate",
"position": [
-48,
1328
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "message"
}
]
}
},
"typeVersion": 1
},
{
"id": "a4ce42c2-d1cc-4233-9ee7-2aac9a5f0c45",
"name": "Google Gemini 2.5 Flash Lite",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
192,
1504
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-flash-lite"
},
"credentials": {
"googlePalmApi": {
"id": "to92mdfNe3L6sBae",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "5c2cd387-3b15-4869-87da-6873437a0d33",
"name": "obtener_mensaje_de_error",
"type": "n8n-nodes-base.set",
"position": [
-1184,
1504
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d8935452-fe20-469d-a68d-1aad056cb8dd",
"name": "message",
"type": "string",
"value": "=It was not possible to process the file.File type not supported."
},
{
"id": "38ba2498-2141-4a04-a22a-64563fe2ee6f",
"name": "chat_id",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d31898fc-4d10-43ea-bc5d-402ac29f3f4b",
"name": "Agente",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
800,
1328
],
"parameters": {
"text": "=Context: {{ $json.output.context }}\nUser request: {{ $('Normalize input').item.json.message }}",
"options": {
"returnIntermediateSteps": true
},
"promptType": "define"
},
"typeVersion": 2.1
},
{
"id": "ee146b55-17b6-414c-be6b-f8416ece7075",
"name": "Nota Adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1984,
1104
],
"parameters": {
"color": 5,
"width": 1312,
"height": 624,
"content": ""
},
"typeVersion": 1
},
{
"id": "e677f7d7-2cfc-476d-bdc0-08b4cf17cb4d",
"name": "Nota Adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-576,
1248
],
"parameters": {
"color": 3,
"width": 1216,
"height": 400,
"content": ""
},
"typeVersion": 1
},
{
"id": "d36b2018-ca63-4c1f-a40e-46d1452753e2",
"name": "Al hacer clic en 'Ejecutar flujo de trabajo'",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1888,
880
],
"parameters": {},
"typeVersion": 1
},
{
"id": "451eb06f-5b47-44c5-86fa-20587cf89870",
"name": "Crear Tabla de Memoria de Chat",
"type": "n8n-nodes-base.postgres",
"position": [
-1696,
880
],
"parameters": {
"query": "CREATE TABLE IF NOT EXISTS public.chat_memory (\n id SERIAL PRIMARY KEY,\n session_id VARCHAR(255) NOT NULL,\n message TEXT DEFAULT 'Could not get data'\n) TABLESPACE pg_default;\n\nCREATE INDEX IF NOT EXISTS chat_memory_session_id_idx \nON public.chat_memory USING btree (session_id) \nTABLESPACE pg_default;\n",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "eQR2NFRag48wov9g",
"name": "Postgres account"
}
},
"typeVersion": 2.6
},
{
"id": "fb4c1415-dd98-4944-8c12-e69f8d445be7",
"name": "Nota Adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1984,
848
],
"parameters": {
"color": 7,
"width": 512,
"height": 208,
"content": ""
},
"typeVersion": 1
},
{
"id": "50ac5adc-ce42-4f35-84ac-cf8e2bea5d38",
"name": "Nota Adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
1248
],
"parameters": {
"color": 6,
"width": 880,
"height": 624,
"content": ""
},
"typeVersion": 1
},
{
"id": "97859eb8-951f-4abf-8b89-63299b456304",
"name": "Actualizar Memoria de Chat (Usuario y Agente)",
"type": "n8n-nodes-base.postgres",
"position": [
1136,
1424
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "chat_memory",
"cachedResultName": "chat_memory"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"message": "=User: {{ $('Normalize input').item.json.message }}\nAgent: {{ $json.output }}\n",
"session_id": "={{ $('Normalize input').item.json.chat_id }}"
},
"schema": [
{
"id": "id",
"type": "number",
"display": true,
"removed": true,
"required": false,
"displayName": "id",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "session_id",
"type": "string",
"display": true,
"required": true,
"displayName": "session_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "message",
"type": "string",
"display": true,
"required": false,
"displayName": "message",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"credentials": {
"postgres": {
"id": "eQR2NFRag48wov9g",
"name": "Postgres account"
}
},
"typeVersion": 2.6
},
{
"id": "632c9d0c-5ab3-46a6-bb4d-0d4bd3341fde",
"name": "Resumir y Categorizar",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
192,
1328
],
"parameters": {
"text": "=Chat Memory: {{ $('Aggregate').item.json.message.join('\\n') }}\n\nUser Request: {{ $('Normalize input').item.json.message }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are a system that analyzes a user request and its chat history.\n\n## Your goals:\n1. Summarize the chat history into only the relevant context for the current user request.\n2. Determine the difficulty of the request:\n - 1: Very simple (short answers, reminders, basic actions).\n - 2: Medium (some reasoning, structured outputs, combining info).\n - 3: Complex (multi-step reasoning, ambiguous queries, coding-level reasoning).\n\n## Output Format:\nYou must return JSON strictly following this schema:\n\n{\n \"difficulty\": 1 | 2 | 3,\n \"context\": \"string - summary of the relevant history\"\n}\n"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "8979f56c-2809-426d-acda-9d455df8836e",
"name": "Nota Adhesiva4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1984,
576
],
"parameters": {
"color": 7,
"width": 512,
"height": 256,
"content": "## ⚙️ Database Initialization (Chat Memory Table)\n\n**Purpose:** \nThis section is responsible for creating and preparing the `chat_memory` table in PostgreSQL. It ensures that chat interactions are stored persistently for later use in context management, summarization, and categorization.\n"
},
"typeVersion": 1
},
{
"id": "cf047f39-85cd-41a6-8d2a-76ecec83b6f2",
"name": "Nota Adhesiva5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1392,
432
],
"parameters": {
"color": 5,
"width": 704,
"height": 640,
"content": "## 🔵 Input Handling (Telegram Trigger & Preprocessing)\n\n### Purpose:\nThis section receives and processes incoming messages from Telegram. It detects whether the input is text, voice, or unsupported media.\n\n### Key Steps:\n\n* **Telegram Trigger** – Listens for new updates (messages from users).\n* **Input Message Router** – Classifies whether the input is:\n * Text message\n * Voice message\n * Unsupported media → redirects to an error handler\n* **Voice Handling** – If it’s a voice message:\n * Downloads the file\n * Normalizes MIME type\n * Sends it to speech-to-text analysis\n * Extracts the text message from audio\n* **Error Handling** – If the input is not supported, an error message is returned.\n\n### Optional Extension:\nFor multimodal and media group support (multiple files, images, videos), you can extend this section with the following template: https://n8n.io/workflows/7455-process-multiple-media-files-in-telegram-with-gemini-ai-and-postgresql-database/"
},
"typeVersion": 1
},
{
"id": "3557d1c3-c155-430a-a11b-5bc901111d46",
"name": "Nota Adhesiva6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-352,
496
],
"parameters": {
"color": 3,
"width": 784,
"height": 720,
"content": "## 🔴 Chat Memory Retrieval & Context Optimization\n\n### Purpose:\nThis section retrieves past interactions from the database, aggregates them into a single string, and summarizes them before passing them to the agent.\n\n### Key Steps:\n\n* **Normalize Input** – Standardizes the message before processing.\n* **Get Chat Memory** – Queries PostgreSQL for previous messages linked to the `session_id`.\n* **Aggregate** – Combines past interactions into one text block.\n* **Summarize & Categorize** – Uses Google Gemini 2.5 Flash Lite (low-cost, low-latency) to:\n * Summarize the chat history.\n * Extract relevant context.\n * Categorize the difficulty level of the task.\n\n### Why Summarization First?\n\n* Only relevant and important context is passed to the agent.\n* Reduces token usage, speeding up responses and lowering costs.\n* Prevents irrelevant history from cluttering the model’s attention.\n\n### Advantages:\n\n* Saves processing time and cost by using a lightweight summarization model.\n* Produces more accurate and focused responses.\n* Optimizes memory queries by reducing data size passed downstream."
},
"typeVersion": 1
},
{
"id": "42fdc20f-2c28-4826-8704-20ffb8792bd7",
"name": "Nota Adhesiva7",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
384
],
"parameters": {
"color": 6,
"width": 784,
"height": 832,
"content": "## 🟣 Agent Processing & Response Delivery\n\n### Purpose:\nThis section routes the request to the appropriate Gemini model depending on task difficulty, generates the final response, and sends it back to Telegram. It also updates the chat memory.\n\n### Key Steps:\n\n* **Agent Node** – Receives:\n * User request\n * Relevant summarized context\n * Difficulty level\n* **Model Selector** – Dynamically chooses the model:\n * **Difficulty 1** → Gemini 2.5 Flash Lite (fastest & cheapest)\n * **Difficulty 2** → Gemini 2.5 Flash\n * **Difficulty 3** → Gemini 2.5 Pro (advanced reasoning)\n* **Markdown Formatting** – Converts model output into Telegram-compatible Markdown V2.\n* **Send Message** – Sends the response back to Telegram.\n* **Update Chat Memory** – Inserts a single row containing both the user and agent message.\n\n### Why single-row storage for user & agent?\n\n* Since the message is sent to Telegram first and then the memory is updated, this saves an average of 0.3 seconds in response time.\n* Reduces the number of queries (`Get Chat Memory` fetches fewer rows).\n* Updates are faster since both messages are stored at once.\n* Optimizes resource usage while maintaining full conversational context.\n\n### Advantages:\n\n* Cost-optimized model usage (only advanced models used when strictly necessary).\n* Faster response times by minimizing context size and database operations.\n"
},
"typeVersion": 1
},
{
"id": "1c29f5cf-c95e-40fc-9dc0-362dcda71574",
"name": "Nota Adhesiva8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2560,
1104
],
"parameters": {
"color": 4,
"width": 544,
"height": 592,
"content": "## ✅ Key Benefits of This Architecture\n\n### Cost Efficiency\n\n* Uses cheaper LLMs for summarization and simple queries.\n* Reserves expensive models only for complex tasks.\n\n### Performance Optimization\n\n* Reduced token consumption through summarization.\n* Faster memory queries due to single-row storage.\n\n### Flexibility & Scalability\n\n* Easy integration of multimodal inputs (images, audio, video).\n* Modular structure allows replacing/upgrading models or database logic.\n\n### User Experience\n\n* Quick Telegram responses.\n* Consistent memory of past interactions without overwhelming the LLM."
},
"typeVersion": 1
},
{
"id": "84019987-a071-44d0-8e4b-d80c838007dc",
"name": "Nota Adhesiva9",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2496,
848
],
"parameters": {
"color": 2,
"width": 368,
"height": 192,
"content": "## Acknowledgment\n\nA special thank you to Davide for the inspiration behind this template. \nHis work on the [**AI Orchestrator that dynamically selects models based on input type**](https://n8n.io/workflows/7004-ai-orchestrator-dynamically-selects-models-based-on-input-type/) served as a foundational guide for this architecture.\n"
},
"typeVersion": 1
},
{
"id": "0c425ab2-41d3-43c9-86bb-9c7cbfe968a4",
"name": "Nota Adhesiva10",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
1648
],
"parameters": {
"color": 2,
"width": 368,
"height": 192,
"content": "## 💡 Need Assistance?\n\nIf you’d like help customizing or extending this workflow, feel free to reach out: \n\n📧 Email: [johnsilva11031@gmail.com](mailto:johnsilva11031@gmail.com) \n🔗 LinkedIn: [John Alejandro Silva Rodríguez](https://www.linkedin.com/in/john-alejandro-silva-rodriguez-48093526b/)\n"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"d31898fc-4d10-43ea-bc5d-402ac29f3f4b": {
"main": [
[
{
"node": "5e6058bc-a3b8-4827-954e-85e3a000a986",
"type": "main",
"index": 0
},
{
"node": "97859eb8-951f-4abf-8b89-63299b456304",
"type": "main",
"index": 0
}
]
]
},
"662e6beb-7498-42c2-ba0c-6530c100bf97": {
"main": [
[
{
"node": "9c28c37b-1d37-4f23-90a3-872f5e3870a7",
"type": "main",
"index": 0
}
]
]
},
"4db66ea5-0817-4326-9991-ab6a905ec0ee": {
"main": [
[
{
"node": "632c9d0c-5ab3-46a6-bb4d-0d4bd3341fde",
"type": "main",
"index": 0
}
]
]
},
"5e6058bc-a3b8-4827-954e-85e3a000a986": {
"main": [
[
{
"node": "6ce0f61c-03d1-4bef-827f-ac3c80f48939",
"type": "main",
"index": 0
}
]
]
},
"2cc40e2c-a2be-435e-8540-9235efc41e08": {
"ai_languageModel": [
[
{
"node": "fccc9f50-71fa-4e25-9b15-8fd540ddc2fa",
"type": "ai_languageModel",
"index": 2
}
]
]
},
"fccc9f50-71fa-4e25-9b15-8fd540ddc2fa": {
"ai_languageModel": [
[
{
"node": "d31898fc-4d10-43ea-bc5d-402ac29f3f4b",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"35f56404-6998-4952-a7f3-8716712bf96a": {
"main": [
[
{
"node": "4db66ea5-0817-4326-9991-ab6a905ec0ee",
"type": "main",
"index": 0
}
]
]
},
"0188bc12-4fc1-4149-8c80-edf8e80009ec": {
"main": [
[
{
"node": "35f56404-6998-4952-a7f3-8716712bf96a",
"type": "main",
"index": 0
}
]
]
},
"412d35ed-cd49-4d20-b425-2f556ef175b1": {
"ai_languageModel": [
[
{
"node": "fccc9f50-71fa-4e25-9b15-8fd540ddc2fa",
"type": "ai_languageModel",
"index": 1
}
]
]
},
"5a1dc986-6bfe-4400-bdbf-d4f5817e0f7e": {
"main": [
[
{
"node": "1ca88076-5c56-4561-bb8a-fdf1db080d2a",
"type": "main",
"index": 0
},
{
"node": "07281d83-6c94-4952-819e-288a4435da24",
"type": "main",
"index": 0
}
]
]
},
"5c2cd387-3b15-4869-87da-6873437a0d33": {
"main": [
[
{
"node": "0188bc12-4fc1-4149-8c80-edf8e80009ec",
"type": "main",
"index": 0
}
]
]
},
"ea646e8c-e6d0-47ea-8743-45eade4c1c4d": {
"main": [
[
{
"node": "0188bc12-4fc1-4149-8c80-edf8e80009ec",
"type": "main",
"index": 0
}
]
]
},
"9c28c37b-1d37-4f23-90a3-872f5e3870a7": {
"main": [
[
{
"node": "e211d4af-9ebd-4524-84ef-934349b6b155",
"type": "main",
"index": 0
}
]
]
},
"3a60caa7-eb1a-4bbd-88fc-55d7a3ffae29": {
"ai_languageModel": [
[
{
"node": "fccc9f50-71fa-4e25-9b15-8fd540ddc2fa",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"1ca88076-5c56-4561-bb8a-fdf1db080d2a": {
"main": [
[
{
"node": "ea646e8c-e6d0-47ea-8743-45eade4c1c4d",
"type": "main",
"index": 0
}
],
[
{
"node": "298fa06c-d3fc-4d20-a2d4-fe879a01527c",
"type": "main",
"index": 0
}
],
[
{
"node": "5c2cd387-3b15-4869-87da-6873437a0d33",
"type": "main",
"index": 0
}
]
]
},
"298fa06c-d3fc-4d20-a2d4-fe879a01527c": {
"main": [
[
{
"node": "662e6beb-7498-42c2-ba0c-6530c100bf97",
"type": "main",
"index": 0
}
]
]
},
"632c9d0c-5ab3-46a6-bb4d-0d4bd3341fde": {
"main": [
[
{
"node": "d31898fc-4d10-43ea-bc5d-402ac29f3f4b",
"type": "main",
"index": 0
}
]
]
},
"cf5b2ea0-78c5-47bf-a3c1-4c59c9a32f76": {
"ai_outputParser": [
[
{
"node": "632c9d0c-5ab3-46a6-bb4d-0d4bd3341fde",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"a4ce42c2-d1cc-4233-9ee7-2aac9a5f0c45": {
"ai_languageModel": [
[
{
"node": "632c9d0c-5ab3-46a6-bb4d-0d4bd3341fde",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"e211d4af-9ebd-4524-84ef-934349b6b155": {
"main": [
[
{
"node": "0188bc12-4fc1-4149-8c80-edf8e80009ec",
"type": "main",
"index": 0
}
]
]
},
"97859eb8-951f-4abf-8b89-63299b456304": {
"main": [
[]
]
},
"d36b2018-ca63-4c1f-a40e-46d1452753e2": {
"main": [
[
{
"node": "451eb06f-5b47-44c5-86fa-20587cf89870",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Avanzado - Chatbot de IA, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
John Alejandro SIlva
@alejandro-silvaDetail-oriented professional with a dual degree in Systems Engineering and Business Administration and international experience in technology and process improvement. I specialize in workflow automation with n8n, API integration, programming, and data analysis. Known for strong analytical skills and clear technical documentation.
Compartir este flujo de trabajo