Plantilla de GiggleGPTBot
Este es unAI Chatbot, Multimodal AIflujo de automatización del dominio deautomatización que contiene 27 nodos.Utiliza principalmente nodos como If, Code, Switch, Postgres, Telegram. Usar OpenRouter para crear un bot de Telegram ingenioso con humor, burlas y estadísticas impulsadas por IA
- •Información de conexión de la base de datos PostgreSQL
- •Bot Token de Telegram
Nodos utilizados (27)
Categoría
{
"id": "TfKEOWRNsYI093kR",
"meta": {
"instanceId": "673dd365761c86615255caaaae908ad0f2b40ed6e6f64e1be5631254544e65ca"
},
"name": "GiggleGPTBot Template",
"tags": [],
"nodes": [
{
"id": "c4dbddca-9165-401f-9a96-995a958a425a",
"name": "Webhook Telegram",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
-1136,
64
],
"webhookId": "3cb5a3b8-5bae-4708-be95-172a7d50cb48",
"parameters": {
"updates": [
"*"
],
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "GNUE3W3kc9bGnXyL",
"name": "GiggleGPTBot"
}
},
"typeVersion": 1
},
{
"id": "5369e144-45cd-4a99-9fa6-284ae24ec585",
"name": "Comandos de OpenRouter",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
-512,
736
],
"parameters": {
"model": "openai/gpt-oss-120b",
"options": {}
},
"credentials": {
"openRouterApi": {
"id": "ONSHmBroionT6JFr",
"name": "OpenRouter account"
}
},
"typeVersion": 1
},
{
"id": "5ee25a3f-a9c7-4994-a9a9-315b9eeec5af",
"name": "Inicializar base de datos",
"type": "n8n-nodes-base.postgres",
"position": [
-1248,
-128
],
"parameters": {
"query": "CREATE TABLE IF NOT EXISTS user_messages (\n id SERIAL PRIMARY KEY,\n user_id BIGINT NOT NULL,\n username VARCHAR(255),\n first_name VARCHAR(255),\n chat_id BIGINT NOT NULL,\n message_text TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS bot_responses (\n id SERIAL PRIMARY KEY,\n user_id BIGINT NOT NULL,\n chat_id BIGINT NOT NULL,\n user_message TEXT,\n bot_response TEXT,\n response_type VARCHAR(50),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS bot_commands (\n id SERIAL PRIMARY KEY,\n user_id BIGINT NOT NULL,\n chat_id BIGINT NOT NULL,\n command VARCHAR(50) NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS message_reactions (\n id SERIAL PRIMARY KEY,\n message_id BIGINT NOT NULL,\n chat_id BIGINT NOT NULL,\n user_id BIGINT NOT NULL,\n emoji VARCHAR(10),\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS scheduled_posts (\n id SERIAL PRIMARY KEY,\n chat_id BIGINT NOT NULL,\n message_text TEXT,\n post_type VARCHAR(50),\n scheduled_time TIME,\n is_active BOOLEAN DEFAULT true,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE user_stats (\n user_id BIGINT NOT NULL,\n chat_id BIGINT NOT NULL,\n messages_count INT DEFAULT 0,\n commands_count INT DEFAULT 0,\n reactions_received INT DEFAULT 0,\n last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (user_id, chat_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_user_messages_chat_user ON user_messages(chat_id, user_id);\nCREATE INDEX IF NOT EXISTS idx_bot_responses_chat_user ON bot_responses(chat_id, user_id);\nCREATE INDEX IF NOT EXISTS idx_commands_user ON bot_commands(user_id, command);\nCREATE INDEX IF NOT EXISTS idx_reactions_message ON message_reactions(message_id);\nCREATE INDEX idx_user_stats_activity ON user_stats(last_activity DESC);\nCREATE INDEX idx_user_stats_messages ON user_stats(messages_count DESC);\n",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "2f7789fd-645c-436f-91ef-04cb2143aa7d",
"name": "Registrar mensaje + estadísticas",
"type": "n8n-nodes-base.postgres",
"onError": "continueRegularOutput",
"position": [
-960,
-192
],
"parameters": {
"query": "INSERT INTO user_messages (user_id, username, first_name, chat_id, message_text) \nVALUES ({{ $('Webhook Telegram').item.json.message.from.id }}, \n '{{ ($('Webhook Telegram').item.json.message.from.username || '').replace(/'/g, \"''\") }}', \n '{{ ($('Webhook Telegram').item.json.message.from.first_name || '').replace(/'/g, \"''\") }}', \n {{ $('Webhook Telegram').item.json.message.chat.id }}, \n '{{ ($('Webhook Telegram').item.json.message.text || '').replace(/'/g, \"''\") }}');\n\nINSERT INTO user_stats (user_id, chat_id, messages_count, last_activity)\nVALUES ({{ $('Webhook Telegram').item.json.message.from.id }}, \n {{ $('Webhook Telegram').item.json.message.chat.id }}, \n 1, CURRENT_TIMESTAMP)\nON CONFLICT (user_id, chat_id) \nDO UPDATE SET \n messages_count = user_stats.messages_count + 1,\n last_activity = CURRENT_TIMESTAMP;",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "5676c8d7-a456-4f12-be6a-2cd5c2ec41e6",
"name": "Agregar una programación",
"type": "n8n-nodes-base.postgres",
"position": [
-1120,
400
],
"parameters": {
"query": "INSERT INTO scheduled_posts (chat_id, post_type, scheduled_time) VALUES\n(-1002837353897, 'morning_joke', '06:00:00'),\n(-1002837353897, 'random_wisdom', '17:00:00'),\n(-1002837353897, 'daily_motivation', '09:00:00');",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "5b7c3300-fa5a-43f5-881f-db48314be81c",
"name": "Programación",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-992,
544
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 * * * *"
}
]
}
},
"typeVersion": 1
},
{
"id": "756e467d-835a-4166-8972-4edf50b0007f",
"name": "Obtener publicaciones programadas",
"type": "n8n-nodes-base.postgres",
"position": [
-800,
544
],
"parameters": {
"query": "SELECT chat_id, post_type \nFROM scheduled_posts \nWHERE is_active = true \nAND EXTRACT(HOUR FROM scheduled_time) = EXTRACT(HOUR FROM CURRENT_TIME);",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "84247455-9eed-424d-8ee4-84ece8f10d54",
"name": "Historial de chat",
"type": "n8n-nodes-base.postgres",
"position": [
-768,
-96
],
"parameters": {
"query": "SELECT \n message_text,\n username,\n first_name,\n created_at,\n 'user' as message_type\nFROM user_messages \nWHERE chat_id = {{ $('Webhook Telegram').item.json.message.chat.id }}\n\nUNION ALL\n\nSELECT \n bot_response as message_text,\n 'GiggleGPTBot' as username,\n 'GiggleGPTBot' as first_name,\n created_at,\n 'bot' as message_type\nFROM bot_responses \nWHERE chat_id = {{ $('Webhook Telegram').item.json.message.chat.id }}\n\nORDER BY created_at DESC \nLIMIT 15;",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "86872198-4d19-4c3e-b5dd-14dda87a9139",
"name": "Análisis de menciones",
"type": "n8n-nodes-base.code",
"position": [
-592,
-96
],
"parameters": {
"jsCode": "const telegramData = $('Webhook Telegram').first().json;\nconst chatHistory = $('Chat history').all();\n\nconst userId = telegramData.message.from.id;\nconst userName = telegramData.message.from.first_name || telegramData.message.from.username || 'Anonymous';\nlet userMessage = telegramData.message.text;\nconst chatId = telegramData.message.chat.id;\n\nconst commandMatch = userMessage.match(/^\\/(\\w+)(?:@\\w+)?(?:\\s+(.*))?/);\nconst command = commandMatch ? commandMatch[1].toLowerCase() : '';\n\nconst originalMessage = userMessage;\nuserMessage = userMessage.replace(/@GiggleGPTBot/gi, '').trim();\nconst isEmptyAfterTag = !userMessage || userMessage.length === 0;\n\nconst recentMessages = chatHistory.map(item => {\n const msgText = item.json.message_text || '';\n const author = item.json.message_type === 'bot' ? 'GiggleGPTBot' : \n (item.json.first_name || item.json.username || 'Anonymous');\n return `${author}: ${msgText}`;\n}).slice(0, 15).join('\\n'); \n\nlet contentType = 'mixed';\nconst lowerMessage = userMessage.toLowerCase();\nif (lowerMessage.includes('joke') || lowerMessage.includes('funny')) {\n contentType = 'funny';\n} else if (lowerMessage.includes('motivat') || lowerMessage.includes('motivat')) {\n contentType = 'inspiring';\n}\n\nif (isEmptyAfterTag) {\n const types = ['funny', 'inspiring', 'mixed'];\n contentType = types[Math.floor(Math.random() * types.length)];\n}\n\nconst now = new Date();\nconst hour = now.getHours();\nlet timeContext = '';\nif (hour < 6) timeContext = 'night';\nelse if (hour < 12) timeContext = 'morning';\nelse if (hour < 18) timeContext = 'day';\nelse timeContext = 'evening';\n\nreturn [{\n json: {\n userId,\n userName,\n userMessage: isEmptyAfterTag ? 'Random phrase' : userMessage,\n originalMessage,\n chatId,\n contentType,\n command,\n timeContext,\n recentMessages,\n chatType: telegramData.message.chat.type,\n isRandomRequest: isEmptyAfterTag\n }\n}];"
},
"typeVersion": 2
},
{
"id": "60de834b-47c8-428a-9d28-87ade8fa8a43",
"name": "Obtener estadísticas de usuario",
"type": "n8n-nodes-base.postgres",
"position": [
-768,
64
],
"parameters": {
"query": "SELECT \n us.user_id,\n us.messages_count,\n us.commands_count,\n 0 as reactions_received,\n us.last_activity,\n (SELECT COUNT(*) FROM bot_responses br WHERE br.user_id = us.user_id AND br.chat_id = us.chat_id) as responses_count\nFROM user_stats us\nWHERE us.chat_id = {{ $json.message.chat.id }}\n AND us.user_id = {{ $json.message.from.id }}\nLIMIT 1;",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "d57380bc-5dc5-4130-a767-463bbf81931c",
"name": "Obtener usuarios principales",
"type": "n8n-nodes-base.postgres",
"position": [
-768,
224
],
"parameters": {
"query": "SELECT \n us.messages_count,\n us.commands_count,\n CONCAT(\n COALESCE((SELECT first_name FROM user_messages WHERE user_id = us.user_id LIMIT 1), 'User'),\n ' (@',\n COALESCE((SELECT username FROM user_messages WHERE user_id = us.user_id LIMIT 1), 'no_username'),\n ')'\n ) as user_name\nFROM user_stats us\nWHERE us.chat_id = {{ $json.message.from.id }}\n AND us.messages_count > 0\nORDER BY (us.messages_count + us.commands_count * 2) DESC\nLIMIT 10;",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "b12f28fc-6fb0-45ca-8348-202bf5a78332",
"name": "Respuesta de IA a comando",
"type": "@n8n/n8n-nodes-langchain.agent",
"maxTries": 2,
"position": [
-240,
-80
],
"parameters": {
"text": "=Command: /{{ $json.command }}\nUser: {{ $json.userName }}\nChat context: {{ $json.recentMessages }}\nTime: {{ $json.timeContext }}\n\nGenerate {{ $json.command === 'joke' ? 'a witty joke' : $json.command === 'inspire' ? 'a motivating line' : $json.command === 'roast' ? 'a sharp roast with folksy sarcasm' : 'a random interesting line' }} in your signature style with subtle humor and playful teasing.\n",
"options": {
"systemMessage": "You are GiggleGPTBot — a witty bot with a big heart. Your job: deliver short humor and folksy wisdom with light irony.\n\nStyle:\n• Modern spin on proverbs and sayings.\n• Gentle irony and kind subtext.\n• Emojis: allowed but sparing (0–2 per reply).\n\nAvoid:\n• Crudeness, profanity, cliché \"Soviet-style\" advice.\n• Rambling, long, or confusing imagery.\n• Explaining the joke or meta-notes about the prompt.\n\nReply format:\n• Strictly 1–2 sentences (3 only in rare cases).\n• One clear idea or joke.\n• At most one simple metaphor.\n• Must be easily understood at first read.\n\nCommands:\n/joke — clever short joke with light irony.\n/inspire — gentle, smiley motivation (no pomp).\n/random — unexpected witty line in folksy wisdom style.\n/roast — sharp roast with sarcastic tone. Some edge allowed, mild profanity ok, but NO insults targeting gender, race, nationality, or other protected traits.\n\nExamples:\n/joke → My neighbor runs every morning. I run too… my eyes over the bus schedule 🚌.\n/inspire → Even a snail has a goal — and you’ve got legs that are faster 🐌➡️🏃♂️.\n/random → Sometimes the kettle boils longer than love… yet both return to warmth ☕❤️.\n/roast → “Your alarm screams like it’s got a mortgage. You? Just laziness and snoring — also on credit 😏”.\n\nSelf-check before sending:\n• Did I stay within 1–2 sentences?\n• Is there light irony or a clear joke/wisdom?\n• Is it instantly clear to a regular person?\n• No extra fluff, confusion, or weird imagery?\n\nIf not, shorten and rewrite to a simple, clear, witty reply."
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "e1345b2b-f824-40cb-b01d-48031d417b1b",
"name": "Generar respuesta informativa",
"type": "n8n-nodes-base.code",
"position": [
-576,
144
],
"parameters": {
"jsCode": "const messageText = $('Webhook Telegram').first().json.message.text\nconst commandMatch = messageText.match(/^\\/(\\w+)(?:@\\w+)?(?:\\s+(.*))?/);\nconst commandData = commandMatch ? commandMatch[1].toLowerCase() : '';\n\nconst safe = (v, d = '') => (v === null || v === undefined ? d : v);\n\nlet userStatsItem;\ntry {\n userStatsItem = $input.first();\n} catch (_) {\n userStatsItem = undefined;\n}\n\nlet topUsersItems = [];\ntry {\n const topNode = $('Get top users');\n if (topNode && typeof topNode.all === 'function') {\n const raw = topNode.all() || [];\n topUsersItems = raw\n .map(i => (i && i.json) ? i.json : null)\n .filter(Boolean);\n }\n} catch (error) {\n console.log('Top users unavailable:', error.message);\n}\n\nconst userId = safe($('Webhook Telegram').first().json.message.from.id);\nconst userName = safe($('Webhook Telegram').first().json.message.chat.first_name, 'User');\nconst chatId = safe($('Webhook Telegram').first().json.message.chat.id);\nconst command = safe(commandData, '');\n\nlet responseText = '';\n\nconst formatDateRu = (iso) => {\n try {\n const d = new Date(iso);\n if (isNaN(d.getTime())) return 'Unknown';\n return d.toLocaleString('en-US', { dateStyle: 'short', timeStyle: 'short' });\n } catch {\n return 'Unknown';\n }\n};\n\nconst calcActivity = (u) => {\n const msgs = Number(u.messages_count || 0);\n const cmds = Number(u.commands_count || 0);\n return msgs + cmds * 2;\n};\n\nswitch (command) {\n case 'stats': {\n if (userStatsItem && userStatsItem.json) {\n const s = userStatsItem.json;\n responseText =\n `📊 Statistics ${userName}:\\n` +\n `💬 Messages: ${Number(s.messages_count || 0)}\\n` +\n `⚡ Commands: ${Number(s.commands_count || 0)}\\n` +\n `📅 Last activity: ${s.last_activity ? formatDateRu(s.last_activity) : 'Unknown'}`;\n } else {\n responseText =\n `📊 Statistics ${userName}:\\n` +\n `There is no data yet. Keep chatting and the statistics will appear! 💬`;\n }\n break;\n }\n\n case 'top': {\n if (topUsersItems.length > 0) {\n const sorted = [...topUsersItems]\n .sort((a, b) => calcActivity(b) - calcActivity(a))\n .slice(0, 10);\n\n const lines = sorted.map((u, idx) => {\n const uname = u.user_name || 'User';\n const msgs = Number(u.messages_count || 0);\n const cmds = Number(u.commands_count || 0);\n const total = calcActivity(u);\n return `${idx + 1}. ${uname}\\n 💬 ${msgs} messages, ⚡ ${cmds} commands\\n 📊 Activity: ${total} points`;\n });\n\n responseText = '🏆 Top Active Users:\\n\\n' + lines.join('\\n\\n');\n } else {\n responseText =\n '🏆 Top Active Users:\\n\\n' +\n 'There are no active users in this chat yet. Be the first! 🚀';\n }\n break;\n }\n\n case 'help': {\n responseText =\n '🤖 GiggleGPTBot commands:\\n\\n' +\n '😄 /joke — funny joke\\n' +\n '💪 /inspire — motivation\\n' +\n '🎲 /random — random phrase\\n' +\n '🔥 /roast — harsh joke with sarcasm (without insults based on prohibited characteristics)\\n' +\n '📊 /stats — your statistics\\n' +\n '🏆 /top — top users\\n' +\n '❓ /help — this help\\n\\n' +\n 'You can also write @GiggleGPTBot + text — I will answer personally.';\n break;\n }\n\n case 'joke':\n case 'inspire':\n case 'random':\n case 'roast': {\n responseText =\n 'This command generates a content response and is processed by another node. ' +\n 'Try again - or use /help for a list of available info commands.';\n break;\n }\n\n default: {\n responseText = 'Unknown command 🤔 Use /help for a list of commands.';\n }\n}\n\nreturn [{\n json: {\n userId,\n userName,\n chatId,\n command,\n responseText,\n isInfoCommand: true\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "bf1fd22f-7df0-4891-82b0-6fd1ed3648dd",
"name": "Tipo de respuesta",
"type": "n8n-nodes-base.if",
"position": [
80,
112
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "boolean",
"operation": "equal",
"singleValue": true
},
"leftValue": "={{ $json.isInfoCommand }}",
"rightValue": true
},
{
"id": "78352c71-3779-4ba9-afb7-1e836a019a24",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.responseText }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "6698db5c-408d-43ac-b4b8-edbb7a3ac367",
"name": "Enviar respuesta informativa",
"type": "n8n-nodes-base.telegram",
"position": [
272,
32
],
"webhookId": "20b4671b-ebf4-4562-b40b-c3eba533ab60",
"parameters": {
"text": "={{ $json.responseText }}",
"chatId": "={{ $json.chatId }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"credentials": {
"telegramApi": {
"id": "GNUE3W3kc9bGnXyL",
"name": "GiggleGPTBot"
}
},
"typeVersion": 1
},
{
"id": "a95f6638-4406-4aab-8088-5d86125eedbf",
"name": "Enviar respuesta de IA",
"type": "n8n-nodes-base.telegram",
"position": [
272,
208
],
"webhookId": "0c3d1f52-87ed-48f4-a056-31beab8dcaf3",
"parameters": {
"text": "={{ $json.output }}",
"chatId": "={{ $('If1').item.json.chatId }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"credentials": {
"telegramApi": {
"id": "GNUE3W3kc9bGnXyL",
"name": "GiggleGPTBot"
}
},
"typeVersion": 1
},
{
"id": "2fc342ba-3372-4e85-91ac-be6cea8b66ae",
"name": "Respuesta de IA a mención",
"type": "@n8n/n8n-nodes-langchain.agent",
"maxTries": 2,
"position": [
-240,
-288
],
"parameters": {
"text": "=User: {{ $json.userName }}\n{{ $json.isRandomRequest ? 'Mentioned the bot without text' : 'Message: \"' + $json.userMessage + '\"' }}\n\nChat context:\n{{ $json.recentMessages }}\n\nTime: {{ $json.timeContext }} | Style: {{ $json.contentType }}\n\n{{ $json.isRandomRequest ? 'Make a witty remark about the chat situation.' : 'Reply to the message with subtle humor.' }}\n\nBe witty and tactful! 🎭\n",
"options": {
"systemMessage": "You are GiggleGPTBot — a witty bot with subtle humor and a big heart. Be concise and to the point.\n\nStyle:\n• Modern spin on proverbs and sayings.\n• References to classics and folksy wisdom.\n• Soft irony with kind subtext.\n• Emojis: allowed but sparing (0–2 per reply).\n\nAvoid:\n• Crudeness, profanity, tired clichés.\n• Long, tangled, or meaningless phrases.\n• Surreal poetry or explaining your own jokes.\n\nFormat:\n• Max 1–2 sentences (3 only in rare cases).\n• One clear idea or joke.\n• At most one simple metaphor.\n• Do not repeat the user’s text; no digressions.\n\nBehavior by situation:\n• Question — answer the gist in one sentence, add a light ironic note in the second.\n• Joy/success — brief congrats; “what’s characteristic” can be used for charm.\n• Complaint/anxiety — tactful support and gentle hope, no moralizing.\n• User tells a joke — reply with a friendly counter-joke.\n• If a name is mentioned — address them by name.\n\nSelf-check before sending:\n• Did I stay within 1–2 sentences?\n• Is there light irony or clear wisdom?\n• Is it clear at first read, no fluff?\n• ≤1 metaphor, ≤2 emojis, ≤2 signature words?\n\nIf not, shorten and rewrite to a simple, clear, witty reply."
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "c343e37c-9fe6-47d5-bae3-b1524d766243",
"name": "Responder a mención",
"type": "n8n-nodes-base.telegram",
"position": [
80,
-288
],
"webhookId": "f77cfa99-a956-4c47-b4ff-41c3267b2505",
"parameters": {
"text": "={{ $json.output }}",
"chatId": "={{ $('Mention Analysis').item.json.chatId }}",
"additionalFields": {
"parse_mode": "HTML",
"reply_to_message_id": "={{ $('Webhook Telegram').first().json.message.message_id }}"
}
},
"credentials": {
"telegramApi": {
"id": "GNUE3W3kc9bGnXyL",
"name": "GiggleGPTBot"
}
},
"typeVersion": 1
},
{
"id": "d581be16-af5f-48bf-bfe2-9a66cf96612f",
"name": "Generación de publicación por IA",
"type": "@n8n/n8n-nodes-langchain.agent",
"maxTries": 2,
"position": [
-416,
528
],
"parameters": {
"text": "=Post type: {{ $json.post_type }}\n\nGenerate {{ $json.post_type === 'morning_joke' ? 'a morning joke' : $json.post_type === 'daily_motivation' ? 'daily motivation' : 'a piece of random wisdom' }} in your style with subtle humor.\n",
"options": {
"systemMessage": "IMPORTANT: Respond with PLAIN TEXT ONLY!\nDo NOT use asterisks, underscores, fancy quotes, or formatting symbols.\n\nYou are GiggleGPTBot — a witty storyteller creating short, warm posts.\n\nTopics:\nmorning_joke: morning jokes with kind subtext\ndaily_motivation: day-starter motivation with gentle irony\nrandom_wisdom: everyday observations in an “isn’t that true!” tone\n\nBehavior by topic:\nmorning_joke — one joke and a short warm finish\ndaily_motivation — one clear thesis and a gentle call to action\nrandom_wisdom — an observation and a simple takeaway\n\nStyle:\n• Concise like Chekhov: max 2–3 sentences (prefer 2)\n• Folksy wisdom in a modern spin\n• Intelligent humor with kind subtext\n• Emojis allowed sparingly: 0–2 per post\n• Max one simple metaphor\n\nAvoid:\n• Crudeness, profanity, cliché “Soviet-style” advice\n• Long, tangled, or surreal imagery\n• Hashtags, links, lists, or explaining your own jokes\n\nSelf-check before sending:\n• Within 2–3 sentences?\n• Light irony or clear joke/wisdom?\n• Clear at first read for a general audience?\n• No fluff or confusion?\n\nIf not, shorten and rewrite to a simple, clear, friendly post."
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 2
},
{
"id": "06b1468d-5af7-46e9-8e50-ec6a3c49d895",
"name": "Enviar publicación programada",
"type": "n8n-nodes-base.telegram",
"position": [
-112,
528
],
"webhookId": "d17bb041-f75d-427a-9529-d93f39a51723",
"parameters": {
"text": "={{ $json.output }}",
"chatId": "={{ $('Get scheduled posts').item.json.chat_id }}",
"additionalFields": {
"parse_mode": "HTML"
}
},
"credentials": {
"telegramApi": {
"id": "GNUE3W3kc9bGnXyL",
"name": "GiggleGPTBot"
}
},
"typeVersion": 1
},
{
"id": "c538c3eb-0ce6-4f34-80fb-b6668a9fe549",
"name": "Guardar respuesta del bot",
"type": "n8n-nodes-base.postgres",
"position": [
80,
-112
],
"parameters": {
"query": "INSERT INTO bot_responses (user_id, chat_id, user_message, bot_response, response_type) \nVALUES ({{ $('Mention Analysis').item.json.userId }}, \n {{ $('Mention Analysis').item.json.chatId }}, \n '{{ $('Mention Analysis').item.json.originalMessage.replace(/'/g, \"''\") }}', \n '{{ $json.output.replace(/'/g, \"''\") }}', \n '{{ $('Mention Analysis').item.json.contentType }}');",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "f78d3e69-c2ba-4640-921a-e3bb52988416",
"name": "Si",
"type": "n8n-nodes-base.if",
"position": [
-624,
544
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e1310a96-8c67-467e-b50d-fee95851424c",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.chat_id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "8afe12f7-ef5b-4b89-8b08-623024c97b3c",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
-960,
-32
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "joke",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "1b2506b4-5b0b-48cb-bb27-5684c9bcbd88",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/joke"
}
]
},
"renameOutput": true
},
{
"outputKey": "inspire",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6362199a-67f8-47c2-a845-3088f33c3338",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/inspire"
}
]
},
"renameOutput": true
},
{
"outputKey": "random",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f883e421-2f6e-490b-ae2c-9952c5083d2c",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/random"
}
]
},
"renameOutput": true
},
{
"outputKey": "roast",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "3d6557c4-2eab-4545-af30-3b4848c424fb",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/roast"
}
]
},
"renameOutput": true
},
{
"outputKey": "GiggleGPTBot",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "69fbe825-df4d-44c5-85eb-5d69e0f17896",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "@GiggleGPTBot"
}
]
},
"renameOutput": true
},
{
"outputKey": "stats",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ac0a71e3-8793-4c32-a5f9-bec6055c04d7",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/stats"
}
]
},
"renameOutput": true
},
{
"outputKey": "help",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "062c9bd8-d63d-4c22-9097-8499b04ebc13",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/help"
}
]
},
"renameOutput": true
},
{
"outputKey": "top",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ceb18c82-23d7-411d-8a48-c212ea7add91",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.message?.text }}",
"rightValue": "/top"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "76628b1a-4f38-4e8b-be27-adf5cf078b83",
"name": "Registrar comando",
"type": "n8n-nodes-base.postgres",
"position": [
-384,
64
],
"parameters": {
"query": "INSERT INTO bot_commands (user_id, chat_id, command) \nVALUES ({{ $json.userId }}, {{ $json.chatId }}, '{{ $json.userMessage.replace(/'/g, \"''\") }}');\n\nUPDATE user_stats \nSET commands_count = commands_count + 1 \nWHERE user_id = {{ $json.userId }} AND chat_id = {{ $json.chatId }};",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "197cb78b-ff9b-46d3-93ca-ff708d45d36d",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
-416,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "39d5e005-69f7-4e27-ad73-e00b7a694a4e",
"operator": {
"type": "string",
"operation": "startsWith"
},
"leftValue": "={{ $json.originalMessage }}",
"rightValue": "@GiggleGPTBot"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "cd2ae05c-a673-4b30-82fd-9c003610799a",
"name": "Guardar respuesta del bot2",
"type": "n8n-nodes-base.postgres",
"position": [
-112,
352
],
"parameters": {
"query": "INSERT INTO bot_responses (user_id, chat_id, user_message, bot_response, response_type) \nVALUES (0,\n {{ $('Get scheduled posts').item.json.chat_id }}, \n 'Scheduled post', \n '{{ $json.output.replace(/'/g, \"''\") }}', \n '{{ $('Get scheduled posts').item.json.post_type }}');",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"id": "Fd7RaI9MHHQBq9hE",
"name": "Supabase Postgres"
}
},
"typeVersion": 2
},
{
"id": "f66c1418-8664-416b-8e0b-08ed120bb64e",
"name": "Nota adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2064,
-416
],
"parameters": {
"color": 5,
"width": 700,
"height": 2844,
"content": "# GiggleGPTBot — Witty Telegram Bot with AI & Postgres\n\n## 📝 Overview\n\nGiggleGPTBot is a witty Telegram bot built with **n8n**, **OpenRouter**, and **Postgres**.\nIt delivers short jokes, motivational one-liners, and playful roasts, responds to mentions, and posts scheduled witty content.\nThe workflow also tracks user activity and provides lightweight statistics and leaderboards.\n\n---\n\n## ✨ Features\n\n* 🤖 **AI-powered humor engine** — replies with jokes, motivation, random witty lines, or sarcastic roasts.\n* 💬 **Command support** — `/joke`, `/inspire`, `/random`, `/roast`, `/help`, `/stats`, `/top`.\n* 🎯 **Mention detection** — replies when users tag `@GiggleGPTBot`.\n* ⏰ **Scheduled posts** — morning jokes, daily motivation, and random wisdom at configured times.\n* 📊 **User analytics** — counts messages, commands, reactions, and generates leaderboards.\n* 🗄️ **Postgres persistence** — robust schema with tables for messages, responses, stats, and schedules.\n\n---\n\n## 🛠️ How It Works\n\n1. **Triggers**\n\n * `Telegram Trigger` — receives all messages and commands from a chat.\n * `Schedule Trigger` — runs hourly to check for planned posts.\n\n2. **Processing**\n\n * `Switch` routes commands (`/joke`, `/inspire`, `/random`, `/roast`, `/help`, `/stats`, `/top`).\n * `Chat history` fetches the latest context.\n * `Mention Analysis` determines if the bot was mentioned.\n * `Generating an information response` builds replies for `/help`, `/stats`, `/top`.\n * AI nodes (`AI response to command`, `AI response to mention`, `AI post generation`) craft witty content via **OpenRouter**.\n\n3. **Persistence**\n\n * `Init Database` ensures tables exist (`user_messages`, `bot_responses`, `bot_commands`, `message_reactions`, `scheduled_posts`, `user_stats`).\n * Logging nodes update stats and store every bot/user interaction.\n\n4. **Delivery**\n\n * Replies are sent back via `Telegram Send` nodes (`Send AI response`, `Send info reply`, `Reply to Mention`, `Submit scheduled post`).\n\n---\n\n## ⚙️ Setup Instructions\n\n1. **Create a Telegram Bot** with [@BotFather](https://t.me/BotFather) and get your API token.\n2. **Add credentials** in n8n:\n\n * `Telegram API` (your bot token)\n * `OpenRouter` (API key from [openrouter.ai](https://openrouter.ai/))\n * `Postgres` (use your DB, Supabase works well).\n3. **Run the `Init Database` node once** to create all required tables.\n4. **(Optional) Seed schedule** with the `Adding a schedule` node — it inserts:\n\n * Morning joke at 06:00\n * Daily motivation at 09:00\n * Random wisdom at 17:00\n (Adjust `chat_id` to your group/channel ID.)\n5. **Activate workflow** and connect Telegram Webhook or Polling.\n\n---\n\n## 📊 Database Schema\n\n* **user\\_messages** — stores user chat messages.\n* **bot\\_responses** — saves bot replies.\n* **bot\\_commands** — logs command usage.\n* **message\\_reactions** — tracks reactions.\n* **scheduled\\_posts** — holds scheduled jokes/wisdom/motivation.\n* **user\\_stats** — aggregates per-user message/command counts and activity.\n\n---\n\n## 🔑 Example Commands\n\n* `/joke` → witty one-liner with light irony.\n* `/inspire` → short motivational phrase.\n* `/random` → unexpected witty remark.\n* `/roast` → sarcastic roast (no offensive targeting).\n* `/stats` → shows your personal stats.\n* `/top` → displays leaderboard.\n* `/help` → lists available commands.\n* `@GiggleGPTBot` + message → bot replies in context.\n\n---\n\n## 🚀 Customization Ideas\n\n* Add new command categories (`/quote`, `/fact`, `/news`).\n* Expand analytics with reaction counts or streaks.\n* Localize prompts into multiple languages.\n* Adjust CRON schedules for posts.\n\n---\n\n## ✅ Requirements\n\n* Telegram Bot token\n* OpenRouter API key\n* Postgres database\n\n---\n\n📦 Import this workflow, configure credentials, run the DB initializer — and your witty AI-powered Telegram companion is ready!\n"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "7c0fe80e-b260-4241-b8fb-328a99300eb1",
"connections": {
"f78d3e69-c2ba-4640-921a-e3bb52988416": {
"main": [
[
{
"node": "d581be16-af5f-48bf-bfe2-9a66cf96612f",
"type": "main",
"index": 0
}
]
]
},
"197cb78b-ff9b-46d3-93ca-ff708d45d36d": {
"main": [
[
{
"node": "2fc342ba-3372-4e85-91ac-be6cea8b66ae",
"type": "main",
"index": 0
}
],
[
{
"node": "b12f28fc-6fb0-45ca-8348-202bf5a78332",
"type": "main",
"index": 0
}
]
]
},
"8afe12f7-ef5b-4b89-8b08-623024c97b3c": {
"main": [
[
{
"node": "84247455-9eed-424d-8ee4-84ece8f10d54",
"type": "main",
"index": 0
}
],
[
{
"node": "84247455-9eed-424d-8ee4-84ece8f10d54",
"type": "main",
"index": 0
}
],
[
{
"node": "84247455-9eed-424d-8ee4-84ece8f10d54",
"type": "main",
"index": 0
}
],
[
{
"node": "84247455-9eed-424d-8ee4-84ece8f10d54",
"type": "main",
"index": 0
}
],
[
{
"node": "84247455-9eed-424d-8ee4-84ece8f10d54",
"type": "main",
"index": 0
}
],
[
{
"node": "60de834b-47c8-428a-9d28-87ade8fa8a43",
"type": "main",
"index": 0
}
],
[
{
"node": "e1345b2b-f824-40cb-b01d-48031d417b1b",
"type": "main",
"index": 0
}
],
[
{
"node": "d57380bc-5dc5-4130-a767-463bbf81931c",
"type": "main",
"index": 0
}
]
]
},
"5b7c3300-fa5a-43f5-881f-db48314be81c": {
"main": [
[
{
"node": "756e467d-835a-4166-8972-4edf50b0007f",
"type": "main",
"index": 0
}
]
]
},
"84247455-9eed-424d-8ee4-84ece8f10d54": {
"main": [
[
{
"node": "86872198-4d19-4c3e-b5dd-14dda87a9139",
"type": "main",
"index": 0
}
]
]
},
"d57380bc-5dc5-4130-a767-463bbf81931c": {
"main": [
[
{
"node": "e1345b2b-f824-40cb-b01d-48031d417b1b",
"type": "main",
"index": 0
}
]
]
},
"bf1fd22f-7df0-4891-82b0-6fd1ed3648dd": {
"main": [
[
{
"node": "6698db5c-408d-43ac-b4b8-edbb7a3ac367",
"type": "main",
"index": 0
}
],
[
{
"node": "a95f6638-4406-4aab-8088-5d86125eedbf",
"type": "main",
"index": 0
}
]
]
},
"86872198-4d19-4c3e-b5dd-14dda87a9139": {
"main": [
[
{
"node": "197cb78b-ff9b-46d3-93ca-ff708d45d36d",
"type": "main",
"index": 0
}
]
]
},
"c4dbddca-9165-401f-9a96-995a958a425a": {
"main": [
[
{
"node": "2f7789fd-645c-436f-91ef-04cb2143aa7d",
"type": "main",
"index": 0
},
{
"node": "8afe12f7-ef5b-4b89-8b08-623024c97b3c",
"type": "main",
"index": 0
}
]
]
},
"d581be16-af5f-48bf-bfe2-9a66cf96612f": {
"main": [
[
{
"node": "06b1468d-5af7-46e9-8e50-ec6a3c49d895",
"type": "main",
"index": 0
},
{
"node": "cd2ae05c-a673-4b30-82fd-9c003610799a",
"type": "main",
"index": 0
}
]
]
},
"756e467d-835a-4166-8972-4edf50b0007f": {
"main": [
[
{
"node": "f78d3e69-c2ba-4640-921a-e3bb52988416",
"type": "main",
"index": 0
}
]
]
},
"60de834b-47c8-428a-9d28-87ade8fa8a43": {
"main": [
[
{
"node": "e1345b2b-f824-40cb-b01d-48031d417b1b",
"type": "main",
"index": 0
}
]
]
},
"5369e144-45cd-4a99-9fa6-284ae24ec585": {
"ai_languageModel": [
[
{
"node": "b12f28fc-6fb0-45ca-8348-202bf5a78332",
"type": "ai_languageModel",
"index": 0
},
{
"node": "2fc342ba-3372-4e85-91ac-be6cea8b66ae",
"type": "ai_languageModel",
"index": 0
},
{
"node": "d581be16-af5f-48bf-bfe2-9a66cf96612f",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"b12f28fc-6fb0-45ca-8348-202bf5a78332": {
"main": [
[
{
"node": "bf1fd22f-7df0-4891-82b0-6fd1ed3648dd",
"type": "main",
"index": 0
},
{
"node": "c538c3eb-0ce6-4f34-80fb-b6668a9fe549",
"type": "main",
"index": 0
}
]
]
},
"2fc342ba-3372-4e85-91ac-be6cea8b66ae": {
"main": [
[
{
"node": "c343e37c-9fe6-47d5-bae3-b1524d766243",
"type": "main",
"index": 0
},
{
"node": "c538c3eb-0ce6-4f34-80fb-b6668a9fe549",
"type": "main",
"index": 0
}
]
]
},
"e1345b2b-f824-40cb-b01d-48031d417b1b": {
"main": [
[
{
"node": "76628b1a-4f38-4e8b-be27-adf5cf078b83",
"type": "main",
"index": 0
},
{
"node": "bf1fd22f-7df0-4891-82b0-6fd1ed3648dd",
"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
Sergey Skorobogatov
@cepreusaCompartir este flujo de trabajo