From 6a1157fb7e78fc1c3ae8189d5d7c74496fe6774a Mon Sep 17 00:00:00 2001 From: Alex Ott <66271487+AlexanderHOtt@users.noreply.github.com> Date: Sat, 31 Dec 2022 14:36:06 -0800 Subject: [PATCH 1/5] merge upstream/main --- discord-bot/bot/extensions/guild_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord-bot/bot/extensions/guild_settings.py b/discord-bot/bot/extensions/guild_settings.py index 1aba9f47..cb419874 100644 --- a/discord-bot/bot/extensions/guild_settings.py +++ b/discord-bot/bot/extensions/guild_settings.py @@ -64,6 +64,7 @@ async def log_channel(ctx: lightbulb.SlashContext) -> None: conn: Connection = ctx.bot.d.db assert ctx.guild_id is not None # `guild_only` check assert isinstance(channel, hikari.PermissibleGuildChannel) + type(channel).mro() # Check if the bot can send messages in that channel assert (me := ctx.bot.get_me()) is not None # non-None after `StartedEvent` From 24530a7cc82cb0661c19a12ba39bfd251fd31ce7 Mon Sep 17 00:00:00 2001 From: Alex Ott <66271487+AlexanderHOtt@users.noreply.github.com> Date: Sat, 31 Dec 2022 16:31:04 -0800 Subject: [PATCH 2/5] update permissions check for guild settings --- discord-bot/bot/extensions/guild_settings.py | 31 +++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/discord-bot/bot/extensions/guild_settings.py b/discord-bot/bot/extensions/guild_settings.py index cb419874..d09407da 100644 --- a/discord-bot/bot/extensions/guild_settings.py +++ b/discord-bot/bot/extensions/guild_settings.py @@ -5,7 +5,7 @@ import lightbulb from aiosqlite import Connection from bot.db.schemas import GuildSettings from bot.utils import mention -from lightbulb.utils.permissions import permissions_in +from lightbulb.utils import permissions_in from loguru import logger plugin = lightbulb.Plugin("GuildSettings") @@ -56,33 +56,42 @@ 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.command("log_channel", "Set the channel that the bot logs task and label completions in.", ephemeral=True) @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 - assert isinstance(channel, hikari.PermissibleGuildChannel) - type(channel).mro() # Check if the bot can send messages in that channel - assert (me := ctx.bot.get_me()) is not None # non-None after `StartedEvent` - if (own_member := ctx.bot.cache.get_member(ctx.guild_id, me.id)) is None: - own_member = await ctx.bot.rest.fetch_member(ctx.guild_id, me.id) - perms = permissions_in(channel, own_member) - if perms & ~hikari.Permissions.SEND_MESSAGES: - await ctx.respond("I don't have permission to send messages in that channel.") + assert isinstance(channel, hikari.InteractionChannel) # Slash commands are interactions + me = ctx.bot.cache.get_me() or await ctx.bot.rest.fetch_my_user() + own_member = ctx.bot.cache.get_member(ctx.guild_id, me.id) or await ctx.bot.rest.fetch_member(ctx.guild_id, me.id) + + # Get the channel from the cache if it is there, otherwise fetch it + if (ch := ctx.bot.cache.get_guild_channel(channel.id)) is None: + ch = {ch.id: ch for ch in await ctx.bot.rest.fetch_guild_channels(channel.id)}[channel.id] + + if not isinstance(ch, hikari.GuildTextChannel): + await ctx.respond(f"{ch.mention} is not a text channel.") + return + + # if the bot's permissions for this channel don't contain SEND_MESSAGE + # This will also filter out categories and voice channels + print(permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES) + if not permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES: + await ctx.respond(f"I don't have permission to send messages in {ch.mention}.") return await ctx.respond(f"Setting `log_channel` to {channel.mention}.") + # update the database 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() logger.info(f"Updated `log_channel` for {ctx.guild_id} to {channel.id}.") From 9dc8aafa406f327ad295c4d5a8f92b17008c177c Mon Sep 17 00:00:00 2001 From: AlexanderHOtt Date: Sun, 1 Jan 2023 03:02:37 -0800 Subject: [PATCH 3/5] add error handler for the bot --- discord-bot/bot/bot.py | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/discord-bot/bot/bot.py b/discord-bot/bot/bot.py index a305946f..b2a2eb25 100644 --- a/discord-bot/bot/bot.py +++ b/discord-bot/bot/bot.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- """Bot logic.""" +from datetime import datetime + import aiosqlite import hikari import lightbulb import miru from bot.api_client import OasstApiClient from bot.settings import Settings +from bot.utils import EMPTY, mention settings = Settings() @@ -38,3 +41,77 @@ async def on_stopping(event: hikari.StoppingEvent): """Cleanup.""" await bot.d.db.close() await bot.d.oasst_api.close() + + +async def _send_error_embed( + content: str, exception: lightbulb.errors.LightbulbError | BaseException, ctx: lightbulb.Context +) -> None: + ctx.command + embed = hikari.Embed( + title=f"`{exception.__class__.__name__}` Error{f' in `{ctx.command.name}`' if ctx.command else '' }", + description=content, + color=0xFF0000, + timestamp=datetime.now().astimezone(), + ).set_author(name=ctx.author.username, url=str(ctx.author.avatar_url)) + + await ctx.respond(EMPTY, embed=embed) + + +@bot.listen(lightbulb.CommandErrorEvent) +async def on_error(event: lightbulb.CommandErrorEvent) -> None: + """Error handler for the bot.""" + # Unwrap the exception to get the original cause + exc = event.exception.__cause__ or event.exception + ctx = event.context + + if isinstance(event.exception, lightbulb.CommandInvocationError): + if not event.context.command: + await _send_error_embed("Something went wrong", exc, ctx) + else: + await _send_error_embed( + f"Something went wrong during invocation of command `{event.context.command.name}`.", exc, ctx + ) + + raise event.exception + + # Not an owner + if isinstance(exc, lightbulb.NotOwner): + await _send_error_embed("You are not the owner of this bot.", exc, ctx) + # Command is on cooldown + elif isinstance(exc, lightbulb.CommandIsOnCooldown): + await _send_error_embed(f"This command is on cooldown. Retry in `{exc.retry_after:.2f}` seconds.", exc, ctx) + # Missing permissions + elif isinstance(exc, lightbulb.errors.MissingRequiredPermission): + await _send_error_embed( + f"You do not have permission to use this command. Missing permissions: {exc.missing_perms}", exc, ctx + ) + # Missing roles + elif isinstance(exc, lightbulb.errors.MissingRequiredRole): + assert event.context.guild_id is not None # Roles only exist in guilds + await _send_error_embed( + f"You do not have the correct role to use this command. Missing role(s): {[mention(r, 'role') for r in exc.missing_roles]}", + exc, + ctx, + ) + # Only a guild command + elif isinstance(exc, lightbulb.errors.OnlyInGuild): + await _send_error_embed("This command can only be run in servers.", exc, ctx) + # Only a DM command + elif isinstance(exc, lightbulb.errors.OnlyInDM): + await _send_error_embed("This command can only be run in DMs.", exc, ctx) + # Not enough arguments + elif isinstance(exc, lightbulb.errors.NotEnoughArguments): + await _send_error_embed( + f"Not enough arguments were supplied to the command. {[opt.name for opt in exc.missing_options]}", exc, ctx + ) + # Bot missing permission + elif isinstance(exc, lightbulb.errors.BotMissingRequiredPermission): + await _send_error_embed( + f"The bot does not have the correct permission(s) to execute this command. Missing permissions: {exc.missing_perms}", + exc, + ctx, + ) + elif isinstance(exc, lightbulb.errors.MissingRequiredAttachment): + await _send_error_embed("Not enough attachemnts were supplied to this command.", exc, ctx) + else: + raise exc From 8d9f3c80afabf15d9c54cd57db22e7a37eb2ec9b Mon Sep 17 00:00:00 2001 From: Alex Ott <66271487+AlexanderHOtt@users.noreply.github.com> Date: Sun, 1 Jan 2023 17:00:24 -0800 Subject: [PATCH 4/5] update readme with a better getting started guide --- discord-bot/README.md | 47 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/discord-bot/README.md b/discord-bot/README.md index 1ff47c31..052c8b43 100644 --- a/discord-bot/README.md +++ b/discord-bot/README.md @@ -10,16 +10,56 @@ To add the official Open-Assistant data collection bot to your discord server [c If you are unfamiliar with `hikari`, `lightbulb`, or `miru`, please refer to the [large list of examples](https://gist.github.com/AlexanderHOtt/7805843a7120f755938a3b75d680d2e7) -### Setup +### Bot Setup + +1. Create a new discord application at the [Discord Developer Portal](https://discord.com/developers/applications) + +1. Go to the "Bot" tab and create a new bot + +1. Scroll down to "Privileged Gateway Intents" and enable the following options: + + - Server Members Intent + - Presence Intent + - Message Content Intent + +This page also contains the bot token, which you will need to add to the `.env` file later. + +2. Go to the "OAuth2" tab scroll to "Default Authorization Link" + +3. Set "AUTHORIZATION METHOD" to "In-app Authorization" + +4. Select the "bot" and "applications.commands" scopes + +5. For testing and local development, it's easiest to set "BOT PERMISSIONS" to "Administrator" + +Remember to save your changes. + +6. Copy the "CLIENT ID" from the top of the page and replace it in the link below to invite your bot. + +``` +https://discord.com/oauth2/authorize?client_id=YOUR_ID_HERE&permissions=8&scope=bot%20applications.commands +``` + +### Environment Setup To run the bot ```bash +# in the base open-assist folder +cd oasst-shared +pip install -e . + +cd ../discord-bot + cp .env.example .env +# edit .env and add your bot token and other values + python -V # 3.10 pip install -r requirements.txt + +# in the discord-bot folder python -m bot ``` @@ -38,11 +78,6 @@ git add . git commit -m "" ``` -To test the bot on your own discord server you need to register a discord application at the [Discord Developer Portal](https://discord.com/developers/applications) and get at bot token. - -1. Follow a tutorial on how to get a bot token, for example this one: [Creating a discord bot & getting a token](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token) -2. The bot script expects the bot token to be in the `.env` file under the `TOKEN` variable. - ### Resources #### Structure From 8e831f897fb065bcb645bba9fe6a9d4ed4c3893d Mon Sep 17 00:00:00 2001 From: Alex Ott <66271487+AlexanderHOtt@users.noreply.github.com> Date: Sun, 1 Jan 2023 17:06:54 -0800 Subject: [PATCH 5/5] fix invite link --- discord-bot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord-bot/README.md b/discord-bot/README.md index 05723895..1ad99a71 100644 --- a/discord-bot/README.md +++ b/discord-bot/README.md @@ -9,7 +9,7 @@ the bot's outputs. If you want to learn more about RLHF please refer ## Invite official bot To add the official Open-Assistant data collection bot to your discord server -[click here](https://discord.com/api/oauth2/authorize?client_id=1054078345542910022&permissions=1634235579456&scope=bot). +[click here](https://discord.com/api/oauth2/authorize?client_id=1054078345542910022&permissions=1634235579456&scope=bot%20applications.commands). The bot needs access to read the contents of user text messages. ## Contributing