├── .ask └── config ├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── hooks ├── post_new_hook.ps1 ├── post_new_hook.sh ├── pre_deploy_hook.ps1 └── pre_deploy_hook.sh ├── instructions ├── 1-voice-user-interface.md ├── 2-lambda-function.md ├── 3-connect-vui-to-code.md ├── 4-testing.md ├── 5-customization.md └── 6-publication.md ├── lambda └── py │ ├── lambda_function.py │ └── requirements.txt ├── models └── en-US.json └── skill.json /.ask/config: -------------------------------------------------------------------------------- 1 | { 2 | "deploy_settings": { 3 | "default": { 4 | "skill_id": "", 5 | "resources": { 6 | "lambda": [ 7 | { 8 | "alexaUsage": [ 9 | "custom/default" 10 | ], 11 | "handler": "lambda_function.lambda_handler", 12 | "runtime": "python3.6" 13 | } 14 | ] 15 | }, 16 | "was_cloned": true, 17 | "merge": {} 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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-labs/skill-sample-python-petmatch/issues), or [recently closed](https://github.com/alexa-labs/skill-sample-python-petmatch/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-labs/skill-sample-python-petmatch/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-labs/skill-sample-python-petmatch/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 | Skill Sample Python Petmatch 2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-on._TTH_.png)](./instructions/1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-off._TTH_.png)](./instructions/2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-off._TTH_.png)](./instructions/3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-off._TTH_.png)](./instructions/4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-off._TTH_.png)](./instructions/5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./instructions/6-publication.md) 5 | 6 | ## Overview 7 | 8 | In this workshop you will create a skill called Pet Match that matches the user 9 | with a pet. When launched, this Alexa Skill will prompt the user for the 10 | information it needs to determine a match. Once all of the required information 11 | is collected, it will send the data to an external web service which processes 12 | the data and returns the match. 13 | 14 | Through this workshop, you'll learn how to use advanced Alexa Skills Kit 15 | features to create and configure an Alexa Skill and AWS Lambda. The features you'll 16 | learn to use are Dialog Management and Entity Resolution. These features leverage 17 | Alexa's Automatic Speech Recognition (ASR), Natural Language Understanding (NLU), 18 | and Machine Learning (ML), which makes your life easier because you don't have 19 | to write code. You only need to provide training data to the Alexa engines via your 20 | interaction model. The skill builder makes it easy to do so. 21 | 22 | To **Get Started** click the button below: 23 | 24 | [![Get Started](https://camo.githubusercontent.com/db9b9ce26327ad3bac57ec4daf0961a382d75790/68747470733a2f2f6d2e6d656469612d616d617a6f6e2e636f6d2f696d616765732f472f30312f6d6f62696c652d617070732f6465782f616c6578612f616c6578612d736b696c6c732d6b69742f7475746f7269616c732f67656e6572616c2f627574746f6e732f627574746f6e5f6765745f737461727465642e5f5454485f2e706e67)](./instructions/1-voice-user-interface.md) 25 | 26 | ## Additional Resources 27 | 28 | ### Community 29 | * [Amazon Developer Forums](https://forums.developer.amazon.com/spaces/165/index.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 | 36 | ### Documentation 37 | * [Official Alexa Skills Kit Python SDK (Beta)](https://github.com/alexa-labs/alexa-skills-kit-sdk-for-python) - The Official Python SDK (Beta) Repository 38 | * [Official Alexa Skills Kit Python SDK (Beta) Documentation](https://alexa-skills-kit-python-sdk.readthedocs.io/en/latest/) - The Official Python SDK (Beta) 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 -------------------------------------------------------------------------------- /hooks/post_new_hook.ps1: -------------------------------------------------------------------------------- 1 | # Powershell script for ask-cli post-new hook for Python 2 | # Script Usage: post_new_hook.ps1 3 | 4 | # SKILL_NAME is the preformatted name passed from the CLI, after removing special characters. 5 | # DO_DEBUG is boolean value for debug logging 6 | 7 | # Run this script one level outside of the skill root folder 8 | 9 | # The script does the following: 10 | # - Create a '.venv' directory under folder 11 | # - Find if python3 is installed. 12 | # - If yes, try creating virtual environment using built-in venv 13 | # - If that fails, install virtualenv and create virtualenv 14 | # - If no, install virtualenv and create virtualenv 15 | # - If virtual environment is created, use container pip to install dependencies from ${SOURCE_DIR}/requirements.txt 16 | # - Provide message on activation script location and additional dependencies 17 | 18 | param( 19 | [string] $SKILL_NAME, 20 | [bool] $DO_DEBUG = $False 21 | ) 22 | 23 | if ($DO_DEBUG) { 24 | Write-Output "###########################" 25 | Write-Output "###### post-new hook ######" 26 | Write-Output "###########################" 27 | } 28 | 29 | function create_env () { 30 | # Check for Python3 installation 31 | python -V | Select-String -Pattern "Python 3." 2>&1 | Out-Null 32 | if ($?) { 33 | python -m venv $ENV_LOC 2>&1 | Out-Null 34 | if ($?) { 35 | return $true 36 | } 37 | } 38 | return create_using_virtualenv 39 | } 40 | 41 | function create_using_virtualenv() { 42 | # Check for virtualenv installation or install 43 | python -m pip install virtualenv 2>&1 | Out-Null 44 | if ($?) { 45 | python -m virtualenv $ENV_LOC 2>&1 | Out-Null 46 | if ($?) { 47 | return $true 48 | } 49 | } 50 | if ($DO_DEBUG) { 51 | Write-Output "There was a problem installing virtualenv" 52 | } 53 | return $false 54 | } 55 | 56 | function install_dependencies($PARAM_SOURCE_DIR) { 57 | # Install dependencies at lambda/py/requirements.txt 58 | $PYTHON_PATH = $ENV_LOC + "\Scripts\python" 59 | $REQUIREMENTS_PATH = $SKILL_NAME + "\" + $PARAM_SOURCE_DIR + "\requirements.txt" 60 | $CMD = "$PYTHON_PATH -m pip -q install -r $REQUIREMENTS_PATH" 61 | return Invoke-Expression $CMD 2>&1 | Out-Null 62 | } 63 | 64 | 65 | $SKILL_ENV_NAME = "skill_env" 66 | $ENV_LOC = $SKILL_NAME + "\.venv\" + $SKILL_ENV_NAME 67 | if (create_env) { 68 | $SKILL_FILE_PATH = $SKILL_NAME + "\skill.json" 69 | $ALL_SOURCE_DIRS = Get-Content -Path $SKILL_FILE_PATH | select-string -Pattern "sourceDir" -CaseSensitive 70 | if ($DO_DEBUG) { 71 | Write-Output "Created $SKILL_ENV_NAME virtualenv at $ENV_LOC" 72 | Write-Output "###########################" 73 | Write-Output "Installing dependencies based on sourceDir" 74 | } 75 | Foreach ($SOURCE_DIR in $ALL_SOURCE_DIRS) { 76 | $FILTER_SOURCE_DIR = $SOURCE_DIR -replace "`"", "" -replace "\s", "" -replace ",","" -replace "sourceDir:", "" 77 | if (-Not (install_dependencies $FILTER_SOURCE_DIR)) { 78 | if ($DO_DEBUG) { 79 | Write-Output "Codebase ($FILTER_SOURCE_DIR) built successfully." 80 | } 81 | } else { 82 | if ($DO_DEBUG) { 83 | Write-Output "There was a problem installing dependencies for ($FILTER_SOURCE_DIR)." 84 | } 85 | exit 1 86 | } 87 | } 88 | if ($DO_DEBUG) { 89 | Write-Output "###########################" 90 | Write-Output "Activate the environment before installing any other dependencies by running 'source $ENV_LOC/bin/activate'" 91 | } 92 | exit 0 93 | } else { 94 | exit 1 95 | } 96 | -------------------------------------------------------------------------------- /hooks/post_new_hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Shell script for ask-cli post-new hook for Python 3 | # Script Usage: post_new_hook.sh 4 | 5 | # SKILL_NAME is the preformatted name passed from the CLI, after removing special characters. 6 | # DO_DEBUG is boolean value for debug logging 7 | 8 | # Run this script one level outside of the skill root folder 9 | 10 | # The script does the following: 11 | # - Create a '.venv' directory under folder 12 | # - Find if python3 is installed. 13 | # - If yes, try creating virtual environment using built-in venv 14 | # - If that fails, install virtualenv and create virtualenv 15 | # - If no, install virtualenv and create virtualenv 16 | # - If virtual environment is created, use container pip to install dependencies from ${SOURCE_DIR}/requirements.txt 17 | # - Provide message on activation script location and additional dependencies 18 | 19 | create_env () { 20 | # Check for Python3 installation 21 | if command -v python3 &> /dev/null; then 22 | PYTHON=python3 23 | # Use Python3's venv script to create virtualenv. 24 | if $PYTHON -m venv "$ENV_LOC"; then 25 | echo "Using Python3's venv script" 26 | return 0 27 | else 28 | # No venv script present (< Py 3.3). Install using virtualenv 29 | create_using_virtualenv $PYTHON 30 | return $? 31 | fi 32 | else 33 | # Python2 environment. Install using virtualenv 34 | PYTHON=python 35 | create_using_virtualenv $PYTHON 36 | return $? 37 | fi 38 | return 1 39 | } 40 | 41 | create_using_virtualenv () { 42 | # Check for virtualenv installation or install 43 | if $1 -m pip install virtualenv; then 44 | echo "Using virtualenv library" 45 | # Try creating env 46 | if $1 -m virtualenv "$ENV_LOC"; then 47 | return 0 48 | else 49 | echo "There was a problem creating virtualenv" 50 | return 1 51 | fi 52 | else 53 | echo "There was a problem installing virtualenv" 54 | return 1 55 | fi 56 | } 57 | 58 | install_dependencies() { 59 | # Install dependencies at lambda/py/requirements.txt 60 | return $("$ENV_LOC"/bin/python -m pip -q install -r "$SKILL_DIR"/"$1"/requirements.txt) 61 | } 62 | 63 | SKILL_NAME=$1 64 | DO_DEBUG=${2:-false} 65 | SKILL_DIR=$SKILL_NAME 66 | SKILL_ENV_NAME="skill_env" 67 | ENV_LOC="$SKILL_DIR/.venv/$SKILL_ENV_NAME" 68 | 69 | if ! $DO_DEBUG ; then 70 | exec > /dev/null 2>&1 71 | fi 72 | 73 | echo "###########################" 74 | echo "###### post-new hook ######" 75 | echo "###########################" 76 | echo "Creating virtualenv for $SKILL_NAME" 77 | mkdir "$SKILL_NAME/.venv" 78 | if create_env; then 79 | echo "Created $SKILL_ENV_NAME virtualenv at $ENV_LOC" 80 | echo "###########################" 81 | echo "Installing dependencies based on sourceDir" 82 | grep "sourceDir" "$SKILL_NAME/skill.json" | cut -d: -f2 | sed 's/"//g' | sed 's/,//g' | while read -r SOURCE_DIR; do 83 | if install_dependencies $SOURCE_DIR; then 84 | echo "Codebase ($SOURCE_DIR) built successfully." 85 | else 86 | echo "There was a problem installing dependencies for ($SOURCE_DIR)." 87 | exit 1 88 | fi 89 | done 90 | echo "###########################" 91 | echo "Activate the environment before installing any other dependencies by running 'source $ENV_LOC/bin/activate'" 92 | exit 0 93 | else 94 | exit 1 95 | fi 96 | -------------------------------------------------------------------------------- /hooks/pre_deploy_hook.ps1: -------------------------------------------------------------------------------- 1 | # Powershell script for ask-cli pre-deploy hook for Python 2 | # Script Usage: pre_deploy_hook.ps1 3 | 4 | # SKILL_NAME is the preformatted name passed from the CLI, after removing special characters. 5 | # DO_DEBUG is boolean value for debug logging 6 | # TARGET is the deploy TARGET provided to the CLI. (eg: all, skill, lambda etc.) 7 | 8 | # Run this script under the skill root folder 9 | 10 | # The script does the following: 11 | # - Create a temporary 'lambda_upload' directories under each SOURCE_DIR folder 12 | # - Copy the contents of '/SOURCE_DIR' folder into '/SOURCE_DIR/lambda_upload' 13 | # - Copy the contents of site packages in $VIRTUALENV created in /.venv/ folder 14 | # - Update the location of this 'lambda_upload' folder to skill.json for zip and upload 15 | 16 | param( 17 | [string] $SKILL_NAME, 18 | [bool] $DO_DEBUG = $False, 19 | [string] $TARGET = "all" 20 | ) 21 | 22 | if ($DO_DEBUG) { 23 | Write-Output "###########################" 24 | Write-Output "##### pre-deploy hook #####" 25 | Write-Output "###########################" 26 | } 27 | 28 | if ($TARGET -eq "all" -Or $TARGET -eq "lambda") { 29 | $ALL_SOURCE_DIRS = Get-Content -Path "skill.json" | select-string -Pattern "sourceDir" -CaseSensitive 30 | Foreach ($SOURCE_DIR in $ALL_SOURCE_DIRS) { 31 | # Step 1: Decide source path and upload path 32 | $FILTER_SOURCE_DIR = $SOURCE_DIR -replace "`"", "" -replace "\s", "" -replace ",","" -replace "sourceDir:", "" 33 | if ($FILTER_SOURCE_DIR.endsWith("/lambda_upload")) { 34 | $UPLOAD_DIR_PATH = $FILTER_SOURCE_DIR 35 | $CODE_PATH = $FILTER_SOURCE_DIR.replace("/lambda_upload", "") 36 | } else { 37 | $UPLOAD_DIR_PATH = $FILTER_SOURCE_DIR + "/lambda_upload" 38 | $CODE_PATH = $FILTER_SOURCE_DIR 39 | } 40 | # Step 2: Create empty lambda_upload folder 41 | Remove-Item -Recurse -Force $UPLOAD_DIR_PATH -ErrorAction Ignore 42 | New-Item -Force $UPLOAD_DIR_PATH -ItemType "directory" 2>&1 | Out-Null 43 | 44 | # Step 3: Copy source code in sourceDir to lambda_upload 45 | $EXCLUDE_PATH = Resolve-Path -Path ((pwd).Path + "/" + $UPLOAD_DIR_PATH) 46 | robocopy $CODE_PATH $UPLOAD_DIR_PATH /s /e /ndl /XD $EXCLUDE_PATH 2>&1 | Out-Null 47 | 48 | # Step 4: Find virtual environment site packages, copy contents to lambda_upload 49 | $SITE = $(.venv\skill_env\Scripts\python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") 50 | Copy-Item "$SITE\*" -Destination $UPLOAD_DIR_PATH -Recurse 51 | 52 | # Step 5: Update the "manifest.apis.custom.endpoint.sourceDir" value in skill.json if necessary 53 | if (!$FILTER_SOURCE_DIR.endsWith("/lambda_upload")) { 54 | $RAW_SOURCE_DIR_LINE = "`"sourceDir`": `"$FILTER_SOURCE_DIR`"" 55 | $NEW_SOURCE_DIR_LINE = "`"sourceDir`": `"$UPLOAD_DIR_PATH`"" 56 | (Get-Content "skill.json").replace($RAW_SOURCE_DIR_LINE, $NEW_SOURCE_DIR_LINE) | Set-Content "skill.json" 57 | } 58 | } 59 | 60 | if ($DO_DEBUG) { 61 | Write-Output "###########################" 62 | } 63 | 64 | exit 0 65 | } 66 | -------------------------------------------------------------------------------- /hooks/pre_deploy_hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Shell script for ask-cli pre-deploy hook for Python 3 | # Script Usage: pre_deploy_hook.sh 4 | 5 | # SKILL_NAME is the preformatted name passed from the CLI, after removing special characters. 6 | # DO_DEBUG is boolean value for debug logging 7 | # TARGET is the deploy TARGET provided to the CLI. (eg: all, skill, lambda etc.) 8 | 9 | # Run this script under skill root folder 10 | 11 | # The script does the following: 12 | # - Create a temporary 'lambda_upload' directories under each SOURCE_DIR folder 13 | # - Copy the contents of '/SOURCE_DIR' folder into '/SOURCE_DIR/lambda_upload' 14 | # - Copy the contents of site packages in $VIRTUALENV created in /.venv/ folder 15 | # - Update the location of this 'lambda_upload' folder to skill.json for zip and upload 16 | 17 | SKILL_NAME=$1 18 | DO_DEBUG=${2:-false} 19 | TARGET=${3:-"all"} 20 | SKILL_ENV_NAME="skill_env" 21 | 22 | if ! $DO_DEBUG ; then 23 | exec > /dev/null 2>&1 24 | fi 25 | 26 | echo "###########################" 27 | echo "##### pre-deploy hook #####" 28 | echo "###########################" 29 | 30 | 31 | RET=0 32 | if [[ $TARGET == "all" || $TARGET == "lambda" ]]; then 33 | grep "sourceDir" ./skill.json | cut -d: -f2 | sed 's/"//g' | sed 's/,//g' | while read -r SOURCE_DIR; do 34 | # Step 1: Decide source path and upload path 35 | if [[ $SOURCE_DIR == */lambda_upload ]]; then 36 | ADJUSTED_SOURCE_DIR=${SOURCE_DIR%"/lambda_upload"} 37 | UPLOAD_DIR=$SOURCE_DIR 38 | else 39 | ADJUSTED_SOURCE_DIR=$SOURCE_DIR 40 | UPLOAD_DIR="$SOURCE_DIR/lambda_upload" 41 | fi 42 | 43 | # Step 2: Create empty lambda_upload folder 44 | echo "Checking for lambda_upload folder existence in sourceDir $ADJUSTED_SOURCE_DIR" 45 | rm -rf $UPLOAD_DIR 46 | mkdir $UPLOAD_DIR 47 | 48 | # Step 3: Copy source code in sourceDir to lambda_upload 49 | echo "Copying source code in $SKILL_NAME/$ADJUSTED_SOURCE_DIR folder to $SKILL_NAME/$UPLOAD_DIR" 50 | rsync -avzq --exclude '*lambda_upload' $ADJUSTED_SOURCE_DIR/* $UPLOAD_DIR 51 | 52 | # Step 4: Find virtual environment site packages, copy contents to lambda_upload 53 | echo "Copying dependencies installed in $SKILL_NAME/.venv/$SKILL_ENV_NAME to $SKILL_NAME/$UPLOAD_DIR" 54 | if [[ ! "$(ls -A .venv/$SKILL_ENV_NAME/bin/python)" ]]; then 55 | echo "Failed to get virtual env Python runtime" 56 | RET=1 57 | break; 58 | fi 59 | 60 | SITE=$(.venv/$SKILL_ENV_NAME/bin/python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())') 61 | if [[ "$(ls -A $SITE/*)" ]]; then 62 | cp -r $SITE/* $UPLOAD_DIR 63 | else 64 | echo "Failed to get the SITE package path" 65 | RET=1 66 | break; 67 | fi 68 | 69 | # Step 5: Update the "manifest.apis.custom.endpoint.sourceDir" value in skill.json if necessary 70 | if ! [[ $SOURCE_DIR == */lambda_upload ]]; then 71 | echo "Updating sourceDir to point to lambda_upload folder in skill.json" 72 | RAW_SOURCE_DIR_LINE="\"sourceDir\": \"$SOURCE_DIR\"" 73 | NEW_SOURCE_DIR_LINE="\"sourceDir\": \"$UPLOAD_DIR\"" 74 | sed -in "s#$RAW_SOURCE_DIR_LINE#$NEW_SOURCE_DIR_LINE#g" ./skill.json 75 | fi 76 | done 77 | echo "###########################" 78 | fi 79 | 80 | exit $RET 81 | -------------------------------------------------------------------------------- /instructions/1-voice-user-interface.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-on._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-off._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-off._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-off._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-off._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./6-publication.md) 5 | 6 | ## Setting up Your Alexa Skill in the Developer Console 7 | 8 | 1. **Go to the [Alexa Developer Console](http://developer.amazon.com/alexa?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=quiz-game-python-V2_GUI-1&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_quiz-game-python-V2_GUI-1_Convert_WW_beginnersdevs&sc_segment=beginnersdevs). In the top-right corner of the screen, click the "Sign In" button.** 9 | (If you don't already have an account, you will be able to create a new one for free.) 10 | 11 | 1. Once you have signed in, select the **Developer Console** link and then **Alexa Skills Kit**. 12 | 13 | 1. From the **Alexa Developer Console** select the **Create Skill** button near the top-right of the list of your Alexa Skills. 14 | 15 | 1. Give your new skill a **Name**, for example, 'Pet Match'. This is the name that will be shown in the Alexa Skills Store, and the name your users will refer to. 16 | 17 | 1. Select the Default Language. This tutorial will presume you have selected 'English (US)'. 18 | 19 | 1. Select the **Custom** model under the *'Choose a model to add to your skill'* section. Click the **Create Skill** button at the top right. 20 | 21 | 1. Choose **Start from scratch** from the *Choose a template* section and click the **Choose** button on the top right. 22 | 23 | 1. **Build the Interaction Model for your skill** 24 | 1. On the left hand navigation panel, select the **JSON Editor** tab under **Interaction Model**. In the textfield provided, replace any existing code with the code provided in the [Interaction Model](../models/en-US.json). Click **Save Model**. 25 | 2. If you want to change the skill invocation name, select the **Invocation** tab. Enter a **Skill Invocation Name**. This is the name that your users will need to say to start your skill. In this case, it's preconfigured to be 'pet match'. 26 | 3. Click "Build Model". 27 | 28 | **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?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=quiz-game-python-V2_GUI-1&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_quiz-game-python-V2_GUI-1_Convert_WW_beginnersdevs&sc_segment=beginnersdevs). 29 | 30 | 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. 31 | 32 | 8. 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). 33 | 34 | If you get an error from your interaction model, check through this list: 35 | 36 | * **Did you copy & paste the provided code correctly?** 37 | * **Did you accidentally add any characters to the Interaction Model?** 38 | 39 | [![Next](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/buttons/button_next_lambda_function._TTH_.png)](./2-lambda-function.md) 40 | -------------------------------------------------------------------------------- /instructions/2-lambda-function.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-locked._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-on._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-off._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-off._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-off._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./6-publication.md) 5 | 6 | ## Setting Up A Lambda Function Using Amazon Web Services 7 | 8 | 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 a 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 Lambda is where our code lives. When a user asks Alexa to use our skill, it is our Lambda function that interprets the appropriate interaction, and provides the conversation back to the user. 9 | 10 | 1. **Go to http://aws.amazon.com and sign in to the console.** If you don't already have an account, you will need to create one. [Check out this quick walkthrough for setting up a new AWS account](https://github.com/alexa/alexa-cookbook/blob/master/aws/set-up-aws.md). 11 | 12 | [![Sign In](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/2-1-sign-in-to-the-console._TTH_.png)](https://console.aws.amazon.com/console/home) 13 | 14 | 2. **Choose "Services" at the top of the screen, and type "Lambda" in the search box.** You can also find it in the list of services. It is in the "Compute" section. 15 | 16 | [![Lambda](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/2-2-services-lambda._TTH_.png)](https://console.aws.amazon.com/lambda/home) 17 | 18 | 3. **Check your AWS region.** Lambda only works with the Alexa Skills Kit in four regions: US East (N. Virginia), EU (Ireland), US West (Oregon) and Asia Pacific (Tokyo). Make sure you choose the region closest to your customers. 19 | 20 | ![Lambda Regions](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/2-3-check-region._TTH_.png) 21 | 22 | 4. **Click the "Create a Lambda function" button.** It should be near the top of your screen. 23 | 24 | ![Create Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/2-4-create-a-lambda-function._TTH_.png) 25 | 26 | 5. **Click on "Author from scratch".** We will configure our Lambda function next. 27 | 1. These values will only ever be visible to you, but make sure that you name your function something meaningful. "samplePythonPetMatch" is sufficient if you don't have another idea for a name. 28 | 29 | 2. From the "Runtime" dropdown select the python version your system supports. This tutorial and sample code works with either Python 2.7 or 3.6. To check the python version, try the following command in a terminal 30 | ``` 31 | $ python --version 32 | Python 2.7.10 33 | ``` 34 | 35 | 3. **Set up your Lambda function role.** If you haven't done this before, we have a [detailed walkthrough for setting up your first role for Lambda](https://github.com/alexa/alexa-cookbook/blob/master/guides/aws-security-and-setup/lambda-role.md). If you have done this before, you only need to select the **Existing role**. 36 | 37 | 4. Click **Create function**. 38 | 39 | 6. **Configure your trigger.** There are many different AWS services that can trigger a Lambda function, but for the purposes of this guide, we need to select "Alexa Skills Kit." from the left hand side. 40 | 41 | Once you have selected Alexa Skills Kit, scroll down and find the Skill ID verification section. Although you will want to paste your skill's ID in the Skill ID field, however for this tutorial, click Disable. Click the **Add** button in the lower right. Click the orange **Save** button in the top right corner. 42 | 43 | 7. **Finish configuring your function**. Click on your function's name (you'll find it in the middle) and scroll to the bottom of the page, you'll see a Cloud9 code editor. 44 | 45 | We have provided the code for this skill [here](../lambda/py). To properly upload this code to Lambda, you'll need to perform the following: 46 | 47 | 1. This skill uses the [ASK SDK for Python](https://github.com/alexa/alexa-skills-kit-sdk-for-python) for development. The skill code is provided in the [lambda_function.py](../lambda/py/lambda_function.py), and the dependencies are mentioned in [requirements.txt](../lambda/py/requirements.txt). Download the contents of the [lambda/py](../lambda/py) folder. 48 | 2. On your system, navigate to the lambda folder and install the dependencies in a new folder called “skill_env” using the following command: 49 | 50 | ``` 51 | pip install -r py/requirements.txt -t skill_env 52 | ``` 53 | 54 | 3. Copy the contents of the `lambda/py` folder into the `skill_env` folder. 55 | 56 | ``` 57 | cp -r py/* skill_env/ 58 | ``` 59 | 60 | 4. Zip the contents of the `skill_env` folder. Remember to zip the **contents** of the folder and **NOT** the folder itself. 61 | 5. On the AWS Lambda console, change the **code entry type** drop-down to **Upload a .ZIP file**, upload the zip created in the previous step and click on **Save**. 62 | 63 | *(Optional)* Follow the ASK Python SDK [Getting Started](https://alexa-skills-kit-python-sdk.readthedocs.io/en/latest/GETTING_STARTED.html#adding-the-ask-sdk-for-python-to-your-project) documentation, to check alternative ways of installing the sdk and deploying to AWS Lambda console. 64 | 65 | 8. (Optional) Click the **Configure test events** dropdown menu on the top of the page. 66 | 1. Select 'Alexa Start Session' from the 'Event Template' dropdown. 67 | 2. Type `LaunchRequest` into the 'Event Name' field. 68 | 3. Click the orange 'Create' button at the bottom of the page 69 | 4. Click the **Test** button at the top of the page. 70 | 1. You should see a light green box with the message: *Execution result: succeeded* at the top of the page. 71 | 72 | 9. **As a final step, copy the ARN value from the top right corner of the screen.** You will need this value in the next section of this guide. 73 | 74 | [![Next Step](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/buttons/button_next_connect_vui_to_code._TTH_.png)](3-connect-vui-to-code.md) 75 | -------------------------------------------------------------------------------- /instructions/3-connect-vui-to-code.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-locked._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-locked._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-on._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-off._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-off._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./6-publication.md) 5 | 6 | ## Connecting Your Voice User Interface To Your Lambda Function 7 | 8 | On [page #1](./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 [page #2](./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 | 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 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). 15 | 16 | 4. Paste your Lambda's ARN (Amazon Resource Name) into the textbox provided for **Default Region**. 17 | 18 | 5. Click the **Save Endpoints** button at the top of the main panel. 19 | 20 | 6. **Click the "Next" button to continue to page #4 of this guide.** 21 | 22 |

23 | 24 | -------------------------------------------------------------------------------- /instructions/4-testing.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-locked._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-locked._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-locked._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-on._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-off._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./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) and [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 | 3. Enable Testing by activating the **Test is enabled for this skill** slider. It should be underneath the top navigation menu. 15 | 16 | 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. 17 | 1. **Type** "Open" followed by the invocation name you gave your skill in [Step 1](./1-voice-user-interface.md). For example, "Open Pet Match". 18 | 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. 19 | 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. 20 | 21 | 5. Ensure your skill works the way that you designed it to. 22 | * 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. 23 | * 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, AWS Lambda offers an additional testing tool to help you troubleshoot your skill. 24 | 25 | 6. **Configure a test event in AWS Lambda.** Now that you are familiar with the **request** and **response** boxes in the Service Simulator, it's important for you to know that you can use your **requests** to directly test your Lambda function every time you update it. To do this: 26 | 1. Enter an utterance in the service simulator, and copy the generated Lambda Request for the next step. 27 | 28 | 2. **Open your Lambda function in AWS, open the Actions menu, and select "Configure test events."** 29 | 30 | 31 | 32 | 3. **Select "Create New Test Event". Choose "Alexa Start Session" as the Event Template from the dropdown list.** You can choose any test event in the list, as they are just templated event requests, but using "Alexa Start Session" is an easy one to remember. 33 | 34 | 35 | 36 | 4. **Type in an Event Name into the Event Name Dialog box. Delete the contents of the code editor, and paste the Lambda request you copied above into the code editor.** The Event Name is only visible to you. Name your test event something descriptive and memorable. For our example, we entered an event name as "startSession". Additionally, by copying and pasting your Lambda Request from the service simulator, you can test different utterances and skill events beyond the pre-populated templates in Lambda. 37 | 38 | 39 | 40 | 5. **Click the "Create" button.** This will save your test event and bring you back to the main configuration for your lambda function. 41 | 42 | 6. **Click the "Test" button to execute the "startSession" test event.** 43 | 44 | 45 | 46 | This gives you visibility into four things: 47 | 48 | * **Your response, listed in the "Execution Result."** 49 | 50 | 51 | 52 | * **A Summary of the statistics for your request.** This includes things like duration, resources, and memory used. 53 | 54 | 55 | 56 | * **Log output.** By effectively using print() or python logger statements in your Lambda code, you can track what is happening inside your function, and help to figure out what is happening when something goes wrong. You will find the log to be incredibly valuable as you move into more advanced skills. 57 | 58 | 59 | 60 | * **A link to your [CloudWatch](https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logs:) logs for this function.** This will show you **all** of the responses and log statements from every user interaction. This is very useful, especially when you are testing your skill from a device with your voice. (It is the "[Click here](https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logs:)" link in the Log Output description.) 61 | 62 | 7. **Other testing methods to consider:** 63 | 64 | * [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. 65 | * [Unit Testing with Alexa](https://github.com/alexa/alexa-cookbook/tree/master/testing/postman/README.md) - a modern approach to unit testing your Alexa skills with [Postman](http://getpostman.com) and [Amazon API Gateway](http://aws.amazon.com/apigateway). 66 | 67 | 8. **If your sample skill is working properly, you can now customize your skill.** 68 | 69 | 70 | -------------------------------------------------------------------------------- /instructions/5-customization.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-locked._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-locked._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-locked._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-locked._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-on._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-off._TTH_.png)](./6-publication.md) 5 | 6 | ## Customize the Skill to be Yours 7 | 8 | At this point, you should have a working copy of our Pet Match 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 sentences to respond to your users.** There are several sentences and responses that you will want to customize for your skill. 11 | 12 | 1. **Open a copy of ``lambda_function.py``.** If you haven't already downloaded the code for this project, [you can find a copy of lambda_function.py here](../lambda/py/lambda_function.py). 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. **Look for lines like this: speech = ('Welcome to pet match...'** These are strings that hold phrases for Alexa to respond with. Customize them to make it as varied and conversational as time allows. 15 | 16 | 3. **Continue through ``lambda_function.py`` until you reach the bottom of the file.** This will ensure that you cover each of the Alexa responses that you need to update. 17 | 18 | 4. **When you have replaced the data in `lambda_function.py`, you need to upload the latest data into Lambda.** Copy the updated contents into the ``skill_env`` folder, zip the contents of the ``skill_env`` folder and upload it to AWS Lambda as discussed in the "**Finish configuring your function**" step in [Lambda setup documentation](./2-lambda-function.md). Test your skill through the Alexa Simulator on the developer portal, with the updated changes. 19 | 20 | 2. **New API.** The sample skill uses a custom API to get pet matches. Update it with your API, for better customization and handling. 21 | 22 | 1. **Go back to your copy of [lambda_function.py](../lambda/py/lambda_function.py).** 23 | 24 | 2. **Look for pet_match_api** This is the API used to get pet matches. Change it according to your API call. 25 | 26 | 3. **When you have replaced the api details in `lambda_function.py`, you need to upload the latest data into Lambda.** Copy the updated contents into the ``skill_env`` folder, zip the contents of the ``skill_env`` folder and upload it to AWS Lambda as discussed in the "**Finish configuring your function**" step in [Lambda setup documentation](./2-lambda-function.md). Test your skill through the Alexa Simulator on the developer portal, with the updated changes. 27 | 28 | 3. **New language.** If you are creating this skill for another language other than English, you will need to make sure Alexa's responses are also in that language. 29 | 30 | * 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. 31 | 32 | 4. **Once you have made the updates listed on this page, you can click "Launch" in the top navigation of Alexa developer console, to move on to Publishing and Certification of your skill.** 33 | 34 | 35 | [![Next](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/buttons/button_next_publication._TTH_.png)](6-publication.md) 36 | -------------------------------------------------------------------------------- /instructions/6-publication.md: -------------------------------------------------------------------------------- 1 | # Build An Alexa Pet Match Skill in Python using ASK Python SDK 2 | 3 | 4 | [![Voice User Interface](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/1-locked._TTH_.png)](./1-voice-user-interface.md)[![Lambda Function](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/2-locked._TTH_.png)](./2-lambda-function.md)[![Connect VUI to Code](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/3-locked._TTH_.png)](./3-connect-vui-to-code.md)[![Testing](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/4-locked._TTH_.png)](./4-testing.md)[![Customization](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/5-locked._TTH_.png)](./5-customization.md)[![Publication](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/navigation/6-on._TTH_.png)](./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 | 1. Select the **Launch** link from the top navigation menu. 10 | 11 | 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!** 12 | * Take the time to get these right so that your skill will pass certification! 13 | 14 | 3. **Write your skill descriptions.** 15 | 16 | * **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). 17 | 18 | 4. For your example phrases, **come up with the three most exciting ways** a user can talk to your skill. 19 | 20 | * 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: 21 | 22 | | Common Failure Points for Example Phrases | 23 | | ----------------------------------------- | 24 | | 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?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=petmatch-nodejs-V2_GUI-6&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_petmatch-nodejs-V2_GUI-6_Convert_WW_beginnersdevs&sc_segment=beginnersdevs). | 25 | | Example phrases **must** be based on sample utterances specified in your Intent Schema. | 26 | | Your first example phrase **must** include a wake word and your invocation name. | 27 | | Example phrases **must** provide a contextual response. | 28 | 29 | * **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. 30 | 31 | 5. **Create your skill's icons.** You need two sizes of your icon: 108x108px and 512x512px. 32 | 33 | * **Make sure you have the rights to the icons you create.** Please don't violate any trademarks or copyrights. 34 | * **You can use the Alexa skill icon builder [tool](https://developer.amazon.com/docs/tools/icon-builder.html).** 35 | * **If you don't have software to make icons, try one of these free options:** 36 | 37 | * [GIMP](https://www.gimp.org/) (Windows/Mac/Linux) 38 | * [Canva](https://www.canva.com/) (Web) 39 | * [Paint.NET](http://www.getpaint.net/index.html) (Windows) 40 | * [Inkscape](http://inkscape.org) (Windows/Mac/Linux) 41 | * [Iconion](http://iconion.com/) (Windows/Mac) 42 | 43 | * To make it easier to get started, we've created blank versions of these icons in both sizes for many formats: 44 | 45 | * [PSD](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/psd._TTH_.zip) 46 | * [PNG](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/png._TTH_.zip) 47 | * [GIF](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/gif._TTH_.zip) 48 | * [PDF](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/pdf._TTH_.zip) 49 | * [JPG](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/jpg._TTH_.zip) 50 | * [SVG](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/svg._TTH_.zip) 51 | * [PDN](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/pdn._TTH_.zip) - for [Paint.NET](http://www.getpaint.net/index.html) 52 | * [XCF](https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/general/icon-templates/xcf._TTH_.zip) - for [GIMP](https://www.gimp.org/) 53 | 54 | 6. Choose the most appropriate category for your skill. 55 | 56 | 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. 57 | 58 | 8. **Privacy Policy URL.** This is an optional field, and should not be required for this Pet Match skill sample. You can leave it blank. 59 | 60 | 9. **Terms of Use URL.** This is also optional, and you can leave it blank. 61 | 62 | 10. When you're ready, click **Save and Continue** at the bottom of the screen to move onto **Privacy & Compliance** 63 | 64 | 11. * **Does this skill allow users to make purchases or spend real money?** For this pet match skill, the answer is no. For future skills, make sure you answer this appropriately. 65 | 66 | * **Does this Alexa skill collect users' personal information?** Again, for this pet match 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. 67 | * Answering "yes" to this question will also require you to provide a link to your Privacy Policy at the bottom of the page. 68 | 69 | * **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 pet match skill, the answer is **no** because it doesn't target a specific age group. 70 | * Factors to consider in determining if this skill is directed to children under 13 include: 71 | * Subject matter of the skill 72 | * Presence of child-oriented activities and incentives 73 | * Type of language used in the skill 74 | * Music and other audio content in the skill 75 | * How the skill is described and marketed 76 | * Intended audience for the skill 77 | 78 | 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. 79 | 80 | 12. **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. 81 | 82 | 13. **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. 83 | 84 | * Since you are using our Sample, make sure to add a sentence to your Testing Instructions referencing the Sample you used. For example: 85 | 86 | ``` 87 | This was built using the Pet Match Sample. 88 | ``` 89 | 90 | This will let the testing team understand what you're providing them, and should decrease the testing time required. 91 | 92 | **Note:** More details on certification are [available here.](https://alexa.design/certification) 93 | 94 | 14. If you feel that your skill is ready for certification, click the **Save and Continue** button at the bottom of the page. 95 | 96 | 97 | 15. **You're done with your submission!** Here are a few things you might need to know: 98 | 99 | * **Certification can take several days to complete.** Please be patient. It takes time because we want to get it right. 100 | 101 | * **Did something go wrong?** Our team of evangelists run [online office hours every Tuesday from 1-2pm Pacific Time](https://register.gotowebinar.com/rt/8924773651242797059). They can help answer any questions you might have. 102 | 103 | * **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?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=petmatch-nodejs-V2_GUI-6&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_petmatch-nodejs-V2_GUI-6_Convert_WW_beginnersdevs&sc_segment=beginnersdevs), [here for the UK](https://developer.amazon.com/en-gb/alexa-skills-kit/alexa-developer-skill-promotion?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=petmatch-nodejs-V2_GUI-6&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_petmatch-nodejs-V2_GUI-6_Convert_WW_beginnersdevs&sc_segment=beginnersdevs), and [here for Germany](https://developer.amazon.com/de-de/alexa-skills-kit/alexa-developer-skill-promotion?&sc_category=Owned&sc_channel=RD&sc_campaign=Evangelism2018&sc_publisher=github&sc_content=Survey&sc_detail=petmatch-nodejs-V2_GUI-6&sc_funnel=Convert&sc_country=WW&sc_medium=Owned_RD_Evangelism2018_github_Survey_petmatch-nodejs-V2_GUI-6_Convert_WW_beginnersdevs&sc_segment=beginnersdevs). 104 | -------------------------------------------------------------------------------- /lambda/py/lambda_function.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | import requests 5 | import six 6 | import random 7 | 8 | from ask_sdk_core.skill_builder import SkillBuilder 9 | from ask_sdk_core.handler_input import HandlerInput 10 | from ask_sdk_core.dispatch_components import ( 11 | AbstractRequestHandler, AbstractExceptionHandler, 12 | AbstractResponseInterceptor, AbstractRequestInterceptor) 13 | from ask_sdk_core.utils import is_intent_name, is_request_type 14 | 15 | from typing import Union, Dict, Any, List 16 | from ask_sdk_model.dialog import ( 17 | ElicitSlotDirective, DelegateDirective) 18 | from ask_sdk_model import ( 19 | Response, IntentRequest, DialogState, SlotConfirmationStatus, Slot) 20 | from ask_sdk_model.slu.entityresolution import StatusCode 21 | 22 | logger = logging.getLogger(__name__) 23 | logger.setLevel(logging.INFO) 24 | 25 | 26 | # Request Handler classes 27 | class LaunchRequestHandler(AbstractRequestHandler): 28 | """Handler for skill launch.""" 29 | def can_handle(self, handler_input): 30 | # type: (HandlerInput) -> bool 31 | return is_request_type("LaunchRequest")(handler_input) 32 | 33 | def handle(self, handler_input): 34 | # type: (HandlerInput) -> Response 35 | logger.info("In LaunchRequestHandler") 36 | speech = ('Welcome to pet match. I can help you find the best dog ' 37 | 'for you. What are two things you are looking for in a ' 38 | 'dog?') 39 | reprompt = "What size and temperament are you looking for in a dog?" 40 | handler_input.response_builder.speak(speech).ask(reprompt) 41 | return handler_input.response_builder.response 42 | 43 | 44 | class MythicalCreaturesHandler(AbstractRequestHandler): 45 | """Handler for MythicalCreatures.""" 46 | def can_handle(self, handler_input): 47 | # type: (HandlerInput) -> bool 48 | if not is_intent_name("PetMatchIntent")(handler_input): 49 | return False 50 | 51 | is_mythical_creature = False 52 | resolved_value = get_resolved_value( 53 | handler_input.request_envelope.request, "pet") 54 | if (resolved_value is not None and 55 | resolved_value == "mythical_creatures"): 56 | is_mythical_creature = True 57 | handler_input.attributes_manager.session_attributes["mythical_creature"] = handler_input.request_envelope.request.intent.slots["pet"].value 58 | return is_mythical_creature 59 | 60 | def handle(self, handler_input): 61 | # type: (HandlerInput) -> Response 62 | logger.info("In MythicalCreaturesHandler") 63 | session_attr = handler_input.attributes_manager.session_attributes 64 | speech = random_phrase(slots_meta["pet"]["invalid_responses"]).format( 65 | session_attr["mythical_creature"]) 66 | 67 | return handler_input.response_builder.speak(speech).response 68 | 69 | 70 | class InProgressPetMatchIntent(AbstractRequestHandler): 71 | def can_handle(self, handler_input): 72 | # type: (HandlerInput) -> bool 73 | return (is_intent_name("PetMatchIntent")(handler_input) 74 | and handler_input.request_envelope.request.dialog_state != DialogState.COMPLETED) 75 | 76 | def handle(self, handler_input): 77 | # type: (HandlerInput) -> Response 78 | logger.info("In InProgressPetMatchIntent") 79 | current_intent = handler_input.request_envelope.request.intent 80 | prompt = "" 81 | 82 | for slot_name, current_slot in six.iteritems( 83 | current_intent.slots): 84 | if slot_name not in ["article", "at_the", "I_Want"]: 85 | if (current_slot.confirmation_status != SlotConfirmationStatus.CONFIRMED 86 | and current_slot.resolutions 87 | and current_slot.resolutions.resolutions_per_authority[0]): 88 | if current_slot.resolutions.resolutions_per_authority[0].status.code == StatusCode.ER_SUCCESS_MATCH: 89 | if len(current_slot.resolutions.resolutions_per_authority[0].values) > 1: 90 | prompt = "Which would you like " 91 | 92 | values = " or ".join([e.value.name for e in current_slot.resolutions.resolutions_per_authority[0].values]) 93 | prompt += values + " ?" 94 | return handler_input.response_builder.speak( 95 | prompt).ask(prompt).add_directive( 96 | ElicitSlotDirective(slot_to_elicit=current_slot.name) 97 | ).response 98 | elif current_slot.resolutions.resolutions_per_authority[0].status.code == StatusCode.ER_SUCCESS_NO_MATCH: 99 | if current_slot.name in required_slots: 100 | prompt = "What {} are you looking for?".format(current_slot.name) 101 | 102 | return handler_input.response_builder.speak( 103 | prompt).ask(prompt).add_directive( 104 | ElicitSlotDirective( 105 | slot_to_elicit=current_slot.name 106 | )).response 107 | 108 | return handler_input.response_builder.add_directive( 109 | DelegateDirective( 110 | updated_intent=current_intent 111 | )).response 112 | 113 | 114 | class CompletedPetMatchIntent(AbstractRequestHandler): 115 | def can_handle(self, handler_input): 116 | # type: (HandlerInput) -> bool 117 | return (is_intent_name("PetMatchIntent")(handler_input) 118 | and handler_input.request_envelope.request.dialog_state == DialogState.COMPLETED) 119 | 120 | def handle(self, handler_input): 121 | # type: (HandlerInput) -> Response 122 | logger.info("In CompletedPetMatchIntent") 123 | filled_slots = handler_input.request_envelope.request.intent.slots 124 | slot_values = get_slot_values(filled_slots) 125 | pet_match_options = build_pet_match_options( 126 | host_name=pet_match_api["host_name"], path=pet_match_api["pets"], 127 | port=pet_match_api["port"], slot_values=slot_values) 128 | 129 | try: 130 | response = http_get(pet_match_options) 131 | 132 | if response["result"]: 133 | speech = ("So a {} " 134 | "{} " 135 | "{} " 136 | "energy dog sounds good for you. Consider a " 137 | "{}".format( 138 | slot_values["size"]["resolved"], 139 | slot_values["temperament"]["resolved"], 140 | slot_values["energy"]["resolved"], 141 | response["result"][0]["breed"]) 142 | ) 143 | else: 144 | speech = ("I am sorry I could not find a match for a " 145 | "{} " 146 | "{} " 147 | "{} energy dog".format( 148 | slot_values["size"]["resolved"], 149 | slot_values["temperament"]["resolved"], 150 | slot_values["energy"]["resolved"]) 151 | ) 152 | except Exception as e: 153 | speech = ("I am really sorry. I am unable to access part of my " 154 | "memory. Please try again later") 155 | logger.info("Intent: {}: message: {}".format( 156 | handler_input.request_envelope.request.intent.name, str(e))) 157 | 158 | return handler_input.response_builder.speak(speech).response 159 | 160 | 161 | class FallbackIntentHandler(AbstractRequestHandler): 162 | """Handler for handling fallback intent. 163 | 164 | 2018-May-01: AMAZON.FallackIntent is only currently available in 165 | en-US locale. This handler will not be triggered except in that 166 | locale, so it can be safely deployed for any locale.""" 167 | def can_handle(self, handler_input): 168 | # type: (HandlerInput) -> bool 169 | return is_intent_name("AMAZON.FallbackIntent")(handler_input) 170 | 171 | def handle(self, handler_input): 172 | # type: (HandlerInput) -> Response 173 | logger.info("In FallbackIntentHandler") 174 | speech = ("I'm sorry Pet Match can't help you with that. I can help " 175 | "find the perfect dog for you. What are two things you're " 176 | "looking for in a dog?") 177 | reprompt = "What size and temperament are you looking for in a dog?" 178 | handler_input.response_builder.speak(speech).ask(reprompt) 179 | return handler_input.response_builder.response 180 | 181 | 182 | class HelpIntentHandler(AbstractRequestHandler): 183 | """Handler for help intent.""" 184 | def can_handle(self, handler_input): 185 | # type: (HandlerInput) -> bool 186 | return is_intent_name("AMAZON.HelpIntent")(handler_input) 187 | 188 | def handle(self, handler_input): 189 | # type: (HandlerInput) -> Response 190 | logger.info("In HelpIntentHandler") 191 | speech = ("This is pet match. I can help you find the perfect pet " 192 | "for you. You can say, I want a dog.") 193 | reprompt = "What size and temperament are you looking for in a dog?" 194 | 195 | handler_input.response_builder.speak(speech).ask(reprompt) 196 | return handler_input.response_builder.response 197 | 198 | 199 | class ExitIntentHandler(AbstractRequestHandler): 200 | """Single Handler for Cancel, Stop and Pause intents.""" 201 | def can_handle(self, handler_input): 202 | # type: (HandlerInput) -> bool 203 | return (is_intent_name("AMAZON.CancelIntent")(handler_input) or 204 | is_intent_name("AMAZON.StopIntent")(handler_input)) 205 | 206 | def handle(self, handler_input): 207 | # type: (HandlerInput) -> Response 208 | logger.info("In ExitIntentHandler") 209 | handler_input.response_builder.speak("Bye").set_should_end_session( 210 | True) 211 | return handler_input.response_builder.response 212 | 213 | 214 | class SessionEndedRequestHandler(AbstractRequestHandler): 215 | """Handler for skill session end.""" 216 | def can_handle(self, handler_input): 217 | # type: (HandlerInput) -> bool 218 | return is_request_type("SessionEndedRequest")(handler_input) 219 | 220 | def handle(self, handler_input): 221 | # type: (HandlerInput) -> Response 222 | logger.info("In SessionEndedRequestHandler") 223 | logger.info("Session ended with reason: {}".format( 224 | handler_input.request_envelope.request.reason)) 225 | return handler_input.response_builder.response 226 | 227 | # Exception Handler classes 228 | class CatchAllExceptionHandler(AbstractExceptionHandler): 229 | """Catch All Exception handler. 230 | 231 | This handler catches all kinds of exceptions and prints 232 | the stack trace on AWS Cloudwatch with the request envelope.""" 233 | def can_handle(self, handler_input, exception): 234 | # type: (HandlerInput, Exception) -> bool 235 | return True 236 | 237 | def handle(self, handler_input, exception): 238 | # type: (HandlerInput, Exception) -> Response 239 | logger.error(exception, exc_info=True) 240 | 241 | speech = "Sorry, I can't understand the command. Please say again." 242 | handler_input.response_builder.speak(speech).ask(speech) 243 | return handler_input.response_builder.response 244 | 245 | 246 | # Request and Response Loggers 247 | class RequestLogger(AbstractRequestInterceptor): 248 | """Log the request envelope.""" 249 | def process(self, handler_input): 250 | # type: (HandlerInput) -> None 251 | logger.info("Request Envelope: {}".format( 252 | handler_input.request_envelope)) 253 | 254 | 255 | class ResponseLogger(AbstractResponseInterceptor): 256 | """Log the response envelope.""" 257 | def process(self, handler_input, response): 258 | # type: (HandlerInput, Response) -> None 259 | logger.info("Response: {}".format(response)) 260 | 261 | 262 | # Data 263 | required_slots = ["energy", "size", "temperament"] 264 | 265 | slots_meta = { 266 | "pet": { 267 | "invalid_responses": [ 268 | "I'm sorry, but I'm not qualified to match you with {}s.", 269 | "Ah yes, {}s are splendid creatures, but unfortunately owning one as a pet is outlawed.", 270 | "I'm sorry I can't match you with {}s." 271 | ] 272 | }, 273 | "error_default": "I'm sorry I can't match you with {}s." 274 | } 275 | 276 | pet_match_api = { 277 | "host_name": "e4v7rdwl7l.execute-api.us-east-1.amazonaws.com", 278 | "pets": "/Test", 279 | "port": 443 280 | } 281 | 282 | 283 | # Utility functions 284 | def get_resolved_value(request, slot_name): 285 | """Resolve the slot name from the request using resolutions.""" 286 | # type: (IntentRequest, str) -> Union[str, None] 287 | try: 288 | return (request.intent.slots[slot_name].resolutions. 289 | resolutions_per_authority[0].values[0].value.name) 290 | except (AttributeError, ValueError, KeyError, IndexError, TypeError) as e: 291 | logger.info("Couldn't resolve {} for request: {}".format(slot_name, request)) 292 | logger.info(str(e)) 293 | return None 294 | 295 | def get_slot_values(filled_slots): 296 | """Return slot values with additional info.""" 297 | # type: (Dict[str, Slot]) -> Dict[str, Any] 298 | slot_values = {} 299 | logger.info("Filled slots: {}".format(filled_slots)) 300 | 301 | for key, slot_item in six.iteritems(filled_slots): 302 | name = slot_item.name 303 | try: 304 | status_code = slot_item.resolutions.resolutions_per_authority[0].status.code 305 | 306 | if status_code == StatusCode.ER_SUCCESS_MATCH: 307 | slot_values[name] = { 308 | "synonym": slot_item.value, 309 | "resolved": slot_item.resolutions.resolutions_per_authority[0].values[0].value.name, 310 | "is_validated": True, 311 | } 312 | elif status_code == StatusCode.ER_SUCCESS_NO_MATCH: 313 | slot_values[name] = { 314 | "synonym": slot_item.value, 315 | "resolved": slot_item.value, 316 | "is_validated": False, 317 | } 318 | else: 319 | pass 320 | except (AttributeError, ValueError, KeyError, IndexError, TypeError) as e: 321 | logger.info("Couldn't resolve status_code for slot item: {}".format(slot_item)) 322 | logger.info(e) 323 | slot_values[name] = { 324 | "synonym": slot_item.value, 325 | "resolved": slot_item.value, 326 | "is_validated": False, 327 | } 328 | return slot_values 329 | 330 | def random_phrase(str_list): 331 | """Return random element from list.""" 332 | # type: List[str] -> str 333 | return random.choice(str_list) 334 | 335 | def build_pet_match_options(host_name, path, port, slot_values): 336 | """Return options for HTTP Get call.""" 337 | # type: (str, str, int, Dict[str, Any]) -> Dict 338 | path_params = { 339 | "SSET": "canine-{}-{}-{}".format( 340 | slot_values["energy"]["resolved"], 341 | slot_values["size"]["resolved"], 342 | slot_values["temperament"]["resolved"]) 343 | } 344 | if host_name[:4] != "http": 345 | host_name = "https://{}".format(host_name) 346 | url = "{}:{}{}".format(host_name, str(port), path) 347 | return { 348 | "url": url, 349 | "path_params": path_params 350 | } 351 | 352 | def http_get(http_options): 353 | url = http_options["url"] 354 | params = http_options["path_params"] 355 | response = requests.get(url=url, params=params) 356 | 357 | if response.status_code < 200 or response.status_code >= 300: 358 | response.raise_for_status() 359 | 360 | return response.json() 361 | 362 | 363 | # Skill Builder object 364 | sb = SkillBuilder() 365 | 366 | # Add all request handlers to the skill. 367 | sb.add_request_handler(LaunchRequestHandler()) 368 | sb.add_request_handler(MythicalCreaturesHandler()) 369 | sb.add_request_handler(InProgressPetMatchIntent()) 370 | sb.add_request_handler(CompletedPetMatchIntent()) 371 | sb.add_request_handler(HelpIntentHandler()) 372 | sb.add_request_handler(FallbackIntentHandler()) 373 | sb.add_request_handler(ExitIntentHandler()) 374 | sb.add_request_handler(SessionEndedRequestHandler()) 375 | 376 | # Add exception handler to the skill. 377 | sb.add_exception_handler(CatchAllExceptionHandler()) 378 | 379 | # Add response interceptor to the skill. 380 | sb.add_global_request_interceptor(RequestLogger()) 381 | sb.add_global_response_interceptor(ResponseLogger()) 382 | 383 | # Expose the lambda handler to register in AWS Lambda. 384 | lambda_handler = sb.lambda_handler() 385 | -------------------------------------------------------------------------------- /lambda/py/requirements.txt: -------------------------------------------------------------------------------- 1 | ask-sdk-core 2 | -------------------------------------------------------------------------------- /models/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactionModel": { 3 | "languageModel": { 4 | "invocationName": "pet match", 5 | "intents": [ 6 | { 7 | "name": "AMAZON.CancelIntent", 8 | "samples": [] 9 | }, 10 | { 11 | "name": "AMAZON.HelpIntent", 12 | "samples": [] 13 | }, 14 | { 15 | "name": "AMAZON.StopIntent", 16 | "samples": [] 17 | }, 18 | { 19 | "name": "PetMatchIntent", 20 | "slots": [ 21 | { 22 | "name": "pet", 23 | "type": "AMAZON.Animal" 24 | }, 25 | { 26 | "name": "size", 27 | "type": "sizeType", 28 | "samples": [ 29 | "{I_Want} {article} {size} {pet}", 30 | "{I_Want} {size}", 31 | "the {size}", 32 | "Something i can {size}", 33 | "{size} size", 34 | "{I_Want} {article} {size} {pet} that {energy}", 35 | "{I_Want} {article} {size} {temperament} {pet}", 36 | "{I_Want} {article} {size} {temperament} to {energy}", 37 | " {temperament} {pet}", 38 | "{energy} energy", 39 | "{size}" 40 | ] 41 | }, 42 | { 43 | "name": "temperament", 44 | "type": "temperamentType", 45 | "samples": [ 46 | "{temperament} {pet}" 47 | ] 48 | }, 49 | { 50 | "name": "I_Want", 51 | "type": "IWantType" 52 | }, 53 | { 54 | "name": "energy", 55 | "type": "energyType", 56 | "samples": [ 57 | "{energy} energy", 58 | "{I_Want} to {energy}" 59 | ] 60 | }, 61 | { 62 | "name": "article", 63 | "type": "articleType" 64 | }, 65 | { 66 | "name": "at_the", 67 | "type": "atTheType" 68 | } 69 | ], 70 | "samples": [ 71 | "{I_Want} {article} {pet}", 72 | "{I_Want} {article} {size} {temperament} dog", 73 | "find a pet", 74 | "match me", 75 | "{size} {pet}", 76 | "for a {pet}", 77 | "{I_Want} {article} {pet} that is {temperament} and {energy}", 78 | "{energy} {pet}", 79 | "{I_Want} {article} {energy} {pet}", 80 | "{I_Want} {article} {temperament} dog that is {energy} energy" 81 | ] 82 | }, 83 | { 84 | "name": "AMAZON.FallbackIntent", 85 | "samples": [] 86 | } 87 | ], 88 | "types": [ 89 | { 90 | "name": "articleType", 91 | "values": [ 92 | { 93 | "name": { 94 | "value": "a" 95 | } 96 | }, 97 | { 98 | "name": { 99 | "value": "an" 100 | } 101 | }, 102 | { 103 | "name": { 104 | "value": "the" 105 | } 106 | } 107 | ] 108 | }, 109 | { 110 | "name": "atTheType", 111 | "values": [ 112 | { 113 | "name": { 114 | "value": "at the" 115 | } 116 | }, 117 | { 118 | "name": { 119 | "value": "on the" 120 | } 121 | }, 122 | { 123 | "name": { 124 | "value": "around the" 125 | } 126 | }, 127 | { 128 | "name": { 129 | "value": "in the" 130 | } 131 | } 132 | ] 133 | }, 134 | { 135 | "name": "energyType", 136 | "values": [ 137 | { 138 | "name": { 139 | "value": "low", 140 | "synonyms": [ 141 | "low energy", 142 | "to cuddle with", 143 | "to watch netflix with", 144 | "lazy" 145 | ] 146 | } 147 | }, 148 | { 149 | "name": { 150 | "value": "medium", 151 | "synonyms": [ 152 | "medium energy", 153 | "for my apartment", 154 | "fun to play with", 155 | "tug of war", 156 | "plays tug of war" 157 | ] 158 | } 159 | }, 160 | { 161 | "name": { 162 | "value": "high", 163 | "synonyms": [ 164 | "that I can run with", 165 | "play frisbee", 166 | "run", 167 | "hike", 168 | "go hiking", 169 | "play fetch", 170 | "energetic", 171 | "high energy" 172 | ] 173 | } 174 | } 175 | ] 176 | }, 177 | { 178 | "name": "IWantType", 179 | "values": [ 180 | { 181 | "name": { 182 | "value": "my favorite is" 183 | } 184 | }, 185 | { 186 | "name": { 187 | "value": "I prefer" 188 | } 189 | }, 190 | { 191 | "name": { 192 | "value": "let's find" 193 | } 194 | }, 195 | { 196 | "name": { 197 | "value": "we like" 198 | } 199 | }, 200 | { 201 | "name": { 202 | "value": "we would like" 203 | } 204 | }, 205 | { 206 | "name": { 207 | "value": "how about" 208 | } 209 | }, 210 | { 211 | "name": { 212 | "value": "I want" 213 | } 214 | }, 215 | { 216 | "name": { 217 | "value": "I like" 218 | } 219 | } 220 | ] 221 | }, 222 | { 223 | "name": "sizeType", 224 | "values": [ 225 | { 226 | "name": { 227 | "value": "large", 228 | "synonyms": [ 229 | "large size", 230 | "huge", 231 | "truck", 232 | "gigantic", 233 | "eat me out of house", 234 | "scary big", 235 | "ginormous", 236 | "ride", 237 | "waist height" 238 | ] 239 | } 240 | }, 241 | { 242 | "name": { 243 | "value": "medium", 244 | "synonyms": [ 245 | "medium size", 246 | "bigger than a cat", 247 | "on the bed", 248 | "up to my knees", 249 | "average" 250 | ] 251 | } 252 | }, 253 | { 254 | "name": { 255 | "value": "small", 256 | "synonyms": [ 257 | "small size", 258 | "little", 259 | "take on an airplane" 260 | ] 261 | } 262 | }, 263 | { 264 | "name": { 265 | "value": "tiny", 266 | "synonyms": [ 267 | "tiny size", 268 | "cheap to feed", 269 | "teacup", 270 | "pocket", 271 | "yippy", 272 | "carry in my purse", 273 | "put in my pocket", 274 | "itty bitty" 275 | ] 276 | } 277 | } 278 | ] 279 | }, 280 | { 281 | "name": "temperamentType", 282 | "values": [ 283 | { 284 | "name": { 285 | "value": "watch", 286 | "synonyms": [ 287 | "adult", 288 | "barks at people" 289 | ] 290 | } 291 | }, 292 | { 293 | "name": { 294 | "value": "guard", 295 | "synonyms": [ 296 | "to protect me", 297 | "protective" 298 | ] 299 | } 300 | }, 301 | { 302 | "name": { 303 | "value": "family", 304 | "synonyms": [ 305 | "hang out", 306 | "hang out with kids", 307 | "is kid friendly", 308 | "good with kids", 309 | "family friends", 310 | "gentle with kids" 311 | ] 312 | } 313 | } 314 | ] 315 | }, 316 | { 317 | "name": "AMAZON.Animal", 318 | "values": [ 319 | { 320 | "name": { 321 | "value": "mythical_creatures", 322 | "synonyms": [ 323 | "unicorn", 324 | "chimera", 325 | "dragon" 326 | ] 327 | } 328 | } 329 | ] 330 | } 331 | ] 332 | }, 333 | "dialog": { 334 | "intents": [ 335 | { 336 | "name": "PetMatchIntent", 337 | "confirmationRequired": false, 338 | "prompts": {}, 339 | "slots": [ 340 | { 341 | "name": "pet", 342 | "type": "AMAZON.Animal", 343 | "confirmationRequired": false, 344 | "elicitationRequired": false, 345 | "prompts": {} 346 | }, 347 | { 348 | "name": "size", 349 | "type": "sizeType", 350 | "confirmationRequired": false, 351 | "elicitationRequired": true, 352 | "prompts": { 353 | "elicitation": "Elicit.Intent-PetMatchIntent.IntentSlot-size" 354 | } 355 | }, 356 | { 357 | "name": "temperament", 358 | "type": "temperamentType", 359 | "confirmationRequired": false, 360 | "elicitationRequired": true, 361 | "prompts": { 362 | "elicitation": "Elicit.Intent-PetMatchIntent.IntentSlot-temperament" 363 | } 364 | }, 365 | { 366 | "name": "I_Want", 367 | "type": "IWantType", 368 | "confirmationRequired": false, 369 | "elicitationRequired": false, 370 | "prompts": {} 371 | }, 372 | { 373 | "name": "energy", 374 | "type": "energyType", 375 | "confirmationRequired": false, 376 | "elicitationRequired": true, 377 | "prompts": { 378 | "elicitation": "Elicit.Intent-PetMatchIntent.IntentSlot-energy" 379 | } 380 | }, 381 | { 382 | "name": "article", 383 | "type": "articleType", 384 | "confirmationRequired": false, 385 | "elicitationRequired": false, 386 | "prompts": {} 387 | }, 388 | { 389 | "name": "at_the", 390 | "type": "atTheType", 391 | "confirmationRequired": false, 392 | "elicitationRequired": false, 393 | "prompts": {} 394 | } 395 | ] 396 | } 397 | ] 398 | }, 399 | "prompts": [ 400 | { 401 | "id": "Elicit.Intent-PetMatchIntent.IntentSlot-size", 402 | "variations": [ 403 | { 404 | "type": "PlainText", 405 | "value": "There are dogs that are tiny, small, medium, and large. Which would you like?" 406 | }, 407 | { 408 | "type": "PlainText", 409 | "value": "What size of a dog would you like?" 410 | } 411 | ] 412 | }, 413 | { 414 | "id": "Elicit.Intent-PetMatchIntent.IntentSlot-temperament", 415 | "variations": [ 416 | { 417 | "type": "PlainText", 418 | "value": "Are you looking for more of a family dog or a guard dog?" 419 | }, 420 | { 421 | "type": "PlainText", 422 | "value": "Would you prefer a dog to hang out with kids or to protect you?" 423 | } 424 | ] 425 | }, 426 | { 427 | "id": "Elicit.Intent-PetMatchIntent.IntentSlot-energy", 428 | "variations": [ 429 | { 430 | "type": "PlainText", 431 | "value": "Do you prefer high energy or low energy dogs?" 432 | } 433 | ] 434 | } 435 | ] 436 | } 437 | } -------------------------------------------------------------------------------- /skill.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest": { 3 | "publishingInformation": { 4 | "locales": { 5 | "en-US": { 6 | "summary": "Find the pet that is just right for you.", 7 | "examplePhrases": [ 8 | "Alexa open pet match", 9 | "Alexa ask pet match for a recommendation", 10 | "Alexa tell pet match that I want a big fluffy dog" 11 | ], 12 | "name": "Pet Match", 13 | "description": "Do you want a pet, but aren't sure what pet would be right for you? Pet Match will find the right pet for you by asking you a series of questions." 14 | } 15 | }, 16 | "isAvailableWorldwide": true, 17 | "testingInstructions": "Sample Testing Instructions.", 18 | "distributionCountries": [] 19 | }, 20 | "apis": { 21 | "custom": { 22 | "endpoint": { 23 | "sourceDir": "lambda/py", 24 | "uri": "ask-custom-petmatch-default" 25 | } 26 | } 27 | }, 28 | "manifestVersion": "1.0" 29 | } 30 | } 31 | --------------------------------------------------------------------------------