From e4b097edff5caa986760ee91284902139835ee07 Mon Sep 17 00:00:00 2001 From: Alex Ott <66271487+AlexanderHOtt@users.noreply.github.com> Date: Fri, 30 Dec 2022 02:59:52 -0800 Subject: [PATCH] add database schema and guild setting --- discord-bot/bot/bot.py | 2 +- discord-bot/bot/db/schemas.py | 16 ++++ discord-bot/bot/extensions/guild_settings.py | 83 ++++++++++++++++++++ discord-bot/bot/extensions/text_labels.py | 8 +- discord-bot/bot/utils.py | 18 +++++ 5 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 discord-bot/bot/db/schemas.py create mode 100644 discord-bot/bot/extensions/guild_settings.py diff --git a/discord-bot/bot/bot.py b/discord-bot/bot/bot.py index e189b765..9b443986 100644 --- a/discord-bot/bot/bot.py +++ b/discord-bot/bot/bot.py @@ -26,7 +26,7 @@ async def on_starting(event: hikari.StartingEvent): miru.install(bot) # component handler bot.load_extensions_from("./bot/extensions") # load extensions - bot.d.db = await aiosqlite.connect(":memory:") # TODO: Update + bot.d.db = await aiosqlite.connect("./bot/db/database.db") # TODO: Update await bot.d.db.executescript(open("./bot/db/schema.sql").read()) await bot.d.db.commit() diff --git a/discord-bot/bot/db/schemas.py b/discord-bot/bot/db/schemas.py new file mode 100644 index 00000000..e3ce9032 --- /dev/null +++ b/discord-bot/bot/db/schemas.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +"""Database schemas.""" +from aiosqlite import Row +from pydantic import BaseModel + + +class GuildSettings(BaseModel): + """Guild settings.""" + + guild_id: int + log_channel_id: int | None + + @classmethod + def parse_obj(cls, obj: Row) -> "GuildSettings": + """Deserialize a Row object from aiosqlite into a GuildSettings object.""" + return cls(guild_id=obj[0], log_channel_id=obj[1]) diff --git a/discord-bot/bot/extensions/guild_settings.py b/discord-bot/bot/extensions/guild_settings.py new file mode 100644 index 00000000..8c9cded4 --- /dev/null +++ b/discord-bot/bot/extensions/guild_settings.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +"""Guild settings.""" +import hikari +import lightbulb +from aiosqlite import Connection + +from bot.db.schemas import GuildSettings +from bot.utils import mention + +plugin = lightbulb.Plugin("GuildSettings") +plugin.add_checks(lightbulb.guild_only) +plugin.add_checks(lightbulb.has_guild_permissions(hikari.Permissions.MANAGE_GUILD)) + + +@plugin.command +@lightbulb.command("settings", "Bot settings for the server.") +@lightbulb.implements(lightbulb.SlashCommandGroup) +async def settings(_: lightbulb.SlashContext) -> None: + """Bot settings for the server.""" + # This will never execute because it is a group + pass + + +@settings.child +@lightbulb.command("get", "Get all the guild settings.") +@lightbulb.implements(lightbulb.SlashSubCommand) +async def get(ctx: lightbulb.SlashContext) -> None: + """Get one of or all the guild settings.""" + conn: Connection = ctx.bot.d.db + assert ctx.guild_id is not None # `guild_only` check + + async with conn.cursor() as cursor: + # Get all settings + await cursor.execute("SELECT * FROM guild_settings WHERE guild_id = ?", (ctx.guild_id,)) + row = await cursor.fetchone() + + if row is None: + await ctx.respond("No settings found for this guild.") + return + + guild_settings = GuildSettings.parse_obj(row) + + # Respond with all + # TODO: Embed + await ctx.respond( + f"""\ +**Guild Settings** +`log_channel`: { +mention(guild_settings.log_channel_id, "channel") +if guild_settings.log_channel_id else 'not set'} +""" + ) + + +@settings.child +@lightbulb.option("channel", "The channel to use.", hikari.TextableGuildChannel) +@lightbulb.command("log_channel", "Set the channel that the bot logs task and label completions in.") +@lightbulb.implements(lightbulb.SlashSubCommand) +async def log_channel(ctx: lightbulb.SlashContext) -> None: + """Set the channel that the bot logs task and label completions in.""" + channel: hikari.TextableGuildChannel = ctx.options.channel + conn: Connection = ctx.bot.d.db + assert ctx.guild_id is not None # `guild_only` check + + await ctx.respond(f"Setting `log_channel` to {channel.mention}.") + + async with conn.cursor() as cursor: + await cursor.execute( + "INSERT OR REPLACE INTO guild_settings (guild_id, log_channel_id) VALUES (?, ?)", + (ctx.guild_id, channel.id), + ) + + await conn.commit() + + +def load(bot: lightbulb.BotApp): + """Add the plugin to the bot.""" + bot.add_plugin(plugin) + + +def unload(bot: lightbulb.BotApp): + """Remove the plugin to the bot.""" + bot.remove_plugin(plugin) diff --git a/discord-bot/bot/extensions/text_labels.py b/discord-bot/bot/extensions/text_labels.py index ab7d109d..e9d08c86 100644 --- a/discord-bot/bot/extensions/text_labels.py +++ b/discord-bot/bot/extensions/text_labels.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- """Hot reload plugin.""" +import typing as t +from datetime import datetime + import hikari import lightbulb import miru -from datetime import datetime - -import typing as t plugin = lightbulb.Plugin( "HotReloadPlugin", @@ -59,7 +59,7 @@ class LabelModal(miru.Modal): .add_field("Total Labeled Message", "0", inline=True) .add_field("Server Ranking", "0/0", inline=True) .add_field("Global Ranking", "0/0", inline=True) - .set_footer(f"Message ID: TODO") + .set_footer("Message ID: TODO") ) channel = await context.bot.rest.fetch_channel(1058299131115872297) assert isinstance(channel, hikari.TextableChannel) diff --git a/discord-bot/bot/utils.py b/discord-bot/bot/utils.py index 1ce99560..03dfea3d 100644 --- a/discord-bot/bot/utils.py +++ b/discord-bot/bot/utils.py @@ -3,6 +3,8 @@ import typing as t from datetime import datetime +import hikari + def format_time(dt: datetime, fmt: t.Literal["t", "T", "D", "f", "F", "R"]) -> str: """Format a datetime object into the discord time format. @@ -28,3 +30,19 @@ EMPTY = "\u200d" This appears as an empty message in Discord. """ + + +def mention( + id: hikari.Snowflakeish, + type: t.Literal["channel", "role", "user"], +) -> str: + """Mention an object.""" + match type: + case "channel": + return f"<#{id}>" + + case "user": + return f"<@{id}>" + + case "role": + return f"<@&{id}>"