#!/usr/bin/env python3 import json import os import subprocess import sys """Dict of dicts associating a second person to an amount""" balance = {} store_path = "." name2num = {} num2name = {} registration_path = "reg.json" group_id = "GELD!!!" send_cmd = "signal-cli -u irgendwer send -g irgendwem" 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" + ("<-" if amount < 0 else "->") summary += " " + person + " " + str(abs(amount)) + "\n" if summary == "": summary = "\tAll fine :)\n" else: summary += "\tBalance: " + str(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 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:]): if line[0:6] == "Body: ": body = line[6:] body_found = True # Only one line bodies are supported elif body_found: not_in_group = lines[idx+1].strip() == "Id: " + group_id break if not_in_group: continue # strip of '\n' w = body.split(' ') # supported commands are: # "!reg" register a name for this number # "!sum" send a summary to the group # "!schieb" give money to somebody # "!zieh" add debt of somebody # "!list" "!ls" list members # "!help" print all commands if w[0] == "!reg": if len(w) != 2: send('ERROR: not in form "!reg name"') else: w[1] = w[1].lower() 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 w[0] == "!sum": if len(w) == 1: send(create_total_summary()) elif len(w) == 2: w[1] = w[1].lower() 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 w[0] == "!schieb" or w[0] == "!zieh": if len(w) != 3: send('ERROR: not in form "!schieb amount recipient"') else: w[2] = w[2].lower() if not sender_number in num2name: send('ERROR: you must register first') elif not w[2] in name2num: send('ERROR: recipient not known') else: sender = num2name[sender_number] try: amount = float(w[1]) except: send("ERROR: amount musst be a number") continue if amount < 0: send("ERROR: amount must be positiv") continue if w[0] == "!zieh": amount *= -1 balance[sender][w[2]] -= amount balance[w[2]][sender] += amount p_balance = balance[sender][w[2]] send("New Balance: {0} {1} {2} {3}\n".format(sender, ("->" if p_balance > 0 else "<-"), abs(p_balance), w[2])) elif w[0] == "!list" or w[0] == "!ls": send(create_members()) elif w[0] == "!help": send(create_help()) 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()