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".
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.
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:
- Create and Start Task
- Wait's until Bot is ready
- Get's the Guild obj from Guild ID
- Checks if the Guild is None
?tag get none
- If it isn't, Get the Channel obj in that Guild using the Channel ID
- Checks if Channel is None
?tag get none
- If is isn't, Send the message in that channel.
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:
- Create the task called "mytask"
- Start the task using "mytask.start"
- The task invokes the "before_loop" where it will wait until the bot is ready.
- Get's the Guild obj from Guild ID
- Checks if the Guild is None
?tag get none
- If it isn't, Get the Channel obj in that Guild using the Channel ID
- Checks if Channel is None
?tag get none
- If is isn't, Send the message in that channel.
discord.ext.tasks
is just an asyncio task helper noted in the docs.
Function | Docs |
---|---|
discord.ext.tasks | Click Here |
loop.create_task | Click Here |
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 of background tasks are in the discord.py github under examples
directory.
ext.tasks
: Click Hereloop/asyncio.create_task
: Click Here
Please correct me if I am wrong... I am sorry :(