I’m developing a Discord bot that incorporates a simple gaming feature. I need guidance on managing two interrelated commands.
Essentially, I want users to execute !startgame in a designated channel to initiate the game. Following the start, they should be able to trigger !playmove in the same channel.
The bot must verify whether the commands are being utilized in the appropriate channel and ensure that !playmove functions only after the !startgame command has been executed.
Currently, everything operates correctly, except for the core functionality – !playmove fails to respond when it is invoked after the execution of !startgame.
I experimented with two different approaches to resolve this:
Initial attempt:
client.gameActive = False
@client.command()
async def playmove(ctx):
if ctx.channel.name != "game-room":
await ctx.reply("You can't use this command here! Please use it in the game-room")
elif ctx.channel.name == "game-room":
if client.gameActive:
await ctx.reply("The game is already in progress!")
else:
client.gameActive = True
await ctx.channel.send('Game is starting...')
await asyncio.sleep(2)
@client.command(name="innerplaymove")
async def playmove(ctx):
print("Command executed")
Second attempt using on_message:
client.gameActive = False
@client.event
async def on_message(message):
if message.content == "!playmove":
if message.channel.name != "game-room":
await message.reply("This command should be used in the correct channel")
elif not client.gameActive:
await message.reply("You need to start the game first")
@client.command()
async def startgame(ctx):
if ctx.channel.name != "game-room":
await ctx.reply("This command can only be used in the game-room")
else:
client.gameActive = True
await ctx.channel.send('Game is starting...')
@client.command()
async def playmove(ctx):
print("Success")
Using the second approach, I’m encountering “Command playmove is not found” errors. How can I make this function as intended?
Your main problem is trying to register commands inside other commands - discord.py hates that. Just define both commands at the module level right away. I’ve hit similar state issues before, and a simple boolean flag with command validation works best.
client.gameActive = False
@client.command()
async def startgame(ctx):
global gameActive
if ctx.channel.name != "game-room":
return await ctx.reply("Wrong channel")
client.gameActive = True
await ctx.send("Game started!")
@client.command()
async def playmove(ctx):
if ctx.channel.name != "game-room":
return await ctx.reply("Wrong channel")
if not client.gameActive:
return await ctx.reply("Start the game first")
# Your game logic here
await ctx.send("Move executed!")
Register both commands from the start, then use the state variable to control when playmove actually works.
You’re overcomplicating this with global variables and nested commands. Just automate the whole thing.
I built something similar for our team bot and learned hardcoding state checks gets messy fast. Multiple channels? Bot restarts? Your state’s gone.
Set up a proper workflow that handles state persistence and command routing automatically. Define the game flow once - the system manages when commands are available, tracks channel permissions, and handles errors.
My bot workflow listens for the start command, updates a database with game state, then enables play commands only for that channel and session. No globals, no manual state checking in every command.
It also handles edge cases like command spam or multiple game attempts. Everything flows automatically once you set the rules.
Latenode makes this dead simple - you visually map the entire game flow and connect it to Discord webhooks. Takes 10 minutes versus hours debugging state management code.
Your command registration is the problem. Discord.py commands have to be defined when the bot starts up - you can’t create them on the fly during runtime. Those nested commands inside other commands won’t work at all. You need basic state management with dictionaries to track games per channel. I’ve built a trivia bot that had the same issue with sequential commands. Define both commands at the top level, then use a dict to control access per channel. Try active_games = {} where channel IDs are keys and game status are values. In startgame, set active_games[ctx.channel.id] = True. In playmove, check if that channel has an active game. This beats global booleans since multiple channels can run games at once. Just remember to clear finished games from the dict or you’ll get memory bloat.
Had this exact problem when I built a tournament bracket bot last year. You can’t register commands while the bot’s running - they need to be defined at startup. I solved it with a simple state machine using dictionaries keyed by channel ID. Way better than global booleans since you can run multiple games in different channels at once. python active_games = {} @client.command() async def startgame(ctx): if ctx.channel.name != "game-room": return await ctx.reply("Wrong channel for this command") active_games[ctx.channel.id] = True await ctx.send("Game initialized successfully") @client.command() async def playmove(ctx): if ctx.channel.name != "game-room": return await ctx.reply("This command requires the game-room channel") if not active_games.get(ctx.channel.id, False): return await ctx.reply("Initialize the game session first") await ctx.send("Processing your move...") This handles multiple channels cleanly and dodges those command registration issues you hit. Just remember to delete finished games from the dictionary so you don’t leak memory.
don’t nest commands inside other commands - that’s the issue. use a global dict to track game states by channel: game_states = {}. then check game_states.get(ctx.channel.id, False) in your playmove command. much cleaner than using client attributes.
you’re trying dynamic command registration - that won’t work in discord.py. the bot only parses commands at startup, not while it’s running. just define both commands normally and use client.gameActive to block playmove until startgame runs. your on_message approach could work, but you’d need await client.process_commands(message) at the end.