cancel
Showing results for 
Search instead for 
Did you mean: 
cancel

Spark Bot Demo

7210
Views
7
Helpful
3
Comments
Cisco Employee

This quick walkthrough is intended to give you a very simple Spark bot that responds to commands typed into a room with a few example responses – nothing too fancy, but easy to build on. The commands will be passed to the bot using mentions – so if I type into a room where the bot is a participant:

@MrWayne batsignal

The bot will retrieve that command, process it, and send us back a message. We’ll go step by step from bot registration to code upload and testing.

Step 1: Create the Bot

To register a Bot, you’ll need to be logged in to Spark with a “real” user – each bot needs to be tied to an actual user account. Adding one is extra simple, however – just click here \and select “Create a Bot”; there’s only a couple fields to fill out:

bot1.png

Display Name is how you want the bot to show up in a room (like “Mr. Wayne”); Bot Username is the email address, since every Spark user is registered under an email – this should be similar to the Display Name, but it can be unique.  Note that you are not populating the entire email – they will always end with @sparkbot.io, can’t make on with a gmail.com email or anything similar, so you’re just adding the prefix. The username prefix does need to be unique; if you don’t get a green check at the end of the @sparkbot.io reference, then the username was already taken. The Icon is the avatar for the bot, which will also show inside a room.

Once the bot is created, you’ll need to save the access token that is provided – keep it someplace safe.  The token effectively never expires (it’s good for 100 years) but if you forget it, you’ll have to generate a new one. There’s no way to get it back.

Note: Because this is a bot, OAuth is not used in this process; no need to refresh tokens any specific period. Only time you’d need to do the regeneration is if you lose the token, as mentioned above.

Step 2: Add the Bot to a Room

Pick a room, any room!  Either add the bot to a room you’re already in or have the bot create a new room using its access token – whatever you prefer.  The bot just needs to be present in a room in order to read messages and send messages back to it.

Step 3: Create an Outbound Webhook

Your webhook URL needs to be accessible on the public Internet – if you want to use your local machine, you can use a service like Ngrok to make your personal machine accessible to the world on a specific port for free.

But that means the webhook only works when you machine is up and live. Alternatively, Amazon Web Services offers a free EC2 micro instance for 1 year and more professional tiers at varied pricing.

Once you have an endpoint to use, create the webhook using the request on this page.

Make sure that the bearer token used in creating the webhook is the bearer token of the bot. You’ll need to know the ‘roomId’ of the room the bot is hanging out in, and you’ll need to know your own ‘targetUrl’ (the Ngrok link or the AWS link in our examples above); you’ll also want to set the ‘resource’ to messages and the ‘event’ to created. Here’s what the Webhook should look like once it’s been created:

{

"id": "Y2lz111111112222222233333333",

"name": "BotDemo Project ROOMNAME",

"targetUrl": "http://ec2-10-20-30-40.us-west-2.compute.amazonaws.com:10010",

"resource": "messages",

"event": "created",

"filter": "roomId=Y2lz12345678901234567890"

}

Check out the section in this link titled “Handling Requests from Spark” for more background info on webhooks.

Step 3b: The Code

The example webhook we just built sends all newly created messages in which the bot is mentioned to the server on port 10010 (all messages not directed to the bot are filtered out); we’ll use a simple Python program to handle them:

from itty import *

import urllib2

import json

def sendSparkGET(url):

    request = urllib2.Request(url,

                            headers={"Accept" : "application/json",

                                     "Content-Type":"application/json"})

    request.add_header("Authorization", "Bearer "+bearer)

    contents = urllib2.urlopen(request).read()

    return contents

@post('/')

def index(request):

    webhook = json.loads(request.body)

    print webhook['data']['id']

    result = sendSparkGET('https://api.ciscospark.com/v1/messages/{0}'.format(webhook['data']['id']))

    result = json.loads(result)

    print result

    return "true"

####CHANGE THIS VALUE#####

