Try implementing a Cisco Spark bot with AWS Lambda + Amazon API Gateway (Python)

1. Overview of Cisco Spark Bot

Cisco Spark has a bot feature. Instead of mentioning and talking to a person, when you mention and talk to a bot, the bot understands it as a command, processes it, and returns the resulting message (actually, the bot just relays and the actual processing is done. Another program does).

It depends on the idea what kind of processing is done and what kind of result is returned. The simplest process here is to create a bot that responds pong to the message ping. AWS Lambda + Amazon API gateway will be used for the actual processing part.

1-1. Overview of processing

  1. Mention the bot and post a message
  2. The bot sends (relays) to the Target URL in the following format:
  1. It is the role of API Gateway to have the Target URL
  2. Lambda launches API Gateway reception as a "trigger"
  3. Reactions to POST from bots need to be programmed in Lambda yourself
CiscoSparkBot.png

Note that when the bot HTTP POSTs to the Target URL in 2., the payload ** does not contain the message itself, only the message ID **. To get the message itself, you need to do an HTTP GET from Lambda to Cisco Spark to get the message corresponding to the ID. Prepare this program yourself (6 in the figure). You also need to have a program (HTTP POST) that displays the results in Cisco Spark (7 in the figure).

2. Configuration procedure on the Cisco Spark side

2-1. Creating a bot

You need to log in as a real user to create a bot. Because Bot is registered in association with the actual user.

Go to the following site and click "Create a Bot": https://developer.ciscospark.com/add-app.html

image.png

Fill in all the following three items on the following screen:

image.png

If you enter it correctly, the Access Token for the bot will be displayed, so make a note of it. It will not expire. Handle with care.

2-2. Add Bot to Room

Open Cisco Spark, go to any room, and add the bot to the room in the same way you would add a person to a room (search by bot name, then add).

2-3. About Webhook

When you post a message to Cisco Spark, the bot relays it and passes it to another program via HTTP POST. There are various ways to process the program, but usually the result is returned to the bot (so-called callback) and displayed on Cisco Spark. The mechanism of callback by HTTP is called Webhook.

The Target URL is especially important for setting up webhooks. The bot is the destination to throw an HTTP POST at the specified URL that exists on the Internet.

The Target URL and program must be prepared by the creator of the bot. For that purpose, for example, you can use the conventional method such as preparing a server, installing the OS, running the Web server, assigning an IP address, and then assigning a URL. Since it takes a lot of time and effort, here we prepare the Target URL using API Gateway and run the actual program on Lambda.

To determine the Target URL, you need to configure Lambda + API Gateway first. Set up the Cisco Spark webhook in 4-1.1.

3. AWS Lambda + Amazon API Gateway side settings

3-1. Create a deployment package for Python

A deployment package is a compressed version of the following two files:

Follow the steps below to create:

  1. Log in to the AWS CLI.
  2. Create a Python program for Lambda. Make the file name the same as the Lambda function name (LF4CiscoSpark). Here, create the following sample program:
$ 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 = ''        #Enter the bot's Access Code
botDisplayName = ''     #Enter the bot name

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')
---
  1. Requests are imported in the above program, but they are not included in the Lambda execution environment as standard. If you want to use a non-standard library, you need to include the file of the corresponding library (requests) in the deployment package. The corresponding files should normally be in site-packages, so first zip the site-packages / requests directory:
$ zip LF4CiscoSpark.zip -r /(path-to-site-packages)/site-packages/requests
  1. Next, add the LF4CiscoSpark.py you just created to the zip file:
$ zip -g LF4CiscoSpark.zip LF4CiscoSpark.py

About the program

Mention the bot and send a "ping" message, and the bot will reply "pong". If you want to use a command other than "ping", you can describe the process in bot_commands.

3-2. Create an IAM role

