Discord.py Cog cannot take in content param - python

So I'm in discord.py right now, and whenever I directly edit bot.py (my main file) I can implement a version of my suggest command.
#bot.command(name = 'suggest', help = 'Use this command to give a suggestion to the server')
async def suggest(ctx, content):
channel = discord.utils.get(bot.guilds[0].channels, name = 'suggestions')
print('Suggest command triggered, content is {}'.format(content))
await channel.send(content)
^ bot is just my version of client
This works perfectly fine (except the fact that I only get the 1st word of content, so if someone could solve that, that would also be nice
But when I copy paste into my cog
#commands.command(name = 'suggest', help = 'Use this command to give a suggestion to the server')
async def suggest(self, bot, ctx, content):
print('Suggest command triggered')
channel = discord.utils.get(bot.guilds[0].channels, name = 'suggestions')
print('Content is {}'.format(content))
await channel.send(content)
It doesn't work, can someone help me?

For a cog, that bot parameter is unnecessary, so you just put
#commands.command(name = 'suggest', help = 'Use this command to give a suggestion to the server')
async def suggest(self, ctx, content):
print('Suggest command triggered')
channel = discord.utils.get(bot.guilds[0].channels, name = 'suggestions')
print('Content is {}'.format(content))
await channel.send(content)
As for the only having the first word, discord.py separates arguments by words, so you can just group them all with a * like so:
async def suggest(self, ctx, *content): # gets all the words in a tuple

Related

Discord.py slash command

I made a function that echoes a text with classic commands. I wanted to make it with the slash command, but it didn't work. Honestly, I was confused, and the discord.py documentation wasn't precise about many things.
In my code, the first two arguments are reserved for channels and time.
The bot will check if the two arguments are provided. If one or both are not, it will ignore them. This is for the channel:
async def echo(ctx, *args):
#If a command is not followed with a text
if not args:
await ctx.send("Please provide some text for me to echo dummy.")
return
#Channel mention
if args[0].startswith("<#") and args[0].endswith(">"):
channel_id = int(args[0][2:-1])
channel = client.get_channel(channel_id)
#If the channel is not valid or doesn't exist
if not channel:
await ctx.send("Invalid channel mention.")
return
args = args[1:]
else:
channel = ctx.channel
The rest of the code is for the time argument:
#Check if there is a time argument
time_match = re.match(r"(\d+)(h|m)", args[0])
if time_match:
time_delta = timedelta(hours=int(time_match.group(1))) if time_match.group(2) == "h" else timedelta(minutes=int(time_match.group(1)))
await ctx.send(f"I will echo the message in {time_delta}.")
await asyncio.sleep(time_delta.total_seconds())
args = args[1:]
else:
time_delta = timedelta()
message = " ".join(args)
await channel.send(message)
I found that converting those will require a lot of understanding, and I am a beginner at python.
I dont know if i understood your problem correct, that creating slash commands is not working for you? I know 2 main ways of how to create slash commands with discord.py:
Use discord.Client and manage the commandTree on your own
Use discord.ext.commands.Bot and use cogs to create commands
Also i want to point out the difference between the commands and the app_commands.
app_commands are the commands you know as slash commands. They are supported by the discord api and will show up in the text channel after typing "/". The normal commands are unknown to disocrd. Discord only sends all text messages to discord.py. discord.py works out if a command was texted or not and will call the coreesponding method.
I highly recommend using commands.Bot and cogs. Also this example is based on it.
So first of all your class for a bot should inherits from commands.Bot. Also we add the mehtod setup_hook to load all cogs you are going to implement.
class MyBot(commands.Bot):
def __init__(self):
super().__init__(intents=discord.Intents.all(), command_prefix='/')
async def setup_hook(self):
for file in os.listdir(f'./cogs'):
if file.endswith('.py'):
await self.load_extension(f'cogs.{file[:-3]}')
Now you can create you first cog in a subfolder named "cogs". I recomment to create one file for every Cog. So for this exmaple we create a file and put the following code inside(note that the name inside #app_commands.command defines how the command will be shown in discord):
class MyCog(commands.Cog):
def __init__(self, client):
self.client = client
#app_commands.command(name='test_command')
async def test_command_method(self, ctx: discord.Interaction, param1: str = None, param2: str = None):
if param1 is None:
param1 = ctx.channel_id # set default value
if param2 is None:
await ctx.response.send_message(f'param2 missing!')
return
await ctx.response.send_message(f'Called with parameters param1: "{param1}" and param2: "{param2}"')
async def setup(client):
await client.add_cog(MyCog(client))
The last thing you need to do is to add another Cog, which will sync your created commands with the discord api, so you can see them in discord after typing "/". Note, that this is using commands and not app_commands! So the sync command will not appear after typing "/", but any other app_command does (after syncing).
class Sync(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def sync(self, ctx: Context, spec: Optional[Literal["~", "*", "^"]] = None) -> None:
ctx.bot.tree.copy_global_to(guild=ctx.guild)
synced = await ctx.bot.tree.sync(guild=ctx.guild)
await ctx.send(f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}")
async def setup(client):
await client.add_cog(Sync(client))
If you have all this things set up correctly and your bot starts without errors you can type "/sync" in any textchannel on any discord server the bot is connected to. After this you should be able to see your commands after typing "/" in a text channel.

discordpy suggestion command

My code:
#client.command()
async def suggest(ctx, suggestion):
embed = discord.Embed(
title = "New suggestion.",
description = f"{suggestion}",
color = 0,
timestamp = ctx.message.created_at
)
embed.set_footer(text='Requested by {} | ID-{}' .format(ctx.message.author, ctx.message.author.id))
await ctx.guild.owner.send(embed=embed)
await ctx.send("Suggestion sent to server owner.")
Problem with this command is that bot does not read the whole message, it reads only first word of it.
For example I send this message: =suggest this is suggestion And bot sends embed to a owner only with the first word in this case it will send embed with only this and not whole sentence. If it is possible, how?
You can pass the keyword-only argument
async def suggest(ctx, *, suggestion):
Take a look at the introduction

ctx.content in command and event difference (discord.py)

In discord.py is there some option how i can get access to msg.content (or ctx.content how everyone uses it) under a command ? Below you can see 2 examples of what i mean. The first one is event and i simply copy the message and let the bot to send it back. The second is command but there the msg.content doesnt work. My problem is that i dont want to use events so much and have everything under a command.
#bot.event
async def on_message(msg):
chat = bot.get_channel(797224597443051611)
if msg.channel.id != channel:
return
if msg.content.startswith("!rps"):
message = str(msg.content)
await chat.send(message)
Someone types !rps hello. Outpup in discord is !rps hello
#bot.command()
async def rps(msg):
if msg.channel.id != channel:
return
message = str(msg.content)
await msg.send(message)
Someone types !rps hello (my prefix is !). Output is error in console:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Context' object has no attribute 'content'
Commands always take commands.Context as the first argument, also you should call it ctx instead of msg, to access the message content you can use ctx.message.content
#bot.command()
async def rps(ctx):
if ctx.channel.id != channel:
return
message = str(ctx.message.content)
await ctx.send(message)
Take a look at the commands introduction
In order to get the rest of the message of a command, you need to pass another argument. This argument will include all the messages that's sent after !rps. Also, using ctx instead of msg is better in commands.
#bot.command()
async def rps(ctx, *, args):
if ctx.channel.id != channel:
return
await ctx.send(args)
In this code, args argument includes all the messages after the !rps.

Ignoring exception in command None: discord.ext.commands.errors.CommandNotFound: Command "memechannel" is not found error (discord.py)

Alright, so I have made a Discord Bot that takes memes from reddit and sends it to a user specified channel every 5 minutes, to do this i had to make a cog
#imports
import discord
...
...
#Automemer
class Automemer(commands.Cog):
def __init__(self, client):
....
#used to loop the task of posting memes
#tasks.loop(minutes=5.0)
async def automemer(self):
all_subs = []
...
#start of formating embed for task loop
name = random_sub.title
url = random_sub.url
em = discord.Embed(title = name)
em.set_image(url = url)
em.color = 0xff0000
em.set_footer(text=f'Memes from r/AmongUsMemes')
#used to post the memes where the user does the command am.memechannel
#commands.command()
async def memechannel(self, ctx):
channel_id = ctx.channel.id
await ctx.send('Memes will now be sent in this channel')
memeChannel = ctx.guild.get_channel(channel_id)
if memeChannel:
emoji1 = ':arrow_up:'
emoji2 = ':arrow_down:'
msg = await ctx.send(embed=em)
await msg.add_reaction(emoji1)
await msg.add_reaction(emoji2)
await ctx.send(embed=em)
Error = Ignoring exception in command None: discord.ext.commands.errors.CommandNotFound: Command "memechannel" is not found
The error happens whenever I run the command am.memechannel.
It would be great if somebody told me how to fix this error.
This is the last feature of the bot, and it will be my first ever Discord Bot released to the public! :)
You defined the commands inside of the task loop, your indentation is messed up. You're supposed to put the command underneath the loop, not inside of it.
#tasks.loop(minutes=5.0)
...
#commands.command()
...
Keep in mind that your em variable is only visible within your task so you'll have to find a way to work around that, otherwise you can't use it in your command. An example is adding it as a class variable instead.
def __init__(self, client):
self.em = discord.Embed()
And then just overwriting that variable as you go along.
self.em = discord.Embed(title=name)
...

an 'Help' commands that auto generate a help for a commands.group subcommand using this function

I tried to make a help commands, but i don't know how can I create the help commands for a commands group, which is the main command is separated with the groups, I tried to do help modmail channel modmail is the group, and channel was the sub commands, it just says that command not exist
This is the function:
Please note that, I'm not asking how to use the commands.group, I'm asking about how can I use my function to create a help for Subcommands on commands.group()
async def cmdhelp(self, ctx, command):
params = []
for key, value in command.params.items():
if key not in ("self", "ctx"):
params.append(f"[{key}]" if "NoneType" in str(value) else f"<{key}>")
params = " ".join(params)
alias = ", ".join(command.aliases)
commandhelp = command.help
commanddesc = command.description
if not command.help:
commandhelp = "`None`"
if not command.description:
commanddesc = "`None`"
if not command.aliases:
alias = "`None`"
embed = discord.Embed(title=f"Help for {command}",
colour=0x59FFE7,
description=f"**Description:**\n{commandhelp}\n**Usage:**\n`{command} {params}`\n**Aliases:**\n`{alias}`\n**Permission:**\n`{commanddesc}`")
await ctx.send(embed=embed)
#commands.group(invoke_without_command=True)
async def help(self, ctx, *,cmd=None):
if cmd is None:
# lets say this is a embed with a help category list
else:
if (command := get(self.bot.commands, name=cmd)):
await self.cmdhelp(ctx, command)
else:
await ctx.send("That command does not exist.")
Examples:
If you see, it works with normal command which is help modmail but how can I make for the subcommand of modmail group? which is help modmail channel
First, that isn't the proper way to use subcommands. This is how to use them:
client.remove_command("help")
#client.group()
async def main_cmd(ctx):
print("main command")
#main_cmd.command()
async def sub_cmd(ctx):
print("subcommand")
Saying main_cmd in discord will just print "main command", but saying main_cmd sub_cmd in discord will print "main command" and then "subcommand".
If you don't want the original command to run if a subcommand is invoked, use ctx.invoked_subcommand:
client.remove_command("help")
#client.group()
async def main_cmd(ctx):
if ctx.invoked_subcommand != None:
return
print("main command")
#main_cmd.command()
async def sub_cmd(ctx):
print("subcommand")
EDIT (after the question was edited):
To create "commands" (it won't really be a command) for each command in the bot, use bot.commands:
client.remove_command("help")
#client.command(description = "THIS IS THE MESSAGE THAT WILL APPEAR IN THE SPECIFIC HELP COMMAND")
async def a_command(ctx):
pass #look at the decorator
#client.command()
async def help(ctx, cmd = None):
if cmd == None:
await ctx.send("DEFAULT HELP")
elif cmd in (cmds := {command.name: command for command in client.commands}):
await ctx.send("cmd: " + cmds[cmd].description)
else:
await ctx.send("command not found")
This creates "subcommands" for each command in the bot.

Categories