Construire un système d'assistance client bidirectionnel avec Symfony Notifier

August 28, 2020
Rédigé par
Alex Dunne
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs
Révisé par

Construire un système d'assistance client bidirectionnel via SMS et Slack à l'aide de Symfony Notifier

Chaque jour, les entreprises présentes sur Internet interagissent avec des millions de clients. Offrir aux clients une expérience positive contribue à valoriser l'image d'une marque et à accroître la confiance qu'ils lui témoignent. La fiabilité, la rapidité et la pertinence de l'assistance client sont essentielles à un service client de qualité, et à ce titre, tout part d'un moyen pratique et facile d'entrer en contact. Par ailleurs, fournir à votre équipe d'assistance client les outils dont elle a besoin pour aider vos clients est tout aussi important.

Nous créerons dans ce tutoriel une application qui transfère les SMS entrants en temps réel et nous permet de répondre directement depuis un canal Slack à l'aide de TwilioSlack et du Symfony Framework.

Ce faisant, vous verrez le composant Symfony Notifier en action et comprendrez comment tirer parti de son abstraction en vue de permettre au client et à votre personnel d'assistance d'échanger par différents canaux de communication. Enfin, nous examinerons comment réagir aux SMS entrants et aux messages Slack à l'aide de webhooks, et comment faire passer les messages entre les deux plates-formes.

Conditions préalables

Pour suivre l'ensemble de ce tutoriel, vous aurez besoin des éléments suivants :

Vous aurez également besoin d'un numéro de téléphone Twilio actif avec des fonctions SMS. Si vous n'avez pas encore de numéro de téléphone, vous pouvez en acheter un ici.

Création d'une nouvelle application Symfony

Pour commencer, nous allons créer un nouveau projet Symfony. Nous allons utiliser le binaire Symfony pour générer le projet pour nous. Si vous ne l'avez pas déjà installé, vous pouvez suivre les instructions d'installation de la documentation Symfony. Une fois le binaire Symfony installé, exécutez la commande suivante dans un terminal :

$ symfony new --full --version=5.1 sms-slack-proxy && cd sms-slack-proxy

Nous installerons également le SDK PHP Twiliocomposant Symfony Notifieradaptateur Slack Notifier pour Symfonyadaptateur Twilio Notifier pour Symfony et Guzzle pour une utilisation ultérieure. Exécutez la commande suivante dans un terminal :

$ composer require symfony/notifier symfony/slack-notifier symfony/twilio-notifier twilio/sdk eightpoints/guzzle-bundle

Lorsque vous y êtes invité, exécutez les recettes de bibliothèque pour créer les fichiers nécessaires. Une fois les fichiers créés, démarrez le serveur Symfony en exécutant la commande suivante dans un terminal :

$ symfony server:start

Vous devrez également exposer votre application Symfony à Internet afin que Twilio et Slack puissent communiquer en retour avec l'application à l'aide de webhooks. Dans un terminal distinct, créez un nouveau tunnel ngrok en exécutant la commande suivante :

$ ngrok http 8000

Si l'exécution réussit, vous devriez voir les informations de session ngrok ainsi qu'une URL de transfert comme suit :

url de transfert ngrok

Vérifiez que tout fonctionne correctement en visitant l'URL affichée sur votre terminal. Si la connexion réussit, la page d'accueil Symfony par défaut doit s'afficher.

Page d'accueil du développement Symfony

Enfin, avant de continuer, créez un fichier .env.local à l'aide de la commande suivante :

$ touch .env.local

Par défaut, Symfony fournit un fichier .gitignore avec .env.local comme l'une des entrées. C'est dans ce fichier que nous stockerons nos valeurs propres à l'environnement, y compris les clés API secrètes telles que nos informations d'identification Twilio et Slack.

Configuration de Twilio

