https://d226lax1qjow5r.cloudfront.net/blog/blogposts/javascript-hotline-with-opentok-and-nodejs-dr/Building-a-JavaScript-Hotline-with-OpenTok-and-Node.js.png

Aufbau einer JavaScript-Hotline mit OpenTok und Node.js

Zuletzt aktualisiert am April 27, 2021

Lesedauer: 10 Minuten

Es gab einmal eine Zeit, in der man bei Fragen zu JavaScript ins IRC (Internet Relay Chat) ging, um jemanden zu finden, der sie beantworten konnte. IRC ist allerdings eine sehr alte Technologie im Internet. Viele JavaScript-Entwickler haben heute vielleicht nicht einmal einen IRC-Client oder haben jemals einen benutzt. Die meisten hingegen sind mit Video-Chat sehr vertraut.

OpenTok ermöglicht bereits einen schnellen Einstieg in einen einfachen Video-Chat zwischen zwei Teilnehmern. Mit ein wenig Warteschlangenlogik können Sie im Handumdrehen eine Hotline für JavaScript-Fragen oder andere Fragen einrichten.

Um Ihre Hotline noch freundlicher zu gestalten, können Sie das Projekt auf Glitch. Das bedeutet weniger Einrichtungsaufwand für Sie. Außerdem wird deine Hotline dadurch noch mehr hilfreicher, da das komplette Projekt anderen zur Verfügung gestellt wird, die es für ihre eigenen Hotlines umgestalten können.

Erste Schritte auf Glitch

Wenn Sie zu einem funktionierenden Projekt übergehen wollen, können Sie das JavaScript-Hotline-Projekt auf Glitch sofort nachbauen. Ansonsten können Sie in wenigen Schritten Ihre eigene Hotline von Grund auf programmieren. Um zu beginnen, erstellen Sie ein neues Projekt auf Glitch und wählen Sie die hello-express Vorlage.

Um einen Video-Chat mit OpenTok, opentok ist eigentlich das einzige zusätzliche Paket, das Sie benötigen. Wenn man die Option hinzufügt, einen Text zu erhalten, wenn jemand eine Frage beantwortet haben möchte, kann die Hotline besser mit Schwankungen im Nutzeraufkommen umgehen. Um dies zu unterstützen, können Sie auch body-parser um Formulareingaben zu empfangen und nexmo um Ihre Texte zu senden:

pnpm install opentok body-parser nexmo -s

Sie können das Beispiel wiederverwenden server.js, index.html, und client.js Dateien bereits in Ihrem Glitch-Projekt verwenden. Das bedeutet, dass Ihre Einrichtung so gut wie abgeschlossen ist.

Umgebungsvariablen bereitstellen

Ihre .env Datei wird in etwa so aussehen:

OPENTOK_API_KEY="12345678" OPENTOK_SECRET="12a3b4c567d89e0f1234567890ab12345678c901" NEXMO_API_KEY="12ab3456" NEXMO_API_SECRET="123AbcdefghIJklM" FROM_PHONE="441234567890"

Um echte Werte für diese Variablen bereitstellen zu können, benötigen Sie sowohl bei OpenTok als auch bei Nexmo ein Entwicklerkonto. Außerdem benötigen Sie ein OpenTok-Projekt und eine virtuelle Nexmo-Nummer.

Innerhalb Ihres OpenTok Account Dashboarderstellen Sie ein neues Projekt für Ihre Hotline mit dem Projekttyp "OpenTok API". Nachdem Sie dem Projekt einen Namen gegeben und einen Video-Codec ausgewählt haben (VP8 sollte ausreichen), sehen Sie den API-Schlüssel und das Geheimnis. Sie können diese in OPENTOK_API_KEY und OPENTOK_SECRETeinfügen.

Ihre Nexmo-Zugangsdaten, die Sie in die folgenden Felder einfügen können NEXMO_API_KEY und NEXMO_API_SECRETeinfügen können, sollten auf der Seite "Getting Started" in Ihrem Nexmo Dashboard. Du kannst jede Telefonnummer aus "[Your Numbers](${CUSTOMER_DASHBOARD_URL}/your-numbers" ohne Konfiguration für FROM_PHONEverwenden, da Sie von dieser Nummer aus SMS senden, aber keine SMS oder Anrufe empfangen.

Einrichten des Servers

Ihr Server enthält bereits einige Initialisierungen, eine View-Route für den Anwendungsstamm und einen Listener, der den Server startet. Sie können den Initialisierungsabschnitt ein wenig ändern, um auch die body-parser Middleware zu verwenden:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(express.static('public'));
app.use(bodyParser.json());

Unterhalb dieses Blocks können Sie neue OpenTok- und Nexmo-Objekte hinzufügen, die mit den Werten aus .envinitialisiert werden, sowie zwei leere Arrays. waiting ist für die Sitzungs-IDs von Chats, die auf einen Helfer warten, der eine Frage beantwortet, und helpers sind die Telefonnummern von Personen, die in Ausfallzeiten Hilfe angeboten haben, wenn niemand eine Frage hatte.

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 = [];

Im Rest der Datei, zwischen der Standardroute und dem Hörer, können Sie die anderen Routen deklarieren. Darunter brauchen Sie eine Funktion, um jemandem eine SMS zu schicken, der angeboten hat, Fragen zu beantworten:

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

Hinzufügen von Teilnehmern zu Fragen und Antworten

Die grundlegendste Funktion der Hotline-Anwendung besteht darin, eine Person, die eine Frage stellt, mit jemandem zu verbinden, der eine Antwort geben kann. Der direkteste Weg, dies zu erreichen, besteht darin, eine Video-Chat-Sitzung einzurichten, wenn jemand erklärt, dass er eine Frage stellen möchte. Dann können Sie die nächste verfügbare Person, die antworten möchte, als zweiten Teilnehmer hinzufügen. Mit Hilfe eines Datenspeichers könnten Sie eine robustere Methode zur Handhabung dieser aktiven Sitzungen erstellen, aber für Testzwecke sollte ein Array ausreichen.

Wenn jemand eine Frage hat, können Sie eine neue OpenTok-Sitzung erstellen und ihre ID in dem waiting Array speichern. Dann können Sie mit der neuen ID, dem OpenTok-API-Schlüssel der Anwendung und einem Token, das diesen speziellen Kunden identifiziert, antworten:

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

Sie können sehen, dass die /ask Route in einem letzten Schritt die Länge des helpers Arrays. Wenn sie Elemente darin findet, ruft sie die textHelper Funktion auf. Wir werden textHelper aber wenn Sie Ihre Hotline vereinfachen wollen, indem Sie nur die Leute verbinden, die Ihre App gerade benutzen, können Sie diese gesamte Bedingung entfernen.

Wenn nun jemand anbietet, eine Frage zu beantworten, können Sie ein ähnliches Antwortobjekt mit Werten für die erste Sitzung in der waiting Warteschlange. Wenn die Warteschlange leer ist, können Sie eine Antwort senden, die dem Kunden mitteilt, dass derzeit niemand um Hilfe bittet:

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

Texting a Helper When Someone Asked a Question

Es verkompliziert unseren bestehenden Arbeitsablauf ein wenig, aber wenn potenzielle Helfer ihre Telefonnummer angeben können und eine SMS erhalten, wenn eine neue Fragestunde bereit ist, kann die Hotline besser mit Flauten umgehen.

Das Speichern der Telefonnummer einer Person ist nur ein paar Zeilen Code. Der /text Endpunkt erhält die Telefonnummer in seinem Anfragekörper und kann sie dann in eine helpers Warteschlange hinzufügen. Anschließend gibt er einen "OK"-Status zurück:

app.post('/text', function(request, response) {
  let phone = request.body.phone;
  helpers.push(phone);
  response.sendStatus(200);
});

Wir können nun die gespeicherte Telefonnummer des Helfers in der textHelper Funktion, die von der Route aufgerufen wird /ask Route aufgerufen wird. Die Funktion holt sich die erste Rufnummer aus helpers und sendet ihr einen Link zu einer bestimmten Video-Chat-Sitzung:

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

