diff --git a/.env.example b/.env.example index 921ffd2..e291cdc 100644 --- a/.env.example +++ b/.env.example @@ -89,3 +89,4 @@ GW2_WVW_COOLDOWN=20 GW2_API_RETRY_MAX_ATTEMPTS=5 GW2_API_RETRY_DELAY=3.0 GW2_API_SESSION_RETRY_BG_DELAY=30.0 +GW2_API_SESSION_END_DELAY=120 diff --git a/src/gw2/cogs/sessions.py b/src/gw2/cogs/sessions.py index 910a1df..1170070 100644 --- a/src/gw2/cogs/sessions.py +++ b/src/gw2/cogs/sessions.py @@ -104,8 +104,18 @@ async def session(ctx): live_stats["date"] = bot_utils.convert_datetime_to_str_short(bot_utils.get_current_date_time()) rs_end = live_stats is_live_snapshot = True + elif gw2_utils.is_session_ending(user_id): + # Background end_session is still waiting for the API cache to refresh + remaining = gw2_utils.get_session_end_remaining_seconds(user_id) + msg = gw2_messages.SESSION_END_PROCESSING + if remaining is not None and remaining > 0: + m = remaining // 60 + s = remaining % 60 + time_str = f"{m}m {s}s" if m > 0 else f"{s}s" + msg += f"\n{gw2_messages.WAITING_TIME}: `{time_str}`" + return await bot_utils.send_warning_msg(ctx, msg) else: - # End data missing and user stopped playing - finalize the session now + # End data missing, user stopped playing, no background task - finalize now progress_msg = await gw2_utils.send_progress_embed(ctx) await gw2_utils.end_session(ctx.bot, ctx.message.author, api_key, skip_delay=True) rs_session = await gw2_session_dal.get_user_last_session(user_id) diff --git a/src/gw2/constants/gw2_messages.py b/src/gw2/constants/gw2_messages.py index 73f4b23..846a0c6 100644 --- a/src/gw2/constants/gw2_messages.py +++ b/src/gw2/constants/gw2_messages.py @@ -95,6 +95,11 @@ def session_not_active(prefix: str) -> str: "Please add another API key with permissions that are MISSING if you want to use this command." ) SESSION_BOT_STILL_UPDATING = "Bot still updating your stats!" +SESSION_END_PROCESSING = ( + "Your session is still being processed.\n" + "The bot waits a few minutes after you stop playing to ensure accurate data from the GW2 API.\n" + "Please try again shortly." +) SESSION_USER_STILL_PLAYING = "You are playing Guild Wars 2 at the moment.\nYour stats may NOT be accurate." WAITING_TIME = "Waiting time" ACCOUNT_NAME = "Account Name" diff --git a/src/gw2/constants/gw2_settings.py b/src/gw2/constants/gw2_settings.py index 96c62a3..a68a69e 100644 --- a/src/gw2/constants/gw2_settings.py +++ b/src/gw2/constants/gw2_settings.py @@ -31,7 +31,7 @@ class Gw2Settings(BaseSettings): api_retry_max_attempts: int | None = Field(default=5) api_retry_delay: float | None = Field(default=3.0) api_session_retry_bg_delay: float | None = Field(default=30.0) - api_session_end_delay: float | None = Field(default=300.0) + api_session_end_delay: float | None = Field(default=120.0) @lru_cache(maxsize=1) diff --git a/src/gw2/tools/gw2_utils.py b/src/gw2/tools/gw2_utils.py index 2a42509..22b74f9 100644 --- a/src/gw2/tools/gw2_utils.py +++ b/src/gw2/tools/gw2_utils.py @@ -344,6 +344,28 @@ def _is_gw2_activity_detected(before_activity, after_activity) -> bool: ) +def is_session_ending(user_id: int) -> bool: + """Check if a session end is currently being processed for this user.""" + if user_id not in _processing_sessions: + return False + state = _processing_sessions[user_id] + return state["current"] == "end" or state.get("pending") == "end" + + +def get_session_end_remaining_seconds(user_id: int) -> int | None: + """Get remaining seconds until end session delay completes, or None if not waiting.""" + if user_id not in _processing_sessions: + return None + state = _processing_sessions[user_id] + started_at = state.get("delay_started_at") + if started_at is None or state["current"] != "end": + return None + end_delay = _gw2_settings.api_session_end_delay or 0 + elapsed = (datetime.now() - started_at).total_seconds() + remaining = max(0, int(end_delay - elapsed)) + return remaining + + async def _handle_gw2_activity_change( bot: Bot, member: discord.Member, @@ -452,6 +474,8 @@ async def end_session(bot: Bot, member: discord.Member, api_key: str, *, skip_de end_delay = _gw2_settings.api_session_end_delay if not skip_delay and end_delay and end_delay > 0: bot.log.debug(f"Waiting {end_delay}s for GW2 API cache to refresh before ending session for user {member.id}") + if member.id in _processing_sessions: + _processing_sessions[member.id]["delay_started_at"] = datetime.now() await asyncio.sleep(end_delay) session = await get_user_stats(bot, api_key)