Scheduling an async function in Discord.py
Question:
I’ve been trying to schedule a discord.py function to be run every day at midnight.
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
I first tried it with schedule
schedule.every().day.at('00:00').do(send)
And then learned it can’t handle async functions. I then ran into an SO answer that explained how to do it with just Discord.py
iself. My code now looks like this:
import ...
load_dotenv()
client = discord.Client(intents=discord.Intents.all())
updays = [0]
...
@tasks.loop(hours=24)
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
@send.before_loop
async def before_send():
hour, minute = 0, 0
await client.wait_until_ready()
now = datetime.now()
future = datetime.datetime(now.year, now.month, now.day, hour, minute)
if now.hour >= hour and now.minute >= minute:
future += timedelta(days=1)
await asyncio.sleep((future - now).seconds)
if __name__ == '__main__':
client.run(os.getenv('TOKEN'))
send.start()
But it doesn’t do anything. The rest of the bot works, but nothing happens at the specified time.
Answers:
I think problem with starting tasks
was already in some question.
client.run()
runs loop which works all time and your code can’t execute
send.start()
– but even if you run it before client.run()
it will not work because it needs already working loop.
You should start task in on_ready()
@client.event
def on_ready()
send.start() # without `await`
if __name__ == '__main__':
client.run(os.getenv('TOKEN'))
EDIT:
I think you could set it with loop(time=datetime.time)
without @send.before_loop
Important is to use correct timezone
. If you don’t use it then it will run it with UTC.
#tz = datetime.timezone.utc # Europe/London (UTC)
#tz = datetime.timezone(datetime.timedelta(hours=2)) # Europe/Warsaw (CEST)
tz = datetime.datetime.now().astimezone().tzinfo # local timezone
print('timezone:', tz)
midnight = datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz)
print('midnight:', midnight, midnight.tzinfo)
@tasks.loop(time=midnight)
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
It should run also with list loop(time=[time1, time2, ...])
midnight = datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz)
noon = datetime.time(hour=12, minute=0, second=0, microsecond=0, tzinfo=tz)
@tasks.loop(time=[midnight, noon])
async def send():
# ...code...
I’ve been trying to schedule a discord.py function to be run every day at midnight.
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
I first tried it with schedule
schedule.every().day.at('00:00').do(send)
And then learned it can’t handle async functions. I then ran into an SO answer that explained how to do it with just Discord.py
iself. My code now looks like this:
import ...
load_dotenv()
client = discord.Client(intents=discord.Intents.all())
updays = [0]
...
@tasks.loop(hours=24)
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
@send.before_loop
async def before_send():
hour, minute = 0, 0
await client.wait_until_ready()
now = datetime.now()
future = datetime.datetime(now.year, now.month, now.day, hour, minute)
if now.hour >= hour and now.minute >= minute:
future += timedelta(days=1)
await asyncio.sleep((future - now).seconds)
if __name__ == '__main__':
client.run(os.getenv('TOKEN'))
send.start()
But it doesn’t do anything. The rest of the bot works, but nothing happens at the specified time.
I think problem with starting tasks
was already in some question.
client.run()
runs loop which works all time and your code can’t execute
send.start()
– but even if you run it before client.run()
it will not work because it needs already working loop.
You should start task in on_ready()
@client.event
def on_ready()
send.start() # without `await`
if __name__ == '__main__':
client.run(os.getenv('TOKEN'))
EDIT:
I think you could set it with loop(time=datetime.time)
without @send.before_loop
Important is to use correct timezone
. If you don’t use it then it will run it with UTC.
#tz = datetime.timezone.utc # Europe/London (UTC)
#tz = datetime.timezone(datetime.timedelta(hours=2)) # Europe/Warsaw (CEST)
tz = datetime.datetime.now().astimezone().tzinfo # local timezone
print('timezone:', tz)
midnight = datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz)
print('midnight:', midnight, midnight.tzinfo)
@tasks.loop(time=midnight)
async def send():
updays[0] += 1
await client.get_channel(XXXXXXXXXXXXXXXXX).send(f"It has been {updays[0]} days.")
It should run also with list loop(time=[time1, time2, ...])
midnight = datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz)
noon = datetime.time(hour=12, minute=0, second=0, microsecond=0, tzinfo=tz)
@tasks.loop(time=[midnight, noon])
async def send():
# ...code...