5
5

More than 5 years have passed since last update.

Cisco Spark の Bot を AWS Lambda + Amazon API Gateway (Python) で実装してみる

Last updated at Posted at 2018-02-06

1. Cisco Spark Bot の概要

Cisco Spark には Bot 機能が用意されている。人にメンションして話かける代わりに、Bot にメンションして話しかけると、Bot はそれをコマンドとして理解して処理を行い、結果のメッセージを返す(実際には Bot は中継をするだけで実処理は別のプログラムが行う)。

「どんな処理」をして「どんな結果」を返すかはアイデア次第。ここでは最も単純な処理として、ping というメッセージに対して pong と返答する Bot を作ってみる。実処理の部分は AWS Lambda + Amazon API gateway を使うこととする。

1-1. 処理の全体像

  1. Bot にメンションしてメッセージを投稿
  2. Bot が Target URL 宛てに次の形式に従って送信(中継):
    • 必ず HTTP POST を使う
    • ペイロードは定型のフォーマット(Envelop と呼ぶ)が使われる
    • 非定型のデータは Envelop の中の ‘data’ へ入れられる
  3. Target URL を持つのは API Gateway の役割
  4. Lambda は API Gateway の受信を「トリガー」として起動
  5. Bot からの POST に対するリアクションは Lambda にて自分でプログラミングする必要がある

CiscoSparkBot.png

注意点として、2. で Bot が Target URL に HTTP POST する際、ペイロードは メッセージそのものを含まず、メッセージ ID しか含まない。メッセージそのものを取得するには Lambda から Cisco Spark に対して HTTP GET を実施し、ID に対応するメッセージを取得する必要がある。このプログラムは自分で用意する(図中の 6)。また、結果を Cisco Spark で表示するプログラム(HTTP POST)も用意が必要(図中の 7)。

2. Cisco Spark 側の設定手順

2-1. Bot の作成

Bot を作成するには実ユーザでログインする必要がある。Bot は実ユーザに紐づいて登録されるため。

以下のサイトへアクセスして「Create a Bot」をクリックする:
https://developer.ciscospark.com/add-app.html

image.png

続く画面で以下の三項目を全て埋める:

  • Display Name:Bot の名前
  • Bot Username:識別子。Bot の名前と同じでも良いが他の人と重複してはならない
  • Icon:画像の URL を入力

image.png

正しく入力すると Bot 用の Access Token が表示されるのでメモする。失効はしない。取り扱いに注意。

2-2. Room へ Bot を追加

Cisco Spark を開いて任意の Room へ行き、人を Room へ加えるのと同じ要領で Bot を Room へ追加する(Bot の名前で検索した後、追加)。

2-3. Webhook について

Cisco Spark へメッセージを投稿すると、Bot はそれを中継し、HTTP POST で別のプログラムへ渡す。プログラムの処理方法は様々だが通常結果は Bot へ戻され(いわゆるコールバック)Cisco Spark 上に表示する。HTTP によるコールバックの仕組みを Webhook と呼ぶ。

Webhook を設定する上で特に重要なのが Target URL である。インターネット上に存在する指定の URL で Bot が HTTP POST を投げる宛先となる。

Target URL 及びプログラムは Bot を作る人が用意しなければならない。そのために、例えば・・・サーバを準備して、OS をインストールして、Web サーバを稼働させ、IPアドレスを割り当てた後、URL を割り当てる・・・等と従来の方法を使っても良いが、相当な手間がかかるので、ここでは API Gateway を使って Target URL を用意し、実プログラムを Lambda で動作させる。

Target URL を決定するには Lambda + API Gateway の設定を先に行う必要がある。Cisco Spark の Webhook の設定は 4-1. で行う。

3. AWS Lambda + Amazon API Gateway 側の設定

※ この項は Amazon API Gateway での AWS Lambda の使用 (オンデマンド HTTPS 経由) のチュートリアルを基本情報として、Cisco Spark Bot へ対応するよう必要に応じて手順をカスタマイズしています。

