Tutorial - Alexa Skills
04/06/2020
PET - Tecnologia em Eletrônica e Computação
Itajubá, Minas Gerais
Visão geral 2
Objetivos 2
O que é Alexa? 3
O que são Alexa Skills? 3
Por que desenvolver uma skill para Alexa? 3
Facilidade de acesso 4
Velocidade e eficiência 4
Que tipo de skill pode-se criar? 4
Skills de Casa Inteligente 4
Skills de Flash Briefing 4
Skills de Vídeo 5
Skills de Música 5
Funcionamento de uma skill 5
Passos para se construir uma skill 6
Passo 1: Design 6
Passo 2: Construção 7
Passo 3: Teste 7
Passo 4: Certificação e Lançamento 7
Alguns conceitos de JavaScript 8
Output 9
Var, let e const 9
Função 10
Array 11
Objeto 14
Factory functions 16
Como funciona uma Alexa Skill 19
Desenvolvendo uma Skill - Quiz de Eletrônica Analógica 21
Registro 21 Conhecendo a interface 21 Fase de desenvolvimento 25 Fase de teste 48
Visão geral
Neste documento, irá ser apresentado o que é uma Alexa Skill, qual a necessidade de desenvolver aplicações de voz, além de um tutorial básico para o desenvolvimento deste tipo de aplicação, de forma simples.
Objetivos
1. Contextualização da necessidade e viabilidade de aplicações com foco na voz. 2. Apresentação da interface de desenvolvimento.
3. Apresentação de alguns conceitos de JavaScript, que serão necessários para a implementação das atividades.
4. Desenvolvimento, do zero, de uma Alexa Skill.
O que é Alexa?
Alexa é o serviço de voz baseado em nuvem desenvolvido pela Amazon (Alexa Voice Service) e está disponível em diversos de dispositivos também da Amazon e de terceiros. Com a Alexa pode-se criar experiências de interação com uma máquina de maneira mais natural e intuitiva, até mesmo de uma forma mais “humana” do que as interações convencionais que temos com celulares e computadores.
Através do Alexa Voice Service (AVS) a Amazon simplificou o desenvolvimento de serviços de voz para os desenvolvedores por meio de abstrações e de funções Lambda.
Para o desenvolvimento de Alexa Skills, a própria Amazon oferece uma coleção de ferramentas, APIs (interface de programação de aplicações), soluções de referência e documentação, tanto online quanto offline.
O que são Alexa Skills?
Skills (“habilidades” do inglês) são como aplicativos para Alexa que fornecem novos conteúdos para os clientes aproveitando da melhor maneira os recursos da AVS disponíveis. As skills permitem que os usuários utilizem suas vozes para realizar tarefas diárias como verificar notícias, ouvir músicas, jogar um jogo. Empresas e indivíduos podem desenvolver skills e publicá-las na Alexa Skill Store.
O Alexa Skills Kit (ASK) fornece APIs e ferramentas que você pode usar para criar skills para Alexa.
Por que desenvolver uma skill para Alexa?
À medida em que os serviços de voz estão ficando cada vez mais populares, como Alexa, Siri, Google Assistant, os consumidores utilizam mais e mais interfaces de usuário de voz (voice user interfaces - VUIs) para jogar, receber notícias e controlar um crescente número de dispositivos domésticos inteligentes.
Facilidade de acesso
As VUIs são naturais, conversacionais e voltadas completamente para o usuário. Uma experiência de voz permite que os usuários expressem suas intenções de maneiras variadas. Ela deve ser rica e flexível, por isso desenvolver para voz é completamente diferente do que criar interfaces de usuário gráficas (GUIs) para Web ou dispositivos móveis.
Quanto mais fácil for utilizar uma skill, mais velocidade e eficiência ela oferecerá.
Velocidade e eficiência
Como a utilização da voz é muito mais natural e intuitiva, e consequentemente mais rápida e eficiente, as skills da alexa utilizam dessa vantagem para tornar as tarefas mundanas ou habituais ainda mais rápidas.
Que tipo de skill pode-se criar?
O ASK permite a criação de diversos tipos de skill, oferecendo quatro modelos de interação pré-definidos, ou pode-se criar uma skill inteiramente personalizada. os modelos pré-construídos incluem solicitações e enunciados pré-definidos para acelerar a construção.
Abaixo estão quatro exemplos de skills:
Skills de Casa Inteligente
Utilize a API para skills de Smart Home para criar uma skill de casa inteligente. Esse tipo de skill controla dispositivos domésticos inteligentes, como câmeras, luzes, termostatos e smart TVs. A Smart Home Skill API oferece menos controle sobre a experiência do usuário, porém simplifica seu desenvolvimento, pois não é necessário a criação da VUI por conta própria.
Skills de Flash Briefing
Use a API para skills de Flash Briefing para fornecer aos seus clientes notícias e outros conteúdos curtos. Como desenvolvedor de skills, você define os feed do conteúdo para o flash briefing solicitado, podendo conter conteúdo de áudio reproduzido para o usuário ou conteúdo de texto lido para o usuário.
Skills de Vídeo
Utilize a API de skills de vídeo para fornecer conteúdo de vídeo, como programas de TV e filmes. Como desenvolvedor você define as solicitações que as skills podem manipular, tais como pesquisar e reproduzir conteúdos de vídeos ou como os resultados da pesquisa são exibidos nos dispositivos.
Skills de Música
Use a API de skills de música para fornecer conteúdo de áudio, como músicas, listas de reprodução ou estações de rádio. Essa API processa palavras que um usuário pode utilizar para solicitar e controlar um conteúdo de áudio. Tais palavras se transformam em solicitações e são enviadas para a sua skills, onde ela é processada e respondida adequadamente.
Simples funcionamento de uma skill
A seguir, observe um fluxograma que demonstra de forma simples como Alexa funciona.
Fonte: Site da Amazon1
1 Disponível em:
<https://developer.amazon.com/pt-BR/alexa/alexa-skills-kit/get-deeper/tutorials-code-samples/build-an-engagi ng-alexa-skill/module-1>
Vocabulário: 1 - Áudio
2 - Reconhecimento de fala
Entendimento da Linguagem Natural Texto para Fala
3 - Intenção Função Lambda Resposta 4 - Visual Explicação do fluxograma:
1 - Para iniciar a skill, o usuário diz: “Alexa, abra (nome da skill)”.
2 - O dispositivo envia o enunciado para o serviço Alexa na nuvem onde o enunciado é processado através de reconhecimento automático de fala, para a conversão em texto e compreensão de linguagem natural para reconhecer a intenção de texto.
3 - A Alexa envia uma solicitação JavaScript Object Notation (JSON) para processar a intenção de uma função do AWS Lambda na nuvem. A função Lambda age em um back-end e executa o código para processar a intenção.
Passos para se construir uma skill
Passo 1: Design
Comece projetando o modelo de interação de voz da sua skill. Como projetar para voz é diferente do que projetar para dispositivos móveis ou baseado em web, deve-se pensar em todas as maneiras diferentes nas quais um usuário pode interagir com a skill. Para fornecer uma experiência de voz fluida e natural, é importante criar scripts e representar as diferentes formas que um usuário pode falar com a Alexa.
Pode-se também construir skills multimodais, oferecendo experiência de voz e visual, porém será necessário pensar em diferentes fluxos de navegação.
Passo 2: Construção
Quando seu modelo de interação estiver pronto, crie os enunciados, intenções e variáveis (slots) no Console do Portal dos Desenvolvedores de Alexa.
O modelo de interação é salvo no formato JSON e você pode editar o modelo com qualquer ferramenta de edição. Depois que seu modelo de interação JSON estiver pronto, crie a função backend Lambda no AWS Management Console.
Selecione a linguagem de programação de sua escolha e o kit de desenvolvimento de software ASK (SDK) correspondente e comece a codifica sua skill. O Lambda suporta as linguagens de programação Java, Go, PowerShell, Node.js, C# Python e Ruby.
Você pode criar e hospedar a maioria das skills gratuitamente com o AWS Lambda, que é gratuito para o primeiro milhão de chamadas por mês . Quando a função backend do Lambda estiver pronta, integre a função à sua skill e teste-a no console do desenvolvedor da Alexa.
Passo 3: Teste
O console de desenvolvedor da AWS possui um simulador de Alexa integrado, que é semelhante ao teste em um dispositivo habilitado para Alexa.
Depois de testar a sua skill com o simulador, recomenda-se que colete feedback de usuários para resolver problemas e fazer melhorias antes de enviar sua skill para a
certificação.
Passo 4: Certificação e Lançamento
Depois de testar sua skill, envie-a para certificação. Uma vez que sua skill passar pela certificação, ela será publicada na Alexa Skill Store para que qualquer pessoa possa descobri-la e usá-la.
Alguns conceitos de JavaScript
Para o desenvolvimento de uma Alexa Skill é necessário o conhecimento de alguns conceitos de NodeJs, uma vez que nesse tutorial será utilizado esse ambiente de execução de JavaScript. Vale ressaltar que é possível criar uma aplicação Alexa utilizando outras linguagens de programação, porém neste documento, iremos focar nesta linguagem.
Os códigos que serão apresentados abaixo poderão ser testados diretamente no navegador que você utiliza, sem a necessidade da instalação de nenhuma ferramenta, uma vez que JavaScript foi inicialmente criada para rodar no navegador. Para isso, basta pressionar a tecla F12, que um console será aberto, como na imagem a seguir
Sempre que um bloco de código é executado neste console, o determinado “comando”, que pode ser uma atribuição de uma variável, fica armazenada na sessão atual do navegador. Se desejar redefinir uma const, por exemplo, você deverá simplesmente recarregar a página, que o cache é limpado automaticamente.
Caso prefira, os códigos também poderão ser testados através de qualquer compilador online de JavaScript, como por exemplo o Repl.it, disponível no seguinte endereço https://repl.it/languages/nodejs
Output
Para observar a saída dos testes a seguir utilizaremos a função log do objeto console, que permite o envio de dados para o console do navegador. Se você ainda não sabe que é objeto e/ou função, não se preocupe, porque isso isso será abordado posteriormente. Portanto, a linha de código ficará da seguinte maneira:
console.log("Olá Mundo");
Var, let e const
Primeiramente, se você vem de uma linguagem tipada, como Java e C++, essa poderá ser a principal “estranheza”, já que JavaScript possui tipagem dinâmica, ou seja o próprio JS define que tipo de variável você está trabalhando no momento, em tempo de execução. Desse modo, não é necessário a utilização de palavras chave como int e char na definição de uma variável. Observe no código abaixo, que é possível até mesmo alterar o tipo de uma variável, depois de definida, sem nenhum tipo de erro.
let a = 2; console.log(a); a = "agora eh um texto"; console.log(a);
Para a definição de variáveis, existem três palavras chave da linguagem: var, let e const. As palavras var e let são utilizadas para variáveis que podem sofrer modificações ao longo da execução do programa. Já const não sofre alteração, uma vez definido seu conteúdo, deve permanecer o mesmo até o final da execução da aplicação. A diferença entre var e let é o escopo, ou seja, onde ela é “visível”, para ser lida e/ou modificada. Var, tem escopo global, ou seja, uma vez definida, pode ser acessada em qualquer lugar do código; já let possui escopo de bloco, ou seja, somente é visível no lugar em foi declarada, seja uma condição, função ou laço de repetição. Agora teste o seguinte código em seu console:
const constante = [1,2,3,4]; constante.push(5);
console.log(constante);
Observou que interessante? Constante é uma const, e mesmo assim, foi possível “modificar” seu conteúdo utilizando a função push(), disponível nos arrays. A explicação disso é que na realidade, a variável não foi alterada, pois quando ela foi declarada, o motor de JavaScript automaticamente reservou, em um endereço específico, um espaço de memória para o array, e esse endereço de fato não foi alterado.
Função
Como utilizado anteriormente, a partir da palavra push(), uma função é um bloco de construção, fundamental para um desenvolvedor JavaScript. Ela pode ser definida como um conjunto de instruções, que executa uma determinada tarefa e retorna ou não algum dado. Para usar uma função, é necessário defini-la e depois chamá-la. Vamos ver isso a partir de um exemplo: function soma (a, b) { return a + b; } console.log(soma(2,2));
Nesse exemplo, declaramos uma função chamada “soma”, que recebe dois parâmetros e retorna a soma deles. Que pode ser visualizada a partir da chamada da função utilizando o console.log, onde foi definindo a e b, valendo 2. Partindo do mesmo exemplo anterior, teste o seguinte bloco de código:
function soma(a, b) { return a+b; } console.log(soma(2));
Agora, analisando o exemplo acima, é possível observar que, mesmo chamando a função com apenas um argumento, ou seja: 2, a função foi executada normalmente, porém retornando um resultado indesejado: NaN. Mesmo assim, é possível chegar em uma conclusão importante: uma mesma função pode receber, ou não, os argumentos que foram definidos, ou seja, não é necessário criar uma nova função, para realizar uma mesma tarefa que necessite de menos argumentos, basta determinar a lógica para tal, diretamente no interior do bloco da função. Neste caso, obtivemos como retorno a palavra
NaN - “Not a Number” -, porque o motor de execução JavaScript está realizando a soma de 2 + Undefined, ou seja, de um número com algo indefinido.
Dessa maneira, ao utilizar uma função, chamando-a com menos argumentos, os argumentos utilizados serão sempre, contabilizados da esquerda para a direita. Assim, no exemplo acima, quando foi chamado a função passando 2 como parâmetro, a variável que recebeu como atribuição esse valor, foi a.
Uma função muito utilizada dentro do JavaScript, é a famosa callback. Uma Função que é passada, ou não, como argumento para outra função, e é chamada após o acontecimento de algum evento. O exemplo a seguir utiliza-se de recursos de manipulação da DOM, não sendo foco neste tutorial, mas a partir dele é possível consolidar essa ideia de callback, uma vez que a função, que possui apenas um console.log() em seu escopo, é chamada apenas quando existe um clique - um evento - dentro da página do navegador em que você estiver.
Depois de executar no console o bloco de código a seguir , clique em qualquer região da página, fora do console, para observar a saída do algoritmo.
document.getElementsByTagName('body')[0].onclick = function (e) {
console.log('A callback foi chamada.'); }
Array
Agora será definido o conceito de Array, já utilizado anteriormente, porém não explicitado como é seu funcionamento. Esse objeto é definido por um par de colchetes, de modo que é possível utilizá-lo como uma lista de elementos; e diferentemente de outras linguagens, não é necessário explicitar seu tamanho, isso é feito dinamicamente. Outra coisa interessante é que, é possível atribuir mais de um tipo de dado para um mesmo array, mas tenha cuidado com isso, como a linguagem possui tipagem dinâmica, misturar tipos pode ser um problema, já que a engine não deve acusar nenhum erro e facilmente você pode se perder em seu código.
let array = [1,2,"ola"];
console.log(array);
Um array pode ser percorrido, utilizando a estrutura de colchetes, a partir de um índice, que vai de zero até o tamanho do array menos um. Por exemplo, tendo em vista que já foi declarado a variável array no exemplo anterior, execute a seguinte linha de código. Onde obteremos, como resposta, a palavra “olá”.
console.log(array[2]);
Os arrays possuem uma série de funções pré-definidas, que podem ajudar muito a manipular esse tipo de dado. Veremos algumas das principais a seguir, mas tenha em mente que existem diversas outras, que podem ser encontradas na documentação do JavaScript, caso você deseje aprofundar-se no assunto. Todos os métodos a seguir, percorrem o array, a fim de realizar algum tipo de manipulação sobre ele.
● forEach: Os principais argumentos que este método recebe é: valorAtual, ou seja, elemento atual durante a iteração no array; além do indiceAtual, que representa o índice do valorAtual dentro do vetor. Além disso, essa função não possui nenhum retorno. Esse método chama uma callback, para cada elemento percorrido. Observe o exemplo a seguir, onde é chamada a callback imprimeElemento para cada elemento do vetor:
let carros = ["carro1", "carro2", "carro3"];
function imprimeElemento(valorAtual, indice) { console.log(indice, valorAtual);
}
carros.forEach(imprimeElemento);
Observe que, para este exemplo, não foi necessário a utilização dos parênteses, a fim de chamar a função callback imprimeElemento, uma vez que internamente, a função forEach já os passa automaticamente.
● map: Esse método recebe basicamente os mesmo argumentos do método anterior, com a diferença que agora ele retorna um novo array, modificado ou não. Assim, ao utilizar a função map(), é possível alterar o conteúdo de um vetor de maneira simples, sem perder o valor da variável utilizada para chamá-la. Observe o exemplo abaixo, onde um novo vetor é criado, adicionando sobrenome aos valores do array inicial.
const nomeSemSobrenome = ["João", "Maria"];
function adicionaSobrenome(nomeAtual) { return `${nomeAtual} Silva`;
}
const nomeComSobrenome = nomeSemSobrenome.map(adicionaSobrenome);
console.log(nomeComSobrenome);
Observe que a função callback, no caso acima, possui apenas um argumento. Isso não gera problema algum para o compilador JavaScript, uma vez que ele identifica que os outros argumentos não estão sendo utilizados no momento. Observe também, que no retorno da função foi utilizado uma estrutura que utiliza acentos graves (` `) - em vez de aspas simples ou duplas -, denominada template literal. Através desse tipo de definição de string, é possível “embutir” variáveis dentro da string de maneira simples, sem necessitar da utilização de operadores de concatenação. Além disso, a partir dessa estrutura, é possível declarar uma string que ocupe multi-linhas.
Análise o exemplo abaixo, onde foi declarado strings utilizando os três tipos possíveis, com aspas simples, duplas e acentos graves, além da utilização da interpolação de uma string dentro de outra, fazendo-se uso dos placeholders ${expressão}:
const cumprimento1 = 'Bom dia!';
const cumprimento2 = "Boa tarde!";
const frase = `${cumprimento1} ${cumprimento2}
Boa noite!`;
console.log(frase);
● filter: Recebe basicamente os mesmo argumentos do método anterior, também retornando um novo array. Nesse caso, a tarefa da callback é selecionar, ou melhor, filtrar, os elementos que estarão presentes no novo array.
const notas = [6.7, 5.9, 7.4, 2.5];
function notasMaioresOuIguaisA6 (nota) { if(nota >= 6) return nota
}
const notasAprovadas = notas.filter(notasMaioresOuIguaisA6)
console.log(notasAprovadas)
● reduce: O método reduce funciona de maneira diferente dos anteriores. Nesse caso, ele literalmente retorna um array reduzido - como o próprio nome já diz -, em um único elemento apenas. Os principais argumentos passados para sua callback são, acumulador e valorAtual. O acumulador representa o último valor retornado pela callback, já o valorAtual representa o elemento que está sendo processado no array, no momento. No exemplo abaixo é realizado a soma de todos os elementos do vetor, utilizando-se desse recurso.
const valores = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 10];
function somaTudo (acumulador, valorAtual) { return acumulador + valorAtual;
}
const soma = valores.reduce(somaTudo)
console.log(soma)
Além desses métodos exemplificados anteriormente, existem diversos outros interessantes que podem ser usados para a manipulação de um array, como por exemplo o método push(elemento), que insere um novo elemento ao final do vetor.
Objeto
Se você já possui conhecimento de alguma linguagem OO, esse conceito já deve estar bem consolidado; porém se você nunca ouviu falar do que venha a ser um Objeto em uma linguagem de programação, vale a pena defini-lo rapidamente aqui, focando é claro, em JavaScript.
Essa estrutura pode ser definida como a representação de algum modelo maior. De modo a utilizar-se em sua declaração, dados e funções, onde os dados são chamados atributos e as funções são chamadas de métodos. Por exemplo, se desejássemos criar um
Objeto, que seja um número complexo, composto por sua parte real e por sua parte imaginária: facilmente podemos representar isso utilizando a notação de objeto, definido como atributo duas variáveis: parteReal e parteImaginaria. Além disso, nessa representação de um número complexo, poderia também, ser definido métodos, para que seja possível manipular esse tipo de número, como por exemplo, realizar a conversão da forma retangular para a forma polar.
const numeroComplexo = { parteReal: 5.0,
parteImaginaria: 5.0,
formaRetangular: function() {
console.log(`${this .parteReal} + i${this.parteImaginaria}`); },
formaPolar: function() {
let modulo = Math.sqrt(Math .pow(this.parteReal,2) +
Math.pow(this.parteImaginaria, 2));
let angulo = Math.atan( this.parteImaginaria / this.parteReal) *
57.2958;
console.log(`Módulo: ${modulo}, Ângulo: ${angulo}`); }
}
numeroComplexo.formaRetangular(); numeroComplexo.formaPolar();
No exemplo acima, foi criado um objeto, chamado numeroComplexo. Essa é a maneira de se declarar um objeto literal dentro da linguagem JavaScript, utilizando-se do par de chaves { }. Dentro do objeto, as linhas de declaração de atributos e métodos devem ser separadas por vírgula (,). Nesse caso, foi utilizado um segundo objeto embutido da linguagem, o Math, a fim de utilizar-se de seus métodos para cálculo de raiz quadrada e potenciação. Para o cálculo do ângulo polar, o arco tangente foi multiplicado por 57.2958, uma vez que esse método retorna o ângulo em radianos e foi realizado a conversão para graus. Outra coisa importante para se destacar no exemplo acima, é que não foi utilizado as palavras chaves private, public e protected, disponível em outras linguagens orientadas a objeto, uma vez que elas não existem em JavaScript, levando a conclusão que os atributos e métodos declarados dentro de um objeto, de certa forma, possuem acesso público. Desse modo, os atributos: parteReal e parteImaginaria, podem ser modificados de fora do objeto, simplesmente utilizando-se de:
Para acessar os atributos e métodos de um objeto, internamente, utiliza-se da palavra reservada this, para explicitar o escopo você está trabalhando.
Para utilizar-se de qualquer objeto literal criado, basta seguir a seguinte nomenclatura: nome do objeto + notação ponto + nome do atributo ou método.
Se você reparou bem na estrutura criada anteriormente, os atributos parteReal e parteImaginaria foram definidos diretamente dentro do objeto, mas e se desejássemos aproveitar esse código, criado diversos números complexos diferentes? A resposta para essa pergunta está nas factory functions.
Factory functions
Um função fábrica, traduzindo literalmente para o português, representa uma função que tenha como principal objetivo, retornar um objeto. Isso mesmo, é possível retornar um objeto a partir de uma função, assim como enviar uma função como parâmetro para outra função, como já visto anteriormente na sessão de funções. Dessa maneira, é plausível a ideia de reaproveitamento de código, tendo em vista que será necessário apenas uma função, para criar diversos objetos diferentes, através da modificação dos argumentos recebidos. A função factory a seguir, é responsável pela criação de um mesmo objeto número complexo - representado no item anterior-.
const criaNumeroComplexo = (parteReal, parteImaginaria) => { return {
parteReal: parteReal,
parteImaginaria: parteImaginaria, formaRetangular() {
console.log(`${this .parteReal} + i${this.parteImaginaria}`); },
formaPolar() {
let modulo = Math.sqrt(Math .pow(this.parteReal,2) +
Math.pow(this.parteImaginaria, 2));
let angulo = Math.atan( this.parteImaginaria / this.parteReal) *
57.2958;
console.log(`Módulo: ${modulo}, Ângulo: ${angulo}`); }
} }
const complexo2 = criaNumeroComplexo(3,3); complexo1.formaRetangular();
complexo2.formaRetangular();
Nesse exemplo, é possível se deparar com algumas “estranhezas”, se comparado ao exemplo da sessão de Objetos. Primeiramente, para a declaração da função factory criaNumeroComplexo, a palavra chave function, foi omitida. Esse tipo de declaração de função chama-se arrow function, de modo que a função é atribuída para uma variável e sua estrutura é a seguinte:
Se a função criada não receber nenhum parâmetro, esse argumento pode ser nulo, fazendo com que a estrutura acima se reduza a:
Outro item interessante com a respeito as arrows functions é que elas possuem return implícito, se o par de chaves { }, não for utilizado, fazendo com que seja possível a criação de funções inline.
Voltando ao exemplo da função factory, observa-se também que, na declaração dos métodos internos do objeto de retorno, a palavra chave function também foi omitida, sendo isso possível, devido a novas versões do JavaScript. Outra simplificação no código que poderia ser realizada, é na declaração dos atributos parteReal e parteImaginaria, uma vez que os argumentos recebidos pela função possuem o mesmo nome que os atributos, ficando da seguinte maneira:
const criaNumeroComplexo = (parteReal, parteImaginaria) => { return {
parteReal,
parteImaginaria, formaRetangular() {
console.log(`${this .parteReal} + i${this.parteImaginaria}`); },
formaPolar() {
let modulo = Math.sqrt(Math .pow(this.parteReal,2) +
Math.pow(this.parteImaginaria, 2));
let angulo = Math.atan( this.parteImaginaria / this.parteReal) *
57.2958;
console.log(`Módulo: ${modulo}, Ângulo: ${angulo}`); }
} }
const complexo1 = criaNumeroComplexo(5,5);
const complexo2 = criaNumeroComplexo(3,3); complexo1.formaRetangular();
complexo2.formaRetangular();
Como funciona, detalhadamente, uma Alexa Skill
Uma skill de Alexa possui tanto um modelo de interação (interface de usuário de voz) como uma lógica de aplicativo. Quando um usuário fala, a Alexa faz o processamento desta fala em seu contexto de modelação de interação para determinar qual solicitação o usuário requeriu.
O processamento é iniciado, assim que o usuário diz a palavra “Alexa” , que é uma palavra de ativação, e a partir desse momento o dispositivo começa a capturar tudo o que o usuário estiver falando.
Uma vez capturado o áudio, ele é enviado para o Alexa Voice Service (AVS), e tudo aquilo que foi dito após a palavra Alexa é chamada de User’s Utterance, expressão do usuário, e esse áudio é encaminhado para o AVS onde será gerenciado pelo componente Speech Platform, em seguida encaminhado para o Automatic Recognition Speed (ASR) que converterá o áudio em texto, nesta parte podemos notar que o AVS usa muito Machine Learning, muita inteligência artificial.
Após convertido em texto, o ASR devolve para o Speech Platform, onde ele enviará o arquivo para um outro componente o Natural Language Understanding (NLU), e lá é feito a compressão da requisição feita pelo usuário, uma vez entendido, o NLU transforma o texto em uma Intent e devolve para o Speech Platform.
Agora que o Speech Platform sabe qual é a intenção ele procurará dentro do catálogo de todas Skills se alguma delas pode realizar essa intenção, se for encontrada alguma Skill que possa realizar, ela será acionada e processará a Intent, devolvendo uma diretiva para o Speech Platform.
Mas ainda precisamos converter o arquivo em texto para áudio novamente, e para isso o componente Text To Speech (TTS) fará a conversão e devolverá para o Speech Platform e finalmente o áudio será enviado para o dispositivo.
Fonte: Site da Amazon2
Dentre todos componentes citados acima, neste tutorial vamos focar apenas no componente das Skills.
Fonte: Site da Amazon2
2 Disponível em: < https://developer.amazon.com/pt-BR/alexa >
Desenvolvendo uma Skill - Quiz de Eletrônica Analógica
Registro
Para começar o processo de desenvolvimento de uma Alexa Skill, é necessário possuir uma conta na Amazon Developer. Se você ainda não possui uma conta, clique aqui para se registrar, o processo é rápido e totalmente gratuito.
Conhecendo a interface
Já possuindo sua conta de desenvolvedor na Amazon, vá para o console de desenvolvimento Alexa, clicando aqui. Você irá se deparar com a seguinte tela.
Nesse ponto, para começar o projeto de uma aplicação Alexa, pressione o botão Create Skill e você será enviado para a próxima tela, onde é possível escolher o nome desejado para sua Skill, além da linguagem padrão de desenvolvimento. Nas opções abaixo, designadas para a escolha de um modelo, você poderá começar o desenvolvimento a partir de um projeto customizado, onde você construirá todas as interações de sua Skill ou um projeto pré-construído, se desejar implementar uma Skill que esteja dentro de uma das categorias disponíveis, possuindo assim, uma série de intenções e sentenças prontas que poderão ser utilizadas como auxílio de desenvolvimento. Na série de opções de escolha para hospedagem do backend da Skill, você pode poderá escolher
para a Alexa hospedar para você, utilizando o nível gratuito do AWS, com disponibilização de uma AWS Lambda, com 5GB de armazenamento e 15GB de transferência de dados mensal, de modo que é possível escolher entre os ambientes do NodeJs ou Python; ou você poderá hospedar em um servidor próprio, opção utilizada quando a aplicação possui alta taxa de armazenamento e transferência de dados, ou seja, uma aplicação que já esteja em fase de produção. Na imagem abaixo é possível observar essa tela explicada anteriormente.
Ao definir o nome para sua Skill e algumas das configurações iniciais, você será enviado para a próxima fase de configuração. Agora, é necessário escolher o template base que você utilizará para o desenvolvimento de seu projeto. Nesse ponto, é possível escolher
criar uma Skill a partir de algumas já desenvolvidas, e que podem ser utilizadas como base para uma aplicação maior. Se a opção desejada não estiver disponível na lista, é possível importar uma skill, a partir de repositório público do GitHub.
Após definir o template base, você será enviado para a página principal do console de desenvolvimento Alexa, onde é possível identificar vários pontos importantes, como por exemplo, alguns recursos disponibilizados que ajudam na construção de uma Alexa Skill, como tutoriais em vídeo; além da documentação oficial, fórum e relatório de atualizações recentes. Na barra lateral à direita, é mostrado as etapas que seu projeto deve seguir para enfim realizar as simulações, em um simulador, em um dispositivo echo ou até mesmo em um smartphone. Na barra lateral à esquerda, os principais itens são:
● Interaction Model: Responsáveis pela interação da Skill
○ Definir o nome de invocação da Skill - que pode ser diferente do nome da Skill -. Através da aba Invocation.
○ Criação e gerenciamento de intenções, a partir da aba Intents. Note que para qualquer Skill criada, irá existir algumas intenções em Built-In Intents, que são padrão e necessárias para o funcionamento correto da aplicação.
○ Criação e gerenciamento de Slot Types, ou seja, novos tipos de dados, que possivelmente não estão presentes de forma padrão.
○ Possibilidade de desenvolvimento do modelo de interações a partir de um JSON, a partir da aba JSON Editor.
● Interfaces: Controle das diversas interfaces possíveis para o projeto, como interface de áudio e vídeo.
● Endpoint: Serviço de hospedagem do back-end em um web server, para que a Alexa Skill possa enviar requisições HTTP e consequentemente obter respostas.
Observando agora a barra de menu superior, é possível definir o funcionamento dos seguinte itens:
● Build: A partir dessa opção - que já inicializa selecionada, por padrão - é possível realizar as construções explicitadas no item anterior.
● Code: Nessa opção é possível, a partir da ferramenta de edição do console de desenvolvimento, programar o backend da Skill, a fim de receber/responder as requisições.
● Test: Opção utilizada para testar a Skill, através do simulador Alexa. Nesse ponto é interessante explicitar também que, é possível realizar o teste das Skills através dos dispositivos que estejam vinculados a sua conta de desenvolvimento, como por exemplo Echo Dot ou até mesmo um dispositivo celular, a partir do aplicativo Amazon Alexa.
● Distribution: Opção utilizada para definir algumas informações e configurações de distribuição para a Skill que está sendo desenvolvida. Normalmente essa etapa é realizada na etapa final de construção da aplicação. Nesta aba, estão disponíveis customizações que aparecerão para sua skill na Alexa Skills Store, por exemplo. ● Certification: Nesta opção, é possível realizar as validações necessárias para a
disponibilização de uma Skill. Assim, é realizado a certificação e se esta etapa for aprovada pela Amazon, a publicação da Skill.
● Analytics: A partir dessa opção, é possível visualizar alguns dados quantitativos referentes à utilização da Skill, como por exemplo a quantidade e quais Intents foram chamadas.
A imagem abaixo exemplifica essa janela principal de desenvolvimento.
Fase de desenvolvimento
Neste item será desenvolvido uma Skill responsável por realizar perguntas e respostas de um determinado assunto. Desse modo, iremos construir do zero, um Quiz - jogo de perguntas e respostas -, com o tema: eletrônica analógica.
Para isso, abra a página do console de desenvolvimento Alexa, clicando aqui , ou através do link https://developer.amazon.com/alexa/console/ask. Nessa página, clique no botão Create Skill. Em Skill name, escreva Quiz de Eletrônica Analógica; em Default Language, selecione Portuguese (BR), se já não estiver marcado; selecione o modelo Custom e o método de hospedagem do backend como sendo Alexa-Hosted (Node.js). As imagens a seguir exemplificam esse passos iniciais.
Feito isso, clique no botão Create Skill, localizado no canto superior direito. Na
próxima tela, deixe selecionado o template padrão, para uma Hello World Skill, e clique no botão Continue with template, como na imagem abaixo.
Nesse ponto a Skill inicial está sendo criada, sendo necessário alguns minutos para
a conclusão.
Com o projeto inicial da Skill criado, primeiramente vamos definir o nome de invocação para Skill, para isso, selecione o item Invocation, disponível no menu lateral esquerdo. No campo Skill Invocation Name, escreva: quiz de eletrônica analógica, esse será o nome utilizado todas as vezes que desejarmos chamar a Skill através da Alexa. Feito isso, clique no botão Save Model e após, no botão Build Model, para que a alteração seja salva e o modelo seja reconstruído; lembre-se sempre se clicar nesses botões ao realizar alguma alteração em sua Skill.
Agora é necessário criarmos as intenções que serão utilizadas pela nossa Skill, além de apagar a intenção HelloWorldIntent, já que não a utilizaremos. Para deletar essa intent, simplesmente clique a lixeira, em seu lado direito, como na imagem a seguir:
Uma janela de confirmação irá aparecer, clique em Delete Intent. Após isso, é hora de criarmos as intents que serão úteis em nossa aplicação. Clique no botão Add, na frente no nome intents (4). Escreva no campo Create custom intent o nome: QuizIntent. Essa intenção será responsável por gerenciar as perguntas do Quiz. Clique no botão Create custom intent. Essa etapa é representada na imagem a seguir:
Após criada a intent, devemos definir quais expressões irão chamar essa intenção. Como essa será a nossa intent principal, devemos utilizar frases que serão responsáveis por iniciar o jogo. Assim, no campo Sample Utterances, escreva as seguintes sentenças, sempre pressionando a tecla Enter, após a inserção de cada uma delas:
● começar quiz ● começar jogo ● jogar
Desse modo, sua tela deve se semelhante com a seguinte:
Feito isso, clique em Save Model e a seguir em Build Model. Agora vamos criar a intenção responsável por gerenciar as respostas das perguntas do quiz. Assim, realizando os mesmos passos anteriores, crie uma intent, com o nome: AnswerIntent. Após isso, ainda não defina nenhuma sentença, pois será necessário a criação de um novo tipo de variável, um novo Slot Type. Para isso, clique no item Add, em frente ao item de menu Slot Type, disponível no menu lateral esquerdo. Escreva: AnswerSlot, como nome do Slot Type. Após, clique no botão Create custom slot type. Como na imagem abaixo:
Esse passo de criação de um Slot Type customizado é importante, uma vez que não
existe um slot padrão que possua os valores que iremos utilizar para as respostas das perguntas do quiz. Utilizaremos as letras a,b e c, como respostas das questões, e para isso, é necessário que a Alexa reconheça quando estamos trabalhando com esses valores, através desse slot criado. É importante ressaltar que, sempre que existir na biblioteca
padrão, os Slots que irão ser utilizados, como por exemplo de datas, opte por eles, já que eles possuem maior compatibilidade. Em Slot Values, escreva os seguintes valores, seguidos pelo pressionamento da tecla Enter do teclado, para que o valor seja inserido :
● a. ● b. ● c.
Os valores de seu AnswerSlot, deve ser semelhante a este:
Observe que foi necessário um ponto (.), ao final de cada valor. Isso é necessário para que a Alexa reconheça que estamos trabalhando com letras e não palavras, já que ela poderia entender B como sendo Be, por exemplo.
Agora que já possuímos o Slot Type customizado, é necessário retornar à AnswerIntent, clicando sobre ela , no menu lateral esquerdo. Crie as seguintes expressões:
● {answer}
● alternativa {answer}
Sempre que desejarmos trabalhar com valores ditos variáveis, aqui chamados de slots, devemos utilizar o par de chaves, delimitando o slot. Quando um slot é digitado, uma
janela é mostrada, para que seja criado um novo slot ou utilizado um já existente. Para a primeira expressão criada, devemos criar o slot answer, já que ele ainda não foi definido. Já para a segunda expressão cadastrada, não é necessário. Após o cadastro das sentenças, sua tela deve estar assim:
Embaixo das utterances, estão os slot cadastrados na intent. Nesse caso temos o slot answer. Como na imagem a seguir.
Agora que já possuímos as expressões e o slot em nossa intent, demos definir qual é o tipo do slot criado. Nesse caso, o slot answer é do tipo AnswerSlot, criado anteriormente. Para isso selecione o Slot Type para o slot, na caixa de seleção à direita do nome do slot. Essa etapa é representada pela imagem a seguir.
Feito isso, clique nos botões Save Model e Build Model.
O próximo passo é definir uma intent responsável por gerenciar a troca de perguntas, para que a Alexa reconheça quando desejarmos ir para a próxima pergunta do quiz. Para isso, Clique em Add, em Intents, para uma nova intenção ser criada. Escreva: NextQuestionIntent, como sendo o nome da Intent e clique no botão Create custom intent. Como na imagem abaixo.
As expressões que iremos criar para a intent são as seguintes:
● próxima pergunta ● próxima
● próximo
Após, clique no botão Save Model e Build Model.
Com a fase de construção da Skill concluído, deve-se agora, programar o backend da Skill, para que seja possível receber e responder as requisições. Assim, selecione o item de menu Code, disponível na barra de menu superior.
Com a janela do código aberto, primeiramente devemos apagar o objeto HelloWorldIntentHandler, já que esta Intent não existem mais. O bloco de código a ser deletado é o seguinte: const HelloWorldIntentHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' && Alexa.getIntentName(handlerInput.requestEnvelope) === 'HelloWorldIntent'; }, handle(handlerInput) {
const speakOutput = 'Hello World!';
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse(); }
};
Após deletado esse bloco de código, é necessário apagar também, a chamada desse objeto na exportação do handler, ao final do código. Desse modo, localize a linha que possua o nome do objeto deletado anteriormente e, exclua essa linha. Feito isso, clique no botão Save e depois Deploy, para que o código seja salvo e compilado. Agora, esse bloco de código deve parecer-se com o seguinte.
exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, HelpIntentHandler, CancelAndStopIntentHandler, SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
) .addErrorHandlers( ErrorHandler, ) .lambda();
Sempre que um novo Handler, ou seja, um objeto que gerencie uma Intent, for criado, ele deverá ser adicionado no método addRequestHandlers, já que é a partir deste, que a Alexa encontra os handlers, agindo assim, como um ponto de entrada para a Skill.
Antes de criarmos os handlers para as intents definidas anteriormente, vamos criar um novo arquivo, onde colocaremos as perguntas do nosso quiz e, utilizando o sistema de módulos do Node.js, importaremos para o index.js, ou seja, para nosso arquivo principal. Desse modo, na lateral esquerda da página, clique com o botão direito do mouse em cima da pasta lambda, e selecione Create File. Crie o arquivo questions.js, como na imagem abaixo.
No arquivo de questões, copie o código a seguir:
const questions = [ {
question: 'Quantos diodos possui um retificador em ponte? Letra a, 1. Letra b, 2. Letra c, 4.',
answer: 'c',
answerSpeech: 'Um retificador em ponte possui 4 diodos.'
}, {
question: 'Qual a principal forma de atuação do diodo Zéner? Letra a, de forma direta. Letra b, de forma reversa. Letra c, de forma polar.',
answer: 'b',
answerSpeech: 'A principal forma de atuação do diodo Zéner é de forma reversa.'
}, {
question: 'Como é construído um diodo? Letra a, a partir da junção JK. Letra b, a partir da junção PN. Letra c, a partir da junção S R.',
answer: 'b' ,
de um semicondutor do tipo P com um semicondutor do tipo N.'
}, {
question: 'Qual a mínima tensão nominal, para um diodo de silício polarizado diretamente, necessária para reduzir a região de depleção do diodo e, fazê-lo começar a conduzir corrente elétrica de fato? Letra a, 0.8 Volts. Letra b, 0.6 Volts. Letra c, 0.7 Volts.',
answer: 'c' ,
answerSpeech: 'É necessário uma tensão nominal de no mínimo 0.7 Volts para um diodo de silício e de no mínimo 0.3 Volts para um diodo de
germânio.'
}, {
question: 'Qual a frequência na carga, para um retificador de onda completa? Letra a, a mesma frequência de entrada. Letra b, duas vezes a frequência de entrada. Letra c, três vezes a frequência de entrada.',
answer: 'b' ,
answerSpeech: 'A frequência na carga, para um retificador de onda completa é duas vezes a frequência de entrada.'
}, {
question: `Qual a tensão de pico na carga, para um retificador de onda completa em ponte? Supondo uma tensão de pico do secundário do transformador com sendo V secundário?
Letra a, V secundário, menos a queda de tensão de quatro diodos. Letra b, V secundário, menos a queda de tensão de 1 diodo. Letra c, V secundário, menos a queda de tensão de dois diodos.`,
answer: 'c' ,
answerSpeech: 'A tensão de pico na carga, para um retificador de onda completa em ponte, é V secundário menos a queda de tensão de dois diodos. Já que tanto no semi-ciclo positivo, quanto negativo, dois dos quatro diodos, estão sempre conduzindo.'
}, ]
const amount = questions.length
questions, amount }
Após isso, salve e compile novamente.
No código apresentado para o arquivo de questões, foi definido um array questions, de objetos, de modo que cada objeto possui três atributos: a pergunta que a Alexa irá realizar, a resposta para a pergunta e a fala que a Alexa irá realizar após o respondimento da questão pelo usuário. Também foi definido uma constante amount que armazena o tamanho do array de perguntas. Esse array possui apenas seis perguntas a título de simplificação, já que poderia apresentar uma quantidade qualquer de questões. Desse modo, foi utilizado o module.exports, para exportar um objeto que possui o array de questões e o tamanho do array, como atributos, a fim de importarmos esse módulo no arquivo principal, o index.js.
Voltando para o arquivo index.js, iremos realizar a importação do arquivo questions.js, utilizando o require e o operador destructuring, disponível a partir da versão 2016 do JavaScript, logo abaixo da linha: const Alexa = require('ask-sdk-core');
const {questions,amount} = require('./questions');
O operador destructuring é definido por esse par de chaves, de modo que a partir dele é possível desestruturar o objeto importado através do require(‘.questions’), e desse modo, recuperar as constantes questions e amount do arquivo requerido, para que seja possível utilizá-las no arquivo atual.
Agora, vamos criar uma função responsável por embaralhar o nosso array de perguntas. Para que todas as vezes que o usuário abra a Skill, as questões sejam realizadas em ordens diferentes. Em baixo da linha de código definida anteriormente, crie a seguinte função, que simplesmente recebe um array e o embaralha:
function shuffle(array) {
let aux1, aux2;
for(let i = amount-1; i > 0; i--) {
aux1 = Math.floor(Math.random() * (i+1)); aux2 = array[i];
array[i] = array[aux1]; array[aux1] = aux2;
} }
Após isso, salve e compile novamente.
Devemos agora, declarar três variáveis que serão utilizadas. Uma variável responsável por definir a quantidade de perguntas que desejamos que seja realizada, em cada vez que a Skill for aberta. Uma segunda variável, responsável por armazenar o índice no array da pergunta que está sendo realizada, para que seja possível iterar sobre ele. E uma terceira variável, para que seja possível contabilizar a quantidade de perguntas que o usuário acertou. Assim, logo abaixo da função definida anteriormente, declare as seguintes constantes: const amountQuestions = 3; let idCurrentQuestion = 0; let score = 0;
Salve e compile o código.
Agora, vamos modificar algumas linhas nos handlers que já estão criados, para que a Alexa fale as frases em português. No LaunchRequestHandler, intent utilizada quando a Skill é inicializada, substitua o código, para o seguinte:
const LaunchRequestHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'; }, handle(handlerInput) {
const speakOutput = `Bem vindo ao quiz de eletrônica analógica. Você
será submetido à ${amountQuestions} perguntas. Para começar o jogo você pode dizer coisas como: começar jogo, jogar e começar quiz.`;
shuffle(questions); return handlerInput.responseBuilder .speak(speakOutput) .reprompt(speakOutput) .getResponse(); } };
Dentro um objeto handler, como o LaunchRequestHandler por exemplo, possuímos dois métodos: canHandle() e o handle(). O método canHandle() é responsável por definir quando o método handle() será executado; nesse caso, dentro do método canHandle(), é definido para que o método handle(), seja executado somente se o intent que estiver fazendo a requisição, for do tipo LaunchRequest. O método handle() que de fato a manipulação desejada pela intenção. Dentro do método handle(), existe uma constante com o nome de speakOutput que, utilizando-se de template string, é declarado uma frase para que Alexa diga. Também nesse método é chamada a função shuffle(), passando o array de perguntas - para que ele seja embaralhado ao iniciar da Skill -. Ao retorno do método handle(), existem três funções disponíveis no respondeBuilder: a função speak, para que a Alexa, fale a frase definida anteriormente; a função reprompt(), para que a sessão continue aberta, esperando uma resposta do usuário; e a função getResponse(), para uma resposta em JSON, seja gerada.
No handler da Intent Help, é necessário alterar somente a constante speakOutput, para que seja uma frase em português. Em HelpIntentHandler, localize a linha dessa variável e substitua-a pela seguinte:
const speakOutput = 'Como posso te ajudar?';
Já no handler da Intent CancelAndStop, também é necessário alterar somente a constante speakOutput, substitua-a pela seguinte:
const speakOutput = 'Tchau. Até mais!';
Para os handlers IntentReflectorHandler e ErrorHandler, também altere a constante speakOutput, substituindo-as respectivamente pelas seguintes:
IntentReflectorHandler :
const speakOutput = `Você acabou de chamar a intent ${intentName}`;
ErrorHandler:
const speakOutput = `Me Desculpe, Eu tive problemas para fazer o que você
pediu. Por favor, tente novamente.`;
Clique nos botões de Salve e Deploy.
A partir de agora, iremos implementar os handlers para as nossas Intents customizadas. O primeiro deles será o handler para a intenção QuizIntent. Logo abaixo do handler LaunchRequestHandler, cole o seguinte código:
const QuizIntentHandler = { canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name ===
'QuizIntent'; },
handle(handlerInput) {
const speechText = questions[idCurrentQuestion].question;
return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .getResponse(); } };
Salve e compile o código.
Esse handle é responsável por “pegar” a questão disponível na posição idCurrentQuestion, no array de perguntas, e armazená-la na constante speechText, na qual a Alexa perguntará para o usuário. O método repromt garante que a sessão permanecerá aberta até o usuário responder a pergunta.
O próximo handle é responsável por gerenciar a Intent de resposta das questões, o AnswerIntent. Ou seja, quando a Intent que será chamada quando o usuário responder a pergunta disponibilizada pelo handle da Intent QuizIntent. Desse modo, copie o seguinte bloco de código logo abaixo do handler construído anteriormente.
const AnswerIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name ===
'AnswerIntent'); },
handle(handlerInput) {
// armazena os slots que vieram junto com a requisição
const slot = handlerInput.requestEnvelope.request.intent.slots;
// pega o valor do slot com nome 'answer', do objeto slot
const answerSlot = slot['answer'].value.toLowerCase();
let speechText = '';
para ir para a proxima pergunta. Se já foi atingido, fala para ver o rendimento
let speechNext = (idCurrentQuestion + 1 < amountQuestions) ? 'Vá para a próxima pergunta dizendo: próxima.' : 'Você respondeu todas as perguntas disponíveis. Descubra seu rendimento dizendo: próximo.';
// se a resposta da questão for igual ao valor que o usuário falou
if(answerSlot === questions[idCurrentQuestion].answer){ speechText = `Isso mesmo!
${questions[idCurrentQuestion].answerSpeech} ${speechNext}`; score++
}
else {
speechText = `Resposta incorreta.
${questions[idCurrentQuestion].answerSpeech} ${speechNext}`; } return handlerInput.responseBuilder .speak(speechText) .reprompt() .getResponse(); } };
Salve e compile o código.
O próximo e último handle, diz respeito à Intent NextQuestionIntent, para o gerenciamento da próxima pergunta ou para o término do jogo. Copie o seguinte bloco de código logo abaixo do handle anterior:
const NextQuestionIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name ===
'NextQuestionIntent'); },
handle(handlerInput) {
idCurrentQuestion++ // aumenta índice, para ir para a próxima pergunta
if(idCurrentQuestion < amountQuestions) // se ainda não atingiu a quantidade de perguntas, chama o handle QuizIntentHandler, para refazer o
ciclo
return QuizIntentHandler.handle(handlerInput);
else {
idCurrentQuestion = 0; // zera o índice, para um possível próximo jogo
const scorePorcent = (score/amountQuestions) * 100; // calcula a quantidade de acertos em porcentagem
score = 0; // zera a pontuação atual, para um possível próximo jogo
shuffle(questions); // embaralha o array de perguntas, para um possível próximo jogo
let speechResult = '';
if(scorePorcent > 60 && scorePorcent < 80)
speechResult = `Você atingiu a porcentagem de acerto entre 60 e 80 por cento.`;
else if(scorePorcent > 80)
speechResult = 'Parabéns! Você obteve um rendimento igual ou superior à 80 por cento.';
else
speechResult = 'Infelizmente você não chegou aos 60 por cento de acerto.';
return handlerInput.responseBuilder
.speak(`${speechResult} Espero você em outro momento para jogarmos novamente! Até mais!`)
.getResponse(); } } };
Por fim, é necessário inserir os handles que foram adicionados ao código, no addRequestHandlers, como feito anteriormente com a Intent HelloWorld. Vá para o final do arquivo e localize o exports.handle. Adicione as linhas faltantes, para que esse bloco de código fique da seguinte forma:
exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, QuizIntentHandler, AnswerIntentHandler, NextQuestionIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler, SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
) .addErrorHandlers( ErrorHandler, ) .lambda();
Salve e faça o Deploy da aplicação.
Pronto! Feito todos esses passos, nosso Quiz de Eletrônica Analógica está concluído. Se você apresentar algum erro durante a fase de teste, volte ao código apresentado a seguir e verifique se seu index.js ficou semelhante ao apresentado, após todas as etapas:
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');
const {questions,amount} = require('./questions');
function shuffle(array) {
let aux1, aux2;
for(let i = amount-1; i > 0; i--) {
aux1 = Math.floor(Math.random() * (i+1)); aux2 = array[i]; array[i] = array[aux1]; array[aux1] = aux2; } } const amountQuestions = 3; let idCurrentQuestion = 0; let score = 0; const LaunchRequestHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) ===
'LaunchRequest'; },
handle(handlerInput) {
const speakOutput = `Bem vindo ao quiz de eletrônica analógica. Você
será submetido à ${amountQuestions} perguntas. Para começar o jogo você pode dizer coisas como: começar jogo, jogar e começar quiz.`;
shuffle(questions); return handlerInput.responseBuilder .speak(speakOutput) .reprompt(speakOutput) .getResponse(); } }; const QuizIntentHandler = { canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name ===
'QuizIntent'; },
handle(handlerInput) {
const speechText = questions[idCurrentQuestion].question;
return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .getResponse(); } }; const AnswerIntentHandler = { canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name ===
'AnswerIntent'); },
handle(handlerInput) {
// armazena os slots que vieram junto com a requisição
const slot = handlerInput.requestEnvelope.request.intent.slots;
// pega o valor do slot com nome 'answer', do objeto slot
const answerSlot = slot['answer'].value.toLowerCase();
let speechText = '';
// Se o valor de perguntas amountQuestions não foi atingido, fala para ir para a proxima pergunta. Se já foi atingido, fala para ver o rendimento
let speechNext = (idCurrentQuestion + 1 < amountQuestions) ? 'Vá para a próxima pergunta dizendo: próxima.' : 'Você respondeu todas as perguntas disponíveis. Descubra seu rendimento dizendo: próximo.';
// se a resposta da questão for igual ao valor que o usuário falou
if(answerSlot === questions[idCurrentQuestion].answer){ speechText = `Isso mesmo!
${questions[idCurrentQuestion].answerSpeech} ${speechNext}`; score++
}
else {
speechText = `Resposta incorreta.
${questions[idCurrentQuestion].answerSpeech} ${speechNext}`; } return handlerInput.responseBuilder .speak(speechText) .reprompt() .getResponse(); } }; const NextQuestionIntentHandler = { canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name ===
'NextQuestionIntent'); },
handle(handlerInput) {
idCurrentQuestion++ // aumenta indice, para ir para a próxima pergunta
if(idCurrentQuestion < amountQuestions) // se ainda não atingiu a quantidade de perguntas, chama o handle QuizIntentHandler, para refazer o ciclo
return QuizIntentHandler.handle(handlerInput);
else {
idCurrentQuestion = 0; // zera o indice, para um possível próximo jogo
const scorePorcent = (score/amountQuestions) * 100; // calcula a quantidade de acertos em porcentagem