From 4125cc3b7c1ce0fe86789e192de5dcf3dfbf1343 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 29 Jun 2026 14:44:59 +1000 Subject: [PATCH] fix(slack): subscribe to message.mpim + mpim scopes so group DMs work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group DMs (multi-person DMs, channel_type=mpim) were never delivered to the Slack bot. The adapter already classifies mpim as a DM and replies ambiently (adapter.py:2526, is_dm = channel_type in {im, mpim}), but the generated app manifest only subscribed to message.im / im:history — the 1:1 DM pair. Without the message.mpim event subscription Slack drops group-DM messages before the adapter ever sees them, so 1:1 DMs worked while group-DM ambient mode was dead. Add message.mpim to bot_events and mpim:history (the scope that event requires per Slack docs) + mpim:read (mirrors im:read for the conversations.info classification call) to bot_scopes. Update the SLACK_BOT_TOKEN / SLACK_APP_TOKEN setup-help strings and the Slack docs (EN + zh-Hans: scope table, event table, troubleshooting) so existing installs are told to add the new scopes and reinstall. Reported by an enterprise customer. Note: this is a manifest/scope change, so it only takes effect after the app is reinstalled and the new scopes are accepted. Tests: assert message.mpim + mpim:history + mpim:read are in the manifest (with and without assistant mode); both fail on current main and pass with this change. --- hermes_cli/config.py | 4 +-- hermes_cli/slack_cli.py | 3 ++ tests/hermes_cli/test_slack_cli.py | 28 +++++++++++++++++++ website/docs/user-guide/messaging/slack.md | 4 +++ .../current/user-guide/messaging/slack.md | 4 +++ 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 71d23a2c138..4f4a030d0fa 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -3795,7 +3795,7 @@ OPTIONAL_ENV_VARS = { "SLACK_BOT_TOKEN": { "description": "Slack bot token (xoxb-). Get from OAuth & Permissions after installing your app. " "Required scopes: chat:write, app_mentions:read, channels:history, groups:history, " - "im:history, im:read, im:write, users:read, files:read, files:write", + "im:history, im:read, im:write, mpim:history, mpim:read, users:read, files:read, files:write", "prompt": "Slack Bot Token (xoxb-...)", "help": "In your Slack app, add the required bot scopes, install the app to the workspace, then copy OAuth & Permissions > Bot User OAuth Token.", "url": "https://api.slack.com/apps", @@ -3805,7 +3805,7 @@ OPTIONAL_ENV_VARS = { "SLACK_APP_TOKEN": { "description": "Slack app-level token (xapp-) for Socket Mode. Get from Basic Information → " "App-Level Tokens. Also ensure Event Subscriptions include: message.im, " - "message.channels, message.groups, app_mention", + "message.channels, message.groups, message.mpim, app_mention", "prompt": "Slack App Token (xapp-...)", "help": "In your Slack app, enable Socket Mode, then create Basic Information > App-Level Tokens with the connections:write scope.", "url": "https://api.slack.com/apps", diff --git a/hermes_cli/slack_cli.py b/hermes_cli/slack_cli.py index 63546614261..75dcdfb2fdd 100644 --- a/hermes_cli/slack_cli.py +++ b/hermes_cli/slack_cli.py @@ -76,6 +76,8 @@ def _build_full_manifest( "im:history", "im:read", "im:write", + "mpim:history", + "mpim:read", "users:read", ] @@ -84,6 +86,7 @@ def _build_full_manifest( "message.channels", "message.groups", "message.im", + "message.mpim", ] if include_assistant: diff --git a/tests/hermes_cli/test_slack_cli.py b/tests/hermes_cli/test_slack_cli.py index 2905859f003..bdc937a59c8 100644 --- a/tests/hermes_cli/test_slack_cli.py +++ b/tests/hermes_cli/test_slack_cli.py @@ -45,6 +45,34 @@ class TestSlackFullManifest: bot_scopes = manifest["oauth_config"]["scopes"]["bot"] assert "groups:read" in bot_scopes + def test_group_dm_scopes_and_event_are_included(self): + """Group DMs (mpim) need message.mpim + mpim:history or Slack never + delivers them — the adapter classifies mpim as a DM and replies + ambiently, but only if the event reaches the bot at all.""" + manifest = _build_full_manifest("Hermes", "Your Hermes agent on Slack") + + bot_scopes = manifest["oauth_config"]["scopes"]["bot"] + bot_events = manifest["settings"]["event_subscriptions"]["bot_events"] + + # The event is the load-bearing piece: without message.mpim Slack + # drops group-DM messages before the adapter sees them. + assert "message.mpim" in bot_events + # mpim:history is the scope message.mpim requires (per Slack docs); + # mpim:read mirrors im:read for conversations.info classification. + assert "mpim:history" in bot_scopes + assert "mpim:read" in bot_scopes + + def test_group_dm_surface_present_without_assistant_mode(self): + """Dropping assistant mode must not strip the group-DM surface.""" + manifest = _build_full_manifest( + "Hermes", "Your Hermes agent on Slack", include_assistant=False + ) + + bot_scopes = manifest["oauth_config"]["scopes"]["bot"] + bot_events = manifest["settings"]["event_subscriptions"]["bot_events"] + assert "message.mpim" in bot_events + assert "mpim:history" in bot_scopes + def test_assistant_features_remain_enabled(self): manifest = _build_full_manifest("Hermes", "Your Hermes agent on Slack") diff --git a/website/docs/user-guide/messaging/slack.md b/website/docs/user-guide/messaging/slack.md index cf281202f7a..b1b65c57ee9 100644 --- a/website/docs/user-guide/messaging/slack.md +++ b/website/docs/user-guide/messaging/slack.md @@ -76,6 +76,8 @@ Navigate to **Features → OAuth & Permissions** in the sidebar. Scroll to **Sco | `im:history` | Read direct message history | | `im:read` | View basic DM info | | `im:write` | Open and manage DMs | +| `mpim:history` | Read group direct message (multi-person DM) history | +| `mpim:read` | View basic group DM info | | `users:read` | Look up user information | | `files:read` | Read and download attached files, including voice notes/audio | | `files:write` | Upload files (images, audio, documents) | @@ -124,6 +126,7 @@ This step is critical — it controls what messages the bot can see. | Event | Required? | Purpose | |-------|-----------|---------| | `message.im` | **Yes** | Bot receives direct messages | +| `message.mpim` | **Recommended** | Bot receives messages in **group DMs** (multi-person DMs) it's added to | | `message.channels` | **Yes** | Bot receives messages in **public** channels it's added to | | `message.groups` | **Recommended** | Bot receives messages in **private** channels it's invited to | | `app_mention` | **Yes** | Prevents Bolt SDK errors when bot is @mentioned | @@ -606,6 +609,7 @@ Notes: | Bot works in DMs but not in channels | **Most common issue.** Add `message.channels` and `message.groups` to event subscriptions, reinstall the app, and invite the bot to the channel with `/invite @Hermes Agent` | | Bot doesn't respond to @mentions in channels | 1) Check `message.channels` event is subscribed. 2) Bot must be invited to the channel. 3) Ensure `channels:history` scope is added. 4) Reinstall the app after scope/event changes | | Bot ignores messages in private channels | Add both the `message.groups` event subscription and `groups:history` scope, then reinstall the app and `/invite` the bot | +| Bot doesn't respond in group DMs (multi-person DMs) | Add the `message.mpim` event subscription and the `mpim:history` scope (plus `mpim:read`), then **reinstall** the app. Without `message.mpim`, Slack never delivers group-DM messages to the bot — even though 1:1 DMs work. | | "Sending messages to this app has been turned off" in DMs | Enable the **Messages Tab** in App Home settings (see Step 5) | | "not_authed" or "invalid_auth" errors | Regenerate your Bot Token and App Token, update `.env` | | Bot responds but can't post in a channel | Invite the bot to the channel with `/invite @Hermes Agent` | diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/messaging/slack.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/messaging/slack.md index 71812c551ca..2b6dbb21225 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/messaging/slack.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/messaging/slack.md @@ -65,6 +65,8 @@ description: "使用 Socket Mode 将 Hermes Agent 设置为 Slack 机器人" | `im:history` | 读取私信历史记录 | | `im:read` | 查看基本私信信息 | | `im:write` | 打开并管理私信 | +| `mpim:history` | 读取群组私信(多人私信)历史记录 | +| `mpim:read` | 查看基本群组私信信息 | | `users:read` | 查询用户信息 | | `files:read` | 读取并下载附件文件,包括语音备忘录/音频 | | `files:write` | 上传文件(图片、音频、文档) | @@ -110,6 +112,7 @@ Socket Mode 让机器人通过 WebSocket 连接,无需公开 URL。 | 事件 | 是否必需 | 用途 | |-------|-----------|---------| | `message.im` | **必需** | 机器人接收私信 | +| `message.mpim` | **推荐** | 机器人接收其加入的**群组私信**(多人私信)消息 | | `message.channels` | **必需** | 机器人接收其加入的**公开**频道消息 | | `message.groups` | **推荐** | 机器人接收被邀请加入的**私有**频道消息 | | `app_mention` | **必需** | 防止机器人被 @ 提及时出现 Bolt SDK 错误 | @@ -558,6 +561,7 @@ slack: | 机器人在私信中正常但在频道中不响应 | **最常见问题。** 将 `message.channels` 和 `message.groups` 添加到事件订阅,重新安装应用,并用 `/invite @Hermes Agent` 邀请机器人加入频道 | | 机器人不响应频道中的 @mention | 1) 检查 `message.channels` 事件是否已订阅。2) 机器人必须被邀请到频道。3) 确保已添加 `channels:history` 权限范围。4) 更改权限范围/事件后重新安装应用 | | 机器人忽略私有频道中的消息 | 添加 `message.groups` 事件订阅和 `groups:history` 权限范围,然后重新安装应用并 `/invite` 机器人 | +| 机器人不响应群组私信(多人私信) | 添加 `message.mpim` 事件订阅和 `mpim:history` 权限范围(以及 `mpim:read`),然后**重新安装**应用。没有 `message.mpim`,即使 1:1 私信正常,Slack 也永远不会向机器人投递群组私信消息。 | | 私信中出现"向此应用发送消息已被关闭" | 在 App Home 设置中启用 **Messages Tab**(见第五步) | | "not_authed" 或 "invalid_auth" 错误 | 重新生成 Bot Token 和 App Token,更新 `.env` | | 机器人响应但无法在频道中发帖 | 用 `/invite @Hermes Agent` 邀请机器人加入频道 |