google_assistant 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|