Gérez les appels dans le navigateur avec Python, Javascript et Twilio Voice

April 21, 2021
Rédigé par
Carlos Mucuho
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs

Gérez les appels téléphoniques dans le navigateur avec Python, Javascript et Twilio Voice

Dans ce tutoriel, nous allons écrire une application qui utilise l'API Twilio Programmable Voice pour passer et recevoir des appels téléphoniques depuis un navigateur Web. Nous mettrons également en place une interface utilisateur nous permettant de passer, d'accepter et de rejeter des appels téléphoniques.

À la fin de ce tutoriel, nous aurons une application qui ressemblera à ce qui suit :

Démonstration du projet

Prérequis pour ce tutoriel

Pour suivre ce tutoriel, vous aurez besoin des composants suivants :

  • Un compte Twilio gratuit ou payant. Si vous êtes nouveau sur Twilio, créez un compte gratuit maintenant. Si vous créez votre compte depuis ce lien et que vous le mettez à niveau vers un compte payant par la suite, vous recevrez un coupon d’une valeur de de 10 $.
  • Un numéro de téléphone Twilio permettant de passer et de recevoir des appels téléphoniques. Créez-en un maintenant si vous n'en avez pas déjà un.
  • Python 3.6+ installé.
  • ngrok installé. ngrok est un service de proxy inverse qui crée un tunnel sécurisé d'un point de terminaison public vers un service Web exécuté localement. Nous devrons utiliser ngrok pour créer une URL sécurisée permettant à Twilio de se connecter à notre application.
  • Un téléphone portable ou un téléphone permettant de passer et recevoir des appels téléphoniques, en vue de tester le projet.

Concepts de base et logique applicative

L'objectif de ce tutoriel est de construire une application Web nous permettant de passer et recevoir des appels téléphoniques dans le navigateur. Nous utiliserons le framework Flask afin de mettre en œuvre les webhooks requis et de servir l'application client.

L'application client utilisera le SDK Twilio Client JS (twilio.js) pour créer un périphérique Twilio.

Le périphérique Twilio désigne notre principal point d'entrée pour passer et recevoir des appels téléphoniques dans le navigateur. Pour établir une connexion entre un périphérique Twilio et les serveurs Twilio, nous devrons générer destokens d'accès (Acces Token) dans notre serveur d'applications.

Lestokens d'accès sont des informations d'identification de courte durée pouvant être distribuées en toute sécurité aux applications côté client qui nous servent à authentifier les SDK Twilio Client tels que Voice, Conversations, Sync et Video. Pour générer destokens d'accès sur notre serveur, nous devrons utiliser une clé API Twilio.

Une clé API Twilio est une information d'identification qui nous permet d'accéder à l'API Twilio. Grâce à une clé API, nous pouvons :

  • Nous authentifier via l'API Twilio
  • Créer et révoquer destokens d'accès

Lestokens d'accès octroieront au client un accès à une Application TwiML (appli TwiML). Twilio s'appuie sur une Application TwiML au sein de notre compte afin de déterminer comment interagir avec notre serveur.

TwiML (Twilio Markup Language, ou langage de balisage de Twilio) est un ensemble d'instructions nous permettant d'indiquer à Twilio quoi faire lorsque nous recevons un appel, un SMS ou un fax entrant.

Créer la structure du projet

Cette section sera consacrée à la création de notre répertoire de projet, dans lequel nous créerons les répertoires standard pour une application Flask. Ensuite, nous créerons et activerons un environnement virtuel. Pour finir, nous installerons les packages Python nécessaires à la construction de cette application Web.

Ouvrez une fenêtre de terminal et saisissez les commandes suivantes :

$ git clone git@github.com:CSFM93/tutorial-twilio-in-browser-calling-start.git twilio-in-browser-calls
$ cd twilio-in-browser-calls

Ici, nous avons cloné un projet de démarrage créé spécialement pour ce tutoriel, que nous avons nommé twilio-in-browser-calls, puis nous nous sommes rendus dans ce répertoire de projet. Ce projet contient le code standard qui nous servira à construire notre application.

On y trouveras les répertoires standard suivants pour une application Flask :

  • static : c'est là que tous les fichiers statiques sont stockés.
  • templates : c'est là que tous les modèles sont stockés.

