Why cant I restrict command use for my bot - discord.py - discord.py

'''
#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

Related

'AsyncTeleBot' object has no attribute 'register_next_step_handler'

Trying to create simple ask, answer, remember telegram bot with pyTelegramBot.
Everything were normal, when TeleBot were used. TeleBot.register_next_message_handler helped me a lot.
Example:
...
#bot.message_handler(func=lambda msg: msg.text is not None and '/start' in msg.text)
def send_welcome(msg):
global cur_user
cur_user = msg.from_user.id
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton('Да', callback_data='old'),
types.InlineKeyboardButton('Нет', callback_data='new'))
keyboard.add(types.InlineKeyboardButton('Пора остановиться', callback_data='stop_bot'))
greet = 'Бла...бла...бла\nМы знакомы?'
bot.send_message(msg.chat.id, greet, reply_markup=keyboard)
...
#bot.callback_query_handler(func=lambda call: True)
def query_processing(call):
global user
global cur_user
if call.data == 'new':
user = dict.fromkeys(user, None)
nxt = bot.send_message(call.message.chat.id, 'Как звать?')
bot.register_next_step_handler(nxt, get_name_ask_goal)
...
Still I need my bot to be asynchronous because of sleep time till message to be send from bot and with simultaneous possibility for user to send messages.
Tried to use AsyncTeleBot, but there is no register_next_step_handler function. I did't find out how to make bot wait user for the name typing and it is almost impossible to me to add register_next_step_handler function into particular files. Although found issue in gitHub and no solution from 2017.
Tried
...
bot = AsyncTeleBot(os.getenv('token2'))
...
async def beep(chat_id) -> None:
"""Send the beep message."""
await bot.send_message(chat_id, text='Beep!')
aioschedule.clear(chat_id)
async def scheduler():
global chat_id
aioschedule.every(5).seconds.do(beep, chat_id).tag(chat_id)
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def main():
await asyncio.gather(scheduler(), bot.polling(non_stop=True))
if __name__ == "__main__":
asyncio.run(main(), debug=True)
...
Result:
TypeError: An asyncio.Future, a coroutine or an awaitable is required
I'm wondering:
is there other appropriate libraries for my task?
is there any simple solution to save user's message?
is it possible to use asyncio with TeleBot that made Thread-style?
Hoping for any help.

Custom help command

Hi I’m trying to code a simple discord bot using discord.py, and one of the things I want my bot to do is send a nice custom embed when a user uses the !help command. So far I know that this simple code, which some people provided in other questions on stackoverflow about custom help commands:
class help(commands.MinimalHelpCommand):
async def send_pages(self):
destination = self.get_destination()
e = discord.Embed(title="Help:", description="description", colour=discord.Colour.blurple(), timestamp=datetime.datetime.utcnow())
await destination.send(embed=e)
client.help_command = help()
works, but the thing is when someone wants to use !help on a specific command or category, the bot will only send the send_pages command. I tried modifying the code and added
async def send_page_command(self, *, command):
destination = self.get_destination
command_e = discord.Embed(title="Dogluva:robot's Help:", description=f"{command.description}", colour=discord.Colour.blurple(), timestamp=datetime.datetime.utcnow())
await destination.send(embed=command_e)
But this doesn’t help. Anyone know what I’m doing wrong here? I’ve set the description for all the commands I wrote in my cogs using commands.command(description=“description") but I don’t know how to make a custom help command access those descriptions.
First, remember to delete the default help command (discord automatically has help command), so you need to delete it first:
client.remove_command('help') # before your own "help" command
# or
client = commands.Bot(command_prefix="<your prefix>", help_command=None) # when you create client
Basic help command:
#client.command()
async def help(ctx):
embed = discord.Embed(title="Help", description="Write your own description here", colour=discord.Color.blue())
await ctx.send(embed=embed)
But you can make it even better. Read more about Embeds in the documentation and check this template:
instead of send_page_command(self, *, command): you can try using send_command_help(self, command):
https://gist.github.com/InterStella0/b78488fb28cadf279dfd3164b9f0cf96
#client.command()
async def help(ctx, *, command=None):
if command == None:
e = discord.Embed(title="Dogluva:robot:'s Help:", description=“Custom Description", colour=discord.Colour.blurple(), timestamp=datetime.datetime.utcnow())
elif command == “{command}":
e = discord.Embed(title="Dogluva:robot:'s Help:", description=“Command Description", colour=discord.Colour.blurple(), timestamp=datetime.datetime.utcnow())
await ctx.send(embed=e)
I just created different cases for each command

how do i display a message if user puts a non existing sub command?

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.

Command to spam something discord-bot

so basically I am trying to make a spam command for my discord bot, which takes in a custom message to spam. Here's the code:
#client.command(name='spam')
async def spam(ctx):
global stop
stop = 0
content = ctx.message.content[11:]
if ctx.author.guild_permissions.administrator or ctx.author.id in admins:
if lock == 1:
await ctx.send('Jesus bot is currently locked.')
elif lock == 0:
await ctx.send('Beginning spam..')
while not stop:
await ctx.send(content)
else:
await ctx.send('Sorry, but you do not have admin permissions in this server, or you are not a verified admin.')
For some reason, whenever I try to use this command, the bot doesn't respond. I'm not sure why this happens, and could use some help please.
Picture of bot not responding:
I have a spam command, but I only use it to mess around with my friends. I would not recommend using this as a public command, as you may get rate limited or banned for abuse or something like that. Anyway here is the code I have used for it.
#commands.command()
#commands.is_owner()
# If you want to use admin only, use this below
# #commands.has_permissions(administrator=True)
async def spam(self, ctx, amount, *, word):
int(amount)
await asyncio.sleep(2)
print(f"Starting to spam {word} in {ctx.guild.name}")
await ctx.message.delete()
await ctx.send(f"{ctx.author.mention}\nPlease note that this will clog up the bot's reaction time")
await asyncio.sleep(3)
count = 0
counting=True
while counting:
await ctx.send(word)
count = count + 1
if count == amount:
await asyncio.sleep(2)
await ctx.send("Spam complete")
print(Fore.GREEN + "Spam complete")
counting = False
At the top of your code, make sure you import asyncio as time.sleep will cause the whole bot to pause. Also the Fore.GREEN stuff is just colorama (import colorama).
Try using tasks instead of asyncio. It is made for such repetetive operations and it is easier and nicer because it is made by discord and is included in discord.ext. Something like this:
from discord.ext import tasks
#client.command(name='spam')
async def spam(ctx):
#get message and do all the ifs that you have there
spamLoop.start()
#client.command(name='stopSpam')
async def spamStop(ctx):
# stop the loop
spamLoop.cancel()
#tasks.loop(seconds=1)
async def spamLoop():
print("The message")
Actually quite a simple way of adding spam
import asyncio
#bot.command(name='spam', help= "Spam to your heart's delight")
async def spam(ctx, thing, amount):
count = 0
while count < int(amount):
await ctx.send(thing)
count += 1
if count < amount:
await asyncio.sleep(1)

How do I take in user input in discord.py

This may be kinda dumb but I'm trying to make a purge command but theres always something wrong with it. Can anyone help please?
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('p!purge'):
numberoftimes = input(int('How many times: '))
await message.channel.purge(limit=str(numberoftimes))
I'll start with answering your question, then I'll clarify on a better way to create a purge command. To answer your question, there is no need to make an input() statement. Rather, this is how you make an input prompt with a Discord bot:
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('p!purge'):
await message.channel.send('How many messages do you want to purge?')
while True:
numberoftimes = client.wait_for('message') # This means, the client is waiting for a message
if numberoftimes.author == message.author: # Make sure that the person responding is the person who did the command
limit = int(numberoftimes.content) # If its a string, it will be treated as a word. You want to purge a "number" of messages
await message.channel.purge(limit=limit+1)
That's how you would purge messages in a channel. But, the way you are creating this command can be made simpler. You can use the command decorator, which is the "standard" way of doing things:
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='p!')
#client.command()
#commands.has_permissions(manage_messages=True)
async def purge(ctx, limit):
limit = int(limit)
await ctx.channel.purge(limit=limit + 1)

Resources