diff --git a/discord-bot/bot/__main__.py b/discord-bot/bot/__main__.py index 45820f7d..73f8e6b1 100644 --- a/discord-bot/bot/__main__.py +++ b/discord-bot/bot/__main__.py @@ -3,6 +3,7 @@ import logging import os from bot.bot import bot +from hikari.presences import Activity, ActivityType, Status logger = logging.getLogger(__name__) @@ -13,4 +14,11 @@ if __name__ == "__main__": uvloop.install() logger.info("Starting bot") - bot.run() + bot.run( + check_for_updates=True, + activity=Activity( + name="/help", + type=ActivityType.PLAYING, + ), + status=Status.ONLINE, + ) diff --git a/discord-bot/bot/bot.py b/discord-bot/bot/bot.py index 8c604e1a..870f445a 100644 --- a/discord-bot/bot/bot.py +++ b/discord-bot/bot/bot.py @@ -19,6 +19,7 @@ bot = lightbulb.BotApp( default_enabled_guilds=settings.declare_global_commands, owner_ids=settings.owner_ids, intents=hikari.Intents.ALL, + help_class=None, ) @@ -28,10 +29,12 @@ async def on_starting(event: hikari.StartingEvent): miru.install(bot) # component handler bot.load_extensions_from("./bot/extensions") # load extensions + # Database setup bot.d.db = await aiosqlite.connect("./bot/db/database.db") await bot.d.db.executescript(open("./bot/db/schema.sql").read()) await bot.d.db.commit() + # OASST API setup bot.d.oasst_api = OasstApiClient(settings.oasst_api_url, settings.oasst_api_key) # A `dict[hikari.Message | None, UUID | None]]` that maps user IDs to (task msg ID, task UUIDs). diff --git a/discord-bot/bot/extensions/help.py b/discord-bot/bot/extensions/help.py new file mode 100644 index 00000000..b7da3868 --- /dev/null +++ b/discord-bot/bot/extensions/help.py @@ -0,0 +1,43 @@ +"""Custom help command.""" +import lightbulb +from bot.messages import help_message, tutorial_message +from bot.settings import Settings +from hikari.permissions import Permissions +from lightbulb.utils import permissions_for + +plugin = lightbulb.Plugin("HelpPlugin") + +settings = Settings() + + +@plugin.command +@lightbulb.command("help", "Help for the bot.", ephemeral=True) +@lightbulb.implements(lightbulb.SlashCommand, lightbulb.PrefixCommand) +async def help_command(ctx: lightbulb.Context) -> None: + """Help for the bot.""" + can_manage_guild = False + if ctx.guild_id: + member = ctx.bot.cache.get_member(ctx.guild_id, ctx.author.id) or await ctx.bot.rest.fetch_member( + ctx.guild_id, ctx.author.id + ) + can_manage_guild = bool(permissions_for(member) & Permissions.MANAGE_GUILD) + + await ctx.respond(help_message(can_manage_guild, ctx.author.id in settings.owner_ids)) + + +@plugin.command +@lightbulb.command("tutorial", "A tutorial for completing tasks.", ephemeral=True) +@lightbulb.implements(lightbulb.SlashCommand, lightbulb.PrefixCommand) +async def tutorial(ctx: lightbulb.Context) -> None: + """Help for the bot.""" + await ctx.respond(tutorial_message(True, True)) + + +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/messages.py b/discord-bot/bot/messages.py index c1a6d355..7bd57bb9 100644 --- a/discord-bot/bot/messages.py +++ b/discord-bot/bot/messages.py @@ -73,6 +73,10 @@ def _hint(hint: str | None) -> str: return f"{NL}Hint: {hint}" if hint else "" +def _li(text: str) -> str: + return f":small_blue_diamond: {text}" + + ### # Messages ### @@ -222,6 +226,60 @@ def confirm_ranking_response_message(content: str, items: list[str]) -> str: """ +def help_message(can_manage_guild: bool, is_dev: bool) -> str: + """The /help command message.""" + content = f"""\ +{_h1("HELP")} + +{_li("**`/help`**")} +Show this message. + +{_li("**`/work [type]`**")} +Start a new task. +**`[type]`**: +The type of task to start. If not provided, a random task will be selected. The different types are +:small_orange_diamond: `random`: A random task type +:small_orange_diamond: ~~`summarize_story`~~ (coming soon) +:small_orange_diamond: ~~`rate_summary`~~ (coming soon) +:small_orange_diamond: `initial_prompt`: Ask the assistant something +:small_orange_diamond: `prompter_reply`: Reply to the assistant +:small_orange_diamond: `assistant_reply`: Reply to the user +:small_orange_diamond: `rank_initial_prompts`: Rank some initial prompts +:small_orange_diamond: `rank_prompter_replies`: Rank some prompter replies +:small_orange_diamond: `rank_assistant_replies`: Rank some assistant replies + +To learn how to complete tasks, run `/tutorial`. +""" + if can_manage_guild: + content += f"""\ + +{_li("**`/settings log_channel `**")} +Set the channel that the bot logs completed task messages in. +**``**: The channel to log completed tasks in. The bot needs to be able to send messages in this channel. + +{_li("**`/settings get`**")} +Get the current settings. +""" + if is_dev: + content += f"""\ + +{_li("**`/reload [plugin]`**")} +Hot-reload a plugin. Only code *inside* of function bodies will be updated. +Any changes to __function signatures__, __other files__, __decorators__, or __imports__ will require a restart. +**`[plugin]`**: +The plugin to hot-reload. If no plugin is provided, all plugins are hot-reload. +""" + return content + + +def tutorial_message() -> str: + """The /tutorial command message.""" + # TODO: Finish message + return f"""\ +{_h1("TUTORIAL")} +""" + + def confirm_label_response_message(content: str) -> str: user_labels = content.lower().replace(" ", "").split(",") user_labels_str = ", ".join(user_labels)