I understand why the error check is printed, but I don't understand why despite the fact that the user calling does not have the mod label, it runs the function anyway, maybe I don't really understand decorators (user also has a token role but I commented that part out for this test)
# token check
#bot.command(name='wallet', help='display if a token is available for user')
#commands.has_role('mod') # remove later
# #commands.has_any_role('PBToken 3', 'PBToken 6')
async def token_check(ctx):
name = ctx.author.name
response = name + ', You have a token available!'
await ctx.send(response)
# error message if user has no token currently
#bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.errors.MissingRole):
name = ctx.author.name
# await ctx.send(name + ', you currently do not have a token, keep leveling up!')
await ctx.send('error check')
Here is the output:
Intead of using the mod role, why don't you run it using the check for whether the user has server administrator. To do that, all you do would be to replace:
#commands.has_role('mod')
with:
#commands.has_guild_permissions(administrator=True)
This would make the command only run if the specified user has server admin.
Please feel free to reply if you need any more help! :)
Related
Seemingly simple question but I'm stuck on how to solve it.
I use discord.py to login to my account and I want to DM a user by inputting his user_id .
import discord
class MyClient(discord.Client):
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
client = MyClient()
client.run('token')
async def send_message(user_id):
user = client.get_user(user_id)
await user.send('My message')
When I run this python file in my python shell, it would print the "Logged in as ..." success message and it would hang up. It wouldn't let me type any other command.
I simply want to run the send_message function with a unique user_id, so I can DM a particular user.
How do I do this?
That's because client.run is blocking, so any code below it won't be executed while the bot is running. The best way to avoid this problem is by moving the function somewhere above it, like in the client subclass.
I don't know exactly where you want to execute this function from, so I would suggest that you run it from on_ready for now.
class MyClient(discord.Client):
async def on_ready(self):
print(f'Logged in as {self.user} (ID: {self.user.id})')
await self.send_message(123456) # DM a user with id "123456" as soon as the bot is ready
async def send_message(self, user_id):
user = self.get_user(user_id)
await user.send('My message')
client = MyClient()
...
If you want to execute this command from Discord, here is a good walkthrough.
With this I'm wanting to have the reason work, I would like it similar to the *ban command where you have a reason, but instead of it logging in audit logs the reason you give replaces {reason} in the embed. It doesn't throw up any error codes when I try it out but it doesn't send the embed to my dm's.
#Command to send a user their denied outcome
#bot.command()
async def deny(ctx, *, user : discord.User, reason):
await ctx.message.add_reaction(f'✅')
embed = Embed(title="Application Denied",
description=f"Hello {user.name}, your application is being denied due to {reason}. Feel free to re-apply in 2 weeks, we wish you luck!",
color=0xffffff,
timestamp=datetime.utcnow())
embed.set_image(url="https://media.discordapp.net/attachments/935059565588930601/946167642207686686/rejected-stamp.gif")
embed.set_footer(text=f'Denied By {ctx.author.name} | {ctx.guild.name}', icon_url="https://media.discordapp.net/attachments/935059565588930601/945768014958178324/ezgif-1-5c3f7a6a54.gif?width=778&height=778")
await user.send(embed=embed)
#bot.command()
async def deny(ctx, user : discord.User,*,reason = "No reason was provided"):
reason = "".join(reason)
embed = discord.Embed(title="Application Denied",
description=f"Hello {user.name}, your application is being denied due to {reason}. Feel free to re-apply in 2 weeks, we wish you luck!",
color=0xffffff,
timestamp=datetime.utcnow())
embed.set_image(url="https://media.discordapp.net/attachments/935059565588930601/946167642207686686/rejected-stamp.gif")
embed.set_footer(text=f'Denied By {ctx.author.name} | {ctx.guild.name}', icon_url="https://media.discordapp.net/attachments/935059565588930601/945768014958178324/ezgif-1-5c3f7a6a54.gif?width=778&height=778")
await ctx.message.add_reaction(f'✅')
await user.send(embed=embed)
'''
#client.event
#commands.has_role('xrole')
async def on_message(message):
#Commands
if message.content.startswith("!set"):
credits_message = message.content.split(" ", 2)
user_name = credits_message[1]
credit_add_amount = credits_message[2]
db[user_name] = int(credit_add_amount)
#DB Stuff
await message.channel.send(user_name + "has been set with " + credit_add_amount + ", For further credit adgustment use !add")
'''
For some reason Users with or without the xrole can acces the bot even with the #commands restriction
User without xrole^
user with xrole ^
firstly don't use on_message for commands, it's much better to use #bot.command
second: this code should work well for you
#bot.command(pass_context=True)
#commands.has_role("xrole")
async def role_check(ctx):
await ctx.channel.send('''you have the role "xrole"''')
#role_check.error
async def info_error(ctx, error):
if isinstance(error, (commands.MissingRole, commands.MissingAnyRole)):
await ctx.channel.send('''you do not have the role "xrole"''')
Because Checks are for Commands, not for Events, so the has_role decorator will never be used. You should be using commands instead of manually parsing everything in on_message anyways, it makes a lot of stuff way less cumbersome.
Here's an explanation from the docs how to make proper commands instead: https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html
So I made a sub command that just sends a message back. I found that if the user types a non existing command it still displays the message from the sub command. It sounds confusing but here's an example.
User: ;id
Bot: This command help you find the name of anyone in the server!
User: ;id Slayer
Bot: Bob Miller
So while testing I found if the user sends some thing like ;id jgfjkag the bot still sends the original message for ;id which is "This command help you find the name of anyone in the server!". How would I make the bot send a specific message if the user trys to use a non existing sub command? Here's the code:
#commands.group()
async def id(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send("This command help you find the name of anyone in the server! ")
#id.command()
async def Slayer(self, ctx):
await ctx.send("Bob Miller")
#id.command()
async def Skel(self, ctx):
await ctx.send("John Dove")
Check ctx.subcommand_passed first:
#commands.group()
async def id(self, ctx):
if ctx.subcommand_passed is None:
await ctx.send("This command help you find the name of anyone in the server!")
elif ctx.invoked_subcommand is None:
await ctx.send(f"Subcommand '{ctx.subcommand_passed}' does not exist.")
This is my favorite method of doing it:
#commands.group(invoke_without_command=True)
async def group(self, ctx):
await ctx.send_help(ctx.command) # invokes the help command for the group
#group.command()
async def subcommand(self, ctx):
await ctx.send("You passed a valid subcommand!")
The invoke_without_command parameter, when set to true, will only call the command if there are no (valid) subcommands passed. This means that if the code inside if the group() function is called, you can safely assume that they didn't pass a subcommand (or passed an invalid one).
I also like to use ctx.send_help, since that will automatically list subcommands. You could replace this with something like:
await ctx.send("You didn't pass a valid subcommand!")
[EDIT]
More careful reading of your question revealed that the subcommand already has some functionality. This makes it more difficult, but this will work:
#commands.group(invoke_without_command=True, ignore_extra=False)
async def group(self, ctx):
await ctx.send("This is a group command!")
#group.error
async def on_group_error(self, ctx, error):
if isinstance(error, commands.TooManyArguments):
await ctx.send("You passed an invalid subcommand!")
#group.command()
async def subcommand(self, ctx):
await ctx.send("You passed a VALID subcommand!")
Here is what it would look like:
!group
Bot: This is a group command!
!group bla
Bot: You passed an invalid subcommand!
!group subcommand
Bot: You passed a VALID subcommand!
NOTE: The ignore_extra field raises the commands.TooManyArguments error, which then invokes the error handler allowing the bot to send the error message. Unfortunately, the default on_command_error hook will still be called. I would suggest ignoring the commands.TooManyArguments error inside of your main error handler to fix this.
Using Discord.py-rewrite, How can we diagnose my_background_task to find the reason why its print statement is not printing every 3 seconds?
Details:
The problem that I am observing is that "print('inside loop')" is printed once in my logs, but not the expected 'every three seconds'. Could there be an exception somewhere that I am not catching?
Note: I do see print(f'Logged in as {bot.user.name} - {bot.user.id}') in the logs so on_ready seems to work, so that method cannot be to blame.
I tried following this example: https://github.com/Rapptz/discord.py/blob/async/examples/background_task.py
however I did not use its client = discord.Client() statement because I think I can achieve the same using "bot" similar to as explained here https://stackoverflow.com/a/53136140/6200445
import asyncio
import discord
from discord.ext import commands
token = open("token.txt", "r").read()
def get_prefix(client, message):
prefixes = ['=', '==']
if not message.guild:
prefixes = ['=='] # Only allow '==' as a prefix when in DMs, this is optional
# Allow users to #mention the bot instead of using a prefix when using a command. Also optional
# Do `return prefixes` if u don't want to allow mentions instead of prefix.
return commands.when_mentioned_or(*prefixes)(client, message)
bot = commands.Bot( # Create a new bot
command_prefix=get_prefix, # Set the prefix
description='A bot for doing cool things. Commands list:', # description for the bot
case_insensitive=True # Make the commands case insensitive
)
# case_insensitive=True is used as the commands are case sensitive by default
cogs = ['cogs.basic','cogs.embed']
#bot.event
async def on_ready(): # Do this when the bot is logged in
print(f'Logged in as {bot.user.name} - {bot.user.id}') # Print the name and ID of the bot logged in.
for cog in cogs:
bot.load_extension(cog)
return
async def my_background_task():
await bot.wait_until_ready()
print('inside loop') # This prints one time. How to make it print every 3 seconds?
counter = 0
while not bot.is_closed:
counter += 1
await bot.send_message(channel, counter)
await channel.send(counter)
await asyncio.sleep(3) # task runs every 3 seconds
bot.loop.create_task(my_background_task())
bot.run(token)
[]
From a cursory inspection, it would seem your problem is that you are only calling it once. Your method my_background_task is not called once every three seconds. It is instead your send_message method that is called once every three seconds. For intended behavior, place the print statement inside your while loop.
Although I am using rewrite, I found both of these resources helpful.
https://github.com/Rapptz/discord.py/blob/async/examples/background_task.py
https://github.com/Rapptz/discord.py/blob/rewrite/examples/background_task.py