Faça e receba chamadas telefônicas do navegador com o Programmable Voice da Twilio com Python e JavaScript

April 21, 2021
Escrito por
Carlos Mucuho
Contribuidor
As opiniões expressas pelos colaboradores da Twilio são de sua autoria

Neste tutorial, vamos escrever um aplicativo que usa a API Twilio Programmable Voice para fazer e receber chamadas telefônicas de um navegador da web. Também implementaremos uma interface de usuário que nos permite fazer, aceitar e rejeitar chamadas telefônicas.

Ao final deste tutorial, teremos um aplicativo semelhante ao seguinte:

Demonstração do projeto

Requisitos do tutorial

Para seguir este tutorial você precisará dos seguintes componentes:

  • Uma conta da Twilio gratuita ou paga. Se você é novo na Twilio, crie uma conta gratuita agora. Se você criar sua conta usando este link e, posteriormente, atualizar para uma conta paga, você receberá US$ 10 em crédito.
  • Um número de telefone Twilio que pode fazer e receber chamadas. Obtenha um agora , se não tiver.
  • Python 3.6 ou superior instalado.
  • O ngrok instalado. O ngrok é um serviço de proxy reverso que cria um túnel seguro de um endpoint público para um serviço da web executado localmente. Precisaremos usar o ngrok para criar um URL segura que permita que a Twilio se conecte ao nosso aplicativo.
  • Um telefone celular ou telefone que pode fazer e receber chamadas telefônicas para testar o projeto.

Conceitos básicos e lógica do aplicativo

O objetivo  deste tutorial é criar um aplicativo da web que nos permita fazer e receber chamadas telefônicas no navegador. Usaremos a estrutura do Flask para implementar os webhooks necessários e atender ao aplicativo cliente.

O cliente do aplicativo usará o Twilio Client JS SDK (twilio.js) para criar um dispositivo Twilio.

Um dispositivo Twilio é nosso principal ponto de entrada para fazer e receber chamadas telefônicas no navegador. Para  configurar uma conexão entre um dispositivo Twilio e os servidores da Twilio, precisaremos gerar tokens de acesso em nosso servidor de aplicativos.

 Os tokens de acesso são credenciais de curta duração que podem ser distribuídas com segurança aos aplicativos do lado do cliente que podemos usar para autenticar SDKs do cliente Twilio comoVoice,Conversations,Sync e Video. Para gerar tokens de acesso em nosso servidor, precisaremos usar uma chave de API da Twilio.

Uma chave de API da Twilio é uma credencial que nos concede acesso à API da Twilio. Uma chave de API nos permite:

  • Autenticar com a API da Twilio
  • Crie e revogue tokens de acesso

Os tokens de acesso concedem ao cliente acesso a um aplicativo TwiML (aplicativo TwiML). A Twilio conta com um aplicativo TwiML em nossa conta para determinar como interagir com nosso servidor.

TwiML (Twilio Markup Language) é um conjunto de instruções que podemos usar para dizer à Twilio o que fazer quando recebermos uma chamada, SMS ou fax.

Criar a estrutura do projeto

Nesta seção, criaremos nosso diretório de projeto e, dentro dele, criaremos os diretórios padrão para um aplicativo Flask. Depois disso, criaremos e ativaremos um ambiente virtual. Por fim, instalaremos os pacotes Python necessários para criar esse aplicativo da web.

Abra uma janela de terminal e digite os seguintes comandos:

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

Aqui, clonamos um projeto inicial que foi criado para este tutorial e definimos o nome como twilio-in-browser-calls, em seguida, navegamos até o diretório do projeto. Este projeto contém o código padrão que usaremos para construir nosso aplicativo.

Você encontrará os seguintes diretórios padrão para um aplicativo Flask dentro de:

  • static: é aqui que todos os arquivos estáticos são armazenados.
  • templates: é aqui que todos os modelos são armazenados

