discord.py command cooldown for ban command - python

I've got this code for my bot, which allows banning users for certain people. However, is there a way to make it so that a staff member can only use the ban command once every 2 hours. I want every other command to have no cooldown, but just for the ban command, have a way to only allow it to be used once per 2 hours per user. Here's the code I've got so far:
#commands.has_permissions(administrator=True)
async def pogoban (ctx, member:discord.User=None, *, reason =None):
if member == None or member == ctx.message.author:
await ctx.channel.send("You cannot ban yourself")
return
if reason == None:
reason = "For breaking the rules."
message = f"You have been banned from {ctx.guild.name} for {reason}"
await member.send(message)
await ctx.guild.ban(member, reason=reason)
await ctx.channel.send(f"{member} is banned!")
What would I need to add/change to add a command cooldown of 2 hours per user for this command. I've tried looking around, but I've only found ways to make all commands have a cooldown, instead of this one specific command. Thanks!

How about something like this:
cooldown = []
#client.command()
async def pogoban(ctx, member: discord.Member = None, *, reason = None):
author = str(ctx.author)
if author in cooldown:
await ctx.send('Calm down! You\'ve already banned someone less that two hours ago.')
return
try:
if reason == None:
reason = 'breaking the rules.'
await member.send(f'You have been banned from **{ctx.guild.name}** for **{reason}**')
await member.ban(reason = reason)
await ctx.send(f'{member.mention} has been banned.')
cooldown.append(author)
await asyncio.sleep(2 * 60 * 60) #The argument is in seconds. 2hr = 7200s
cooldown.remove(author)
except:
await ctx.send('Sorry, you aren\'t allowed to do that.')
Note: Remember to import asyncio. Also remember that once your bot goes offline, all the users stored in the list will be erased.
Please Read
A better approach would be to store the time of banning along with the the authors name and check if the current time is at least an hour more than the saved time. And to make it a lot safer, the authors name along with the time can be saved in a database or an external text file, this way your data won't get erased if the bot goes offline.

Related

How do i code an afk command that comes out the way dyno runs

I need help making a afk command for my discord server. When the afk command is triggered, my bot doesn't respond with a reasoning when you ping the person whos afk. Also, when you return from being afk and type, the bot doesn't send a message saying "(user) is no longer afk". Please help me and tell me what i'm doing wrong and how can I fix this?
afkdict = {User: "their reason"} # somewhere in the code
#bot.command("afk")
async def afk(ctx, reason=None):
afkdict[ctx.user] = reason
await ctx.send("You are now afk. Beware of the real world!")
#bot.event
async def on_message(message):
afkdict = {user: "their reason"}
# some other checks here
for user, reason in afkdict.items():
if user in message.mentions:
if reason is None:
reason = ""
embed = discord.Embed(title=f"{user} is AFK", color=0xFF0000, description=reason[:2500])
await message.reply()
I was expecting this to work, the way dyno works. When i ran the command i got a message back saying user has no context. I dont know what to do anymore.
I think there's a couple of issues. Firstly, you are redefining afkdict in your on_message function it doesn't matter that you're adding users to it in the afk command. Secondly, when you're doing await message.reply(), you're not actually sending the created embed along with it.
I've resolved those problems and changed the logic slightly. Instead of iterating over the users in the afk_dict and checking if they're mentioned, we're iterating over the mentions and seeing if they're in the afk_dict. I'm also using user.id rather user objects as keys.
# defined somewhere
afk_dict = {}
#bot.command()
async def afk(ctx, reason=None):
afk_dict[ctx.user.id] = reason
await ctx.send("You are now afk. Beware of the real world!")
#bot.event
async def on_message(message):
# do whatever else you're doing here
for user in message.mentions:
if user.id not in afk_dict:
continue
# mentioned used is "afk"
reason = afk_dict[user.id] or ""
embed = discord.Embed(title=f"{user.mention} is AFK", color=0xFF0000, description=reason[:2500])
await message.reply(embed=embed)
It looks like you are missing some pieces in your code. Here is an updated version of the code:
afkdict = {}
#bot.command("afk")
async def afk(ctx, reason=None):
user = ctx.message.author
afkdict[user] = reason
await ctx.send(f"You are now AFK. {'Reason: ' + reason if reason else ''}")
#bot.event
async def on_message(message):
for user, reason in afkdict.items():
if user in message.mentions:
if reason is None:
reason = ""
embed = discord.Embed(title=f"{user} is AFK", color=0xFF0000, description=reason[:2500])
await message.channel.send(embed=embed)
if message.author in afkdict:
afkdict.pop(message.author)
await message.channel.send(f"{message.author} is no longer AFK")
In this code, the afk command will add the user who runs the command to the afkdict dictionary along with the reason for being AFK. The on_message event handler will then check if any of the mentioned users are in the afkdict and if so, it will send an embed with the AFK status and reason. Finally, if the author of the message is in the afkdict, it will remove them from the dictionary and send a message indicating that they are no longer AFK.

