Downloading a File from a Spark Room
August 14, 2016

If you’ve ever wondered, “How do I retrieve a file using the API – it’s just a URL with an encrypted ID!”, this example should hold the answer. It uses a bot to download a file sent to a 1:1 room (a conversation between the bot and a single user), using a webhook. If your bot is in a group room, it will only be able to retrieve files where the bot is explicitly mentioned, like @botname.
For additional help creating a webhook, we recommend checking out our webhook blog and our simple (but complete) bot demo.
Alternatively, if you’re using an access token retrieved using Oauth, you can retrieve the messages in a room with this function.
In the Webhooks Explained page, the section titled Handling Requests from Spark describes the format of webhook messages. When a file is sent to the 1:1 room with the bot, the webhook will send JSON to your server containing a files key, under the data element, in addition to the other attributes such as personEmail:
{"resource": "messages", "name": "Web hook name", "created": "2016-07-12T18:24:17.179Z", "id": "Y2lzY29zcGFyazovL3VzL1dFQkhPT0svMWU3Y2Y4OGMtYTE4My00ZGQyL123456789", "filter": "roomId=Y2lzY29zcGFyazovL3VzL1JPT00vNmFjNjA0OTAtMDU3YS0xMW123456789", "targetUrl": "[http://myurl.com](http://myurl.com/)", "data": {"files": \["[https://api.ciscospark.com/v1/contents/Y2lzY29zcGFyazovL3VzL0NPTlRFTlQvY2IyZDNhYjAtNWEzZS0xMWU2L12345678](https://api.ciscospark.com/v1/contents/Y2lzY29zcGFyazovL3VzL0NPTlRFTlQvY2IyZDNhYjAtNWEzZS0xMWU2L12345678)"\], "roomType": "group", "created": "2016-08-04T12:27:22.459Z", "personId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS84ZDUwY2M5NS0wMWMyLTQwZmUtOTU5OC1mZmE4M1234567", "personEmail": "[person@example.com](mailto:person@example.com)", "roomId": "Y2lzY29zcGFyazovL3VzL1JPT00vNmFjNjA0OTAtMDU3YS0xMWU2LWEw123456789", "id": "Y2lzY29zcGFyazovL3VzL01FU1NBR0UvY2IyZDNhYjAtNWEzZS0xMWU123456789"}, "event": "created"}
The files attribute is a list of URLs (one URL if only one file is sent in a single message, multiple URLs if more than one file is sent at the same time) that will be used to retrieve the actual files. The URL for the file will be the request URL in your REST GET, and you’d just need to then pass the bot’s authentication token in a Bearer authorization header to retrieve the details (because who can view the file is limited to who is in the room – again though, in a group room, a bot’s permission is limited to messages in which it is also mentioned).
Information and additional examples on retrieving messages is detailed here.
The “Content-Disposition” header in the GET response is used to determine the file’s name and file type to be saved. The file data is the entire body of the GET response. There is no base 64 or other special encoding of the file data, so it can be written directly to the destination. So if we were retrieving an image from our example above, we would run the GET on:
and we would get back:
Cache-Control: no-cache
Content-Disposition: attachment; filename="04\_12\_02.jpg"
Content-Length: 264178
Content-Type: image/jpeg
Date: Thu, 04 Aug 2016 20:09:05 GMT
Server: Redacted
Trackingid: NA_028c214f-a7dc-44e8-a8bd-f82373fbc860
X-Cf-Requestid: 317eaaad-b2cf-4651-5ce0-4dbdcc700abd
Connection: close
The below code is an app that receives the data from the webhook and - if the data contains a files key - attempts to download the files directly to the source folder from where it is run (so if the app is located in your /Documents/Files folder, it will download the file to /Documents/Files).
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)
return contents
@post('/')
def index(request):
webhook = json.loads(request.body)
if webhook\['data'\].has_key('files'):
for file_url in webhook\['data'\]\['files'\]:
response = sendSparkGET(file_url)
content_disp = response.headers.get('Content-Disposition', None)
if content_disp is not None:
filename = content_disp.split("filename=")\[1\]
filename = filename.replace('"', '')
with open(filename, 'w') as f:
f.write(response.read())
print 'Saved-', filename
else:
print "Cannot save file- no Content-Disposition header received."
else:
print "No files attached to retrieve!"
return "true"
####CHANGE THIS VALUE#####
bearer = "BOT\_TOKEN\_HERE"
run_itty(server='wsgiref', host='0.0.0.0', port=10002)
The full code can be found on our Github.
As always, If you have any questions, please contact devsupport@ciscospark.com 24/7/365 - we’re happy to help!