|
|
@ -1,4 +1,6 @@ |
|
|
|
import requests |
|
|
|
import random |
|
|
|
import gevent |
|
|
|
|
|
|
|
from holster.enum import Enum |
|
|
|
|
|
|
@ -19,66 +21,69 @@ class Routes(object): |
|
|
|
GATEWAY_GET = (HTTPMethod.GET, '/gateway') |
|
|
|
|
|
|
|
# Channels |
|
|
|
CHANNELS_GET = (HTTPMethod.GET, '/channels/{}') |
|
|
|
CHANNELS_MODIFY= (HTTPMethod.PATCH, '/channels/{}') |
|
|
|
CHANNELS_DELETE = (HTTPMethod.DELETE, '/channels/{}') |
|
|
|
|
|
|
|
CHANNELS_MESSAGES_LIST = (HTTPMethod.GET, '/channels/{}/messages') |
|
|
|
CHANNELS_MESSAGES_GET = (HTTPMethod.GET, '/channels/{}/messages/{}') |
|
|
|
CHANNELS_MESSAGES_CREATE = (HTTPMethod.POST, '/channels/{}/messages') |
|
|
|
CHANNELS_MESSAGES_MODFIY = (HTTPMethod.PATCH, '/channels/{}/messages/{}') |
|
|
|
CHANNELS_MESSAGES_DELETE = (HTTPMethod.DELETE, '/channels/{}/messages/{}') |
|
|
|
CHANNELS_MESSAGES_DELETE_BULK = (HTTPMethod.POST, '/channels/{}/messages/bulk_delete') |
|
|
|
|
|
|
|
CHANNELS_PERMISSIONS_MODIFY = (HTTPMethod.PUT, '/channels/{}/permissions/{}') |
|
|
|
CHANNELS_PERMISSIONS_DELETE = (HTTPMethod.DELETE, '/channels/{}/permissions/{}') |
|
|
|
CHANNELS_INVITES_LIST = (HTTPMethod.GET, '/channels/{}/invites') |
|
|
|
CHANNELS_INVITES_CREATE = (HTTPMethod.POST, '/channels/{}/invites') |
|
|
|
|
|
|
|
CHANNELS_PINS_LIST = (HTTPMethod.GET, '/channels/{}/pins') |
|
|
|
CHANNELS_PINS_CREATE = (HTTPMethod.PUT, '/channels/{}/pins/{}') |
|
|
|
CHANNELS_PINS_DELETE = (HTTPMethod.DELETE, '/channels/{}/pins/{}') |
|
|
|
CHANNELS = '/channels/{channel}' |
|
|
|
CHANNELS_GET = (HTTPMethod.GET, CHANNELS) |
|
|
|
CHANNELS_MODIFY = (HTTPMethod.PATCH, CHANNELS) |
|
|
|
CHANNELS_DELETE = (HTTPMethod.DELETE, CHANNELS) |
|
|
|
|
|
|
|
CHANNELS_MESSAGES_LIST = (HTTPMethod.GET, CHANNELS + '/messages') |
|
|
|
CHANNELS_MESSAGES_GET = (HTTPMethod.GET, CHANNELS + '/messages/{message}') |
|
|
|
CHANNELS_MESSAGES_CREATE = (HTTPMethod.POST, CHANNELS + '/messages') |
|
|
|
CHANNELS_MESSAGES_MODIFY = (HTTPMethod.PATCH, CHANNELS + '/messages/{message}') |
|
|
|
CHANNELS_MESSAGES_DELETE = (HTTPMethod.DELETE, CHANNELS + '/messages/{message}') |
|
|
|
CHANNELS_MESSAGES_DELETE_BULK = (HTTPMethod.POST, CHANNELS + '/messages/bulk_delete') |
|
|
|
|
|
|
|
CHANNELS_PERMISSIONS_MODIFY = (HTTPMethod.PUT, CHANNELS + '/permissions/{permission}') |
|
|
|
CHANNELS_PERMISSIONS_DELETE = (HTTPMethod.DELETE, CHANNELS + '/permissions/{permission}') |
|
|
|
CHANNELS_INVITES_LIST = (HTTPMethod.GET, CHANNELS + '/invites') |
|
|
|
CHANNELS_INVITES_CREATE = (HTTPMethod.POST, CHANNELS + '/invites') |
|
|
|
|
|
|
|
CHANNELS_PINS_LIST = (HTTPMethod.GET, CHANNELS + '/pins') |
|
|
|
CHANNELS_PINS_CREATE = (HTTPMethod.PUT, CHANNELS + '/pins/{pin}') |
|
|
|
CHANNELS_PINS_DELETE = (HTTPMethod.DELETE, CHANNELS + '/pins/{pin}') |
|
|
|
|
|
|
|
# Guilds |
|
|
|
GUILDS_GET = (HTTPMethod.GET, '/guilds/{}') |
|
|
|
GUILDS_MODIFY = (HTTPMethod.PATCH, '/guilds/{}') |
|
|
|
GUILDS_DELETE = (HTTPMethod.DELETE, '/guilds/{}') |
|
|
|
GUILDS_CHANNELS_LIST = (HTTPMethod.GET, '/guilds/{}/channels') |
|
|
|
GUILDS_CHANNELS_CREATE = (HTTPMethod.POST, '/guilds/{}/channels') |
|
|
|
GUILDS_CHANNELS_MODIFY = (HTTPMethod.PATCH, '/guilds/{}/channels') |
|
|
|
GUILDS_MEMBERS_LIST = (HTTPMethod.GET, '/guilds/{}/members') |
|
|
|
GUILDS_MEMBERS_GET = (HTTPMethod.GET, '/guilds/{}/members/{}') |
|
|
|
GUILDS_MEMBERS_MODIFY = (HTTPMethod.PATCH, '/guilds/{}/members/{}') |
|
|
|
GUILDS_MEMBERS_KICK = (HTTPMethod.DELETE, '/guilds/{}/members/{}') |
|
|
|
GUILDS_BANS_LIST = (HTTPMethod.GET, '/guilds/{}/bans') |
|
|
|
GUILDS_BANS_CREATE = (HTTPMethod.PUT, '/guilds/{}/bans/{}') |
|
|
|
GUILDS_BANS_DELETE = (HTTPMethod.DELETE, '/guilds/{}/bans/{}') |
|
|
|
GUILDS_ROLES_LIST = (HTTPMethod.GET, '/guilds/{}/roles') |
|
|
|
GUILDS_ROLES_CREATE = (HTTPMethod.GET, '/guilds/{}/roles') |
|
|
|
GUILDS_ROLES_MODIFY_BATCH = (HTTPMethod.PATCH, '/guilds/{}/roles') |
|
|
|
GUILDS_ROLES_MODIFY = (HTTPMethod.PATCH, '/guilds/{}/roles/{}') |
|
|
|
GUILDS_ROLES_DELETE = (HTTPMethod.DELETE, '/guilds/{}/roles/{}') |
|
|
|
GUILDS_PRUNE_COUNT = (HTTPMethod.GET, '/guilds/{}/prune') |
|
|
|
GUILDS_PRUNE_BEGIN = (HTTPMethod.POST, '/guilds/{}/prune') |
|
|
|
GUILDS_VOICE_REGIONS_LIST = (HTTPMethod.GET, '/guilds/{}/regions') |
|
|
|
GUILDS_INVITES_LIST = (HTTPMethod.GET, '/guilds/{}/invites') |
|
|
|
GUILDS_INTEGRATIONS_LIST = (HTTPMethod.GET, '/guilds/{}/integrations') |
|
|
|
GUILDS_INTEGRATIONS_CREATE = (HTTPMethod.POST, '/guilds/{}/integrations') |
|
|
|
GUILDS_INTEGRATIONS_MODIFY = (HTTPMethod.PATCH, '/guilds/{}/integrations/{}') |
|
|
|
GUILDS_INTEGRATIONS_DELETE = (HTTPMethod.DELETE, '/guilds/{}/integrations/{}') |
|
|
|
GUILDS_INTEGRATIONS_SYNC = (HTTPMethod.POST, '/guilds/{}/integrations/{}/sync') |
|
|
|
GUILDS_EMBED_GET = (HTTPMethod.GET, '/guilds/{}/embed') |
|
|
|
GUILDS_EMBED_MODIFY = (HTTPMethod.PATCH, '/guilds/{}/embed') |
|
|
|
GUILDS = '/guilds/{guild}' |
|
|
|
GUILDS_GET = (HTTPMethod.GET, GUILDS) |
|
|
|
GUILDS_MODIFY = (HTTPMethod.PATCH, GUILDS) |
|
|
|
GUILDS_DELETE = (HTTPMethod.DELETE, GUILDS) |
|
|
|
GUILDS_CHANNELS_LIST = (HTTPMethod.GET, GUILDS + '/channels') |
|
|
|
GUILDS_CHANNELS_CREATE = (HTTPMethod.POST, GUILDS + '/channels') |
|
|
|
GUILDS_CHANNELS_MODIFY = (HTTPMethod.PATCH, GUILDS + '/channels') |
|
|
|
GUILDS_MEMBERS_LIST = (HTTPMethod.GET, GUILDS + '/members') |
|
|
|
GUILDS_MEMBERS_GET = (HTTPMethod.GET, GUILDS + '/members/{member}') |
|
|
|
GUILDS_MEMBERS_MODIFY = (HTTPMethod.PATCH, GUILDS + '/members/{member}') |
|
|
|
GUILDS_MEMBERS_KICK = (HTTPMethod.DELETE, GUILDS + '/members/{member}') |
|
|
|
GUILDS_BANS_LIST = (HTTPMethod.GET, GUILDS + '/bans') |
|
|
|
GUILDS_BANS_CREATE = (HTTPMethod.PUT, GUILDS + '/bans/{user}') |
|
|
|
GUILDS_BANS_DELETE = (HTTPMethod.DELETE, GUILDS + '/bans/{user}') |
|
|
|
GUILDS_ROLES_LIST = (HTTPMethod.GET, GUILDS + '/roles') |
|
|
|
GUILDS_ROLES_CREATE = (HTTPMethod.GET, GUILDS + '/roles') |
|
|
|
GUILDS_ROLES_MODIFY_BATCH = (HTTPMethod.PATCH, GUILDS + '/roles') |
|
|
|
GUILDS_ROLES_MODIFY = (HTTPMethod.PATCH, GUILDS + '/roles/{role}') |
|
|
|
GUILDS_ROLES_DELETE = (HTTPMethod.DELETE, GUILDS + '/roles/{role}') |
|
|
|
GUILDS_PRUNE_COUNT = (HTTPMethod.GET, GUILDS + '/prune') |
|
|
|
GUILDS_PRUNE_BEGIN = (HTTPMethod.POST, GUILDS + '/prune') |
|
|
|
GUILDS_VOICE_REGIONS_LIST = (HTTPMethod.GET, GUILDS + '/regions') |
|
|
|
GUILDS_INVITES_LIST = (HTTPMethod.GET, GUILDS + '/invites') |
|
|
|
GUILDS_INTEGRATIONS_LIST = (HTTPMethod.GET, GUILDS + '/integrations') |
|
|
|
GUILDS_INTEGRATIONS_CREATE = (HTTPMethod.POST, GUILDS + '/integrations') |
|
|
|
GUILDS_INTEGRATIONS_MODIFY = (HTTPMethod.PATCH, GUILDS + '/integrations/{integration}') |
|
|
|
GUILDS_INTEGRATIONS_DELETE = (HTTPMethod.DELETE, GUILDS + '/integrations/{integration}') |
|
|
|
GUILDS_INTEGRATIONS_SYNC = (HTTPMethod.POST, GUILDS + '/integrations/{integration}/sync') |
|
|
|
GUILDS_EMBED_GET = (HTTPMethod.GET, GUILDS + '/embed') |
|
|
|
GUILDS_EMBED_MODIFY = (HTTPMethod.PATCH, GUILDS + '/embed') |
|
|
|
|
|
|
|
# Users |
|
|
|
USERS_ME_GET = (HTTPMethod.GET, '/users/@me') |
|
|
|
USERS_ME_PATCH = (HTTPMethod.PATCH, '/users/@me') |
|
|
|
USERS_ME_GUILDS_LIST = (HTTPMethod.GET, '/users/@me/guilds') |
|
|
|
USERS_ME_GUILDS_LEAVE = (HTTPMethod.DELETE, '/users/@me/guilds/{}') |
|
|
|
USERS_ME_DMS_LIST = (HTTPMethod.GET, '/users/@me/channels') |
|
|
|
USERS_ME_DMS_CREATE = (HTTPMethod.POST, '/users/@me/channels') |
|
|
|
USERS_ME_CONNECTIONS_LIST = (HTTPMethod.GET, '/users/@me/connections') |
|
|
|
USERS_GET = (HTTPMethod.GET, '/users/{}') |
|
|
|
USERS = '/users' |
|
|
|
USERS_ME_GET = (HTTPMethod.GET, USERS + '/@me') |
|
|
|
USERS_ME_PATCH = (HTTPMethod.PATCH, USERS + '/@me') |
|
|
|
USERS_ME_GUILDS_LIST = (HTTPMethod.GET, USERS + '/@me/guilds') |
|
|
|
USERS_ME_GUILDS_LEAVE = (HTTPMethod.DELETE, USERS + '/@me/guilds/{guild}') |
|
|
|
USERS_ME_DMS_LIST = (HTTPMethod.GET, USERS + '/@me/channels') |
|
|
|
USERS_ME_DMS_CREATE = (HTTPMethod.POST, USERS + '/@me/channels') |
|
|
|
USERS_ME_CONNECTIONS_LIST = (HTTPMethod.GET, USERS + '/@me/connections') |
|
|
|
USERS_GET = (HTTPMethod.GET, USERS + '/{user}') |
|
|
|
|
|
|
|
|
|
|
|
class APIException(Exception): |
|
|
@ -89,7 +94,7 @@ class APIException(Exception): |
|
|
|
|
|
|
|
|
|
|
|
class HTTPClient(LoggingClass): |
|
|
|
BASE_URL = 'https://discordapp.com/api' |
|
|
|
BASE_URL = 'https://discordapp.com/api/v6' |
|
|
|
MAX_RETRIES = 5 |
|
|
|
|
|
|
|
def __init__(self, token): |
|
|
@ -100,7 +105,8 @@ class HTTPClient(LoggingClass): |
|
|
|
'Authorization': 'Bot ' + token, |
|
|
|
} |
|
|
|
|
|
|
|
def __call__(self, route, *args, **kwargs): |
|
|
|
def __call__(self, route, args=None, **kwargs): |
|
|
|
args = args or {} |
|
|
|
retry = kwargs.pop('retry_number', 0) |
|
|
|
|
|
|
|
# Merge or set headers |
|
|
@ -109,17 +115,20 @@ class HTTPClient(LoggingClass): |
|
|
|
else: |
|
|
|
kwargs['headers'] = self.headers |
|
|
|
|
|
|
|
# Compile URL args |
|
|
|
compiled = (str(route[0]), (self.BASE_URL) + route[1].format(*args)) |
|
|
|
# Build the bucket URL |
|
|
|
filtered = {k: (v if v in ('guild', 'channel') else '') for k, v in args.items()} |
|
|
|
bucket = (route[0].value, route[1].format(**filtered)) |
|
|
|
|
|
|
|
# Possibly wait if we're rate limited |
|
|
|
self.limiter.check(compiled) |
|
|
|
self.limiter.check(bucket) |
|
|
|
|
|
|
|
# Make the actual request |
|
|
|
r = requests.request(compiled[0], compiled[1], **kwargs) |
|
|
|
url = self.BASE_URL + route[1].format(**args) |
|
|
|
print route[0].value, url, kwargs |
|
|
|
r = requests.request(route[0].value, url, **kwargs) |
|
|
|
|
|
|
|
# Update rate limiter |
|
|
|
self.limiter.update(compiled, r) |
|
|
|
self.limiter.update(bucket, r) |
|
|
|
|
|
|
|
# If we got a success status code, just return the data |
|
|
|
if r.status_code < 400: |
|
|
@ -134,5 +143,14 @@ class HTTPClient(LoggingClass): |
|
|
|
self.log.error('Failing request, hit max retries') |
|
|
|
raise APIException('Request failed after {} attempts'.format(self.MAX_RETRIES), r.status_code, r.content) |
|
|
|
|
|
|
|
backoff = self.random_backoff() |
|
|
|
self.log.warning('Request to `{}` failed with code {}, retrying after {}s'.format(url, r.status_code, backoff)) |
|
|
|
gevent.sleep(backoff) |
|
|
|
|
|
|
|
# Otherwise just recurse and try again |
|
|
|
return self(route, retry_number=retry, *args, **kwargs) |
|
|
|
return self(route, args, retry_number=retry, **kwargs) |
|
|
|
|
|
|
|
@staticmethod |
|
|
|
def random_backoff(): |
|
|
|
# 500 milliseconds to 5 seconds) |
|
|
|
return random.randint(500, 5000) / 1000.0 |
|
|
|