Le sous-répertoire static contient les éléments suivants :

  • css : c'est là que nous stockerons tous les fichiers CSS. Dans ce sous-répertoire se trouvera un fichier nommé style.css, lequel sera responsable du style de l'interface utilisateur de notre client d'application.
  • images : c'est là que nous stockerons toutes les images. Dans ce sous-répertoire se trouvera un fichier nommé user.png que nous afficherons dans l'interface utilisateur de notre client d'application.
  • js : c'est là que nous stockons tous nos fichiers Javascript.  Dans ce répertoire se trouvera un fichier nommé modals.js. Ce fichier contient le code permettant de gérer les fenêtres modales stockées dans le répertoire templates.

Le sous-répertoire templates comporte trois fichiers : call_in_progress_modal.htmldial_modal.html et incoming_call_modal.html.

Le fichier call_in_progress_modal.html contient le code HTML implémentant une fenêtre modale qui s'affichera pendant un appel. Cette fenêtre modale indique la durée de l'appel en cours, le numéro appelé, ainsi qu'un bouton avec une icône de téléphone rouge permettant de mettre fin à l'appel. Voici à quoi ressemble cette fenêtre modale :

Page d'appel en cours

Le fichier dial_modal.html contient le code HTML implémentant une fenêtre modale qui s'affichera lorsque vous souhaiterez composer un numéro de téléphone. Cette fenêtre modale montre un pavé numérique et un bouton avec une icône de téléphone vert. Voici comment elle se présente :

page de composition de numéro

Le fichier incoming_call_modal.html contient le code HTML d'un modèle implémentant une fenêtre modale qui ne s'affichera que lorsque vous recevrez un appel. Cette fenêtre modale affiche le numéro appelant, ainsi que deux boutons : l'un avec une icône de téléphone vert et l'autre avec une icône de téléphone rouge. Le premier bouton vous permettra d'accepter un appel entrant, le second de le rejeter.

Page d'appel entrant

Dans notre répertoire de travail, créons un environnement virtuel et activons-le. Si vous utilisez un système d'exploitation Unix ou macOS, exécutez pour ce faire les commandes suivantes :

$ python3 -m venv venv
$ source venv/bin/activate

Si vous suivez ce tutoriel sous Windows, exécutez plutôt les commandes suivantes :

$ python -m venv venv
$ venv\Scripts\activate

Maintenant que nous avons créé et activé notre environnement virtuel, nous pouvons installer les librairies dont nous avons besoin pour créer notre application :

$ pip install twilio flask python-dotenv

Dans la commande ci-dessus, nous avons utilisé pip, le programme d'installation du package Python, pour installer les packages qui nous serviront pour ce projet, à savoir :

  • Twilio, un package Python permettant de communiquer avec l'API Twilio.
  • Flask, un micro-framework Python pour la construction d'applications Web. Nous l'utiliserons pour créer un webhook qui permettra d'interagir avec Twilio, et pour créer l'interface utilisateur client qui permettra de passer et recevoir des appels téléphoniques.
  • Python-dotenv, une bibliothèque qui lit des paires clé-valeur à partir d'un fichier et les ajoute en tant que variables d'environnement. Nous utiliserons ce module afin de récupérer nos informations d'identification Twilio stockées dans un fichier de configuration .env.

À titre de référence, les versions des packages ci-dessus ainsi que leurs dépendances testées, étaient les suivantes au moment de la publication de ce post :

certifi==2020.12.5
chardet==4.0.0
click==7.1.2
Flask==1.1.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
PyJWT==1.7.1
python-dotenv==0.17.0
pytz==2021.1
requests==2.25.1
six==1.15.0
twilio==6.55.0
urllib3==1.26.4
Werkzeug==1.0.1

Outre les packages Python mentionnés ci-dessus, nous utiliserons les librairies front-end suivantes :

  • Bootstrap, un puissant framework front-end utilisé pour créer des sites Web modernes.
  • Le SDK Twilio Client JS (twilio.js), une librairie permettant de passer des appels vocaux à partir d'un navigateur Web.
  • JQuery, une bibliothèque JavaScript rapide, légère et riche en fonctionnalités qui simplifie considérablement la traversée et la manipulation de code HTML, la gestion des événements, l'animation, ainsi que l'utilisation d'Ajax. Nous l'utiliserons à des fins de manipulation du DOM et de gestion des événements.
  • Font Awesome, une boîte à outils populaire alliant SVG, polices et CSS. Nous utiliserons certaines des icônes qu'ils ont conçues dans notre client d'application.

Créer une application TwiML

Dans cette section, nous utiliserons la Console Twilio afin de créer une « App TwiML », dans laquelle nous stockerons par la suite l'URL du webhook que nous construirons.

Ouvrez une nouvelle fenêtre de navigateur et rendez-vous sur Twilio account Console > Voice > TwiML > TwiML Apps (Console de compte Twilio > Voix > TwiML > App TwiML). Cliquez sur le bouton Create new TwiML App (Créer une nouvelle appli TwiM) ou sur l'icône rouge « + » si vous avez déjà d'autres applis TwiML.

App TwiML

Saisissez le nom de votre appli TwiML dans le champ Friendly Name (Nom convivial), par exemple in-browser calls. Laissez les autres champs vides pour le moment. Cliquez sur le bouton Create (Créer) pour créer l'application TwiML.

Page appli TwiML

Vous serez redirigé vers le tableau de bord des applis TwiML. Cliquez sur l'appli TwiML que vous venez de créer. Sur la page de cette appli, sélectionnez la valeur du SID et copiez-la dans le presse-papiers.

Page appli TwiML

Dans le répertoire racine du projet, créez un fichier nommé .env et insérez-y le contenu suivant :

TWIML_APP_SID="collez votre TwiML app SID ici"

Créer une clé API Twilio

Pour l'étape suivante, nous allons créer une clé API Twilio pour l'API Voice. La clé API sera utilisée pour générer destokens d'accès, lesquels permettront au front-end exécuté dans le navigateur de passer des appels vers les API Twilio.

Accédez à Twilio console > Voice > Settings > API Keys (Console Twilio > Voix > Paramètres > Clés API) dans votre navigateur. Cliquez sur le bouton Create new API Key (Créer une nouvelle clé API) ou sur l'icône rouge « + » si vous disposez déjà d'autres clés API.

Clés API Twilio

Saisissez le nom de votre clé API dans le champ Friendly Name (Nom convivial), par exemple, in-browser calls. Laissez le Key Type (Type de clé) défini sur « Standard ». Cliquez sur le bouton Create API Key (Créer une clé API) afin de la créer.

Nouvelle clé API

Vous serez redirigé vers une page où vous trouverez des informations sur votre nouvelle clé API. Copiez les valeurs « SID » et « Secret » et collez-les dans votre fichier .env sous TWILIO_API_KEY_SID et TWILIO_API_KEY_SECRET. Votre fichier .env devrait maintenant se présenter comme suit :

TWIML_APP_SID="collez votre TwiML app SID ici"
TWILIO_API_KEY_SID="collez votre API key SID ici"
TWILIO_API_KEY_SECRET="collez votre API key secret ici"

Cochez la case Got it! (Compris !), puis cliquez sur le bouton Done (Terminé).

Clé API créée

Rendez-vous maintenant sur la page d'accueil de la Console Twilio et copiez la valeur SID du compte (Account SID) Twilio dans le fichier .env comme suit :

TWIML_APP_SID="collez votre TwiML app SID ici"
TWILIO_API_KEY_SID="collez votre API key SID ici"
TWILIO_API_KEY_SECRET="collez votre API key secret ici"
TWILIO_ACCOUNT_SID="collez votre Twilio Account SID ici"

Ensuite, rendez-vous dans Twilio account console > Phone Numbers > Manage Numbers > Active Numbers (Console de compte Twilio > Numéros de téléphone > Gérer les numéros > Numéros actifs), sélectionnez le numéro que vous avez acheté pour ce tutoriel et vous serez redirigé vers une page où vous pourrez configurer ce numéro. Localisez le champ Phone Number (Numéro de téléphone), copiez le numéro de téléphone qui s'affiche sous ce champ et collez-le dans le fichier .env sous TWILIO_NUMBER. Supprimez les espaces entre les chiffres, mais laissez le signe « + » du début afin de vous assurer que le numéro est au format E.164.

Numéro de téléphone Twilio

Une fois le numéro de téléphone ajouté, votre fichier .env devrait ressembler à ceci :

TWIML_APP_SID="collez votre TwiML app SID ici"
TWILIO_API_KEY_SID="collez votre API key SID ici"
TWILIO_API_KEY_SECRET="collez votre API key secret ici"
TWILIO_ACCOUNT_SID="collez votre Twilio Account SID ici"
TWILIO_NUMBER="collez votre Twilio phone number ici"

Créer l'application Flask

Dans cette section, nous allons créer la logique de notre application Flask, laquelle fournira les fonctions d'assistance nécessaires au front-end pour passer et recevoir des appels téléphoniques.

Créer le serveur d'applications

