From c520a528d12cfdb84b7efea22a44f39e120cc3c6 Mon Sep 17 00:00:00 2001 From: datawhores Date: Sun, 18 Feb 2024 17:37:37 -0600 Subject: [PATCH] rearrange auth accept direct pasting of OnlyFans Cookie Helper into text document --- ofscraper/api/pinned.py | 2 - ofscraper/classes/sessionbuilder.py | 10 +- ofscraper/commands/check.py | 5 +- ofscraper/download/keyhelpers.py | 21 +- ofscraper/prompts/prompt_groups/auth.py | 17 +- ofscraper/prompts/prompt_groups/config.py | 4 +- ofscraper/runner/load.py | 2 +- ofscraper/utils/auth.py | 350 ---------------------- ofscraper/utils/auth/context.py | 44 +++ ofscraper/utils/auth/data.py | 61 ++++ ofscraper/utils/auth/file.py | 53 ++++ ofscraper/utils/auth/helpers.py | 76 +++++ ofscraper/utils/auth/make.py | 50 ++++ ofscraper/utils/auth/request.py | 186 ++++++++++++ ofscraper/utils/auth/schema.py | 34 +++ ofscraper/utils/checkers.py | 5 +- ofscraper/utils/config/config.py | 15 +- ofscraper/utils/config/context.py | 7 +- ofscraper/utils/config/custom.py | 24 +- ofscraper/utils/config/menu.py | 16 +- ofscraper/utils/logs/other.py | 2 +- ofscraper/utils/menu.py | 4 +- 22 files changed, 573 insertions(+), 415 deletions(-) delete mode 100644 ofscraper/utils/auth.py create mode 100644 ofscraper/utils/auth/context.py create mode 100644 ofscraper/utils/auth/data.py create mode 100644 ofscraper/utils/auth/file.py create mode 100644 ofscraper/utils/auth/helpers.py create mode 100644 ofscraper/utils/auth/make.py create mode 100644 ofscraper/utils/auth/request.py create mode 100644 ofscraper/utils/auth/schema.py diff --git a/ofscraper/api/pinned.py b/ofscraper/api/pinned.py index cce464789..b19725d95 100644 --- a/ofscraper/api/pinned.py +++ b/ofscraper/api/pinned.py @@ -34,8 +34,6 @@ from ofscraper.classes.semaphoreDelayed import semaphoreDelayed from ofscraper.utils.context.run_async import run -from ..utils import auth - log = logging.getLogger("shared") attempt = contextvars.ContextVar("attempt") sem = None diff --git a/ofscraper/classes/sessionbuilder.py b/ofscraper/classes/sessionbuilder.py index 79279324d..85d0637c5 100644 --- a/ofscraper/classes/sessionbuilder.py +++ b/ofscraper/classes/sessionbuilder.py @@ -6,11 +6,11 @@ import certifi import httpx +import ofscraper.utils.auth.file as files +import ofscraper.utils.auth.request as auth_requests import ofscraper.utils.config.data as data import ofscraper.utils.constants as constants -from ..utils import auth - #### # This class allows the user to select which backend aiohttp or httpx they want to use # httpx has better compatiblilty but is slower @@ -121,17 +121,17 @@ def __exit__(self, exc_type, exc_val, exc_tb): def _create_headers(self, headers, url): headers = headers or {} if self._set_header: - new_headers = auth.make_headers(auth.read_auth()) + new_headers = auth_requests.make_headers(files.read_auth()) headers.update(new_headers) headers = self._create_sign(headers, url) return headers def _create_sign(self, headers, url): - auth.create_sign(url, headers) if self._set_sign else None + auth_requests.create_sign(url, headers) if self._set_sign else None return headers def _create_cookies(self): - return auth.add_cookies() + return auth_requests.add_cookies() def requests( self, diff --git a/ofscraper/commands/check.py b/ofscraper/commands/check.py index bddcf5525..72c972fe0 100644 --- a/ofscraper/commands/check.py +++ b/ofscraper/commands/check.py @@ -23,7 +23,8 @@ import ofscraper.models.selector as selector import ofscraper.utils.args.read as read_args import ofscraper.utils.args.write as write_args -import ofscraper.utils.auth as auth +import ofscraper.utils.auth.file as auth_file +import ofscraper.utils.auth.request as auth_requests import ofscraper.utils.cache as cache import ofscraper.utils.console as console_ import ofscraper.utils.constants as constants @@ -298,7 +299,7 @@ def message_checker(): def purchase_checker(): user_dict = {} - auth.make_headers(auth.read_auth()) + auth_requests.make_headers(auth_file.read_auth()) ROWS = [] for user_name in read_args.retriveArgs().username: user_name = profile.scrape_profile(user_name)["username"] diff --git a/ofscraper/download/keyhelpers.py b/ofscraper/download/keyhelpers.py index 3c585cead..ef74d0dc1 100644 --- a/ofscraper/download/keyhelpers.py +++ b/ofscraper/download/keyhelpers.py @@ -21,7 +21,8 @@ import ofscraper.classes.sessionbuilder as sessionbuilder import ofscraper.download.common as common import ofscraper.utils.args.read as read_args -import ofscraper.utils.auth as auth +import ofscraper.utils.auth.file as auth_file +import ofscraper.utils.auth.request as auth_requests import ofscraper.utils.cache as cache import ofscraper.utils.config.data as config_data import ofscraper.utils.constants as constants @@ -101,9 +102,9 @@ async def key_helper_cdrm(c, pssh, licence_url, id): try: log.debug(f"ID:{id} pssh: {pssh!=None}") log.debug(f"ID:{id} licence: {licence_url}") - headers = auth.make_headers(auth.read_auth()) - headers["cookie"] = auth.get_cookies() - auth.create_sign(licence_url, headers) + headers = auth_requests.make_headers(auth_file.read_auth()) + headers["cookie"] = auth_requests.get_cookies() + auth_requests.create_sign(licence_url, headers) json_data = { "license": licence_url, "headers": json.dumps(headers), @@ -151,9 +152,9 @@ async def key_helper_cdrm2(c, pssh, licence_url, id): try: log.debug(f"ID:{id} pssh: {pssh!=None}") log.debug(f"ID:{id} licence: {licence_url}") - headers = auth.make_headers(auth.read_auth()) - headers["cookie"] = auth.get_cookies() - auth.create_sign(licence_url, headers) + headers = auth_requests.make_headers(auth_file.read_auth()) + headers["cookie"] = auth_requests.get_cookies() + auth_requests.create_sign(licence_url, headers) json_data = { "license": licence_url, "headers": json.dumps(headers), @@ -201,9 +202,9 @@ async def key_helper_keydb(c, pssh, licence_url, id): try: log.debug(f"ID:{id} pssh: {pssh!=None}") log.debug(f"ID:{id} licence: {licence_url}") - headers = auth.make_headers(auth.read_auth()) - headers["cookie"] = auth.get_cookies() - auth.create_sign(licence_url, headers) + headers = auth_requests.make_headers(auth_file.read_auth()) + headers["cookie"] = auth_requests.get_cookies() + auth_requests.create_sign(licence_url, headers) json_data = { "license_url": licence_url, "headers": json.dumps(headers), diff --git a/ofscraper/prompts/prompt_groups/auth.py b/ofscraper/prompts/prompt_groups/auth.py index fac944176..cdc72af57 100644 --- a/ofscraper/prompts/prompt_groups/auth.py +++ b/ofscraper/prompts/prompt_groups/auth.py @@ -32,7 +32,7 @@ def auth_prompt(auth) -> dict: "type": "input", "name": "sess", "message": "Enter your sess cookie:", - "default": auth["sess"], + "default": auth["sess"] or "", "validate": EmptyInputValidator(), "multiline": True, }, @@ -40,7 +40,7 @@ def auth_prompt(auth) -> dict: "type": "input", "name": "auth_id", "message": "Enter your auth_id cookie:", - "default": auth["auth_id"], + "default": auth["auth_id"] or "", "validate": EmptyInputValidator(), "multiline": True, }, @@ -48,14 +48,14 @@ def auth_prompt(auth) -> dict: "type": "input", "name": "auth_uid_", "message": "Enter your auth_uid cookie (can be left blank if you don't use 2FA):", - "default": auth["auth_uid_"], + "default": auth["auth_uid_"] or "", "multiline": True, }, { "type": "input", "name": "user_agent", "message": "Enter your `user agent`:", - "default": auth["user_agent"], + "default": auth["user_agent"] or "", "validate": EmptyInputValidator(), "multiline": True, }, @@ -63,7 +63,7 @@ def auth_prompt(auth) -> dict: "type": "input", "name": "x-bc", "message": "Enter your `x-bc` token:", - "default": auth["x-bc"], + "default": auth["x-bc"] or "", "validate": EmptyInputValidator(), "multiline": True, }, @@ -185,7 +185,7 @@ def user_agent_prompt(current): "type": "input", "name": name, "message": "Enter User_Agent from browser", - "default": current, + "default": current or "", "validate": EmptyInputValidator(), "filter": lambda x: prompt_validators.cleanTextInput(x), } @@ -194,7 +194,7 @@ def user_agent_prompt(current): return questions[name] -def xbc_prompt(): +def xbc_prompt(xbc): name = "input" questions = promptClasses.batchConverter( *[ @@ -205,6 +205,7 @@ def xbc_prompt(): "instruction": f"\nGo to browser network tools to view\nFor more instructions visit https://github.com/datawhores/ofscraper\n\n", "validate": EmptyInputValidator(), "filter": lambda x: prompt_validators.cleanTextInput(x), + "default": xbc or "", } ] ) @@ -228,7 +229,7 @@ def auth_full_paste(): } ] ) - return questions[name] + return questions[name]["auth"] def reset_auth_prompt() -> bool: diff --git a/ofscraper/prompts/prompt_groups/config.py b/ofscraper/prompts/prompt_groups/config.py index d48330f76..1a4173197 100644 --- a/ofscraper/prompts/prompt_groups/config.py +++ b/ofscraper/prompts/prompt_groups/config.py @@ -738,8 +738,8 @@ def reset_config_prompt() -> bool: "message": "How do you want to fix this issue", "choices": [ Choice("reset", "Reset Default"), - Choice("manual", "Through script Edit Config"), - Choice("again", "File was fixed manually"), + Choice("manual", "Edit Config manually with script"), + Choice("again", "File was fixed manually via text editor"), ], } ] diff --git a/ofscraper/runner/load.py b/ofscraper/runner/load.py index 8944a2c29..5bb741ae5 100644 --- a/ofscraper/runner/load.py +++ b/ofscraper/runner/load.py @@ -17,8 +17,8 @@ def main(): systemSet() args_loader() setdate() - setLogger() readConfig() + setLogger() make_folder() run.main() except Exception as E: diff --git a/ofscraper/utils/auth.py b/ofscraper/utils/auth.py deleted file mode 100644 index 0842c789e..000000000 --- a/ofscraper/utils/auth.py +++ /dev/null @@ -1,350 +0,0 @@ -r""" - - _______ _______ _______ _______ _______ _______ _______ _______ _______ -( ___ )( ____ \ ( ____ \( ____ \( ____ )( ___ )( ____ )( ____ \( ____ ) -| ( ) || ( \/ | ( \/| ( \/| ( )|| ( ) || ( )|| ( \/| ( )| -| | | || (__ _____ | (_____ | | | (____)|| (___) || (____)|| (__ | (____)| -| | | || __) (_____)(_____ )| | | __)| ___ || _____)| __) | __) -| | | || ( ) || | | (\ ( | ( ) || ( | ( | (\ ( -| (___) || ) /\____) || (____/\| ) \ \__| ) ( || ) | (____/\| ) \ \__ -(_______)|/ \_______)(_______/|/ \__/|/ \||/ (_______/|/ \__/ - -""" - -import hashlib -import json -import logging -import re -import time -from urllib.parse import urlparse - -import browser_cookie3 -import requests -from rich.console import Console -from tenacity import ( - Retrying, - retry, - retry_if_not_exception_type, - stop_after_attempt, - wait_fixed, -) - -import ofscraper.classes.sessionbuilder as sessionbuilder -import ofscraper.prompts.prompts as prompts -import ofscraper.utils.args.read as read_args -import ofscraper.utils.config.data as data -import ofscraper.utils.constants as constants -import ofscraper.utils.paths.common as common_paths -import ofscraper.utils.profiles.data as profiles_data - -console = Console() -log = logging.getLogger("shared") - - -def read_auth(): - authFile = common_paths.get_auth_file() - while True: - try: - with open(authFile, "r") as f: - authText = f.read() - auth = json.loads(authText) - nested_auth = auth["auth"] - for key in nested_auth.keys(): - if key == "auth_uid_": - continue - elif nested_auth[key] == None or nested_auth[key] == "": - console.print("Auth Value not set retriving missing values") - make_auth() - break - value = str(nested_auth[key]).strip() - re.sub("^ +", "", value) - value = re.sub(" +$", "", value) - value = re.sub("\n+", "", value) - nested_auth[key] = value - break - except FileNotFoundError: - console.print("You don't seem to have an `auth.json` file") - make_auth() - except json.JSONDecodeError as e: - print("Your auth.json has a syntax error") - print(f"{e}\n\n") - auth_prompt = prompts.reset_auth_prompt() - if auth_prompt == "manual": - with open(authFile, "w") as f: - f.write(prompts.manual_auth_prompt(authText)) - elif auth_prompt == "reset": - with open(authFile, "w") as f: - f.write(json.dumps(get_empty())) - - make_request_auth() - return auth - - -def get_empty(): - return { - "auth": { - "sess": "", - "auth_id": "", - "auth_uid_": "", - "user_agent": "", - "x-bc": "", - } - } - - -def edit_auth(): - authFile = common_paths.get_auth_file() - log.info(f"Auth Path {authFile}") - try: - with open(authFile, "r") as f: - authText = f.read() - auth = json.loads(authText) - return make_auth(auth) - - console.print("Your `auth.json` file has been edited.") - except FileNotFoundError: - if prompts.ask_make_auth_prompt(): - return make_auth() - except json.JSONDecodeError as e: - while True: - try: - print("Your auth.json has a syntax error") - auth_prompt = prompts.reset_auth_prompt() - if auth_prompt == "reset": - return make_auth() - elif auth_prompt == "manual": - with open(authFile, "w") as f: - f.write(prompts.manual_auth_prompt(authText)) - - with open(authFile, "r") as f: - authText = f.read() - auth = json.loads(authText) - break - except Exception: - continue - - -def authwarning(authFile): - console.print( - "[bold yellow]For an example of how your auth file should look see \ - \n [bold blue]https://of-scraper.gitbook.io/of-scraper/auth#example[/bold blue][/bold yellow]" - ) - console.print( - f"[bold yellow]If you still can't authenticate after editing from script consider manually edit the file at\n[bold blue]{authFile}[/bold blue][/bold yellow]" - ) - - -def make_auth(auth=None): - authFile = common_paths.get_auth_file() - authwarning(authFile) - defaultAuth = get_empty() - - browserSelect = prompts.browser_prompt() - - auth = auth or defaultAuth - if browserSelect in {"quit", "main"}: - return browserSelect - elif ( - browserSelect != "Enter Each Field Manually" - and browserSelect != "Paste From M-rcus' OnlyFans-Cookie-Helper" - ): - temp = requests.utils.dict_from_cookiejar( - getattr(browser_cookie3, browserSelect.lower())(domain_name="onlyfans") - ) - auth = auth or defaultAuth - for key in ["sess", "auth_id", "auth_uid_"]: - auth["auth"][key] = temp.get(key, "") - console.print( - "You'll need to go to onlyfans.com and retrive header information\nGo to https://github.com/datawhores/OF-Scraper and find the section named 'Getting Your Auth Info'\nCookie information has been retived automatically\nSo You only need to retrive the x-bc header and the user-agent", - style="yellow", - ) - if not auth["auth"].get("x-bc"): - auth["auth"]["x-bc"] = prompts.xbc_prompt() - auth["auth"]["user_agent"] = prompts.user_agent_prompt( - auth["auth"].get("user_agent") or "" - ) - - elif browserSelect == "Paste From M-rcus' OnlyFans-Cookie-Helper": - auth = prompts.auth_full_paste() - for key in ["username", "support_2fa", "active", "email", "password", "hashed"]: - auth["auth"].pop(key) - auth["auth"]["x-bc"] = auth["auth"].pop("x_bc").strip() - tempCookie = auth["auth"].pop("cookie") - for ele in tempCookie.split(";"): - if ele.find("auth_id") != -1: - auth["auth"]["auth_id"] = ele.replace("auth_id=", "") - elif ele.find("sess") != -1: - auth["auth"]["sess"] = ele.replace("sess=", "") - elif ele.find("auth_uid") != -1: - auth["auth"]["auth_uid_"] = ele.replace("auth_uid_", "").replace( - "=", "" - ) - - else: - console.print( - "You'll need to go to onlyfans.com and retrive header information\nGo to https://github.com/datawhores/OF-Scraper and find the section named 'Getting Your Auth Info'\nYou only need to retrive the x-bc header,the user-agent, and cookie information", - style="yellow", - ) - auth["auth"].update(prompts.auth_prompt(auth["auth"])) - for key, item in auth["auth"].items(): - newitem = item.strip() - newitem = re.sub("^ +", "", newitem) - newitem = re.sub(" +$", "", newitem) - newitem = re.sub("\n+", "", newitem) - auth["auth"][key] = newitem - console.print(f"{auth}\nWriting to {authFile}", style="yellow") - with open(authFile, "w") as f: - f.write(json.dumps(auth, indent=4)) - - -def make_headers(auth): - headers = { - "accept": "application/json, text/plain, */*", - "app-token": constants.getattr("APP_TOKEN"), - "user-id": auth["auth"]["auth_id"], - "x-bc": auth["auth"]["x-bc"], - "referer": "https://onlyfans.com", - "user-agent": auth["auth"]["user_agent"], - } - return headers - - -def add_cookies(): - authFile = common_paths.get_auth_file() - with open(authFile, "r") as f: - auth = json.load(f) - - cookies = {} - cookies.update({"sess": auth["auth"]["sess"]}) - cookies.update({"auth_id": auth["auth"]["auth_id"]}) - cookies.update({"auth_uid_": auth["auth"]["auth_uid_"] or auth["auth"]["auth_id"]}) - return cookies - - -def get_cookies(): - authFile = common_paths.get_auth_file() - - with open(authFile, "r") as f: - auth = json.load(f) - return f"auth_id={auth['auth']['auth_id']};sess={auth['auth']['sess']};" - - -def create_sign(link, headers): - """ - credit: DC and hippothon - """ - content = read_request_auth() - - time2 = str(round(time.time() * 1000)) - - path = urlparse(link).path - query = urlparse(link).query - path = path if not query else f"{path}?{query}" - - static_param = content["static_param"] - - a = [static_param, time2, path, headers["user-id"]] - msg = "\n".join(a) - - message = msg.encode("utf-8") - hash_object = hashlib.sha1(message, usedforsecurity=False) - sha_1_sign = hash_object.hexdigest() - sha_1_b = sha_1_sign.encode("ascii") - - checksum_indexes = content["checksum_indexes"] - checksum_constant = content["checksum_constant"] - checksum = sum(sha_1_b[i] for i in checksum_indexes) + checksum_constant - - final_sign = content["format"].format(sha_1_sign, abs(checksum)) - - headers.update({"sign": final_sign, "time": time2}) - return headers - - -def read_request_auth() -> dict: - profile = profiles_data.get_active_profile() - - p = common_paths.get_config_home() / profile / constants.getattr("requestAuth") - with open(p, "r") as f: - content = json.load(f) - return content - - -def make_request_auth(): - request_auth = { - "static_param": "", - "format": "", - "checksum_indexes": [], - "checksum_constant": 0, - } - - # *values, = get_request_auth() - result = get_request_auth() - if result: - (*values,) = result - - request_auth.update(zip(request_auth.keys(), values)) - - profile = profiles_data.get_active_profile() - - p = common_paths.get_config_home() / profile - if not p.is_dir(): - p.mkdir(parents=True, exist_ok=True) - - with open(p / constants.getattr("requestAuth"), "w") as f: - f.write(json.dumps(request_auth, indent=4)) - - -def get_request_auth(): - if (read_args.retriveArgs().dynamic_rules or data.get_dynamic() or "deviint") in { - "deviint", - "dv", - "dev", - }: - return get_request_auth_deviint() - else: - return get_request_digitalcriminals() - - -def get_request_auth_deviint(): - with sessionbuilder.sessionBuilder( - backend="httpx", set_header=False, set_cookies=False, set_sign=False - ) as c: - for _ in Retrying( - retry=retry_if_not_exception_type(KeyboardInterrupt), - stop=stop_after_attempt(constants.getattr("NUM_TRIES")), - wait=wait_fixed(8), - ): - with _: - with c.requests(constants.getattr("DEVIINT"))() as r: - if r.ok: - content = r.json_() - static_param = content["static_param"] - fmt = f"{content['start']}:{{}}:{{:x}}:{content['end']}" - checksum_indexes = content["checksum_indexes"] - checksum_constant = content["checksum_constant"] - return (static_param, fmt, checksum_indexes, checksum_constant) - else: - r.raise_for_status() - - -def get_request_digitalcriminals(): - with sessionbuilder.sessionBuilder( - backend="httpx", set_header=False, set_cookies=False, set_sign=False - ) as c: - for _ in Retrying( - retry=retry_if_not_exception_type(KeyboardInterrupt), - stop=stop_after_attempt(constants.getattr("NUM_TRIES")), - wait=wait_fixed(8), - ): - with _: - with c.requests(constants.getattr("DIGITALCRIMINALS"))() as r: - if r.ok: - content = r.json_() - static_param = content["static_param"] - fmt = content["format"] - checksum_indexes = content["checksum_indexes"] - checksum_constant = content["checksum_constant"] - return (static_param, fmt, checksum_indexes, checksum_constant) - else: - r.raise_for_status() diff --git a/ofscraper/utils/auth/context.py b/ofscraper/utils/auth/context.py new file mode 100644 index 000000000..bb3f73cff --- /dev/null +++ b/ofscraper/utils/auth/context.py @@ -0,0 +1,44 @@ +r""" + + _______ _______ _______ _______ _______ _______ _______ _______ _______ +( ___ )( ____ \ ( ____ \( ____ \( ____ )( ___ )( ____ )( ____ \( ____ ) +| ( ) || ( \/ | ( \/| ( \/| ( )|| ( ) || ( )|| ( \/| ( )| +| | | || (__ _____ | (_____ | | | (____)|| (___) || (____)|| (__ | (____)| +| | | || __) (_____)(_____ )| | | __)| ___ || _____)| __) | __) +| | | || ( ) || | | (\ ( | ( ) || ( | ( | (\ ( +| (___) || ) /\____) || (____/\| ) \ \__| ) ( || ) | (____/\| ) \ \__ +(_______)|/ \_______)(_______/|/ \__/|/ \||/ (_______/|/ \__/ + +""" + +import json +import logging +from contextlib import contextmanager + +from rich.console import Console + +import ofscraper.prompts.prompts as prompts +import ofscraper.utils.auth.helpers as helpers +import ofscraper.utils.auth.make as make +import ofscraper.utils.paths.common as common_paths + +console = Console() +log = logging.getLogger("shared") + + +@contextmanager +def auth_context(): + try: + yield + except FileNotFoundError: + console.print("You don't seem to have an `auth.json` file") + make.make_auth() + except json.JSONDecodeError as e: + print("Your auth.json has a syntax error") + print(f"{e}\n\n") + auth_prompt = prompts.reset_auth_prompt() + if auth_prompt == "manual": + f.write(prompts.manual_auth_prompt(helpers.get_auth_string())) + elif auth_prompt == "reset": + with open(common_paths.get_auth_file(), "w") as f: + f.write(json.dumps(helpers.get_empty())) diff --git a/ofscraper/utils/auth/data.py b/ofscraper/utils/auth/data.py new file mode 100644 index 000000000..409e1fe9e --- /dev/null +++ b/ofscraper/utils/auth/data.py @@ -0,0 +1,61 @@ +import re + + +def get_session(auth): + val = None + if auth.get("sess"): + val = auth.get("sess") + cookie = auth.get("auth", {}).get("cookie") or auth.get("cookie") + if cookie: + val = ( + next(filter(lambda x: x.find("sess") != -1, cookie.split(";")), "") or "" + ).replace("sess=", "") + return fix_val(val) + + +def get_id(auth): + val = None + if auth.get("auth_id"): + val = auth.get("auth_id") + cookie = auth.get("auth", {}).get("cookie") or auth.get("cookie") + if cookie: + val = ( + next(filter(lambda x: x.find("auth_id") != -1, cookie.split(";")), "") or "" + ).replace("auth_id=", "") + return fix_val(val) + + +def get_uid(auth): + val = None + if auth.get("auth_uid"): + val = auth.get("auth_uid") + elif auth.get("auth_uid_"): + val = auth.get("auth_uid_") + cookie = auth.get("auth", {}).get("cookie") or auth.get("cookie") + if cookie: + val = ( + next(filter(lambda x: x.find("auth_uid") != -1, cookie.split(";")), "") + or "" + ) + val = val.replace("auth_uid", "") + val = re.sub("[=_]", "", val) + return fix_val(val) + + +def x_bc(auth): + val = auth.get("x_bc") or auth.get("x-bc") or "" + return fix_val(val) + + +def get_user_agent(auth): + val = auth.get("user_agent", "") or "" + return fix_val(val) + + +def fix_val(value): + value = value or "" + value = value.strip() + re.sub("^ +", "", value) + value = re.sub(" +$", "", value) + value = re.sub("\n+", "", value) + return value diff --git a/ofscraper/utils/auth/file.py b/ofscraper/utils/auth/file.py new file mode 100644 index 000000000..e1a0cc536 --- /dev/null +++ b/ofscraper/utils/auth/file.py @@ -0,0 +1,53 @@ +import ofscraper.utils.auth.helpers as helpers + +r""" + + _______ _______ _______ _______ _______ _______ _______ _______ _______ +( ___ )( ____ \ ( ____ \( ____ \( ____ )( ___ )( ____ )( ____ \( ____ ) +| ( ) || ( \/ | ( \/| ( \/| ( )|| ( ) || ( )|| ( \/| ( )| +| | | || (__ _____ | (_____ | | | (____)|| (___) || (____)|| (__ | (____)| +| | | || __) (_____)(_____ )| | | __)| ___ || _____)| __) | __) +| | | || ( ) || | | (\ ( | ( ) || ( | ( | (\ ( +| (___) || ) /\____) || (____/\| ) \ \__| ) ( || ) | (____/\| ) \ \__ +(_______)|/ \_______)(_______/|/ \__/|/ \||/ (_______/|/ \__/ + +""" +import json + +from rich.console import Console + +import ofscraper.utils.auth.context as auth_context +import ofscraper.utils.auth.make as make +import ofscraper.utils.auth.request as request_auth +import ofscraper.utils.auth.schema as auth_schema +import ofscraper.utils.paths.common as common_paths + +console = Console() + + +@auth_context.auth_context() +def read_auth(): + old_auth = helpers.get_auth_dict() + auth = auth_schema.auth_schema(old_auth) + if auth_schema.auth_key_missing(old_auth): + auth = write_auth(auth) + if auth_schema.auth_key_null(auth): + auth = make.make_auth(auth) + request_auth.make_request_auth() + return auth + + +@auth_context.auth_context() +def edit_auth(): + auth = helpers.get_auth_dict() + auth = make.make_auth(auth) + console.print("Your `auth.json` file has been edited.") + return auth + + +def write_auth(auth): + if isinstance(auth, dict): + auth = json.dumps(auth, indent=4) + with open(common_paths.get_auth_file(), "w") as f: + f.write(auth) + return helpers.get_auth_dict(auth) diff --git a/ofscraper/utils/auth/helpers.py b/ofscraper/utils/auth/helpers.py new file mode 100644 index 000000000..6433eb2aa --- /dev/null +++ b/ofscraper/utils/auth/helpers.py @@ -0,0 +1,76 @@ +r""" + + _______ _______ _______ _______ _______ _______ _______ _______ _______ +( ___ )( ____ \ ( ____ \( ____ \( ____ )( ___ )( ____ )( ____ \( ____ ) +| ( ) || ( \/ | ( \/| ( \/| ( )|| ( ) || ( )|| ( \/| ( )| +| | | || (__ _____ | (_____ | | | (____)|| (___) || (____)|| (__ | (____)| +| | | || __) (_____)(_____ )| | | __)| ___ || _____)| __) | __) +| | | || ( ) || | | (\ ( | ( ) || ( | ( | (\ ( +| (___) || ) /\____) || (____/\| ) \ \__| ) ( || ) | (____/\| ) \ \__ +(_______)|/ \_______)(_______/|/ \__/|/ \||/ (_______/|/ \__/ + +""" +import json +import logging + +import browser_cookie3 +import requests +from rich.console import Console + +import ofscraper.prompts.prompts as prompts +import ofscraper.utils.paths.common as common_paths + +console = Console() +log = logging.getLogger("shared") + + +def get_auth_dict(authStr=None): + auth = json.loads(authStr or get_auth_string()) + if "auth" in auth: + return auth.get("auth") + return auth + + +def get_auth_string(): + authFile = common_paths.get_auth_file() + with open(authFile, "r") as f: + authText = f.read() + return authText + + +def get_empty(): + return { + "sess": "", + "auth_id": "", + "auth_uid": "", + "user_agent": "", + "x-bc": "", + } + + +def authwarning(authFile): + console.print( + "[bold yellow]For an example of how your auth file should look see \ + \n [bold blue]https://of-scraper.gitbook.io/of-scraper/auth#example[/bold blue][/bold yellow]" + ) + console.print( + f"[bold yellow]If you still can't authenticate after editing from script consider manually edit the file at\n[bold blue]{authFile}[/bold blue][/bold yellow]" + ) + + +def browser_cookie_helper(auth, browserSelect): + temp = requests.utils.dict_from_cookiejar( + getattr(browser_cookie3, browserSelect.lower())(domain_name="onlyfans") + ) + for key in ["sess", "auth_id", "auth_uid_"]: + auth[key] = auth[key] or temp.get(key, "") + console.print( + "You'll need to go to onlyfans.com and retrive/update header information\nGo to https://github.com/datawhores/OF-Scraper and find the section named 'Getting Your Auth Info'\nCookie information has been retived automatically\nSo You only need to retrive the x-bc header and the user-agent", + style="yellow", + ) + auth["x-bc"] = prompts.xbc_prompt(auth.get("x-bc")) + auth["user_agent"] = prompts.user_agent_prompt(auth.get("user_agent")) + + +def cookie_helper(): + return prompts.auth_full_paste() diff --git a/ofscraper/utils/auth/make.py b/ofscraper/utils/auth/make.py new file mode 100644 index 000000000..84c37dd29 --- /dev/null +++ b/ofscraper/utils/auth/make.py @@ -0,0 +1,50 @@ +import json +import logging +import re +from contextlib import contextmanager +from urllib.parse import urlparse + +from rich.console import Console + +import ofscraper.prompts.prompts as prompts +import ofscraper.utils.auth.helpers as helpers +import ofscraper.utils.auth.schema as auth_schema +import ofscraper.utils.paths.common as common_paths + +console = Console() + + +def make_auth(auth=None): + helpers.authwarning(common_paths.get_auth_file()) + browserSelect = prompts.browser_prompt() + + auth = auth or helpers.get_empty() + if browserSelect in {"quit", "main"}: + return browserSelect + elif ( + browserSelect != "Enter Each Field Manually" + and browserSelect != "Paste From M-rcus' OnlyFans-Cookie-Helper" + ): + auth = helpers.browser_cookie_helper(auth, browserSelect) + + elif browserSelect == "Paste From M-rcus' OnlyFans-Cookie-Helper": + auth = helpers.cookie_helper(auth) + + else: + console.print( + "You'll need to go to onlyfans.com and retrive/update header information\nGo to https://github.com/datawhores/OF-Scraper and find the section named 'Getting Your Auth Info'\nYou only need to retrive the x-bc header,the user-agent, and cookie information", + style="yellow", + ) + auth.update(prompts.auth_prompt(auth)) + for key, item in auth.items(): + newitem = item.strip() + newitem = re.sub("^ +", "", newitem) + newitem = re.sub(" +$", "", newitem) + newitem = re.sub("\n+", "", newitem) + auth[key] = newitem + authFile = common_paths.get_auth_file() + console.print(f"{auth}\nWriting to {authFile}", style="yellow") + auth = auth_schema.auth_schema(auth) + with open(authFile, "w") as f: + f.write(json.dumps(auth, indent=4)) + return auth diff --git a/ofscraper/utils/auth/request.py b/ofscraper/utils/auth/request.py new file mode 100644 index 000000000..1c358b48e --- /dev/null +++ b/ofscraper/utils/auth/request.py @@ -0,0 +1,186 @@ +r""" + + _______ _______ _______ _______ _______ _______ _______ _______ _______ +( ___ )( ____ \ ( ____ \( ____ \( ____ )( ___ )( ____ )( ____ \( ____ ) +| ( ) || ( \/ | ( \/| ( \/| ( )|| ( ) || ( )|| ( \/| ( )| +| | | || (__ _____ | (_____ | | | (____)|| (___) || (____)|| (__ | (____)| +| | | || __) (_____)(_____ )| | | __)| ___ || _____)| __) | __) +| | | || ( ) || | | (\ ( | ( ) || ( | ( | (\ ( +| (___) || ) /\____) || (____/\| ) \ \__| ) ( || ) | (____/\| ) \ \__ +(_______)|/ \_______)(_______/|/ \__/|/ \||/ (_______/|/ \__/ + +""" + +import hashlib +import json +import time +from contextlib import contextmanager +from urllib.parse import urlparse + +from tenacity import ( + Retrying, + retry, + retry_if_not_exception_type, + stop_after_attempt, + wait_fixed, +) + +import ofscraper.classes.sessionbuilder as sessionbuilder +import ofscraper.utils.args.read as read_args +import ofscraper.utils.config.data as data +import ofscraper.utils.constants as constants +import ofscraper.utils.paths.common as common_paths +import ofscraper.utils.profiles.data as profiles_data + + +def make_request_auth(): + request_auth = { + "static_param": "", + "format": "", + "checksum_indexes": [], + "checksum_constant": 0, + } + + # *values, = get_request_auth() + result = get_request_auth() + if result: + (*values,) = result + + request_auth.update(zip(request_auth.keys(), values)) + + profile = profiles_data.get_active_profile() + + p = common_paths.get_config_home() / profile + if not p.is_dir(): + p.mkdir(parents=True, exist_ok=True) + + with open(p / constants.getattr("requestAuth"), "w") as f: + f.write(json.dumps(request_auth, indent=4)) + + +def get_request_auth(): + if (read_args.retriveArgs().dynamic_rules or data.get_dynamic() or "deviint") in { + "deviint", + "dv", + "dev", + }: + return get_request_auth_deviint() + else: + return get_request_digitalcriminals() + + +def get_request_auth_deviint(): + with sessionbuilder.sessionBuilder( + backend="httpx", set_header=False, set_cookies=False, set_sign=False + ) as c: + for _ in Retrying( + retry=retry_if_not_exception_type(KeyboardInterrupt), + stop=stop_after_attempt(constants.getattr("NUM_TRIES")), + wait=wait_fixed(8), + ): + with _: + with c.requests(constants.getattr("DEVIINT"))() as r: + if r.ok: + content = r.json_() + static_param = content["static_param"] + fmt = f"{content['start']}:{{}}:{{:x}}:{content['end']}" + checksum_indexes = content["checksum_indexes"] + checksum_constant = content["checksum_constant"] + return (static_param, fmt, checksum_indexes, checksum_constant) + else: + r.raise_for_status() + + +def get_request_digitalcriminals(): + with sessionbuilder.sessionBuilder( + backend="httpx", set_header=False, set_cookies=False, set_sign=False + ) as c: + for _ in Retrying( + retry=retry_if_not_exception_type(KeyboardInterrupt), + stop=stop_after_attempt(constants.getattr("NUM_TRIES")), + wait=wait_fixed(8), + ): + with _: + with c.requests(constants.getattr("DIGITALCRIMINALS"))() as r: + if r.ok: + content = r.json_() + static_param = content["static_param"] + fmt = content["format"] + checksum_indexes = content["checksum_indexes"] + checksum_constant = content["checksum_constant"] + return (static_param, fmt, checksum_indexes, checksum_constant) + else: + r.raise_for_status() + + +def make_headers(auth): + headers = { + "accept": "application/json, text/plain, */*", + "app-token": constants.getattr("APP_TOKEN"), + "user-id": auth["auth_id"], + "x-bc": auth["x-bc"], + "referer": "https://onlyfans.com", + "user-agent": auth["user_agent"], + } + return headers + + +def add_cookies(): + authFile = common_paths.get_auth_file() + with open(authFile, "r") as f: + auth = json.load(f) + + cookies = {} + cookies.update({"sess": auth["sess"]}) + cookies.update({"auth_id": auth["auth_id"]}) + cookies.update({"auth_uid": auth["auth_uid"] or auth["auth_id"]}) + return cookies + + +def get_cookies(): + authFile = common_paths.get_auth_file() + + with open(authFile, "r") as f: + auth = json.load(f) + return f"auth_id={auth['auth']['auth_id']};sess={auth['auth']['sess']};" + + +def create_sign(link, headers): + """ + credit: DC and hippothon + """ + content = read_request_auth() + + time2 = str(round(time.time() * 1000)) + + path = urlparse(link).path + query = urlparse(link).query + path = path if not query else f"{path}?{query}" + + static_param = content["static_param"] + + a = [static_param, time2, path, headers["user-id"]] + msg = "\n".join(a) + + message = msg.encode("utf-8") + hash_object = hashlib.sha1(message, usedforsecurity=False) + sha_1_sign = hash_object.hexdigest() + sha_1_b = sha_1_sign.encode("ascii") + + checksum_indexes = content["checksum_indexes"] + checksum_constant = content["checksum_constant"] + checksum = sum(sha_1_b[i] for i in checksum_indexes) + checksum_constant + + final_sign = content["format"].format(sha_1_sign, abs(checksum)) + + headers.update({"sign": final_sign, "time": time2}) + return headers + + +def read_request_auth() -> dict: + profile = profiles_data.get_active_profile() + + p = common_paths.get_config_home() / profile / constants.getattr("requestAuth") + with open(p, "r") as f: + content = json.load(f) + return content diff --git a/ofscraper/utils/auth/schema.py b/ofscraper/utils/auth/schema.py new file mode 100644 index 000000000..638a07b02 --- /dev/null +++ b/ofscraper/utils/auth/schema.py @@ -0,0 +1,34 @@ +import ofscraper.utils.auth.data as auth_data + + +def auth_schema(auth): + return { + "sess": auth_data.get_session(auth), + "auth_id": auth_data.get_id(auth), + "auth_uid": auth_data.get_uid(auth), + "user_agent": auth_data.get_user_agent(auth), + "x-bc": auth_data.x_bc(auth), + } + + +def auth_key_missing(auth): + if "auth" in auth: + auth = auth["auth"] + for key in [ + "x-bc", + "user_agent", + "auth_id", + "sess", + ]: + if auth.get(key) == None: + return True + return False + + +def auth_key_null(auth): + if "auth" in auth: + auth = auth["auth"] + for key in ["x-bc", "user_agent", "auth_id", "sess"]: + if auth.get(key) == None or auth.get(key) == "": + return True + return False diff --git a/ofscraper/utils/checkers.py b/ofscraper/utils/checkers.py index ebcec8f62..216a75dc2 100644 --- a/ofscraper/utils/checkers.py +++ b/ofscraper/utils/checkers.py @@ -2,7 +2,8 @@ from contextlib import contextmanager import ofscraper.api.init as init -import ofscraper.utils.auth as auth +import ofscraper.utils.auth.file as auth_file +import ofscraper.utils.auth.make as make import ofscraper.utils.config.config as config_ import ofscraper.utils.config.data as data import ofscraper.utils.console as console @@ -17,7 +18,7 @@ def check_auth(): status = init.getstatus() if status == "DOWN": log.warning("Auth Failed") - auth.make_auth(auth=auth.read_auth()) + make.make_auth(auth=auth_file.read_auth()) continue break diff --git a/ofscraper/utils/config/config.py b/ofscraper/utils/config/config.py index 98a2c0943..e6fcbc31b 100644 --- a/ofscraper/utils/config/config.py +++ b/ofscraper/utils/config/config.py @@ -26,13 +26,14 @@ def read_config(update=True): - with config_context.config_context(): - config = config_file.open_config() - if update and schema.config_diff(config): - config = config_file.auto_update_config(config) - if config.get("config"): - config = config["config"] - return config + while True: + with config_context.config_context(): + config = config_file.open_config() + if update and schema.config_diff(config): + config = config_file.auto_update_config(config) + if config.get("config"): + config = config["config"] + return config def update_config(field: str, value): diff --git a/ofscraper/utils/config/context.py b/ofscraper/utils/config/context.py index 9f09a56ae..b558170d4 100644 --- a/ofscraper/utils/config/context.py +++ b/ofscraper/utils/config/context.py @@ -15,7 +15,10 @@ def config_context(): configStr = None while True: try: - print("You config.json has a syntax error") + print("Your config.json has a syntax error") + import traceback + + print(traceback.format_exc()) print(f"{e}\n\n") config_prompt = prompts.reset_config_prompt() if config_prompt == "manual": @@ -23,7 +26,7 @@ def config_context(): configStr or config_file.config_string() ) config_file.write_config(configStr) - config = config_file.open_config() + config_file.open_config() elif config_prompt == "reset": config_file.make_config_original() break diff --git a/ofscraper/utils/config/custom.py b/ofscraper/utils/config/custom.py index 4f6afd2dd..a2557992f 100644 --- a/ofscraper/utils/config/custom.py +++ b/ofscraper/utils/config/custom.py @@ -1,20 +1,18 @@ import json -import ofscraper.utils.config.context as config_context import ofscraper.utils.config.file as config_file def get_custom(config=None): - with config_context.config_context(): - if config == False: + if config == False: + return None + config = config or config_file.open_config() + value = config.get("custom") or config.get("advanced_options", {}).get( + "custom_values" + ) + if isinstance(value, str): + try: + return json.loads(value) + except json.JSONDecodeError: return None - config = config or config_file.open_config() - value = config.get("custom") or config.get("advanced_options", {}).get( - "custom_values" - ) - if isinstance(value, str): - try: - return json.loads(value) - except json.JSONDecodeError: - return None - return value + return value diff --git a/ofscraper/utils/config/menu.py b/ofscraper/utils/config/menu.py index 52f6e26f2..c65e6c234 100644 --- a/ofscraper/utils/config/menu.py +++ b/ofscraper/utils/config/menu.py @@ -22,53 +22,53 @@ def download_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.download_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def file_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.file_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def binary_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.binary_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def cdm_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.cdm_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def performance_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.performance_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def general_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.general_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def advanced_config(): with config_context.config_context(): config_file.open_config() updated_config = prompts.advanced_config() - update_config_helper(updated_config) + update_config_helper(updated_config) def response_type(): with config_context.config_context(): config_file.open_config() updated_config = prompts.response_type() - update_config_helper(updated_config) + update_config_helper(updated_config) diff --git a/ofscraper/utils/logs/other.py b/ofscraper/utils/logs/other.py index d3aa968b0..7e10e1de0 100644 --- a/ofscraper/utils/logs/other.py +++ b/ofscraper/utils/logs/other.py @@ -34,7 +34,7 @@ def logger_other(input_, name=None, stop_count=1, event=None): if event and event.is_set(): return True try: - messages = funct(timeout=constants.getattr("LOGGER_TIMEOUT")) + messages = funct() except: continue if not isinstance(messages, list): diff --git a/ofscraper/utils/menu.py b/ofscraper/utils/menu.py index 6afa2bf8d..9faf990da 100644 --- a/ofscraper/utils/menu.py +++ b/ofscraper/utils/menu.py @@ -5,7 +5,7 @@ import ofscraper.models.selector as userselector import ofscraper.prompts.prompts as prompts import ofscraper.utils.actions as actions -import ofscraper.utils.auth as auth +import ofscraper.utils.auth.file as auth_file import ofscraper.utils.checkers as checkers import ofscraper.utils.config.menu as config_menu import ofscraper.utils.profiles.manage as profiles_manage @@ -45,7 +45,7 @@ def main_menu_action(): count = count + 1 elif result_main_prompt == "auth": # Edit `auth.json` file - auth_result_prompt = auth.edit_auth() + auth_result_prompt = auth_file.edit_auth() if auth_result_prompt == "quit": return True elif result_main_prompt == "config":