Skip to main content
slack-expert Expert-level Slack platform development using Bolt SDK — build Slack apps with slash commands, interactive components (buttons, modals, shortcuts), home tabs, event subscriptions, workflows, Socket Mode, and Block Kit. Use for building Slack bots, workflow automation, and rich interactive Slack experiences.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/oimiragieo/agent-studio --skill slack-expert命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name slack-expert description Expert-level Slack platform development using Bolt SDK — build Slack apps with slash commands, interactive components (buttons, modals, shortcuts), home tabs, event subscriptions, workflows, Socket Mode, and Block Kit. Use for building Slack bots, workflow automation, and rich interactive Slack experiences. version 1.0.0 model sonnet invoked_by both user_invocable true tools ["Bash","Read","Write","Edit","WebFetch"] best_practices ["Always use Bolt SDK (not raw HTTP) for new Slack apps","Prefer Socket Mode for internal tools; HTTP for public apps","Validate request signatures before processing payloads","Use lazy listeners for long-running operations","Store OAuth tokens in a secure database, never env-vars for multi-workspace apps"] error_handling graceful streaming supported verified false lastVerifiedAt "2026-03-14T00:00:00.000Z" source builtin trust_score 100 provenance_sha 5d2e560139ce9700
Slack Expert Skill
Overview
Full-platform Slack development using the Bolt SDK. Covers interactive app development beyond basic messaging — slash commands, modals, shortcuts, home tabs, event subscriptions, workflow steps, and multi-workspace OAuth.
Related skill: slack-notifications — use that skill for simple one-way messaging/alerts. Use slack-expert when building interactive Slack apps or bots.
Requirements
Variable Purpose Required SLACK_BOT_TOKENBot OAuth token (xoxb-...) Yes (single workspace) SLACK_SIGNING_SECRETRequest signature verification Yes SLACK_APP_TOKENSocket Mode app-level token (xapp-...) Socket Mode only SLACK_CLIENT_IDOAuth client ID Multi-workspace only SLACK_CLIENT_SECRETOAuth client secret Multi-workspace only SLACK_STATE_SECRETOAuth state token Multi-workspace only
Quick Start — Bolt App (Node.js)
const { App } = require ('@slack/bolt' );
const app = new App ({
token : process.env .SLACK_BOT_TOKEN ,
signingSecret : process.env .SLACK_SIGNING_SECRET ,
socketMode : true ,
appToken : process.env .SLACK_APP_TOKEN ,
});
app. ( , ({ command, ack, respond }) => {
();
( );
});
app. ( , ({ body, ack, client }) => {
();
client. . ({
: body. ,
: confirmationModal,
});
});
app. ( , ({ event, say }) => {
({ : , : event. });
});
( () => {
app. (process. . || );
. ( );
})();
command
'/hello'
async
await
ack
await
respond
`Hello <@${command.user_id} >!`
action
'approve_button'
async
await
ack
await
views
open
trigger_id
trigger_id
view
event
'app_mention'
async
await
say
text
`Hello <@${event.user} >!`
thread_ts
ts
async
await
start
env
PORT
3000
console
log
'Bolt app running'
Core Concepts
Listeners
app.message (/deploy/ , async ({ message, say }) => {
await say ('Triggering deployment...' );
});
app.command ('/status' , async ({ ack, respond, command }) => {
await ack ();
respond ({ text : 'Checking status...' , response_type : 'ephemeral' });
const status = await fetchStatus ();
await respond ({ text : `Status: ${status} ` });
});
app.shortcut ('create_ticket' , async ({ shortcut, ack, client }) => {
await ack ();
await client.views .open ({
trigger_id : shortcut.trigger_id ,
view : ticketModal,
});
});
Modals (Views) const modal = {
type : 'modal' ,
callback_id : 'ticket_submission' ,
title : { type : 'plain_text' , text : 'Create Ticket' },
submit : { type : 'plain_text' , text : 'Submit' },
close : { type : 'plain_text' , text : 'Cancel' },
blocks : [
{
type : 'input' ,
block_id : 'title_block' ,
element : {
type : 'plain_text_input' ,
action_id : 'title_input' ,
placeholder : { type : 'plain_text' , text : 'Ticket title' },
},
label : { type : 'plain_text' , text : 'Title' },
},
{
type : 'input' ,
block_id : 'priority_block' ,
element : {
type : 'static_select' ,
action_id : 'priority_select' ,
options : [
{ text : { type : 'plain_text' , text : 'High' }, value : 'high' },
{ text : { type : 'plain_text' , text : 'Medium' }, value : 'medium' },
{ text : { type : 'plain_text' , text : 'Low' }, value : 'low' },
],
},
label : { type : 'plain_text' , text : 'Priority' },
},
],
};
app.view ('ticket_submission' , async ({ ack, view, client, body }) => {
await ack ();
const title = view.state .values .title_block .title_input .value ;
const priority = view.state .values .priority_block .priority_select .selected_option .value ;
await createTicket ({ title, priority, userId : body.user .id });
});
Block Kit — Rich Messages const richMessage = {
blocks : [
{
type : 'header' ,
text : { type : 'plain_text' , text : 'Deployment Ready' },
},
{
type : 'section' ,
fields : [
{ type : 'mrkdwn' , text : '*Service:*\napi-gateway' },
{ type : 'mrkdwn' , text : '*Version:*\nv1.2.3' },
{ type : 'mrkdwn' , text : '*Environment:*\nProduction' },
{ type : 'mrkdwn' , text : '*Requested by:*\n<@U1234567>' },
],
},
{ type : 'divider' },
{
type : 'actions' ,
block_id : 'deploy_actions' ,
elements : [
{
type : 'button' ,
text : { type : 'plain_text' , text : 'Approve' },
style : 'primary' ,
action_id : 'approve_deploy' ,
value : 'v1.2.3' ,
confirm : {
title : { type : 'plain_text' , text : 'Confirm Deploy' },
text : { type : 'mrkdwn' , text : 'Deploy *v1.2.3* to production?' },
confirm : { type : 'plain_text' , text : 'Deploy' },
deny : { type : 'plain_text' , text : 'Cancel' },
},
},
{
type : 'button' ,
text : { type : 'plain_text' , text : 'Reject' },
style : 'danger' ,
action_id : 'reject_deploy' ,
value : 'v1.2.3' ,
},
],
},
],
};
App Home Tab
app.event ('app_home_opened' , async ({ event, client }) => {
if (event.tab !== 'home' ) return ;
await client.views .publish ({
user_id : event.user ,
view : {
type : 'home' ,
blocks : [
{
type : 'header' ,
text : { type : 'plain_text' , text : 'My App Dashboard' },
},
{
type : 'section' ,
text : { type : 'mrkdwn' , text : 'Welcome to your app home!' },
accessory : {
type : 'button' ,
text : { type : 'plain_text' , text : 'Refresh' },
action_id : 'refresh_home' ,
},
},
],
},
});
});
Lazy Listeners (Long-Running Operations)
app.command (
'/report' ,
async ({ command, ack, respond, say }) => {
await ack ();
},
async ({ command, respond }) => {
const report = await generateReport (command.text );
await respond ({ text : report, response_type : 'in_channel' });
}
);
Socket Mode (Internal Tools)
const app = new App ({
token : process.env .SLACK_BOT_TOKEN ,
signingSecret : process.env .SLACK_SIGNING_SECRET ,
socketMode : true ,
appToken : process.env .SLACK_APP_TOKEN ,
});
OAuth (Multi-Workspace Apps) const { App , ExpressReceiver } = require ('@slack/bolt' );
const { FileInstallationStore } = require ('@slack/oauth' );
const receiver = new ExpressReceiver ({
signingSecret : process.env .SLACK_SIGNING_SECRET ,
clientId : process.env .SLACK_CLIENT_ID ,
clientSecret : process.env .SLACK_CLIENT_SECRET ,
stateSecret : process.env .SLACK_STATE_SECRET ,
scopes : ['chat:write' , 'channels:read' , 'commands' ],
installationStore : new FileInstallationStore (),
});
const app = new App ({ receiver });
Workflow Steps (Deprecated → Automations)
const ws = new WorkflowStep ('copy_review' , {
edit : async ({ ack, step, configure }) => {
await ack ();
await configure ({ blocks : editBlocks });
},
save : async ({ ack, step, view, update }) => {
await ack ();
const { values } = view.state ;
await update ({ inputs : {}, outputs : [] });
},
execute : async ({ step, complete, fail }) => {
try {
await complete ({ outputs : {} });
} catch (err) {
await fail ({ error : { message : err.message } });
}
},
});
app.step (ws);
Scopes Reference Capability Required Scopes Post messages chat:writeRead channels channels:read, groups:readRead messages channels:history, groups:historySlash commands commandsReact to messages reactions:writeUpload files files:writeRead user profiles users:read, users:read.emailHome tab im:historyBot mentions app_mentions:readDMs im:write, mpim:write
Common Patterns
Approval Workflow app.command ('/approve-request' , async ({ command, ack, client }) => {
await ack ();
await client.chat .postMessage ({
channel : '#approvals' ,
text : `<@${command.user_id} > requests approval` ,
blocks : buildApprovalBlocks (command),
});
});
app.action ('approve_action' , async ({ action, body, ack, client }) => {
await ack ();
const requestId = action.value ;
await markApproved (requestId, body.user .id );
await client.chat .update ({
channel : body.channel .id ,
ts : body.message .ts ,
text : `Approved by <@${body.user.id} >` ,
blocks : buildApprovedBlocks (requestId),
});
});
User Lookup async function lookupUser (client, email ) {
const result = await client.users .lookupByEmail ({ email });
return result.user .id ;
}
Error Handling app.error (async error => {
console .error ('Global error handler' , error);
await app.client .chat .postMessage ({
channel : process.env .ADMIN_CHANNEL ,
text : `App error: ${error.message} ` ,
});
});
Security
Always verify SLACK_SIGNING_SECRET to authenticate requests from Slack
Never log raw payloads (may contain user PII)
Use response_type: 'ephemeral' for sensitive command responses
Store tokens in encrypted secrets manager, not .env in production
Scope bot tokens to minimum required permissions
Rotate signing secrets if compromised — requires reinstalling the app
Debugging
DEBUG=bolt:* node app.js
slack run
slack triggers create --trigger-def "triggers/trigger.json"
slack events replay
Related