※ AWS マネジメントコンソール、及び、AWS CLI の設定が完了しアクセス可能であることが前提です。完了していない場合はこちらの記事の「ステップ 1: 準備」等を参考に設定を完了して下さい。

3-1. Python 用デプロイパッケージを作成する

デプロイパッケージとは以下の二つを zip ファイルとして圧縮したもの:

  • Lambda のプログラム(今回は Python3.6)
  • 上記が使用するライブラリ

作成は以下の手順で行う:
1.AWS CLI へログインする。
2.Lambda 用の Python プログラムを作成する。ファイル名は Lambda 関数名と同じにする(LF4CiscoSpark)。ここでは以下のサンプルプログラムを作成する:

$ vi LF4CiscoSpark.py
---
from __future__ import print_function

import boto3
import json
import requests
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

access_code = ''        # BotのAccess Codeを入力
botDisplayName = ''     # Bot名を入力

def sendSparkGET(event):
    url = 'https://api.ciscospark.com/v1/messages/{0}'.format(event.get('data')['id'])
    headers = {
        'Authorization' : 'Bearer ' + access_code,
        'Content-Type' : 'application/json'
    }
    r1 = requests.get(url, headers = headers)
    return json.loads(r1.text)

def sendSparkPOST(event, message_detail):
    url = 'https://api.ciscospark.com/v1/messages/'
    headers = {
        'Authorization' : 'Bearer ' + access_code,
        'Content-Type' : 'application/json'
    }
    payload = {
      "roomId" : event.get('data')['roomId'],
      "text" : 'pong'
    }
    r1 = requests.post(url, headers = headers, data = json.dumps(payload))
    return True

def handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))

    message_detail = sendSparkGET(event)
    bot_command = message_detail['text']

    bot_commands = {
        botDisplayName + 'ping' : lambda x, y : sendSparkPOST(x, y)
    }

    if bot_command in bot_commands:
        return bot_commands[bot_command](event, message_detail)
    else:
        raise ValueError('Unrecognized operation')
---

3.上記プログラム中で requests を import しているが、Lambda の実行環境には標準で含まれていない。標準外ライブラリを使いたい場合、デプロイパッケージへ該当ライブラリ( requests)のファイルを含める必要がある。対応ファイルは通常 site-packages の中にあるはずなので、まず site-packages/requests ディレクトリを zip する:

$ zip LF4CiscoSpark.zip -r /(path-to-site-packages)/site-packages/requests

4.次に、今作成した LF4CiscoSpark.py を zip ファイルへ追加する:

$ zip -g LF4CiscoSpark.zip LF4CiscoSpark.py

プログラムについて

Bot にメンションして "ping" とメッセージを送ると、Bot が "pong" と返信してくれる。 "ping" 以外のコマンドを使いたい場合は bot_commands に処理を記述すればよい。

  • sendSparkGET() … 「1-1. 処理の全体像」の図中 6 に対応
  • sendSparkPOST() … 「1-1. 処理の全体像」の図中 7 に対応

3-2. 実行ロール (IAM role) を作成する

