2017/10/12 追記: API.AIのサービス名はDialogflowに変わりました。
API.AIのContextsとWebhookでChatOps環境を作ってみます。 https://docs.api.ai/docs/concept-contextsdocs.api.ai 今回は、SlackのChatbotとやりとりし、「管理者モード開始」から「管理者モード終了」までのやりとりの間に操作コマンドを実行できるようにします。
インテントを用意する
インテントは『管理者モード開始』、『管理者モード終了』、『stats』の3つを用意します。
『管理者モード開始』は、Output contextに「administrator」、User saysに「管理者モード開始」、アクションに「enter_administration」、パラメータとしてpasswordを@sys.anyエンティティで必須入力としておきます。また、パスワード検証をWebhook側で行うため、「Use Webhook」もチェックしておきます。
『管理者モード終了』は、Input contextに「administrator」、Output contextに「administrator」で、Output側はLife spanを0に設定します。Life spanを0にしたコンテキストを出力すると、既存のコンテキストから外れるようです。TextResponseで「管理者モードを終了します。」を返すように設定します。終了時はコンテキストを外す処理だけなので、Webhookは使いません。
『stats』は、操作コマンド用に使います。Input contextに「administrator」、Output contextに「administrator」で、OutputのLifespanは5としておきます。アクションは「stats」を設定しておきます。Webhookで処理するため、「Use Webhook」もチェックしておきます。
Webhookを用意する
今回は操作コマンドとして、botのホストされている環境のメモリ使用量を表示してみます。コマンド名はstatsとしておきます。 WebフレームワークはFlask、また追加のモジュールでpsutilを使います。 Pythonのバージョンは3.6です。
from flask import Flask, request, jsonify import psutil app = Flask(__name__) SECRET_PASSWORD = 'hoge' @app.route("/", methods=['POST']) def webhook(): req = request.get_json(silent=True, force=True) resp = jsonify(process_request(req)) return resp def process_request(request_data): if "result" not in request_data: return {} action = request_data['result']['action'] if action == 'stats': return stats() elif action == 'enter_administration': return enter_administration(request_data) return {} def enter_administration(request_data): # get context contexts = request_data['result']['contexts'] for context in contexts: if context['name'] == 'administrator': administrator_context = context break else: return {} password = administrator_context['parameters']['password'] if password != SECRET_PASSWORD: return { 'speech': "パスワードが間違っています。", 'contextOut': [{'name': 'administrator', 'lifespan': 0}]} message = "管理者モードを開始します。" return { "data": {"slack": {"text": message}}, "displayText": message, "speech": message} def stats(): mem = psutil.virtual_memory() total_gb = mem.total / (1024 ** 3) used_gb = mem.used / (1024 ** 3) message = "メモリ使用量: {:01.1f}GB / {:01.1f}GB".format(used_gb, total_gb) return { "data": {"slack": {"text": message}}, "displayText": message, "speech": message} if __name__ == "__main__": app.run()
起動はpythonコマンドにソースコードのファイルを指定します。psutilを使っているのでWindowsでも動作します。
$ python main.py
起動すると、localhost:5000でlistenされます。
前回と同様に、ngrokで外部から接続できるようにして設定します。
$ ngrok http 5000
fulfillmentのWebhookにURLを設定してSAVEしておきます。
動作確認
Slackでテスト用に追加されたapiai_botにDMで話しかけてみます。
正常動作していれば、botから応答があります。
感想
コンテキストを使って状態を変えられるのは便利ですね。またコンテキストにパラメータを保持できるのも何かに使えそうです。
今回はパスワード検証部分はハッシュ化などはしていませんが、ワンタイムトークンを発行したりすれば、実用上も問題無さそうですかね。