https://d226lax1qjow5r.cloudfront.net/blog/blogposts/play-the-game-of-telephone-with-the-nexmo-voice-api-ruby-on-rails-and-google-cloud-platform-dr/Game-of-Telephones_1200x675.jpg

Jouer au téléphone avec l'API Voice de Nexmo

Publié le May 14, 2021

Temps de lecture : 12 minutes

Vous souvenez-vous du jeu du téléphone lorsque vous étiez enfant ? Peut-être y avez-vous joué dans la cour de récréation de l'école ou dans un camp de vacances pendant l'été ? Je me souviens d'un enseignant qui utilisait ce jeu pour illustrer le manque de fiabilité des communications. Si vous n'y avez jamais joué, voici un petit récapitulatif :

Le téléphone commence lorsqu'une personne chuchote un message à son voisin. La deuxième personne chuchote le même message à la personne suivante, qui le partage avec son voisin, et ainsi de suite. Le jeu se poursuit jusqu'à ce que le message soit passé par toutes les personnes qui jouent et revienne à l'expéditeur initial. Souvent, le message final est très différent du message original.

Il existe d'innombrables tâches essentielles et critiques que les applications modernes de communication en nuage peuvent accomplir. Cependant, il est parfois amusant de faire une pause et de créer quelque chose d'amusant. Ainsi, dans ce guide, nous allons recréer le jeu du téléphone en utilisant Ruby on Rails, l'API Voice de Nexmo et les API Speech to Text et Translate de Google Cloud Platform.

Lorsque nous aurons terminé, nous disposerons d'une application vocale qui acceptera un appel téléphonique entrant, prendra un message original, le convertira en texte, le traduira dans plusieurs langues jusqu'à ce qu'il soit enfin retraduit en anglais, et diffusera le message final à l'appelant.

Vous pouvez également cloner sur votre machine locale une copie de travail de cette application à partir de GitHub.

Vous êtes prêts ? C'est parti !

Conditions préalables

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Pour commencer, vous avez besoin des éléments suivants :

Mise en place de ngrok

Il y a plusieurs façons de rendre notre serveur de développement local accessible à l'extérieur, mais l'une des façons les plus simples est d'utiliser ngrok. Vous pouvez lire cet article pour une explication plus détaillée du fonctionnement de ngrok. Cependant, pour nos besoins, nous avons juste besoin de le faire fonctionner et de copier l'URL qu'il nous fournit.

Pour démarrer ngrok, ouvrez une nouvelle fenêtre de terminal et exécutez la commande suivante :

ngrok http 3000

Vous verrez maintenant une interface d'enregistrement ngrok dans votre fenêtre de terminal. En haut de l'interface se trouve une ligne qui commence par Forwarding et qui contient deux URL. La première est l'URL de ngrok accessible de l'extérieur, qui se termine par ngrok.io suivie de http://localhost:3000qui est votre serveur de développement local. Maintenant, lorsque vous ou Nexmo contacte l'URL ngrok.io l'URL sera transmise à votre serveur local.

Veillez à copier l ngrok.io dans un endroit sûr. Nous l'utiliserons lors de la prochaine étape de configuration de notre compte Nexmo, de notre numéro de téléphone et de l'application Voice.

Création d'un compte Nexmo

Pour que notre application vocale fonctionne, nous avons besoin d'un compte Nexmo, d'un numéro de téléphone approvisionné par Nexmo, d'une application Nexmo et, enfin, nous devons lier notre application à notre numéro de téléphone.

Dans le menu de gauche, cliquez sur l'élément Voice menu dans le menu de gauche. Les quatre options suivantes s'affichent sous APPLICATIONS:

Create voice app

Cliquez sur l'option Create an application et vous serez dirigé vers une page où vous pourrez créer une nouvelle application Nexmo.

Complétez le formulaire avec les informations suivantes :

  • Application name champ de texte entrer rails-telephone-game

  • Event URL Dans le champ de texte, entrez l'URL de votre ngrok : https://[ngrok url here]/event

  • Answer URL Dans le champ de texte, entrez à nouveau l'URL de votre ngrok : https://[ngrok url here]/webhooks/answer

