
Compartir:
Amable educador tecnológico, padre de familia, defensor de la diversidad, probablemente discuta demasiado. Anteriormente ingeniero de backend. Háblame de JavaScript (frontend o backend), el increíble Vue.js, DevOps, DevSecOps, cualquier cosa JamStack. Escritor en DEV.to
Añadir funciones de voz a una aplicación de chat existente
Tiempo de lectura: 14 minutos
¿Alguna vez te has quedado a medio escribir un largo mensaje a alguien en un chat y has pensado: no sería mucho más fácil si pudiera hablar con esa persona? Por supuesto que sí. Utilizando el Client SDK de Nexmo en tu aplicación de chat, podrás hacerlo.
La aplicación de demostración aplicación de demostración y un ejemplo terminado ya están disponibles en GitHub.
Requisitos previos
Nodo y NPM
Para empezar vas a necesitar Node y NPM instalados. Esta guía utiliza Node 8 y NPM 6. Comprueba que están instalados y actualizados.
Tanto Node como NPM necesitan estar instalados y en la versión correcta. Vaya a nodejs.org e instale la versión correcta si no la tiene.
Nexmo CLI
Para configurar tu aplicación, necesitarás instalar Nexmo CLI. Instálalo usando NPM en la terminal.
Configurar el Nexmo CLI con la clave de la API y el secreto que se puede encontrar en el tablero de instrumentos.
Git (opcional)
Puede utilizar git para clonar nuestra aplicación de demostración desde GitHub.
Para aquellos que no se sientan cómodos con los comandos git, no os preocupéis, os tengo cubiertos. Esta guía contiene instrucciones para descargar el proyecto como archivo ZIP.
Siga esta guía para instalar git
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.
La aplicación de demostración
La aplicación es, ante todo, un simple cliente de chat. Permite que dos usuarios (o más, si así lo configuras) se conecten y empiecen a chatear.
Instalación básica
Para que esta guía sea fácil de seguir, clona la aplicación de demostración directamente desde GitHub.
Para quienes no se sientan cómodos con los comandos git, pueden descargar la aplicación de demostración como archivo zip y descomprimirlo localmente.
Una vez clonado o descomprimido, cambie al nuevo directorio de aplicaciones de demostración.
Instale las dependencias de npm.
Ahora, inicia la aplicación.
Ahora puedes probarlo en tu navegador favorito y ver la aplicación, que debería estar ejecutándose en la dirección predeterminada: http://127.0.0.1:8080.
Login box
Sin configurar, verás un cuadro de inicio de sesión. No puedes iniciar sesión porque aún no sabes quién puede hacerlo.
Tenga en cuenta, sólo estás simulando la autenticación aquí, y debes estar preparado para configurar algo real y seguro para las aplicaciones de producción.
User not found - Login Box
Detener la aplicación: En terminal o bash, puedes usar CTRL+C para detener el proceso en ejecución. A medida que realices cambios, no deberías necesitar iniciar o detener la aplicación, ya que todo se solicita de nuevo al servidor cada vez que recargas una página.
Instalación muy sencilla
En la demo (la que estás ejecutando ahora,) hay un script para hacer los siguientes pasos mucho más fáciles.
Cómo funciona el script: Te pide algunos datos y luego crea la aplicación, la conversación y los usuarios necesarios para esta guía ejecutando todos los comandos de configuración que de otro modo tendrías que hacer manualmente. A continuación, genera la configuración de la aplicación de demostración. Puedes ver el código aquí para asegurarte de que no hace nada malo.
Woah¿No quieres ejecutar mi script? ¿Prefieres generar el archivo de configuración manualmente? Te cubro las espaldas - pasos para crear el archivo de configuración manualmente.
Ejecutar el script de instalación
Por lo tanto, para configurar la aplicación para los próximos pasos, ejecute el script de configuración.
El guión plantea algunas preguntas.
Script Questions
Al final, actualiza el config.js para que se parezca más a esto.
const USERS = {
luke: 'eyJhbGciOiJIkpXVCJ9.eyJpYXQiOnt9fX19.EDHi1R61yh01oeZ9DYQ',
alex: 'eyJhbGciOi234JXVCJ9.eyJpyXQiOjt9fX19.VqLdU97Fdb2ZiOfqmoQ',
}
const CONVERSATION_ID = 'CON-da9c1a6b-c2dc-4bdd-ac03-cc041ef03502' ¿Y qué hizo ese guión?
Así, entre bastidores, el script lleva a cabo los siguientes pasos.
Crea una aplicación Nexmo utilizando el comando
nexmo app:createy anota el ID.Crea una conversación Nexmo utilizando el comando
nexmo conversation:createy anota el ID.Crea ambos usuarios utilizando el comando
nexmo user:createy toma nota de los ID.Añade ambos usuarios a la conversación Nexmo con
nexmo member:add.Genera JWTs para que ambos usuarios accedan a la aplicación y toma nota de los JWTs.
Escribe la configuración en config.js usando los IDs y JWTs que ha guardado.
Chitty Chitty Chat Chat
Ahora que ya has configurado nuestra aplicación de demostración básica, ¡ya puedes charlar un poco! Aquí estoy yo probándolo con uno de mis colegas.
Chat text
Activar audio
Ahora, ya estás en marcha. Tienes una aplicación de demostración que puedes utilizar para chatear con otras personas. A continuación, añade un botón para habilitar el audio para que también podáis hablar entre vosotros.
El HTML
Encuentre el siguiente código dentro del archivo index.html archivo.
<section id="messages">
<!-- /audio-toggle -->
<h1>Messages</h1>
<div id="messageFeed"></div>
<textarea id="messageTextarea"></textarea>
<br>
<button id="send">Send</button>
</section>Sustituya la línea <!-- /audio-toggle --> por el siguiente HTML.
<div>
<audio id="audio">
</audio>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-secondary">
<span id="audioToggleText">Enable Audio</span>
</label>
</div>
</div>Ahora, permítanme explicar el código anterior.
<audio> se añadieron a la especificación HTML para permitir la incrustación de archivos de audio (o secuencias) en páginas web. La etiqueta se utiliza para indicar la fuente (ruta/url) y la versión del audio, lo que permite incrustar/codificar varias versiones del audio para distintos contextos (o navegadores, sistemas operativos, etc.).
Además, está añadiendo un botón que será nuestra palanca.
El archivo index.html debería contener una sección parecida a ésta.
<section id="messages">
<div>
<audio id="audio">
</audio>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-secondary">
<span id="audioToggleText">Enable Audio</span>
</label>
</div>
</div>
<h1>Messages</h1>
<div id="messageFeed"></div>
<textarea id="messageTextarea"></textarea>
<br>
<button id="send">Send</button>
</section>Estos son todos los cambios de HTML esta vez. ¿Y ahora qué?
El JavaScript
A continuación, editaremos el JavaScript de nuestra aplicación de demostración.
Encuentre el siguiente código dentro del archivo chat.js archivo.
constructor() {
this.messageTextarea = document.getElementById('messageTextarea')
this.sendButton = document.getElementById('send')
this.loginForm = document.getElementById('login')
this.loginButton = document.getElementById('loginButton')
this.messages = document.getElementById('messages')
this.messageFeed = document.getElementById('messageFeed')
// audio-elements
this.setupUserEvents()
}Sustituya la línea // audio-elements por el siguiente código JavaScript.
this.audio = document.getElementById('audio')
this.audioToggle = document.getElementById('audioToggle')
this.audioToggleText = document.getElementById('audioToggleText')Este código 'registra' 3 nuevos elementos para que puedas utilizar más fácilmente los elementos on-page en todo el archivo JavaScript.
Ahora, encuentra este código dentro del mismo archivo chat.js archivo.
// audio-toggle-event
this.showConversationHistory(conversation)Sustituya la línea // audio-toggle-event por el siguiente código JavaScript.
conversation.on("member:media", (member, event) => {
console.log(`*** Member changed media state`, member, event)
const text = `${member.user.name} <b>${event.body.audio ? 'enabled' : 'disabled'} audio in the conversation</b><br>`
this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
})Este código también 'registra' un oyente para el evento member:media evento. Cuando ve ese evento, entonces envía un console.log al navegador; como, miembro y datos del evento. Especialmente útil para la depuración. También va a crear un poco de texto con formato y actualizar el messageFeed anteponiendo el texto a la alimentación existente.
A continuación, busque este código dentro del archivo chat.js archivo.
})
// audio-click-event
}Sustituya la línea // audio-click-event por el siguiente código JavaScript.
this.audioToggle.addEventListener('click', () => {
const buttonContainer = this.audioToggle.parentNode
if (this.audioToggle.checked) {
this.audioToggleText.innerHTML = 'Disable Audio'
buttonContainer.classList.add('btn-danger')
buttonContainer.classList.add('active')
buttonContainer.classList.remove('btn-secondary')
this.conversation.media.enable().then(stream => {
// Older browsers may not have srcObject
if ("srcObject" in this.audio) {
this.audio.srcObject = stream
} else {
// Avoid using this in new browsers, as it is going away.
this.audio.src = window.URL.createObjectURL(stream)
}
this.audio.onloadedmetadata = () => {
this.audio.play()
}
this.eventLogger('member:media')()
}).catch(this.errorLogger)
} else {
this.audioToggleText.innerHTML = 'Enable Audio'
buttonContainer.classList.remove('btn-danger')
buttonContainer.classList.remove('active')
buttonContainer.classList.add('btn-secondary')
this.conversation.media.disable().then(this.eventLogger('member:media')).catch(this.errorLogger)
}
})
Este código es un biggy. Y, esto también registra un oyente. Esta vez, está escuchando cuando el usuario hace clic en nuestro audioToggle botón que acaba de agregar.
Si un usuario pulsa el botón y ya estaba activado, se desactiva. Si estaba desactivado, se activa.
Cuando está activada, activa el audio añadiendo la URL del flujo de audio a la etiqueta y actualiza el estilo del botón. Por lo tanto, cuando está desactivado, desactiva el audio eliminando la URL del flujo de audio de la etiqueta y actualiza el estilo del botón.
El archivo chat.js debería ser algo (largo) parecido a esto.
class ChatApp {
constructor() {
this.messageTextarea = document.getElementById('messageTextarea')
this.sendButton = document.getElementById('send')
this.loginForm = document.getElementById('login')
this.loginButton = document.getElementById('loginButton')
this.messages = document.getElementById('messages')
this.messageFeed = document.getElementById('messageFeed')
this.audio = document.getElementById('audio')
this.audioToggle = document.getElementById('audioToggle')
this.audioToggleText = document.getElementById('audioToggleText')
this.setupUserEvents()
}
joinConversation(userToken) {
new NexmoClient({ debug: false })
.createSession(userToken)
.then(app => {
console.log('*** Logged into app', app)
return app.getConversation(CONVERSATION_ID)
})
.then((conversation) => {
console.log('*** Joined conversation', conversation)
this.setupConversationEvents(conversation)
})
.catch(this.errorLogger)
}
showConversationHistory(conversation) {
conversation
.getEvents({ page_size: 20 })
.then((events_page) => {
var eventsHistory = ""
events_page.items.forEach((value, key) => {
if (conversation.members.get(value.from)) {
const date = new Date(Date.parse(value.timestamp))
switch (value.type) {
case 'text':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date.toLocaleString('en-GB')}: <b>${value.body.text}</b><br>` + eventsHistory
break;
case 'member:joined':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date.toLocaleString('en-GB')}: <b>joined the conversation</b><br>` + eventsHistory
break;
}
}
})
this.messageFeed.innerHTML = eventsHistory + this.messageFeed.innerHTML
})
.catch(this.errorLogger)
}
setupConversationEvents(conversation) {
this.conversation = conversation
this.messages.style.display = "block"
// Bind to events on the conversation
conversation.on('text', (sender, message) => {
const date = new Date(Date.parse(message.timestamp))
console.log('*** Message received', sender, message)
const text = `${sender.user.name} @ ${date.toLocaleString('en-GB')}: <b>${message.body.text}</b><br>`
this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
})
conversation.on("member:joined", (member, event) => {
const date = new Date(Date.parse(event.timestamp))
console.log(`*** ${member.user.name} joined the conversation`)
const text = `${member.user.name} @ ${date.toLocaleString('en-GB')}: <b>joined the conversation</b><br>`
this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
})
conversation.on("member:media", (member, event) => {
console.log(`*** Member changed media state`, member, event)
const text = `${member.user.name} <b>${event.body.audio ? 'enabled' : 'disabled'} audio in the conversation</b><br>`
this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
})
this.showConversationHistory(conversation)
}
errorLogger(error) {
console.log(error)
}
eventLogger(event) {
return () => {
console.log("'%s' event was sent", event)
}
}
setupUserEvents() {
this.sendButton.addEventListener('click', () => {
this.conversation.sendText(this.messageTextarea.value)
.then(() => {
this.eventLogger('text')()
this.messageTextarea.value = ''
})
.catch(this.errorLogger)
})
this.loginForm.addEventListener('submit', (event) => {
event.preventDefault()
const userName = this.loginForm.children.username.value
const userToken = this.authenticate(userName)
this.loginForm.children.username.value = ''
if (userToken) {
this.joinConversation(userToken)
this.loginForm.style.display = 'none'
} else {
alert('user not found')
}
})
this.audioToggle.addEventListener('click', () => {
const buttonContainer = this.audioToggle.parentNode
if (this.audioToggle.checked) {
this.audioToggleText.innerHTML = 'Disable Audio'
buttonContainer.classList.add('btn-danger')
buttonContainer.classList.add('active')
buttonContainer.classList.remove('btn-secondary')
this.conversation.media.enable().then(stream => {
// Older browsers may not have srcObject
if ("srcObject" in this.audio) {
this.audio.srcObject = stream
} else {
// Avoid using this in new browsers, as it is going away.
this.audio.src = window.URL.createObjectURL(stream)
}
this.audio.onloadedmetadata = () => {
this.audio.play()
}
this.eventLogger('member:media')()
}).catch(this.errorLogger)
} else {
this.audioToggleText.innerHTML = 'Enable Audio'
buttonContainer.classList.remove('btn-danger')
buttonContainer.classList.remove('active')
buttonContainer.classList.add('btn-secondary')
this.conversation.media.disable().then(this.eventLogger('member:media')).catch(this.errorLogger)
}
})
}
authenticate(username) {
return USERS[username] || null
}
}
new ChatApp()Suponiendo que lo has hecho todo bien, ejecuta npm start de nuevo y abra la aplicación en http://127.0.0.1:8080. Si ya se estaba ejecutando, deberías poder actualizar la página para obtener la última versión.
Login box
Si crees que el JavaScript no está funcionando y reiniciar NPM no ayuda, usa CTRL+F5 para refrescar la página web, lo que solicita todo el JS y CSS de la página fresco.
Inicie sesión con las credenciales de prueba que ha configurado.
Login using credentials
Ahora que has iniciado sesión, puedes ver el feed de mensajes como antes, y el botón para Activar Audio. Adelante, haz clic en Activar audio.
Permite que la aplicación utilice tu micrófono. Esto es en Chrome para macOS, otros navegadores y sistemas operativos pueden variar.
Allow microphone
Con ambos usuarios conectados y el audio activado, puedes mantener una conversación entre los dos usuarios.
Two user convo
En este punto, si estás en una cafetería y lo pruebas en dos ventanas del navegador como yo, prepárate para que tus compañeros frunzan el ceño cuando generes un bucle de retroalimentación ruidoso.
Ahora puedes hacer clic en Desactivar audio para volver a desactivar el micrófono. Los demás usuarios recibirán un aviso de que has desactivado el audio.
Disable Audio
Resultados
Siempre va a existir la necesidad de permitir la comunicación de audio entre usuarios web y nuestro Client SDK es una solución perfecta para ello.
Pruébalo y cuéntame lo que piensas en nuestra Comunidad Slack o en la sección de comentarios.
Compartir:
Amable educador tecnológico, padre de familia, defensor de la diversidad, probablemente discuta demasiado. Anteriormente ingeniero de backend. Háblame de JavaScript (frontend o backend), el increíble Vue.js, DevOps, DevSecOps, cualquier cosa JamStack. Escritor en DEV.to
