Slack の bot を Firebase Functions で作るときの身内用メモ

基本、下記の通りです。

- [Slack アプリフレームワーク Bolt for JavaScript を Cloud Functions for Firebase で動かそう](https://qiita.com/seratch/items/3e44f58a7c8ab41f21f0)

上記記事に slack に関する知識が足りてなくて、ハマりまくった部分をかきだしたものです。

node (bolt.js) を使う前提です。

# 手順

- slack app を作成する
    - App manifest を入力する。(これはマジでわからん。)
    - ワークスペースにインストールする
    - Bot User OAuth Token (長い文字列) を取得する
    - Signing Secret (長い文字列) を取得する
    - 適当なチャンネルに invite する
- firebase functions のプロジェクトを作る
    - yarn add @slack/bolt する
    - コードを書く
    - firebase configs を設定
    - deploy
- slack app の最終設定とテスト
    - Slash Command の Request URL を設定する： これもハマった
- メモ
    - 動作テスト
    - functions のエラーログのとりかた

# Slack App の作成

- [Slack App](https://api.slack.com/)

上記から作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/11eb1eb1-0007-0e3e-cb1f-2808d77fcebb.png)

Create App のボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/98d0936e-8b80-fd0f-ead0-e5fcb806b362.png)

このページにある、右上にある Create App ボタンを押すと、中央に Create an app というパネルがでます。押さなくても出るかもしれません。

このパネルで From an app manifest を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/16ad35cc-5da0-35ab-65d5-2b739f9ed30e.png)

bot を追加する先のワークスペースを選びます。このとき、ワークスペースがプリダウンメニューに出てこないことがあります。その場合は、ワークスペースへのアプリへの追加権限がないかもしれません。その場合は、ワークスペースの管理者に権限を追加してもらうか、自分がオーナーのワークスペースを作成します。

権限があるのに出てこない場合は、slack のアプリなり web でまずワークスペースにログインして「設定と管理」から「ワークスペースの設定」を選んで、設定用の web ページを出します。それから、再度 Create App からやりなおすと出てくることがあります。(このへんよくわからない)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/0e41e89b-654c-113b-627f-558805882630.png)

この画面が出たら、次のように入力します。権限 (scope) が過剰な気がしますが、とりあえずこれならいけるはずです。

```.yaml
_metadata:
  major_version: 1
display_information:
  name: TestBot
features:
  bot_user:
    display_name: TestBot
    always_online: false
  slash_commands:
    - command: /echo-from-firebase
      url: https://your-firebase-project-name.cloudfunctions.net/slack/events
      description: Echo From Firebase
      usage_hint: <something to echo>
      should_escape: false
oauth_config:
  scopes:
    bot:
      - chat:write.public
      - chat:write
      - commands
settings:
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false
```

`name: TestBot` のところを変更すると、slack 上で表示されるアプリ名とかユーザ名が変更されます。

`url` のところは、あとで入力しなおすので、http://localhost とか入れといてもとりあえず大丈夫です(多分)。

入力したら Next を押します。ちなみに、ここで入力漏れがあっても、あとで変更もできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/141d6811-2209-1fb8-54b7-a7497417e19a.png)

この画面が出たら Create を押します。

## OAuth Token を取得する

作成できたら、下記の画面になるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/b1ff1fcb-2daa-71fe-fa8f-4ffdfdee56f9.png)

ちなみに、ゼロ状態からこの画面に行くには、Slack Api のページの右上にある Your Apps を触ってアプリの名前をクリックすると行けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/0c5f2ebd-77e4-7e4c-8529-3eeabe423f24.png)

アプリ名が出てこないときは、slack のアプリか web でワークスペースにログインしてから、アプリの「設定と管理」から「ワークスペースの設定」を選んで、管理ページを出した状態にしてから、slack api のページに行くと、選べるかもしれません…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/bc696105-4269-3070-4107-28699ec03800.png)

左にある OAuth & Permissions を選びます。アプリを作りたてのときは、右側の OAuth Tokens for Your Workspace のところが Install to Workspace になってるので、そのボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/1b9d8c50-e988-b809-d55f-0b5bdb8b8aa1.png)

