bobot 1.0.52

Sign up to get free protection for your applications and to get access to all the features.
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