Visual Studio+C#でAlexaカスタムスキル1 Twitterにあいさつしてみた


やったこと

1か月以上待ってようやく届いたAmazon Echoを使ってTwitterで挨拶してみました。

IFTTTを使えばスキルを作らなくても可能なのかもしれませんが、 スキルを作る事が目的なのでカスタムスキルで実装します。

チュートリアルやサンプルではLambdaメソッドはNode.jsを使ったものが多いのですが、 C# LoveなのでC#で書いてみました。

環境

実装に必要な環境は以下になります。 なおC#は.NET Core1.0を使うためMacでも開発はできると思います。 VS(Windows版)は2017でやりましたが、AWS Toolkitは2017より前のバージョンにも対応しているっぽいです。

Alexaのスキルの実装

ほぼチュートリアル通りに進みます (このチュートリアル日本語で書かれててめちゃ分かりやすかったです。)

事前に Amazon Developerのアカウント登録が必要です。

なお今回はスキルビルダーは使っていません。

スキル名 & 呼び出し名

ともに「ついったー」としました。

インテントスキーマ

TwitterIntentというインテントで、WordというスロットをGREETING_WORD型で定義

{
  "intents": [
    {
      "slots": [
        {
          "name": "Word",
          "type": "GREETING_WORD"
        }
      ],
      "intent": "TwitterIntent"
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    }
  ]
}

カスタムスロットタイプ

スロットには挨拶の言葉が入るようにします。 ざっと見た感じデフォルトのスロットタイプには該当しないように思えたので カスタムスロットにしました。

タイプ:GREETING_WORD

値: おはよう こんにちは おやすみ こんばんは やあ へい はーい

サンプル発話

本当はもっとたくさんのパターンを登録するんでしょうが、 とりあえず以下の5通り

TwitterIntent {Word} をつぶやいて TwitterIntent {Word} ってつぶやいて TwitterIntent {Word} ってやって TwitterIntent {Word} て言って TwitterIntent {Word} して

