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 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: