Discord.py Rewrite Cooldown Commands - python

I am using Discord.py Rewrite to make a Discord bot. I have a command called work, with the following code:
import discord
from discord.ext import commands
client = commands.Bot(command_prefix="c!")
# on_ready command
#client.command()
async def work(ctx):
author_id = ctx.author.id
currency_dict[author_id] += 50
await ctx.send("You Gained 50 Coins")
# currency_dict is a dictionary that is already defined
Is there any way to make it so that the user can only use the command once every 30 seconds? Thanks!

First, you would have to import from discord.ext.commands.cooldowns import BucketType. This is what will help you with cooldowns. Below is both the cooldown check as well as the max_concurrency check you can use with this import.
from discord.ext.commands.cooldowns import BucketType
# BucketType can be BucketType.default, member, user, guild, role, or channel
#commands.cooldown(rate,per,BucketType)
# Limit how often a command can be used, (num per, seconds, BucketType)
#commands.max_concurrency(number, per=BucketType.default, *, wait=False)
# Limit how many instances of the command can be running at the same time.
# Setting wait=True will queue up additional commands. False will raise MaxConcurrencyReached
# Checks can be stacked, and will Raise a CheckFailure if any check fails.
In your case, you would want to use commands.cooldown(rate,per,BucketType).
import discord
from discord.ext import commands
from discord.ext.commands.cooldowns import BucketType
client = commands.Bot(command_prefix="c!")
# on_ready command
#client.command()
#commands.cooldown(1,30,commands.BucketType.user) # one command, every 30 seconds, per user
async def work(ctx):
author_id = ctx.author.id
currency_dict[author_id] += 50
await ctx.send("You Gained 50 Coins")
# currency_dict is a dictionary that is already defined
# cooldown error-handling
#work.error
async def work_error(ctx, error):
if isinstance(error, commands.CommandOnCooldown):
await ctx.send(f'This command is on cooldown, you can use it in {round(error.retry_after, 2)} seconds')
references:
docs: discord.ext.commands.cooldown
StackOverflow: Command cooldown in discord.py
GitHub Gist: Built-in Checks for the commands extension of discord py

Related

How do i make a working slash command in discord.py