IAM is a mechanism that controls which AWS resources users can access and how they can be used. As a general setting procedure, 3 steps of 1) creating a role, 2) creating a policy, and 3) attaching a policy to a role are performed. Since the policy uses the standard policy this time, it is not necessary to perform 2). When the procedure is complete, you will see a string called Role ARN. This is needed to create a Lambda function in the next step.

  1. Sign in to the IAM console (https://console.aws.amazon.com/iam/).
  2. Click "Roll".
  3. Click Create New Role.
  4. Make sure the AWS Service Role is selected.
  5. Click Select next to AWS Lambda.
  6. Enter lambda in the field labeled "Filter"
  7. Check the appropriate policy from the displayed standard policies. Here, select AWSLambdaBasicExecutionRole. Note) It is important to select an appropriate policy for security. There is a list of AWS standard policies at here, so search for the policy name with "AWS Lambda" and select the one that is more appropriate than the AWS LambdaBasicExecutionRole. You should pay attention to "Action" and compare.
  8. The "role name" is lambda-gateway-execution-role.
  9. Click Create Role.
  10. Click the lambda-gateway-execution-role you just created (the role name instead of checking it)
  11. Make a note of the role ARN (string starting with arn :). This is needed to create a Lambda function in the next step

3-3. Create a Lambda function

  1. Log in to the AWS CLI.
  2. Create a lambda function with the following command. For arn :, specify the one obtained in the previous step, and for region and profile, specify the appropriate ones. It seems that the zip-file name and handler name must be the same as 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
$ aws lambda update-function-code \
--function-name LF4CiscoSpark \
--zip-file fileb://LF4CiscoSpark.zip

3-4. Create API Gateway

This time, we will use the AWS CLI to create the API Gateway. There are a few steps, but it is easier to understand if you work while checking the work results in the CLI one by one on the AWS console.

3-4-1. Creating API

  1. Log in to the AWS CLI.
  2. Create an API named API4CiscoSpark with the following command. If the creation is successful, an API ID will be generated to identify the API.
$ aws apigateway create-rest-api \
--name API4CiscoSpark \
--region us-west-2 \
--profile adminuser
(Execution result below)
{
    "name": "API4CiscoSpark",
    "createdDate": 1501839827,
    "id": ""				           # API ID
}

AWS console display result (API Gateway):

API_Gateway_step1.png
  1. Find out the ID of the root resource of the API:
$ aws apigateway get-resources \
--rest-api-id (API ID)
(Execution result below)
{
    "items": [
        {
            "path": "/",
            "id": ""			       # ROOT RESOURCE ID
        }
    ]
}

3-4-2. Creating API child resources

  1. Create a child resource Resource4CiscoSpark of the API4CiscoSpark API with the following command:
$ aws apigateway create-resource \
--rest-api-id (API ID) \
--parent-id (ROOT RESOURCE ID) \
--path-part Resource4CiscoSpark
(Execution result below)
{
    "pathPart": "Resource4CiscoSpark",
    "parentId": "",
    "path": "/Resource4CiscoSpark",
    "id": ""			                # RESOURCE ID
}

AWS console display result (API Gateway):

API_Gateway_step2.png

3-4-3. Creating a method

  1. Add a POST method to the Resource4CiscoSpark resource with the following command:
$ aws apigateway put-method \
--rest-api-id (API ID) \
--resource-id (RESOURCE ID) \
--http-method POST \
--authorization-type NONE

AWS console display result:

API_Gateway_step3.png

3-4-4. Set the Lambda function as the destination of the POST method

  1. Set the Lambda function as the destination of the POST method with the following command. For (account number), enter the 12-digit number of your AWS account number:
$ 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):(Account number):function:LF4CiscoSpark/invocations

AWS console display result (API Gateway):

API_Gateway_step4.png

3-4-5. Set the response of the POST method to JSON

  1. Set the response of the POST method to JSON with the following command. This setting specifies the type of response that the API method returns.
$ 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. Set the integrated response of the POST method to JSON

  1. Set the integrated response of the POST method to JSON with the following command. With this setting Specifies the type of response returned by the Lambda function.
$ 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 console display result (API Gateway):

API_Gateway_step5.png

3-4-7. Deploy the API

  1. Deploy the created API to the stage called prod (production = production environment) with the following command:
$ aws apigateway create-deployment \
--rest-api-id (API ID) \
--stage-name prod

AWS console display result (API Gateway):

API_Gateway_step6.png

The URL on the right side of Calling URL is the Target URL for Cisco Spark webhooks.

3-4-8. Specify API Gateway as a trigger for Lambda

$ 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:(Account number):(API ID)/prod/POST/Resource4CiscoSpark"

AWS console display result (* AWS Lambda *):

API_Gateway_step7.png

4. Completion of setting and operation check

4-1. Setting up a webhook on Cisco Spark

To set up a webhook, run HTTP PUT under the following conditions:

