diff --git a/xactfetch.py b/xactfetch.py index 67a019e..c14df1c 100644 --- a/xactfetch.py +++ b/xactfetch.py @@ -13,17 +13,21 @@ from types import TracebackType from typing import Any, Optional, Type import httpx -from playwright.async_api import Page +from playwright.async_api import Playwright, Page from playwright.async_api import async_playwright log = logging.getLogger('xactfetch') -NTFY_URL = os.environ['NTFY_URL'] -NTFY_TOPIC = os.environ['NTFY_TOPIC'] -FIREFLY_III_URL = os.environ['FIREFLY_III_URL'] -FIREFLY_III_IMPORTER_URL = os.environ['FIREFLY_IMPORT_URL'] +NTFY_URL = os.environ.get('NTFY_URL', 'https://ntfy.pyrocufflink.blue') +NTFY_TOPIC = os.environ.get('NTFY_TOPIC', 'dustin') +FIREFLY_III_URL = os.environ.get( + 'FIREFLY_III_URL', 'https://firefly.pyrocufflink.blue' +) +FIREFLY_III_IMPORTER_URL = os.environ.get( + 'FIREFLY_IMPORT_URL', 'https://firefly-importer.pyrocufflink.blue' +) SECRET_SOCKET_PATH = os.environ.get('SECRET_SOCKET_PATH') XDG_RUNTIME_DIR = os.environ.get('XDG_RUNTIME_DIR') @@ -166,6 +170,7 @@ async def get_last_transaction_date(key: int, token: str) -> datetime.date: 'Authorization': f'Bearer {token}', 'Accept': 'application/vnd.api+json', }, + timeout=10, ) r.raise_for_status() last_date = datetime.datetime.min @@ -283,8 +288,12 @@ class ntfyerror: ) if os.environ.get('DEBUG_NTFY', '1') == '0': return True - if ss := await self.page.screenshot(): - await asyncio.to_thread(save_screenshot, ss) + try: + if ss := await self.page.screenshot(): + await asyncio.to_thread(save_screenshot, ss) + except Exception: + log.exception('Failed to get screenshot:') + ss = None await ntfy( message=str(exc_value), title=f'xactfetch failed for {self.bank}', @@ -702,10 +711,7 @@ class Chase: return secret.decode() -async def amain() -> None: - logging.basicConfig(level=logging.DEBUG) - secrets = SecretsClient() - await secrets.connect() +async def fetch_transactions(pw: Playwright, secrets: SecretsClient) -> bool: log.debug('Getting Firefly III access token') token = (await secrets.get_secret('firefly.token')).decode() import_secret = ( @@ -720,31 +726,40 @@ async def amain() -> None: ) end_date = datetime.date.today() - datetime.timedelta(days=1) failed = False - async with async_playwright() as pw, secrets: - browser = await pw.chromium.launch(headless=False) - context = await browser.new_context() - await context.tracing.start(screenshots=True, snapshots=True) - page = await context.new_page() - banks = sys.argv[1:] or list(ACCOUNTS.keys()) - if 'commerce' in banks: - if not await download_commerce( - page, secrets, end_date, token, importer - ): - failed = True - if 'chase' in banks: - if not await download_chase( - page, secrets, end_date, token, importer - ): - failed = True - if failed: - await context.tracing.stop(path='trace.zip') - with open('trace.zip', 'rb') as f: - await ntfy( - 'Downloading one or more transaction lists failed.', - attach=f.read(), - filename='trace.zip', - ) - raise SystemExit(1 if failed else 0) + browser = await pw.chromium.launch(headless=False) + context = await browser.new_context() + await context.tracing.start(screenshots=True, snapshots=True) + page = await context.new_page() + banks = sys.argv[1:] or list(ACCOUNTS.keys()) + if 'commerce' in banks: + if not await download_commerce( + page, secrets, end_date, token, importer + ): + failed = True + if 'chase' in banks: + if not await download_chase(page, secrets, end_date, token, importer): + failed = True + if failed: + await context.tracing.stop(path='trace.zip') + with open('trace.zip', 'rb') as f: + await ntfy( + 'Downloading one or more transaction lists failed.', + attach=f.read(), + filename='trace.zip', + ) + return failed + + +async def amain() -> None: + logging.basicConfig(level=logging.DEBUG) + + async with SecretsClient() as secrets: + try: + async with async_playwright() as pw: + failed = await fetch_transactions(pw, secrets) + raise SystemExit(1 if failed else 0) + except asyncio.exceptions.InvalidStateError as e: + log.debug('Ignoring exception: %s', e, exc_info=sys.exc_info()) def main():