counting the stars

This commit is contained in:
Stefan Harmuth 2025-01-26 10:00:58 +01:00
parent 3d03c1eec3
commit f67f9ce4b9

View File

@ -2,6 +2,8 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import os.path import os.path
from collections import defaultdict
import requests import requests
import sys import sys
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
@ -41,6 +43,7 @@ class LeaderboardCache:
self.__aoc_user_agent = aoc_user_agent self.__aoc_user_agent = aoc_user_agent
self.__last_fetch_year = 2015 self.__last_fetch_year = 2015
self.__stats = {} self.__stats = {}
self.__stars = {}
self.update_stats() self.update_stats()
def fetch_leaderboard(self, year: int) -> dict: def fetch_leaderboard(self, year: int) -> dict:
@ -66,11 +69,12 @@ class LeaderboardCache:
continue continue
year_keys = {} year_keys = {}
year_stats = {"year": {}, "days": {}, "member_name_len": 0} year_stats = {"year": {}, "days": {}, "stars": {}, "member_name_len": 0}
for member_id, member_data in self.__cache[year]["members"].items(): for member_id, member_data in self.__cache[year]["members"].items():
member_key = (int(member_data["local_score"]), member_data["name"]) member_key = (int(member_data["local_score"]), member_data["name"])
year_keys[member_id] = member_key year_keys[member_id] = member_key
year_stats["year"][member_key] = [" "] * 25 year_stats["year"][member_key] = [" "] * 25
year_stats["stars"][member_data["name"]] = member_data["stars"]
year_stats["member_name_len"] = max(year_stats["member_name_len"], len(member_data["name"])) year_stats["member_name_len"] = max(year_stats["member_name_len"], len(member_data["name"]))
member_count = len(self.__cache[year]["members"]) member_count = len(self.__cache[year]["members"])
@ -119,6 +123,13 @@ class LeaderboardCache:
self.__stats[year] = year_stats self.__stats[year] = year_stats
star_stats = defaultdict(int)
for year, year_stats in self.__stats.items():
for member, stars in year_stats["stars"].items():
star_stats[member] += stars
self.__stars = star_stats
def update(self) -> dict: def update(self) -> dict:
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
if now.month == 12: # on season if now.month == 12: # on season
@ -176,6 +187,14 @@ class LeaderboardCache:
def get_years(self) -> list[str]: def get_years(self) -> list[str]:
return list(sorted(self.__stats.keys())) return list(sorted(self.__stats.keys()))
def get_year_stars(self, year: str):
for member, stars in sorted(self.__stats[year]["stars"].items(), key=lambda x: x[1], reverse=True):
yield member, stars
def get_all_stars(self):
for member, stars in sorted(self.__stars.items(), key=lambda x: x[1], reverse=True):
yield member, stars
def get_year_items(self, year: str) -> (str, int, str): def get_year_items(self, year: str) -> (str, int, str):
for (score, member_name), solve_string in sorted(self.__stats[year]["year"].items(), reverse=True): for (score, member_name), solve_string in sorted(self.__stats[year]["year"].items(), reverse=True):
yield member_name, score, solve_string yield member_name, score, solve_string
@ -187,6 +206,9 @@ class LeaderboardCache:
def get_year_member_name_len(self, year: str) -> int: def get_year_member_name_len(self, year: str) -> int:
return self.__stats[year]["member_name_len"] return self.__stats[year]["member_name_len"]
def get_all_member_name_len(self) -> int:
return max(self.__stats[x]["member_name_len"] for x in self.__stats)
class AOCBot: class AOCBot:
def __init__(self, config_file: str = "config.json"): def __init__(self, config_file: str = "config.json"):
@ -352,6 +374,28 @@ class AOCBot:
break break
self.__irc_bot.privmsg(self.__irc_channel, format_string % (member_name, score, "".join(solve_string))) self.__irc_bot.privmsg(self.__irc_channel, format_string % (member_name, score, "".join(solve_string)))
def command_stars(self, msg_from: str, message: str):
if not self.check_spam(msg_from, "stars"):
return
if not message:
max_name_len = self.__cache.get_all_member_name_len()
star_gen = self.__cache.get_all_stars()
else:
year = message
if not self.__cache.has_year(year):
self.__irc_bot.privmsg(self.__irc_channel, "Unknown year: %s" % year)
return
max_name_len = self.__cache.get_year_member_name_len(year)
star_gen = self.__cache.get_year_stars(year)
for count, (member, stars) in enumerate(star_gen):
if stars == 0:
break
line = "%2d. %-" + str(max_name_len) + "s %4d Stars"
self.__irc_bot.privmsg(self.__irc_channel, line % (count + 1, member, stars))
def update_leaderboard(self): def update_leaderboard(self):
new_stars = self.__cache.update() new_stars = self.__cache.update()
self.__last_update = datetime.now(timezone.utc) self.__last_update = datetime.now(timezone.utc)
@ -370,6 +414,10 @@ class AOCBot:
def on_raw(self, msg_from: str, msg_type: str, msg_to: str, message: str): def on_raw(self, msg_from: str, msg_type: str, msg_to: str, message: str):
print("[%s] <%s> (%s) -> <%s>: %s" % (datetime.now().strftime("%H:%M:%S"), msg_from, msg_type, msg_to, message)) print("[%s] <%s> (%s) -> <%s>: %s" % (datetime.now().strftime("%H:%M:%S"), msg_from, msg_type, msg_to, message))
def on_quit(self, msg_from: str, msg_type: str, msg_to: str, message: str):
if msg_from == self.__irc_bot.getUser().identifier:
sys.exit(0)
def flush_output(self): def flush_output(self):
sys.stdout.flush() sys.stdout.flush()
sys.stderr.flush() sys.stderr.flush()
@ -411,10 +459,12 @@ class AOCBot:
print("[AoCBot] Registering Commands") print("[AoCBot] Registering Commands")
self.__irc_bot.on(ServerMessage.RAW, self.on_raw) self.__irc_bot.on(ServerMessage.RAW, self.on_raw)
self.__irc_bot.on(ServerMessage.MSG_QUIT, self.on_quit)
self.__irc_bot.register_channel_command("!info", self.__irc_channel, self.command_info) self.__irc_bot.register_channel_command("!info", self.__irc_channel, self.command_info)
self.__irc_bot.register_channel_command("!help", self.__irc_channel, self.command_help) self.__irc_bot.register_channel_command("!help", self.__irc_channel, self.command_help)
self.__irc_bot.register_channel_command("!day", self.__irc_channel, self.command_day) self.__irc_bot.register_channel_command("!day", self.__irc_channel, self.command_day)
self.__irc_bot.register_channel_command("!year", self.__irc_channel, self.command_year) self.__irc_bot.register_channel_command("!year", self.__irc_channel, self.command_year)
self.__irc_bot.register_channel_command("!stars", self.__irc_channel, self.command_stars)
print("[AoCBot] Starting Main Loop") print("[AoCBot] Starting Main Loop")
self.__irc_bot.run() self.__irc_bot.run()