DEV Community

Jonathan
Jonathan

Posted on

Automatizando lectura de mensajes de Discord con Python y Alexa Skills

Automatización

La automatización consiste en utilizar la tecnología para realizar tareas casi sin necesidad de realizar acciones humanas.

Alexa como asistente inteligente

Un asistente inteligente es un software que realiza tareas y ofrece servicios a un individuo, ayuda en las tareas en sistemas automatizando y realizando acciones con la mínima interacción hombre-máquina. Una persona se comunica usando la voz o texto y el asistente lo procesa, identifica, interpreta, ejecuta y responde utilizando la voz o medios visuales.

Alexa es el servicio de voz ubicado en la nube de Amazon disponible en dispositivos Amazon y dispositivos con Alexa integrada, se encuentran dispositivos con solo altavoces como Echo, Echo Dot, dispositivos con pantalla cómo Echo Show, Fire Stick TV y aplicaciones en Android, Windows, y muchos otros dispositivos más incluido televisores smart.

Skills de alexa y Rutinas

Una Skill son las habilidades que posee Alexa para realizar tareas, similar a una aplicación en un smartphone es una Skill de Alexa, a través de las Skills se permite realizar ciertas tareas, interactuar con otros dispositivos, estas habilidades son realizadas por desarrolladores y compañías para permitir a Alexa integrarse con otros Sistemas y dispositivos.

Son proyectos de software o funciones que se alojan en alguna nube y se ejecutan según la demanda de los usuarios, cada vez que se invoca la habilidad específica.

Las rutinas son un conjunto de reglas y ejecuciones que permiten ejecutar diferentes tareas que se programan a través de la Aplicación de Alexa, enlazando características de los diferentes dispositivos, skills, tiempo de ejecución de tareas, todas ellas permiten crear rutinas, tan simple como encender una alarma sonora a una hora específica, controlar la temperatura de un ambiente utilizando un termómetro y un ventilador, o recordarte comprar un objeto al llegar a un lugar, escuchar las noticias de diferentes proveedores con solo decirle “Quiero escuchar noticias”.

Las rutinas más las Skills permiten sacar gran parte del potencial del asistente inteligente, a tal punto de influir en la vida misma y las acciones del dia a dia de cada persona, facilitando el trabajo, ayudando a recordar o informar, ayudando a mantenerse en salud óptima, divertirse y disfrutar de un momento de ocio.

Serverless

La computación sin servidor o Serverless es un modelo en el cual los proveedores de cloud (AWS, Azure, GCP, etc.) son responsables de ejecutar un fragmento de código mediante la asignación dinámica de recursos. Cobrando solo por la cantidad de recursos utilizados para ejecutar el código. El código se envía al proveedor en la nube para ejecución en forma de una función, serverless puede ser denominado como “Funciones como servicio” o FaaS.

La computación sin servidor no significa que no hay un servidor en el cual se ejecuta sino que los proveedores se encargan del mantenimiento y solo se cobra por la ejecución de las funciones, tradicionalmente se suele pagar el mantenimiento de máquinas virtuales, plataformas como servicio aun cuando no se encuentran en uso por los clientes a través de solicitudes HTTP o Sockets y se encuentran en un estado Idle o de espera, con Serverless cuando no hay llamadas de ejecución, solicitudes HTTP o CRON, no hay recursos destinados a mantener activa la función en estado de espera, el costo se basa en la cantidad de recursos consumidos en cada ejecución por función.

Entre los más populares se encuentra:

  • Amazon Web Services: AWS Lambda
  • Microsoft Azure: Azure Functions
  • Google Cloud: Cloud Functions

AWS Lambda vs Azure Functions (Diferencias)

  • Configurabilidad AWS Lambda, necesita configurar la asignación máxima de memoria entre 128 mb a 3 GB, se ejecuta bajo Amazon Linux. Azure Functions, posee un plan único para todas las funciones, 1.5 GB de memoria, es posible ejecutar en Windows o Linux
  • Lenguajes AWS Lambda, permite JavaScript, Java, Python, C#, F#, PowerShell, Go y Ruby Azure Functions, permite JavaScript, Java, Python, C#, F#, y PowerShell, TypeScript
  • Costo El precio de ambos es de aproximada 0.20 USD por millón de solicitudes y 16 dólares por millón de GB/s AWS Lambda cobra por la capacidad total de memoria aprovisionada. Azure Functions mide el consumo de memoria promedio real de las ejecuciones.