許可を求められるので、許可します。もしインストールできない場合は、自分にインストール権限がない可能性があります。その場合は、ワークスペースの管理者に権限を追加してもらいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/8240fcb4-364a-1dbb-a719-cd658d0ef27d.png)

インストールができると、install to workspace と書かれていた場所に Bot User OAuth Token という項目ができて、そこに xoxb- で始まる長い文字列が表示されているはずで。これを Copy して、エディタとかにメモっておきます。

## Signing Secret を取得する

左側にある Basic Information を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/74575c26-27e0-a46f-1f9e-5132f67d02ca.png)

ずっと下のほうにいくと、App Credentials という項目があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/75c1ac90-140f-4a0e-35dd-642799f1f9a7.png)

ここの Signing Secret という項目の右にある Show を押すと、伏せ字になっている部分に文字列が表示されるので、これをコピーしてエディタにメモしておきます。

## チャンネルに invite する

この状態で、slack のアプリの左下に App のところに TestBot が追加されているはずです。追加されていなければ、インストール失敗してます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/9492ed32-dd0a-6f99-71f1-6e55e2d117fd.png)

bot を追加したいチャンネルで `/invite @TestBot` とかして、チャンネルに追加します。

これで slack 側の設定はいったん終わりです。

# Firebase Functions 側の設定

## functions のプロジェクトを作成する(または既存のプロジェクトを使う)。

functions を使うには blaze プラン(有料プラン)にする必要があります。

firebase のプロジェクトを作成した状態で、左側の functions のところを選ぶと、プランをアップグレードするように言われるので、アップグレードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/070349b5-31e4-d524-db85-85569e802e1a.png)

すでに blaze プランになっているプロジェクトを使う場合は、何もしなくても大丈夫です。

## functions のプロジェクトを作る

下記コマンド群がインストールされている前提です。Windows/Mac のどっちでも同じです。

```
$ node -v
v16.11.0
$ npm -v
yarn 8.2.0
$ yarn -v
1.22.17
$ firebase --version
9.23.0
```

node は v10 以上で動くらしいです。

```
$ firebase init functions
...
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: your-project-name (your-project-name)
i  Using project your-project-name (your-project-name)
=== Functions Setup
...
✔  Firebase initialization complete!
```

project の名前は、blaze プランになっている firebase のプロジェクトを選びます。

## bolt.js のインストール

```
$ yarn add @slack/bolt
```

これで、package.json は以下のような感じになりました。

```package.json
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "16"
  },
  "main": "index.js",
  "dependencies": {
    "@slack/bolt": "^3.8.1",
    "firebase-admin": "^9.8.0",
    "firebase-functions": "^3.14.1"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}
```

バージョンが違うと動かないとかあるかもしれないので、動かないときはバージョンを合わせてみてください。

## コードを書く (index.js)

下記ページのままです。

