google_assistant 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +86 -3
- data/lib/google_assistant/argument.rb +24 -0
- data/lib/google_assistant/assistant.rb +66 -15
- data/lib/google_assistant/device.rb +30 -0
- data/lib/google_assistant/intent.rb +4 -0
- data/lib/google_assistant/permission.rb +16 -0
- data/lib/google_assistant/user.rb +22 -0
- data/lib/google_assistant/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53efc649d01191b0cfdabc34974a52e124fa8b8d
|
4
|
+
data.tar.gz: 8668eb3b467923e6db3d1667e7ef1c0bfde46c22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf5090c145e291632812c652f89f6fb729172def0fb3449413bbff1c262dacc0e6401a7999289a8109931521630113597ace1bc9bc7a26d24eebff8a19c172fb
|
7
|
+
data.tar.gz: 5faf5231646608ba776b52810d9a29ccae204c418ed04821c571f5b6407aa8846db0c3ad4a1e072ae180c8958bacedd72ff4392b63929295a6c640d7993678c7
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ class GoogleAssistantController < ApplicationController
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
render assistant_response
|
49
|
+
render json: assistant_response
|
50
50
|
end
|
51
51
|
end
|
52
52
|
```
|
@@ -107,6 +107,10 @@ assistant.intent.text do
|
|
107
107
|
end
|
108
108
|
```
|
109
109
|
|
110
|
+
### SSML
|
111
|
+
|
112
|
+
SSML is Google Assistant's markup language for text to speech. It provides options to pause, interpret dates and numbers, and more. You can provide SSML responses or plain text. See [Google's documentation on SSML](https://developers.google.com/actions/reference/ssml).
|
113
|
+
|
110
114
|
### User input
|
111
115
|
|
112
116
|
GoogleAssistant allows you to read the user's input using `assistant.arguments` so that you can respond appropriately.
|
@@ -163,9 +167,88 @@ GoogleAssistant.respond_to(params, response) do |assistant|
|
|
163
167
|
end
|
164
168
|
```
|
165
169
|
|
166
|
-
###
|
170
|
+
### User ID
|
167
171
|
|
168
|
-
|
172
|
+
You can get the user's ID. This will allow you to identify the user across conversations. It works much in the same way a cookie might work; it is possible for the user to reset that ID, so don't rely on it too much.
|
173
|
+
|
174
|
+
```rb
|
175
|
+
# Get the user's ID
|
176
|
+
assistant.user.id
|
177
|
+
```
|
178
|
+
|
179
|
+
### Permissions
|
180
|
+
|
181
|
+
You can request information about the user and their device. Google handles collecting this information, but you can provide a prompt to let the user know why you need this information. The Google Assistant API responds with the `permission` intent after a permission request.
|
182
|
+
|
183
|
+
#### Name
|
184
|
+
|
185
|
+
Request the user's name. This will result in a prompt to the user like:
|
186
|
+
> So that I can address you by name, I'll just need to get your name from Google. Is that ok?
|
187
|
+
|
188
|
+
```rb
|
189
|
+
assistant.intent.main do
|
190
|
+
# Request the user's name
|
191
|
+
assistant.ask_for_permission(context: "So that I can address you by name", permissions: GoogleAssistant::Permission::NAME)
|
192
|
+
end
|
193
|
+
|
194
|
+
assistant.intent.permission do
|
195
|
+
if assistant.permission_granted?
|
196
|
+
# Get the user's name from the response
|
197
|
+
given_name = assistant.user.given_name
|
198
|
+
family_name = assistant.user.family_name
|
199
|
+
display_name = assistant.user.display_name
|
200
|
+
else
|
201
|
+
# The user denied permission
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
#### Coarse Location
|
207
|
+
|
208
|
+
Request the device's zip code and city. This will result in a prompt to the user like:
|
209
|
+
> To provide weather information for where you live, I'll just need to get your zip code from Google. Is that ok?
|
210
|
+
|
211
|
+
```rb
|
212
|
+
assistant.intent.main do
|
213
|
+
# Request the device's zip code and city
|
214
|
+
assistant.ask_for_permission(context: "To provide weather information for where you live", permissions: GoogleAssistant::Permission::DEVICE_COARSE_LOCATION)
|
215
|
+
end
|
216
|
+
|
217
|
+
assistant.intent.permission do
|
218
|
+
if assistant.permission_granted?
|
219
|
+
# Get the device's location from the response
|
220
|
+
zip_code = assistant.device.zip_code
|
221
|
+
city = assistant.device.city
|
222
|
+
else
|
223
|
+
# The user denied permission
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
#### Precise Location
|
229
|
+
|
230
|
+
Request the device's precise location. This will result in a prompt to the user like:
|
231
|
+
> So that I can find out where you sleep at night, I'll just need to get your street address from Google. Is that ok?
|
232
|
+
|
233
|
+
```rb
|
234
|
+
assistant.intent.main do
|
235
|
+
# Request the device's precise location
|
236
|
+
assistant.ask_for_permission(context: "So that I can find out where you sleep at night", permissions: GoogleAssistant::Permission::DEVICE_PRECISE_LOCATION)
|
237
|
+
end
|
238
|
+
|
239
|
+
assistant.intent.permission do
|
240
|
+
if assistant.permission_granted?
|
241
|
+
# Get the device's location from the response
|
242
|
+
zip_code = assistant.device.zip_code
|
243
|
+
city = assistant.device.city
|
244
|
+
formatted_address = assistant.device.formatted_address
|
245
|
+
latitude = assistant.device.latitude
|
246
|
+
longitude = assistant.device.longitude
|
247
|
+
else
|
248
|
+
# The user denied permission
|
249
|
+
end
|
250
|
+
end
|
251
|
+
```
|
169
252
|
|
170
253
|
### Testing your assistant
|
171
254
|
|
@@ -1,11 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleAssistant
|
2
4
|
class Argument
|
3
5
|
attr_reader :name, :raw_text, :text_value
|
4
6
|
|
7
|
+
def self.from(opts)
|
8
|
+
case opts["name"]
|
9
|
+
when "permission_granted"
|
10
|
+
PermissionArgument.new(opts)
|
11
|
+
when "text"
|
12
|
+
TextArgument.new(opts)
|
13
|
+
else
|
14
|
+
Argument.new(opts)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
5
18
|
def initialize(opts)
|
6
19
|
@name = opts["name"]
|
7
20
|
@raw_text = opts["raw_text"]
|
8
21
|
@text_value = opts["text_value"]
|
9
22
|
end
|
10
23
|
end
|
24
|
+
|
25
|
+
class TextArgument < Argument
|
26
|
+
alias_method :value, :text_value
|
27
|
+
end
|
28
|
+
|
29
|
+
class PermissionArgument < Argument
|
30
|
+
|
31
|
+
def permission_granted?
|
32
|
+
text_value == "true"
|
33
|
+
end
|
34
|
+
end
|
11
35
|
end
|
@@ -7,6 +7,8 @@ module GoogleAssistant
|
|
7
7
|
InvalidIntent = Class.new(StandardError)
|
8
8
|
InvalidMessage = Class.new(StandardError)
|
9
9
|
InvalidInputPrompt = Class.new(StandardError)
|
10
|
+
InvalidPermission = Class.new(StandardError)
|
11
|
+
InvalidPermissionContext = Class.new(StandardError)
|
10
12
|
MissingRequestInputs = Class.new(StandardError)
|
11
13
|
MissingRequestIntent = Class.new(StandardError)
|
12
14
|
|
@@ -31,7 +33,14 @@ module GoogleAssistant
|
|
31
33
|
|
32
34
|
def arguments
|
33
35
|
@_arguments ||= inputs[0]["arguments"].map do |argument|
|
34
|
-
Argument.
|
36
|
+
Argument.from(argument)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def permission_granted?
|
41
|
+
arguments.any? do |argument|
|
42
|
+
argument.is_a?(PermissionArgument) &&
|
43
|
+
argument.permission_granted?
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
@@ -39,6 +48,14 @@ module GoogleAssistant
|
|
39
48
|
@_conversation ||= Conversation.new(conversation_params)
|
40
49
|
end
|
41
50
|
|
51
|
+
def user
|
52
|
+
@_user ||= User.new(user_params)
|
53
|
+
end
|
54
|
+
|
55
|
+
def device
|
56
|
+
@_device ||= Device.new(device_params)
|
57
|
+
end
|
58
|
+
|
42
59
|
def tell(message)
|
43
60
|
raise InvalidMessage if message.nil? || message.empty?
|
44
61
|
|
@@ -57,22 +74,23 @@ module GoogleAssistant
|
|
57
74
|
raise InvalidInputPrompt if prompt.nil? || prompt.empty?
|
58
75
|
|
59
76
|
no_input_prompt = [*no_input_prompt].compact
|
60
|
-
|
61
77
|
prompt = build_input_prompt(prompt, no_input_prompt)
|
62
|
-
|
63
78
|
expected_intent = build_expected_intent(StandardIntents::TEXT)
|
64
79
|
|
65
|
-
|
66
|
-
|
67
|
-
possible_intents: [expected_intent]
|
68
|
-
}]
|
80
|
+
build_ask_response(prompt, expected_intent)
|
81
|
+
end
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
83
|
+
def ask_for_permission(context:, permissions:)
|
84
|
+
raise InvalidPermissionContext if context.nil? || context.empty?
|
85
|
+
|
86
|
+
permissions = [*permissions].compact
|
87
|
+
raise InvalidPermission unless Permission.valid?(permissions)
|
88
|
+
raise InvalidPermission if permissions.size == 0
|
89
|
+
|
90
|
+
prompt = build_input_prompt("placeholder for permission")
|
91
|
+
expected_intent = build_expected_intent(StandardIntents::PERMISSION, permissions, context)
|
92
|
+
|
93
|
+
build_ask_response(prompt, expected_intent)
|
76
94
|
end
|
77
95
|
|
78
96
|
private
|
@@ -92,6 +110,20 @@ module GoogleAssistant
|
|
92
110
|
}
|
93
111
|
end
|
94
112
|
|
113
|
+
def build_ask_response(prompt, expected_intent)
|
114
|
+
expected_inputs = [{
|
115
|
+
input_prompt: prompt,
|
116
|
+
possible_intents: [expected_intent]
|
117
|
+
}]
|
118
|
+
|
119
|
+
build_response(
|
120
|
+
conversation.dialog_state,
|
121
|
+
true,
|
122
|
+
expected_inputs,
|
123
|
+
nil
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
95
127
|
def build_response(dialog_state, expect_user_response, expected_input, final_response)
|
96
128
|
response = {}
|
97
129
|
|
@@ -103,10 +135,21 @@ module GoogleAssistant
|
|
103
135
|
response
|
104
136
|
end
|
105
137
|
|
106
|
-
def build_expected_intent(intent)
|
138
|
+
def build_expected_intent(intent, permissions = nil, context = nil)
|
107
139
|
raise InvalidIntent if intent.nil? || intent.empty?
|
108
140
|
|
109
|
-
{ intent: intent }
|
141
|
+
expected_intent = { intent: intent }
|
142
|
+
|
143
|
+
unless context.nil? || permissions.nil?
|
144
|
+
expected_intent[:input_value_spec] = {
|
145
|
+
permission_value_spec: {
|
146
|
+
opt_context: context,
|
147
|
+
permissions: permissions
|
148
|
+
}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
expected_intent
|
110
153
|
end
|
111
154
|
|
112
155
|
def is_ssml?(text)
|
@@ -130,5 +173,13 @@ module GoogleAssistant
|
|
130
173
|
def conversation_params
|
131
174
|
params["conversation"] || {}
|
132
175
|
end
|
176
|
+
|
177
|
+
def user_params
|
178
|
+
params["user"] || {}
|
179
|
+
end
|
180
|
+
|
181
|
+
def device_params
|
182
|
+
params["device"] || {}
|
183
|
+
end
|
133
184
|
end
|
134
185
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module GoogleAssistant
|
2
|
+
class Device
|
3
|
+
attr_reader :location, :coordinates
|
4
|
+
|
5
|
+
def initialize(opts)
|
6
|
+
@location = opts["location"] || {}
|
7
|
+
@coordinates = @location["coordinates"] || {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def city
|
11
|
+
location["city"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def zip_code
|
15
|
+
location["zip_code"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatted_address
|
19
|
+
location["formatted_address"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def latitude
|
23
|
+
coordinates["latitude"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def longitude
|
27
|
+
coordinates["longitude"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GoogleAssistant
|
4
|
+
module Permission
|
5
|
+
NAME = "NAME"
|
6
|
+
DEVICE_PRECISE_LOCATION = "DEVICE_PRECISE_LOCATION"
|
7
|
+
DEVICE_COARSE_LOCATION = "DEVICE_COARSE_LOCATION"
|
8
|
+
|
9
|
+
def self.valid?(permissions)
|
10
|
+
permissions = [*permissions]
|
11
|
+
permissions.all? do |permission|
|
12
|
+
[NAME, DEVICE_PRECISE_LOCATION, DEVICE_COARSE_LOCATION].include?(permission)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GoogleAssistant
|
2
|
+
class User
|
3
|
+
attr_reader :id, :profile
|
4
|
+
|
5
|
+
def initialize(opts)
|
6
|
+
@id = opts["user_id"]
|
7
|
+
@profile = opts["profile"] || {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def display_name
|
11
|
+
profile["display_name"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def given_name
|
15
|
+
profile["given_name"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def family_name
|
19
|
+
profile["family_name"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google_assistant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Milam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,8 +73,11 @@ files:
|
|
73
73
|
- lib/google_assistant/argument.rb
|
74
74
|
- lib/google_assistant/assistant.rb
|
75
75
|
- lib/google_assistant/conversation.rb
|
76
|
+
- lib/google_assistant/device.rb
|
76
77
|
- lib/google_assistant/dialog_state.rb
|
77
78
|
- lib/google_assistant/intent.rb
|
79
|
+
- lib/google_assistant/permission.rb
|
80
|
+
- lib/google_assistant/user.rb
|
78
81
|
- lib/google_assistant/version.rb
|
79
82
|
homepage: https://github.com/armilam/google-assistant-ruby
|
80
83
|
licenses:
|