https://d226lax1qjow5r.cloudfront.net/blog/blogposts/real-time-sms-demo-with-react-node-and-google-translate-dr/E_SMS-Translations_1200x600.png

Démonstration de SMS en temps réel avec React, Node et Google Translate

Publié le May 24, 2021

Temps de lecture : 8 minutes

L'année dernière, j'ai travaillé avec l Google Translate API pour traduire des SMS. Après l'avoir montré au reste de l'équipe, ils voulaient une démo qu'ils pourraient montrer à d'autres développeurs lors des conférences auxquelles nous participions. Sur cette base, j'ai entrepris de créer un frontend avec React qui pourrait afficher les traductions en temps réel.

Construire le WebSocket

Qu'est-ce qu'une WebSocket ?

Pour cette démo, j'ai décidé que l'utilisation d'une WebSocket serait une bonne solution. Si vous n'avez jamais utilisé de WebSocket, il s'agit d'un protocole qui permet à un client et à un serveur de communiquer en temps réel. Les WebSockets sont bidirectionnelles, ce qui signifie que le client et le serveur peuvent à la fois envoyer et recevoir des messages. Lorsque vous vous connectez pour la première fois à une WebSocket, la connexion est établie en passant d'un protocole HTTP au protocole WebSocket et est maintenue en vie tant qu'elle n'est pas interrompue. Une fois établie, elle fournit un flux continu de contenu. C'est exactement ce dont nous avons besoin pour recevoir des SMS traduits.

Créer le serveur WebSocket en Node

Comme première étape de la création des WebSockets, le serveur a besoin d'un chemin d'accès pour permettre les connexions des clients. En commençant par le fichier serveur original de mon article précédentnous pouvons faire quelques changements mineurs pour créer le serveur WebSocket et les événements et écouteurs requis par le client.

En utilisant le paquet ws sur NPM, nous pouvons rapidement créer ce dont nous avons besoin pour que cela fonctionne.

npm install ws

Une fois installé, incluez le paquet dans votre fichier serveur et créez le serveur WebSocket. WS permet une option path pour définir la route utilisée par le client pour se connecter.

const express = require('express');
const WebSocket = require('ws');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server, path: "/socket" });

Avec ce bout de code, le client dispose maintenant d'un endroit pour se connecter à la route WebSocket /socket. Le serveur étant prêt à fonctionner, vous devez maintenant écouter un connection . Lorsque le client se connecte, le serveur utilise ce qui suit pour mettre en place les autres récepteurs dont nous avons besoin :

wss.on('connection', (ws) => {
  ws.isAlive = true;
  ws.translateTo = 'en';

  ws.on('pong', () => {
    ws.isAlive = true;
  });

  ws.on('message', (message) => {
    translateTo = message;
  });

});

Il y a deux points principaux à souligner :

  1. Lors de la connexion, nous définissons la propriété isAlive à trueet attendons l'événement pong et écoutons l'événement Cet événement permet au serveur de vérifier et de maintenir la connexion avec le client. Le serveur envoie un ping et répond par pong pour vérifier que la connexion est toujours active.

  2. Ici, j'ai défini translateTo comme propriété à stocker. translateTo est défini par chaque client à l'aide d'une liste déroulante. Lorsqu'une personne utilisant notre application de démonstration pour le stand sélectionne une langue différente, cette action définit cette propriété pour traduire les textes SMS dans la langue demandée.

Maintenir la connexion

Un élément essentiel à prendre en compte est la vérification des clients qui se déconnectent. Il est possible qu'au cours du processus de déconnexion, le serveur n'en soit pas conscient et que des problèmes surviennent. Avec un bon ami setInterval()nous pouvons vérifier si nos clients sont toujours là et les reconnecter si nécessaire.

setInterval(() => {
  wss.clients.forEach((ws) => {
    if (!ws.isAlive) return ws.terminate();
    ws.isAlive = false;
    ws.ping(null, false, true);
  });
}, 10000);

Envoi de messages au client

Maintenant que la WebSocket est connectée et surveillée, nous pouvons gérer les messages entrants de Nexmo, la traduction et la réponse au client. La méthode handleRoute doit être mise à jour à partir de son état original afin d'ajouter la réponse pour chaque client.

