From 5b22e61cfa91e67990147eea8251a90251dc476c Mon Sep 17 00:00:00 2001 From: Jeff Davis Date: Fri, 10 Apr 2026 03:37:16 -0500 Subject: [PATCH] feat(discord): add allowed_channels whitelist config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add DISCORD_ALLOWED_CHANNELS (env var) / discord.allowed_channels (config.yaml) support to restrict the bot to only respond in specified channels. When set, messages from any channel NOT in the allowed list are silently ignored — even if the bot is @mentioned. This provides a secure default- deny posture vs the existing ignored_channels which is default-allow. This is especially useful when bots in other channels may create new channels dynamically (e.g., project bots) — a blacklist requires constant maintenance while a whitelist is set-and-forget. Follows the same config pattern as ignored_channels and free_response_channels: - Env var: DISCORD_ALLOWED_CHANNELS (comma-separated channel IDs) - Config: discord.allowed_channels (string or list of channel IDs) - Env var takes precedence over config.yaml - Empty/unset = no restriction (backward compatible) Files changed: - gateway/platforms/discord.py: check allowed_channels before ignored_channels - gateway/config.py: map discord.allowed_channels → DISCORD_ALLOWED_CHANNELS - hermes_cli/config.py: add allowed_channels to DEFAULT_CONFIG --- gateway/config.py | 6 ++++++ gateway/platforms/discord.py | 16 +++++++++++++--- hermes_cli/config.py | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/gateway/config.py b/gateway/config.py index e4f04d891..98b191805 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -581,6 +581,12 @@ def load_gateway_config() -> GatewayConfig: if isinstance(ic, list): ic = ",".join(str(v) for v in ic) os.environ["DISCORD_IGNORED_CHANNELS"] = str(ic) + # allowed_channels: if set, bot ONLY responds in these channels (whitelist) + ac = discord_cfg.get("allowed_channels") + if ac is not None and not os.getenv("DISCORD_ALLOWED_CHANNELS"): + if isinstance(ac, list): + ac = ",".join(str(v) for v in ac) + os.environ["DISCORD_ALLOWED_CHANNELS"] = str(ac) # no_thread_channels: channels where bot responds directly without creating thread ntc = discord_cfg.get("no_thread_channels") if ntc is not None and not os.getenv("DISCORD_NO_THREAD_CHANNELS"): diff --git a/gateway/platforms/discord.py b/gateway/platforms/discord.py index 74aaa75a4..0e51fc75e 100644 --- a/gateway/platforms/discord.py +++ b/gateway/platforms/discord.py @@ -2234,6 +2234,7 @@ class DiscordAdapter(BasePlatformAdapter): # discord.require_mention: Require @mention in server channels (default: true) # discord.free_response_channels: Channel IDs where bot responds without mention # discord.ignored_channels: Channel IDs where bot NEVER responds (even when mentioned) + # discord.allowed_channels: If set, bot ONLY responds in these channels (whitelist) # discord.no_thread_channels: Channel IDs where bot responds directly without creating thread # discord.auto_thread: Auto-create thread on @mention in channels (default: true) @@ -2245,12 +2246,21 @@ class DiscordAdapter(BasePlatformAdapter): parent_channel_id = self._get_parent_channel_id(message.channel) if not isinstance(message.channel, discord.DMChannel): - # Check ignored channels first - never respond even when mentioned - ignored_channels_raw = os.getenv("DISCORD_IGNORED_CHANNELS", "") - ignored_channels = {ch.strip() for ch in ignored_channels_raw.split(",") if ch.strip()} channel_ids = {str(message.channel.id)} if parent_channel_id: channel_ids.add(parent_channel_id) + + # Check allowed channels - if set, only respond in these channels + allowed_channels_raw = os.getenv("DISCORD_ALLOWED_CHANNELS", "") + if allowed_channels_raw: + allowed_channels = {ch.strip() for ch in allowed_channels_raw.split(",") if ch.strip()} + if not (channel_ids & allowed_channels): + logger.debug("[%s] Ignoring message in non-allowed channel: %s", self.name, channel_ids) + return + + # Check ignored channels - never respond even when mentioned + ignored_channels_raw = os.getenv("DISCORD_IGNORED_CHANNELS", "") + ignored_channels = {ch.strip() for ch in ignored_channels_raw.split(",") if ch.strip()} if channel_ids & ignored_channels: logger.debug("[%s] Ignoring message in ignored channel: %s", self.name, channel_ids) return diff --git a/hermes_cli/config.py b/hermes_cli/config.py index a54d07562..93aa1cc0c 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -552,6 +552,7 @@ DEFAULT_CONFIG = { "discord": { "require_mention": True, # Require @mention to respond in server channels "free_response_channels": "", # Comma-separated channel IDs where bot responds without mention + "allowed_channels": "", # If set, bot ONLY responds in these channel IDs (whitelist) "auto_thread": True, # Auto-create threads on @mention in channels (like Slack) "reactions": True, # Add 👀/✅/❌ reactions to messages during processing },