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
18 changes: 14 additions & 4 deletions src/bot/tools/bot_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,26 @@ 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:
await send_error_msg(ctx, messages.DISABLED_DM)
except (discord.Forbidden, discord.HTTPException) as e:
ctx.bot.log.error(f"Failed to send message: {e}")
if dm or is_private_message(ctx):
# Only show DM disabled message when we were actually trying to DM
try:
await ctx.send(embed=discord.Embed(
description=chat_formatting.error(messages.DISABLED_DM),
color=discord.Color.red(),
))
except (discord.Forbidden, discord.HTTPException):
pass # Can't send to channel either, nothing we can do
# If channel send failed, the error is already logged above
except Exception as e:
ctx.bot.log.error(e)
ctx.bot.log.error(f"Unexpected error sending message: {e}")


async def delete_message(ctx, warning=False):
Expand Down
25 changes: 12 additions & 13 deletions tests/unit/bot/tools/test_bot_utils_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,29 +195,30 @@ async def test_send_embed_normal_channel_send(self, mock_ctx):
mock_ctx.author.send.assert_not_called()

@pytest.mark.asyncio
@patch("src.bot.tools.bot_utils.send_error_msg")
async def test_send_embed_discord_forbidden_exception(self, mock_send_error, mock_ctx):
"""Test send_embed handles discord.Forbidden exception."""
async def test_send_embed_discord_forbidden_dm_shows_disabled_msg(self, mock_ctx):
"""Test send_embed shows DM disabled message when DM send fails."""
embed = discord.Embed(description="Test", color=discord.Color.green())
mock_ctx.channel = MagicMock(spec=discord.DMChannel)
mock_ctx.author.send.side_effect = discord.Forbidden(MagicMock(), "Cannot send messages")

await bot_utils.send_embed(mock_ctx, embed)

mock_send_error.assert_called_once()
call_args = mock_send_error.call_args[0]
assert call_args[0] == mock_ctx
# Should log the error
mock_ctx.bot.log.error.assert_called_once()
# Should try to send DM disabled message to channel
mock_ctx.send.assert_called_once()

@pytest.mark.asyncio
@patch("src.bot.tools.bot_utils.send_error_msg")
async def test_send_embed_discord_http_exception(self, mock_send_error, mock_ctx):
"""Test send_embed handles discord.HTTPException."""
async def test_send_embed_discord_http_exception_channel_logs_error(self, mock_ctx):
"""Test send_embed logs error when channel send fails (no DM disabled message)."""
embed = discord.Embed(description="Test", color=discord.Color.green())
mock_ctx.send.side_effect = discord.HTTPException(MagicMock(), "HTTP error")

await bot_utils.send_embed(mock_ctx, embed, dm=False)

mock_send_error.assert_called_once()
# Should log the error but NOT show DM disabled (was a channel send)
mock_ctx.bot.log.error.assert_called_once()
assert "Failed to send message" in mock_ctx.bot.log.error.call_args[0][0]

@pytest.mark.asyncio
async def test_send_embed_generic_exception(self, mock_ctx):
Expand All @@ -228,9 +229,7 @@ async def test_send_embed_generic_exception(self, mock_ctx):
await bot_utils.send_embed(mock_ctx, embed, dm=False)

mock_ctx.bot.log.error.assert_called_once()
error_arg = mock_ctx.bot.log.error.call_args[0][0]
assert isinstance(error_arg, ValueError)
assert str(error_arg) == "Some unexpected error"
assert "Unexpected error" in mock_ctx.bot.log.error.call_args[0][0]


class TestDeleteMessage:
Expand Down