Realiza y recibe llamadas telefónicas desde el navegador con Twilio Programmable Voice, Python y JavaScript.

April 21, 2021
Redactado por
Carlos Mucuho
Colaborador
Las opiniones expresadas por los colaboradores de Twilio son propias.

En este tutorial, escribiremos una aplicación que utiliza la API de voz programable de Twilio para realizar y recibir llamadas telefónicas desde un navegador web. También implementaremos una IU que nos permita realizar, aceptar y rechazar llamadas telefónicas.

Al final de este tutorial, tendremos una aplicación que se parecerá a la siguiente:

Demostración del proyecto

Requisitos del tutorial

Para seguir este tutorial, necesitarás los siguientes componentes:

  • Una cuenta de Twilio gratuita o pagada. Si eres nuevo en Twilio, crea una cuenta gratuita ahora. Si creas tu cuenta con este enlace y, luego la actualizas a una cuenta pagada, recibirás USD 10 en crédito.
  • Un número de teléfono de Twilio que pueda realizar y recibir llamadas. Obtén uno ahora si no lo tienes.
  • Tener Python 3.6+ instalado.
  • Tener ngrok instalado. ngrok es un servicio de proxy inverso que crea un túnel seguro desde un punto final público hacia un servicio web que se ejecuta localmente. Tendremos que utilizar ngrok para crear una URL segura que permita que Twilio se conecte a nuestra aplicación.
  • Un teléfono celular o teléfono capaz de realizar y recibir llamadas telefónicas para probar el proyecto.

Conceptos básicos y lógica de la aplicación

El objetivo de este tutorial es crear una aplicación web que nos permita realizar y recibir llamadas telefónicas en el navegador. Utilizaremos la estructura Flask para implementar los webhooks necesarios y poner en funcionamiento la aplicación del cliente.

El cliente de la aplicación utilizará el SDK de Twilio Client JS (twilio.js) para crear un dispositivo de Twilio.

Un dispositivo de Twilio es nuestro principal punto de entrada para realizar y recibir llamadas telefónicas en el navegador. Para configurar una conexión entre un dispositivo de Twilio y los servidores de Twilio, necesitaremos generar tokens de acceso en nuestro servidor de aplicaciones.

Los tokens de acceso son credenciales de corta duración que se pueden distribuir de forma segura a las aplicaciones del lado del cliente que podemos utilizar para autenticar SDK de Twilio Client como Voice (Voz), Conversations (Conversaciones), Sync (Sincronización) y Video (Video). Para generar tokens de acceso en nuestro servidor, tendremos que utilizar una clave de la API de Twilio.

Una clave de la API de Twilio es una credencial que nos otorga acceso a la API de Twilio. Una clave de API nos permite:

  • autenticar con la API de Twilio;
  • crear y revocar tokens de acceso.

Los tokens de acceso otorgarán al cliente acceso a una aplicación de TwiML . Twilio se basa en una aplicación de TwiML dentro de nuestra cuenta para determinar cómo interactuar con nuestro servidor.

TwiML  (Twilio Markup Language) es un conjunto de instrucciones que se pueden utilizar para decirle a Twilio lo que debe hacer cuando recibe una llamada entrante, un SMS o un fax.

Creación de la estructura del proyecto

En esta sección, crearemos nuestro directorio de proyectos y, dentro de este, crearemos los directorios estándar para una aplicación de Flask. Después de eso, crearemos y activaremos un entorno virtual. Por último, instalaremos los paquetes Python necesarios para crear esta aplicación web.

Abre una ventana de tu terminal y ejecuta los siguientes comandos:

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

Aquí clonamos un proyecto de inicio que se creó para este tutorial con el nombre twilio-in-browser-calls. Después de eso nos dirigimos a este directorio de proyectos. Este proyecto contiene el código base que usaremos para construir nuestra aplicación.

Encontrarás los siguientes directorios estándar para una aplicación de Flask en:

  • static: aquí es donde se almacenan todos los archivos estáticos.
  • templates: aquí es donde se almacenan todas las plantillas.