O subdiretório static tem o seguinte conteúdo:

  • css - é aqui que armazenaremos todos os         arquivos CSS. Dentro deste subdiretório você encontrará um arquivo chamado style.css, este arquivo será responsável por modelar a UI do cliente do aplicativo.
  • images - é aqui que armazenaremos todas as imagens. Neste  subdiretório você encontrará um arquivo chamado user.png, que exibiremos na IU do cliente do nosso aplicativo.
  • js:  É aqui que armazenamos todos os nossos arquivos Javascript.  Dentro desse diretório, você encontrará um arquivo chamado modals.js. Este arquivo contém o código para gerenciar os módulos armazenados no diretório templates.

O subdiretório templates tem três arquivos: call_in_progress_modal.html, dial_modal.html e incoming_call_modal.html.

O arquivo call_in_progress_modal.html contém o código HTML que implementa um modal que será exibido durante uma chamada. Este modal mostra a duração da chamada em andamento, o número que você está chamando e um botão com um ícone de telefone vermelho que permitirá que você encerre a chamada. É assim que este modal se parece:

 

Página Chamada em andamento

O arquivo dial_modal.html contém o código HTML que implementa um modal que será exibido quando você quiser discar um número de telefone. Este modal mostra um teclado numérico e um botão com um ícone de telefone verde. É assim que ele se parece:

Página discar um número

O arquivo incoming_call_modal.html contém o código HTML de um modelo que implementa um modal que só será exibido quando você estiver recebendo uma chamada. Este modal mostra o número que está chamando você, e dois botões, um botão com um ícone de telefone verde e outro com um ícone de telefone vermelho. O primeiro botão permitirá que você aceite uma chamada recebida e o segundo, que a rejeite.

Página de chamada recebida

Dentro do nosso diretório de trabalho, crie um ambiente virtual e ative-o. Se você estiver usando um sistema Unix ou Mac OS, execute os seguintes comandos para fazer isso:

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

Se você estiver seguindo o tutorial no Windows, execute os seguintes comandos:

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

Agora que criamos e ativamos nosso ambiente virtual, podemos instalar as bibliotecas necessárias para criar nosso aplicativo:

$ pip install twilio flask python-dotenv

No comando acima utilizamos, pip, o instalador de pacotes Python, para instalar os seguintes pacotes que iremos utilizar neste projeto:

  • Twilio, um pacote Python para comunicação com a API da Twilio.
  • Flask, uma microestrutura Python para a criação de aplicativos da web. Vamos usá-lo para criar um webhook para interagir com a Twilio e criar a interface do usuário do cliente para fazer e receber chamadas telefônicas.
  • Python-dotenv, uma biblioteca que lê pares chave-valor de um arquivo e os adiciona como variáveis de ambiente. Usaremos este módulo para recuperar nossas credenciais da Twilio armazenadas em um arquivo de configuração .env.

Para sua referência, quando este tutorial foi lançado, estas eram as versões dos pacotes acima e das respectivas dependências testadas:

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

Além dos pacotes Python mencionados acima, usaremos as seguintes bibliotecas de front-end:

  • Bootstrap, uma poderosa estrutura de front-end usada para criar sites modernos.
  • Twilio Client JS SDK (twilio.js), uma biblioteca que permite fazer chamadas de voz a partir de um navegador da web.
  • JQuery, uma biblioteca JavaScript rápida, pequena e repleta de recursos que torna coisas como passagem e manipulação de documentos HTML, manipulação de eventos, animação e Ajax muito mais simples. Nós o usaremos para realizar alguma manipulação de Dom e tratamento de eventos.
  • Font Awesome, um popular kit de ferramentas de ícones de SVG, fontes e CSS. Usaremos alguns dos ícones criados por eles em nosso cliente de aplicativos.

Criar um aplicativo TwiML

Nesta seção, vamos utilizar o console da Twilio para criar um "aplicativoTwiML", no qual iremos posteriormente armazenar o URL do webhook que criaremos.

