1
1
from __future__ import annotations
2
2
3
- from typing import TYPE_CHECKING , Optional
3
+ from typing import TYPE_CHECKING , Annotated , Optional , Union
4
4
5
5
import asyncpg
6
6
import discord
7
7
import msgspec
8
8
from async_lru import alru_cache
9
+ from discord import app_commands
9
10
from discord .ext import commands
10
11
from libs .utils import GuildContext
11
12
from libs .utils .checks import bot_check_permissions , check_permissions
13
+ from libs .utils .embeds import Embed
14
+ from libs .utils .prefix import get_prefix
12
15
13
16
if TYPE_CHECKING :
14
17
from rodhaj import Rodhaj
@@ -96,6 +99,16 @@ class SetupFlags(commands.FlagConverter):
96
99
)
97
100
98
101
102
+ class PrefixConverter (commands .Converter ):
103
+ async def convert (self , ctx : commands .Context , argument : str ):
104
+ user_id = ctx .bot .user .id
105
+ if argument .startswith ((f"<@{ user_id } >" , f"<@!{ user_id } >" )):
106
+ raise commands .BadArgument ("That is a reserved prefix already in use." )
107
+ if len (argument ) > 100 :
108
+ raise commands .BadArgument ("That prefix is too long." )
109
+ return argument
110
+
111
+
99
112
class Config (commands .Cog ):
100
113
"""Config and setup commands for Rodhaj"""
101
114
@@ -120,6 +133,12 @@ async def get_guild_config(self, guild_id: int) -> Optional[GuildConfig]:
120
133
config = GuildConfig (bot = self .bot , ** dict (rows ))
121
134
return config
122
135
136
+ def clean_prefixes (self , prefixes : Union [str , list [str ]]) -> str :
137
+ if isinstance (prefixes , str ):
138
+ return f"`{ prefixes } `"
139
+
140
+ return ", " .join (f"`{ prefix } `" for prefix in prefixes [2 :])
141
+
123
142
@check_permissions (manage_guild = True )
124
143
@bot_check_permissions (manage_channels = True , manage_webhooks = True )
125
144
@commands .guild_only ()
@@ -334,6 +353,99 @@ async def delete(self, ctx: GuildContext) -> None:
334
353
else :
335
354
await ctx .send ("Cancelling." )
336
355
356
+ @check_permissions (manage_guild = True )
357
+ @commands .guild_only ()
358
+ @config .group (name = "prefix" , fallback = "info" )
359
+ async def prefix (self , ctx : GuildContext ) -> None :
360
+ """Shows and manages custom prefixes for the guild
361
+
362
+ Passing in no subcommands will effectively show the currently set prefixes.
363
+ """
364
+ prefixes = await get_prefix (self .bot , ctx .message )
365
+ embed = Embed ()
366
+ embed .add_field (
367
+ name = "Prefixes" , value = self .clean_prefixes (prefixes ), inline = False
368
+ )
369
+ embed .add_field (name = "Total" , value = len (prefixes ) - 2 , inline = False )
370
+ embed .set_author (name = ctx .guild .name , icon_url = ctx .guild .icon .url ) # type: ignore
371
+ await ctx .send (embed = embed )
372
+
373
+ @prefix .command (name = "add" )
374
+ @app_commands .describe (prefix = "The new prefix to add" )
375
+ async def prefix_add (
376
+ self , ctx : GuildContext , prefix : Annotated [str , PrefixConverter ]
377
+ ) -> None :
378
+ """Adds an custom prefix"""
379
+ prefixes = await get_prefix (self .bot , ctx .message )
380
+
381
+ # 2 are the mention prefixes, which are always prepended on the list of prefixes
382
+ if isinstance (prefixes , list ) and len (prefixes ) > 12 :
383
+ await ctx .send (
384
+ "You can not have more than 10 custom prefixes for your server"
385
+ )
386
+ return
387
+ elif prefix in prefixes :
388
+ await ctx .send ("The prefix you want to set already exists" )
389
+ return
390
+
391
+ query = """
392
+ UPDATE guild_config
393
+ SET prefix = ARRAY_APPEND(prefix, $1)
394
+ WHERE id = $2;
395
+ """
396
+ await self .pool .execute (query , prefix , ctx .guild .id )
397
+ get_prefix .cache_invalidate (self .bot , ctx .message )
398
+ await ctx .send (f"Added prefix: `{ prefix } `" )
399
+
400
+ @prefix .command (name = "edit" )
401
+ @app_commands .describe (
402
+ old = "The prefix to edit" , new = "A new prefix to replace the old"
403
+ )
404
+ @app_commands .rename (old = "old_prefix" , new = "new_prefix" )
405
+ async def prefix_edit (
406
+ self ,
407
+ ctx : GuildContext ,
408
+ old : Annotated [str , PrefixConverter ],
409
+ new : Annotated [str , PrefixConverter ],
410
+ ) -> None :
411
+ """Edits and replaces a prefix"""
412
+ query = """
413
+ UPDATE guild_config
414
+ SET prefix = ARRAY_REPLACE(prefix, $1, $2)
415
+ WHERE id = $3;
416
+ """
417
+ prefixes = await get_prefix (self .bot , ctx .message )
418
+
419
+ guild_id = ctx .guild .id
420
+ if old in prefixes :
421
+ await self .pool .execute (query , old , new , guild_id )
422
+ get_prefix .cache_invalidate (self .bot , ctx .message )
423
+ await ctx .send (f"Prefix updated to from `{ old } ` to `{ new } `" )
424
+ else :
425
+ await ctx .send ("The prefix is not in the list of prefixes for your server" )
426
+
427
+ @prefix .command (name = "delete" )
428
+ @app_commands .describe (prefix = "The prefix to delete" )
429
+ async def prefix_delete (
430
+ self , ctx : GuildContext , prefix : Annotated [str , PrefixConverter ]
431
+ ) -> None :
432
+ """Deletes a set prefix"""
433
+ query = """
434
+ UPDATE guild_config
435
+ SET prefix = ARRAY_REMOVE(prefix, $1)
436
+ WHERE id=$2;
437
+ """
438
+ msg = f"Do you want to delete the following prefix: { prefix } "
439
+ confirm = await ctx .prompt (msg , timeout = 120.0 , delete_after = True )
440
+ if confirm :
441
+ await self .pool .execute (query , prefix , ctx .guild .id )
442
+ get_prefix .cache_invalidate (self .bot , ctx .message )
443
+ await ctx .send (f"The prefix `{ prefix } ` has been successfully deleted" )
444
+ elif confirm is None :
445
+ await ctx .send ("Confirmation timed out. Cancelled deletion..." )
446
+ else :
447
+ await ctx .send ("Confirmation cancelled. Please try again" )
448
+
337
449
338
450
async def setup (bot : Rodhaj ) -> None :
339
451
await bot .add_cog (Config (bot ))
0 commit comments