discord.py "sub help command"

I was wondering if it's possible to make a somewhat "sub help command" basically if I were to do ;help mute it would show how to use the mute command and so on for each command. Kinda like dyno how you can do ?help (command name) and it shows you the usage of the command. I have my own help command already finished but I was thinking about adding to it so if someone did ;help commandname it would show them the usage such as arguments I tried at the bottom but I don't think that will work. If you know how please let me know
#client.hybrid_command(name = "help", with_app_command=True, description="Get a list of commands")
#commands.guild_only()
async def help(ctx, arg = None):
pages = 3
cur_page = 1
roleplayembed = discord.Embed(color=embedcolor, title="Roleplay Commands")
roleplayembed.add_field(name=f"{client.command_prefix}Cuddle", value="Cuddle a user and add a message(Optional)",inline=False)
roleplayembed.add_field(name=f"{client.command_prefix}Hug", value="Hug a user and add a message(Optional)",inline=False)
roleplayembed.add_field(name=f"{client.command_prefix}Kiss", value="Kiss a user and add a message(Optional)",inline=False)
roleplayembed.add_field(name=f"{client.command_prefix}Slap", value="Slap a user and add a message(Optional)",inline=False)
roleplayembed.add_field(name=f"{client.command_prefix}Pat", value="Pat a user and add a message(Optional)",inline=False)
roleplayembed.set_footer(text=f"Page {cur_page+1} of {pages}")
roleplayembed.timestamp = datetime.datetime.utcnow()
basicembed = discord.Embed(color=embedcolor, title="Basic Commands")
basicembed.add_field(name=f"{client.command_prefix}Waifu", value="Posts a random AI Generated Image of a waifu",inline=False)
basicembed.add_field(name=f"{client.command_prefix}8ball", value="Works as an 8 ball",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Ara", value="Gives you a random ara ara from Kurumi Tokisaki",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Wikipedia", value="Search something up on the wiki",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Userinfo", value="Look up info about a user",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Ask", value="Ask the bot a question",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Askwhy", value="Ask the boy a question beginning with 'why'",inline=False)
basicembed.add_field(name=f"{client.command_prefix}Avatar", value="Get a user's avatar or your own avatar",inline=False)
basicembed.set_footer(text=f"Page {cur_page} of {pages}")
basicembed.timestamp = datetime.datetime.utcnow()
moderationembed = discord.Embed(color=embedcolor, title="Moderation Commands")
moderationembed.add_field(name=f"{client.command_prefix}Kick", value="Kick a member",inline=False)
moderationembed.add_field(name=f"{client.command_prefix}Ban", value="Ban a member",inline=False)
moderationembed.add_field(name=f"{client.command_prefix}Slowmode", value="Set the slowmode of a channel",inline=False)
moderationembed.add_field(name=f"{client.command_prefix}Purge", value="Purge an amount of messages in a channel",inline=False)
moderationembed.add_field(name=f"{client.command_prefix}Mute", value="Mute a member for a time and reason",inline=False)
moderationembed.add_field(name=f"{client.command_prefix}Unmute", value="Unmute a member for a time and reason",inline=False)
moderationembed.set_footer(text=f"Page {cur_page+2} of {pages}")
moderationembed.timestamp = datetime.datetime.utcnow()
contents = [basicembed, roleplayembed, moderationembed]
if arg == None:
message = await ctx.send(embed=contents[cur_page-1])
await message.add_reaction("◀️")
await message.add_reaction("▶️")
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in ["◀️", "▶️"]
while True:
try:
reaction, user = await client.wait_for("reaction_add", timeout=60, check=check)
if str(reaction.emoji) == "▶️":
cur_page += 1
elif str(reaction.emoji) == "◀️":
cur_page -= 1
if cur_page > pages: #check if forward on last page
cur_page = 1
elif cur_page < 1: #check if back on first page
cur_page = pages
await message.edit(embed=contents[cur_page-1])
await message.remove_reaction(reaction, user)
except asyncio.TimeoutError:
await message.delete()
break
if arg.lower() == client.command_name:
await ctx.reply(f"{client.command_prefix}{client.command_name}{client.command_argument}")
There are several ways you can do this.
When you're using slash commands (which you are currently not,) there is a really elegant way to do this in the form of SlashCommandGroups. This would get the commands as [command name] help instead, but I don't think that is a downside.
This would work like this, an example I thought of was blocking:
class Block(discord.ext.commands.Cog):
block = SlashCommandGroup("block")
def __init__(self, bot):
self.bot = bot
#block.command(name="add")
async def add(args):
# Something here
#block.command(name="remove")
async def remove(args):
# Something here
#block.command(name="help")
async def help(args):
# Get help message for this command
This would expose the commands block add [args], block remove [args] and block help each of which calls their own sub-command in the cog, and I think this is the cleanest way to get a consistent help system.
You can add this cog to your bot with bot.add_cog(Block(bot)) somewhere in your code. Specifically, I'd look into extensions
Then, for what you want to do, You're not using slash commands, so you don't need to provide autocomplete. If you want to, you can do something really hacky, using this helper function, which will work as long as you have the function in your current scope:
def help(function_name):
return globals()[function_name].__doc__
Now, you can define the help of every function individually using docstrings, and the help command will simply get those doc strings and presumably do something with it.
The way Dyno would do it is more complex, using slash commands again, but really similar to the first version. You simply add a slash command group again, but this time it is for helping specifically. I personally don't like this as much, as I think the code is a lot less clean, but if you really want the help [function] syntax instead of [function] help, this is how to do that:
class Help(discord.ext.commands.Cog):
help = SlashCommandGroup("help")
def __init__(self, bot):
self.bot = bot
#help.command(name="block")
async def block(args):
# Send the user the help response for blocking
#help.command(name="ask")
async def ask(args):
# Send the user the help response for asking
I hope that helps! :-)