Iniciando el proyecto

Crear el servidor de Discord (Si no se lo tiene)

Crear Servidor Discord

Crear el canal de texto de noticias a utilizar, si no se tiene, puede ser público o privado.
Para el acceso al canal se tiene que obtener su ID del canal el cual se obtiene desde el Discord, este dato será nuestro DISCORD_CHANNEL_ID con el que accederemos a un canal en específico para noticias.

Crear un canal

Obtener el ID del Canal

En caso de que no aparezca la opción para “Copiar ID” se debe activar desde Ajustes del Usuario > Avanzado > Modo Desarrollador

Se necesita crear una aplicación de discord para conectarse con el servidor, desde Discord Developer , en la cual crearemos una aplicación.

Crear Aplicación

Información general de la Aplicación

Una vez creada la aplicación debemos generar un bot dentro la aplicación el cual tendrá la capacidad de conectarse a los canales de Discord y obtener los mensajes.

Adicionar el Bot

Al generar el bot podemos obtener su Token de acceso con el cual podremos acceder mediante el bot, el cual lo utilizaremos más adelante.

Detalles del Bot

Para agregar el bot de prueba a su servidor de Discord puede utilizar el siguiente enlace modificando los parámetros de acuerdo a los permisos que requiera.

https://discord.com/oauth2/authorize?client_id=APPLICATION_ID&permissions=PERMISSION_INTEGER&scope=bot

APPLICATION_ID lo podremos obtener de la información general de la Aplicación, reemplazamos en la URL de autorización.

PERMISSION_INTEGER es el número referencial a los permisos que tendrá el bot usamos el 66560 para la lectura de mensajes.

Permisos del Bot

Para más información de los alcances existentes puede visitar: https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes

Adición del Bot al Servidor

Una vez agregado el bot al Servidor ya podemos realizar la lectura de los mensajes, en caso de que se encuentre en un canal privado primero se debe agregar al bot al canal para poder acceder a los mensajes.

Consumo del API de Discord para obtención de los mensajes de un canal

Teniendo ya el bot configurado podemos realizar la lectura de los mensajes utilizando solicitudes HTTP para lo cual podemos realizar desde python de la siguiente manera.

import requests
url = "https://discordapp.com/api/channels/DISCORD_CHANNEL_ID/messages?limit=10"
payload={}
headers = {'Authorization': 'Bot BOT_TOKEN'}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
Enter fullscreen mode Exit fullscreen mode

Crear la Skill

Ingresamos con nuestra cuenta a https://developer.amazon.com/alexa/console/ask desde el cual procederemos a crear nuestra Skill

Crear Skill

Al crear nuestra Skill tenemos que configurar su nombre, el idioma primario para el que utilizaremos el Español, el modelo para el Skill será Custom ya que será proporcionado por nosotros, y la ubicación de nuestro backend que también la proporcionaremos nosotros mismos.

Configurar Skill

Para la demostración utilizaremos el Template inicial el cual contiene funcionalidades básicas

Template Skill

Una vez terminado de construir ya tenemos la Skill inicial en la cual nosotros podemos realizar la configuración de los Intents, Samples y Slots para que Alexa pueda entender nuestra Skill y realizar un correcto funcionamiento.

Nombre de Invocación

Lo primero que debemos revisar es el nombre de invocación de nuestra Skill, es el conjunto de palabras que permite a Alexa identificar y ejecutar nuestra Skill, ingresando a Skill Invocation Name cambiamos el nombre para la invocación, y guardamos nuestro modelo.

Nombre Invocación Skill

Definición de Intents, samples y slots

Los Intents representan a las acciones que cumplen con la solicitud hablada de un usuario, para lo cual crearemos nuestro Intent desde el menú de Intents y nuestro nuevo intent será “NoticiasIntent”.

Intents de la Skill

De manera integrada en cada Skill tenemos 4 Intents que son para Cancelar, Ayuda, Detener ejecución y navegar entre nuestra Skill.

Posterior a crear nuestro intent debemos adicionar los Samples que representan la forma en la que los usuarios llaman a nuestro intent desde la Skill, y guardamos nuestro modelo

Samples de la Skill

Los Slots son argumentos que nos permiten obtener parámetros del usuario que se extraen de los Intent y se envían como datos adicionales. En nuestro caso utilizaremos un slot para obtener un número y obtener un límite de mensajes de acuerdo a necesidad del usuario.

