tf2r 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/Gemfile.lock +1 -1
- data/lib/tf2r/api.rb +19 -2
- data/lib/tf2r/raffle.rb +37 -0
- data/lib/tf2r/scraper.rb +2 -2
- data/lib/tf2r/text_helpers.rb +7 -1
- data/lib/tf2r/version.rb +1 -1
- data/spec/raffle_spec.rb +43 -10
- data/spec/scraper_spec.rb +17 -61
- data/spec/spec_helper.rb +4 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d040ee6e9265eed2195362f8c115193590c62207
|
4
|
+
data.tar.gz: be7f1f7cf1a805e4fa5b16fc02fd41b0692193c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51aef9d710a7e17129106d3ec3f0843f822cd7e43e5a89e39de7fa48f324877437028fc0f75a3e6a4b7c946d5085dd7a7e4eb721305c0ca5e06bb4fcb134a068
|
7
|
+
data.tar.gz: 00ea3e40215556e898e0a9e3c078bab6506083e4f11cac1ca4ee4eb97e36688e7c85ef988586b576fc065130f531a0e39e0a49a760dbba45b02c7b9113b66033
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/Gemfile.lock
CHANGED
data/lib/tf2r/api.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module TF2R
|
2
2
|
# This class handles interaction with TF2R's "API", which is really just an
|
3
3
|
# open endpoint located at http://tf2r.com/job.php, in use all over the site
|
4
|
-
#
|
4
|
+
# for dynamically updating pages.
|
5
5
|
class API
|
6
6
|
HOST = 'http://tf2r.com'
|
7
7
|
ENDPOINT = '/job.php'
|
@@ -9,6 +9,22 @@ module TF2R
|
|
9
9
|
# a problem for any raffles with a maximum entries greater than 2500.
|
10
10
|
MAX_ENTRY_RESPONSE_COUNT = 2500
|
11
11
|
|
12
|
+
# Queries the API for information about a raffle.
|
13
|
+
#
|
14
|
+
# @params link_snippet [String] the link snippet of the desired raffle.
|
15
|
+
# @return [Array] the API response for the raffle.
|
16
|
+
# * ended (+Boolean+) whether the raffle is done.
|
17
|
+
# * timeleft (+Fixnum+) seconds remaining.
|
18
|
+
# * max_entry (+Fixnum+) maximum number of entries.
|
19
|
+
# * entry (+String+) but really a number. This is the ID in TF2R's db of
|
20
|
+
# the last entry returned in the response.
|
21
|
+
# * wc (+Float+) the chance for any one participant to win.
|
22
|
+
# * cur_entry (+Fixnum+) current number of entries.
|
23
|
+
# * started (+Boolean+) whether the raffle has begun.
|
24
|
+
# * newentry (+Array+) contains hashes representing participants in
|
25
|
+
# chronological order.
|
26
|
+
# * chaten (+Array+) I have no idea.
|
27
|
+
# * chatmax (+Fixnum) ¯\_(ツ)_/¯
|
12
28
|
def self.raffle_info(link_snippet)
|
13
29
|
params = {checkraffle: true, rid: link_snippet[1..-1],
|
14
30
|
lastentrys: 0, lastchat: 0}
|
@@ -17,7 +33,8 @@ module TF2R
|
|
17
33
|
|
18
34
|
private
|
19
35
|
|
20
|
-
#
|
36
|
+
# Submits a request to job.php with the given params.
|
37
|
+
# TODO: should this do something with the responses headers?
|
21
38
|
def self.request(params)
|
22
39
|
uri = URI.parse(HOST)
|
23
40
|
http = Net::HTTP.new(uri.host, uri.port)
|
data/lib/tf2r/raffle.rb
CHANGED
@@ -15,6 +15,34 @@ module TF2R
|
|
15
15
|
get_full_participants if max_entries > API::MAX_ENTRY_RESPONSE_COUNT
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
# Gives information about the raffle.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# r = Raffle.new('kstzcbd')
|
23
|
+
# r.info #=>
|
24
|
+
# {:link_snippet=>"kstzcbd",
|
25
|
+
# :title=>"Just one refined [1 hour]",
|
26
|
+
# :description=>"Plain and simple.",
|
27
|
+
# :start_time=>2012-10-29 09:51:45 -0400,
|
28
|
+
# :end_time=>2012-10-29 09:53:01 -0400,
|
29
|
+
# :win_chance=>0.1,
|
30
|
+
# :current_entries=>10,
|
31
|
+
# :max_entries=>10,
|
32
|
+
# :is_done=>true}
|
33
|
+
#
|
34
|
+
# @param page [Mechanize::Page] the raffle page.
|
35
|
+
# @return [Hash] a representation of the raffle.
|
36
|
+
# * :link_snippet (+String+) — the "raffle id" in the URL.
|
37
|
+
# * :title (+String+) — the raffle's title.
|
38
|
+
# * :description (+String+) — the raffle's "message".
|
39
|
+
# * :start_time (+Time+) — the creation time of the raffle.
|
40
|
+
# * :end_time (+Time+) — the projects/observed end time for the raffle.
|
41
|
+
# * :win_chance (+Float+) — a participant's chance to win the raffle.
|
42
|
+
# Rounded to 5 digits.
|
43
|
+
# * :current_entries (+Fixnum+) — the current number of participants.
|
44
|
+
# * :max_entries (+Fixnum+) — the maximum number of particpants allowed.
|
45
|
+
# * :is_done (+Boolean+) — whether new users can enter the raffle.
|
18
46
|
def info
|
19
47
|
@info ||= {link_snippet: @link_snippet, title: title,
|
20
48
|
description: description, start_time: start_time,
|
@@ -23,6 +51,11 @@ module TF2R
|
|
23
51
|
is_done: is_done}
|
24
52
|
end
|
25
53
|
|
54
|
+
# Gives information about the raffle's creator.
|
55
|
+
#
|
56
|
+
# Taken straight from +Scraper+.
|
57
|
+
#
|
58
|
+
# @see Scraper#scrape_raffle_for_creator
|
26
59
|
def creator
|
27
60
|
@scraper_info[1]
|
28
61
|
end
|
@@ -38,6 +71,8 @@ module TF2R
|
|
38
71
|
|
39
72
|
private
|
40
73
|
|
74
|
+
# Asynchronously makes network connections to TF2R to query its API and
|
75
|
+
# scrape the raffle page.
|
41
76
|
def populate_raffle_info
|
42
77
|
threads = []
|
43
78
|
threads << Thread.new do
|
@@ -56,6 +91,8 @@ module TF2R
|
|
56
91
|
@full_participants = @scraper.scrape_raffle_for_participants(page)
|
57
92
|
end
|
58
93
|
|
94
|
+
# Converts the representation of participants by TF2R's API into our own
|
95
|
+
# standard representation.
|
59
96
|
def normalize_entries(entries)
|
60
97
|
entries.map do |entry|
|
61
98
|
entry[:steam_id] = extract_steam_id entry.delete('link')
|
data/lib/tf2r/scraper.rb
CHANGED
@@ -4,6 +4,7 @@ module TF2R
|
|
4
4
|
include TF2R::TextHelpers
|
5
5
|
|
6
6
|
class InvalidUserPage < StandardError; end
|
7
|
+
|
7
8
|
# Creates a Scraper. Pass values using the options hash.
|
8
9
|
#
|
9
10
|
# :user_agent a String used for the User-Agent header
|
@@ -207,12 +208,11 @@ module TF2R
|
|
207
208
|
raise InvalidUserPage, 'The given page does not correspond to any user.'
|
208
209
|
else
|
209
210
|
infos = user_page.parser.css('.raffle_infomation') #sic
|
210
|
-
avatar_anchor = infos[0].css('img')[0]
|
211
211
|
user_anchor = infos[1].css('a')[0]
|
212
212
|
|
213
213
|
steam_id = extract_steam_id(user_page.uri.to_s)
|
214
214
|
username = /TF2R Item Raffles - (.+)/.match(user_page.title)[1]
|
215
|
-
avatar_link =
|
215
|
+
avatar_link = infos[0].css('img')[0].attribute('src').to_s
|
216
216
|
|
217
217
|
posrep = infos.css('.upvb').text.to_i
|
218
218
|
negrep = infos.css('.downvb').text.to_i
|
data/lib/tf2r/text_helpers.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module TF2R
|
2
|
-
#
|
2
|
+
# This module provides common methods for manipulating text throughout this
|
3
|
+
# gem. Common operations include messing with Steam ID 64s, hex color codes,
|
4
|
+
# and link to resources on TF2R.
|
3
5
|
module TextHelpers
|
4
6
|
# Extracts a hex colour code.
|
5
7
|
#
|
@@ -43,6 +45,10 @@ module TF2R
|
|
43
45
|
link = "http://tf2r.com/#{link_snippet}.html"
|
44
46
|
end
|
45
47
|
|
48
|
+
# @example
|
49
|
+
# raffle_link_full('kabc123') => 'http://tf2r.com/kabc123.html?full'
|
50
|
+
#
|
51
|
+
# @see raffle_link
|
46
52
|
def raffle_link_full(link_snippet)
|
47
53
|
"#{raffle_link(link_snippet)}?full"
|
48
54
|
end
|
data/lib/tf2r/version.rb
CHANGED
data/spec/raffle_spec.rb
CHANGED
@@ -8,23 +8,56 @@ describe TF2R::Raffle do
|
|
8
8
|
}
|
9
9
|
|
10
10
|
describe '#info' do
|
11
|
-
let(:
|
11
|
+
let(:info) { raffle.info }
|
12
12
|
|
13
13
|
it 'returns correct values' do
|
14
|
-
expect(
|
15
|
-
expect(
|
16
|
-
expect(
|
17
|
-
expect(
|
14
|
+
expect(info[:link_snippet]).to eql('kstzcbd')
|
15
|
+
expect(info[:title]).to eql('Just one refined [1 hour]')
|
16
|
+
expect(info[:description]).to eql('Plain and simple.')
|
17
|
+
expect(info[:start_time]).to eql(
|
18
18
|
Time.new(2012, 10, 29, 14, 51, 45, '+01:00')
|
19
19
|
)
|
20
|
-
expect(
|
20
|
+
expect(info[:end_time]).to eql(
|
21
21
|
Time.new(2012, 10, 29, 14, 53, 01, '+01:00')
|
22
22
|
)
|
23
|
-
expect(
|
23
|
+
expect(info[:win_chance]).to eql(0.1)
|
24
24
|
# TODO: use a raffle where current_entries != max_entries
|
25
|
-
expect(
|
26
|
-
expect(
|
27
|
-
expect(
|
25
|
+
expect(info[:current_entries]).to eql(10)
|
26
|
+
expect(info[:max_entries]).to eql(10)
|
27
|
+
expect(info[:is_done]).to eql(true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#creator' do
|
32
|
+
let(:creator) { raffle.creator }
|
33
|
+
|
34
|
+
it 'returns the raffle creator' do
|
35
|
+
expect(creator[:steam_id]).to match(76561198061719848)
|
36
|
+
expect(creator[:username]).to eql('Yulli')
|
37
|
+
expect(creator[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
38
|
+
expect(creator[:posrep]).to eql(11459)
|
39
|
+
expect(creator[:negrep]).to eql(0)
|
40
|
+
expect(creator[:colour]).to match(HEX_COLOUR_REGEXP)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#participants' do
|
45
|
+
let(:participants) { raffle.participants }
|
46
|
+
|
47
|
+
it 'returns the correct number of participants' do
|
48
|
+
expect(participants.length).to eql(10)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns the correct participants' do
|
52
|
+
expect(participants[0][:steam_id]).to match(76561198017461530)
|
53
|
+
expect(participants[0][:username]).to eql('TheJohn')
|
54
|
+
expect(participants[0][:colour]).to match(HEX_COLOUR_REGEXP)
|
55
|
+
expect(participants[0][:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
56
|
+
|
57
|
+
expect(participants[-1][:steam_id]).to match(76561198029274581)
|
58
|
+
expect(participants[-1][:username]).to eql('pony danza (OUT...')
|
59
|
+
expect(participants[-1][:colour]).to match(HEX_COLOUR_REGEXP)
|
60
|
+
expect(participants[-1][:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
28
61
|
end
|
29
62
|
end
|
30
63
|
end
|
data/spec/scraper_spec.rb
CHANGED
@@ -8,12 +8,6 @@ describe TF2R::Scraper do
|
|
8
8
|
end
|
9
9
|
}
|
10
10
|
|
11
|
-
it 'is instantiable' do
|
12
|
-
expect{
|
13
|
-
TF2R::Scraper.new()
|
14
|
-
}.not_to raise_error
|
15
|
-
end
|
16
|
-
|
17
11
|
describe '#new' do
|
18
12
|
context 'no options are given' do
|
19
13
|
it 'creates an agent with default user agent if none is specified' do
|
@@ -53,51 +47,26 @@ describe TF2R::Scraper do
|
|
53
47
|
:headers => {'content_type' => 'text/html'})
|
54
48
|
end
|
55
49
|
|
56
|
-
it 'makes a request to the specified URL' do
|
57
|
-
expect(
|
58
|
-
scraper.fetch('http://google.com')
|
59
|
-
).to have_requested(:get, 'google.com')
|
60
|
-
end
|
61
|
-
|
62
50
|
it 'returns a Mechanize::Page' do
|
63
51
|
expect(scraper.fetch('http://google.com')).to be_a(Mechanize::Page)
|
64
52
|
end
|
65
53
|
end
|
66
54
|
|
67
|
-
describe '#scrape_raffle' do
|
68
|
-
let(:result) { scraper.scrape_raffle(raffle_page) }
|
69
|
-
|
70
|
-
it 'returns an Array' do
|
71
|
-
expect(result).to be_an(Array)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns the same as its child methods' do
|
75
|
-
expect(result[0]).to eql(scraper.scrape_raffle_for_raffle(raffle_page))
|
76
|
-
expect(result[1]).to eql(scraper.scrape_raffle_for_creator(raffle_page))
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
55
|
describe '#scrape_main_page' do
|
81
56
|
let(:result) {
|
82
|
-
|
83
|
-
scraper.scrape_main_page
|
84
|
-
end
|
57
|
+
scraper.scrape_main_page
|
85
58
|
}
|
86
59
|
|
87
60
|
before(:each) do
|
88
61
|
raffles_file = File.new(File.join(File.dirname(__FILE__), 'raffles.html'))
|
62
|
+
|
89
63
|
stub_request(:get, "http://tf2r.com/raffles.html").
|
90
64
|
with(:headers => {'Accept'=>'*/*', 'Host'=>'tf2r.com'}).
|
91
65
|
to_return(:status => 200, :body => raffles_file,
|
92
66
|
:headers => {'content_type' => 'text/html'})
|
93
67
|
end
|
94
68
|
|
95
|
-
|
96
|
-
expect(result).to be_an(Array)
|
97
|
-
expect(result[0]).to be_a(String)
|
98
|
-
expect(result[-1]).to be_a(String)
|
99
|
-
end
|
100
|
-
|
69
|
+
# TODO: figure out a way to periodicially use more recent pages
|
101
70
|
it 'returns raffle links in reverse chronological order' do
|
102
71
|
# See spec/raffles.html for the hard-coded values.
|
103
72
|
expect(result[0]).to eql('http://tf2r.com/kzkp8jo.html')
|
@@ -105,12 +74,17 @@ describe TF2R::Scraper do
|
|
105
74
|
end
|
106
75
|
end
|
107
76
|
|
108
|
-
describe '#
|
109
|
-
let(:result) { scraper.
|
77
|
+
describe '#scrape_raffle' do
|
78
|
+
let(:result) { scraper.scrape_raffle(raffle_page) }
|
110
79
|
|
111
|
-
it 'returns
|
112
|
-
expect(result).to
|
80
|
+
it 'returns the same as its child methods' do
|
81
|
+
expect(result[0]).to eql(scraper.scrape_raffle_for_raffle(raffle_page))
|
82
|
+
expect(result[1]).to eql(scraper.scrape_raffle_for_creator(raffle_page))
|
113
83
|
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#scrape_raffle_for_creator' do
|
87
|
+
let(:result) { scraper.scrape_raffle_for_creator(raffle_page) }
|
114
88
|
|
115
89
|
# This is a terrible spec right now. The raffle tested against is one of
|
116
90
|
# my own, which is constantly updated as I use the site. If I gain or lose
|
@@ -118,13 +92,13 @@ describe TF2R::Scraper do
|
|
118
92
|
# TODO: Use a stagnant alt account to make a test raffle.
|
119
93
|
it 'returns correct values' do
|
120
94
|
expect(result[:steam_id]).to eql(76561198061719848)
|
121
|
-
expect(result[:colour]).to
|
95
|
+
expect(result[:colour]).to match(HEX_COLOUR_REGEXP)
|
122
96
|
|
123
97
|
# The following expectations are very brittle. TF2R updates the certain
|
124
98
|
# user attributes (such as name and avatar) every time the user logs in
|
125
99
|
# with new ones.
|
126
100
|
expect(result[:username]).to eql('Yulli')
|
127
|
-
expect(result[:avatar_link]).to
|
101
|
+
expect(result[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
128
102
|
|
129
103
|
# Talk about brittle.
|
130
104
|
expect(result[:posrep]).to eql(11459)
|
@@ -136,10 +110,6 @@ describe TF2R::Scraper do
|
|
136
110
|
describe '#scrape_raffle_for_raffle' do
|
137
111
|
let(:result) { scraper.scrape_raffle_for_raffle(raffle_page) }
|
138
112
|
|
139
|
-
it 'returns a Hash representation of a raffle' do
|
140
|
-
expect(result).to be_a(Hash)
|
141
|
-
end
|
142
|
-
|
143
113
|
it 'returns correct values' do
|
144
114
|
expect(result[:link_snippet]).to eql('kstzcbd')
|
145
115
|
expect(result[:title]).to eql('Just one refined [1 hour]')
|
@@ -154,6 +124,7 @@ describe TF2R::Scraper do
|
|
154
124
|
end
|
155
125
|
|
156
126
|
describe '#scrape_raffle_for_participants' do
|
127
|
+
# TODO: use a real test raffle
|
157
128
|
let(:page) {
|
158
129
|
VCR.use_cassette('raffle_kstzcbd_full') do
|
159
130
|
scraper.fetch('http://tf2r.com/kstzcbd.html?full')
|
@@ -161,12 +132,6 @@ describe TF2R::Scraper do
|
|
161
132
|
}
|
162
133
|
let(:result) { scraper.scrape_raffle_for_participants(page) }
|
163
134
|
|
164
|
-
it 'returns an Array of Hashes' do
|
165
|
-
expect(result).to be_an(Array)
|
166
|
-
expect(result[0]).to be_a(Hash)
|
167
|
-
expect(result[-1]).to be_a(Hash)
|
168
|
-
end
|
169
|
-
|
170
135
|
it 'returns correct values' do
|
171
136
|
expect(result[0][:steam_id]).to eql(76561198017461530)
|
172
137
|
expect(result[0][:username]).to eql('TheJohn')
|
@@ -187,17 +152,13 @@ describe TF2R::Scraper do
|
|
187
152
|
}
|
188
153
|
let(:result) { scraper.scrape_user(user_page) }
|
189
154
|
|
190
|
-
it 'returns a Hash' do
|
191
|
-
expect(result).to be_a(Hash)
|
192
|
-
end
|
193
|
-
|
194
155
|
# This spec is also brittle. It relies on my TF2R user account, which is
|
195
156
|
# updated constantly.
|
196
157
|
# TODO: use a stagnant alt account
|
197
158
|
it 'returns correct values' do
|
198
159
|
expect(result[:steam_id]).to eql(76561198061719848)
|
199
160
|
expect(result[:username]).to eql('Yulli')
|
200
|
-
expect(result[:avatar_link]).to
|
161
|
+
expect(result[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
201
162
|
expect(result[:posrep]).to eql(11459)
|
202
163
|
expect(result[:negrep]).to eql(0)
|
203
164
|
expect(result[:colour]).to eql('70b01b')
|
@@ -228,12 +189,6 @@ describe TF2R::Scraper do
|
|
228
189
|
}
|
229
190
|
let(:result) { scraper.scrape_ranks(info_page) }
|
230
191
|
|
231
|
-
it 'returns an Array of Hashes' do
|
232
|
-
expect(result).to be_an(Array)
|
233
|
-
expect(result[0]).to be_a(Hash)
|
234
|
-
expect(result[-1]).to be_a(Hash)
|
235
|
-
end
|
236
|
-
|
237
192
|
it 'returns correct values' do
|
238
193
|
expect(result[0][:name]).to eql('User')
|
239
194
|
expect(result[0][:description]).to eql(
|
@@ -242,6 +197,7 @@ describe TF2R::Scraper do
|
|
242
197
|
expect(result[0][:colour]).to eql('ebe2ca')
|
243
198
|
|
244
199
|
# This is brittle. Every time a new rank is added, this fails.
|
200
|
+
# TODO: explore possibilities to limber this up
|
245
201
|
expect(result[-1][:name]).to eql('Lense')
|
246
202
|
expect(result[-1][:description]).to eql('[ Custom donator rank ]')
|
247
203
|
expect(result[-1][:colour]).to eql('545d6c')
|
data/spec/spec_helper.rb
CHANGED
@@ -23,4 +23,7 @@ VCR.configure do |c|
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
WebMock.disable_net_connect!(allow_localhost: true)
|
26
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
27
|
+
|
28
|
+
STEAM_AVATAR_LINK_REGEXP = /^http:\/\/media\.steampowered\.com\/steamcommunity\/public\/images\/avatars\/\w{2}\/[\w]+\.jpg$/
|
29
|
+
HEX_COLOUR_REGEXP = /^[a-f0-9]{3}{1,2}$/i
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tf2r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Kim
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,6 +143,7 @@ executables: []
|
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
145
145
|
files:
|
146
|
+
- ".coveralls.yml"
|
146
147
|
- ".gitignore"
|
147
148
|
- ".ruby-gemset"
|
148
149
|
- ".ruby-version"
|