El subdirectorio static tiene los siguientes contenidos:

  • css: aquí es donde almacenaremos todos los archivos CSS. Dentro de este subdirectorio encontrarás un archivo llamado style.css. Este archivo será responsable de estilizar la IU para los clientes de nuestra aplicación.
  • images: aquí es donde almacenaremos todas las imágenes. Dentro de este subdirectorio, encontrarás un archivo llamado user.png, que se mostrará en nuestra IU para los clientes de nuestra aplicación.
  • js:  aquí es donde almacenamos todos nuestros archivos Javascript.  Dentro de este directorio, encontrarás un archivo con el nombre modals.js. Este archivo contiene el código para administrar los modales almacenados en el directorio templates.

El subdirectorio templates tiene tres archivos: call_in_progress_modal.html, dial_modal.html y incoming_call_modal.html.

El archivo call_in_progress_modal.html contiene el código HTML que implementa un modal que se mostrará durante una llamada. Este modal muestra la duración de la llamada en curso, el número al que estás llamando y un botón con un ícono de teléfono rojo que te permitirá finalizar la llamada. Así es como se ve este modal:

 

Página de llamada en curso

El archivo dial_modal.html contiene el código HTML que implementa un modal que aparecerá cuando desees marcar un número de teléfono. Este modal muestra un teclado numérico y un botón con un ícono de teléfono verde. Así es como se ve:

Página para marcar un número

El archivo incoming_call_modal.html contiene el código HTML de una plantilla que implementa un modal que solo aparecerá cuando recibes una llamada. Este modal muestra el número que te llama y dos botones, un botón con un ícono de teléfono verde y otro con un ícono de teléfono rojo. El primer botón te permitirá aceptar una llamada entrante y el segundo, rechazarla.

Página de llamada entrante

Dentro de nuestra carpeta de trabajo, crea un entorno virtual y actívalo. Si utilizas un sistema operativo Unix o Mac, introduce los siguientes comandos para hacerlo:

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

Si estás siguiendo este tutorial desde Windows, ingresa los siguientes comandos:

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

Ahora que creamos y activamos nuestro entorno virtual, podemos instalar las bibliotecas que necesitamos para crear nuestra aplicación:

$ pip install twilio flask python-dotenv

En el comando anterior, utilizamos pip, el instalador de paquetes de Python para instalar los siguientes paquetes que utilizaremos en este proyecto:

  • Twilio, un paquete de Python para la comunicación con la API de Twilio.
  • Flask, una microestructura de Python para construir aplicaciones web. La usaremos con el fin de crear un webhook para interactuar con Twilio y para crear la IU del cliente para realizar y recibir llamadas telefónicas.
  • Python-dotenv, una biblioteca que lee pares de valores clave de un archivo y los agrega como variables de entorno. Utilizaremos este módulo para recuperar nuestras credenciales de Twilio almacenadas en un archivo de configuración .env.

Como referencia, en el momento en que se publicó este tutorial, estas eran las versiones de los paquetes anteriores y las dependencias que se probaron:

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

Aparte de los paquetes de Python mencionados anteriormente, usaremos las siguientes bibliotecas de la interfaz:

  • Bootstrap, una potente estructura de interfaz utilizada para crear sitios web modernos.
  • SDK de Twilio Client JS (twilio.js), una biblioteca que te permite realizar llamadas de voz desde un navegador web.
  • JQuery, una biblioteca JavaScript rápida, pequeña y con muchas funciones que hace mucho más sencillo realizar tareas como la manipulación y el desplazamiento de documentos HTML, el manejo de eventos, la animación y Ajax. Lo usaremos para realizar alguna manipulación de DOM y manejo de eventos.
  • Font Awesome, un popular conjunto de herramientas de SVG, fuente y CSS. Usaremos algunos de los íconos diseñados por ellas en nuestro cliente de la aplicación.

Crea una aplicación de TwiML

En esta sección, usaremos la consola de Twilio para crear una "aplicación de TwiML", en la que luego almacenaremos la URL del webhook que crearemos.

Abre una nueva ventana del navegador y sigue esta ruta: Twilio account Console > Voice > TwiML > TwiML Apps (consola de la cuenta Twilio > Voice > TwiML > aplicaciones de TwiML). Haz clic en el botón "Create new TwiML App" (Crear nueva aplicación de TwiML) o en el ícono rojo "+" si ya tienes otras aplicaciones de TwiML.

 

