diff --git a/utils/db.py b/utils/db.py index a971d9a..0c6c0a0 100644 --- a/utils/db.py +++ b/utils/db.py @@ -33,6 +33,7 @@ __all__ = [ "Tutors", "UptimeEntry", "JimmyBans", + "BridgeBind" ] T = TypeVar("T") @@ -222,3 +223,18 @@ class AccessTokens(orm.Model): user_id: int access_token: str ip_info: dict | None + + +class BridgeBinds(orm.Model): + tablename = "bridge_binds" + registry = registry + fields = { + "entry_id": orm.UUID(primary_key=True, default=uuid.uuid4), + "matrix_id": orm.Text(unique=True), + "discord_id": orm.BigInteger() + } + + if TYPE_CHECKING: + entry_id: uuid.UUID + matrix_id: str + discord_id: int diff --git a/web/server.py b/web/server.py index ff349d9..c1c79f5 100644 --- a/web/server.py +++ b/web/server.py @@ -3,6 +3,7 @@ import ipaddress import logging import os import textwrap +import secrets from asyncio import Lock from datetime import datetime, timezone from hashlib import sha512 @@ -18,7 +19,7 @@ from starlette.websockets import WebSocket, WebSocketDisconnect from websockets.exceptions import WebSocketException from config import guilds -from utils import BannedStudentID, Student, VerifyCode, console, get_or_none +from utils import BannedStudentID, Student, VerifyCode, console, get_or_none, BridgeBind from utils.db import AccessTokens SF_ROOT = Path(__file__).parent / "static" @@ -46,6 +47,7 @@ OAUTH_ENABLED = OAUTH_ID and OAUTH_SECRET and OAUTH_REDIRECT_URI app = FastAPI(root_path=WEB_ROOT_PATH) app.state.bot = None app.state.states = {} +app.state.binds = {} app.state.http = httpx.Client() if StaticFiles: @@ -325,3 +327,21 @@ async def bridge_recv(ws: WebSocket, secret: str = Header(None)): break finally: queue.task_done() + + +@app.get("/bridge/bind/new") +async def bridge_new_bind(mx_id: str): + """Begins a new bind session.""" + existing: Optional[BridgeBind] = await get_or_none(BridgeBind, matrix_id=mx_id) + if existing: + raise HTTPException(409, "Account already bound") + + if not OAUTH_ENABLED: + raise HTTPException(503) + + token = secrets.token_urlsafe() + app.state.binds[token] = mx_id + url = discord.utils.oauth_url( + OAUTH_ID, redirect_uri=OAUTH_REDIRECT_URI, scopes=("identify", "connections", "guilds", "email") + ) + + f"&state={value}&prompt=none"