feat: complete roll command
This commit is contained in:
parent
74f991af40
commit
3ab4e424a1
2 changed files with 120 additions and 10 deletions
102
dice.py
102
dice.py
|
|
@ -1,18 +1,40 @@
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
from pyparsing import Combine, OpAssoc, Word, infix_notation, nums, one_of
|
from pyparsing import Combine, OpAssoc, ParseResults, Word, infix_notation, nums, one_of
|
||||||
|
|
||||||
SEPARATOR = "|"
|
SEPARATOR = "|"
|
||||||
|
|
||||||
|
|
||||||
class Dice:
|
class Realizable:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Integer(Realizable):
|
||||||
|
def __init__(self, tokens):
|
||||||
|
self.value = int(tokens[0])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
def realize(self):
|
||||||
|
return [self.value]
|
||||||
|
|
||||||
|
|
||||||
|
class Dice(Realizable):
|
||||||
def __init__(self, tokens):
|
def __init__(self, tokens):
|
||||||
split_tokens = tokens[0].split(SEPARATOR)
|
split_tokens = tokens[0].split(SEPARATOR)
|
||||||
self.dice, self.faces = split_tokens[0], split_tokens[2]
|
self.dice, self.faces = int(split_tokens[0]), int(split_tokens[2])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.dice}d{self.faces}"
|
||||||
|
|
||||||
|
def realize(self):
|
||||||
|
rolls = [randint(1, self.faces) for _ in range(self.dice)]
|
||||||
|
return rolls
|
||||||
|
|
||||||
|
|
||||||
def roll_parse(roll):
|
def roll_parse(roll):
|
||||||
integer = Word(nums)
|
integer = Word(nums).set_parse_action(Integer)
|
||||||
dice = Combine(integer + "d" + integer, join_string="|")
|
dice = Combine(integer + "d" + integer, join_string="|")
|
||||||
dice.set_parse_action(Dice)
|
dice.set_parse_action(Dice)
|
||||||
|
|
||||||
|
|
@ -28,7 +50,75 @@ def roll_parse(roll):
|
||||||
return expr.parse_string(roll, parse_all=True)
|
return expr.parse_string(roll, parse_all=True)
|
||||||
|
|
||||||
|
|
||||||
|
def realize(expr):
|
||||||
|
if isinstance(expr, ParseResults):
|
||||||
|
return list(map(realize, expr))
|
||||||
|
elif isinstance(expr, Realizable):
|
||||||
|
return expr.realize()
|
||||||
|
elif isinstance(expr, str):
|
||||||
|
return expr
|
||||||
|
else:
|
||||||
|
raise ValueError(f"{expr} has an unexpected type in realization")
|
||||||
|
|
||||||
|
|
||||||
|
def pstr_expr(expr):
|
||||||
|
if isinstance(expr, Realizable):
|
||||||
|
return str(expr)
|
||||||
|
elif isinstance(expr, list) and all(map(lambda e: isinstance(e, int), expr)):
|
||||||
|
return "/".join(map(str, expr))
|
||||||
|
elif isinstance(expr, str):
|
||||||
|
return expr
|
||||||
|
elif isinstance(expr, list) and len(expr) == 1:
|
||||||
|
return pstr_expr(expr[0])
|
||||||
|
elif isinstance(expr, ParseResults) or isinstance(expr, list):
|
||||||
|
return " ".join(map(pstr_expr, expr))
|
||||||
|
else:
|
||||||
|
raise ValueError(f"{expr} has an unexpected type in pstr_expr")
|
||||||
|
|
||||||
|
|
||||||
|
def eval_op(op):
|
||||||
|
if op == "+":
|
||||||
|
return int.__add__
|
||||||
|
elif op == "*":
|
||||||
|
return int.__mul__
|
||||||
|
elif op == "-":
|
||||||
|
return int.__sub__
|
||||||
|
elif op == "/":
|
||||||
|
return int.__floordiv__
|
||||||
|
elif op == "%":
|
||||||
|
return int.__mod__
|
||||||
|
raise ValueError(f"Unknown {op} operator in eval_op")
|
||||||
|
|
||||||
|
|
||||||
|
def compute(rexpr):
|
||||||
|
if isinstance(rexpr, Realizable):
|
||||||
|
raise ValueError(f"{rexpr} should already be realized")
|
||||||
|
elif isinstance(rexpr, int):
|
||||||
|
return rexpr
|
||||||
|
elif isinstance(rexpr, list) and all(map(lambda e: isinstance(e, int), rexpr)):
|
||||||
|
return sum(rexpr)
|
||||||
|
elif isinstance(rexpr, list) and len(rexpr) == 1:
|
||||||
|
return compute(rexpr[0])
|
||||||
|
elif isinstance(rexpr, list):
|
||||||
|
if len(rexpr) % 2 == 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"{rexpr} should be an infix expression with an odd number of elements"
|
||||||
|
)
|
||||||
|
total = compute(rexpr[0])
|
||||||
|
for i in range(1, len(rexpr), 2):
|
||||||
|
op = eval_op(rexpr[i])
|
||||||
|
total = op(total, compute(rexpr[i + 1]))
|
||||||
|
return total
|
||||||
|
else:
|
||||||
|
raise ValueError(f"{rexpr} has an unexpected type in compute")
|
||||||
|
|
||||||
|
|
||||||
async def roll(bot, mtch, room, message):
|
async def roll(bot, mtch, room, message):
|
||||||
await bot.api.send_text_message(
|
expr = roll_parse(" ".join(mtch.args()))
|
||||||
room.room_id, str(roll_parse(" ".join(mtch.args())))
|
rexpr = realize(expr)
|
||||||
|
value = compute(rexpr)
|
||||||
|
await bot.api.send_markdown_message(
|
||||||
|
room.room_id,
|
||||||
|
"> " + pstr_expr(rexpr) + "\n\n" + str(value),
|
||||||
|
reply_to=message.event_id,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
28
main.py
28
main.py
|
|
@ -26,21 +26,36 @@ bot.start_time = datetime.now()
|
||||||
async def ping(room, message):
|
async def ping(room, message):
|
||||||
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
||||||
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("ping"):
|
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("ping"):
|
||||||
await misc.ping(bot, mtch, room, message)
|
try:
|
||||||
|
await misc.ping(bot, mtch, room, message)
|
||||||
|
except Exception as e:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id, f"Unexpected error: {e}", reply_to=message.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
async def echo(room, message):
|
async def echo(room, message):
|
||||||
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
||||||
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("echo"):
|
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("echo"):
|
||||||
await misc.echo(bot, mtch, room, message)
|
try:
|
||||||
|
await misc.echo(bot, mtch, room, message)
|
||||||
|
except Exception as e:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id, f"Unexpected error: {e}", reply_to=message.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
async def uptime(room, message):
|
async def uptime(room, message):
|
||||||
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
mtch = matrix.match.MessageMatch(room, message, bot, PREFIX)
|
||||||
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("uptime"):
|
if mtch.is_not_from_this_bot and mtch.prefix and mtch.command("uptime"):
|
||||||
await misc.uptime(bot, mtch, room, message)
|
try:
|
||||||
|
await misc.uptime(bot, mtch, room, message)
|
||||||
|
except Exception as e:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id, f"Unexpected error: {e}", reply_to=message.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
|
|
@ -51,7 +66,12 @@ async def roll(room, message):
|
||||||
and mtch.prefix
|
and mtch.prefix
|
||||||
and (mtch.command("roll") or mtch.command("r"))
|
and (mtch.command("roll") or mtch.command("r"))
|
||||||
):
|
):
|
||||||
await dice.roll(bot, mtch, room, message)
|
try:
|
||||||
|
await dice.roll(bot, mtch, room, message)
|
||||||
|
except Exception as e:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id, f"Unexpected error: {e}", reply_to=message.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
bot.run()
|
bot.run()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue