Skip to content

Commit fae1ba0

Browse files
authored
Merge pull request #63 from kkrypt0nn/v2-migration
Migration to discord.py 2.0
2 parents 3db1791 + f07af1a commit fae1ba0

22 files changed

+539
-1433
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,8 @@ dmypy.json
137137
# Cython debug symbols
138138
cython_debug/
139139

140-
.idea/*
140+
# PyCharm IDEA
141+
.idea/*
142+
143+
# SQLITE database
144+
*.db

README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ I would've been happy if there were any template existing. However, there wasn't
1717
decided to create my own template to let <b>you</b> guys create your discord bot easily.
1818

1919
Please note that this template is not supposed to be the best template, but a good template to start learning how
20-
discord.py works and to make your own bot easily. After the version 4.0 the template is using disnake, as discord.py has
21-
stoppped development.
20+
discord.py works and to make your own bot easily.
2221

2322
If you plan to use this template to make your own template or bot, you **have to**:
2423

@@ -40,22 +39,21 @@ All the updates of the template are available [here](UPDATES.md).
4039

4140
## Disclaimer
4241

43-
Slash commands can take **some hours** to get registered on guilds, so if you want to test a command you should use
44-
the `guild_ids` parameter in the command decorator so that it gets registered instantly. Example:
42+
Slash commands can take some time to get registered globally, so if you want to test a command you should use
43+
the `@app_commands.guilds()` decorator so that it gets registered instantly. Example:
4544

4645
```py
47-
@commands.slash_command(
48-
name="command",
49-
description="Command description",
50-
guild_ids=[GUILD_ID1, GUILD_ID2] # These should be testing guild(s) ID, as always: an integer.
46+
@commands.hybrid_command(
47+
name="command",
48+
description="Command description",
5149
)
50+
@app_commands.guilds(GUILD_ID) # Place your guild ID here
5251
```
5352

5453
When using the template you confirm that you have read the [license](LICENSE.md) and comprehend that I can take down
5554
your repository if you do not meet these requirements.
5655

57-
Please do not open issues or pull requests about things that are written in the [TODO file](TODO.md), they are **
58-
already** under work for a future version of the template.
56+
Please do not open issues or pull requests about things that are written in the [TODO file](TODO.md), they are **already** under work for a future version of the template.
5957

6058
## How to download it
6159

@@ -90,7 +88,6 @@ Here is an explanation of what everything is:
9088
| YOUR_APPLICATION_ID_HERE | The application ID of your bot |
9189
| OWNERS | The user ID of all the bot owners |
9290

93-
In the [blacklist](blacklist.json) file you now can add IDs (as integers) in the `ids` list.
9491

9592
## How to start
9693

UPDATES.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22

33
Here is the list of all the updates that I made on this template.
44

5+
### Version 5.0 (20 August 2022)
6+
7+
> ⚠️ **Moved to discord.py 2.0 as it is now officially released**
8+
9+
* Added `warnings` command that will show you all the warnings a user has
10+
* Moved the blacklist to `sqlite3` database
11+
* Now using **Hybrid Commands**, both prefix and slash commands will get created
12+
* When using the `warn` command, the warning will also be added in a new `sqlite3` database
13+
514
### Version 4.1.1 (18 July 2022)
615

7-
* Fixed the custom checks not being sent in the channels correctly.
16+
* Fixed the custom checks not being sent in the channels correctly
817

918
### Version 4.1 (09 January 2022)
1019

blacklist.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

bot.py

Lines changed: 61 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33
Description:
44
This is a template to create your own discord bot in python.
55
6-
Version: 4.1.1
6+
Version: 5.0
77
"""
88

9+
import asyncio
910
import json
1011
import os
1112
import platform
1213
import random
14+
import sqlite3
1315
import sys
1416

15-
import disnake
16-
from disnake import ApplicationCommandInteraction
17-
from disnake.ext import tasks, commands
18-
from disnake.ext.commands import Bot
19-
from disnake.ext.commands import Context
17+
from contextlib import closing
18+
19+
import discord
20+
from discord import Interaction
21+
from discord.ext import tasks, commands
22+
from discord.ext.commands import Bot
23+
from discord.ext.commands import Context
2024

2125
import exceptions
2226

@@ -29,8 +33,8 @@
2933
"""
3034
Setup bot intents (events restrictions)
3135
For more information about intents, please go to the following websites:
32-
https://docs.disnake.dev/en/latest/intents.html
33-
https://docs.disnake.dev/en/latest/intents.html#privileged-intents
36+
https://discordpy.readthedocs.io/en/latest/intents.html
37+
https://discordpy.readthedocs.io/en/latest/intents.html#privileged-intents
3438
3539
3640
Default Intents:
@@ -59,7 +63,7 @@
5963
intents.presences = True
6064
"""
6165

62-
intents = disnake.Intents.default()
66+
intents = discord.Intents.default()
6367

6468
"""
6569
Uncomment this if you don't want to use prefix (normal) commands.
@@ -71,6 +75,18 @@
7175

7276
bot = Bot(command_prefix=commands.when_mentioned_or(config["prefix"]), intents=intents, help_command=None)
7377

78+
79+
def init_db():
80+
with closing(connect_db()) as db:
81+
with open("database/schema.sql", "r") as f:
82+
db.cursor().executescript(f.read())
83+
db.commit()
84+
85+
86+
def connect_db():
87+
return sqlite3.connect("database/database.db")
88+
89+
7490
"""
7591
Create a bot variable to access the config file in cogs so that you don't need to import it every time.
7692
@@ -79,19 +95,20 @@
7995
- self.bot.config # In cogs
8096
"""
8197
bot.config = config
82-
98+
bot.db = connect_db()
8399

84100
@bot.event
85101
async def on_ready() -> None:
86102
"""
87103
The code in this even is executed when the bot is ready
88104
"""
89105
print(f"Logged in as {bot.user.name}")
90-
print(f"disnake API version: {disnake.__version__}")
106+
print(f"discord.py API version: {discord.__version__}")
91107
print(f"Python version: {platform.python_version()}")
92108
print(f"Running on: {platform.system()} {platform.release()} ({os.name})")
93109
print("-------------------")
94110
status_task.start()
111+
await bot.tree.sync()
95112

96113

97114
@tasks.loop(minutes=1.0)
@@ -100,35 +117,14 @@ async def status_task() -> None:
100117
Setup the game status task of the bot
101118
"""
102119
statuses = ["with you!", "with Krypton!", "with humans!"]
103-
await bot.change_presence(activity=disnake.Game(random.choice(statuses)))
104-
105-
106-
def load_commands(command_type: str) -> None:
107-
for file in os.listdir(f"./cogs/{command_type}"):
108-
if file.endswith(".py"):
109-
extension = file[:-3]
110-
try:
111-
bot.load_extension(f"cogs.{command_type}.{extension}")
112-
print(f"Loaded extension '{extension}'")
113-
except Exception as e:
114-
exception = f"{type(e).__name__}: {e}"
115-
print(f"Failed to load extension {extension}\n{exception}")
116-
117-
118-
if __name__ == "__main__":
119-
"""
120-
This will automatically load slash commands and normal commands located in their respective folder.
121-
122-
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.
123-
"""
124-
load_commands("slash")
125-
load_commands("normal")
120+
await bot.change_presence(activity=discord.Game(random.choice(statuses)))
126121

127122

128123
@bot.event
129-
async def on_message(message: disnake.Message) -> None:
124+
async def on_message(message: discord.Message) -> None:
130125
"""
131126
The code in this event is executed every time someone sends a message, with or without the prefix
127+
132128
:param message: The message that was sent.
133129
"""
134130
if message.author == bot.user or message.author.bot:
@@ -137,77 +133,18 @@ async def on_message(message: disnake.Message) -> None:
137133

138134

139135
@bot.event
140-
async def on_slash_command(interaction: ApplicationCommandInteraction) -> None:
136+
async def on_app_command_completion(interaction: Interaction) -> None:
141137
"""
142-
The code in this event is executed every time a slash command has been *successfully* executed
143-
:param interaction: The slash command that has been executed.
138+
The code in this event is executed every time an app command has been **successfully** executed
139+
140+
:param interaction: The app command that has been executed.
144141
"""
145142

146143
if interaction.guild is not None:
147144
print(
148-
f"Executed {interaction.data.name} slash command in {interaction.guild.name} (ID: {interaction.guild.id}) by {interaction.author} (ID: {interaction.author.id})")
145+
f"Executed {interaction.data.name} app command in {interaction.guild.name} (ID: {interaction.guild.id}) by {interaction.author} (ID: {interaction.author.id})")
149146
else:
150-
print(f"Executed {interaction.data.name} slash command by {interaction.author} (ID: {interaction.author.id}) in DMs")
151-
152-
153-
@bot.event
154-
async def on_slash_command_error(interaction: ApplicationCommandInteraction, error: Exception) -> None:
155-
"""
156-
The code in this event is executed every time a valid slash command catches an error
157-
158-
'ephemeral=True' will make so that only the user who execute the command can see the message
159-
160-
:param interaction: The slash command that failed executing.
161-
:param error: The error that has been faced.
162-
"""
163-
if isinstance(error, commands.CommandOnCooldown):
164-
minutes, seconds = divmod(error.retry_after, 60)
165-
hours, minutes = divmod(minutes, 60)
166-
hours = hours % 24
167-
embed = disnake.Embed(
168-
title="Hey, please slow down!",
169-
description=f"You can use this command again in {f'{round(hours)} hours' if round(hours) > 0 else ''} {f'{round(minutes)} minutes' if round(minutes) > 0 else ''} {f'{round(seconds)} seconds' if round(seconds) > 0 else ''}.",
170-
color=0xE02B2B
171-
)
172-
return await interaction.send(embed=embed, ephemeral=True)
173-
elif isinstance(error, exceptions.UserBlacklisted):
174-
"""
175-
The code here will only execute if the error is an instance of 'UserBlacklisted', which can occur when using
176-
the @checks.not_blacklisted() check in your command, or you can raise the error by yourself.
177-
"""
178-
embed = disnake.Embed(
179-
title="Error!",
180-
description="You are blacklisted from using the bot.",
181-
color=0xE02B2B
182-
)
183-
return await interaction.send(embed=embed, ephemeral=True)
184-
elif isinstance(error, exceptions.UserNotOwner):
185-
"""
186-
Same as above, just for the @checks.is_owner() check.
187-
"""
188-
embed = disnake.Embed(
189-
title="Error!",
190-
description="You are not the owner of the bot!",
191-
color=0xE02B2B
192-
)
193-
return await interaction.send(embed=embed, ephemeral=True)
194-
elif isinstance(error, commands.MissingPermissions):
195-
embed = disnake.Embed(
196-
title="Error!",
197-
description="You are missing the permission(s) `" + ", ".join(
198-
error.missing_permissions) + "` to execute this command!",
199-
color=0xE02B2B
200-
)
201-
return await interaction.send(embed=embed, ephemeral=True)
202-
elif isinstance(error, commands.MissingRequiredArgument):
203-
embed = disnake.Embed(
204-
title="Error!",
205-
# We need to capitalize because the command arguments have no capital letter in the code.
206-
description=str(error).capitalize(),
207-
color=0xE02B2B
208-
)
209-
await interaction.send(embed=embed, ephemeral=True)
210-
raise error
147+
print(f"Executed {interaction.data.name} app command by {interaction.author} (ID: {interaction.author.id}) in DMs")
211148

212149

213150
@bot.event
@@ -237,7 +174,7 @@ async def on_command_error(context: Context, error) -> None:
237174
minutes, seconds = divmod(error.retry_after, 60)
238175
hours, minutes = divmod(minutes, 60)
239176
hours = hours % 24
240-
embed = disnake.Embed(
177+
embed = discord.Embed(
241178
title="Hey, please slow down!",
242179
description=f"You can use this command again in {f'{round(hours)} hours' if round(hours) > 0 else ''} {f'{round(minutes)} minutes' if round(minutes) > 0 else ''} {f'{round(seconds)} seconds' if round(seconds) > 0 else ''}.",
243180
color=0xE02B2B
@@ -248,7 +185,7 @@ async def on_command_error(context: Context, error) -> None:
248185
The code here will only execute if the error is an instance of 'UserBlacklisted', which can occur when using
249186
the @checks.not_blacklisted() check in your command, or you can raise the error by yourself.
250187
"""
251-
embed = disnake.Embed(
188+
embed = discord.Embed(
252189
title="Error!",
253190
description="You are blacklisted from using the bot.",
254191
color=0xE02B2B
@@ -258,22 +195,22 @@ async def on_command_error(context: Context, error) -> None:
258195
"""
259196
Same as above, just for the @checks.is_owner() check.
260197
"""
261-
embed = disnake.Embed(
198+
embed = discord.Embed(
262199
title="Error!",
263200
description="You are not the owner of the bot!",
264201
color=0xE02B2B
265202
)
266203
await context.send(embed=embed)
267204
elif isinstance(error, commands.MissingPermissions):
268-
embed = disnake.Embed(
205+
embed = discord.Embed(
269206
title="Error!",
270207
description="You are missing the permission(s) `" + ", ".join(
271208
error.missing_permissions) + "` to execute this command!",
272209
color=0xE02B2B
273210
)
274211
await context.send(embed=embed)
275212
elif isinstance(error, commands.MissingRequiredArgument):
276-
embed = disnake.Embed(
213+
embed = discord.Embed(
277214
title="Error!",
278215
# We need to capitalize because the command arguments have no capital letter in the code.
279216
description=str(error).capitalize(),
@@ -283,5 +220,22 @@ async def on_command_error(context: Context, error) -> None:
283220
raise error
284221

285222

286-
# Run the bot with the token
287-
bot.run(config["token"])
223+
async def main() -> None:
224+
"""
225+
The code in this function is executed whenever the bot will start.
226+
"""
227+
for file in os.listdir(f"./cogs"):
228+
if file.endswith(".py"):
229+
extension = file[:-3]
230+
try:
231+
await bot.load_extension(f"cogs.{extension}")
232+
print(f"Loaded extension '{extension}'")
233+
except Exception as e:
234+
exception = f"{type(e).__name__}: {e}"
235+
print(f"Failed to load extension {extension}\n{exception}")
236+
await bot.start(config["token"])
237+
238+
239+
if __name__ == "__main__":
240+
init_db()
241+
asyncio.run(main())

0 commit comments

Comments
 (0)