Edited at

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

More than 1 year has passed since last update.

最近 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↓