diff --git a/src/gw2/cogs/sessions.py b/src/gw2/cogs/sessions.py index 91baf75..24ae585 100644 --- a/src/gw2/cogs/sessions.py +++ b/src/gw2/cogs/sessions.py @@ -105,9 +105,9 @@ async def session(ctx): async with ctx.message.channel.typing(): color = ctx.bot.settings["gw2"]["EmbedColor"] - # Use DB timestamps for wall-clock session duration - start_time = rs_session[0]["created_at"] - end_time = rs_session[0]["updated_at"] + # Use JSONB date fields for session duration + start_time = bot_utils.convert_str_to_datetime_short(rs_start["date"]) + end_time = bot_utils.convert_str_to_datetime_short(rs_end["date"]) time_passed = gw2_utils.get_time_passed(start_time, end_time) player_wait_minutes = 1 @@ -119,7 +119,7 @@ async def session(ctx): ) acc_name = rs_session[0]["acc_name"] - session_date = str(start_time).split()[0] + session_date = rs_start["date"].split()[0] embed = discord.Embed(color=color) embed.set_author( name=f"{ctx.message.author.display_name}'s {gw2_messages.SESSION_TITLE} ({session_date})", @@ -128,12 +128,8 @@ async def session(ctx): embed.add_field(name=gw2_messages.ACCOUNT_NAME, value=chat_formatting.inline(acc_name)) embed.add_field(name=gw2_messages.SERVER, value=chat_formatting.inline(gw2_server)) - # Play time from API age (actual in-game time) - play_time_seconds = rs_end["age"] - rs_start["age"] - if play_time_seconds > 0: - play_time_str = gw2_utils.format_seconds_to_time(play_time_seconds) - else: - play_time_str = str(time_passed.timedelta) + # Play time from DB timestamps (session duration) + play_time_str = gw2_utils.format_seconds_to_time(int(time_passed.timedelta.total_seconds())) embed.add_field(name=gw2_messages.PLAY_TIME, value=chat_formatting.inline(play_time_str)) # Gold (special formatting) @@ -152,6 +148,11 @@ async def session(ctx): # All wallet currencies (except gold, handled above) _add_wallet_currency_fields(embed, rs_start, rs_end) + embed.set_footer( + icon_url=ctx.bot.user.avatar.url if ctx.bot.user.avatar else None, + text=f"{bot_utils.get_current_date_time_str_long()} UTC", + ) + if ( not (isinstance(ctx.channel, discord.DMChannel)) and hasattr(ctx.message.author, "activity") diff --git a/tests/unit/gw2/cogs/test_sessions.py b/tests/unit/gw2/cogs/test_sessions.py index 9c2e598..8f5ed20 100644 --- a/tests/unit/gw2/cogs/test_sessions.py +++ b/tests/unit/gw2/cogs/test_sessions.py @@ -2,7 +2,7 @@ import discord import pytest -from datetime import datetime +from datetime import datetime, timedelta from src.gw2.cogs.sessions import ( GW2Session, _add_deaths_field, @@ -48,6 +48,7 @@ def test_gw2_session_inheritance(self, gw2_session_cog): def _make_session_data(start_overrides=None, end_overrides=None): """Helper to create session data with defaults.""" base_stats = { + "date": "2024-01-15 10:00:00", "age": 5000000, "gold": 100000, "karma": 50000, @@ -71,8 +72,9 @@ def _make_session_data(start_overrides=None, end_overrides=None): "gems": 0, } end_stats = { + "date": "2024-01-15 12:30:00", "age": 5009000, - **{k: v for k, v in base_stats.items() if k != "age"}, + **{k: v for k, v in base_stats.items() if k not in ("date", "age")}, } if start_overrides: base_stats.update(start_overrides) @@ -163,7 +165,7 @@ def sample_time_passed(self): time_obj.minutes = 30 time_obj.seconds = 0 time_obj.days = 0 - time_obj.timedelta = "2:30:00" + time_obj.timedelta = timedelta(hours=2, minutes=30) return time_obj def _run_session(self, mock_ctx, sample_api_key_data, session_data, sample_time_passed, extra_patches=None): @@ -183,6 +185,7 @@ async def run(self_runner): patch("src.gw2.cogs.sessions.Gw2KeyDal") as mock_dal, patch("src.gw2.cogs.sessions.Gw2ConfigsDal") as mock_configs, patch("src.gw2.cogs.sessions.Gw2SessionsDal") as mock_sessions_dal, + patch("src.gw2.cogs.sessions.bot_utils.convert_str_to_datetime_short", side_effect=lambda x: x), patch("src.gw2.cogs.sessions.gw2_utils.get_time_passed", return_value=sample_time_passed), patch("src.gw2.cogs.sessions.Gw2SessionCharsDal") as mock_chars_dal_class, patch("src.gw2.cogs.sessions.bot_utils.send_paginated_embed") as mock_send, @@ -326,22 +329,20 @@ async def test_session_time_passed_less_than_one_minute(self, mock_ctx, sample_a mock_configs.return_value.get_gw2_server_configs = AsyncMock(return_value=[{"session": True}]) with patch("src.gw2.cogs.sessions.Gw2SessionsDal") as mock_sessions_dal: mock_sessions_dal.return_value.get_user_last_session = AsyncMock(return_value=session_data) - with patch("src.gw2.cogs.sessions.gw2_utils.get_time_passed", return_value=time_obj): - with patch("src.gw2.cogs.sessions.gw2_utils.send_msg") as mock_send_msg: - await session(mock_ctx) - mock_send_msg.assert_called_once() - msg = mock_send_msg.call_args[0][1] - assert "still updating" in msg.lower() or "Bot still updating" in msg + with patch("src.gw2.cogs.sessions.bot_utils.convert_str_to_datetime_short", side_effect=lambda x: x): + with patch("src.gw2.cogs.sessions.gw2_utils.get_time_passed", return_value=time_obj): + with patch("src.gw2.cogs.sessions.gw2_utils.send_msg") as mock_send_msg: + await session(mock_ctx) + mock_send_msg.assert_called_once() + msg = mock_send_msg.call_args[0][1] + assert "still updating" in msg.lower() or "Bot still updating" in msg # === Playtime tests === @pytest.mark.asyncio - async def test_session_play_time_from_api_age(self, mock_ctx, sample_api_key_data, sample_time_passed): - """Test that play time uses API age difference when available.""" - session_data = _make_session_data( - start_overrides={"age": 5000000}, - end_overrides={"age": 5009000}, - ) + async def test_session_play_time_from_jsonb_dates(self, mock_ctx, sample_api_key_data, sample_time_passed): + """Test that play time uses JSONB date fields for session duration.""" + session_data = _make_session_data() runner = self._run_session(mock_ctx, sample_api_key_data, session_data, sample_time_passed) async with runner.run() as r: await session(mock_ctx) @@ -350,21 +351,6 @@ async def test_session_play_time_from_api_age(self, mock_ctx, sample_api_key_dat assert play_time_field is not None assert "2h 30m" in play_time_field.value - @pytest.mark.asyncio - async def test_session_play_time_fallback_when_zero_age(self, mock_ctx, sample_api_key_data, sample_time_passed): - """Test that play time falls back to timedelta when age diff is 0.""" - session_data = _make_session_data( - start_overrides={"age": 5000000}, - end_overrides={"age": 5000000}, - ) - runner = self._run_session(mock_ctx, sample_api_key_data, session_data, sample_time_passed) - async with runner.run() as r: - await session(mock_ctx) - embed = r.mock_send.call_args[0][1] - play_time_field = next((f for f in embed.fields if f.name == "Play time"), None) - assert play_time_field is not None - assert "2:30:00" in play_time_field.value - # === Gold tests === @pytest.mark.asyncio @@ -807,7 +793,7 @@ async def test_session_time_passed_exactly_one_minute(self, mock_ctx, sample_api time_obj.hours = 0 time_obj.minutes = 1 time_obj.seconds = 0 - time_obj.timedelta = "0:01:00" + time_obj.timedelta = timedelta(minutes=1) runner = self._run_session(mock_ctx, sample_api_key_data, session_data, time_obj) async with runner.run() as r: