mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 14:01:06 +00:00
399 lines
9 KiB
Markdown
399 lines
9 KiB
Markdown
# Minimal Slack Bot Setup (No Web Server, WebSocket Only)
|
|
|
|
Here's how to connect your Node.js agent to Slack using **Socket Mode** - no Express, no HTTP server, just WebSockets and callbacks.
|
|
|
|
---
|
|
|
|
## 1. Dependencies
|
|
|
|
```bash
|
|
npm install @slack/socket-mode @slack/web-api
|
|
```
|
|
|
|
That's it. Two packages:
|
|
- `@slack/socket-mode` - Receives events via WebSocket
|
|
- `@slack/web-api` - Sends messages back to Slack
|
|
|
|
---
|
|
|
|
## 2. Get Your Tokens
|
|
|
|
You need **TWO tokens**:
|
|
|
|
### A. Bot Token (`xoxb-...`)
|
|
1. Go to https://api.slack.com/apps
|
|
2. Create app → "From scratch"
|
|
3. Click "OAuth & Permissions" in sidebar
|
|
4. Add **Bot Token Scopes** (all 16):
|
|
```
|
|
app_mentions:read
|
|
channels:history
|
|
channels:join
|
|
channels:read
|
|
chat:write
|
|
files:read
|
|
files:write
|
|
groups:history
|
|
groups:read
|
|
im:history
|
|
im:read
|
|
im:write
|
|
mpim:history
|
|
mpim:read
|
|
mpim:write
|
|
users:read
|
|
```
|
|
5. Click "Install to Workspace" at top
|
|
6. Copy the **Bot User OAuth Token** (starts with `xoxb-`)
|
|
|
|
### B. App-Level Token (`xapp-...`)
|
|
1. In same app, click "Basic Information" in sidebar
|
|
2. Scroll to "App-Level Tokens"
|
|
3. Click "Generate Token and Scopes"
|
|
4. Name it whatever (e.g., "socket-token")
|
|
5. Add scope: `connections:write`
|
|
6. Click "Generate"
|
|
7. Copy the token (starts with `xapp-`)
|
|
|
|
---
|
|
|
|
## 3. Enable Socket Mode
|
|
|
|
1. Go to https://api.slack.com/apps → select your app
|
|
2. Click **"Socket Mode"** in sidebar
|
|
3. Toggle **"Enable Socket Mode"** to ON
|
|
4. This routes your app's interactions and events over WebSockets instead of public HTTP endpoints
|
|
5. Done - no webhook URL needed!
|
|
|
|
**Note:** Socket Mode is intended for internal apps in development or behind a firewall. Not for apps distributed via Slack Marketplace.
|
|
|
|
---
|
|
|
|
## 4. Enable Direct Messages
|
|
|
|
1. Go to https://api.slack.com/apps → select your app
|
|
2. Click **"App Home"** in sidebar
|
|
3. Scroll to **"Show Tabs"** section
|
|
4. Check **"Allow users to send Slash commands and messages from the messages tab"**
|
|
5. Save
|
|
|
|
---
|
|
|
|
## 5. Subscribe to Events
|
|
|
|
1. Go to https://api.slack.com/apps → select your app
|
|
2. Click **"Event Subscriptions"** in sidebar
|
|
3. Toggle **"Enable Events"** to ON
|
|
4. **Important:** No Request URL needed (Socket Mode handles this)
|
|
5. Expand **"Subscribe to bot events"**
|
|
6. Click **"Add Bot User Event"** and add:
|
|
- `app_mention` (required - to see when bot is mentioned)
|
|
- `message.channels` (required - to log all channel messages for context)
|
|
- `message.groups` (optional - to see private channel messages)
|
|
- `message.im` (required - to see DMs)
|
|
7. Click **"Save Changes"** at bottom
|
|
|
|
---
|
|
|
|
## 6. Store Tokens
|
|
|
|
Create `.env` file:
|
|
|
|
```bash
|
|
SLACK_BOT_TOKEN=xoxb-your-bot-token-here
|
|
SLACK_APP_TOKEN=xapp-your-app-token-here
|
|
```
|
|
|
|
Add to `.gitignore`:
|
|
|
|
```bash
|
|
echo ".env" >> .gitignore
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Minimal Working Code
|
|
|
|
```javascript
|
|
require('dotenv').config();
|
|
const { SocketModeClient } = require('@slack/socket-mode');
|
|
const { WebClient } = require('@slack/web-api');
|
|
|
|
const socketClient = new SocketModeClient({
|
|
appToken: process.env.SLACK_APP_TOKEN
|
|
});
|
|
|
|
const webClient = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
|
|
// Listen for app mentions (@mom do something)
|
|
socketClient.on('app_mention', async ({ event, ack }) => {
|
|
try {
|
|
// Acknowledge receipt
|
|
await ack();
|
|
|
|
console.log('Mentioned:', event.text);
|
|
console.log('Channel:', event.channel);
|
|
console.log('User:', event.user);
|
|
|
|
// Process with your agent
|
|
const response = await yourAgentFunction(event.text);
|
|
|
|
// Send response
|
|
await webClient.chat.postMessage({
|
|
channel: event.channel,
|
|
text: response
|
|
});
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
}
|
|
});
|
|
|
|
// Start the connection
|
|
(async () => {
|
|
await socketClient.start();
|
|
console.log('⚡️ Bot connected and listening!');
|
|
})();
|
|
|
|
// Your existing agent logic
|
|
async function yourAgentFunction(text) {
|
|
// Your code here
|
|
return "I processed: " + text;
|
|
}
|
|
```
|
|
|
|
**That's it. No web server. Just run it:**
|
|
|
|
```bash
|
|
node bot.js
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Listen to ALL Events (Not Just Mentions)
|
|
|
|
If you want to see every message in channels/DMs the bot is in:
|
|
|
|
```javascript
|
|
// Listen to all Slack events
|
|
socketClient.on('slack_event', async ({ event, body, ack }) => {
|
|
await ack();
|
|
|
|
console.log('Event type:', event.type);
|
|
console.log('Event data:', event);
|
|
|
|
if (event.type === 'message' && event.subtype === undefined) {
|
|
// Regular message (not bot message, not edited, etc.)
|
|
console.log('Message:', event.text);
|
|
console.log('Channel:', event.channel);
|
|
console.log('User:', event.user);
|
|
|
|
// Your logic here
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Common Operations
|
|
|
|
### Send a message
|
|
```javascript
|
|
await webClient.chat.postMessage({
|
|
channel: 'C12345', // or channel ID from event
|
|
text: 'Hello!'
|
|
});
|
|
```
|
|
|
|
### Send a DM
|
|
```javascript
|
|
// Open DM channel with user
|
|
const result = await webClient.conversations.open({
|
|
users: 'U12345' // user ID
|
|
});
|
|
|
|
// Send message to that DM
|
|
await webClient.chat.postMessage({
|
|
channel: result.channel.id,
|
|
text: 'Hey there!'
|
|
});
|
|
```
|
|
|
|
### List channels
|
|
```javascript
|
|
const channels = await webClient.conversations.list({
|
|
types: 'public_channel,private_channel'
|
|
});
|
|
console.log(channels.channels);
|
|
```
|
|
|
|
### Get channel members
|
|
```javascript
|
|
const members = await webClient.conversations.members({
|
|
channel: 'C12345'
|
|
});
|
|
console.log(members.members); // Array of user IDs
|
|
```
|
|
|
|
### Get user info
|
|
```javascript
|
|
const user = await webClient.users.info({
|
|
user: 'U12345'
|
|
});
|
|
console.log(user.user.name);
|
|
console.log(user.user.real_name);
|
|
```
|
|
|
|
### Join a channel
|
|
```javascript
|
|
await webClient.conversations.join({
|
|
channel: 'C12345'
|
|
});
|
|
```
|
|
|
|
### Upload a file
|
|
```javascript
|
|
await webClient.files.uploadV2({
|
|
channel_id: 'C12345',
|
|
file: fs.createReadStream('./file.pdf'),
|
|
filename: 'document.pdf',
|
|
title: 'My Document'
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Complete Example with Your Agent
|
|
|
|
```javascript
|
|
require('dotenv').config();
|
|
const { SocketModeClient } = require('@slack/socket-mode');
|
|
const { WebClient } = require('@slack/web-api');
|
|
|
|
const socketClient = new SocketModeClient({
|
|
appToken: process.env.SLACK_APP_TOKEN
|
|
});
|
|
|
|
const webClient = new WebClient(process.env.SLACK_BOT_TOKEN);
|
|
|
|
// Your existing agent/AI/whatever
|
|
class MyAgent {
|
|
async process(message, context) {
|
|
// Your complex logic here
|
|
// context has: user, channel, etc.
|
|
return `Processed: ${message}`;
|
|
}
|
|
}
|
|
|
|
const agent = new MyAgent();
|
|
|
|
// Handle mentions
|
|
socketClient.on('app_mention', async ({ event, ack }) => {
|
|
await ack();
|
|
|
|
try {
|
|
// Remove the @mention from text
|
|
const text = event.text.replace(/<@[A-Z0-9]+>/g, '').trim();
|
|
|
|
// Process with your agent
|
|
const response = await agent.process(text, {
|
|
user: event.user,
|
|
channel: event.channel
|
|
});
|
|
|
|
// Send response
|
|
await webClient.chat.postMessage({
|
|
channel: event.channel,
|
|
text: response
|
|
});
|
|
} catch (error) {
|
|
console.error('Error processing mention:', error);
|
|
|
|
// Send error message
|
|
await webClient.chat.postMessage({
|
|
channel: event.channel,
|
|
text: 'Sorry, something went wrong!'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Start
|
|
(async () => {
|
|
await socketClient.start();
|
|
console.log('⚡️ Agent connected to Slack!');
|
|
})();
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Available Event Types
|
|
|
|
You subscribed to these in step 4:
|
|
|
|
- `app_mention` - Someone @mentioned the bot
|
|
- `message` - Any message in a channel/DM the bot is in
|
|
|
|
Event object structure:
|
|
|
|
```javascript
|
|
{
|
|
type: 'app_mention' or 'message',
|
|
text: 'the message text',
|
|
user: 'U12345', // who sent it
|
|
channel: 'C12345', // where it was sent
|
|
ts: '1234567890.123456' // timestamp
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Advantages of Socket Mode
|
|
|
|
✅ **No web server needed** - just run your script
|
|
✅ **No public URL needed** - works behind firewall
|
|
✅ **No ngrok** - works on localhost
|
|
✅ **Auto-reconnect** - SDK handles connection drops
|
|
✅ **Event-driven** - just listen to callbacks
|
|
|
|
---
|
|
|
|
## 13. Disadvantages
|
|
|
|
❌ Can't distribute to Slack App Directory (only for your workspace)
|
|
❌ Script must be running to receive messages (unlike webhooks)
|
|
❌ Max 10 concurrent connections per app
|
|
|
|
---
|
|
|
|
## Important Notes
|
|
|
|
1. **You MUST call `ack()`** on every event or Slack will retry
|
|
2. **Bot token** (`xoxb-`) is for sending messages
|
|
3. **App token** (`xapp-`) is for receiving events via WebSocket
|
|
4. **Connection is persistent** - your script stays running
|
|
5. **No URL validation** needed (unlike HTTP webhooks)
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### "invalid_auth" error
|
|
- Check you're using the right tokens
|
|
- Bot token for WebClient, App token for SocketModeClient
|
|
|
|
### "missing_scope" error
|
|
- Make sure you added all 16 bot scopes
|
|
- Reinstall the app after adding scopes
|
|
|
|
### Not receiving events
|
|
- Check Socket Mode is enabled
|
|
- Check you subscribed to events in "Event Subscriptions"
|
|
- Make sure bot is in the channel (or use `channels:join`)
|
|
|
|
### Bot doesn't respond to mentions
|
|
- Must subscribe to `app_mention` event
|
|
- Bot must be installed to workspace
|
|
- Check `await ack()` is called
|
|
|
|
---
|
|
|
|
That's it. No HTTP server bullshit. Just WebSockets and callbacks.
|