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

Ajouter une fonction Voice à une application de chat existante

Temps de lecture : 14 minutes

Vous est-il déjà arrivé, alors que vous étiez à mi-chemin de la rédaction d'un long message destiné à quelqu'un dans un chat, de vous demander si ce ne serait pas tellement plus facile si je pouvais lui parler ? Bien sûr, vous l'avez fait ! En utilisant le Client SDK de Nexmo dans votre application de chat, vous serez en mesure de le faire.

L'application application de démonstration et un exemple terminé sont disponibles sur GitHub.

Conditions préalables

Node et NPM

Pour commencer, vous aurez besoin d'installer Node et NPM. Ce guide utilise Node 8 et NPM 6. Vérifiez qu'ils sont installés et à jour.

node --version npm --version

Node et NPM doivent être installés et à la bonne version. Allez sur nodejs.org et installez la bonne version si vous ne l'avez pas.

CLI Nexmo

Pour mettre en place votre application, vous aurez besoin d'installer le Nexmo CLI. Installez-le en utilisant NPM dans le terminal.

npm install -g nexmo-cli@beta

Configurez le CLI Nexmo avec la clé API et le secret qui se trouvent sur le tableau de bord.

nexmo setup

Git (optionnel)

Vous pouvez utiliser git pour cloner notre application de démonstration depuis GitHub.

Pour ceux qui ne sont pas à l'aise avec les commandes git, ne vous inquiétez pas, je vous couvre. Ce guide contient des instructions pour télécharger le projet sous forme de fichier ZIP.

Suivez ce pour installer git

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.

L'application de démonstration

L'application est avant tout un simple client de chat. Elle permet à deux utilisateurs (ou plus, si vous la configurez à cet effet) de se connecter et de commencer à discuter.

Installation de base

Pour que ce guide soit facile à suivre, clonez l'application de démonstration directement depuis GitHub.

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

Pour ceux qui ne sont pas à l'aise avec les commandes git, vous pouvez télécharger l'application de démonstration sous forme de fichier zip et le décompresser localement.

Une fois cloné ou décompressé, passez dans le nouveau répertoire de l'application de démonstration.

cd enable-audio-in-chat

Installer les dépendances npm.

npm install

Lancez maintenant l'application.

npm start

Vous pouvez maintenant l'essayer dans votre navigateur préféré et visualiser l'application, qui doit être exécutée à l'adresse par défaut : http://127.0.0.1:8080.

Login boxLogin box

Non configuré, vous verrez une boîte de connexion. Vous ne pouvez pas vous connecter parce que vous ne savez pas encore qui peut le faire !

Gardez à l'esprit que vous ne faites que simuler l'authentification ici, et vous devriez être prêt à mettre en place quelque chose de réel et de sécurisé pour les applications de production.

User not found - Login BoxUser not found - Login Box

Arrêter l'application : Dans le terminal ou bash, vous pouvez utiliser CTRL+C pour arrêter le processus en cours. Au fur et à mesure que vous apportez des modifications, vous ne devriez pas avoir besoin de démarrer ou d'arrêter l'application, car tout est redemandé au serveur à chaque fois que vous rechargez une page.

Une installation très simple

Dans la démo (celle que vous exécutez actuellement), il y a un script qui facilite les étapes suivantes.

Comment fonctionne le script : Il vous demande quelques informations, puis crée l'application, la conversation et les utilisateurs requis pour ce guide en exécutant toutes les commandes d'installation que vous auriez dû faire manuellement. Il génère ensuite la configuration de l'application de démonstration. Vous pouvez vérifier le code ici pour vous assurer qu'il ne fait rien de méchant.

Woahvous ne voulez pas exécuter mon script ? Vous préférez générer le fichier de configuration manuellement ? Je suis là pour vous aider - étapes pour créer le fichier de configuration manuellement.

Exécuter le script d'installation

Ainsi, pour configurer l'application pour les étapes à venir, exécutez le script d'installation.

npm run setup-script

Le texte pose des questions.

Script QuestionsScript Questions

À la fin de l'opération, il met à jour le config.js pour ressembler à ceci.

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

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

Qu'a fait ce texte ?!?

Ainsi, dans les coulisses, le script exécute les étapes suivantes.

  • Créez une application Nexmo à l'aide de la commande nexmo app:create et notez l'identifiant.

  • Créez une conversation Nexmo à l'aide de la commande nexmo conversation:create et notez l'identifiant.

  • Créez les deux utilisateurs à l'aide de la commande nexmo user:create et notez les identifiants.

  • Ajoutez les deux utilisateurs à la conversation Nexmo avec nexmo member:add.

  • Générer des JWT pour les deux utilisateurs afin qu'ils accèdent à l'application et prendre note des JWT.

  • Écrit la configuration dans config.js en utilisant les ID et les JWT qu'il a conservés.

Chitty Chitty Chat Chat

Maintenant que vous avez configuré notre application de démonstration de base, vous pouvez discuter un peu ! Me voici en train de la tester avec l'un de mes collègues.

Chat textChat text

Activer l'audio

