【AWS re:Invent 2016】 Amazon Echo Dotでブログ投稿数を読み上げるcustom skillの作成 ③ Lambda function作成編

Amazon Echo Dotにて弊社ブログの投稿数を取得するAmazon Echo(dot)用のcustom skillを作成します。

  1. 開封の義
  2. Amazon Echo Dot初期設定編
  3. Amazon DeveloperConsoleを有効化
  4. Lambda function作成 <- 今回はこちら
  5. Amazon DeveloperConsoleよりAlexa Skillを登録

※Amazon EchoやAmazon Tap, Echo dotは日本の技適を通っていませんので、日本国内での利用は行なえません、ご注意下さい

今回の記事では、custom skill作成例として弊社ブログ記事の投稿数を取得 -> 発話を行います。
Amazonの提供しているサンプルコードをベースとした、わかりやすい動作概要解説につきまして最下段に記載のリンク先を参考にさせて頂きました。

対象として利用出来るブログシステム

WordPressにJSON API pluginがインストール済みで、Core(get系の機能)が有効化されているもの
(何らかのJsonを返してくれるAPIであれば比較的簡単な修正で対応出来ると思います)

※作成を進める上で警告が出ますが、リージョンをeu-west1 もしくは us-east1(バージニア北部) にて実施する必要があります。

新規Lambda functionの作成

Select blueprint

AWSマネジメントコンソールにログイン後、Lambdaを選択して下さい。
blueprintとして 「alexa-skills-kit-color-expert-python」を選択しますが、数が多いため Filter に 「alexa」と入力すると選択が楽です。

01-create_new_function

Configure triggers

Alexa Skills Kit を選択します。
02-config_triggers

ここで選択後、下記のような画面となる場合はリージョンを再設定し初めから実行して下さい。
02-config_triggers_warn

Configure function

こちらでコードと動作設定を行います、コードは下段に記載しております。

入力項目

  • Name, Description : 任意の名前を入力
  • Lambda function code : 最下段に記載のコードを入力
  • Lambda funtcion handler role : 既に存在する lambda_basic_execution を指定します
    下記に記載しておりますが、存在しない場合「Create a custom role」を選択するとIAM管理画面が開き、作成を半自動で行えます。
  • Timeout : APIリクエストの返却される時間に余裕を持った秒数を選択します、こちらでは10秒を指定しています。
03-config_function-_create_iam

IAMロールの半自動生成

Create a custom role を選択後、自動で開かれたIAM管理画面にて許可を押すとRoleが生成されます。

10-create_iam_policy

Lambdaコード

ほぼblueprintですが、追加・変更・削除した部分は下記となります。
ロジックについては、外部URLを叩き取得したJSONをパースしているのみです。

  • 削除
    もともと持っているサンプルとしての機能、関数部分を削除
  • 変更
    on_intent 中の intent_name を変更
  • 追加
    先頭部分 DATE / URLを追加
    get_blog_count() 関数を追加

コード全体

※URI部分を自身のWordpress+JSON API導入済みのWebサイト等に変更しなければ動作しません。

"""
return BlogCount from WordPress (using JSON API plugin)

Based on http://amzn.to/1LzFrj6
"""

from __future__ import print_function
from urllib2 import urlopen
import json
import time

DATE = time.strftime("%Y%m%d")
URI  = 'https://URL_OF_YOUR_BLOG/?json=get_date_posts&date=' + DATE

# --------------- Helpers that build all of the responses ----------------------
def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }


def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }


# --------------- Functions that control the skill's behavior ------------------

def get_welcome_response():
    """ If we wanted to initialize the session to have some attributes we could
    add those here
    """

    session_attributes = {}
    card_title = "Blog articles counter"
    speech_output = "Please tell me How many blog posted?"

    # If the user either does not reply to the welcome message or says something
    # that is not understood, they will be prompted again with this text.
    reprompt_text = "Please tell me How many blog posted?"

    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))


def handle_session_end_request():
    card_title = "Session Ended"
    speech_output = "Thank you for trying Blog articles counter!"

    # Setting this to true ends the session and exits the skill.
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

