Skip to content

Implement Slack Socket Mode, Discord MESSAGE_CONTENT intent, and bridge fixes#2252

Open
johnnyxmas wants to merge 9 commits into42wim:masterfrom
johnnyxmas:master
Open

Implement Slack Socket Mode, Discord MESSAGE_CONTENT intent, and bridge fixes#2252
johnnyxmas wants to merge 9 commits into42wim:masterfrom
johnnyxmas:master

Conversation

@johnnyxmas
Copy link
Copy Markdown

@johnnyxmas johnnyxmas commented Mar 25, 2026

Summary

  • Slack Socket Mode: Migrated from RTM to Socket Mode (slackevents + socketmode packages). Requires both Token (xoxb-) and AppToken (xapp-) now.
  • Discord MESSAGE_CONTENT intent: Added privileged intent so the bot can read message content, embeds, and attachments.
  • Fix edit echo loop: Fixed skipMessageEvent failing to recognize message_changed events from the bridge's own bot, which caused Discord edits to round-trip back as new messages via Slack.
  • Fix message echo (all types): The callback ID block check only inspected BlockSet[0], but Slack can inject rich_text blocks before our SectionBlock, causing the bridge to miss its own marker and re-relay messages back to the source. Now iterates all blocks. Also added ev.User == b.si.UserID check for cases where the event's BotID doesn't match b.si.ID.
  • Fix Discord display name resolution: getNick() now uses Member.DisplayName() (server nick > global display name > username), matching Discord's UI display priority instead of falling back directly to the raw username handle.
  • Fix Slack username fallback: Username resolution now falls back to RealName before the handle (user.Name), matching Slack's own display behavior. Previously, users without a DisplayName set would show only their short handle (e.g. "jeff") instead of their full name (e.g. "Jeff Jarmoc").
  • README: Added complete Slack OAuth scope table, Discord bot permissions/intents setup guide, and updated the Slack-Discord bridge example for Socket Mode.

Changed files

  • bridge/slack/slack.go, bridge/slack/handlers.go — Socket Mode migration + echo fixes (edit echo, block iteration, UserID check)
  • bridge/slack/helpers.go, bridge/slack/users_channels.go — RealName fallback for username resolution
  • bridge/discord/discord.go — MESSAGE_CONTENT intent
  • bridge/discord/helpers.go — DisplayName() fix in getNick()
  • bridge/config/config.go — AppToken field
  • matterbridge.toml.sample — Updated docs for Socket Mode and Discord intents
  • README.md — Slack scopes, Discord permissions, updated example
  • release_1.27/ — Rebuilt all 7 platform binaries

Test plan

  • Messages bridge Discord → Slack with correct server nicknames
  • Messages bridge Slack → Discord with correct usernames via webhooks
  • Editing a Discord message updates the Slack message without echoing back
  • Messages from the bridge's own bot are correctly skipped (no echo to source)
  • Slack bot posts with custom username/avatar (requires chat:write.customize scope)
  • Slack users without DisplayName show RealName in Discord instead of handle
  • All 7 platform binaries build successfully

🤖 Generated with Claude Code

- Migrate Slack bridge from RTM to Socket Mode (slackevents + socketmode packages)
- Add AppToken field to Protocol config for Slack bot app-level tokens (xapp-*)
- Update Slack message handlers to use Events API event types from slackevents
- Fix Slack API calls (GetBotInfo, GetConversationInfo) for Socket Mode compatibility
- Add MESSAGE_CONTENT privileged intent to Discord bridge for reading message content/embeds
- Replace MakeIntent() with direct bitwise OR for cleaner intent configuration
- Slack: Document bot token (xoxb-) and App-Level Token (xapp-) requirements for Socket Mode
- Slack: Remove outdated references to legacy tokens
- Discord: Note MESSAGE_CONTENT privileged intent requirement in token documentation
…event echos on Discord<>Slack

Issue:
When you edit a Discord message, matterbridge correctly updates the corresponding Slack message via UpdateMessage. But Slack then fires a message_changed event, and the Slack bridge's skipMessageEvent fails to recognize it as its own edit because:

For message_changed events, Slack puts the bot_id on ev.Message.BotID (the inner message), but the code only checks ev.BotID (the outer event envelope, which is empty)
The callback ID block check also fails because blocks are posted at the top level, not inside attachments

So the edited message round-trips: Discord edit → Slack update → Slack bridge picks it up → bridges back to Discord as a new message from [slack].

Fix:
Added a check for ev.Message.BotID in handlers.go:178-183 so that message_changed events from the bridge's own bot are properly skipped.
@johnnyxmas johnnyxmas changed the title Implement Slack Socket Mode and add Discord MESSAGE_CONTENT intent Implement Slack Socket Mode, Discord MESSAGE_CONTENT intent, and bridge fixes Mar 25, 2026
johnnyxmas and others added 4 commits March 26, 2026 15:21
The hasOurCallbackID check was only looking inside ev.Attachments[0].Blocks,
but MsgOptionBlocks() puts blocks at the top-level ev.Blocks field. This meant
the callback ID was never found for Socket Mode/Events API events, making the
bridge unable to reliably recognize its own messages and causing edit echoes.

Now checks ev.Blocks.BlockSet and ev.Message.Blocks.BlockSet (for
message_changed events) in addition to the attachment-nested locations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The callback ID block check only looked at BlockSet[0], but Slack can
inject rich_text blocks before our SectionBlock. Now iterates all blocks.
Also adds ev.User == b.si.UserID check for cases where BotID doesn't match.

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

1 participant