├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── ask-resources.json
├── instructions
├── 1-voice-user-interface.md
├── 2-customization.md
├── 2-lambda-function.md
├── 3-connect-vui-to-code.md
├── 4-testing.md
├── 5-customization.md
├── 6-publication.md
├── 7-cli.md
└── images
│ ├── arn.png
│ ├── ask.png
│ ├── availability.png
│ ├── build.png
│ ├── buttons-trivia.png
│ ├── certification.png
│ ├── cloudwatch.png
│ ├── create-function.png
│ ├── create-lambda.png
│ ├── custom.png
│ ├── distribution.png
│ ├── dynamo.png
│ ├── echo-buttons.png
│ ├── endpoint-tab.png
│ ├── endpoint.png
│ ├── function-code.png
│ ├── gadget-interfaces.png
│ ├── interfaces.png
│ ├── json.png
│ ├── lambda.png
│ ├── logs.png
│ ├── role.png
│ ├── save-endpoint.png
│ ├── test-enable.png
│ ├── test.png
│ ├── uk-store.png
│ ├── us-store.png
│ └── useast.png
├── lambda
└── custom
│ ├── config
│ ├── messages.js
│ ├── questions.js
│ └── settings.js
│ ├── handlers
│ ├── gamePlayHandlers.js
│ ├── globalHandlers.js
│ ├── rollCallHandlers.js
│ └── startHandlers.js
│ ├── index.js
│ ├── package-lock.json
│ ├── package.json
│ └── utils
│ ├── animations.js
│ ├── directives.js
│ ├── display.js
│ ├── game.js
│ ├── logger.js
│ └── rollcall.js
└── skill-package
├── interactionModels
└── custom
│ ├── de-DE.json
│ ├── en-GB.json
│ └── en-US.json
└── skill.json
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lambda/custom/node_modules
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/alexa/skill-sample-nodejs-buttons-game-templates/issues), or [recently closed](https://github.com/alexa/skill-sample-nodejs-buttons-game-templates/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/alexa/skill-sample-nodejs-buttons-game-templates/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/alexa/skill-sample-nodejs-buttons-game-templates/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Amazon Software License 1.0
2 |
3 | This Amazon Software License ("License") governs your use, reproduction, and
4 | distribution of the accompanying software as specified below.
5 |
6 | 1. Definitions
7 |
8 | "Licensor" means any person or entity that distributes its Work.
9 |
10 | "Software" means the original work of authorship made available under this
11 | License.
12 |
13 | "Work" means the Software and any additions to or derivative works of the
14 | Software that are made available under this License.
15 |
16 | The terms "reproduce," "reproduction," "derivative works," and
17 | "distribution" have the meaning as provided under U.S. copyright law;
18 | provided, however, that for the purposes of this License, derivative works
19 | shall not include works that remain separable from, or merely link (or bind
20 | by name) to the interfaces of, the Work.
21 |
22 | Works, including the Software, are "made available" under this License by
23 | including in or with the Work either (a) a copyright notice referencing the
24 | applicability of this License to the Work, or (b) a copy of this License.
25 |
26 | 2. License Grants
27 |
28 | 2.1 Copyright Grant. Subject to the terms and conditions of this License,
29 | each Licensor grants to you a perpetual, worldwide, non-exclusive,
30 | royalty-free, copyright license to reproduce, prepare derivative works of,
31 | publicly display, publicly perform, sublicense and distribute its Work and
32 | any resulting derivative works in any form.
33 |
34 | 2.2 Patent Grant. Subject to the terms and conditions of this License, each
35 | Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free
36 | patent license to make, have made, use, sell, offer for sale, import, and
37 | otherwise transfer its Work, in whole or in part. The foregoing license
38 | applies only to the patent claims licensable by Licensor that would be
39 | infringed by Licensor's Work (or portion thereof) individually and
40 | excluding any combinations with any other materials or technology.
41 |
42 | 3. Limitations
43 |
44 | 3.1 Redistribution. You may reproduce or distribute the Work only if
45 | (a) you do so under this License, (b) you include a complete copy of this
46 | License with your distribution, and (c) you retain without modification
47 | any copyright, patent, trademark, or attribution notices that are present
48 | in the Work.
49 |
50 | 3.2 Derivative Works. You may specify that additional or different terms
51 | apply to the use, reproduction, and distribution of your derivative works
52 | of the Work ("Your Terms") only if (a) Your Terms provide that the use
53 | limitation in Section 3.3 applies to your derivative works, and (b) you
54 | identify the specific derivative works that are subject to Your Terms.
55 | Notwithstanding Your Terms, this License (including the redistribution
56 | requirements in Section 3.1) will continue to apply to the Work itself.
57 |
58 | 3.3 Use Limitation. The Work and any derivative works thereof only may be
59 | used or intended for use with the web services, computing platforms or
60 | applications provided by Amazon.com, Inc. or its affiliates, including
61 | Amazon Web Services, Inc.
62 |
63 | 3.4 Patent Claims. If you bring or threaten to bring a patent claim against
64 | any Licensor (including any claim, cross-claim or counterclaim in a
65 | lawsuit) to enforce any patents that you allege are infringed by any Work,
66 | then your rights under this License from such Licensor (including the
67 | grants in Sections 2.1 and 2.2) will terminate immediately.
68 |
69 | 3.5 Trademarks. This License does not grant any rights to use any
70 | Licensor's or its affiliates' names, logos, or trademarks, except as
71 | necessary to reproduce the notices described in this License.
72 |
73 | 3.6 Termination. If you violate any term of this License, then your rights
74 | under this License (including the grants in Sections 2.1 and 2.2) will
75 | terminate immediately.
76 |
77 | 4. Disclaimer of Warranty.
78 |
79 | THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
80 | EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF
81 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR
82 | NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER
83 | THIS LICENSE. SOME STATES' CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN
84 | IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU.
85 |
86 | 5. Limitation of Liability.
87 |
88 | EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL
89 | THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE
90 | SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT,
91 | INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR
92 | RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING
93 | BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS
94 | OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES
95 | OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
96 | SUCH DAMAGES.
97 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Alexa Skill: Trivia Game
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | **Important: The Gadgets Skill API is in beta and is subject to change at any time without notice. We welcome your feedback.**
5 |
6 | This Alexa sample skill is a template for a trivia game which is played with [Echo Buttons](https://www.amazon.com/Echo-Buttons-Alexa-Gadget-Pack/dp/B072C4KCQH) in multiplayer mode but can also be played without buttons in single player mode. Provided a list of interesting questions about a topic, Alexa will select a group of questions to ask the player(s), keep score throughout, and announce the winner at the end of the game.
7 |
8 | This sample skill demonstrates how to discover Echo Buttons using [roll call](https://developer.amazon.com/docs/gadget-skills/discover-echo-buttons.html#goals), how to [receive Echo Button events](https://developer.amazon.com/docs/gadget-skills/receive-echo-button-events.html), and how to [animate the Echo Button lights](https://developer.amazon.com/docs/gadget-skills/control-echo-buttons.html#animate).
9 |
10 | You may use this sample game as a starting point to build your own 'Better with Buttons' trivia game. You can customize Alexa's voice responses, the question list, and some game options without making changes to the code. However, to provide a truely unique game experience code changes will likely be necessary.
11 |
12 | ### About
13 | This guide assumes you have your developer environment ready to go and that you have some familiarity with CLI (Command Line Interface) Tools, [AWS](https://aws.amazon.com/), and the [ASK Developer Portal](https://developer.amazon.com/alexa-skills-kit).
14 |
15 | ### Pre-requisites
16 |
17 | * Node.js (> v8)
18 | * Register for an [AWS Account](https://aws.amazon.com/)
19 | * Register for an [Amazon Developer Account](https://developer.amazon.com)
20 |
21 |
22 | [](./instructions/1-voice-user-interface.md)
23 |
24 | Or click [here](./instructions/7-cli.md) for instructions using the ASK CLI (command line interface).
25 |
26 | ## Additional Resources
27 |
28 | ### Community
29 | * [Amazon Developer Forums](https://forums.developer.amazon.com/spaces/311/gadgets-beta.html) - Join the conversation!
30 | * [Hackster.io](https://www.hackster.io/amazon-alexa) - See what others are building with Alexa.
31 |
32 | ### Tutorials & Guides
33 | * [Voice Design Guide](https://developer.amazon.com/designing-for-voice/) - A great resource for learning conversational and voice user interface design.
34 | * [Codecademy: Learn Alexa](https://www.codecademy.com/learn/learn-alexa) - Learn how to build an Alexa Skill from within your browser with this beginner friendly tutorial on Codecademy!
35 | * [Echo Buttons Color Changer Sample Skill](https://github.com/alexa/skill-sample-nodejs-buttons-colorchanger) - A simpler skill that shows how to do roll call and control light animations for Echo Buttons.
36 |
37 | ### Documentation
38 | * [Official Alexa Skills Kit Node.js SDK](https://www.npmjs.com/package/ask-sdk) - The Official Node.js SDK Documentation
39 | * [Official Alexa Skills Kit Documentation](https://developer.amazon.com/docs/ask-overviews/build-skills-with-the-alexa-skills-kit.html) - Official Alexa Skills Kit Documentation
40 | * [Official Alexa Gadgets Documentation](https://developer.amazon.com/alexa/alexa-gadgets) - The Echo Buttons are the first Alexa Gadget
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ask-resources.json:
--------------------------------------------------------------------------------
1 | {
2 | "askcliResourcesVersion": "2020-03-31",
3 | "profiles": {
4 | "default": {
5 | "skillMetadata": {
6 | "src": "./skill-package"
7 | },
8 | "code": {
9 | "default": {
10 | "src": "lambda/custom"
11 | }
12 | },
13 | "skillInfrastructure": {
14 | "userConfig": {
15 | "runtime": "nodejs10.x",
16 | "handler": "index.handler",
17 | "awsRegion": "us-east-1"
18 | },
19 | "type": "@ask-cli/lambda-deployer"
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/instructions/1-voice-user-interface.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 |
6 | 1. Go to the **[Amazon Developer Portal](https://developer.amazon.com/alexa)**. In the top-right corner of the screen, click the **Sign In** button.
7 | (If you don't already have an account, you will be able to create a new one for free.)
8 |
9 | 2. Once you have signed in move your mouse over the **Your Alexa Consoles** text at the top of the screen and Select the **Skills** Link.
10 |
11 | 3. From the **Alexa Skills Kit Developer Console** select the blue **Create Skill** button on the right side of the screen.
12 |
13 | 4. Give your new skill a **Name**. This is the name that will be shown in the Alexa Skills Store, and the name your users will refer to.
14 |
15 | 5. Select the **Custom** model from the list at the bottom of the page and then click the **Create Skill** button at the top right.
16 |
17 | 
18 |
19 | 6. **Build the Interaction Model for your skill**
20 | 1. On the left hand navigation panel, select the **JSON Editor** tab. In the textfield provided, replace any existing code with the code provided in the [Interaction Model](../models) (make sure to pick the model that matches your skill's language).
21 |
22 | 
23 |
24 | 2. Next, select the **Invocation** tab. Change the **Skill Invocation Name** from the default provided. This is the name that your users will need to say to start your skill.
25 | 3. Click **Save Model**.
26 |
27 | **Note:** You should notice that **Intents** and **Slot Types** will auto populate based on the JSON Interaction Model that you have now applied to your skill. Feel free to explore the changes here, to learn about **Intents**, **Slots**, and **Utterances** open our [technical documentation in a new tab](https://developer.amazon.com/docs/custom-skills/create-intents-utterances-and-slots.html).
28 |
29 | 7. **Optional:** Select an intent by expanding the **Intents** from the left side navigation panel. Add some more sample utterances for your newly generated intents. Think of all the different ways that a user could request to make a specific intent happen. A few examples are provided. Be sure to click **Save Model** and **Build Model** after you're done making changes here.
30 |
31 | 8. **Enable Interfaces for Your Game**
32 | 1. On the left hand navigation panel, select the **Interfaces** tab.
33 |
34 | 
35 |
36 | 2. On the resulting page enable the **Display Interface**, **Gadget Controller**, and **Game Engine**.
37 |
38 | 
39 |
40 | 3. Click **Save Interfaces** and then **Build Model**.
41 |
42 | 9. If your interaction model builds successfully, proceed to the next step. If not, you should see an error. Try to resolve the errors. In our next step of this guide, we will be creating our Lambda function in the AWS developer console, but keep this browser tab open, because we will be returning here on [Page #3: Connect VUI to Code](./3-connect-vui-to-code.md).
43 |
44 |
45 | If you get an error from your interaction model, check through this list:
46 |
47 | * **Did you copy & paste the provided code correctly?**
48 | * **Did you accidentally add any characters to the Interaction Model or Sample Utterances?**
49 |
50 | [](./2-lambda-function.md)
51 |
--------------------------------------------------------------------------------
/instructions/2-customization.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # Echo Button Game Template
5 |
6 | **Important: The Gadgets Skill API is in beta and is subject to change at any time without notice. We welcome your feedback.**
7 |
8 | These instructions show how to customize the Trivia Template skill.
9 |
10 | # How to configure your game skill
11 |
12 | ## Introduction
13 | This Echo Button Game Template provides a way for you to customize your skill without necessarily writing code. You can affect the following features through configuration only:
14 | * Roll Call Behavior
15 | * Questions and Answers.
16 | * Total number of questions to be asked per game
17 | * Total number of questions asked/answered before a score summary is shared with the players
18 | * Button colors to indicate
19 | * Answer/buzz-in readiness
20 | * First to buzz-in
21 | * Missed buzz-in
22 | * Correct answers
23 | * Incorrect answers
24 | * Sound effects, including:
25 | * When roll call is complete
26 | * When the skill is waiting for buttons to be pressed after asking a question
27 | * When a user presses their button
28 | * When a user's answer is correct
29 | * When a user's answer is incorrect.
30 | * Changing the background, correct and incorrect images on Echo Show and Echo Spot devices
31 | * Changing the maximum number of players
32 | * Changing the acceptable answer threshold
33 |
34 | ## Changing the roll call behavior
35 | You can change the [roll call](https://developer.amazon.com/docs/gadget-skills/discover-echo-buttons.html#goals) behavior for the template by changing `ROLL_CALL` value in the [settings file](lambda/custom/config/settings.js)
36 | ```
37 | /**
38 | * ROLLCALL - Control how players register themselves for the game
39 | * QUICK_START
40 | * Allows for all buttons up to GAME.MAX_PLAYERS to press their buttons during
41 | * roll call before the skill will decide they are registered
42 | * NAMED_PLAYERS
43 | * On each button press up to GAME.MAX_PLAYERS, acknowledge the button press
44 | * and call the player out by name
45 | */
46 | ROLLCALL : {
47 | QUICK_START : true,
48 | NAMED_PLAYERS: false
49 | }
50 | ```
51 | ### `QUICK_START` mode
52 | When setting `QUICK_START` to `true`, this means the skill will allow all buttons up to [MAX_PLAYERS](#changing-the-maximum-number-of-players) to be pressed before acknowledging that all players are registered and continuing with the game
53 |
54 | ### `NAMED_PLAYERS` mode
55 | When setting `NAMED_PLAYERS` to `true`, Alexa will acknowledge each player when they press their button during [roll call](https://developer.amazon.com/docs/gadget-skills/discover-echo-buttons.html#goals) by saying 'Hello, player #' where # is a value between 1 and [MAX_PLAYERS](#changing-the-maximum-number-of-players)
56 |
57 | ## Changing the questions and answers
58 | This sample comes with a list of 10 default animal based questions located in [questions.js](lambda/custom/config/questions.js) which you can modify to your liking.
59 | ```
60 | {
61 | index: 1,
62 | question: 'What is the name for a group of lions?',
63 | answers: ['pack', 'pride', 'den', 'frat'],
64 | correct_answer: 'pride'
65 | }
66 | ```
67 |
68 | The question objects in the array have the following properties per question:
69 | * `index` - The ordinal position of the question in the list. Questions will be fetched per this numbering.
70 | * `question` - The question to be asked.
71 | * `answers` - The list of answer options to read to the user.
72 | * `correct_answer` - The correct answer for the question.
73 |
74 | ### Interaction Model Dependencies
75 | You must add any new values in the `answers` array to the `{answers}` slot in your Alexa skill's interaction model. See the section on building the [interaction model](#step-4-create-an-interaction-model) for instructions on how to do that.
76 |
77 |
78 | ## Changing the button colors
79 | You can also adjust the button colors show to the users for the different button events and states in the game.
80 | In general, you want to use visual cueing in the form of button colors to indicate different states and readiness for your game.
81 | ```
82 | COLORS : {
83 | // Color you want the buttons to be when expecting input
84 | QUESTION_COLOR: 'purple',
85 | // Color you want the first button to chime in to be
86 | BUZZ_IN_COLOR: 'blue',
87 | // Color you want the other buttons who didn't chime in
88 | MISSED_BUZZ_IN: 'black',
89 | // Incorrect answer color
90 | INCORRECT_COLOR: 'red',
91 | // Correct color
92 | CORRECT_COLOR: 'green'
93 | }
94 | ```
95 | You can affect the the above list by changing the string value for the color. However, you must make sure the string and its corresponding hex value are present in [colorsList.js](lambda/custom/button_animations/colorsList.js) and if not, you will need to add a mapping of the color's string value, for example `fuschia` to its hex value `FF00FF` to the array of colors in the [colorsList.js](lambda/custom/button_animations/colorsList.js) file.
96 | ```
97 | {
98 | "value":"FF00FF",
99 | "name":"fuschia"
100 | }
101 | ```
102 | ## Changing the sounds
103 | You can change the sounds in the game by changing the value for the `",
121 | WAITING_FOR_BUZZ_IN_AUDIO: "",
122 | BUZZ_IN_AUDIO : "",
123 | CORRECT_ANSWER_AUDIO : "",
124 | INCORRECT_ANSWER_AUDIO : ""
125 | }
126 | ```
127 | ## Changing the screens for Echo Show and Echo Spot
128 | This skill also supports the Echo devices with screens, the Echo Show and the Echo Spot.
129 | To change the available selection you can replace or add to the list of images in any given condition's array of values.
130 | There are multiple images per condition to provide some variability to your players so they aren't always seeing the same
131 | screens. The logic that selects a particular image can be found in the [displayUtil](lambda/custom/utils/displayUtil.js) file.
132 | It effectively selects a random image in this list each time it is called.
133 |
134 | If you are replacing or adding to this list, please make sure the images meet Alexa's [display interface](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#image-size-and-format-allowed-by-display-templates) requirements
135 |
136 | ```
137 | IMAGES :{
138 | BACKGROUND_IMAGES : [
139 | 'https://s3.amazonaws.com/echo-buttons-template/bg1.jpg',
140 | 'https://s3.amazonaws.com/echo-buttons-template/bg2.png'
141 | ],
142 | CORRECT_ANSWER_IMAGES : [
143 | 'https://s3.amazonaws.com/echo-buttons-template/correct1.png',
144 | 'https://s3.amazonaws.com/echo-buttons-template/correct2.png',
145 | 'https://s3.amazonaws.com/echo-buttons-template/correct3.png',
146 | 'https://s3.amazonaws.com/echo-buttons-template/correct4.png'
147 | ],
148 | INCORRECT_ANSWER_IMAGES : [
149 | 'https://s3.amazonaws.com/echo-buttons-template/wrong1.png',
150 | 'https://s3.amazonaws.com/echo-buttons-template/wrong1.png',
151 | 'https://s3.amazonaws.com/echo-buttons-template/wrong1.png',
152 | ]
153 | }
154 | ```
155 |
156 | ## Changing the maximum number of players
157 | You can also affect the maximum number of players by changing the value for `GAME.MAX_PLAYERS`
158 | ```
159 | /**
160 | * GAME - Game settings
161 | * MAX_PLAYERS - A number between 2 and 4
162 | * QUESTIONS - The total number of questions you will ask per game. Must be
163 | * less than or equal to the total number of questions in config/questions.js
164 | * QUESTIONS_PER_ROUND - Number of questions you want to ask before giving a game summary.
165 | * Should divide evenly into the total number of questions.
166 | * ANSWER_SIMILARITY - A percentage value marking how similar an answer need to be to the
167 | * correct answer to be considered correct. Used with the string-similarity package
168 | * See github readme for setup instructions
169 | */
170 | GAME : {
171 | MAX_PLAYERS: 4,
172 | QUESTIONS: 10,
173 | QUESTIONS_PER_ROUND: 5,
174 | ANSWER_SIMILARITY: .60
175 | }
176 | ```
177 | ## Changing the number of rounds per game
178 | You can also affect how many questions Alexa will ask before giving a summary of the scores by changing the value of `GAME.QUESTIONS_PER_ROUND`.
179 | This value **MUST** satisfy the equation `GAME.QUESTIONS_PER_GAME` % `GAME.QUESTIONS_PER_ROUND` = 0 (i.e. it should divide evenly into the total number of questions)
180 | ```
181 | /**
182 | * GAME - Game settings
183 | * MAX_PLAYERS - A number between 2 and 4
184 | * QUESTIONS - The total number of questions you will ask per game. Must be
185 | * less than or equal to the total number of questions in config/questions.js
186 | * QUESTIONS_PER_ROUND - Number of questions you want to ask before giving a game summary.
187 | * Should divide evenly into the total number of questions.
188 | * ANSWER_SIMILARITY - A percentage value marking how similar an answer need to be to the
189 | * correct answer to be considered correct. Used with the string-similarity package
190 | * See github readme for setup instructions
191 | */
192 | GAME : {
193 | MAX_PLAYERS: 4,
194 | QUESTIONS: 10,
195 | QUESTIONS_PER_ROUND: 5,
196 | ANSWER_SIMILARITY: .60
197 | }
198 | ```
199 | ## Changing the acceptable answer threshold
200 | Alexa will always pass what the user said to the skill, whether or not it is a member of the `{answers}` slot. In this regard, direct string matching of answers to correct answers is never a good idea. This example, and a best practice when comparing spoken user input passed in a slot value to expected results, is to use a [string-similarity](https://www.npmjs.com/package/string-similarity) algorithm to compute how similar the strings are.
201 | The fractional (or percentage) value for `GAME.ANSWER_SIMILARITY` is a minimum threshold to allow the passed-in slot value to match the value of `correct_answer` in the [questions](lambda/custom/config/questions.js). For example, at the default setting the spoken answer must be at least 60% similar to the correct answer to be considered a correct response.
202 | ```
203 | /**
204 | * GAME - Game settings
205 | * MAX_PLAYERS - A number between 2 and 4
206 | * QUESTIONS - The total number of questions you will ask per game. Must be
207 | * less than or equal to the total number of questions in config/questions.js
208 | * QUESTIONS_PER_ROUND - Number of questions you want to ask before giving a game summary.
209 | * Should divide evenly into the total number of questions.
210 | * ANSWER_SIMILARITY - A percentage value marking how similar an answer need to be to the
211 | * correct answer to be considered correct. Used with the string-similarity package
212 | * See github readme for setup instructions
213 | */
214 | GAME : {
215 | MAX_PLAYERS: 4,
216 | QUESTIONS: 10,
217 | QUESTIONS_PER_ROUND: 5,
218 | ANSWER_SIMILARITY: .60
219 | }
220 | ```
221 |
222 |
223 | ## License
224 |
225 | This content is licensed under the Amazon Software License.
--------------------------------------------------------------------------------
/instructions/2-lambda-function.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 |
6 | ## Prepare the Code for Deployment
7 | 1. **Clone** the repository.
8 |
9 | ```bash
10 | $ git clone https://github.com/alexa/skill-sample-nodejs-buttons-trivia/
11 | ```
12 |
13 | 2. **Install** npm dependencies by navigating into the `/lambda/custom` directory and running the npm command: `npm install`.
14 |
15 | ```bash
16 | $ cd lambda/custom
17 | $ npm install
18 | ```
19 |
20 | 3. Create a **zip archive** of the code and dependencies while still in the `/lambda/custom` directory.
21 |
22 | ```bash
23 | $ zip -r ../buttons-trivia.zip .
24 | ```
25 |
26 | ## Setting Up A Lambda Function Using Amazon Web Services
27 |
28 | In the [first step of this guide](./1-voice-user-interface.md), we built the Voice User Interface (VUI) for our Alexa skill. On this page, we will be creating an AWS Lambda function using [Amazon Web Services](http://aws.amazon.com). You can [read more about what a Lambda function is](http://aws.amazon.com/lambda), but for the purposes of this guide, what you need to know is that AWS Lambda is where our code lives. When a user asks Alexa to use our skill, it is our AWS Lambda function that interprets the appropriate interaction, and provides the conversation and button actions back to the user(s).
29 |
30 | ### Create the Lambda Function
31 |
32 | 1. Go to **[AWS](https://aws.amazon.com)** and sign in to the console. If you don't already have an account, you will need to create one. [If you don't have an AWS account, check out this quick walkthrough for setting it up](https://github.com/alexa/alexa-cookbook/blob/master/guides/aws-security-and-setup/set-up-aws.md).
33 |
34 | 2. Click **Services** at the top of the screen, and type "Lambda" in the search box. You can also find Lambda in the list of services. It is in the "Compute" section.
35 |
36 | [](https://console.aws.amazon.com/lambda/home)
37 |
38 | 3. **Check your AWS region.** AWS Lambda only works with the Alexa Skills Kit in these regions: US East (N. Virginia), US West (Oregon), Asia Pacific (Tokyo) and EU (Ireland). Make sure you choose the region closest to your customers.
39 |
40 | 
41 |
42 | 4. **Click the orange "Create function" button.** It should be near the top of your screen. (If you don't see this button, it is because you haven't created a Lambda function before. Click the blue "Get Started" button near the center of your screen.)
43 |
44 | 
45 |
46 | 5. There are three boxes labeled "Author from scratch", "Blueprints" and "Serverless Application Repository". **Click the radio button in the box titled "Author From Scratch"**
47 |
48 | 6. In the "Author from Scratch" section give you funcation a **name**, select **Node.js 8.10** as the **Runtime**, and select **Create a custom role** for the **Role**.
49 |
50 | 7. A new window or tab will appear, taking you to the creation of a new IAM role. This sets up permissions for execution of your Lambda. In the **IAM Role** selection choose **lambda_basic_execution** and for **Policy** select **Create a new Role Policy**.
51 |
52 | 
53 |
54 | 8. Click the **Allow** button to return to the previous screen.
55 |
56 | 9. Click the **Create Function** button.
57 |
58 | 
59 |
60 | 10. You are now on the screen that defines your Lambda. Under the **Add Triggers** section on the left select **Alexa Skills Kit** to allow your skill to call this Lambda.
61 |
62 | 
63 |
64 | 11. Under **Configure Triggers**, at the bottom of the page, select **Disable** for **Skill ID Verification**. Next click the **Add** button in the lower right corner.
65 |
66 | **Note:** If you wish to secure this Lambda function in the future there is a guide [here](https://github.com/alexa/alexa-cookbook/blob/master/guides/aws-security-and-setup/secure-lambda-function.md)
67 |
68 | 12. Select your Lambda at the top middle of the page (above the boxes for Alexa Skills Kit and Amazon Cloudwatch Logs) and then scroll down the page until you see a section called **Function code**.
69 |
70 | 
71 |
72 | 13. Change the **Code entry type** to **Upload a ZIP** and select the zip you created in the **Prepare the Code for Deployment** section.
73 |
74 | 
75 |
76 | 14. Save the Lambda by clicking the **Save** button in the upper right corner of the screen.
77 |
78 | 15. You should see the Amazon Resource Name (ARN) a unique identifier for this function in the top right corner of the page. **Copy the ARN value for this Lambda function** for use in the next section of the guide.
79 |
80 | 
81 |
82 | ### Update Lambda Role for DynamoDB Access to Store Player and Game Attributes
83 |
84 | 1. Click **Services** at the top of the screen, and type "IAM" in the search box, and then press enter.
85 |
86 | 2. Click **Roles** in the left hand navigation of the IAM Dashboard.
87 |
88 | 3. Click the role you created above, **lambda_basic_execution**, then click the **Attach Policies** button.
89 |
90 | 4. In the serch box type "Dynamo" and then select the checkbox next to **AmazonDynamoDBFullAccess**. Click the **Attach Policy** button.
91 |
92 | 
93 |
94 |
95 | [](./3-connect-vui-to-code.md)
96 |
--------------------------------------------------------------------------------
/instructions/3-connect-vui-to-code.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 |
6 | ## Connecting Your Voice User Interface To Your Lambda Function
7 |
8 | On the [first page](./1-voice-user-interface.md) of this guide, we created a voice user interface for the intents and utterances we expect from our users. On the [second page](./2-lambda-function.md), we created a Lambda function that contains all of our logic for the skill. On this page, we need to connect those two pieces together.
9 |
10 | 1. Go back to the **[Amazon Developer Portal](https://developer.amazon.com/edw/home.html#/skills/list)** and select your skill from the list. You may still have a browser tab open if you started at the beginning of this tutorial.
11 |
12 | 2. Select the **Endpoint** tab on the left side navigation panel.
13 |
14 | 
15 |
16 | 3. Select the **AWS Lambda ARN** option for your endpoint. You have the ability to host your code anywhere that you would like, but for the purposes of simplicity and frugality, we are using AWS Lambda. ([Read more about Hosting Your Own Custom Skill Web Service](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service).) With the AWS Free Tier, you get 1,000,000 free requests per month, up to 3.2 million seconds of compute time per month. Learn more at [aws.amazon.com/free](https://aws.amazon.com/free/). In addition, Amazon now offers [AWS Promotional Credits for developers who have live Alexa skills that incur costs on AWS related to those skills](https://developer.amazon.com/alexa-skills-kit/alexa-aws-credits).
17 |
18 | 4. Paste your Lambda's ARN (Amazon Resource Name) into the textbox provided for **Default Region**.
19 |
20 | 
21 |
22 | 5. Click the **Save Endpoints** button at the top of the main panel.
23 |
24 | 
25 |
26 | [](./4-testing.md)
27 |
--------------------------------------------------------------------------------
/instructions/4-testing.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 |
6 | ## Testing Your Alexa Skill
7 |
8 | So far, we have [created a Voice User Interface](./1-voice-user-interface.md), [a Lambda function](./2-lambda-function.md), and [connected the two together](./3-connect-vui-to-code.md). Your skill is now ready to test.
9 |
10 | 1. Go back to the **[Amazon Developer Portal](https://developer.amazon.com/edw/home.html#/skills/list)** and select your skill from the list. You may still have a browser tab open if you started at the beginning of this tutorial.
11 |
12 | 2. Open the **Test** Pane, by selecting the **Test** link from the top navigation menu.
13 |
14 | 
15 |
16 | 3. Enable Testing by activating the **Test is enabled for this skill** slider. It should be underneath the top navigation menu.
17 |
18 | 
19 |
20 | 4. To validate that your skill is working as expected, invoke your skill from the **Alexa Simulator**. You can either type or click and hold the mic from the input box to use your voice.
21 | 1. **Type** "Open" followed by the invocation name you gave your skill in [Step 1](./1-voice-user-interface.md). For example, "Open Buttons Trivia".
22 | 2. **Use your voice** by clicking and holding the mic on the side panel and saying "Open" followed by the invocation name you gave your skill.
23 | 3. **If you've forgotten the invocation name** for your skill, revisit the **Build** panel on the top navigation menu and select Invocation from the sidebar to review it.
24 |
25 | 5. Use the virtual Echo Buttons in the simulator to play your game.
26 |
27 | 
28 |
29 | 6. Ensure your skill works the way that you designed it to.
30 | * After you interact with the Alexa Simulator, you should see the Skill I/O **JSON Input** and **JSON Output** boxes get populated with JSON data. You can also view the **Device Log** to trace your steps.
31 | * If it's not working as expected, you can dig into the JSON to see exactly what Alexa is sending and receiving from the endpoint. If something is broken, viewing the log output produced by your Lambda code offers additional insight.
32 |
33 | 7. **Viewing Lambda Logs in Cloud Watch**.
34 | * Go back to **[AWS](https://aws.amazon.com)** and sign in to the console.
35 | * Click **Services** at the top of the screen, and type "CloudWatch" in the search box. You can also find Lambda in the list of services. It is in the "Management Tools" section.
36 |
37 | [](https://console.aws.amazon.com/cloudwatch/home)
38 |
39 | * Select **Logs** from the left side menu.
40 |
41 | 
42 |
43 | * Select the log group with the name matching the name you gave your Lambda function. You will now be presented with a list of logs by date that you can view for more insight on what is happening with the code.
44 |
45 | 8. **Other testing methods to consider:**
46 |
47 | * [Virtual Alexa](https://github.com/bespoken/virtual-alexa) from [Bespoken.io](https://bespoken.io/) - provides a mechanism to easily test Alexa skills programatically.
48 | * [Echosim.io](https://echosim.io) - a browser-based Alexa skill testing tool that makes it easy to test your skills without carrying a physical device everywhere you go.
49 |
50 | 9. If your sample skill is working properly, you can now customize your skill.
51 |
52 | [](./5-customization.md)
53 |
--------------------------------------------------------------------------------
/instructions/5-customization.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 |
6 | ## Customize the Game to be Yours
7 |
8 | At this point, you should have a working copy of our Trivia skill. In order to make it your own, you will need to customize it with data and responses that you create. Here are the things you will need to change:
9 |
10 | 1. **New questions**. You will need to provide a set of trivia for your topic. We recommend a minimum of 25, but a total closer to 100 offers a better experience.
11 |
12 | 1. **Open /lambda/custom/config/questions.js.** You can use a simple, lightweight code editor like [Atom](http://atom.io), [Sublime Text](http://sublimetext.com), or [VSCode](http://code.visualstudio.com)
13 |
14 | 2. This file has a fairly simple format. There is a question **index**, the **question** itself, a list of possible **answers**, and the **correct_answer**. Simply replace the text with your new questions and answers. To add more just copy existing blocks and append to the end. Make sure each block ends with a comma. Also make sure each new question gets a new unique **index**. This is how questions are looked up with the questions are randomized.
15 |
16 | 2. **New Voice Responses**. There are several voice prompts and responses that you will want to customize for your skill.
17 |
18 | 1. Open **/lambda/custom/config/messages.js.**
19 |
20 | 2. Replace **GAME_TITLE**. This is the name of your game and it is used in many of the other responses so make sure you do this first.
21 |
22 | 3. Continue through **messages.js** until you reach the bottom of the file. This will ensure that you cover each of the values that you need or want to update.
23 |
24 | 3. **Additional languages**. If you are creating this skill for multiple languages, you will need to make sure Alexa's responses are also in that language.
25 |
26 | 1. Both **/lambda/custom/config/messages.js** and **/lambda/custom/config/questions.js** have sections to override for **en-GB**. To add additional languages simply follow the existing format to add them based on the locale code.
27 |
28 | 2. For example, if you are creating your skill in German, every single response that Alexa makes has to be in German. You can't use English responses or your skill will fail certification.
29 |
30 | 4. **Game Options** including **Sounds** and **Button Animations**
31 |
32 | 1. Open **/lambda/custom/config/settings.js.**
33 |
34 | 2. You will find a variety of easily modified settings in this file, each well documented. Some particularily interesting ones might be:
35 | * **GAME_OPTIONS** - Here you can change things like the number of players allowed, the number of questions to ask, and if the questions should be asked in order or should be randomized at the beginning of the game.
36 | * **COLORS** - You can change the colors used for the animations easily here in variables like **QUESTION_COLOR** and **BUZZ_IN_COLOR**.
37 | * **GAME_ANIMATIONS** - If you're feeling more adventerous you can play with the timings of the animations in this section. Details on how to build animations can be found [here](https://developer.amazon.com/docs/gadget-skills/echo-button-animations.html).
38 | * **AUDIO** - This has links to the audio files used in the game. If you change these note that they need to be server over a SSL connection from the web - they cannot be local to the Lambda.
39 |
40 | 5. Once you have made the updates you would like you will need to redeploy your code by creating and uploading a new zip file as detailed in [Step 2](./2-lambda-function.md).
41 |
42 | 6. Once things are deployed you can return to the Developer Console and click **Next** to move on to Publishing and Certification of your skill.
43 |
44 | [](6-publication.md)
45 |
--------------------------------------------------------------------------------
/instructions/6-publication.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | [](./1-voice-user-interface.md)[](./2-lambda-function.md)[](./3-connect-vui-to-code.md)[](./4-testing.md)[](./5-customization.md)[](./6-publication.md)
5 | ## Get Your Skill Certified and Published
6 |
7 | We are almost done! The last step is to add the metadata that your skill will use in the [Skill Store](http://amazon.com/skills). This page will walk you through the remaining steps to launch your skill!
8 |
9 | ### Distribution
10 |
11 | 
12 |
13 | 1. Select the **Distribution** link from the top navigation menu.
14 |
15 | #### NOTE
16 |
17 | You will fill out all of the fields in the **Distribution** section for every locale you have created a language model for. We recommend submitting to the US and UK at a minimum. In this example we have created models for both and will need to fill out these questions for each.
18 |
19 | 
20 |
21 | 
22 |
23 | 2. Fill out the form fields per the guidance on the screen. Hover over the question mark icons for details regarding each respective field. **Fields marked with an Asterisk, are required!**
24 | * Take the time to get these right so that your skill will pass certification!
25 |
26 | 3. Write your skill descriptions.
27 |
28 | * **Spend some time coming up with an enticing, succinct description.** This is one of the few places you have an opportunity to attract new users, so make the most of it! These descriptions show up in the list of skills available in the [Alexa app](http://alexa.amazon.com/spa/index.html#skills) and the [skills store](http://www.amazon.com/skills).
29 |
30 | 4. For your example phrases, **come up with the three most exciting ways** a user can talk to your skill.
31 |
32 | * Make sure that each of your example phrases are a **perfect match with one of your Sample Utterances.** Incorrect example phrases are one of the most common reasons that skills fail certification, so we have provided a short list of things to consider as you write your example phrases:
33 |
34 | | Common Failure Points for Example Phrases |
35 | | ----------------------------------------- |
36 | | Example phrases **must** adhere to the [supported phrases](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/supported-phrases-to-begin-a-conversation). |
37 | | Example phrases **must** be based on sample utterances specified in your Intent Schema. |
38 | | Your first example phrase **must** include a wake word and your invocation name. |
39 | | Example phrases **must** provide a contextual response. |
40 |
41 | * **Choose three example phrases that are likely to be the most common ways that users will attempt to interact with your skill.** Make sure that each of them works well, and provides an excellent user experience.
42 |
43 | 5. **Create your skill's icons.** You need two sizes of your icon: 108x108px and 512x512px.
44 |
45 |
46 | * **Make sure you have the rights to the icons you create.** Please don't violate any trademarks or copyrights.
47 | * **If you don't have software to make icons, try one of these free options:**
48 |
49 | * [GIMP](https://www.gimp.org/) (Windows/Mac/Linux)
50 | * [Canva](https://www.canva.com/) (Web)
51 | * [Paint.NET](http://www.getpaint.net/index.html) (Windows)
52 | * [Inkscape](http://inkscape.org) (Windows/Mac/Linux)
53 | * [Iconion](http://iconion.com/) (Windows/Mac)
54 |
55 | 6. Choose the most appropriate category for your skill.
56 |
57 | 7. **Provide a comprehensive list of keywords for users that are searching for new skills.** This is an optional field, and searching the [Alexa app](http://alexa.amazon.com) or the [skill store](http://www.amazon.com/skills) will also find the words in your Skill Name and descriptions, so you don't need to overdo it. That being said, if there are words that you want users to find your skill with, you should include them here. Separate the keywords with commas.
58 |
59 | 8. **Privacy Policy URL.** This is an optional field, and should not be required for this Trivia skill sample. You can leave it blank.
60 |
61 | 9. **Terms of Use URL.** This is also optional, and you can leave it blank.
62 |
63 | 10. When you're ready, click **Save and Continue** at the bottom of the screen to move onto the **Privacy & Compliance** section.
64 |
65 | ### Privacy and Compliance
66 |
67 | 1. **Does this skill allow users to make purchases or spend real money?** For this trivia skill, the answer is no. For future skills, make sure you answer this appropriately.
68 |
69 | 2. **Does this Alexa skill collect users' personal information?** Again, for this trivia skill, the answer is no. If you do collect information about a user, such as names, email addresses, phone numbers, and so forth, ensure that you answer Yes to this question.
70 | * Answering "yes" to this question will also require you to provide a link to your Privacy Policy at the bottom of the page.
71 |
72 | 3. **Is your skill directed to children under the age of 13?** Because you customized this skill with data you provided, it is possible that you created a skill that targets children under the age of 13. For this trivia skill, the answer is **no** because it doesn't target a specific age group.
73 | * Factors to consider in determining if this skill is directed to children under 13 include:
74 | * Subject matter of the skill
75 | * Presence of child-oriented activities and incentives
76 | * Type of language used in the skill
77 | * Music and other audio content in the skill
78 | * How the skill is described and marketed
79 | * Intended audience for the skill
80 |
81 | If you're not sure, please see the [FTC's COPPA Guidance and FAQ](https://www.ftc.gov/tips-advice/business-center/guidance/complying-coppa-frequently-asked-questions) for more information.
82 |
83 | 4. **Export Compliance.** Be certain that you agree with all of the conditions. If you do, make sure to check this box, as Amazon requires this permission to distribute your skill around the globe.
84 |
85 | 5. **Provide testing instructions.** Testing instructions give you an opportunity to explain your skill, and any special or possibly confusing features, to the certification team. A value is required in this box.
86 |
87 | * Since you are using our Sample, make sure to add a sentence to your Testing Instructions referencing the Sample you used. For example:
88 |
89 | ```
90 | This was built using the Buttons Trivia Sample.
91 | ```
92 |
93 | This will let the testing team understand what you're providing them, and should decrease the testing time required.
94 |
95 | **Note:** More details on certification are [available here.](https://alexa.design/certification)
96 |
97 | 6. When you're ready, click **Save and Continue** at the bottom of the screen to move onto **Availability**.
98 |
99 | ### Availability
100 |
101 | 1. Here you can select to make this skill available to the public or just your business. You can also set up a beta test for you skill. More information is available at [Beta Testing for Alexa Skills](https://developer.amazon.com/docs/custom-skills/skills-beta-testing-for-alexa-skills.html).
102 |
103 | 2. Select the regions to make your game available to. In this example we language models for both the UK and the US so we will select those countries.
104 |
105 | 
106 |
107 | 3. Click the **Save and Continue** button at the bottom of the page to move to the final step before submitting your game for certification.
108 |
109 | ### Certification
110 |
111 | 
112 |
113 | 1. On the final page of this flow you will run some validation and functional tests. Correct any errors that are reported back to you. These will generally report back issues dealing the the values you've previously entered or glaring problems with the skill such as bad certificates or missing endpoints. When they both pass click **Submit** to sumbmit your game for certification.
114 |
115 | 2. **You're done with your submission!** Here are a few things you might need to know:
116 |
117 | * **Certification can take several days to complete.** Please be patient. It takes time because we want to get it right.
118 |
119 | * **Did something go wrong?** Our team of evangelists run [online office hours every Tuesday from 1-2pm Pacific Time on Twitch](https://www.twitch.tv/amazonalexa). They can help answer any questions you might have.
120 |
121 | * **Want the coolest t-shirt you've ever seen?** Every month, we create a brand-new Alexa Developer t-shirt or hoodie, and send them out to developers that published a skill that month. [You can get yours here if you live in the US](https://developer.amazon.com/alexa-skills-kit/alexa-developer-skill-promotion), [here for the UK](https://developer.amazon.com/en-gb/alexa-skills-kit/alexa-developer-skill-promotion), and [here for Germany](https://developer.amazon.com/de-de/alexa-skills-kit/alexa-developer-skill-promotion).
122 |
--------------------------------------------------------------------------------
/instructions/7-cli.md:
--------------------------------------------------------------------------------
1 | # Build An Alexa 'Better with Buttons' Trivia Game
2 |
3 |
4 | ## Setup using the ASK CLI
5 |
6 | ### About
7 | This readme assumes you have your developer environment ready to go and that you have some familiarity with CLI (Command Line Interface) Tools, [AWS](https://aws.amazon.com/), and the [ASK Developer Portal](https://developer.amazon.com/alexa). If not, [click here](./1-voice-user-interface.md) for a more detailed walkthrough.
8 |
9 | ### Pre-requisites
10 |
11 | * Node.js (> v8)
12 | * Register for an [AWS Account](https://aws.amazon.com/)
13 | * Register for an [Amazon Developer Account](https://developer.amazon.com/alexa)
14 | * Install and Setup [ASK CLI](https://developer.amazon.com/docs/smapi/quick-start-alexa-skills-kit-command-line-interface.html)
15 |
16 | ### Installation
17 | 1. **Make sure** you are running the latest version of the CLI
18 |
19 | ```bash
20 | $ npm update -g ask-cli
21 | ```
22 |
23 | 2. **Clone** the repository.
24 |
25 | ```bash
26 | $ git clone https://github.com/alexa/skill-sample-nodejs-buttons-trivia/
27 | ```
28 |
29 | 3. If it's your first time using it, **initiatialize** the [ASK CLI](https://developer.amazon.com/docs/smapi/quick-start-alexa-skills-kit-command-line-interface.html) by navigating into the repository and running npm command: `ask init`. Follow the prompts.
30 |
31 | ```bash
32 | $ cd skill-sample-nodejs-trivia
33 | $ ask init
34 | ```
35 |
36 | 4. Install npm dependencies by navigating into the `/lambda/custom` directory and running the npm command: `npm install --save`
37 |
38 | ```bash
39 | $ cd lambda/custom
40 | $ npm install
41 | ```
42 |
43 | ### Deployment
44 |
45 | ASK CLI **will create the skill and the lambda function for you**. The Lambda function will be created in ```us-east-1 (Northern Virginia)``` by default.
46 |
47 | 1. Navigate to the project's root directory. you should see a file named 'skill.json' there.
48 | 2. Deploy the skill and the lambda function in one step by running the following command:
49 |
50 | ```bash
51 | $ ask deploy
52 | ```
53 |
54 | ##### Add DynamoDB permissions to the Lambda role
55 |
56 | The ASK CLI automatically created a role for your lambda function to run under that can execute lambda functions and write to cloudwatch logs, but since we are using the [built-in persistence](https://ask-sdk-for-nodejs.readthedocs.io/en/latest/Managing-Attributes.html#persistent-attributes) of the [Alexa Skills Kit for NodeJS SDK](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/), we need to add a policy that will allow the role to create/read/write to DynamoDB
57 |
58 | 1. Sign in to the [AWS Management Console](https://console.aws.amazon.com/console/home)
59 | 2. Navigate to the [IAM Service console](https://console.aws.amazon.com/iam/home) which is located under **Security, Identity & Compliance**.
60 | 3. Click Roles on the left side bar and find the role created for you by the ASK CLI. The name should start with **ask-lambda-**. Click the role name to see the details.
61 | 4. On the Permissions tab, click the **Attach Policy** button
62 | 5. In the search box, search for **AmazonDynamoDBFullAccess**, select the policy by clicking the checkbox to the left, and click the **Attach Policy** button at the bottom of the page.
63 |
64 | ### Testing
65 |
66 | 1. To test, you need to login to Alexa Developer Console, and **enable the "Test" switch on your skill from the "Test" Tab**.
67 |
68 | 2. Simulate verbal interaction with your skill through the command line (this might take a few moments) using the following example:
69 |
70 | ```bash
71 | $ ask simulate -l en-GB -t "start better buttons trivia"
72 |
73 | ✓ Simulation created for simulation id: 4a7a9ed8-94b2-40c0-b3bd-fb63d9887fa7
74 | ◡ Waiting for simulation response{
75 | "status": "SUCCESSFUL",
76 | ...
77 | ```
78 |
79 | 3. Once the "Test" switch is enabled, your skill can be tested on devices associated with the developer account as well. Speak to Alexa from any enabled device, from your browser at [echosim.io](https://echosim.io/welcome), or through your Amazon Mobile App and say :
80 |
81 | ```text
82 | Alexa, start better buttons trivia
83 | ```
84 | ## Customization
85 |
86 | 1. ```./skill.json```
87 |
88 | Change the skill name, example phrase, icons, testing instructions etc ...
89 |
90 | Remember than many of the details are locale-specific and must be changed for each locale (en-GB and en-US)
91 |
92 | See the Skill [Manifest Documentation](https://developer.amazon.com/docs/smapi/skill-manifest.html) for more information.
93 |
94 | 2. ```./lambda/custom/config/messages.js, ./lambda/custom/config/questions.js, ./lambda/custom/config/settings.js```
95 |
96 | Modify messages, questiosn, and settings from the source code to customize the skill. More information can be found in [Customizations](./5-customization.md)
97 |
98 | 3. ```./models/*.json```
99 |
100 | Change the model definition to replace the invocation name and the sample phrase for each intent. Repeat the operation for each locale you are planning to support.
101 |
102 | 4. Remember to re-deploy your skill and lambda function for your changes to take effect.
103 |
--------------------------------------------------------------------------------
/instructions/images/arn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/arn.png
--------------------------------------------------------------------------------
/instructions/images/ask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/ask.png
--------------------------------------------------------------------------------
/instructions/images/availability.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/availability.png
--------------------------------------------------------------------------------
/instructions/images/build.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/build.png
--------------------------------------------------------------------------------
/instructions/images/buttons-trivia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/buttons-trivia.png
--------------------------------------------------------------------------------
/instructions/images/certification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/certification.png
--------------------------------------------------------------------------------
/instructions/images/cloudwatch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/cloudwatch.png
--------------------------------------------------------------------------------
/instructions/images/create-function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/create-function.png
--------------------------------------------------------------------------------
/instructions/images/create-lambda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/create-lambda.png
--------------------------------------------------------------------------------
/instructions/images/custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/custom.png
--------------------------------------------------------------------------------
/instructions/images/distribution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/distribution.png
--------------------------------------------------------------------------------
/instructions/images/dynamo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/dynamo.png
--------------------------------------------------------------------------------
/instructions/images/echo-buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/echo-buttons.png
--------------------------------------------------------------------------------
/instructions/images/endpoint-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/endpoint-tab.png
--------------------------------------------------------------------------------
/instructions/images/endpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/endpoint.png
--------------------------------------------------------------------------------
/instructions/images/function-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/function-code.png
--------------------------------------------------------------------------------
/instructions/images/gadget-interfaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/gadget-interfaces.png
--------------------------------------------------------------------------------
/instructions/images/interfaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/interfaces.png
--------------------------------------------------------------------------------
/instructions/images/json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/json.png
--------------------------------------------------------------------------------
/instructions/images/lambda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/lambda.png
--------------------------------------------------------------------------------
/instructions/images/logs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/logs.png
--------------------------------------------------------------------------------
/instructions/images/role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/role.png
--------------------------------------------------------------------------------
/instructions/images/save-endpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/save-endpoint.png
--------------------------------------------------------------------------------
/instructions/images/test-enable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/test-enable.png
--------------------------------------------------------------------------------
/instructions/images/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/test.png
--------------------------------------------------------------------------------
/instructions/images/uk-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/uk-store.png
--------------------------------------------------------------------------------
/instructions/images/us-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/us-store.png
--------------------------------------------------------------------------------
/instructions/images/useast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexa-samples/skill-sample-nodejs-buttons-trivia/758e12c249944e5890633e5d8637a669bcbd2923/instructions/images/useast.png
--------------------------------------------------------------------------------
/lambda/custom/config/questions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /*
3 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
4 | * Licensed under the Amazon Software License (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | * http://aws.amazon.com/asl/
8 | * or in the "license" file accompanying this file. This file is distributed
9 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10 | * express or implied. See the License for the specific language governing
11 | * permissions and limitations under the License.
12 | */
13 |
14 |
15 | /**
16 | * Questions library
17 | *
18 | * Use this file to create your own set of questions.
19 | *
20 | * Object properties:
21 | * index: The index of the question in this list
22 | * question: The question you want Alexa to ask
23 | * answers: The list of available answers
24 | * correct_answer: The correct answer to the question
25 | *
26 | * When adding or updating questions and answers, you must take the list of all values
27 | * in each of the 'answers' arrays for all questions and add them to a custom slot
28 | * in your skill called 'answers'.
29 | *
30 | * The 'answers' custom slot is be mapped to a couple of intents in the interaction model.
31 | * One intent, named 'AnswerOnlyIntent', contains only the slot, by itself, in order
32 | * to maximize the accuracy of the model.
33 | *
34 | * For example:
35 | * AnswerOnlyIntent {answers}
36 | *
37 | * The other intent, 'AnswerQuestionIntent', provides more complex speech patterns
38 | * to match other utternaces users may include with their answers.
39 | *
40 | * For example:
41 | * AnswerQuestionIntent is it {answers}
42 | * AnswerQuestionIntent it is {answers}
43 | * AnswerQuestionIntent the answer is {answers}
44 | * AnswerQuestionIntent I think the answer is {answers}
45 | *
46 | * See model file at models/en-US.json for a complete example.
47 | */
48 | module.exports = Object.freeze({
49 | questions_en_US: [
50 | {
51 | index: 1,
52 | question: 'What is the name for a group of lions?',
53 | answers: ['pack', 'pride', 'den', 'frat'],
54 | correct_answer: 'pride'
55 | },
56 | {
57 | index: 2,
58 | question: 'What is the only type of bear native to South America?',
59 | answers: ['brown bear', 'kodiac', 'giant panda', 'spectacled bear'],
60 | correct_answer: 'spectacled bear'
61 | },
62 | {
63 | index: 3,
64 | question: 'What type of animal is a seahorse?',
65 | answers: ['crustacean', 'arachnid', 'fish', 'shell'],
66 | correct_answer: 'fish'
67 | },
68 | {
69 | index: 4,
70 | question: 'What color are zebras?',
71 | answers: ['white with black stripes', 'black with white stripes'],
72 | correct_answer: 'black with white stripes'
73 | },
74 | {
75 | index: 5,
76 | question: 'What is the fastest water animal?',
77 | answers: ['porpoise', 'sailfish', 'flying fish', 'tuna'],
78 | correct_answer: 'sailfish'
79 | },
80 | {
81 | index: 6,
82 | question: 'What is the only venomous snake found in Britain?',
83 | answers: ['cobra', 'coral snake', 'copperhead', 'adder'],
84 | correct_answer: 'adder'
85 | },
86 | {
87 | index: 7,
88 | question: 'What is a female donkey called?',
89 | answers: ['joey', 'jenny', 'janet'],
90 | correct_answer: 'jenny'
91 | },
92 | {
93 | index: 8,
94 | question: 'What land mammal other than man has the longest lifespan?',
95 | answers: ['blue whale', 'dolphin', 'elephant', 'orangutan'],
96 | correct_answer: 'elephant'
97 | },
98 | {
99 | index: 9,
100 | question: 'Eskimos call what kind of creature a nanook?',
101 | answers: ['penguin', 'narwhal', 'polar bear', 'caribou'],
102 | correct_answer: 'polar bear'
103 | },
104 | {
105 | index: 10,
106 | question: 'Lupus is the Latin name for what animal?',
107 | answers: ['dog', 'cat', 'wolf', 'fox'],
108 | correct_answer: 'wolf'
109 | }
110 |
111 | ],
112 | questions_en_GB: [
113 | {
114 | index: 1,
115 | question: 'What is the name for a group of lions?',
116 | answers: ['pack', 'pride', 'den', 'frat'],
117 | correct_answer: 'pride'
118 | },
119 | {
120 | index: 2,
121 | question: 'What is the only type of bear native to South America?',
122 | answers: ['brown bear', 'kodiac', 'giant panda', 'spectacled bear'],
123 | correct_answer: 'spectacled bear'
124 | },
125 | {
126 | index: 3,
127 | question: 'What type of animal is a seahorse?',
128 | answers: ['crustacean', 'arachnid', 'fish', 'shell'],
129 | correct_answer: 'fish'
130 | },
131 | {
132 | index: 4,
133 | question: 'What color are zebras?',
134 | answers: ['white with black stripes', 'black with white stripes'],
135 | correct_answer: 'black with white stripes'
136 | },
137 | {
138 | index: 5,
139 | question: 'What is the fastest water animal?',
140 | answers: ['porpoise', 'sailfish', 'flying fish', 'tuna'],
141 | correct_answer: 'sailfish'
142 | },
143 | {
144 | index: 6,
145 | question: 'What is the only venomous snake found in Britain?',
146 | answers: ['cobra', 'coral snake', 'copperhead', 'adder'],
147 | correct_answer: 'adder'
148 | },
149 | {
150 | index: 7,
151 | question: 'What is a female donkey called?',
152 | answers: ['joey', 'jenny', 'janet'],
153 | correct_answer: 'jenny'
154 | },
155 | {
156 | index: 8,
157 | question: 'What land mammal other than man has the longest lifespan?',
158 | answers: ['blue whale', 'dolphin', 'elephant', 'orangutan'],
159 | correct_answer: 'elephant'
160 | },
161 | {
162 | index: 9,
163 | question: 'Eskimos call what kind of creature a nanook?',
164 | answers: ['penguin', 'narwhal', 'polar bear', 'caribou'],
165 | correct_answer: 'polar bear'
166 | },
167 | {
168 | index: 10,
169 | question: 'Lupus is the Latin name for what animal?',
170 | answers: ['dog', 'cat', 'wolf', 'fox'],
171 | correct_answer: 'wolf'
172 | }
173 | ],
174 | questions_de_DE: [
175 | {
176 | index: 1,
177 | question: 'Wie wird eine Gruppe von Löwen genannt?',
178 | answers: ['Rotte', 'Rudel', 'Schule', 'Meute'],
179 | correct_answer: 'Rudel'
180 | },
181 | {
182 | index: 2,
183 | question: 'Welches ist die einzige aus Südamerika stammende Bärenart?',
184 | answers: ['Braunbär', 'Kodiakbär', 'Riesenpanda', 'Brillenbär'],
185 | correct_answer: 'Brillenbär'
186 | },
187 | {
188 | index: 3,
189 | question: 'Was für eine Art Tier ist ein Seepferdchen?',
190 | answers: ['Krustentier', 'Spinnentier', 'Fisch', 'Muschel'],
191 | correct_answer: 'Fisch'
192 | },
193 | {
194 | index: 4,
195 | question: 'Welche Farbe haben Zebras?',
196 | answers: ['Weiß mit schwarzen Streifen', 'Schwarz mit weißen Streifen'],
197 | correct_answer: 'Schwarz mit weißen Streifen'
198 | },
199 | {
200 | index: 5,
201 | question: 'Was ist das schnellste Wassertier?',
202 | answers: ['Schweinswal', 'Fächerfisch', 'Fliegender Fisch', 'Thunfisch'],
203 | correct_answer: 'Fächerfisch'
204 | },
205 | {
206 | index: 6,
207 | question: 'Welches ist die einzige giftige Schlange in Großbritannien?',
208 | answers: ['Kobra', 'Korallenschlange', 'Mokassinschlange', 'Kreuzotter'],
209 | correct_answer: 'Kreuzotter'
210 | },
211 | {
212 | index: 7,
213 | question: 'Was ist der Name eines berühmten männlichen Eisbären?',
214 | answers: ['Sven', 'Knut', 'Olaf'],
215 | correct_answer: 'Knut'
216 | },
217 | {
218 | index: 8,
219 | question: 'Welches Landsäugetier hat abgesehen vom Menschen die längste Lebensdauer?',
220 | answers: ['Blauwal', 'Delfin', 'Elefant', 'Orang-Utan'],
221 | correct_answer: 'Elefant'
222 | },
223 | {
224 | index: 9,
225 | question: 'Welches Tier bezeichnen die Eskimos als Nanook?',
226 | answers: ['Pinguin', 'Narwal', 'Eisbär', 'Karibu'],
227 | correct_answer: 'Eisbär'
228 | },
229 | {
230 | index: 10,
231 | question: 'Lupus ist der lateinische Name für welches Tier?',
232 | answers: ['Hund', 'Katze', 'Wolf', 'Fuchs'],
233 | correct_answer: 'Wolf'
234 | }
235 | ]
236 | });
237 |
--------------------------------------------------------------------------------
/lambda/custom/config/settings.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | /*
3 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
4 | * Licensed under the Amazon Software License (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | * http://aws.amazon.com/asl/
8 | * or in the "license" file accompanying this file. This file is distributed
9 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
10 | * express or implied. See the License for the specific language governing
11 | * permissions and limitations under the License.
12 | */
13 |
14 | const animations = require('../utils/animations.js');
15 |
16 | /**
17 | * Settings file
18 | *
19 | * Use this file to configure the behavior of your trivia game
20 | */
21 | module.exports = (function () {
22 | /**
23 | * APP_ID:
24 | * The skill ID to be matched against requests for confirmation.
25 | * It helps protect against spamming your skill.
26 | * Replace this with the value of your skill ID to enable this protection.
27 | */
28 | const APP_ID = '';
29 |
30 | /**
31 | * GAME - Game settings
32 | * MAX_PLAYERS - A number between 2 and 4
33 | * QUESTIONS - The total number of questions you will ask per game. Must be
34 | * less than or equal to the total number of questions in config/questions.js
35 | * QUESTIONS_PER_ROUND - Number of questions you want to ask before giving a game summary.
36 | * Should divide evenly into the total number of questions.
37 | * ANSWER_SIMILARITY - A percentage value marking how similar an answer need to be to the
38 | * correct answer to be considered correct. Used with the string-similarity package
39 | * See github readme for setup instructions
40 | * MAX_ANSWERS_PER_QUESTION - Maximum number of answers allowed for each question.
41 | * SHUFFLE_QUESTIONS - if enabled, questions are presented in randomized order, otherwise
42 | * each question is presented in the same answer as they are listed in the questions file.
43 | * NOTIFY_CORRECT_ANSWER - if enabled Alexa will let the players know what the correct answer
44 | * was if everyone has answered incorrectly
45 | * MULTIPLE_CHOICE - Offer answer options as defined in the answers array in question.js. If
46 | * this is false only the question will be asked with the answer compared directly against
47 | * correct_answer from questions.js.
48 | */
49 | const GAME_OPTIONS = {
50 | MAX_PLAYERS: 4,
51 | QUESTIONS_PER_GAME: 6,
52 | QUESTIONS_PER_ROUND: 2,
53 | ANSWER_SIMILARITY: .60,
54 | MAX_ANSWERS_PER_QUESTION: 4,
55 | SHUFFLE_QUESTIONS: true,
56 | NOTIFY_CORRECT_ANSWER: true,
57 | MULTIPLE_CHOICE: true
58 | };
59 |
60 | /**
61 | * ROLLCALL - Control how players register themselves for the game
62 | * QUICK_START
63 | * Allows for all buttons up to GAME.MAX_PLAYERS to press their buttons during
64 | * roll call before the skill will decide they are registered
65 | * NAMED_PLAYERS
66 | * On each button press up to GAME.MAX_PLAYERS, acknowledge the button press
67 | * and call the player out by name
68 | */
69 | const ROLLCALL_STATES = {
70 | QUICK_START: false,
71 | NAMED_PLAYERS: true
72 | };
73 |
74 | /**
75 | * STORAGE.SESSION_TABLE:
76 | * The name of the table in DynamoDB where you want to store session and game data.
77 | * You can leave this empty if you do not wish to use DynamoDB to automatically
78 | * store game data between sessions after each request.
79 | */
80 | const STORAGE = {
81 | // Session persistence
82 | SESSION_TABLE: 'better-with-buttons-trivia'
83 | };
84 |
85 | /**
86 | * COLORS - Change the behavior and colors of the buttons
87 | *
88 | * QUESTION_COLOR - The color the buttons will be when a question is asked.
89 | * This is the signal to the users that they should buzz in
90 | * BUZZ_IN_COLOR - The color to change the buttons to when someone buzzes in
91 | * MISSED_BUZZ_IN - This is the color other buttons will turn when the first player
92 | * buzzes in. In this case 'black' is off
93 | * INCORRECT_COLOR - The color the button will blink when a player gets a question correct
94 | * CORRECT_COLOR - The color a button will blink when the answering player gets the question
95 | * correct.
96 | */
97 | const COLORS = Object.freeze({
98 | // Color you want the buttons to be when expecting input
99 | QUESTION_COLOR: 'purple',
100 | // Color you want the first button to chime in to be
101 | BUZZ_IN_COLOR: 'blue',
102 | // Color you want the other buttons who didn't chime in
103 | MISSED_BUZZ_IN: 'black',
104 | // Incorrect answer color
105 | INCORRECT_COLOR: 'red',
106 | // Correct color
107 | CORRECT_COLOR: 'green',
108 | // Exit color
109 | EXIT_COLOR: 'white'
110 | });
111 |
112 | /**
113 | * AUDIO - Links to sound effects used in the game
114 | * ROLL_CALL_COMPLETE
115 | * Once all players have buzzed in, play this sound
116 | * WAITING_FOR_BUZZ_IN_AUDIO
117 | * A ticking sound used to indicate that the skill is waiting for a button press
118 | * BUZZ_IN_AUDIO
119 | * The sound to play when a user 'buzzes in' and is ready to answer a question
120 | * CORRECT_ANSWER_AUDIO
121 | * A sound effect to play when the users answer correctly
122 | * INCORRECT_ANSWER_AUDIO
123 | * The sound effect to play when a user answers incorrectly
124 | */
125 | const AUDIO = Object.freeze({
126 | WAITING_FOR_ROLL_CALL_AUDIO: "",
127 | ROLL_CALL_COMPLETE: "",
128 | WAITING_FOR_BUZZ_IN_AUDIO: "",
129 | BUZZ_IN_AUDIO: "",
130 | CORRECT_ANSWER_AUDIO: "",
131 | INCORRECT_ANSWER_AUDIO: ""
132 | });
133 |
134 | /**
135 | * A set of images to show on backgrounds and in display templates when the skill
136 | * is used with a device with a screen like the Echo Show or Echo Spot
137 | * https://developer.amazon.com/docs/custom-skills/display-interface-reference.html
138 | *
139 | * The skill template chooses images randomly from each array to provide some
140 | * variety for the user.
141 | */
142 | const IMAGES = Object.freeze({
143 | BACKGROUND_IMAGES: [
144 | 'https://d2vbr0xakfjx9a.cloudfront.net/bg1.jpg',
145 | 'https://d2vbr0xakfjx9a.cloudfront.net/bg2.jpg'
146 | ],
147 | CORRECT_ANSWER_IMAGES: [
148 | 'https://d2vbr0xakfjx9a.cloudfront.net/correct1.png',
149 | 'https://d2vbr0xakfjx9a.cloudfront.net/correct2.png',
150 | 'https://d2vbr0xakfjx9a.cloudfront.net/correct3.png',
151 | 'https://d2vbr0xakfjx9a.cloudfront.net/correct4.png'
152 | ],
153 | INCORRECT_ANSWER_IMAGES: [
154 | 'https://d2vbr0xakfjx9a.cloudfront.net/wrong1.png',
155 | 'https://d2vbr0xakfjx9a.cloudfront.net/wrong2.png',
156 | 'https://d2vbr0xakfjx9a.cloudfront.net/wrong3.png',
157 | ]
158 | });
159 |
160 | /**
161 | * ANIMATIONS - set up light animations that will be used throughout the game
162 | */
163 | const GAME_ANIMATIONS = Object.freeze({
164 | // Intro - Plays when a customer opens a Skill.
165 | 'INTRO_ANIMATION': animations.ComplexAnimations
166 | .SpectrumAnimation(10, ["red", "orange", "yellow"]),
167 |
168 | // ** Pre-Roll Call Animation - Buttons that are connected light up.
169 | 'PRE_ROLL_CALL_ANIMATION': animations.BasicAnimations
170 | .FadeInAnimation(1, "white", 40000),
171 |
172 | // ** Pre-Roll Call Animation - Buttons that are connected light up.
173 | 'ROLL_CALL_BUTTON_ADDED_ANIMATION': animations.BasicAnimations
174 | .SolidAnimation(1, "green", 40000),
175 |
176 | // ** Roll Call Complete Animation - displays on all buttons in play
177 | 'ROLL_CALL_COMPLETE_ANIMATION': animations.ComplexAnimations
178 | .SpectrumAnimation(6, ['red', 'orange', 'green', 'yellow', 'white']),
179 |
180 | // ** Roll Call Check-In Animation - buttons change state when added via roll call.
181 | 'ROLL_CALL_CHECKIN_ANIMATION': animations.BasicAnimations
182 | .SolidAnimation(1, "green", 3000),
183 |
184 | // Buzz In Animation - plays on answering players button
185 | 'BUZZ_IN_ANIMATION': animations.BasicAnimations
186 | .SolidAnimation(1, COLORS.BUZZ_IN_COLOR, 6000),
187 |
188 | // Buzz In Animation - played for non-answering players buttons
189 | 'BUZZ_IN_OTHER_PLAYERS_ANIMATION': animations.BasicAnimations
190 | .SolidAnimation(1, 'black', 200),
191 |
192 | // Listen For Answer Animation - played to all buttons after a question is asked
193 | 'LISTEN_FOR_ANSWER_ANIMATION': animations.BasicAnimations
194 | .SolidAnimation(1, COLORS.QUESTION_COLOR, 26000),
195 |
196 | // Wrong Answer Animation - Player gets something wrong.
197 | 'INCORRECT_ANSWER_ANIMATION': animations.ComplexAnimations
198 | .AnswerAnimation(COLORS.INCORRECT_COLOR, 'black', 1000),
199 |
200 | // Right Answer Animation - Player gets something right.
201 | 'CORRECT_ANSWER_ANIMATION': animations.ComplexAnimations
202 | .AnswerAnimation(COLORS.CORRECT_COLOR, 'black', 1000),
203 |
204 | // Exit Animation - plays when the exiting the skill
205 | 'EXIT_ANIMATION': animations.BasicAnimations
206 | .FadeOutAnimation(1, COLORS.EXIT_COLOR, 1500),
207 | });
208 |
209 | /*
210 | * Define the different states that this skill can be in. For the Trivia skill,
211 | * we define ROLLCALL, GAME_LOOP, ROLLCALL_EXIT, and the initial state called
212 | * START_GAME_STATE (which maps to the initial state).
213 | */
214 | const SKILL_STATES = {
215 | // Start mode performs roll call and button registration.
216 | // https://developer.amazon.com/docs/echo-button-skills/discover-echo-buttons.html
217 | START_GAME_STATE: '',
218 | ROLLCALL_STATE: '_ROLLCALL',
219 | BUTTON_GAME_STATE: '_BUTTON_GAME',
220 | BUTTONLESS_GAME_STATE: '_BUTTONLESS_GAME'
221 | };
222 |
223 | // return the externally exposed settings object
224 | return Object.freeze({
225 | APP_ID: APP_ID,
226 | STORAGE: STORAGE,
227 | ROLLCALL: ROLLCALL_STATES,
228 | AUDIO: AUDIO,
229 | IMAGES: IMAGES,
230 | GAME: GAME_OPTIONS,
231 | COLORS: COLORS,
232 | ANIMATIONS: GAME_ANIMATIONS,
233 | STATE: SKILL_STATES,
234 | LOG_LEVEL: 'DEBUG',
235 | pickRandom(arry) {
236 | if (Array.isArray(arry)) {
237 | return arry[Math.floor(Math.random() * Math.floor(arry.length))]
238 | }
239 | return arry;
240 | }
241 | });
242 | })();
--------------------------------------------------------------------------------
/lambda/custom/handlers/gamePlayHandlers.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 |
13 | const Game = require('../utils/game.js');
14 | const logger = require('../utils/logger.js');
15 | const settings = require('../config/settings.js');
16 |
17 | /**
18 | * Handling everything for the BUTTON_GAME_STATE state.
19 | */
20 | const gamePlayHandlers = {
21 | /**
22 | * The player has responded 'stop', 'cancel', 'no', requesting the game end.
23 | */
24 | EndGameHandler: {
25 | canHandle(handlerInput) {
26 | logger.debug('GAME.EndGameHandler: canHandle');
27 | let {
28 | attributesManager,
29 | requestEnvelope
30 | } = handlerInput;
31 | return requestEnvelope.request.type === 'IntentRequest' &&
32 | (requestEnvelope.request.intent.name === 'AMAZON.StopIntent' ||
33 | requestEnvelope.request.intent.name === 'AMAZON.CancelIntent' ||
34 | requestEnvelope.request.intent.name === 'AMAZON.NoIntent') &&
35 | (attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE ||
36 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTONLESS_GAME_STATE);
37 | },
38 | handle(handlerInput) {
39 | logger.debug('GAME.EndGameHandler: handle');
40 | Game.endGame(handlerInput, false);
41 | return handlerInput.responseBuilder.getResponse();
42 | }
43 | },
44 | /**
45 | * Events from the game engine
46 | */
47 | GameEventHandler: {
48 | canHandle(handlerInput) {
49 | logger.debug('GAME.GameEventHandler: canHandle');
50 | let {
51 | attributesManager,
52 | requestEnvelope
53 | } = handlerInput;
54 | return requestEnvelope.request.type === 'GameEngine.InputHandlerEvent' &&
55 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE;
56 | },
57 | handle(handlerInput) {
58 | logger.debug('GAME.GameEventHandler: handle');
59 | Game.handleGameInputEvent(handlerInput);
60 | return handlerInput.responseBuilder.getResponse();
61 | }
62 | },
63 | /**
64 | * The player has asked to play a game while in the middle of a game, continue on
65 | */
66 | PlayGameHandler: {
67 | canHandle(handlerInput) {
68 | logger.debug('GAME.PlayGameHandler: canHandle');
69 | let {
70 | attributesManager,
71 | requestEnvelope
72 | } = handlerInput;
73 | return requestEnvelope.request.type === 'IntentRequest' &&
74 | requestEnvelope.request.intent.name === 'PlayGame' &&
75 | (attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE ||
76 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTONLESS_GAME_STATE);
77 | },
78 | handle(handlerInput) {
79 | logger.debug('GAME.PlayGameHandler: handle');
80 | let { attributesManager, responseBuilder } = handlerInput;
81 | let ctx = attributesManager.getRequestAttributes();
82 | let sessionAttributes = attributesManager.getSessionAttributes();
83 |
84 | let isFirstQuestion = parseInt(sessionAttributes.currentQuestion || 0, 10) <= 1;
85 | let messageKey = isFirstQuestion ? 'PLAY_GAME_FIRST_QUESTION' : 'PLAY_GAME_MID_GAME';
86 | let responseMessage = ctx.t(messageKey, {current_question: sessionAttributes.currentQuestion})
87 |
88 | ctx.outputSpeech.push(responseMessage.outputSpeech + "");
89 |
90 | Game.askQuestion(handlerInput, false);
91 | return responseBuilder.getResponse();
92 | }
93 | },
94 | /**
95 | * Player has responded 'yes' to being ready to start the game
96 | */
97 | YesHandler: {
98 | canHandle(handlerInput) {
99 | logger.debug('GAME.YesHandler: canHandle');
100 | let {
101 | attributesManager,
102 | requestEnvelope
103 | } = handlerInput;
104 | return requestEnvelope.request.type === 'IntentRequest' &&
105 | requestEnvelope.request.intent.name === 'AMAZON.YesIntent' &&
106 | (attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE ||
107 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTONLESS_GAME_STATE);
108 | },
109 | handle(handlerInput) {
110 | logger.debug('GAME.YesHandler: handle');
111 |
112 | Game.askQuestion(handlerInput, false);
113 | return handlerInput.responseBuilder.getResponse();
114 | }
115 | },
116 | /**
117 | * The player is answering a question.
118 | */
119 | AnswerHandler: {
120 | canHandle(handlerInput) {
121 | logger.debug('GAME.AnswerHandler: canHandle');
122 | let {
123 | attributesManager,
124 | requestEnvelope
125 | } = handlerInput;
126 | return requestEnvelope.request.type === 'IntentRequest' &&
127 | (requestEnvelope.request.intent.name === 'AnswerQuestionIntent' ||
128 | requestEnvelope.request.intent.name === 'AnswerOnlyIntent') &&
129 | (attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE ||
130 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTONLESS_GAME_STATE);
131 | },
132 | handle(handlerInput) {
133 | logger.debug('GAME.AnswerHandler: handle');
134 | Game.answerQuestion(handlerInput);
135 | return handlerInput.responseBuilder.getResponse();
136 | }
137 | },
138 | /**
139 | * The player has responded 'don't know', 'next', or similar.
140 | */
141 | DontKnowNextHandler: {
142 | canHandle(handlerInput) {
143 | logger.debug('GAME.DontKnowNextHandler: canHandle');
144 | let {
145 | attributesManager,
146 | requestEnvelope
147 | } = handlerInput;
148 | return requestEnvelope.request.type === 'IntentRequest' &&
149 | (requestEnvelope.request.intent.name === 'DontKnowIntent' ||
150 | requestEnvelope.request.intent.name === 'AMAZON.NextIntent') &&
151 | (attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTON_GAME_STATE ||
152 | attributesManager.getSessionAttributes().STATE === settings.STATE.BUTTONLESS_GAME_STATE);
153 | },
154 | handle(handlerInput) {
155 | logger.debug('GAME.DontKnowNextHandler: handle');
156 | let { attributesManager, responseBuilder } = handlerInput;
157 | let sessionAttributes = attributesManager.getSessionAttributes();
158 | let ctx = attributesManager.getRequestAttributes();
159 |
160 | sessionAttributes.currentQuestion = parseInt(sessionAttributes.currentQuestion || 0, 10) + 1;
161 |
162 | let isLastQuestion = parseInt(sessionAttributes.currentQuestion || 1, 10) > settings.GAME.QUESTIONS_PER_GAME;
163 | let messageKey = isLastQuestion ? 'PLAY_GAME_SKIP_LAST_QUESTION' : 'PLAY_GAME_SKIP_QUESTION';
164 | let responseMessage = ctx.t(messageKey, sessionAttributes.currentQuestion);
165 | ctx.outputSpeech.push(responseMessage.outputSpeech + "");
166 |
167 | Game.askQuestion(handlerInput, false);
168 | return responseBuilder.getResponse();
169 | }
170 | }
171 | }
172 |
173 | module.exports = gamePlayHandlers;
--------------------------------------------------------------------------------
/lambda/custom/handlers/globalHandlers.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | 'use strict';
13 |
14 | const display = require('../utils/display.js');
15 | const i18n = require('i18next');
16 | const logger = require('../utils/logger.js');
17 | const messages = require('../config/messages.js');
18 | const settings = require('../config/settings.js');
19 | const Game = require('../utils/game.js');
20 | const startHandlers = require('./startHandlers.js');
21 | const gameHandlers = require('./gamePlayHandlers.js');
22 |
23 | const globalHandlers = {
24 | RequestInterceptor: {
25 | async process(handlerInput) {
26 | logger.debug('Global.RequestInterceptor: pre-processing response');
27 | let {
28 | attributesManager,
29 | requestEnvelope
30 | } = handlerInput;
31 | let ctx = attributesManager.getRequestAttributes();
32 | let persistentAtttributes = await attributesManager.getPersistentAttributes();
33 | let sessionAttributes = attributesManager.getSessionAttributes();
34 |
35 | /**
36 | * Ensure a state in case we're starting fresh
37 | */
38 | if (sessionAttributes.STATE == null) {
39 | logger.debug('SETTING STATE TO START_GAME_STATE');
40 | sessionAttributes.STATE = settings.STATE.START_GAME_STATE;
41 | } else if (sessionAttributes.STATE === '_GAME_LOOP'){
42 | logger.debug('Changing state from _GAME_LOOP for backward compatability.')
43 | sessionAttributes.STATE = settings.STATE.BUTTON_GAME_STATE;
44 | }
45 |
46 | // Apply the persistent attributes to the current session
47 | attributesManager.setSessionAttributes(Object.assign({}, persistentAtttributes, sessionAttributes));
48 |
49 | /**
50 | * Log the request for debug purposes.
51 | */
52 | logger.debug('----- REQUEST -----');
53 | logger.debug(JSON.stringify(requestEnvelope, null, 2));
54 |
55 | /**
56 | * Ensure we're starting at a clean state.
57 | */
58 | ctx.directives = [];
59 | ctx.outputSpeech = [];
60 | ctx.reprompt = [];
61 |
62 | /**
63 | * For ease of use we'll attach the utilities for rendering display
64 | * and handling localized tts to the request attributes.
65 | */
66 | logger.debug('Initializing messages for ' + handlerInput.requestEnvelope.request.locale);
67 | const localizationClient = i18n.init({
68 | lng: handlerInput.requestEnvelope.request.locale,
69 | resources: messages,
70 | returnObjects: true,
71 | fallbackLng: 'en'
72 | });
73 | ctx.t = function (...args) {
74 | return localizationClient.t(...args);
75 | };
76 | ctx.render = function (...args) {
77 | return display.render(...args);
78 | }
79 | logger.debug('Global.RequestInterceptor: pre-processing response complete');
80 | }
81 | },
82 | ResponseInterceptor: {
83 | async process(handlerInput) {
84 | logger.debug('Global.ResponseInterceptor: post-processing response');
85 | let {
86 | attributesManager,
87 | responseBuilder
88 | } = handlerInput;
89 | let ctx = attributesManager.getRequestAttributes();
90 | let sessionAttributes = attributesManager.getSessionAttributes();
91 | let persistentAtttributes = await attributesManager.getPersistentAttributes();
92 |
93 | /**
94 | * Log the attributes and response for debug purposes.
95 | */
96 | logger.debug('----- REQUEST ATTRIBUTES -----');
97 | logger.debug(JSON.stringify(ctx, null, 2));
98 |
99 | logger.debug('----- SESSION ATTRIBUTES -----');
100 | logger.debug(JSON.stringify(sessionAttributes, null, 2));
101 |
102 | logger.debug('----- CURRENT PERSISTENT ATTRIBUTES -----');
103 | logger.debug(JSON.stringify(persistentAtttributes, null, 2));
104 |
105 | /**
106 | * Build the speech response.
107 | */
108 | if (ctx.outputSpeech.length > 0) {
109 | let outputSpeech = ctx.outputSpeech.join(' ');
110 | logger.debug('Global.ResponseInterceptor: adding ' +
111 | ctx.outputSpeech.length + ' speech parts');
112 | responseBuilder.speak(outputSpeech);
113 | }
114 | if (ctx.reprompt.length > 0) {
115 | logger.debug('Global.ResponseInterceptor: adding ' +
116 | ctx.outputSpeech.length + ' speech reprompt parts');
117 | let reprompt = ctx.reprompt.join(' ');
118 | responseBuilder.reprompt(reprompt);
119 | }
120 |
121 | /**
122 | * Add the display response
123 | */
124 | if (ctx.renderTemplate) {
125 | responseBuilder.addRenderTemplateDirective(ctx.renderTemplate);
126 | }
127 |
128 | let response = responseBuilder.getResponse();
129 |
130 | /**
131 | * Apply the custom directives to the response.
132 | */
133 | if (Array.isArray(ctx.directives)) {
134 | logger.debug('Global.ResponseInterceptor: processing ' + ctx.directives.length + ' custom directives ');
135 | response.directives = response.directives || [];
136 | for (let i = 0; i < ctx.directives.length; i++) {
137 | response.directives.push(ctx.directives[i]);
138 | }
139 | }
140 |
141 | if ('openMicrophone' in ctx) {
142 | if (ctx.openMicrophone) {
143 | /**
144 | * setting shouldEndSession = false - lets Alexa know that we want an answer from the user
145 | * see: https://developer.amazon.com/docs/echo-button-skills/receive-voice-input.html#open
146 | * https://developer.amazon.com/docs/echo-button-skills/keep-session-open.html
147 | */
148 | response.shouldEndSession = false;
149 | logger.debug('Global.ResponseInterceptor: request to open microphone -> shouldEndSession = false');
150 | } else {
151 | if (ctx.endSession){
152 | // We have explicitely asked for the session to end
153 | response.shouldEndSession = true;
154 | } else {
155 | /**
156 | * deleting shouldEndSession will keep the skill session going,
157 | * while the input handler is active, waiting for button presses
158 | * see: https://developer.amazon.com/docs/echo-button-skills/keep-session-open.html
159 | */
160 | delete response.shouldEndSession;
161 | }
162 |
163 | logger.debug('Global.ResponseInterceptor: request to open microphone -> delete shouldEndSession');
164 | }
165 | }
166 |
167 | /**
168 | * Persist the current session attributes
169 | */
170 | attributesManager.setPersistentAttributes(sessionAttributes);
171 | await attributesManager.savePersistentAttributes();
172 | logger.debug('----- NEW PERSISTENT ATTRIBUTES -----');
173 | logger.debug(JSON.stringify(attributesManager.getPersistentAttributes(), null, 2));
174 |
175 | /**
176 | * Log the attributes and response for debug purposes.
177 | */
178 | logger.debug('----- RESPONSE -----');
179 | logger.debug(JSON.stringify(response, null, 2));
180 |
181 | return response;
182 | }
183 | },
184 | NumericResponseHandler: {
185 | /**
186 | * Handle numeric responses in one place and then route to the appropriate logic based on state.
187 | */
188 | canHandle(handlerInput) {
189 | logger.debug('Global.NumericResponseIntentHandler: canHandle');
190 | return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
191 | handlerInput.requestEnvelope.request.intent.name === 'NumericResponseIntent'
192 | },
193 | handle(handlerInput) {
194 | logger.debug('Global.NumericResponseIntentHandler: handle');
195 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
196 |
197 | if (sessionAttributes.STATE === settings.STATE.START_GAME_STATE) {
198 | return startHandlers.PlayerCountHandler.handle(handlerInput);
199 | } else if (sessionAttributes.STATE === settings.STATE.BUTTON_GAME_STATE ||
200 | sessionAttributes.STATE === settings.STATE.BUTTONLESS_GAME_STATE) {
201 | return gameHandlers.AnswerHandler.handle(handlerInput);
202 | } else {
203 | return globalHandlers.DefaultHandler.handle(handlerInput);
204 | }
205 | }
206 | },
207 | DefaultHandler: {
208 | canHandle(handlerInput) {
209 | logger.debug('Global.DefaultHandler: canHandle');
210 |
211 | /**
212 | * Catch all for requests.
213 | */
214 | return true;
215 | },
216 | handle(handlerInput) {
217 | logger.debug('Global.DefaultHandler: handle');
218 | let {
219 | attributesManager,
220 | responseBuilder
221 | } = handlerInput;
222 | let ctx = attributesManager.getRequestAttributes();
223 | let {
224 | outputSpeech,
225 | reprompt
226 | } = ctx.t('UNHANDLED_REQUEST');
227 |
228 | ctx.outputSpeech.push(outputSpeech);
229 | ctx.reprompt.push(reprompt);
230 | ctx.openMicrophone = true;
231 |
232 | return responseBuilder.getResponse();
233 | }
234 | },
235 | HelpHandler: {
236 | canHandle(handlerInput) {
237 | logger.debug('Global.HelpHandler: canHandle');
238 | /**
239 | * Handle all help requests and treat don't know requests as
240 | * help requests except when in game loop state
241 | */
242 | let {
243 | attributesManager,
244 | requestEnvelope
245 | } = handlerInput;
246 | let request = requestEnvelope.request;
247 | let sessionAttributes = attributesManager.getSessionAttributes();
248 | return request.type === 'IntentRequest' &&
249 | (request.intent.name === 'AMAZON.HelpIntent' ||
250 | (request.intent.name === 'DontKnowIntent' &&
251 | sessionAttributes.STATE !== settings.STATE.BUTTON_GAME_STATE &&
252 | sessionAttributes.STATE !== settings.STATE.BUTTONLESS_GAME_STATE))
253 | },
254 | handle(handlerInput) {
255 | logger.debug('Global.HelpHandler: handle');
256 | let {
257 | attributesManager,
258 | responseBuilder
259 | } = handlerInput;
260 | let ctx = attributesManager.getRequestAttributes();
261 | let sessionAttributes = attributesManager.getSessionAttributes();
262 |
263 | /**
264 | * Figure out where we need help
265 | */
266 | let messageKey;
267 | switch (sessionAttributes.STATE) {
268 | case settings.STATE.ROLLCALL_STATE:
269 | messageKey = 'ROLL_CALL_HELP';
270 | break;
271 | case settings.STATE.BUTTON_GAME_STATE:
272 | messageKey = 'GAME_PLAY_HELP';
273 | // Clean up the in game state if they interrupted for help
274 | Game.stopCurrentInputHandler(handlerInput);
275 | delete sessionAttributes.answeringButton;
276 | delete sessionAttributes.answeringPlayer;
277 | delete sessionAttributes.correct;
278 | break;
279 | default:
280 | messageKey = 'GENERAL_HELP'
281 | }
282 |
283 | let responseMessage = ctx.t(messageKey);
284 | ctx.render(handlerInput, responseMessage);
285 | ctx.outputSpeech.push(responseMessage.outputSpeech);
286 | ctx.reprompt.push(responseMessage.reprompt);
287 | ctx.openMicrophone = true;
288 |
289 | return responseBuilder.getResponse();
290 | }
291 | },
292 | /**
293 | * Stop and Cancel both respond by saying goodbye and ending the session by not setting openMicrophone
294 | */
295 | StopCancelHandler: {
296 | canHandle(handlerInput) {
297 | logger.debug('Global.StopCancelHandler: canHandle');
298 | return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
299 | (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent' ||
300 | handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent')
301 | },
302 | handle(handlerInput) {
303 | logger.debug('Global.StopCancelHandler: handle');
304 | let {
305 | attributesManager,
306 | responseBuilder
307 | } = handlerInput;
308 | let ctx = attributesManager.getRequestAttributes();
309 | let {
310 | outputSpeech
311 | } = ctx.t('GOOD_BYE');
312 |
313 | ctx.outputSpeech.push(outputSpeech);
314 | ctx.openMicrophone = false;
315 | ctx.endSession = true;
316 |
317 | return responseBuilder.getResponse();
318 | }
319 | },
320 | SessionEndedRequestHandler: {
321 | canHandle(handlerInput) {
322 | logger.debug('Global.SessionEndedRequestHandler: canHandle');
323 | return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
324 | },
325 | handle(handlerInput) {
326 | logger.debug('Global.SessionEndedRequestHandler: handle');
327 | logger.info(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
328 | let {
329 | attributesManager,
330 | responseBuilder
331 | } = handlerInput;
332 | let sessionAttributes = attributesManager.getSessionAttributes();
333 |
334 | /**
335 | * Clean out the session attributes that won't be persisted
336 | */
337 | delete sessionAttributes.STATE;
338 |
339 | /**
340 | * setting shouldEndSession = true - lets Alexa know that the skill is done
341 | * see: https://developer.amazon.com/docs/echo-button-skills/receive-voice-input.html
342 | */
343 | let response = responseBuilder.getResponse();
344 | response.shouldEndSession = true;
345 | return response;
346 | }
347 | },
348 | ErrorHandler: {
349 | canHandle() {
350 | // Handle all errors. We'll just log them.
351 | logger.debug('Global.ErrorHandler: canHandle');
352 | return true;
353 | },
354 | handle(handlerInput, error) {
355 | logger.debug('Global.ErrorHandler: handle');
356 | logger.error('Global.ErrorHandler: Error = ' + error.message);
357 | logger.error(JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
358 |
359 | return handlerInput.responseBuilder
360 | .speak('An error was encountered while handling your request. Try again later')
361 | .getResponse();
362 | }
363 | }
364 | }
365 |
366 | module.exports = globalHandlers;
--------------------------------------------------------------------------------
/lambda/custom/handlers/rollCallHandlers.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 |
13 | const RollCall = require('../utils/rollcall.js');
14 | const logger = require('../utils/logger.js');
15 | const settings = require('../config/settings.js');
16 |
17 | /**
18 | * Handling everything for the ROLLCALL_STATE state.
19 | */
20 | const rollCallHandlers = {
21 | /**
22 | * Events from the game engine
23 | */
24 | GameEventHandler: {
25 | canHandle(handlerInput) {
26 | logger.debug('ROLLCALL.GameEventHandler: canHandle');
27 | let {
28 | attributesManager,
29 | requestEnvelope
30 | } = handlerInput;
31 | return requestEnvelope.request.type === 'GameEngine.InputHandlerEvent' &&
32 | attributesManager.getSessionAttributes().STATE === settings.STATE.ROLLCALL_STATE;
33 | },
34 | handle(handlerInput) {
35 | logger.debug('ROLLCALL.GameEventHandler: handle');
36 | let {
37 | attributesManager,
38 | requestEnvelope,
39 | responseBuilder
40 | } = handlerInput;
41 | const sessionAttributes = attributesManager.getSessionAttributes();
42 | const ctx = attributesManager.getRequestAttributes();
43 |
44 | // Ensure the events are current
45 | if (requestEnvelope.request.originatingRequestId !== sessionAttributes.inputHandlerId) {
46 | logger.debug("Global.GameEngineInputHandler: stale input event received -> " +
47 | "received event from " + request.originatingRequestId +
48 | " (was expecting " + sessionAttributes.inputHandlerId + ")");
49 | ctx.openMicrophone = false;
50 | return handlerInput.responseBuilder.getResponse();
51 | }
52 |
53 | RollCall.handleEvents(handlerInput, requestEnvelope.request.events);
54 | return responseBuilder.getResponse();
55 | }
56 | },
57 | /**
58 | * The player has responded 'no' to the option of continuing roll call.
59 | */
60 | NoHandler: {
61 | canHandle(handlerInput) {
62 | logger.debug('ROLLCALL.NoHandler: canHandle');
63 | let {
64 | attributesManager,
65 | requestEnvelope
66 | } = handlerInput;
67 | return requestEnvelope.request.type === 'IntentRequest' &&
68 | requestEnvelope.request.intent.name === 'AMAZON.NoIntent' &&
69 | attributesManager.getSessionAttributes().STATE === settings.STATE.ROLLCALL_STATE;
70 | },
71 | handle(handlerInput) {
72 | logger.debug('ROLLCALL.NoHandler: handle');
73 | let {
74 | attributesManager,
75 | responseBuilder
76 | } = handlerInput;
77 | let ctx = attributesManager.getRequestAttributes();
78 |
79 | let responseMessage = ctx.t('GOOD_BYE');
80 | ctx.outputSpeech.push(responseMessage.outputSpeech);
81 | ctx.openMicrophone = false;
82 | ctx.endSession = true;
83 |
84 | return responseBuilder.getResponse();
85 | }
86 | },
87 | /**
88 | * The player has responded 'yes' to the option of continuing roll call.
89 | */
90 | YesHandler: {
91 | canHandle(handlerInput) {
92 | logger.debug('ROLLCALL.YesHandler: canHandle');
93 | let {
94 | attributesManager,
95 | requestEnvelope
96 | } = handlerInput;
97 | return requestEnvelope.request.type === 'IntentRequest' &&
98 | requestEnvelope.request.intent.name === 'AMAZON.YesIntent' &&
99 | attributesManager.getSessionAttributes().STATE === settings.STATE.ROLLCALL_STATE;
100 | },
101 | handle(handlerInput) {
102 | logger.debug('ROLLCALL.YesHandler: handle');
103 | let {
104 | attributesManager,
105 | responseBuilder
106 | } = handlerInput;
107 | let sessionAttributes = attributesManager.getSessionAttributes();
108 |
109 | RollCall.start(handlerInput, false, sessionAttributes.playerCount);
110 | return responseBuilder.getResponse();
111 | }
112 | }
113 | }
114 |
115 | module.exports = rollCallHandlers;
--------------------------------------------------------------------------------
/lambda/custom/handlers/startHandlers.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 |
13 | const RollCall = require('../utils/rollcall.js');
14 | const logger = require('../utils/logger.js');
15 | const settings = require('../config/settings.js');
16 | const directives = require('../utils/directives.js');
17 |
18 | const startHandlers = {
19 | /**
20 | * Invoked when a user says 'open' or 'play' or some other variant
21 | */
22 | LaunchPlayGameHandler: {
23 | canHandle(handlerInput) {
24 | logger.debug('START.LaunchPlayGameHandler: canHandle');
25 | let {
26 | requestEnvelope
27 | } = handlerInput;
28 | return (requestEnvelope.request.type === 'LaunchRequest' ||
29 | requestEnvelope.request.type === 'NewSession' ||
30 | (requestEnvelope.request.type === 'IntentRequest' &&
31 | requestEnvelope.request.intent.name === 'PlayGame'));
32 | },
33 | handle(handlerInput) {
34 | logger.debug('START.LaunchPlayGameHandler: handle');
35 | let {
36 | attributesManager,
37 | responseBuilder
38 | } = handlerInput;
39 | let ctx = attributesManager.getRequestAttributes();
40 | let sessionAttributes = attributesManager.getSessionAttributes();
41 | sessionAttributes.STATE = settings.STATE.START_GAME_STATE;
42 |
43 | /**
44 | * Check to see if we have an active game
45 | */
46 | let validPlayerCount = sessionAttributes.playerCount &&
47 | sessionAttributes.playerCount <= settings.GAME.MAX_PLAYERS && sessionAttributes.playerCount > 0;
48 | let gameInProgress = (sessionAttributes.currentQuestion || 0) <= settings.GAME.QUESTIONS_PER_GAME;
49 |
50 | /**
51 | * If we have an active game ask to resume, otherwise start a new game and ask how many players.
52 | */
53 | let responseMessage;
54 | if (validPlayerCount && gameInProgress) {
55 | responseMessage = ctx.t('ASK_TO_RESUME', {'player_count': sessionAttributes.playerCount});
56 | } else {
57 | responseMessage = ctx.t('START_GAME');
58 |
59 | // it's a new game so delete all attributes
60 | let attributeNames = Object.keys(sessionAttributes);
61 | for (let k = 0; k < attributeNames.length; k++) {
62 | delete sessionAttributes[attributeNames[k]];
63 | }
64 | }
65 | ctx.outputSpeech.push(responseMessage.outputSpeech);
66 | ctx.reprompt.push(responseMessage.reprompt);
67 | ctx.render(handlerInput, responseMessage);
68 | ctx.openMicrophone = true;
69 |
70 | // Send an intro animation to all connected buttons
71 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
72 | 'animations': settings.ANIMATIONS.INTRO_ANIMATION
73 | }));
74 |
75 | return responseBuilder.getResponse();
76 | }
77 | },
78 | StartNewGameHandler: {
79 | canHandle(handlerInput) {
80 | logger.debug('START.StartNewGameHandler: canHandle');
81 | let request = handlerInput.requestEnvelope.request;
82 | return request.type === 'IntentRequest' &&
83 | request.intent.name === 'AMAZON.StartOverIntent';
84 | },
85 | handle(handlerInput) {
86 | logger.debug('START.StartNewGameHandler: handle');
87 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
88 | // Deleting playerCount to force launching of a new game
89 | delete sessionAttributes.playerCount;
90 | return startHandlers.LaunchPlayGameHandler.handle(handlerInput);
91 | }
92 | },
93 | PlayerCountHandler: {
94 | /**
95 | * Occasionally a mispronounced number might fall into an answer question intent.
96 | * We'll be open to that possibility here and do our best to handle.
97 | */
98 | canHandle(handlerInput) {
99 | logger.debug('START.PlayerCountHandler: canHandle');
100 | let {
101 | attributesManager,
102 | requestEnvelope
103 | } = handlerInput;
104 | return requestEnvelope.request.type === 'IntentRequest' &&
105 | (requestEnvelope.request.intent.name === 'PlayerCount' ||
106 | requestEnvelope.request.intent.name === 'AnswerQuestionIntent' ||
107 | requestEnvelope.request.intent.name === 'AnswerOnlyIntent') &&
108 | attributesManager.getSessionAttributes().STATE === settings.STATE.START_GAME_STATE;
109 | },
110 | handle(handlerInput) {
111 | logger.debug('START.PlayerCountHandler: handle');
112 | let {
113 | requestEnvelope,
114 | attributesManager,
115 | responseBuilder
116 | } = handlerInput;
117 | let sessionAttributes = attributesManager.getSessionAttributes();
118 | let ctx = attributesManager.getRequestAttributes();
119 |
120 | // Pull the value, could be from answer or digit
121 | let playerCountResponse = requestEnvelope.request.intent.slots.answers ?
122 | requestEnvelope.request.intent.slots.answers.value :
123 | (requestEnvelope.request.intent.slots.digit ?
124 | requestEnvelope.request.intent.slots.digit.value : '');
125 |
126 | logger.info('PlayerCount response is \'' + playerCountResponse + '\'');
127 |
128 | sessionAttributes.playerCount = !isNaN(playerCountResponse) ?
129 | parseInt(playerCountResponse, 10) : 0;
130 |
131 | let validPlayerCount = sessionAttributes.playerCount &&
132 | (sessionAttributes.playerCount <= settings.GAME.MAX_PLAYERS) && (sessionAttributes.playerCount > 0);
133 |
134 | if (validPlayerCount){
135 | if (sessionAttributes.playerCount === 1){
136 | // Play a buttonless game
137 | sessionAttributes.STATE = settings.STATE.BUTTONLESS_GAME_STATE;
138 |
139 | let responseMessage = ctx.t('SINGLE_PLAYER_GAME_READY');
140 | ctx.render(handlerInput, responseMessage);
141 | ctx.outputSpeech.push(settings.AUDIO.ROLL_CALL_COMPLETE);
142 | ctx.outputSpeech.push(responseMessage.outputSpeech);
143 | ctx.reprompt.push(responseMessage.reprompt);
144 | ctx.openMicrophone = true;
145 | } else {
146 | RollCall.start(handlerInput, false, sessionAttributes.playerCount);
147 | }
148 | } else {
149 | let responseMessage = ctx.t('PLAYERCOUNT_INVALID');
150 | ctx.outputSpeech.push(responseMessage.outputSpeech);
151 | ctx.reprompt.push(responseMessage.reprompt);
152 | ctx.openMicrophone = true;
153 | }
154 |
155 | return responseBuilder.getResponse();
156 | }
157 | },
158 | /**
159 | * The player has responded 'no' to the option of resuming the previous game.
160 | */
161 | NoHandler: {
162 | canHandle(handlerInput) {
163 | logger.debug('START.NoHandler: canHandle');
164 | let {
165 | attributesManager,
166 | requestEnvelope
167 | } = handlerInput;
168 | return requestEnvelope.request.type === 'IntentRequest' &&
169 | requestEnvelope.request.intent.name === 'AMAZON.NoIntent' &&
170 | (attributesManager.getSessionAttributes().STATE === settings.STATE.START_GAME_STATE);
171 | },
172 | handle(handlerInput) {
173 | logger.debug('START.NoHandler: handle');
174 | let {
175 | attributesManager,
176 | responseBuilder
177 | } = handlerInput;
178 | let sessionAttributes = attributesManager.getSessionAttributes();
179 | let ctx = attributesManager.getRequestAttributes();
180 |
181 | let responseMessage = ctx.t('DONT_RESUME_GAME');
182 | ctx.outputSpeech.push(responseMessage.outputSpeech);
183 | ctx.reprompt.push(responseMessage.reprompt);
184 | ctx.render(handlerInput, responseMessage);
185 | ctx.openMicrophone = true;
186 |
187 | // Send an intro animation to all connected buttons
188 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
189 | 'animations': settings.ANIMATIONS.INTRO_ANIMATION
190 | }));
191 |
192 | // it's a new game so delete all attributes
193 | let attributeNames = Object.keys(sessionAttributes);
194 | for (let k = 0; k < attributeNames.length; k++) {
195 | delete sessionAttributes[attributeNames[k]];
196 | }
197 |
198 | return responseBuilder.getResponse();
199 | }
200 | },
201 | /**
202 | * The player has responded 'yes' to the option of resuming the previous game.
203 | */
204 | YesHandler: {
205 | canHandle(handlerInput) {
206 | logger.debug('START.NoHandler: canHandle');
207 | let {
208 | attributesManager,
209 | requestEnvelope
210 | } = handlerInput;
211 | return requestEnvelope.request.type === 'IntentRequest' &&
212 | requestEnvelope.request.intent.name === 'AMAZON.YesIntent' &&
213 | attributesManager.getSessionAttributes().STATE === settings.STATE.START_GAME_STATE;
214 | },
215 | handle(handlerInput) {
216 | logger.debug('START.NoHandler: handle');
217 | let {
218 | attributesManager,
219 | responseBuilder
220 | } = handlerInput;
221 | let sessionAttributes = attributesManager.getSessionAttributes();
222 | let ctx = attributesManager.getRequestAttributes();
223 |
224 | let validPlayerCount = sessionAttributes.playerCount &&
225 | sessionAttributes.playerCount <= settings.GAME.MAX_PLAYERS && sessionAttributes.playerCount > 0;
226 |
227 | if (validPlayerCount) {
228 | logger.debug('Resume roll call. We know the number of players: ' + sessionAttributes.playerCount);
229 |
230 | if (sessionAttributes.playerCount === 1){
231 | // Play a buttonless game
232 | sessionAttributes.STATE = settings.STATE.BUTTONLESS_GAME_STATE;
233 |
234 | let responseMessage = ctx.t('SINGLE_PLAYER_GAME_READY');
235 | ctx.render(handlerInput, responseMessage);
236 | ctx.outputSpeech.push(settings.AUDIO.ROLL_CALL_COMPLETE);
237 | ctx.outputSpeech.push(responseMessage.outputSpeech);
238 | ctx.reprompt.push(responseMessage.reprompt);
239 | ctx.openMicrophone = true;
240 | } else {
241 | let resumingGame = (sessionAttributes.buttons && sessionAttributes.buttons.length === sessionAttributes.playerCount);
242 | RollCall.start(handlerInput, resumingGame, sessionAttributes.playerCount);
243 | }
244 | } else {
245 | logger.debug('Resuming roll call, but starting from scratch!');
246 |
247 | // Send an intro animation to all connected buttons
248 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
249 | 'animations': settings.ANIMATIONS.INTRO_ANIMATION
250 | }));
251 |
252 | let responseMessage = ctx.t('RESUME_GAME');
253 | ctx.outputSpeech.push(responseMessage.outputSpeech);
254 | ctx.reprompt.push(responseMessage.reprompt);
255 | ctx.render(handlerInput, responseMessage);
256 | ctx.openMicrophone = true;
257 | }
258 | return responseBuilder.getResponse();
259 | }
260 | }
261 | }
262 |
263 | module.exports = startHandlers;
--------------------------------------------------------------------------------
/lambda/custom/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | 'use strict';
13 |
14 | const Alexa = require('ask-sdk');
15 | const settings = require('./config/settings.js');
16 |
17 | /**
18 | * Import interceptors and handlers for the different skill states.
19 | */
20 | const GlobalHandlers = require('./handlers/globalHandlers');
21 | const StartHandlers = require('./handlers/startHandlers.js');
22 | const RollCallHandlers = require('./handlers/rollCallHandlers.js');
23 | const GamePlayHandlers = require('./handlers/gamePlayHandlers.js');
24 |
25 | /**
26 | * Lambda setup.
27 | */
28 | exports.handler = function (event, context) {
29 | let factory = Alexa.SkillBuilders.standard()
30 | .addRequestHandlers(
31 | GamePlayHandlers.AnswerHandler,
32 | GamePlayHandlers.DontKnowNextHandler,
33 | GamePlayHandlers.GameEventHandler,
34 | GamePlayHandlers.PlayGameHandler,
35 | GamePlayHandlers.EndGameHandler,
36 | GamePlayHandlers.YesHandler,
37 | RollCallHandlers.YesHandler,
38 | RollCallHandlers.NoHandler,
39 | RollCallHandlers.GameEventHandler,
40 | StartHandlers.PlayerCountHandler,
41 | StartHandlers.YesHandler,
42 | StartHandlers.NoHandler,
43 | StartHandlers.LaunchPlayGameHandler,
44 | StartHandlers.StartNewGameHandler,
45 | GlobalHandlers.NumericResponseHandler,
46 | GlobalHandlers.HelpHandler,
47 | GlobalHandlers.StopCancelHandler,
48 | GlobalHandlers.SessionEndedRequestHandler,
49 | GlobalHandlers.DefaultHandler
50 | )
51 | .addRequestInterceptors(GlobalHandlers.RequestInterceptor)
52 | .addResponseInterceptors(GlobalHandlers.ResponseInterceptor)
53 | .addErrorHandlers(GlobalHandlers.ErrorHandler);
54 |
55 | if (settings.APP_ID) {
56 | factory.withSkillId(settings.APP_ID);
57 | }
58 |
59 | console.log("===ENV VAR DYNAMODBTABLE===: " + process.env.DYNAMODB_TABLE_NAME);
60 | if (process.env.DYNAMODB_TABLE_NAME && process.env.DYNAMODB_TABLE_NAME !== '') {
61 | settings.STORAGE.SESSION_TABLE = process.env.DYNAMODB_TABLE_NAME;
62 | console.log("===STORAGE SESSION TABLE Set to===: " + settings.STORAGE.SESSION_TABLE);
63 | }
64 |
65 | if (settings.STORAGE.SESSION_TABLE) {
66 | factory.withTableName(settings.STORAGE.SESSION_TABLE)
67 | .withAutoCreateTable(true);
68 | }
69 |
70 | let skill = factory.create();
71 |
72 | return skill.invoke(event, context);
73 | }
--------------------------------------------------------------------------------
/lambda/custom/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "skill-sample-nodejs-buttons-game-templates",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ask-sdk": {
8 | "version": "2.3.0",
9 | "resolved": "https://registry.npmjs.org/ask-sdk/-/ask-sdk-2.3.0.tgz",
10 | "integrity": "sha512-yJqGeYSRscp4II9v5j/FK5D5t/ywXwvnBKDepstZZ0tlzByYHBxMH55LpL/F5mcfnYmnqzrIS4nrGiX87Xnpfw==",
11 | "requires": {
12 | "ask-sdk-core": "2.3.0",
13 | "ask-sdk-dynamodb-persistence-adapter": "2.3.0",
14 | "ask-sdk-model": "1.9.0"
15 | }
16 | },
17 | "ask-sdk-core": {
18 | "version": "2.3.0",
19 | "resolved": "https://registry.npmjs.org/ask-sdk-core/-/ask-sdk-core-2.3.0.tgz",
20 | "integrity": "sha512-aSc/xaY0lHZu8pmkYPR1ms72WPH73aggo7uL/k0dut6AhP3CmdU3Y9ui7IOAvRJnha0cToQ8TYU0jLEOH6MMNw==",
21 | "requires": {
22 | "ask-sdk-runtime": "2.2.0"
23 | }
24 | },
25 | "ask-sdk-dynamodb-persistence-adapter": {
26 | "version": "2.3.0",
27 | "resolved": "https://registry.npmjs.org/ask-sdk-dynamodb-persistence-adapter/-/ask-sdk-dynamodb-persistence-adapter-2.3.0.tgz",
28 | "integrity": "sha512-NeXeXH2YsgsthaijQPej/Peago7oNPxXChPtC6whdPFMvBKQNGWJWI8hPN8o8DWQg4kXSDDPJsjLaUSpBjFFmg==",
29 | "requires": {
30 | "aws-sdk": "2.276.1"
31 | }
32 | },
33 | "ask-sdk-model": {
34 | "version": "1.9.0",
35 | "resolved": "https://registry.npmjs.org/ask-sdk-model/-/ask-sdk-model-1.9.0.tgz",
36 | "integrity": "sha512-2oJgVTTtUacpngbKiLlq4hCV1d8rmsl0zN+AEbffUkNWngtaxQEdKJiEABHQhuk5PwVWCx7ZvNMc7/xyx11vJg=="
37 | },
38 | "ask-sdk-runtime": {
39 | "version": "2.2.0",
40 | "resolved": "https://registry.npmjs.org/ask-sdk-runtime/-/ask-sdk-runtime-2.2.0.tgz",
41 | "integrity": "sha512-pYqTizpk+13l7sU/bdyIZKRptyR7fP05T8OdY0/tpD8WQaCyIvPigIEHjYcXN0Yz23FkKECbIRvfXN9OQ7Ka5w=="
42 | },
43 | "aws-sdk": {
44 | "version": "2.276.1",
45 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.276.1.tgz",
46 | "integrity": "sha1-T1zVL00aE6QSyKjXWydsVWrROUc=",
47 | "requires": {
48 | "buffer": "4.9.1",
49 | "events": "1.1.1",
50 | "ieee754": "1.1.8",
51 | "jmespath": "0.15.0",
52 | "querystring": "0.2.0",
53 | "sax": "1.2.1",
54 | "url": "0.10.3",
55 | "uuid": "3.1.0",
56 | "xml2js": "0.4.19"
57 | }
58 | },
59 | "base64-js": {
60 | "version": "1.3.0",
61 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
62 | "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM="
63 | },
64 | "buffer": {
65 | "version": "4.9.1",
66 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
67 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
68 | "requires": {
69 | "base64-js": "1.3.0",
70 | "ieee754": "1.1.8",
71 | "isarray": "1.0.0"
72 | }
73 | },
74 | "events": {
75 | "version": "1.1.1",
76 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
77 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
78 | },
79 | "i18next": {
80 | "version": "12.1.0",
81 | "resolved": "https://registry.npmjs.org/i18next/-/i18next-12.1.0.tgz",
82 | "integrity": "sha512-AexmwGkKxwKfo5fGeXTWEY4xqzRPigQ1S/0InOUUVziGO54cd4fKyYK8ED1Thx9fd+WA3fRSZ+1iekvFQMbsFw=="
83 | },
84 | "ieee754": {
85 | "version": "1.1.8",
86 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
87 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
88 | },
89 | "isarray": {
90 | "version": "1.0.0",
91 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
92 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
93 | },
94 | "jmespath": {
95 | "version": "0.15.0",
96 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
97 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
98 | },
99 | "punycode": {
100 | "version": "1.3.2",
101 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
102 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
103 | },
104 | "querystring": {
105 | "version": "0.2.0",
106 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
107 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
108 | },
109 | "sax": {
110 | "version": "1.2.1",
111 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
112 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
113 | },
114 | "string-similarity": {
115 | "version": "2.0.0",
116 | "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-2.0.0.tgz",
117 | "integrity": "sha512-62FBZrVXV5cI23bQ9L49Y4d9u9yaH61JhAwLyUFUzQbHDjdihxdfCwIherg+vylR/s4ucCddK8iKSEO7kinffQ=="
118 | },
119 | "url": {
120 | "version": "0.10.3",
121 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
122 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
123 | "requires": {
124 | "punycode": "1.3.2",
125 | "querystring": "0.2.0"
126 | }
127 | },
128 | "uuid": {
129 | "version": "3.1.0",
130 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
131 | "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ="
132 | },
133 | "xml2js": {
134 | "version": "0.4.19",
135 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
136 | "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=",
137 | "requires": {
138 | "sax": "1.2.1",
139 | "xmlbuilder": "9.0.7"
140 | }
141 | },
142 | "xmlbuilder": {
143 | "version": "9.0.7",
144 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
145 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/lambda/custom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "skill-sample-nodejs-buttons-game-templates",
3 | "version": "1.0.0",
4 | "description": "Echo Button Game Template Skill",
5 | "main": "index.js",
6 | "dependencies": {
7 | "ask-sdk": "^2.3.0",
8 | "i18next": "^12.0.0",
9 | "string-similarity": "^2.0.0"
10 | },
11 | "devDependencies": {},
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "keywords": [
16 | "alexa",
17 | "skill",
18 | "echo buttons"
19 | ],
20 | "author": "Amazon.com",
21 | "license": "ADSL"
22 | }
23 |
--------------------------------------------------------------------------------
/lambda/custom/utils/animations.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | // Basic Animation Helper Library
13 |
14 | 'use strict';
15 |
16 | //const logger = require('./logger.js');
17 |
18 | const COLORS = {
19 | "white": "ffffff",
20 | "red": "ff0000",
21 | "orange": "ff3300",
22 | "green": "00ff00",
23 | "dark green": "004411",
24 | "blue": "0000ff",
25 | "light blue": "00a0b0",
26 | "purple": "4b0098",
27 | "yellow": "ffd400",
28 | "black": "000000"
29 | };
30 |
31 | const ColorHelper = {
32 | getColor: function (colorName) {
33 | if (typeof colorName === 'string' &&
34 | COLORS[colorName.toLowerCase()]) {
35 | return COLORS[colorName.toLowerCase()];
36 | }
37 | console.log('# WARN # UNKNOWN COLOR: ' + entry);
38 | },
39 | validateColor: function (requestedColor) {
40 | var color = requestedColor || '';
41 | if (color.indexOf('0x') === 0) {
42 | return color.substring(2);
43 | } else if (color.indexOf('#') === 0) {
44 | return color.substring(1);
45 | } else {
46 | return ColorHelper.getColor(color) || color;
47 | }
48 | }
49 | };
50 |
51 | const BasicAnimations = {
52 | // Solid Animation
53 | SolidAnimation: function (cycles, color, duration) {
54 | return [
55 | {
56 | "repeat": cycles,
57 | "targetLights": ["1"],
58 | "sequence": [
59 | {
60 | "durationMs": duration,
61 | "blend": false,
62 | "color": ColorHelper.validateColor(color)
63 | }
64 | ]
65 | }
66 | ];
67 | },
68 | // FadeIn Animation
69 | FadeInAnimation: function (cycles, color, duration) {
70 | return [
71 | {
72 | "repeat": cycles,
73 | "targetLights": ["1"],
74 | "sequence": [
75 | {
76 | "durationMs": 1,
77 | "blend": true,
78 | "color": "000000"
79 | }, {
80 | "durationMs": duration,
81 | "blend": true,
82 | "color": ColorHelper.validateColor(color)
83 | }
84 | ]
85 | }
86 | ];
87 | },
88 | // FadeOut Animation
89 | FadeOutAnimation: function (cycles, color, duration) {
90 | return [
91 | {
92 | "repeat": cycles,
93 | "targetLights": ["1"],
94 | "sequence": [
95 | {
96 | "durationMs": duration,
97 | "blend": true,
98 | "color": ColorHelper.validateColor(color)
99 | }, {
100 | "durationMs": 1,
101 | "blend": true,
102 | "color": "000000"
103 | }
104 | ]
105 | }
106 | ];
107 | },
108 | // CrossFade Animation
109 | CrossFadeAnimation: function (cycles, colorOne, colorTwo, durationOne, durationTwo) {
110 | return [
111 | {
112 | "repeat": cycles,
113 | "targetLights": ["1"],
114 | "sequence": [
115 | {
116 | "durationMs": durationOne,
117 | "blend": true,
118 | "color": ColorHelper.validateColor(colorOne)
119 | }, {
120 | "durationMs": durationTwo,
121 | "blend": true,
122 | "color": ColorHelper.validateColor(colorTwo)
123 | }
124 | ]
125 | }
126 | ];
127 | },
128 | // Breathe Animation
129 | BreatheAnimation: function (cycles, color, duration) {
130 | return [
131 | {
132 | "repeat": cycles,
133 | "targetLights": ["1"],
134 | "sequence": [
135 | {
136 | "durationMs": 1,
137 | "blend": true,
138 | "color": "000000"
139 | },
140 | {
141 | "durationMs": duration,
142 | "blend": true,
143 | "color": ColorHelper.validateColor(color)
144 | },
145 | {
146 | "durationMs": 300,
147 | "blend": true,
148 | "color": ColorHelper.validateColor(color)
149 | },
150 | {
151 | "durationMs": 300,
152 | "blend": true,
153 | "color": "000000"
154 | }
155 | ]
156 | }
157 | ];
158 | },
159 | // Blink Animation
160 | BlinkAnimation: function (cycles, color) {
161 | return [
162 | {
163 | "repeat": cycles,
164 | "targetLights": ["1"],
165 | "sequence": [
166 | {
167 | "durationMs": 500,
168 | "blend": false,
169 | "color": ColorHelper.validateColor(color)
170 | }, {
171 | "durationMs": 500,
172 | "blend": false,
173 | "color": "000000"
174 | }
175 | ]
176 | }
177 | ];
178 | },
179 | // Flip Animation
180 | FlipAnimation: function (cycles, colorOne, colorTwo, durationOne, durationTwo) {
181 | return [
182 | {
183 | "repeat": cycles,
184 | "targetLights": ["1"],
185 | "sequence": [
186 | {
187 | "durationMs": durationOne,
188 | "blend": false,
189 | "color": ColorHelper.validateColor(colorOne)
190 | }, {
191 | "durationMs": durationTwo,
192 | "blend": false,
193 | "color": ColorHelper.validateColor(colorTwo)
194 | }
195 | ]
196 | }
197 | ];
198 | },
199 | // Pulse Animation
200 | PulseAnimation: function (cycles, colorOne, colorTwo) {
201 | return [
202 | {
203 | "repeat": cycles,
204 | "targetLights": ["1"],
205 | "sequence": [
206 | {
207 | "durationMs": 500,
208 | "blend": true,
209 | "color": ColorHelper.validateColor(colorOne)
210 | }, {
211 | "durationMs": 1000,
212 | "blend": true,
213 | "color": ColorHelper.validateColor(colorTwo)
214 | }
215 | ]
216 | }
217 | ];
218 | }
219 | };
220 |
221 | const ComplexAnimations = {
222 | AnswerAnimation: function (correct_color, baseline_color, duration) {
223 | // blink correct color for 3 seconds
224 | let colorSequence = [];
225 | for (let i = 0; i < 3; i++) {
226 | colorSequence.push({
227 | "durationMs": 500,
228 | "color": ColorHelper.validateColor(correct_color),
229 | "blend": false
230 | });
231 | colorSequence.push({
232 | "durationMs": 500,
233 | "color": ColorHelper.validateColor('black'),
234 | "blend": false
235 | });
236 | }
237 | colorSequence.push({
238 | "durationMs": duration,
239 | "color": ColorHelper.validateColor(baseline_color),
240 | "blend": false
241 | });
242 | return [
243 | {
244 | "repeat": 1,
245 | "targetLights": ["1"],
246 | "sequence": colorSequence
247 | }
248 | ];
249 | },
250 | // Spectrum Animation
251 | SpectrumAnimation: function (cycles, color) {
252 | let colorSequence = [];
253 | for (let i = 0; i < color.length; i++) {
254 |
255 | colorSequence.push({
256 | "durationMs": 400,
257 | "color": ColorHelper.validateColor(color[i]),
258 | "blend": true
259 | });
260 | }
261 | return [
262 | {
263 | "repeat": cycles,
264 | "targetLights": ["1"],
265 | "sequence": colorSequence
266 | }
267 | ];
268 | }
269 | };
270 |
271 | module.exports.ComplexAnimations = ComplexAnimations;
272 | module.exports.BasicAnimations = BasicAnimations;
273 |
--------------------------------------------------------------------------------
/lambda/custom/utils/directives.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | 'use strict';
13 |
14 | const RequiredParam = function (param) {
15 | const requiredParamError = new Error(
16 | `Required parameter, "${param}" is missing.`
17 | )
18 | // preserve original stack trace
19 | if (typeof Error.captureStackTrace === 'function') {
20 | Error.captureStackTrace(
21 | requiredParamError,
22 | RequiredParam
23 | )
24 | }
25 | throw requiredParamError;
26 | };
27 |
28 | const GameEngine = {
29 | // returns a StartInputHandler directive that can be added to an Alexa skill response
30 | startInputHandler: function ({
31 | timeout = RequiredParam('timeout'),
32 | proxies,
33 | recognizers = RequiredParam('recognizers'),
34 | events = RequiredParam('events')
35 | } = {}, params) {
36 | return {
37 | "type": "GameEngine.StartInputHandler",
38 | "timeout": (params && params.timeout) || timeout,
39 | "proxies": (params && params.proxies) || proxies,
40 | "recognizers": (params && params.recognizers) || recognizers,
41 | "events": (params && params.events) || events,
42 | };
43 | },
44 |
45 | // returns a StopInputHandler directive that can be added to an Alexa skill response
46 | stopInputHandler: function ({
47 | id = RequiredParam('id')
48 | } = {}) {
49 | return {
50 | "type": "GameEngine.StopInputHandler",
51 | "originatingRequestId": id
52 | };
53 | },
54 | };
55 | const GadgetController = {
56 | // returns a SetLight directive, with a 'buttonDown' trigger, that can be added to an Alexa skill response
57 | setButtonDownAnimation: function ({
58 | targetGadgets = [],
59 | animations = RequiredParam('animations'),
60 | triggerEventTimeMs = 0
61 | } = {}, params) {
62 | return {
63 | "type": "GadgetController.SetLight",
64 | "version": 1,
65 | "targetGadgets": (params && params.targetGadgets) || targetGadgets,
66 | "parameters": {
67 | "animations": (params && params.animations) || animations,
68 | "triggerEvent": "buttonDown",
69 | "triggerEventTimeMs": (params && params.triggerEventTimeMs) || triggerEventTimeMs,
70 | }
71 | };
72 | },
73 |
74 | // returns a SetLight directive, with a 'buttonUp' trigger, that can be added to an Alexa skill response
75 | setButtonUpAnimation: function ({
76 | targetGadgets = [],
77 | animations = RequiredParam('animations'),
78 | triggerEventTimeMs = 0
79 | } = {}, params) {
80 | return {
81 | "type": "GadgetController.SetLight",
82 | "version": 1,
83 | "targetGadgets": (params && params.targetGadgets) || targetGadgets,
84 | "parameters": {
85 | "animations": (params && params.animations) || animations,
86 | "triggerEvent": "buttonUp",
87 | "triggerEventTimeMs": (params && params.triggerEventTimeMs) || triggerEventTimeMs,
88 | }
89 | };
90 | },
91 |
92 | // returns a SetLight directive, with a 'none' trigger, that can be added to an Alexa skill response
93 | setIdleAnimation: function ({
94 | targetGadgets = [],
95 | animations = RequiredParam('animations'),
96 | triggerEventTimeMs = 0
97 | } = {}, params) {
98 | return {
99 | "type": "GadgetController.SetLight",
100 | "version": 1,
101 | "targetGadgets": (params && params.targetGadgets) || targetGadgets,
102 | "parameters": {
103 | "animations": (params && params.animations) || animations,
104 | "triggerEvent": "none",
105 | "triggerEventTimeMs": (params && params.triggerEventTimeMs) || triggerEventTimeMs,
106 | }
107 | };
108 | }
109 | };
110 |
111 | module.exports.GameEngine = GameEngine;
112 | module.exports.GadgetController = GadgetController;
--------------------------------------------------------------------------------
/lambda/custom/utils/display.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | const Alexa = require('ask-sdk');
13 | const settings = require('../config/settings.js');
14 | const logger = require('../utils/logger.js');
15 |
16 | const Display = {
17 | render: function (
18 | /* The Alexa request and attributes */
19 | handlerInput,
20 | {
21 | displayTitle,
22 | /* primary text content to display */
23 | displayText,
24 | /* (optional) secondary text content to display */
25 | displaySubText,
26 | /* a background image to be displayed under the text content */
27 | backgroundImage,
28 | /* (optional) an image to be displayed on the side of the text content */
29 | image
30 | } = {}) {
31 |
32 | let ctx = handlerInput.attributesManager.getRequestAttributes();
33 |
34 | /**
35 | * Check for display
36 | */
37 | if (!handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display) {
38 | logger.debug('No display to render.');
39 | return;
40 | }
41 |
42 | if (!displayText) {
43 | logger.warn('Render template without primary text!');
44 | }
45 |
46 | let text = displayText || '';
47 | if (Array.isArray(text)) {
48 | text = settings.pickRandom(text);
49 | }
50 |
51 | let subText = displaySubText || '';
52 | if (Array.isArray(subText)) {
53 | subText = settings.pickRandom(subText);
54 | }
55 |
56 | const background = backgroundImage || settings.pickRandom(settings.IMAGES.BACKGROUND_IMAGES);
57 |
58 | // Rich can handle plain as well
59 | const textContent = new Alexa.RichTextContentHelper()
60 | .withPrimaryText(text)
61 | .withSecondaryText(subText)
62 | .getTextContent();
63 |
64 | const renderBackground = new Alexa.ImageHelper()
65 | .addImageInstance(background)
66 | .getImage();
67 |
68 | if (image) {
69 | renderImage = new Alexa.ImageHelper()
70 | .addImageInstance(image)
71 | .getImage();
72 | ctx.renderTemplate = {
73 | type: 'BodyTemplate3',
74 | backButton: 'HIDDEN',
75 | backgroundImage: renderBackground,
76 | title: displayTitle,
77 | image: renderImage,
78 | textContent: textContent
79 | }
80 | } else {
81 | ctx.renderTemplate = {
82 | type: 'BodyTemplate1',
83 | backButton: 'HIDDEN',
84 | backgroundImage: renderBackground,
85 | title: displayTitle,
86 | textContent: textContent
87 | }
88 | }
89 | }
90 | };
91 | module.exports = Display;
--------------------------------------------------------------------------------
/lambda/custom/utils/logger.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | 'use strict';
13 |
14 | function getLevelValue(level) {
15 | switch (level) {
16 | case 'DEBUG':
17 | return 3;
18 | case 'WARN':
19 | return 1;
20 | case 'ERROR':
21 | return 0;
22 | default:
23 | return 2;
24 | }
25 | }
26 |
27 | const LOG_VALUE = getLevelValue(require('../config/settings.js').LOG_LEVEL);
28 |
29 | function log(level, entry) {
30 | if (getLevelValue(level) <= LOG_VALUE) {
31 | console.log('# ' + level + ' # ' + entry);
32 | }
33 | }
34 |
35 | function info(entry){
36 | log('INFO', entry);
37 | }
38 |
39 | function debug(entry){
40 | log('DEBUG', entry);
41 | }
42 |
43 | function warn(entry){
44 | log('WARN', entry);
45 | }
46 |
47 | function error(entry){
48 | log('ERROR', entry);
49 | }
50 |
51 | module.exports.log = log;
52 | module.exports.info = info;
53 | module.exports.debug = debug;
54 | module.exports.warn = warn;
55 | module.exports.error = error;
--------------------------------------------------------------------------------
/lambda/custom/utils/rollcall.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
3 | * Licensed under the Amazon Software License (the "License").
4 | * You may not use this file except in compliance with the License.
5 | * A copy of the License is located at
6 | * http://aws.amazon.com/asl/
7 | * or in the "license" file accompanying this file. This file is distributed
8 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9 | * express or implied. See the License for the specific language governing
10 | * permissions and limitations under the License.
11 | */
12 | 'use strict';
13 |
14 | const settings = require('../config/settings.js');
15 | const animations = require('./animations.js');
16 | const directives = require('./directives.js');
17 | const logger = require('./logger.js');
18 |
19 | const DEFAULT_BUTTON_ANIMATION_DIRECTIVES = {
20 | 'BUTTON_DOWN': directives.GadgetController.setButtonDownAnimation({
21 | 'animations': animations.BasicAnimations.FadeOutAnimation(1, "blue", 200)
22 | }),
23 | 'BUTTON_UP': directives.GadgetController.setButtonUpAnimation({
24 | 'animations': animations.BasicAnimations.SolidAnimation(1, "black", 100)
25 | })
26 | };
27 |
28 | /**
29 | * A template input handler configuration that will be
30 | * used as a starting point for the roll call input handler.
31 | *
32 | * The final configuration is dynamically generated.
33 | */
34 | const ROLL_CALL_INPUT_HANDLER_CONFIG_TEMPLATE = {
35 | 'timeout': 1000,
36 | 'proxies': [],
37 | 'recognizers': {
38 | 'roll_call_all_buttons': {
39 | "type": "match",
40 | "fuzzy": true,
41 | "anchor": "end",
42 | "pattern": []
43 | }
44 | },
45 | 'events': {
46 | 'roll_call_complete': {
47 | 'meets': ['roll_call_all_buttons'],
48 | 'reports': 'matches',
49 | 'shouldEndInputHandler': true,
50 | 'maximumInvocations': 1
51 | },
52 | 'roll_call_timeout': {
53 | 'meets': ['timed out'],
54 | 'reports': 'history',
55 | 'shouldEndInputHandler': true
56 | }
57 | }
58 | };
59 |
60 | const rollCallHelper = {
61 | /**
62 | * Generates an input handler configuration, based on given number of players
63 | */
64 | generateInputHandlerConfig: function ({
65 | playerCount,
66 | timeout
67 | }) {
68 | logger.debug('ROLLCALL_HELPER: generate input handler config');
69 |
70 | playerCount = parseInt(playerCount || 1, 10);
71 |
72 | /**
73 | * For roll call we will use a list of proxies because we won't
74 | * know the Id of any of the buttons ahead of time.
75 | * The proxies will be filld out dynamically in a loop below.
76 | */
77 | let proxies = [];
78 |
79 | /**
80 | * create a recognizer pattern that matches once when all
81 | * the buttons have been pressed at least once.
82 | */
83 | let allButtonsRecognizerPattern = [];
84 |
85 | /**
86 | * create intermediate recognizers, one for first button,
87 | * one for second button, etc. that will match when each
88 | * of the buttons is pressed the first time (identifed by
89 | * proxy)
90 | */
91 | let intermediateRecognizerPatterns = [];
92 |
93 | /**
94 | * set up the proxies and recognizer patterns dynamically
95 | * based on the number of players.
96 | */
97 | for (let numPlayer = 0; numPlayer < playerCount; numPlayer++) {
98 | let proxyName = 'btn' + (1 + numPlayer);
99 | proxies.push(proxyName);
100 | let patternStep = {
101 | "gadgetIds": [proxyName],
102 | "action": "down"
103 | };
104 | allButtonsRecognizerPattern.push(patternStep);
105 | if (numPlayer < (playerCount - 1)) {
106 | // for all but the last player, add an intermediate recognizer
107 | intermediateRecognizerPatterns.push(Array.of(patternStep));
108 | }
109 | }
110 |
111 | /**
112 | * create the input handler configuration object
113 | * that defines the recognizers and events used for roll call
114 | * the full definition will be filled in dynamically
115 | */
116 | let inputHandlerConfig = Object.assign({}, ROLL_CALL_INPUT_HANDLER_CONFIG_TEMPLATE);
117 | inputHandlerConfig.proxies = proxies;
118 | inputHandlerConfig.timeout = timeout;
119 | inputHandlerConfig.recognizers
120 | .roll_call_all_buttons.pattern = allButtonsRecognizerPattern;
121 |
122 | /**
123 | * now fill in the dynamically generated recognizer and event
124 | * definitions into the input handler configuration object
125 | */
126 | for (let i = 0; i < intermediateRecognizerPatterns.length; i++) {
127 | let name = 'roll_call_button_' + (1 + i);
128 | inputHandlerConfig.recognizers[name] = {
129 | "type": "match",
130 | "fuzzy": true,
131 | "anchor": "end",
132 | /* each intermediate event has a corresponding recognizer */
133 | "pattern": intermediateRecognizerPatterns[i]
134 | }
135 | inputHandlerConfig.events[name] = {
136 | 'meets': [name],
137 | 'reports': 'matches',
138 | /* intermediate events don't stop the input handler! */
139 | 'shouldEndInputHandler': false,
140 | 'maximumInvocations': 1
141 | }
142 | }
143 |
144 | return inputHandlerConfig;
145 | },
146 |
147 | listenForRollCall: function (handlerInput, inputHandlerConfig) {
148 | logger.debug('ROLLCALL_HELPER: listen for roll call');
149 | let ctx = handlerInput.attributesManager.getRequestAttributes();
150 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
151 |
152 | sessionAttributes.inputHandlerId = handlerInput.requestEnvelope.request.requestId;
153 | ctx.directives.push(directives.GameEngine.startInputHandler(inputHandlerConfig));
154 |
155 | // Send Pre-Roll Call Animation to all connected buttons
156 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
157 | 'animations': settings.ANIMATIONS.PRE_ROLL_CALL_ANIMATION
158 | }));
159 | // Send Button Down Event
160 | ctx.directives.push(directives.GadgetController.setButtonDownAnimation({
161 | 'animations': settings.ANIMATIONS.ROLL_CALL_CHECKIN_ANIMATION
162 | }));
163 | },
164 |
165 | dispatchGameEngineEvents: function (handlerInput, inputEvents) {
166 | logger.debug('ROLLCALL_HELPER: dispatch game engine events');
167 | // try to process events in order of importance
168 | // 1) first pass through to see if there are any non-timeout events
169 | for (let eventIndex = 0; eventIndex < inputEvents.length; eventIndex++) {
170 | if ('roll_call_complete' === inputEvents[eventIndex].name) {
171 | rollCallHelper.handleRollCallComplete(handlerInput, inputEvents[eventIndex]);
172 | } else if ('roll_call_button' === inputEvents[eventIndex].name.substring(0, 16)) {
173 | rollCallHelper.handleRollCallButtonCheckIn(handlerInput, inputEvents[eventIndex]);
174 | }
175 | }
176 |
177 | // 2) second pass through to see if there are any timeout events
178 | for (let eventIndex = 0; eventIndex < inputEvents.length; eventIndex++) {
179 | if (inputEvents[eventIndex].name == 'roll_call_timeout') {
180 | rollCallHelper.handleRollCallTimeout(handlerInput);
181 | }
182 | }
183 | },
184 |
185 | handleRollCallComplete: function (handlerInput, inputEvent) {
186 | logger.debug('ROLLCALL_HELPER: handle roll call complete: ' +
187 | JSON.stringify(inputEvent));
188 | let ctx = handlerInput.attributesManager.getRequestAttributes();
189 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
190 |
191 | // Move to the button game state to begin the game
192 | sessionAttributes.STATE = settings.STATE.BUTTON_GAME_STATE;
193 |
194 | sessionAttributes.buttons = inputEvent.inputEvents
195 | .map((evt, idx) => {
196 | return {
197 | buttonId: evt.gadgetId,
198 | count: (1 + idx)
199 | };
200 | });
201 | // clear animations on all other buttons that haven't been added to the game
202 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
203 | 'animations': animations.BasicAnimations.SolidAnimation(1, "black", 100)
204 | }));
205 | // display roll call complete animation on all buttons that were added to the game
206 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
207 | 'targetGadgets': sessionAttributes.buttons.map(b => b.buttonId),
208 | 'animations': settings.ANIMATIONS.ROLL_CALL_COMPLETE_ANIMATION
209 | }));
210 |
211 | logger.debug('RollCall: resuming game play, from question: ' +
212 | sessionAttributes.currentQuestion);
213 |
214 | let currentPrompts;
215 | if (settings.ROLLCALL.NAMED_PLAYERS) {
216 | // tell the next player to press their button.
217 | let responseMessage = ctx.t('ROLL_CALL_HELLO_PLAYER', {
218 | player_number: sessionAttributes.buttons.length
219 | });
220 | currentPrompts = responseMessage;
221 | }
222 |
223 | let responseMessage = ctx.t('ROLL_CALL_COMPLETE', sessionAttributes.buttons.length);
224 | let mixedOutputSpeech = '';
225 | if (currentPrompts) {
226 | mixedOutputSpeech = currentPrompts.outputSpeech +
227 | settings.AUDIO.ROLL_CALL_COMPLETE + settings.pickRandom(responseMessage.outputSpeech);
228 | } else {
229 | mixedOutputSpeech = settings.AUDIO.ROLL_CALL_COMPLETE + settings.pickRandom(responseMessage.outputSpeech);
230 | }
231 |
232 | ctx.render(handlerInput, responseMessage);
233 | ctx.outputSpeech.push(mixedOutputSpeech);
234 | ctx.reprompt.push(responseMessage.reprompt);
235 | ctx.openMicrophone = true;
236 | },
237 |
238 | // handles the case when the roll call process times out before all players are checked in
239 | handleRollCallTimeout: function (handlerInput) {
240 | logger.debug('ROLLCALL_HELPER: handling time out event during roll call');
241 | let ctx = handlerInput.attributesManager.getRequestAttributes();
242 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
243 | // Reset button animation for all buttons
244 | ctx.directives.push(DEFAULT_BUTTON_ANIMATION_DIRECTIVES.BUTTON_DOWN);
245 | ctx.directives.push(DEFAULT_BUTTON_ANIMATION_DIRECTIVES.BUTTON_UP);
246 |
247 | let responseMessage = ctx.t('ROLL_CALL_TIME_OUT');
248 | ctx.outputSpeech.push(responseMessage.outputSpeech);
249 | ctx.reprompt.push(responseMessage.reprompt);
250 | ctx.openMicrophone = true;
251 | },
252 |
253 | handleRollCallButtonCheckIn: function (handlerInput, inputEvent) {
254 | logger.debug('ROLLCALL_HELPER: handle button press event: ' +
255 | JSON.stringify(inputEvent));
256 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
257 |
258 | let buttonId = inputEvent.inputEvents[0].gadgetId;
259 | let buttons = sessionAttributes.buttons || [];
260 |
261 | // Failsafe - Check to see if we already have this button registered and if so skip registration
262 | for (let buttonIndex = 0; buttonIndex < buttons.length; buttonIndex++) {
263 | if (buttons[buttonIndex].buttonId === buttonId){
264 | logger.debug('This button is already registered. GadgetId=' + buttonId);
265 | return;
266 | }
267 | }
268 |
269 | let buttonNumber = buttons.length + 1;
270 | logger.debug('Found a new button. New button number: ' + buttonNumber);
271 | buttons.push({
272 | count: buttonNumber,
273 | buttonId: buttonId
274 | });
275 | sessionAttributes.buttons = buttons;
276 |
277 | rollCallHelper.handlePlayerCheckedIn(handlerInput, buttonId, buttons.length);
278 | },
279 |
280 | // handles a player checking in
281 | handlePlayerCheckedIn: function (handlerInput, buttonId, playerNumber) {
282 | logger.debug('ROLLCALL_HELPER: handle new player checked in: playerNumber = ' +
283 | playerNumber + ', buttonId = ' + buttonId);
284 | let ctx = handlerInput.attributesManager.getRequestAttributes();
285 |
286 | ctx.directives.push(directives.GadgetController.setIdleAnimation({
287 | 'targetGadgets': [buttonId],
288 | 'animations': settings.ANIMATIONS.ROLL_CALL_BUTTON_ADDED_ANIMATION
289 | }));
290 | ctx.directives.push(directives.GadgetController.setButtonDownAnimation({
291 | 'targetGadgets': [buttonId],
292 | 'animations': settings.ANIMATIONS.ROLL_CALL_CHECKIN_ANIMATION
293 | }));
294 |
295 | if (settings.ROLLCALL.NAMED_PLAYERS) {
296 | // tell the next player to press their button.
297 | let responseMessage = ctx.t('ROLL_CALL_HELLO_PLAYER', {
298 | player_number: playerNumber
299 | });
300 | let currentPrompts = responseMessage;
301 | responseMessage = ctx.t('ROLL_CALL_NEXT_PLAYER_PROMPT', {
302 | player_number: playerNumber + 1
303 | });
304 | ctx.outputSpeech.push(currentPrompts.outputSpeech);
305 | ctx.outputSpeech.push("");
306 | ctx.outputSpeech.push(responseMessage.outputSpeech);
307 | ctx.outputSpeech.push(settings.AUDIO.WAITING_FOR_ROLL_CALL_AUDIO);
308 | }
309 | ctx.openMicrophone = false;
310 | },
311 |
312 | startRollCall: function (handlerInput, messageKey) {
313 | logger.debug('ROLLCALL_HELPER: resume roll call');
314 | let ctx = handlerInput.attributesManager.getRequestAttributes();
315 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
316 | let inputHandlerConfig = rollCallHelper.generateInputHandlerConfig({
317 | playerCount: sessionAttributes.playerCount,
318 | /* allow 35 seconds for roll call to complete */
319 | timeout: 35000
320 | });
321 | rollCallHelper.listenForRollCall(handlerInput, inputHandlerConfig);
322 |
323 | let responseMessage = ctx.t(messageKey);
324 | ctx.render(handlerInput, responseMessage);
325 | ctx.outputSpeech.push(responseMessage.outputSpeech);
326 | ctx.outputSpeech.push(settings.AUDIO.WAITING_FOR_ROLL_CALL_AUDIO);
327 | ctx.openMicrophone = true;
328 |
329 | sessionAttributes.buttons = [];
330 | }
331 | }
332 |
333 | const RollCall = {
334 | /**
335 | * Exported method that starts Roll Call.
336 | */
337 | start: function (handlerInput, resumingGame, playerCount) {
338 | logger.debug('ROLLCALL: start roll call');
339 | let ctx = handlerInput.attributesManager.getRequestAttributes();
340 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
341 |
342 | sessionAttributes.STATE = settings.STATE.ROLLCALL_STATE;
343 | sessionAttributes.playerCount = playerCount;
344 | sessionAttributes.buttons = [];
345 | ctx.openMicrophone = false;
346 |
347 | let messageKey = resumingGame ? 'ROLL_CALL_RESUME_GAME' : 'ROLL_CALL_CONTINUE';
348 | rollCallHelper.startRollCall(handlerInput, messageKey);
349 | },
350 |
351 | /**
352 | * Exported method that cancels an in-progress roll call.
353 | */
354 | cancel: function (handlerInput) {
355 | logger.debug('ROLLCALL: canceling roll call');
356 | let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
357 |
358 | delete sessionAttributes.buttons;
359 | if (sessionAttributes.inputHandlerId) {
360 | // Stop the previous InputHandler if one was running
361 | let ctx = attributesManager.getRequestAttributes();
362 | ctx.directives.push(directives.GameEngine.stopInputHandler({
363 | 'id': sessionAttributes.inputHandlerId
364 | }));
365 | }
366 | },
367 |
368 | /**
369 | * Exported GameEngine event handler.
370 | * Should be called to receive all GameEngine InputHandlerEvents
371 | */
372 | handleEvents: function (handlerInput, inputEvents) {
373 | return rollCallHelper.dispatchGameEngineEvents(handlerInput, inputEvents);
374 | }
375 | };
376 | module.exports = RollCall;
--------------------------------------------------------------------------------
/skill-package/interactionModels/custom/de-DE.json:
--------------------------------------------------------------------------------
1 | {
2 | "interactionModel": {
3 | "languageModel": {
4 | "invocationName": "besser buttons quiz",
5 | "intents": [
6 | {
7 | "name": "AMAZON.CancelIntent",
8 | "samples": []
9 | },
10 | {
11 | "name": "AMAZON.HelpIntent",
12 | "samples": [
13 | "Wie wird dieses Spiel gespielt",
14 | "Wie spielt man dieses Spiel",
15 | "Wie spielt man das"
16 | ]
17 | },
18 | {
19 | "name": "AMAZON.YesIntent",
20 | "samples": [
21 | "bereit"
22 | ]
23 | },
24 | {
25 | "name": "AMAZON.NoIntent",
26 | "samples": []
27 | },
28 | {
29 | "name": "AMAZON.StopIntent",
30 | "samples": []
31 | },
32 | {
33 | "name": "PlayerCount",
34 | "slots": [
35 | {
36 | "name": "digit",
37 | "type": "AMAZON.NUMBER"
38 | }
39 | ],
40 | "samples": [
41 | "{digit} Spieler",
42 | "wir sind {digit}",
43 | "wir sind {digit} Spieler",
44 | "wir sind insgesamt {digit}",
45 | "wir haben {digit}",
46 | "wir haben {digit} Spieler",
47 | "es sind {digit}",
48 | "es sind {digit} Spieler"
49 | ]
50 | },
51 | {
52 | "name": "AnswerQuestionIntent",
53 | "slots": [
54 | {
55 | "name": "answers",
56 | "type": "answers"
57 | }
58 | ],
59 | "samples": [
60 | "in {answers}",
61 | "eine {answers}",
62 | "ein {answers}",
63 | "der {answers}",
64 | "die {answers}",
65 | "das {answers}",
66 | "die Antwort lautet {answers}",
67 | "meine Antwort lautet {answers}",
68 | "ist es {answers}",
69 | "es ist {answers}",
70 | "es war {answers}",
71 | "ist es vielleicht {answers}",
72 | "ich glaube es ist {answers}",
73 | "ich glaube es war {answers}",
74 | "ich glaube es könnte {answers} sein",
75 | "ich glaube die Antwort lautet {answers}",
76 | "ich glaube die Antwort lautet eventuell {answers}"
77 | ]
78 | },
79 | {
80 | "name": "AnswerOnlyIntent",
81 | "slots": [
82 | {
83 | "name": "answers",
84 | "type": "answers"
85 | }
86 | ],
87 | "samples": [
88 | "{answers}"
89 | ]
90 | },
91 | {
92 | "name": "PlayGame",
93 | "samples": [
94 | "abspielen",
95 | "spiel das Spiel",
96 | "spiel ein Spiel",
97 | "Ich bin bereit für das Spiel",
98 | "lass uns spielen",
99 | "starte ein Spiel",
100 | "starte das Spiel",
101 | "lass uns mit dem Spiel anfangen",
102 | "spielbereit",
103 | "startbereit",
104 | "lass uns anfangen",
105 | "lass uns loslegen",
106 | "beginn mit dem Spiel",
107 | "fang an"
108 | ]
109 | },
110 | {
111 | "name": "AMAZON.StartOverIntent",
112 | "samples": [
113 | "beginn mit einem neuen Spiel",
114 | "spiel ein neues Spiel",
115 | "starte ein neues Spiel",
116 | "beginn ein neues Spiel",
117 | "Spiel starten",
118 | "neues Spiel beginnen",
119 | "neues Spiel",
120 | "erneut starten"
121 | ]
122 | },
123 | {
124 | "name": "DontKnowIntent",
125 | "samples": [
126 | "ich weiß es nicht",
127 | "weiß nicht",
128 | "ich bin mir nicht sicher",
129 | "ich habe keine Ahnung",
130 | "das verstehe ich nicht",
131 | "ich habe keinen blassen Schimmer",
132 | "mir fällt dazu nichts ein",
133 | "keine Ahnung",
134 | "ich habe keinen Schimmer",
135 | "ich bin ahnungslos"
136 | ]
137 | },
138 | {
139 | "name": "AMAZON.MoreIntent",
140 | "samples": []
141 | },
142 | {
143 | "name": "AMAZON.NavigateHomeIntent",
144 | "samples": []
145 | },
146 | {
147 | "name": "AMAZON.NavigateSettingsIntent",
148 | "samples": []
149 | },
150 | {
151 | "name": "AMAZON.NextIntent",
152 | "samples": [
153 | "überspringen",
154 | "überspring diese Frage",
155 | "Frage überspringen",
156 | "nächste Frage",
157 | "weiter zur nächsten Frage",
158 | "geh zur nächsten Frage",
159 | "ich gebe auf",
160 | "wir geben auf"
161 | ]
162 | },
163 | {
164 | "name": "AMAZON.PageUpIntent",
165 | "samples": []
166 | },
167 | {
168 | "name": "AMAZON.PageDownIntent",
169 | "samples": []
170 | },
171 | {
172 | "name": "AMAZON.PreviousIntent",
173 | "samples": []
174 | },
175 | {
176 | "name": "AMAZON.ScrollRightIntent",
177 | "samples": []
178 | },
179 | {
180 | "name": "AMAZON.ScrollDownIntent",
181 | "samples": []
182 | },
183 | {
184 | "name": "AMAZON.ScrollLeftIntent",
185 | "samples": []
186 | },
187 | {
188 | "name": "AMAZON.ScrollUpIntent",
189 | "samples": []
190 | },
191 | {
192 | "name": "NumericResponseIntent",
193 | "slots": [
194 | {
195 | "name": "digit",
196 | "type": "AMAZON.NUMBER"
197 | }
198 | ],
199 | "samples": [
200 | "{digit}"
201 | ]
202 | }
203 | ],
204 | "types": [
205 | {
206 | "values": [
207 | {
208 | "name": {
209 | "value": "Fuchs"
210 | }
211 | },
212 | {
213 | "name": {
214 | "value": "Wolf"
215 | }
216 | },
217 | {
218 | "name": {
219 | "value": "Katze"
220 | }
221 | },
222 | {
223 | "name": {
224 | "value": "Hund"
225 | }
226 | },
227 | {
228 | "name": {
229 | "value": "Karibu"
230 | }
231 | },
232 | {
233 | "name": {
234 | "value": "Eisbär"
235 | }
236 | },
237 | {
238 | "name": {
239 | "value": "Narwal"
240 | }
241 | },
242 | {
243 | "name": {
244 | "value": "Pinguin"
245 | }
246 | },
247 | {
248 | "name": {
249 | "value": "Orang-Utan"
250 | }
251 | },
252 | {
253 | "name": {
254 | "value": "Elefant"
255 | }
256 | },
257 | {
258 | "name": {
259 | "value": "Kodiakbär"
260 | }
261 | },
262 | {
263 | "name": {
264 | "value": "Blauwal"
265 | }
266 | },
267 | {
268 | "name": {
269 | "value": "Olaf"
270 | }
271 | },
272 | {
273 | "name": {
274 | "value": "Knut"
275 | }
276 | },
277 | {
278 | "name": {
279 | "value": "Sven"
280 | }
281 | },
282 | {
283 | "name": {
284 | "value": "Kreuzotter"
285 | }
286 | },
287 | {
288 | "name": {
289 | "value": "Mokassinschlange"
290 | }
291 | },
292 | {
293 | "name": {
294 | "value": "Korallenschlange"
295 | }
296 | },
297 | {
298 | "name": {
299 | "value": "Kobra"
300 | }
301 | },
302 | {
303 | "name": {
304 | "value": "Fächerfisch"
305 | }
306 | },
307 | {
308 | "name": {
309 | "value": "Schweinswal"
310 | }
311 | },
312 | {
313 | "name": {
314 | "value": "Schwarz mit weißen Streifen"
315 | }
316 | },
317 | {
318 | "name": {
319 | "value": "Weiß mit schwarzen Streifen"
320 | }
321 | },
322 | {
323 | "name": {
324 | "value": "Muschel"
325 | }
326 | },
327 | {
328 | "name": {
329 | "value": "Fisch"
330 | }
331 | },
332 | {
333 | "name": {
334 | "value": "Spinnentier"
335 | }
336 | },
337 | {
338 | "name": {
339 | "value": "Krustentier"
340 | }
341 | },
342 | {
343 | "name": {
344 | "value": "Brillenbär"
345 | }
346 | },
347 | {
348 | "name": {
349 | "value": "Riesenpanda"
350 | }
351 | },
352 | {
353 | "name": {
354 | "value": "Delfine"
355 | }
356 | },
357 | {
358 | "name": {
359 | "value": "Braunbär"
360 | }
361 | },
362 | {
363 | "name": {
364 | "value": "Meute"
365 | }
366 | },
367 | {
368 | "name": {
369 | "value": "Schule"
370 | }
371 | },
372 | {
373 | "name": {
374 | "value": "Rudel"
375 | }
376 | },
377 | {
378 | "name": {
379 | "value": "Rotte"
380 | }
381 | },
382 | {
383 | "name": {
384 | "value": "Fliegender Fisch"
385 | }
386 | },
387 | {
388 | "name": {
389 | "value": "Thunfisch"
390 | }
391 | }
392 | ],
393 | "name": "answers"
394 | }
395 | ]
396 | }
397 | }
398 | }
399 |
--------------------------------------------------------------------------------
/skill-package/interactionModels/custom/en-GB.json:
--------------------------------------------------------------------------------
1 | {
2 | "interactionModel": {
3 | "languageModel": {
4 | "invocationName": "better buttons trivia",
5 | "intents": [
6 | {
7 | "name": "AMAZON.CancelIntent",
8 | "samples": []
9 | },
10 | {
11 | "name": "AMAZON.HelpIntent",
12 | "samples": [
13 | "how do I play this game",
14 | "how is this game played",
15 | "how do I play this"
16 | ]
17 | },
18 | {
19 | "name": "AMAZON.YesIntent",
20 | "samples": [
21 | "ready"
22 | ]
23 | },
24 | {
25 | "name": "AMAZON.NoIntent",
26 | "samples": []
27 | },
28 | {
29 | "name": "AMAZON.StopIntent",
30 | "samples": []
31 | },
32 | {
33 | "name": "PlayerCount",
34 | "slots": [
35 | {
36 | "name": "digit",
37 | "type": "AMAZON.NUMBER"
38 | }
39 | ],
40 | "samples": [
41 | "{digit} players",
42 | "there are {digit}",
43 | "there are {digit} players",
44 | "there are {digit} of us",
45 | "we have {digit}",
46 | "we have {digit} players",
47 | "there will be {digit}",
48 | "there will be {digit} players"
49 | ]
50 | },
51 | {
52 | "name": "AnswerQuestionIntent",
53 | "slots": [
54 | {
55 | "name": "answers",
56 | "type": "answers"
57 | }
58 | ],
59 | "samples": [
60 | "a {answers}",
61 | "the {answers}",
62 | "the answer is {answers}",
63 | "my answers is {answers}",
64 | "is it {answers}",
65 | "it is {answers}",
66 | "it was {answers}",
67 | "it might be {answers}",
68 | "i think it is {answers}",
69 | "i think it was {answers}",
70 | "i think it might be {answers}",
71 | "i think the answer is {answers}",
72 | "i think the answer might be {answers}"
73 | ]
74 | },
75 | {
76 | "name": "AnswerOnlyIntent",
77 | "slots": [
78 | {
79 | "name": "answers",
80 | "type": "answers"
81 | }
82 | ],
83 | "samples": [
84 | "{answers}"
85 | ]
86 | },
87 | {
88 | "name": "PlayGame",
89 | "samples": [
90 | "play",
91 | "play the game",
92 | "play a game",
93 | "I'm ready to play",
94 | "let's play",
95 | "start a game",
96 | "start the game",
97 | "let's start the game",
98 | "ready to play",
99 | "ready to start",
100 | "let's start",
101 | "let's begin",
102 | "begin the game",
103 | "get started"
104 | ]
105 | },
106 | {
107 | "name": "AMAZON.StartOverIntent",
108 | "samples": [
109 | "start new game",
110 | "play a new game",
111 | "start a new game",
112 | "begin a new game",
113 | "start game",
114 | "begin new game",
115 | "new game",
116 | "start over"
117 | ]
118 | },
119 | {
120 | "name": "DontKnowIntent",
121 | "samples": [
122 | "I don't know",
123 | "don't know",
124 | "I am not sure",
125 | "I have not idea",
126 | "I don't understand",
127 | "I've got no clue",
128 | "I've got no idea",
129 | "I have no clue",
130 | "I have no idea",
131 | "I am clueless"
132 | ]
133 | },
134 | {
135 | "name": "AMAZON.MoreIntent",
136 | "samples": []
137 | },
138 | {
139 | "name": "AMAZON.NavigateHomeIntent",
140 | "samples": []
141 | },
142 | {
143 | "name": "AMAZON.NavigateSettingsIntent",
144 | "samples": []
145 | },
146 | {
147 | "name": "AMAZON.NextIntent",
148 | "samples": [
149 | "skip",
150 | "skip this question",
151 | "skip this one",
152 | "next question",
153 | "go to next question",
154 | "go to the next question",
155 | "I give up",
156 | "we give up"
157 | ]
158 | },
159 | {
160 | "name": "AMAZON.PageUpIntent",
161 | "samples": []
162 | },
163 | {
164 | "name": "AMAZON.PageDownIntent",
165 | "samples": []
166 | },
167 | {
168 | "name": "AMAZON.PreviousIntent",
169 | "samples": []
170 | },
171 | {
172 | "name": "AMAZON.ScrollRightIntent",
173 | "samples": []
174 | },
175 | {
176 | "name": "AMAZON.ScrollDownIntent",
177 | "samples": []
178 | },
179 | {
180 | "name": "AMAZON.ScrollLeftIntent",
181 | "samples": []
182 | },
183 | {
184 | "name": "AMAZON.ScrollUpIntent",
185 | "samples": []
186 | },
187 | {
188 | "name": "NumericResponseIntent",
189 | "slots": [
190 | {
191 | "name": "digit",
192 | "type": "AMAZON.NUMBER"
193 | }
194 | ],
195 | "samples": [
196 | "{digit}"
197 | ]
198 | }
199 | ],
200 | "types": [
201 | {
202 | "values": [
203 | {
204 | "name": {
205 | "value": "fox"
206 | }
207 | },
208 | {
209 | "name": {
210 | "value": "wolf"
211 | }
212 | },
213 | {
214 | "name": {
215 | "value": "cat"
216 | }
217 | },
218 | {
219 | "name": {
220 | "value": "dog"
221 | }
222 | },
223 | {
224 | "name": {
225 | "value": "caribou"
226 | }
227 | },
228 | {
229 | "name": {
230 | "value": "polar bear"
231 | }
232 | },
233 | {
234 | "name": {
235 | "value": "narwhal"
236 | }
237 | },
238 | {
239 | "name": {
240 | "value": "penguin"
241 | }
242 | },
243 | {
244 | "name": {
245 | "value": "orangutan"
246 | }
247 | },
248 | {
249 | "name": {
250 | "value": "elephant"
251 | }
252 | },
253 | {
254 | "name": {
255 | "value": "kodiac"
256 | }
257 | },
258 | {
259 | "name": {
260 | "value": "blue whale"
261 | }
262 | },
263 | {
264 | "name": {
265 | "value": "janet"
266 | }
267 | },
268 | {
269 | "name": {
270 | "value": "jenny"
271 | }
272 | },
273 | {
274 | "name": {
275 | "value": "joey"
276 | }
277 | },
278 | {
279 | "name": {
280 | "value": "adder"
281 | }
282 | },
283 | {
284 | "name": {
285 | "value": "copperhead"
286 | }
287 | },
288 | {
289 | "name": {
290 | "value": "coral snake"
291 | }
292 | },
293 | {
294 | "name": {
295 | "value": "cobra"
296 | }
297 | },
298 | {
299 | "name": {
300 | "value": "sailfish"
301 | }
302 | },
303 | {
304 | "name": {
305 | "value": "porpoise"
306 | }
307 | },
308 | {
309 | "name": {
310 | "value": "black with white stripes"
311 | }
312 | },
313 | {
314 | "name": {
315 | "value": "white with black stripes"
316 | }
317 | },
318 | {
319 | "name": {
320 | "value": "shell"
321 | }
322 | },
323 | {
324 | "name": {
325 | "value": "fish"
326 | }
327 | },
328 | {
329 | "name": {
330 | "value": "arachnid"
331 | }
332 | },
333 | {
334 | "name": {
335 | "value": "crustacean"
336 | }
337 | },
338 | {
339 | "name": {
340 | "value": "spectacled bear"
341 | }
342 | },
343 | {
344 | "name": {
345 | "value": "giant panda"
346 | }
347 | },
348 | {
349 | "name": {
350 | "value": "dolphins"
351 | }
352 | },
353 | {
354 | "name": {
355 | "value": "brown bear"
356 | }
357 | },
358 | {
359 | "name": {
360 | "value": "frat"
361 | }
362 | },
363 | {
364 | "name": {
365 | "value": "den"
366 | }
367 | },
368 | {
369 | "name": {
370 | "value": "pride"
371 | }
372 | },
373 | {
374 | "name": {
375 | "value": "pack"
376 | }
377 | },
378 | {
379 | "name": {
380 | "value": "flying fish"
381 | }
382 | },
383 | {
384 | "name": {
385 | "value": "tuna"
386 | }
387 | }
388 | ],
389 | "name": "answers"
390 | }
391 | ]
392 | }
393 | }
394 | }
395 |
--------------------------------------------------------------------------------
/skill-package/interactionModels/custom/en-US.json:
--------------------------------------------------------------------------------
1 | {
2 | "interactionModel": {
3 | "languageModel": {
4 | "invocationName": "better buttons trivia",
5 | "intents": [
6 | {
7 | "name": "AMAZON.CancelIntent",
8 | "samples": []
9 | },
10 | {
11 | "name": "AMAZON.HelpIntent",
12 | "samples": [
13 | "how do I play this game",
14 | "how is this game played",
15 | "how do I play this"
16 | ]
17 | },
18 | {
19 | "name": "AMAZON.YesIntent",
20 | "samples": [
21 | "ready"
22 | ]
23 | },
24 | {
25 | "name": "AMAZON.NoIntent",
26 | "samples": []
27 | },
28 | {
29 | "name": "AMAZON.StopIntent",
30 | "samples": []
31 | },
32 | {
33 | "name": "PlayerCount",
34 | "slots": [
35 | {
36 | "name": "digit",
37 | "type": "AMAZON.NUMBER"
38 | }
39 | ],
40 | "samples": [
41 | "{digit} players",
42 | "there are {digit}",
43 | "there are {digit} players",
44 | "there are {digit} of us",
45 | "we have {digit}",
46 | "we have {digit} players",
47 | "there will be {digit}",
48 | "there will be {digit} players"
49 | ]
50 | },
51 | {
52 | "name": "AnswerQuestionIntent",
53 | "slots": [
54 | {
55 | "name": "answers",
56 | "type": "answers"
57 | }
58 | ],
59 | "samples": [
60 | "a {answers}",
61 | "the {answers}",
62 | "the answer is {answers}",
63 | "my answers is {answers}",
64 | "is it {answers}",
65 | "it is {answers}",
66 | "it was {answers}",
67 | "it might be {answers}",
68 | "i think it is {answers}",
69 | "i think it was {answers}",
70 | "i think it might be {answers}",
71 | "i think the answer is {answers}",
72 | "i think the answer might be {answers}"
73 | ]
74 | },
75 | {
76 | "name": "AnswerOnlyIntent",
77 | "slots": [
78 | {
79 | "name": "answers",
80 | "type": "answers"
81 | }
82 | ],
83 | "samples": [
84 | "{answers}"
85 | ]
86 | },
87 | {
88 | "name": "PlayGame",
89 | "samples": [
90 | "play",
91 | "play the game",
92 | "play a game",
93 | "I'm ready to play",
94 | "let's play",
95 | "start a game",
96 | "start the game",
97 | "let's start the game",
98 | "ready to play",
99 | "ready to start",
100 | "let's start",
101 | "let's begin",
102 | "begin the game",
103 | "get started"
104 | ]
105 | },
106 | {
107 | "name": "AMAZON.StartOverIntent",
108 | "samples": [
109 | "start new game",
110 | "play a new game",
111 | "start a new game",
112 | "begin a new game",
113 | "start game",
114 | "begin new game",
115 | "new game",
116 | "start over"
117 | ]
118 | },
119 | {
120 | "name": "DontKnowIntent",
121 | "samples": [
122 | "I don't know",
123 | "don't know",
124 | "I am not sure",
125 | "I have not idea",
126 | "I don't understand",
127 | "I've got no clue",
128 | "I've got no idea",
129 | "I have no clue",
130 | "I have no idea",
131 | "I am clueless"
132 | ]
133 | },
134 | {
135 | "name": "AMAZON.MoreIntent",
136 | "samples": []
137 | },
138 | {
139 | "name": "AMAZON.NavigateHomeIntent",
140 | "samples": []
141 | },
142 | {
143 | "name": "AMAZON.NavigateSettingsIntent",
144 | "samples": []
145 | },
146 | {
147 | "name": "AMAZON.NextIntent",
148 | "samples": [
149 | "skip",
150 | "skip this question",
151 | "skip this one",
152 | "next question",
153 | "go to next question",
154 | "go to the next question",
155 | "I give up",
156 | "we give up"
157 | ]
158 | },
159 | {
160 | "name": "AMAZON.PageUpIntent",
161 | "samples": []
162 | },
163 | {
164 | "name": "AMAZON.PageDownIntent",
165 | "samples": []
166 | },
167 | {
168 | "name": "AMAZON.PreviousIntent",
169 | "samples": []
170 | },
171 | {
172 | "name": "AMAZON.ScrollRightIntent",
173 | "samples": []
174 | },
175 | {
176 | "name": "AMAZON.ScrollDownIntent",
177 | "samples": []
178 | },
179 | {
180 | "name": "AMAZON.ScrollLeftIntent",
181 | "samples": []
182 | },
183 | {
184 | "name": "AMAZON.ScrollUpIntent",
185 | "samples": []
186 | },
187 | {
188 | "name": "NumericResponseIntent",
189 | "slots": [
190 | {
191 | "name": "digit",
192 | "type": "AMAZON.NUMBER"
193 | }
194 | ],
195 | "samples": [
196 | "{digit}"
197 | ]
198 | }
199 | ],
200 | "types": [
201 | {
202 | "values": [
203 | {
204 | "name": {
205 | "value": "fox"
206 | }
207 | },
208 | {
209 | "name": {
210 | "value": "wolf"
211 | }
212 | },
213 | {
214 | "name": {
215 | "value": "cat"
216 | }
217 | },
218 | {
219 | "name": {
220 | "value": "dog"
221 | }
222 | },
223 | {
224 | "name": {
225 | "value": "caribou"
226 | }
227 | },
228 | {
229 | "name": {
230 | "value": "polar bear"
231 | }
232 | },
233 | {
234 | "name": {
235 | "value": "narwhal"
236 | }
237 | },
238 | {
239 | "name": {
240 | "value": "penguin"
241 | }
242 | },
243 | {
244 | "name": {
245 | "value": "orangutan"
246 | }
247 | },
248 | {
249 | "name": {
250 | "value": "elephant"
251 | }
252 | },
253 | {
254 | "name": {
255 | "value": "kodiac"
256 | }
257 | },
258 | {
259 | "name": {
260 | "value": "blue whale"
261 | }
262 | },
263 | {
264 | "name": {
265 | "value": "janet"
266 | }
267 | },
268 | {
269 | "name": {
270 | "value": "jenny"
271 | }
272 | },
273 | {
274 | "name": {
275 | "value": "joey"
276 | }
277 | },
278 | {
279 | "name": {
280 | "value": "adder"
281 | }
282 | },
283 | {
284 | "name": {
285 | "value": "copperhead"
286 | }
287 | },
288 | {
289 | "name": {
290 | "value": "coral snake"
291 | }
292 | },
293 | {
294 | "name": {
295 | "value": "cobra"
296 | }
297 | },
298 | {
299 | "name": {
300 | "value": "sailfish"
301 | }
302 | },
303 | {
304 | "name": {
305 | "value": "porpoise"
306 | }
307 | },
308 | {
309 | "name": {
310 | "value": "black with white stripes"
311 | }
312 | },
313 | {
314 | "name": {
315 | "value": "white with black stripes"
316 | }
317 | },
318 | {
319 | "name": {
320 | "value": "shell"
321 | }
322 | },
323 | {
324 | "name": {
325 | "value": "fish"
326 | }
327 | },
328 | {
329 | "name": {
330 | "value": "arachnid"
331 | }
332 | },
333 | {
334 | "name": {
335 | "value": "crustacean"
336 | }
337 | },
338 | {
339 | "name": {
340 | "value": "spectacled bear"
341 | }
342 | },
343 | {
344 | "name": {
345 | "value": "giant panda"
346 | }
347 | },
348 | {
349 | "name": {
350 | "value": "dolphins"
351 | }
352 | },
353 | {
354 | "name": {
355 | "value": "brown bear"
356 | }
357 | },
358 | {
359 | "name": {
360 | "value": "frat"
361 | }
362 | },
363 | {
364 | "name": {
365 | "value": "den"
366 | }
367 | },
368 | {
369 | "name": {
370 | "value": "pride"
371 | }
372 | },
373 | {
374 | "name": {
375 | "value": "pack"
376 | }
377 | },
378 | {
379 | "name": {
380 | "value": "flying fish"
381 | }
382 | },
383 | {
384 | "name": {
385 | "value": "tuna"
386 | }
387 | }
388 | ],
389 | "name": "answers"
390 | }
391 | ]
392 | }
393 | }
394 | }
395 |
--------------------------------------------------------------------------------
/skill-package/skill.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest": {
3 | "publishingInformation": {
4 | "locales": {
5 | "en-US": {
6 | "summary": "A trivia game made better with Echo Buttons for competitive play.",
7 | "examplePhrases": [
8 | "Alexa, open better buttons trivia",
9 | "Alexa, start better buttons trivia",
10 | "Alexa, ask better buttons trivia to start a new game"
11 | ],
12 | "keywords": [
13 | "buttons",
14 | "games",
15 | "trivia"
16 | ],
17 | "name": "Buttons Trivia Template",
18 | "description": "This is a fast-paced, trivia-style game, made better with Echo Buttons. Challenge your friends, or play by yourself."
19 | },
20 | "en-GB": {
21 | "summary": "A trivia game featuring Echo Buttons for competitive play.",
22 | "examplePhrases": [
23 | "Alexa, open better buttons trivia",
24 | "Alexa, start better buttons trivia",
25 | "Alexa, ask better buttons trivia to start a new game"
26 | ],
27 | "keywords": [
28 | "buttons",
29 | "games",
30 | "trivia"
31 | ],
32 | "name": "Buttons Trivia Template",
33 | "description": "This is a fast-paced, trivia-style game, made better with Echo Buttons. Challenge your friends, or play by yourself."
34 | },
35 | "de-DE": {
36 | "summary": "Ein Trivia-Spiel, das mit Echo Buttons besser gemacht wird.",
37 | "examplePhrases": [
38 | "Alexa, öffnen besser buttons quiz",
39 | "Alexa, beginnen Sie besser buttons quiz",
40 | "Alexa, fragen Sie besser buttons quiz, um ein neues Spiel zu starten"
41 | ],
42 | "keywords": [
43 | "buttons",
44 | "Spiele",
45 | "Quiz"
46 | ],
47 | "name": "Buttons Quiz-Vorlage",
48 | "description": "Dies ist ein schnelllebig, Trivia-Stil Spiel, besser gemacht mit Echo Buttons. Fordern Sie Ihre Freunde heraus oder spielen Sie selbst."
49 | }
50 | },
51 | "isAvailableWorldwide": false,
52 | "category": "KNOWLEDGE_AND_TRIVIA",
53 | "distributionCountries": [
54 | "US"
55 | ],
56 | "gadgetSupport": {
57 | "maxGadgetButtons": 4,
58 | "minGadgetButtons": 1,
59 | "requirement": "OPTIONAL",
60 | "numPlayersMin": 1
61 | }
62 | },
63 | "apis": {
64 | "custom": {
65 | "interfaces": [
66 | {
67 | "type": "RENDER_TEMPLATE"
68 | },
69 | {
70 | "type": "GAME_ENGINE"
71 | },
72 | {
73 | "type": "GADGET_CONTROLLER"
74 | }
75 | ]
76 | }
77 | },
78 | "manifestVersion": "1.0"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------