AutomationFlowsSlack & Telegram › Telegram Query Bot

Telegram Query Bot

Telegram Query Bot. Uses telegramTrigger, httpRequest, telegram. Event-driven trigger; 16 nodes.

Event trigger★★★★☆ complexity16 nodesTelegram TriggerHTTP RequestTelegram
Slack & Telegram Trigger: Event Nodes: 16 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Telegram 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 →

Download .json
{
  "name": "Telegram Query Bot",
  "nodes": [
    {
      "id": "tg-trigger-id",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1,
      "position": [
        100,
        300
      ],
      "parameters": {
        "updates": [
          "message",
          "callback_query"
        ],
        "additionalFields": {}
      }
    },
    {
      "id": "parse-command-id",
      "name": "Parse Command",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        280,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const msg      = $json.message;\nconst callback = $json.callback_query;\n\nlet command, args, chatId, msgId, callbackQueryId;\n\nif (callback) {\n  const data      = callback.data || '';\n  const colonIdx  = data.indexOf(':');\n  command         = colonIdx >= 0 ? data.slice(0, colonIdx) : data;\n  args            = colonIdx >= 0 ? data.slice(colonIdx + 1) : '';\n  chatId          = callback.message?.chat?.id?.toString() || '';\n  msgId           = callback.message?.message_id;\n  callbackQueryId = callback.id;\n} else {\n  const text = (msg?.text || '').trim();\n  chatId = msg?.chat?.id?.toString() || '';\n  msgId  = msg?.message_id;\n  callbackQueryId = null;\n\n  if (text.startsWith('/search')) {\n    command = 'search';\n    args    = text.replace(/^\\/search\\s*/i, '').trim();\n  } else if (text.startsWith('/latest')) {\n    command = 'latest';\n    const match = text.match(/(\\d+)/);\n    args = match ? match[1] : '5';\n  } else if (text.startsWith('/stats')) {\n    command = 'stats';\n    args    = '';\n  } else if (text.startsWith('/help')) {\n    command = 'help';\n    args    = '';\n  } else {\n    command = 'unknown';\n    args    = '';\n  }\n}\n\nreturn { json: { command, args, chatId, msgId, callbackQueryId } };\n"
      }
    },
    {
      "id": "route-command-id",
      "name": "Route Command",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        460,
        300
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "search",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "0"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "latest",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "1"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "help",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "2"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "stats",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "3"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "expand",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "4"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "skip",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "5"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "save",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "6"
            }
          ]
        },
        "fallbackOutput": "extra"
      }
    },
    {
      "id": "call-search-id",
      "name": "Call Search Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        660,
        160
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://{{ $vars.N8N_DOMAIN }}/webhook/search",
        "sendBody": true,
        "contentType": "json",
        "body": {
          "query": "={{ $json.args }}",
          "top_k": 5
        },
        "options": {
          "timeout": 60000
        }
      }
    },
    {
      "id": "format-search-reply-id",
      "name": "Format Search Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        860,
        160
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const chatId = $('Parse Command').first().json.chatId;\nconst data   = $json;\n\nconst sourcesText = (data.sources || []).slice(0, 5).map((s, i) =>\n  `${i + 1}. [${s.title}](${s.url})\\n   _${s.relevance_to_query || ''}_`\n).join('\\n\\n');\n\nconst confidence = data.confidence ? `\\n\\n\ud83d\udcca *Confidence:* ${data.confidence}` : '';\n\nconst text = [\n  `\ud83d\udd0d *Query:* ${data.query}`,\n  '',\n  data.answer || '_No answer generated._',\n  confidence,\n  sourcesText ? `\\n\\n\ud83d\udcc4 *Sources:*\\n${sourcesText}` : '\\n\\n_No relevant papers found._'\n].join('\\n');\n\nreturn { json: { chatId, text } };\n"
      }
    },
    {
      "id": "fetch-latest-id",
      "name": "Fetch Latest Papers",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const limit = Math.min(parseInt($json.args) || 5, 10);\nconst chatId = $json.chatId;\n\n// Scroll the arxiv_papers collection ordered by score (proxy for recency)\n// Qdrant scroll returns points in insertion order by default\nconst response = await fetch('http://qdrant:6333/collections/arxiv_papers/points/scroll', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    limit,\n    with_payload: true,\n    order_by: { key: 'metadata.ingested_at', direction: 'desc' }\n  })\n});\n\nif (!response.ok) {\n  const err = await response.text();\n  throw new Error(`Qdrant scroll failed: ${err}`);\n}\n\nconst data = await response.json();\nconst points = (data.result?.points || []).map(p => ({\n  title:     p.payload?.metadata?.title     || p.payload?.title     || 'Untitled',\n  url:       p.payload?.metadata?.url       || p.payload?.url       || '',\n  relevance: p.payload?.metadata?.relevance || p.payload?.relevance || '',\n  tags:      p.payload?.metadata?.tags      || p.payload?.tags      || []\n}));\n\nreturn { json: { chatId, papers: points } };\n"
      }
    },
    {
      "id": "format-latest-reply-id",
      "name": "Format Latest Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        860,
        300
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const { chatId, papers } = $json;\n\nif (!papers || papers.length === 0) {\n  return { json: { chatId, text: '\ud83d\udced No papers found in the database yet.' } };\n}\n\nconst lines = papers.map((p, i) => {\n  const tags = Array.isArray(p.tags) ? p.tags.join(', ') : p.tags || '';\n  const rel  = p.relevance ? ` \u00b7 ${p.relevance}` : '';\n  return `${i + 1}. [${p.title}](${p.url})\\n   \ud83c\udff7\ufe0f ${tags}${rel}`;\n});\n\nconst text = `\ud83d\udcda *Latest papers:*\\n\\n${lines.join('\\n\\n')}`;\nreturn { json: { chatId, text } };\n"
      }
    },
    {
      "id": "help-reply-id",
      "name": "Help Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        440
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const chatId = $json.chatId;\nconst text = [\n  '\ud83e\udd16 *ResearchFlow AI \u2014 Commands*',\n  '',\n  '`/search <question>` \u2014 Ask a question and get a cited answer from stored papers',\n  '_Example: /search How do mixture of experts models scale?_',\n  '',\n  '`/latest [n]` \u2014 Show the n most recently ingested papers (default 5, max 10)',\n  '_Example: /latest 3_',\n  '',\n  '`/stats` \u2014 Show a 7-day run summary (papers indexed, relevance breakdown, fallback rate)',\n  '',\n  '`/help` \u2014 Show this message'\n].join('\\n');\nreturn { json: { chatId, text } };\n"
      }
    },
    {
      "id": "unknown-reply-id",
      "name": "Unknown Command Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        580
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const chatId = $json.chatId;\nconst text = '\u2753 Unknown command. Send /help to see available commands.';\nreturn { json: { chatId, text } };\n"
      }
    },
    {
      "id": "send-reply-id",
      "name": "Send Reply",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1060,
        300
      ],
      "parameters": {
        "resource": "message",
        "operation": "sendMessage",
        "chatId": "={{ $json.chatId }}",
        "text": "={{ $json.text }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "disable_web_page_preview": true
        }
      }
    },
    {
      "id": "answer-callback-id",
      "name": "Answer Callback Query",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        860,
        720
      ],
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{ $vars.TELEGRAM_BOT_TOKEN }}/answerCallbackQuery",
        "sendBody": true,
        "contentType": "json",
        "body": {
          "callback_query_id": "={{ $json.callbackQueryId }}",
          "text": "={{ $json.command === 'expand' ? '\ud83d\udd0d Fetching deep summary...' : $json.command === 'skip' ? '\u23ed Marked as skipped' : '\ud83d\udd16 Saved to your library' }}"
        },
        "options": {}
      }
    },
    {
      "id": "handle-expand-id",
      "name": "Handle Expand",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1260,
        580
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Fetch the paper from Qdrant by URL, then call OpenAI\n// for a deeper summary including methodology and limitations.\nconst paperUrl = $json.args;\nconst chatId   = $json.chatId;\n\n// 1. Search Qdrant for the paper by URL metadata field\nconst searchResp = await fetch('http://qdrant:6333/collections/arxiv_papers/points/scroll', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    filter: {\n      must: [{ key: 'metadata.url', match: { value: paperUrl } }]\n    },\n    limit: 1,\n    with_payload: true\n  })\n});\n\nconst searchData = await searchResp.json();\nconst point = searchData.result?.points?.[0];\n\nif (!point) {\n  return { json: { chatId, text: '\u274c Paper not found in the database.' } };\n}\n\nconst meta   = point.payload?.metadata || point.payload || {};\nconst title  = meta.title  || 'Unknown title';\nconst stored = point.payload?.document || meta.abstract || '';\n\n// 2. Call GPT-4o for a deeper analysis\nconst gptResp = await fetch('https://api.openai.com/v1/chat/completions', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'Authorization': `Bearer ${$vars.OPENAI_API_KEY}`\n  },\n  body: JSON.stringify({\n    model: 'gpt-4o',\n    max_tokens: 800,\n    messages: [\n      {\n        role: 'system',\n        content: 'You are a research assistant. Given a paper summary, produce a deeper analysis covering: (1) Methodology in detail \u2014 architecture, training procedure, key equations if any; (2) Limitations and open questions; (3) Practical applications; (4) Related work it builds on. Be technical but concise. Use plain text, no markdown headers.'\n      },\n      {\n        role: 'user',\n        content: `Title: ${title}\\n\\nSummary:\\n${stored}`\n      }\n    ]\n  })\n});\n\nconst gptData = await gptResp.json();\nconst analysis = gptData.choices?.[0]?.message?.content || 'Could not generate analysis.';\n\nconst text = [\n  `\ud83d\udd0d *Deep analysis: ${title}*`,\n  '',\n  analysis,\n  '',\n  `\ud83d\udd17 ${paperUrl}`\n].join('\\n');\n\nreturn { json: { chatId, text } };\n"
      }
    },
    {
      "id": "handle-skip-id",
      "name": "Handle Skip",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1260,
        720
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Find the Qdrant point for this paper and set user_rating: -1\nconst paperUrl = $json.args;\nconst chatId   = $json.chatId;\n\n// Scroll to find the point ID\nconst scrollResp = await fetch('http://qdrant:6333/collections/arxiv_papers/points/scroll', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    filter: { must: [{ key: 'metadata.url', match: { value: paperUrl } }] },\n    limit: 1,\n    with_payload: false\n  })\n});\n\nconst scrollData = await scrollResp.json();\nconst pointId = scrollData.result?.points?.[0]?.id;\n\nif (!pointId) {\n  return { json: { chatId, text: '\u274c Paper not found \u2014 cannot update rating.' } };\n}\n\n// PATCH the payload\nconst patchResp = await fetch(`http://qdrant:6333/collections/arxiv_papers/points/payload`, {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    payload: { 'metadata.user_rating': -1, 'metadata.user_rated_at': new Date().toISOString() },\n    points: [pointId]\n  })\n});\n\nif (!patchResp.ok) {\n  const err = await patchResp.text();\n  throw new Error(`Qdrant PATCH failed: ${err}`);\n}\n\nreturn { json: { chatId, text: '\u23ed Got it \u2014 this paper will be deprioritised in future searches.' } };\n"
      }
    },
    {
      "id": "handle-save-id",
      "name": "Handle Save",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1260,
        860
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Find the Qdrant point for this paper and set user_rating: 1\nconst paperUrl = $json.args;\nconst chatId   = $json.chatId;\n\nconst scrollResp = await fetch('http://qdrant:6333/collections/arxiv_papers/points/scroll', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    filter: { must: [{ key: 'metadata.url', match: { value: paperUrl } }] },\n    limit: 1,\n    with_payload: false\n  })\n});\n\nconst scrollData = await scrollResp.json();\nconst pointId = scrollData.result?.points?.[0]?.id;\n\nif (!pointId) {\n  return { json: { chatId, text: '\u274c Paper not found \u2014 cannot update rating.' } };\n}\n\nconst patchResp = await fetch(`http://qdrant:6333/collections/arxiv_papers/points/payload`, {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({\n    payload: { 'metadata.user_rating': 1, 'metadata.user_rated_at': new Date().toISOString() },\n    points: [pointId]\n  })\n});\n\nif (!patchResp.ok) {\n  const err = await patchResp.text();\n  throw new Error(`Qdrant PATCH failed: ${err}`);\n}\n\nreturn { json: { chatId, text: '\ud83d\udd16 Saved! This paper is now in your library. It will be used as a quality signal for future prompt tuning.' } };\n"
      }
    },
    {
      "id": "route-feedback-id",
      "name": "Route Feedback",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1060,
        720
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "expand",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "0"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "skip",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "1"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.command }}",
                    "rightValue": "save",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "outputKey": "2"
            }
          ]
        },
        "fallbackOutput": "none"
      }
    },
    {
      "id": "fetch-stats-id",
      "name": "Fetch Stats",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        720
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const chatId = $json.chatId;\n\n// Scroll the run_logs collection \u2014 small collection, no pagination needed\nconst resp = await fetch('http://qdrant:6333/collections/run_logs/points/scroll', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'api-key': $vars.QDRANT_API_KEY || ''\n  },\n  body: JSON.stringify({ limit: 100, with_payload: true })\n});\n\nif (!resp.ok) {\n  const err = await resp.text();\n  return { json: { chatId, text: `\u274c Could not fetch stats: ${err}` } };\n}\n\nconst data   = await resp.json();\nconst points = data.result?.points || [];\n\nif (points.length === 0) {\n  return { json: { chatId, text: '\ud83d\udcca No runs logged yet. Stats will appear after the first pipeline run.' } };\n}\n\n// Filter to last 7 days\nconst since7d = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);\nconst recentRuns = points\n  .map(p => p.payload)\n  .filter(p => p?.timestamp && new Date(p.timestamp) >= since7d)\n  .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));\n\nconst allRuns = points.map(p => p.payload).filter(Boolean);\n\nif (recentRuns.length === 0) {\n  return { json: { chatId, text: '\ud83d\udcca No runs in the last 7 days.' } };\n}\n\n// Aggregate\nconst sum = (arr, key) => arr.reduce((a, r) => a + (r[key] || 0), 0);\nconst totalFetched   = sum(recentRuns, 'papers_fetched');\nconst totalNew       = sum(recentRuns, 'processed');\nconst totalSeen      = sum(recentRuns, 'already_seen');\nconst totalHigh      = sum(recentRuns, 'high');\nconst totalMedium    = sum(recentRuns, 'medium');\nconst totalLow       = sum(recentRuns, 'low');\nconst totalFallbacks = sum(recentRuns, 'pdf_fallbacks');\nconst avgDuration    = recentRuns.reduce((a, r) => a + (r.duration_ms || 0), 0) / recentRuns.length;\nconst fallbackRate   = totalNew > 0 ? ((totalFallbacks / totalNew) * 100).toFixed(1) : '0.0';\n\n// Last 7 runs table\nconst runRows = recentRuns.slice(0, 7).map(r => {\n  const date = new Date(r.timestamp).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });\n  return `  ${date}: ${r.processed ?? 0} new (H:${r.high ?? 0} M:${r.medium ?? 0} L:${r.low ?? 0})`;\n}).join('\\n');\n\nconst text = [\n  `\ud83d\udcca *ResearchFlow \u2014 7-day stats* (${recentRuns.length} runs)`,\n  '',\n  `\ud83d\udce5 Fetched: ${totalFetched} \u00b7 Already seen: ${totalSeen} \u00b7 New: ${totalNew}`,\n  `\u2705 High: ${totalHigh} \u00b7 \u26a1 Medium: ${totalMedium} \u00b7 \ud83d\udd07 Low: ${totalLow}`,\n  `\u26a0\ufe0f PDF fallback rate: ${fallbackRate}%`,\n  `\u23f1 Avg run time: ${(avgDuration / 1000).toFixed(1)}s`,\n  '',\n  '*Recent runs:*',\n  runRows,\n  '',\n  `\ud83d\udce6 Total runs logged: ${allRuns.length}`\n].join('\\n');\n\nreturn { json: { chatId, text } };\n"
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Parse Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Command": {
      "main": [
        [
          {
            "node": "Route Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Command": {
      "main": [
        [
          {
            "node": "Call Search Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch Latest Papers",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Help Reply",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch Stats",
            "type": "main",
            "index": 0
          }
        ],
        [],
        [
          {
            "node": "Answer Callback Query",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Answer Callback Query",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Answer Callback Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call Search Webhook": {
      "main": [
        [
          {
            "node": "Format Search Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Search Reply": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Latest Papers": {
      "main": [
        [
          {
            "node": "Format Latest Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Latest Reply": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Help Reply": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Unknown Command Reply": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Answer Callback Query": {
      "main": [
        [
          {
            "node": "Route Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Feedback": {
      "main": [
        [
          {
            "node": "Handle Expand",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Skip",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Save",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Expand": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Skip": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Save": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Stats": {
      "main": [
        [
          {
            "node": "Send Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": ""
  },
  "tags": [
    {
      "name": "search"
    },
    {
      "name": "telegram"
    }
  ]
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Telegram Query Bot. Uses telegramTrigger, httpRequest, telegram. Event-driven trigger; 16 nodes.

Source: https://github.com/keila-moral/researchflow-ai/blob/main/workflows/Telegram_Query_Bot.json — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

N8N Complete Final. Uses telegramTrigger, dataTable, telegram, mqtt. Event-driven trigger; 58 nodes.

Telegram Trigger, Data Table, Telegram +3
Slack & Telegram

Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 57 nodes.

HTTP Request, Telegram, Postgres +1
Slack & Telegram

TextMain. Uses telegramTrigger, stopAndError, telegram, httpRequest. Event-driven trigger; 56 nodes.

Telegram Trigger, Stop And Error, Telegram +2
Slack & Telegram

Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 53 nodes.

HTTP Request, Telegram, Postgres +1
Slack & Telegram

📄 Documentation: Notion Guide

Telegram Trigger, @Blotato/N8N Nodes Blotato, Telegram +1