Avant de commencer à communiquer avec Twilio, nous devons d'abord récupérer nos informations d'identification Twilio. Si vous n'avez pas encore créé de compte, vous pouvez créer un nouveau compte ici. Twilio vous fournira également des crédits gratuits pour tester l'API. Une fois connecté, accédez au tableau de bord et vous verrez vos Account SID et Auth Token (token d'authentification).

Tableau de bord Twilio

Vous aurez également besoin d'un numéro de téléphone actif avec des fonctions SMS. Si vous n'avez pas encore de numéro de téléphone, vous pouvez en acheter un ici.

Nous disposons maintenant de toutes les données nécessaires pour communiquer avec Twilio. Copiez vos Account SID, Auth Token et numéro de téléphone dans le fichier .env.local que nous avons créé précédemment comme suit, en remplaçant les valeurs en conséquence :

# .env.local
TWILIO_DSN=twilio://<ACCOUNT_SID>:<AUTH_TOKEN>@default?from=<NUMBER>

Configuration de Slack

Rendez-vous sur https://api.slack.com/apps?new_app=1 et créez une nouvelle application Slack. Donnez à l'application un nom descriptif, comme « SMS Customer Support », et sélectionnez l'Espace de travail auquel vous souhaitez ajouter l'application.


Nouvelle configuration de l&#x27;application Slack

Une fois l'application Slack créée, sélectionnez l'option Incoming Webhooks (WebHooks entrants) et cochez la case Activate Incoming Webhooks (Activer les WebHooks entrants).


Fonctions et fonctionnalités de l&#x27;application Slack

Option Activate Incoming Webhooks (Activer les WebHooks entrants)

Appuyez maintenant sur le bouton Add New Webhook to Workspace (Ajouter un nouveau webhook à l'Espace de travail) au bas de la page :


Création d&#x27;une nouvelle URL Webhook

Sélectionnez le canal sur lequel vous souhaitez recevoir les questions des clients. Je vais utiliser un canal « customer-support » (assistance-client).


Autorisation d&#x27;accès de l&#x27;application Slack à un canal Slack

Appuyez sur Allow (Autoriser) pour donner à l'application Slack l'autorisation d'accéder au canal sélectionné et de générer ensuite une nouvelle URL de webhook.

Exemple d&#x27;URL Webhook

Copiez l'URL de webhook fournie et remplacez https://hooks.slack.com/services par slack://default. Ajoutez cette valeur au fichier .env.local comme suit :

# .env.local
SLACK_DSN=slack://default/TXXXXXX/BXXXXXX/9XXXXXX

Configuration du composant Symfony Notifier

La configuration de Symfony Notifier est très facile, grâce au travail colossal que les contributeurs à la bibliothèque abattent pour nous. Mettez à jour le fichier config/packages/notifier.yaml avec le contenu suivant :

# config/packages/notifier.yaml
framework:
    notifier:
        texter_transports:
            twilio: '%env(TWILIO_DSN)%'
        chatter_transports:
            slack: '%env(SLACK_DSN)%'

Vous venez de configurer deux types de canaux différents : les SMS et le chat. Il existe quatre types de canaux d'assistance et chacun s'intègre à différentes plates-formes. Outre les canaux de SMS et de chat que vous avez déjà découverts, il existe les canaux d'e-mail et de navigateur. Le composant Notifier fournit une couche d'abstraction puissante sur chaque type de canal, vous permettant ainsi, en tant que développeur, d'interagir avec les services de messagerie externes avec un minimum d'effort.

Gestion des messages SMS entrants

Accédez à la page des détails du numéro de téléphone Twilio correspondant au numéro de téléphone que vous utilisez dans ce projet. Faites défiler vers le bas jusqu'à la section Messaging (Messagerie) dans l'onglet Configure (Configurer), et remplacez la sélection dans le menu déroulant A MESSAGE COMES IN (UN MESSAGE ENTRE) par « Webhook ». Dans le champ de texte adjacent, ajoutez la valeur http://xyz.ngrok.io/webhooks/twilio/sms/incoming, en veillant à bien modifier l'URL xyz.ngrok.io pour qu'elle corresponde à celle de votre terminal.

Un message s&#x27;affiche dans un webhook pointant vers votre url ngrok

Maintenant, créez une route et renvoyez une réponse TwiML simple pour vérifier que la configuration fonctionne. Dans le répertoire src/Controller, créez un nouveau fichier nommé TwilioWebhookController.php et ajoutez le contenu suivant :

 

<?php
// src/Controller/TwilioWebhookController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Twilio\TwiML\MessagingResponse;

/**
 * @Route("/webhooks/twilio")
 */
class TwilioWebhookController extends AbstractController
{
    /**
     * @Route("/sms/incoming", name="webhook.twilio.sms_incoming")
     * @return Response
     */
    public function handleIncomingSmsMessage()
    {
        $response = new MessagingResponse();
        $response->message("Thanks for getting in touch. We'll get back to you as quickly as possible.");

        return new Response($response);
    }
}

Essayez d'envoyer un SMS à votre numéro de téléphone Twilio afin de vous assurer que tout est configuré correctement.

Confirmation initiale du test webhook

Transfert de messages SMS vers un canal Slack

Une fois la configuration du webhook Twilio terminée, nous pouvons passer au transfert du message vers le canal Slack d'assistance client à récupérer par votre équipe d'assistance clientèle. Mettez à jour le fichier TwilioWebhookController.php comme suit :

 

<?php
// src/Controller/TwilioWebhookController.php

// ...
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\NotifierInterface;
// ...

/**
 * @Route("/sms/incoming", name="webhook.twilio.sms_incoming")
 * @param Request $request
 * @param NotifierInterface $notifier
 * @return Response
 */
public function handleIncomingSmsMessage(Request $request, NotifierInterface $notifier)
{
    $from = $request->request->get('From');
    $body = $request->request->get('Body');

    $notification = (new Notification($from, ['chat/slack']))
        ->content($body);
    $notifier->send($notification);

    return new Response();
}

Passons en revue ces changements. Tout d'abord, vous extrayez les composants From et Body depuis la charge utile de la requête. Ces valeurs sont fournies par Twilio et correspondent respectivement au numéro de téléphone et au contenu du message SMS reçu.

Avec ces valeurs, vous construisez une nouvelle Notification en utilisant le numéro de téléphone du client comme objet et le corps du message comme contenu de la notification.

Vous pouvez voir aussi que nous avons fourni [‘chat/slack’] comme deuxième paramètre au constructeur de Notification. Il s'agit du paramètre de canal. Ce paramètre accepte un tableau de canaux via lesquels envoyer le message. En fournissant plusieurs canaux, la notification peut être envoyée en parallèle via différents mécanismes de transport. Par exemple, si nous voulions envoyer un SMS et un e-mail lorsqu'une commande est confirmée, nous pourrions utiliser [‘email’, ‘sms’].

Il est important de noter que lorsque vous utilisez un canal Chatter, vous devez également fournir le transport, tel que [‘chat/slack’] ou [‘chat/telegram’].

Si vous envoyez un SMS à votre numéro de téléphone Twilio, le message devrait apparaître dans le canal Slack que vous avez sélectionné précédemment.

Envoi d&#x27;un SMS au numéro Twilio


Réception du SMS dans Slack

Réponse aux clients depuis Slack

Bravo ! Votre application transfère maintenant les questions des clients depuis Twilio vers un canal Slack et peut commencer à y répondre.

Retournez sur votre application Slack, accédez à Event Subscriptions (Abonnements aux événements) et à Enable Events (Activer les événements). Ces sections se trouvent dans le sous-menu des fonctionnalités de votre application.

Activation des abonnements aux événements

L'application nécessite une vérification avant que Slack ne commence à publier des événements sur les webhooks de votre application. Cela garantit que l'application vous appartient, car vous ne voudriez pas que des messages chat personnels soient envoyés à d'autres personnes !

Le processus de vérification implique que Slack envoie à votre API une requête HTTP contenant une chaîne de vérification. En retour, il est attendu de votre API qu'elle renvoie cette chaîne de vérification dans la réponse à vérifier.

Créez un nouveau fichier nommé SlackWebhookController.php dans le répertoire src/Controller avec le contenu suivant :

 

<?php
// src/Controller/SlackWebhookController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route("/webhooks/slack")
 */
class SlackWebhookController extends AbstractController
{
    /**
     * @Route("/events", name="webhook.slack.action")
     * @param Request $request
     * @return Response
     */
    public function handleSlackEvent(Request $request)
    {
        $data = json_decode($request->getContent(), true);

        return new JsonResponse(['challenge' => $data['challenge']]);
    }
}

Revenez à la page Web de l'application Slack et insérez la valeur http://xyz.ngrok.io/webhooks/slack/events dans le champ Request URL (URL de requête). Veillez à remplacer l'URL ngrok par l'URL ngrok qui se trouve dans votre terminal. Slack devrait automatiquement réessayer la vérification et, en cas de réussite, vous serez accueilli par une confirmation vérifiée.

Vérification du Webhook Slack

Ouvrez la section Subscribe to bot events (S'abonner aux événements bot) et créez un nouvel événement d'utilisateur bot pour les événements message.channels. En vous abonnant à cet événement, chaque nouveau message auquel le bot a accès sera envoyé à l'URL de requête que nous avons configurée ci-dessus. Cela inclut les messages ajoutés par une personne directement au canal, les réponses à un thread, et même les messages bot qui transfèrent les messages SMS du client.

Abonnement à de nouveaux événements bot

Appuyez sur le bouton Save Changes (Enregistrer les modifications) au bas de la page et réinstallez votre application Slack si vous y êtes invité. Invitez votre bot au canal que vous avez sélectionné précédemment (par exemple, #customer-support), si cela n'a pas été fait automatiquement pour vous.

Invitation du bot au canal de support client

Transfert des messages Slack aux clients

Maintenant que nous avons indiqué à Slack les messages qui nous intéressent et où les envoyer, nous pouvons commencer à traiter les réponses de votre équipe d'assistance client. Les réponses à chaque requête client seront traitées à l'aide de la fonctionnalité de threading de Slack. Ce faisant, nous évitons le risque qu'une réponse du canal principal ne soit envoyée accidentellement au mauvais client. En outre, le threading garantit que toutes les informations sont bien contenues et isolées.

Comme nous voulons uniquement traiter les messages qui font partie d'un thread, vous devez filtrer les messages non concernés et exclure également les messages parents du thread.

Allons-y ! Mettez à jour la méthode handleSlackEvent dans votre fichier src/Controller/SlackWebhookController.php comme suit :

// src/Controller/SlackWebhookController.php

/**
 * @Route("/", name="webhook.slack.action")
 * @param Request $request
 * @return Response
 */
public function handleSlackEvent(Request $request)
{
    $data = json_decode($request->getContent(), true);
    $event = $data['event'];

    // Ignore bot messages
    if (array_key_exists('subtype', $event) && $event === 'bot_message') {
        return new JsonResponse();
    }

    // Ignore messages that don't belong to a thread
    if (!array_key_exists('thread_ts', $event)) {
        return new JsonResponse();
    }

    $messageId = $event['ts'];
    $threadId = $event['thread_ts'];

    // This is the thread parent message which we're also not interested in
    if ($messageId === $threadId) {
        return new JsonResponse();
    }

    $supportResponseMessage = $event['text'];

    return new JsonResponse();
}

À ce stade, vous avez filtré tous les messages qui ne vous intéressent pas et extrait la réponse à la question du client.

Nous devons maintenant déterminer à quel numéro de téléphone envoyer la réponse. Lorsque vous avez transféré plus haut les messages de Twilio vers Slack, vous avez défini l'objet de la notification comme étant le numéro de téléphone du client. Pour y accéder, nous devons communiquer avec l'API de Slack pour accéder au thread de messages auquel appartient la réponse actuelle. Ce faisant, nous pouvons accéder au parent du thread et en retour, au numéro de téléphone du client.

Nous allons maintenant configurer un nouveau client Guzzle qui interagira avec l'API Slack. Avant cela, nous devons acquérir un token d'accès et l'ID de canal pour notre canal #customer-support. Pour trouver votre token d'accès, retournez à la page de l'application Slack et sélectionnez OAuth & Permissions (Authentification et autorisations) dans le menu Features (Fonctionnalités).

Éléments de menu OAuth and Permissions (Authentification et autorisations)

Sur cette page, vous verrez un token d'accès OAuth d'utilisateur bot. Copiez cette valeur et ajoutez-la au fichier .env.local.

Exemple de token OAuth (Authentification)
# .env.local
SLACK_ACCESS_TOKEN=xoxb-XXX

Nous devons ensuite trouver l'ID de canal de votre canal d'assistance client. La façon la plus simple de le faire est de vous rendre sur votre canal Slack via un navigateur Web.

Rendez-vous sur https://slack.com/intl/fr-fr/  et appuyez sur le bouton Lancer Slack en haut à droite.

Une fois chargé, accédez à votre canal d'assistance client (par exemple, #customer-support). L'URL est au format https://app.slack.com/client/<team_id>/<channel_id>. Extrayez l'ID de canal de l'URL et ajoutez cette valeur au fichier .env.local comme suit :

# .env.local
SLACK_CHANNEL=<channel_id>

Une fois le token d'accès et le canal configurés dans votre variable d'environnement, vous pouvez maintenant configurer un client Guzzle pour accéder à l'API Slack. Copiez le contenu suivant dans votre fichier config/packages/eight_points_guzzle.yaml :

# config/packages/eight_points_guzzle.yaml
eight_points_guzzle:
    clients:
        slack:
            base_url: https://slack.com/api/

            options:
                timeout: 30
                http_errors: true
                headers:
                    Accept: "application/x-www-form-urlencoded"
                query:
                    token: '%env(SLACK_ACCESS_TOKEN)%'
                    channel: '%env(SLACK_CHANNEL)%'

La configuration ci-dessus crée un nouveau client Guzzle instancié avec l'URL de base de l'API Slack et le type de contenu attendu par l'API Slack. La configuration gère également l'ajout de votre token d'accès Slack et de votre ID de canal à chacune des requêtes en tant que paramètres de requête.  

Maintenant, construisons une enveloppe légère sur la base du client afin d'encapsuler la récupération des données. Créez un nouveau répertoire Service dans le répertoire src. Dans le répertoire src/Service, créez un nouveau fichier nommé SlackApiClient.php et ajoutez les éléments suivants :

 

<?php
// src/Service/SlackApiClient.php

namespace App\Service;

use GuzzleHttp\Client;

class SlackApiClient
{
    /** @var Client $client */
    private $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function getConversationReplies(string $threadParentId)
    {
        $response = $this->client->get('conversations.replies', [
            'query' => array_merge(
                $this->client->getConfig()['query'],
                ['ts' => $threadParentId]
            )
        ]);

        $body = json_decode($response->getBody()->getContents(), true);

        return $body['messages'];
    }
}

La méthode getConversationReplies renvoie tous les messages d'un thread pour l'ID donné. Cela inclut également le message parent qui contient le numéro de téléphone du client que nous recherchons.

Pour le moment, Symfony ne peut pas injecter correctement de dépendances dans GuzzleHttp\Client par lui-même, c'est pourquoi nous devons indiquer à Symfony quel client Guzzle utiliser. Ajoutez ce qui suit à votre fichier config/services.yaml :

# config/services.yaml
App\Service\SlackApiClient:
        arguments:
            - '@eight_points_guzzle.client.slack'

Retournez sur le SlackWebhookController et utilisez le SlackApiClient pour extraire le numéro de téléphone du client depuis le thread.

 

<?php
// src/Controller/SlackWebhookController.php

// ...
use App\Service\SlackApiClient;

// ...

/**
 * @Route("/", name="webhook.slack.action")
 * @param Request $request
 * @param SlackApiClient $slackApiClient
 * @return Response
 */
public function handleSlackEvent(Request $request, SlackApiClient $slackApiClient)
{
    // ...

    $messages = $slackApiClient->getConversationReplies($threadId);
    $parent = $messages[0];
    $firstBlock = $parent['blocks'][0];
    $customerPhoneNumber = $firstBlock['text']['text'];

    return new JsonResponse();
}

Vous pouvez désormais utiliser le composant Symfony Notifier et Twilio pour envoyer au client la réponse au message qu'il a envoyé à l'assistance.

 

<?php
// src/Controller/SlackWebhookController.php

// ...
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\NotifierInterface;
use Symfony\Component\Notifier\Recipient\AdminRecipient;

// ...

/**
  * @Route("/events", name="webhook.slack.action")
  * @param Request $request
  * @param SlackApiClient $slackApiClient
  * @param NotifierInterface $notifier
  * @return Response
  */
public function handleSlackEvent(
        Request $request, SlackApiClient $slackApiClient, NotifierInterface $notifier
)
{
        // ...

        $notifier->send(
                new Notification($supportResponseMessage, ['sms']), 
                new AdminRecipient('', $customerPhoneNumber)
            );

        return new JsonResponse();
}

Le composant Notifier fournit trois types de destinataires différents :

  • NoRecipient : destinataire par défaut si aucun n'est fourni. Ce destinataire peut être utilisé lorsqu'aucune information de contact sur l'utilisateur n'est requise, comme les messages flash pour les notifications du navigateur.
  • Recipient : un échelon de plus par rapport au type NoRecipient. Ce type contient une adresse e-mail qui sera utilisée pour les notifications par e-mail et par navigateur.
  • AdminRecipient : ce type peut contenir à la fois une adresse e-mail et un numéro de téléphone, ce qui signifie qu'il peut être utilisé pour toutes les notifications. Il s'agit du type de destinataire que nous utilisons car nous devons fournir un numéro de téléphone pour contacter le client.

Test

Pour tester le bon fonctionnement de votre application, essayez d'envoyer un SMS à votre numéro de téléphone Twilio. Peu de temps après l'envoi du message, vous devriez voir le message transféré dans le canal Slack que vous avez sélectionné. Répondez au message en sélectionnant l'option Reply in thread (Répondre dans le thread). Peu de temps après, vous devriez recevoir un message SMS avec la réponse à l'appareil qui a envoyé le message initial.

Exemple de message

Réponse de Slack

Conclusion

Félicitations ! Vous avez utilisé avec succès l'API Twilio Programmable SMS et l'API de Slack via une abstraction derrière la formidable bibliothèque de composants Symfony Notifier, afin de transférer des messages d'utilisateurs externes vers un canal Slack. Vous devriez maintenant comprendre à quel point cette abstraction peut être puissante, et à quel point Twilio facilite l'ajout d'autres formes de communication à vos applications.

À partir de là, on pourrait aller dans différentes directions. Avant tout, je vous recommanderais de vérifier les requêtes provenant de Slack à l'aide de la signature x-slack, si vous prévoyez de l'utiliser dans un environnement de production. Pour en savoir plus, cliquez ici. 

Si vous cherchez quelque chose de plus amusant, vous pouvez aussi donner aux membres de votre équipe d'assistance client la possibilité de marquer les questions auxquelles ils ont répondu à l'aide d'un emoji. Des emojis différents pourraient avoir une signification différente pour votre entreprise. Pour commencer, il vous faudrait écouter les occurrences d'événements emoji, et en retour, envoyer automatiquement des messages de remerciement au client en fonction de l'emoji utilisé.

Alex Dunne est un ingénieur logiciel basé à Birmingham, au Royaume-Uni. On le retrouve souvent à expérimenter avec les nouvelles technologies à ajouter à sa panoplie, déjà bien fournie.