steam-condenser 0.8.0 → 0.9.0

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.
@@ -11,50 +11,59 @@ require "steam/community/game_achievement"
11
11
  class GameStats
12
12
  end
13
13
 
14
- require "steam/community/l4d/l4d_stats"
14
+ require 'steam/community/defense_grid/defense_grid_stats'
15
15
  require "steam/community/dods/dods_stats"
16
+ require "steam/community/l4d/l4d_stats"
16
17
  require "steam/community/tf2/tf2_stats"
17
18
 
18
19
  # The GameStats class represents the game statistics for a single user and a
19
20
  # specific game
20
21
  class GameStats
21
-
22
+
22
23
  protected :initialize
23
24
 
24
- attr_reader :app_id, :game_friendly_name, :game_name, :hours_played,
25
- :privacy_state, :steam_id
25
+ attr_reader :app_id, :custom_url, :game_friendly_name, :game_name,
26
+ :hours_played, :privacy_state, :steam_id64
26
27
 
27
28
  # Creates a GameStats (or one of its subclasses) object for the given user
28
29
  # depending on the game selected
29
30
  def self.create_game_stats(steam_id, game_name)
30
31
  case game_name
31
- when "DoD:S":
32
+ when 'defensegrid:awakening'
33
+ DefenseGridStats.new(steam_id)
34
+ when 'dod:s'
32
35
  DoDSStats.new(steam_id)
33
- when "L4D":
36
+ when 'l4d'
34
37
  L4DStats.new(steam_id)
35
- when "TF2":
38
+ when 'tf2'
36
39
  TF2Stats.new(steam_id)
37
40
  else
38
41
  new(steam_id, game_name)
39
42
  end
40
43
  end
41
-
44
+
42
45
  # Creates a GameStats object and fetchs data from Steam Community for the
43
46
  # given user and game
44
- def initialize(steam_id, game_name)
45
- @steam_id = steam_id
46
-
47
- @xml_data = REXML::Document.new(open("http://www.steamcommunity.com/id/#{@steam_id}/stats/#{game_name}?xml=1", {:proxy => true}).read).elements["playerstats"]
48
-
49
- @privacy_state = @xml_data.elements["privacyState"].text
47
+ def initialize(id, game_name)
48
+ if id.is_a? Numeric
49
+ @steam_id64 = id
50
+ else
51
+ @custom_url = id.downcase
52
+ end
53
+ @game_friendly_name = game_name
54
+
55
+ url = base_url + '?xml=1'
56
+ @xml_data = REXML::Document.new(open(url, {:proxy => true}).read).root
57
+
58
+ @privacy_state = @xml_data.elements['privacyState'].text
50
59
  if public?
51
- @app_id = @xml_data.elements["game"].elements["gameLink"].text.match("http://store.steampowered.com/app/([1-9][0-9]+)")[1]
52
- @game_friendly_name = @xml_data.elements["game"].elements["gameFriendlyName"].text
53
- @game_name = @xml_data.elements["game"].elements["gameName"].text
54
- @hours_played = @xml_data.elements["stats"].elements["hoursPlayed"].text
60
+ @app_id = @xml_data.elements['game/gameLink'].text.match(/http:\/\/store.steampowered.com\/app\/([1-9][0-9]+)/)[1]
61
+ @game_friendly_name = @xml_data.elements['game/gameFriendlyName'].text
62
+ @game_name = @xml_data.elements['game/gameName'].text
63
+ @hours_played = @xml_data.elements['stats/hoursPlayed'].text
55
64
  end
56
65
  end
57
-
66
+
58
67
  # Returns the achievements for this stats' user and game. If the achievements
59
68
  # haven't been parsed already, parsing is done now.
60
69
  def achievements
@@ -62,16 +71,16 @@ class GameStats
62
71
 
63
72
  if @achievements.nil?
64
73
  @achievements = Array.new
65
- @xml_data.elements["achievements"].elements.each("achievement") do |achievement|
66
- @achievements << GameAchievement.new(@steam_id, @app_id, achievement.elements["name"].text, (achievement.attributes["closed"].to_i == 1))
74
+ @xml_data.elements.each('achievements/achievement') do |achievement|
75
+ @achievements << GameAchievement.new(@steam_id, @app_id, achievement.elements['name'].text, (achievement.attributes['closed'].to_i == 1))
67
76
  end
68
77
 
69
78
  @achievements_done = @achievements.reject{ |a| !a.done? }.size
70
79
  end
71
-
72
- return @achievements
80
+
81
+ @achievements
73
82
  end
74
-
83
+
75
84
  # Returns the count of achievements done by this player. If achievements
76
85
  # haven't been parsed yet, parsing is done now.
77
86
  def achievements_done
@@ -85,8 +94,17 @@ class GameStats
85
94
  achievements_done.to_f / @achievements.size
86
95
  end
87
96
 
97
+ # Returns the base URL for this Steam Communtiy object
98
+ def base_url
99
+ if @custom_url.nil?
100
+ "http://steamcommunity.com/profiles/#{@steam_id64}/stats/#{@game_friendly_name}"
101
+ else
102
+ "http://steamcommunity.com/id/#{@custom_url}/stats/#{@game_friendly_name}"
103
+ end
104
+ end
105
+
88
106
  def public?
89
107
  @privacy_state == 'public'
90
108
  end
91
-
92
- end
109
+
110
+ end
@@ -20,11 +20,11 @@ class L4DMap
20
20
  @times_played = map_data.elements['timesplayed'].text.to_i
21
21
 
22
22
  case map_data.elements['medal'].text
23
- when 'gold':
23
+ when 'gold'
24
24
  @medal = L4DMap::GOLD
25
- when 'silver':
25
+ when 'silver'
26
26
  @medal = L4DMap::SILVER
27
- when 'bronze':
27
+ when 'bronze'
28
28
  @medal = L4DMap::BRONZE
29
29
  else
30
30
  @medal = L4DMap::NONE
@@ -1,16 +1,87 @@
1
1
  # This code is free software; you can redistribute it and/or modify it under the
2
2
  # terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2008, Sebastian Staudt
5
- #
6
- # $Id$
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ require 'open-uri'
7
+ require 'rexml/document'
8
+
9
+ require 'steam/community/cacheable'
10
+ require 'steam/community/steam_id'
7
11
 
8
12
  # The SteamGroup class represents a group in the Steam Community
9
13
  class SteamGroup
10
-
14
+
15
+ include Cacheable
16
+ cacheable_with_ids :custom_url, :group_id64
17
+
18
+ attr_reader :custom_url, :group_id64
19
+
11
20
  # Creates a SteamGroup object with the given group ID
12
- def initialize(id)
13
- @id = id
21
+ def initialize(id, fetch = true)
22
+ begin
23
+ if id.is_a? Numeric
24
+ @group_id64 = id
25
+ else
26
+ @custom_url = id.downcase
27
+ end
28
+
29
+ super(fetch)
30
+ rescue REXML::ParseException
31
+ raise SteamCondenserException.new('Group could not be loaded.')
32
+ end
33
+ end
34
+
35
+ # Returns the URL to the group's Steam Community page
36
+ def base_url
37
+ if @custom_url.nil?
38
+ "http://steamcommunity.com/gid/#{@group_id64}"
39
+ else
40
+ "http://steamcommunity.com/groups/#{@custom_url}"
41
+ end
42
+ end
43
+
44
+ # Parses the data about this groups members
45
+ def fetch
46
+ @members = []
47
+ page = 0
48
+
49
+ begin
50
+ page += 1
51
+ url = open("#{base_url}/memberslistxml?p=#{page}", {:proxy => true})
52
+ member_data = REXML::Document.new(url.read).root
53
+
54
+ if page == 1
55
+ @group_id64 = member_data.elements['groupID64'].text.to_i
56
+ end
57
+ total_pages = member_data.elements['totalPages'].text.to_i
58
+
59
+ member_data.elements['members'].elements.each do |member|
60
+ @members << SteamId.new(member.text.to_i, false)
61
+ end
62
+ end while page < total_pages
63
+
64
+ super
65
+ end
66
+
67
+ # Returns the number of members this group has.
68
+ # If the members have already been fetched with +fetch_members+ the size of
69
+ # the member array is returned. Otherwise the group size is separately
70
+ # fetched.
71
+ def member_count
72
+ if @members.nil?
73
+ url = open("#{base_url}/memberslistxml", {:proxy => true})
74
+ REXML::Document.new(url.read).root.elements['memberCount'].text.to_i
75
+ else
76
+ @members.size
77
+ end
78
+ end
79
+
80
+ # Returns the members of this group
81
+ # Calls +fetch_members+ if the members haven't been fetched already.
82
+ def members
83
+ fetch_members if @members.nil? or @members[0].nil?
84
+ @members
14
85
  end
15
86
 
16
87
  end
@@ -3,55 +3,60 @@
3
3
  #
4
4
  # Copyright (c) 2008-2009, Sebastian Staudt
5
5
 
6
- require "open-uri"
7
- require "rexml/document"
6
+ require 'open-uri'
7
+ require 'rexml/document'
8
8
 
