diff --git a/.github/workflows/get-inputs.yml b/.github/workflows/get-inputs.yml new file mode 100644 index 00000000..cdc19c52 --- /dev/null +++ b/.github/workflows/get-inputs.yml @@ -0,0 +1,49 @@ +name: Get inputs + +on: + workflow_call: + inputs: + year: + required: false + type: number + secrets: + SESSION: + required: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Get Day + id: get-day + run: | + from datetime import datetime, timedelta, timezone + import os + tz = timezone(timedelta(hours=-5)) + year = ${{ inputs.year }} or 2024 + day = max(0, min(25, (datetime.now(tz) - datetime(year, 12, 1, 0, 0, 0, 0, tz)).days + 1)) + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + print("year=" + str(year), file=f) + print("day=" + str(min(25, max(0, day))), file=f) + print("days=" + ' '.join(map(str, range(1, day + 1))), file=f) + shell: python + - id: cache + uses: actions/cache@v4 + with: + key: inputs-${{ steps.get-day.outputs.day }} + restore-keys: inputs- + path: day*.txt + - name: Download inputs + if: steps.cache.outputs.cache-hit != 'true' + run: | + for day in ${{ steps.get-day.outputs.days }}; do + [[ -e day$day.txt ]] || curl -A https://github.com/ephemient/aoc2024/blob/main/.github/workflows/get-inputs.yml -b session=$SESSION -o day$day.txt -f https://adventofcode.com/${{ steps.get-day.outputs.year }}/day/$day/input + done + shell: bash --noprofile --norc -euxo pipefail {0} + env: + SESSION: ${{ secrets.SESSION }} + - uses: actions/upload-artifact@v4 + with: + name: inputs + path: day*.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a126557c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/day*.txt diff --git a/README.md b/README.md new file mode 100644 index 00000000..012db731 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# [Advent of Code 2024](https://adventofcode.com/2024) +### my answers diff --git a/get-inputs b/get-inputs new file mode 100755 index 00000000..f57a76ec --- /dev/null +++ b/get-inputs @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import argparse +from datetime import datetime, timedelta, timezone +from pathlib import Path +import shutil +import sys +import time +from urllib.error import HTTPError +from urllib.request import urlopen, Request + +tz = timezone(timedelta(hours=-5)) + + +def main(): + terminal_output = sys.stdout if sys.stdout.isatty( + ) else sys.stderr if sys.stderr.isatty() else None + + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group() + group.add_argument("-n", "--dry-run", action='store_true') + group.add_argument("-o", "--overwrite", action='store_true', dest='force') + group = parser.add_mutually_exclusive_group() + group.add_argument("-s", "--session") + group.add_argument("-S", + "--session-file", + metavar='FILE', + type=argparse.FileType()) + parser.add_argument("-y", "--year", type=int, default=2024) + parser.add_argument("days", metavar='DAY', type=int, nargs='*') + args = parser.parse_args() + + dry_run, force, session = args.dry_run, args.force, args.session + if not session: + session_file = args.session_file + if not session_file: + session_file = open(Path.home() / '.aocrc') + with session_file: + session = session_file.read().strip() + + year, days = args.year, args.days + base = datetime(year, 12, 1, 0, 0, 0, 0, tz) + if days: + days = sorted(set(days)) + else: + days = (datetime.now(tz) - base).days + 1 + days = range(1, max(0, min(25, days)) + 1) + + for day in days: + file = f"day{day}.txt" + if not force and Path(file).exists(): + print(f"{file} already exists") + continue + if not dry_run: + target = base + timedelta(days=day - 1) + while True: + now = datetime.now(tz) + if now >= target: + break + delta = target - now + message = f"{file} available in {delta}" + if terminal_output: + print(message, end='', file=terminal_output, flush=True) + if delta > timedelta(hours=2): + delta = delta % timedelta(hours=1) + elif delta > timedelta(minutes=2): + delta = delta % timedelta(minutes=1) + elif delta > timedelta(seconds=2): + delta = delta % timedelta(seconds=1) + elif delta > timedelta(milliseconds=20): + delta = delta % timedelta(milliseconds=10) + time.sleep(delta.total_seconds()) + print('\033[2K\r', + end='', + file=terminal_output, + flush=True) + else: + print(message) + time.sleep(delta.total_seconds()) + url = f"https://www.adventofcode.com/{year}/day/{day}/input" + print(f"{file} = {url}") + if dry_run: + continue + request = Request( + url, + headers={ + "User-Agent": + "https://github.com/ephemient/aoc2024/blob/main/get-inputs", + "Cookie": f"session={session}" + }) + response = urlopen(request) + if response.status != 200: + raise HTTPError(response.url, response.code, response.msg, + response.headers, None) + with response: + with open(file, 'wb') as f: + shutil.copyfileobj(response, f) + + +if __name__ == "__main__": + main()