diff --git a/.gitignore b/.gitignore
index c3d38e3..5e15c45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
**/My-Finances*
notes
**/*qfx
+**/2fa
+**/output
# Others
node_modules/
diff --git a/actual-api/src/index.js b/actual-api/src/index.js
index 9a78fe0..d2d6c38 100644
--- a/actual-api/src/index.js
+++ b/actual-api/src/index.js
@@ -1,11 +1,13 @@
require("dotenv").config();
+
+const fs = require("node:fs");
let api = require("@actual-app/api");
// Constants
-const ACTUAL_URL = process.env.ACTUAL_URL
-const ACTUAL_PASSWORD = process.env.ACTUAL_PASSWORD
-const ACTUAL_SYNC_ID = process.env.ACTUAL_SYNC_ID
-const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID
+const ACTUAL_URL = process.env.ACTUAL_URL;
+const ACTUAL_PASSWORD = process.env.ACTUAL_PASSWORD;
+const ACTUAL_SYNC_ID = process.env.ACTUAL_SYNC_ID;
+const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID;
(async () => {
await api.init({
@@ -17,16 +19,80 @@ const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID
console.log("Downloading the Budget Data")
await api.downloadBudget(ACTUAL_SYNC_ID);
- console.log("Getting Account Info")
- let accountInfo = await api.getAccounts()
- console.log(accountInfo)
+ while(true) {
+ console.log("Getting the most recent transactions")
+ var latestTrans = await api.getTransactions(ACTUAL_CHECKING_ID)
- function inspectBankFile() {
-
+ var actual_date = new Date(latestTrans[0].date)
+ var actual_amount = Number(latestTrans[0].amount)
+
+ fs.readFile("./webscrape-bank/data/output", "utf8", async (err, data) => {
+ if (err) {
+ console.error(err);
+ return;
+ }
+
+ var start = []
+ var end = []
+
+ var lines = data.split("\n");
+
+
+ // Parse to grab indexes of each transaction
+ for (var i = 0; i < lines.length; i++) {
+ if(lines[i] == "") {
+ start.push(i)
+ } else if (lines[i] == "") {
+ end.push(i)
+ }
+ }
+
+ // Search through every section of output file
+ for (var i = 0; i < end.length; i++) {
+ for(var j = start[i]; j < end[i]; j++) {
+ var date
+ var type
+ var amount
+ var notes
+ var name
+
+ if(lines[j].includes("")) {
+ type = lines[j].substring(9)
+ } else if(lines[j].includes("")) {
+ var dt_string = lines[j].substring(10).substring(0, 8)
+ var year = dt_string.substring(0, 4)
+ var month = dt_string.substring(5, 6) - 1
+ var day = dt_string.substring(6, 8)
+
+ date = new Date(year, month, day)
+ } else if(lines[j].includes("")) {
+ amount = Number(lines[j].substring(8).replace(".", ""))
+ } else if(lines[j].includes("")) {
+ name = lines[j].substring(6)
+ if(lines[j+1].includes("")) {
+ notes = lines[j+1].substring(6)
+ }
+ }
+
+ // If everything is defined and we are at the last line.
+ if(date, type, amount, notes !== undefined && j + 1 == end[i]) {
+ console.log("Everything is defined")
+ if((actual_date < date && actual_date !== date) && amount !== actual_amount) {
+ console.log("NEW TRANSACTION!")
+ api.importTransactions(ACTUAL_CHECKING_ID, [
+ {
+ date: date,
+ amount: amount,
+ payee_name: name,
+ notes: notes
+ }
+ ])
+ }
+ }
+ }
+ }
+ });
+ await new Promise(r => setTimeout(r, 10000));
}
-
-
- //await api.shutdown();
-
-
})();
+
diff --git a/package.json b/package.json
index 817167d..167d662 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,8 @@
"license": "ISC",
"main": "index.js",
"scripts": {
- "actual": "node actual-api/src/index.js"
+ "actual": "node actual-api/src/index.js",
+ "start": "concurrently 'npm run actual' 'cd ./webscrape-bank && ./run.sh'"
},
"dependencies": {
"@actual-app/api": "^26.1.0",
diff --git a/webscrape-bank/src/extract.py b/webscrape-bank/src/extract.py
index 3ab746f..e00117c 100644
--- a/webscrape-bank/src/extract.py
+++ b/webscrape-bank/src/extract.py
@@ -12,7 +12,7 @@ LOGIN_LINK = os.getenv("LOGIN_LINK")
ACCOUNT_LINK = os.getenv("ACCOUNT_LINK")
def main(playwright: Playwright) -> None:
- browser = playwright.chromium.launch(headless=False)
+ browser = playwright.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
@@ -28,10 +28,27 @@ def main(playwright: Playwright) -> None:
page.get_by_role("button", name="Text").click()
page.get_by_test_id("text-field").click()
- # Bank prompts 2FA (hopefully only once)
- two_fac = input("2FA Code: ")
+ # Bank prompts 2FA (hopefully only once) in this meantime
- page.get_by_test_id("text-field").fill(two_fac)
+ two_fac_code = ""
+
+ # Write/Overwrite an empty 2FA file
+ with open("./2fa", "w") as two_fac:
+ two_fac.write("")
+
+ with open("./2fa", "r") as two_fac:
+ line = two_fac.readline()
+ print(line)
+ while line == "":
+ print("2FA code hasn't been entered, make sure to enter code in ./2fa !")
+ print("Retrying in 5 seconds...")
+ sleep(5)
+ line = two_fac.readline()
+
+ two_fac_code = line
+ print("2FA Code Found: " + two_fac_code)
+
+ page.get_by_test_id("text-field").fill(two_fac_code)
page.get_by_test_id("private-button").click()
# Wait for everything to load...
diff --git a/webscrape-bank/src/main.py b/webscrape-bank/src/main.py
index 3b53bd3..da6dd99 100644
--- a/webscrape-bank/src/main.py
+++ b/webscrape-bank/src/main.py
@@ -1,49 +1,87 @@
from playwright.sync_api import sync_playwright
-from threading import Thread
import os
import extract
-#from datetime import datetime
-import difflib
+from datetime import datetime
+from time import sleep
+from multiprocessing import Process
# Playwright in the background
def playwright():
+ print("Starting Playwright")
with sync_playwright() as playwright:
extract.main(playwright)
def revise():
- # Removes the OLDEST file on the list ()
- file_count = 0
- for file in os.scandir("./qfx"):
- print(file_count)
- if file_count > 1:
- #print(os.listdir("./qfx"))
- print("Removed: " + os.listdir("./qfx")[0])
- os.remove("./qfx/" + os.listdir("./qfx")[0])
+ print("Starting Revision")
+ while (True):
+ with open("./data/output", "w") as output:
+ output.write("")
- if file.is_file():
- file_count += 1
+ # Removes the OLDEST file on the list ()
+ file_count = 0
+ for file in os.scandir("./qfx"):
+ #print(file_count)
+ if file_count > 1:
+ #print(os.listdir("./qfx"))
+ print("Removed: " + os.listdir("./qfx")[0])
+ os.remove("./qfx/" + os.listdir("./qfx")[0])
- # Get the last two files
- oldest_file = open("./qfx/" + os.listdir("./qfx")[0])
- newest_file = open("./qfx/" + os.listdir("./qfx")[-1])
- # Differs the two files
- diff = difflib.ndiff(oldest_file.readlines(), newest_file.readlines())
+ if file.is_file():
+ file_count += 1
+
+ # Thanks to: https://stackoverflow.com/a/33657921
+ # Find the for the newest files
+ # Grabs all starting and ending transaction indexes aswell as the time of the latest transaction
+ start = []
+ end = []
+ latest_trans_time = datetime.now()
+ with open("./qfx/" + os.listdir("./qfx")[-1]) as newest_file:
+ num = 0
+ # Find the number and line under a object that supports iteration
+ for num, line in enumerate(newest_file, 1):
+ if "DTEND" in line:
+ time_string = line.strip()[7:-8]
+
+ year = int(time_string[0:4])
+ month = int(time_string[5:6])
+ day = int(time_string[6:8])
+
+ latest_trans_time.replace(year, month, day)
+ num = str(num)
+
+ # TODO: The idea is to find a way to grab all data relating to latest_trans_time WITHIN
+ if "" in line:
+ start.append(num-1)
+
+ if "" in line:
+ end.append(num)
+
+ # Seperates all transactions to the latest ones to ./data/output
+ with open("./qfx/" + os.listdir("./qfx")[-1]) as newest_file:
+ lines = newest_file.read().splitlines()
+
+ for i in range(len(start)):
+ ST_BEGINS = start[i]
+ ST_ENDS = end[i]
+
+ with open("./data/output", "a") as output:
+ # NOTE: Puts all data from to into its individual line
+ output.write("\n".join(lines[ST_BEGINS:ST_ENDS]).replace(" ", "").replace("\t", ""))
+ output.write("\n")
+
+ # Wait a minute before executing again
+ print("Done with revision!")
+ sleep(60)
- # Grabs only changes
- # Thanks to: https://stackoverflow.com/a/15864920
- changes = [l for l in diff if l.startswith("+ ") or l.startswith('- ')]
- print("RESULT:")
- for change in changes:
- print(change[2:])
def main():
- revise_thread = Thread(target=revise())
- #pw_thread = Thread(target=playwright())
- #pw_thread.start()
- revise_thread.start()
+ pw_proc = Process(target=playwright)
+ pw_proc.start()
+ revise_proc = Process(target=revise)
+ revise_proc.start()
if __name__ == "__main__":