https://d226lax1qjow5r.cloudfront.net/blog/blogposts/adding-voice-functionality-to-an-existing-chat-application-dr/Elevate_Enable-Audio-1.png

Hinzufügen von Voice-Funktionalität zu einer bestehenden Chat-Applikation

Zuletzt aktualisiert am May 13, 2021

Lesedauer: 14 Minuten

Haben Sie schon einmal eine lange Nachricht an jemanden in einem Chat geschrieben und gedacht: "Wäre es nicht viel einfacher, wenn ich mit demjenigen sprechen könnte? Natürlich, das haben Sie! Wenn Sie das Nexmo Client SDK in Ihrer Chat-Anwendung verwenden, können Sie genau das tun.

Die Demo-Anwendung und ein fertiges Beispiel können jetzt auf GitHub gefunden werden.

Voraussetzungen

Knotenpunkt & NPM

Um loszulegen, müssen Sie Node und NPM installiert haben. Diese Anleitung verwendet Node 8 und NPM 6. Stellen Sie sicher, dass sie installiert und aktuell sind.

node --version npm --version

Sowohl Node als auch NPM müssen installiert sein und die richtige Version haben. Gehen Sie zu nodejs.org und installieren Sie die richtige Version, falls Sie sie noch nicht haben.

Nexmo CLI

Um Ihre Anwendung einzurichten, müssen Sie die Nexmo CLI installieren. Installieren Sie es mit NPM im Terminal.

npm install -g nexmo-cli@beta

Richten Sie die Nexmo CLI mit dem API-Schlüssel und dem Geheimnis ein, die Sie auf dem Dashboard finden.

nexmo setup

Git (optional)

Sie können git verwenden, um unsere Demo-Anwendung von GitHub zu klonen.

Für diejenigen, die sich mit Git-Befehlen nicht auskennen, habe ich eine Anleitung für Sie. Diese Anleitung enthält Anweisungen zum Herunterladen des Projekts als ZIP-Datei.

Folgen Sie dieser Anleitung zur Installation von Git

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.

Die Demo-Anwendung

Die Anwendung ist in erster Linie ein einfacher Chat-Client. Sie ermöglicht es zwei Benutzern (oder mehr, wenn Sie sie entsprechend konfigurieren), sich anzumelden und mit dem Chatten zu beginnen.

Grundlegende Installation

Damit diese Anleitung einfach zu befolgen ist, klonen Sie die Demo-Anwendung direkt von GitHub.

git clone https://github.com/nexmo-community/enable-audio-in-chat.git

Für diejenigen, die mit Git-Befehlen nicht vertraut sind, können Sie die Demo-Anwendung als Zip-Datei herunterladen herunterladen und lokal entpacken.

Wechseln Sie nach dem Klonen oder Entpacken in das neue Verzeichnis der Demoanwendung.

cd enable-audio-in-chat

Installieren Sie die npm-Abhängigkeiten.

npm install

Starten Sie nun die Anwendung.

npm start

Jetzt können Sie es in Ihrem Lieblingsbrowser ausprobieren und die Anwendung anzeigen, die unter der Standardadresse ausgeführt werden sollte: http://127.0.0.1:8080.

Login boxLogin box

Unkonfiguriert sehen Sie ein Anmeldefeld. Sie können sich nicht anmelden, weil Sie noch nicht wissen, wer sich anmelden kann!

Denken Sie daran, Sie simulieren hier nur die Authentifizierung, und Sie sollten darauf vorbereitet sein, etwas Echtes und Sicheres für Produktionsanwendungen einzurichten.

User not found - Login BoxUser not found - Login Box

Beenden Sie die Anwendung: Im Terminal oder in der Bash können Sie den laufenden Prozess mit der Tastenkombination STRG+C anhalten. Wenn Sie Änderungen vornehmen, sollten Sie die Anwendung weder starten noch anhalten müssen, da bei jedem Neuladen einer Seite alles neu vom Server angefordert wird.

Super einfache Einrichtung

