Créer un répondeur avec Twilio et Ruby on Rails

February 23, 2021
Rédigé par
Révisé par

Créer un répondeur avec Twilio et Ruby On Rails

Une façon utile et répandue d’utiliser Twilio est de créer un numéro de téléphone qui redirige vers notre numéro de téléphone réel. On peut ainsi donner ce numéro à nos contacts professionnels et gérer nos appels business depuis notre portable. Notre numéro personnel est ainsi masqué derrière notre numéro Twilio.

Dans ce post je vais vous montrer comment créer un système de boîte vocale qui s’activera si vous êtes déjà en communication ou que vous ne prenez pas l’appel. Ce répondeur diffusera un message préenregistré, prendra le message de l’appelant et vous enverra un SMS avec un lien permettant d’y accéder. Encore plus simple que de devoir appeler sa boîte vocale.

Avant de commencer : Comment Twilio gère les appels

Lorsqu’un appel est émis vers votre numéro de téléphone Twilio, une requête HTTP est envoyée à une URL qui pointe vers un serveur web. Le serveur web reçoit et traite la requête. Il répond ensuite à Twilio avec du TwiML contenant les instructions à exécuter.

schéma de l'interaction entre un téléphone physique, Twilio et une application

Pour programmer le comportement de notre numéro Twilio nous allons créer un serveur web qui acceptera des requêtes venant de Twilio et qui répondra avec du TwiML, un langage de Markup basé sur XML que Twilio interprète notamment pour la gestion des appels - d’autres possibilités seront explorées dans de futurs articles.

Nous allons utiliser Ruby comme langage de programmation et RubyOnRails comme web framework. Mais il est tout à fait possible de faire la même chose dans tous les langages de programmation, comme ici en Java.

Pré-requis

Pour créer votre boîte vocale vous avez besoin de:

  • Un compte Twilio

  • Un numéro de téléphone Twilio (nous le prendrons ensemble plus bas)

  • Ruby on Rails installé dans votre machine

Créer le projet

Dans votre console, créer un nouveau projet RubyOnRails à l’aide de la CLI

$ rails new VoiceMail
$ cd VoiceMail

Cette commande crée un projet RubyOnRails prêt à l’emploi. Ouvrez le avec votre éditeur de texte préféré et allons-y!

Dépendances et configuration

Pour créer cette application nous allons utiliser:

  • La gem Twilio pour interagir avec la plateforme
  • La gem Figaro pour gérer nos secrets (clés d’API surtout)

Ouvrez le Gemfile qui se trouve à la racine de votre projet et ajoutez le code suivant:

# Gemfile
gem 'figaro'
gem 'twilio-ruby'

Depuis votre console installez les gem avec la commande$ bundle install et complétez l’installation de Figaro avec $ bundle exec figaro install.

Cette dernière commande crée un nouveau fichier de configuration config/application.yml, et il crée ou édite le fichier .gitignore en ajoutant pour git l’instruction de ne pas suivre le fichier qui vient d’être créé.

Et c’est exactement là où nous allons mettre nos variables d'environnement.

Ouvrez donc le fichier avec votre éditeur de texte et remplacez les X avec les valeurs correspondantes depuis votre console Twilio. Pour la ligne my_phone_number, vous devez ajouter votre numéro de téléphone personnel - et pas votre numéro Twilio - au format E.164.

# config/application.yml
twilio_account_sid: "X"
twilio_auth_token: "X"
my_phone_number: "+X"

Je me répète mais assurez vous d’avoir lancé la commande $ bundle exec figaro install, dans le cas contraire vos secrets peuvent être compromis lors du push de votre code sur git.

C’est fait ? Très bien, nous pouvons maintenant créer un initialiseur pour notre Client Twilio.

Dans le dossier config/initializers, créez un nouveau fichier nommé twilio.rb.

# config/initializers/twilio.rb
Twilio.configure do |config|
  config.account_sid = ENV["twilio_account_sid"]
  config.auth_token = ENV["twilio_auth_token"]
end

Grâce à cette configuration, nous n'aurons pas besoin de mettre les identifiants en dur dans le code ni à spécifier où ils sont.


La logique de l'application

Une chose que j'aime à propos de la programmation est la façon dont elle vous oblige à diviser un problème en petits morceaux de problème. Alors, quels sont-ils dans cette application?
Vous devez demander à Twilio de:

Gérer les appels entrants et les rediriger vers votre vrai numéro de portable,Si vous répondez, il n'y a rien de plus à faire. Mais dans le cas où votre ligne est occupée ou que vous ne décrochez pas, lire un message audio et enregistrer le message de l'appelant .Quand le message de l'appelant à été pris, nous envoyer un SMS contenant un lien vers le message vocal.
Chacune de ces actions va être gérée par une route différente, et nous allons toutes les créer maintenant.

Ouvrez config/routes.rb et ajoutez-y

