
Compartir:
Antiguo educador de desarrolladores @Vonage. Procedente de PHP, pero no limitado a un solo lenguaje. Un ávido jugador y un entusiasta de Raspberry pi. A menudo se le encuentra practicando escalada en rocódromo.
Construye una cápsula del tiempo para felicitaciones de cumpleaños con Go
Tiempo de lectura: 15 minutos
Introducción
Con la pandemia, a veces nos hemos visto obligados a interactuar virtualmente con nuestros familiares y amigos. Pero incluso con una pandemia en marcha, nuestras vidas han continuado. La gente se sigue casando, todos cumplen años una vez al año.
Así que cuando llegó mi cumpleaños, también me acordé de algo que mi abuela solía hacer todos los años. Me llamaba a primera hora de la mañana y me cantaba el cumpleaños feliz por teléfono.
Este recuerdo despertó en mi cabeza la idea de crear una cápsula del tiempo de cumpleaños en la que todos tus amigos y familiares llamen a un número. Cuando llamen, pueden dejar sus buenos deseos en una grabación de voz. Entonces, recibirías una llamada y escucharías todas las grabaciones de buenos deseos en una fecha y hora predeterminadas.
Requisitos previos
Para completar este tutorial necesitarás
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.
Crear el túnel de Ngrok
Al realizar o recibir llamadas de voz, Vonage enviará una solicitud HTTP a tus URL de webhook preconfiguradas. Tu aplicación debe ser accesible a Internet para recibirla, por lo que recomendamos usar Ngrok.
Ejecute ngrok con el siguiente comando:
Asegúrate de copiar la URL HTTPS de ngrok, ya que la necesitarás más adelante cuando configures el proyecto.
Crear aplicaciones de Vonage con Webhooks
Este proyecto se basará en escuchar las solicitudes de webhook entrantes realizadas por las API de Vonage, por lo que necesitaremos crear una nueva aplicación. Continúa y crea una nueva aplicación con la siguiente entrada:
Nombre - puede ser cualquier cosa que desee; es un nombre que sólo usted verá
Capacidades
Voice
En
Answer URLañadir:<your ngrok url>/webhooks/answerEn
Event URLañadir:<your ngrok url>/webhooks/event
RTC (voz y mensajería dentro de la aplicación)
En
Event URLañadir:<your ngrok url>/webhooks/event
Haga clic en "Generar clave pública y privada" y mueva el archivo
private.keyal directorio del proyecto.Haga clic en "Guardar cambios".
Tu aplicación ya está lista para enviarte cualquier webhook predefinido.
Nota Si utiliza ngrok sin una Account,
<your ngrok url>será diferente cada vez que ejecutes ngrok. Recuerda actualizar las URLs de tus webhooks cada vez que ejecutes el comando. Alternativamente, regístrate en una Account gratuita para que la URL persista.
Recopilar grabaciones de voz
La primera parte de este proyecto consiste en recibir las grabaciones de voz de los bienhechores.
Instalar los paquetes necesarios
Necesitaremos varias librerías Go de terceros para ejecutar con éxito este proyecto. Estas incluyen las siguientes:
joho/godotenv- para almacenar de forma segura nuestras credenciales de Vonagevonage/vonage-go-sdk- para realizar nuestras solicitudes API en Vonagegormysqlitepara almacenar los nombres de los archivos de mensajes de voz y si se han reproducido en una base de datos SQLite
Para instalar estas bibliotecas de terceros, ejecute los cuatro comandos siguientes:
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 el paquete joho/gotdotenv y empezar a almacenar tus credenciales en un archivo, crea tu archivo .env en el directorio de tu proyecto y añade las siguientes variables:
Asegúrese de rellenar estas variables con los valores correctos que ha recopilado en los pasos anteriores. A continuación se muestra una lista de cómo obtener todos los valores necesarios:
VONAGE_APPLICATION_ID- Tu ID de aplicación es el ID que recibiste cuando creaste una aplicación en el tableroVONAGE_PRIVATE_KEY- La ubicación del archivoprivate.keycorrespondiente al directorio del proyectoVONAGE_NUMBER- Tu número de Vonage es el número de teléfono virtual que compraste en el Panel de VonageTO_NUMBER- El número que recibirá la llamada con todas las grabaciones de voz en la fecha y hora predeterminadas por ustedPERSON_NAME- El nombre de la persona que recibirá estos buenos deseosNGROK_URL- La URL ngrok que recibió y almacenó en un paso anterior
Los Structs son colecciones tipificadas de campos que utilizaremos para agrupar datos de peticiones webhook a lo largo de este tutorial. Crea un nuevo archivo llamado structs.go y añade lo siguiente:
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
}Ahora que hemos creado algunas de las partes aburridas para empezar, vamos a crear el archivo principal del proyecto, main.goen el directorio del proyecto y añádele el siguiente código:
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)
}El código del ejemplo anterior es la estructura inicial del proyecto. Actualmente carga el archivo .env en el proyecto y crea un servidor web que escucha en el puerto 8080.
Creación del modelo de base de datos
Para guardar el nombre de archivo de los ficheros de audio y si se han reproducido o no, necesitaremos crear una base de datos. Vamos a crear el modelo BirthdayEntry y una función connectDb() para manejar la conexión a nuestra base de datos. Crear un nuevo archivo llamado models.go y añade el siguiente 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{})
}
Cómo responder a una llamada
Habrá varios pasos en el proceso de grabación de un mensaje de voz. El primero responderá la llamada inicial e indicará a las API de Vonage qué hacer a continuación. Por lo tanto, crea un nuevo archivo en el directorio de tu proyecto llamado recording.go y agrega lo siguiente:
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)
}
La funcionalidad anterior creará un nuevo Objeto de Control de Llamada (NCCO) con dos acciones a realizar. La primera acción será "Hablar", convirtiendo el texto predefinido en Voice, y la segunda será manejar la entrada del usuario vía Dual Tone Multi-Frequency (DTMF), con otra URL webhook predefinida.
A continuación, estas acciones se convierten en un objeto JSON y se devuelven en la solicitud.
Esta función no se utiliza actualmente, ¡cambiémosla! Volver a main.go dentro de la función main() añada la siguiente línea de código, que indica al servidor web que escuche la URL webhooks/answery cuando se active, llame a la función answer() función:
// First Step - Answer phone call
http.HandleFunc("/webhooks/answer", answer) Grabar la llamada
En una llamada de voz, la función RecordAction del NCCO se activa y empieza a grabar todo lo que capte tu micrófono. Cuando se activa el RecordActionnecesitas definir la URL del webhook para que te proporcione los detalles del archivo grabado al finalizar la llamada.
Para activar una grabación, primero tendrá que registrar dos nuevas rutas en su servidor web. En su archivo main.go debajo de la llamada a la función answer añada las dos líneas siguientes:
// 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)En su archivo recording.go una de las funciones que ha definido en el paso anterior es la función recordUsersMessage() que se activa cuando el usuario introduce su respuesta DTMF en la llamada (pulsando 1, por ejemplo). Esta función creará una nueva NCCO, que primero convertirá un texto a voz, dándoles las gracias, y luego solicitando que dejen un mensaje después del tono.
La segunda acción es un RecordActionque le dice a la API que grabe lo que se diga después del tono. Añada esta nueva función a su archivo:
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)
}
Guardar el archivo de audio
Una vez finalizada la grabación de voz, se activa una llamada a la ruta /webhooks/recording-file con JSON, similar al ejemplo siguiente:
{
"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"
}En este ejemplo JSON, podemos ver el carácter recording_urlque es vital para que nuestro tutorial funcione. Esta URL de grabación está protegida; necesitas generar un JSON Web Token (JWT) y proporcionarlo con la petición GET cuando obtengas el archivo de grabación.
El primer paso es crear una nueva fila en la base de datos para este archivo, crear el nombre del archivo (Unix timestamp) y llamar a la función downloadFile() y llamar a la función A continuación, en su recordings.go añada la siguiente función:
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)
}
Descargar el archivo
Te habrás dado cuenta de que todavía no hemos llamado a la función downloadFile() en el ejemplo anterior. Nuestro siguiente paso es añadir esto, así como otra función para generar nuestro JWT. El JWT necesita ser pasado como una cabecera en la petición.
Añade lo siguiente a tu archivo recordings.go archivo. Esta acción descargará el archivo de audio de los servidores de Vonage y lo guardará como archivo en el directorio recordings con el nombre de archivo predeterminado.
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
}
¡Aún no hemos generado nuestro token JWT! Entonces, usando el Go SDK de Vonage, agrega la siguiente función a recordings.go. Esta función utiliza tu VONAGE_APPLICATION_ID y tu VONAGE_PRIVATE_KEY_PATH para generar un nuevo 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
}Eso es todo para la parte del sistema que recoge las llamadas de voz; antes de pasar a la segunda mitad del tutorial, vamos a querer probar esta mitad de principio a fin.
En primer lugar, asegúrese de que su proyecto se está ejecutando. En tu Terminal, dentro del directorio de tu proyecto, ejecuta el comando:
Aún deberías tener ngrok en funcionamiento, así que sigue adelante y llama a tu número virtual de Vonage usando tu teléfono.
La primera respuesta es el siguiente mensaje de voz: "Gracias por llamar a la línea directa de felicitaciones de cumpleaños para <insert name here>.. Si desea dejar un mensaje, pulse 1. De lo contrario, finalice la llamada. Gracias".
Si pulsa uno en el teclado, oirá: "Gracias. Por favor, deje un mensaje después del tono". Ahora grábate diciendo unas palabras y cuelga.
Unos segundos después de finalizar la llamada telefónica, compruebe su recordings directorio. Verás que se ha creado un nuevo archivo.
Es hora de construir la parte del sistema para la persona que cumple años.
Llamar a la persona que cumple años
Crear un Cronjob y Felicitar
Este proyecto necesita un método para ejecutar una de las funciones en una fecha y hora específicas.
El cron job es un programador de tiempo en los sistemas operativos Unix. Este proyecto utilizará una librería cron para Go para definir una fecha y hora concretas en la ejecución de una función específica.
En su Terminal, ejecute el siguiente comando para instalar esta biblioteca cron:
Dentro de la función main() dentro de su main.go vamos a llamar a una función aún por crear, runCongratulateCron()así que añade esto debajo de la parte donde llamas a connectDb():
runCongratulateCron()Para mantener la funcionalidad separada de la primera parte del tutorial, añadiremos la funcionalidad necesaria para esta parte en un archivo separado. Crea un nuevo archivo llamado congratulate.go y añade el siguiente 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})
}
El código anterior tiene dos funciones.
En primer lugar, la función runCongratulateCron() define un nuevo cronjob y añade la hora especificada para que la persona que cumple años reciba su llamada telefónica. Si no está seguro de cómo configurar las horas con un cronjob, consulte la página Gurú Crontab para construir su conjunto de tiempo personalizado.
La segunda función se llama desde la primera, y ésta realiza la llamada saliente de voz de texto a voz a la persona que cumple años, y luego le pide un InputAction ("Pulse 1 para continuar"). Para mantener la llamada activa para el receptor, se necesita una función ConversationAction es necesario. Aprenderemos cómo reproducir el audio en la llamada en el siguiente paso, pero esto debe hacerse en una conversación activa.
Reproducir audio en una llamada
Ahora que tenemos una llamada, tenemos que añadir el código para reproducir los archivos de audio en la llamada de voz. Para ello, tendrás que coger el UUID y pasarlo a una petición que llame a la función PlayAudioStream junto con la URL del archivo que deseas reproducir primero.
Nota no puede poner en cola los archivos de audio. Si reproduces en bucle cada archivo de audio en la llamada, interrumpirá cada archivo de audio con el último. Para evitar esto, tenemos que reproducir el archivo y luego esperar a que se produzca un evento al finalizar. Entonces encontraremos el siguiente archivo no reproducido en la base de datos y lo reproduciremos al finalizar el archivo de audio anterior.
Así, en congratulate.go añade el siguiente 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)
}
En main.go busque la línea http.HandleFunc("/webhooks/recording-file", getFileRecording) y añade lo siguiente:
http.HandleFunc("/congratulate", congratulate)
http.HandleFunc("/webhooks/play-audio", congratulatePlayAudio) Solicitud de activación para reproducir el siguiente archivo de audio
Como se ha comentado anteriormente, necesitamos reproducir el siguiente archivo de audio en la llamada al finalizar la anterior. Usando la URL webhook previamente definida bajo: RTC (In-app voice & messaging) en el dashboard, escucharemos un evento específico que contenga una clave concreta en la petición. Al escuchar la parte event.type de la solicitud, podremos comprobar si el valor es: audio:play:doney luego llamar a la función playAudio para encontrar el siguiente archivo de audio no reproducido.
Dentro de congratulate.go añada esta nueva event función:
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)
}
}
Entonces, en main.godebajo de la línea http.HandleFunc("/webhooks/play-audio", congratulatePlayAudio) añada:
http.HandleFunc("/webhooks/event", event)Ya está. Ya hemos creado nuestra cápsula del tiempo de celebraciones de cumpleaños con Go. A continuación vamos a recorrer el proceso paso a paso para probar la funcionalidad.
Póngalo a prueba
Ahora que hemos construido este proyecto, vamos a esbozar el proceso de principio a fin:
Los simpatizantes llaman a tu número virtual de Vonage
Tu aplicación responde a la llamada con un mensaje de texto a voz: "Gracias por llamar a la línea de felicitaciones de cumpleaños de
<insert name here>..Si desea dejar un mensaje, pulse 1. De lo contrario, finalice la llamada. Gracias".La aplicación espera a que introduzcas un número en el teclado.
El siguiente webhook recibe una solicitud, envía un mensaje de texto a voz: "Gracias. Por favor, deje un mensaje después del tono".
Se oye un pitido y la llamada pasa a grabar todo lo que capte tu micrófono.
Finaliza la llamada cuando hayas terminado.
Sin embargo, muchos bienquerientes pueden repetir los pasos 1-6 hay.
En el momento especificado (definido en la
runCongratulateCron()función), se llama a la funcióncongratulate()es llamada.La solicitud a la persona que cumple años realiza una llamada saliente.
Al contestar la llamada, el receptor recibe un mensaje de "¡Feliz cumpleaños! He recopilado varias grabaciones de tus amigos y familiares deseándote un feliz cumpleaños. Si quieres escucharlas, pulsa 1".
La llamada está ahora a la espera de que el receptor pulse un número en su teclado.
A continuación, la aplicación recuperará el primer archivo de audio no reproducido de la base de datos y lo transmitirá a la llamada de voz.
Cuando finaliza la transmisión del archivo de audio, se envía un evento a la aplicación. Cuando se recibe este evento, la aplicación busca el siguiente archivo de audio no reproducido y lo transmite a través de la llamada.
Cuando no hay archivos de audio sin reproducir, la llamada finaliza.
Ahora has integrado una cápsula del tiempo para celebraciones de cumpleaños con Go, usando Voice API de Vonage. El ejemplo proporcionado es solo una de las muchas maneras de usar Voice API.
Si este tutorial ha despertado tu interés en nuestra Voice API, pero Go no es el idioma de tu elección, puedes encontrar otros tutoriales en varios idiomas o servicios aquí en el blog de Vonagepor ejemplo:
Si tienes alguna pregunta, consejo o idea que quieras compartir con la comunidad, no dudes en entrar en nuestro espacio de trabajo espacio de trabajo comunitario Slacko ponte en contacto conmigo en Twitter. Me encantaría saber de cualquier persona que ha implementado este tutorial y cómo funciona su proyecto.