Bot isn't responding to my function calls

I need my Discord bot to handle DM's and therefore I wrote this script:
#client.command()
async def dm(ctx, user_id=None, *, args=None):
if user_id != None and args != None:
try:
target = await client.fetch_user(user_id)
await target.send(args)
await ctx.channel.send("'" + args + "' sent to: " + target.name)
except:
await ctx.channel.send("Couldn't dm the given user.")
else:
await ctx.channel.send("You didn't provide a user's id and/or a message.")
My problem now is, until this point in my project I was satisfied by coding if-else-branches into the "on_message()"-function in order to make my bot react to certain commands but now my bot isn't reacting to function calls: When I try to call the "dm"-function (my command prefix is set to ".") it doesn't throw an error in the console it just doesn't work and I'm clueless to what I'm doing wrong here.
Is the code snippet not located in the right place inside my script? What am I missing?
I'd be glad for any form of help. Thanks
As per the docs, you need to add a await client.process_commands(message) at the end of your on_message listener.
https://discordpy.readthedocs.io/en/master/faq.html#why-does-on-message-make-my-commands-stop-working
Edit: Separate question but as I wrote in the comment, here's an example on DM'ing people on a certain time:
from discord.ext import tasks
#tasks.loop(hours=24*7)
async def dm_loop():
user_ids = (123, 456, 789)
for i in user_ids:
user = await client.fetch_user(i)
await user.send("hello")
#client.event
async def on_ready():
dm_loop.start() #remember to add this to your on_ready event
This would make it run on startup and then once a week.

