'use strict';
const Alexa = require('alexa-sdk');
const APP_ID = process.env.Emergency_APPID; // 緊急連絡 app ID
//環境変数の取り込み
const smtp_host = process.env.SMTP_HOST; // SMTPサーバー
const smtp_user = process.env.SMTP_USER_NAME;
const smtp_password = process.env.SMTP_PASSWORD;
const emergency_to = process.env.EMERGENCY_SENDTO; // 緊急連絡先メールアドレス
const twilio_account = process.env.TWILIO_ACCOUNT_SID;
const twilio_authtoken = process.env.TWILIO_AUTH_TOKEN;
const twilio_call_to = process.env.EMERGENCY_CALL_TO_NUMBER; // 緊急連絡先電話番号
const twilio_call_from = process.env.EMERGENCY_CALL_FROM_NUMBER;
var nodemailer = require('nodemailer');
//SMTPの設定
var SSLSmtpConfig = {
host: smtp_host,
port: 465, // SSLを使用する場合のポート番号
secure: true, // SSLの有効化
auth: {
user: smtp_user,
pass: smtp_password,
tls: {
rejectUnauthorized: false // do not fail on invalid certs
}
}
};
var GeneralSmtpConfig = {
host: smtp_host,
port: 25,
auth: {
user: smtp_user,
pass: smtp_password,
use_authentication: false, // 認証しない
}
};
// 言語リソース
const SLEEP = '<break time="300ms"/>';
const CR = "\n";
const ALEXA_EMERGENCY_TEXT = "これはアレクサからの緊急連絡です。";
const ASP = "「大至急、";
const CONTACT_MESSAGE_TEXT = "自宅に連絡してください。」";
const REQMAIL_MESSAGE_TEXT = "メールしてください。」";
const PHONECALL_MESSAGE_TEXT = "自宅へ電話してください。」";
const BACK2HOME_MEESSAGE_TEXT = "自宅へ帰って来てください。」";
const REQHELP_MESSAGE_TEXT = "「自宅で助けを呼んでいます。」";
const languageStrings = {
"ja": { // 日本語
translation: {
THIS_SKILL_NAME: "緊急連絡",
MESSAGE_OF: "と、",
WELCOME_MESSAGE: "さん、緊急連絡メール、緊急電話連絡が使用できます。",
HELP_MESSAGE: "緊急連絡のメール送信、または電話ができます。"+
"メールの場合は、「緊急連絡で、連絡してとメール」、「緊急連絡で、電話してとメール」、などと指示してください。"+
"電話の場合は、「緊急連絡で、連絡してと電話」、「緊急連絡で、帰ってきてと電話」、などと指示してください。"+
"送ることができるメッセージは、" + SLEEP +
"連絡して" + SLEEP +
"メールして" + SLEEP +
"電話して" + SLEEP +
"帰宅して" + SLEEP +
"助けて" + SLEEP + "です。",
AUTH_MESSAGE: "このスキルを使用するには、Amazonへのログインを許可してください。",
NOPROF_MESSAGE: "Amazonの認証情報が取得できませんでした。",
HTTPS_GET_ERROR: "通信に問題が発生しました。",
ASK_TEST: "動作確認のため、"+
"テストメールを送信する場合は、「緊急連絡で、テストメールを送信」と、"+
"テスト電話をかける場合は、「緊急連絡で、テスト電話をかけて」と指示してください。",
TESTMAIL_TEXT: "からのテストメールです。",
TESTCALL_TEXT: "からのテスト電話です。",
TESTMAIL_SUCCESS: "さんへ、テストメールを送信しました。",
TESTCALL_SUCCESS: "さんへ、テスト電話をかけました。",
SEND_FAILED: "メールの送信に失敗しました。",
PHONECALL_FAILED: "さんに電話が繋がりませんでした。",
SEND_TO: "さんへ、",
SEND_MAIL: "メールを送信しました。",
PHONE_CALL: "電話をかけました。",
CONTACT_MESSAGE: ASP + CONTACT_MESSAGE_TEXT,
REQMAIL_MESSAGE: ASP + REQMAIL_MESSAGE_TEXT,
PHONECALL_MESSAGE: ASP + PHONECALL_MESSAGE_TEXT,
BACK2HOME_MEESSAGE: ASP + BACK2HOME_MEESSAGE_TEXT,
REQHELP_MESSAGE: REQHELP_MESSAGE_TEXT,
EMERGENCY_SUBJECT: ALEXA_EMERGENCY_TEXT,
EMERGENCY_CONTACT: ALEXA_EMERGENCY_TEXT + CR + ASP + CONTACT_MESSAGE_TEXT,
EMERGENCY_REQMAIL: ALEXA_EMERGENCY_TEXT + CR + ASP + REQMAIL_MESSAGE_TEXT,
EMERGENCY_CALL: ALEXA_EMERGENCY_TEXT + CR + ASP + PHONECALL_MESSAGE_TEXT,
EMERGENCY_COMEBACK: ALEXA_EMERGENCY_TEXT + CR + ASP + BACK2HOME_MEESSAGE_TEXT,
EMERGENCY_HELP: ALEXA_EMERGENCY_TEXT + CR + REQHELP_MESSAGE_TEXT,
WHAT_REQUEST: "どのような緊急メッセージを送りますか?"+
"メールを送る場合は、「連絡してとメール」、「電話してとメール」などと。"+
"電話をかける場合は、「連絡してと電話」、「帰って来てと電話」などと、"+
"指示してください。",
WHAT_REQUEST_MAIL: "どのような緊急メッセージを送りますか?"+
"「連絡してとメール」、「電話してとメール」、などと指示してください。",
WHAT_REQUEST_CALL: "どのような緊急メッセージを送りますか?"+
"「連絡してと電話」、「帰って来てと電話」、などと指示してください。",
NAME_IS: "連絡先氏名:",
ADDRESS_IS: "緊急連絡先のメールアドレスと電話番号は、",
MAIL_ADDRESS_IS: "緊急連絡先メールアドレス:",
PHONE_NUMBER_IS: "緊急連絡先電話番号:",
SEE_THE_CARD: "アレクサアプリで緊急連絡のカードを参照してください。",
CANCEL_MESSAGE: "緊急連絡を中止します。",
STOP_MESSAGE: "緊急連絡を終了します。",
UNKNOWN_MESSAGE: "すみません、分かりません。",
RETRY_MESSAGE: "すみません、もう一度お願いします。",
},
},
};
const handlers = {
// 「緊急連絡を開く」で呼び出された場合
'LaunchRequest': function() {
var name;
var email;
console.log("LaunchRequest: start");
try {
name = this.attributes['name'];
email = this.attributes['email'];
} catch (e) {
this.emit("WelcomeIntent");
}
this.attributes['stat'] = undefined;
if (name === undefined || email === undefined) {
this.emit("WelcomeIntent");
}
else {
console.log("LaunchRequest: Ask your request");
this.emit(':ask', this.t("WHAT_REQUEST"));
}
},
// 「緊急連絡で○○をメール」で呼び出された場合
'SendEmergencyMail': function (callback) {
var session = this.event.session; // セッション
var intent = this.event.request.intent; // インテント
var req = intent.slots.Message.value; // スロット{Message}の値
var name = this.attributes['name']; // DynamoDBから取り出す
var email = this.attributes['email']; // DynamoDBから取り出す
var sendTo = emergency_to;
var msg_text;
var req_text;
var self = this;
if (!req) {
let dialogState = this.event.request.dialogState;
if (dialogState === "STARTED") {
let updatedIntent = intent;
//updatedIntent.slots.Message.value = undefined;
console.log("SendEmergencyMail: dialogState is STARTED.");
this.emit(':delegate');
//this.emit(':delegate', updatedIntent); //初期値を設定したい場合
}
else if (dialogState !== "COMPLETED") {
console.log("SendEmergencyMail: dialogState is " + dialogState);
this.emit(':delegate');
//スロット{Message}について尋ねる
//this.emit(':elicitSlot', "Message", this.t("WHAT_REQUEST_MAIL"));
}
}
this.attributes['stat'] = "SendEmergencyMail";
console.log("SendEmergencyMail: name="+name+", email="+email);
console.log("SendEmergencyMail: req="+req);
switch (req) {
case "連絡":
case "連絡して":
case "連絡ください":
msg_text = this.t("EMERGENCY_CONTACT");
req_text = this.t("CONTACT_MESSAGE")+this.t("MESSAGE_OF");
break;
case "メール":
case "メールして":
case "メールください":
msg_text = this.t("EMERGENCY_REQMAIL");
req_text = this.t("REQMAIL_MESSAGE")+this.t("MESSAGE_OF");
break;
case "電話":
case "電話して":
case "電話ください":
msg_text = this.t("EMERGENCY_CALL");
req_text = this.t("PHONECALL_MESSAGE")+this.t("MESSAGE_OF");
break;
case "帰宅":
case "帰宅して":
case "帰って":
case "帰ってきて":
case "戻って":
case "戻ってきて":
msg_text = this.t("EMERGENCY_COMEBACK");
req_text = this.t("BACK2HOME_MEESSAGE")+this.t("MESSAGE_OF");
break;
case "助け":
case "助けて":
case "ヘルプ":
msg_text = this.t("EMERGENCY_HELP");
req_text = this.t("REQHELP_MESSAGE")+this.t("MESSAGE_OF");
break;
default: {
// 送信するメッセージ(スロットの値)がない、または不明の場合、
// どんなメッセージを送りたいのか問い合わせる。
this.emit(':ask', this.t("WHAT_REQUEST_MAIL"));
return;
}
}
var message = {
from: "Alexa <" + email + ">",
to: sendTo,
subject: this.t("EMERGENCY_SUBJECT"),
text: msg_text
};
this.emit('SendEmail', message, (err) => {
self.attributes['stat'] = undefined;
if (err) {
self.emit(':tell', self.t("SEND_FAILED"));
}
else {
let res = name + self.t("SEND_TO") + req_text + self.t("SEND_MAIL");
self.emit(':tellWithCard', res, ALEXA_EMERGENCY_TEXT, res);
}
});
},
'SendEmail': function (message, callback) {
//SMTPの接続(SSLを使用する場合。SSLを使用しない場合は"GeneralSmtpConfig"の方を使う)
let smtp = nodemailer.createTransport(SSLSmtpConfig);
// verify connection configuration
smtp.verify((error, success) => {
if (error) {
console.log(error);
callback(error);
}
console.log("Server is ready to take our messages", success);
//メールの送信
console.log("SendEmail:" + JSON.stringify(message));
smtp.sendMail(message, (err, info) => {
if (err) {
//送信失敗
console.log(err);
callback(err);
}
console.log("Message sent: " + info.messageId);
//SMTPの切断
smtp.close();
callback(null, "success");
});
});
},
//「緊急連絡で連絡先を教えて」と呼び出された場合
'SayMailAddress': function () {
var name = this.attributes['name']; // DynamoDBから取り出す
var email = this.attributes['email']; // DynamoDBから取り出す
var msg = this.t("ADDRESS_IS") + this.t("SEE_THE_CARD");
var txt = this.t("NAME_IS") + name + CR +
this.t("MAIL_ADDRESS_IS") + emergency_to + CR +
this.t("PHONE_NUMBER_IS") + twilio_call_to;
this.attributes['stat'] = undefined;
this.emit(':tellWithCard', msg, this.t("THIS_SKILL_NAME"), txt);
},
// 「緊急連絡で○○と電話」と呼び出された場合
'PhoneCall': function (callback) {
var session = this.event.session; // セッション
var intent = this.event.request.intent; // インテント
var req = intent.slots.Message.value; // スロット{Message}の値
var name = this.attributes['name']; // DynamoDBから取り出す
var email = this.attributes['email']; // DynamoDBから取り出す
var msg_text;
var req_text;
var self = this;
if (!req) {
let dialogState = this.event.request.dialogState;
if (dialogState === "STARTED") {
let updatedIntent = intent;
//updatedIntent.slots.Message.value = undefined;
//console.log("SendEmergencyMail: dialogState is STARTED.");
this.emit(':delegate');
//this.emit(':delegate', updatedIntent); //初期値を設定したい場合
}
else if (dialogState !== "COMPLETED") {
//console.log("SendEmergencyMail: dialogState is " + dialogState);
this.emit(':delegate');
//スロット{Message}について尋ねる
//this.emit(':elicitSlot', "Message", this.t("WHAT_REQUEST_CALL"));
}
}
this.attributes['stat'] = "PhoneCall";
console.log("PhoneCall: req=",req);
switch (req) {
case "連絡":
case "連絡して":
case "連絡ください":
msg_text = this.t("EMERGENCY_CONTACT");
req_text = this.t("CONTACT_MESSAGE")+this.t("MESSAGE_OF");
break;
case "メール":
case "メールして":
case "メールください":
msg_text = this.t("EMERGENCY_REQMAIL");
req_text = this.t("REQMAIL_MESSAGE")+this.t("MESSAGE_OF");
break;
case "電話":
case "電話して":
case "電話ください":
msg_text = this.t("EMERGENCY_CALL");
req_text = this.t("PHONECALL_MESSAGE")+this.t("MESSAGE_OF");
break;
case "帰宅":
case "帰宅して":
case "帰って":
case "帰ってきて":
case "戻って":
case "戻ってきて":
msg_text = this.t("EMERGENCY_COMEBACK");
req_text = this.t("BACK2HOME_MEESSAGE")+this.t("MESSAGE_OF");
break;
case "助け":
case "助けて":
case "ヘルプ":
msg_text = this.t("EMERGENCY_HELP");
req_text = this.t("REQHELP_MESSAGE")+this.t("MESSAGE_OF");
break;
default: {
// 電話するメッセージ(スロットの値)がない、または不明の場合、
// どんなメッセージを送りたいのか問い合わせる。
this.emit(':ask', this.t("WHAT_REQUEST_CALL"));
return;
}
}
console.log("PhoneCall: message=" + req);
console.log("PhoneCall: " + msg_text);
this.emit('TwilioPhoneCall', msg_text, (err) => {
self.attributes['stat'] = undefined;
if (err) {
self.emit(':tell', name + self.t("PHONECALL_FAILED"));
}
else {
let res = name + self.t("SEND_TO") + req_text + self.t("PHONE_CALL");
self.emit(':tellWithCard', res, ALEXA_EMERGENCY_TEXT, res);
}
});
},
'TwilioPhoneCall': function (message, callback) {
var self = this;
var https = require("https");
var key = twilio_account; // Twilio ACCOUNT SID
var secret = twilio_authtoken; // Twilio AUTHTOKEN
var options = {
hostname: "api.twilio.com",
port: 443,
path: "/2010-04-01/Accounts/" + key + "/Calls",
method: "POST",
auth: key + ":" + secret,
headers : { "Content-Type" : "application/x-www-form-urlencoded" }
};
var req = https.request(options, (response) => {
var res = response.statusCode;
if (res == 200 || res <= 204) {
console.log("Twilio PhoneCall is OK!");
callback(null, "success"); // 送信成功
}
else {
response.setEncoding("utf8");
response.on("data", (data) => {
console.log("res=" + res);
console.log(data);
callback(new Error("Invalid response"));
});
}
}).on('error', (error) => {
console.log(error);
callback(error);
});
var rawXml =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<Response>' +
' <Say voice="alice" language="ja-JP" loop="3">' + message + '</Say>' +
'</Response>';
var urlParam = encodeURIComponent('http://twimlets.com/echo?Twiml=' + encodeURIComponent(rawXml));
console.log("TwilioPhoneCall:" + urlParam);
req.write("To=" + twilio_call_to + "&");
req.write("From=" + twilio_call_from + "&");
req.write("Method=GET&");
req.write("Url=" + urlParam);
req.end();
},
'WelcomeIntent': function () {
console.log("WelcomeIntent: start");
let accessToken = this.event.session.user.accessToken;
if (accessToken == undefined) {
// トークンが空の場合は、ユーザーに許可を促す
this.emit(':tellWithLinkAccountCard', this.t("AUTH_MESSAGE"));
return;
}
// LWAからプロファイルを取得する
var self = this;
var https = require("https");
var url = "https://api.amazon.com/user/profile?access_token=" + accessToken;
https.get(url, (response) => {
if (response.statusCode == 200) {
// プロファイルから名前とメールアドレスを取り出す
var body = "";
response.setEncoding("utf8");
response.on("data", function(chunk) {
// レスポンスボディーを変数に追加
body += chunk;
});
response.on("end", (res) => {
console.log(JSON.stringify(body));
let name = JSON.parse(body).name;
let email = JSON.parse(body).email;
self.attributes['name'] = name;
self.attributes['email'] = email;
console.log("Name=" + name + ", Email=" + email);
self.emit(':tell', name + self.t("WELCOME_MESSAGE") + self.t("ASK_TEST"));
});
}
else {
console.log("statusCode=" + response.statusCode);
self.emit(':tell', self.t("NOPROF_MESSAGE"));
}
}).on('error', (error) => {
console.log(error);
self.emit(':tell', self.t("HTTPS_GET_ERROR"));
});
},
'TestMail': function () {
// テストメールを送信する
var self = this;
var intent = this.event.request.intent; // インテント
let name = this.attributes['name'];
let email = this.attributes['email'];
console.log("TestMail: name=" + name + ", sendTo=" + emergency_to);
var message = {
from: 'Alexa <' + email + '>',
to: emergency_to,
subject: "Test Mail",
text: name + this.t("TESTMAIL_TEXT")
};
this.emit('SendEmail', message, (err) => {
if (err) {
self.emit(':tell', self.t("SEND_FAILED"));
}
else {
self.emit(':tell', name + self.t("TESTMAIL_SUCCESS"));
}
});
},
'TestCall': function () {
// テスト電話を発信する
var self = this;
var intent = this.event.request.intent; // インテント
let name = this.attributes['name'];
console.log("TestCall: name=" + name + ", CallTo=" + twilio_call_to);
this.emit('TwilioPhoneCall', name + this.t("TESTCALL_TEXT"), (err) => {
if (err) {
self.emit(':tell', name + self.t("PHONECALL_FAILED"));
}
else {
self.emit(':tell', name + self.t("TESTCALL_SUCCESS"));
}
});
},
'AMAZON.HelpIntent': function () {
const speechOutput = this.t("HELP_MESSAGE");
const reprompt = this.t("HELP_MESSAGE");
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
this.attributes['stat'] = undefined;
this.emit(':tell', this.t("CANCEL_MESSAGE"));
},
'AMAZON.StopIntent': function () {
this.attributes['stat'] = undefined;
this.emit(':tell', this.t("STOP_MESSAGE"));
},
'Unhandled': function () {
console.log("Unhandled Intent: start");
this.attributes['stat'] = undefined;
this.emit("WelcomeIntent");
}
};
// Lambda関数ハンドラー
// パラメタ
// event: イベントデータ
// context: ランタイム情報
// callback: 呼び出し元へ情報を返すためのコールバック関数
exports.handler = function (event, context, callback) {
var session = event.session; // セッション
var apid = session.application.applicationId; // アプリケーションID
const alexa = Alexa.handler(event, context, callback);
if (apid != APP_ID) {
context.fail("Invalid Application ID"); // 緊急連絡アプリ以外からの呼び出し
return;
}
alexa.appId = APP_ID;
alexa.dynamoDBTableName = "UserInfoTable"; // session attributeを保存するテーブル
// To enable string internationalization (i18n) features, set a resources object.
alexa.resources = languageStrings; // 言語リソースを設定
alexa.registerHandlers(handlers); // ハンドラを設定
alexa.execute();
};
|