diff options
| -rw-r--r-- | bot/exts/backend/branding/_cog.py | 49 | ||||
| -rw-r--r-- | bot/exts/backend/branding/_repository.py | 18 |
2 files changed, 56 insertions, 11 deletions
diff --git a/bot/exts/backend/branding/_cog.py b/bot/exts/backend/branding/_cog.py index 332d4ad58..50ae11b11 100644 --- a/bot/exts/backend/branding/_cog.py +++ b/bot/exts/backend/branding/_cog.py @@ -62,6 +62,18 @@ def extract_event_duration(event: Event) -> str: return f"{start_date} - {end_date}" +def extract_event_name(event: Event) -> str: + """ + Extract title-cased event name from the path of `event`. + + An event with a path of 'events/black_history_month' will resolve to 'Black History Month'. + """ + name = event.path.split("/")[-1] # Inner-most directory name + words = name.split("_") # Words from snake case + + return " ".join(word.title() for word in words) + + class Branding(commands.Cog): """Guild branding management.""" @@ -80,6 +92,10 @@ class Branding(commands.Cog): # corresponding to the amount of times each icon has been used in the current rotation cache_icons = RedisCache() + # Cache holding all available event names & their durations; this is cached by the daemon and read by + # the calendar command with the intention of preventing API spam; doesn't contain the fallback event + cache_events = RedisCache() + def __init__(self, bot: Bot) -> None: """Instantiate repository abstraction & allow daemon to start.""" self.bot = bot @@ -271,12 +287,35 @@ class Branding(commands.Cog): """ log.debug("Synchronise: fetching current event") - event = await self.repository.get_current_event() + current_event, available_events = await self.repository.get_current_event() - if event is None: + await self.populate_cache_events(available_events) + + if current_event is None: log.error("Failed to fetch event ~ cannot synchronise!") else: - await self.enter_event(event) + await self.enter_event(current_event) + + async def populate_cache_events(self, events: t.List[Event]) -> None: + """ + Clear `cache_events` and re-populate with names and durations of `events`. + + For each event, we store its name and duration string. This is the information presented to users in the + calendar command. If a format change is needed, it has to be done here. + + The cache does not store the fallback event, as it is not shown in the calendar. + """ + log.debug(f"Populating events cache with {len(events)} events") + + await self.cache_events.clear() + + no_fallback = [event for event in events if not event.meta.is_fallback] + chronological_events = sorted(no_fallback, key=lambda event_: event_.meta.start_date) + + await self.cache_events.update({ + extract_event_name(event): extract_event_duration(event) + for event in chronological_events + }) # endregion # region: Daemon @@ -322,7 +361,9 @@ class Branding(commands.Cog): """ log.debug("Daemon awakens: checking current event") - new_event = await self.repository.get_current_event() + new_event, available_events = await self.repository.get_current_event() + + await self.populate_cache_events(available_events) if new_event is None: log.warning("Failed to get current event from the branding repository, daemon will do nothing!") diff --git a/bot/exts/backend/branding/_repository.py b/bot/exts/backend/branding/_repository.py index ef292619e..b1368c59e 100644 --- a/bot/exts/backend/branding/_repository.py +++ b/bot/exts/backend/branding/_repository.py @@ -189,11 +189,14 @@ class BrandingRepository: log.trace(f"Found {len(instances)} correctly configured events") return instances - async def get_current_event(self) -> t.Optional[Event]: + async def get_current_event(self) -> t.Tuple[t.Optional[Event], t.List[Event]]: """ Get the currently active event, or the fallback event. - Returns None in the case that no event is active, and no fallback event is found. + The second return value is a list of all available events. The caller may discard it, if not needed. + Returning all events alongside the current one prevents having to query the API twice in some cases. + + The current event may be None in the case that no event is active, and no fallback event is found. """ utc_now = datetime.utcnow() log.debug(f"Finding active event for: {utc_now}") @@ -201,17 +204,18 @@ class BrandingRepository: # As all events exist in the arbitrary year, we construct a separate object for the purposes of comparison lookup_now = date(year=ARBITRARY_YEAR, month=utc_now.month, day=utc_now.day) - events = await self.get_events() + available_events = await self.get_events() - for event in events: + for event in available_events: meta = event.meta if not meta.is_fallback and (meta.start_date <= lookup_now <= meta.end_date): - return event + return event, available_events log.debug("No active event found, looking for fallback") - for event in events: + for event in available_events: if event.meta.is_fallback: - return event + return event, available_events log.warning("No event is currently active and no fallback event was found!") + return None, available_events |