async def check_warns(bot, message): """Checks for issued warnings.""" if (not message.guild or message.guild.id != configurations.get(bot, __name__, 'guild_id') or not message.content.lower().startswith('!warn ') or not data.is_mod(bot, member=message.author) or 'autolog.py' not in bot.plugins): return split, quotes = parser.split_parameters(message.content, include_quotes=True, quote_list=True) if len(split) < 3: # Not enough arguments return name = split[2][1:-1] if 2 in quotes else split[2] member = data.get_member(bot, name, guild=message.guild, safe=True, strict=True) if not member or data.is_mod( bot, member=member): # Member not found or is another mod return details = '{0} (<@{0.id}>) was warned by {1} (<@{1.id}>): {2}'.format( member, message.author, ''.join(split[4:]) or "No warn reason given") await bot.plugins['autolog.py'].automated_dump_message( bot, message.guild, details, query=member.id, moderator_id=message.author.id)
def get_commands(bot): new_commands = [] new_commands.append(Command( 'gdq', subcommands=[ SubCommand(doc='Shows the GDQ menu.'), SubCommand(Opt('about'), doc='Shows some basic information about GDQ.'), SubCommand( Opt('status'), doc='Shows the stream status and total amount amount of money raised.'), SubCommand(Opt('current'), doc='Shows the current game being played.'), SubCommand( Opt('next'), Arg('number', convert=int, check=lambda b, m, v, *a: 1 <= v <= 5, check_error='Must be between 1 and 5 inclusive.', default=1, argtype=ArgTypes.OPTIONAL, quotes_recommended=False), doc='Shows the next game(s). If a number is given (between 1 and 5 ' 'inclusive), it will show the next number of games.'), SubCommand( Opt('search'), Arg('title', argtype=ArgTypes.MERGED), doc='Searches for the given game.'), SubCommand( Opt('notify'), Opt('channel', optional=True, check=lambda b, m, v, *a: data.is_mod(b, m.guild, m.author.id), check_error='Only bot moderators can notify the channel.'), Arg('title', argtype=ArgTypes.MERGED), doc='Sends a message to either the user or the channel for the given ' 'game when it is about to be streamed (approximately 5-10 minutes ' 'beforehand).\nOnly bot moderators can use the channel option.')], description='Games Done Quick for Discord.', category='service')) return new_commands
async def can_interact(bot, member, channel_id=None): """Checks that the given member can be interacted with. This ensures that the user is: Not a bot Not blocked in the server Additionally, if the user is a member (guild exists): Not in a blocked channel Not blacklisted by the botowners If given a channel ID, also checks that the bot is not muted in there This also checks for maintenace mode """ if data.is_owner(bot, member.id): return True elif member.bot or member.id in data.get(bot, 'core', 'blacklist', default=[]): return False elif bot.maintenance_mode: return False # Guild specific check guild = getattr(member, 'guild', None) if guild: if data.is_mod(bot, member=member): return True guild_data = data.get(bot, 'core', None, guild.id, default={}) if (guild_data.get('muted', False) or (channel_id in guild_data.get('muted_channels', [])) or (member.id in guild_data.get('blocked', []))): return False return True
async def check_warns(bot, message): """Checks for issued warnings.""" if (not message.guild or message.guild.id != configurations.get(bot, __name__, 'guild_id') or not message.content.lower().startswith('!warn ') or not data.is_mod(bot, member=message.author) or 'autolog.py' not in bot.plugins): return split, quotes = parser.split_parameters(message.content, include_quotes=True, quote_list=True) if len(split) < 3: # Not enough arguments return name = split[2][1:-1] if 2 in quotes else split[2] member = data.get_member(bot, name, guild=message.guild, safe=True, strict=True) if not member or data.is_mod(bot, member=member): # Member not found or is another mod return details = '{0} (<@{0.id}>) was warned by {1} (<@{1.id}>): {2}'.format( member, message.author, ''.join(split[4:]) or "No warn reason given") await bot.plugins['autolog.py'].automated_dump_message( bot, message.guild, details, query=member.id, moderator_id=message.author.id)
def get_commands(bot): new_commands = [] new_commands.append( Command( 'gdq', subcommands=[ SubCommand(doc='Shows the GDQ menu.'), SubCommand(Opt('about'), doc='Shows some basic information about GDQ.'), SubCommand( Opt('status'), doc= 'Shows the stream status and total amount amount of money raised.' ), SubCommand(Opt('current'), doc='Shows the current game being played.'), SubCommand( Opt('next'), Arg('number', convert=int, check=lambda b, m, v, *a: 1 <= v <= 5, check_error='Must be between 1 and 5 inclusive.', default=1, argtype=ArgTypes.OPTIONAL, quotes_recommended=False), doc= 'Shows the next game(s). If a number is given (between 1 and 5 ' 'inclusive), it will show the next number of games.'), SubCommand(Opt('search'), Arg('title', argtype=ArgTypes.MERGED), doc='Searches for the given game.'), SubCommand( Opt('notify'), Opt('channel', optional=True, check=lambda b, m, v, *a: data.is_mod( b, m.guild, m.author.id), check_error= 'Only bot moderators can notify the channel.'), Arg('title', argtype=ArgTypes.MERGED), doc= 'Sends a message to either the user or the channel for the given ' 'game when it is about to be streamed (approximately 5-10 minutes ' 'beforehand).\nOnly bot moderators can use the channel option.' ) ], description='Games Done Quick for Discord.', category='service')) return new_commands
async def character_forceremove(bot, context): """Forcibly removes the character of the given user.""" owner = context.arguments[0] if data.is_mod(bot, member=owner): if not data.is_admin(bot, context.guild, context.author.id): raise CBException("Cannot remove characters of other bot moderators.") character_search = utilities.clean_text(context.arguments[1]) search_result = _user_character_search(bot, context.author, owner, character_search) character = search_result[1][search_result[0]] data.db_delete( bot, 'characters', where_arg='clean_name=%s AND owner_id=%s', input_args=(character.clean_name, owner.id)) return Response(content="Character forcefully deleted.")
async def owner_wrapper(bot, message, base, blueprint_index, options, arguments, keywords, cleaned_content): response, tts, message_type, extra = ('', False, 0, None) mod_action = '' send_notifications = data.get(bot, 'base', 'notifications', server_id=message.server.id, default=True) if blueprint_index in (0, 1): # Add or remove moderator user = data.get_member(bot, arguments[0], server=message.server) user_is_mod = data.is_mod(bot, message.server, user.id, strict=True) user_is_elevated = data.is_mod(bot, message.server, user.id) blocked = data.is_blocked(bot, message.server, user.id, strict=True) mod_action = 'Added {}' if blueprint_index == 0 else 'Removed {}' mod_action = mod_action.format( '{0} ({0.id}) as a moderator'.format(user)) if blocked: raise BotException(EXCEPTION, "User is blocked.") elif blueprint_index == 0: # add if user_is_mod or user_is_elevated: raise BotException(EXCEPTION, "User is already a moderator.") else: data.list_data_append(bot, 'base', 'moderators', user.id, server_id=message.server.id) response = "User is now a moderator." else: # remove if not user_is_mod: raise BotException(EXCEPTION, "User is not in the moderators list.") else: data.list_data_remove(bot, 'base', 'moderators', user.id, server_id=message.server.id) response = "User is no longer a moderator." elif blueprint_index == 2: # Send feedback if data.get(bot, 'base', 'feedbackdisabled', default=False): response = ("Feedback has been temporarily disabled, probably " "due to some troll spammers.") else: text = arguments[0] if len(text) > 1500: raise BotException( EXCEPTION, "Whoa! That's a lot of feedback. " "1500 characters or fewer, please.") text = ('{0} ({0.id}) on {1.timestamp}:' '\n\t{2}').format(message.author, message, text) await utilities.notify_owners(bot, text, user_id=message.author.id) response = "Message sent to bot owners." elif blueprint_index == 3: # Toggle notifications response = ("Bot moderator activity notifications are now turned " "{}").format("OFF." if send_notifications else "ON.") data.add(bot, 'base', 'notifications', not send_notifications, server_id=message.server.id) # Send notification if configured if mod_action and send_notifications: if message.edited_timestamp: timestamp = message.edited_timestamp else: timestamp = message.timestamp notification = 'From {0.server} on {1}, you:\n\t{2}'.format( message.author, timestamp, mod_action) logs = await utilities.get_log_text(bot, message.channel, limit=20, before=message) logs += '\n{}'.format(utilities.get_formatted_message(message)) await bot.send_message(message.server.owner, notification) await utilities.send_text_as_file(bot, message.server.owner, logs, 'context') return (response, tts, message_type, extra)
async def mod_wrapper(bot, message, base, blueprint_index, options, arguments, keywords, cleaned_content): response, tts, message_type, extra = ('', False, 0, None) mod_action = '' if blueprint_index == 0: # info server_data = data.get(bot, 'base', None, server_id=message.server.id, default={}) disabled_commands = server_data.get('disabled', []) display_list = [] for disabled_command in disabled_commands: display_list.append('{0} ({1})'.format( disabled_command[0], 'all' if disabled_command[1] == -1 else disabled_command[1] + 1)) response = ('```\n' 'Information for server {0}\n' 'ID: {0.id}\n' 'Owner: {0.owner.id}\n' 'Moderators: {1}\n' 'Blocked users: {2}\n' 'Muted: {3}\n' 'Muted channels: {4}\n' 'Command invoker: {5}\n' 'Mention mode: {6}\n' 'Disabled commands: {7}```').format( message.server, server_data.get('moderators', []), server_data.get('blocked', []), server_data.get('muted', []), server_data.get('muted_channels', []), server_data.get('command_invoker', None), server_data.get('mention_mode', False), display_list) elif blueprint_index == 1: # Toggle command try: # Explicit index split_arguments = arguments[0].split() command = bot.commands[split_arguments[0]] guess = [command.base, int(split_arguments[1]) - 1] assert -1 < guess[1] < len(command.blueprints) except IndexError: # No index guess = [command.base, -1] except: # Guess the help index guess = list(parser.guess_index(bot, arguments[0])) if guess[0] is None: raise BotException(EXCEPTION, "Invalid base.") command = bot.commands[guess[0]] if command.plugin is bot.commands['base'].plugin: raise BotException(EXCEPTION, "The base commands cannot be disabled.") pass_in = (bot, 'base', 'disabled', guess) pass_in_keywords = {'server_id': message.server.id} disabled_commands = data.get(*pass_in[:-1], **pass_in_keywords, default=[]) if guess in disabled_commands: data.list_data_remove(*pass_in, **pass_in_keywords) response = "Enabled" else: data.list_data_append(*pass_in, **pass_in_keywords) response = "Disabled" response += " the `{0}` command {1}.".format( guess[0], "and all associated subcommands" if guess[1] == -1 else "(subcommand {})".format(guess[1] + 1)) mod_action = response elif blueprint_index in (2, 3): # Block or unblock user = data.get_member(bot, arguments[0], message.server) block = blueprint_index == 2 mod_action = 'Blocked {}' if block else 'Unblocked {}' mod_action = mod_action.format('{0} ({0.id})'.format(user)) blocked = data.is_blocked(bot, message.server, user.id, strict=True) mod = data.is_mod(bot, message.server, user.id) if mod: raise BotException(EXCEPTION, "Cannot block or unblock a moderator.") elif block: if blocked: raise BotException(EXCEPTION, "User is already blocked.") else: data.list_data_append(bot, 'base', 'blocked', user.id, server_id=message.server.id) response = "User is now blocked." else: if not blocked: raise BotException(EXCEPTION, "User is already unblocked.") else: data.list_data_remove(bot, 'base', 'blocked', user.id, server_id=message.server.id) response = "User is now unblocked." elif blueprint_index == 4: # Clear response = ('' + '\n' * 80 + "The chat was pushed up by a bot moderator.") elif blueprint_index in (5, 6): # Mute or unmute server_id = message.server.id mute = blueprint_index == 5 mod_action = 'Muted {}' if mute else 'Unmuted {}' if arguments[0]: channel = data.get_channel(bot, arguments[0], message.server) muted = channel.id in data.get(bot, 'base', 'muted_channels', server_id=server_id, default=[]) mod_action = mod_action.format(channel.name) if mute: if muted: raise BotException(EXCEPTION, "Channel is already muted.") else: data.list_data_append(bot, 'base', 'muted_channels', channel.id, server_id=server_id) if str(channel.type) == 'voice': # disconnect await utilities.leave_and_stop(bot, message.server) response = "Channel muted." else: # unmute if not muted: raise BotException(EXCEPTION, "Channel is already unmuted.") else: data.list_data_remove(bot, 'base', 'muted_channels', channel.id, server_id=server_id) response = "Channel unmuted." else: # server mod_action = mod_action.format('the server') muted = data.get(bot, 'base', 'muted', server_id=server_id, default=False) if not (muted ^ mute): response = "Server is already {}muted.".format( '' if muted else 'un') raise BotException(EXCEPTION, response) else: data.add(bot, 'base', 'muted', mute, server_id=server_id) response = "Server {}muted.".format('' if mute else 'un') elif blueprint_index == 7: # Invoker if len(arguments[0]) > 10: raise BotException( EXCEPTION, "The invoker can be a maximum of 10 characters long.") data.add(bot, 'base', 'command_invoker', arguments[0] if arguments[0] else None, server_id=message.server.id) response = "Custom command invoker {}.".format( 'set' if arguments[0] else 'cleared') if arguments[0]: response = "Custom command invoker set." mod_action = "Set the server command invoker to '{}'.".format( arguments[0]) else: response = "Custom command invoker cleared." mod_action = "Removed the custom command invoker." elif blueprint_index == 8: # Mention current_mode = data.get(bot, 'base', 'mention_mode', server_id=message.server.id, default=False) data.add(bot, 'base', 'mention_mode', not current_mode, server_id=message.server.id) response = "Mention mode {}activated.".format( 'de' if current_mode else '') mod_action = "{}activated mention mode.".format( 'de' if current_mode else '').capitalize() # Send notification if configured send_notifications = data.get(bot, 'base', 'notifications', server_id=message.server.id, default=True) if mod_action and send_notifications: if message.edited_timestamp: timestamp = message.edited_timestamp else: timestamp = message.timestamp notification = ('Moderator {0} ({0.id}) from {0.server} on {1}:\n\t' '{2}').format(message.author, timestamp, mod_action) logs = await utilities.get_log_text(bot, message.channel, limit=20, before=message) logs += '\n{}'.format(utilities.get_formatted_message(message)) await bot.send_message(message.server.owner, notification) await utilities.send_text_as_file(bot, message.server.owner, logs, 'context') return (response, tts, message_type, extra)
async def base_wrapper(bot, message, base, blueprint_index, options, arguments, keywords, cleaned_content): response, tts, message_type, extra = ('', False, 0, None) if blueprint_index == 0: # version response = '`{}`\n{}'.format(bot.version, bot.date) elif blueprint_index == 1: # source response = random.choice([ "It's shit. I'm sorry.", "You want to see what the Matrix is like?", "Script kiddie level stuff in here.", "Beware the lack of PEP 8 guidelines inside!", "Snarky comments inside and out.", "Years down the road, this will all just be a really " "embarrassing but funny joke.", "Made with ~~love~~ pure hatred.", "At least he's using version control.", "Yes, I know I'm not very good. Sorry...", "Take it easy on me, okay?", "You're going to groan. A lot.", "You might be better off *not* looking inside." ]) response += ("\nhttps://github.com/jkchen2/JshBot\n" "https://github.com/jkchen2/JshBot-plugins") elif blueprint_index == 2: # uptime uptime_total_seconds = int(time.time()) - bot.time uptime_struct = time.gmtime(uptime_total_seconds) days = int(uptime_total_seconds / 86400) hours = uptime_struct.tm_hour minutes = uptime_struct.tm_min seconds = uptime_struct.tm_sec response = ("The bot has been on since **{0}**\n{1} days, {2} hours, " "{3} minutes, and {4} seconds").format( bot.readable_time, days, hours, minutes, seconds) elif blueprint_index == 3: # Announcement announcement = data.get(bot, 'base', 'announcement') if not announcement: response = "No announcement right now!" else: response = announcement elif blueprint_index == 4: # Invite if bot.selfbot: raise BotException(EXCEPTION, "Nope.") response_list = [] if 'details' in options: for plugin in bot.plugins.keys(): permission_items = data.get(bot, plugin, 'permissions', volatile=True, default={}).items() if permission_items: response_list.append('***`{}`***'.format(plugin)) response_list.append('\t' + '\n\t'.join([ '**`{0[0]}`** -- {0[1]}'.format(item) for item in permission_items ]) + '\n') permissions_number = utilities.get_permission_bits(bot) app_id = (await bot.application_info()).id response_list.append( 'https://discordapp.com/oauth2/authorize?&client_id={0}' '&scope=bot&permissions={1}\n**Remember: you must have the ' '"Administrator" role on the server you are trying to add the ' 'bot to.**'.format(app_id, permissions_number)) response = '\n'.join(response_list) elif blueprint_index in (5, 6): # Join/leave voice channel if message.channel.is_private: raise BotException( EXCEPTION, "This command cannot be used in direct messages.") voice_channel = message.author.voice_channel if not voice_channel: raise BotException(EXCEPTION, "You are not in a voice channel.") try: if blueprint_index == 5: await utilities.join_and_ready(bot, voice_channel, reconnect=True, is_mod=data.is_mod( bot, message.server, message.author.id)) response = "Joined {}.".format(voice_channel.name) else: await utilities.leave_and_stop(bot, message.server, member=message.author, safe=False) response = "Left {}.".format(voice_channel.name) except BotException as e: raise e # Pass up except Exception as e: action = 'join' if blueprint_index == 5 else 'leave' raise BotException( EXCEPTION, "Failed to {} the voice channel.".format(action), e=e) return (response, tts, message_type, extra)
async def on_message(self, message, replacement_message=None): # Ensure bot can respond properly try: initial_data = self.can_respond(message) except Exception as e: # General error logger.error(e) logger.error(traceback.format_exc()) self.last_exception = e return if not initial_data: return # Ensure command is valid content = initial_data[0] elevation = 3 - (initial_data[4:0:-1] + [True]).index(True) split_content = content.split(' ', 1) if len(split_content) == 1: # No spaces split_content.append('') base, parameters = split_content base = base.lower() try: command = self.commands[base] except KeyError: logger.debug("Suitable command not found: " + base) return # Check that user is not spamming author_id = message.author.id direct = isinstance(message.channel, PrivateChannel) spam_value = self.spam_dictionary.get(author_id, 0) if elevation > 0 or direct: # Moderators ignore custom limit spam_limit = self.spam_limit else: spam_limit = min( self.spam_limit, data.get(self, 'core', 'spam_limit', guild_id=message.guild.id, default=self.spam_limit)) if spam_value >= spam_limit: if spam_value == spam_limit: self.spam_dictionary[author_id] = spam_limit + 1 plugins.broadcast_event(self, 'bot_on_user_ratelimit', message.author) await self.send_message( message.channel, "{0}, you appear to be issuing/editing " "commands too quickly. Please wait {1} seconds.". format(message.author.mention, self.spam_timeout)) return # Parse command and reply try: context = None with message.channel.typing(): logger.debug(message.author.name + ': ' + message.content) subcommand, options, arguments = parser.parse( self, command, parameters, message) context = self.Context( message, base, subcommand, options, arguments, subcommand.command.keywords, initial_data[0], elevation, message.guild, message.channel, message.author, direct, subcommand.index, subcommand.id, self) plugins.broadcast_event(self, 'bot_on_command', context) logger.info([subcommand, options, arguments]) response = await commands.execute(self, context) if response is None: response = Response() if self.selfbot and response.content: response.content = '\u200b' + response.content except Exception as e: # General error response = Response() destination = message.channel message_reference = await self.handle_error( e, message, context, response, edit=replacement_message, command_editable=True) else: # Attempt to respond send_arguments = response.get_send_kwargs(replacement_message) try: destination = response.destination if response.destination else message.channel message_reference = None if replacement_message: try: await replacement_message.edit(**send_arguments) message_reference = replacement_message except discord.NotFound: # Message deleted response = Response() message_reference = None elif (not response.is_empty() and not (self.selfbot and response.message_type is MessageTypes.REPLACE)): message_reference = await destination.send( **send_arguments) response.message = message_reference plugins.broadcast_event(self, 'bot_on_response', response, context) except Exception as e: message_reference = await self.handle_error( e, message, context, response, edit=replacement_message, command_editable=True) # Incremement the spam dictionary entry if author_id in self.spam_dictionary: self.spam_dictionary[author_id] += 1 else: self.spam_dictionary[author_id] = 1 # MessageTypes: # NORMAL - Normal. The issuing command can be edited. # PERMANENT - Message is not added to the edit dictionary. # REPLACE - Deletes the issuing command after 'extra' seconds. Defaults # to 0 seconds if 'extra' is not given. # ACTIVE - The message reference is passed back to the function defined # with 'extra_function'. If 'extra_function' is not defined, it will call # plugin.handle_active_message. # INTERACTIVE - Assembles reaction buttons given by extra['buttons'] and # calls 'extra_function' whenever one is pressed. # WAIT - Wait for event. Calls 'extra_function' with the result, or None # if the wait timed out. # # Only the NORMAL message type can be edited. response.message = message_reference if message_reference and isinstance(message_reference.channel, PrivateChannel): permissions = self.user.permissions_in( message_reference.channel) elif message_reference: permissions = message_reference.guild.me.permissions_in( message_reference.channel) else: permissions = None self.last_response = message_reference if response.message_type is MessageTypes.NORMAL: # Edited commands are handled in base.py if message_reference is None: # Forbidden exception return wait_time = self.edit_timeout if wait_time: self.edit_dictionary[str(message.id)] = message_reference await asyncio.sleep(wait_time) if str(message.id) in self.edit_dictionary: del self.edit_dictionary[str(message.id)] if message_reference.embeds: embed = message_reference.embeds[0] if embed.footer.text and embed.footer.text.startswith( '\u200b' * 3): embed.set_footer() try: await message_reference.edit(embed=embed) except: pass elif response.message_type is MessageTypes.REPLACE: try: if self.selfbot and not replacement_message: # Edit instead await message.edit(**send_arguments) else: if response.extra: await asyncio.sleep(response.extra) try: await message.delete(reason='Automatic') except: # Ignore permissions errors pass except Exception as e: message_reference = await self.handle_error( e, message, context, response, edit=message_reference) self.last_response = message_reference elif response.message_type is MessageTypes.ACTIVE: if message_reference is None: # Forbidden exception return try: await response.extra_function(self, context, response) except Exception as e: # General error message_reference = await self.handle_error( e, message, context, response, edit=message_reference) self.last_response = message_reference elif response.message_type is MessageTypes.INTERACTIVE: try: buttons = response.extra['buttons'] kwargs = response.extra.get('kwargs', {}) if 'timeout' not in kwargs: kwargs['timeout'] = 300 if 'check' not in kwargs: kwargs['check'] = (lambda r, u: r.message.id == message_reference.id and not u.bot) for button in buttons: await message_reference.add_reaction(button) reaction_check = await destination.get_message( message_reference.id) for reaction in reaction_check.reactions: if not reaction.me or reaction.count > 1: async for user in reaction.users(): if user != self.user and permissions.manage_messages: asyncio.ensure_future( message_reference.remove_reaction( reaction, user)) await response.extra_function(self, context, response, None, False) process_result = True while process_result is not False: try: if not permissions.manage_messages: add_task = self.wait_for( 'reaction_add', **kwargs) remove_task = self.wait_for( 'reaction_remove', **kwargs) done, pending = await asyncio.wait( [add_task, remove_task], return_when=FIRST_COMPLETED) result = next(iter(done)).result() for future in pending: future.cancel() else: # Can remove reactions result = await self.wait_for( 'reaction_add', **kwargs) if result[1] != self.user: asyncio.ensure_future( message_reference.remove_reaction( *result)) else: continue is_mod = data.is_mod(self, message.guild, result[1].id) if (response.extra.get('reactionlock', True) and not result[0].me or (response.extra.get('userlock', True) and not (result[1] == message.author or is_mod))): continue except (asyncio.futures.TimeoutError, asyncio.TimeoutError): await response.extra_function( self, context, response, None, True) process_result = False else: process_result = await response.extra_function( self, context, response, result, False) try: await response.message.clear_reactions() except: pass except Exception as e: message_reference = await self.handle_error( e, message, context, response, edit=message_reference) self.last_response = message_reference elif response.message_type is MessageTypes.WAIT: try: kwargs = response.extra.get('kwargs', {}) if 'timeout' not in kwargs: kwargs['timeout'] = 300 process_result = True while process_result is not False: try: result = await self.wait_for( response.extra['event'], **kwargs) except asyncio.TimeoutError: await response.extra_function( self, context, response, None) process_result = False else: process_result = await response.extra_function( self, context, response, result) if not response.extra.get('loop', False): process_result = False except Exception as e: message_reference = await self.handle_error( e, message, context, response, edit=message_reference) self.last_response = message_reference else: logger.error("Unknown message type: {}".format( response.message_type)) '''