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