https://d226lax1qjow5r.cloudfront.net/blog/blogposts/video-chat-javascript-opentok-nexmo-dr/Video-Chat-Application-with-OpenTok-and-Nexmo-In-App-Messaging.png

Créer une application de Video Chat avec OpenTok et Nexmo In-App Messaging

Publié le May 11, 2021

Temps de lecture : 6 minutes

Dans cet article de blog, nous allons créer une application web qui permet aux utilisateurs de discuter en vidéo et de s'envoyer des messages à l'aide d' OpenTok et Nexmo In-App Messaging.

Pour voir le code complet, veuillez consulter la page suivante repo. Vous pouvez également consulter notre récent webinaire qui couvre l'application.

Conditions préalables

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.

Structure de l'application

Créez un répertoire et donnez-lui le nom que vous souhaitez :

mkdir video-messaging-app cd video-messaging-app

Nous allons créer quelques fichiers et sous-dossiers dans le répertoire à l'aide des commandes suivantes :

mkdir public public/js views touch public/js/index.js views/index.ejs server.js config.js

La structure de notre projet devrait maintenant ressembler à ceci :

video-messaging-app
├── package.json
├── package-lock.json
├── views
│   ├── index.ejs
├── public
│   ├── js
│       ├── index.js
├── config.js
├── server.js

Dépendances

Nous allons créer un projet NPM et installer toutes les dépendances nécessaires au projet :

npm init -y // we use the -y flag to skip through the questions
npm install opentok @opentok/client nexmo nexmo-stitch express ejs

