Create a chatbot that tells you the weather forecast for any city in under 100 LoC
Tags: chatbot, weather, api, openweather
Published: 2020-10-15 13:12:48

Combining chatbot platforms with open data sources is one of the most powerful ways to easily create chat interfaces that provide useful information to its users. This is what we call <a href="https://xatkit.com/chatbots-open-data-public-administration-citizen-development/" target="_blank" rel="noopener noreferrer">Open Data chatbots.</a>

In particular, today, we show how you can create a chatbot with <a href="https://xatkit.com/" target="_blank" rel="noopener noreferrer">Xatkit</a> that queries <a href="https://openweathermap.org/" target="_blank" rel="noopener noreferrer">OpenWeather</a> to give you the weather forecast in any city in the world. All this in less than 100 lines of code. You have the full code of the bot in our <a href="https://github.com/xatkit-bot-platform/xatkit-examples" target="_blank" rel="noopener noreferrer">examples repository</a> but we'll see in this post all its key components.
<h2>Declaring the weather forecast intent</h2>

The first step is defining the <em>asking for weather forecast</em> intent. This is how you do it in Xatkit

```java
        val howIsTheWeather = intent("HowIsTheWeather")
                .trainingSentence("How is the weather today in CITY?")
                .trainingSentence("What is the forecast for today in CITY?")
                .parameter("cityName").fromFragment("CITY").entity(city());
```

Note how as part of the intent we collect the name of the desired city. Also to help the NLP Engine, we declare that the City name should be part of the predefined <em>city</em> entity, comprising the list of all (major) cities in the world. Xatkit maps this to the corresponding city entity available in all the popular Intent Recognition Providers. So, make sure you configure the bot to be used with <a href="https://github.com/xatkit-bot-platform/xatkit/wiki/Integrating-DialogFlow" rel="noopener noreferrer" target="_blank">DialogFlow</a> or <a href="https://github.com/xatkit-bot-platform/xatkit/wiki/Using-NLP.js" rel="noopener noreferrer" target="_blank">Nlp.js</a> (and remember that your choice of NLP Engine does not affect the bot code, just its <a href="https://github.com/xatkit-bot-platform/xatkit/wiki/Xatkit-Options" rel="noopener noreferrer" target="_blank">property file</a>). 

<h2>Reacting to the weather forecast request</h2>
Once the intent has been matched, the bot must call the REST API of OpenWeather passing the requested city as parameter. 

We first wait for the intent to match in an initial state for the bot

```java
      awaitingInput
                .next()
                .when(intentIs(howIsTheWeather)).moveTo(printWeather);
```

And once the match takes place the transition to the <code> printWeather</code> state to actually process the request. See the full code of this state below.

Remember that Xatkit comes with a <a href="https://github.com/xatkit-bot-platform/xatkit-rest-platform" rel="noopener noreferrer" target="_blank">REST platform abstraction</a> that you can use to simplify the query and response processing of any external REST API. Thus, <code>getJsonRequest</code> helps you build the exact URL request to send to the API and colects the answer in the <code>response</code> object you can then use to check for the success status of the query and the easy access to the returned information.

If successful, from this response object, we build the reply to print back to the user. And go back to the initial state in case the user wants to ask for another city.

```java
     printWeather
                .body(context -> {
                    String cityName = (String) context.getIntent().getValue("cityName");
                    Map<String, Object> queryParameters = new HashMap<>();
                    queryParameters.put("q", cityName);
                    ApiResponse<JsonElement> response = restPlatform.getJsonRequest(context, "http://api" +
                                    ".openweathermap.org/data/2.5/weather", queryParameters, Collections.emptyMap(),
                            Collections.emptyMap());
                    if (response.getStatus() == 200) {
                        long temp = Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                "temp").getAsDouble());
                        long tempMin =
                                Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                        "temp_min").getAsDouble());
                        long tempMax =
                                Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                        "temp_max").getAsDouble());
                        String weather =
                                response.getBody().getAsJsonObject().get("weather").getAsJsonArray().get(0).getAsJsonObject().get("description").getAsString();
                        String weatherIcon =
                                "http://openweathermap.org/img/wn/" + response.getBody().getAsJsonObject().get(
                                        "weather").getAsJsonArray().get(0).getAsJsonObject().get("icon").getAsString() + ".png";
                        reactPlatform.reply(context, MessageFormat.format("The current weather is {0} &deg;C with " +
                                        "{1} ![{1}]({2}) with a high of {3} &deg;C and a low of {4} &deg;C", temp,
                                weather,
                                weatherIcon, tempMax, tempMin));
                    } else if (response.getStatus() == 400) {
                        reactPlatform.reply(context, "Oops, I couldn't find this city");
                    } else {
                        reactPlatform.reply(context, "Sorry, an error " +  response.getStatus() + " " + response.getStatusText() + " occurred when accessing the openweathermap service");
                    }

                })
                .next()
                .moveTo(awaitingInput);

```


<h2>The weather forecast chatbot</h2>

You can see the resulting bot in action here. 

<img class="aligncenter  wp-image-119305" src="https://xatkit.com/wp-content/uploads/2020/10/weather-bot.gif" alt="weather bot in action" width="452" height="786" />

Note that the bot is able to understand the user questions even if they are not an exact match of the training sentences as we using our DialogFlow connector as NLP provider in this case. Note that the bot is also giving you a nice reason to come live in Barcelona :-).

Obviously, this is a minimal example you could build on to expand the conversation or provide additional information. You have the full power of <a href="https://xatkit.com/fluent-interface-building-chatbots-bots/">our Fluent API and state machine semantics</a> backing you up!.

<em>Photo by <a href="https://unsplash.com/@wimvanteinde?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Wim van 't Einde</a> on <a href="https://unsplash.com/s/photos/weather?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></em>