https://d226lax1qjow5r.cloudfront.net/blog/blogposts/how-to-build-an-on-page-live-chat-dr/TW_Live-Chat_1200x675.png

Wie man einen On-Page Live-Chat einrichtet

Zuletzt aktualisiert am May 13, 2021

Lesedauer: 19 Minuten

Die Vonage Conversation API ermöglicht es Entwicklern, Konversationsfunktionen zu erstellen, bei denen die Kommunikation über mehrere Medien hinweg stattfinden kann. Ein wichtiger Aspekt dabei ist, dass der Kontext der Unterhaltungen über verschiedene Medien hinweg beibehalten werden kann, was eine Vielzahl von Möglichkeiten eröffnet.

In diesem Tutorial werden die Grundlagen der Funktionsweise der Conversation API erläutert, indem ein rudimentärer On-Page-Live-Chat als Beispiel für einen Anwendungsfall erstellt wird. Diese Chatbox ermöglicht es Kunden, einem Support-Agenten in Echtzeit eine Nachricht zu senden, und der Support-Agent kann dem Kunden antworten.

Darüber hinaus werden wir uns auch mit dem Styling und dem Layout befassen, z. B. wie man ein Slide-in-Chat-Fenster erstellt und die Chat-Nachrichten auf beiden Seiten der Chat-Oberfläche anordnet. Der vollständige Code und die Demo sind auf Glitch verfügbarzur Verfügung, du kannst ihn also gerne nach Belieben umgestalten.

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.

Bevor Sie mit diesem Lernprogramm beginnen, müssen Sie die folgenden Voraussetzungen erfüllen:

  • Haben Node.js auf Ihrem Rechner installiert

  • Installieren Sie die Beta-Version der Vonage CLI

    npm install @vonage/cli -g
  • Richten Sie die CLI so ein, dass Ihr Vonage API-Schlüssel und -Geheimnis verwendet wird, die auf der Einstellungsseite im Vonage Dashboard verfügbar sind.

    vonage config:set --apiKey=VONAGE_API_KEY --apiSecret=VONAGE_API_SECRET

Um die Konsole auf Glitch wie auf einem lokalen Rechner zu verwenden, klicken Sie auf Werkzeugeund dann Protokolle und schließlich, Konsole.

Glitch consoleGlitch console

Auf Störungverwenden Sie nicht das -g Flagge bei der Installation der Vonage CLI

npm install @vonage/cli

Die Vonage CLI verfügt über Plugins, die, wenn sie installiert werden, zusätzliche Funktionen bieten. In diesem Tutorial werden wir mit Conversations arbeiten, daher hier der Befehl zur Installation des Plugins:

vonage plugins:install @vonage/cli-plugin-conversations

Szenario

Was wir aufbauen werden, ähnelt den Schnittstellen auf Websites, die eine Live-Chat-Option anbieten, die in der Regel durch Anklicken einer Schaltfläche, die ein Chat-Fenster öffnet, ausgelöst wird.

Das Chat-Fenster verbindet Sie mit einem Support-Mitarbeiter auf der anderen Seite des Kundendienstportals, und Sie beide können in Echtzeit chatten.

Erstmalige Einrichtung

Eine einfache Möglichkeit, mit dem Projekt zu beginnen, ist die Verwendung von Glitch, da Sie damit eine Node-Anwendung erhalten, die von Anfang an auf Express basiert. Es steht Ihnen frei, jedes andere Node-Framework zu verwenden, das Sie mögen, oder sogar Ihr eigenes zu schreiben, aber für dieses Tutorial werden wir Express verwenden.

Erstellen Sie zunächst eine neue Vonage-Anwendung mit dem vonage apps:create Befehl.

vonage apps:create "Support Agent" --rtc_event_url=https://YOUR_GLITCH_PROJECT.glitch.me/webhooks/event

Schauen wir uns an, was die zusätzlichen Flags und Parameter bewirken. vonage apps:create "NAME_OF_APPLICATION" erstellt eine Vonage-Anwendung mit einem beliebigen Namen, den Sie Ihrer Anwendung geben möchten, und ist erforderlich, damit der Befehl funktioniert.

Die --rtc_event_url gibt die Ereignis-URL an, d. h. den Webhook, an den Vonage alle Ereignisse in der Anwendung sendet.

Wenn Sie den Befehl ausführen, sollten Sie eine Ausgabe erhalten, die in etwa so aussieht (die Pfade sehen so aus, wenn Sie Glitch verwenden):

Application created: aaaaaaaa-bbbb-cccc-dddd-0123456789ab ... Private Key File: .../support_agent.key

