Amazon Connect+Transcribe+KVS+SNS で留守番電話をテキストに起こしてSMSで通知するシステムを作った
みなさん、突発的にかかってくる電話は好きですか?
自分は、"それまでの思考や作業が中断され"、"同期的なコミュニケーションを強いられる上に"、"だいたいの場合においてこっちにメリットがない" という3点で大嫌いです。
留守電にしても、ヘッダー情報が電話番号くらいしかないメッセージを開くのが手間に思えます。*1
留守番電話をテキストにして通知してくれればいいのに・・・*2
というわけでAWS上に置いたシステムがこちらです。
各コンポーネントを簡単に説明していきます。
(1) Calls +81-50-xxxx-xxxx
まずは電話番号の取得から。Amazon Connect でインスタンスを作成し、出来上がったポータルに admin でログインして申請します。*3
(2) Invokes Lambda
Contact flow を作成し、Lambda を Invoke できるようにします。
途中の Play prompt (録音待ち) の部分は SSML の break time を使用したのですが、10秒以上の待ち時間を指定するとうまく待ってくれない事象が起きたため、2つの要素に分割しました。*4
<speak> <break time="10s"/><p/> <break time="10s"/><p/> 承りました<p/> </speak>
(3) Records audio to KVS (raw data format)
(3’) Stores session information in S3
(4) Converts KVS audio to WAV
こちらに関しては、クラスメソッド平内さんのソースを パク 借用しただけなので割愛させていただきます。Rawデータ変換の部分は本当に助かりました。
なお、const ebml = require('ebml'); が "errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'ebml'" でコケたので、こちらを参考に ebml の Layer を追加して対応しました。
(5) Transcribes text from WAV
WAVから文字起こしの処理です。S3 bucket が input/output で分かれているのは、Amazon Transcribe で S3 の出力先フォルダを指定する方法が見つからなかったため、管理の都合上やむを得ず分割しました。
InvokeTranscription
const AWS = require("aws-sdk"); const region = 'ap-northeast-1'; const inBucket = 'connect-voicemail'; const outBucket = 'connect-voicemail-text'; /** * exports.handler */ exports.handler = async(event) => { const transcribe = new TranscribeService(AWS, region); for (let record of event.Records) { const key = record.s3.object.key; const jobName = key.split('/')[1]; const uri = 'https://' + inBucket + '.s3-' + region + '.amazonaws.com/' + key; var res = await transcribe.start(jobName, uri); console.log('res: ' + JSON.stringify(res)); } return {}; }; /** * Class TranscribeService */ class TranscribeService { constructor(AWS, region) { this._transcribe = new AWS.TranscribeService({ apiVersion: '2017-10-26', region: region }); } async start(jobName, uri) { const params = { 'TranscriptionJobName': jobName, 'LanguageCode': 'ja-JP', 'Media': { 'MediaFileUri': uri }, 'MediaFormat': 'wav', 'MediaSampleRateHertz': 8000, 'OutputBucketName': outBucket, }; return await this._transcribe.startTranscriptionJob(params).promise(); } }
(6) Sends SMS to my phone (+81-80-xxxx-xxxx)
SendTranscriptionBySMS
import boto3 import logging import json s3 = boto3.client('s3') sns = boto3.client('sns') logger = logging.getLogger() topicArn = 'arn:aws:sns:ap-northeast-1:<Account ID>:TranscriptionSNS' def handler(event, context): # Get transcription from S3 object (JSON) bucket = event['Records'][0]['s3']['bucket']['name'] key = event['Records'][0]['s3']['object']['key'] s3Object = s3.get_object( Bucket = bucket, Key = key ) content = json.loads(s3Object['Body'].read().decode('utf-8')) for transcript in content['results']['transcripts']: logger.warn(transcript) # TODO Shorten message if the length is larger than 160 bytes message = transcript['transcript'] res = sns.publish( TopicArn = topicArn, Message = message ) #logger.warn(res) return {};
SMSのテスト中に "No quota left for account" というエラーに引っ掛かり、調べたところ、デフォルトの Quota が 1 USD / month (about 15 SMS messages per month) となっているとのことだったので サポートケースをオープンして上限を引き上げました。
4時間ほどで返信あり。
Hello, Your new SMS monthly spending limit of $10 USD was implemented. This may take up to one hour to reflect in your console. Before you can send messages, you must update your account spend limit using the Amazon PINPOINT console or API ( https://docs.aws.amazon.com/pinpoint/latest/userguide/channels-sms-setup.html ). When you complete these procedures, you may see a message stating that your default limit is $1.00. You can disregard this message. We recommend monitoring metrics for Amazon Pinpoint using CloudWatch ( https://docs.aws.amazon.com/pinpoint/latest/userguide/monitoring.html ). As you get started with Amazon Pinpoint, we recommend that you:
-
- Apply for higher spending limits before you need them ( https://docs.aws.amazon.com/pinpoint/latest/userguide/channels-sms-awssupport-spend-threshold.html ).
- Open a case in the AWS Support Center to request other SMS options, if needed ( https://docs.aws.amazon.com/pinpoint/latest/userguide/channels-sms-awssupport.html ).
- Review additional information on sending SMS messages for best practices ( https://docs.aws.amazon.com/pinpoint/latest/userguide/channels-sms.html ).
Amazon PINPOINT で Limit を Update せよとのことでしたが、それはせず普通に [Amazon SNS] -> [Text messaging (SMS)] -> [Text messaging preferences] -> [Account spend limit] の設定を変えれば即座に反映されました。
ためしに迷惑電話っぽい電話を自分にかけてテストしました。ところどころうまくいってないのはたぶん滑舌のせい。(だから電話はイヤなのだ)
追記:
本記事公開のわずか2日後に発表された Contact Lens for Amazon Connect (Preview) に Sign up すると、音声の文字起こし部分を AWS-Managed で利用できるようです。
dev.classmethod.jp
aws.amazon.com
*1:イヤなものは全部イヤに見えてくるのだ
*2:キャリアSIMだとあるらしいですね
*3:日本の電話番号は 050 のみ。1 インスタンスあたり 10 番号まで申請できます。 https://docs.aws.amazon.com/connect/latest/adminguide/connect-tokyo-region.html https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-service-limits.html
*4:今の実装だと、留守電を途中(「承りました」の前)で切られた際にその後の Invoke が効かなくなるという不具合があり・・・ break time を短くする以外の Workaround があるといいのですが。