最近 SNS でよく見かける 画像を利用した "カード" と呼ばれるフォーマット。Bot Framework で利用可能になった Adaptive Cards を使って、情報をグラフィカルに表示する方法を紹介します。
Livedoor 天気予報 Weather Hacks の天気予報 API を利用してデータを取得、日付と気温と共に天気アイコンを表示するカードを作成、表示する BOT を作成します。
##手順
- 天気予報 BOT - Adaptive Card : 天気情報を取得して表示する (※このページ)
- 天気予報 BOT - Cognitive Services LUIS(1): 自然言語(文章)を解釈して、場所や日時を取得する
- 天気予報 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 アプリケーションの作成 と同じプロセスで新規アプリケーションを作成します。
ソリューションエクスプローラーからプロジェクトの配下にある [参照] を右クリックして [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 Rootobject は class WeatherModel に変更します。
適時ファイルの保存を行っておきます。
天気予報データの取得テスト
Dialogs フォルダ を開き、自動生成されている RootDialog.cs を編集します。
RootDialog.cs の冒頭に System.Net.Http, Newtonsoft.Json, Adaptive Card への参照を追加します。
上記で作成した WeatherModel を利用するために、Models フォルダー への参照も追加します。
using System.Net.Http;
using Newtonsoft.Json;
using AdaptiveCards;
using WeatherBot.Models; // <projectname>.Models になります
ユーザーからメッセージが送信された時の動作を MessageReceivedAsync に記載していきます。
まず、MessageReceivedAsync 内のコードを下記に書き換えます。
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) を指定して天気情報を取得しています。
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 として返答にセットします。
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 を以下のように作成します。
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 を追加します。
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 に変更します。
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↓