Skip to content

feat: add Slack adapter with multi-platform ChatAdapter architecture#259

Open
dogzzdogzz wants to merge 6 commits intoopenabdev:mainfrom
dogzzdogzz:feat/slack-adapter
Open

feat: add Slack adapter with multi-platform ChatAdapter architecture#259
dogzzdogzz wants to merge 6 commits intoopenabdev:mainfrom
dogzzdogzz:feat/slack-adapter

Conversation

@dogzzdogzz
Copy link
Copy Markdown

@dogzzdogzz dogzzdogzz commented Apr 12, 2026

Summary

  • Introduces ChatAdapter trait + AdapterRouter, decoupling session management, streaming, and reactions from any specific chat platform
  • Refactors existing Discord support to implement the new trait (no behavior change)
  • Adds Slack adapter using Socket Mode (WebSocket, no public URL needed) with auto-reconnect
  • Extracts shared image/audio processing into media.rs for cross-adapter reuse
  • Both adapters can run simultaneously, sharing one SessionPool with namespaced keys
  • Updates Helm chart to support Slack deployment (backward compatible)

Closes #93. Implements Phase 1–3 of RFC #94.

Changes

File Change
src/adapter.rs NewChatAdapter trait, ChannelRef/MessageRef/SenderContext, AdapterRouter
src/slack.rs NewSlackAdapter + Socket Mode event loop
src/media.rs New — shared image resize/compress + STT download (extracted from discord.rs)
src/discord.rs Refactored — DiscordAdapter implements ChatAdapter, delegates to router
src/reactions.rs Decoupled from serenity — uses Arc<dyn ChatAdapter>
src/format.rs Added shared shorten_thread_name()
src/config.rs discord now optional, added SlackConfig
src/main.rs Multi-adapter startup, Slack as background task
config.toml.example Added [slack] section with docs
charts/openab/values.yaml Added slack config per agent (enabled, botToken, appToken, allowedChannels, allowedUsers)
charts/openab/templates/configmap.yaml Conditionally generates [discord] and/or [slack] sections
charts/openab/templates/secret.yaml Stores slack-bot-token + slack-app-token alongside discord token
charts/openab/templates/deployment.yaml Injects SLACK_BOT_TOKEN + SLACK_APP_TOKEN env vars
charts/openab/templates/NOTES.txt Added Slack setup instructions

Design Decisions

  • Socket Mode over Events API — no public URL needed, mirrors Discord's persistent WebSocket model
  • Session keys namespaced by platform (discord:xxx, slack:xxx) to prevent collision
  • Sender context wrapping centralized in AdapterRouter (not duplicated per adapter)
  • Image/audio processing shared via media.rs with optional auth_token param (Discord CDN is public, Slack files need Bearer token)
  • Unicode → Slack emoji mapping in adapter (Slack reactions API requires short names like eyes not 👀)
  • Slack user name resolution via users.info API (Discord provides names in message payload, Slack only gives user ID)
  • Thread follow-ups — Slack message events with thread_ts handled without requiring re-mention (parity with Discord)
  • Helm backward compatible — existing Discord-only values work unchanged; [discord] section now conditional (omitted when no botToken)

Helm Usage

# Discord only (unchanged)
helm install openab openab/openab \
  --set agents.kiro.discord.botToken="$DISCORD_TOKEN" \
  --set-string 'agents.kiro.discord.allowedChannels[0]=CHANNEL_ID'

# Slack only
helm install openab openab/openab \
  --set agents.kiro.slack.enabled=true \
  --set agents.kiro.slack.botToken="$SLACK_BOT_TOKEN" \
  --set agents.kiro.slack.appToken="$SLACK_APP_TOKEN" \
  --set-string 'agents.kiro.slack.allowedChannels[0]=C0123456789'

