API.AIのコンテキストを使ってChatOps環境を作る

API.AIのContextsとWebhookでChatOps環境を作ってみます。 docs.api.ai 今回は、SlackのChatbotとやりとりし、「管理者モード開始」から「管理者モード終了」までのやりとりの間に操作コマンドを実行できるようにします。

インテントを用意する

インテントは『管理者モード開始』、『管理者モード終了』、『stats』の3つを用意します。

『管理者モード開始』は、Output contextに「administrator」、User saysに「管理者モード開始」、アクションに「enter_administration」、パラメータとしてpasswordを@sys.anyエンティティで必須入力としておきます。また、パスワード検証をWebhook側で行うため、「Use Webhook」もチェックしておきます。

f:id:nullpobug:20170612185733p:plain

『管理者モード終了』は、Input contextに「administrator」、Output contextに「administrator」で、Output側はLife spanを0に設定します。Life spanを0にしたコンテキストを出力すると、既存のコンテキストから外れるようです。TextResponseで「管理者モードを終了します。」を返すように設定します。終了時はコンテキストを外す処理だけなので、Webhookは使いません。

f:id:nullpobug:20170612185725p:plain

『stats』は、操作コマンド用に使います。Input contextに「administrator」、Output contextに「administrator」で、OutputのLifespanは5としておきます。アクションは「stats」を設定しておきます。Webhookで処理するため、「Use Webhook」もチェックしておきます。

f:id:nullpobug:20170612185716p:plain

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から応答があります。

f:id:nullpobug:20170612185713p:plain

感想

コンテキストを使って状態を変えられるのは便利ですね。またコンテキストにパラメータを保持できるのも何かに使えそうです。

今回はパスワード検証部分はハッシュ化などはしていませんが、ワンタイムトークンを発行したりすれば、実用上も問題無さそうですかね。