develpr/alexa-app

一套类库,用于简化使用Lumen(以及一定程度上Laravel)创建简单的亚马逊Echo Alexa应用

0.3.5 2017-11-10 13:20 UTC

README

Latest Version Total Downloads Software License StyleCI

一套类库,用于简化使用Laravel和Lumen创建简单的亚马逊Echo Alexa应用(注意:5.2.x版本的Lumen存在一个需要解决的问题[5]

主要更新 - 0.2.0 - 被称为测试版

我最近重构了几乎所有这个包,使其与Laravel兼容,并避免了之前完全替换默认Lumen Application 的做法。我还做了一些我认为最好的改动,例如,我将Laravel/Lumen会话与Alexa AlexaSkillsKit特定的会话数据解耦,并创建了一个单一代理接口,以便通过单个外观处理大多数Alexa交互。但这主要与重构相关——还有很多新功能,其中最重要的是支持亚马逊的AlexaSkillsKit安全相关要求。

主要功能

  1. 允许Laravel/Lumen风格的意图、启动和会话结束请求的路由。
  2. 处理亚马逊提出的所有安全要求,包括证书/签名验证、时间戳验证等。
  3. 通过熟悉的Laravel风格接口提供对Alexa AlexaSkillsKit会话数据的访问。
  4. 将响应填充为Laravel会话数据,以在Lumen和Alexa之间保持1:1的会话数据集。
  5. 提供易于返回Alexa友好响应的类,包括SpeechCardRe-prompt响应。
  6. 可选地提供一种简单检索连接的Echo设备信息的方法($device = Alexa::device();)。

快速示例

AlexaRoute::intent('/alexa-end-point', 'GetAntiJoke', function(){
    Alexa::say("Why was the little boy crying? Because he had a frog stapled to his face!");
});

演示

我很快将录制一系列新的教程视频。

安装

先决条件

AlexaApp所需的是基于5.2版本的Laravel或Lumen框架。

通过composer安装后(即composer require develpr/alexa-app

1: 自动加载框架相应的服务提供者

需要将Develpr\AlexaApp\Provider\LaravelServiceProvider添加到自动加载的服务提供者数组中

Laravel

config/app.php配置文件中添加

'providers' => [
    ...snip...
    \Develpr\AlexaApp\Provider\LaravelServiceProvider::class,
    ...snip...
],

Lumen

在应用的bootstrap/app.php文件中添加

$app->register(\Develpr\AlexaApp\Provider\LumenServiceProvider::class);

2: 添加AlexaAlexaRoute的外观/别名(可选)

这并非必需,但可能会非常方便。如果您愿意,可以注入\Develpr\AlexaApp\Alexa\Develpr\AlexaApp\Routing\AlexaRouter类的一个实例,或者使用$app['alexa']或$app['alexa.router']分别获取它们。

Laravel

如果您想使用外观/别名,您需要在config/app.php文件中添加两个单独的别名配置。

    'aliases' => [
        ...
        'AlexaRoute' => \Develpr\AlexaApp\Facades\AlexaRouter::class,
        'Alexa' => \Develpr\AlexaApp\Facades\Alexa::class,
        ...
    ],

Lumen

实际上,我不太确定在Lumen中添加别名/外观是否有“官方”的方法,我通常不使用Lumen的自定义外观,然而,如如这个StackExchange帖子中提到的,这应该可行

首先确保在您的bootstrap/app.php文件中启用别名/外观,通过取消注释$app->withFacades();,然后添加以下内容

class_alias(\Develpr\AlexaApp\Facades\AlexaRouter::class, 'AlexaRoute');
class_alias(\Develpr\AlexaApp\Facades\Alexa::class, 'Alexa');

对于Lumen,可能更容易简单地使用$app['alexa.router']或将上述类之一的一个实例注入到您的类中。

3: 注册证书中间件以验证请求是否来自亚马逊/AlexaSkillsKit(可选)

对于任何生产应用,保护应用非常重要,亚马逊甚至要求按照其文档中描述的方式进行保护(请参阅相关文档)。然而,您不需要注册此中间件,在特定测试中也可以选择不进行注册。

此包通过提供满足亚马逊提供的所有必需安全参数的中间件来简化这一过程。目前,如果您想启用此功能,您需要根据Laravel/Lumen文档中的说明注册Certificate中间件。

如果您想保护应用中的所有路由,您可以简单地添加Certificate中间件到全局中间件中,如下所示;否则,您可以选择保护某些端点(例如,仅在/alexa-api-endpoint处运行证书/安全检查)。

Laravel

要保护所有路由,在您的app/Http/Kernal.php文件中

protected $middleware = [
    ...
    \Develpr\AlexaApp\Http\Middleware\Certificate::class,
    ...
];

Lumen

要保护所有路由,在您的bootstrap/app.php文件中

$app->middleware([
    ...snip...
    \Develpr\AlexaApp\Http\Middleware\Certificate::class,
    ...snip...
]);

一切安装完成

此时,一切应该“正常工作”(有关更多信息,请参阅下文的使用说明),但可能需要配置一些元素。

#配置

根据您的应用,许多东西都可以修改,或者甚至可能需要修改,最重要的是,安全选项需要设置以匹配您的AppId等。如果使用Laravel或Lumen,大多数或所有这些修改的方式相同,并且所有配置值都应该在config/文件中定义,或者使用.env文件。

如果您使用Laravel,可以使用控制台Artisan命令将AlexaApp配置文件发布到应用的配置目录,使用artisan vendor:publish,或者如果您更喜欢(或正在使用Lumen),可以手动从vendor/develpr/alexa-app/config/alexa.php复制此文件。

alexa.php配置文件中有许多注释,请仔细阅读以获取更多有关特定选项的信息! - 我在这里只涵盖更重要的、更广泛的选项。

证书/安全

为了使AlexaApp能够成功验证请求是有效的/来自亚马逊/AlexaSkillsKit,需要设置一些简单的配置选项。

亚马逊 / AlexaSkillsKit "应用ID"(applicationId)

这是您的AlexaSkillsKit应用ID,用于验证请求是否为您的应用。如果您不确定您的应用ID是什么,最简单的方法(对我来说至少)是查看从您的应用发送到您的Web服务器的示例请求。JSON体的一部分将包括..."application":{"applicationId":"amzn1.echo-sdk-ams.app.9ec3744a-d1b2-48f2-8e08-3b2045c00616"},... - 您想要在配置中输入的applicationId就是此applicationId

可以使用.env文件中的ALEXA__APPLICATION_IDS密钥设置applicationIds配置值,或者直接在配置文件中设置。请注意,配置文件接受一个数组形式的applicationIds,以防您计划从单个Laravel/Lumen应用中提供多个应用。.env文件方法仅允许指定单个applicationId。

请求时间戳容差

截至本文撰写时,亚马逊指定请求不应超过150秒,以防止重放攻击。这是默认设置,但如果您想更改此设置,可以在这里进行更改。请注意,如果您将此值设置为0,则不会检查请求年龄 - 这对于测试如果您有一个希望持续测试的示例请求很有用。

可以通过配置文件('timestampTolerance')或通过在.env文件中设置ALEXA_TIMESTAMP_TOLERANCE来修改此配置。

证书提供者

默认情况下,AlexaApp将使用文件存储来本地缓存亚马逊的远程证书文件。很快还将支持其他提供者,包括redis、数据库和eloquent。这些相关选项可以在配置文件中查看/配置。

Alexa设备

如果您想使用设备功能(例如Alexa::device()),您可能需要配置多个选项。

基本上,您需要告诉Alexa应用您在哪里持久化以及如何访问设备信息 - 目前提供了两个提供者,分别是eloquentdatabase。如果您使用eloquent提供者,请确保如果使用Lumen,eloquent已经被启用。

设备提供者

目前仅支持databaseeloquent选项,但通过实现\Develpr\AlexaApp\Contracts\DeviceProvider接口,可以轻松支持更多提供者。

默认设备提供者是Eloquent,/vendor/develpr/alexa-app/Device/Device.php中有一个示例设备,可以复制到您的app目录并修改以满足您的需求。这个模型可以视为类似于Laravel基本安装中提供的User模型。

示例迁移

AlexaApp提供了一个示例迁移,可以复制到您的迁移文件夹(手动或使用Laravel的控制台命令php artisan vendor:publish --tag="migrations"),迁移后,将“开箱即用”与包含的DeviceProvider一起使用。如果您不想使用此迁移也完全可以,但您需要确保查看配置文件,以确保您修改/理解您可能需要更新的任何选项,以便与您的存储模式兼容。

使用

在以下章节中,您可以了解如何使用此包。**请注意,虽然以下示例中可能使用了外观/别名,但您绝对不必这样做!**如果您想了解更多关于安装部分的外观/别名的信息,请参阅安装部分 -> 外观/别名

路由

来自亚马逊AlexaApp中间件到您的应用程序将有三种类型的请求。这些是

  1. LaunchRequest(当您的应用程序“打开”时发生)
  2. SessionEndedRequest(当应用程序关闭时发送到您的应用程序)
  3. IntentRequest(这些是所有不属于上述类型的请求 - 很可能是您的应用程序的“主食” - 最有意义的交互)

这三种类型的请求可以在您的应用程序中使用新功能路由,就像正常的LaravelLumen请求一样!所有这些示例都可能在您的app/Http/routes.php文件中。

LaunchRequest

AlexaRoute::launch('/your-app-uri', 'App\Http\Controllers\AnyController@anyMethod');

或者

$app['alexa.router']->launch('/your-app-uri', 'App\Http\Controllers\AnyController@anyMethod');

SessionEndedRequest

AlexaRoute::sessionEnded('/your-app-uri', function() use ($app) {
    return '{"version":"1.0","response":{"shouldEndSession":true}}';
});

或者

$app['alexa.router']->sessionEnded('/your-app-uri', function() use ($app) {
    return '{"version":"1.0","response":{"shouldEndSession":true}}';
});

IntentRequest

AlexaRoute::intent('/your-app-uri', 'GetZodiacHoroscopeIntent', 'App\Http\Controllers\AnyController@anyMethod');

或者

$app['alexa.router']->intent('/your-app-uri', 'GetZodiacHoroscopeIntent', 'App\Http\Controllers\AnyController@anyMethod');

请注意,在以下示例中,同时使用了闭包和控制器来处理请求,但没有具体要求根据请求类型使用其中之一。

请注意,其他getpostputpatchdelete等选项仍然可用且未更改。

会话

会话值通过亚马逊/AlexaSkillsKit的JSON有效载荷传递到您的应用程序。这些值可以通过AlexaRequest或使用Alexa外观/别名访问。

检索会话值

$previousChoice = Alexa::session('previousChoice');

检索所有会话值

Alexa::session();

设置会话值

Alexa::session('previousChoice', "Pizza");

或者

Alexa::setSession('previousChoice', "Pizza");

取消设置会话值

Alexa::unsetSession('previousChoice');

会话值也将包含在响应json中,但只有在你使用AlexaResponse类时才如此!

你可以检索槽的值(目前仅适用于IntentRequests)

$usersChoice = Alexa::slot('choice');

如果槽为空,将返回null。你可以通过传入你想要的默认值作为第二个参数来更改此默认值

$usersChoice = Alexa::slot('choice', 'foo');

响应

你可以使用这个包和Alexa外观轻松地从你的应用程序创建有效的响应,但了解外观背后的类也很重要。最重要的是要知道Alexa::say("Hello");只是返回一个包含\Develpr\AlexaApp\Response\Speech对象的新的\Develpr\AlexaApp\Response\AlexaResponse对象。

使用Alexa外观/别名

向Amazon/AlexaSkillsKit/最终用户发送有效响应的最简单方法是

return Alexa::say("Oh hi Denny");

如上所述,最终将生成并返回一个AlexaResponse,因此你可以链式调用其他方法以添加其他响应功能。例如...

return Alexa::say("Oh hi Denny")->withCard(new Card("Hello message"))->endSession();

...将返回一个语音消息("Oh hi Denny"),一个标题为"Hello message"的卡片,并且将结束会话。

AlexaResponse

有一些有用的类可以用来自生成适合Amazon Echo的json响应。这些类并没有什么特别复杂或神奇的地方,它们只是让创建有效的响应变得更加容易,而无需过多思考。

主要类是AlexaResponse - 我的意思是Echo总是返回这个类的实例。你可以做很多有用的事情。

你可以返回这个类的实例而不做任何其他事情,这将是一个有效的响应(尽管用处不大!)

return new AlexaResponse;

你可以告诉Echo会话应该结束

$alexaResponse = new AlexaResponse;
$alexaResponse->endSession();

return $alexaResponse;

或者,你可以添加一个(或两个)Speech/Card/Reprompt对象,以便向最终Echo用户发送语音文本或卡片(请注意,你不需要返回两者!)。

$alexaResponse = new AlexaResponse;
$alexaResponse->withSpeech(new Speech("Hello!!"));

$alexaResponse->withCard(new Card("Hello Title", "Hello Subtitle", "Hello content here!"));

return $alexaResponse;

你总是可以一行返回这个

return new AlexaResponse(new Speech("Hello!!"), new Card("Hello Title", "Hello Subtitle", "Hello content here!"), true);

这里,当第三个参数设置为true时,将结束会话。

测试

 $ phpunit --configuration phpunit.xml

谢谢

感谢 @jasonlewis - 我在他的ding/api包中使用了大量关于路由部件的想法。

感谢大家查看这个项目。我猜在接下来的几周/几个月/一年中,Amazon Echo开发者社区、开发者API等许多东西都会迅速变化,我会尽我所能跟上这些变化,并一定会查看和感谢任何拉取请求、功能请求等。

##//todo

我认为这个目前还处于beta版本。我毫不怀疑,随着我继续真正测试这个项目,会有一些错误出现,我非常感谢任何反馈、错误报告、功能请求或评论。还有一些方面,我还不确定我是否会做一点点改变(例如,如果你使用Alexa::外观,它做了很多不同的事情,我认为明智的做法可能是将一些功能分割开来)。

  1. 找到一种方法,不需要替换默认的Application
  2. 在不要求用户返回AlexaResponse实例的情况下将会话添加到响应中
  3. 测试!!!
  4. 为基于userIds验证Echo设备/用户添加某种简单的认证选项
  5. 找出验证请求是否来自亚马逊的最佳方法 - 不确定这是否可能,或者将来是否可能,但希望很快就能实现
  6. 添加解析来自Alexa的语音的基本助手 - 并非完全“完成”,但我添加了一些选项来帮助。我非常感兴趣地想听听您的意见,看看如何才能使其更有帮助!