Webhook

传入 Webhook 允许其他系统通过向一个秘密 URL 发送 JSON 来向频道发送消息。传出 Webhook 则相反:它将消息发送到你的 URL,这些消息要么以触发关键字开头,要么由用户在消息菜单中为其选择,然后发布你的回复。

管理 Webhook 的位置

打开右上角的头像,然后选择集成。当前社区的集成页面列出了传入 Webhook和传出 Webhook。每个 Webhook 属于一个社区。

默认情况下,社区管理员和系统管理员可以创建 Webhook。系统管理员可以在系统控制台的权限设置中授予其他角色此权限。系统管理员还可以在系统控制台 › 集成 › 集成管理中关闭每种类型的 Webhook:启用传入 Webhook和启用传出 Webhook默认均为开启状态。

A channel with messages from a CI bot and from GitHub, shown as cards with fields, pipeline stages and a button
以卡片形式呈现的集成消息:字段、流水线阶段和操作按钮。

创建传入 Webhook

  1. 在集成中,选择传入 Webhook,然后选择添加传入 Webhook。
  2. 填写标题和描述:它们仅在集成页面上显示。
  3. 选择消息默认发送到的频道。对于私有频道,您必须是该频道的成员。
  4. 如果发送者不能发布到其他位置,请勾选锁定至此频道。
  5. 可选地,为其消息设置用户名和个人资料照片 URL(参见名称和图标)。
  6. 选择保存。复制显示的 URL:https://chat.example.com/hooks/<key>。
URL 是唯一的秘密:任何拥有它的人都可以向该频道发布消息。请将其排除在公共仓库之外,如果泄露,请删除该 Webhook。

使用 API:POST /api/v4/hooks/incoming,参数为 {"channel_id", "display_name", "description", "username", "icon_url", "channel_locked"};响应中的 id 即为密钥。从命令行:amctl webhook create-incoming --channel <channel id> --user <user> --display-name "CI"。

向传入 Webhook 发送消息

curl -X POST https://chat.example.com/hooks/xgh5ycrx1jf7mbs9w8z4c8e7ho \
  -H 'Content-Type: application/json' \
  -d '{"text": "Deploy of **api 2.4.0** finished :white_check_mark:"}'

服务器以 200 状态码和 ok 作为响应体进行回复。它还接受 application/x-www-form-urlencoded 格式,其中 JSON 位于 payload 字段中,正如 Slack 的 Webhook 所做的那样,因此为 Slack 制作的工具通常可以不加修改地工作。

