How to set up a Discord bot to execute a function daily at a specified time using Python?

I’m trying to schedule a function in my Python Discord bot to run every day at a specific time. While the bot can trigger actions every 24 hours, I want it to start execution at a certain hour and minute.

I’ve looked into using libraries such as schedule and aioscheduler, but I’ve faced challenges in getting them to work correctly. The bot operates without any exceptions, yet my defined function roletask() doesn’t appear to execute. During testing, I have it set to run every 5 seconds.

This is my current implementation:

import discord
import random
import asyncio
import schedule
import threading
import time
from datetime import datetime, timedelta
from discord.ext import commands, tasks
from discord.utils import get

bot = commands.Bot(command_prefix='[]')
bot.remove_command("help")
guild = bot.get_guild(607452358544195588)
role_id = 738129548805275710
ROLE_NAME1 = "q-and-a"
ROLE_NAME2 = "tunes"

@tasks.loop(seconds=5)
async def roletask():
    print("Executing task")
    channel = bot.get_channel(681179611253571688)
    await channel.send('<@&738129548805275710> You are part of the test role!')

@roletask.before_loop
async def before_my_task():
    hour = 23
    minute = 23
    await bot.wait_until_ready()
    now = datetime.now()
    future = datetime.datetime(now.year, now.month, now.day, hour, minute)
    if datetime.hour >= hour and datetime.minute > minute:
        future += timedelta(days=1)
    await asyncio.sleep((future - now).seconds)

roletask.start()

@bot.event
async def on_ready():
    await bot.change_presence(status=discord.Status.online, activity=discord.Game('[]help'))
    print('We have logged in as {0.user}'.format(bot))

What mistakes might I be making in my scheduling strategy? I would appreciate any insights or recommendations.

I ran into this exact problem when building my first Discord bot scheduler. The core issue is mixing different approaches - you’re using both the tasks.loop decorator and manual delay calculations which conflict with each other. Your before_loop function calculates when to start, but then the loop continues every 5 seconds afterwards instead of waiting 24 hours. What worked for me was ditching the manual timing entirely and using tasks.loop(time=datetime.time(23, 23)) which handles daily execution automatically. You can also pass multiple time objects if you need it to run at different times throughout the day. The discord.py task system is designed specifically for this kind of scheduling and handles timezone issues and leap seconds properly. Make sure to move your roletask.start() call inside the on_ready event though, otherwise you might get errors if the bot isn’t fully connected when the task tries to access channels or guilds.

There’s a logical error in your before_loop function that’s causing the issue. The main problem is in this line: if datetime.hour >= hour and datetime.minute > minute: - you’re checking datetime.hour and datetime.minute instead of now.hour and now.minute. This should be if now.hour >= hour and now.minute > minute:.

Additionally, using .seconds on the timedelta object only gives you the seconds component, not the total seconds. For delays longer than a day, you should use .total_seconds() instead.

I’d recommend simplifying your approach by calculating the next occurrence more reliably. Also, make sure you’re calling roletask.start() after the bot is ready, preferably in the on_ready event, rather than at module level. This ensures the bot client is properly initialized before starting the task loop.

Your implementation has the right idea but there’s a fundamental issue with how you’re handling the scheduling logic. The problem is that your loop is set to run every 5 seconds, but your before_loop function only calculates the initial delay - after that first execution, it will continue running every 5 seconds regardless of the time. I’ve dealt with similar scheduling requirements and found that using tasks.loop(hours=24) combined with a proper time calculation works much better. Instead of calculating the delay in before_loop, you should check the current time within the task function itself and only execute your code when it matches your target time. Alternatively, you could stick with the current approach but change your loop to tasks.loop(hours=24) and fix the datetime references as mentioned in the previous answer. The key is ensuring that once your task runs at the specified time, it waits exactly 24 hours before running again, not just continuing with short intervals.

The issue stems from a bug in your datetime calculation logic. You’re using datetime.datetime(now.year, now.month, now.day, hour, minute) which should just be datetime(now.year, now.month, now.day, hour, minute) since you already imported datetime. Also, the condition check has incorrect references as others mentioned, but there’s another problem - you’re mixing asyncio sleep with a loop that runs every 5 seconds, which defeats the purpose of the initial delay calculation. Once your before_loop completes, the task will just keep firing every 5 seconds forever. I’ve found success using a hybrid approach where you keep your current setup but change the loop interval to 60 seconds and add a time check inside the roletask function itself. This way you can verify the current time matches your target before actually executing the channel send operation. It’s less elegant than the pure tasks.loop solutions others suggested, but it gives you more control over the execution logic and is easier to debug when things go wrong.

honestly the easiest fix is just removing that entire before_loop mess and using tasks.loop(time=discord.utils.utcnow().replace(hour=23, minute=23)) instead. way cleaner than calculating delays manually and handles the daily scheduling automatically without all those datetime headaches