誰に聞かれるかわからないメッセージ（前編）

先日はVonageのCommunication APIを使ってみようということで、下記の記事を投稿させていただきました。　

https://qiita.com/syantien/items/02fe4e1b7fc6db2404ef

なんとなく、どんなことができるのかご理解いただけたかと思います。今日は実際に動くもので、体験していただけるようなサービスをサクッと作ってみたいと思います。

とはいえ、使えるサービスだと運用したくなってしまうので、あまり使えないサービスを作ってみます。もうタイトルにありますが、誰に聞かれるかわからないメッセージサービスを作ってみます。そして作り始めたら以外に長くなってしまったので、前編と後編で分けてお届けします。

サービスの内容は簡単で、特定の番号に電話すると

1. 誰かが残したボイスメッセージを聞くことができる。
2. 誰に聞かれるかわからないけどボイスメッセージを残すことができる。

です。ニーズもなければ用途もありませんが、早速作っていきましょう。

# プロジェクトの作成

今回は（といか、”も") C#, Visual Studioでロジックを書きます。

今回使うサーバーは予めAzure Web Appを用意しましたが、この手順は省きます。

まずはプロジェクトの作成を行います、今回は特にユーザー用のUIいらないのでAPIサービスを作成します。Visual Studioから下記のステップを踏んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/dba5764f-3d50-ab1b-8094-10ffc6ce22bf.png)

新規プロジェクトが作成されましたら、`Controllers`フォルダーを右クリック、コントローラーを作成します。　今回はAPIコントローラーを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/535ca8cc-17cf-721c-8e15-22381c813112.png)

`HandleCallsControllers.cs`というコントローラーを作成しました。

一旦コントローラーを作成したところで、準備完了ということで次にいきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/b1ebebf7-16a6-f971-9bac-26ec9552f60f.png)

今回はVonage SDKを利用しますので、こちらも事前にインストールしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/805de936-dda2-0f99-39b2-247fa03da959.png)

これで準備は一旦完了です。

# コールを受けられるようにする

では場所を移動して、次はVonageの管理画面へ。まずはコールを受けられるように管理画面にて設定します。管理画面にログインして下記のステップを踏んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/c5e76d91-f748-23ce-14a3-aba0ab987ab2.png)

新規プロジェクトが作成されたら次は新しいControllerを作成します。既存のコントローラーは消して大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/8e7c022a-f29e-fe16-9881-2f8e432d0ca2.png)

次に回答URL、イベントURL、フォールバックURLが求められるので、Visual Studioに戻り、それぞれ一旦作成します。

何をどうするかはまた後で考えるとして、一旦受け口を下記のように準備します。

```csharp
[Route("api/[controller]")]
[ApiController]
public class HandleCallsController : ControllerBase
{

    [HttpPost]
    [AllowAnonymous]
    [Route("/answer")]
    [Produces("application/json")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public IActionResult CallAnswer()
    {
        return Ok();
    }

    [HttpPost]
    [AllowAnonymous]
    [Route("/event")]
    [Produces("application/json")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public IActionResult CallEvent()
    {
        return Ok();
    }

    [HttpPost]
    [AllowAnonymous]
    [Route("/fallback")]
    [Produces("application/json")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public IActionResult CallFallback()
    {
        return Ok();
    }

}
```
一旦リリースして、アクセスできるようにします。リリースできていることを確認。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/08d0d4b1-e703-c0ed-b31b-cd35ddcd6843.png)

Swaggerに関してはだいぶ古くなってしまいましたが、こちらの記事参照ください。

https://qiita.com/syantien/items/b82879afad3b4c18f224

それでは、下記のように入力して、アプリケーション作成とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/13502081-9ad9-03b8-8087-148b97f5f3b0.png)

生成ボタンは画面右下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/408d2e31-5169-1d16-71a3-a9f29c824a08.png)

生成されると、電話番号とリンクできるページに遷移されますので、もっている電話番号と紐づけましょう。電話番号の取得などはVonageさんにサポートお願いできるかと思います（未確認）。

今回アメリカの番号がリンクされてない状態でありましたので、それを使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/180d9dea-b2e5-e475-8f31-521cd2f542a6.png)

リンクされたました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/707058b6-9c28-4c09-6ced-732c93a0fb91.png)


