Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
main.py
rewrite.py
rewrite.py
discordoauth2/__pycache__/
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
50 changes: 47 additions & 3 deletions discordoauth2/__init__.py
Original file line number Diff line number Diff line change
@@ -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={
Expand Down Expand Up @@ -74,23 +78,52 @@ 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")
self.scope = data.get("scope", "").split(" ")
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):
self.id = id
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)
Expand Down Expand Up @@ -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):
Expand Down
45 changes: 35 additions & 10 deletions example.py
Original file line number Diff line number Diff line change
@@ -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():
Expand All @@ -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}<br><br>{connections}<br><br>{guilds}<br><br>guild data for the first guild: {guilds_member}"

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down