1. 概述

Slack 是全球广泛使用的团队协作通信工具,其强大之处在于支持丰富的第三方集成和自定义插件开发。通过调用 Slack 提供的 HTTP API,我们可以轻松构建自动化机器人(bot),实现与频道、用户的交互。

虽然 Slack 官方没有提供 Java SDK,但社区有一个官方推荐的高质量开源 SDK —— HubSpot Slack Client,它封装了绝大多数 Slack API,让我们无需关心底层 HTTP 细节,专注业务逻辑。

本文将基于该 SDK 实现一个系统监控机器人:定时检查服务器磁盘使用率,当空间不足时自动发送告警。

✅ 推荐使用 HubSpot 的 SDK,踩坑少,API 设计清晰,异步非阻塞,适合生产环境。


2. 获取 API 凭据

要接入 Slack,必须先在开发者平台创建应用并获取认证 Token。

步骤概览:

  1. 访问 Slack API 管理页面 创建新 App
  2. 配置 Bot 用户(虚拟账号)
  3. 安装 App 到工作区,获取访问 Token
  4. 将 Bot 邀请至目标频道

详细操作:

  1. 进入 https://api.slack.com/apps,点击 “Create New App”
    Screenshot-2020-01-31-at-21.37.19

  2. 填写 App 名称(如 System Monitor)并选择所属 Workspace
    Screenshot-2020-01-31-at-21.39.37

  3. 进入左侧菜单 “Bot” 配置,创建 Bot 用户
    Screenshot-2020-01-31-at-21.40.38

  4. 设置 Bot 显示名与用户名(如 system_monitoring
    Screenshot-2020-01-31-at-21.40.59

  5. 左侧导航选择 “Install App”,点击 “Install App to Workspace” 完成安装
    Screenshot-2020-01-31-at-21.41.47

  6. 安装成功后,复制 Bot User OAuth Access Token(形如 xoxb-...),这是后续调用 API 的凭据
    ⚠️ 注意:Token 需妥善保管,不要提交到代码仓库
    Screenshot-2020-01-31-at-21.45.00

  7. 最后,进入目标 Slack 频道(如 #dev-ops),@ 你的 Bot(如 @system_monitoring)将其加入频道,否则无法发送消息。


3. 引入 Slack SDK 依赖

使用 Maven,在 pom.xml 中添加 HubSpot Slack Client 依赖:

<properties>
    <slack.version>1.8.0</slack.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.hubspot.slack</groupId>
        <artifactId>slack-base</artifactId>
        <version>${slack.version}</version>
    </dependency>
    <dependency>
        <groupId>com.hubspot.slack</groupId>
        <artifactId>slack-java-client</artifactId>
        <version>${slack.version}</version>
    </dependency>
</dependencies>

slack-base 提供核心模型,slack-java-client 是异步客户端实现。


4. 应用架构设计

为实现灵活扩展,采用接口解耦设计:

核心接口定义

  • ErrorChecker:错误检测器,负责执行检查逻辑
  • ErrorReporter:错误上报器,负责通知(可对接不同渠道)
public interface ErrorChecker {
    void check();
}
public interface ErrorReporter {
    void reportProblem(String problem);
}

设计优势

  • 同一个检查器可绑定多个上报器(例如:磁盘 90% 告警发频道,98% 告警发私信)
  • 上报器可复用(邮件、短信、Slack 等)
  • 易于测试和替换实现

5. 磁盘空间检查实现

使用 Java 7 引入的 NIO.2 FileStore API 跨平台获取磁盘信息。

public class DiskSpaceErrorChecker implements ErrorChecker {
    private static final Logger LOG = LoggerFactory.getLogger(DiskSpaceErrorChecker.class);

    private ErrorReporter errorReporter;
    private double limit; // 触发告警的可用空间阈值(如 0.1 表示 10%)

    public DiskSpaceErrorChecker(ErrorReporter errorReporter, double limit) {
        this.errorReporter = errorReporter;
        this.limit = limit;
    }

    @Override
    public void check() {
        FileSystems.getDefault().getFileStores().forEach(fileStore -> {
            try {
                long totalSpace = fileStore.getTotalSpace();
                long usableSpace = fileStore.getUsableSpace();
                double usablePercentage = ((double) usableSpace) / totalSpace;

                if (totalSpace > 0 && usablePercentage < limit) {
                    String error = String.format("File store %s only has %d%% usable disk space",
                        fileStore.name(), (int)(usablePercentage * 100));
                    errorReporter.reportProblem(error);
                }
            } catch (IOException e) {
                LOG.error("Error getting disk space for file store {}", fileStore, e);
            }
        });
    }
}

FileStore 能正确识别 Windows 盘符、Linux 挂载点等,比 File.getUsableSpace() 更可靠。


6. 实现 Slack 频道消息上报

通过 SlackClient 向指定频道发送消息。

public class SlackChannelErrorReporter implements ErrorReporter {
    private SlackClient slackClient;
    private String channel; // 频道 ID,如 C012AB3CD

    public SlackChannelErrorReporter(SlackClient slackClient, String channel) {
        this.slackClient = slackClient;
        this.channel = channel;
    }

    @Override
    public void reportProblem(String problem) {
        slackClient.postMessage(
          ChatPostMessageParams.builder()
            .setText(problem)
            .setChannelId(channel)
            .build()
        ).join().unwrapOrElseThrow();
    }
}

⚠️ 注意:channel 应使用频道 ID(以 C 开头),而非名称。可在 Slack 网页版右键频道 → “复制链接” 获取。


7. 应用启动与调度

使用 Java 原生 Timer 实现定时任务(生产环境建议用 ScheduledExecutorService 或 Spring Scheduler)。

public class MainClass {
    public static final long MINUTES = 1000 * 60;

    public static void main(String[] args) throws IOException {
        // 初始化 Slack 客户端
        SlackClientRuntimeConfig runtimeConfig = SlackClientRuntimeConfig.builder()
          .setTokenSupplier(() -> "xoxb-your-bot-token-here") // 替换为真实 Token
          .build();

        SlackClient slackClient = SlackClientFactory.defaultFactory().build(runtimeConfig);

        // 配置频道告警:低于 10% 发 #general 频道
        ErrorReporter slackChannelErrorReporter = new SlackChannelErrorReporter(slackClient, "C012AB3CD");
        ErrorChecker diskSpaceErrorChecker10pct = new DiskSpaceErrorChecker(slackChannelErrorReporter, 0.1);

        // 配置私信告警:低于 2% 直接发给运维负责人
        ErrorReporter slackUserErrorReporter = new SlackUserErrorReporter(slackClient, "alice@example.com");
        ErrorChecker diskSpaceErrorChecker2pct = new DiskSpaceErrorChecker(slackUserErrorReporter, 0.02);

        // 启动定时任务,每 5 分钟执行一次
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                diskSpaceErrorChecker10pct.check();
                diskSpaceErrorChecker2pct.check();
            }
        }, 0, 5 * MINUTES);
    }
}