You can find roomId from here.

API_Gateway_step11.png
import requests

access_code = ''                       #Enter the Access Code here

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. Operation check

Mention the bot in Cisco Spark and type "ping" and the bot will return "pong".

API_Gateway_step10.png

(Supplement) To generate API Gateway log

  1. Open the API Gateway service in the AWS console.
  2. Click API Name (API4CiscoSpark)> Stage> prod to display the Settings tab.
  3. Check "Enable CloudWatch Logs". Set the "log level".
  4. "Save changes"
API_Gateway_step8.png
  1. You can check the log on CloudWatch

Recommended Posts

Try implementing a Cisco Spark bot with AWS Lambda + Amazon API Gateway (Python)
LINE BOT with Python + AWS Lambda + API Gateway
[AWS] Try tracing API Gateway + Lambda with X-Ray
Amazon API Gateway and AWS Lambda Python version
[AWS] Create API with API Gateway + Lambda
Quickly take a query string with API Gateway-> Lambda (Python)
AWS Amplify + API Gateway + Lambda + Python returns a normal response
Create API with Python, lambda, API Gateway quickly using AWS SAM
[Python] I wrote a REST API using AWS API Gateway and Lambda.
LINE BOT (Messaging API) development with API Gateway and Lambda (Python) [Part 2]
I tried to make "Sakurai-san" a LINE BOT with API Gateway + Lambda
[AWS SAM] Create API with DynamoDB + Lambda + API Gateway
Create a Layer for AWS Lambda Python with Docker
AWS CDK-Lambda + API Gateway (Python)
[AWS] Try adding Python library to Layer with SAM + Lambda (Python)
Made "Unofficial Apple Refurbished Product Introduction" BOT with LINE Messaging API (v2) + API Gateway + lambda (python)
I wrote a script to create a Twitter Bot development environment quickly with AWS Lambda + Python 2.7
Notify HipChat with AWS Lambda (Python)
Send images taken with ESP32-WROOM-32 to AWS (API Gateway → Lambda → S3)
I made a Twitter Bot with Go x Qiita API x Lambda
[LINE Messaging API] Create a BOT that connects with someone with Python
I tried ChatOps with Slack x API Gateway x Lambda (Python) x RDS
How to create a serverless machine learning API with AWS Lambda
[AWS] Using ini files with Lambda [Python]
Building a Python3 environment with Amazon Linux2
Python calling Google Cloud Vision API from LINE BOT via AWS Lambda
[AWS] Create a Python Lambda environment with CodeStar and do Hello World
Retrieving food data with Amazon API (Python)
Try implementing XOR with Keras Functional API
Try HTML scraping with a Python library
Connect to s3 with AWS Lambda Python
Try drawing a map with python + cartopy 0.18.0
Let's make a Twitter Bot with Python!
Easy REST API with API Gateway / Lambda / DynamoDB
Try assigning or switching with Python: lambda
I wrote a Slack bot that notifies delay information with AWS Lambda
Python + Selenium + Headless Chromium with aws lambda
Create a bot with AWS Lambda that automatically starts / stops Instances with specific tags
Periodically run a python program on AWS Lambda
Make a Twitter trend bot with heroku + Python
Try Tensorflow with a GPU instance on AWS
View images on S3 with API Gateway + Lambda
Try to draw a life curve with python
Try to make a "cryptanalysis" cipher with Python
Serverless application with AWS SAM! (APIGATEWAY + Lambda (Python))
Steps to create a Twitter bot with python
Try to make a dihedral group with Python
Make a LINE WORKS bot with Amazon Lex
I made a Mattermost bot with Python (+ Flask)
A note that connects to Lambda via AWS API Gateway (HTTP API) to process POST data
Try running a Schedule to start and stop an instance on AWS Lambda (Python)
[AWS] I made a reminder BOT with LINE WORKS
I made a Twitter BOT with GAE (python) (with a reference)
Dynamic HTML pages made with AWS Lambda and Python
[First API] Try to get Qiita articles with Python
Try to make a command standby tool with python
Easily try Amazon EMR / Cloud Dataproc with Python [mrjob]
Playing with a user-local artificial intelligence API in Python
Try implementing associative memory with Hopfield network in Python
Try embedding Python in a C ++ program with pybind11
[Blender] Complement Blender's Python API with a text editor