In der Demo (die Sie gerade ausführen) gibt es ein Skript, das die nächsten Schritte erheblich erleichtert.

So funktioniert das Skript: Es bittet Sie um einige Eingaben und erstellt dann die Anwendung, die Konversation und die Benutzer, die für diese Anleitung benötigt werden, indem es alle Setup-Befehle ausführt, die Sie sonst manuell ausführen müssten. Anschließend generiert es die Konfiguration für die Demo-Anwendung. Sie können sich den Code hier ansehen, um sicherzugehen, dass er nichts Unanständiges tut.

WoahSie wollen mein Skript nicht ausführen? Sie möchten die Konfigurationsdatei lieber manuell erstellen? Ich gebe dir Rückendeckung - Schritte zur manuellen Erstellung der Konfigurationsdatei.

Führen Sie das Setup-Skript aus

Um die Anwendung für die nächsten Schritte zu konfigurieren, führen Sie also das Setup-Skript aus.

npm run setup-script

Das Drehbuch stellt einige Fragen.

Script QuestionsScript Questions

Am Ende wird die Datei config.js um mehr wie folgt auszusehen.

const USERS = {
  luke: 'eyJhbGciOiJIkpXVCJ9.eyJpYXQiOnt9fX19.EDHi1R61yh01oeZ9DYQ',
  alex: 'eyJhbGciOi234JXVCJ9.eyJpyXQiOjt9fX19.VqLdU97Fdb2ZiOfqmoQ',
}

const CONVERSATION_ID = 'CON-da9c1a6b-c2dc-4bdd-ac03-cc041ef03502'

Was hat das Drehbuch bewirkt?!?

Hinter den Kulissen führt das Skript also die folgenden Schritte aus.

  • Erstellen Sie eine Nexmo-Anwendung mit dem nexmo app:create an und notieren Sie sich die ID.

  • Erstellen Sie eine Nexmo-Konversation mit dem nexmo conversation:create Befehl und notieren Sie sich die ID.

  • Erstellen Sie beide Benutzer mit dem nexmo user:create an und notieren Sie sich die IDs.

  • Fügen Sie beide Benutzer zur Nexmo-Konversation mit nexmo member:add.

  • Generieren Sie JWTs für beide Benutzer, um auf die Anwendung zuzugreifen, und notieren Sie sich die JWTs.

  • Schreibt die Konfiguration in config.js unter Verwendung der IDs und JWTs, die es gespeichert hat.

Chitty Chitty Chat Chat

Jetzt haben Sie unsere Basis-Demoanwendung konfiguriert und können sich ein wenig unterhalten! Hier teste ich sie mit einem meiner Kollegen.

Chat textChat text

Audio einschalten

Jetzt sind Sie startklar. Sie haben eine Demo-Anwendung, mit der Sie mit anderen Personen chatten können. Als Nächstes fügen Sie eine Schaltfläche hinzu, um Audio zu aktivieren, damit Sie auch miteinander sprechen können.

Das HTML

Der folgende Code befindet sich in der Datei index.html Datei.

<section id="messages">
    <!-- /audio-toggle -->
    <h1>Messages</h1>
    <div id="messageFeed"></div>
    
    <textarea id="messageTextarea"></textarea>
    <br>
    <button id="send">Send</button>
  </section>

Ersetzen Sie die Zeile <!-- /audio-toggle --> durch das folgende HTML.

<div>
      <audio id="audio">
        
      </audio>
      <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-secondary">
           <span id="audioToggleText">Enable Audio</span>
        </label>
      </div>
    </div>

Lassen Sie mich nun den obigen Code erklären.

<audio> Tags wurden der HTML-Spezifikation hinzugefügt, um die Einbettung von Audiodateien (oder Streams) in Webseiten zu ermöglichen. Der Tag wird verwendet, um eine Quelle (Pfad/Url) und eine Version für das Audio anzugeben, so dass mehrere Versionen des Audios für verschiedene Kontexte (oder Browser, Betriebssysteme usw.) eingebettet/kodiert werden können.

