Skip to content

feat(slack): auto-approve channels the bot is added to (resolves #68)#78

Open
juniperbevensee wants to merge 1 commit into
mainfrom
dev/juniperbevensee/slack-channel-auto-approve
Open

feat(slack): auto-approve channels the bot is added to (resolves #68)#78
juniperbevensee wants to merge 1 commit into
mainfrom
dev/juniperbevensee/slack-channel-auto-approve

Conversation

@juniperbevensee

Copy link
Copy Markdown
Collaborator

Resolves #68 — Slack parity with Signal's group reconcile.

Root cause

gateway/platforms/slack.py (the plugins/platforms/slack/adapter.py adapter) had no discover/reconcile step. Channels had to be listed in SLACK_ALLOWED_CHANNELS manually, so an admin adding the bot to a channel did nothing until an operator hand-edited the env var. Signal already solves the equivalent problem via _reconcile_groups + an invite handler + a persisted signal_approved_groups.json.

Approach (mirrors Signal, additive + self-contained)

Two entry points, matching the two ways a channel becomes live:

  • member_joined_channel event handler — when the joining member is our bot user, auto-approve the channel. Trusts the action of being added (a Slack workspace is admin-managed, so being added is already admin-gated), the same way Signal's invite handler trusts the inviter.
  • _reconcile_channels() on connect — lists the bot's member channels via users.conversations (paginated, capped) and approves them. Recovers the add-while-offline / missed-event gap, analogous to Signal's add-at-creation _reconcile_groups.

Approvals are:

  • Persisted to slack_approved_channels.json under HERMES_HOME (tmp-file + os.replace, best-effort) so they survive restarts — parity with signal_approved_groups.json.
  • Unioned into the effective allowlist only when a static whitelist is active. With no whitelist the bot already responds everywhere, so approving would wrongly turn an unrestricted install into a restricted one. This matches Signal reconcile's early return when * is present. _slack_allowed_channels() now layers runtime approvals on top of the operator-authored _slack_configured_channels().
  • Gated by SLACK_CHANNEL_JOIN_POLICY (auto | disabled, default auto). disabled restores the pre-Slack: auto-approve channels the bot is added to (parity with Signal group reconcile) #68 manual behavior.

Every path is best-effort and self-guarded — a reconcile/persist failure never breaks connect. Fork stays minimally divergent: no upstream files touched beyond the Slack adapter, no behavior change for existing installs (empty/* allowlist unaffected).

Tests

New tests/gateway/test_slack_channel_auto_approve.py26 tests, all passing:

  • policy parsing (default/config/env/unknown)
  • _channel_approval_active truth table (no whitelist / * / whitelist / disabled)
  • allowlist union (unions when active; ignores approvals when unrestricted or *)
  • _approve_channel (adds+persists, idempotent, no-op when inactive, skips already-configured)
  • persistence round-trip + corrupt-file tolerance
  • member_joined_channel (bot added → approve; other user → ignore; no-whitelist no-op; malformed event non-fatal)
  • _reconcile_channels (approves members, no-op without whitelist/when disabled, API-failure isolation, pagination)

Existing test_slack_mention.py (79) and test_slack.py still pass for the code under change; the 15 unrelated failures in my ad-hoc venv (SSRF/session-scope/bot-auth) reproduce identically on the clean tree (environmental, not caused by this change).

🤖 Generated with Claude Code

…ignal reconcile)

Resolves #68. Slack had no equivalent of Signal's group reconcile: channels
had to be listed in SLACK_ALLOWED_CHANNELS manually, so adding the bot to a
new channel did nothing until an operator edited the env var.

Mirror the Signal pattern with two additive, self-contained entry points:

- member_joined_channel event handler — when the joining member is our bot
  user, auto-approve the channel (trusts the *action* of being added, the way
  Signal's invite handler trusts the inviter).
- _reconcile_channels() on connect — list the bot's member channels via
  users.conversations and approve them, recovering the add-while-offline gap
  (analogous to Signal's _reconcile_groups add-at-creation recovery).

Approvals are persisted to slack_approved_channels.json under HERMES_HOME so
they survive restarts, and unioned into the effective allowlist only when a
static whitelist is active — with no whitelist the bot already responds
everywhere, so approving would wrongly turn an unrestricted install into a
restricted one (matches Signal's early return on the "*" wildcard).

Gated by SLACK_CHANNEL_JOIN_POLICY (auto|disabled, default auto). All paths
are best-effort and never break connect. 26 new tests cover policy parsing,
the allowlist union, persistence round-trip, the join handler, and reconcile
(pagination + failure isolation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZiFLur67KjEViZb3oQ2Z3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slack: auto-approve channels the bot is added to (parity with Signal group reconcile)

1 participant