const handleRoute = (req, res) => {

  let params = req.body;

  if (req.method === "GET") {
    params = req.query
  }

  if (!params.to || !params.msisdn) {
    res.status(400).send({ 'error': 'This is not a valid inbound SMS message!' });
  } else {
    wss.clients.forEach(async (client) => {
      let translation = await translateText(params, client.translateTo);
      let response = {
        from: obfuscateNumber(req.body.msisdn),
        translation: translation.translatedText,
        originalLanguage: translation.detectedSourceLanguage,
        originalMessage: params.text,
        translatedTo: client.translateTo
      }

      client.send(JSON.stringify(response));
    });

    res.status(200).end();
  }

};

La méthode wss.clients.forEach itère à travers chaque connexion, et envoie les paramètres SMS de Nexmo à l'API Google Translate. Une fois que la traduction revient, nous pouvons décider quelles données le front-end doit avoir, et les renvoyer sous forme de chaîne comme je l'ai fait ici avec client.send(JSON.stringify(response)).

Récapitulons ce qui s'est passé ici : Chaque client se connecte au serveur WebSocket en appelant la route /socket et en établissant une connexion. Un message SMS est envoyé du téléphone de l'expéditeur à Nexmo, qui appelle alors la route. /inboundSMS route. L'application transmet le message texte à l'API de traduction de Google pour chaque client connecté, puis le renvoie finalement à l'interface utilisateur du client.

Diagram of WebSocket FlowDiagram of WebSocket Flow

Ensuite, construisons les éléments de l'interface utilisateur pour l'afficher à l'écran.

WebSockets avec React

Une fois le serveur WebSocket lancé, nous pouvons passer à l'affichage des messages à l'écran. Comme j'aime utiliser Reactet plus important encore, React Hooksj'ai cherché quelque chose pour m'aider à me connecter aux WebSockets. Bien sûr, j'en ai trouvé un qui répondait exactement à mes besoins.

L'interface de l'application de démonstration est construite avec create-react-appet j'ai utilisé le module Grommet et j'ai utilisé le framework Grommet. Ces sujets ne sont pas abordés dans ce billet, mais vous pouvez consulter mon code source et le suivre.

Connexion au WebSocket

La première étape consiste à établir une connexion et à entamer une communication bidirectionnelle. Le module que j'ai trouvé est react-use-websocketet il a rendu la mise en place très simple.

npm install react-use-websocket

Il existe des tonnes de bibliothèques de crochets React qui vous aident à créer des fonctionnalités impressionnantes en peu de temps. Dans le cas présent, il a suffi d'importer le module et de mettre en place quelques éléments de configuration pour obtenir une connexion.

import useWebSocket from 'react-use-websocket';

const App = () => {
  const STATIC_OPTIONS = useMemo(() => ({
    shouldReconnect: (closeEvent) => true,
  }), []);

  const protocolPrefix = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  let { host } = window.location;
  const [sendMessage, lastMessage, readyState] = useWebSocket(`${protocolPrefix}//${host}/socket`, STATIC_OPTIONS);

  //...
}

Dans le composant, nous importons la méthode useWebSocket pour transmettre l'URL WebSocket et l'objet STATIC_OPTIONS comme deuxième argument. La méthode useWebSocket est un crochet personnalisé qui renvoie la méthode sendMessage méthode, lastMessage l'objet du serveur (qui est notre message traduit), et l'objet readyState qui est un entier pour nous donner l'état de la connexion.

Réception de messages entrants

Une fois que react-use-websocket a établi la connexion avec le serveur, nous pouvons maintenant commencer à écouter les messages provenant de la propriété lastMessage . Lorsque nous recevons des messages du serveur, ils s'affichent ici et mettent à jour le composant. Si votre serveur a plusieurs types de messages, vous discernez ces informations ici. Comme nous n'en avons qu'un, l'implémentation est plus simple.

const [messageHistory, setMessageHistory] = useState([]);

useEffect(() => {
  if (lastMessage !== null) {
    setMessageHistory(prev => prev.concat(lastMessage))
  }
}, [lastMessage]);

return (
  <Main>
    {messageHistory.map((message, idx) => {
      let msg = JSON.parse(message.data);
      return (
        <Box>
          <Text>From: {msg.from}</Text>
          <Heading level={2}>{msg.translation}</Heading>
        </Box>
      )
    })}
  </Main>
)

Le crochet intégré useEffect s'exécute chaque fois que l'état est mis à jour. Lorsque lastMessage n'est pas nul, il ajoute le nouveau message à la fin du tableau de l'état du message précédent, et l'interface utilisateur se met à jour en utilisant la fonction map pour afficher tous les messages. C'est dans la fonction messageHistory où sont stockées toutes les chaînes JSON transmises par le serveur. La fonctionnalité principale de notre WebSocket est complète, mais je souhaite encore ajouter quelques éléments.

Envoi de messages au serveur

Comme il s'agit d'une démonstration de traduction, le fait d'avoir plus d'une langue est un excellent moyen de montrer la puissance de l'API Google Translate en conjonction avec les messages SMS de Nexmo. J'ai créé une liste déroulante avec des langues à choisir. C'est dans cette liste déroulante que se fait la communication bidirectionnelle avec le serveur, et que l'application envoie la langue sélectionnée depuis le client.

const languages = [
  { label: "English", value: "en"},
  { label: "French", value: "fr"},
  { label: "German", value: "de"},
  { label: "Spanish", value: "es"}
];

<Select
  labelKey="label"
  onChange={({ option }) => {
    sendMessage(option.value)
    setTranslateValue(option.label)
  }}
  options={languages}
  value={translateValue}
  valueKey="value"
/>

Ici, la sendMessage de react-use-websocket nous permet de renvoyer des informations à notre serveur et de les consommer. C'est au cours de ce processus que le gestionnaire d'événements que nous avons mis en place plus tôt s'avère utile. C'est cette liste déroulante qui détermine la langue dans laquelle l'API Google Translate traduit le message et l'affiche à l'écran.

Affichage de l'état de la connexion

Comme il s'agit d'une démonstration dans un environnement de conférence, j'ai pensé qu'un indicateur de connectivité serait une bonne idée. Tant que le front-end reste connecté à la WebSocket, la lumière est verte.

const CONNECTION_STATUS_CONNECTING = 0;
const CONNECTION_STATUS_OPEN = 1;
const CONNECTION_STATUS_CLOSING = 2;

function Status({ status }) {
  switch (status) {
    case CONNECTION_STATUS_OPEN:
      return <>Connected<div className="led green"></div></>;
    case CONNECTION_STATUS_CONNECTING:
      return <>Connecting<div className="led yellow"></div></>;
    case CONNECTION_STATUS_CLOSING:
      return <>Closing<div className="led yellow"></div></>;
    default:
      return <>Disconnected<div className="led grey"></div></>;;
  }
}

//....
<Status status={readyState} />
//...

Le composant Status utilise la touche readyState pour passer d'un état à l'autre et l'indiquer à l'utilisateur. S'il devient rouge, vous savez que quelque chose ne va pas avec le serveur WebSocket, et vous devriez le vérifier.

Une fois que tout est en place et que tout fonctionne, cela ressemble à quelque chose comme ça :

Animation of Working Demo AppAnimation of Working Demo App

Essayez-le

Le code code de l'application de démonstration se trouve sur notre organisation communautaire GitHub, et vous pouvez l'essayer vous-même. J'ai créé un README qui devrait vous aider à configurer l'application et à l'exécuter localement sur votre serveur ou à la déployer sur Heroku. J'ai également fourni un fichier Docker, si vous préférez suivre cette voie. Laissez-moi savoir ce que vous en pensez, et si vous avez des problèmes, n'hésitez pas à me contacter et à soumettre un problème sur le repo.

Partager:

https://a.storyblok.com/f/270183/384x384/444c073b5e/kellyjandrews.png
Kelly J AndrewsAncien membre de l'équipe

Kelly J. Andrews est un défenseur des développeurs pour Nexmo. Il bricole des ordinateurs depuis plus de 30 ans et a utilisé BASIC pour la première fois à l'âge de 5 ans.

Ce n'est que lorsqu'il a créé sa première page web en 1997 et qu'il a essayé JavaScript pour la première fois qu'il a trouvé sa véritable vocation. Kelly se bat désormais pour JavaScript, un code testable et une livraison rapide.

Vous pouvez le trouver en train de chanter au karaoké, de faire de la magie ou d'encourager les Cubs et les Fighting Irish.