Skip to content

Instantly share code, notes, and snippets.

@Ishan-sa
Created December 7, 2025 16:26
Show Gist options
  • Select an option

  • Save Ishan-sa/c6c1c65827667fb69df5bf2892f09511 to your computer and use it in GitHub Desktop.

Select an option

Save Ishan-sa/c6c1c65827667fb69df5bf2892f09511 to your computer and use it in GitHub Desktop.
{
"name": "Smart Receipt Organizer",
"nodes": [
{
"parameters": {
"updates": [
"message",
"channel_post"
],
"additionalFields": {}
},
"id": "b092b3b3-989e-4ac3-abc2-f785293eae72",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
-576,
-416
],
"webhookId": "auto-generated",
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
}
},
{
"parameters": {
"name": "={{ $json.newFileName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $json.driveFolderId }}",
"mode": "id"
},
"options": {}
},
"id": "867168cc-7ee7-4a42-af40-2e5bd1158f05",
"name": "Backup to Drive",
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
544,
-416
],
"credentials": {
"googleDriveOAuth2Api": {
"id": "EkoZTEdJjBuNNGjR",
"name": "Google Drive account"
}
}
},
{
"parameters": {
"jsCode": "// --------------------------------------------------------\n// CONFIGURATION: PASTE YOUR GOOGLE DRIVE FOLDER IDS HERE\n// --------------------------------------------------------\nconst FOLDER_IDS = {\n \"Eating Out\": \"1g9DjOYM9DZIYWziVI5L5MzmLxMgGHB55\",\n \"Groceries\": \"16DmSj7kAA2H4PDLy_sOG3C_7a5ufNcUw\",\n \"Utilities\": \"1cR6bubHvq_QcP1SGTfmYZ-83k07vPHA4\",\n \"Transport\": \"1zr7kNNV030t3a912Q3ZL44cqFWId8sDr\",\n \"Shopping\": \"1ekTo1jjZJRccu-SRNouKq2Yl2pEdnvtP\",\n \"Subscriptions\": \"1xd6SzHSjm197h9SgQTgVhFy02UV-oC4l\",\n \"Tech & Office\": \"1_ucFcwTlQWeT0zbRH-CJQBnBnhTbt3ox\",\n \"Medical\": \"1z-dHiIqLLfI1c_HWsKo7XPloQGBinwj6\",\n \"Miscellaneous\": \"1edg_WVd9edu1UeynSSyn865vV72VgmtW\" \n};\n\n// --------------------------------------------------------\n// 1. EXTRACT AI RESPONSE\n// --------------------------------------------------------\nconst item = $input.first();\nconst json = item.json;\n\n// Handle Google Gemini's deeply nested structure\nlet aiOutput = json.candidates?.[0]?.content?.parts?.[0]?.text || json.text || json.response;\n\nif (!aiOutput) {\n throw new Error(\"Could not find text in AI response. Check input structure.\");\n}\n\n// --------------------------------------------------------\n// 2. CLEAN & PARSE JSON\n// --------------------------------------------------------\nif (aiOutput.includes('```')) {\n aiOutput = aiOutput.split('```')[1];\n aiOutput = aiOutput.replace(/^json\\s*/i, '');\n}\n\nlet receiptData = {};\ntry {\n receiptData = JSON.parse(aiOutput.trim());\n} catch (e) {\n receiptData = { \n summary: aiOutput, \n vendor: 'Parse Error', \n amount: 0, \n currency: 'CAD',\n category: 'Miscellaneous' \n }; \n}\n\n// --------------------------------------------------------\n// 3. LOGIC: FOLDERS, RENAMING & CURRENCY\n// --------------------------------------------------------\n\nconst category = receiptData.category || 'Miscellaneous';\nconst targetFolderId = FOLDER_IDS[category] || FOLDER_IDS['Miscellaneous'];\n\n// --- FILE EXTENSION FIX (PDF/JPG) ---\nconst binaryData = $('Get File').first().binary;\nconst binaryKey = Object.keys(binaryData)[0]; \nconst fileExtension = binaryData[binaryKey].fileExtension || 'jpg';\n\nconst date = receiptData.date || new Date().toISOString().split('T')[0];\nconst safeVendor = (receiptData.vendor || 'Unknown').replace(/[^a-zA-Z0-9]/g, ''); \nconst amount = receiptData.amount || 0;\n\n// Use the detected currency, or default to CAD if AI missed it\nconst currency = (receiptData.currency || 'CAD').toUpperCase();\n\n// Filename includes currency now for clarity: \"2025-11-28_GitHub_20_USD.pdf\"\nconst newFileName = `${date}_${safeVendor}_${amount}_${currency}.${fileExtension}`;\n\n// --------------------------------------------------------\n// 4. RETURN DATA\n// --------------------------------------------------------\nreturn {\n json: {\n vendor: receiptData.vendor || 'Unknown',\n date: date,\n amount: parseFloat(amount),\n // Pass the dynamic currency to Sheets\n currency: currency, \n category: category,\n summary: receiptData.summary || '',\n driveFolderId: targetFolderId,\n newFileName: newFileName\n },\n binary: binaryData\n};"
},
"id": "715fa366-277c-4081-b1df-e426a45d195c",
"name": "Format Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
304,
-416
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1zdHPccdCdOzoV6TRQwddHdtZ4P-I91fnANsvQs4cvDU",
"mode": "list",
"cachedResultName": "receipt_hub",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1zdHPccdCdOzoV6TRQwddHdtZ4P-I91fnANsvQs4cvDU/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "Sheet1",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $('Format Data').first().json.date }}",
"Vendor": "={{ $('Format Data').first().json.vendor }}",
"Amount": "={{ $('Format Data').first().json.amount }}",
"Currency": "={{ $('Format Data').first().json.currency }}",
"Category": "={{ $('Format Data').first().json.category }}",
"Link": "={{ $json.webViewLink }}"
},
"matchingColumns": [],
"schema": [
{
"id": "Date",
"displayName": "Date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Vendor",
"displayName": "Vendor",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Amount",
"displayName": "Amount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Currency",
"displayName": "Currency",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Category",
"displayName": "Category",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Link",
"displayName": "Link",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"id": "ffc80004-d07a-4d8e-8893-f484ea57a232",
"name": "Save to Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
736,
-416
],
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Epz7c0im8OMtFCM4",
"name": "Google Sheets account 2"
}
}
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').first().json.message.chat.id }}",
"text": "=✅ <b>Receipt saved!</b>\n\n💰 Amount: ${{ $('Format Data').first().json.amount }} {{ $('Format Data').first().json.currency }}\n🏪 Vendor: {{ $('Format Data').first().json.vendor }}\n📁 Category: {{ $('Format Data').first().json.category }}\n📅 Date: {{ $('Format Data').first().json.date }}\n\n🔗 <a href=\"{{ $('Backup to Drive').first().json.webViewLink }}\">View in Drive</a>",
"additionalFields": {
"parse_mode": "HTML"
}
},
"id": "416a7789-a767-41ba-a055-3cbec823c3f1",
"name": "Send Confirmation",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
960,
-416
],
"webhookId": "a7e3681e-27de-44be-be8d-7c9fce3f8031",
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
}
},
{
"parameters": {
"resource": "image",
"operation": "analyze",
"modelId": {
"__rl": true,
"value": "models/gemini-2.5-flash",
"mode": "list",
"cachedResultName": "models/gemini-2.5-flash"
},
"text": "Analyze this receipt and return ONLY a raw JSON object (no markdown) with fields: \n- vendor\n- date (YYYY-MM-DD)\n- amount (number)\n- currency: Return the 3-letter ISO code (e.g. 'USD', 'CAD', 'EUR'). If the currency symbol is generic ($) and no address is found, assume 'CAD'.\n- summary\n- category: This MUST be exactly one of the following strings: 'Eating Out', 'Groceries', 'Utilities', 'Transport', 'Shopping', 'Subscriptions', 'Tech & Office', 'Medical', 'Miscellaneous'. If you are unsure, use 'Miscellaneous'.",
"inputType": "binary",
"simplify": false,
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1,
"position": [
64,
-576
],
"id": "21949401-5686-4352-9526-c239036a4d4d",
"name": "Analyze an image",
"credentials": {
"googlePalmApi": {
"id": "p84bEFwG3jhgIwIp",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "39bc76ff-01b1-4699-8db3-00faa20e5a82",
"leftValue": "={{ $input.first().binary.data.mimeType }}",
"rightValue": "image",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-144,
-416
],
"id": "795539c8-d668-4341-ba38-5c39eeb8ac0d",
"name": "If"
},
{
"parameters": {
"resource": "document",
"modelId": {
"__rl": true,
"value": "models/gemini-2.5-flash",
"mode": "list",
"cachedResultName": "models/gemini-2.5-flash"
},
"text": "Analyze this receipt and return ONLY a raw JSON object (no markdown) with fields: \n- vendor\n- date (YYYY-MM-DD)\n- amount (number)\n- currency: Return the 3-letter ISO code (e.g. 'USD', 'CAD', 'EUR'). If the currency symbol is generic ($) and no address is found, assume 'CAD'.\n- summary\n- category: This MUST be exactly one of the following strings: 'Eating Out', 'Groceries', 'Utilities', 'Transport', 'Shopping', 'Subscriptions', 'Tech & Office', 'Medical', 'Miscellaneous'. If you are unsure, use 'Miscellaneous'.",
"inputType": "binary",
"simplify": false,
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1,
"position": [
64,
-304
],
"id": "29857232-9d49-48c4-9dbf-2edb6717deff",
"name": "Analyze a file",
"credentials": {
"googlePalmApi": {
"id": "p84bEFwG3jhgIwIp",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $json.message.photo ? $json.message.photo[$json.message.photo.length - 1].file_id : $json.message.document.file_id }}",
"additionalFields": {}
},
"id": "4402927e-3d54-4282-8ba7-ea1e84fcfb2c",
"name": "Get File",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-368,
-416
],
"webhookId": "cbe76ba7-fcb6-445a-894a-9c7088f8822f",
"credentials": {
"telegramApi": {
"id": "xuFTbOQ62YpwhOpY",
"name": "Telegram account"
}
}
}
],
"pinData": {},
"connections": {
"Telegram Trigger": {
"main": [
[
{
"node": "Get File",
"type": "main",
"index": 0
}
]
]
},
"Format Data": {
"main": [
[
{
"node": "Backup to Drive",
"type": "main",
"index": 0
}
]
]
},
"Save to Sheets": {
"main": [
[
{
"node": "Send Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Backup to Drive": {
"main": [
[
{
"node": "Save to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Analyze an image": {
"main": [
[
{
"node": "Format Data",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "Analyze an image",
"type": "main",
"index": 0
}
],
[
{
"node": "Analyze a file",
"type": "main",
"index": 0
}
]
]
},
"Analyze a file": {
"main": [
[
{
"node": "Format Data",
"type": "main",
"index": 0
}
]
]
},
"Get File": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "91550b15-bca1-4aec-a1ea-6779c9ef35ed",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "0023d4f5dfb57fec37ca9d230f69e0e19118190dca2bfc4d821fffd957cc37e7"
},
"id": "4zHWtizvsvvFg5By",
"tags": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment