aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar kwzrd <[email protected]>2024-10-09 13:31:20 +0200
committerGravatar Chris Lovering <[email protected]>2024-11-23 19:16:22 +0000
commit79b6d5a85ea6713d451286716534761cb0a2e453 (patch)
tree4d0058d4df3eeb48e14262ffad373ed97abcd12c
parentDeps: add tenacity (diff)
Branding: handle repo errors in cog
If we fail to fetch an event, the whole branding sync will now be aborted. This will prevent situations where we fail to fetch the current event due to a 5xx error and the cog resorts to the fallback branding in the middle of an event. Error handling is moved to the cog. The repo abstraction will now propagate errors rather than silence them.
-rw-r--r--bot/errors.py2
-rw-r--r--bot/exts/backend/branding/_cog.py34
-rw-r--r--bot/exts/backend/branding/_repository.py30
3 files changed, 37 insertions, 29 deletions
diff --git a/bot/errors.py b/bot/errors.py
index 7949dc1b4..31d264686 100644
--- a/bot/errors.py
+++ b/bot/errors.py
@@ -59,7 +59,7 @@ class InvalidInfractionError(ConversionError):
class BrandingMisconfigurationError(RuntimeError):
- """Raised by the Branding cog when a misconfigured event is encountered."""
+ """Raised by the Branding cog when branding misconfiguration is detected."""
diff --git a/bot/exts/backend/branding/_cog.py b/bot/exts/backend/branding/_cog.py
index 433f152b4..40c2c2c6f 100644
--- a/bot/exts/backend/branding/_cog.py
+++ b/bot/exts/backend/branding/_cog.py
@@ -342,14 +342,14 @@ class Branding(commands.Cog):
"""
log.debug("Synchronise: fetching current event.")
- current_event, available_events = await self.repository.get_current_event()
+ try:
+ current_event, available_events = await self.repository.get_current_event()
+ except Exception:
+ log.exception("Synchronisation aborted: failed to fetch events.")
+ return False, False
await self.populate_cache_events(available_events)
- if current_event is None:
- log.error("Failed to fetch event. Cannot synchronise!")
- return False, False
-
return await self.enter_event(current_event)
async def populate_cache_events(self, events: list[Event]) -> None:
@@ -433,10 +433,6 @@ class Branding(commands.Cog):
await self.populate_cache_events(available_events)
- if new_event is None:
- log.warning("Daemon main: failed to get current event from branding repository, will do nothing.")
- return
-
if new_event.path != await self.cache_information.get("event_path"):
log.debug("Daemon main: new event detected!")
await self.enter_event(new_event)
@@ -596,8 +592,24 @@ class Branding(commands.Cog):
log.info("Performing command-requested event cache refresh.")
async with ctx.typing():
- available_events = await self.repository.get_events()
- await self.populate_cache_events(available_events)
+ try:
+ available_events = await self.repository.get_events()
+ except Exception:
+ log.exception("Refresh aborted: failed to fetch events.")
+ resp = make_embed(
+ "Refresh aborted",
+ "Failed to fetch events. See log for details.",
+ success=False,
+ )
+ else:
+ await self.populate_cache_events(available_events)
+ resp = make_embed(
+ "Refresh successful",
+ "The event calendar has been refreshed.",
+ success=True,
+ )
+
+ await ctx.send(embed=resp)
await ctx.invoke(self.branding_calendar_group)
diff --git a/bot/exts/backend/branding/_repository.py b/bot/exts/backend/branding/_repository.py
index 20cad0a5d..7d2de9bf2 100644
--- a/bot/exts/backend/branding/_repository.py
+++ b/bot/exts/backend/branding/_repository.py
@@ -186,37 +186,34 @@ class BrandingRepository:
"""
Discover available events in the branding repository.
- Misconfigured events are skipped. May return an empty list in the catastrophic case.
+ Propagate errors if an event fails to fetch or deserialize.
"""
log.debug("Discovering events in branding repository.")
- try:
- event_directories = await self.fetch_directory("events", types=("dir",)) # Skip files.
- except Exception:
- log.exception("Failed to fetch 'events' directory.")
- return []
+ event_directories = await self.fetch_directory("events", types=("dir",)) # Skip files.
instances: list[Event] = []
for event_directory in event_directories.values():
- log.trace(f"Attempting to construct event from directory: '{event_directory.path}'.")
- try:
- instance = await self.construct_event(event_directory)
- except Exception as exc:
- log.warning(f"Could not construct event '{event_directory.path}'.", exc_info=exc)
- else:
- instances.append(instance)
+ log.trace(f"Reading event directory: '{event_directory.path}'.")
+ instance = await self.construct_event(event_directory)
+ instances.append(instance)
return instances
- async def get_current_event(self) -> tuple[Event | None, list[Event]]:
+ async def get_current_event(self) -> tuple[Event, list[Event]]:
"""
Get the currently active event, or the fallback event.
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.
+ Raise an error in the following cases:
+ * GitHub request fails
+ * The branding repo contains an invalid event
+ * No event is active and the fallback event is missing
+
+ Events are validated in the branding repo. The bot assumes that events are valid.
"""
utc_now = datetime.now(tz=UTC)
log.debug(f"Finding active event for: {utc_now}.")
@@ -249,5 +246,4 @@ class BrandingRepository:
if event.meta.is_fallback:
return event, available_events
- log.warning("No event is currently active and no fallback event was found!")
- return None, available_events
+ raise BrandingMisconfigurationError("No event is active and the fallback event is missing!")