IAM はユーザーがどの AWS リソースへアクセスできるか、そのリソースをどのような方法で使用できるかを制御する仕組み。一般的な設定手順としては、1)ロールの作成 2)ポリシーの作成 3)ロールへポリシーをアタッチ、の3ステップを行う。今回ポリシーは標準ポリシーを使うので2)は行わなくてよい。手順が完了すると ロール ARN と呼ばれる文字列が表示される。これは次の手順で Lambda 関数を作成するのに必要。

  1. IAM コンソール (https://console.aws.amazon.com/iam/) にサインインする。
  2. 「ロール」をクリック。
  3. 「新しいロールの作成」をクリック。
  4. 「AWS サービスロール」が選択されていることを確認。
  5. 「AWS Lambda」の横の「選択」をクリック。
  6. 「フィルター」と記載されたフィールドに lambda と入力
  7. 表示された標準ポリシーから適切なポリシーにチェックを入れる。ここでは AWSLambdaBasicExecutionRole を選択する。注)セキュリティ上適切なポリシーを選択することは重要。こちらに AWS 標準ポリシーの一覧があるので "AWSLambda" でポリシー名を検索して AWSLambdaBasicExecutionRole より適切なものがあればそちらを選択する。"Action" に注目して比較するとよい。
  8. 「ロール名」は lambda-gateway-execution-role とする。
  9. 「ロールの作成」をクリック。
  10. 今作成した lambda-gateway-execution-role (チェックを入れるのではなくロール名)をクリック
  11. ロール ARN をメモする(arn:で始まる文字列)。これは次のステップで Lambda 関数を作るのに必要

3-3. Lambda 関数を作成する

1.AWS CLI へログインする。
2.以下のコマンドで lambda 関数を作成する。arn: には前の手順で取得したものを、region と profile には適切なものを指定する。zip-file 名と handler 名は function-name と同じにする必要がある模様。

$ aws lambda create-function --region us-west-2 \
--function-name LF4CiscoSpark \
--zip-file fileb://LF4CiscoSpark.zip \
--role arn:xxxxxxxxxxxxxxxxxx \
--handler LF4CiscoSpark.handler \
--runtime python3.6 \
--profile adminuser

※ なお、コードを update するときは以下:

$ aws lambda update-function-code \
--function-name LF4CiscoSpark \
--zip-file fileb://LF4CiscoSpark.zip

3-4. API Gateway を作成する

今回 API Gateway の作成には AWS CLI を使う。少々ステップが多いが、CLI での作業結果を逐一 AWS コンソールで確認しながら作業を行うとわかりやすい。

3-4-1. API の作成

1.AWS CLI へログインする。
2.以下のコマンドで API4CiscoSpark という名の API を作成する。作成に成功すると API を識別するための API ID が生成される。
※ API が生成されるとルートリソースも同時に生成される。次の手順でルートリソースの子リソースを作成するが、そのためのはルートリソースの ID も必要。

$ aws apigateway create-rest-api \
--name API4CiscoSpark \
--region us-west-2 \
--profile adminuser
(以下実行結果)
{
    "name": "API4CiscoSpark",
    "createdDate": 1501839827,
    "id": ""                           # API ID
}

AWS コンソールの表示結果(API Gateway):

API_Gateway_step1.png

3.API のルートリソースの ID を調べる:

$ aws apigateway get-resources \
--rest-api-id (API ID)
(以下実行結果)
{
    "items": [
        {
            "path": "/",
            "id": ""                   # ROOT RESOURCE ID
        }
    ]
}

3-4-2. API 子リソースの作成

1.以下のコマンドで API4CiscoSpark API の子リソース Resource4CiscoSpark を作成する:

$ aws apigateway create-resource \
--rest-api-id (API ID) \
--parent-id (ROOT RESOURCE ID) \
--path-part Resource4CiscoSpark
(以下実行結果)
{
    "pathPart": "Resource4CiscoSpark",
    "parentId": "",
    "path": "/Resource4CiscoSpark",
    "id": ""                            # RESOURCE ID
}

AWS コンソールの表示結果(API Gateway):

API_Gateway_step2.png

3-4-3. メソッドの作成

1.以下のコマンドで Resource4CiscoSpark リソースに POST メソッドを追加する:

$ aws apigateway put-method \
--rest-api-id (API ID) \
--resource-id (RESOURCE ID) \
--http-method POST \
--authorization-type NONE

AWS コンソールの表示結果:

API_Gateway_step3.png

3-4-4. Lambda 関数を POST メソッドの送信先に設定

1.以下のコマンドで Lambda 関数を POST メソッドの送信先に設定する。(アカウント番号) には AWS のアカウント番号12桁の数字を入れる:

