1

I'm trying to solve a problem, I have many commands and events in my discord bot (minigames) and I want users to be able to use only one command at a time so if there is one minigame already running, other commands or events can't be used. So I created the on_command variable and at the beginning of every command and event the variable changes to 1 and at the end of every command and event the variable changes back to 0. So if someone tries to use some command or event and the variable is 1, the bot just sends him that he can't use that command now. But for some reason, it doesn't work and I get the next error:

discord.ext.commands.errors.CommandInvokeError: Command raised an exception: UnboundLocalError: local variable 'on_command' referenced before assignment

My code:

on_command = 0

@bot.command()
@commands.cooldown(1, 180.0, commands.BucketType.guild)
async def question(msg):
    if msg.channel.id != channel:
        return
    if on_command == 1:
        await msg.send("Another command or event is running")
        return
    on_command = 1
    ....
    ....
    ....
    on_command = 0
Axisnix
  • 2,822
  • 5
  • 19
  • 41
garadosix
  • 87
  • 2
  • 8

2 Answers2

2

Use python's global

on_command = 0

@bot.command()
@commands.cooldown(1, 180.0, commands.BucketType.guild)
async def question(msg):
    global on_command 
    if msg.channel.id != channel:
        return
    if on_command == 1:
        await msg.send("Another command or event is running")
        return
    on_command = 1
    ....
    ....
    ....
    on_command = 0
Axisnix
  • 2,822
  • 5
  • 19
  • 41
2
  1. You should rename the on_command variable to something else (discord.py uses on_command event, if you're going to use it in the future you should rename it).
  2. The on_command variable never really changes, you should use the global keyword.
  3. Your current solution lets only 1 user at a time to use the bot (if any other person wants to use it, he won't be able)

One solution is to create a bot var called current_users (or whatever), make a global check that's called before any command, and check if the user can play, when the command is completed remove him from the variable

bot.current_users = set() # Empty set

@bot.check
async def is_user_playing(ctx):
    if not ctx.author in bot.current_users:
        # The user can play a game, add him to the set
        bot.current_users.add(ctx.author)
        return True

    return False


@bot.command()
async def test(ctx):
    """Command for testing"""
    await ctx.send("Starting game...")
    await asyncio.sleep(100)
    await ctx.send("Finishing game... You can play other games now")


@bot.event
async def on_command_completion(ctx):
    # Removing the user when he finishes playing a game
    bot.current_users.remove(ctx.author)


@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.CheckFailure):
        # The user can't play games, send the message here
        await ctx.send("You can't play more games, please finish your current game")

Note: events it will work differently, you'll need to do it manually

@bot.event # Example event
async def on_message(message):
    if message.author in bot.current_users:
        return await message.channel.send("You can't play games...")

    bot.current_users.add(message.author)
    # ...
    bot.current_users.remove(message.author)


# Another example event
@bot.event
async def on_reaction_add(reaction, user):
    if user in bot.current_users:
        return await reaction.message.channel.send("...")

    bot.current_users.add(user)
    # ...
    bot.current_users.remove(user)

Reference:

Łukasz Kwieciński
  • 14,992
  • 4
  • 21
  • 39