App de TwiML

Ingresa el nombre de tu app de TwiML en el campo "Friendly Name" (Nombre descriptivo), por ejemplo, llamadas en el navegador. Deja los otros campos vacíos por ahora. Haz clic en el botón "Create" (Crear) para crear la aplicación de TwiML.

Página de la app de TwiML

Se te redirigirá de nuevo al TwiML Apps dashboard (panel de control de aplicaciones de TwiML). Haz clic en la app de TwiML que acaba de crear. En la página de esta aplicación, selecciona el SID Value (valor de SID) y cópialo en el portapapeles.

Página de la app de TwiML

En el directorio raíz del proyecto, crea un archivo con el nombre .env e ingresa el siguiente contenido:

TWIML_APP_SID="paste your TwiML app SID here"

Creación de una clave de la API de Twilio

Para el siguiente paso, vamos a crear una clave de la API de Twilio para la API de voz. La clave de API se utilizará para generar tokens de acceso, lo que permitirá que la interfaz se ejecute en el navegador para realizar llamadas a las API de Twilio.

Sigue esta ruta en el navegador: Twilio console > Voice > Settings > API Keys (consola de Twilio > Voice > Configuración > Claves de API). Haz clic en el botón "Create new API Key" (Crear nueva clave de API) o en el ícono rojo "+" si ya tienes otras claves de API.

Claves de API de Twilio

Ingresa el nombre de tu clave API en el campo "Friendly Name" (Nombre descriptivo), por ejemplo, llamadas en el navegador. Deja el "Key Type" (Tipo de clave) como "Standard" (Estándar). Haz clic en el botón "Create API Key" (Crear clave de API) para crear la clave de API.

Nueva clave de API

Se te redirigirá a una página donde encontrarás información sobre tu nueva clave de API. Copia los valores "SID" (SID) y "Secret" (Secreto) y pégalos en tu archivo .env como TWILIO_API_KEY_SID y TWILIO_API_KEY_SECRET. Tu archivo .env debería verse de la siguiente manera:

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"

Selecciona "Got it!" (Entendido) y, a continuación, haz clic en el botón "Done" (Listo).

Se creó la clave de API

Ahora ve a la Twilio Console home page (Página de inicio de la consola de Twilio) y copia el valor de SID de la cuenta de Twilio en el archivo .env de la siguiente manera:

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"

A continuación, sigue la ruta Twilio account console > Phone Numbers > Manage Numbers > Active Numbers (Consola de la cuenta de Twilio > Números de teléfono > Administrar números > Números activos), selecciona el número que compraste para este tutorial y se te redirigirá a una página donde puedes configurar este número. Busca el campo "Phone Number" (Número de teléfono), copia el número de teléfono que aparece en este campo y pégalo en el archivo .env como TWILIO_NUMBER. Elimina cualquier espacio entre los dígitos, pero deja el signo + inicial y asegúrate de que el número esté en formato E.164.

Número de teléfono de Twilio

El archivo .env debe tener el siguiente aspecto una vez que se agregue el número de teléfono:

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"

Creación de una aplicación de Flask

En esta sección, crearemos la lógica de nuestra aplicación de Flask, que proporcionará las funciones de soporte necesarias para que el usuario realice y reciba llamadas telefónicas.

Creación del servidor de la aplicación

En esta sección, vamos a crear los puntos finales necesarios para realizar y recibir llamadas telefónicas. Necesitaremos crear los siguientes puntos finales:

  • /: este punto final será responsable de prestar servicio a la IU de la aplicación (es decir, al cliente).
  • /token: este punto final será responsable de generar y devolver tokens de acceso al cliente.
  • /handle_calls: este punto final será responsable de generar las instrucciones de TwiML necesarias para realizar y recibir llamadas telefónicas.

En el directorio raíz de su proyecto, crea un archivo con el nombre main.py. Abre el archivo con tu editor de texto favorito y, luego, agrega el siguiente código:

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

