Darkを使用して、Slack App作成する

# はじめに

本書では、Darkを使用したSlack Appの作成を行います。Darkに関しては、[「新たな開発プラットフォーム "Dark/Darklang" を実際に触ってみて」](https://qiita.com/Morero/items/9cb710c1502c76d6c07f)を参考して頂ければと思います。

Darkでは、**トレース駆動開発**を駆使する事で、ビジネスロジックのみを記述するだけで開発が可能ですので、その点も注目して貰えたらと思います。

# 1. Slackアプリケーションの開発
Slack Appの開発を始めていきます。アプリケーション名は`sampleApp`としました。

今回は、簡単にスラッシュコマンド`/test`を入力するとワニの画像が返されるようにします。

## 1.1. キャンバスの作成

開発する為にキャンバスを新規作成します。

アカウントには、`darklang.com/a/USERNAME-CANVASNAME`からアクセスできます。今回は、Slack Appを作成するので、`darklang.com/a/USERNAME-slackapp`と名付けました。

入力後にアクセスすると、キャンバスが立ち上がります。

## 1.2. Slackコマンドの追加

次に、[Slack API](https://api.slack.com/)からSlack Appを新規作成します。

アプリケーションをワークスペースにインストールする為には権限が必要です。その為、筆者は独自のワークスペースを作成し、その中にSlack Appをセットアップする事にしました。

Slackコマンドに`/test`コマンドを追加し、`USERNAME-CANVASNAME.builtwithdark.com/test`でリクエストURLを指定します。今回は以下の様に設定しました。

![Screen Shot 2020-03-27 at 1.54.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/1e83909b-6221-5018-5410-ad1f9339a5fe.png)


必要に応じて、ここでSlackbot等の他の機能を追加できますが、今回はこれをベースに開発していきます。

## 1.3. OAuthのセットアップ
### 1.3.1 リダイレクトURLの設定

アプリを配布できるようにする為、OAuthをセットアップします。Slack APIの`OAuth & Permissions`から、リダイレクトURLを追加します。

![Screen Shot 2020-03-24 at 10.55.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/57f71835-b1e7-62d7-57e2-badbd25b91b6.png)

DarkではOAuthの使用する際は以下の形式でURLを指定する必要があるので、それに則って記述します。

```
https://USERNAME-CANVASNAME.builtwithdark.com/OAUTH-REDIRECT
```

URLを保存後、`Settings/Manage Distribution`から共有可能なURLをブラウザのアドレスバーにコピーします。アクセスするとアプリケーションをインストールできるのでインストールします。この段階ではアクセスしてもバックエンドのロジックは何も記述していない為、以下の様なエラーが表示されます。

```
404 Not Found: No route matches
```

しかし、この様な振る舞いを行う事でDarkは痕跡を確認する事ができ、それをトレースする事で開発を行います。

筆者自身、この**トレース駆動開発**にはまだ慣れていない部分もあるが、敢えてこの様に異常検知を起こす事でアクセスに必要な情報をトレースして開発を行う事がDarkを使用する上での１つのメリットです。

Darkのキャンバス(`https://darklang.com/a/USERNAME-slackapp`)に戻り、セクションを確認してみると404セクションに`/oauth-redirect`が表示されている事が確認できます。

![Screen Shot 2020-03-23 at 4.41.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/9f1db7b9-2955-eed3-8a65-d39538adf029.png)

この404にある`/oauth-redirect`をプロダクショントレースしてバックエンドを構築していきます。`+`をクリックすると、`GET`メソッドのOAuthリクエストに応答するHTTPハンドラを作成します。

![Screen Shot 2020-03-23 at 5.11.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/af8f7195-2f0f-73b5-d6d3-54c5176094b1.png)

作成したものを確認してみると、SlackがDarkアプリに対して行ったリクエストを示すトレースが表示されています。ここには、Slackに返答する際に必要なコードが含まれます。(一部個人情報を含む為、加工している)

### 1.3.2. トークンの取得

Slackアプリに送信する為には`client_id`と`client_secret`が必要です。これらの情報は、Slackの`Settings/App Credentials`から確認する事ができます。

![Screen Shot 2020-03-27 at 1.58.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/67fb8776-02fb-47ee-6ee9-8135c428d89d.png)

確認後、作成した`/oauth-redirect`ハンドラにコードを記述していきます。ここで、`client_id`、`client_secret`をペーストする際は最初に文字リテラル`"`を入力してからでないと、Darkではフロート型の様に扱われ、全ての文字列がペーストされません。

![Screen Shot 2020-03-27 at 0.16.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/cad730a3-3111-d444-d6c3-4b6b8ac7b8f0.png)

また、実行すると偶に`oauth_authorization_url_mismatch`と返される場合がありますが、それはOAuthの認証URLが異なるからです。どちらのURLを使用するかは、`Settings/App Credentials`のClient IDを見ると右下に小さく書いているので、それで判断する事が可能できます。

```
# どちらかのURLを使用する
https://slack.com/api/oauth.access
https://slack.com/api/oauth.v2.access
```

※ 今回、筆者は`oauth.v2.access`を使用していますが、`oauth.v2.access`からレスポンスで返されるJSONの仕様が変更されています。その為、`oauth.access`を使用する場合は、後述で説明するトークン保存時のパラメータの指定方法が異なるので注意して下さい。

これにより、Slack APIの一部([oauth.v2.access](https://api.slack.com/methods/oauth.v2.access))が呼び出され、リクエストされたSlackにアクセス可能になります。実行すると、正しくレスポンスが返されれば`team.id`、`access_token`が取得できます。

しかし、コードの有効期限が切れると以下の様なエラーが表示されます。その場合は、面倒だが再度ブラウザから共有リンクを開き、認証を行う必要があります。

![Screen Shot 2020-03-23 at 21.28.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/c6e3fd5f-2a69-fd70-0d35-2c698981005f.png)

### 1.3.2. トークンの保存

次に、取得したトークンとデータストアに保存します。アドレスバーから新しいデートストアを作成し、保存するフィールドにスキーマを設定します。`bot_token`は`oauth.v2.access`を使用する場合は不要です。

![Screen Shot 2020-03-23 at 21.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/ac883260-3f03-1b37-0839-a2728fc098e0.png)

データストアの作成が完了すれば、`/oauth-redirect`にロジックを記述していきます。

![Screen Shot 2020-03-27 at 0.15.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/3b667ee2-34e5-e392-bfb9-6d5220605873.png)

## 1.4. アプリケーション機能の構築

ワークスペースにアプリケーションをインストールしたので、Slackを起動して確認してみます。設定したスラッシュコマンド`/test`を入力してもまだロジックが実装されていない為、エラーが表示されます。

![Screen Shot 2020-03-23 at 22.50.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/75981690-9230-37e0-e552-4e643a7ebc3f.png)

しかし、このトリガーからDarkの404セクションにルートが表示されるので、それをトレースして機能を構築していきます。404セクションからエンドポイントを作成するとトレースを操作可能です。

![Screen Shot 2020-03-23 at 22.43.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/4a20d6be-d57e-b405-1289-a77b2eac55f8.png)

今回のアプリケーションでは以下の様に実装します。リクエストURLを取得し、そのリクエストに対してテキストを返します。

![Screen Shot 2020-03-27 at 0.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/dd562abe-a087-6f65-641f-7e6e01f22621.png)

## 1.5. 動作検証

実際にSlack側で動作させてみます。`/test`と入力すると、Darkで設定したテキストと成功した事を証明するJSONが出力された事が確認できます。

![ezgif.com-video-to-gif (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/e2d8fac0-8cb6-d854-2d26-3aab605f26e7.gif)

JSONの出力を非表示にしたい場合は、`Http::respondWithText`を使用して200コードに対して空白を指定すると表示されなくなります。

![Screen Shot 2020-03-27 at 0.44.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/83a637e9-b97b-524d-fa5c-854d97aa5045.png)

以上で、DarkでSlack Appの実装する事ができました。

## 2. データストアを活用したアプリケーションの実装

応用として、データストアに保存された50音を行ごとにランダムに出力する様に実装してみます。

まず、`RandomMessages`と言うデータストアを用意し、フィールドに`message`と言うスキーマを用意します。そこにREPLでデータストアに対して登録を行う`addMessages`を用意する。続けて、データストアに登録された情報を確認する為にREPLで`setMessages`も用意した。

![Screen Shot 2020-03-27 at 1.09.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/cfe07afe-2c13-b157-34bc-aa801e06141a.png)

次に、先程使用した`/test`のロジックを一部修正し、その結果をSlack側に送信します。

![Screen Shot 2020-03-27 at 1.11.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/c66d9d73-5fd3-265d-a4e2-2c03f7e42b03.png)

追加した変数の概要は以下の通りです。

|変数名|内容|
|---|---|
| allMessages |`RandomMessages`に保存されている情報を全て取得し、配列に保存する。 |
| messageCount | 配列の長さを取得する。 |
| messageCountLessOne | 配列は0から始まる為、`1`除算する。 |
| index |ランダム値を取得する。範囲として`0`から`messageCountLessOne`を指定する。 |
| message | indexの値に該当する情報をデータストアから取得する。 |

実際に動かしてみると、以下の様にランダムに結果が表示される事がわかります。先程、実装した`Http::respondWithText`によりJSONが出力されない事も併せて見て貰うと良いかと思います。

![ezgif.com-video-to-gif (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/bc2b3268-dd03-dbb8-40c1-941cb6254938.gif)


## 3. 最後に

今回は、DarkでSlack Appの実装を行いました。筆者は、Slack Appの開発は初めてであった為、Dark以外でいくつか躓く点が多かったのですが、開発した事のある方であれば比較的容易に開発できるのではないでしょうか。実際に今回開発した全体図を見ると、非常に少ないコードで開発できている事がわかります。

![Screen Shot 2020-03-27 at 1.41.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223371/1fcfaf07-9566-e912-9d76-b99c07cbb268.png)

また、Darkでは定義した変数名をクリックすると即座に値を確認できるので、意図した値が返ってきているかどうかを知ることができます。これにより、コードを記述している間も修正箇所に気づきやすいので、逐一デバッグを行い確認する必要はありません。

最後に開発を通して発見した事ですが、Darkで日本語文字列を入力しようとすると変換をする際にスペースも誤入力されてしまう様です。この点に関しては、今後の改善希望として報告しておこうと思います。

【2020/03/27 03:43追記】
ベータ版と言う事もあり、現段階ではテキスト構成のサポートがまだ十分ではないとの事。回避策として、もし日本語入力を行いたい際はコピー&ペーストを使用して入力して下さい。