Une fois que vous avez terminé, cliquez sur le bouton bleu Create Application bleu.

Vous avez maintenant créé une application Nexmo Voice. La prochaine étape consiste à acheter un numéro de téléphone Nexmo et à le lier à cette application.

Dans le tableau de bord Nexmo, cliquez sur l'élément de menu Numbers dans le menu de gauche. Vous verrez apparaître trois options :

Numbers

Cliquez sur l'option Buy numbers et vous serez dirigé vers une page où vous pourrez choisir un pays, des caractéristiques, un type et les quatre chiffres que vous souhaitez voir figurer sur le numéro.

Buy numbers

Pour ce qui nous concerne : choisissez le pays dans lequel vous vous trouvez actuellement, afin que l'appel soit un appel local pour vous ; choisissez Voice pour les caractéristiques et mobile ou fixe pour le type. Vous n'avez pas besoin d'entrer quoi que ce soit dans le Number pour le champ de texte. Lorsque vous cliquez sur Searchvous verrez une liste de numéros de téléphone disponibles. Choisissez-en un en cliquant sur le bouton orange Buy et en cliquant à nouveau sur le bouton orange Buy à la demande de confirmation.

Une fois que vous possédez le numéro, vous pouvez maintenant le lier à votre rails-telephone-game Voice. Pour ce faire, cliquez sur l'icône d'engrenage à côté du numéro de téléphone et vous verrez le menu suivant :

Edit numbers conference call

Sélectionnez l'application voice-proxy-forwarding-demo Application dans la liste déroulante et cliquez sur le bouton bleu Ok bleu. Votre numéro de téléphone Numbers est maintenant lié à votre application Voice et prêt à accepter et à transférer les appels téléphoniques entrants via le proxy vocal.

Configuration d'un compte Google Cloud Platform

La documentation de Google Cloud Platform contient une excellente documentation sur la mise en place d'un nouveau compte.

En bref, une fois que vous avez créé un Account, vous devez créer une nouvelle application. Après avoir créé votre nouvelle application, vous devrez vous assurer de télécharger vos informations d'identification, qui se trouveront dans un fichier contenant du JSON. Enregistrez ce fichier dans un endroit dont vous vous souviendrez, car nous l'utiliserons bientôt. Nous discuterons de l'ajout de ce fichier, ainsi que de vos identifiants API Nexmo, en tant que variables d'environnement dans un instant.

Enfin, dans votre tableau de bord Google Cloud Platform, assurez-vous que vous avez activé l'API vocale et l'API de traduction. Nous utiliserons les deux dans notre jeu de téléphone.

Mise en place d'une application Rails

Nous sommes maintenant prêts à configurer notre application Rails pour jouer au téléphone. Nous allons procéder comme suit :

  1. Initialisation d'une nouvelle application

  2. Ajout des informations d'identification de l'API

  3. Définition des actions et des itinéraires du contrôleur

Initialisation d'une nouvelle application

Pour initialiser une nouvelle application, exécutez ce qui suit à partir de la ligne de commande :

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

Cela créera une nouvelle application Rails avec PostgreSQL comme base de données par défaut.

Une fois cette commande terminée, ouvrez l'application dans votre éditeur de code préféré et modifiez le fichier Gemfile dans le dossier racine de l'application. Nous allons ajouter les gemmes Nexmo Ruby, dotenv-rails, Google Cloud Platform Translate API et Google Cloud Platform Speech API :

# Gemfile

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

Après avoir sauvegardé le fichier Gemfilevous pouvez installer les gemmes en exécutant bundle install à partir de la ligne de commande.

Vous voudrez également exécuter rake db:migrate à ce stade pour initialiser le schéma de la base de données. Pour les besoins de cette démonstration, nous ne persistons pas les données, mais vous pouvez le faire vous-même.

Ajouter des informations d'identification pour l'API

Nous devons fournir les identifiants de l'API à la fois pour Nexmo et pour Google Cloud Platform dans notre application. Afin de gérer de manière sécurisée nos variables d'environnement, nous utiliserons la fonctionnalité de la gem dotenv-rails que nous avons installée. Tout d'abord, créez un nouveau fichier appelé .env dans le dossier racine de votre projet et ajoutez le chemin vers ce fichier à votre fichier .gitignore le chemin vers ce fichier. Ouvrez le fichier .env et ajoutez ce qui suit :

# .env

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

La première variable d'environnement, GOOGLE_APPLICATION_CREDENTIALSpointe vers le chemin d'accès au fichier JSON contenant vos informations d'identification pour l'API de Google. Déplacez le fichier que vous avez téléchargé lors de la création de votre Account dans le dossier racine de votre application et ajoutez le chemin d'accès à la droite du signe = (c.-à-d. GOOGLE_APPLICATION_CREDENTIALS=./my-google-cloud-platform-credentials.json).

La deuxième variable d'environnement, GOOGLE_PROJECT_IDest l'identifiant du projet Google Cloud Platform que vous avez créé lors de la procédure d'inscription (c'est-à-dire GOOGLE_PROJECT_ID=rails-telephone-game).

Les cinq variables d'environnement suivantes concernent votre Account Nexmo. Vous pouvez trouver vos variables NEXMO_API_KEY et NEXMO_API_SECRET sur la page principale de votre tableau de bord Nexmo.

La valeur de la variable NEXMO_NUMBER est le numéro de téléphone que vous avez fourni par Nexmo.

Le NEXMO_APPLICATION_ID se trouve dans la liste de vos Applications dans le tableau de bord Nexmo. Le NEXMO_PRIVATE_KEY est, comme les informations d'identification Google, le chemin d'accès aux informations d'identification de la clé privée de votre Account. Lorsque vous avez créé votre application vocale Nexmo, vous avez généré une paire de clés publique/privée, ce qui a automatiquement téléchargé la clé privée sur votre ordinateur. Déplacez la clé privée dans le dossier racine de votre application et ajoutez le chemin comme valeur de cette variable, exactement comme vous l'avez fait pour GOOGLE_APPLICATION_CREDENTIALS.

La dernière variable d'environnement est l'URL externe de ngrok, par exemple : http://my-sample-url.ngrok.io.

Maintenant que vos identifiants sont tous ajoutés à votre application, nous sommes maintenant prêts à créer notre Controller et nos Routes.

Définition des actions du contrôleur

Tout d'abord, créez un nouveau fichier dans /app/controllers/ appelé telephone_controller.rb. Le travail initial que nous ferons à l'intérieur de ce fichier est de définir des instances créditées du client Nexmo et du client 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

Nous allons également prendre le temps de définir la liste des langues dans lesquelles nous voulons que l'API de traduction de Google Cloud Platform fonctionne dans le jeu :

# telephone_controller.rb

class TelephoneController < ApplicationController

....

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

Pour ce guide, j'ai choisi l'arabe, l'hébreu, l'hindi, le kurde, le russe, le turc et le yiddish. Vous pouvez, bien sûr, remplacer ou ajouter n'importe quelle langue.

Nous avons besoin de deux actions définies dans notre contrôleur : #answer et #event. La méthode #answer est chargée de répondre à l'appel téléphonique, d'écouter et d'enregistrer le message de l'utilisateur, et de maintenir l'appel ouvert pendant son traitement. La méthode #event est chargée de transcrire l'enregistrement en texte, de le faire passer par le traducteur et de renvoyer le message nouvellement traduit à l'appelant.

La #answer méthode :

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

La méthode #answer est composée d'instructions Nexmo Call Control Object (NCCO) au format JSON. La première instruction envoie un message de bienvenue à l'appelant, la seconde enregistre la réponse de l'appelant et indique à Nexmo que l'URL de rappel est /event à l'URL ngrok de votre application. La troisième instruction fait savoir à l'appelant que le message est en cours de traitement, tandis que la quatrième instruction utilise la fonctionnalité Nexmo conversation de Nexmo pour maintenir l'appel ouvert pendant le traitement.

