JavaによるSlack Bot の実装

# 既存のアプリケーションをSlackに対応させるには？

Hubot, Microsoft BotFrameworkなど選択肢は複数ありますが、Slackが提供するフレームワークboltを使用して既存のアプリをSlackに対応させる方法・手順について記載します。

## Slack Boltフレームワークとは？フレームワークの選定経緯

Slackアプリを作成するための、nodejsで実装されたフレームワーク
なんと言ってもSDKの形で、JavaScript, python, javaなどにバインディングされているのが良いと思います。
チュートリアル、入門資料も豊富にあり実装へのハードルは他のフレームワークより低いと考えられます。

## この記事で実現するもの

### 以下の機能を備えたSlackアプリを構築する

* Event(ユーザとの通常の対話）を処理する

# Slackアプリを登録する（Slack側）

## アプリ作成

[Create New App](https://api.slack.com/apps) より新規アプリを登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/ef4fd006-f138-d065-f4a7-35d75340f432.png)

## OAuth & Permissionsの設定

Permissionを設定します。
設定は一例ですので必要に応じて設定を変更してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/037a7eb1-ce4d-6d9f-a891-d37697d79c54.png)

### Scope - Bot Token Scopes
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/34d45c50-003e-9325-48a3-f27160ff6d5a.png)


## Workspace にアプリをインストール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/70ddd236-5c9f-b7a7-1304-5161d9e7baa2.png)

## Tokenの確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/50e0cd3e-6d37-78b8-7f5d-8aea9fa98d11.png)

プログラム側で`User OAuth Token`, `Bot User UAuth Token`は使用します。

# Eventの購読

## 有効化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/582dd952-d0b5-c5cc-5d5c-f86374f2dc14.png)

有効なURLであるかを確認するリクエストを処理する必要があります。

### Java (Spring)

Restを処理できればなんで良いですが、ここではSpringを使用します。

Maven

```xml
<dependency>
	<groupId>com.slack.api</groupId>
	<artifactId>bolt</artifactId>
	<version>1.7.1</version>
</dependency>
<dependency>
	<groupId>com.slack.api</groupId>
	<artifactId>slack-api-client</artifactId>
	<version>1.7.1</version>
</dependency>
<dependency>
	<groupId>com.slack.api</groupId>
	<artifactId>slack-api-model</artifactId>
	<version>1.7.1</version>
</dependency>
<dependency>
	<groupId>com.slack.api</groupId>
	<artifactId>bolt-servlet</artifactId>
	<version>1.7.1</version>
</dependency>
<dependency>
	<groupId>com.slack.api</groupId>
	<artifactId>slack-app-backend</artifactId>
	<version>1.7.1</version>
</dependency>
```

```java:SlackController.java
@PostMapping("/event")
public String postSlackEvent(@RequestBody String args) throws Exception {
    Map<String, Object> argsMap = gson.fromJson(args, new TypeToken<HashMap<String, Object>>() {}.getType());
    switch (argsMap.get("type").toString()) {
        case "url_verification":
            return argsMap.get("challenge").toString();
        default:
            throw new Exception();
    }
}

```

下記の形式のJSONを受信するので`type=url_verification` の場合に、`challenge`を単純に返答するだけでOK

```json
{
  "token": "aksfaksdjfadfTAJq46UTBUv",
  "challenge": "mheVygaHsfalksfjlasdkfjalskdfj940AeLzE9hNZL5",
  "type": "url_verification"
}
```

当然ながら、インターネット（Slack)から接続できる必要があります。
[ngrokが便利すぎる](https://qiita.com/mininobu/items/b45dbc70faedf30f484e)

## URL検証イベントの処理

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/1975ec47-4ec5-7af2-2ee0-f8e19d983d03.png)

## 購読するイベントの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/5e6f53b0-a0f7-7cd7-9d9a-a7940cfdb37e.png)

設定は一例ですので必要に応じて設定を変更してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339006/afa0e57b-383e-8435-acf8-eb9c2a3673a4.png)
`Save Changes`を忘れずに。

# Event処理

単純なエコーを実装します

```java:SlackController.java
@PostMapping("/event")
public String postSlackEvent(@RequestBody String args) throws Exception {
    Map<String, Object> argsMap = gson.fromJson(args, new TypeToken<HashMap<String, Object>>() {}.getType());
    switch (argsMap.get("type").toString()) {
        case "url_verification":
            return argsMap.get("challenge").toString();

        case "event_callback": 
            var msg = gson.fromJson(args, MessagePayload.class);
            if (!StringUtils.isEmpty(msg.getEvent().getBotId())) {
                return "";
            }
            var who = msg.getEvent().getUser();
            var what = msg.getEvent().getText();          
            var channel = msg.getEvent().getChannel();
            var rmsg = String.format("Echo: %s (%s)", what, who);
            Slack.getInstance().methods(BOT_USER_OAUTH_TOKEN)
                .chatPostMessage(r->r.channel(channel).text(rmsg));
            return "";            
        default:
            throw new Exception();
    }
}
```

EventデータもURL確認と同じエンドポイントに送信されます。

1. `type=event_callback`を識別し、`MessagePayload`にデシアライズ
1. 入力値などから返答メッセージを構築
1. `chatPostMessageでメッセージを送信

シンプルですね。
凝ったメッセージを作成するに、[Block kit](https://slack.dev/java-slack-sdk/guides/ja/composing-messages)を使用します。

# Links

https://api.slack.com/lang/ja-jp/hello-world-bolt
https://slack.dev/java-slack-sdk/guides/ja/composing-messages