Help us understand the problem. What is going on with this article?

FirebaseアプリからSlackへ通知を行う。Slack AppのWEBへのデプロイ編。

WEBアプリを利用してくれるユーザへの通知方法として、そのユーザのSlackに通知が飛ばせたらイイねという内容をQiita記事化しています。

概要

これまでの記事も含めて、やってる内容は以下の通り。

  • FirebaseでつくったWEBアプリに「Add to Slack」ボタンをつけて、FirebaseのユーザIDに対してSlackのアクセストークンを紐付けて保存する
  • 各FirebaseのユーザIDに紐付けたSlackのアクセストークンをつかうことで、各ユーザが指定したSlackへ通知を送る
  • GCPのスケジューラ機能を使って、それらの通知を定期的に行う(今回の記事)
  • 「FirebaseのWEBアプリ」「Slackのアクセストークンを取得するOAuthの処理」「スケジューラから呼び出される通知処理」などを、FirebaseがホスティングしてくれるFirebase Hosting/Cloud Functionsへデプロイ(本番リリース)する(今回の記事)
  • つくったSlackアプリを任意のSlackのワークスペースで使用するための設定(今回の記事)

前回までで、基本的な処理シーケンスができあがりました。

image.png

上記の処理シーケンスがローカルの WEBサーバとFirebase Functionsで実装出来たので、あとは

  • 「WEBアプリ」と「Firebase Functions」の本番(Firebase Hosting)へのデプロイ
  • スケジューラから chat_pub関数を呼び出す事で、よりSlack上で動くアプリっぽくする
  • そのための諸々の環境設定(ローカルと本番だとURLがかわったりするので。。)

などを行っていこうと思います。

前提環境

これまでの記事でインストールされてるプロダクトは以下のとおり。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.3
BuildVersion:   18D42

$ node --version
v10.14.2

$ firebase --version
6.4.0

今回追加で、スケジューラを操作するための「Google Cloud Platform のCLI」を入れます。インストール はこちら。

完了すると、

$ gcloud --version
Google Cloud SDK 263.0.0
bq 2.0.47
core 2019.09.13
gsutil 4.42
$

こんな感じになるかと思います。

「WEBアプリ」と「Firebase Functions」の本番(Firebase Hosting)へのデプロイ

前回までのセットアップを行うことで、

$ firebase serve --only hosting,functions

でローカルでWEBアプリとFunctionsが起動したのでした。

さて Firebase上の Hosting へのデプロイですが、いままでの一連の記事で「Firebase上にアプリは作成済み」で進めてきているので、デプロイ自体は

$ firebase deploy --only hosting,functions

とシンプルです。しかしながら前回の記事までは、ローカルのサーバやFunctionsで疎通していたため、URLなどがローカルを指しているので、それらを書き換えます。おさらいすると、

項目
Firebaseプロジェクト名 ##PROJECT ID##
WEBアプリのアクセスURL http://client.example.com:5000/ コード上は記載がないので変更箇所ナシ
Functions上の関数(oauth) http://client.example.com:5001/##PROJECT ID##/us-central1/oauth 要変更
Functions上の関数(chat) http://client.example.com:5001/##PROJECT ID##/us-central1/chat コード上は記載がないので変更箇所ナシ
投稿先のSlackワークスペース Slack Appを開発しているワークスペース
Slack App のclient_id ##SLACK CLIENT ID##
Slack App のclient_secret ##SLACK CLIENT SECRET##
認可後の、Redirect URLs http://client.example.com:5001/##PROJECT ID##/us-central1/oauth要変更(Slack画面にて)

こんな感じだったので、該当箇所を書き換えていきます。

設定変更と再ビルド

具体的には以下の通り。

$ cd todo-examples/

src/restConfig.js を自分の設定に書き換え

$ cat src/restConfig.js
export default {
  apiUri: 'http://client.example.com:5001/##PROJECT ID##/us-central1/oauth'
  → https://us-central1-[##PROJECT ID##].cloudfunctions.net/oauth
}

$ npm run build

つづいて、Firebase Functionsのビルド。

$ cd functions/
functions/src/oauthConfig.ts  を自分の設定に書き換え

$ cat src/oauthConfig.ts 
export default {
  client_id: '##SLACK CLIENT ID##', ← さきほどひかえておいたSlackのClient IDの値を設定
  client_secret: '##SLACK CLIENT SECRET##',← さきほどひかえておいたSlackのClient Secretの値を設定
  authorization_endpoint: 'https://slack.com/oauth/authorize', ←ココはこのまま
  token_endpoint: 'https://slack.com/api/oauth.access', ←ココはこのまま
  redirect_uri: 'http://client.example.com:5001/##PROJECT ID##/us-central1/oauth', 
  → https://us-central1-[##PROJECT ID##].cloudfunctions.net/oauth
  scope: 'chat:write:user' ←ココはこのまま
}