Wenn jemand einem Sitzungslink folgt, den er per Textnachricht erhalten hat, kann der Client vom Server Anmeldeinformationen anfordern, um dieser speziellen Sitzung beizutreten. Die Anwendung kann die Sitzung sicher aus der waiting Warteschlange entfernen, sobald sie tatsächlich angefordert wurde:

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

Hinzufügen einer Schnittstelle

Zum Testen ist es am einfachsten, wenn Sie Ihr gesamtes HTML auf einer Seite halten. Das Beispiel index.html importiert bereits client.js. Oberhalb dieses Skript-Tags müssen Sie auch die OpenTok-Client-Bibliothek von den OpenTok-Servern importieren:

<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>

Sie können den Inhalt des <body> Tags durch drei Blöcke ersetzen: Schaltflächen, um den Prozess des Fragens oder Antwortens einzuleiten, ein Pseudoformular, um die Telefonnummern von Personen zu sammeln, die eine SMS erhalten möchten, und die Ziele für Ihre Videoelemente:

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

Wir werden uns in diesem Tutorial nicht mit CSS befassen, aber zumindest sollten Sie wahrscheinlich die #addNumber und #videos Elemente beim ersten Laden der Seite ausblenden. Sie können dieses und alle anderen Stilelemente zum bestehenden Stylesheet hinzufügen unter style.css.

Client-seitige Einrichtung durchführen

Das erste, was Ihr Client-Skript tun soll, ist zu prüfen, ob es einen id Parameter in der URL vorhanden ist. Dies bedeutet, dass jemand einem Link in einem Text gefolgt ist, um einer laufenden Chatsitzung beizutreten. Wenn dies der Fall ist, können Sie sofort die für den Beitritt erforderlichen Anmeldeinformationen von dem /answer/:sessionId Endpunkt auf dem Server abrufen. Die Bearbeitung der Serverantwort erfolgt in einer von zwei Funktionen, initializeSession oder handleErrordie wir beide in einer Minute behandeln werden:

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

Nachdem Sie den weniger häufigen Fall abgedeckt haben, dass jemand einer bestimmten Sitzung beitreten möchte, können Sie das Skripting einrichten, das Sie für die Handhabung von Aktionen in der Schnittstelle verwenden werden. Dies ist auch ein guter Ort, um zu definieren handleError. Wenn Sie möchten, kann diese Funktion viel umfangreicher sein als ihre jetzige Form, bei der sie lediglich den Fehler an die Konsole sendet. Danach können Sie Elemente der obersten Ebene auswählen, die eine dynamische Funktionalität erhalten sollen. Wenn die von Ihnen ausgewählten Schaltflächen existieren, können Sie ihnen Klick-Handler zuweisen:

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;

Initialisierung einer Sitzung

Der Prozess der Initialisierung einer Frage- und Antwortsitzung beginnt mit einem Klick auf die Schaltflächen Fragen oder Antworten. Die Handler für diese Schaltflächen, askQuestion und answerQuestionsind fast identisch. Sie unterdrücken zunächst die Standardaktion des Links und rufen dann die Chat-Zugangsdaten vom entsprechenden Endpunkt auf dem Server ab. Wenn es möglich ist, eine Sitzung zu erstellen oder ihr beizutreten, initializeSession wird mit den Anmeldeinformationen aufgerufen. Im Fall von answerQuestionWenn niemand eine Frage stellt, sieht der Helfer stattdessen die Option, seine Telefonnummer anzugeben:

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

Die Funktion initializeSession Funktion kann die komplexeste Logik in der gesamten Anwendung sein. Erstaunlicherweise vereinfacht die OpenTok-API tatsächlich die erforderliche Logik. Sie kümmert sich um die Koordination zwischen den Chat-Session-Listenern und den DOM-Elementen, so dass Aufgaben auf niedrigerer Ebene wie das Erstellen eines <video> Element und die Zuweisung seiner Quelle hinter den Kulissen ablaufen.

