7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

30分くらいでサクッと作るPodcast対応Alexaスキル

Last updated at Posted at 2018-12-18

スマートスピーカーAdventカレンダー18日目担当 @snaka です。投稿間に合わず、日付変わってしまいました。すみません。

自作のスキルはもっぱらPodcast再生スキルのみなので、必要最低限レベルの実装で動くPodcast再生スキルの作り方というテーマで記事を書いてみた。

例題として「Misreading Chat」というポッドキャストを再生するスキルを作ってみた。

この記事での作業手順は基本的に CLI ベースで作業する前提となっている。以下の記事も参考にすると良いかも。

この記事の手順を実行した結果は GitHub に公開しているので参考まで。

プロジェクト作成

まずは、ask new でプロジェクトを作成する。

$ ask new
? Please select the runtime Node.js V8
? List of templates you can choose Hello World
? Please type in your skill name:  skill-misreading-podcast
Skill "skill-misreading-podcast" has been created based on the chosen template

スキルのデプロイ

以下のように skill.json を編集

  • manifest.publishingInformation.locales の下に ja-JP のエントリを作成
  • manifest.publishingInformation.categoryPODCAST
  • manifest.apis.custominterfaces のエントリを記述

以下のようになっているはず。

skill.json
{
  "manifest": {
    "publishingInformation": {
      "locales": {
        "ja-JP": {
          "summary": "Misreading Chat",
          "examplePhrases": [
            "アレクサ、ミスリーディングチャットを開いて"
          ],
          "name": "skill-misreading-podcast",
          "description": "Sample Full Description"
        }
      },
      "isAvailableWorldwide": true,
      "testingInstructions": "Sample Testing Instructions.",
      "category": "PODCAST",
      "distributionCountries": []
    },
    "apis": {
      "custom": {
        "endpoint": {
          "sourceDir": "lambda/custom",
          "uri": "ask-custom-skill-misreading-podcast-default"
        },
        "interfaces": [
          { "type": "AUDIO_PLAYER" }
        ]
      }
    },
    "manifestVersion": "1.0"
  }
}

上記を保存したら、以下のコマンドでスキルのみをデプロイ

$ ask deploy -t skill

モデルをデプロイ

以下のように models 配下にJSONファイルを作成

  • invocationName はスキルの呼び出し名
models/ja-JP.json
{
  "interactionModel": {
    "languageModel": {
      "invocationName": "ミスリーディングチャット",
      "intents": [
        {
          "name": "AMAZON.CancelIntent",
          "samples": [
            "キャンセル"
          ]
        },
        {
          "name": "AMAZON.HelpIntent",
          "samples": []
        },
        {
          "name": "AMAZON.StopIntent",
          "samples": []
        },
        {
          "name": "AMAZON.PauseIntent",
          "samples": []
        },
        {
          "name": "AMAZON.ResumeIntent",
          "samples": []
        }
      ]
    }
  }
}

デプロイするためには sample を1つ以上登録する必要があるらしいので、とりあえずキャンセルという発話サンプルだけ登録している。

jsonを保存して以下のようにモデルをデプロイする。

$ ask deploy -t model

Lambda 関数のデプロイ設定

Lambda 関数デプロイの準備として以下のように serverless.yml を作成

  • alexaSkill の値は自分のスキルのIDに置き換える
    • ask deploy -t skill の実行結果などからコピペする
lambda/custom/serverless.yml
service: skill-misreading-podcast

provider:
  name: aws
  region: ap-northeast-1
  runtime: nodejs8.10
  memorySize: 128
  timeout: 30

package:
  include:
    - node_modules/

functions:
  main:
    handler: index.handler
    events:
      - alexaSkill: amzn1.ask.skill.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
        enabled: true

必要なパッケージのインストール

後述のコードでrequest, feedparser を利用するため、以下のようにパッケージをインストールしておく。

$ npm install --save request feedparser

Lambda 関数を作成

index.js を以下のように実装。

  • PODCAST_FEED_URL はポッドキャストのフィードURL
  • PODCAST_NAMEはAlexaがエピソード再生前に読み上げるポッドキャスト名
    • models/ja-JP.json に定義した「スキル呼び出し名」とは別の名前を設定してもよい
lambda/custom/index.js
/* eslint-disable  func-names */
/* eslint-disable  no-console */
const request = require('request')
const FeedParser = require('feedparser')

const PODCAST_FEED_URL = 'https://misreading.chat/category/episodes/feed/'
const PODCAST_NAME = 'ミスリーディングチャット'

function pickSslMediaUrl (enclosures) {
  const sslMedia = enclosures.find(item => item.url.startsWith('https'))
  if (sslMedia) return sslMedia.url

  const nonSslMedia = enclosures[0]
  // Alexa Skill の AudioPlayer は https: で提供されるURLしか対応していないため強引に書き換える
  if (nonSslMedia) return nonSslMedia.url.replace(/^http:/, 'https:')

  throw new Error('Media not found.')
}

function getLatestEpisode () {
  return new Promise(async (resolve, reject) => {
    const feedparser = new FeedParser()

    request.get(PODCAST_FEED_URL).pipe(feedparser)

    feedparser.on('data', (data) => {
      const audioUrl = pickSslMediaUrl(data.enclosures)
      resolve({
        title: data.title,
        url: audioUrl,
        published_at: data.pubDate.toISOString()
      })
    })
  })
}