$ npm run build
$ cd ../

環境によってprefixが異なるかもしれませんが、一度ビルドするとURLは固定になるので(後で出てきます)厳密にはそのURLなどを記載してください。

Slack App のRedirect URLsの設定追加

前回は 「Slack認可サーバからのリダイレクト先として、Slack Appの開発画面ではなく Firebase Functionsのoauth関数」をリダイレクト先にしましたが、今回はそれが本番サーバになるので、リダイレクト先の追加(もしくは書き換え)が必要ですね。

Slack Appの開発画面>>「OAuth & Permissions」にRedirect URLs を設定出来る箇所があるんでしたね。そこで https://us-central1-[##PROJECT ID##].cloudfunctions.net/oauth を追加し Save URLs をクリックして保存しましょう。

03.png

Distribution 設定を修正し、他のワークスペースからもSlackアプリを使用できるようにする

さてさて、これで本番デプロイでもよいんですが、もうひとつ。
このアプリはいま「Not distributed」というステータスです。
n01.png

このステータスは、このSlackアプリを作成したワークスペース以外からは使用できない状態ということです。ためしに他のワークスペースにログインした状態でSlackアプリのOAuth認可画面を呼び出してみると、、
n02.png

エラーとなりました。他のワークスペースでも使用可能にするためには、Slackアプリの開発画面より、Basic Information >> Manage Distribution >> Distribute App を選択
n03.png

Remove Hard Coded Information をひらいて「I’ve reviewed and removed any hard-coded information.」 をチェックし「Activate Public Distribution」を押下します。
n04.png

この欄は上記の説明にもあるとおり「OAuthのアクセストークン値」や「WebhookのURL」など、バレたら使われちゃう値をハードコード「してない」ことをちゃんと確認しました、というチェックリストの役割になっています。

Slackアプリのステータスも「Distribution activated」となりましたね。
n05.png

再度、別のワークスペースにログインしてSlackアプリの認可画面を開いてみると、、
n06.png

開けました。まだSlackさまが検証・承認はしてないけどねというステータスだそうですが、まずはココまででよいでしょう。

いよいよ本番デプロイ

$ pwd
/xxx/xxx/todo-examples
$ firebase deploy --only hosting,functions

...割愛

=== Deploying to 'fb-samples-xxxxx'...

i  deploying functions, hosting
Running command: npm --prefix "$RESOURCE_DIR" run build

> functions@ build /xxx/xxx/todo-examples/functions
> tsc

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (54.9 KB) for uploading
✔  functions: functions folder uploaded successfully
i  hosting[fb-samples-xxxxx]: beginning deploy...
...割愛


i  functions: creating Node.js 6 function chat(us-central1)...
i  functions: creating Node.js 6 function chat_pub(us-central1)...
i  functions: creating Node.js 6 function oauth(us-central1)...
Function URL (chat): https://us-central1-fb-samples-xxxxx.cloudfunctions.net/chat
Function URL (oauth): https://us-central1-fb-samples-xxxxx.cloudfunctions.net/oauth  '← URL Fixしました'

...割愛

✔  hosting[fb-samples-xxxxx]: release complete

✔  Deploy complete!

Please note that it can take up to 30 seconds for your updated functions to propagate.
Project Console: https://console.firebase.google.com/project/fb-samples-xxxxx/overview
Hosting URL: https://fb-samples-xxxxx.firebaseapp.com
$

以上で終了です。https://fb-samples-xxxxx.firebaseapp.com へアクセスしてみると、、、
01.png

表示されました!使ってみて、OAuthの処理シーケンスの確認や、FirestoreへSlackのアクセストークンが格納されていることなどを、あらためて確認しましょう!
あ、必要に応じて、前Firestoreに登録されたトークンなどは適宜削除してください:-)

参考:Firebaseのプロジェクト上へのアプリの作成

さてさて、一連の記事で「アプリは作成済み(=configを生成済)」としましたが、一応Firebaseのプロジェクト上にアプリを作成する記事のリンクをつけておきます。参考: Firebase や Google Cloud Platformの初回のサインアップその他の備忘メモ
「アプリを追加」の節で、サーバ上にアプリ作成が実施でき、あわせてHostingの領域も割り当てられます。

また、記事中のリンク先に、デプロイするためのWEBアプリをイチから作成する手順も記載してありますので、git cloneでコードを取得するのではなくイチからWEBアプリを作成する際には適宜ご参照ください。

