
Compartir:
Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.
Creación de una línea directa en JavaScript con OpenTok y Node.js
Tiempo de lectura: 11 minutos
Érase una vez, cuando la gente tenía preguntas sobre JavaScript iban al IRC (Internet Relay Chat) para encontrar a alguien que les respondiera. Sin embargo, el IRC es una tecnología muy antigua en los años de Internet. Muchos desarrolladores de JavaScript hoy en día puede que ni siquiera tengan un cliente IRC, o que nunca hayan utilizado uno. La mayoría, en cambio, están muy familiarizados con el Video Chat.
OpenTok ya permite empezar rápidamente con un simple videochat entre dos participantes. Con un poco de lógica de colas añadida, puedes tener una línea directa para preguntas sobre JavaScript, o cualquier otra cosa, en un abrir y cerrar de ojos.
Para que tu línea directa sea aún más amigable puedes construir el proyecto en Glitch. Esto significa menos configuración para ti. También hace que tu línea directa sea más útil al proporcionar el proyecto completo para que otros lo remezclen para sus propias líneas directas.
Primeros pasos en Glitch
Si desea saltar a un proyecto de trabajo puede remezclar el proyecto JavaScript hotline en Glitch de inmediato. De lo contrario, en unos pocos pasos puedes codificar tu propia línea directa desde cero. Para empezar, crea un nuevo proyecto en Glitch, eligiendo la plantilla hello-express plantilla.
Para proporcionar Video chat con OpenTok, opentok es en realidad el único paquete adicional que necesitas. Sin embargo, añadir la opción de recibir un texto cuando alguien necesita respuesta a una pregunta hará que la línea de atención telefónica sea más capaz de hacer frente a las variaciones en el volumen de usuarios. Para ello, también puedes instalar body-parser para recibir entradas de formularios y nexmo para enviar los textos:
pnpm install opentok body-parser nexmo -sPuede reutilizar el ejemplo server.js, index.htmly client.js que ya están en tu proyecto Glitch. Eso significa que tu configuración está prácticamente hecha.
Suministro de variables de entorno
Su archivo .env se parecerá un poco a esto:
Para proporcionar valores reales para esas variables, necesitarás cuentas de desarrollador tanto en OpenTok como en Nexmo. También necesitarás un proyecto OpenTok y un número virtual Nexmo.
En el Account de OpenTokcrea un nuevo proyecto para tu línea directa con el tipo de proyecto "OpenTok API". Después de darle un nombre y seleccionar un códec de vídeo (VP8 debería estar bien), verás su clave API y su secreto. Puedes pegarlas en OPENTOK_API_KEY y OPENTOK_SECRETrespectivamente.
Tus credenciales de Nexmo, que puedes pegar en NEXMO_API_KEY y NEXMO_API_SECRETdeberían ser visibles en la página "Primeros pasos" de su Panel de Nexmo. Puedes utilizar cualquier número de teléfono de "[Your Numbers](${CUSTOMER_DASHBOARD_URL}/your-numbers" sin configurar para FROM_PHONEya que enviarás mensajes desde ese número pero no recibirás mensajes ni llamadas.
Configuración del servidor
Tu servidor ya incluye alguna inicialización, una ruta de vista para la raíz de la aplicación, y un listener que inicia el servidor. Puedes modificar un poco la sección de inicialización para usar también el body-parser middleware:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(express.static('public'));
app.use(bodyParser.json());Debajo de ese bloque puedes añadir nuevos objetos OpenTok y Nexmo inicializados con los valores de .envy dos matrices vacías. waiting es para los identificadores de sesión de los chats que esperan a que un ayudante responda a una pregunta, y helpers son los números de teléfono de las personas que han ofrecido ayuda durante los tiempos de inactividad cuando nadie tenía una pregunta.
const OpenTok = require('opentok');
const opentok = new OpenTok(process.env.OPENTOK_API_KEY, process.env.OPENTOK_SECRET);
const Nexmo = require('nexmo');
const nexmo = new Nexmo({
apiKey: process.env.NEXMO_API_KEY,
apiSecret: process.env.NEXMO_API_SECRET
});
var waiting = [];
var helpers = [];En el resto del archivo, entre la ruta por defecto y el listener, puedes declarar las otras rutas. Debajo de ellas necesitarás una función para enviar un mensaje de texto a alguien que se haya ofrecido a responder preguntas:
app.get('/', function(request, response) {
response.sendFile(__dirname + '/views/index.html');
});
app.get('/ask', function(request, response) {});
app.get('/answer', function(request, response) {});
app.get('/answer/:sessionId', function(request, response) {});
app.post('/text', function(request, response) {});
function textHelper(sessionId) {}
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
}); Añadir participantes a Preguntas y Respuestas
La función más básica de la aplicación de línea directa es conectar a una persona que hace una pregunta con alguien que se ofrece a responderla. La forma más directa de conseguirlo es crear una sesión de videochat cuando alguien declara que quiere preguntar algo. Entonces puedes añadir a la siguiente persona disponible que quiera responder como segundo participante. Podrías crear una forma más robusta de manejar estas sesiones activas usando un almacén de datos, pero para propósitos de prueba un array debería estar bien.
Cuando alguien tiene una pregunta puede crear una nueva sesión OpenTok y almacenar su ID en el waiting array. Luego puedes responder con el nuevo ID, la clave de la API de OpenTok de la aplicación y un token que identifique a ese cliente específico:
app.get('/ask', function(request, response) {
opentok.createSession(function(err, session) {
let sessionId = session.sessionId;
waiting.push(sessionId);
response.send({
apiKey: process.env.OPENTOK_API_KEY,
sessionId: sessionId,
token: opentok.generateToken(sessionId)
});
if (helpers.length) {
textHelper(sessionId);
}
});
});Puede ver que la
/askcomo paso final, comprueba la longitud de la matrizhelpersmatriz. Si encuentra elementos en él, llama a la funcióntextHelpera la función DiscutiremostextHelperpor separado más adelante, pero si quieres simplificar tu línea directa conectando sólo a las personas que utilizan actualmente tu aplicación puedes eliminar toda esa condicional.
Ahora, cuando alguien se ofrezca a responder a una pregunta, puede enviar un objeto de respuesta similar con los valores de la primera sesión de la cola. waiting cola. Si está vacía, puedes enviar una respuesta indicando al cliente que no hay nadie pidiendo ayuda en ese momento:
app.get('/answer', function(request, response) {
if (waiting.length) {
let sessionId = waiting.shift();
response.send({
apiKey: process.env.OPENTOK_API_KEY,
sessionId: sessionId,
token: opentok.generateToken(sessionId)
});
} else {
response.send({
wait: true
});
}
}); Enviar un mensaje de texto a un ayudante cuando alguien hace una pregunta
Complica un poco nuestro flujo de trabajo actual, pero permitir que los posibles ayudantes faciliten su número de teléfono y reciban un mensaje de texto cuando esté lista una nueva sesión de preguntas permite a la línea directa afrontar mejor los periodos lentos.
Guardar el número de teléfono de alguien son sólo unas líneas de código. El endpoint /text recibe el número de teléfono en el cuerpo de la solicitud y puede añadirlo a una cola de espera. helpers cola. A continuación, devuelve un estado "OK":
app.post('/text', function(request, response) {
let phone = request.body.phone;
helpers.push(phone);
response.sendStatus(200);
});Ahora podemos utilizar el número de teléfono almacenado en el helper en la función textHelper a la que llama la ruta /ask ruta. La función obtiene el primer número de teléfono de helpers y le envía un enlace a una sesión de videochat específica:
function textHelper(sessionId) {
let phone = helpers.shift();
nexmo.message.sendSms(
process.env.FROM_PHONE,
phone,
'JavaScript question for you! Caller is waiting at: https://' + process.env.PROJECT_DOMAIN + '.glitch.me/?id=' + sessionId
);
}Si alguien sigue un enlace de sesión que se le ha enviado por mensaje de texto, el cliente puede solicitar al servidor las credenciales para unirse a esa sesión específica. La aplicación puede eliminar con seguridad la sesión de la cola waiting una vez que se haya solicitado:
app.get('/answer/:sessionId', function(request, response) {
let sessionId = request.params.sessionId;
let index = waiting.indexOf(sessionId);
waiting.splice(index, 1);
response.send({
apiKey: process.env.OPENTOK_API_KEY,
sessionId: sessionId,
token: opentok.generateToken(sessionId)
});
}); Añadir una interfaz
Para las pruebas, lo más sencillo es mantener todo el HTML en una sola página. El ejemplo index.html ya importa client.js. Sobre esa etiqueta script, también querrás importar la librería cliente OpenTok desde los servidores de OpenTok:
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>Puede sustituir el contenido de la etiqueta <body> por tres bloques: botones para iniciar el proceso de preguntar o responder, un pseudoformulario para recopilar los números de teléfono de las personas que desean recibir un mensaje de texto y los destinos de los elementos de vídeo:
<div id="buttons">
<a href="/ask" class="bigbutton" id="askBtn">Ask a Question</a>
<a href="/answer" class="bigbutton" id="answerBtn">Answer a Question</a>
</div>
<div id="addNumber">
No one has a question right now. Want a text when someone does?
<label>Phone number (e.g. 441234123456):
<input type="tel" id="phoneNumber" name="phoneNumber" />
</label>
<button id="phoneBtn">Text me!</button>
</div>
<div id="videos">
<div id="subscriber"></div>
<div id="publisher"></div>
</div>
No vamos a entrar en el CSS en este tutorial, pero como mínimo es probable que desee ocultar el
#addNumbery#videoscuando se cargue la página por primera vez. Usted puede agregar que y cualquier otro estilo a la hoja de estilos existente enstyle.css.
Configuración del lado del cliente
Lo primero que quieres que haga tu script cliente es ver si hay un parámetro id en la URL. Esto indica que alguien ha seguido un enlace en un texto para unirse a una sesión de chat en curso. Si lo hay, puede obtener inmediatamente las credenciales necesarias para unirse desde el punto final del servidor. /answer/:sessionId en el servidor. El manejo de la respuesta del servidor ocurre en una de dos funciones, initializeSession o handleErrorque veremos en un minuto:
let params = new URLSearchParams(window.location.search);
let ongoingId = params.get('id');
if (ongoingId) {
fetch('/answer/' + ongoingId).then(function fetch(res) {
return res.json();
}).then(function fetchJson(json) {
initializeSession(json.apiKey, json.sessionId, json.token);
}).catch(function catchErr(error) {
handleError(error);
});
}Una vez cubierto el caso menos común en el que alguien quiere unirse a una sesión específica, puede configurar la secuencia de comandos que utilizará para gestionar las acciones en la interfaz. Este es también un buen lugar para definir handleError. Si quieres, esta función puede ser mucho más complicada que su forma actual, donde sólo envía el error a la consola. Después de esto puedes seleccionar elementos de nivel superior que recibirán funcionalidad dinámica. Si los botones que has seleccionado existen puedes asignarles manejadores de clic:
function handleError(error) {
if (error) {
console.error(error);
}
}
var askBtn = document.querySelector('#askBtn');
var answerBtn = document.querySelector('#answerBtn');
var addPhone = document.querySelector('#addNumber');
var phoneBtn = document.querySelector('#phoneBtn');
if (askBtn) askBtn.onclick = askQuestion;
if (answerBtn) answerBtn.onclick = answerQuestion;
if (phoneBtn) phoneBtn.onclick = addPhoneNumber; Inicializar una sesión
El proceso de inicialización de una sesión de preguntas y respuestas comienza haciendo clic en los botones Preguntar o Responder. Los manejadores para esos botones askQuestion y answerQuestionson prácticamente idénticos. En primer lugar, suprimen la acción predeterminada del enlace y, a continuación, obtienen las credenciales de chat del punto final correspondiente en el servidor. Si es posible crear o unirse a una sesión, initializeSession se llama a , con sus credenciales. En el caso de answerQuestionsi nadie está haciendo una pregunta, el ayudante ve en su lugar la opción de proporcionar su número de teléfono:
function askQuestion(e) {
e.preventDefault();
fetch('/ask').then(function fetch(res) {
return res.json();
}).then(function fetchJson(json) {
initializeSession(json.apiKey, json.sessionId, json.token);
}).catch(function catchErr(error) {
handleError(error);
});
}
function answerQuestion(e) {
e.preventDefault();
fetch('/answer').then(function fetch(res) {
return res.json();
}).then(function fetchJson(json) {
if (json.wait) {
addPhone.style.display = 'block';
return;
}
initializeSession(json.apiKey, json.sessionId, json.token);
}).catch(function catchErr(error) {
handleError(error);
});
}La función initializeSession puede ser la lógica más compleja de toda la aplicación. Sorprendentemente, la API de OpenTok simplifica la lógica necesaria. Se encarga de la coordinación entre los oyentes de la sesión de chat y los elementos DOM, de modo que las tareas de bajo nivel como la creación de un elemento <video> y asignar su fuente se realicen entre bastidores.
La función crea primero una instancia de la sesión proporcionando a la API de OpenTok la clave API y el ID de sesión.
Los métodos estáticos de la API OpenTok del lado del cliente están disponibles bajo la variable
OTvariable. Puedes ignorar cualquier queja del editor sobreOTno están definidos. Sin embargo, para una aplicación más robusta sería mejor hacer alguna comprobación de errores y verificar queOTes definida. A la inversa, no querrás definirOTdemasiado asignando cualquier otra cosa en tu código a esa variable, a menos que cambies primero el nombre por defecto del objeto API.
El primer controlador que necesitas es para el evento streamCreated evento. Cuando se crea un flujo en la sesión actual, aparecerá en el elemento con el ID subscriber. También puedes añadir un manejador para notificar al cliente si se desconecta de la sesión.
A continuación, defina algunas propiedades para la fuente de vídeo del editor. El cliente es siempre el editor desde su propia perspectiva. Su vídeo tendrá un tamaño del 100% y se anexará al elemento publisher elemento.
Con la configuración y los controladores de eventos mínimos, puede conectarse a la sesión y añadir la fuente del editor al cliente:
function initializeSession(apiKey, sessionId, token) {
var session = OT.initSession(apiKey, sessionId);
// Subscribe to a newly created stream
session.on('streamCreated', function streamCreated(event) {
var subscriberOptions = {
insertMode: 'append',
width: '100%',
height: '100%'
};
session.subscribe(event.stream, 'subscriber', subscriberOptions, handleError);
});
session.on('sessionDisconnected', function sessionDisconnected(event) {
console.log('You were disconnected from the session.', event.reason);
});
// initialize the publisher
var publisherOptions = {
insertMode: 'append',
width: '100%',
height: '100%'
};
var publisher = OT.initPublisher('publisher', publisherOptions, handleError);
// Connect to the session
session.connect(token, function callback(error) {
if (error) {
handleError(error);
} else {
// If the connection is successful, publish the publisher to the session
session.publish(publisher, handleError);
}
});
} Cómo guardar un número de teléfono
En este punto, deberías tener una línea directa que funcione. Si alguien hace clic en el botón Hacer una pregunta y, poco después, otra persona hace clic en Responder una pregunta, las dos partes deberían encontrarse chateando por vídeo y, con suerte, ser capaces de resolver todos sus enigmas de JavaScript. Lo único que queda por añadir es la posibilidad de enviar un número de teléfono por SMS en caso de que no haya preguntas en ese momento pero surja alguna más tarde.
El manejador addPhoneNumber handler es, como los handlers de los botones, simplemente suprimir el evento por defecto y luego hacer un fetch. En este caso POST al servidor, estableciendo el parámetro Content-Type a application/json y se cadena el valor del campo #phoneNumber campo. Si tiene éxito, volverá a ocultar el campo de entrada del número de teléfono:
function addPhoneNumber(e) {
e.preventDefault();
fetch('/text', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({phone: document.querySelector('#phoneNumber').value})
}).then(() => {
addPhone.style.display = 'none';
}).catch(function catchErr(error) {
handleError(error);
});
}
Próximos pasos
Hay un montón de funciones diferentes que podrías añadir a tu línea directa, y un montón de líneas directas diferentes que podrías crear. Sería bueno que los usuarios vieran cuántas personas están esperando con preguntas o respuestas, y añadiría solidez tener la posibilidad de volver a conectarse a una sesión que se ha interrumpido.
Dado que ya está utilizando la API Nexmo, es posible que desee añadir la opción de hacer un simple chat de voz. Y puesto que ya está utilizando OpenTok, que sin duda podría considerar la adición de características como pantalla compartida o la opción de archivar preguntas comunes.
Puede obtener más información sobre lo que puede hacer con OpenTok en el Centro de desarrollo de TokBoxy puedes ver y remezclar el código de este ejemplo en Glitch:
Compartir:
Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.