
Compartir:
Manik es ingeniero de software sénior. Le gusta trabajar con desarrolladores y crear API. Cuando no está construyendo APIs o SDKs, se le puede encontrar hablando en conferencias y meetups.
Construir una aplicación de Video Chat con OpenTok y Nexmo In-App Messaging
Tiempo de lectura: 6 minutos
En esta entrada de blog, vamos a crear una aplicación web que permita a los usuarios chatear por vídeo y enviarse mensajes utilizando OpenTok y Nexmo In-App Messaging.
Para ver el código completo, consulte el siguiente repo. También puede consultar nuestro webinar reciente que cubre la aplicación.
Requisitos previos
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.
A TokBox Account y la API Key y Secret de un proyecto API
Estructura de la aplicación
Crea un directorio y ponle el nombre que quieras:
Seguiremos adelante y crearemos algunos archivos y subcarpetas dentro del directorio utilizando los siguientes comandos:
La estructura de nuestro proyecto debería ser la siguiente:
video-messaging-app
├── package.json
├── package-lock.json
├── views
│ ├── index.ejs
├── public
│ ├── js
│ ├── index.js
├── config.js
├── server.js
Dependencias
Crearemos un proyecto NPM e instalaremos todas las dependencias necesarias para el proyecto:
npm init -y // we use the -y flag to skip through the questions
npm install opentok @opentok/client nexmo nexmo-stitch express ejsAhora, vamos a crear nuestro servidor añadiendo el código del servidor al archivo server.js archivo.
const OpenTok = require('opentok');
const Nexmo = require('nexmo');
const express = require('express');
const app = express();
app.use(express.static(`${__dirname}/public`));
app.get('/', (req, res) => {
res.json({
opentokApiKey: null,
opentokSessionId: null,
opentokToken: null,
nexmoConversationId: null,
nexmoJWT: null,
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Running server on PORT: ${PORT}`));
Tenga en cuenta que hemos creado un servidor utilizando ExpressJS y estamos devolviendo credenciales vacías tanto para OpenTok como para Nexmo. No te preocupes, vamos a generar las credenciales en los próximos pasos, pero antes de eso vamos a seguir adelante y crear nuestra aplicación de mensajería Nexmo utilizando el Nexmo CLI:
nexmo app:create video-messaging-app https://example.com/answer https://example.com/event --keyfile=private.key
nexmo conversation:create display_name="Nexmo In-App Messaging"
nexmo user:create name="jamie"
nexmo member:add YOUR_CONVERSATION_ID action=invite channel='{"type":"app"}' user_id=USER_ID // make sure to replace the conversation ID and the user IDCon el comando app:create obtendremos el ID de la aplicación para nuestro archivo video-messaging-app junto con una clave privada que se añadirá al directorio. Ten en cuenta que hemos establecido las urls de respuesta y evento como urls de ejemplo, pero podemos cambiarlas más tarde. Usando el comando conversation:create también hemos creado una conversación llamada Nexmo In-App Messaging. Esto resultará en un ID de conversación que usaremos más tarde para conectarnos a la conversación. El comando user:create también permite crear un usuario vinculado a la aplicación. Ten en cuenta el nombre de este usuario porque lo utilizaremos como parte de nuestro proceso de generación de JWT.
Ahora crearemos un proyecto de API de OpenTok utilizando el panel de control de TokBox para que puedas acceder a la clave y el secreto de la API.
Ahora, abramos nuestro archivo config.js para que podamos almacenar nuestras credenciales:
module.exports = {
opentokApiKey: '',
opentokApiSecret: '',
nexmoApiKey: '',
nexmoApiSecret: '',
nexmoApplicationId: '',
nexmoPrivateKey: '',
nexmoConversationId: '',
};Asegúrese de añadir las credenciales adecuadas al archivo config.js archivo
Importar las variables de configuración:
En nuestro archivo server.js vamos a importar las variables de configuración para que podamos utilizarlas para crear instancias de OpenTok y Nexmo clases.
const {
opentokApiKey,
opentokApiSecret,
nexmoApiKey,
nexmoApiSecret,
nexmoApplicationId,
nexmoPrivateKey,
nexmoConversationId,
} = require('./config');
const opentok = new OpenTok(opentokApiKey, opentokApiSecret);
const nexmo = new Nexmo({
apiKey: nexmoApiKey,
apiSecret: nexmoApiSecret,
applicationId: nexmoApplicationId,
privateKey: nexmoPrivateKey,
});Ahora vamos a seguir adelante y actualizar la ruta de petición GET para que podamos devolver credenciales válidas:
app.get('/', (req, res) => {
opentok.createSession({
mediaMode: 'routed'
}, (error, session) => {
if (error) {
res.status(500).send('There was an error generating an OpenTok session');
} else {
const opentokSessionId = session.sessionId;
const opentokToken = opentok.generateToken(opentokSessionId);
const nexmoJWT = nexmo.generateJwt({
exp: new Date().getTime() + 86400,
acl: {
"paths": {
"/v1/users/**": {},
"/v1/conversations/**": {},
"/v1/sessions/**": {},
"/v1/devices/**": {},
"/v1/image/**": {},
"/v3/media/**": {},
"/v1/push/**": {},
"/v1/knocking/**": {}
}
},
sub: 'jamie' // this is the name we set when creating the user with the Nexmo CLI
});
res.json({
opentokApiKey,
opentokSessionId,
opentokToken,
nexmoConversationId,
nexmoJWT,
});
}
});
});Usando el código anterior, crearemos lo siguiente cada vez que alguien visite la ruta / desde su navegador:
ID de sesión de OpenTok
Token OpenTok para el identificador de sesión correspondiente
Token JWT para nuestra aplicación Nexmo con las ACLs apropiadas
Ahora que hemos creado un mecanismo para obtener las credenciales vamos a seguir adelante y trabajar en el lado cliente de la aplicación.
Abra el archivo index.js situado en el directorio js directorio.
const OT = require('@opentok/client');
const ConversationClient = require('nexmo-stitch');
const session = OT.initSession(opentokApiKey, opentokSessionId);
const publisher = OT.initPublisher('publisher');
session.on({
streamCreated: (event) => {
const subscriberClassName = `subscriber-${event.stream.streamId}`;
const subscriber = document.createElement('div');
subscriber.setAttribute('id', subscriberClassName);
document.getElementById('subscribers').appendChild(subscriber);
session.subscribe(event.stream, subscriberClassName);
},
streamDestroyed: (event) => {
console.log(`Stream ${event.stream.name} ended because ${event.reason}.`);
},
sessionConnected: event => {
session.publish(publisher);
},
});
session.connect(opentokToken, (error) => {
if (error) {
console.log('error connecting to session');
}
});
En el código anterior, inicializamos una OpenTok Sesión llamando al método initSession en el objeto OT del objeto. A continuación, creamos un editor y establecemos los siguientes escuchadores de eventos: streamCreated, streamDestroyedy sessionConnected. Estos escuchadores de eventos se utilizan para suscribirse a flujos cuando se crea un flujo, imprimir un mensaje cuando se destruye un flujo y publicar en la sesión cuando estamos conectados. Luego procedemos a conectarnos a la sesión usando el token que generamos en el servidor.
Ahora que hemos añadido el código para un chat de vídeo, vamos a añadir In-App Messaging.
class ChatApp {
constructor() {
this.messageTextarea = document.getElementById('messageTextarea');
this.messageFeed = document.getElementById('messageFeed');
this.sendButton = document.getElementById('send');
this.loginForm = document.getElementById('login');
}
}La clase ChatApp se utilizará para añadir nuestras funciones In-App Messaging. También tomaremos la referencia a algunos elementos DOM que crearemos en nuestro archivo index.ejs archivo.
Sigamos adelante y añadamos algunos métodos de ayuda a la clase ChatApp para registrar nuestros eventos y errores en la consola:
errorLogger(error) {
console.log(`There was an error ${error}`);
}
eventLogger(event) {
console.log(`This event happened: ${event}`);
}A continuación, tenemos que crear una instancia de ConversationClient y autenticarnos con el nexmoJWT generado por nuestro servidor:
joinConversation(userToken) {
new ConversationClient({
debug: false
})
.login(userToken)
.then(app => {
console.log('*** Logged into app', app)
return app.getConversation(nexmoConversationId)
})
.then(this.setupConversationEvents.bind(this))
.catch(this.errorLogger)
}
Ahora que tenemos una referencia a la conversación, vamos a seguir adelante y configurar nuestros eventos de conversación:
setupConversationEvents(conversation) {
console.log('*** Conversation Retrieved', conversation)
console.log('*** Conversation Member', conversation.me)
conversation.on('text', (sender, message) => {
console.log('*** Message received', sender, message)
const date = new Date(Date.parse(message.timestamp))
const text = `${sender.user.name} @ ${date}: <b>${message.body.text}</b><br>`
this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
});
this.showConversationHistory(conversation);
}
Podemos recuperar el historial de conversaciones llamando al método getEvents en el objeto conversation del objeto Vamos a seguir adelante y crear un método de ayuda para que podamos mostrar el historial de chat en el DOM. Como puedes ver a continuación, estamos utilizando los diferentes métodos types para distinguir entre los eventos:
showConversationHistory(conversation) {
conversation.getEvents().then((events) => {
var eventsHistory = ""
events.forEach((value, key) => {
if (conversation.members.get(value.from)) {
const date = new Date(Date.parse(value.timestamp))
switch (value.type) {
case 'text:seen':
break;
case 'text:delivered':
break;
case 'text':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date}: <b>${value.body.text}</b><br>` + eventsHistory
break;
case 'member:joined':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date}: <b>joined the conversation</b><br>` + eventsHistory
break;
case 'member:left':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date}: <b>left the conversation</b><br>` + eventsHistory
break;
case 'member:invited':
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date}: <b>invited to the conversation</b><br>` + eventsHistory
break;
default:
eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date}: <b>unknown event</b><br>` + eventsHistory
}
}
})
this.messageFeed.innerHTML = eventsHistory + this.messageFeed.innerHTML
})
}
También deberíamos configurar algunos eventos de usuario para saber cuándo el usuario final ha desencadenado acciones en la página HTML:
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();
document.getElementById('messages').style.display = 'block';
document.getElementById('login').style.display = 'none';
this.joinConversation(nexmoJWT);
});
}
Asegurémonos de llamar al método setupUserEvents() en nuestro constructor:
class ChatApp {
constructor() {
this.messageTextarea = document.getElementById('messageTextarea');
this.messageFeed = document.getElementById('messageFeed');
this.sendButton = document.getElementById('send');
this.loginForm = document.getElementById('login');
this.setupUserEvents();
}
}Recapitulemos lo que hemos hecho en el código anterior. Hemos creado una clase llamada ChatApp que crea un ConversationClient que autenticamos usando el nexmoJWT token. También establecemos un oyente de eventos texten el objeto de conversación para escuchar cualquier mensaje entrante. Ten en cuenta que para recuperar mensajes antiguos de la conversación, utilizamos el método getEvents . Usamos algunos escuchadores de eventos en el DOM para mostrar información cuando las cosas cambian.
Ahora que hemos creado la clase ChatApp vamos a seguir adelante e instanciar una clase ChatApp cuando el evento onload se dispara para que podamos utilizar los elementos DOM según sea necesario.
window.onload = () => {
new ChatApp();
}
Después de completar nuestro index.jsvamos a añadir algo de información a nuestro archivo index.ejs archivo:
<!DOCTYPE html>
<html>
<head>
<style>
#login,
#messages {
width: 80% ; height: 300px;
}
#messages {
display: none
}
#conversations {
display: none
}
</style>
<script type="text/javascript">
const opentokApiKey = '<%= opentokApiKey %>';
const opentokSessionId = '<%= opentokSessionId %>';
const opentokToken = '<%= opentokToken %>';
const nexmoConversationId = '<%= nexmoConversationId %>';
const nexmoJWT = '<%= nexmoJWT %>';
</script>
<script src="/js/bundle.js"></script>
</head>
<body>
<form id="login">
<h1>Login</h1>
<input type="text" name="username" value="">
<input type="submit" value="Login" />
</form>
<section id="messages">
<button id="leave">Leave Conversation</button>
<h1>Messages</h1>
<div id="messageFeed"></div>
<textarea id="messageTextarea"></textarea>
<br>
<button id="send">Send</button>
</section>
<section id="conversations">
<h1>Conversations</h1>
</section>
</body>
</html>
El código anterior es renderizado por nuestro servidor cuando alguien visita la ruta / ruta. Como puedes ver, pasamos nuestras credenciales que usamos para la Sesión OpenTok y el Cliente de Conversación Nexmo.
Por último, vamos a modificar nuestro servidor para renderizar la vista index.ejs con las variables correctas:
res.render('index.ejs', {
opentokApiKey,
opentokSessionId,
opentokToken,
nexmoConversationId,
nexmoJWT,
});Ahora que tenemos todo configurado, vamos a añadir un script start a nuestro archivo package.json para que podamos iniciar fácilmente el servidor:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "browserify public/js/index.js -o public/js/bundle.js && node server.js"
}
Ejecute npm start en tu terminal y ejecuta la aplicación.
Conclusión
En este blog, hemos cubierto conceptos importantes de OpenTok y Nexmo In-App Messaging mostrando la capacidad de añadir vídeo en directo y mensajería in-app a las aplicaciones web. Para ver el código completo, por favor refiérase a la siguiente repo.
