5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SlackAdvent Calendar 2022

Day 7

Slackアプリの開発環境について深堀りしてみた:Bolt for Python

Last updated at Posted at 2022-12-06

今年はSlackの次世代プラットフォームがオープンベータになったということで @seratch が刺激的な記事をpostし続けてくれていますね。
こちらの記事は現行のイケてるSlackアプリ開発用フレームワークである、Bolt for Pythonについて深堀りしていきたいと思います。

昨年のAdvent Calendarでは実際に使ってみた系の記事をpostしていましたが、そこからSlack-SDK及びBoltにもアップデートが(頻繁に)入っていますので、自分の知識のアップデートも兼ねて、深堀りしていきたいと思います。

なぜそんなことを思ったかといいますと、先日書いたこちらの記事のように、昔はなかったけど、今はBoltが自動的にハンドリングしてくれますよみたいなアプデが入ってるからです!

単純にリリースノート読み込んでないだけだろと言われてしまうとそのとおりなのですが、反省して振り返って行きたいと思います。それでは、Slack Advent Calendar 2022 7日目、スタートしていきたいと思います。Let's go🎉

Bolt 入門ガイドを読む

ありがたいことに日本語化されています。
はじめに目につくのはソケットモードでしょうか。これは2021年の1月にGAになっていますが、開発初期はこのモードを使うことで最短で開発に挑むことができるようになります。詳細については既に @seratch さんが書いていますのでそちらを参考にしましょう。

開発環境ではCloud Flare Tunnelsを使ってローカル環境とSlack間をつなげているのですが、面倒なので開発環境の場合はソケットモードで接続するようにしようかなと思いつつありますが、それはそれで環境が違うのでだめっぽいか。

メッセージイベントをサブタイプでフィルタする

出典

# 変更されたすべてのメッセージに一致
@app.event({
    "type": "message",
    "subtype": "message_changed"
})
def log_message_change(logger, event):
    user, text = event["user"], event["text"]
    logger.info(f"The user {user} changed the message to {text}")

メッセージイベント取得時に、サブタイプ属性を付けておくことで処理の分岐に使うことができるようです、知らなかった。
これまで作ったアプリでは、 @app.event("message") で全部受け付けて、その中で分岐処理を書いていた!分けちゃうとうまく書けない部分が結構あったのでまだ使ってないけど、こうやって処理を分けておくとboltがハンドリングする量が減るという可能性は考えられる。
メッセージイベント取得しはじめると、全部のpostに反応する事になるので、処理量削減したい場合は検討してもいいかも。

アクションのリスニング時にブロックIDでフィルタできる

出典

# この関数は、block_id が 'assign_ticket' に一致し
# かつ action_id が 'select_user' に一致する場合にのみ呼び出される
@app.action({
    "block_id": "assign_ticket",
    "action_id": "select_user"
})
def update_message(ack, body, client):
    ack()

    if "container" in body and "message_ts" in body["container"]:
        client.reactions_add(
            name="white_check_mark",
            channel=body["channel"]["id"],
            timestamp=body["container"]["message_ts"],
        )

この組み合わせ使ったことなかった。普段は@app.action("approve_button")と書くのですが、block_idと組み合わせることでaction_id共通にして処理を分岐できるようだ。

Lazy リスナーについて

出典
これは既に当アドカレで @seratch さんより詳細書いていただいていますが

たまにしか発生しないアクションに対する反応を行うbotなんかはこれがいいよねとずっと思ってきたのですが、実装例が公開されたことでより使いやすくなったのではないかなと思います。

アダプター

出典
私の環境ではFlaskを使っているのですが、どうやって組み込めば…!?みたいに思っていたことがあります。こちらを見れば書き方がわかるので良いですね。
Bolt for Pythonのアダプターページに、様々な事例が掲載されていますので、自分の環境に合わせたサンプルを見つけられると思います。

リスナーミドルウェア

出典
上述したサブタイプやblock_idによるフィルタリングに似ていますが、これを関数にまとめた形で設定することができます。

# "bot_message" サブタイプのメッセージを抽出するリスナーミドルウェア
def no_bot_messages(message, next):
    subtype = message.get("subtype")
    if subtype != "bot_message":
       next()

# このリスナーは人間によって送信されたメッセージのみを受け取ります
@app.event(event="message", middleware=[no_bot_messages])
def log_message(logger, event):
    logger.info(f"(MSG) User: {event['user']}\nMessage: {event['text']}")

# リスナーマッチャー: 簡略化されたバージョンのリスナーミドルウェア
def no_bot_messages(message) -> bool:
    return message.get("subtype") != "bot_message"

@app.event(
    event="message", 
    matchers=[no_bot_messages]
    # or matchers=[lambda message: message.get("subtype") != "bot_message"]
)
def log_message(logger, event):
    logger.info(f"(MSG) User: {event['user']}\nMessage: {event['text']}")

Slackアプリでは最初はメッセージイベント全体を受け取って処理するみたいな形で書き始めてしまいますが、アプリが大きくなってきた際にはミドルウェアを書く事によってより低負荷なアプリケーションにすることができそうです。
私自身が書くアプリはメッセージイベントを多用しているので、うまくフィルタできないのかなと思っていたのですが、今後はこれらを使ってみるのも検討してみると恩恵がありそうです。

コンテキストの追加

出典
ミドルウェアでcontextにデータ追加するサンプルが掲載されています。
サンプルみたいにblocksを構成するというのはよくやるのでリスナーの実行前に処理を寄せてしまうというのもありなのかもしれない。

Bolt for Pythonのリリースノートを読むには

昨年のクリスマスイブ(2021.12.24)はver.1.11.1だったようです。現在のバージョンが1.15.5ですからバージョンアップ頻度が多くて安心できます。

