alexa_web_service 0.0.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +41 -0
- data/README.md +235 -0
- data/Rakefile +2 -0
- data/alexa_web_service.gemspec +39 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/alexa_web_service.rb +7 -4
- data/lib/alexa_web_service/card.rb +54 -0
- data/lib/alexa_web_service/display_directive.rb +73 -0
- data/lib/alexa_web_service/hint_directive.rb +15 -0
- data/lib/alexa_web_service/progressive_response.rb +39 -0
- data/lib/alexa_web_service/request.rb +80 -0
- data/lib/alexa_web_service/response.rb +52 -0
- data/lib/alexa_web_service/{alexa_verify.rb → verify.rb} +1 -1
- data/lib/alexa_web_service/version.rb +1 -1
- metadata +42 -16
- data/lib/alexa_web_service/alexa_request.rb +0 -54
- data/lib/alexa_web_service/alexa_response.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e77c95bba2bc9c56617f050c1579f572e76450c
|
4
|
+
data.tar.gz: 04ed856e3976cc87dcc15957bc27a4101c032fe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6d032a7f83d9a97043165a8601cac3781280d22b37822a816597f19ef9b5136dfebfc7a7bebc1aad7a6447c0b50d488eed899bee629058e78f7e064f0420d4f
|
7
|
+
data.tar.gz: 460410d8ae38d6b3eddfdf90f9c58c64f6045f1344f8cbad40a93b7914a180349808b21484f9a414e437f9ad60dbcad8f11459e429c11dbb99adc58d3224434e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
alexa_web_service (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
httparty (0.15.7)
|
11
|
+
multi_xml (>= 0.5.2)
|
12
|
+
json (2.1.0)
|
13
|
+
multi_xml (0.6.0)
|
14
|
+
rake (10.5.0)
|
15
|
+
rspec (3.5.0)
|
16
|
+
rspec-core (~> 3.5.0)
|
17
|
+
rspec-expectations (~> 3.5.0)
|
18
|
+
rspec-mocks (~> 3.5.0)
|
19
|
+
rspec-core (3.5.2)
|
20
|
+
rspec-support (~> 3.5.0)
|
21
|
+
rspec-expectations (3.5.0)
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
+
rspec-support (~> 3.5.0)
|
24
|
+
rspec-mocks (3.5.0)
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
+
rspec-support (~> 3.5.0)
|
27
|
+
rspec-support (3.5.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
alexa_web_service!
|
34
|
+
bundler (~> 1.15)
|
35
|
+
httparty (~> 0.15.7)
|
36
|
+
json (~> 2.1, >= 2.1.0)
|
37
|
+
rake (~> 10.0)
|
38
|
+
rspec (~> 3.0)
|
39
|
+
|
40
|
+
BUNDLED WITH
|
41
|
+
1.16.1
|
data/README.md
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
# AlexaWebService
|
2
|
+
|
3
|
+
Framework for building an Alexa skill.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'alexa_web_service'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install alexa_web_service
|
20
|
+
|
21
|
+
## Usage ##
|
22
|
+
|
23
|
+
The Alexa Web Service gem handles the JSON requests and responses that constitute an Alexa "Skill."
|
24
|
+
For general information on creating an Alexa skill as a web service, look here:
|
25
|
+
https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interface-reference#Introduction
|
26
|
+
|
27
|
+
Alexa will send your web service JSON in an HTTP POST request, like so:
|
28
|
+
|
29
|
+
````Ruby
|
30
|
+
{
|
31
|
+
"session": {
|
32
|
+
"sessionId": "SessionId.abc12d34-12ab-1abc-111-a12c3456d7ef9",
|
33
|
+
"application": {
|
34
|
+
"applicationId": "amzn1.echo-sdk-ams.app.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
35
|
+
},
|
36
|
+
"attributes": {},
|
37
|
+
"user": {
|
38
|
+
"userId": "amzn1.account.AHHLP1234ABC5DEFG6HIJK7XLMN"
|
39
|
+
},
|
40
|
+
"new": true
|
41
|
+
},
|
42
|
+
"request": {
|
43
|
+
"type": "LaunchRequest",
|
44
|
+
"requestId": "EdwRequestId.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
45
|
+
"timestamp": "2016-06-17T21:07:57Z",
|
46
|
+
"locale": "en-US"
|
47
|
+
},
|
48
|
+
"version": "1.0"
|
49
|
+
}
|
50
|
+
````
|
51
|
+
|
52
|
+
#### Verify: Verify the Alexa request ####
|
53
|
+
|
54
|
+
The Alexa Web Service framework will automatically verify that the request comes from Amazon, as outlined [here](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service)
|
55
|
+
|
56
|
+
The AlexaVerify class takes two parameters: the request body sent by the client, and the raw environment hash.
|
57
|
+
So if you're setting up a Sinatra server, you can verify the request like so:
|
58
|
+
|
59
|
+
````Ruby
|
60
|
+
# If the request body has been read, like in the Eight Ball example,
|
61
|
+
# you need to rewind it.
|
62
|
+
request.body.rewind
|
63
|
+
|
64
|
+
# Verify the request.
|
65
|
+
verified = AlexaWebService::Verify.new(request.env, request.body.read).verify_request
|
66
|
+
halt 400, "#{verified}" unless verified == "OK"
|
67
|
+
````
|
68
|
+
|
69
|
+
#### Request: Handling the request from Alexa ####
|
70
|
+
|
71
|
+
Create an instance of the AlexaRequest class to provide some convenience methods for handling the JSON request:
|
72
|
+
|
73
|
+
````Ruby
|
74
|
+
@echo_request = AlexaWebService::Request.new(request_json)
|
75
|
+
|
76
|
+
@echo_request.intent_name
|
77
|
+
@echo_request.slots
|
78
|
+
@echo_request.slots.myslot
|
79
|
+
@echo_request.launch_request?
|
80
|
+
@echo_request.intent_request?
|
81
|
+
@echo_request.session_ended_request?
|
82
|
+
@echo_request.session_new?
|
83
|
+
````
|
84
|
+
|
85
|
+
Your skill provides different responses (see next section) depending on the the type/name/slot values of the request:
|
86
|
+
|
87
|
+
````Ruby
|
88
|
+
if @echo_request.launch_request
|
89
|
+
# have alexa say hello
|
90
|
+
elsif @echo_request.intent_name == "InformationRequest"
|
91
|
+
# ask use for what kind of information she wants
|
92
|
+
elsif @echo_request.slots.time
|
93
|
+
# tell user the time
|
94
|
+
end
|
95
|
+
````
|
96
|
+
|
97
|
+
(Take a look at eight_ball.rb for some further examples.)
|
98
|
+
|
99
|
+
|
100
|
+
#### Response: Respond to Alexa requests ####
|
101
|
+
|
102
|
+
The AlexaResponse class generates the proper JSON to make Alexa responses.
|
103
|
+
|
104
|
+
Create a new response object:
|
105
|
+
|
106
|
+
````response = AlexaWebService::Response.new````
|
107
|
+
|
108
|
+
Then, define the response attributes:
|
109
|
+
|
110
|
+
````Ruby
|
111
|
+
response.end_session = true
|
112
|
+
response.spoken_response = "This is what Alexa will say to you"
|
113
|
+
response.reprompt_text = "This is what she'll say if she doesn't hear your response the first time."
|
114
|
+
````
|
115
|
+
|
116
|
+
You can then send the response back to Alexa with the following command:
|
117
|
+
|
118
|
+
|
119
|
+
````Ruby
|
120
|
+
response.without_card.to_json
|
121
|
+
````
|
122
|
+
|
123
|
+
So, putting the AlexaRequest and AlexaResponse together:
|
124
|
+
|
125
|
+
````Ruby
|
126
|
+
response = AlexaWebService::Response.new
|
127
|
+
|
128
|
+
if @echo_request.launch_request
|
129
|
+
response.spoken_response = "Hello user"
|
130
|
+
response.end_session = true
|
131
|
+
end
|
132
|
+
|
133
|
+
response.without_card.to_json
|
134
|
+
````
|
135
|
+
|
136
|
+
You can use [SSML](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference):
|
137
|
+
|
138
|
+
````Ruby
|
139
|
+
response.speech_type = "SSML"
|
140
|
+
response.text_type = "ssml"
|
141
|
+
response.spoken_response = "<speak>Here is a word spelled out: <say-as interpret-as="spell-out">hello</say-as></speak>"
|
142
|
+
````
|
143
|
+
|
144
|
+
Alexa uses a "session attribute" to persist data within a session. (It's the empty "attributes" hash in the sample JSON request above.) The #add_attribute adds key:value pairs to that attributes hash.
|
145
|
+
|
146
|
+
````Ruby
|
147
|
+
response.add_attribute("favorite_color", "blue" )
|
148
|
+
````
|
149
|
+
### Adding a Directive ###
|
150
|
+
AlexaWebService supports directives: [hint and display directives](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html) (for Alexa devices with screens), and [progresssive response](https://developer.amazon.com/docs/custom-skills/send-the-user-a-progressive-response.html). They work a little differently.
|
151
|
+
|
152
|
+
*Display Directive*
|
153
|
+
Please see the Amazon docs. Display Directives are a bit involved.
|
154
|
+
First, create the directive:
|
155
|
+
````ruby
|
156
|
+
display = AlexaWebService::DisplayDirective.new(
|
157
|
+
type: "Body Template Type", token: "Your Token", title: "Your Title"
|
158
|
+
)
|
159
|
+
display.add_text(primary_text: "first text", secondary_text: "second_text", tertiary_text: "third text")
|
160
|
+
display.add_background_image("title", url)
|
161
|
+
display.add__image("title", url)
|
162
|
+
````
|
163
|
+
then, add it to your response:
|
164
|
+
````
|
165
|
+
response.add_directive(display.directive)
|
166
|
+
````
|
167
|
+
*Hint Directive*
|
168
|
+
````
|
169
|
+
hint = AlexaWebService::HintDirective.new("Buy low, sell high!")
|
170
|
+
response.add_directive(hint.directive)
|
171
|
+
````
|
172
|
+
*Progressive Response*
|
173
|
+
These are a bit different than other directives. They are not sent with your response, but are sent before. They take two parameters, the entire request object, and the text you'd like Alexa to speak.
|
174
|
+
````
|
175
|
+
progressive_response = AlexaWebService::ProgressiveResponse.new(request, "Hang on, looking up information.")
|
176
|
+
````
|
177
|
+
Then send it....
|
178
|
+
````
|
179
|
+
progressive_response.post
|
180
|
+
````
|
181
|
+
|
182
|
+
|
183
|
+
### Sending a Card ###
|
184
|
+
|
185
|
+
You can also [send a card](https://developer.amazon.com/docs/custom-skills/include-a-card-in-your-skills-response.html) to the Alexa app:
|
186
|
+
AlexaWebService supports four kinds of cards:
|
187
|
+
- Plain Text
|
188
|
+
- Image
|
189
|
+
- Linking
|
190
|
+
- Permissions
|
191
|
+
|
192
|
+
First, create a card:
|
193
|
+
````
|
194
|
+
card = AlexaWebService::Card.new
|
195
|
+
````
|
196
|
+
|
197
|
+
Then add the card attributes you want (text, image, or permissions if sending a permissions card):
|
198
|
+
````Ruby
|
199
|
+
card.title = "My Alexa Card"
|
200
|
+
card.content "Formating is really limited to: \nline breaks"
|
201
|
+
card.small_image = "https://your_small_image_url.jpg"
|
202
|
+
card.large_image = "https://your_large_image_url.jpg"
|
203
|
+
````
|
204
|
+
Finally, add the card to your response, specifying the kind of card you created:
|
205
|
+
````
|
206
|
+
response.add_card(card.with_image)
|
207
|
+
````
|
208
|
+
other possibilities:
|
209
|
+
````
|
210
|
+
# text only
|
211
|
+
response.add_card(card.with_text)
|
212
|
+
|
213
|
+
# permissions
|
214
|
+
response.add_card(card.with_permissions)
|
215
|
+
````
|
216
|
+
If you create a skill that uses [account linking](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/linking-an-alexa-user-with-a-user-in-your-system)
|
217
|
+
````
|
218
|
+
# linking
|
219
|
+
response.add_card(card.linking)
|
220
|
+
````
|
221
|
+
Finally, post your response
|
222
|
+
|
223
|
+
````
|
224
|
+
response.post
|
225
|
+
````
|
226
|
+
|
227
|
+
|
228
|
+
## Contributing
|
229
|
+
|
230
|
+
1. Fork it ( https://github.com/[my-github-username]/alexa_web_service/fork )
|
231
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
232
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
233
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
234
|
+
5. Create a new Pull Request
|
235
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "alexa_web_service/version"
|
5
|
+
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "alexa_web_service"
|
9
|
+
spec.version = AlexaWebService::VERSION
|
10
|
+
spec.authors = ["sarkonovich"]
|
11
|
+
spec.email = ["arkonovs @ reed. edu"]
|
12
|
+
|
13
|
+
spec.summary = "A framework to handle the JSON request/responses for an Alexa skill"
|
14
|
+
spec.description = "A framework to handle and verify the JSON request/responses for an Alexa skill"
|
15
|
+
spec.homepage = "https://github.com/sarkonovich/Alexa-Web-Service"
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
# if spec.respond_to?(:metadata)
|
21
|
+
# spec.metadata["allowed_push_host"] = "http://mygemserver.com"
|
22
|
+
# else
|
23
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
# "public gem pushes."
|
25
|
+
# end
|
26
|
+
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
28
|
+
f.match(%r{^(test|spec|features)/})
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
37
|
+
spec.add_development_dependency "httparty", "~> 0.15.7"
|
38
|
+
spec.add_development_dependency "json", '~> 2.1', '>= 2.1.0'
|
39
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "alexa_web_service"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/alexa_web_service.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require_relative 'alexa_web_service/display_directive'
|
2
|
+
require_relative 'alexa_web_service/hint_directive'
|
3
|
+
require_relative 'alexa_web_service/progressive_response'
|
4
|
+
require_relative 'alexa_web_service/request'
|
5
|
+
require_relative 'alexa_web_service/response'
|
6
|
+
require_relative 'alexa_web_service/verify'
|
7
|
+
require_relative 'alexa_web_service/card'
|
5
8
|
|
6
9
|
module AlexaWebService
|
7
10
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module AlexaWebService
|
2
|
+
class Card
|
3
|
+
attr_accessor :title, :content, :small_image, :large_image, :permissions
|
4
|
+
|
5
|
+
def initialize(type = 'text_card')
|
6
|
+
@title = ''
|
7
|
+
@content = ''
|
8
|
+
@small_image = nil
|
9
|
+
@large_image = nil
|
10
|
+
@type = type
|
11
|
+
@permissions = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_image
|
15
|
+
{
|
16
|
+
"type": "Standard",
|
17
|
+
"title": title,
|
18
|
+
"text": content,
|
19
|
+
"image": {
|
20
|
+
"smallImageUrl": small_image,
|
21
|
+
"largeImageUrl": large_image
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_text
|
27
|
+
{
|
28
|
+
"type": "Simple",
|
29
|
+
"title": title,
|
30
|
+
"content": content
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def linking
|
35
|
+
{"type": "LinkAccount"}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Will only return permissions requested in skill configuration
|
39
|
+
# Possible values for permissions:
|
40
|
+
# write for notifications: "write::alexa:devices:all:notifications:standard"
|
41
|
+
# read for full address: "read::alexa:device:all:address"
|
42
|
+
# read for restricted address: "read::alexa:device:all:address:country_and_postal_code"
|
43
|
+
|
44
|
+
def add_permission(permission)
|
45
|
+
self.permissions << permission
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_permission
|
49
|
+
{
|
50
|
+
"type": "AskForPermissionsConsent", "permissions": permissions
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module AlexaWebService
|
3
|
+
class DisplayDirective
|
4
|
+
attr_accessor :directive
|
5
|
+
|
6
|
+
def initialize(type: "BodyTemplate1", token: "", back_button: "VISIBLE", title: "")
|
7
|
+
@directive = {
|
8
|
+
"type": "Display.RenderTemplate",
|
9
|
+
"template": {
|
10
|
+
"type": type,
|
11
|
+
"token": token,
|
12
|
+
"backButton": back_button,
|
13
|
+
"backgroundImage": image_object,
|
14
|
+
"title": title,
|
15
|
+
"image": image_object,
|
16
|
+
"textContent": {}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def image_object(content_description = '', url = '', size = 'X_SMALL')
|
22
|
+
{
|
23
|
+
"contentDescription": content_description,
|
24
|
+
"sources": [
|
25
|
+
{
|
26
|
+
"url": url,
|
27
|
+
"size": size
|
28
|
+
}
|
29
|
+
]
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_image(content_description, url, size = 'X_SMALL')
|
34
|
+
@directive[:template][:image] = image_object(content_description, url, size = 'X_SMALL')
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_background_image(content_description, url, size = 'X_SMALL')
|
38
|
+
@directive[:template][:backgroundImage] = image_object(content_description, url, size = 'X_SMALL')
|
39
|
+
end
|
40
|
+
|
41
|
+
def text_object(primary_text: nil, secondary_text: nil, tertiary_text: nil)
|
42
|
+
{
|
43
|
+
"primaryText": {
|
44
|
+
"text": primary_text,
|
45
|
+
"type": "RichText"
|
46
|
+
},
|
47
|
+
"secondaryText": {
|
48
|
+
"text": secondary_text,
|
49
|
+
"type": "RichText"
|
50
|
+
},
|
51
|
+
"tertiaryText": {
|
52
|
+
"text": tertiary_text,
|
53
|
+
"type": "RichText"
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_text(primary_text:, secondary_text: nil, tertiary_text: nil)
|
59
|
+
@directive[:template][:textContent] = text_object(primary_text: primary_text, secondary_text: secondary_text, tertiary_text: tertiary_text)
|
60
|
+
end
|
61
|
+
|
62
|
+
# To create Show lists, first create image and text objects and then add show list item.
|
63
|
+
def add_list_item(token: '', image_object: {}, text_object: {})
|
64
|
+
item = {
|
65
|
+
"token": token,
|
66
|
+
"image": image_object,
|
67
|
+
"textContent": text_object
|
68
|
+
}
|
69
|
+
@directive[:template][:listItems] ||= []
|
70
|
+
@directive[:template][:listItems] << item
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module AlexaWebService
|
4
|
+
class ProgressiveResponse
|
5
|
+
attr_accessor :speech
|
6
|
+
attr_reader :directive, :request
|
7
|
+
|
8
|
+
def initialize(request, speech)
|
9
|
+
@request = request
|
10
|
+
@speech = speech
|
11
|
+
@directive = {
|
12
|
+
"header"=> {
|
13
|
+
"requestId"=>request.request_id
|
14
|
+
},
|
15
|
+
"directive"=> {
|
16
|
+
"type"=>"VoicePlayer.Speak",
|
17
|
+
"speech"=>speech
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
if request.api_endpoint
|
24
|
+
request.api_endpoint + "/v1/directives"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
{
|
30
|
+
"Authorization" => "Bearer #{request.api_access_token}",
|
31
|
+
"Content-Type" => "application/json"
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def post
|
36
|
+
HTTParty.post(url, :headers=>headers, :body=>self.directive.to_json)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module AlexaWebService
|
2
|
+
class Request
|
3
|
+
attr_reader :request_hash, :api_access_token, :api_endpoint, :device_id,
|
4
|
+
:request_id, :request_type, :timestamp, :session_new, :user_id,
|
5
|
+
:access_token, :application_id, :intent_name, :slots
|
6
|
+
|
7
|
+
attr_accessor :attributes
|
8
|
+
|
9
|
+
def initialize(request_hash)
|
10
|
+
@request_hash = request_hash
|
11
|
+
@api_access_token = request_hash["context"]["System"]["apiAccessToken"]
|
12
|
+
@api_endpoint = request_hash["context"]["System"]["apiEndpoint"]
|
13
|
+
@device_id = request_hash["context"]["System"]["device"]["deviceId"]
|
14
|
+
@request_id = request_hash["request"]["requestId"]
|
15
|
+
@request_type = request_hash["request"]["type"]
|
16
|
+
@timestamp = request_hash["request"]["timestamp"]
|
17
|
+
@session_new = request_hash["session"]["new"]
|
18
|
+
@user_id = request_hash["session"]["user"]["userId"]
|
19
|
+
@access_token = request_hash["session"]["user"]["accessToken"]
|
20
|
+
@application_id = request_hash["session"]["application"]["applicationId"]
|
21
|
+
@intent_name = get_intent_name
|
22
|
+
@slots = get_slots
|
23
|
+
@attributes = get_attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def filled_slots
|
27
|
+
@slots.select { |slot| slot != nil} rescue []
|
28
|
+
end
|
29
|
+
|
30
|
+
def intent_request?
|
31
|
+
request_type == "IntentRequest"
|
32
|
+
end
|
33
|
+
|
34
|
+
def launch_request?
|
35
|
+
request_type == "LaunchRequest"
|
36
|
+
end
|
37
|
+
|
38
|
+
def slot_hash
|
39
|
+
if request_hash["request"]["intent"]
|
40
|
+
request_hash["request"]["intent"]["slots"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def session_ended_request?
|
45
|
+
request_type == "SessionEndedRequest"
|
46
|
+
end
|
47
|
+
|
48
|
+
def session_new?
|
49
|
+
session_new
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
private
|
54
|
+
def get_intent_name
|
55
|
+
if request_hash["request"]["intent"]
|
56
|
+
request_hash["request"]["intent"]["name"]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_slots
|
61
|
+
if request_hash["request"]["intent"]
|
62
|
+
build_struct(request_hash["request"]["intent"]["slots"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_attributes
|
67
|
+
request_hash["session"]["attributes"] ? request_hash["session"]["attributes"] : {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_struct(hash)
|
71
|
+
if hash.nil? || hash.empty?
|
72
|
+
nil
|
73
|
+
else
|
74
|
+
slot_names = hash.keys.map {|k| k.to_sym.downcase }
|
75
|
+
slot_values = hash.values.map { |v| v["value"] }
|
76
|
+
Struct.new(*slot_names).new(*slot_values)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module AlexaWebService
|
2
|
+
class Response
|
3
|
+
attr_accessor :session_attributes, :spoken_response, :card,
|
4
|
+
:reprompt_text, :end_session, :speech_type,
|
5
|
+
:text_type, :directives
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@session_attributes = {}
|
9
|
+
@speech_type = "PlainText"
|
10
|
+
@spoken_response = ''
|
11
|
+
@reprompt_text = ''
|
12
|
+
@text_type = "text"
|
13
|
+
@end_session = 'true'
|
14
|
+
@card = nil
|
15
|
+
@directives = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_attribute(key, value)
|
19
|
+
@session_attributes.merge!(key => value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_directive(directive)
|
23
|
+
self.directives << directive if $display_support == true
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_card(card)
|
27
|
+
self.card = card
|
28
|
+
end
|
29
|
+
|
30
|
+
def post
|
31
|
+
{
|
32
|
+
"version": "1.0",
|
33
|
+
"sessionAttributes": @session_attributes,
|
34
|
+
"response": {
|
35
|
+
"outputSpeech": {
|
36
|
+
"type": speech_type,
|
37
|
+
"#{text_type}": spoken_response
|
38
|
+
},
|
39
|
+
"card": card,
|
40
|
+
"reprompt": {
|
41
|
+
"outputSpeech": {
|
42
|
+
"type": speech_type,
|
43
|
+
"text": reprompt_text
|
44
|
+
}
|
45
|
+
},
|
46
|
+
"directives": @directives,
|
47
|
+
"shouldEndSession": end_session
|
48
|
+
}
|
49
|
+
}.to_json
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alexa_web_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sarkonovich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.15'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.15'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,40 +38,54 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: httparty
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
48
|
-
type: :
|
61
|
+
version: 0.15.7
|
62
|
+
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
68
|
+
version: 0.15.7
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: json
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '1
|
75
|
+
version: '2.1'
|
62
76
|
- - ">="
|
63
77
|
- !ruby/object:Gem::Version
|
64
|
-
version: 1.
|
65
|
-
type: :
|
78
|
+
version: 2.1.0
|
79
|
+
type: :development
|
66
80
|
prerelease: false
|
67
81
|
version_requirements: !ruby/object:Gem::Requirement
|
68
82
|
requirements:
|
69
83
|
- - "~>"
|
70
84
|
- !ruby/object:Gem::Version
|
71
|
-
version: '1
|
85
|
+
version: '2.1'
|
72
86
|
- - ">="
|
73
87
|
- !ruby/object:Gem::Version
|
74
|
-
version: 1.
|
88
|
+
version: 2.1.0
|
75
89
|
description: A framework to handle and verify the JSON request/responses for an Alexa
|
76
90
|
skill
|
77
91
|
email:
|
@@ -80,10 +94,22 @@ executables: []
|
|
80
94
|
extensions: []
|
81
95
|
extra_rdoc_files: []
|
82
96
|
files:
|
97
|
+
- ".gitignore"
|
98
|
+
- Gemfile
|
99
|
+
- Gemfile.lock
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- alexa_web_service.gemspec
|
103
|
+
- bin/console
|
104
|
+
- bin/setup
|
83
105
|
- lib/alexa_web_service.rb
|
84
|
-
- lib/alexa_web_service/
|
85
|
-
- lib/alexa_web_service/
|
86
|
-
- lib/alexa_web_service/
|
106
|
+
- lib/alexa_web_service/card.rb
|
107
|
+
- lib/alexa_web_service/display_directive.rb
|
108
|
+
- lib/alexa_web_service/hint_directive.rb
|
109
|
+
- lib/alexa_web_service/progressive_response.rb
|
110
|
+
- lib/alexa_web_service/request.rb
|
111
|
+
- lib/alexa_web_service/response.rb
|
112
|
+
- lib/alexa_web_service/verify.rb
|
87
113
|
- lib/alexa_web_service/version.rb
|
88
114
|
homepage: https://github.com/sarkonovich/Alexa-Web-Service
|
89
115
|
licenses:
|
@@ -97,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
123
|
requirements:
|
98
124
|
- - ">="
|
99
125
|
- !ruby/object:Gem::Version
|
100
|
-
version:
|
126
|
+
version: '0'
|
101
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
128
|
requirements:
|
103
129
|
- - ">="
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module AlexaWebService
|
2
|
-
class AlexaRequest
|
3
|
-
attr_reader :intent_name, :slots, :timestamp, :request_type, :session_new, :user_id, :access_token, :application_id
|
4
|
-
attr_accessor :attributes
|
5
|
-
alias :session_new? :session_new
|
6
|
-
|
7
|
-
def initialize(response_hash)
|
8
|
-
session = response_hash["session"]
|
9
|
-
request = response_hash["request"]
|
10
|
-
if session
|
11
|
-
@attributes = session["attributes"] ? session["attributes"] : {}
|
12
|
-
@user_id = session["user"]["userId"] if session["user"]
|
13
|
-
@access_token = session["user"]["accessToken"] if session["user"]
|
14
|
-
@application_id = session["application"]["applicationId"] if session["application"]
|
15
|
-
end
|
16
|
-
|
17
|
-
if request
|
18
|
-
@request_type = request["type"]
|
19
|
-
@timestamp = request["timestamp"]
|
20
|
-
@session_new = request["new"]
|
21
|
-
|
22
|
-
if request["intent"]
|
23
|
-
@intent_name = request["intent"]["name"] if request["intent"]
|
24
|
-
@slots = build_struct(request["intent"]["slots"]) if request["intent"]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def filled_slots
|
30
|
-
@slots.select { |slot| slot != nil} rescue []
|
31
|
-
end
|
32
|
-
|
33
|
-
def intent_request?
|
34
|
-
request_type == "IntentRequest"
|
35
|
-
end
|
36
|
-
|
37
|
-
def launch_request?
|
38
|
-
request_type == "LaunchRequest"
|
39
|
-
end
|
40
|
-
|
41
|
-
def session_ended_request?
|
42
|
-
request_type == "SessionEndedRequest"
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
def build_struct(hash)
|
47
|
-
unless hash.nil?
|
48
|
-
slot_names = hash.keys.map {|k| k.to_sym.downcase }
|
49
|
-
slot_values = hash.values.map { |v| v["value"] }
|
50
|
-
Struct.new(*slot_names).new(*slot_values)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module AlexaWebService
|
2
|
-
class Response
|
3
|
-
attr_accessor :session_attributes, :spoken_response, :card_title, :card_content, :reprompt_text, :end_session, :speech_type, :text_type
|
4
|
-
def initialize(params={})
|
5
|
-
@session_attributes = params[:session_attributes] || {}
|
6
|
-
@speech_type = params[:speech_type] || "PlainText"
|
7
|
-
@spoken_response = params[:spoken_response] || nil
|
8
|
-
@card_title = params[:card_title] || nil
|
9
|
-
@card_content = params[:card_content] || nil
|
10
|
-
@reprompt_text = params[:reprompt_text] || nil
|
11
|
-
@text_type = params[:text_type] || "text"
|
12
|
-
@end_session = params[:end_session] || true
|
13
|
-
end
|
14
|
-
|
15
|
-
def add_attribute(key, value)
|
16
|
-
@session_attributes.merge!(key => value)
|
17
|
-
end
|
18
|
-
|
19
|
-
def append_attribute(key, value)
|
20
|
-
@session_attributes[key] << value if @session_attributes[key] != nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def with_card
|
24
|
-
{
|
25
|
-
"version" => "1.0",
|
26
|
-
"sessionAttributes" =>
|
27
|
-
@session_attributes,
|
28
|
-
"response" => {
|
29
|
-
"outputSpeech" => {
|
30
|
-
"type" => speech_type,
|
31
|
-
"#{text_type}" => spoken_response
|
32
|
-
},
|
33
|
-
"card" => {
|
34
|
-
"type" => "Simple",
|
35
|
-
"title" => card_title,
|
36
|
-
"content" => card_content
|
37
|
-
},
|
38
|
-
"reprompt" => {
|
39
|
-
"outputSpeech" => {
|
40
|
-
"type" => speech_type,
|
41
|
-
"text" => reprompt_text
|
42
|
-
}
|
43
|
-
},
|
44
|
-
"shouldEndSession" => end_session
|
45
|
-
}
|
46
|
-
}
|
47
|
-
end
|
48
|
-
|
49
|
-
def link_card
|
50
|
-
self.with_card.tap { |hs| hs["response"]["card"] = {"type" => "LinkAccount"} }
|
51
|
-
end
|
52
|
-
|
53
|
-
def without_card
|
54
|
-
self.with_card.tap { |hs| hs["response"].delete("card") }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|