Slide 1

Slide 1 text

Cloud Functionsで作る Slackアプリ 2021/09/29 mikan3rd

Slide 2

Slide 2 text

Who is mikan3rd? ● Hiroki Ota ○ 基本 mikan3rd という名前で活動してます ● 1992年生まれ、エンジニア5年目 ○ JavaScript / TypeScript / Ruby / Python / Go ○ TypeScript / React が特に得意 ● 現在は株式会社ZENKIGEN所属 ○ 採用向けweb面接「harutaka」 ○ 録画面接のAI分析「harutaka エントリーファインダー」 ○ ライブ面接のAIサポート「harutaka インタビューアセスメント」 ○ エンジニア職も募集中!! ● Google CloudのProfessional Cloud Developer認定 ○ 弊社のインフラは GCPがメインです Twitter: @mikan_the_third

Slide 3

Slide 3 text

本題 Cloud Functionsで作る Slackアプリ

Slide 4

Slide 4 text

Q. こんなこと思ったことありませんか? ● HTTPリクエストで呼び出せる処理を作りたい! ● PubSubで非同期に実行させたい! ● cronみたいに定期実行させたい! ● (JavaScriptでいう)オブジェクトをそのまま DBに保存したい! ● CI経由で自動デプロイさせたい! ● 1ヶ月あたり無料枠〜数十円におさまる範囲で動かしたい!

Slide 5

Slide 5 text

それ、         で Serverlessでできます!!

Slide 6

Slide 6 text

Firebaseとは? ● mBaaS(mobile Backend as a Servie) ● GCPのサービスのうちアプリ開発によく使う機能をまとめて使いやすくしたやつ ○ データベースFirestore ○ ストレージは Cloud Storage for Firebase ○ ユーザー認証は Authentication ○ サーバーレスコンピューティングは Cloud Function ○ などなど(モバイルアプリ向けの機械学習もある) ● インフラや一部バックエンドは GCPに任せてサーバーレスで開発できる! ○ 上記の機能をフロントから使える

Slide 7

Slide 7 text

Cloud Functions for Firebaseの良いところ ● 関数単位のちょっとした複数の処理をサーバーレスで用意できる ○ フレームワーク不要、関数だけでバックエンドサーバーができる ○ サーバー(の管理が)レス ● 様々なトリガーで関数を起動できる ○ HTTPリクエスト ○ PubSub ○ Cloud Schedulerの自動PubSub連携(定期処理) ○ フロント側からSDK経由で直接呼び出し ○ Firestore / Storage にデータの変更があった場合 ○ などなど ● firebase-toolsを使用したデプロイが楽 ○ `firebase deploy` だけ! ○ Firestoreのセキュリティルールとかもまとめてデプロイ可能 ● エミュレーターを使えばローカルでも動作確認可能

Slide 8

Slide 8 text

Firestoreのいいところ ● 何も準備がいらないので楽 ○ テーブルを追加する必要なし(ドキュメント追加と共に自動で作成される) ● ちょっとしたクエリなら使える ○ RedisなどのKey-Value型のDBではクエリが書けないが、 Firestoreはドキュメント型なので簡単なク エリなら書ける ● 少量の使用なら無料枠におさまる ○ Cloud SQLなどマネージドDBは最低スペックでも 1ヶ月数千円かかる ○ Google Compute EngineにMySQLサーバーを自前で立てることもできるが面倒

Slide 9

Slide 9 text

今回作ったやつ

Slide 10

Slide 10 text

Lively ● LivelyはSlack上のコミュニケーションをより便利に楽しくするための機能を複数備えたアプリで す! ● https://github.com/mikan3rd/lively ● 様々なチャンネルから人気の投稿を探して通知します ● 週間・月間で人気のあった投稿を振り返ります ● アプリのホームタブから簡単に設定ができます ● 新しく作成された絵文字やチャンネルをお知らせします ● privateチャンネルには関与しないようになっているため安心です

Slide 11

Slide 11 text

Livelyの構成 ● TypeScript (Node.js) ● Firebase ○ Cloud Functions ○ Firestore ● Slack App ○ OAuth Permission ○ App Home ○ Interactive Components ○ Event Subscription

Slide 12

Slide 12 text

Slack App

Slide 13

Slide 13 text

Slack App とは? ● Slack AppをSlackにメッセージを投げたり、Slackのデータを取得したり、SlackのUIを使って操 作したりできる ● 無料!!

Slide 14

Slide 14 text

OAuth認証 ● アプリを作成したworkspace以外でも使え るようにするためには、OAuth認証で他の workspaceの認証トークンを取得する必要 がある ○ Slack Appを作成したworkspaceのみで使 用する場合は不要 ● 使用したいAPIに応じてscopeを付与する必 要がある ○ 今回のアプリでは privateチャンネルに対 するアクセス権はない ● 認証用URLにリダイレクトするendpointと認 証処理用のendpointを用意

Slide 15

Slide 15 text

Interactive Component & App Home ● Interactive ComponentとはSlackのメッセージにテキス トだけでなくボタンやセレクトボックスなどを表示できる 機能 ○ 各パーツはJSON形式で決まった構造で組み立てる必要が ある ○ https://app.slack.com/block-kit-builder でプレビューでき る ● App HomeをONにするとSlack上でアプリを選択した時 にHomeタブが表示できるようになる ○ ここにもInteractive Componentを表示させることができる ○ 今回の場合はここでアプリの設定ができるようにしている ● Interactive Componentで選択された結果を受け取る endpointが必要

Slide 16

Slide 16 text

Event Subscription ● 特定のイベントが発生した時にリクエストを送ってくれる webhook的なやつ ○ 先ほどのApp Homeが開かれた時には `app_home_opened` というイベントが送られてくるので最新 のデータを反映した Interactive Componentを送っている ○ `channel_created` `emoji_changed` のイベントが送られた 時にチャンネル作成の通知やスタンプ追加の通知をリアル タイムで送るようにしている(簡単) ● イベントを受け取るための endpointが必要

Slide 17

Slide 17 text

実際に触ってみて分かった注意事項

Slide 18

Slide 18 text

● 外部へのアクセスなど一部の機能には従量課金プランに切り替えが必要 ○ と言っても従量課金プランにも無料枠はあるのでそんなに気にすることはない ● コールドスタート ○ 実行環境はゼロから初期化されるため、関数の実行までその分の時間がかかる ● タイムアウト ○ 最大9分 ○ そもそも呼び出し回数、メモリ、実行時間による従量課金なので長く重い処理には向かない ● デプロイによる課金 ○ `firebase deploy` は内部的に Cloud Build を使用しているため Cloud Buildの実行時間が課金対象となる(無料枠あるので そう超えないはず) ○ 関数は無料枠のない Container Registry に保存されるため、その分のストレージ料金がかかる ○ デプロイする度に古い Containerが削除されずに溜まっていって課金対象となってしまう ● 使えるのは Node.js (JavaScript / TypeScript)だけ ○ 普通のCloud FunctionはRuby / Python / Java / PHP / Go でも使えます ○ https://firebase.google.com/docs/functions/functions-and-firebase ○ `gcloud functions deploy` Cloud Functions for Firebaseを使う際の注意

Slide 19

Slide 19 text

Firestoreを使う際の注意 ● 読み取り、書き込み、削除のドキュメントの数による従量課金 ○ 頻繁に上記の処理が発生する場合や大量のデータを扱う場合は不向き ○ ページングなどでドキュメントの合計数が欲しい場合もデータの読み取りとして課金される ○ コンソール上で上記の処理を行った場合も課金対象 ● NoSQLなのでRDBでいうリレーションを再現し辛い ○ 一応、サブコレクションや参照型など用意されているがクセがある ● NoSQLなのでカラムごとに型を制限できない ○ KeyとValueのように格納できるが型の制限はできないので想定外の値が入ってもエラーにならない ○ またマイグレーションのような概念もない ● 複数Keyを使ったクエリを書くには別途 indexを貼る必要がある

Slide 20

Slide 20 text

Slackアプリを使う際の注意 ● レート制限 ○ 同じAPIを呼べるのは1分間に20回までなどの制限がある ○ OAuth認証であればトークンごとに回数が測定される ● メッセージを指定して取得する APIが存在しない ○ そもそもメッセージにサロゲートキーがない(ちなみにメッセージへのリンク取得 APIではチャンネル IDをタイムスタンプを指定して取得するのでこれが複合キーっぽい) ○ 例えば1年前のとあるメッセージのチャンネル IDとタイムスタンプが特定できていても、 API経由で取 得するには1年前まで順番に遡るしかない ○ メッセージを順番に取得する際にも件数を指定しないといけないので、ヤマカンででかい件数を指定 して繰り返す処理をしないといけない(レート制限にかからないように) ● Event APIには3秒以内にレスポンスを返さないとリトライが走る ○ 何か処理を行う時間がないので非同期にしないといけない ○ リクエストヘッダーの `x-slack-retry-num` `x-slack-retry-reason` を見ればハンドリング可能

Slide 21

Slide 21 text

スタンプの多いメッセージを取得・通知するには? ● 一定数以上のスタンプがついたメッセージを取得したいが、そのような APIはない ○ メッセージの一覧を取得する APIで全件取得してスタンプ数でフィルタリングする処理を行っている ○ ただしメッセージの一覧はチャンネルごとにしか取得できないので、チャンネル数だけメッセージの全 件取得を行わなかればいけない ○ チャンネル数が多い場合はレート制限に引っかかってしまうため調整している ■ Cloud Task ○ 上記の処理を毎時実行する定期処理にしている ○ また、一度通知したメッセージが重複して通知されないように、通知済みのメッセージのチャンネル ID とタイムスタンプは Firestoreに保存してチェックしている ● 週間、月間で集計するには? ○ 毎時の処理で取得したスタンプの多いメッセージを保存して流用したい ○ しかし、メッセージを指定して取得できる APIがないため、上記の処理の取得期間を週間と月間に伸 ばして実行するチカラ技の処理となってしまっている・・・・

Slide 22

Slide 22 text

スタンプ数の多いスレッドも通知したい! ● メッセージ一覧取得時にスレッドのデータは付与されない ● スレッドの一覧はメッセージ指定でしか取得できない ● つまり全てのメッセージに対してスレッド一覧取得を行うしかない ○ 流石にやめた

Slide 23

Slide 23 text

伝えたいこと ● ちょっとした用途のバックエンドにはCloud Functions for Firebase / Firestoreが便利!安い! ● Slackアプリは簡単に作れる(けど制約があるので思い通りにな らないことも) ● 今回登場してないけどCloud Runも便利!

Slide 24

Slide 24 text

ご静聴 ありがとうございました! Qiitaでより詳しいコードの解説してます https://qiita.com/mikan3rd/items/b3f333314c15c866ab84