
Compartir:
Aaron era un defensor de los desarrolladores en Nexmo. Ingeniero de software experimentado y aspirante a artista digital, Aaron suele crear cosas con código o electrónica; a veces ambas cosas. Cuando está trabajando en algo nuevo, suele percibir el olor a componentes quemados en el aire.
Realización de una llamada telefónica de texto a voz con Django
Tiempo de lectura: 5 minutos
Entre las incesantes notificaciones que la gente recibe cada día, un teléfono que suena sigue siendo mucho más difícil de ignorar o pasar por alto.

Crea una sensación de urgencia que lo convierte en el medio perfecto para enviar mensajes críticos o urgentes, como por ejemplo códigos de autenticación de dos factores o notificaciones de servicios importantes.
En este tutorial, vamos a ver cómo se puede utilizar API de texto a voz de Nexmo para hacer llamadas salientes con Python y Django.
Requisitos previos
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Su servidor Django tendrá que ser accesible por la API Nexmo. Si lo estás ejecutando localmente, entonces tendrás que utilizar una herramienta como ngrok para exponerlo a la Internet pública.
Aplicaciones Nexmo
Una última cosa antes de empezar a escribir nuestro código Python/Django necesitamos entender Aplicaciones Nexmo. Cuando creamos una nueva aplicación Nexmo no sólo la usamos para almacenar datos de configuración como la URL de nuestro objeto de control de llamada Nexmo (NCCO)o dónde Nexmo debe enviar la información de eventos; también podemos utilizarla para generar nuestro par de claves pública/privada.
La seguridad es fundamental para nosotros, y no queremos que nadie pueda suplantar tu identidad o la de tu aplicación realizando llamadas desde tu número. Así que para ayudar a proteger nuestra Voice API utilizamos su clave privada para crear un Token Web JSON (JWT).
Así que antes de empezar vamos a crear una nueva aplicación Nexmoasociémosla a un número virtual y, a continuación, generemos y descarguemos nuestra clave privada.

Recuerda mantener tu clave privada a salvo; te recomiendo usar algo como Bóveda. Si por alguna razón crees que alguien ha comprometido tu clave privada, debes dejar de usarla inmediatamente y generar un nuevo par de claves pública/privada.
Creación de una OCN básica
La API de voz saliente requiere un answer_url. Cuando alguien conteste nuestra llamada, Nexmo recuperará nuestro archivo NCCO desde esta URL y ejecutará cualquier acción definida en él. Vamos a crear una aplicación Django para que podamos servir nuestro archivo JSON NCCO.
Vamos a instalar nuestras dependencias a través de pip. Siempre recomiendo mantener cada proyecto Python y sus dependencias en su propio entorno virtual.
pip install django nexmo
django-admin startproject ttsUna vez que tenemos nuestro proyecto Django necesitamos crear una nueva app, aquí será donde ocurra la mayor parte de nuestro desarrollo.
cd tts
python manage.py startapp outboundDespués de haber creado su nueva aplicación, no olvide añadirla a su directorio de aplicaciones. tts/settings.pyprobablemente debería editar su ALLOWED_HOSTS mientras editas tu configuración.
INSTALLED_APPS = [
…
'outbound'
]
ALLOWED_HOSTS = ["*"] # Never do this in production!Nuestra primera vista va a ser un archivo JSON estático. Vamos a hacer un directorio de plantillas dentro de nuestra nueva carpeta de la aplicación y añadir nuestro archivo JSON allí.
mkdir -p outbound/templates/outbound
touch outbound/templates/outbound/hello.jsonEdite su hello.json y añada la primera acción para su NCCO
[
{
"action": "talk",
"text": "Hello World from Nexmo"
}
]En el código anterior estamos definiendo una nueva lista que contiene una única acción talk que utilizará la función de texto a voz para leer la cadena text a nuestro interlocutor cada vez que responda a nuestra llamada saliente. Todavía tenemos que renderizar este archivo cada vez que recibamos una solicitud en nuestra ruta especificada. GET en nuestra ruta especificada, el genérico de Django TemplateView de Django es perfecto para esto. Como no estamos extendiendo el TemplateViewpodemos importarlo directamente a nuestro 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'
)),
]Una vez que haya editado su urls.py arranca tu servidor Django y comprueba que todo funciona visitando http://127.0.0.1:8000/hello/
python manage.py runserverCon suerte, verá el archivo NCCO que creamos anteriormente. Si no lo ves, comprueba si hay algún error en la pantalla de depuración del navegador o en el terminal.
Antes de que podamos hacer nuestra llamada saliente, necesitamos que nuestro servidor Django sea accesible por la API Nexmo. Recomendamos el uso de ngrok para esto si tienes problemas lee nuestra entrada de blog sobre cómo conectar tu servidor de desarrollo local a la API Nexmo usando un túnel ngrok.
ngrok http 8000Vamos a necesitar múltiples terminales para la siguiente parte, así que puede que quieras usar screen o tmux. Asegúrate de que todavía tienes tu servidor Django ejecutándose en un terminal y ngrok activo en otro. Vamos a hacer nuestra primera llamada saliente a través del REPL de Python, así que ejecuta python en otra ventana de terminal, ¡pero no olvides activar primero tu entorno virtual!
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})
Después de ejecutar los comandos anteriores mira tu terminal ngrok y deberías ver a Nexmo solicitando tu NCCO. Así que ese fue un ejemplo bastante simple, vamos a tratar de enviar un mensaje más emocionante.

