# 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.