Last week I blogged about a sample application I built using OpenWhisk, Twilio, and IBM Watson. The idea was - I could send a picture to a SMS number set up with Twilio, Twilio would send the data to OpenWhisk, OpenWhisk would get the picture and send it to Watson for identification, and finally the result would be sent back to the user. This morning a coworker pointed out a few issues and I found a way for the code to be much simpler. Normally I'd just update the post, but I thought a followup would be better.

First off - the code for the action that listens for a POST from Twilio had this line:

 var client = new twilio(args.accountSid, args.authToken);

I totally forgot to say that those two values come from Twilio and should be set as default parameters for your OpenWhisk action. Like so:

wsk action update smsidentify/gotMessage --param accountSid X --param authToken Y

Where X and Y are the two values. You could hard code them too I suppose, but it's better to have them as parameters.

As someone who tries his best to teach well, I look out for stuff like this - when the writer makes assumptions - and I try my best to not do that myself. Sorry!

The next issue was a bit more subtle. I had noticed an error in the Twilio logs in regards to the response type. Here is an example:

Error

However, since the error didn't seem to break the app, I forgot about it. I should not have done so. Turns out, you need to return an XML response in reply to the HTTP POST Twilio sends to your action. My original code in sendResults.js looked like so:

client.messages.create({
	body: message,
    to: args.from,  
    from: args.to
})
.then((message) => {
	resolve({result:1});
})

Basically - use the Twilio library to message back and just return 1 since I don't really care about what I return. However, this is what caused the problem - I wasn't doing a proper XML response. I initially tried to find out how, "How do I send an empty XML back to just make Twilio happy" when I discovered something else - I can actually send my result in the XML! Not only does this make it easier to send the result, but it solves other issues as well.

In the original post, I talked about how the "identify" action had to "carry over" the phone numbers that the first action received. This was so that the third and final action could properly "call back". But now that I'm simply returning XML, that's no longer an issue!

So my corrections involved the following. I edited identify.js to remove the "carry over additional args" hack. (You can see the source here). Then, I edited sendResults.js to be much more simpler:

function main(args) {
	return new Promise((resolve, reject) => {

		/*
		ok, so if #1 doesn't tie with #2, return "1 or maybe a 2"
		if it ties, return "1 or 2"
		*/
		let message = '';
		if(args.tags[0].score !== args.tags[1].score) {
			message = `I think this is a ${args.tags[0].class}. But it may be a ${args.tags[1].class}.`;
		} else {
			message = `I think this is a ${args.tags[0].class} or a ${args.tags[1].class}.`;
		}
		console.log('Message to send via SMS: '+message);

		resolve({
			headers:{
				'Content-Type':'text/xml'
			},
			body:'<Response><Message>'+message+'</Message></Response>'
		});

	});
}

Finally, and this is critical - I edited the Web Hook URL configuration in Twilio to remove ".json" from the end and change it to ".http". This tells OpenWhisk that I'm going to be defining my own response type with headers and the like. (As much as XML is kinda meh, I think I'd like it if OpenWhisk Web Actions supported .xml in the URL. I'm going to file a request for that.)

All in all, I'm really happy that this bug helped flesh out (a bit) my knowledge of the Twilio API. As a reminder, all three actions can be found on GitHub here: https://github.com/cfjedimaster/Serverless-Examples/tree/master/smsidentify.