LoginSignup
13

More than 5 years have passed since last update.

Microsoft Bot Framework v3.0 からはじめる BOT 開発: 天気予報 BOT~カード表示で分かりやすく - Adaptive Cards

Last updated at Posted at 2017-08-04

最近 SNS でよく見かける 画像を利用した "カード" と呼ばれるフォーマット。Bot Framework で利用可能になった Adaptive Cards を使って、情報をグラフィカルに表示する方法を紹介します。
Livedoor 天気予報 Weather Hacks の天気予報 API を利用してデータを取得、日付と気温と共に天気アイコンを表示するカードを作成、表示する BOT を作成します。

手順

  1. 天気予報 BOT - Adaptive Card : 天気情報を取得して表示する (※このページ)
  2. 天気予報 BOT - Cognitive Services LUIS(1): 自然言語(文章)を解釈して、場所や日時を取得する
  3. 天気予報 BOT - Cognitive Services LUIS(2): 自然言語判定結果をBOTに組み込む

開発環境

Windows 10 + Visual Studio 2017 Enterprise, Bot Framework v3.8 (C#) で作成を行っています。

無償の Visual Studio 2017 Community or 2015 Community でOKなので、既存の環境がない場合は、ダウンロードしてインストールします。
Visual Studio 2017 Community ダウンロードサイト
Visual Studio 用の Bot Framework C# テンプレート
Bot Framework Channel Emulator (Windows版) ※Mac/Linux は Console版 をご利用ください

Bot Framework 開発環境の作り方は、Microsoft Bot Framework v3.0 からはじめる BOT 開発: Bot Framework を使うための開発環境 をご覧ください。

天気予報 BOT - Adaptive Card : 天気情報を取得して表示する

Visual Studio テンプレートから新規 BOT アプリケーションの作成~下準備

Visual Studio テンプレートから Bot アプリケーションの作成 と同じ手順で、Visual Studio テンプレートから Bot アプリケーションの作成 と同じプロセスで新規アプリケーションを作成します。

今回は WeatherBot という名称で作成しています。

ソリューションエクスプローラーからプロジェクトの配下にある [参照] を右クリックして [NuGet パッケージの管理] をクリックします。

上部バーに このソリューションに一部のNuGetパッケージが見つかりません... と表示される場合は、[復元] をクリックして、必要なパッケージをインストールします。(表示されない場合は次に進みます)

[参照] タブをクリックし、検索バーに adaptive cards と入力して検索、Microsoft.Adaptive.Cards をクリックします。すぐ右側に表示される詳細画面の [インストール] をクリックしてインストールします。

インストールの確認 が表示されたら [OK] 、ライセンスへの同意 は [同意する] をクリックして、インストールします。

天気予報データを格納するクラスを作成

個人の範囲で無料で利用できる Weather Hack の お天気 Webサービス を使うと、天気と気温の予報データがJSON 形式で取得できます。 (→サービス仕様)
[例] 横浜(cityid=140010)の天気予報を取得
http://weather.livedoor.com/forecast/webservice/json/v1?city=140010

このような JSON データを格納して利用しやすくするためのクラスを作成します。
プロジェクトを右クリックして [追加]>[新しいフォルダー] をクリック、Models というフォルダーを作成します。

作成した Models フォルダーを右クリックして、[追加]>[クラス]をクリックします。

名前に WeatherModel.cs と入力して、[追加] をクリックして新規クラスファイルを作成します。

WeatherModel.cs を開き、デフォルトで作成されている WeatherModel クラスを一旦削除します。

API のフォーマットを取得するため、ブラウザーを起動して API にアクセスを行います。
http://weather.livedoor.com/forecast/webservice/json/v1?city=140010

表示される結果をすべて選択して、コピーします。

Visual Studio に戻り、namespace .Models 内をクリックして選択してから、上部ツールバーから [編集]>[形式を選択して貼り付け]>[JSON をクラスとして貼り付ける] を選択して、コピーした内容を貼り付けます。

class Rootobjectclass WeatherModel に変更します。

適時ファイルの保存を行っておきます。

天気予報データの取得テスト

Dialogs フォルダ を開き、自動生成されている RootDialog.cs を編集します。

RootDialog.cs の冒頭に System.Net.Http, Newtonsoft.Json, Adaptive Card への参照を追加します。
上記で作成した WeatherModel を利用するために、Models フォルダー への参照も追加します。

RootDialog.cs
using System.Net.Http;
using Newtonsoft.Json;
using AdaptiveCards;
using WeatherBot.Models; // <projectname>.Models になります

ユーザーからメッセージが送信された時の動作を MessageReceivedAsync に記載していきます。
まず、MessageReceivedAsync 内のコードを下記に書き換えます。

RootDialog.cs
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var activity = await result as Activity;

    // 返答メッセージを作成
    var message = context.MakeMessage();
    // 天気を取得
    WeatherModel weather = await GetWeatherAsync();

    // 天気をメッセージにセット
    message.Text = $"今日の天気は {weather.forecasts[0].telop.ToString()} です";

    // 返答メッセージをPost
    await context.PostAsync(message);
    context.Wait(MessageReceivedAsync);
}

天気を取得する GetWeatherAsync を作成します。
下記のコードでは、横浜 (city=1400100) を指定して天気情報を取得しています。

RootDialog.cs
private async Task<WeatherModel> GetWeatherAsync()
{
    // API から天気情報を取得 (都市コード 140010 (横浜) の場合)
    var client = new HttpClient();
    var weatherResult = await client.GetStringAsync("http://weather.livedoor.com/forecast/webservice/json/v1?city=140010");

    // API 取得したデータをデコードして WeatherModel に取得
    weatherResult = Uri.UnescapeDataString(weatherResult);
    var weatherModel = JsonConvert.DeserializeObject<WeatherModel>(weatherResult);
    return weatherModel;
}

ここで一旦 BOT の動作確認を行います。、F5 または デバック>デバックの開始 をクリックして、プロジェクトのビルドおよび起動を行います。ブラウザが起動して Bot Framework のデフォルト画面が表示されたら、Bot Framework Channel Emulator を起動してアクセスを行います。
Bot Framework Channel Emulator の上部中央にある Bot Url に、起動しているブラウザと同じ URL (デフォルトでは http://localhost:xxxx) に /api/messages を追加したアドレス (http://localhost:xxxx/api/messages) を指定します。

何か文字を入力して送信すると、天気予報が返されるのを確認してください。

天気予報データを Adaptive Cards で表示する

(1) 単純なカードの作成

次に Adaptive Cards を使って、カード形式で情報を表示します。
MessageReceivedAsync を以下のように変更します。message.text の代わりに Card を作成、Attachment として返答にセットします。

RootDialog.cs
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var activity = await result as Activity;

    // 返答メッセージを作成
    var message = context.MakeMessage();
    // 天気を取得
    WeatherModel weather = await GetWeatherAsync();

    //// 天気をメッセージにセット
    //message.Text = $"今日の天気は {weather.forecasts[0].telop.ToString()} です";

    // 取得した天気情報をカードにセット
    var weatherCard = GetCard(weather);
    var attachment = new Attachment()
    {
        Content = weatherCard,
        ContentType = "application/vnd.microsoft.card.adaptive",
        Name = "Weather Forecast"
    };
    message.Attachments.Add(attachment);

    // 返答メッセージをPost
    await context.PostAsync(message);
    context.Wait(MessageReceivedAsync);
}

Card を作成する GetCard, その中で天気情報をセットする AddCurrentWeather を以下のように作成します。

RootDialog.cs
private static AdaptiveCard GetCard(WeatherModel model)
{
    var card = new AdaptiveCard();
    AddCurrentWeather(model, card);
    return card;
}

private static void AddCurrentWeather(WeatherModel model, AdaptiveCard card)
{
    // タイトル作成
    var titleColumnSet = new ColumnSet();
    card.Body.Add(titleColumnSet);

    var titleColumn = new Column();
    titleColumnSet.Columns.Add(titleColumn);

    var locationText = new TextBlock()
    {
        Text = $"{model.location.city} の天気",
        Size = TextSize.ExtraLarge,
        HorizontalAlignment = HorizontalAlignment.Center
    };
    titleColumn.Items.Add(locationText);

    // 本文作成
    // 天気情報をセット
    var mainColumnSet = new ColumnSet();
    card.Body.Add(mainColumnSet);

    var mainColumn = new Column();
    mainColumnSet.Columns.Add(mainColumn);

    var mainText = new TextBlock()
    {
        Text = $"{model.publicTime.Date.ToString("M/d")}",
        Size = TextSize.Large,
        HorizontalAlignment = HorizontalAlignment.Center
    };
    mainColumn.Items.Add(mainText);

    // 天気アイコンをセット
    var mainImage = new AdaptiveCards.Image();
    mainImage.Url = model.forecasts[0].image.url;
    mainImage.HorizontalAlignment = HorizontalAlignment.Center;
    mainColumn.Items.Add(mainImage);
}

再度 BOT の動作確認を行います。
何か文字を入力して送信すると、今度は天気予報がカードで返されるのを確認してください。

(2) 複数 Column を持ったカードの作成

今度は、Adaptive Cards で複数の Column を持つ形のカードを作成します。
何度か TextBlock と Image を追加する工程があるので、メソッドを作成して再利用できるようにします。
以下のように、RootDialog.cs に AddTextBlock と AddImage を追加します。

RootDialog.cs
private static void AddTextBlock(Column column, string text, TextSize size, HorizontalAlignment alignment)
{
    column.Items.Add(new TextBlock()
    {
        Text = text,
        Size = size,
        HorizontalAlignment = alignment
    });
}
private static void AddImage(Column column, string url, ImageSize size, HorizontalAlignment alignment)
{
    column.Items.Add(new AdaptiveCards.Image()
    {
        Url = url,
        Size = size,
        HorizontalAlignment = alignment
    });
}

RootDialog.cs に AddWeather を下記のように追加して、MessageReceivedAsync からの呼び出しを AddCurrentWeather → AddWeather に変更します。

RootDialog.cs

private static AdaptiveCard GetCard(WeatherModel model)
{
    var card = new AdaptiveCard();
    //AddCurrentWeather(model, card);
    AddWeather(model, card);
    return card;
}

    : (中略)

private static void AddWeather (WeatherModel model, AdaptiveCard card)
{
    // タイトル作成
    var titleColumnSet = new ColumnSet();
    card.Body.Add(titleColumnSet);

    var titleColumn = new Column();
    titleColumnSet.Columns.Add(titleColumn);
    AddTextBlock(titleColumn, $"{model.location.city} の天気", TextSize.ExtraLarge, HorizontalAlignment.Center);

    // 本文作成
    // 天気情報をセット
    var mainColumnSet = new ColumnSet();
    card.Body.Add(mainColumnSet);

    foreach (var item in model.forecasts)
    {
        var mainColumn = new Column();
        mainColumnSet.Columns.Add(mainColumn);

        // 天気データの取得と加工
        string day = item.dateLabel;
        string date = DateTime.Parse(item.date).Date.ToString("M/d");            

        // temperature が null の場合は "--" に変換
        string maxTemp, minTemp;
        try
        {
            maxTemp = item.temperature.max.celsius;
            minTemp = item.temperature.min.celsius;
        }
        catch
        {
            maxTemp = "--";
            minTemp = "--";
        }

        // データのセット
        AddTextBlock(mainColumn, $"{day}({ date})", TextSize.Large, HorizontalAlignment.Center);
        AddTextBlock(mainColumn, $"{maxTemp} / {minTemp} °C", TextSize.Medium, HorizontalAlignment.Center);
        AddImage(mainColumn, item.image.url, ImageSize.Medium, HorizontalAlignment.Center);
    }

}

再度 BOT の動作確認を行います。
何か文字を入力して送信すると、3日間の天気予報がカードで返されるのを確認してください。

Appendix

ご参考まで、ここまで作成したアプリはこちらからダウンロードできます
Bot Framework × Adaptive Cards: WeatherBot アプリサンプル

Next Step

次回以降の手順で、Cognitive Services LUIS を用いて、自然言語を判断して回答する BOT に仕立てていきます。
手順 3 まで行って作成できる 天気予報BOT↓

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13