This workflow corresponds to n8n.io template #8455 — we link there as the canonical source.
This workflow follows the Chainllm → Google Sheets 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 →
{
"id": "3dPhcViLijZ5zBah",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "TagihanAir",
"tags": [],
"nodes": [
{
"id": "0251a048-cd44-464c-8094-89a9baff4695",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
-2688,
-240
],
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"typeVersion": 1.2
},
{
"id": "9da0ef32-0aae-47d4-ad66-ddeb8ff60e07",
"name": "Redirect Message Types",
"type": "n8n-nodes-base.switch",
"position": [
-2384,
-256
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Image Message",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "05b30af4-967b-4824-abdc-84a8292ac0e5",
"operator": {
"type": "object",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.message.photo[0] }}",
"rightValue": ""
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "Text Message"
}
},
"typeVersion": 3.2
},
{
"id": "22e0a88e-aa48-46f2-896a-99473313fe34",
"name": "Get a file",
"type": "n8n-nodes-base.telegram",
"position": [
-1936,
-240
],
"parameters": {
"fileId": "={{ $json.message.photo[3].file_id }}",
"resource": "file",
"additionalFields": {}
},
"typeVersion": 1.2
},
{
"id": "40406678-0fce-4f9a-9335-69e3600c84e8",
"name": "Image Explainer",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-1488,
-64
],
"parameters": {
"text": "=Ambil angka didalam kotak m\u00b3\nCaption : {{ $('Telegram Trigger').item.json.message.caption }}",
"messages": {
"messageValues": [
{
"type": "HumanMessagePromptTemplate",
"messageType": "imageBinary"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.4
},
{
"id": "0d24bce4-067b-40af-bb51-b6ca5c0a9ae3",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-1536,
192
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "edfc1d10-e1b8-460c-b850-7eb4d1b5f30f",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-1344,
144
],
"parameters": {
"jsonSchemaExample": "{\n\t\"m3\": \"00000\",\n \"caption\": \"Nama\"\n}"
},
"typeVersion": 1.3
},
{
"id": "8faed6ed-cabf-4ed2-a6bd-164ee72e1483",
"name": "Get row(s) in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1136,
-64
],
"parameters": {
"options": {
"dataLocationOnSheet": {
"values": {
"rangeDefinition": "detectAutomatically"
}
}
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ/edit?usp=drivesdk",
"cachedResultName": "Tagihan Air"
}
},
"typeVersion": 4.7
},
{
"id": "da0f78a6-3146-4e15-911f-61f3f6a073e2",
"name": "Append row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-256,
-64
],
"parameters": {
"columns": {
"value": {
"Nama": "={{ $json.Nama }}",
"Beban": "={{ $json.Beban }}",
"Harga/m\u00b3": "={{ $json['Harga/m\u00b3'] }}",
"Total Bayar": "={{ $json['Total Bayar'] }}",
"Jumlah Bayar": "={{ $json['Jumlah Bayar'] }}",
"Tanggal Input": "={{ $json.tanggalF }}",
"Volume Saat Ini": "={{ $json['Volume Saat ini'] }}",
"Volume Sebelumnya": "={{ $json['Volume Sebelumnya'] }}"
},
"schema": [
{
"id": "Nama",
"type": "string",
"display": true,
"required": false,
"displayName": "Nama",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Volume Sebelumnya",
"type": "string",
"display": true,
"required": false,
"displayName": "Volume Sebelumnya",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Volume Saat Ini",
"type": "string",
"display": true,
"required": false,
"displayName": "Volume Saat Ini",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Harga/m\u00b3",
"type": "string",
"display": true,
"required": false,
"displayName": "Harga/m\u00b3",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Jumlah Bayar",
"type": "string",
"display": true,
"required": false,
"displayName": "Jumlah Bayar",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Beban",
"type": "string",
"display": true,
"required": false,
"displayName": "Beban",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total Bayar",
"type": "string",
"display": true,
"required": false,
"displayName": "Total Bayar",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Tanggal Input",
"type": "string",
"display": true,
"required": false,
"displayName": "Tanggal Input",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"useAppend": true
},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1kbzli64xlywauDLgVVfc2FC4CHqnjzc3f9EU0kkvfIQ/edit?usp=drivesdk",
"cachedResultName": "Tagihan Air"
}
},
"typeVersion": 4.7
},
{
"id": "c58a7b4d-b780-4167-be6e-5f91cee2eb72",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2016,
32
],
"parameters": {
"width": 400,
"height": 624,
"content": "## 1. Create Google Sheet\nRows :\n- Nama\t\n- Volume Sebelumnya\t\n- Volume Saat Ini\t\n- Harga/m\u00b3\t\n- Jumlah Bayar\t\n- Beban\t\n- Total Bayar\t\n- Tanggal Input\n\n## 2. Create Telegram Bot\n- Open Telegram and search for @BotFather.\n- Use the command /newbot and follow the instructions (choose a name and username).\n- BotFather will give you a token.\n- Copy the token and paste it into your n8n (or automation platform) credentials.\n\n## 3. Create Your Gemini API Integration\n- Go to Google AI Studio.\n- Generate an API key for Gemini.\n- Paste the Gemini API key into your credentials."
},
"typeVersion": 1
},
{
"id": "efe8f266-d509-4372-a98d-082af2f5d2c1",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1136,
336
],
"parameters": {
"color": 4,
"width": 544,
"height": 272,
"content": "## Short Explanation of the Water Bill BOT\nEach resident sends a picture of the water meter along with a caption that includes their name to the chatbot.\nThe BOT automatically calculates the difference in volume between the previous and current payment.\nExample calculation:\nPrevious Volume: 535\nCurrent Volume: 545\nDifference: 10 m\u00b3\nIf the price per 1 m\u00b3 is Rp3,000, then 10 \u00d7 3,000 = Rp30,000\nAdditional mandatory fee (fixed charge): Rp3,000\nTotal = Rp33,000\n\n"
},
"typeVersion": 1
},
{
"id": "ad11f386-8b9c-4e06-921b-bb8cd54fb5f4",
"name": "Find Latest Row",
"type": "n8n-nodes-base.code",
"position": [
-928,
-64
],
"parameters": {
"jsCode": "const data = items.map(item => item.json);\n\nconst maxRowItem = data.reduce((max, current) => {\n return current.row_number > max.row_number ? current : max;\n}, data[0]);\n\nreturn [\n {\n json: maxRowItem\n }\n];"
},
"typeVersion": 2
},
{
"id": "ac746257-cbf5-40be-85de-7262071f643b",
"name": "Format Bill Message",
"type": "n8n-nodes-base.code",
"position": [
-64,
-64
],
"parameters": {
"jsCode": "const current = $json;\nconst previous = $('Calculate Bill').item.json;\nconst previousInfo = $('Find Latest Row').item.json;\nconst user = $('Prepare Data for Sheet').item.json.user;\nconst nama = $('Prepare Data for Sheet').item.json.Nama;\n\nfunction formatRupiah(angka) {\n return angka.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.');\n}\n\nfunction removeLeadingZeros(str) {\n return str.toString().replace(/^0+/, '');\n}\n\nconst volPrevlong = Number(previous['Volume Sebelumnya']);\nconst volPrev = Number(current['Volume Sebelumnya']);\nconst volumeSelisihlalu = Math.abs(volPrevlong - volPrev);\nconst volNowRaw = current['Volume Saat Ini'];\nconst volNow = Number(volNowRaw);\nconst volumeSelisih = Math.abs(volNow - volPrev);\n\nconst volNowDisplay = removeLeadingZeros(volNowRaw);\n\nconst totalBayarNow = formatRupiah(current['Total Bayar']);\nconst totalBayarPrev = formatRupiah(previousInfo['Total Bayar']);\n\nreturn [\n {\n json: {\n user,\n nama,\n tanggal_sebelumnya: previousInfo['Tanggal Input'],\n tanggal_sekarang: current['Tanggal Input'],\n vol_sebelumnyalagi: volPrevlong,\n vol_sebelumnya: volPrev,\n vol_saat_ini: volNowDisplay,\n volume_selisihlalu: volumeSelisihlalu,\n volume_selisih: volumeSelisih,\n total_bayar_sekarang: `Rp${totalBayarNow}`,\n total_bayar_sebelumnya: `Rp${totalBayarPrev}`\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "f68c7969-bdf0-4d70-a7e3-96894db6c2d8",
"name": "Send Bill to Telegram",
"type": "n8n-nodes-base.telegram",
"position": [
144,
-64
],
"parameters": {
"text": "=Hallo Kak {{ $('Prepare Data for Sheet').item.json.user }}, \nberikut tagihan untuk Bapak/Ibu {{ $('Prepare Data for Sheet').item.json.Nama }}\n-----------------------------\n\n<b>Tagihan Sebelumnya</b>\n\n<b>{{ $json.tanggal_sebelumnya }}</b>\nVolume : {{ $('Calculate Bill').item.json['Volume Sebelumnya'] }} \u27a4 {{ $('Calculate Bill').item.json['Volume Saat Ini'] }} = {{ $json.volume_selisihlalu }}m\u00b3\nTotal Bayar : {{ $json.total_bayar_sebelumnya }}\nStatus : Lunas\n\n-----------------------------\n\n<b>Tagihan Saat Ini</b>\n\n<b>{{ $json.tanggal_sekarang }}</b>\nVolume : {{ $json.vol_sebelumnya }} \u27a4 {{ $json.vol_saat_ini }} = {{ $json.volume_selisih }}m\u00b3\nTotal Bayar : {{ $json.total_bayar_sekarang }}\nStatus : Pending\n\n<b>Silakan lakukan pembayaran melalui QRIS <a href=\"https://miftahrahmat.com\">disini</a> ( Bendahara RT )</b>\n",
"chatId": "={{ $('Prepare Data for Sheet').item.json.chat_id }}",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false,
"reply_to_message_id": "={{ $('Prepare Data for Sheet').item.json.mess_id }}"
}
},
"typeVersion": 1.2
},
{
"id": "e29bafb4-47de-4220-bf11-bb2c882be340",
"name": "Prepare Data for Sheet",
"type": "n8n-nodes-base.set",
"position": [
-512,
-64
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d66900d4-cb6a-4bca-bca2-99c94f70d7e7",
"name": "Volume Sebelumnya",
"type": "number",
"value": "={{ $json['Volume Saat Ini'] }}"
},
{
"id": "4a2b3dda-e6ac-43b1-a8d4-3e8e6e49f443",
"name": "Volume Saat ini",
"type": "string",
"value": "={{ $json.VolumeSaatini }}"
},
{
"id": "888a80f9-effe-479b-9fcf-dea623bd5cb1",
"name": "Nama",
"type": "string",
"value": "={{ $json.caption }}"
},
{
"id": "73834b6e-1daf-494d-adc2-074178c77e0d",
"name": "tanggalF",
"type": "string",
"value": "={{ $json.tanggalF }}"
},
{
"id": "32823266-a5d3-42a2-9279-fbf989c2f763",
"name": "Tanggal bayar sebelumnya",
"type": "string",
"value": "={{ $json['Tanggal Input'] }}"
},
{
"id": "432c4403-74f5-46d4-9527-46d0b4fee725",
"name": "Volume",
"type": "number",
"value": "={{ $json.Volume }}"
},
{
"id": "40551b2e-d541-4cac-a7b9-0a7175f04467",
"name": "Jumlah Bayar",
"type": "number",
"value": "={{ $json['Jumlah Bayar'] }}"
},
{
"id": "20b5be6f-0e70-49db-95fb-db04f5bf6b69",
"name": "Beban",
"type": "number",
"value": "={{ $json.Beban }}"
},
{
"id": "a62d41b0-6f00-4ac3-b3a0-b5d558d8cee1",
"name": "Total Bayar",
"type": "number",
"value": "={{ $json['Total Bayar'] }}"
},
{
"id": "04aaabba-14fd-4232-97cc-c406bf127a53",
"name": "Harga/m\u00b3",
"type": "number",
"value": "={{ $json['Harga/m\u00b3'] }}"
},
{
"id": "f6738814-fd06-4e20-a727-6de499dac851",
"name": "user",
"type": "string",
"value": "={{ $json.user }}"
},
{
"id": "b3643859-3815-4a7b-82e0-87a7504ec190",
"name": "chat_id",
"type": "number",
"value": "={{ $json.chat_id }}"
},
{
"id": "bcd99fe0-0322-4a25-9453-017bc620a4ef",
"name": "mess_id",
"type": "number",
"value": "={{ $json.mess_id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e24e6599-1475-478b-a83e-a026e6a8aca6",
"name": "Calculate Bill",
"type": "n8n-nodes-base.code",
"position": [
-720,
-64
],
"parameters": {
"jsCode": "const item = $json;\n\nconst volumeSebelumnya = Number(item[\"Volume Saat Ini\"] || 0);\nconst volumeSekarang = String($('Image Explainer').first().json.output.m3).padStart(5, \"0\");\nconst hargaPerM3 = Number(item[\"Harga/m\u00b3\"] || 0);\nconst beban = Number(item[\"Beban\"] || 0);\nconst nama = item[\"Nama\"] || \"\";\n\nconst volume = volumeSekarang - volumeSebelumnya;\nconst jumlahBayar = volume * hargaPerM3;\nconst totalBayar = jumlahBayar + beban;\n\nconst inputDate = $now;\n\nconst dateObj = new Date(inputDate);\n\nconst hari = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];\nconst bulan = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];\n\nconst namaHari = hari[dateObj.getDay()];\nconst tanggal = dateObj.getDate();\nconst namaBulan = bulan[dateObj.getMonth()];\nconst tahun = dateObj.getFullYear();\n\nconst formatted = `${namaHari}, ${tanggal} ${namaBulan} ${tahun}`;\n\nreturn [{\n json: {\n ...item,\n VolumeSaatini: volumeSekarang,\n Volume: volume,\n \"Jumlah Bayar\": jumlahBayar,\n \"Total Bayar\": totalBayar,\n caption: nama,\n tanggalF: formatted,\n mess_id: $('Telegram Trigger').first().json.message.message_id,\n chat_id: $('Telegram Trigger').first().json.message.chat.id,\n user: $('Telegram Trigger').first().json.message.from.first_name\n }\n}];"
},
"typeVersion": 2
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "8fb90ae0-c3bb-4b63-976b-4faef238f2ba",
"connections": {
"Get a file": {
"main": [
[
{
"node": "Image Explainer",
"type": "main",
"index": 0
}
]
]
},
"Calculate Bill": {
"main": [
[
{
"node": "Prepare Data for Sheet",
"type": "main",
"index": 0
}
]
]
},
"Find Latest Row": {
"main": [
[
{
"node": "Calculate Bill",
"type": "main",
"index": 0
}
]
]
},
"Image Explainer": {
"main": [
[
{
"node": "Get row(s) in sheet",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Redirect Message Types",
"type": "main",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[
{
"node": "Format Bill Message",
"type": "main",
"index": 0
}
]
]
},
"Format Bill Message": {
"main": [
[
{
"node": "Send Bill to Telegram",
"type": "main",
"index": 0
}
]
]
},
"Get row(s) in sheet": {
"main": [
[
{
"node": "Find Latest Row",
"type": "main",
"index": 0
}
]
]
},
"Prepare Data for Sheet": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Redirect Message Types": {
"main": [
[
{
"node": "Get a file",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Image Explainer",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Image Explainer",
"type": "ai_outputParser",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates the calculation of monthly water bills. Residents can send a photo of their water meter along with their name via Telegram. The workflow uses Gemini AI to extract the meter reading, calculates the usage difference compared to the previous month, and…
Source: https://n8n.io/workflows/8455/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
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.
This workflow transforms your Telegram bot into an intelligent creative assistant. It can chat conversationally, fetch trending image prompts from PromptHero for inspiration, or perform a deep "remix"
This automation is designed to help you generate AI-powered music tracks, cover art, and fully rendered music videos — all triggered from a simple Telegram chat and managed via Google Sheets.