#config/routes.rb
Rails.application.routes.draw do
  get '/initial-answer', action: :initial_answer, controller: 'twilio'
  get '/handle-unanswered-call', action: :handle_unanswered_call, controller: 'twilio'
  post '/recordings', action: :recordings, controller: 'twilio'
end

Voilà, nous avons trois routes, une par action et elles sont toutes liées à un contrôleur nommé Twilio.

Créez le fichier /twilio_controller.rb dans le dossier des contrôleurs, ce qui doit donner app/controllers/twilio_controller.rb.

Voici une base pour ce contrôleur avec juste les déclarations des fonctions que nous allons coder juste ensuite.

# app/controllers/twilio_controller.rb
class TwilioController < ApplicationController
  skip_before_action :verify_authenticity_token

  def initial_answer
  end

  def handle_unanswered_call
  end

  def recordings
  end
end

On peut noter ici le skip_before_action :verify_authenticity_token. Cette ligne est là uniquement pour faciliter le développement de notre application et utilisée seule n’est pas une bonne pratique pour du code dédié à la production. Vous pouvez implémenter cette méthode de vérification au moment voulu.

Gérer et rediriger les appels entrants

Dans notre TwilioController, nous allons nous intéresser premièrement à la méthode initial_answer. Cette méthode retourne du TwiML (Twilio Markup Language) contenant des instructions pour répondre à l’appel et le rediriger vers votre téléphone.

# app/controllers/twilio_controller.rb
def initial_answer
  response = Twilio::TwiML::VoiceResponse.new
  initial_answer = response.dial(number: ENV['my_phone_number'],
                                 timeout: 10,
                                 action: "/handle-unanswered-call",
                                 method: "GET")
  render xml: initial_answer
end

Okay, il se passe pas mal de choses dans ce code donc regardons-y de plus près:

  • On crée une VoiceResponse qui va envoyer du TwiML à Twilio
  • On utilise le verbe TwiML Dial pour rediriger vers notre propre numéro .
  • Le verbe Dial accepte différents attributs, ici nous avons utilisé:
    • number pour rediriger l’appel entrant, ici celui que nous avons spécifié dans la variable d’environnement ENV['my_phone_number'].
    • timeout qui est le temps alloué à la sonnerie. Après ce délai l’appel est considéré comme sans réponse. Il est réglé sur 10 secondes mais vous pouvez le changer à votre guise (à noter que Twilio ajoute un buffer de 5 secondes, ce qui correspond au temps d’établir la connexion téléphonique)
    • action et method disent à Twilio où aller chercher les prochaines instructions TwiML quand l’appel aura été soit répondu, soit rejeté soit non décroché.
  • Et finalement, on rend le TwiML que l’on a créé au format  xml en utilisant le helper render.

Tiens, tiens, ne serait-ce pas le moment de tester si notre projet a été correctement configuré ?

Lançons le serveur avec $ rails s dans notre terminal. Cette commande lance notre serveur Rails sur localhost:3000.

Une fois qu’il a bien démarré, allez dans votre navigateur à l’adresse http://localhost:3000/initial-answer. Vous devriez voir une réponse semblable à:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial action="/handle-unanswered-call" 
        method="GET"
        timeout="10">
    +xxxxxxxxxxxx
  </Dial>
</Response>

Si vous voyez bien toutes ces informations, c’est bon, votre setup est correct. Si vous voyez presque ça, mais sans le numéro de téléphone, ça veut dire que la variable d'environnement  my_phone_number n’a pas été bien configurée.

Le répondeur

Penchons-nous maintenant sur notre méthode handle_unanswered_call. Dans notre TwilioController nous allons ajouter les lignes suivantes à ladite méthode

# app/controllers/twilio_controller.rb
def handle_unanswered_call
  if params["DialCallStatus"] == "busy" || params["DialCallStatus"] == "no-answer" 
    render xml: voicemail_twiml
  end
end

La condition if nous permet de vérifier que nous permet de vérifier que nous sommes dans un cas ou nous étions déjà en ligne ou que nous n’avons pas décroché.

Si l’un des deux cas est vrai, nous retournons le résultat de la fonction voicemail_twiml.

Mais, pause ! Où est cette fonction ? Nous ne l’avons pas encore créée.

Puisque cette méthode n’a pas besoin d’être en contact direct avec le world wide web, nous allons la définir comme privée.

Avant le end final de notre TwilioController, ajoutez la fonction sous la mention private comme suit:

# app/controllers/twilio_controller.rb
private

def voicemail_twiml
  response = Twilio::TwiML::VoiceResponse.new
                
  response.pause(length: 2)
          .play(url: '/message.mp3')
          .record(play_beep: true, action: '/recordings')                                
end                                

Ce code génère du TwiML qui définit une pause de deux secondes avant de diffuser notre message de répondeur puis d'enregistrer le message vocal de l’appelant.

Vous pouvez enregistrer votre propre message.mp3 ou utiliser celui ci qui est un classique.

Peu importe quelle méthode vous utilisez, une fois que vous avez votre fichier message.mp3, mettez le dans le dossier /public situé à la racine du projet. Rails sert automatiquement tous les fichiers statiques placés dans ce dossier, donc rien de plus à configurer.

