Added code to download user input by session key

master
Tibor Bizjak 2023-03-14 18:02:48 +01:00
parent 699631a3c1
commit 01d64b9061
2 changed files with 118 additions and 5 deletions

75
main.py
View File

@ -1,5 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env pypy3
from scrapper import AdventSession
from getpass import getpass
import importlib import importlib
import os import os
import re import re
@ -8,6 +10,7 @@ import traceback
from argparse import ArgumentParser from argparse import ArgumentParser
YEAR = 2019
PATH = pathlib.Path(__file__).parent PATH = pathlib.Path(__file__).parent
INPUT_PATH = PATH / 'input' INPUT_PATH = PATH / 'input'
@ -96,19 +99,22 @@ parser.add_argument('-d', '--day',
metavar='DAY[-DAY]', metavar='DAY[-DAY]',
help='solve puzzle of day DAY or of range 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', action='store',
type=str, type=str,
metavar='INPUT', metavar='INPUT',
default=INPUT_PATH, default=INPUT_PATH,
help='use INPUT as puzzle input') help='use INPUT as puzzle input')
group.add_argument('-t', '--test',
parser.add_argument('-t', '--test',
action='store_true', action='store_true',
help="run tests") 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): def parse_day_arg(s, mind, maxd):
"""Parses day argument. Returns a list of days.""" """Parses day argument. Returns a list of days."""
split = s.split('-') split = s.split('-')
@ -158,6 +164,65 @@ def main():
day = days[0] day = days[0]
inputs[day] = args.input 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 #Solve days
for d in days: for d in days:
print("=" * 6 + " Day {} ".format(d) + "=" * 6) print("=" * 6 + " Day {} ".format(d) + "=" * 6)

48
scrapper.py 100644
View File

@ -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 <code>(.*?)</code>')
user_pattern = re.compile(r'<div class="user">(.*?)</div>')
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)