N
n8n Store
Workflow Market
Track Expenses from Receipt Photos with Telegram & Google Sheets using OCR.space

Track Expenses from Receipt Photos with Telegram & Google Sheets using OCR.space

by candrareza0 views

説明

Categories

🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.ifn8n-nodes-base.ifn8n-nodes-base.ifn8n-nodes-base.functionn8n-nodes-base.httpRequestn8n-nodes-base.telegramBotn8n-nodes-base.telegramBotn8n-nodes-base.telegramBotn8n-nodes-base.telegramBotn8n-nodes-base.telegramBot
Price無料
Views0
最終更新11/28/2025
workflow.json
{
  "nodes": [
    {
      "name": "Telegram Bot (Webhook)",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        250,
        200
      ],
      "parameters": {
        "webhookUrl": "=https://api.telegram.org/bot{{$env.YOUR_TELEGRAM_BOT_TOKEN}}/getUpdates",
        "pollInterval": 5,
        "allowedUpdates": [
          "message"
        ],
        "onlyNewUpdates": true
      },
      "typeVersion": 1
    },
    {
      "name": "Check 'Income'",
      "type": "n8n-nodes-base.if",
      "position": [
        450,
        100
      ],
      "parameters": {
        "conditions": [
          {
            "value1": "={{$json.message.text}}",
            "value2": "income",
            "operation": "contains"
          }
        ]
      },
      "typeVersion": 1
    },
    {
      "name": "Check 'Expense' (Text)",
      "type": "n8n-nodes-base.if",
      "position": [
        450,
        300
      ],
      "parameters": {
        "conditions": [
          {
            "value1": "={{$json.message.text}}",
            "value2": "expense",
            "operation": "contains"
          }
        ]
      },
      "typeVersion": 1
    },
    {
      "name": "Add Income to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        700,
        100
      ],
      "parameters": {
        "operation": "appendRow",
        "sheetName": "Income",
        "columnValues": {
          "mapping": {
            "Date": "={{$today}}",
            "Type": "Income",
            "Amount": "={{$json.message.text.split(' ')[2]}}",
            "Source": "Manual",
            "Description": "={{$json.message.text.split(' ')[1]}}"
          }
        },
        "spreadsheetId": "={{$env.YOUR_GOOGLE_SHEET_ID}}",
        "authentication": "oAuth2",
        "valueInputOption": "USER_ENTERED"
      },
      "typeVersion": 1
    },
    {
      "name": "Add Expense to Google Sheet (Text)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        700,
        300
      ],
      "parameters": {
        "operation": "appendRow",
        "sheetName": "Expenses",
        "columnValues": {
          "mapping": {
            "Date": "={{$today}}",
            "Type": "Expense",
            "Amount": "={{$json.message.text.split(' ')[2]}}",
            "Source": "Manual",
            "Category": "={{$json.message.text.split(' ')[3]}}",
            "Description": "={{$json.message.text.split(' ')[1]}}"
          }
        },
        "spreadsheetId": "={{$env.YOUR_GOOGLE_SHEET_ID}}",
        "authentication": "oAuth2",
        "valueInputOption": "USER_ENTERED"
      },
      "typeVersion": 1
    },
    {
      "name": "Send Income Confirmation",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        950,
        100
      ],
      "parameters": {
        "text": "Income \"{{$json.message.text.split(' ')[1]}}\" of ${{parseFloat($json.message.text.split(' ')[2]).toLocaleString('en-US')}} successfully recorded!",
        "chatId": "={{$json.message.chat.id}}",
        "parseMode": "HTML"
      },
      "typeVersion": 1
    },
    {
      "name": "Send Expense Confirmation (Text)",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        950,
        300
      ],
      "parameters": {
        "text": "Expense \"{{$json.message.text.split(' ')[1]}}\" of ${{parseFloat($json.message.text.split(' ')[2]).toLocaleString('en-US')}} for category \"{{$json.message.text.split(' ')[3]}}\" successfully recorded!",
        "chatId": "={{$json.message.chat.id}}",
        "parseMode": "HTML"
      },
      "typeVersion": 1
    },
    {
      "name": "Send Error Message (Text)",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        700,
        700
      ],
      "parameters": {
        "text": "Sorry, message format not recognized. Use format:\n*income [description] [amount]*\nor\n*expense [description] [amount] [category]*\nAlternatively, send a photo of your receipt for automatic tracking!",
        "chatId": "={{$json.message.chat.id}}",
        "parseMode": "HTML"
      },
      "typeVersion": 1
    },
    {
      "name": "Check 'Photo'",
      "type": "n8n-nodes-base.if",
      "position": [
        450,
        500
      ],
      "parameters": {
        "conditions": [
          {
            "value1": "={{$json.message.photo}}",
            "value2": "true",
            "operation": "isNotEmpty"
          }
        ]
      },
      "typeVersion": 1
    },
    {
      "name": "Get Telegram Photo",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        700,
        500
      ],
      "parameters": {
        "chatId": "={{$json.message.chat.id}}",
        "fileId": "={{$json.message.photo[{$json.message.photo.length - 1}].file_id}}",
        "operation": "getFile"
      },
      "typeVersion": 1
    },
    {
      "name": "OCR.space Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        950,
        500
      ],
      "parameters": {
        "url": "https://api.ocr.space/parse/image",
        "method": "POST",
        "options": {
          "query": [
            {
              "name": "apikey",
              "value": "={{$env.YOUR_OCR_SPACE_API_KEY}}"
            },
            {
              "name": "language",
              "value": "eng"
            },
            {
              "name": "isOverlayRequired",
              "value": "true"
            }
          ],
          "bodyParameters": [
            {
              "name": "file",
              "value": "={{$binary.data}}"
            }
          ],
          "bodyContentType": "formData"
        }
      },
      "typeVersion": 1
    },
    {
      "name": "Parse OCR Data",
      "type": "n8n-nodes-base.function",
      "position": [
        1200,
        500
      ],
      "parameters": {
        "function": "const ocrData = $json.ParsedResults[0].ParsedText;\n\n// Basic regex to find common patterns for amount ($) and dates (MM/DD/YYYY, YYYY-MM-DD, DD/MM/YYYY)\nconst amountRegex = /(?:[USD|SGD|MYR|\\$])?\\s*(\\d{1,3}(?:[.,]\\d{3})*(?:[.,]\\d{2})?)/i;\nconst dateRegex = /(\\d{1,2}[-/.]\\d{1,2}[-/.]\\d{2,4})/;\n\nlet description = 'OCR Expense';\nlet amount = 0;\nlet date = new Date().toISOString().slice(0, 10); // Default to today\nlet category = 'Uncategorized';\n\nconst amountMatch = ocrData.match(amountRegex);\nif (amountMatch && amountMatch[1]) {\n  amount = parseFloat(amountMatch[1].replace(/[,.]/g, m => m === ',' ? '.' : ',')).toFixed(2); // Handle both comma and dot decimals\n}\n\nconst dateMatch = ocrData.match(dateRegex);\nif (dateMatch && dateMatch[1]) {\n  // Attempt to parse date, try common formats\n  const dateStr = dateMatch[1];\n  let parsedDate = null;\n\n  // Try YYYY-MM-DD\n  if (dateStr.match(/^\\d{4}[-/.]\\d{1,2}[-/.]\\d{1,2}$/)) {\n    parsedDate = new Date(dateStr);\n  }\n  // Try MM/DD/YYYY or DD/MM/YYYY (be careful with ambiguity)\n  else if (dateStr.match(/^\\d{1,2}[-/.]\\d{1,2}[-/.]\\d{2,4}$/)) {\n    // Assuming MM/DD/YYYY for simplicity, adjust if DD/MM/YYYY is more common for you\n    const parts = dateStr.split(/[-/.]/);\n    if (parts.length === 3) {\n      parsedDate = new Date(`${parts[2]}-${parts[0]}-${parts[1]}`); // YYYY-MM-DD format for constructor\n    }\n  }\n\n  if (parsedDate && !isNaN(parsedDate.getTime())) {\n    date = parsedDate.toISOString().slice(0, 10);\n  }\n}\n\n// Simple keyword-based categorization (you'd make this more robust)\nif (ocrData.toLowerCase().includes('food') || ocrData.toLowerCase().includes('restaurant') || ocrData.toLowerCase().includes('cafe')) {\n  category = 'Food & Drinks';\n} else if (ocrData.toLowerCase().includes('transport') || ocrData.toLowerCase().includes('uber') || ocrData.toLowerCase().includes('taxi')) {\n  category = 'Transportation';\n} else if (ocrData.toLowerCase().includes('shopping') || ocrData.toLowerCase().includes('store')) {\n  category = 'Shopping';\n} else if (ocrData.toLowerCase().includes('utility') || ocrData.toLowerCase().includes('electric') || ocrData.toLowerCase().includes('water')) {\n  category = 'Utilities';\n}\n\n// Try to get a simple description from the text (e.g., first few words of the text)\nconst lines = ocrData.split('\\n').filter(line => line.trim() !== '');\nif (lines.length > 0) {\n  description = lines[0].substring(0, Math.min(lines[0].length, 50)); // Take first 50 chars of the first non-empty line\n}\n\nreturn [\n  {\n    json: {\n      description: description,\n      amount: amount,\n      date: date,\n      category: category\n    }\n  }\n];"
      },
      "typeVersion": 1
    },
    {
      "name": "Add Expense to Google Sheet (OCR)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1450,
        500
      ],
      "parameters": {
        "operation": "appendRow",
        "sheetName": "Expenses",
        "columnValues": {
          "mapping": {
            "Date": "={{$json.date}}",
            "Type": "Expense",
            "Amount": "={{$json.amount}}",
            "Source": "OCR",
            "Category": "={{$json.category}}",
            "Description": "={{$json.description}}"
          }
        },
        "spreadsheetId": "={{$env.YOUR_GOOGLE_SHEET_ID}}",
        "authentication": "oAuth2",
        "valueInputOption": "USER_ENTERED"
      },
      "typeVersion": 1
    },
    {
      "name": "Send Expense Confirmation (OCR)",
      "type": "n8n-nodes-base.telegramBot",
      "position": [
        1700,
        500
      ],
      "parameters": {
        "text": "Expense from receipt successfully recorded:\nDescription: *{{$json.description}}*\nAmount: *${{parseFloat($json.amount).toLocaleString('en-US')}}*\nCategory: *{{$json.category}}*",
        "chatId": "={{$json.message.chat.id}}",
        "parseMode": "HTML"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Check 'Photo'": {
      "output": [
        {
          "node": "Get Telegram Photo",
          "type": "main",
          "index": 0
        },
        {
          "node": "Send Error Message (Text)",
          "type": "main",
          "index": 1
        }
      ]
    },
    "Check 'Income'": {
      "output": [
        {
          "node": "Add Income to Google Sheet",
          "type": "main",
          "index": 0
        },
        {
          "node": "Send Error Message (Text)",
          "type": "main",
          "index": 1
        }
      ]
    },
    "Parse OCR Data": {
      "output": [
        {
          "node": "Add Expense to Google Sheet (OCR)",
          "type": "main",
          "index": 0
        }
      ]
    },
    "OCR.space Request": {
      "output": [
        {
          "node": "Parse OCR Data",
          "type": "main",
          "index": 0
        }
      ]
    },
    "Get Telegram Photo": {
      "output": [
        {
          "node": "OCR.space Request",
          "type": "main",
          "index": 0
        }
      ]
    },
    "Check 'Expense' (Text)": {
      "output": [
        {
          "node": "Add Expense to Google Sheet (Text)",
          "type": "main",
          "index": 0
        },
        {
          "node": "Send Error Message (Text)",
          "type": "main",
          "index": 1
        }
      ]
    },
    "Telegram Bot (Webhook)": {
      "output": [
        {
          "node": "Check 'Income'",
          "type": "main",
          "index": 0
        },
        {
          "node": "Check 'Expense' (Text)",
          "type": "main",
          "index": 0
        },
        {
          "node": "Check 'Photo'",
          "type": "main",
          "index": 0
        }
      ]
    },
    "Add Income to Google Sheet": {
      "output": [
        {
          "node": "Send Income Confirmation",
          "type": "main",
          "index": 0
        }
      ]
    },
    "Add Expense to Google Sheet (OCR)": {
      "output": [
        {
          "node": "Send Expense Confirmation (OCR)",
          "type": "main",
          "index": 0
        }
      ]
    },
    "Add Expense to Google Sheet (Text)": {
      "output": [
        {
          "node": "Send Expense Confirmation (Text)",
          "type": "main",
          "index": 0
        }
      ]
    }
  }
}

相关工作流