Logman
Logman

Reputation: 131

Schedule Asyncio task to execute every X seconds?

I'm trying to create a python discord bot that will check active members every X seconds, and award members with points for their time online. I'm using asyncio to handle the chat commands and that is all working. My issue is finding a way to schedule this checking of active members every X seconds with async

I've read the asnycio documentation but this is my first time working with it and I'm having a hard time wrapping my head around tasks and loops and co routines and etc.

@client.event
async def on_message(message):

    # !gamble command
    if message.content.startswith('!gamble'):

        ...code that works....

    # !help command
    elif message.content == '!help':

         ...code that works....

    # !balance command
    elif message.content == '!balance':

      ...code that works....

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

# Do this every X seconds to give online users +1 points
async def periodic_task():
      TODO

My goal is to have the bot be able to handle commands given to it through chat, while also triggering a function every X seconds unrelated to chat commands or events in the Discord server. I know how to make the code inside the function achieve my goal, just not how to trigger it

Upvotes: 13

Views: 18323

Answers (2)

marcoc88
marcoc88

Reputation: 791

If you want to make sure the execution time doesn't cause drift in the intervals, you can use asyncio.gather.

import asyncio, time, random


start_time = time.time()


async def stuff():
    await asyncio.sleep(random.random() * 3)
    print(round(time.time() - start_time, 1), "Finished doing stuff")


async def do_stuff_periodically(interval, periodic_function):
    while True:
        print(round(time.time() - start_time, 1), "Starting periodic function")
        await asyncio.gather(
            asyncio.sleep(interval),
            periodic_function(),
        )


asyncio.run(do_stuff_periodically(5, stuff))

The output then becomes:

0.0 Starting periodic function
0.5 Finished doing stuff
5.0 Starting periodic function
7.2 Finished doing stuff
10.0 Starting periodic function
10.1 Finished doing stuff
15.0 Starting periodic function
17.9 Finished doing stuff

As you can see the execution time of the periodic function called doesn't affect the start time of the new interval.

Upvotes: 18

balki
balki

Reputation: 27664

async def do_stuff_every_x_seconds(timeout, stuff):
    while True:
        await asyncio.sleep(timeout)
        await stuff()

And add this to loop.

task = asyncio.create_task(do_stuff_every_x_seconds(10, stuff))

When you no longer want to do that,

task.cancel()

Upvotes: 12

Related Questions