Beispiel #1
0
    async def _daemon_func(self) -> None:
        """
        Manage all automated behaviour of the BrandingManager cog.

        Once a day, the daemon will perform the following tasks:
            - Update `current_season`
            - Poll GitHub API to see if the available branding for `current_season` has changed
            - Update assets if changes are detected (banner, guild icon, bot avatar, bot nickname)
            - Check whether it's time to cycle guild icons

        The internal loop runs once when activated, then periodically at the time
        given by `time_until_midnight`.

        All method calls in the internal loop are considered safe, i.e. no errors propagate
        to the daemon's loop. The daemon itself does not perform any error handling on its own.
        """
        await self.bot.wait_until_guild_available()

        while True:
            self.current_season = get_current_season()
            branding_changed = await self.refresh()

            if branding_changed:
                await self.apply()

            elif next(self.days_since_cycle) == Branding.cycle_frequency:
                await self.cycle()

            until_midnight = time_until_midnight()
            await asyncio.sleep(until_midnight.total_seconds())
Beispiel #2
0
    async def branding_set(self,
                           ctx: commands.Context,
                           *,
                           season_name: t.Optional[str] = None) -> None:
        """
        Manually set season, or reset to current if none given.

        Season search is a case-less comparison against both seasonal class name,
        and its `season_name` attr.

        This only pre-loads the cog's internal state to the chosen season, but does not
        automatically apply the branding. As that is an expensive operation, the `apply`
        command must be called explicitly after this command finishes.

        This means that this command can be used to 'preview' a season gathering info
        about its available assets, without applying them to the guild.

        If the daemon is running, it will automatically reset the season to current when
        it wakes up. The season set via this command can therefore remain 'detached' from
        what it should be - the daemon will make sure that it's set back properly.
        """
        if season_name is None:
            new_season = get_current_season()
        else:
            new_season = get_season(season_name)
            if new_season is None:
                raise BrandingError("No such season exists")

        if self.current_season is new_season:
            raise BrandingError(
                f"Season {self.current_season.season_name} already active")

        self.current_season = new_season
        await self.branding_refresh(ctx)
Beispiel #3
0
    def __init__(self, bot: SeasonalBot) -> None:
        """
        Assign safe default values on init.

        At this point, we don't have information about currently available branding.
        Most of these attributes will be overwritten once the daemon connects, or once
        the `refresh` command is used.
        """
        self.bot = bot
        self.current_season = get_current_season()

        self.banner = None
        self.avatar = None

        self.available_icons = []
        self.remaining_icons = []

        self.days_since_cycle = itertools.cycle([None])

        self.config_file = make_persistent(
            Path("bot", "resources", "evergreen", "branding.json"))
        should_run = self._read_config()["daemon_active"]

        if should_run:
            self.daemon = self.bot.loop.create_task(self._daemon_func())
        else:
            self.daemon = None