9
- require "exceptions/steam_condenser_exception"
10
- require "steam/community/game_stats"
11
- require "steam/community/steam_group"
9
+ require 'exceptions/steam_condenser_exception'
10
+ require 'steam/community/cacheable'
11
+ require 'steam/community/game_stats'
12
+ require 'steam/community/steam_group'
12
13
 
13
14
  # The SteamId class represents a Steam Community profile (also called Steam ID)
14
15
  class SteamId
15
-
16
+
17
+ include Cacheable
18
+ cacheable_with_ids :custom_url, :steam_id64
19
+
16
20
  attr_reader :custom_url, :favorite_game, :favorite_game_hours_played,
17
- :friends, :groups, :head_line, :hours_played, :image_url, :links,
18
- :location, :member_since, :most_played_games, :privacy_state,
19
- :real_name, :state_message, :steam_rating, :steam_rating_text,
20
- :summary, :vac_banned, :visibility_state
21
+ :groups, :head_line, :hours_played, :image_url, :links, :location,
22
+ :member_since, :most_played_games, :nickname, :privacy_state,
23
+ :real_name, :state_message, :steam_id64, :steam_rating,
24
+ :steam_rating_text, :summary, :vac_banned, :visibility_state
21
25
 
22
- # Converts a SteamID as reported by game servers to a 64bit SteamID
26
+ # Converts the SteamID +steam_id+ as reported by game servers to a 64bit
27
+ # SteamID
23
28
  def self.convert_steam_id_to_community_id(steam_id)
24
- if steam_id == "STEAM_ID_LAN"
25
- raise SteamCondenserException.new("Cannot convert SteamID \"STEAM_ID_LAN\" to a community ID.")
26
- elsif steam_id.match(/STEAM_[0-1]:[0-1]:[0-9]+/).nil?
29
+ if steam_id == 'STEAM_ID_LAN' or steam_id == 'BOT'
30
+ raise SteamCondenserException.new("Cannot convert SteamID \"#{steam_id}\" to a community ID.")
31
+ elsif steam_id.match(/^STEAM_[0-1]:[0-1]:[0-9]+$/).nil?
27
32
  raise SteamCondenserException.new("SteamID \"#{steam_id}\" doesn't have the correct format.")
28
33
  end
29
-
30
- steam_id = steam_id[6..-1].split(":").map!{|s| s.to_i}
31
-
32
- return steam_id[1] + 76561197960265728 + steam_id[2] * 2
34
+
35
+ steam_id = steam_id[6..-1].split(':').map!{|s| s.to_i}
36
+
37
+ steam_id[1] + steam_id[2] * 2 + 76561197960265728
33
38
  end
34
39
 
35
- # Creates a new SteamId object using a SteamID64 converted from a server
36
- # SteamID
40
+ # Creates a new SteamId object using the SteamID64 converted from a server
41
+ # SteamID given by +steam_id+
37
42
  def self.get_from_steam_id(steam_id)
38
- return self.new(self.convert_steam_id_to_community_id(steam_id))
43
+ self.new(self.convert_steam_id_to_community_id(steam_id))
39
44
  end
40
45
 
41
- # Creates a new SteamId object for the given SteamID, either numeric or the
42
- # custom URL specified by the user. If +fetch+ is +true+ (default), fetch_data
43
- # is used to load data into the object.
46
+ # Creates a new SteamId object for the given SteamID +id+, either numeric or
47
+ # the custom URL specified by the user. If +fetch+ is +true+ (default),
48
+ # fetch_data is used to load data into the object.
44
49
  def initialize(id, fetch = true)
45
- if id.is_a? Numeric
46
- @steam_id64 = id
47
- else
48
- @custom_url = id
49
- end
50
-
51
50
  begin
52
- self.fetch_data if fetch
51
+ if id.is_a? Numeric
52
+ @steam_id64 = id
53
+ else
54
+ @custom_url = id.downcase
55
+ end
56
+
57
+ super(fetch)
53
58
  rescue REXML::ParseException
54
- raise SteamCondenserException.new("SteamID could not be loaded.")
59
+ raise SteamCondenserException.new('SteamID could not be loaded.')
55
60
  end
56
61
  end
57
62
 
@@ -63,78 +68,92 @@ class SteamId
63
68
  "http://steamcommunity.com/id/#{@custom_url}"
64
69
  end
65
70
  end
66
-
71
+
67
72
  # Fetchs data from the Steam Community by querying the XML version of the
68
73
  # profile specified by the ID of this SteamID
69
- def fetch_data
70
- url = base_url << '?xml=1'
74
+ def fetch
75
+ profile_url = open(base_url + '?xml=1', {:proxy => true})
76
+ profile = REXML::Document.new(profile_url.read).root
71
77
 