Aquí, importamos todos los paquetes que necesitaremos para construir nuestra aplicación de servidor:

  • flask se utilizará para definir los puntos finales de la aplicación.
  • El paquete de twilio se utilizará para interactuar con la API de Twilio, lo que nos permite realizar y recibir llamadas telefónicas a través del dispositivo de Twilio que se creará en el lado del cliente.
  • load_dotenv se utilizará para importar las credenciales de nuestra cuenta de Twilio desde el archivo .env.
  • pprint se utilizará para formatear e imprimir los datos recibidos cuando Twilio envíe una solicitud al punto final /handle_calls para notificar que hay una llamada.
  • os se utilizará junto con load_dotenv para recuperar las credenciales almacenadas en el archivo .env.

Agrega el siguiente código a la parte inferior del archivo 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",
    )

La aplicación comienza importando las variables del entorno almacenadas en el archivo .env con una llamada a load_dotenv(). Esto nos permite recuperar las cinco variables de configuración que necesitamos para esta aplicación.

A continuación, creamos una instancia de una aplicación de Flask en una variable llamada app y con eso creamos el punto final / de nuestra aplicación. Esta ruta brinda una plantilla con el nombre home.html que crearemos más adelante.

Agrega el código siguiente debajo de la ruta /:

@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

Esto agrega el punto final /token, que el cliente invocará para solicitar un token de acceso.

Cuando se activa este punto final, creamos una variable llamada identity y la asignamos a nuestro número de Twilio. Una identidad es única para cada usuario y puede iniciar sesión en varios dispositivos a la vez. En un servidor de aplicaciones diseñado para que varios usuarios lo utilicen, debemos decidir, según la solicitud de token que se nos envíe, quién es el usuario y qué puede hacer. Para averiguar quién es el usuario (su identidad), usaremos nuestro sistema de inicio de sesión o proveedor de identidad existente (p. ej., cookies de sesión, un token de API o cualquier otro mecanismo que se utilice para asegurar sus solicitudes de API). Sin embargo, en este tutorial, somos el único usuario, por lo que no necesitamos trabajar con varias identidades y el número de Twilio que compramos para este tutorial funciona bien para este propósito.

A continuación, usamos account_sid, api_key, api_key_secret y identity para crear un token de acceso. El token se debe proporcionar con "concesiones", que determinan qué operaciones tiene permitido realizar el cliente que presenta el token. Para esta aplicación, creamos un objeto de concesión de voz que está configurado con el sid de la app de TwiML que creamos anteriormente.

Para completar el punto final, devolvemos el access_token y identity al cliente en formato JSON.

Agrega el código siguiente debajo de la ruta /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)

Este bloque agrega un punto final llamado /handle_calls. Twilio invocará este punto final cada vez que hagamos o recibamos una llamada telefónica.

Cuando se activa este punto final, se utiliza pprint para imprimir el contenido de request.form y, luego, creamos un objeto de respuesta de TwiML y un objeto de marcado de TwiML. En el objeto dial, el callerId será el número de Twilio que compramos para este tutorial. De esta manera, cuando llamemos a un número de teléfono con esta aplicación, el teléfono del destinatario mostrará este número en lugar de anonymous.

Después de eso, utilizamos la lógica condicional para comprobar si el objeto request.form tiene la propiedad con el nombre To y si este valor de propiedad no es el mismo que nuestro twilio_number. Lo que esta prueba logra es asegurarse de que la invocación del punto final sea para realizar una llamada (y no recibir una), que es el primer caso que manejaremos.

Una vez que estamos seguros de que esta es una solicitud para realizar una llamada, estableceremos el número que queremos marcar en el valor del request.form['To'], anexaremos el objeto dial al objeto response y devolveremos el objeto response como una cadena que regresa a Twilio, que ejecutará estas instrucciones y marcará el número solicitado.

La parte inferior del guion es una condicional estándar que ejecuta el servidor de desarrollo de Flask en el puerto 3000 cuando se invoca el guion desde la línea de comandos.

Creación de un cliente de la aplicación

En esta sección, crearemos la interfaz que nos permitirá realizar y recibir llamadas telefónicas en el navegador.

Creación de la página de inicio

Crea un archivo con el nombre home.html dentro del directorio templates. Ábrelo y agrega el siguiente código:

<!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>