Si vous ne voulez pas utiliser votre propre audio, vous pouvez aussi utiliser le verbe TwiML Say, ce qui lira le texte dans une voix générée informatiquement. Pour ça, remplacez .play(url: '/message.mp3') par:

.say(message: "Je ne peux pas prendre votre appel pour le moment, merci de laisser un message.")

Le verbe  Record accepte un paramètre optionnel action qui comme précédemment va dire à Twilio où chercher les instructions suivantes après que notre appelant ait laissé son message. Ici nous lui disons d’appeler notre endpoint /recordings.

Gérer les messages vocaux

Notre dernière fonction à coder est recordings, que l’on vient justement de dire à Twilio d’appeler. Dans les paramètres renvoyés par Twilio on peut noter la présence de

  • RecordingUrl - Vous pouvez télécharger le message vocal à cette url, au format wav sans rien modifier ou en ajoutant .mp3 à l’url pour l’avoir dans cet autre format.
  • From - le numéro de l’appelant
  • To - le numéro appelé, ici, votre numéro Twilio.

Ce code Ruby se sert donc de ces paramètres pour envoyer un SMS avec les détails de l’appel manqué à votre vrai numéro de téléphone.

# app/controllers/twilio_controller.rb
def recordings
  mp3_recording_url = params["RecordingUrl"] + ".mp3"
  sms_notification = "Vous avez un message vocal de #{params["From"]} - écoutez le ici: #{mp3_recording_url}"

  client = Twilio::REST::Client.new
  client.messages.create(from: params["To"],
                         to: ENV['my_phone_number'],
                         body: sms_notification)

  head :ok
end

Prendre en charge les vrais appels avec notre serveur

Nous avons besoin de trois choses pour que notre implémentation fonctionne dans le monde réel:

  • Twilio doit pouvoir communiquer avec le serveur,
  • On doit acheter un numéro de téléphone Twilio,
  • Configurer le numéro Twilio pour qu’il contacte notre application via la route /initial-answer lorsque l’on reçoit un appel sur notre numéro Twilio.

Ouvrir notre application au monde

Nous avons besoin de nous assurer que notre serveur est accessible via internet, et localhost n’est pas suffisant pour ça. Il y a des tas de façons de déployer des applications Ruby et Rails, mais la façon la plus simple de tester que tout fonctionne bien est d’utiliser ngrok, un petit outil en ligne de commande qui crée un tunnel entre notre localhost et une URL publique avec la simple commande $ ngrok http 3000.

Maintenant que notre serveur peut communiquer avec les internets mondiaux, nous devons dire à Rails qu’il peut gérer les requêtes lui arrivant depuis cette url.

Puisque pour l’instant on est en mode dev, on va éditer le fichier de configuration spécifique au développement, mais il suffirait de faire la même chose dans le fichier production ou test selon vos besoins.

Juste avant le end final dans config/environments/development, ajoutez

#config/environments/development
config.hosts << "your-url.ngrok.io"

Vous pouvez voir le code complet de l’application sur mon GitHub.

Obtenir un numéro de téléphone Twilio

Si vous avez déjà un numéro de téléphone Twilio que vous voulez utiliser, passez directement à l’étape suivante.

Dans le cas contraire, rendez vous dans la console Twilio, vous devriez avoir un numéro de test disponible. Si vous voulez utiliser un vrai numéro depuis la France par exemple, vous aurez besoin d’un regulatory bundle.

Une fois que vous avez votre numéro, allez sur la page de configuration de celui-ci et ajoutez votre url publique + /initial-answer à When a call comes in.

N’oubliez pas de définir la méthode HTTP sur GET.

Configuration du webhook dans la console Twilio

N’oubliez pas de Save votre configuration en bas de page. Bravo, vous avez un numéro de téléphone secondaire avec une boîte vocale.

Faites vous appeler sur votre numéro Twilio (ça ne fonctionnera pas si vous essayez depuis le numéro sur lequel vous redirigez l’appel).

Vous verrez l’appel entrant, et si vous décidez de ne pas décrocher, la personne à l’autre bout de la ligne entendra votre répondeur, aura la possibilité de laisser un message vocal. Si un message est enregistré vous recevrez un SMS contenant un lien cliquable vers l’audio du message vocal.

Conclusion                                                                        

Si vous m’avez suivie jusqu'ici, vous avez maintenant une boîte vocale fonctionnelle pour notre numéro Twilio.

Vous en savez désormais un peu plus sur la façon dont Twilio utilise les webhooks pour gérer les appels vocaux et comment les créer avec Ruby On Rails.

Maintenant que vous avez une boîte vocale fonctionnelle, pourquoi ne pas laisser sonner votre téléphone et continuer à jouer avec Ruby et Twilio ? Vous pouvez...

Si vous créez des choses avec Twilio et Ruby j’adorerais en entendre parler, contactez moi sur Twitter ou via email!