# Both simultaneously
helm install openab openab/openab \
  --set agents.kiro.discord.botToken="$DISCORD_TOKEN" \
  --set-string 'agents.kiro.discord.allowedChannels[0]=DISCORD_CHANNEL' \
  --set agents.kiro.slack.enabled=true \
  --set agents.kiro.slack.botToken="$SLACK_BOT_TOKEN" \
  --set agents.kiro.slack.appToken="$SLACK_APP_TOKEN"

Test plan

  • cargo build — zero warnings
  • cargo test — 26 tests pass
  • cargo clippy — clean
  • helm template — Discord-only, Slack-only, and both modes validated
  • Discord-only config works identically (no regression)
  • Slack-only config starts Socket Mode and responds to @mentions
  • Discord + Slack simultaneous config works
  • Thread follow-up in Slack (reply without @mention)
  • Reactions display correctly in Slack
  • Image attachments processed in Slack
  • Auto-reconnect on Socket Mode disconnect

🤖 Generated with Claude Code

tonylee-shopback and others added 4 commits April 12, 2026 23:57
Introduces a platform-agnostic adapter layer (ChatAdapter trait + AdapterRouter)
that decouples session management, streaming, and reactions from any specific
chat platform. Refactors Discord support to implement this trait and adds a
new Slack adapter using Socket Mode (WebSocket) with auto-reconnect.

- Extract ChatAdapter trait with send/edit/thread/react methods + message_limit()
- Extract AdapterRouter with shared session routing, edit-streaming, and reactions
- Decouple StatusReactionController from serenity types (uses Arc<dyn ChatAdapter>)
- Implement DiscordAdapter (ChatAdapter for Discord via serenity)
- Implement SlackAdapter (ChatAdapter for Slack via reqwest + tokio-tungstenite)
- Add [slack] config section (bot_token, app_token, allowed_channels, allowed_users)
- Support running Discord + Slack simultaneously in one process
- Session keys namespaced by platform (discord:xxx, slack:xxx)
- Unicode emoji → Slack short name mapping for reactions

Relates to openabdev#93, openabdev#94, openabdev#86

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract image resize/compress, download+encode, and STT download
  from discord.rs into shared media.rs module
- Both Discord and Slack adapters now use media::download_and_encode_image()
  and media::download_and_transcribe() with optional auth_token parameter
- Move sender context XML wrapping into AdapterRouter.handle_message()
  (was duplicated in each adapter)
- Add image/audio attachment support to Slack adapter via files[] in
  app_mention events (requires files:read bot scope)
- Slack file downloads use Bearer token auth (private URLs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ollow-up

- Fix strip_slack_mention to use LazyLock<Regex> instead of compiling per-call
- Fix shorten_thread_name regex to use LazyLock, move to shared format.rs
- Resolve Slack display name via users.info API (fallback to user_id)
- Handle message events with thread_ts for thread follow-ups without @mention
- Fix misleading allowed_channels comment (empty = allow all, not deny all)
- Add allowed_channels docs to config.toml.example

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tonylee-shopback and others added 2 commits April 13, 2026 00:57
- Add slack config to values.yaml (enabled, botToken, appToken, allowedChannels, allowedUsers)
- Generate [slack] section in configmap.yaml when slack.enabled=true
- Store slack-bot-token and slack-app-token in secret.yaml
- Inject SLACK_BOT_TOKEN and SLACK_APP_TOKEN env vars in deployment.yaml
- Make [discord] section conditional (omitted when no botToken)
- Add Slack setup instructions to NOTES.txt
- Backward compatible: existing Discord-only configs work unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update README.md: add Slack to description, architecture diagram,
  features, Quick Start (Discord/Slack toggle), config example, Helm
  example, Configuration Reference, and Project Structure
- Add docs/slack-bot-howto.md: complete Slack bot setup guide
- Add cross-reference from discord-bot-howto.md to slack-bot-howto.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

feat: Slack adapter — bring agent-broker to Slack workspaces

2 participants