Skip to content

通过 RPC 推送告警到 Slack

1. 场景说明

ThinkLink(TKL)平台的 RPC 支持调用外部 HTTP 接口(见 RPC 模型 §1.3.2 类型四),可以把设备告警实时推送到 Slack。本文介绍两种集成方式:

  • Incoming Webhook(推荐 / 简单):每个 channel 一个固定 URL,只能向该 channel 发消息
  • Bot Token + chat.postMessage(灵活):一个 Token 可以向多个 channel 发消息、支持更丰富的功能

2. 准备工作

2.1 方式 A:创建 Incoming Webhook

  1. 访问 https://api.slack.com/apps,点击 Create New App → From scratch
  2. 选择目标 workspace,App 名称随意
  3. 在左侧菜单 Incoming Webhooks 中开启 Activate Incoming Webhooks
  4. 点击 Add New Webhook to Workspace,选择推送的目标 channel
  5. 复制生成的 Webhook URL,形如:
    https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

2.2 方式 B:创建 Bot Token

  1. 在同一个 App 配置页,进入 OAuth & Permissions
  2. Scopes → Bot Token Scopes 中添加:chat:writechat:write.public(如需向未加入的 channel 发消息)
  3. 点击 Install to Workspace,授权后会获得 Bot User OAuth Token,形如:
    xoxb-1234567890-1234567890123-xxxxxxxxxxxxxxxxxxxxxxxx
  4. 把 Bot 邀请进目标 channel:/invite @YourBotName

2.3 妥善保管 Webhook URL / Bot Token(必读)

⚠️ 安全提示

  • Incoming Webhook URL 泄漏后,任何人都能向对应 channel 推送消息
  • Bot Token 权限更大,泄漏后可能可以读取 channel 消息、向多个 channel 发送消息、修改 Bot 自身配置

请遵守以下规则:

  • 不要把 Webhook URL 或 Bot Token 硬编码进脚本、写进截图、提交到代码库(GitHub 的 secret scan 会自动检测 Slack Token)
  • 推荐做法:把 URL / Token 保存在设备(或资产)的 server_attrs 中,脚本里通过 device.server_attrs.slack_webhook.slack_token 引用
  • 一旦发现泄漏,立即在 Slack App 管理页 Revoke Token / Disable Webhook,然后重新生成
  • 仅为 Bot 申请最小必要权限(principle of least privilege):只需发送时不要申请 chat:readusers:read 等读取权限
  • 定期回顾 App 的安装范围与权限列表
  • 为不同业务场景使用不同的 App 或 Webhook,降低单点泄漏影响范围

3. 在 RPC 中调用 Slack(方式 A:Webhook)

3.1 在设备属性中保存凭证

json
{
    "slack_webhook": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
}

3.2 RPC 脚本:使用 Block Kit 发送富文本消息

javascript
function rpc_script({ device, params, alarms, logger }) {
    let url = device.server_attrs?.slack_webhook;
    if (!url) {
        logger.error("slack_webhook not configured", { eui: device.eui });
        return null;
    }

    let levelEmoji = {
        urgent: ":rotating_light:",
        high:   ":warning:",
        mid:    ":large_yellow_circle:",
        low:    ":information_source:"
    }[params.level] || ":bell:";

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: url,
                headers: { "Content-Type": "application/json" },
                data: {
                    text: `${levelEmoji} Device Alert - ${device.name}`,   // fallback for notifications
                    blocks: [
                        {
                            type: "header",
                            text: { type: "plain_text", text: `${levelEmoji} Device Alert` }
                        },
                        {
                            type: "section",
                            fields: [
                                { type: "mrkdwn", text: `*Device:*\n${device.name}` },
                                { type: "mrkdwn", text: `*EUI:*\n\`${device.eui}\`` },
                                { type: "mrkdwn", text: `*Level:*\n${(params.level || "").toUpperCase()}` },
                                { type: "mrkdwn", text: `*Time:*\n${new Date().toISOString()}` }
                            ]
                        },
                        {
                            type: "section",
                            text: { type: "mrkdwn", text: `*Description:*\n${params.desc || "n/a"}` }
                        }
                    ]
                },
                timeout: 5000
            }
        }
    ];
}

4. 在 RPC 中调用 Slack(方式 B:Bot Token)

4.1 在设备属性中保存凭证

json
{
    "slack_token":   "xoxb-1234567890-xxxxxxxxxxxxxxxxxxxxxxxx",
    "slack_channel": "C01234ABCDE"
}

💡 slack_channel 可以是 channel ID(推荐,更稳定)或 #channel-name。channel ID 可在 Slack 客户端右键 channel → "View channel details" 底部查看。

4.2 RPC 脚本

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token   = device.server_attrs?.slack_token;
    let channel = device.server_attrs?.slack_channel;
    if (!token || !channel) {
        logger.error("slack credentials not configured", { eui: device.eui });
        return null;
    }

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: "https://slack.com/api/chat.postMessage",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type":  "application/json; charset=utf-8"
                },
                data: {
                    channel: channel,
                    text: `:warning: *${device.name}* alert: ${params.desc || ""}`,
                    mrkdwn: true
                },
                timeout: 5000
            }
        }
    ];
}

5. 配置 RPC 参数

字段标识类型别名说明
levelstring告警等级low / mid / high / urgent
descstring告警描述显示在消息正文中

6. 集成到告警流程

参考 企业微信 AN 中的触发联动示例,把 method 改成本 RPC 的 Method 名即可。

7. 故障排查

现象可能原因
Webhook 返回 invalid_payloadJSON 结构错误;blocks 字段格式不合规
Webhook 返回 no_service / 404Webhook URL 错误或已被吊销
chat.postMessage 返回 ok: false, error: not_in_channelBot 未被邀请进目标 channel(需 /invite @bot
chat.postMessage 返回 error: missing_scopeBot Token 权限不足,需在 App 配置中添加 chat:write 后重新安装
请求超时服务器到 hooks.slack.com / slack.com 网络不通
频率限制Slack 限制每个 channel 平均每秒 1 条消息

8. 注意事项

  • 推荐使用 Block Kit(blocks 字段)而非旧版 attachments,前者在所有 Slack 客户端的展示更一致
  • 消息中不要泄漏 Bot Token 自身(避免把异常堆栈或完整请求直接放进消息)
  • Slack workspace 设置中可启用 Token Auto-rotation(OAuth v2),开启后可降低长期 Token 泄漏的影响
  • 重要告警建议同时配置多个通道(如 Slack + 邮件),避免单一通道故障漏报