Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions src/bot/constants/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ class CommandError:
PRIVILEGE_LOW = "Your Privilege is too low."
DIRECT_MESSAGES_DISABLED = (
"Direct messages are disabled in your configuration.\n"
"If you want to receive messages from Bots, "
"you need to enable this option under Privacy & Safety:"
'"Allow direct messages from server members."'
"To receive messages from Bots, enable **Direct messages** "
"under **Settings > Content & Social > Social permissions**."
)


Expand Down Expand Up @@ -170,9 +169,8 @@ class BotUtils:
LOADING_EXTENSION_FAILED = "ERROR: FAILED to load extension"
DISABLED_DM = (
"Direct messages are disabled in your configuration.\n"
"If you want to receive messages from Bots, "
"you need to enable this option under Privacy & Safety:\n"
'"Allow direct messages from server members."\n'
"To receive messages from Bots, enable **Direct messages** "
"under **Settings > Content & Social > Social permissions**."
)
MESSAGE_REMOVED_FOR_PRIVACY = "Your message was removed for privacy."
DELETE_MESSAGE_NO_PERMISSION = "Bot does not have permission to delete messages."
Expand Down
4 changes: 2 additions & 2 deletions src/bot/tools/bot_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ async def send_embed(ctx, embed, dm=False):
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url,
)
await ctx.send(embed=notification_embed)
except (discord.Forbidden, discord.HTTPException):
except discord.Forbidden, discord.HTTPException:
# DM failed, fall back to sending in the channel
await ctx.send(embed=embed)
else:
# Send to channel
await ctx.send(embed=embed)
except (discord.Forbidden, discord.HTTPException):
except discord.Forbidden, discord.HTTPException:
await send_error_msg(ctx, messages.DISABLED_DM)
except Exception as e:
ctx.bot.log.error(e)
Expand Down
4 changes: 2 additions & 2 deletions src/gw2/cogs/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async def _keep_typing_alive(ctx, stop_event):
await asyncio.sleep(4) # Renew every 4 seconds (Discord typing lasts ~5s)
except asyncio.CancelledError:
raise # Re-raise CancelledError
except (discord.HTTPException, discord.Forbidden):
except discord.HTTPException, discord.Forbidden:
# Handle Discord API errors gracefully and stop the loop
break
except asyncio.CancelledError:
Expand Down Expand Up @@ -265,7 +265,7 @@ async def limited_guild_fetch(task):
try:
stop_typing.set()
typing_task.cancel()
except (AttributeError, RuntimeError):
except AttributeError, RuntimeError:
# Handle cases where task is already done or event is invalid
pass
await bot_utils.send_error_msg(ctx, e)
Expand Down
12 changes: 11 additions & 1 deletion src/gw2/cogs/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,17 @@ async def session(ctx):
rs_end = rs_session[0]["end"]

if rs_end is None or rs_end.get("date") is None:
return await bot_utils.send_error_msg(ctx, gw2_messages.SESSION_SAVE_ERROR)
# Check if the user is currently playing GW2
is_playing = (
not isinstance(ctx.channel, discord.DMChannel)
and hasattr(ctx.message.author, "activity")
and ctx.message.author.activity is not None
and "guild wars 2" in str(ctx.message.author.activity.name).lower()
)
if is_playing:
return await gw2_utils.send_msg(ctx, gw2_messages.SESSION_IN_PROGRESS)
# Game stopped but end data not saved yet — bot may still be updating
return await gw2_utils.send_msg(ctx, gw2_messages.SESSION_BOT_STILL_UPDATING)

await ctx.message.channel.typing()
color = ctx.bot.settings["gw2"]["EmbedColor"]
Expand Down
3 changes: 3 additions & 0 deletions src/gw2/constants/gw2_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ def session_not_active(prefix: str) -> str:
TOWERS_CAPTURED = "Towers captured"
CAMPS_CAPTURED = "Camps captured"
SMC_CAPTURED = "SMC captured"
SESSION_IN_PROGRESS = (
"Your Guild Wars 2 session is still in progress.\nSession stats will be available after you stop playing."
)
SESSION_SAVE_ERROR = (
"There was a problem trying to record your last finished session.\n"
"Please, do not close discord when the game is running."
Expand Down
27 changes: 22 additions & 5 deletions tests/unit/gw2/cogs/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
session,
setup,
)
from src.gw2.constants import gw2_messages
from unittest.mock import AsyncMock, MagicMock, patch


Expand Down Expand Up @@ -272,19 +273,35 @@ async def test_session_no_session_found(self, mock_ctx, sample_api_key_data):

@pytest.mark.asyncio
async def test_session_end_date_is_none(self, mock_ctx, sample_api_key_data):
"""Test session command when session end date is None."""
"""Test session command when session end date is None and user not playing."""
session_data = [{"acc_name": "TestUser.1234", "start": {"date": "2024-01-15 10:00:00"}, "end": {"date": None}}]
with patch("src.gw2.cogs.sessions.Gw2KeyDal") as mock_dal:
mock_dal.return_value.get_api_key_by_user = AsyncMock(return_value=sample_api_key_data)
with patch("src.gw2.cogs.sessions.Gw2ConfigsDal") as mock_configs:
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.bot_utils.send_error_msg") as mock_error:
with patch("src.gw2.cogs.sessions.gw2_utils.send_msg") as mock_send:
await session(mock_ctx)
mock_error.assert_called_once()
# Error sent to channel (not via DM)
assert len(mock_error.call_args[0]) == 2
mock_send.assert_called_once()
assert gw2_messages.SESSION_BOT_STILL_UPDATING in mock_send.call_args[0][1]

@pytest.mark.asyncio
async def test_session_end_date_is_none_while_playing(self, mock_ctx, sample_api_key_data):
"""Test session command when end date is None and user is currently playing GW2."""
session_data = [{"acc_name": "TestUser.1234", "start": {"date": "2024-01-15 10:00:00"}, "end": {"date": None}}]
mock_ctx.message.author.activity = MagicMock()
mock_ctx.message.author.activity.name = "Guild Wars 2"
with patch("src.gw2.cogs.sessions.Gw2KeyDal") as mock_dal:
mock_dal.return_value.get_api_key_by_user = AsyncMock(return_value=sample_api_key_data)
with patch("src.gw2.cogs.sessions.Gw2ConfigsDal") as mock_configs:
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.send_msg") as mock_send:
await session(mock_ctx)
mock_send.assert_called_once()
assert gw2_messages.SESSION_IN_PROGRESS in mock_send.call_args[0][1]

@pytest.mark.asyncio
async def test_session_time_passed_less_than_one_minute(self, mock_ctx, sample_api_key_data):
Expand Down