Alter 4 files
Add `.gitignore` Add `LICENSE` Add `aocDiscordConfig.json.example` Add `main.py`
This commit is contained in:
commit
84a5dd86d9
4 changed files with 161 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
aocDiscordConfig.json
|
||||
aocDiscordData.json
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -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.
|
6
aocDiscordConfig.json.example
Normal file
6
aocDiscordConfig.json.example
Normal file
|
@ -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"
|
||||
}
|
132
main.py
Normal file
132
main.py
Normal file
|
@ -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()
|
Reference in a new issue