feat(integration): integration is fully finished, will refractor later

This commit is contained in:
devaine 2026-02-02 19:17:44 -06:00
commit 2491eae95f
Signed by: devaine
GPG key ID: 954B1DCAC6FF84EE
5 changed files with 171 additions and 47 deletions

2
.gitignore vendored
View file

@ -3,6 +3,8 @@
**/My-Finances*
notes
**/*qfx
**/2fa
**/output
# Others
node_modules/

View file

@ -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] == "<STMTTRN>") {
start.push(i)
} else if (lines[i] == "</STMTTRN>") {
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("<TRNTYPE>")) {
type = lines[j].substring(9)
} else if(lines[j].includes("<DTPOSTED>")) {
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("<TRNAMT>")) {
amount = Number(lines[j].substring(8).replace(".", ""))
} else if(lines[j].includes("<NAME>")) {
name = lines[j].substring(6)
if(lines[j+1].includes("<MEMO>")) {
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();
})();

View file

@ -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",

View file

@ -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...

View file

@ -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 <DTEND> 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 <STMTTRN>
if "<STMTTRN>" in line:
start.append(num-1)
if "</STMTTRN>" 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 <STMTTRN> to </STMTTRN> 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__":