Closed8

slack apiで遊ぶ by Bolt for Python

Kumamoto-HamachiKumamoto-Hamachi

チャンネル全部取得

conversations.list method | Slack

import os
from slack_bolt import App

# ボットトークンとソケットモードハンドラーを使ってアプリを初期化
app = App(token=os.environ.get("SLACK_USER_TOKEN"))
all_channels = app.client.conversations_list().get("channels")

slack connectで外部にshareされているかどうかとかも各channelのdictが持っているis_sharedで判別出来る。

Kumamoto-HamachiKumamoto-Hamachi

チャンネルに招待

conversations.invite method | Slack

import os
from slack_bolt import App
from slack_sdk.errors import SlackApiError

# ボットトークンとソケットモードハンドラーを使ってアプリを初期化
app = App(token=os.environ.get("SLACK_USER_TOKEN"))
# Bot UserのID
bot_user = os.environ.get("BOT_USER")

if __name__ == "__main__":
    all_channels = app.client.conversations_list().get("channels")
    # print("all_channels", all_channels)  # debug
    targets = []
    for channel in all_channels:
        print("channel['name']",
                channel['name'],
                "channel['id']",
                channel['id'])  # debug
        try:
            app.client.conversations_invite(
                    channel=channel['id'],
                    users=bot_user
                    )
        except SlackApiError as e:
            if e.response.get('error') != 'already_in_channel':
                raise Exception
            print("already exists")
            continue

新規のチャンネルが出来たらそこに招待 TODO

Slack Connectで外部にチャンネルをシェアしたら招待 TODO

Kumamoto-HamachiKumamoto-Hamachi

スタンプリアクションのリッスン(イベント発火)

reaction_added event | Slack
python - slack API - reaction_added - Stack Overflow
地味にEvent Subscriptions を忘れがち

先にnekoningenという名前のスタンプを登録しておく。

# ユーザーが 猫人間 のリアクションをつけたら、Neko !!!と書き込む
@app.event("reaction_added")
def message_hello(say, event):
    # TODO
    # print("event", event)  # debug
    reaction = event["reaction"]
    if reaction == "nekoningen":
        say(f"Neko !!!")
Kumamoto-HamachiKumamoto-Hamachi

リアクションしたスレッドに返信

『スタンプリアクションのリッスン(イベント発火)』の応用

@app.event("reaction_added")
def message_hello(client, event):
    # print("event", event)  # debug
    reaction = event["reaction"]
    if reaction == "nekoningen":
        conversations_replies = client.chat_postMessage(
                channel=event['item']['channel'],
                thread_ts=event['item']['ts'],
                text="猫がいたわよ",
                )
Kumamoto-HamachiKumamoto-Hamachi

下ごしらえ諸々 TODO

アプリ登録

アイコン変更

トークンの設定

イベント設定

チャンネルにアプリを追加(手動)

Kumamoto-HamachiKumamoto-Hamachi

「slack signing secret」と「challengeパラメーター」の話

slack signing secretとは

https://api.slack.com/authentication/verifying-requests-from-slack

下記のように、timestampやrequestオブジェクトのbodyの中身を用いて作ったベースの文字列を、signing secret をkeyとしてハッシュ化した値、この値がheadの中のx-slack-signatureの値と等しいかを比較することで、slackサーバーからのリクエストであることを確認している。

// timestampはheaderの`x-slack-request-timestamp`の値
sig_basestring = 'v0:' + timestamp + ':' + request.body()

my_signature = 'v0=' + hmac.compute_hash_sha256(
slack_signing_secret,
sig_basestring
).hexdigest()
>>> 'v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'

このアタリはslack_sdksignature/__init__.py内のclass SignatureVerifier内の処理を見ると同じ様なことをやっているのが確認出来る。

challengeパラメーターとは

一般的な「チャレンジ(/レスポンス)認証」とは実際の秘密情報(パスワード)を直接やり取りしないで行う認証方式のことを指す。

具体的に言うと、サーバー側がchallegeと呼ばれる乱数を元に決めた毎回異なるデータ列を送信する。クライアントは利用者が自分の知っているパスワードとして入力した文字列とチャレンジを組み合わせ、これをハッシュ関数を通してハッシュ値に変換したものを「レスポンス」としてサーバに返信する。

サーバは手元の認証情報から正しいパスワードとチャレンジを組み合わせてハッシュ値を算出し、レスポンスと比較・照合する。両者が一致すれば確かにクライアントに入力されたパスワードはサーバ上のものと同一であると確認できる。

The events sent to your Request URL may contain sensitive information associated with the workspaces having approved your Slack app. To ensure that events are being delivered to a server under your direct control, we must verify your ownership by issuing you a challenge request.

各種Eventが起こった時に事前設定したURLに送る情報にはセンシティブなものが含まれる。なので送り先サーバーが本当にアプリ製作者の意図したものなのかを確認するためにchallenge(認証のための)リクエストを行う。

https://api.slack.com/apis/connections/events-api#the-events-api__subscribing-to-event-types__events-api-request-urls__request-url-configuration--verification

{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}

tokenは(Slackからのリクエストであることの)検証用トークン。『Basic Information』の『App Credentials』にある『Verification Token』の値と同じ値が来ているか見ることで検証出来る。

ただしこれは廃止予定なのであまり気にしなくて良い。この後説明する『Slack Signing Secret』を使うことが推奨されている。

a randomly generated string produced by Slack. The point of this little game of cat and mouse is that you're going to respond to this request with a response body containing this value.

challengeは(Slack側で)ランダムに作成された文字列。これをそのまま返すことでアプリ製作者の意図したサーバーなのね、とSlack側が認証出来る。

typeはペイロードの種類を表すためのもの。例えば今回ならurlの検証用の1種だと示すurl_verificationという文字列が入っている。

Event Subscriptionでchallengeパラメーターが返ってこないとエラーになる

Your URL didn't respond with the value of the challenge parameter.

(1)直接の原因としてはslackが送ってきたchangeの値をレスポンスで返してやってないから
(2)で、じゃあなぜ返さないのかというとsigning secret前はslackからのリクエストだと判定できないから

でこの説が正しいか見るためにはsigning secretが無くてexceptionとかをappが出してるのを確認取れればまず良い。出来れば雑な生Pythonでsigning secretの確認を取らずにchange paramを返すのだと上手くurl登録できるのかも確認してやれば良い

検証のための前準備

  • Bolt for Pythonのインスール
    Slack API を使ったアプリ開発のためのSlack社公式のフレームワーク、のPython版。
    Slack | Bolt for Python

  • Ngrokへの登録とインスール
    ローカル環境のネットワークを一時的に外部公開出来るサービス。
    ユーザー登録を下記の公式サイトから行ってから、ngrokコマンドをダウンロードサイト等からインスールしてください。
    ngrok - Online in One Line

  • Slack appの登録と
    Slack API: Applications | Slackから『Create New App』でアプリを作成する。

その他、細かいことはBolt for Pythonの公式ドキュメントを参考にしてください。

challengeパラメーターが返ってくることを確認

『Event Subscriptions』の『Request URL』ににNgrokが出してくれたURLを入力する。


このスクラップは2022/11/19にクローズされました