Alexa,运行这个 JavaScript 应用程序
概括
因此,有两种类型的 Alexa 技能教程,一种有大量屏幕截图,向您展示如何创建 AWS lambda 函数、配置技能和部署一些任意代码……另一种向您展示如何开发技能、测试技能、部署技能并获得认证。前一类教程太多了(我推荐这个和这个),所以我选择后者。
您将看到的许多 Alexa 代码示例将非常专注于快速设置某些意图,这是可以理解的。本文将重点介绍如何组合一个强大的应用程序,您可以放心地扩展复杂性。它将是模块化的,使用 TDD,经过 linted,具有 CI 管道和使用Gulp 的简单部署步骤。
一般情况下,我会假设您对 Alexa 技能有初步了解,如果没有,请先阅读这两个链接,然后再回来...拜托。哦,是的,这也与 JavaScript 有关(如果您对 Python 情有独钟的话)。
当前形势
Fact.prototype = Object.create(AlexaSkill.prototype);
Fact.prototype.constructor = Fact;
...但是帮助就在眼前,因为你偶然发现了这个家伙,你会想,啊,太酷了,这个 API 好一点,然后开始尝试用它编写你的技能......尽管你仍然不完全确定如何去做,而且它也不完全像亚马逊官方的。
但是,有一个新的 SDK 发布了(你很难在网站上找到它,所以 repo 在这里)。这就是我们将在以下教程中使用的工具。
10 份入门套餐
结构
所有技能都将代码分为src和语音资产- 我们不要逆潮流而行。除此之外,我还将徽标(在商店上线时需要)和我的circleci配置粘贴在这里。您可以使用任何 CI,但正如您从我的 CI 中看到的那样,您需要指定所有代码都在嵌套的src目录中,并且因为我们是使用 ES2015 编写测试的受虐狂,所以我们需要 Node 6.xx
言语资产
意图-schema.json
一个配置,基本上向 Alexa 解释了她应该期待的“人们说的话”(意图)以及插槽的类型(意图中的变量)。这将帮助她更轻松地弄清楚如何解释用户所说的话。
话语
完全令人费解的野兽试图驯服英语。这基本上是另一只援助之手,让 Alexa 有机会解读用户所说的话。如果他们说了这件事,他们的意思就是这样。花括号中的单词代表插槽,其类型在隔壁的 json 模式中定义。小心单词意图,对于 Arithlistic,我使用“pass”,当你期望它只在有人说“pass”时触发时,它会通过随机单词触发得有点太多了……
源码
在特殊src中您可以施展自己的技能。
在顶层
我们实际上拥有了开发资产和装置。您可能可以将它们拆分到自己的目录中,但感觉有点过头了。所以我们有:
- 由于我们将要使用的 SDK,eslint 配置必须相当宽松
- 你的秘密deploy.env.json(显然你会自己创建)看起来有点像这样:
{
"ROLE": "arn:aws:iam::[123456789012]:role/[the AWS lambda role you defined]",
"ACCESS_KEY_ID": "ABCDEFGHIJKLMNOPQRST",
"SECRET_ACCESS_KEY": "abcdefghijklmnopqrst/abcdefghijklmnopqrs"
}
这使我们能够使用 gulp 任务部署代码
- enums.js将帮助我们管理技能的状态,只是为了在 JavaScript 领域给我们一些类型安全的感觉
- 一个gulpfile.js,详细说明了我们的部署任务。它删除了旧的dist内容,抓取了我们关心的所有内容并将其放入dist中,然后将其压缩,因为这是 AWS lambda 接收多个文件的方式,并使用node-aws-lambda使用 lambda-config 上传我们的文件,该lambda-config的值已替换为我们上面的秘密值
- index.js - 我们的技能入口 - 它设置环境路径以帮助我们的 AWS lambda 遍历我们的文件,注册我们的appId和处理程序(我们将介绍)并启动整个过程
- lambda -config.js看起来和你的AWS lambda 控制台中的屏幕非常相似。根据需要设置这些值
- package.json - 我假设您知道。唯一需要注意的是脚本:
- 找到一个部署 Alexa 技能的好方法有点麻烦,这不是部署工作流程。我找到了这两个示例(Rick 的和Thoughtworks 的),我从中大量借鉴了它们,用于我自己的gulpfile。然而,它们都喜欢PROD安装,而我采用了一种略有不同的方法,以避免每次部署时都进行安装。当你为你的技能执行 npm i时,它会将PROD依赖项直接粘贴到dist中
- 我还在其中加入了iron-node,虽然我对它的使用体验褒贬不一,但当你想在 Chrome 的开发工具中轻松调试某些东西时,它实际上非常有用。不过,请随意以你喜欢的方式调试你的节点代码,例如通过你的 IDE
- responses.js应该是你放置 Alexa 所说的任何内容的地方。我会将它们作为函数来使用一致的 API,以便你可以传递要动态构建的值。事实上,我重构了这些函数来调用 emit 方法,以便存储最后已知的响应和状态,帮助我们在“暂停”后继续。将所有响应放在一个地方可以使测试更容易(因为你可以在不破坏测试的情况下更改短语),当 i18n 出现时,你可以交换这个文件,而不是让硬编码的文本散落在你的代码库中
处理程序
这些人负责接收意图(Alexa 根据您的语音资产认为用户所说的内容)、执行某种业务逻辑、可能在会话状态(或 dynamo db)中存储一些内容并返回响应(Alexa 要回复的内容)。
状态内容是必需的,因为该技能非常愚蠢,它会接收一些 JSON,然后发送一些 JSON。正是由于这种纯粹的性质,TDD(以及一般的测试)才如此简单。当我们稍后看到事件示例时,您将看到该 JSON 的样子。最好让您的处理程序尽可能精简,并将业务逻辑移到模块文件夹中。
我采用了一种相当严格的方法来注释每个处理程序的意图,以确保它在编辑时遵循相同的模式。模式是,设置、状态更改、响应。作为函数式编程教会的一员,最好在意图中获取一个不可变的模型,执行你的逻辑,更新它,然后将其与你的响应一起返回。然而,SDK 采用了更面向对象的方法,大量使用它来触发事件和管理状态……这并不理想,并导致了一些不稳定的解决方法。
我建议尽可能多地管理小型处理程序,这将使处理“是”和“否”等模糊意图变得更加容易,并有助于组织代码和测试。
在入门套件中我们只有三个处理程序:
- core.handlers.js - 我们最后会谈到“认证”,但现在你只需要知道这些是必需的,所以我们会将它们混合到我们所有其他处理程序中。AMAZON.StopIntent基本上是暂停,AMAZON.CancelIntent是“让我离开这里”。SessionEndedRequest只是为了记录目的而准备的
- new-session.handlers.js - 这是技能入口点(与代码入口点index.js相反),我们将在这里设置一些状态(决定要转到哪个处理程序)并告诉我们的技能要运行哪个意图。还有随机的“catch-all”,它将注销一些真正无用的东西……
- checked.handlers.js - 因为StopIntent和CancelIntent通常是必需的,理想情况下,我们希望有一个地方来管理“暂停”。在这里,如果用户希望继续,我们将状态设置回之前的状态;如果他们不想继续,则退出
模块
这是您的业务逻辑应该存在的地方。如上所述,所有繁重的工作都应该在这里进行。我们不希望意图变得太忙太脏,因为这会使它难以阅读和维护。我在utils模块中包含了一个小型的简单 mixin 实现,这样我们就不需要一遍又一遍地添加核心意图了。
测试
所有示例均未经过测试。我认为,不经过测试,您无法真正开发和发布 Alexa 技能。有如此多的路径、分支和意图,只要您在技能中增加一点点复杂性,就会出现错误。
在测试文件夹中,我们拥有称为事件样本的装置和一个巨大的测试文件。现在,您可能可以将其拆分,但感觉像是“预优化”。我非常喜欢将所有技能测试路径放在一起。
因此我们在顶部有一些助手:
- 如果您使用了多行模板字符串并想在语音输出上进行断言,那么清理只是为了让生活更轻松
- getOutputSpeech只是对响应进行解构,剥离所有内容(ssml 标签),并允许我们断言我们关心的内容,即 Alexa 说了什么
- getAttribute也是一个很好的抽象,可以挑选出我们要测试的会话属性
- 最后,但绝非最不重要的一点是runIntent ,它将使您的所有测试都成为梦想。它使用aws-lambda-mock-context来消除我们并不真正关心的所有上下文内容,并为我们提供流畅的 API 来轻松调用我们的意图
我采用了一种嵌套很深的方法,这样当你阅读输出时,它就会给人一种对话的感觉。因此,每个描述都是用户会说的话,然后测试名称是你认为你的技能应该做的事情。正如你所见,我们现在可以很好地解构并以相当干净的方式断言。此外,使用粗箭头意味着每个花括号更少,这是获胜的关键。
我会根据处理程序状态拆分</font
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~