Slot de la Skill

Una vez adicionado el Slot podemos agregar Samples que contengan nuestro Slot y tener llamadas con cantidades específicas de mensajes, quedándonos de la siguiente manera nuestro Intent con sus Samples y Slot!, y guardamos nuestro modelo Final

Intent Completo de la SKill

Hasta este punto ya tenemos la estructura que tendrá nuestra Skill y cómo será invocado mediante voz, ahora tenemos que construir nuestro Backend, una vez procesado y analizado la comunicación con el usuario se debe comunicar al Backend de la Skill que la tendremos alojada en Azure Functions

Creación de una Función con VS Code

Para crear el Backend primero abriremos VS Code e instalaremos la extensión de Azure Functions, esto para crear y publicar más fácilmente nuestras funciones https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions

Desde la pestaña de Azure se crea un nuevo Proyecto en la carpeta que contendrá nuestras Funciones.

Crear Función

Seleccionamos el Lenguaje “Python”

Seleccionar lenguaje

El entorno virtual a utilizar, se puede realizar un skip si no se utilizara entorno virtual, pero es recomendado siempre utilizar un entorno virtual.

Seleccionar entorno virtual

Seleccionamos el tipo de Template de nuestro proyecto, al ser un Backend de la Skill tiene el formato de Solicitud HTTP Trigger

Template de la Función

Como paso siguiente le proporcionamos el nombre a una función, en nuestro caso será “PotatoNoticiasTrigger”

Nombre de la función

Para este ejemplo utilizaremos un nivel de autorización anónimo

Autenticación de la función

Por último lo abrimos en el mismo Workspace o en una nueva ventana

Ventana para el proyecto

Con este último paso se realiza el generado del proyecto y tendremos una estructura similar de la siguiente forma:

.
├── .funcignore
├── .gitignore
├── host.json
├── local.settings.json
├── PotatoNoticiasTrigger
│   ├── function.json
│   ├── __init__.py
│   └── sample.dat
├── requirements.txt
└── .vscode
    ├── extensions.json
    ├── launch.json
    ├── settings.json
    └── tasks.json

Enter fullscreen mode Exit fullscreen mode

Para probar podemos ejecutar desde el proyecto la función

func host start
Enter fullscreen mode Exit fullscreen mode

Prueba Función

Al momento de iniciar se levanta la instancia de Azure Functions Core Tools, puede suceder una excepción

Value cannot be null. Parameter name: Provider error on func start
Enter fullscreen mode Exit fullscreen mode

Puede ser causada por la versión de la herramienta por lo cual una forma es verificar la versión en el archivo host.json
Para el proyecto actual se utiliza la version "[3.3.0, 4.0.0)",

"extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.3.0, 4.0.0)"
  }
Enter fullscreen mode Exit fullscreen mode

Adicionamos a las librerías de requerimientos los siguientes para el diseño de la Skill y consumo de solicitudes

requirements.txt

requests
ask-sdk-core
ask-sdk-webservice-support
Enter fullscreen mode Exit fullscreen mode

Instalaremos las librerías utilizando

pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Ahora podemos implementar la lógica de nuestra Skill sobre la función. Para lo cual primero empezaremos por añadir un archivo de configuración, para obtener en formato de variables de entorno los Keys e ID’s para proteger esa información de publicarla libremente.

config.py

import os
ALEXA_SKILL_ID = os.getenv("ALEXA_SKILL_ID", "")
DISCORD_CHANNEL_ID = os.getenv("DISCORD_CHANNEL_ID", "")
DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN", "")
Enter fullscreen mode Exit fullscreen mode

Estos 3 valores son los que usamos desde los distintos lugares para acceder a nuestros demás servicios.

ALEXA_SKILL_ID lo podemos obtener desde la consola de Alexa https://developer.amazon.com/alexa/console/ask en la cual podemos copiar nuestro Skill ID

DISCORD_CHANNEL_ID ya lo habíamos obtenido anteriormente de la creación del canal

DISCORD_BOT_TOKEN al igual lo obtuvimos con anterioridad al crear el BOT de Discord

Obtención de los N mensajes desde Discord

Ahora crearemos el método en python que nos permitirá obtener los “N” últimos mensajes de discord

discord.py

from typing import List
import requests
import json
from PotatoNoticiasTrigger.config import DISCORD_CHANNEL_ID, DISCORD_BOT_TOKEN
def get_n_messages(size: int = 5):
    # type: (int) -> List
    payload = {}
    url = "https://discordapp.com/api/channels/{0}/messages?limit={1}".format(
        DISCORD_CHANNEL_ID, size)
    headers = {
        'Authorization': 'Bot {0}'.format(DISCORD_BOT_TOKEN)
    }
    response = requests.request("GET", url, headers=headers, data=payload)
    if (response.status_code == 200):
        data = response.json()
        return data
    else:
        return None
Enter fullscreen mode Exit fullscreen mode

Implementación de las funciones básicas para un Skill en Python

Ahora debemos implementar las funciones básicas de la Skill e incluir todos los Intents, el que vamos a implementar “NoticiasIntent” como los por defecto como de excepciones para poder responder a posibles fallas de ejecución.

Los Intents son manejados en formato de clase, en el cual definimos de tipo AbstractRequestHandler, un método “can_handle” con el nombre del Intent a manejar en esa clase, y un método “handle” en el cual se realiza la ejecución de la acción para ser devuelta a Alexa a través de la Skill ya sea en voz o en texto visual.

Invocación

default_intents.py

class LaunchRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        print("Ejecucion de la Skill.")
        speech_text  = "Bienvenido a Potato Noticias, puedes decir últimas noticias"
        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("Potato Noticias", speech_text)).set_should_end_session(False)
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode
Ayuda

default_intents.py

class HelpIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        print("Ejecucion Intent Ayuda")
        speech_text = "Puedes decir últimas noticias, últimos 2 mensajes"
        handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
            SimpleCard("Potato Noticias - Ayuda", speech_text))
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode
Cancelar o Detener

default_intents.py

class CancelAndStopIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        print("Ejecución Intent Cancel o Stop")
        speech_text = "Hasta luego Atentamente Potato Noticias!"
        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("Potato Noticias", speech_text)).set_should_end_session(True)
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode
Terminar Sesión

default_intents.py

class SessionEndedRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        # any cleanup logic goes here
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode
Manejo de Excepciones

default_intents.py

class AllExceptionHandler(AbstractExceptionHandler):
    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        return True

    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        print(exception)
        speech = "Lo siento, No pude procesarlo. Puedes intentarlo nuevamente o decir Ayuda"
        handler_input.response_builder.speak(speech).ask(speech)
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode

El archivo final default_intents quedaria de la siguiente manera

from ask_sdk_core.dispatch_components import AbstractRequestHandler, AbstractExceptionHandler
from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
from ask_sdk_model.ui import SimpleCard


class LaunchRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        print("Ejecucion de la Skill.")
        speech_text = "Bienvenido a Potato Noticias, puedes decir últimas noticias"
        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("Potato Noticias", speech_text)).set_should_end_session(False)
        return handler_input.response_builder.response


class HelpIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        print("Ejecucion Intent Ayuda")
        speech_text = "Puedes decir últimas noticias, últimos 2 mensajes"
        handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
            SimpleCard("Potato Noticias - Ayuda", speech_text))
        return handler_input.response_builder.response


class CancelAndStopIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        print("Ejecucion Intent Cancel o Stop")
        speech_text = "Hasta luego Atentamente Potato Noticias!"
        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("Potato Noticias", speech_text)).set_should_end_session(True)
        return handler_input.response_builder.response


class SessionEndedRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        # any cleanup logic goes here
        return handler_input.response_builder.response


class AllExceptionHandler(AbstractExceptionHandler):
    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        return True

    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        print(exception)
        speech = "Lo siento, No pude procesarlo. Puedes intentarlo nuevamente o decir Ayuda"
        handler_input.response_builder.speak(speech).ask(speech)
        return handler_input.response_builder.response

Enter fullscreen mode Exit fullscreen mode

Implementación de la lógica para el Intent de solicitud de N mensajes

Hasta este punto ya hemos implementado los Intents básicos que debe poseer una Skill para facilitar a los usuarios su administración y manejo, ahora nos toca implementar de la misma manera nuestro propio Intent :D

Primeramente definiremos un generate_reponse que recibe un array de mensajes y los convierte en texto para ser escuchado y texto para ser leído desde un dispositivo.

noticias_intent.py

def generate_response(messages):
    # type: (List) -> Tuple[str,str]
    speak_out = ""
    speak_out_visible = ""
    itemCount = 1
    for message in messages:
        speak_out += 'Mensaje {0}: <break time="0.5s"/> {1}. <break time="1s"/>'.format(
            itemCount, message.get("content", ""))
        speak_out_visible += '{0}: {1}.\n'.format(
            itemCount, message.get("content", ""))
        itemCount += 1
    return speak_out, speak_out_visible
Enter fullscreen mode Exit fullscreen mode

En el caso de speak_out se añade la etiqueta break esto hace referencia al SSML (Speech Synthesis Markup Language), que es un estándar que permite a los asistentes de voz añadir pausas, efectos especiales de voz como susurro, o la pronunciación de palabras en un idioma extranjero de forma correcta

Después creamos el Handler para nuestro NoticiasIntent, en el cual accederemos a discord y obtendremos los mensajes les daremos un formato para ser escuchados y vistos y sean devueltos hacia el asistente.

noticias_intent.py

class NoticiasIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("NoticiasIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        cantidad = get_slot_value(handler_input, "cantidad")
        print(cantidad)
        if cantidad is not None:
            messages = get_n_messages(size=cantidad)
        else:
            messages = get_n_messages()
        if messages is None or len(messages) == 0:
            speak_output = "No se encontraron mensajes"
            visual_output = "No se encontraron mensajes :("
        else:
            speak_output,visual_output = generate_response(messages)
            speak_output += " Esos son todos los mensajes!."
            visual_output += " Esos son todos los mensajes!."
        handler_input.response_builder.speak(speak_output).set_card(
            SimpleCard("Potato Noticias", visual_output)).set_should_end_session(True)
        return handler_input.response_builder.response
Enter fullscreen mode Exit fullscreen mode

Hasta este punto ya tenemos los intents y nos faltaria integrarlo en el init de la función y realizar pruebas :D

Integración de los intents en el init de la función

Reemplazamos todo el contenido de nuestro con lo siguiente, que integra tanto nuestros intents por defecto como el Intent custom para lectura de discord.

__ init __.py

import json
import azure.functions as func
from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_webservice_support.webservice_handler import WebserviceSkillHandler
from PotatoNoticiasTrigger.default_intents import AllExceptionHandler, CancelAndStopIntentHandler, HelpIntentHandler, LaunchRequestHandler, SessionEndedRequestHandler
from PotatoNoticiasTrigger.noticias_intent import NoticiasIntentHandler
from PotatoNoticiasTrigger.config import ALEXA_SKILL_ID

def main(req: func.HttpRequest) -> func.HttpResponse:
    skill_builder = SkillBuilder()
    skill_builder.skill_id = ALEXA_SKILL_ID
    # Default Intents
    skill_builder.add_request_handler(LaunchRequestHandler())
    skill_builder.add_request_handler(HelpIntentHandler())
    skill_builder.add_request_handler(CancelAndStopIntentHandler())
    skill_builder.add_request_handler(SessionEndedRequestHandler())
    skill_builder.add_exception_handler(AllExceptionHandler())
    # Custom Intents
    skill_builder.add_request_handler(NoticiasIntentHandler())

    webservice_handler = WebserviceSkillHandler(skill=skill_builder.create())
    response = webservice_handler.verify_request_and_dispatch(req.headers, req.get_body().decode("utf-8"))
    return func.HttpResponse(json.dumps(response),mimetype="application/json")
Enter fullscreen mode Exit fullscreen mode

Ahora podemos probar el Intent y conectarlo a la Skill a través de Ngrok :D

Primeramente ejecutamos nuestra Función utilizando:

func host start
Enter fullscreen mode Exit fullscreen mode

Esta llamada nos genera una salida similar en la cual nos muestra la salida de nuestra Función en local similar a:

http://localhost:7071/api/PotatoNoticiasTrigger
Enter fullscreen mode Exit fullscreen mode

Ahora gracias a la herramienta Ngrok que nos permite abrir tuneles publicos hacia nuestros Endpoints locales ejecutaremos en otra terminal

ngrok http 7071
Enter fullscreen mode Exit fullscreen mode

Que nos generará una dirección temporal para que conectemos nuestro EndPoint de la siguiente manera:

Forwarding                    http://2dba-200-87-92-9.ngrok.io -> http://localhost:7071
Forwarding                    https://2dba-200-87-92-9.ngrok.io -> http://localhost:7071
Enter fullscreen mode Exit fullscreen mode

Utilizando la dirección HTTPS volvemos a la consola de Amazon y nos dirigimos a la parte de Endpoint en Default Region y añadimos nuestra dirección Ngrok más la ruta del Trigger teniendo algo similar a:

https://2dba-200-87-92-9.ngrok.io/api/PotatoNoticiasTrigger
Enter fullscreen mode Exit fullscreen mode

Seleccionamos la opción para el certificado SSL “My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority”

Endpoint Skill

Guardamos el Endpoint, ahora tenemos que compilar para lo cual nos dirigimos hacia el menú principal y seleccionamos la Opción Build Model para construir el modelo de la Skill.

Construir Modelo

Esperamos unos minutos a que termine la construcción del modelo.

Finalizado construcción

Pruebas de funcionamiento de la Skill integrada con el Endpoint Local

Desde la pestaña de Test podemos probar nuestra Skill apuntando hacia el Backend local en nuestro equipo. Habilitamos la Skill en modo Development y ya podemos probar

Pestaña de prueba Skill

Prueba de Ejecución

Prueba Visual

Publicación en Azure del Backend de la Skill

Hemos logrado hacer funcionar la Skill y que nos responda desde Local ahora podemos publicarlo en Azure :D y actualizar el EndPoint y volver a compilar nuestro modelo.
Desde VS Code seleccionamos la Opción Deploy to Function App

Deploy Function App

Seleccionamos la Suscripción de Azure que tengamos disponible, para usuarios nuevos pueden habilitar una suscripción de prueba y para estudiantes habilitar una suscripción de tipo estudiantil si se los permite sus cuentas institucionales.

Seleccionar Suscripción

Seleccionar una nueva función en Azure

Seleccionar nueva función

Ingresamos el nombre de nuestra nueva función

Nombre de la función

Seleccionamos la pila de ejecución que se recomienda que sea coincidente con la que tenemos en local

Runtime de la función

Seleccionamos la región donde se aloja nuestra función, en nuestro caso WEST US y se procede a crear nuestra función, esperamos unos minutos mientras se generan los servicios.

Implementación de la función
Tenemos que configurar nuestras variables de entorno con nuestros KEYS y ID Seleccionamos la función y abrimos en Portal

Open in Portal

Desde las configuraciones en Azure Añadimos 3 Configuraciones de Aplicación con el mismo nombre de nuestras variables de entorno

Añadir variables de entorno

Una vez adicionado nuestras variables de entorno procedemos a guardar donde se reiniciará la instancia de la función

Guardar Variables de Entorno

Ahora desde la parte de funciones podemos obtener la URL de nuestro nuevo ENDPOINT similar a

https://potatonoticias.azurewebsites.net/api/PotatoNoticiasTrigger
Enter fullscreen mode Exit fullscreen mode

el cual actualizaremos en el Endpoint en el portal de Alexa y volveremos a construir el modelo esperemos que termine y podemos probar nuestra Skill

De esta manera podemos concluir el contenido, consumir un canal de Discord, pasar por una Skill de Alexa y publicar el backend de nuestra función en Azure, todo un conjunto de elementos integrados para automatizar y permitir a las personas escuchar potato noticias desde cualquier dispositivo con Alexa.

Muchas gracias!!

Un agradecimiento a la comunidad de Python La Paz por permitirme dar una charla sobre esto, síganlos en sus redes sociales Facebook y Twitter y pregunten por su servidor de Discord, para conocer más sobre futuras charlas y eventos, también pueden ser expositores y enseñarnos su conocimiento para crecer con esta bonita comunidad.

El repositorio público en Github lo pueden encontrar aqui del proyecto Potato Noticias
La presentación se encuentra disponible en el siguiente enlace Presentación

Algunos enlaces en cuanto a contenido sobre todo lo expuesto.

Bibliografías

https://www.redhat.com/es/topics/automation
https://blog.grupocajamar.com/que-son-los-asistentes-virtuales-inteligentes/
https://developer.amazon.com/es-ES/alexa
https://alexaecho.es/las-mejores-ideas-de-rutina-y-habilidades-de-alexa-para-automatizar-tu-vida/
https://serverless-stack.com/chapters/es/what-is-serverless.html
https://es.wikipedia.org/wiki/Serverless_computing
https://iamondemand.com/blog/aws-lambda-vs-azure-functions-ten-major-differences

Top comments (0)