From 3e09caca4ac52402f0ed0084eb636e5aa7462002 Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:18:37 -0500 Subject: [PATCH 1/3] fix(levels): fix issue where xp gets reset if db errors --- tux/database/controllers/levels.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tux/database/controllers/levels.py b/tux/database/controllers/levels.py index 87d39af72..2a59aa9d4 100644 --- a/tux/database/controllers/levels.py +++ b/tux/database/controllers/levels.py @@ -1,5 +1,6 @@ import datetime import math +from typing import NoReturn, cast from loguru import logger @@ -78,18 +79,26 @@ async def get_xp_and_level(self, member_id: int, guild_id: int) -> tuple[float, Returns ------- tuple[float, int] - A tuple containing the XP and level of the member, or (0.0, 0) if not found + A tuple containing the XP and level of the member. Returns None if not found. """ + + def _fail(msg: str) -> NoReturn: + raise ValueError(msg) + try: record = await self.find_one(where={"member_id": member_id, "guild_id": guild_id}) + if record is None: + return 0.0, 0 + + xp = getattr(record, "xp", None) + level = getattr(record, "level", None) + if xp is None or level is None: + _fail(f"Levels record missing xp/level for member {member_id} in guild {guild_id}") - if record: - return (self.safe_get_attr(record, "xp", 0.0), self.safe_get_attr(record, "level", 0)) - return (0.0, 0) # noqa: TRY300 + return cast(float, xp), cast(int, level) except Exception as e: - logger.error(f"Error querying XP and level for member_id: {member_id}, guild_id: {guild_id}: {e}") - return (0.0, 0) + _fail(f"Error querying XP and level for member_id: {member_id}, guild_id: {guild_id}: {e}") async def get_last_message_time(self, member_id: int, guild_id: int) -> datetime.datetime | None: """Get the last message time of a member in a guild. From 67179a77a35aeaeb6cb88d4dd02e6cbd7d2a6e3b Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:22:41 -0500 Subject: [PATCH 2/3] docs(levels): clarify return value in get_xp_and_level docstring --- tux/database/controllers/levels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tux/database/controllers/levels.py b/tux/database/controllers/levels.py index 2a59aa9d4..7e5d099b8 100644 --- a/tux/database/controllers/levels.py +++ b/tux/database/controllers/levels.py @@ -79,7 +79,7 @@ async def get_xp_and_level(self, member_id: int, guild_id: int) -> tuple[float, Returns ------- tuple[float, int] - A tuple containing the XP and level of the member. Returns None if not found. + A tuple containing the XP and level of the member. """ def _fail(msg: str) -> NoReturn: From b0392439d2d043e5b3ce14c9e6555794b7f981ed Mon Sep 17 00:00:00 2001 From: electron271 <66094410+electron271@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:27:25 -0500 Subject: [PATCH 3/3] feat(levels): add debug logging for missing level records --- tux/database/controllers/levels.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tux/database/controllers/levels.py b/tux/database/controllers/levels.py index 7e5d099b8..3dc746e9f 100644 --- a/tux/database/controllers/levels.py +++ b/tux/database/controllers/levels.py @@ -88,6 +88,9 @@ def _fail(msg: str) -> NoReturn: try: record = await self.find_one(where={"member_id": member_id, "guild_id": guild_id}) if record is None: + logger.debug( + f"Level record not found for member_id: {member_id}, guild_id: {guild_id}. Returning 0.0, 0", + ) return 0.0, 0 xp = getattr(record, "xp", None)