🐶

Fast API+Cloud run+FirestoreでSlackアプリを作る【OauthテーブルもNoSQLを使用】

2023/08/08に公開2

はじめに

この記事は、FastAPI+Cloud Run+Firestore で Slack アプリを作る際の備忘録のようなものです。
全てを話すと長くなるので、一応適当に作成したリポジトリを用意しました。

他の人の記事でも、firestore を使用しているものはありますが、Oauth テーブルも NoSQL で実装しているものをスニペット付きで記述ある人が見つからなかったので、ないならと思い。そんなにテストしてないので、参考程度にしてもらえればと思います。一応 ouath による他ワークスペースへのインストールも可能です。

なぜこの構成にしてるかというと、以下のような利点があります。

  • 開発時サーバ代は比較的安い
  • CI/CD 設定が簡単
  • データ整理がしやすい
  • サーバ管理が楽
  • 当たり前ですが、GCP サービスとの繋ぎ込みが楽

デメリットは以下の通りです。

  • NoSQL なので、データベースの設計が難しい
  • あまり GCP が人気がないので、何かに詰まったときに解説している人が少ない
    • 公式ドキュメントは他のクラウドよりもしっかりしているとは思います。

ディレクトリ構成

これはミニマムな構成です。適宜変更を加えてください。私の場合、実際にはもっと細かくフォルダを分けていますが、小さいアプリを作成する際はこのくらいで良いと思います。

./
|- app.py # 初期化処理など
|- slack.py # Slack アプリのエントリーポイント
|- config.py # 設定ファイル
|- repositorys.py # DB処理
|- Dockerfile
|- requirements.txt

イベント処理について

一応公式で、サンプルがあります。
ただし、一点気をつけなければならないのが、fastapi が基本的に非同期通信を対象としている点です。始めのapp変数の初期化方法で変わります。async を使用する場合はそれに対応した関数を使用してください。
そのため、適宜 await を使用しないと、上手いこと動きません。例えば、app_mention イベントは以下のように記述しています。

slack.py
from logging import getLogger, Logger
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
from slack_bolt.async_app import AsyncApp
from slack_sdk.web.async_client import AsyncWebClient
from slack_bolt.context.say.async_say import AsyncSay
from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings
from fastapi import APIRouter, Request
from config import settings, firestore_client
from repositorys import InstallationStoreRepository, StateStoreRepository, AppConfigRepository, BotStoreRepository

~~~色んな初期化~~~

@app.event("app_mention")
async def app_mention(body: dict, say: AsyncSay, logger: Logger, client: AsyncWebClient):
    logger.info(body)
    await say("Hi there!")

ここではわざと型アノテートしていますが、Say のところが AsyncSay 、WebClient のところは AsyncWebClient になっています。使用されているクラスが違うことに注意が必要です。非同期関数なので、await を適宜使用しないと、上手く動きません。

サンプルでは、色々と実装方法を例示されているため、適宜そちらを参考にすると良いと思います。

テーブル 実装について

python で slack アプリを作るときには、boltというライブラリがあります。
ただ、このライブラリで提供される Oauth テーブルラッパーは、RDB を対象とし SQLAlchemy を使用しているため、NoSQL で実装するには、自作するしかありません。ただ、インターフェースクラスが提供されているので、基本的にはこれと sqlalchemy に対応したコードを参考にすれば、実装できます。以下は公式のインタフェースクラスです。

公開しているリポジトリでは「repositorys.py」にて実装しています。

以下は今回のテーブル構造です。思想によって変わると思うので、参考程度にしてください。installation_storeで一つのテーブルがないのは、あまりテーブルを増やしたくなかったためです。

デプロイについて

Cloud Run でのデプロイ時は以下の環境変数を設定してください。slack のアプリの設定画面から取得できます。

  • APP_ID
  • CLIENT_ID
  • CLIENT_SECRET
  • SIGNING_SECRET

開発時は良いですが、本番環境では「CPU を常に割り当てる」にしないと、動作が不安定になります(ボットメッセージが二回発行されたり)。

デプロイが完了したら、slack のアプリの設定画面から、必要なイベントやリダイレクト設定等をおこなってください。参考程度に App Manifest を以下に示します。<app_url>は、Cloud Run の URL に置き換えてください。

display_information:
  name: <app_name>
features:
  app_home:
    home_tab_enabled: false
    messages_tab_enabled: false
    messages_tab_read_only_enabled: false
  bot_user:
    display_name: <app_name>
    always_online: false
oauth_config:
  redirect_urls:
    - <app_url>/<APP_ID>/slack/oauth_redirect
  scopes:
    bot:
      - app_mentions:read
settings:
  event_subscriptions:
    request_url: <app_url>/<APP_ID>/slack/events
    bot_events:
      - app_mention
      - app_uninstalled
  interactivity:
    is_enabled: true
    request_url: <app_url>/<APP_ID>/slack/events
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false

ここまでやれば「<app_url>/<APP_ID>/slack/install」にアクセスすることで、インストールが可能になります。

おわりに

以上です。まだ色々記述したいことがあるのですが、一旦ここまで。
何かしらの参考になれば幸いです。

参考

Discussion

Kazuhiro SeraKazuhiro Sera

Bolt を利用いただいてありがとうございます!

一点だけ、訂正が。

FastAPI は利用側のコードでは async/await でもそうでないコードでもどちらでもサポートしています。
https://fastapi.tiangolo.com/async/#in-a-hurry

そのため、Bolt の公式のコード例は両方を用意しているものであり async/await ではない方のコードが動作しないというわけではありません。

コード例でそれぞれ import しているように adapte が二種類用意されています。もし adapter の間違った方を import してしまった場合はコードが動作しないということはあると思います。

  • slack_bolt.adapter.fastapi.SlackRequestHandler
  • slack_bolt.adapter.fastapi.async_handler.AsyncSlackRequestHandler

これからもぜひ Bolt で便利な Slack アプリをたくさん作ってみてください!

stray dogstray dog

訂正ありがとうございます! 言葉足らずでした。加筆します!
あと、余談ですが最近記事参考にさせてもらっております!ありがとうございます!