mirror of
https://github.com/uhIgnacio/EmoteManager.git
synced 2024-08-15 02:23:13 +00:00
we only need to track RLs for POST
DELETE has an RL of 1/2s so no need
This commit is contained in:
parent
82706d8ea2
commit
47ca9b5029
2 changed files with 14 additions and 50 deletions
|
@ -330,7 +330,7 @@ class Emotes(commands.Cog):
|
||||||
if not url and not context.message.attachments:
|
if not url and not context.message.attachments:
|
||||||
raise commands.BadArgument('A URL or attachment must be given.')
|
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
|
url = url or context.message.attachments[0].url
|
||||||
async with context.typing():
|
async with context.typing():
|
||||||
|
@ -369,7 +369,7 @@ class Emotes(commands.Cog):
|
||||||
|
|
||||||
async def add_safe(self, context, name, url, author_id, *, reason=None):
|
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."""
|
"""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:
|
try:
|
||||||
image_data = await self.fetch_safe(url)
|
image_data = await self.fetch_safe(url)
|
||||||
except errors.InvalidFileError:
|
except errors.InvalidFileError:
|
||||||
|
@ -454,11 +454,7 @@ class Emotes(commands.Cog):
|
||||||
"""
|
"""
|
||||||
if not emotes:
|
if not emotes:
|
||||||
emote = await self.parse_emote(context, emote)
|
emote = await self.parse_emote(context, emote)
|
||||||
await self.emote_client.delete(
|
await emote.delete(reason='Removed by ' + utils.format_user(self.bot, context.author.id))
|
||||||
guild_id=context.guild.id,
|
|
||||||
emote_id=emote.id,
|
|
||||||
reason='Removed by ' + utils.format_user(self.bot, context.author.id),
|
|
||||||
)
|
|
||||||
await context.send(fr'Emote \:{emote.name}: successfully removed.')
|
await context.send(fr'Emote \:{emote.name}: successfully removed.')
|
||||||
else:
|
else:
|
||||||
for emote in (emote,) + emotes:
|
for emote in (emote,) + emotes:
|
||||||
|
|
|
@ -15,24 +15,6 @@ import utils.image as image_utils
|
||||||
from utils.errors import RateLimitedError
|
from utils.errors import RateLimitedError
|
||||||
from discord import HTTPException, Forbidden, NotFound, DiscordServerError
|
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
|
GuildId = int
|
||||||
|
|
||||||
async def json_or_text(resp):
|
async def json_or_text(resp):
|
||||||
|
@ -55,7 +37,7 @@ class EmoteClient:
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.guild_rls: Dict[GuildId, GuildRetryTimes] = {}
|
self.guild_rls: Dict[GuildId, float] = {}
|
||||||
self.http = aiohttp.ClientSession(headers={
|
self.http = aiohttp.ClientSession(headers={
|
||||||
'User-Agent': bot.config['user_agent'] + ' ' + bot.http.user_agent,
|
'User-Agent': bot.config['user_agent'] + ' ' + bot.http.user_agent,
|
||||||
'Authorization': 'Bot ' + bot.config['tokens']['discord'],
|
'Authorization': 'Bot ' + bot.config['tokens']['discord'],
|
||||||
|
@ -63,7 +45,7 @@ class EmoteClient:
|
||||||
})
|
})
|
||||||
|
|
||||||
async def request(self, method, path, guild_id, **kwargs):
|
async def request(self, method, path, guild_id, **kwargs):
|
||||||
self._check_rl(method, guild_id)
|
self.check_rl(guild_id)
|
||||||
|
|
||||||
headers = {}
|
headers = {}
|
||||||
# Emote Manager shouldn't use walrus op until Debian adopts 3.8 :(
|
# 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)
|
error_cls = self.HTTP_ERROR_CLASSES.get(resp.status, HTTPException)
|
||||||
raise error_cls(resp, data)
|
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:
|
try:
|
||||||
rls = self.guild_rls[guild_id]
|
retry_at = self.guild_rls[guild_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not rls.validate():
|
now = datetime.datetime.now(tz=datetime.timezone.utc).timestamp()
|
||||||
|
if retry_at < now:
|
||||||
del self.guild_rls[guild_id]
|
del self.guild_rls[guild_id]
|
||||||
return
|
return
|
||||||
|
|
||||||
retry_at = getattr(rls, method, None)
|
raise RateLimitedError(retry_at)
|
||||||
if retry_at:
|
|
||||||
raise RateLimitedError(retry_at)
|
|
||||||
|
|
||||||
async def _handle_rl(self, resp, method, path, guild_id, **kwargs):
|
async def _handle_rl(self, resp, method, path, guild_id, **kwargs):
|
||||||
retry_after = (await resp.json())['retry_after'] / 1000.0
|
retry_after = (await resp.json())['retry_after'] / 1000.0
|
||||||
retry_at = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(seconds=retry_after)
|
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
|
# cache unconditionally in case request() is called again while we're sleeping
|
||||||
try:
|
self.guild_rls[guild_id] = retry_at.timestamp()
|
||||||
rls = self.guild_rls[guild_id]
|
|
||||||
except KeyError:
|
|
||||||
self.guild_rls[guild_id] = rls = GuildRetryTimes()
|
|
||||||
|
|
||||||
setattr(rls, resp.method, retry_at.timestamp())
|
if retry_after < 10.0:
|
||||||
|
|
||||||
if resp.method not in GuildRetryTimes.__slots__ or retry_after < 10.0:
|
|
||||||
await asyncio.sleep(retry_after)
|
await asyncio.sleep(retry_after)
|
||||||
# woo mutual recursion
|
# woo mutual recursion
|
||||||
return await self.request(method, path, guild_id, **kwargs)
|
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
|
# we've been hit with one of those crazy high rate limits, which only occur for specific methods
|
||||||
raise RateLimitedError(retry_at)
|
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):
|
async def create(self, *, guild, name, image: bytes, role_ids=(), reason=None):
|
||||||
data = await self.request(
|
data = await self.request(
|
||||||
'POST', f'/guilds/{guild.id}/emojis',
|
'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'))
|
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):
|
async def __aenter__(self):
|
||||||
self.http = await self.http.__aenter__()
|
self.http = await self.http.__aenter__()
|
||||||
return self
|
return self
|
||||||
|
|
Loading…
Reference in a new issue