""" Classes used to handle bot events and errors. *Classes* --------- EventHandler ErrorHandler """ import traceback # Used to get the traceback of errors import sys # Used to get traceback when the specific error is not # available import discord # Used to init discord.Game and discord.Status, as well # as compare errors to discord errors and as typehints from discord.ext import commands # Used to compare errors with command # errors from discord_slash.context import SlashContext from gwendolyn.utils.util_functions import emoji_to_command class EventHandler(): """ Handles bot events. *Methods* --------- on_ready() on_slash_command(ctx: discord_slash.context.SlashContext) on_reaction_add(ctx: discord_slash.context.SlashContext) """ def __init__(self, bot): """Initialize the handler.""" self.bot = bot async def on_ready(self): """Log and sets status when it logs in.""" await self.bot.database_funcs.imdb_commands() name = self.bot.user.name userid = str(self.bot.user.id) logged_in_message = f"Logged in as {name}, {userid}" self.bot.log(logged_in_message, level=25) game = discord.Game("Use /help for commands") online_status = discord.Status.online await self.bot.change_presence(activity=game, status=online_status) async def on_slash_command(self, ctx: SlashContext): """Log when a slash command is given.""" if ctx.subcommand_name is not None: subcommand = f" {ctx.subcommand_name} " else: subcommand = " " if ctx.subcommand_group is not None: sub_command_group = f"{ctx.subcommand_group} " else: sub_command_group = "" args = " ".join([str(i) for i in ctx.args]) full_command = f"/{ctx.command}{subcommand}{sub_command_group}{args}" log_message = f"{ctx.author.display_name} ran {full_command}" self.bot.log(log_message, str(ctx.channel_id), level=25) async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User): """Take action if the reaction is on a command message.""" if not user.bot: tests = self.bot.database_funcs message = reaction.message channel = message.channel reacted_message = f"{user.display_name} reacted to a message" self.bot.log(reacted_message, str(channel.id)) plex_data = tests.plex_reaction_test(message) # plex_data is a list containing 3 elements: whether it was # the add_show/add_movie command message the reaction was to # (bool), whether it's a movie (bool) (if false, it's a # show), and the imdb ids/names for the for the movies or # shows listed in the message (list). reaction_test_parameters = [message, f"#{str(user.id)}"] if tests.connect_four_reaction_test(*reaction_test_parameters): column = emoji_to_command(reaction.emoji) params = [message, f"#{user.id}", column-1] await self.bot.games.connect_four.place_piece(*params) if plex_data[0]: plex_functions = self.bot.other.plex if plex_data[1]: movie_pick = emoji_to_command(reaction.emoji) if movie_pick == "none": imdb_id = None else: imdb_id = plex_data[2][movie_pick-1] if isinstance(channel, discord.DMChannel): await message.delete() await plex_functions.add_movie(message, imdb_id, False) else: await message.clear_reactions() await plex_functions.add_movie(message, imdb_id) else: show_pick = emoji_to_command(reaction.emoji) if show_pick == "none": imdb_name = None else: imdb_name = plex_data[2][show_pick-1] if isinstance(channel, discord.DMChannel): await message.delete() await plex_functions.add_show(message, imdb_name, False) else: await message.clear_reactions() await plex_functions.add_show(message, imdb_name) elif tests.hangman_reaction_test(*reaction_test_parameters): self.bot.log("They reacted to the hangman message") if ord(reaction.emoji) in range(127462, 127488): # The range is letter-emojis guess = chr(ord(reaction.emoji)-127397) # Converts emoji to letter params = [message, f"#{user.id}", guess] await self.bot.games.hangman.guess(*params) else: self.bot.log("Bot they didn't react with a valid guess") class ErrorHandler(): """ Handles errors. *Methods* --------- on_slash_command_error(ctx: discord_slash.context.SlashContext, error: Exception) on_error(method: str) """ def __init__(self, bot): """Initialize the handler.""" self.bot = bot async def on_slash_command_error(self, ctx: SlashContext, error: Exception): """Log when there's a slash command.""" if isinstance(error, commands.CommandNotFound): await ctx.send("That's not a command") elif isinstance(error, discord.errors.NotFound): self.bot.log("Deleted message before I could add all reactions") elif isinstance(error, commands.errors.MissingRequiredArgument): self.bot.log(f"{error}", str(ctx.channel_id)) await ctx.send(self.bot.long_strings["missing parameters"]) else: params = [type(error), error, error.__traceback__] exception = traceback.format_exception(*params) exception_string = "".join(exception) log_messages = [f"exception in /{ctx.name}", f"{exception_string}"] self.bot.log(log_messages, str(ctx.channel_id), 40) if isinstance(error, discord.errors.NotFound): self.bot.log("Context is non-existant", level=40) else: await ctx.send("Something went wrong (error code 000)") async def on_error(self, method: str): """Log when there's an error.""" error_type = sys.exc_info()[0] if error_type == discord.errors.NotFound: self.bot.log("Deleted message before I could add all reactions") else: exception = traceback.format_exc() exception_string = "".join(exception) log_messages = [f"exception in {method}", f"{exception_string}"] self.bot.log(log_messages, level=40)