Slack/Mattermost(以降、まとめてSlack)のbotといえばHubotが有名だ。
Hubotはcoffeescriptで書かれているのだが、botのためだけに新しい言語を覚えるのも面倒。
加えてHubotを動作させるにはnpmが必要。
ちょっとそこまでは…。
Pythonなら普段使いで最初から環境は作ってあるし、楽だし。
そう思っていろいろと調べてみた。
Slackのbotに必要なことは?
Slackのbotに必要なのは、端的に以下の二つ。
- Slackからのデータ送信を受ける。
- Slackへ何かをする。
1.は、HTTP POSTでやってくるので、POSTを受けることさえできればよい。
2.は、例えばSlack上で何かの発言をすることだが、こちらはcurl(Pythonならrequests)でも使えば簡単にできる。
PythonでHTTP POSTを受けるには?
Flaskである。
Flaskとはごくごく軽量なwebフレームワークである。
驚くくらい簡単にwebサーバを作ることができる。
もちろんPOSTを受けるのもすぐにできる。
以下、説明。
Flaskのインストール
pipでもなんでも、好きなものを使ってインストールせい。
Flaskのテスト
以下のようなスクリプトを書く。
適当な名前で保存する。
flask.pyとかはやめようね。
#!/usr/local/bin/python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def welcome():
html = '<html><title>welcome</title>'
html = html + '<body>welcome</body></html>'
return html
if __name__ == '__main__':
app.debug = True
app.run(host='0.0.0.0')
上記のapp.route(’/’)とは、ルーティング。
ルートディレクトリ(’/’)へのアクセスがあったら、次のdef welcome()が(デコレートによって)呼ばれる、ということである。
上記の場合、welcomeと表示するhtmlが返る。
app.debug = Trueについては後述。
app.run(host=‘0.0.0.0’)のhost=…の記述により、サーバを外部に公開することになる。
テストスクリプトの実行
$ python ./flasktest.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger pin code: 213-546-048
localhostのポート5000でサーバが立ち上がったと分かる。
ブラウザでアクセスして「welcome」と表示されればよい。
ところで、app.debut = Trueだが、スクリプトを書き換えると、自動的に再読み込みをしてくれる。
実際のログは以下。
* Detected change in '/usr/home/doe/py/flasktest.py', reloading
* Restarting with stat
* Debugger is active!
激烈に便利な機能なので、開発中は有効にしておくこと。
SlackからのPOSTを受ける。
先ほどのスクリプト冒頭のimportを以下のように変更。
from flask import Flask, request
さらに以下を追加。
@app.route('/matter', methods=['POST'])
def post():
print request.form
上記のようにしておいて、Slack側のOutgoing Webhookで、呼び出すURLにhttp://<あなたのサーバのアドレス>:5000/matter
を指定する。
Slack側からwebhookを呼び出してみると、POSTされた内容がログに表示されるはず。
なお、def post()が何も返さないのでエラーは出るが気にしない。
以下が例。
ImmutableMultiDict([('user_id', u'xxxxx'), ('channel_id', u'xxxxx'), ('timestamp', u'1414562326'), ('team_id', u'xxxx'), ('trigger_word', u'test'), ('channel_name', u'off-topic'), ('token', u'xxxtokenxxx'), ('text', u'test'), ('team_domain', u'xxxx'), ('user_name', u'xxxx')])
つまりPOSTの内容はディクショナリになっている。
Slackへ応答を返そう
Slackへ応答を返す場合には、POSTでもらった中にあるtokenが必要である。
応答文、tokenのディクショナリを作っておいて、それをjsonに変換し、返してやればよい。
Mattermostの場合、tokenのキーは’MATTERMOST_TOKEN’
先ほどのスクリプトを最終的に以下のようにする。
(わざわざjsonをimportしているが、Flaskにもそういう機能がある気がしてならない)
これでSlackから通知を受けると、「You said <トリガーワード>」という応答がSlackへ帰る。
#!/usr/local/bin/python
from flask import Flask, request
import requests
import json
app = Flask(__name__)
@app.route('/')
def welcome():
html = '<html><title>welcome</title>'
html = html + '<body>welcome</body></html>'
return html
@app.route('/matter', methods=['POST'])
def post():
postdic = request.form
incomeText = postdic['text']
token = postdic['token']
text = 'You said ' + incomeText
payload = {
'text': text,
'MATTERMOST_TOKEN': token}
json_payload = json.dumps(payload)
return json_payload
if __name__ == '__main__':
app.debug = True
app.run(host='0.0.0.0')
あとはどんどん膨らませていけばよい。
【補遺】slackのoutgoing webhooks
slackでのwebhooks設定方法を示す。
webでいうと、左下のAppsからBrowse Appsを選び、outgoins WebHooksを探す
Add Configurationで設定へ。
webhooksを投げる条件を設定する。以下の3つ。
- 指定のチャンネルで発言したら
- 特定のトリガーワードを発言したら
- 特定のチャンネルで特定のトリガーワードを発言したら
1.はChannelで、2.はTrigger Word(s)で指定する。
3の場合には1.2.の両方を指定する。
上記条件に合致したらwebhooksを投げるURLを「URL(s)」で指定。
そのときのTokenは記載の通り。
webhooksで飛んでくるデータは以下の通り。json「ではない」ことに注意。
token=qFosSxxxxxxxxERh1Jfdy5
team_id=T0009
team_domain=example
channel_id=C0000000000
channel_name=test
timestamp=1355517523.000005
user_id=U0000000000
user_name=Steve
text=googlebot: What is the air-speed velocity of an unladen swallow?
trigger_word=googlebot: