├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug.md │ └── feature.md ├── PULL_REQUEST_TEMPLATE.md └── semantic.yml ├── .gitignore ├── .mailmap ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE ├── MAINTAINERS.md ├── NOTICE ├── README.md ├── docs └── theme │ ├── assets │ ├── css │ │ ├── main.css │ │ └── main.css.map │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ └── js │ │ ├── main.js │ │ └── search.js │ └── partials │ ├── footer.hbs │ └── header.hbs ├── lerna.json ├── media └── images │ ├── gameon-api-keys.png │ ├── gameon-competition-publish-success.png │ ├── gameon-note-matchid.png │ ├── gameon-select-leaderboard.png │ ├── icon.png │ ├── skills-gameon-sdk-banner.png │ └── word-word-screenshot.png ├── mocha.opts ├── package.json ├── packages ├── gameon-sdk │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── client │ │ │ ├── api │ │ │ │ ├── apiClient.ts │ │ │ │ ├── apiClientFactory.ts │ │ │ │ └── gameOnApiClient.ts │ │ │ └── http │ │ │ │ ├── apiClient.ts │ │ │ │ └── defaultApiClient.ts │ │ └── index.ts │ ├── test │ │ └── client │ │ │ └── api │ │ │ └── defaultApiClient.spec.ts │ ├── tsconfig.compile.json │ └── tsconfig.lint.json └── skills-gameon-sdk │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ └── avatarmaker.js │ ├── package.json │ ├── src │ ├── client │ │ └── skillsGameOnApiClient.ts │ ├── index.ts │ ├── player │ │ ├── hashUtility.ts │ │ ├── nameLists.ts │ │ ├── phraseProvider.ts │ │ ├── playerAvatarUriGenerator.ts │ │ ├── playerAvatarUriGeneratorConfig.ts │ │ ├── playerColorGenerator.ts │ │ ├── playerNameGenerator.ts │ │ ├── playerNameGeneratorBuilder.ts │ │ ├── playerNameGeneratorConfig.ts │ │ ├── playerProfile.ts │ │ ├── playerProfileConfig.ts │ │ ├── playerProfileGenerator.ts │ │ └── playerProfileGeneratorBuilder.ts │ └── utils │ │ ├── displayUtils.ts │ │ ├── kmsDecryptionHelper.ts │ │ └── views │ │ └── player-rank.json │ ├── test │ ├── client │ │ ├── getCombinationLeaderboards.mocks.ts │ │ └── skillsGameOnApiClient.spec.ts │ ├── player │ │ ├── phraseProvider.spec.ts │ │ ├── playerAvatarSelector.spec.ts │ │ ├── playerColorGenerator.spec.ts │ │ ├── playerNameGenerator.spec.ts │ │ ├── playerNameGeneratorBuilder.spec.ts │ │ └── playerProfileGenerator.spec.ts │ └── utils │ │ └── displayUtils.spec.ts │ ├── tsconfig.compile.json │ └── tsconfig.lint.json ├── samples └── word-word-lite │ ├── .eslintrc │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── models │ └── en-US.json │ ├── package.json │ ├── skill.json │ └── src │ ├── gameOn.js │ ├── index.js │ ├── settings.js │ └── words.js ├── tools └── update-credits.sh ├── tsconfig.json ├── tslint.json └── typedoc.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [{*.js,*.json,*.yml}] 10 | indent_size = 2 11 | indent_style = space -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json -diff -merge 2 | package-lock.json linguist-generated=true 3 | 4 | # Set the default behavior, in case people don't have core.autocrlf set. 5 | * text=auto 6 | 7 | # Force code files to use lf for linting 8 | *.js eol=lf 9 | *.ts eol=lf 10 | 11 | # Force the bash scripts to be checked out with LF line endings. 12 | *.bash text eol=lf 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Issue 3 | about: This is not a feature or a bug 4 | title: 'Issue: ' 5 | labels: '' 6 | --- 7 | 8 | # Issue Details 9 | 10 | ## Current SDK Version 11 | 12 | ## Current Behavior 13 | 14 | ## Desired Behavior 15 | 16 | ## Additional Information 17 | 18 | ```javascript 19 | // code samples 20 | ``` 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Create bug report 3 | about: Tell us about an issue 4 | title: 'Bug: ' 5 | labels: 'bug' 6 | --- 7 | 8 | # Bug Report 9 | 10 | ## SDK Version 11 | 12 | ## Current Behavior 13 | 14 | ## Expected Behavior 15 | 16 | ## Steps to Reproduce 17 | 18 | ## Additional Information 19 | 20 | 21 | ## Operating System 22 | 23 | ## Node Version 24 | 25 | ```javascript 26 | // code samples 27 | ``` 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New feature request 3 | about: Help us improve our software 4 | title: 'Feature: ' 5 | labels: 'feature' 6 | --- 7 | 8 | # Feature Request 9 | 10 | ## Current SDK Version 11 | 12 | ## Current Behavior 13 | 14 | ## Desired Behavior 15 | 16 | ## Additional Information 17 | 18 | ```javascript 19 | // code samples 20 | ``` 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Title 2 | 3 | 4 | 5 | ## Description 6 | 7 | 8 | 9 | ## Issue link(s) - Optional 10 | 11 | 12 | 13 | ## Motivation and Context 14 | 15 | 16 | 17 | 18 | ## Testing 19 | 20 | 21 | 22 | 23 | 24 | ## Screenshots (if appropriate) 25 | 26 | ## Types of changes 27 | 28 | 29 | 30 | - [ ] Bug fix (non-breaking change which fixes an issue) 31 | - [ ] New feature (non-breaking change which adds functionality) 32 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 33 | 34 | ## Checklist 35 | 36 | 37 | 38 | 39 | - [ ] My code follows the code style of this project 40 | - [ ] My change requires a change to the documentation 41 | - [ ] I have updated the documentation accordingly 42 | - [ ] I have read the **README** document 43 | - [ ] I have added tests to cover my changes 44 | - [ ] All new and existing tests passed 45 | 46 | ## License 47 | 48 | 49 | 50 | 51 | - [ ] I confirm that this pull request can be released under the Apache 2 license 52 | 53 | [issues]: https://github.com/alexa-games/skills-gameon-sdk-js/issues 54 | [license]: https://www.apache.org/licenses/LICENSE-2.0 55 | [cla]: http://en.wikipedia.org/wiki/Contributor_License_Agreement 56 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | # Always validate the PR title, and ignore the commits 2 | titleOnly: true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | packages/**/lib 4 | package-lock.json 5 | 6 | # Ignore auto generated docs, but keep the theme 7 | docs/* 8 | !docs/theme 9 | !docs/theme/* 10 | docs/theme/.DS_Store 11 | 12 | # AWS-specific 13 | **/aws-config.json 14 | 15 | # Lerna Tool 16 | lerna-debug.log 17 | 18 | # JetBrains Tools 19 | .idea 20 | 21 | # VsCode 22 | .vscode 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | .nyc_output 27 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # 2 | # This list is used by git-shortlog to fix a few botched name translations 3 | # in the git archive, either because the author's full name was messed up 4 | # and/or not always written the same way, making contributions from the 5 | # same person appearing not to be so. 6 | # 7 | # Examples 8 | # First Last 9 | Dylan House 10 | Dylan House 11 | Alexa Games <50157283+alexa-games-github-admin@users.noreply.github.com> -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '12' 5 | - '10' 6 | - '8' 7 | 8 | os: 9 | - linux 10 | - osx 11 | 12 | git: 13 | depth: false 14 | 15 | cache: 16 | directories: 17 | - $HOME/.npm 18 | - node_modules 19 | - packages/*/node_modules 20 | 21 | stages: 22 | - name: compile 23 | - name: lint 24 | - name: test 25 | # - name: nightly 26 | # if: type = cron AND branch = develop 27 | - name: publish 28 | if: branch = master AND type = push AND tag IS blank 29 | 30 | jobs: 31 | include: 32 | - stage: compile 33 | name: 'Compile' 34 | script: npm run build 35 | before_script: 36 | - echo "$TRAVIS_EVENT_TYPE" 37 | - echo "$TRAVIS_COMMIT" 38 | - echo "$TRAVIS_BRANCH" 39 | - echo "$TRAVIS_TAG" 40 | - stage: lint 41 | name: 'Code Lint' 42 | script: npm run lint 43 | # - stage: nightly 44 | # name: 'Nightly Build' 45 | # script: 46 | # - npm run build 47 | # - lerna publish --yes --canary --preid nightly.$TRAVIS_BUILD_ID --dist-tag nightly 48 | # before_script: 49 | # - echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" >> $HOME/.npmrc 2> /dev/null 50 | - stage: publish 51 | name: 'Publish' 52 | script: 53 | - npm run build 54 | - lerna version --github-release --conventional-commits --yes 55 | - lerna publish from-git --yes 56 | before_script: 57 | - echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" >> $HOME/.npmrc 2> /dev/null 58 | - git config user.email $GH_EMAIL 59 | - git config user.name $GH_NAME 60 | - git remote set-url origin https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git 61 | - git checkout master 62 | after_script: 63 | - git fetch 64 | - git checkout -t origin/develop 65 | - git merge master 66 | - git push 67 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.2.0](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.5...v0.2.0) (2020-06-09) 7 | 8 | 9 | ### Features 10 | 11 | * Multiple Languages leaderboard view ([#42](https://github.com/alexa-games/skills-gameon-sdk-js/issues/42)) ([730d2ba](https://github.com/alexa-games/skills-gameon-sdk-js/commit/730d2ba551b3c56e53fa31514fa0dedcc68a8803)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.1.5](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.4...v0.1.5) (2020-02-05) 18 | 19 | **Note:** Version bump only for package skills-gameon-sdk-js 20 | 21 | 22 | 23 | 24 | 25 | ## [0.1.4](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.3...v0.1.4) (2020-01-20) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * samples does not export refreshPlayerSession function ([#21](https://github.com/alexa-games/skills-gameon-sdk-js/issues/21)) ([b5687bf](https://github.com/alexa-games/skills-gameon-sdk-js/commit/b5687bfce73192ddff841c3e8794f736c2afbea6)) 31 | 32 | 33 | 34 | 35 | 36 | 37 | ## [0.1.3](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.2...v0.1.3) (2019-09-11) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * combined leaderboards error with single entry ([#17](https://github.com/alexa-games/skills-gameon-sdk-js/issues/17)) ([75b0d82](https://github.com/alexa-games/skills-gameon-sdk-js/commit/75b0d82)) 43 | * sample skill player session refresh logic fixes ([#18](https://github.com/alexa-games/skills-gameon-sdk-js/issues/18)) ([2d2cdf3](https://github.com/alexa-games/skills-gameon-sdk-js/commit/2d2cdf3)) 44 | * updated APL template ([#16](https://github.com/alexa-games/skills-gameon-sdk-js/issues/16)) ([167f927](https://github.com/alexa-games/skills-gameon-sdk-js/commit/167f927)) 45 | 46 | 47 | 48 | ## [0.1.2](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.1...v0.1.2) (2019-08-13) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * return empty match response when base response is undefined. ([#13](https://github.com/alexa-games/skills-gameon-sdk-js/issues/13)) ([f6766b8](https://github.com/alexa-games/skills-gameon-sdk-js/commit/f6766b8)) 54 | 55 | 56 | 57 | 58 | 59 | ## 0.1.1 (2019-06-27) 60 | 61 | **Note:** Version bump only for package skills-gameon-sdk-js 62 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the 4 | [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 5 | For more information see the 6 | [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 7 | [opensource-codeofconduct@amazon.com](mailto:opensource-codeofconduct@amazon.com) 8 | with any additional questions or comments. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributions Welcome 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. 4 | 5 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 6 | information to effectively respond to your bug report or contribution. 7 | 8 | If you're looking to report a bug or have an idea, follow the 9 | [bug and potential feature workflow](#reporting-bugs-and-potential-features). 10 | 11 | Follow the [contribution workflow](#contribution-workflow) for submitting your 12 | changes to the Skills GameOn SDK for Node.js codebase. If you want to receive high-level but still 13 | commit-based feedback for a contribution, follow the 14 | [request for comments](#request-for-comments) steps instead. 15 | 16 | ## Reporting Bugs and Potential Features 17 | 18 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 19 | 20 | When filing an issue, please check [existing open](https://github.com/alexa-games/skills-gameon-sdk-js/issues), or [recently closed](https://github.com/alexa-games/skills-gameon-sdk-js/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 21 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 22 | 23 | * A reproducible test case or series of steps 24 | * The version of our code being used 25 | * Any modifications you've made relevant to the bug 26 | * Anything unusual about your environment or deployment 27 | 28 | ## Contribution Workflow 29 | 30 | Skills GameOn SDK for Node.js uses the “fork-and-pull” development model. Follow these steps if 31 | you want to merge your changes to Skills GameOn SDK for Node.js: 32 | 33 | 1. Within your fork of 34 | [Skills GameOn SDK for Node.js](https://github.com/alexa-games/skills-gameon-sdk-js), create a 35 | branch for your contribution. Use a meaningful name. 36 | 1. Create your contribution, meeting all 37 | [contribution quality standards](#contribution-quality-standards) 38 | 1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) 39 | against the develop branch of the Skills GameOn SDK for Node.js repository. 40 | 1. Add two reviewers to your pull request (a maintainer will do that for you if 41 | you're new). Work with your reviewers to address any comments and obtain a 42 | minimum of 2 approvals, at least one of which must be provided by 43 | [a maintainer](MAINTAINERS.md). 44 | To update your pull request amend existing commits whenever applicable and 45 | then push the new changes to your pull request branch. 46 | 1. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 47 | 1. Once the pull request is approved, one of the maintainers will merge it. 48 | 49 | ## Request for Comments 50 | 51 | If you just want to receive feedback for a contribution proposal, open an “RFC” 52 | (“Request for Comments”) pull request: 53 | 54 | 1. On your fork of 55 | [Skills GameOn SDK for Node.js](https://github.com/alexa-games/skills-gameon-sdk-js), create a 56 | branch for the contribution you want feedback on. Use a meaningful name. 57 | 1. Create your proposal based on the existing codebase. 58 | 1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) 59 | against the develop branch of the Skills GameOn SDK for Node.js repository. Prefix your pull 60 | request name with `[RFC]`. 61 | 1. Discuss your proposal with the community on the pull request page (or on any 62 | other channel). Add the conclusion(s) of this discussion to the pull request 63 | page. 64 | 65 | ## Contribution Quality Standards 66 | 67 | Most quality and style standards are enforced automatically during integration 68 | testing. Your contribution needs to meet the following standards: 69 | 70 | * Separate each **logical change** into its own commit. 71 | * Each commit must pass all unit & code style tests, and the full pull request 72 | must pass all integration tests. See https://github.com/alexa-games/skills-gameon-sdk-js for more information on style and tests. 73 | * Unit test coverage must _increase_ the overall project code coverage. 74 | * Include integration tests for any new functionality in your pull request. 75 | * Document all your public functions. 76 | * Commits in your forked repository may be brief or absent. 77 | * Document your pull requests. Include the reasoning behind each change, and 78 | the testing done. Following [commit message best practices](https://github.com/erlang/otp/wiki/writing-good-commit-messages) is recommended. 79 | Your pull request will be squashed into a single commit if it is approved. 80 | * Acknowledge Skills GameOn SDK for Node.js is provisionally licensed as 81 | "Restricted Program Materials" under the Program Materials License Agreement(LICENSE) and certify that no 82 | part of your contribution contravenes this license by signing off on all your 83 | commits with `git -s`. Ensure that every file in your pull request has a 84 | header referring to the repository license file. 85 | 86 | ## Finding Contributions to Work On 87 | 88 | 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-games/skills-gameon-sdk-js/labels/help%20wanted) issues is a great place to start. 89 | 90 | ## Code of Conduct 91 | 92 | This project has adopted the [Amazon Open Source Code of Conduct](CODE_OF_CONDUCT.md). 93 | 94 | ## Security Issue Notifications 95 | 96 | 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. 97 | 98 | ## Licensing 99 | 100 | See the [LICENSE](https://github.com/alexa-games/skills-gameon-sdk-js/blob/master/LICENSE) file for our project's licensing and the [NOTICE](https://github.com/alexa-games/skills-gameon-sdk-js/blob/master/NOTICE) for anything that differs or related notices. We will ask you to confirm the licensing of your contribution. 101 | 102 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 103 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Skills GameOn SDK for Node.js Credits and Thanks 2 | (This file is autogenerated using [update-credits.sh](tools/update-credits.sh).) 3 | 4 | The Skills GameOn SDK for Node.js provides easy access to Amazon GameOn and creates higher-level methods, utilities, and examples that simplify the integration of GameOn features, like leaderboards, with Alexa skills. 5 | 6 | Contributors to the Skills GameOn SDK for Node.js repository: 7 | 8 | * Alexa Games 9 | * Daniel Wang 10 | * Dylan House 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Amazon.com, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | Skills GameOn SDK for Node.js is maintained by a dedicated team within Amazon: 4 | 5 | * Alexa Games 6 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Alexa Games GameOn SDK 2 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /docs/theme/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/docs/theme/assets/images/icons.png -------------------------------------------------------------------------------- /docs/theme/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/docs/theme/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/theme/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/docs/theme/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/theme/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/docs/theme/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /docs/theme/partials/footer.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Legend

5 |
6 |
    7 |
  • Module
  • 8 |
  • Object literal
  • 9 |
  • Variable
  • 10 |
  • Function
  • 11 |
  • Function with type parameter
  • 12 |
  • Index signature
  • 13 |
  • Type alias
  • 14 |
  • Type alias with type parameter
  • 15 | 16 |
17 |
    18 |
  • Enumeration
  • 19 |
  • Enumeration member
  • 20 |
  • Property
  • 21 |
  • Method
  • 22 |
23 |
    24 |
  • Interface
  • 25 |
  • Interface with type parameter
  • 26 |
  • Constructor
  • 27 |
  • Property
  • 28 |
  • Method
  • 29 |
  • Index signature
  • 30 |
31 |
    32 |
  • Class
  • 33 |
  • Class with type parameter
  • 34 |
  • Constructor
  • 35 |
  • Property
  • 36 |
  • Method
  • 37 |
  • Accessor
  • 38 |
  • Index signature
  • 39 |
40 |
    41 |
  • Inherited constructor
  • 42 |
  • Inherited property
  • 43 |
  • Inherited method
  • 44 |
  • Inherited accessor
  • 45 |
46 |
    47 |
  • Protected property
  • 48 |
  • Protected method
  • 49 |
  • Protected accessor
  • 50 |
51 |
    52 |
  • Private property
  • 53 |
  • Private method
  • 54 |
  • Private accessor
  • 55 |
56 |
    57 |
  • Static property
  • 58 |
  • Static method
  • 59 |