I am trying to make a slash command with discord.py I have tried a lot of stuff it doesn't seem to be working. Help would be appreciated.
Note: I will include a version for pycord at the end because I think it's much simpler, also it was the original answer.
discord.py version
First make sure that you have the newest version of discord.py installed.
In your code, you first import the library:
import discord
from discord import app_commands
and then you define your client and tree:
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
The tree holds all of your application commands. Then you can define your command:
#tree.command(name = "commandname", description = "My first application Command", guild=discord.Object(id=12417128931)) #Add the guild ids in which the slash command will appear. If it should be in all, remove the argument, but note that it will take some time (up to an hour) to register the command if it's for all guilds.
async def first_command(interaction):
await interaction.response.send_message("Hello!")
Then you also have to sync your commands to discord once the client is ready, so we do that in the on_ready event:
#client.event
async def on_ready():
await tree.sync(guild=discord.Object(id=Your guild id))
print("Ready!")
And at the end we have to run our client:
client.run("token")
pycord version
To install py-cord, first run pip uninstall discord.py and then pip install py-cord.
Then in your code, first import the library with
import discord
from discord.ext import commands
create you bot class with
bot = commands.Bot()
and create your slash command with
#bot.slash_command(name="first_slash", guild_ids=[...]) #Add the guild ids in which the slash command will appear. If it should be in all, remove the argument, but note that it will take some time (up to an hour) to register the command if it's for all guilds.
async def first_slash(ctx):
await ctx.respond("You executed the slash command!")
and then run the bot with your token
bot.run(TOKEN)
They're sort of in the middle of adding slash commands to discord.py but you can see a few examples in https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6 You seem to be using discord_slash, which I have not used.
The main documentation for this stuff is https://discordpy.readthedocs.io/en/master/interactions/api.html?highlight=dropdown#decorators but the main "how to" is that you've gotta make a "tree", attach commands to that tree, and sync your tree for the commands to show up. discord.ext.Bot makes its own tree, which is why I'm using that instead of client, which I think doesn't make a tree by default.
If you specify the guilds, the commands sync takes place instantly, but if you don't specify the guild, I think it takes an hour to update or something like that, so specify the guild until you're ready for deployment.
I'm not quite sure how to do it in cogs, because I have mine in groups but basically what I'm doing is a combination of #bot.tree.command() in the main bot file and a few groups in separate files.
So here's my main file
import discord
import simplegeneralgroup
from config import TOKEN
MY_GUILD = discord.Object(id=1234567890)
class MyBot(discord.ext.commands.Bot):
async def on_ready(self):
await self.tree.sync(guild=MY_GUILD)
bot: discord.ext.commands.Bot = MyBot
#bot.tree.command(guild=MY_GUILD)
async def slash(interaction: discord.Interaction, number: int, string: str):
await interaction.response.send_message(f'Modify {number=} {string=}', ephemeral=True)
bot.tree.add_command(simplegeneralgroup.Generalgroup(bot), guild=MY_GUILD)
if __name__ == "__main__":
bot.run(TOKEN)
and then the simplegeneralgroup file
import discord
from discord import app_commands as apc
class Generalgroup(apc.Group):
"""Manage general commands"""
def __init__(self, bot: discord.ext.commands.Bot):
super().__init__()
self.bot = bot
#apc.command()
async def hello(self, interaction: discord.Interaction):
await interaction.response.send_message('Hello')
#apc.command()
async def version(self, interaction: discord.Interaction):
"""tells you what version of the bot software is running."""
await interaction.response.send_message('This is an untested test version')
There should be three commands: /slash, which will prompt the user for a number and string, /generalgroup hello, and /generalgroup version
# This is new in the discord.py 2.0 update
# imports
import discord
import discord.ext
# setting up the bot
intents = discord.Intents.all()
# if you don't want all intents you can do discord.Intents.default()
client = discord.Client(intents=intents)
tree = discord.app_commands.CommandTree(client)
# sync the slash command to your server
#client.event
async def on_ready():
await tree.sync(guild=discord.Object(id=Your guild ID here))
# print "ready" in the console when the bot is ready to work
print("ready")
# make the slash command
#tree.command(name="name", description="description")
async def slash_command(int: discord.Interaction):
await int.response.send_message("command")
# run the bot
client.run("token")
discord.py does not support slash commands. I recommend you use discord-py-interactions for slash commands. To install it is to do python3.exe -m pip install discord-py-interactions. It works well. Here is a sample code to base off:
import interactions
bot = interactions.Client(token="your_secret_bot_token")
#bot.command(
name="my_first_command",
description="This is the first command I made!",
scope=the_id_of_your_guild,
)
async def my_first_command(ctx: interactions.CommandContext):
await ctx.send("Hi there!")
bot.start()
Slash Commands with discord.py (2.0)
While this is a new answer to an old question when I first started coding a bot I ran into this but non of the answers worked.
Some Context: There are 2 ways to code a slash command in discord.py 2.0
discord.Client, +Easy to Sync -No Prefix Commands
commands.Bot, -Harder to Sync +Prefix Commands
I will show one examples I am more confidant on the commands.Bot FYI
A great external source for discord.Client slash command examples is Rapptz-app_command_examples
commands.Bot Slash Command
from typing import Literal, Optional
import discord
from discord.ext.commands import Greedy, Context
from discord import app_commands
from discord.ext import commands
#------ Bot ------
# Can add command_prefix='!', in commands.Bot() for Prefix Commands
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
bot = commands.Bot(intents=intents)
#--- Bot Startup
#bot.event
async def on_ready():
print(f'Logged in as {bot.user}') #Bot Name
print(bot.user.id) #Bot ID
#------ Slash Commands ------
#Parameters can be added in def help()
# Ex- async def help(interaction: discord.Interaction, left:int,right:int)
#bot.tree.command()
async def help(interaction: discord.Interaction):
"""Help""" #Description when viewing / commands
await interaction.response.send_message("hello")
#------ Sync Tree ------
guild = discord.Object(id='guildID')
# Get Guild ID from right clicking on server icon
# Must have devloper mode on discord on setting>Advance>Developer Mode
#More info on tree can be found on discord.py Git Repo
#bot.command()
#commands.guild_only()
#commands.is_owner()
async def sync(
ctx: Context, guilds: Greedy[discord.Object], spec: Optional[Literal["~", "*", "^"]] = None) -> None:
if not guilds:
if spec == "~":
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "*":
ctx.bot.tree.copy_global_to(guild=ctx.guild)
synced = await ctx.bot.tree.sync(guild=ctx.guild)
elif spec == "^":
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.bot.tree.sync(guild=ctx.guild)
synced = []
else:
synced = await ctx.bot.tree.sync()
await ctx.send(
f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}"
)
return
ret = 0
for guild in guilds:
try:
await ctx.bot.tree.sync(guild=guild)
except discord.HTTPException:
pass
else:
ret += 1
await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")
bot.run('token')
!sync -> global/server sync (No ID) or (ID SET)
!sync ~ -> sync current guild (Bot In)
!sync * -> copies all global app commands to current guild and syncs
!sync ^ -> clears all commands from the current guild target and syncs (removes guild commands)
!sync id_1 id_2 -> syncs guilds with id 1 and 2
If I had made any errors please comment and I will fix them, goodluck on your discord bot journey
discord.py does not support slash commands and will never add support for slash commands (as it has shut down) thus I recommend disnake (a popular fork). Specifically disnake because out of all the forks disnake seems to be the more intellectual one.

