diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | account.sample.ini | 16 | ||||
-rw-r--r-- | requirements.txt | 3 | ||||
-rwxr-xr-x | transfers.py | 91 |
5 files changed, 119 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69a43fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +account.ini +*.db +*.iml +*.idea
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..996ed6e --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Finanzstatus scripts + +## transfers + +fetch transactions via hbci and send jabber notices.
\ No newline at end of file diff --git a/account.sample.ini b/account.sample.ini new file mode 100644 index 0000000..0d54dfd --- /dev/null +++ b/account.sample.ini @@ -0,0 +1,16 @@ +[bank] +; The banks hbci url +url=https://brokerage-hbci.consors.de/hbci +; Bankleitzeil +blz=76030080 +; Login (Consorsbank: Kontonummer + Berechtigtennummer 001) +username=123123123001 +; PIN +password=secre +; The account-number to be inspected (kontonummer) +accountnumber=123123123 + +[jabber] +jid = bot@domain.tld +password = bot-password +recipient = receiver-of-notifications
\ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ceba34e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fints==0.2.1 +peewee==2.10.2 +slixmpp==1.2.4.post1
\ No newline at end of file diff --git a/transfers.py b/transfers.py new file mode 100755 index 0000000..c14055f --- /dev/null +++ b/transfers.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +import configparser +import argparse +import datetime +import logging + +from fints.client import FinTS3PinTanClient +import peewee as p +from slixmpp import ClientXMPP + +logging.basicConfig(level=logging.INFO) + +parser = argparse.ArgumentParser() +parser.add_argument('--config', nargs=1, required=True, help='Account .ini config file') +parser.add_argument('--db', nargs=1, default='log.db', help='Database') +parser.add_argument('--fetch-last', metavar='DAYS', default=None, type=int, help='Fetch all last DAYS') +args = parser.parse_args() + +config = configparser.ConfigParser() +config.read(args.config) +db = p.SqliteDatabase(args.db) + + +class Transaction(p.Model): + created_date = p.DateField(default=datetime.date.today) + accountnumber = p.CharField() + sepa_entry_date = p.DateField() + sepa_amount_str = p.CharField() + sepa_transaction_details = p.CharField() + + class Meta: + database = db + indexes = ((('accountnumber', 'sepa_entry_date', 'sepa_transaction_details'), True),) + + +db.create_tables([Transaction], True) + +if args.fetch_last is None: + # Look for last date of a saved transaction or fallback default timesamp + last_transaction_date = Transaction \ + .select(p.fn.max(Transaction.sepa_entry_date)) \ + .where(Transaction.accountnumber == config['bank']['accountnumber']) \ + .scalar(convert=True) or datetime.date.today() - datetime.timedelta(30) +else: + last_transaction_date = datetime.date.today() - datetime.timedelta(args.fetch_last) + +f = FinTS3PinTanClient(config['bank']['blz'], config['bank']['username'], config['bank']['password'], + config['bank']['url']) +accounts = f.get_sepa_accounts() +account = list(filter(lambda a: a.accountnumber == config['bank']['accountnumber'], accounts))[0] + +logging.info("select transactions on account %s starting from %s", account.accountnumber, last_transaction_date) +transactions = f.get_statement(account, last_transaction_date, datetime.date.today()) + +new_transactions = [] +for t in transactions: + y, m, d, *_ = t.data['entry_date'].timetuple() + entry_date = datetime.date(y, m, d) + filter_pred = { + 'accountnumber': config['bank']['accountnumber'], + 'sepa_entry_date': entry_date, + 'sepa_transaction_details': t.data['transaction_details'], + } + data = {'sepa_amount_str': str(t.data['amount'])} + try: + record = Transaction.get(**filter_pred) + record.update(data) + logging.info("Updated %s", record.id) + except Transaction.DoesNotExist: + data.update(filter_pred) + record = Transaction.create(**data) + new_transactions += [record] + logging.info("Created %s", record.id) + record.save() + +cl = ClientXMPP(config['jabber']['jid'], config['jabber']['password']) + + +def session_start(event): + cl.send_presence() + for t in new_transactions: + message = "{} {} {}".format(t.accountnumber, t.sepa_amount_str, t.sepa_transaction_details) + cl.send_message(config['jabber']['recipient'], message, mtype='chat') + logging.info("Sent info %s", message) + cl.disconnect() + + +cl.add_event_handler('session_start', session_start) +cl.add_event_handler('killed', lambda *_: cl.loop.stop()) +cl.connect() +cl.process() |