Abra uma nova janela do navegador e navegue até Console da conta Twilio > Voice > TwiML > Apps TwiML. Clique no botão "Criar novo aplicativo TwiML" ou no ícone vermelho "+" se você já tiver outros aplicativos TwiML.

 

Aplicativos TwiML

Digite o nome do seu aplicativo TwiML no campo "Nome amigável", por exemplo, chamadas no navegador. Deixe os outros campos vazios por enquanto. Clique no botão "Criar" para criar o aplicativo TwiML.

Página do aplicativo TwiML

Você será redirecionado para o dashboard de aplicativos TwiML. Clique no aplicativo TwiML que você acabou de criar. Na página deste aplicativo, selecione o valor SID e copie-o para a área de transferência.

Página do aplicativo TwiML

No diretório raiz do projeto, crie um arquivo nomeado .env e insira o seguinte conteúdo nele:

TWIML_APP_SID="paste your TwiML app SID here"

Criar uma chave de API Twilio

Para a próxima etapa, vamos criar uma chave de API da Twilio para a API de voz. A chave de API será usada para gerar tokens de acesso, o que permitirá que o front-end em execução no navegador faça chamadas para as APIs da Twilio.

Navegue até o Console da Twilio > Voice > Configurações > Chaves de API em seu navegador. Clique no botão "Criar nova chave de API" ou no ícone vermelho "+" se já tiver outras chaves de API.

Chaves de API da Twilio

Digite o nome da sua chave de API no campo "Nome amigável", por exemplo, chamadas no navegador. Deixe o "Tipo de chave" como "Padrão". Clique no botão "Criar chave de API" para criar a chave de API.

Nova chave de API

Você será redirecionado para uma página onde encontrará informações sobre sua nova chave de API. Copie os valores "SID" e "Segredo" e cole-os no arquivo .env como TWILIO_API_KEY_SID e TWILIO_API_KEY_SECRET. Seu arquivo .env agora deve ter a seguinte aparência:

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"

Verifique o campo "Entendi!" e, em seguida, clique no botão "Finalizar".

Chave de API criada

Agora vá para a página inicial do console da Twilioe copie o valor SID da conta da Twilio para o arquivo .env da seguinte maneira:

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"

Em seguida, vá para o Console da sua conta Twilio > Números de telefone > Gerenciar números > Números ativos, selecione o número que você comprou para este tutorial e você será redirecionado para uma página onde poderá configurar esse número. Localize o campo "Número de telefone", copie o número de telefone exibido neste campo e cole-o no arquivo .env como TWILIO_NUMBER. Remova todos os espaços entre os dígitos, mas deixe o sinal de mais inicial, garantindo que o número esteja no formato E.164.

número de telefone da Twilio

Seu arquivo .env deve ter a seguinte aparência quando o número de telefone for adicionado:

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"

Criar o aplicativo Flask

Nesta seção, criaremos a lógica do nosso aplicativo Flask, que fornecerá as funções de suporte necessárias ao front-end para fazer e receber chamadas telefônicas.

Criar o servidor de aplicativos

Nesta subseção, criaremos os endpoints necessários para fazer e receber chamadas telefônicas. Precisaremos criar os seguintes endpoints:

  • / - esse endpoint será responsável por servir a interface do usuário do aplicativo (cliente).
  • /token - esse endpoint será responsável por gerar e devolver tokens de acesso ao cliente.
  • /handle_calls -  este endpoint será responsável por gerar as instruções TwiML necessárias para fazer e receber chamadas telefônicas.

No diretório raiz do projeto, crie um arquivo chamado main.py. Abra-o usando seu editor de texto favorito e adicione o seguinte código a ele:

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

Aqui, importamos todos os pacotes que precisaremos para criar nosso aplicativo de servidor:

  • flask será usado para definir os endpoints do aplicativo.
  • O pacote twilio será usado para interagir com a API da Twilio, permitindo que façamos e recebam chamadas telefônicas por meio do dispositivo Twilio que será criado no cliente.
  • load_dotenv será usado para importar nossas credenciais de conta da Twilio do arquivo .env.
  • pprint será usado para formatar e imprimir os dados recebidos quando a Twilio enviar uma solicitação ao endpoint /handle_calls para notificar que há uma chamada.
  • os será usado com load_dotenv para recuperar as credenciais armazenadas no arquivo .env

Adicione o seguinte código na parte inferior do arquivo 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",
    )

O aplicativo é iniciado importando as variáveis de ambiente armazenadas no arquivo .env com uma chamada para o load_dotenv(). Isso nos permite recuperar as cinco variáveis de configuração necessárias para este aplicativo.

Em seguida, criamos uma instância de aplicativo Flask em uma variável com o nome de app e, com isso, criamos o endpoint / do nosso aplicativo. Esta rota está servindo um modelo chamado home.html que criaremos mais tarde.

Adicione o seguinte código abaixo da rota /:

@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

Isso adiciona o endpoint /token, que será chamado pelo cliente para solicitar um token de acesso.

Quando esse endpoint é acionado, criamos uma variável nomeada identity e a atribuímos a ele nosso número Twilio. Uma identidade é exclusiva para um usuário e pode ser conetada em vários dispositivos simultaneamente. Em um servidor de aplicativos destinado a ser usado por vários usuários, precisaríamos decidir, com base na solicitação de token que nos seria enviada, quem é o usuário e o que ele tem permissão para fazer. Para descobrir quem é o usuário (sua identidade), usaríamos nosso sistema de login ou provedor de identidade existente (por exemplo, cookies de sessão, um token de API ou qualquer outro mecanismo usado para proteger suas solicitações de API). Neste tutorial, no entanto, somos o único usuário, portanto, não precisamos trabalhar com várias identidades, e o número da Twilio que adquirimos para este tutorial funciona bem para essa finalidade.

Em seguida, usamos o account_sid, api_key, api_key_secret e identity para criar um token de acesso. O token precisa ser provisionado com "concessões", que determinam quais operações o cliente que apresenta o token tem permissão para executar. Para este aplicativo, criamos um objeto de concessão de voz que é configurado com o sid do aplicativo TwiML criado anteriormente.

Para concluir o endpoint, retornamos o access_token e identity para o cliente no formato JSON.

Adicione o seguinte código abaixo da rota /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)

Esse bloco adiciona um endpoint chamado /handle_calls. Esse endpoint será chamado pela Twilio sempre que fizermos ou recebermos uma chamada telefônica.

Quando esse endpoint é acionado, usamos pprint para imprimir o conteúdo do request.form, e, em seguida, criamos um objeto de resposta TwiML e um objeto de discagem TwiML. No objeto dial, configuramos o callerId como o número Twilio que adquirimos para este tutorial, desta forma, quando ligamos para um número de telefone usando este aplicativo, o telefone do destinatário mostrará esse número em vez de anonymous.

Depois disso, usamos a lógica condicional para verificar se o objeto request.form possui a propriedade chamada To, e se o valor dessa propriedade não é igual ao nosso twilio_number. Esse teste garante que a chamada do endpoint foi feita para fazer uma chamada (e não para receber uma), que é o primeiro caso que trataremos.

Quando tivermos certeza de que se trata de uma solicitação para fazer uma chamada, definimos o número que queremos discar para o valor em request.form['To'], anexamos o objeto dial ao objeto response e retornamos o objeto response como uma string de volta à Twilio, que executará essas instruções e discará o número solicitado.

A parte inferior do script é uma condicional padrão que executa o servidor de desenvolvimento do Flask na porta 3000 quando o script é chamado a partir da linha de comando.

Criar o cliente do aplicativo

Nesta subseção, criaremos o front-end que nos permitirá fazer e receber chamadas telefônicas no navegador.

Criar a página inicial

Crie um arquivo chamado home.html dentro do diretório templates. Abra-o e adicione o seguinte código a ele:

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