Vous êtes maintenant opérationnel. Vous disposez d'une application de démonstration que vous pouvez utiliser pour discuter avec d'autres personnes. Ensuite, ajoutez un bouton pour activer l'audio afin que vous puissiez également parler entre vous.

Le HTML

Trouvez le code suivant dans le fichier index.html le code suivant.

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

Remplacez la ligne <!-- /audio-toggle --> par le code HTML suivant.

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

Je vais maintenant expliquer le code ci-dessus.

<audio> ont été ajoutées à la spécification HTML pour permettre l'intégration de fichiers audio (ou de flux) dans les pages web. La balise est utilisée pour indiquer une source (chemin/url) et une version de l'audio, ce qui permet d'incorporer/encoder plusieurs versions de l'audio pour différents contextes (ou navigateurs, systèmes d'exploitation, etc.).

De plus, vous ajoutez un bouton qui sera notre bascule.

Le fichier index.html devrait maintenant contenir une section qui ressemble à ceci.

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

Il s'agit là de tous les changements apportés au HTML cette fois-ci. Qu'est-ce qui va suivre ?

Le JavaScript

Ensuite, vous allez éditer le JavaScript pour notre application de démonstration.

Trouvez le code suivant dans le fichier chat.js le code suivant.

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

Remplacez la ligne // audio-elements par le code JavaScript suivant.

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

Ce code "enregistre" 3 nouveaux éléments afin que vous puissiez utiliser plus facilement les éléments de la page dans le fichier JavaScript.

Maintenant, trouvez ce code dans le même fichier chat.js fichier.

// audio-toggle-event

    this.showConversationHistory(conversation)

Remplacez la ligne // audio-toggle-event par le code JavaScript suivant.

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

Ce code "enregistre" également un récepteur pour l'événement member:media événement. Lorsqu'il voit cet événement, il envoie un console.log au navigateur, comme les données relatives au membre et à l'événement. C'est particulièrement utile pour le débogage. Il va également créer du texte formaté et mettre à jour le fichier messageFeed en ajoutant le texte au flux existant.

Ensuite, trouvez ce code à l'intérieur du fichier chat.js à l'intérieur du fichier

})

    // audio-click-event
  }

Remplacez la ligne // audio-click-event par le code JavaScript suivant.

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

Ce code est très important. Il enregistre lui aussi un écouteur. Cette fois, il écoute lorsque l'utilisateur clique sur le bouton audioToggle que vous venez d'ajouter.

Si un utilisateur clique sur le bouton et que celui-ci était déjà activé, il est désactivé. S'il était désactivé, il est activé.

Lorsqu'il est activé, il active l'audio en ajoutant l'URL du flux audio à la balise et met à jour le style du bouton. Par conséquent, lorsqu'il est désactivé, il désactive l'audio en supprimant l'URL du flux audio de la balise et met à jour le style du bouton.

L'ensemble du fichier chat.js devrait maintenant ressembler à quelque chose (de long) comme ceci.

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

En supposant que vous avez tout fait correctement, exécutez à nouveau le programme npm start et ouvrez l'application à l'adresse suivante http://127.0.0.1:8080. Si l'application était déjà en cours d'exécution, il vous suffira de rafraîchir la page pour obtenir la dernière version.

Login boxLogin box

Si vous pensez que le JavaScript ne fonctionne pas et que le redémarrage de NPM ne vous aide pas, utilisez CTRL+F5 pour rafraîchir la page web, ce qui demande à tous les JS et CSS de la page d'être rafraîchis.

Connectez-vous à l'aide des identifiants de test que vous avez définis.

Login using credentialsLogin using credentials

Maintenant que vous êtes connecté, vous pouvez voir le flux de messages comme avant, et le bouton pour activer l'audio. Allez-y et cliquez sur Activer l'audio.

Autorisez l'application à utiliser votre microphone. Ceci est dans Chrome pour MacOS, d'autres navigateurs et systèmes d'exploitation peuvent varier.

Allow microphoneAllow microphone

Lorsque les deux utilisateurs sont connectés et que l'audio est activé, vous pouvez tenir une conversation entre les deux utilisateurs.

Two user convoTwo user convo

À ce stade, si vous êtes dans un café et que vous le testez dans deux fenêtres de navigateur comme je l'ai fait, préparez-vous à ce que les autres buveurs de café froncent les sourcils lorsque vous générez une boucle de rétroaction bruyante.

Vous pouvez maintenant cliquer sur Désactiver l'audio pour réactiver le microphone. Les autres utilisateurs sont avertis que vous avez désactivé l'audio.

Disable AudioDisable Audio

Les résultats

Il y aura toujours un besoin de permettre la communication audio entre les utilisateurs du web et notre Client SDK est une solution parfaite pour cela.

Essayez-le et dites-moi ce que vous en pensez dans notre Communauté Slack ou dans les commentaires ci-dessous.

Partager:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke OliffAnciens de Vonage

Éducateur technique sympathique, père de famille, défenseur de la diversité, il discute probablement un peu trop. Anciennement ingénieur backend. Parlez-moi de JavaScript (frontend ou backend), de l'incroyable Vue.js, de DevOps, de DevSecOps, de tout ce qui concerne JamStack. Rédacteur sur DEV.to