Dans cette sous-section, nous allons créer les points de terminaison nécessaires pour passer et recevoir des appels téléphoniques. Nous devrons créer les points de terminaison suivants :

  • / : ce point de terminaison aura pour responsabilité de servir l'interface utilisateur de l'application (client).
  • /token : ce point de terminaison aura pour responsabilité de générer et renvoyer lestokens d'accès au client.
  • /handle_calls : ce point de terminaison aura pour responsabilité de générer les instructions TwiML nécessaires pour passer et recevoir des appels téléphoniques.

Dans le répertoire racine de votre projet, créez un fichier nommé main.py. Ouvrez-le à l'aide de votre éditeur de texte favori, puis ajoutez-y le code suivant :

from flask import Flask, render_template, jsonify
from flask import request
 
from twilio.jwt.access_token import AccessToken
from twilio.jwt.access_token.grants import VoiceGrant
from twilio.twiml.voice_response import VoiceResponse, Dial
 
from dotenv import load_dotenv
import os
import pprint as p

Ici, vous avez importé tous les packages dont vous allez avoir besoin pour construire votre application de serveur :

  • flask sera utilisé pour définir les points de terminaison de l'application.
  • Le package twilio sera utilisé pour interagir avec l'API Twilio, ce qui nous permettra de passer et de recevoir des appels téléphoniques via le périphérique Twilio qui sera créé dans le client.
  • load_dotenv servira à importer les informations d'identification du compte Twilio à partir du fichier .env.
  • pprint sera utilisé afin de formater et imprimer les données reçues lorsque Twilio envoie une demande au point de terminaison /handle_calls pour notifier qu'un appel est en cours.
  • os sera utilisé conjointement avec load_dotenv pour récupérer les informations d'identification stockées dans le fichier .env.

Ajoutez le code suivant à la fin du fichier main.py :

load_dotenv()

account_sid = os.environ['TWILIO_ACCOUNT_SID']
api_key = os.environ['TWILIO_API_KEY_SID']
api_key_secret = os.environ['TWILIO_API_KEY_SECRET']
twiml_app_sid = os.environ['TWIML_APP_SID']
twilio_number = os.environ['TWILIO_NUMBER']

app = Flask(__name__)


@app.route('/')
def home():
    return render_template(
        'home.html',
        title="In browser calls",
    )

L'application commence par importer les variables d'environnement stockées dans le fichier .env en appelant load_dotenv(). Cela vous permet de récupérer les cinq variables de configuration dont vous avez besoin pour cette application.

Nous créons ensuite une instance d'application Flask dans une variable nommée app, après quoi nous créons le point de terminaison / de notre application. Cette route sert un modèle nommé home.html que nous créerons ultérieurement.

Ajoutez le code suivant sous la route / :

@app.route('/token', methods=['GET'])
def get_token():
    identity = twilio_number
    outgoing_application_sid = twiml_app_sid

    access_token = AccessToken(account_sid, api_key,
                               api_key_secret, identity=identity)

    voice_grant = VoiceGrant(
        outgoing_application_sid=outgoing_application_sid,
        incoming_allow=True,
    )
    access_token.add_grant(voice_grant)

    response = jsonify(
        {'token': access_token.to_jwt().decode(), 'identity': identity})

    return response

Cela ajoute le point de terminaison /token, lequel sera appelé par le client en vue de la demande d'un token d'accès.

Lorsque ce point de terminaison est déclenché, nous créons une variable nommée identity et nous lui attribuons notre numéro Twilio. Une identité est propre à un utilisateur et peut être connectée simultanément sur plusieurs périphériques. Sur un serveur d'applications destiné à être utilisé par plusieurs utilisateurs, nous devrions décider, en fonction de la demande de token qui nous serait envoyée, de l'identité de l'utilisateur et de ce qu'il serait autorisé à faire. Pour connaître l'utilisateur (son identité), nous utiliserions notre système de connexion ou fournisseur d'identité existant (par exemple, les cookies de session, un token API ou tout autre mécanisme servant à sécuriser les demandes API). Dans ce tutoriel, toutefois, comme nous sommes le seul utilisateur, nous n'avons pas besoin de travailler avec plusieurs identités, et le numéro Twilio que nous avons acheté pour l'occasion fonctionne bien à cet effet.

Nous utilisons ensuite les valeurs account_sidapi_keyapi_key_secret et identity afin de créer un token d'accès. Le Token doit être provisionné à l'aide d'autorisations, lesquelles déterminent les opérations que le client porteur du token a le droit d'effectuer. Pour cette application, nous créons un objet d'autorisation vocale configuré à l'aide du sid de l'appli TwiML créée précédemment.

