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*
|
**/My-Finances*
|
||||||
notes
|
notes
|
||||||
**/*qfx
|
**/*qfx
|
||||||
|
**/2fa
|
||||||
|
**/output
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
|
const fs = require("node:fs");
|
||||||
let api = require("@actual-app/api");
|
let api = require("@actual-app/api");
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const ACTUAL_URL = process.env.ACTUAL_URL
|
const ACTUAL_URL = process.env.ACTUAL_URL;
|
||||||
const ACTUAL_PASSWORD = process.env.ACTUAL_PASSWORD
|
const ACTUAL_PASSWORD = process.env.ACTUAL_PASSWORD;
|
||||||
const ACTUAL_SYNC_ID = process.env.ACTUAL_SYNC_ID
|
const ACTUAL_SYNC_ID = process.env.ACTUAL_SYNC_ID;
|
||||||
const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID
|
const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await api.init({
|
await api.init({
|
||||||
|
|
@ -17,16 +19,80 @@ const ACTUAL_CHECKING_ID = process.env.ACTUAL_CHECKING_ID
|
||||||
console.log("Downloading the Budget Data")
|
console.log("Downloading the Budget Data")
|
||||||
await api.downloadBudget(ACTUAL_SYNC_ID);
|
await api.downloadBudget(ACTUAL_SYNC_ID);
|
||||||
|
|
||||||
console.log("Getting Account Info")
|
while(true) {
|
||||||
let accountInfo = await api.getAccounts()
|
console.log("Getting the most recent transactions")
|
||||||
console.log(accountInfo)
|
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",
|
"license": "ISC",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"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": {
|
"dependencies": {
|
||||||
"@actual-app/api": "^26.1.0",
|
"@actual-app/api": "^26.1.0",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ LOGIN_LINK = os.getenv("LOGIN_LINK")
|
||||||
ACCOUNT_LINK = os.getenv("ACCOUNT_LINK")
|
ACCOUNT_LINK = os.getenv("ACCOUNT_LINK")
|
||||||
|
|
||||||
def main(playwright: Playwright) -> None:
|
def main(playwright: Playwright) -> None:
|
||||||
browser = playwright.chromium.launch(headless=False)
|
browser = playwright.chromium.launch(headless=True)
|
||||||
context = browser.new_context()
|
context = browser.new_context()
|
||||||
|
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
|
|
@ -28,10 +28,27 @@ def main(playwright: Playwright) -> None:
|
||||||
page.get_by_role("button", name="Text").click()
|
page.get_by_role("button", name="Text").click()
|
||||||
page.get_by_test_id("text-field").click()
|
page.get_by_test_id("text-field").click()
|
||||||
|
|
||||||
# Bank prompts 2FA (hopefully only once)
|
# Bank prompts 2FA (hopefully only once) in this meantime
|
||||||
two_fac = input("2FA Code: ")
|
|
||||||
|
|
||||||
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()
|
page.get_by_test_id("private-button").click()
|
||||||
|
|
||||||
# Wait for everything to load...
|
# Wait for everything to load...
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,28 @@
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
from threading import Thread
|
|
||||||
import os
|
import os
|
||||||
import extract
|
import extract
|
||||||
#from datetime import datetime
|
from datetime import datetime
|
||||||
import difflib
|
from time import sleep
|
||||||
|
from multiprocessing import Process
|
||||||
|
|
||||||
# Playwright in the background
|
# Playwright in the background
|
||||||
def playwright():
|
def playwright():
|
||||||
|
print("Starting Playwright")
|
||||||
with sync_playwright() as playwright:
|
with sync_playwright() as playwright:
|
||||||
extract.main(playwright)
|
extract.main(playwright)
|
||||||
|
|
||||||
|
|
||||||
def revise():
|
def revise():
|
||||||
|
print("Starting Revision")
|
||||||
|
while (True):
|
||||||
|
with open("./data/output", "w") as output:
|
||||||
|
output.write("")
|
||||||
|
|
||||||
|
|
||||||
# Removes the OLDEST file on the list ()
|
# Removes the OLDEST file on the list ()
|
||||||
file_count = 0
|
file_count = 0
|
||||||
for file in os.scandir("./qfx"):
|
for file in os.scandir("./qfx"):
|
||||||
print(file_count)
|
#print(file_count)
|
||||||
if file_count > 1:
|
if file_count > 1:
|
||||||
#print(os.listdir("./qfx"))
|
#print(os.listdir("./qfx"))
|
||||||
print("Removed: " + os.listdir("./qfx")[0])
|
print("Removed: " + os.listdir("./qfx")[0])
|
||||||
|
|
@ -25,25 +32,56 @@ def revise():
|
||||||
if file.is_file():
|
if file.is_file():
|
||||||
file_count += 1
|
file_count += 1
|
||||||
|
|
||||||
# Get the last two files
|
# Thanks to: https://stackoverflow.com/a/33657921
|
||||||
oldest_file = open("./qfx/" + os.listdir("./qfx")[0])
|
# Find the <DTEND> for the newest files
|
||||||
newest_file = open("./qfx/" + os.listdir("./qfx")[-1])
|
# 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
|
year = int(time_string[0:4])
|
||||||
diff = difflib.ndiff(oldest_file.readlines(), newest_file.readlines())
|
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():
|
def main():
|
||||||
revise_thread = Thread(target=revise())
|
pw_proc = Process(target=playwright)
|
||||||
#pw_thread = Thread(target=playwright())
|
pw_proc.start()
|
||||||
#pw_thread.start()
|
revise_proc = Process(target=revise)
|
||||||
revise_thread.start()
|
revise_proc.start()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue