stealth-facebook 0.12.0 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +72 -89
- data/LICENSE +4 -17
- data/README.md +29 -6
- data/VERSION +1 -1
- data/lib/stealth/services/facebook/client.rb +58 -17
- data/lib/stealth/services/facebook/events/message_event.rb +22 -11
- data/lib/stealth/services/facebook/events/message_reads_event.rb +49 -0
- data/lib/stealth/services/facebook/events/messaging_referral_event.rb +31 -0
- data/lib/stealth/services/facebook/events/postback_event.rb +2 -2
- data/lib/stealth/services/facebook/message_handler.rb +22 -1
- data/lib/stealth/services/facebook/reply_handler.rb +17 -7
- data/stealth-facebook.gemspec +2 -2
- metadata +15 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eae4e1dafe5f39e579df40a4d45f3c5d52761e3b5afe00a958018448a8bcaed
|
4
|
+
data.tar.gz: 8a83fbff458909bf1cb6beb18bc6a8f2879256e5bac5bd611a28bd7cf47b489a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa9b3453020fc70539bd4996bb257adf859ddd98770569f8804392a20cd9fe3147f582ca893485fff4c728ee191528a95dab5016a1b5e53a5394eb6f8152e21d
|
7
|
+
data.tar.gz: c3caf9325353e5deb40288e515c1df2464679ff6e8b711b700d3fe91859e3892e5a2f68ff54a1415fa3756f145acc475f7c8901815e0a61332a1ed0578eb4f87
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,116 +1,98 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
stealth-facebook (0.
|
5
|
-
|
6
|
-
stealth (
|
4
|
+
stealth-facebook (0.22.0)
|
5
|
+
http (~> 4.4)
|
6
|
+
stealth (>= 2.0.0.beta)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
|
12
|
-
actionview (= 5.2.1)
|
13
|
-
activesupport (= 5.2.1)
|
14
|
-
rack (~> 2.0)
|
15
|
-
rack-test (>= 0.6.3)
|
16
|
-
rails-dom-testing (~> 2.0)
|
17
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
18
|
-
actionview (5.2.1)
|
19
|
-
activesupport (= 5.2.1)
|
20
|
-
builder (~> 3.1)
|
21
|
-
erubi (~> 1.4)
|
22
|
-
rails-dom-testing (~> 2.0)
|
23
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
24
|
-
activemodel (5.2.1)
|
25
|
-
activesupport (= 5.2.1)
|
26
|
-
activerecord (5.2.1)
|
27
|
-
activemodel (= 5.2.1)
|
28
|
-
activesupport (= 5.2.1)
|
29
|
-
arel (>= 9.0)
|
30
|
-
activesupport (5.2.1)
|
11
|
+
activesupport (6.0.3.3)
|
31
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
32
13
|
i18n (>= 0.7, < 2)
|
33
14
|
minitest (~> 5.1)
|
34
15
|
tzinfo (~> 1.1)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
diff-lcs (1.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
16
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
17
|
+
addressable (2.7.0)
|
18
|
+
public_suffix (>= 2.0.2, < 5.0)
|
19
|
+
concurrent-ruby (1.1.7)
|
20
|
+
connection_pool (2.2.3)
|
21
|
+
diff-lcs (1.4.4)
|
22
|
+
domain_name (0.5.20190701)
|
23
|
+
unf (>= 0.0.5, < 1.0.0)
|
24
|
+
ffi (1.13.1)
|
25
|
+
ffi-compiler (1.0.1)
|
26
|
+
ffi (>= 1.0.0)
|
27
|
+
rake
|
28
|
+
http (4.4.1)
|
29
|
+
addressable (~> 2.3)
|
30
|
+
http-cookie (~> 1.0)
|
31
|
+
http-form_data (~> 2.2)
|
32
|
+
http-parser (~> 1.2.0)
|
33
|
+
http-cookie (1.0.3)
|
34
|
+
domain_name (~> 0.5)
|
35
|
+
http-form_data (2.3.0)
|
36
|
+
http-parser (1.2.1)
|
37
|
+
ffi-compiler (>= 1.0, < 2.0)
|
38
|
+
i18n (1.8.5)
|
45
39
|
concurrent-ruby (~> 1.0)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
mini_portile2 (~> 2.3.0)
|
57
|
-
puma (3.12.0)
|
58
|
-
rack (2.0.6)
|
59
|
-
rack-protection (2.0.4)
|
40
|
+
minitest (5.14.2)
|
41
|
+
multi_json (1.15.0)
|
42
|
+
mustermann (1.1.1)
|
43
|
+
ruby2_keywords (~> 0.0.1)
|
44
|
+
nio4r (2.5.4)
|
45
|
+
public_suffix (4.0.6)
|
46
|
+
puma (4.3.6)
|
47
|
+
nio4r (~> 2.0)
|
48
|
+
rack (2.2.3)
|
49
|
+
rack-protection (2.1.0)
|
60
50
|
rack
|
61
51
|
rack-test (1.1.0)
|
62
52
|
rack (>= 1.0, < 3)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
rake (>= 0.8.7)
|
73
|
-
thor (>= 0.19.0, < 2.0)
|
74
|
-
rake (12.3.1)
|
75
|
-
redis (4.0.2)
|
76
|
-
rspec (3.8.0)
|
77
|
-
rspec-core (~> 3.8.0)
|
78
|
-
rspec-expectations (~> 3.8.0)
|
79
|
-
rspec-mocks (~> 3.8.0)
|
80
|
-
rspec-core (3.8.0)
|
81
|
-
rspec-support (~> 3.8.0)
|
82
|
-
rspec-expectations (3.8.1)
|
53
|
+
rake (13.0.1)
|
54
|
+
redis (4.2.2)
|
55
|
+
rspec (3.9.0)
|
56
|
+
rspec-core (~> 3.9.0)
|
57
|
+
rspec-expectations (~> 3.9.0)
|
58
|
+
rspec-mocks (~> 3.9.0)
|
59
|
+
rspec-core (3.9.2)
|
60
|
+
rspec-support (~> 3.9.3)
|
61
|
+
rspec-expectations (3.9.2)
|
83
62
|
diff-lcs (>= 1.2.0, < 2.0)
|
84
|
-
rspec-support (~> 3.
|
85
|
-
rspec-mocks (3.
|
63
|
+
rspec-support (~> 3.9.0)
|
64
|
+
rspec-mocks (3.9.1)
|
86
65
|
diff-lcs (>= 1.2.0, < 2.0)
|
87
|
-
rspec-support (~> 3.
|
88
|
-
rspec-support (3.
|
66
|
+
rspec-support (~> 3.9.0)
|
67
|
+
rspec-support (3.9.3)
|
89
68
|
rspec_junit_formatter (0.4.1)
|
90
69
|
rspec-core (>= 2, < 4, != 2.12.0)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
redis (>= 3.3.5, < 5)
|
95
|
-
sinatra (2.0.4)
|
96
|
-
mustermann (~> 1.0)
|
70
|
+
ruby2_keywords (0.0.2)
|
71
|
+
sidekiq (6.1.2)
|
72
|
+
connection_pool (>= 2.2.2)
|
97
73
|
rack (~> 2.0)
|
98
|
-
|
74
|
+
redis (>= 4.2.0)
|
75
|
+
sinatra (2.1.0)
|
76
|
+
mustermann (~> 1.0)
|
77
|
+
rack (~> 2.2)
|
78
|
+
rack-protection (= 2.1.0)
|
99
79
|
tilt (~> 2.0)
|
100
|
-
stealth (
|
101
|
-
|
102
|
-
activesupport (~> 5.2)
|
80
|
+
stealth (2.0.0.beta1)
|
81
|
+
activesupport (~> 6.0)
|
103
82
|
multi_json (~> 1.12)
|
104
|
-
puma (
|
105
|
-
|
106
|
-
sidekiq (~> 5.0)
|
83
|
+
puma (>= 4.2, < 5.0)
|
84
|
+
sidekiq (~> 6.0)
|
107
85
|
sinatra (~> 2.0)
|
108
|
-
thor (~> 0
|
109
|
-
thor (0.
|
86
|
+
thor (~> 1.0)
|
87
|
+
thor (1.0.1)
|
110
88
|
thread_safe (0.3.6)
|
111
|
-
tilt (2.0.
|
112
|
-
tzinfo (1.2.
|
89
|
+
tilt (2.0.10)
|
90
|
+
tzinfo (1.2.7)
|
113
91
|
thread_safe (~> 0.1)
|
92
|
+
unf (0.1.4)
|
93
|
+
unf_ext
|
94
|
+
unf_ext (0.0.7.7)
|
95
|
+
zeitwerk (2.4.0)
|
114
96
|
|
115
97
|
PLATFORMS
|
116
98
|
ruby
|
@@ -119,7 +101,8 @@ DEPENDENCIES
|
|
119
101
|
rack-test (~> 1.1)
|
120
102
|
rspec (~> 3.6)
|
121
103
|
rspec_junit_formatter (~> 0.3)
|
104
|
+
stealth (>= 2.0.0.beta)
|
122
105
|
stealth-facebook!
|
123
106
|
|
124
107
|
BUNDLED WITH
|
125
|
-
1.
|
108
|
+
2.1.4
|
data/LICENSE
CHANGED
@@ -1,20 +1,7 @@
|
|
1
|
-
Copyright (c) 2017 Mauricio Gomes,
|
1
|
+
Copyright (c) 2017-2020 Mauricio Gomes, Mav Automation Ventures Inc.
|
2
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:
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
10
4
|
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
13
6
|
|
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.
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -12,7 +12,26 @@ When configuring your webhooks, you'll want to enable the `messages`, `messaging
|
|
12
12
|
|
13
13
|
<img src='fb-config.png' width='875px' alt='Facebook Config Screenshot' />
|
14
14
|
|
15
|
-
|
15
|
+
### message_reads
|
16
|
+
|
17
|
+
Beginning with version `0.13.0` of this gem, `message_reads` webhooks are supported. When enabled in your Facebook configuration, they will appear as `current_message.read`. That will return a hash:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
{
|
21
|
+
watermark: 1458668856253,
|
22
|
+
seq: 38
|
23
|
+
}
|
24
|
+
```
|
25
|
+
|
26
|
+
More info about `message_reads` webhooks can be found in the [Facebook Developer docs](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/message-reads).
|
27
|
+
|
28
|
+
### messaging_referrals
|
29
|
+
|
30
|
+
Facebook separates referral data for users with an existing thread versus those that have initiated a new thread. For the former case, the `messaging_referrals` webhook event is used to send the referral data. For the latter case, the referral payload is sent via the `postback` event.
|
31
|
+
|
32
|
+
For both scenarios above, this driver will include the referral payload as part of the `current_message`. It can be accessed via `current_message.referral`.
|
33
|
+
|
34
|
+
More info about `messaging_referrals` webhooks and the different referral payloads can be found in the [Facebook Developer docs](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_referrals/)
|
16
35
|
|
17
36
|
## Configure The Integration
|
18
37
|
|
@@ -68,6 +87,8 @@ test:
|
|
68
87
|
|
69
88
|
As with all Stealth integrations, integrations can be specified by environment. You'll want to replace `FACEBOOK_VERIFY_TOKEN` and `FACEBOOK_ACCESS_TOKEN` with your respective keys from your Facebook page.
|
70
89
|
|
90
|
+
You may also specify a Facebook Graph API version by setting the ENV var `FACEBOOK_API_VERSION` to API version your bot has been cofnigured to use. If none is specified, this driver will currently default to version `3.2`.
|
91
|
+
|
71
92
|
These are the supported setup options:
|
72
93
|
|
73
94
|
### greeting
|
@@ -130,9 +151,9 @@ fb_profile = Stealth::Services::Facebook::Client.fetch_profile(
|
|
130
151
|
|
131
152
|
### Analytics
|
132
153
|
|
133
|
-
If you'd like to track custom bot metrics in addition to the ones provided automatically by Facebook Analytics, you can do so
|
154
|
+
If you'd like to track custom bot metrics in addition to the ones provided automatically by Facebook Analytics, you can do so starting with version `0.12.0` of this gem.
|
134
155
|
|
135
|
-
In order to send these metrics, you'll need to
|
156
|
+
In order to send these metrics, you'll need to include the `app_id` of the bot as well as the `page_id` of the Facebook page (attached to the bot) to `services.yml`:
|
136
157
|
|
137
158
|
```yaml
|
138
159
|
default: &default
|
@@ -150,7 +171,7 @@ Then to collect a metric:
|
|
150
171
|
Stealth::Services::Facebook::Client.track(recipient_id: u.recipient_id, metric: 'name of your metric', value: 2)
|
151
172
|
```
|
152
173
|
|
153
|
-
|
174
|
+
You can specify additional options:
|
154
175
|
|
155
176
|
```ruby
|
156
177
|
Stealth::Services::Facebook::Client.track(recipient_id: u.recipient_id, metric: 'signup', value: 2, options: { 'fb_description' => 'A signup occured.' })
|
@@ -181,7 +202,7 @@ Text replies can also include suggestions, which will be rendered as quick repli
|
|
181
202
|
- text: Red
|
182
203
|
```
|
183
204
|
|
184
|
-
|
205
|
+
Text replies can also include buttons:
|
185
206
|
|
186
207
|
```yaml
|
187
208
|
- reply_type: text
|
@@ -197,7 +218,9 @@ Although not as common, text replies can also include buttons:
|
|
197
218
|
|
198
219
|
### suggestions
|
199
220
|
|
200
|
-
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. Facebook supports a few special types of quick replies,
|
221
|
+
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. Facebook supports a few special types of quick replies, discussed below.
|
222
|
+
|
223
|
+
Please note that starting with version `0.17.0` of this gem, quick replies return their response via `current_message.message` as well as `current_message.payload`. Please make sure your `route` method in `BotController` handles that accordingly.
|
201
224
|
|
202
225
|
#### Location
|
203
226
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.22.0
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'http'
|
5
5
|
|
6
6
|
require 'stealth/services/facebook/message_handler'
|
7
7
|
require 'stealth/services/facebook/reply_handler'
|
@@ -10,9 +10,13 @@ require 'stealth/services/facebook/setup'
|
|
10
10
|
module Stealth
|
11
11
|
module Services
|
12
12
|
module Facebook
|
13
|
-
|
14
13
|
class Client < Stealth::Services::BaseClient
|
15
|
-
|
14
|
+
|
15
|
+
FB_ENDPOINT = if ENV['FACEBOOK_API_VERSION'].present?
|
16
|
+
"https://graph.facebook.com/v#{ENV['FACEBOOK_API_VERSION']}/me"
|
17
|
+
else
|
18
|
+
"https://graph.facebook.com/v3.2/me"
|
19
|
+
end
|
16
20
|
|
17
21
|
attr_reader :api_endpoint, :reply
|
18
22
|
|
@@ -23,9 +27,34 @@ module Stealth
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def transmit
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
res = self
|
31
|
+
.class
|
32
|
+
.http_client
|
33
|
+
.post(api_endpoint, body: MultiJson.dump(reply))
|
34
|
+
|
35
|
+
if res.status.client_error? # HTTP 4xx error
|
36
|
+
# Messenger error sub-codes (https://developers.facebook.com/docs/messenger-platform/reference/send-api/error-codes)
|
37
|
+
case res.body
|
38
|
+
when /1545041/
|
39
|
+
raise Stealth::Errors::UserOptOut
|
40
|
+
when /2018108/
|
41
|
+
raise Stealth::Errors::UserOptOut
|
42
|
+
when /2018028/
|
43
|
+
raise Stealth::Errors::InvalidSessionID.new('Cannot message users who are not admins, developers or testers of the app until pages_messaging permission is reviewed and the app is live.')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Stealth::Logger.l(
|
48
|
+
topic: "facebook",
|
49
|
+
message: "Transmitted. Response: #{res.status.code}: #{res.body}"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.http_client
|
54
|
+
headers = {
|
55
|
+
'Content-Type' => 'application/json'
|
56
|
+
}
|
57
|
+
HTTP.timeout(connect: 15, read: 60).headers(headers)
|
29
58
|
end
|
30
59
|
|
31
60
|
def self.fetch_profile(recipient_id:, fields: nil)
|
@@ -44,13 +73,19 @@ module Stealth
|
|
44
73
|
query: query_hash.to_query
|
45
74
|
)
|
46
75
|
|
47
|
-
|
48
|
-
Stealth::Logger.l(topic:
|
76
|
+
res = http_client.get(uri.to_s)
|
77
|
+
Stealth::Logger.l(topic:
|
78
|
+
'facebook',
|
79
|
+
message: "Requested user profile for #{recipient_id}. Response: #{res.status.code}: #{res.body}"
|
80
|
+
)
|
49
81
|
|
50
|
-
if
|
51
|
-
MultiJson.load(
|
82
|
+
if res.status.success?
|
83
|
+
MultiJson.load(res.body.to_s)
|
52
84
|
else
|
53
|
-
raise(
|
85
|
+
raise(
|
86
|
+
Stealth::Errors::ServiceError,
|
87
|
+
"Facebook error #{res.status}: #{res.body}"
|
88
|
+
)
|
54
89
|
end
|
55
90
|
end
|
56
91
|
|
@@ -77,17 +112,23 @@ module Stealth
|
|
77
112
|
path: "/#{Stealth.config.facebook.app_id}/activities"
|
78
113
|
)
|
79
114
|
|
80
|
-
|
81
|
-
Stealth::Logger.l(
|
115
|
+
res = http_client.post(uri.to_s, body: MultiJson.dump(params))
|
116
|
+
Stealth::Logger.l(
|
117
|
+
topic: "facebook",
|
118
|
+
message: "Sent custom event for metric: #{metric} and value: #{value}. Response: #{res.status}: #{res.body}"
|
119
|
+
)
|
82
120
|
|
83
|
-
if
|
84
|
-
MultiJson.load(
|
121
|
+
if res.status.success?
|
122
|
+
MultiJson.load(res.body.to_s)
|
85
123
|
else
|
86
|
-
raise(
|
124
|
+
raise(
|
125
|
+
Stealth::Errors::ServiceError,
|
126
|
+
"Facebook error #{res.status}: #{res.body}"
|
127
|
+
)
|
87
128
|
end
|
88
129
|
end
|
89
|
-
end
|
90
130
|
|
131
|
+
end
|
91
132
|
end
|
92
133
|
end
|
93
134
|
end
|
@@ -23,20 +23,21 @@ module Stealth
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def fetch_message
|
26
|
-
if params
|
27
|
-
service_message.message = params
|
28
|
-
|
29
|
-
|
26
|
+
if params.dig('message', 'quick_reply').present?
|
27
|
+
service_message.message = params.dig('message', 'text')
|
28
|
+
service_message.payload = params.dig('message', 'quick_reply', 'payload')
|
29
|
+
elsif params.dig('message', 'text').present?
|
30
|
+
service_message.message = params.dig('message', 'text')
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
def fetch_location
|
34
|
-
if params
|
35
|
-
params
|
35
|
+
if params.dig('message', 'attachments').present? && params.dig('message', 'attachments').is_a?(Array)
|
36
|
+
params.dig('message', 'attachments').each do |attachment|
|
36
37
|
next unless attachment['type'] == 'location'
|
37
38
|
|
38
|
-
lat = attachment
|
39
|
-
lng = attachment
|
39
|
+
lat = attachment.dig('payload', 'coordinates', 'lat')
|
40
|
+
lng = attachment.dig('payload', 'coordinates', 'long')
|
40
41
|
|
41
42
|
service_message.location = {
|
42
43
|
lat: lat,
|
@@ -47,11 +48,21 @@ module Stealth
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def fetch_attachments
|
50
|
-
if params
|
51
|
-
params
|
51
|
+
if params.dig('message', 'attachments').present? && params.dig('message', 'attachments').is_a?(Array)
|
52
|
+
params.dig('message', 'attachments').each do |attachment|
|
53
|
+
# Seems to be a bug in Messenger, but in attachments of type `fallback`
|
54
|
+
# we are seeing the URL come in at the attachment-level rather than
|
55
|
+
# nested within the payload as the API specifies:
|
56
|
+
# https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messages
|
57
|
+
payload_url = if attachment.dig('payload', 'url').present?
|
58
|
+
attachment['payload']['url']
|
59
|
+
else
|
60
|
+
attachment['url']
|
61
|
+
end
|
62
|
+
|
52
63
|
service_message.attachments << {
|
53
64
|
type: attachment['type'],
|
54
|
-
url:
|
65
|
+
url: payload_url
|
55
66
|
}
|
56
67
|
end
|
57
68
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
module Stealth
|
7
|
+
module Services
|
8
|
+
module Facebook
|
9
|
+
|
10
|
+
class MessageReadsEvent
|
11
|
+
|
12
|
+
attr_reader :service_message, :params
|
13
|
+
|
14
|
+
def initialize(service_message:, params:)
|
15
|
+
@service_message = service_message
|
16
|
+
@params = params
|
17
|
+
end
|
18
|
+
|
19
|
+
def process
|
20
|
+
fetch_read
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def fetch_read
|
26
|
+
service_message.read = { watermark: get_timestamp, seq: params['read']['seq'] }
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_timestamp
|
30
|
+
Time.at(params['read']['watermark']/1000).to_datetime
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module ReadsEvent
|
35
|
+
attr_accessor :read
|
36
|
+
def initialize(service:)
|
37
|
+
@read = {}
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
class ServiceMessage
|
47
|
+
prepend Services::Facebook::ReadsEvent
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Stealth
|
5
|
+
module Services
|
6
|
+
module Facebook
|
7
|
+
|
8
|
+
class MessagingReferralEvent
|
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_referral
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def fetch_referral
|
24
|
+
service_message.referral = params['referral']
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -22,11 +22,11 @@ module Stealth
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def fetch_payload
|
25
|
-
service_message.payload = params
|
25
|
+
service_message.payload = params.dig('postback', 'payload')
|
26
26
|
end
|
27
27
|
|
28
28
|
def fetch_referral
|
29
|
-
service_message.referral = params
|
29
|
+
service_message.referral = params.dig('postback', 'referral')
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'stealth/services/facebook/events/message_event'
|
5
5
|
require 'stealth/services/facebook/events/postback_event'
|
6
|
+
require 'stealth/services/facebook/events/message_reads_event'
|
7
|
+
require 'stealth/services/facebook/events/messaging_referral_event'
|
6
8
|
|
7
9
|
module Stealth
|
8
10
|
module Services
|
@@ -23,7 +25,11 @@ module Stealth
|
|
23
25
|
else
|
24
26
|
# Queue the request processing so we can respond quickly to FB
|
25
27
|
# and also keep track of this message
|
26
|
-
Stealth::Services::HandleMessageJob.perform_async(
|
28
|
+
Stealth::Services::HandleMessageJob.perform_async(
|
29
|
+
'facebook',
|
30
|
+
params,
|
31
|
+
headers
|
32
|
+
)
|
27
33
|
|
28
34
|
# Relay our acceptance
|
29
35
|
[200, 'OK']
|
@@ -34,6 +40,7 @@ module Stealth
|
|
34
40
|
@service_message = ServiceMessage.new(service: 'facebook')
|
35
41
|
@facebook_message = params['entry'].first['messaging'].first
|
36
42
|
service_message.sender_id = get_sender_id
|
43
|
+
service_message.target_id = get_target_id
|
37
44
|
service_message.timestamp = get_timestamp
|
38
45
|
process_facebook_event
|
39
46
|
|
@@ -58,6 +65,10 @@ module Stealth
|
|
58
65
|
facebook_message['sender']['id']
|
59
66
|
end
|
60
67
|
|
68
|
+
def get_target_id
|
69
|
+
facebook_message['recipient']['id']
|
70
|
+
end
|
71
|
+
|
61
72
|
def get_timestamp
|
62
73
|
Time.at(facebook_message['timestamp']/1000).to_datetime
|
63
74
|
end
|
@@ -73,6 +84,16 @@ module Stealth
|
|
73
84
|
service_message: service_message,
|
74
85
|
params: facebook_message
|
75
86
|
)
|
87
|
+
elsif facebook_message['read'].present?
|
88
|
+
message_event = Stealth::Services::Facebook::MessageReadsEvent.new(
|
89
|
+
service_message: service_message,
|
90
|
+
params: facebook_message
|
91
|
+
)
|
92
|
+
elsif facebook_message['referral'].present?
|
93
|
+
message_event = Stealth::Services::Facebook::MessagingReferralEvent.new(
|
94
|
+
service_message: service_message,
|
95
|
+
params: facebook_message
|
96
|
+
)
|
76
97
|
end
|
77
98
|
|
78
99
|
message_event.process
|
@@ -363,6 +363,12 @@ module Stealth
|
|
363
363
|
quick_reply = { "content_type" => "user_phone_number" }
|
364
364
|
when 'email'
|
365
365
|
quick_reply = { "content_type" => "user_email" }
|
366
|
+
when 'birthday'
|
367
|
+
quick_reply = { "content_type" => "user_birthday" }
|
368
|
+
when 'state'
|
369
|
+
quick_reply = { "content_type" => "user_state" }
|
370
|
+
when 'zip_code'
|
371
|
+
quick_reply = { "content_type" => "user_zip_code" }
|
366
372
|
else
|
367
373
|
quick_reply = {
|
368
374
|
"content_type" => "text",
|
@@ -391,34 +397,38 @@ module Stealth
|
|
391
397
|
fb_buttons = buttons.collect do |button|
|
392
398
|
case button['type']
|
393
399
|
when 'url'
|
394
|
-
|
400
|
+
_button = {
|
395
401
|
"type" => "web_url",
|
396
402
|
"url" => button["url"],
|
397
403
|
"title" => button["text"]
|
398
404
|
}
|
399
405
|
|
400
406
|
if button["webview_height"].present?
|
401
|
-
|
407
|
+
_button["webview_height_ratio"] = button["webview_height"]
|
402
408
|
end
|
403
409
|
|
404
|
-
button
|
410
|
+
if button['messenger_extensions'].present?
|
411
|
+
_button['messenger_extensions'] = true
|
412
|
+
end
|
413
|
+
|
414
|
+
_button
|
405
415
|
|
406
416
|
when 'payload'
|
407
|
-
|
417
|
+
_button = {
|
408
418
|
"type" => "postback",
|
409
419
|
"payload" => button["payload"],
|
410
420
|
"title" => button["text"]
|
411
421
|
}
|
412
422
|
|
413
423
|
when 'call'
|
414
|
-
|
424
|
+
_button = {
|
415
425
|
"type" => "phone_number",
|
416
426
|
"payload" => button["phone_number"],
|
417
427
|
"title" => button["text"]
|
418
428
|
}
|
419
429
|
|
420
430
|
when 'nested'
|
421
|
-
|
431
|
+
_button = {
|
422
432
|
"type" => "nested",
|
423
433
|
"title" => button["text"],
|
424
434
|
"call_to_actions" => generate_buttons(buttons: button["buttons"])
|
@@ -428,7 +438,7 @@ module Stealth
|
|
428
438
|
raise(Stealth::Errors::ServiceImpaired, "Sorry, we don't yet support #{button["type"]} buttons yet!")
|
429
439
|
end
|
430
440
|
|
431
|
-
|
441
|
+
_button
|
432
442
|
end
|
433
443
|
|
434
444
|
fb_buttons
|
data/stealth-facebook.gemspec
CHANGED
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.author = 'Mauricio Gomes'
|
13
13
|
s.email = 'mauricio@edge14.com'
|
14
14
|
|
15
|
-
s.add_dependency 'stealth', '
|
16
|
-
s.add_dependency '
|
15
|
+
s.add_dependency 'stealth', '>= 2.0.0.beta'
|
16
|
+
s.add_dependency 'http', '~> 4.4'
|
17
17
|
|
18
18
|
s.add_development_dependency 'rspec', '~> 3.6'
|
19
19
|
s.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stealth-facebook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mauricio Gomes
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: stealth
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.0.0.beta
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.0.0.beta
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: http
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '4.4'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '4.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,6 +98,8 @@ files:
|
|
98
98
|
- lib/stealth/facebook.rb
|
99
99
|
- lib/stealth/services/facebook/client.rb
|
100
100
|
- lib/stealth/services/facebook/events/message_event.rb
|
101
|
+
- lib/stealth/services/facebook/events/message_reads_event.rb
|
102
|
+
- lib/stealth/services/facebook/events/messaging_referral_event.rb
|
101
103
|
- lib/stealth/services/facebook/events/postback_event.rb
|
102
104
|
- lib/stealth/services/facebook/message_handler.rb
|
103
105
|
- lib/stealth/services/facebook/reply_handler.rb
|
@@ -110,7 +112,7 @@ homepage: https://github.com/hellostealth/stealth-facebook
|
|
110
112
|
licenses:
|
111
113
|
- MIT
|
112
114
|
metadata: {}
|
113
|
-
post_install_message:
|
115
|
+
post_install_message:
|
114
116
|
rdoc_options: []
|
115
117
|
require_paths:
|
116
118
|
- lib
|
@@ -125,9 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
127
|
- !ruby/object:Gem::Version
|
126
128
|
version: '0'
|
127
129
|
requirements: []
|
128
|
-
|
129
|
-
|
130
|
-
signing_key:
|
130
|
+
rubygems_version: 3.1.2
|
131
|
+
signing_key:
|
131
132
|
specification_version: 4
|
132
133
|
summary: Stealth Facebook Messenger driver
|
133
134
|
test_files:
|