初心者でも分かる「Slack Events API」の使い方



# はじめに
Slack APIには、Web API、Events API、RTM API などの種類があります。（詳細は[公式サイト](https://api.slack.com/lang/ja-jp/which-api)参照）

今回は、その中でも所謂WebHookの一種である<font color="Orange">Slack Events API</font>の使い方をまとめていきます。

:::note 
## 記事の概要
- 実現したいこと
    - Slackのイベント（メッセージ投稿）などをトリガーにして、特定の処理を実行させたい
　
- ポイントは2つ
    - Slack Appを作成する
    - Event Subscriptionsを設定する
:::

# 1. Slack Appの作成
### Slack Events APIを使用するためには、まずSlack Appを作成する必要があります。
　
① Slack API の[公式サイト](https://api.slack.com/)にアクセス（サインインした状態で）し、「Your Apps」に移動
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/085368c2-fd6e-d588-0cd7-545a97c2e9f7.png" width=350>

② 「Create New App」をクリック
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/dc85f50a-3a93-a7e2-437d-44336290f79c.png" width=350>

③ 「From scratch」を選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/5ffb9a73-d096-7679-1193-047797878ba5.png" width=350>

④ アプリの名前を入力し、使用するワークスペースを選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/a16788fe-3c7e-cf49-532e-0fd5f687981f.png" width=350>

⑤ 無事にアプリが作成されると「Basic Information」というページに遷移する
次に、サイドメニューから「OAuth & Permissions」をクリックして、アプリに必要な権限設定に進みます
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/e21a02b2-1b55-4156-0d21-1c7cc1cdd707.png" width=350>


# 2. 権限の設定
### 作成したSlack Appに必要な権限を設定します。

① 「OAuth & Permissions」ページを下にスクロールし、「Scopes」セクションで、アプリに必要な権限の範囲を設定します。
（Slack Appのボットが行う操作に必要な一般的な権限はボット権限 (Bot Token Scopes)に追加します。）

<details><summary><font color="Orange">⚠️ユーザー権限 (User Token Scopes)とボット権限 (Bot Token Scopes)について</font></summary>

:::note warn
- ユーザー権限 (User Token Scopes)
    - 個々のユーザーに関連する情報や操作が可能です。
    - ユーザーの個人情報にアクセスするための権限が含まれます。例えば、users:read スコープを使用すると、ユーザーの基本情報を取得できます。
    - ユーザートークンを使用すると、そのユーザーがアクセスできるすべての情報や操作が可能になります。これには、ユーザーが参加しているすべてのパブリックチャンネルの情報を取得することも含まれます。
　
- ボット権限 (Bot Token Scopes)
    - Slackアプリのボットが行う操作に必要な権限です。
    - ボットはプログラムによって制御され、与えられた権限に基づいてのみ操作を行います。
    - 例えば、chat:write スコープを使用すると、ボットがチャンネルにメッセージを投稿できるようになります。
    - ボットトークンを使用すると、ボットが参加しているチャンネルの情報のみを操作や取得が可能です。
　
- トークンの種類とスコープの違い
    - ユーザートークンは、特定のユーザーができる操作をアプリが代行できるようにします。これにより、ユーザーがアクセスできるすべての情報や機能にアプリがアクセスできるようになります。
    - ボットトークンは、アプリがSlack内で自動化された活動を行うために使用されます。ボットは、そのトークンで許可された操作のみを実行できます。
:::
</details>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/1169a03e-2a52-e43e-d6f8-559bad7449e4.png" width=350>

② 「Add an OAuth Scope」ボタンをクリックして必要なスコープを追加
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/1a2928f3-6e3d-e165-0155-d44a493a5bab.png" width=350>


:::note info
主なスコープの説明
- `channels:history`
このスコープは、ボットがパブリックチャンネルのメッセージ履歴にアクセスするために必要です。メッセージを読み取る際に使用します。
- `chat:write`
ボットがチャンネルにメッセージを投稿するために必要なスコープです。メッセージを送信する機能を実装する際に必要になります。
- `app_mentions:read`
ボットが自身へのメンションをリアルタイムで読み取るために使用します。ボットがメンションされた際に反応する機能を持たせたい場合に必要です。
- `im:history`
ボットがダイレクトメッセージの履歴にアクセスするために必要なスコープです。プライベートな会話の内容を取得する場合に使用します。

**※ 公式サイトの[Permission scopes](https://api.slack.com/scopes)から権限一覧を確認できるので、必要な権限を追加しましょう**
:::

③ スコープを追加したら変更を保存し、ページ上部までスクロールを戻し、「Install to Workspace」ボタンをクリックして、アプリをワークスペースにインストールします。
これにより、新しい権限がアプリに適用されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/c698ddc4-d6ff-a22c-3527-b253366e5ea7.png" width=350>

# 3. Bot User OAuth Tokenの取得
### Slack Appをワークスペースにインストールした後、ページに表示される「Bot User OAuth Access Token」をコピーしておきます。（必要であれば）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/a5e754d4-c62a-e97f-e3a0-bb31daa1489e.png" width=350>

<details><summary><font color="Orange">⚠️Bot User OAuth Access Tokenとは</font></summary>

:::note warn
SlackのWeb APIを使用する場合、Bot User OAuth Access Tokenが必要です。
Events APIを使用して単にイベントを受け取るだけであれば、トークンは必要ありません。

つまり...
- `Events API`
    - Slackからのイベント（例えばメッセージが投稿されたときなど）を受け取るために設定します。
    - このAPIは、指定されたURL（あなたのサーバー上のエンドポイント）にHTTP POSTリクエストを送信してイベントを通知します。
    - このプロセスにはトークンは不要です。
- `Web APIの使用`
    - Slackの機能をプログラム的に操作するためにAPIを呼び出す際には、Bot User OAuth Access Tokenが必要です。
    - これにはメッセージの送信、チャンネルの管理などが含まれます。

したがって、もし単にEvents APIを使ってSlackからのイベントを受け取り、それに基づいて何か処理を行うだけであれば、トークンは必要ありません。
しかし、そのイベントに応じてSlackに対して何かアクションを起こす（例えばメッセージを送るなど）場合は、そのアクションを認証するためにトークンが必要になります。
:::
</details>

# 4.Event Subscriptionsの登録
### 作成したSlack Appに「トリガーとなるイベント」と「実行させたい処理のエンドポイント」を設定します。

① 「Event Subscriptions」ページを開き、「Enable Events」をオン
② リクエストを受け取るエンドポイントのURLを「Request URL」に入力します。
（このURLは、Slackからのイベントを受け取るためのものです。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/92263577-f4d5-391e-cddb-d789a2f57fbb.png" width=350>

③ URLを入力すると、Slackはそのエンドポイントに一時的な検証リクエストを送信し、応答を確認します。
このプロセスはURLの検証と呼ばれ、エンドポイントが正しく設定されていることを確認するために必要です。

<font color="red">...？？？</font>

どういうことかというと...
Slackから「あるパラメーター」が送られてくるので、それをそのまま「レスポンスとして返す」コードを書いておき、その状態で一度URLを設定する必要があるのです。

:::note alert
レスポンスを返さないURLをそのまま入力すると以下のようにエラーとなり登録ができません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/7aa32d42-b13e-3526-5d75-d577f4b5d7d5.png" width=350>
:::

これだけだと何のことか分からないので、URLの検証については、次でもう少し詳しく記載していきます。

---
### URLの検証プロセス

Slackは、設定されたRequest URLが正しいかどうかを確認するために、以下のステップでURL検証プロセスを実施します。

(1) URL設定
Slackアプリの設定ページで、Events APIのRequest URLを入力します。
　
(2) 検証リクエストの送信
SlackからそのURLに対してHTTP POSTリクエストが送られます。
このリクエストには、以下JSON形式のパラメータが含まれています。
```json
{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}
```

(3) レスポンスの返送
サーバーは、受け取ったchallengeパラメータをそのままレスポンスとして返す必要があります。

(4) 検証の完了
Slackは返されたchallengeパラメータを確認し、一致する場合はURLが正しく設定されていると認識されます。

:::note info
この検証プロセスにより、Slackはイベントデータを送信する先が正しいサーバーであることを確認します。
URL検証は、Request URLを初めて設定する際、またはRequest URLを変更した際にのみ行われるので、一度検証が成功すれば、その後は同じURLに対して再度検証を行う必要はありません。

**※ 詳細は[公式サイトのURL verification handshake](https://api.slack.com/apis/connections/events-api#handshake)を参照**
:::
---

### コードの実装例
URL検証に対応したコードの実装例です。Goで書くとこんな感じになると思います。
```go
1.Slackから受け取ったパラメーターをそのまま返す処理


func SlackURLVerification(w http.ResponseWriter, r *http.Request) {
	// Slackからのリクエストを格納する構造体
	var reqBody struct {
		Token     string `json:"token"`
		Challenge string `json:"challenge"`
		Type      string `json:"type"`
	}

	// HTTPリクエストからボディを読み込む
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "could not read request body", http.StatusBadRequest)
		return
	}

	// リクエストボディを構造体に変換
	if err := json.Unmarshal(body, &reqBody); err != nil {
		http.Error(w, "could not decode request body", http.StatusBadRequest)
		return
	}

	// リクエストのタイプがURL検証（url_verification）の場合は、チャレンジ（Challenge）を返す
	if reqBody.Type == "url_verification" {
		w.Header().Set("Content-Type", "text/plain")
		w.Write([]byte(reqBody.Challenge))
		return
	}
}
```
```go
2.上の処理をサーバーのエンドポイントに設定する


package main

func main() {
	port := 4000

	// SlackEventAPIのエンドポイントの設定
	http.HandleFunc("/slack/events", func(w http.ResponseWriter, r *http.Request) {
		SlackURLVerification(w, r)
	})

	// CORSの設定
	c := cors.New(cors.Options{
		AllowedOrigins:   []string{"*"},
		AllowCredentials: true,
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE"},
	})
	httpHandler := c.Handler(http.DefaultServeMux)

	// サーバーの起動
	err = http.ListenAndServe(":"+strconv.Itoa(port), httpHandler)
	if err != nil {
		log.Fatalf("failed to start HTTP server: %v", err)
	}
}

```
---
⑤ URL検証に対する準備ができたら、再度URLを設定します。
正しくレスポンスを返すようにコードを書いていれば、認証が通り「Verified」が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/92d8ccf0-5815-5037-bf1f-fb4cec5e4253.png" width=350>

⑥ 「Subscribe to bot events」の登録
画面を下にスクロールして「Subscribe to bot events」セクションで、「Add Bot User Event」ボタンをクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/38ec1fe5-df8d-fa45-73d9-6c3c9ed6c73d.png" width=350>


⑦ 上で登録したRequest URL（エンドポイント）に対してHTTPリクエストを送信するトリガーとなるイベントを登録します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/23515ec4-80d8-c28a-b1bf-c8089041425b.png" width=350>

:::note info
例えば、メッセージがチャンネルに投稿されたときをトリガーとして処理を実行させたい場合は、`message.channels`イベントを追加します。

**※ 公式サイトの[Event types](https://api.slack.com/events)からイベント一覧を確認できるので、必要なイベントを追加しましょう**
:::

:::note warn
この画面の「Required Scope」が、「2.権限の設定」で設定したScopeに含まれている必要があります。
もしも設定が足りない場合、再度「2.権限の設定」に戻り設定を追加してください。
:::


⑧ 設定が完了したら、「Save Changes」ボタンをクリックして設定を保存します。
ここまでで、Slackサイト上での設定は完了です！



# 5.Slackのチャンネルにアプリを追加する
① チャンネルの詳細から作成したSlack Appをチャンネルに追加します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192049/632b8f34-a7df-ff76-dc87-cdb680488c12.png" width=350>

② これで、特定のイベントに基づいて(今回の例だと「チャンネルにメッセージが投稿されるたび」）に、登録したエンドポイントの処理が実行されるようになります。

# 6.おわりに
今回の記事では、あくまでもEvents APIの導入の仕方までにとどめ、Slack Web APIの使い方については別にしたいと思います。

なお余談ですが、サーバーエラー時などのSlack Events APIのリクエスト再送の仕様などについては、下記記事を参考にしていただくと良さそうです。

https://dev.classmethod.jp/articles/slack-resend-matome/

最後まで当記事をご覧いただきありがとうございました:bow: