Skip to content

Commit 45899d3

Browse files
authored
added linked roles and token management - #5
added linked roles and token management
2 parents cea3ff9 + 6f02f97 commit 45899d3

File tree

5 files changed

+104
-17
lines changed

5 files changed

+104
-17
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
main.py
2-
rewrite.py
2+
rewrite.py
3+
discordoauth2/__pycache__/

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,38 @@ I've finally published the library to PyPi! So now you can use pip.
1313
pip install discord-oauth2.py
1414
```
1515
### Example With Flask
16-
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.
16+
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.
1717
```py
1818
import discordoauth2
1919
from flask import Flask, request
2020

2121
client = discordoauth2.Client(849930878276993044, secret="very-secret-code",
22-
redirect="https://findingfakeurlsisprettyhard.tv/oauth2", bot_token="bot-token-only-required-for-guild-joining")
22+
redirect="https://findingfakeurlsisprettyhard.tv/oauth2", bot_token="bot-token-only-required-for-guild-joining-or-updating-linked-roles-metadata")
2323
app = Flask(__name__)
2424

25+
client.update_linked_roles_metadata([
26+
{
27+
"type": 2,
28+
"key": "level",
29+
"name": "Level",
30+
"description": "The level the user is on"
31+
},
32+
{
33+
"type": 7,
34+
"key": "supporter",
35+
"name": "Supporter",
36+
"description": "Spent money to help the game"
37+
}
38+
])
39+
2540
@app.route("/oauth2")
2641
def oauth2():
2742
code = request.args.get("code")
2843

2944
access = client.exchange_code(code)
3045

46+
access.update_metadata("Platform Name", "Username", level=69, supporter=True)
47+
3148
identify = access.fetch_identify()
3249
connections = access.fetch_connections()
3350
guilds = access.fetch_guilds()

discordoauth2/__init__.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import requests
2+
from datetime import datetime
23

34
class PartialAccessToken():
45
def __init__(self, access_token, client) -> None:
5-
self.client: Client = client
6+
self.client = client
67
self.token = access_token
8+
9+
def revoke(self):
10+
return self.client.revoke_token(self.token, token_type="access_token")
711