bearer = "BOT BEARER TOKEN HERE"

run_itty(server='wsgiref', host='0.0.0.0', port=10010)

For each message that is sent (‘created’), the data that hits your server is in a JSON format, so parsing them is easy, thanks to the built-in ‘json’ python library.  We have a ‘result’ object that has been JSON parsed and should contain a ‘text’ field, unless it’s something like an image someone posts in the Spark room.  That means that we can now use the ‘text’ attribute of the result object to start looking for specific commands or messages!  We want our bot to reply to a few commands, and everyone loves Batman, so how about these three:

batman

batcave

batsignal

Right after we print the result object, let’s add:

…snip…

print result

if 'batman' in result.get('text', '').lower():

    print "I'm Batman!"

elif 'batcave' in result.get('text', '').lower():

    print "The Batcave is silent..."

elif 'batsignal' in result.get('text', '').lower():

    print "NANA NANA NANA NANA"

…snip…

While it’s bat-tastic that our batbot can now recognize different commands, we want it to actually do something.  Let’s have it reply to the Spark room if any of our three commands are found in the text of the Spark message.  First, we’ll need a slightly different function (for HTTP POST), to send messages to a room.  Add this below the “sendSparkGET” function:


…snip…

def sendSparkPOST(url, data):

    request = urllib2.Request(url, json.dumps(data),

                            headers={"Accept" : "application/json",

                                     "Content-Type":"application/json"})

    request.add_header("Authorization", "Bearer "+bearer)

    contents = urllib2.urlopen(request).read()

    return contents

…snip…

This function is almost identical to "sendSparkGET", except that it also sends data, as POST requests do - now we can reply to the Spark room!  Let’s go back to our if-else block and make some changes.

…snip…

msg = None

if webhook['data']['personEmail'] != bot_email:

    if 'batman' in result.get('text', '').lower():

        msg = "I'm Batman!"

    elif 'batcave' in result.get('text', '').lower():

        msg = "The Batcave is silent..."

    elif 'batsignal' in result.get('text', '').lower():

        msg = "NANA NANA NANA NANA"

    if msg != None:

        print msg

        sendSparkPOST("https://api.ciscospark.com/v1/messages", {"roomId": webhook['data']['roomId'], "text": msg})

…snip…

With the above code added, we now have a reply to each of our commands!  Even better, the use of our sendSparkPOST function passes the roomId of the room that issued the command, so we don’t have to worry about multiple webhooks from different rooms getting mixed up!  To really make this bat worthy, we might want our functions to act a little differently from each other.  Let’s have batcave echo the message, if any text is sent after batcave.  For example, @MrWayne batcave Hello!, should send “Hello!” back to the room… and maybe we want the batsignal to really light up the Spark room with an image.  Here’s the full code to make all the magic happen:

from itty import *

import urllib2

import json

def sendSparkGET(url):

    """

    This method is used for:

        -retrieving message text, when the webhook is triggered with a message

        -Getting the username of the person who posted the message if a command is recognized

    """

    request = urllib2.Request(url,

                            headers={"Accept" : "application/json",

                                     "Content-Type":"application/json"})

    request.add_header("Authorization", "Bearer "+bearer)

    contents = urllib2.urlopen(request).read()

    return contents

  

def sendSparkPOST(url, data):

    """

    This method is used for:

        -posting a message to the Spark room to confirm that a command was received and processed

    """

    request = urllib2.Request(url, json.dumps(data),

                            headers={"Accept" : "application/json",

                                     "Content-Type":"application/json"})

    request.add_header("Authorization", "Bearer "+bearer)

    contents = urllib2.urlopen(request).read()

    return contents

  

@post('/')

