selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

SlackのBlock Kitをjsxの記法で書ける jsx-slackを試してみた

TL;DR

  • 僕は個人的に datadog_slack_reporterというものを作成して、datadogで監視しているサービスの台数をslackに通知しています
  • Slackのメッセージ作成部分を、SlackのBlock Kitをjsxの方式で記述できる jsx-slack に置き換えてみました
  • 面倒なjsonの作成部分がReactっぽく書けるので、めちゃくちゃ便利でした
  • この資料にはjsx-slackをTypeScriptで導入する上で必要な設定方法を記載します

jsx-slackとは何か

Slackのメッセージ作成をjsxのフォーマットで行うことができるnpmのpackageです。これを利用すれば、Reactのcomponentを書くような書き味で複雑なjsonの作成をすることができます。

スクリーンショット 2019-03-04 12.07.34.png

上の図はjsx-slack内で生成されるjsonが確認できるページです。こちらの右側のjsonがslackが要求するjsonのフォーマットとなっており、左側がjsx-slackを利用して記述するコードになります。jsx-slackの動作確認ページはこちらになります。

成果物

最初に、今回作成したものの全体像をざっくり説明します。

Slackへのメッセージ出力

スクリーンショット 2019-03-04 11.22.05.png

こちらが、今回僕が作成したbotが実際にslackへ投稿したメッセージです。

ソースコード (一部抜粋)

jsx-slackを使った slackへのメッセージ送信ボットのプログラムがこちらになります。全体像を掴むために、さらっと眺めてもらえれば大丈夫です。

Cloud Functionsのhandler

import moment from "moment-timezone";
import "source-map-support/register";
import { DatadogHostMetrics } from "./datadog";
import { DatadogClient } from "./DatadogClient";
import { SlackClient } from "./SlackClient";
import { slackMessageBlock } from "./SlackMessageBlock";

const datadogClient = new DatadogClient();
const slackClient = new SlackClient();

export async function datadog_handler(data: any): Promise<void> {
  const fromTime = moment({ hour: 0, minute: 0, second: 0 })
    .tz("Asia/Tokyo")
    .subtract(1, "days")
    .format("X");

  const toTime = moment({ hour: 23, minute: 59, second: 59 })
    .tz("Asia/Tokyo")
    .subtract(1, "days")
    .format("X");
  // datadogのAPIを叩いてデータを取ってくる
  const hostMetrics: DatadogHostMetrics[] = await datadogClient.countHosts(fromTime, toTime);
  // 送信するメッセージを生成する
  const blocks = slackMessageBlock(fromTime, toTime, hostMetrics);
  // メッセージを送信する
  await slackClient.post(blocks);
}

messageの作成部分 (jsx-slackで書き直された場所)

/** @jsx JSXSlack.h */
import JSXSlack, { Block, Section } from "@speee-js/jsx-slack";
import moment from "moment-timezone";
import { DatadogHostMetrics } from "./datadog";
import { ProductMetrics } from "./ProductMetrics";

export function slackMessageBlock(fromTime: string, toTime: string, hostMetrics: DatadogHostMetrics[]) {
  const messages = [];
  for (const metrics of hostMetrics) {
    const productMetrics = new ProductMetrics(metrics);
    const message = (
      <blockquote>
        <b> {productMetrics.name} </b>
        <br />
        min:${productMetrics.minHostCount()} ~ max:${productMetrics.maxHostCount()}
        sum(host*hours):${productMetrics.sum()}
      </blockquote>
    );
    messages.push(message);
  }

  return JSXSlack(
    <Block>
      <Section>
        <p>datadog monitoring daily report</p>
        {moment.unix(parseInt(fromTime, 10)).toString()} ~ {moment.unix(parseInt(toTime, 10)).toString()}
        {messages}
      </Section>
    </Block>
  );
}

messageの送信部分

import { WebClient } from "@slack/client";

export class SlackClient {
  private static username: string = "Datadog按分計算Bot";
  private readonly channelID: string = process.env.CHANNEL_ID as string;
  private readonly token: string = process.env.SlackToken as string;
  private readonly client: WebClient;

  constructor() {
    this.client = new WebClient(this.token);
  }

  public post(blocks: any) {
    return this.client.chat.postMessage({
      channel: this.channelID,
      text: "",
      blocks,
      username: SlackClient.username,
    });
  }
}

実際のコードはこちらを参照してください。 https://github.com/selmertsx/datadog_slack_report/pull/4

基本的な書き方

  • 最初にslack本家のblock-kit-builderで、サンプルを見ながらどのようなblock kitが作りたいのか考えます。
  • jsx-slackのリポジトリ内にはサンプルがいくつかあるので、そちらを参照しても良いでしょう
  • その後、jsx-slackのサイトで目的のjsonが生成できる記法を探します。

jsx-slackの導入設定

TypeScriptで導入する際は少しだけ設定をする必要があります。公式ドキュメントを見ると下記のような記載があります。

https://github.com/speee/jsx-slack

A prgama would work in Babel (@babel/plugin-transform-react-jsx) and TypeScript >= 2.8 with --jsx react.

ということで、jsx-slackをTypeScriptで利用するにはtsconfigの設定が必要であることが分かります。以降、それらの設定を行っていきましょう。

@speee-js/jsx-slackのinstall

npm i @speee-js/jsx-slack

今回の設定に必要なpackageを上記コマンドで全部installします。

tsconfigの設定

TypeScriptのjsxに関するドキュメントを読んでみましょう。

https://www.typescriptlang.org/docs/handbook/jsx.html#factory-functions

The exact factory function used by the jsx: react compiler option is configurable. It may be set using either the jsxFactory command line option, or an inline @jsx comment pragma to set it on a per-file basis.

上記コメントで示されているように、TypeScriptでjsxを利用するためには、ファイルごとのpragmaの設定と、コンパイラオプションの指定が必要になります。pragmaの設定とは、サンプルコードで示されていた /** @jsx JSXSlack.h */ の部分を指します。コンパイラオプションは、下記のように設定します。

{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "lib": [
      "es5",
      "es2015",
      "es2016.array.include",
      "esnext.asynciterable"
    ],
    "strict": true,
    "outDir": "./",
    "esModuleInterop": true,
    "noImplicitAny": true,
    "allowJs": true,
    "jsx": "react" // <= 今回追加した設定
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules",
    "__tests__",
    "**/__mocks__/*.ts"
  ]
}

実際のコードはこちら

最後に

jsx-slack 導入の手順は以上になります。今回の例はちょっとシンプルすぎるのであまり有り難みを感じにくいかも知れませんが、もうちょっと複雑になってくると jsxを利用してReactっぽく書ける jsx-slackに大きなメリットを感じられるかと思います。