Use Azure Functions and Twilio SMS to know: Will it Rain Today?

November 21, 2018
Written by

8yf3DI9q3T04ILu-x88AeMjsNpbgbaETyS_SgFbM6wimXN9RXOdA5P2_1UHPoe62gZni1rOhbmib9eWzWbYRAkaIQ4wSOuMMnhX_owhwG6kkfpwFJI1fNdP5iTIuq_6BB_CimEEF

Will you need an umbrella today? Don't know? That's easy to fix by looking out the window or by using Azure Functions and its Twilio SMS output binding. In this post, I'll show you how with just a few lines of code you can have a text message arrive each morning which lets you know if it might rain that day.

You can build along with me by signing up for free Azure and free Twilio accounts.

Create an Azure Function

We'll start by creating a new Azure Function App in the Azure Portal.

Once created continue configuring the Function App by adding environment variables to hold our Twilio credentials.  Open the Applications Settings for the Function App.

Add two new keys named TwilioAccountSid and TwilioAuthToken, setting their values to your Twilio AccountSid and AuthToken respectively. You can find your Twilio credentials in the Twilio Console.

Save the Application Settings and then create a new Function using the Timer template.  

Use a cron expression to configure the timer to run once per day.

With the Function created, open the Integrate tab for the Function and add Twilio SMS as a new Output:

Configure the Output settings with the TwilioAccountSid and TwilioAuthToken environment variable keys we created earlier, a Twilio phone number to send From, and your own phone number to send To.

Return to the Function code and add references for the Twilio library:

#r "Twilio.Api"

using System;
using Twilio;

In the Run function, add a new out method parameter of type SMSMessage named message.  Next, within the method create a new instance of the SMSMessage type and set its Body property to Will it rain today?.

public static void Run(TimerInfo myTimer, out SMSMessage message, TraceWriter log)
{
   log.Info($"C# Timer trigger function executed at: {DateTime.Now}");

   message = new SMSMessage();
   message.Body = $"Will it rain today?";
}

Test the Function and you should get a text message asking you if it will rain.

Next, we need to answer the question we just asked: Will it rain today? To do that we'll scrape the website willitrainin.com, grabbing the simple Yes or No value it displays on its city-specific forecast pages and appending that to the text message.

Add a new method called WillItRainIn in run.csx and use an instance of HttpClient to fetch the Will It Rain In page for your local area:

public static async Task<string> WillItRainIn() {
  
   string result = "No";
   string website = "https://willitrainin.com/1105779/australia/sydney";

   var client = new HttpClient();
   var html = await client.GetStringAsync(website);

   return result;
}

We'll parse the Yes or No text from the HTML received by the HttpClient using the HtmlAgilityPack library.

Add the HtmlAgilityPack to the Function by creating a project.json file and adding the libraries NuGet package reference as a dependency.  

{
 "frameworks": {
   "net46":{
     "dependencies": {
       "HtmlAgilityPack": "1.8.2"
     }
   }
 }
}

Save the file and Azure will automatically install the package for you.

Add a using statement for the library in the run.csx file.

#r "Twilio.Api"

using System;
using HtmlAgilityPack;
using Twilio;

Load the HTML into an HtmlDocument and then use the SelectSingleNode method to search for a <p> tag marked with the result class.  Put that tag's content into the result variable.

public static async Task<string> WillItRainIn() {

   string result = "No";
   string website = "https://willitrainin.com/1105779/australia/sydney";

   var client = new HttpClient();
   var html = await client.GetStringAsync(website);

   var doc = new HtmlDocument();
   doc.LoadHtml(html);

   result = doc.DocumentNode.SelectSingleNode("//p[@class='result']").InnerHtml;

   return result;
}

Now we'll add a call to the WillitRainIn method to our main Run method.  

Because we're using HttpClient's asynchronous methods, we've marked the WillItRainIn method itself as async.  That means in order to use it in our Run method we need to modify it to also be async.

Add the async keyword to the Run method signature and change the default return value from void to Task. Additionally, because async methods cannot include out parameters in their method signatures, we have to change the message parameter to be a reference parameter (by removing the out keyword) wrapped with an IAsyncCollector interface.

public async static Task Run(TimerInfo myTimer, IAsyncCollector<SMSMessage> message, TraceWriter log)

Add the async call to the WillItRainIn method.  Create a new SMSMessage, set its Body property and then pass it to the IAsyncCollector.

public async static Task Run(TimerInfo myTimer, IAsyncCollector<SMSMessage> message, TraceWriter log)
{
   log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
   var result = await WillItRainIn();
   log.Info(result);

   SMSMessage sms = new SMSMessage();
   sms.Body = $"Will it rain today? {result}";

   await message.AddAsync(sms);
}

Run the function again and now the text message should contain both the question and the answer.  And now with just a few lines of code, you can know every day if you'll need to grab your umbrella!  Let me know what you're building on Azure Functions -  drop me a line on Twitter @devinrader or send an email to devin@twilio.com.