From 6d406d93c902e209f56f9f0ec0872fcb8abb5aa6 Mon Sep 17 00:00:00 2001 From: chengyongru Date: Mon, 13 Jul 2026 10:20:44 +0800 Subject: [PATCH] fix(discord): route unauthorized DMs to pairing --- nanobot/channels/discord.py | 4 +++- tests/channels/test_discord_channel.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/nanobot/channels/discord.py b/nanobot/channels/discord.py index 9e0f9829..94781913 100644 --- a/nanobot/channels/discord.py +++ b/nanobot/channels/discord.py @@ -664,7 +664,9 @@ class DiscordChannel(BaseChannel): content: str, ) -> bool: """Check if inbound Discord message should be processed.""" - if not self.is_allowed(sender_id): + # Reject unauthorized guild messages before any side effects, but let DMs + # reach BaseChannel._handle_message so it can issue a pairing code. + if message.guild is not None and not self.is_allowed(sender_id): return False # Channel-based filtering: only respond in allowed channels allow_channels = self.config.allow_channels diff --git a/tests/channels/test_discord_channel.py b/tests/channels/test_discord_channel.py index 9ce91a9f..717e3108 100644 --- a/tests/channels/test_discord_channel.py +++ b/tests/channels/test_discord_channel.py @@ -362,6 +362,26 @@ async def test_on_message_accepts_allowlisted_dm() -> None: assert handled[0]["metadata"] == {"message_id": "789", "guild_id": None, "reply_to": None} +@pytest.mark.asyncio +async def test_on_message_unauthorized_dm_sends_pairing_code(monkeypatch) -> None: + channel = DiscordChannel(DiscordConfig(enabled=True, allow_from=[]), MessageBus()) + client = _FakeDiscordClient(channel, intents=None) + message = _make_message(author_id=123, channel_id=456) + client.channels[456] = message.channel + channel._client = client + channel._running = True + monkeypatch.setattr("nanobot.channels.base.is_approved", lambda _ch, _sid: False) + monkeypatch.setattr( + "nanobot.channels.base.generate_code", lambda _ch, _sid: "ABCD-EFGH" + ) + + await channel._on_message(message) + + assert len(message.channel.sent_payloads) == 1 + assert "ABCD-EFGH" in message.channel.sent_payloads[0]["content"] + assert channel._typing_tasks == {} + + @pytest.mark.asyncio async def test_on_message_accepts_when_channel_in_allow_channels() -> None: # When allow_channels is set, messages from listed channels should be forwarded.