diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8021230 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Dev +.mypy_cache/ +.python-version +poetry.lock + +# Python +__pycache__/ + +# Secrets and static files +.env +store/ +attachments/ \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ed50523 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "tomservobot" +version = "0.1.0" +description = "" +authors = ["jodhus "] +readme = "README.md" +packages = [{include = "tomservobot", from = "src"}] + +[tool.poetry.dependencies] +python = ">=3.12,<3.13" +nio-bot = {extras = ["cli", "e2ee"], version = "^1.1.0.post3"} +python-decouple = "^3.8" + +[tool.poetry.group.dev.dependencies] +pylint = "^3.2.3" +mypy = "^1.10.0" +black = "^24.4.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..32d3e13 --- /dev/null +++ b/src/main.py @@ -0,0 +1,42 @@ +import logging + +from decouple import config +from niobot import Context, NioBot, SyncResponse + + +DEBUG = config("DEBUG", default=False, cast=bool) + +if DEBUG: + logging.basicConfig(level=logging.DEBUG) +else: + logging.basicConfig(level=logging.INFO) + + +bot = NioBot( + homeserver=config("HOMESERVER", cast=str), + user_id=config("USER_ID", cast=str), + device_id=config("DEVICE_ID", default="deez-nutz", cast=str), + store_path=config("STORE_PATH", default="./store", cast=str), + command_prefix="!", + case_insensitive=True, + owner_id=config("OWNER_ID", cast=str), +) + +bot.mount_module("tomservobot.chance") +bot.mount_module("tomservobot.reactions") + + +@bot.on_event("ready") +async def on_ready(_: SyncResponse): + """Triggers when the bot is completely loaded.""" + print("Bot is ready!") + + +@bot.on_event("command_error") +async def on_command_error(_: Context, error: Exception): + """Triggers when an error occurs in a command so we can handle it.""" + print(f"Error: {error}") + + +if __name__ == "__main__": + bot.run(access_token=config("ACCESS_TOKEN", cast=str)) diff --git a/src/tomservobot/__init__.py b/src/tomservobot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tomservobot/chance.py b/src/tomservobot/chance.py new file mode 100644 index 0000000..5f7f640 --- /dev/null +++ b/src/tomservobot/chance.py @@ -0,0 +1,45 @@ +from random import randint +import re + +import niobot +from niobot import ( + CommandParserError, + Context, + Module, + NioBot, +) + + +class ChanceModule(Module): + """Commands that simulate chance (Dice rolls, card selection, etc).""" + + def __init__(self, bot: NioBot): + self.bot = bot + + @niobot.command() + async def roll(self, ctx: Context, dice: str): + """Roll some dice with an optional modifier.""" + valid = re.match(r"(\d+)d(\d+)([\+-/\*]\d+)?", dice) + if valid is None: + await ctx.respond("Error: Invalid dice roll notation.") + raise CommandParserError("Invalid dice roll notation.") + dice_parts = (int(valid.group(1)), int(valid.group(2)), valid.group(3)) + + if not 1 <= dice_parts[0] <= 100: + await ctx.respond("Error: Can only roll between 1 and 100 dice.") + raise CommandParserError("Can only roll between 1 and 100 dice.") + + if not 2 <= dice_parts[1] <= 100: + await ctx.respond("Error: Dice must have between 2 and 100 faces.") + raise CommandParserError("Dice must have between 2 and 100 faces.") + + rolls: list[int] = [] + for _ in range(dice_parts[0]): + rolls.append(randint(1, dice_parts[1])) + total = sum(rolls) + + await ctx.respond(f"{dice}: {total} ({rolls})") + + # await ctx.client.send_message( + # ctx.room, f"{dice_parts[0]}, {dice_parts[1]}, {dice_parts[2]}" + # ) diff --git a/src/tomservobot/reactions.py b/src/tomservobot/reactions.py new file mode 100644 index 0000000..7123492 --- /dev/null +++ b/src/tomservobot/reactions.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from decouple import config +import niobot + + +ATTACH_PATH = Path(config("ATTACH_PATH", default="./attachments/", cast=str)) + + +class ReactionsModule(niobot.Module): + """Specifically for reactions/replies to certain comments/events.""" + + def __init__(self, bot: niobot.NioBot): + self.bot = bot + + @niobot.command() + async def weekend(self, ctx: niobot.Context): + """Ladies and gentlemen--the weekend. . .""" + print(ATTACH_PATH / "weekend.mp4") + attachment = await niobot.VideoAttachment.from_file(ATTACH_PATH / "weekend.mp4") + await ctx.client.send_message(ctx.room, None, attachment) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29