1+ import asyncio
2+ import logging
13from copy import copy
24from re import search
35from string import Formatter
4- from typing import Dict , List
6+ from typing import Dict , List , Literal
57
68import discord
79from redbot .core import Config , commands , checks
1416
1517_ = Translator ("Alias" , __file__ )
1618
19+ log = logging .getLogger ("red.cogs.alias" )
20+
1721
1822class _TrackingFormatter (Formatter ):
1923 def __init__ (self ):
@@ -38,24 +42,107 @@ class Alias(commands.Cog):
3842 and append them to the stored alias.
3943 """
4044
41- default_global_settings : Dict [str , list ] = {"entries" : []}
42-
43- default_guild_settings : Dict [str , list ] = {"entries" : []} # Going to be a list of dicts
44-
4545 def __init__ (self , bot : Red ):
4646 super ().__init__ ()
4747 self .bot = bot
4848 self .config = Config .get_conf (self , 8927348724 )
4949
50- self .config .register_global (** self . default_global_settings )
51- self .config .register_guild (** self . default_guild_settings )
50+ self .config .register_global (entries = [], handled_string_creator = False )
51+ self .config .register_guild (entries = [] )
5252 self ._aliases : AliasCache = AliasCache (config = self .config , cache_enabled = True )
53+ self ._ready_event = asyncio .Event ()
54+
55+ async def red_delete_data_for_user (
56+ self ,
57+ * ,
58+ requester : Literal ["discord_deleted_user" , "owner" , "user" , "user_strict" ],
59+ user_id : int ,
60+ ):
61+ if requester != "discord_deleted_user" :
62+ return
63+
64+ await self ._ready_event .wait ()
65+ await self ._aliases .anonymize_aliases (user_id )
66+
67+ async def cog_before_invoke (self , ctx ):
68+ await self ._ready_event .wait ()
69+
70+ async def _maybe_handle_string_keys (self ):
71+ # This isn't a normal schema migration because it's being added
72+ # after the fact for GH-3788
73+ if await self .config .handled_string_creator ():
74+ return
75+
76+ async with self .config .entries () as alias_list :
77+ bad_aliases = []
78+ for a in alias_list :
79+ for keyname in ("creator" , "guild" ):
80+ if isinstance ((val := a .get (keyname )), str ):
81+ try :
82+ a [keyname ] = int (val )
83+ except ValueError :
84+ # Because migrations weren't created as changes were made,
85+ # and the prior form was a string of an ID,
86+ # if this fails, there's nothing to go back to
87+ bad_aliases .append (a )
88+ break
89+
90+ for a in bad_aliases :
91+ alias_list .remove (a )
92+
93+ # if this was using a custom group of (guild_id, aliasname) it would be better but...
94+ all_guild_aliases = await self .config .all_guilds ()
95+
96+ for guild_id , guild_data in all_guild_aliases .items ():
97+
98+ to_set = []
99+ modified = False
100+
101+ for a in guild_data .get ("entries" , []):
102+
103+ for keyname in ("creator" , "guild" ):
104+ if isinstance ((val := a .get (keyname )), str ):
105+ try :
106+ a [keyname ] = int (val )
107+ except ValueError :
108+ break
109+ finally :
110+ modified = True
111+ else :
112+ to_set .append (a )
113+
114+ if modified :
115+ await self .config .guild_from_id (guild_id ).entries .set (to_set )
116+
117+ await asyncio .sleep (0 )
118+ # control yielded per loop since this is most likely to happen
119+ # at bot startup, where this is most likely to have a performance
120+ # hit.
121+
122+ await self .config .handled_string_creator .set (True )
123+
124+ def sync_init (self ):
125+ t = asyncio .create_task (self ._initialize ())
126+
127+ def done_callback (fut : asyncio .Future ):
128+ try :
129+ t .result ()
130+ except Exception as exc :
131+ log .exception ("Failed to load alias cog" , exc_info = exc )
132+ # Maybe schedule extension unloading with message to owner in future
133+
134+ t .add_done_callback (done_callback )
135+
136+ async def _initialize (self ):
137+ """ Should only ever be a task """
138+
139+ await self ._maybe_handle_string_keys ()
53140
54- async def initialize (self ):
55- # This can be where we set the cache_enabled attribute later
56141 if not self ._aliases ._loaded :
57142 await self ._aliases .load_aliases ()
58143
144+ self ._ready_event .set ()
145+
59146 def is_command (self , alias_name : str ) -> bool :
60147 """
61148 The logic here is that if this returns true, the name should not be used for an alias
@@ -327,6 +414,8 @@ async def _list_global_alias(self, ctx: commands.Context):
327414 @commands .Cog .listener ()
328415 async def on_message_without_command (self , message : discord .Message ):
329416
417+ await self ._ready_event .wait ()
418+
330419 if message .guild is not None :
331420 if await self .bot .cog_disabled_in_guild (self , message .guild ):
332421 return
0 commit comments