npm install
.env.sampleファイルをコピーし、.envとリネームします。このファイルにはアカウント登録時に送られてきたAITalk Web APIのエンドポイント、ユーザー名、パスワードを保存します。これらの情報はサンプルコード内で利用されます。AI_TALK_BASE_URL=https://api.example.com AI_TALK_USERNAME=username AI_TALK_PASSWORD=password
index.jsを開いてください。必要なパッケージの宣言と、生成した音声ファイルを保存しておくフォルダを設定しています。'use strict'; require('dotenv').config(); const express = require('express'); const bodyParser = require('body-parser'); const path = require('path'); const fsp = require('fs').promises; const got = require('got'); //着信に対して応答を返すVoice用TwiMLを作成するクラス const VoiceResponse = require('twilio').twiml.VoiceResponse; const app = express(); const VOICE_FOLDER_NAME = 'voice_files'; app.use(bodyParser.urlencoded({ extended: true })); // 生成したファイルを保存しておくフォルダを設定 app.use(express.static(path.join(__dirname, VOICE_FOLDER_NAME))); asyncキーワードを指定しています。// Twilioから着信リクエストを受け取るWebhook app.post('/incoming', async (req, res, next) => { //処理を実装 }); /incoming内部の実装は次のようになっています。'speaker_name' : 'nozomi_emo')、最大限の喜びを('style' : { 'j': '1.0'})抑揚('range' : 2.00)をつけてmp3で出力('ext' : 'mp3')するように設定しました。// 音声合成するテキストを設定。実際はAIエンジンなどから生成される。 const text = '今日はお電話ありがとう!エーアイトークとTwilioを連携した音声合成デモだよ! あなたの電話番号は' + req.body.From + 'だよね?ぜひ、両方のサービスを試してみてね。'; // AITalk Web API用のパラメーターを作成 const aiTalkOptions = { 'username' : process.env.AI_TALK_USERNAME, 'password' : process.env.AI_TALK_PASSWORD, 'text' : text, 'speaker_name' : 'nozomi_emo', 'style' : { 'j': '1.0'}, 'range' : 2.00, 'ext' : 'mp3' } try { // リクエストオプション const options = { method: 'POST', form: aiTalkOptions, encoding: 'binary' }; //AITalk Web APIにリクエスト const response = await got(process.env.AI_TALK_API_URL, options); if (response.statusCode === 200 ) { //現在のCallSidから出力ファイル名を生成 const outputFileName = `${req.body.CallSid}.mp3`; // mp3ファイルを保存 await fsp.writeFile( path.join(VOICE_FOLDER_NAME, outputFileName), response.body, 'binary'); // TwiMLを作成(次のコードブロック) } } catch (error) { console.error(error); next(error); } Play句を使用し、先ほど保存したmp3ファイルを再生するというTwiMLを生成します。このTwiMLをレスポンスとして返しています。// TwiMLを初期化 const twiml = new VoiceResponse(); // 生成した音声を再生させる。 twiml.play(path.join(outputFileName)); //作成したTwiMLをレスポンスとして送信 res.status(200).send(twiml.toString());
/statuschangedでは、リクエストのbodyに含まれているCallStatusで現在の通話状態を確認できます。今回のサンプルでは、通話が完了したcompletedとなった時点で、通話IDを表すCallSidを基にmp3ファイルを削除しています。app.post('/statuschanged', async(req, res, next) => { try { // 通話が完了したかどうかを確認 if (req.body.CallStatus === "completed") { // CallSidからファイルパスを構築し、削除 const filePath = path.join(VOICE_FOLDER_NAME, `${req.body.CallSid}.mp3`); await fsp.unlink(filePath); console.info(`${filePath}を削除しました`); } res.sendStatus(200); } catch (error) { console.error(error); next (error); } }); app.listen(3000, () => console.log('Listening on port 3000')); ngrok http 3000
https://2c2a973d.ngrok.ioなので下記のスクリーンショットのようになります。npm start
'speaker_name' : 'miyabi_west')に変更して違いをくらべてみるのも面白いかもしれません。