AINode.jsAutomation
Slack Bot with GPT-4o Integration
TT
TopicTrick Team
Slack Bot with GPT-4o Integration
Your team's knowledge is scattered across Slack threads, docs, and people's heads. This Slack bot brings GPT-4o directly into your workspace — answering questions, summarizing threads, drafting messages, and running AI commands via slash commands.
This is Tool 15 of the Build 50 AI Automation Tools course.
What You'll Build
- Mention
@YourBotin any channel to ask questions - DM the bot for private AI assistance
/summarize— summarize the current channel's recent activity/draft— draft a message or document- Maintain conversation history per thread
Setup
bash
mkdir slack-ai-bot && cd slack-ai-bot
npm init -y
npm install @slack/bolt openai dotenvbash
# .env
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
OPENAI_API_KEY=sk-your-key-here
PORT=3000The Bot
js
// src/bot.js
import 'dotenv/config';
import { App } from '@slack/bolt';
import OpenAI from 'openai';
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// In-memory conversation history per thread
const threadHistories = new Map();
function getHistory(threadTs) {
if (!threadHistories.has(threadTs)) {
threadHistories.set(threadTs, []);
}
return threadHistories.get(threadTs);
}
async function askGPT(userMessage, threadTs, systemPrompt) {
const history = getHistory(threadTs);
history.push({ role: 'user', content: userMessage });
const messages = [
{
role: 'system',
content: systemPrompt || `You are a helpful AI assistant in a Slack workspace. Be concise and direct — Slack messages should be brief. Use *bold* for emphasis, bullet points for lists. Do not use markdown headers.`,
},
...history.slice(-10), // Last 10 messages for context
];
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
temperature: 0.7,
max_tokens: 1000,
});
const reply = response.choices[0].message.content;
history.push({ role: 'assistant', content: reply });
threadHistories.set(threadTs, history);
return reply;
}
// Respond to @mentions in channels
app.event('app_mention', async ({ event, say }) => {
// Remove the bot mention from the message
const userMessage = event.text.replace(/<@[A-Z0-9]+>/g, '').trim();
if (!userMessage) {
await say({ text: 'Hi! How can I help you?', thread_ts: event.ts });
return;
}
const threadTs = event.thread_ts || event.ts;
const reply = await askGPT(userMessage, threadTs);
await say({ text: reply, thread_ts: event.ts });
});
// Respond to direct messages
app.message(async ({ message, say }) => {
if (message.subtype || !message.text) return;
const reply = await askGPT(message.text, message.ts);
await say(reply);
});
// /summarize slash command
app.command('/summarize', async ({ command, ack, client, say }) => {
await ack();
const { data } = await client.conversations.history({
channel: command.channel_id,
limit: 50,
});
const messages = data.messages
.filter(m => !m.bot_id)
.reverse()
.map(m => `${m.user || 'user'}: ${m.text}`)
.join('\n');
const summary = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Summarize this Slack channel conversation in 3-5 bullet points. Be concise.' },
{ role: 'user', content: messages },
],
temperature: 0.3,
});
await say({
text: `*Channel Summary:*\n${summary.choices[0].message.content}`,
channel: command.channel_id,
});
});
// /draft slash command
app.command('/draft', async ({ command, ack, say }) => {
await ack();
const topic = command.text;
if (!topic) return say('Usage: /draft <what to draft>');
const draft = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Draft a clear, professional Slack message on the given topic. Keep it under 200 words.' },
{ role: 'user', content: topic },
],
temperature: 0.7,
});
await say({
text: `*Draft:*\n${draft.choices[0].message.content}`,
channel: command.channel_id,
});
});
// /explain slash command
app.command('/explain', async ({ command, ack, say }) => {
await ack();
const topic = command.text;
if (!topic) return say('Usage: /explain <topic or term>');
const explanation = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Explain the topic clearly and concisely for a technical audience. Use analogies where helpful. Keep it under 150 words.' },
{ role: 'user', content: topic },
],
temperature: 0.5,
});
await say(`*${topic}:*\n${explanation.choices[0].message.content}`);
});
(async () => {
await app.start(process.env.PORT ?? 3000);
console.log(`Slack AI Bot running on port ${process.env.PORT ?? 3000}`);
})();Local Development with ngrok
Slack requires a public HTTPS URL for event subscriptions. Use ngrok for development:
bash
npm install -g ngrok
ngrok http 3000
# Copy the https://xxx.ngrok.io URLIn your Slack app dashboard:
- Event Subscriptions → Request URL:
https://xxx.ngrok.io/slack/events - Slash Commands → Request URL:
https://xxx.ngrok.io/slack/events - Click Verify — should show "Verified"
Adding Knowledge Base Context
Make the bot aware of company-specific knowledge:
js
const SYSTEM_PROMPT = `You are an AI assistant for Acme Corp's engineering team.
Company context:
- We use React + Node.js + PostgreSQL
- Our CI/CD runs on GitHub Actions deploying to AWS ECS
- The on-call rotation is managed via PagerDuty
- Slack channels: #engineering (general), #deployments (release updates), #incidents
Answer questions using this context where relevant. For code questions, use our tech stack.`;
// Pass to askGPT as the systemPrompt parameterProduction Deployment
bash
# Deploy to Railway
railway up
# Or run with PM2
pm2 start src/bot.js --name slack-bot
pm2 save
pm2 startupUpdate your Slack app's event subscription URL to your production URL.
Build 50 AI Automation Tools — Tool 15 of 50
Your Slack AI bot is live. Continue to Tool 16 to build an AI SMS response bot with Twilio.
Summary
- Slack Bolt handles authentication, event routing, and slash commands with minimal boilerplate
- In-memory thread history enables multi-turn conversations within Slack threads
- Slash commands give power users quick access to specific AI capabilities
- ngrok enables local development without deploying — essential for fast Slack app iteration
- For production, move thread history to Redis so history persists across bot restarts
Continue to Tool 16: AI SMS Response Bot with Twilio →