- [Slack アプリフレームワーク Bolt for JavaScript を Cloud Functions for Firebase で動かそう](https://qiita.com/seratch/items/3e44f58a7c8ab41f21f0)

```index.js
'use strict'
const functions = require('firebase-functions');
const config = functions.config();

const { App, ExpressReceiver } = require('@slack/bolt');
const expressReceiver = new ExpressReceiver({
    signingSecret: config.slack.signing_secret,
    endpoints: '/events',
    processBeforeResponse: true
});
const app = new App({
    receiver: expressReceiver,
    token: config.slack.bot_token,
    processBeforeResponse: true
});
app.error(console.log);

app.command('/echo-from-firebase', async ({ command, ack, say }) => {
    ack();
    say(`You said "${command.text}"`);
});

// https://{your domain}.cloudfunctions.net/slack/events
exports.slack = functions.https.onRequest(expressReceiver.app);
```

## firebase configs を設定

ここで、前もってメモしておいた OAuth Token と Signing Secret を環境変数 (firebase config) に設定します。`slack.bot_token` のほうに `xoxb-` で始まる Bot User OAuth Token を設定します (なんで名前が微妙に違うのか)。

```
$ firebase functions:config:set slack.signing_secret=1234567hogehogehogehogehogehoge
$ firebase functions:config:set slack.bot_token=xoxb-1234567890-123456789012-hogehogehogehogehogehoge
```

コード中にトークン文字列を書いてしまうと、github みたいなところに push すると、トークンが全世界に公開されてしまうので、そうならないための措置です(多分)。

コード中で `config.slack.bot_token` とか `config.slack.signing_secret` という形で参照されます。設定されていないと、deploy する段階でエラーになります。

## deploy する

```
$ firebase deploy
...
? Would you like to proceed with deletion? Selecting no will continue the rest o
f the deployments. Yes
i  functions: creating Node.js 16 function slack(location)...
✔  functions[slack(us-central1)] Successful create operation.
i  functions: deleting Node.js 14 function server(location)...
✔  functions[server(us-central1)] Successful delete operation.
Function URL (slack(us-central1)): https://location-hogehoge.cloudfunctions.net/slack
...
✔  Deploy complete!
```

ここまで間違っていなければ deploy できるはず。

deploy 中のログの中に Function URL というのが表示されるので、この URL をメモっておきます。

# slack api 側の最後の設定

最後に App Manifest の中にあった url の項目を設定します。これをやらないと動きません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/8010ebe0-5c88-41c7-a80b-9b327777e439.png)

[slack api](https://api.slack.com/) のページにいって、右上の Your app からアプリを選びます。左の App Manifest を選ぶと、最初に入力した Manifest の内容が表示されるはずです。この右側の Manifest の中にある url の部分を直接編集して、functions を deploy したときのログにあった、Function Url を入力します。

たとえば、Function URL が `https://location-hogehoge.cloudfunctions.net/slack` だったときは、url は `https://location-hogehoge.cloudfunctions.net/slack/events` と入力します。この末尾の `/events` がないと動作しません。これ重要!

この `/events` がどこから来たかというと、

```index.js
const expressReceiver = new ExpressReceiver({
    signingSecret: config.slack.signing_secret,
    endpoints: '/events',
    processBeforeResponse: true
});
```

コード中にある `endpoints: '/events'` で指定してます。ここの記述を変更すると、`/events` ではない別の名前を使うこともできます(多分)。

url を入力したら、Save Changes を押します。これで、動作するはずです!!

# 動作テスト

slack のアプリで、bot を invite したチャンネルで `/event-from-firebase HelloWorld` とか入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/21d30c8d-0d6c-3e3c-5505-ae255b9e144d.png)

インストールが成功していれば、上のような感じでアプリでコマンドが自動補完できるはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/6e6f3811-c3ea-9606-2498-a4ec542c0a8a.png)

正常に動作すれば、こうなるはず。

# メモ

slack の scope の説明が雑すぎて、トライアンドエラーしてみないと何が何だかよくわかりませんでした(今もわかってない)

- [Permission scopes](https://api.slack.com/scopes)

上記ページの Bot と書いてある scope だけが bot で使えます。channels:read は使えるけど channels:write は bot 使えなかったり、chat:write と chat:write.public と chat:write.customize があって、どれで何ができて何ができないのかという違いがよくわからなかったりします。

functions のログは下記コマンドでとれます。

```
$ firebase functions:log --lines 10
```

これでログが 10 行とれます。

## Error: An API error occurred: missing_scope とか言われる

scope (権限) の設定がまちがってます。いろいろ追加して試すしかないぽいです。App manifest を Save しただけではダメで、Install App のところから Reinstall App を押して、再インストールしないと Manifest の設定は反映されません(多分)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/96e3a7fd-e74c-1678-e853-73a17def5788.png)

再インストールは上の画面からやります。なぜか OAuth & Permmissions のところからもできます。

## dispatch_failed とかいわれる

Manifest の url の設定が間違っていたり、manifest と functions の index.js の設定の整合がとれてないときに出ることがあるようです。manifest の url を書きかえたのに再インストールしないまま functions だけ deploy した(あるいはその逆をした)場合にも出ることがあるようです。