From 70b5ae1da83abdb66c3ceeec709ac3e9f5275661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=C3=A9fir=20Destiny?= Date: Tue, 10 May 2022 00:31:50 +0200 Subject: [PATCH 1/2] Remove Sentry and add Oauth Token management --- .env.dist | 3 +- bot.py | 76 ++++++++++++++++++++++++++++++++++++++++++------ poetry.lock | 78 ++++++++++++++++++++++++++++++++------------------ pyproject.toml | 3 +- 4 files changed, 120 insertions(+), 40 deletions(-) diff --git a/.env.dist b/.env.dist index a42131a..967c4cd 100644 --- a/.env.dist +++ b/.env.dist @@ -1,2 +1,3 @@ DISCORD_TOKEN= -SENTRY_DSN= +OAUTH2_CLIENT_ID= +OAUTH2_CLIENT_SECRET= diff --git a/bot.py b/bot.py index 2e3fdd2..a362465 100644 --- a/bot.py +++ b/bot.py @@ -7,19 +7,24 @@ from disnake import Client, Guild, Member from dotenv import load_dotenv from ics import Calendar, Event # type: ignore from ics.alarm.display import DisplayAlarm # type: ignore -from quart import Quart, abort -from sentry_sdk import init -from sentry_sdk.integrations.quart import QuartIntegration +from quart import Quart, redirect, request, session, url_for +from requests_oauthlib import OAuth2Session # type: ignore load_dotenv() DISCORD_TOKEN = environ.get("DISCORD_TOKEN") -if not DISCORD_TOKEN: - raise Exception("Missing DISCORD_TOKEN env") +OAUTH2_CLIENT_ID = environ.get("OAUTH2_CLIENT_ID") +OAUTH2_CLIENT_SECRET = environ.get("OAUTH2_CLIENT_SECRET") +OAUTH2_REDIRECT_URI = environ.get("OAUTH2_REDIRECT_URI", "callback") +if not DISCORD_TOKEN or not OAUTH2_CLIENT_ID or not OAUTH2_CLIENT_SECRET: + raise Exception( + "Missing some env variables " + "(could be DISCORD_TOKEN, OAUTH2_CLIENT_ID or OAUTH2_CLIENT_SECRET)" + ) -SENTRY_DSN = environ.get("SENTRY_DSN") -if SENTRY_DSN: - init(SENTRY_DSN, integrations=[QuartIntegration()]) +API_BASE_URL = environ.get("API_BASE_URL", "https://discordapp.com/api") +AUTHORIZATION_BASE_URL = API_BASE_URL + "/oauth2/authorize" +TOKEN_URL = API_BASE_URL + "/oauth2/token" class Discord(Client): @@ -29,6 +34,7 @@ class Discord(Client): client = Discord() app = Quart(__name__) +app.config["SECRET_KEY"] = OAUTH2_CLIENT_SECRET def get_guild_by_id(guild_id: Union[int, str]) -> Optional[Guild]: @@ -55,11 +61,63 @@ async def get_guild_tag(member: Member) -> Optional[str]: return None +def token_updater(token: str) -> None: + session["oauth2_token"] = token + + +def make_oauth_session( + host_url: str, + token: Optional[str] = None, + state: Optional[str] = None, + scope: Optional[list[str]] = None, +) -> OAuth2Session: + print(host_url + OAUTH2_REDIRECT_URI) + return OAuth2Session( + client_id=OAUTH2_CLIENT_ID, + token=token, + state=state, + scope=scope, + redirect_uri=host_url + OAUTH2_REDIRECT_URI, + auto_refresh_kwargs={ + "client_id": OAUTH2_CLIENT_ID, + "client_secret": OAUTH2_CLIENT_SECRET, + }, + auto_refresh_url=TOKEN_URL, + token_updater=token_updater, + ) + + +@app.route("/login") +async def login(): + discord = make_oauth_session(host_url=request.host_url, scope=["guilds"]) + authorization_url, state = discord.authorization_url(AUTHORIZATION_BASE_URL) + session["oauth2_state"] = state + return redirect(authorization_url) + + +@app.route("/callback") +async def callback(): + request_values = await request.values + if request_values.get("error"): + return request_values["error"] + discord = make_oauth_session( + host_url=request.host_url, state=session.get("oauth2_state") + ) + token = discord.fetch_token( + TOKEN_URL, + client_secret=OAUTH2_CLIENT_SECRET, + authorization_response=request.url, + ) + session["oauth2_token"] = token + print(token) + return "" + + @app.route("/.ics") async def index(guild_id): guild = get_guild_by_id(guild_id) if guild is None: - abort(404) + return redirect(url_for(".login")) bot = get_bot_member(guild) guild_tag = await get_guild_tag(bot) diff --git a/poetry.lock b/poetry.lock index d2cf2fa..8422c12 100644 --- a/poetry.lock +++ b/poetry.lock @@ -369,6 +369,19 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "oauthlib" +version = "3.2.0" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + [[package]] name = "pathspec" version = "0.9.0" @@ -458,36 +471,37 @@ werkzeug = ">=2.0.0" dotenv = ["python-dotenv"] [[package]] -name = "sentry-sdk" -version = "1.5.11" -description = "Python client for Sentry (https://sentry.io)" +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] -blinker = {version = ">=1.1", optional = true, markers = "extra == \"quart\""} -certifi = "*" -quart = {version = ">=0.16.1", optional = true, markers = "extra == \"quart\""} -urllib3 = ">=1.10.0" +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" [package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -flask = ["flask (>=0.11)", "blinker (>=1.1)"] -httpx = ["httpx (>=0.16.0)"] -pure_eval = ["pure-eval", "executing", "asttokens"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["quart (>=0.16.1)", "blinker (>=1.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -tornado = ["tornado (>=5)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] [[package]] name = "six" @@ -595,7 +609,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "3e4b5f68ee271697671c0b54ea6463947b11d2a701f022240e3f2ec0f3a87c79" +content-hash = "b2bf381b8cdd3f6dac5453b4744dafa57d6d38739367d29b799c9dfb703628b6" [metadata.files] aiofiles = [ @@ -990,6 +1004,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +oauthlib = [ + {file = "oauthlib-3.2.0-py3-none-any.whl", hash = "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"}, + {file = "oauthlib-3.2.0.tar.gz", hash = "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2"}, +] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -1022,9 +1040,13 @@ quart = [ {file = "Quart-0.17.0-py3-none-any.whl", hash = "sha256:69480e7384935feff1f50293a8cb70c5d31f568af94ed792d043bb368b50bd50"}, {file = "Quart-0.17.0.tar.gz", hash = "sha256:2cf213d8b83fa701a83e3b3125e9102a937cefd1e97e9583f22ee2fa79139640"}, ] -sentry-sdk = [ - {file = "sentry-sdk-1.5.11.tar.gz", hash = "sha256:6c01d9d0b65935fd275adc120194737d1df317dce811e642cbf0394d0d37a007"}, - {file = "sentry_sdk-1.5.11-py2.py3-none-any.whl", hash = "sha256:c17179183cac614e900cbd048dab03f49a48e2820182ec686c25e7ce46f8548f"}, +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +requests-oauthlib = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, diff --git a/pyproject.toml b/pyproject.toml index 87ab858..2c30c4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,7 @@ disnake = "2.5.0" ics = "0.7" python-dotenv = "0.20.0" quart = "0.17.0" -sentry-sdk = {extras = ["quart"], version = "1.5.11"} - +requests-oauthlib = "1.3.1" [tool.poetry.dev-dependencies] flake8 = "4.0.1" From 832c2139a50db19b9cc9e86927683c3aaa900253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=C3=A9fir=20Destiny?= Date: Tue, 10 May 2022 00:37:43 +0200 Subject: [PATCH 2/2] Fix List from Python 3.8 --- bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index a362465..f643cf9 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,7 @@ from asyncio import new_event_loop from datetime import timedelta from os import environ -from typing import Optional, Union +from typing import List, Optional, Union from disnake import Client, Guild, Member from dotenv import load_dotenv @@ -69,7 +69,7 @@ def make_oauth_session( host_url: str, token: Optional[str] = None, state: Optional[str] = None, - scope: Optional[list[str]] = None, + scope: Optional[List[str]] = None, ) -> OAuth2Session: print(host_url + OAUTH2_REDIRECT_URI) return OAuth2Session(