ignoring_self_events_enabled=True について

デフォルトでTrueになっています。アプリの初期化時に渡す引数です。こんな形になります。

bolt_app = App(
    logger=logger,
    signing_secret=os.environ.get("SIGNING_SECRET"),
    installation_store=installation_store,
    raise_error_for_unhandled_request=True,
    client=WebClient(retry_handlers = retry_handlers),
    ignoring_self_events_enabled=True,
    oauth_settings=OAuthSettings(
        client_id=client_id,
        client_secret=client_secret,
        state_store=oauth_state_store,
        scopes=os.environ.get("SLACK_SCOPES"),
        user_scopes=os.environ.get("SLACK_USER_SCOPES"),
        callback_options=callback_options,
    ),
)

設定しなくても良いのですが、なぜこれをピックアップしたかというと、昔はこれなかったんですよね。
例えば、「Hello」というワードに「Hello」とbotが返すみたいな実装をした場合、レスポンスにもHelloが入っているのでループする事になります。
実は、少し前に現在pilotであるSalesforce Apex SDK for Slackを試用してみたのですが、そこにはまだ入っていませんでした。

こうすることで、チャンネルに投げられたメッセージをそのチャンネルにリポストしてくれるようにすることができるのですが、無限ループになるのでこの実装はやめましょう(笑

その他、初期化時に使える機能についてはここを参照しましょう

引数
logger
 このアプリで使用できるカスタムロガーです。
name
 ロギングに使用するアプリケーション名。省略した場合は、ソースファイル名が使用されます。
process_before_response
 このアプリがFunction as a Serviceで動作している場合はTrueを指定します。(デフォルト: False)
raise_error_for_unhandled_request
 処理されないリクエストに対して例外を発生させ、警告ログをピントして Slack に 404 を返す組み込みハンドラではなく @app.error リスナーを使用したい場合は True を指定します (デフォルト: False)
signing_secret
 Slackからのリクエストを検証するために使用されるSigning Secretの値。
token
 単一ワークスペースのアプリにのみ必要なボット/ユーザーアクセストークン。
token_verification_enabled
 True の場合、指定されたトークンの有効性を検証します。
client
 このアプリのシングルトン slack_sdk.WebClient インスタンスです。
authorize
インストールデータにチーム/ユーザーがあるかどうかを確認し、Slackからの着信リクエストを承認する関数です。
installation_store
 インストールデータの保存/検索操作を提供するモジュール
installation_store_bot_only
 Trueの場合、InstallationStore#find_bot()を使用する(デフォルト:False)
request_verification_enabled
 組み込みミドルウェアを無効にしたい場合はFalse (デフォルト: True)。RequestVerificationは、HTTP Modeのリクエストに含まれる署名を検証する組み込みミドルウェアです。内蔵ミドルウェアをオフにする場合は、十分に安全であることを確認してください。より安全性を高めるために、RequestVerificationの使用を強く推奨します。もし、Boltアプリの前でリクエストの署名を検証するプロキシがある場合は、作業の重複を避けるためにRequestVerificationを無効にしても全く問題ないでしょう。開発のしやすさだけを考えてオフにしないようにしましょう。
request_verification_enabled
 組み込みのミドルウェアを無効にしたい場合は False を指定します (デフォルト: True)。IgnoringSelfEventsは、Boltアプリがこのアプリのボットユーザーが生成したイベントを簡単にスキップできるようにする内蔵ミドルウェアです(無限ループを引き起こすコードエラーを回避するのに有効です)。
url_verification_enabled
 組み込みのミドルウェアを無効にしたい場合は、Falseを指定します(デフォルト:True)。UrlVerificationは、HTTP ModeリクエストでEvent APIのエンドポイントを検証するurl_verificationリクエストを処理する組み込みのミドルウェアです。
ssl_check_enabled
 bool = 組み込みミドルウェアを無効にしたい場合はFalse (Default: True)。SslCheckは、Slackからのssl_checkリクエストを処理する組み込みのミドルウェアです。
oauth_settings
 Slackアプリのインストールフロー(OAuthフロー)に関する設定です。
oauth_flow
 インスタンス化されたOAuthFlow。oauth_settingsよりも常に優先される。
verification_token
 非推奨の検証メカニズム。ssl_check リクエストにのみ使用可能です。
listener_executor
 バックグラウンドタスクを実行するためのカスタムエグゼキュータ。指定しなかった場合は、デフォルトの ThreadPoolExecutor が使われます。

Slack APIのChange logを見る

こちらはAPI周りの変更点について書かれています。Slackのアプリからは頻繁にAPIを呼び出しますので、新しい変更点が気になる場合はこのページが外せません。

どんなAPIが使えるのかについてはここを参照しましょう

個人的にはhaddleに関するAPIがはえてこないかなと期待しているのですがまだ無いですね。

まとめ

ということで、改めてドキュメントを読み直してみたり、GithubみたりAPI-Docsみてみたりと、改めて深堀りをしてみました。
あらゆるリリースを頭に入れておくというのはなかなか難しいのですが、実はSlackにはコミュニティ向けのワークスペースが存在します。

こちらからサインアップするとSlack Communityという名前のワークスペースに参加することができます。
#lang-japanese チャンネルもありますので、日本語でのコミュニケーションも可能です。上述の @seratch がリリース内容を必ずpostしてくれるので、それを追いかけるだけでも十分な情報が手に入るかなと思います。
Slackアプリ開発に躓いたときは、Twitterに流すのも良いのですが、こちらのSlackワークスペースでやり取りするほうがやりやすいのではないかなと思います。
無料で参加できますので是非ご参加ください!

以上、Slack Advent Clendar 2022のDAY7の記事となります。読んで頂きありがとうございました!

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?