Esta plantilla implementa una página que muestra una consola donde podemos monitorear el estado del dispositivo de Twilio que crearemos pronto con JavaScript. Hemos incluido en esta plantilla las plantillas call_in_progress_modal.html, dial_modal.html y incoming_call_modal.html que venían con el modelo estándar de este proyecto. Esta página también muestra un botón con un ícono de teléfono. Cada vez que presionemos este botón, se abrirá un modal y, en este modal, ingresaremos el número al que nos gustaría llamar.

Aparte de Bootstrap, jQuery, FontAwesome y los archivos twilio.js, observa cómo incluimos los siguientes archivos en esta plantilla:

  • style.css: este archivo contiene algunos de las CSS utilizadas para modelar nuestra aplicación.
  • main.js: este archivo javascript contiene el código para crear un dispositivo de Twilio y conectarlo a nuestra aplicación de TwiML.
  • modals.js: este archivo javascript contiene el código para administrar los modales de nuestra aplicación.

Los archivos style.css y modals.js se encontraban dentro del repositorio estándar del proyecto. Vamos a crear el archivo main.js en la siguiente sección.

Creación del dispositivo de Twilio

Crea un archivo con el nombre main.js dentro del directorio static/js. Ábrelo y agrega el siguiente código:

$(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;
    }

});

Aquí, crearemos el dispositivo de Twilio que nos permitirá realizar y recibir llamadas telefónicas en el navegador.

En primer lugar, utilizamos la función getJSON() proporcionada por jQuery para enviar una solicitud GET (OBTENCIÓN) al punto final /token de nuestro servidor de aplicaciones y obtener un token de acceso. Después de obtener el token, utilizamos twilio.js y el token para crear un dispositivo de Twilio y conectarlo a nuestra aplicación de TwiML.

Después de crear el dispositivo de Twilio, agregamos algunos oyentes de eventos a este dispositivo y algún código que nos permitirá interactuar con este dispositivo mediante la IU.

Realizar llamadas telefónicas salientes

En esta sección, vamos a utilizar nuestra aplicación para realizar llamadas telefónicas. Sin embargo, antes de poder hacerlo, debemos ejecutar la aplicación, configurar ngrok y configurar nuestra aplicación de TwiML.

Abre una segunda ventana de terminal en nuestro directorio de proyectos, activa el entorno virtual de Python e inicia la aplicación ejecutando el siguiente comando:

$ python main.py
``

Open another terminal window and start `ngrok` on it:

Después de ejecutar el comando anterior, deberías ver algo similar a esto:

Salida de ngrok

Copia la URL https de ngrok en el portapapeles. Luego, sigue la ruta Twilio account Console > Voice> TwiML > TwiML Apps dashboard (Consola de la cuenta de Twilio > Voice> TwiML > Panel de control de aplicaciones de TwiML) y selecciona la app de TwiML que creaste para este tutorial.

Configura el webhook de Voice en la aplicación de TwiML

Encuentra la sección "Voice" en la configuración de la app de TwiML y pega la URL https:// que proporcionó ngrok seguida de /handle_calls en el campo "Request URL" (Solicitar URL) y haz clic en el botón "Save" (Guardar). Esto creará un webhook que conecta tu aplicación a la app de TwiML.

En este ejemplo, la URL de ngrok es https://48dcc810632b.ngrok.io/handle_calls. La primera parte de la URL será diferente cada vez que se inicie ngrok.

Ahora, conecta un auricular con micrófono a tu computadora, abre el navegador y escribe http://localhost:3000/ en la barra de direcciones. Deberías ver algo similar a esto:

 

Página inicial de la aplicación

Cuando veas un mensaje que diga Twilio.Device Ready! (Tu dispositivo de Twilio está funcionando correctamente), sabrás que todo funciona como debería. Haz clic en el botón con el ícono verde del teléfono y verás el modal de marcado:

marcador telefónico

Utiliza los números del panel para insertar el número al que deseas llamar o simplemente escríbelo con el teclado en el campo de inserción sobre el panel y, una vez que esté listo, haz clic en el teléfono verde para realizar la llamada.

Cuando hagas clic en dicho botón, tu navegador te pedirá permiso para usar el micrófono y deberás concederlo. Cuando el número responda tu llamada, verás el modal de llamada en curso:

Página de llamada en curso

