特集
» 2018年04月03日 05時00分 公開

特集:はじめてのAlexaスキル開発:Alexa×Slack連携! アカウントリンクを使うスキルをC#で実装 (1/3)

Alexaと他のWebサービスをつなぐ「アカウントリンク」機能を使って、Alexaに話しかけた内容をSlackへと投稿するスキルを作成してみよう。

[かわさきしんじ,Insider.NET編集部]
「特集:はじめてのAlexaスキル開発」のインデックス

「特集:はじめてのAlexaスキル開発」

 Alexaが持つ「アカウントリンク」機能を使うと、Alexaから特定のサービスに特定のユーザーとしてアクセスできる。この機能を使って、「原稿を書くよ」とAlexaに伝えると、その本心をSlackに投稿してくれるスキルを作ってみよう。実際にこのスキルが動作している様子を以下に示す。

「原稿書くよ」とAlexaに伝えると、「そんな気がないこと」をSlackでバラしてくれる恐ろしいスキル


 なお、アカウントリンクではOAuth 2.0が使われるが、本稿では詳しい説明は省略する。OAuthを利用したアカウントリンクの詳細については「Alexaユーザーとシステムユーザーを関連付ける」ページなどを、OAuthの概要については「『OAuth』の基本動作を知る」や「図解:OAuth 2.0に潜む『5つの脆弱性』と解決法」などを参照されたい。

今回作成したスキルとその対話モデル

 上でご覧いただいたように、今回作成した「PostTelltaleFromAlexaToSlack」スキルは筆者の「ぼやき」や「やる気マンマンな発言」を、Alexaが全てネガティブな内容としてSlackに書き込んでくれるというものだ。ここでは、以下の3種類のインテントを作成し、ユーザー(筆者)がAlexaにしゃべった内容に応じて、Alexaの対応(Slackに書き込むメッセージの内容や、ユーザーに返送するメッセージ)に変化を付けている。

  • WantNotToWriteIntent
  • WantNotToWorkIntent
  • WantToBeAbsentIntent

 「原稿を書きたくない」系の発言ならWantNotToWriteIntentが、「仕事がいや」系の発言ならWantNotToWorkIntentが、「休みたい」系の発言ならWantToBeAbsentIntentが起動される。さらにちゃんと「原稿書くぞ」とAlexaに伝えてもやはり「WantNotToWriteIntent」が起動されるようにしてある。例として、「WantNotToWriteIntent」インテントのサンプル発話(の一部)を以下に示す。ここではスキルの「呼び出し名」を「スラック」にしている。

WantNoToWriteIntentを起動するサンプル発話 WantNoToWriteIntentを起動するサンプル発話
「スラックで原稿を書きたくないよと伝えて」とAlexaに話しかけても、「スラックで原稿書くぞと伝えて」とAlexaに話しかけても、どのみち、WantNotToWriteIntentが起動され(て、原稿を書きたくないことにされ)てしまう。

 他の2つのインテントも同様だ(「スラックで仕事をしたくないよと伝えて」といえば、WantNotToWorkIntentインテントが起動されるといった具合)。

C#コード

 Slackへ実際に投稿するAWS Lambda関数のコードは次のようになる(AWS Toolkit for Visual Studioを使用して、Lambdaプロジェクトを新規に作成し、Alexa.NET NuGetパッケージをインストールしている。具体的な手順については前々回および前回を参照されたい)。

public class Function
{
  SkillResponse response = new SkillResponse
  {
    Version = "1.0",
    Response = new ResponseBody
    {
      ShouldEndSession = true
    }
  };

