// ==UserScript== // @name ING DiBa Transactions to CSV // @namespace http://tampermonkey.net/ // @version 1.2 // @description Extract ING transactions and convert to CSV format // @author Aron Schüler // @match https://banking.ing.de/* // @grant none // ==/UserScript== (function () { "use strict"; function extractTransactions(extractPrebookedTransactions = false) { let transactions = []; // Function to parse amount to separate outflow and inflow function parseAmount(amountStr) { let amount = amountStr .replace("€", "") .trim() .replace(".", "") .replace(",", "."); let outflow = ""; let inflow = ""; if (parseFloat(amount) < 0) { outflow = Math.abs(parseFloat(amount)); } else { inflow = Math.abs(parseFloat(amount)); } return { outflow, inflow }; } // Function to parse dates into en-US format function parseDate(dateStr) { let [day, month, year] = dateStr.split("."); return `${month}/${day}/${year}`; } // Function to clean up the extracted text function cleanText(text) { if (text) { return text .replace("/ ", " ") .replace(/\n/g, " ") .replace(/\s\s+/g, " ") .trim(); } return text; } // Function to remove prepended noise function cleanMemo(memo) { if (memo) { return memo.replace("Lastschrift / ", " "); } return memo; } // Function to parse memo into payee function getPayeeFromMemo(memo) { if (memo) { // Match for "Überweisung" transactions let payee = memo.match(/(.*?)(?:\s+IBAN|,)/); if (payee) { return payee[1]; } // Match for transactions to or from other money sources let transfer = memo.match(/Kleingeld Plus - (.*)/) || memo.includes("Bareinzahlung"); if (transfer) { return "YOUR_NAME"; } // Match for "Entgelt" transactions let entgelt = memo.includes("Entgelt"); if (entgelt) { return "ING DiBa"; } } return ""; } if (extractPrebookedTransactions) { // Extract prebooked transactions document .querySelectorAll(".g2p-transaction-summary--prebooked") .forEach((summary) => { let button = summary.querySelector(".g2p-transaction-details-btn"); let payee = cleanText( summary.querySelector(".g2p-transaction-summary__recipient") ?.textContent || "", ); let memo = "Vorgebuchte Transaktion"; let amountText = cleanText( summary.querySelector( ".g2p-transaction-summary__amount .g2p-amount", )?.textContent || "", ); let { outflow, inflow } = parseAmount(amountText); let dateMatch = button .getAttribute("aria-label") .match(/Datum (\d{2}\.\d{2}\.\d{4})/); let date = dateMatch ? parseDate(dateMatch[1]) : new Date().toLocaleDateString("en-US"); if (payee === "") { payee = getPayeeFromMemo(memo); } transactions.push({ date, payee, memo, outflow, inflow }); }); } else { // Extract past transactions document.querySelectorAll(".g2p-transaction-group").forEach((group) => { let dateElement = group.querySelector(".g2p-transaction-group__title"); let date = dateElement ? parseDate(dateElement.textContent.trim()) : new Date().toLocaleDateString("en-US"); group .querySelectorAll(".g2p-transaction-summary--notags") .forEach((summary) => { let payee = cleanText( summary.querySelector(".g2p-transaction-summary__recipient") ?.textContent || "", ); let memo = cleanText( summary.querySelector(".g2p-transaction-summary__type") ?.textContent || "", ); memo = cleanMemo(memo); let amountText = cleanText( summary.querySelector( ".g2p-transaction-summary__amount .g2p-amount", )?.textContent || "", ); if (payee === "") { payee = getPayeeFromMemo(memo); } let { outflow, inflow } = parseAmount(amountText); transactions.push({ date, payee, memo, outflow, inflow }); }); }); } return transactions; } function convertToCSV(transactions) { let csvContent = "Date\tPayee\tMemo\tOutflow\tInflow\n"; transactions.forEach((tr) => { csvContent += `${tr.date}\t${tr.payee}\t${tr.memo}\t${tr.outflow}\t${tr.inflow}\n`; }); return csvContent; } function downloadCSV(csvContent, prefix = "") { let blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); let link = document.createElement("a"); let url = URL.createObjectURL(blob); link.setAttribute("href", url); let name = `${prefix}transactions-${new Date() .toLocaleDateString("de-DE") .replace(/\./g, "-")}.csv`; link.setAttribute("download", name); link.style.visibility = "hidden"; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // Add a button to trigger the CSV download of prebooked let btn = document.createElement("button"); btn.innerHTML = "Download prebooked transactions CSV"; btn.style.position = "fixed"; btn.style.top = "10px"; btn.style.right = "10px"; btn.style.zIndex = 1000; btn.onclick = function () { let transactions = extractTransactions(true); let csvContent = convertToCSV(transactions); downloadCSV(csvContent, "prebooked-"); }; document.body.appendChild(btn); // Add a button to trigger the CSV download of past transactions let btn2 = document.createElement("button"); btn2.innerHTML = "Download past transactions CSV"; btn2.style.position = "fixed"; btn2.style.top = "50px"; btn2.style.right = "10px"; btn2.style.zIndex = 1000; btn2.onclick = function () { let transactions = extractTransactions(false); let csvContent = convertToCSV(transactions); downloadCSV(csvContent); }; document.body.appendChild(btn2); })();