GiggleGPTBot 템플릿
고급
이것은AI Chatbot, Multimodal AI분야의자동화 워크플로우로, 27개의 노드를 포함합니다.주로 If, Code, Switch, Postgres, Telegram 등의 노드를 사용하며. OpenRouter를 사용하여 AI로운 히어로우, 조롱 및 통계 기능을 포함한 지혜로운 Telegram 로봇을 만듭니다.
사전 요구사항
- •PostgreSQL 데이터베이스 연결 정보
- •Telegram Bot Token
사용된 노드 (27)
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"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": "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": "데이터베이스 초기화",
"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": "메시지 로깅 + 통계",
"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": "일정 추가",
"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": "일정",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-992,
544
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 * * * *"
}
]
}
},
"typeVersion": 1
},
{
"id": "756e467d-835a-4166-8972-4edf50b0007f",
"name": "예약된 게시물 가져오기",
"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": "채팅 기록",
"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": "멘션 분석",
"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": "사용자 통계 가져오기",
"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": "상위 사용자 가져오기",
"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": "명령어에 대한 AI 응답",
"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": "정보 응답 생성",
"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": "응답 유형",
"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": "정보 답변 전송",
"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": "AI 응답 전송",
"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": "멘션에 대한 AI 응답",
"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": "멘션에 답장",
"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": "AI 게시물 생성",
"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": "예약된 게시물 제출",
"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": "봇 응답 저장",
"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": "조건문",
"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": "스위치",
"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": "명령어 로깅",
"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": "조건문1",
"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": "봇 응답 저장2",
"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": "스티커 노트2",
"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
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
고급 - AI 챗봇, 멀티모달 AI
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
주식 분석 템플릿
기술 분석, AI, Telegram을 결합하여 주식 시장 통찰력 생성
If
Set
Code
+
If
Set
Code
25 노드Sergey Skorobogatov
암호화폐 거래
Telegram 포럼 패ulse: Gemini와 Groq AI 모델을 사용한 커뮤니티 모니터링
Telegram 포럼 패스: Gemini와 Groq AI 모델을 사용한 커뮤니티 모니터링
If
Set
Code
+
If
Set
Code
59 노드Nguyen Thieu Toan
기타
완전히 맞춤화된 3통의 이메일 후속 작업이 포함된 GPT-4 기반 콜드 이메일 워크플로
GPT-4, Mailgun 및 Supabase를 사용한 개인 맞춤형 콜드 이메일 시퀀스 자동화
If
Set
Code
+
If
Set
Code
100 노드Paul
리드 육성
완전한 B2B 판매 프로세스: Apollo 잠재 고객 생성, Mailgun 프로모션 및 AI 응답 관리
완전한 B2B 판매 프로세스: Apollo 잠재 고객 생성, Mailgun 확장 및 AI 응답 관리
If
Set
Code
+
If
Set
Code
116 노드Paul
콘텐츠 제작
펫 그루밍 게시 및 예약 자동화
AI, Facebook 및 Telegram 봇을 사용한 펫 그루밍 게시 및 예약 자동화
If
Set
Switch
+
If
Set
Switch
36 노드Christian Moises
AI 챗봇
지능형 Telegram 어시스턴트
Gemini AI, PostgreSQL 기억, 동적 라우팅을 사용하여 지능형 Telegram 어시스턴트를 구축합니다.
Set
Code
Switch
+
Set
Code
Switch
36 노드John Alejandro SIlva
AI 챗봇