Skip to content

Commit 53a7493

Browse files
committed
feat(poetry.toml): add poetry.toml file to create virtual environments within the project directory
refactor(dev.py): rearrange error handling methods for better readability and maintainability refactor(avatar.py): extract common logic into send_avatar method to reduce code duplication refactor(remindme.py): simplify database access by directly using the reminder table feat(case.py, note.py, reminder.py): add ensure_guild_exists method to check and create guild if not exists refactor(case.py, note.py, reminder.py): call ensure_guild_exists before creating a new entry to ensure guild exists feat(snippet.py): add ensure_guild_exists method to check and create guild if not exists refactor(snippet.py): modify create_snippet method to call ensure_guild_exists before creating a snippet, ensuring guild existence
1 parent fe57c02 commit 53a7493

File tree

8 files changed

+107
-53
lines changed

8 files changed

+107
-53
lines changed

poetry.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[virtualenvs]
2+
in-project = true

tux/cogs/admin/dev.py

+32-33
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ async def sync_tree(self, ctx: commands.Context[commands.Bot], guild: discord.Gu
5050
await self.bot.tree.sync(guild=ctx.guild)
5151
await ctx.reply("Application command tree synced.")
5252

53+
@sync_tree.error
54+
async def sync_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
55+
if isinstance(error, commands.MissingRequiredArgument):
56+
await ctx.send(f"Please specify a guild to sync application commands to. {error}")
57+
else:
58+
logger.error(f"Error syncing application commands: {error}")
59+
5360
@commands.has_guild_permissions(administrator=True)
5461
@dev.command(
5562
name="clear_tree",
@@ -131,6 +138,22 @@ async def load_cog(self, ctx: commands.Context[commands.Bot], *, cog: str) -> No
131138
await ctx.send(f"Cog {cog} loaded.")
132139
logger.info(f"Cog {cog} loaded.")
133140

141+
@load_cog.error
142+
async def load_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
143+
if isinstance(error, commands.MissingRequiredArgument):
144+
await ctx.send(f"Please specify an cog to load. {error}")
145+
elif isinstance(error, commands.ExtensionAlreadyLoaded):
146+
await ctx.send(f"The specified cog is already loaded. {error}")
147+
elif isinstance(error, commands.ExtensionNotFound):
148+
await ctx.send(f"The specified cog is not found. {error}")
149+
elif isinstance(error, commands.ExtensionFailed):
150+
await ctx.send(f"Failed to load cog: {error}")
151+
elif isinstance(error, commands.NoEntryPointError):
152+
await ctx.send(f"The specified cog does not have a setup function. {error}")
153+
else:
154+
await ctx.send(f"Failed to load cog: {error}")
155+
logger.error(f"Failed to load cog: {error}")
156+
134157
@commands.has_guild_permissions(administrator=True)
135158
@dev.command(
136159
name="unload_cog",
@@ -169,6 +192,15 @@ async def unload_cog(self, ctx: commands.Context[commands.Bot], *, cog: str) ->
169192
logger.info(f"Cog {cog} unloaded.")
170193
await ctx.send(f"Cog {cog} unloaded.")
171194

195+
@unload_cog.error
196+
async def unload_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
197+
if isinstance(error, commands.MissingRequiredArgument):
198+
await ctx.send(f"Please specify an extension to unload. {error}")
199+
elif isinstance(error, commands.ExtensionNotLoaded):
200+
await ctx.send(f"That cog is not loaded. {error}")
201+
else:
202+
logger.error(f"Error unloading cog: {error}")
203+
172204
@commands.has_guild_permissions(administrator=True)
173205
@dev.command(
174206
name="reload_cog",
@@ -208,14 +240,6 @@ async def reload_cog(self, ctx: commands.Context[commands.Bot], *, cog: str) ->
208240
await ctx.send(f"Cog {cog} reloaded.")
209241
logger.info(f"Cog {cog} reloaded.")
210242

211-
@sync_tree.error
212-
async def sync_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
213-
if isinstance(error, commands.MissingRequiredArgument):
214-
await ctx.send(f"Please specify a guild to sync application commands to. {error}")
215-
216-
else:
217-
logger.error(f"Error syncing application commands: {error}")
218-
219243
@reload_cog.error
220244
async def reload_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
221245
if isinstance(error, commands.MissingRequiredArgument):
@@ -226,31 +250,6 @@ async def reload_error(self, ctx: commands.Context[commands.Bot], error: Excepti
226250
await ctx.send(f"Error reloading cog: {error}")
227251
logger.error(f"Error reloading cog: {error}")
228252

229-
@unload_cog.error
230-
async def unload_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
231-
if isinstance(error, commands.MissingRequiredArgument):
232-
await ctx.send(f"Please specify an extension to unload. {error}")
233-
elif isinstance(error, commands.ExtensionNotLoaded):
234-
await ctx.send(f"That cog is not loaded. {error}")
235-
else:
236-
logger.error(f"Error unloading cog: {error}")
237-
238-
@load_cog.error
239-
async def load_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
240-
if isinstance(error, commands.MissingRequiredArgument):
241-
await ctx.send(f"Please specify an cog to load. {error}")
242-
elif isinstance(error, commands.ExtensionAlreadyLoaded):
243-
await ctx.send(f"The specified cog is already loaded. {error}")
244-
elif isinstance(error, commands.ExtensionNotFound):
245-
await ctx.send(f"The specified cog is not found. {error}")
246-
elif isinstance(error, commands.ExtensionFailed):
247-
await ctx.send(f"Failed to load cog: {error}")
248-
elif isinstance(error, commands.NoEntryPointError):
249-
await ctx.send(f"The specified cog does not have a setup function. {error}")
250-
else:
251-
await ctx.send(f"Failed to load cog: {error}")
252-
logger.error(f"Failed to load cog: {error}")
253-
254253

255254
async def setup(bot: commands.Bot) -> None:
256255
await bot.add_cog(Dev(bot))

tux/cogs/utility/avatar.py

+28-11
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,7 @@ async def prefix_avatar(
2828
member : discord.Member
2929
The member to get the avatar of.
3030
"""
31-
guild_avatar = member.guild_avatar.url if member.guild_avatar else None
32-
profile_avatar = member.avatar.url if member.avatar else None
33-
34-
files = [await self.create_avatar_file(avatar) for avatar in [guild_avatar, profile_avatar] if avatar]
35-
36-
if files:
37-
await ctx.reply(files=files)
38-
else:
39-
await ctx.reply("Member has no avatar.")
31+
await self.send_avatar(ctx, member)
4032

4133
@app_commands.command(name="avatar", description="Get the avatar of a member.")
4234
@app_commands.describe(member="The member to get the avatar of.")
@@ -51,15 +43,40 @@ async def slash_avatar(self, interaction: discord.Interaction, member: discord.M
5143
member : discord.Member
5244
The member to get the avatar of.
5345
"""
46+
await self.send_avatar(interaction, member)
47+
48+
async def send_avatar(
49+
self,
50+
source: commands.Context[commands.Bot] | discord.Interaction,
51+
member: discord.Member,
52+
) -> None:
53+
"""
54+
Send the avatar of a member.
55+
56+
Parameters
57+
----------
58+
source : commands.Context[commands.Bot] | discord.Interaction
59+
The source object for sending the message.
60+
member : discord.Member
61+
The member to get the avatar of.
62+
"""
63+
5464
guild_avatar = member.guild_avatar.url if member.guild_avatar else None
5565
profile_avatar = member.avatar.url if member.avatar else None
5666

5767
files = [await self.create_avatar_file(avatar) for avatar in [guild_avatar, profile_avatar] if avatar]
5868

5969
if files:
60-
await interaction.response.send_message(files=files)
70+
if isinstance(source, discord.Interaction):
71+
await source.response.send_message(files=files)
72+
else:
73+
await source.reply(files=files)
6174
else:
62-
await interaction.response.send_message(content="Member has no avatar.")
75+
message = "Member has no avatar."
76+
if isinstance(source, discord.Interaction):
77+
await source.response.send_message(content=message)
78+
else:
79+
await source.reply(content=message)
6380

6481
@staticmethod
6582
async def create_avatar_file(url: str) -> discord.File:

tux/cogs/utility/remindme.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def get_closest_reminder(reminders: list[Reminder]) -> Reminder | None:
3434
class RemindMe(commands.Cog):
3535
def __init__(self, bot: commands.Bot) -> None:
3636
self.bot = bot
37-
self.db_controller = DatabaseController()
37+
self.db = DatabaseController().reminder
3838
self.bot.loop.create_task(self.update())
3939

4040
async def send_reminders(self, reminder: Reminder) -> None:
@@ -88,7 +88,7 @@ async def send_reminders(self, reminder: Reminder) -> None:
8888
logger.error(f"Failed to send reminder to {reminder.reminder_user_id}, user not found.")
8989

9090
# Delete the reminder after sending
91-
await self.db_controller.reminders.delete_reminder_by_id(reminder.reminder_id)
91+
await self.db.delete_reminder_by_id(reminder.reminder_id)
9292

9393
# wait for a second so that the reminder is deleted before checking for more reminders
9494
# who knows if this works, it seems to
@@ -120,7 +120,7 @@ async def update(self) -> None:
120120

121121
try:
122122
# Get all reminders
123-
reminders = await self.db_controller.reminders.get_all_reminders()
123+
reminders = await self.db.get_all_reminders()
124124
# Get the closest reminder
125125
closest_reminder = get_closest_reminder(reminders)
126126

@@ -170,7 +170,7 @@ async def remindme(self, interaction: discord.Interaction, time: str, *, reminde
170170
seconds = datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=seconds)
171171

172172
try:
173-
await self.db_controller.reminders.insert_reminder(
173+
await self.db.insert_reminder(
174174
reminder_user_id=interaction.user.id,
175175
reminder_content=reminder,
176176
reminder_expires_at=seconds,

tux/database/controllers/case.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
from datetime import datetime
22

33
from prisma.enums import CaseType
4-
from prisma.models import Case
4+
from prisma.models import Case, Guild
55
from tux.database.client import db
66

77

88
class CaseController:
99
def __init__(self):
1010
self.table = db.case
11+
self.guild_table = db.guild
12+
13+
async def ensure_guild_exists(self, guild_id: int) -> Guild | None:
14+
guild = await self.guild_table.find_first(where={"guild_id": guild_id})
15+
if guild is None:
16+
return await self.guild_table.create(data={"guild_id": guild_id})
17+
return guild
1118

1219
async def get_all_cases(self) -> list[Case]:
1320
return await self.table.find_many()
@@ -24,13 +31,15 @@ async def insert_case(
2431
case_reason: str,
2532
case_expires_at: datetime | None = None,
2633
) -> Case | None:
34+
await self.ensure_guild_exists(guild_id)
35+
2736
return await self.table.create(
2837
data={
2938
"guild_id": guild_id,
3039
"case_target_id": case_target_id,
3140
"case_moderator_id": case_moderator_id,
32-
"case_reason": case_reason,
3341
"case_type": case_type,
42+
"case_reason": case_reason,
3443
"case_expires_at": case_expires_at,
3544
},
3645
)

tux/database/controllers/note.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
from prisma.models import Note
1+
from prisma.models import Guild, Note
22
from tux.database.client import db
33

44

55
class NoteController:
66
def __init__(self):
77
self.table = db.note
8+
self.guild_table = db.guild
9+
10+
async def ensure_guild_exists(self, guild_id: int) -> Guild | None:
11+
guild = await self.guild_table.find_first(where={"guild_id": guild_id})
12+
if guild is None:
13+
return await self.guild_table.create(data={"guild_id": guild_id})
14+
return guild
815

916
async def get_all_notes(self) -> list[Note]:
1017
return await self.table.find_many()
@@ -19,6 +26,8 @@ async def insert_note(
1926
note_content: str,
2027
guild_id: int,
2128
) -> Note:
29+
await self.ensure_guild_exists(guild_id)
30+
2231
return await self.table.create(
2332
data={
2433
"note_target_id": note_target_id,

tux/database/controllers/reminder.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
from datetime import datetime
22

3-
from prisma.models import Reminder
3+
from prisma.models import Guild, Reminder
44
from tux.database.client import db
55

66

77
class ReminderController:
88
def __init__(self) -> None:
99
self.table = db.reminder
10+
self.guild_table = db.guild
11+
12+
async def ensure_guild_exists(self, guild_id: int) -> Guild | None:
13+
guild = await self.guild_table.find_first(where={"guild_id": guild_id})
14+
if guild is None:
15+
return await self.guild_table.create(data={"guild_id": guild_id})
16+
return guild
1017

1118
async def get_all_reminders(self) -> list[Reminder]:
1219
return await self.table.find_many()
@@ -22,6 +29,8 @@ async def insert_reminder(
2229
reminder_channel_id: int,
2330
guild_id: int,
2431
) -> Reminder:
32+
await self.ensure_guild_exists(guild_id)
33+
2534
return await self.table.create(
2635
data={
2736
"reminder_user_id": reminder_user_id,

tux/database/controllers/snippet.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import datetime
22

3-
from prisma.models import Snippet
3+
from prisma.models import Guild, Snippet
44
from tux.database.client import db
55

66

77
class SnippetController:
88
def __init__(self) -> None:
99
self.table = db.snippet
10+
self.guild_table = db.guild
11+
12+
async def ensure_guild_exists(self, guild_id: int) -> Guild | None:
13+
guild = await self.guild_table.find_first(where={"guild_id": guild_id})
14+
if guild is None:
15+
return await self.guild_table.create(data={"guild_id": guild_id})
16+
return guild
1017

1118
async def get_all_snippets(self) -> list[Snippet]:
1219
return await self.table.find_many()
@@ -36,6 +43,8 @@ async def create_snippet(
3643
snippet_user_id: int,
3744
guild_id: int,
3845
) -> Snippet:
46+
await self.ensure_guild_exists(guild_id)
47+
3948
return await self.table.create(
4049
data={
4150
"snippet_name": snippet_name,

0 commit comments

Comments
 (0)