
Compartilhar:
Ben é um desenvolvedor que mudou de carreira, tendo atuado anteriormente por uma década nas áreas de educação de adultos, organização comunitária e gestão de organizações sem fins lucrativos. Ele trabalhou como representante de desenvolvedores na Vonage. Escreve regularmente sobre a interseção entre desenvolvimento comunitário e tecnologia. Natural do sul da Califórnia e morador de longa data da cidade de Nova York, Ben reside atualmente perto de Tel Aviv, em Israel.
Jogue o “Telefone sem fio” com a Voice API da Nexmo
Tempo de leitura: 12 minutos
Você se lembra do jogo do telefone sem fio de quando era criança? Talvez você tenha jogado no pátio da escola durante o recreio ou em um acampamento de verão? Lembro-me de um professor do ensino fundamental usando esse jogo para ilustrar como a comunicação pode ser pouco confiável. Se você nunca jogou, aqui vai um breve resumo:
O jogo do “telefone sem fio” começa quando uma pessoa sussurra uma mensagem para a pessoa ao seu lado. A segunda pessoa sussurra a mesma mensagem para a próxima pessoa, que, por sua vez, a repassa para a pessoa ao seu lado, e assim por diante. O jogo continua até que a mensagem complete seu percurso por todas as pessoas que estão participando e retorne ao remetente original. Muitas vezes, a mensagem final é bem diferente da original.
Existem inúmeras tarefas essenciais e críticas que as aplicações modernas de comunicação em nuvem podem realizar. No entanto, às vezes é simplesmente divertido fazer uma pausa e criar algo engraçado. Por isso, neste passo a passo, vamos recriar o jogo do telefone utilizando Ruby on Rails, a Voice API da Nexmo e as APIs de conversão de voz em texto e tradução do Google Cloud Platform.
Quando terminarmos, teremos um aplicativo de voz que aceitará uma chamada telefônica, receberá uma mensagem original, a converterá em texto, a traduzirá para vários idiomas até, por fim, traduzi-la de volta para o inglês e reproduzirá a mensagem final para o chamador.
Você também pode clonar em seu computador local uma cópia de trabalho deste aplicativo a partir de GitHub.
Pronto? Vamos começar!
Pré-requisitos
Para começar, você precisa do seguinte:
Configurando o ngrok
Existem várias maneiras de tornar nosso servidor de desenvolvimento local acessível externamente, mas uma das mais simples é usando o ngrok. Você pode ler este artigo para obter uma explicação mais detalhada sobre como o ngrok funciona. No entanto, para nossos propósitos, basta colocá-lo em funcionamento e copiar a URL que ele nos fornece.
Para iniciar o ngrok, abra uma nova janela do terminal e execute o seguinte na linha de comando:
Agora você verá uma interface de registro do ngrok na janela do seu terminal. Na parte superior da interface, há uma linha que começa com Forwarding e contém duas URLs. A primeira é a URL do ngrok acessível externamente, que termina com ngrok.io seguida por http://localhost:3000, que é o seu servidor de desenvolvimento local. Agora, quando você ou a Nexmo acessarem a ngrok.io URL, ela será redirecionada para o seu servidor local.
Certifique-se de copiar o ngrok.io URL em algum lugar seguro. Vamos usá-lo na próxima etapa da configuração da nossa conta Nexmo, do número de telefone e do aplicativo Voice.
Como criar um Account no Nexmo
Para que nosso aplicativo de voz funcione, precisamos de um Account Nexmo, um número de telefone provisionado pela Nexmo, um aplicativo Nexmo e, por fim, precisamos vincular nosso aplicativo ao nosso número de telefone.
No menu à esquerda, clique no Voice menu item. Você verá as quatro opções a seguir em APPLICATIONS:

Clique na Create an application opção e você será direcionado para uma página onde poderá configurar um novo aplicativo Nexmo.
Preencha o formulário com as seguintes informações:
Application namecampo de texto: Enterrails-telephone-gameEvent URLNo campo de texto, insira sua URL do ngrok:https://[ngrok url here]/eventAnswer URLNo campo de texto, digite novamente a URL do ngrok:https://[ngrok url here]/webhooks/answer
Quando terminar, clique no botão azul Create Application .
Você acabou de criar um aplicativo do Nexmo Voice. O próximo passo é adquirir um número de telefone do Nexmo e vinculá-lo a esse aplicativo.
No Painel do Nexmo, clique no Numbers item de menu no menu à esquerda. Você verá três opções aparecerem:

Clique na Buy numbers opção e você será direcionado para uma página onde poderá escolher o país, os recursos, o tipo e os quatro dígitos que deseja que o número tenha.

Para os nossos fins: escolha o país em que você está no momento, para que a ligação seja considerada local para você; escolha Voice “Recursos” e “Celular” ou “Fixo” como tipo. Você não precisa preencher nada no Number campo de texto. Ao clicar Search, você verá uma lista de números de telefone disponíveis. Escolha um clicando no botão laranja Buy e clicar no botão laranja Buy mais uma vez na janela de confirmação.
Depois de adquirir o número, você já pode vinculá-lo ao seu rails-telephone-game aplicativo Voice. Para isso, clique no ícone de engrenagem ao lado do número de telefone e você verá o seguinte menu:

