async def respond(self, message, context, response, replacement_message=None): """Takes a response and sends a basic message. Returns message_reference.""" message_reference = None send_arguments = response.get_send_kwargs(replacement_message) try: # Edit the replacement_message as the response if replacement_message: try: await replacement_message.edit(**send_arguments) message_reference = replacement_message except discord.NotFound: # Message deleted response = Response() message_reference = None # Send a new message as the response elif (not response.is_empty() and not (self.selfbot and response.message_type is MessageTypes.REPLACE)): destination = response.destination or message.channel 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) return message_reference
async def on_message(self, message): plugins.broadcast_event(self, 2, message) # Ensure bot can respond properly if not self.can_respond(message): return # Ensure command is valid if message.content.startswith(self.user.mention): split_content = message.content.split(' ', 2)[1:] else: split_content = message.content[1:].split(' ', 1) if len(split_content) == 1: # No spaces split_content.append('') base, parameters = split_content command_pair, shortcut = commands.get_command_pair(self, base) if not command_pair: # Suitable command not found logging.debug("Suitable command not found: " + base) return # Bot is clear to get response. Send typing to signify if self.configurations['core']['send_typing']: await self.send_typing(message.channel) # Parse command and reply try: print(message.author.name + ': ' + message.content) parsed_command = parser.parse(self, base, parameters, command_pair, shortcut) print('\t' + str(parsed_command)) response = await (commands.execute(self, message, parsed_command)) except BotException as e: # Respond with error message response = (str(e), False, 0, None) except Exception as e: # General error logging.error(e) traceback.print_exc() error = 'Uh oh. The bot encountered an exception: {0}: {1}'.format( type(e).__name__, e) response = (error, False, 0, None) message_reference = await self.send_message(message.channel, response[0], tts=response[1]) # A response looks like this: # (text, tts, message_type, extra) # message_type can be: # 0 - normal # 1 - permanent # 2 - terminal (deletes itself after 'extra' seconds) # 3 - active (pass the reference back to the plugin to edit) # If message_type is >= 1, do not add to the edit dictionary # TODO: Add normal message response to the edit dictionary if response[2] == 2: # Terminal await asyncio.sleep(response[3]) await self.delete_message(message_reference)
async def on_message(self, message): plugins.broadcast_event(self, 2, message) # Ensure bot can respond properly if not self.can_respond(message): return # Ensure command is valid if message.content.startswith(self.user.mention): split_content = message.content.split(' ', 2)[1:] else: split_content = message.content[1:].split(' ', 1) if len(split_content) == 1: # No spaces split_content.append('') base, parameters = split_content command_pair, shortcut = commands.get_command_pair(self, base) if not command_pair: # Suitable command not found logging.debug("Suitable command not found: " + base) return # Bot is clear to get response. Send typing to signify if self.configurations['core']['send_typing']: await self.send_typing(message.channel) # Parse command and reply try: print(message.author.name + ': ' + message.content) parsed_command = parser.parse( self, base, parameters, command_pair, shortcut) print('\t' + str(parsed_command)) response = await (commands.execute(self, message, parsed_command)) except BotException as e: # Respond with error message response = (str(e), False, 0, None) except Exception as e: # General error logging.error(e) traceback.print_exc() error = 'Uh oh. The bot encountered an exception: {0}: {1}'.format( type(e).__name__, e) response = (error, False, 0, None) message_reference = await self.send_message( message.channel, response[0], tts=response[1]) # A response looks like this: # (text, tts, message_type, extra) # message_type can be: # 0 - normal # 1 - permanent # 2 - terminal (deletes itself after 'extra' seconds) # 3 - active (pass the reference back to the plugin to edit) # If message_type is >= 1, do not add to the edit dictionary # TODO: Add normal message response to the edit dictionary if response[2] == 2: # Terminal await asyncio.sleep(response[3]) await self.delete_message(message_reference)
async def on_ready(self): plugins.broadcast_event(self, 0) # Make sure server data is ready servers.check_all(self) if self.debug: logging.debug("=== {} online ===".format(self.user.name)) else: print("=== {} online ===".format(self.user.name))
async def _parse_command( self, message, command, parameters, initial_data, elevation, direct): """Parses the command and builds a context.""" subcommand, options, arguments = await parser.parse(self, command, parameters, message) context = self.Context( message, command.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]) return context
async def handle_active_message(bot, message_reference, extra): """ This function is called if the given message was marked as active (message_type of 3). """ if extra[0] == 'ping': latency_time = "Latency time: {:.2f} ms".format((time.time() * 1000) - extra[1]) await bot.edit_message(message_reference, latency_time) elif extra[0] == 'reload': data.save_data(bot) # Safety save logging.debug("Reloading plugins and commands...") # Cancel running tasks associated with plugins tasks = asyncio.Task.all_tasks() pattern = re.compile('([^/(:\d>$)])+(?!.*\/)') for task in tasks: callback_info = task._repr_info()[1] plugin_name = pattern.search(callback_info).group(0) if plugin_name in bot.plugins.keys(): logging.debug("Canceling task: {}".format(task)) task.cancel() bot.plugins = {} bot.commands = {} bot.manuals = [] plugins.add_plugins(bot) commands.add_manuals(bot) logging.debug("Reloading configurations...") bot.configurations = {} configurations.add_configurations(bot) bot.volatile_data = {'global_users': {}, 'global_plugins': {}} data.check_all(bot) bot.fresh_boot = True # Reset one-time startup plugins.broadcast_event(bot, 'on_ready') plugins.broadcast_event(bot, 'bot_on_ready_boot') await asyncio.sleep(1) # Deception await bot.edit_message(message_reference, "Reloaded!")
async def on_server_role_delete(self, server, role): plugins.broadcast_event(self, 16, server, role)
async def on_member_update(self, before, after): plugins.broadcast_event(self, 11, before, after)
async def on_server_remove(self, server): plugins.broadcast_event(self, 13, server)
async def on_channel_delete(self, channel): plugins.broadcast_event(self, 7, channel)
async def on_channel_update(self, before, after): plugins.broadcast_event(self, 9, before, after)
async def on_typing(self, channel, user, when): plugins.broadcast_event(self, 23, channel, user, when)
async def on_socket_raw_send(self, payload): plugins.broadcast_event(self, 4, payload)
async def on_server_role_create(self, server, role): plugins.broadcast_event(self, 15, server, role)
async def on_server_join(self, server): plugins.broadcast_event(self, 12, server)
async def on_member_join(self, member): plugins.broadcast_event(self, 10, member)
async def on_server_unavailable(self, server): plugins.broadcast_event(self, 19, server)
async def on_server_role_update(self, before, after): plugins.broadcast_event(self, 17, before, after)
async def on_member_ban(self, member): plugins.broadcast_event(self, 21, member)
async def on_channel_create(self, channel): plugins.broadcast_event(self, 8, channel)
async def on_voice_state_update(self, before, after): plugins.broadcast_event(self, 20, before, after)
async def on_message_edit(self, before, after): plugins.broadcast_event(self, 6, before, after)
async def on_member_unban(self, server, user): plugins.broadcast_event(self, 22, server, user)
async def handle_error(self, error, message, context, response, edit=None, command_editable=False): """Common error handler for sending responses.""" send_function = edit.edit if edit else message.channel.send self.last_exception = error if response.message: try: await response.message.clear_reactions() except: pass if isinstance(error, BotException): self.last_traceback = error.traceback plugins.broadcast_event(self, 'bot_on_error', error, message) if error.use_embed: content, embed = '', error.embed else: content, embed = str(error), None if command_editable: if content: content += '\n\n(Note: The issuing command can be edited)' embed.set_footer( text= "\u200b\u200b\u200bThe issuing command can be edited", icon_url="http://i.imgur.com/fM9yGzI.png") message_reference = await send_function(content=content, embed=embed) if error.autodelete > 0: await asyncio.sleep(error.autodelete) try: # Avoid delete_messages for selfbot mode message_reference = edit if edit else message_reference await self.delete_message(message_reference, reason='Automatic') await self.delete_message(message, reason='Automatic') except: pass return elif isinstance(error, discord.Forbidden): plugins.broadcast_event(self, 'bot_on_discord_error', error, message) message_reference = None try: await message.author.send( content="Sorry, I don't have permission to carry " "out that command in that channel. The bot may have had " "its `Send Messages` permission revoked (or any other " "necessary permissions, like `Manage Messages` or " "`Speak`).\nIf you are a bot moderator or server owner, " "you can mute channels with `{}mod mute <channel>` " "instead of using permissions directly. If that's not the " "issue, be sure to check that the bot has the proper " "permissions on the server and each channel!".format( self.command_invokers[0])) except: # User has blocked the bot pass else: if isinstance( error, discord.HTTPException) and len(str(response)) > 1998: plugins.broadcast_event(self, 'bot_on_discord_error', error, message) message_reference = await utilities.send_text_as_file( message.channel, str(response), 'response', extra= "The response is too long. Here is a text file of the contents." ) else: insult = random.choice(exception_insults) error = '**`{0}:`**`{1}`'.format( type(error).__name__, error) embed = discord.Embed(title=':x: Internal error', description=insult, colour=discord.Colour(0xdd2e44)) embed.add_field(name='Details:', value=error) embed.set_footer( text="The bot owners have been notified of this error." ) message_reference = await send_function(content='', embed=embed) self.last_traceback = traceback.format_exc() plugins.broadcast_event(self, 'bot_on_general_error', error, message) logger.error(self.last_traceback) logger.error(self.last_exception) parsed_input = '[{0.subcommand}, {0.options}, {0.arguments}]'.format( context) await utilities.notify_owners( self, '```\n{0}\n{1}\n{2}\n{3}```'.format( message.content, parsed_input, self.last_exception, self.last_traceback)) return edit if edit else message_reference
def dispatch(self, event, *args, **kwargs): super().dispatch(event, *args, **kwargs) plugins.broadcast_event(self, 'on_' + event, *args, **kwargs)
async def on_message_delete(self, message): plugins.broadcast_event(self, 5, message)
async def on_socket_raw_receive(self, msg): plugins.broadcast_event(self, 3, msg)
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)) '''
async def on_ready(self): if self.fresh_boot is None: app_info = await self.application_info() if app_info.owner.id not in self.owners: self.owners.append(app_info.owner.id) if self.selfbot: # Selfbot safety checks if len(self.owners) != 1: raise CBException( "There can be only one owner for " "a selfbot.", error_type=ErrorTypes.STARTUP) elif self.owners[0] != self.user.id: raise CBException("Token does not match the owner.", error_type=ErrorTypes.STARTUP) # Start scheduler asyncio.ensure_future(utilities._start_scheduler(self)) # Make sure guild data is ready data.check_all(self) data.load_data(self) self.fresh_boot = True plugins.broadcast_event(self, 'bot_on_ready_boot') elif self.fresh_boot: self.fresh_boot = False if self.debug: debug_channel = self.get_channel( self.configurations['core']['debug_channel']) if self.fresh_boot and debug_channel is not None: log_file = '{}/temp/last_logs.txt'.format(self.path) error_file = '{}/temp/error.txt'.format(self.path) if not os.path.isfile(log_file): await debug_channel.send(content="Started up fresh.") else: discord_file = discord.File(log_file) await debug_channel.send(content="Logs:", file=discord_file) if not os.path.isfile(error_file): await debug_channel.send(content="No error log.") else: discord_file = discord.File(error_file) await debug_channel.send(content="Last error:", file=discord_file) elif debug_channel is not None: await debug_channel.send("Reconnected.") logger.info("=== {0: ^40} ===".format(self.user.name + ' online')) if self.fresh_boot: asyncio.ensure_future(self.spam_clear_loop()) asyncio.ensure_future(self.save_loop()) asyncio.ensure_future(self.backup_loop()) if self.selfbot: asyncio.ensure_future(self.selfbot_away_loop()) elif len(self.guilds) == 0: link = ( 'https://discordapp.com/oauth2/authorize?&client_id={}' '&scope=bot&permissions=8').format(app_info.id) first_start_note = ( "It appears that this is the first time you are starting up the bot. " "In order to have it function properly, you must add the bot to the " "server with the specified debug channel. Invite link:\n{}\n\nIt is " "highly recommended that you update the core using [{}botowner update] " "to not only update the bot, but also add the core manual." ).format(link, self.command_invokers[0]) logger.info(first_start_note)
async def on_error(self, event, *args, **kwargs): plugins.broadcast_event(self, 1, event, *args, **kwargs)
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 = Elevation.BOT_OWNERS - (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: if self.single_command: try: parameters = content base = self.single_command command = self.commands[base] except KeyError: logger.error("Single command fill not found!") return else: logger.debug("Suitable command not found: %s", 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 > Elevation.ALL 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 message.channel.send(content=( "{0}, you appear to be issuing/editing " "commands too quickly. Please wait {1} seconds.".format( message.author.mention, self.spam_timeout))) return context = None try: # Check for maintenance mode if self.maintenance_mode and elevation != Elevation.BOT_OWNERS: if self.maintenance_mode == 1: # Ignore attempts to respond if not 1 if self.maintenance_message: fields = [('\u200b', self.maintenance_message)] else: fields = [] raise CBException( "The bot is currently in maintenance mode.", embed_fields=fields, editable=False) else: return # Parse command and reply logger.debug('%s (%s): %s', message.author, message.author.id, message.content) parse_command = self._parse_command( message, command, parameters, initial_data, elevation, direct) if replacement_message: context = await parse_command response = await self._get_response(context) else: with message.channel.typing(): context = await parse_command response = await self._get_response(context) self.response_deque.appendleft((context, response)) except Exception as e: # General error response = Response() message_reference = await self.handle_error( e, message, context, response, edit=replacement_message, command_editable=True) else: # Attempt to respond message_reference = await self.respond( message, context, response, replacement_message=replacement_message) # 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 self.last_response = message_reference self.last_context = context await self.handle_response( message, response, message_reference=message_reference, replacement_message=replacement_message, context=context)
async def on_ready(self): if self.fresh_boot is None: if self.selfbot: # Selfbot safety checks self.owners = [self.user.id] else: app_info = await self.application_info() if app_info.owner.id not in self.owners: self.owners.append(app_info.owner.id) # Make sure guild data is ready data.check_all(self) data.load_data(self) # Set single command notification if self.single_command: try: command = self.commands[self.single_command] except KeyError: raise CBException( "Invalid single command base.", error_type=ErrorTypes.STARTUP) command.help_embed_fields.append(( '[Single command mode]', 'The base `{}` can be omitted when invoking these commands.'.format( self.single_command))) self.fresh_boot = True self.ready = True plugins.broadcast_event(self, 'bot_on_ready_boot') # Start scheduler asyncio.ensure_future(utilities._start_scheduler(self)) elif self.fresh_boot: self.fresh_boot = False if self.debug: debug_channel = self.get_channel( self.configurations['core']['debug_channel']) if self.fresh_boot and debug_channel is not None: log_file = '{}/temp/last_logs.txt'.format(self.path) error_file = '{}/temp/error.txt'.format(self.path) if not os.path.isfile(log_file): await debug_channel.send(content="Started up fresh.") else: discord_file = discord.File(log_file, filename='last_logs.txt') await debug_channel.send(content="Logs:", file=discord_file) if not os.path.isfile(error_file): await debug_channel.send(content="No error log.") else: discord_file = discord.File(error_file) await debug_channel.send(content="Last error:", file=discord_file) elif debug_channel is not None: await debug_channel.send("Reconnected.") logger.info("=== {0: ^40} ===".format(self.user.name + ' online')) if self.fresh_boot: asyncio.ensure_future(self.spam_clear_loop()) asyncio.ensure_future(self.save_loop()) asyncio.ensure_future(self.backup_loop()) if self.selfbot: asyncio.ensure_future(self.selfbot_away_loop()) elif len(self.guilds) == 0: link = ( 'https://discordapp.com/oauth2/authorize?&client_id={}' '&scope=bot&permissions=8').format(app_info.id) first_start_note = ( "It appears that this is the first time you are starting up the bot. " "In order to have it function properly, you must add the bot to the " "server with the specified debug channel. Invite link:\n{}\n\nIt is " "highly recommended that you update the core using [{}botowner update] " "to not only update the bot, but also add the core manual.").format( link, self.command_invokers[0]) logger.info(first_start_note)
async def handle_error( self, error, message, context, response, edit=None, command_editable=False): """Common error handler for sending responses.""" send_function = edit.edit if edit else message.channel.send self.last_exception = error if response.message and response.message.reactions: try: await response.message.clear_reactions() except: # No permissions pass if isinstance(error, BotException): self.last_traceback = error.traceback plugins.broadcast_event(self, 'bot_on_exception', error, message) content, embed = ('', error.embed) if error.use_embed else (str(error), None) if command_editable and error.autodelete == 0 and error.editable: if content: content += '\n\n(Note: The issuing command can be edited)' elif embed: embed.set_footer( text="\u200b\u200b\u200bThe issuing command can be edited", icon_url="http://i.imgur.com/fM9yGzI.png") # TODO: Handle long messages message_reference = await send_function(content=content, embed=embed) if error.autodelete > 0: await asyncio.sleep(error.autodelete) try: # Avoid delete_messages for selfbot mode message_reference = edit if edit else message_reference await message_reference.delete() await message.delete() except: pass return elif isinstance(error, discord.Forbidden): plugins.broadcast_event(self, 'bot_on_discord_exception', error, message) message_reference = None try: await message.author.send( content="Sorry, I don't have permission to carry " "out that command in that channel. The bot may have had " "its `Send Messages` permission revoked (or any other " "necessary permissions, like `Embed Links`, `Manage Messages`, or " "`Speak`).\nIf you are a bot moderator or server owner, " "you can mute channels with `{}mod mute <channel>` " "instead of using permissions directly. If that's not the " "issue, be sure to check that the bot has the proper " "permissions on the server and each channel!".format( self.command_invokers[0])) except: # User has blocked the bot pass # TODO: Consider sending a general permissions error else: if isinstance(error, discord.HTTPException) and len(str(response)) > 1998: plugins.broadcast_event(self, 'bot_on_discord_exception', error, message) message_reference = await utilities.send_text_as_file( message.channel, str(response), 'response', extra="The response is too long. Here is a text file of the contents.") else: insult = random.choice(self.exception_messages) error = '**`{0}:`**`{1}`'.format(type(error).__name__, error) embed = discord.Embed( title=':x: Internal error', description=insult, color=discord.Color(0xdd2e44)) embed.add_field(name='Details:', value=error) embed.set_footer(text="The bot owners have been notified of this error.") message_reference = await send_function(content='', embed=embed) self.last_traceback = traceback.format_exc() plugins.broadcast_event(self, 'bot_on_uncaught_exception', error, message) logger.error(self.last_traceback) logger.error(self.last_exception) if context: parsed_input = '[{0.subcommand}, {0.options}, {0.arguments}]'.format(context) else: parsed_input = '!Context is missing!' self.error_deque.appendleft(( context or message, parsed_input, self.last_exception, self.last_traceback)) await utilities.notify_owners( self, '```\n{0}\n{1}\n{2}\n{3}```'.format( message.content, parsed_input, self.last_exception, self.last_traceback)) return edit if edit else message_reference