Implement rate limit headers.
This commit is contained in:
parent
6adfd633ea
commit
6d33ea748a
316
discord/http.py
316
discord/http.py
@ -29,8 +29,9 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import inspect
|
|
||||||
import weakref
|
import weakref
|
||||||
|
import datetime
|
||||||
|
from email.utils import parsedate_to_datetime
|
||||||
from random import randint as random_integer
|
from random import randint as random_integer
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -45,9 +46,41 @@ def json_or_text(response):
|
|||||||
return json.loads(text)
|
return json.loads(text)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def _func_():
|
class Route:
|
||||||
# emulate __func__ from C++
|
BASE = 'https://discordapp.com/api/v6'
|
||||||
return inspect.currentframe().f_back.f_code.co_name
|
|
||||||
|
def __init__(self, method, path, **parameters):
|
||||||
|
self.path = path
|
||||||
|
self.method = method
|
||||||
|
url = (self.BASE + self.path)
|
||||||
|
if parameters:
|
||||||
|
self.url = url.format(**parameters)
|
||||||
|
else:
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
# major parameters:
|
||||||
|
self.channel_id = parameters.get('channel_id')
|
||||||
|
self.guild_id = parameters.get('guild_id')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bucket(self):
|
||||||
|
# the bucket is just method + path w/ major parameters
|
||||||
|
return '{0.method}:{0.channel_id}:{0.guild_id}:{0.path}'.format(self)
|
||||||
|
|
||||||
|
class MaybeUnlock:
|
||||||
|
def __init__(self, lock):
|
||||||
|
self.lock = lock
|
||||||
|
self._unlock = True
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def defer(self):
|
||||||
|
self._unlock = False
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
if self._unlock:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
class HTTPClient:
|
class HTTPClient:
|
||||||
"""Represents an HTTP client sending HTTP requests to the Discord API."""
|
"""Represents an HTTP client sending HTTP requests to the Discord API."""
|
||||||
@ -72,6 +105,7 @@ class HTTPClient:
|
|||||||
self.connector = connector
|
self.connector = connector
|
||||||
self.session = aiohttp.ClientSession(connector=connector, loop=self.loop)
|
self.session = aiohttp.ClientSession(connector=connector, loop=self.loop)
|
||||||
self._locks = weakref.WeakValueDictionary()
|
self._locks = weakref.WeakValueDictionary()
|
||||||
|
self._global_lock = asyncio.Lock(loop=self.loop)
|
||||||
self.token = None
|
self.token = None
|
||||||
self.bot_token = False
|
self.bot_token = False
|
||||||
|
|
||||||
@ -79,7 +113,11 @@ class HTTPClient:
|
|||||||
self.user_agent = user_agent.format(__version__, sys.version_info, aiohttp.__version__)
|
self.user_agent = user_agent.format(__version__, sys.version_info, aiohttp.__version__)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def request(self, method, url, *, bucket=None, **kwargs):
|
def request(self, route, *, header_bypass_delay=None, **kwargs):
|
||||||
|
bucket = route.bucket
|
||||||
|
method = route.method
|
||||||
|
url = route.url
|
||||||
|
|
||||||
lock = self._locks.get(bucket)
|
lock = self._locks.get(bucket)
|
||||||
if lock is None:
|
if lock is None:
|
||||||
lock = asyncio.Lock(loop=self.loop)
|
lock = asyncio.Lock(loop=self.loop)
|
||||||
@ -100,7 +138,13 @@ class HTTPClient:
|
|||||||
kwargs['data'] = utils.to_json(kwargs.pop('json'))
|
kwargs['data'] = utils.to_json(kwargs.pop('json'))
|
||||||
|
|
||||||
kwargs['headers'] = headers
|
kwargs['headers'] = headers
|
||||||
with (yield from lock):
|
|
||||||
|
if self._global_lock.locked():
|
||||||
|
# wait until the global lock is complete
|
||||||
|
yield from self._global_lock
|
||||||
|
|
||||||
|
yield from lock
|
||||||
|
with MaybeUnlock(lock) as maybe_lock:
|
||||||
for tries in range(5):
|
for tries in range(5):
|
||||||
r = yield from self.session.request(method, url, **kwargs)
|
r = yield from self.session.request(method, url, **kwargs)
|
||||||
log.debug(self.REQUEST_LOG.format(method=method, url=url, status=r.status, json=kwargs.get('data')))
|
log.debug(self.REQUEST_LOG.format(method=method, url=url, status=r.status, json=kwargs.get('data')))
|
||||||
@ -108,6 +152,22 @@ class HTTPClient:
|
|||||||
# even errors have text involved in them so this is safe to call
|
# even errors have text involved in them so this is safe to call
|
||||||
data = yield from json_or_text(r)
|
data = yield from json_or_text(r)
|
||||||
|
|
||||||
|
# check if we have rate limit header information
|
||||||
|
remaining = r.headers.get('X-Ratelimit-Remaining')
|
||||||
|
if remaining == '0':
|
||||||
|
# we've depleted our current bucket
|
||||||
|
if header_bypass_delay is None:
|
||||||
|
now = parsedate_to_datetime(r.headers['Date'])
|
||||||
|
reset = datetime.datetime.fromtimestamp(int(r.headers['X-Ratelimit-Reset']), datetime.timezone.utc)
|
||||||
|
delta = (reset - now).total_seconds()
|
||||||
|
else:
|
||||||
|
delta = header_bypass_delay
|
||||||
|
|
||||||
|
fmt = 'A rate limit bucket has been exhausted (bucket: {bucket}, retry: {delta}).'
|
||||||
|
log.info(fmt.format(bucket=bucket, delta=delta))
|
||||||
|
maybe_lock.defer()
|
||||||
|
self.loop.call_later(delta, lock.release)
|
||||||
|
|
||||||
# the request was successful so just return the text/json
|
# the request was successful so just return the text/json
|
||||||
if 300 > r.status >= 200:
|
if 300 > r.status >= 200:
|
||||||
log.debug(self.SUCCESS_LOG.format(method=method, url=url, text=data))
|
log.debug(self.SUCCESS_LOG.format(method=method, url=url, text=data))
|
||||||
@ -120,7 +180,21 @@ class HTTPClient:
|
|||||||
# sleep a bit
|
# sleep a bit
|
||||||
retry_after = data['retry_after'] / 1000.0
|
retry_after = data['retry_after'] / 1000.0
|
||||||
log.info(fmt.format(retry_after, bucket))
|
log.info(fmt.format(retry_after, bucket))
|
||||||
|
|
||||||
|
# check if it's a global rate limit
|
||||||
|
is_global = data.get('global', False)
|
||||||
|
if is_global:
|
||||||
|
log.info('Global rate limit has been hit. Retrying in {:.2} seconds.'.format(retry_after))
|
||||||
|
# acquire the global lock and block all processing
|
||||||
|
yield from self._global_lock
|
||||||
|
|
||||||
yield from asyncio.sleep(retry_after, loop=self.loop)
|
yield from asyncio.sleep(retry_after, loop=self.loop)
|
||||||
|
|
||||||
|
# release the global lock now that the
|
||||||
|
# global rate limit has passed
|
||||||
|
if is_global:
|
||||||
|
self._global_lock.release()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# we've received a 502, unconditional retry
|
# we've received a 502, unconditional retry
|
||||||
@ -177,7 +251,7 @@ class HTTPClient:
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = yield from self.post(self.LOGIN, json=payload, bucket=_func_())
|
data = yield from self.request(Route('POST', '/auth/login'), json=payload)
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
if e.response.status == 400:
|
if e.response.status == 400:
|
||||||
raise LoginFailure('Improper credentials have been passed.') from e
|
raise LoginFailure('Improper credentials have been passed.') from e
|
||||||
@ -192,7 +266,7 @@ class HTTPClient:
|
|||||||
self._token(token, bot=bot)
|
self._token(token, bot=bot)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = yield from self.get(self.ME)
|
data = yield from self.request(Route('GET', '/users/@me'))
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
self._token(old_token, bot=old_bot)
|
self._token(old_token, bot=old_bot)
|
||||||
if e.response.status == 401:
|
if e.response.status == 401:
|
||||||
@ -202,7 +276,7 @@ class HTTPClient:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
return self.post(self.LOGOUT, bucket=_func_())
|
return self.request(Route('POST', '/auth/logout'))
|
||||||
|
|
||||||
# Message management
|
# Message management
|
||||||
|
|
||||||
@ -211,10 +285,12 @@ class HTTPClient:
|
|||||||
'recipient_id': user_id
|
'recipient_id': user_id
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.post(self.ME + '/channels', json=payload, bucket=_func_())
|
return self.request(Route('POST', '/users/@me/channels'), json=payload)
|
||||||
|
|
||||||
|
# TODO: remove guild_id parameters here
|
||||||
|
|
||||||
def send_message(self, channel_id, content, *, guild_id=None, tts=False, embed=None):
|
def send_message(self, channel_id, content, *, guild_id=None, tts=False, embed=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages'.format(self, channel_id)
|
r = Route('POST', '/channels/{channel_id}/messages', channel_id=channel_id)
|
||||||
payload = {
|
payload = {
|
||||||
'nonce': random_integer(-2**63, 2**63 - 1)
|
'nonce': random_integer(-2**63, 2**63 - 1)
|
||||||
}
|
}
|
||||||
@ -228,14 +304,13 @@ class HTTPClient:
|
|||||||
if embed:
|
if embed:
|
||||||
payload['embed'] = embed
|
payload['embed'] = embed
|
||||||
|
|
||||||
return self.post(url, json=payload, bucket='messages:' + str(guild_id))
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def send_typing(self, channel_id):
|
def send_typing(self, channel_id):
|
||||||
url = '{0.CHANNELS}/{1}/typing'.format(self, channel_id)
|
return self.request(Route('POST', '/channels/{channel_id}/typing', channel_id=channel_id))
|
||||||
return self.post(url, bucket=_func_())
|
|
||||||
|
|
||||||
def send_file(self, channel_id, buffer, *, guild_id=None, filename=None, content=None, tts=False, embed=None):
|
def send_file(self, channel_id, buffer, *, guild_id=None, filename=None, content=None, tts=False, embed=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages'.format(self, channel_id)
|
r = Route('POST', '/channels/{channel_id}/messages', channel_id=channel_id)
|
||||||
form = aiohttp.FormData()
|
form = aiohttp.FormData()
|
||||||
|
|
||||||
payload = {'tts': tts}
|
payload = {'tts': tts}
|
||||||
@ -247,23 +322,24 @@ class HTTPClient:
|
|||||||
form.add_field('payload_json', utils.to_json(payload))
|
form.add_field('payload_json', utils.to_json(payload))
|
||||||
form.add_field('file', buffer, filename=filename, content_type='application/octet-stream')
|
form.add_field('file', buffer, filename=filename, content_type='application/octet-stream')
|
||||||
|
|
||||||
return self.post(url, data=form, bucket='messages:' + str(guild_id))
|
return self.request(r, data=form)
|
||||||
|
|
||||||
def delete_message(self, channel_id, message_id, guild_id=None):
|
def delete_message(self, channel_id, message_id, guild_id=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}'.format(self, channel_id, message_id)
|
r = Route('DELETE', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id,
|
||||||
bucket = '{}:{}'.format(_func_(), guild_id)
|
message_id=message_id)
|
||||||
return self.delete(url, bucket=bucket)
|
return self.request(r)
|
||||||
|
|
||||||
def delete_messages(self, channel_id, message_ids, guild_id=None):
|
def delete_messages(self, channel_id, message_ids, guild_id=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages/bulk_delete'.format(self, channel_id)
|
r = Route('POST', '/channels/{channel_id}/messages/bulk_delete', channel_id=channel_id)
|
||||||
payload = {
|
payload = {
|
||||||
'messages': message_ids
|
'messages': message_ids
|
||||||
}
|
}
|
||||||
bucket = '{}:{}'.format(_func_(), guild_id)
|
|
||||||
return self.post(url, json=payload, bucket=bucket)
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def edit_message(self, message_id, channel_id, content, *, guild_id=None, embed=None):
|
def edit_message(self, message_id, channel_id, content, *, guild_id=None, embed=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}'.format(self, channel_id, message_id)
|
r = Route('PATCH', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id,
|
||||||
|
message_id=message_id)
|
||||||
payload = {}
|
payload = {}
|
||||||
|
|
||||||
if content:
|
if content:
|
||||||
@ -272,33 +348,36 @@ class HTTPClient:
|
|||||||
if embed:
|
if embed:
|
||||||
payload['embed'] = embed
|
payload['embed'] = embed
|
||||||
|
|
||||||
return self.patch(url, json=payload, bucket='messages:' + str(guild_id))
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def add_reaction(self, message_id, channel_id, emoji):
|
def add_reaction(self, message_id, channel_id, emoji):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}/reactions/{3}/@me'.format(
|
r = Route('PUT', '/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me',
|
||||||
self, channel_id, message_id, emoji)
|
channel_id=channel_id, message_id=message_id, emoji=emoji)
|
||||||
return self.put(url, bucket='%s:%s' % (_func_(), channel_id))
|
return self.request(r, header_bypass_delay=0.25)
|
||||||
|
|
||||||
def remove_reaction(self, message_id, channel_id, emoji, member_id):
|
def remove_reaction(self, message_id, channel_id, emoji, member_id):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}/reactions/{3}/{4}'.format(
|
r = Route('DELETE', '/channels/{channel_id}/messages/{message_id}/reactions/{emoji}/{member_id}',
|
||||||
self, channel_id, message_id, emoji, member_id)
|
channel_id=channel_id, message_id=message_id, member_id=member_id, emoji=emoji)
|
||||||
return self.delete(url, bucket='%s:%s' % (_func_(), channel_id))
|
return self.request(r, header_bypass_delay=0.25)
|
||||||
|
|
||||||
def get_reaction_users(self, message_id, channel_id, emoji, limit, after=None):
|
def get_reaction_users(self, message_id, channel_id, emoji, limit, after=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}/reactions/{3}'.format(
|
r = Route('GET', '/channels/{channel_id}/messages/{message_id}/reactions/{emoji}',
|
||||||
self, channel_id, message_id, emoji)
|
channel_id=channel_id, message_id=message_id, emoji=emoji)
|
||||||
|
|
||||||
params = {'limit': limit}
|
params = {'limit': limit}
|
||||||
if after:
|
if after:
|
||||||
params['after'] = after
|
params['after'] = after
|
||||||
return self.get(url, params=params, bucket='%s:%s' % (_func_(), channel_id))
|
return self.request(r, params=params)
|
||||||
|
|
||||||
def clear_reactions(self, message_id, channel_id):
|
def clear_reactions(self, message_id, channel_id):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}/reactions'.format(self, channel_id, message_id)
|
r = Route('DELETE', '/channels/{channel_id}/messages/{message_id}/reactions',
|
||||||
return self.delete(url, bucket='%s:%s' % (_func_(), channel_id))
|
channel_id=channel_id, message_id=message_id)
|
||||||
|
|
||||||
|
return self.request(r)
|
||||||
|
|
||||||
def get_message(self, channel_id, message_id):
|
def get_message(self, channel_id, message_id):
|
||||||
url = '{0.CHANNELS}/{1}/messages/{2}'.format(self, channel_id, message_id)
|
r = Route('GET', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id, message_id=message_id)
|
||||||
return self.get(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
def logs_from(self, channel_id, limit, before=None, after=None, around=None):
|
def logs_from(self, channel_id, limit, before=None, after=None, around=None):
|
||||||
url = '{0.CHANNELS}/{1}/messages'.format(self, channel_id)
|
url = '{0.CHANNELS}/{1}/messages'.format(self, channel_id)
|
||||||
@ -313,39 +392,38 @@ class HTTPClient:
|
|||||||
if around:
|
if around:
|
||||||
params['around'] = around
|
params['around'] = around
|
||||||
|
|
||||||
return self.get(url, params=params, bucket=_func_())
|
return self.request(Route('GET', '/channels/{channel_id}/messages', channel_id=channel_id), params=params)
|
||||||
|
|
||||||
def pin_message(self, channel_id, message_id):
|
def pin_message(self, channel_id, message_id):
|
||||||
url = '{0.CHANNELS}/{1}/pins/{2}'.format(self, channel_id, message_id)
|
return self.request(Route('PUT', '/channels/{channel_id}/pins/{message_id}',
|
||||||
return self.put(url, bucket=_func_())
|
channel_id=channel_id, message_id=message_id))
|
||||||
|
|
||||||
def unpin_message(self, channel_id, message_id):
|
def unpin_message(self, channel_id, message_id):
|
||||||
url = '{0.CHANNELS}/{1}/pins/{2}'.format(self, channel_id, message_id)
|
return self.request(Route('DELETE', '/channels/{channel_id}/pins/{message_id}',
|
||||||
return self.delete(url, bucket=_func_())
|
channel_id=channel_id, message_id=message_id))
|
||||||
|
|
||||||
def pins_from(self, channel_id):
|
def pins_from(self, channel_id):
|
||||||
url = '{0.CHANNELS}/{1}/pins'.format(self, channel_id)
|
return self.request(Route('GET', '/channels/{channel_id}/pins/', channel_id=channel_id))
|
||||||
return self.get(url, bucket=_func_())
|
|
||||||
|
|
||||||
# Member management
|
# Member management
|
||||||
|
|
||||||
def kick(self, user_id, guild_id):
|
def kick(self, user_id, guild_id):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
r = Route('DELETE', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
return self.delete(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
def ban(self, user_id, guild_id, delete_message_days=1):
|
def ban(self, user_id, guild_id, delete_message_days=1):
|
||||||
url = '{0.GUILDS}/{1}/bans/{2}'.format(self, guild_id, user_id)
|
r = Route('PUT', '/guilds/{guild_id}/bans/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
params = {
|
params = {
|
||||||
'delete-message-days': delete_message_days
|
'delete-message-days': delete_message_days
|
||||||
}
|
}
|
||||||
return self.put(url, params=params, bucket=_func_())
|
return self.request(r, params=params)
|
||||||
|
|
||||||
def unban(self, user_id, guild_id):
|
def unban(self, user_id, guild_id):
|
||||||
url = '{0.GUILDS}/{1}/bans/{2}'.format(self, guild_id, user_id)
|
r = Route('DELETE', '/guilds/{guild_id}/bans/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
return self.delete(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
def guild_voice_state(self, user_id, guild_id, *, mute=None, deafen=None):
|
def guild_voice_state(self, user_id, guild_id, *, mute=None, deafen=None):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
payload = {}
|
payload = {}
|
||||||
if mute is not None:
|
if mute is not None:
|
||||||
payload['mute'] = mute
|
payload['mute'] = mute
|
||||||
@ -353,7 +431,7 @@ class HTTPClient:
|
|||||||
if deafen is not None:
|
if deafen is not None:
|
||||||
payload['deaf'] = deafen
|
payload['deaf'] = deafen
|
||||||
|
|
||||||
return self.patch(url, json=payload, bucket='members:' + str(guild_id))
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def edit_profile(self, password, username, avatar, **fields):
|
def edit_profile(self, password, username, avatar, **fields):
|
||||||
payload = {
|
payload = {
|
||||||
@ -368,28 +446,24 @@ class HTTPClient:
|
|||||||
if 'new_password' in fields:
|
if 'new_password' in fields:
|
||||||
payload['new_password'] = fields['new_password']
|
payload['new_password'] = fields['new_password']
|
||||||
|
|
||||||
return self.patch(self.ME, json=payload, bucket=_func_())
|
return self.request(Route('PATCH', '/users/@me'), json=payload)
|
||||||
|
|
||||||
def change_my_nickname(self, guild_id, nickname):
|
def change_my_nickname(self, guild_id, nickname):
|
||||||
url = '{0.GUILDS}/{1}/members/@me/nick'.format(self, guild_id)
|
|
||||||
payload = {
|
payload = {
|
||||||
'nick': nickname
|
'nick': nickname
|
||||||
}
|
}
|
||||||
bucket = '{}:{}'.format(_func_(), guild_id)
|
return self.request(Route('PATCH', '/guilds/{guild_id}/members/@me/nick', guild_id=guild_id), json=payload)
|
||||||
return self.patch(url, json=payload, bucket=bucket)
|
|
||||||
|
|
||||||
def change_nickname(self, guild_id, user_id, nickname):
|
def change_nickname(self, guild_id, user_id, nickname):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
payload = {
|
payload = {
|
||||||
'nick': nickname
|
'nick': nickname
|
||||||
}
|
}
|
||||||
bucket = 'members:{}'.format(guild_id)
|
return self.request(r, json=payload)
|
||||||
return self.patch(url, json=payload, bucket=bucket)
|
|
||||||
|
|
||||||
def edit_member(self, guild_id, user_id, **fields):
|
def edit_member(self, guild_id, user_id, **fields):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
r = Route('PATCH', '/guilds/{guild_id}/members/{user_id}', guild_id=guild_id, user_id=user_id)
|
||||||
bucket = 'members:%s' % guild_id
|
return self.request(r, json=fields, bucket=bucket)
|
||||||
return self.patch(url, json=fields, bucket=bucket)
|
|
||||||
|
|
||||||
# Channel management
|
# Channel management
|
||||||
|
|
||||||
@ -401,10 +475,9 @@ class HTTPClient:
|
|||||||
k: v for k, v in options.items() if k in valid_keys
|
k: v for k, v in options.items() if k in valid_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.patch(url, json=payload, bucket=_func_())
|
return self.request(Route('PATCH', '/channels/{channel_id}', channel_id=channel_id), json=payload)
|
||||||
|
|
||||||
def create_channel(self, guild_id, name, channe_type, permission_overwrites=None):
|
def create_channel(self, guild_id, name, channe_type, permission_overwrites=None):
|
||||||
url = '{0.GUILDS}/{1}/channels'.format(self, guild_id)
|
|
||||||
payload = {
|
payload = {
|
||||||
'name': name,
|
'name': name,
|
||||||
'type': channe_type
|
'type': channe_type
|
||||||
@ -413,21 +486,18 @@ class HTTPClient:
|
|||||||
if permission_overwrites is not None:
|
if permission_overwrites is not None:
|
||||||
payload['permission_overwrites'] = permission_overwrites
|
payload['permission_overwrites'] = permission_overwrites
|
||||||
|
|
||||||
return self.post(url, json=payload, bucket=_func_())
|
return self.request(Route('POST', '/guilds/{guild_id}/channels', guild_id=guild_id), json=payload)
|
||||||
|
|
||||||
def delete_channel(self, channel_id):
|
def delete_channel(self, channel_id):
|
||||||
url = '{0.CHANNELS}/{1}'.format(self, channel_id)
|
return self.request(Route('DELETE', '/channels/{channel_id}', channel_id=channel_id))
|
||||||
return self.delete(url, bucket=_func_())
|
|
||||||
|
|
||||||
# Guild management
|
# Guild management
|
||||||
|
|
||||||
def leave_guild(self, guild_id):
|
def leave_guild(self, guild_id):
|
||||||
url = '{0.USERS}/@me/guilds/{1}'.format(self, guild_id)
|
return self.request(Route('DELETE', '/users/@me/guilds/{guild_id}', guild_id=guild_id))
|
||||||
return self.delete(url, bucket=_func_())
|
|
||||||
|
|
||||||
def delete_guild(self, guild_id):
|
def delete_guild(self, guild_id):
|
||||||
url = '{0.GUILDS}/{1}'.format(self, guild_id)
|
return self.request(Route('DELETE', '/guilds/{guild_id}', guild_id=guild_id))
|
||||||
return self.delete(url, bucket=_func_())
|
|
||||||
|
|
||||||
def create_guild(self, name, region, icon):
|
def create_guild(self, name, region, icon):
|
||||||
payload = {
|
payload = {
|
||||||
@ -436,7 +506,7 @@ class HTTPClient:
|
|||||||
'region': region
|
'region': region
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.post(self.GUILDS, json=payload, bucket=_func_())
|
return self.request(Route('POST', '/guilds'), json=payload)
|
||||||
|
|
||||||
def edit_guild(self, guild_id, **fields):
|
def edit_guild(self, guild_id, **fields):
|
||||||
valid_keys = ('name', 'region', 'icon', 'afk_timeout', 'owner_id',
|
valid_keys = ('name', 'region', 'icon', 'afk_timeout', 'owner_id',
|
||||||
@ -446,26 +516,22 @@ class HTTPClient:
|
|||||||
k: v for k, v in fields.items() if k in valid_keys
|
k: v for k, v in fields.items() if k in valid_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
url = '{0.GUILDS}/{1}'.format(self, guild_id)
|
return self.request(Route('PATCH', '/guilds/{guild_id}', guild_id=guild_id), json=payload)
|
||||||
return self.patch(url, json=payload, bucket=_func_())
|
|
||||||
|
|
||||||
def get_bans(self, guild_id):
|
def get_bans(self, guild_id):
|
||||||
url = '{0.GUILDS}/{1}/bans'.format(self, guild_id)
|
return self.request(Route('GET', '/guilds/{guild_id}/bans', guild_id=guild_id))
|
||||||
return self.get(url, bucket=_func_())
|
|
||||||
|
|
||||||
def prune_members(self, guild_id, days):
|
def prune_members(self, guild_id, days):
|
||||||
url = '{0.GUILDS}/{1}/prune'.format(self, guild_id)
|
|
||||||
params = {
|
params = {
|
||||||
'days': days
|
'days': days
|
||||||
}
|
}
|
||||||
return self.post(url, params=params, bucket=_func_())
|
return self.request(Route('POST', '/guilds/{guild_id}/prune', guild_id=guild_id), params=params)
|
||||||
|
|
||||||
def estimate_pruned_members(self, guild_id, days):
|
def estimate_pruned_members(self, guild_id, days):
|
||||||
url = '{0.GUILDS}/{1}/prune'.format(self, guild_id)
|
|
||||||
params = {
|
params = {
|
||||||
'days': days
|
'days': days
|
||||||
}
|
}
|
||||||
return self.get(url, params=params, bucket=_func_())
|
return self.request(Route('GET', '/guilds/{guild_id}/prune', guild_id=guild_id), params=params)
|
||||||
|
|
||||||
def create_custom_emoji(self, guild_id, name, image):
|
def create_custom_emoji(self, guild_id, name, image):
|
||||||
payload = {
|
payload = {
|
||||||
@ -473,117 +539,105 @@ class HTTPClient:
|
|||||||
'image': image
|
'image': image
|
||||||
}
|
}
|
||||||
|
|
||||||
bucket = '%s:%s' % (_func_(), guild_id)
|
r = Route('POST', '/guilds/{guild_id}/emojis', guild_id=guild_id)
|
||||||
return self.post('{0.GUILDS}/{1}/emojis'.format(self, guild_id), json=payload, bucket=bucket)
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def delete_custom_emoji(self, guild_id, emoji_id):
|
def delete_custom_emoji(self, guild_id, emoji_id):
|
||||||
url = '{0.GUILDS}/{1}/emojis/{2}'.format(self, guild_id, emoji_id)
|
return self.request(Route('DELETE', '/guilds/{guild_id}/emojis/{emoji_id}', guild_id=guild_id, emoji_id=emoji_id))
|
||||||
bucket = '%s:%s' % (_func_(), guild_id)
|
|
||||||
return self.delete(url, bucket=bucket)
|
|
||||||
|
|
||||||
def edit_custom_emoji(self, guild_id, emoji_id, *, name):
|
def edit_custom_emoji(self, guild_id, emoji_id, *, name):
|
||||||
payload = {
|
payload = {
|
||||||
'name': name
|
'name': name
|
||||||
}
|
}
|
||||||
url = '{0.GUILDS}/{1}/emojis/{2}'.format(self, guild_id, emoji_id)
|
r = Route('PATCH', '/guilds/{guild_id}/emojis/{emoji_id}', guild_id=guild_id, emoji_id=emoji_id)
|
||||||
bucket = '%s:%s' % (_func_(), guild_id)
|
return self.request(r, json=payload)
|
||||||
return self.patch(url, bucket=bucket, json=payload)
|
|
||||||
|
|
||||||
# Invite management
|
# Invite management
|
||||||
|
|
||||||
def create_invite(self, channel_id, **options):
|
def create_invite(self, channel_id, **options):
|
||||||
url = '{0.CHANNELS}/{1}/invites'.format(self, channel_id)
|
r = Route('POST', '/channels/{channel_id}/invites', channel_id=channel_id)
|
||||||
payload = {
|
payload = {
|
||||||
'max_age': options.get('max_age', 0),
|
'max_age': options.get('max_age', 0),
|
||||||
'max_uses': options.get('max_uses', 0),
|
'max_uses': options.get('max_uses', 0),
|
||||||
'temporary': options.get('temporary', False),
|
'temporary': options.get('temporary', False)
|
||||||
'xkcdpass': options.get('xkcd', False)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.post(url, json=payload, bucket=_func_())
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def get_invite(self, invite_id):
|
def get_invite(self, invite_id):
|
||||||
url = '{0.API_BASE}/invite/{1}'.format(self, invite_id)
|
return self.request(Route('GET', '/invite/{invite_id}', invite_id=invite_id))
|
||||||
return self.get(url, bucket=_func_())
|
|
||||||
|
|
||||||
def invites_from(self, guild_id):
|
def invites_from(self, guild_id):
|
||||||
url = '{0.GUILDS}/{1}/invites'.format(self, guild_id)
|
return self.request(Route('GET', '/guilds/{guild_id}/invites', guild_id=guild_id))
|
||||||
return self.get(url, bucket=_func_())
|
|
||||||
|
def invites_from_channel(self, channel_id):
|
||||||
|
return self.request(Route('GET', '/channels/{channel_id}/invites', channel_id=channel_id))
|
||||||
|
|
||||||
def accept_invite(self, invite_id):
|
def accept_invite(self, invite_id):
|
||||||
url = '{0.API_BASE}/invite/{1}'.format(self, invite_id)
|
return self.request(Route('POST', '/invite/{invite_id}', invite_id=invite_id))
|
||||||
return self.post(url, bucket=_func_())
|
|
||||||
|
|
||||||
def delete_invite(self, invite_id):
|
def delete_invite(self, invite_id):
|
||||||
url = '{0.API_BASE}/invite/{1}'.format(self, invite_id)
|
return self.request(Route('DELETE', '/invite/{invite_id}', invite_id=invite_id))
|
||||||
return self.delete(url, bucket=_func_())
|
|
||||||
|
|
||||||
# Role management
|
# Role management
|
||||||
|
|
||||||
def edit_role(self, guild_id, role_id, **fields):
|
def edit_role(self, guild_id, role_id, **fields):
|
||||||
url = '{0.GUILDS}/{1}/roles/{2}'.format(self, guild_id, role_id)
|
r = Route('PATCH', '/guilds/{guild_id}/roles/{role_id}', guild_id=guild_id, role_id=role_id)
|
||||||
valid_keys = ('name', 'permissions', 'color', 'hoist', 'mentionable')
|
valid_keys = ('name', 'permissions', 'color', 'hoist', 'mentionable')
|
||||||
payload = {
|
payload = {
|
||||||
k: v for k, v in fields.items() if k in valid_keys
|
k: v for k, v in fields.items() if k in valid_keys
|
||||||
}
|
}
|
||||||
return self.patch(url, json=payload, bucket='roles:' + str(guild_id))
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def delete_role(self, guild_id, role_id):
|
def delete_role(self, guild_id, role_id):
|
||||||
url = '{0.GUILDS}/{1}/roles/{2}'.format(self, guild_id, role_id)
|
r = Route('DELETE', '/guilds/{guild_id}/roles/{role_id}', guild_id=guild_id, role_id=role_id)
|
||||||
return self.delete(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
def replace_roles(self, user_id, guild_id, role_ids):
|
def replace_roles(self, user_id, guild_id, role_ids):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
return self.edit_member(guild_id=guild_id, user_id=user_id, roles=role_ids)
|
||||||
payload = {
|
|
||||||
'roles': role_ids
|
|
||||||
}
|
|
||||||
return self.patch(url, json=payload, bucket='members:' + str(guild_id))
|
|
||||||
|
|
||||||
def create_role(self, guild_id):
|
def create_role(self, guild_id):
|
||||||
url = '{0.GUILDS}/{1}/roles'.format(self, guild_id)
|
r = Route('POST', '/guilds/{guild_id}/roles/', guild_id=guild_id)
|
||||||
return self.post(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
def add_role(self, guild_id, member_id, role_id):
|
def add_role(self, guild_id, user_id, role_id):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}/{3}'.format(self, guild_id, member_id, role_id)
|
r = Route('PUT', '/guilds/{guild_id}/members/{user_id}/roles/{role_id}',
|
||||||
return self.put(url, bucket='%s:%s' % (_func_(), guild_id))
|
guild_id=guild_id, user_id=user_id, role_id=role_id)
|
||||||
|
return self.request(r)
|
||||||
|
|
||||||
def remove_role(self, guild_id, member_id, role_id):
|
def remove_role(self, guild_id, user_id, role_id):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}/{3}'.format(self, guild_id, member_id, role_id)
|
r = Route('DELETE', '/guilds/{guild_id}/members/{user_id}/roles/{role_id}',
|
||||||
return self.delete(url, bucket='%s:%s' % (_func_(), guild_id))
|
guild_id=guild_id, user_id=user_id, role_id=role_id)
|
||||||
|
return self.request(r)
|
||||||
|
|
||||||
def edit_channel_permissions(self, channel_id, target, allow, deny, type):
|
def edit_channel_permissions(self, channel_id, target, allow, deny, type):
|
||||||
url = '{0.CHANNELS}/{1}/permissions/{2}'.format(self, channel_id, target)
|
|
||||||
payload = {
|
payload = {
|
||||||
'id': target,
|
'id': target,
|
||||||
'allow': allow,
|
'allow': allow,
|
||||||
'deny': deny,
|
'deny': deny,
|
||||||
'type': type
|
'type': type
|
||||||
}
|
}
|
||||||
return self.put(url, json=payload, bucket=_func_())
|
r = Route('PUT', '/channels/{channel_id}/permissions/{target}', channel_id=channel_id, target=target)
|
||||||
|
return self.request(r, json=payload)
|
||||||
|
|
||||||
def delete_channel_permissions(self, channel_id, target):
|
def delete_channel_permissions(self, channel_id, target):
|
||||||
url = '{0.CHANNELS}/{1}/permissions/{2}'.format(self, channel_id, target)
|
r = Route('DELETE', '/channels/{channel_id}/permissions/{target}', channel_id=channel_id, target=target)
|
||||||
return self.delete(url, bucket=_func_())
|
return self.request(r)
|
||||||
|
|
||||||
# Voice management
|
# Voice management
|
||||||
|
|
||||||
def move_member(self, user_id, guild_id, channel_id):
|
def move_member(self, user_id, guild_id, channel_id):
|
||||||
url = '{0.GUILDS}/{1}/members/{2}'.format(self, guild_id, user_id)
|
return self.edit_member(guild_id=guild_id, user_id=user_id, channel_id=channel_id)
|
||||||
payload = {
|
|
||||||
'channel_id': channel_id
|
|
||||||
}
|
|
||||||
return self.patch(url, json=payload, bucket='members:' + str(guild_id))
|
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
|
|
||||||
def application_info(self):
|
def application_info(self):
|
||||||
url = '{0.APPLICATIONS}/@me'.format(self)
|
return self.request(Route('GET', '/oauth/applications/@me'))
|
||||||
return self.get(url, bucket=_func_())
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def get_gateway(self):
|
def get_gateway(self):
|
||||||
try:
|
try:
|
||||||
data = yield from self.get(self.GATEWAY, bucket=_func_())
|
data = yield from self.request(Route('GET', '/gateway'))
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise GatewayNotFound() from e
|
raise GatewayNotFound() from e
|
||||||
return data.get('url') + '?encoding=json&v=6'
|
return data.get('url') + '?encoding=json&v=6'
|
||||||
@ -591,11 +645,11 @@ class HTTPClient:
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def get_bot_gateway(self):
|
def get_bot_gateway(self):
|
||||||
try:
|
try:
|
||||||
data = yield from self.get(self.GATEWAY + '/bot', bucket=_func_())
|
data = yield from self.request(Route('GET', '/gateway/bot'))
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise GatewayNotFound() from e
|
raise GatewayNotFound() from e
|
||||||
else:
|
else:
|
||||||
return data['shards'], data['url'] + '?encoding=json&v=6'
|
return data['shards'], data['url'] + '?encoding=json&v=6'
|
||||||
|
|
||||||
def get_user_info(self, user_id):
|
def get_user_info(self, user_id):
|
||||||
return self.get('{0.USERS}/{1}'.format(self, user_id), bucket=_func_())
|
return self.request(Route('GET', '/users/{user_id}', user_id=user_id))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user