Bot Discord python deleting message only of one person

I'm programming a bot for discord and trying to delete messages but only the one of the bots since I'm doing some commands that make him spam quite a lot.
So what I've found works well to bulk delete would be
#bot.command(pass_context = True)
async def purge(ctx,msglimit : int):
deleted = await bot.purge_from(ctx.message.channel, limit=msglimit)
await bot.say("Cleared **{}** Messages".format(len(deleted)))
but the documentation shows this command
def is_me(m):
return m.author == client.user
deleted = await client.purge_from(channel, limit=100, check=is_me)
await client.send_message(channel, 'Deleted {} message(s)'.format(len(deleted)))
But i can't really guet it to work if someone has an idea
def is_me(m):
return m.author == bot.user
#bot.command(pass_context = True)
async def purge(ctx):
deleted = await bot.purge_from(ctx.message.channel, check=is_me)
await bot.say("Cleared **{}** Messages".format(len(deleted)))
This deletes a certain number of messages (100 by default).
The purge_from method takes an argument called check which is a function that takes a message and returns whether the message should be deleted.
The is_me function will return true if the message author is the bot.
Which means that calling purge will delete the bot that picked up this command. If you need the bot to delete a different user's messages, you will need to change the condition.

DM commands to discord bot

I recently had the idea of DMing my bot commands. For example, a command which would unban me from every server that the bot is on.
Unfortunately I don't have any starting point for the command because I'm not even sure DMing commands is possible.
Keywords like discord.py, command or DM are so common in google that finding any good info on the topic is very hard.
I'm looking for a way for the bot to receive DMs as commands and only accept them from me (my ID is stored in the variable ownerID if anyone wants to share any code).
While I'm mostly looking for the above, some code for the DM unban command would also be very helpful.
EDIT: I was asked to show some example code from my bot. Here is the code for the command number which generates a random number and sends it in a message. I hope this gives you an idea of how my bot is made:
#BSL.command(pass_context = True)
async def number(ctx, minInt, maxInt):
if ctx.message.author.server_permissions.send_messages or ctx.message.author.id == ownerID:
maxInt = int(maxInt)
minInt = int(minInt)
randomInt = random.randint(minInt, maxInt)
randomInt = str(randomInt)
await BSL.send_message(ctx.message.channel, 'Your random number is: ' + randomInt)
else:
await BSL.send_message(ctx.message.channel, 'Sorry, you do not have the permissions to do that #{}!'.format(ctx.message.author))
You can send commands in private messages. Something like
#BSL.command(pass_context=True)
async def unban(ctx):
if ctx.message.channel.is_private and ctx.message.author.id == ownerID:
owner = await BSL.get_user_info(ownerID)
await BSL.say('Ok {}, unbanning you.'.format(owner.name))
for server in BSL.servers:
try:
await BSL.unban(server, owner) # I think having the same name should be fine. If you see weird errors this may be why.
await BSL.say('Unbanned from {}'.format(server.name))
except discord.HTTPException as e:
await BSL.say('Unbanning failed in {} because\n{}'.format(server.name, e.text))
except discord.Forbidden:
await BSL.say('Forbidden to unban in {}'.format(server.name))
else:
if ctx.message.author.id != ownerID:
await BSL.say('You are not my owner')
if not ctx.message.channel.is_private:
await BSL.say('This is a public channel')
should work. I'm not sure what happens if you try to unban a user who is not banned, that may be what raises HTTPException

Categories