import argparse import glob import json import logging import os import sys from termcolor import cprint from aocd.exceptions import DeadTokenError from aocd.models import AOCD_CONFIG_DIR from aocd.utils import _ensure_intermediate_dirs from aocd.utils import get_owner log = logging.getLogger(__name__) def get_working_tokens(): log.debug("checking for installation of browser-cookie3 package") try: import browser_cookie3 as bc3 # soft dependency except ImportError: sys.exit("To use this feature you must pip install browser-cookie3") log.info("checking browser cookies storage for auth tokens, this might pop up an auth dialog!") log.info("checking chrome cookie jar...") cookie_files = glob.glob(os.path.expanduser("~/.config/google-chrome/*/Cookies")) + [None] chrome_cookies = [] for cookie_file in cookie_files: try: chrome = bc3.chrome(cookie_file=cookie_file, domain_name=".adventofcode.com") except Exception as err: log.debug("Couldn't scrape chrome - %s: %s", type(err), err) else: chrome_cookies += [c for c in chrome if c.name == "session"] log.info("%d candidates from chrome", len(chrome_cookies)) chrome = chrome_cookies log.info("checking firefox cookie jar...") try: firefox = bc3.firefox(domain_name=".adventofcode.com") except Exception as err: log.debug("Couldn't scrape firefox - %s: %s", type(err), err) firefox = [] else: firefox = [c for c in firefox if c.name == "session"] log.info("%d candidates from firefox", len(firefox)) # order preserving de-dupe tokens = list({}.fromkeys([c.value for c in chrome + firefox])) removed = len(chrome + firefox) - len(tokens) if removed: log.info("Removed %d duplicate%s", removed, "s"[:removed-1]) result = {} # map of {token: auth source} for token in tokens: try: owner = get_owner(token) except DeadTokenError: pass else: result[token] = owner return result def scrape_session_tokens(): aocd_token_file = os.path.join(AOCD_CONFIG_DIR, "token") aocd_tokens_file = os.path.join(AOCD_CONFIG_DIR, "tokens.json") parser = argparse.ArgumentParser(description="Scrapes AoC session tokens from your browser's cookie storage") parser.add_argument("-v", "--verbose", action="count", help="increased logging (may be specified multiple)") parser.add_argument("-c", "--check", nargs="?", help="check existing token(s) and exit", const=True) args = parser.parse_args() if args.verbose is None: log_level = logging.WARNING elif args.verbose == 1: log_level = logging.INFO else: log_level = logging.DEBUG logging.basicConfig(level=log_level) log.debug("called with %r", args) if args.check is not None: if args.check is True: tokens = {} if os.environ.get("AOC_SESSION"): tokens["AOC_SESSION"] = os.environ["AOC_SESSION"] if os.path.isfile(aocd_token_file): with open(aocd_token_file) as f: txt = f.read().strip() if txt: tokens[aocd_token_file] = txt.split()[0] if os.path.isfile(aocd_tokens_file): with open(aocd_tokens_file) as f: tokens.update(json.load(f)) else: tokens = {"CLI": args.check} if not tokens: sys.exit("no existing tokens found") log.debug("%d tokens to check", len(tokens)) for name, token in tokens.items(): try: owner = get_owner(token) except DeadTokenError: cprint("{} ({}) is dead".format(token, name), color="red") else: print("{} ({}) is alive".format(token, name)) if name != owner: log.info("{} ({}) is owned by {}".format(token, name, owner)) sys.exit(0) working = get_working_tokens() if not working: sys.exit("could not find any working tokens in browser cookies, sorry :(") log.debug("found %d live tokens", len(working)) for cookie in working.items(): print("%s <- %s" % cookie) if "AOC_SESSION" not in os.environ: if not os.path.isfile(aocd_token_file): if len(working) == 1: [(token, auth_source)] = working.items() _ensure_intermediate_dirs(aocd_token_file) with open(aocd_token_file, "w") as f: f.write(token) log.info("wrote %s session to %s", auth_source, aocd_token_file)