运行后效果:
Screenshot-2020-01-31-at-21.42.55


8. 实现 Slack 私信(IM)上报

对于紧急告警,直接发送私信更有效,避免被频道消息淹没。

public class SlackUserErrorReporter implements ErrorReporter {
    private SlackClient slackClient;
    private String user; // 用户邮箱

    public SlackUserErrorReporter(SlackClient slackClient, String user) {
        this.slackClient = slackClient;
        this.user = user;
    }

    @Override
    public void reportProblem(String problem) {
        // 1. 根据邮箱查找用户 ID
        UsersInfoResponse usersInfoResponse = slackClient
            .lookupUserByEmail(UserEmailParams.builder()
              .setEmail(user)
              .build()
            ).join().unwrapOrElseThrow();

        // 2. 打开与该用户的 IM(私聊)通道
        ImOpenResponse imOpenResponse = slackClient.openIm(ImOpenParams.builder()
            .setUserId(usersInfoResponse.getUser().getId())
            .build()
        ).join().unwrapOrElseThrow();

        // 3. 向该 IM 通道发送消息
        imOpenResponse.getChannel().ifPresent(channel -> {
            slackClient.postMessage(
                ChatPostMessageParams.builder()
                  .setText(problem)
                  .setChannelId(channel.getId())
                  .build()
            ).join().unwrapOrElseThrow();
        });
    }
}

✅ 使用邮箱查找用户最稳定,因为用户名可更改,邮箱不可变。

私信效果:
Screenshot-2020-01-31-at-21.44.01


9. 总结

本文通过一个系统监控机器人示例,演示了:

  • 如何使用 HubSpot Slack SDK 快速接入 Slack
  • 解耦设计:检查器与上报器分离,便于扩展
  • 频道消息与私信两种通知方式的实现
  • 定时任务集成

该模式可轻松扩展至 CPU、内存、服务健康检查等场景。Slack API 功能丰富,如需更复杂交互(如交互式按钮、模态框),可进一步探索其 Block Kit。

源码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/saas-modules/slack


原始标题:How to Create a Slack Plugin in Java