def index(request):

    """

    When messages come in from the webhook, they are processed here.  The message text needs to be retrieved from Spark,

    using the sendSparkGet() function.  The message text is parsed.  If an expected command is found in the message,

    further actions are taken. i.e.

    /batman    - replies to the room with text

    /batcave   - echoes the incoming text to the room

    /batsignal - replies to the room with an image

    """

    webhook = json.loads(request.body)

    print webhook['data']['id']

    result = sendSparkGET('https://api.ciscospark.com/v1/messages/{0}'.format(webhook['data']['id']))

    result = json.loads(result)

    msg = None

    if webhook['data']['personEmail'] != bot_email:

        in_message = result.get('text', '').lower()

        in_message = in_message.replace(bot_name, '')

        if 'batman' in in_message or "whoareyou" in in_message:

            msg = "I'm Batman!"

        elif 'batcave' in in_message:

            message = result.get('text').split('batcave')[1].strip(" ")

            if len(message) > 0:

                msg = "The Batcave echoes, '{0}'".format(message)

            else:

                msg = "The Batcave is silent..."

        elif 'batsignal' in in_message:

            print "NANA NANA NANA NANA"

            sendSparkPOST("https://api.ciscospark.com/v1/messages", {"roomId": webhook['data']['roomId'], "files": bat_signal})

        if msg != None:

            print msg

            sendSparkPOST("https://api.ciscospark.com/v1/messages", {"roomId": webhook['data']['roomId'], "text": msg})

    return "true"

####CHANGE THESE VALUES#####

bot_email = "yourbot@sparkbot.io"

bot_name = "yourBotDisplayName"

bearer = "BOT BEARER TOKEN HERE"

bat_signal  = "https://upload.wikimedia.org/wikipedia/en/c/c6/Bat-signal_1989_film.jpg"

run_itty(server='wsgiref', host='0.0.0.0', port=10010)

Don’t forget to fill in your Bot’s auth token above.  For Mac and Linux systems, Python is installed by default.  For Windows, it’s a pretty easy install, just make sure you look for version 2.7.  To run the server application, open a command terminal, and navigate to the folder where you saved this Python script - we named ours bot_demo.py - then run:


python bot_demo.py

As long as all of the right ports are available, you should see this in the terminal:

Listening on http://0.0.0.0:10010...

Use Ctrl-C to quit.

Holy Spark-Bot, Batman!  We’re done!

Enjoy the example - you can also get the complete code on Github ! Let us know if you have any questions.

Taylor Hanson, Customer Support Engineer II

3 Comments
Enthusiast

Thanks for adding this information.  Very helpful...  Do you have more any more documentation regarding how to set up your application through AWS?

Hi, Thank you.

I have question; I have created the bot. But when I add the bot to space/room, it doesn't display/say any greeting message.

How do I add greeting message, when someone add the Bot to their room/space?

Thanks

Rajan

Beginner

This is a great example to get started in creating a bot. The bot_demo.py works fine when a message is sent directly to the bot in Webex Teams. If I create a new space and add the bot to it as a member, when I send a message, the script throws the following error:

<class 'urllib2.HTTPError'> occurred on '/': HTTP Error 404: Not Found

Traceback: Traceback (most recent call last):

  File "/Library/Python/2.7/site-packages/itty.py", line 603, in handle_request

    response = callback(request, **kwargs)

  File "gazoo_bot.py", line 45, in index

    result = sendSparkGET('https://api.ciscospark.com/v1/messages/{0}'.format(webhook['data']['id']))

  File "gazoo_bot.py", line 16, in sendSparkGET

    contents = urllib2.urlopen(request).read()

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 154, in urlopen

    return opener.open(url, data, timeout)

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 437, in open

    response = meth(req, response)

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 550, in http_response

    'http', request, response, code, msg, hdrs)

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 475, in error

    return self._call_chain(*args)

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 409, in _call_chain

    result = func(*args)

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 558, in http_error_default

    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)

HTTPError: HTTP Error 404: Not Found

127.0.0.1 - - [01/Jun/2018 10:37:22] "POST / HTTP/1.1" 500 17

Any ideas what this might be?