From 93fe4b357d83db1882eeceec5e39743031fcd63c Mon Sep 17 00:00:00 2001 From: Matteo De Agazio Date: Fri, 17 Apr 2026 06:35:18 -0700 Subject: [PATCH] fix(discord): free-response channels skip auto-threading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Free-response channels already bypassed the @mention gate so users could chat inline with the bot, but auto-threading still fired on every message — spinning off a thread per message and defeating the lightweight-chat purpose. Fix: fold `is_free_channel` into `skip_thread` so threading is skipped whenever the channel is in DISCORD_FREE_RESPONSE_CHANNELS (via env or discord.free_response_channels in config.yaml). Net change: one line in _handle_message + one regression test. Partially addresses #9399. Authored by @Hypn0sis (salvaged from PR #9650; the bundled 'smart' auto-thread mode from that PR was dropped in favor of deterministic true/false semantics). --- gateway/platforms/discord.py | 2 +- tests/gateway/test_discord_free_response.py | 27 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 79e70592..3c222587 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -2778,7 +2778,7 @@ class DiscordAdapter(BasePlatformAdapter): if not is_thread and not isinstance(message.channel, discord.DMChannel): no_thread_channels_raw = os.getenv("DISCORD_NO_THREAD_CHANNELS", "") no_thread_channels = {ch.strip() for ch in no_thread_channels_raw.split(",") if ch.strip()} - skip_thread = bool(channel_ids & no_thread_channels) + skip_thread = bool(channel_ids & no_thread_channels) or is_free_channel auto_thread = os.getenv("DISCORD_AUTO_THREAD", "true").lower() in ("true", "1", "yes") if auto_thread and not skip_thread and not is_voice_linked_channel: thread = await self._auto_create_thread(message) diff --git a/tests/gateway/test_discord_free_response.py b/tests/gateway/test_discord_free_response.py index c2ef286d..ee4f14e6 100644 --- a/tests/gateway/test_discord_free_response.py +++ b/tests/gateway/test_discord_free_response.py @@ -385,6 +385,33 @@ async def test_discord_voice_linked_channel_skips_mention_requirement_and_auto_t assert event.source.chat_type == "group" +@pytest.mark.asyncio +async def test_discord_free_channel_skips_auto_thread(adapter, monkeypatch): + """Free-response channels must NOT auto-create threads — bot replies inline. + + Without this, every message in a free-response channel would spin off a + thread (since the channel bypasses the @mention gate), defeating the + lightweight-chat purpose of free-response mode. + """ + monkeypatch.setenv("DISCORD_REQUIRE_MENTION", "true") + monkeypatch.setenv("DISCORD_FREE_RESPONSE_CHANNELS", "789") + monkeypatch.delenv("DISCORD_AUTO_THREAD", raising=False) # default true + + adapter._auto_create_thread = AsyncMock() + + message = make_message( + channel=FakeTextChannel(channel_id=789), + content="free chat message", + ) + + await adapter._handle_message(message) + + adapter._auto_create_thread.assert_not_awaited() + adapter.handle_message.assert_awaited_once() + event = adapter.handle_message.await_args.args[0] + assert event.source.chat_type == "group" + + @pytest.mark.asyncio async def test_discord_voice_linked_parent_thread_still_requires_mention(adapter, monkeypatch): """Threads under a voice-linked channel should still require @mention."""