I'm trying to get an Azure Web Function to receive a Twilio SMS message - and failing!
I've created a Web Function to successfully send SMS messages - now I want to listen and react to responses.
I've set up a web function as per the below. Its pretty simple at the moment, and is supposed to parrot back the original message:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var data = await req.Content.ReadAsStringAsync();
var formValues = data.Split('&')
.Select(value => value.Split('='))
.ToDictionary(pair => Uri.UnescapeDataString(pair[0]).Replace("+", " "),
pair => Uri.UnescapeDataString(pair[1]).Replace("+", " "));
// Perform calculations, API lookups, etc. here
var response = new MessagingResponse()
.Message($"You said: {formValues["Body"]}");
var twiml = response.ToString();
twiml = twiml.Replace("utf-16", "utf-8");
return new HttpResponseMessage
{
Content = new StringContent(twiml, Encoding.UTF8, "application/xml")
};
}
In Twilio, I've configured the phone to use web hooks:
I've deployed the Web Function, however when I try testing by sending a message, I get the following error in the Twilio logs:
11200 There was a failure attempting to retrieve the contents of this URL
Msg Unsupported Media Type
Message: The WebHook request must contain an entity body formatted as JSON.
Does anyone have any experience in how to fix this error?
I just got this working with Twilio's SMS services. In the Azure Portal, if you go to the function, then go to Integrate, change the mode to Standard. This forces the Azure function to return a normal HTTP response and you can control the content type. If you use application/xml it will work fine.
Okay, the current solution to this is .... it can't be done in Azure Web Functions. An Azure Web Function expects a JSON payload. Twilio Webhooks are an XML value/pair. So, the web function will reject the webhook call.
The best/easiest approach is to use a WebAPI or MVC Controller as per the Twilio example. I tried a sample version and had my Twilio Webhooks working to reply to an SMS in about 15 minutes from start to finish.
To debug, I'd use a tool such as Postman or Fiddler to manually replay (or create from scratch) an identical request to what you're expecting from Twilio. You can then see what kind of response you get and not have to solely rely on Twilio's error message.
From the error code, I'd imagine that the problem is either:
Your URL set up in the Twilio number configuration isn't actually reaching your function.
Your function is taking too long to respond. Twilio will throw 11200 if it doesn't get a response in a certain time.
Your response is formatted incorrectly. This is where the aforementioned strategy can help you diagnose the issue.
Related
I am confused as to how should the watch feature in the gmail API be implemented to recieve the push notificatons inside a node.js script. Should I call the method inside an infinite loop or something so that it doesn't stop listening for notifications for email once after the call is made?
Here's the sample code that I've written in node.js:
const getEmailNotification = () => {
return new Promise(async (resolve, reject) => {
try{
let auth = await authenticate();
const gmail = google.gmail({version: 'v1', auth});
await gmail.users.stop({
userId: '<email id>'
});
let watchResponse = await gmail.users.watch({
userId: '<email id>',
labelIds: ['INBOX'],
topicName: 'projects/<projectName>/topics/<topicName>'
})
return resolve(watchResponse);
} catch(err){
return reject(`Some error occurred`);
}
})
Thank you!
Summary
To receive push notifications through PUB/SUB you need to create a web-hook to receive them. What does this mean? You need a WEB application or any kind of service that exposes a URL where notifications can be received.
As stated in the Push subscription documentation:
The Pub/Sub server sends each message as an HTTPS request to the subscriber application at a pre-configured endpoint.
The endpoint acknowledges the message by returning an HTTP success status code. A non-success response indicates that the message should be resent.
Setup a channel for watch the notifications could be summarized in the following steps (the documentation you refer to indicates them):
Select/Create a project within the Google Cloud Console.
Create a new PUB/SUB topic
Create a subscription (PUSH) for that topic.
Add the necessary permissions, in this case add gmail-api-push#system.gserviceaccount.com as Pub/Sub Publisher.
Indicate what types of mail you want it to listen for via Users.watch() method (which is what you are doing in your script).
Example
I give you an example using Apps Script (it is an easy way to visualize it, but this could be achieved from any kind of WEB application, as you are using Node.js I suppose that you are familiarized with Express.js or related frameworks).
First I created a new Google Apps Script project, this will be my web-hook. Basically I want it to make a log of all HTTP/POST requests inside a Google Doc that I have previously created. For it I use the doPost() equal to app.post() in Express. If you want to know more about how Apps Script works, you can visit this link), but this is not the main topic.
Code.gs
const doPost = (e) => {
const doc = DocumentApp.openById(<DOC_ID>)
doc.getBody().appendParagraph(JSON.stringify(e, null, 2))
}
Later I made a new implementation as a Web App where I say that it is accessible by anyone, I write down the URL for later. This will be similar to deploying your Node.js application to the internet.
I select a project in the Cloud Console, as indicated in the Prerequisites of Cloud Pub/Sub.
Inside this project, I create a new topic that I call GmailAPIPush. After, click in Add Main (in the right bar of the Topics section ) and add gmail-api-push#system.gserviceaccount.com with the Pub/Sub Publisher role. This is a requirement that grants Gmail privileges to publish notification.
In the same project, I create a Subscription. I tell it to be of the Push type and add the URL of the Web App that I have previously created.
This is the most critical part and makes the difference of how you want your application to work. If you want to know which type of subscription best suits your needs (PUSH or PULL), you have a detailed documentation that will help you choose between these two types.
Finally we are left with the simplest part, configuring the Gmail account to send updates on the mailbox. I am going to do this from Apps Script, but it is exactly the same as with Node.
const watchUserGmail = () => {
const request = {
'labelIds': ['INBOX'],
'topicName': 'projects/my_project_name/topics/GmailAPIPush'
}
Gmail.Users.watch(request, 'me')
}
Once the function is executed, I send a test message, and voila, the notification appears in my document.
Returning to the case that you expose, I am going to try to explain it with a metaphor. Imagine you have a mailbox, and you are waiting for a very important letter. As you are nervous, you go every 5 minutes to check if the letter has arrived (similar to what you propose with setInterval), that makes that most of the times that you go to check your mailbox, there is nothing new. However, you train your dog to bark (push notification) every time the mailman comes, so you only go to check your mailbox when you know you have new letters.
I have developed an application that sends thousands of SMS using Twilio Notify Service.
const bindings = phoneNumbers.map(number =>
this.createBinding('sms', number)
);
await this.notifyService.notifications.create({
toBinding: bindings,
body
});
The code above doesn't give me a feedback of whether the messages were received or not, but as I can see in Twilio dashboard, some messages fail with error codes 30005, 30003, 30006 and 52001.
I'd like to consume all those error logs and unsubscribe the numbers with error codes. I'm thinking of creating a job that runs every night to do that.
I've tried to list all the alerts:
client.monitor.alerts.each(alert => {
console.log(alert.errorCode);
});
But it seems to fetch only some alerts with error code 52001.
How can I consume all the errors with Twilio API? Is there any other way to be notified of those errors?
Ok, this is not what you think it is, I am not asking for help with the async/wait pattern or asynchronous programming I am well versed with those. I am however querying whether something is possible within a Node.JS Express service.
The Scenario
I have a web service which is developed in Node.JS and uses Express.JS to expose some REST endpoints that a client can connect to and send a POST request. For the most part these are Synchronous and will create a SOAP message and send that on to an external service and receive an immediate response which can then be returned to the client, all really simple stuff which is already implemented. So what's your point I hear you say, I am coming to that.
I have a couple of POST interactions that will build a SOAP message to send to an Asynchronous external endpoint where the response will be received asynchronously through an inbound endpoint.
Option 1: What I am looking for in these cases is to be able to build the SOAP message, create a listener (so I can listen for the response to my request), and then send the request to the external service which immediately returns a 200.
Option 2: When I setup the service I want to also setup and listen for incoming requests from the external service whilst also listening for REST requests from the internal service.
The Question
Is either option possible in Node and Express? and, if so how would one achieve this?
NOTE: I know its possible in C# using WCF or a Listener but I would like to avoid this and use Node.JS so any help would be greatly appreciated.
First of all check node-soap if it fits your needs.
Option 1: What I am looking for in these cases is to be able to build the SOAP message, create a listener (so I can listen for the response to my request), and then send the request to the external service which immediately returns a 200.
Here's a very basic non-soap service implementation.
let request = require('request-promise');
let express = require('express');
let app = express();
//Validate the parameters for the request
function validateRequest(req) { ... }
//Transform the request to match the internal API endpoint
function transformRequest(req) { ... }
app.post('/external', function(req, res) {
if(!validateRequest(req))
return res.status(400).json({success: false, error: 'Bad request format');
res.status(200).send();
let callbackUrl = req.query.callback;
let transformedRequest = transformRequest(req);
let internalServiceUrl = 'https://internal.service.com/internal'
request.post(internalServiceUrl, {body: transformedRequest}).then(function (internalResponse){
//Return some of the internal response?
return request.get(callbackUrl, {success: true, processed: true});
}).catch(function (e) {
request.get(callbackUrl, {success: false, error: e});
});
});
Option 2: When I setup the service I want to also setup and listen for incoming requests from the external service whilst also listening for REST requests from the internal service.
There is no "listening" in http. Check socket.io if you need realtime listening. It uses websockets.
Your other option is to poll the internal service (say if you want to check for its availability).
When a user texts my number, I would like to respond with a message using TwiML. I'd also like to know whether my response message sends or fails. I'm creating my TwiML response using the Twilio library for Node.js.
Here is my code:
const response = new twilio.TwimlResponse();
response.message(Response, {
statusCallback: '/sms/status/'
});
Here is the code for the route. Please note that the router is mounted upon the "/sms/" router:
router.post('/status/', acceptRequestsOnlyFromTwilio, (req, res) => {
const Event = require('./../models/schemas/event');
const event = new Event({
description:
`MessageSid = ${req.body.MessageSid}
MessageStatus = ${req.body.MessageStatus}`
});
event.save();
console.log(
`MessageSid = ${req.body.MessageSid}
MessageStatus = ${req.body.MessageStatus}`
);
res.sendStatus(200);
});
When I check my logs using the Heroku CLI, I don't log nor do I see a record when I check MongoDB.
Anyone have any ideas or tips on how I can go about debugging this or if I'm implementing this incorrectly?
Thanks for you time!
Twilio developer evangelist here.
Firstly, have you set your Messaging request URL for the Twilio phone number you are using? Is it pointing at the correct route for your Message TwiML response?
Also, your first code block should also be within a router.post endpoint for your server. Do you correctly return the TwiML response as the server response?
As a tip, it might be better to develop this application locally and test your webhook endpoints using a tool like ngrok. I love using ngrok to test webhooks.
I am using the new scheduler service within Windows Azure Moblie Services. I wanted to know if I can do an HTTP post from the script?
Here is my scenario: I have built a service that goes out and does some logic and updates my database with the results. In a certain condition, I would like it to notify me via SMS text message. => Enter Twilio.
Twilio has a RESTful api for me to communicate with cellphones via a simple HTTP POST. How can I generate the HTTP post from the script within the scheduler to post (something) to an external API?
Easiest way it to use Twilio Module for nodejs.
Blog post how to use modules on your server side script.
Twilio evangelist here.
Heres some Azure documentation on using Twilios node.js library with Mobile Services:
http://www.windowsazure.com/en-us/develop/mobile/tutorials/twilio-for-voice-and-sms/
Hope that helps.
Since I had a management website already on AZURE anyway, I just wrote a custom handler passing the data and then using the extremely easy-to-use Twilio .NET Rest api:
Created an (Notify.ashx) http handler
Downloaded the Twilio Rest API helper library
Added the following code to my handler
Dim twilio = New TwilioRestClient(myAccountSid, myAuthToken)
Dim twilio_message = twilio.SendSmsMessage(FromTwilioPhone, ToCellphone, message)
Added this to my Server Script:
function notify(uniqueident) {
var url = "http://{mywebsite}.azurewebsites.net/Notify.ashx?id=" + uniqueident;
request(url, function siteLoaded (error, response, body) {
if (!error && response.statusCode == 200) {
console.log('Notification sent: ' + uniqueident);
} else {
console.error('Could not contact azure.');
}
});
}
What would be really nice is that if WindowsAzure had a add-on (app service) for Twilio like #SendGrid. (hint... hint...)