Pour terminer le point de terminaison, nous renvoyons au client les valeurs access_token et identity au format JSON.

Ajoutez le code suivant sous la route /token :

@app.route('/handle_calls', methods=['POST'])
def call():
    p.pprint(request.form)
    response = VoiceResponse()
    dial = Dial(callerId=twilio_number)

    if 'To' in request.form and request.form['To'] != twilio_number:
        print('outbound call')
        dial.number(request.form['To'])
        return str(response.append(dial))

    return ''


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=3000, debug=True)

Ce bloc ajoute un point de terminaison nommé /handle_calls. Ce point de terminaison sera appelé par Twilio à chaque fois que nous passerons ou recevrons un appel téléphonique.

Lorsque ce point de terminaison est déclenché, nous utilisons pprint pour afficher le contenu de request.form, puis créons un objet de réponse TwiML et un objet de numérotation TwiML. Dans l'objet dial, nous définissons le callerId sur le numéro Twilio que nous avons acheté pour ce tutoriel. Ainsi, lorsque nous appellerons un numéro de téléphone à l'aide de cette application, le téléphone du destinataire affichera ce numéro au lieu de anonymous.

Ensuite, nous utilisons une logique conditionnelle afin de vérifier si l'objet request.form possède la propriété nommée To, et le cas échéant, que cette valeur de propriété n'est pas identique à notre twilio_number. Ce test nous permet de nous assurer que l'appel du point de terminaison était bien destiné à passer un appel (et non à en recevoir un), ce qui correspond au premier cas que nous allons traiter.

Une fois que nous sommes sûrs que cette demande est destinée à passer un appel, nous définissons le numéro que nous voulons composer sur la valeur de request.form['To'], ajoutons l'objet dial à l'objet response, puis renvoyons l'objet response sous la forme d'une chaîne à Twilio, qui exécutera ces instructions et composera le numéro demandé.

La partie inférieure du script est un opérateur conditionnel standard qui exécute le serveur de développement Flask sur le port 3000 lorsque le script est appelé à partir de la ligne de commande.

Créer le client d'application

Dans cette sous-section, nous créerons le front-end qui nous permettra de passer et recevoir des appels téléphoniques dans le navigateur.

Créer la page d'accueil

Créez un fichier nommé home.html dans le répertoire templates. Ouvrez-le, puis ajoutez-y le code suivant :

<!DOCTYPE html>
<html>
<head>
    <title>In browser calls</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" />
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <div class="container">
        <!-- log output -->
        <div class="card text-center log-container">
            <h3>Device log</h3>
            <div id="log"></div>
            <div class="btn-container">
                <button type="button" id="btnOpenNumberPad" class="btn btn-default btn-circle btn-lg">
                    <i class="fa fa-phone fa-flip-horizontal " aria-hidden="true" style="color: green;"></i>
                </button>
            </div>
        </div>

        <!-- Modal appeler -->
        {% include 'dial_modal.html' %}

        <!-- Modal appel en cours -->
        {% include 'call_in_progress_modal.html' %}

        <!-- Modal appel entrant -->
        {% include 'incoming_call_modal.html' %}


        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://media.twiliocdn.com/sdk/js/client/v1.8/twilio.min.js"></script>
        <script type="text/javascript" src="/static/js/main.js"></script>
        <script type="text/javascript" src="/static/js/modals.js" defer></script>
</body>

</html>

Ce modèle implémente une page qui affiche une console depuis laquelle nous pouvons surveiller l'état du périphérique Twilio que nous créerons sous peu à l'aide de JavaScript. Nous avons inclus dans ce modèle les modèles call_in_progress_modal.htmldial_modal.html et incoming_call_modal.html qui étaient fournis avec le code standard de ce projet. Cette page affiche également un bouton avec une icône de téléphone. À chaque fois que nous appuierons sur ce bouton, une fenêtre modale s'ouvrira, dans laquelle nous saisirons le numéro que nous souhaitons appeler.

Outre les fichiers Bootstrap, jQuery, FontAwesome et twilio.js, voyez comment nous incluons dans ce modèle les fichiers suivants :

  • style.css : ce fichier contient certaines des CSS utilisées pour créer le style de notre application;
  • main.js : ce fichier javascript contient le code permettant de créer un périphérique Twilio et de le connecter à notre application TwiML.
  • modals.js : ce fichier javascript contient le code permettant de gérer les fenêtres modales de notre application.