Este modelo implementa uma página que mostra um console onde podemos monitorar o estado do dispositivo Twilio que criaremos em breve usando JavaScript. Incluímos neste modelo os modelos call_in_progress_modal.html, dial_modal.html e incoming_call_modal.html e os modelos que vieram com o padrão deste projeto. Esta página também mostra um botão com um ícone de telefone. Sempre que pressionarmos este botão, será aberto um modal e, neste modal, inseriremos o número que desejamos chamar.

Além do Bootstrap, jQuery, FontAwesome e arquivos twilio.js, observe como estamos incluindo os seguintes arquivos neste modelo:

  • style.css - este arquivo contém algumas das CSS usadas para modelar nosso aplicativo
  • main.js - este arquivo javascript contém o código para criar um dispositivo Twilio e conectá-lo ao aplicativo TwiML .
  • modals.js - este arquivo javascript contém o código para gerenciar os módulos de nosso aplicativo.

Os arquivos style.css e modals.js vieram dentro do repositório de boilerplate do projeto. Criaremos o arquivo main.js na próxima subseção.

Criando o dispositivo Twilio

Crie um arquivo chamado main.js dentro do diretório static/js. Abra-o e adicione o seguinte 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;
    }

});

Aqui criaremos o dispositivo Twilio que nos permitirá fazer e receber chamadas telefônicas no navegador.

Primeiro, usamos a função getJSON() fornecida pelo jQuery para enviar uma solicitação GET para o endpoint /token em nosso servidor de aplicativos e recuperar um token de acesso. Depois de recuperar o token, usamos o twilio.js e o token para criar um dispositivo Twilio e conectá-lo ao aplicativo TwiML.

Depois de criar o dispositivo Twilio, adicionamos alguns ouvintes de eventos a este dispositivo e algum código que nos permitirá interagir com este dispositivo usando a interface do usuário.

Fazer chamadas telefônicas outbound

Nesta seção, usaremos nosso aplicativo para fazer chamadas telefônicas outbound. Mas antes de fazermos isso, precisamos executar o aplicativo, configurar o ngrok e configurar nosso aplicativo TwiML.

Abra uma segunda janela de terminal em nosso diretório de projeto, ative o ambiente virtual Python e inicie o aplicativo executando o seguinte comando:

$ python main.py
``

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

Depois de executar o comando acima, você verá algo semelhante a este:

Saída do Ngrok

Copie o URL https do ngrok para a área de transferência. Em seguida, acesse o dashboard do Console da conta Twilio > Voice > TwiML > Apps TwiML e selecione o aplicativo TwiML que você criou para este tutorial.

Configure o webhook de voz no aplicativo TwiML

Localize a seção "Voice" da configuração do aplicativo TwiML e cole o URL https:// fornecida pelo ngrok seguida do endpoint /handle_calls no campo "URL de solicitação" e clique no botão "Salvar". Isso criará um webhook que conecta seu aplicativo ao aplicativo TwiML.

Neste exemplo, o URL do ngrok é https://48dcc810632b.ngrok.io/handle_calls. A primeira parte do URL será diferente toda vez que o ngrok for iniciado.

Agora, conecte um fone de ouvido com microfone ao seu computador, depois abra seu navegador e digite http://localhost:3000/ na barra de endereço. Você deverá ver algo semelhante a isto:

 

Página inicial do aplicativo

Depois de ver uma mensagem dizendo Twilio.Device Ready!, seu dispositivo Twilio está funcionando como deveria. Clique no botão com o ícone verde do telefone e você verá o modal de discagem:

discador do telefone

Use os números no teclado para inserir o número que você gostaria de chamar ou apenas digite-o usando seu teclado no campo de inserção acima do teclado e, quando estiver pronto, clique no telefone verde para fazer a chamada.

Depois de clicar neste botão, seu navegador solicitará sua permissão para usar o microfone e concederá a permissão. Assim que o número que você está ligando atender a chamada, você verá a chamada em andamento modal:

Página Chamada em andamento

Vá para o terminal que está executando nosso aplicativo Flask e os dados de solicitação que a Twilio envia para nosso endpoint /handle_calls serão semelhante ao seguinte:

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

Como o valor da propriedade To (o número que chamamos) não é igual ao nosso número Twilio, o código dentro da instrução if no edpoint handle_calls foi executado.

Atender chamadas telefônicas recebidas

Na seção anterior, você conseguiu usar seu aplicativo para fazer chamadas telefônicas, mas até o momento o aplicativo não pode receber nenhuma chamada. Para poder receber chamadas, precisaremos adicionar algum código adicional em main.py, main.js, e home.html, além de configurar o número que compramos para este tutorial no console da Twilio para poder receber telefonemas.

Volte para o arquivo main.py e substitua o código no endpoint /handle_calls pelo seguinte:

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

Aqui adicionamos a instrução else ao endpoint /handle_calls. O código nesta parte será executado se o número que recebe a chamada for o número que adquirimos para este tutorial, o que significa que temos uma chamada recebida.

Estamos definindo o callerId no objeto de discagem TwiML para o valor da propriedade Caller em request.form. Como o nome sugere, Caller é o número que está ligando para nosso número Twilio. Isso é para que possamos ver quem está nos ligando na interface do usuário do nosso aplicativo. Também estamos definindo client no objeto de discagem como o valor de identity que usamos quando criamos um token de acesso no endpoint /token.

Para concluir o fluxo de chamadas recebidas, anexamos o objeto de discagem ao objeto de resposta TwML e, em seguida, retornamos esse objeto de resposta como uma string.

Volte para o arquivo main.js e adicione o seguinte código abaixo do ouvinte 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();
    })

});

Aqui, adicionamos um ouvinte de eventos que permitirá que seu dispositivo Twilio monitore chamadas recebidas e mostre o modal de chamadas recebidas assim que detectar uma.

Vá para o dashboard do Console da Twilio > Números de telefone > Gerenciar números > Números ativos e selecione o número que você comprou para este tutorial.

Localize a seção "Voz e fax" da configuração do número de telefone e selecione "aplicativo TwiML" no campo "Configurar com". Depois, selecione o nome do aplicativo TwiML que você criou para este tutorial no campo "aplicativo TwiML". Isso vinculará o número da Twilio adquirido para este tutorial com o aplicativo TwiML que você criou. Dessa forma, sempre que esse número receber uma chamada telefônica, ele recuperará o URL do webhook e outras configurações no aplicativo TwiML e o usará para responder à chamada.  Em nosso caso, ele enviará uma solicitação de POST para https://48dcc810632b.ngrok.io/handle_calls contendo o número do chamador e outras informações úteis.

Volte para o seu navegador e navegue até http://localhost:3000/, aguarde a mensagem Twilio.Device Ready! a ser exibida na página e, em seguida, use um dispositivo capaz de fazer chamadas para ligar para o número Twilio que você comprou para este tutorial. Quando o número estiver tocando, você deverá ver o modal de chamada recebida :

 

Página de chamada recebida

Pressione o botão com o ícone verde para aceitar a chamada e o ícone de telefone vermelho para rejeitá-la.

Vá para o terminal que está executando nosso aplicativo Flask e os dados de solicitação que a Twilio envia para nosso endpoint /handle_calls serão semelhante ao seguinte:

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

 

Conclusão

Neste tutorial, aprendemos a usar a API do Twilio Voice para fazer e receber chamadas telefônicas no navegador. Aprendemos a usar a estrutura do Flask para criar o aplicativo cliente que nos permitiu interagir com um dispositivo Twilio criado com o Twilio Client JS SDK.

O código de todo o aplicativo está disponível no seguinte repositório https://github.com/CSFM93/twilio-in-browser-calls.

Carlos Mucuho é um geólogo moçambicano que se tornou desenvolvedor e gosta de usar a programação para transformar ideias em realidade. https://github.com/CSFM93