Spark Bot Demo
July 18, 2016This 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:
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](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.