Summary
ユーザーの投稿やコマンドに反応するSlack botを作成した。
Socket Modeを使うと、外部のpublic HTTP endpointを契約することなく、slack_boltモジュールを用いて記述したPythonのAppでの通信を実現できる。
webには古い情報が多く、古いやり方では最新のslack APIでは動作しなくなっている。日本語の情報も結構あって使いやすそうに見えたslackbotモジュールは2019年12月のSlack APIの仕様変更でちゃんと動作しない。Slack APIのTutorialsのリンクにslackclientというのが紹介されていたが、実際にはそちらも開発を終了していて、現在はslack_sdkというのが後継のようだが、低レベルAPIっぽい。slack_boltというのがslackbotに近い感じ。
実際にはslack_boltはslack_sdkを呼び出すラッパーとして実装されているようだ。
ということで、bolt for pythonを用いた実装にチャレンジ!
2019年12月にSlackの通信仕様が変更され、現在ではSlackからHTTPSPOSTメッセージを受け取れるPublic URLを持ったサーバがBot appとの間を取り持つ必要があるようだ。このモードではサーバを契約する必要があるのと、HTTPサーバが公開されていないといけないのがやりにくい。 そのような懸念がある場合に、boltでも使えるSocket Modeというのがあるらしい。
まとめると、botの実体のクライアントAppがslackから通知を受け取るためには、Slack Events APIを使う接続方法と、Socket Modeを使う接続方法の2つが用意されている。本検討では、後者のSocket Modeでの実装を試みた。
slack apiのYour Appsページでslack appの新規作成を行い、必要な設定をしてtokenなどの情報を入手する必要がある。
手順は以下のとおり
https://api.slack.com/appsで「Create New App」をクリック。
From an app manifestを選択して次に進む。
Workspaceを選択
新規作成でManifestをコピー&ペーストするだけで既存アプリの基本設定を引き継ぐことができる。
_metadata:
major_version: 1
minor_version: 1
display_information:
name: HelloBot2000
description: An example app
background_color: "#da3a79"
long_description: An example app that can display actionable notifications in
Slack. App contains all the necessary scopes to find a channel for
publishing, and to publish a message. Enables Interactivity features to
handle use of the interactive elements of the notification. Used with
tutorials on api.slack.com/tutorials.
features:
app_home:
home_tab_enabled: false
messages_tab_enabled: true
messages_tab_read_only_enabled: true
bot_user:
display_name: HelloBot2000
always_online: true
shortcuts:
- name: HelloBot2000-shortcut
type: global
callback_id: HelloBot2000-shortcut
description: HelloBot2000のグローバルショートカット
oauth_config:
redirect_urls:
- https://example.com/slack/auth
scopes:
bot:
- app_mentions:read
- channels:history
- channels:read
- chat:write
- chat:write.customize
- commands
- files:write
- groups:history
- im:history
- im:read
- im:write
- reactions:read
- reactions:write
- users:read
- users:write
- files:read
settings:
event_subscriptions:
bot_events:
- app_mention
- message.channels
- message.groups
- message.im
- reaction_added
interactivity:
is_enabled: true
request_url: https://example.com/slack/message_action
org_deploy_enabled: false
socket_mode_enabled: true
動作中のAppの設定をExportしたManifesto.ymlをまるごとコピペしても、なんだか怒られる。
赤丸に×がついているエラー部分をみながら適当に解消して、あとは設定ページから変更する。
Connect using Socket ModeのトグルスイッチをONにする。
Interactivity & ShortcutsもONになる
Shortcutsイベントも受け取るならON(Manifestで最初に指定しておけば自動で設定される)
Botにイベントに応じた動作をさせるならイベントの受信を有効にする必要がある
イベントのトグルスイッチをONにしたところ
App IDやSecretはBasic Informationで確認できる
Bot User OAuth TokenはInstall Appまたは
OAuth & Permissionsで確認可能
Manifestで指定したScopeを編集できる
Shortcutsの設定確認
Restrict API Token UsageにAllowed IP Address Rangesを設定すると、Socket Modeで通信を行うAppの送信元IPアドレスを制限できる。
BotをWorkspaceにinstallする
専用のディレクトリを掘って、そこにpyenvで環境を作りlocalに設定する。
$ mkdir test_bolt
$ cd test_bolt
$ pyenv versions
$ pyenv virtualenv 3.9.4 3.9.4_test_bolt
$ pyenv local 3.9.4_test_bolt
依存関係によりslack_boltをpip installすると、slack_sdkもinstallされるようだ。
$ python -m pip install --upgrade pip
$ pip install slack_bolt
Signing Secretと Signing Secretは、Your Appからbotを選択し、Basic Informationのページに、App IDやClient IDなどの情報と一緒に表示されている。Show
ボタンを押して表示する。 Bot User OAuth TokenはYour Appの左に表示されているSettingsのメニューからInstall AppのリンクをクリックするとInstall App Settingsで表示できる。
たとえば、以下のようなファイルにtokenを設定しておく。 slack_tokens.sh
#!/bin/zsh
export SLACK_SIGNING_SECRET='<your-signing-secret>'
export SLACK_BOT_TOKEN='xoxb-<your-bot-token>'
export SLACK_APP_TOKEN='xapp-<your-app-token>'
以下を実行すると当該shellでtokenがexportされている。
chmod a+x ./slack_tokens.sh
source ./slack_tokens.sh
export -p
Slack Workspaceのchannelに常駐して、イベントに反応するbotのサンプルを作成した。 @app.event()や@app.message()でイベントをフックし、ハンドラを起動する。
@app.message()と@app.event(“message”)は、どちらか先に該当した方が実行され、それ以降のハンドラは無視されます。なので、@app.event(“message”)より後ろに@app.message()を書いても実行されないようだ。
@app.event(“app_mention”)は、@app.message()や@app.event(“message”)があっても、それとは独立のイベントとして処理される。
実行例はこちら
上記の処理をしたプログラムはこちら
(bolt_simpleSocketModeApp.py)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =======================================================
# slack_boltでpythonからslackにメッセージを返信
#
# bolt_simpleSocketModeApp.py
# coded by Noboru Harada (noboru@ieee.org)
#
# Changes:
# 2021/07/11: First version
# =======================================================
import logging
import os
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
=logging.DEBUG)
logging.basicConfig(level
= App(token=os.environ["SLACK_BOT_TOKEN"])
app
# respond mentions to this bot
@app.event("app_mention")
def handle_mention_events(body, say, logger):
print("### BODY ### -----------------------------------")
print(body)
print("### SAY ### -----------------------------------")
print(say)
print("### LOGGER ### -----------------------------------")
print(logger)
print("### Send say ### -----------------------------------")
f"In 'app_mention'\nこんにちは <@{body['event']['user']}> さん!")
say(print("### end handler ### -----------------------------------")
@app.message("こんにちは")
def handle_messge(message, say):
print("### MESSAGE ### -----------------------------------")
print(message)
print("### SAY ### -----------------------------------")
print(say)
print("### Send say ### -----------------------------------")
f"In 'こんにちは' こんにちは <@{message['user']}> さん!")
say(print("### end handler ### -----------------------------------")
# deafault handler for 'message' event
@app.event("message")
def handle_messge_events(body, say, logger):
print("### BODY ### -----------------------------------")
print(body)
print("### SAY ### -----------------------------------")
print(say)
print("### LOGGER ### -----------------------------------")
print(logger)
print("### Send say ### -----------------------------------")
f"In 'message'\nこんにちは <@{body['event']['user']}> さん!")
say(print("### end handler ### -----------------------------------")
if __name__ == "__main__":
= SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
handler handler.start()
定時にslack: /remind me say @hellobot2000 capture every weekday at 22:05 (https://slack.com/intl/en-in/blog/productivity/never-forget-the-little-things-with-remind)
古い情報が平気でapi.slack.comからリンクされているので注意!!