{
  "name": "50_540_TELEGRAM_ASSISTANT",
  "nodes": [
    {
      "id": "tg-webhook",
      "name": "Telegram Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        200,
        300
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "telegram-assistant",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "tg-extract",
      "name": "Extract & Guard",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        300
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json?.body ?? $input.first().json;\n\nconst isCallback = !!body.callback_query;\nconst message = body.message ?? body.callback_query?.message ?? {};\nconst callbackData = body.callback_query?.data ?? null;\nconst callbackQueryId = body.callback_query?.id ?? null;\n\nconst chatId = message?.chat?.id ?? body.callback_query?.from?.id;\nconst fromId = message?.from?.id ?? body.callback_query?.from?.id;\nconst messageId = message?.message_id ?? null;\nconst text = callbackData ?? message?.text ?? '';\n\nconst allowedChatId = $env.TELEGRAM_ALLOWED_CHAT_ID ? parseInt($env.TELEGRAM_ALLOWED_CHAT_ID) : null;\nconst blocked = allowedChatId !== null && chatId !== allowedChatId;\n\nconst isConfirmation = isCallback && (callbackData?.startsWith('confirm_') || callbackData === 'cancel');\n\nreturn [{ json: {\n  chatId, fromId, messageId, text,\n  isCallback, callbackQueryId, callbackData, isConfirmation, blocked\n} }];"
      }
    },
    {
      "id": "tg-guard-check",
      "name": "Blocked?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        640,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-blocked",
              "leftValue": "={{ $json.blocked }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "tg-respond-200-silent",
      "name": "Respond 200 Silent",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        860,
        200
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ok: true}) }}"
      }
    },
    {
      "id": "tg-respond-200-ok",
      "name": "Respond 200 OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        860,
        400
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ok: true}) }}"
      }
    },
    {
      "id": "tg-ack-callback",
      "name": "ACK Callback Query",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1080,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/answerCallbackQuery",
        "authentication": "none",
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ callback_query_id: $json.callbackQueryId ?? '' }) }}",
        "options": {
          "timeout": 5000
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "id": "tg-is-callback",
      "name": "Is Callback?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cb-check",
              "leftValue": "={{ $json.isCallback }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "tg-intent-ai",
      "name": "AI Intent Classifier",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1520,
        400
      ],
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.OPENROUTER_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({\n  model: 'google/gemini-2.0-flash:free',\n  messages: [\n    {\n      role: 'system',\n      content: 'You are an intent classifier for an AIOS automation control bot. Analyze the user message and return ONLY valid JSON without markdown or explanation.\\n\\nAvailable intents:\\n- LIST_WORKFLOWS: show all n8n workflows\\n- ACTIVATE_WORKFLOW: enable/activate a workflow\\n- DEACTIVATE_WORKFLOW: disable/stop/deactivate a workflow\\n- TRIGGER_WORKFLOW: run/execute/start a workflow now\\n- UPDATE_SCHEDULE: change when a workflow runs (schedule/cron)\\n- STATUS_REPORT: system status, health check, overview, Systemstatus\\n- N8N_STATS: n8n execution statistics\\n- AGENTS: agent teams, crew status, KI-Agenten, Crews\\n- CONTENT_PIECES: content pieces, generierter Content, Drafts, Artikel\\n- PIPELINE: content pipeline, pipeline jobs, laufende Jobs\\n- AUDIT: audit trail, recent actions, was ist passiert, Protokoll\\n- NOCODB_TRENDS: trend data, research trends, aktuelle Trends\\n- NOCODB_SENTIMENT: sentiment analysis data, Stimmung\\n- NOCODB_CONTENT: content opportunities, Content-Ideen, Chancen\\n- ALERTS: alerts from Grafana or Prometheus, Warnungen\\n- HELP: list available commands, Hilfe, what can you do\\n- UNKNOWN: none of the above\\n\\nReturn this exact JSON structure:\\n{\"intent\":\"LIST_WORKFLOWS\",\"workflow_name\":null,\"workflow_id\":null,\"cron_expression\":null,\"cron_human\":null,\"confidence\":0.95}\\n\\nFor UPDATE_SCHEDULE: parse natural language schedule into cron expression.\\nExamples: \"zweimal t\u00e4glich\" \u2192 \"0 8,20 * * *\" | \"jeden Tag um 7\" \u2192 \"0 7 * * *\" | \"st\u00fcndlich\" \u2192 \"0 * * * *\"'\n    },\n    {\n      role: 'user',\n      content: $('Extract & Guard').first().json.text\n    }\n  ],\n  temperature: 0.1,\n  max_tokens: 200\n}) }}",
        "options": {
          "timeout": 15000
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "id": "tg-parse-intent",
      "name": "Parse Intent",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        400
      ],
      "parameters": {
        "jsCode": "const guardData = $('Extract & Guard').first().json;\nconst chatId = guardData.chatId;\nconst text = guardData.text;\n\nconst raw = $input.first().json?.choices?.[0]?.message?.content ?? '{}';\n\nlet parsed = { intent: 'UNKNOWN', workflow_name: null, workflow_id: null, cron_expression: null, cron_human: null, confidence: 0 };\ntry {\n  const cleaned = raw.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n  parsed = { ...parsed, ...JSON.parse(cleaned) };\n} catch(e) {\n  parsed.intent = 'UNKNOWN';\n}\n\n// Merge N8N_STATS into STATUS_REPORT\nif (parsed.intent === 'N8N_STATS') parsed.intent = 'STATUS_REPORT';\n\nreturn [{ json: {\n  ...parsed,\n  chatId,\n  originalText: text,\n  isCallback: false,\n  callbackData: null,\n  isConfirmation: false\n} }];"
      }
    },
    {
      "id": "tg-callback-prep",
      "name": "Prep Callback Intent",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1520,
        240
      ],
      "parameters": {
        "jsCode": "const guardData = $('Extract & Guard').first().json;\nreturn [{ json: {\n  intent: 'LIST_WORKFLOWS',\n  chatId: guardData.chatId,\n  callbackData: guardData.callbackData,\n  isConfirmation: true,\n  isCallback: true,\n  workflow_name: null,\n  workflow_id: null,\n  cron_expression: null,\n  cron_human: null,\n  confidence: 1.0,\n  originalText: guardData.text\n} }];"
      }
    },
    {
      "id": "tg-router",
      "name": "Intent Router",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        2000,
        340
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "outputKey": "WORKFLOW_CONTROL",
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": ""
                },
                "conditions": [
                  {
                    "leftValue": "={{ ['LIST_WORKFLOWS','ACTIVATE_WORKFLOW','DEACTIVATE_WORKFLOW','TRIGGER_WORKFLOW','UPDATE_SCHEDULE'].includes($json.intent) || $json.isConfirmation === true }}",
                    "rightValue": true,
                    "operator": {
                      "type": "boolean",
                      "operation": "equal"
                    }
                  }
                ]
              }
            },
            {
              "outputKey": "STATUS",
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": ""
                },
                "conditions": [
                  {
                    "leftValue": "={{ ['STATUS_REPORT','ALERTS'].includes($json.intent) }}",
                    "rightValue": true,
                    "operator": {
                      "type": "boolean",
                      "operation": "equal"
                    }
                  }
                ]
              }
            },
            {
              "outputKey": "RESEARCH",
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": ""
                },
                "conditions": [
                  {
                    "leftValue": "={{ ['NOCODB_TRENDS','NOCODB_SENTIMENT','NOCODB_CONTENT','AGENTS','CONTENT_PIECES','PIPELINE','AUDIT'].includes($json.intent) }}",
                    "rightValue": true,
                    "operator": {
                      "type": "boolean",
                      "operation": "equal"
                    }
                  }
                ]
              }
            },
            {
              "outputKey": "HELP",
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": ""
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.intent }}",
                    "rightValue": "HELP",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      }
    },
    {
      "id": "tg-exec-workflow-control",
      "name": "Run 541 Workflow Control",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.1,
      "position": [
        2240,
        220
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "sJeuBZW98Cu8DTRv",
          "mode": "id"
        },
        "waitForSubWorkflow": true,
        "inputData": "={{ $json }}"
      }
    },
    {
      "id": "tg-exec-status",
      "name": "Run 542 Status Report",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.1,
      "position": [
        2240,
        340
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "PJooaBKts3OiDQHN",
          "mode": "id"
        },
        "waitForSubWorkflow": true,
        "inputData": "={{ $json }}"
      }
    },
    {
      "id": "tg-exec-research",
      "name": "Run 543 Research Data",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.1,
      "position": [
        2240,
        460
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "UGDbCCci4P1SMjW6",
          "mode": "id"
        },
        "waitForSubWorkflow": true,
        "inputData": "={{ $json }}"
      }
    },
    {
      "id": "tg-help",
      "name": "Help Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2240,
        580
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "h1",
              "name": "chatId",
              "value": "={{ $json.chatId }}",
              "type": "string"
            },
            {
              "id": "h2",
              "name": "response",
              "value": "*AIOS Control Bot*\n\n*Workflow-Steuerung:*\n\u2022 `workflows` \u2014 alle Workflows anzeigen\n\u2022 `aktiviere [Name]` \u2014 Workflow aktivieren\n\u2022 `deaktiviere [Name]` \u2014 Workflow stoppen\n\u2022 `starte [Name]` \u2014 Workflow jetzt ausf\u00fchren\n\u2022 `lass [Name] t\u00e4glich um 8 laufen` \u2014 Zeitplan \u00e4ndern\n\n*System Status:*\n\u2022 `status` \u2014 n8n + Crews + Prometheus \u00dcbersicht\n\u2022 `alerts` \u2014 feuernde Prometheus Alerts\n\n*Agent Teams:*\n\u2022 `agents` \u2014 Crew AI Teams + letzter Run\n\u2022 `pipeline` \u2014 aktive Pipeline-Jobs\n\n*Content:*\n\u2022 `content` \u2014 letzte Content Pieces (Drafts/Published)\n\u2022 `audit` \u2014 letzte Aktionen im Audit-Trail\n\n*Research-Daten:*\n\u2022 `trends` \u2014 aktuelle Trends (t\u00e4gl. 08:00)\n\u2022 `sentiment` \u2014 Sentiment-Analyse (Mo. 08:00)\n\u2022 `chancen` \u2014 Content Opportunities (t\u00e4gl. 09:30)\n\n_Fragen auf Deutsch oder Englisch m\u00f6glich._",
              "type": "string"
            }
          ]
        }
      }
    },
    {
      "id": "tg-unknown",
      "name": "Unknown Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2240,
        700
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "u1",
              "name": "chatId",
              "value": "={{ $json.chatId }}",
              "type": "string"
            },
            {
              "id": "u2",
              "name": "response",
              "value": "\u2753 Das habe ich nicht verstanden.\n\nTippe *hilfe* f\u00fcr verf\u00fcgbare Befehle.",
              "type": "string"
            }
          ]
        }
      }
    },
    {
      "id": "tg-merge-response",
      "name": "Merge Responses",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        2480,
        420
      ],
      "parameters": {
        "mode": "passThrough",
        "output": "input1"
      }
    },
    {
      "id": "tg-format",
      "name": "Format for Telegram",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2700,
        420
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nconst chatId = item.chatId;\nlet response = item.response ?? '';\n\nif (!response || response.trim() === '') {\n  return [{ json: { chatId, response: null, skipSend: true } }];\n}\n\nconst MAX_LEN = 4000;\nif (response.length > MAX_LEN) {\n  response = response.substring(0, MAX_LEN) + '\\n\\n_[Ausgabe gek\u00fcrzt]_';\n}\n\nreturn [{ json: { chatId, response, skipSend: false } }];"
      }
    },
    {
      "id": "tg-send-check",
      "name": "Skip Send?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2920,
        420
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "skip-send",
              "leftValue": "={{ $json.skipSend }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "tg-send-message",
      "name": "Send Telegram Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3160,
        480
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/sendMessage",
        "authentication": "none",
        "sendBody": true,
        "specifyBody": "string",
        "body": "={{ JSON.stringify({ chat_id: $json.chatId, text: $json.response, parse_mode: 'Markdown' }) }}",
        "options": {
          "timeout": 10000
        }
      },
      "onError": "continueRegularOutput"
    }
  ],
  "connections": {
    "Telegram Webhook": {
      "main": [
        [
          {
            "node": "Extract & Guard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract & Guard": {
      "main": [
        [
          {
            "node": "Blocked?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Blocked?": {
      "main": [
        [
          {
            "node": "Respond 200 Silent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond 200 OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond 200 OK": {
      "main": [
        [
          {
            "node": "ACK Callback Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ACK Callback Query": {
      "main": [
        [
          {
            "node": "Is Callback?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Callback?": {
      "main": [
        [
          {
            "node": "Prep Callback Intent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Intent Classifier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Callback Intent": {
      "main": [
        [
          {
            "node": "Intent Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Intent Classifier": {
      "main": [
        [
          {
            "node": "Parse Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Intent": {
      "main": [
        [
          {
            "node": "Intent Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Intent Router": {
      "main": [
        [
          {
            "node": "Run 541 Workflow Control",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run 542 Status Report",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run 543 Research Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Help Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Unknown Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run 541 Workflow Control": {
      "main": [
        [
          {
            "node": "Merge Responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run 542 Status Report": {
      "main": [
        [
          {
            "node": "Merge Responses",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Run 543 Research Data": {
      "main": [
        [
          {
            "node": "Merge Responses",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Help Response": {
      "main": [
        [
          {
            "node": "Merge Responses",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Unknown Response": {
      "main": [
        [
          {
            "node": "Merge Responses",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Merge Responses": {
      "main": [
        [
          {
            "node": "Format for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for Telegram": {
      "main": [
        [
          {
            "node": "Skip Send?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Send?": {
      "main": [
        [],
        [
          {
            "node": "Send Telegram Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "none"
  },
  "staticData": null,
  "tags": [
    "telegram"
  ],
  "meta": {
    "description": "540 \u2014 Telegram AI Chatbot Dispatcher: empf\u00e4ngt Nachrichten, klassifiziert Intent via Gemini Flash, routet zu 541/542/543"
  }
}