Exemplo n.º 1
0
        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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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))
Exemplo n.º 5
0
    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))
Exemplo n.º 6
0
 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
Exemplo n.º 7
0
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!")
Exemplo n.º 8
0
 async def on_server_role_delete(self, server, role):
     plugins.broadcast_event(self, 16, server, role)
Exemplo n.º 9
0
 async def on_member_update(self, before, after):
     plugins.broadcast_event(self, 11, before, after)
Exemplo n.º 10
0
 async def on_server_remove(self, server):
     plugins.broadcast_event(self, 13, server)
Exemplo n.º 11
0
 async def on_channel_delete(self, channel):
     plugins.broadcast_event(self, 7, channel)
Exemplo n.º 12
0
 async def on_channel_update(self, before, after):
     plugins.broadcast_event(self, 9, before, after)
Exemplo n.º 13
0
 async def on_typing(self, channel, user, when):
     plugins.broadcast_event(self, 23, channel, user, when)
Exemplo n.º 14
0
 async def on_socket_raw_send(self, payload):
     plugins.broadcast_event(self, 4, payload)
Exemplo n.º 15
0
 async def on_server_role_create(self, server, role):
     plugins.broadcast_event(self, 15, server, role)
Exemplo n.º 16
0
 async def on_server_role_delete(self, server, role):
     plugins.broadcast_event(self, 16, server, role)
Exemplo n.º 17
0
 async def on_server_join(self, server):
     plugins.broadcast_event(self, 12, server)
Exemplo n.º 18
0
 async def on_server_remove(self, server):
     plugins.broadcast_event(self, 13, server)
Exemplo n.º 19
0
 async def on_member_update(self, before, after):
     plugins.broadcast_event(self, 11, before, after)
Exemplo n.º 20
0
 async def on_member_join(self, member):
     plugins.broadcast_event(self, 10, member)
Exemplo n.º 21
0
 async def on_channel_update(self, before, after):
     plugins.broadcast_event(self, 9, before, after)
Exemplo n.º 22
0
 async def on_server_unavailable(self, server):
     plugins.broadcast_event(self, 19, server)
Exemplo n.º 23
0
 async def on_server_role_update(self, before, after):
     plugins.broadcast_event(self, 17, before, after)
Exemplo n.º 24
0
 async def on_member_ban(self, member):
     plugins.broadcast_event(self, 21, member)
Exemplo n.º 25
0
 async def on_server_unavailable(self, server):
     plugins.broadcast_event(self, 19, server)
Exemplo n.º 26
0
 async def on_channel_create(self, channel):
     plugins.broadcast_event(self, 8, channel)
Exemplo n.º 27
0
 async def on_voice_state_update(self, before, after):
     plugins.broadcast_event(self, 20, before, after)
Exemplo n.º 28
0
 async def on_message_edit(self, before, after):
     plugins.broadcast_event(self, 6, before, after)
Exemplo n.º 29
0
 async def on_member_ban(self, member):
     plugins.broadcast_event(self, 21, member)
Exemplo n.º 30
0
 async def on_channel_create(self, channel):
     plugins.broadcast_event(self, 8, channel)
Exemplo n.º 31
0
 async def on_member_unban(self, server, user):
     plugins.broadcast_event(self, 22, server, user)
Exemplo n.º 32
0
 async def on_member_join(self, member):
     plugins.broadcast_event(self, 10, member)
Exemplo n.º 33
0
 async def on_typing(self, channel, user, when):
     plugins.broadcast_event(self, 23, channel, user, when)
Exemplo n.º 34
0
 async def on_server_join(self, server):
     plugins.broadcast_event(self, 12, server)
Exemplo n.º 35
0
 async def on_message_edit(self, before, after):
     plugins.broadcast_event(self, 6, before, after)
Exemplo n.º 36
0
 async def on_server_role_create(self, server, role):
     plugins.broadcast_event(self, 15, server, role)
Exemplo n.º 37
0
Arquivo: core.py Projeto: sasma/JshBot
        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
Exemplo n.º 38
0
 async def on_server_role_update(self, before, after):
     plugins.broadcast_event(self, 17, before, after)
Exemplo n.º 39
0
Arquivo: core.py Projeto: sasma/JshBot
 def dispatch(self, event, *args, **kwargs):
     super().dispatch(event, *args, **kwargs)
     plugins.broadcast_event(self, 'on_' + event, *args, **kwargs)
Exemplo n.º 40
0
 async def on_voice_state_update(self, before, after):
     plugins.broadcast_event(self, 20, before, after)
Exemplo n.º 41
0
 async def on_message_delete(self, message):
     plugins.broadcast_event(self, 5, message)
Exemplo n.º 42
0
 async def on_member_unban(self, server, user):
     plugins.broadcast_event(self, 22, server, user)
Exemplo n.º 43
0
 async def on_socket_raw_receive(self, msg):
     plugins.broadcast_event(self, 3, msg)
Exemplo n.º 44
0
Arquivo: core.py Projeto: sasma/JshBot
        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))
            '''
Exemplo n.º 45
0
 async def on_channel_delete(self, channel):
     plugins.broadcast_event(self, 7, channel)
Exemplo n.º 46
0
Arquivo: core.py Projeto: sasma/JshBot
        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)
Exemplo n.º 47
0
 async def on_error(self, event, *args, **kwargs):
     plugins.broadcast_event(self, 1, event, *args, **kwargs)
Exemplo n.º 48
0
        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)
Exemplo n.º 49
0
        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)
Exemplo n.º 50
0
 async def on_socket_raw_receive(self, msg):
     plugins.broadcast_event(self, 3, msg)
Exemplo n.º 51
0
 async def on_error(self, event, *args, **kwargs):
     plugins.broadcast_event(self, 1, event, *args, **kwargs)
Exemplo n.º 52
0
        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
Exemplo n.º 53
0
 async def on_socket_raw_send(self, payload):
     plugins.broadcast_event(self, 4, payload)
Exemplo n.º 54
0
 def dispatch(self, event, *args, **kwargs):
     super().dispatch(event, *args, **kwargs)
     plugins.broadcast_event(self, 'on_' + event, *args, **kwargs)
Exemplo n.º 55
0
 async def on_message_delete(self, message):
     plugins.broadcast_event(self, 5, message)