Browse Source

Oauth and guild integrations.

pull/161/head
Luke 6 years ago
parent
commit
2cc410b7f1
  1. 76
      disco/api/client.py
  2. 10
      disco/api/http.py
  3. 3
      disco/client.py
  4. 19
      disco/types/guild.py
  5. 87
      disco/types/oauth.py

76
disco/api/client.py

@ -11,7 +11,8 @@ from disco.util.logging import LoggingClass
from disco.util.sanitize import S
from disco.types.user import User
from disco.types.message import Message
from disco.types.guild import Guild, GuildMember, GuildBan, PruneCount, Role, GuildEmoji, AuditLogEntry
from disco.types.oauth import AccessToken, Application, Connection
from disco.types.guild import Guild, GuildMember, GuildBan, PruneCount, Role, GuildEmoji, AuditLogEntry, Integration
from disco.types.channel import Channel
from disco.types.invite import Invite
from disco.types.voice import VoiceRegion
@ -32,6 +33,10 @@ def _reason_header(value):
return optional(**{'X-Audit-Log-Reason': quote(to_bytes(value)) if value else None})
def _oauth2_header(token):
return optional(**{'Authorization': 'Bearer {}'.format(token) if token else None})
class Responses(list):
def rate_limited_duration(self):
return sum(i.rate_limited_duration for i in self)
@ -101,6 +106,31 @@ class APIClient(LoggingClass):
data = self.http(Routes.GATEWAY_BOT_GET).json()
return data
def oauth2_token_get(self, grant_type, scope, refresh_token=None, redirect_url=None):
payload = {
'client_id': self.client.state.me.id,
'client_secret': self.client.config.secret,
'grant_type': grant_type,
'scope': scope,
}
payload.update(optional(
refresh_token=refresh_token,
redirect_url=redirect_url,
))
r = self.http(Routes.OAUTH2_TOKEN, data=payload)
return AccessToken.create(self.client, r.json())
def oauth_token_revoke(self, token):
self.http(Routes.OAUTH2_TOKEN_REVOKE, data={
'client_id': self.client.state.me.id,
'client_secret': self.client.config.secret,
'token': token,
})
def oauth2_applications_me_get(self):
r = self.http(Routes.OAUTH2_APPLICATIONS_ME)
return Application.create(self.client, r.json())
def channels_get(self, channel):
r = self.http(Routes.CHANNELS_GET, dict(channel=channel))
return Channel.create(self.client, r.json())
@ -512,6 +542,37 @@ class APIClient(LoggingClass):
r = self.http(Routes.GUILDS_INVITES_LIST, dict(guild=guild))
return Invite.create_map(self.client, r.json())
def guilds_integrations_list(self, guild):
r = self.http(Routes.GUILDS_INTEGRATIONS_LIST, dict(guild=guild))
return Integration.create_map(self.client, r.json())
def guilds_integrations_create(self, guild, type, id):
r = self.http(Routes.GUILDS_INTEGRATIONS_CREATE, dict(guild=guild), json={"type": type, "id": id})
return Integration.create(r.json())
def guilds_integrations_modify(
self,
guild,
integration,
expire_behavior=None,
expire_grace_period=None,
enable_emoticons=None):
self.http(
Routes.GUILDS_INTEGRATIONS_MODIFY,
dict(guild=guild, integration=integration),
json=optional(
expire_behavior=expire_behavior,
expire_grace_period=expire_grace_period,
enable_emoticons=enable_emoticons,
))
def guilds_integrations_delete(self, guild, integration):
self.http(Routes.GUILDS_INTEGRATIONS_DELETE, dict(guild=guild, integration=integration))
def guilds_integrations_sync(self, guild, integration):
self.http(Routes.GUILDS_INTEGRATIONS_SYNC, dict(guild=guild, integration=integration))
def guilds_vanity_url_get(self, guild):
r = self.http(Routes.GUILDS_VANITY_URL_GET, dict(guild=guild))
return Invite.create(self.client, r.json())
@ -568,13 +629,18 @@ class APIClient(LoggingClass):
r = self.http(Routes.USERS_GET, dict(user=user))
return User.create(self.client, r.json())
def users_me_get(self):
return User.create(self.client, self.http(Routes.USERS_ME_GET).json())
def users_me_get(self, bearer_token=None):
r = self.http(Routes.USERS_ME_GET, headers=_oauth2_header(bearer_token))
return User.create(self.client, r.json())
def users_me_patch(self, payload):
r = self.http(Routes.USERS_ME_PATCH, json=payload)
return User.create(self.client, r.json())
def users_me_guilds_list(self, bearer_token=None):
r = self.http(Routes.USERS_ME_GUILDS_LIST, headers=_oauth2_header(bearer_token))
return Guild.create_map(self.client, r.json())
def users_me_guilds_delete(self, guild):
self.http(Routes.USERS_ME_GUILDS_DELETE, dict(guild=guild))
@ -584,6 +650,10 @@ class APIClient(LoggingClass):
})
return Channel.create(self.client, r.json())
def users_me_connections_list(self, bearer_token=None):
r = self.http(Routes.USERS_ME_CONNECTIONS_LIST, headers=_oauth2_header(bearer_token))
return Connection.create_map(self.client, r.json())
def invites_get(self, invite):
r = self.http(Routes.INVITES_GET, dict(invite=invite))
return Invite.create(self.client, r.json())

