Google HomeにRSSを読み上げさせよう:準備編:Google Homeプログラミングを始めよう(2/2 ページ)
Google HomeにRSSフィードを読み上げてもらえるとしたらどうだろう。今回はそのための前準備としてAzureの機能を幾つか使ってみよう。
Functionsアプリの実装
先ほど取りあえず作成した関数のままでは、テーブルストレージにアクセスができない。そのため、「バインディング」と呼ばれる設定を行う必要がある。これには、Functionsアプリの設定画面を表示したら、以下の(1)([統合])をクリックしてから、テーブルストレージを使うように設定する。
そして[出力]の下にある(2)の[新しい出力]を選択すると、出力先候補が表示されるので、(3)の[Azure Table Storage]を選択する。ページ最下部にある[選択]ボタンをクリックすると、次のようにテーブルストレージと関数とをバインディングするための設定項目が表示される。
ここでは[Table parameter name]と[Table name]を「rsstable」とした。前者は関数コードでテーブルを参照する際の名前、後者はテーブルストレージに作成されるテーブルの名前となる。[保存]ボタンをクリックすると、設定が「function.json」ファイルに保存される。
なお、本稿ではテーブルストレージへの保存ができればよいので、(手抜きをして)[出力]から[HTTP($return)]を削除した。これには該当項目を[出力]の下で選択し、画面下部で[削除]リンクをクリックすればよい(画像は割愛)。「$return」というのは、関数の戻り値のことで、これを利用しないということは関数も値を返さないということになる。
では、コードを記述していこう。デフォルトの関数は次のようなシグネチャを持っている。
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
…… 省略 ……
}
まず、上でも述べた通り、ここでは戻り値を返さない。また、このシグネチャには出力に使用するテーブルストレージも含まれていない(戻り値以外の出力先は、シグネチャに指定する必要がある)。そのため、関数は次のようになる。
#r "Newtonsoft.Json"
using System.Net;
using Newtonsoft.Json;
public static async Task Run(HttpRequestMessage req,
ICollector<TableItem> rssTable,
TraceWriter log)
{
…… 省略 ……
}
public class TableItem
{
…… 省略 ……
}
先ほども述べたようにこのFunctionsアプリは値を返さない。そして非同期関数なので、戻り値型は「void」ではなくTaskとなる。また、出力先のテーブル(rssTable)にはICollector<T>インタフェースを介してアクセスする(フィードの内容はTableItemクラスとしてモデル化してあり、これを実際には保存する)。
第1パラメーターの「req」には、Logicアプリからデータが送られてくる。このデータはJSON形式のデータなので、これをデシリアライズして、文字列値としてテーブルストレージに保存するのが、この関数で行うことだ。実際のコードは次のようになる。
#r "Newtonsoft.Json"
using System.Net;
using Newtonsoft.Json;
public static async Task Run(HttpRequestMessage req,
ICollector<TableItem> rssTable,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
dynamic data = await req.Content.ReadAsStringAsync();
var jsondata = JsonConvert.DeserializeObject<TableItem>(data as string);
string partitionKey = "insider.net rss feed item";
if (!jsondata.title.Contains("AD:")) {
int index = jsondata.summary.IndexOf('\n');
jsondata.summary = jsondata.summary.Substring(3, index - 3);
jsondata.PartitionKey = partitionKey;
index = jsondata.primaryLink.IndexOf('=');
jsondata.RowKey = jsondata.primaryLink.Substring(
index + 1, jsondata.primaryLink.Length - index - 1);
log.Info($"index: {index}, key: {jsondata.RowKey}");
rssTable.Add(jsondata);
}
}
public class TableItem
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string id { get; set; }
public string title { get; set; }
public string primaryLink { get; set; }
public string updateOn { get; set; }
public string publishDate { get; set; }
public string summary { get; set; }
}
先にも述べた通り、個々のフィードをモデル化したのがTableItemクラスであり、デシリアライズしたデータはこのクラスのインスタンスに保存される(RSSフィードの要素の中で必要と筆者が判断したものだけをメンバとしている)。
ただし、通常のRSSフィードのデータに加えて、TableItemクラスにはPartitionKey/RowKeyという2つのプロパティがある。これら2つのプロパティはテーブルストレージへのデータの保存に必須の要素であり、PartitionKeyプロパティが同一のデータはテーブル内の同一パーティション(連続領域)に保存される。RowKeyプロパティは、パーティション内で個々の行を識別するためのキーとなる。実際にはこれら2つのプロパティにより、テーブルストレージ内のデータが一意に識別される。
ここではPartitionKeyプロパティの値は「insider.net rss feed item」に統一してある。また、RowKeyプロパティの値は得られたRSSフィードに含まれるprimaryLink値を加工したものとしている。
if文があるのは、RSSフィードに宣伝が含まれているためだ。「AD:」が含まれているフィードについてはテーブルストレージに保存しないようにしている。また、summaryプロパティにはHTMLコードが含まれていたので、ピュアテキストだけを抜き出すようにしている。
なお、ここでは記事ごとのフィードが連続してreqパラメーターに送られてくることを想定している。全てのフィードがまとめて送られてくる場合には、自分でループを廻すなどして、個々の記事を保存する必要がある。
などと書いているのは、ここまで書いて実際にフィードを保存できるかを試そうとしたが、諸般の都合で(恐らく動作確認のタイミングではRSSフィードが新規に発行されていないと判断されたため)動作を確認できなかったためだ。そこで、取りあえず、以下のようなLogicアプリを作成して、この関数を呼び出すようにした。先に作成したLogicアプリの動作については次回、検証した結果をお知らせしたいと思う。
これは上で利用している「トリガーとしてのRSSコネクター」ではなく、「アクションとしてのRSSコネクター」を利用して、本フォーラムのRSSの一覧を取得して、それを上で見た関数に送り込もうというものだ。RSSコネクターをアクションに使っていること以外は、このLogicアプリの内容は先ほどと同様だ。一番上の[HTTP 要求の受信時]というトリガーは作成しただけで、何も設定していない。
このLogicアプリを作成し、画面左上にある[実行]ボタンをクリックすると、RSSが取得され、個々のフィードが関数へと送られる。Functionsアプリ側での実行結果を以下に示す。
Azure Storage Explorerで該当のテーブル(rssTable)を確認した結果が以下だ。
なお、Dialogflowの側から呼び出すと、(決め打ちで)RSSのフィードデータをテーブルストレージから取得して、それを返送してくれる関数も作成した。細かく説明しないが、ここでは、先ほど作成したrssTableテーブルストレージを入力に使うように、バインディングの設定を行っている。DialogflowアプリからFunctionsアプリを呼び出す手順などについては前回を参照されたい。
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
IQueryable<TableItem> inputTable,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string text = "ありません";
var item = inputTable.Where(
x => x.title == "C# 7.2").ToList().FirstOrDefault();
if (item != null)
{
text = $"{item.title}の概要は次の通りです: {item.summary}";
log.Info(text);
}
var result = req.CreateResponse(HttpStatusCode.OK, new {
speech = text,
displayText = text
});
result.Headers.Add("ContentType", "application/json");
return result;
}
public class TableItem : TableEntity
{
public string id { get; set; }
public string title { get; set; }
public string primaryLink { get; set; }
public string updateOn { get; set; }
public string publishDate { get; set; }
public string summary { get; set; }
}
適当にDialogflowアプリを作成して、シミュレーターからこのFunctionsアプリを呼び出したところを以下に示す。
決め打ちのデータとなっているが、Functionsアプリがテーブルストレージから記事の概要を取得して返送してくれていることが分かる(実機のGoogle Home Miniでも動作を確認した)。
今回は、Google HomeにRSSフィードを読み上げさせるための準備として、LogicアプリとFunctionsアプリを組み合わせて、RSSフィードを取得し、それをテーブルストレージへ保存する方法を説明した(上述したように、実際にはまだ動作確認が必要だ)。次回はこのようにして得られたRSSフィードをちゃんとGoogle Homeが読み上げてくれるようにしてみよう。
Copyright© Digital Advantage Corp. All Rights Reserved.