初めて投稿します。
ようやくAmazon Echoを手に入れたのでTwitterAPIを使ったAlexaスキル開発をしてみました。
本記事はプログラミングというよりも、Alexaスキル開発の流れを説明する形になっています。
作成したスキル
今回作成したスキルは、
Alexaに「行ってきます」「ただいま」と声をかけるとツイッターにその時間を投稿する
というシンプルなものです。
スキル名は「ツイッター」+「ログ」で『ツイログ』としました。
- 「アレクサ、ツイログで行ってきます」
- 「アレクサ、ツイログでただいま」
と、不自然な発話をすることでツイートします。
ツイートが成功するとAlexaが「つぶやきました」と喋ります。
注意
作成するスキルはTwitterAPIに必要なACCESS_TOKEN、ACCESS_TOKEN_SECRETをプログラム中に埋め込むため、アカウントリンクを行わない特定のTwitterアカウント専用スキルです。
参考資料
Amazon公式のAlexaスキル開発トレーニングで最低限必要な知識は身につきます。
スキル開発してみようと思ったら、まずはこれを全て実施しましょう。1日あれば終わります。
全体イメージ
Alexaスキルの開発
TwitterApplicationManagementの登録
まず初めに外部からTwitterAPIを叩くための各種キーを取得します。(画像は過去の使い回しなので入力内容はあまり気にしないでください)
Twitter Application Managementへアクセスし、自分のTwitterアカウントでログインします。
次に右上の「Create New App」をクリックします。
登録するアプリケーションの入力フォームが出ます。「Name」「Description」「Website」は必須入力項目です。適当な内容で入力します。
- Name:アプリケーションの登録名
- Description:アプリケーションの説明文
- Website:アプリケーションの問い合わせ先が分かるようなURLを入力します。
- CallbackURL:今回は設定不要です。
入力したらページ最下部の「Create your application」をクリックします。
続いてAPIの利用に必要となるAccess Tokenを発行します。表示されたページのタブ「Keys And Access Tokens」から「Create my access token」をクリックします。
これでTwitterAPIの準備は完了です。ページに表示されているConsumer Key
、Consumer Secret
、Access Token
、Access Token Secret
をプログラム内で利用します。
Amazon開発者コンソール
いよいよAlexaスキルを作成していきます。まずAmazon開発者コンソールにログインします。
上段メニュー「ALEXA」を選択して「Alexa Skills Kit」へ進みます。
スキルの基本情報として言語
スキル名
呼び出し名
を入力して保存、次へ進みます。
続いて対話モデルを設定します。ここがAlexaスキル開発のポイントです。「インテントスキーマ」と「サンプル発話」を設定します。
インテントスキーマ
インテントスキーマは「Alexaスキルで実行するアクションの種類」です。JSON形式で以下のように入力します。
{
"intents": [
{
"intent": "GoOutTweet"
},
{
"intent": "ComeHomeTweet"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.CancelIntent"
}
]
}
-
GoOutTweet
:「行ってきます」に対応するアクション。 -
ComeHomeTweet
:「ただいま」に対応するアクション。 -
AMAZON.HelpIntent
:「使い方」や「説明」に対応するアクション。 -
AMAZON.StopIntent
:「中止」「停止」に対応するアクション。 -
AMAZON.Cancelntent
:「キャンセル」に対応するアクション。
AMAZON.
と付いているインテントは「標準インテント」とよばれ、インテントスキーマに定義するだけでデフォルトのキーフレーズで使えるようになります。
一方でGoOutTweet
、ComeHomeTweet
は今回作成するAlexaスキルのオリジナルアクションである「カスタムインテント」です。カスタムインテントは次に説明するサンプル発話でキーフレーズを設定する必要があります。
サンプル発話
サンプル発話ではカスタムインテントに対して「そのインテントがアクションを起こすためのキーフレーズ」を定義します。例えば「行ってきます」っぽいことを言えばGoOutTweetが、「ただいま」っぽいことを言えばComeHomeTweetが起動するイメージです。
今回はGoOutTweet、ComeHomeTweetに対して以下のように発話フレーズを設定します。
GoOutTweet 行く
GoOutTweet 行って
GoOutTweet 行きます
GoOutTweet 出かける
ComeHomeTweet ただいま
ComeHomeTweet 帰った
ComeHomeTweet 帰って
ComeHomeTweet 戻った
入力が完了したら次に進みます。
AWS Lambda
ここで一度Amazon開発者コンソールを離れ、AWSにログインします。
作成フォームで「一から作成」を選択します。名前
は任意の名前を入力し、ランタイム
は「Node.js 6.10」を選択します。
続いてロール
の項目から「カスタムロールの作成」を選択します。
ロールの作成画面が表示されます。全てそのままで「許可」をクリックします。
ここまでできたら右下の「関数の作成」をクリック。
実際に動かすプログラムを直接コーディングすることもできますが、今回はzipファイルをアップロードする形式を取ります。zipファイルとコードはgithub上にアップロードしています。
https://github.com/quotto/TweetByAlexa
画面の中頃に「関数コード」という項目があるので、ここでコードエントリータイプ
「.ZIPファイルをアップロード」を選択して、ファイルskill.zip
をアップロードします。
一応プログラムの説明
index.js
がメインの実行プログラムです。Amazon開発者コンソールで定義したインテントごとにAlexaの振る舞いを実装します。
'use strict';
const Alexa = require('alexa-sdk');
const Twitter = require('twitter');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
const JSTOffset = 60 * 9 * 60 * 1000; // JST時間を求めるためのオフセット
const ErrorMessage = '<say-as interpret-as="interjection">ごめんなさい、</say-as>つぶやけませんでした。';
function calculateJSTTime() {
var localdt = new Date(); // 実行サーバのローカル時間
var jsttime = localdt.getTime() + (localdt.getTimezoneOffset() * 60 * 1000) + JSTOffset;
var dt = new Date(jsttime);
return dt;
}
const handlers = {
'LaunchRequest': function () {
this.emit('AMAZON.HelpIntent');
},
'GoOutTweet' : function() {
var dt = calculateJSTTime();
var stringTime = dt.getFullYear() + "年" + (dt.getMonth()+1) + "月" + dt.getDate() + "日 " + dt.getHours() + "時" + dt.getMinutes() + "分" + dt.getSeconds() + "秒 ";
var message = stringTime + "に出かけました。(Alexaスキルでつぶやいています)";
Twitter.postTweet(message).then(()=>{
this.emit(':tell','つぶやきました。<say-as interpret-as="interjection">いってらっしゃい。</say-as>');
},(error)=>{
console.log(error);
this.emit(':tell',ErrorMessage);
})
},
'ComeHomeTweet' : function() {
var dt = calculateJSTTime();
var stringTime = dt.getFullYear() + "年" + (dt.getMonth()+1) + "月" + dt.getDate() + "日 " + dt.getHours() + "時" + dt.getMinutes() + "分" + dt.getSeconds() + "秒 ";
var message = stringTime + "に帰ってきました。(Alexaスキルでつぶやいています)";
Twitter.postTweet(message).then(()=>{
this.emit(':tell','つぶやきました。<say-as interpret-as="interjection">おかえりなさい。</say-as>');
},(error)=>{
console.log(error);
this.emit(':tell',ErrorMessage);
})
},
'AMAZON.HelpIntent': function () {
const speechOutput = '<say-as interpret-as="interjection">「いってきます」「ただいま」</say-as>、とつぶやくと、その時間をツイートします。';
const reprompt = speechOutput;
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
'AMAZON.StopIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
};
exports.handler = function (event, context) {
const alexa = Alexa.handler(event, context);
alexa.APP_ID = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
alexa.registerHandlers(handlers);
alexa.execute();
};
Alexa.handler
のthis.emit
を実行することでAlexaが喋ります。
ツイッターに投稿するTwitter.postTweet
の部分は同梱のtwitter.js
に自作のOAuthプログラムを実装しています。
"use strict";
const AWS = require('aws-sdk');
const https = require('https');
const request = require('request');
const crypto = require('crypto');
const url='https://api.twitter.com/1.1/statuses/update.json';
function postTweet(message) {
return new Promise((resolve,reject) => {
const include_entities = {
status: message,
// include_entities: true
};
const params = {
oauth_consumer_key: process.env.CONSUMER_KEY,
oauth_token: process.env.ACCESS_TOKEN,
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: (() => {
const date = new Date();
return Math.floor(date.getTime() / 1000);
})(),
oauth_nonce: (() => {
const date = new Date();
return date.getTime();
})(),
oauth_version: '1.0'
};
let auth_params = Object.assign(include_entities,params);
let encoded_auth_params = Object.keys(auth_params).map(function(key){
return `${encodeURIComponent(key)}=${encodeURIComponent(this[key])}`;
},auth_params);
encoded_auth_params.sort((a,b) => {
if(a < b) return -1;
if(a > b) return 1;
return 0;
});
const sigunature_base = `${encodeURIComponent('POST')}&${encodeURIComponent(url)}&${encodeURIComponent(encoded_auth_params.join('&'))}`;
const keyOfSign = `${encodeURIComponent(process.env.CONSUMER_SECRET)}&${encodeURIComponent(process.env.ACCESS_TOKEN_SECRET)}`;
const signature = crypto.createHmac('sha1',keyOfSign).update(sigunature_base).digest('base64');
params.oauth_signature = signature;
let authorization = Object.keys(params).map(function(key) {
return `${encodeURIComponent(key)}="${encodeURIComponent(this[key])}"`;
},params);
authorization.sort((a,b) => {
if(a < b) return -1;
if(a > b) return 1;
return 0;
});
const headers = {
Authorization: `OAuth ${authorization.join(', ')}`
};
const options = {
url: url+"?status="+encodeURIComponent(message),
headers: headers
};
request.post(options ,(error,response,body) => {
if(error) {
reject("reject:"+error);
} else if(response.statusCode!=200) {
reject("reject:statusCode="+response.statusCode);
} else {
resolve();
}
});
});
};
module.exports.postTweet=postTweet;
require('aws-sdk')
を実行することで、process.env.CONSUMER_KEY
という形式で、後述するAWS lambdaに設定した環境変数を利用することができます。
続いて環境変数を設定します。CONSUMER_KEY
、CONSUMER_SECRET
、ACCESS_TOKEN
、ACCESS_TOKEN_SECRET
をキーとして、TwitterApplicationManagementで取得したそれぞれの値を設定します。
次に画面上段の「Designer」のトリガーの追加
から「Alexa Skills Kit」を選択します。
画面最下部に「トリガーの追加」メニューが表示されるので「追加」を実行します。
ここまでで関数の作成は完了です。正常に関数が動作するかをテストします。
「テスト」をクリックすると子画面が表示されるので、「新しいイベントの作成」を選びイベントテンプレート「Alexa Start Session」を選択します。
すると何やらコードが表示されます。イベント名に適当な名前を入力し「作成」を実行します。
画面に戻り再び「テスト」を実行して「実行結果:成功」と表示されれば関数のテストは完了です。
最後に画面右上に表示されたARN
の値をコピーしておきます。この後の設定に利用します。
Amazon開発者コンソールで仕上げとテスト
関数の作成が終わったらAmazon開発者コンソールに戻ります。
設定メニューの項目エンドポイント
で「AWS LambdaのARN(Amazonリソースネーム)」を選択します。すると、下の方にデフォルト
というフォームが表示されるので、先ほどコピーしたARNを貼り付けて次へ進みます。
テスト画面が表示されるので、ここで最終的な動作確認を行います。この画面では作成したAlexaスキルが起動した状態になっているため、発話サンプルで設定したキーフレーズを入力します。
フォームに「ただいま」と入力し、「ツイログを呼び出す」を実行します。
左側にはAlexaへ送信した入力情報、右側にはAlexaが情報を受け取りAWS Lambdaで実行された関数の結果が表示されます。右側のoutputSpeech
がAlexaが喋る内容です。ここに想定通りのセリフが表示されていればOKです!
AmazonEchoに導入
ここまででAlexaスキルの作成は全て完了です。最後は実機へ導入します。
次の画面へ進むと「公開情報」の設定になります。今回は本当に公開するわけではないので、適当に入力します。
最後に「プライバシーとコンプライアンス」の設定です。とりあえず「いいえ」と「輸出コンプライアンス」にチェックすればOKです。
保存を実行すると画面左側のメニューから「スキルのベータテスト」がクリックできるようになります。
「テスターのメールアドレス」に自分のアドレスを入力して追加します。他にも使ってもらいたい人がいれば最大500件まで登録できます。
自分のアドレスにメールが届くので、その中のリンクをクリックします。なお日本語環境では2番目の「JP customer~」リンクを選択します。
Alexaスキルのページ(アプリ画面)が表示されるので「スキルテスト」でスキルを有効化します。
これで全て完了です!実際にAlexaに呼びかけてみましょう。
次回
今回はACCESS_TOKENとACCESS_TOKEN_SECRETをベタ打ちにしたため、特定のTwitterアカウント専用のスキルとなりました。
この理由はAlexa Skills Kitがサポートする認可方式がOAuth2.0であるのに対して、TwitterAPI(の投稿)はOAuth1.0であるため、単純にアカウントリンクができないためです。
次はそれを解決するために中間の橋渡しをするサーバーを用意して、アカウントリンクバージョンを作ってみます。
こちらに作成記事を公開しました!
https://qiita.com/quotto/items/865326ac1e7aa3ac9c3c