Wake up to Useful Daily Messages with Java and Azure Functions

October 08, 2019
Written by

Wake up to Useful Daily Messages with Java and Azure Functions

I wake up slowly. I often need a few minutes to remember where I am and what day it is. I usually have my phone handy - it’s my alarm clock, too. But then I decide to check the weather, and before I know it I’ve got carried away on Twitter, Reddit, email and so on. It takes me ages to start getting ready for the day. I decided to build a small app which would send me an SMS every morning. I can see what the day has in store without getting involved in anything online.

I decided to use Azure Functions with a TimerTrigger to run some Java code every morning. The code pulls data from wttr.in and affirmations.dev, formats it into a dense (read: emoji-laden) message and sends it to me:

An SMS in my messaging app: Good morning (weather: rain showers, temp +10 Celsius), Today is Monday, October 7. Remember you are a capable human being. Have a great day (rainbows and sparkles)

In this post I’ll walk you through how to create this and customise it for yourself. The completed project is also available on GitHub.

The Plan of Action

  1. Starting Out: Project Setup
  2. The First Message: Printing Today’s Date
  3. More Data: Adding Weather & Phase of Moon
  4. Starting on a High: Adding an Affirmation
  5. Tying it Together: Sending SMS
  6. Run Your Code: Deploying as an Azure Function

Starting Out: Project Setup

Azure Functions for Java uses Java 8, so If you don’t have that installed head to AdoptOpenJDK for download and installation instructions. The project uses Apache Maven for its build.

Use the Azure Functions Maven archetype to create the project. This sets up all the config you need and creates a small function to start working on:

mvn archetype:generate \                        
    -DarchetypeGroupId=com.microsoft.azure \
    -DarchetypeArtifactId=azure-functions-archetype

This will ask for some details of the project. If you’re not sure about groupId and artifactId, check the Maven naming conventions for details. You can accept the defaults for everything else.

The project template will be generated in a directory named after your artifactId. Import it into your favourite IDE and find the class named Function which the template generated.

The First Message: Printing Today’s Date

In the same package as Function, create a new class called DailyMessage, with a method class that returns a message with today’s date, and a main method for quickly testing it:

public static void main(String[] args) {
   System.out.println(new DailyMessage().getTodaysMessage());
}

public String getTodaysMessage(){
   LocalDate today = LocalDate.now(Config.MY_TIMEZONE);
   DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("EEEE, MMMM dd");
   String todaysDate = today.format(dateFormatter);

   return String.format(
       "Good Morning!\n\n" +
       "It's %s",
       todaysDate
   );
}

(code on GitHub)

The Config class keeps the various configuration options needed throughout this post. If you intend to share this code, it’s best to use environment variables but the config can be hard-coded for now:

public class Config {
   public static final ZoneId MY_TIMEZONE = ZoneId.of("Europe/London");
}

(code on GitHub)

You can run the main method and see today’s date printed 🎉🎉

More Data: Adding Weather & Phase of Moon

wttr.in is a great tool for providing facts about the weather. The simplest way to use it is on the command line with curl wttr.in which returns a beautiful ascii-art weather forecast in your terminal, using IP geolocation to detect where you are:

Output of "curl wttr.in" - an ascii-art calendar styled weather forecast.

This human-readable output is extremely nice, even if the weather is a bit rubbish.

Using curl 'wttr.in/Bath?format=%c,%m,%t&m' will make life easier. This returns a comma-separated list of current weather ☀️, moon-phase 🌒 and temperature +12°C in my location. The &m specifies “metric” (ie Celsius) or you could use &u for Farenheit. You could also customise wttr’s response for yourself.

To use this in Java, add a new dependency on the fluent-hc HTTP client from Apache to pom.xml. You can see how in the code on GitHub.

In a new class called Wttr, this method calls the wttr API:


public WttrResult getTodaysWeather() throws IOException {

   String url = String.format("https://wttr.in/%s?format=%%25c,%%25m,%%25t&m",
       URLEncoder.encode(Config.WTTR_LOCATION, "UTF-8"));

   String[] response = Request
       .Get(URI.create(url)).execute()
       .returnContent().asString()
       .split(",");

   return new WttrResult(response[0], response[1], response[2].trim());
}

(code on GitHub)

The highlighted line refers to a new value for your location in the config class. This is needed as IP geolocation won’t be accurate when the code is run on Azure, so add your location to Config.java:

public static final String WTTR_LOCATION = "Bath";

There is an inner class WttrResult, which is helpful for returning all the values needed in one go. As it is an immutable class I didn’t create any getters but made the fields public final and set them in the constructor.

Now, use that in DailyMessage:

Wttr.WttrResult wttr = new Wttr().getTodaysWeather();

return String.format(
   "%s Good Morning %s 🌡%s\n" +
   "It's %s %s",
   wttr.weather, wttr.weather, wttr.temperature,
   todaysDate, wttr.moonPhase
);

(code on GitHub)

Run this to see output like:

☁️ Good Morning ☁️ 🌡+10°C
It's Tuesday, October 08 🌘

It’s getting better 😊 The next step is to add some much-needed cheer to your early morning.

Starting on a High: Adding an Affirmation

