https://a.storyblok.com/f/270183/95981/63bef33cb9/game-of-telephones_1200x675.jpg

Nexmo Voice APIで電話ゲームをしよう

最終更新日 May 14, 2021

所要時間:3 分

子供の頃の電話ゲームを覚えているだろうか?休み時間に学校の校庭で、あるいは夏休みにお泊りキャンプで遊んだのではないだろうか。私は小学校の頃、教師がこのゲームを使って、通信がいかに信頼できないかを説明していたのを覚えている。もし遊んだことがないのなら、ここで簡単に復習してみよう:

電話は、ある人が隣の人にメッセージをささやくことから始まる。2番目の人は同じメッセージを隣の人に囁き、その人は隣の人とメッセージを共有する。このゲームは、メッセージが遊んでいる人全員を通り抜け、元の送信者に戻るまで続く。多くの場合、最終的なメッセージは元のメッセージとはまったく異なるものになる。

最新のクラウド・コミュニケーション・アプリケーションが達成できる必須かつ重要なタスクは数え切れないほどある。しかし、時には息抜きに楽しいものを作ってみるのも面白いかもしれない。そこでこのウォークスルーでは、Ruby on Rails、Nexmo Voice API、Google Cloud Platform Speech to Text および Translate API を使って電話ゲームを再現します。

完成すれば、着信電話を受け、オリジナルのメッセージを受け取り、それをテキストに変換し、最終的に英語に再翻訳するまで複数の言語を翻訳し、最終的なメッセージを発信者に再生するVoiceアプリケーションができる。

このアプリケーションの作業コピーをローカルマシンにクローンすることもできます。 GitHub.

準備はいいかい?始めよう!

前提条件

始めるには以下のものが必要だ:

ngrokのセットアップ

ローカルの開発サーバーを外部からアクセスできるようにする方法はいくつかあるが、最も簡単な方法のひとつがngrokを使う方法だ。以下の記事を読んでほしい。 この記事を参照してほしい。しかし、今回の目的では、ngrokを起動して、ngrokが提供するURLをコピーするだけでいい。

ngrokを起動するには、新しいターミナル・ウィンドウを開き、コマンドラインから以下を実行する:

ngrok http 3000

ターミナル・ウィンドウにngrokロギング・インターフェースが表示されます。インターフェイスの最上部近くには Forwardingで始まる行があり、2つのURLが含まれています。最初のURLは外部からアクセス可能なngrokのURLで、末尾に ngrok.ioが続きます。 http://localhost:3000これはあなたのローカル開発サーバーです。これで、あなたやNexmoが ngrok.ioURLはあなたのローカルサーバーに転送されます。

必ず ngrok.ioURLを安全な場所にコピーしてください。このURLは、次のステップでNexmoアカウント、電話番号、Voiceアプリケーションを設定する際に使用します。

Nexmoアカウントの設定

音声アプリケーションを動作させるためには、Nexmoアカウント、Nexmoプロビジョニングされた電話番号、Nexmoアプリケーション、そして最後にアプリケーションと電話番号をリンクさせる必要があります。

左側のメニューから Voice menuの項目をクリックします。以下の4つのオプションが表示されます。 APPLICATIONS:

Create voice app

をクリックしてください。 Create an applicationオプションをクリックすると、新しいNexmoアプリケーションを設定するページが表示されます。

以下の項目をフォームに記入してください:

  • Application nameテキストフィールド入力 rails-telephone-game

  • Event URLテキストフィールドにngrokのURLを入力してください: https://[ngrok url here]/event

  • Answer URLテキスト・フィールドにもう一度ngrokのURLを入力してください: https://[ngrok url here]/webhooks/answer

完了したら、先に進んで青い Create Applicationボタンをクリックしてください。

これでNexmo Voiceアプリケーションが作成できました。次のステップは、Nexmoの電話番号を購入し、このアプリケーションにリンクさせることです。

Nexmoダッシュボードから左メニューの Numbersメニュー項目をクリックします。3つのオプションが表示されます:

Numbers

オプションをクリックすると Buy numbersをクリックすると、国、機能、タイプ、4桁の番号を選択するページに移動します。

Buy numbers

現在お住まいの国を選択し、市内通話になるようにします。 Voiceを選び、タイプは携帯電話か固定電話のいずれかを選ぶ。テキストフィールドには何も入力する必要はありません。 Numberテキストフィールドには何も入力する必要はありません。をクリックすると Searchをクリックすると、利用可能な電話番号のリストが表示されます。オレンジ色の Buyボタンをクリックし、確認画面でもう一度オレンジ色の Buyボタンをもう一度クリックします。

一度番号を取得すれば、あとはその番号をあなたの rails-telephone-gameVoiceアプリケーションにリンクさせることができます。電話番号の横にある歯車のアイコンをクリックすると、以下のメニューが表示されます:

Edit numbers conference call

を選択します。 voice-proxy-forwarding-demoドロップダウンリストからアプリケーションを選択し、青いボタンをクリックします。 Okボタンをクリックします。これでNexmoの電話番号がVoiceアプリケーションにリンクされ、ボイスプロキシ経由で着信電話を受けたり転送したりできるようになります。

Google Cloud Platform アカウントの設定

Google Cloud Platform のドキュメントには ドキュメントがあります。

要するに、アカウントを作成したら、新しいアプリケーションを作成する必要がある。新しいアプリケーションを作成したら、クレデンシャルをダウンロードする必要があります。このファイルはJSONを含むファイルになっています。このファイルをNexmo API認証情報とともに環境変数として追加する方法については、後ほど説明します。

最後に、Google Cloud Platformのダッシュボードで、Speech APIとTranslate APIの両方が有効になっていることを確認してください。電話ゲームでは両方を使用します。

Railsアプリケーションのセットアップ

これで、電話をかけるためのRailsアプリケーションをセットアップする準備ができました。これから次のことを行います:

  1. 新しいアプリケーションの初期化

  2. API認証情報の追加

  3. コントローラアクションとルートの定義

新規アプリケーションの初期化

新しいアプリケーションを初期化するには、コマンドラインから以下を実行する:

rails new rails-telephone-game --database=postgresql

これで、PostgreSQLをデフォルトのデータベースとする新しいRailsアプリケーションが作成されます。

このコマンドが終わったら、お気に入りのコード・エディターでアプリケーションを開き、アプリケーションのルート・フォルダーにある Gemfileを編集します。Nexmo Ruby、dotenv-rails、Google Cloud Platform Translate API、Google Cloud Platform Speech API gems を追加します:

# Gemfile

gem 'nexmo'
gem 'dotenv-rails'
gem 'google-cloud-translate'
gem 'google-cloud-speech'

を保存した後 Gemfileを保存したら、コマンドラインから bundle installをコマンドラインから実行することでインストールできる。

また、この段階で rake db:migrateを実行してデータベース・スキーマを初期化したい。このウォークスルーでは、データの永続化は行いませんが、自分で行うこともできます。

API資格情報の追加

NexmoとGoogle Cloud Platformの両方のAPI認証情報をアプリケーションに提供する必要があります。環境変数を安全に管理するために、インストールした dotenv-railsgemの機能を使います。まず、プロジェクトのルートフォルダに .envという新しいファイルをプロジェクトのルート・フォルダに作成し、そのファイルへのパスを .gitignoreファイルに追加します。そのファイルを開き .envファイルを開き、以下を追加する:

# .env

GOOGLE_APPLICATION_CREDENTIALS=
GOOGLE_PROJECT_ID=
NEXMO_API_KEY=
NEXMO_API_SECRET=
NEXMO_NUMBER=
NEXMO_APPLICATION_ID=
NEXMO_PRIVATE_KEY=
BASE_URL=

最初の環境変数 GOOGLE_APPLICATION_CREDENTIALSは、GoogleのAPI認証情報を含むJSONファイルのパスを指す。先ほどアカウントを設定したときにダウンロードしたファイルをアプリケーションのルートフォルダに移動し、パスを =記号の右側にパスを追加します(例 GOOGLE_APPLICATION_CREDENTIALS=./my-google-cloud-platform-credentials.json).

つ目の環境変数 GOOGLE_PROJECT_IDは、サインアップ時に作成したGoogle Cloud PlatformプロジェクトのID(つまり GOOGLE_PROJECT_ID=rails-telephone-game).

次の5つの環境変数は、あなたのNexmoアカウントに関するものです。あなたの NEXMO_API_KEYNEXMO_API_SECRETダッシュボード.

変数 NEXMO_NUMBER変数の値は、Nexmoからプロビジョニングした電話番号です。

が表示されます。 NEXMO_APPLICATION_IDはNexmoダッシュボードのアプリケーション一覧にあります。は NEXMO_PRIVATE_KEYはGoogleの認証情報のように、あなたのアカウントの秘密鍵認証情報へのパスです。Nexmo音声アプリケーションを作成する際に、公開鍵と秘密鍵のペアを生成し、秘密鍵は自動的にコンピュータにダウンロードされます。秘密鍵をアプリケーションのルートフォルダに移動し、この変数の値としてパスを追加します。 GOOGLE_APPLICATION_CREDENTIALS.

最後の環境変数は、外部ngrok URLである: http://my-sample-url.ngrok.io.

