mcpField
S
slack-bot

slack-bot

github.com/openclaudia生产力722429 次安装1 周前更新
代码仓库

关于 slack-bot

通过 Incoming Webhooks 或 Slack Web API 向 Slack 发送消息和格式化内容的 Skill,提供 Block Kit 公告、报告、线程回复、文件上传等示例。

主要功能

  1. 01Webhook 与 Web API 发消息
  2. 02Block Kit 消息排版
  3. 03线程回复与消息更新
  4. 04频道列表与文件上传
  5. 05Slack mrkdwn 格式参考

适用场景

  1. 01发布格式化的营销报告
  2. 02在频道分享公告
  3. 03回复已有消息线程
  4. 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 作用域,并且仅限用于单个频道。

  1. 前往 https://api.slack.com/apps,然后点击 Create New App > From scratch。
  2. 为应用命名(例如,“Marketing Bot”),并选择工作区。
  3. 在左侧边栏中,点击 Incoming Webhooks 并将其切换为 On。
  4. 点击底部的 Add New Webhook to Workspace。
  5. 选择要发布消息的频道,然后点击 Allow。
  6. 复制 Webhook URL(以 https://hooks.slack.com/services/... 开头)。
  7. 将其添加到环境中:
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:向机器人所在的任意频道发布消息、回复讨论串、列出频道、上传文件等。

  1. 前往 https://api.slack.com/apps,然后点击 Create New App > From scratch。
  2. 为应用命名并选择工作区。
  3. 在左侧边栏中,点击 OAuth & Permissions。
  4. 在 Bot Token Scopes 下添加以下作用域:
    • chat:write - 发布消息
    • chat:write.public - 无需加入频道即可向频道发布消息
    • channels:read - 列出公开频道
    • files:write - 上传文件(可选,用于图片/报告)
    • reactions:write - 添加表情回应(可选)
  5. 点击顶部的 Install to Workspace 并授权。
  6. 复制 Bot User OAuth Token(以 xoxb- 开头)。
  7. 将其添加到环境中:
echo 'SLACK_BOT_TOKEN=xoxb-your-token-here' >> .env
  1. 将机器人邀请到需要发布消息的频道:在每个频道中输入 /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 中没有标题。请改用 header Block 类型。
  • 图片不能内嵌在 mrkdwn 中。请改用 image Block 或 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 发布公告、产品更新或新闻时:

  1. 收集详情 - 询问公告标题、正文、链接、图片 URL 和目标频道。
  2. 选择模板 - 从上方模板中选择,或构建自定义 Block Kit 负载。
  3. 构建负载 - 使用正确的 mrkdwn 格式构造 JSON。
  4. 预览 - 向用户展示完整的 JSON 负载,并说明其呈现效果。
  5. 确认 - 发送前请用户批准。
  6. 发送 - 执行 curl 命令。
  7. 报告 - 显示 API 响应。对于 Web API,记录用于串接消息的 ts。
工作流 2:发布每周指标

当用户要求将指标报告或仪表板发送到 Slack 时:

  1. 收集指标 - 询问相关数据,或协助从分析工具中提取数据。
  2. 使用字段排版 - 使用带有 fields 的分区块,采用双列指标布局。
  3. 补充背景 - 使用箭头表情符号加入周环比对比。
  4. 添加要点 - 在分区块中总结 2-3 条关键见解。
  5. 添加仪表板链接 - 添加一个指向完整分析仪表板的按钮。
  6. 发送并在串线中跟进 - 发布主报告,然后在串线中以回复形式发送详细分类。
工作流 3:分享博客文章

需要向团队分发一篇新博客文章时:

  1. 获取 URL - 询问博客文章的 URL,或从博客中获取最新文章。
  2. 提取元数据 - 使用 WebFetch 获取标题、描述、作者和 OG 图片。
  3. 构建分享消息 - 使用模板 3,并附上社交平台推广文案。
  4. 添加分享按钮 - 添加预先填入文章内容的 Twitter 和 LinkedIn 意图 URL。
  5. 发布到频道 - 发送到团队营销频道。
工作流 4:社区互动

管理 Slack 社区频道(公开社区、客户频道)时:

  1. 欢迎消息 - 有新成员加入时,发布包含规则和资源的欢迎消息。
  2. 定期更新 - 每周发布热门讨论或新资源的汇总。
  3. 活动公告 - 分享即将举行的网络研讨会、AMA 或线下聚会,并附上 RSVP 按钮。
  4. 投票和反馈 - 使用操作块中的按钮收集快速反馈。
  5. 串线管理 - 在现有串线中回复更新或解答问题。

使用动态内容发送消息

使用 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 中转义特殊字符:& 替换为 &amp;,< 替换为 &lt;,> 替换为 &gt;。
  • 速率限制:Slack 每个频道大约允许每秒发送 1 条消息。批量发布时,请在请求之间添加 1 秒延迟。
  • 发布指标时,使用 section fields 实现双列布局,不要尝试用 mrkdwn 排版表格(Slack 不支持表格)。
  • 发布前,务必向用户展示完整的消息载荷并征求确认。

评论

支持 Markdown