Les fichiers style.css et modals.js étaient fournis dans le répertoire standard du projet. Nous allons créer le fichier main.js dans la sous-section suivante.

Créer le périphérique Twilio

Créez un fichier nommé main.js dans le répertoire static/js. Ouvrez-le, puis ajoutez-y le code suivant :

$(function () {
    var device;

    log("Requesting Access Token...");
    // Using a relative link to access the Voice Token function
    $.getJSON("./token")
        .then(function (data) {
            log("Got a token.");
            console.log("Token: " + data.token);

            // Setup Twilio.Device
            device = new Twilio.Device(data.token, {
                // Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
                // providing better audio quality in restrained network conditions. Opus will be default in 2.0.
                codecPreferences: ["opus", "pcmu"],
                // Use fake DTMF tones client-side. Real tones are still sent to the other end of the call,
                // but the client-side DTMF tones are fake. This prevents the local mic capturing the DTMF tone
                // a second time and sending the tone twice. This will be default in 2.0.
                fakeLocalDTMF: true,
                // Use `enableRingingState` to enable the device to emit the `ringing`
                // state. The TwiML backend also needs to have the attribute
                // `answerOnBridge` also set to true in the `Dial` verb. This option
                // changes the behavior of the SDK to consider a call `ringing` starting
                // from the connection to the TwiML backend to when the recipient of
                // the `Dial` verb answers.
                enableRingingState: true,
                debug: true,
            });

            device.on("ready", function (device) {
                log("Twilio.Device Ready!");
            });

            device.on("error", function (error) {
                log("Twilio.Device Error: " + error.message);
            });

            device.on("connect", function (conn) {
                log('Successfully established call ! ');
                $('#modal-call-in-progress').modal('show')
            });

            device.on("disconnect", function (conn) {
                log("Call ended.");
                $('.modal').modal('hide')
            });

        })
        .catch(function (err) {
            console.log(err);
            log("Could not get a token from server!");
        });

    // Bind button to make call
    $('#btnDial').bind('click', function () {
        $('#modal-dial').modal('hide')

        // get the phone number to connect the call to
        var params = {
            To: document.getElementById("phoneNumber").value
        };

        // output destination number
        $("#txtPhoneNumber").text(params.To)
        

        console.log("Calling " + params.To + "...");
        if (device) {
            var outgoingConnection = device.connect(params);
            outgoingConnection.on("ringing", function () {
                log("Ringing...");
            });
        }

    })

    // Bind button to hangup call

    $('.btnHangUp').bind('click', function () {
        $('.modal').modal('hide')
        log("Hanging up...");
        if (device) {
            device.disconnectAll();
        }
    })

    // Activity log
    function log(message) {
        var logDiv = document.getElementById("log");
        logDiv.innerHTML += "<p>&gt;&nbsp;" + message + "</p>";
        logDiv.scrollTop = logDiv.scrollHeight;
    }

});

Nous créons ici le périphérique Twilio qui nous permettra de passer et recevoir des appels téléphoniques dans le navigateur.

Tout d'abord, nous utilisons la fonction getJSON() fournie par jQuery pour envoyer une demande GET au point de terminaison /token de notre serveur d'applications et récupérer un token d'accès. Après avoir récupéré le token, nous utilisons twilio.js et le token pour créer un périphérique Twilio et le connecter à notre application TwiML.

Une fois le périphérique Twilio créé, nous lui ajoutons quelques écouteurs d'événements et du code qui nous permettra d'interagir avec lui en utilisant l'interface utilisateur.

Passer des appels téléphoniques sortants

Dans cette section, nous allons utiliser notre application pour passer des appels téléphoniques sortants. Pour ce faire, nous devons toutefois exécuter l'application et configurer ngrok ainsi que notre appli TwiML.

Ouvrez une deuxième fenêtre de terminal dans notre répertoire de projet, activez l'environnement virtuel Python et démarrez l'application en exécutant la commande suivante :

$ python main.py

Open another terminal window and start ngrok on it:

$ ngrok http 3000

Après avoir exécuté la commande ci-dessus, vous devriez voir quelque chose qui ressemble à ceci :

Sortie ngrok

Copiez l'URL ngrok https dans le presse-papiers. Rendez-vous ensuite sur votre tableau de bord Twilio account Console > Voice> TwiML > TwiML Apps (Console de compte Twilio > Voix > TwiML > App TwiML) et sélectionnez l'appli TwiML que vous avez créée pour ce tutoriel.