72
- profile_url = open(url, {:proxy => true})
73
- if profile_url.base_uri.to_s != url
74
- profile_url = open(profile_url.base_uri.to_s + "?xml=1", {:proxy => true})
78
+ unless REXML::XPath.first(profile, 'error').nil?
79
+ raise SteamCondenserException.new(profile.elements['error'].text)
75
80
  end
76
-
77
- profile = REXML::Document.new(profile_url.read).elements["profile"]
78
-
79
- @image_url = profile.elements["avatarIcon"].text[0..-5]
80
- @online_state = profile.elements["onlineState"].text
81
- @privacy_state = profile.elements["privacyState"].text
82
- @state_message = profile.elements["stateMessage"].text
83
- @steam_id = profile.elements["steamID"].text
84
- @steam_id64 = profile.elements["steamID64"].text.to_i
85
- @vac_banned = (profile.elements["vacBanned"].text == 1)
86
- @visibility_state = profile.elements["visibilityState"].text.to_i
87
-
88
- # Only public profiles can be scanned for further information
89
- if @privacy_state == "public"
90
- @custom_url = profile.elements["customURL"].text
91
81
 
92
- unless REXML::XPath.first(profile, "favoriteGame").nil?
93
- @favorite_game = profile.elements["favoriteGame/name"].text
94
- @favorite_game_hours_played = profile.elements["favoriteGame/hoursPlayed2wk"].text
82
+ @image_url = profile.elements['avatarIcon'].text[0..-5]
83
+ @online_state = profile.elements['onlineState'].text
84
+ @privacy_state = profile.elements['privacyState'].text
85
+ @state_message = profile.elements['stateMessage'].text
86
+ @nickname = profile.elements['steamID'].text
87
+ @steam_id64 = profile.elements['steamID64'].text.to_i
88
+ @vac_banned = (profile.elements['vacBanned'].text == 1)
89
+ @visibility_state = profile.elements['visibilityState'].text.to_i
90
+
91
+ # Only public profiles can be scanned for further information
92
+ if @privacy_state == 'public'
93
+ @custom_url = profile.elements['customURL'].text.downcase
94
+
95
+ # The favorite game cannot be set since 10/10/2008, but old profiles
96
+ # still have this. May be removed in a future version.
97
+ unless REXML::XPath.first(profile, 'favoriteGame').nil?
98
+ @favorite_game = profile.elements['favoriteGame/name'].text
99
+ @favorite_game_hours_played = profile.elements['favoriteGame/hoursPlayed2wk'].text
95
100
  end
96
101
 
97
- @head_line = profile.elements["headline"].text
98
- @hours_played = profile.elements["hoursPlayed2Wk"].text.to_f
99
- @location = profile.elements["location"].text
100
- @member_since = Time.parse profile.elements["memberSince"].text
101
- @real_name = profile.elements["realname"].text
102
- @steam_rating = profile.elements["steamRating"].text.to_f
103
- @summary = profile.elements["summary"].text
104
-
105
- unless REXML::XPath.first(profile, "mostPlayedGames").nil?
106
- @most_played_games = Hash.new
107
- profile.elements["mostPlayedGames"].elements.each("mostPlayedGame") do |most_played_game|
108
- @most_played_games[most_played_game.elements["gameName"].text] = most_played_game.elements["hoursPlayed"].text.to_f
102
+ @head_line = profile.elements['headline'].text
103
+ @hours_played = profile.elements['hoursPlayed2Wk'].text.to_f
104
+ @location = profile.elements['location'].text
105
+ @member_since = Time.parse(profile.elements['memberSince'].text)
106
+ @real_name = profile.elements['realname'].text
107
+ @steam_rating = profile.elements['steamRating'].text.to_f
108
+ @summary = profile.elements['summary'].text
109
+
110
+ # The most played games only exist if a user played at least one game in
111
+ # the last two weeks
112
+ @most_played_games = {}
113
+ unless REXML::XPath.first(profile, 'mostPlayedGames').nil?
114
+ profile.elements.each('mostPlayedGames/mostPlayedGame') do |most_played_game|
115
+ @most_played_games[most_played_game.elements['gameName'].text] = most_played_game.elements['hoursPlayed'].text.to_f
109
116
  end
110
117
  end