字段含义
text以 Markdown 格式编写的消息内容。除非包含 attachments,否则此项为必填。超过服务器消息长度限制的内容将被拆分为多条消息。
channelWebhook 所属社区中的另一个频道,通过名称(ops-alerts 或 #ops-alerts)指定,或者使用 @username 从 Webhook 的所有者发送私信。当 Webhook 被锁定到其所在频道时,此操作将被拒绝。旧名称 town-square 仍然可以到达社区的默认频道,该频道现在称为 general。
username, icon_url, icon_emoji当服务器允许覆盖时,用于显示的名称和图片。
attachments消息附件,以卡片形式呈现(见下文)。
props存储在消息上的额外属性,供读取这些属性的应用使用。
type自定义帖子类型,例如 custom_poll,用于由应用渲染。以 system_ 开头的类型会被拒绝。
root_id话题首条消息的 ID,用于在该话题中回复。
priority消息优先级,例如 {"priority": "urgent"}。
silent设置为 true 时,发送消息但不触发推送通知。

Webhook 发布的每条消息旁边都会带有 机器人 标签,即使它显示在其创建者的名称下,这样人们也能将其与真实用户的消息区分开来。Webhook 消息永远不会触发传出 Webhook。

名称和图标

除非服务器允许覆盖,否则 Webhook 会以创建者的身份发布。在 系统控制台 › 集成 › 集成管理 中有两个设置项(默认均为关闭),可以更改此行为:

  • 允许集成覆盖用户名(ServiceSettings.EnablePostUsernameOverride):将显示 Webhook 的 用户名 或请求中的 username;如果两者均未提供,名称将显示为 webhook。
  • 允许集成覆盖头像(ServiceSettings.EnablePostIconOverride):将显示 Webhook 的 个人资料照片,或请求中的 icon_url 或 icon_emoji。

请求中的值优先于保存在 Webhook 上的值。

附件

附件在文本下方添加结构化内容。它们遵循 Slack 的消息附件格式:

{
  "text": "Build failed on `main`",
  "attachments": [{
    "fallback": "main #1902 failed",
    "color": "#d24b4e",
    "author_name": "GitHub Actions",
    "author_icon": "https://chat.example.com/static/ci.png",
    "title": "main #1902 failed",
    "title_link": "https://ci.example.com/runs/1902",
    "text": "Commit `0f4d8e2` by Jonas Weber",
    "fields": [
      {"title": "Branch", "value": "zephyr-3.7", "short": true},
      {"title": "Duration", "value": "3m 40s", "short": true}
    ],
    "footer": "Lumen CI",
    "ts": 1760000000
  }]
}
字段含义
fallback纯文本,用于无法显示卡片的场景,例如通知。
color颜色条:十六进制颜色值,或 good、warning、danger。
pretext卡片上方的文本。
author_name, author_link, author_icon小型标题行。
title, title_link卡片标题,可选链接。
text卡片正文,使用 Markdown 格式。
fields{"title", "value", "short"} 的列表;short 字段并排显示。
image_url, thumb_url下方显示的大图,或侧边显示的小图。
footer, footer_icon, ts页脚行和时间(Unix 秒)。
actions按钮和菜单:请参阅 交互式消息。

卡片:阶段和图表

Antimatter 为附件添加了两个字段,由默认界面渲染:

  • stages:以药丸形式显示流水线中的步骤,最多 20 个。每个步骤为 {"name", "status"},状态可以是 passed、failed、running、pending、skipped 或 canceled。
  • chart:最近数值的小型折线图,{"values": [2 to 200 numbers], "threshold": number, "label": "…"}。阈值以虚线绘制,表示告警的阈值限制。
{
  "attachments": [{
    "color": "danger",
    "title": "main #1902 failed",
    "stages": [
      {"name": "build", "status": "failed"},
      {"name": "unit", "status": "skipped"},
      {"name": "publish", "status": "skipped"}
    ]
  }, {
    "color": "warning",
    "title": "API latency above 400 ms",
    "chart": {"values": [210, 230, 260, 390, 450, 470], "threshold": 400, "label": "p95 latency, ms"}
  }]
}

经典界面和其他客户端会忽略这两个字段,因此也请将关键信息放入 text 或 fields 中。

创建传出 Webhook

传出 Webhook 监视社区的公共频道。当消息的第一个词匹配其触发关键字之一,或者有人在消息菜单中为其选择(消息操作)时,它会向你的 URL 发送消息。

  1. 在集成中,选择传出 Webhook,然后选择添加传出 Webhook。
  2. 填写标题和描述。
  3. 选择请求的内容类型:application/x-www-form-urlencoded 或 application/json。
  4. 选择一个频道,输入触发词(一行一个),或者两者都设置。如果只选择频道而不设置触发词,该频道的每条消息都会被发送。如果两者都设置,消息必须同时满足两个条件。
  5. 在触发时机下,选择第一个完全匹配触发关键字、第一个词以触发关键字开头或从消息菜单。选择从消息菜单时,触发关键字将消失,频道变为可选(如果不指定,该操作将在社区的每个公共频道中提供),并且标题为必填项:它是菜单中该操作的名称。
  6. 输入回调 URL(每行一个)。每个 URL 都会收到所有匹配的消息。
  7. 可选地设置用于回复的用户名和个人资料照片。对于来自消息菜单的操作,图标字段将替代个人资料照片:可以是 .png 或 .jpg 图像的 URL,或者在 Antimatter 默认界面中为已上传的图像。菜单中会以灰色显示该图标;如果没有设置,该操作将获得一个中性图标。
  8. 选择保存并复制显示的令牌。您的服务将使用它来验证请求是否来自您的服务器。

通过 API:POST /api/v4/hooks/outgoing,参数为 {"team_id", "channel_id", "trigger_words", "trigger_when", "callback_urls", "content_type", "display_name"}(trigger_when:0 表示完全匹配,1 表示“以……开头”,2 表示“从消息菜单触发”)。trigger_when 为 2 的 Webhook 不需要触发词,但必须提供 display_name。使用 POST /api/v4/hooks/outgoing/{hook_id}/icon 上传图标(multipart 格式,字段 image:PNG、JPEG 或 GIF,存储为 128 px 的 PNG);其 icon_url 将指向 GET /api/v4/hooks/outgoing/{hook_id}/icon。

如果您的服务位于私有网络中,系统管理员必须在允许不受信任的内部连接到(ServiceSettings.AllowedUntrustedInternalConnections)中允许其地址。否则,服务器不会调用私有地址。

传出请求

Antimatter 发送包含以下字段的 POST 请求,根据内容类型以 JSON 或表单编码格式发送:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "text": "deploy api 2.4.0",
  "trigger_word": "deploy",
  "file_ids": ""
}

在执行任何操作之前,请先将 token 与 Webhook 的令牌进行比对。在表单编码版本中,timestamp 以秒为单位,而非毫秒。服务器会等待你的响应,时长受 集成请求超时 限制(默认为 30 秒,ServiceSettings.OutgoingIntegrationRequestsTimeout)。

回复出站 Webhook

要发布回复,请以 JSON 格式响应。要发布空内容,请返回空响应体。

