From 01d64b906184b4e673599e3c67f64256510e6757 Mon Sep 17 00:00:00 2001 From: Tibor Bizjak Date: Tue, 14 Mar 2023 18:02:48 +0100 Subject: [PATCH] Added code to download user input by session key --- main.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++---- scrapper.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 scrapper.py diff --git a/main.py b/main.py index 3ad5337..58a590a 100755 --- a/main.py +++ b/main.py @@ -1,5 +1,7 @@ -#!/usr/bin/env python3 +#!/usr/bin/env pypy3 +from scrapper import AdventSession +from getpass import getpass import importlib import os import re @@ -8,6 +10,7 @@ import traceback from argparse import ArgumentParser +YEAR = 2019 PATH = pathlib.Path(__file__).parent INPUT_PATH = PATH / 'input' @@ -96,19 +99,22 @@ parser.add_argument('-d', '--day', metavar='DAY[-DAY]', help='solve puzzle of day DAY or of range DAY-DAY') -parser.add_argument('-i', '--input', +group = parser.add_mutually_exclusive_group() +group.add_argument('-i', '--input', action='store', type=str, metavar='INPUT', default=INPUT_PATH, help='use INPUT as puzzle input') - - -parser.add_argument('-t', '--test', +group.add_argument('-t', '--test', action='store_true', help="run tests") +group.add_argument('--fetch-session', + action='store_true', + help='fetch puzzle inputs and solutions for session key') + def parse_day_arg(s, mind, maxd): """Parses day argument. Returns a list of days.""" split = s.split('-') @@ -158,6 +164,65 @@ def main(): day = days[0] inputs[day] = args.input + #Fetch inputs of session + if args.fetch_session: + answer = input('Fetch puzzle input and solutions for session key?\n' + + 'The session key will not be stored. [Y/n]: ') + if 'n' in answer.lower(): + return + key = getpass('Session key: ') + sess = AdventSession(key) + anon_pattern = re.compile(r'\(anonymous user #(\d+)\)') + user = sess.get_user() + print('Fetching inputs for {}'.format(user)) + + anon_match = anon_pattern.fullmatch(user) + if anon_match == None: + dir_name = 'user_{}'.format(user.replace(' ', '-')) + else: + dir_name = 'anon_{}'.format(anon_match.group(1)) + + if not os.path.exists(dir_name): + os.makedirs(dir_name) + mssg = 'Choose directory name or press ENTER for [{}]: '.format(dir_name) + r = input(mssg) + if r != '': + dir_name = r + path = PATH / pathlib.Path(dir_name) + sol_fn = 'solutions' + #Create / overwrite solution file + open(path / sol_fn, 'a').close() + with open(path / sol_fn, 'w') as f: + f.write('') + + input_format = 'day{0:0>2}.txt' + for d in days: + print('[{}{}] {}/25'.format('#'*d, '.'*(25-d), d), end='\r') + try: + puzzle_input = sess.get_input(YEAR, d) + except e: + print('warning : failed to fetch input for day {}'.format(d)) + print(e) + continue + #Create file if it doesn't exist + fn = path / input_format.format(d) + open(fn, 'a').close() + with open(fn, 'w') as f: + f.write(puzzle_input) + + try: + solutions = sess.get_solutions(YEAR, d) + except e: + print('warning : failed to fetch solutions for day {}'.format(d)) + print(e) + continue + + with open(path / sol_fn, 'a') as f: + f.write(' '.join(solutions) + '\n') + + print('\nDone') + return + #Solve days for d in days: print("=" * 6 + " Day {} ".format(d) + "=" * 6) diff --git a/scrapper.py b/scrapper.py new file mode 100644 index 0000000..013ce7c --- /dev/null +++ b/scrapper.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +from urllib import request +import gzip +import re + +host = 'https://adventofcode.com/' +sol_path = '{year}/day/{day}' +input_path = sol_path + '/input' + +sol_pattern = re.compile(r'Your puzzle answer was (.*?)') +user_pattern = re.compile(r'
(.*?)
') + + +class BadRequest(Exception): + pass + +class AdventSession(object): + def __init__(self, session_key): + head = 'session=' + if session_key.startswith(head): + session = session_key + else: + session = head + session_key + self.headers = {'Cookie' : session, + 'Accept-Encoding' : 'gzip'} + + def get(self, url): + req = request.Request(url, headers=self.headers) + res = request.urlopen(req) + text = gzip.decompress(res.read()).decode('utf-8') + if res.status != 200: + raise BadRequest(text) + return text + + def get_user(self): + text = self.get(host) + return user_pattern.search(text).group(1) + + def get_input(self, year, day): + text = self.get(host + input_path.format(year=year, day=day)) + return text + + def get_solutions(self, year, day): + text = self.get(host + sol_path.format(year=year, day=day)) + return sol_pattern.findall(text) + +