Die erzeugte lange Zeichenfolge ist die Anwendungs-ID, die Sie sich merken sollten. Wir werden sie in diesem Tutorial als YOUR_APP_ID während des gesamten Tutorials. Der private Schlüssel wird verwendet, um JWTs zu erzeugen, die zur Authentifizierung Ihrer Interaktionen mit Vonage verwendet werden.

Wenn Sie ls -alausführen, sollten Sie die folgende Datei sehen können support_agent.key Datei in Ihrem Home-Ordner sehen. Verschieben Sie die Datei in ein .data/ Ordner, da Glitch diesen Ordner für sensible Daten verwendet.

mv support_agent.key .data/support_agent.key

Mit dem nächsten Befehl wird ein Benutzer erstellt. Dieser Benutzer ist der Support-Agent, der immer zum Chat hinzugefügt wird. In einem realen Szenario wird es immer eine Handvoll Support-Agenten geben, die mit mehreren Benutzern sprechen, die Unterstützung benötigen.

vonage apps:users:create agent --display_name="Support Agent"

Dadurch wird ein Benutzer generiert und Sie erhalten in der Konsole etwa Folgendes:

USR-00000xxx-000x-0x00-0x00-0x00xx0x0000

Notieren Sie sich diese Benutzer-ID, da Sie sie benötigen, wenn Sie den Support-Mitarbeiter zu dem Gespräch hinzufügen möchten.

Sie können Ihre Anmeldedaten auch in der Datei .env Datei im Stammverzeichnis des Projekts speichern.

NEXMO_API_KEY="x0xx0x0x" NEXMO_API_SECRET="00x0x0x0xxx0x0000x0xx00x" NEXMO_APPLICATION_ID="aaaaaaaa-bbbb-cccc-dddd-0123456789ab" NEXMO_APPLICATION_PRIVATE_KEY_PATH=".data/private.key" SUPPORT_AGENT="USR-00000xxx-000x-0x00-0x00-0x00xx0x0000"

Einrichten Ihrer Node-Anwendung

Wenn Sie mit der grundlegenden hello-express Glitch-Vorlage begonnen hätten, wäre Ihre server.js Datei eher schlicht gehalten und nur Express installiert sein.

const express = require("express");
const app = express();

app.use(express.static("public"));

app.get("/", function(request, response) {
  response.sendFile(__dirname + "/views/index.html");
});

const listener = app.listen(process.env.PORT, function() {
  console.log("Your app is listening on port " + listener.address().port);
});

Aber dies ist ein guter Anfang. Installieren Sie zunächst die Beta-Version der nexmo-Node-Bibliothek, den body-parser und einen Generator für zufällige Benutzernamen:

npm install nexmo@beta body-parser username-generator

Fügen Sie diese Abhängigkeiten der Datei server.js Datei hinzu:

const bodyParser = require('body-parser');
const rug = require('username-generator');

const Nexmo = require('nexmo');

Als nächstes wollen Sie eine Nexmo-Instanz wie folgt instanziieren:

const nexmo = new Nexmo({
  apiKey: process.env.NEXMO_API_KEY,
  apiSecret: process.env.NEXMO_API_SECRET,
  applicationId: process.env.NEXMO_APPLICATION_ID,
  privateKey: process.env.NEXMO_APPLICATION_PRIVATE_KEY_PATH
});

Klären wir die nicht-Vonage-bezogenen Teile der server.js Datei, nämlich die Routen für unsere jeweiligen Webseiten, und die Einrichtung der body-parser Middleware zum Parsen eingehender Anfragen.

app.use(bodyParser.json());
app.use(
  bodyParser.urlencoded({
    extended: true
  })
);
app.use(express.static('public'));