{
  "text": "Deploying **api 2.4.0** to staging…",
  "response_type": "comment",
  "username": "deploy-bot",
  "icon_url": "https://chat.example.com/static/deploy.png"
}
  • text 和 attachments:回复内容。如果两者均未提供,则不会发布任何内容。
  • response_type:comment 会将回复发布在触发消息的话题中;否则,回复将发布到频道。
  • username、icon_url:当服务器允许覆盖时显示;否则使用 Webhook 自身的值。
  • props、type、priority:与入站 Webhook 相同。

回复将以 Webhook 创建者的身份发布,如果存在覆盖名称,则使用覆盖后的名称。对于比简短回复更长的内容,请立即响应,稍后通过入站 Webhook 或 API 发布。

消息操作

消息操作是一种出站 Webhook,其 触发时机 设置为 从消息菜单。它不会针对已发布的消息触发。相反,用户会在消息菜单:消息操作(⋯)› 更多操作 中,于 集成 标题下看到它及其图标。经典界面不显示消息操作。选择它会将该条消息发送到回调 URL,并显示一条简短通知,说明已 发送至 该操作名称,或显示您的 ephemeral_text。

  • 显示位置:在 Webhook 所在的频道中;如果没有指定频道,则在其所属工作区的所有公共频道中。只有公共频道才支持消息操作。
  • 访客 只能看到绑定到其所加入频道的操作,无法看到工作区级别的操作。
  • 已归档和只读频道:消息仍然可以发送。归档频道会删除绑定到该频道的 Webhook,因此已归档频道仅保留工作区级别的操作。如果用户无法在该频道中发布回复,该回复将仅对该用户可见。
  • 速率限制:每位用户每分钟可执行 10 次消息操作,突发上限为 5 次。超出限制后,服务器将返回 429。
  • 私有网络:与其他出站 Webhook 一样,内部地址必须在 允许不受信任的内部连接到 中允许。

请求

负载以 JSON 或表单编码形式发送,具体取决于 Webhook 的内容类型:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "trigger": "message_action",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "post_root_id": "",
  "post_create_at": 1760009000000,
  "post_user_id": "…",
  "post_user_name": "jonas",
  "permalink": "https://chat.example.com/lumen-labs/pl/…",
  "text": "The staging database is full again",
  "files": [{"id": "…", "name": "df.txt", "link": "https://chat.example.com/api/v4/files/…"}],
  "priority": "",
  "requested_ack": false,
  "burn_on_read": false
}
  • user_id 和 user_name 是选择该操作的用户;post_user_id 和 post_user_name 是消息的作者。
  • 仅发送该条消息,绝不发送其所属话题中的其他消息。
  • files 仅在选择操作的用户有权下载时列出消息的文件。下载 link 需要会话或令牌,与 API 中的其他文件相同。
  • 阅后即焚消息发送时不包含其文本和文件,且 burn_on_read 设置为 true。
  • 在表单编码中,文件以重复的 file_id、file_name 和 file_link 字段形式出现,顺序相同。

响应

响应方式与任何传出 Webhook 相同(参见回复传出 Webhook):text 或 attachments 会在频道中发布回复,而 "response_type": "comment" 会在消息的话题中发布回复。你也可以使用 ephemeral_text(截断至 300 个字符)进行响应:它仅显示给选择该操作的人,并出现在通知中。空响应体不会发布任何内容。

{"ephemeral_text": "Ticket OPS-412 created"}

使用 API

  • GET /api/v4/channels/{channel_id}/message_actions:返回你在频道中可能选择的操作,包含 id、display_name、description 和 icon_url。不会返回令牌或回调 URL。
  • POST /api/v4/posts/{post_id}/message_actions/{hook_id}:在你可以阅读的消息上运行操作。它返回 {"ephemeral_text"}。

当 Webhook 无法工作时

  • 来自 Webhook URL 的 501:服务器上的入站 Webhook 已关闭。
  • 400 “Failed to decode the payload…”:JSON 无效,或表单字段名称不是 payload。“No text specified.” 表示请求中既没有 text 也没有 attachments。
  • 403:Webhook 被锁定到其他频道,或其所有者已无法读取该频道。
  • 传出 Webhook 从未触发:频道是私有的、触发词不是第一个单词、消息来自 Webhook,或者 Webhook 设置为从消息菜单触发。
  • 消息操作未出现在菜单中:频道是私有的、操作绑定到其他频道、该用户是访客且操作没有关联频道,或者传出 Webhook 已关闭。
  • 运行消息操作时返回 404:操作不存在,或者该操作不覆盖该用户在该消息所在频道的权限。429:该用户在一分钟内运行了超过 10 次操作。502:无法访问任何回调 URL(日志中包含原因)。
  • 日志:开启 启用 Webhook 调试(LogSettings.EnableWebhookDebugging),并将日志级别设置为 DEBUG,服务器将记录失败的入站请求的负载。失败的出站调用将记录为 Outgoing Webhook POST failed。