
Partager:
Hui Jing est développeuse chez Nexmo. Elle a un amour immodéré pour le CSS et la typographie, et est généralement passionnée par tout ce qui touche au web.
Comment créer un chat en direct sur la page
Temps de lecture : 20 minutes
L'API de conversation de Vonage Conversation API de Vonage de Vonage permet aux développeurs de créer des fonctions de conversation où la communication peut avoir lieu sur plusieurs supports. Un aspect essentiel est que le contexte des conversations peut être maintenu sur tous les supports, ce qui ouvre une myriade de possibilités.
Ce tutoriel explique les bases du fonctionnement de l'API Conversation en l'utilisant pour créer un chat en direct rudimentaire sur la page comme exemple de cas d'utilisation. Cette boîte de dialogue permettra aux clients d'envoyer un message à un agent d'assistance en temps réel et l'agent d'assistance pourra répondre au client.
En outre, nous couvrirons également la partie stylistique et la mise en page, y compris la construction d'une fenêtre de chat coulissante et la disposition des messages de chat de part et d'autre de l'interface de chat. Le code complet et la démo sont disponible sur Glitchalors n'hésitez pas à le remixer comme bon vous semble.
Conditions préalables
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.
Vous devez disposer des conditions préalables suivantes avant de commencer ce tutoriel :
Avoir Node.js installé sur votre machine
Installez la version bêta du CLI de Vonage
npm install @vonage/cli -gConfigurez le CLI pour utiliser votre clé et votre secret d'API de Vonage, qui sont disponibles sur la page de configuration du tableau de bord de Vonage.
vonage config:set --apiKey=VONAGE_API_KEY --apiSecret=VONAGE_API_SECRET
Pour utiliser la console sur Glitch comme vous le feriez sur une machine locale, cliquez sur Outilspuis sur Journaux et enfin, Console.
Glitch console
Sur Glitchn'utilisez pas le drapeau -g lors de l'installation de l CLI de Vonage
Le CLI de Vonage comporte des modules d'extension qui, lorsqu'ils sont installés, offrent des capacités supplémentaires. Dans ce tutoriel, nous travaillerons avec Conversations, voici donc la commande pour installer son plugin :
Scénario
Ce que nous allons construire est similaire aux interfaces des sites web qui offrent une option de chat en direct, généralement déclenchée en cliquant sur un bouton qui fait apparaître une fenêtre de chat.
La fenêtre de chat vous mettra en relation avec un agent d'assistance de l'autre côté du portail de service à la clientèle, et vous pourrez tous deux discuter en temps réel.
Configuration initiale
Un moyen facile de démarrer le projet est d'utiliser Glitch, parce qu'il démarre avec une application Node construite sur Express. Vous êtes libre d'utiliser n'importe quel autre framework Node, ou même d'écrire le vôtre, mais pour ce tutoriel, nous utiliserons Express.
Tout d'abord, créez une nouvelle application Vonage avec la commande vonage apps:create commande.
Voyons ce que font les drapeaux et les paramètres supplémentaires. vonage apps:create "NAME_OF_APPLICATION" crée une application Vonage avec le nom que vous voulez donner à votre application, et est nécessaire pour que la commande fonctionne.
L'option --rtc_event_url spécifie l'URL de l'événement, qui est le webhook où Vonage envoie tous les événements qui se produisent sur l'application.
L'exécution de la commande devrait vous donner une sortie qui ressemble à ceci (les chemins ressembleront à ceci si vous utilisez Glitch) :
La longue chaîne générée est l'identifiant de l'Applications, dont vous devez prendre note. Nous l'appellerons YOUR_APP_ID tout au long du tutoriel. La clé privée est utilisée pour générer des JWT qui servent à authentifier vos interactions avec Vonage.
Si vous exécutez ls -alvous devriez voir le fichier support_agent.key dans votre dossier personnel. Déplacez le fichier dans un dossier .data/ car Glitch utilise ce dossier pour les données sensibles.
La commande suivante permet de créer un utilisateur. Cet utilisateur est l'agent d'assistance qui sera toujours ajouté au chat. Dans un scénario réel, il y aura toujours une poignée d'agents d'assistance qui parleront à plusieurs utilisateurs ayant besoin d'assistance.
Cela générera un utilisateur et vous donnera quelque chose comme ceci dans la console :
Notez bien cet identifiant car vous en aurez besoin lorsque vous voudrez ajouter l'agent d'assistance à la conversation.
Vous pouvez également stocker vos informations d'identification dans le fichier .env à la racine du projet.
Mise en place de votre application Node
Si vous aviez commencé avec le modèle de base hello-express Glitch de base, votre fichier server.js serait plutôt dépouillé, avec seulement Express installé.
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);
});Mais c'est un bon point de départ. Tout d'abord, installez la version beta de la bibliothèque nexmo-node, body-parser, et un générateur de nom d'utilisateur aléatoire :
Ajoutez ces dépendances au fichier server.js au fichier server.js :
const bodyParser = require('body-parser');
const rug = require('username-generator');
const Nexmo = require('nexmo');Ensuite, vous voulez instancier une instance Nexmo comme suit :
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
});Réglons les parties non liées à Vonage du fichier server.js à savoir les routes pour nos pages web respectives, et la mise en place de l'intergiciel body-parser pour analyser les requêtes entrantes.
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');
});Comme vous pouvez le voir sur les itinéraires, il y a deux pages web distinctes. En théorie, il s'agirait de deux applications distinctes, mais pour les besoins de ce tutoriel, nous les avons combinées en un seul projet.
Définissons une liste de contrôle d'accès (ACL) dans le fichier server.js dans le fichier server.js. Il s'agit d'une liste de chemins qui sont des chemins d'accès à l'API de Vonage et qui sont utilisés pour générer le JWT avec les permissions appropriées.
const ACL = {
paths: {
'/*/users/**': {},
'/*/conversations/**': {},
'/*/sessions/**': {},
'/*/devices/**': {},
'/*/image/**': {},
'/*/media/**': {},
'/*/push/**': {},
'/*/knocking/**': {},
'/*/legs/**': {}
}
};Nous devons définir quelques variables pour mettre en œuvre une certaine forme de persistance en mémoire pour cette application de tutoriel. Voici un aperçu du processus : si la conversation existe déjà, renvoyer les détails de la conversation à l'interface utilisateur. Dans le cas contraire, les étapes suivantes seront exécutées :
Créer un nouvel utilisateur aléatoire (qui agit comme un client demandant de l'aide)
Si la création de l'utilisateur est réussie, créez une nouvelle conversation.
Si la création de la conversation est réussie, ajouter l'utilisateur nouvellement créé à la conversation.
Si l'utilisateur est ajouté avec succès, ajoutez l'agent d'assistance à la conversation.
Si l'agent d'assistance est ajouté avec succès, renvoyer les détails de la conversation active à l'interface utilisateur.
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);
}
}
);
}
}
);
}
}
);
}
}
);
}
});
Pour l'agent d'assistance, le chemin est beaucoup plus court.
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
});
});
Enfin, nous devons configurer l'URL du webhook, qui reçoit tous les événements qui se produisent sur l'application, et qui peut être utilisé pour le débogage ou le développement de fonctionnalités supplémentaires.
app.route('/webhooks/event').post((req, res) => {
console.log(req.body);
});
Utilisation du Client SDK de Vonage pour Javascript
Glitch commence par un simple index.html dans le répertoire vues/ dans le dossier views/. Ajoutez un autre fichier HTML au dossier vues/ appelé agent.html.
Add another html file
Nous aurons également des fichiers CSS et Javascript distincts pour chaque page. Pour éviter une complexité supplémentaire due aux chargeurs de modules, ce tutoriel a placé toutes les fonctions partagées dans un fichier common.js common.js.
Votre dossier public ressemblerait à ceci :
Le gros du travail est effectué avec le Vonage Client SDK pour Javascript. Vous pouvez installer la bibliothèque du client via NPM ou utiliser une version hébergée par le CDN. Incluez le script dans le fichier index.html et agent.html
<script src="https://cdn.jsdelivr.net/npm/nexmo-client@6.0.1/dist/nexmoClient.js"></script>Les fonctionnalités du côté du client et de l'agent sont assez similaires et quelques fonctions peuvent être abstraites dans un fichier Javascript commun. Nous avons besoin d'une fonction pour récupérer les détails de la conversation sur le serveur afin de les utiliser pour l'interface utilisateur.
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);
});
}
Nous voulons également disposer de quelques récepteurs qui rempliront le texte saisi par l'une ou l'autre des parties dans leurs fenêtres de discussion respectives. Le texte est obtenu à partir de la charge utile renvoyée par la requête "fetch" du serveur Nexmo.
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 */
}
Du côté des clients
L'interface côté client comporterait une fenêtre de discussion qui se déclenche lorsque l'on clique sur le bouton de discussion. Cette fenêtre se glissera par la droite et permettra au client de discuter avec l'agent d'assistance.
Le balisage de cette fenêtre de discussion n'est pas trop compliqué. Elle comporte un en-tête, une zone de message principale et un formulaire de saisie de texte au bas de la fenêtre.
<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>
Nous n'allons pas couvrir chaque ligne de CSS pour cela, mais je veux souligner comment faire glisser la fenêtre de chat depuis le côté d'une manière relativement plus performante. En général, les propriétés qui peuvent être animées en toute sécurité sont les transformations et l'opacité.
Idéalement, la fenêtre de discussion doit rester en place pendant que l'utilisateur fait défiler la page principale. position: fixed sur la fenêtre de discussion. De plus, vous traduiriez la fenêtre de discussion en dehors du cadre et la feriez glisser à l'intérieur lorsque l'utilisateur clique sur le déclencheur.
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);
}L'utilisation de display: flex pour la fenêtre de discussion nous permet de nous assurer que l'en-tête et le pied de page se trouvent toujours respectivement en haut et en bas de la fenêtre de discussion, tandis que la zone de message s'agrandit pour remplir l'espace disponible.
.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);
}De même, dans un chat typique, la conversation est présentée dans des bulles de message alternant entre la gauche et la droite, en fonction des participants respectifs au chat. Ce type de présentation est également plus simple avec flexbox.
Appliquer à nouveau un display: flex sur la zone de message. Cela vous permet d'utiliser les propriétés d'alignement de la boîte sur les messages individuels. Il suffit ensuite d'appliquer un align-self: flex-end sur les messages que vous souhaitez voir apparaître à droite de la zone de message.
.message.input {
position: relative;
align-self: flex-end;
}Le Javascript côté client pour la fenêtre de chat est nécessaire pour basculer la classe CSS appropriée afin de masquer et d'afficher la fenêtre de chat.
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 example
Du côté des agents
Du côté de l'agent, les messages peuvent occuper la totalité de la fenêtre et être affichés par défaut. Le fichier Javascript côté client pour la page de l'agent implique donc de transmettre au serveur la route correcte pour la requête de récupération.
window.addEventListener('load', function() {
setupConversation('/api/jwt/agent');
});Le style des messages serait assez similaire à celui de la fenêtre de discussion avec les clients, à l'exception des styles qui seraient inversés, car le modèle de conception typique est que vos propres messages apparaissent à droite, tandis que les messages reçus apparaissent à gauche.
Chat portal final
Quelle est la prochaine étape ?
Ce tutoriel n'a pas utilisé de frameworks frontaux ou de chargeurs de modules car il était destiné à simplifier l'application pour se concentrer sur l'API Conversation, ce qu'elle fait et comment elle fonctionne. Si vous souhaitez en savoir plus sur la Conversation API, voici quelques liens qui pourraient vous être utiles :
Documentation pour l'API Conversation sur le portail des développeurs
Si vous avez besoin de nous, essayez le canal Slack de la communauté Vonage
Dites-nous ce que vous en pensez en tweetant à @VonageDev