Außerdem fügen Sie eine Schaltfläche hinzu, die unser Umschalter sein wird.

Die Datei index.html Datei sollte nun einen Abschnitt enthalten, der in etwa so aussieht.

<section id="messages">
    <div>
      <audio id="audio">
        
      </audio>
      <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-secondary">
           <span id="audioToggleText">Enable Audio</span>
        </label>
      </div>
    </div>
    <h1>Messages</h1>
    <div id="messageFeed"></div>
    
    <textarea id="messageTextarea"></textarea>
    <br>
    <button id="send">Send</button>
  </section>

Das sind alle HTML-Änderungen dieses Mal. Und was kommt als Nächstes?

Das JavaScript

Als Nächstes werden Sie das JavaScript für unsere Demo-Anwendung bearbeiten.

Der folgende Code befindet sich in der Datei chat.js Datei.

constructor() {
    this.messageTextarea = document.getElementById('messageTextarea')
    this.sendButton = document.getElementById('send')
    this.loginForm = document.getElementById('login')
    this.loginButton = document.getElementById('loginButton')
    this.messages = document.getElementById('messages')
    this.messageFeed = document.getElementById('messageFeed')
    // audio-elements
    this.setupUserEvents()
  }

Ersetzen Sie die Zeile // audio-elements durch den folgenden JavaScript-Code.

this.audio = document.getElementById('audio')
    this.audioToggle = document.getElementById('audioToggle')
    this.audioToggleText = document.getElementById('audioToggleText')

Dieser Code "registriert" 3 neue Elemente, so dass Sie die On-Page-Elemente in der gesamten JavaScript-Datei leichter verwenden können.

Nun finden Sie diesen Code in der gleichen chat.js Datei.

// audio-toggle-event

    this.showConversationHistory(conversation)

Ersetzen Sie die Zeile // audio-toggle-event durch den folgenden JavaScript-Code.

conversation.on("member:media", (member, event) => {
      console.log(`*** Member changed media state`, member, event)
      const text = `${member.user.name} <b>${event.body.audio ? 'enabled' : 'disabled'} audio in the conversation</b><br>`
      this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
    })

Dieser Code 'registriert' auch einen Listener für das member:media Ereignis. Wenn er das Ereignis sieht, sendet er dann eine console.log an den Browser; z. B. Mitglieds- und Ereignisdaten. Besonders nützlich für die Fehlersuche. Es wird auch ein formatierter Text erstellt und die messageFeed indem der Text dem bestehenden Feed vorangestellt wird.

Dann finden Sie diesen Code in der Datei chat.js Datei.

})

    // audio-click-event
  }

Ersetzen Sie die Zeile // audio-click-event durch den folgenden JavaScript-Code.

    this.audioToggle.addEventListener('click', () => {
      const buttonContainer = this.audioToggle.parentNode
      if (this.audioToggle.checked) {
        this.audioToggleText.innerHTML = 'Disable Audio'
        buttonContainer.classList.add('btn-danger')
        buttonContainer.classList.add('active')
        buttonContainer.classList.remove('btn-secondary')
        this.conversation.media.enable().then(stream => {
          // Older browsers may not have srcObject
          if ("srcObject" in this.audio) {
            this.audio.srcObject = stream
          } else {
            // Avoid using this in new browsers, as it is going away.
            this.audio.src = window.URL.createObjectURL(stream)
          }

          this.audio.onloadedmetadata = () => {
            this.audio.play()
          }

          this.eventLogger('member:media')()
        }).catch(this.errorLogger)
      } else {
        this.audioToggleText.innerHTML = 'Enable Audio'
        buttonContainer.classList.remove('btn-danger')
        buttonContainer.classList.remove('active')
        buttonContainer.classList.add('btn-secondary')
        this.conversation.media.disable().then(this.eventLogger('member:media')).catch(this.errorLogger)
      }
    })

