bobot 1.0.52

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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +66 -0
  4. data/Rakefile +19 -0
  5. data/app/controllers/bobot/application_controller.rb +5 -0
  6. data/app/controllers/bobot/webhook_controller.rb +76 -0
  7. data/app/jobs/bobot/application_job.rb +4 -0
  8. data/app/jobs/bobot/commander_job.rb +9 -0
  9. data/app/jobs/bobot/deliver_job.rb +16 -0
  10. data/app/mailers/bobot/application_mailer.rb +6 -0
  11. data/app/models/bobot/application_record.rb +5 -0
  12. data/config/locales/bobot.en.yml +28 -0
  13. data/config/routes.rb +6 -0
  14. data/lib/bobot.rb +18 -0
  15. data/lib/bobot/buttons.rb +168 -0
  16. data/lib/bobot/commander.rb +68 -0
  17. data/lib/bobot/configuration.rb +206 -0
  18. data/lib/bobot/engine.rb +33 -0
  19. data/lib/bobot/error_parser.rb +102 -0
  20. data/lib/bobot/event.rb +40 -0
  21. data/lib/bobot/events/account_linking.rb +15 -0
  22. data/lib/bobot/events/common.rb +170 -0
  23. data/lib/bobot/events/delivery.rb +20 -0
  24. data/lib/bobot/events/message.rb +72 -0
  25. data/lib/bobot/events/message_echo.rb +8 -0
  26. data/lib/bobot/events/optin.rb +11 -0
  27. data/lib/bobot/events/postback.rb +20 -0
  28. data/lib/bobot/events/read.rb +15 -0
  29. data/lib/bobot/events/referral.rb +33 -0
  30. data/lib/bobot/exceptions.rb +73 -0
  31. data/lib/bobot/graph_facebook.rb +90 -0
  32. data/lib/bobot/profile.rb +23 -0
  33. data/lib/bobot/subscription.rb +19 -0
  34. data/lib/bobot/user.rb +13 -0
  35. data/lib/bobot/version.rb +14 -0
  36. data/lib/generators/bobot/install_generator.rb +28 -0
  37. data/lib/generators/bobot/templates/app/bobot/message.rb +3 -0
  38. data/lib/generators/bobot/templates/app/bobot/postback.rb +22 -0
  39. data/lib/generators/bobot/templates/app/bobot/workflow.rb +17 -0
  40. data/lib/generators/bobot/templates/config/bobot.yml +39 -0
  41. data/lib/generators/bobot/templates/config/initializers/bobot.rb +30 -0
  42. data/lib/generators/bobot/templates/config/locales/bobot.en.yml +30 -0
  43. data/lib/generators/bobot/templates/config/locales/bobot.fr.yml +29 -0
  44. data/lib/generators/bobot/uninstall_generator.rb +24 -0
  45. data/lib/generators/bobot/utils.rb +30 -0
  46. data/lib/tasks/bobot_tasks.rake +11 -0
  47. data/spec/bobot/bobot_spec.rb +24 -0
  48. data/spec/bobot/event/account_linking_spec.rb +59 -0
  49. data/spec/bobot/event/common_spec.rb +259 -0
  50. data/spec/bobot/event/delivery_spec.rb +62 -0
  51. data/spec/bobot/event/message_echo_spec.rb +276 -0
  52. data/spec/bobot/event/message_spec.rb +276 -0
  53. data/spec/bobot/event/optin_spec.rb +50 -0
  54. data/spec/bobot/event/postback_spec.rb +94 -0
  55. data/spec/bobot/event/read_spec.rb +51 -0
  56. data/spec/bobot/event/referral_spec.rb +66 -0
  57. data/spec/bobot/event_spec.rb +167 -0
  58. data/spec/bobot/install_generator_spec.rb +43 -0
  59. data/spec/bobot/profile_spec.rb +170 -0
  60. data/spec/bobot/subscription_spec.rb +139 -0
  61. data/spec/bobot/user_spec.rb +91 -0
  62. data/spec/controllers/bobot/application_controller_spec.rb +4 -0
  63. data/spec/controllers/bobot/webhook_controller_spec.rb +5 -0
  64. data/spec/dummy/Rakefile +6 -0
  65. data/spec/dummy/app/assets/config/manifest.js +3 -0
  66. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  67. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  68. data/spec/dummy/app/bobot/workflow.rb +17 -0
  69. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  70. data/spec/dummy/app/jobs/application_job.rb +2 -0
  71. data/spec/dummy/app/models/application_record.rb +3 -0
  72. data/spec/dummy/bin/bundle +3 -0
  73. data/spec/dummy/bin/rails +4 -0
  74. data/spec/dummy/bin/rake +4 -0
  75. data/spec/dummy/bin/setup +35 -0
  76. data/spec/dummy/bin/update +29 -0
  77. data/spec/dummy/config.ru +5 -0
  78. data/spec/dummy/config/application.rb +30 -0
  79. data/spec/dummy/config/bobot.yml +27 -0
  80. data/spec/dummy/config/boot.rb +5 -0
  81. data/spec/dummy/config/database.yml +19 -0
  82. data/spec/dummy/config/environment.rb +5 -0
  83. data/spec/dummy/config/environments/development.rb +42 -0
  84. data/spec/dummy/config/environments/production.rb +78 -0
  85. data/spec/dummy/config/environments/test.rb +38 -0
  86. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  87. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  88. data/spec/dummy/config/initializers/bobot.rb +30 -0
  89. data/spec/dummy/config/initializers/cors.rb +16 -0
  90. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  91. data/spec/dummy/config/initializers/inflections.rb +16 -0
  92. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  93. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  94. data/spec/dummy/config/locales/bobot.en.yml +28 -0
  95. data/spec/dummy/config/locales/bobot.fr.yml +27 -0
  96. data/spec/dummy/config/locales/en.yml +33 -0
  97. data/spec/dummy/config/puma.rb +56 -0
  98. data/spec/dummy/config/routes.rb +3 -0
  99. data/spec/dummy/config/secrets.yml +32 -0
  100. data/spec/dummy/config/spring.rb +6 -0
  101. data/spec/dummy/db/schema.rb +15 -0
  102. data/spec/examples.txt +111 -0
  103. data/spec/helpers/graph_api_helpers.rb +6 -0
  104. data/spec/jobs/bobot/commander_job_spec.rb +31 -0
  105. data/spec/lint/rubocop_spec.rb +8 -0
  106. data/spec/rails_helper.rb +67 -0
  107. data/spec/spec_helper.rb +105 -0
  108. data/spec/travis/database.travis.mysql.yml +19 -0
  109. metadata +251 -0
@@ -0,0 +1,66 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Bobot::Event::Referral do
4
+ let :payload do
5
+ {
6
+ 'sender' => {
7
+ 'id' => '3'
8
+ },
9
+ 'recipient' => {
10
+ 'id' => '3'
11
+ },
12
+ 'timestamp' => 1_458_692_752_478,
13
+ 'referral' => {
14
+ 'ref' => 'my-ref-value',
15
+ 'source' => 'SHORTLINK',
16
+ 'type' => 'OPEN_THREAD'
17
+ }
18
+ }
19
+ end
20
+
21
+ subject { Bobot::Event::Referral.new(payload) }
22
+
23
+ describe '.messaging' do
24
+ it 'returns the original payload' do
25
+ expect(subject.messaging).to eq(payload)
26
+ end
27
+ end
28
+
29
+ describe '.sender' do
30
+ it 'returns the sender' do
31
+ expect(subject.sender).to eq(payload['sender'])
32
+ end
33
+ end
34
+
35
+ describe '.recipient' do
36
+ it 'returns the recipient' do
37
+ expect(subject.recipient).to eq(payload['recipient'])
38
+ end
39
+ end
40
+
41
+ describe '.sent_at' do
42
+ it 'returns when the message was sent' do
43
+ expect(subject.sent_at).to eq(Time.zone.at(payload['timestamp'] / 1000))
44
+ end
45
+ end
46
+
47
+ describe '.referral' do
48
+ it 'returns the ref value' do
49
+ expect(subject.referral.ref).to eq(payload['referral']['ref'])
50
+ end
51
+
52
+ it 'returns the source value' do
53
+ expect(subject.referral.source).to eq(payload['referral']['source'])
54
+ end
55
+
56
+ it 'returns the type value' do
57
+ expect(subject.referral.type).to eq(payload['referral']['type'])
58
+ end
59
+ end
60
+
61
+ describe '.ref' do
62
+ it 'returns the ref value' do
63
+ expect(subject.ref).to eq(payload['referral']['ref'])
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,167 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Bobot::Event do
4
+ describe '.parse' do
5
+ context 'when the payload is unknown' do
6
+ let :payload do
7
+ {
8
+ 'foo' => 'bar'
9
+ }
10
+ end
11
+
12
+ it 'raises UnknownPayload' do
13
+ expect { subject.parse(payload) }.to raise_error(
14
+ Bobot::Event::UnknownPayload, payload.to_s
15
+ )
16
+ end
17
+ end
18
+
19
+ context 'when the payload is a message' do
20
+ let :payload do
21
+ {
22
+ 'sender' => {
23
+ 'id' => '2'
24
+ },
25
+ 'recipient' => {
26
+ 'id' => '3'
27
+ },
28
+ 'timestamp' => 145_776_419_762_7,
29
+ 'message' => {
30
+ 'mid' => 'mid.1457764197618:41d102a3e1ae206a38',
31
+ 'seq' => 73,
32
+ 'text' => 'Hello, bot!'
33
+ }
34
+ }
35
+ end
36
+
37
+ it 'returns an Event::Message' do
38
+ expect(subject.parse(payload)).to be_a(
39
+ Bobot::Event::Message
40
+ )
41
+ end
42
+ end
43
+
44
+ context 'when the payload is a message echo' do
45
+ let :payload do
46
+ {
47
+ 'sender' => {
48
+ 'id' => '2'
49
+ },
50
+ 'recipient' => {
51
+ 'id' => '3'
52
+ },
53
+ 'timestamp' => 145_776_419_762_7,
54
+ 'message' => {
55
+ 'is_echo' => true,
56
+ 'mid' => 'mid.1457764197618:41d102a3e1ae206a38',
57
+ 'seq' => 73,
58
+ 'text' => 'Hello, bot!'
59
+ }
60
+ }
61
+ end
62
+
63
+ it 'returns an Event::MessageEcho' do
64
+ expect(subject.parse(payload)).to be_a(
65
+ Bobot::Event::MessageEcho
66
+ )
67
+ end
68
+ end
69
+
70
+ context 'when the payload is a delivery' do
71
+ let :payload do
72
+ {
73
+ 'sender' => {
74
+ 'id' => '3'
75
+ },
76
+ 'recipient' => {
77
+ 'id' => '3'
78
+ },
79
+ 'delivery' => {
80
+ 'mids' => [
81
+ 'mid.1457764197618:41d102a3e1ae206a38',
82
+ 'mid.1458668856218:ed81099e15d3f4f233'
83
+ ],
84
+ 'watermark' => 145_866_885_625_3,
85
+ 'seq' => 37
86
+ }
87
+ }
88
+ end
89
+
90
+ it 'returns an Event::Delivery' do
91
+ expect(subject.parse(payload)).to be_a(
92
+ Bobot::Event::Delivery
93
+ )
94
+ end
95
+ end
96
+
97
+ context 'when the payload is a postback' do
98
+ let :payload do
99
+ {
100
+ 'sender' => {
101
+ 'id' => '3'
102
+ },
103
+ 'recipient' => {
104
+ 'id' => '3'
105
+ },
106
+ 'timestamp' => 145_776_419_762_7,
107
+ 'postback' => {
108
+ 'payload' => 'USER_DEFINED_PAYLOAD'
109
+ }
110
+ }
111
+ end
112
+
113
+ it 'returns an Event::Postback' do
114
+ expect(subject.parse(payload)).to be_a(
115
+ Bobot::Event::Postback
116
+ )
117
+ end
118
+ end
119
+
120
+ context 'when the payload is an optin' do
121
+ let :payload do
122
+ {
123
+ 'sender' => {
124
+ 'id' => '3'
125
+ },
126
+ 'recipient' => {
127
+ 'id' => '3'
128
+ },
129
+ 'timestamp' => 145_776_419_762_7,
130
+ 'optin' => {
131
+ 'ref' => 'PASS_THROUGH_PARAM'
132
+ }
133
+ }
134
+ end
135
+
136
+ it 'returns an Event::Optin' do
137
+ expect(subject.parse(payload)).to be_a(
138
+ Bobot::Event::Optin
139
+ )
140
+ end
141
+ end
142
+
143
+ context 'when the payload is a read' do
144
+ let :payload do
145
+ {
146
+ 'sender' => {
147
+ 'id' => '3'
148
+ },
149
+ 'recipient' => {
150
+ 'id' => '3'
151
+ },
152
+ 'timestamp' => 145_776_419_762_7,
153
+ 'read' => {
154
+ 'watermark' => 145_866_885_625_3,
155
+ 'seq' => 38
156
+ }
157
+ }
158
+ end
159
+
160
+ it 'returns an Event::Read' do
161
+ expect(subject.parse(payload)).to be_a(
162
+ Bobot::Event::Read
163
+ )
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,43 @@
1
+ require 'rails_helper'
2
+ require 'generators/bobot/install_generator'
3
+
4
+ RSpec.describe Bobot::InstallGenerator, type: :generator do
5
+ destination File.expand_path('../../dummy/tmp/generator', __FILE__)
6
+ arguments ['bot']
7
+
8
+ before do
9
+ prepare_destination
10
+ end
11
+
12
+ it 'mounts Bobot as Engine and generates Bobot Initializer' do
13
+ expect_any_instance_of(generator_class).to receive(:route).
14
+ with('mount Bobot::Engine => \'/bot\', as: \'bot\'')
15
+ silence_stream(STDOUT) do
16
+ generator.invoke('install')
17
+ end
18
+ expect(destination_root).to have_structure {
19
+ directory 'config' do
20
+ directory 'initializers' do
21
+ file 'bobot.rb' do
22
+ contains 'Bobot.configure'
23
+ end
24
+ end
25
+ directory 'locales' do
26
+ file 'bobot.en.yml' do
27
+ contains 'en:'
28
+ end
29
+ file 'bobot.fr.yml' do
30
+ contains 'fr:'
31
+ end
32
+ end
33
+ end
34
+ directory 'app' do
35
+ directory 'bobot' do
36
+ file 'workflow.rb' do
37
+ contains 'Bobot::Commander'
38
+ end
39
+ end
40
+ end
41
+ }
42
+ end
43
+ end
@@ -0,0 +1,170 @@
1
+ require 'rails_helper'
2
+ require 'helpers/graph_api_helpers'
3
+
4
+ RSpec.describe Bobot::Profile do
5
+ let(:access_token) { 'access token' }
6
+
7
+ let(:messenger_profile_url) do
8
+ File.join(
9
+ described_class::GRAPH_FB_URL,
10
+ described_class::GRAPH_FB_VERSION,
11
+ '/me/messenger_profile'
12
+ )
13
+ end
14
+
15
+ before do
16
+ Bobot.page_access_token = access_token
17
+ end
18
+
19
+ describe '.set' do
20
+ context 'with a successful response' do
21
+ before do
22
+ stub_request(:post, messenger_profile_url)
23
+ .with(
24
+ query: {
25
+ access_token: access_token
26
+ },
27
+ body: ActiveSupport::JSON.encode(
28
+ get_started: {
29
+ payload: 'GET_STARTED_PAYLOAD'
30
+ }
31
+ )
32
+ )
33
+ .to_return(
34
+ body: ActiveSupport::JSON.encode(
35
+ result: 'Successfully added Get Started button'
36
+ ),
37
+ status: :ok,
38
+ headers: default_graph_api_response_headers
39
+ )
40
+ end
41
+
42
+ it 'returns hash' do
43
+ expect(
44
+ subject.set(
45
+ body: {
46
+ get_started: {
47
+ payload: 'GET_STARTED_PAYLOAD'
48
+ }
49
+ },
50
+ query: {
51
+ access_token: access_token
52
+ }
53
+ )
54
+ ).to include('result' => 'Successfully added Get Started button')
55
+ end
56
+ end
57
+
58
+ context 'with an unsuccessful response' do
59
+ let(:error_message) { 'Invalid OAuth access token.' }
60
+
61
+ before do
62
+ stub_request(:post, messenger_profile_url)
63
+ .with(query: { access_token: access_token })
64
+ .to_return(
65
+ body: ActiveSupport::JSON.encode(
66
+ 'error' => {
67
+ 'message' => error_message,
68
+ 'type' => 'OAuthException',
69
+ 'code' => 190,
70
+ 'fbtrace_id' => 'Hlssg2aiVlN'
71
+ }
72
+ ),
73
+ status: :ok,
74
+ headers: default_graph_api_response_headers
75
+ )
76
+ end
77
+
78
+ it 'raises an error' do
79
+ expect do
80
+ subject.set(
81
+ body: {
82
+ get_started: {
83
+ payload: 'GET_STARTED_PAYLOAD'
84
+ }
85
+ },
86
+ query: {
87
+ access_token: access_token
88
+ }
89
+ )
90
+ end.to raise_error(Bobot::AccessTokenError)
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '.unset' do
96
+ context 'with a successful response' do
97
+ before do
98
+ stub_request(:delete, messenger_profile_url)
99
+ .with(
100
+ query: {
101
+ access_token: access_token
102
+ },
103
+ body: ActiveSupport::JSON.encode(
104
+ fields: [
105
+ 'get_started'
106
+ ]
107
+ )
108
+ )
109
+ .to_return(
110
+ body: ActiveSupport::JSON.encode(
111
+ result: 'Successfully deleted Get Started button'
112
+ ),
113
+ status: :ok,
114
+ headers: default_graph_api_response_headers
115
+ )
116
+ end
117
+
118
+ it 'returns hash' do
119
+ expect(
120
+ subject.unset(
121
+ body: {
122
+ fields: [
123
+ 'get_started'
124
+ ]
125
+ },
126
+ query: {
127
+ access_token: access_token
128
+ }
129
+ )
130
+ ).to include('result' => 'Successfully deleted Get Started button')
131
+ end
132
+ end
133
+
134
+ context 'with an unsuccessful response' do
135
+ let(:error_message) { 'Invalid OAuth access token.' }
136
+
137
+ before do
138
+ stub_request(:delete, messenger_profile_url)
139
+ .with(query: { access_token: access_token })
140
+ .to_return(
141
+ body: ActiveSupport::JSON.encode(
142
+ 'error' => {
143
+ 'message' => error_message,
144
+ 'type' => 'OAuthException',
145
+ 'code' => 190,
146
+ 'fbtrace_id' => 'Hlssg2aiVlN'
147
+ }
148
+ ),
149
+ status: :ok,
150
+ headers: default_graph_api_response_headers
151
+ )
152
+ end
153
+
154
+ it 'raises an error' do
155
+ expect do
156
+ subject.unset(
157
+ body: {
158
+ fields: [
159
+ 'get_started'
160
+ ]
161
+ },
162
+ query: {
163
+ access_token: access_token
164
+ }
165
+ )
166
+ end.to raise_error(Bobot::AccessTokenError)
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,139 @@
1
+ require 'rails_helper'
2
+ require 'helpers/graph_api_helpers'
3
+
4
+ RSpec.describe Bobot::Subscription do
5
+ let(:access_token) { 'access token' }
6
+ let(:page_id) { 'page-id' }
7
+
8
+ let(:subscription_url) do
9
+ File.join(
10
+ described_class::GRAPH_FB_URL,
11
+ described_class::GRAPH_FB_VERSION,
12
+ "/#{page_id}",
13
+ '/subscribed_apps',
14
+ )
15
+ end
16
+
17
+ before do
18
+ Bobot.page_access_token = access_token
19
+ Bobot.page_id = page_id
20
+ end
21
+
22
+ describe '.set' do
23
+ context 'with a successful response' do
24
+ before do
25
+ stub_request(:post, subscription_url)
26
+ .with(query: { access_token: access_token})
27
+ .to_return(
28
+ body: ActiveSupport::JSON.encode(
29
+ success: true,
30
+ ),
31
+ status: :ok,
32
+ headers: default_graph_api_response_headers
33
+ )
34
+ end
35
+
36
+ it 'returns hash' do
37
+ expect(
38
+ subject.set(
39
+ query: {
40
+ page_id: page_id,
41
+ access_token: access_token,
42
+ }
43
+ )
44
+ ).to include('success' => true)
45
+ end
46
+ end
47
+
48
+ context 'with an unsuccessful response' do
49
+ let(:error_message) { 'Invalid OAuth access token.' }
50
+
51
+ before do
52
+ stub_request(:post, subscription_url)
53
+ .with(query: { access_token: access_token })
54
+ .to_return(
55
+ body: {
56
+ 'error' => {
57
+ 'message' => error_message,
58
+ 'type' => 'OAuthException',
59
+ 'code' => 190,
60
+ 'fbtrace_id' => 'Hlssg2aiVlN'
61
+ }
62
+ }.to_json,
63
+ status: :ok,
64
+ headers: default_graph_api_response_headers
65
+ )
66
+ end
67
+
68
+ it 'raises an error' do
69
+ expect do
70
+ subject.set(
71
+ query: {
72
+ page_id: page_id,
73
+ access_token: access_token,
74
+ }
75
+ )
76
+ end.to raise_error(Bobot::AccessTokenError)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '.unset' do
82
+ context 'with a successful response' do
83
+ before do
84
+ stub_request(:delete, subscription_url)
85
+ .with(query: { access_token: access_token })
86
+ .to_return(
87
+ body: ActiveSupport::JSON.encode(
88
+ success: true,
89
+ ),
90
+ status: :ok,
91
+ headers: default_graph_api_response_headers
92
+ )
93
+ end
94
+
95
+ it 'returns hash' do
96
+ expect(
97
+ subject.unset(
98
+ query: {
99
+ page_id: page_id,
100
+ access_token: access_token,
101
+ }
102
+ )
103
+ ).to include('success' => true)
104
+ end
105
+ end
106
+
107
+ context 'with an unsuccessful response' do
108
+ let(:error_message) { 'Invalid OAuth access token.' }
109
+
110
+ before do
111
+ stub_request(:delete, subscription_url)
112
+ .with(query: { access_token: access_token })
113
+ .to_return(
114
+ body: {
115
+ 'error' => {
116
+ 'message' => error_message,
117
+ 'type' => 'OAuthException',
118
+ 'code' => 190,
119
+ 'fbtrace_id' => 'Hlssg2aiVlN'
120
+ }
121
+ }.to_json,
122
+ status: :ok,
123
+ headers: default_graph_api_response_headers
124
+ )
125
+ end
126
+
127
+ it 'raises an error' do
128
+ expect do
129
+ subject.unset(
130
+ query: {
131
+ page_id: page_id,
132
+ access_token: access_token,
133
+ }
134
+ )
135
+ end.to raise_error(Bobot::AccessTokenError)
136
+ end
137
+ end
138
+ end
139
+ end