60 |
61 |
62 | 63 | 64 | {{#unless settings.hideGenerator}} 65 |
66 |

Generated using TypeDoc

67 |
68 | {{/unless}} 69 |
70 | Brought to you with ❤ by the Alexa Games team at Amazon. © 2019, Amazon.com, Inc. or its affiliates. All Rights Reserved. 71 |
72 | -------------------------------------------------------------------------------- /docs/theme/partials/header.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 26 | 27 |
28 | Menu 29 |
30 |
31 |
32 |
33 |
34 |
35 |
    36 | {{#with model}}{{> breadcrumb}}{{/with}} 37 |
38 |

{{#compact}} 39 | {{model.kindString}}  40 | {{model.name}} 41 | {{#if model.typeParameters}} 42 | < 43 | {{#each model.typeParameters}} 44 | {{#if @index}}, {{/if}} 45 | {{name}} 46 | {{/each}} 47 | > 48 | {{/if}} 49 | {{/compact}}

50 |
51 |
52 |
-------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.2.0", 6 | "command": { 7 | "version": { 8 | "message": "chore(release): publish %s \n\n [skip ci]" 9 | }, 10 | "bootstrap": { 11 | "hoist": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /media/images/gameon-api-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/gameon-api-keys.png -------------------------------------------------------------------------------- /media/images/gameon-competition-publish-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/gameon-competition-publish-success.png -------------------------------------------------------------------------------- /media/images/gameon-note-matchid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/gameon-note-matchid.png -------------------------------------------------------------------------------- /media/images/gameon-select-leaderboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/gameon-select-leaderboard.png -------------------------------------------------------------------------------- /media/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/icon.png -------------------------------------------------------------------------------- /media/images/skills-gameon-sdk-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/skills-gameon-sdk-banner.png -------------------------------------------------------------------------------- /media/images/word-word-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa-games/skills-gameon-sdk-js/fc10869cf621bc05c8904a412735456243679866/media/images/word-word-screenshot.png -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --require ts-node/register 2 | --require source-map-support/register 3 | --require tsconfig-paths/register 4 | --recursive test/ 5 | --watch-extensions ts 6 | --full-trace 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skills-gameon-sdk-js", 3 | "scripts": { 4 | "bootstrap": "npx lerna bootstrap", 5 | "build": "npx lerna run build", 6 | "build:gameon-sdk": "npx lerna run build --scope @alexa-games/gameon-sdk", 7 | "build:skills-gameon-sdk": "npx lerna run build --scope @alexa-games/skills-gameon-sdk", 8 | "clean": "npm run clean:dependencies && npm run clean:artifacts", 9 | "clean:artifacts": "npx lerna run clean:artifacts", 10 | "clean:dependencies": "rm -rf node_modules && npx lerna clean --yes", 11 | "compile": "npx lerna run compile", 12 | "document": "npx typedoc ./packages", 13 | "lint": "npx lerna run lint", 14 | "postinstall": "npm run bootstrap", 15 | "test": "npx lerna run test", 16 | "test:skills-gameon-sdk": "npx lerna run test --scope @alexa-games/skills-gameon-sdk" 17 | }, 18 | "devDependencies": { 19 | "lerna": "3.13.2" 20 | }, 21 | "dependencies": {} 22 | } 23 | -------------------------------------------------------------------------------- /packages/gameon-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /packages/gameon-sdk/.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | **/*.ts 3 | *.ts 4 | 5 | # definitions 6 | !**/*.d.ts 7 | !*.d.ts 8 | 9 | # configuration 10 | package-lock.json 11 | tsconfig.compile.json 12 | tsconfig.lint.json 13 | .prettierrc 14 | -------------------------------------------------------------------------------- /packages/gameon-sdk/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.2.0](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.5...v0.2.0) (2020-06-09) 7 | 8 | **Note:** Version bump only for package @alexa-games/gameon-sdk 9 | 10 | 11 | 12 | 13 | 14 | ## [0.1.5](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.4...v0.1.5) (2020-02-05) 15 | 16 | **Note:** Version bump only for package @alexa-games/gameon-sdk 17 | 18 | 19 | 20 | 21 | 22 | ## [0.1.4](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.3...v0.1.4) (2020-01-20) 23 | 24 | **Note:** Version bump only for package @alexa-games/gameon-sdk 25 | 26 | 27 | 28 | 29 | 30 | ## [0.1.3](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.2...v0.1.3) (2019-10-23) 31 | 32 | **Note:** Version bump only for package @alexa-games/gameon-sdk 33 | 34 | 35 | 36 | 37 | 38 | ## 0.1.1 (2019-06-27) 39 | 40 | **Note:** Version bump only for package @alexa-games/gameon-sdk 41 | -------------------------------------------------------------------------------- /packages/gameon-sdk/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is a client for Amazon GameOn's REST APIs. It is currently owned & maintained by the Alexa Games team. 4 | 5 | * Skills GameOn SDK Technical Documentation: https://skills-gameon-sdk.github.io 6 | -------------------------------------------------------------------------------- /packages/gameon-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alexa-games/gameon-sdk", 3 | "version": "0.2.0", 4 | "description": "SDK for Amazon GameOn", 5 | "keywords": [ 6 | "amazon", 7 | "gameon", 8 | "tournaments", 9 | "leaderboards", 10 | "gaming" 11 | ], 12 | "author": "Amazon", 13 | "license": "Apache-2.0", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/alexa-games/skills-gameon-sdk-js" 17 | }, 18 | "engines": { 19 | "node": ">=8.10.0" 20 | }, 21 | "main": "lib/index.js", 22 | "nyc": { 23 | "extension": [ 24 | ".ts", 25 | ".tsx" 26 | ], 27 | "exclude": [ 28 | "**/*.d.ts" 29 | ], 30 | "reporter": [ 31 | "html", 32 | "text" 33 | ], 34 | "all": false 35 | }, 36 | "types": "lib/index.d.ts", 37 | "files": [ 38 | "lib/**/*" 39 | ], 40 | "scripts": { 41 | "build": "npm run test && npm run clean:artifacts && npm run compile", 42 | "clean:artifacts": "rm -rf lib", 43 | "compile": "npx tsc -p tsconfig.compile.json", 44 | "lint": "npx tslint -p tsconfig.lint.json -c ../../tslint.json", 45 | "prepublish": "npm run build", 46 | "pretest": "npm run lint", 47 | "test": "npx mocha --opts ../../mocha.opts './test/**/*.spec.ts'", 48 | "test:coverage": "npx nyc mocha --require ts-node/register test/**/*.spec.ts" 49 | }, 50 | "devDependencies": { 51 | "@types/chai": "4.2.0", 52 | "@types/mocha": "5.2.7", 53 | "@types/nock": "9.3.1", 54 | "@types/sinon": "7.0.13", 55 | "chai": "4.2.0", 56 | "mocha": "^7.2.0", 57 | "nock": "10.0.6", 58 | "nyc": "^15.1.0", 59 | "sinon": "7.4.1", 60 | "source-map-support": "0.5.13", 61 | "ts-node": "8.3.0", 62 | "tsconfig-paths": "3.8.0", 63 | "tslint": "5.20.1", 64 | "typedoc": "0.14.2", 65 | "typescript": "3.3.3333" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/gameon-sdk/src/client/api/apiClient.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // This file is autogenerated using an internal tool that consumes the GameOn swagger document. Please do not edit manually. 9 | 10 | import { ApiClientRequest, ApiClientResponse, ApiConfiguration } from '../http/apiClient'; 11 | 12 | export abstract class ApiClient { 13 | private static isCodeSuccessful( responseCode: number ): boolean { 14 | // tslint:disable-next-line:no-magic-numbers 15 | return responseCode >= 200 && responseCode < 300; 16 | } 17 | 18 | private static buildUrl( 19 | endpoint: string, 20 | path: string, 21 | queryParameters: Map, 22 | pathParameters: Map 23 | ): string { 24 | const processedEndpoint: string = endpoint.endsWith('/') ? endpoint.substr(0, endpoint.length - 1) : endpoint; 25 | const pathWithParams: string = this.interpolateParams(path, pathParameters); 26 | const isConstantQueryPresent: boolean = pathWithParams.includes('?'); 27 | const queryString: string = this.buildQueryString(queryParameters, isConstantQueryPresent); 28 | 29 | return processedEndpoint + pathWithParams + queryString; 30 | } 31 | 32 | private static interpolateParams(path: string, params: Map): string { 33 | if (!params) { 34 | return path; 35 | } 36 | 37 | let result: string = path; 38 | 39 | params.forEach((paramValue: string, paramName: string) => { 40 | result = result.replace('{' + paramName + '}', encodeURIComponent(paramValue)); 41 | }); 42 | 43 | return result; 44 | } 45 | 46 | private static buildQueryString(params: Map, isQueryStart: boolean): string { 47 | if (!params) { 48 | return ''; 49 | } 50 | 51 | const sb: string[] = []; 52 | 53 | if (isQueryStart) { 54 | sb.push('&'); 55 | } else { 56 | sb.push('?'); 57 | } 58 | 59 | params.forEach((paramValue: string, paramName: string) => { 60 | sb.push(encodeURIComponent(paramName)); 61 | sb.push('='); 62 | sb.push(encodeURIComponent(paramValue)); 63 | sb.push('&'); 64 | }); 65 | sb.pop(); 66 | 67 | return sb.join(''); 68 | } 69 | 70 | /** 71 | * ApiConfiguration instance to provide dependencies for this service client 72 | */ 73 | protected apiConfiguration: ApiConfiguration; 74 | 75 | /** 76 | * Creates new instance of the ApiClient 77 | * @param {ApiConfiguration} apiConfiguration configuration parameter to provide dependencies to service client instance 78 | */ 79 | protected constructor(apiConfiguration: ApiConfiguration) { 80 | this.apiConfiguration = apiConfiguration; 81 | } 82 | 83 | /** 84 | * Invocation wrapper to implement service operations in generated classes 85 | * @param method HTTP method, such as 'POST', 'GET', 'DELETE', etc. 86 | * @param endpoint base API url 87 | * @param path the path pattern with possible placeholders for path parameters in form {paramName} 88 | * @param pathParams path parameters collection 89 | * @param queryParams query parameters collection 90 | * @param headerParams headers collection 91 | * @param bodyParam if body parameter is present it is provided here, otherwise null or undefined 92 | * @param errors maps recognized status codes to messages 93 | */ 94 | protected async invoke( 95 | method: string, 96 | endpoint: string, 97 | path: string, 98 | pathParams: Map, 99 | queryParams: Map, 100 | headerParams: Array<{ key: string, value: string }>, 101 | bodyParam: any, errors: Map 102 | ): Promise { 103 | const request: ApiClientRequest = { 104 | url : ApiClient.buildUrl(endpoint, path, queryParams, pathParams), 105 | method, 106 | headers : headerParams 107 | }; 108 | if (bodyParam !== null) { 109 | request.body = JSON.stringify(bodyParam); 110 | } 111 | 112 | const apiClient = this.apiConfiguration.apiClient; 113 | let response: ApiClientResponse; 114 | try { 115 | response = await apiClient.invoke(request); 116 | } catch (err) { 117 | err.message = `Call to service failed: ${err.message}`; 118 | 119 | throw err; 120 | } 121 | 122 | let body; 123 | 124 | try { 125 | body = response.body ? JSON.parse(response.body) : undefined; 126 | } catch (err) { 127 | throw new SyntaxError(`Failed trying to parse the response body: ${response.body}`); 128 | } 129 | 130 | if (ApiClient.isCodeSuccessful(response.statusCode)) { 131 | return body; 132 | } 133 | 134 | const err = new Error('Unknown error'); 135 | err.name = 'ServiceError'; 136 | err['statusCode'] = response.statusCode; // tslint:disable-line:no-string-literal 137 | err['response'] = body; // tslint:disable-line:no-string-literal 138 | if (errors && errors.has(response.statusCode)) { 139 | err.message = JSON.stringify(response.body); 140 | } 141 | 142 | throw err; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /packages/gameon-sdk/src/client/api/apiClientFactory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // This file is autogenerated using an internal tool that consumes the GameOn swagger document. Please do not edit manually. 9 | 10 | import { ApiConfiguration } from '../http/apiClient'; 11 | 12 | /** 13 | * Helper class that instantiates an ApiClient implementation automatically resolving its 14 | * required ApiConfiguration. 15 | * @export 16 | * @class ApiClientFactory 17 | */ 18 | import { GameOnApiClient } from './gameOnApiClient'; 19 | 20 | export class ApiClientFactory { 21 | public apiConfiguration: ApiConfiguration; 22 | 23 | constructor(apiConfiguration: ApiConfiguration) { 24 | this.apiConfiguration = apiConfiguration; 25 | } 26 | 27 | /** 28 | * Gets an instance of GameOnApiClient. 29 | */ 30 | public getGameOnApiClient(): GameOnApiClient { 31 | try { 32 | return new GameOnApiClient(this.apiConfiguration); 33 | } catch (e) { 34 | const factoryError = new Error(`ApiClientFactory Error while initializing GameOnApiClient: ${e.message}`); 35 | factoryError['name'] = 'ApiClientFactoryError'; 36 | 37 | throw factoryError; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/gameon-sdk/src/client/http/apiClient.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | /** 9 | * Represents the interface between ApiClient and a Service Client. 10 | * @export 11 | * @interface ApiClientMessage 12 | */ 13 | export interface ApiClientMessage { 14 | headers: Array<{key: string, value: string}>; 15 | body?: string; 16 | } 17 | 18 | /** 19 | * Represents a request sent from Service Clients to an ApiClient implementation. 20 | * @export 21 | * @interface ApiClientRequest 22 | * @extends {ApiClientMessage} 23 | */ 24 | export interface ApiClientRequest extends ApiClientMessage { 25 | url: string; 26 | method: string; 27 | } 28 | 29 | /** 30 | * Represents a response returned by ApiClient implementation to a Service Client. 31 | * @export 32 | * @interface ApiClientResponse 33 | * @extends {ApiClientMessage} 34 | */ 35 | export interface ApiClientResponse extends ApiClientMessage { 36 | /** 37 | * Result code of the attempt to satisfy the request. Normally this 38 | * corresponds to the HTTP status code returned by the server. 39 | */ 40 | statusCode: number; 41 | } 42 | 43 | /** 44 | * Represents a basic contract for API request execution 45 | * @export 46 | * @interface ApiClient 47 | */ 48 | export interface ApiClient { 49 | /** 50 | * Dispatches a request to an API endpoint described in the request. 51 | * An ApiClient is expected to resolve the Promise in the case an API returns a non-200 HTTP 52 | * status code. The responsibility of translating a particular response code to an error lies with the 53 | * caller to invoke. 54 | * @param {ApiClientRequest} request request to dispatch to the ApiClient 55 | * @returns {Promise} Response from the ApiClient 56 | * @memberof ApiClient 57 | */ 58 | invoke(request: ApiClientRequest): Promise; 59 | } 60 | 61 | /** 62 | * Represents an interface that provides API configuration options needed by service clients. 63 | * @interface ApiConfiguration 64 | */ 65 | export interface ApiConfiguration { 66 | /** 67 | * Configured ApiClient implementation 68 | */ 69 | apiClient: ApiClient; 70 | /** 71 | * Endpoint to hit by the service client instance 72 | */ 73 | apiEndpoint: string; 74 | } 75 | -------------------------------------------------------------------------------- /packages/gameon-sdk/src/client/http/defaultApiClient.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { RequestOptions as HttpRequestOptions } from 'http'; 9 | import { RequestOptions as HttpsRequestOptions } from 'https'; 10 | import * as url from 'url'; 11 | import { ApiClient, ApiClientRequest, ApiClientResponse } from './apiClient'; 12 | 13 | /** 14 | * Default implementation of {@link ApiClient} which uses the native HTTP/HTTPS library of Node.JS. 15 | */ 16 | export class DefaultApiClient implements ApiClient { 17 | /** 18 | * Dispatches a request to an API endpoint described in the request. 19 | * An ApiClient is expected to resolve the Promise in the case an API returns a non-200 HTTP 20 | * status code. The responsibility of translating a particular response code to an error lies with the 21 | * caller to invoke. 22 | * @param {ApiClientRequest} request request to dispatch to the ApiClient 23 | * @returns {Promise} response from the ApiClient 24 | */ 25 | public async invoke(request: ApiClientRequest): Promise { 26 | const urlObj = url.parse(request.url); 27 | 28 | const clientRequestOptions: HttpRequestOptions | HttpsRequestOptions = { 29 | // tslint:disable:object-literal-sort-keys 30 | hostname : urlObj.hostname, 31 | path : urlObj.path, 32 | port : urlObj.port, 33 | protocol : urlObj.protocol, 34 | auth : urlObj.auth, 35 | headers : arrayToObjectHeader(request.headers), 36 | method : request.method 37 | }; 38 | 39 | const client = clientRequestOptions.protocol === 'https:' ? require('https') : require('http'); 40 | 41 | return new Promise((resolve, reject) => { 42 | const clientRequest = client.request(clientRequestOptions, (response) => { 43 | const chunks: any = []; 44 | response.on('data', (chunk) => { 45 | chunks.push(chunk); 46 | }); 47 | 48 | response.on('end', () => { 49 | const responseStr = chunks.join(''); 50 | const responseObj: ApiClientResponse = { 51 | statusCode : response.statusCode, 52 | body : responseStr, 53 | headers : objectToArrayHeader(response.headers) 54 | }; 55 | 56 | resolve(responseObj); 57 | }); 58 | }); 59 | 60 | clientRequest.on('error', (err) => { 61 | reject(new Error(err.message)); 62 | }); 63 | 64 | if (request.body) { 65 | clientRequest.write(request.body); 66 | } 67 | 68 | clientRequest.end(); 69 | }); 70 | } 71 | } 72 | 73 | /** 74 | * Converts the header array in {@link ApiClientRequest} to compatible JSON object. 75 | * @private 76 | * @param {{key : string, value : string}[]} header header array from ApiClientRequest} 77 | * @returns {Object.} header object to pass into HTTP client 78 | */ 79 | function arrayToObjectHeader(header: Array<{key: string, value: string}>): {[key: string]: string[]} { 80 | const reducer = (obj: {[key: string]: string[]}, item: {key: string, value: string}) 81 | : {[key: string]: string | string[]} => { 82 | if (obj[item.key]) { 83 | obj[item.key].push(item.value); 84 | } else { 85 | obj[item.key] = [item.value]; 86 | } 87 | 88 | return obj; 89 | }; 90 | 91 | return header.reduce(reducer, {}); 92 | } 93 | 94 | /** 95 | * Converts JSON header object to header array required for {ApiClientResponse} 96 | * @private 97 | * @param {Object.} header JSON header object returned by HTTP client 98 | * @returns {{key : string, value : string}[]} 99 | */ 100 | function objectToArrayHeader(header: {[key: string]: string | string[]}): Array<{key: string, value: string}> { 101 | const arrayHeader = > []; 102 | 103 | Object.keys(header).forEach((key: string) => { 104 | const headerArray = Array.isArray(header[key]) ? header[key] : [header[key]]; 105 | for (const value of headerArray) { 106 | arrayHeader.push({ 107 | key, 108 | value 109 | }); 110 | } 111 | }); 112 | 113 | return arrayHeader; 114 | } 115 | -------------------------------------------------------------------------------- /packages/gameon-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | export * from './client/http/defaultApiClient'; 9 | export * from './client/http/apiClient'; 10 | export * from './client/api/gameOnApiClient'; 11 | export * from './client/api/apiClientFactory'; 12 | -------------------------------------------------------------------------------- /packages/gameon-sdk/test/client/api/defaultApiClient.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | 10 | import { expect } from 'chai'; 11 | import nock from 'nock'; 12 | import { ApiClient, ApiClientRequest, ApiClientResponse } from '../../../src/client/http/apiClient'; 13 | import { DefaultApiClient } from '../../../src/client/http/defaultApiClient'; 14 | 15 | describe('defaultApiClient', () => { 16 | const testHttpUrl: string = 'http://dummy.com'; 17 | const testHttpsUrl: string = 'https://dummy.com'; 18 | let apiClient: ApiClient; 19 | 20 | before(() => { 21 | apiClient = new DefaultApiClient(); 22 | }); 23 | 24 | it('should be able to send POST request', async () => { 25 | const request: ApiClientRequest = { 26 | body : 'Test POST Message', 27 | headers : [ 28 | {key : 'k1', value : 'v1'}, 29 | {key : 'k1', value : 'k2'} 30 | ], 31 | method : 'POST', 32 | url : testHttpUrl 33 | }; 34 | 35 | const apiFake = nock(testHttpUrl) 36 | .matchHeader('k1', 'v1,k2') 37 | .post('/', 'Test POST Message') 38 | .reply(200, 'Success', { 39 | v1 : ['k_1', 'k_2'] 40 | }); 41 | 42 | const response: ApiClientResponse = await apiClient.invoke(request); 43 | 44 | expect(apiFake.isDone()).equal(true); 45 | expect(response.statusCode).equal(200); 46 | expect(response.body).equal('Success'); 47 | expect(response.headers[0]).deep.equal({key : 'v1', value : 'k_1'}); 48 | expect(response.headers[1]).deep.equal({key : 'v1', value : 'k_2'}); 49 | }); 50 | 51 | it('should be able to send GET request', async () => { 52 | const request: ApiClientRequest = { 53 | headers : [ 54 | {key : 'k1', value : 'v1'}, 55 | {key : 'k1', value : 'k2'} 56 | ], 57 | method : 'GET', 58 | url : testHttpUrl 59 | }; 60 | 61 | const apiFake = nock(testHttpUrl) 62 | .matchHeader('k1', 'v1,k2') 63 | .get('/') 64 | .reply(200, 'Success', { 65 | v1 : ['k_1', 'k_2'] 66 | }); 67 | 68 | const response: ApiClientResponse = await apiClient.invoke(request); 69 | 70 | expect(apiFake.isDone()).equal(true); 71 | expect(response.statusCode).equal(200); 72 | expect(response.body).equal('Success'); 73 | expect(response.headers[0]).deep.equal({key : 'v1', value : 'k_1'}); 74 | expect(response.headers[1]).deep.equal({key : 'v1', value : 'k_2'}); 75 | }); 76 | 77 | it('should be able to send DELETE request', async () => { 78 | const request: ApiClientRequest = { 79 | headers : [ 80 | {key : 'k1', value : 'v1'}, 81 | {key : 'k1', value : 'k2'} 82 | ], 83 | method : 'DELETE', 84 | url : testHttpsUrl 85 | }; 86 | 87 | const apiFake = nock(testHttpsUrl) 88 | .matchHeader('k1', 'v1,k2') 89 | .delete('/') 90 | .reply(200, 'Success', { 91 | v1 : ['k_1', 'k_2'] 92 | }); 93 | 94 | const response: ApiClientResponse = await apiClient.invoke(request); 95 | 96 | expect(apiFake.isDone()).equal(true); 97 | expect(response.statusCode).equal(200); 98 | expect(response.body).equal('Success'); 99 | expect(response.headers[0]).deep.equal({key : 'v1', value : 'k_1'}); 100 | expect(response.headers[1]).deep.equal({key : 'v1', value : 'k_2'}); 101 | }); 102 | 103 | it('should be able to send PUT request', async () => { 104 | const request: ApiClientRequest = { 105 | body : 'Test PUT Message', 106 | headers : [ 107 | {key : 'k1', value : 'v1'}, 108 | {key : 'k1', value : 'k2'} 109 | ], 110 | method : 'PUT', 111 | url : testHttpsUrl 112 | }; 113 | 114 | const apiFake = nock(testHttpsUrl) 115 | .matchHeader('k1', 'v1,k2') 116 | .put('/', 'Test PUT Message') 117 | .reply(200, 'Success', { 118 | v1 : 'k_1' 119 | }); 120 | 121 | const response: ApiClientResponse = await apiClient.invoke(request); 122 | 123 | expect(apiFake.isDone()).equal(true); 124 | expect(response.statusCode).equal(200); 125 | expect(response.body).equal('Success'); 126 | expect(response.headers[0]).deep.equal({key : 'v1', value : 'k_1'}); 127 | }); 128 | 129 | it('should throw an error if API has returned an error', async () => { 130 | const request: ApiClientRequest = { 131 | body : 'Test PUT Message', 132 | headers : [ 133 | {key : 'k1', value : 'v1'}, 134 | {key : 'k1', value : 'k2'} 135 | ], 136 | method : 'PUT', 137 | url : testHttpsUrl 138 | }; 139 | 140 | const apiFake = nock(testHttpsUrl) 141 | .matchHeader('k1', 'v1,k2') 142 | .put('/', 'Test PUT Message') 143 | .replyWithError('UnknownError'); 144 | 145 | try { 146 | await apiClient.invoke(request); 147 | } catch (err) { 148 | expect(apiFake.isDone()).equal(true); 149 | expect(err.name).equal('Error'); 150 | expect(err.message).equal('UnknownError'); 151 | 152 | return; 153 | } 154 | 155 | throw new Error('should have thrown an error!'); 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /packages/gameon-sdk/tsconfig.compile.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "declaration": true 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "exclude": [ 11 | "node_modules", 12 | "test" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/gameon-sdk/tsconfig.lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*", "test/**/*"], 4 | "exclude": ["node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | **/*.ts 3 | *.ts 4 | 5 | # definitions 6 | !**/*.d.ts 7 | !*.d.ts 8 | 9 | # configuration 10 | package-lock.json 11 | tsconfig.compile.json 12 | tsconfig.lint.json 13 | .prettierrc 14 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.2.0](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.5...v0.2.0) (2020-06-09) 7 | 8 | 9 | ### Features 10 | 11 | * Multiple Languages leaderboard view ([#42](https://github.com/alexa-games/skills-gameon-sdk-js/issues/42)) ([730d2ba](https://github.com/alexa-games/skills-gameon-sdk-js/commit/730d2ba551b3c56e53fa31514fa0dedcc68a8803)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.1.5](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.4...v0.1.5) (2020-02-05) 18 | 19 | **Note:** Version bump only for package @alexa-games/skills-gameon-sdk 20 | 21 | 22 | 23 | 24 | 25 | ## [0.1.4](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.3...v0.1.4) (2020-01-20) 26 | 27 | **Note:** Version bump only for package @alexa-games/skills-gameon-sdk 28 | 29 | 30 | 31 | 32 | 33 | ## [0.1.3](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.2...v0.1.3) (2019-10-23) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * combined leaderboards error with single entry ([#17](https://github.com/alexa-games/skills-gameon-sdk-js/issues/17)) ([75b0d82](https://github.com/alexa-games/skills-gameon-sdk-js/commit/75b0d82)) 39 | * updated APL template ([#16](https://github.com/alexa-games/skills-gameon-sdk-js/issues/16)) ([167f927](https://github.com/alexa-games/skills-gameon-sdk-js/commit/167f927)) 40 | 41 | 42 | 43 | 44 | 45 | ## [0.1.2](https://github.com/alexa-games/skills-gameon-sdk-js/compare/v0.1.1...v0.1.2) (2019-08-13) 46 | 47 | 48 | ### Bug Fixes 49 | 50 | * return empty match response when base response is undefined. ([#13](https://github.com/alexa-games/skills-gameon-sdk-js/issues/13)) ([f6766b8](https://github.com/alexa-games/skills-gameon-sdk-js/commit/f6766b8)) 51 | 52 | 53 | 54 | 55 | 56 | ## 0.1.1 (2019-06-27) 57 | 58 | **Note:** Version bump only for package @alexa-games/skills-gameon-sdk 59 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | The Skills GameOn SDK for Node.js provides easy access to Amazon GameOn and creates higher-level methods, utilities, and examples that simplify the integration of GameOn features, like leaderboards, with Alexa skills. 4 | 5 | * Skills GameOn SDK Technical Documentation: https://skills-gameon-sdk.github.io 6 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/bin/avatarmaker.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | /* 4 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 | * SPDX-License-Identifier: Apache-2.0 7 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 | */ 9 | 10 | // Ensure required dependencies are installed before running script 11 | const requiredPackages = ["convert-svg-to-png", "gridy-avatars"]; 12 | for (let dependency of requiredPackages) { 13 | try { 14 | require(dependency); 15 | } catch (e) { 16 | console.log(dependency + " not found, please install with: npm install " + dependency); 17 | return; 18 | } 19 | } 20 | 21 | const av = require('gridy-avatars'); 22 | const { convert } = require('convert-svg-to-png'); 23 | const fs = require('fs'); 24 | const { DEFAULT_NUMBER_OF_UNIQUE_AVATARS } = require('../lib/player/playerAvatarUriGeneratorConfig'); 25 | 26 | const AvatarDirectory = 'avatars'; 27 | const DefaultNumberToCreate = DEFAULT_NUMBER_OF_UNIQUE_AVATARS; 28 | const AvatarDimension = 100; 29 | 30 | (async () => { 31 | 32 | let numberToCreate = DefaultNumberToCreate; 33 | let directory = AvatarDirectory; 34 | 35 | if (process.argv.length > 3) { 36 | const value = process.argv[3]; 37 | 38 | if (value === '--help') { 39 | console.log('This CLI tool will generate a 100x100 pixel image that can be used as a player avatar.'); 40 | console.log(`By default, the tool will create ${DefaultNumberToCreate} images in the ${AvatarDirectory} directory.`); 41 | console.log('You may specify different values in the first two parameters of the script. For example: '); 42 | console.log('\n avatar-maker -- 50 avatar1'); 43 | console.log('\nWill create 50 images in the avatar1 directory.'); 44 | process.exit(0); 45 | } 46 | 47 | numberToCreate = value; 48 | } 49 | 50 | if (process.argv.length > 4) { 51 | directory = process.argv[4]; 52 | } 53 | 54 | console.log(`Generating ${numberToCreate} avatars in directory '${directory}'`); 55 | 56 | const fullPath = `${process.cwd()}/${directory}`; 57 | 58 | if (!fs.existsSync(fullPath)) { 59 | console.log('Creating avatars'); 60 | fs.mkdirSync(fullPath); 61 | } 62 | 63 | for (let i = 0; i < numberToCreate; i++){ 64 | let randomId = av.random(); 65 | let outer = av.outer(randomId, AvatarDimension); 66 | const fileName = `${fullPath}/${i}.png`; 67 | try { 68 | if (fs.existsSync(fileName)) { 69 | console.log(`File already present ${fileName}`); 70 | } else { 71 | const png = await convert(outer); 72 | fs.writeFileSync(fileName, png); 73 | console.log(`Saving file ${fileName} from ${randomId}`); 74 | } 75 | } catch (err) { 76 | console.log(err); 77 | } 78 | } 79 | 80 | console.log(`\nDone!`); 81 | })(); 82 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alexa-games/skills-gameon-sdk", 3 | "version": "0.2.0", 4 | "description": "Alexa Skills SDK for Amazon GameOn", 5 | "keywords": [ 6 | "amazon", 7 | "alexa", 8 | "skills", 9 | "gameon", 10 | "alexa skills", 11 | "alexa skills kit", 12 | "ask", 13 | "tournaments", 14 | "leaderboards", 15 | "gaming" 16 | ], 17 | "author": "Amazon", 18 | "license": "Apache-2.0", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/alexa-games/skills-gameon-sdk-js" 22 | }, 23 | "engines": { 24 | "node": ">=8.10.0" 25 | }, 26 | "main": "lib/index.js", 27 | "nyc": { 28 | "extension": [ 29 | ".ts", 30 | ".tsx" 31 | ], 32 | "exclude": [ 33 | "**/*.d.ts" 34 | ], 35 | "reporter": [ 36 | "html", 37 | "text" 38 | ], 39 | "all": false 40 | }, 41 | "types": "lib/index.d.ts", 42 | "files": [ 43 | "lib/**/*" 44 | ], 45 | "bin": { 46 | "avatar-maker": "bin/avatarmaker.js" 47 | }, 48 | "scripts": { 49 | "build": "npm run test && npm run clean:artifacts && npm run compile && npm run copy", 50 | "clean:artifacts": "rm -rf lib", 51 | "compile": "npx tsc -p tsconfig.compile.json", 52 | "copy": "cp -rf bin lib/bin", 53 | "document": "npx typedoc --out docs", 54 | "lint": "npx tslint -p tsconfig.lint.json -c ../../tslint.json", 55 | "prepublish": "npm run build", 56 | "pretest": "npm run lint", 57 | "test": "npx mocha --opts ../../mocha.opts './test/**/*.spec.ts'", 58 | "test:coverage": "npx nyc mocha --require ts-node/register test/**/*.spec.ts" 59 | }, 60 | "dependencies": { 61 | "@alexa-games/gameon-sdk": "^0.0.1", 62 | "aws-sdk": "2.519.0", 63 | "ordinal": "1.0.3", 64 | "random-words": "1.1.0", 65 | "uuid": "3.3.3" 66 | }, 67 | "devDependencies": { 68 | "@types/chai": "4.2.0", 69 | "@types/mocha": "5.2.7", 70 | "@types/sinon": "7.0.13", 71 | "@types/uuid": "3.4.5", 72 | "chai": "4.2.0", 73 | "mocha": "^7.2.0", 74 | "nyc": "^15.1.0", 75 | "sinon": "7.4.1", 76 | "source-map-support": "0.5.13", 77 | "ts-node": "8.3.0", 78 | "tsconfig-paths": "3.8.0", 79 | "tslint": "5.20.1", 80 | "typescript": "3.3.3333" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | export * from './client/skillsGameOnApiClient'; 9 | export * from './utils/displayUtils'; 10 | 11 | export {PlayerNameGeneratorBuilder} from './player/playerNameGeneratorBuilder'; 12 | export {NameLists} from './player/nameLists'; 13 | export {PlayerNameGenerator} from './player/playerNameGenerator'; 14 | export {SimplePhraseProvider, PhraseProvider} from './player/phraseProvider'; 15 | export {PlayerNameGeneratorConfig} from './player/playerNameGeneratorConfig'; 16 | export {PlayerProfile} from './player/playerProfile'; 17 | export {PlayerProfileGenerator} from './player/playerProfileGenerator'; 18 | export {PlayerAvatarUriGenerator} from './player/playerAvatarUriGenerator'; 19 | export {DEFAULT_NUMBER_OF_UNIQUE_AVATARS, PlayerAvatarUriGeneratorConfig} from './player/playerAvatarUriGeneratorConfig'; 20 | export {PlayerProfileGeneratorBuilder} from './player/playerProfileGeneratorBuilder'; 21 | export {KmsDecryptionHelper} from './utils/kmsDecryptionHelper'; 22 | 23 | export * from '@alexa-games/gameon-sdk'; 24 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/hashUtility.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import crypto from 'crypto'; 9 | 10 | /** 11 | * Generate hash from id 12 | * @param id Used to generate hash 13 | * @return Buffer 14 | */ 15 | export function getHash(id: string | number): Buffer { 16 | if (typeof (id) !== 'number' && (!id || id.length === 0)) { 17 | throw new Error('Parameter id cannot be empty.'); 18 | } 19 | 20 | const hash = crypto.createHash('sha256'); 21 | hash.update(String(id)); 22 | return hash.digest(); 23 | } 24 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/nameLists.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | /** 9 | * List of name fragments used in name generation 10 | */ 11 | export class NameLists { 12 | public static first = [ 13 | 'Super', 14 | 'Abnormally', 15 | 'Absentmindedly', 16 | 'Accidentally', 17 | 'Actually', 18 | 'Adventurously', 19 | 'Afterwards', 20 | 'Almost', 21 | 'Always', 22 | 'Annually', 23 | 'Bashfully', 24 | 'Beautifully', 25 | 'Blindly', 26 | 'Blissfully', 27 | 'Boastfully', 28 | 'Boldly', 29 | 'Bravely', 30 | 'Briefly', 31 | 'Brightly', 32 | 'Briskly', 33 | 'Broadly', 34 | 'Busily', 35 | 'Calmly', 36 | 'Carefully', 37 | 'Carelessly', 38 | 'Cautiously', 39 | 'Certainly', 40 | 'Cheerfully', 41 | 'Clearly', 42 | 'Cleverly', 43 | 'Closely', 44 | 'Coaxingly', 45 | 'Colorfully', 46 | 'Commonly', 47 | 'Continually', 48 | 'Coolly', 49 | 'Correctly', 50 | 'Courageously', 51 | 'Crossly', 52 | 'Curiously', 53 | 'Daily', 54 | 'Daintily', 55 | 'Dearly', 56 | 'Deeply', 57 | 'Defiantly', 58 | 'Deliberately', 59 | 'Delightfully', 60 | 'Diligently', 61 | 'Doubtfully', 62 | 'Dreamily', 63 | 'Easily', 64 | 'Elegantly', 65 | 'Energetically', 66 | 'Enormously', 67 | 'Enthusiastically', 68 | 'Equally', 69 | 'Especially', 70 | 'Even', 71 | 'Evenly', 72 | 'Eventually', 73 | 'Exactly', 74 | 'Excitedly', 75 | 'Extremely', 76 | 'Fairly', 77 | 'Faithfully', 78 | 'Famously', 79 | 'Far', 80 | 'Fast', 81 | 'Ferociously', 82 | 'Fervently', 83 | 'Fiercely', 84 | 'Fondly', 85 | 'Foolishly', 86 | 'Fortunately', 87 | 'Frankly', 88 | 'Frantically', 89 | 'Freely', 90 | 'Frightfully', 91 | 'Fully', 92 | 'Furiously', 93 | 'Generally', 94 | 'Generously', 95 | 'Gently', 96 | 'Gladly', 97 | 'Gleefully', 98 | 'Gracefully', 99 | 'Gratefully', 100 | 'Greatly', 101 | 'Greedily', 102 | 'Happily', 103 | 'Hastily', 104 | 'Healthily', 105 | 'Heavily', 106 | 'Helpfully', 107 | 'Helplessly', 108 | 'Highly', 109 | 'Honestly', 110 | 'Hopelessly', 111 | 'Hourly', 112 | 'Hungrily', 113 | 'Immediately', 114 | 'Innocently', 115 | 'Inquisitively', 116 | 'Instantly', 117 | 'Intensely', 118 | 'Intently', 119 | 'Interestingly', 120 | 'Inwardly', 121 | 'Jaggedly', 122 | 'Jealously', 123 | 'Jovially', 124 | 'Joyfully', 125 | 'Joyously', 126 | 'Jubilantly', 127 | 'Judgmentally', 128 | 'Justly', 129 | 'Keenly', 130 | 'Kiddingly', 131 | 'Kindheartedly', 132 | 'Kindly', 133 | 'Knavishly', 134 | 'Knowingly', 135 | 'Knowledgeably', 136 | 'Kookily', 137 | 'Lazily', 138 | 'Lightly', 139 | 'Likely', 140 | 'Limply', 141 | 'Lively', 142 | 'Loftily', 143 | 'Longingly', 144 | 'Loosely', 145 | 'Loudly', 146 | 'Lovingly', 147 | 'Loyally', 148 | 'Madly', 149 | 'Majestically', 150 | 'Meaningfully', 151 | 'Mechanically', 152 | 'Merrily', 153 | 'Mockingly', 154 | 'Monthly', 155 | 'More', 156 | 'Mortally', 157 | 'Mostly', 158 | 'Mysteriously', 159 | 'Naturally', 160 | 'Nearly', 161 | 'Neatly', 162 | 'Nervously', 163 | 'Never', 164 | 'Nicely', 165 | 'Noisily', 166 | 'Not', 167 | 'Obediently', 168 | 'Oddly', 169 | 'Offensively', 170 | 'Officially', 171 | 'Often', 172 | 'Only', 173 | 'Openly', 174 | 'Optimistically', 175 | 'Overconfidently', 176 | 'Painfully', 177 | 'Partially', 178 | 'Patiently', 179 | 'Perfectly', 180 | 'Physically', 181 | 'Playfully', 182 | 'Politely', 183 | 'Poorly', 184 | 'Positively', 185 | 'Potentially', 186 | 'Powerfully', 187 | 'Promptly', 188 | 'Properly', 189 | 'Punctually', 190 | 'Quaintly', 191 | 'Questionably', 192 | 'Quicker', 193 | 'Quickly', 194 | 'Quietly', 195 | 'Quirkily', 196 | 'Quizzically', 197 | 'Randomly', 198 | 'Rapidly', 199 | 'Rarely', 200 | 'Readily', 201 | 'Really', 202 | 'Reassuringly', 203 | 'Recklessly', 204 | 'Regularly', 205 | 'Reluctantly', 206 | 'Repeatedly', 207 | 'Reproachfully', 208 | 'Restfully', 209 | 'Righteously', 210 | 'Rightfully', 211 | 'Scarcely', 212 | 'Searchingly', 213 | 'Sedately', 214 | 'Seemingly', 215 | 'Selfishly', 216 | 'Separately', 217 | 'Seriously', 218 | 'Sheepishly', 219 | 'Smoothly', 220 | 'Solemnly', 221 | 'Sometimes', 222 | 'Speedily', 223 | 'Stealthily', 224 | 'Successfully', 225 | 'Suddenly', 226 | 'Supposedly', 227 | 'Surprisingly', 228 | 'Suspiciously', 229 | 'Sympathetically', 230 | 'Tenderly', 231 | 'Thankfully', 232 | 'Thoroughly', 233 | 'Thoughtfully', 234 | 'Tomorrow', 235 | 'Tremendously', 236 | 'Triumphantly', 237 | 'Truthfully', 238 | 'Ultimately', 239 | 'Upbeat', 240 | 'Upright', 241 | 'Upside-down', 242 | 'Upward', 243 | 'Urgently', 244 | 'Usefully', 245 | 'Uselessly', 246 | 'Usually', 247 | 'Utterly', 248 | 'Vacantly', 249 | 'Vaguely', 250 | 'Vainly', 251 | 'Valiantly', 252 | 'Vastly', 253 | 'Verbally', 254 | 'Very', 255 | 'Viciously', 256 | 'Victoriously', 257 | 'Violently', 258 | 'Vivaciously', 259 | 'Voluntarily', 260 | 'Warmly', 261 | 'Weakly', 262 | 'Wearily', 263 | 'Well', 264 | 'Wholly', 265 | 'Wildly', 266 | 'Willfully', 267 | 'Wisely', 268 | 'Woefully', 269 | 'Wonderfully', 270 | 'Worriedly', 271 | 'Wrongly', 272 | 'Yawningly', 273 | 'Yearly', 274 | 'Yearningly', 275 | 'Yesterday', 276 | 'Yieldingly', 277 | 'Youthfully', 278 | 'Zealously', 279 | 'Zestfully', 280 | 'Zestily' 281 | ]; 282 | 283 | public static second = [ 284 | 'Adorable', 285 | 'Adventurous', 286 | 'Aggressive', 287 | 'Agreeable', 288 | 'Alert', 289 | 'Alive', 290 | 'Amused', 291 | 'Angry', 292 | 'Annoyed', 293 | 'Annoying', 294 | 'Ashamed', 295 | 'Attractive', 296 | 'Average', 297 | 'Awful', 298 | 'Bad', 299 | 'Beautiful', 300 | 'Better', 301 | 'Bewildered', 302 | 'Black', 303 | 'Blue', 304 | 'Blue-eyed', 305 | 'Blushing', 306 | 'Bored', 307 | 'Brainy', 308 | 'Brave', 309 | 'Breakable', 310 | 'Bright', 311 | 'Busy', 312 | 'Calm', 313 | 'Careful', 314 | 'Cautious', 315 | 'Charming', 316 | 'Cheerful', 317 | 'Clean', 318 | 'Clear', 319 | 'Clever', 320 | 'Cloudy', 321 | 'Clumsy', 322 | 'Colorful', 323 | 'Combative', 324 | 'Comfortable', 325 | 'Concerned', 326 | 'Condemned', 327 | 'Confused', 328 | 'Cooperative', 329 | 'Courageous', 330 | 'Crazy', 331 | 'Crowded', 332 | 'Cruel', 333 | 'Curious', 334 | 'Cute', 335 | 'Dangerous', 336 | 'Defiant', 337 | 'Delightful', 338 | 'Determined', 339 | 'Different', 340 | 'Difficult', 341 | 'Distinct', 342 | 'Dizzy', 343 | 'Eager', 344 | 'Easy', 345 | 'Elated', 346 | 'Elegant', 347 | 'Embarrassed', 348 | 'Enchanting', 349 | 'Encouraging', 350 | 'Energetic', 351 | 'Enthusiastic', 352 | 'Envious', 353 | 'Evil', 354 | 'Excited', 355 | 'Expensive', 356 | 'Exuberant', 357 | 'Fair', 358 | 'Faithful', 359 | 'Famous', 360 | 'Fancy', 361 | 'Fantastic', 362 | 'Fierce', 363 | 'Fine', 364 | 'Foolish', 365 | 'Fragile', 366 | 'Frantic', 367 | 'Friendly', 368 | 'Funny', 369 | 'Gentle', 370 | 'Gifted', 371 | 'Glamorous', 372 | 'Gleaming', 373 | 'Glorious', 374 | 'Good', 375 | 'Gorgeous', 376 | 'Graceful', 377 | 'Grumpy', 378 | 'Handsome', 379 | 'Happy', 380 | 'Healthy', 381 | 'Helpful', 382 | 'Hilarious', 383 | 'Hungry', 384 | 'Important', 385 | 'Impossible', 386 | 'Inexpensive', 387 | 'Innocent', 388 | 'Inquisitive', 389 | 'Itchy', 390 | 'Jealous', 391 | 'Jittery', 392 | 'Jolly', 393 | 'Joyous', 394 | 'Kind', 395 | 'Lazy', 396 | 'Light', 397 | 'Lively', 398 | 'Lonely', 399 | 'Long', 400 | 'Lovely', 401 | 'Lucky', 402 | 'Magnificent', 403 | 'Misty', 404 | 'Modern', 405 | 'Motionless', 406 | 'Muddy', 407 | 'Mushy', 408 | 'Mysterious', 409 | 'Nasty', 410 | 'Naughty', 411 | 'Nervous', 412 | 'Nice', 413 | 'Nutty', 414 | 'Obedient', 415 | 'Obnoxious', 416 | 'Odd', 417 | 'Old-fashioned', 418 | 'Open', 419 | 'Outrageous', 420 | 'Outstanding', 421 | 'Panicky', 422 | 'Perfect', 423 | 'Plain', 424 | 'Pleasant', 425 | 'Poised', 426 | 'Powerful', 427 | 'Precious', 428 | 'Prickly', 429 | 'Proud', 430 | 'Puzzled', 431 | 'Quaint', 432 | 'Real', 433 | 'Relieved', 434 | 'Rich', 435 | 'Scary', 436 | 'Selfish', 437 | 'Shiny', 438 | 'Shy', 439 | 'Silly', 440 | 'Sleepy', 441 | 'Smiling', 442 | 'Smoggy', 443 | 'Sore', 444 | 'Sparkling', 445 | 'Splendid', 446 | 'Spotless', 447 | 'Stormy', 448 | 'Strange', 449 | 'Stupid', 450 | 'Successful', 451 | 'Super', 452 | 'Talented', 453 | 'Tame', 454 | 'Tender', 455 | 'Tense', 456 | 'Terrible', 457 | 'Thankful', 458 | 'Thoughtful', 459 | 'Thoughtless', 460 | 'Tired', 461 | 'Tough', 462 | 'Uninterested', 463 | 'Unusual', 464 | 'Vast', 465 | 'Victorious', 466 | 'Vivacious', 467 | 'Wandering', 468 | 'Weary', 469 | 'Wicked', 470 | 'Wide-eyed', 471 | 'Wild', 472 | 'Witty', 473 | 'Worrisome', 474 | 'Worried', 475 | 'Wrong', 476 | 'Zany', 477 | 'Zealous' 478 | ]; 479 | 480 | public static third = [ 481 | 'Turtle', 482 | 'Tortoise', 483 | 'Aardvark', 484 | 'Albatross', 485 | 'Alligator', 486 | 'Angelfish', 487 | 'Anteater', 488 | 'Antelope', 489 | 'Armadillo', 490 | 'Baboon', 491 | 'Badger', 492 | 'Barracuda', 493 | 'Bat', 494 | 'Hound', 495 | 'Beagle', 496 | 'Bear', 497 | 'Beaver', 498 | 'Beetle', 499 | 'Tiger', 500 | 'Bird', 501 | 'Bison', 502 | 'Bloodhound', 503 | 'Bobcat', 504 | 'Buffalo', 505 | 'Bulldog', 506 | 'Bullfrog', 507 | 'Butterfly', 508 | 'Camel', 509 | 'Cat', 510 | 'Caterpillar', 511 | 'Catfish', 512 | 'Chameleon', 513 | 'Cheetah', 514 | 'Chicken', 515 | 'Chihuahua', 516 | 'Chimpanzee', 517 | 'Chinchilla', 518 | 'Chipmunk', 519 | 'Cougar', 520 | 'Coyote', 521 | 'Crab', 522 | 'Crane', 523 | 'Crocodile', 524 | 'Dalmatian', 525 | 'Deer', 526 | 'Dingo', 527 | 'Dog', 528 | 'Dolphin', 529 | 'Donkey', 530 | 'Duck', 531 | 'Eagle', 532 | 'Elephant', 533 | 'Emu', 534 | 'Falcon', 535 | 'Fox', 536 | 'Ferret', 537 | 'Toad', 538 | 'Fish', 539 | 'Flamingo', 540 | 'Flounder', 541 | 'Fox', 542 | 'Lizard', 543 | 'Frog', 544 | 'Gecko', 545 | 'Gerbil', 546 | 'Gibbon', 547 | 'Giraffe', 548 | 'Goat', 549 | 'Goose', 550 | 'Gopher', 551 | 'Gorilla', 552 | 'Grasshopper', 553 | 'Greyhound', 554 | 'Guppy', 555 | 'Hamster', 556 | 'Hare', 557 | 'Hedgehog', 558 | 'Hippopotamus', 559 | 'Horse', 560 | 'Hummingbird', 561 | 'Hyena', 562 | 'Iguana', 563 | 'Jackal', 564 | 'Jaguar', 565 | 'Jellyfish', 566 | 'Kangaroo', 567 | 'Koala', 568 | 'Komodo Dragon', 569 | 'Labradoodle', 570 | 'Lemming', 571 | 'Lemur', 572 | 'Leopard', 573 | 'Liger', 574 | 'Lion', 575 | 'Lizard', 576 | 'Llama', 577 | 'Lobster', 578 | 'Lynx', 579 | 'Macaw', 580 | 'Manatee', 581 | 'Mastiff', 582 | 'Meerkat', 583 | 'Mongoose', 584 | 'Monkey', 585 | 'Moose', 586 | 'Eel', 587 | 'Mountain Lion', 588 | 'Mouse', 589 | 'Mule', 590 | 'Newt', 591 | 'Nightingale', 592 | 'Ocelot', 593 | 'Octopus', 594 | 'Sheepdog', 595 | 'Ostrich', 596 | 'Otter', 597 | 'Panther', 598 | 'Parrot', 599 | 'Peacock', 600 | 'Pelican', 601 | 'Penguin', 602 | 'Pheasant', 603 | 'Piranha', 604 | 'Platypus', 605 | 'Polar Bear', 606 | 'Poodle', 607 | 'Porcupine', 608 | 'Possum', 609 | 'Puffin', 610 | 'Pug', 611 | 'Puma', 612 | 'Hippopotamus', 613 | 'Rabbit', 614 | 'Raccoon', 615 | 'Rat', 616 | 'Rattlesnake', 617 | 'Tarantula', 618 | 'Panda', 619 | 'Wolf', 620 | 'Reindeer', 621 | 'Rhinoceros', 622 | 'Dolphin', 623 | 'Robin', 624 | 'Rottweiler', 625 | 'Penguin', 626 | 'Salamander', 627 | 'Scorpion', 628 | 'Sea Dragon', 629 | 'Sea Lion', 630 | 'Sea Otter', 631 | 'Sea Slug', 632 | 'Sea Squirt', 633 | 'Sea Turtle', 634 | 'Sea Urchin', 635 | 'Seahorse', 636 | 'Seal', 637 | 'Sheep', 638 | 'Shrimp', 639 | 'Husky', 640 | 'Skunk', 641 | 'Sloth', 642 | 'Snail', 643 | 'Snake', 644 | 'Owl', 645 | 'Toad', 646 | 'Sparrow', 647 | 'Bear', 648 | 'Spider Monkey', 649 | 'Squid', 650 | 'Squirrel', 651 | 'Monkey', 652 | 'Starfish', 653 | 'Stingray', 654 | 'Elephant', 655 | 'Rhinoceros', 656 | 'Tiger', 657 | 'Bear', 658 | 'Swan', 659 | 'Owl', 660 | 'Termite', 661 | 'Devil', 662 | 'Mastiff', 663 | 'Tiger', 664 | 'Salamander', 665 | 'Shark', 666 | 'Tortoise', 667 | 'Toucan', 668 | 'Frog', 669 | 'Turkey', 670 | 'Monkey', 671 | 'Vulture', 672 | 'Wallaby', 673 | 'Walrus', 674 | 'Warthog', 675 | 'Wasp', 676 | 'Buffalo', 677 | 'Dragon', 678 | 'Weasel', 679 | 'Corgi', 680 | 'Gorilla', 681 | 'Whippet', 682 | 'Wild Boar', 683 | 'Wildebeest', 684 | 'Wolf', 685 | 'Wolverine', 686 | 'Wombat', 687 | 'Woodpecker', 688 | 'Mammoth', 689 | 'Yak', 690 | 'Penguin', 691 | 'Terrier', 692 | 'Zebra' 693 | ]; 694 | } 695 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/phraseProvider.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | export interface PhraseProvider { 10 | /** 11 | * Returns a word or phrase at random. 12 | * @return Random phrase 13 | */ 14 | getRandomPhrase(): string; 15 | 16 | /** 17 | * Returns a constant word or phrase from the list. This value is moduled with the 18 | * array size so any value can be safely used for the seed. 19 | * @param seed Value whose modulo is used to index the phrase/word list. 20 | * @return Deterministic phrase based on the seed. 21 | */ 22 | getConstantPhrase(seed: number): string; 23 | } 24 | 25 | /** 26 | * Simple word provider that provides words selected from any array provided to the 27 | * class constructor. 28 | */ 29 | export class SimplePhraseProvider implements PhraseProvider { 30 | /** 31 | * Creates a new instance of SimplePhraseProvider 32 | * @param phrases Array of words or phrases that are returned by the provider. 33 | */ 34 | constructor( 35 | private readonly phrases: string[] 36 | ) { 37 | if (!this.phrases || this.phrases.length === 0) { 38 | throw new Error('The parameter phrases cannot be empty.'); 39 | } 40 | } 41 | 42 | public getRandomPhrase(): string { 43 | const randomValue = Math.floor((Math.random() * this.phrases.length)); 44 | return this.getConstantPhrase(randomValue); 45 | } 46 | 47 | public getConstantPhrase(seed: number): string { 48 | return this.phrases[seed % this.phrases.length]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerAvatarUriGenerator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { getHash } from './hashUtility'; 9 | import { DEFAULT_NUMBER_OF_UNIQUE_AVATARS, PlayerAvatarUriGeneratorConfig } from './playerAvatarUriGeneratorConfig'; 10 | 11 | /** 12 | * Returns a URL linking to an avatar. The avatar image must exist as a file at the provided avatarBaseUrl location 13 | * and consist of a number (no leading zeros) ending with .png. For example, if there are 500 avatar images, the 14 | * first one will be 0.png and the last one will be 499.png. 15 | */ 16 | export class PlayerAvatarUriGenerator { 17 | /** 18 | * Creates a new instance of PlayerAvatarUriGenerator 19 | * @param config 20 | */ 21 | constructor(private config: PlayerAvatarUriGeneratorConfig) { 22 | if (!config) { 23 | throw new Error('The parameter config cannot be empty.'); 24 | } 25 | 26 | if (!config.avatarBaseUrl || config.avatarBaseUrl.length === 0) { 27 | throw new Error('The property avatarBaseUrl must be provided.'); 28 | } 29 | 30 | this.config.numberOfUniqueAvatars = this.config.numberOfUniqueAvatars || DEFAULT_NUMBER_OF_UNIQUE_AVATARS; 31 | 32 | if (this.config.numberOfUniqueAvatars < 1) { 33 | throw new Error('The property numberOfUniqueAvatars cannot be less than one.'); 34 | } 35 | } 36 | 37 | /** 38 | * Returns the URL of a randomly selected avatar. 39 | * @return Url of the avatar 40 | */ 41 | public getRandom(): string { 42 | const index = Math.floor(Math.random() * this.config.numberOfUniqueAvatars!); 43 | 44 | return this.composeUrl(index); 45 | } 46 | 47 | /** 48 | * Selects a consistant avatar given an ID. 49 | * @param id Identity used to select a constant name. 50 | * @return Url of avatar 51 | */ 52 | public getFromId(id: string | number): string { 53 | const hash = getHash(id); 54 | 55 | return this.getFromHash(hash); 56 | } 57 | 58 | /** 59 | * Selects a consistant avatar using the supplied hash buffer. 60 | * @param hash Buffer which should contain a 32 bit hash 61 | * of (such as SHA 256) of player's identity. 62 | * @return Url of avatar 63 | */ 64 | public getFromHash(hash: Buffer): string { 65 | // Use the first int in the hash to decide which avatar to select. 66 | const index = (Math.abs(hash.readInt32BE(0)) % this.config.numberOfUniqueAvatars!); 67 | 68 | return this.composeUrl(index); 69 | } 70 | 71 | /** 72 | * Generates a URL based on the supplied index. 73 | * @param index Index indicating which image to use. 74 | */ 75 | private composeUrl(index: number): string { 76 | if (index < 0 || index >= this.config.numberOfUniqueAvatars!) { 77 | throw new Error(`Parameter index is out of range. Must be 0 or larger but less than ${this.config.numberOfUniqueAvatars}`); 78 | } 79 | 80 | let separator = ''; 81 | if (!this.config.avatarBaseUrl.endsWith('/')) { 82 | separator = '/'; 83 | } 84 | 85 | return `${this.config.avatarBaseUrl}${separator}${index % this.config.numberOfUniqueAvatars!}.png`; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerAvatarUriGeneratorConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | export interface PlayerAvatarUriGeneratorConfig { 10 | /** 11 | * URL where the avatar images are hosted. This URL contains the path 12 | * up to, but not including, the image file name. 13 | */ 14 | avatarBaseUrl: string; 15 | 16 | /** 17 | * Number of avatar images available at the supplied URL. If a value is not provided the 18 | * default value in DEFAULT_NUMBER_OF_UNIQUE_AVATARS is used. 19 | */ 20 | numberOfUniqueAvatars?: number; 21 | } 22 | 23 | /** 24 | * Default number of avatar images. 25 | */ 26 | export const DEFAULT_NUMBER_OF_UNIQUE_AVATARS = 500; 27 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerColorGenerator.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | import uuidv4 from 'uuid/v4'; 10 | import { getHash} from './hashUtility'; 11 | import { PlayerNameGeneratorConfig } from './playerNameGeneratorConfig'; 12 | 13 | export class PlayerColorGenerator { 14 | private readonly HEX_COLOR_LENGTH = 6; 15 | /** 16 | * Generates a random color. 17 | */ 18 | public getRandom(): string { 19 | const uuid = uuidv4(); 20 | return this.getFromHash(getHash(uuid)); 21 | } 22 | 23 | /** 24 | * Generates a deterministic color given an Id. 25 | * @param id Identity to generate a constant name from. 26 | */ 27 | public getFromId(id: string | number) { 28 | const hash = getHash(String(id)); 29 | return this.getFromHash(hash); 30 | } 31 | 32 | /** 33 | * Create a player color using the supplied hash buffer. 34 | * @param hash Generates a name from the supplied buffer which should contain a 32 byte hash 35 | * of (such as SHA 256) of player's identity. 36 | */ 37 | public getFromHash(hash: Buffer) { 38 | const hexId = hash.toString('hex'); 39 | // Retrieve the first 6 characters of hex string 40 | return hexId.substr(hexId.length - this.HEX_COLOR_LENGTH); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerNameGenerator.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | import { getHash} from './hashUtility'; 10 | import { PlayerNameGeneratorConfig } from './playerNameGeneratorConfig'; 11 | 12 | /** 13 | * Generates a name based on the provided configuration. The configuration includes the 14 | * minimum and maximum number of phrases that compose the name as well as an array of phrase 15 | * providers. A different phrase provider can be used for each ordinal phrase allowing 16 | * different lists for the first phrase, second phrase, etc. A phrase may be only one 17 | * word long. 18 | */ 19 | export class PlayerNameGenerator { 20 | 21 | private static verifyConfig(config: PlayerNameGeneratorConfig) { 22 | if (!config) { 23 | throw new Error('The parameter config cannot be empty.'); 24 | } 25 | 26 | if (config.minPhrases < 1) { 27 | throw new Error('The property minPhrases must be 1 or higher.'); 28 | } 29 | 30 | if (config.maxPhrases < config.minPhrases) { 31 | throw new Error('The property maxPhrases must be the same as minPhrases or higher.'); 32 | } 33 | 34 | if (!config.phraseProviders || config.phraseProviders.length === 0) { 35 | throw new Error('The property phraseProviders cannot be empty.'); 36 | } 37 | 38 | if (config.phraseProviders.length < config.maxPhrases) { 39 | throw new Error('The property phraseProviders must have as many array elements as the value for maxPhrases.'); 40 | } 41 | } 42 | 43 | constructor(private readonly config: PlayerNameGeneratorConfig) { 44 | PlayerNameGenerator.verifyConfig(this.config); 45 | 46 | // If no formatter is specified, just return the input phrase unmodified. 47 | this.config.formatter = this.config.formatter || ((phrase) => phrase); 48 | } 49 | 50 | /** 51 | * Creates a random name. 52 | */ 53 | public getRandom(): string { 54 | 55 | const MaxPhraseIndex = 65535; 56 | 57 | const phrasesRequired = this.getNumberOfPhrasesInName(); 58 | 59 | const indexList: number[] = []; 60 | 61 | for (let phraseCount = 0; phraseCount < phrasesRequired; phraseCount++) { 62 | indexList.push(this.getRandomInt(0, MaxPhraseIndex)); 63 | } 64 | 65 | return this.createName(indexList); 66 | } 67 | 68 | /** 69 | * Generates a consistant name given an ID. This method works by taking a hash of the 70 | * provided ID and call the getFromHash() method. 71 | * @param id Identity to generate a constant name from. 72 | */ 73 | public getFromId(id: string | number) { 74 | const hash = getHash(String(id)); 75 | 76 | return this.getFromHash(hash); 77 | } 78 | 79 | /** 80 | * Create a player name using the supplied hash buffer. 81 | * @param hash Generates a name from the supplied buffer which should contain a 32 byte hash 82 | * of (such as SHA 256) of player's identity. 83 | */ 84 | public getFromHash(hash: Buffer) { 85 | const BytesPerPhrase = 4; 86 | const maxPhrasesForHash = hash.length / BytesPerPhrase; 87 | 88 | // The hash returns 32 bytes which will support an 8 phrase name. This should be 89 | // plenty, but we verify and throw a helpful error message anyway. 90 | if (maxPhrasesForHash < this.config.maxPhrases) { 91 | throw new Error(`The value for maxPhrases (${this.config.maxPhrases}) ` + 92 | `is too large for this method which supports ${maxPhrasesForHash} phrases.`); 93 | } 94 | 95 | // Use the first int in the hash to decide how many phrases will be in the 96 | // name for this ID. 97 | const phrasesRequired = (Math.abs(hash.readInt32BE(0)) % (this.config.maxPhrases - this.config.minPhrases + 1) + this.config.minPhrases); 98 | const indexList: number[] = []; 99 | 100 | for (let phraseCount = 0; phraseCount < phrasesRequired; phraseCount++) { 101 | const phraseIndex = hash.readInt32BE(phraseCount * BytesPerPhrase); 102 | indexList.push(Math.abs(phraseIndex)); 103 | } 104 | 105 | return this.createName(indexList); 106 | } 107 | 108 | private createName(indexList: number[]): string { 109 | let name = ''; 110 | 111 | for (let phraseCount = 0; phraseCount < indexList.length; phraseCount++) { 112 | const phrase = this.config.phraseProviders[phraseCount].getConstantPhrase(indexList[phraseCount]); 113 | if (phraseCount > 0) { 114 | name += ' '; 115 | } 116 | 117 | name += this.config.formatter!(phrase, phraseCount); 118 | } 119 | 120 | return name; 121 | } 122 | 123 | private getNumberOfPhrasesInName() { 124 | return this.getRandomInt(this.config.minPhrases, this.config.maxPhrases); 125 | } 126 | 127 | private getRandomInt(min: number, max: number) { 128 | return Math.floor(Math.random() * (max - min + 1)) + min; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerNameGeneratorBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { NameLists } from './nameLists'; 9 | import { SimplePhraseProvider } from './phraseProvider'; 10 | import { PlayerNameGenerator } from './playerNameGenerator'; 11 | 12 | import randomWords = require('random-words'); 13 | 14 | /** 15 | * Builder for PlayerNameGenerator. Because there are many ways a PlayerNameGenerator can 16 | * be configured, this builder provides two basic configurations. One provides a name with 17 | * three to four words using the supplied NameLists grouping of words. The other provides a 18 | * two word name using the word list from the random-words package. See each builder 19 | * method for more details. 20 | */ 21 | export class PlayerNameGeneratorBuilder { 22 | 23 | /** 24 | * Returns three words from three built in lists of words. 25 | * @param locale Create the name generator based on the provided locale. 26 | * Unknown locales will default to en-US. Currently, only en-US is 27 | * implemented. 28 | */ 29 | public static getGenerator(locale?: string): PlayerNameGenerator { 30 | 31 | locale = locale || 'en-US'; 32 | 33 | switch (locale) { 34 | // Special unsupported case for unit test support. 35 | case 'XX-UNITTEST': 36 | throw new Error(`Unsupported locale ${locale}`); 37 | case 'en-US': 38 | default: 39 | return new PlayerNameGenerator({ 40 | minPhrases: 3, 41 | maxPhrases: 3, 42 | phraseProviders: [ 43 | new SimplePhraseProvider(NameLists.first), 44 | new SimplePhraseProvider(NameLists.second), 45 | new SimplePhraseProvider(NameLists.third) 46 | ] 47 | }); 48 | } 49 | } 50 | 51 | /** 52 | * Returns two words from the random-words NPM package. 53 | */ 54 | public static getGeneratorRandomWords(): PlayerNameGenerator { 55 | return new PlayerNameGenerator({ 56 | minPhrases: 2, 57 | maxPhrases: 2, 58 | phraseProviders: [ 59 | new SimplePhraseProvider(randomWords.wordList), 60 | new SimplePhraseProvider(randomWords.wordList) 61 | ], 62 | formatter: (word: string, count: number) => { 63 | // Ensure each word starts with a capital letter. 64 | if (word.length > 1) { 65 | word = word.charAt(0).toUpperCase() + word.slice(1); 66 | } 67 | return word; 68 | } 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerNameGeneratorConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { PhraseProvider } from './phraseProvider'; 9 | 10 | export interface PlayerNameGeneratorConfig { 11 | /** 12 | * Shortest name to return. This value must be at least 1. 13 | */ 14 | minPhrases: number; 15 | 16 | /** 17 | * Longest name to return. This value cannot be smaller than mixPhrases. 18 | */ 19 | maxPhrases: number; 20 | 21 | /** 22 | * Array of phrase providers. The number of providers must match maxPhrases. 23 | * The provider position in the array coresponds to the phrase position returned. 24 | * The same provider instance may be used in each array position if the same 25 | * provider should be used for each possible phrase. 26 | */ 27 | phraseProviders: PhraseProvider[]; 28 | 29 | /** 30 | * Optional formatter that gives the caller the ability to modify the word or phrase before 31 | * it is contatinated to the name. 32 | */ 33 | formatter?: (phrase: string, wordIndex: number) => string; 34 | } 35 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerProfile.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | export interface PlayerProfile { 10 | /** 11 | * Display name for the player. 12 | */ 13 | name: string; 14 | 15 | /** 16 | * URL referencing an avatar image assigned to the player. 17 | */ 18 | avatar: string; 19 | 20 | /** 21 | * Hex color code 22 | */ 23 | color: string; 24 | } 25 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerProfileConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { PlayerAvatarUriGenerator } from './playerAvatarUriGenerator'; 9 | import { PlayerNameGenerator } from './playerNameGenerator'; 10 | 11 | export interface PlayerProfileGeneratorConfig { 12 | 13 | /** 14 | * A function that returns an instance of a PlayerNameGenerator 15 | */ 16 | playerNameGeneratorBuilder: () => PlayerNameGenerator; 17 | 18 | /** 19 | * A function that returns an instance of a PlayerAvatarSelector 20 | */ 21 | playerAvatarUriGeneratorBuilder: () => PlayerAvatarUriGenerator; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerProfileGenerator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { getHash } from './hashUtility'; 9 | import { PlayerAvatarUriGenerator } from './playerAvatarUriGenerator'; 10 | import { PlayerColorGenerator } from './playerColorGenerator'; 11 | import { PlayerNameGenerator } from './playerNameGenerator'; 12 | import { PlayerProfile } from './playerProfile'; 13 | import { PlayerProfileGeneratorConfig } from './playerProfileConfig'; 14 | 15 | /** 16 | * Generates an object with a name and avatar property by using the configured 17 | * Player Name Generator and Player Avatar URI generator. 18 | */ 19 | export class PlayerProfileGenerator { 20 | 21 | private readonly nameGenerator: PlayerNameGenerator; 22 | private readonly avatarSelector: PlayerAvatarUriGenerator; 23 | private readonly colorGenerator: PlayerColorGenerator; 24 | 25 | constructor(config: PlayerProfileGeneratorConfig) { 26 | this.nameGenerator = config.playerNameGeneratorBuilder(); 27 | this.avatarSelector = config.playerAvatarUriGeneratorBuilder(); 28 | this.colorGenerator = new PlayerColorGenerator(); 29 | } 30 | 31 | /** 32 | * Generates a profile with the same name, avatar URI, and color for a given ID. 33 | * @param id ID of the player for whom to generate a profile. 34 | */ 35 | public getPlayerProfileFromId(id: number | string): PlayerProfile { 36 | const hash = getHash(id); 37 | 38 | return { 39 | name: this.nameGenerator.getFromHash(hash), 40 | avatar: this.avatarSelector.getFromHash(hash), 41 | color: this.colorGenerator.getFromHash(hash) 42 | }; 43 | } 44 | 45 | /** 46 | * Generates a randomly selected name, avatar URI, and color. 47 | */ 48 | public getRandomPlayerProfile(): PlayerProfile { 49 | return { 50 | name: this.nameGenerator.getRandom(), 51 | avatar: this.avatarSelector.getRandom(), 52 | color: this.colorGenerator.getRandom() 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/player/playerProfileGeneratorBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { PlayerAvatarUriGenerator } from './playerAvatarUriGenerator'; 9 | import { PlayerAvatarUriGeneratorConfig } from './playerAvatarUriGeneratorConfig'; 10 | import { PlayerNameGeneratorBuilder } from './playerNameGeneratorBuilder'; 11 | import { PlayerProfileGenerator } from './playerProfileGenerator'; 12 | 13 | export interface PlayerProfileGeneratorBuilderParams extends PlayerAvatarUriGeneratorConfig { 14 | /** 15 | * Locale to use when generating the player's name. This allows for the possibility of selecting 16 | * words appropriate to the player's locale when selecting a random name. 17 | */ 18 | locale?: string; 19 | } 20 | 21 | export class PlayerProfileGeneratorBuilder { 22 | 23 | /** 24 | * Returns a generator for Display properties of a player. 25 | * 26 | * @param config Configuration specific to the environment 27 | */ 28 | public static getGenerator(config: PlayerProfileGeneratorBuilderParams): PlayerProfileGenerator { 29 | return new PlayerProfileGenerator({ 30 | playerAvatarUriGeneratorBuilder: () => { 31 | return new PlayerAvatarUriGenerator(config); 32 | }, 33 | playerNameGeneratorBuilder: () => { 34 | return PlayerNameGeneratorBuilder.getGenerator(config.locale); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/utils/displayUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import { GetMatchLeaderboardResponseLeaderboardItem } from '@alexa-games/gameon-sdk'; 9 | import { AugmentedPlayer, CombinationLeaderboard, PlayerScore } from '../client/skillsGameOnApiClient'; 10 | import { PlayerProfile } from '../player/playerProfile'; 11 | import { PlayerProfileGenerator } from '../player/playerProfileGenerator'; 12 | import playerRankDoc from './views/player-rank.json'; 13 | 14 | // tslint:disable-next-line: no-var-requires 15 | const ordinal = require('ordinal'); 16 | 17 | /** 18 | * Displayable player for APL rendering 19 | */ 20 | interface DisplayablePlayer { 21 | score: PlayerScore; 22 | profile: PlayerProfile; 23 | isCurrentPlayer: boolean; 24 | } 25 | 26 | interface RenderOptions { 27 | /** 28 | * Url of the background image 29 | * Recommended minimum size: 1280x800px 30 | */ 31 | backgroundImageUrl?: string; 32 | 33 | /** 34 | * Url of the trophy image 35 | * Recommended minimum size: 70x70px 36 | */ 37 | trophyUrl?: string; 38 | 39 | /** 40 | * Url of the logo image 41 | * Recommended minimum size: 170x130px 42 | */ 43 | logoImageUrl?: string; 44 | 45 | /** 46 | * Valid CSS color. Will default to #1da1a3 if not supplied 47 | */ 48 | primaryColor?: string; 49 | 50 | /** 51 | * Valid CSS color. Will default to #66298f if not supplied 52 | */ 53 | secondaryColor?: string; 54 | 55 | /** 56 | * Leaderboard score primary text. Will default to `You placed ${player.score.ordinalRank}!` if not supplied 57 | */ 58 | scorePrimaryText?: string; 59 | 60 | /** 61 | * Leaderboard score secondary text. Will default to `${player.score.score} points` if not supplied 62 | */ 63 | scoreSecondaryText?: string; 64 | } 65 | 66 | /** 67 | * @desc Renders an example leaderboard APL screen document 68 | * 69 | * @param player AugmentedPlayer 70 | * @param combinationLeaderboard Combination leaderboard containing top players and neighboring players 71 | * @param renderOptions rendering options containing image urls and colors 72 | * @param playerProfileGenerator Player profile generator 73 | * @return Alexa APL document directive 74 | */ 75 | export function renderLeaderboard(player: AugmentedPlayer, 76 | combinationLeaderboard: CombinationLeaderboard, 77 | renderOptions: RenderOptions, 78 | playerProfileGenerator: PlayerProfileGenerator): any { 79 | const leaderboard: DisplayablePlayer[] = []; 80 | for (const item of combinationLeaderboard.topNLeaderboard) { 81 | leaderboard.push({ 82 | isCurrentPlayer: !!item.isCurrentPlayer, 83 | profile: getPlayerProfile(item, playerProfileGenerator), 84 | score: getPlayerScore(item) 85 | }); 86 | } 87 | for (const item of combinationLeaderboard.neighborLeaderboard) { 88 | leaderboard.push({ 89 | isCurrentPlayer: !!item.isCurrentPlayer, 90 | profile: getPlayerProfile(item, playerProfileGenerator), 91 | score: getPlayerScore(item) 92 | }); 93 | } 94 | if (!renderOptions.primaryColor) { 95 | renderOptions.primaryColor = '#1da1a3'; 96 | } 97 | if (!renderOptions.secondaryColor) { 98 | renderOptions.secondaryColor = '#66298f'; 99 | } 100 | if (!renderOptions.scorePrimaryText) { 101 | renderOptions.scorePrimaryText = `You placed ${player.score.ordinalRank}!`; 102 | } 103 | if (!renderOptions.scoreSecondaryText) { 104 | renderOptions.scoreSecondaryText = `${player.score.score} points`; 105 | } 106 | 107 | return { 108 | type: 'Alexa.Presentation.APL.RenderDocument', 109 | version: '1.0', 110 | document: playerRankDoc, 111 | datasources: { 112 | data: { 113 | leaderboard, 114 | player, 115 | renderOptions 116 | } 117 | } 118 | }; 119 | } 120 | 121 | export function getPlayerProfile(item: GetMatchLeaderboardResponseLeaderboardItem, playerProfileGenerator: PlayerProfileGenerator): PlayerProfile { 122 | return item.externalPlayerId ? 123 | playerProfileGenerator.getPlayerProfileFromId(item.externalPlayerId) : playerProfileGenerator.getRandomPlayerProfile(); 124 | } 125 | 126 | export function getPlayerScore(item: GetMatchLeaderboardResponseLeaderboardItem): PlayerScore { 127 | return {rank: item.rank || 0, score: item.score, ordinalRank: ordinal(item.rank)}; 128 | } 129 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/src/utils/kmsDecryptionHelper.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | import * as AWS from 'aws-sdk'; 9 | import * as https from 'https'; 10 | 11 | /** 12 | * Utility for decrypting GameOn API key 13 | */ 14 | export class KmsDecryptionHelper { 15 | /** 16 | * Retrieves previously decrypted payload, if cached. Otherwise, makes a call to KMS for decryption 17 | * @param encryptedFileUrl 18 | * @param kmsRegion 19 | * @param forceUpdate 20 | * @return Decrypted object containing GameOn API key 21 | */ 22 | public static async getSecrets(encryptedFileUrl: string, kmsRegion: string, forceUpdate = false): Promise { 23 | if (KmsDecryptionHelper.resolvedSecrets && !forceUpdate) { 24 | return KmsDecryptionHelper.resolvedSecrets; 25 | } 26 | const kms = new AWS.KMS({ 27 | region: kmsRegion 28 | }); 29 | const getEncryptedStream = async () => { 30 | return new Promise((resolve, reject) => { 31 | https.get(encryptedFileUrl, 32 | (res) => { 33 | const data: any[] = []; 34 | res.on('data', (chunk: any) => { 35 | // @ts-ignore 36 | data.push(chunk); 37 | }); 38 | res.on('end', () => { 39 | try { 40 | const returnBuffer = Buffer.concat(data); 41 | resolve(returnBuffer); 42 | } catch (e) { 43 | console.error('Original Exception', e.message); 44 | } 45 | }); 46 | }); 47 | }); 48 | }; 49 | 50 | return new Promise(async (resolve, reject) => { 51 | const stream = (await getEncryptedStream()) as Buffer; 52 | const params = { 53 | CiphertextBlob: stream 54 | }; 55 | kms.decrypt(params, (err, data) => { 56 | if (err) { 57 | reject(err); 58 | } else { 59 | // @ts-ignore 60 | KmsDecryptionHelper.resolvedSecrets = JSON.parse(data.Plaintext.toString()); 61 | resolve(KmsDecryptionHelper.resolvedSecrets); 62 | } 63 | }); 64 | }); 65 | } 66 | private static resolvedSecrets: any; 67 | } 68 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/client/getCombinationLeaderboards.mocks.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-unused-expression 9 | // tslint:disable:no-magic-numbers 10 | 11 | import { GetMatchLeaderboardResponseLeaderboardItem } from '@alexa-games/gameon-sdk'; 12 | import { GetLeaderboardParams } from '../../src/client/skillsGameOnApiClient'; 13 | 14 | export const topScorersLeaderboardResult = { 15 | leaderboard: [ 16 | { 17 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b', 18 | rank: 1, 19 | playerName: 'Bob', 20 | score: 60 21 | }, 22 | { 23 | externalPlayerId: '58f2b0ec-7b89-462d-aee0-82daa115db6b', 24 | rank: 2, 25 | playerName: 'Upright Angry Ferret', 26 | score: 55 27 | }, { 28 | externalPlayerId: 'f6460ffc-d52d-44cc-8d6f-51a1032af15b', 29 | rank: 3, 30 | playerName: 'Knavishly Stormy Macaw', 31 | score: 55 32 | }, { 33 | externalPlayerId: '610dd01c-e408-47a7-9142-20e0d00b7f36', 34 | rank: 4, 35 | playerName: 'Frightfully Jittery Bison', 36 | score: 52 37 | }, { 38 | externalPlayerId: 'a70c7b90-72ee-471d-b0d0-f7e6edb3b6f6', 39 | rank: 5, 40 | playerName: 'closely beautiful Panther', 41 | score: 51 42 | }, { 43 | externalPlayerId: 'f9938294-20ff-476e-ae05-4f6d6e0ba4d1', 44 | rank: 6, 45 | playerName: 'Positively Weary Rhinoceros', 46 | score: 51 47 | }, { 48 | externalPlayerId: '69db99e0-3b42-4ef5-aeb9-ba563b7a6d8a', 49 | rank: 7, 50 | playerName: 'Supposedly Better Horse', 51 | score: 51 52 | }, { 53 | externalPlayerId: '7c2e321e-8cf3-42c7-a1cb-b163e6422230', 54 | rank: 8, 55 | playerName: 'Supposedly Aggressive Dingo', 56 | score: 50 57 | }, { 58 | externalPlayerId: 'adb667b0-ae02-4920-b61c-93123589c9fe', 59 | rank: 9, 60 | playerName: 'Loosely Creepy Wallaby', 61 | score: 45 62 | }, { 63 | externalPlayerId: '275592bd-07b4-4f8f-8450-19963815cd4e', 64 | rank: 10, 65 | playerName: 'Crossly Difficult Starfish', 66 | score: 41 67 | }, { 68 | externalPlayerId: '74a66c29-8ffc-4070-a13f-522cb4af6a51', 69 | rank: 11, 70 | playerName: 'hourly dangerous Alligator', 71 | score: 36 72 | }, { 73 | externalPlayerId: '0edb0e69-9aa6-452b-acb2-59831f4ac418', 74 | rank: 12, 75 | playerName: 'tremendously good Sea Slug', 76 | score: 32 77 | }, { 78 | externalPlayerId: '0af83141-e016-4d02-b083-6a408ef49cad', 79 | rank: 13, 80 | playerName: 'overconfidently cute Stingray', 81 | score: 30 82 | }, { 83 | externalPlayerId: '8b31dba1-aa0a-4b47-9139-cb8a981afa61', 84 | rank: 14, 85 | playerName: 'Painfully Bloody Starfish', 86 | score: 29 87 | }, { 88 | externalPlayerId: 'd228c038-977d-4845-8645-b4217b133985', 89 | rank: 15, 90 | playerName: 'lively tense Bullfrog', 91 | score: 22 92 | }, { 93 | externalPlayerId: '6995ea46-ef04-48d8-9d4f-3a095cb35855', 94 | rank: 16, 95 | playerName: 'Speedily Nice Chihuahua', 96 | score: 22 97 | }, { 98 | externalPlayerId: '2d70edd3-0b4f-4360-b87b-38a4aff72400', 99 | rank: 17, 100 | playerName: 'Longingly Anxious Koala', 101 | score: 19 102 | }, { 103 | externalPlayerId: '5c9f3056-b458-46c7-bde5-ccbb526e4e70', 104 | rank: 18, 105 | playerName: 'Bleakly Bright Goose', 106 | score: 11 107 | }, { 108 | externalPlayerId: '706d2de2-f518-423a-9f75-d386bc04e194', 109 | rank: 19, 110 | playerName: 'player_706d2d', 111 | score: 10 112 | }, { 113 | externalPlayerId: 'f283268b-e014-4d82-973e-0af5f73a467b', 114 | rank: 20, 115 | playerName: 'player_f28326', 116 | score: 10 117 | }, { 118 | externalPlayerId: 'f1d3cbdc-fcf0-4e40-9c7f-ecb9f4ab1256', 119 | rank: 21, 120 | playerName: 'a1f214eb-7427-49fc-b156-ba79e8673d71', 121 | score: 10 122 | }, { 123 | externalPlayerId: '9ddefdb6-0aaf-4a39-beda-6c3bc7829c85', 124 | rank: 22, 125 | playerName: '3d65ca98-21d8-48cc-8f7d-5d89f038e44c', 126 | score: 10 127 | }, { 128 | externalPlayerId: '8155a74a-3e2a-45a8-93f0-f9a06a07ac37', 129 | rank: 23, 130 | playerName: '64bbb2f9-9c69-44da-90a1-25e8df1f4697', 131 | score: 10 132 | }, { 133 | externalPlayerId: '9db33502-8e7b-42e4-ae91-f6b3711ca332', 134 | rank: 24, 135 | playerName: 'd543d566-f05c-41e4-a264-3e151670f6e0', 136 | score: 10 137 | }, { 138 | externalPlayerId: '05b62aab-c691-443e-b124-40cb81d68649', 139 | rank: 25, 140 | playerName: '64bcae62-c3e2-470e-aa56-d39f4f02852b', 141 | score: 10 142 | }], 143 | next: '/matches/someKey' 144 | }; 145 | 146 | export const neighborBoardResponse = { 147 | currentPlayer: { 148 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b', 149 | rank: 1, 150 | playerName: 'Position 1', 151 | score: 60 152 | }, 153 | neighbors: [{ 154 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b', 155 | isCurrentPlayer: true, 156 | rank: 1, 157 | playerName: 'Position 1', 158 | score: 60 159 | }, { 160 | externalPlayerId: '58f2b0ec-7b89-462d-aee0-82daa115db6b', 161 | rank: 2, 162 | playerName: 'Position 2', 163 | score: 55 164 | }, 165 | { 166 | externalPlayerId: 'f6460ffc-d52d-44cc-8d6f-51a1032af15b', 167 | rank: 3, 168 | playerName: 'Position 3', 169 | score: 55 170 | }] 171 | }; 172 | 173 | export const neighborBoardNotIncludedResponse = { 174 | currentPlayer: { 175 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b1', 176 | rank: 26, 177 | playerName: 'Position 1', 178 | score: 60 179 | }, 180 | neighbors: [{ 181 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b1', 182 | isCurrentPlayer: true, 183 | rank: 27, 184 | playerName: 'Position 1', 185 | score: 60 186 | }, { 187 | externalPlayerId: '58f2b0ec-7b89-462d-aee0-82daa115db6b2', 188 | rank: 28, 189 | playerName: 'Position 2', 190 | score: 55 191 | }, 192 | { 193 | externalPlayerId: 'f6460ffc-d52d-44cc-8d6f-51a1032af15b3', 194 | rank: 29, 195 | playerName: 'Position 3', 196 | score: 55 197 | }] 198 | }; 199 | 200 | const getLeaderboardStub = async (params: GetLeaderboardParams) => { 201 | return new Promise((resolve) => { 202 | if (params.currentPlayerNeighbors) { 203 | return resolve({ 204 | currentPlayer: { 205 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b', 206 | rank: 1, 207 | playerName: 'Position 1', 208 | score: 60 209 | }, 210 | neighbors: [{ 211 | externalPlayerId: 'a6dd07a6-1367-4c3a-a950-af399d5cee4b', 212 | isCurrentPlayer: true, 213 | rank: 1, 214 | playerName: 'Position 1', 215 | score: 60 216 | }, { 217 | externalPlayerId: '58f2b0ec-7b89-462d-aee0-82daa115db6b', 218 | rank: 2, 219 | playerName: 'Position 2', 220 | score: 55 221 | }, 222 | { 223 | externalPlayerId: 'f6460ffc-d52d-44cc-8d6f-51a1032af15b', 224 | rank: 3, 225 | playerName: 'Position 3', 226 | score: 55 227 | }] 228 | }); 229 | } 230 | return resolve({ 231 | leaderboard: topScorersLeaderboardResult.leaderboard 232 | .filter((item: GetMatchLeaderboardResponseLeaderboardItem) => { 233 | // @ts-ignore 234 | return item.rank <= 10; 235 | }) 236 | }); 237 | }); 238 | }; 239 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/phraseProvider.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | 10 | import { expect } from 'chai'; 11 | import { SimplePhraseProvider } from '../../src/player/phraseProvider'; 12 | 13 | describe('phraseProvider', () => { 14 | 15 | it('Disallows empty list.', () => { 16 | expect(() => new SimplePhraseProvider([])).to.throw('cannot be empty'); 17 | }); 18 | 19 | it('Gets consistent word back for same input.', () => { 20 | const provider = new SimplePhraseProvider(['a', 'b', 'c']); 21 | 22 | const iterations = 100; 23 | const seed = 10; 24 | const phraseBase = provider.getConstantPhrase(seed); 25 | 26 | let matchCount = 0; 27 | 28 | for (let i = 0; i < iterations; i++) { 29 | const phrase = provider.getConstantPhrase(seed); 30 | if (phrase === phraseBase) { 31 | matchCount++; 32 | } 33 | } 34 | expect(matchCount).to.equal(iterations, 35 | 'We should get the same phrase for the same seed'); 36 | }); 37 | 38 | it('Gets random words back', () => { 39 | const provider = new SimplePhraseProvider(['a', 'b', 'c']); 40 | 41 | const iterations = 100; 42 | const phraseBase = provider.getRandomPhrase(); 43 | 44 | let matchCount = 0; 45 | 46 | for (let i = 0; i < iterations; i++) { 47 | const phrase = provider.getRandomPhrase(); 48 | if (phrase === phraseBase) { 49 | matchCount++; 50 | } 51 | } 52 | expect(matchCount).to.not.equal(iterations, 53 | 'We should not get the same phrase for random calls.'); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/playerAvatarSelector.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | 10 | import { expect } from 'chai'; 11 | import { PlayerAvatarUriGenerator } from '../../src/player/playerAvatarUriGenerator'; 12 | 13 | describe('playerAvatarSelector', () => { 14 | 15 | it('Rejects bad config.', () => { 16 | expect(() => new PlayerAvatarUriGenerator( 17 | { 18 | numberOfUniqueAvatars: -10, 19 | avatarBaseUrl: 'https://example.com/avatars/' 20 | } 21 | )).to.throw('cannot be less than one', 'numberOfUniqueAvatars cannot be less than one.'); 22 | 23 | expect(() => new PlayerAvatarUriGenerator( 24 | { 25 | avatarBaseUrl: '' 26 | } 27 | )).to.throw('The property avatarBaseUrl', 'avatarBaseUrl must be provided.'); 28 | }); 29 | 30 | it('Gets consistent URL back for same ID.', () => { 31 | const selector = new PlayerAvatarUriGenerator({ 32 | avatarBaseUrl: 'https://example.com' 33 | }); 34 | 35 | const id = Math.random() * 1000; 36 | 37 | const iterations = 100; 38 | const urlBase = selector.getFromId(id); 39 | 40 | let matchCount = 0; 41 | 42 | for (let i = 0; i < iterations; i++) { 43 | const url = selector.getFromId(id); 44 | if (url === urlBase) { 45 | matchCount++; 46 | } 47 | } 48 | expect(matchCount).to.equal(iterations, 49 | 'We should get the same URL for the same ID'); 50 | }); 51 | 52 | it('Gets random URls back', () => { 53 | const selector = new PlayerAvatarUriGenerator({ 54 | avatarBaseUrl: 'https://example.com' 55 | }); 56 | 57 | const iterations = 100; 58 | const urlBase = selector.getRandom(); 59 | 60 | let matchCount = 0; 61 | 62 | for (let i = 0; i < iterations; i++) { 63 | const url = selector.getRandom(); 64 | if (url === urlBase) { 65 | matchCount++; 66 | } 67 | } 68 | expect(matchCount).to.not.equal(iterations, 69 | 'We should get different random URLs'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/playerColorGenerator.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | // tslint:disable:no-unused-expression 10 | 11 | import { expect } from 'chai'; 12 | import { getHash } from '../../src/player/hashUtility'; 13 | import { PlayerColorGenerator } from '../../src/player/playerColorGenerator'; 14 | 15 | describe('playerColorGenerator', () => { 16 | it('Gets a valid color back', () => { 17 | const generator = new PlayerColorGenerator(); 18 | const color = generator.getFromHash(getHash('test-1')); 19 | expect(/^[0-9A-F]{6}$/i.test(color)).to.be.true; // regex for valid hex color 20 | }); 21 | 22 | it('Gets consistent color back for same ID.', () => { 23 | const generator = new PlayerColorGenerator(); 24 | const id = Math.random() * 1000; 25 | const iterations = 100; 26 | const colorBase = generator.getFromId(id); 27 | let matchCount = 0; 28 | 29 | for (let i = 0; i < iterations; i++) { 30 | const color = generator.getFromId(id); 31 | expect(/^[0-9A-F]{6}$/i.test(color)).to.be.true; // regex for valid hex color 32 | if (color === colorBase) { 33 | matchCount++; 34 | } 35 | } 36 | expect(matchCount).to.equal(iterations, 37 | 'We should get the same color for the same ID'); 38 | }); 39 | 40 | it('Gets random colors back', () => { 41 | const generator = new PlayerColorGenerator(); 42 | 43 | const iterations = 100; 44 | const colorBase = generator.getRandom(); 45 | 46 | let matchCount = 0; 47 | 48 | for (let i = 0; i < iterations; i++) { 49 | const color = generator.getRandom(); 50 | expect(/^[0-9A-F]{6}$/i.test(color)).to.be.true; // regex for valid hex color 51 | if (color === colorBase) { 52 | matchCount++; 53 | } 54 | } 55 | expect(matchCount).to.not.equal(iterations, 56 | 'We should get different random colors'); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/playerNameGenerator.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | 10 | import { expect } from 'chai'; 11 | import { SimplePhraseProvider } from '../../src/player/phraseProvider'; 12 | import { PlayerNameGenerator } from '../../src/player/playerNameGenerator'; 13 | 14 | const testList1 = ['one', 'two', 'three']; 15 | const provider1 = new SimplePhraseProvider(testList1); 16 | 17 | const testList2 = ['four', 'five', 'siz']; 18 | const provider2 = new SimplePhraseProvider(testList2); 19 | 20 | describe('playerNameGenerator', () => { 21 | 22 | it('Rejects bad config.', () => { 23 | expect(() => new PlayerNameGenerator( 24 | { 25 | maxPhrases: 10, 26 | minPhrases: 1, 27 | phraseProviders: [] 28 | } 29 | )).to.throw('cannot be empty', 'phraseProviders cannot be empty.'); 30 | 31 | expect(() => new PlayerNameGenerator( 32 | { 33 | maxPhrases: 1, 34 | minPhrases: 3, 35 | phraseProviders: [new SimplePhraseProvider(['a'])] 36 | } 37 | )).to.throw('maxPhrases must be the same as minPhrases or higher', 'max must be greater than or equal to min.'); 38 | 39 | expect(() => new PlayerNameGenerator( 40 | { 41 | maxPhrases: 1, 42 | minPhrases: 0, 43 | phraseProviders: [new SimplePhraseProvider(['a'])] 44 | } 45 | )).to.throw('must be 1 or higher.', 'min cannot be less than 1.'); 46 | 47 | expect(() => new PlayerNameGenerator( 48 | { 49 | maxPhrases: 2, 50 | minPhrases: 1, 51 | phraseProviders: [new SimplePhraseProvider(['a'])] 52 | } 53 | )).to.throw('must have as many array elements', 'A phrase provider must be given for each phrase.'); 54 | }); 55 | 56 | it('Gets consistent phrase back for same ID.', () => { 57 | const generator = new PlayerNameGenerator({ 58 | maxPhrases: 2, 59 | minPhrases: 1, 60 | phraseProviders: [provider1, provider2] 61 | }); 62 | 63 | const id = Math.random() * 1000; 64 | 65 | const iterations = 100; 66 | const nameBase = generator.getFromId(id); 67 | 68 | let matchCount = 0; 69 | 70 | for (let i = 0; i < iterations; i++) { 71 | const name = generator.getFromId(id); 72 | if (name === nameBase) { 73 | matchCount++; 74 | } 75 | } 76 | expect(matchCount).to.equal(iterations, 77 | 'We should get the same name for the same ID'); 78 | }); 79 | 80 | it('Gets random names back', () => { 81 | const generator = new PlayerNameGenerator({ 82 | maxPhrases: 2, 83 | minPhrases: 1, 84 | phraseProviders: [provider1, provider1] 85 | }); 86 | 87 | const iterations = 100; 88 | const nameBase = generator.getRandom(); 89 | 90 | let matchCount = 0; 91 | 92 | for (let i = 0; i < iterations; i++) { 93 | const name = generator.getRandom(); 94 | if (name === nameBase) { 95 | matchCount++; 96 | } 97 | } 98 | expect(matchCount).to.not.equal(iterations, 99 | 'We should get different random names'); 100 | }); 101 | 102 | it('Always gets two words back.', () => { 103 | 104 | const ExpectedWordCount = 2; 105 | 106 | const generator = new PlayerNameGenerator({ 107 | maxPhrases: ExpectedWordCount, 108 | minPhrases: ExpectedWordCount, 109 | phraseProviders: [provider1, provider1] 110 | }); 111 | 112 | const iterations = 100; 113 | 114 | for (let i = 0; i < iterations; i++) { 115 | const name = generator.getRandom(); 116 | const parts = name.split(' '); 117 | expect(parts.length).equal(ExpectedWordCount, 118 | 'Should get same number of words back due to config with matchin min/max.'); 119 | } 120 | }); 121 | 122 | it('Gets random number of words back', () => { 123 | const generator = new PlayerNameGenerator({ 124 | maxPhrases: 2, 125 | minPhrases: 1, 126 | phraseProviders: [provider1, provider1] 127 | }); 128 | 129 | const iterations = 100; 130 | const nameBase = generator.getRandom(); 131 | const partsBase = nameBase.split(' '); 132 | 133 | let matchCount = 0; 134 | 135 | for (let i = 0; i < iterations; i++) { 136 | const name = generator.getRandom(); 137 | const parts = name.split(' '); 138 | 139 | if (parts.length === partsBase.length) { 140 | matchCount++; 141 | } 142 | } 143 | expect(matchCount).to.not.equal(iterations, 144 | 'We should get different random number of words in the names'); 145 | }); 146 | 147 | it('Word formatter works', () => { 148 | const generator = new PlayerNameGenerator({ 149 | maxPhrases: 2, 150 | minPhrases: 2, 151 | phraseProviders: [provider1, provider1], 152 | formatter: (word: string, count: number) => { 153 | return String(count); 154 | } 155 | }); 156 | 157 | const iterations = 100; 158 | let matchCount = 0; 159 | 160 | for (let i = 0; i < iterations; i++) { 161 | const name = generator.getRandom(); 162 | if (name === '0 1') { 163 | matchCount++; 164 | } 165 | } 166 | expect(matchCount).to.equal(iterations, 167 | 'We should get all 0 1'); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/playerNameGeneratorBuilder.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable:no-magic-numbers 9 | 10 | import { expect } from 'chai'; 11 | import { PlayerNameGeneratorBuilder } from '../../src/player/playerNameGeneratorBuilder'; 12 | 13 | describe('playerNameGeneratorBuilder', () => { 14 | 15 | it('Get basic three word generator.', () => { 16 | const generator = PlayerNameGeneratorBuilder.getGenerator('en-US'); 17 | 18 | const ExpectedMinWordCount = 3; 19 | const ExpectedMaxWordCount = 4; 20 | 21 | const iterations = 100; 22 | 23 | for (let i = 0; i < iterations; i++) { 24 | const name = generator.getRandom(); 25 | const parts = name.split(' '); 26 | expect(parts.length).not.lessThan(ExpectedMinWordCount, 27 | `Should get at least ${ExpectedMinWordCount} words back. name=${name}`); 28 | expect(parts.length).not.greaterThan(ExpectedMaxWordCount, 29 | `Should get no more than ${ExpectedMaxWordCount} words back. name=${name}`); 30 | } 31 | }); 32 | 33 | it('Get random two word generator.', () => { 34 | const generator = PlayerNameGeneratorBuilder.getGeneratorRandomWords(); 35 | 36 | const ExpectedWordCount = 2; 37 | 38 | const iterations = 100; 39 | 40 | for (let i = 0; i < iterations; i++) { 41 | const name = generator.getRandom(); 42 | const parts = name.split(' '); 43 | expect(parts.length).equal(ExpectedWordCount, 44 | 'Should get two words back.'); 45 | 46 | for (const word of parts) { 47 | expect(word.substr(0, 1)).equals(word.substr(0, 1).toUpperCase(), 48 | 'All words should have an initial upper case letter.'); 49 | } 50 | } 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/player/playerProfileGenerator.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * SPDX-License-Identifier: Apache-2.0 6 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 | */ 8 | 9 | // tslint:disable:no-magic-numbers 10 | 11 | import { expect } from 'chai'; 12 | import { PlayerProfileGeneratorBuilder } from '../../src/player/playerProfileGeneratorBuilder'; 13 | 14 | describe('displayPlayerGeneratorBuilder', () => { 15 | 16 | it('Get a display player generator builder.', () => { 17 | const generator = PlayerProfileGeneratorBuilder.getGenerator({ 18 | avatarBaseUrl: 'https://example.com/avatars', 19 | numberOfUniqueAvatars: 1000 20 | }); 21 | 22 | const player = generator.getPlayerProfileFromId(5); 23 | 24 | // tslint:disable-next-line:no-unused-expression 25 | expect(player.name).is.not.empty; 26 | // tslint:disable-next-line:no-unused-expression 27 | expect(player.avatar).is.not.empty; 28 | }); 29 | 30 | it('Get a display player generator builder in English for fr-FR.', () => { 31 | const generatorEnUs = PlayerProfileGeneratorBuilder.getGenerator({ 32 | avatarBaseUrl: 'https://example.com/avatars', 33 | numberOfUniqueAvatars: 1000, 34 | locale: 'en-US' 35 | }); 36 | 37 | const playerId = 5; 38 | 39 | const playerEnUs = generatorEnUs.getPlayerProfileFromId(playerId); 40 | 41 | const generatorFrFr = PlayerProfileGeneratorBuilder.getGenerator({ 42 | avatarBaseUrl: 'https://example.com/avatars', 43 | numberOfUniqueAvatars: 1000, 44 | locale: 'fr-FR' 45 | }); 46 | 47 | const playerFrFr = generatorFrFr.getPlayerProfileFromId(playerId); 48 | 49 | expect(playerEnUs.name).equals(playerFrFr.name, 'French defaults to English and gets same name.'); 50 | }); 51 | 52 | it('Get an exception for bad locale.', () => { 53 | expect(() => { return PlayerProfileGeneratorBuilder.getGenerator({ 54 | avatarBaseUrl: 'https://example.com/avatars', 55 | numberOfUniqueAvatars: 1000, 56 | locale: 'XX-UNITTEST' 57 | }); }).to.throw('Unsupported locale', 'Expected this special test locale to throw.'); 58 | }); 59 | 60 | it('Gets random display data back', () => { 61 | const generator = PlayerProfileGeneratorBuilder.getGenerator({ 62 | avatarBaseUrl: 'https://example.com/avatars', 63 | numberOfUniqueAvatars: 1000 64 | }); 65 | 66 | const iterations = 100; 67 | const playerBase = generator.getRandomPlayerProfile(); 68 | 69 | let matchNameCount = 0; 70 | let matchAvatarCount = 0; 71 | 72 | for (let i = 0; i < iterations; i++) { 73 | const player = generator.getRandomPlayerProfile(); 74 | 75 | if (player.name === playerBase.name) { 76 | matchNameCount++; 77 | } 78 | 79 | if (player.avatar === playerBase.avatar) { 80 | matchAvatarCount++; 81 | } 82 | } 83 | 84 | expect(matchNameCount).to.not.equal(iterations, 85 | 'We should get different names'); 86 | expect(matchAvatarCount).to.not.equal(iterations, 87 | 'We should get different avatars'); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/test/utils/displayUtils.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // tslint:disable: no-magic-numbers 9 | import { expect } from 'chai'; 10 | import { 11 | AugmentedPlayer, 12 | CombinationLeaderboard, 13 | getPlayerProfile, 14 | getPlayerScore, 15 | PlayerProfileGeneratorBuilder, 16 | renderLeaderboard 17 | } from '../../src'; 18 | 19 | describe('displayUtils', () => { 20 | let generator; 21 | beforeEach(() => { 22 | const config = { 23 | avatarBaseUrl: '/', 24 | numberOfUniqueAvatars: 50 25 | }; 26 | 27 | generator = PlayerProfileGeneratorBuilder.getGenerator({ 28 | ...config, 29 | locale: 'en-US' 30 | }); 31 | }); 32 | it('Gets a player profile', async () => { 33 | const item = { 34 | externalPlayerId: '123-abc' 35 | }; 36 | 37 | // @ts-ignore 38 | const player = getPlayerProfile(item, generator); 39 | expect(player.name).to.eq('Energetically Fierce Crocodile'); 40 | expect(player.avatar).to.eq('/38.png'); 41 | expect(player.color).to.eq('3c42f0'); 42 | delete item.externalPlayerId; 43 | // This should never happen, but the method does support it. 44 | // @ts-ignore 45 | const player2 = getPlayerProfile(item, generator); 46 | expect(player2.name.length).to.be.greaterThan(0); 47 | expect(player2.avatar.length).to.be.greaterThan(0); 48 | expect(player2.color.length).to.be.greaterThan(0); 49 | }); 50 | 51 | it('Gets a player score', async () => { 52 | const item = { 53 | score: 5, 54 | rank: 2 55 | }; 56 | const score = getPlayerScore(item); 57 | expect(score.score).to.eq(5); 58 | expect(score.rank).to.eq(2); 59 | expect(score.ordinalRank).to.eq('2nd'); 60 | }); 61 | 62 | describe('Generates APL directives', () => { 63 | const renderOptions = { 64 | backgroundImageUrl: '/background.png', 65 | trophyUrl: '/trophy.png', 66 | logoImageUrl: '/logo.png', 67 | primaryColor: '#abc123', 68 | secondaryColor: '#123abc' 69 | }; 70 | it('Single player leaderboard', async () => { 71 | const player = { 72 | score: { 73 | rank: 1, 74 | score: 22, 75 | ordinalRank: '1st' 76 | }, 77 | profile: { 78 | name: 'Patiently Plain Lynx', 79 | avatar: '/37.png', 80 | color: 'cef2a0' 81 | }, 82 | sessionApiKey: '5155ce02-1111-1111-2222-71882d1a52ad', 83 | sessionId: '553c5f49-1111-1111-2222-fb2e5d43c0b5', 84 | sessionExpirationDate: 0, 85 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 86 | playerToken: '0774aba7-1111-1111-2222-c2355aebf8c8' 87 | } as AugmentedPlayer; 88 | 89 | const combinationLeaderboard = { 90 | topNLeaderboard: [ 91 | { 92 | externalPlayerId: player.externalPlayerId, 93 | score: player.score.score, 94 | rank: player.score.rank 95 | } 96 | ], 97 | neighborLeaderboard: [] 98 | } as CombinationLeaderboard; 99 | 100 | const expectedLeaderboard = [{ 101 | isCurrentPlayer: false, 102 | profile: 103 | { 104 | name: 'Patiently Plain Lynx', 105 | avatar: '/37.png', 106 | color: 'cef2a0' 107 | }, 108 | score: {rank: 1, score: 22, ordinalRank: '1st'} 109 | }]; 110 | const leaderboardApl = renderLeaderboard(player, combinationLeaderboard, renderOptions, generator); 111 | expect(leaderboardApl.datasources.data.player).to.eq(player); 112 | expect(leaderboardApl.datasources.data.renderOptions).to.eq(renderOptions); 113 | expect(leaderboardApl.datasources.data.renderOptions.scorePrimaryText).to.eq(`You placed ${player.score.ordinalRank}!`); 114 | expect(leaderboardApl.datasources.data.renderOptions.scoreSecondaryText).to.eq(`${player.score.score} points`); 115 | expect(leaderboardApl.datasources.data.leaderboard).to.deep.eq(expectedLeaderboard); 116 | }); 117 | 118 | it('Single player leaderboard with no render options', async () => { 119 | const player = { 120 | score: { 121 | rank: 1, 122 | score: 22, 123 | ordinalRank: '1st' 124 | }, 125 | profile: { 126 | name: 'Patiently Plain Lynx', 127 | avatar: '/37.png', 128 | color: 'cef2a0' 129 | }, 130 | sessionApiKey: '5155ce02-1111-1111-2222-71882d1a52ad', 131 | sessionId: '553c5f49-1111-1111-2222-fb2e5d43c0b5', 132 | sessionExpirationDate: 0, 133 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 134 | playerToken: '0774aba7-1111-1111-2222-c2355aebf8c8' 135 | } as AugmentedPlayer; 136 | 137 | const combinationLeaderboard = { 138 | topNLeaderboard: [ 139 | { 140 | externalPlayerId: player.externalPlayerId, 141 | score: player.score.score, 142 | rank: player.score.rank 143 | } 144 | ], 145 | neighborLeaderboard: [] 146 | } as CombinationLeaderboard; 147 | 148 | const expectedLeaderboard = [{ 149 | isCurrentPlayer: false, 150 | profile: 151 | { 152 | name: 'Patiently Plain Lynx', 153 | avatar: '/37.png', 154 | color: 'cef2a0' 155 | }, 156 | score: {rank: 1, score: 22, ordinalRank: '1st'} 157 | }]; 158 | const leaderboardApl = renderLeaderboard(player, combinationLeaderboard, {}, generator); 159 | expect(leaderboardApl.datasources.data.player).to.eq(player); 160 | expect(leaderboardApl.datasources.data.renderOptions.primaryColor).to.eq('#1da1a3'); 161 | expect(leaderboardApl.datasources.data.renderOptions.secondaryColor).to.eq('#66298f'); 162 | expect(leaderboardApl.datasources.data.leaderboard).to.deep.eq(expectedLeaderboard); 163 | }); 164 | 165 | it('Top 3 player leaderboard', async () => { 166 | const player = { 167 | score: { 168 | rank: 2, 169 | score: 22, 170 | ordinalRank: '2nd' 171 | }, 172 | profile: { 173 | name: 'Patiently Plain Lynx', 174 | avatar: '/37.png', 175 | color: 'cef2a0' 176 | }, 177 | sessionApiKey: '5155ce02-1111-1111-2222-71882d1a52ad', 178 | sessionId: '553c5f49-1111-1111-2222-fb2e5d43c0b5', 179 | sessionExpirationDate: 0, 180 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 181 | playerToken: '0774aba7-1111-1111-2222-c2355aebf8c8' 182 | } as AugmentedPlayer; 183 | 184 | const combinationLeaderboard = { 185 | topNLeaderboard: [ 186 | { 187 | externalPlayerId: 'a6dd07a6-1111-1111-1111-af399d5cee4b', 188 | rank: 1, 189 | playerName: 'Loyally Super Squid', 190 | score: 25 191 | }, 192 | { 193 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 194 | rank: 2, 195 | playerName: 'Patiently Plain Lynx', 196 | score: 22, 197 | isCurrentPlayer: true 198 | }, 199 | { 200 | externalPlayerId: 'f6460ffc-1111-1111-1111-51a1032af15b', 201 | rank: 3, 202 | playerName: 'Woefully Plain Salamander', 203 | score: 20 204 | } 205 | ], 206 | neighborLeaderboard: [] 207 | } as CombinationLeaderboard; 208 | 209 | const expectedLeaderboard = [{ 210 | isCurrentPlayer: false, 211 | profile: 212 | { 213 | name: 'Loyally Super Squid', 214 | avatar: '/20.png', 215 | color: '386317' 216 | }, 217 | score: {rank: 1, score: 25, ordinalRank: '1st'} 218 | }, 219 | { 220 | isCurrentPlayer: true, 221 | profile: 222 | { 223 | name: 'Patiently Plain Lynx', 224 | avatar: '/37.png', 225 | color: 'cef2a0' 226 | }, 227 | score: {rank: 2, score: 22, ordinalRank: '2nd'} 228 | }, 229 | { 230 | isCurrentPlayer: false, 231 | profile: 232 | { 233 | name: 'Woefully Plain Salamander', 234 | avatar: '/21.png', 235 | color: 'fe8a43' 236 | }, 237 | score: {rank: 3, score: 20, ordinalRank: '3rd'} 238 | }]; 239 | 240 | const leaderboardApl = renderLeaderboard(player, combinationLeaderboard, renderOptions, generator); 241 | expect(leaderboardApl.datasources.data.player).to.eq(player); 242 | expect(leaderboardApl.datasources.data.renderOptions).to.eq(renderOptions); 243 | expect(leaderboardApl.datasources.data.leaderboard).to.deep.eq(expectedLeaderboard); 244 | }); 245 | 246 | it('Combined leaderboard', async () => { 247 | const player = { 248 | score: { 249 | rank: 9, 250 | score: 13, 251 | ordinalRank: '9th' 252 | }, 253 | profile: { 254 | name: 'Patiently Plain Lynx', 255 | avatar: '/37.png', 256 | color: 'cef2a0' 257 | }, 258 | sessionApiKey: '5155ce02-1111-1111-2222-71882d1a52ad', 259 | sessionId: '553c5f49-1111-1111-2222-fb2e5d43c0b5', 260 | sessionExpirationDate: 0, 261 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 262 | playerToken: '0774aba7-1111-1111-2222-c2355aebf8c8' 263 | } as AugmentedPlayer; 264 | 265 | const combinationLeaderboard = { 266 | topNLeaderboard: [ 267 | { 268 | externalPlayerId: 'a6dd07a6-1111-1111-1111-af399d5cee4b', 269 | rank: 1, 270 | playerName: 'Loyally Super Squid', 271 | score: 25 272 | }, 273 | { 274 | externalPlayerId: '7c2e321e-1111-1111-1111-b163e6422230', 275 | rank: 2, 276 | playerName: 'Supposedly Aggressive Dingo', 277 | score: 22 278 | }, 279 | { 280 | externalPlayerId: 'f6460ffc-1111-1111-1111-51a1032af15b', 281 | rank: 3, 282 | playerName: 'Woefully Plain Salamander', 283 | score: 20 284 | } 285 | ], 286 | neighborLeaderboard: [ 287 | { 288 | externalPlayerId: 'adb667b0-1111-1111-1111-93123589c9fe', 289 | rank: 8, 290 | playerName: 'Loosely Creepy Wallaby', 291 | score: 15 292 | }, 293 | { 294 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 295 | rank: 9, 296 | playerName: 'Patiently Plain Lynx', 297 | score: 13, 298 | isCurrentPlayer: true 299 | }, 300 | { 301 | externalPlayerId: '275592bd-1111-1111-1111-19963815cd4e', 302 | rank: 10, 303 | playerName: 'Crossly Difficult Starfish', 304 | score: 10 305 | } 306 | ] 307 | } as CombinationLeaderboard; 308 | 309 | const expectedLeaderboard = [{ 310 | isCurrentPlayer: false, 311 | profile: 312 | { 313 | name: 'Loyally Super Squid', 314 | avatar: '/20.png', 315 | color: '386317' 316 | }, 317 | score: {rank: 1, score: 25, ordinalRank: '1st'} 318 | }, 319 | { 320 | isCurrentPlayer: false, 321 | profile: 322 | { 323 | name: 'Limply Angry Baboon', 324 | avatar: '/35.png', 325 | color: 'fdbd65' 326 | }, 327 | score: {rank: 2, score: 22, ordinalRank: '2nd'} 328 | }, 329 | { 330 | isCurrentPlayer: false, 331 | profile: 332 | { 333 | name: 'Woefully Plain Salamander', 334 | avatar: '/21.png', 335 | color: 'fe8a43' 336 | }, 337 | score: {rank: 3, score: 20, ordinalRank: '3rd'} 338 | }, 339 | { 340 | isCurrentPlayer: false, 341 | profile: 342 | { 343 | name: 'Joyously Fair Sheepdog', 344 | avatar: '/14.png', 345 | color: '5acc04' 346 | }, 347 | score: {rank: 8, score: 15, ordinalRank: '8th'} 348 | }, 349 | { 350 | isCurrentPlayer: true, 351 | profile: 352 | { 353 | name: 'Patiently Plain Lynx', 354 | avatar: '/37.png', 355 | color: 'cef2a0' 356 | }, 357 | score: {rank: 9, score: 13, ordinalRank: '9th'} 358 | }, 359 | { 360 | isCurrentPlayer: false, 361 | profile: 362 | { 363 | name: 'Vastly Jittery Bullfrog', 364 | avatar: '/11.png', 365 | color: '103e97' 366 | }, 367 | score: {rank: 10, score: 10, ordinalRank: '10th'} 368 | }]; 369 | 370 | const leaderboardApl = renderLeaderboard(player, combinationLeaderboard, renderOptions, generator); 371 | expect(leaderboardApl.datasources.data.player).to.eq(player); 372 | expect(leaderboardApl.datasources.data.renderOptions).to.eq(renderOptions); 373 | expect(leaderboardApl.datasources.data.leaderboard).to.deep.eq(expectedLeaderboard); 374 | }); 375 | 376 | it('Single player leaderboard with custom Leaderboard text', async () => { 377 | const scorePrimaryText = 'You are in the top 1%'; 378 | const scoreSecondaryText = '8 stars'; 379 | 380 | const updatedTextRenderOption = { 381 | ...renderOptions, 382 | scorePrimaryText, 383 | scoreSecondaryText 384 | }; 385 | 386 | const player = { 387 | score: { 388 | rank: 1, 389 | score: 22, 390 | ordinalRank: '1st' 391 | }, 392 | profile: { 393 | name: 'Patiently Plain Lynx', 394 | avatar: '/37.png', 395 | color: 'cef2a0' 396 | }, 397 | sessionApiKey: '5155ce02-1111-1111-2222-71882d1a52ad', 398 | sessionId: '553c5f49-1111-1111-2222-fb2e5d43c0b5', 399 | sessionExpirationDate: 0, 400 | externalPlayerId: 'a99c89bd-1111-1111-2222-671b012cd1c4', 401 | playerToken: '0774aba7-1111-1111-2222-c2355aebf8c8' 402 | } as AugmentedPlayer; 403 | 404 | const combinationLeaderboard = { 405 | topNLeaderboard: [ 406 | { 407 | externalPlayerId: player.externalPlayerId, 408 | score: player.score.score, 409 | rank: player.score.rank 410 | } 411 | ], 412 | neighborLeaderboard: [] 413 | } as CombinationLeaderboard; 414 | 415 | const leaderboardApl = renderLeaderboard(player, combinationLeaderboard, updatedTextRenderOption, generator); 416 | expect(leaderboardApl.datasources.data.renderOptions.scorePrimaryText).to.eq(scorePrimaryText); 417 | expect(leaderboardApl.datasources.data.renderOptions.scoreSecondaryText).to.eq(scoreSecondaryText); 418 | }); 419 | 420 | }); 421 | 422 | }); 423 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/tsconfig.compile.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "declaration": true 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "exclude": [ 11 | "node_modules", 12 | "test" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/skills-gameon-sdk/tsconfig.lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*", "test/**/*"], 4 | "exclude": ["node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /samples/word-word-lite/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "browser": false, 5 | "node": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "globals": { 11 | "Buffer": true, 12 | "escape": true 13 | }, 14 | "rules": { 15 | "quotes": [2, "single"], 16 | "strict": 0, 17 | "eol-last": ["error", "always"], 18 | "curly": 0, 19 | "no-empty": 0, 20 | "no-underscore-dangle": 0, 21 | "new-cap": 0, 22 | "dot-notation": 0, 23 | "no-use-before-define": 0, 24 | "semi": [2, "always"], 25 | "keyword-spacing": [2, {"before": true, "after": true}], 26 | "space-before-blocks": [2, "always"], 27 | "no-trailing-spaces": 2, 28 | "space-unary-ops": 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /samples/word-word-lite/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 | -------------------------------------------------------------------------------- /samples/word-word-lite/NOTICE: -------------------------------------------------------------------------------- 1 | Alexa Games Sample Code 2 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /samples/word-word-lite/README.md: -------------------------------------------------------------------------------- 1 | ## Code Sample: Word Word Lite 2 | 3 | * For information on getting started, see the samples section of the Skills GameOn SDK Technical Documentation: https://skills-gameon-sdk.github.io/#samples 4 | -------------------------------------------------------------------------------- /samples/word-word-lite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "word-word-lite", 3 | "description": "Lite version of the Word Word Alexa Skill that showcases the Skills GameOn SDK for Node.js", 4 | "private": true, 5 | "keywords": [ 6 | "alexa", 7 | "game", 8 | "skill" 9 | ], 10 | "author": "Amazon", 11 | "license": "SEE LICENSE IN LICENSE", 12 | "scripts": { 13 | "lint": "eslint src" 14 | }, 15 | "dependencies": { 16 | "@alexa-games/skills-gameon-sdk": "^0.1.0", 17 | "ask-sdk": "2.519.0" 18 | }, 19 | "devDependencies": { 20 | "eslint": "^6.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/word-word-lite/skill.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest": { 3 | "publishingInformation": { 4 | "locales": { 5 | "en-US": { 6 | "summary": "Repeat the prompted word as many times as possible", 7 | "examplePhrases": [ 8 | "Alexa open word word lite", 9 | "open word word lite", 10 | "word word lite" 11 | ], 12 | "name": "Word Word Lite", 13 | "description": "Skill inspired by Word Word" 14 | } 15 | }, 16 | "isAvailableWorldwide": true, 17 | "testingInstructions": "", 18 | "category": "GAMES", 19 | "distributionCountries": [] 20 | }, 21 | "apis": { 22 | "custom": { 23 | "endpoint": { 24 | "sourceDir": "lambda/custom", 25 | "uri": "ask-custom-SampleSkill-default" 26 | }, 27 | "interfaces": [ 28 | { 29 | "type": "ALEXA_PRESENTATION_APL" 30 | } 31 | ] 32 | } 33 | }, 34 | "manifestVersion": "1.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /samples/word-word-lite/src/gameOn.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | const sdk = require('@alexa-games/skills-gameon-sdk'); 9 | const settings = require('./settings.js'); 10 | 11 | const defaultClient = new sdk.SkillsGameOnApiClient(); 12 | const generator = sdk.PlayerProfileGeneratorBuilder.getGenerator({ 13 | locale: 'en-US', 14 | avatarBaseUrl: settings.gameAvatarBaseUrl, 15 | numberOfUniqueAvatars: settings.numberOfUniqueAvatars 16 | }); 17 | 18 | /** 19 | * Initializes a new player with added profile info (name, avatar, color) 20 | * 21 | * @param {SkillsGameOnApiClient} [client=defaultClient] 22 | * @returns {AugmentedPlayer} 23 | */ 24 | async function newPlayer(client = defaultClient) { 25 | let alexaPlayer = await client.initializeNewAugmentedPlayer({ 26 | gameApiKey: settings.gameOnApiKey, 27 | appBuildType: settings.appBuildType, 28 | playerProfileGenerator: generator 29 | }); 30 | await client.enterTournamentForPlayer({ 31 | tournamentId: settings.tournamentId, 32 | player: alexaPlayer 33 | }); 34 | return alexaPlayer; 35 | } 36 | 37 | /** 38 | * Looks up the player profile by the external player id 39 | * @param {String} externalPlayerId 40 | * @returns {PlayerProfile} 41 | */ 42 | function lookupPlayerProfile(externalPlayerId) { 43 | const profile = generator.getPlayerProfileFromId(externalPlayerId); 44 | return profile; 45 | } 46 | 47 | /** 48 | * Enter the match for a player. Assumes match and tournament are persistent / eternal. 49 | * This is the most simple use case. If you are implementing recurring leaderboards e.g. daily or monthly, 50 | * it is recommended to use SkillsGameOnApiClient.getTournamentsByTitle to retrieve the ephemeral tournamentId 51 | * and SkillsGameOnApiClient.getMatchListForPlayer to retrieve the ephemeral matchId. 52 | * 53 | * @param {Player} alexaPlayer 54 | * @param {SkillsGameOnApiClient} [client=defaultClient] 55 | * @returns {EnterMatchResponse} 56 | */ 57 | async function enterMatch(alexaPlayer, client = defaultClient) { 58 | return await client.enterMatchForPlayer({ 59 | matchId: settings.matchId, 60 | player: alexaPlayer 61 | }); 62 | } 63 | 64 | /** 65 | * Submits score for player. Ensures session has not expired before submission. 66 | * NOTE: stats can also be submitted in the sdk method, but we are not showcasing that here. 67 | * 68 | * @param {Player} alexaPlayer 69 | * @param {Number} score 70 | * @param {SkillsGameOnApiClient} [client=defaultClient] 71 | * @returns {Player} 72 | */ 73 | async function submitScore(alexaPlayer, score, client = defaultClient) { 74 | await client.submitScoreForPlayer({ 75 | matchId: settings.matchId, 76 | submitScoreRequest: { score }, 77 | player: alexaPlayer, 78 | ensureMatchEntered: true 79 | }); 80 | return alexaPlayer; 81 | } 82 | 83 | /** 84 | * Retrieves the player's PlayerScore. The PlayerScore is scoped to a particular matchId and contains the 85 | * players rank, score, and ordinalRank e.g. first, second, third, etc. 86 | * 87 | * @param {Player} alexaPlayer 88 | * @param {SkillsGameOnApiClient} [client=defaultClient] 89 | * @returns {PlayerScore} 90 | */ 91 | async function getPlayerScore(alexaPlayer, client = defaultClient) { 92 | return await client.getPlayerScore( 93 | settings.matchId, 94 | alexaPlayer); 95 | } 96 | 97 | /** 98 | * Refresh a player session by retrieving a new sessionId and sessionApiKey from GameOn. 99 | * If the session has not expired, then do nothing. 100 | * 101 | * @param {Player} alexaPlayer 102 | * @param {SkillsGameOnApiClient} [client=defaultClient] 103 | * @returns {Player} 104 | */ 105 | async function refreshPlayerSession(alexaPlayer, client = defaultClient) { 106 | alexaPlayer = await client.refreshPlayerSession({ 107 | gameApiKey: settings.gameOnApiKey, 108 | appBuildType: settings.appBuildType, 109 | player: alexaPlayer 110 | }); 111 | return alexaPlayer; 112 | } 113 | 114 | /** 115 | * Retrieve a rendered leaderboard APL document. This function assumes that you always want the score and rank 116 | * stored with GameOn. If you configure the leaderboard to only persist the best score, but want to display how the 117 | * player performed in this particular instance, you can use the SkillsGameOnApiClient.renderLeaderboard and pass in 118 | * an AugmentedPlayer with the desired PlayerScore. 119 | * 120 | * @param {Player} alexaPlayer 121 | * @param {SkillsGameOnApiClient} [client=defaultClient] 122 | * @returns Alexa APL document directive 123 | */ 124 | async function getLeaderboard(alexaPlayer, client = defaultClient) { 125 | const leaderboard = await client.getCombinationLeaderboards({ 126 | matchId: settings.matchId, 127 | topScoresLimit: settings.topNleaderboardItemCount, 128 | playerNeighborsLimit: settings.playerNeighborsCount, 129 | player: alexaPlayer 130 | }); 131 | const currentScore = await client.getPlayerScore( 132 | settings.matchId, 133 | alexaPlayer); 134 | alexaPlayer.score.ordinalRank = currentScore.ordinalRank; 135 | alexaPlayer.score.rank = currentScore.rank; 136 | alexaPlayer.score.score = currentScore.score; 137 | const renderOptions = { backgroundImageUrl: settings.leaderboardBackgroundImageUrl }; 138 | return sdk.renderLeaderboard(alexaPlayer, leaderboard, renderOptions, generator); 139 | } 140 | module.exports = { 141 | newPlayer, 142 | lookupPlayerProfile, 143 | submitScore, 144 | getPlayerScore, 145 | enterMatch, 146 | getLeaderboard, 147 | refreshPlayerSession 148 | }; 149 | -------------------------------------------------------------------------------- /samples/word-word-lite/src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | const Alexa = require('ask-sdk-core'); 9 | const WordConstants = require('./words.js'); 10 | const GameOn = require('./gameOn.js'); 11 | 12 | const {DynamoDbPersistenceAdapter} = require('ask-sdk-dynamodb-persistence-adapter'); 13 | const persistenceAdapter = new DynamoDbPersistenceAdapter({ 14 | tableName: 'WordWordLitePersistence', 15 | createTable: true 16 | }); 17 | 18 | const PlayGameRequestHandler = { 19 | canHandle(handlerInput) { 20 | this.handlerInput = handlerInput; 21 | return this.requestType === 'IntentRequest' && this.intentName === 'PLAY_GAME_INTENT'; 22 | }, 23 | async handle(handlerInput) { 24 | this.handlerInput = handlerInput; 25 | const attributes = this.getSessionAttributesManager; 26 | const player = await this.getPersistentAttributesManager; 27 | 28 | // Update score from count of user utterances. 29 | const matchWord = attributes.currentWord.toLowerCase(); 30 | const utteranceCount = Object.values(this.slots) 31 | .filter((slotValue) => typeof slotValue.value !== 'undefined' && slotValue.value.toLowerCase() === matchWord) 32 | .length; 33 | await GameOn.submitScore(player, utteranceCount); 34 | 35 | // Construct Alexa response dialog. 36 | const playerScore = await GameOn.getPlayerScore(player); 37 | const speechText = `You have said ${attributes.currentWord} ${utteranceCount} times. Your current rank is ${playerScore.rank} . Your next word is ${attributes.nextWord} `; 38 | const repromptText = `Your next word is ${attributes.nextWord}`; 39 | const displayText = `You have said ${attributes.currentWord} ${utteranceCount} times.Your current rank is ${playerScore.rank}.Your next word is ${attributes.nextWord}`; 40 | 41 | // Initialize the next game session. 42 | attributes.currentWord = attributes.nextWord; 43 | attributes.nextWord = WordConstants.getWord(); 44 | handlerInput.attributesManager.setSessionAttributes(attributes); 45 | return handlerInput.responseBuilder 46 | .speak(speechText) 47 | .reprompt(repromptText) 48 | .withSimpleCard('Word Repeat', displayText) 49 | .getResponse(); 50 | } 51 | }; 52 | 53 | const LaunchRequestHandler = { 54 | canHandle(handlerInput) { 55 | this.handlerInput = handlerInput; 56 | return this.requestType === 'LaunchRequest'; 57 | }, 58 | async handle(handlerInput) { 59 | this.handlerInput = handlerInput; 60 | const attributes = this.getSessionAttributesManager; 61 | let player = await this.getPersistentAttributesManager; 62 | 63 | // Bootstrap new users by registering them with GameOn and persisting to DynamoDb 64 | if (Object.keys(player).length === 0) { 65 | player = await GameOn.newPlayer(); 66 | } else { 67 | player = await GameOn.refreshPlayerSession(player); 68 | } 69 | this.setPersistentAttributes(player); 70 | await this.savePersistentAttributes(); 71 | 72 | // Initialize a game session for the player. 73 | attributes.currentWord = WordConstants.getWord(); 74 | attributes.nextWord = WordConstants.getWord(); 75 | this.setSessionAttributes(attributes); 76 | const profile = GameOn.lookupPlayerProfile(player.externalPlayerId); 77 | await GameOn.submitScore(player, 0); 78 | 79 | // Construct Alexa response dialog. 80 | const speechText = `Welcome to the word repetition challenge. The game where you compete to see who can say the same word most times in a row ? 81 | Your name is ${profile.name}. When you are ready to begin, just say Alexa, and then say the word 82 | ${attributes.currentWord} as many times as possible.`; 83 | const repromptText = `To start just say Alexa, and then say the word ${attributes.currentWord} as many times as possible.`; 84 | const displayText = `Welcome ${profile.name} to start just say Alexa, and then say the word ${attributes.currentWord} as many times as possible.`; 85 | return handlerInput.responseBuilder 86 | .speak(speechText) 87 | .reprompt(repromptText) 88 | .withSimpleCard('Word Repeat', displayText) 89 | .getResponse(); 90 | } 91 | }; 92 | 93 | const HelpIntentHandler = { 94 | canHandle(handlerInput) { 95 | this.handlerInput = handlerInput; 96 | return this.requestType === 'IntentRequest' && this.intentName === 'AMAZON.HelpIntent'; 97 | }, 98 | handle(handlerInput) { 99 | const speechText = 'I’ll give you a word. Simply repeat the word as many times as you can, as fast as you can, without messing up. Are you ready to play?. Say Alexa and repeat the word as many times as possible.'; 100 | const displayText = 'To start just say Alexa, and then say the word as many times as possible.'; 101 | return handlerInput.responseBuilder 102 | .speak(speechText) 103 | .reprompt(speechText) 104 | .withSimpleCard('Word Repeat', displayText) 105 | .getResponse(); 106 | } 107 | }; 108 | const CancelAndStopIntentHandler = { 109 | canHandle(handlerInput) { 110 | this.handlerInput = handlerInput; 111 | return this.requestType === 'IntentRequest' 112 | && (this.intentName === 'AMAZON.CancelIntent' 113 | || this.intentName === 'AMAZON.StopIntent'); 114 | }, 115 | handle(handlerInput) { 116 | const speechText = 'Come back soon for another round. Bye Bye'; 117 | return handlerInput.responseBuilder 118 | .speak(speechText) 119 | .withSimpleCard('Word Repeat', speechText) 120 | .getResponse(); 121 | } 122 | }; 123 | const SessionEndedRequestHandler = { 124 | canHandle(handlerInput) { 125 | this.handlerInput = handlerInput; 126 | return this.requestType === 'SessionEndedRequest'; 127 | }, 128 | handle(handlerInput) { 129 | return handlerInput.responseBuilder.getResponse(); 130 | } 131 | }; 132 | 133 | const LeaderboardRequestHandler = { 134 | canHandle(handlerInput) { 135 | this.handlerInput = handlerInput; 136 | return this.requestType === 'IntentRequest' 137 | && this.intentName === 'SHOW_LEADERBOARD_INTENT'; 138 | }, 139 | async handle(handlerInput) { 140 | this.handlerInput = handlerInput; 141 | const attributes = this.getSessionAttributesManager; 142 | const player = await this.getPersistentAttributesManager; 143 | const leaderboardDirective = await GameOn.getLeaderboard(player); 144 | const playerScore = await GameOn.getPlayerScore(player); 145 | attributes.currentWord = WordConstants.getWord(); 146 | attributes.nextWord = WordConstants.getWord(); 147 | this.setSessionAttributes(attributes); 148 | const speechText = `Your current rank is ${playerScore.rank}. To continue playing , just say Alexa, and then say the word ${attributes.currentWord} as many times as possible.`; 149 | return handlerInput.responseBuilder 150 | .speak(speechText) 151 | .addDirective(leaderboardDirective) 152 | .getResponse(); 153 | } 154 | }; 155 | 156 | const IntentReflectorHandler = { 157 | canHandle(handlerInput) { 158 | this.handlerInput = handlerInput; 159 | return this.requestType === 'IntentRequest'; 160 | }, 161 | handle(handlerInput) { 162 | const speechText = ''; 163 | return handlerInput.responseBuilder 164 | .speak(speechText) 165 | .getResponse(); 166 | } 167 | }; 168 | 169 | const ErrorHandler = { 170 | canHandle() { 171 | return true; 172 | }, 173 | handle(handlerInput, error) { 174 | console.log(`~~~~ Error handled: ${error.message}`); 175 | const speechText = 'Sorry, I couldn\'t understand what you said. Please try again.'; 176 | 177 | return handlerInput.responseBuilder 178 | .speak(speechText) 179 | .reprompt(speechText) 180 | .withSimpleCard('Word Repeat', speechText) 181 | .getResponse(); 182 | } 183 | }; 184 | 185 | let EventHandlerInput = { 186 | get requestType() { 187 | return this.request.type; 188 | }, 189 | get intent() { 190 | return this.request.intent || {}; 191 | }, 192 | get slots() { 193 | return this.intent.slots; 194 | }, 195 | get intentName() { 196 | return this.intent.name; 197 | }, 198 | get input() { 199 | return this.handlerInput || {}; 200 | }, 201 | get envelope() { 202 | return this.handlerInput.requestEnvelope || {}; 203 | }, 204 | get request() { 205 | return this.envelope.request || {}; 206 | }, 207 | get getSessionAttributesManager() { 208 | return this.handlerInput.attributesManager.getSessionAttributes() || {}; 209 | }, 210 | get getPersistentAttributesManager() { 211 | return this.input.attributesManager.getPersistentAttributes() || {}; 212 | }, 213 | setSessionAttributes: async function setSessionAttributesManager(attributes) { 214 | await this.input.attributesManager.setSessionAttributes(attributes); 215 | }, 216 | setPersistentAttributes: async function setPersistentAttributesManager(player) { 217 | await this.input.attributesManager.setPersistentAttributes(player); 218 | }, 219 | savePersistentAttributes: async function setPersistentAttributesManager() { 220 | await this.input.attributesManager.savePersistentAttributes(); 221 | } 222 | }; 223 | 224 | Object.setPrototypeOf(LaunchRequestHandler, EventHandlerInput); 225 | Object.setPrototypeOf(PlayGameRequestHandler, EventHandlerInput); 226 | Object.setPrototypeOf(HelpIntentHandler, EventHandlerInput); 227 | Object.setPrototypeOf(CancelAndStopIntentHandler, EventHandlerInput); 228 | Object.setPrototypeOf(SessionEndedRequestHandler, EventHandlerInput); 229 | Object.setPrototypeOf(IntentReflectorHandler, EventHandlerInput); 230 | Object.setPrototypeOf(LeaderboardRequestHandler, EventHandlerInput); 231 | 232 | 233 | exports.handler = Alexa.SkillBuilders.custom() 234 | .addRequestHandlers( 235 | LaunchRequestHandler, 236 | PlayGameRequestHandler, 237 | HelpIntentHandler, 238 | CancelAndStopIntentHandler, 239 | SessionEndedRequestHandler, 240 | LeaderboardRequestHandler, 241 | IntentReflectorHandler) 242 | .withPersistenceAdapter(persistenceAdapter) 243 | .addErrorHandlers( 244 | ErrorHandler) 245 | .lambda(); 246 | -------------------------------------------------------------------------------- /samples/word-word-lite/src/settings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | // Should be stored securely with KMS or as a secure environment variable on your lambda 9 | // See https://github.com/alexa-games/skills-gameon-sdk-js/blob/master/README.md#gameon-api-secret-management 10 | const gameOnApiKey = process.env.gameOnApiKey; 11 | 12 | // Preferable to store the following settings as AWS Lambda environment variables 13 | const matchId = process.env.matchId || '<>'; 14 | const tournamentId = process.env.tournamentId || '<>'; 15 | 16 | // Required for GameOn. Value must be set to 'development' or 'release' 17 | const appBuildType = process.env.appBuildType || '<>'; 18 | 19 | // Base url for the player avatars. See https://github.com/alexa-games/skills-gameon-sdk-js/blob/master/README.md#avatar-generation 20 | // Cannot be empty string. Passing in any other value will allow the leaderboard to render, 21 | // but will display blank placeholders. 22 | const gameAvatarBaseUrl = process.env.gameAvatarBaseUrl || '<>'; 23 | 24 | // Background image for the leaderboard template 25 | // Recommended minimum size: 1280x800px 26 | // Cannot be empty string. Passing in any other value will allow the leaderboard to render, 27 | // but will display a blank white background 28 | const leaderboardBackgroundImageUrl = process.env.leaderboardBackgroundImageUrl || '<>'; 29 | 30 | // Top n places to show on the leaderboard 31 | const topNleaderboardItemCount = process.env.topNleaderboardItemCount || 5; 32 | // Number of players to render before and after current player 33 | const playerNeighborsCount = process.env.playerNeighborsCount || 1; 34 | // Number of avatars that have been generated 35 | // See https://github.com/alexa-games/skills-gameon-sdk-js/blob/master/README.md#avatar-generation 36 | const numberOfUniqueAvatars = process.env.numberOfUniqueAvatars || 50; 37 | 38 | module.exports = { 39 | matchId, 40 | tournamentId, 41 | appBuildType, 42 | gameOnApiKey, 43 | gameAvatarBaseUrl, 44 | leaderboardBackgroundImageUrl, 45 | topNleaderboardItemCount, 46 | playerNeighborsCount, 47 | numberOfUniqueAvatars 48 | }; 49 | -------------------------------------------------------------------------------- /samples/word-word-lite/src/words.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 | */ 7 | 8 | const words = [ 9 | 'able', 10 | 'about', 11 | 'absolute', 12 | 'accept', 13 | 'account', 14 | 'achieve', 15 | 'across', 16 | 'active', 17 | 'actual', 18 | 'address', 19 | 'admit', 20 | 'advertise', 21 | 'affect', 22 | 'afford', 23 | 'after', 24 | 'afternoon', 25 | 'against', 26 | 'agent', 27 | 'agree', 28 | 'allow', 29 | 'almost', 30 | 'along', 31 | 'already', 32 | 'alright', 33 | 'also', 34 | 'although', 35 | 'always', 36 | 'america', 37 | 'amount', 38 | 'another', 39 | 'answer', 40 | 'apart', 41 | 'apparent', 42 | 'appear', 43 | 'apply', 44 | 'appoint', 45 | 'approach', 46 | 'appropriate', 47 | 'area', 48 | 'argue', 49 | 'around', 50 | 'arrange', 51 | 'associate', 52 | 'assume', 53 | 'attend', 54 | 'authority', 55 | 'available', 56 | 'aware', 57 | 'away', 58 | 'awful', 59 | 'baby', 60 | 'back', 61 | 'balance', 62 | 'ball', 63 | 'bank', 64 | 'base', 65 | 'basis', 66 | 'bear', 67 | 'beat', 68 | 'beauty', 69 | 'because', 70 | 'become', 71 | 'before', 72 | 'begin', 73 | 'behind', 74 | 'believe', 75 | 'benefit', 76 | 'best', 77 | 'between', 78 | 'bill', 79 | 'birth', 80 | 'black', 81 | 'bloke', 82 | 'blood', 83 | 'blow', 84 | 'blue', 85 | 'board', 86 | 'boat', 87 | 'body', 88 | 'book', 89 | 'both', 90 | 'bother', 91 | 'bottle', 92 | 'bottom', 93 | 'break', 94 | 'brief', 95 | 'brilliant', 96 | 'bring', 97 | 'britain', 98 | 'brother', 99 | 'budget', 100 | 'build', 101 | 'business', 102 | 'busy', 103 | 'cake', 104 | 'call', 105 | 'card', 106 | 'care', 107 | 'carry', 108 | 'case', 109 | 'catch', 110 | 'cause', 111 | 'cent', 112 | 'certain', 113 | 'chair', 114 | 'chairman', 115 | 'chance', 116 | 'change', 117 | 'chap', 118 | 'character', 119 | 'charge', 120 | 'cheap', 121 | 'check', 122 | 'child', 123 | 'choice', 124 | 'choose', 125 | 'Christ', 126 | 'Christmas', 127 | 'church', 128 | 'city', 129 | 'claim', 130 | 'class', 131 | 'clean', 132 | 'clear', 133 | 'client', 134 | 'clock', 135 | 'close', 136 | 'closes', 137 | 'clothe', 138 | 'club', 139 | 'coffee', 140 | 'cold', 141 | 'colleague', 142 | 'collect', 143 | 'college', 144 | 'come', 145 | 'comment', 146 | 'commit', 147 | 'committee', 148 | 'common', 149 | 'community', 150 | 'company', 151 | 'compare', 152 | 'complete', 153 | 'compute', 154 | 'concern', 155 | 'condition', 156 | 'confer', 157 | 'consider', 158 | 'consult', 159 | 'contact', 160 | 'continue', 161 | 'contract', 162 | 'control', 163 | 'converse', 164 | 'cook', 165 | 'copy', 166 | 'corner', 167 | 'correct', 168 | 'cost', 169 | 'could', 170 | 'council', 171 | 'count', 172 | 'country', 173 | 'county', 174 | 'couple', 175 | 'course', 176 | 'court', 177 | 'cover', 178 | 'create', 179 | 'cross', 180 | 'current', 181 | 'danger', 182 | 'date', 183 | 'dead', 184 | 'deal', 185 | 'dear', 186 | 'debate', 187 | 'decide', 188 | 'decision', 189 | 'deep', 190 | 'definite', 191 | 'degree', 192 | 'department', 193 | 'depend', 194 | 'describe', 195 | 'design', 196 | 'detail', 197 | 'develop', 198 | 'difference', 199 | 'difficult', 200 | 'dinner', 201 | 'direct', 202 | 'discuss', 203 | 'district', 204 | 'divide', 205 | 'doctor', 206 | 'document', 207 | 'door', 208 | 'double', 209 | 'doubt', 210 | 'down', 211 | 'draw', 212 | 'dress', 213 | 'drink', 214 | 'drive', 215 | 'drop', 216 | 'during', 217 | 'each', 218 | 'early', 219 | 'east', 220 | 'easy', 221 | 'economy', 222 | 'educate', 223 | 'effect', 224 | 'eight', 225 | 'either', 226 | 'elect', 227 | 'electric', 228 | 'eleven', 229 | 'else', 230 | 'employ', 231 | 'encourage', 232 | 'engine', 233 | 'english', 234 | 'enjoy', 235 | 'enough', 236 | 'enter', 237 | 'environment', 238 | 'equal', 239 | 'especial', 240 | 'europe', 241 | 'even', 242 | 'evening', 243 | 'ever', 244 | 'every', 245 | 'evidence', 246 | 'exact', 247 | 'example', 248 | 'except', 249 | 'excuse', 250 | 'exercise', 251 | 'exist', 252 | 'expect', 253 | 'expense', 254 | 'experience', 255 | 'explain', 256 | 'express', 257 | 'extra', 258 | 'face', 259 | 'fact', 260 | 'fair', 261 | 'fall', 262 | 'family', 263 | 'farm', 264 | 'fast', 265 | 'father', 266 | 'feed', 267 | 'feel', 268 | 'field', 269 | 'fight', 270 | 'figure', 271 | 'file', 272 | 'fill', 273 | 'film', 274 | 'final', 275 | 'finance', 276 | 'find', 277 | 'fine', 278 | 'finish', 279 | 'fire', 280 | 'first', 281 | 'fish', 282 | 'five', 283 | 'flat', 284 | 'floor', 285 | 'follow', 286 | 'food', 287 | 'foot', 288 | 'force', 289 | 'forget', 290 | 'form', 291 | 'fortune', 292 | 'forward', 293 | 'four', 294 | 'france', 295 | 'free', 296 | 'friday', 297 | 'friend', 298 | 'from', 299 | 'front', 300 | 'full', 301 | 'function', 302 | 'fund', 303 | 'further', 304 | 'future', 305 | 'game', 306 | 'garden', 307 | 'general', 308 | 'germany', 309 | 'girl', 310 | 'give', 311 | 'glass', 312 | 'good', 313 | 'goodbye', 314 | 'govern', 315 | 'grand', 316 | 'grant', 317 | 'great', 318 | 'green', 319 | 'ground', 320 | 'group', 321 | 'grow', 322 | 'guess', 323 | 'hair', 324 | 'half', 325 | 'hall', 326 | 'hand', 327 | 'hang', 328 | 'happen', 329 | 'happy', 330 | 'hard', 331 | 'hate', 332 | 'have', 333 | 'head', 334 | 'health', 335 | 'hear', 336 | 'heart', 337 | 'heat', 338 | 'heavy', 339 | 'hell', 340 | 'here', 341 | 'high', 342 | 'history', 343 | 'hold', 344 | 'holiday', 345 | 'home', 346 | 'honest', 347 | 'hope', 348 | 'horse', 349 | 'hospital', 350 | 'hour', 351 | 'house', 352 | 'however', 353 | 'hullo', 354 | 'hundred', 355 | 'husband', 356 | 'idea', 357 | 'identify', 358 | 'imagine', 359 | 'important', 360 | 'improve', 361 | 'include', 362 | 'income', 363 | 'increase', 364 | 'indeed', 365 | 'individual', 366 | 'industry', 367 | 'inform', 368 | 'inside', 369 | 'instead', 370 | 'insure', 371 | 'interest', 372 | 'into', 373 | 'introduce', 374 | 'invest', 375 | 'involve', 376 | 'issue', 377 | 'item', 378 | 'jesus', 379 | 'join', 380 | 'judge', 381 | 'jump', 382 | 'just', 383 | 'keep', 384 | 'kill', 385 | 'kind', 386 | 'king', 387 | 'kitchen', 388 | 'knock', 389 | 'lady', 390 | 'land', 391 | 'language', 392 | 'large', 393 | 'last', 394 | 'late', 395 | 'laugh', 396 | 'lead', 397 | 'learn', 398 | 'leave', 399 | 'left', 400 | 'less', 401 | 'letter', 402 | 'level', 403 | 'life', 404 | 'light', 405 | 'like', 406 | 'likely', 407 | 'limit', 408 | 'line', 409 | 'link', 410 | 'list', 411 | 'listen', 412 | 'little', 413 | 'live', 414 | 'load', 415 | 'local', 416 | 'lock', 417 | 'london', 418 | 'long', 419 | 'look', 420 | 'lord', 421 | 'lose', 422 | 'love', 423 | 'luck', 424 | 'lunch', 425 | 'machine', 426 | 'main', 427 | 'major', 428 | 'make', 429 | 'manage', 430 | 'many', 431 | 'mark', 432 | 'market', 433 | 'marry', 434 | 'match', 435 | 'matter', 436 | 'maybe', 437 | 'mean', 438 | 'meaning', 439 | 'measure', 440 | 'meet', 441 | 'member', 442 | 'mention', 443 | 'middle', 444 | 'might', 445 | 'mile', 446 | 'milk', 447 | 'million', 448 | 'mind', 449 | 'minister', 450 | 'minus', 451 | 'minute', 452 | 'miss', 453 | 'mister', 454 | 'moment', 455 | 'monday', 456 | 'money', 457 | 'month', 458 | 'more', 459 | 'morning', 460 | 'most', 461 | 'mother', 462 | 'motion', 463 | 'move', 464 | 'missus', 465 | 'much', 466 | 'music', 467 | 'must', 468 | 'name', 469 | 'nation', 470 | 'nature', 471 | 'near', 472 | 'necessary', 473 | 'need', 474 | 'never', 475 | 'news', 476 | 'next', 477 | 'nice', 478 | 'night', 479 | 'nine', 480 | 'none', 481 | 'normal', 482 | 'north', 483 | 'note', 484 | 'notice', 485 | 'number', 486 | 'obvious', 487 | 'occasion', 488 | 'offer', 489 | 'office', 490 | 'often', 491 | 'okay', 492 | 'once', 493 | 'only', 494 | 'operate', 495 | 'opportunity', 496 | 'oppose', 497 | 'order', 498 | 'organize', 499 | 'original', 500 | 'other', 501 | 'otherwise', 502 | 'ought', 503 | 'over', 504 | 'pack', 505 | 'page', 506 | 'paint', 507 | 'pair', 508 | 'paper', 509 | 'paragraph', 510 | 'pardon', 511 | 'parent', 512 | 'park', 513 | 'part', 514 | 'particular', 515 | 'party', 516 | 'pass', 517 | 'past', 518 | 'pence', 519 | 'pension', 520 | 'people', 521 | 'percent', 522 | 'perfect', 523 | 'perhaps', 524 | 'period', 525 | 'person', 526 | 'photograph', 527 | 'pick', 528 | 'picture', 529 | 'piece', 530 | 'place', 531 | 'plan', 532 | 'please', 533 | 'plus', 534 | 'point', 535 | 'police', 536 | 'policy', 537 | 'politic', 538 | 'poor', 539 | 'position', 540 | 'positive', 541 | 'possible', 542 | 'post', 543 | 'pound', 544 | 'power', 545 | 'prepare', 546 | 'present', 547 | 'press', 548 | 'pressure', 549 | 'presume', 550 | 'pretty', 551 | 'previous', 552 | 'price', 553 | 'print', 554 | 'private', 555 | 'probable', 556 | 'problem', 557 | 'proceed', 558 | 'process', 559 | 'produce', 560 | 'product', 561 | 'project', 562 | 'proper', 563 | 'propose', 564 | 'protect', 565 | 'provide', 566 | 'public', 567 | 'pull', 568 | 'purpose', 569 | 'push', 570 | 'quality', 571 | 'quarter', 572 | 'question', 573 | 'quick', 574 | 'quid', 575 | 'quiet', 576 | 'quite', 577 | 'radio', 578 | 'rail', 579 | 'raise', 580 | 'range', 581 | 'rate', 582 | 'rather', 583 | 'read', 584 | 'ready', 585 | 'real', 586 | 'really', 587 | 'reason', 588 | 'receive', 589 | 'recent', 590 | 'reckon', 591 | 'recognize', 592 | 'recommend', 593 | 'record', 594 | 'reduce', 595 | 'refer', 596 | 'regard', 597 | 'region', 598 | 'relation', 599 | 'remember', 600 | 'report', 601 | 'represent', 602 | 'require', 603 | 'research', 604 | 'resource', 605 | 'respect', 606 | 'responsible', 607 | 'rest', 608 | 'result', 609 | 'return', 610 | 'right', 611 | 'ring', 612 | 'rise', 613 | 'road', 614 | 'role', 615 | 'roll', 616 | 'room', 617 | 'round', 618 | 'rule', 619 | 'safe', 620 | 'sale', 621 | 'same', 622 | 'saturday', 623 | 'save', 624 | 'scheme', 625 | 'school', 626 | 'science', 627 | 'score', 628 | 'scotland', 629 | 'seat', 630 | 'second', 631 | 'secretary', 632 | 'section', 633 | 'secure', 634 | 'seem', 635 | 'self', 636 | 'sell', 637 | 'send', 638 | 'sense', 639 | 'separate', 640 | 'serious', 641 | 'serve', 642 | 'service', 643 | 'settle', 644 | 'seven', 645 | 'shall', 646 | 'share', 647 | 'sheet', 648 | 'shoe', 649 | 'shoot', 650 | 'shop', 651 | 'short', 652 | 'should', 653 | 'show', 654 | 'shut', 655 | 'sick', 656 | 'side', 657 | 'sign', 658 | 'similar', 659 | 'simple', 660 | 'since', 661 | 'sing', 662 | 'single', 663 | 'sister', 664 | 'site', 665 | 'situate', 666 | 'size', 667 | 'sleep', 668 | 'slight', 669 | 'slow', 670 | 'small', 671 | 'smoke', 672 | 'social', 673 | 'society', 674 | 'some', 675 | 'soon', 676 | 'sorry', 677 | 'sort', 678 | 'sound', 679 | 'south', 680 | 'space', 681 | 'speak', 682 | 'special', 683 | 'specific', 684 | 'speed', 685 | 'spell', 686 | 'spend', 687 | 'square', 688 | 'staff', 689 | 'stage', 690 | 'stairs', 691 | 'stand', 692 | 'standard', 693 | 'state', 694 | 'station', 695 | 'stay', 696 | 'step', 697 | 'stick', 698 | 'still', 699 | 'story', 700 | 'straight', 701 | 'strategy', 702 | 'street', 703 | 'strike', 704 | 'strong', 705 | 'structure', 706 | 'student', 707 | 'study', 708 | 'stuff', 709 | 'stupid', 710 | 'subject', 711 | 'succeed', 712 | 'such', 713 | 'sudden', 714 | 'suggest', 715 | 'suit', 716 | 'summer', 717 | 'sunday', 718 | 'supply', 719 | 'support', 720 | 'suppose', 721 | 'surprise', 722 | 'switch', 723 | 'system', 724 | 'table', 725 | 'take', 726 | 'talk', 727 | 'tape', 728 | 'teach', 729 | 'team', 730 | 'telephone', 731 | 'television', 732 | 'tell', 733 | 'tend', 734 | 'term', 735 | 'terrible', 736 | 'test', 737 | 'than', 738 | 'thank', 739 | 'then', 740 | 'there', 741 | 'therefore', 742 | 'they', 743 | 'thing', 744 | 'think', 745 | 'thirteen', 746 | 'thirty', 747 | 'this', 748 | 'thou', 749 | 'though', 750 | 'thousand', 751 | 'three', 752 | 'through', 753 | 'throw', 754 | 'thursday', 755 | 'time', 756 | 'today', 757 | 'together', 758 | 'tomorrow', 759 | 'tonight', 760 | 'total', 761 | 'touch', 762 | 'toward', 763 | 'town', 764 | 'trade', 765 | 'traffic', 766 | 'train', 767 | 'transport', 768 | 'travel', 769 | 'treat', 770 | 'tree', 771 | 'trouble', 772 | 'true', 773 | 'trust', 774 | 'tuesday', 775 | 'turn', 776 | 'twelve', 777 | 'twenty', 778 | 'type', 779 | 'under', 780 | 'understand', 781 | 'union', 782 | 'unit', 783 | 'unite', 784 | 'university', 785 | 'unless', 786 | 'until', 787 | 'upon', 788 | 'usual', 789 | 'value', 790 | 'various', 791 | 'very', 792 | 'video', 793 | 'view', 794 | 'village', 795 | 'visit', 796 | 'vote', 797 | 'wage', 798 | 'wait', 799 | 'walk', 800 | 'wall', 801 | 'want', 802 | 'warm', 803 | 'wash', 804 | 'waste', 805 | 'watch', 806 | 'water', 807 | 'wear', 808 | 'wednesday', 809 | 'week', 810 | 'weigh', 811 | 'welcome', 812 | 'well', 813 | 'west', 814 | 'what', 815 | 'when', 816 | 'where', 817 | 'whether', 818 | 'which', 819 | 'while', 820 | 'white', 821 | 'whole', 822 | 'wide', 823 | 'wife', 824 | 'will', 825 | 'wind', 826 | 'window', 827 | 'wish', 828 | 'with', 829 | 'within', 830 | 'without', 831 | 'woman', 832 | 'wonder', 833 | 'wood', 834 | 'word', 835 | 'work', 836 | 'world', 837 | 'worry', 838 | 'worse', 839 | 'worth', 840 | 'would', 841 | 'write', 842 | 'wrong', 843 | 'year', 844 | 'yesterday', 845 | 'young', 846 | 'Lucky', 847 | 'Salad', 848 | 'Fluffy', 849 | 'Glitter', 850 | 'Sandwich', 851 | 'Funny', 852 | 'Jelly', 853 | 'Balloon', 854 | 'Narwhal', 855 | 'Silly', 856 | 'Yogurt', 857 | 'Cuckoo', 858 | 'Hula', 859 | 'Tidbit', 860 | 'Burger', 861 | 'Iceberg', 862 | 'Tadpole', 863 | 'Firefly', 864 | 'Rowboat', 865 | 'Pecan', 866 | 'Boing', 867 | 'Glitch', 868 | 'Jumbo', 869 | 'Loony', 870 | 'Pogo', 871 | 'Vertigo', 872 | 'Whimsy', 873 | 'Zigzag', 874 | 'Thunder', 875 | 'Breakfast', 876 | 'Crayon', 877 | 'Secret', 878 | 'Maple', 879 | 'Orange', 880 | 'Lemon', 881 | 'Degree', 882 | 'Novel', 883 | 'Ketchup', 884 | 'Marble', 885 | 'Robot', 886 | 'Ninja', 887 | 'Monkey', 888 | 'Button', 889 | 'Pumpkin', 890 | 'Shadow', 891 | 'Snowman', 892 | 'Apple', 893 | 'Horseshoe', 894 | 'Honey', 895 | 'July', 896 | 'Pumpernickel', 897 | 'Meow', 898 | 'Woof', 899 | 'Bark', 900 | 'Chirp', 901 | 'Squawk', 902 | 'Lull', 903 | 'cucumber', 904 | 'badger', 905 | 'squawk' 906 | ]; 907 | 908 | function getWord() { 909 | return words[Math.floor(Math.random() * words.length)]; 910 | } 911 | 912 | module.exports.words = words; 913 | module.exports.getWord = getWord; 914 | -------------------------------------------------------------------------------- /tools/update-credits.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | cd "$(dirname "$BASH_SOURCE")/.." 5 | 6 | # see also ".mailmap" for how email addresses and names are deduplicated 7 | 8 | { 9 | cat <<-'EOH' 10 | # Skills GameOn SDK for Node.js Credits and Thanks 11 | (This file is autogenerated using [update-credits.sh](tools/update-credits.sh).) 12 | 13 | The Skills GameOn SDK for Node.js provides easy access to Amazon GameOn and creates higher-level methods, utilities, and examples that simplify the integration of GameOn features, like leaderboards, with Alexa skills. 14 | 15 | Contributors to the Skills GameOn SDK for Node.js repository: 16 | EOH 17 | echo 18 | git log --format='* %aN <%aE>' | LC_ALL=C.UTF-8 sort -uf 19 | } > CREDITS.md 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "target": "ES2017", 5 | "module": "commonjs", 6 | "allowJs": false, 7 | "declaration": false, 8 | "removeComments": false, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noImplicitAny": false, 12 | "moduleResolution": "node", 13 | "baseUrl": "./", 14 | "paths": { 15 | "*": ["types/*"], 16 | "@test/*": ["test/*"] 17 | }, 18 | "types": ["node", "mocha"], 19 | "esModuleInterop": true, 20 | "inlineSourceMap": true, 21 | "inlineSources": true, 22 | "lib": [ 23 | "es2017", 24 | "dom", 25 | "scripthost", 26 | "dom.iterable", 27 | "esnext.asynciterable" 28 | ], 29 | "experimentalDecorators": true, 30 | "emitDecoratorMetadata": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended"], 3 | "rules": { 4 | // Namespaces 5 | "no-namespace": false, 6 | // Alphabetized imports 7 | "ordered-imports": [ 8 | true, 9 | { 10 | "import-sources-order": "case-insensitive", 11 | "named-imports-order": "case-insensitive" 12 | } 13 | ], 14 | // when the complexity of a function gets to be crazy 15 | "cyclomatic-complexity": [ 16 | true, 17 | 15 18 | ], 19 | // Enforces indentation with 4 spaces. Although 4 spaces is not enforced, as there may be special cases where 20 | // it cannot be avoided, it is highly recommended to use 4 spaces everywhere for indents 21 | "indent": [ 22 | true, 23 | "spaces", 24 | 4 25 | ], 26 | "file-name-casing": [ 27 | false, 28 | { 29 | ".ts": "camel-case" 30 | } 31 | ], 32 | "import-spacing": false, 33 | "interface-name": false, 34 | // Sorry Windows users, you'll have to convert line endings to remove /r as this causes issues later. 35 | "linebreak-style": [ 36 | true, 37 | "LF" 38 | ], 39 | // Number of classes per file. Currently not enforced, but recommend having separate classes in separate files. 40 | "max-classes-per-file": [ 41 | false, 42 | 1 43 | ], 44 | // Maximum line length will be 150 chars, which is better than 80! 45 | "max-line-length": [ 46 | true, 47 | 150 48 | ], 49 | "no-angle-bracket-type-assertion": false, 50 | // Don't use console commands like console.log(), use other loggers instead with log levels 51 | "no-console": [ 52 | true, 53 | "debug", 54 | "info", 55 | "log", 56 | "time", 57 | "timeEnd", 58 | "trace" 59 | ], 60 | "no-string-literal": false, 61 | "no-magic-numbers": true, 62 | // Enforces consistent object literal property quote style. 63 | "object-literal-key-quotes": [ 64 | true, 65 | "consistent-as-needed" 66 | ], 67 | "object-literal-sort-keys": false, 68 | // Don't declare multiple variables with one 'let' followed by commas 69 | "one-variable-per-declaration": [ 70 | true, 71 | "ignore-for-loop" 72 | ], 73 | "only-arrow-functions": false, 74 | "prefer-object-spread": true, 75 | "promise-function-async": true, 76 | // Use single quotes for string literals for consistency 77 | "quotemark": [ 78 | true, 79 | "single", 80 | "avoid-escape" 81 | ], 82 | // Always. Do it. 83 | "semicolon": [ 84 | true, 85 | "always" 86 | ], 87 | // Require a default case in all switch statements. Log something if you can't think of anything. 88 | "switch-default": true, 89 | // disallows multiline/singleline trailing commas 90 | "trailing-comma": [ 91 | true, 92 | { 93 | "multiline": "never", 94 | "singleline": "never", 95 | "esSpecCompliant": true 96 | } 97 | ], 98 | // Use === instead of ==. 99 | "triple-equals": true, 100 | // whitespace rules 101 | "whitespace": [ 102 | true, 103 | "check-branch", 104 | "check-decl", 105 | "check-operator", 106 | "check-preblock", 107 | "check-separator", 108 | "check-type", 109 | "check-typecast", 110 | "check-type-operator" 111 | ] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Skills GameOn SDK", 3 | "mode": "file", 4 | "out": "docs/typedoc", 5 | "theme": "./docs/theme", 6 | "ignoreCompilerErrors": false, 7 | "preserveConstEnums": true, 8 | "excludeExternals": true, 9 | "excludePrivate": true, 10 | "exclude": "**/test/**", 11 | "hideGenerator": true, 12 | "target": "ES6", 13 | "module": "commonjs", 14 | "readme": "./README.md", 15 | "media": "./media" 16 | } 17 | --------------------------------------------------------------------------------