I'm trying to change the help command to use a pagination version of help.
I understand that the following line of code removes the help command entirely:
bot.remove_command('help')
The docs/dicord.py server offer the following example as a way to change the default help command:
class MyHelpCommand(commands.MinimalHelpCommand):
def get_command_signature(self, command):
return '{0.clean_prefix}{1.qualified_name} {1.signature}'.format(self, command)
class MyCog(commands.Cog):
def __init__(self, bot):
self._original_help_command = bot.help_command
bot.help_command = MyHelpCommand()
bot.help_command.cog = self
def cog_unload(self):
self.bot.help_command = self._original_help_command
I'm still a newbie in python, and I've only been learning rewrite for about 3 days - I'm struggling to find any working examples or an explanation that doesn't lead me back to the above code. I can't work out how to implement this into my own code - so my question is, could anyone provide further explaination into how this would be implemented using cogs?
You can use help_command=None. It delete default help command and you can create your help command. Example:
bot = commands.Bot(command_prefix='!', help_command=None)
#bot.command()
async def help(context):
await context.send("Custom help command")
If you don't set help_command=None and try to create your help command, you get this error: discord.errors.ClientException: Command help is already registered.
You don't really need to remove the command... It isn't good, using the (prefix)help commandname <- It wont appear then... If you want it embed you can do.
class NewHelpName(commands.MinimalHelpCommand):
async def send_pages(self):
destination = self.get_destination()
for page in self.paginator.pages:
emby = discord.Embed(description=page)
await destination.send(embed=emby)
client.help_command = NewHelpName()
The built in help command is of great use
Related
Task:
I made this command that should create a file .txt file and if it has a valid name and it does not exist in ListPython.txt. But when I run it I get the error bellow. Could someone please help me?
Code:
#bot.tree.command(name="newpython")
#app_commands.describe(FileName='FileName')
async def newpython(interaction: discord.Interaction,FileName: str):
AddToLog(f'[INFO][{GetTime()}] newpython command has been used')
_ = open("Program Files/ListPython.txt", "r")
_ = _.read()
_ = _.split()
if FileName not in _:
with open("Program Files/ListPython.txt", "a") as file1:
file1.write(f' {FileName}')
try:
f = open(f"Python Files/{FileName}.txt", "x")
await interaction.response.send_message(f"{FileName} created")
except OSError:
await interaction.response.send_message(f"{FileName} is a invalid file name.")
else:
await interaction.response.send_message(f"{FileName} already exists.")
Error:
discord.app_commands.errors.CommandSignatureMismatch: The signature for command 'newpython' is different from the one provided by Discord. This can happen because either your code is out of date or you have not synced the commands with Discord, causing the mismatch in data. It is recommended to sync the command tree to fix this issue.
The problem is not for app_commands because when I removed it I still got the error though I could be wrong because I'm new to discod.py.
I solved the problem with replacing:
#app_commands.describe(FileName='FileName')
with
#app_commands.describe(user='FileName')
I really don't know how this fix the problem but it does
I am attempting to try using Deferrable opertators however, the example seem to be lacking info on where BaseTrigger and TriggerEvent are imported from. I cant seem to find info on the docs too
https://airflow.apache.org/docs/apache-airflow/stable/concepts/deferring.html?highlight=triggerevent
def __init__(self, moment):
super().__init__()
self.moment = moment
def serialize(self):
return ("airflow.triggers.temporal.DateTimeTrigger", {"moment": self.moment})
async def run(self):
while self.moment > timezone.utcnow():
await asyncio.sleep(1)
yield TriggerEvent(self.moment)
Is BaseTrigger the same as airflow.models.trigger.Trigger?
BaseTrigger is located in airflow.triggers.base.BaseTrigger and TriggerEvent is located in airflow.triggers.base.TriggerEvent.
An IDE such as PyCharm is very helpful in finding such things: press double shift and search BaseTrigger will get you to module directly.
How do I go about retrieving a list of commands (Commands) that require the user to have the 'administrator' permission (to use them)? I am making a 'help' command; I would like to be able to retrieve all commands that require a certain permission in a list.
For example:
class Moderate(commands.Cog):
#commands.command()
#has_permissions(administrator=True)
async def kick(ctx, member):
#kick member
cog = Moderate
print(cog.commands.checks) -> {"kick" : administrator=True} #How to get this or the commands my bot has with this permission requirement?
My solution to this problem is to read the file and get the commands' names from it by checking if there's a "has_permissions(" string before it:
import os
file_path = os.path.abspath(__file__) #Get the file path of this file
self = open(file_path, "r", encoding="utf-8") #Specify the encoding as it's a Python file
code = self.readlines()
commands = [] #This is the list of all the commands your bot has
for line_number, line in enumerate(code):
if "has_permissions(administrator" in line: #Check if the decorator ("#has_permissions(administrator)") is in the line
command = code[line_number + 1] #Get the line after the decorator's
command_name = command.replace("async def ", "").strip()[:-1] #Removing "async def ", colon and extra spaces
if "command = code[line_number + " not in command_name: #Adding all commands' names except this code we're using
commands.append(command_name)
print(commands) #See the 100 commands your bot has
This will not work if you check if the user has the 'administrator' permission in a way other than the #has_permissions(administrator=True) decorator. For example, if you have a 'ban' command like this, it won't be added to the list:
bot.command()
async def ban(message, member:discord.Member):
if message.author.guild_permissions.administrator: #The method won't work if you check using this
#Ban the member
This also won't work if you have extra blank lines between the decorator and the command's definition etc.
However, you can modify this to meet your requirements.
Here's a less hacky way of doing it that I personally prefer:
We can use a decorator to set a special attribute in the Command object, which we can then look at later. (Note: it is important that the #special decorator is placed before the #client.command. It needs to work off the Command object, not the actual callable function itself.)
def special(command):
command.__special_flag__ = None # the value is irrelevant, you just need to set it
return command
#special
#client.command()
async def special_command(ctx):
await ctx.send('hi')
#special
#client.command()
async def list_special_commands(ctx):
for command in client.commands:
if hasattr(command, '__special_flag__'): # this checks if "__special_flag__" is set
await ctx.send(f'special command! {command}')
await ctx.send('ok')
You can then iterate through every command in the client and check if that flag is set. You can then add this extra decorator to any command you want to check permissions for. Then, in your help command, only display commands with this flag set.
Output looks like this:
As #The Amateur Coder mentioned, this might take a bit of time to add them all, so you can make this combined decorator that does all of it:
def combine_stuff(function):
function = commands.has_permissions(...)(function)
command = client.command(...)(function)
command = special(command)
Explanation
There exists a very simple way to see which commands require administrator permissions. All command checks' predicates are in closures, and for has_permissions, one of the nonlocal variables of these functions will be the specified permissions.
This code must be run after all the commands are defined. For example, it can be run in on_ready, or before bot.run. You do not need to modify any other functions to run this code.
Code
#bot.event
async def on_ready():
command_list = []
for command in bot.walk_commands():
for check in command.checks:
closure = check.__closure__
if closure is None:
continue
# loop through variables used by the check
for cell in closure:
# if administrator=True was passed
if cell.cell_contents.get("administrator"):
command_list.append(command)
break
print(command_list)
Reference
How do I access/modify variables from a function's closure?
Definition of has_permissions
So I want to make a command for my discord bot that translates stuff. I'm using repl.it and the googletrans pip install code won't work for some reason. I also tried doing pip install googletrans==3.1.0a0 in the shell, but it won't work either. Is there currently a code for google translate that works in python or an updated one that works for repl.it??
here's my current code (when i try the command, it doesn't respond):
#client.command(aliases=['tr'])
async def translate(ctx, lang_to, *args):
lang_to = lang_to.lower()
if lang_to not in googletrans.LANGUAGES and lang_to not in googletrans.LANGCODES:
raise commands.BadArgument("Invalid language detected. Make sure to check if it is spelled correctly and that it is a real language.")
text = ' '.join(args)
translator = googletrans.Translator()
text_translated = translator.translate(text, dest=lang_to).text
await ctx.send(text_translated)```
Hey try to Install google translator version 4.0.0rc1!
pip install googletrans==4.0.0rc1
My code:
#bot.command()
async def trans(ctx, lang, *, args):
t= Translator()
a= t.translate(args, dest=lang)
tembed= discord.Embed(title=f'Translating Language....', description=f'Successfully translated the text below :point_down: \n \n**{a.text}**', color=discord.Colour.random())
await ctx.send(embed=tembed)
print(googletrans.LANGUAGES)
How can I create an alias for a command in a line-oriented command interpreter implemented using the cmd module?
To create a command, I must implement the do_cmd method. But I have commands with long names (like constraint) and I want to provide aliases (in fact, shortcuts) for these commands (like co). How can I do that?
One possibility that came to my mind is to implement the do_alias (like do_co) method and just calling do_cmd (do_constraint) in this method. But this brings me new commands in the help of the CLI.
Is there any other way to achieve this? Or may be is there a way to hide commands from the help output?
The following solution makes aliased commands share a single help message. It lets you keep all of your aliases in a single easy to edit place, while making documentation much easier. It checks user input against an alias dictionary with function values and overrides both the default() (See sloth & brian) and do_help() methods.
Here I've made aliases 'c' and 'con' execute do_constraint(), 'q' invoke do_quit(), and 'h' invoke do_help(). The bonus of this solution is that 'h q' and 'help quit' print the same message. Documentation for several aliased commands can maintained in a single docstring.
import cmd
class prompt(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.aliases = { 'c' : self.do_constraint ,
'con' : self.do_constraint ,
'q' : self.do_quit ,
'h' : self.do_help }
def do_constraint(self, arg):
'''Constrain things.'''
print('Constraint complete.')
def do_quit(self, arg):
'''Exit the program.'''
return True
def do_help(self, arg):
'''List available commands.'''
if arg in self.aliases:
arg = self.aliases[arg].__name__[3:]
cmd.Cmd.do_help(self, arg)
def default(self, line):
cmd, arg, line = self.parseline(line)
if cmd in self.aliases:
self.aliases[cmd](arg)
else:
print("*** Unknown syntax: %s" % line)
You can overwrite the default method and search for a suitable command handler (as already suggested by Brian):
import cmd
class WithAliasCmd(cmd.Cmd):
def default(self, line):
cmd, arg, line = self.parseline(line)
func = [getattr(self, n) for n in self.get_names() if n.startswith('do_' + cmd)]
if func: # maybe check if exactly one or more elements, and tell the user
func[0](arg)
The docs mention a default method, which you can override to handle any unknown command. Code it to prefix scan a list of commands and invoke them as you suggest for do_alias.