
Compartilhar:
Ex-educador de desenvolvedores na @Vonage. Tenho formação em PHP, mas não me limito a uma única linguagem. Sou um jogador ávido e entusiasta do Raspberry Pi. Costumo praticar escalada em rocha em centros de escalada indoor.
Crie uma cápsula do tempo de parabéns de aniversário com o Go
Tempo de leitura: 15 minutos
Introdução
Com a pandemia, às vezes fomos obrigados a interagir virtualmente com nossa família e nossos amigos. Mas, mesmo com a pandemia em curso, nossas vidas continuaram. As pessoas ainda estão se casando; os aniversários ainda acontecem uma vez por ano para todos.
Então, quando chegou o meu aniversário, isso também me fez lembrar de algo que minha avó costumava fazer todos os anos. Ela me ligava logo de manhã cedo e cantava “Parabéns a você” para mim pelo telefone.
Essa lembrança me deu a ideia de criar uma cápsula do tempo de aniversário, na qual todos os seus amigos e familiares ligariam para um número. Ao ligarem, eles poderiam deixar seus votos de felicidades em forma de gravação de voz. Assim, você receberia uma ligação e ouviria todas as gravações com votos de felicidades em uma data e hora pré-determinadas.
Pré-requisitos
Para concluir este tutorial, você precisará de:
Criar um túnel Ngrok
Ao fazer ou receber chamadas de voz, a Vonage enviará uma solicitação HTTP para as URLs de webhook pré-configuradas. Seu aplicativo deve estar acessível pela internet para recebê-la; por isso, recomendamos usar o Ngrok.
Inicie o ngrok com o seguinte comando:
Certifique-se de copiar sua URL HTTPS do ngrok, pois você precisará dela mais tarde, ao configurar o projeto.
Criar uma aplicação da Vonage com webhooks
Este projeto dependerá da escuta das solicitações de webhook recebidas feitas pelas APIs da Vonage; portanto, precisaremos criar um novo aplicativo. Vá em frente e crie um novo aplicativo com as seguintes informações:
Nome — pode ser o que você quiser; é um nome que só você verá
Recursos
Voice
Em
Answer URLadicionar:<your ngrok url>/webhooks/answerEm
Event URLadicionar:<your ngrok url>/webhooks/event
RTC (chamadas de voz e mensagens no aplicativo)
Em
Event URLadicionar:<your ngrok url>/webhooks/event
Clique em “Gerar chave pública e privada” e mova o
private.keyarquivo para o diretório do seu projeto.Clique em “Salvar alterações”
Seu aplicativo já está pronto para enviar a você quaisquer webhooks predefinidos!
Observação Se você estiver usando o ngrok sem um Account,
<your ngrok url>será diferente a cada vez que você executar o ngrok. Lembre-se de atualizar as URLs dos seus webhooks sempre que executar o comando. Como alternativa, cadastre-se para obter um Account gratuito e garantir que a URL permaneça a mesma.
Coletar gravações de voz
A primeira etapa deste projeto consiste em receber as gravações de voz das pessoas que enviaram mensagens de apoio.
Instalar os pacotes necessários
Precisaremos de várias bibliotecas Go de terceiros para executar este projeto com sucesso. Entre elas estão as seguintes:
joho/godotenv- para armazenar com segurança nossas credenciais da Vonagevonage/vonage-go-sdk- para realizar nossas solicitações de API na Vonagegormesqlitearmazenar os nomes dos arquivos de mensagens de voz e se eles já foram reproduzidos em um banco de dados SQLite
Para instalar essas bibliotecas de terceiros, execute os quatro comandos a seguir:
go get github.com/joho/godotenv
go get github.com/vonage/vonage-go-sdk
go get gorm.io/gorm
go get gorm.io/driver/sqlitePara utilizar joho/gotdotenv pacote e começar a armazenar suas credenciais em um arquivo, crie seu .env arquivo no diretório do seu projeto e adicione as seguintes variáveis:
Certifique-se de preencher essas variáveis com os valores corretos que você obteve nas etapas anteriores. Veja a seguir uma lista de como obter todos os valores necessários:
VONAGE_APPLICATION_ID- O ID do seu aplicativo é o ID atribuído quando você criou um aplicativo no painel de controle da VonageVONAGE_PRIVATE_KEY- A localização doprivate.keyarquivo em relação ao diretório do projetoVONAGE_NUMBER- Seu número da Vonage é o número de telefone virtual que você adquiriu no Painel de Controle da VonageTO_NUMBER- O número que receberá a ligação com todas as gravações de voz na data e hora que você definirPERSON_NAME- O nome da pessoa que receberá esses votos de felicidadesNGROK_URL- A URL do ngrok que você recebeu e salvou em uma etapa anterior
Estruturas são coleções tipadas de campos que usaremos para agrupar dados de solicitações de webhook ao longo deste tutorial. Crie um novo arquivo chamado structs.go e adicione o seguinte:
package main
type Dtmf struct {
Digits string
Timed_out bool
}
type EventResponse struct {
Conversation_id string
Type string
Body EventBodyResponse
}
type EventBodyResponse struct {
Channel EventBodyChannelResponse
}
type EventBodyChannelResponse struct {
Id string
Type string
}
type Recording struct {
Start_time string
Recording_url string
Size int
Recording_uuid string
End_time string
Conversation_uuid string
Timestamp string
}
type Response struct {
Speech []string
Dtmf Dtmf
From string
To string
Uuid string
Conversation_uuid string
Timestamp string
}Agora que já criamos algumas das partes mais enfadonhas para dar início ao projeto, vamos criar o arquivo principal do projeto, main.go, no diretório do seu projeto e vamos adicionar o seguinte código a ele:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
"github.com/vonage/vonage-go-sdk/jwt"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
connectDb()
http.ListenAndServe(":8080", nil)
}O código do exemplo acima representa a estrutura inicial do projeto. Atualmente, ele carrega o .env arquivo no projeto e cria um servidor web que escuta na porta 8080.
Criação do modelo de banco de dados
Para salvar o nome dos arquivos de áudio e se eles foram reproduzidos ou não, precisaremos criar um banco de dados. Vamos criar o modelo BirthdayEntry e uma função connectDb() para lidar com a conexão ao nosso banco de dados. Crie um novo arquivo chamado models.go e adicione o seguinte código:
package main
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var db *gorm.DB
var err error
type BirthdayEntry struct {
gorm.Model
FileName string
Played bool
}
func connectDb() {
db, err = gorm.Open(sqlite.Open("voiceRecordings.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&BirthdayEntry{})
}
Como atender uma chamada
O processo de gravação de uma mensagem de voz envolverá várias etapas. A primeira delas consistirá em atender a chamada inicial e instruir as APIs da Vonage sobre o que fazer a seguir. Portanto, crie um novo arquivo no diretório do seu projeto chamado recording.go e adicione o seguinte:
package main
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strconv"
"time"
"github.com/vonage/vonage-go-sdk"
"github.com/vonage/vonage-go-sdk/ncco"
"github.com/vonage/vonage-go-sdk/jwt"
)
func answer(w http.ResponseWriter, req *http.Request) {
MyNcco := ncco.Ncco{}
talk := ncco.TalkAction{Text: "Thank you for calling the birthday congratulations hotline for " + os.Getenv("PERSON_NAME") + ".. If you would like to leave a message, please press 1. Otherwise end the call. Thank you"}
MyNcco.AddAction(talk)
inputAction := ncco.InputAction{EventUrl: []string{"https://" + req.Host + "/webhooks/record"}, Dtmf: &ncco.DtmfInput{MaxDigits: 1}}
MyNcco.AddAction(inputAction)
data, _ := json.Marshal(MyNcco)
w.Header().Set("Content-Type", "application/json")
w.Write(data)
}
A funcionalidade acima criará um novo Objeto de Controle de Chamada (NCCO) com duas ações a serem executadas. A primeira ação será “Falar”, convertendo um texto predefinido em Voice, e a segunda será processar a entrada do usuário por meio do Dual Tone Multi-Frequency (DTMF), com outro URL de webhook predefinido.
Essas ações são então convertidas em um objeto JSON e retornadas na solicitação.
Essa função não está sendo usada no momento, então vamos mudar isso! Voltando main.go dentro da main() função, adicione a seguinte linha de código, que instrui o servidor web a ficar à espera da URL webhooks/answere, quando acionada, chame a answer() função:
// First Step - Answer phone call
http.HandleFunc("/webhooks/answer", answer) Gravação da ligação
Durante uma chamada de voz, o RecordAction no NCCO é acionado e começa a gravar tudo o que o seu microfone captar. Ao acionar o RecordAction, é necessário definir a URL do webhook para receber os detalhes do arquivo gravado após o término da chamada.
Para iniciar uma gravação, você precisará primeiro registrar duas novas rotas no seu servidor web. No seu main.go arquivo, abaixo da chamada à answer função, adicione as duas linhas a seguir:
// Second Step - Take Voice Recording
http.HandleFunc("/webhooks/record", recordUsersMessage)
// Third Step - Receive Voice Recording confirmation + Download the file
http.HandleFunc("/webhooks/recording-file", getFileRecording)No seu recording.go arquivo, uma das funções que você definiu na etapa acima é a recordUsersMessage() função, acionada quando o usuário insere sua resposta DTMF na chamada (pressionando 1, por exemplo). Essa função criará um novo NCCO, que primeiro converterá um texto em fala, agradecendo ao usuário, e depois solicitará que ele deixe uma mensagem após o tom.
A segunda ação é uma RecordAction, que instrui a API a gravar tudo o que for dito após o tom. Adicione essa nova função ao seu arquivo:
func recordUsersMessage(w http.ResponseWriter, req *http.Request) {
data, _ := ioutil.ReadAll(req.Body)
var response Response
json.Unmarshal(data, &response)
MyNcco := ncco.Ncco{}
talk := ncco.TalkAction{Text: "Thank you. Please leave a message after the tone."}
MyNcco.AddAction(talk)
recordAction := ncco.RecordAction{EventUrl: []string{"https://" + req.Host + "/webhooks/recording-file"}, Format: "mp3", BeepStart: true, EndOnSilence: 10}
MyNcco.AddAction(recordAction)
responseData, _ := json.Marshal(MyNcco)
w.Header().Set("Content-Type", "application/json")
w.Write(responseData)
}
Salvar o arquivo de áudio
Assim que a gravação de voz for concluída, uma chamada para o /webhooks/recording-file caminho é acionada com JSON, semelhante ao exemplo abaixo:
{
"start_time": "2020-01-01T12:00:00Z",
"recording_url": "https://api.nexmo.com/v1/files/aaaaaaaa-bbbb-cccc-dddd-0123456789ab",
"size": 12345,
"recording_uuid": "aaaaaaaa-bbbb-cccc-dddd-0123456789ab",
"end_time": "2020-01-01T12:01:00Z",
"conversation_uuid": "bbbbbbbb-cccc-dddd-eeee-0123456789ab",
"timestamp": "2020-01-01T14:00:00.000Z"
}Neste exemplo de JSON, podemos ver o recording_url, que é essencial para que nosso tutorial funcione. Essa URL de gravação é protegida; você precisa gerar um JSON Web Token (JWT) e fornecê-lo na GET solicitação ao baixar esse arquivo de gravação.
O primeiro passo é criar uma nova linha no banco de dados para esse arquivo, gerar o nome do arquivo (timestamp do Unix) e chamar a downloadFile() função. Em seguida, no seu recordings.go arquivo, adicione a seguinte função:
func getFileRecording(w http.ResponseWriter, req *http.Request) {
data, _ := ioutil.ReadAll(req.Body)
var recording Recording
json.Unmarshal(data, &recording)
responseData, _ := json.Marshal(data)
fileName := strconv.FormatInt(time.Now().UTC().UnixNano(), 10) + ".mp3"
err := downloadFile(recording.Recording_url, fileName)
if err != nil {
log.Fatal(err)
}
birthdayEntry := BirthdayEntry{FileName: fileName, Played: false}
_ = db.Create(&birthdayEntry)
w.Header().Set("Content-Type", "application/json")
w.Write(responseData)
}
Baixando o arquivo
Você deve ter notado que ainda não temos a downloadFile() função mencionada no exemplo acima. Nosso próximo passo é adicioná-la, bem como outra função para gerar nosso JWT. O JWT precisa ser passado como um cabeçalho na solicitação.
Adicione o seguinte ao seu recordings.go arquivo. Essa ação fará o download do arquivo de áudio dos servidores da Vonage e o salvará como um arquivo no recordings diretório com o nome de arquivo pré-definido.
func downloadFile(audioUrl string, fileName string) error {
//Get the response bytes from the url
reqUrl, _ := url.Parse(audioUrl)
token := generateJWT()
request := &http.Request{
Method: "GET",
URL: reqUrl,
Header: map[string][]string{
"Authorization": {"Bearer " + token},
},
}
response, err := http.DefaultClient.Do(request)
if err != nil {
log.Fatal("Error:", err)
}
defer response.Body.Close()
if response.StatusCode != 200 {
return errors.New("received non 200 response code")
}
file, err := os.Create("./recordings/" + fileName)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, response.Body)
if err != nil {
return err
}
return nil
}
Ainda não geramos nosso token JWT! Portanto, usando o SDK do Go da Vonage, adicione a seguinte função a recordings.go. Essa função usa seu VONAGE_APPLICATION_ID e suas VONAGE_PRIVATE_KEY_PATH variáveis de ambiente para gerar um novo JWT.
func generateJWT() string {
applicationId := os.Getenv("VONAGE_APPLICATION_ID")
privateKey, _ := ioutil.ReadFile(os.Getenv("VONAGE_PRIVATE_KEY_PATH"))
g := jwt.NewGenerator(applicationId, privateKey)
token, _ := g.GenerateToken()
return token
}É isso aí quanto à parte do sistema que capta as chamadas de voz; antes de passarmos para a segunda metade do tutorial, vamos testar essa parte do início ao fim.
Primeiro, certifique-se de que seu projeto esteja em execução. No Terminal, dentro do diretório do seu projeto, execute o comando:
Você ainda deve ter o ngrok em execução; então, vá em frente e ligue para o seu número virtual da Vonage usando seu celular.
A primeira resposta é a seguinte mensagem de voz: “Obrigado por ligar para a linha direta de parabéns de aniversário para <insert name here>.. Se você quiser deixar uma mensagem, pressione 1. Caso contrário, encerre a ligação. Obrigado”.
Se você digitar o número 1 no teclado, ouvirá: “Obrigado. Por favor, deixe uma mensagem após o sinal”. Agora, grave-se dizendo algumas palavras e desligue.
Alguns segundos após o término da ligação, verifique seu recordings diretório. Você verá que um novo arquivo foi criado.
É hora de montar a parte do sistema destinada ao aniversariante!
Ligando para o aniversariante
Crie uma tarefa Cron e parabenize
Este projeto precisa de um método para executar uma das funções em uma data e hora específicas.
O cron job é um agendador de tarefas nos sistemas operacionais Unix. Este projeto utilizará uma biblioteca cron para Go a fim de definir uma data e hora específicas para a execução de uma determinada função.
No Terminal, execute o comando abaixo para instalar essa biblioteca do cron:
Dentro da sua main() função dentro do seu main.go vamos chamar uma função que ainda precisa ser criada, runCongratulateCron(), então adicione isso abaixo da parte em que você chama connectDb():
runCongratulateCron()Para manter a funcionalidade separada da primeira parte do tutorial, vamos adicionar a funcionalidade necessária para esta parte em um arquivo separado. Crie um novo arquivo chamado congratulate.go e adicione o seguinte código:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/robfig/cron"
"github.com/vonage/vonage-go-sdk"
"github.com/vonage/vonage-go-sdk/ncco"
)
func runCongratulateCron() {
c := cron.New()
// This would be triggered at midnight on 1st Jan
c.AddFunc("0 0 0 1 1 *", func() {
congratulate()
})
c.Start()
}
func congratulate(w http.ResponseWriter, req *http.Request) {
privateKey, _ := ioutil.ReadFile(os.Getenv("VONAGE_PRIVATE_KEY_PATH"))
auth, _ := vonage.CreateAuthFromAppPrivateKey(os.Getenv("VONAGE_APPLICATION_ID"), privateKey)
client := vonage.NewVoiceClient(auth)
from := vonage.CallFrom{Type: "phone", Number: os.Getenv("VONAGE_NUMBER")}
to := vonage.CallTo{Type: "phone", Number: os.Getenv("TO_NUMBER")}
MyNcco := ncco.Ncco{}
talkAction := ncco.TalkAction{Text: "Happy Birthday! I have collected a number of recordings from your friends and family wishing you a happy birthday. If you would like to listen to this, please press 1."}
MyNcco.AddAction(talkAction)
inputAction := ncco.InputAction{EventUrl: []string{"https://" + os.Getenv("NGROK_URL") + "/webhooks/play-audio"}, Dtmf: &ncco.DtmfInput{MaxDigits: 1}}
MyNcco.AddAction(inputAction)
conversationAction := ncco.ConversationAction{Name: os.Getenv("TO_NUMBER"), StartOnEnter: "false"}
MyNcco.AddAction(conversationAction)
client.CreateCall(vonage.CreateCallOpts{From: from, To: to, Ncco: MyNcco})
}
O código acima tem duas funções.
Primeiro, a runCongratulateCron() função define uma nova tarefa cron e adiciona o horário especificado para que a pessoa que está fazendo aniversário receba a ligação. Se você não souber como configurar os horários em uma tarefa cron, consulte o Crontab Guru para criar sua configuração de horário personalizada.
A segunda função é chamada a partir da primeira e, com isso, realiza a chamada de Voice com o recurso de conversão de texto em fala para a pessoa que está fazendo aniversário e, em seguida, solicita uma ação de entrada (“Pressione 1 para continuar”). Para manter a chamada ativa para o destinatário, é ConversationAction é necessário. Vamos aprender como reproduzir o áudio na chamada na próxima etapa, mas isso precisa ser feito durante uma conversa ativa.
Reproduzir áudio durante uma chamada
Agora que temos uma chamada, precisamos adicionar o código para reproduzir os arquivos de áudio durante a chamada Voice. Para isso, você precisará obter o UUID e passá-lo em uma solicitação que chame a PlayAudioStream função, juntamente com a URL do arquivo que você deseja reproduzir primeiro.
Observação que não é possível colocar os arquivos de áudio em fila. Se você reproduzir cada arquivo de áudio em loop durante a chamada, o arquivo mais recente interromperá a reprodução do anterior. Para evitar isso, precisamos reproduzir o arquivo e, em seguida, aguardar a ocorrência de um evento ao final da reprodução. Em seguida, localizamos o próximo arquivo ainda não reproduzido no banco de dados e o reproduzimos assim que a reprodução do arquivo anterior for concluída.
Então, em congratulate.go adicionar o seguinte código:
func congratulatePlayAudio(w http.ResponseWriter, req *http.Request) {
data, _ := ioutil.ReadAll(req.Body)
var response Response
json.Unmarshal(data, &response)
playAudio(response.Uuid, req.Host)
}
func playAudio(uuid string, host string) {
var birthdayEntry BirthdayEntry
privateKey, _ := ioutil.ReadFile(os.Getenv("VONAGE_PRIVATE_KEY_PATH"))
auth, _ := vonage.CreateAuthFromAppPrivateKey(os.Getenv("VONAGE_APPLICATION_ID"), privateKey)
client := vonage.NewVoiceClient(auth)
if err := db.First(&birthdayEntry, "played = ?", false).Error; err != nil {
client.PlayTts(uuid, "This is the end of your birthday wishes, you may now hang up.", vonage.PlayTtsOpts{})
return
}
fmt.Println("https://" + host + "/" + birthdayEntry.FileName)
result, _, _ := client.PlayAudioStream(uuid,
"https://"+host+"/"+birthdayEntry.FileName,
vonage.PlayAudioOpts{},
)
birthdayEntry.Played = true
db.Save(&birthdayEntry)
fmt.Println("Update message: " + result.Message)
}
Em main.go encontre a linha http.HandleFunc("/webhooks/recording-file", getFileRecording) e adicione o seguinte:
http.HandleFunc("/congratulate", congratulate)
http.HandleFunc("/webhooks/play-audio", congratulatePlayAudio) Acionar a solicitação para reproduzir o próximo arquivo de áudio
Conforme discutido anteriormente, precisamos reproduzir o próximo arquivo de áudio na chamada assim que o anterior for concluído. Usando a URL do webhook definida anteriormente em: RTC (In-app voice & messaging) no painel, vamos monitorar um evento específico que contenha uma determinada chave na solicitação. Ao analisar a event.type parte da solicitação, poderemos verificar se o valor é: audio:play:done, e então chamar a função playAudio para localizar o próximo arquivo de áudio ainda não reproduzido.
Aqui dentro congratulate.go adicione esta nova event função:
func event(w http.ResponseWriter, req *http.Request) {
var event EventResponse
err := json.NewDecoder(req.Body).Decode(&event)
if err != nil {
return
}
if event.Type == "audio:play:done" {
playAudio(event.Body.Channel.Id, req.Host)
}
}
Em seguida, em main.go, abaixo da linha http.HandleFunc("/webhooks/play-audio", congratulatePlayAudio) , adicione:
http.HandleFunc("/webhooks/event", event)É isso aí! Acabamos de criar nossa cápsula do tempo das comemorações de aniversário com o Go! A seguir, vamos explicar passo a passo como testar a funcionalidade.
Experimente!
Agora que já desenvolvemos este projeto, vamos traçar o processo do início ao fim:
Quem quiser enviar mensagens de felicitações pode ligar para o seu número virtual da Vonage
Seu aplicativo atende a chamada com uma mensagem gerada por sintetizador de voz: “Obrigado por ligar para a linha direta de parabéns de aniversário para
<insert name here>..Se você quiser deixar uma mensagem, pressione 1. Caso contrário, encerre a ligação. Obrigado”O aplicativo aguarda que você digite um número no teclado.
O próximo webhook recebe uma solicitação e envia uma mensagem gerada pelo recurso de conversão de texto em fala: “Obrigado. Por favor, deixe uma mensagem após o sinal.”
Ouve-se um bipe, e a ligação passa a gravar tudo o que for captado pelo seu microfone.
Você encerra a ligação quando terminar.
No entanto, muitos interessados podem repetir as etapas 1 a 6.
No horário especificado (definido na
runCongratulateCron()função), a funçãocongratulate()é chamada.The application for the person celebrating their birthday makes a call out.
Ao atender a ligação, o destinatário ouve a seguinte mensagem: “Feliz Aniversário! Reuni várias gravações de seus amigos e familiares desejando-lhe um feliz aniversário. Se quiser ouvir, pressione 1.”
A chamada está agora aguardando que o destinatário digite um número no teclado.
O aplicativo irá, então, recuperar o primeiro arquivo de áudio ainda não reproduzido do banco de dados e transmiti-lo durante a chamada de voz.
Ao término da transmissão do arquivo de áudio, um evento é enviado de volta ao aplicativo. Quando esse evento é recebido, o aplicativo localiza o próximo arquivo de áudio ainda não reproduzido e o transmite durante a chamada.
Quando não houver mais arquivos de áudio por reproduzir, a chamada é encerrada.
Você acabou de integrar uma cápsula do tempo para comemorações de aniversário ao Go, usando a Voice API da Vonage. O exemplo fornecido é apenas uma das muitas maneiras de usar a Voice API.
Se este tutorial despertou seu interesse pela nossa Voice API, mas Go não é a linguagem de sua preferência, você pode encontrar outros tutoriais em várias linguagens ou sobre diversos serviços aqui no blog da Vonage, como:
Se você tiver alguma dúvida, sugestão ou ideia que gostaria de compartilhar com a comunidade, fique à vontade para participar do nosso espaço de trabalho do Slack da Comunidadeou entre em contato comigo pelo Twitter. Adoraria receber feedback de quem tiver colocado este tutorial em prática e saber como está o seu projeto.