サーバへのデプロイは以上です。

スケジューラから chat_pub関数を呼び出す事で、よりSlack上で動くアプリっぽくする

Slackのアクセストークンを取得する処理を、本番WEBサーバへデプロイするコトが出来ました。

のこりは Google のスケジューラから呼び出す件ですが、実はそのためのコードchat_pubはすでに先ほどリリース済みです。

functions/src/index.ts(スケジューラから呼び出すコード部のみ)
export const chat_pub = functions.pubsub.topic('slackChatTopic').onPublish(async message => {
    await sendSlack()
  })

async function sendSlack() {
  const querySnapshot = await admin.firestore().collection('slackToken').get()

  querySnapshot.forEach(doc => {
    const fbUserId = doc.id
    const jsonData = doc.data()

    const option = {
      url: 'https://slack.com/api/chat.postMessage',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=UTF-8',
        Authorization: `Bearer ${jsonData.access_token}`
      },
      json: {
        channel: '#general',
        text: `${fbUserId} です、今日は!`
      }
    }
    request(option, (error, response, body) => {
      if (error) {
        console.log('error:', error)
        return
      }
      if (response && body) {
        console.log('status Code:', response && response.statusCode)
        console.log(body)
      }
    })
  })
}

slackChatTopicというTopicにメッセージが投げられたら functions.pubsub.topic('slackChatTopic').onPublish に渡されるコールバック関数が呼び出されるという仕組みです。

試しに疎通してみます。

$ gcloud pubsub topics publish slackChatTopic --message '{"param1":"value1"}'
messageIds:
- '68937086780xxxx'

コールバックに渡された関数が呼び出された結果、Slackへ通知が行ったはずです。よさそうですね。

つづいてスケジュール設定ですが、かつて「Cloud Functions for Firebase の関数をスケジューラから定期的に呼び出す」へ整理したので、それを参考に、10分に一回、実行するよう設定してみます。

まずはスケジュールの確認。まだ設定してないので空のハズ。

$ gcloud scheduler jobs list
Listed 0 items.

ですね。さて、10分おきというcronを仕込んでみます。

$ gcloud scheduler jobs create pubsub testJob \
   --topic slackChatTopic \
   --message-body='test' \
   --schedule '*/10 * * * *' \
   --time-zone='Asia/Tokyo'

name: projects/fb-samples-xxxxx/locations/asia-northeast1/jobs/testJob
pubsubTarget:
  data: dGVzdA==
  topicName: projects/fb-samples-xxxxx/topics/slackChatTopic
retryConfig:
  maxBackoffDuration: 3600s
  maxDoublings: 16
  maxRetryDuration: 0s
  minBackoffDuration: 5s
schedule: '*/10 * * * *'
state: ENABLED
timeZone: Asia/Tokyo
userUpdateTime: '2019-09-21T14:34:22Z'

ちゃんと設定されているか確認してみます。

$ gcloud scheduler jobs list
ID       LOCATION         SCHEDULE (TZ)              TARGET_TYPE  STATE
testJob  asia-northeast1  */10 * * * * (Asia/Tokyo)  Pub/Sub      ENABLED
$

OKそうですね。

スケジューラが設定されたので、、、Slack側も、10分おきに通知が来てるか確認します。

02.png

おお。OKですね!!

まとめ

これまで数回の記事のまとめ。

  • FirebaseでつくったWEBアプリに「Add to Slack」ボタンをつけて、FirebaseのユーザIDに対してSlackのアクセストークンを紐付けて保存する処理を実装しました。
  • 各FirebaseのユーザIDに紐付けたSlackのアクセストークンをつかうことで、各ユーザが指定したSlackへ通知を送る処理を実装しました。
  • GCPのスケジューラ機能を使って、それらの通知を定期的に行う実装をしました。
  • 「FirebaseのWEBアプリ」「Slackのアクセストークンを取得するOAuthの処理」「スケジューラから呼び出される通知処理」などを、FirebaseがホスティングしてくれるFirebase Hosting/Cloud Functionsへデプロイ(本番リリース)する作業を実施しました。
  • つくったSlackアプリを任意のSlackのワークスペースで使用するための設定を行いました。

今回は「WEBアプリ上でのSlack通知機能」でしたが、Slack上で使用するアプリ1などを実装する場合は、Slackサイトの「Appディレクトリ」に公開して、そこからインストールする導線がスマートですよね。その「Appディレクトリへの公開」などについては、またいつか記事にしたいと思います。

おつかれさまでしたー。

関連リンク


  1. Slack上でアンケートとる「Polly」とか 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away