https://a.storyblok.com/f/270183/601381/4325448037/tts-superhero.png

Como fazer uma chamada telefônica com conversão de texto em fala usando o Django

Publicado em August 14, 2017

Tempo de leitura: 5 minutos

Entre as notificações incessantes que as pessoas recebem todos os dias, o toque de um telefone ainda é muito mais difícil de ignorar ou deixar passar despercebido.

Tony Stark looking stressed

Isso cria uma sensação de urgência, o que torna essa a maneira perfeita de enviar mensagens críticas ou urgentes, como códigos de autenticação de dois fatores ou notificações importantes sobre serviços.

Neste tutorial, veremos como você pode usar a API de conversão de texto em fala da Nexmo para fazer chamadas de saída com Python e Django.

Pré-requisitos

Seu servidor Django precisará estar acessível pela API do Nexmo. Se você estiver executando-o localmente, então você precisará usar uma ferramenta como o ngrok para expô-lo à internet pública.

Aplicativos da Nexmo

Uma última coisa antes de começarmos a escrever nosso código em Python/Django: precisamos entender Applications Nexmo. Quando criamos uma nova aplicação Nexmo, não a utilizamos apenas para armazenar dados de configuração, como a URL do nosso objeto de controle de chamadas do Nexmo (NCCO), ou para onde o Nexmo deve enviar informações de eventos; também podemos usá-la para gerar nosso par de chaves pública/privada.

A segurança é fundamental para nós, e não queremos que ninguém possa se passar por você ou pelo seu aplicativo fazendo chamadas a partir do seu número. Portanto, para ajudar a proteger nossa Voice API, usamos sua chave privada para criar um JSON Web Token (JWT).

Então, antes de começarmos, vamos criar um novo aplicativo Nexmo, associá-la a um número virtual e, em seguida, gerar e baixar nossa chave privada.

Voice application creation screencast

Lembre-se de manter sua chave privada em segurança; recomendo usar algo como Vault. Se, por qualquer motivo, você achar que alguém comprometeu sua chave privada, pare de usá-la imediatamente e gere um novo par de chaves pública/privada.

Criação de um NCCO básico

A Voice API de saída requer um answer_url. Quando alguém atender nossa chamada, a Nexmo recuperará nosso arquivo NCCO dessa URL e executará todas as ações definidas nele. Vamos criar um aplicativo Django para que possamos disponibilizar nosso arquivo JSON NCCO.

Vamos instalar nossas dependências usando o pip. Eu sempre recomendo manter cada projeto Python e suas dependências em seu próprio ambiente virtual.

pip install django nexmo
django-admin startproject tts

Assim que tivermos nosso projeto Django, precisamos criar um novo aplicativo; é nele que ocorrerá a maior parte do nosso desenvolvimento.

cd tts
python manage.py startapp outbound

Depois de criar seu novo aplicativo, não se esqueça de adicioná-lo ao seu tts/settings.py; provavelmente você também deve editar seu ALLOWED_HOSTS enquanto estiver editando suas configurações também.

INSTALLED_APPS = [

    'outbound'
]
ALLOWED_HOSTS = ["*"] # Never do this in production!

Nossa primeira visualização será um arquivo JSON estático. Vamos criar um diretório chamado “templates” dentro da pasta do nosso novo aplicativo e adicionar nosso arquivo JSON lá.

mkdir -p outbound/templates/outbound
touch outbound/templates/outbound/hello.json

Edite seu hello.json arquivo e adicione a primeira ação para o seu NCCO

[
    {
        "action": "talk",
        "text": "Hello World from Nexmo"
    }
]

No código acima, estamos definindo uma nova lista que contém uma única talk ação que utilizará a conversão de texto em fala para ler a text string para quem está atendendo, sempre que essa pessoa atender à nossa chamada. Ainda precisamos renderizar esse arquivo sempre que recebermos uma GET solicitação em nossa rota especificada; o genérico do Django TemplateView é perfeito para isso. Como não estamos estendendo o TemplateView, podemos importá-lo diretamente para nosso tts/urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

urlpatterns = [
    url(r'^hello/', TemplateView.as_view(
        template_name='outbound/hello.json',
        content_type='application/json'
    )),
]

Depois de editar seu urls.py inicie seu servidor Django e verifique se tudo está funcionando acessando http://127.0.0.1:8000/hello/

python manage.py runserver

