stealth-smooch 0.9.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 +7 -0
- data/.gitignore +12 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +133 -0
- data/LICENSE +20 -0
- data/README.md +335 -0
- data/VERSION +1 -0
- data/lib/stealth-smooch.rb +2 -0
- data/lib/stealth/services/smooch/client.rb +82 -0
- data/lib/stealth/services/smooch/events/message_event.rb +56 -0
- data/lib/stealth/services/smooch/events/postback_event.rb +31 -0
- data/lib/stealth/services/smooch/message_handler.rb +71 -0
- data/lib/stealth/services/smooch/reply_handler.rb +319 -0
- data/lib/stealth/services/smooch/setup.rb +35 -0
- data/lib/stealth/services/smooch/version.rb +16 -0
- data/lib/stealth/smooch.rb +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/version_spec.rb +16 -0
- data/stealth-smooch.gemspec +27 -0
- metadata +147 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6e3657bee135d475584a394c1a2248f356d0e9173a49f6e941f1b162b92b2069
|
|
4
|
+
data.tar.gz: d19f031c8632ae4b3db4bfc9797f303e084b7bc7fe6d5bb7847d945102409180
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: fef040a938acf8c730b36351812fbcbbc799406ca81e79a2f3e3360ff641f00fefb37004d8ef3f3ecb2187ed116cdb1f8b2a6a6da064a166d11516961ab42193
|
|
7
|
+
data.tar.gz: 61f7ebbb9e1eb16de0fa5234bafea9111cee6cc76f19efe12711d0c380f8c19c443001d52900aa8b26f9f1176e6c57040bfd269f3a40c684f19299a363fdb67c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
stealth-smooch (0.9.0)
|
|
5
|
+
jwt (~> 2.1)
|
|
6
|
+
smooch-api (~> 4.0)
|
|
7
|
+
stealth (< 2.0)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
actionpack (5.2.1)
|
|
13
|
+
actionview (= 5.2.1)
|
|
14
|
+
activesupport (= 5.2.1)
|
|
15
|
+
rack (~> 2.0)
|
|
16
|
+
rack-test (>= 0.6.3)
|
|
17
|
+
rails-dom-testing (~> 2.0)
|
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
19
|
+
actionview (5.2.1)
|
|
20
|
+
activesupport (= 5.2.1)
|
|
21
|
+
builder (~> 3.1)
|
|
22
|
+
erubi (~> 1.4)
|
|
23
|
+
rails-dom-testing (~> 2.0)
|
|
24
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
25
|
+
activemodel (5.2.1)
|
|
26
|
+
activesupport (= 5.2.1)
|
|
27
|
+
activerecord (5.2.1)
|
|
28
|
+
activemodel (= 5.2.1)
|
|
29
|
+
activesupport (= 5.2.1)
|
|
30
|
+
arel (>= 9.0)
|
|
31
|
+
activesupport (5.2.1)
|
|
32
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
33
|
+
i18n (>= 0.7, < 2)
|
|
34
|
+
minitest (~> 5.1)
|
|
35
|
+
tzinfo (~> 1.1)
|
|
36
|
+
arel (9.0.0)
|
|
37
|
+
builder (3.2.3)
|
|
38
|
+
concurrent-ruby (1.1.3)
|
|
39
|
+
connection_pool (2.2.2)
|
|
40
|
+
crass (1.0.4)
|
|
41
|
+
diff-lcs (1.3)
|
|
42
|
+
erubi (1.7.1)
|
|
43
|
+
ethon (0.11.0)
|
|
44
|
+
ffi (>= 1.3.0)
|
|
45
|
+
ffi (1.9.25)
|
|
46
|
+
i18n (1.1.1)
|
|
47
|
+
concurrent-ruby (~> 1.0)
|
|
48
|
+
json (1.8.6)
|
|
49
|
+
jwt (2.1.0)
|
|
50
|
+
loofah (2.2.3)
|
|
51
|
+
crass (~> 1.0.2)
|
|
52
|
+
nokogiri (>= 1.5.9)
|
|
53
|
+
method_source (0.9.2)
|
|
54
|
+
mini_portile2 (2.3.0)
|
|
55
|
+
minitest (5.11.3)
|
|
56
|
+
multi_json (1.13.1)
|
|
57
|
+
mustermann (1.0.3)
|
|
58
|
+
nokogiri (1.8.5)
|
|
59
|
+
mini_portile2 (~> 2.3.0)
|
|
60
|
+
puma (3.12.0)
|
|
61
|
+
rack (2.0.6)
|
|
62
|
+
rack-protection (2.0.4)
|
|
63
|
+
rack
|
|
64
|
+
rack-test (1.1.0)
|
|
65
|
+
rack (>= 1.0, < 3)
|
|
66
|
+
rails-dom-testing (2.0.3)
|
|
67
|
+
activesupport (>= 4.2.0)
|
|
68
|
+
nokogiri (>= 1.6)
|
|
69
|
+
rails-html-sanitizer (1.0.4)
|
|
70
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
71
|
+
railties (5.2.1)
|
|
72
|
+
actionpack (= 5.2.1)
|
|
73
|
+
activesupport (= 5.2.1)
|
|
74
|
+
method_source
|
|
75
|
+
rake (>= 0.8.7)
|
|
76
|
+
thor (>= 0.19.0, < 2.0)
|
|
77
|
+
rake (12.3.1)
|
|
78
|
+
redis (4.0.3)
|
|
79
|
+
rspec (3.8.0)
|
|
80
|
+
rspec-core (~> 3.8.0)
|
|
81
|
+
rspec-expectations (~> 3.8.0)
|
|
82
|
+
rspec-mocks (~> 3.8.0)
|
|
83
|
+
rspec-core (3.8.0)
|
|
84
|
+
rspec-support (~> 3.8.0)
|
|
85
|
+
rspec-expectations (3.8.1)
|
|
86
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
87
|
+
rspec-support (~> 3.8.0)
|
|
88
|
+
rspec-mocks (3.8.0)
|
|
89
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
90
|
+
rspec-support (~> 3.8.0)
|
|
91
|
+
rspec-support (3.8.0)
|
|
92
|
+
rspec_junit_formatter (0.4.1)
|
|
93
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
|
94
|
+
sidekiq (5.2.3)
|
|
95
|
+
connection_pool (~> 2.2, >= 2.2.2)
|
|
96
|
+
rack-protection (>= 1.5.0)
|
|
97
|
+
redis (>= 3.3.5, < 5)
|
|
98
|
+
sinatra (2.0.4)
|
|
99
|
+
mustermann (~> 1.0)
|
|
100
|
+
rack (~> 2.0)
|
|
101
|
+
rack-protection (= 2.0.4)
|
|
102
|
+
tilt (~> 2.0)
|
|
103
|
+
smooch-api (4.0)
|
|
104
|
+
json (~> 1.8, >= 1.8.3)
|
|
105
|
+
typhoeus (~> 1.0, >= 1.0.1)
|
|
106
|
+
stealth (1.0.4)
|
|
107
|
+
activerecord (~> 5.2)
|
|
108
|
+
activesupport (~> 5.2)
|
|
109
|
+
multi_json (~> 1.12)
|
|
110
|
+
puma (~> 3.10)
|
|
111
|
+
railties (~> 5.2)
|
|
112
|
+
sidekiq (~> 5.0)
|
|
113
|
+
sinatra (~> 2.0)
|
|
114
|
+
thor (~> 0.20)
|
|
115
|
+
thor (0.20.3)
|
|
116
|
+
thread_safe (0.3.6)
|
|
117
|
+
tilt (2.0.8)
|
|
118
|
+
typhoeus (1.3.1)
|
|
119
|
+
ethon (>= 0.9.0)
|
|
120
|
+
tzinfo (1.2.5)
|
|
121
|
+
thread_safe (~> 0.1)
|
|
122
|
+
|
|
123
|
+
PLATFORMS
|
|
124
|
+
ruby
|
|
125
|
+
|
|
126
|
+
DEPENDENCIES
|
|
127
|
+
rack-test (~> 1.1)
|
|
128
|
+
rspec (~> 3.6)
|
|
129
|
+
rspec_junit_formatter (~> 0.3)
|
|
130
|
+
stealth-smooch!
|
|
131
|
+
|
|
132
|
+
BUNDLED WITH
|
|
133
|
+
1.16.6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2018 Mauricio Gomes, Black Ops Bureau
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Stealth Smooch
|
|
2
|
+
|
|
3
|
+
This integration adds support for [Smooch](https://smooch.io) powered bots within [Stealth](https://github.com/hellostealth/stealth). It can be used as a drop-in replacement for `stealth-facebook` with the exception of some specialized quick reply buttons (such as Email & Phone).
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/rb/stealth-smooch)
|
|
6
|
+
|
|
7
|
+
## Create Your Smooch App
|
|
8
|
+
|
|
9
|
+
Via the Smooch interface, create a new Smooch app for your Stealth bot. Once you do, you'll be given your `SMOOCH_APP_ID`.
|
|
10
|
+
|
|
11
|
+
Follow the instructions on the [smooch-api Ruby](https://github.com/smooch/smooch-ruby) page to generate your secret keys. This will get you your
|
|
12
|
+
`SMOOCH_KEY_ID` and `SMOOCH_SECRET`.
|
|
13
|
+
|
|
14
|
+
The last thing you will need is the `SMOOCH_JWT_TOKEN` which can be generated using this gem. After you have set the above creds to your `services.yml` file, from your bot's console, run:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
Stealth::Services::Smooch::Client.generate_jwt_token
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It will output the JWT token based on the `app_id`, `key_id`, and `secret` from your `services.yml` file.
|
|
21
|
+
|
|
22
|
+
## Configure the Integration
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
default: &default
|
|
26
|
+
smooch:
|
|
27
|
+
app_id: <%= ENV['SMOOCH_APP_ID'] %>
|
|
28
|
+
key_id: <%= ENV['SMOOCH_KEY_ID'] %>
|
|
29
|
+
secret: <%= ENV['SMOOCH_SECRET'] %>
|
|
30
|
+
jwt_token: <%= ENV['SMOOCH_JWT_TOKEN'] %>
|
|
31
|
+
setup:
|
|
32
|
+
persistent_menu:
|
|
33
|
+
- type: 'url'
|
|
34
|
+
url: 'https://mywebsite.com'
|
|
35
|
+
text: 'About Us'
|
|
36
|
+
- type: 'payload'
|
|
37
|
+
payload: 'contact_support'
|
|
38
|
+
text: 'Contact Support'
|
|
39
|
+
|
|
40
|
+
production:
|
|
41
|
+
<<: *default
|
|
42
|
+
|
|
43
|
+
development:
|
|
44
|
+
<<: *default
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
<<: *default
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Additionally, you will need to create an initializer called `smooch.rb` in `config/initializers`:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
SmoochApi.configure do |config|
|
|
54
|
+
config.api_key['Authorization'] = Stealth.config.smooch.jwt_token
|
|
55
|
+
config.api_key_prefix['Authorization'] = 'Bearer'
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
As with all Stealth integrations, integrations can be specified by environment.
|
|
60
|
+
|
|
61
|
+
These are the supported setup options:
|
|
62
|
+
|
|
63
|
+
### persistent_menu
|
|
64
|
+
|
|
65
|
+
The persistent menu is not supported by all integrations. For a complete list, please check out the [Smooch Pesistent Menu Docs](https://docs.smooch.io/rest/#persistent-menus).
|
|
66
|
+
|
|
67
|
+
Setting the persistent menu is identical to creating buttons in text replies. Please see those docs for more info.
|
|
68
|
+
|
|
69
|
+
### Webhooks
|
|
70
|
+
|
|
71
|
+
In order for your bot to receive messages from the Smooch app, we'll need to register our webhooks.
|
|
72
|
+
|
|
73
|
+
Set `SMOOCH_ENDPOINT` to the endpoint that will be receiving the hooks. It's configured as an ENV variable so you can specify different endpoints for each of your environments.
|
|
74
|
+
|
|
75
|
+
After you have set `SMOOCH_ENDPOINT`, running setup below will register your webhooks.
|
|
76
|
+
|
|
77
|
+
### Running Setup
|
|
78
|
+
|
|
79
|
+
This will set the persistent menu (if available) and register your webhooks.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
stealth setup smooch
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Replies
|
|
86
|
+
|
|
87
|
+
Here are the supported replies for the Smooch integration:
|
|
88
|
+
|
|
89
|
+
### text
|
|
90
|
+
|
|
91
|
+
These are standard text replies.
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
- reply_type: text
|
|
95
|
+
text: Hello World!
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Text replies can also include suggestions, which will be rendered as quick replies:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
- reply_type: text
|
|
102
|
+
text: What is your favorite color?
|
|
103
|
+
suggestions:
|
|
104
|
+
- text: Blue
|
|
105
|
+
- text: Red
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Although not as common, text replies can also include buttons:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
- reply_type: text
|
|
112
|
+
text: Would you like to give us a call?
|
|
113
|
+
buttons:
|
|
114
|
+
- type: payload
|
|
115
|
+
text: 'Yes'
|
|
116
|
+
payload: 'Yes'
|
|
117
|
+
- type: payload
|
|
118
|
+
text: 'No'
|
|
119
|
+
payload: 'No'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### suggestions
|
|
123
|
+
|
|
124
|
+
Though suggestions are not a reply type on their own, they are frequently used to optimize the accuracy and speed of your bot. In the `text` reply type above, we used simple labels for our suggestions. Smooch supports a few special types of quick replies, however.
|
|
125
|
+
|
|
126
|
+
#### Location
|
|
127
|
+
|
|
128
|
+
You can ask a user for their location:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
- reply_type: text
|
|
132
|
+
text: "Where are you located?"
|
|
133
|
+
suggestions:
|
|
134
|
+
- type: location
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If the user chooses to share their location, the `lat` and `lng` will be available via `current_message.location`:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
current_message.location[:lat]
|
|
141
|
+
current_message.location[:lng]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Images
|
|
145
|
+
|
|
146
|
+
While images are not a special quick reply type, you can include and `image_url` for a quick reply as way of adding an icon to a quick reply button:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
- reply_type: text
|
|
150
|
+
text: "What is your favorite color?"
|
|
151
|
+
suggestions:
|
|
152
|
+
- text: Red
|
|
153
|
+
image_url: "http://example.com/img/red.png"
|
|
154
|
+
- text: Blue
|
|
155
|
+
image_url: "http://example.com/img/blue.png"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
More info [here](https://docs.smooch.io/rest/#reply).
|
|
159
|
+
|
|
160
|
+
### buttons
|
|
161
|
+
|
|
162
|
+
As with `suggestions`, `buttons` are not a reply type of their own but are used to make your bot more efficient. Smooch supports a few button types and these are the ones currently supported by this integration:
|
|
163
|
+
|
|
164
|
+
#### payload
|
|
165
|
+
|
|
166
|
+
This is the most common button type. When a user presses a button that is `payload` type, that payload string will be sent to your bot. For example:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
- reply_type: text
|
|
170
|
+
text: Please press the button below
|
|
171
|
+
buttons:
|
|
172
|
+
- type: payload
|
|
173
|
+
text: 'Press me!'
|
|
174
|
+
payload: 'button pressed'
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
When a user presses the button labeled "Press me!", the payload `button pressed` will be accessible in bot via `current_message.payload`.
|
|
179
|
+
|
|
180
|
+
#### url
|
|
181
|
+
|
|
182
|
+
The `url` button is useful when sharing a link to a website. By default, it will open up within Facebook Messenger.
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
- reply_type: text
|
|
186
|
+
text: Find out more via our website
|
|
187
|
+
buttons:
|
|
188
|
+
- type: url
|
|
189
|
+
text: 'Visit website'
|
|
190
|
+
url: 'https://example.org'
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Delay
|
|
195
|
+
|
|
196
|
+
Delays are a very important part of bot design. They introduce a pause between text replies to give the user a chance to read each reply. With this integration, in addition to introducing a delay, we will also send a typing indicator to the user to indicate another reply is forthcoming. To insert a delay in your bot:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
- reply_type: delay
|
|
200
|
+
duration: 2
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This will add a `2` second delay (with typing indicator). The `duration` can be specified as any floating point value, in seconds.
|
|
204
|
+
|
|
205
|
+
### Cards
|
|
206
|
+
|
|
207
|
+
Smooch distinguishes between a single card and a carousel of cards. This integration does not, however. You can send a single card the same way you would send 10 cards (the current maximum).
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
- reply_type: cards
|
|
211
|
+
elements:
|
|
212
|
+
- title: My App
|
|
213
|
+
subtitle: Download our app below or visit our website for more info.
|
|
214
|
+
image_url: "https://my-app.com/app-image.png"
|
|
215
|
+
buttons:
|
|
216
|
+
- type: url
|
|
217
|
+
url: "https://my-app.com"
|
|
218
|
+
text: 'View'
|
|
219
|
+
webview_height: 'tall'
|
|
220
|
+
- type: url
|
|
221
|
+
url: "https://itunes.apple.com/us/app/my-app"
|
|
222
|
+
text: 'Download iOS App'
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The above is a single card with two buttons. If you want to include more cards, though, you would just need to specify another listing under the `elements` heading.
|
|
226
|
+
|
|
227
|
+
More info about Smooch cards [here](https://docs.smooch.io/rest/#carousel).
|
|
228
|
+
|
|
229
|
+
### List
|
|
230
|
+
|
|
231
|
+
A Smooch list is useful for displaying things like a news feed. You can find more info about Smooch lists [here](https://docs.smooch.io/rest/#list).
|
|
232
|
+
|
|
233
|
+
To generate a list:
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
- reply_type: list
|
|
237
|
+
buttons:
|
|
238
|
+
- type: payload
|
|
239
|
+
text: View More
|
|
240
|
+
payload: view_more
|
|
241
|
+
elements:
|
|
242
|
+
- title: Your Daily News Update
|
|
243
|
+
subtitle: The following stories have been curated just for you.
|
|
244
|
+
image_url: "https://picsum.photos/320/240"
|
|
245
|
+
buttons:
|
|
246
|
+
- type: url
|
|
247
|
+
url: "https://news-articles.com/199"
|
|
248
|
+
text: 'View'
|
|
249
|
+
- title: Breakthrough in AI
|
|
250
|
+
subtitle: Major breakthrough in the AI space.
|
|
251
|
+
image_url: "https://picsum.photos/320/320"
|
|
252
|
+
buttons:
|
|
253
|
+
- type: url
|
|
254
|
+
url: "https://news-articles.com/201"
|
|
255
|
+
text: 'View'
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The list itself supports having a single button that will be rendered on the bottom of the list. Each individual list item supports having one button as well. List items should have between 2-4 elements.
|
|
259
|
+
|
|
260
|
+
More info about Smooch lists [here](https://docs.smooch.io/rest/#list).
|
|
261
|
+
|
|
262
|
+
### Images
|
|
263
|
+
|
|
264
|
+
To send an image:
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
- reply_type: image
|
|
268
|
+
image_url: 'https://example.org/image.png'
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The `image_url` should be set to URL where the image has been uploaded.
|
|
272
|
+
|
|
273
|
+
Image replies support buttons and suggestions like text replies.
|
|
274
|
+
|
|
275
|
+
### Files
|
|
276
|
+
|
|
277
|
+
To send a file:
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
- reply_type: file
|
|
281
|
+
file_url: 'https://example.org/some.pdf'
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The `file_url` should be set to URL where the file has been uploaded.
|
|
285
|
+
|
|
286
|
+
File replies support buttons and suggestions like text replies.
|
|
287
|
+
|
|
288
|
+
### Video
|
|
289
|
+
|
|
290
|
+
To send a video:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
- reply_type: video
|
|
294
|
+
video_url: 'https://example.org/cool_video.mp4'
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The `video_url` should be set to URL where the video has been uploaded.
|
|
298
|
+
|
|
299
|
+
Video replies support buttons and suggestions like text replies.
|
|
300
|
+
|
|
301
|
+
### Audio
|
|
302
|
+
|
|
303
|
+
To send an audio clip:
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
- reply_type: audio
|
|
307
|
+
audio_url: 'https://example.org/podcast.mp3'
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The `audio_url` should be set to URL where the video has been uploaded.
|
|
311
|
+
|
|
312
|
+
Audio replies support buttons and suggestions like text replies.
|
|
313
|
+
|
|
314
|
+
## Development
|
|
315
|
+
|
|
316
|
+
When adding features to this library, you might find it helpful to get a full printout of the HTTP requests and responses from Smooch.
|
|
317
|
+
|
|
318
|
+
In order to configure your bot to show the debug output, modify your `smooch.rb` initializer like so:
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
class SmoochLogger
|
|
322
|
+
|
|
323
|
+
def self.debug(msg)
|
|
324
|
+
Stealth::Logger.l(topic: 'smooch', message: msg)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
SmoochApi.configure do |config|
|
|
330
|
+
config.logger = SmoochLogger
|
|
331
|
+
config.debugging = true
|
|
332
|
+
config.api_key['Authorization'] = Stealth.config.smooch.jwt_token
|
|
333
|
+
config.api_key_prefix['Authorization'] = 'Bearer'
|
|
334
|
+
end
|
|
335
|
+
```
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.9.0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'stealth/services/smooch/message_handler'
|
|
5
|
+
require 'stealth/services/smooch/reply_handler'
|
|
6
|
+
require 'stealth/services/smooch/setup'
|
|
7
|
+
|
|
8
|
+
module Stealth
|
|
9
|
+
module Services
|
|
10
|
+
module Smooch
|
|
11
|
+
|
|
12
|
+
class Client < Stealth::Services::BaseClient
|
|
13
|
+
|
|
14
|
+
attr_reader :reply
|
|
15
|
+
|
|
16
|
+
def initialize(reply:)
|
|
17
|
+
@reply = reply
|
|
18
|
+
@smooch = SmoochApi::ConversationApi.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def transmit
|
|
22
|
+
begin
|
|
23
|
+
response = @smooch.send(
|
|
24
|
+
reply[:reply_type],
|
|
25
|
+
Stealth.config.smooch.app_id,
|
|
26
|
+
reply[:recipient_id],
|
|
27
|
+
reply[:message]
|
|
28
|
+
)
|
|
29
|
+
rescue SmoochApi::ApiError => e
|
|
30
|
+
msg = Stealth::Logger.colorize('[Error]', color: :red) + " #{e.code}: #{e.response_body}"
|
|
31
|
+
Stealth::Logger.l(topic: 'smooch', message: msg)
|
|
32
|
+
raise Stealth::Errors::ServiceError
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if response.present?
|
|
36
|
+
Stealth::Logger.l(topic: "smooch", message: "Message #{response.message._id} successfully sent.")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.generate_jwt_token
|
|
41
|
+
payload = { scope: 'app' }
|
|
42
|
+
jwtHeader = { kid: Stealth.config.smooch.key_id }
|
|
43
|
+
token = JWT.encode(payload, Stealth.config.smooch.secret, 'HS256', jwtHeader)
|
|
44
|
+
|
|
45
|
+
puts "#{Stealth::Logger.colorize('[JWT Token]', color: :green)} Your Smooch token is below. Please set the value `jwt_token` to the token in services.yml."
|
|
46
|
+
puts token
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.register_webhooks(endpoint:)
|
|
50
|
+
smooch_webhook_api = SmoochApi::WebhookApi.new
|
|
51
|
+
webhook_create_body = SmoochApi::WebhookCreate.new(
|
|
52
|
+
target: endpoint,
|
|
53
|
+
triggers: ['message:appUser', 'postback']
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
response = smooch_webhook_api.create_webhook(
|
|
57
|
+
Stealth.config.smooch.app_id,
|
|
58
|
+
webhook_create_body
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
puts "#{Stealth::Logger.colorize('[Web Hooks]', color: :green)} Your Smooch webhooks have been registered to: #{endpoint}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.set_persistent_menu(menu)
|
|
65
|
+
smooch_api = SmoochApi::IntegrationApi.new
|
|
66
|
+
response = smooch_api.list_integrations(Stealth.config.smooch.app_id)
|
|
67
|
+
response.integrations.each do |integration|
|
|
68
|
+
begin
|
|
69
|
+
smooch_api.update_integration_menu(Stealth.config.smooch.app_id, integration._id, menu)
|
|
70
|
+
puts "#{Stealth::Logger.colorize('[Persistent Menu]', color: :green)} set for #{integration.type} integration."
|
|
71
|
+
rescue SmoochApi::ApiError
|
|
72
|
+
# Not all integrations support the persistent menu
|
|
73
|
+
puts "#{Stealth::Logger.colorize('[Persistent Menu]', color: :red)} Skipping #{integration.type} integration. Persistent Menu is not supported."
|
|
74
|
+
next
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Stealth
|
|
5
|
+
module Services
|
|
6
|
+
module Smooch
|
|
7
|
+
|
|
8
|
+
class MessageEvent
|
|
9
|
+
|
|
10
|
+
attr_reader :service_message, :params
|
|
11
|
+
|
|
12
|
+
def initialize(service_message:, params:)
|
|
13
|
+
@service_message = service_message
|
|
14
|
+
@params = params
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process
|
|
18
|
+
fetch_message
|
|
19
|
+
fetch_location
|
|
20
|
+
fetch_attachments
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def fetch_message
|
|
26
|
+
service_message.message = params['text']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fetch_location
|
|
30
|
+
if params['type'] == 'location'
|
|
31
|
+
service_message.location = {
|
|
32
|
+
lat: params['coordinates']['lat'],
|
|
33
|
+
lng: params['coordinates']['lng']
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def fetch_attachments
|
|
39
|
+
if params['type'] == 'image'
|
|
40
|
+
service_message.attachments << {
|
|
41
|
+
type: 'image',
|
|
42
|
+
url: params['mediaUrl']
|
|
43
|
+
}
|
|
44
|
+
elsif params['type'] == 'file'
|
|
45
|
+
service_message.attachments << {
|
|
46
|
+
type: params['mediaType'],
|
|
47
|
+
url: params['mediaUrl']
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Stealth
|
|
5
|
+
module Services
|
|
6
|
+
module Smooch
|
|
7
|
+
|
|
8
|
+
class PostbackEvent
|
|
9
|
+
|
|
10
|
+
attr_reader :service_message, :params
|
|
11
|
+
|
|
12
|
+
def initialize(service_message:, params:)
|
|
13
|
+
@service_message = service_message
|
|
14
|
+
@params = params
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process
|
|
18
|
+
fetch_payload
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def fetch_payload
|
|
24
|
+
service_message.payload = params['action']['payload']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'stealth/services/smooch/events/message_event'
|
|
5
|
+
require 'stealth/services/smooch/events/postback_event'
|
|
6
|
+
|
|
7
|
+
module Stealth
|
|
8
|
+
module Services
|
|
9
|
+
module Smooch
|
|
10
|
+
|
|
11
|
+
class MessageHandler < Stealth::Services::BaseMessageHandler
|
|
12
|
+
|
|
13
|
+
attr_reader :service_message, :params, :headers,
|
|
14
|
+
:smooch_response, :smooch_message
|
|
15
|
+
|
|
16
|
+
def initialize(params:, headers:)
|
|
17
|
+
@params = params
|
|
18
|
+
@headers = headers
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def coordinate
|
|
22
|
+
# Queue the request processing so we can respond quickly to Smooch
|
|
23
|
+
# and also keep track of this message
|
|
24
|
+
Stealth::Services::HandleMessageJob.perform_async('smooch', params, {})
|
|
25
|
+
|
|
26
|
+
# Relay our acceptance
|
|
27
|
+
[200, 'OK']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def process
|
|
31
|
+
@service_message = ServiceMessage.new(service: 'smooch')
|
|
32
|
+
@smooch_response = params
|
|
33
|
+
@smooch_message = @smooch_response['messages'].first
|
|
34
|
+
service_message.sender_id = get_sender_id
|
|
35
|
+
service_message.timestamp = get_timestamp
|
|
36
|
+
|
|
37
|
+
process_smooch_event
|
|
38
|
+
|
|
39
|
+
service_message
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def get_sender_id
|
|
45
|
+
smooch_response['appUser']['_id']
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_timestamp
|
|
49
|
+
Time.at(smooch_message['received']).to_datetime
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def process_smooch_event
|
|
53
|
+
if smooch_response['trigger'] == 'message:appUser'
|
|
54
|
+
message_event = Stealth::Services::Smooch::MessageEvent.new(
|
|
55
|
+
service_message: service_message,
|
|
56
|
+
params: smooch_message
|
|
57
|
+
)
|
|
58
|
+
elsif smooch_response['trigger'] == 'postback'
|
|
59
|
+
message_event = Stealth::Services::Smooch::PostbackEvent.new(
|
|
60
|
+
service_message: service_message,
|
|
61
|
+
params: smooch_response['postbacks'].first
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
message_event.process
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Stealth
|
|
5
|
+
module Services
|
|
6
|
+
module Smooch
|
|
7
|
+
|
|
8
|
+
class ReplyHandler < Stealth::Services::BaseReplyHandler
|
|
9
|
+
|
|
10
|
+
attr_reader :recipient_id, :reply
|
|
11
|
+
|
|
12
|
+
def initialize(recipient_id: nil, reply: nil)
|
|
13
|
+
@recipient_id = recipient_id
|
|
14
|
+
@reply = reply
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def text
|
|
18
|
+
message = SmoochApi::MessagePost.new(
|
|
19
|
+
role: 'appMaker',
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: reply['text']
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if reply['suggestions'].present?
|
|
25
|
+
smooch_suggestions = generate_suggestions(suggestions: reply['suggestions'])
|
|
26
|
+
message.actions = smooch_suggestions
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if reply['buttons'].present?
|
|
30
|
+
smooch_buttons = generate_buttons(buttons: reply['buttons'])
|
|
31
|
+
message.actions = smooch_buttons
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
message_template(action: 'post_message', message: message)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def image
|
|
38
|
+
check_if_arguments_are_valid!(
|
|
39
|
+
suggestions: reply['suggestions'],
|
|
40
|
+
buttons: reply['buttons']
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
message = SmoochApi::MessagePost.new(
|
|
44
|
+
role: 'appMaker',
|
|
45
|
+
type: 'image',
|
|
46
|
+
mediaUrl: reply['image_url']
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if reply['suggestions'].present?
|
|
50
|
+
smooch_suggestions = generate_suggestions(suggestions: reply['suggestions'])
|
|
51
|
+
message.actions = smooch_suggestions
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
message_template(action: 'post_message', message: message)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def audio
|
|
58
|
+
check_if_arguments_are_valid!(
|
|
59
|
+
suggestions: reply['suggestions'],
|
|
60
|
+
buttons: reply['buttons']
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
message = SmoochApi::MessagePost.new(
|
|
64
|
+
role: 'appMaker',
|
|
65
|
+
type: 'file',
|
|
66
|
+
text: reply['text'],
|
|
67
|
+
mediaUrl: reply['audio_url']
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if reply['suggestions'].present?
|
|
71
|
+
smooch_suggestions = generate_suggestions(suggestions: reply['suggestions'])
|
|
72
|
+
message.actions = smooch_suggestions
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
message_template(action: 'post_message', message: message)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def video
|
|
79
|
+
check_if_arguments_are_valid!(
|
|
80
|
+
suggestions: reply['suggestions'],
|
|
81
|
+
buttons: reply['buttons']
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
message = SmoochApi::MessagePost.new(
|
|
85
|
+
role: 'appMaker',
|
|
86
|
+
type: 'file',
|
|
87
|
+
text: reply['text'],
|
|
88
|
+
mediaUrl: reply['video_url']
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if reply['suggestions'].present?
|
|
92
|
+
smooch_suggestions = generate_suggestions(suggestions: reply['suggestions'])
|
|
93
|
+
message.actions = smooch_suggestions
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
message_template(action: 'post_message', message: message)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def file
|
|
100
|
+
check_if_arguments_are_valid!(
|
|
101
|
+
suggestions: reply['suggestions'],
|
|
102
|
+
buttons: reply['buttons']
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
message = SmoochApi::MessagePost.new(
|
|
106
|
+
role: 'appMaker',
|
|
107
|
+
type: 'file',
|
|
108
|
+
text: reply['text'],
|
|
109
|
+
mediaUrl: reply['file_url']
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if reply['suggestions'].present?
|
|
113
|
+
smooch_suggestions = generate_suggestions(suggestions: reply['suggestions'])
|
|
114
|
+
message.actions = smooch_suggestions
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
message_template(action: 'post_message', message: message)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def cards
|
|
121
|
+
message = SmoochApi::MessagePost.new(
|
|
122
|
+
role: 'appMaker',
|
|
123
|
+
type: 'carousel'
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
smooch_items = generate_card_items(elements: reply["elements"])
|
|
127
|
+
message.items = smooch_items
|
|
128
|
+
|
|
129
|
+
message_template(action: 'post_message', message: message)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def list
|
|
133
|
+
message = SmoochApi::MessagePost.new(
|
|
134
|
+
role: 'appMaker',
|
|
135
|
+
type: 'list'
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
smooch_items = generate_list_items(elements: reply["elements"])
|
|
139
|
+
message.items = smooch_items
|
|
140
|
+
|
|
141
|
+
if reply['buttons'].present?
|
|
142
|
+
if reply["buttons"].size > 1
|
|
143
|
+
raise(ArgumentError, "Smooch lists support a single button attached to the list itsef.")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
smooch_buttons = generate_buttons(buttons: reply['buttons'])
|
|
147
|
+
message.actions = smooch_buttons
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
message_template(action: 'post_message', message: message)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def enable_typing_indicator
|
|
154
|
+
message = SmoochApi::TypingActivityTrigger.new(
|
|
155
|
+
role: 'appMaker',
|
|
156
|
+
type: 'typing:start'
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
message_template(action: 'trigger_typing_activity', message: message)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def disable_typing_indicator
|
|
163
|
+
message = SmoochApi::TypingActivityTrigger.new(
|
|
164
|
+
role: 'appMaker',
|
|
165
|
+
type: 'typing:stop'
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
message_template(action: 'trigger_typing_activity', message: message)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def delay
|
|
172
|
+
enable_typing_indicator
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def persistent_menu
|
|
176
|
+
smooch_menu = SmoochApi::Menu.new
|
|
177
|
+
|
|
178
|
+
smooch_menu_items = generate_buttons(buttons: Stealth.config.smooch.setup.persistent_menu)
|
|
179
|
+
smooch_menu.items = smooch_menu_items
|
|
180
|
+
|
|
181
|
+
smooch_menu
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
def message_template(action:, message:)
|
|
187
|
+
{
|
|
188
|
+
recipient_id: recipient_id,
|
|
189
|
+
reply_type: action,
|
|
190
|
+
message: message
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def generate_card_items(elements:)
|
|
195
|
+
if elements.size > 10
|
|
196
|
+
raise(ArgumentError, "Smooch cards can have at most 10 cards.")
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
smooch_items = elements.collect do |element|
|
|
200
|
+
smooch_item = item_template(element_type: 'card', element: element)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
smooch_items
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def generate_list_items(elements:)
|
|
207
|
+
if elements.size < 2 || elements.size > 4
|
|
208
|
+
raise(ArgumentError, "Smooch lists must have 2-4 elements.")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
smooch_items = elements.collect do |element|
|
|
212
|
+
smooch_item = item_template(element_type: 'list', element: element)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
smooch_items
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def item_template(element_type:, element:)
|
|
219
|
+
unless element["title"].present?
|
|
220
|
+
raise(ArgumentError, "Smooch card and list elements must have a 'title' attribute.")
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
smooch_item = SmoochApi::MessageItem.new
|
|
224
|
+
|
|
225
|
+
smooch_item.title = element['title']
|
|
226
|
+
|
|
227
|
+
if element["subtitle"].present?
|
|
228
|
+
smooch_item.description = element["subtitle"]
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
if element["image_url"].present?
|
|
232
|
+
smooch_item.media_url = element["image_url"]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
if element["buttons"].present?
|
|
236
|
+
if element_type == 'card' && element["buttons"].size > 3
|
|
237
|
+
raise(ArgumentError, "Smooch card elements only support 3 buttons.")
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
if element_type == 'list' && element["buttons"].size > 1
|
|
241
|
+
raise(ArgumentError, "Smooch list elements only support 1 button.")
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
smooch_buttons = generate_buttons(buttons: element['buttons'])
|
|
245
|
+
smooch_item.actions = smooch_buttons
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
smooch_item
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def generate_suggestions(suggestions:)
|
|
252
|
+
quick_replies = suggestions.collect do |suggestion|
|
|
253
|
+
quick_reply = SmoochApi::Action.new(type: 'reply')
|
|
254
|
+
|
|
255
|
+
case suggestion["type"]
|
|
256
|
+
when 'location'
|
|
257
|
+
quick_reply.type = 'locationRequest'
|
|
258
|
+
quick_reply.text = suggestion["text"]
|
|
259
|
+
when 'phone'
|
|
260
|
+
quick_reply.text = suggestion["text"]
|
|
261
|
+
when 'email'
|
|
262
|
+
quick_reply.text = suggestion["text"]
|
|
263
|
+
else
|
|
264
|
+
quick_reply.text = suggestion["text"]
|
|
265
|
+
|
|
266
|
+
if suggestion["payload"].present?
|
|
267
|
+
quick_reply.payload = suggestion["payload"]
|
|
268
|
+
else
|
|
269
|
+
quick_reply.payload = suggestion["text"]
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if suggestion["image_url"].present?
|
|
273
|
+
quick_reply.icon_url = suggestion["image_url"]
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
quick_reply
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
quick_replies
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def generate_buttons(buttons:)
|
|
284
|
+
smooch_buttons = buttons.collect do |button|
|
|
285
|
+
case button['type']
|
|
286
|
+
when 'url'
|
|
287
|
+
smooch_button = SmoochApi::Action.new(type: 'webview')
|
|
288
|
+
smooch_button.uri = smooch_button.fallback = button["url"]
|
|
289
|
+
smooch_button.text = smooch_button.fallback = button["text"]
|
|
290
|
+
|
|
291
|
+
if button["webview_height"].present?
|
|
292
|
+
smooch_button.size = button["webview_height"]
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
when 'payload'
|
|
296
|
+
smooch_button = SmoochApi::Action.new(type: 'postback')
|
|
297
|
+
smooch_button.payload = button["payload"]
|
|
298
|
+
smooch_button.text = button["text"]
|
|
299
|
+
|
|
300
|
+
else
|
|
301
|
+
raise(Stealth::Errors::ServiceImpaired, "Sorry, we don't yet support #{button["type"]} buttons yet!")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
smooch_button
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
smooch_buttons
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def check_if_arguments_are_valid!(suggestions:, buttons:)
|
|
311
|
+
if suggestions.present? && buttons.present?
|
|
312
|
+
raise(ArgumentError, "A reply cannot have buttons and suggestions!")
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'stealth/services/smooch/client'
|
|
5
|
+
|
|
6
|
+
module Stealth
|
|
7
|
+
module Services
|
|
8
|
+
module Smooch
|
|
9
|
+
|
|
10
|
+
class Setup
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def trigger
|
|
14
|
+
SmoochApi.configure do |config|
|
|
15
|
+
config.api_key['Authorization'] = Stealth.config.smooch.jwt_token
|
|
16
|
+
config.api_key_prefix['Authorization'] = 'Bearer'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
reply_handler = Stealth::Services::Smooch::ReplyHandler.new
|
|
20
|
+
menu = reply_handler.persistent_menu
|
|
21
|
+
Stealth::Services::Smooch::Client.set_persistent_menu(menu)
|
|
22
|
+
|
|
23
|
+
if ENV['SMOOCH_ENDPOINT'].present?
|
|
24
|
+
Stealth::Services::Smooch::Client.register_webhooks(endpoint: ENV['SMOOCH_ENDPOINT'])
|
|
25
|
+
else
|
|
26
|
+
puts '[ERROR] Please set SMOOCH_ENDPOINT to the endpoint that will receive Smooch webhooks.'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Stealth
|
|
5
|
+
module Services
|
|
6
|
+
module Smooch
|
|
7
|
+
module Version
|
|
8
|
+
def self.version
|
|
9
|
+
File.read(File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'VERSION')).strip
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
VERSION = Version.version
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
|
+
require 'rspec'
|
|
4
|
+
|
|
5
|
+
require 'stealth'
|
|
6
|
+
require 'stealth-smooch'
|
|
7
|
+
|
|
8
|
+
# Requires supporting files with custom matchers and macros, etc,
|
|
9
|
+
# in ./support/ and its subdirectories.
|
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
11
|
+
|
|
12
|
+
RSpec.configure do |config|
|
|
13
|
+
|
|
14
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
4
|
+
|
|
5
|
+
describe "Stealth::Services::Smooch::Version" do
|
|
6
|
+
|
|
7
|
+
let(:version_in_file) { File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).strip }
|
|
8
|
+
|
|
9
|
+
it "should return the current gem version" do
|
|
10
|
+
expect(Stealth::Services::Smooch::Version.version).to eq version_in_file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should return the current gem version via a constant" do
|
|
14
|
+
expect(Stealth::Services::Smooch::VERSION).to eq version_in_file
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
|
2
|
+
|
|
3
|
+
version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'stealth-smooch'
|
|
7
|
+
s.summary = 'Stealth Smooch driver'
|
|
8
|
+
s.description = 'Smooch driver for Stealth.'
|
|
9
|
+
s.homepage = 'https://github.com/hellostealth/stealth-smooch'
|
|
10
|
+
s.licenses = ['MIT']
|
|
11
|
+
s.version = version
|
|
12
|
+
s.author = 'Mauricio Gomes'
|
|
13
|
+
s.email = 'mauricio@edge14.com'
|
|
14
|
+
|
|
15
|
+
s.add_dependency 'stealth', '< 2.0'
|
|
16
|
+
s.add_dependency 'jwt', '~> 2.1'
|
|
17
|
+
s.add_dependency 'smooch-api', '~> 4.0'
|
|
18
|
+
|
|
19
|
+
s.add_development_dependency 'rspec', '~> 3.6'
|
|
20
|
+
s.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
|
|
21
|
+
s.add_development_dependency 'rack-test', '~> 1.1'
|
|
22
|
+
|
|
23
|
+
s.files = `git ls-files`.split("\n")
|
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
26
|
+
s.require_paths = ['lib']
|
|
27
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: stealth-smooch
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.9.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mauricio Gomes
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-11-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: stealth
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "<"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "<"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: jwt
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: smooch-api
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '4.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '4.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.6'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.6'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec_junit_formatter
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.3'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.3'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rack-test
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.1'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.1'
|
|
97
|
+
description: Smooch driver for Stealth.
|
|
98
|
+
email: mauricio@edge14.com
|
|
99
|
+
executables: []
|
|
100
|
+
extensions: []
|
|
101
|
+
extra_rdoc_files: []
|
|
102
|
+
files:
|
|
103
|
+
- ".gitignore"
|
|
104
|
+
- Gemfile
|
|
105
|
+
- Gemfile.lock
|
|
106
|
+
- LICENSE
|
|
107
|
+
- README.md
|
|
108
|
+
- VERSION
|
|
109
|
+
- lib/stealth-smooch.rb
|
|
110
|
+
- lib/stealth/services/smooch/client.rb
|
|
111
|
+
- lib/stealth/services/smooch/events/message_event.rb
|
|
112
|
+
- lib/stealth/services/smooch/events/postback_event.rb
|
|
113
|
+
- lib/stealth/services/smooch/message_handler.rb
|
|
114
|
+
- lib/stealth/services/smooch/reply_handler.rb
|
|
115
|
+
- lib/stealth/services/smooch/setup.rb
|
|
116
|
+
- lib/stealth/services/smooch/version.rb
|
|
117
|
+
- lib/stealth/smooch.rb
|
|
118
|
+
- spec/spec_helper.rb
|
|
119
|
+
- spec/version_spec.rb
|
|
120
|
+
- stealth-smooch.gemspec
|
|
121
|
+
homepage: https://github.com/hellostealth/stealth-smooch
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
124
|
+
metadata: {}
|
|
125
|
+
post_install_message:
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '0'
|
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
requirements: []
|
|
140
|
+
rubyforge_project:
|
|
141
|
+
rubygems_version: 2.7.7
|
|
142
|
+
signing_key:
|
|
143
|
+
specification_version: 4
|
|
144
|
+
summary: Stealth Smooch driver
|
|
145
|
+
test_files:
|
|
146
|
+
- spec/spec_helper.rb
|
|
147
|
+
- spec/version_spec.rb
|