Selecione a voice-proxy-forwarding-demo Applications na lista suspensa e clique no botão azul Ok . Seu número de telefone da Nexmo agora está vinculado ao seu aplicativo Voice e pronto para receber e encaminhar chamadas recebidas por meio do proxy de voz.
Como criar um Account no Google Cloud Platform
A documentação do Google Cloud Platform oferece excelentes orientações sobre como começar a usar uma nova Account.
Resumindo, depois de criar um Account, você precisa criar um novo aplicativo. Após criar seu novo aplicativo, certifique-se de baixar suas credenciais, que estarão em um arquivo no formato JSON. Salve o arquivo em um local que você consiga lembrar, pois vamos usá-lo em breve. Falaremos sobre como adicioná-lo, juntamente com suas credenciais da API do Nexmo, como variáveis de ambiente daqui a pouco.
Por fim, no painel do Google Cloud Platform, certifique-se de ter ativado tanto a API de Reconhecimento de Fala quanto a API de Tradução. Usaremos ambas no nosso jogo do telefone.
Configurando uma aplicação Rails
Agora estamos prontos para configurar nossa aplicação Rails para jogar “telefone sem fio”. Faremos o seguinte:
Inicializando um novo aplicativo
Adicionando nossas credenciais da API
Definindo ações e rotas do controlador
Inicializando um novo aplicativo
Para inicializar um novo aplicativo, execute o seguinte na linha de comando:
Isso criará uma nova aplicação Rails com o PostgreSQL como banco de dados padrão.
Assim que esse comando for concluído, abra o aplicativo no seu editor de código preferido e edite o arquivo Gemfile na pasta raiz do aplicativo. Vamos adicionar as gems Nexmo Ruby, dotenv-rails, Google Cloud Platform Translate API e Google Cloud Platform Speech API:
# Gemfile
gem 'nexmo'
gem 'dotenv-rails'
gem 'google-cloud-translate'
gem 'google-cloud-speech'Depois de salvar o Gemfile, você pode instalar as gems executando bundle install na linha de comando.
Você também vai querer executar rake db:migrate nesta etapa para inicializar o esquema do banco de dados. Para os fins deste passo a passo, não estamos salvando os dados, mas você pode fazer isso por conta própria.
Adicionar credenciais da API
Precisamos fornecer as credenciais da API tanto do Nexmo quanto do Google Cloud Platform em nosso aplicativo. Para gerenciar nossas variáveis de ambiente com segurança, utilizaremos a funcionalidade do dotenv-rails gem que instalamos. Primeiro, crie um novo arquivo chamado .env na pasta raiz do seu projeto e adicione o caminho para esse arquivo ao seu .gitignore arquivo. Abra o .env arquivo e adicione o seguinte:
# .env
GOOGLE_APPLICATION_CREDENTIALS=
GOOGLE_PROJECT_ID=
NEXMO_API_KEY=
NEXMO_API_SECRET=
NEXMO_NUMBER=
NEXMO_APPLICATION_ID=
NEXMO_PRIVATE_KEY=
BASE_URL=A primeira variável de ambiente, GOOGLE_APPLICATION_CREDENTIALS, aponta para o caminho do arquivo JSON que contém suas credenciais de API do Google. Vá em frente e mova o arquivo que você baixou ao configurar seu Account anteriormente para a pasta raiz do seu aplicativo e adicione o caminho à direita do = sinal (ou seja, GOOGLE_APPLICATION_CREDENTIALS=./my-google-cloud-platform-credentials.json).
A segunda variável de ambiente, GOOGLE_PROJECT_ID, é o ID do projeto do Google Cloud Platform que você criou ao realizar o processo de cadastro (ou seja, GOOGLE_PROJECT_ID=rails-telephone-game).
As próximas cinco variáveis de ambiente estão relacionadas à sua conta Nexmo. Você pode encontrar sua NEXMO_API_KEY e NEXMO_API_SECRET na página principal do seu painel do Nexmo.
O NEXMO_NUMBER valor da variável é o número de telefone que você configurou na Nexmo.
O NEXMO_APPLICATION_ID pode ser encontrado na lista de suas Applications no painel do Nexmo. O NEXMO_PRIVATE_KEY é, assim como as credenciais do Google, o caminho para as credenciais da chave privada da sua conta. Ao criar seu aplicativo de voz do Nexmo, você gerou um par de chaves pública/privada, o que baixou automaticamente a chave privada para o seu computador. Mova a chave privada para a pasta raiz do seu aplicativo e adicione o caminho como valor dessa variável, exatamente como você fez para GOOGLE_APPLICATION_CREDENTIALS.
A última variável de ambiente é a sua URL externa do ngrok, por exemplo: http://my-sample-url.ngrok.io.
Agora que todas as suas credenciais foram adicionadas ao seu aplicativo, estamos prontos para criar nosso controlador e nossas rotas.
Definindo ações do controlador
Primeiro, crie um novo arquivo em /app/controllers/ chamado telephone_controller.rb. O trabalho inicial que faremos nele é definir instâncias com credenciais do cliente Nexmo e do cliente do 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
Vamos também reservar um momento agora para definir a lista de idiomas que queremos que a API do Google Cloud Platform Translate processe no jogo:
# telephone_controller.rb
class TelephoneController < ApplicationController
....
LANGUAGES = [
'ar',
'he',
'hi',
'ku',
'ru',
'tr',
'yi'
]
Para este passo a passo, escolhi árabe, hebraico, hindi, curdo, russo, turco e iídiche. É claro que você pode substituir ou adicionar qualquer idioma que desejar.
Precisamos definir duas ações em nosso controlador: #answer e #event. O #answer método é responsável por atender a ligação, ouvir e gravar a mensagem do usuário e manter a ligação aberta enquanto ela é processada. O #event é responsável por transcrever a gravação em texto, submetê-la ao tradutor e devolver a mensagem recém-traduzida ao chamador.
O #answer método:
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
O #answer método é composto por instruções do Nexmo Call Control Object (NCCO) no formato JSON. A primeira instrução envia uma mensagem de boas-vindas ao chamador; a segunda registra a resposta do chamador e informa à Nexmo que a URL de retorno de chamada é /event na URL do ngrok do seu aplicativo. A terceira informa ao chamador que a mensagem está sendo processada, enquanto a quarta instrução usa o conversation para manter a chamada aberta enquanto o processamento ocorre.
O #event método está inserido em uma verificação condicional para verificar se há uma gravação de áudio. O /event ponto de extremidade é acessado várias vezes pela Nexmo com diferentes atualizações de status, mas estamos interessados apenas naquela que contém uma gravação:
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
Há muita coisa acontecendo no #event método; vamos analisar isso.
Inicialmente, assim que os parâmetros com um recording_url retornarem “true”, prosseguimos e salvamos essa gravação localmente. Em seguida, utilizamos a API Speech to Text do GCP para converter a gravação de áudio em texto transcrito.
Para isso, definimos várias variáveis que contêm a configuração de áudio. Optamos por simular o streaming de texto para a API do GCP para conversão, em vez de enviar diretamente o arquivo de áudio salvo.
Há uma diferença positiva perceptível na velocidade de execução com essa abordagem. O resultado é uma matriz contendo as transcrições possíveis. Queremos apenas a primeira, e é isso que estamos fazendo aqui:
results.first.alternatives.each do |alternatives|
transcribed_text = alternatives.transcript
endA variável transcribed_text agora contém o texto da mensagem de áudio do chamador. Nosso próximo passo no método é processá-la pelos idiomas que definimos para tradução. Queremos que o texto que seja traduzido a cada vez que passar pelo Translator seja a próxima iteração da mensagem traduzida. É isso que tornará nossa mensagem final ainda mais diferente da mensagem original e, portanto, mais divertida!
Para isso, criamos uma nova variável chamada translated_text, que é inicialmente definida pelo conteúdo de transcribed_text. Após cada iteração, translated_texto valor de passa a ser a tradução da iteração atual, que é então usada como o texto a ser traduzido na próxima iteração. Por fim, a última tradução é processada uma última vez pelo tradutor e convertida de volta para o inglês. É isso que será reproduzido para quem fez a chamada:
# 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')Um ponto a ser observado no código acima é que utilizamos um operador ternário para verificar se o valor de translated_text é igual a esse transcribed_text. Fazemos isso porque precisamos acessar o texto de maneira diferente dependendo se ele é um objeto do Google Cloud Platform Speech to Text (ou seja, com notação de ponto) ou não.
A última etapa dentro do método é reproduzir a mensagem para quem ligou. Fazemos isso usando a @@uuid variável, que contém o ID da conversa, e inserindo o texto como sintetização de voz na chamada atual:
# 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 != ''Depois de definirmos as ações do nosso Controller, a etapa final que precisamos realizar antes de estarmos prontos é criar as rotas do nosso aplicativo.
Definindo as rotas
Abra o routes.rb arquivo na /config pasta. Vamos adicionar um GET e uma POST solicitação:
# routes.rb
get '/answer', to: 'telephone#answer'
post '/event', to: 'telephone#event'É isso aí! Criamos nosso aplicativo com sucesso. Agora estamos prontos para executá-lo. Certifique-se de que o ngrok esteja rodando em segundo plano e inicie seu servidor Rails no terminal executando: rails s. Agora você pode ligar para o seu número de telefone da Nexmo e brincar de telefone. Divirta-se!
Compartilhar:
Ben é um desenvolvedor que mudou de carreira, tendo atuado anteriormente por uma década nas áreas de educação de adultos, organização comunitária e gestão de organizações sem fins lucrativos. Ele trabalhou como representante de desenvolvedores na Vonage. Escreve regularmente sobre a interseção entre desenvolvimento comunitário e tecnologia. Natural do sul da Califórnia e morador de longa data da cidade de Nova York, Ben reside atualmente perto de Tel Aviv, em Israel.