これでアプリケーションに認証情報がすべて追加されたので、 コントローラとルートを作成する準備ができました。

コントローラアクションの定義

まず /app/controllers/という telephone_controller.rb.この中で行う最初の作業は、NexmoクライアントとGoogle Cloud Platformクライアントの認証済みインスタンスを定義することだ:

# telephone_controller.rb

class TelephoneController < ApplicationController

    Translator = Google::Cloud::Translate.new(project: ENV['GOOGLE_PROJECT_ID'])
    NexmoClient = Nexmo::Client.new(
        application_id: ENV['NEXMO_APPLICATION_ID'],
        private_key: File.read(ENV['NEXMO_PRIVATE_KEY'])
    )
    Converter = Google::Cloud::Speech.new

また、Google Cloud Platform Translate API をゲーム内で使用する言語のリストを定義します:

# telephone_controller.rb

class TelephoneController < ApplicationController

....

LANGUAGES = [
    'ar',
    'he',
    'hi',
    'ku',
    'ru',
    'tr',
    'yi'
]

このチュートリアルでは、アラビア語、ヘブライ語、ヒンディー語、クルド語、ロシア語、トルコ語、イディッシュ語を選んだ。もちろん、好きな言語に置き換えたり、追加したりすることができる。

コントローラには2つのアクションが必要だ: #answerそして #event.この #answerメソッドは電話への応答、ユーザーからのメッセージの聞き取りと録音を担当し、処理中は通話をオープンにしておきます。メソッドは #eventメソッドは、録音をテキストに書き起こし、それをトランスレータに通し、新しく翻訳されたメッセージを発信者に返します。

を使用する。 #answerメソッドである:

def answer
    puts "Starting Call"
    @@uuid = params[:uuid]
    render json:
    [
        { 
            :action => 'talk', 
            :text => 'Welcome to the Nexmo Telephone Game. To begin say your message at the beep. To end the recording press the pound key.'
        },
        {
            :action => 'record',
            :eventUrl => ["#{ENV['BASE_URL']}/event"],
            :beepStart => true,
            :format => "wav",
            :endOnKey => "#",
            :timeOut => 30
        },
        {
            :action => 'talk',
            :text => 'Please wait a moment as your message runs through our sophisticated top secret linguistic algorithm...'
        },
        {
            :action => 'conversation',
            :name => 'telephone-game-conversation'
        }
    ].to_json
end

この #answerメソッドは、JSON形式のNexmo Call Control Object(NCCO)命令で構成される。最初の命令は呼び出し元にウェルカムメッセージを送信し、2番目の命令は呼び出し元の応答を記録し、コールバックURLがあなたのアプリケーションのngrok URLであることをNexmoに伝えます。 /eventであることを伝えます。3番目の命令は発信者にメッセージが処理中であることを知らせ、4番目の命令はNexmoの conversation機能を使用して、処理が行われている間、コールを開いたままにします。

この #eventメソッドは、オーディオ録音があるかどうかを確認する条件チェックの中にラップされています。この /eventエンドポイントはNexmoによって何度もアクセスされ、異なるステータスが更新されますが、私たちは録音があるものだけに関心があります:

def event
    if params['recording_url']
        # Save Recording
        puts "Saving Audio File"
        NexmoClient.files.save(params['recording_url'], 'recording.wav')

        # Transcribe Recording
        transcribed_text = ''
        file_name = './recording.wav'
        audio_content  = File.binread(file_name)
        bytes_total    = audio_content.size
        bytes_sent     = 0
        chunk_size     = 32000
        streaming_config = {
            config: {
                encoding: :LINEAR16,
                sample_rate_hertz: 16000,
                language_code: "en-US",
                enable_word_time_offsets: true     
            },
            interim_results: true
        }
        puts "Converting Speech to Text with GCP Speech API"
        stream = Converter.streaming_recognize(streaming_config)
        # Simulated streaming from a microphone
        # Stream bytes...
        while bytes_sent < bytes_total do
            stream.send audio_content[bytes_sent, chunk_size]
            bytes_sent += chunk_size
            sleep 1
        end
        puts "Stopped passing audio to be transcribed"
        stream.stop
        # Wait until processing is complete...
        stream.wait_until_complete!
        puts "Transcription processing complete"
        results = stream.results
        results.first.alternatives.each do |alternatives|
            transcribed_text = alternatives.transcript
        end

        # Run Transcription Through Translations
        puts "Translating Message"
        translated_text = transcribed_text
        LANGUAGES.each do |language|
            translated_text = (translated_text == transcribed_text) ? 
            Translator.translate(translated_text, to: language) : Translator.translate(translated_text.text, to: language)
        end
        final_translation = Translator.translate(translated_text.text, to: 'en')

        # Play Final Text Back To Call
        puts "Playing Translated Audio to Call"
        puts "Transcribed Original Message: #{transcribed_text}"
        puts "Final Message: #{final_translation.text}"
        closing_msg = "Your message was translated through Arabic, Hebrew, Hindi, Kurdish, Russian, Turkish and Yiddish and is returned to you as: #{final_translation.text}"
        NexmoClient.calls.talk.start(@@uuid, text: closing_msg, voice_name: "Kimberly") if transcribed_text != ''
    end
