-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added a non async version of the library
- Loading branch information
1 parent
77a3df9
commit 499e139
Showing
6 changed files
with
259 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import weakref | ||
|
||
from typing import Optional, Union, List | ||
|
||
from .http import Route, HTTPClient | ||
from ..token import AccessTokenResponse | ||
from ..user import User | ||
|
||
|
||
__all__: tuple = ( | ||
"OAuth2Client", | ||
) | ||
|
||
|
||
class NoAsyncOAuth2Client: | ||
""" | ||
A class representing a client interacting with the discord OAuth2 API. | ||
""" | ||
def __init__( | ||
self, | ||
*, | ||
client_id: int, | ||
client_secret: str, | ||
redirect_uri: str, | ||
scopes: Optional[List[str]] = None | ||
): | ||
"""A class representing a client interacting with the discord OAuth2 API. | ||
:param client_id: The OAuth application's client_id | ||
:type client_id: int | ||
:param client_secret: The OAuth application's client_secret | ||
:type client_secret: str | ||
:param redirect_uri: The OAuth application's redirect_uri. Must be from one of the configured uri's on the developer portal | ||
:type redirect_uri: str | ||
:param scopes: A list of OAuth2 scopes, defaults to None | ||
:type scopes: Optional[List[str]], optional | ||
""" | ||
self._id = client_id | ||
self._auth = client_secret | ||
self._redirect = redirect_uri | ||
self._scopes = " ".join(scopes) if scopes is not None else None | ||
|
||
self.http = HTTPClient() | ||
self.http._state_info.update( | ||
{ | ||
"client_id": self._id, | ||
"client_secret": self._auth, | ||
"redirect_uri": self._redirect, | ||
"scopes": self._scopes, | ||
} | ||
) | ||
|
||
self._user_cache = weakref.WeakValueDictionary() | ||
|
||
def exchange_code(self, code: str) -> AccessTokenResponse: | ||
"""Exchanges the code you receive from the OAuth2 redirect. | ||
:param code: The code you've received from the OAuth2 redirect | ||
:type code: str | ||
:return: A response class containing information about the access token | ||
:rtype: AccessTokenResponse | ||
""" | ||
route = Route("POST", "/oauth2/token") | ||
post_data = { | ||
"client_id": self._id, | ||
"client_secret": self._auth, | ||
"grant_type": "authorization_code", | ||
"code": code, | ||
"redirect_uri": self._redirect, | ||
} | ||
if self._scopes is not None: | ||
post_data["scope"] = self._scopes | ||
request_data = self.http.request(route, data=post_data) | ||
token_resp = AccessTokenResponse(data=request_data) | ||
return token_resp | ||
|
||
def refresh_token(self, refresh_token: Union[str, AccessTokenResponse]) -> AccessTokenResponse: | ||
"""Refreshes an access token. Takes either a string or an AccessTokenResponse. | ||
:param refresh_token: The refresh token you received when exchanging a redirect code | ||
:type refresh_token: Union[str, AccessTokenResponse] | ||
:return: A new access token response containg information about the refreshed access token | ||
:rtype: AccessTokenResponse | ||
""" | ||
refresh_token = ( | ||
refresh_token if isinstance(refresh_token, str) else refresh_token.token | ||
) | ||
route = Route("POST", "/oauth2/token") | ||
post_data = { | ||
"client_id": self._id, | ||
"client_secret": self._auth, | ||
"grant_type": "refresh_token", | ||
"refresh_token": refresh_token, | ||
} | ||
request_data = self.http.request(route, data=post_data) | ||
token_resp = AccessTokenResponse(data=request_data) | ||
return token_resp | ||
|
||
def fetch_user(self, access_token_response: AccessTokenResponse) -> User: | ||
"""Makes an api call to fetch a user using their access token. | ||
:param access_token_response: A class holding information about an access token | ||
:type access_token_response: AccessTokenResponse | ||
:return: Returns a User object holding information about the select user | ||
:rtype: User | ||
""" | ||
access_token = access_token_response.token | ||
route = Route("GET", "/users/@me") | ||
headers = {"Authorization": "Bearer {}".format(access_token)} | ||
resp = self.http.request(route, headers=headers) | ||
user = User(http=self.http, data=resp, acr=access_token_response) | ||
self._user_cache.update({user.id: user}) | ||
return user | ||
|
||
def get_user(self, id: int) -> Optional[User]: | ||
"""Gets a user from the cache. The cache is a WeakValueDictionary, so objects may be removed without notice. | ||
:param id: The id of the user you want to get | ||
:type id: int | ||
:return: A possible user object. Returns None if no User is found in cache. | ||
:rtype: Optional[User] | ||
""" | ||
user = self._user_cache.get(id) | ||
return user | ||
|
||
def close(self): | ||
"""Closes and performs cleanup operations on the client, such as clearing its cache. | ||
""" | ||
self._user_cache.clear() | ||
self.http.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from requests import Response | ||
|
||
|
||
class ExtOauthException(Exception): | ||
""" | ||
The base exception the library always raises | ||
""" | ||
|
||
|
||
class HTTPException(ExtOauthException): | ||
""" | ||
The error that is raised whenever an http error occurs | ||
""" | ||
|
||
def __init__(self, resp: Response, *, json: dict = {}): | ||
self.resp = resp | ||
self.msg = json.get("error_description") or json.get("message") | ||
|
||
def __str__(self): | ||
fmt = "{0.status_code}: {0.reason}: {1}" | ||
return fmt.format(self.resp, self.msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import requests | ||
|
||
from .errors import HTTPException | ||
|
||
|
||
__all__: tuple = ( | ||
"Route", | ||
"HTTPClient" | ||
) | ||
|
||
|
||
class Route: | ||
BASE = "https://discord.com/api/v9" | ||
|
||
def __init__(self, method: str, endpoint: str, **params): | ||
self.url = self.BASE + endpoint.format(**params) | ||
self.method = method | ||
|
||
|
||
class HTTPClient: | ||
def __init__(self): | ||
self.__session = None # filled in later | ||
self._state_info = {} # client fills this | ||
|
||
def _create_session(self) -> requests.Session: | ||
self.__session = requests.Session() | ||
return self.__session | ||
|
||
def request(self, route: Route, **kwargs) -> dict: | ||
if self.__session is None or self.__session.closed is True: | ||
self._create_session() | ||
|
||
headers = kwargs.pop("headers", {}) | ||
|
||
headers["Content-Type"] = "application/x-www-form-urlencoded" # the discord OAuth2 api requires this header to be set to this | ||
kwargs["headers"] = headers | ||
|
||
resp = self.__session.request(route.method, route.url, **kwargs) | ||
json = resp.json() | ||
if 200 <= resp.status < 300: | ||
return json | ||
else: | ||
raise HTTPException(resp, json=json) | ||
|
||
def close(self): | ||
self.__session.close() | ||
self.__session = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from __future__ import annotations | ||
|
||
from typing import List, TYPE_CHECKING | ||
|
||
from .http import Route | ||
from ..user import User | ||
|
||
if TYPE_CHECKING: | ||
from ..guild import Guild | ||
from ..token import AccessTokenResponse | ||
|
||
|
||
__all__: tuple = ( | ||
"User", | ||
) | ||
|
||
class NoAsyncUser(User): | ||
def refresh(self) -> AccessTokenResponse: | ||
"""Refreshes the access token for the user and returns a fresh access token response. | ||
:return: A class holding information about the new access token | ||
:rtype: AccessTokenResponse | ||
""" | ||
refresh_token = self.refresh_token | ||
route = Route("POST", "/oauth2/token") | ||
post_data = { | ||
"client_id": self._http._state_info["client_id"], | ||
"client_secret": self._http._state_info["client_secret"], | ||
"grant_type": "refresh_token", | ||
"refresh_token": refresh_token, | ||
} | ||
request_data = self._http.request(route, data=post_data) | ||
token_resp = AccessTokenResponse(data=request_data) | ||
self.refresh_token = token_resp.refresh_token | ||
self.access_token = token_resp.token | ||
self._acr = token_resp | ||
return token_resp | ||
|
||
def fetch_guilds(self, *, refresh: bool = True) -> List[Guild]: | ||
"""Makes an api call to fetch the guilds the user is in. Can fill a normal dictionary cache. | ||
:param refresh: Whether or not to refresh the guild cache attached to this user object. If false, returns the cached guilds, defaults to True | ||
:type refresh: bool, optional | ||
:return: A List of Guild objects either from cache or returned from the api call | ||
:rtype: List[Guild] | ||
""" | ||
if not refresh and self.guilds: | ||
return self.guilds | ||
|
||
route = Route("GET", "/users/@me/guilds") | ||
headers = {"Authorization": "Bearer {}".format(self.access_token)} | ||
resp = self._http.request(route, headers=headers) | ||
self.guilds = [] | ||
for array in resp: | ||
guild = Guild(data=array, user=self) | ||
self.guilds.append(guild) | ||
|
||
return self.guilds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
aiohttp | ||
aiohttp | ||
requests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters