{
  "id": "eS5GkK1VpvRY4s8O",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Meal Calorie Estimator for Telegram",
  "tags": [],
  "nodes": [
    {
      "id": "85f7dfe6-a982-4e14-a6e0-c7f3a4dc49de",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5776,
        3616
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 320,
        "content": "## Receive and authorize\n\nStarts from the Telegram bot trigger, extracts the sender identifier, and checks whether the user is allowed to use the calorie estimator."
      },
      "typeVersion": 1
    },
    {
      "id": "959ce961-bbe5-48f4-b64e-8acc9218e6c2",
      "name": "If Authorized User",
      "type": "n8n-nodes-base.if",
      "position": [
        6192,
        3776
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "df3a14a9-50d4-474e-bdf1-02cdc1005e36",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.message.chat.id.toString() }}",
              "rightValue": "={{ $json.id.toString() }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "6581f935-6979-4821-bb48-e2ad1a4d6b09",
      "name": "Set User ID Field",
      "type": "n8n-nodes-base.set",
      "position": [
        6016,
        3776
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "70eb1623-baf4-4731-9cbd-82019cd2d40f",
              "name": "id",
              "type": "number",
              "value": 0
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e12eaefb-9396-4a57-ad8c-ffd2f28b866c",
      "name": "Route by Message Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        6496,
        3728
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Photo",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "d705a6dc-7bc6-4393-82ad-72a8e1e84db9",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.message.photo[0] }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Text",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "47a2c3cc-bfd9-4f42-be3d-16af6550c9ad",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $('When Telegram Message Received').last().json.message.text }}",
                    "rightValue": "True"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Error",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "128cb80e-a6e6-44dc-b33b-f3e3569929a7",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "error",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Voice",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c3416ed7-33aa-48d7-b7de-ac50dff8d41b",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.message.voice }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "ec73b789-ca19-4525-a1f4-ef1ca2ecf6d0",
      "name": "When Telegram Message Received",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        5824,
        3776
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "ce7d71b3-8dc6-4eb8-acc7-8338c0bf028f",
      "name": "Calorie Estimation Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        7568,
        3744
      ],
      "parameters": {
        "text": "={{ $json['Mensagem de Texto'] }}{{ $json['Descri\u00e7\u00e3o de imagem'] }}{{ $('When Telegram Message Received').last().json.message.text }}",
        "options": {
          "systemMessage": "=You are an agent specialized in estimating the calorie content of meals.\n\n## Objective\n\nAnalyze the input provided by the user and estimate, approximately, the number of calories consumed in the described meal.\n\n## Input\n\nThe input will always be provided as text and may represent:\n\n* A written description of a meal provided by the user;\n* A textual description of an image of a meal generated by an image analysis model;\n* A transcription of an audio message in which the user describes a meal.\n\nInterpret the received content based on its context and the user's intent. Identify the foods mentioned or recognized in the description, their quantities, serving sizes, and, when available, ingredients or preparation methods that are relevant to the calorie estimate.\n\n## Estimation\n\nCalculate an approximate estimate of the meal's calorie content based on the available information.\n\nWhen the quantity or serving size is not specified, make a reasonable estimate using typical serving sizes, as long as it is possible to do so reliably. Do not present excessively precise estimates when the available data does not justify that level of precision.\n\nWhen there are relevant factors that could significantly affect the result, take them into account. For example, additional ingredients, sauces, oils, frying, or other components mentioned by the user.\n\nIf the information provided is insufficient to produce a reasonably reliable estimate, ask the user for the necessary details, such as the quantity or serving size.\n\n## Response\n\nPresent the result clearly and concisely.\n\nWhenever possible, include:\n\n* The foods identified;\n* The quantity or serving size considered in the estimate;\n* The approximate calories for each food;\n* The approximate total calorie content of the meal.\n\nMake it clear that the result is an estimate and may vary depending on the actual quantities, ingredients used, and preparation methods.\n\nDo not invent foods, ingredients, or quantities that are not present in the input or cannot be reasonably inferred. When you need to make an assumption, briefly indicate it.\n\nDo not provide medical or nutritional advice. Your sole purpose is to estimate the calorie content of the meal described by the user.\n"
        },
        "promptType": "define"
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "alwaysOutputData": true
    },
    {
      "id": "8a1ee7a6-8768-4fe1-a335-85d2f46477ce",
      "name": "Gemini Chat Model for Queries",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        7520,
        3984
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "151e03cc-f744-44bc-87b6-5445c95aa4a9",
      "name": "Track Conversation History",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        7664,
        3984
      ],
      "parameters": {
        "sessionKey": "={{ $('When Telegram Message Received').last().json.message.chat.id }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "d94a5598-dc42-4276-a1f6-8117ad99e8f1",
      "name": "Download Meal Image from Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        6784,
        3376
      ],
      "parameters": {
        "fileId": "={{ $json.message.photo[0].file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "3ab46755-682e-41bd-af1b-0ff640acbc01",
      "name": "Analyze Image for Calories",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        6992,
        3376
      ],
      "parameters": {
        "text": "Analyze the meal shown in the image and provide a detailed textual description to be used for calorie estimation.\n\nIdentify, whenever possible:\n\n* The foods and beverages visible in the image;\n* The approximate portion size or quantity of each item, based on visual cues;\n* Relevant ingredients or toppings that can be identified;\n* The apparent preparation method, such as fried, grilled, baked, or boiled;\n* Sauces, dressings, oils, or other visible additions.\n\nDo not estimate calories or provide nutritional information. Focus only on accurately describing the meal and estimating visible portion sizes.\n\nWhen the exact ingredients or quantities cannot be determined from the image, state the uncertainty clearly and provide a reasonable visual estimate rather than inventing specific details.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "options": {},
        "resource": "image",
        "inputType": "binary",
        "operation": "analyze"
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "e85f963d-826e-4695-9057-3a775bb02358",
      "name": "Aggregate User Data",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        7312,
        3744
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "renameField": true,
              "outputFieldName": "Descri\u00e7\u00e3o de imagem",
              "fieldToAggregate": "content.parts[0].text"
            },
            {
              "renameField": true,
              "outputFieldName": "Mensagem de Texto",
              "fieldToAggregate": "=('Telegram Trigger').item.json.message.text"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d68a68f4-563b-45b9-a97b-d4616871d253",
      "name": "Respond to User on Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        8000,
        3744
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('When Telegram Message Received').last().json.message.chat.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "49d82b1a-f94c-46f0-9fb6-8d240c0a3a3a",
      "name": "Notify Unsupported Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        6832,
        4000
      ],
      "parameters": {
        "text": "=Unsupported Message: Please send a meal description as a text message or photo.",
        "chatId": "={{ $('When Telegram Message Received').item.json.message.from.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "51121e77-b4a3-48e5-946d-077f9605e324",
      "name": "Calculate Nutritional Values",
      "type": "@n8n/n8n-nodes-langchain.toolCalculator",
      "position": [
        7808,
        3984
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b4994b21-e7ff-4ce4-837c-e74af5dcd4aa",
      "name": "Respond Unauthorized User",
      "type": "n8n-nodes-base.telegram",
      "position": [
        6560,
        4000
      ],
      "parameters": {
        "text": "Voc\u00ea n\u00e3o possui autoriza\u00e7\u00e3o para usar esse bot. ",
        "chatId": "={{ $('When Telegram Message Received').last().json.message.chat.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a74a09bc-6696-4b1b-b98b-8fc663bc74dc",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5216,
        3216
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## AI Meal Calorie Estimator for Telegram\n\n### How it works\n\nThis workflow acts as a Telegram-based meal calorie estimator. It receives a Telegram message, verifies the sender, routes supported text or photo inputs, and analyzes meal photos with Gemini when present. It then combines the available user input, asks an AI agent to estimate calories, and sends the result back to the user while returning fallback messages for unauthorized or unsupported requests.\n\n### Setup steps\n\n- Configure Telegram credentials for the trigger, download, and response nodes using the target bot token.\n- Configure Google Gemini credentials for both the image analysis node and the AI agent chat model.\n- Set the allowed Telegram user ID or IDs in the Edit Fields/If authorization logic.\n- Review the Switch rules so the workflow accepts the intended Telegram message types, such as photos and text captions.\n- Test the unauthorized and unsupported-message response nodes to ensure their chat IDs and message text are correct.\n\n### Customization\n\nYou can adjust the AI agent prompt to change the calorie-estimation style, add more allowed users, modify the fallback Telegram messages, or extend the Switch node to support additional input formats."
      },
      "typeVersion": 1
    },
    {
      "id": "e9f6de65-04e7-4699-bf0a-3db87fddc600",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6448,
        3568
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 592,
        "content": "## Route message outcomes\n\nRoutes authorized messages by type and handles nearby rejection paths for unauthorized users or unsupported Telegram message formats."
      },
      "typeVersion": 1
    },
    {
      "id": "33c8c960-1c68-4908-9ea1-90ba6c6568c8",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6736,
        3216
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 320,
        "content": "## Analyze meal photo\n\nDownloads an incoming Telegram photo and sends the image to Gemini for initial visual analysis of the meal contents."
      },
      "typeVersion": 1
    },
    {
      "id": "e9a9035e-515d-4977-95dc-43892d08884f",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7264,
        3616
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 528,
        "content": "## Estimate and reply\n\nCombines user input and image analysis, uses an AI agent with Gemini, memory, and calculator support to estimate meal calories, then sends the final response back through Telegram."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b3c5b1b2-6e4a-4fb8-9904-384bdf664f79",
  "connections": {
    "Set User ID Field": {
      "main": [
        [
          {
            "node": "If Authorized User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Authorized User": {
      "main": [
        [
          {
            "node": "Route by Message Type",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Unauthorized User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate User Data": {
      "main": [
        [
          {
            "node": "Calorie Estimation Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Message Type": {
      "main": [
        [
          {
            "node": "Download Meal Image from Telegram",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Aggregate User Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Unsupported Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Unsupported Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calorie Estimation Agent": {
      "main": [
        [
          {
            "node": "Respond to User on Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Image for Calories": {
      "main": [
        [
          {
            "node": "Aggregate User Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Track Conversation History": {
      "ai_memory": [
        [
          {
            "node": "Calorie Estimation Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Nutritional Values": {
      "ai_tool": [
        [
          {
            "node": "Calorie Estimation Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Chat Model for Queries": {
      "ai_languageModel": [
        [
          {
            "node": "Calorie Estimation Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When Telegram Message Received": {
      "main": [
        [
          {
            "node": "Set User ID Field",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Meal Image from Telegram": {
      "main": [
        [
          {
            "node": "Analyze Image for Calories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}