betforker 1.0.1

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +90 -0
  7. data/README.md +53 -0
  8. data/Rakefile +12 -0
  9. data/betforker.gemspec +23 -0
  10. data/bin/betforker +64 -0
  11. data/config.yml +14 -0
  12. data/lib/betforker.rb +80 -0
  13. data/lib/betforker/bookmakers/__to_change__/betfair.rb +112 -0
  14. data/lib/betforker/bookmakers/__to_change__/parimatch.rb +137 -0
  15. data/lib/betforker/bookmakers/__to_change__/sbobet.rb +90 -0
  16. data/lib/betforker/bookmakers/__to_change__/winlinebet.rb +122 -0
  17. data/lib/betforker/bookmakers/marathon.rb +121 -0
  18. data/lib/betforker/bookmakers/williamhill.rb +115 -0
  19. data/lib/betforker/comparer.rb +176 -0
  20. data/lib/betforker/config.rb +201 -0
  21. data/lib/betforker/downloader.rb +128 -0
  22. data/lib/betforker/event.rb +74 -0
  23. data/lib/betforker/fork.rb +29 -0
  24. data/lib/betforker/names_winlinebet.rb +1092 -0
  25. data/lib/betforker/parsed_page.rb +13 -0
  26. data/lib/betforker/version.rb +3 -0
  27. data/spec/betforker_spec.rb +39 -0
  28. data/spec/bookmakers/__to_change__/betfair_test.rb +40 -0
  29. data/spec/bookmakers/__to_change__/parimatch_test.rb +41 -0
  30. data/spec/bookmakers/__to_change__/sbobet_test.rb +40 -0
  31. data/spec/bookmakers/__to_change__/winlinebet_test.rb +39 -0
  32. data/spec/bookmakers/marathon_spec.rb +84 -0
  33. data/spec/bookmakers/williamhill_spec.rb +81 -0
  34. data/spec/comparer_spec.rb +162 -0
  35. data/spec/config_spec.rb +155 -0
  36. data/spec/downloader_spec.rb +88 -0
  37. data/spec/event_spec.rb +94 -0
  38. data/spec/features/without_downloads_spec.rb +138 -0
  39. data/spec/fork_spec.rb +21 -0
  40. data/spec/spec_helper.rb +199 -0
  41. data/spec/support/html/betfair/betfair1.html +3453 -0
  42. data/spec/support/html/betfair/betfair2.html +3996 -0
  43. data/spec/support/html/betfair/betfair3.html +6566 -0
  44. data/spec/support/html/betfair/betfair4.html +3373 -0
  45. data/spec/support/html/betfair/bf_live.htm +5905 -0
  46. data/spec/support/html/fake/fake.html +15759 -0
  47. data/spec/support/html/marathon/first.html +2717 -0
  48. data/spec/support/html/marathon/live_page.html +7226 -0
  49. data/spec/support/html/marathon/second.html +3500 -0
  50. data/spec/support/html/marathon/with_forks.html +2525 -0
  51. data/spec/support/html/parimatch/parimatch1.html +588 -0
  52. data/spec/support/html/parimatch/parimatch2.html +589 -0
  53. data/spec/support/html/parimatch/parimatch3.html +581 -0
  54. data/spec/support/html/parimatch/parimatch4.html +641 -0
  55. data/spec/support/html/parimatch/pm_live.html +1049 -0
  56. data/spec/support/html/sbobet/sb1.htm +8 -0
  57. data/spec/support/html/sbobet/sb2.htm +8 -0
  58. data/spec/support/html/sbobet/sb3.htm +8 -0
  59. data/spec/support/html/sbobet/sb4.htm +8 -0
  60. data/spec/support/html/sbobet/sbobet_live.htm +8 -0
  61. data/spec/support/html/williamhill/first.html +13273 -0
  62. data/spec/support/html/williamhill/live_page.html +42735 -0
  63. data/spec/support/html/williamhill/second.html +15759 -0
  64. data/spec/support/html/williamhill/with_forks.html +13473 -0
  65. data/spec/support/html/williamhill/without_forks.html +14016 -0
  66. data/spec/support/html/winlinebet/win1.htm +23797 -0
  67. data/spec/support/html/winlinebet/win2.htm +23797 -0
  68. data/spec/support/html/winlinebet/win3.htm +23797 -0
  69. data/spec/support/html/winlinebet/win4.htm +23797 -0
  70. data/spec/support/html/winlinebet/win_live.htm +23815 -0
  71. metadata +211 -0
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Betforker::Config do
4
+ describe '.update' do
5
+ before do
6
+ $config = { a: 'cc', b: 10, c: true }
7
+ allow(Config).to receive(:manual_enter).and_return(
8
+ { a: 'aa', b: 15, c: false })
9
+ allow(Config).to receive(:write_personal_config).and_return nil
10
+ end
11
+
12
+ it 'user can update config' do
13
+ values = Config.manual_enter
14
+ Config.update_config values
15
+
16
+ expect($config.size).to eq 3
17
+ expect($config.keys).to eq [:a, :b, :c]
18
+ expect($config.values).to eq ['aa', 15, false]
19
+ end
20
+ end
21
+
22
+ describe '.check_personal_configuration' do
23
+ let!(:template) do
24
+ { a: 'aa',
25
+ b: true,
26
+ c: false,
27
+ d: 10,
28
+ e: 1.0,
29
+ f: [1,2,3] }
30
+ end
31
+ let!(:personal) { template.dup }
32
+
33
+ before do
34
+ allow(Config).to receive(:write_personal_config).and_return nil
35
+ end
36
+
37
+ it 'personal config equal template' do
38
+ result = Config.check_personal_configuration(template, personal)
39
+
40
+ expect(result).to eq personal
41
+ expect(result.keys).to eq template.keys
42
+ end
43
+
44
+ it 'personal config have other valid values with the same keys' do
45
+ personal[:a] = 'aaaa'
46
+ personal[:b] = false
47
+ personal[:c] = true
48
+ personal[:d] = 1
49
+ personal[:e] = 10.0
50
+ personal[:f] = [1]
51
+ result = Config.check_personal_configuration(template, personal)
52
+
53
+ expect(result).to eq personal
54
+ expect(result.keys).to eq template.keys
55
+ end
56
+
57
+ it 'personal config have other invalid values with the same keys' do
58
+ personal[:a] = false
59
+ personal[:b] = 'aaaa'
60
+ personal[:c] = 10
61
+ personal[:d] = 1.0
62
+ personal[:e] = true
63
+ personal[:f] = 'bbbb'
64
+ result = Config.check_personal_configuration(template, personal)
65
+
66
+ expect(result).to eq template
67
+ expect(result.keys).to eq template.keys
68
+ end
69
+
70
+ context 'in personal config keys in other order' do
71
+ it 'with same values' do
72
+ personal = {}
73
+ template.reverse_each { |k, v| personal[k] = v }
74
+ result = Config.check_personal_configuration(template, personal)
75
+
76
+ expect(result).to eq personal
77
+ expect(result.keys).to eq template.keys
78
+ expect(result.keys).to_not eq personal.keys
79
+ end
80
+
81
+ it 'with unique values' do
82
+ personal = {}
83
+ personal[:f] = [1,2]
84
+ personal[:e] = 10.0
85
+ personal[:d] = 1
86
+ personal[:c] = true
87
+ personal[:b] = false
88
+ personal[:a] = 'bb'
89
+ result = Config.check_personal_configuration(template, personal)
90
+
91
+ expect(result).to eq personal
92
+ expect(result.keys).to eq template.keys
93
+ expect(result.keys).to_not eq personal.keys
94
+ end
95
+ end
96
+
97
+ context 'personal config has not enough keys and values' do
98
+ it 'with same order' do
99
+ template[:g] = true
100
+ template[:h] = false
101
+ result = Config.check_personal_configuration(template, personal)
102
+
103
+ expect(result.keys).to eq template.keys
104
+ expect(result.keys.count).to eq (personal.keys.count + 2)
105
+ expect(result.values).to eq ((personal.values << true) << false)
106
+ end
107
+
108
+ it 'with other order' do
109
+ reversed_template = {}
110
+ reversed_template[:g] = true
111
+ template.reverse_each { |k, v| reversed_template[k] = v }
112
+ result = Config.check_personal_configuration(reversed_template, personal)
113
+
114
+ expect(result.keys).to eq reversed_template.keys
115
+ expect(result.keys.count).to eq (personal.keys.count + 1)
116
+ expect(result[:g]).to eq true
117
+ personal.each do |key, value|
118
+ expect(result[key]).to eq value
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ describe '.same_typeofs' do
125
+ it 'strings' do
126
+ a, b = [String, String]
127
+ expect(Config.same_typeofs(a, b)).to be_truthy
128
+ end
129
+
130
+ it 'fixnums' do
131
+ a, b = [Fixnum, Fixnum]
132
+ expect(Config.same_typeofs(a, b)).to be_truthy
133
+ end
134
+
135
+ it 'floats' do
136
+ a, b = [Float, Float]
137
+ expect(Config.same_typeofs(a, b)).to be_truthy
138
+ end
139
+
140
+ it 'floats and fixnums' do
141
+ a, b = [Float, Fixnum]
142
+ expect(Config.same_typeofs(a, b)).to be_falsey
143
+ end
144
+
145
+ it 'true and false' do
146
+ a, b = [TrueClass, FalseClass]
147
+ expect(Config.same_typeofs(a, b)).to be_truthy
148
+ end
149
+
150
+ it 'false and true' do
151
+ a, b = [FalseClass, TrueClass]
152
+ expect(Config.same_typeofs(a, b)).to be_truthy
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Betforker::Downloader do
4
+ describe '#download_live_page' do
5
+ before do
6
+ $config = { log: false }
7
+ end
8
+
9
+ it 'for marathon properly' do
10
+ allow(Downloader).to receive(:download_from_marathon).
11
+ and_return open_right_live_page 'marathon'
12
+ marathon_live_page = Downloader.download_live_page 'Marathon'
13
+ page = Nokogiri::HTML(marathon_live_page)
14
+ login_attr = page.css('#auth').attribute('action').text
15
+ script_with_data = page.css('script').find {|s| s.text.include? 'initData'}.text
16
+
17
+ expect(page.text.size).to be > 1024
18
+ expect(login_attr).to include ':443/en/login.htm'
19
+ expect(page.title).to include 'betting odds'
20
+ expect(script_with_data).to include '"oddsType":"Decimal"'
21
+ expect(script_with_data).to include '"locale_name":"en"'
22
+ end
23
+
24
+ it 'for williamhill properly' do
25
+ allow(Downloader).to receive(:download_from_williamhill).
26
+ and_return open_right_live_page 'williamhill'
27
+ williamhill_live_page = Downloader.download_live_page 'WilliamHill'
28
+ page = Nokogiri::HTML(williamhill_live_page)
29
+ login_text = page.css('#login').text
30
+ script_with_data = page.css('script').find {|s| s.text.include? 'flashvars'}.text
31
+
32
+ expect(page.text.size).to be > 1024
33
+ expect(login_text).to include 'Join Now'
34
+ expect(script_with_data).to include 'priceFormat: "decimal"'
35
+ expect(script_with_data).to include 'WilliamHill'
36
+ end
37
+
38
+ it 'block from provider' do
39
+ allow(Downloader).to receive(:download_from_marathon).
40
+ and_return page_from_provider
41
+
42
+ expect { Downloader.download_live_page 'Marathon' }.to raise_error OpenSSL::SSL::SSLError
43
+ end
44
+
45
+ it 'get wrong argument' do
46
+ expect { Downloader.download_live_page 'SomethingUnique' }.to raise_error RuntimeError
47
+ end
48
+ end
49
+
50
+ describe '#download_event_pages' do
51
+ let(:sport) { 'tennis' }
52
+ let(:addresses) { [ Betforker::MARATHON_BASE, Betforker::WILLIAMHILL_BASE ] }
53
+
54
+ before do
55
+ allow(Downloader).to receive(:download_from_marathon).
56
+ and_return open_event_page('marathon', 'first.html')
57
+ allow(Downloader).to receive(:download_from_williamhill).
58
+ and_return open_event_page('williamhill', 'first.html')
59
+ end
60
+
61
+ it 'marathon properly' do
62
+ result = Downloader.download_event_pages addresses
63
+ page = Nokogiri::HTML(result['marathon'])
64
+ script_with_data = page.css('script').find {|s| s.text.include? 'initData'}.text
65
+
66
+ expect(page.text.size).to be > 1024
67
+ expect(page.title).to include 'betting odds'
68
+ expect(page.css('.live-today-member-name').size).to eq 2
69
+ expect(page.css('.active-shortcut-menu-link').text).to include 'All Markets'
70
+ expect(script_with_data).to include '"oddsType":"Decimal"'
71
+ expect(script_with_data).to include '"locale_name":"en"'
72
+ end
73
+
74
+ it 'williamhill properly' do
75
+ result = Downloader.download_event_pages addresses
76
+ page = Nokogiri::HTML(result['williamhill'])
77
+
78
+ expect(page.css('#selectedLive').text).to include 'All Markets'
79
+ expect(page.css('#primaryCollectionContainer').text).to include 'Match Betting Live'
80
+ end
81
+
82
+ it 'block from provider' do
83
+ allow(Downloader).to receive(:download_from_marathon).
84
+ and_return page_from_provider
85
+ expect { Downloader.download_event_pages addresses }.to raise_error OpenSSL::SSL::SSLError
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Betforker::Event do
4
+ let(:sport) { 'tennis' }
5
+ let(:addresses) do
6
+ [ 'http://first-valid-address.com',
7
+ 'https://second-valid-address.com/en/bet',
8
+ 'http://third-valid-address.com/where-is-it?q=42424',
9
+ 'https://fourth-valid-address.com' ]
10
+ end
11
+ let(:event) { Event.new(addresses, sport) }
12
+ let(:webpages) do
13
+ { 'first' => fake_event_webpage,
14
+ 'second' => fake_event_webpage,
15
+ 'third' => fake_event_webpage,
16
+ 'fourth' => fake_event_webpage }
17
+ end
18
+
19
+ describe '#get_webpages' do
20
+ it 'properly' do
21
+ allow(Downloader).to receive(:download_event_pages).
22
+ and_return(webpages)
23
+ event.get_webpages
24
+ pages = event.webpages
25
+
26
+ expect(pages.size).to eq 4
27
+ pages.each do |bookie_name, page|
28
+ expect(page.size).to be > 1024
29
+ end
30
+ end
31
+
32
+ it 'when returns only one page' do
33
+ allow(Downloader).to receive(:download_event_pages).
34
+ and_return({ 'marathon' => 'something interesting' })
35
+ event.get_webpages
36
+ pages = event.webpages
37
+
38
+ expect(pages.size).to eq 1
39
+ end
40
+
41
+ it 'when returns no pages' do
42
+ allow(Downloader).to receive(:download_event_pages).
43
+ and_return({})
44
+ event.get_webpages
45
+ pages = event.webpages
46
+
47
+ expect(pages.size).to eq 0
48
+ end
49
+
50
+ end
51
+
52
+ describe '#parse_webpages' do
53
+ let(:bookmakers) { [ 'Marathon'] }
54
+
55
+ it 'successfully' do
56
+ allow(Marathon).to receive(:parse_event).
57
+ and_return(updated_event(event))
58
+
59
+ event.webpages.merge! webpages
60
+ event.parse_webpages(bookmakers)
61
+ result = event.parsed_webpages
62
+
63
+ expect(result.size).to eq 1
64
+ expect(result.first.class).to eq Betforker::ParsedPage
65
+ end
66
+ end
67
+
68
+
69
+ describe '#forking' do
70
+ before do
71
+ 3.times { event.parsed_webpages << ParsedPage.new }
72
+ end
73
+
74
+ it 'find forks' do
75
+ allow(Comparer).to receive(:compare).
76
+ and_return([Fork.new, Fork.new])
77
+ event.forking
78
+
79
+ expect(event.forks.size).to eq 3
80
+ expect(event.forks.flatten.size).to eq 6
81
+ event.forks.each do |f|
82
+ expect(f.class).to eq Array
83
+ end
84
+ end
85
+
86
+ it 'find no forks' do
87
+ allow(Comparer).to receive(:compare).
88
+ and_return([])
89
+ event.forking
90
+ expect(event.forks.size).to eq 3
91
+ expect(event.forks.flatten.size).to eq 0
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Betforker finds forks without downloads' do
4
+ let(:sport) { 'tennis' }
5
+ let(:event) { Betforker::Event.new [1,2,3], sport }
6
+ before do
7
+ $config = { min_percent: 1.1, filtering: true , bookmakers: ['Marathon', 'WilliamHill'] }
8
+ end
9
+
10
+ describe 'beginning with parsed webpages' do
11
+ it 'with many forks' do
12
+ event.parsed_webpages << parsed_marathon
13
+ event.parsed_webpages << parsed_williamhill
14
+ event.forking
15
+ forks = event.forks.flatten
16
+
17
+ expect(forks.size).to eq 4
18
+ expect(forks.first.what).to eq 'match'
19
+ forks.each do |f|
20
+ expect(f.what).to match /match|game|set/
21
+ expect(f.score).to eq '0:0 (3:2)'
22
+ expect(f.players).to eq 'AwayPlayer VS HomePlayer'
23
+ end
24
+ end
25
+
26
+ it 'without any fork' do
27
+ changed_parsed_williamhill = parsed_marathon
28
+ changed_parsed_williamhill.bookie = 'WilliamHill'
29
+ event.parsed_webpages << parsed_marathon
30
+ event.parsed_webpages << changed_parsed_williamhill
31
+ event.forking
32
+ forks = event.forks.flatten
33
+
34
+ expect(forks.size).to eq 0
35
+ end
36
+
37
+ it 'no forks if all pages from one bookie' do
38
+ changed_parsed_marathon = parsed_williamhill
39
+ changed_parsed_marathon.bookie = 'Marathon'
40
+ event.parsed_webpages << parsed_marathon
41
+ event.parsed_webpages << changed_parsed_marathon
42
+ event.forking
43
+ forks = event.forks.flatten
44
+
45
+ expect(forks.size).to eq 0
46
+ end
47
+ end
48
+
49
+ describe 'beginning with parsing webpages' do
50
+ let(:mar_with_forks) do
51
+ { 'marathon' => open_event_page('marathon', 'with_forks.html') }
52
+ end
53
+ let(:wh_with_forks) do
54
+ { 'williamhill' => open_event_page('williamhill', 'with_forks.html') }
55
+ end
56
+ let(:wh_without_forks) do
57
+ { 'williamhill' => open_event_page('williamhill', 'without_forks.html') }
58
+ end
59
+
60
+ it 'successfully with 1 fork' do
61
+ event.webpages.merge! mar_with_forks
62
+ event.webpages.merge! wh_with_forks
63
+ event.parse_webpages ['Marathon', 'WilliamHill']
64
+ event.forking
65
+ forks = event.forks.flatten
66
+
67
+ expect(forks.size).to eq 1
68
+ expect(forks.first.what).to eq 'game7'
69
+ end
70
+
71
+ it 'successfully with 2 forks' do
72
+ $config = { min_percent: 1.1, filtering: false }
73
+ event.webpages.merge! mar_with_forks
74
+ event.webpages.merge! wh_with_forks
75
+ event.parse_webpages ['Marathon', 'WilliamHill']
76
+ event.forking
77
+ forks = event.forks.flatten
78
+
79
+ expect(forks.size).to eq 2
80
+ expect(forks.first.what).to eq 'match'
81
+ expect(forks.last.what).to eq 'game7'
82
+ $config = { min_percent: 1.1, filtering: true }
83
+ end
84
+
85
+ it 'without forks' do
86
+ event.webpages.merge! mar_with_forks
87
+ event.webpages.merge! wh_without_forks
88
+ event.parse_webpages ['Marathon', 'WilliamHill']
89
+ event.forking
90
+ forks = event.forks.flatten
91
+
92
+ expect(forks.size).to eq 0
93
+ end
94
+ end
95
+
96
+ describe 'beginning with created Events' do
97
+ let(:event) { Event.new ['mar_with_forks', 'wh_with_forks'], sport }
98
+ let(:return_from_downloader) do
99
+ { 'marathon' => open_event_page('marathon', 'with_forks.html'),
100
+ 'williamhill' => open_event_page('williamhill', 'with_forks.html') }
101
+ end
102
+
103
+ it 'find a fork' do
104
+ allow(Downloader).to receive(:download_event_pages).
105
+ and_return(return_from_downloader)
106
+ allow(event).to receive(:all_bookmakers).
107
+ and_return(['Marathon', 'WilliamHill'])
108
+
109
+ forks = event.find_forks
110
+ f = forks.first
111
+
112
+ expect(forks.size).to eq 1
113
+ expect(f.what).to eq 'game7'
114
+ expect(f.bookmakers).to eq 'Marathon - WilliamHill'
115
+ end
116
+ end
117
+
118
+ describe 'from the beginning' do
119
+ it 'one time' do
120
+ allow(Betforker).to receive(:pull_live_events).
121
+ and_return unstructured_events
122
+ allow(Downloader).to receive(:download_from_marathon).
123
+ and_return open_event_page('marathon', 'with_forks.html')
124
+ allow(Downloader).to receive(:download_from_williamhill).
125
+ and_return open_event_page('williamhill', 'with_forks.html')
126
+
127
+ events = Betforker.build_events ['Marathon', 'WilliamHill'], sport
128
+ expect(events.size).to eq structured_events.size
129
+
130
+ events.each do |ev|
131
+ forks = ev.find_forks
132
+ if ev.addresses.include?(Betforker::MARATHON_BASE) and ev.addresses.include?(Betforker::WILLIAMHILL_BASE)
133
+ expect(forks).to_not be_empty
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end