Espero que você consiga ver o arquivo NCCO que criamos acima. Caso contrário, verifique a tela de depuração do navegador ou seu terminal para ver se há algum erro.

Antes de podermos fazer nossa chamada de saída, precisamos que nosso servidor Django esteja acessível pela API do Nexmo. Recomendamos usar o ngrok para isso, caso você esteja enfrentando problemas leia nossa postagem no blog sobre como conectar seu servidor de desenvolvimento local à API do Nexmo usando um túnel do ngrok.

ngrok http 8000

Vamos precisar de vários terminais para a próxima parte, então talvez seja uma boa ideia usar o screen ou o tmux. Certifique-se de que seu servidor Django ainda esteja em execução em um terminal e o ngrok ativo em outro. Vamos fazer nossa primeira chamada de saída por meio do REPL do Python; portanto, execute python em outra janela de terminal, mas não se esqueça de ativar seu ambiente virtual primeiro!

import nexmo
client = nexmo.Client(application_id='<VOICE APP ID>', private_key='private.key')
to_number = [{'type': 'phone', 'number': '<YOUR NUMBER>'}]
from_number = {'type': 'phone', 'number': '<NEXMO VIRTUAL NUMBER>'}
answer_url = ['https://<NGROK URL>/hello/']
client.create_call({'to': to_number, 'from': from_number, 'answer_url': answer_url})

Depois de executar os comandos acima, fique de olho no terminal do ngrok: você deverá ver o Nexmo solicitando seu NCCO! Esse foi um exemplo bem simples; vamos tentar enviar uma mensagem mais interessante.

Is that the best you can do?

Chamada de saída com dados dinâmicos

Desta vez, vamos criar nosso NCCO dinamicamente usando informações da API da Marvel. Antes de começarmos a próxima parte, você precisará se cadastrar para obter uma account gratuito de desenvolvedor da Marvel; após o cadastro, adicionei minhas credenciais da Marvel como variáveis de ambiente.

export MARVEL_API_KEY='<YOUR API KEY>'
export MARVEL_PRIVATE_KEY='<YOUR PRIVATE KEY>'

Esses comandos criarão as variáveis de ambiente em um sistema UNIX. No entanto, você precisará exportá-las toda vez que reiniciar seu shell. Talvez seja interessante usar python-dotenv para automatizar esse processo.

Vamos criar uma nova rota em nosso urls.py para esse novo endpoint NCCO.

from django.conf.urls import url
from django.views.generic import TemplateView
from outbound.views import MarvelView

urlpatterns = [
    url(r'^hello/', TemplateView.as_view(
        template_name='outbound/hello.json',
        content_type='application/json'
    )),
    url(r'^marvel/', MarvelView.as_view())
]

No seu views.py vamos importar e estender o TemplateView.

import os
from hashlib import md5
from time import time
import random
import requests
from django.utils.html import strip_tags
from django.views.generic import TemplateView


class MarvelView(TemplateView):
    template_name = 'outbound/marvel.json'
    content_type = 'application/json'

    @staticmethod
    def get_marvel_data():
        marvel_api_url = 'https://gateway.marvel.com:443/v1/public/characters'
        private_key = os.environ['MARVEL_PRIVATE_KEY']
        api_key = os.environ['MARVEL_API_KEY']

        # Create Marvel API request params
        timestamp = str(time())
        hashed_key = md5(
            str(timestamp + private_key + api_key).encode('utf-8')
        )

        # Fetch Avengers data from Marvel API
        response = requests.get(
            marvel_api_url,
            params={
                'series': '22547',  # Avengers (2016 - Present)
                'apikey': api_key,
                'ts': timestamp,
                'hash': hashed_key.hexdigest()
            },
            headers={
                'Accept': 'application/json'
            }
        )
        marvel_response_data = response.json()

        # Some characters don't have descriptions, ignore those characters
        return [{
            'name': x['name'],
            'description': x['description']
        } for x in marvel_response_data['data']['results'] if x['description']]
    
    @staticmethod
    def random_voice_name():
        # https://developer.nexmo.com/api/voice/ncco#voice-names
        return random.choice([
            'Salli', 'Joey', 'Nicole', 'Russell', 'Amy', 'Brian', 'Emma',
            'Gwyneth', 'Geraint', 'Raveena', 'Chipmunk', 'Eric', 'Ivy', 
            'Jennifer', 'Justin', 'Kendra', 'Kimberly',
        ])

    # Add our Marvel data to the templete context
    def get_context_data(self, **kwargs):
        marvel_data = self.get_marvel_data()
        random_character = random.choice(marvel_data)

        kwargs['voice_name'] = self.random_voice_name()

        # Concat our character name & bio together to act as our voice message
        # Also remove any errant HTML tags from Marvel text
        kwargs['marvel_message'] = "{name} - {description}".format(
            name=strip_tags(random_character['name']),
            description=strip_tags(random_character['description'])
        )

        return super(MarvelView, self).get_context_data(**kwargs)

Sobre nossa visualização personalizada

Looking at the code

Vamos ver o que está acontecendo em nosso novo MarvelView. Precisamos adicionar dois dados disponíveis em nosso contexto ao renderizar nosso marvel.json modelo, voice_name e marvel_message. O voice_name é simples; trata-se do nome da voz sintetizada aleatória em inglês, da seleção oferecida pela API de conversão de texto em fala da Nexmo. Para o marvel_message , consultamos a API da Marvel para obter todos os personagens da série “Os Vingadores” (2016 – presente). Depois de limpar um pouco os dados — removendo quaisquer tags HTML indesejadas e ignorando personagens com informações ausentes —, concatenamos o nome do personagem e sua descrição em uma única sequência de caracteres; esse é o nosso marvel_message.

Se tentássemos acessar http://127.0.0.1:8000/marvel/ agora, encontraríamos uma TemplateDoesNotExist exceção. Na nossa pasta de modelos, precisamos criar um marvel.json

[
    {
        "action": "talk",
        "text": "{{ marvel_message|safe }}",
        "voiceName": "{{ voice_name }}"
    }
]

Agora podemos testar nosso novo endpoint e, com sorte, devemos ver algumas informações sobre um personagem aleatório dos Vingadores! Dados fornecidos pela Marvel. © 2014 Marvel

[
    {
        "action": "talk",
        "text": "Taskmaster - Taskmaster first exhibited his unusual ability, called 'photographic reflexes,' which allowed him to mimic the motion of anyone he saw, when he was a young boy.",
        "voiceName": "Emma"
    }
]

Avengers Assemble!

Realizando nossa chamada de saída com o recurso de conversão de texto em fala do Avengers

Desta vez, em vez de usar o REPL do Python para fazer nossa chamada, vamos incorporá-la a um comando de gerenciamento para que possamos fazer rapidamente uma chamada de saída pelo Marvel para qualquer número. Os comandos de gerenciamento do Django exigem uma estrutura de diretórios específica; vamos criá-la primeiro.

mkdir -p outbound/management/commands
touch outbound/management/__init__.py
touch outbound/management/commands/__init__.py
touch outbound/management/commands/marvel.py

Agora que os arquivos já estão no lugar, podemos escrever nosso marvel.py

import nexmo
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = 'Random Avenger character as a TTS phonecall'

    def add_arguments(self, parser):
        parser.add_argument('to_number', type=str)
        parser.add_argument('from_number', type=str)

    def handle(self, *args, **options):
        
        client = nexmo.Client(
            application_id='<YOUR NEXMO VOICE APP ID>',
            private_key='private.key'
        )

        to_number = [{'type': 'phone', 'number': options['to_number']}]
        from_number = {'type': 'phone', 'number': options['from_number']}
        answer_url = ['https://<NGROK URL>/marvel/']
        
        response = client.create_call({
            'to': to_number,
            'from': from_number,
            'answer_url': answer_url
        })

        self.stdout.write(str(response))

Esse código é basicamente o mesmo que fizemos antes no REPL, mas agora o colocamos dentro de um comando de gerenciamento do Django. O novo marvel comando recebe dois argumentos: o número para o qual queremos ligar e o número virtual da Nexmo de onde a ligação deve partir.

Screencast of the Marvel command making a call

E agora?

What's next?

Quando você receber um alerta urgente, as chamadas com conversão de texto em fala são perfeitas, mas às vezes não basta apenas saber que alguém atendeu a chamada. Combine chamadas de texto-para-voz com o IVR para garantir que a mensagem foi recebida.

Compartilhar:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Aaron BassettEx-funcionários da Vonage

Aaron era um defensor de desenvolvedores na Nexmo. Engenheiro de software experiente e aspirante a artista digital, Aaron costuma ser visto criando coisas com código ou com eletrônica; às vezes, os dois. Geralmente, dá para perceber quando ele está trabalhando em algo novo pelo cheiro de componentes queimando no ar.