{
  "nodes": [
    {
      "id": "56e6453f-0f54-4765-b116-c3f918dd6e6c",
      "name": "Image / PDF",
      "type": "n8n-nodes-base.if",
      "position": [
        3300,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "166f3bd9-c0e1-4c0c-9f62-9076d1a82b05",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.file_class }}",
              "rightValue": "false"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0582f318-9bad-430d-a175-56653542df5f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1020,
        -380
      ],
      "parameters": {
        "color": 5,
        "width": 3600,
        "height": 660,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "92ed2483-049b-4167-bec0-4dea6c6c40a3",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        -360
      ],
      "parameters": {
        "color": 7,
        "width": 1020,
        "height": 320,
        "content": "## Initial Processing"
      },
      "typeVersion": 1
    },
    {
      "id": "b982ffad-af98-4e27-a16e-1af8f3329658",
      "name": "Mistral OCR",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        3760,
        -120
      ],
      "parameters": {
        "url": "https://api.mistral.ai/v1/ocr",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"model\": \"mistral-ocr-latest\",\n  \"document\": {\n    \"type\": \"{{ $('File classifier').item.json.file_class }}\",\n    \"{{ $('File classifier').item.json.file_class }}\": \"{{ $('Settings').item.json.File_Downloader_Prod_URL.trim() }}?tg_file={{ $('Generating temporary file link').item.json.result.file_id }}\"\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "mistralCloudApi"
      },
      "credentials": {
        "mistralCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "236d399e-541e-4f2a-bebd-298b39c7c83f",
      "name": "Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        1300,
        -240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "659b4c9d-488e-4faa-8c28-04aed2fcdfec",
              "name": "Bot_Whitelist_Active",
              "type": "boolean",
              "value": false
            },
            {
              "id": "6c5b5cec-a69b-45d5-8903-4db166c8b2a2",
              "name": "Allowed_Chat_IDs",
              "type": "string",
              "value": "1234, 5678"
            },
            {
              "id": "d66abb69-57bb-4069-97fc-97e31922be7d",
              "name": "File_Downloader_Prod_URL",
              "type": "string",
              "value": ""
            },
            {
              "id": "ffddd258-8aec-4e96-a1d1-eb0c5640b70f",
              "name": "Chat_ID",
              "type": "string",
              "value": "={{ $json.body?.message?.chat?.id || $json.body?.callback_query?.from?.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "92fe4ac3-c051-4e00-a6f0-9ae041c79b3f",
      "name": "Telegram Event Handler",
      "type": "n8n-nodes-base.switch",
      "position": [
        1720,
        -240
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Message",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "30ef2843-5b42-4ec2-820c-6976852be3eb",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{$('Incoming request').item.json.body.hasOwnProperty('message')}}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Callback",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "88ed9f14-1c63-4a11-8f7b-74a1c57f7068",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{$('Incoming request').item.json.body.hasOwnProperty('callback_query')}}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "49d86dbd-cc98-4d4a-b83d-9be5344ca30f",
      "name": "Status \u201cTyping\u2026\u201d",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2600,
        -260
      ],
      "parameters": {
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "operation": "sendChatAction"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2,
      "alwaysOutputData": true
    },
    {
      "id": "c8dbb458-dc53-402a-8d9a-69118a4ee7f2",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        -360
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 600,
        "content": "## Whitelist\n\nThis block will activate if the Bot_Whitelist_Active parameter in the Settings node is set to true and Allowed_Chat_IDs is specified (comma-separated, e.g., \"1234, 4567\")."
      },
      "typeVersion": 1
    },
    {
      "id": "f18ff881-be68-4c5f-ae4e-89415dfe20b9",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        -1080
      ],
      "parameters": {
        "width": 820,
        "height": 940,
        "content": "This n8n template provides a complete solution for **Optical Character Recognition (OCR)** of image and PDF files directly within Telegram\n\n---\n\nUsers can simply send **PNG, JPEG, or PDF** documents to your Telegram bot, and the workflow will process them, extract text using **Mistral OCR**, and return the content as a downloadable Markdown (`.md`) text file.\n\n## Key Features & How it Works:\n\n* **Effortless OCR via Telegram**: Users send a file to the bot, and the system automatically detects the file type (PNG, JPEG, or PDF).\n* **File Size Validation**: The workflow enforces a **25 MB file size limit**, in line with Telegram Bot API restrictions, ensuring smooth operation.\n* **Mistral-Powered Recognition**: Leveraging **Mistral OCR**, the template accurately extracts text from various document types.\n* **Markdown Output**: Recognized text is automatically converted into a clean Markdown (`.md`) text file, ready for easy editing, storage, or further processing.\n* **Secure File Delivery**: The processed Markdown file is delivered back to the user via Telegram. For this, the workflow ingeniously uses a **GET request to itself** (acting as a file downloader proxy). This generated link allows Telegram to fetch the `.md` file directly. **Please note: This download functionality requires the workflow to be in an `Active` status.**\n* **Optional Whitelist Security**: Enhance your bot's security with an **optional whitelist feature**. You can configure specific Telegram User IDs to restrict access, ensuring only authorized users can interact with your bot.\n* **Simplified Webhook Management**: The template includes dedicated utility flows for convenient management of your Telegram bot's webhooks (for both development and production environments).\n\nThis template is ideal for digitizing documents on the go, extracting text from scanned files, or converting image-based content into versatile, searchable text.\n\n## Getting Started\n\nTo get this powerful OCR bot up and running, follow these two main steps:\n\n1.  **Set Up Your Telegram Bot:** First, you'll need to configure your Telegram bot and its webhooks. Follow the instructions detailed in the **Telegram Bot Webhook Setup** section to create your bot, obtain its API token, and set up the necessary webhook URLs.\n\n2.  **Configure Bot Settings:** Next, you'll need to define key operational parameters for your bot. Proceed to the **Settings Configuration** section and populate the variables according to your preferences, including options for whitelist access."
      },
      "typeVersion": 1
    },
    {
      "id": "9a90d668-85d8-40e3-be1e-d51599d00f29",
      "name": "Notification about correct commands",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2880,
        80
      ],
      "parameters": {
        "text": "=\ud83e\udd16 *Sorry, I don't respond to messages*\n\nI can recognize data in PDF and image files.\nJust send me the information *as a file up to 20 megabytes*.",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e2737704-743f-4ac0-a1ac-d4ce9bb73e05",
      "name": "Maximum file size exceeded",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3100,
        80
      ],
      "parameters": {
        "text": "=\ud83e\udd16 *Oops! File size is too large*\n\nI only accept files *up to 20 megabytes*. This is a Telegram-imposed limit. Please split your file into parts, or try optimizing the existing one.",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4e9189f2-2db9-4fd0-ab57-53d6edcf50ec",
      "name": "Checking file size",
      "type": "n8n-nodes-base.if",
      "position": [
        2880,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "166f3bd9-c0e1-4c0c-9f62-9076d1a82b05",
              "operator": {
                "type": "number",
                "operation": "lte"
              },
              "leftValue": "={{ $('Incoming request').item.json.body.message.document.file_size }}",
              "rightValue": 20971520
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a23c952a-33bc-4a6f-9b2b-cdfe7d1c11cb",
      "name": "File classifier",
      "type": "n8n-nodes-base.code",
      "position": [
        3100,
        -80
      ],
      "parameters": {
        "jsCode": "const mimeType = $('Incoming request').item.json.body.message.document.mime_type;\n\nlet file_class = 'false';\n\nif (mimeType === 'image/jpeg' || mimeType === 'image/png') {\n  file_class = 'image_url';\n} else if (mimeType === 'application/pdf') {\n  file_class = 'document_url';\n}\n\nreturn [{\n  json: {\n    file_class\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "61d8d030-78be-4724-8077-cd3f9fa5a2b9",
      "name": "Generating temporary file link",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3540,
        -120
      ],
      "parameters": {
        "fileId": "={{ $('Incoming request').item.json.body.message.document.file_id }}",
        "download": false,
        "resource": "file"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "458a7b66-bdcc-42fa-976b-3306344176e4",
      "name": "Invalid file",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3540,
        60
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f *I can only work with PDF, JPG, PNG files*\n\nPlease upload a valid file",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "50079f09-6dc3-48d0-a943-10016ea43e68",
      "name": "Markdown converter",
      "type": "n8n-nodes-base.code",
      "position": [
        3960,
        -180
      ],
      "parameters": {
        "jsCode": "// Get data from the previous block\nconst inputData = items[0].json;\n\n// Validate input data\nif (!inputData.pages || !Array.isArray(inputData.pages)) {\n  throw new Error('Invalid input: pages is missing or not an array');\n}\n\n// Format page-by-page markdown text\nconst markdownPages = inputData.pages.map((page, index) => {\n  if (typeof page.markdown !== 'string') {\n    throw new Error(`Invalid input: markdown is missing or not a string on page ${index + 1}`);\n  }\n  return `# Page ${index + 1}\\n\\n${page.markdown}`;\n});\n\n// Combine pages with a convenient separator\nconst finalText = markdownPages.join('\\n\\n---\\n\\n');\n\n// Return data for the next block\nreturn [{\n  json: {\n    fileName: 'output_markdown.txt',\n    fileContent: finalText\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0a6069d9-963e-4230-86bc-7d10f7ef4abc",
      "name": "Converting Markdown to File",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        4180,
        -180
      ],
      "parameters": {
        "options": {
          "encoding": "utf8"
        },
        "operation": "toText",
        "sourceProperty": "fileContent"
      },
      "typeVersion": 1.1
    },
    {
      "id": "c1176323-7355-466c-bc58-cb961a02d7a4",
      "name": "Manual Webhook Setup Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        1100,
        -620
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "0598e6f7-bf85-48af-a2cd-445f89687a84",
      "name": "Telegram Webhook Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        1300,
        -620
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cac2c789-04c1-4c53-8532-b59ddc673fc0",
              "name": "Production_Mode",
              "type": "boolean",
              "value": true
            },
            {
              "id": "27d019f9-639f-42e0-82a6-39cb728708f4",
              "name": "Bot_API_Key",
              "type": "string",
              "value": ""
            },
            {
              "id": "52e21aa0-8bfa-41fb-bfee-da534427e1dc",
              "name": "Webhook_Prod_URL",
              "type": "string",
              "value": ""
            },
            {
              "id": "2ba85a6c-6471-46eb-a470-267715729de2",
              "name": "Webhook_Dev_URL",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4,
      "alwaysOutputData": true
    },
    {
      "id": "af30b965-608f-4dfd-92bf-15eb12ff2cf8",
      "name": "Check Production Mode",
      "type": "n8n-nodes-base.if",
      "position": [
        1500,
        -620
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "61f8c143-7e13-4b33-85f8-a7d3668f43bb",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Telegram Webhook Configuration').item.json.Production_Mode }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1cc1e4a6-3d8c-4bec-8702-f17eb259e2fb",
      "name": "Set Production Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1720,
        -700
      ],
      "parameters": {
        "url": "=https://api.telegram.org/bot{{ $('Telegram Webhook Configuration').item.json.Bot_API_Key.trim() }}/setWebhook",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $('Telegram Webhook Configuration').item.json.Webhook_Prod_URL.trim() }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "db5d7250-bdc6-473d-9cfd-f53d2181dffb",
      "name": "Set Development Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1720,
        -560
      ],
      "parameters": {
        "url": "=https://api.telegram.org/bot{{ $('Telegram Webhook Configuration').item.json.Bot_API_Key.trim() }}/setWebhook",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $('Telegram Webhook Configuration').item.json.Webhook_Dev_URL.trim() }}"
            },
            {
              "name": "drop_pending_updates",
              "value": "1"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d60f4fd4-65c6-49bf-b8ed-58605c971d4c",
      "name": "Return Webhook Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1920,
        -620
      ],
      "parameters": {
        "url": "=https://api.telegram.org/bot{{ $('Telegram Webhook Configuration').item.json.Bot_API_Key }}/getWebhookInfo",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "4d7f4700-cff1-431d-9135-0f74c55d67e6",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1020,
        -1080
      ],
      "parameters": {
        "color": 2,
        "width": 1060,
        "height": 680,
        "content": "## Telegram Bot Webhook Setup \n\n1.  **Create your bot** by chatting with **BotFather** on Telegram (`@BotFather`). Send `/newbot`, follow the prompts to name your bot, and then copy the **Bot API Token** it provides.\n\n2.  In this node's settings (`Telegram Webhook Configuration`), paste your **Bot API Token** into the `Bot_API_Key` field.\n\n3.  Now, get the webhook URLs from the **\"Incoming Request\"** node in this workflow (it's usually the first node):\n\n    * Copy the **\"Test URL\"** from its settings and paste it into the `Webhook_Dev_URL` field in *this* node (`Telegram Webhook Configuration`).\n    * Copy the **\"Production URL\"** from the \"Incoming Request\" node and paste it into the `Webhook_Prod_URL` field in *this* node (`Telegram Webhook Configuration`).\n\n4.  Finally, use the `Production_Mode` toggle in this node to switch between development (`false`) and production (`true`) environments for your bot's webhook."
      },
      "typeVersion": 1
    },
    {
      "id": "4425724a-a266-4cb8-b2a6-13bb2300ed02",
      "name": "Check Whitelist Status",
      "type": "n8n-nodes-base.if",
      "position": [
        2340,
        -160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0e9d0585-a189-4552-bfa1-843809f74144",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Whitelist Logic').item.json.isWhitelisted }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "32bd81fe-b37c-4d14-9b33-094397a6746b",
      "name": "Whitelist Logic",
      "type": "n8n-nodes-base.code",
      "position": [
        2140,
        -160
      ],
      "parameters": {
        "jsCode": "// Get Chat_ID from the incoming Telegram message\nconst chatId = String($('Settings').item.json.Chat_ID);\n\n// Get allowed Chat IDs string from 'Settings' node\nconst allowedChatIdsString = $('Settings').item.json.Allowed_Chat_IDs;\n\n// Initialize allowedChatIds as an empty array\nlet allowedChatIds = [];\n\n// Process allowedChatIdsString only if it's a non-empty string\nif (typeof allowedChatIdsString === 'string' && allowedChatIdsString.trim() !== '') {\n  // Split string by comma, map to string, and trim whitespace from each ID\n  allowedChatIds = allowedChatIdsString.split(',').map(id => String(id).trim());\n\n  // Filter out any empty strings that might result from multiple commas (e.g., \"1, ,2\")\n  allowedChatIds = allowedChatIds.filter(id => id !== '');\n}\n\n// If allowedChatIds is empty after processing, it means no specific IDs are whitelisted.\n// The regex will correctly handle an empty array (it will become '^(?:)$' matching nothing).\n\n// Escape special regex characters in IDs to prevent errors and ensure literal matching\nconst escapedChatIds = allowedChatIds.map(id => id.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n\n// Construct regex for full string match.\n// If escapedChatIds is empty, the regex will be '^(?:)$', which correctly matches nothing.\nconst regex = new RegExp(`^(?:${escapedChatIds.join('|')})$`);\n\n// Check if chatId is in the whitelist\nconst isWhitelisted = regex.test(chatId);\n\n// Return the result\nreturn [{ json: { isWhitelisted: isWhitelisted } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "62f7cf5c-ea3f-47a5-b555-d3a5ae59d268",
      "name": "Access Denied",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2340,
        60
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f *Oops! You don't have access to this bot.*",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e3fdd1f0-f5fe-406e-9441-52bc433994dd",
      "name": "Determine Message Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        2600,
        40
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Bot Command",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "44f27b19-4dd1-4e1d-ac4e-dc666a5dfcaf",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Incoming request').item.json.body.message.entities[0].type }}",
                    "rightValue": "bot_command"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "File",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7d13a9c3-dc53-4bd6-9657-325ff887be8a",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Incoming request').item.json.body.message.document }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Callback",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e3817a32-199e-4959-a4f0-03ee3b37613a",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Incoming request').item.json.body.callback_query.data }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "d3c505c4-5943-4463-aafe-808403e83240",
      "name": "Inform Bot Capabilities",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3100,
        -240
      ],
      "parameters": {
        "text": "=\ud83d\udc4b *Hi there!*\n\nI can recognize data in PDF and image files.\nJust send me the information as a file *up to 20 megabytes*.",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1cf53d92-e6b7-4cc2-a3f6-9c026e7a37e6",
      "name": "Command Router",
      "type": "n8n-nodes-base.switch",
      "position": [
        2880,
        -260
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Start",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "f64462ba-530b-4a67-9d67-2ddfb09b542b",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $('Incoming request').item.json.body.message.text }}",
                    "rightValue": "/start"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "none"
        }
      },
      "executeOnce": false,
      "notesInFlow": false,
      "typeVersion": 3.1,
      "alwaysOutputData": false
    },
    {
      "id": "03c53b82-8701-4efe-9de8-293a98419945",
      "name": "Send Markdown File to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        4400,
        -180
      ],
      "parameters": {
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "operation": "sendDocument",
        "binaryData": true,
        "additionalFields": {
          "fileName": "={{ $('Incoming request').item.json.body.message.document.file_name }}-ocr.md"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cf06fe33-b466-43ff-bb20-2e8e580ca4ff",
      "name": "Respond with attachment",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1920,
        80
      ],
      "parameters": {
        "options": {},
        "respondWith": "binary"
      },
      "typeVersion": 1
    },
    {
      "id": "5844d8a9-c5e7-41cd-82b8-c9af0dbdb8ee",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -20
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 260,
        "content": "## Telegram File Downloader\n### IMPORTANT: This block operates only when the workflow is Active."
      },
      "typeVersion": 1
    },
    {
      "id": "8403d395-4a8d-49d5-9ad9-311f8ecc4092",
      "name": "Determine Incoming Request Source Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        1500,
        -240
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "POST",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "f8bdaef7-663e-426a-9ec7-a736ad40d941",
                    "operator": {
                      "type": "number",
                      "operation": "notEmpty",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Incoming request').item.json.body.message.message_id || \"\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "File Proxy",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "327d0192-6488-4136-a1c8-5a2643a32d19",
                    "operator": {
                      "type": "string",
                      "operation": "notEmpty",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('Incoming request').item.json.query.tg_file || \"\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "dbbcae45-8047-4aca-a88c-04aba607ada1",
      "name": "Is Whitelist Disabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        1920,
        -240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3144f141-4f9f-4e8a-b5d6-839da21ca114",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $('Settings').item.json.Bot_Whitelist_Active }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a15729d3-3c3d-4165-903e-26864321696b",
      "name": "Get a file",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1720,
        80
      ],
      "parameters": {
        "fileId": "={{ $('Incoming request').item.json.query.tg_file }}",
        "resource": "file"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1b2f96b6-bbf3-4b74-a32c-40398fe003e5",
      "name": "Incoming request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1100,
        -240
      ],
      "parameters": {
        "path": "0be4fd3e-05dd-4d57-8563-0275f74d727e",
        "options": {
          "binaryPropertyName": "data"
        },
        "httpMethod": [
          "POST",
          "GET"
        ],
        "responseMode": "responseNode",
        "multipleMethods": true
      },
      "typeVersion": 2
    },
    {
      "id": "c92aa484-e8ed-41c4-b8c9-74bc7f237df7",
      "name": "Problem with file recognition",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3960,
        -40
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f *Oops! There was a problem with file recognition (OCR)*\n\nPlease try sending the file again later or check your bot's configuration.",
        "chatId": "={{ $('Settings').item.json.Chat_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9e2e3f9f-783e-4262-be74-698e67d83e15",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3480,
        -360
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 600,
        "content": "## Provide File Download Access for Mistral from Telegram\n\nIn this block, we use Telegram to create a download link for the file the user uploaded. We then send this link to Mistral, pointing it back to this very workflow. Mistral then performs a GET request to the current workflow, and using our bot's API key, it downloads the specified file."
      },
      "typeVersion": 1
    },
    {
      "id": "318f2ae1-5b71-4517-9138-ae17fde0b147",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        -120
      ],
      "parameters": {
        "width": 820,
        "height": 400,
        "content": "## Settings Configuration\n\nThe `Settings` node is where you'll configure crucial parameters for your bot's behavior.\n\n1.  **`Bot_Whitelist_Active`**: This boolean (`true`/`false`) setting controls whether the bot's whitelist access is enabled. If set to `true`, only users whose IDs are listed in `Allowed_Chat_IDs` will be able to interact with the bot.\n\n2.  **`Allowed_Chat_IDs`**: This field works with `Bot_Whitelist_Active`. If the whitelist is active, you **must** provide a comma-separated list of Telegram User IDs (e.g., `\"12345, 67890, 112233\"`) for users permitted to use the bot.\n\n3.  **`File_Downloader_Prod_URL`**: This variable should contain the **Production URL** from your `Incoming Request` node (typically the first node).\n\n    * **Heads up!** If your n8n instance runs on a non-standard port (e.g., something other than 80 or 443), you might need to manually append the port number to this URL. For example, if the URL is `https://yourdomain.com` and your n8n is on port `5678`, you might need to change it to `https://yourdomain.com:5678`."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Settings": {
      "main": [
        [
          {
            "node": "Determine Incoming Request Source Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a file": {
      "main": [
        [
          {
            "node": "Respond with attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Image / PDF": {
      "main": [
        [
          {
            "node": "Generating temporary file link",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Invalid file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mistral OCR": {
      "main": [
        [
          {
            "node": "Markdown converter",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Problem with file recognition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Command Router": {
      "main": [
        [
          {
            "node": "Inform Bot Capabilities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "File classifier": {
      "main": [
        [
          {
            "node": "Image / PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Whitelist Logic": {
      "main": [
        [
          {
            "node": "Check Whitelist Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Incoming request": {
      "main": [
        [
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Checking file size": {
      "main": [
        [
          {
            "node": "File classifier",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Maximum file size exceeded",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Markdown converter": {
      "main": [
        [
          {
            "node": "Converting Markdown to File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Production Mode": {
      "main": [
        [
          {
            "node": "Set Production Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Development Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Whitelist Status": {
      "main": [
        [
          {
            "node": "Status \u201cTyping\u2026\u201d",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Access Denied",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Message Type": {
      "main": [
        [
          {
            "node": "Command Router",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Checking file size",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notification about correct commands",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notification about correct commands",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Whitelist Disabled?": {
      "main": [
        [
          {
            "node": "Status \u201cTyping\u2026\u201d",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Whitelist Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Production Webhook": {
      "main": [
        [
          {
            "node": "Return Webhook Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Status \u201cTyping\u2026\u201d": {
      "main": [
        [
          {
            "node": "Determine Message Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Event Handler": {
      "main": [
        [
          {
            "node": "Is Whitelist Disabled?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Whitelist Disabled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Development Webhook": {
      "main": [
        [
          {
            "node": "Return Webhook Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Converting Markdown to File": {
      "main": [
        [
          {
            "node": "Send Markdown File to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Webhook Setup Trigger": {
      "main": [
        [
          {
            "node": "Telegram Webhook Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generating temporary file link": {
      "main": [
        [
          {
            "node": "Mistral OCR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Webhook Configuration": {
      "main": [
        [
          {
            "node": "Check Production Mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Incoming Request Source Type": {
      "main": [
        [
          {
            "node": "Telegram Event Handler",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}