39
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SlackAdvent Calendar 2019

Day 9

Bolt + jsx-slack + TypeScript を使って Slack App をつくる

Last updated at Posted at 2019-12-09

となるため、今後はこんな構成になりがちなのではないでしょうか:thinking:

がまだそんなに事例を見ていないのでちょっとやってみて記事に起こしてみました。
(まとめが追いついておらずだいぶ半端になっている)

私自身まだ色々試行錯誤中なため、色々ご意見頂戴できると嬉しいです:pray:

やらない

  • Slack App の作成とインストール
  • デプロイ
    • いろんな環境へのデプロイが考えられるしどうしようか考え中
    • そもそもまだデプロイまでやってない

Bolt、jsx-slackについて

:zap: Bolt

Bolt は Slack App をスッと開発できるように作られたフレームワークです。
イベントをリスニングするエンドポイントを作ったり、APIのリクエストをしたりすることが簡単にできます。

:point_down: のコードですぐに立ち上がります。
slackapi/bolt: A framework for building Slack apps

本来であれば、Webアプリケーションサーバをたててイベントを受け付けるエンドポイントをつくり、リクエストのボディを処理して、何らかのロジックを組み込む必要がありますが、Boltを使えば Slack App のロジック部分を作ることに集中できます。

jsx-slack

まず、jsx-slack の前にBlock Kit についてです。

Block Kit

Block Kit は Slack App のための UI フレームワークです。
メッセージやモーダル、最近では Slack App の Home タブで UI コンポーネントを組み込み、インタラクティブな操作が可能になっています。

  • 画像
  • フォーム
  • テキスト

などをかなり自由にレイアウトすることができます。

本来であれば、 Block Kit Builderを使って GUI でつくって生成される JSON をコードに組み込むことになりますが、一度コードに組み込んだあとのメンテナンスが辛くなります。


それを解決してくれるのが jsx-slack です。
名前のとおり、JSX で Block Kit で作る UI の記述ができます。

:point_down: 下記サンプルのように JSX だとコードをみて直感的に UI のイメージがつきやすくなります。
speee/jsx-slack: Build JSON object for Slack Block Kit surfaces from readable JSX

とりあえず動かすところまで

ボイラープレート + Sample Code

Bolt + TypeScript + jsx-slack でボイラープレート作ったのでこちらを使って動かすとこまで行くことを想定しています。見ながら参考にしてください。
phigasui/slack_app_boilerplate

これでいつでもサクッと Slack App がつくれるぞ :muscle:

TypeScriptをトランスパイルせずそのまま実行できるように ts-node をつかいます。
また、開発中はホットリロードしてほしいので nodemon をつかいます。

Slack App 作って Token を設定

PORT 番号はよしなに

.env
SLACK_SIGNING_SECRET=****************************
SLACK_BOT_TOKEN=xoxb-**********************************

PORT=3000

ローカルで動かす

Bolt のチュートリアルでも使っている ngrok をつかってローカルで Slack App を使えるようにします。

各々の環境で ngrok をインストールし、

$ ngrok http {PORT}

再実行すると、Slack App に設定する URL を変えないとならないため、かなりしんどい

Slack App に エンドポイントのURLを設定

Bolt で作るアプリケーションは /slack/events をリスニングするため、ngrok が発行した URL を使って下記のように設定します。

スラッシュコマンド

image.png

モーダルなどでのアクション

image.png

Slack App を立ち上げる

$ yarn start
  yarn run v1.19.1                                                                                                                                                   
  $ ./node_modules/.bin/nodemon                                                                                                                                      
  [nodemon] 2.0.1                                                                                                                                                    
  [nodemon] to restart at any time, enter `rs`                                                                                                                       
  [nodemon] watching dir(s): src/**/*                                                                                                                                
  [nodemon] watching extensions: ts,tsx                                                                                                                              
  [nodemon] starting `./node_modules/.bin/ts-node ./src/App.ts`                                                                                                      
  ⚡️ Bolt app is running!

実装について

ディレクトリ構成

JSX のコードはメインではなく、ロジックのコードが .tsx になってしまうのは嫌だったので Views.ts で各種 .tsx をimport して一括でexport して切り離すようにしました。

$ tree src
src
├── App.ts
├── Views.ts
└── views
    └── SampleFormModal.tsx

スラッシュコマンドの定義とBoltの実行

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET
})

app.command('/sample_form_modal', async ({ ack, body, context, payload }) => {
  ack()

  const user_name: string = body.user_name

  try {
    app.client.views.open({
      token: context.botToken,
      trigger_id: payload.trigger_id,
      view: SampleFormModal({ name: user_name })
    })
  } catch (error) {
    console.log(error)
  }
})

const run = async () => {
  await app.start(process.env.PORT || 3000)

  console.log('⚡️ Bolt app is running!')
}

run()

View を作る

JSX で Block Kit の UI をつくってそれを Block Kit Builder で表示できるツールを用意してくれていたため、それを利用しました。
@speee-js/jsx-slack: Build JSON for Slack Block Kit from JSX

/** @jsx JSXSlack.h */
import JSXSlack, { Input, Modal, Section, Textarea } from '@speee-js/jsx-slack'

export default ({ name }: { name: string }) => {
  return JSXSlack(
    <Modal title="Article" close="Cancel" callbackId="send_form">
      <Section>
        Hello {name}!
        <p>記事を作成します。</p>
      </Section>

      <Input label="タイトル" type="text" blockId="title" actionId="title" required />
      <Input label="タグ" type="text" blockId="tags" actionId="tags" placeholder="スペース区切りで複数登録 ex.'Ruby Rails'" />
      <Textarea label="本文" blockId="body" actionId="body" required />

      <Input type="submit" value="Save" />
    </Modal>
  )
}

サンプルを見ながら作ってみるとそれっぽくできるため JSX のありがたみを感じます。

サンプルのフォームを見るとなんとなくわかるのですが、もともと実装していたものでは Qiita、Qitta Team の Personal Access Token の保存のための DB アクセスや API リクエストを組み込んでいますが、今回は割愛:pray:

モーダルからのアクションの受け取り

上記 view で作ったフォームを送信するとイベントが送信されるためそれをリスニングする app.view を定義します。

interface FormViewStateValues {
  values: {
    title: { title: { value: string } }
    tags: { tags: { value: string } }
    body: { body: { value: string } }
  }
}

app.view('send_form', async ({ ack, body, context, view }) => {
  ack()

  const user_id: string = body.user.id

  const form_view_state_values = (view.state as FormViewStateValues).values
  const form_title = form_view_state_values.title.title.value
  const form_tags = form_view_state_values.tags.tags.value
  const form_body = form_view_state_values.body.body.value

  app.client.chat.postMessage({
    token: context.botToken,
    channel: user_id,
    text: `Title: ${form_title}\nTags: ${form_tags}\nBody: ${form_body}`
  })
})

ViewOutput.state は作ったUIで変わるため Bolt では抽象度の高い Object になってしまいます。
そのため、TSで扱うには自分でどんな Interface になるか定義する必要があります。

bolt/index.ts at 5ed8c89f3442448f7b0c1c2c8581d46b93533e7b · slackapi/bolt

ここはしんどいですが、ViewOutputSlackViewAction など、コールバックで受け取る引数のオブジェクトがどんなプロパティを持っているのか補完が効くところで TypeScript で実装している恩恵が受けれて良いかなと思います。

もうおしまい

Bolt や jsx-slack のおかげで Slack App を作る敷居がかなり下がっていて大変ありがたさを感じます:pray:
今後開発していく上で手放せない存在になりそうです。

参考

39
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?