
Compartilhar:
Manik é engenheiro de software sênior. Ele gosta de trabalhar com desenvolvedores e criar APIs. Quando não está desenvolvendo APIs ou SDKs, é possível encontrá-lo palestrando em conferências e encontros.
Crie um aplicativo de chat por vídeo com o OpenTok e o Nexmo In-App Messaging
Tempo de leitura: 6 minutos
Nesta postagem do blog, vamos criar um aplicativo web que permita aos usuários fazerem videochamadas e trocarem mensagens entre si usando OpenTok e Nexmo In-App Messaging.
Para ver o código completo, acesse o seguinte repositório. Você também pode conferir nosso webinar recente que aborda o aplicativo.
Pré-requisitos
A conta do TokBox e o Account e a chave e o segredo da API de um projeto de API
Estrutura do aplicativo
Crie uma pasta e dê a ela o nome que quiser:
Vamos criar alguns arquivos e subpastas dentro do diretório usando os seguintes comandos:
A estrutura do nosso projeto agora deve ficar assim:
video-messaging-app
├── package.json
├── package-lock.json
├── views
│ ├── index.ejs
├── public
│ ├── js
│ ├── index.js
├── config.js
├── server.js
Dependências
Vamos criar um projeto NPM e instalar todas as dependências necessárias para o projeto:
npm init -y // we use the -y flag to skip through the questions
npm install opentok @opentok/client nexmo nexmo-stitch express ejsAgora, vamos criar nosso servidor adicionando o código do servidor ao server.js arquivo.
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}`));
Observe que criamos um servidor usando ExpressJS e estamos retornando credenciais vazias tanto para o OpenTok quanto para o Nexmo. Não se preocupe, vamos gerar as credenciais nas próximas etapas, mas antes disso vamos criar nosso aplicativo de mensagens do Nexmo usando a CLI do Nexmo:
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 IDUsando o app:create comando, obteremos o ID do aplicativo para o nosso video-messaging-app , juntamente com uma chave privada que será adicionada ao diretório. Observe que definimos as URLs de resposta e de evento como exemplos, mas podemos alterá-las posteriormente. Usando o conversation:create , também criamos uma conversa chamada Nexmo In-App Messaging. Isso resultará em um ID de conversa que usaremos mais tarde para nos conectarmos à conversa. O user:create comando também permite criar um usuário vinculado ao aplicativo. Anote o nome desse usuário, pois vamos usá-lo como parte do nosso processo de geração de JWT.
Agora vamos criar um projeto da API do OpenTok usando o painel do TokBox para que você possa obter acesso à chave e ao segredo da API.
Agora, vamos abrir nosso config.js arquivo para que possamos armazenar nossas credenciais:
module.exports = {
opentokApiKey: '',
opentokApiSecret: '',
nexmoApiKey: '',
nexmoApiSecret: '',
nexmoApplicationId: '',
nexmoPrivateKey: '',
nexmoConversationId: '',
};Certifique-se de adicionar as credenciais adequadas ao config.js arquivo
Importando as variáveis de configuração:
No nosso server.js arquivo, vamos importar as variáveis de configuração para que possamos usá-las para instanciar OpenTok e Nexmo classes.
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,
});Agora vamos atualizar o caminho da solicitação GET para que possamos retornar credenciais 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 o código acima, criaremos o seguinte sempre que alguém acessar a / caminho a partir do navegador:
ID da sessão do OpenTok
Token do OpenTok para o ID da sessão correspondente
Token JWT para nosso aplicativo Nexmo com as ACLs adequadas
Agora que criamos um mecanismo para obter as credenciais, vamos prosseguir e trabalhar no lado do cliente do aplicativo.
Abra o index.js arquivo localizado no js diretório.
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');
}
});
No código acima, inicializamos um OpenTok Session chamando o initSession método no OT objeto. Em seguida, criamos um publisher e definimos os seguintes ouvintes de eventos: streamCreated, streamDestroyede sessionConnected. Esses ouvintes de evento são usados para se inscrever em fluxos quando um fluxo é criado, exibir uma mensagem quando um fluxo é destruído e publicar na sessão quando estivermos conectados. Em seguida, prosseguimos com a conexão à sessão usando o token que geramos no servidor.
Agora que já adicionamos o código para o chat por vídeo, vamos adicionar o recurso de 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');
}
}A ChatApp classe será usada para adicionar nossos recursos de In-App Messaging. Também obteremos a referência a alguns elementos DOM que criaremos em nosso index.ejs arquivo.
Vamos adicionar alguns métodos auxiliares à ChatApp classe para registrar nossos eventos e erros no console:
errorLogger(error) {
console.log(`There was an error ${error}`);
}
eventLogger(event) {
console.log(`This event happened: ${event}`);
}Continuando, precisamos instanciar um ConversationClient e nos autenticar com o nexmoJWT token gerado pelo nosso 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)
}
Agora que já temos uma referência à conversa, vamos definir nossos eventos de conversa:
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 o histórico de conversas chamando o getEvents método no conversation objeto. Vamos criar um método auxiliar para que possamos exibir o histórico de bate-papo no DOM. Como você pode ver abaixo, estamos usando os diferentes types para distinguir entre os 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
})
}
Também devemos definir alguns eventos de usuário para saber quando o usuário final aciona alguma ação na 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);
});
}
Vamos nos certificar de chamar o setupUserEvents() em nosso construtor:
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();
}
}Vamos recapitular o que fizemos no código acima. Criamos uma classe chamada ChatApp que cria um ConversationClient que autenticamos usando o nexmoJWT token. Também definimos um ouvinte de eventos, text, no objeto de conversa para monitorar quaisquer mensagens recebidas. Observe que, para recuperar mensagens mais antigas da conversa, usamos o getEvents método. Usamos alguns ouvintes de eventos no DOM para exibir informações quando há alterações.
Agora que criamos a classe ChatApp, vamos instanciar uma instância dessa classe quando o evento onload for disparado, para que possamos usar os elementos do DOM conforme necessário.
window.onload = () => {
new ChatApp();
}
Depois de concluir nosso index.js, vamos em frente e adicionar algumas informações ao nosso index.ejs arquivo:
<!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>
O código acima é gerado pelo nosso servidor quando alguém acessa o / caminho. Como você pode ver, passamos nossas credenciais, que usamos para a sessão do OpenTok e para o Nexmo Conversation Client.
Por fim, vamos modificar nosso servidor para renderizar a index.ejs visualização com as variáveis corretas:
res.render('index.ejs', {
opentokApiKey,
opentokSessionId,
opentokToken,
nexmoConversationId,
nexmoJWT,
});Agora que já temos tudo configurado, vamos adicionar um start script ao nosso package.json arquivo para que possamos iniciar o servidor com facilidade:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "browserify public/js/index.js -o public/js/bundle.js && node server.js"
}
Execute npm start no seu terminal e execute o aplicativo!
Conclusão
Neste blog, abordamos conceitos importantes do OpenTok e do Nexmo In-App Messaging, demonstrando a capacidade de adicionar vídeo ao vivo e mensagens no aplicativo a aplicativos web. Para ver o código completo, consulte o seguinte repositório.