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

Erstellen einer Video-Chat-Applikation mit OpenTok und Nexmo In-App Messaging

Zuletzt aktualisiert am May 11, 2021

Lesedauer: 6 Minuten

In diesem Blog-Beitrag werden wir eine Webanwendung erstellen, mit der Benutzer per Video chatten und sich gegenseitig Nachrichten schicken können. OpenTok und Nexmo In-App Messaging.

Um den vollständigen Code zu sehen, besuchen Sie bitte das folgende Repo. Sie können sich auch unser jüngsten Webinar das die Anwendung behandelt.

Voraussetzungen

Vonage API-Konto

Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.

Aufbau der App

Erstellen Sie ein Verzeichnis und benennen Sie es nach Ihren Wünschen:

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

Mit den folgenden Befehlen erstellen wir einige Dateien und Unterordner in diesem Verzeichnis:

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

Unsere Projektstruktur sollte nun wie folgt aussehen:

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

Abhängigkeiten

Wir erstellen ein NPM-Projekt und installieren alle für das Projekt erforderlichen Abhängigkeiten:

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

Lassen Sie uns nun unseren Server erstellen, indem wir den Servercode in die Datei server.js Datei hinzufügen.

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

Bitte beachten Sie, dass wir einen Server erstellt haben, der ExpressJS erstellt haben und sowohl für OpenTok als auch für Nexmo leere Anmeldeinformationen zurückgeben. Keine Sorge, wir werden die Anmeldeinformationen in den nächsten Schritten generieren, aber bevor wir das tun, lassen Sie uns fortfahren und unsere Nexmo Messaging-Anwendung mit der Nexmo CLI erstellen:

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

Mit dem Befehl app:create Befehl erhalten wir die Anwendungs-ID für unsere video-messaging-app zusammen mit einem privaten Schlüssel, der dem Verzeichnis hinzugefügt wird. Bitte beachten Sie, dass wir die Antwort- und Ereignis-URLs als Beispiel-URLs festgelegt haben, aber wir können diese später ändern. Mit dem Befehl conversation:create Befehl haben wir auch eine Konversation namens Nexmo In-App Messaging. Daraus ergibt sich eine Konversations-ID, mit der wir uns später mit der Konversation verbinden werden. Der Befehl user:create Befehl ermöglicht auch die Erstellung eines an die Anwendung gebundenen Benutzers. Bitte notieren Sie sich den Namen dieses Benutzers, da wir ihn als Teil unseres JWT-Generierungsprozesses verwenden werden.

Jetzt erstellen wir ein OpenTok-API-Projekt über das TokBox-Dashboard, damit Sie Zugriff auf den API-Schlüssel und das Geheimnis erhalten.

Öffnen wir nun unsere config.js Datei, damit wir unsere Anmeldedaten speichern können:

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

Vergewissern Sie sich, dass Sie die entsprechenden Anmeldedaten in die config.js Datei

Importieren der Konfigurationsvariablen:

In unserer server.js Datei importieren wir die Konfigurationsvariablen, so dass wir diese für die Instanziierung von OpenTok und Nexmo Klassen.

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

Aktualisieren wir nun den GET-Anforderungspfad, damit wir gültige Anmeldedaten zurückgeben können:

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

Mit dem obigen Code erzeugen wir jedes Mal, wenn jemand den / Pfad von seinem Browser aus besucht:

  • OpenTok-Sitzungs-ID

  • OpenTok Token für die entsprechende Sitzungs-ID

  • JWT-Token für unsere Nexmo-Anwendung mit den entsprechenden ACLs

Nachdem wir nun einen Mechanismus zum Abrufen der Anmeldeinformationen erstellt haben, können wir uns nun der Client-Seite der Anwendung zuwenden.

Öffnen Sie die index.js Datei, die sich im Verzeichnis js Verzeichnis.

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

In dem obigen Code initialisieren wir eine OpenTok Sitzung durch den Aufruf der initSession Methode für das OT Objekt. Anschließend erstellen wir einen Publisher und setzen die folgenden Event-Listener: streamCreated, streamDestroyed, und sessionConnected. Diese Ereignis-Listener werden verwendet, um Streams zu abonnieren, wenn ein Stream erstellt wird, um eine Nachricht zu drucken, wenn ein Stream zerstört wird, und um in der Sitzung zu veröffentlichen, wenn wir verbunden sind. Anschließend stellen wir eine Verbindung zur Sitzung her, indem wir das Token verwenden, das wir auf dem Server erzeugt haben.

