commit 84a5dd86d9df8191d6193c9c0af33d510202a3c8 Author: AKP Date: Fri Dec 16 02:26:03 2022 +0000 Alter 4 files Add `.gitignore` Add `LICENSE` Add `aocDiscordConfig.json.example` Add `main.py` diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0082b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +aocDiscordConfig.json +aocDiscordData.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2a74264 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 codemicro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/aocDiscordConfig.json.example b/aocDiscordConfig.json.example new file mode 100644 index 0000000..b2c35c5 --- /dev/null +++ b/aocDiscordConfig.json.example @@ -0,0 +1,6 @@ +{ + "session": "your AoC session token", + "leaderboardID": "your AoC private leaderboard ID, as a string", + "webhook": "your Discord webhook URL", + "email_address": "your email address, used as a user agent when sending requests to AoC" +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..97c7b88 --- /dev/null +++ b/main.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +import json +import requests +import datetime +import time + +CONFIG_FILE = "aocDiscordConfig.json" +DATA_FILE = "aocDiscordData.json" + + +def get_star_state(n, day, is_part_two): + target_bit = ((day - 1) * 2) + (1 if is_part_two else 0) + return (n & (1 << target_bit)) != 0 + + +def count_stars(n): + x = 0 + while n != 0: + if n & 1 == 1: + x += 1 + n = n >> 1 + return x + + +def transform_response(x): + res = {} + for user_id in x["members"]: + n = 0 + for day_number in x["members"][user_id]["completion_day_level"]: + day = int(day_number) + if "1" in x["members"][user_id]["completion_day_level"][day_number]: + n = n | (1 << ((day - 1) * 2)) + if "2" in x["members"][user_id]["completion_day_level"][day_number]: + n = n | (1 << (((day - 1) * 2) + 1)) + res[user_id] = n + return res + + +def dump_data(dat): + f = open(DATA_FILE, "w") + json.dump(transform_response(dat), f) + f.close() + + +def make_embed(player, day, part, time): + return { + "title": f"{player} solved day {day}, part {part}!", + "color": 0xFFFF66 if part == "2" else 0x9999CC, + "timestamp": datetime.datetime.fromtimestamp(int(time)).isoformat(), + } + + +def send_embeds(url, embeds): + while len(embeds) != 0: + time.sleep(1) + n = 10 + if len(embeds) < n: + n = len(embeds) + + requests.post(url, json={"embeds": embeds[:n]}).raise_for_status() + + embeds = embeds[n:] + + +def main(): + # load settings + with open(CONFIG_FILE) as f: + raw_json = json.load(f) + SESSION_TOKEN = raw_json["session"] + LEADERBOARD_ID = raw_json["leaderboardID"] + WEBHOOK_URL = raw_json["webhook"] + EMAIL_ADDR = raw_json["email_address"] + + r = requests.get( + f"https://adventofcode.com/2022/leaderboard/private/view/{LEADERBOARD_ID}.json", + cookies={"session": SESSION_TOKEN}, + headers={"User-Agent": EMAIL_ADDR}, + ) + r.raise_for_status() + response = r.json() + + # load cache file + try: + f = open(DATA_FILE) + historial_data = json.load(f) + f.close() + except FileNotFoundError: + dump_data(response) + return + + embeds = [] + for user_id in response["members"]: + n = historial_data.get(user_id, 0) + historical_num_stars = count_stars(n) + current_num_stars = response["members"][user_id]["stars"] + + if current_num_stars != historical_num_stars: + + for day_number in response["members"][user_id]["completion_day_level"]: + day = int(day_number) + ds = response["members"][user_id]["completion_day_level"][day_number] + + if not get_star_state(n, day, False) and "1" in ds: + embeds.append( + make_embed( + response["members"][user_id]["name"], + day_number, + "1", + response["members"][user_id]["completion_day_level"][ + day_number + ]["1"]["get_star_ts"], + ) + ) + if not get_star_state(n, day, True) and "2" in ds: + embeds.append( + make_embed( + response["members"][user_id]["name"], + day_number, + "2", + response["members"][user_id]["completion_day_level"][ + day_number + ]["2"]["get_star_ts"], + ) + ) + + send_embeds(WEBHOOK_URL, embeds) + + dump_data(response) + + +if __name__ == "__main__": + main()