Claude Code से Slack Bot बनाएं: triage, incident response और daily reports
Bolt JS, Socket Mode, slash commands, सुरक्षा, tests और production checklist के साथ Slack Bot guide.
Bot को सिर्फ notification तक सीमित न रखें
Slack Bot एक app है जो Slack में messages, slash commands, buttons और modal submissions पर प्रतिक्रिया देता है। Bolt for JavaScript Slack का official Node.js framework है, जो इन events को सही handler function तक पहुंचाता है। आसान भाषा में, Bolt वह ढांचा है जिससे आप कह सकते हैं: “जब यह Slack event आए, तो यह function चलाओ.”
Claude Code के साथ आम गलती यह है कि हम सिर्फ alert भेजने वाला छोटा bot बना देते हैं। असली उपयोगी bot channel के शोर को structured काम में बदलता है: support triage, incident की पहली response, daily report, approval और publish से पहले checks। Masa के workflow में पहला notification bot कुछ दिन उपयोगी लगा, लेकिन वह यह नहीं बताता था कि owner कौन है, urgency क्या है और item बंद हुआ या नहीं।
यह guide 3 जून 2026 को Slack की official docs से verify की गई है: Bolt for JavaScript, Bolt command listeners, Socket Mode, slash commands, Events API, chat.postMessage, request verification और tokens। संबंधित ClaudeCodeLab guides के लिए webhook implementation, API development, secrets management और workflow automation देखें।
Use case पहले तय करें
अगर आप Claude Code से सिर्फ “Slack bot बनाओ” कहेंगे, तो अक्सर एक पतला demo मिलेगा। पहले तय करें कि Slack में entry point क्या होगा, कौन से fields लेने हैं, bot क्या reply करेगा और failure पर क्या होगा।
| Use case | Slack entry | Bot क्या करेगा | ध्यान देने वाली बात |
|---|---|---|---|
| Support triage | /triage add, modal | title, severity, requester और channel notification को standard बनाएगा | users customer names, secrets या private URLs paste कर सकते हैं |
| Incident first response | @bot mention, button | first checklist देगा और thread में context रखेगा | bot बहुत confident न लगे; escalation rule होना चाहिए |
| Daily report | /triage list, scheduled job | open items को daily या report के लिए summarize करेगा | बहुत लंबा message Slack में पढ़ना कठिन हो सकता है |
| Article या landing page check | Slash Command | CTA, internal links, owner और publish URL check करेगा | draft URL और production URL mix हो सकते हैं |
Architecture जानबूझकर छोटी रखी गई है।
flowchart LR
A["Slack user"] --> B["/triage or @mention"]
B --> C["Bolt listener"]
C --> D["Triage logic"]
D --> E["chat.postMessage"]
D --> F["Modal and button"]
Claude Code को ऐसा prompt दें:
Bolt for JavaScript से Slack Bot implement करें।
Goal support triage है।
Include करें:
- Environment variables से Socket Mode और Request URL switch
- /triage add, /triage list, /triage modal
- Modal input और view_submission handling
- Mark done button
- app_mention के लिए help reply
- scopes, secrets और request verification की explanation
- triage.ts के unit tests
Pseudo APIs मत इस्तेमाल करें। Copy-paste runnable TypeScript लिखें।
Socket Mode या Request URL
Socket Mode में आपका app Slack से WebSocket connection खोलकर events receive करता है। Local development में public HTTPS endpoint की जरूरत नहीं होती। यह prototype, internal tool और firewall के पीछे की PoC के लिए अच्छा है। Slack docs बताती हैं कि Socket Mode के लिए xapp- से शुरू होने वाला app-level token चाहिए।
Request URL में Slack आपके HTTPS endpoint पर HTTP POST भेजता है। Production apps में यह common pattern है। HTTP mode में Signing Secret से request signature verify करें। Bolt यह verification कर सकता है, लेकिन design note में यह साफ लिखें कि deprecated verification token पर निर्भर नहीं रहना है।
| Mode | कब उपयोग करें | जरूरी setup | Pitfall |
|---|---|---|---|
| Socket Mode | Local dev, internal PoC | SLACK_APP_TOKEN, connections:write | Process बंद हुआ तो events नहीं मिलेंगे; Marketplace के लिए ideal नहीं |
| Request URL | Production HTTP deploy | HTTPS URL, SLACK_SIGNING_SECRET | धीमा ack() Slack timeout बन जाता है |
पहले Socket Mode से test करें, फिर production channels या external users के लिए Request URL पर जाएं। नीचे का code SLACK_SOCKET_MODE=true से switch करता है।
Slack manifest और scopes
Manifest को repository में रखें ताकि dev और production app अलग-अलग न हो जाएं। यहां scopes कम रखे गए हैं: commands slash command के लिए, chat:write messages भेजने के लिए और app_mentions:read bot mention लेने के लिए।
display_information:
name: Claude Triage Bot
description: Collect triage requests from Slack
background_color: "#2E2A24"
features:
bot_user:
display_name: Claude Triage
always_online: false
slash_commands:
- command: /triage
description: Add or list triage items
usage_hint: "add Fix login | list | modal"
should_escape: true
oauth_config:
scopes:
bot:
- app_mentions:read
- chat:write
- commands
settings:
event_subscriptions:
bot_events:
- app_mention
interactivity:
is_enabled: true
socket_mode_enabled: true
org_deploy_enabled: false
token_rotation_enabled: false
channels:history या groups:history शुरुआत में न जोड़ें। History scopes तभी जोड़ें जब bot सच में channel history पढ़े और privacy review हो चुका हो।
Local project बनाना
Node.js 20 या नया version मानकर चलें।
mkdir claude-slack-triage-bot
cd claude-slack-triage-bot
npm init -y
npm install @slack/bolt @slack/types dotenv
npm install -D typescript tsx vitest @types/node
npm pkg set type=module
npm pkg set scripts.dev="tsx watch src/app.ts"
npm pkg set scripts.build="tsc"
npm pkg set scripts.start="node dist/app.js"
npm pkg set scripts.test="vitest run"
mkdir src tests
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}
.env.example बनाएं। असली values .env या hosting secret manager में रखें, Git में नहीं।
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_SOCKET_MODE=true
SLACK_APP_TOKEN=xapp-your-app-level-token
TRIAGE_CHANNEL_ID=C0123456789
PORT=3000
xoxb- bot token है। xapp- Socket Mode के लिए app-level token है। Signing Secret यह साबित करने में मदद करता है कि HTTP request Slack से आई है। Claude Code को असली values नहीं चाहिए; उसे variable names, expected behavior और logging rules चाहिए।
Copy-paste Bolt implementation
पहले Slack से independent logic को src/triage.ts में रखें।
// src/triage.ts
import type { KnownBlock, View } from "@slack/types";
export type Severity = "low" | "normal" | "high";
export interface Ticket {
id: string;
channelId: string;
title: string;
createdBy: string;
severity: Severity;
status: "open" | "done";
createdAt: string;
}
const tickets = new Map<string, Ticket>();
export function resetForTest() {
tickets.clear();
}
export function parseTriageText(text: string) {
const [actionRaw, ...rest] = text.trim().split(/\s+/);
return { action: actionRaw || "help", title: rest.join(" ").trim() };
}
export function addTicket(input: {
channelId: string;
title: string;
createdBy: string;
severity?: Severity;
}) {
const ticket: Ticket = {
id: `triage_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
channelId: input.channelId,
title: input.title,
createdBy: input.createdBy,
severity: input.severity ?? "normal",
status: "open",
createdAt: new Date().toISOString(),
};
tickets.set(ticket.id, ticket);
return ticket;
}
export function completeTicket(id: string) {
const ticket = tickets.get(id);
if (!ticket) return undefined;
const updated: Ticket = { ...ticket, status: "done" };
tickets.set(id, updated);
return updated;
}
export function formatTicketList(channelId: string) {
const open = [...tickets.values()].filter((ticket) => {
return ticket.channelId === channelId && ticket.status === "open";
});
if (open.length === 0) return "No open triage items.";
return open
.map((ticket, index) => {
return `${index + 1}. [${ticket.severity}] ${ticket.title} by <@${ticket.createdBy}>`;
})
.join("\n");
}
export function ticketBlocks(ticket: Ticket): KnownBlock[] {
return [
{
type: "section",
text: {
type: "mrkdwn",
text: `*${ticket.title}*\nSeverity: ${ticket.severity}\nOwner: <@${ticket.createdBy}>`,
},
},
{
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "Mark done" },
action_id: "triage_done",
value: ticket.id,
},
],
},
];
}
export function modalView(): View {
return {
type: "modal",
callback_id: "triage_modal_submit",
title: { type: "plain_text", text: "New triage" },
submit: { type: "plain_text", text: "Create" },
close: { type: "plain_text", text: "Cancel" },
blocks: [
{
type: "input",
block_id: "title_block",
label: { type: "plain_text", text: "What needs attention?" },
element: {
type: "plain_text_input",
action_id: "title_input",
min_length: 3,
max_length: 120,
},
},
{
type: "input",
block_id: "severity_block",
label: { type: "plain_text", text: "Severity" },
element: {
type: "static_select",
action_id: "severity_input",
initial_option: {
text: { type: "plain_text", text: "Normal" },
value: "normal",
},
options: [
{ text: { type: "plain_text", text: "High" }, value: "high" },
{ text: { type: "plain_text", text: "Normal" }, value: "normal" },
{ text: { type: "plain_text", text: "Low" }, value: "low" },
],
},
},
],
};
}
अब Bolt listeners जोड़ें।
// src/app.ts
import "dotenv/config";
import { App, LogLevel } from "@slack/bolt";
import {
addTicket,
completeTicket,
formatTicketList,
modalView,
parseTriageText,
ticketBlocks,
type Severity,
} from "./triage.js";
const socketMode = process.env.SLACK_SOCKET_MODE === "true";
const required = ["SLACK_BOT_TOKEN", socketMode ? "SLACK_APP_TOKEN" : "SLACK_SIGNING_SECRET"];
for (const key of required) {
if (!process.env[key]) throw new Error(`Missing environment variable: ${key}`);
}
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
socketMode,
appToken: process.env.SLACK_APP_TOKEN,
logLevel: LogLevel.INFO,
});
app.command("/triage", async ({ ack, command, respond, client }) => {
await ack();
const parsed = parseTriageText(command.text);
if (parsed.action === "add") {
if (!parsed.title) {
await respond("Usage: `/triage add Fix login redirect`");
return;
}
const ticket = addTicket({
channelId: command.channel_id,
title: parsed.title,
createdBy: command.user_id,
severity: "normal",
});
await respond({
response_type: "in_channel",
text: `Triage item added: ${ticket.title}`,
blocks: ticketBlocks(ticket),
});
return;
}
if (parsed.action === "list") {
await respond({ response_type: "ephemeral", text: formatTicketList(command.channel_id) });
return;
}
if (parsed.action === "modal") {
await client.views.open({ trigger_id: command.trigger_id, view: modalView() });
return;
}
await respond("Usage: `/triage add ...`, `/triage list`, or `/triage modal`");
});
app.view("triage_modal_submit", async ({ ack, view, body, client }) => {
const titleState = view.state.values.title_block.title_input;
const severityState = view.state.values.severity_block.severity_input;
const title = titleState.type === "plain_text_input" ? titleState.value?.trim() : "";
const severity =
severityState.type === "static_select"
? severityState.selected_option?.value ?? "normal"
: "normal";
if (!title) {
await ack({ response_action: "errors", errors: { title_block: "Please enter a title." } });
return;
}
await ack();
const channelId = process.env.TRIAGE_CHANNEL_ID ?? "modal-only";
const ticket = addTicket({
channelId,
title,
createdBy: body.user.id,
severity: severity as Severity,
});
if (process.env.TRIAGE_CHANNEL_ID) {
await client.chat.postMessage({
channel: process.env.TRIAGE_CHANNEL_ID,
text: `New triage item: ${ticket.title}`,
blocks: ticketBlocks(ticket),
});
}
});
app.action("triage_done", async ({ ack, action, respond }) => {
await ack();
const value = action.type === "button" ? action.value : undefined;
if (!value) return;
const ticket = completeTicket(value);
await respond(ticket ? `Closed: ${ticket.title}` : "Ticket not found.");
});
app.event("app_mention", async ({ event, say }) => {
await say({
thread_ts: event.ts,
text: "Use `/triage add ...`, `/triage list`, or `/triage modal`.",
});
});
const port = Number(process.env.PORT ?? 3000);
if (socketMode) {
await app.start();
} else {
await app.start(port);
}
app.logger.info(`Slack bot started in ${socketMode ? "Socket Mode" : `HTTP mode on ${port}`}`);
Slack connection के बिना unit tests जोड़ें।
// tests/triage.test.ts
import { beforeEach, describe, expect, it } from "vitest";
import {
addTicket,
completeTicket,
formatTicketList,
parseTriageText,
resetForTest,
} from "../src/triage";
describe("triage helpers", () => {
beforeEach(() => resetForTest());
it("parses slash command text", () => {
expect(parseTriageText("add Fix login")).toEqual({
action: "add",
title: "Fix login",
});
});
it("lists only open tickets", () => {
const ticket = addTicket({
channelId: "C123",
title: "Review pricing CTA",
createdBy: "U123",
severity: "high",
});
expect(formatTicketList("C123")).toContain("[high] Review pricing CTA");
completeTicket(ticket.id);
expect(formatTicketList("C123")).toBe("No open triage items.");
});
});
Run करें:
npm run test
npm run build
npm run dev
Socket Mode में npm run dev चलने दें और Slack में /triage add Test from Slack लिखें। Request URL mode में app deploy करें और https://example.com/slack/events को slash commands, interactivity और event subscriptions में set करें।
Pitfalls और सुरक्षा
Slow work से पहले ack() call करें। Command, button और modal को database या external API से पहले receive confirmation चाहिए।
trigger_id को short-lived मानें। Modal पहले खोलें, फिर detail validation view_submission में करें।
Permission bugs को सिर्फ code में debug न करें। Missing chat:write, bot का channel में invite न होना, या missing app_mention Slack settings में ठीक होते हैं।
Modes mix न करें। Socket Mode को SLACK_APP_TOKEN चाहिए; Request URL को HTTPS और SLACK_SIGNING_SECRET चाहिए। Startup पर selected mode log करें।
Secrets कभी expose न करें। xoxb-, xapp-, या Signing Secret को Claude Code prompts, screenshots, logs, fixtures या articles में paste न करें। Leak हो तो तुरंत rotate करें।
Bot को बहुत ज्यादा निर्णय न लेने दें। Support और incident में bot को next checks और escalation rule देना चाहिए, root cause invent नहीं करना चाहिए।
Production checklist
- Manifest scopes code में इस्तेमाल APIs से match करते हैं।
/triageकिसी दूसरी installed app से conflict नहीं करता।- Modals और buttons के लिए Interactivity enabled है।
- Bot destination channel में invited है।
SLACK_BOT_TOKEN,SLACK_APP_TOKEN, औरSLACK_SIGNING_SECRETsecrets में stored हैं।npm run testऔरnpm run buildpass होते हैं।- Request URL HTTPS और Slack signature verification इस्तेमाल करता है।
- Socket Mode में process monitoring और restart है।
- Logs में tokens, unmasked personal data, customer names या private URLs नहीं हैं।
- Channel topic बताता है कि bot fail होने पर escalation किसे जाएगी।
Claude Code का scope अलग-अलग रखें: manifest और scopes proposal, Slack-independent logic, Bolt listeners, unit tests और deployment checklist। इससे Slack configuration गलती और code bug अलग दिखते हैं।
ClaudeCodeLab ऐसे internal bots, webhooks, APIs और secrets workflows को training और consultation में कवर करता है। अगर reusable CLAUDE.md rules, pre-publish review templates और team checklists चाहिए, तो इस bot pattern को templates और products के साथ जोड़ें ताकि यह demo से आगे जाकर operations और revenue में मदद करे।
Test करने के बाद परिणाम
सबसे तेज रास्ता एक बड़ा Slack Bot एक साथ generate करना नहीं था। स्थिर तरीका यह रहा: पहले manifest और scopes freeze करें, फिर triage.ts जैसी pure logic लिखें, और आखिर में Bolt listeners को Slack admin settings से जोड़ें। Claude Code सबसे अच्छा तब काम करता है जब code, permissions, secrets, tests और production checklist को एक reviewable work unit माना जाता है।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.