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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dfd97fdfaea944f3be60796786ec4f2d9e43f8cc
4
- data.tar.gz: a1d07df44788b57d8e4c7bf81cde7506cf41d1be
3
+ metadata.gz: 53efc649d01191b0cfdabc34974a52e124fa8b8d
4
+ data.tar.gz: 8668eb3b467923e6db3d1667e7ef1c0bfde46c22
5
5
  SHA512:
6
- metadata.gz: b048a4da4c4e612b38b676a0622bcda406dc1e5f2b31e94a8f0ed042432285662dee80c49c633df24d3272e5fa88eb426bc4f2111ef59441deedfd7f0e08d6f2
7
- data.tar.gz: e3bab745f936fcee45e80936d3b45517542bc1b3f2f9deb1b5d03461b6577792ed73462ef6b4619f73a740e85f68ed311322fd31c07a4eea107bcd7441ddb907
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
- ### SSML
170
+ ### User ID
167
171
 
168
- 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).
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.new(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
- expected_inputs = [{
66
- input_prompt: prompt,
67
- possible_intents: [expected_intent]
68
- }]
80
+ build_ask_response(prompt, expected_intent)
81
+ end
69
82
 
70
- build_response(
71
- conversation.dialog_state,
72
- true,
73
- expected_inputs,
74
- nil
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
@@ -28,6 +28,10 @@ module GoogleAssistant
28
28
  intents[StandardIntents::TEXT] = block
29
29
  end
30
30
 
31
+ def permission(&block)
32
+ intents[StandardIntents::PERMISSION] = block
33
+ end
34
+
31
35
  def call
32
36
  block = intents[intent_string]
33
37
  return if block.nil?
@@ -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
@@ -1,3 +1,3 @@
1
1
  module GoogleAssistant
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  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.1.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-13 00:00:00.000000000 Z
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: