Passez et recevez des appels téléphoniques à partir du navigateur avec Twilio Programmable Voice, Python et JavaScript

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

Dans ce tutoriel, nous allons écrire une application qui utilise l'API Programmable Voice de Twilio 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 débutez avec 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 bon d'achat de 10 $.
  • Un numéro de téléphone Twilio permettant de passer et de recevoir des appels téléphoniques. Obtenez-en un dès maintenant si vous n'en avez pas.
  • Python 3.6+ installé.
  • ngrok installé. ngrok est un service de Proxy inverse qui crée un tunnel sécurisé d'un endpoint 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 des access tokens dans notre serveur d'applications.

Les access tokens 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 des tokens 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 des tokens d'accès

Les access tokens octroieront au client un accès à une application TwiML (app 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 contient trois fichiers : call_in_progress_modal.html, dial_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 :

  • Twilio, un package Python permettant de communiquer avec l'API Twilio.
  • Flask, un micro-cadre 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 librairie 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 cadre front-end utilisé pour créer des sites Web modernes.
  • Le SDK Twilio Client JS  (twilio.js), une librairie qui vous permet de passer des appels vocaux à partir d'un navigateur Web.
  • JQuery, une librairie 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 > Voice > 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 app 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="paste your TwiML app SID here"

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 des tokens 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="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"

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="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"
TWILIO_ACCOUNT_SID="paste your Twilio Account SID here"

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="paste your TwiML app SID here"
TWILIO_API_KEY_SID="paste your API key SID here"
TWILIO_API_KEY_SECRET="paste your API key secret here"
TWILIO_ACCOUNT_SID="paste your Twilio Account SID here"
TWILIO_NUMBER="paste your Twilio phone number here"

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 :

  • / : cet endpoint aura pour responsabilité de servir l'interface utilisateur de l'application (client).
  • /token : cet endpoint aura pour responsabilité de générer et renvoyer les access tokens au client.
  • /handle_calls : cet endpoint 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 endpoints 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 identifiants de notre 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 endpoint /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 avons créé une instance d'application Flask dans une variable nommée app, après quoi nous avons créé l'endpoint / de notre application. Ce routage sert un modèle nommé home.html que nous créerons ultérieurement.

Ajoutez le code suivant sous le routage / :

@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 l'endpoint /token, lequel sera appelé par le client en vue de la demande d'un access token.

Lorsque cet endpoint 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 devons décider, en fonction de la demande de token qui nous sera envoyée, qui est l'utilisateur et ce qu'il est autorisé à faire. Pour déterminer qui est l'utilisateur (son identité), nous utilisons notre système de connexion ou fournisseur d'identité existant (par exemple, des cookies de session, un token API ou tout autre mécanisme utilisé pour sécuriser vos demandes d'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 account_sid, api_key, api_key_secret et identity pour créer un access token. 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é avec le sid de l'app twiML créée précédemment.

Pour terminer l'endpoint, nous renvoyons le access_token et l'identity au client au format JSON.

Ajoutez le code suivant sous le routage /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 endpoint nommé /handle_calls. Ce point de terminaison sera appelé par Twilio à chaque fois que nous passerons ou recevrons un appel téléphonique.

Lorsque cet endpoint est déclenché, nous utilisons pprint pour imprimer le contenu du request.form, puis nous créons un objet de réponse TwiML et un objet de numérotation TwiML. Dans l'objet dial, nous avons défini le callerId sur le numéro Twilio que nous avons acheté pour ce tutoriel. De cette manière, lorsque nous appelons 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 pour vérifier si l'objet request.form possède la propriété nommée To, et si cette valeur de propriété n'est pas la même que 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 qu'il s'agit d'une demande d'appel, nous définissons le numéro que nous voulons composer sur la valeur dans request.form['To'], nous ajoutons l'objet dial à l'objet response, et nous renvoyons l'objet response sous forme de chaîne à Twilio, qui exécute ces instructions et compose 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 dial -->
        {% include 'dial_modal.html' %}

        <!-- Modal call in progress -->
        {% include 'call_in_progress_modal.html' %}

        <!-- Modal incoming call -->
        {% 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.html, dial_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 se trouvent dans le référentiel de code 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 à l'endpoint /token de notre serveur d'applications et récupérer un access token. 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:

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 > Voice > TwiML > App TwiML) et sélectionnez l'app TwiML que vous avez créée pour ce tutoriel.

Configuration du webhook vocal dans l'appli TwiML

Localisez la section « Voice » de la configuration de l'app TwiML et collez 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.

Ensuite, connectez un casque équipé d'un microphone à votre ordinateur. Ouvrez votre navigateur et saisissez http://localhost:3000/ dans la barre d'adresse. Vous devriez voir quelque chose qui ressemble à ceci :

 

Page d'application initiale

Lorsque vous voyez le message Twilio.Device Ready!, votre périphérique Twilio fonctionne normalement. 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

Accédez au terminal exécutant notre application Flask. Les données de demande envoyées par Twilio à notre endpoint /handle_calls ressembleront à ce qui suit :

{'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

Étant donné que la valeur de la propriété To (le numéro que nous avons appelé) n'est pas égale à notre numéro Twilio, le code à l'intérieur de l'instruction if de l'endpoint handle_calls s'est 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 de l'endpoint /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))

Ici, nous avons ajouté l'instruction else à l'endpoint /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.

Nous allons définir le 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 access token dans l'endpoint /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.

Revenez dans votre navigateur et accédez à http://localhost:3000/, attendez que le message Twilio.Device Ready! s'affiche sur la page, puis utilisez un périphérique 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.

Accédez au terminal exécutant notre application Flask. Les données de demande envoyées par Twilio à notre endpoint /handle_calls ressembleront à ce qui suit :

{'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