Assistant Telegram intelligent
Ceci est unAI Chatbot, Multimodal AIworkflow d'automatisation du domainecontenant 36 nœuds.Utilise principalement des nœuds comme Set, Code, Switch, Postgres, Telegram. Construire un assistant Telegram intelligent avec l'IA Gemini, la mémoire PostgreSQL et le routage dynamique
- •Informations de connexion à la base de données PostgreSQL
- •Token Bot Telegram
- •Clé API Google Gemini
Nœuds utilisés (36)
Catégorie
{
"meta": {
"instanceId": "50be75eaab016244f302e16f06394e6613d664bfc61e8cd41452474a0de6a3ee",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "fccc9f50-71fa-4e25-9b15-8fd540ddc2fa",
"name": "Sélecteur de Modèle",
"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": "Analyseur de Sortie Structurée",
"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": "Obtenir la Mémoire 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": "Envoyer un message texte",
"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": "Corriger le type 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": "En train d'écrire…",
"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": "obtenir_message (message 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": "Analyser le message vocal",
"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": "obtenir_message (texte)",
"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": "Routeur de Message d'Entrée1",
"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": "Télécharger le Message Vocal",
"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 Déclencheur",
"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": "Normaliser l'entrée",
"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": "Agréger",
"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": "obtenir_message_erreur",
"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": "Agent",
"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": "Note Adhésive",
"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": "Note Adhésive1",
"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": "Lors du clic sur 'Exécuter le workflow'",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1888,
880
],
"parameters": {},
"typeVersion": 1
},
{
"id": "451eb06f-5b47-44c5-86fa-20587cf89870",
"name": "Créer une Table de Mémoire 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": "Note Adhésive2",
"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": "Note Adhésive3",
"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": "Mettre à Jour la Mémoire de Chat (Utilisateur et Agent)",
"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": "Résumer et Catégoriser",
"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": "Note Adhésive4",
"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": "Note Adhésive5",
"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": "Note Adhésive6",
"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": "Note Adhésive7",
"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": "Note Adhésive8",
"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": "Note Adhésive9",
"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": "Note Adhésive10",
"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
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Chatbot IA, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
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.
Partager ce workflow