$ aws apigateway put-integration \
--rest-api-id (API ID) \
--resource-id (RESOURCE ID) \
--http-method POST \
--type AWS \
--integration-http-method POST \
--uri arn:aws:apigateway:(region):lambda:path/2015-03-31/functions/arn:aws:lambda:(region):(アカウント番号):function:LF4CiscoSpark/invocations

AWS コンソールの表示結果(API Gateway):

API_Gateway_step4.png

3-4-5. POST メソッドのレスポンスを JSON に設定

1.以下のコマンドで POST メソッドのレスポンスを JSON に設定する。この設定により API メソッドが返すレスポンスのタイプが指定される。

$ aws apigateway put-method-response \
--rest-api-id (API ID) \
--resource-id (RESOURCE ID) \
--http-method POST \
--status-code 200 \
--response-models "{\"application/json\": \"Empty\"}"

3-4-6. POST メソッドの統合レスポンスを JSON に設定

1.以下のコマンドで POST メソッドの統合レスポンスを JSON に設定する。この設定により
Lambda 関数が返すレスポンスのタイプを指定している。

$ aws apigateway put-integration-response \
--rest-api-id (API ID) \
--resource-id (RESOURCE ID) \
--http-method POST \
--status-code 200 \
--response-templates "{\"application/json\": \"\"}"

AWS コンソールの表示結果(API Gateway):

API_Gateway_step5.png

3-4-7. API をデプロイする

1.次のコマンドで、作成した API を prod (プロダクション=本番環境)というステージにデプロイする:

$ aws apigateway create-deployment \
--rest-api-id (API ID) \
--stage-name prod

AWS コンソールの表示結果(API Gateway):

API_Gateway_step6.png

「URL の呼び出し」の右側に記載されている URL が Cisco Spark の Webhook 用 Target URL となる。

3-4-8. Lambda のトリガーとして API Gateway を指定する

$ aws lambda add-permission \
--function-name LF4CiscoSpark \
--statement-id apigateway \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-2:(アカウント番号):(API ID)/prod/POST/Resource4CiscoSpark"

AWS コンソールの表示結果(AWS Lambda):

API_Gateway_step7.png

4. 設定の完了と動作確認

4-1. Cisco Spark で Webhook を設定

Webhook を設定するには、HTTP PUT を以下の条件で実行すればよい:

  • URL:
  • ヘッダ
    • 'Authorization' : 'Bearer ' + Bot のアクセスコード
    • 'Content-Type' : 'application/json' を指定
  • ペイロード
    • 'name' : webhook の名前を任意でつける
    • 'targetUrl' : Amazon API Gateway で設定した Target URL
    • 'resource' : 'message' を指定する
    • 'event' : 今回は webhook を作るので 'created' を指定する
    • 'filter' : 'roomId=' に続けて Bot の所属する roomId を指定する

roomId はこちらから調査可能。

API_Gateway_step11.png

import requests

access_code = ''                       # ここに Access Code を入力

url = 'https://api.ciscospark.com/v1/webhooks'
headers = {
    'Authorization' : 'Bearer ' + access_code,
    'Content-Type' : 'application/json'
}

r = requests.put(url, headers = headers)

{
  'name' : 'My Awesome Webhook',
  'targetUrl' : 'https://example.com/mywebhook',
  'resource' : 'messages',
  'event' : 'created',
  'filter' : 'roomId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
}

4-2. 動作確認

Cisco Spark にて Bot にメンションして "ping" と入力すると、Bot が "pong" と返してくれる。

API_Gateway_step10.png

(補足)API Gateway のログを生成するには

1.AWS コンソールで API Gateway サービスを開く。
2.「API名(API4CiscoSpark)」>「ステージ」>「prod」とクリックして「設定」タブを表示する。
3.「CloudWatch ログを有効化」にチェックを入れる。「ログレベル」を設定する。
4.「変更を保存」

API_Gateway_step8.png

5.CloudWatch にてログを確認可能

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5