def get_blog_count():
    card_title = "Blog articles counter"
    session_attributes = {}

    result = json.load(urlopen(URI))
    speech_output = str(result["count_total"]) + "Blog posts Today"

    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

# --------------- Events ------------------

def on_session_started(session_started_request, session):
    """ Called when the session starts """

    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])


def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """

    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])

def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # Dispatch to your skill's intent handlers
    if intent_name == "HowManyBlogsIntent":
        return get_blog_count()
    else:
        raise ValueError("Invalid intent")


def on_session_ended(session_ended_request, session):
    """ Called when the user ends the session.

    Is not called when the skill returns should_end_session=true
    """
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here

# --------------- Main handler ------------------

def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" +
          event['session']['application']['applicationId'])

    """
    Uncomment this if statement and populate with your skill's application ID to
    prevent someone else from configuring a skill that sends requests to this
    function.
    """
    # if (event['session']['application']['applicationId'] !=
    #         "amzn1.echo-sdk-ams.app.[unique-value-here]"):
    #     raise ValueError("Invalid Application ID")

    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])

テスト

登録したLambda functionが適切に動作するかテストします。

作成したLambda functionを選択

20-test_lambda

Configure test event イベント指定Windowを開く

21-configure_test

発行するイベントを入力

22-configure_test

intent name を HowManyBlogsIntent として Lambda functionを呼び出し結果を取得・表示するための物です。

{
  "session": {
    "new": false,
    "sessionId": "amzn1.echo-api.session.[unique-value-here]",
    "attributes": {},
    "user": {
      "userId": "amzn1.ask.account.[unique-value-here]"
    },
    "application": {
      "applicationId": "amzn1.ask.skill.[unique-value-here]"
    }
  },
  "version": "1.0",
  "request": {
    "locale": "en-US",
    "timestamp": "2016-10-27T21:06:28Z",
    "type": "IntentRequest",
    "requestId": "amzn1.echo-api.request.[unique-value-here]",
    "intent": {
      "name": "HowManyBlogsIntent"
    }
  },
  "context": {
    "AudioPlayer": {
      "playerActivity": "IDLE"
    },
    "System": {
      "device": {
        "supportedInterfaces": {
          "AudioPlayer": {}
        }
      },
      "application": {
        "applicationId": "amzn1.ask.skill.[unique-value-here]"
      },
      "user": {
        "userId": "amzn1.ask.account.[unique-value-here]"
      }
    }
  }
}

結果確認

コントロールパネル下段のExecution resultにresponseとしてoutputSpeechに 「XX Blog posts Today」と表示されればテストOKです!

23-configure_test

※こちらの内容をAmazon echo (dot)が喋ります

次回はいよいよAlexa Skillを登録し、Amazon echo (dot)を喋らせましょう!

この連載を通じて出来上がるもの

単独で動作しますが、自分の発音の英語を5回に1度程しか聞き取ってくれないため(悲)、PCにて喋らせております。

PC – 「Alexa, ask blog get count」
Amazon Echo Dot – 「16 Blog posts Today」

この間に自分の作成したLambda funtcionを通っていると考えると感激モノですね!

動作概要解説で参考にさせて頂いたページ

公式ドキュメント

投稿者プロフィール

takashi
開発会社での ASP型WEBサービス企画 / 開発 / サーバ運用 を経て
2010年よりスカイアーチネットワークスに在籍しております

機械化/効率化/システム構築を軸に人に喜んで頂ける物作りが大好きです。
個人ブログではRaspberryPiを利用したシステムやロボット作成も
実施しております。

スカイアーチネットワークスで一緒に働きましょう!

コメントを残す

メールアドレスが公開されることはありません。

Time limit is exhausted. Please reload CAPTCHA.

ABOUTこの記事をかいた人

開発会社での ASP型WEBサービス企画 / 開発 / サーバ運用 を経て 2010年よりスカイアーチネットワークスに在籍しております 機械化/効率化/システム構築を軸に人に喜んで頂ける物作りが大好きです。 個人ブログではRaspberryPiを利用したシステムやロボット作成も 実施しております。 スカイアーチネットワークスで一緒に働きましょう!