5
5
import msgspec
6
6
from async_lru import alru_cache
7
7
from discord .ext import commands
8
- from libs .utils import is_manager
8
+ from libs .utils import RoboContext , is_manager
9
9
10
10
from rodhaj import Rodhaj
11
11
12
+ UNKNOWN_ERROR_MESSAGE = (
13
+ "An unknown error happened. Please contact the dev team for assistance"
14
+ )
15
+
12
16
13
17
# Msgspec Structs are usually extremely fast compared to slotted classes
14
18
class GuildConfig (msgspec .Struct ):
15
19
bot : Rodhaj
16
20
id : int
21
+ category_id : int
17
22
ticket_channel_id : int
18
23
logging_channel_id : int
19
24
logging_broadcast_url : str
20
25
locked : bool = False
21
26
27
+ @property
28
+ def category_channel (self ) -> Optional [discord .CategoryChannel ]:
29
+ guild = self .bot .get_guild (self .id )
30
+ return guild and guild .get_channel (self .category_id ) # type: ignore
31
+
22
32
@property
23
33
def logging_channel (self ) -> Optional [discord .TextChannel ]:
24
34
guild = self .bot .get_guild (self .id )
25
35
return guild and guild .get_channel (self .logging_channel_id ) # type: ignore
26
36
27
37
@property
28
- def ticket_channel (self ) -> Optional [discord .TextChannel ]:
38
+ def ticket_channel (self ) -> Optional [discord .ForumChannel ]:
29
39
guild = self .bot .get_guild (self .id )
30
40
return guild and guild .get_channel (self .ticket_channel_id ) # type: ignore
31
41
@@ -48,7 +58,7 @@ async def get_webhook(self) -> Optional[discord.Webhook]:
48
58
@alru_cache ()
49
59
async def get_config (self ) -> Optional [GuildConfig ]:
50
60
query = """
51
- SELECT id, ticket_channel_id, logging_channel_id, logging_broadcast_url, locked
61
+ SELECT id, category_id, ticket_channel_id, logging_channel_id, logging_broadcast_url, locked
52
62
FROM guild_config
53
63
WHERE id = $1;
54
64
"""
@@ -63,8 +73,8 @@ async def get_config(self) -> Optional[GuildConfig]:
63
73
class SetupFlags (commands .FlagConverter ):
64
74
ticket_name : str = commands .flag (
65
75
name = "ticket_name" ,
66
- default = "modmail " ,
67
- description = "The name of the ticket forum. Defaults to modmail " ,
76
+ default = "tickets " ,
77
+ description = "The name of the ticket forum. Defaults to tickets " ,
68
78
)
69
79
log_name : str = commands .flag (
70
80
name = "log_name" ,
@@ -80,17 +90,29 @@ def __init__(self, bot: Rodhaj) -> None:
80
90
self .bot = bot
81
91
self .pool = self .bot .pool
82
92
93
+ @alru_cache ()
94
+ async def get_guild_config (self , guild_id : int ) -> Optional [GuildConfig ]:
95
+ # Normally using the star is bad practice but...
96
+ # Since I don't want to write out every single column to select,
97
+ # we are going to use the star
98
+ # The guild config roughly maps to it as well
99
+ query = "SELECT * FROM guild_config WHERE guild_id = $1;"
100
+ rows = await self .pool .fetchrow (query , guild_id )
101
+ if rows is None :
102
+ return None
103
+ config = GuildConfig (bot = self .bot , ** dict (rows ))
104
+ return config
105
+
83
106
@is_manager ()
84
107
@commands .guild_only ()
85
108
@commands .hybrid_group (name = "config" )
86
- async def config (self , ctx : commands . Context ) -> None :
109
+ async def config (self , ctx : RoboContext ) -> None :
87
110
"""Commands to configure, setup, or delete Rodhaj"""
88
111
if ctx .invoked_subcommand is None :
89
112
await ctx .send_help (ctx .command )
90
113
91
- # TODO: Make a delete command (just in case but shouldn't really be needed)
92
114
@config .command (name = "setup" )
93
- async def setup (self , ctx : commands . Context , * , flags : SetupFlags ) -> None :
115
+ async def setup (self , ctx : RoboContext , * , flags : SetupFlags ) -> None :
94
116
"""First-time setup for Rodhaj
95
117
96
118
You only need to run this once
@@ -198,19 +220,26 @@ async def setup(self, ctx: commands.Context, *, flags: SetupFlags) -> None:
198
220
available_tags = forum_tags ,
199
221
)
200
222
except discord .Forbidden :
201
- await ctx .send ("Missing permissions to either" )
223
+ await ctx .send (
224
+ "\N{NO ENTRY SIGN} Rodhaj is missing permissions: Manage Channels and Manage Webhooks"
225
+ )
202
226
return
203
227
except discord .HTTPException :
204
- await ctx .send ("Some error happened" )
228
+ await ctx .send (UNKNOWN_ERROR_MESSAGE )
205
229
return
206
230
207
231
query = """
208
- INSERT INTO guild_config (id, ticket_channel_id, logging_channel_id, logging_broadcast_url)
209
- VALUES ($1, $2, $3, $4);
232
+ INSERT INTO guild_config (id, category_id, ticket_channel_id, logging_channel_id, logging_broadcast_url)
233
+ VALUES ($1, $2, $3, $4, $5 );
210
234
"""
211
235
try :
212
236
await self .pool .execute (
213
- query , guild_id , ticket_channel .id , logging_channel .id , lgc_webhook .url
237
+ query ,
238
+ guild_id ,
239
+ rodhaj_category .id ,
240
+ ticket_channel .id ,
241
+ logging_channel .id ,
242
+ lgc_webhook .url ,
214
243
)
215
244
except asyncpg .UniqueViolationError :
216
245
await ticket_channel .delete (reason = delete_reason )
@@ -220,11 +249,65 @@ async def setup(self, ctx: commands.Context, *, flags: SetupFlags) -> None:
220
249
"Failed to create the channels. Please contact Noelle to figure out why (it's more than likely that the channels exist and bypassed checking the lru cache for some reason)"
221
250
)
222
251
else :
223
- # Invalidate LRU cache
252
+ # Invalidate LRU cache just to clear it out
224
253
dispatcher .get_config .cache_invalidate ()
225
254
msg = f"Rodhaj channels successfully created! The ticket channel can be found under { ticket_channel .mention } "
226
255
await ctx .send (msg )
227
256
257
+ @config .command (name = "delete" )
258
+ async def delete (self , ctx : RoboContext ) -> None :
259
+ """Permanently deletes Rodhaj channels and tickets."""
260
+ if ctx .guild is None :
261
+ await ctx .send ("Really... This module is meant to be ran in a server" )
262
+ return
263
+
264
+ guild_id = ctx .guild .id
265
+
266
+ dispatcher = GuildWebhookDispatcher (self .bot , guild_id )
267
+ guild_config = await self .get_guild_config (guild_id )
268
+
269
+ msg = "Are you really sure that you want to delete the Rodhaj channels?"
270
+ confirm = await ctx .prompt (msg , timeout = 300.0 )
271
+ if confirm :
272
+ if guild_config is None :
273
+ msg = (
274
+ "Could not find the guild config. Perhaps Rodhaj is not set up yet?"
275
+ )
276
+ await ctx .send (msg )
277
+ return
278
+
279
+ reason = f"Requested by { ctx .author .name } (ID: { ctx .author .id } ) to purge Rodhaj channels"
280
+
281
+ if (
282
+ guild_config .logging_channel is not None
283
+ and guild_config .ticket_channel is not None
284
+ and guild_config .category_channel is not None
285
+ ):
286
+ try :
287
+ await guild_config .logging_channel .delete (reason = reason )
288
+ await guild_config .ticket_channel .delete (reason = reason )
289
+ await guild_config .category_channel .delete (reason = reason )
290
+ except discord .Forbidden :
291
+ await ctx .send (
292
+ "\N{NO ENTRY SIGN} Rodhaj is missing permissions: Manage Channels"
293
+ )
294
+ return
295
+ except discord .HTTPException :
296
+ await ctx .send (UNKNOWN_ERROR_MESSAGE )
297
+ return
298
+
299
+ query = """
300
+ DELETE FROM guild_config WHERE guild_id = $1;
301
+ """
302
+ await self .pool .execute (query , guild_id )
303
+ dispatcher .get_config .cache_invalidate ()
304
+ self .get_guild_config .cache_invalidate ()
305
+ await ctx .send ("Successfully deleted channels" )
306
+ elif confirm is None :
307
+ await ctx .send ("Not removing Rodhaj channels. Canceling." )
308
+ else :
309
+ await ctx .send ("Cancelling." )
310
+
228
311
229
312
async def setup (bot : Rodhaj ) -> None :
230
313
await bot .add_cog (Config (bot ))
0 commit comments