diff --git a/.gitignore b/.gitignore
index 2748024..b2629ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
main.py
-rewrite.py
\ No newline at end of file
+rewrite.py
+discordoauth2/__pycache__/
\ No newline at end of file
diff --git a/README.md b/README.md
index b0e9d78..553e0cb 100644
--- a/README.md
+++ b/README.md
@@ -13,21 +13,38 @@ I've finally published the library to PyPi! So now you can use pip.
pip install discord-oauth2.py
```
### Example With Flask
-Don't forget to replace all the client information on line 20 and 21 with your application's own information. You can leave bot token empty if your not adding members to guilds.
+Don't forget to replace all the client information with your application's own information. You can leave bot token empty if your not adding members to guilds.
```py
import discordoauth2
from flask import Flask, request
client = discordoauth2.Client(849930878276993044, secret="very-secret-code",
-redirect="https://findingfakeurlsisprettyhard.tv/oauth2", bot_token="bot-token-only-required-for-guild-joining")
+redirect="https://findingfakeurlsisprettyhard.tv/oauth2", bot_token="bot-token-only-required-for-guild-joining-or-updating-linked-roles-metadata")
app = Flask(__name__)
+client.update_linked_roles_metadata([
+ {
+ "type": 2,
+ "key": "level",
+ "name": "Level",
+ "description": "The level the user is on"
+ },
+ {
+ "type": 7,
+ "key": "supporter",
+ "name": "Supporter",
+ "description": "Spent money to help the game"
+ }
+])
+
@app.route("/oauth2")
def oauth2():
code = request.args.get("code")
access = client.exchange_code(code)
+ access.update_metadata("Platform Name", "Username", level=69, supporter=True)
+
identify = access.fetch_identify()
connections = access.fetch_connections()
guilds = access.fetch_guilds()
diff --git a/discordoauth2/__init__.py b/discordoauth2/__init__.py
index 5405de9..afc350f 100644
--- a/discordoauth2/__init__.py
+++ b/discordoauth2/__init__.py
@@ -1,9 +1,13 @@
import requests
+from datetime import datetime
class PartialAccessToken():
def __init__(self, access_token, client) -> None:
- self.client: Client = client
+ self.client = client
self.token = access_token
+
+ def revoke(self):
+ return self.client.revoke_token(self.token, token_type="access_token")
def fetch_identify(self):
response = requests.get("https://discord.com/api/v10/users/@me", headers={
@@ -74,9 +78,31 @@ def join_guild(self, guild_id, user_id, nick = None, role_ids = None, mute = Fal
elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after'])
else:
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
+
+ def update_metadata(self, platform_name=None, username=None, **metadata):
+ def metadataTypeHook(item):
+ print(item, type(item), type(type(item)))
+ if type(item) == bool:
+ return 1 if item else 0
+ if type(item) == datetime:
+ return item.isoformat()
+ else: return item
+ response = requests.put(f"https://discord.com/api/v10/users/@me/applications/{self.client.id}/role-connection", headers={
+ "authorization": f"Bearer {self.token}"}, json={
+ "platform_name": platform_name,
+ "platform_username": username,
+ "metadata": {key: metadataTypeHook(value) for key, value in metadata.items()}
+ })
+
+ if response.ok:
+ return response.json()
+ elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.")
+ elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after'])
+ else:
+ raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
class AccessToken(PartialAccessToken):
- def __init__(self, data: dict, client) -> None:
+ def __init__(self, data, client) -> None:
super().__init__(data["access_token"], client)
self.expires = data.get("expires_in")
@@ -84,6 +110,9 @@ def __init__(self, data: dict, client) -> None:
self.refresh_token = data.get("refresh_token")
self.webhook = data.get("webhook")
self.guild = data.get("guild")
+
+ def revoke_refresh_token(self):
+ return self.client.revoke_token(self.refresh_token, token_type="refresh_token")
class Client():
def __init__(self, id, secret, redirect, bot_token=None):
@@ -91,6 +120,10 @@ def __init__(self, id, secret, redirect, bot_token=None):
self.redirect_url = redirect
self.__secret = secret
self.__bot_token = bot_token
+
+ def update_linked_roles_metadata(self, metadata):
+ requests.put(f"https://discord.com/api/v10/applications/{self.id}/role-connections/metadata", headers={
+ "authorization": f"Bot {self.__bot_token}"}, json=metadata)
def from_access_token(self, access_token):
return PartialAccessToken(access_token, self)
@@ -124,13 +157,24 @@ def client_credentails_grant(self, scope):
response = requests.post("https://discord.com/api/v10/oauth2/token", data={
"grant_type": "client_credentials", "scope": " ".join(scope)},
auth=(self.id, self.__secret))
-
if response.ok:
return AccessToken(response.json(), self)
elif response.status_code == 400: raise exceptions.HTTPException("the scope, client id or client secret is invalid/don't match.")
elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after'])
else:
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
+
+ def revoke_token(self, token, token_type=None):
+ response = requests.post("https://discord.com/api/oauth2/token/revoke",
+ data={"token": token, "token_type_hint": token_type},
+ auth=(self.id, self.__secret))
+ print(response.status_code, response.text)
+ if response.ok:
+ return
+ elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.")
+ elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after'])
+ else:
+ raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
class exceptions():
class BaseException(Exception):
diff --git a/example.py b/example.py
index 3de3b35..ad26e67 100644
--- a/example.py
+++ b/example.py
@@ -1,12 +1,33 @@
from flask import Flask, redirect, request
-from discordoauth2 import discordOauth2
+import discordoauth2
import os
app = Flask('Discord OAuth2 Example')
-client = discordOauth2(client=your-client-id, secret="your-client-secret",
+client = discordoauth2.Client(client=your-client-id, secret="your-client-secret",
redirect="your-redirect-url", token="your-bot-token (optional)")
# Replace your-client-id with your application's client id, replace your-client-secret with your client secret and replace your-redirect-url with the url that discord will redirect users to once they complete OAuth2.
-# If you want to add users to a guild, insert a bot token with CREATE_INSTANT_INVITE permissions in the guilds you want to add users to.
+# If you want to updated linked roles metadata or add users to a guild, insert a bot token with CREATE_INSTANT_INVITE permissions in the guilds you want to add users to.
+
+client.update_linked_roles_metadata([
+ {
+ "type": 2,
+ "key": "level",
+ "name": "Level",
+ "description": "The level the user is on"
+ },
+ {
+ "type": 2,
+ "key": "wins",
+ "name": "Wins",
+ "description": "The number of times the user has won"
+ },
+ {
+ "type": 7,
+ "key": "supporter",
+ "name": "Supporter",
+ "description": "Spent money to help the game"
+ }
+])
@app.route('/')
def main():
@@ -15,19 +36,23 @@ def main():
@app.route('/oauth2')
def oauth():
- tokenObject = client.exchange_code(token=request.args.get('code'))
- print("refresh token: "+tokenObject.refresh_token)
+ access_token = client.exchange_code(token=request.args.get('code'))
+ print("refresh token: "+access_token.refresh)
# returns basic data about the user, including username, avatar and badges, if the email scope was parsed, it will also return their email.
- identify = tokenObject.access.identify()
+ identify = access_token.fetch_identify()
# returns visible and hidden connections such as GitHub, YouTube or Twitter.
- connections = tokenObject.access.connections()
+ connections = access_token.fetch_connections()
# returns a list of guilds that the user is in
- guilds = tokenObject.access.guilds()
+ guilds = access_token.fetch_guilds()
# returns a member object for the provided guild
- guilds_member = tokenObject.access.guilds_member(guilds[0]["id"])
+ guilds_member = access_token.fetch_guild_member(guilds[0]["id"])
# makes a user join a guild, bot token provided must have CREATE_INSTANT_INVITE in that guild
- tokenObject.access.guilds_join(guild-id-here)
+ access_token.join_guild(guild-id-here, identify["id"])
+ # this update's the user's metadata for linked roles.
+ access_token.update_metadata("Platform Name", "Username", level=69, wins=420, supporter=True)
+ # when you're done with the token, you can revoke it: note this revokes both the access token and refresh token.
+ access_token.revoke()
return f"{identify}
{connections}
{guilds}
guild data for the first guild: {guilds_member}"
diff --git a/setup.py b/setup.py
index abbe7ca..43fb5fc 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
setup(
name='discord-oauth2.py',
description='Use Discord\'s OAuth2 effortlessly! Turns the auth code to a access token and the access token into scope infomation. ',
- version="1.0.1",
+ version="1.1.0",
long_description=long_description,
long_description_content_type="text/markdown",
license='MIT',