Configuration du webhook vocal dans l'appli TwiML

Localisez la section Voice (Voix) de la configuration de l'appli TwiML et collez-y l'URL https:// fournie par ngrok, suivie de /handle_calls dans le champ Request URL (URL de demande), puis cliquez sur le bouton Save (Enregistrer). Cela a pour effet de créer un webhook qui connectera votre application à l'appli TwiML.

Dans cet exemple, l'URL ngrok est https://48dcc810632b.ngrok.io/handle_calls. La première partie de l'URL sera différente à chaque lancement de ngrok.

Connectez maintenant à votre ordinateur un casque équipé d'un microphone, puis ouvrez votre navigateur et tapez http://localhost:3000/ dans la barre d'adresse. Vous devriez voir quelque chose qui ressemble à ceci :

Page d'application initiale

Dès que vous voyez un message indiquant Twilio.Device Ready!, votre périphérique Twilio fonctionne comme prévu. Cliquez sur le bouton avec l'icône de téléphone vert pour afficher la fenêtre modale de numérotation :

Dialer téléphonique

Utilisez les numéros du pavé numérique pour insérer le numéro que vous souhaitez appeler, ou saisissez-le simplement à l'aide de votre clavier dans le champ d'insertion au-dessus du pavé numérique. Quand vous êtes prêt, cliquez sur le téléphone vert pour passer l'appel.

Une fois que vous aurez cliqué sur ce bouton, votre navigateur vous demandera l'autorisation d'utiliser le microphone. Accordez-lui cette autorisation. Dès que le numéro que vous appelez répond, la fenêtre modale d'appel en cours s'affiche :

Page d'appel en cours

En accédant au terminal qui exécute notre application Flask, les données de demande que Twilio envoie à notre point de terminaison /handle_calls ressembleront à ceci :

{'AccountSid': 'ACe6c069eb8828c025719e6bbb20d63c75',
 'ApiVersion': '2010-04-01',
 'ApplicationSid': 'AP596a0557d23b118259f2cf355fb3693a',
 'CallSid': 'CAc8356bdd69dec58624588f7d578e5668',
 'CallStatus': 'ringing',
 'Called': '',
 'Caller': 'client:+1xxxxxxxxxx',
 'Direction': 'inbound',
 'From': 'client:+1xxxxxxxxxx',
 'To': '+1xxxxxxxxxx'}
outbound call

La valeur de la propriété To (le numéro que nous avons appelé) correspondant à notre numéro Twilio, c'est le code figurant dans l'instruction if du point de terminaison handle_calls qui a été exécuté.

Répondre aux appels téléphoniques entrants

Dans la section précédente, vous avez pu utiliser votre application pour passer des appels téléphoniques, mais à ce stade, l'application ne peut pas en recevoir. Pour pouvoir recevoir des appels, nous devrons ajouter du code supplémentaire à main.py, main.js et home.html, mais également configurer le numéro que nous avons acheté pour ce tutoriel dans la console Twilio afin qu'il permette de recevoir des appels téléphoniques.

Revenez au fichier main.py et remplacez le code du point de terminaison /handle_calls par le code suivant :

@app.route('/handle_calls', methods=['POST'])
def call():
    p.pprint(request.form)
    response = VoiceResponse()
    dial = Dial(callerId=twilio_number)

    if 'To' in request.form and request.form['To'] != twilio_number:
        print('outbound call')
        dial.number(request.form['To'])
    else:
        print('incoming call')
        caller = request.form['Caller']
        dial = Dial(callerId=caller)
        dial.client(twilio_number)

    return str(response.append(dial))

Nous avons ajouté ici l'instruction else au point de terminaison /handle_calls. Le code de cette partie sera exécuté si le numéro recevant l'appel est celui que nous avons acheté pour ce tutoriel, ce qui signifie que nous avons un appel entrant.

Définissons maintenant la valeur callerId dans l'objet de numérotation TwiML sur la valeur de la propriété Caller dans request.form. Comme son nom l'indique, Caller est le numéro qui appelle notre numéro Twilio. Cela nous permet de voir qui nous appelle dans l'interface utilisateur de notre application. Il nous faut également définir le client dans l'objet de numérotation sur la valeur identity que nous avons utilisée lors de la création d'un token d'accès dans le point de terminaison /token.

