feat(slack): add allowed_channels whitelist config

This commit is contained in:
Cash Williams 2026-04-10 16:39:22 -05:00 committed by Teknium
parent 6a4ecc0a9f
commit cd3ef685c4
4 changed files with 173 additions and 7 deletions

View file

@ -809,6 +809,12 @@ def load_gateway_config() -> GatewayConfig:
os.environ["SLACK_FREE_RESPONSE_CHANNELS"] = str(frc)
if "reactions" in slack_cfg and not os.getenv("SLACK_REACTIONS"):
os.environ["SLACK_REACTIONS"] = str(slack_cfg["reactions"]).lower()
# allowed_channels: if set, bot ONLY responds in these channels (whitelist)
ac = slack_cfg.get("allowed_channels")
if ac is not None and not os.getenv("SLACK_ALLOWED_CHANNELS"):
if isinstance(ac, list):
ac = ",".join(str(v) for v in ac)
os.environ["SLACK_ALLOWED_CHANNELS"] = str(ac)
# Discord settings → env vars (env vars take precedence)
discord_cfg = yaml_cfg.get("discord", {})

View file

@ -1887,6 +1887,12 @@ class SlackAdapter(BasePlatformAdapter):
is_thread_reply = bool(event_thread_ts and event_thread_ts != ts)
if not is_dm and bot_uid:
# Check allowed channels — if set, only respond in these channels (whitelist)
allowed_channels = self._slack_allowed_channels()
if allowed_channels and channel_id not in allowed_channels:
logger.debug("[Slack] Ignoring message in non-allowed channel: %s", channel_id)
return
if channel_id in self._slack_free_response_channels():
pass # Free-response channel — always process
elif not self._slack_require_mention():
@ -2924,3 +2930,19 @@ class SlackAdapter(BasePlatformAdapter):
if s:
return {part.strip() for part in s.split(",") if part.strip()}
return set()
def _slack_allowed_channels(self) -> set:
"""Return the whitelist of channel IDs the bot will respond in.
When non-empty, messages from channels NOT in this set are silently
ignored even if the bot is @mentioned. DMs are never filtered.
Empty set means no restriction (fully backward compatible).
"""
raw = self.config.extra.get("allowed_channels")
if raw is None:
raw = os.getenv("SLACK_ALLOWED_CHANNELS", "")
if isinstance(raw, list):
return {str(part).strip() for part in raw if str(part).strip()}
if isinstance(raw, str) and raw.strip():
return {part.strip() for part in raw.split(",") if part.strip()}
return set()