From 6e2e5bf94ad210d156c839c66990cc9ee6c00cfc Mon Sep 17 00:00:00 2001 From: bytequill Date: Mon, 31 Mar 2025 21:14:49 +0200 Subject: [PATCH] Initial commit && dcRemoteAuthCLI.py v1 --- .gitignore | 2 + .../DiscordRemoteAuthCLI/dcRemoteAuthCLI.py | 129 ++++++++++++++++++ .../dcRemoteAuthCLI.py.requirements | Bin 0 -> 106 bytes Res/dcRemoteAuthCLI.png | Bin 0 -> 12563 bytes readme.md | 24 ++++ 5 files changed, 155 insertions(+) create mode 100644 .gitignore create mode 100644 Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py create mode 100644 Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py.requirements create mode 100644 Res/dcRemoteAuthCLI.png create mode 100644 readme.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a42c24 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.env +*venv/ \ No newline at end of file diff --git a/Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py b/Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py new file mode 100644 index 0000000..371b78b --- /dev/null +++ b/Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +""" +This is a simple script wrapper for the "remote-auth" discord protocol. +It lets you generate a new discord session on command. +It was made quickly and is very crude and not very clean. + +Docs on the protocol: https://github.com/discord-userdoccers/discord-userdoccers/blob/master/pages/remote-authentication/mobile.mdx + +Author: https://github.com/bytequill +Made as part of https://github.com/bytequill/smallscripts series +""" +try: + import os, sys, json + import dotenv, requests + from rich.console import Console +except ImportError as e: + print("✘ Could not find package "+e.name+". Please install it from the "+sys.argv[0]+".requirements or if unavailable. use the requirements provided below:\n"+"""requests==2.32.3 +rich==14.0.0 +python-dotenv==1.1.0""") + exit(1) + +API_URL="https://discord.com/api/v9" +DC_TOKEN: str + +P_ERR = "[red]✘[/red] " +P_CHK = "[green]✓[/green] " +P_INF = "[blue]I[/blue] " +P_INP = "[yellow]/[/yellow] " + +def prompt_bool(con: Console, msg: str, default: bool) -> bool: + yn: str + if default: + yn = "- Y/n " + else: + yn = "- y/N " + + res = con.input(msg+yn) + if res == "": + return default + elif res.lower() == "y": + return True + elif res.lower() == "n": + return False + else: + con.print(P_ERR+"Invalid value. Please use '' or y or n") + return prompt_bool(con, msg, default) + +def contentfulInput(con: Console, msg: str) -> str: + i = con.input(msg + ": ") + if i and len(i) > 0: + return i + else: + con.print(P_ERR+"Please enter a value. It cannot be blank") + return contentfulInput(con, msg) + +def do_dcAPIreq(s: requests.Session, endpoint: str, method: str, body: dict) -> tuple[int, dict]: + r = requests.Request(method, API_URL+endpoint) + if body: r.data = json.dumps(body) + r = r.prepare() + + r.headers["authorization"] = DC_TOKEN + if body: r.headers["Content-Type"] = "application/json" + res = s.send(r) + + if res.status_code != 204: + try: + return (res.status_code, res.json()) + except Exception as e: + return (500, {"err": e}) + else: + return (res.status_code, None) + +def main(): + global DC_TOKEN + + con = Console() + s = requests.Session() + dotenv.load_dotenv() + DC_TOKEN = os.getenv("DC_TOKEN") + if DC_TOKEN and len(DC_TOKEN) > 0: + con.print(P_CHK+"Got token from env") + else: + con.print(P_ERR+"Could not get token from env. Create a .env with the variable DC_TOKEN") + exit(1) + + res = prompt_bool(con,P_INP+"Do you want to verify token?", False) + if res: + rcode, rdata = do_dcAPIreq(s, "/users/@me", "GET", None) + if rcode != 200: + con.print(P_ERR+"Could not verify token. Status="+str(rcode)) + exit(1) + else: + con.print(P_CHK+"Token verified as working. Username="+rdata["username"]+" id="+rdata["id"]) + + fingerprint = contentfulInput(con,P_INP+"Please enter remote auth fingerprint") + + con.print(P_INF+"Fingerprint is "+fingerprint) + + rcode, rdata = do_dcAPIreq(s, "/users/@me/remote-auth", "POST", {"fingerprint": fingerprint}) + handshake: str + if rcode != 200: + con.print(P_ERR+"Invalid respose from discord. Status="+str(rcode)+" rdata="+str(rdata)) + exit(1) + + if rdata["handshake_token"] and len(rdata["handshake_token"]) > 0: + handshake = rdata["handshake_token"] + else: + con.print(P_ERR+"Invalid handshake_token respose from discord. rdata="+str(rdata)) + exit(1) + + if prompt_bool(con, P_INP+"Are you sure you want to [green]accept[/green] the creation of a new session", True): + rcode, rdata = do_dcAPIreq(s, "/users/@me/remote-auth/finish", "POST", {"handshake_token": handshake, "temporary_token": False}) + if rcode != 204: + con.print(P_ERR+"Could not create a new session. status="+str(rcode)+" rdata="+str(rdata)) + exit(1) + else: + rcode, rdata = do_dcAPIreq(s, "/users/@me/remote-auth/cancel", "POST", {"handshake_token": handshake}) + if rcode != 204: + con.print(P_ERR+"Could not cancel succesfully. status="+str(rcode)+" rdata="+str(rdata)) + exit(1) + con.print(P_CHK+"done") + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + except Exception: + Console().print_exception() diff --git a/Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py.requirements b/Python/CLI/DiscordRemoteAuthCLI/dcRemoteAuthCLI.py.requirements new file mode 100644 index 0000000000000000000000000000000000000000..b39d7ec7f88e16ee611ff72f1b6e8d015c514b19 GIT binary patch literal 106 zcmXYpK@LDL6hz-z;wT!$)@hKmo1#kM@)%7snfZ%1^WP`3uyZO8k~~TshQ4G?MZ`1V cGP4Y#ZT8PLZtWZ^freQ(=~v(?ZG+9-4;D5NPyhe` literal 0 HcmV?d00001 diff --git a/Res/dcRemoteAuthCLI.png b/Res/dcRemoteAuthCLI.png new file mode 100644 index 0000000000000000000000000000000000000000..f462d75b77417e234c07e822108868484079381a GIT binary patch literal 12563 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 uNaugauLiU4KqsM(yzl-MkL