end

この #eventそれを分解してみよう。

最初は、パラメータに recording_urlが真になったら、その録音をローカルに保存します。そして、GCP Speech to Text APIを利用して、音声録音を書き起こしテキストに変換します。

そのために、オーディオ設定を含むいくつかの変数を定義します。私たちは テキストをGCP APIにテキストをストリーミングして変換するシミュレーションを行うことにしました。

このアプローチでは、パフォーマンス速度に顕著な違いが見られる。結果は、可能なトランスクリプションを含む配列である。私たちが欲しいのは最初の1つだけです:

results.first.alternatives.each do |alternatives|
    transcribed_text = alternatives.transcript
end

変数 transcribed_textには、発信者の音声メッセージのテキストが格納されます。メソッドの次のステップは、翻訳用に定義した言語を通して実行することです。メソッドが実行されるたびに翻訳されるテキストを、次に翻訳されるメッセージにしたいのです。 Translatorを通過するたびに翻訳されるテキストを、翻訳されたメッセージの次のイテレーションにしたいのです。こうすることで、最終的なメッセージは元のメッセージとはさらに違ったものになり、より楽しいものになります!

という新しい変数を作成する。 translated_textという新しい変数を作る。 transcribed_text.各反復の後、、 translated_textの値は現在の反復の翻訳に変わり、次の反復で翻訳されるテキストとして使用されます。最後に、最後の翻訳がトランスレーターを通してもう一度実行され、英語に戻される。これが呼び出し元に再生されるものである:

# Run Transcription Through Translations
puts "Translating Message"
translated_text = transcribed_text
LANGUAGES.each do |language|
    translated_text = (translated_text == transcribed_text) ? 
    Translator.translate(translated_text, to: language) : Translator.translate(translated_text.text, to: language)
end
final_translation = Translator.translate(translated_text.text, to: 'en')

上記のコードで注意すべき点は、三項演算子を使って translated_textの値が transcribed_text.これは、テキストがGoogle Cloud Platform Speech to Textオブジェクト(つまりドット記法)かそうでないかを区別してアクセスする必要があるためです。

メソッド内部での最後の仕事は、それを呼び出し元に再生することである。そのためには、会話IDを保持する @@uuid変数を使い、会話IDを保持し、テキストを音声テキストとして現在の呼び出しに注入する:

# Play Final Text Back To Call
puts "Playing Translated Audio to Call"
puts "Transcribed Original Message: #{transcribed_text}"
puts "Final Message: #{final_translation.text}"
closing_msg = "Your message was translated through Arabic, Hebrew, Hindi, Kurdish, Russian, Turkish and Yiddish and is returned to you as: #{final_translation.text}"
NexmoClient.calls.talk.instance_variable_set(:@host, 'api-us-1.nexmo.com')
NexmoClient.calls.talk.start(@@uuid, text: closing_msg, voice_name: "Kimberly") if transcribed_text != ''

コントローラのアクションが定義できたら、最後にアプリケーションのルートを作成します。

ルートの定義

ファイルを routes.rbファイルを /configフォルダ内のファイルを開いてください。これから GETPOSTをリクエストします:

# routes.rb

get '/answer', to: 'telephone#answer'
post '/event', to: 'telephone#event'

以上です!これでアプリケーションの作成は完了です。これでアプリケーションを実行する準備ができました。ngrokがバックグラウンドで実行されていることを確認し、ターミナルからRailsサーバーを起動します: rails s.これでNexmoの電話番号に電話して電話ごっこができます。楽しんでください!

シェア:

https://a.storyblok.com/f/270183/384x384/e5480d2945/ben-greenberg.png
Ben Greenbergヴォネージの卒業生

ベンはセカンドキャリアの開発者で、以前は成人教育、コミュニティ組織化、非営利団体運営の分野で10年を過ごした。彼はVonageの開発者支援者として働いていた。コミュニティ開発とテクノロジーの交差点について定期的に執筆している。南カリフォルニア出身で、長年ニューヨークに住んでいたが、現在はイスラエルのテルアビブ近郊に在住。