feat(integration): integration is fully finished, will refractor later
This commit is contained in:
parent
97c8331866
commit
2491eae95f
5 changed files with 171 additions and 47 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,6 +3,8 @@
|
|||
**/My-Finances*
|
||||
notes
|
||||
**/*qfx
|
||||
**/2fa
|
||||
**/output
|
||||
|
||||
# Others
|
||||
node_modules/
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
||||
//await api.shutdown();
|
||||
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));
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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...
|
||||
|
|
|
|||
|
|
@ -1,21 +1,28 @@
|
|||
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():
|
||||
print("Starting Revision")
|
||||
while (True):
|
||||
with open("./data/output", "w") as output:
|
||||
output.write("")
|
||||
|
||||
|
||||
# Removes the OLDEST file on the list ()
|
||||
file_count = 0
|
||||
for file in os.scandir("./qfx"):
|
||||
print(file_count)
|
||||
#print(file_count)
|
||||
if file_count > 1:
|
||||
#print(os.listdir("./qfx"))
|
||||
print("Removed: " + os.listdir("./qfx")[0])
|
||||
|
|
@ -25,25 +32,56 @@ def revise():
|
|||
if file.is_file():
|
||||
file_count += 1
|
||||
|
||||
# Get the last two files
|
||||
oldest_file = open("./qfx/" + os.listdir("./qfx")[0])
|
||||
newest_file = open("./qfx/" + os.listdir("./qfx")[-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]
|
||||
|
||||
# Differs the two files
|
||||
diff = difflib.ndiff(oldest_file.readlines(), newest_file.readlines())
|
||||
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__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue