Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Cloud Functionsで作るSlack App

mikan3rd
September 29, 2021

Cloud Functionsで作るSlack App

mikan3rd

September 29, 2021
Tweet

More Decks by mikan3rd

Other Decks in Programming

Transcript

  1. 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
  2. Firebaseとは? • mBaaS(mobile Backend as a Servie) • GCPのサービスのうちアプリ開発によく使う機能をまとめて使いやすくしたやつ ◦

    データベースFirestore ◦ ストレージは Cloud Storage for Firebase ◦ ユーザー認証は Authentication ◦ サーバーレスコンピューティングは Cloud Function ◦ などなど(モバイルアプリ向けの機械学習もある) • インフラや一部バックエンドは GCPに任せてサーバーレスで開発できる! ◦ 上記の機能をフロントから使える
  3. Cloud Functions for Firebaseの良いところ • 関数単位のちょっとした複数の処理をサーバーレスで用意できる ◦ フレームワーク不要、関数だけでバックエンドサーバーができる ◦ サーバー(の管理が)レス

    • 様々なトリガーで関数を起動できる ◦ HTTPリクエスト ◦ PubSub ◦ Cloud Schedulerの自動PubSub連携(定期処理) ◦ フロント側からSDK経由で直接呼び出し ◦ Firestore / Storage にデータの変更があった場合 ◦ などなど • firebase-toolsを使用したデプロイが楽 ◦ `firebase deploy` だけ! ◦ Firestoreのセキュリティルールとかもまとめてデプロイ可能 • エミュレーターを使えばローカルでも動作確認可能
  4. Firestoreのいいところ • 何も準備がいらないので楽 ◦ テーブルを追加する必要なし(ドキュメント追加と共に自動で作成される) • ちょっとしたクエリなら使える ◦ RedisなどのKey-Value型のDBではクエリが書けないが、 Firestoreはドキュメント型なので簡単なク

    エリなら書ける • 少量の使用なら無料枠におさまる ◦ Cloud SQLなどマネージドDBは最低スペックでも 1ヶ月数千円かかる ◦ Google Compute EngineにMySQLサーバーを自前で立てることもできるが面倒
  5. Lively • LivelyはSlack上のコミュニケーションをより便利に楽しくするための機能を複数備えたアプリで す! • https://github.com/mikan3rd/lively • 様々なチャンネルから人気の投稿を探して通知します • 週間・月間で人気のあった投稿を振り返ります

    • アプリのホームタブから簡単に設定ができます • 新しく作成された絵文字やチャンネルをお知らせします • privateチャンネルには関与しないようになっているため安心です
  6. Livelyの構成 • TypeScript (Node.js) • Firebase ◦ Cloud Functions ◦

    Firestore • Slack App ◦ OAuth Permission ◦ App Home ◦ Interactive Components ◦ Event Subscription
  7. OAuth認証 • アプリを作成したworkspace以外でも使え るようにするためには、OAuth認証で他の workspaceの認証トークンを取得する必要 がある ◦ Slack Appを作成したworkspaceのみで使 用する場合は不要

    • 使用したいAPIに応じてscopeを付与する必 要がある ◦ 今回のアプリでは privateチャンネルに対 するアクセス権はない • 認証用URLにリダイレクトするendpointと認 証処理用のendpointを用意
  8. 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が必要
  9. Event Subscription • 特定のイベントが発生した時にリクエストを送ってくれる webhook的なやつ ◦ 先ほどのApp Homeが開かれた時には `app_home_opened` というイベントが送られてくるので最新

    のデータを反映した Interactive Componentを送っている ◦ `channel_created` `emoji_changed` のイベントが送られた 時にチャンネル作成の通知やスタンプ追加の通知をリアル タイムで送るようにしている(簡単) • イベントを受け取るための endpointが必要
  10. • 外部へのアクセスなど一部の機能には従量課金プランに切り替えが必要 ◦ と言っても従量課金プランにも無料枠はあるのでそんなに気にすることはない • コールドスタート ◦ 実行環境はゼロから初期化されるため、関数の実行までその分の時間がかかる • タイムアウト

    ◦ 最大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を使う際の注意
  11. Firestoreを使う際の注意 • 読み取り、書き込み、削除のドキュメントの数による従量課金 ◦ 頻繁に上記の処理が発生する場合や大量のデータを扱う場合は不向き ◦ ページングなどでドキュメントの合計数が欲しい場合もデータの読み取りとして課金される ◦ コンソール上で上記の処理を行った場合も課金対象 •

    NoSQLなのでRDBでいうリレーションを再現し辛い ◦ 一応、サブコレクションや参照型など用意されているがクセがある • NoSQLなのでカラムごとに型を制限できない ◦ KeyとValueのように格納できるが型の制限はできないので想定外の値が入ってもエラーにならない ◦ またマイグレーションのような概念もない • 複数Keyを使ったクエリを書くには別途 indexを貼る必要がある
  12. Slackアプリを使う際の注意 • レート制限 ◦ 同じAPIを呼べるのは1分間に20回までなどの制限がある ◦ OAuth認証であればトークンごとに回数が測定される • メッセージを指定して取得する APIが存在しない

    ◦ そもそもメッセージにサロゲートキーがない(ちなみにメッセージへのリンク取得 APIではチャンネル IDをタイムスタンプを指定して取得するのでこれが複合キーっぽい) ◦ 例えば1年前のとあるメッセージのチャンネル IDとタイムスタンプが特定できていても、 API経由で取 得するには1年前まで順番に遡るしかない ◦ メッセージを順番に取得する際にも件数を指定しないといけないので、ヤマカンででかい件数を指定 して繰り返す処理をしないといけない(レート制限にかからないように) • Event APIには3秒以内にレスポンスを返さないとリトライが走る ◦ 何か処理を行う時間がないので非同期にしないといけない ◦ リクエストヘッダーの `x-slack-retry-num` `x-slack-retry-reason` を見ればハンドリング可能
  13. スタンプの多いメッセージを取得・通知するには? • 一定数以上のスタンプがついたメッセージを取得したいが、そのような APIはない ◦ メッセージの一覧を取得する APIで全件取得してスタンプ数でフィルタリングする処理を行っている ◦ ただしメッセージの一覧はチャンネルごとにしか取得できないので、チャンネル数だけメッセージの全 件取得を行わなかればいけない

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