Nachdem wir nun den Code für einen Video-Chat hinzugefügt haben, fügen wir In-App Messaging hinzu.

class ChatApp {
  constructor() {
   this.messageTextarea = document.getElementById('messageTextarea');
   this.messageFeed = document.getElementById('messageFeed');
   this.sendButton = document.getElementById('send');
   this.loginForm = document.getElementById('login');
  }
}

Die ChatApp Klasse wird verwendet, um unsere In-App Messaging-Funktionen hinzuzufügen. Wir werden auch den Verweis auf ein paar DOM-Elemente aufnehmen, die wir in unserer index.ejs Datei erstellen.

Lassen Sie uns fortfahren und einige Hilfsmethoden zur ChatApp Klasse einige Hilfsmethoden für die Protokollierung unserer Ereignisse und Fehler auf der Konsole hinzu:

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

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

Weiter geht es mit der Instanziierung eines ConversationClient instanziieren und uns mit dem nexmoJWT Token authentifizieren, das von unserem Server generiert wurde:

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

Da wir nun einen Verweis auf die Konversation haben, können wir unsere Konversationsereignisse einrichten:

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

Wir können den Gesprächsverlauf abrufen, indem wir die getEvents Methode für das conversation Objekt aufrufen. Machen wir weiter und erstellen eine Hilfsmethode, um den Chatverlauf im DOM anzuzeigen. Wie Sie unten sehen können, verwenden wir die verschiedenen types um zwischen den Ereignissen zu unterscheiden:

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

Wir sollten auch einige Benutzerereignisse einrichten, um zu wissen, wann der Endbenutzer Aktionen auf der HTML-Seite ausgelöst hat:

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

Stellen wir sicher, dass wir den Aufruf der setupUserEvents() Methode in unserem Konstruktor aufzurufen:

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

Rekapitulieren wir, was wir im obigen Code getan haben. Wir haben eine Klasse namens ChatApp erstellt, die eine ConversationClient die wir mit dem nexmoJWT Token. Wir setzen auch einen Ereignis-Listener, textauf das Konversationsobjekt, um alle eingehenden Nachrichten abzuhören. Bitte beachten Sie, dass wir zum Abrufen älterer Nachrichten aus der Konversation die getEvents Methode. Wir verwenden einige Ereignis-Listener auf dem DOM, um Informationen anzuzeigen, wenn sich etwas geändert hat.

Nachdem wir nun die ChatApp-Klasse erstellt haben, können wir eine ChatApp-Klasse instanziieren, wenn das Onload-Ereignis ausgelöst wird, damit wir die DOM-Elemente nach Bedarf verwenden können.

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

Nach Fertigstellung unserer index.jsfertiggestellt haben, lassen Sie uns fortfahren und einige Informationen zu unserer index.ejs Datei:

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

Der obige Code wird von unserem Server gerendert, wenn jemand den / Pfad aufruft. Wie Sie sehen können, geben wir unsere Anmeldeinformationen ein, die wir für die OpenTok-Sitzung und den Nexmo Conversation Client verwenden.

Abschließend müssen wir unseren Server so modifizieren, dass er die index.ejs Ansicht mit den richtigen Variablen zu rendern:

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

Nachdem wir nun alles eingerichtet haben, fügen wir ein start Skript in unsere package.json Datei hinzufügen, damit wir den Server einfach starten können:

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

Führen Sie npm start in Ihrem Terminal und führen Sie die Anwendung aus!

Schlussfolgerung

In diesem Blog haben wir wichtige OpenTok- und Nexmo In-App Messaging-Konzepte vorgestellt, die die Möglichkeit bieten, Web-Applikationen mit Live-Video und In-App Messaging zu versehen. Um den vollständigen Code zu sehen, besuchen Sie bitte das folgende Repo.

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/63f654d765/manik.png
Manik SachdevaVonage Ehemalige

Manik ist ein leitender Software-Ingenieur. Er arbeitet gerne mit Entwicklern zusammen und entwirft APIs. Wenn er nicht gerade APIs oder SDKs entwickelt, kann man ihn bei Konferenzen und Meetups als Redner antreffen.