diff --git a/README.md b/README.md
index 4106b2a..186fe35 100644
--- a/README.md
+++ b/README.md
@@ -166,7 +166,7 @@ nanobot agent -m "Hello from my local LLM!"
## 💬 Chat Apps
-Talk to your nanobot through Telegram, Discord, WhatsApp, Feishu, DingTalk, or Email — anytime, anywhere.
+Talk to your nanobot through Telegram, Discord, WhatsApp, Feishu, DingTalk, Slack, or Email — anytime, anywhere.
| Channel | Setup |
|---------|-------|
@@ -175,6 +175,7 @@ Talk to your nanobot through Telegram, Discord, WhatsApp, Feishu, DingTalk, or E
| **WhatsApp** | Medium (scan QR) |
| **Feishu** | Medium (app credentials) |
| **DingTalk** | Medium (app credentials) |
+| **Slack** | Medium (bot + app tokens) |
| **Email** | Medium (IMAP/SMTP credentials) |
@@ -374,6 +375,44 @@ nanobot gateway
+
+Slack
+
+Uses **Socket Mode** — no public URL required.
+
+**1. Create a Slack app**
+- Go to [Slack API](https://api.slack.com/apps) → Create New App
+- **OAuth & Permissions**: Add bot scopes: `chat:write`, `reactions:write`, `app_mentions:read`
+- Install to your workspace and copy the **Bot Token** (`xoxb-...`)
+- **Socket Mode**: Enable it and generate an **App-Level Token** (`xapp-...`) with `connections:write` scope
+- **Event Subscriptions**: Subscribe to `message.im`, `message.channels`, `app_mention`
+
+**2. Configure**
+
+```json
+{
+ "channels": {
+ "slack": {
+ "enabled": true,
+ "botToken": "xoxb-...",
+ "appToken": "xapp-...",
+ "groupPolicy": "mention"
+ }
+ }
+}
+```
+
+> `groupPolicy`: `"mention"` (respond only when @mentioned), `"open"` (respond to all messages), or `"allowlist"` (restrict to specific channels).
+> DM policy defaults to open. Set `"dm": {"enabled": false}` to disable DMs.
+
+**3. Run**
+
+```bash
+nanobot gateway
+```
+
+
+
Email
@@ -592,7 +631,7 @@ PRs welcome! The codebase is intentionally small and readable. 🤗
- [ ] **Multi-modal** — See and hear (images, voice, video)
- [ ] **Long-term memory** — Never forget important context
- [ ] **Better reasoning** — Multi-step planning and reflection
-- [ ] **More integrations** — Slack, calendar, and more
+- [ ] **More integrations** — Calendar and more
- [ ] **Self-improvement** — Learn from feedback and mistakes
### Contributors
diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py
index 64c95ba..b764c3d 100644
--- a/nanobot/agent/loop.py
+++ b/nanobot/agent/loop.py
@@ -246,7 +246,7 @@ class AgentLoop:
channel=msg.channel,
chat_id=msg.chat_id,
content=final_content,
- metadata=msg.metadata or {},
+ metadata=msg.metadata or {}, # Pass through for channel-specific needs (e.g. Slack thread_ts)
)
async def _process_system_message(self, msg: InboundMessage) -> OutboundMessage | None:
diff --git a/nanobot/channels/slack.py b/nanobot/channels/slack.py
index 32abe3b..be95dd2 100644
--- a/nanobot/channels/slack.py
+++ b/nanobot/channels/slack.py
@@ -5,7 +5,7 @@ import re
from typing import Any
from loguru import logger
-from slack_sdk.socket_mode.aiohttp import SocketModeClient
+from slack_sdk.socket_mode.websockets import SocketModeClient
from slack_sdk.socket_mode.request import SocketModeRequest
from slack_sdk.socket_mode.response import SocketModeResponse
from slack_sdk.web.async_client import AsyncWebClient
@@ -115,8 +115,8 @@ class SlackChannel(BaseChannel):
sender_id = event.get("user")
chat_id = event.get("channel")
- # Ignore bot/system messages to prevent loops
- if event.get("subtype") == "bot_message" or event.get("subtype"):
+ # Ignore bot/system messages (any subtype = not a normal user message)
+ if event.get("subtype"):
return
if self._bot_user_id and sender_id == self._bot_user_id:
return