812
def fetch_identify(self):
913
response = requests.get("https://discord.com/api/v10/users/@me", headers={
@@ -74,23 +78,52 @@ def join_guild(self, guild_id, user_id, nick = None, role_ids = None, mute = Fal
7478
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'])
7579
else:
7680
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
81+
82+
def update_metadata(self, platform_name=None, username=None, **metadata):
83+
def metadataTypeHook(item):
84+
print(item, type(item), type(type(item)))
85+
if type(item) == bool:
86+
return 1 if item else 0
87+
if type(item) == datetime:
88+
return item.isoformat()
89+
else: return item
90+
response = requests.put(f"https://discord.com/api/v10/users/@me/applications/{self.client.id}/role-connection", headers={
91+
"authorization": f"Bearer {self.token}"}, json={
92+
"platform_name": platform_name,
93+
"platform_username": username,
94+
"metadata": {key: metadataTypeHook(value) for key, value in metadata.items()}
95+
})
96+
97+
if response.ok:
98+
return response.json()
99+
elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.")
100+
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'])
101+
else:
102+
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
77103

78104
class AccessToken(PartialAccessToken):
79-
def __init__(self, data: dict, client) -> None:
105+
def __init__(self, data, client) -> None:
80106
super().__init__(data["access_token"], client)
81107

82108
self.expires = data.get("expires_in")
83109
self.scope = data.get("scope", "").split(" ")
84110
self.refresh_token = data.get("refresh_token")
85111
self.webhook = data.get("webhook")
86112
self.guild = data.get("guild")
113+
114+
def revoke_refresh_token(self):
115+
return self.client.revoke_token(self.refresh_token, token_type="refresh_token")
87116

88117
class Client():
89118
def __init__(self, id, secret, redirect, bot_token=None):
90119
self.id = id
91120
self.redirect_url = redirect
92121
self.__secret = secret
93122
self.__bot_token = bot_token
123+
124+
def update_linked_roles_metadata(self, metadata):
125+
requests.put(f"https://discord.com/api/v10/applications/{self.id}/role-connections/metadata", headers={
126+
"authorization": f"Bot {self.__bot_token}"}, json=metadata)
94127

95128
def from_access_token(self, access_token):
96129
return PartialAccessToken(access_token, self)
@@ -124,13 +157,24 @@ def client_credentails_grant(self, scope):
124157
response = requests.post("https://discord.com/api/v10/oauth2/token", data={
125158
"grant_type": "client_credentials", "scope": " ".join(scope)},
126159
auth=(self.id, self.__secret))
127-
128160
if response.ok:
129161
return AccessToken(response.json(), self)
130162
elif response.status_code == 400: raise exceptions.HTTPException("the scope, client id or client secret is invalid/don't match.")
131163
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'])
132164
else:
133165
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
166+
167+
def revoke_token(self, token, token_type=None):
168+
response = requests.post("https://discord.com/api/oauth2/token/revoke",
169+
data={"token": token, "token_type_hint": token_type},
170+
auth=(self.id, self.__secret))
171+
print(response.status_code, response.text)
172+
if response.ok:
173+
return
174+
elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.")
175+
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'])
176+
else:
177+
raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}")
134178

135179
class exceptions():
136180
class BaseException(Exception):

example.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
from flask import Flask, redirect, request
2-
from discordoauth2 import discordOauth2
2+
import discordoauth2
33
import os
44

55
app = Flask('Discord OAuth2 Example')
6-
client = discordOauth2(client=your-client-id, secret="your-client-secret",
6+
client = discordoauth2.Client(client=your-client-id, secret="your-client-secret",
77
redirect="your-redirect-url", token="your-bot-token (optional)")
88
# 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.
9-
# 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.
9+
# 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.
10+
11+
client.update_linked_roles_metadata([
12+
{
13+
"type": 2,
14+
"key": "level",
15+
"name": "Level",
16+
"description": "The level the user is on"
17+
},
18+
{
19+
"type": 2,
20+
"key": "wins",
21+
"name": "Wins",
22+
"description": "The number of times the user has won"
23+
},
24+
{
25+
"type": 7,
26+
"key": "supporter",
27+
"name": "Supporter",
28+
"description": "Spent money to help the game"
29+
}
30+
])
1031

1132
@app.route('/')
1233
def main():
@@ -15,19 +36,23 @@ def main():
1536

1637
@app.route('/oauth2')
1738
def oauth():
18-
tokenObject = client.exchange_code(token=request.args.get('code'))
19-
print("refresh token: "+tokenObject.refresh_token)
39+
access_token = client.exchange_code(token=request.args.get('code'))
40+
print("refresh token: "+access_token.refresh)
2041

2142
# returns basic data about the user, including username, avatar and badges, if the email scope was parsed, it will also return their email.
22-
identify = tokenObject.access.identify()
43+
identify = access_token.fetch_identify()
2344
# returns visible and hidden connections such as GitHub, YouTube or Twitter.
24-
connections = tokenObject.access.connections()
45+
connections = access_token.fetch_connections()
2546
# returns a list of guilds that the user is in
26-
guilds = tokenObject.access.guilds()
47+
guilds = access_token.fetch_guilds()
2748
# returns a member object for the provided guild
28-
guilds_member = tokenObject.access.guilds_member(guilds[0]["id"])
49+
guilds_member = access_token.fetch_guild_member(guilds[0]["id"])
2950
# makes a user join a guild, bot token provided must have CREATE_INSTANT_INVITE in that guild
30-
tokenObject.access.guilds_join(guild-id-here)
51+
access_token.join_guild(guild-id-here, identify["id"])
52+
# this update's the user's metadata for linked roles.
53+
access_token.update_metadata("Platform Name", "Username", level=69, wins=420, supporter=True)
54+
# when you're done with the token, you can revoke it: note this revokes both the access token and refresh token.
55+
access_token.revoke()
3156

3257
return f"{identify}<br><br>{connections}<br><br>{guilds}<br><br>guild data for the first guild: {guilds_member}"
3358

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
setup(
77
name='discord-oauth2.py',
88
description='Use Discord\'s OAuth2 effortlessly! Turns the auth code to a access token and the access token into scope infomation. ',
9-
version="1.0.1",
9+
version="1.1.0",
1010
long_description=long_description,
1111
long_description_content_type="text/markdown",
1212
license='MIT',

0 commit comments

Comments
 (0)