diff options
Diffstat (limited to 'bot/exts')
| -rw-r--r-- | bot/exts/christmas/adventofcode.py | 77 | 
1 files changed, 66 insertions, 11 deletions
| diff --git a/bot/exts/christmas/adventofcode.py b/bot/exts/christmas/adventofcode.py index fdd84d5d..be1c733a 100644 --- a/bot/exts/christmas/adventofcode.py +++ b/bot/exts/christmas/adventofcode.py @@ -9,18 +9,19 @@ from typing import List, Tuple  import aiohttp  import discord +from async_rediscache import RedisCache  from bs4 import BeautifulSoup  from discord.ext import commands  from pytz import timezone +from bot.bot import Bot  from bot.constants import AdventOfCode as AocConfig, Channels, Colours, Emojis, Month, Tokens, WHITELISTED_CHANNELS  from bot.utils import unlocked_role -from bot.utils.decorators import in_month, override_in_channel +from bot.utils.decorators import in_month, override_in_channel, seasonal_task  log = logging.getLogger(__name__)  AOC_REQUEST_HEADER = {"user-agent": "PythonDiscord AoC Event Bot"} -AOC_SESSION_COOKIE = {"session": Tokens.aoc_session_cookie}  EST = timezone("EST")  COUNTDOWN_STEP = 60 * 5 @@ -131,12 +132,19 @@ async def day_countdown(bot: commands.Bot) -> None:  class AdventOfCode(commands.Cog):      """Advent of Code festivities! Ho Ho Ho!""" -    def __init__(self, bot: commands.Bot): +    # Mapping for AoC PyDis community leaderboard IDs -> cached amount of members in leaderboard. +    public_leaderboard_members = RedisCache() + +    # We don't want that users join to multiple leaderboards, so return only 1 code to user. +    # User ID -> AoC leaderboard ID +    user_join_codes = RedisCache() + +    def __init__(self, bot: Bot):          self.bot = bot          self._base_url = f"https://adventofcode.com/{AocConfig.year}"          self.global_leaderboard_url = f"https://adventofcode.com/{AocConfig.year}/leaderboard" -        self.private_leaderboard_url = f"{self._base_url}/leaderboard/private/view/{AocConfig.leaderboard_id}" +        self.private_leaderboard_url = f"{self._base_url}/leaderboard/private/view/{AocConfig.leaderboard_staff_id}"          self.about_aoc_filepath = Path("./bot/resources/advent_of_code/about.json")          self.cached_about_aoc = self._build_about_embed() @@ -146,6 +154,7 @@ class AdventOfCode(commands.Cog):          self.countdown_task = None          self.status_task = None +        self.leaderboard_member_update_task = self.bot.loop.create_task(self.leaderboard_members_updater())          countdown_coro = day_countdown(self.bot)          self.countdown_task = self.bot.loop.create_task(countdown_coro) @@ -153,6 +162,32 @@ class AdventOfCode(commands.Cog):          status_coro = countdown_status(self.bot)          self.status_task = self.bot.loop.create_task(status_coro) +        self.leaderboard_join_codes = { +            aoc_id: join_code for aoc_id, join_code in zip( +                AocConfig.leaderboard_public_ids, AocConfig.leaderboard_public_join_codes +            ) +        } +        self.leaderboard_cookies = { +            aoc_id: cookie for aoc_id, cookie in zip( +                AocConfig.leaderboard_public_ids, Tokens.aoc_public_session_cookies +            ) +        } + +    @seasonal_task(Month.DECEMBER, sleep_time=60 * 30) +    async def leaderboard_members_updater(self) -> None: +        """Updates public leaderboards cached member amounts in every 30 minutes.""" +        # Whole December isn't advent +        if not is_in_advent(): +            return + +        # Update every leaderboard for what we have session cookie +        for aoc_id, cookie in self.leaderboard_cookies.items(): +            leaderboard = await AocPrivateLeaderboard.from_url(aoc_id, cookie) +            log.info(leaderboard.members) +            # Update only when API return any members +            if len(leaderboard.members) > 0: +                await self.public_leaderboard_members.set(aoc_id, len(leaderboard.members)) +      @in_month(Month.DECEMBER)      @commands.group(name="adventofcode", aliases=("aoc",))      @override_in_channel(AOC_WHITELIST) @@ -234,9 +269,29 @@ class AdventOfCode(commands.Cog):          author = ctx.message.author          log.info(f"{author.name} ({author.id}) has requested the PyDis AoC leaderboard code") +        if ctx.channel.id == Channels.advent_of_code_staff: +            join_code = AocConfig.leaderboard_staff_join_code +            log.info(f"{author.name} ({author.id}) ran command in staff AoC channel. Returning staff code.") +        else: +            # We want that user get only 1 code +            if await self.user_join_codes.contains(ctx.author.id): +                join_code = await self.user_join_codes.get(ctx.author.id) +                log.info(f"{author.name} ({author.id}) have already cached AoC join code. Returning it.") +            else: +                least_id, least = 0, 200 +                for aoc_id, amount in await self.public_leaderboard_members.items(): +                    log.info(amount, least) +                    if amount < least: +                        least, least_id = amount, aoc_id + +                join_code = self.leaderboard_join_codes[least_id] +                # Persist this code to Redis, so we can get it later again. +                await self.user_join_codes.set(ctx.author.id, join_code) +                log.info(f"{author.name} ({author.id}) got new join code. Persisted it to cache.") +          info_str = (              "Head over to https://adventofcode.com/leaderboard/private " -            f"with code `{AocConfig.leaderboard_join_code}` to join the PyDis private leaderboard!" +            f"with code `{join_code}` to join the PyDis private leaderboard!"          )          try:              await author.send(info_str) @@ -580,8 +635,8 @@ class AocPrivateLeaderboard:      @staticmethod      async def json_from_url( -        leaderboard_id: int = AocConfig.leaderboard_id, year: int = AocConfig.year -    ) -> "AocPrivateLeaderboard": +        leaderboard_id: int, cookie: str, year: int = AocConfig.year +    ) -> dict:          """          Request the API JSON from Advent of Code for leaderboard_id for the specified year's event. @@ -590,7 +645,7 @@ class AocPrivateLeaderboard:          api_url = f"https://adventofcode.com/{year}/leaderboard/private/view/{leaderboard_id}.json"          log.debug("Querying Advent of Code Private Leaderboard API") -        async with aiohttp.ClientSession(cookies=AOC_SESSION_COOKIE, headers=AOC_REQUEST_HEADER) as session: +        async with aiohttp.ClientSession(headers=AOC_REQUEST_HEADER, cookies={"session": cookie}) as session:              async with session.get(api_url) as resp:                  if resp.status == 200:                      raw_dict = await resp.json() @@ -608,9 +663,9 @@ class AocPrivateLeaderboard:          )      @classmethod -    async def from_url(cls) -> "AocPrivateLeaderboard": +    async def from_url(cls, leaderboard_id: int, cookie: str) -> "AocPrivateLeaderboard":          """Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json.""" -        api_json = await cls.json_from_url() +        api_json = await cls.json_from_url(leaderboard_id, cookie)          return cls.from_json(api_json)      @staticmethod @@ -738,6 +793,6 @@ def _error_embed_helper(title: str, description: str) -> discord.Embed:      return discord.Embed(title=title, description=description, colour=discord.Colour.red()) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None:      """Advent of Code Cog load."""      bot.add_cog(AdventOfCode(bot)) | 
