8
n8n 中文网amn8n.com

水费账单

中级

这是一个Miscellaneous, Multimodal AI领域的自动化工作流,包含 15 个节点。主要使用 Set, Code, Switch, Telegram, GoogleSheets 等节点。 使用 Telegram、Gemini AI 和 Google Sheets 自动化水费计算

前置要求
  • Telegram Bot Token
  • Google Sheets API 凭证
  • Google Gemini API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "3dPhcViLijZ5zBah",
  "meta": {
    "instanceId": "f357e038067887a62c03e581da0f98dfa8010dcc02f1fa97f81df592773a1c40",
    "templateCredsSetupCompleted": true
  },
  "name": "水费账单",
  "tags": [],
  "nodes": [
    {
      "id": "0251a048-cd44-464c-8094-89a9baff4695",
      "name": "Telegram 触发器",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -2688,
        -240
      ],
      "webhookId": "665df113-870a-4b73-832c-075ca8e3caa2",
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "9da0ef32-0aae-47d4-ad66-ddeb8ff60e07",
      "name": "重定向消息类型",
      "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": "获取文件",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1936,
        -240
      ],
      "webhookId": "4b59179c-2b2a-47fa-9a06-cfe9a8be8db7",
      "parameters": {
        "fileId": "={{ $json.message.photo[3].file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "40406678-0fce-4f9a-9335-69e3600c84e8",
      "name": "图片解释器",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -1488,
        -64
      ],
      "parameters": {
        "text": "=Ambil angka didalam kotak m³\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 聊天模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -1536,
        192
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "edfc1d10-e1b8-460c-b850-7eb4d1b5f30f",
      "name": "多表:您可以连接多个表以实现有组织的数据结构",
      "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": "获取表中的行",
      "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": "在表格中追加行",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -256,
        -64
      ],
      "parameters": {
        "columns": {
          "value": {
            "Nama": "={{ $json.Nama }}",
            "Beban": "={{ $json.Beban }}",
            "Harga/m³": "={{ $json['Harga/m³'] }}",
            "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³",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Harga/m³",
              "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": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        32
      ],
      "parameters": {
        "width": 400,
        "height": 624,
        "content": "## 1. 创建 Google 表格"
      },
      "typeVersion": 1
    },
    {
      "id": "efe8f266-d509-4372-a98d-082af2f5d2c1",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1136,
        336
      ],
      "parameters": {
        "color": 4,
        "width": 544,
        "height": 272,
        "content": "## 水费账单机器人简要说明"
      },
      "typeVersion": 1
    },
    {
      "id": "ad11f386-8b9c-4e06-921b-bb8cd54fb5f4",
      "name": "查找最新行",
      "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": "格式化账单消息",
      "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": "发送账单到 Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        144,
        -64
      ],
      "webhookId": "d1f3b592-bfaa-4095-b613-46e52ecc754a",
      "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'] }} ➤ {{ $('Calculate Bill').item.json['Volume Saat Ini'] }} = {{ $json.volume_selisihlalu }}m³\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 }} ➤ {{ $json.vol_saat_ini }} = {{ $json.volume_selisih }}m³\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": "准备表格数据",
      "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³",
              "type": "number",
              "value": "={{ $json['Harga/m³'] }}"
            },
            {
              "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": "计算账单",
      "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³\"] || 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,
  "pinData": {},
  "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
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级 - 杂项, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量15
分类2
节点类型10
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

作者
Miftah Rahmat

Miftah Rahmat

@miftahr

I’m an automation enthusiast specializing in n8n workflow design and integration.

外部链接
在 n8n.io 查看

分享此工作流