discord.py not responding to any commands

I followed a tutorial to make add a basic music function to a discord bot, but it doesnt respond to any commands. I get the "henlo frens i is alieving" message as to indicate that the bot is ready, and there are no errors showing up. But then when i try to use the ping command i get no response, and nothing shows up in the terminal unlike the other bots i've written.
from plznodie import plznodie
from discord.ext import commands
import music
import time
#vars
cogs=[music]
intents = discord.Intents()
intents.all()
client = commands.Bot(command_prefix = "!" , intents=intents)
intents.members = True
activvv = discord.Game("you guys complain")
#config
for i in range(len(cogs)):
cogs[i].setup(client)
#client.event
async def on_ready ():
await client.change_presence(status = discord.Status.online, activity = activvv)
print("Henlo frens i is alieving")
#client.command()
async def ping(ctx):
before = time.monotonic()
message = await ctx.send("Pong!")
ping = (time.monotonic() - before) * 1000
await message.edit(content=f"Pong! `{int(ping)}ms`")
client.run("TOKEN")```
Don't change_presence (or make API calls) in on_ready within your Bot or Client.
Discord has a high chance to completely disconnect you during the READY or GUILD_CREATE events (1006 close code) and there is nothing you can do to prevent it.
Instead set the activity and status kwargs in the constructor of these Classes.
bot = commands.Bot(command_prefix="!", activity=..., status=...)
As noted in the docs, on_ready is also triggered multiple times, not just once.
I can further confirm this because running your code snippet (without on_ready defined) on my machine actually worked :
to process commands you need to add on_message function
#client.event
async def on_message(msg):
await client.process_commands(msg)

How to fix this problem discord.py bot problem

I created a bot in discord.py which works perfectly before i add a block of code. Once i added the code it stops working. Then i removed the newly added code. But my bot is not working now and sometimes it sends reply after 5 minutes but it doesn't shows any error. Then i created a new bot and pasted the token to check whether there is any problem with my code but it works perfectly.
i am wondering what is the problem bcs it didn't show any error and even with token as it prints in terminal too and even it sends reply to commands sometimes after 5 minutes. I regenerated the token of my bot even but now too the same problem is there.
Can someone say how to make my old bot to work properly? I don't want to start with new bot since my old bot is already in 86 servers and struggled hard to join it in that much servers.
from discord.ext.commands import Bot
from discord.ext import commands
import os
import discord
from random import randint
import keep_alive
import DiscordUtils
import random
from discord_components import *
# intents = discord.Intents.all()
intents = discord.Intents(messages=True, guilds=True,members=True,typing=True,presences=True)
bot = Bot(command_prefix="?", case_insensitive=True,intents=intents)
bot.remove_command("help")
global music
music = DiscordUtils.Music()
DiscordComponents(bot)
#bot.event
async def on_ready():
await bot.change_presence(status=discord.Status.idle, activity=discord.Game(f'on {len(bot.guilds)} servers | ?help'))
print(f"Logged in as {bot.user}")
#bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.MissingRequiredArgument):
title_error_one = 'You have not entered anything after the command'
desc_error_one = 'Use **?help** to see a list of all the commands available'
embed_var_one = discord.Embed(title=title_error_one,
description=desc_error_one,
colour=randint(0, 0xffffff))
await ctx.reply(embed=embed_var_one)
if isinstance(error, commands.MaxConcurrencyReached):
title_error_four = 'Someone is already playing'
desc_error_four = 'Please wait until the person currently playing is done with their turn'
embed_var_four = discord.Embed(title=title_error_four,
description=desc_error_four,
colour=randint(0, 0xffffff))
await ctx.reply(embed=embed_var_four)
if isinstance(error,commands.CommandOnCooldown):
embed = discord.Embed(
title="**Still on Cooldown!**",
description=f"Try again in {error.retry_after:.2f}s.",
colour=randint(0, 0xffffff)
)
await ctx.reply(embed=embed)
if isinstance(error, commands.CommandNotFound):
return
if __name__ == "__main__":
try:
for filename in os.listdir('./cogs'):
if(filename.endswith('.py')):
bot.load_extension(f'cogs.{filename[:-3]}')
except Exception as exc:
print(exc)
keep_alive.keep_alive()
token = os.environ.get("TOKEN")
bot.run(token)
This is the code on main.py file
I don't think other files have any errors. I only doubt with this file.
Thank you for help in advance
Atlast i found that it is due to on_member_update command. Once i commented it. My bot works fine(: and below is my on_member_update code. Is there anything Wrong or some way to make it fast. Below is the code
#commands.Cog.listener()
async def on_member_update(self,before, after):
try:
serverId=after.guild.id
channel_id=int(logs_info.find_one(
{'id': serverId})['action_logs'])
logchannel = self.bot.get_channel(channel_id)
except:
return
IST = pytz.timezone('Asia/Kolkata')
time_now = (datetime.now(IST))
time_now = str(time_now.strftime('%Y-%m-%d %H:%M:%S'))
if len(before.roles) < len(after.roles):
new_role = next(role for role in after.roles if role not in before.roles)
embed=discord.Embed(title=f"Roles given",description=f"{after.mention} has been given {new_role.mention} role",colour=randint(0, 0xffffff))
embed.add_field(name="Role added at",value=time_now)
elif(len(before.roles) > len(after.roles)):
new_role = next(role for role in before.roles if role not in after.roles)
embed=discord.Embed(title=f"Removed",description=f"{new_role.mention} role has been removed from {after.mention}",colour=randint(0, 0xffffff))
embed.add_field(name="Role removed at",value=time_now)
else:
return
embed.set_thumbnail(url=after.guild.icon_url)
embed.set_footer(text=self.bot.user.name,icon_url=self.bot.user.avatar_url)
embed.set_author(name=after.name, icon_url=after.avatar_url)
await logchannel.send(embed=embed)
Problem explanation
You use pymongo library to fetch some data in your on_member_update event:
channel_id=int(logs_info.find_one({'id': serverId})['action_logs'])
It's so bad because pymongo is a blocking library. It means that when you interact with your database this library blocks other code. Therefore your bot is so slow. It can't process command while it waits for pymongo request.
Solution
You can use motor instead of pymongo. It's an asynchronous wrapper for pymongo and it is related to this. But motor doesn't block your code execution.

How to execute a python discord bot function in another python script?

I am trying to create a python script that would mute the users of a specific voice channel when executed. Till now I am able to mute everyone by typing the command in the discord text channel, but I would like to mute everyone when another python script tells the bot to. Below is my attempt to do that.
bot.py is the python discord bot:
import discord
from discord.ext import commands
from time import sleep
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
#client.command()
async def play_music_test():
channel = await client.get_channel(voice_channel_number)
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
client.run(TOKEN)
abc.py is the other python script trying to call a function:
from bot import play_music_test
import asyncio
print("")
asyncio.run(play_music_test())
I ran the bot.py first and then tried to execute the abc.py next, bot came online but when executing abc.py, it didn't print or do anything at all. I just started to learn discord.py so if this is a silly question, forgive me.
Instead of running the python file you can import it, or making it a cog
heres an example:
importing: bot.py
import discord
from discord.ext import commands
from time import sleep
from abc import *
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
#client.command(name = "play_music", aliases = ["you can asign aliases like this", "multiple ones too"])
async def play_music_test(ctx, channel): # If the python file doesn't know what channel is, it can be asigned when calling the function in discord, think of it like sys.argv or argparse
if channel == None: channel = ctx.channel # This sets the current channel you are in if none was provided when executing it in discord
# channel = await client.get_channel(channel) now you don't need this line
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
client.run(TOKEN)
importing: abc.py
remove asyncio.run(play_music_test())
use it in discord instead! ex. play_music_test #general
Making it a cog: bot.py
import discord
from discord.ext import commands
from time import sleep
import os
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
for filename in os.listdir("./"):
if filename == "bot.py": continue
else: client.load_extension(f"cogs.{filename[:-3]}")
client.run(TOKEN)
Making it a cog: abc.py
from bot import play_music_test
import asyncio
class Mycog(commands.Cog):
def __init__(self, client):
self.client = client
#client.command(name = "play_music", aliases = ["you can asign aliases like this",
"multiple ones too"])
async def play_music_test(ctx, channel): # If the python file doesn't know what
channel is, it can be asigned when calling the function in discord, think of it like sys.argv or argparse
if channel == None: channel = ctx.channel # This sets the current channel you are in if none was provided when executing it in discord
# channel = await client.get_channel(channel) now you don't need this line
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
def setup(client):
client.add_cog(Mycog(client))
This anwser is not the best but hope this helps!
i'm not sure, if i really understand, what you try to do, but to open a script from another script, i use subprocess.Popen()
but i think, in your case it fits more, to change the way you try to solve your problem. maybe you should take a look into the on_message event and use message.content
?
You can importing your client variable (from bot import play_music_test, client) and then client.loop.create_task(play_music_test())
(Although I do not understand why you would do such a thing)

The channel provided must be a voice channel. error with move_member

import discord
from discord.ext import commands
from discord.ext.commands import Bot
import asyncio
import time
bot = commands.Bot(command_prefix='$')
#bot.event
async def on_ready():
print ("Ready")
#bot.command(pass_context=True)
async def Move(ctx):
#channel to move to '414543063575429131'
#user to move '192361974053470208'
await bot.move_member('192361974053470208', '414543063575429131')
print("done")
bot.run("token_here")
This is my code but I when I try to move the user it gives me the error "The channel provided must be a voice channel."
I know the bot works because I had some simple commands earlier that would reply to messages earlier and they worked fine.
I am new to python and discord bots so I don't really know what to do. Any help is appreciated.
The channel argument to move_member must be a Channel object, not just the channel id. This is noted in the documentation for move_member
You cannot pass in a Object instead of a Channel object in this function.
#bot.command(pass_context=True)
async def move(ctx):
destination = '414543063575429131'
user = '192361974053470208'
await bot.move_member(ctx.message.server.get_member(user), bot.get_channel(destination))
# The get_member doesn't look to be strictly necessary, but it doesn't hurt
# and improves readability
print("done")

Categories

Resources