Translator via SMS with C# and Azure Functions

September 19, 2018
Written by

pwv8AtRTNvGi2RBhg7igAUDd3FSB0HEIhl-BVojY7M01jf8lcShxgnA5k237tZS5uIOc7NDkBm04IWmVtC4VwBG-qlNISJA2IdV31Qcy10HIG8TqXtv35FPjCTIzthyVcTfkwW8Q

Ahoy and yo-ho-ho! In celebration o' International Talk Like A Pirate Day, let's build a SMS Pirate Speak translator with Azure Functions, the Fun Translations API and, of course, Twilio.

What you’ll need:

Azure Functions allow you to write code without having to worry about infrastructure or servers, so they are perfect for setting up a simple webhook for an incoming SMS or call from Twilio.

If you would like to see a full integration of Twilio APIs in a .NET Core application then checkout this free 5-part video series I created. It's separate from this blog post tutorial but will give you a full run down of many APIs at once.

Create an Azure Function

From the Azure portal, click the Create a resource button, found at the top of the menu on the left.  Search for a Serverless Function App and then click on the matching result.

Give your app a name, I called mine twilio-sms-translator, create or reuse a resource group, and choose the Location that best suits you.  You can then click Create at the bottom of the panel.

 

Once the new resource has deployed, go into Functions from the menu on the left.  If you can’t see Functions then click on All services below Create a resource, search for Function Apps, and then click the star to the right of the result to favourite it.  Now it should be on the panel on the left.

You should see your newly created Function App with three submenus; Functions, Proxies and Slots.  Click on the plus button beside Functions to add a new function to our Function app.

 

You’ll be given some options for quickstarts but we are going to create a Custom Function, so go ahead and click on that link.

The link will take you to a screen with loads of template types.  The one we want, HTTP trigger should be first. Select the link for C#.  A panel will slide out from the right of the screen asking you to name your function. Do this and then click create.

 

 

You should now see your newly created function listed under your function app. Click on the Get function URL button and save the URL for later.

 

 

On the far right, you will see a panel, expand this and click on the View Files tab.  Add a new file called project.json to enable us to bring in NuGet packages.  Once added, copy and paste in the code below then save it.

 

{
 "frameworks": {
   "net46": {
     "dependencies": {
       "Twilio": "5.15.0"
     }
   }
 }
}

Calling the Fun Translations API

Let’s add another file to the function called Translator.csx. In this code, we are using an HTTP client to make a call to the Fun Translations API.  If our call is successful, we deserialize the returned JSON string, casting it to a dynamic object and then return contents.translated.

#r "Newtonsoft.Json"

using System;
using System.Net;
using System.Net.Http.Headers;
using Newtonsoft.Json;

public static async Task<string> GetTranslationAsync(string text)
{
  string translatedText = "No translation for you!  Something went wrong!";

  using (var httpClient = new HttpClient())
  {
      var apiEndPoint = "https://api.funtranslations.com/translate/pirate?text="+ text;
      httpClient.BaseAddress = new Uri(apiEndPoint);
      httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

      var responseMessage = await
          httpClient
              .GetAsync(apiEndPoint);

      if (responseMessage.IsSuccessStatusCode)
      {
          string jsonContent = await responseMessage.Content.ReadAsStringAsync();
          dynamic data = JsonConvert.DeserializeObject(jsonContent);
          translatedText = data.contents.translated;       
      }
else if(responseMessage.StatusCode == (HttpStatusCode)429)  {
          //Fun Translations API rate limits to 5 calls an hour unless you are using the paid for service.
          translatedText = "too much pirate speak, drink some rum and try again later";
      }

  }
  return translatedText;
}

Azure Functions come with some external assemblies, like Newtonsoft.json already included, we just need to reference them using the #r "Newtonsoft.Json" notation.  This also means we don’t need to add them to the project.json file.

Click on the run.csx file.  This is our entry point where we will handle the incoming API call.

Update the file where we set up our usings with the following code. Note how we reference our Translator method with #load “Translator.csx”

#r "System.Xml.Linq"
#load "Translator.csx"

using System.Net;
using System.Text;
using Twilio.TwiML;

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("+", " "));

   var body = formValues["Body"];

   
}

The message data from Twilio will come in as form data, so we need to split it and add it to a dictionary. In this case I've called it formValues.  We can then extract the Body of the incoming SMS from that dictionary.

Now that we have the Body from the SMS, we can call our GetTranslationAsync method and pass in the value.

All that's left to do in our function is create a new MessageResponse, pass in our pirateSpeak translated text as the new message body, and return it to Twilio.

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("+", " "));

   var body = formValues["Body"];

   var pirateSpeak= await GetTranslationAsync(body);
  
   var response = new MessagingResponse();
       response.Message(pirateSpeak);
   var twiml = response.ToString();

   return new HttpResponseMessage
   {
       Content = new StringContent(twiml, Encoding.UTF8, "application/xml")
   };
}

Now that we have our function functioning we can add the URL we saved earlier to our Twilio number configuration.

 

Time to try it out

Send your Twilio number a phrase you need translated and you should receive some Pirate back!

 

If you get too piratey, requesting too many translations, Fun Translations API will cut you off with a 429 Status Code, or Too Many Requests, so we've handled that too.

 

 

 

What else can we do?

The Fun Translations API has several cool APIs to play around with from Klingon to Pig-Latin, so why not make some other translators?

Azure Functions are great for small sections of code that you want to reuse across different projects like token generators for example.

Azure Functions on the Consumption plan will scale automatically so they are also great for sporadically high-load jobs.

 

Let me know what cool ideas you have for Twilio and Azure Functions with and feel free to get in touch with any questions.

I cannot wait t' see what ye build!