Ve a la terminal que ejecuta nuestra aplicación de Flask. Los datos de la solicitud que Twilio envía a nuestro punto final /handle_calls se parecerán a estos:

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

Dado que el valor de la propiedad To (el número al que llamamos) no es igual a nuestro número de Twilio, se ejecutó el código dentro de la declaración if en el punto final handle_calls.

Responder llamadas telefónicas entrantes

En la sección anterior, pudiste utilizar tu aplicación para realizar llamadas telefónicas, pero hasta ahora no puedes recibir ninguna llamada. Para poder recibir llamadas, necesitaremos agregar un código adicional a main.py, main.js y home.html, así como configurar el número que compramos para este tutorial en la consola de Twilio para que pueda recibir llamadas telefónicas.

Vuelve al archivo main.py y reemplaza el código en el punto final /handle_calls con el siguiente:

@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))

Aquí hemos agregado la declaración else al punto final /handle_calls. El código en esta parte se ejecutará si el número que recibe la llamada es el número que compramos para este tutorial, lo que significa que tenemos una llamada entrante.

Estamos configurando el callerId en el objeto de marcado de TwiML para el valor de la propiedad Caller en request.form. Caller (palabra que, en inglés, significa "agente de llamada") es el número que llama a nuestro número de Twilio. Esto es para que podamos ver quién nos llama en la IU de nuestra aplicación. También estamos configurando el client en el objeto de marcado con el valor identity que utilizamos cuando creamos un token de acceso en el punto final /token.

Para completar el flujo de llamadas entrantes, adjuntamos el objeto de marcado al objeto de respuesta de TwiML y, luego, devolvemos este objeto de respuesta como una cadena.

Vuelve a tu archivo main.js y agrega el siguiente código debajo del oyente 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();
    })

});

Aquí agregamos un oyente de eventos que permitirá que el dispositivo de Twilio monitoree las llamadas entrantes y muestre el modal de llamadas entrantes una vez que detecte una.

Sigue la ruta Twilio console > Phone Numbers > Manage Numbers > Active Numbers dashboard  (Consola de Twilio > Números de teléfono > Administrar números > Panel de control de números activos) y selecciona el número que compraste para este tutorial.

Busca la sección "Voice & Fax" (Voz y fax) de la configuración del número de teléfono y selecciona "TwiML App" (App de TwiML) en el campo "Configure With" (Configurar con). Luego, selecciona el nombre de la app de TwiML que creaste para este tutorial en el campo "TwiML App" (App de TwiML). Esto vinculará el número de Twilio que compraste para este tutorial con la app de TwiML que creaste. De esta manera, cada vez que este número reciba una llamada telefónica, obtendrás la URL del webhook y otros ajustes en la app de TwiML y la usarás para responder a la llamada.  En nuestro caso, enviará una solicitud POST a https://48dcc810632b.ngrok.io/handle_calls que contenga el número del agente de llamada y otra información útil.

Vuelve a tu navegador y ve a http://localhost:3000/, espera a que el mensaje Twilio.Device Ready! (Tu dispositivo de Twilio está funcionando correctamente) aparezca en la página y, a continuación, utiliza un dispositivo capaz de realizar llamadas telefónicas para llamar al número de Twilio que compraste para este tutorial. Cuando el número dé tono, deberías ver el modal de llamada entrante:

 

Página de llamada entrante

Presiona el botón con el ícono verde para aceptar la llamada y el botón con el ícono rojo para rechazarla.

Ve a la terminal que ejecuta nuestra aplicación de Flask. Los datos de la solicitud que Twilio envía a nuestro punto final /handle_calls se parecerán a estos:

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

 

Conclusión

En este tutorial, aprendimos a utilizar la API de Twilio Voice para realizar y recibir llamadas telefónicas en el navegador. Aprendimos cómo utilizar la estructura Flask para construir el cliente de aplicación que nos permitió interactuar con un dispositivo de Twilio creado con el SDK de Twilio Client JS.

El código para la aplicación completa está disponible en el siguiente repositorio https://github.com/CSFM93/twilio-in-browser-calls.

Carlos Mucuho es un geólogo mozambiqueño que se ha convertido en desarrollador y disfruta de usar la programación para hacer que las ideas se vuelvan realidad https://github.com/CSFM93.