111
-
112
- @friends = Array.new
113
- profile.elements["friends"].elements.each("friend") do |friend|
114
- @friends << SteamId.new(friend.elements["steamID64"].text.to_i, false)
115
- end
116
-
117
- @groups = Array.new
118
- profile.elements["groups"].elements.each("group") do |group|
119
- @groups << SteamGroup.new(group.elements["groupID64"].text.to_i)
118
+
119
+ @groups = []
120
+ unless REXML::XPath.first(profile, 'groups').nil?
121
+ profile.elements.each('groups/group') do |group|
122
+ @groups << SteamGroup.new(group.elements['groupID64'].text.to_i, false)
123
+ end
120
124
  end
121
-
122
- @links = Hash.new
123
- profile.elements["weblinks"].elements.each("weblink") do |link|
124
- @links[link.elements["title"].text] = link.elements["link"].text
125
+
126
+ @links = {}
127
+ unless REXML::XPath.first(profile, 'mostPlayedGames').nil?
128
+ profile.elements.each('weblinks/weblink') do |link|
129
+ @links[link.elements['title'].text] = link.elements['link'].text
130
+ end
125
131
  end
126
132
  end
133
+
134
+ super
135
+ end
136
+
137
+ # Fetches the friends of this user
138
+ def fetch_friends
139
+ url = "#{base_url}/friends?xml=1"
140
+
141
+ @friends = []
142
+ friends_data = REXML::Document.new(open(url, {:proxy => true}).read).root
143
+ friends_data.elements.each('friends/friend') do |friend|
144
+ @friends << SteamId.new(friend.text.to_i, false)
145
+ end
127
146
  end
128
147
 
129
148
  # Fetches the games this user owns
130
149
  def fetch_games
131
150
  require 'rubygems'
132
- require 'Hpricot'
151
+ require 'hpricot'
133
152
 
134
- url = base_url << '/games'
153
+ url = "#{base_url}/games"
135
154
 
136
155
  @games = {}
137
- games_data = Hpricot(open(url).read).at('div#mainContents')
156
+ games_data = Hpricot(open(url, {:proxy => true}).read).at('div#mainContents')
138
157
 
139
158
  games_data.traverse_some_element('h4') do |game|
140
159
  game_name = game.inner_html
@@ -153,18 +172,18 @@ class SteamId
153
172
  end
154
173
  end
155
174
 
156
- return true
175
+ true
157
176
  end
158
-
159
- # Returns the URL of the full version of this user's avatar
177
+
178
+ # Returns the URL of the full-sized version of this user's avatar
160
179
  def full_avatar_url
161
- return "#{@image_url}_full.jpg"
180
+ "#{@image_url}_full.jpg"
162
181
  end
163
-
182
+
164
183
  # Returns a GameStats object for the given game for the owner of this SteamID
165
184
  def game_stats(game_name)
166
185
  game_name.downcase!
167
-
186
+
168
187
  if games.has_value? game_name
169
188
  friendly_name = game_name
170
189
  elsif games.has_key? game_name
@@ -176,6 +195,13 @@ class SteamId
176
195
  GameStats.create_game_stats(@custom_url || @steam_id64, friendly_name)
177
196
  end
178
197
 
198
+ # Returns an Array of SteamId representing all Steam Community friends of this
199
+ # user.
200
+ def friends
201
+ fetch_friends if @friends.nil?
202
+ @friends
203
+ end
204
+
179
205
  # Returns a Hash with the games this user owns. The keys are the games' names
180
206
  # and the values are the "friendly names" used for stats or +false+ if the
181
207
  # games has no stats.
@@ -183,30 +209,30 @@ class SteamId
183
209
  fetch_games if @games.nil?
184
210
  @games
185
211
  end
186
-
212
+
187
213
  # Returns the URL of the icon version of this user's avatar
188
214
  def icon_url
189
- return "#{@image_url}.jpg"
215
+ "#{@image_url}.jpg"
190
216
  end
191
-
217
+
192
218
  # Returns whether the owner of this SteamID is VAC banned
193
219
  def is_banned?
194
- return @vac_banned
220
+ @vac_banned
195
221
  end
196
-
222
+
197
223
  # Returns whether the owner of this SteamId is playing a game
198
224
  def is_in_game?
199
- return @online_state == "in-game"
225
+ @online_state == 'in-game'
200
226
  end
201
-
227
+
202
228
  # Returns whether the owner of this SteamID is currently logged into Steam
203
229
  def is_online?
204
- return @online_state != "offline"
230
+ @online_state != 'offline'
205
231
  end
206
-
207
- # Returns the URL of the medium version of this user's avatar
232
+
233
+ # Returns the URL of the medium-sized version of this user's avatar
208
234
  def medium_avatar_url
209
- return "#{@image_url}_medium.jpg"
235
+ "#{@image_url}_medium.jpg"
210
236
  end
211
237
 
212
238
  end