This workflow follows the Executecommand → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "03 - Command Handler",
"nodes": [
{
"parameters": {},
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "// Parse command and arguments\nconst text = $input.first().json.text;\nconst parts = text.split(' ');\nconst command = parts[0].toLowerCase();\nconst args = parts.slice(1).join(' ');\n\n// Extract command name without /\nconst commandName = command.replace('/', '');\n\nreturn [{\n json: {\n command: commandName,\n args: args,\n fullText: text,\n chatId: $input.first().json.chatId,\n userId: $input.first().json.userId,\n username: $input.first().json.username\n }\n}];"
},
"name": "Parse Command",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
450,
300
]
},
{
"parameters": {
"rules": {
"rules": [
{
"value": "help",
"output": 0
},
{
"value": "status",
"output": 1
},
{
"value": "remember",
"output": 2
},
{
"value": "recall",
"output": 3
},
{
"value": "clear",
"output": 4
},
{
"value": "read",
"output": 5
},
{
"value": "list",
"output": 6
},
{
"value": "exec",
"output": 7
},
{
"value": "email",
"output": 8
},
{
"value": "system",
"output": 9
},
{
"value": "todo",
"output": 10
}
]
}
},
"name": "Route Command",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
650,
300
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "\ud83d\udc4b Hey there! Here are the things I can help you with:\\n\\n\ud83d\udcac **Chat** - Just message me naturally\\n\ud83d\udcca **/status** - Check your Mac's system status\\n\ud83e\udde0 **/remember [fact]** - I'll remember something for you\\n\ud83d\udcad **/recall** - Show what I remember\\n\ud83d\uddd1\ufe0f **/clear** - Clear conversation history\\n\ud83d\udcc1 **/read [filepath]** - Read a text file\\n\ud83d\udcc2 **/list [directory]** - List directory contents\\n\u26a1 **/exec [command]** - Run a safe command\\n\\n\u2705 **Todo Commands:**\\n`/todo` - List your active todos\\n`/todo add <task> [due:YYYY-MM-DD] [recur:daily|weekly|monthly]` - Add a todo\\n`/todo done <id>` - Mark a todo complete\\n`/todo delete <id>` - Delete a todo\\n\\n\ud83d\udce7 **Email Commands:**\\n`/email auth` - Connect your Gmail account\\n`/email check` - Check unread emails\\n`/email read [id]` - Read a specific email\\n`/email reply [id]` - AI-powered reply drafts\\n`/email send` - Compose a new email\\n`/email search [query]` - Search your emails\\n\\n\ud83d\udd27 **System Control (owner only):**\\n`/system status` - Check if bot is running\\n`/system restart` - Restart ronkbot\\n`/system stop` - Stop ronkbot\\n`/system wake` - Check power/sleep status\\n\\nJust chat naturally or use any command above! \ud83d\ude0a",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Help Response",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
850,
100
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"command": "df -h",
"cwd": "/Users/ronkuwonku"
},
"name": "Get Disk Usage",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
850,
200
]
},
{
"parameters": {
"command": "vm_stat | head -5",
"cwd": "/Users/ronkuwonku"
},
"name": "Get Memory",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
850,
300
]
},
{
"parameters": {
"jsCode": "// Format status response\nconst diskOutput = $('Get Disk Usage').first().json.stdout || 'N/A';\nconst memOutput = $('Get Memory').first().json.stdout || 'N/A';\n\nconst message = `\ud83d\udcca **System Status**\\n\\n\ud83d\udcbe **Disk Usage:**\\n\\`\\`\\`\\n${diskOutput}\\`\\`\\`\\n\\n\ud83e\udde0 **Memory:**\\n\\`\\`\\`\\n${memOutput}\\`\\`\\`\\n\\n\u23f1\ufe0f Last updated: ${new Date().toLocaleString()}`;\n\nreturn [{\n json: {\n message: message,\n chatId: $input.first().json.chatId\n }\n}];"
},
"name": "Format Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1050,
250
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Status",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1250,
250
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO memory (user_id, username, fact, timestamp) VALUES ($1, $2, $3, datetime('now'))",
"additionalFields": {
"parameters": [
{
"name": "user_id",
"value": "={{ $json.userId }}"
},
{
"name": "username",
"value": "={{ $json.username }}"
},
{
"name": "fact",
"value": "={{ $json.args }}"
}
]
}
},
"name": "Save Memory",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.2,
"position": [
850,
400
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "\u2705 Got it! I'll remember that:\\\\n\\\\n\\\"{{ $json.args }}\\\"\\\\n\\\\n\ud83d\udca1 Use /recall to see everything I remember!",
"additionalFields": {}
},
"name": "Confirm Remember",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1050,
400
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT fact FROM memory WHERE user_id = $1 ORDER BY timestamp DESC",
"additionalFields": {
"parameters": [
{
"name": "user_id",
"value": "={{ $json.userId }}"
}
]
}
},
"name": "Get All Memory",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.2,
"position": [
850,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Format memory list\nconst items = $('Get All Memory').all();\n\nif (!items || items.length === 0) {\n return [{\n json: {\n message: '\ud83e\udde0 I don\\'t remember anything yet!\\n\\nUse /remember to tell me things I should remember. \ud83d\ude0a',\n chatId: $input.first().json.chatId\n }\n }];\n}\n\nlet message = '\ud83e\udde0 **Here\\'s what I remember about you:**\\n\\n';\nitems.forEach((item, index) => {\n message += `${index + 1}. ${item.json.fact}\\n`;\n});\n\nmessage += '\\n\ud83d\udca1 Use /remember to add more!';\n\nreturn [{\n json: {\n message: message,\n chatId: $input.first().json.chatId\n }\n}];"
},
"name": "Format Memory List",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1050,
500
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Memory",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1250,
500
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "DELETE FROM conversations WHERE user_id = $1",
"additionalFields": {
"parameters": [
{
"name": "user_id",
"value": "={{ $json.userId }}"
}
]
}
},
"name": "Clear History",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.2,
"position": [
850,
600
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "\ud83e\uddf9 **Conversation history cleared!**\\\\n\\\\nI'm starting fresh. What would you like to talk about? \ud83d\ude0a",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Confirm Clear",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1050,
600
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Parse email subcommand from args\n// Format: /email <subcommand> [args...]\n// e.g. /email check\n// /email read abc123\n// /email reply abc123\n// /email send\n// /email search invoice\n// /email auth\n\nconst args = $input.first().json.args || '';\nconst parts = args.trim().split(' ');\nconst subcommand = (parts[0] || 'check').toLowerCase();\nconst subArgs = parts.slice(1).join(' ');\n\nreturn [{\n json: {\n ...$input.first().json,\n subcommand: subcommand,\n subArgs: subArgs\n }\n}];"
},
"name": "Parse Email Subcommand",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
750
]
},
{
"parameters": {
"rules": {
"rules": [
{
"value": "auth",
"output": 0
},
{
"value": "check",
"output": 1
},
{
"value": "read",
"output": 2
},
{
"value": "reply",
"output": 3
},
{
"value": "send",
"output": 4
},
{
"value": "search",
"output": 5
}
]
}
},
"name": "Route Email Subcommand",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
1050,
750
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "\ud83d\udd10 **Connect Your Gmail Account**\\n\\nClick the link below to authorize ronkbot:\\n\\n`http://localhost:5678/gmail-auth`\\n\\n**Steps:**\\n1. Click the link above (open in browser)\\n2. Sign in with your Google account\\n3. Grant the requested permissions\\n4. You'll see a success message\\n\\nAfter connecting, use `/email check` to see your emails! \ud83d\udce7\\n\\n\u26a0\ufe0f Make sure you've set up Gmail credentials in your `.env` file first. See `docs/EMAIL_SETUP.md` for details.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Email Auth Message",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1300,
550
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"workflowId": "05 - Email Reader",
"workflowInputs": {
"values": [
{
"name": "chatId",
"stringValue": "={{ $json.chatId }}"
},
{
"name": "userId",
"stringValue": "={{ $json.userId }}"
},
{
"name": "username",
"stringValue": "={{ $json.username }}"
},
{
"name": "mode",
"stringValue": "check"
},
{
"name": "limit",
"stringValue": "10"
},
{
"name": "label",
"stringValue": "INBOX"
},
{
"name": "searchQuery",
"stringValue": "is:unread"
}
]
}
},
"name": "Check Emails",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1300,
650
]
},
{
"parameters": {
"workflowId": "05 - Email Reader",
"workflowInputs": {
"values": [
{
"name": "chatId",
"stringValue": "={{ $json.chatId }}"
},
{
"name": "userId",
"stringValue": "={{ $json.userId }}"
},
{
"name": "username",
"stringValue": "={{ $json.username }}"
},
{
"name": "mode",
"stringValue": "read"
},
{
"name": "emailId",
"stringValue": "={{ $json.subArgs }}"
}
]
}
},
"name": "Read Email",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1300,
750
]
},
{
"parameters": {
"workflowId": "06 - Email Sender",
"workflowInputs": {
"values": [
{
"name": "chatId",
"stringValue": "={{ $json.chatId }}"
},
{
"name": "userId",
"stringValue": "={{ $json.userId }}"
},
{
"name": "username",
"stringValue": "={{ $json.username }}"
},
{
"name": "mode",
"stringValue": "reply"
},
{
"name": "emailId",
"stringValue": "={{ $json.subArgs }}"
}
]
}
},
"name": "Reply Email",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1300,
850
]
},
{
"parameters": {
"workflowId": "06 - Email Sender",
"workflowInputs": {
"values": [
{
"name": "chatId",
"stringValue": "={{ $json.chatId }}"
},
{
"name": "userId",
"stringValue": "={{ $json.userId }}"
},
{
"name": "username",
"stringValue": "={{ $json.username }}"
},
{
"name": "mode",
"stringValue": "compose"
}
]
}
},
"name": "Send Email",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1300,
950
]
},
{
"parameters": {
"workflowId": "05 - Email Reader",
"workflowInputs": {
"values": [
{
"name": "chatId",
"stringValue": "={{ $json.chatId }}"
},
{
"name": "userId",
"stringValue": "={{ $json.userId }}"
},
{
"name": "username",
"stringValue": "={{ $json.username }}"
},
{
"name": "mode",
"stringValue": "search"
},
{
"name": "limit",
"stringValue": "10"
},
{
"name": "searchQuery",
"stringValue": "={{ $json.subArgs }}"
}
]
}
},
"name": "Search Emails",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1300,
1050
]
},
{
"parameters": {
"jsCode": "// Security: only the owner can run /system commands\nconst ownerUsername = $env.TELEGRAM_OWNER_USERNAME || '';\nconst callerUsername = $input.first().json.username || '';\n\nif (!ownerUsername || callerUsername.toLowerCase() !== ownerUsername.toLowerCase()) {\n return [{\n json: {\n ...$input.first().json,\n accessDenied: true,\n message: '\u26d4 Access denied. System commands are owner-only.'\n }\n }];\n}\n\nconst args = $input.first().json.args || '';\nconst parts = args.trim().split(' ');\nconst subcommand = (parts[0] || 'status').toLowerCase();\nconst subArgs = parts.slice(1).join(' ');\n\nreturn [{\n json: {\n ...$input.first().json,\n accessDenied: false,\n subcommand: subcommand,\n subArgs: subArgs\n }\n}];"
},
"name": "System Owner Check",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
1200
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {}
},
"name": "System Access Denied",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1050,
1100
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"rules": [
{
"value": "access_denied",
"output": 0
},
{
"value": "status",
"output": 1
},
{
"value": "restart",
"output": 2
},
{
"value": "stop",
"output": 3
},
{
"value": "wake",
"output": 4
}
]
},
"dataType": "string",
"value1": "={{ $json.accessDenied ? 'access_denied' : $json.subcommand }}"
},
"name": "Route System Subcommand",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
1050,
1200
]
},
{
"parameters": {
"command": "docker ps --filter name=ronkbot --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}' 2>&1"
},
"name": "Docker Status",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1250,
1150
]
},
{
"parameters": {
"jsCode": "const output = $input.first().json.stdout || 'No output';\nconst running = output.includes('Up');\nconst icon = running ? '\u2705' : '\u26a0\ufe0f';\nconst msg = `${icon} *ronkbot Container Status*\\n\\n\\`\\`\\`\\n${output.trim()}\\`\\`\\``;\nreturn [{ json: { message: msg, chatId: $('System Owner Check').first().json.chatId } }];"
},
"name": "Format Docker Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1450,
1150
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send System Status",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1150
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"command": "docker restart ronkbot-n8n 2>&1 && echo 'RESTART_OK' || echo 'RESTART_FAILED'"
},
"name": "Docker Restart",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1250,
1250
]
},
{
"parameters": {
"chatId": "={{ $('System Owner Check').first().json.chatId }}",
"text": "\ud83d\udd04 *Restarting ronkbot...*\\n\\nI'll be back in ~30 seconds! Use `/system status` to check.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Restart Confirm",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1450,
1250
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"command": "docker stop ronkbot-n8n 2>&1 && echo 'STOP_OK' || echo 'STOP_FAILED'"
},
"name": "Docker Stop",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1250,
1350
]
},
{
"parameters": {
"chatId": "={{ $('System Owner Check').first().json.chatId }}",
"text": "\ud83d\uded1 *ronkbot stopped.*\\n\\nTo restart from your iPhone, tap the *Start ronkbot* Shortcut or run:\\n`docker start ronkbot-n8n`",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Stop Confirm",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1450,
1350
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"command": "pmset -g batt 2>&1; echo '---'; pmset -g assertions 2>&1 | grep -E '(PreventSleep|PreventUserIdleSystem|NoIdleSleepAssertion)' | head -5"
},
"name": "Get Power Status",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
1250,
1450
]
},
{
"parameters": {
"jsCode": "const output = $input.first().json.stdout || '';\nconst hasACPower = output.includes('AC Power') || output.includes('charged');\nconst hasSleepPrevention = output.includes('PreventSleep') || output.includes('NoIdleSleep');\nconst battLine = output.split('\\n').find(l => l.includes('%')) || 'Unknown';\n\nlet wakeStatus;\nif (hasACPower && hasSleepPrevention) {\n wakeStatus = '\u2705 Mac is plugged in and sleep prevention is active. Bot will stay awake!';\n} else if (hasACPower) {\n wakeStatus = '\u26a0\ufe0f On AC power but no sleep prevention detected.\\n\\nInstall *Amphetamine* (free) to stay awake with lid closed:\\nhttps://apps.apple.com/us/app/amphetamine/id937984704';\n} else {\n wakeStatus = '\ud83d\udd0b On battery \u2014 Mac may sleep!\\n\\nPlug in + install *Amphetamine* (free) for always-on use:\\nhttps://apps.apple.com/us/app/amphetamine/id937984704';\n}\n\nconst msg = `\ud83d\udcf1 *Wake / Power Status*\\n\\n${wakeStatus}\\n\\n*Battery:* ${battLine.trim()}`;\nreturn [{ json: { message: msg, chatId: $('System Owner Check').first().json.chatId } }];"
},
"name": "Format Wake Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1450,
1450
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Wake Status",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1450
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Parse todo subcommand from args\n// Format: /todo [subcommand] [args...]\n// e.g. /todo\n// /todo list\n// /todo add Buy milk due:2026-03-15 recur:weekly\n// /todo done 12\n// /todo delete 12\n\nconst args = $input.first().json.args || '';\nconst trimmedArgs = args.trim();\nconst firstSpaceIndex = trimmedArgs.indexOf(' ');\n\nlet subcommand;\nlet subArgs;\n\nif (firstSpaceIndex === -1) {\n subcommand = trimmedArgs || 'list';\n subArgs = '';\n} else {\n subcommand = trimmedArgs.substring(0, firstSpaceIndex);\n subArgs = trimmedArgs.substring(firstSpaceIndex + 1).trim();\n}\n\nsubcommand = subcommand.toLowerCase();\n\nreturn [{\n json: {\n ...$input.first().json,\n subcommand: subcommand,\n subArgs: subArgs\n }\n}];"
},
"name": "Parse Todo Subcommand",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
1600
]
},
{
"parameters": {
"rules": {
"rules": [
{
"value": "list",
"output": 0
},
{
"value": "add",
"output": 1
},
{
"value": "done",
"output": 2
},
{
"value": "delete",
"output": 3
}
]
},
"dataType": "string",
"value1": "={{ $json.subcommand }}"
},
"name": "Route Todo Subcommand",
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
1050,
1600
]
},
{
"parameters": {
"method": "GET",
"url": "=http://host.docker.internal:4242/brain/todos?chat_id={{ $json.chatId }}&completed=false",
"options": {}
},
"name": "Fetch Todos",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1250,
1500
]
},
{
"parameters": {
"jsCode": "const resp = $input.first().json;\nconst todos = resp.todos || [];\nconst chatId = $('Parse Todo Subcommand').first().json.chatId;\n\nif (todos.length === 0) {\n return [{ json: { chatId, message: '\\u2705 No active todos! Use `/todo add <task>` to create one.' } }];\n}\n\nlet msg = `\\u2705 *Your Todos (${todos.length})*\\n`;\ntodos.forEach((t, i) => {\n msg += `\\n${i + 1}. [#${t.id}] ${t.task}`;\n const parts = [];\n if (t.due_at) parts.push(`\\ud83d\\udcc5 Due: ${t.due_at.slice(0, 10)}`);\n if (t.recurrence) parts.push(`\\ud83d\\udd01 Recurrence: ${t.recurrence}`);\n if (parts.length) msg += `\\n ${parts.join(' | ')}`;\n});\n\nreturn [{ json: { chatId, message: msg } }];"
},
"name": "Format Todo List",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1450,
1500
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Todo List",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1500
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Parse: /todo add Buy milk due:2026-03-15 recur:weekly\nconst raw = $input.first().json.subArgs || '';\nconst chatId = $input.first().json.chatId;\n\nlet task = raw;\nlet due_at = null;\nlet recurrence = null;\n\n// Match and remove due date from anywhere in the string\nconst dueRegex = /\\s*due:\\s*(\\d{4}-\\d{2}-\\d{2})\\s*/;\nconst dueMatch = task.match(dueRegex);\nif (dueMatch) {\n const parsed = new Date(dueMatch[1] + 'T00:00:00Z');\n const [y, m, d] = dueMatch[1].split('-').map(Number);\n if (isNaN(parsed.getTime()) || parsed.getUTCFullYear() !== y || parsed.getUTCMonth() + 1 !== m || parsed.getUTCDate() !== d) {\n return [{ json: { chatId, error: true, message: '\\u26a0\\ufe0f Invalid date. Use format: `due:YYYY-MM-DD` (e.g. `due:2026-03-15`)' } }];\n }\n due_at = dueMatch[1] + 'T00:00:00+00:00';\n task = task.replace(dueMatch[0], ' ').trim();\n}\n\n// Match and remove recurrence from anywhere in the string\nconst recurRegex = /\\s*recur:\\s*(daily|weekly|monthly)\\s*/i;\nconst recurMatch = task.match(recurRegex);\nif (recurMatch) {\n recurrence = recurMatch[1].toLowerCase();\n task = task.replace(recurMatch[0], ' ').trim();\n}\n\ntask = task.trim();\n\nif (!task) {\n return [{ json: { chatId, error: true, message: '\\u26a0\\ufe0f Usage: `/todo add <task> [due:YYYY-MM-DD] [recur:daily|weekly|monthly]`' } }];\n}\n\nreturn [{ json: { chatId, error: false, task, due_at, recurrence } }];"
},
"name": "Parse Todo Add Args",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1250,
1600
]
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.error }}",
"value2": true
}
]
}
},
"name": "Todo Add Error?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1450,
1600
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Todo Add Error",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1550
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "http://host.docker.internal:4242/brain/todo",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "chat_id",
"value": "={{ $json.chatId }}"
},
{
"name": "task",
"value": "={{ $json.task }}"
},
{
"name": "due_at",
"value": "={{ $json.due_at }}"
},
{
"name": "recurrence",
"value": "={{ $json.recurrence }}"
}
]
},
"options": {}
},
"name": "Create Todo",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1650,
1650
]
},
{
"parameters": {
"jsCode": "const resp = $input.first().json;\nconst chatId = $('Parse Todo Add Args').first().json.chatId;\nconst parts = [`\\u2705 *Todo created!*\\n\\n#${resp.id} \\u2014 ${resp.task}`];\nif (resp.due_at) parts.push(`\\ud83d\\udcc5 Due: ${resp.due_at.slice(0, 10)}`);\nif (resp.recurrence) parts.push(`\\ud83d\\udd01 Recurrence: ${resp.recurrence}`);\nreturn [{ json: { chatId, message: parts.join('\\n') } }];"
},
"name": "Format Todo Created",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1850,
1650
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Todo Created",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
2050,
1650
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "PATCH",
"url": "=http://host.docker.internal:4242/brain/todo/{{ $json.subArgs }}",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "completed",
"value": "={{true}}"
}
]
},
"options": {
"response": {
"neverError": true
}
}
},
"name": "Complete Todo",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1250,
1700
]
},
{
"parameters": {
"jsCode": "const input = $input.first().json;\nconst chatId = $('Parse Todo Subcommand').first().json.chatId;\n\nif (input.statusCode && input.statusCode >= 400) {\n return [{ json: { chatId, message: '\\u26a0\\ufe0f Todo not found. Check the ID and try again.' } }];\n}\n\nconst resp = input;\nlet msg = '\\u2705 *Todo completed!*';\nif (resp.next_todo) {\n const nxt = resp.next_todo;\n msg += `\\n\\n\\ud83d\\udd01 Next instance created:\\n#${nxt.id} \\u2014 ${nxt.task}`;\n if (nxt.due_at) msg += `\\n\\ud83d\\udcc5 Due: ${nxt.due_at.slice(0, 10)}`;\n}\n\nreturn [{ json: { chatId, message: msg } }];"
},
"name": "Format Todo Done",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1450,
1700
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Todo Done",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1700
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "DELETE",
"url": "=http://host.docker.internal:4242/brain/todo/{{ $json.subArgs }}",
"options": {
"response": {
"neverError": true
}
}
},
"name": "Delete Todo",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1250,
1800
]
},
{
"parameters": {
"jsCode": "const input = $input.first().json;\nconst chatId = $('Parse Todo Subcommand').first().json.chatId;\nconst todoId = $('Parse Todo Subcommand').first().json.subArgs;\n\nif (input.statusCode && input.statusCode >= 400) {\n return [{ json: { chatId, message: '\\u26a0\\ufe0f Todo not found. Check the ID and try again.' } }];\n}\n\nreturn [{ json: { chatId, message: `\\ud83d\\uddd1\\ufe0f Todo #${todoId} deleted.` } }];"
},
"name": "Format Todo Deleted",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1450,
1800
]
},
{
"parameters": {
"chatId": "={{ $json.chatId }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Send Todo Deleted",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
1650,
1800
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Execute Workflow Trigger": {
"main": [
[
{
"node": "Parse Command",
"type": "main",
"index": 0
}
]
]
},
"Parse Command": {
"main": [
[
{
"node": "Route Command",
"type": "main",
"index": 0
}
]
]
},
"Route Command": {
"main": [
[
{
"node": "Help Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Disk Usage",
"type": "main",
"index": 0
},
{
"node": "Get Memory",
"type": "main",
"index": 0
}
],
[
{
"node": "Save Memory",
"type": "main",
"index": 0
}
],
[
{
"node": "Get All Memory",
"type": "main",
"index": 0
}
],
[
{
"node": "Clear History",
"type": "main",
"index": 0
}
],
[],
[],
[],
[
{
"node": "Parse Email Subcommand",
"type": "main",
"index": 0
}
],
[
{
"node": "System Owner Check",
"type": "main",
"index": 0
}
],
[
{
"node": "Parse Todo Subcommand",
"type": "main",
"index": 0
}
]
]
},
"Get Disk Usage": {
"main": [
[
{
"node": "Format Status",
"type": "main",
"index": 0
}
]
]
},
"Get Memory": {
"main": [
[
{
"node": "Format Status",
"type": "main",
"index": 0
}
]
]
},
"Format Status": {
"main": [
[
{
"node": "Send Status",
"type": "main",
"index": 0
}
]
]
},
"Save Memory": {
"main": [
[
{
"node": "Confirm Remember",
"type": "main",
"index": 0
}
]
]
},
"Get All Memory": {
"main": [
[
{
"node": "Format Memory List",
"type": "main",
"index": 0
}
]
]
},
"Format Memory List": {
"main": [
[
{
"node": "Send Memory",
"type": "main",
"index": 0
}
]
]
},
"Clear History": {
"main": [
[
{
"node": "Confirm Clear",
"type": "main",
"index": 0
}
]
]
},
"Parse Email Subcommand": {
"main": [
[
{
"node": "Route Email Subcommand",
"type": "main",
"index": 0
}
]
]
},
"Route Email Subcommand": {
"main": [
[
{
"node": "Email Auth Message",
"type": "main",
"index": 0
}
],
[
{
"node": "Check Emails",
"type": "main",
"index": 0
}
],
[
{
"node": "Read Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Reply Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Search Emails",
"type": "main",
"index": 0
}
]
]
},
"System Owner Check": {
"main": [
[
{
"node": "Route System Subcommand",
"type": "main",
"index": 0
}
]
]
},
"Route System Subcommand": {
"main": [
[
{
"node": "System Access Denied",
"type": "main",
"index": 0
}
],
[
{
"node": "Docker Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Docker Restart",
"type": "main",
"index": 0
}
],
[
{
"node": "Docker Stop",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Power Status",
"type": "main",
"index": 0
}
]
]
},
"Docker Status": {
"main": [
[
{
"node": "Format Docker Status",
"type": "main",
"index": 0
}
]
]
},
"Format Docker Status": {
"main": [
[
{
"node": "Send System Status",
"type": "main",
"index": 0
}
]
]
},
"Docker Restart": {
"main": [
[
{
"node": "Send Restart Confirm",
"type": "main",
"index": 0
}
]
]
},
"Docker Stop": {
"main": [
[
{
"node": "Send Stop Confirm",
"type": "main",
"index": 0
}
]
]
},
"Get Power Status": {
"main": [
[
{
"node": "Format Wake Status",
"type": "main",
"index": 0
}
]
]
},
"Format Wake Status": {
"main": [
[
{
"node": "Send Wake Status",
"type": "main",
"index": 0
}
]
]
},
"Parse Todo Subcommand": {
"main": [
[
{
"node": "Route Todo Subcommand",
"type": "main",
"index": 0
}
]
]
},
"Route Todo Subcommand": {
"main": [
[
{
"node": "Fetch Todos",
"type": "main",
"index": 0
}
],
[
{
"node": "Parse Todo Add Args",
"type": "main",
"index": 0
}
],
[
{
"node": "Complete Todo",
"type": "main",
"index": 0
}
],
[
{
"node": "Delete Todo",
"type": "main",
"index": 0
}
]
]
},
"Fetch Todos": {
"main": [
[
{
"node": "Format Todo List",
"type": "main",
"index": 0
}
]
]
},
"Format Todo List": {
"main": [
[
{
"node": "Send Todo List",
"type": "main",
"index": 0
}
]
]
},
"Parse Todo Add Args": {
"main": [
[
{
"node": "Todo Add Error?",
"type": "main",
"index": 0
}
]
]
},
"Todo Add Error?": {
"main": [
[
{
"node": "Send Todo Add Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Todo",
"type": "main",
"index": 0
}
]
]
},
"Create Todo": {
"main": [
[
{
"node": "Format Todo Created",
"type": "main",
"index": 0
}
]
]
},
"Format Todo Created": {
"main": [
[
{
"node": "Send Todo Created",
"type": "main",
"index": 0
}
]
]
},
"Complete Todo": {
"main": [
[
{
"node": "Format Todo Done",
"type": "main",
"index": 0
}
]
]
},
"Format Todo Done": {
"main": [
[
{
"node": "Send Todo Done",
"type": "main",
"index": 0
}
]
]
},
"Delete Todo": {
"main": [
[
{
"node": "Format Todo Deleted",
"type": "main",
"index": 0
}
]
]
},
"Format Todo Deleted": {
"main": [
[
{
"node": "Send Todo Deleted",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
"ronku-bot",
"commands"
]
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgrestelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
03 - Command Handler. Uses executeWorkflowTrigger, telegram, executeCommand, postgres. Event-driven trigger; 53 nodes.
Source: https://github.com/rohankag/ronkbot/blob/db03432335532bdeb4ebcee0c72d9109e04b466f/n8n-workflows/03-command-handler.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
VIVID v5.0 — Chapter Sub-workflow. Uses executeWorkflowTrigger, executeCommand, itemLists, httpRequest. Event-driven trigger; 21 nodes.
Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 57 nodes.
Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 53 nodes.
Deal-Finder. Uses executeWorkflowTrigger, googleSheets, perplexity, httpRequest. Event-driven trigger; 49 nodes.
Execute_Command. Uses executeWorkflowTrigger, postgres, discord, httpRequest. Event-driven trigger; 47 nodes.