I have the following code:
self.current_task = self.bot.wait_for('message', check=check, timeout=10 * 60)
response = await self.current_task
However I want the bot.wait_for to cancel if a condition becomes true in another function within the same class. I've tried doing self.current_task.close(), however it still waits for a response/timeout before returning a nonetype.
You can have a variable which evaluates if the condition is met, in the check func simply check if it's met and raise an error and catch it in a try/except block
def check(m):
if self.is_condition_met: # Change the variable accordingly
raise # Whatever error you want
return # normal check
An example would be
class SomeCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.is_condition_met = False
#commands.command()
async def met(self, ctx):
self.is_condition_met = True
#commands.command()
async def foo(self, ctx):
await ctx.send("Please send a message")
def check(m):
if self.is_condition_met:
raise RuntimeError("condition is met")
return ctx.author == m.author
try:
message = await bot.wait_for("message", check=check, timeout=20.0)
except RuntimeError:
await ctx.send("Condition was met")
except asyncio.TimeoutError:
await ctx.send("Time is over")
If you invoke the foo command first, and then the met command the wait should be cancelled as soon as someone sends a message
Related
I have some code for a cog that looks something like this:
class Example(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.counter = 0
#commands.Cog.listener()
async def on_message(self, message):
print("Listener triggered")
self.counter += 1
#commands.group()
async def first(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send("Invalid subcommand")
#first.command()
async def second(self, ctx):
print("Command triggered")
await ctx.send(f"Current counter: {self.counter}")
When I run this code and send a message to my bot, second gets called before on_message. I have some code in second that expects on_message to be executed first, but I can't figure out a good way to make this happen. Suggestions?
check wait_for event.
and I think you are trying to make a bot count the number of valid uses of command. To do this, try:
def check(message):
#here put ur condition for message is a valide command
await client.wait_for('message', check=check)
I wanted to make a command that edits the bot's old message trought another command, example:
user: !cmds
bot: *commands list*
user: !next
bot: *edits old message into a new one*
I have tried many times to make it myself, but I failed all the times, can anyone help me?
How I tried to make it:
#client.command
async def test():
embed=discord.Embed(title="Page1")
await ctx.send(embed=embed)
#client.event
async def on_command(message):
embed2=discord.Embed(title="Page2")
await client.process_commands(message)
if message.content == '$next':
await message.edit(content=embed2)
The on_command event is called whenever a command is called, you didn't register a next command, that's one, second of all on_command doesn't take message argument, it takes the Context(ctx).
An idea to make what you're asking for is having a cog with an instance variable that references to the previous message, then edit it when the next command is called.
class SomeCog(commands.Cog):
def __init__(self, client):
self.client = client
self._previous_message = None
#commands.command()
async def test(self, ctx):
message = await ctx.send("I'll edit this message when you type `$next`")
self._previous_message = message
#commands.command()
async def next(self, ctx):
if self._previous_message is None: # Exiting if the `test` command wasn't called before, i.e the message is a NoneType
return await ctx.send("You need to invoke first `$test`")
await self._previous_message.edit(content="Edited the message")
client.add_cog(SomeCog(client))
I'm guessing you could do it with global or bot vars but this is the best way to do it, in my opinion.
I'm trying to create a code that takes a users id, then checks if it matches with one, if it does it sends a message...I keep getting the error TypeError: on_message() missing 1 required positional argument: 'message'...
#client.event
async def on_message(ctx,message):
member = message.author.id
if member == <userid>:
await ctx.send("yo whats up")
else:
return
await client.process_commands(message)
You're not defining your function the way it should be. on_message event only takes in one argument: message. You will have to work without the ctx argument in on_message events. By taking that into consideration, you can re-format your current function code from:
#client.event
async def on_message(ctx,message):
member = message.author.id
if member == <userid>:
await ctx.send("yo whats up")
else:
return
await client.process_commands(message)
to:
#client.event
# Only pass in "message" argument
async def on_message(message):
member = message.author.id
if member == <userid>:
# Use message.channel.send() instead of ctx.send()
await message.channel.send("yo whats up")
else:
return
await client.process_commands(message)
I'm making an Administration cog for my discord bot and my code wouldn't identify 'ctx'. PyCharm suggested to replace 'ctx' with 'self' and I have no idea what 'self' does. And from what PyCharm is saying, There are millions of other stuff which I have to write down what it is. PyCharm couldn't identify guild, send, author and channel and it also says that return ctx.author.guild_permissions.manage_messages is an unreachable code. As a note if this seems to be a really stupid question, I am a beginner who started 2 weeks ago.
As for the code:
class Administration(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print("Admin cog ready")
async def cog_check(self, ctx):
admin = get(ctx.guild.roles, name="Admin")
return admin in ctx.author.roles
return ctx.author.guild_permissions.manage_messages
#commands.command(aliases=["purge"])
async def clear(ctx, amount=3):
"""Clears 3 messages"""
await ctx.channel.purge(limit=amount)
#commands.command(pass_context=True)
async def giverole(ctx, user: discord.Member, role: discord.Role):
"""Gives a role to a user"""
await user.add_roles(role)
await ctx.send(f"hey {ctx.author.name}, {user.name} has been giving a role called: {role.name}")
#commands.command(aliases=['make_role'])
#commands.has_permissions(manage_roles=True)
async def create_role(ctx, *, name):
"""Creates a role"""
guild = ctx.guild
await guild.create_role(name=name)
await ctx.send(f'Role `{name}` has been created')
#commands.command(name="slap", aliases=["warn"])
async def slap(ctx, members: commands.Greedy[discord.Member], *, reason='no reason'):
"""Warns someone"""
slapped = ", ".join(x.name for x in members)
await ctx.send('{} just got slapped for {}'.format(slapped, reason))
def setup(client):
client.add_cog(Administration(client))
In classes, (unless it's a staticmethod or classmethod) you always pass self as the first argument.
#commands.command(aliases=["purge"])
async def clear(self, ctx, amount=3): # Note how I put `self` as the first arg, do the same in all commands in the cog
"""Clears 3 messages"""
await ctx.channel.purge(limit=amount)
Also, this will never work
async def cog_check(self, ctx):
admin = get(ctx.guild.roles, name="Admin")
return admin in ctx.author.roles
return ctx.author.guild_permissions.manage_messages
The function will end no matter what when it reaches the first return, you can simply use the AND or OR logical operator if you want to also evaluate the second return statement
async def cog_check(self, ctx):
admin = get(ctx.guild.roles, name="Admin")
return admin in ctx.author.roles and/or ctx.author.guild_permissions.manage_messages
So I want to be able to have my command only work in direct messages, can anyone help?
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('bb!help'):
await message.channel.send('Huge long commands list')
Use isinstance().
isinstance checks whether if an object has the specified type. For example, isinstance(3, int) checks if 3 is an integer (which it is and therefore returns True). In your case you could check if the message.channel is a DMChannel. The correct code would be:
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('bb!help') and isinstance(message.channel, discord.DMChannel):
await message.channel.send('Huge long commands list')
If you're using commands.Bot
#client.command()
#commands.dm_only()
async def command(ctx):
...
If you're using on_message
#client.event
async def on_message(message):
if isinstance(message.channel, discord.DMChannel):
# The message is sent in the dm's of the bot