Maintenant, créons notre serveur en ajoutant le code du serveur au fichier server.js au fichier

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}`));

Veuillez noter que nous avons créé un serveur en utilisant ExpressJS et que nous renvoyons des identifiants vides pour OpenTok et Nexmo. Ne vous inquiétez pas, nous allons générer les informations d'identification dans les prochaines étapes, mais avant cela, allons de l'avant et créons notre application de messagerie Nexmo à l'aide de la CLI 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 ID

En utilisant la commande app:create nous obtiendrons l'identifiant de l'application pour notre video-messaging-app ainsi qu'une clé privée qui sera ajoutée au répertoire. Veuillez noter que nous avons défini les urls de réponse et d'événement comme des urls d'exemple, mais nous pourrons les modifier ultérieurement. En utilisant la commande conversation:create nous avons également créé une conversation appelée Nexmo In-App Messaging. Il en résultera un identifiant de conversation que nous utiliserons plus tard pour nous connecter à la conversation. La commande user:create permet également de créer un utilisateur lié à l'application. Veuillez noter le nom de cet utilisateur car nous l'utiliserons dans le cadre de notre processus de génération de JWT.

Nous allons maintenant créer un projet API OpenTok à l'aide du tableau de bord TokBox afin de pouvoir accéder à la clé et au secret de l'API.

Ouvrons maintenant notre fichier config.js afin de stocker nos informations d'identification :

module.exports = {
 opentokApiKey: '',
 opentokApiSecret: '',
 nexmoApiKey: '',
 nexmoApiSecret: '',
 nexmoApplicationId: '',
 nexmoPrivateKey: '',
 nexmoConversationId: '',
};

Veillez à ajouter les informations d'identification appropriées au fichier config.js fichier

Importation des variables de configuration :

Dans notre fichier server.js nous allons importer les variables de configuration afin de pouvoir les utiliser pour l'instanciation de OpenTok et 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,
});

Maintenant, mettons à jour le chemin de la requête GET afin de renvoyer des informations d'identification valides :

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,
     });
   }
 });
});

En utilisant le code ci-dessus, nous créerons ce qui suit chaque fois que quelqu'un visitera le chemin / à partir de son navigateur :

  • ID de session OpenTok

  • Token OpenTok pour l'ID de session correspondant

  • Token JWT pour notre application Nexmo avec les ACLs appropriés

Maintenant que nous avons créé un mécanisme pour obtenir les informations d'identification, travaillons sur le côté client de l'application.

Ouvrez le fichier index.js situé dans le répertoire js dans le répertoire

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');
 }
});

Dans le code ci-dessus, nous initialisons une OpenTok en appelant la en appelant la méthode initSession sur l'objet OT . Nous créons ensuite un éditeur et définissons les récepteurs d'événements suivants : streamCreated, streamDestroyed, et sessionConnected. Ces récepteurs d'événements sont utilisés pour s'abonner aux flux lorsqu'un flux est créé, pour imprimer un message lorsqu'un flux est détruit et pour publier dans la session lorsque nous sommes connectés. Nous nous connectons ensuite à la session en utilisant le jeton que nous avons généré sur le serveur.

Maintenant que nous avons ajouté le code pour un Video Chat, ajoutons 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 classe ChatApp sera utilisée pour ajouter nos fonctionnalités In-App Messaging. Nous allons également saisir la référence à quelques éléments du DOM que nous créerons dans notre fichier index.ejs dans notre fichier

Ajoutons quelques méthodes d'aide à la classe ChatApp pour enregistrer nos événements et nos erreurs dans la console :

errorLogger(error) {
   console.log(`There was an error ${error}`);
 }

 eventLogger(event) {
   console.log(`This event happened: ${event}`);
 }

Ensuite, nous devons instancier un fichier ConversationClient et nous authentifier avec le jeton nexmoJWT généré par notre serveur :

 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)
 }

Maintenant que nous avons une référence à la conversation, allons-y et configurons nos événements de conversation :

 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);
 }

Nous pouvons récupérer l'historique des conversations en appelant la méthode getEvents sur l'objet conversation sur l'objet Continuons et créons une méthode d'aide pour afficher l'historique de la conversation dans le DOM. Comme vous pouvez le voir ci-dessous, nous utilisons les différents types pour distinguer les événements :

  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
   })
 }

Nous devons également mettre en place des événements utilisateur pour savoir quand l'utilisateur final a déclenché des actions sur la page 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);
  });
 }

Veillons à appeler la méthode setupUserEvents() dans notre constructeur :

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();
  }
}

Récapitulons ce que nous avons fait dans le code ci-dessus. Nous avons créé une classe appelée ChatApp qui crée un ConversationClient que nous authentifions à l'aide du jeton nexmoJWT pour l'authentifier. Nous avons également mis en place un écouteur d'événements, textsur l'objet conversation pour écouter les messages entrants. Veuillez noter que pour récupérer les anciens messages de la conversation, nous utilisons la méthode getEvents pour récupérer les anciens messages de la conversation. Nous utilisons des récepteurs d'événements sur le DOM pour afficher des informations lorsque des éléments sont modifiés.

Maintenant que nous avons créé la classe ChatApp, nous allons instancier une classe ChatApp lorsque l'événement onload se déclenche afin de pouvoir utiliser les éléments du DOM si nécessaire.

window.onload = () => {
 new ChatApp();
}

Après avoir complété notre index.jsnous allons ajouter quelques informations à notre fichier index.ejs fichier :

<!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>

Le code ci-dessus est rendu par notre serveur lorsque quelqu'un visite le / chemin d'accès. Comme vous pouvez le voir, nous transmettons nos informations d'identification que nous utilisons pour la session OpenTok et le client de conversation Nexmo.

Enfin, modifions notre serveur pour qu'il rende la vue index.ejs avec les bonnes variables :

res.render('index.ejs', {
    opentokApiKey,
    opentokSessionId,
    opentokToken,
    nexmoConversationId,
    nexmoJWT,
  });

Maintenant que tout est en place, ajoutons un script start à notre fichier package.json afin de pouvoir facilement démarrer le serveur :

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "browserify public/js/index.js -o public/js/bundle.js && node server.js"
  }

Exécutez npm start dans votre terminal et lancez l'application !

Conclusion

Dans ce blog, nous avons couvert les concepts importants d'OpenTok et de Nexmo In-App Messaging en montrant la possibilité d'ajouter de la vidéo en direct et de la messagerie in-app aux applications web. Pour voir le code complet, veuillez vous référer au repo.

Partager:

https://a.storyblok.com/f/270183/384x384/63f654d765/manik.png
Manik SachdevaAnciens de Vonage

Manik est ingénieur logiciel senior. Il aime travailler avec les développeurs et créer des API. Lorsqu'il ne construit pas d'API ou de SDK, vous pouvez le trouver en train de parler lors de conférences et de rencontres.