├── .github └── workflows │ └── test.yml ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml ├── aoc-badges.py └── entrypoint.sh /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Test 4 | 5 | on: workflow_dispatch 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: joblo2213/aoc-badges-action@master 13 | with: 14 | userid: 658601 15 | session: ${{ secrets.AOC_SESSION }} 16 | - uses: stefanzweifel/git-auto-commit-action@v4 17 | with: 18 | commit_message: Update badges 19 | file_pattern: README.md 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | RUN pip install requests 4 | RUN pip install pytz 5 | 6 | 7 | ADD entrypoint.sh /entrypoint.sh 8 | ADD aoc-badges.py /aoc-badges.py 9 | 10 | RUN chmod +x /entrypoint.sh 11 | ENTRYPOINT ["/entrypoint.sh"] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 joblo2213 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AoC-badges 2 | Github Action to update the [badges](https://github.com/badges/shields) of your Readme to show your current stats for [Advent of Code](https://adventofcode.com/). 3 | 4 | Have a look at these examples: 5 | 6 | ![](https://img.shields.io/badge/day%20📅-22-blue) 7 | ![](https://img.shields.io/badge/stars%20⭐-34-yellow) 8 | 9 | ## Setup 10 | First of all you have to add the badges to your README. 11 | You can use only some of them or even customize them to fit your needs. 12 | Note that if you want to customize the badges you might have to tweak the regular expressions used to match your badges. 13 | Here are the default ones: 14 | 15 | | Badge | Raw Badge | Description | 16 | |----------------------------------------------------------|-----------------------------------------------------------|--------------------------------------------------------| 17 | | ![](https://img.shields.io/badge/day%20📅-22-blue) | `![](https://img.shields.io/badge/day%20📅-22-blue)` | Displays the current calendar day | 18 | | ![](https://img.shields.io/badge/stars%20⭐-34-yellow) | `![](https://img.shields.io/badge/stars%20⭐-34-yellow)` | Displays the total amount of collected stars | 19 | | ![](https://img.shields.io/badge/days%20completed-17-red) | `![](https://img.shields.io/badge/days%20completed-17-red)` | Displyas on how many days you completed all tasks | 20 | 21 | The default values for the regular expressions can be found in the [`actions.yml`](https://github.com/joblo2213/AoC-badges/blob/master/action.yml) 22 | if you need to tweak them. 23 | 24 | The action will search through your readme and updates all badges it finds using the provided (or default) regular expressions. 25 | 26 | Next you have to obtain your session and your user id. 27 | Go to [Advent of Code leaderboards](https://adventofcode.com/2020/leaderboard/private) and click on `[Create]` and then on `[View]` to visit your private leaderboard. 28 | Then have a look at the url, the numbers at the end are your user id: 29 | 30 | ``` 31 | https://adventofcode.com/2020/leaderboard/private/view/ 32 | ``` 33 | 34 | To get your session secret press F12 while you are logged in on [adventofcode.com](https://adventofcode.com/) to open the developer tools of your browser. 35 | Then open the `Application` Tab on Chromium Browsers or `Storage` on firefox. There you can have a look at your cookies and copy the session id. 36 | You need to add this session id as [encrypted secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) to your repository. 37 | 38 | Now you can set up the workflow. The sample workflow beyond will help you. 39 | 40 | If you want to set up badges for multiple years in one repository just add this action multiple times (once for each year using the `year` input). 41 | Have slightly different badges for each year with a custom regex using the regex inputs. 42 | The day badge probably doesn't makes sense for multiple years as it only depends on the current date (and therefore only works in December). 43 | 44 | ## Sample Workflow 45 | 46 | ```yml 47 | name: Update AoC Badges 48 | on: 49 | schedule: # run workflow based on schedule 50 | - cron: '6 5 1-25 12 *' # from the 1. December till 25. December every day at 5:06am (avoid load at full hours) 51 | 52 | workflow_dispatch: # allow to manually start the workflow 53 | 54 | # push: # (disabled) run on push, be carefull with this setting 55 | # as the workflow should only be triggered at a rate lower than 56 | # 4 times a hour to keep traffic on aoc site low 57 | jobs: 58 | update: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v2 # clones your repo 62 | 63 | - uses: joblo2213/aoc-badges-action@v3 64 | with: 65 | userid: 00000 # your user id, see setup on how to obtain 66 | session: ${{ secrets.AOC_SESSION }} # secret containing session code, see setup on how to obtain 67 | 68 | # Optional inputs: 69 | # 70 | # year: 2021 # The year for which stats should be retrieved 71 | # leaderboard: 'https://adventofcode.com/2020/leaderboard/private/view/00000.json' # The url of the leaderboard from witch the data is fetched. Typically your private leaderboard. 72 | # file: 'README.md' # The file that contains the badges 73 | # dayRegex: '(?<=https:\/\/img\.shields\.io\/badge\/day%20📅-)[0-9]+(?=-blue)' # Regular expression that finds the content of the day badge in your file. 74 | # starsRegex: '(?<=https:\/\/img\.shields\.io\/badge\/stars%20⭐-)[0-9]+(?=-yellow)' # Regular expression that finds the content of the stars badge in your file. 75 | # daysCompletedRegex: '(?<=https:\/\/img\.shields\.io\/badge\/days%20completed-)[0-9]+(?=-red)' # Regular expression that finds the content of the days completed badge iun your file. 76 | 77 | - uses: stefanzweifel/git-auto-commit-action@v4 # Step that pushes these local changes back to your github repo 78 | with: 79 | commit_message: Update badges 80 | file_pattern: README.md 81 | ``` 82 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'AoC-badges' 2 | description: 'Updates the badges of your Readme to show your current Advent of Code stats ' 3 | runs: 4 | using: docker 5 | image: Dockerfile 6 | inputs: 7 | session: 8 | description: > 9 | Your session code for login. Retrive it from your browser cookies. 10 | Make sure you use a secret to not leak this to public. 11 | required: true 12 | userid: 13 | description: > 14 | Your unique userid on adventofcode.com, obtain it from **YOUR** private leaderboard url (its the number at the end of the url). 15 | required: true 16 | year: 17 | description: > 18 | The year for which the stats should be retrived. 19 | If not speified, the current year (from system time) is used. 20 | required: false 21 | leaderboard: 22 | description: > 23 | The url of your leaderboard json file. 24 | You can get it by just appending .json to the url of your leaderboard. 25 | required: false 26 | file: 27 | description: > 28 | The file where the badges should be updated. Default is README.md 29 | required: false 30 | default: 'README.md' 31 | dayRegex: 32 | description: > 33 | A regular expression to find the day badge in your file. Must be changed if you change the badge. 34 | required: false 35 | default: '(?<=https:\/\/img\.shields\.io\/badge\/day%20📅-)[0-9]+(?=-blue)' 36 | starsRegex: 37 | description: > 38 | A regular expression to find the stars badge in your file. Must be changed if you change the badge. 39 | required: false 40 | default: '(?<=https:\/\/img\.shields\.io\/badge\/stars%20⭐-)[0-9]+(?=-yellow)' 41 | daysCompletedRegex: 42 | description: > 43 | A regular expression to find the completed days badge in your file. Must be changed if you change the badge. 44 | required: false 45 | default: '(?<=https:\/\/img\.shields\.io\/badge\/days%20completed-)[0-9]+(?=-red)' 46 | branding: 47 | icon: 'star' 48 | color: 'yellow' 49 | -------------------------------------------------------------------------------- /aoc-badges.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import os 4 | import re 5 | import io 6 | from datetime import date, timedelta, datetime 7 | import pytz 8 | 9 | # environment variables 10 | year = os.getenv('INPUT_YEAR') 11 | leaderboard = os.getenv('INPUT_LEADERBOARD') 12 | session = os.getenv('INPUT_SESSION') 13 | readme = os.getenv('INPUT_FILE') 14 | userid = os.getenv('INPUT_USERID') 15 | day_regex = os.getenv('INPUT_DAYREGEX') 16 | stars_regex = os.getenv('INPUT_STARSREGEX') 17 | days_completed_regex = os.getenv('INPUT_DAYSCOMPLETEDREGEX') 18 | if year is None or not year: 19 | year = date.today().year 20 | else: 21 | try: 22 | year = int(year) 23 | except ValueError: 24 | print('year input is not an integer') 25 | exit(1) 26 | if leaderboard is None or not leaderboard: 27 | leaderboard = f'https://adventofcode.com/{year}/leaderboard/private/view/{userid}.json' 28 | 29 | # fetch stars 30 | cookie = {'session': session} 31 | print('Fetching leaderboard data from : ' + leaderboard) 32 | r = requests.get(leaderboard, cookies=cookie) 33 | if r.status_code != 200: 34 | print(f'Leaderboard API returned status code {r.status_code}: {r.text}') 35 | exit(1) 36 | try: 37 | data = json.loads(r.text) 38 | except json.JSONDecodeError as err: 39 | print('Could not parse leaderboard json. Is the leaderboard url correct & your session code valid?') 40 | print(err) 41 | exit(1) 42 | # noinspection PyUnboundLocalVariable 43 | stars = data['members'][userid]['stars'] 44 | 45 | # completed days 46 | days_completed = 0 47 | for day in data['members'][userid]['completion_day_level']: 48 | if '2' in data['members'][userid]['completion_day_level'][day]: 49 | days_completed += 1 50 | 51 | # Set the timezone to New York 52 | new_york_tz = pytz.timezone('America/New_York') 53 | 54 | # Get the current time in New York 55 | today = datetime.now(new_york_tz).date() 56 | 57 | # Your existing logic to determine the day 58 | if today < datetime(year, 12, 1, tzinfo=new_york_tz).date(): 59 | day = 0 60 | elif today > datetime(year, 12, 31, tzinfo=new_york_tz).date(): 61 | day = 24 62 | else: 63 | day = today.day 64 | 65 | print(f'Day: {day}') 66 | print(f'Stars: {stars}') 67 | print(f'Days completed: {days_completed}') 68 | 69 | # read file 70 | f = io.open(readme, mode='r', encoding='utf-8') 71 | txt = f.read() 72 | f.close() 73 | 74 | # replace values 75 | txt = re.sub(day_regex, str(day), txt) 76 | txt = re.sub(stars_regex, str(stars), txt) 77 | txt = re.sub(days_completed_regex, str(days_completed), txt) 78 | 79 | # write back file 80 | f = io.open(readme, mode='w', encoding='utf-8') 81 | f.write(txt) 82 | f.close() 83 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | python /aoc-badges.py 5 | --------------------------------------------------------------------------------