I want to add a similar commands suggestion when the user make a typo.
Example: Right command: clear // User input: Cler // Bot response: Did you mean 'clear'?
I'm heard about difflib.get_close_matches and I'm be able to make this works in a .py script but not with discord.py
You wanna have a list of all your bot commands.
Then you test with the on command error event if the command wasnt found. If it wasnt test for similar commands (Levenshtein distance: http://en.wikipedia.org/wiki/Levenshtein_distance). Then send the user a message with the expected command if similar commands are found. Here is an example of how I would do it):
import discord
from discord.ext import commands
from difflib import SequenceMatcher
BOT_PREFIX = '!'
bot = discord.Client(intents=discord.Intents.all(), status=discord.Status.dnd)
bot = commands.Bot(command_prefix=BOT_PREFIX)
bot_commands = [] # all cmd names
#bot.event
async def on_ready():
global bot_commands
bot_commands = [cmd.name for cmd in bot.commands]
#bot.event
async def on_message(message):
if message.author.id == bot.user.id:
return
await bot.process_commands(message)
#bot.event
async def on_command_error(ctx, error): #catch error
if isinstance(error, commands.CommandNotFound):
for i in bot_commands: #Looking for close matches in commands
if float(SequenceMatcher(a=str(ctx.message.content).replace(BOT_PREFIX, '').lower(), b=i.lower()).ratio()) > float(0.67): #You can adjust the second float
await ctx.send(f'Did you mean {i}?') #Your message
break #Ignore similiar matches
#raise error (optional)
bot.run('token')
Related
I am using pycord for this so I can use slash commands.
Here is my code:
import discord, os
from dotenv import load_dotenv
from discord.ext import commands
load_dotenv(f'{os.getcwd()}/token.env')
bot = commands.Bot()
token = str(os.getenv('TOKEN'))
#bot.slash_command(description = 'mutes member of choice')
#commands.has_permissions(administrator = True)
async def mute(ctx, member: discord.Member):
guild = ctx.guild
mutedRole = discord.utils.get(guild.roles, name="Muted")
if not mutedRole:
mutedRole = await guild.create_role(name="Muted")
for channel in guild.channels:
await channel.set_permissions(mutedRole, speak=False, send_messages=False, read_message_history=True, read_messages=False)
await member.add_roles(mutedRole)
await ctx.send(f"Muted {member.mention}")
#mute.error
async def permserror(ctx, error):
if isinstance(error, commands.MissingPermissions):
ctx.respond('Sorry! You dont have permission.')
#bot.event
async def on_ready():
print(f'Client ready as {bot.user}')
bot.run(token)
This isn't my full code, but the other commands don't seem necessary for my problem.
Anyway, when I try to use this command, it doesn't throw an error in the console, but the application doesn't respond and it doesn't add the role.
I put a print statement between each line of code, and it seems to stop just before it adds the role.
How can I fix this?
Update: My previous code did not run so I have updated it.
This doesn't actually have to do with the code of the program. All you have to do is move the bot role to the top and it'll work. The error is 403 Forbidden (error code: 50013): Missing Permissions. Also, you have to reinvite the bot and add applications.commands if you haven't already.
I'm trying to make a discord.py bot with the help of nextcord and i've come far enough to make the code work in theory but nothing happens when i try to kick / ban, it just throws an
nextcord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Context' object has no attribute 'ban'
and i have no idea why it does so, i want it to work as inteded.
Note, the command(s) are class commands so the main bot loads in the command from another file.
Main Bot Script
# import nextcord
from nextcord.ext import commands
# import asyncio
import json
# Import all of the commands
from cogs.ban import ban
from cogs.hello import hello
from cogs.help import help
from cogs.info import info
from cogs.kick import kick
from cogs.test import test
#Define bot prefix and also remove the help command built in.
bot = commands.Bot(command_prefix=json.load(open("config.json"))["prefix"])
bot.remove_command("help")
#bot.event
async def on_ready():
print(f'Logged in as {bot.user}')
bot.add_cog(hello(bot))
bot.add_cog(help(bot))
bot.add_cog(info(bot))
bot.add_cog(test(bot))
bot.add_cog(ban(bot))
bot.add_cog(kick(bot))
bot.run(json.load(open("config.json"))["token"])
Problematic command
import discord
from nextcord.ext import commands
from nextcord.ext.commands import has_permissions, CheckFailure
bot = commands.bot
class ban(commands.Cog):
def __init__(self, client):
self.client = client
self._last_member = None
#commands.Cog.listener()
async def on_ready(self):
print('ban Cog Ready')
#commands.command()
#has_permissions(ban_members=True)
async def ban(ctx, user: discord.Member = None, *, Reason = None):
if user == None:
await ctx.send("Could you please enter a valid user?")
return
try:
await user.ban(reason=Reason)
await ctx.send(f'**{0}** has been banned.'.format(str(user)))
except Exception as error:
if isinstance(error, CheckFailure):
await ctx.send("Looks like you don't have the permissions to use this command.")
else:
await ctx.send(error)
You are doing:
user: discord.Member
You need to use nextcord instead.
user: nextcord.Member
#commands.command()
#has_permissions(ban_members=True)
async def ban(ctx, user: nextcord.Member = None, *, Reason = None):#
#The rest of your code here
You can do the following
For Normal bot
#client.command(name="ban", aliases=["modbancmd"], description="Bans the mentioned user.")
#commands.has_permissions(ban_members=True)
async def ban(self, ctx, member: nextcord.Member, *, reason=None):
# Code for embed (optional)
await member.ban(reason=reason)
# (optional) await ctx.send(embed=banembed)
You can do the following
For Cogs
#commands.command(name="ban", aliases=["modbancmd"], description="Bans the mentioned user.")
#commands.has_permissions(ban_members=True)
async def ban(self, ctx, member: nextcord.Member, *, reason=None):
# Code for embed (optional)
await member.ban(reason=reason)
# (optional) await ctx.send(embed=banembed)
Well, here are the problems:
Your command is in a cog therefore you need to say (self, ctx, and so on)
You need to change the discord to nextcord
You should change the commands.bot to commands.Bot()
And if I'm correct, you should change the self.client to self.bot since your commands.Bot() is defined as bot
And uhh yeah it should work perfectly after you fix those.
I want to know in which text channels admin want to enable my bot functions. But in this case my code is not working.
The main idea was when admin typing !enable in text-chat, bot reacts to it and add text-chat id, guild id(ctx.channel.id) to the list, then bot responds in chat with bot has been enabled.
code where this command is not working:
channel = []
#bot.command()
async def enable(ctx):
global channel
print("Debug")
await ctx.send('Restriction bot has been enabled for this text channel.')
channel.append(ctx.channel.id)
full bot code:
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix='!')
#bot.event
async def on_ready():
print(f'We have logged in as {bot.user.name}.')
channel = []
#bot.command()
async def enable(ctx):
global channel
print("Debug")
await ctx.send('Restriction bot has been enabled for this text channel.')
channel.append(ctx.channel.id)
#bot.event
async def on_message(ctx):
if ctx.author == bot.user:
return
#if ctx.channel.id != [for channnels in channel]:
# return
if ctx.attachments[0].height:
await ctx.author.send('Your media file is restricted!')
await ctx.delete()
The channel variable is initialized in your program so each time you will restart your bot, it will be emptied. One way you can solve your problem is by storing them in a file. The easiest way to do it would be to use the json library. You'll need to create a channels.json file.
The code :
from json import loads, dumps
def get_data():
with open('channels.json', 'r') as file:
return loads(file.read())
def set_data(chan):
with open('channels.json', 'w') as file:
file.write(dumps(chan, indent=2))
#bot.command()
async def enable(ctx):
channels = get_data()
channels.append(ctx.channel.id)
set_data(channels)
await ctx.send('Restriction bot has been enabled for this text channel.')
The channels.json file :
[]
Let's just say the user enters an invalid command, the bot suggests a command with (y/n)? If y, then the bot should trigger the suggested command.
I feel this can be achieved in 2 ways:
If the bot can trigger commands on its own message
If I can call the command from the other cogs
I can't seem to get either of em working.
Here is an example to help you guys help me better:
Let's just say this below code is from a cog called Joke.py:
#commands.command()
async def joke(self,ctx):
await ctx.send("A Joke")
And then there another cog "CommandsCorrection.py" that corrects wrong commands used by the user that are saved on data.json file:
#commands.Cog.listener()
async def on_message(self, message):
channel = message.channel
prefix = get_prefix(self,message)
if message.author.id == bot.id:
return
elif message.content.startswith(prefix):
withoutprefix = message.content.replace(prefix,"")
if withoutprefix in data:
return
else:
try:
rightCommand= get_close_matches(withoutprefix, data.keys())[0]
await message.channel.send(f"Did you mean {prefix}%s instead? Enter Y if yes, or N if no:" %rightCommand)
def check(m):
return m.content == "Y" or "N" and m.channel == channel
msg = await self.client.wait_for('message', check=check, timeout = 10.0)
msg.content = msg.content.lower()
if msg.content == "y":
await channel.send(f'{prefix}{rightCommand}')
elif msg.content == "n":
await channel.send('You said no.')
except asyncio.TimeoutError:
await channel.send('Timed Out')
except IndexError as error:
await channel.send("Command Not Found. Try !help for the list of commands and use '!' as prefix.")
in the above code await message.channel.send(f"Did you mean {prefix}%s instead? Enter Y if yes, or N if no:" %rightCommand) suggests the right command and await channel.send(f'{prefix}{rightCommand}') sends the right command.
So,for example:
user : !jok
bot : Did you mean !joke instead? Enter Y if yes, or N if no:
user : y
bot : !joke **I want to trigger the command when it sends this message by reading its own message or my just calling that command/function
How should I go about this?
One solution would be to separate the logic of your commands from the command callbacks and put it in it's own coroutines. Then you can call these coroutines freely from any of the command callbacks.
So you would turn code like this:
#bot.command()
async def my_command(ctx):
await ctx.send("Running command")
#bot.command()
async def other_command(ctx, arg):
if arg == "command":
await ctx.send("Running command")
Into something like this:
async def command_logic(ctx):
await ctx.send("Running command")
#bot.command()
async def my_command(ctx):
await command_logic(ctx)
#bot.command()
async def other_command(ctx, arg):
if arg == "command":
await command_logic(ctx)
#Patrick Haugh suggested this idea, he was on to something but wasn't really working but I got a way to get it working.
If you use the following method for a cog, you'll be able to read bot commands as well:
import discord
from discord.ext import commands
async def command_logic(self, ctx):
await ctx.send("Running command")
class Example(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def my_command(self, ctx):
await command_logic(self,ctx)
#commands.Cog.listener()
async def on_message(self, message):
if message.content == '!command':
ctx = await self.client.get_context(message)
await command_logic(self,ctx)
def setup(client):
client.add_cog(Example(client))
from discord.ext.commands import Bot
import secrets
from time import sleep
discordBot = Bot(command_prefix = ".")
listStrings = ['add a reaction', 'adnd']
#discordBot.event
async def on_read():
print('Client logged in')
#discordBot.command()
async def s(*args):
return await discordBot.say (" ".join(args))
#discordBot.command()
async def close():
quit()
discordBot.run(secrets.token)
I wanted to say ".s (text)" and that the bot says the text(works already) but deletes your message, how can i do this? i got the part working of on_message:
#discordbot.event
async def on_message(message):
await discordBot.delete_message(message)
What is the best way to do this? Thanks in advance.
I'm sorry you haven't got an answer for 26 days. If you are still looking for an answer, I hope I can provide some help. So I ran your code, and the bot ran the command, my message deleted, then the bot's message also deleted. One way to fix this is to provide an if statement which means the bot only deletes the message if the author is not a bot.
#test_bot.event
async def on_message(message):
if not (message.author).bot:
await test_bot.delete_message(message)
await test_bot.process_commands(message)
Another way to remove only messages that are the commands is to use another if statement. The following code is specific, so you only delete commands, not just all user's messages if they aren't a bot. The following code uses message.content which returns the message as a string.
#test_bot.event
async def on_message(message):
if message.content.startswith('.s'):
await test_bot.delete_message(message)
await test_bot.process_commands(message)