La méthode #event est enveloppée dans une vérification conditionnelle pour voir s'il y a un enregistrement audio. Le point de terminaison /event est consulté plusieurs fois par Nexmo avec différentes mises à jour d'état, mais nous ne nous intéressons qu'à celle qui contient un enregistrement :

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

Il se passe beaucoup de choses dans la méthode #event est riche en événements ; décomposons-les.

Initialement, une fois que les paramètres avec un recording_url est vrai, nous sauvegardons l'enregistrement localement. Nous utilisons ensuite l'API Speech to Text du GCP pour convertir l'enregistrement audio en texte transcrit.

Pour ce faire, nous définissons plusieurs variables contenant la configuration audio. Nous avons choisi de simuler un flux de texte vers l'API GCP pour conversion, plutôt que d'envoyer directement le fichier audio sauvegardé.

Il y a une différence positive notable dans la vitesse d'exécution avec cette approche. Le résultat est un tableau contenant les transcriptions possibles. Nous ne voulons que la première, et c'est ce que nous faisons ici :

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

La variable transcribed_text contient maintenant le texte du message audio de l'appelant. La prochaine étape de la méthode consiste à la faire passer par les langues que nous avons définies pour la traduction. Nous voulons que le texte qui est traduit à chaque fois qu'il passe par la méthode Translator soit la prochaine itération du message traduit. C'est ce qui rendra notre message final encore plus différent du message original et, par conséquent, plus amusant !

Pour ce faire, nous créons une nouvelle variable appelée translated_textqui est initialement définie par le contenu de transcribed_text. Après chaque itération, la valeur de , translated_textest remplacée par la traduction de l'itération en cours, qui est ensuite utilisée comme texte à traduire lors de l'itération suivante. Enfin, la dernière traduction est exécutée une dernière fois par le traducteur et retournée en anglais. C'est ce texte qui sera restitué à l'appelant :

# 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')

Un point à noter dans le code ci-dessus est que nous utilisons un opérateur ternaire pour vérifier si la valeur de translated_text est égale à celle de transcribed_text. Nous faisons cela parce que nous devons accéder au texte différemment s'il s'agit d'un objet Google Cloud Platform Speech to Text (c'est-à-dire avec la notation par points) ou non.

La dernière chose à faire à l'intérieur de la méthode est de la restituer à l'appelant. Pour ce faire, nous utilisons la variable @@uuid qui contient l'identifiant de la conversation et en injectant le texte sous forme de synthèse vocale dans l'appel en cours :

# 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 != ''

Une fois les actions de notre contrôleur définies, la dernière étape à réaliser avant d'être prêt est de créer les Routes de notre application.

Définir les itinéraires

Ouvrez le fichier routes.rb dans le dossier /config dans le dossier. Nous allons ajouter un GET et une POST demande :

# routes.rb

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

Et voilà ! Nous avons créé notre application avec succès. Nous sommes maintenant prêts à l'exécuter. Assurez-vous que ngrok tourne en arrière-plan, et démarrez votre serveur Rails depuis le terminal en exécutant : rails s. Vous pouvez maintenant appeler votre numéro de téléphone Nexmo et jouer au téléphone. Amusez-vous bien !

Partager:

https://a.storyblok.com/f/270183/384x384/e5480d2945/ben-greenberg.png
Ben GreenbergAnciens de Vonage

Ben est un développeur en seconde carrière qui a auparavant passé une décennie dans les domaines de la formation pour adultes, de l'organisation communautaire et de la gestion d'organisations à but non lucratif. Il a travaillé comme défenseur des développeurs pour Vonage. Il écrit régulièrement sur l'intersection du développement communautaire et de la technologie. Originaire de Californie du Sud et ayant longtemps vécu à New York, Ben réside aujourd'hui près de Tel Aviv, en Israël.