From 47ca9b5029d94c6b42a960723c25325f4d784da0 Mon Sep 17 00:00:00 2001 From: io Date: Thu, 8 Jul 2021 06:55:00 +0000 Subject: [PATCH] we only need to track RLs for POST DELETE has an RL of 1/2s so no need --- cogs/emote.py | 10 +++----- utils/emote_client.py | 54 +++++++++---------------------------------- 2 files changed, 14 insertions(+), 50 deletions(-) diff --git a/cogs/emote.py b/cogs/emote.py index 732879e..90cea8d 100644 --- a/cogs/emote.py +++ b/cogs/emote.py @@ -330,7 +330,7 @@ class Emotes(commands.Cog): if not url and not context.message.attachments: raise commands.BadArgument('A URL or attachment must be given.') - self.emote_client.check_create(context.guild.id) + self.emote_client.check_rl(context.guild.id) url = url or context.message.attachments[0].url async with context.typing(): @@ -369,7 +369,7 @@ class Emotes(commands.Cog): async def add_safe(self, context, name, url, author_id, *, reason=None): """Try to add an emote. Returns a string that should be sent to the user.""" - self.emote_client.check_create(context.guild.id) + self.emote_client.check_rl(context.guild.id) try: image_data = await self.fetch_safe(url) except errors.InvalidFileError: @@ -454,11 +454,7 @@ class Emotes(commands.Cog): """ if not emotes: emote = await self.parse_emote(context, emote) - await self.emote_client.delete( - guild_id=context.guild.id, - emote_id=emote.id, - reason='Removed by ' + utils.format_user(self.bot, context.author.id), - ) + await emote.delete(reason='Removed by ' + utils.format_user(self.bot, context.author.id)) await context.send(fr'Emote \:{emote.name}: successfully removed.') else: for emote in (emote,) + emotes: diff --git a/utils/emote_client.py b/utils/emote_client.py index 53a463a..27dda62 100644 --- a/utils/emote_client.py +++ b/utils/emote_client.py @@ -15,24 +15,6 @@ import utils.image as image_utils from utils.errors import RateLimitedError from discord import HTTPException, Forbidden, NotFound, DiscordServerError -class GuildRetryTimes: - """Holds the times, for a particular guild, - that we have to wait until for the rate limit for a particular HTTP method to elapse. - """ - __slots__ = frozenset({'POST', 'DELETE'}) - - def __init__(self, POST=None, DELETE=None): - self.POST = POST - self.DELETE = DELETE - - def validate(self): - now = datetime.datetime.now(tz=datetime.timezone.utc).timestamp() - if self.POST and self.POST < now: - self.POST = None - if self.DELETE and self.DELETE < now: - self.DELETE = None - return self.POST or self.DELETE - GuildId = int async def json_or_text(resp): @@ -55,7 +37,7 @@ class EmoteClient: } def __init__(self, bot): - self.guild_rls: Dict[GuildId, GuildRetryTimes] = {} + self.guild_rls: Dict[GuildId, float] = {} self.http = aiohttp.ClientSession(headers={ 'User-Agent': bot.config['user_agent'] + ' ' + bot.http.user_agent, 'Authorization': 'Bot ' + bot.config['tokens']['discord'], @@ -63,7 +45,7 @@ class EmoteClient: }) async def request(self, method, path, guild_id, **kwargs): - self._check_rl(method, guild_id) + self.check_rl(guild_id) headers = {} # Emote Manager shouldn't use walrus op until Debian adopts 3.8 :( @@ -84,33 +66,29 @@ class EmoteClient: error_cls = self.HTTP_ERROR_CLASSES.get(resp.status, HTTPException) raise error_cls(resp, data) - def _check_rl(self, method, guild_id): + # optimization method that lets us check the RL before downloading the user's image. + # also lets us preemptively check the RL before doing a request + def check_rl(self, guild_id): try: - rls = self.guild_rls[guild_id] + retry_at = self.guild_rls[guild_id] except KeyError: return - if not rls.validate(): + now = datetime.datetime.now(tz=datetime.timezone.utc).timestamp() + if retry_at < now: del self.guild_rls[guild_id] return - retry_at = getattr(rls, method, None) - if retry_at: - raise RateLimitedError(retry_at) + raise RateLimitedError(retry_at) async def _handle_rl(self, resp, method, path, guild_id, **kwargs): retry_after = (await resp.json())['retry_after'] / 1000.0 retry_at = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(seconds=retry_after) # cache unconditionally in case request() is called again while we're sleeping - try: - rls = self.guild_rls[guild_id] - except KeyError: - self.guild_rls[guild_id] = rls = GuildRetryTimes() + self.guild_rls[guild_id] = retry_at.timestamp() - setattr(rls, resp.method, retry_at.timestamp()) - - if resp.method not in GuildRetryTimes.__slots__ or retry_after < 10.0: + if retry_after < 10.0: await asyncio.sleep(retry_after) # woo mutual recursion return await self.request(method, path, guild_id, **kwargs) @@ -118,13 +96,6 @@ class EmoteClient: # we've been hit with one of those crazy high rate limits, which only occur for specific methods raise RateLimitedError(retry_at) - # optimization methods that let us check the RLs before downloading the user's image - def check_create(self, guild_id): - self._check_rl('POST', guild_id) - - def check_delete(self, guild_id): - self._check_rl('DELETE', guild_id) - async def create(self, *, guild, name, image: bytes, role_ids=(), reason=None): data = await self.request( 'POST', f'/guilds/{guild.id}/emojis', @@ -134,9 +105,6 @@ class EmoteClient: ) return PartialEmoji(animated=data.get('animated', False), name=data.get('name'), id=data.get('id')) - async def delete(self, *, guild_id, emote_id, reason=None): - return await self.request('DELETE', f'/guilds/{guild_id}/emojis/{emote_id}', guild_id, reason=reason) - async def __aenter__(self): self.http = await self.http.__aenter__() return self