目次
はじめに
スキルからユーザーに何らかの情報を返すとき、音声で読み上げるには長すぎたり、記録として手元に残しておきたい場合があります。このような場合に、スキルの返したい情報をEメールで送る方法について紹介します。
Eメールの宛先は、デバイスを所有するアカウント(オーナー)のEメールアドレスになります。またEメールの送信には、Amazon Simple Email Service (SES) を使用します。
Alexaは音声プロフィールを登録していると、個々の話者を特定できます。ユースケース的には、個々の話者にEメールを送れると面白いのですが、話者ごとのEメールアドレスを取得する機能はAlexaにはありません。全てデバイスオーナー宛にEメールされるため、複数ユーザーで利用している場合は、利用上の注意が必要です。
なお本記事ではAlexa-Hostedスキルを前提にしていますが、自前のLambda環境で稼働するスキルの場合も、同様の手法を適用可能です。
事前準備
読者はすでにカスタムスキルの開発経験があるという想定です。次のアカウント及び環境について、準備と理解がされているものとします。
- Amazon開発者アカウント (Alexaの開発用)
- Alexa開発環境
- AWSアカウントの取得 (こちら)
構築手順
下記の手順に従って構築を進めます。
- Eメールアドレスへのアクセス権の取得
- SESへのアクセス権の付与
- SESのセットアップ
- スキルの実装
- テスト
Eメールアドレスへのアクセス権の取得
デバイスオーナーのEメールアドレスにアクセスするには、デバイスオーナーによる許可を得る必要があります。下記のステップにより許可を得ることができます。
- 開発者コンソールでEメールアドレスへのアクセス権をリクエストする
- ユーザーがアクセス権を付与する
- AlexaアプリでEメールアドレスへのアクセス権をユーザーが付与する、または
- スキル内でユーザーに対して、Eメールアドレスへのアクセス権をリクエストする
以下、それぞれのステップを見ていきます。
開発者コンソールでアクセス権をリクエスト
開発者コンソールで、対象のスキルのビルド
タブを選び、左側のメニューからモデル
をクリックします。
メニューが切り替わるので、更にアクセス権限
をクリックします。アクセス権をユーザーにリクエストする項目がリストされていますから、その中からユーザーのEメールアドレス
を選んでオンにします。
ここでの設定は、あくまでスキルがEメールアドレスへのアクセス権を必要としていることを、宣言するだけです。実際にEメールアドレスにアクセスするには、この後に述べるように、ユーザーにアクセス権を付与してもらう必要があります。
Alexaアプリでアクセス権を付与
Alexaアプリからスキルを有効にすると、アクセス権の付与画面が表示され、Eメールアドレスへのアクセス権を付与できます。Alexaアプリからのアクセス権の付与は何時でもできます。
スキルの中からアクセス権をリクエスト
ユーザーがスキルの有効化時に、AlexaアプリからEメールアドレスへのアクセス権を付与しなかった場合、スキルの実行時にユーザーからのアクセス権の付与を得る必要があります。このために、スキルからAlexaアプリにアクセス権をリクエストするカードを表示します。
スキルからのレスポンスを下記に示します。.withAskForPermissionsConsentCard()
を含めることで、AlexaアプリにEメールアドレスへのアクセス権を求めるカードを表示します。
handlerInput.responseBuilder
.speak('Eメールへのアクセス権が必要です。アレクサアプリに、アクセス権を求めるカードを送ったので、許可をお願いします。')
.withAskForPermissionsConsentCard(['alexa::profile:email:read'])
.withShouldEndSession(true)
.getResponse();
Alexaアプリには下記のようなカードが表示されます。ユーザーは許可をアップデート
をクリックします。
アクセス権を付与できます。
SESへのアクセス権の付与
スキルからSES経由でEメールを送信するには、スキルにSESへのアクセス権を付与する必要があります。これはSecurity Token Service (STS) を使って実現します。
- SESからメールを送信するポリシー (
AlexaEmailWriteAccess
) を定義する - ロール (
AlexaEmailSender
) を定義して、上記ポリシー (AlexaEmailWriteAccess
) をアタッチする - 上記ロールの信頼関係にスキルの実行ロールを指定し、スキルが上記ロールを獲得できるようにする
STSを使ったAWSリソースへのアクセスについては、下記についても参照してください。
もし自前のLambda環境でスキルを実行する場合は、スキルの実行ロールにSESへのアクセス権を直接含めることで、STSによるアクセス権の付与は不要となります。
(本記事の以前のバージョンでは、SESにアクセスできるIAMユーザーを作成し、その認証情報をスキルコードで使っていました。しかしこの方式はAWSでは非推奨とされているため、STSを使う方式に変更しました)
STS版の記事をアップして直ぐにAmazonからメールが飛んできました。
Subject: ACTION REQUIRED: Your AWS Access Key is Exposed for AWS Account 123456789012
GitHubにサンプルコードを公開する際に、旧版のIAMユーザーの認証情報の入ったファイルを間違ってアップしていました。アップした直後に気がついて、レポジトリごと作り直したのですが、その前に検出されてしまったようです。すぐさまIAMコンソールで認証情報自体を無効化しました。
GitHubやNPMのレポジトリは、間違って公開された認証情報が無いか、スキャンされていると何かで読んだことを思い出しました。不正利用を企む組織の場合もあるそうです。
STSならばスキル側に認証情報を持つことは無いので、このようなミスはそもそも発生しません。図らずもSTSによるセキュリティ担保の必要性を認識することになりました。
ポリシーの作成
AWSコンソールからIAM (Identify and Access Management) サービスのコンソールを表示します。
- 左メニューから①
ポリシー
を選択します - ②
ポリシーを作成
をクリックします
- ①
ビジュアルエディタ
タブを選択します - ②
サービス
を展開しSES V2
を選択します - ③
アクション
を展開し、SendEmail
を選択します - ④
リソース
を展開し、指定
、このアカウント内のいずれか
を選択します - ⑤
次のステップ:タグ
をクリックします
タグはレポートなどで関連するリソースをまとめる際に使います。ここでは指定してもしなくても良いです。
次のステップ: 確認
をクリックします。
- 名前に①
AlexaEmailWriteAccess
を入力します - ②
ポリシーの作成
をクリックします
ポリシーAlexaEmailWriteAccess
が作成され一覧に追加されます。
ロールの作成
次にAlexaEmailSender
ロールを作成し、AlexaEmailWriteAcces
ポリシーをアタッチします。
AWSコンソールからIAMサービスのコンソールを表示します。
- 左メニューから①
ロール
を選択します - ②
ロールを作成
をクリックします
- 信頼されたエンティティの種類を選択から①
AWSサービス
を選択します。 - 一般的なユースケースから②
Lambda
を選択します - ③
次のステップ:アクセス権限
をクリックします
- ポリシーの一覧から、先に作成した①
AlexaEmailAcceess
を選択します - ②
次のステップ:タグ
をクリックします
タグはレポートなどで関連するリソースをまとめる際に使います。ここでは指定してもしなくても良いです。
次のステップ:確認
をクリックします。
- ロール名に①
AlexaEmailSender
を入力します - ②
ロールの作成
をクリックします
- ロールが作成され一覧に
AlexaEmailSender
が表示されます AlexaEmailSender
をクリックし概要を開きます
- ①
信頼関係
タブを選択します - ②
信頼関係の編集
をクリックします
- 信頼関係の編集画面で、①
スキルの実行ロールARN
を入力します (実行ロールARNの取得方法はこの後に説明します) - ②
信頼ポリシーの更新
をクリックします
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "<Skillの実行ロールARN>"
},
"Action": "sts:AssumeRole"
}
]
}
Alexa-Hostedスキルの実行ロールARNは、Alexaの開発者コンソールから取得できます。
- 開発者コンソールで①
コードエディタ
タブを選択します - ②
aws integrate
をクリックします - ポップアップから③
コピー
をクリックします
概要画面に戻ると信頼されたエンティティに、スキルの実行ロールARNが表示されていることが確認できます。
SESのセットアップ
下記のAWSのチュートリアルに従ってSESをセットアップします。
ネット上には他にも詳しい解説記事が多数あるので参考になると思います。
チュートリアルの中でも触れられていますが、チュートリアルでセットアップされるのは、サンドボックスという検証用の環境です。このサンドボックスには幾つか利用上の制限があります。そのひとつが、SESで事前に登録・検証したEメールアドレスにしかメールを送れないというものです。テスト目的でしたら問題ありませんが、実際の運用では任意のデバイスオーナーのEメールアドレスにメールを送る必要があります。認定申請の前には、プロダクションに移行して、この制限を解除しておく必要があります。
プロダクションへの移行
サンドボックスからプロダクションへの移行申請のステップを説明します。下記の資料も参考にしてください。
SESダッシュボードを開き、左側のメニューからSending Statistics
をクリックします。
Edit your account details
をクリックします。
申請フォームが開くので記入して送信します。
筆者が申請したのは随分前なので、どういった内容を入力したかは忘れてしまいました。もし今から申請するならこんなところかな、と思われるところを書いてみます。
Enable Production Access | Yes |
Mail Type | Transactional |
Website URL | 公開済みのスキルの場合は、そのスキルのホームページ 未公開の場合は、ブランク |
Use Case Description | Usecase Amazon Alexaのカスタムスキルから、Alexaデバイスのオーナー宛にメールを送信します。 How do you plan to build or acquire your mailing list? 宛先のメールアドレスは、オーナーからの許可を得て、Alexaサービスから取得します。 How do you plan to handle bounces and complaints? メールアドレスは毎回Alexaサービスから最新のものを取得します。またスキルの利用規約に問い合わせ先のメールアドレスを記載します。 How can recipients opt out of receiving email from you? オーナーは、何時でもAlexaアプリからメールアドレスへのアクセス許可を取り消せます。 How did you choose the sending rate or sending quota that you specified in this request? 送信レートは、想定されるスキルの利用ユーザー数から、デフォルトのクオータで開始可能と見積もっています。 |
Additional Contact Address | ブランク |
Preferred contact language | Japanese |
Use Case Description
の質問は、参照先の記事にあったものです。最後の質問、”How did you choose the sending rate or sending quota that you specified in this request?”は、ちょっと違和感があります。申請フォームにはクオータを指定するところはありませんから。
スキルの実装
スキルの定義をGitHubのレポジトリに登録しています。
対話モデルの定義
対話モデルにSendEmailIntentを定義しています。発話サンプルは下記の3つです。
- メール
- メールを送って
- メールして
環境変数の定義
下記の項目を.envの中に定義して、環境変数として取り込んでいます。
- メールの送信元のEメールアドレス
- AlexaEmailSenderRoleのARN
- SESをプロビジョニングしたリージョン
EmailSender='<送信元のEメールアドレス>'
RoleArn='<AlexaEmailSenderのARN>'
SESRegion='<SESをプロビジョニングしたリージョン>'
これらの環境変数は、スキルのコードの中に読み込まれます。
require('dotenv').config();
const EmailSender = process.env.EmailSender;
const RoleArn = process.env.RoleArn;
const SESRegion = process.env.SESRegion;
SendEmailIntentのハンドラ
SendEmailIntent
に対するハンドラの中でEメールアドレスを取得し、Eメールを送信しています。Eメールアドレスが取得できなかった場合は、Eメールアドレへのアクセス権が無いとみなし、アクセス権を求めるカードをレスポンスに含めています。
const SendEmailItentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'SendEmailIntent';
},
async handle(handlerInput) {
const emailAddress = await getEmailAddress(handlerInput);
if (emailAddress) {
await sendEmail(emailAddress);
return handlerInput.responseBuilder
.speak('Eメールを送信しました。')
.withShouldEndSession(true)
.getResponse();
}
else {
return handlerInput.responseBuilder
.speak('Eメールへのアクセス権が必要です。アレクサ・アプリに、アクセス権を求めるカードを送ったので、許可をお願いします。')
.withAskForPermissionsConsentCard(['alexa::profile:email:read'])
.withShouldEndSession(true)
.getResponse();
}
}
};
Eメールアドレスへのアクセス権の確認
Eメールアドレスへのアクセス権があるかどうかは、Eメールアドレスにアクセスしてみないと判りません。getEmailAddress()
関数の中で、サービスクライアント経由でアクセスし、403
の例外が発生した場合は、アクセス権が付与されていないと判断しています。
const getEmailAddress = async (handlerInput) => {
try {
const upsServiceClient = handlerInput.serviceClientFactory.getUpsServiceClient();
const emailAddress = await upsServiceClient.getProfileEmail();
return emailAddress;
}
catch (error) {
if (error.name === 'ServiceError') {
if (error.statusCode === 403) {
return null;
}
}
throw error;
}
}
Eメールの送信
Eメールを送信するsendEmail()
関数を下記に示します。
const sendEmail = async (emailAddress) => {
const params = {
Source: EmailSender,
Destination: {
ToAddresses: [emailAddress],
},
Message: {
Subject: {
Data: 'テストメール',
Charset: 'UTF-8'
},
Body: {
Text: {
Data: 'こんにちは\nAlexaからのテストメールです。',
Charset: 'UTF-8'
}
}
}
};
const STS = new AWS.STS({ apiVersion: '2011-06-15' });
const assumeResp = await STS.assumeRole({
RoleArn: RoleArn,
RoleSessionName: 'SendEmailRoleSession'
}, (err, res) => {
if (err) {
console.log('AssumeRole FAILED: ', err);
throw new Error('Error while assuming role');
}
return res;
}).promise();
const credentials = {
secretAccessKey: assumeResp.Credentials.SecretAccessKey,
accessKeyId: assumeResp.Credentials.AccessKeyId,
sessionToken: assumeResp.Credentials.SessionToken
};
const ses = new AWS.SES({
apiVersion: '2010-12-01',
credentials: credentials,
region: SESRegion
});
const response = await ses.sendEmail(params).promise();
console.log(JSON.stringify(response));
}
テスト
スキルをデプロイしたら、Alexaの開発者コンソールからテストします。
U: メール試験を開いて
A: こんにちは、メールして、と言ってみてください。
U: メールして
A: Eメールを送信しました
U: ユーザー、A: アレクサ
メールボックスにメールが配信されていることを確認してください。
おわりに
Alexaスキルからデバイスのオーナーに、Eメールを送る方法を紹介しました。
ユーザーにとっては、VUI (Voice User Interface)の中に閉じている(Alexaとの対話で完結する)ほうが、より良いインタフェースだと考えられます。ただやり取りされる情報の性質や量によっては、音声以外の手段で情報のやりとりをしたほうが良いこともあります。その場合は、外部のサービスやアプリケーションと連携したマルチモーダルなインタフェースを追求する必要があるでしょう。Eメールもその選択肢の1つとして考えています。
参考情報
付録. ソースコード
更新履歴
日付 | 内容 |
2021/09/16 | STSを使用するように変更 |
2021/04/13 | SESユーザー作成の参考資料へのリンクを追加 |
2021/04/02 | 初版公開 |