10
disco/api/http.py

@ -34,6 +34,12 @@ class Routes(object):
GATEWAY_GET = (HTTPMethod.GET, '/gateway')
GATEWAY_BOT_GET = (HTTPMethod.GET, '/gateway/bot')
# OAUTH2
OAUTH2 = '/oauth2'
OAUTH2_TOKEN = (HTTPMethod.POST, OAUTH2 + '/token')
OAUTH2_TOKEN_REVOKE = (HTTPMethod.POST, OAUTH2 + '/token/revoke')
OAUTH2_APPLICATIONS_ME = (HTTPMethod.GET, OAUTH2 + '/applications/@me')
# Channels
CHANNELS = '/channels/{channel}'
CHANNELS_GET = (HTTPMethod.GET, CHANNELS)
@ -178,7 +184,9 @@ class APIException(Exception):
self.msg = '{} ({} - {})'.format(data['message'], self.code, self.errors)
elif len(data) == 1:
key, value = list(data.items())[0]
self.msg = 'Request Failed: {}: {}'.format(key, ', '.join(value))
if not isinstance(value, str):
value = ', '.join(value)
self.msg = 'Request Failed: {}: {}'.format(key, value)
except ValueError:
pass

3
disco/client.py

@ -21,6 +21,8 @@ class ClientConfig(Config):
token : str
Discord authentication token, can be validated using the
`disco.util.token.is_valid_token` function.
secret : str
Discord client secret used for the oauth2 flow.
shard_id : int
The shard ID for the current client instance.
shard_count : int
@ -42,6 +44,7 @@ class ClientConfig(Config):
"""
token = ''
secret = ''
shard_id = 0
shard_count = 1
guild_subscriptions = True

19
disco/types/guild.py

@ -603,6 +603,25 @@ class Guild(SlottedModel, Permissible):
return self.client.api.guilds_auditlogs_list(self.id, *args, **kwargs)
class IntegrationAccount(SlottedModel):
id = Field(text)
name = Field(text)
class Integration(SlottedModel):
id = Field(snowflake)
name = Field(text)
type = Field(text)
enabled = Field(bool)
syncing = Field(bool)
role_id = Field(snowflake)
expire_behavior = Field(int)
expire_grace_period = Field(int)
user = Field(User)
account = Field(IntegrationAccount)
synced_at = Field(datetime)
class AuditLogActionTypes(object):
GUILD_UPDATE = 1
CHANNEL_CREATE = 10

87
disco/types/oauth.py

@ -0,0 +1,87 @@
from disco.types.base import (
SlottedModel, Field, ListField, snowflake, text, enum,
)
from disco.types.guild import Integration
from disco.types.user import User
class AccessToken(SlottedModel):
access_token = Field(text)
token_type = Field(text)
expires_in = Field(int)
refresh_token = Field(text)
scope = Field(text)
class TeamMembershipState(object):
INVITED = 1
ACCEPTED = 2
class TeamMember(SlottedModel):
membership_state = Field(enum(TeamMembershipState))
permissions = Field(text)
team_id = Field(snowflake)
user = Field(User)
class Team(SlottedModel):
icon = Field(text)
id = Field(snowflake)
members = ListField(TeamMember)
owner_user_id = Field(snowflake)
class Application(SlottedModel):
id = Field(snowflake)
name = Field(text)
icon = Field(text)
description = Field(text)
rpc_origins = ListField(text)
bot_public = Field(bool)
bot_require_code_grant = Field(bool)
owner = Field(User)
summary = Field(text)
verify_key = Field(text)
team = Field(Team)
guild_id = Field(snowflake)
primary_sku_id = Field(snowflake)
slug = Field(text)
cover_image = Field(text)
def get_icon_url(self, fmt='webp', size=1024):
if not self.icon:
return ''
return 'https://cdn.discordapp.com/app-icons/{}/{}.{}?size={}'.format(self.id, self.icon, fmt, size)
def get_cover_image_url(self, fmt='webp', size=1024):
if not self.cover_image:
return ''
return 'https://cdn.discordapp.com/app-icons/{}/{}.{}?size={}'.format(self.id, self.cover_image, fmt, size)
@property
def icon_url(self):
return self.get_icon_url()
@property
def cover_image_url(self):
return self.get_cover_image_url()
class ConnectionVisibility(object):
Nobody = 0
Everyone = 1
class Connection(SlottedModel):
id = Field(text)
name = Field(text)
type = Field(text)
revoked = Field(bool)
integrations = ListField(Integration)
verified = Field(bool)
friend_sync = Field(bool)
show_activity = Field(bool)
visibility = Field(enum(ConnectionVisibility))
Loading…
Cancel
Save