Die Funktion erstellt zunächst eine Instanz der Sitzung, indem sie der OpenTok-API den API-Schlüssel und die Sitzungs-ID übermittelt.

Statische Methoden in der clientseitigen OpenTok-API sind unter der OT Variable verfügbar. Sie können alle Beschwerden des Editors darüber ignorieren OT nicht definiert ist. Für eine robustere Anwendung wäre es jedoch am besten, eine Fehlerprüfung durchzuführen und zu verifizieren, dass OT ist. tatsächlich definiert ist. Umgekehrt sollten Sie nicht zu viel definieren OT nicht zu viel definieren, indem Sie dieser Variablen irgendetwas anderes in Ihrem Code zuweisen, es sei denn, Sie ändern zuerst den Standard-API-Objektnamen.

Der erste Handler, den Sie benötigen, ist für das streamCreated Ereignis. Wenn ein Stream in der aktuellen Sitzung erstellt wird, erscheint er in dem Element mit der ID subscriber. Sie können auch einen Handler hinzufügen, um den Client zu benachrichtigen, wenn die Verbindung zur Sitzung unterbrochen wird.

Als nächstes definieren Sie einige Eigenschaften für den Video-Feed des Herausgebers. Der Kunde ist immer der Herausgeber aus seiner eigenen Perspektive. Sein Video hat eine Größe von 100 % und wird an das publisher Element angehängt.

Wenn die minimalen Ereignisbehandlungen und die Konfiguration vorhanden sind, können Sie eine Verbindung zur Sitzung herstellen und den Publisher-Feed zum Client hinzufügen:

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

Speichern einer Rufnummer

Jetzt sollten Sie eine funktionierende Hotline haben. Wenn jemand auf die Schaltfläche "Frage stellen" klickt und kurz darauf jemand anderes auf "Frage beantworten" klickt, sollten sich die beiden Parteien in einem Videochat wiederfinden und hoffentlich alle ihre JavaScript-Rätsel lösen können. Das Einzige, was noch hinzugefügt werden muss, ist die Möglichkeit, eine Telefonnummer per SMS zu senden, für den Fall, dass es im Moment keine Fragen gibt, aber später eine auftaucht.

Der addPhoneNumber Handler ist, wie die Button-Handler, nur die Unterdrückung der Standard-Ereignis und dann tun, ein fetch. In diesem Fall werden Sie POST Daten an den Server, wobei die Content-Type auf application/json setzen und den Wert des #phoneNumber Feldes. Wenn dies erfolgreich ist, blenden Sie das Eingabefeld für die Telefonnummer wieder aus:

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

Nächste Schritte

Es gibt viele verschiedene Funktionen, die Sie zu Ihrer Hotline hinzufügen könnten, und viele verschiedene Hotlines, die Sie erstellen könnten. Es wäre schön, wenn die Nutzer sehen könnten, wie viele Leute mit Fragen oder Antworten warten, und es wäre ein Plus an Stabilität, wenn man eine unterbrochene Sitzung wiederherstellen könnte.

Da Sie bereits die Nexmo-API verwenden, könnten Sie die Option hinzufügen, einen einfachen Voice-Chat. Und da Sie bereits OpenTok verwenden, könnten Sie auf jeden Fall Funktionen hinzufügen wie Bildschirmfreigabe oder die Option zum zu archivieren. allgemeine Fragen zu archivieren.

Sie können mehr darüber lesen, was Sie mit OpenTok tun können, im TokBox-Entwicklerzentrumund Sie können den Code für dieses Beispiel auf Glitch ansehen und nachbauen:

Teilen Sie:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann MeansEntwickler Pädagoge

Ich bin ein JavaScript-Entwickler und ein Developer Educator bei Vonage. Im Laufe der Jahre habe ich mich für Templates, Node.js, progressive Web-Apps und Offline-First-Strategien begeistert, aber was ich immer geliebt habe, ist eine nützliche, gut dokumentierte API. Mein Ziel ist es, Ihre Erfahrung mit unseren APIs so gut wie möglich zu gestalten.