| 1 | import os, pwd, hashlib, pickle |
| 2 | |
| 3 | def _localname(type): |
| 4 | mod = type.__module__ |
| 5 | if mod.startswith("fulbank."): |
| 6 | mod = mod[8:] |
| 7 | return "%s.%s" % (mod, type.__name__) |
| 8 | |
| 9 | class account(object): |
| 10 | @property |
| 11 | def number(self): raise NotImplementedError("account.number") |
| 12 | @property |
| 13 | def name(self): raise NotImplementedError("account.name") |
| 14 | def transactions(self): raise NotImplementedError("account.transactions") |
| 15 | |
| 16 | def __repr__(self): |
| 17 | return "#<%s %s: %r>" % (_localname(type(self)), self.number, self.name) |
| 18 | |
| 19 | class txnaccount(account): |
| 20 | @property |
| 21 | def balance(self): raise NotImplementedError("txnaccount.balance") |
| 22 | @property |
| 23 | def clearing(self): raise NotImplementedError("txnaccount.clearing") |
| 24 | @property |
| 25 | def fullnumber(self): raise NotImplementedError("txnaccount.fullnumber") |
| 26 | |
| 27 | class cardaccount(account): |
| 28 | pass |
| 29 | |
| 30 | class transaction(object): |
| 31 | @property |
| 32 | def value(self): raise NotImplementedError("transaction.value") |
| 33 | @property |
| 34 | def message(self): raise NotImplementedError("transaction.message") |
| 35 | @property |
| 36 | def date(self): raise NotImplementedError("transaction.date") |
| 37 | |
| 38 | @property |
| 39 | def hash(self): |
| 40 | dig = hashlib.sha256() |
| 41 | dig.update(str(self.date.toordinal()).encode("ascii") + b"\0") |
| 42 | dig.update(self.message.encode("utf-8") + b"\0") |
| 43 | dig.update(str(self.value.amount).encode("ascii") + b"\0") |
| 44 | dig.update(self.value.currency.symbol.encode("ascii") + b"\0") |
| 45 | return dig.hexdigest() |
| 46 | |
| 47 | def __repr__(self): |
| 48 | return "#<%s %s: %r>" % (_localname(type(self)), self.value, self.message) |
| 49 | |
| 50 | class session(object): |
| 51 | def save(self, filename): |
| 52 | with open(filename, "wb") as fp: |
| 53 | pickle.dump(self, fp) |
| 54 | |
| 55 | @staticmethod |
| 56 | def load(filename): |
| 57 | with open(filename, "rb") as fp: |
| 58 | return pickle.load(fp) |
| 59 | |
| 60 | def getsessnam(name): |
| 61 | if name == "fsb": |
| 62 | from . import fsb |
| 63 | return fsb.session |
| 64 | raise ValueError("no such known session type: " + name) |
| 65 | |
| 66 | def _sesspath(name): |
| 67 | return os.path.join(pwd.getpwuid(os.getuid()).pw_dir, ".cache/fulbank", name) |
| 68 | |
| 69 | def defaultsess(): |
| 70 | ret = os.getenv("NETBANKSESS") |
| 71 | if ret: |
| 72 | return ret |
| 73 | return "master" |
| 74 | |
| 75 | def loadsess(name=None, default=FileNotFoundError): |
| 76 | if name is None: name = defaultsess() |
| 77 | path = _sesspath(name) |
| 78 | if not os.path.exists(path): |
| 79 | if default is FileNotFoundError: |
| 80 | raise FileNotFoundError(name) |
| 81 | return default |
| 82 | return session.load(path) |
| 83 | |
| 84 | def savesess(sess, name=None): |
| 85 | if name is None: name = defaultsess() |
| 86 | path = _sesspath(name) |
| 87 | if sess is not None: |
| 88 | sessdir = os.path.dirname(path) |
| 89 | if not os.path.isdir(sessdir): |
| 90 | os.makedirs(sessdir) |
| 91 | return sess.save(_sesspath(name)) |
| 92 | else: |
| 93 | if os.path.exists(path): |
| 94 | os.unlink(path) |
| 95 | |
| 96 | class savedsess(object): |
| 97 | def __init__(self, name=None): |
| 98 | if name is None: name = defaultsess() |
| 99 | self.name = name |
| 100 | self.sess = None |
| 101 | |
| 102 | def __enter__(self): |
| 103 | self.sess = loadsess(self.name) |
| 104 | return self.sess |
| 105 | |
| 106 | def __exit__(self): |
| 107 | savesess(self.sess, name) |
| 108 | self.sess = None |