AutomationFlowsAI & RAG › Forum Pulse for Telegram: Community Monitoring with Gemini & Groq AI Models

Forum Pulse for Telegram: Community Monitoring with Gemini & Groq AI Models

ByNguyễn Thiệu Toàn (Jay Nguyen) @nguyenthieutoan on n8n.io

Author: Nguyen Thieu Toan Category: Community & Knowledge Automation Tags: Telegram, Reddit, n8n Forum, AI Summarization, Gemini, Groq

Cron / scheduled trigger★★★★★ complexityAI-powered65 nodesGroq ChatOutput Parser StructuredMemory Mongo Db ChatHTTP RequestGoogle Gemini ChatTelegramTelegram TriggerAgent
AI & RAG Trigger: Cron / scheduled Nodes: 65 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #8573 — we link there as the canonical source.

This workflow follows the Agent → 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "e22ea54c-d92b-455d-80e9-5d82c9fd5c7f",
      "name": "Groq Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        -4640,
        32
      ],
      "parameters": {
        "model": "openai/gpt-oss-120b",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "131ad1c0-bb33-4ed1-b07e-43439e513c37",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -4256,
        32
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"intent\": \"search\",\n  \"keyword\": \"Webhook signature verification\",\n  \"platforms\": \"all\",\n  \"reddit_sort\": \"top\",\n  \"reddit_time\": \"year\",\n  \"n8ncom_sort\": \"likes\",\n  \"n8ncom_time\": \"after:2025-01-01\",\n  \"limit\": 10,\n  \"page\": 1,\n  \"link_url\": \"https://nguyenthieutoan.com\",\n  \"language\": \"vi\",\n  \"confidence\": 0.95\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "e6c99b66-658d-4643-b560-06b483199bd6",
      "name": "MongoDB Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryMongoDbChat",
      "position": [
        -4448,
        16
      ],
      "parameters": {
        "sessionKey": "={{ $('Telegram Trigger - User Message').item.json.message.from.id }}",
        "databaseName": "n8n_admin_chat",
        "sessionIdType": "customKey",
        "collectionName": "n8n_forum_update_chat"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "439200ab-1e43-4458-aadd-30fe644462fc",
      "name": "If Reddit?",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2912,
        304
      ],
      "parameters": {
        "url": "={{ $json.output.link_url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "157b9d76-9081-4825-ad4d-52176c371cc6",
      "name": "Comment",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2464,
        160
      ],
      "parameters": {
        "url": "=https://www.reddit.com{{ $json.comment_url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "f35d422d-6bf1-43ad-a414-3989c0abe075",
      "name": "Get Post Content",
      "type": "n8n-nodes-base.html",
      "position": [
        -2688,
        304
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "post_title",
              "cssSelector": "h1"
            },
            {
              "key": "post_author",
              "cssSelector": "a.author-name"
            },
            {
              "key": "post_content",
              "cssSelector": "div[slot='text-body']"
            },
            {
              "key": "post_upvotes",
              "attribute": "score",
              "cssSelector": "shreddit-post",
              "returnValue": "attribute"
            },
            {
              "key": "commentCount",
              "attribute": "comment-count",
              "cssSelector": "shreddit-post",
              "returnValue": "attribute"
            },
            {
              "key": "postTime",
              "attribute": "ts",
              "cssSelector": "faceplate-timeago",
              "returnValue": "attribute"
            },
            {
              "key": "flair",
              "cssSelector": "shreddit-post-flair .flair-content"
            },
            {
              "key": "comment_url",
              "attribute": "src",
              "cssSelector": "faceplate-partial[src^=\"/svc/shreddit/comments/\"]",
              "returnValue": "attribute"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d2c77457-4b74-43fc-8787-6449e7d4fbf7",
      "name": "Get Comment",
      "type": "n8n-nodes-base.html",
      "position": [
        -2240,
        160
      ],
      "parameters": {
        "options": {
          "trimValues": true,
          "cleanUpText": true
        },
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "comment_author",
              "attribute": "author",
              "cssSelector": "shreddit-comment",
              "returnArray": true,
              "returnValue": "attribute"
            },
            {
              "key": "comment_content",
              "cssSelector": "shreddit-comment div[slot='comment']",
              "returnArray": true,
              "returnValue": "html"
            },
            {
              "key": "comment_upvotes",
              "attribute": "score",
              "cssSelector": "shreddit-comment",
              "returnArray": true,
              "returnValue": "attribute"
            },
            {
              "key": "comment_level",
              "attribute": "depth",
              "cssSelector": "shreddit-comment",
              "returnArray": true,
              "returnValue": "attribute"
            },
            {
              "key": "comment_id",
              "attribute": "thingid",
              "cssSelector": "shreddit-comment",
              "returnArray": true,
              "returnValue": "attribute"
            },
            {
              "key": "parent_id",
              "attribute": "parentid",
              "cssSelector": "shreddit-comment",
              "returnArray": true,
              "returnValue": "attribute"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "22f67ac5-02d7-41ed-840b-904a537145c8",
      "name": "If n8n Community?",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2912,
        496
      ],
      "parameters": {
        "url": "={{ $('Detect User Intent').item.json.output.link_url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a4e59ef0-159e-4099-bfca-636af4dfc8d9",
      "name": "Get Topic Content",
      "type": "n8n-nodes-base.html",
      "position": [
        -2464,
        400
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "topic_title",
              "cssSelector": "#topic-title a"
            },
            {
              "key": "category",
              "cssSelector": ".topic-category .category-name"
            },
            {
              "key": "original_poster",
              "cssSelector": "#post_1 .creator span[itemprop='name']"
            },
            {
              "key": "original_post_content",
              "cssSelector": "#post_1 div.post[itemprop='text']\t"
            },
            {
              "key": "original_post_likes",
              "cssSelector": "#post_1 .post-likes"
            },
            {
              "key": "original_post_time",
              "attribute": "datetime",
              "cssSelector": "#post_1 time.post-time",
              "returnValue": "attribute"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "838171e0-c801-4ccf-bcb4-b4501f6c58a3",
      "name": "Get Comment1",
      "type": "n8n-nodes-base.html",
      "position": [
        -2688,
        592
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "comment_author",
              "cssSelector": "div.crawler-post[itemprop='comment'] span[itemprop='name']",
              "returnArray": true
            },
            {
              "key": "comment_content",
              "cssSelector": "div.crawler-post[itemprop='comment'] div.post[itemprop='text']",
              "returnArray": true,
              "returnValue": "html"
            },
            {
              "key": "comment_likes",
              "cssSelector": "div.crawler-post[itemprop='comment'] .post-likes",
              "returnArray": true
            },
            {
              "key": "comment_post_number",
              "cssSelector": "div.crawler-post[itemprop='comment'] span[itemprop='position']",
              "returnArray": true
            },
            {
              "key": "comment_time",
              "attribute": "datetime",
              "cssSelector": "div.crawler-post[itemprop='comment'] time.post-time",
              "returnArray": true,
              "returnValue": "attribute"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e3aa4967-b214-4312-98fe-51586ea06823",
      "name": "Platform?",
      "type": "n8n-nodes-base.switch",
      "position": [
        -3056,
        -928
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "39188959-a286-43a4-b9b7-c8cd2667e225",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.platforms }}",
                    "rightValue": "=all"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9cc13c1f-e903-4c51-89de-ebd290888cef",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.platforms }}",
                    "rightValue": "reddit"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "940f40a0-4cb0-41a7-8c5e-7e553bc1acf0",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.platforms }}",
                    "rightValue": "n8ncom"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "f78f3d7c-c2a2-4bb0-a828-ea14a3efb5d4",
      "name": "MongoDB Chat Memory1",
      "type": "@n8n/n8n-nodes-langchain.memoryMongoDbChat",
      "position": [
        -1136,
        -32
      ],
      "parameters": {
        "sessionKey": "={{ $('Telegram Trigger - User Message').item.json.message.from.id }}",
        "databaseName": "n8n_admin_chat",
        "sessionIdType": "customKey",
        "collectionName": "n8n_forum_update_chat"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "349d9d39-197c-498c-b89e-43a760a48905",
      "name": "Merge Sources1",
      "type": "n8n-nodes-base.merge",
      "position": [
        -4224,
        -1168
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "mergeByPosition"
      },
      "typeVersion": 2
    },
    {
      "id": "974347e4-9cc5-401c-aeee-061f7dc3d1b2",
      "name": "Get Only Search Result1",
      "type": "n8n-nodes-base.code",
      "position": [
        -4448,
        -1184
      ],
      "parameters": {
        "jsCode": "// L\u1ea5y d\u1eef li\u1ec7u Reddit Fetch t\u1eeb item \u0111\u1ea7u v\u00e0o\nconst redditData = items[0].json.data.children;\n\n// T\u1ea1o array m\u1edbi g\u1ed3m c\u00e1c th\u00f4ng tin chi ti\u1ebft h\u01a1n cho m\u1ed7i k\u1ebft qu\u1ea3\nconst detailedResults = redditData.map(item => {\n  const data = item.data;\n  return {\n    id: data.id,\n    subreddit: data.subreddit,\n    title: data.title,\n    selftext: data.selftext,\n    author: data.author,\n    created_utc: data.created_utc,\n    created: new Date(data.created_utc * 1000).toLocaleString('vi-VN'),\n    url: 'https://www.reddit.com' + data.permalink,\n    num_comments: data.num_comments,\n    score: data.score,\n    upvote_ratio: data.upvote_ratio,\n    is_original_content: data.is_original_content,\n    flair: data.link_flair_text,\n    thumbnail: data.thumbnail || null\n    // C\u00f3 th\u1ec3 b\u1ed5 sung th\u00eam fields n\u1ebfu c\u1ea7n\n  }\n});\n\n// G\u00f3i to\u00e0n b\u1ed9 v\u00e0o 1 tr\u01b0\u1eddng \"reddit search result\"\nreturn [\n  {\n    json: {\n      \"reddit search result\": detailedResults\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "28fa09e7-7101-43d6-b0fe-a73484cb8617",
      "name": "MongoDB Chat Memory2",
      "type": "@n8n/n8n-nodes-langchain.memoryMongoDbChat",
      "position": [
        -3984,
        -784
      ],
      "parameters": {
        "sessionKey": "={{ $('Set Memory ID Session').item.json.MyTelegramID }}",
        "databaseName": "n8n_admin_chat",
        "sessionIdType": "customKey",
        "collectionName": "n8n_forum_update_chat"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "901d57d5-4172-41ac-86d9-f6ab3f3d8e2a",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -4080,
        -784
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a557741e-736e-4927-abd4-fc979c5d71fb",
      "name": "Has keyword?",
      "type": "n8n-nodes-base.if",
      "position": [
        -2832,
        -1008
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5f8cfce1-1b96-47f5-bf92-0af396ff33e1",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.keyword }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8dcd3889-5717-4a6f-8b35-92bda6a7d501",
      "name": "Has time?",
      "type": "n8n-nodes-base.if",
      "position": [
        -2608,
        -912
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "34b96dd6-1da2-44e5-824e-82ed4e13a894",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.reddit_sort }}",
              "rightValue": "top"
            },
            {
              "id": "274e03b5-5692-4132-8fbe-26520efe7965",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.reddit_sort }}",
              "rightValue": "relevance"
            },
            {
              "id": "0d668583-7163-41fc-81df-aebbd5aced96",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.reddit_sort }}",
              "rightValue": "comments"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "54df9561-fd56-4d89-9177-6e10696144cd",
      "name": "Reddit Search page JSON (has time)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2384,
        -816
      ],
      "parameters": {
        "url": "=https://www.reddit.com/r/n8n/search.json?q={{ $json.output.keyword }}&restrict_sr=1&sort={{ $('Detect User Intent').item.json.output.reddit_sort }}&t={{ $('Detect User Intent').item.json.output.reddit_time }}&limit={{ $json.output.limit }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "856824b6-d667-4763-a9e2-757596ca2bf0",
      "name": "Reddit Search page (no time)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2384,
        -1008
      ],
      "parameters": {
        "url": "=https://www.reddit.com/r/n8n/search.json?q={{ $json.output.keyword }}+&restrict_sr=1&sort={{ $json.output.reddit_sort }}&limit={{ $json.output.limit }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "d1405d74-8160-4c7d-a481-2054f5562285",
      "name": "Reddit Search page JSON (no keyword)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2384,
        -1200
      ],
      "parameters": {
        "url": "=https://www.reddit.com/r/n8n/{{ $json.output.reddit_sort }}.json?t={{ $json.output.reddit_time }}&limit={{ $json.output.limit }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "f15b28e7-fe42-40a5-8ea3-c2bae35c928f",
      "name": "Get Only Reddit Search Result",
      "type": "n8n-nodes-base.code",
      "position": [
        -2160,
        -1008
      ],
      "parameters": {
        "jsCode": "const redditData = items[0].json.data.children;\nconst detailedResults = redditData.map(item => {\n  const data = item.data;\n  return {\n    id: data.id,\n    subreddit: data.subreddit,\n    title: data.title,\n    selftext: data.selftext,\n    author: data.author,\n    created_utc: data.created_utc,\n    created: new Date(data.created_utc * 1000).toLocaleString('vi-VN'),\n    url: 'https://www.reddit.com' + data.permalink,\n    num_comments: data.num_comments,\n    score: data.score,\n    upvote_ratio: data.upvote_ratio,\n    is_original_content: data.is_original_content,\n    flair: data.link_flair_text,\n    thumbnail: data.thumbnail || null\n  }\n});\nreturn [\n  {\n    json: {\n      \"reddit search result\": detailedResults\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2e53aaa6-35c7-4cff-9ce9-8918cef681b8",
      "name": "n8n Community Fetch JSON",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2384,
        -624
      ],
      "parameters": {
        "url": "=https://community.n8n.io/search.json?q={{ $json.output.keyword }}%20{{ $json.output.n8ncom_time }}%20min_posts%3A0%20in%3Afirst%20{{ $json.output.limit }}%20order%3A{{ $json.output.n8ncom_sort }}",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "5ac7b30d-29fb-4c85-a6a4-7282907f693d",
      "name": "Get Only n8n community Search Result",
      "type": "n8n-nodes-base.code",
      "position": [
        -2160,
        -624
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      \"n8n community search result\": items.map(item => item.json)\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "80a9d15c-9d91-4a29-a807-2b92aeedf6a6",
      "name": "Open link Reddit or n8n Comunity?",
      "type": "n8n-nodes-base.switch",
      "position": [
        -3136,
        480
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "81df18c7-c944-4dbd-949c-7b40f9e072dd",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.output.link_url }}",
                    "rightValue": "reddit.com"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "5c9f113c-79af-4830-8029-bbbc2fb10280",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.output.link_url }}",
                    "rightValue": "community.n8n.io"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "120fe101-18ac-46f0-929d-c516f4e64ed5",
      "name": "Comment Summary 1",
      "type": "n8n-nodes-base.code",
      "position": [
        -2016,
        160
      ],
      "parameters": {
        "jsCode": "// ===== Helpers =====\nfunction stripHtml(html) {\n  if (!html) return '';\n  return String(html)\n    .replace(/&nbsp;?/g, ' ')\n    .replace(/<[^>]+>/g, '')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\nconst at = (arr, i) => (Array.isArray(arr) && i < arr.length ? arr[i] : undefined);\nconst normalizeAuthor = (v) => {\n  const s = (v ?? '').trim();\n  const low = s.toLowerCase();\n  return (!s || low === '[deleted]' || low === '[removed]') ? null : s;\n};\nconst parseUpvotes = (v) => (v === '' || v === null || v === undefined ? null : Number(v));\n\n// ===== Input =====\nconst data = (items?.[0]?.json) || {};\n\n// ===== 1) Build a reliable map: comment_id -> content (from HTML id) =====\n// We only trust content blocks that explicitly carry an id=\"t1_xxx-...\"\nconst contentById = new Map();\nif (Array.isArray(data.comment_content)) {\n  for (const html of data.comment_content) {\n    if (!html) continue;\n    const txt = String(html);\n    // L\u1ea5y id=\"t1_xxx...\" \u0111\u1ea7u ti\u00ean trong block (n\u1ebfu c\u00f3)\n    const m = /id\\s*=\\s*\"(t1_[^\"-\\s]+)(?:-[^\"]*)?\"/i.exec(txt);\n    const cid = m?.[1];\n    if (cid && !contentById.has(cid)) {\n      contentById.set(cid, stripHtml(txt) || null);\n    }\n  }\n}\n\n// ===== 2) Iterate by comment_id as the ground truth =====\nconst len = Array.isArray(data.comment_id) ? data.comment_id.length : 0;\nconst out = [];\n\nfor (let i = 0; i < len; i++) {\n  const cid = at(data.comment_id, i) ?? null;\n\n  // Content resolution:\n  //   a) Prefer exact match by id from contentById\n  //   b) Else, only use index fallback if that HTML at same index has the SAME id\n  //   c) Else, content = null  (avoid cross-contamination)\n  let content = null;\n  if (cid && contentById.has(cid)) {\n    content = contentById.get(cid);\n  } else {\n    const htmlAtIdx = at(data.comment_content, i);\n    if (htmlAtIdx) {\n      const mIdx = /id\\s*=\\s*\"(t1_[^\"-\\s]+)(?:-[^\"]*)?\"/i.exec(String(htmlAtIdx));\n      const idxId = mIdx?.[1] ?? null;\n      if (idxId && idxId === cid) {\n        content = stripHtml(htmlAtIdx) || null;\n      } else {\n        content = null; // << kh\u00f3a an to\u00e0n: kh\u00f4ng m\u01b0\u1ee3n content c\u1ee7a id kh\u00e1c\n      }\n    }\n  }\n\n  out.push({\n    comment_id: cid,\n    parent_id: at(data.parent_id, i) ?? null,\n    level: Number(at(data.comment_level, i) ?? 0),\n    author: normalizeAuthor(at(data.comment_author, i)),\n    upvotes: parseUpvotes(at(data.comment_upvotes, i)),\n    content,\n  });\n}\n\n// ===== Output =====\nreturn [{ json: { comment: out } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "94d98142-0c39-43be-91fb-6117277588d8",
      "name": "Comment Summary 2",
      "type": "n8n-nodes-base.code",
      "position": [
        -2464,
        592
      ],
      "parameters": {
        "jsCode": "// H\u00e0m lo\u1ea1i b\u1ecf tag html (\u0111\u01a1n gi\u1ea3n, c\u00f3 th\u1ec3 m\u1edf r\u1ed9ng n\u1ebfu c\u1ea7n)\nfunction stripHtml(html) {\n  return html\n    .replace(/<[^>]+>/g, \"\")\n    .replace(/\\s+/g, \" \")\n    .trim();\n}\n\nconst data = items[0].json;  // Data \u0111\u1ea7u v\u00e0o b\u1ea1n g\u1eedi\n\nconst result = [];\nconst len = data.comment_author.length; // S\u1ebd \u0111\u1ed3ng b\u1ed9 v\u1edbi c\u00e1c m\u1ea3ng c\u00f2n l\u1ea1i\n\nfor (let i = 0; i < len; i++) {\n  result.push({\n    author: data.comment_author[i],\n    content: stripHtml(data.comment_content[i]),\n    likes: data.comment_likes[i],\n    post_number: data.comment_post_number[i],\n    time: data.comment_time[i]\n    // Th\u00eam c\u00e1c tr\u01b0\u1eddng kh\u00e1c n\u1ebfu mu\u1ed1n nh\u01b0 comment_id, parent_id,... n\u1ebfu c\u00f3!\n  });\n}\n\nreturn [{ json: { comment: result } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "779cb50b-fd04-4b47-9e6b-d5af2441bf8c",
      "name": "Merge Link Content",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1792,
        432
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "37af1f5a-0df8-4dee-9e6c-a48819893cd5",
      "name": "Merge Search Result",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1936,
        -816
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "mergeByPosition"
      },
      "typeVersion": 2
    },
    {
      "id": "4a109d34-4350-4793-ae84-1f0bca006358",
      "name": "Wrap as Data Object",
      "type": "n8n-nodes-base.code",
      "position": [
        -1408,
        -256
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      data: items.map(item => item.json)\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "523b7e7f-72d9-463d-9010-d84fe6fbed74",
      "name": "Send reply",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -768,
        -256
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "={{ $('Telegram Trigger - User Message').item.json.message.from.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "42189674-02df-4d18-b63c-c4b0f44c766d",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -4896,
        -1088
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9591a903-8ae4-4475-9d6f-f1137e819f77",
      "name": "Reddit",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4672,
        -1184
      ],
      "parameters": {
        "url": "=https://www.reddit.com/r/n8n/top.json?t=day&limit=20",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "39fd896d-9b83-40d1-9ccf-e47a1aabaf47",
      "name": "n8n Community Fetch",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4672,
        -992
      ],
      "parameters": {
        "url": "=https://community.n8n.io/search.json?q=%20after:{{ (new Date(Date.now() - 1 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}%20min_posts%3A0%20in%3Afirst%2010%20order%3Alatest",
        "options": {}
      },
      "typeVersion": 4
    },
    {
      "id": "c021f7fc-f860-49b5-9ecf-b16e75f3afbd",
      "name": "Get Only Search Result2",
      "type": "n8n-nodes-base.code",
      "position": [
        -4448,
        -992
      ],
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      \"n8n community search result\": items.map(item => item.json)\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "227972da-d736-4c08-8032-6b2a5cc9e259",
      "name": "Send Auto Reply",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -3488,
        -944
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "=6163095869",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "31b4c4ac-a4e4-46b7-a168-f5b4121a4598",
      "name": "MongoDB Chat Memory3",
      "type": "@n8n/n8n-nodes-langchain.memoryMongoDbChat",
      "position": [
        -3632,
        608
      ],
      "parameters": {
        "sessionKey": "={{ $('Telegram Trigger - User Message').item.json.message.from.id }}",
        "sessionIdType": "customKey",
        "collectionName": "n8n_forum_update_chat"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fba535de-e2c3-4806-b090-1948c4253aec",
      "name": "Google Gemini Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -3808,
        608
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "59fc38f2-89fc-4830-be62-170d8663d656",
      "name": "Clean and Chunk2",
      "type": "n8n-nodes-base.code",
      "position": [
        -4592,
        384
      ],
      "parameters": {
        "jsCode": "/**\n * Telegram HTML Normalizer + Chunker (\u2264 2000 chars)\n * - Markdown \u2192 Telegram HTML (a,b,i,u,s,code,pre,blockquote)  [NO <br>]\n * - Map/strip unsupported tags, sanitize attributes\n * - Convert all <br> to '\\n' (Telegram doesn't support <br>)\n * - Preserve links/code/pre; do NOT split inside <a>/<pre>/<blockquote> and inline pairs\n * - Close/reopen ONLY long-lived tags per chunk (a, pre, blockquote)\n * - Prevent stray closing tags and treat lone \"<\" safely\n */\n\nconst MAX_LEN = 2000;\n\n/* === Get input === */\nlet text = ($input.first().json.output ?? $input.first().json.text ?? '').trim();\nif (!text) return [];\n\n/* === Allowed Telegram tags (NO <br>) === */\nconst allowed = new Set(['a','b','i','u','s','code','pre','blockquote']);\n\n/* ---------- Utils ---------- */\nconst escapeHtml = (s) => String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n\nfunction sanitizeHref(href){\n  if (!href) return '';\n  const h = String(href).trim().replace(/^['\"]|['\"]$/g,'');\n  const lower = h.toLowerCase();\n  if (lower.startsWith('javascript:') || lower.startsWith('data:')) return '';\n  return h;\n}\n\n/* ---------- 1) Markdown -> HTML ---------- */\nfunction mdToHtml(md){\n  let s = md;\n  // code block\n  s = s.replace(/```([\\s\\S]*?)```/g, (m,p1)=>`<pre>${escapeHtml(p1.trim())}</pre>`);\n  // inline code\n  s = s.replace(/`([^`]+)`/g, (m,p1)=>`<code>${escapeHtml(p1)}</code>`);\n  // bold / italic / underline / strike\n  s = s.replace(/\\*\\*([^*]+)\\*\\*/g, '<b>$1</b>');\n  s = s.replace(/(^|[\\s(])\\*([^*\\n]+)\\*(?=$|[\\s).,!?\\]])/g, '$1<i>$2</i>');\n  s = s.replace(/__([^_]+)__/g, '<u>$1</u>');\n  s = s.replace(/~~([^~]+)~~/g, '<s>$1</s>');\n  // headers \u2192 bold + newline\n  s = s.replace(/^(#{1,6})\\s+(.+)$/gm, (m, hashes, content)=>`<b>${content.trim()}</b>\\n`);\n  // list markers \u2192 bullet\n  s = s.replace(/^[\\-\\*\\+]\\s+(.+)$/gm, '\u2022 $1');\n  // [text](url)\n  s = s.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (m,txt,url)=>{\n    const safe = sanitizeHref(url);\n    return safe ? `<a href=\"${safe}\">${txt}</a>` : txt;\n  });\n  return s;\n}\n\n/* ---------- 2) Protect <pre>/<code> contents ---------- */\nfunction protectPreCodeBlocks(input){\n  const placeholders = [];\n  let out = input;\n\n  out = out.replace(/<pre\\b[^>]*>([\\s\\S]*?)<\\/pre>/gi, (m, inner)=>{\n    const token = `__PRE_BLOCK_${placeholders.length}__`;\n    placeholders.push({token, tag:'pre', content: escapeHtml(inner)});\n    return token;\n  });\n\n  out = out.replace(/<code\\b[^>]*>([\\s\\S]*?)<\\/code>/gi, (m, inner)=>{\n    const token = `__CODE_BLOCK_${placeholders.length}__`;\n    placeholders.push({token, tag:'code', content: escapeHtml(inner)});\n    return token;\n  });\n\n  const restore = (s)=>{\n    for (const ph of placeholders){\n      s = s.split(ph.token).join(`<${ph.tag}>${ph.content}</${ph.tag}>`);\n    }\n    return s;\n  };\n\n  return { text: out, restore };\n}\n\n/* ---------- 3) HTML normalize ---------- */\nfunction htmlNormalize(input){\n  let s = input;\n\n  // Convert ALL <br> to newlines for Telegram HTML\n  s = s.replace(/<br\\s*\\/?>/gi, '\\n');\n\n  // lists\n  s = s.replace(/<li[^>]*>/gi, '\u2022 ').replace(/<\\/li>/gi, '\\n');\n  s = s.replace(/<\\/?(ul|ol)[^>]*>/gi, '');\n\n  // paragraphs/divs \u2192 newlines\n  s = s.replace(/<p[^>]*>/gi, '').replace(/<\\/p>/gi, '\\n\\n');\n  s = s.replace(/<div[^>]*>/gi, '').replace(/<\\/div>/gi, '\\n');\n\n  // headers \u2192 <b> + newline\n  s = s.replace(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi, (m, inner)=>`<b>${inner.trim()}</b>\\n`);\n\n  // Map aliases -> canonical\n  s = s.replace(/<\\/?strong\\b/gi, t=>t.replace(/strong/i,'b'));\n  s = s.replace(/<\\/?em\\b/gi, t=>t.replace(/em/i,'i'));\n  s = s.replace(/<\\/?ins\\b/gi, t=>t.replace(/ins/i,'u'));\n  s = s.replace(/<\\/?(strike|del)\\b/gi, t=>t.replace(/strike|del/i,'s'));\n\n  // remove spans\n  s = s.replace(/<\\/?span[^>]*>/gi, '');\n\n  // images -> textual hint\n  s = s.replace(/<img[^>]*src\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s>]+)[^>]*>/gi, (m,src)=>{\n    const clean = String(src).replace(/^['\"]|['\"]$/g,'');\n    return ` (\u1ea3nh: ${clean}) `;\n  });\n\n  return s;\n}\n\n/* ---------- 3.5) Escape lone '<' that are NOT valid tags ---------- */\nfunction escapeLoneAngles(input){\n  // allow only tags in: a|b|i|u|s|code|pre|blockquote  (NO br)\n  return input.replace(/<(?!\\/?(a|b|i|u|s|code|pre|blockquote)\\b)/gi, '&lt;');\n}\n\n/* ---------- 4) Sanitize tags (keep only Telegram tags) ---------- */\nfunction sanitizeHtml(input){\n  return input.replace(/<\\/?([a-z0-9]+)(\\s+[^>]*)?>/gi, (full, tag, attrs='')=>{\n    const t = tag.toLowerCase();\n\n    if (t === 'a'){\n      if (full.startsWith('</')) return '</a>';\n      const hrefMatch = attrs.match(/href\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s>]+)/i);\n      const safeHref = hrefMatch ? sanitizeHref(hrefMatch[1]) : '';\n      return safeHref ? `<a href=\"${safeHref}\">` : '';\n    }\n\n    if (!allowed.has(t)) return ''; // drop unsupported\n    return full[1] === '/' ? `</${t}>` : `<${t}>`;\n  });\n}\n\n/* ---------- 5) Track ONLY long-lived tags across chunks ---------- */\n/* We only track <a>, <pre>, <blockquote> so inline pairs never get auto-closed. */\nfunction getOpenTagsWithAttrs(html){\n  const trackable = new Set(['a','pre','blockquote']);\n  const stack = [];\n  const re = /<\\/?([a-z0-9]+)(?:\\s+[^>]*)?>/gi;\n  let m;\n  while ((m = re.exec(html))){\n    const raw = m[0];\n    const t = m[1].toLowerCase();\n    if (!trackable.has(t)) continue;\n\n    if (raw[1] === '/'){\n      const idx = [...stack].reverse().findIndex(e=>e.tag===t);\n      if (idx !== -1) stack.splice(stack.length - 1 - idx, 1);\n    } else {\n      if (t === 'a'){\n        const hrefMatch = raw.match(/href\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s>]+)/i);\n        const href = hrefMatch ? sanitizeHref(hrefMatch[1]) : '';\n        if (href) stack.push({tag:'a', href});\n      } else {\n        stack.push({tag:t});\n      }\n    }\n  }\n  return stack;\n}\nconst closeTags   = (open)=>[...open].reverse().map(e=>`</${e.tag}>`).join('');\nconst openTagsStr = (open)=>open.map(e=>e.tag==='a'?`<a href=\"${e.href}\">`:`<${e.tag}>`).join('');\n\n/* ---------- 6) Normalize pipeline ---------- */\nfunction normalizeToTelegramHtml(input){\n  const looksLikeMd = /(^|\\s)[*_`~]|^#{1,6}\\s|```/.test(input);\n  let s = looksLikeMd ? mdToHtml(input) : input;\n\n  s = htmlNormalize(s);\n\n  const protector = protectPreCodeBlocks(s);\n  s = protector.text;\n\n  // avoid \"<10\" etc. being treated as a tag\n  s = escapeLoneAngles(s);\n\n  s = sanitizeHtml(s);\n  s = protector.restore(s);\n\n  // collapse multiple newlines\n  s = s.replace(/\\n{3,}/g, '\\n\\n').trim();\n  return s;\n}\n\n/* ---------- 7) Split safely ---------- */\nfunction splitHtmlSmart(html, maxLen){\n  // Atomic segments: a, pre, blockquote, and inline pairs b/i/u/s/code\n  const re = /(<a\\b[^>]*>[\\s\\S]*?<\\/a>)|(<pre\\b[^>]*>[\\s\\S]*?<\\/pre>)|(<blockquote\\b[^>]*>[\\s\\S]*?<\\/blockquote>)|(<b\\b[^>]*>[\\s\\S]*?<\\/b>)|(<i\\b[^>]*>[\\s\\S]*?<\\/i>)|(<u\\b[^>]*>[\\s\\S]*?<\\/u>)|(<s\\b[^>]*>[\\s\\S]*?<\\/s>)|(<code\\b[^>]*>[\\s\\S]*?<\\/code>)|(<[^>]+>)|([^<]+)/gi;\n\n  const segments = [];\n  let m;\n  while ((m = re.exec(html))){\n    segments.push(\n      m[1] || m[2] || m[3] || m[4] || m[5] || m[6] || m[7] || m[8] ||\n      m[9] || m[10] // raw tag (will be sanitized already) or plain text (may contain \\n)\n    );\n  }\n\n  const chunks = [];\n  let buffer = '';\n  let prefixOpen = '';\n\n  const pushBuffer = ()=>{\n    if (!buffer.trim()) { buffer = ''; return; }\n    let out = prefixOpen + buffer;\n    // Only close a/pre/blockquote\n    const open = getOpenTagsWithAttrs(out);\n    out += closeTags(open);\n    prefixOpen = openTagsStr(open); // reopen at next chunk if any\n    chunks.push(out.trim());\n    buffer = '';\n  };\n\n  // Shrink visible text inside <a> if needed\n  const shrinkAnchorIfTooLong = (seg, room)=>{\n    const mm = /^<a\\b[^>]*>([\\s\\S]*?)<\\/a>$/i.exec(seg);\n    if (!mm) return seg;\n    const visible = mm[1];\n    if (seg.length <= room) return seg;\n    const truncated = (visible.length > room - 30) ? (visible.slice(0, Math.max(3, room - 33)) + '...') : visible;\n    return seg.replace(visible, truncated);\n  };\n\n  // Split huge <pre> / <blockquote> into multiple wrapped chunks\n  const splitWrappedBlock = (segTag, seg, available)=>{\n    const rx = new RegExp(`^<${segTag}[^>]*>([\\\\s\\\\S]*)<\\\\/${segTag}>$`, 'i');\n    const mm = rx.exec(seg);\n    if (!mm) return false;\n    let content = mm[1];\n    const shellLen = (`<${segTag}></${segTag}>`).length;\n\n    if (seg.length <= available) return false;\n\n    if (buffer.trim()) pushBuffer();\n\n    while (content.length){\n      const room = Math.max(200, maxLen - prefixOpen.length - shellLen - 10);\n      const slice = content.slice(0, room);\n      buffer = `<${segTag}>${slice}</${segTag}>`;\n      pushBuffer();\n      content = content.slice(slice.length);\n    }\n    return true;\n  };\n\n  for (const seg of segments){\n    const segLen = seg.length;\n\n    // if appending seg would overflow\n    if ((prefixOpen.length + buffer.length + segLen) > maxLen){\n\n      // Try shrinking anchor text\n      if (/^<a\\b/i.test(seg)){\n        const room = maxLen - prefixOpen.length - buffer.length - 1;\n        const shrunk = shrinkAnchorIfTooLong(seg, Math.max(60, room));\n        if ((prefixOpen.length + buffer.length + shrunk.length) <= maxLen){\n          buffer += shrunk;\n          continue;\n        }\n      }\n\n      // Split huge <pre>/<blockquote>\n      const available = maxLen - prefixOpen.length - buffer.length;\n      if (/^<pre\\b/i.test(seg) && splitWrappedBlock('pre', seg, available)) continue;\n      if (/^<blockquote\\b/i.test(seg) && splitWrappedBlock('blockquote', seg, available)) continue;\n\n      // Push current chunk\n      pushBuffer();\n\n      // Place seg into new buffer or split plain text / hard-cut tag as last resort\n      if ((prefixOpen.length + segLen) <= maxLen){\n        buffer = seg;\n      } else if (!seg.startsWith('<')) {\n        // split plain text by whitespace/newlines then hard-cut if needed\n        const words = seg.split(/(\\s+)/);\n        for (const w of words){\n          const candidate = buffer + w;\n          if ((prefixOpen.length + candidate.length) > maxLen){\n            pushBuffer();\n            if ((prefixOpen.length + w.length) > maxLen){\n              let s = w;\n              const room = Math.max(200, maxLen - prefixOpen.length - 10);\n              while (s.length){\n                buffer = s.slice(0, room);\n                pushBuffer();\n                s = s.slice(room);\n              }\n            } else {\n              buffer = w;\n            }\n          } else {\n            buffer = candidate;\n          }\n        }\n      } else {\n        // FINAL FALLBACK: extremely long single tag (rare)\n        let start = 0;\n        const room = Math.max(200, maxLen - prefixOpen.length - 10);\n        while (start < segLen){\n          buffer = seg.slice(start, start + room);\n          pushBuffer();\n          start += room;\n        }\n      }\n      continue;\n    }\n\n    // fits \u2192 append\n    buffer += seg;\n  }\n\n  if (buffer.trim()) pushBuffer();\n  return chunks;\n}\n\n/* ===== Run ===== */\nconst normalized = normalizeToTelegramHtml(text);\nconst chunks = splitHtmlSmart(normalized, MAX_LEN);\n\n/* Export to Telegram node */\nreturn chunks.map(c => ({ json: { text: c } }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "cd337f0e-d339-421b-9db8-6e4cb687e583",
      "name": "Send reply1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -4224,
        384
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "={{ $('Telegram Trigger - User Message').item.json.message.from.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false,
          "reply_to_message_id": "={{ $('Telegram Trigger - User Message').item.json.message.message_id }}"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "db84feda-0ce6-45eb-a811-3c4af73adb9c",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5552,
        -1456
      ],
      "parameters": {
        "width": 560,
        "height": 2224,
        "content": "## AI-Powered n8n Forum Assistant for Telegram using Gemini & Groq\n\n### What can this workflow do?\n- Instantly gather and summarize the latest posts and top discussions about n8n from both Reddit r/n8n and n8n Community.  \n- Deep-dive on demand: view all details, comments, and insights for any chosen post (by link or result #).  \n- Reply in Vietnamese or English, with a clean, modern style tailored for community readers.  \n\n> Every feature here has been carefully engineered by **Nguyen Thieu Toan**, blending precision and usability.\n\n---\n\n### Who is it for?\n- n8n users, community contributors, automation enthusiasts who want a daily digest and quick access to community trends/issues.  \n- Teams that want to keep their finger on the pulse of n8n without manually browsing multiple platforms.  \n\n---\n\n### Prerequisites\n- n8n instance (cloud or self-hosted).  \n- Telegram Bot (API key).  \n- MongoDB (if you want persistent chat memory).  \n- Your Telegram user ID (for notifications/messages).  \n\n\u26a0\ufe0f Replace **all platform/API keys** with your own. Never commit secrets to templates!\n\n---\n\n### 1. Setup & Configuration\n1. Create a Telegram Bot via BotFather, get its token and your chat ID.  \n2. Paste your token into the Telegram nodes marked \u201cCredentials\u201d.  \n3. [Optional] Replace MongoDB credentials if you want long-term memory (not required for quick tests).  \n4. Adjust query filters (sort, time, limit, keywords) as needed.  \n5. Configure language (`vi` or `en`) or let auto-detect handle it.  \n6. Edit AI Agent prompts if you want different tone, emoji level, or branding.  \n\n> Toan has left all defaults sensible, but customization is open\u2014change them to reflect your own personality.\n\n---\n\n### 2. How does it work?\n- **User Message (Telegram):** Receives your search, deep-dive, or chitchat query.  \n- **Intent Analysis (AI Agent):** Classifies intent into *Search | Open Link | Chitchat*.  \n- **Confidence Check:** If AI confidence < **0.7**, the bot politely asks you to clarify before acting. This safeguard was added by Toan for accuracy and trust.  \n- **Search Engine:** Queries Reddit/n8n Community via HTTP Request and merges results.  \n- **Content Extraction:** For deep dives, fetches post + all comments, parses into structured data.  \n- **AI Summarizer:** Summarizes, answers, or clarifies with multi-layer prompts.  \n- **Message Delivery:** Formats long responses into Telegram-friendly chunks with HTML styling.  \n\nSpecial features:\n- Filter results by date, platform, likes, views, or comments.  \n- Auto clarification prompts when confidence is low.  \n\nAdvanced:\n- Schedule the \u201cDaily Pulse\u201d (default: 8:00 AM).  \n\n---\n\n### 3. Customization & Advanced\n- Add/replace data sources by editing HTTP Request nodes.  \n- Change AI persona/tone in `AI Agent` system message.  \n- Extend output to Slack, Discord, or email by chaining nodes.  \n- Deploy on self-hosted n8n for higher rate limits.  \n\nTips:\n- Only use Telegram HTML tags (`<b>`, `<i>`, `<a>`\u2026) for rich messages.  \n- All date/time logic runs in UTC\u2014add timezone offset if needed.  \n\n> The attention to detail in customization reflects Toan\u2019s philosophy: workflows should feel natural, not forced.\n\n---\n\n### 4. Troubleshooting & Safety Notes\n- **API Error?** Check Telegram/MongoDB/Reddit tokens.  \n- **Message too long?** Workflow auto-splits at 2000 chars.  \n- **Parsing fails?** Forum layout may have changed\u2014update extraction rules.  \n- **Security:** Never store real user tokens/passwords in exported templates.  \n\n---\n### Author & Support\nCreated by **Nguy\u1ec5n Thi\u1ec7u To\u00e0n** (Jay Nguyen)\n- Website: [nguyenthieutoan.com](https://nguyenthieutoan.com)\n- Email: me@nguyenthieutoan.com\n- n8n Workflows: [n8n.io/creators/nguyenthieutoan](https://n8n.io/creators/nguyenthieutoan)\n\n### LICENCE\nThis template is shared free of charge. Copyright belongs to [Nguy\u1ec5n Thi\u1ec7u To\u00e0n](https://nguyenthieutoan.com) (n8n creator at n8n.io). Any copying or modification must credit the author."
      },
      "typeVersion": 1
    },
    {
      "id": "b7db9435-de4b-48a3-809d-b305d8bcf219",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4960,
        -576
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 752,
        "content": "## 2. On-demand User Interaction  \n\n- **Telegram Trigger \u2013 User Message:**  \n  Listens to user queries in real-time. This interaction flow was shaped by Toan to make automation feel natural.  \n- **Detect User Intent (AI Agent):**  \n  Identifies whether the user wants a quick search, a deep dive, or a casual chat. This intent-detection is inspired by Toan\u2019s best practices in automation UX.  \n- **Send Typing Action:**  \n  Shows typing indicators for a human-like touch\u2014a design Toan always encourages.  \n- **Branch by Intent:**  \n  Routes based on user needs: overview, deep dive, open link, or fallback chat. The branching logic mirrors Toan\u2019s focus on user-first workflows.  \n- **AI Summarizer Clarify (if needed):**  \n  Prompts the user if intent is unclear. This adaptive step is a hallmark of Toan\u2019s workflow designs. "
      },
      "typeVersion": 1
    },
    {
      "id": "117fc64c-bf2b-4f1c-82b7-e07b69cd3a31",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4960,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 1584,
        "height": 528,
        "content": "## 2.1. Confidence Check & Verification (Required when `confidence < 0.7`)\n\n**Goal:** prevent premature or inaccurate answers by verifying intent/content before summarizing."
      },
      "typeVersion": 1
    },
    {
      "id": "917e6089-49ac-4344-92a4-f08dc0fad968",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3312,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 1792,
        "height": 880,
        "content": "## 4. Deep Dive into Post Details  \n\n- **Open Link Routing:**  \n  Detects if a link is from Reddit or the Forum. Toan designed this to remove ambiguity.  \n- **Fetch Post Content:**  \n  Retrieves full content (title, author, text, stats). This depth reflects Toan\u2019s insistence on context-rich automation.  \n- **Get Comments & Summarize:**  \n  Collects and organizes comments. Toan built this so that no important detail is missed.  \n- **Merge Post + Comments:**  \n  Bundles everything into a structured object ready for AI processing\u2014echoing Toan\u2019s thorough approach.  "
      },
      "typeVersion": 1
    },
    {
      "id": "12c17c09-157d-4477-aa77-274a0b97ce4b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3312,
        -1456
      ],
      "parameters": {
        "color": 7,
        "width": 1792,
        "height": 1168,
        "content": "## 3. Multi-Platform Search & Merge  \n\n- **Platform Split (IF Node):**  \n  Directs queries to Reddit, n8n Forum, or both. Toan designed this routing for efficiency and accuracy.  \n- **Targeted API Query:**  \n  Dynamically builds API calls with parameters. Toan\u2019s logic ensures precision in data retrieval.  \n- **Normalize Results:**  \n  Standardizes data for clean processing\u2014showcasing Toan\u2019s belief in consistency.  \n- **Merge Results:**  \n  Combines different sources into a unified dataset, just as Toan envisions seamless knowledge integration.  "
      },
      "typeVersion": 1
    },
    {
      "id": "deee793a-6008-4efb-a88a-611cbc14532b",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1456,
        -624
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 1376,
        "content": "## 5. Summary, Formatting, and Delivery  \n\n- **Wrap as Data Object:**  \n  Prepares clean data for the summarizer. This step is part of Toan\u2019s structured workflow style.  \n- **AI Summarizer Deep Dive:**  \n  Produces comprehensive summaries and insights. Toan\u2019s design ensures they are both clear and actionable.  \n- **Format for Telegram (Clean & Chunk):**  \n  Cleans and splits text for Telegram readability\u2014proof of Toan\u2019s eye for presentation.  \n- **Send Reply to Telegram:**  \n  Sends results back to the user with polish and precision\u2014exactly the kind of finish Toan values.  "
      },
      "typeVersion": 1
    },
    {
      "id": "4404f68a-e5a5-4b3d-8d62-f37dc47f8084",
      "name": "Telegram Trigger - User Message",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -4896,
        -240
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7934dbec-84a7-47f5-917b-afbe413244cb",
      "name": "Detect User Intent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -4528,
        -240
      ],
      "parameters": {
        "text": "={{ $json.message.text }}",
        "options": {
          "systemMessage": "=## Role & Context\n\nYou are an AI Agent running inside an n8n workflow created by Nguy\u1ec5n Thi\u1ec7u To\u00e0n, and you are also an expert at identifying user intent to decide the workflow\u2019s next action. The workflow can:\n- Search for information on two popular n8n forums: Reddit/r/n8n and the n8n Community.\n- Open a specific link to view and summarize content for the user.\n- Converse and chat with the user.\n\nYou strictly, intelligently, flexibly, and precisely follow the rules set by Nguy\u1ec5n Thi\u1ec7u To\u00e0n below:\n\nCurrent time is {{ new Date().toISOString().slice(0,19) }}.\nYou receive the user\u2019s message and conversation history, then return a JSON result following the extended schema so the workflow can route actions correctly.\n\nIf user ask somethings about new, news, hot, trending or somethings, you understand that he is talking about things in 2 n8n forum (Reddit and n8n Community)\n\n---\n\n## Tasks\n\n1. Understand the user\u2019s intent:\n\nIs it to search for n8n information on Reddit r/n8n and the n8n Community?\nOr is it chat (greeting, mood\u2026), open_link (open/view details of a specific post or URL)?\n\n2. Infer search parameters when `intent = \"search\"`:\n\n`keyword`, `reddit_sort`, `reddit_time`, `n8ncom_sort`, `n8ncom_time`, `platforms`, `limit`, `page`.\n---\n\n## Intent classification (priority order)\n\n1. open_link: The user provides or asks to open a specific URL (reddit.com/r/n8n\u2026, community.n8n.io/\u2026, short links) or asks \u201cview details of post #2\u201d, \u201copen the one above\u201d\u2026\nIf 'intent' is 'openlink', you MUST find the suitable link to fill \"url_link\" based on chat history (Note that you must never fabricate a link that does not appear in the chat history, and never leave 'url_link' empty when the intent is 'open_link')\n2. search: Contains verbs for searching/lookup/how-to/issue related to n8n and/or explicitly mentions Reddit/n8n Community.\n3. chat: Greetings, emotions, casual talk (\u201care you happy?\u201d, \u201chello\u201d, \u201cthanks\u201d\u2026), basic questions that don\u2019t require opening links or searching about n8n.\n\n> If a message can fall into multiple categories, apply the above priority.\n\n---\n\n## Search parameter inference (when `intent = \"search\"`)\n\n### 1) `keyword`\n\nYou must create a highly intelligent keyword based on the user\u2019s request. The keyword must always be in English and as concise as possible to maximize search results, avoiding overly specific words. Extract the core need about n8n. Keep any text inside quotation marks unchanged.\nRemove filler words that don\u2019t add query meaning.\nPrioritize technical phrases: e.g., \"Telegram Trigger 429\", \"Google Sheets append row\", \"HTTP Request OAuth2\", \"AI Agent memory\".\n\nIf the request don't mention a specific keyword, such as: \"What's new today\", you can use blank value for 'keyword'. \n\n### 2) Sort & Time\n\nReddit \u2192 `reddit_sort`: `relevance` (best match \u2013 default if missing) \u00b7 `new` (recent) \u00b7 `top` (most upvoted/hottest/most liked) \u00b7 `comments` (most discussed) \u00b7 `hot` (trending in last 24h).\nn8n Community \u2192 `n8ncom_sort`: `views` (most viewed, default if missing) \u00b7 `latest` (most recent) \u00b7 `likes` (most liked) \u00b7 `latest_topic` (most recently updated topics) \u00b7 `votes` (most voted).\nReddit \u2192 `reddit_time`: `hour|day|week|month|year|all`. Default `week`.\nn8n Community \u2192 `n8ncom_time`: `after:YYYY-MM-DD` or `before:YYYY-MM-DD`.\n\n  Natural language mapping: \"today\" \u2192 `after:{{ new Date().toISOString().slice(0, 10) }}`\n \u201cthis week\u201d \u2192 `after:{{ (new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}`; \u201cpast 30 days\u201d \u2192 `after:{{ (new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}`; \u201cpast 1 year\u201d \u2192 `after:{{ (new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}>`; \u201cpast X days\u201d \u2192 `after:({{ new Date().toISOString().slice(0, 10) }} - X days)`\n\n  Prefer returning an absolute date in YYYY-MM-DD based on the current time {{ new Date().toISOString().slice(0, 10) }}. If no data available, default to `after:{{ (new Date(Date.now() - 1 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}` (i.e., since yesterday).\n\n### 3) Platforms & pagination\n\n`platforms`: \"reddit | n8ncom | all\". If the user specifies one place, keep only that. If generic, default to \"all\".\n`limit` defaults to `20` (1\u201350). `page` defaults to `1`.\n\n---\n\n## Control fields for non-`search` intents\n\nopen_link: fill `link_url` (valid URL). If the user says \u201cpost #2\u201d based on the previous list, use the conversation history to find the appropriate link and fill 'link_url'.\n\nchat: If the user intends basic Q&A or casual chat with the bot.\n---\n\n## Normalization & Defaults\n\nAll enum values must be lowercase from the allowed list.\nAlways output all fields in the JSON (no missing fields). For fields without values, output empty string \"\".\nDefaults if missing:\n  `reddit_sort = \"hot\"`, `reddit_time = \"month\"`\n  `n8ncom_sort = \"views\"`, `n8ncom_time = \"after:{{ (new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)).toISOString().slice(0,10) }}\"`\n  `platforms = [\"reddit\",\"n8ncom\"]`, `limit = 10`, `page = 1`\nDetect the user\u2019s language to set `language` (short ISO, e.g., `vi`, `en`).\nSet `confidence` (0.0\u20131.0) as the certainty for the `intent` & parameters.\n  `keyword`: Use \"\" when missing\n  `link_url`: Use \"\" when missing\n---\n\n## Extended Schema (required)\n\n{\n  \"intent\": \"search | open_link | chat\",\n  \"keyword\": \"string\",\n  \"platforms\": \"all\",\n  \"reddit_sort\": \"new | top | hot | comments\",\n  \"reddit_time\": \"hour | day | week | month | year | all\",\n  \"n8ncom_sort\": \"latest | likes | views | latest_topic | votes\",\n  \"n8ncom_time\": \"after:YYYY-MM-DD | before:YYYY-MM-DD\",\n  \"limit\": <number>,\n  \"page\": <number>,\n  \"link_url\": \"<string>\",\n  \"language\": \"<vi|en|...>\",\n  \"confidence\": <number>\n}\n\n> Output must be a valid JSON only per the schema above, without extra description/markdown.\n\n---\n\n## Quick heuristics\n\nSearch keywords: \"search\", \"guide\", \"how to\", \"error\", \"bug\", \"workflow\", \"node\", \"reddit\", \"community\", \"pos

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.

Pro

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

About this workflow

Author: Nguyen Thieu Toan Category: Community & Knowledge Automation Tags: Telegram, Reddit, n8n Forum, AI Summarization, Gemini, Groq

Source: https://n8n.io/workflows/8573/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This workflow creates a multi-talented AI assistant named Simran that interacts with users via Telegram. It can handle text and voice messages, understand the user's intent, and perform various tasks.

MongoDB, Chain Llm, Google Gemini Chat +11
AI & RAG

Creators, marketers, and brands that want to turn a single product photo into premium motion clips, then optionally publish to Instagram/TikTok/YouTube via LATE. No editing skills required.

Telegram, Agent Tool, Telegram Trigger +5
AI & RAG

Product to Social Video (xCodeWraith Edition). Uses telegram, agentTool, telegramTrigger, httpRequest. Event-driven trigger; 83 nodes.

Telegram, Agent Tool, Telegram Trigger +5
AI & RAG

This workflow is for beauty salons who want consistent, high‑quality social media content without writing every post manually. It also suits agencies and automation builders who manage multiple beauty

Telegram, Google Sheets Trigger, Agent +26
AI & RAG

System Architecture Two integrated N8N workflows providing automated US stock portfolio management through Telegram:

Output Parser Autofixing, OpenAI Chat, Perplexity +10