Lambda関数の実装(C#)

事前にAWSのアカウント登録を行ってLambda関数を利用可能にしておきます。

AWS Toolkit for Visual Studioのインストール

AWSのサイトからダウンロードしてインストールしておきます。 これがあるとVisual Studioのみで関数の実装からPublish、テストまで行うことができるので楽です。

インストール後Visual Studioを立ち上げるとAWSのユーザー情報を入力するウィンドウが立ち上がったので入力しておきます。

Lambda用のプロジェクト作成

プロジェクトの作成から「AWS Lambda Project(.NET Core)」を選択します。 FunctionのタイプはEmpty Functionにしました。

ライブラリの追加

NuGetから以下の2つを追加しました。

  • Alexa.Net
  • CoreTweet

関数の実装

Function.csを開いて以下のように実装しました。

TwitterAPIの情報

とりあえずの確認なのでハードコーディングしようかと思いましたが、 Lambdaの環境変数を利用すると値は暗号化されるらしいので、 そちらの機能を利用しました。 ※Lambdaあんまり詳しくないので間違っていたら指摘してください。

   private string APIKey;
   private string APISecret;
   private string AccessToken;
   private string AccessTokenSecret;
        
   public Function()
   {
       APIKey = Environment.GetEnvironmentVariable("API_KEY");
       APISecret = Environment.GetEnvironmentVariable("API_KEY_SECRET");
       AccessToken = Environment.GetEnvironmentVariable("ACCESS_TOKEN");
       AccessTokenSecret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET");
   }

使用するリクエストの仕分け

今回はTwitterIntent以外のリクエストは全て無視する方向で実装しました。

   // リクエストタイプを取得
   var requestType = input.GetRequestType();

   // インテントリクエスト以外は無視
   if (requestType != typeof(IntentRequest)) return null;

   var intentRequest = input.Request as IntentRequest;

   // TwitterIntetn以外は無視
   if (!intentRequest.Intent.Name.Equals("TwitterIntent")) return null;

応答の実装

リクエストから挨拶文言を抜き取ってTwitterにつぶやいた後、つぶやいた事を発話しています。

   // Wordスロットの値を取得
    var wordSlotValue = intentRequest.Intent.Slots["Word"].Value;

   // Twitter APIの必要情報を生成
   var tokens = CoreTweet.Tokens.Create($"{APIKey}", $"{APISecret}", $"{AccessToken}", $"{AccessTokenSecret}");

   // つぶやき実施
   tokens.Statuses.UpdateAsync(new { status = wordSlotValue }).Wait();

   // Axexaから応答
   return ResponseBuilder.Tell($"<speak>{wordSlotValue}とつぶやきました</speak>");

Function.cs全体

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Amazon.Lambda.Core;
using Alexa.NET;
using Alexa.NET.Request;
using Alexa.NET.Response;
using Alexa.NET.Request.Type;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace AlexaToTwitter
{
    public class Function
    {
        private readonly string APIKey;
        private readonly string APISecret;
        private readonly string AccessToken;
        private readonly string AccessTokenSecret;

        public Function()
        {
            APIKey = Environment.GetEnvironmentVariable("API_KEY");
            APISecret = Environment.GetEnvironmentVariable("API_KEY_SECRET");
            AccessToken = Environment.GetEnvironmentVariable("ACCESS_TOKEN");
            AccessTokenSecret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET");
        }

        public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            // リクエストタイプを取得
            var requestType = input.GetRequestType();

            // インテントリクエスト以外は無視
            if (requestType != typeof(IntentRequest)) return null;

            var intentRequest = input.Request as IntentRequest;

            // TwitterIntetn以外は無視
            if (!intentRequest.Intent.Name.Equals("TwitterIntent")) return null;

            // Wordスロットの値を取得
            var wordSlotValue = intentRequest.Intent.Slots["Word"].Value;

            // Twitter APIの必要情報を生成
            var tokens = CoreTweet.Tokens.Create($"{APIKey}", $"{APISecret}", $"{AccessToken}", $"{AccessTokenSecret}");

            // つぶやき実施
            tokens.Statuses.UpdateAsync(new { status = wordSlotValue }).Wait();

            // Axexaから応答
            return ResponseBuilder.Tell($"{wordSlotValue}とつぶやきました");
        }
    }
}

Lambdaへのアップロード

プロジェクトを右クリック→「Publish to AWS Lambda…」をクリック。

Functin NameやRoleを選択してアップロード。 ここで環境変数の値のセットや、Publish後のFunctionのテストもできます。

テストして問題なかったら、AWSのWebからトリガーに「Alexa Skills Kit」を追加しておきます。 (この設定もVisual Studioからできたらもっと楽だと思った思ったのですが、やり方がわかりませんでした)

実機での確認

Amazon DeveloperのAlexaスキルの設定でLambdaとの接続を行った後、 必要事項を埋めて、Skills Beta Testingから自分のAmazonアカウントでスキルを動かせるようにします。

そして、ドキドキしながらEchoに向かって 「あれくさ、ついったーでおはようってつぶやいて」 って話したところ、ちゃんとTwitter上に反映されてました。

“おはよう"の部分を他の言葉にしてもだいたい認識してくれます。 またリストに定義しなかった言葉でも聞き取れることもありました。

まとめ

Amazon Echoを使って何かスキルを作りたいと思い、使い慣れたC#を使って実装してみました。

AWS Toolkit for Visual StudioとAlexa.NetのおかげでほとんどLambdaやAlexaの事は意識しなくてもコーディングでき、思ったよりはハードルが低かったです。

Alexaの認識率も結構よいので、一時期Amazon Dush Buttonを使って色々日常を自動化させようと思ったことがありましたが、Alexaでもアイデア次第で色々できるような気がしてます。


See also