# ボイスメッセージを残せるようにする

それでは、早速コールを受けてボイスメッセージを残せるようにしましょう。

まず、コールが来ると先ほどの `/answer` にリクエストが来るので、それに対して回答をします。回答は`TalkAction`というオブジェクトに格納します。

```csharp
 var talkAction = new TalkAction
 {
     Text = "Welcome to Leave Message application service from " +
     $"{string.Join(" ", Request.Query["from"].ToString().ToCharArray())}" +
     ", Press 1 to leave a message, or 2 to listen to messages."
 };
 ```

 その次に、1か2を選択してほしいので、アクションを求めます。ここで`MultiInputAction`というオブジェクトに格納します。ここで指定したアクションは`EventUrl`に送信されます。

```csharp
var inputAction = new MultiInputAction
{
  EventUrl = new[] { $"{Request.Scheme}://{Request.Host}/dtmf" },
  Dtmf = new DtmfSettings
  {
    TimeOut = 10,
    MaxDigits = 1
  }
};
 ```

 これらをNccoというオブジェクトにいれ、その文字列にして下記のように返します(Jsonで返す)。

 ```csharp
 
var ncco = new Ncco(talkAction, inputAction);

return ncco.ToString();

 ```
 
ユーザーが`1`を押した場合の、指定したURLにリクエストが飛びますので、そのアクションを下記のように用意します。 リクエストできた`MultiInput`オブジェクトをC#のオブジェクトに載せ、`input.Dtmf.Digits`で番号を理解し、アクションを組み立てます。メッセージを録音するロジックはまた別に組んであるので、ここでは「メッセージを録音してください」とアナウンスしてから３秒後にメッセージを録音するアクションにリクエストを送ります。

 ```csharp
 [HttpPost]
 [Route("/dtmf")]
 public ActionResult<string> HandleInput()
 {
     var input = WebhookParser.ParseWebhook<MultiInput>(Request.Body, Request.ContentType);

     if (input.Dtmf.Digits == "1")
     {
         // Return recordNcco
        
         var talkAction = new TalkAction
         {
             Text = "Please record your random message. Press # when finished."
         };
         var recordAction = new RecordAction
         {
             EndOnSilence = "3",
             BeepStart = true,
             EventUrl = new[] { $"{Request.Scheme}://{Request.Host}/recording" },
             EventMethod = "POST",
             EndOnKey = "#"
         };

         var ncco = new Ncco(talkAction, recordAction);
         return ncco.ToString();

     }
     else
     {
        // Handle invalid input
        var talkAction = new TalkAction
        {
            Text = "You did not listen to me too well, you can only choose 1 or 2, Press 1 to leave a message, or 2 to listen to messages."
        };

        var inputAction = new MultiInputAction
        {
            EventUrl = new[] { $"{Request.Scheme}://{Request.Host}/dtmf" },
            Dtmf = new DtmfSettings
            {
                TimeOut = 10,
                MaxDigits = 1
            }
        };

        var ncco = new Ncco(talkAction, inputAction);

        return ncco.ToString();
    }
}
 ```

 録音を処理するロジックは下記です。ここでは特に変わった処理はなくSDKにある録音機能を使って指定したファイル名で保存するようにしています。

 ```csharp
[HttpPost("/recording")]
public async Task<IActionResult> Recording()
{
     var appId = _applicationId;
     var privateKeyPath = _privateKeyPath;
     var credentials = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKeyPath);
     var voiceClient = new VoiceClient(credentials);
     var record = await Vonage.Utility.WebhookParser.ParseWebhookAsync<Record>(Request.Body, Request.ContentType);
     var recording = await voiceClient.GetRecordingAsync(record.RecordingUrl);

     var fileName = Guid.NewGuid().ToString() + "_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".mp3";

     await System.IO.File.WriteAllBytesAsync(fileName, recording.ResultStream);
     return StatusCode(204);
}

```

一旦ここまでで、実装して今日の記事は終わりとします。

# 次回

次回は、レコーディングしたメッセージを聞いてみる、をやってみます。そうすると、とりあえず、だれに聞かれるかわからないメッセージを残せて、誰にあてて残されたかわからないメッセージを聞くことができるサービスの完成です。