Pour terminer le flux relatif aux appels entrants, ajoutons l'objet de numérotation à l'objet de réponse TwiML, puis renvoyons cet objet de réponse sous la forme d'une chaîne.

Revenez à votre fichier main.js et ajoutez le code suivant sous le listener device.on('Disconnect') :

device.on("incoming", function (conn) {
    console.log(conn.parameters)
    log("Incoming connection from " + conn.parameters.From);
    $("#callerNumber").text(conn.parameters.From)
    $("#txtPhoneNumber").text(conn.parameters.From)

    $('#modal-incomming-call').modal('show')

    $('.btnReject').bind('click', function () {
        $('.modal').modal('hide')
        log("Rejected call ...");
        conn.reject();
    })

    $('.btnAcceptCall').bind('click', function () {
        $('.modal').modal('hide')
        log("Accepted call ...");
        conn.accept();
    })

});

Nous avons ajouté ici un listener d'événements qui autorisera votre périphérique Twilio à surveiller les appels entrants et à afficher la fenêtre modale d'appel entrant à chaque fois qu'il en détectera un.

Accédez à votre tableau de bord Twilio console > Phone Numbers > Manage Numbers > Active Numbers (Console Twilio > Numéros de téléphone > Gérer les numéros > Numéros actifs) et sélectionnez le numéro que vous avez acheté pour ce tutoriel.

Localisez la section Voice & Fax (Voix et fax) de la configuration du numéro de téléphone et sélectionnez TwiML App (App TwiML) dans le champ Configure With (Configurer avec). Ensuite, sélectionnez le nom de l'appli TwiML que vous avez créée pour ce tutoriel dans le champ TwiML App (App TwiML). Le numéro Twilio que vous avez acheté pour ce tutoriel sera alors lié à l'appli TwiML que vous avez créée. Ainsi, à chaque fois que ce numéro recevra un appel téléphonique, il récupérera l'URL du webhook et d'autres configurations dans l'appli TwiML et les utilisera pour répondre à l'appel.  Dans notre cas, il enverra à https://48dcc810632b.ngrok.io/handle_calls une demande POST contenant le numéro de l'appelant et d'autres informations utiles.

Retournez dans votre navigateur, accédez à http://localhost:3000/ et attendez que le message Twilio.Device Ready! s'affiche sur la page, puis utilisez un appareil capable de passer des appels téléphoniques pour appeler le numéro Twilio que vous avez acheté pour ce tutoriel. Dès que ça sonne, vous devriez voir la fenêtre modale d'appel entrant :

Page d'appel entrant

Appuyez sur le bouton avec l'icône de téléphone vert pour accepter l'appel, ou sur celui avec l'icône de téléphone rouge pour le rejeter.

En accédant au terminal qui exécute notre application Flask, les données de demande que Twilio envoie à notre point de terminaison /handle_calls ressembleront à ceci :

{'AccountSid': 'ACe6c069eb8828c025719e6bbb20d63c75',
 'ApiVersion': '2010-04-01',
 'ApplicationSid': 'AP596a0557d23b118259f2cf355fb3693a',
 'CallSid': 'CAf0eb0489e54978471b96dd258dff4de9',
 'CallStatus': 'ringing',
 'Called': '+17146134152',
 'CalledCity': 'SILVERADO',
 'CalledCountry': 'US',
 'CalledState': 'CA',
 'CalledZip': '92676',
 'Caller': '+17205752613',
 'CallerCity': '',
 'CallerCountry': 'US',
 'CallerState': 'CO',
 'CallerZip': '',
 'Direction': 'inbound',
 'From': '+17205752613',
 'FromCity': '',
 'FromCountry': 'US',
 'FromState': 'CO',
 'FromZip': '',
 'To': '+17146134152',
 'ToCity': 'SILVERADO',
 'ToCountry': 'US',
 'ToState': 'CA',
 'ToZip': '92676'}
incoming call

Conclusion

Dans ce tutoriel, nous avons appris à utiliser l'API Twilio Voice pour passer et recevoir des appels téléphoniques dans le navigateur. Nous avons appris à utiliser le framework Flask pour construire le client d'application qui nous a permis d'interagir avec un périphérique Twilio créé avec le SDK JS client de Twilio.

Le code de l'ensemble de l'application est disponible dans le répertoire suivant : https://github.com/CSFM93/twilio-in-browser-calls.

Carlos Mucuho est un ex-géologue mozambicain devenu développeur qui aime utiliser la programmation pour donner vie à ses idées. https://github.com/CSFM93