  public SkillResponse FunctionHandler(
    SkillRequest input, ILambdaContext context)
  {
    var token = input.Session.User.AccessToken;
    if (token == null)
      return ComposeMessage("認証が必要です");

    if (input.Request.Type == "LaunchRequest")
      return ComposeMessage("何でもSlackに投稿するよ");

    IntentRequest ir = input.Request as IntentRequest;

    if (ir == null)
      return null;

    var name = ir.Intent.Name;
    string text = "";
    switch (name)
    {
      case "AMAZON.CancelIntent":
      case "AMAZON.StopIntent":
        text = "終了します";
        return ComposeMessage(text);
      case "WantNotToWorkIntent":
        text = "この人、仕事したくないとかゆーてますよ(by Alexa)";
        break;
      case "WantNotToWriteIntent":
        text = "この人、原稿書く気ありませんわ(by Alexa)";
        break;
      case "WantToBeAbsentIntent":
        text = "この人、仮病で会社休もうとしてますよ!(by Alexa)";
        break;
      default:
        text = "この人、ダメな人ですわ(by Alexa)";
        break;
    }

    var result = Post2Slack(text, input.Session.User.AccessToken);
    if (result.IsSuccessStatusCode)
    {
      return ComposeMessage($"Slackに「{text}」て告げ口しときましたわ");
    }
    else
    {
      return ComposeMessage("Slackすら相手にしてくれませんわ");
    }
  }

  private SkillResponse ComposeMessage(string text)
  {
    response.Response.OutputSpeech = new PlainTextOutputSpeech
    {
      Text = text
    };
    return response;
  }

  private HttpResponseMessage Post2Slack(string msg, string token)
  {
    var client = new HttpClient();
    var content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
      { "channel", "#general" },
      { "token", token },
      { "text", msg }
    });
    var task = client.PostAsync(
      "https://slack.com/api/chat.postMessage", content);
    task.Wait();
    var result = task.Result;
    return result;
  }
}

今回のLambda関数の実装コード

 Alexaスキルから呼び出されるFunctionHandler関数では、だいたい次のようなことを行っている。

  • アカウントリンク機能によりAlexaとSlackの連携が確立されているかの確認
  • ローンチリクエストやスキルをストップ/キャンセルするインテントの処理
  • 上で述べた3種類のインテントに合わせてメッセージを作成
  • 作成したメッセージをSlackへ投稿
  • 投稿結果に応じて、ユーザーにメッセージを返送

 アカウントリンクによって、Alexaとの連携が確立されたサービスからはユーザーがそのサービスにアクセスするためのトークンが渡される。これはFunctionHandler関数に渡されるSkillResponseオブジェクトのSession.User.AccessTokenプロパティに保存されるので、その値を見れば、アカウントリンクが行われているかを判断できる。

var token = input.Session.User.AccessToken;
if (token == null)
  return ComposeMessage("認証が必要です");

アクセストークンの有無でアカウントリンクが設定されているかを確認

 また、このプロパティの値は後続のコード(ここではPost2Slackメソッド)で実際にサービスを利用する際に使用される。ここではHttpClientクラスのPostAsyncメソッドを使用して、Slackに接続したユーザーが所属するチームの「#general」チャネルに投稿するようにしている(サンプルコードということもあり、手を抜いて、非同期呼び出しを待機し、Slackへのポストが成功したかどうかの結果を得ている)。

private HttpResponseMessage Post2Slack(string msg, string token)
{
  var client = new HttpClient();
  var content = new FormUrlEncodedContent(new Dictionary<string, string>
  {
    { "channel", "#general" },
    { "token", token },
    { "text", msg }
  });
  var task = client.PostAsync(
    "https://slack.com/api/chat.postMessage", content);
  task.Wait();
  var result = task.Result;
  return result;
}

Slackでユーザーが所属しているチームの「#general」チャネルへ投稿するコード
content変数では上で得たアクセストークンと実際に投稿するメッセージを利用している。

 残りのコードについては、深く説明する必要はないだろう。送られてきたリクエストがLaunchRequestであればウエルカムメッセージを表示している。その後、リクエストからインテントを取り出して、その名前に応じてメッセージを作成し(て、上記のPost2Slackメソッドを呼び出し)ているだけだ(あるいは、「ストップ」「キャンセル」などについては、それを処理するようにしているだけだ)。

 コードについてはこれくらいにして、次にアカウントリンクの設定について見ていこう。

       1|2|3 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。