From 5be41d3f464f6da365491a131f6e241c9b20a7c2 Mon Sep 17 00:00:00 2001 From: Krypton Date: Sun, 9 Jan 2022 15:50:50 +0100 Subject: [PATCH] Template v4.1 * Added the `hackban` command * Separated slash commands and normal commands so that you remove one of them more easily * Moved normal commands in [`cogs/normal`](cogs/normal) * Moved slash commands in [`cogs/slash`](cogs/slash) --- README.md | 3 +- TODO.md | 18 +- UPDATES.md | 7 + bot.py | 78 +++- cogs/general.py | 418 ------------------ cogs/normal/fun-normal.py | 184 ++++++++ cogs/normal/general-normal.py | 230 ++++++++++ cogs/normal/moderation-normal.py | 254 +++++++++++ cogs/normal/owner-normal.py | 158 +++++++ cogs/normal/template-normal.py | 50 +++ cogs/{fun.py => slash/fun-slash.py} | 84 +--- cogs/slash/general-slash.py | 238 ++++++++++ .../moderation-slash.py} | 252 +++-------- cogs/{owner.py => slash/owner-slash.py} | 154 +------ cogs/{template.py => slash/template-slash.py} | 19 +- exceptions/__init__.py | 10 +- helpers/checks.py | 17 +- helpers/json_manager.py | 14 +- 18 files changed, 1336 insertions(+), 852 deletions(-) delete mode 100644 cogs/general.py create mode 100644 cogs/normal/fun-normal.py create mode 100644 cogs/normal/general-normal.py create mode 100644 cogs/normal/moderation-normal.py create mode 100644 cogs/normal/owner-normal.py create mode 100644 cogs/normal/template-normal.py rename cogs/{fun.py => slash/fun-slash.py} (70%) create mode 100644 cogs/slash/general-slash.py rename cogs/{moderation.py => slash/moderation-slash.py} (56%) rename cogs/{owner.py => slash/owner-slash.py} (57%) rename cogs/{template.py => slash/template-slash.py} (77%) diff --git a/README.md b/README.md index 95d87ed5..f6f0596e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ I would've been happy if there were any template existing. However, there wasn't decided to create my own template to let you guys create your discord bot easily. Please note that this template is not supposed to be the best template, but a good template to start learning how -discord.py works and to make your own bot easily. After the version 4.0 the template is using disnake, as discord.py has stoppped development. +discord.py works and to make your own bot easily. After the version 4.0 the template is using disnake, as discord.py has +stoppped development. If you plan to use this template to make your own template or bot, you **have to**: diff --git a/TODO.md b/TODO.md index 1ebd4347..ab31d214 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,22 @@ # TODO List -* [ ] Add a *"hackban"* command (ban a user by ID from a guild without him being in it) -* [ ] Add a basic slur word filter -* [ ] Add a code of conduct for the repository +## Version 4.2 (Goal: Middle January 2022) + +* [ ] Add a timeout command. + +## Version 4.3 (Goal: End February 2022) + * [ ] Add a lock command that will let administrators temporary lock a channel, useful in case of a raid * [ ] Add an archive command that lets administrators archive a text channel in a text file + +## No milestone + +* [ ] Add a code of conduct for the repository +* [ ] Add a basic slur word filter * [ ] Add an issue and pull request template for the repository + +## Done + +* [X] Add a *"hackban"* command (ban a user by ID from a guild without the user being in it) * [X] Be able to save data to JSON (blacklist, owners, etc.) * [X] Move config to JSON \ No newline at end of file diff --git a/UPDATES.md b/UPDATES.md index 62f28999..f37a955a 100644 --- a/UPDATES.md +++ b/UPDATES.md @@ -2,6 +2,13 @@ Here is the list of all the updates that I made on this template. +### Version 4.1 (09 January 2022) + +* Added the `hackban` command +* Separated slash commands and normal commands so that you remove one of them more easily + * Moved normal commands in [`cogs/normal`](cogs/normal) + * Moved slash commands in [`cogs/slash`](cogs/slash) + ### Version 4.0.1 * Fixed some *weird* code diff --git a/bot.py b/bot.py index 8f75a3f8..dc869a4d 100644 --- a/bot.py +++ b/bot.py @@ -3,7 +3,7 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json @@ -16,6 +16,7 @@ from disnake import ApplicationCommandInteraction from disnake.ext import tasks, commands from disnake.ext.commands import Bot +from disnake.ext.commands import Context import exceptions @@ -60,9 +61,11 @@ bot = Bot(command_prefix=config["prefix"], intents=intents) -# The code in this even is executed when the bot is ready @bot.event -async def on_ready(): +async def on_ready() -> None: + """ + The code in this even is executed when the bot is ready + """ print(f"Logged in as {bot.user.name}") print(f"disnake API version: {disnake.__version__}") print(f"Python version: {platform.python_version()}") @@ -71,9 +74,11 @@ async def on_ready(): status_task.start() -# Setup the game status task of the bot @tasks.loop(minutes=1.0) -async def status_task(): +async def status_task() -> None: + """ + Setup the game status task of the bot + """ statuses = ["with you!", "with Krypton!", "with humans!"] await bot.change_presence(activity=disnake.Game(random.choice(statuses))) @@ -81,37 +86,57 @@ async def status_task(): # Removes the default help command of discord.py to be able to create our custom help command. bot.remove_command("help") -if __name__ == "__main__": - for file in os.listdir("./cogs"): + +def load_commands(command_type: str) -> None: + for file in os.listdir(f"./cogs/{command_type}"): if file.endswith(".py"): extension = file[:-3] try: - bot.load_extension(f"cogs.{extension}") + bot.load_extension(f"cogs.{command_type}.{extension}") print(f"Loaded extension '{extension}'") except Exception as e: exception = f"{type(e).__name__}: {e}" print(f"Failed to load extension {extension}\n{exception}") -# The code in this event is executed every time someone sends a message, with or without the prefix +if __name__ == "__main__": + """ + This will automatically load slash commands and normal commands located in their respective folder. + + If you want to remove slash commands, which is not recommended due to the Message Intent being a privileged intent, you can remove the loading of slash commands below. + """ + load_commands("slash") + load_commands("normal") + + @bot.event -async def on_message(message: disnake.Message): - # Ignores if a command is being executed by a bot or by the bot itself +async def on_message(message: disnake.Message) -> None: + """ + The code in this event is executed every time someone sends a message, with or without the prefix + :param message: The message that was sent. + """ if message.author == bot.user or message.author.bot: return await bot.process_commands(message) -# The code in this event is executed every time a slash command has been *successfully* executed @bot.event -async def on_slash_command(interaction: ApplicationCommandInteraction): +async def on_slash_command(interaction: ApplicationCommandInteraction) -> None: + """ + The code in this event is executed every time a slash command has been *successfully* executed + :param interaction: The slash command that has been executed. + """ print( f"Executed {interaction.data.name} command in {interaction.guild.name} (ID: {interaction.guild.id}) by {interaction.author} (ID: {interaction.author.id})") -# The code in this event is executed every time a valid slash command catches an error @bot.event -async def on_slash_command_error(interaction: ApplicationCommandInteraction, error: Exception): +async def on_slash_command_error(interaction: ApplicationCommandInteraction, error: Exception) -> None: + """ + The code in this event is executed every time a valid slash command catches an error + :param interaction: The slash command that failed executing. + :param error: The error that has been faced. + """ if isinstance(error, exceptions.UserBlacklisted): """ The code here will only execute if the error is an instance of 'UserBlacklisted', which can occur when using @@ -138,19 +163,26 @@ async def on_slash_command_error(interaction: ApplicationCommandInteraction, err raise error -# The code in this event is executed every time a normal command has been *successfully* executed @bot.event -async def on_command_completion(ctx): - fullCommandName = ctx.command.qualified_name - split = fullCommandName.split(" ") - executedCommand = str(split[0]) +async def on_command_completion(context: Context) -> None: + """ + The code in this event is executed every time a normal command has been *successfully* executed + :param context: The context of the command that has been executed. + """ + full_command_name = context.command.qualified_name + split = full_command_name.split(" ") + executed_command = str(split[0]) print( - f"Executed {executedCommand} command in {ctx.guild.name} (ID: {ctx.message.guild.id}) by {ctx.message.author} (ID: {ctx.message.author.id})") + f"Executed {executed_command} command in {context.guild.name} (ID: {context.message.guild.id}) by {context.message.author} (ID: {context.message.author.id})") -# The code in this event is executed every time a normal valid command catches an error @bot.event -async def on_command_error(context, error): +async def on_command_error(context: Context, error) -> None: + """ + The code in this event is executed every time a normal valid command catches an error + :param context: The normal command that failed executing. + :param error: The error that has been faced. + """ if isinstance(error, commands.CommandOnCooldown): minutes, seconds = divmod(error.retry_after, 60) hours, minutes = divmod(minutes, 60) diff --git a/cogs/general.py b/cogs/general.py deleted file mode 100644 index d0d41286..00000000 --- a/cogs/general.py +++ /dev/null @@ -1,418 +0,0 @@ -"""" -Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) -Description: -This is a template to create your own discord bot in python. - -Version: 4.0.1 -""" - -import json -import os -import platform -import random -import sys - -import aiohttp -import disnake -from disnake import ApplicationCommandInteraction, Option, OptionType -from disnake.ext import commands -from disnake.ext.commands import Context - -from helpers import checks - -if not os.path.isfile("config.json"): - sys.exit("'config.json' not found! Please add it and try again.") -else: - with open("config.json") as file: - config = json.load(file) - - -class General(commands.Cog, name="general"): - def __init__(self, bot): - self.bot = bot - - @commands.slash_command( - name="botinfo", - description="Get some useful (or not) information about the bot.", - ) - @checks.not_blacklisted() - async def botinfo(self, interaction: ApplicationCommandInteraction): - """ - Get some useful (or not) information about the bot. - """ - embed = disnake.Embed( - description="Used [Krypton's](https://krypt0n.co.uk) template", - color=0x9C84EF - ) - embed.set_author( - name="Bot Information" - ) - embed.add_field( - name="Owner:", - value="Krypton#7331", - inline=True - ) - embed.add_field( - name="Python Version:", - value=f"{platform.python_version()}", - inline=True - ) - embed.add_field( - name="Prefix:", - value=f"/ (Slash Commands)", - inline=False - ) - embed.set_footer( - text=f"Requested by {interaction.author}" - ) - await interaction.send(embed=embed) - - @commands.command( - name="botinfo", - description="Get some useful (or not) information about the bot.", - ) - @checks.not_blacklisted() - async def botinfo(self, context: Context): - """ - Get some useful (or not) information about the bot. - """ - embed = disnake.Embed( - description="Used [Krypton's](https://krypt0n.co.uk) template", - color=0x9C84EF - ) - embed.set_author( - name="Bot Information" - ) - embed.add_field( - name="Owner:", - value="Krypton#7331", - inline=True - ) - embed.add_field( - name="Python Version:", - value=f"{platform.python_version()}", - inline=True - ) - embed.add_field( - name="Prefix:", - value=f"/ (Slash Commands)", - inline=False - ) - embed.set_footer( - text=f"Requested by {context.author}" - ) - await context.send(embed=embed) - - @commands.slash_command( - name="serverinfo", - description="Get some useful (or not) information about the server.", - ) - @checks.not_blacklisted() - async def serverinfo(self, interaction: ApplicationCommandInteraction): - """ - Get some useful (or not) information about the server. - """ - roles = [role.name for role in interaction.guild.roles] - if len(roles) > 50: - roles = roles[:50] - roles.append(f">>>> Displaying[50/{len(roles)}] Roles") - roles = ", ".join(roles) - - embed = disnake.Embed( - title="**Server Name:**", - description=f"{interaction.guild}", - color=0x9C84EF - ) - embed.set_thumbnail( - url=interaction.guild.icon.url - ) - embed.add_field( - name="Server ID", - value=interaction.guild.id - ) - embed.add_field( - name="Member Count", - value=interaction.guild.member_count - ) - embed.add_field( - name="Text/Voice Channels", - value=f"{len(interaction.guild.channels)}" - ) - embed.add_field( - name=f"Roles ({len(interaction.guild.roles)})", - value=roles - ) - embed.set_footer( - text=f"Created at: {interaction.guild.created_at}" - ) - await interaction.send(embed=embed) - - @commands.command( - name="serverinfo", - description="Get some useful (or not) information about the server.", - ) - @checks.not_blacklisted() - async def serverinfo(self, context: Context): - """ - Get some useful (or not) information about the server. - """ - roles = [role.name for role in context.guild.roles] - if len(roles) > 50: - roles = roles[:50] - roles.append(f">>>> Displaying[50/{len(roles)}] Roles") - roles = ", ".join(roles) - - embed = disnake.Embed( - title="**Server Name:**", - description=f"{context.guild}", - color=0x9C84EF - ) - embed.set_thumbnail( - url=context.guild.icon.url - ) - embed.add_field( - name="Server ID", - value=context.guild.id - ) - embed.add_field( - name="Member Count", - value=context.guild.member_count - ) - embed.add_field( - name="Text/Voice Channels", - value=f"{len(context.guild.channels)}" - ) - embed.add_field( - name=f"Roles ({len(context.guild.roles)})", - value=roles - ) - embed.set_footer( - text=f"Created at: {context.guild.created_at}" - ) - await context.send(embed=embed) - - @commands.slash_command( - name="ping", - description="Check if the bot is alive.", - ) - @checks.not_blacklisted() - async def ping(self, interaction: ApplicationCommandInteraction): - """ - Check if the bot is alive. - """ - embed = disnake.Embed( - title="🏓 Pong!", - description=f"The bot latency is {round(self.bot.latency * 1000)}ms.", - color=0x9C84EF - ) - await interaction.send(embed=embed) - - @commands.command( - name="ping", - description="Check if the bot is alive.", - ) - @checks.not_blacklisted() - async def ping(self, context: Context): - """ - Check if the bot is alive. - """ - embed = disnake.Embed( - title="🏓 Pong!", - description=f"The bot latency is {round(self.bot.latency * 1000)}ms.", - color=0x9C84EF - ) - await context.send(embed=embed) - - @commands.slash_command( - name="invite", - description="Get the invite link of the bot to be able to invite it.", - ) - @checks.not_blacklisted() - async def invite(self, interaction: ApplicationCommandInteraction): - """ - Get the invite link of the bot to be able to invite it. - """ - embed = disnake.Embed( - description=f"Invite me by clicking [here](https://discordapp.com/oauth2/authorize?&client_id={config['application_id']}&scope=bot+applications.commands&permissions={config['permissions']}).", - color=0xD75BF4 - ) - try: - # To know what permissions to give to your bot, please see here: https://discordapi.com/permissions.html and remember to not give Administrator permissions. - await interaction.author.send(embed=embed) - await interaction.send("I sent you a private message!") - except disnake.Forbidden: - await interaction.send(embed=embed) - - @commands.command( - name="invite", - description="Get the invite link of the bot to be able to invite it.", - ) - @checks.not_blacklisted() - async def invite(self, context: Context): - """ - Get the invite link of the bot to be able to invite it. - """ - embed = disnake.Embed( - description=f"Invite me by clicking [here](https://discordapp.com/oauth2/authorize?&client_id={config['application_id']}&scope=bot+applications.commands&permissions={config['permissions']}).", - color=0xD75BF4 - ) - try: - # To know what permissions to give to your bot, please see here: https://discordapi.com/permissions.html and remember to not give Administrator permissions. - await context.author.send(embed=embed) - await context.send("I sent you a private message!") - except disnake.Forbidden: - await context.send(embed=embed) - - @commands.slash_command( - name="server", - description="Get the invite link of the discord server of the bot for some support.", - ) - @checks.not_blacklisted() - async def server(self, interaction: ApplicationCommandInteraction): - """ - Get the invite link of the discord server of the bot for some support. - """ - embed = disnake.Embed( - description=f"Join the support server for the bot by clicking [here](https://discord.gg/mTBrXyWxAF).", - color=0xD75BF4 - ) - try: - await interaction.author.send(embed=embed) - await interaction.send("I sent you a private message!") - except disnake.Forbidden: - await interaction.send(embed=embed) - - @commands.command( - name="server", - description="Get the invite link of the discord server of the bot for some support.", - ) - @checks.not_blacklisted() - async def server(self, context: Context): - """ - Get the invite link of the discord server of the bot for some support. - """ - embed = disnake.Embed( - description=f"Join the support server for the bot by clicking [here](https://discord.gg/mTBrXyWxAF).", - color=0xD75BF4 - ) - try: - await context.author.send(embed=embed) - await context.send("I sent you a private message!") - except disnake.Forbidden: - await context.send(embed=embed) - - @commands.slash_command( - name="8ball", - description="Ask any question to the bot.", - options=[ - Option( - name="question", - description="The question you want to ask.", - type=OptionType.string, - required=True - ) - ], - ) - @checks.not_blacklisted() - async def eight_ball(self, interaction: ApplicationCommandInteraction, question: str): - """ - Ask any question to the bot. - """ - answers = ['It is certain.', 'It is decidedly so.', 'You may rely on it.', 'Without a doubt.', - 'Yes - definitely.', 'As I see, yes.', 'Most likely.', 'Outlook good.', 'Yes.', - 'Signs point to yes.', 'Reply hazy, try again.', 'Ask again later.', 'Better not tell you now.', - 'Cannot predict now.', 'Concentrate and ask again later.', 'Don\'t count on it.', 'My reply is no.', - 'My sources say no.', 'Outlook not so good.', 'Very doubtful.'] - embed = disnake.Embed( - title="**My Answer:**", - description=f"{random.choice(answers)}", - color=0x9C84EF - ) - embed.set_footer( - text=f"The question was: {question}" - ) - await interaction.send(embed=embed) - - @commands.command( - name="8ball", - description="Ask any question to the bot.", - ) - @checks.not_blacklisted() - async def eight_ball(self, context: Context, *, question: str): - """ - Ask any question to the bot. - """ - answers = ['It is certain.', 'It is decidedly so.', 'You may rely on it.', 'Without a doubt.', - 'Yes - definitely.', 'As I see, yes.', 'Most likely.', 'Outlook good.', 'Yes.', - 'Signs point to yes.', 'Reply hazy, try again.', 'Ask again later.', 'Better not tell you now.', - 'Cannot predict now.', 'Concentrate and ask again later.', 'Don\'t count on it.', 'My reply is no.', - 'My sources say no.', 'Outlook not so good.', 'Very doubtful.'] - embed = disnake.Embed( - title="**My Answer:**", - description=f"{random.choice(answers)}", - color=0x9C84EF - ) - embed.set_footer( - text=f"The question was: {question}" - ) - await context.send(embed=embed) - - @commands.slash_command( - name="bitcoin", - description="Get the current price of bitcoin.", - ) - @checks.not_blacklisted() - async def bitcoin(self, interaction: ApplicationCommandInteraction): - """ - Get the current price of bitcoin. - """ - # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request - async with aiohttp.ClientSession() as session: - async with session.get("https://api.coindesk.com/v1/bpi/currentprice/BTC.json") as request: - if request.status == 200: - data = await request.json( - content_type="application/javascript") # For some reason the returned content is of type JavaScript - embed = disnake.Embed( - title="Bitcoin price", - description=f"The current price is {data['bpi']['USD']['rate']} :dollar:", - color=0x9C84EF - ) - else: - embed = disnake.Embed( - title="Error!", - description="There is something wrong with the API, please try again later", - color=0xE02B2B - ) - await interaction.send(embed=embed) - - @commands.command( - name="bitcoin", - description="Get the current price of bitcoin.", - ) - @checks.not_blacklisted() - async def bitcoin(self, context: Context): - """ - Get the current price of bitcoin. - """ - # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request - async with aiohttp.ClientSession() as session: - async with session.get("https://api.coindesk.com/v1/bpi/currentprice/BTC.json") as request: - if request.status == 200: - data = await request.json( - content_type="application/javascript") # For some reason the returned content is of type JavaScript - embed = disnake.Embed( - title="Bitcoin price", - description=f"The current price is {data['bpi']['USD']['rate']} :dollar:", - color=0x9C84EF - ) - else: - embed = disnake.Embed( - title="Error!", - description="There is something wrong with the API, please try again later", - color=0xE02B2B - ) - await context.send(embed=embed) - - -def setup(bot): - bot.add_cog(General(bot)) diff --git a/cogs/normal/fun-normal.py b/cogs/normal/fun-normal.py new file mode 100644 index 00000000..faa51a39 --- /dev/null +++ b/cogs/normal/fun-normal.py @@ -0,0 +1,184 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import random +import sys + +import aiohttp +import disnake +from disnake.ext import commands +from disnake.ext.commands import Context + +from helpers import checks + +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +class Choice(disnake.ui.View): + def __init__(self): + super().__init__() + self.choice = None + + @disnake.ui.button(label="Heads", style=disnake.ButtonStyle.blurple) + async def confirm(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): + self.choice = button.label.lower() + self.stop() + + @disnake.ui.button(label="Tails", style=disnake.ButtonStyle.blurple) + async def cancel(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): + self.choice = button.label.lower() + self.stop() + + +class RockPaperScissors(disnake.ui.Select): + def __init__(self): + + options = [ + disnake.SelectOption( + label="Scissors", description="You choose scissors.", emoji="🪨" + ), + disnake.SelectOption( + label="Rock", description="You choose rock.", emoji="🧻" + ), + disnake.SelectOption( + label="paper", description="You choose paper.", emoji="✂" + ), + ] + + super().__init__( + placeholder="Choose...", + min_values=1, + max_values=1, + options=options, + ) + + async def callback(self, interaction: disnake.MessageInteraction): + choices = { + "rock": 0, + "paper": 1, + "scissors": 2, + } + user_choice = self.values[0].lower() + user_choice_index = choices[user_choice] + + bot_choice = random.choice(list(choices.keys())) + bot_choice_index = choices[bot_choice] + + result_embed = disnake.Embed(color=0x9C84EF) + result_embed.set_author(name=interaction.author.display_name, icon_url=interaction.author.avatar.url) + + if user_choice_index == bot_choice_index: + result_embed.description = f"**That's a draw!**\nYou've chosen {user_choice} and I've chosen {bot_choice}." + result_embed.colour = 0xF59E42 + elif user_choice_index == 0 and bot_choice_index == 2: + result_embed.description = f"**You won!**\nYou've chosen {user_choice} and I've chosen {bot_choice}." + result_embed.colour = 0x9C84EF + elif user_choice_index == 1 and bot_choice_index == 0: + result_embed.description = f"**You won!**\nYou've chosen {user_choice} and I've chosen {bot_choice}." + result_embed.colour = 0x9C84EF + elif user_choice_index == 2 and bot_choice_index == 1: + result_embed.description = f"**You won!**\nYou've chosen {user_choice} and I've chosen {bot_choice}." + result_embed.colour = 0x9C84EF + else: + result_embed.description = f"**I won!**\nYou've chosen {user_choice} and I've chosen {bot_choice}." + result_embed.colour = 0xE02B2B + await interaction.response.defer() + await interaction.edit_original_message(embed=result_embed, content=None, view=None) + + +class RockPaperScissorsView(disnake.ui.View): + def __init__(self): + super().__init__() + + self.add_item(RockPaperScissors()) + + +class Fun(commands.Cog, name="fun-normal"): + def __init__(self, bot): + self.bot = bot + + @commands.command( + name="randomfact", + description="Get a random fact." + ) + @checks.not_blacklisted() + async def randomfact(self, context: Context): + """ + Get a random fact. + :param context: The context in which the command has been executed. + """ + # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request + async with aiohttp.ClientSession() as session: + async with session.get("https://uselessfacts.jsph.pl/random.json?language=en") as request: + if request.status == 200: + data = await request.json() + embed = disnake.Embed( + description=data["text"], + color=0xD75BF4 + ) + else: + embed = disnake.Embed( + title="Error!", + description="There is something wrong with the API, please try again later", + color=0xE02B2B + ) + await context.send(embed=embed) + + @commands.command( + name="coinflip", + description="Make a coin flip, but give your bet before." + ) + @checks.not_blacklisted() + async def coinflip(self, context: Context) -> None: + """ + Make a coin flip, but give your bet before. + :param context: The context in which the command has been executed. + """ + buttons = Choice() + embed = disnake.Embed( + description="What is your bet?", + color=0x9C84EF + ) + message = await context.send(embed=embed, view=buttons) + await buttons.wait() # We wait for the user to click a button. + result = random.choice(["heads", "tails"]) + if buttons.choice == result: + # User guessed correctly + embed = disnake.Embed( + description=f"Correct! You guessed `{buttons.choice}` and I flipped the coin to `{result}`.", + color=0x9C84EF + ) + else: + embed = disnake.Embed( + description=f"Woops! You guessed `{buttons.choice}` and I flipped the coin to `{result}`, better luck next time!", + color=0xE02B2B + ) + await message.edit(embed=embed, view=None) + + @commands.command( + name="rps", + description="Play the rock paper scissors against the bot." + ) + @checks.not_blacklisted() + async def rock_paper_scissors(self, context: Context) -> None: + """ + Play the rock paper scissors game against the bot. + :param context: The context in which the command has been executed. + """ + view = RockPaperScissorsView() + await context.send("Please make your choice", view=view) + + +def setup(bot): + bot.add_cog(Fun(bot)) diff --git a/cogs/normal/general-normal.py b/cogs/normal/general-normal.py new file mode 100644 index 00000000..ed64d474 --- /dev/null +++ b/cogs/normal/general-normal.py @@ -0,0 +1,230 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import platform +import random +import sys + +import aiohttp +import disnake +from disnake.ext import commands +from disnake.ext.commands import Context + +from helpers import checks + +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +class General(commands.Cog, name="general-normal"): + def __init__(self, bot): + self.bot = bot + + @commands.command( + name="botinfo", + description="Get some useful (or not) information about the bot.", + ) + @checks.not_blacklisted() + async def botinfo(self, context: Context) -> None: + """ + Get some useful (or not) information about the bot. + :param context: The context in which the command has been executed. + """ + embed = disnake.Embed( + description="Used [Krypton's](https://krypt0n.co.uk) template", + color=0x9C84EF + ) + embed.set_author( + name="Bot Information" + ) + embed.add_field( + name="Owner:", + value="Krypton#7331", + inline=True + ) + embed.add_field( + name="Python Version:", + value=f"{platform.python_version()}", + inline=True + ) + embed.add_field( + name="Prefix:", + value=f"/ (Slash Commands) or {config['prefix']} for normal commands", + inline=False + ) + embed.set_footer( + text=f"Requested by {context.author}" + ) + await context.send(embed=embed) + + @commands.command( + name="serverinfo", + description="Get some useful (or not) information about the server.", + ) + @checks.not_blacklisted() + async def serverinfo(self, context: Context) -> None: + """ + Get some useful (or not) information about the server. + :param context: The context in which the command has been executed. + """ + roles = [role.name for role in context.guild.roles] + if len(roles) > 50: + roles = roles[:50] + roles.append(f">>>> Displaying[50/{len(roles)}] Roles") + roles = ", ".join(roles) + + embed = disnake.Embed( + title="**Server Name:**", + description=f"{context.guild}", + color=0x9C84EF + ) + embed.set_thumbnail( + url=context.guild.icon.url + ) + embed.add_field( + name="Server ID", + value=context.guild.id + ) + embed.add_field( + name="Member Count", + value=context.guild.member_count + ) + embed.add_field( + name="Text/Voice Channels", + value=f"{len(context.guild.channels)}" + ) + embed.add_field( + name=f"Roles ({len(context.guild.roles)})", + value=roles + ) + embed.set_footer( + text=f"Created at: {context.guild.created_at}" + ) + await context.send(embed=embed) + + @commands.command( + name="ping", + description="Check if the bot is alive.", + ) + @checks.not_blacklisted() + async def ping(self, context: Context) -> None: + """ + Check if the bot is alive. + :param context: The context in which the command has been executed. + """ + embed = disnake.Embed( + title="🏓 Pong!", + description=f"The bot latency is {round(self.bot.latency * 1000)}ms.", + color=0x9C84EF + ) + await context.send(embed=embed) + + @commands.command( + name="invite", + description="Get the invite link of the bot to be able to invite it.", + ) + @checks.not_blacklisted() + async def invite(self, context: Context) -> None: + """ + Get the invite link of the bot to be able to invite it. + :param context: The context in which the command has been executed. + """ + embed = disnake.Embed( + description=f"Invite me by clicking [here](https://discordapp.com/oauth2/authorize?&client_id={config['application_id']}&scope=bot+applications.commands&permissions={config['permissions']}).", + color=0xD75BF4 + ) + try: + # To know what permissions to give to your bot, please see here: https://discordapi.com/permissions.html and remember to not give Administrator permissions. + await context.author.send(embed=embed) + await context.send("I sent you a private message!") + except disnake.Forbidden: + await context.send(embed=embed) + + @commands.command( + name="server", + description="Get the invite link of the discord server of the bot for some support.", + ) + @checks.not_blacklisted() + async def server(self, context: Context) -> None: + """ + Get the invite link of the discord server of the bot for some support. + :param context: The context in which the command has been executed. + """ + embed = disnake.Embed( + description=f"Join the support server for the bot by clicking [here](https://discord.gg/mTBrXyWxAF).", + color=0xD75BF4 + ) + try: + await context.author.send(embed=embed) + await context.send("I sent you a private message!") + except disnake.Forbidden: + await context.send(embed=embed) + + @commands.command( + name="8ball", + description="Ask any question to the bot.", + ) + @checks.not_blacklisted() + async def eight_ball(self, context: Context, *, question: str) -> None: + """ + Ask any question to the bot. + :param context: The context in which the command has been executed. + :param question: The question that should be asked by the user. + """ + answers = ["It is certain.", "It is decidedly so.", "You may rely on it.", "Without a doubt.", + "Yes - definitely.", "As I see, yes.", "Most likely.", "Outlook good.", "Yes.", + "Signs point to yes.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", + "Cannot predict now.", "Concentrate and ask again later.", "Don't count on it.", "My reply is no.", + "My sources say no.", "Outlook not so good.", "Very doubtful."] + embed = disnake.Embed( + title="**My Answer:**", + description=f"{random.choice(answers)}", + color=0x9C84EF + ) + embed.set_footer( + text=f"The question was: {question}" + ) + await context.send(embed=embed) + + @commands.command( + name="bitcoin", + description="Get the current price of bitcoin.", + ) + @checks.not_blacklisted() + async def bitcoin(self, context: Context) -> None: + """ + Get the current price of bitcoin. + :param context: The context in which the command has been executed. + """ + # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request + async with aiohttp.ClientSession() as session: + async with session.get("https://api.coindesk.com/v1/bpi/currentprice/BTC.json") as request: + if request.status == 200: + data = await request.json( + content_type="application/javascript") # For some reason the returned content is of type JavaScript + embed = disnake.Embed( + title="Bitcoin price", + description=f"The current price is {data['bpi']['USD']['rate']} :dollar:", + color=0x9C84EF + ) + else: + embed = disnake.Embed( + title="Error!", + description="There is something wrong with the API, please try again later", + color=0xE02B2B + ) + await context.send(embed=embed) + + +def setup(bot): + bot.add_cog(General(bot)) diff --git a/cogs/normal/moderation-normal.py b/cogs/normal/moderation-normal.py new file mode 100644 index 00000000..58a2d586 --- /dev/null +++ b/cogs/normal/moderation-normal.py @@ -0,0 +1,254 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import sys + +import disnake +from disnake.ext import commands +from disnake.ext.commands import Context + +from helpers import checks + +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +class Moderation(commands.Cog, name="moderation-normal"): + def __init__(self, bot): + self.bot = bot + + @commands.command( + name="kick", + description="Kick a user out of the server.", + ) + @commands.has_permissions(kick_members=True) + @checks.not_blacklisted() + async def kick(self, context: Context, member: disnake.Member, *, reason: str = "Not specified") -> None: + """ + Kick a user out of the server. + :param context: The context in which the command has been executed. + :param member: The member that should be kicked from the server. + :param reason: The reason for the kick. Default is "Not specified". + """ + if member.guild_permissions.administrator: + embed = disnake.Embed( + title="Error!", + description="User has Admin permissions.", + color=0xE02B2B + ) + await context.send(embed=embed) + else: + try: + embed = disnake.Embed( + title="User Kicked!", + description=f"**{member}** was kicked by **{context.author}**!", + color=0x9C84EF + ) + embed.add_field( + name="Reason:", + value=reason + ) + await context.send(embed=embed) + try: + await member.send( + f"You were kicked by **{context.author}**!\nReason: {reason}" + ) + except disnake.Forbidden: + # Couldn't send a message in the private messages of the user + pass + await member.kick(reason=reason) + except: + embed = disnake.Embed( + title="Error!", + description="An error occurred while trying to kick the user. Make sure my role is above the role of the user you want to kick.", + color=0xE02B2B + ) + await context.send(embed=embed) + + @commands.command( + name="nick", + description="Change the nickname of a user on a server.", + ) + @commands.has_permissions(manage_nicknames=True) + @checks.not_blacklisted() + async def nick(self, context: Context, member: disnake.Member, *, nickname: str = None) -> None: + """ + Change the nickname of a user on a server. + :param context: The context in which the command has been executed. + :param member: The member that should have its nickname changed. + :param nickname: The new nickname of the user. Default is None, which will reset the nickname. + """ + try: + await member.edit(nick=nickname) + embed = disnake.Embed( + title="Changed Nickname!", + description=f"**{member}'s** new nickname is **{nickname}**!", + color=0x9C84EF + ) + await context.send(embed=embed) + except: + embed = disnake.Embed( + title="Error!", + description="An error occurred while trying to change the nickname of the user. Make sure my role is above the role of the user you want to change the nickname.", + color=0xE02B2B + ) + await context.send(embed=embed) + + @commands.command( + name="ban", + description="Bans a user from the server.", + ) + @commands.has_permissions(ban_members=True) + @checks.not_blacklisted() + async def ban(self, context: Context, member: disnake.Member, *, reason: str = "Not specified") -> None: + """ + Bans a user from the server. + :param context: The context in which the command has been executed. + :param member: The member that should be banned from the server. + :param reason: The reason for the ban. Default is "Not specified". + """ + try: + if member.guild_permissions.administrator: + embed = disnake.Embed( + title="Error!", + description="User has Admin permissions.", + color=0xE02B2B + ) + await context.send(embed=embed) + else: + embed = disnake.Embed( + title="User Banned!", + description=f"**{member}** was banned by **{context.author}**!", + color=0x9C84EF + ) + embed.add_field( + name="Reason:", + value=reason + ) + await context.send(embed=embed) + try: + await member.send(f"You were banned by **{context.author}**!\nReason: {reason}") + except disnake.Forbidden: + # Couldn't send a message in the private messages of the user + pass + await member.ban(reason=reason) + except: + embed = disnake.Embed( + title="Error!", + description="An error occurred while trying to ban the user. Make sure my role is above the role of the user you want to ban.", + color=0xE02B2B + ) + await context.send(embed=embed) + + @commands.command( + name="warn", + description="Warns a user in the server.", + ) + @commands.has_permissions(manage_messages=True) + @checks.not_blacklisted() + async def warn(self, context: Context, member: disnake.Member, *, reason: str = "Not specified") -> None: + """ + Warns a user in his private messages. + :param context: The context in which the command has been executed. + :param member: The member that should be warned. + :param reason: The reason for the warn. Default is "Not specified". + """ + embed = disnake.Embed( + title="User Warned!", + description=f"**{member}** was warned by **{context.author}**!", + color=0x9C84EF + ) + embed.add_field( + name="Reason:", + value=reason + ) + await context.send(embed=embed) + try: + await member.send(f"You were warned by **{context.author}**!\nReason: {reason}") + except disnake.Forbidden: + # Couldn't send a message in the private messages of the user + await context.send(f"{member.mention}, you were warned by **{context.author}**!\nReason: {reason}") + + @commands.command( + name="purge", + description="Delete a number of messages.", + ) + @commands.has_guild_permissions(manage_messages=True) + @checks.not_blacklisted() + async def purge(self, context: Context, amount: int) -> None: + """ + Delete a number of messages. + :param context: The context in which the command has been executed. + :param amount: The number of messages that should be deleted. + """ + try: + amount = int(amount) + except: + embed = disnake.Embed( + title="Error!", + description=f"`{amount}` is not a valid number.", + color=0xE02B2B + ) + await context.send(embed=embed) + return + if amount < 1: + embed = disnake.Embed( + title="Error!", + description=f"`{amount}` is not a valid number.", + color=0xE02B2B + ) + await context.send(embed=embed) + return + purged_messages = await context.channel.purge(limit=amount) + embed = disnake.Embed( + title="Chat Cleared!", + description=f"**{context.author}** cleared **{len(purged_messages)}** messages!", + color=0x9C84EF + ) + await context.send(embed=embed) + + @commands.command( + name="hackban", + description="Bans a user without the user having to be in the server." + ) + async def hackban(self, context: Context, user_id: int, *, reason: str) -> None: + """ + Bans a user without the user having to be in the server. + :param context: The context in which the command has been executed. + :param user_id: The ID of the user that should be banned. + :param reason: The reason for the ban. Default is "Not specified". + """ + try: + await self.bot.http.ban(user_id, context.guild.id, reason=reason) + user = await self.bot.get_or_fetch_user(user_id) + embed = disnake.Embed( + title="User Banned!", + description=f"**{user} (ID: {user_id}) ** was banned by **{context.author}**!", + color=0x9C84EF + ) + embed.add_field( + name="Reason:", + value=reason + ) + await context.send(embed=embed) + except: + embed = disnake.Embed( + title="Error!", + description="An error occurred while trying to ban the user. Make sure ID is an existing ID that belongs to a user.", + color=0xE02B2B + ) + await context.send(embed=embed) + + +def setup(bot): + bot.add_cog(Moderation(bot)) diff --git a/cogs/normal/owner-normal.py b/cogs/normal/owner-normal.py new file mode 100644 index 00000000..f6f8b2fe --- /dev/null +++ b/cogs/normal/owner-normal.py @@ -0,0 +1,158 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import sys + +import disnake +from disnake.ext import commands +from disnake.ext.commands import Context + +from helpers import json_manager, checks + +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +class Owner(commands.Cog, name="owner-normal"): + def __init__(self, bot): + self.bot = bot + + @commands.command( + name="shutdown", + description="Make the bot shutdown.", + ) + @checks.is_owner() + async def shutdown(self, context: Context): + """ + Makes the bot shutdown. + """ + embed = disnake.Embed( + description="Shutting down. Bye! :wave:", + color=0x9C84EF + ) + await context.send(embed=embed) + await self.bot.close() + + @commands.command( + name="say", + description="The bot will say anything you want.", + ) + @checks.is_owner() + async def say(self, context: Context, *, message: str): + """ + The bot will say anything you want. + """ + await context.send(message) + + @commands.command( + name="embed", + description="The bot will say anything you want, but within embeds.", + ) + @checks.is_owner() + async def embed(self, context: Context, *, message: str): + """ + The bot will say anything you want, but within embeds. + """ + embed = disnake.Embed( + description=message, + color=0x9C84EF + ) + await context.send(embed=embed) + + @commands.group( + name="blacklist" + ) + async def blacklist(self, context: Context): + """ + Lets you add or remove a user from not being able to use the bot. + """ + if context.invoked_subcommand is None: + with open("blacklist.json") as file: + blacklist = json.load(file) + embed = disnake.Embed( + title=f"There are currently {len(blacklist['ids'])} blacklisted IDs", + description=f"{', '.join(str(id) for id in blacklist['ids'])}", + color=0x9C84EF + ) + await context.send(embed=embed) + + @blacklist.command( + name="add" + ) + async def blacklist_add(self, context: Context, member: disnake.Member = None): + """ + Lets you add a user from not being able to use the bot. + """ + try: + user_id = member.id + with open("blacklist.json") as file: + blacklist = json.load(file) + if user_id in blacklist['ids']: + embed = disnake.Embed( + title="Error!", + description=f"**{member.name}** is already in the blacklist.", + color=0xE02B2B + ) + return await context.send(embed=embed) + json_manager.add_user_to_blacklist(user_id) + embed = disnake.Embed( + title="User Blacklisted", + description=f"**{member.name}** has been successfully added to the blacklist", + color=0x9C84EF + ) + with open("blacklist.json") as file: + blacklist = json.load(file) + embed.set_footer( + text=f"There are now {len(blacklist['ids'])} users in the blacklist" + ) + await context.send(embed=embed) + except: + embed = disnake.Embed( + title="Error!", + description=f"An unknown error occurred when trying to add **{member.name}** to the blacklist.", + color=0xE02B2B + ) + await context.send(embed=embed) + + @blacklist.command( + name="remove" + ) + async def blacklist_remove(self, context, member: disnake.Member = None): + """ + Lets you remove a user from not being able to use the bot. + """ + try: + user_id = member.id + json_manager.remove_user_from_blacklist(user_id) + embed = disnake.Embed( + title="User removed from blacklist", + description=f"**{member.name}** has been successfully removed from the blacklist", + color=0x9C84EF + ) + with open("blacklist.json") as file: + blacklist = json.load(file) + embed.set_footer( + text=f"There are now {len(blacklist['ids'])} users in the blacklist" + ) + await context.send(embed=embed) + except: + embed = disnake.Embed( + title="Error!", + description=f"**{member.name}** is not in the blacklist.", + color=0xE02B2B + ) + await context.send(embed=embed) + + +def setup(bot): + bot.add_cog(Owner(bot)) diff --git a/cogs/normal/template-normal.py b/cogs/normal/template-normal.py new file mode 100644 index 00000000..efee133a --- /dev/null +++ b/cogs/normal/template-normal.py @@ -0,0 +1,50 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import sys + +from disnake.ext import commands +from disnake.ext.commands import Context + +from helpers import checks + +# Only if you want to use variables that are in the config.json file. +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +# Here we name the cog and create a new class for the cog. +class Template(commands.Cog, name="template-normal"): + def __init__(self, bot): + self.bot = bot + + @commands.command( + name="testcommand", + description="This is a testing command that does nothing.", + ) + @checks.not_blacklisted() + @checks.is_owner() + async def testcommand(self, context: Context) -> None: + """ + This is a testing command that does nothing. + :param context: The context in which the command has been executed. + """ + # Do your stuff here + + # Don't forget to remove "pass", that's just because there's no content in the method. + pass + + +# And then we finally add the cog to the bot so that it can load, unload, reload and use it's content. +def setup(bot): + bot.add_cog(Template(bot)) diff --git a/cogs/fun.py b/cogs/slash/fun-slash.py similarity index 70% rename from cogs/fun.py rename to cogs/slash/fun-slash.py index c6249c07..ef85c075 100644 --- a/cogs/fun.py +++ b/cogs/slash/fun-slash.py @@ -3,7 +3,7 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json @@ -15,7 +15,6 @@ import disnake from disnake import ApplicationCommandInteraction from disnake.ext import commands -from disnake.ext.commands import Context from helpers import checks @@ -105,7 +104,7 @@ def __init__(self): self.add_item(RockPaperScissors()) -class Fun(commands.Cog, name="fun"): +class Fun(commands.Cog, name="fun-slash"): def __init__(self, bot): self.bot = bot @@ -114,9 +113,10 @@ def __init__(self, bot): description="Get a random fact." ) @checks.not_blacklisted() - async def randomfact(self, interaction: ApplicationCommandInteraction): + async def randomfact(self, interaction: ApplicationCommandInteraction) -> None: """ Get a random fact. + :param interaction: The application command interaction. """ # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request async with aiohttp.ClientSession() as session: @@ -135,38 +135,16 @@ async def randomfact(self, interaction: ApplicationCommandInteraction): ) await interaction.send(embed=embed) - @commands.command( - name="randomfact", - description="Get a random fact." - ) - @checks.not_blacklisted() - async def randomfact(self, context: Context): - """ - Get a random fact. - """ - # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request - async with aiohttp.ClientSession() as session: - async with session.get("https://uselessfacts.jsph.pl/random.json?language=en") as request: - if request.status == 200: - data = await request.json() - embed = disnake.Embed( - description=data["text"], - color=0xD75BF4 - ) - else: - embed = disnake.Embed( - title="Error!", - description="There is something wrong with the API, please try again later", - color=0xE02B2B - ) - await context.send(embed=embed) - @commands.slash_command( name="coinflip", description="Make a coin flip, but give your bet before." ) @checks.not_blacklisted() - async def coinflip(self, interaction: ApplicationCommandInteraction): + async def coinflip(self, interaction: ApplicationCommandInteraction) -> None: + """ + Make a coin flip, but give your bet before. + :param interaction: The application command interaction. + """ buttons = Choice() embed = disnake.Embed( description="What is your bet?", @@ -188,51 +166,19 @@ async def coinflip(self, interaction: ApplicationCommandInteraction): ) await interaction.edit_original_message(embed=embed, view=None) - @commands.command( - name="coinflip", - description="Make a coin flip, but give your bet before." - ) - @checks.not_blacklisted() - async def coinflip(self, context: Context): - buttons = Choice() - embed = disnake.Embed( - description="What is your bet?", - color=0x9C84EF - ) - message = await context.send(embed=embed, view=buttons) - await buttons.wait() # We wait for the user to click a button. - result = random.choice(["heads", "tails"]) - if buttons.choice == result: - # User guessed correctly - embed = disnake.Embed( - description=f"Correct! You guessed `{buttons.choice}` and I flipped the coin to `{result}`.", - color=0x9C84EF - ) - else: - embed = disnake.Embed( - description=f"Woops! You guessed `{buttons.choice}` and I flipped the coin to `{result}`, better luck next time!", - color=0xE02B2B - ) - await message.edit(embed=embed, view=None) - @commands.slash_command( name="rps", - description="Play the rock paper scissors against the bot." + description="Play the rock paper scissors game against the bot." ) @checks.not_blacklisted() - async def rock_paper_scissors(self, interaction: ApplicationCommandInteraction): + async def rock_paper_scissors(self, interaction: ApplicationCommandInteraction) -> None: + """ + Play the rock paper scissors game against the bot. + :param interaction: The application command interaction. + """ view = RockPaperScissorsView() await interaction.send("Please make your choice", view=view) - @commands.command( - name="rps", - description="Play the rock paper scissors against the bot." - ) - @checks.not_blacklisted() - async def rock_paper_scissors(self, context: Context): - view = RockPaperScissorsView() - await context.send("Please make your choice", view=view) - def setup(bot): bot.add_cog(Fun(bot)) diff --git a/cogs/slash/general-slash.py b/cogs/slash/general-slash.py new file mode 100644 index 00000000..a503ea76 --- /dev/null +++ b/cogs/slash/general-slash.py @@ -0,0 +1,238 @@ +"""" +Copyright © Krypton 2021 - https://github.com/kkrypt0nn (https://krypt0n.co.uk) +Description: +This is a template to create your own discord bot in python. + +Version: 4.1 +""" + +import json +import os +import platform +import random +import sys + +import aiohttp +import disnake +from disnake import ApplicationCommandInteraction, Option, OptionType +from disnake.ext import commands + +from helpers import checks + +if not os.path.isfile("config.json"): + sys.exit("'config.json' not found! Please add it and try again.") +else: + with open("config.json") as file: + config = json.load(file) + + +class General(commands.Cog, name="general-slash"): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command( + name="botinfo", + description="Get some useful (or not) information about the bot.", + ) + @checks.not_blacklisted() + async def botinfo(self, interaction: ApplicationCommandInteraction) -> None: + """ + Get some useful (or not) information about the bot. + :param interaction: The application command interaction. + """ + embed = disnake.Embed( + description="Used [Krypton's](https://krypt0n.co.uk) template", + color=0x9C84EF + ) + embed.set_author( + name="Bot Information" + ) + embed.add_field( + name="Owner:", + value="Krypton#7331", + inline=True + ) + embed.add_field( + name="Python Version:", + value=f"{platform.python_version()}", + inline=True + ) + embed.add_field( + name="Prefix:", + value=f"/ (Slash Commands) or {config['prefix']} for normal commands", + inline=False + ) + embed.set_footer( + text=f"Requested by {interaction.author}" + ) + await interaction.send(embed=embed) + + @commands.slash_command( + name="serverinfo", + description="Get some useful (or not) information about the server.", + ) + @checks.not_blacklisted() + async def serverinfo(self, interaction: ApplicationCommandInteraction) -> None: + """ + Get some useful (or not) information about the server. + :param interaction: The application command interaction. + """ + roles = [role.name for role in interaction.guild.roles] + if len(roles) > 50: + roles = roles[:50] + roles.append(f">>>> Displaying[50/{len(roles)}] Roles") + roles = ", ".join(roles) + + embed = disnake.Embed( + title="**Server Name:**", + description=f"{interaction.guild}", + color=0x9C84EF + ) + embed.set_thumbnail( + url=interaction.guild.icon.url + ) + embed.add_field( + name="Server ID", + value=interaction.guild.id + ) + embed.add_field( + name="Member Count", + value=interaction.guild.member_count + ) + embed.add_field( + name="Text/Voice Channels", + value=f"{len(interaction.guild.channels)}" + ) + embed.add_field( + name=f"Roles ({len(interaction.guild.roles)})", + value=roles + ) + embed.set_footer( + text=f"Created at: {interaction.guild.created_at}" + ) + await interaction.send(embed=embed) + + @commands.slash_command( + name="ping", + description="Check if the bot is alive.", + ) + @checks.not_blacklisted() + async def ping(self, interaction: ApplicationCommandInteraction) -> None: + """ + Check if the bot is alive. + :param interaction: The application command interaction. + """ + embed = disnake.Embed( + title="🏓 Pong!", + description=f"The bot latency is {round(self.bot.latency * 1000)}ms.", + color=0x9C84EF + ) + await interaction.send(embed=embed) + + @commands.slash_command( + name="invite", + description="Get the invite link of the bot to be able to invite it.", + ) + @checks.not_blacklisted() + async def invite(self, interaction: ApplicationCommandInteraction) -> None: + """ + Get the invite link of the bot to be able to invite it. + :param interaction: The application command interaction. + """ + embed = disnake.Embed( + description=f"Invite me by clicking [here](https://discordapp.com/oauth2/authorize?&client_id={config['application_id']}&scope=bot+applications.commands&permissions={config['permissions']}).", + color=0xD75BF4 + ) + try: + # To know what permissions to give to your bot, please see here: https://discordapi.com/permissions.html and remember to not give Administrator permissions. + await interaction.author.send(embed=embed) + await interaction.send("I sent you a private message!") + except disnake.Forbidden: + await interaction.send(embed=embed) + + @commands.slash_command( + name="server", + description="Get the invite link of the discord server of the bot for some support.", + ) + @checks.not_blacklisted() + async def server(self, interaction: ApplicationCommandInteraction) -> None: + """ + Get the invite link of the discord server of the bot for some support. + :param interaction: The application command interaction. + """ + embed = disnake.Embed( + description=f"Join the support server for the bot by clicking [here](https://discord.gg/mTBrXyWxAF).", + color=0xD75BF4 + ) + try: + await interaction.author.send(embed=embed) + await interaction.send("I sent you a private message!") + except disnake.Forbidden: + await interaction.send(embed=embed) + + @commands.slash_command( + name="8ball", + description="Ask any question to the bot.", + options=[ + Option( + name="question", + description="The question you want to ask.", + type=OptionType.string, + required=True + ) + ], + ) + @checks.not_blacklisted() + async def eight_ball(self, interaction: ApplicationCommandInteraction, question: str) -> None: + """ + Ask any question to the bot. + :param interaction: The application command interaction. + :param question: The question that should be asked by the user. + """ + answers = ["It is certain.", "It is decidedly so.", "You may rely on it.", "Without a doubt.", + "Yes - definitely.", "As I see, yes.", "Most likely.", "Outlook good.", "Yes.", + "Signs point to yes.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", + "Cannot predict now.", "Concentrate and ask again later.", "Don't count on it.", "My reply is no.", + "My sources say no.", "Outlook not so good.", "Very doubtful."] + embed = disnake.Embed( + title="**My Answer:**", + description=f"{random.choice(answers)}", + color=0x9C84EF + ) + embed.set_footer( + text=f"The question was: {question}" + ) + await interaction.send(embed=embed) + + @commands.slash_command( + name="bitcoin", + description="Get the current price of bitcoin.", + ) + @checks.not_blacklisted() + async def bitcoin(self, interaction: ApplicationCommandInteraction) -> None: + """ + Get the current price of bitcoin. + :param interaction: The application command interaction. + """ + # This will prevent your bot from stopping everything when doing a web request - see: https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-make-a-web-request + async with aiohttp.ClientSession() as session: + async with session.get("https://api.coindesk.com/v1/bpi/currentprice/BTC.json") as request: + if request.status == 200: + data = await request.json( + content_type="application/javascript") # For some reason the returned content is of type JavaScript + embed = disnake.Embed( + title="Bitcoin price", + description=f"The current price is {data['bpi']['USD']['rate']} :dollar:", + color=0x9C84EF + ) + else: + embed = disnake.Embed( + title="Error!", + description="There is something wrong with the API, please try again later", + color=0xE02B2B + ) + await interaction.send(embed=embed) + + +def setup(bot): + bot.add_cog(General(bot)) diff --git a/cogs/moderation.py b/cogs/slash/moderation-slash.py similarity index 56% rename from cogs/moderation.py rename to cogs/slash/moderation-slash.py index 0b4a8aee..009d6e7c 100644 --- a/cogs/moderation.py +++ b/cogs/slash/moderation-slash.py @@ -3,7 +3,7 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json @@ -13,7 +13,6 @@ import disnake from disnake import ApplicationCommandInteraction, Option, OptionType from disnake.ext import commands -from disnake.ext.commands import Context from helpers import checks @@ -24,12 +23,12 @@ config = json.load(file) -class Moderation(commands.Cog, name="moderation"): +class Moderation(commands.Cog, name="moderation-slash"): def __init__(self, bot): self.bot = bot @commands.slash_command( - name='kick', + name="kick", description="Kick a user out of the server.", options=[ Option( @@ -48,11 +47,15 @@ def __init__(self, bot): ) @commands.has_permissions(kick_members=True) @checks.not_blacklisted() - async def kick(self, interaction: ApplicationCommandInteraction, user: disnake.User, reason: str = "Not specified"): + async def kick(self, interaction: ApplicationCommandInteraction, user: disnake.User, + reason: str = "Not specified") -> None: """ Kick a user out of the server. + :param interaction: The application command interaction. + :param user: The user that should be kicked from the server. + :param reason: The reason for the kick. Default is "Not specified". """ - member = interaction.guild.get_member(user.id) or await interaction.guild.fetch_member(user.id) + member = await interaction.guild.get_or_fetch_member(user.id) if member.guild_permissions.administrator: embed = disnake.Embed( title="Error!", @@ -88,53 +91,8 @@ async def kick(self, interaction: ApplicationCommandInteraction, user: disnake.U ) await interaction.send(embed=embed) - @commands.command( - name='kick', - description="Kick a user out of the server.", - ) - @commands.has_permissions(kick_members=True) - @checks.not_blacklisted() - async def kick(self, context: Context, member: disnake.Member, *, reason: str = "Not specified"): - """ - Kick a user out of the server. - """ - if member.guild_permissions.administrator: - embed = disnake.Embed( - title="Error!", - description="User has Admin permissions.", - color=0xE02B2B - ) - await context.send(embed=embed) - else: - try: - embed = disnake.Embed( - title="User Kicked!", - description=f"**{member}** was kicked by **{context.author}**!", - color=0x9C84EF - ) - embed.add_field( - name="Reason:", - value=reason - ) - await context.send(embed=embed) - try: - await member.send( - f"You were kicked by **{context.author}**!\nReason: {reason}" - ) - except disnake.Forbidden: - # Couldn't send a message in the private messages of the user - pass - await member.kick(reason=reason) - except: - embed = disnake.Embed( - title="Error!", - description="An error occurred while trying to kick the user. Make sure my role is above the role of the user you want to kick.", - color=0xE02B2B - ) - await context.send(embed=embed) - @commands.slash_command( - name='nick', + name="nick", description="Change the nickname of a user on a server.", options=[ Option( @@ -153,11 +111,14 @@ async def kick(self, context: Context, member: disnake.Member, *, reason: str = ) @commands.has_permissions(manage_nicknames=True) @checks.not_blacklisted() - async def nick(self, interaction: ApplicationCommandInteraction, user: disnake.User, nickname: str = None): + async def nick(self, interaction: ApplicationCommandInteraction, user: disnake.User, nickname: str = None) -> None: """ Change the nickname of a user on a server. + :param interaction: The application command interaction. + :param user: The user that should have its nickname changed. + :param nickname: The new nickname of the user. Default is None, which will reset the nickname. """ - member = interaction.guild.get_member(user.id) or await interaction.guild.fetch_member(user.id) + member = await interaction.guild.get_or_fetch_member(user.id) try: await member.edit(nick=nickname) embed = disnake.Embed( @@ -174,34 +135,8 @@ async def nick(self, interaction: ApplicationCommandInteraction, user: disnake.U ) await interaction.send(embed=embed) - @commands.command( - name='nick', - description="Change the nickname of a user on a server.", - ) - @commands.has_permissions(manage_nicknames=True) - @checks.not_blacklisted() - async def nick(self, context: Context, member: disnake.Member, *, nickname: str = None): - """ - Change the nickname of a user on a server. - """ - try: - await member.edit(nick=nickname) - embed = disnake.Embed( - title="Changed Nickname!", - description=f"**{member}'s** new nickname is **{nickname}**!", - color=0x9C84EF - ) - await context.send(embed=embed) - except: - embed = disnake.Embed( - title="Error!", - description="An error occurred while trying to change the nickname of the user. Make sure my role is above the role of the user you want to change the nickname.", - color=0xE02B2B - ) - await context.send(embed=embed) - @commands.slash_command( - name='ban', + name="ban", description="Bans a user from the server.", options=[ Option( @@ -220,11 +155,15 @@ async def nick(self, context: Context, member: disnake.Member, *, nickname: str ) @commands.has_permissions(ban_members=True) @checks.not_blacklisted() - async def ban(self, interaction: ApplicationCommandInteraction, user: disnake.User, reason: str = "Not specified"): + async def ban(self, interaction: ApplicationCommandInteraction, user: disnake.User, + reason: str = "Not specified") -> None: """ Bans a user from the server. + :param interaction: The application command interaction. + :param user: The user that should be banned from the server. + :param reason: The reason for the ban. Default is "Not specified". """ - member = interaction.guild.get_member(user.id) or await interaction.guild.fetch_member(user.id) + member = await interaction.guild.get_or_fetch_member(user.id) try: if member.guild_permissions.administrator: embed = disnake.Embed( @@ -258,51 +197,8 @@ async def ban(self, interaction: ApplicationCommandInteraction, user: disnake.Us ) await interaction.send(embed=embed) - @commands.command( - name='ban', - description="Bans a user from the server.", - ) - @commands.has_permissions(ban_members=True) - @checks.not_blacklisted() - async def ban(self, context: Context, member: disnake.Member, *, reason: str = "Not specified"): - """ - Bans a user from the server. - """ - try: - if member.guild_permissions.administrator: - embed = disnake.Embed( - title="Error!", - description="User has Admin permissions.", - color=0xE02B2B - ) - await context.send(embed=embed) - else: - embed = disnake.Embed( - title="User Banned!", - description=f"**{member}** was banned by **{context.author}**!", - color=0x9C84EF - ) - embed.add_field( - name="Reason:", - value=reason - ) - await context.send(embed=embed) - try: - await member.send(f"You were banned by **{context.author}**!\nReason: {reason}") - except disnake.Forbidden: - # Couldn't send a message in the private messages of the user - pass - await member.ban(reason=reason) - except: - embed = disnake.Embed( - title="Error!", - description="An error occurred while trying to ban the user. Make sure my role is above the role of the user you want to ban.", - color=0xE02B2B - ) - await context.send(embed=embed) - @commands.slash_command( - name='warn', + name="warn", description="Warns a user in the server.", options=[ Option( @@ -321,11 +217,15 @@ async def ban(self, context: Context, member: disnake.Member, *, reason: str = " ) @commands.has_permissions(manage_messages=True) @checks.not_blacklisted() - async def warn(self, interaction: ApplicationCommandInteraction, user: disnake.User, reason: str = "Not specified"): + async def warn(self, interaction: ApplicationCommandInteraction, user: disnake.User, + reason: str = "Not specified") -> None: """ Warns a user in his private messages. + :param interaction: The application command interaction. + :param user: The user that should be warned. + :param reason: The reason for the warn. Default is "Not specified". """ - member = interaction.guild.get_member(user.id) or await interaction.guild.fetch_member(user.id) + member = await interaction.guild.get_or_fetch_member(user.id) embed = disnake.Embed( title="User Warned!", description=f"**{member}** was warned by **{interaction.author}**!", @@ -342,34 +242,8 @@ async def warn(self, interaction: ApplicationCommandInteraction, user: disnake.U # Couldn't send a message in the private messages of the user await interaction.send(f"{member.mention}, you were warned by **{interaction.author}**!\nReason: {reason}") - @commands.command( - name='warn', - description="Warns a user in the server.", - ) - @commands.has_permissions(manage_messages=True) - @checks.not_blacklisted() - async def warn(self, context: Context, member: disnake.Member, *, reason: str = "Not specified"): - """ - Warns a user in his private messages. - """ - embed = disnake.Embed( - title="User Warned!", - description=f"**{member}** was warned by **{context.author}**!", - color=0x9C84EF - ) - embed.add_field( - name="Reason:", - value=reason - ) - await context.send(embed=embed) - try: - await member.send(f"You were warned by **{context.author}**!\nReason: {reason}") - except disnake.Forbidden: - # Couldn't send a message in the private messages of the user - await context.send(f"{member.mention}, you were warned by **{context.author}**!\nReason: {reason}") - @commands.slash_command( - name='purge', + name="purge", description="Delete a number of messages.", options=[ Option( @@ -384,9 +258,12 @@ async def warn(self, context: Context, member: disnake.Member, *, reason: str = ) @commands.has_guild_permissions(manage_messages=True) @checks.not_blacklisted() - async def purge(self, interaction: ApplicationCommandInteraction, amount: int): + async def purge(self, interaction: ApplicationCommandInteraction, amount: int) -> None: """ Delete a number of messages. + + :param interaction: The application command interaction. + :param amount: The number of messages that should be deleted. """ purged_messages = await interaction.channel.purge(limit=amount) embed = disnake.Embed( @@ -396,39 +273,56 @@ async def purge(self, interaction: ApplicationCommandInteraction, amount: int): ) await interaction.send(embed=embed) - @commands.command( - name='purge', - description="Delete a number of messages.", + @commands.slash_command( + name="hackban", + description="Bans a user without the user having to be in the server.", + options=[ + Option( + name="user_id", + description="The ID of the user that should be banned.", + type=OptionType.string, + required=True + ), + Option( + name="reason", + description="The reason you banned the user.", + type=OptionType.string, + required=False + ) + ], + guild_ids=[911613911315382332] ) - @commands.has_guild_permissions(manage_messages=True) + @commands.has_permissions(ban_members=True) @checks.not_blacklisted() - async def purge(self, context: Context, amount: int): + async def hackban(self, interaction: ApplicationCommandInteraction, user_id: str, + reason: str = "Not specified") -> None: """ - Delete a number of messages. + Bans a user without the user having to be in the server. + :param interaction: The application command interaction. + :param user_id: The ID of the user that should be banned. + :param reason: The reason for the ban. Default is "Not specified". """ try: - amount = int(amount) - except: + await self.bot.http.ban(user_id, interaction.guild.id, reason=reason) + user = await self.bot.get_or_fetch_user(int(user_id)) embed = disnake.Embed( - title="Error!", - description=f"`{amount}` is not a valid number.", - color=0xE02B2B + title="User Banned!", + description=f"**{user} (ID: {user_id}) ** was banned by **{interaction.author}**!", + color=0x9C84EF + ) + embed.add_field( + name="Reason:", + value=reason ) - return await context.send(embed=embed) - if amount < 1: + await interaction.send(embed=embed) + except Exception as e: embed = disnake.Embed( title="Error!", - description=f"`{amount}` is not a valid number.", + description="An error occurred while trying to ban the user. Make sure ID is an existing ID that belongs to a user.", color=0xE02B2B ) - return await context.send(embed=embed) - purged_messages = await context.channel.purge(limit=amount) - embed = disnake.Embed( - title="Chat Cleared!", - description=f"**{context.author}** cleared **{len(purged_messages)}** messages!", - color=0x9C84EF - ) - await context.send(embed=embed) + await interaction.send(embed=embed) + print(e) def setup(bot): diff --git a/cogs/owner.py b/cogs/slash/owner-slash.py similarity index 57% rename from cogs/owner.py rename to cogs/slash/owner-slash.py index 7c814f24..380b0892 100644 --- a/cogs/owner.py +++ b/cogs/slash/owner-slash.py @@ -3,7 +3,7 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json @@ -13,7 +13,6 @@ import disnake from disnake import ApplicationCommandInteraction, Option, OptionType from disnake.ext import commands -from disnake.ext.commands import Context from helpers import json_manager, checks @@ -24,7 +23,7 @@ config = json.load(file) -class Owner(commands.Cog, name="owner"): +class Owner(commands.Cog, name="owner-slash"): def __init__(self, bot): self.bot = bot @@ -33,9 +32,10 @@ def __init__(self, bot): description="Make the bot shutdown.", ) @checks.is_owner() - async def shutdown(self, interaction: ApplicationCommandInteraction): + async def shutdown(self, interaction: ApplicationCommandInteraction) -> None: """ Makes the bot shutdown. + :param interaction: The application command interaction. """ embed = disnake.Embed( description="Shutting down. Bye! :wave:", @@ -44,22 +44,6 @@ async def shutdown(self, interaction: ApplicationCommandInteraction): await interaction.send(embed=embed) await self.bot.close() - @commands.command( - name="shutdown", - description="Make the bot shutdown.", - ) - @checks.is_owner() - async def shutdown(self, context: Context): - """ - Makes the bot shutdown. - """ - embed = disnake.Embed( - description="Shutting down. Bye! :wave:", - color=0x9C84EF - ) - await context.send(embed=embed) - await self.bot.close() - @commands.slash_command( name="say", description="The bot will say anything you want.", @@ -73,23 +57,14 @@ async def shutdown(self, context: Context): ], ) @checks.is_owner() - async def say(self, interaction: ApplicationCommandInteraction, message: str): + async def say(self, interaction: ApplicationCommandInteraction, message: str) -> None: """ The bot will say anything you want. + :param interaction: The application command interaction. + :param message: The message that should be repeated by the bot. """ await interaction.send(message) - @commands.command( - name="say", - description="The bot will say anything you want.", - ) - @checks.is_owner() - async def say(self, context: Context, *, message: str): - """ - The bot will say anything you want. - """ - await context.send(message) - @commands.slash_command( name="embed", description="The bot will say anything you want, but within embeds.", @@ -103,9 +78,11 @@ async def say(self, context: Context, *, message: str): ], ) @checks.is_owner() - async def embed(self, interaction: ApplicationCommandInteraction, message: str): + async def embed(self, interaction: ApplicationCommandInteraction, message: str) -> None: """ - The bot will say anything you want, but within embeds. + The bot will say anything you want, but using embeds. + :param interaction: The application command interaction. + :param message: The message that should be repeated by the bot. """ embed = disnake.Embed( description=message, @@ -113,50 +90,18 @@ async def embed(self, interaction: ApplicationCommandInteraction, message: str): ) await interaction.send(embed=embed) - @commands.command( - name="embed", - description="The bot will say anything you want, but within embeds.", - ) - @checks.is_owner() - async def embed(self, context: Context, *, message: str): - """ - The bot will say anything you want, but within embeds. - """ - embed = disnake.Embed( - description=message, - color=0x9C84EF - ) - await context.send(embed=embed) - @commands.slash_command( name="blacklist", description="Get the list of all blacklisted users.", ) @checks.is_owner() - async def blacklist(self, interaction: ApplicationCommandInteraction): + async def blacklist(self, interaction: ApplicationCommandInteraction) -> None: """ Lets you add or remove a user from not being able to use the bot. + :param interaction: The application command interaction. """ pass - @commands.group( - name="blacklist" - ) - async def blacklist_normal(self, - context: Context): # Here we need to rename the function name because of sub commands. - """ - Lets you add or remove a user from not being able to use the bot. - """ - if context.invoked_subcommand is None: - with open("blacklist.json") as file: - blacklist = json.load(file) - embed = disnake.Embed( - title=f"There are currently {len(blacklist['ids'])} blacklisted IDs", - description=f"{', '.join(str(id) for id in blacklist['ids'])}", - color=0x9C84EF - ) - await context.send(embed=embed) - @blacklist.sub_command( base="blacklist", name="add", @@ -171,9 +116,11 @@ async def blacklist_normal(self, ], ) @checks.is_owner() - async def blacklist_add(self, interaction: ApplicationCommandInteraction, user: disnake.User = None): + async def blacklist_add(self, interaction: ApplicationCommandInteraction, user: disnake.User = None) -> None: """ Lets you add a user from not being able to use the bot. + :param interaction: The application command interaction. + :param user: The user that should be added to the blacklist. """ try: user_id = user.id @@ -207,44 +154,6 @@ async def blacklist_add(self, interaction: ApplicationCommandInteraction, user: await interaction.send(embed=embed) print(exception) - @blacklist_normal.command( - name="add" - ) - async def blacklist_add(self, context: Context, member: disnake.Member = None): - """ - Lets you add a user from not being able to use the bot. - """ - try: - user_id = member.id - with open("blacklist.json") as file: - blacklist = json.load(file) - if user_id in blacklist['ids']: - embed = disnake.Embed( - title="Error!", - description=f"**{member.name}** is already in the blacklist.", - color=0xE02B2B - ) - return await context.send(embed=embed) - json_manager.add_user_to_blacklist(user_id) - embed = disnake.Embed( - title="User Blacklisted", - description=f"**{member.name}** has been successfully added to the blacklist", - color=0x9C84EF - ) - with open("blacklist.json") as file: - blacklist = json.load(file) - embed.set_footer( - text=f"There are now {len(blacklist['ids'])} users in the blacklist" - ) - await context.send(embed=embed) - except: - embed = disnake.Embed( - title="Error!", - description=f"An unknown error occurred when trying to add **{member.name}** to the blacklist.", - color=0xE02B2B - ) - await context.send(embed=embed) - @blacklist.sub_command( base="blacklist", name="remove", @@ -262,6 +171,8 @@ async def blacklist_add(self, context: Context, member: disnake.Member = None): async def blacklist_remove(self, interaction: ApplicationCommandInteraction, user: disnake.User = None): """ Lets you remove a user from not being able to use the bot. + :param interaction: The application command interaction. + :param user: The user that should be removed from the blacklist. """ try: json_manager.remove_user_from_blacklist(user.id) @@ -292,35 +203,6 @@ async def blacklist_remove(self, interaction: ApplicationCommandInteraction, use await interaction.send(embed=embed) print(exception) - @blacklist_normal.command( - name="remove" - ) - async def blacklist_remove(self, context, member: disnake.Member = None): - """ - Lets you remove a user from not being able to use the bot. - """ - try: - user_id = member.id - json_manager.remove_user_from_blacklist(user_id) - embed = disnake.Embed( - title="User removed from blacklist", - description=f"**{member.name}** has been successfully removed from the blacklist", - color=0x9C84EF - ) - with open("blacklist.json") as file: - blacklist = json.load(file) - embed.set_footer( - text=f"There are now {len(blacklist['ids'])} users in the blacklist" - ) - await context.send(embed=embed) - except: - embed = disnake.Embed( - title="Error!", - description=f"**{member.name}** is not in the blacklist.", - color=0xE02B2B - ) - await context.send(embed=embed) - def setup(bot): bot.add_cog(Owner(bot)) diff --git a/cogs/template.py b/cogs/slash/template-slash.py similarity index 77% rename from cogs/template.py rename to cogs/slash/template-slash.py index 93385dad..ebf4cf8d 100644 --- a/cogs/template.py +++ b/cogs/slash/template-slash.py @@ -3,7 +3,7 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json @@ -12,7 +12,6 @@ from disnake import ApplicationCommandInteraction from disnake.ext import commands -from disnake.ext.commands import Context from helpers import checks @@ -25,7 +24,7 @@ # Here we name the cog and create a new class for the cog. -class Template(commands.Cog, name="template"): +class Template(commands.Cog, name="template-slash"): def __init__(self, bot): self.bot = bot @@ -42,25 +41,13 @@ async def testcommand(self, interaction: ApplicationCommandInteraction): """ This is a testing command that does nothing. Note: This is a SLASH command + :param interaction: The application command interaction. """ # Do your stuff here # Don't forget to remove "pass", that's just because there's no content in the method. pass - @commands.command( - name="testcommand", - description="This is a testing command that does nothing.", - ) - @checks.not_blacklisted() - @checks.is_owner() - async def testcommand(self, context: Context): - """ - This is a testing command that does nothing. - Note: This is a SLASH command - """ - pass - # And then we finally add the cog to the bot so that it can load, unload, reload and use it's content. def setup(bot): diff --git a/exceptions/__init__.py b/exceptions/__init__.py index e3e6ebf2..ed281450 100644 --- a/exceptions/__init__.py +++ b/exceptions/__init__.py @@ -3,17 +3,25 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ class UserBlacklisted(Exception): + """ + Thrown when a user is attempting something, but is blacklisted. + """ + def __init__(self, message="User is blacklisted!"): self.message = message super().__init__(self.message) class UserNotOwner(Exception): + """ + Thrown when a user is attempting something, but is not an owner of the bot. + """ + def __init__(self, message="User is not an owner of the bot!"): self.message = message super().__init__(self.message) diff --git a/helpers/checks.py b/helpers/checks.py index 576fa217..75e5e10f 100644 --- a/helpers/checks.py +++ b/helpers/checks.py @@ -3,17 +3,24 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json +from typing import TypeVar, Callable from disnake.ext import commands from exceptions import * +T = TypeVar("T") + + +def is_owner() -> Callable[[T], T]: + """ + This is a custom check to see if the user executing the command is an owner of the bot. + """ -def is_owner(): async def predicate(context: commands.Context) -> bool: with open("config.json") as file: data = json.load(file) @@ -24,7 +31,11 @@ async def predicate(context: commands.Context) -> bool: return commands.check(predicate) -def not_blacklisted(): +def not_blacklisted() -> Callable[[T], T]: + """ + This is a custom check to see if the user executing the command is blacklisted. + """ + async def predicate(context: commands.Context) -> bool: with open("blacklist.json") as file: data = json.load(file) diff --git a/helpers/json_manager.py b/helpers/json_manager.py index 26eb8ad5..9dc0f676 100644 --- a/helpers/json_manager.py +++ b/helpers/json_manager.py @@ -3,13 +3,17 @@ Description: This is a template to create your own discord bot in python. -Version: 4.0.1 +Version: 4.1 """ import json -def add_user_to_blacklist(user_id: int): +def add_user_to_blacklist(user_id: int) -> None: + """ + This function will add a user based on its ID in the blacklist.json file. + :param user_id: The ID of the user that should be added into the blacklist.json file. + """ with open("blacklist.json", "r+") as file: file_data = json.load(file) file_data["ids"].append(user_id) @@ -18,7 +22,11 @@ def add_user_to_blacklist(user_id: int): json.dump(file_data, file, indent=4) -def remove_user_from_blacklist(user_id: int): +def remove_user_from_blacklist(user_id: int) -> None: + """ + This function will remove a user based on its ID from the blacklist.json file. + :param user_id: The ID of the user that should be removed from the blacklist.json file. + """ with open("blacklist.json", "r") as file: file_data = json.load(file) file_data["ids"].remove(user_id)