#!/usr/bin/env python3 import json import os import subprocess import sys import tanken """Dict of dicts associating a second person to an amount""" balance = {} name2num = {} num2name = {} group_id = "GELD!!!" send_cmd = "signal-cli -u irgendwer send -g irgendwem" def to_cent(euro): euro = euro.split('.') if len(euro) > 2: raise TypeError euro[0] = int(euro[0]) if len(euro) < 2: euro.append(0) else: if len(euro[1]) == 1: euro[1] = int(euro[1]) * 10 else: euro[1] = int(euro[1]) return euro[0] * 100 + euro[1] def to_euro(cents): return str(cents/100) def send(msg): subprocess.run(send_cmd.split(' '), input=msg.encode()) def create_summary(user): summary = "" total = 0 p_balances = balance[user] # failes if user is not in balance for person in p_balances: amount = p_balances[person] if amount == 0: continue total -= amount summary += "\t{} {} {}\n".format("<-" if amount < 0 else "->", person, to_euro(abs(amount))) if summary == "": summary = "\tAll fine :)\n" else: summary += "\tBalance: {}".format(to_euro(total)) summary = user + ":\n" + summary return summary def create_total_summary(): summary = "Summary:" for person in balance: summary += '\n' summary += "* " + create_summary(person) return summary def create_members(): r = "" for m in name2num: r += m + ": " + name2num[m] + "\n" return r def create_help(): return """ Usage: send a message starting with '!' followed by a command Commands: ls | list - print all registered members help - print this help message reg name - register the sender with the name: name sum [name] - print a summary schieb amount recipient - give money to recipient zieh amount donor - get money from donor Happy Geldschieben! """ def handle_input(inp): messages = inp.split("\n\n") for message in messages: lines = message.split('\n') # A message with Body must have at least 4 lines if len(lines) < 4: continue lines[0].lower() w = lines[0].split(' ') # Broken message if len(w) < 4: print("Broken msg:") print(message) continue # TODO support linked devices sender_number = w[-3] body_found = False body = [] # message is not in the group with group_id not_in_group = True for idx, line in enumerate(lines[1:]): # Collect body till we find Group ID: # THIS COULD BREAK VERY EASILY if line[0:6] == "Body: ": body.append(line[6:]) body_found = True elif body_found: if not line == "Group info:": body.append(line) else: not_in_group = lines[idx+1].strip() == "Id: " + group_id break if not_in_group: continue w = body[0].split(' ') cmd = w[0] # supported commands are: # "!reg" register a name for this number # "!sum" send a summary to the group # "!schieb" "!gib" give money to somebody # "!zieh" "!nimm" add debt of somebody # "!list" "!ls" list members # "!tanken" calculate fuel cost parts # "!help" print all commands if cmd == "!reg": if len(w) != 2: send('ERROR: not in form "!reg name"') else: if w[1] in name2num: send("ERROR: name already registered") elif sender_number in num2name: send("ERROR: you are already registered") else: num2name[sender_number] = w[1] name2num[w[1]] = sender_number # add to balance nm = {} for m in balance: balance[m][w[1]] = 0 nm[m] = 0 balance[w[1]] = nm elif cmd == "!sum": if len(w) == 1: send(create_total_summary()) elif len(w) == 2: if w[1] in name2num: send("Summary:\n" + create_summary(w[1])) else: send("ERROR: name not registered") else: send('ERROR: not in form "!sum [name]"') elif cmd == "!list" or cmd == "!ls": send(create_members()) elif cmd == "!help": send(create_help()) elif cmd in ["!schieb", "!gib", "!zieh", "!nimm"]: if len(w) != 3: send('ERROR: not in form "!cmd amount recipient"') else: if not sender_number in num2name: send('ERROR: you must register first') else: if w[1] in name2num: recipient = w[1] amount = w[2] elif w[2] in name2num: recipient = w[2] amount = w[1] else: send('ERROR: recipient not known') continue sender = num2name[sender_number] try: amount = to_cents(amount) except: send("ERROR: amount musst be a number") continue if amount < 0: send("ERROR: amount must be positiv") continue if cmd in ["!zieh", "!nimm"]: amount *= -1 balance[sender][recipient] -= amount balance[recipient][sender] += amount p_balance = balance[sender][recipient] send("New Balance: {0} {1} {2:.2} {3}\n".format(sender, ("->" if p_balance > 0 else "<-"), to_euro(abs(p_balance)), recipient)) elif cmd == "!tanken": if len(w) < 3: send('ERROR: !tanken not in form "!tanken amount person [info]"') continue try: amount = to_cents(w[1]) except: send("ERROR: amount musst be a number") continue if w[2] in name2num: recipient = w[2] else: recipient = num2name[sender_number] parts, err = tanken.tanken(body[1:], amount) if err != None: send("ERROR: " + err) continue msg = "" for p in parts.items(): msg += p[0] + ": {}km = {:.2}\n".format(p[1][0], p[1][1]) if p[0] != recipient: if p[0] in name2num: balance[recipient][p[0]] -= p[1][1] balance[p[0]][recipient] += p[1][1] else: msg += p[0] + " not known. Please take care manually\n" msg += "New Balance:\n" msg += create_summary(recipient) send(msg) def main(): global group_id group_id = os.environ["GSB_GROUP_ID"] global balance store_path = os.environ["GSB_STORE_PATH"] balance_path = store_path + "/balance.json" registration_path = store_path + "/registration.json" global name2num global num2name global send_cmd send_cmd = os.environ["GSB_SEND_CMD"] if os.path.isfile(balance_path): balance = json.load(open(balance_path, "r")) if os.path.isfile(registration_path): name2num = json.load(open(registration_path, "r")) for name in name2num: num2name[name2num[name]] = name handle_input(sys.stdin.read()) with open(balance_path, "w") as f: json.dump(balance, f) with open(registration_path, "w") as f: json.dump(name2num, f) if __name__ == "__main__": main()