First thing in the morning is a great time to motivate yourself. We’ll use the API at affirmations.dev to find a positive message. Because this API returns JSON, add the Jackson JSON parser to your pom.xml next to fluent-hc.

Create a class called Affirmation with the following method:

   public String getAffirmation() throws IOException {

       String affirmationJson = Request
           .Get(URI.create("https://www.affirmations.dev"))
           .execute()
           .returnContent().asString();

       String affirmationText = Config.MAPPER
           .readTree(affirmationJson)
           .get("affirmation")
           .asText();

       return String.format("🤔 Remember: %s 👍", affirmationText);
   }

(code on GitHub)

MAPPER is defined in the Config class. Use the Affirmation class in DailyMessage:

String affirmation = new Affirmation().getAffirmation();

return String.format(
   "%s Good Morning %s 🌡%s\n" +
   "It's %s %s\n" +
   "%s\n" +
   "🌈 Have a Great Day 🌈",
   wttr.weather, wttr.weather, wttr.temperature,
   todaysDate, wttr.moonPhase,
   affirmation
);

(code on GitHub)

The message is now complete, and looks like this:

⛅️ Good Morning ⛅️ 🌡+12°C
It's Tuesday, October 08 🌘
🤔 Remember: You know more than you think 👍
🌈 Have a Great Day 🌈

Tying it Together: Sending SMS

You will need a Twilio account and a phone number (if you don’t have one already, sign up for a free trial account).

Add a Maven dependency for the Twilio Helper Library, in the same place as fluent-hc and jackson-databind, and create a new class for sending SMS, called TwilioSMSSender:

public class TwilioSMSSender {

   static {
       Twilio.init(Config.TWILIO_ACCOUNT_SID, Config.TWILIO_AUTH_TOKEN);
   }

   public void sendMessage(String message) {
       Message.creator(
           new PhoneNumber(Config.MY_PHONE_NUMBER),
           new PhoneNumber(Config.TWILIO_PHONE_NUMBER),
           message)
       .create();
   }
}

(code on GitHub)

Again there are new Config values needed here. Your Account SID and Auth Token can be copied from the Twilio console.

Security reminder: Do not share your Account SID and Auth Token.

I have called the Twilio.init method in a static block because it’s possible that Azure will use the same Java process for multiple messages, and there’s no need to initialize the Twilio client more than once.

Change the main method in DailyMessage to test this out:

public static void main(String[] args) throws IOException {
   String todaysMessage = new DailyMessage().getTodaysMessage();
   System.out.println(todaysMessage);
   new TwilioSMSSender().sendMessage(todaysMessage);
}

(code on GitHub)

Run this again and you’ll see the message printed to your screen, and sent to your phone 📱🎉. The message is all done now, for the final part move on to using it in an Azure Function.

Run Your Code: Deploying as an Azure Function

Azure Functions for Java uses annotations to mark the entry points for the functions it will run. @FunctionName in the Function class marks an Azure Function. It’s time to make that function run on a timer and send a message, so change the run method to this:

@FunctionName("TimerTrigger-Java")
public void run(
   @TimerTrigger(name = "keepAliveTrigger", schedule = "0 30 7 * * 1-5") String timerInfo,
   final ExecutionContext context) throws IOException {

   String todaysMessage = new DailyMessage().getTodaysMessage();
   new TwilioSMSSender().sendMessage(todaysMessage);

   return;
}

(code on GitHub)

The schedule parameter there is an NCronTab expression, which means in this case that the function will be triggered at 7:30am UTC every weekday. You can adjust the time for your timezone, or use the WEBSITE_TIME_ZONE app setting.

You will need to delete (or fix) the unit test that we’ve been ignoring in src/test/java to make the project build after making these changes. Then, deploy your function to Azure with:

mvn clean package && mvn azure-functions:deploy

You may be asked to log in to Azure in your browser to complete this step, so you will need an Azure account. If you need to sign up you might need a credit card but the code here won’t trouble Azure’s free tier of 1 million executions per month. Once the function is deployed you can test it in the Azure Portal. Head to portal.azure.com then choose “Function App” from the menu on the left and choose your function from the list. Click the “Run” button to test:

Visual guide to Azure's portal. Choose "Function App" then "Functions" then "TimerTrigger-Java" then click the "Run" button.

All being well, your function will run, some log output will be shown at the bottom of the screen and you will get your SMS. The next step is to wait patiently until the TimerTrigger fires it again tomorrow morning.

Recap

Using fluent-hc and Jackson your code fetches data from wttr.in and affirmations.dev, creates a short friendly message and sends it to your phone. Azure Functions runs the code daily using a Timer Trigger. Not bad!

What Next?

  • You’ve probably thought of some useful additions to the daily message already. Add them and redeploy.
  • Check out Twilio’s MMS support if you want to start the day with a gif.
  • Learn how to handle incoming SMS so that you can reply to your daily message

I already find these messages to be very helpful - I’ve got ideas about extending them with data from the Google Calendar API as shown in this post and a random fact about the day from Wikipedia.

If you found this useful, I’d love to hear about it and see what you build - get in touch in the comments below or on Twitter I'm @MaximumGilliard