app.get('/', function(request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

app.get('/agent', function(request, response) {
  response.sendFile(__dirname + '/views/agent.html');
});

Wie Sie an den Routen sehen können, gibt es 2 separate Webseiten. Theoretisch wären dies 2 separate Applications, aber für die Zwecke dieses Tutorials haben wir sie zu einem einzigen Projekt zusammengefasst.

Definieren wir eine Zugriffskontrollliste (ACL) in der Datei server.js Datei. Dies ist eine Liste von Pfaden, die Vonage-API-Pfade sind und verwendet werden, um das JWT mit entsprechenden Berechtigungen zu generieren.

const ACL = {
  paths: {
    '/*/users/**': {},
    '/*/conversations/**': {},
    '/*/sessions/**': {},
    '/*/devices/**': {},
    '/*/image/**': {},
    '/*/media/**': {},
    '/*/push/**': {},
    '/*/knocking/**': {},
    '/*/legs/**': {}
  }
};

Wir müssen einige Variablen definieren, um eine Form der In-Memory-Persistenz für diese Tutorial-Anwendung zu implementieren. Ein Überblick über den Prozess ist wie folgt: Wenn die Konversation bereits existiert, werden die Konversationsdetails an die Benutzeroberfläche zurückgegeben. Andernfalls finden diese Schritte statt:

  1. Erstellen Sie einen neuen zufälligen Benutzer (der als Kunde fungiert, der Unterstützung anfordert)

  2. Wenn die Benutzererstellung erfolgreich war, erstellen Sie eine neue Konversation

  3. Wenn die Konversation erfolgreich erstellt wurde, fügen Sie den neu erstellten Benutzer zur Konversation hinzu

  4. Wenn der Benutzer erfolgreich hinzugefügt wurde, fügen Sie den Support-Agenten zu der Unterhaltung hinzu

  5. Wenn der Support-Agent erfolgreich hinzugefügt wurde, werden die aktiven Gesprächsdetails an die Benutzeroberfläche zurückgegeben.

let activeConversationDetails;
let agentMember;

app.route('/api/new').get((req, res) => {
  if (activeConversationDetails) {
    res.json(activeConversationDetails);
  } else {
    nexmo.users.create( /* Creates a new random user */
      {
        name: rug.generateUsername('-')
      },
      (error, user) => {
        if (error) console.log(error);

        if (user) { /* If user creation successful, create a new conversation */
          nexmo.conversations.create(
            {
              display_name: rug.generateUsername()
            },
            (error, conversation) => {
              if (error) console.log(error);

              if (conversation) { /* If conversation creation successful, add the newly created user to the conversation */
                nexmo.conversations.members.add(
                  conversation.id,
                  {
                    action: 'join',
                    user_id: user.id,
                    channel: {
                      type: 'app'
                    }
                  },
                  (error, member) => {
                    if (error) console.log(error);

                    if (member) { /* If user was successfully added, then add the support agent */
                      nexmo.conversations.members.add(
                        conversation.id,
                        {
                          action: 'join',
                          user_id: process.env.SUPPORT_AGENT,
                          channel: {
                            type: 'app'
                          }
                        },
                        (error, agent) => {
                          if (error) console.log(error);
                          const jwt = Nexmo.generateJwt( /* Generate JWT for random user, needed for logging into the client SDK */
                            process.env.NEXMO_APPLICATION_PRIVATE_KEY_PATH,
                            {
                              application_id: process.env.NEXMO_APPLICATION_ID,
                              sub: member.name,
                              exp: new Date().getTime() + 86400,
                              acl: ACL
                            }
                          );
                          if (agent) { /* If agent was successfully added, then return active conversation details */
                            agentMember = agent.id;
                            activeConversationDetails = {
                              user,
                              conversation,
                              member,
                              agent,
                              jwt
                            };
                            res.json(activeConversationDetails);
                          }
                        }
                      );
                    }
                  }
                );
              }
            }
          );
        }
      }
    );
  }
});

Für den Support-Mitarbeiter ist der Weg viel kürzer.

app.route('/api/jwt/:user').get((req, res) => {
  const jwt = Nexmo.generateJwt( /* For programatically generating JWT */
    process.env.NEXMO_APPLICATION_PRIVATE_KEY_PATH,
    {
      application_id: process.env.NEXMO_APPLICATION_ID,
      sub: req.params.user,
      exp: new Date().getTime() + 86400,
      acl: ACL
    }
  );
  res.json({
    jwt: jwt,
    conversation: activeConversationDetails.conversation
  });
});

Schließlich müssen wir die Webhook-URL einrichten, die alle in der Anwendung auftretenden Ereignisse abruft und für das Debugging oder die Entwicklung weiterer Funktionen verwendet werden kann.

app.route('/webhooks/event').post((req, res) => {
  console.log(req.body);
});

Verwendung des Vonage Client SDK für Javascript

Glitch beginnt mit einer einzigen index.html Datei im Verzeichnis Ansichten/ Ordner. Fügen Sie eine weitere HTML-Datei in den Ordner views/ mit dem Namen agent.html.

Add another html fileAdd another html file

Wir werden auch separate CSS- und Javascript-Dateien für jede Seite haben. Um zusätzliche Komplexität durch Modullader zu vermeiden, wurden in diesem Tutorial alle gemeinsamen Funktionen in eine common.js Datei.

Ihr öffentlicher Ordner würde etwa so aussehen:

public/ |-- agent.css |-- agent.js |-- client.css |-- client.js `-- common.js

Der Großteil der Arbeit wird mit dem Vonage Client SDK für Javascript. Sie können die Client-Bibliothek entweder über NPM installieren oder eine im CDN gehostete Version verwenden. Fügen Sie das Skript sowohl in die index.html und ein. agent.html

<script src="https://cdn.jsdelivr.net/npm/nexmo-client@6.0.1/dist/nexmoClient.js"></script>

Die Funktionalität auf der Kunden- und Agentenseite ist ziemlich ähnlich und einige Funktionen können in eine gemeinsame Javascript-Datei abstrahiert werden. Wir brauchen eine Funktion, um die Gesprächsdetails vom Server zu holen, damit sie für die Benutzeroberfläche verwendet werden können.

let activeConversation;

function setupConversation(apiPath) {
  fetch(apiPath) /* To generate the JWT for the agent */
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      new NexmoClient({
        debug: false
      })
        .login(response.jwt) /* Used to log into Nexmo */
        .then(app => {
          console.log('*** Logged into app', app);
          return app.getConversation(response.conversation.id); /* Grabs conversation from Nexmo's server */
        })
        .then(conversation => {
          console.log('*** Retrieved conversations', conversation);
          activeConversation = conversation;
          setupListeners();
        })
        .catch(console.error);
    });
}

Wir möchten auch einige Listener haben, die den von einer der beiden Parteien eingegebenen Text in ihre jeweiligen Chat-Fenster einfügen. Der Text wird aus der Nutzlast gewonnen, die von der Abrufanfrage des Nexmo-Servers zurückgegeben wird.

function setupListeners() {
  const form = document.getElementById('textentry');
  const textbox = document.getElementById('textbox');

  activeConversation.on('text', (sender, message) => {
    console.log(sender, message);
    appendMessage(
      message.body.text,
      `${sender.user.name === 'agent' ? 'agent' : 'input'}`
    );
  });

  form.addEventListener('submit', event => {
    event.preventDefault();
    event.stopPropagation();
    const inputText = textbox.value;
    activeConversation.sendText(inputText);
    textbox.value = '';
  }, false);
}

let messageId = 0;

function appendMessage(message, sender, appendAfter) {
  const messageDiv = document.createElement('div');
  messageDiv.classList = `message ${sender}`;
  messageDiv.innerHTML = '<p>' + message + '</p>';
  messageDiv.dataset.messageId = messageId++;

  const messageArea = document.getElementById('message-area');
  if (appendAfter == null) {
    messageArea.appendChild(messageDiv);
  } else {
    const inputMsg = document.querySelector(
      `.message[data-message-id='${appendAfter}']`
    );
    inputMsg.parentNode.insertBefore(messageDiv, inputMsg.nextElementSibling);
  }

  messageArea.scroll({ /* Scroll the message area to the bottom. */
    top: messageArea.scrollHeight,
    behavior: 'smooth'
  });

  return messageDiv.dataset.messageId; /* Return this message id so that a reply can be posted to it later */
}

Auf der Kundenseite

Die kundenorientierte Schnittstelle würde ein Chat-Fenster umfassen, das durch Anklicken der Chat-Schaltfläche ausgelöst wird. Dieses Fenster wird von rechts eingeblendet und ermöglicht es dem Kunden, mit dem Support-Mitarbeiter zu chatten.

Das Markup für dieses Chat-Fenster ist nicht allzu kompliziert. Es hat eine Kopfzeile, einen Hauptnachrichtenbereich und ein Formular zur Texteingabe am unteren Rand des Fensters.

<aside id="chatWindow">
  <div class="header">
    <h1>Live support</h1>
    <button class="btn-close" id="closeChat"><svg viewBox="0 0 47.971 47.971"><path fill="white" d="M28.228 23.986L47.092 5.122a2.998 2.998 0 000-4.242 2.998 2.998 0 00-4.242 0L23.986 19.744 5.121.88a2.998 2.998 0 00-4.242 0 2.998 2.998 0 000 4.242l18.865 18.864L.879 42.85a2.998 2.998 0 104.242 4.241l18.865-18.864L42.85 47.091c.586.586 1.354.879 2.121.879s1.535-.293 2.121-.879a2.998 2.998 0 000-4.242L28.228 23.986z"/></svg></button>
  </div>

  <div id="message-area" class="messages">
  </div>

  <div class="controls">
    <form id="textentry">
      <input id="textbox" type="text" />
      <input id="submit" type="submit" value="Send" />
    </form>
  </div>
</aside>

Wir werden nicht jede CSS-Zeile dafür behandeln, aber ich möchte hervorheben, wie man das Chat-Fenster von der Seite auf eine relativ leistungsfähigere Weise einschieben kann. Im Allgemeinen sind die Eigenschaften, die sicher zu animieren sind Transformationen und Deckkraft.

Im Idealfall sollte das Chat-Fenster an Ort und Stelle bleiben, während der Benutzer auf der Hauptseite scrollt, daher würden Sie ein position: fixed für das Chat-Fenster verwenden. Außerdem würden Sie das Chat-Fenster aus dem Rahmen heraus übersetzen und es hineinschieben, wenn der Auslöser angeklickt wird.

aside {
  position: fixed;
  top: 0;
  right: 0;
  transform: translateX(100%);
  display: flex;
  flex-direction: column;
  min-width: 20em;
  width: 25%;
  height: 100%;
  background: var(--background);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  transition: transform 0.5s ease;
}

aside.active {
  transform: translateX(0);
}

Die Verwendung von display: flex für das Chat-Fenster können wir sicherstellen, dass sich die Kopf- und die Fußzeile immer am oberen bzw. unteren Rand des Chat-Fensters befinden, während der Nachrichtenbereich wächst, um den verfügbaren Platz auszufüllen.

.messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow-y: scroll;
  padding: 1em 1.5em;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.24);
  max-height: calc(100vh - 6em);
}

In einem typischen Chat wird die Unterhaltung in abwechselnd links und rechts angeordneten Sprechblasen entsprechend den jeweiligen Teilnehmern des Chats angezeigt. Ein solches Layout ist mit Flexbox ebenfalls einfacher zu gestalten.

Wenden Sie wiederum ein display: flex auf den Nachrichtenbereich. So können Sie die Eigenschaften der Rahmenausrichtung auf die einzelnen Nachrichten anwenden. Dann müssen Sie nur noch ein align-self: flex-end auf die Nachrichten anzuwenden, die rechts vom Nachrichtenbereich erscheinen sollen.

.message.input {
  position: relative;
  align-self: flex-end;
}

Das clientseitige Javascript für das Chat-Fenster wird benötigt, um die entsprechende CSS-Klasse zum Ein- und Ausblenden des Chat-Fensters umzuschalten.

function triggerChat() {
  const button = document.getElementById('showChat');
  appendMessage('Hello! My name is James, how can I help you today?', 'agent');
  button.addEventListener('click', event => {
    const chatWindow = document.getElementById('chatWindow');
    chatWindow.classList.toggle('active');     
  }, false);
}

function closeChat() {
  const button = document.getElementById('closeChat');
  console.log(button);
  button.addEventListener('click', event => {
    const chatWindow = document.getElementById('chatWindow');
    chatWindow.classList.remove('active');
  }, false);
}

window.addEventListener('load', function() {
  triggerChat();
  closeChat();
  setupConversation('/api/new');
});

Chat exampleChat example

Auf der Agentenseite

Auf der Agentenseite können die Nachrichten den gesamten Viewport einnehmen und standardmäßig angezeigt werden. Die clientseitige Javascript-Datei für die Agentenseite beinhaltet also die Übergabe der richtigen Route für die Abrufanforderung an den Server.

window.addEventListener('load', function() {
  setupConversation('/api/jwt/agent');
});

Die Gestaltung der Nachrichten würde dem Kunden-Chat-Fenster recht ähnlich sein, mit dem Unterschied, dass die Gestaltung umgekehrt wäre, da das typische Design-Muster darin besteht, dass Ihre eigenen Nachrichten auf der rechten Seite erscheinen, während die empfangenen Nachrichten auf der linken Seite erscheinen.

Chat portal finalChat portal final

Wie geht es weiter?

In diesem Tutorial wurden keine Frontend-Frameworks oder Modullader verwendet, da die Anwendung vereinfacht werden sollte, um den Schwerpunkt auf die Conversation API, ihre Funktionen und ihre Funktionsweise zu legen. Wenn Sie mehr mit der Conversation API machen wollen, finden Sie hier einige Links, die für Sie hilfreich sein könnten:

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/46621147f0/huijing.png
Hui Jing ChenVonage Ehemalige

Hui Jing ist Developer Advocate bei Nexmo. Sie hat eine übermäßige Liebe zu CSS und Typografie und ist allgemein leidenschaftlich über alle Dinge im Web.