Skip to content

Instantly share code, notes, and snippets.

@proguy914629bot
Last active June 9, 2021 14:05
Show Gist options
  • Save proguy914629bot/d19f68bbcd2ff71f11ce1fffcef8aecb to your computer and use it in GitHub Desktop.
Save proguy914629bot/d19f68bbcd2ff71f11ce1fffcef8aecb to your computer and use it in GitHub Desktop.

NOTICE:

You should understand the code below. DO NOT COPY AND PASTE.

How to do stuff when the bot is ready?

So you want to do stuff when the bot is ready huh? Well docs say that on_ready triggeres multiple times (more info on ?tag orp), So we are going to do a work-around.

Note for changing presence of bot, you are good to go with activity and status kwarg in ur Bot constuctor such as:

bot = commands.Bot(activity=discord.Game("A Cool Game!"), status=discord.Status.dnd)
# More info on this is in "?tag orp".
# For a list of activities and status, you can see more on "?tag activity" and "?tag status".

Using "loop.create_task" or "ext.tasks"

We can use a loop.create_task or ext.tasks (discord.ext.tasks) to help us.

Let's say we want to get a channel in a guild using it's id and send it to that channel saying that "I am online!".

I'll give you an example for both ext.tasks and loop.create_task for this one.

Using loop.create_task:

async def mytask(): # Our task function. Pass `self` as a function if this is in a cog/class.
    await bot.wait_until_ready() # Wait's until the bot is ready before doing the things below.
    guild = bot.get_guild(guild_id) # Get a guild to get the channel. Optionally I guess you can use bot.get_channel...
    if guild is not None:
        channel = guild.get_channel(channel_id) # Grabs the TextChannel obj of the channel.
        if channel is not None:
            await channel.send("I am online!")

# Creating and Starting the task
# To create and start the task, you can use bot.loop.create_task to do it.
# This should be anywhere but cannot be under Client.run (if ur doing this in ur bot file)
# If ur doing this in a cog, it's mostly better to put it in ur __init__ function. 
# Note if you do put it in ur __init__ function, the task will trigger when you reload the cog and/or when you unload and load it back as it gets triggered in your setup function that Discord.Py needs to add the cog class.
bot.loop.create_task(mytask()) # Replace mytask with your task function.

The logic here is just:

  1. Create and Start Task
  2. Wait's until Bot is ready
  3. Get's the Guild obj from Guild ID
  4. Checks if the Guild is None ?tag get none
  5. If it isn't, Get the Channel obj in that Guild using the Channel ID
  6. Checks if Channel is None ?tag get none
  7. If is isn't, Send the message in that channel.

Using ext.tasks (discord.ext.tasks):

from discord.ext import tasks # Import the tasks module.

@tasks.loop(count=1) # Make sure that it only triggeres once, cause we want it to only trigger when the bot is online.
async def mytask(): # Our task function. Pass `self` as a function if this is in a cog/class.
    guild = bot.get_guild(guild_id)
    if guild is not None:
        channel = guild.get_channel(channel_id)
        if channel is not None:
            await channel.send("I am online!")

@mytask.before_loop # This triggers before the task starts. So we can wait the bot to be ready in here instead.
async def before_mytask():
    await bot.wait_until_ready() # Wait's until the bot is ready before getting the guild, getting the channel and sending the message

# Creating and Starting the task
# To create and start the task, you can use task.start (in this case, mytask.start) to do it.
# This should be anywhere but cannot be under Client.run (if ur doing this in ur bot file)
# If ur doing this in a cog, it's mostly better to put it in ur __init__ function. 
# Note if you do put it in ur __init__ function, the task will trigger when you reload the cog and/or when you unload and load it back as it gets triggered in your setup function that Discord.Py needs to add the cog class.
mytask.start()

Again, the logic here is just:

  1. Create the task called "mytask"
  2. Start the task using "mytask.start"
  3. The task invokes the "before_loop" where it will wait until the bot is ready.
  4. Get's the Guild obj from Guild ID
  5. Checks if the Guild is None ?tag get none
  6. If it isn't, Get the Channel obj in that Guild using the Channel ID
  7. Checks if Channel is None ?tag get none
  8. If is isn't, Send the message in that channel.

discord.ext.tasks is just an asyncio task helper noted in the docs.

Documentation:

Function Docs
discord.ext.tasks Click Here
loop.create_task Click Here

Using a decorator:

There are idk, maybe lots of decorators that can do this for you, but personally, I'd like to have my own deco for my own bot/file. Here is the deco that I use if you are interested... (Thanks to Juuzou Suzuya Zeno#2678 for suggesting this.)

import functools

def run_only_once(func):
    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        if wrapped.runned == False:
            # The function isn't runned before. So we will change
            # wrapped.runned to True so it doesn't hit this if statement again and just go to the else.
            wrapped.runned = True
            return func(*args, **kwargs) # Calls the function.
        else:
            # The function was runned before. You can, idk, raise an error, or return some other stuff. For now, I will return None.
            return None
    wrapped.runned = False # Makes it False at first.
    return wrapped

# Here is an example.
@run_only_once
def myfunction():
    return "Called."
    
myfunction() # "Called."
myfunction() # None
myfunction() # None
...

This might be worst than making tasks, cause on_ready is supposed to trigger multiple times when the bot is reconnecting and/or whenever a RESUME request fails.

Examples:

Examples of background tasks are in the discord.py github under examples directory.

Note:

Please correct me if I am wrong... I am sorry :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment