diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3faff97
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+
+reader/ngrok\.exe
diff --git a/f-ask/.gitignore b/f-ask/.gitignore
deleted file mode 100644
index b601c41..0000000
--- a/f-ask/.gitignore
+++ /dev/null
@@ -1,102 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*,cover
-.hypothesis/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# IPython Notebook
-.ipynb_checkpoints
-
-# pyenv
-.python-version
-
-# celery beat schedule file
-celerybeat-schedule
-
-# dotenv
-.env
-
-# virtualenv
-venv/
-ENV/
-
-# Spyder project settings
-.spyderproject
-
-# Rope project settings
-.ropeproject
-
-# Vagrant
-.vagrant
-
-# Misc
-.DS_Store
-temp
-.idea/
-
-# Project
-backups
-settings.cfg
-fabfile/settings.py
diff --git a/f-ask/.vscode/launch.json b/f-ask/.vscode/launch.json
deleted file mode 100644
index e69de29..0000000
diff --git a/f-ask/LICENSE.txt b/f-ask/LICENSE.txt
deleted file mode 100644
index 264ebb3..0000000
--- a/f-ask/LICENSE.txt
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2016 John Wheeler
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/f-ask/MANIFEST.in b/f-ask/MANIFEST.in
deleted file mode 100644
index 557c423..0000000
--- a/f-ask/MANIFEST.in
+++ /dev/null
@@ -1,6 +0,0 @@
-include *.rst *.txt LICENSE tox.ini .travis.yml docs/Makefile .coveragerc conftest.py
-recursive-include tests *.py
-recursive-include docs *.rst
-recursive-include docs *.py
-prune docs/_build
-
diff --git a/f-ask/README.md b/f-ask/README.md
deleted file mode 100644
index 64dd38e..0000000
--- a/f-ask/README.md
+++ /dev/null
@@ -1 +0,0 @@
-GST
diff --git a/f-ask/README.rst b/f-ask/README.rst
deleted file mode 100644
index 63b4d34..0000000
--- a/f-ask/README.rst
+++ /dev/null
@@ -1,142 +0,0 @@
-
-.. image:: http://flask-ask.readthedocs.io/en/latest/_images/logo-full.png
-
-===================================
-Program the Amazon Echo with Python
-===================================
-
-Flask-Ask is a `Flask extension `_ that makes building Alexa skills for the Amazon Echo easier and much more fun.
-
-* `Flask-Ask quickstart on Amazon's Developer Blog `_.
-* `Level Up with our Alexa Skills Kit Video Tutorial `_
-* `Chat on Gitter.im `_
-
-The Basics
-===============
-
-A Flask-Ask application looks like this:
-
-.. code-block:: python
-
- from flask import Flask
- from flask_ask import Ask, statement
-
- app = Flask(__name__)
- ask = Ask(app, '/')
-
- @ask.intent('HelloIntent')
- def hello(firstname):
- speech_text = "Hello %s" % firstname
- return statement(speech_text).simple_card('Hello', speech_text)
-
- if __name__ == '__main__':
- app.run()
-
-In the code above:
-
-#. The ``Ask`` object is created by passing in the Flask application and a route to forward Alexa requests to.
-#. The ``intent`` decorator maps ``HelloIntent`` to a view function ``hello``.
-#. The intent's ``firstname`` slot is implicitly mapped to ``hello``'s ``firstname`` parameter.
-#. Lastly, a builder constructs a spoken response and displays a contextual card in the Alexa smartphone/tablet app.
-
-More code examples are in the `samples `_ directory.
-
-Jinja Templates
----------------
-
-Since Alexa responses are usually short phrases, you might find it convenient to put them in the same file.
-Flask-Ask has a `Jinja template loader `_ that loads
-multiple templates from a single YAML file. For example, here's a template that supports the minimal voice interface
-above:
-
-.. code-block:: yaml
-
- hello: Hello, {{ firstname }}
-
-Templates are stored in a file called `templates.yaml` located in the application root. Checkout the `Tidepooler example `_ to see why it makes sense to extract speech out of the code and into templates as the number of spoken phrases grow.
-
-Features
-===============
-
-Flask-Ask handles the boilerplate, so you can focus on writing clean code. Flask-Ask:
-
-* Has decorators to map Alexa requests and intent slots to view functions
-* Helps construct ask and tell responses, reprompts and cards
-* Makes session management easy
-* Allows for the separation of code and speech through Jinja templates
-* Verifies Alexa request signatures
-
-Installation
-===============
-
-To install Flask-Ask::
-
- pip install flask-ask
-
-Documentation
-===============
-
-These resources will get you up and running quickly:
-
-* `5-minute quickstart `_
-* `Full online documentation `_
-
-Fantastic 3-part tutorial series by Harrison Kinsley
-
-* `Intro and Skill Logic - Alexa Skills w/ Python and Flask-Ask Part 1 `_
-* `Headlines Function - Alexa Skills w/ Python and Flask-Ask Part 2 `_
-* `Testing our Skill - Alexa Skills w/ Python and Flask-Ask Part 3 `_
-
-Deployment
-===============
-
-You can deploy using any WSGI compliant framework (uWSGI, Gunicorn). If you haven't deployed a Flask app to production, `checkout flask-live-starter `_.
-
-To deploy on AWS Lambda, you have two options. Use `Zappa `_ to automate the deployment of an AWS Lambda function and an AWS API Gateway to provide a public facing endpoint for your Lambda function. This `blog post `_ shows how to deploy Flask-Ask with Zappa from scratch. Note: When deploying to AWS Lambda with Zappa, make sure you point the Alexa skill to the HTTPS API gateway that Zappa creates, not the Lambda function's ARN.
-
-Alternatively, you can use AWS Lambda directly without the need for an AWS API Gateway endpoint. In this case you will need to `deploy `_ your Lambda function yourself and use `virtualenv `_ to create a deployment package that contains your Flask-Ask application along with its dependencies, which can be uploaded to Lambda. If your Lambda handler is configured as `lambda_function.lambda_handler`, then you would save the full application example above in a file called `lambda_function.py` and add the following two lines to it:
-
-.. code-block:: python
-
- def lambda_handler(event, _context):
- return ask.run_aws_lambda(event)
-
-
-Development
-===============
-
-If you'd like to work from the Flask-Ask source, clone the project and run::
-
- pip install -r requirements-dev.txt
-
-This will install all base requirements from `requirements.txt` as well as requirements needed for running tests from the `tests` directory.
-
-Tests can be run with::
-
- python setup.py test
-
-Or::
-
- python -m unittest
-
-To install from your local clone or fork of the project, run::
-
- python setup.py install
-
-Related projects
-===============
-
-`cookiecutter-flask-ask `_ is a Cookiecutter to easily bootstrap a Flask-Ask project, including documentation, speech assets and basic built-in intents.
-
-Have a Google Home? Checkout `Flask-Assistant `_ (early alpha)
-
-
-Thank You
-===============
-
-Thanks for checking this library out! I hope you find it useful.
-
-Of course, there's always room for improvement.
-Feel free to `open an issue `_ so we can make Flask-Ask better.
-
-Special thanks to `@kennethreitz `_ for his `sense `_ of `style `_, and of course, `@mitsuhiko `_ for `Flask `_
diff --git a/f-ask/docs/Makefile b/f-ask/docs/Makefile
deleted file mode 100644
index 8061fad..0000000
--- a/f-ask/docs/Makefile
+++ /dev/null
@@ -1,230 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
- $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help
-help:
- @echo "Please use \`make ' where is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " applehelp to make an Apple Help Book"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " epub3 to make an epub3"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " xml to make Docutils-native XML files"
- @echo " pseudoxml to make pseudoxml-XML files for display purposes"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
- @echo " coverage to run coverage check of the documentation (if enabled)"
- @echo " dummy to check syntax errors of document sources"
-
-.PHONY: clean
-clean:
- rm -rf $(BUILDDIR)/*
-
-.PHONY: html
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-.PHONY: dirhtml
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-.PHONY: singlehtml
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-.PHONY: pickle
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-.PHONY: json
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-.PHONY: htmlhelp
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-.PHONY: qthelp
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-Ask.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-Ask.qhc"
-
-.PHONY: applehelp
-applehelp:
- $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
- @echo
- @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
- @echo "N.B. You won't be able to view it unless you put it in" \
- "~/Library/Documentation/Help or install it in your application" \
- "bundle."
-
-.PHONY: devhelp
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-Ask"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-Ask"
- @echo "# devhelp"
-
-.PHONY: epub
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-.PHONY: epub3
-epub3:
- $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
- @echo
- @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
-
-.PHONY: latex
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-.PHONY: latexpdf
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: latexpdfja
-latexpdfja:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through platex and dvipdfmx..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: text
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-.PHONY: man
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-.PHONY: texinfo
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-.PHONY: info
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-.PHONY: gettext
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-.PHONY: changes
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-.PHONY: linkcheck
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-.PHONY: doctest
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-.PHONY: coverage
-coverage:
- $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
- @echo "Testing of coverage in the sources finished, look at the " \
- "results in $(BUILDDIR)/coverage/python.txt."
-
-.PHONY: xml
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-.PHONY: pseudoxml
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
-
-.PHONY: dummy
-dummy:
- $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
- @echo
- @echo "Build finished. Dummy builder generates no files."
diff --git a/f-ask/docs/_static/logo-full.png b/f-ask/docs/_static/logo-full.png
deleted file mode 100644
index 9762abb..0000000
Binary files a/f-ask/docs/_static/logo-full.png and /dev/null differ
diff --git a/f-ask/docs/_static/logo-sm.png b/f-ask/docs/_static/logo-sm.png
deleted file mode 100644
index b4754fd..0000000
Binary files a/f-ask/docs/_static/logo-sm.png and /dev/null differ
diff --git a/f-ask/docs/_templates/links.html b/f-ask/docs/_templates/links.html
deleted file mode 100644
index b236224..0000000
--- a/f-ask/docs/_templates/links.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
diff --git a/f-ask/docs/_themes/LICENSE b/f-ask/docs/_themes/LICENSE
deleted file mode 100644
index 8daab7e..0000000
--- a/f-ask/docs/_themes/LICENSE
+++ /dev/null
@@ -1,37 +0,0 @@
-Copyright (c) 2010 by Armin Ronacher.
-
-Some rights reserved.
-
-Redistribution and use in source and binary forms of the theme, with or
-without modification, are permitted provided that the following conditions
-are met:
-
-* Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
-* The names of the contributors may not be used to endorse or
- promote products derived from this software without specific
- prior written permission.
-
-We kindly ask you to only use these themes in an unmodified manner just
-for Flask and Flask-related products, not for unrelated projects. If you
-like the visual style and want to use it for your own projects, please
-consider making some larger changes to the themes (such as changing
-font faces, sizes, colors or margins).
-
-THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/f-ask/docs/_themes/README b/f-ask/docs/_themes/README
deleted file mode 100644
index b3292bd..0000000
--- a/f-ask/docs/_themes/README
+++ /dev/null
@@ -1,31 +0,0 @@
-Flask Sphinx Styles
-===================
-
-This repository contains sphinx styles for Flask and Flask related
-projects. To use this style in your Sphinx documentation, follow
-this guide:
-
-1. put this folder as _themes into your docs folder. Alternatively
- you can also use git submodules to check out the contents there.
-2. add this to your conf.py:
-
- sys.path.append(os.path.abspath('_themes'))
- html_theme_path = ['_themes']
- html_theme = 'flask'
-
-The following themes exist:
-
-- 'flask' - the standard flask documentation theme for large
- projects
-- 'flask_small' - small one-page theme. Intended to be used by
- very small addon libraries for flask.
-
-The following options exist for the flask_small theme:
-
- [options]
- index_logo = '' filename of a picture in _static
- to be used as replacement for the
- h1 in the index.rst file.
- index_logo_height = 120px height of the index logo
- github_fork = '' repository name on github for the
- "fork me" badge
diff --git a/f-ask/docs/_themes/flask/layout.html b/f-ask/docs/_themes/flask/layout.html
deleted file mode 100644
index a76d787..0000000
--- a/f-ask/docs/_themes/flask/layout.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{%- extends "basic/layout.html" %} {%- block extrahead %} {{ super() }}
-
-
-
-
-
-
-
-
-{% if theme_touch_icon %}
- {% endif %} {% endblock %} {%- block relbar2 %} {% if theme_github_fork %}
-
-
-
-{% endif %} {% endblock %} {%- block footer %}
-
-{%- endblock %}
diff --git a/f-ask/docs/_themes/flask/relations.html b/f-ask/docs/_themes/flask/relations.html
deleted file mode 100644
index 3bbcde8..0000000
--- a/f-ask/docs/_themes/flask/relations.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-Welcome to Flask-Ask
-====================
-
-Building high-quality Alexa skills for Amazon Echo Devices takes time. Flask-Ask makes it easier and much more fun.
-Use Flask-Ask with `ngrok `_ to eliminate the deploy-to-test step and get work done faster.
-
-Flask-Ask:
-
-* Has decorators to map Alexa requests and intent slots to view functions
-* Helps construct ask and tell responses, reprompts and cards
-* Makes session management easy
-* Allows for the separation of code and speech through Jinja templates
-* Verifies Alexa request signatures
-
-.. raw:: html
-
-
-
-
-Follow along with this quickstart on `Amazon
-`_.
-
-.. include:: contents.rst.inc
-
-.. raw:: html
-
-
-
-
diff --git a/f-ask/docs/make.bat b/f-ask/docs/make.bat
deleted file mode 100644
index 8fae2bf..0000000
--- a/f-ask/docs/make.bat
+++ /dev/null
@@ -1,281 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^` where ^ is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. epub3 to make an epub3
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- echo. coverage to run coverage check of the documentation if enabled
- echo. dummy to check syntax errors of document sources
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-REM Check if sphinx-build is available and fallback to Python version if any
-%SPHINXBUILD% 1>NUL 2>NUL
-if errorlevel 9009 goto sphinx_python
-goto sphinx_ok
-
-:sphinx_python
-
-set SPHINXBUILD=python -m sphinx.__init__
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-:sphinx_ok
-
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask-Ask.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask-Ask.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "epub3" (
- %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %~dp0
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %~dp0
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "coverage" (
- %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of coverage in the sources finished, look at the ^
-results in %BUILDDIR%/coverage/python.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-if "%1" == "dummy" (
- %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. Dummy builder generates no files.
- goto end
-)
-
-:end
diff --git a/f-ask/docs/requests.rst b/f-ask/docs/requests.rst
deleted file mode 100644
index 56ba348..0000000
--- a/f-ask/docs/requests.rst
+++ /dev/null
@@ -1,265 +0,0 @@
-Handling Requests
-=================
-
-With the Alexa Skills Kit, spoken phrases are mapped to actions executed on a server. Alexa converts
-speech into JSON and delivers the JSON to your application.
-For example, the phrase:
-
- "Alexa, Tell HelloApp to say hi to John"
-
-produces JSON like the following:
-
-.. code-block:: javascript
-
- "request": {
- "intent": {
- "name": "HelloIntent",
- "slots": {
- "firstname": {
- "name": "firstname",
- "value": "John"
- }
- }
- }
- ...
- }
-
-Parameters called 'slots' are defined and parsed out of speech at runtime.
-For example, the spoken word 'John' above is parsed into the slot named ``firstname`` with the ``AMAZON.US_FIRST_NAME``
-data type.
-
-For detailed information, see
-`Handling Requests Sent by Alexa `_
-on the Amazon developer website.
-
-This section shows how to process Alexa requests with Flask-Ask. It contains the following subsections:
-
-.. contents::
- :local:
- :backlinks: none
-
-Mapping Alexa Requests to View Functions
-----------------------------------------
-
-📼 Here is a video demo on `Handling Requests with Flask-Ask video `_.
-
-Flask-Ask has decorators to map Alexa requests to view functions.
-
-The ``launch`` decorator handles launch requests::
-
- @ask.launch
- def launched():
- return question('Welcome to Foo')
-
-The ``intent`` decorator handles intent requests::
-
- @ask.intent('HelloWorldIntent')
- def hello():
- return statement('Hello, world')
-
-The ``session_ended`` decorator is for the session ended request::
-
- @ask.session_ended
- def session_ended():
- return "{}", 200
-
-Launch and intent requests can both start sessions. Avoid duplicate code with the ``on_session_started`` callback::
-
- @ask.on_session_started
- def new_session():
- log.info('new session started')
-
-
-Mapping Intent Slots to View Function Parameters
-------------------------------------------------
-
-📼 Here is a video demo on `Intent Slots with Flask-Ask video `_.
-
-
-When Parameter and Slot Names Differ
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Tell Flask-Ask when slot and view function parameter names differ with ``mapping``::
-
- @ask.intent('WeatherIntent', mapping={'city': 'City'})
- def weather(city):
- return statement('I predict great weather for {}'.format(city))
-
-Above, the parameter ``city`` is mapped to the slot ``City``.
-
-
-Assigning Default Values when Slots are Empty
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Parameters are assigned a value of ``None`` if the Alexa service:
-
-* Does not return a corresponding slot in the request
-* Includes a corresponding slot without its ``value`` attribute
-* Includes a corresponding slot with an empty ``value`` attribute (e.g. ``""``)
-
-Use the ``default`` parameter for default values instead of ``None``. The default itself should be a
-literal or a callable that resolves to a value. The next example shows the literal ``'World'``::
-
- @ask.intent('HelloIntent', default={'name': 'World'})
- def hello(name):
- return statement('Hello, {}'.format(name))
-
-
-Converting Slots Values to Python Data Types
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-📼 Here is a video demo on `Slot Conversions with Flask-Ask video `_.
-
-When slot values are available, they're always assigned to parameters as strings. Convert to other Python
-data types with ``convert``. ``convert`` is a ``dict`` that maps parameter names to callables::
-
- @ask.intent('AddIntent', convert={'x': int, 'y': int})
- def add(x, y):
- z = x + y
- return statement('{} plus {} equals {}'.format(x, y, z))
-
-
-Above, ``x`` and ``y`` will both be passed to ``int()`` and thus converted to ``int`` instances.
-
-Flask-Ask provides convenient API constants for Amazon ``AMAZON.DATE``, ``AMAZON.TIME``, and ``AMAZON.DURATION``
-types exist since those are harder to build callables against. Instead of trying to define functions that work with
-inputs like those in Amazon's
-`documentation `_,
-just pass the strings in the second column below:
-
-📼 Here is a video demo on `Slot Conversion Helpers with Flask-Ask video `_.
-
-=================== =============== ======================
-Amazon Data Type String Python Data Type
-=================== =============== ======================
-``AMAZON.DATE`` ``'date'`` ``datetime.date``
-``AMAZON.TIME`` ``'time'`` ``datetime.time``
-``AMAZON.DURATION`` ``'timedelta'`` ``datetime.timedelta``
-=================== =============== ======================
-
-**Examples**
-
-.. code-block:: python
-
- convert={'the_date': 'date'}
-
-converts ``'2015-11-24'``, ``'2015-W48-WE'``, or ``'201X'`` into a ``datetime.date``
-
-.. code-block:: python
-
- convert={'appointment_time': 'time'}
-
-converts ``'06:00'``, ``'14:15'``, or ``'23:59'`` into a ``datetime.time``.
-
-.. code-block:: python
-
- convert={'ago': 'timedelta'}
-
-converts ``'PT10M'``, ``'PT45S'``, or ``'P2YT3H10M'`` into a ``datetime.timedelta``.
-
-
-Handling Conversion Errors
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Sometimes Alexa doesn't understand what's said, and slots come in with question marks:
-
-.. code-block:: javascript
-
- "slots": {
- "age": {
- "name": "age",
- "value": "?"
- }
- }
-
-Recover gracefully with the ``convert_errors`` context local. Import it to use it:
-
-.. code-block:: python
-
- ...
- from flask_ask import statement, question, convert_errors
-
-
- @ask.intent('AgeIntent', convert={'age': int})
- def say_age(age):
- if 'age' in convert_errors:
- # since age failed to convert, it keeps its string
- # value (e.g. "?") for later interrogation.
- return question("Can you please repeat your age?")
-
- # conversion guaranteed to have succeeded
- # age is an int
- return statement("Your age is {}".format(age))
-
-
-``convert_errors`` is a ``dict`` that maps parameter names to the ``Exceptions`` raised during
-conversion. When writing your own converters, raise ``Exceptions`` on failure, so
-they work with ``convert_errors``::
-
- def to_direction_const(s):
- if s.lower() not in ['left', 'right']
- raise Exception("must be left or right")
- return LEFT if s == 'left' else RIGHT
-
- @ask.intent('TurnIntent', convert={'direction': to_direction_const})
- def turn(direction):
- # do something with direction
- ...
-
-
-That ``convert_errors`` is a ``dict`` allows for granular error recovery::
-
- if 'something' in convert_errors:
- # Did something fail?
-
-or::
-
- if convert_errors:
- # Did anything fail?
-
-
-
-``session``, ``context``, ``request`` and ``version`` Context Locals
----------------------------------------------------------------------
-An Alexa
-`request payload `_
-has four top-level elements: ``session``, ``context``, ``request`` and ``version``. Like Flask, Flask-Ask provides `context
-locals `_ that spare you from having to add these as extra parameters to
-your functions. However, the ``request`` and ``session`` objects are distinct from Flask's ``request`` and ``session``.
-Flask-Ask's ``request``, ``context`` and ``session`` correspond to the Alexa request payload components while Flask's correspond
-to lower-level HTTP constructs.
-
-To use Flask-Ask's context locals, just import them::
-
- from flask import App
- from flask_ask import Ask, request, context, session, version
-
- app = Flask(__name__)
- ask = Ask(app)
- log = logging.getLogger()
-
- @ask.intent('ExampleIntent')
- def example():
- log.info("Request ID: {}".format(request.requestId))
- log.info("Request Type: {}".format(request.type))
- log.info("Request Timestamp: {}".format(request.timestamp))
- log.info("Session New?: {}".format(session.new))
- log.info("User ID: {}".format(session.user.userId))
- log.info("Alexa Version: {}".format(version))
- log.info("Device ID: {}".format(context.System.device.deviceId))
- log.info("Consent Token: {}".format(context.System.user.permissions.consentToken))
- ...
-
-If you want to use both Flask and Flask-Ask context locals in the same module, use ``import as``::
-
- from flask import App, request, session
- from flask_ask import (
- Ask,
- request as ask_request,
- session as ask_session,
- version
- )
-
-For a complete reference on ``request``, ``context`` and ``session`` fields, see the
-`JSON Interface Reference for Custom Skills `_
-in the Alexa Skills Kit documentation.
diff --git a/f-ask/docs/responses.rst b/f-ask/docs/responses.rst
deleted file mode 100644
index b262929..0000000
--- a/f-ask/docs/responses.rst
+++ /dev/null
@@ -1,152 +0,0 @@
-Building Responses
-==================
-
-📼 Here is a video demo on `Building Responses with Flask-Ask video `_ .
-
-The two primary constructs in Flask-Ask for creating responses are ``statement`` and ``question``.
-
-Statements terminate Echo sessions. The user is free to start another session, but Alexa will have no memory of it
-(unless persistence is programmed separately on the server with a database or the like).
-
-A ``question``, on the other hand, prompts the user for additional speech and keeps a session open.
-This session is similar to an HTTP session but the implementation is different. Since your application is
-communicating with the Alexa service instead of a browser, there are no cookies or local storage. Instead, the
-session is maintained in both the request and response JSON structures. In addition to the session component of
-questions, questions also allow a ``reprompt``, which is typically a rephrasing of the question if user did not answer
-the first time.
-
-This sections shows how to build responses with Flask-Ask. It contains the following subsections:
-
-.. contents::
- :local:
- :backlinks: none
-
-Telling with ``statement``
---------------------------
-``statement`` closes the session::
-
- @ask.intent('AllYourBaseIntent')
- def all_your_base():
- return statement('All your base are belong to us')
-
-
-Asking with ``question``
-------------------------
-Asking with ``question`` prompts the user for a response while keeping the session open::
-
- @ask.intent('AppointmentIntent')
- def make_appointment():
- return question("What day would you like to make an appointment for?")
-
-If the user doesn't respond, encourage them by rephrasing the question with ``reprompt``::
-
- @ask.intent('AppointmentIntent')
- def make_appointment():
- return question("What day would you like to make an appointment for?") \
- .reprompt("I didn't get that. When would you like to be seen?")
-
-
-Session Management
-------------------
-
-The ``session`` context local has an ``attributes`` dictionary for persisting information across requests::
-
- session.attributes['city'] = "San Francisco"
-
-When the response is rendered, the session attributes are automatically copied over into
-the response's ``sessionAttributes`` structure.
-
-The renderer looks for an ``attribute_encoder`` on the session. If the renderer finds one, it will pass it to
-``json.dumps`` as either that function's ``cls`` or ``default`` keyword parameters depending on whether
-a ``json.JSONEncoder`` or a function is used, respectively.
-
-Here's an example that uses a function::
-
- def _json_date_handler(obj):
- if isinstance(obj, datetime.date):
- return obj.isoformat()
-
- session.attributes['date'] = date
- session.attributes_encoder = _json_date_handler
-
-See the `json.dump documentation `_ for for details about
-that method's ``cls`` and ``default`` parameters.
-
-
-Automatic Handling of Plaintext and SSML
-----------------------------------------
-The Alexa Skills Kit supports plain text or
-`SSML `_ outputs. Flask-Ask automatically
-detects if your speech text contains SSML by attempting to parse it into XML, and checking
-if the root element is ``speak``::
-
- try:
- xmldoc = ElementTree.fromstring(text)
- if xmldoc.tag == 'speak':
- # output type is 'SSML'
- except ElementTree.ParseError:
- pass
- # output type is 'PlainText'
-
-
-Displaying Cards in the Alexa Smartphone/Tablet App
----------------------------------------------------
-In addition to speaking back, Flask-Ask can display contextual cards in the Alexa smartphone/tablet app. All four
-of the Alexa Skills Kit card types are supported.
-
-Simple cards display a title and message::
-
- @ask.intent('AllYourBaseIntent')
- def all_your_base():
- return statement('All your base are belong to us') \
- .simple_card(title='CATS says...', content='Make your time')
-
-Standard cards are like simple cards but they also support small and large image URLs::
-
- @ask.intent('AllYourBaseIntent')
- def all_your_base():
- return statement('All your base are belong to us') \
- .standard_card(title='CATS says...',
- text='Make your time',
- small_image_url='https://example.com/small.png',
- large_image_url='https://example.com/large.png')
-
-Link account cards display a link to authorize the Alexa user with a user account in your system. The link displayed is the auhorization URL you configure in the amazon skill developer portal::
-
- @ask.intent('AllYourBaseIntent')
- def all_your_base():
- return statement('Please link your account in the Alexa app') \
- .link_account_card()
-
-Consent cards ask for the permission to access the device's address. You can either ask for the country and postal code (`read::alexa:device:all:address:country_and_postal_code`) or for the full address (`read::alexa:device:all:address`). The permission you ask for has to match what you've specified in the amazon skill developer portal::
-
- @ask.intent('AllYourBaseIntent')
- def all_your_base():
- return statement('Please allow access to your location') \
- .consent_card("read::alexa:device:all:address")
-
-
-Jinja Templates
----------------
-You can also use Jinja templates. Define them in a YAML file named `templates.yaml` inside your application root::
-
- @ask.intent('RBelongToUsIntent')
- def all_your_base():
- notice = render_template('all_your_base_msg', who='us')
- return statement(notice)
-
-.. code-block:: yaml
-
- all_your_base_msg: All your base are belong to {{ who }}
-
- multiple_line_example: |
-
- I am a multi-line SSML template. My content spans more than one line,
- so there's a pipe and a newline that separates my name and value.
- Enjoy the sounds of the ocean.
-
-
-
-You can also use a custom templates file passed into the Ask object::
-
- ask = Ask(app, '/', None, 'custom-templates.yml')
diff --git a/f-ask/docs/user_contributions.rst b/f-ask/docs/user_contributions.rst
deleted file mode 100644
index 7b8f5d4..0000000
--- a/f-ask/docs/user_contributions.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-User Contributions
-==================
-
-Have an article or video to submit? Please send it to john@johnwheeler.org
-
-`Flask-Ask: A New Python Framework for Rapid Alexa Skills Kit Development `_
-
- by John Wheeler
-
-`Running with Alexa Part I. `_
-
- by Tim Kjær Lange
-
-`Intro and Skill Logic - Alexa Skills w/ Python and Flask-Ask Part 1 `_
-
- by Harrison Kinsley
-
-`Headlines Function - Alexa Skills w/ Python and Flask-Ask Part 2 `_
-
- by Harrison Kinsley
-
-`Testing our Skill - Alexa Skills w/ Python and Flask-Ask Part 3 `_
-
- by Harrison Kinsley
-
-`Flask-Ask — A tutorial on a simple and easy way to build complex Alexa Skills `_
-
- by Bjorn Vuylsteker
diff --git a/f-ask/flask_ask/__init__.py b/f-ask/flask_ask/__init__.py
deleted file mode 100644
index d878d12..0000000
--- a/f-ask/flask_ask/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import logging
-
-logger = logging.getLogger('flask_ask')
-logger.addHandler(logging.StreamHandler())
-if logger.level == logging.NOTSET:
- logger.setLevel(logging.WARN)
-
-
-from .core import (
- Ask,
- request,
- session,
- version,
- context,
- current_stream,
- convert_errors
-)
-
-from .models import (
- question,
- statement,
- audio,
- delegate,
- elicit_slot,
- confirm_slot,
- confirm_intent,
- buy,
- upsell,
- refund
-)
diff --git a/f-ask/flask_ask/cache.py b/f-ask/flask_ask/cache.py
deleted file mode 100644
index b810af0..0000000
--- a/f-ask/flask_ask/cache.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""
-Stream cache functions
-"""
-
-
-def push_stream(cache, user_id, stream):
- """
- Push a stream onto the stream stack in cache.
-
- :param cache: werkzeug BasicCache-like object
- :param user_id: id of user, used as key in cache
- :param stream: stream object to push onto stack
-
- :return: True on successful update,
- False if failed to update,
- None if invalid input was given
- """
- stack = cache.get(user_id)
- if stack is None:
- stack = []
- if stream:
- stack.append(stream)
- return cache.set(user_id, stack)
- return None
-
-
-def pop_stream(cache, user_id):
- """
- Pop an item off the stack in the cache. If stack
- is empty after pop, it deletes the stack.
-
- :param cache: werkzeug BasicCache-like object
- :param user_id: id of user, used as key in cache
-
- :return: top item from stack, otherwise None
- """
- stack = cache.get(user_id)
- if stack is None:
- return None
-
- result = stack.pop()
-
- if len(stack) == 0:
- cache.delete(user_id)
- else:
- cache.set(user_id, stack)
-
- return result
-
-
-def set_stream(cache, user_id, stream):
- """
- Overwrite stack in the cache.
-
- :param cache: werkzeug BasicCache-liek object
- :param user_id: id of user, used as key in cache
- :param stream: value to initialize new stack with
-
- :return: None
- """
- if stream:
- return cache.set(user_id, [stream])
-
-
-def top_stream(cache, user_id):
- """
- Peek at the top of the stack in the cache.
-
- :param cache: werkzeug BasicCache-like object
- :param user_id: id of user, used as key in cache
-
- :return: top item in user's cached stack, otherwise None
- """
- if not user_id:
- return None
-
- stack = cache.get(user_id)
- if stack is None:
- return None
- return stack.pop()
diff --git a/f-ask/flask_ask/convert.py b/f-ask/flask_ask/convert.py
deleted file mode 100644
index fcc6a8a..0000000
--- a/f-ask/flask_ask/convert.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import re
-from datetime import datetime, time
-
-import aniso8601
-
-from . import logger
-
-
-_DATE_PATTERNS = {
- # "today", "tomorrow", "november twenty-fifth": 2015-11-25
- '^\d{4}-\d{2}-\d{2}$': '%Y-%m-%d',
- # "this week", "next week": 2015-W48
- '^\d{4}-W\d{2}$': '%Y-W%U-%w',
- # "this weekend": 2015-W48-WE
- '^\d{4}-W\d{2}-WE$': '%Y-W%U-WE-%w',
- # "this month": 2015-11
- '^\d{4}-\d{2}$': '%Y-%m',
- # "next year": 2016
- '^\d{4}$': '%Y',
-}
-
-
-def to_date(amazon_date):
- # make so 'next decade' matches work against 'next year' regex
- amazon_date = re.sub('X$', '0', amazon_date)
- for re_pattern, format_pattern in list(_DATE_PATTERNS.items()):
- if re.match(re_pattern, amazon_date):
- if '%U' in format_pattern:
- # http://stackoverflow.com/a/17087427/1163855
- amazon_date += '-0'
- return datetime.strptime(amazon_date, format_pattern).date()
- return None
-
-
-def to_time(amazon_time):
- if amazon_time == "AM":
- return time(hour=0)
- if amazon_time == "PM":
- return time(hour=12)
- if amazon_time == "MO":
- return time(hour=5)
- if amazon_time == "AF":
- return time(hour=12)
- if amazon_time == "EV":
- return time(hour=17)
- if amazon_time == "NI":
- return time(hour=21)
- try:
- return aniso8601.parse_time(amazon_time)
- except ValueError as e:
- logger.warn("ValueError for amazon_time '{}'.".format(amazon_time))
- logger.warn("ValueError message: {}".format(e.message))
- return None
-
-
-def to_timedelta(amazon_duration):
- return aniso8601.parse_duration(amazon_duration)
diff --git a/f-ask/flask_ask/core.py b/f-ask/flask_ask/core.py
deleted file mode 100644
index bf006c1..0000000
--- a/f-ask/flask_ask/core.py
+++ /dev/null
@@ -1,951 +0,0 @@
-import os
-import sys
-import yaml
-import inspect
-import io
-from datetime import datetime
-from functools import wraps, partial
-
-import aniso8601
-from werkzeug.contrib.cache import SimpleCache
-from werkzeug.local import LocalProxy, LocalStack
-from jinja2 import BaseLoader, ChoiceLoader, TemplateNotFound
-from flask import current_app, json, request as flask_request, _app_ctx_stack
-
-from . import verifier, logger
-from .convert import to_date, to_time, to_timedelta
-from .cache import top_stream, set_stream
-import collections
-
-
-def find_ask():
- """
- Find our instance of Ask, navigating Local's and possible blueprints.
-
- Note: This only supports returning a reference to the first instance
- of Ask found.
- """
- if hasattr(current_app, 'ask'):
- return getattr(current_app, 'ask')
- else:
- if hasattr(current_app, 'blueprints'):
- blueprints = getattr(current_app, 'blueprints')
- for blueprint_name in blueprints:
- if hasattr(blueprints[blueprint_name], 'ask'):
- return getattr(blueprints[blueprint_name], 'ask')
-
-
-def dbgdump(obj, default=None, cls=None):
- if current_app.config.get('ASK_PRETTY_DEBUG_LOGS', False):
- indent = 2
- else:
- indent = None
- msg = json.dumps(obj, indent=indent, default=default, cls=cls)
- logger.debug(msg)
-
-
-request = LocalProxy(lambda: find_ask().request)
-session = LocalProxy(lambda: find_ask().session)
-version = LocalProxy(lambda: find_ask().version)
-context = LocalProxy(lambda: find_ask().context)
-convert_errors = LocalProxy(lambda: find_ask().convert_errors)
-current_stream = LocalProxy(lambda: find_ask().current_stream)
-stream_cache = LocalProxy(lambda: find_ask().stream_cache)
-
-from . import models
-
-
-_converters = {'date': to_date, 'time': to_time, 'timedelta': to_timedelta}
-
-
-class Ask(object):
- """The Ask object provides the central interface for interacting with the Alexa service.
-
- Ask object maps Alexa Requests to flask view functions and handles Alexa sessions.
- The constructor is passed a Flask App instance, and URL endpoint.
- The Flask instance allows the convienient API of endpoints and their view functions,
- so that Alexa requests may be mapped with syntax similar to a typical Flask server.
- Route provides the entry point for the skill, and must be provided if an app is given.
-
- Keyword Arguments:
- app {Flask object} -- App instance - created with Flask(__name__) (default: {None})
- route {str} -- entry point to which initial Alexa Requests are forwarded (default: {None})
- blueprint {Flask blueprint} -- Flask Blueprint instance to use instead of Flask App (default: {None})
- stream_cache {Werkzeug BasicCache} -- BasicCache-like object for storing Audio stream data (default: {SimpleCache})
- path {str} -- path to templates yaml file for VUI dialog (default: {'templates.yaml'})
- """
-
- def __init__(self, app=None, route=None, blueprint=None, stream_cache=None, path='templates.yaml'):
- self.app = app
- self._route = route
- self._intent_view_funcs = {}
- self._intent_converts = {}
- self._intent_defaults = {}
- self._intent_mappings = {}
- self._launch_view_func = None
- self._session_ended_view_func = None
- self._on_session_started_callback = None
- self._default_intent_view_func = None
- self._player_request_view_funcs = {}
- self._player_mappings = {}
- self._player_converts = {}
- if app is not None:
- self.init_app(app, path)
- elif blueprint is not None:
- self.init_blueprint(blueprint, path)
- if stream_cache is None:
- self.stream_cache = SimpleCache()
- else:
- self.stream_cache = stream_cache
-
- def init_app(self, app, path='templates.yaml'):
- """Initializes Ask app by setting configuration variables, loading templates, and maps Ask route to a flask view.
-
- The Ask instance is given the following configuration variables by calling on Flask's configuration:
-
- `ASK_APPLICATION_ID`:
-
- Turn on application ID verification by setting this variable to an application ID or a
- list of allowed application IDs. By default, application ID verification is disabled and a
- warning is logged. This variable should be set in production to ensure
- requests are being sent by the applications you specify.
- Default: None
-
- `ASK_VERIFY_REQUESTS`:
-
- Enables or disables Alexa request verification, which ensures requests sent to your skill
- are from Amazon's Alexa service. This setting should not be disabled in production.
- It is useful for mocking JSON requests in automated tests.
- Default: True
-
- `ASK_VERIFY_TIMESTAMP_DEBUG`:
-
- Turn on request timestamp verification while debugging by setting this to True.
- Timestamp verification helps mitigate against replay attacks. It relies on the system clock
- being synchronized with an NTP server. This setting should not be enabled in production.
- Default: False
-
- `ASK_PRETTY_DEBUG_LOGS`:
-
- Add tabs and linebreaks to the Alexa request and response printed to the debug log.
- This improves readability when printing to the console, but breaks formatting when logging to CloudWatch.
- Default: False
- """
- if self._route is None:
- raise TypeError("route is a required argument when app is not None")
-
- self.app = app
-
- app.ask = self
-
- app.add_url_rule(self._route, view_func=self._flask_view_func, methods=['POST'])
- app.jinja_loader = ChoiceLoader([app.jinja_loader, YamlLoader(app, path)])
-
- def init_blueprint(self, blueprint, path='templates.yaml'):
- """Initialize a Flask Blueprint, similar to init_app, but without the access
- to the application config.
-
- Keyword Arguments:
- blueprint {Flask Blueprint} -- Flask Blueprint instance to initialize (Default: {None})
- path {str} -- path to templates yaml file, relative to Blueprint (Default: {'templates.yaml'})
- """
- if self._route is not None:
- raise TypeError("route cannot be set when using blueprints!")
-
- # we need to tuck our reference to this Ask instance into the blueprint object and find it later!
- blueprint.ask = self
-
- # BlueprintSetupState.add_url_rule gets called underneath the covers and
- # concats the rule string, so we should set to an empty string to allow
- # Blueprint('blueprint_api', __name__, url_prefix="/ask") to result in
- # exposing the rule at "/ask" and not "/ask/".
- blueprint.add_url_rule("", view_func=self._flask_view_func, methods=['POST'])
- blueprint.jinja_loader = ChoiceLoader([YamlLoader(blueprint, path)])
-
- @property
- def ask_verify_requests(self):
- return current_app.config.get('ASK_VERIFY_REQUESTS', True)
-
- @property
- def ask_verify_timestamp_debug(self):
- return current_app.config.get('ASK_VERIFY_TIMESTAMP_DEBUG', False)
-
- @property
- def ask_application_id(self):
- return current_app.config.get('ASK_APPLICATION_ID', None)
-
- def on_session_started(self, f):
- """Decorator to call wrapped function upon starting a session.
-
- @ask.on_session_started
- def new_session():
- log.info('new session started')
-
- Because both launch and intent requests may begin a session, this decorator is used call
- a function regardless of how the session began.
-
- Arguments:
- f {function} -- function to be called when session is started.
- """
- self._on_session_started_callback = f
-
- def launch(self, f):
- """Decorator maps a view function as the endpoint for an Alexa LaunchRequest and starts the skill.
-
- @ask.launch
- def launched():
- return question('Welcome to Foo')
-
- The wrapped function is registered as the launch view function and renders the response
- for requests to the Launch URL.
- A request to the launch URL is verified with the Alexa server before the payload is
- passed to the view function.
-
- Arguments:
- f {function} -- Launch view function
- """
- self._launch_view_func = f
-
- @wraps(f)
- def wrapper(*args, **kw):
- self._flask_view_func(*args, **kw)
- return f
-
- def session_ended(self, f):
- """Decorator routes Alexa SessionEndedRequest to the wrapped view function to end the skill.
-
- @ask.session_ended
- def session_ended():
- return "{}", 200
-
- The wrapped function is registered as the session_ended view function
- and renders the response for requests to the end of the session.
-
- Arguments:
- f {function} -- session_ended view function
- """
- self._session_ended_view_func = f
-
- @wraps(f)
- def wrapper(*args, **kw):
- self._flask_view_func(*args, **kw)
- return f
-
- def intent(self, intent_name, mapping={}, convert={}, default={}):
- """Decorator routes an Alexa IntentRequest and provides the slot parameters to the wrapped function.
-
- Functions decorated as an intent are registered as the view function for the Intent's URL,
- and provide the backend responses to give your Skill its functionality.
-
- @ask.intent('WeatherIntent', mapping={'city': 'City'})
- def weather(city):
- return statement('I predict great weather for {}'.format(city))
-
- Arguments:
- intent_name {str} -- Name of the intent request to be mapped to the decorated function
-
- Keyword Arguments:
- mapping {dict} -- Maps parameters to intent slots of a different name
- default: {}
-
- convert {dict} -- Converts slot values to data types before assignment to parameters
- default: {}
-
- default {dict} -- Provides default values for Intent slots if Alexa reuqest
- returns no corresponding slot, or a slot with an empty value
- default: {}
- """
- def decorator(f):
- self._intent_view_funcs[intent_name] = f
- self._intent_mappings[intent_name] = mapping
- self._intent_converts[intent_name] = convert
- self._intent_defaults[intent_name] = default
-
- @wraps(f)
- def wrapper(*args, **kw):
- self._flask_view_func(*args, **kw)
- return f
- return decorator
-
- def default_intent(self, f):
- """Decorator routes any Alexa IntentRequest that is not matched by any existing @ask.intent routing."""
- self._default_intent_view_func = f
-
- @wraps(f)
- def wrapper(*args, **kw):
- self._flask_view_func(*args, **kw)
- return f
-
- def display_element_selected(self, f):
- """Decorator routes Alexa Display.ElementSelected request to the wrapped view function.
-
- @ask.display_element_selected
- def eval_element():
- return "", 200
-
- The wrapped function is registered as the display_element_selected view function
- and renders the response for requests.
-
- Arguments:
- f {function} -- display_element_selected view function
- """
- self._display_element_selected_func = f
-
- @wraps(f)
- def wrapper(*args, **kw):
- self._flask_view_func(*args, **kw)
- return f
-
-
- def on_purchase_completed(self, mapping={'payload': 'payload','name':'name','status':'status','token':'token'}, convert={}, default={}):
- """Decorator routes an Connections.Response to the wrapped function.
-
- Request is sent when Alexa completes the purchase flow.
- See https://developer.amazon.com/docs/in-skill-purchase/add-isps-to-a-skill.html#handle-results
-
-
- The wrapped view function may accept parameters from the Request.
- In addition to locale, requestId, timestamp, and type
-
-
- @ask.on_purchase_completed( mapping={'payload': 'payload','name':'name','status':'status','token':'token'})
- def completed(payload, name, status, token):
- logger.info(payload)
- logger.info(name)
- logger.info(status)
- logger.info(token)
-
- """
- def decorator(f):
- self._intent_view_funcs['Connections.Response'] = f
- self._intent_mappings['Connections.Response'] = mapping
- self._intent_converts['Connections.Response'] = convert
- self._intent_defaults['Connections.Response'] = default
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
-
- def on_playback_started(self, mapping={'offset': 'offsetInMilliseconds'}, convert={}, default={}):
- """Decorator routes an AudioPlayer.PlaybackStarted Request to the wrapped function.
-
- Request sent when Alexa begins playing the audio stream previously sent in a Play directive.
- This lets your skill verify that playback began successfully.
- This request is also sent when Alexa resumes playback after pausing it for a voice request.
-
- The wrapped view function may accept parameters from the AudioPlayer Request.
- In addition to locale, requestId, timestamp, and type
- AudioPlayer Requests include:
- offsetInMilliseconds - Position in stream when request was sent.
- Not end of stream, often few ms after Play Directive offset.
- This parameter is automatically mapped to 'offset' by default
-
- token - token of the stream that is nearly finished.
-
- @ask.on_playback_started()
- def on_playback_start(token, offset):
- logger.info('stream has token {}'.format(token))
- logger.info('Current position within the stream is {} ms'.format(offset))
- """
- def decorator(f):
- self._intent_view_funcs['AudioPlayer.PlaybackStarted'] = f
- self._intent_mappings['AudioPlayer.PlaybackStarted'] = mapping
- self._intent_converts['AudioPlayer.PlaybackStarted'] = convert
- self._intent_defaults['AudioPlayer.PlaybackStarted'] = default
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
- def on_playback_finished(self, mapping={'offset': 'offsetInMilliseconds'}, convert={}, default={}):
- """Decorator routes an AudioPlayer.PlaybackFinished Request to the wrapped function.
-
- This type of request is sent when the stream Alexa is playing comes to an end on its own.
-
- Note: If your skill explicitly stops the playback with the Stop directive,
- Alexa sends PlaybackStopped instead of PlaybackFinished.
-
- The wrapped view function may accept parameters from the AudioPlayer Request.
- In addition to locale, requestId, timestamp, and type
- AudioPlayer Requests include:
- offsetInMilliseconds - Position in stream when request was sent.
- Not end of stream, often few ms after Play Directive offset.
- This parameter is automatically mapped to 'offset' by default.
-
- token - token of the stream that is nearly finished.
-
- Audioplayer Requests do not include the stream URL, it must be accessed from current_stream.url
- """
- def decorator(f):
- self._intent_view_funcs['AudioPlayer.PlaybackFinished'] = f
- self._intent_mappings['AudioPlayer.PlaybackFinished'] = mapping
- self._intent_converts['AudioPlayer.PlaybackFinished'] = convert
- self._intent_defaults['AudioPlayer.PlaybackFinished'] = default
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
- def on_playback_stopped(self, mapping={'offset': 'offsetInMilliseconds'}, convert={}, default={}):
- """Decorator routes an AudioPlayer.PlaybackStopped Request to the wrapped function.
-
- Sent when Alexa stops playing an audio stream in response to one of the following:
- -AudioPlayer.Stop
- -AudioPlayer.Play with a playBehavior of REPLACE_ALL.
- -AudioPlayer.ClearQueue with a clearBehavior of CLEAR_ALL.
-
- This request is also sent if the user makes a voice request to Alexa,
- since this temporarily pauses the playback.
- In this case, the playback begins automatically once the voice interaction is complete.
-
- Note: If playback stops because the audio stream comes to an end on its own,
- Alexa sends PlaybackFinished instead of PlaybackStopped.
-
- The wrapped view function may accept parameters from the AudioPlayer Request.
- In addition to locale, requestId, timestamp, and type
- AudioPlayer Requests include:
- offsetInMilliseconds - Position in stream when request was sent.
- Not end of stream, often few ms after Play Directive offset.
- This parameter is automatically mapped to 'offset' by default.
-
- token - token of the stream that is nearly finished.
-
- Audioplayer Requests do not include the stream URL, it must be accessed from current_stream.url
- """
- def decorator(f):
- self._intent_view_funcs['AudioPlayer.PlaybackStopped'] = f
- self._intent_mappings['AudioPlayer.PlaybackStopped'] = mapping
- self._intent_converts['AudioPlayer.PlaybackStopped'] = convert
- self._intent_defaults['AudioPlayer.PlaybackStopped'] = default
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
- def on_playback_nearly_finished(self, mapping={'offset': 'offsetInMilliseconds'}, convert={}, default={}):
- """Decorator routes an AudioPlayer.PlaybackNearlyFinished Request to the wrapped function.
-
- This AudioPlayer Request sent when the device is ready to receive a new stream.
- To progress through a playlist, respond to this request with an enqueue or play_next audio response.
-
- **Note** that this request is sent when Alexa is ready to receive a new stream to enqueue, and not
- necessarily when the stream's offset is near the end.
- The request may be sent by Alexa immediately after your skill sends a Play Directive.
-
- The wrapped view function may accept parameters from the AudioPlayer Request.
- In addition to locale, requestId, timestamp, and type
- This AudioPlayer Request includes:
- AudioPlayer Requests include:
- offsetInMilliseconds - Position in stream when request was sent.
- Not end of stream, often few ms after Play Directive offset.
- This parameter is automatically mapped to 'offset' by default.
-
- token - token of the stream that is nearly finished.
-
- Audioplayer Requests do not include the stream URL, and must be accessed from current_stream
-
- Example usage:
-
- @ask.on_playback_nearly_finished()
- def play_next_stream():
- audio().enqueue(my_next_song)
-
- # offsetInMilliseconds is mapped to offset by default for convenience
- @ask.on_playback_nearly_finished()
- def show_request_feedback(offset, token):
- logging.info('Nearly Finished')
- logging.info('Stream at {} ms when Playback Request sent'.format(offset))
- logging.info('Stream holds the token {}'.format(token))
- logging.info('Streaming from {}'.format(current_stream.url))
-
- # example of changing the default parameter mapping
- @ask.on_playback_nearly_finished(mapping={'pos': 'offsetInMilliseconds', 'stream_token': 'token'})
- def show_request_feedback(pos, stream_token):
- _infodump('Nearly Finished')
- _infodump('Stream at {} ms when Playback Request sent'.format(pos))
- _infodump('Stream holds the token {}'.format(stream_token))
- """
- def decorator(f):
- self._intent_view_funcs['AudioPlayer.PlaybackNearlyFinished'] = f
- self._intent_mappings['AudioPlayer.PlaybackNearlyFinished'] = mapping
- self._intent_converts['AudioPlayer.PlaybackNearlyFinished'] = convert
- self._intent_defaults['AudioPlayer.PlaybackNearlyFinished'] = default
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
- def on_playback_failed(self, mapping={}, convert={}, default={}):
- """Decorator routes an AudioPlayer.PlaybackFailed Request to the wrapped function.
-
- This AudioPlayer Request sent when Alexa encounters an error when attempting to play a stream.
-
- The wrapped view function may accept parameters from the AudioPlayer Request.
- In addition to locale, requestId, timestamp, and type
-
- PlayBackFailed Requests include:
- error - Contains error info under parameters type and message
-
- token - represents the stream that failed to play.
-
- currentPlaybackState - Details about the playback activity occurring at the time of the error
- Contains the following parameters:
-
- token - represents the audio stream currently playing when the error occurred.
- Note that this may be different from the value of the request.token property.
-
- offsetInMilliseconds - Position in stream when request was sent.
- Not end of stream, often few ms after Play Directive offset.
- This parameter is automatically mapped to 'offset' by default.
-
- playerActivity - player state when the error occurred
- """
- def decorator(f):
- self._intent_view_funcs['AudioPlayer.PlaybackFailed'] = f
- self._intent_mappings['AudioPlayer.PlaybackFailed'] = mapping
- self._intent_converts['AudioPlayer.PlaybackFailed'] = convert
- self._intent_defaults['AudioPlayer.PlaybackFailed'] = default
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- self._flask_view_func(*args, **kwargs)
- return f
- return decorator
-
- @property
- def request(self):
- return getattr(_app_ctx_stack.top, '_ask_request', None)
-
- @request.setter
- def request(self, value):
- _app_ctx_stack.top._ask_request = value
-
- @property
- def session(self):
- return getattr(_app_ctx_stack.top, '_ask_session', models._Field())
-
- @session.setter
- def session(self, value):
- _app_ctx_stack.top._ask_session = value
-
- @property
- def version(self):
- return getattr(_app_ctx_stack.top, '_ask_version', None)
-
- @version.setter
- def version(self, value):
- _app_ctx_stack.top._ask_version = value
-
- @property
- def context(self):
- return getattr(_app_ctx_stack.top, '_ask_context', None)
-
- @context.setter
- def context(self, value):
- _app_ctx_stack.top._ask_context = value
-
- @property
- def convert_errors(self):
- return getattr(_app_ctx_stack.top, '_ask_convert_errors', None)
-
- @convert_errors.setter
- def convert_errors(self, value):
- _app_ctx_stack.top._ask_convert_errors = value
-
- @property
- def current_stream(self):
- #return getattr(_app_ctx_stack.top, '_ask_current_stream', models._Field())
- user = self._get_user()
- if user:
- stream = top_stream(self.stream_cache, user)
- if stream:
- current = models._Field()
- current.__dict__.update(stream)
- return current
- return models._Field()
-
- @current_stream.setter
- def current_stream(self, value):
- # assumption 1 is we get a models._Field as value
- # assumption 2 is if someone sets a value, it's resetting the stack
- user = self._get_user()
- if user:
- set_stream(self.stream_cache, user, value.__dict__)
-
- def run_aws_lambda(self, event):
- """Invoke the Flask Ask application from an AWS Lambda function handler.
-
- Use this method to service AWS Lambda requests from a custom Alexa
- skill. This method will invoke your Flask application providing a
- WSGI-compatible environment that wraps the original Alexa event
- provided to the AWS Lambda handler. Returns the output generated by
- a Flask Ask application, which should be used as the return value
- to the AWS Lambda handler function.
-
- Example usage:
-
- from flask import Flask
- from flask_ask import Ask, statement
-
- app = Flask(__name__)
- ask = Ask(app, '/')
-
- # This function name is what you defined when you create an
- # AWS Lambda function. By default, AWS calls this function
- # lambda_handler.
- def lambda_handler(event, _context):
- return ask.run_aws_lambda(event)
-
- @ask.intent('HelloIntent')
- def hello(firstname):
- speech_text = "Hello %s" % firstname
- return statement(speech_text).simple_card('Hello', speech_text)
- """
-
- # We are guaranteed to be called by AWS as a Lambda function does not
- # expose a public facing interface.
- self.app.config['ASK_VERIFY_REQUESTS'] = False
-
- # Convert an environment variable to a WSGI "bytes-as-unicode" string
- enc, esc = sys.getfilesystemencoding(), 'surrogateescape'
- def unicode_to_wsgi(u):
- return u.encode(enc, esc).decode('iso-8859-1')
-
- # Create a WSGI-compatible environ that can be passed to the
- # application. It is loaded with the OS environment variables,
- # mandatory CGI-like variables, as well as the mandatory WSGI
- # variables.
- environ = {k: unicode_to_wsgi(v) for k, v in os.environ.items()}
- environ['REQUEST_METHOD'] = 'POST'
- environ['PATH_INFO'] = '/'
- environ['SERVER_NAME'] = 'AWS-Lambda'
- environ['SERVER_PORT'] = '80'
- environ['SERVER_PROTOCOL'] = 'HTTP/1.0'
- environ['wsgi.version'] = (1, 0)
- environ['wsgi.url_scheme'] = 'http'
- environ['wsgi.errors'] = sys.stderr
- environ['wsgi.multithread'] = False
- environ['wsgi.multiprocess'] = False
- environ['wsgi.run_once'] = True
-
- # Convert the event provided by the AWS Lambda handler to a JSON
- # string that can be read as the body of a HTTP POST request.
- body = json.dumps(event)
- environ['CONTENT_TYPE'] = 'application/json'
- environ['CONTENT_LENGTH'] = len(body)
-
- PY3 = sys.version_info[0] == 3
-
- if PY3:
- environ['wsgi.input'] = io.StringIO(body)
- else:
- environ['wsgi.input'] = io.BytesIO(body)
-
- # Start response is a required callback that must be passed when
- # the application is invoked. It is used to set HTTP status and
- # headers. Read the WSGI spec for details (PEP3333).
- headers = []
- def start_response(status, response_headers, _exc_info=None):
- headers[:] = [status, response_headers]
-
- # Invoke the actual Flask application providing our environment,
- # with our Alexa event as the body of the HTTP request, as well
- # as the callback function above. The result will be an iterator
- # that provides a serialized JSON string for our Alexa response.
- result = self.app(environ, start_response)
- try:
- if not headers:
- raise AssertionError("start_response() not called by WSGI app")
-
- output = b"".join(result)
- if not headers[0].startswith("2"):
- raise AssertionError("Non-2xx from app: hdrs={}, body={}".format(headers, output))
-
- # The Lambda handler expects a Python object that can be
- # serialized as JSON, so we need to take the already serialized
- # JSON and deserialize it.
- return json.loads(output)
-
- finally:
- # Per the WSGI spec, we need to invoke the close method if it
- # is implemented on the result object.
- if hasattr(result, 'close'):
- result.close()
-
-
- def _get_user(self):
- if self.context:
- return self.context.get('System', {}).get('user', {}).get('userId')
- return None
-
-
- def _alexa_request(self, verify=True):
- raw_body = flask_request.data
- alexa_request_payload = json.loads(raw_body)
-
- if verify:
- cert_url = flask_request.headers['Signaturecertchainurl']
- signature = flask_request.headers['Signature']
-
- # load certificate - this verifies a the certificate url and format under the hood
- cert = verifier.load_certificate(cert_url)
- # verify signature
- verifier.verify_signature(cert, signature, raw_body)
-
- # verify timestamp
- raw_timestamp = alexa_request_payload.get('request', {}).get('timestamp')
- timestamp = self._parse_timestamp(raw_timestamp)
-
- if not current_app.debug or self.ask_verify_timestamp_debug:
- verifier.verify_timestamp(timestamp)
-
- # verify application id
- try:
- application_id = alexa_request_payload['session']['application']['applicationId']
- except KeyError:
- application_id = alexa_request_payload['context'][
- 'System']['application']['applicationId']
- if self.ask_application_id is not None:
- verifier.verify_application_id(application_id, self.ask_application_id)
-
- return alexa_request_payload
-
- @staticmethod
- def _parse_timestamp(timestamp):
- """
- Parse a given timestamp value, raising ValueError if None or Flasey
- """
- if timestamp:
- try:
- return aniso8601.parse_datetime(timestamp)
- except AttributeError:
- # raised by aniso8601 if raw_timestamp is not valid string
- # in ISO8601 format
- try:
- return datetime.utcfromtimestamp(timestamp)
- except:
- # relax the timestamp a bit in case it was sent in millis
- return datetime.utcfromtimestamp(timestamp/1000)
-
- raise ValueError('Invalid timestamp value! Cannot parse from either ISO8601 string or UTC timestamp.')
-
-
- def _update_stream(self):
- fresh_stream = models._Field()
- fresh_stream.__dict__.update(self.current_stream.__dict__) # keeps url attribute after stopping stream
- fresh_stream.__dict__.update(self._from_directive())
-
- context_info = self._from_context()
- if context_info != None:
- fresh_stream.__dict__.update(context_info)
-
- self.current_stream = fresh_stream
- dbgdump(current_stream.__dict__)
-
- def _from_context(self):
- return getattr(self.context, 'AudioPlayer', {})
-
- def _from_directive(self):
- from_buffer = top_stream(self.stream_cache, self._get_user())
- if from_buffer:
- if self.request.intent and 'PauseIntent' in self.request.intent.name:
- return {}
- return from_buffer
- return {}
-
- def _flask_view_func(self, *args, **kwargs):
- ask_payload = self._alexa_request(verify=self.ask_verify_requests)
- dbgdump(ask_payload)
- request_body = models._Field(ask_payload)
-
- self.request = request_body.request
- self.version = request_body.version
- self.context = getattr(request_body, 'context', models._Field())
- self.session = getattr(request_body, 'session', self.session) # to keep old session.attributes through AudioRequests
-
- if not self.session:
- self.session = models._Field()
- if not self.session.attributes:
- self.session.attributes = models._Field()
-
- self._update_stream()
-
- # add current dialog state in session
- try:
- self.session["dialogState"] = request.dialogState
- except KeyError:
- self.session["dialogState"] = "unknown"
-
- try:
- if self.session.new and self._on_session_started_callback is not None:
- self._on_session_started_callback()
- except AttributeError:
- pass
-
- result = None
- request_type = self.request.type
-
- if request_type == 'LaunchRequest' and self._launch_view_func:
- result = self._launch_view_func()
- elif request_type == 'SessionEndedRequest':
- if self._session_ended_view_func:
- result = self._session_ended_view_func()
- else:
- result = "{}", 200
- elif request_type == 'IntentRequest' and self._intent_view_funcs:
- result = self._map_intent_to_view_func(self.request.intent)()
- elif request_type == 'Display.ElementSelected' and self._display_element_selected_func:
- result = self._display_element_selected_func()
- elif 'AudioPlayer' in request_type:
- result = self._map_player_request_to_func(self.request.type)()
- # routes to on_playback funcs
- # user can also access state of content.AudioPlayer with current_stream
- elif 'Connections.Response' in request_type:
- result = self._map_purchase_request_to_func(self.request.type)()
-
- if result is not None:
- if isinstance(result, models._Response):
- return result.render_response()
- return result
- return "", 400
-
- def _map_intent_to_view_func(self, intent):
- """Provides appropiate parameters to the intent functions."""
- if intent.name in self._intent_view_funcs:
- view_func = self._intent_view_funcs[intent.name]
- elif self._default_intent_view_func is not None:
- view_func = self._default_intent_view_func
- else:
- raise NotImplementedError('Intent "{}" not found and no default intent specified.'.format(intent.name))
-
- PY3 = sys.version_info[0] == 3
- if PY3:
- argspec = inspect.getfullargspec(view_func)
- else:
- argspec = inspect.getargspec(view_func)
-
- arg_names = argspec.args
- arg_values = self._map_params_to_view_args(intent.name, arg_names)
-
- return partial(view_func, *arg_values)
-
- def _map_player_request_to_func(self, player_request_type):
- """Provides appropriate parameters to the on_playback functions."""
- # calbacks for on_playback requests are optional
- view_func = self._intent_view_funcs.get(player_request_type, lambda: None)
-
- argspec = inspect.getargspec(view_func)
- arg_names = argspec.args
- arg_values = self._map_params_to_view_args(player_request_type, arg_names)
-
- return partial(view_func, *arg_values)
-
- def _map_purchase_request_to_func(self, purchase_request_type):
- """Provides appropriate parameters to the on_purchase functions."""
-
- if purchase_request_type in self._intent_view_funcs:
- view_func = self._intent_view_funcs[purchase_request_type]
- else:
- raise NotImplementedError('Request type "{}" not found and no default view specified.'.format(purchase_request_type))
-
- argspec = inspect.getargspec(view_func)
- arg_names = argspec.args
- arg_values = self._map_params_to_view_args(purchase_request_type, arg_names)
-
- print('_map_purchase_request_to_func', arg_names, arg_values, view_func, purchase_request_type)
- return partial(view_func, *arg_values)
-
- def _get_slot_value(self, slot_object):
- slot_name = slot_object.name
- slot_value = getattr(slot_object, 'value', None)
- resolutions = getattr(slot_object, 'resolutions', None)
-
- if resolutions is not None:
- resolutions_per_authority = getattr(resolutions, 'resolutionsPerAuthority', None)
- if resolutions_per_authority is not None and len(resolutions_per_authority) > 0:
- values = resolutions_per_authority[0].get('values', None)
- if values is not None and len(values) > 0:
- value = values[0].get('value', None)
- if value is not None:
- slot_value = value.get('name', slot_value)
-
- return slot_value
-
- def _map_params_to_view_args(self, view_name, arg_names):
-
- arg_values = []
- convert = self._intent_converts.get(view_name)
- default = self._intent_defaults.get(view_name)
- mapping = self._intent_mappings.get(view_name)
-
- convert_errors = {}
-
- request_data = {}
- intent = getattr(self.request, 'intent', None)
- if intent is not None:
- if intent.slots is not None:
- for slot_key in intent.slots.keys():
- slot_object = getattr(intent.slots, slot_key)
- request_data[slot_object.name] = self._get_slot_value(slot_object=slot_object)
-
- else:
- for param_name in self.request:
- request_data[param_name] = getattr(self.request, param_name, None)
-
- for arg_name in arg_names:
- param_or_slot = mapping.get(arg_name, arg_name)
- arg_value = request_data.get(param_or_slot)
- if arg_value is None or arg_value == "":
- if arg_name in default:
- default_value = default[arg_name]
- if isinstance(default_value, collections.Callable):
- default_value = default_value()
- arg_value = default_value
- elif arg_name in convert:
- shorthand_or_function = convert[arg_name]
- if shorthand_or_function in _converters:
- shorthand = shorthand_or_function
- convert_func = _converters[shorthand]
- else:
- convert_func = shorthand_or_function
- try:
- arg_value = convert_func(arg_value)
- except Exception as e:
- convert_errors[arg_name] = e
- arg_values.append(arg_value)
- self.convert_errors = convert_errors
- return arg_values
-
-
-class YamlLoader(BaseLoader):
-
- def __init__(self, app, path):
- self.path = app.root_path + os.path.sep + path
- self.mapping = {}
- self._reload_mapping()
-
- def _reload_mapping(self):
- if os.path.isfile(self.path):
- self.last_mtime = os.path.getmtime(self.path)
- with open(self.path) as f:
- self.mapping = yaml.safe_load(f.read())
-
- def get_source(self, environment, template):
- if not os.path.isfile(self.path):
- return None, None, None
- if self.last_mtime != os.path.getmtime(self.path):
- self._reload_mapping()
- if template in self.mapping:
- source = self.mapping[template]
- return source, None, lambda: source == self.mapping.get(template)
- raise TemplateNotFound(template)
diff --git a/f-ask/flask_ask/models.py b/f-ask/flask_ask/models.py
deleted file mode 100644
index d159f7b..0000000
--- a/f-ask/flask_ask/models.py
+++ /dev/null
@@ -1,460 +0,0 @@
-import inspect
-from flask import json
-from xml.etree import ElementTree
-import aniso8601
-from .core import session, context, current_stream, stream_cache, dbgdump
-from .cache import push_stream
-import uuid
-
-
-class _Field(dict):
- """Container to represent Alexa Request Data.
-
- Initialized with request_json and creates a dict object with attributes
- to be accessed via dot notation or as a dict key-value.
-
- Parameters within the request_json that contain their data as a json object
- are also represented as a _Field object.
-
- Example:
-
- payload_object = _Field(alexa_json_payload)
-
- request_type_from_keys = payload_object['request']['type']
- request_type_from_attrs = payload_object.request.type
-
- assert request_type_from_keys == request_type_from_attrs
- """
-
- def __init__(self, request_json={}):
- super(_Field, self).__init__(request_json)
- for key, value in request_json.items():
- if isinstance(value, dict):
- value = _Field(value)
- self[key] = value
-
- def __getattr__(self, attr):
- # converts timestamp str to datetime.datetime object
- if 'timestamp' in attr:
- return aniso8601.parse_datetime(self.get(attr))
- return self.get(attr)
-
- def __setattr__(self, key, value):
- self.__setitem__(key, value)
-
-
-class _Response(object):
-
- def __init__(self, speech):
- self._json_default = None
- self._response = {
- 'outputSpeech': _output_speech(speech)
- }
-
- def simple_card(self, title=None, content=None):
- card = {
- 'type': 'Simple',
- 'title': title,
- 'content': content
- }
- self._response['card'] = card
- return self
-
- def standard_card(self, title=None, text=None, small_image_url=None, large_image_url=None):
- card = {
- 'type': 'Standard',
- 'title': title,
- 'text': text
- }
-
- if any((small_image_url, large_image_url)):
- card['image'] = {}
- if small_image_url is not None:
- card['image']['smallImageUrl'] = small_image_url
- if large_image_url is not None:
- card['image']['largeImageUrl'] = large_image_url
-
- self._response['card'] = card
- return self
-
- def list_display_render(self, template=None, title=None, backButton='HIDDEN', token=None, background_image_url=None, image=None, listItems=None, hintText=None):
- directive = [
- {
- 'type': 'Display.RenderTemplate',
- 'template': {
- 'type': template,
- 'backButton': backButton,
- 'title': title,
- 'listItems': listItems
- }
- }
- ]
-
- if background_image_url is not None:
- directive[0]['template']['backgroundImage'] = {
- 'sources': [
- {'url': background_image_url}
- ]
- }
-
- if hintText is not None:
- hint = {
- 'type':'Hint',
- 'hint': {
- 'type':"PlainText",
- 'text': hintText
- }
- }
- directive.append(hint)
- self._response['directives'] = directive
- return self
-
- def display_render(self, template=None, title=None, backButton='HIDDEN', token=None, background_image_url=None, image=None, text=None, hintText=None):
- directive = [
- {
- 'type': 'Display.RenderTemplate',
- 'template': {
- 'type': template,
- 'backButton': backButton,
- 'title': title,
- 'textContent': text
- }
- }
- ]
-
- if background_image_url is not None:
- directive[0]['template']['backgroundImage'] = {
- 'sources': [
- {'url': background_image_url}
- ]
- }
-
- if image is not None:
- directive[0]['template']['image'] = {
- 'sources': [
- {'url': image}
- ]
- }
-
- if token is not None:
- directive[0]['template']['token'] = token
-
- if hintText is not None:
- hint = {
- 'type':'Hint',
- 'hint': {
- 'type':"PlainText",
- 'text': hintText
- }
- }
- directive.append(hint)
-
- self._response['directives'] = directive
- return self
-
- def link_account_card(self):
- card = {'type': 'LinkAccount'}
- self._response['card'] = card
- return self
-
- def consent_card(self, permissions):
- card = {
- 'type': 'AskForPermissionsConsent',
- 'permissions': [permissions]
- }
- self._response['card'] = card
- return self
-
- def render_response(self):
- response_wrapper = {
- 'version': '1.0',
- 'response': self._response,
- 'sessionAttributes': session.attributes
- }
-
- kw = {}
- if hasattr(session, 'attributes_encoder'):
- json_encoder = session.attributes_encoder
- kwargname = 'cls' if inspect.isclass(json_encoder) else 'default'
- kw[kwargname] = json_encoder
- dbgdump(response_wrapper, **kw)
-
- return json.dumps(response_wrapper, **kw)
-
-
-class statement(_Response):
-
- def __init__(self, speech):
- super(statement, self).__init__(speech)
- self._response['shouldEndSession'] = True
-
-
-class question(_Response):
-
- def __init__(self, speech):
- super(question, self).__init__(speech)
- self._response['shouldEndSession'] = False
-
- def reprompt(self, reprompt):
- reprompt = {'outputSpeech': _output_speech(reprompt)}
- self._response['reprompt'] = reprompt
- return self
-
-
-class buy(_Response):
-
- def __init__(self, productId=None):
- self._response = {
- 'shouldEndSession': True,
- 'directives': [{
- 'type': 'Connections.SendRequest',
- 'name': 'Buy',
- 'payload': {
- 'InSkillProduct': {
- 'productId': productId
- }
- },
- 'token': 'correlationToken'
- }]
- }
-
-
-class refund(_Response):
-
- def __init__(self, productId=None):
- self._response = {
- 'shouldEndSession': True,
- 'directives': [{
- 'type': 'Connections.SendRequest',
- 'name': 'Cancel',
- 'payload': {
- 'InSkillProduct': {
- 'productId': productId
- }
- },
- 'token': 'correlationToken'
- }]
- }
-
-class upsell(_Response):
-
- def __init__(self, productId=None, msg=None):
- self._response = {
- 'shouldEndSession': True,
- 'directives': [{
- 'type': 'Connections.SendRequest',
- 'name': 'Upsell',
- 'payload': {
- 'InSkillProduct': {
- 'productId': productId
- },
- 'upsellMessage': msg
- },
- 'token': 'correlationToken'
- }]
- }
-
-class delegate(_Response):
-
- def __init__(self, updated_intent=None):
- self._response = {
- 'shouldEndSession': False,
- 'directives': [{'type': 'Dialog.Delegate'}]
- }
-
- if updated_intent:
- self._response['directives'][0]['updatedIntent'] = updated_intent
-
-
-class elicit_slot(_Response):
- """
- Sends an ElicitSlot directive.
- slot - The slot name to elicit
- speech - The output speech
- updated_intent - Optional updated intent
- """
-
- def __init__(self, slot, speech, updated_intent=None):
- self._response = {
- 'shouldEndSession': False,
- 'directives': [{
- 'type': 'Dialog.ElicitSlot',
- 'slotToElicit': slot,
- }],
- 'outputSpeech': _output_speech(speech),
- }
-
- if updated_intent:
- self._response['directives'][0]['updatedIntent'] = updated_intent
-
-class confirm_slot(_Response):
- """
- Sends a ConfirmSlot directive.
- slot - The slot name to confirm
- speech - The output speech
- updated_intent - Optional updated intent
- """
-
- def __init__(self, slot, speech, updated_intent=None):
- self._response = {
- 'shouldEndSession': False,
- 'directives': [{
- 'type': 'Dialog.ConfirmSlot',
- 'slotToConfirm': slot,
- }],
- 'outputSpeech': _output_speech(speech),
- }
-
- if updated_intent:
- self._response['directives'][0]['updatedIntent'] = updated_intent
-
-class confirm_intent(_Response):
- """
- Sends a ConfirmIntent directive.
-
- """
- def __init__(self, speech, updated_intent=None):
- self._response = {
- 'shouldEndSession': False,
- 'directives': [{
- 'type': 'Dialog.ConfirmIntent',
- }],
- 'outputSpeech': _output_speech(speech),
- }
-
- if updated_intent:
- self._response['directives'][0]['updatedIntent'] = updated_intent
-
-
-class audio(_Response):
- """Returns a response object with an Amazon AudioPlayer Directive.
-
- Responses for LaunchRequests and IntentRequests may include outputSpeech in addition to an audio directive
-
- Note that responses to AudioPlayer requests do not allow outputSpeech.
- These must only include AudioPlayer Directives.
-
- @ask.intent('PlayFooAudioIntent')
- def play_foo_audio():
- speech = 'playing from foo'
- stream_url = www.foo.com
- return audio(speech).play(stream_url)
-
-
- @ask.intent('AMAZON.PauseIntent')
- def stop_audio():
- return audio('Ok, stopping the audio').stop()
- """
-
- def __init__(self, speech=''):
- super(audio, self).__init__(speech)
- if not speech:
- self._response = {}
- self._response['directives'] = []
-
- def play(self, stream_url, offset=0, opaque_token=None):
- """Sends a Play Directive to begin playback and replace current and enqueued streams."""
-
- self._response['shouldEndSession'] = True
- directive = self._play_directive('REPLACE_ALL')
- directive['audioItem'] = self._audio_item(stream_url=stream_url, offset=offset, opaque_token=opaque_token)
- self._response['directives'].append(directive)
- return self
-
- def enqueue(self, stream_url, offset=0, opaque_token=None):
- """Adds stream to the queue. Does not impact the currently playing stream."""
- directive = self._play_directive('ENQUEUE')
- audio_item = self._audio_item(stream_url=stream_url,
- offset=offset,
- push_buffer=False,
- opaque_token=opaque_token)
- audio_item['stream']['expectedPreviousToken'] = current_stream.token
-
- directive['audioItem'] = audio_item
- self._response['directives'].append(directive)
- return self
-
- def play_next(self, stream_url=None, offset=0, opaque_token=None):
- """Replace all streams in the queue but does not impact the currently playing stream."""
-
- directive = self._play_directive('REPLACE_ENQUEUED')
- directive['audioItem'] = self._audio_item(stream_url=stream_url, offset=offset, opaque_token=opaque_token)
- self._response['directives'].append(directive)
- return self
-
- def resume(self):
- """Sends Play Directive to resume playback at the paused offset"""
- directive = self._play_directive('REPLACE_ALL')
- directive['audioItem'] = self._audio_item()
- self._response['directives'].append(directive)
- return self
-
- def _play_directive(self, behavior):
- directive = {}
- directive['type'] = 'AudioPlayer.Play'
- directive['playBehavior'] = behavior
- return directive
-
- def _audio_item(self, stream_url=None, offset=0, push_buffer=True, opaque_token=None):
- """Builds an AudioPlayer Directive's audioItem and updates current_stream"""
- audio_item = {'stream': {}}
- stream = audio_item['stream']
-
- # existing stream
- if not stream_url:
- # stream.update(current_stream.__dict__)
- stream['url'] = current_stream.url
- stream['token'] = current_stream.token
- stream['offsetInMilliseconds'] = current_stream.offsetInMilliseconds
-
- # new stream
- else:
- stream['url'] = stream_url
- stream['token'] = opaque_token or str(uuid.uuid4())
- stream['offsetInMilliseconds'] = offset
-
- if push_buffer: # prevents enqueued streams from becoming current_stream
- push_stream(stream_cache, context['System']['user']['userId'], stream)
- return audio_item
-
- def stop(self):
- """Sends AudioPlayer.Stop Directive to stop the current stream playback"""
- self._response['directives'].append({'type': 'AudioPlayer.Stop'})
- return self
-
- def clear_queue(self, stop=False):
- """Clears queued streams and optionally stops current stream.
-
- Keyword Arguments:
- stop {bool} set True to stop current current stream and clear queued streams.
- set False to clear queued streams and allow current stream to finish
- default: {False}
- """
-
- directive = {}
- directive['type'] = 'AudioPlayer.ClearQueue'
- if stop:
- directive['clearBehavior'] = 'CLEAR_ALL'
- else:
- directive['clearBehavior'] = 'CLEAR_ENQUEUED'
-
- self._response['directives'].append(directive)
- return self
-
-
-def _copyattr(src, dest, attr, convert=None):
- if attr in src:
- value = src[attr]
- if convert is not None:
- value = convert(value)
- setattr(dest, attr, value)
-
-
-def _output_speech(speech):
- try:
- xmldoc = ElementTree.fromstring(speech)
- if xmldoc.tag == 'speak':
- return {'type': 'SSML', 'ssml': speech}
- except (UnicodeEncodeError, ElementTree.ParseError) as e:
- pass
- return {'type': 'PlainText', 'text': speech}
diff --git a/f-ask/flask_ask/verifier.py b/f-ask/flask_ask/verifier.py
deleted file mode 100644
index 29b256c..0000000
--- a/f-ask/flask_ask/verifier.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import base64
-import posixpath
-from datetime import datetime
-from six.moves.urllib.parse import urlparse
-from six.moves.urllib.request import urlopen
-
-from OpenSSL import crypto
-
-from . import logger
-
-
-class VerificationError(Exception): pass
-
-
-def load_certificate(cert_url):
- if not _valid_certificate_url(cert_url):
- raise VerificationError("Certificate URL verification failed")
- cert_data = urlopen(cert_url).read()
- cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_data)
- if not _valid_certificate(cert):
- raise VerificationError("Certificate verification failed")
- return cert
-
-
-def verify_signature(cert, signature, signed_data):
- try:
- signature = base64.b64decode(signature)
- crypto.verify(cert, signature, signed_data, 'sha1')
- except crypto.Error as e:
- raise VerificationError(e)
-
-
-def verify_timestamp(timestamp):
- dt = datetime.utcnow() - timestamp.replace(tzinfo=None)
- if abs(dt.total_seconds()) > 150:
- raise VerificationError("Timestamp verification failed")
-
-
-def verify_application_id(candidate, records):
- if candidate not in records:
- raise VerificationError("Application ID verification failed")
-
-
-def _valid_certificate_url(cert_url):
- parsed_url = urlparse(cert_url)
- if parsed_url.scheme == 'https':
- if parsed_url.hostname == "s3.amazonaws.com":
- if posixpath.normpath(parsed_url.path).startswith("/echo.api/"):
- return True
- return False
-
-
-def _valid_certificate(cert):
- not_after = cert.get_notAfter().decode('utf-8')
- not_after = datetime.strptime(not_after, '%Y%m%d%H%M%SZ')
- if datetime.utcnow() >= not_after:
- return False
- found = False
- for i in range(0, cert.get_extension_count()):
- extension = cert.get_extension(i)
- short_name = extension.get_short_name().decode('utf-8')
- value = str(extension)
- if 'subjectAltName' == short_name and 'DNS:echo-api.amazon.com' == value:
- found = True
- break
- if not found:
- return False
- return True
diff --git a/f-ask/ngrok.exe b/f-ask/ngrok.exe
deleted file mode 100644
index 0ec6adb..0000000
Binary files a/f-ask/ngrok.exe and /dev/null differ
diff --git a/f-ask/requirements-dev.txt b/f-ask/requirements-dev.txt
deleted file mode 100644
index f8d2793..0000000
--- a/f-ask/requirements-dev.txt
+++ /dev/null
@@ -1,5 +0,0 @@
--r requirements.txt
-mock==2.0.0
-requests==2.13.0
-tox==2.7.0
-
diff --git a/f-ask/requirements.txt b/f-ask/requirements.txt
deleted file mode 100644
index 50a7927..0000000
--- a/f-ask/requirements.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-aniso8601==1.2.0
-Flask==0.12.1
-cryptography==2.1.4
-pyOpenSSL==17.0.0
-PyYAML==3.12
-six==1.11.0
-
diff --git a/f-ask/samples/audio/playlist_demo/playlist.py b/f-ask/samples/audio/playlist_demo/playlist.py
deleted file mode 100644
index fd493e1..0000000
--- a/f-ask/samples/audio/playlist_demo/playlist.py
+++ /dev/null
@@ -1,256 +0,0 @@
-import collections
-import logging
-import os
-from copy import copy
-
-from flask import Flask, json
-from flask_ask import Ask, question, statement, audio, current_stream, logger
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.INFO)
-
-
-playlist = [
- # 'https://www.freesound.org/data/previews/367/367142_2188-lq.mp3',
- 'https://archive.org/download/mailboxbadgerdrumsamplesvolume2/Ringing.mp3',
- 'https://archive.org/download/petescott20160927/20160927%20RC300-53-127.0bpm.mp3',
- 'https://archive.org/download/plpl011/plpl011_05-johnny_ripper-rain.mp3',
- 'https://archive.org/download/piano_by_maxmsp/beats107.mp3',
- 'https://archive.org/download/petescott20160927/20160927%20RC300-58-115.1bpm.mp3',
- 'https://archive.org/download/PianoScale/PianoScale.mp3',
- # 'https://archive.org/download/FemaleVoiceSample/Female_VoiceTalent_demo.mp4',
- 'https://archive.org/download/mailboxbadgerdrumsamplesvolume2/Risset%20Drum%201.mp3',
- 'https://archive.org/download/mailboxbadgerdrumsamplesvolume2/Submarine.mp3',
- # 'https://ia800203.us.archive.org/27/items/CarelessWhisper_435/CarelessWhisper.ogg'
-]
-
-
-class QueueManager(object):
- """Manages queue data in a seperate context from current_stream.
-
- The flask-ask Local current_stream refers only to the current data from Alexa requests and Skill Responses.
- Alexa Skills Kit does not provide enqueued or stream-histroy data and does not provide a session attribute
- when delivering AudioPlayer Requests.
-
- This class is used to maintain accurate control of multiple streams,
- so that the user may send Intents to move throughout a queue.
- """
-
- def __init__(self, urls):
- self._urls = urls
- self._queued = collections.deque(urls)
- self._history = collections.deque()
- self._current = None
-
- @property
- def status(self):
- status = {
- 'Current Position': self.current_position,
- 'Current URL': self.current,
- 'Next URL': self.up_next,
- 'Previous': self.previous,
- 'History': list(self.history)
- }
- return status
-
- @property
- def up_next(self):
- """Returns the url at the front of the queue"""
- qcopy = copy(self._queued)
- try:
- return qcopy.popleft()
- except IndexError:
- return None
-
- @property
- def current(self):
- return self._current
-
- @current.setter
- def current(self, url):
- self._save_to_history()
- self._current = url
-
- @property
- def history(self):
- return self._history
-
- @property
- def previous(self):
- history = copy(self.history)
- try:
- return history.pop()
- except IndexError:
- return None
-
- def add(self, url):
- self._urls.append(url)
- self._queued.append(url)
-
- def extend(self, urls):
- self._urls.extend(urls)
- self._queued.extend(urls)
-
- def _save_to_history(self):
- if self._current:
- self._history.append(self._current)
-
- def end_current(self):
- self._save_to_history()
- self._current = None
-
- def step(self):
- self.end_current()
- self._current = self._queued.popleft()
- return self._current
-
- def step_back(self):
- self._queued.appendleft(self._current)
- self._current = self._history.pop()
- return self._current
-
- def reset(self):
- self._queued = collections.deque(self._urls)
- self._history = []
-
- def start(self):
- self.__init__(self._urls)
- return self.step()
-
- @property
- def current_position(self):
- return len(self._history) + 1
-
-
-queue = QueueManager(playlist)
-
-
-@ask.launch
-def launch():
- card_title = 'Playlist Example'
- text = 'Welcome to an example for playing a playlist. You can ask me to start the playlist.'
- prompt = 'You can ask start playlist.'
- return question(text).reprompt(prompt).simple_card(card_title, text)
-
-
-@ask.intent('PlaylistDemoIntent')
-def start_playlist():
- speech = 'Heres a playlist of some sounds. You can ask me Next, Previous, or Start Over'
- stream_url = queue.start()
- return audio(speech).play(stream_url)
-
-
-# QueueManager object is not stepped forward here.
-# This allows for Next Intents and on_playback_finished requests to trigger the step
-@ask.on_playback_nearly_finished()
-def nearly_finished():
- if queue.up_next:
- _infodump('Alexa is now ready for a Next or Previous Intent')
- # dump_stream_info()
- next_stream = queue.up_next
- _infodump('Enqueueing {}'.format(next_stream))
- return audio().enqueue(next_stream)
- else:
- _infodump('Nearly finished with last song in playlist')
-
-
-@ask.on_playback_finished()
-def play_back_finished():
- _infodump('Finished Audio stream for track {}'.format(queue.current_position))
- if queue.up_next:
- queue.step()
- _infodump('stepped queue forward')
- dump_stream_info()
- else:
- return statement('You have reached the end of the playlist!')
-
-
-# NextIntent steps queue forward and clears enqueued streams that were already sent to Alexa
-# next_stream will match queue.up_next and enqueue Alexa with the correct subsequent stream.
-@ask.intent('AMAZON.NextIntent')
-def next_song():
- if queue.up_next:
- speech = 'playing next queued song'
- next_stream = queue.step()
- _infodump('Stepped queue forward to {}'.format(next_stream))
- dump_stream_info()
- return audio(speech).play(next_stream)
- else:
- return audio('There are no more songs in the queue')
-
-
-@ask.intent('AMAZON.PreviousIntent')
-def previous_song():
- if queue.previous:
- speech = 'playing previously played song'
- prev_stream = queue.step_back()
- dump_stream_info()
- return audio(speech).play(prev_stream)
-
- else:
- return audio('There are no songs in your playlist history.')
-
-
-@ask.intent('AMAZON.StartOverIntent')
-def restart_track():
- if queue.current:
- speech = 'Restarting current track'
- dump_stream_info()
- return audio(speech).play(queue.current, offset=0)
- else:
- return statement('There is no current song')
-
-
-@ask.on_playback_started()
-def started(offset, token, url):
- _infodump('Started audio stream for track {}'.format(queue.current_position))
- dump_stream_info()
-
-
-@ask.on_playback_stopped()
-def stopped(offset, token):
- _infodump('Stopped audio stream for track {}'.format(queue.current_position))
-
-@ask.intent('AMAZON.PauseIntent')
-def pause():
- seconds = current_stream.offsetInMilliseconds / 1000
- msg = 'Paused the Playlist on track {}, offset at {} seconds'.format(
- queue.current_position, seconds)
- _infodump(msg)
- dump_stream_info()
- return audio(msg).stop().simple_card(msg)
-
-
-@ask.intent('AMAZON.ResumeIntent')
-def resume():
- seconds = current_stream.offsetInMilliseconds / 1000
- msg = 'Resuming the Playlist on track {}, offset at {} seconds'.format(queue.current_position, seconds)
- _infodump(msg)
- dump_stream_info()
- return audio(msg).resume().simple_card(msg)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-def dump_stream_info():
- status = {
- 'Current Stream Status': current_stream.__dict__,
- 'Queue status': queue.status
- }
- _infodump(status)
-
-
-def _infodump(obj, indent=2):
- msg = json.dumps(obj, indent=indent)
- logger.info(msg)
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
diff --git a/f-ask/samples/audio/playlist_demo/speech_assets/IntentSchema.json b/f-ask/samples/audio/playlist_demo/speech_assets/IntentSchema.json
deleted file mode 100644
index 0c699c4..0000000
--- a/f-ask/samples/audio/playlist_demo/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "intents": [
- {
- "intent": "AMAZON.PauseIntent"
- },
- {
- "intent": "PlaylistDemoIntent"
- },
- {
- "intent": "AMAZON.StopIntent"
- },
- {
- "intent": "AMAZON.ResumeIntent"
- }
- ]
-}
\ No newline at end of file
diff --git a/f-ask/samples/audio/playlist_demo/speech_assets/SampleUtterances.txt b/f-ask/samples/audio/playlist_demo/speech_assets/SampleUtterances.txt
deleted file mode 100644
index 167d52f..0000000
--- a/f-ask/samples/audio/playlist_demo/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1 +0,0 @@
-PlaylistDemoIntent start the playlist
\ No newline at end of file
diff --git a/f-ask/samples/audio/simple_demo/ask_audio.py b/f-ask/samples/audio/simple_demo/ask_audio.py
deleted file mode 100644
index edaec93..0000000
--- a/f-ask/samples/audio/simple_demo/ask_audio.py
+++ /dev/null
@@ -1,89 +0,0 @@
-import logging
-import os
-
-from flask import Flask, json, render_template
-from flask_ask import Ask, request, session, question, statement, context, audio, current_stream
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logger = logging.getLogger()
-logging.getLogger('flask_ask').setLevel(logging.INFO)
-
-
-@ask.launch
-def launch():
- card_title = 'Audio Example'
- text = 'Welcome to an audio example. You can ask to begin demo, or try asking me to play the sax.'
- prompt = 'You can ask to begin demo, or try asking me to play the sax.'
- return question(text).reprompt(prompt).simple_card(card_title, text)
-
-
-@ask.intent('DemoIntent')
-def demo():
- speech = "Here's one of my favorites"
- stream_url = 'https://www.vintagecomputermusic.com/mp3/s2t9_Computer_Speech_Demonstration.mp3'
- return audio(speech).play(stream_url, offset=93000)
-
-
-# 'ask audio_skil Play the sax
-@ask.intent('SaxIntent')
-def george_michael():
- speech = 'yeah you got it!'
- stream_url = 'https://ia800203.us.archive.org/27/items/CarelessWhisper_435/CarelessWhisper.ogg'
- return audio(speech).play(stream_url)
-
-
-@ask.intent('AMAZON.PauseIntent')
-def pause():
- return audio('Paused the stream.').stop()
-
-
-@ask.intent('AMAZON.ResumeIntent')
-def resume():
- return audio('Resuming.').resume()
-
-@ask.intent('AMAZON.StopIntent')
-def stop():
- return audio('stopping').clear_queue(stop=True)
-
-
-
-# optional callbacks
-@ask.on_playback_started()
-def started(offset, token):
- _infodump('STARTED Audio Stream at {} ms'.format(offset))
- _infodump('Stream holds the token {}'.format(token))
- _infodump('STARTED Audio stream from {}'.format(current_stream.url))
-
-
-@ask.on_playback_stopped()
-def stopped(offset, token):
- _infodump('STOPPED Audio Stream at {} ms'.format(offset))
- _infodump('Stream holds the token {}'.format(token))
- _infodump('Stream stopped playing from {}'.format(current_stream.url))
-
-
-@ask.on_playback_nearly_finished()
-def nearly_finished():
- _infodump('Stream nearly finished from {}'.format(current_stream.url))
-
-@ask.on_playback_finished()
-def stream_finished(token):
- _infodump('Playback has finished for stream with token {}'.format(token))
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-def _infodump(obj, indent=2):
- msg = json.dumps(obj, indent=indent)
- logger.info(msg)
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
-
diff --git a/f-ask/samples/audio/simple_demo/speech_assets/IntentSchema.json b/f-ask/samples/audio/simple_demo/speech_assets/IntentSchema.json
deleted file mode 100644
index 553597f..0000000
--- a/f-ask/samples/audio/simple_demo/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "intents": [
- {
- "intent": "AMAZON.PauseIntent"
- },
- {
- "intent": "DemoIntent"
- },
- {
- "intent": "SaxIntent"
- },
- {
- "intent": "AMAZON.StopIntent"
- },
- {
- "intent": "AMAZON.ResumeIntent"
- }
- ]
-}
\ No newline at end of file
diff --git a/f-ask/samples/audio/simple_demo/speech_assets/SampleUtterances.txt b/f-ask/samples/audio/simple_demo/speech_assets/SampleUtterances.txt
deleted file mode 100644
index 98fdc7d..0000000
--- a/f-ask/samples/audio/simple_demo/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-DemoIntent begin demo
-SaxIntent play the sax
-SaxIntent play sax
\ No newline at end of file
diff --git a/f-ask/samples/blueprint_demo/demo.py b/f-ask/samples/blueprint_demo/demo.py
deleted file mode 100644
index 746c14c..0000000
--- a/f-ask/samples/blueprint_demo/demo.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import logging
-import os
-
-from flask import Flask
-from helloworld import blueprint
-
-app = Flask(__name__)
-app.register_blueprint(blueprint)
-
-logging.getLogger('flask_app').setLevel(logging.DEBUG)
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
diff --git a/f-ask/samples/blueprint_demo/helloworld.py b/f-ask/samples/blueprint_demo/helloworld.py
deleted file mode 100644
index 55e6405..0000000
--- a/f-ask/samples/blueprint_demo/helloworld.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import logging
-
-from flask import Blueprint, render_template
-from flask_ask import Ask, question, statement
-
-
-blueprint = Blueprint('blueprint_api', __name__, url_prefix="/ask")
-ask = Ask(blueprint=blueprint)
-
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-@ask.launch
-def launch():
- speech_text = render_template('welcome')
- return question(speech_text).reprompt(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.intent('HelloWorldIntent')
-def hello_world():
- speech_text = render_template('hello')
- return statement(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.intent('AMAZON.HelpIntent')
-def help():
- speech_text = render_template('help')
- return question(speech_text).reprompt(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
diff --git a/f-ask/samples/blueprint_demo/speech_assets/IntentSchema.json b/f-ask/samples/blueprint_demo/speech_assets/IntentSchema.json
deleted file mode 100644
index 37c2405..0000000
--- a/f-ask/samples/blueprint_demo/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "intents": [
- {
- "intent": "HelloWorldIntent"
- },
- {
- "intent": "AMAZON.HelpIntent"
- }
- ]
-}
diff --git a/f-ask/samples/blueprint_demo/speech_assets/SampleUtterances.txt b/f-ask/samples/blueprint_demo/speech_assets/SampleUtterances.txt
deleted file mode 100644
index d9f178e..0000000
--- a/f-ask/samples/blueprint_demo/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-HelloWorldIntent say hello
-HelloWorldIntent say hello world
-HelloWorldIntent hello
-HelloWorldIntent say hi
-HelloWorldIntent say hi world
-HelloWorldIntent hi
-HelloWorldIntent how are you
diff --git a/f-ask/samples/blueprint_demo/templates.yaml b/f-ask/samples/blueprint_demo/templates.yaml
deleted file mode 100644
index 81996fb..0000000
--- a/f-ask/samples/blueprint_demo/templates.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-welcome: "Welcome to the Alexa Skills Kit, you can say hello"
-hello: "Hello world!"
-help: "You can say hello to me!"
\ No newline at end of file
diff --git a/f-ask/samples/helloworld/helloworld.py b/f-ask/samples/helloworld/helloworld.py
deleted file mode 100644
index 0daba43..0000000
--- a/f-ask/samples/helloworld/helloworld.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import logging
-import os
-
-from flask import Flask
-from flask_ask import Ask, request, session, question, statement
-
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-@ask.launch
-def launch():
- speech_text = 'Welcome to the Alexa Skills Kit, you can say hello'
- return question(speech_text).reprompt(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.intent('HelloWorldIntent')
-def hello_world():
- speech_text = 'Hello world'
- return statement(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.intent('AMAZON.HelpIntent')
-def help():
- speech_text = 'You can say hello to me!'
- return question(speech_text).reprompt(speech_text).simple_card('HelloWorld', speech_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
diff --git a/f-ask/samples/helloworld/speech_assets/IntentSchema.json b/f-ask/samples/helloworld/speech_assets/IntentSchema.json
deleted file mode 100644
index 37c2405..0000000
--- a/f-ask/samples/helloworld/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "intents": [
- {
- "intent": "HelloWorldIntent"
- },
- {
- "intent": "AMAZON.HelpIntent"
- }
- ]
-}
diff --git a/f-ask/samples/helloworld/speech_assets/SampleUtterances.txt b/f-ask/samples/helloworld/speech_assets/SampleUtterances.txt
deleted file mode 100644
index d9f178e..0000000
--- a/f-ask/samples/helloworld/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-HelloWorldIntent say hello
-HelloWorldIntent say hello world
-HelloWorldIntent hello
-HelloWorldIntent say hi
-HelloWorldIntent say hi world
-HelloWorldIntent hi
-HelloWorldIntent how are you
diff --git a/f-ask/samples/historybuff/historybuff.py b/f-ask/samples/historybuff/historybuff.py
deleted file mode 100644
index fbe94f2..0000000
--- a/f-ask/samples/historybuff/historybuff.py
+++ /dev/null
@@ -1,145 +0,0 @@
-import logging
-import os
-import re
-from six.moves.urllib.request import urlopen
-
-
-from flask import Flask
-from flask_ask import Ask, request, session, question, statement
-
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-# URL prefix to download history content from Wikipedia.
-URL_PREFIX = 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts' + \
- '&format=json&explaintext=&exsectionformat=plain&redirects=&titles='
-
-# Constant defining number of events to be read at one time.
-PAGINATION_SIZE = 3
-
-# Length of the delimiter between individual events.
-DELIMITER_SIZE = 2
-
-# Size of events from Wikipedia response.
-SIZE_OF_EVENTS = 10
-
-# Constant defining session attribute key for the event index
-SESSION_INDEX = 'index'
-
-# Constant defining session attribute key for the event text key for date of events.
-SESSION_TEXT = 'text'
-
-
-@ask.launch
-def launch():
- speech_output = 'History buff. What day do you want events for?'
- reprompt_text = "With History Buff, you can get historical events for any day of the year. " + \
- "For example, you could say today, or August thirtieth. " + \
- "Now, which day do you want?"
- return question(speech_output).reprompt(reprompt_text)
-
-
-@ask.intent('GetFirstEventIntent', convert={ 'day': 'date' })
-def get_first_event(day):
- month_name = day.strftime('%B')
- day_number = day.day
- events = _get_json_events_from_wikipedia(month_name, day_number)
- if not events:
- speech_output = "There is a problem connecting to Wikipedia at this time. Please try again later."
- return statement('{}'.format(speech_output))
- else:
- card_title = "Events on {} {}".format(month_name, day_number)
- speech_output = "
For {} {}
".format(month_name, day_number)
- card_output = ""
- for i in range(PAGINATION_SIZE):
- speech_output += "
{}
".format(events[i])
- card_output += "{}\n".format(events[i])
- speech_output += " Wanna go deeper into history?"
- card_output += " Wanna go deeper into history?"
- reprompt_text = "With History Buff, you can get historical events for any day of the year. " + \
- "For example, you could say today, or August thirtieth. " + \
- "Now, which day do you want?"
- session.attributes[SESSION_INDEX] = PAGINATION_SIZE
- session.attributes[SESSION_TEXT] = events
- speech_output = '{}'.format(speech_output)
- return question(speech_output).reprompt(reprompt_text).simple_card(card_title, card_output)
-
-
-@ask.intent('GetNextEventIntent')
-def get_next_event():
- events = session.attributes[SESSION_TEXT]
- index = session.attributes[SESSION_INDEX]
- card_title = "More events on this day in history"
- speech_output = ""
- card_output = ""
- i = 0
- while i < PAGINATION_SIZE and index < len(events):
- speech_output += "
{}
".format(events[index])
- card_output += "{}\n".format(events[index])
- i += 1
- index += 1
- speech_output += " Wanna go deeper into history?"
- reprompt_text = "Do you want to know more about what happened on this date?"
- session.attributes[SESSION_INDEX] = index
- speech_output = '{}'.format(speech_output)
- return question(speech_output).reprompt(reprompt_text).simple_card(card_title, card_output)
-
-
-@ask.intent('AMAZON.StopIntent')
-def stop():
- return statement("Goodbye")
-
-
-@ask.intent('AMAZON.CancelIntent')
-def cancel():
- return statement("Goodbye")
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-def _get_json_events_from_wikipedia(month, date):
- url = "{}{}_{}".format(URL_PREFIX, month, date)
- data = urlopen(url).read().decode('utf-8')
- return _parse_json(data)
-
-
-def _parse_json(text):
- events = []
- try:
- slice_start = text.index("\\nEvents\\n") + SIZE_OF_EVENTS
- slice_end = text.index("\\n\\n\\nBirths")
- text = text[slice_start:slice_end];
- except ValueError:
- return events
- start_index = end_index = 0
- done = False
- while not done:
- try:
- end_index = text.index('\\n', start_index + DELIMITER_SIZE)
- event_text = text[start_index:end_index]
- start_index = end_index + 2
- except ValueError:
- event_text = text[start_index:]
- done = True
- # replace dashes returned in text from Wikipedia's API
- event_text = event_text.replace('\\u2013', '')
- # add comma after year so Alexa pauses before continuing with the sentence
- event_text = re.sub('^\d+', r'\g<0>,', event_text)
- events.append(event_text)
- events.reverse()
- return events
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
-
diff --git a/f-ask/samples/historybuff/speech_assets/IntentSchema.json b/f-ask/samples/historybuff/speech_assets/IntentSchema.json
deleted file mode 100644
index 8979d54..0000000
--- a/f-ask/samples/historybuff/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "intents": [
- {
- "intent": "GetFirstEventIntent",
- "slots": [
- {
- "name": "day",
- "type": "AMAZON.DATE"
- }
- ]
- },
- {
- "intent": "GetNextEventIntent"
- },
- {
- "intent": "AMAZON.HelpIntent"
- },
- {
- "intent": "AMAZON.StopIntent"
- },
- {
- "intent": "AMAZON.CancelIntent"
- }
- ]
-}
diff --git a/f-ask/samples/historybuff/speech_assets/SampleUtterances.txt b/f-ask/samples/historybuff/speech_assets/SampleUtterances.txt
deleted file mode 100644
index 56ef054..0000000
--- a/f-ask/samples/historybuff/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-GetFirstEventIntent get events for {day}
-GetFirstEventIntent give me events for {day}
-GetFirstEventIntent what happened on {day}
-GetFirstEventIntent what happened
-GetFirstEventIntent {day}
-
-GetNextEventIntent yes
-GetNextEventIntent yup
-GetNextEventIntent sure
-GetNextEventIntent yes please
-
-AMAZON.StopIntent no
-AMAZON.StopIntent nope
-AMAZON.StopIntent no thanks
-AMAZON.StopIntent no thank you
diff --git a/f-ask/samples/purchase/IntentSchema.json b/f-ask/samples/purchase/IntentSchema.json
deleted file mode 100644
index 80b813f..0000000
--- a/f-ask/samples/purchase/IntentSchema.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- "interactionModel": {
- "languageModel": {
- "invocationName": "demo",
- "intents": [
- {
- "name": "AMAZON.FallbackIntent",
- "samples": []
- },
- {
- "name": "AMAZON.CancelIntent",
- "samples": []
- },
- {
- "name": "AMAZON.HelpIntent",
- "samples": []
- },
- {
- "name": "AMAZON.StopIntent",
- "samples": []
- },
- {
- "name": "BuySkillItemIntent",
- "slots": [
- {
- "name": "ProductName",
- "type": "LIST_OF_PRODUCT_NAMES"
- }
- ],
- "samples": [
- "{ProductName}",
- "buy",
- "shop",
- "buy {ProductName}",
- "purchase {ProductName}",
- "want {ProductName}",
- "would like {ProductName}"
- ]
- },
- {
- "name": "RefundSkillItemIntent",
- "slots": [
- {
- "name": "ProductName",
- "type": "LIST_OF_PRODUCT_NAMES"
- }
- ],
- "samples": [
- "cancel {ProductName}",
- "return {ProductName}",
- "refund {ProductName}",
- "want a refund for {ProductName}",
- "would like to return {ProductName}"
- ]
- }
- ],
- "types": [
- {
- "name": "LIST_OF_PRODUCT_NAMES",
- "values": [
- {
- "name": {
- "value": "monthly subscription"
- }
- },
- {
- "name": {
- "value": "start smoking"
- }
- },
- {
- "name": {
- "value": "stop smoking"
- }
- }
- ]
- }
- ]
- }
- }
-}
diff --git a/f-ask/samples/purchase/model.py b/f-ask/samples/purchase/model.py
deleted file mode 100644
index 957033c..0000000
--- a/f-ask/samples/purchase/model.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import requests
-from flask import json
-from flask_ask import logger
-
-class Product():
- '''
- Object model for inSkillProducts and methods to access products.
-
- {"inSkillProducts":[
- {"productId":"amzn1.adg.product.your_product_id",
- "referenceName":"product_name",
- "type":"ENTITLEMENT",
- "name":"product name",
- "summary":"This product has helped many people.",
- "entitled":"NOT_ENTITLED",
- "purchasable":"NOT_PURCHASABLE"}],
- "nextToken":null,
- "truncated":false}
-
- '''
-
- def __init__(self, apiAccessToken):
- self.token = apiAccessToken
- self.product_list = self.query()
-
-
- def query(self):
- # Information required to invoke the API is available in the session
- apiEndpoint = "https://api.amazonalexa.com"
- apiPath = "/v1/users/~current/skills/~current/inSkillProducts"
- token = "bearer " + self.token
- language = "en-US" #self.event.request.locale
-
- url = apiEndpoint + apiPath
- headers = {
- "Content-Type" : 'application/json',
- "Accept-Language" : language,
- "Authorization" : token
- }
- #Call the API
- res = requests.get(url, headers=headers)
- logger.info('PRODUCTS:' + '*' * 80)
- logger.info(res.status_code)
- logger.info(res.text)
- if res.status_code == 200:
- data = json.loads(res.text)
- return data['inSkillProducts']
- else:
- return None
-
- def list(self):
- """ return list of purchasable and not entitled products"""
- mylist = []
- for prod in self.product_list:
- if self.purchasable(prod) and not self.entitled(prod):
- mylist.append(prod)
- return mylist
-
- def purchasable(self, product):
- """ return True if purchasable product"""
- return 'PURCHASABLE' == product['purchasable']
-
- def entitled(self, product):
- """ return True if entitled product"""
- return 'ENTITLED' == product['entitled']
-
-
- def productId(self, name):
- print(self.product_list)
- for prod in self.product_list:
- if name == prod['name'].lower():
- return prod['productId']
- return None
-
- def productName(self, id):
- for prod in self.product_list:
- if id == prod['productId']:
- return prod['name']
- return None
diff --git a/f-ask/samples/purchase/purchase.py b/f-ask/samples/purchase/purchase.py
deleted file mode 100644
index ba03a27..0000000
--- a/f-ask/samples/purchase/purchase.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import logging
-import os
-import requests
-
-from flask import Flask, json, render_template
-from flask_ask import Ask, request, session, question, statement, context, buy, upsell, refund, logger
-from model import Product
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-PRODUCT_KEY = "PRODUCT"
-
-
-
-@ask.on_purchase_completed( mapping={'payload': 'payload','name':'name','status':'status','token':'token'})
-def completed(payload, name, status, token):
- products = Product(context.System.apiAccessToken)
- logger.info('on-purchase-completed {}'.format( request))
- logger.info('payload: {} {}'.format(payload.purchaseResult, payload.productId))
- logger.info('name: {}'.format(name))
- logger.info('token: {}'.format(token))
- logger.info('status: {}'.format( status.code == 200))
- product_name = products.productName(payload.productId)
- logger.info('Product name'.format(product_name))
- if status.code == '200' and ('ACCEPTED' in payload.purchaseResult):
- return question('To listen it just say - play {} '.format(product_name))
- else:
- return question('Do you want to buy another product?')
-
-@ask.launch
-def launch():
- products = Product(context.System.apiAccessToken)
- question_text = render_template('welcome', products=products.list())
- reprompt_text = render_template('welcome_reprompt')
- return question(question_text).reprompt(reprompt_text).simple_card('Welcome', question_text)
-
-
-@ask.intent('BuySkillItemIntent', mapping={'product_name': 'ProductName'})
-def buy_intent(product_name):
- products = Product(context.System.apiAccessToken)
- logger.info("PRODUCT: {}".format(product_name))
- buy_card = render_template('buy_card', product=product_name)
- productId = products.productId(product_name)
- if productId is not None:
- session.attributes[PRODUCT_KEY] = productId
- else:
- return statement("I didn't find a product {}".format(product_name))
- raise NotImplementedError()
- return buy(productId).simple_card('Welcome', question_text)
-
- #return upsell(product,'get this great product')
-
-
-@ask.intent('RefundSkillItemIntent', mapping={'product_name': 'ProductName'})
-def refund_intent(product_name):
- refund_card = render_template('refund_card')
- logger.info("PRODUCT: {}".format(product_name))
-
- products = Product(context.System.apiAccessToken)
- productId = products.productId(product_name)
-
- if productId is not None:
- session.attributes[PRODUCT_KEY] = productId
- else:
- raise NotImplementedError()
- return refund(productId)
-
-
-@ask.intent('AMAZON.FallbackIntent')
-def fallback_intent():
- return statement("FallbackIntent")
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
-
diff --git a/f-ask/samples/purchase/templates.yaml b/f-ask/samples/purchase/templates.yaml
deleted file mode 100644
index 7b3d637..0000000
--- a/f-ask/samples/purchase/templates.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-welcome: |
- Welcome to the Flask-ask purchase demo.
- {% if products %}
- Here is a list of products available:
- {%for product in products%}
- {{ product.name}},
- {%endfor %}
- Please tell me the product name you want to buy.
- {%else%}
- You have no products configured. Please configure products using ASK CLI.
- {%endif%}
-
-
-welcome_reprompt: Please tell me the product name you want to buy.
-
-refund_card: |
- Refund Intent for {{product}}
-
-
-buy_card: |
- Buy Intent for {{product}}
diff --git a/f-ask/samples/reddit/main.py b/f-ask/samples/reddit/main.py
deleted file mode 100644
index 1cb6e8f..0000000
--- a/f-ask/samples/reddit/main.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import logging
-import os
-from flask import Flask
-from flask_ask import Ask, request, session, question, statement
-import random
-import yaml
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-@ask.launch
-def launch():
- return question("Each excercise will take about 5 minutes. Are you Ready?")
-
-
-def get_workoutplan():
- with open("C:/Users/John/Desktop/GST/samples/reddit/workouts.yaml", 'r') as stream:
- exercises = yaml.load(stream)
- exercise_names = random.sample(list(exercises), 5)
- workout_plan = []
- for exercise_name in exercise_names:
- exercise = str(exercises[exercise_name]["duration"][0]) + " " + str(exercises[exercise_name]["duration"][1] )+ " of " + exercise_name
- workout_plan.append(exercise)
- return workout_plan
-
-
-@ask.intent('YesIntent', mapping={'part': 'Part'})
-def start_workout(part):
- print(part)
- workout_plan = get_workoutplan()
- response = "Do "
-
- for excercise in workout_plan[:-1]:
- response += excercise + ", "
- response += " and " + workout_plan[-1]
- print(response)
- return statement(response)
-
-@ask.intent('NoIntent')
-def no():
- return statement("Allright, maybe later.")
-
-@ask.intent('AMAZON.HelpIntent')
-def help():
- speech_text = 'If you want a quick workout just say "workout"'
- return statement(speech_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run()
diff --git a/f-ask/samples/reddit/speech_assets/IntentSchema.json b/f-ask/samples/reddit/speech_assets/IntentSchema.json
deleted file mode 100644
index bd9db8d..0000000
--- a/f-ask/samples/reddit/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,50 +0,0 @@
-
-{
- "interactionModel": {
- "languageModel": {
- "invocationName": "give me a workout",
- "intents": [
- {
- "name": "AMAZON.CancelIntent",
- "samples": []
- },
- {
- "name": "AMAZON.HelpIntent",
- "samples": []
- },
- {
- "name": "AMAZON.StopIntent",
- "samples": []
- },
- {
- "name": "YesIntent",
- "slots": [],
- "samples": [
- "yes"
- ]
- },
- {
- "name": "NoIntent",
- "slots": [],
- "samples": [
- "no"
- ]
- },
- {
- "name": "HelloWorldIntent",
- "slots": [],
- "samples": [
- "let's workout",
- "give me todays workout",
- "what's todays workout"
- ]
- },
- {
- "name": "AMAZON.NavigateHomeIntent",
- "samples": []
- }
- ],
- "types": []
- }
- }
-}
\ No newline at end of file
diff --git a/f-ask/samples/reddit/speech_assets/SampleUtterances.txt b/f-ask/samples/reddit/speech_assets/SampleUtterances.txt
deleted file mode 100644
index d9f178e..0000000
--- a/f-ask/samples/reddit/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-HelloWorldIntent say hello
-HelloWorldIntent say hello world
-HelloWorldIntent hello
-HelloWorldIntent say hi
-HelloWorldIntent say hi world
-HelloWorldIntent hi
-HelloWorldIntent how are you
diff --git a/f-ask/samples/reddit/speech_assets/customSlotTypes/NAME_OF_BODY_PARTS b/f-ask/samples/reddit/speech_assets/customSlotTypes/NAME_OF_BODY_PARTS
deleted file mode 100644
index 451682c..0000000
--- a/f-ask/samples/reddit/speech_assets/customSlotTypes/NAME_OF_BODY_PARTS
+++ /dev/null
@@ -1,4 +0,0 @@
-Chest
-Legs
-Torso
-Upper body
diff --git a/f-ask/samples/reddit/test.py b/f-ask/samples/reddit/test.py
deleted file mode 100644
index 6acd029..0000000
--- a/f-ask/samples/reddit/test.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import logging
-import os
-from flask import Flask
-from flask_ask import Ask, request, session, question, statement
-import random
-import yaml
-
-def read_exercises():
- with open("workouts.yaml", 'r') as stream:
- exercises = yaml.load(stream)
- exercise_names = random.sample(list(exercises), 5)
- workout_plan = []
- for exercise_name in exercise_names:
- exercise = str(exercises[exercise_name]["duration"][0]) + " " + str(exercises[exercise_name]["duration"][1] )+ " of " + exercise_name
- workout_plan.append(exercise)
- print(exercise)
-
-
-
-
-read_exercises()
\ No newline at end of file
diff --git a/f-ask/samples/reddit/workouts.yaml b/f-ask/samples/reddit/workouts.yaml
deleted file mode 100644
index 7a11123..0000000
--- a/f-ask/samples/reddit/workouts.yaml
+++ /dev/null
@@ -1,55 +0,0 @@
----
-
-Run in place:
- duration:
- - 30
- - seconds
- explaination: "just run in place... dummy"
-
-Jumping jacks:
- duration:
- - 30
- - repetitions
- explaination: "act like your Patric Star jumping of joy"
-
-Burpees:
- duration:
- - 10
- - repetitions
- explaination: "do a push-up, then stand up and do a little jump"
-
-Push-ups:
- duration:
- - 10
- - repetitions
- explaination: "lie on the ground, with your arms besides your toso. Angle them at 45° and push."
-
-Mountain climbers:
- duration:
- - 20
- - repetitions
- explaination: "get in the upwards push-up position and move alternating knees to your chest"
-
-Jump lunges:
- duration:
- - 15
- - repetitions
- explaination: "Walk in a way that your knees almost touch the ground"
-
-Jump squats:
- duration:
- - 15
- - repetitions
- explaination: "jump and pull ypur knees to your chest"
-
-Bicycle crunches:
- duration:
- - 20
- - repetitions
- explaination: "lay on your back with your hands behind your head, move your ellenbow to your opposite knee"
-
-Squats:
- duration:
- - 15
- - repetitions
- explaination: "you know what a squat is, mind the 90°"
\ No newline at end of file
diff --git a/f-ask/samples/session/session.py b/f-ask/samples/session/session.py
deleted file mode 100644
index 0cd3927..0000000
--- a/f-ask/samples/session/session.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import logging
-import os
-
-from flask import Flask, json, render_template
-from flask_ask import Ask, request, session, question, statement
-
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-COLOR_KEY = "COLOR"
-
-
-@ask.launch
-def launch():
- card_title = render_template('card_title')
- question_text = render_template('welcome')
- reprompt_text = render_template('welcome_reprompt')
- return question(question_text).reprompt(reprompt_text).simple_card(card_title, question_text)
-
-
-@ask.intent('MyColorIsIntent', mapping={'color': 'Color'})
-def my_color_is(color):
- card_title = render_template('card_title')
- if color is not None:
- session.attributes[COLOR_KEY] = color
- question_text = render_template('known_color', color=color)
- reprompt_text = render_template('known_color_reprompt')
- else:
- question_text = render_template('unknown_color')
- reprompt_text = render_template('unknown_color_reprompt')
- return question(question_text).reprompt(reprompt_text).simple_card(card_title, question_text)
-
-
-@ask.intent('WhatsMyColorIntent')
-def whats_my_color():
- card_title = render_template('card_title')
- color = session.attributes.get(COLOR_KEY)
- if color is not None:
- statement_text = render_template('known_color_bye', color=color)
- return statement(statement_text).simple_card(card_title, statement_text)
- else:
- question_text = render_template('unknown_color_reprompt')
- return question(question_text).reprompt(question_text).simple_card(card_title, question_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
-
diff --git a/f-ask/samples/session/speech_assets/IntentSchema.json b/f-ask/samples/session/speech_assets/IntentSchema.json
deleted file mode 100644
index a8e06f2..0000000
--- a/f-ask/samples/session/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "intents": [
- {
- "intent": "MyColorIsIntent",
- "slots": [
- {
- "name": "Color",
- "type": "LIST_OF_COLORS"
- }
- ]
- },
- {
- "intent": "WhatsMyColorIntent"
- }
- ]
-}
diff --git a/f-ask/samples/session/speech_assets/SampleUtterances.txt b/f-ask/samples/session/speech_assets/SampleUtterances.txt
deleted file mode 100644
index 6d4ae18..0000000
--- a/f-ask/samples/session/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-MyColorIsIntent my color is {Color}
-MyColorIsIntent my favorite color is {Color}
-WhatsMyColorIntent whats my color
-WhatsMyColorIntent what is my color
-WhatsMyColorIntent say my color
-WhatsMyColorIntent tell me my color
-WhatsMyColorIntent whats my favorite color
-WhatsMyColorIntent what is my favorite color
-WhatsMyColorIntent say my favorite color
-WhatsMyColorIntent tell me my favorite color
-WhatsMyColorIntent tell me what my favorite color is
diff --git a/f-ask/samples/session/speech_assets/customSlotTypes/LIST_OF_COLORS b/f-ask/samples/session/speech_assets/customSlotTypes/LIST_OF_COLORS
deleted file mode 100644
index f9e6bcf..0000000
--- a/f-ask/samples/session/speech_assets/customSlotTypes/LIST_OF_COLORS
+++ /dev/null
@@ -1,6 +0,0 @@
-green
-blue
-purple
-red
-orange
-yellow
diff --git a/f-ask/samples/session/templates.yaml b/f-ask/samples/session/templates.yaml
deleted file mode 100644
index 454528f..0000000
--- a/f-ask/samples/session/templates.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-welcome: |
- Welcome to the Alexa Skills Kit sample. Please tell me your favorite color by
- saying, my favorite color is red
-
-welcome_reprompt: Please tell me your favorite color by saying, my favorite color is red
-
-known_color: |
- I now know that your favorite color is {{ color }}. You can ask me your favorite color
- by saying, what's my favorite color?
-
-known_color_reprompt: You can ask me your favorite color by saying, what's my favorite color?
-
-known_color_bye: Your favorite color is {{ color }}. Goodbye
-
-unknown_color: I'm not sure what your favorite color is, please try again
-
-unknown_color_reprompt: |
- I'm not sure what your favorite color is. You can tell me your favorite color by saying,
- my favorite color is red
-
-card_title: Session
diff --git a/f-ask/samples/spacegeek/spacegeek.py b/f-ask/samples/spacegeek/spacegeek.py
deleted file mode 100644
index 4f2153f..0000000
--- a/f-ask/samples/spacegeek/spacegeek.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import logging
-import os
-from random import randint
-
-from flask import Flask, render_template
-from flask_ask import Ask, request, session, question, statement
-
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-@ask.launch
-def launch():
- return get_new_fact()
-
-
-@ask.intent('GetNewFactIntent')
-def get_new_fact():
- num_facts = 13 # increment this when adding a new fact template
- fact_index = randint(0, num_facts-1)
- fact_text = render_template('space_fact_{}'.format(fact_index))
- card_title = render_template('card_title')
- return statement(fact_text).simple_card(card_title, fact_text)
-
-
-@ask.intent('AMAZON.HelpIntent')
-def help():
- help_text = render_template('help')
- return question(help_text).reprompt(help_text)
-
-
-@ask.intent('AMAZON.StopIntent')
-def stop():
- bye_text = render_template('bye')
- return statement(bye_text)
-
-
-@ask.intent('AMAZON.CancelIntent')
-def cancel():
- bye_text = render_template('bye')
- return statement(bye_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
diff --git a/f-ask/samples/spacegeek/speech_assets/IntentSchema.json b/f-ask/samples/spacegeek/speech_assets/IntentSchema.json
deleted file mode 100644
index dfdf9ba..0000000
--- a/f-ask/samples/spacegeek/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "intents": [
- {
- "intent": "GetNewFactIntent"
- },
- {
- "intent": "AMAZON.HelpIntent"
- },
- {
- "intent": "AMAZON.StopIntent"
- },
- {
- "intent": "AMAZON.CancelIntent"
- }
- ]
-}
diff --git a/f-ask/samples/spacegeek/speech_assets/SampleUtterances.txt b/f-ask/samples/spacegeek/speech_assets/SampleUtterances.txt
deleted file mode 100644
index 9d3e6c6..0000000
--- a/f-ask/samples/spacegeek/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-GetNewFactIntent a fact
-GetNewFactIntent a space fact
-GetNewFactIntent tell me a fact
-GetNewFactIntent tell me a space fact
-GetNewFactIntent give me a fact
-GetNewFactIntent give me a space fact
-GetNewFactIntent tell me trivia
-GetNewFactIntent tell me a space trivia
-GetNewFactIntent give me trivia
-GetNewFactIntent give me a space trivia
-GetNewFactIntent give me some information
-GetNewFactIntent give me some space information
-GetNewFactIntent tell me something
-GetNewFactIntent give me something
diff --git a/f-ask/samples/spacegeek/templates.yaml b/f-ask/samples/spacegeek/templates.yaml
deleted file mode 100644
index 930ea0b..0000000
--- a/f-ask/samples/spacegeek/templates.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-space_fact_0: A year on Mercury is just 88 days long.
-space_fact_1: Despite being farther from the Sun, Venus experiences higher temperatures than Mercury.
-space_fact_2: Venus rotates counter-clockwise, possibly because of a collision in the past with an asteroid.
-space_fact_3: On Mars, the Sun appears about half the size as it does on Earth.
-space_fact_4: Earth is the only planet not named after a god.
-space_fact_5: Jupiter has the shortest day of all the planets.
-space_fact_6: The Milky Way galaxy will collide with the Andromeda Galaxy in about 5 billion years.
-space_fact_7: The Sun contains 99.86% of the mass in the Solar System.
-space_fact_8: The Sun is an almost perfect sphere.
-space_fact_9: A total solar eclipse can happen once every 1 to 2 years. This makes them a rare event.
-space_fact_10: Saturn radiates two and a half times more energy into space than it receives from the sun.
-space_fact_11: The temperature inside the Sun can reach 15 million degrees Celsius.
-space_fact_12: The Moon is moving approximately 3.8 cm away from our planet every year.
-card_title: SpaceGeek
-help: You can ask Space Geek tell me a space fact, or, you can say exit. What can I help you with?
-bye: Goodbye
diff --git a/f-ask/samples/tidepooler/speech_assets/IntentSchema.json b/f-ask/samples/tidepooler/speech_assets/IntentSchema.json
deleted file mode 100644
index a428bdd..0000000
--- a/f-ask/samples/tidepooler/speech_assets/IntentSchema.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "intents": [
- {
- "intent": "OneshotTideIntent",
- "slots": [
- {
- "name": "City",
- "type": "LIST_OF_CITIES"
- },
- {
- "name": "State",
- "type": "LIST_OF_STATES"
- },
- {
- "name": "Date",
- "type": "AMAZON.DATE"
- }
- ]
- },
- {
- "intent": "DialogTideIntent",
- "slots": [
- {
- "name": "City",
- "type": "LIST_OF_CITIES"
- },
- {
- "name": "State",
- "type": "LIST_OF_STATES"
- },
- {
- "name": "Date",
- "type": "AMAZON.DATE"
- }
- ]
- },
- {
- "intent": "SupportedCitiesIntent"
- },
- {
- "intent": "AMAZON.HelpIntent"
- },
- {
- "intent": "AMAZON.StopIntent"
- },
- {
- "intent": "AMAZON.CancelIntent"
- }
- ]
-}
diff --git a/f-ask/samples/tidepooler/speech_assets/SampleUtterances.txt b/f-ask/samples/tidepooler/speech_assets/SampleUtterances.txt
deleted file mode 100644
index afee81d..0000000
--- a/f-ask/samples/tidepooler/speech_assets/SampleUtterances.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-DialogTideIntent {City}
-DialogTideIntent {City} {State}
-DialogTideIntent {Date}
-
-OneshotTideIntent get high tide
-OneshotTideIntent get high tide for {City} {State}
-OneshotTideIntent get high tide for {City} {State} {Date}
-OneshotTideIntent get high tide for {City} {Date}
-OneshotTideIntent get high tide for {Date}
-OneshotTideIntent get high tide {Date}
-OneshotTideIntent get the high tide for {City} {Date}
-OneshotTideIntent get the next tide for {City} for {Date}
-OneshotTideIntent get the tides for {Date}
-OneshotTideIntent get tide information for {City}
-OneshotTideIntent get tide information for {City} {State}
-OneshotTideIntent get tide information for {City} {State} on {Date}
-OneshotTideIntent get tide information for {City} city
-OneshotTideIntent get tide information for {City} for {Date}
-OneshotTideIntent get tide information for {City} on {Date}
-OneshotTideIntent get tide information for {City} {Date}
-OneshotTideIntent get tides for {City}
-OneshotTideIntent get tides for {City} {State}
-OneshotTideIntent get tides for {City} {State} {Date}
-OneshotTideIntent get tides for {City} {Date}
-OneshotTideIntent tide information
-OneshotTideIntent tide information for {City}
-OneshotTideIntent tide information for {City} {State}
-OneshotTideIntent tide information for {City} on {Date}
-OneshotTideIntent tide information for {Date}
-OneshotTideIntent when high tide is
-OneshotTideIntent when is high tide
-OneshotTideIntent when is high tide for {City} {Date}
-OneshotTideIntent when is high tide in {City}
-OneshotTideIntent when is high tide in {City} {State}
-OneshotTideIntent when is high tide in {City} city
-OneshotTideIntent when is high tide in {City} on {Date}
-OneshotTideIntent when is high tide on {Date}
-OneshotTideIntent when is high tide {Date}
-OneshotTideIntent when is next tide
-OneshotTideIntent when is next tide in {City} {State}
-OneshotTideIntent when is next tide in {City} {State} on {Date}
-OneshotTideIntent when is next tide on {Date}
-OneshotTideIntent when is the highest tide in {City}
-OneshotTideIntent when is the highest tide in {City} {Date}
-OneshotTideIntent when is the highest tide {Date}
-OneshotTideIntent when is the next high tide {Date}
-OneshotTideIntent when is the next highest water
-OneshotTideIntent when is the next highest water for {City}
-OneshotTideIntent when is the next highest water for {City} {State} for {Date}
-OneshotTideIntent when is the next highest water for {City} {State}
-OneshotTideIntent when is the next highest water for {Date}
-OneshotTideIntent when is the next tide for {City}
-OneshotTideIntent when is the next tide for {City} {State}
-OneshotTideIntent when is the next tide for {City} city
-OneshotTideIntent when is the next tide for {City} for {Date}
-OneshotTideIntent when is the next tide for {Date}
-OneshotTideIntent when is today's high tide
-OneshotTideIntent when is today's highest tide {Date}
-OneshotTideIntent when will the water be highest for {City}
-OneshotTideIntent when will the water be highest for {City} for {Date}
-OneshotTideIntent when will the water be highest for {Date}
-OneshotTideIntent when will the water be highest {Date}
-OneshotTideIntent when is high tide on {Date}
-OneshotTideIntent when is the next tide for {Date}
-OneshotTideIntent when is next tide on {Date}
-OneshotTideIntent get the tides for {Date}
-OneshotTideIntent when is the highest tide {Date}
-OneshotTideIntent when is the next highest water for {Date}
-OneshotTideIntent when is high tide on {Date}
-OneshotTideIntent get high tide for {Date}
-OneshotTideIntent get high tide {Date}
-OneshotTideIntent when is high tide {Date}
-OneshotTideIntent tide information for {Date}
-OneshotTideIntent when is high tide in {City} on {Date}
-OneshotTideIntent get the next tide for {City} for {Date}
-OneshotTideIntent get high tide for {City} California {Date}
-OneshotTideIntent when is high tide for {City} {Date}
-OneshotTideIntent tide information for {City} on {Date}
-OneshotTideIntent when is high tide in {City} on {Date}
-OneshotTideIntent when is the next tide for {City} for {Date}
-OneshotTideIntent when is next tide in {City} {State} on {Date}
-OneshotTideIntent get high tide for {City} {Date}
-OneshotTideIntent get the high tide for {City} {Date}
-OneshotTideIntent tide information for {City} on {Date}
-OneshotTideIntent get tide information for {City} on {Date}
-OneshotTideIntent get tides for {City} {Date}
-
-SupportedCitiesIntent what cities
-SupportedCitiesIntent what cities are supported
-SupportedCitiesIntent which cities are supported
-SupportedCitiesIntent which cities
-SupportedCitiesIntent which cities do you know
\ No newline at end of file
diff --git a/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_CITIES b/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_CITIES
deleted file mode 100644
index 63eb710..0000000
--- a/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_CITIES
+++ /dev/null
@@ -1,17 +0,0 @@
-seattle
-los angeles
-monterey
-san diego
-san francisco
-boston
-new york
-miami
-wilmington
-tampa
-galveston
-morehead
-new orleans
-beaufort
-myrtle beach
-virginia beach
-charleston
\ No newline at end of file
diff --git a/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_STATES b/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_STATES
deleted file mode 100644
index 3a8b3bb..0000000
--- a/f-ask/samples/tidepooler/speech_assets/customSlotTypes/LIST_OF_STATES
+++ /dev/null
@@ -1,10 +0,0 @@
-california
-florida
-louisiana
-massachusetts
-new york
-north carolina
-south carolina
-texas
-virginia
-washington
\ No newline at end of file
diff --git a/f-ask/samples/tidepooler/templates.yaml b/f-ask/samples/tidepooler/templates.yaml
deleted file mode 100644
index 3ea2456..0000000
--- a/f-ask/samples/tidepooler/templates.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-welcome: |
-
- Welcome to Tide Pooler.
-
- Which city would you like tide information for?
-
-
-tide_info: |
- {{ date | humanize_date }} in {{ city }}, the first high tide will be around
- {{ tideinfo.first_high_tide_time | humanize_time }}, and will peak at about
- {{ tideinfo.first_high_tide_height | humanize_height }}, followed by a low tide around
- {{ tideinfo.low_tide_time | humanize_time }}, that will be about
- {{ tideinfo.low_tide_height | humanize_height }}.
-
- The second high tide will be around {{ tideinfo.second_high_tide_time | humanize_time }},
- and will peak at about {{ tideinfo.second_high_tide_height | humanize_height }}
-
-help: |
- I can lead you through providing a city and day of the week to get tide information, or you can simply open
- Tide Pooler and ask a question like, get tide information for Seattle on Saturday. For a list of supported
- cities, ask what cities are supported. Which city would you like tide information for?
-
-list_cities: |
- Currently, I know tide information for these coastal cities: {{ cities }}
- Which city would you like tide information for?
-
-list_cities_reprompt: Which city would you like tide information for?
-
-city_dialog: For which city would you like tide information for {{ date | humanize_date }}
-
-city_dialog_reprompt: For which city?
-
-date_dialog: For which date would you like tide information for {{ city }}?
-
-date_dialog_reprompt: For which date?
-
-date_dialog2: Please try again saying a day of the week, for example, Saturday
-
-noaa_problem: Sorry, the National Oceanic tide service is experiencing a problem. Please try again later.
-
-bye: Goodbye
diff --git a/f-ask/samples/tidepooler/tidepooler.py b/f-ask/samples/tidepooler/tidepooler.py
deleted file mode 100644
index e051b58..0000000
--- a/f-ask/samples/tidepooler/tidepooler.py
+++ /dev/null
@@ -1,298 +0,0 @@
-import os
-import logging
-import datetime
-import math
-import re
-from six.moves.urllib.request import urlopen
-from six.moves.urllib.parse import urlencode
-
-import aniso8601
-from flask import Flask, json, render_template
-from flask_ask import Ask, request, session, question, statement
-
-
-ENDPOINT = "http://tidesandcurrents.noaa.gov/api/datagetter"
-SESSION_CITY = "city"
-SESSION_DATE = "date"
-
-# NOAA station codes
-STATION_CODE_SEATTLE = "9447130"
-STATION_CODE_SAN_FRANCISCO = "9414290"
-STATION_CODE_MONTEREY = "9413450"
-STATION_CODE_LOS_ANGELES = "9410660"
-STATION_CODE_SAN_DIEGO = "9410170"
-STATION_CODE_BOSTON = "8443970"
-STATION_CODE_NEW_YORK = "8518750"
-STATION_CODE_VIRGINIA_BEACH = "8638863"
-STATION_CODE_WILMINGTON = "8658163"
-STATION_CODE_CHARLESTON = "8665530"
-STATION_CODE_BEAUFORT = "8656483"
-STATION_CODE_MYRTLE_BEACH = "8661070"
-STATION_CODE_MIAMI = "8723214"
-STATION_CODE_TAMPA = "8726667"
-STATION_CODE_NEW_ORLEANS = "8761927"
-STATION_CODE_GALVESTON = "8771341"
-
-STATIONS = {}
-STATIONS["seattle"] = STATION_CODE_SEATTLE
-STATIONS["san francisco"] = STATION_CODE_SAN_FRANCISCO
-STATIONS["monterey"] = STATION_CODE_MONTEREY
-STATIONS["los angeles"] = STATION_CODE_LOS_ANGELES
-STATIONS["san diego"] = STATION_CODE_SAN_DIEGO
-STATIONS["boston"] = STATION_CODE_BOSTON
-STATIONS["new york"] = STATION_CODE_NEW_YORK
-STATIONS["virginia beach"] = STATION_CODE_VIRGINIA_BEACH
-STATIONS["wilmington"] = STATION_CODE_WILMINGTON
-STATIONS["charleston"] = STATION_CODE_CHARLESTON
-STATIONS["beaufort"] = STATION_CODE_BEAUFORT
-STATIONS["myrtle beach"] = STATION_CODE_MYRTLE_BEACH
-STATIONS["miami"] = STATION_CODE_MIAMI
-STATIONS["tampa"] = STATION_CODE_TAMPA
-STATIONS["new orleans"] = STATION_CODE_NEW_ORLEANS
-STATIONS["galveston"] = STATION_CODE_GALVESTON
-
-
-app = Flask(__name__)
-ask = Ask(app, "/")
-logging.getLogger('flask_ask').setLevel(logging.DEBUG)
-
-
-class TideInfo(object):
-
- def __init__(self):
- self.first_high_tide_time = None
- self.first_high_tide_height = None
- self.low_tide_time = None
- self.low_tide_height = None
- self.second_high_tide_time = None
- self.second_high_tide_height = None
-
-
-@ask.launch
-def launch():
- welcome_text = render_template('welcome')
- help_text = render_template('help')
- return question(welcome_text).reprompt(help_text)
-
-
-@ask.intent('OneshotTideIntent',
- mapping={'city': 'City', 'date': 'Date'},
- convert={'date': 'date'},
- default={'city': 'seattle', 'date': datetime.date.today })
-def one_shot_tide(city, date):
- if city.lower() not in STATIONS:
- return supported_cities()
- return _make_tide_request(city, date)
-
-
-@ask.intent('DialogTideIntent',
- mapping={'city': 'City', 'date': 'Date'},
- convert={'date': 'date'})
-def dialog_tide(city, date):
- if city is not None:
- if city.lower() not in STATIONS:
- return supported_cities()
- if SESSION_DATE not in session.attributes:
- session.attributes[SESSION_CITY] = city
- return _dialog_date(city)
- date = aniso8601.parse_date(session.attributes[SESSION_DATE])
- return _make_tide_request(city, date)
- elif date is not None:
- if SESSION_CITY not in session.attributes:
- session.attributes[SESSION_DATE] = date.isoformat()
- return _dialog_city(date)
- city = session.attributes[SESSION_CITY]
- return _make_tide_request(city, date)
- else:
- return _dialog_no_slot()
-
-
-@ask.intent('SupportedCitiesIntent')
-def supported_cities():
- cities = ", ".join(sorted(STATIONS.keys()))
- list_cities_text = render_template('list_cities', cities=cities)
- list_cities_reprompt_text = render_template('list_cities_reprompt')
- return question(list_cities_text).reprompt(list_cities_reprompt_text)
-
-
-@ask.intent('AMAZON.HelpIntent')
-def help():
- help_text = render_template('help')
- list_cities_reprompt_text = render_template('list_cities_reprompt')
- return question(help_text).reprompt(list_cities_reprompt_text)
-
-
-@ask.intent('AMAZON.StopIntent')
-def stop():
- bye_text = render_template('bye')
- return statement(bye_text)
-
-
-@ask.intent('AMAZON.CancelIntent')
-def cancel():
- bye_text = render_template('bye')
- return statement(bye_text)
-
-
-@ask.session_ended
-def session_ended():
- return "{}", 200
-
-
-@app.template_filter()
-def humanize_date(dt):
- # http://stackoverflow.com/a/20007730/1163855
- ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])
- month_and_day_of_week = dt.strftime('%A %B')
- day_of_month = ordinal(dt.day)
- year = dt.year if dt.year != datetime.datetime.now().year else ""
- formatted_date = "{} {} {}".format(month_and_day_of_week, day_of_month, year)
- formatted_date = re.sub('\s+', ' ', formatted_date)
- return formatted_date
-
-
-@app.template_filter()
-def humanize_time(dt):
- morning_threshold = 12
- afternoon_threshold = 17
- evening_threshold = 20
- hour_24 = dt.hour
- if hour_24 < morning_threshold:
- period_of_day = "in the morning"
- elif hour_24 < afternoon_threshold:
- period_of_day = "in the afternoon"
- elif hour_24 < evening_threshold:
- period_of_day = "in the evening"
- else:
- period_of_day = " at night"
- the_time = dt.strftime('%I:%M')
- formatted_time = "{} {}".format(the_time, period_of_day)
- return formatted_time
-
-
-@app.template_filter()
-def humanize_height(height):
- round_down_threshold = 0.25
- round_to_half_threshold = 0.75
- is_negative = False
- if height < 0:
- height = abs(height)
- is_negative = True
- remainder = height % 1
- if remainder < round_down_threshold:
- remainder_text = ""
- feet = int(math.floor(height))
- elif remainder < round_to_half_threshold:
- remainder_text = "and a half"
- feet = int(math.floor(height))
- else:
- remainder_text = ""
- feet = int(math.floor(height))
- if is_negative:
- feet *= -1
- formatted_height = "{} {} feet".format(feet, remainder_text)
- formatted_height = re.sub('\s+', ' ', formatted_height)
- return formatted_height
-
-
-def _dialog_no_slot():
- if SESSION_CITY in session.attributes:
- date_dialog2_text = render_template('date_dialog2')
- return question(date_dialog2_text).reprompt(date_dialog2_text)
- else:
- return supported_cities()
-
-
-def _dialog_date(city):
- date_dialog_text = render_template('date_dialog', city=city)
- date_dialog_reprompt_text = render_template('date_dialog_reprompt')
- return question(date_dialog_text).reprompt(date_dialog_reprompt_text)
-
-
-def _dialog_city(date):
- session.attributes[SESSION_DATE] = date
- session.attributes_encoder = _json_date_handler
- city_dialog_text = render_template('city_dialog', date=date)
- city_dialog_reprompt_text = render_template('city_dialog_reprompt')
- return question(city_dialog_text).reprompt(city_dialog_reprompt_text)
-
-
-def _json_date_handler(obj):
- if isinstance(obj, datetime.date):
- return obj.isoformat()
-
-
-def _make_tide_request(city, date):
- station = STATIONS.get(city.lower())
- noaa_api_params = {
- 'station': station,
- 'product': 'predictions',
- 'datum': 'MLLW',
- 'units': 'english',
- 'time_zone': 'lst_ldt',
- 'format': 'json'
- }
- if date == datetime.date.today():
- noaa_api_params['date'] = 'today'
- else:
- noaa_api_params['begin_date'] = date.strftime('%Y%m%d')
- noaa_api_params['range'] = 24
- url = ENDPOINT + "?" + urlencode(noaa_api_params)
- resp_body = urlopen(url).read()
- if len(resp_body) == 0:
- statement_text = render_template('noaa_problem')
- else:
- noaa_response_obj = json.loads(resp_body)
- predictions = noaa_response_obj['predictions']
- tideinfo = _find_tide_info(predictions)
- statement_text = render_template('tide_info', date=date, city=city, tideinfo=tideinfo)
- return statement(statement_text).simple_card("Tide Pooler", statement_text)
-
-
-def _find_tide_info(predictions):
- """
- Algorithm to find the 2 high tides for the day, the first of which is smaller and occurs
- mid-day, the second of which is larger and typically in the evening.
- """
-
- last_prediction = None
- first_high_tide = None
- second_high_tide = None
- low_tide = None
- first_tide_done = False
- for prediction in predictions:
- if last_prediction is None:
- last_prediction = prediction
- continue
- if last_prediction['v'] < prediction['v']:
- if not first_tide_done:
- first_high_tide = prediction
- else:
- second_high_tide = prediction
- else: # we're decreasing
- if not first_tide_done and first_high_tide is not None:
- first_tide_done = True
- elif second_high_tide is not None:
- break # we're decreasing after having found the 2nd tide. We're done.
- if first_tide_done:
- low_tide = prediction
- last_prediction = prediction
-
- fmt = '%Y-%m-%d %H:%M'
- parse = datetime.datetime.strptime
- tideinfo = TideInfo()
- tideinfo.first_high_tide_time = parse(first_high_tide['t'], fmt)
- tideinfo.first_high_tide_height = float(first_high_tide['v'])
- tideinfo.second_high_tide_time = parse(second_high_tide['t'], fmt)
- tideinfo.second_high_tide_height = float(second_high_tide['v'])
- tideinfo.low_tide_time = parse(low_tide['t'], fmt)
- tideinfo.low_tide_height = float(low_tide['v'])
- return tideinfo
-
-
-if __name__ == '__main__':
- if 'ASK_VERIFY_REQUESTS' in os.environ:
- verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
- if verify == 'false':
- app.config['ASK_VERIFY_REQUESTS'] = False
- app.run(debug=True)
diff --git a/f-ask/setup.cfg b/f-ask/setup.cfg
deleted file mode 100644
index 1eee7db..0000000
--- a/f-ask/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[bdist_wheel]
-universal = 1
-
-[metadata]
-description-file = README.md
diff --git a/f-ask/setup.py b/f-ask/setup.py
deleted file mode 100644
index 17031ea..0000000
--- a/f-ask/setup.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""
-Flask-Ask
--------------
-
-Easy Alexa Skills Kit integration for Flask
-"""
-from setuptools import setup
-
-def parse_requirements(filename):
- """ load requirements from a pip requirements file """
- lineiter = (line.strip() for line in open(filename))
- return [line for line in lineiter if line and not line.startswith("#")]
-
-setup(
- name='Flask-Ask',
- version='0.9.7',
- url='https://github.com/johnwheeler/flask-ask',
- license='Apache 2.0',
- author='John Wheeler',
- author_email='john@johnwheeler.org',
- description='Rapid Alexa Skills Kit Development for Amazon Echo Devices in Python',
- long_description=__doc__,
- packages=['flask_ask'],
- zip_safe=False,
- include_package_data=True,
- platforms='any',
- install_requires=parse_requirements('requirements.txt'),
- test_requires=[
- 'mock',
- 'requests'
- ],
- test_suite='tests',
- classifiers=[
- 'License :: OSI Approved :: Apache Software License',
- 'Framework :: Flask',
- 'Programming Language :: Python',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'Operating System :: OS Independent',
- 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
- 'Topic :: Software Development :: Libraries :: Python Modules'
- ]
-)
diff --git a/f-ask/tests/__init__.py b/f-ask/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/f-ask/tests/test_audio.py b/f-ask/tests/test_audio.py
deleted file mode 100644
index 310d082..0000000
--- a/f-ask/tests/test_audio.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import unittest
-from mock import patch, MagicMock
-from flask import Flask
-from flask_ask import Ask, audio
-from flask_ask.models import _Field
-
-
-class AudioUnitTests(unittest.TestCase):
-
- def setUp(self):
- self.ask_patcher = patch('flask_ask.core.find_ask', return_value=Ask())
- self.ask_patcher.start()
- self.context_patcher = patch('flask_ask.models.context', return_value=MagicMock())
- self.context_patcher.start()
-
- def tearDown(self):
- self.ask_patcher.stop()
- self.context_patcher.stop()
-
- def test_token_generation(self):
- """ Confirm we get a new token when setting a stream url """
- audio_item = audio()._audio_item(stream_url='https://fakestream', offset=123)
- self.assertEqual(36, len(audio_item['stream']['token']))
- self.assertEqual(123, audio_item['stream']['offsetInMilliseconds'])
-
- def test_custom_token(self):
- """ Check to see that the provided opaque token remains constant"""
- token = "hello_world"
- audio_item = audio()._audio_item(stream_url='https://fakestream', offset=10, opaque_token=token)
- self.assertEqual(token, audio_item['stream']['token'])
- self.assertEqual(10, audio_item['stream']['offsetInMilliseconds'])
-
-
-class AskStreamHandlingTests(unittest.TestCase):
-
- def setUp(self):
- fake_context = {'System': {'user': {'userId': 'dave'}}}
- self.context_patcher = patch.object(Ask, 'context', return_value=fake_context)
- self.context_patcher.start()
- self.request_patcher = patch.object(Ask, 'request', return_value=MagicMock())
- self.request_patcher.start()
-
- def tearDown(self):
- self.context_patcher.stop()
- self.request_patcher.stop()
-
- def test_setting_and_getting_current_stream(self):
- ask = Ask()
- with patch('flask_ask.core.find_ask', return_value=ask):
- self.assertEqual(_Field(), ask.current_stream)
-
- stream = _Field()
- stream.__dict__.update({'token': 'asdf', 'offsetInMilliseconds': 123, 'url': 'junk'})
- with patch('flask_ask.core.top_stream', return_value=stream):
- self.assertEqual(stream, ask.current_stream)
-
- def test_from_directive_call(self):
- ask = Ask()
- fake_stream = _Field()
- fake_stream.__dict__.update({'token':'fake'})
- with patch('flask_ask.core.top_stream', return_value=fake_stream):
- from_buffer = ask._from_directive()
- self.assertEqual(fake_stream, from_buffer)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/f-ask/tests/test_cache.py b/f-ask/tests/test_cache.py
deleted file mode 100644
index 418dde8..0000000
--- a/f-ask/tests/test_cache.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import unittest
-from mock import patch, Mock
-from werkzeug.contrib.cache import SimpleCache
-from flask_ask.core import Ask
-from flask_ask.cache import push_stream, pop_stream, top_stream, set_stream
-
-
-class CacheTests(unittest.TestCase):
-
- def setUp(self):
- self.patcher = patch('flask_ask.core.find_ask', return_value=Ask())
- self.ask = self.patcher.start()
- self.user_id = 'dave'
- self.token = '123-abc'
- self.cache = SimpleCache()
-
- def tearDown(self):
- self.patcher.stop()
-
- def test_adding_removing_stream(self):
- self.assertTrue(push_stream(self.cache, self.user_id, self.token))
-
- # peak at the top
- self.assertEqual(self.token, top_stream(self.cache, self.user_id))
- self.assertIsNone(top_stream(self.cache, 'not dave'))
-
- # pop it off
- self.assertEqual(self.token, pop_stream(self.cache, self.user_id))
- self.assertIsNone(top_stream(self.cache, self.user_id))
-
- def test_pushing_works_like_a_stack(self):
- push_stream(self.cache, self.user_id, 'junk')
- push_stream(self.cache, self.user_id, self.token)
-
- self.assertEqual(self.token, pop_stream(self.cache, self.user_id))
- self.assertEqual('junk', pop_stream(self.cache, self.user_id))
- self.assertIsNone(pop_stream(self.cache, self.user_id))
-
- def test_cannot_push_nones_into_stack(self):
- self.assertIsNone(push_stream(self.cache, self.user_id, None))
-
- def test_set_overrides_stack(self):
- push_stream(self.cache, self.user_id, '1')
- push_stream(self.cache, self.user_id, '2')
- self.assertEqual('2', top_stream(self.cache, self.user_id))
-
- set_stream(self.cache, self.user_id, '3')
- self.assertEqual('3', pop_stream(self.cache, self.user_id))
- self.assertIsNone(pop_stream(self.cache, self.user_id))
-
- def test_calls_to_top_with_no_user_return_none(self):
- """ RedisCache implementation doesn't like None key values. """
- mock = Mock()
- result = top_stream(mock, None)
- self.assertFalse(mock.get.called)
- self.assertIsNone(result)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/f-ask/tests/test_core.py b/f-ask/tests/test_core.py
deleted file mode 100644
index 6b424c4..0000000
--- a/f-ask/tests/test_core.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- coding: utf-8 -*-
-import unittest
-from aniso8601.timezone import UTCOffset, build_utcoffset
-from flask_ask.core import Ask
-
-from datetime import datetime, timedelta
-from mock import patch, MagicMock
-import json
-
-
-class FakeRequest(object):
- """ Fake out a Flask request for testing purposes for now """
-
- headers = {'Signaturecertchainurl': None, 'Signature': None}
-
- def __init__(self, data):
- self.data = json.dumps(data)
-
-
-class TestCoreRoutines(unittest.TestCase):
- """ Tests for core Flask Ask functionality """
-
-
- def setUp(self):
- self.mock_app = MagicMock()
- self.mock_app.debug = True
- self.mock_app.config = {'ASK_VERIFY_TIMESTAMP_DEBUG': False}
-
- # XXX: this mess implies we should think about tidying up Ask._alexa_request
- self.patch_current_app = patch('flask_ask.core.current_app', new=self.mock_app)
- self.patch_load_cert = patch('flask_ask.core.verifier.load_certificate')
- self.patch_verify_sig = patch('flask_ask.core.verifier.verify_signature')
- self.patch_current_app.start()
- self.patch_load_cert.start()
- self.patch_verify_sig.start()
-
- @patch('flask_ask.core.flask_request',
- new=FakeRequest({'request': {'timestamp': 1234},
- 'session': {'application': {'applicationId': 1}}}))
- def test_alexa_request_parsing(self):
- ask = Ask()
- ask._alexa_request()
-
-
- def test_parse_timestamp(self):
- utc = build_utcoffset('UTC', timedelta(hours=0))
- result = Ask._parse_timestamp('2017-07-08T07:38:00Z')
- self.assertEqual(datetime(2017, 7, 8, 7, 38, 0, 0, utc), result)
-
- result = Ask._parse_timestamp(1234567890)
- self.assertEqual(datetime(2009, 2, 13, 23, 31, 30), result)
-
- with self.assertRaises(ValueError):
- Ask._parse_timestamp(None)
-
- def test_tries_parsing_on_valueerror(self):
- max_timestamp = 253402300800
-
- # should cause a ValueError normally
- with self.assertRaises(ValueError):
- datetime.utcfromtimestamp(max_timestamp)
-
- # should safely parse, assuming scale change needed
- # note: this assert looks odd, but Py2 handles the parsing
- # differently, resulting in a differing timestamp
- # due to more granularity of microseconds
- result = Ask._parse_timestamp(max_timestamp)
- self.assertEqual(datetime(1978, 1, 11, 21, 31, 40).timetuple()[0:6],
- result.timetuple()[0:6])
-
- with self.assertRaises(ValueError):
- # still raise an error if too large
- Ask._parse_timestamp(max_timestamp * 1000)
-
- def tearDown(self):
- self.patch_current_app.stop()
- self.patch_load_cert.stop()
- self.patch_verify_sig.stop()
diff --git a/f-ask/tests/test_integration.py b/f-ask/tests/test_integration.py
deleted file mode 100644
index 0c18c5a..0000000
--- a/f-ask/tests/test_integration.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import unittest
-import json
-import uuid
-
-from flask_ask import Ask, audio
-from flask import Flask
-
-
-play_request = {
- "version": "1.0",
- "session": {
- "new": True,
- "sessionId": "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000",
- "application": {
- "applicationId": "fake-application-id"
- },
- "attributes": {},
- "user": {
- "userId": "amzn1.account.AM3B00000000000000000000000"
- }
- },
- "context": {
- "System": {
- "application": {
- "applicationId": "fake-application-id"
- },
- "user": {
- "userId": "amzn1.account.AM3B00000000000000000000000"
- },
- "device": {
- "supportedInterfaces": {
- "AudioPlayer": {}
- }
- }
- },
- "AudioPlayer": {
- "offsetInMilliseconds": 0,
- "playerActivity": "IDLE"
- }
- },
- "request": {
- "type": "IntentRequest",
- "requestId": "string",
- "timestamp": "string",
- "locale": "string",
- "intent": {
- "name": "TestPlay",
- "slots": {
- }
- }
- }
-}
-
-
-class AudioIntegrationTests(unittest.TestCase):
- """ Integration tests of the Audio Directives """
-
- def setUp(self):
- self.app = Flask(__name__)
- self.app.config['ASK_VERIFY_REQUESTS'] = False
- self.ask = Ask(app=self.app, route='/ask')
- self.client = self.app.test_client()
- self.stream_url = 'https://fakestream'
- self.custom_token = 'custom_uuid_{0}'.format(str(uuid.uuid4()))
-
- @self.ask.intent('TestPlay')
- def play():
- return audio('playing').play(self.stream_url)
-
- @self.ask.intent('TestCustomTokenIntents')
- def custom_token_intents():
- return audio('playing with custom token').play(self.stream_url,
- opaque_token=self.custom_token)
-
- def tearDown(self):
- pass
-
- def test_play_intent(self):
- """ Test to see if we can properly play a stream """
- response = self.client.post('/ask', data=json.dumps(play_request))
- self.assertEqual(200, response.status_code)
-
- data = json.loads(response.data.decode('utf-8'))
- self.assertEqual('playing',
- data['response']['outputSpeech']['text'])
-
- directive = data['response']['directives'][0]
- self.assertEqual('AudioPlayer.Play', directive['type'])
-
- stream = directive['audioItem']['stream']
- self.assertIsNotNone(stream['token'])
- self.assertEqual(self.stream_url, stream['url'])
- self.assertEqual(0, stream['offsetInMilliseconds'])
-
- def test_play_intent_with_custom_token(self):
- """ Test to check that custom token supplied is returned """
-
- # change the intent name to route to our custom token for play_request
- original_intent_name = play_request['request']['intent']['name']
- play_request['request']['intent']['name'] = 'TestCustomTokenIntents'
-
- response = self.client.post('/ask', data=json.dumps(play_request))
- self.assertEqual(200, response.status_code)
-
- data = json.loads(response.data.decode('utf-8'))
- self.assertEqual('playing with custom token',
- data['response']['outputSpeech']['text'])
-
- directive = data['response']['directives'][0]
- self.assertEqual('AudioPlayer.Play', directive['type'])
-
- stream = directive['audioItem']['stream']
- self.assertEqual(stream['token'], self.custom_token)
- self.assertEqual(self.stream_url, stream['url'])
- self.assertEqual(0, stream['offsetInMilliseconds'])
-
- # reset our play_request
- play_request['request']['intent']['name'] = original_intent_name
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/f-ask/tests/test_integration_support_entity_resolution.py b/f-ask/tests/test_integration_support_entity_resolution.py
deleted file mode 100644
index 959e702..0000000
--- a/f-ask/tests/test_integration_support_entity_resolution.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import unittest
-import json
-import uuid
-
-from flask_ask import Ask, statement
-from flask import Flask
-
-
-play_request = {
- "version": "1.0",
- "session": {
- "new": False,
- "sessionId": "amzn1.echo-api.session.f6ebc0ba-9d7a-4c3f-b056-b6c3f9da0713",
- "application": {
- "applicationId": "amzn1.ask.skill.26338c44-65da-4d58-aa75-c86b21271eb7"
- },
- "user": {
- "userId": "amzn1.ask.account.AHR7KBC3MFCX7LYT6HJBGDLIGQUU3FLANWCZ",
- }
- },
- "context": {
- "AudioPlayer": {
- "playerActivity": "IDLE"
- },
- "Display": {
- "token": ""
- },
- "System": {
- "application": {
- "applicationId": "amzn1.ask.skill.26338c44-65da-4d58-aa75-c86b21271eb7"
- },
- "user": {
- "userId": "amzn1.ask.account.AHR7KBC3MFCX7LYT6HJBGDLIGQUU3FLANWCZ",
- },
- "device": {
- "deviceId": "amzn1.ask.device.AELNXV4JQJMF5QALYUQXHOZJ",
- "supportedInterfaces": {
- "AudioPlayer": {},
- "Display": {
- "templateVersion": "1.0",
- "markupVersion": "1.0"
- }
- }
- },
- "apiEndpoint": "https://api.amazonalexa.com",
- }
- },
- "request": {
- "type": "IntentRequest",
- "requestId": "amzn1.echo-api.request.4859a7e3-1960-4ed9-ac7b-854309346916",
- "timestamp": "2018-04-04T06:28:23Z",
- "locale": "en-US",
- "intent": {
- "name": "TestCustomSlotTypeIntents",
- "confirmationStatus": "NONE",
- "slots": {
- "child_info": {
- "name": "child_info",
- "value": "friends info",
- "resolutions": {
- "resolutionsPerAuthority": [
- {
- "authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.26338c44-65da-4d58-aa75-c86b21271eb7.child_info_type",
- "status": {
- "code": "ER_SUCCESS_MATCH"
- },
- "values": [
- {
- "value": {
- "name": "friend_info",
- "id": "FRIEND_INFO"
- }
- }
- ]
- }
- ]
- },
- "confirmationStatus": "NONE"
- }
- }
- },
- "dialogState": "STARTED"
- }
-}
-
-
-class CustomSlotTypeIntegrationTests(unittest.TestCase):
- """ Integration tests of the custom slot type """
-
- def setUp(self):
- self.app = Flask(__name__)
- self.app.config['ASK_VERIFY_REQUESTS'] = False
- self.ask = Ask(app=self.app, route='/ask')
- self.client = self.app.test_client()
-
- @self.ask.intent('TestCustomSlotTypeIntents')
- def custom_slot_type_intents(child_info):
- return statement(child_info)
-
- def tearDown(self):
- pass
-
- def test_custom_slot_type_intent(self):
- """ Test to see if custom slot type value is correct """
- response = self.client.post('/ask', data=json.dumps(play_request))
- self.assertEqual(200, response.status_code)
-
- data = json.loads(response.data.decode('utf-8'))
- self.assertEqual('friend_info',
- data['response']['outputSpeech']['text'])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/f-ask/tests/test_samples.py b/f-ask/tests/test_samples.py
deleted file mode 100644
index 4926029..0000000
--- a/f-ask/tests/test_samples.py
+++ /dev/null
@@ -1,171 +0,0 @@
-"""
-Smoke test using the samples.
-"""
-
-import unittest
-import os
-import six
-import sys
-import time
-import subprocess
-
-from requests import post
-
-import flask_ask
-
-
-launch = {
- "version": "1.0",
- "session": {
- "new": True,
- "sessionId": "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000",
- "application": {
- "applicationId": "fake-application-id"
- },
- "attributes": {},
- "user": {
- "userId": "amzn1.account.AM3B00000000000000000000000"
- }
- },
- "context": {
- "System": {
- "application": {
- "applicationId": "fake-application-id"
- },
- "user": {
- "userId": "amzn1.account.AM3B00000000000000000000000"
- },
- "device": {
- "supportedInterfaces": {
- "AudioPlayer": {}
- }
- }
- },
- "AudioPlayer": {
- "offsetInMilliseconds": 0,
- "playerActivity": "IDLE"
- }
- },
- "request": {
- "type": "LaunchRequest",
- "requestId": "string",
- "timestamp": "string",
- "locale": "string",
- "intent": {
- "name": "TestPlay",
- "slots": {
- }
- }
- }
-}
-
-
-project_root = os.path.abspath(os.path.join(flask_ask.__file__, '../..'))
-
-
-@unittest.skipIf(six.PY2, "Not yet supported on Python 2.x")
-class SmokeTestUsingSamples(unittest.TestCase):
- """ Try launching each sample and sending some requests to them. """
-
- def setUp(self):
- self.python = sys.executable
- self.env = {'PYTHONPATH': project_root,
- 'ASK_VERIFY_REQUESTS': 'false'}
- if os.name == 'nt':
- self.env['SYSTEMROOT'] = os.getenv('SYSTEMROOT')
- self.env['PATH'] = os.getenv('PATH')
-
- def _launch(self, sample):
- prefix = os.path.join(project_root, 'samples/')
- path = prefix + sample
- process = subprocess.Popen([self.python, path], env=self.env)
- time.sleep(1)
- self.assertIsNone(process.poll(),
- msg='Poll should work,'
- 'otherwise we failed to launch')
- self.process = process
-
- def _post(self, route='/', data={}):
- url = 'http://127.0.0.1:5000' + str(route)
- print('POSTing to %s' % url)
- response = post(url, json=data)
- self.assertEqual(200, response.status_code)
- return response
-
- @staticmethod
- def _get_text(http_response):
- data = http_response.json()
- return data.get('response', {})\
- .get('outputSpeech', {})\
- .get('text', None)
-
- @staticmethod
- def _get_reprompt(http_response):
- data = http_response.json()
- return data.get('response', {})\
- .get('reprompt', {})\
- .get('outputSpeech', {})\
- .get('text', None)
-
- def tearDown(self):
- try:
- self.process.terminate()
- self.process.communicate(timeout=1)
- except Exception as e:
- try:
- print('[%s]...trying to kill.' % str(e))
- self.process.kill()
- self.process.communicate(timeout=1)
- except Exception as e:
- print('Error killing test python process: %s' % str(e))
- print('*** it is recommended you manually kill with PID %s',
- self.process.pid)
-
- def test_helloworld(self):
- """ Test the HelloWorld sample project """
- self._launch('helloworld/helloworld.py')
- response = self._post(data=launch)
- self.assertTrue('hello' in self._get_text(response))
-
- def test_session_sample(self):
- """ Test the Session sample project """
- self._launch('session/session.py')
- response = self._post(data=launch)
- self.assertTrue('favorite color' in self._get_text(response))
-
- def test_audio_simple_demo(self):
- """ Test the SimpleDemo Audio sample project """
- self._launch('audio/simple_demo/ask_audio.py')
- response = self._post(data=launch)
- self.assertTrue('audio example' in self._get_text(response))
-
- def test_audio_playlist_demo(self):
- """ Test the Playlist Audio sample project """
- self._launch('audio/playlist_demo/playlist.py')
- response = self._post(data=launch)
- self.assertTrue('playlist' in self._get_text(response))
-
- def test_blueprints_demo(self):
- """ Test the sample project using Flask Blueprints """
- self._launch('blueprint_demo/demo.py')
- response = self._post(route='/ask', data=launch)
- self.assertTrue('hello' in self._get_text(response))
-
- def test_history_buff(self):
- """ Test the History Buff sample """
- self._launch('historybuff/historybuff.py')
- response = self._post(data=launch)
- self.assertTrue('History buff' in self._get_text(response))
-
- def test_spacegeek(self):
- """ Test the Spacegeek sample """
- self._launch('spacegeek/spacegeek.py')
- response = self._post(data=launch)
- # response is random
- self.assertTrue(len(self._get_text(response)) > 1)
-
- def test_tidepooler(self):
- """ Test the Tide Pooler sample """
- self._launch('tidepooler/tidepooler.py')
- response = self._post(data=launch)
- self.assertTrue('Which city' in self._get_reprompt(response))
diff --git a/f-ask/tests/test_unicode.py b/f-ask/tests/test_unicode.py
deleted file mode 100644
index 0c1f60f..0000000
--- a/f-ask/tests/test_unicode.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-import unittest
-from flask_ask import statement, question
-
-
-class UnicodeTests(unittest.TestCase):
- """ Test using Unicode in responses. (Issue #147) """
-
- unicode_string = u"Was kann ich für dich tun?"
-
- def test_unicode_statements(self):
- """ Test unicode statement responses """
- stmt = statement(self.unicode_string)
- speech = stmt._response['outputSpeech']['text']
- print(speech)
- self.assertTrue(self.unicode_string in speech)
-
- def test_unicode_questions(self):
- """ Test unicode in question responses """
- q = question(self.unicode_string)
- speech = q._response['outputSpeech']['text']
- self.assertTrue(self.unicode_string in speech)
diff --git a/f-ask/tox.ini b/f-ask/tox.ini
deleted file mode 100644
index 3c6a9c1..0000000
--- a/f-ask/tox.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[tox]
-envlist = py27,py3
-skipsdist = True
-
-[testenv]
-deps = -rrequirements-dev.txt
-commands = python setup.py test
-