I built a working Discord bot but it runs really slow. After some research I learned that putting too much code in the on_ready event isn’t good practice. I also switched from using Client to Bot.
I want to set up regular tasks in cogs like updating member roles daily or monthly. But I’ve never used cogs before and I’m confused about the setup. Do I need separate Python files for each cog that I import to main.py? How do I start them properly?
Main questions:
How do I move my on_ready code to cogs?
Should I use multiple cogs for different purposes?
Do multiple cogs run faster or slower?
How do I handle button callbacks in cogs?
Here’s my current code structure:
import discord
from discord.ext import commands, tasks
from discord import app_commands
import os
import datetime
import random
import string
intents = discord.Intents.all()
client = commands.Bot(command_prefix='?', intents=intents)
# Global variables
server = discord.Guild
check_emoji = discord.Emoji
folder_emoji = discord.Emoji
return_emoji = discord.Emoji
verify_channel = discord.TextChannel
code_channel = discord.TextChannel
inactive_role = discord.Role
member_role = discord.Role
helper_role = discord.Role
owner_role = discord.Role
access_codes = []
# Basic cog attempt
class TaskCog(commands.Cog):
def __init__(self, client):
self.client = client
self.daily_task.start()
def cog_unload(self):
self.daily_task.cancel()
@tasks.loop(hours=24)
async def daily_task(self):
print("Running daily maintenance!")
@client.event
async def on_ready():
print(f'Bot is ready: {client.user}')
# Initialize server objects
global server
server = discord.utils.get(client.guilds, id=1234567890123456789)
global check_emoji
check_emoji = await server.fetch_emoji(1111111111111111111)
global verify_channel
verify_channel = client.get_channel(2222222222222222222)
global inactive_role
inactive_role = discord.utils.get(server.roles, name="inactive")
# Setup file browser for each category
for cat in server.categories:
if "course" in cat.name.lower():
for ch in cat.channels:
old_messages = [msg async for msg in ch.history()]
for old_msg in old_messages:
if old_msg.author == client.user:
await old_msg.delete()
await create_file_browser(ch, cat.name)
# Update all member roles on startup
for user in server.members:
if user != client.user:
await check_user_access(user)
async def create_file_browser(channel, category):
# File browser logic here
pass
async def check_user_access(user):
# Role checking logic here
pass
client.run('TOKEN')
I tried using client.add_cog() but got async errors. When I put it in on_ready it seems to work but doesn’t actually do anything.
Update: I got one cog working but I’m still confused about best practices. Should I split things into separate cogs like startup tasks, daily maintenance, and commands? Any help would be great!
Your performance problems aren’t just about cogs - you’re running heavy operations in on_ready that shouldn’t be there. Stop manually coding role updates and file browser setups. Automate it instead.
I use Latenode for exactly this. Set up workflows that check member roles on a schedule, not at startup. Create triggers for Discord events that handle file browser updates in the background. Your bot starts instantly because the heavy work happens in automated workflows.
Keep one main cog for Discord interactions and let Latenode handle data processing. When someone joins or needs role updates, send a webhook to Latenode. It processes everything and sends results back.
Turn your daily tasks into Latenode scheduled workflows - member role audits, file browser maintenance, inactive user cleanup. Everything runs automatically without your bot lifting a finger.
This scales way better than splitting into multiple cogs. Your bot stays responsive because it just handles Discord events instead of processing data.
don’t load everything in on_ready - that’s what’s killing your speed. move your server setup to a separate init method and call it from a cog listener. i had the same problem and fixed it with bot.wait_until_ready() before any discord api calls.
yea, multiple cogs work way better than cramming everything into on_ready. split ur functions into separate files - startup.py, tasks.py, commands.py, whatever makes sense. then just await bot.load_extension('startup') in ur main file. those async errors ur seeing? u probably forgot to add await when loading the cogs.
Your problem is blocking the startup with sync operations in on_ready. I hit the same issue when I started using cogs - moving initialization to a dedicated setup cog fixed most of my performance problems.
Make a separate initialization cog that handles server object fetching after the bot’s ready. Use @commands.Cog.listener() instead of @client.event for on_ready inside the cog. You can properly await async operations without blocking the main thread.
For file browser setup and member role checks, try a queue system or batch these operations instead of running everything at once. I process these in chunks of 10-20 users with small delays between batches to avoid rate limiting.
For cog organization, I keep startup tasks separate from recurring ones. Your daily maintenance cog looks good but make sure you call await self.client.wait_until_ready() before starting loops that interact with Discord objects.
Your global variables approach is creating way more problems than just performance issues. When Discord objects get invalidated or your bot reconnects, those globals turn into stale references that’ll break everything.
Don’t store server objects globally - fetch them when you need them inside your cog methods. Use self.client.get_guild() and cache results temporarily if you’re hitting them multiple times in the same operation. This cuts out the on_ready dependency and makes your code way more resilient.
For button callbacks in cogs, define them as methods in the same cog class and pass self when you create the view. Your callback can then access all the cog’s methods and the client through self.client.
Yeah, definitely use separate Python files for each major feature. Make a cogs folder with individual files like maintenance.py, verification.py, etc. Load them in your main file with await client.load_extension('cogs.maintenance'). Keeps everything organized and debugging becomes much easier when stuff breaks.