Abstracted some common data-types.
[fulbank.git] / netbank
1 #!/usr/bin/python3
2
3 import sys, os, getopt, pwd
4 from fulbank import auth
5
6 sesstype = None
7 sess = None
8
9 def find(seq, *, item=None, match=None, key=None, default=LookupError):
10     if key is None:
11         key = lambda o: o
12     if match is None and item is not None:
13         match = lambda o: o == item
14     for thing in seq:
15         if match(key(thing)):
16             return thing
17     if default is LookupError:
18         raise LookupError()
19     else:
20         return default
21
22 def usage(out):
23     out.write("usage: netbank [-h] BANK-ID COMMAND [ARGS...]\n")
24
25 def requiresess(fn):
26     def wrap(cmd, args):
27         if sess is None:
28             sys.stderr.write("netbank: %s: no current session\n" % (cmd))
29             sys.exit(1)
30         return fn(cmd, args)
31     return wrap
32
33 commands = {}
34
35 def cmd_login(cmd, args):
36     global sess
37     if len(args) < 1:
38         sys.stderr.write("usage: login TYPE\n")
39         sys.exit(1)
40     sess = sesstype.create()
41     if args[0] == "bankid":
42         if len(args) < 2:
43             sys.stderr.write("usage: login bankid USER-ID\n")
44             sys.exit(1)
45         with auth.ttyconv() as conv:
46             sess.auth_bankid(args[1], conv)
47     else:
48         sys.stderr.write("netbank: %s: unknown authentication type\n" % (args[0]))
49         sys.exit(1)
50 commands["login"] = cmd_login
51
52 @requiresess
53 def cmd_logout(cmd, args):
54     global sess
55     if sess is not None:
56         sess.close()
57         sess = None
58 commands["logout"] = cmd_logout
59
60 @requiresess
61 def cmd_ping(cmd, args):
62     sess.keepalive()
63 commands["ping"] = cmd_ping
64
65 @requiresess
66 def cmd_lsacct(cmd, args):
67     for acct in sess.accounts:
68         sys.stdout.write("%s (%s): %s\n" % (acct.number, acct.name, acct.balance))
69 commands["lsacct"] = cmd_lsacct
70
71 @requiresess
72 def cmd_lstxn(cmd, args):
73     opts, args = getopt.getopt(args, "n:")
74     num = 10
75     for o, a in opts:
76         if o == "-n":
77             num = int(a)
78     if len(args) < 1:
79         sys.stderr.write("usage: lstxn [-n NUM] ACCOUNT\n")
80         sys.exit(1)
81     try:
82         acct = find(sess.accounts, item=args[0], key=lambda acct: acct.number)
83     except LookupError:
84         sys.stderr.write("netbank: %s: no such account\n" % (args[0]))
85         sys.exit(1)
86     for i, txn in zip(range(num), acct.transactions()):
87         sys.stdout.write("%s %s: %s\n" % (txn.date.isoformat(), txn.value, txn.message))
88 commands["lstxn"] = cmd_lstxn
89
90 def main():
91     global sess, sesstype
92
93     opts, args = getopt.getopt(sys.argv[1:], "h")
94     for o, a in opts:
95         if o == "-h":
96             usage(sys.stdout)
97             sys.exit(0)
98     if len(args) < 2:
99         usage(sys.stderr)
100         sys.exit(1)
101
102     if args[0] == "fsb":
103         import fulbank.fsb
104         sesstype = fulbank.fsb.session
105     else:
106         sys.stderr.write("netbank: %s: unknown bank id\n" % (args[0]))
107         sys.exit(1)
108     sesspath = os.path.join(pwd.getpwuid(os.getuid()).pw_dir, ".cache/fulbank", args[0])
109     cmd = args[1]
110     args = args[2:]
111
112     if os.path.exists(sesspath):
113         sess = sesstype.load(sesspath)
114     else:
115         sess = None
116     if cmd in commands:
117         commands[cmd](cmd, args)
118     else:
119         sys.stderr.write("netbank: %s: unknown command\n" % (cmd))
120         sys.exit(1)
121     if sess is not None:
122         sessdir = os.path.dirname(sesspath)
123         if not os.path.isdir(sessdir):
124             os.makedirs(sessdir)
125         sess.save(sesspath)
126     else:
127         if os.path.exists(sesspath):
128             os.unlink(sesspath)
129
130 try:
131     if __name__ == "__main__":
132         main()
133 except KeyboardInterrupt:
134     sys.exit(1)