stealth 0.9.5 → 0.9.6

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: f317db5c10b48f1bcc63be86f46c4a134b65cb5e
4
- data.tar.gz: 060fd76828976b6d2548b34aca28b5c701e30b8d
3
+ metadata.gz: 5b2f50621f9aca430ba407f83791be90ddec4400
4
+ data.tar.gz: 5a2bbecfc460f3350437266dc65a9b954ea57229
5
5
  SHA512:
6
- metadata.gz: f297db3c34ae2ff00df6ce1d23253a83b8a4c74db1773fc26113baf54beb25d931bbc271eca8918706c966773deb4d6a059c4c44ea8b1744e1a21e6b05cca4eb
7
- data.tar.gz: 16121b80352c65529cb71da25048130e8a3d8b2420be44ad261400db96a41b288f1cd2a71594eec62254b8910a19311318f576dcd0aaa1cf320396dbe688589f
6
+ metadata.gz: 59aa56ef208c1a00ee25102c330b67593c087284fd96fd146327208c0152ad0015724173daddedb9e6c460eb5ccd9b0e3ba52db42d01e26798f7664f57d053e4
7
+ data.tar.gz: 1f7886d7a3acb82bd11619536744c5a5045c1718cc7f70bbc32ee7067b1bc1c14a43561e18d359234bc3f1dfe698633e6df3de25250d53ac52b35d52142b7022
data/Gemfile.lock CHANGED
@@ -1,19 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stealth (0.9.0)
4
+ stealth (0.9.5)
5
5
  activesupport (~> 5.1)
6
- faraday (~> 0.13)
7
6
  multi_json (~> 1.12)
8
- puma (~> 3.10.0)
7
+ puma (~> 3.10)
9
8
  sidekiq (~> 5.0)
10
- sinatra (~> 2.0.0)
9
+ sinatra (~> 2.0)
11
10
  thor (~> 0.20)
12
11
 
13
12
  GEM
14
13
  remote: https://rubygems.org/
15
14
  specs:
16
- activesupport (5.1.3)
15
+ activesupport (5.1.4)
17
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
17
  i18n (~> 0.7)
19
18
  minitest (~> 5.1)
@@ -21,12 +20,10 @@ GEM
21
20
  concurrent-ruby (1.0.5)
22
21
  connection_pool (2.2.1)
23
22
  diff-lcs (1.3)
24
- faraday (0.13.1)
25
- multipart-post (>= 1.2, < 3)
26
- i18n (0.8.6)
23
+ i18n (0.9.1)
24
+ concurrent-ruby (~> 1.0)
27
25
  minitest (5.10.3)
28
26
  multi_json (1.12.2)
29
- multipart-post (2.0.0)
30
27
  mustermann (1.0.1)
31
28
  oj (3.3.6)
32
29
  puma (3.10.0)
@@ -35,7 +32,7 @@ GEM
35
32
  rack
36
33
  rack-test (0.7.0)
37
34
  rack (>= 1.0, < 3)
38
- redis (3.3.3)
35
+ redis (4.0.1)
39
36
  rspec (3.6.0)
40
37
  rspec-core (~> 3.6.0)
41
38
  rspec-expectations (~> 3.6.0)
@@ -51,11 +48,11 @@ GEM
51
48
  rspec-support (3.6.0)
52
49
  rspec_junit_formatter (0.3.0)
53
50
  rspec-core (>= 2, < 4, != 2.12.0)
54
- sidekiq (5.0.4)
51
+ sidekiq (5.0.5)
55
52
  concurrent-ruby (~> 1.0)
56
53
  connection_pool (~> 2.2, >= 2.2.0)
57
54
  rack-protection (>= 1.5.0)
58
- redis (~> 3.3, >= 3.3.3)
55
+ redis (>= 3.3.4, < 5)
59
56
  sinatra (2.0.0)
60
57
  mustermann (~> 1.0)
61
58
  rack (~> 2.0)
@@ -64,7 +61,7 @@ GEM
64
61
  thor (0.20.0)
65
62
  thread_safe (0.3.6)
66
63
  tilt (2.0.8)
67
- tzinfo (1.2.3)
64
+ tzinfo (1.2.4)
68
65
  thread_safe (~> 0.1)
69
66
 
70
67
  PLATFORMS
@@ -72,9 +69,9 @@ PLATFORMS
72
69
 
73
70
  DEPENDENCIES
74
71
  oj (~> 3.3)
75
- rack-test (~> 0.7.0)
76
- rspec (~> 3.6.0)
77
- rspec_junit_formatter (~> 0.3.0)
72
+ rack-test (~> 0.7)
73
+ rspec (~> 3.6)
74
+ rspec_junit_formatter (~> 0.3)
78
75
  stealth!
79
76
 
80
77
  BUNDLED WITH
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.5
1
+ 0.9.6
data/lib/stealth/base.rb CHANGED
@@ -50,7 +50,7 @@ module Stealth
50
50
  services_config = YAML.load(ERB.new(services_yaml).result)
51
51
 
52
52
  unless services_config.has_key?(env)
53
- raise Stealth::Errors::ConfigurationError, "Could not find services.yml configuration for #{env} environment."
53
+ raise Stealth::Errors::ConfigurationError, "Could not find services.yml configuration for #{env} environment"
54
54
  end
55
55
 
56
56
  Stealth::Configuration.new(services_config[env])
@@ -27,7 +27,7 @@ module Stealth
27
27
  end
28
28
 
29
29
  def route
30
- raise(Stealth::Errors::ControllerRoutingNotImplemented, "Please implement `route` method in BotController.")
30
+ raise(Stealth::Errors::ControllerRoutingNotImplemented, "Please implement `route` method in BotController")
31
31
  end
32
32
 
33
33
  def send_replies
@@ -53,7 +53,7 @@ module Stealth
53
53
  sleep_duration = Float(reply["duration"])
54
54
  sleep(sleep_duration)
55
55
  rescue ArgumentError, TypeError
56
- raise(ArgumentError, 'Invalid duration specified. Duration must be a float.')
56
+ raise(ArgumentError, 'Invalid duration specified. Duration must be a float')
57
57
  end
58
58
  end
59
59
  end
@@ -128,7 +128,7 @@ module Stealth
128
128
  begin
129
129
  Kernel.const_get("Stealth::Services::#{current_service.capitalize}::ReplyHandler")
130
130
  rescue NameError
131
- raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized.")
131
+ raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
132
132
  end
133
133
  end
134
134
 
@@ -136,7 +136,7 @@ module Stealth
136
136
  begin
137
137
  Kernel.const_get("Stealth::Services::#{current_service.capitalize}::Client")
138
138
  rescue NameError
139
- raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized.")
139
+ raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
140
140
  end
141
141
  end
142
142
 
@@ -150,7 +150,7 @@ module Stealth
150
150
  begin
151
151
  File.read(reply_file_path)
152
152
  rescue Errno::ENOENT
153
- raise(Stealth::Errors::ReplyNotFound, "Could not find a reply in #{reply_file_path}.")
153
+ raise(Stealth::Errors::ReplyNotFound, "Could not find a reply in #{reply_file_path}")
154
154
  end
155
155
  end
156
156
 
@@ -168,7 +168,7 @@ module Stealth
168
168
 
169
169
  def get_flow_and_state(session: nil, flow: nil, state: nil)
170
170
  if session.nil? && flow.nil? && state.nil?
171
- raise(ArgumentError, "A session, flow, or state must be specified.")
171
+ raise(ArgumentError, "A session, flow, or state must be specified")
172
172
  end
173
173
 
174
174
  if session.present?
@@ -195,7 +195,7 @@ module Stealth
195
195
  if next_state.nil?
196
196
  raise(
197
197
  Stealth::Errors::InvalidStateTransitions,
198
- "The next state after #{current_session.state_string} has not yet been defined."
198
+ "The next state after #{current_session.state_string} has not yet been defined"
199
199
  )
200
200
  end
201
201
 
@@ -40,7 +40,7 @@ module Stealth
40
40
  begin
41
41
  Kernel.const_get("Stealth::Services::#{service.capitalize}::MessageHandler")
42
42
  rescue NameError
43
- raise(Stealth::Errors::ServiceNotRecognized, "The service '#{service}' was not recognized.")
43
+ raise(Stealth::Errors::ServiceNotRecognized, "The service '#{service}' was not recognized")
44
44
  end
45
45
  end
46
46
 
@@ -168,7 +168,7 @@ module Stealth
168
168
  # Reported by Kyle Burton
169
169
  if !spec.states[event.transitions_to]
170
170
  raise StealthFlowError.new("Event[#{event.name}]'s " +
171
- "transitions_to[#{event.transitions_to}] is not a declared state.")
171
+ "transitions_to[#{event.transitions_to}] is not a declared state")
172
172
  end
173
173
  end
174
174
 
@@ -17,7 +17,7 @@ module Stealth
17
17
  end
18
18
 
19
19
  get_or_post '/incoming/:service' do
20
- Stealth::Logger.l(topic: "incoming", message: "Received webhook from #{params[:service]}.")
20
+ Stealth::Logger.l(topic: "incoming", message: "Received webhook from #{params[:service]}")
21
21
 
22
22
  # JSON params need to be parsed and added to the params
23
23
  if request.env['CONTENT_TYPE'] == 'application/json'
@@ -17,12 +17,9 @@ module Stealth
17
17
  end
18
18
 
19
19
  def transmit
20
- raise(Stealth::Errors::ServiceImpaired, "Service implementation does not implement 'transmit'.")
20
+ raise(Stealth::Errors::ServiceImpaired, "Service implementation does not implement 'transmit'")
21
21
  end
22
22
 
23
23
  end
24
24
  end
25
25
  end
26
-
27
-
28
- require 'stealth/services/facebook/client'
@@ -14,7 +14,7 @@ module Stealth
14
14
 
15
15
  # Should respond with a Rack response (https://github.com/sinatra/sinatra#return-values)
16
16
  def coordinate
17
- raise(Stealth::Errors::ServiceImpaired, "Service request handler does not implement 'process'.")
17
+ raise(Stealth::Errors::ServiceImpaired, "Service request handler does not implement 'process'")
18
18
  end
19
19
 
20
20
  # After coordinate responds to the service, an optional async job
@@ -12,7 +12,7 @@ module Stealth
12
12
  @user_id = user_id
13
13
 
14
14
  unless defined?($redis)
15
- raise(Stealth::Errors::RedisNotConfigured, "Please make sure REDIS_URL is configured before using sessions.")
15
+ raise(Stealth::Errors::RedisNotConfigured, "Please make sure REDIS_URL is configured before using sessions")
16
16
  end
17
17
 
18
18
  get
data/stealth.gemspec CHANGED
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency 'puma', '~> 3.10'
17
17
  s.add_dependency 'thor', '~> 0.20'
18
18
  s.add_dependency 'multi_json', '~> 1.12'
19
- s.add_dependency 'faraday', '~> 0.13'
20
19
  s.add_dependency 'sidekiq', '~> 5.0'
21
20
  s.add_dependency 'activesupport', '~> 5.1'
22
21
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stealth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mauricio Gomes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-06 00:00:00.000000000 Z
11
+ date: 2017-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.12'
69
- - !ruby/object:Gem::Dependency
70
- name: faraday
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '0.13'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '0.13'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: sidekiq
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -192,12 +178,6 @@ files:
192
178
  - lib/stealth/services/base_client.rb
193
179
  - lib/stealth/services/base_message_handler.rb
194
180
  - lib/stealth/services/base_reply_handler.rb
195
- - lib/stealth/services/facebook/client.rb
196
- - lib/stealth/services/facebook/events/message_event.rb
197
- - lib/stealth/services/facebook/events/postback_event.rb
198
- - lib/stealth/services/facebook/message_handler.rb
199
- - lib/stealth/services/facebook/reply_handler.rb
200
- - lib/stealth/services/facebook/setup.rb
201
181
  - lib/stealth/services/jobs/handle_message_job.rb
202
182
  - lib/stealth/session.rb
203
183
  - lib/stealth/version.rb
@@ -232,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
212
  version: '0'
233
213
  requirements: []
234
214
  rubyforge_project:
235
- rubygems_version: 2.6.11
215
+ rubygems_version: 2.6.12
236
216
  signing_key:
237
217
  specification_version: 4
238
218
  summary: Ruby framework for conversational bots
@@ -1,60 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- require 'faraday'
5
-
6
- require 'stealth/services/facebook/message_handler'
7
- require 'stealth/services/facebook/reply_handler'
8
- require 'stealth/services/facebook/setup'
9
-
10
- module Stealth
11
- module Services
12
- module Facebook
13
-
14
- class Client < Stealth::Services::BaseClient
15
- FB_ENDPOINT = "https://graph.facebook.com/v2.10/me"
16
-
17
- attr_reader :api_endpoint, :reply
18
-
19
- def initialize(reply:, endpoint: 'messages')
20
- @reply = reply
21
- access_token = "access_token=#{Stealth.config.facebook.page_access_token}"
22
- @api_endpoint = [[FB_ENDPOINT, endpoint].join('/'), access_token].join('?')
23
- end
24
-
25
- def transmit
26
- headers = { "Content-Type" => "application/json" }
27
- response = Faraday.post(api_endpoint, reply.to_json, headers)
28
- Stealth::Logger.l(topic: "facebook", message: "Transmitting. Response: #{response.status}: #{response.body}")
29
- end
30
-
31
- def self.fetch_profile(recipient_id:, fields: nil)
32
- if fields.blank?
33
- fields = [:first_name, :last_name, :profile_pic, :locale, :timezone, :gender, :is_payment_enabled, :last_ad_referral]
34
- end
35
-
36
- query_hash ={
37
- fields: fields.join(','),
38
- access_token: Stealth.config.facebook.page_access_token
39
- }
40
-
41
- uri = URI::HTTPS.build(
42
- host: "graph.facebook.com",
43
- path: "/v2.10/#{recipient_id}",
44
- query: query_hash.to_query
45
- )
46
-
47
- response = Faraday.get(uri.to_s)
48
- Stealth::Logger.l(topic: "facebook", message: "Requested user profile for #{recipient_id}. Response: #{response.status}: #{response.body}")
49
-
50
- if response.status.in?(200..299)
51
- MultiJson.load(response.body)
52
- else
53
- false
54
- end
55
- end
56
- end
57
-
58
- end
59
- end
60
- end
@@ -1,59 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- module Stealth
5
- module Services
6
- module Facebook
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
- if params['message']['quick_reply'].present?
27
- service_message.message = params['message']['quick_reply']['payload']
28
- elsif params['message']['text'].present?
29
- service_message.message = params['message']['text']
30
- end
31
- end
32
-
33
- def fetch_location
34
- if params['location'].present?
35
- lat = params['location']['coordinates']['lat']
36
- lng = params['location']['coordinates']['long']
37
- service_message.location = {
38
- lat: lat,
39
- lng: lng
40
- }
41
- end
42
- end
43
-
44
- def fetch_attachments
45
- if params['attachments'].present? && params['attachments'].is_a?(Array)
46
- params['attachments'].each do |attachment|
47
- service_message.attachments << {
48
- type: attachment['type'],
49
- url: attachment['payload']['url']
50
- }
51
- end
52
- end
53
- end
54
-
55
- end
56
-
57
- end
58
- end
59
- end
@@ -1,36 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- module Stealth
5
- module Services
6
- module Facebook
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
- fetch_referral
20
- end
21
-
22
- private
23
-
24
- def fetch_payload
25
- service_message.payload = params['postback']['payload']
26
- end
27
-
28
- def fetch_referral
29
- service_message.referral = params['postback']['referral']
30
- end
31
-
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,84 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- require 'stealth/services/facebook/events/message_event'
5
- require 'stealth/services/facebook/events/postback_event'
6
-
7
- module Stealth
8
- module Services
9
- module Facebook
10
-
11
- class MessageHandler < Stealth::Services::BaseMessageHandler
12
-
13
- attr_reader :service_message, :params, :headers, :facebook_message
14
-
15
- def initialize(params:, headers:)
16
- @params = params
17
- @headers = headers
18
- end
19
-
20
- def coordinate
21
- if facebook_is_validating_webhook?
22
- respond_with_validation
23
- else
24
- # Queue the request processing so we can respond quickly to FB
25
- # and also keep track of this message
26
- Stealth::Services::HandleMessageJob.perform_async('facebook', params, {})
27
-
28
- # Relay our acceptance
29
- [200, 'OK']
30
- end
31
- end
32
-
33
- def process
34
- @service_message = ServiceMessage.new(service: 'facebook')
35
- @facebook_message = params['entry'].first['messaging'].first
36
- service_message.sender_id = get_sender_id
37
- service_message.timestamp = get_timestamp
38
- process_facebook_event
39
-
40
- service_message
41
- end
42
-
43
- private
44
-
45
- def facebook_is_validating_webhook?
46
- params['hub.verify_token'].present?
47
- end
48
-
49
- def respond_with_validation
50
- if params['hub.verify_token'] == Stealth.config.facebook.verify_token
51
- [200, params['hub.challenge']]
52
- else
53
- [401, "Verify token did not match environment variable."]
54
- end
55
- end
56
-
57
- def get_sender_id
58
- facebook_message['sender']['id']
59
- end
60
-
61
- def get_timestamp
62
- Time.at(facebook_message['timestamp']).to_datetime
63
- end
64
-
65
- def process_facebook_event
66
- if facebook_message['message'].present?
67
- message_event = Stealth::Services::Facebook::MessageEvent.new(
68
- service_message: service_message,
69
- params: facebook_message
70
- )
71
- elsif facebook_message['postback'].present?
72
- message_event = Stealth::Services::Facebook::PostbackEvent.new(
73
- service_message: service_message,
74
- params: facebook_message
75
- )
76
- end
77
-
78
- message_event.process
79
- end
80
- end
81
-
82
- end
83
- end
84
- end
@@ -1,480 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- module Stealth
5
- module Services
6
- module Facebook
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
- check_if_arguments_are_valid!(
19
- suggestions: reply['suggestions'],
20
- buttons: reply['buttons']
21
- )
22
-
23
- template = unstructured_template
24
- template['message']['text'] = reply['text']
25
-
26
- if reply['suggestions'].present?
27
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
28
- template["message"]["quick_replies"] = fb_suggestions
29
- end
30
-
31
- # If buttons are present, we need to convert this to a button template
32
- if reply['buttons'].present?
33
- template['message'].delete('text')
34
-
35
- fb_buttons = generate_buttons(buttons: reply['buttons'])
36
- attachment = button_attachment_template(text: reply['text'], buttons: fb_buttons)
37
- template['message']['attachment'] = attachment
38
- end
39
-
40
- template
41
- end
42
-
43
- def image
44
- check_if_arguments_are_valid!(
45
- suggestions: reply['suggestions'],
46
- buttons: reply['buttons']
47
- )
48
-
49
- template = unstructured_template
50
- attachment = attachment_template(
51
- attachment_type: 'image',
52
- attachment_url: reply['image_url']
53
- )
54
- template['message']['attachment'] = attachment
55
-
56
- if reply['suggestions'].present?
57
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
58
- template["message"]["quick_replies"] = fb_suggestions
59
- end
60
-
61
- template
62
- end
63
-
64
- def audio
65
- check_if_arguments_are_valid!(
66
- suggestions: reply['suggestions'],
67
- buttons: reply['buttons']
68
- )
69
-
70
- template = unstructured_template
71
- attachment = attachment_template(
72
- attachment_type: 'audio',
73
- attachment_url: reply['audio_url']
74
- )
75
- template['message']['attachment'] = attachment
76
-
77
- if reply['suggestions'].present?
78
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
79
- template["message"]["quick_replies"] = fb_suggestions
80
- end
81
-
82
- template
83
- end
84
-
85
- def video
86
- check_if_arguments_are_valid!(
87
- suggestions: reply['suggestions'],
88
- buttons: reply['buttons']
89
- )
90
-
91
- template = unstructured_template
92
- attachment = attachment_template(
93
- attachment_type: 'video',
94
- attachment_url: reply['video_url']
95
- )
96
- template['message']['attachment'] = attachment
97
-
98
- if reply['suggestions'].present?
99
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
100
- template["message"]["quick_replies"] = fb_suggestions
101
- end
102
-
103
- template
104
- end
105
-
106
- def file
107
- check_if_arguments_are_valid!(
108
- suggestions: reply['suggestions'],
109
- buttons: reply['buttons']
110
- )
111
-
112
- template = unstructured_template
113
- attachment = attachment_template(
114
- attachment_type: 'file',
115
- attachment_url: reply['file_url']
116
- )
117
- template['message']['attachment'] = attachment
118
-
119
- if reply['suggestions'].present?
120
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
121
- template["message"]["quick_replies"] = fb_suggestions
122
- end
123
-
124
- template
125
- end
126
-
127
- def cards
128
- template = card_template(
129
- sharable: reply["sharable"],
130
- aspect_ratio: reply["aspect_ratio"]
131
- )
132
-
133
- fb_elements = generate_card_elements(elements: reply["elements"])
134
- template["message"]["attachment"]["payload"]["elements"] = fb_elements
135
-
136
- if reply['suggestions'].present?
137
- fb_suggestions = generate_suggestions(suggestions: reply['suggestions'])
138
- template["message"]["quick_replies"] = fb_suggestions
139
- end
140
-
141
- template
142
- end
143
-
144
- def list
145
- template = list_template(
146
- top_element_style: reply["top_element_style"]
147
- )
148
-
149
- fb_elements = generate_list_elements(elements: reply["elements"])
150
- template["message"]["attachments"]["payload"]["elements"] = fb_elements
151
-
152
- if reply["buttons"].present?
153
- if reply["buttons"].size > 1
154
- raise(ArgumentError, "Facebook lists support a single button attached to the list itsef.")
155
- end
156
-
157
- template["message"]["attachments"]["payload"]["buttons"] = generate_buttons(buttons: reply["buttons"])
158
- end
159
-
160
- template
161
- end
162
-
163
- def mark_seen
164
- sender_action_template(action: 'mark_seen')
165
- end
166
-
167
- def enable_typing_indicator
168
- sender_action_template(action: 'typing_on')
169
- end
170
-
171
- def disable_typing_indicator
172
- sender_action_template(action: 'typing_off')
173
- end
174
-
175
- def delay
176
- enable_typing_indicator
177
- end
178
-
179
- # generates property/value pairs required to set the profile
180
- def messenger_profile
181
- unless Stealth.config.facebook.setup.present?
182
- raise Stealth::Errors::ConfigurationError, "Setup for Facebook is not specified in services.yml."
183
- end
184
-
185
- profile = {}
186
- Stealth.config.facebook.setup.each do |profile_option, _|
187
- profile[profile_option] = self.send(profile_option)
188
- end
189
-
190
- profile
191
- end
192
-
193
- private
194
-
195
- def unstructured_template
196
- {
197
- "recipient" => {
198
- "id" => recipient_id
199
- },
200
- "message" => { }
201
- }
202
- end
203
-
204
- def card_template(sharable: nil, aspect_ratio: nil)
205
- template = {
206
- "recipient" => {
207
- "id" => recipient_id
208
- },
209
- "message" => {
210
- "attachment" => {
211
- "type" => "template",
212
- "payload" => {
213
- "template_type" => "generic",
214
- "elements" => []
215
- }
216
- }
217
- }
218
- }
219
-
220
- if sharable.present?
221
- template["message"]["payload"]["sharable"] = sharable
222
- end
223
-
224
- if aspect_ratio.present?
225
- template["message"]["payload"]["image_aspect_ratio"] = aspect_ratio
226
- end
227
-
228
- template
229
- end
230
-
231
- def list_template(top_element_style: nil, buttons: [])
232
- template = {
233
- "recipient" => {
234
- "id" => recipient_id
235
- },
236
- "message" => {
237
- "attachment" => {
238
- "type" => "template",
239
- "payload" => {
240
- "template_type" => "list",
241
- "elements" => []
242
- }
243
- }
244
- }
245
- }
246
-
247
- if top_element_style.present?
248
- unless ['large', 'compact'].include?(top_element_style)
249
- raise(ArgumentError, "Facebook list replies only support 'large' or 'compact' as the top_element_style.")
250
- end
251
-
252
- template["message"]["payload"]["top_element_style"] = top_element_style
253
- end
254
-
255
- if buttons.present?
256
- unless buttons.size > 1
257
- raise(ArgumentError, "Facebook lists only support a single button in the top element.")
258
- end
259
-
260
- template["message"]["payload"]["buttons"] = aspect_ratio
261
- end
262
-
263
- template
264
- end
265
-
266
- def element_template(element_type:, element:)
267
- unless element["title"].present?
268
- raise(ArgumentError, "Facebook card and list elements must have a 'title' attribute.")
269
- end
270
-
271
- template = {
272
- "title" => element["title"]
273
- }
274
-
275
- if element["subtitle"].present?
276
- template["subtitle"] = element["subtitle"]
277
- end
278
-
279
- if element["image_url"].present?
280
- template["image_url"] = element["image_url"]
281
- end
282
-
283
- if element["default_action"].present?
284
- default_action = generate_default_action(action_params: element["default_action"])
285
- template["default_action"] = default_action
286
- end
287
-
288
- if element["buttons"].present?
289
- if element_type == 'card' && element["buttons"].size > 3
290
- raise(ArgumentError, "Facebook card elements only support 3 buttons.")
291
- end
292
-
293
- if element_type == 'list' && element["buttons"].size > 1
294
- raise(ArgumentError, "Facebook list elements only support 1 button.")
295
- end
296
-
297
- fb_buttons = generate_buttons(buttons: element["buttons"])
298
- template["buttons"] = fb_buttons
299
- end
300
-
301
- template
302
- end
303
-
304
- def attachment_template(attachment_type:, attachment_url:)
305
- {
306
- "type" => attachment_type,
307
- "payload" => {
308
- "url" => attachment_url
309
- }
310
- }
311
- end
312
-
313
- def button_attachment_template(text:, buttons:)
314
- {
315
- "type" => "template",
316
- "payload" => {
317
- "template_type" => "button",
318
- "text" => text,
319
- "buttons" => buttons
320
- }
321
- }
322
- end
323
-
324
- def sender_action_template(action:)
325
- {
326
- "recipient" => {
327
- "id" => recipient_id
328
- },
329
- "sender_action" => action
330
- }
331
- end
332
-
333
- def generate_card_elements(elements:)
334
- if elements.size > 10
335
- raise(ArgumentError, "Facebook cards can have at most 10 cards.")
336
- end
337
-
338
- fb_elements = elements.collect do |element|
339
- element_template(element_type: 'card', element: element)
340
- end
341
-
342
- fb_elements
343
- end
344
-
345
- def generate_list_elements(elements:)
346
- if elements.size < 2 || elements.size > 4
347
- raise(ArgumentError, "Facebook lists must have 2-4 elements.")
348
- end
349
-
350
- fb_elements = elements.collect do |element|
351
- element_template(element_type: 'list', element: element)
352
- end
353
-
354
- fb_elements
355
- end
356
-
357
- def generate_suggestions(suggestions:)
358
- quick_replies = suggestions.collect do |suggestion|
359
- # If the user selected a location-type button, no other info needed
360
- if suggestion["type"] == 'location'
361
- quick_reply = { "content_type" => "location" }
362
-
363
- # Facebook only supports these two types for now
364
- else
365
- quick_reply = {
366
- "content_type" => "text",
367
- "title" => suggestion["text"]
368
- }
369
-
370
- if suggestion["payload"].present?
371
- quick_reply["payload"] = suggestion["payload"]
372
- else
373
- quick_reply["payload"] = suggestion["text"]
374
- end
375
-
376
- if suggestion["image_url"].present?
377
- quick_reply["image_url"] = suggestion["image_url"]
378
- end
379
- end
380
-
381
- quick_reply
382
- end
383
-
384
- quick_replies
385
- end
386
-
387
- # Requires adding support for Buy, Log In, Log Out, and Share button types
388
- def generate_buttons(buttons:)
389
- fb_buttons = buttons.collect do |button|
390
- case button['type']
391
- when 'url'
392
- button = {
393
- "type" => "web_url",
394
- "url" => button["url"],
395
- "title" => button["text"]
396
- }
397
-
398
- if button["webview_height"].present?
399
- button["webview_height_ratio"] = button["webview_height"]
400
- end
401
-
402
- button
403
-
404
- when 'payload'
405
- button = {
406
- "type" => "postback",
407
- "payload" => button["payload"],
408
- "title" => button["text"]
409
- }
410
-
411
- when 'call'
412
- button = {
413
- "type" => "phone_number",
414
- "payload" => button["phone_number"],
415
- "title" => button["text"]
416
- }
417
-
418
- when 'nested'
419
- button = {
420
- "type" => "nested",
421
- "title" => button["text"],
422
- "call_to_actions" => generate_buttons(buttons: button["buttons"])
423
- }
424
-
425
- else
426
- raise(Stealth::Errors::ServiceImpaired, "Sorry, we don't yet support #{button["type"]} buttons yet!")
427
- end
428
-
429
- button
430
- end
431
-
432
- fb_buttons
433
- end
434
-
435
- def generate_default_action(action_params:)
436
- default_action = {
437
- "type" => "web_url",
438
- "url" => action_params["url"]
439
- }
440
-
441
- if action_params["webview_height"].present?
442
- action_params["webview_height_ratio"] = action_params["webview_height"]
443
- end
444
-
445
- default_action
446
- end
447
-
448
- def check_if_arguments_are_valid!(suggestions:, buttons:)
449
- if suggestions.present? && buttons.present?
450
- raise(ArgumentError, "A reply cannot have buttons and suggestions!")
451
- end
452
- end
453
-
454
- def greeting
455
- Stealth.config.facebook.setup.greeting.map do |greeting|
456
- {
457
- "locale" => greeting["locale"],
458
- "text" => greeting["text"]
459
- }
460
- end
461
- end
462
-
463
- def persistent_menu
464
- Stealth.config.facebook.setup.persistent_menu.map do |persistent_menu|
465
- {
466
- "locale" => persistent_menu['locale'],
467
- "composer_input_disabled" => (persistent_menu['composer_input_disabled'] || false),
468
- "call_to_actions" => generate_buttons(buttons: persistent_menu['call_to_actions'])
469
- }
470
- end
471
- end
472
-
473
- def get_started
474
- Stealth.config.facebook.setup.get_started
475
- end
476
- end
477
-
478
- end
479
- end
480
- end
@@ -1,25 +0,0 @@
1
- # coding: utf-8
2
- # frozen_string_literal: true
3
-
4
- require 'stealth/services/facebook/client'
5
-
6
- module Stealth
7
- module Services
8
- module Facebook
9
-
10
- class Setup
11
-
12
- class << self
13
- def trigger
14
- reply_handler = Stealth::Services::Facebook::ReplyHandler.new
15
- reply = reply_handler.messenger_profile
16
- client = Stealth::Services::Facebook::Client.new(reply: reply, endpoint: 'messenger_profile')
17
- client.transmit
18
- end
19
- end
20
-
21
- end
22
-
23
- end
24
- end
25
- end