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,137 @@
1
+ class Parimatch
2
+ attr_reader :live_address
3
+
4
+ def initialize
5
+ @live_address = 'http://www.parimatch.com/en/live.html'
6
+ @parsed_event = {
7
+ bookie: 'Parimatch',
8
+ score: '',
9
+ home_player: Hash.new,
10
+ away_player: Hash.new
11
+ }
12
+ end
13
+
14
+ def live_page_parsed html_source
15
+ nok = Nokogiri::HTML(html_source)
16
+ links = Hash.new
17
+ nok.css('#liveContentHolder0 #tennisItem tbody .td_n a').each do |link|
18
+ next unless link['href']
19
+ link.css('span').remove
20
+ href = link['href']
21
+ domain = 'http://www.parimatch.com/en/'
22
+ href = domain + href unless href.include?(domain)
23
+ who = link.text
24
+ links[href] = unified_names(who)
25
+ end
26
+ links
27
+ end
28
+
29
+ def event_parsed html_source
30
+ nok = Nokogiri::HTML(html_source)
31
+ score = nok.css('.l .l').text.split(/\(|\)|,/)
32
+ if not score.empty? and score[-1].include? ':'
33
+ games = score.pop.strip
34
+ sets = score.pop
35
+ elsif score.empty?
36
+ games = ""
37
+ sets = ""
38
+ else
39
+ games = '0:0'
40
+ sets = score.pop
41
+ end
42
+ home_set, away_set = sets.scan(/\d+/)
43
+ home_game, away_game = games.split(':')
44
+ @parsed_event[:score] = "#{home_game}:#{away_game} (#{home_set}:#{away_set})"
45
+ names_chunk = nok.css('.l')
46
+ names_chunk.css('img').remove
47
+ names_chunk.css('span').remove
48
+ h_pl, a_pl = names_chunk.inner_html.split('<br>')
49
+ h_pl = Nokogiri::HTML(h_pl).text if h_pl.include? '<small>'
50
+ a_pl = Nokogiri::HTML(a_pl).text if a_pl.include? '<small>'
51
+ h_pl.strip!
52
+ a_pl.strip!
53
+ unless h_pl.empty? or a_pl.empty?
54
+ @parsed_event[:home_player][:name] = unified_names(h_pl)
55
+ @parsed_event[:away_player][:name] = unified_names(a_pl)
56
+ end
57
+ ##########################
58
+ table_header = nil
59
+ first, second = 0, 0
60
+ nok.css('.gray tr').each_with_index do |line|
61
+ table_header ||= line
62
+ games_line = line.css('.dyn')
63
+ if games_line.empty?
64
+ table_header.css('th').each_with_index do |head,i|
65
+ case head.text
66
+ when '1' then first = i
67
+ when '2' then second = i
68
+ end
69
+ end
70
+ new_line = []
71
+ line.css('td').each do |l|
72
+ if l.attribute('colspan')
73
+ t = nil
74
+ l.attribute('colspan').value.to_i.times {|f| new_line << t}
75
+ else
76
+ new_line << l.text
77
+ end
78
+ end
79
+ if (new_line[first] and new_line[second]) and (new_line[first].to_f < 100.0 and new_line[second].to_f < 100.0)
80
+ ####win match
81
+ if not line.css('td.l').empty?
82
+ @parsed_event[:home_player][:match] = new_line[first].to_f
83
+ @parsed_event[:away_player][:match] = new_line[second].to_f
84
+ ###win set
85
+ elsif line.css('td')[1].text.include?(' set:')
86
+ num = line.css('td')[1].inner_html[0]
87
+ @parsed_event[:home_player][:set] ||= Hash.new
88
+ @parsed_event[:away_player][:set] ||= Hash.new
89
+ @parsed_event[:home_player][:set][num] = new_line[first].to_f
90
+ @parsed_event[:away_player][:set][num] = new_line[second].to_f
91
+ end
92
+ end
93
+ else
94
+ next if games_line.text =~ /point|score|who|deuce|race/i
95
+ num = games_line.css('.p2r').text.scan(/\d+/)[-1]
96
+ c1, c2 = games_line.css('nobr')
97
+ coeff1 = c1.text.split(/ /)[-1].to_f
98
+ coeff2 = c2.text.split(/ /)[-1].to_f
99
+ @parsed_event[:home_player][:game] ||= Hash.new
100
+ @parsed_event[:away_player][:game] ||= Hash.new
101
+ @parsed_event[:home_player][:game][num] = coeff1
102
+ @parsed_event[:away_player][:game][num] = coeff2
103
+ end
104
+ end
105
+ @parsed_event[:home_player][:name] ||= 'HomePlayer'
106
+ @parsed_event[:away_player][:name] ||= 'AwayPlayer'
107
+ @parsed_event
108
+ end
109
+
110
+ def unified_names who
111
+ w = []
112
+ if who.include?(' - ')
113
+ who.split(' - ').each do |pl|
114
+ w += second_names_finder(pl)
115
+ end
116
+ else
117
+ w += second_names_finder(who)
118
+ end
119
+ who = ""
120
+ w.sort.each {|wh| who << wh}
121
+ who
122
+ end
123
+
124
+ def second_names_finder names
125
+ w = []
126
+ #names.gsub!('-', ' ') do not drop second name
127
+ if names.include?('/')
128
+ nn = names.split(/\//)
129
+ nn.each do |n|
130
+ w << n.scan(/\w+/)[0]
131
+ end
132
+ else
133
+ w << names.scan(/\w+/)[0]
134
+ end
135
+ w
136
+ end
137
+ end
@@ -0,0 +1,90 @@
1
+ class Sbobet
2
+ attr_reader :live_address
3
+
4
+ def initialize
5
+ #with tennis works only!!
6
+ @live_address = 'http://www.sbobet.com/euro/live-betting/tennis'
7
+ @parsed_event = {
8
+ bookie: 'Sbobet',
9
+ score: '0:0 (0:0)',
10
+ home_player: Hash.new,
11
+ away_player: Hash.new
12
+ }
13
+ end
14
+
15
+ def live_page_parsed html_source
16
+ nok = Nokogiri::HTML(html_source)
17
+ links = Hash.new
18
+ nok.css('tr').each do |event|
19
+ href = "https://www.sbobet.com"
20
+ event.css('.IconMarkets').map do |link|
21
+ unless link['href'].include?(href)
22
+ href += link['href']
23
+ else
24
+ href = link['href']
25
+ end
26
+ end
27
+ h_p, a_p = event.css('.OddsL').map {|name| name.text }
28
+ who = h_p + ' v ' + a_p
29
+ links[href] = unified_names(who)
30
+ end
31
+ links
32
+ end
33
+
34
+ def event_parsed html_source
35
+ nok = Nokogiri::HTML(html_source)
36
+ @parsed_event[:home_player][:name] = unified_names(nok.css('.THomeName').text)
37
+ @parsed_event[:away_player][:name] = unified_names(nok.css('.TAwayName').text)
38
+ nok.css('#event-section .LiveMarket .Hdp tr').each do |what|
39
+ #without scores and only bet on wins
40
+ h_p_info = what.css('.OddsTabL')
41
+ a_p_info = what.css('.OddsTabR')
42
+ if h_p_info.css('.OddsR').text.to_f != 0 and a_p_info.css('.OddsR').text.to_f != 0
43
+ not_handicap = h_p_info.css('.OddsM').text.to_f == 0.0 && a_p_info.css('.OddsM').text.to_f == 0.0
44
+ else
45
+ not_handicap = false
46
+ end
47
+ if not_handicap
48
+ @parsed_event[:home_player][:match] = h_p_info.css('.OddsR').text.to_f
49
+ @parsed_event[:away_player][:match] = a_p_info.css('.OddsR').text.to_f
50
+ end
51
+ end
52
+ @parsed_event[:home_player][:name] ||= 'HomePlayer'
53
+ @parsed_event[:away_player][:name] ||= 'AwayPlayer'
54
+ @parsed_event
55
+ end
56
+
57
+ def unified_names who
58
+ w = []
59
+ if who.include?(' v ')
60
+ who.split(' v ').each do |pl|
61
+ w += second_names_finder(pl)
62
+ end
63
+ else
64
+ w += second_names_finder(who)
65
+ end
66
+ who = ""
67
+ w.sort.each {|wh| who << wh}
68
+ who
69
+ end
70
+
71
+ def second_names_finder names
72
+ w = []
73
+ names.gsub!('-', ' ')
74
+ if names.include? ',' #from betfair feature
75
+ ss = names.split(/,/)
76
+ w << ss[0].scan(/\w+/)[-1] #if singles play
77
+ ss.each do |s|
78
+ w << s.scan(/\w+/)[-1] if s.include? '/'
79
+ end
80
+ elsif names.include? '/'
81
+ ss = names.split(/\//)
82
+ ss.each do |s|
83
+ w << s.scan(/\w+/)[-1]
84
+ end
85
+ else
86
+ w << names.scan(/\w+/)[-1]
87
+ end
88
+ w
89
+ end
90
+ end
@@ -0,0 +1,122 @@
1
+ class Winlinebet
2
+ attr_reader :live_address
3
+
4
+ def initialize
5
+ @live_address = 'http://winlinebet.com/stavki/sport/tennis'
6
+ @parsed_event = {
7
+ bookie: 'Winlinebet',
8
+ score: '0:0 (0:0)',
9
+ home_player: Hash.new,
10
+ away_player: Hash.new
11
+ }
12
+ end
13
+
14
+ def live_page_parsed html_source
15
+ nok = Nokogiri::HTML(html_source)
16
+ links = Hash.new
17
+ nok.css('#TABLEL .long').each do |link|
18
+ who = link.text
19
+ plushash = link.attribute('onclick').to_s.scan(/\d+/)[0]
20
+ href = "http://winlinebet.com/plus/#{plushash}"
21
+ links[href] = unified_names(who)
22
+ end
23
+ links
24
+ end
25
+
26
+ def event_parsed html_source
27
+ nok = Nokogiri::HTML(html_source)
28
+ nok.css('script').remove
29
+ nok.css('style').remove
30
+ #score extracting
31
+ scores = nok.css('#radarscore4larg li').map {|s| s.text.strip}
32
+ unless scores.size.odd?
33
+ home_game_score = scores[0]
34
+ away_game_score = scores[scores.size / 2]
35
+ home_set_score = scores[(scores.size / 2) - 1]
36
+ away_set_score = scores[-1]
37
+ @parsed_event[:score] = "#{home_game_score}:#{away_game_score} (#{home_set_score}:#{away_set_score})"
38
+ end
39
+ #players
40
+ h_pl = nok.css('#urad1larg').attribute('title').text.strip
41
+ a_pl = nok.css('#urad2larg').attribute('title').text.strip
42
+ unless h_pl.empty? or a_pl.empty?
43
+ @parsed_event[:home_player][:name] = unified_names(h_pl)
44
+ @parsed_event[:away_player][:name] = unified_names(a_pl)
45
+ end
46
+ #bets parsing
47
+ nok.css('#tracker .left tbody tr').each do |event|
48
+ what = event.css('.inf').text
49
+ h_coeff, a_coeff = event.css('.betbox')
50
+ if what =~ /матч/i
51
+ @parsed_event[:home_player][:match] = h_coeff.text.split('#')[0].to_f
52
+
53
+ @parsed_event[:away_player][:match] = a_coeff.text.split('#')[0].to_f
54
+ elsif what =~ /\d сет/i
55
+ @parsed_event[:home_player][:set] ||= Hash.new
56
+ @parsed_event[:home_player][:set][what.to_i.to_s] = h_coeff.text.split('#')[0].to_f
57
+
58
+ @parsed_event[:away_player][:set] ||= Hash.new
59
+ @parsed_event[:away_player][:set][what.to_i.to_s] = a_coeff.text.split('#')[0].to_f
60
+ elsif what =~/\d+ гейм/i
61
+ if scores.size > 4 and what.to_i >= 6 and (what.to_i - (scores[(scores.size/2)-1].to_i + scores[-1].to_i)) != 2
62
+ #home_player 40 6 3 1 => %w[40 6 3 1 30 3 6 4]
63
+ #away_player 30 3 6 4
64
+ scores.slice!(scores.size / 2)# removing away player game score
65
+ scores.shift# removing home player game score
66
+ scores.slice!((scores.size / 2) - 1)#removing home player current set score
67
+ scores.pop# removing away player current set score
68
+ sum_of_sets = 0
69
+ scores.each { |a| sum_of_sets += a.to_i }
70
+ num_of_game = what.to_i - sum_of_sets
71
+ else
72
+ num_of_game = what.to_i
73
+ end
74
+ @parsed_event[:home_player][:game] ||= Hash.new
75
+ @parsed_event[:home_player][:game][num_of_game.to_s] = h_coeff.text.split('#')[0].to_f
76
+
77
+ @parsed_event[:away_player][:game] ||= Hash.new
78
+ @parsed_event[:away_player][:game][num_of_game.to_s] = a_coeff.text.split('#')[0].to_f
79
+ end
80
+ end
81
+ @parsed_event[:home_player][:name] ||= 'HomePlayer'
82
+ @parsed_event[:away_player][:name] ||= 'AwayPlayer'
83
+ @parsed_event
84
+ end
85
+
86
+ def unified_names who
87
+ w = []
88
+ if who.include?(' - ')
89
+ who.split(' - ').each do |pl|
90
+ w += second_names_finder(pl)
91
+ end
92
+ else
93
+ w += second_names_finder(who)
94
+ end
95
+ who = ""
96
+ w.map! { |name| changed_name(name) }
97
+ w.sort.each { |wh| who << wh }
98
+ who
99
+ end
100
+
101
+ def second_names_finder names
102
+ w = []
103
+ if names.include?('/')
104
+ nn = names.split(/\//)
105
+ nn.each do |n|
106
+ w << n.strip.split(/ /)[0]
107
+ end
108
+ else
109
+ w << names.strip.split(/ /)[0]
110
+ end
111
+ w
112
+ end
113
+
114
+ def changed_name name
115
+ en_name = name
116
+ Winline::NAMES.each do |rus,en|
117
+ en_name = en if name.eql?(rus) and not en.empty?
118
+ end
119
+ en_name
120
+ end
121
+
122
+ end
@@ -0,0 +1,121 @@
1
+ module Betforker
2
+ module Bookmakers
3
+ module Marathon
4
+ module_function
5
+
6
+ def parse_live_page(html, sport)
7
+ nok = Nokogiri::HTML(html)
8
+ links = {}
9
+ nok.css('tbody').each do |table|
10
+ next unless table.attribute('data-event-treeid')
11
+ number = table.attribute('data-event-treeid').text.to_i
12
+ href = "#{Betforker::MARATHON_BASE}/live/#{number}?openedMarkets=#{number}"
13
+ players = table.css('.live-today-member-name')[0].text.strip + " v " + table.css('.live-today-member-name')[1].text.strip
14
+ links[href] = concatenated_names(players)
15
+ end
16
+ links
17
+ end
18
+
19
+ def parse_event(event, sport)
20
+ result = Betforker::ParsedPage.new bookie: 'Marathon'
21
+ html = extract_html_from(event)
22
+ return event if html.nil?
23
+ nok = Nokogiri::HTML(html)
24
+ nok.css('script').remove
25
+ games_score = nok.css('.cl-left .result-description-part').text.strip[1...-1]
26
+ nok.css('.cl-left .result-description-part').remove
27
+ sets_score = nok.css('.cl-left').text.strip
28
+ result.score = "#{games_score} (#{sets_score})"
29
+ h_pl = nok.css('.live-today-name .live-today-member-name')[0].text.strip
30
+ a_pl = nok.css('.live-today-name .live-today-member-name')[1].text.strip
31
+ unless h_pl.empty? or a_pl.empty?
32
+ result.home_player[:name] = concatenated_names(h_pl)
33
+ result.away_player[:name] = concatenated_names(a_pl)
34
+ end
35
+ #find sets & match
36
+ sets_nums = %w{ 1st 2nd 3rd 4th 5th }
37
+ nok.css('span.selection-link').each do |link|
38
+ if link.attribute('data-selection-key').text.include? 'Match_Result.1'
39
+ result.home_player[:match] = link.text.to_f
40
+ elsif link.attribute('data-selection-key').text.include? 'Match_Result.3'
41
+ result.away_player[:match] = link.text.to_f
42
+ end
43
+ sets_nums.each do |v|
44
+ if link.attribute('data-selection-key').text.include? "#{v}_Set_Result.RN_H"
45
+ result.home_player[:set] ||= Hash.new
46
+ result.home_player[:set][v[0]] = link.text.to_f
47
+ elsif link.attribute('data-selection-key').text.include? "#{v}_Set_Result.RN_A"
48
+ result.away_player[:set] ||= Hash.new
49
+ result.away_player[:set][v[0]] = link.text.to_f
50
+ end
51
+ end
52
+ end
53
+ #find games
54
+ nok.css('.block-market-wrapper').each do |chunk|
55
+ next unless chunk.attribute('data-mutable-id').text =~ /B82|Block_82/
56
+ chunk.css('.market-inline-block-table-wrapper').each do |ch|
57
+ next unless ch.css('.name-field').text.include? 'To Win Game'
58
+ ch.css('table tr').each do |t|
59
+ next if t.to_s.include?('<th')
60
+ num = t.css('.market-table-name b').text
61
+ coeff1, coeff2 = t.css('.price .selection-link').collect {|c| c.text.to_f}
62
+ if coeff1 and coeff2
63
+ result.home_player[:game] ||= {}
64
+ result.away_player[:game] ||= {}
65
+ result.home_player[:game][num] = coeff1
66
+ result.away_player[:game][num] = coeff2
67
+ end
68
+ end
69
+ end
70
+ end
71
+ result.home_player[:name] ||= 'HomePlayer'
72
+ result.away_player[:name] ||= 'AwayPlayer'
73
+ event.parsed_webpages << result
74
+ end
75
+
76
+ def extract_html_from(event)
77
+ arr = event.webpages.values_at 'marathon'
78
+ arr.first
79
+ end
80
+
81
+ def concatenated_names(string)
82
+ w = []
83
+ if string.include?(' v ')
84
+ string.split(' v ').each do |pl|
85
+ w += second_names_finder(pl)
86
+ end
87
+ else
88
+ w += second_names_finder(string)
89
+ end
90
+ who = ""
91
+ w.sort.each { |wh| who << wh }
92
+ who
93
+ end
94
+
95
+ def second_names_finder(names)
96
+ second_name = []
97
+ if names.include?('/')
98
+ nn = names.split(/\//)
99
+ nn.each do |n|
100
+ second_name << n.scan(/\w+/)[-1]
101
+ end
102
+ elsif names.include?('Doubles')
103
+ second_name << names.scan(/\w+/)[-2]
104
+ else
105
+ second_name << names.split(',')[0].scan(/\w+/)[-1]
106
+ end
107
+ second_name
108
+ end
109
+
110
+ def set_cookies
111
+ domain = Betforker::MARATHON_CHANGABLE.gsub('https://www', '')
112
+ [{
113
+ domain: domain,
114
+ name: 'panbet.oddstype',
115
+ value: 'Decimal',
116
+ path: '/'
117
+ }]
118
+ end
119
+ end
120
+ end
121
+ end