Llamada saliente con datos dinámicos
Esta vez vamos a crear nuestra NCCO dinámicamente utilizando información de la API de Marvel. Antes de empezar con la siguiente parte, tendrás que registrarte para obtener una cuenta gratuita de desarrollador de Marveldespués de registrarme añadí mis credenciales de Marvel como variables de entorno.
export MARVEL_API_KEY='<YOUR API KEY>'
export MARVEL_PRIVATE_KEY='<YOUR PRIVATE KEY>'
Estos comandos crearán las variables de entorno en un sistema UNIX. Sin embargo, tendrá que exportarlas cada vez que reinicie su shell. Puede utilizar python-dotenv para hacer esto automático.
Vamos a crear una nueva ruta en nuestro urls.py para este nuevo 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())
]En su views.py importaremos y ampliaremos el archivo 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)
Acerca de nuestra vista personalizada

Repasemos lo que está ocurriendo en nuestro nuevo MarvelView. Tenemos que añadir dos datos disponibles en nuestro contexto al renderizar nuestra marvel.json plantilla, voice_name y marvel_message. El voice_name no es complicado; es el nombre de una voz sintetizada aleatoria en inglés de la selección selección ofrecida por la API de texto a voz de Nexmo. Para el marvel_message consultamos la API de Marvel en busca de todos los personajes de la serie Vengadores (2016 - presente). Después de ordenar ligeramente los datos, eliminamos cualquier etiqueta HTML errónea e ignoramos los personajes a los que les falta información. Concatenamos el nombre del personaje y su descripción en una sola cadena; esta es nuestra cadena de caracteres marvel_message.
Si ahora intentáramos acceder a http://127.0.0.1:8000/marvel/, obtendríamos una TemplateDoesNotExist excepción. En nuestra carpeta de plantillas, tenemos que crear un archivo marvel.json
[
{
"action": "talk",
"text": "{{ marvel_message|safe }}",
"voiceName": "{{ voice_name }}"
}
]Ahora podemos probar nuestro nuevo punto final y, con un poco de suerte, veremos información sobre un personaje cualquiera de los Vengadores. Datos proporcionados por 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"
}
]
Realización de nuestra llamada saliente Avengers de texto a voz
Esta vez, en lugar de usar el REPL de Python para hacer nuestra llamada, la envolveremos en un comando de gestión para que podamos hacer rápidamente una llamada saliente Marvel a cualquier número. Los comandos de gestión de Django requieren una estructura de directorios particular, vamos a crearla primero.
mkdir -p outbound/management/commands
touch outbound/management/__init__.py
touch outbound/management/commands/__init__.py
touch outbound/management/commands/marvel.pyAhora que tenemos los archivos en su lugar podemos escribir nuestro 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))
Este código es esencialmente el mismo que hicimos antes en la REPL, pero ahora lo hemos envuelto en un comando de gestión de Django. El nuevo comando marvel recibe dos argumentos: el número al que queremos llamar y el número virtual de Nexmo desde el que debe originarse la llamada.

¿Y ahora qué?

Cuando tenga una alerta urgentelas llamadas de texto a voz son perfectas, pero a veces no basta con saber que alguien ha respondido a la llamada. Combine la salida de texto a voz con IVR para asegurarse de que han recibido el mensaje.
Compartir:
Aaron era un defensor de los desarrolladores en Nexmo. Ingeniero de software experimentado y aspirante a artista digital, Aaron suele crear cosas con código o electrónica; a veces ambas cosas. Cuando está trabajando en algo nuevo, suele percibir el olor a componentes quemados en el aire.
