stealth 0.9.5 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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