aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--geldschieberbot.py80
-rwxr-xr-xtest.py96
2 files changed, 175 insertions, 1 deletions
diff --git a/geldschieberbot.py b/geldschieberbot.py
index 0a912c3..79cb643 100644
--- a/geldschieberbot.py
+++ b/geldschieberbot.py
@@ -69,6 +69,7 @@ class Geldschieberbot:
"cars": {},
"scheduled_cmds": {},
"changes": {},
+ "aliases": {},
}
self.balance = self.state["balance"]
@@ -77,6 +78,7 @@ class Geldschieberbot:
self.available_cars = self.state["cars"]
self.scheduled_cmds = self.state["scheduled_cmds"]
self.changes = self.state["changes"]
+ self.aliases = self.state.setdefault("aliases", {})
def save_state(self, state_path=STATE_FILE):
"""Load state from disk"""
@@ -168,6 +170,33 @@ class Geldschieberbot:
for m in self.balance:
del self.balance[m][name]
+ def expand_aliases(self,
+ users: list[str],
+ exclude_users=None) -> list[str]:
+ """Expand any alias
+
+ Any user name in exclude_users will not be expanded. This is usefull
+ when using aliases with commands implicitly adding a user like !split.
+ """
+ ret = []
+ for user in users:
+ if not user in self.aliases and user != 'all':
+ ret.append(user)
+ continue
+
+ expanded_users = []
+ if user in self.aliases:
+ expanded_users += self.aliases[user]
+ else:
+ expanded_users += list(self.name2num.keys())
+
+ ret.extend([
+ u for u in expanded_users
+ if not exclude_users or u not in exclude_users
+ ])
+
+ return ret
+
@classmethod
def create_help(cls) -> str:
return """
@@ -179,6 +208,10 @@ class Geldschieberbot:
sum [name] - print summary of specific users
full-sum - print summary of all users
+ alias - list registered aliases
+ alias name person [persons] - create an alias for one or multiple persons
+ alias remove name - remove an alias
+
split amount person [persons] - split amount between the sender and persons
teil amount person [persons] - split amount between the sender and persons
@@ -247,7 +280,7 @@ class Geldschieberbot:
return {'msg': f'Summary:\n{self.create_summary(name)}'}
msg = "Summary:\n"
- for name in args[1:]:
+ for name in self.expand_aliases(args[1:]):
if name in self.name2num or name in self.available_cars:
msg += self.create_summary(name) + "\n"
else:
@@ -293,6 +326,8 @@ class Geldschieberbot:
return {'err': 'amount must be a positive number'}
recipient = self.num2name[sender]
+ # exclude the implicit recipient from alias expension
+ persons = self.expand_aliases(persons, exclude_users=[recipient])
# persons + sender
npersons = len(persons) + 1
amount_per_person = int(amount / npersons)
@@ -821,6 +856,48 @@ class Geldschieberbot:
return {'msg': msg}
+ def alias(self, sender, args, msg) -> dict[str, str]: # pylint: disable=unused-argument
+ """List or create aliases"""
+
+ if len(args) == 1:
+ msg = ''
+ for alias, users in self.aliases.items():
+ msg += f'\n\t{alias}: {" ".join(users)}'
+ return {'msg': f'Aliases:\n\tall: {" ".join(self.name2num)}{msg}'}
+
+ if len(args) == 2:
+ return {'err': 'Not a valid alias command'}
+
+ if args[1] == 'remove':
+ alias = args[2]
+ if alias not in self.aliases:
+ return {'err': f'Alias "{alias}" not registered'}
+
+ del self.aliases[alias]
+ return {'msg': f'Alias "{alias}" removed'}
+
+ # create a new alias
+ alias = args[1]
+ if alias in self.aliases or alias == 'all':
+ return {'err': f'Alias "{alias}" is already registered'}
+
+ if alias in self.name2num:
+ return {'err': f'A user "{alias}" is already registered'}
+
+ try:
+ to_cent(alias)
+ return {'err': 'Pure numerical aliases are not allowed'}
+ except (ValueError, TypeError):
+ pass
+
+ users = args[2:]
+ for user in users:
+ if user not in self.name2num:
+ return {'err': f'User {user} is not registered'}
+
+ self.aliases[alias] = users
+ return {'msg': f'New alias "{alias}" registered'}
+
def __init__(self, dry_run=False):
self.dry_run = dry_run # Run without changing the stored state
self.load_state()
@@ -831,6 +908,7 @@ class Geldschieberbot:
self.cmds = {
'reg': self.register,
'register': self.register,
+ 'alias': self.alias,
'sum': self.summary,
'summary': self.summary,
'full-sum': self.full_summary,
diff --git a/test.py b/test.py
index 3cf5c8d..d279e0b 100755
--- a/test.py
+++ b/test.py
@@ -419,6 +419,102 @@ alice:
self.assertEqual(res.stdout, msg)
+class TestAliasAdd(unittest.TestCase):
+
+ def setUp(self):
+ reset_state("test/state_3users.json")
+
+ def test_add_success(self):
+ res = run_bot(self, num[alice], f"!alias foo {alice} {bob}")
+ self.assertEqual(res.stdout, 'New alias "foo" registered')
+
+ res = run_bot(self, num[alice], f"!alias bar {charlie}")
+ self.assertEqual(res.stdout, 'New alias "bar" registered')
+
+ def test_add_invalid_aliases(self):
+ res = run_bot(self, num[alice], f"!alias foo {alice} {bob}")
+ self.assertEqual(res.stdout, 'New alias "foo" registered')
+ res = run_bot(self, num[alice], f"!alias foo {alice} {bob}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Alias "foo" is already registered')
+
+ res = run_bot(self, num[alice], f"!alias {alice} {alice} {bob}")
+ self.assertEqual(res.stdout,
+ f'ERROR: A user "{alice}" is already registered')
+
+ res = run_bot(self, num[alice], f"!alias 13.23 {charlie}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Pure numerical aliases are not allowed')
+
+ res = run_bot(self, num[alice], "!alias bar ingo")
+ self.assertEqual(res.stdout, 'ERROR: User ingo is not registered')
+
+ res = run_bot(self, num[alice], f"!alias all {alice}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Alias "all" is already registered')
+
+
+class TestAlias(unittest.TestCase):
+
+ def setUp(self):
+ reset_state("test/state_3users.json")
+
+ def test_list_aliases_empty_state(self):
+ res = run_bot(self, num[alice], "!alias")
+ self.assertEqual(res.stdout, f"""Aliases:
+\tall: {alice} {bob} {charlie}""")
+
+ def test_list_aliases(self):
+ run_bot(self, num[alice], f"!alias alob {alice} {bob}")
+ res = run_bot(self, num[alice], "!alias")
+ self.assertEqual(
+ res.stdout, f"""Aliases:
+\tall: {alice} {bob} {charlie}
+\talob: {alice} {bob}""")
+
+ def test_add_invalid_aliases(self):
+ run_bot(self, num[alice], f"!alias foo {alice} {bob}")
+ res = run_bot(self, num[alice], f"!alias foo {alice} {bob}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Alias "foo" is already registered')
+
+ res = run_bot(self, num[alice], f"!alias {alice} {alice} {bob}")
+ self.assertEqual(res.stdout,
+ f'ERROR: A user "{alice}" is already registered')
+
+ res = run_bot(self, num[alice], f"!alias 13.23 {charlie}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Pure numerical aliases are not allowed')
+
+ res = run_bot(self, num[alice], "!alias bar ingo")
+ self.assertEqual(res.stdout, 'ERROR: User ingo is not registered')
+
+ res = run_bot(self, num[alice], f"!alias all {alice}")
+ self.assertEqual(res.stdout,
+ 'ERROR: Alias "all" is already registered')
+
+ def test_split_all(self):
+ res = run_bot(self, num[alice], "!split 9 all")
+ self.assertEqual(res.stdout, \
+"""Split 9.00 between 3 -> 3.00 each
+New Balance:
+alice:
+\t<- bob 3.00
+\t<- charlie 3.00
+\tBalance: 6.00""")
+
+ def test_split_alias(self):
+ run_bot(self, num[alice], f"!alias alob {alice} {bob}")
+ res = run_bot(self, num[charlie], "!split 9 alob")
+ self.assertEqual(res.stdout, \
+"""Split 9.00 between 3 -> 3.00 each
+New Balance:
+charlie:
+\t<- alice 3.00
+\t<- bob 3.00
+\tBalance: 6.00""")
+
+
class TestCarsAdd(unittest.TestCase):
def setUp(self):