VeryBakedPotato
VeryBakedPotato

Reputation: 25

How do I correctly use tasks/the event loop in a discord.py bot?

I read the answer here and tried to adapt it for my own usage: How to add a function to discord.py event loop?

My situation is as follows:

I'm developing a discord bot in which I have some commands users can run. These commands have a possibility of erroring, in which case I do client.loop.create_task(errorReportingFunction()) which reports the error to me by messaging me. I also have a command which uses asyncio.create_task() instead.

However I'm having memory leaks which vary from mild to crashing the bot after prolonged usage which leads me to think that I'm not using the tasks system correctly. Should I be cleaning up after the tasks I create and deleting them somehow after I'm done using them? Or is there a system that does that automatically.

I'm also not sure how asyncio.create_task() and client.loop.create_task() differ so I'd appreciate some advice on which to use when, or if they're basically the same.

Upvotes: 1

Views: 1165

Answers (1)

Shunya
Shunya

Reputation: 2474

I believe that what you want is to do something (for example sending a message) when an error happens. If so, there are better ways to handle this in discord.py instead of creating tasks.

In the case that you want to control this only for a certain function, you could create an error handler to track the error raised from that function and send you a message whenever one happens:

@bot.command()
async def info(ctx, *, member: discord.Member):
    """Tells you some info about the member."""
    fmt = '{0} joined on {0.joined_at} and has {1} roles.'
    await ctx.send(fmt.format(member, len(member.roles)))

@info.error
async def info_error(ctx, error):
    if isinstance(error, commands.BadArgument):
        await ctx.send('I could not find that member...')
    # Send a message to the bot owner about the error
    await self.bot.get_user(self.bot.owner_id).send('There has been an error in the command')

Although a common practice in discord bots is to have an error handling cog, which would allow you to centralize all the error handling in a single function. It could be something like this:

class ErrorCog(commands.Cog, name='Error'):
    '''Cog in charge of the error handling functions.'''

    def __init__(self, bot):
        self.bot = bot

    @commands.Cog.listener()
    async def on_command_error(self, ctx, error):
        '''Event that takes place when there is an error in a command.
    
        Keyword arguments:
        error -- error message '''

        error = getattr(error, 'original', error)
        
        # Wrong command
        if isinstance(error, commands.CommandNotFound):
            message = 'This is not a valid command'
            return await ctx.send(message)

        # Command is on cooldown
        elif isinstance(error, commands.CommandOnCooldown):
            if ctx.author.id is self.bot.owner_id:
                ctx.command.reset_cooldown(ctx)
                return await ctx.command.reinvoke(ctx)
            cooldowns = {
                commands.BucketType.default: f'for the whole bot.',
                commands.BucketType.user: f'for you.',
                commands.BucketType.guild: f'for this server.',
                commands.BucketType.channel: f'for this channel.',
                commands.BucketType.member: f'cooldown for you.',
                commands.BucketType.category: f'for this channel category.',
                commands.BucketType.role: f'for your role.'
            }
            return await ctx.send(f'The command `{ctx.command}` is on cooldown {cooldowns[error.cooldown.type]} ')
        
        # Bot lacks permissions.
        elif isinstance(error, commands.BotMissingPermissions):
            permissions = '\n'.join([f'> {permission}' for permission in error.missing_perms])
            message = f'I am missing the following permissions required to run the command `{ctx.command}`.\n{permissions}'
            try:
                return await ctx.send(message)
            except discord.Forbidden:
                try:
                    return await ctx.author.send(message)
                except discord.Forbidden:
                    return

        # Here you need to add more instances
        # of errors according to your needs

def setup(bot):
    bot.add_cog(ErrorCog(bot))

Upvotes: 1

Related Questions