diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index 8b697275bdd..c58afffcd74 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -4820,8 +4820,10 @@ class DiscordAdapter(BasePlatformAdapter): # recovery) # DMs skip entirely because every DM message triggers the bot, # so the session transcript already has everything. + # Auto-threaded messages also skip — we just created the thread, + # there's nothing prior to backfill. _has_mention_gap = require_mention and not is_free_channel and not in_bot_thread - if _has_mention_gap or is_thread: + if (_has_mention_gap or is_thread) and auto_threaded_channel is None: _backfill_text = await self._fetch_channel_context( message.channel, before=message, ) diff --git a/tests/gateway/test_discord_free_response.py b/tests/gateway/test_discord_free_response.py index 1e4497c83cc..e2133d56c35 100644 --- a/tests/gateway/test_discord_free_response.py +++ b/tests/gateway/test_discord_free_response.py @@ -905,3 +905,25 @@ async def test_discord_dm_does_not_backfill(adapter, monkeypatch): assert event.channel_context is None +@pytest.mark.asyncio +async def test_discord_auto_thread_skips_backfill(adapter, monkeypatch): + """Auto-created threads skip backfill — the thread is brand new with no prior context.""" + monkeypatch.setenv("DISCORD_REQUIRE_MENTION", "true") + monkeypatch.setenv("DISCORD_AUTO_THREAD", "true") + monkeypatch.delenv("DISCORD_NO_THREAD_CHANNELS", raising=False) + monkeypatch.delenv("DISCORD_FREE_RESPONSE_CHANNELS", raising=False) + adapter.config.extra["history_backfill"] = True + + fake_thread = FakeThread(channel_id=777, name="auto-thread") + adapter._auto_create_thread = AsyncMock(return_value=fake_thread) + adapter._fetch_channel_context = AsyncMock(return_value="[Recent channel messages]\n[Alice] noise") + + bot_user = adapter._client.user + parent = FakeTextChannel(channel_id=200, name="general") + message = make_message(channel=parent, content="hello", mentions=[bot_user]) + await adapter._handle_message(message) + + adapter._auto_create_thread.assert_awaited_once() + adapter._fetch_channel_context.assert_not_awaited() + + diff --git a/tests/gateway/test_discord_slash_commands.py b/tests/gateway/test_discord_slash_commands.py index d5ed297faad..8d44f77302e 100644 --- a/tests/gateway/test_discord_slash_commands.py +++ b/tests/gateway/test_discord_slash_commands.py @@ -624,6 +624,13 @@ class _FakeTextChannel: self.guild = SimpleNamespace(name=guild_name, id=1) self.topic = None + def history(self, *args, **kwargs): + async def _empty(): + return + yield # pragma: no cover — make this an async generator + + return _empty() + class _FakeThreadChannel(_discord_mod.Thread): """isinstance(ch, discord.Thread) → True.""" @@ -636,6 +643,13 @@ class _FakeThreadChannel(_discord_mod.Thread): self.topic = None self.parent = SimpleNamespace(id=parent_id, name="general", guild=SimpleNamespace(name=guild_name, id=1)) + def history(self, *args, **kwargs): + async def _empty(): + return + yield # pragma: no cover — make this an async generator + + return _empty() + def _fake_message(channel, *, content="Hello", author_id=42, display_name="Jezza"): return SimpleNamespace(