关于 slack-bot
通过 Incoming Webhooks 或 Slack Web API 向 Slack 发送消息和格式化内容的 Skill,提供 Block Kit 公告、报告、线程回复、文件上传等示例。
主要功能
- 01Webhook 与 Web API 发消息
- 02Block Kit 消息排版
- 03线程回复与消息更新
- 04频道列表与文件上传
- 05Slack mrkdwn 格式参考
适用场景
- 01发布格式化的营销报告
- 02在频道分享公告
- 03回复已有消息线程
- 04向 Slack 上传报告文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ openclaudia/ openclaudia-skills --skill slack-bot安装后在 Claude Code 中输入/slack-bot确认已加载。
详细介绍
Slack 机器人
使用 Incoming Webhooks 或 Slack Web API 向 Slack 频道发送消息和富内容。 使用 Block Kit 构建格式化公告、营销报告、指标仪表板和社区动态。
前提条件
需要在 .env、.env.local 或 ~/.claude/.env.global 中设置 SLACK_WEBHOOK_URL 或 SLACK_BOT_TOKEN。
source ~/.claude/.env.global 2>/dev/null
source .env 2>/dev/null
source .env.local 2>/dev/null
if [ -n "$SLACK_WEBHOOK_URL" ]; then
echo "SLACK_WEBHOOK_URL is set. Webhook mode available."
elif [ -n "$SLACK_BOT_TOKEN" ]; then
echo "SLACK_BOT_TOKEN is set. Web API mode available."
else
echo "Neither SLACK_WEBHOOK_URL nor SLACK_BOT_TOKEN is set."
echo "See the Setup Guide below to configure Slack credentials."
fi
如果这两个变量都未设置,请指示用户参阅下方的设置指南部分。
设置指南
选项 A:Incoming Webhook(简单)
Incoming Webhooks 是发布消息最快的方式。它们不需要 OAuth 作用域,并且仅限用于单个频道。
- 前往 https://api.slack.com/apps,然后点击 Create New App > From scratch。
- 为应用命名(例如,“Marketing Bot”),并选择工作区。
- 在左侧边栏中,点击 Incoming Webhooks 并将其切换为 On。
- 点击底部的 Add New Webhook to Workspace。
- 选择要发布消息的频道,然后点击 Allow。
- 复制 Webhook URL(以
https://hooks.slack.com/services/...开头)。 - 将其添加到环境中:
echo 'SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxxx' >> .env
限制: 每个频道只能使用一个 webhook。无法读取消息、列出频道或以编程方式回复讨论串(必须从之前的 API 响应中获取 thread_ts)。
选项 B:Bot Token(功能完整)
Bot token 可访问完整的 Slack Web API:向机器人所在的任意频道发布消息、回复讨论串、列出频道、上传文件等。
- 前往 https://api.slack.com/apps,然后点击 Create New App > From scratch。
- 为应用命名并选择工作区。
- 在左侧边栏中,点击 OAuth & Permissions。
- 在 Bot Token Scopes 下添加以下作用域:
chat:write- 发布消息chat:write.public- 无需加入频道即可向频道发布消息channels:read- 列出公开频道files:write- 上传文件(可选,用于图片/报告)reactions:write- 添加表情回应(可选)
- 点击顶部的 Install to Workspace 并授权。
- 复制 Bot User OAuth Token(以
xoxb-开头)。 - 将其添加到环境中:
echo 'SLACK_BOT_TOKEN=xoxb-your-token-here' >> .env
- 将机器人邀请到需要发布消息的频道:在每个频道中输入
/invite @YourBotName。
可选: 设置默认频道以便使用:
echo 'SLACK_DEFAULT_CHANNEL=#marketing' >> .env
方法 1:Incoming Webhooks
发送简单文本消息
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello from the marketing bot!"
}'
发送带用户名和图标覆盖设置的消息
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"text": "New blog post published!",
"username": "Marketing Bot",
"icon_emoji": ":mega:"
}'
使用 Block Kit 发送消息(Webhook)
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "New Product Launch"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Product X* is now live! Check out the announcement."
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Read the full announcement on our blog."
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Read More"
},
"url": "https://example.com/blog/launch"
}
}
]
}'
方法 2:Slack Web API(Bot Token)
Web API 可全面控制消息传递、讨论串、频道管理等。
API 基础地址
所有请求都发送至 https://slack.com/api/,并携带请求头 Authorization: Bearer {SLACK_BOT_TOKEN}。
向频道发布消息
curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"channel": "#marketing",
"text": "Weekly metrics report is ready!",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Weekly Marketing Metrics"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Here are the numbers for this week."
}
}
]
}'
响应中包含一个 ts(时间戳)字段,用于标识消息。保存此值以便回复讨论串:
# Post and capture the message timestamp for threading
RESPONSE=$(curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"channel": "#marketing",
"text": "Thread parent message"
}')
MESSAGE_TS=$(echo "$RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin).get('ts',''))")
echo "Message timestamp: $MESSAGE_TS"
回复讨论串
使用 thread_ts 参数在现有讨论串中回复:
curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"channel\": \"#marketing\",
\"thread_ts\": \"${MESSAGE_TS}\",
\"text\": \"This is a threaded reply with additional details.\"
}"
若还要将回复广播到频道(使其也显示在主对话中),请添加 "reply_broadcast": true。
更新现有消息
curl -s -X POST "https://slack.com/api/chat.update" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"channel\": \"#marketing\",
\"ts\": \"${MESSAGE_TS}\",
\"text\": \"Updated message content.\",
\"blocks\": []
}"
删除消息
curl -s -X POST "https://slack.com/api/chat.delete" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"channel\": \"#marketing\",
\"ts\": \"${MESSAGE_TS}\"
}"
列出公开频道
可用于查找要发布消息的频道:
curl -s "https://slack.com/api/conversations.list?types=public_channel&limit=100" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for ch in data.get('channels', []):
members = ch.get('num_members', 0)
print(f\"#{ch['name']} | Members: {members} | ID: {ch['id']}\")
"
向频道上传文件
curl -s -X POST "https://slack.com/api/files.uploadV2" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-F "[email protected]" \
-F "filename=weekly-report.pdf" \
-F "channel_id=C0123456789" \
-F "initial_comment=Here is this week's marketing report."
添加表情回应
curl -s -X POST "https://slack.com/api/reactions.add" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"channel\": \"C0123456789\",
\"timestamp\": \"${MESSAGE_TS}\",
\"name\": \"white_check_mark\"
}"
Slack Block Kit 参考
Block Kit 是 Slack 的 UI 框架,用于构建丰富的交互式消息。消息由一个区块数组组成,每个区块都有特定的类型和结构。
Block 类型
| Block 类型 | 用途 | 支持 mrkdwn |
|---|---|---|
header | 大号粗体标题文本 | 否(仅限 plain_text) |
section | 主要内容 Block,包含文本和可选的附加元素 | 是 |
divider | 水平分隔线 | 不适用 |
image | 带替代文本的全宽图片 | 不适用 |
context | 小号、浅色文本和图片(用于元数据、时间戳) | 是 |
actions | 交互元素行(按钮、选择器、日期选择器) | 不适用 |
rich_text | 高级格式文本(列表、引用、代码块) | 不适用 |
Header Block
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Weekly Marketing Report",
"emoji": true
}
}
Section Block
纯文本 Section:
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Traffic is up 23%* this week compared to last week.\nOrganic search drove most of the growth."
}
}
带字段的 Section(两列布局):
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Visitors*\n12,450"
},
{
"type": "mrkdwn",
"text": "*Signups*\n342"
},
{
"type": "mrkdwn",
"text": "*MRR*\n$28,500"
},
{
"type": "mrkdwn",
"text": "*Churn*\n1.2%"
}
]
}
带按钮附加元素的 Section:
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "New blog post: *How We Grew 10x in 6 Months*"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Read Post"
},
"url": "https://example.com/blog/growth-story",
"action_id": "read_blog_post"
}
}
带图片附加元素的 Section:
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*New Feature: Dark Mode*\nOur most requested feature is finally here."
},
"accessory": {
"type": "image",
"image_url": "https://example.com/images/dark-mode-preview.png",
"alt_text": "Dark mode preview"
}
}
Divider Block
{
"type": "divider"
}
Image Block
{
"type": "image",
"image_url": "https://example.com/images/chart.png",
"alt_text": "Weekly traffic chart",
"title": {
"type": "plain_text",
"text": "Traffic Overview"
}
}
Context Block
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Posted by *Marketing Team* | Feb 10, 2026"
},
{
"type": "image",
"image_url": "https://example.com/logo-small.png",
"alt_text": "Company logo"
}
]
}
Actions Block(按钮)
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Approve"
},
"style": "primary",
"action_id": "approve_action",
"value": "approved"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Reject"
},
"style": "danger",
"action_id": "reject_action",
"value": "rejected"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Details"
},
"url": "https://example.com/details",
"action_id": "view_details"
}
]
}
按钮样式:"primary"(绿色)、"danger"(红色),或省略以使用默认样式(灰色)。
关于交互性的说明: 带有 action_id(无 url)的按钮需要在 Slack App 设置的 Interactivity & Shortcuts 下配置 Request URL,才能接收按钮点击负载。带有 url 字段的按钮会直接打开链接,无需后端。
Block Kit 限制
| 限制 | 值 |
|---|---|
| 每条消息的 Block 数量 | 50 |
| 每个文本 Block 的字符数 | 3,000 |
| 每个 header 的字符数 | 150 |
| 每个 Section 的字段数 | 10 |
| 每个 actions Block 的元素数 | 25 |
| 每个 context Block 的元素数 | 10 |
Block Kit 构建器
使用可视化构建器,在编写代码前设计并预览消息: https://app.slack.com/block-kit-builder
Slack mrkdwn 格式指南
Slack 使用一种名为 mrkdwn 的 Markdown 变体。它与标准 Markdown 有几处不同。
| 格式 | 语法 | 示例 |
|---|---|---|
| 粗体 | *text* | 粗体文本 |
| 斜体 | _text_ | 斜体文本 |
| 删除线 | ~text~ | |
| 代码(行内) | `text` | 行内代码 |
| 代码块 | ```text``` | 多行代码块 |
| 引用块 | >text | 引用文本 |
| 链接 | <https://url|display text> | 可点击的链接 |
| 用户提及 | <@U0123456> | @用户名 |
| 频道提及 | <#C0123456> | #频道 |
| Emoji | :emoji_name: | :rocket: |
| 无序列表 | 行首使用 - 或 * | 列表项 |
| 有序列表 | 行首使用 1. | 编号项 |
| 换行 | JSON 字符串中的 \n | 新行 |
与标准 Markdown 的重要区别:
- 粗体使用单个星号
*bold*,而不是双星号**bold**。 - 斜体使用下划线
_italic_,而不是单个星号。 - 链接使用
<url|text>格式,以竖线分隔,而不是[text](url)。 - mrkdwn 中没有标题。请改用
headerBlock 类型。 - 图片不能内嵌在 mrkdwn 中。请改用
imageBlock 或accessory。
消息模板
模板 1:产品公告
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":rocket: New Feature: [Feature Name]",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "We just shipped *[Feature Name]* — here is what it does and why it matters.\n\n:point_right: *What it does:* [One-sentence description]\n:point_right: *Why it matters:* [Key benefit for users]\n:point_right: *How to try it:* [Quick instructions or link]"
}
},
{
"type": "image",
"image_url": "https://example.com/feature-screenshot.png",
"alt_text": "Feature screenshot"
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":newspaper: Read Announcement",
"emoji": true
},
"url": "https://example.com/blog/feature-launch",
"action_id": "read_announcement"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":play_or_pause_button: Watch Demo",
"emoji": true
},
"url": "https://example.com/demo",
"action_id": "watch_demo"
}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Posted by *Product Team* | :speech_balloon: Reply in thread with questions"
}
]
}
]
}'
模板 2:每周指标报告
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":bar_chart: Weekly Marketing Metrics — Feb 3-9, 2026",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Here is this week'\''s performance snapshot."
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":globe_with_meridians: *Website Traffic*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Sessions*\n45,230 (:arrow_up: 12%)"
},
{
"type": "mrkdwn",
"text": "*Unique Visitors*\n31,870 (:arrow_up: 8%)"
},
{
"type": "mrkdwn",
"text": "*Bounce Rate*\n42.3% (:arrow_down: 2.1%)"
},
{
"type": "mrkdwn",
"text": "*Avg. Session Duration*\n3m 42s (:arrow_up: 15s)"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":money_with_wings: *Conversion Metrics*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Signups*\n487 (:arrow_up: 18%)"
},
{
"type": "mrkdwn",
"text": "*Trial-to-Paid*\n12.4% (:arrow_up: 1.2%)"
},
{
"type": "mrkdwn",
"text": "*MRR*\n$52,300 (:arrow_up: $3,200)"
},
{
"type": "mrkdwn",
"text": "*Churn*\n1.8% (:arrow_down: 0.3%)"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":email: *Email Performance*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Emails Sent*\n12,400"
},
{
"type": "mrkdwn",
"text": "*Open Rate*\n34.2%"
},
{
"type": "mrkdwn",
"text": "*Click Rate*\n4.8%"
},
{
"type": "mrkdwn",
"text": "*Unsubscribes*\n23"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:bulb: Key Takeaways*\n- Organic traffic up 15% after publishing 3 new blog posts\n- Email welcome sequence A/B test: Variant B outperformed by 22%\n- Trial signup spike on Thursday correlated with Product Hunt feature"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Full Dashboard",
"emoji": true
},
"url": "https://analytics.example.com/dashboard",
"action_id": "view_dashboard"
}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Auto-generated by Marketing Bot | Data from Google Analytics + Stripe"
}
]
}
]
}'
模板 3:分享博客文章
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":pencil: New Blog Post Published",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*<https://example.com/blog/post-slug|Blog Post Title Here>*\n\nA brief summary of what the post covers — keep it to 2-3 sentences that capture the key value and make people want to click through."
},
"accessory": {
"type": "image",
"image_url": "https://example.com/blog/post-og-image.png",
"alt_text": "Blog post cover image"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": ":bust_in_silhouette: Author: *Jane Smith* | :clock1: 6 min read | :label: SEO, Growth"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":mega: *Help us amplify!* Share this post on your socials. Here are ready-to-use snippets:\n\n*Twitter/X:* _Just published: [title]. [Key insight from the post]. Link in reply._\n\n*LinkedIn:* _We just published a deep dive on [topic]. Here is the #1 takeaway: [insight]._"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Read the Post",
"emoji": true
},
"style": "primary",
"url": "https://example.com/blog/post-slug",
"action_id": "read_post"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Share on Twitter",
"emoji": true
},
"url": "https://twitter.com/intent/tweet?text=Check%20out%20this%20post&url=https://example.com/blog/post-slug",
"action_id": "share_twitter"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Share on LinkedIn",
"emoji": true
},
"url": "https://www.linkedin.com/sharing/share-offsite/?url=https://example.com/blog/post-slug",
"action_id": "share_linkedin"
}
]
}
]
}'
模板 4:团队动态 / 每日站会
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":clipboard: Marketing Team Update — Monday, Feb 10",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:white_check_mark: Completed Last Week*\n- Launched email welcome sequence v2\n- Published 3 blog posts (SEO, product, case study)\n- Set up Google Ads remarketing campaign\n- Shipped landing page A/B test (Variant B live)"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:construction: In Progress*\n- Content calendar for March (70% done)\n- Competitor analysis report (due Wednesday)\n- Social media campaign for Product Hunt launch"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:dart: This Week'\''s Priorities*\n1. Finalize Product Hunt launch assets\n2. Send weekly newsletter (Thursday 9am)\n3. Review and approve Q1 ad spend budget\n4. Onboard new content writer"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*:warning: Blockers*\n- Waiting on design team for Product Hunt gallery images\n- Need legal review on new case study before publishing"
}
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": ":speech_balloon: Reply in thread with your own updates or questions"
}
]
}
]
}'
模板 5:事故 / 紧急通知
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":rotating_light: Marketing Alert",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Issue:* [Brief description of the problem]\n*Impact:* [Who/what is affected]\n*Status:* :red_circle: Active\n*Owner:* <@U0123456>"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Details:*\n[Longer explanation. What happened, when it started, what we know so far.]"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Next Steps:*\n1. [Action item 1]\n2. [Action item 2]\n3. [Action item 3]"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Status Page"
},
"url": "https://status.example.com",
"action_id": "status_page"
}
]
}
]
}'
工作流
工作流 1:发布营销公告
当用户要求在 Slack 发布公告、产品更新或新闻时:
- 收集详情 - 询问公告标题、正文、链接、图片 URL 和目标频道。
- 选择模板 - 从上方模板中选择,或构建自定义 Block Kit 负载。
- 构建负载 - 使用正确的 mrkdwn 格式构造 JSON。
- 预览 - 向用户展示完整的 JSON 负载,并说明其呈现效果。
- 确认 - 发送前请用户批准。
- 发送 - 执行 curl 命令。
- 报告 - 显示 API 响应。对于 Web API,记录用于串接消息的
ts。
工作流 2:发布每周指标
当用户要求将指标报告或仪表板发送到 Slack 时:
- 收集指标 - 询问相关数据,或协助从分析工具中提取数据。
- 使用字段排版 - 使用带有
fields的分区块,采用双列指标布局。 - 补充背景 - 使用箭头表情符号加入周环比对比。
- 添加要点 - 在分区块中总结 2-3 条关键见解。
- 添加仪表板链接 - 添加一个指向完整分析仪表板的按钮。
- 发送并在串线中跟进 - 发布主报告,然后在串线中以回复形式发送详细分类。
工作流 3:分享博客文章
需要向团队分发一篇新博客文章时:
- 获取 URL - 询问博客文章的 URL,或从博客中获取最新文章。
- 提取元数据 - 使用 WebFetch 获取标题、描述、作者和 OG 图片。
- 构建分享消息 - 使用模板 3,并附上社交平台推广文案。
- 添加分享按钮 - 添加预先填入文章内容的 Twitter 和 LinkedIn 意图 URL。
- 发布到频道 - 发送到团队营销频道。
工作流 4:社区互动
管理 Slack 社区频道(公开社区、客户频道)时:
- 欢迎消息 - 有新成员加入时,发布包含规则和资源的欢迎消息。
- 定期更新 - 每周发布热门讨论或新资源的汇总。
- 活动公告 - 分享即将举行的网络研讨会、AMA 或线下聚会,并附上 RSVP 按钮。
- 投票和反馈 - 使用操作块中的按钮收集快速反馈。
- 串线管理 - 在现有串线中回复更新或解答问题。
使用动态内容发送消息
使用 Shell 变量构建有效负载
TITLE="Product X v2.0 Released"
DESCRIPTION="Version 2.0 includes dark mode, API improvements, and 3x faster performance."
LINK="https://example.com/changelog/v2"
IMAGE_URL="https://example.com/images/v2-banner.png"
CHANNEL="#announcements"
curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import json
payload = {
'channel': '${CHANNEL}',
'text': '${TITLE}',
'blocks': [
{
'type': 'header',
'text': {'type': 'plain_text', 'text': '${TITLE}', 'emoji': True}
},
{
'type': 'section',
'text': {'type': 'mrkdwn', 'text': '${DESCRIPTION}'}
},
{
'type': 'image',
'image_url': '${IMAGE_URL}',
'alt_text': '${TITLE}'
},
{
'type': 'actions',
'elements': [{
'type': 'button',
'text': {'type': 'plain_text', 'text': 'Learn More'},
'url': '${LINK}',
'style': 'primary',
'action_id': 'learn_more'
}]
}
]
}
print(json.dumps(payload))
")"
从 JSON 文件构建有效负载
对于复杂消息,请先将有效负载写入文件:
# Write the payload
cat > /tmp/slack-message.json << 'PAYLOAD'
{
"channel": "#marketing",
"text": "Fallback text for notifications",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Message Title"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Message body with *bold* and _italic_ formatting."
}
}
]
}
PAYLOAD
# Send it
curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d @/tmp/slack-message.json
定时消息
使用 chat.scheduleMessage 在指定的未来时间发布消息:
# Schedule a message for a specific Unix timestamp
# Use: date -d "2026-02-12 09:00:00" +%s (Linux) or date -j -f "%Y-%m-%d %H:%M:%S" "2026-02-12 09:00:00" +%s (macOS)
SEND_AT=$(date -j -f "%Y-%m-%d %H:%M:%S" "2026-02-12 09:00:00" +%s 2>/dev/null || date -d "2026-02-12 09:00:00" +%s)
curl -s -X POST "https://slack.com/api/chat.scheduleMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"channel\": \"#marketing\",
\"post_at\": ${SEND_AT},
\"text\": \"Good morning team! Here is today's marketing agenda.\",
\"blocks\": []
}"
列出定时消息:
curl -s "https://slack.com/api/chat.scheduledMessages.list" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" | \
python3 -c "
import json, sys, datetime
data = json.load(sys.stdin)
for msg in data.get('scheduled_messages', []):
ts = datetime.datetime.fromtimestamp(msg['post_at']).strftime('%Y-%m-%d %H:%M')
print(f\"ID: {msg['id']} | Channel: {msg['channel_id']} | Scheduled: {ts}\")
"
删除定时消息:
curl -s -X POST "https://slack.com/api/chat.deleteScheduledMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"channel": "C0123456789",
"scheduled_message_id": "Q0123456789"
}'
多频道发布
将同一条消息发布到多个频道:
CHANNELS=("#marketing" "#general" "#product")
MESSAGE='{"text":"Big announcement coming tomorrow!","blocks":[{"type":"section","text":{"type":"mrkdwn","text":":mega: *Big announcement coming tomorrow!* Stay tuned."}}]}'
for CHANNEL in "${CHANNELS[@]}"; do
echo "Posting to ${CHANNEL}..."
echo "$MESSAGE" | python3 -c "
import json, sys
msg = json.load(sys.stdin)
msg['channel'] = '${CHANNEL}'
print(json.dumps(msg))
" | curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d @- | python3 -c "
import json, sys
r = json.load(sys.stdin)
if r.get('ok'):
print(f' Sent. ts={r[\"ts\"]}')
else:
print(f' Error: {r.get(\"error\", \"unknown\")}')
"
done
错误处理
常见 API 错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
invalid_auth | 令牌无效或已过期 | 在 Slack 应用设置中重新生成机器人令牌 |
channel_not_found | 机器人不在频道中,或频道名称有误 | 使用 /invite @BotName 邀请机器人,或使用频道 ID |
not_in_channel | 机器人需要先加入频道 | 邀请机器人,或使用 chat:write.public 权限范围 |
too_many_attachments | 超过 50 个块 | 将消息拆分为多条消息或串线回复 |
msg_too_long | 文本超过 40,000 个字符 | 缩短消息或拆分成多部分 |
rate_limited | 请求过多 | 等待 Retry-After 标头中指定的秒数 |
missing_scope | 令牌缺少所需权限 | 在 OAuth 和权限中添加权限范围,然后重新安装应用 |
验证响应
RESPONSE=$(curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"channel":"#marketing","text":"Test message"}')
python3 -c "
import json, sys
r = json.loads('${RESPONSE}'.replace(\"'\", \"\"))
if r.get('ok'):
print(f'Message sent successfully. ts={r[\"ts\"]} channel={r[\"channel\"]}')
else:
print(f'Error: {r.get(\"error\", \"unknown\")}')
if r.get('response_metadata', {}).get('messages'):
for m in r['response_metadata']['messages']:
print(f' Detail: {m}')
" 2>/dev/null || echo "$RESPONSE"
使用临时文件的更稳健做法:
RESPONSE_FILE=$(mktemp)
curl -s -X POST "https://slack.com/api/chat.postMessage" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"channel":"#marketing","text":"Test message"}' \
-o "$RESPONSE_FILE"
python3 -c "
import json
with open('${RESPONSE_FILE}') as f:
r = json.load(f)
if r.get('ok'):
print(f'Sent. ts={r[\"ts\"]}')
else:
print(f'Error: {r.get(\"error\")}')
"
rm -f "$RESPONSE_FILE"
提示
- 始终在
blocks旁边包含text字段——它可作为通知、无障碍阅读器以及不支持 Block Kit 的客户端的备用内容。 - 使用 https://app.slack.com/block-kit-builder 上的 Block Kit Builder,在构建 curl 命令前直观地设计和预览消息。
- 对于生产工作流,优先使用
chat.postMessage(Web API),而不是 Webhook。它会返回一个消息ts,可用于串接消息、更新和删除消息。 - 将长篇报告放在线程中。将摘要作为父消息发布,并通过线程回复补充详情,以保持频道整洁。
- 在
plain_text字段中使用:emoji:代码,并设置"emoji": true,即可在标题和按钮标签中显示表情符号。 - 在 mrkdwn 中转义特殊字符:
&替换为&,<替换为<,>替换为>。 - 速率限制:Slack 每个频道大约允许每秒发送 1 条消息。批量发布时,请在请求之间添加 1 秒延迟。
- 发布指标时,使用 section
fields实现双列布局,不要尝试用 mrkdwn 排版表格(Slack 不支持表格)。 - 发布前,务必向用户展示完整的消息载荷并征求确认。