async function playPodcast (handlerInput) {
  const latestEpisode = await getLatestEpisode()
    const speechText = `${PODCAST_NAME} の最新エピソード ${latestEpisode.title} を再生します`;

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard(PODCAST_NAME, speechText)
      .addAudioPlayerPlayDirective('REPLACE_ALL', latestEpisode.url, latestEpisode.url, 0)
      .getResponse();
}

const Alexa = require('ask-sdk-core');

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  async handle(handlerInput) {
    return await playPodcast(handlerInput)
  },
};

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('ごめんなさい、よくわかりません')
      .reprompt('ごめんなさい、よくわかりません')
      .getResponse();
  },
};

const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

index.js を保存したら以下のように Lambda 関数をデプロイ

$ sls deploy

スキルのエンドポイントにLambda関数のARNを登録

Lambda 関数をデプロイしたら AWS の管理コンソールでデプロイした Lambda 関数を開き、関数のARNを控える。

image.png

そしてskill.json を開き以下のように、manifest.apis.custom.endpoint.uri の値として Lambda 関数のARNをセットする

skill.json(抜粋)
        "endpoint": {
          "uri": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:skill-misreading-podcast-dev-main"
        },

上記を保存した後、スキルをデプロイ

$ ask deploy -t skill

スキルの呼び出しをテスト

まず、テストを有効化する

$  ask api enable-skill -s amzn1.ask.skill.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx --stage development

以下のようにスキルを呼び出してみる、-t の後の文字列は、skill.json に登録したスキル呼び出し名にする

$ ask simulate -l 'ja-JP' -t 'ミスリーディングチャットを開いて'

レスポンスで以下のようにAudioPlayer.Playディレクティブに、音源(mp3)のURLがセットされていたらOK

実行結果の一部抜粋
{
  "type": "AudioPlayer.Play",
  "playBehavior": "REPLACE_ALL",
  "audioItem": {
    "stream": {
      "token": "https://misreadingchat.files.wordpress.com/2018/11/40.mp3",
      "url": "https://misreadingchat.files.wordpress.com/2018/11/40.mp3",
      "offsetInMilliseconds": 0
    }
  }
}

ベータテスト

シミュレータではAudioPlayerでの音声ファイルの再生は行えないので、音声を再生まで確認したい場合はベータテストとしてスキルを公開する必要がある。

その準備として、skill.json を開き以下のように記述を追加

  • smallIconUri, largeIconUri にアイコンのURLを設定
    • アイコンは規定のサイズでないとデプロイでエラーになるので注意
    • 下記の例で示しているアイコン画像はpublicに公開してるので自由に使ってもらってOK
  • `privacyAndCompliance' のエントリを追加

最終的に skill.json は以下のようになっているはず。

skill.json
{
  "manifest": {
    "publishingInformation": {
      "locales": {
        "ja-JP": {
          "summary": "Misreading Chat",
          "examplePhrases": [
            "アレクサ、ミスリーディング開いて"
          ],
          "name": "skill-misreading-podcast",
          "description": "Sample Full Description",
          "smallIconUri": "https://s3-ap-northeast-1.amazonaws.com/snaka-public/alexa-icons/Sample_108.png",
          "largeIconUri": "https://s3-ap-northeast-1.amazonaws.com/snaka-public/alexa-icons/Sample_512.png"
        }
      },
      "isAvailableWorldwide": true,
      "testingInstructions": "Sample Testing Instructions.",
      "category": "PODCAST",
      "distributionCountries": []
    },
    "privacyAndCompliance": {
      "allowsPurchases": false,
      "usesPersonalInfo": false,
      "isChildDirected": false,
      "isExportCompliant": true,
      "containsAds": false
    },
    "apis": {
      "custom": {
        "endpoint": {
          "uri": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:skill-misreading-podcast-dev-main"
        },
        "interfaces": [
          { "type": "AUDIO_PLAYER" }
        ]
      }
    },
    "manifestVersion": "1.0"
  }
}

上記を保存したらスキルをデプロイ

$ ask deploy -t skill

スキルのデプロイが完了したら、開発者ポータル上でベータテストの有効化を行う。
具体的な手順は以下のページなどを参照。

Alexaスキルのベータテストを行う | Custom Skills

ベータテストに参加して、スキルがEchoデバイスで有効になったら「アレクサ、ミスリーディングチャットを開いて」と発話することで Echo デバイスで Misreading Chat の最新エピソードが再生されるはず。

さいごに

ここで紹介したコードは、必要なインテントの実装が抜けていたりするので、そのままでは審査を通すことはできないのでご注意を。
また、第三者が権利を有するコンテンツをスキルで使用する場合、そのコンテンツを扱うための許諾を受けていることを審査時に提示しなければならない、そのあたりについては以前記事を書いているので下記を参照のこと。

現在、Podcast 対応スキルを最小限のコーディングで実装するためのパッケージを作成中なので、いい感じにできてきたら、そのうち Qiita にチュートリアルの記事をアップできたらいいなぁ(モチベーション次第... :sweat_smile:

関連リンク

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?