Dieser Code ist eine große Sache. Und auch hier wird ein Listener registriert. Diesmal hört er darauf, wenn der Benutzer auf unsere audioToggle Schaltfläche klickt, die Sie gerade hinzugefügt haben.

Wenn ein Benutzer auf die Schaltfläche klickt und sie bereits eingeschaltet war, wird sie ausgeschaltet. Wenn sie ausgeschaltet war, schaltet sie sich ein.

Wenn sie eingeschaltet ist, wird Audio aktiviert, indem die URL des Audiostreams zum Tag hinzugefügt und das Design der Schaltfläche aktualisiert wird. Wenn sie ausgeschaltet ist, wird der Ton deaktiviert, indem die URL des Audiostreams aus dem Tag entfernt und das Design der Schaltfläche aktualisiert wird.

Die gesamte chat.js Datei sollte nun in etwa so (lang) wie diese aussehen.

class ChatApp {
  constructor() {
    this.messageTextarea = document.getElementById('messageTextarea')
    this.sendButton = document.getElementById('send')
    this.loginForm = document.getElementById('login')
    this.loginButton = document.getElementById('loginButton')
    this.messages = document.getElementById('messages')
    this.messageFeed = document.getElementById('messageFeed')
    this.audio = document.getElementById('audio')
    this.audioToggle = document.getElementById('audioToggle')
    this.audioToggleText = document.getElementById('audioToggleText')
    this.setupUserEvents()
  }

  joinConversation(userToken) {
    new NexmoClient({ debug: false })
      .createSession(userToken)
      .then(app => {
        console.log('*** Logged into app', app)
        return app.getConversation(CONVERSATION_ID)
      })
      .then((conversation) => {
        console.log('*** Joined conversation', conversation)
        this.setupConversationEvents(conversation)
      })
      .catch(this.errorLogger)
  }

  showConversationHistory(conversation) {
    conversation
      .getEvents({ page_size: 20 })
      .then((events_page) => {
        var eventsHistory = ""

        events_page.items.forEach((value, key) => {
          if (conversation.members.get(value.from)) {
            const date = new Date(Date.parse(value.timestamp))
            switch (value.type) {
              case 'text':
                eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date.toLocaleString('en-GB')}: <b>${value.body.text}</b><br>` + eventsHistory
                break;
              case 'member:joined':
                eventsHistory = `${conversation.members.get(value.from).user.name} @ ${date.toLocaleString('en-GB')}: <b>joined the conversation</b><br>` + eventsHistory
                break;
            }
          }
        })

        this.messageFeed.innerHTML = eventsHistory + this.messageFeed.innerHTML
      })
      .catch(this.errorLogger)
  }

  setupConversationEvents(conversation) {
    this.conversation = conversation
    this.messages.style.display = "block"

    // Bind to events on the conversation
    conversation.on('text', (sender, message) => {
      const date = new Date(Date.parse(message.timestamp))
      console.log('*** Message received', sender, message)
      const text = `${sender.user.name} @ ${date.toLocaleString('en-GB')}: <b>${message.body.text}</b><br>`
      this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
    })

    conversation.on("member:joined", (member, event) => {
      const date = new Date(Date.parse(event.timestamp))
      console.log(`*** ${member.user.name} joined the conversation`)
      const text = `${member.user.name} @ ${date.toLocaleString('en-GB')}: <b>joined the conversation</b><br>`
      this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
    })

    conversation.on("member:media", (member, event) => {
      console.log(`*** Member changed media state`, member, event)
      const text = `${member.user.name} <b>${event.body.audio ? 'enabled' : 'disabled'} audio in the conversation</b><br>`
      this.messageFeed.innerHTML = text + this.messageFeed.innerHTML
    })

    this.showConversationHistory(conversation)
  }

  errorLogger(error) {
    console.log(error)
  }

  eventLogger(event) {
    return () => {
      console.log("'%s' event was sent", event)
    }
  }

  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()
      const userName = this.loginForm.children.username.value
      const userToken = this.authenticate(userName)
      this.loginForm.children.username.value = ''
      if (userToken) {
        this.joinConversation(userToken)
        this.loginForm.style.display = 'none'
      } else {
        alert('user not found')
      }
    })

    this.audioToggle.addEventListener('click', () => {
      const buttonContainer = this.audioToggle.parentNode
      if (this.audioToggle.checked) {
        this.audioToggleText.innerHTML = 'Disable Audio'
        buttonContainer.classList.add('btn-danger')
        buttonContainer.classList.add('active')
        buttonContainer.classList.remove('btn-secondary')
        this.conversation.media.enable().then(stream => {
          // Older browsers may not have srcObject
          if ("srcObject" in this.audio) {
            this.audio.srcObject = stream
          } else {
            // Avoid using this in new browsers, as it is going away.
            this.audio.src = window.URL.createObjectURL(stream)
          }

          this.audio.onloadedmetadata = () => {
            this.audio.play()
          }

          this.eventLogger('member:media')()
        }).catch(this.errorLogger)
      } else {
        this.audioToggleText.innerHTML = 'Enable Audio'
        buttonContainer.classList.remove('btn-danger')
        buttonContainer.classList.remove('active')
        buttonContainer.classList.add('btn-secondary')
        this.conversation.media.disable().then(this.eventLogger('member:media')).catch(this.errorLogger)
      }
    })
  }

  authenticate(username) {
    return USERS[username] || null
  }
}
new ChatApp()

Vorausgesetzt, Sie haben alles richtig gemacht, führen Sie npm start erneut aus und öffnen Sie die Anwendung unter http://127.0.0.1:8080. Wenn die Anwendung bereits ausgeführt wurde, sollten Sie die Seite einfach aktualisieren können, um die neueste Version zu erhalten.

Login boxLogin box

Wenn Sie glauben, dass das JavaScript nicht funktioniert und ein Neustart von NPM nicht hilft, aktualisieren Sie die Webseite mit STRG+F5, wodurch alle JS- und CSS-Funktionen der Seite neu angefordert werden.

Melden Sie sich mit den von Ihnen eingerichteten Testanmeldedaten an.

Login using credentialsLogin using credentials

Jetzt sind Sie eingeloggt und sehen wie zuvor den Nachrichten-Feed und die Schaltfläche zum Aktivieren von Audio. Klicken Sie nun auf Audio aktivieren.

Erlauben Sie der Anwendung, Ihr Mikrofon zu verwenden. Dies gilt für Chrome für MacOS, andere Browser und Betriebssysteme können abweichen.

Allow microphoneAllow microphone

Wenn beide Benutzer angemeldet sind und Audio aktiviert haben, können Sie ein Gespräch zwischen den beiden Benutzern führen.

Two user convoTwo user convo

Wenn Sie wie ich in einem Café sitzen und das Programm in zwei Browserfenstern testen, müssen Sie damit rechnen, dass Ihre Kollegen die Stirn runzeln, wenn Sie eine laute Feedbackschleife erzeugen.

Jetzt können Sie auf Audio deaktivieren klicken, um das Mikrofon wieder auszuschalten. Andere Benutzer werden darauf aufmerksam gemacht, dass Sie den Ton deaktiviert haben.

Disable AudioDisable Audio

Die Ergebnisse

Es wird immer die Notwendigkeit bestehen, Audio-Kommunikation zwischen Web-Benutzern zu ermöglichen, und unser Client SDK ist die perfekte Lösung dafür.

Probieren Sie es aus und lassen Sie mich wissen, was Sie denken in unserer Gemeinschaft Slack oder im Kommentarbereich unten.

Teilen Sie:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke OliffVonage Ehemalige

Freundlicher Tech-Pädagoge, Familienvater, Verfechter der Vielfalt, streitet wahrscheinlich ein bisschen zu viel. Ehemals Backend-Ingenieur. Sprich mit mir über JavaScript (Frontend oder Backend), das erstaunliche Vue.js, DevOps, DevSecOps, alles was mit JamStack zu tun hat. Autorin auf DEV.to