steam-condenser 1.3.5 → 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +228 -0
  3. data/CONTRIBUTING.md +59 -0
  4. data/Gemfile.lock +12 -13
  5. data/README.md +9 -4
  6. data/lib/steam-condenser/version.rb +1 -1
  7. data/lib/steam/community/alien_swarm/alien_swarm_mission.rb +10 -9
  8. data/lib/steam/community/alien_swarm/alien_swarm_stats.rb +74 -72
  9. data/lib/steam/community/css/css_map.rb +2 -2
  10. data/lib/steam/community/css/css_stats.rb +54 -56
  11. data/lib/steam/community/css/css_weapon.rb +4 -4
  12. data/lib/steam/community/game_item.rb +2 -3
  13. data/lib/steam/community/game_leaderboard.rb +22 -15
  14. data/lib/steam/community/l4d/abstract_l4d_stats.rb +64 -61
  15. data/lib/steam/community/l4d/l4d2_stats.rb +26 -21
  16. data/lib/steam/community/steam_id.rb +8 -11
  17. data/lib/steam/community/web_api.rb +19 -14
  18. data/lib/steam/packets/s2a_info2_packet.rb +16 -14
  19. data/lib/steam/packets/s2a_info_detailed_packet.rb +24 -19
  20. data/lib/steam/packets/steam_packet_factory.rb +7 -2
  21. data/lib/steam/servers/game_server.rb +3 -2
  22. data/lib/steam/servers/master_server.rb +1 -1
  23. data/lib/steam/sockets/source_socket.rb +4 -9
  24. data/pkg/steam-condenser-1.3.5.gem +0 -0
  25. data/pkg/steam-condenser-1.3.5/Gemfile +7 -0
  26. data/pkg/steam-condenser-1.3.5/Gemfile.lock +45 -0
  27. data/pkg/steam-condenser-1.3.5/LICENSE +25 -0
  28. data/pkg/steam-condenser-1.3.5/README.md +70 -0
  29. data/pkg/steam-condenser-1.3.5/Rakefile +46 -0
  30. data/pkg/steam-condenser-1.3.5/lib/core_ext/stringio.rb +91 -0
  31. data/pkg/steam-condenser-1.3.5/lib/errors/packet_format_error.rb +13 -0
  32. data/pkg/steam-condenser-1.3.5/lib/errors/rcon_ban_error.rb +22 -0
  33. data/pkg/steam-condenser-1.3.5/lib/errors/rcon_no_auth_error.rb +21 -0
  34. data/pkg/steam-condenser-1.3.5/lib/errors/steam_condenser_error.rb +26 -0
  35. data/pkg/steam-condenser-1.3.5/lib/errors/timeout_error.rb +28 -0
  36. data/pkg/steam-condenser-1.3.5/lib/errors/web_api_error.rb +57 -0
  37. data/pkg/steam-condenser-1.3.5/lib/steam-condenser.rb +18 -0
  38. data/pkg/steam-condenser-1.3.5/lib/steam-condenser/community.rb +24 -0
  39. data/pkg/steam-condenser-1.3.5/lib/steam-condenser/servers.rb +24 -0
  40. data/pkg/steam-condenser-1.3.5/lib/steam-condenser/version.rb +11 -0
  41. data/pkg/steam-condenser-1.3.5/lib/steam/community/alien_swarm/alien_swarm_mission.rb +119 -0
  42. data/pkg/steam-condenser-1.3.5/lib/steam/community/alien_swarm/alien_swarm_stats.rb +186 -0
  43. data/pkg/steam-condenser-1.3.5/lib/steam/community/alien_swarm/alien_swarm_weapon.rb +49 -0
  44. data/pkg/steam-condenser-1.3.5/lib/steam/community/app_news.rb +133 -0
  45. data/pkg/steam-condenser-1.3.5/lib/steam/community/cacheable.rb +199 -0
  46. data/pkg/steam-condenser-1.3.5/lib/steam/community/css/css_map.rb +61 -0
  47. data/pkg/steam-condenser-1.3.5/lib/steam/community/css/css_stats.rb +142 -0
  48. data/pkg/steam-condenser-1.3.5/lib/steam/community/css/css_weapon.rb +69 -0
  49. data/pkg/steam-condenser-1.3.5/lib/steam/community/defense_grid/defense_grid_stats.rb +238 -0
  50. data/pkg/steam-condenser-1.3.5/lib/steam/community/dods/dods_class.rb +90 -0
  51. data/pkg/steam-condenser-1.3.5/lib/steam/community/dods/dods_stats.rb +62 -0
  52. data/pkg/steam-condenser-1.3.5/lib/steam/community/dods/dods_weapon.rb +67 -0
  53. data/pkg/steam-condenser-1.3.5/lib/steam/community/dota2/dota2_beta_inventory.rb +30 -0
  54. data/pkg/steam-condenser-1.3.5/lib/steam/community/dota2/dota2_inventory.rb +30 -0
  55. data/pkg/steam-condenser-1.3.5/lib/steam/community/dota2/dota2_item.rb +33 -0
  56. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_achievement.rb +109 -0
  57. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_class.rb +22 -0
  58. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_inventory.rb +171 -0
  59. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_item.rb +156 -0
  60. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_item_schema.rb +131 -0
  61. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_leaderboard.rb +205 -0
  62. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_leaderboard_entry.rb +43 -0
  63. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_stats.rb +175 -0
  64. data/pkg/steam-condenser-1.3.5/lib/steam/community/game_weapon.rb +41 -0
  65. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/abstract_l4d_stats.rb +185 -0
  66. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/abstract_l4d_weapon.rb +56 -0
  67. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d2_map.rb +90 -0
  68. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d2_stats.rb +174 -0
  69. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d2_weapon.rb +38 -0
  70. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d_explosive.rb +34 -0
  71. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d_map.rb +65 -0
  72. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d_stats.rb +73 -0
  73. data/pkg/steam-condenser-1.3.5/lib/steam/community/l4d/l4d_weapon.rb +26 -0
  74. data/pkg/steam-condenser-1.3.5/lib/steam/community/portal2/portal2_inventory.rb +30 -0
  75. data/pkg/steam-condenser-1.3.5/lib/steam/community/portal2/portal2_item.rb +55 -0
  76. data/pkg/steam-condenser-1.3.5/lib/steam/community/portal2/portal2_stats.rb +33 -0
  77. data/pkg/steam-condenser-1.3.5/lib/steam/community/steam_game.rb +193 -0
  78. data/pkg/steam-condenser-1.3.5/lib/steam/community/steam_group.rb +129 -0
  79. data/pkg/steam-condenser-1.3.5/lib/steam/community/steam_id.rb +498 -0
  80. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_beta_inventory.rb +28 -0
  81. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_class.rb +92 -0
  82. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_class_factory.rb +40 -0
  83. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_engineer.rb +43 -0
  84. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_golden_wrench.rb +70 -0
  85. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_inventory.rb +30 -0
  86. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_item.rb +55 -0
  87. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_medic.rb +35 -0
  88. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_sniper.rb +28 -0
  89. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_spy.rb +41 -0
  90. data/pkg/steam-condenser-1.3.5/lib/steam/community/tf2/tf2_stats.rb +77 -0
  91. data/pkg/steam-condenser-1.3.5/lib/steam/community/web_api.rb +130 -0
  92. data/pkg/steam-condenser-1.3.5/lib/steam/community/xml_data.rb +27 -0
  93. data/pkg/steam-condenser-1.3.5/lib/steam/packets/a2m_get_servers_batch2_packet.rb +59 -0
  94. data/pkg/steam-condenser-1.3.5/lib/steam/packets/a2s_info_packet.rb +24 -0
  95. data/pkg/steam-condenser-1.3.5/lib/steam/packets/a2s_player_packet.rb +31 -0
  96. data/pkg/steam-condenser-1.3.5/lib/steam/packets/a2s_rules_packet.rb +32 -0
  97. data/pkg/steam-condenser-1.3.5/lib/steam/packets/a2s_serverquery_getchallenge_packet.rb +25 -0
  98. data/pkg/steam-condenser-1.3.5/lib/steam/packets/c2m_checkmd5_packet.rb +31 -0
  99. data/pkg/steam-condenser-1.3.5/lib/steam/packets/m2a_server_batch_packet.rb +52 -0
  100. data/pkg/steam-condenser-1.3.5/lib/steam/packets/m2c_isvalidmd5_packet.rb +34 -0
  101. data/pkg/steam-condenser-1.3.5/lib/steam/packets/m2s_requestrestart_packet.rb +34 -0
  102. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_auth_request.rb +28 -0
  103. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_auth_response.rb +30 -0
  104. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_exec_request.rb +28 -0
  105. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_exec_response.rb +37 -0
  106. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_goldsrc_request.rb +35 -0
  107. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_goldsrc_response.rb +34 -0
  108. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_packet.rb +57 -0
  109. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_packet_factory.rb +43 -0
  110. data/pkg/steam-condenser-1.3.5/lib/steam/packets/rcon/rcon_terminator.rb +29 -0
  111. data/pkg/steam-condenser-1.3.5/lib/steam/packets/request_with_challenge.rb +19 -0
  112. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_info2_packet.rb +74 -0
  113. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_info_base_packet.rb +25 -0
  114. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_info_detailed_packet.rb +59 -0
  115. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_logstring_packet.rb +30 -0
  116. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_player_packet.rb +44 -0
  117. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2a_rules_packet.rb +48 -0
  118. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2c_challenge_packet.rb +35 -0
  119. data/pkg/steam-condenser-1.3.5/lib/steam/packets/s2m_heartbeat2_packet.rb +72 -0
  120. data/pkg/steam-condenser-1.3.5/lib/steam/packets/steam_packet.rb +52 -0
  121. data/pkg/steam-condenser-1.3.5/lib/steam/packets/steam_packet_factory.rb +108 -0
  122. data/pkg/steam-condenser-1.3.5/lib/steam/servers/game_server.rb +392 -0
  123. data/pkg/steam-condenser-1.3.5/lib/steam/servers/goldsrc_server.rb +74 -0
  124. data/pkg/steam-condenser-1.3.5/lib/steam/servers/master_server.rb +188 -0
  125. data/pkg/steam-condenser-1.3.5/lib/steam/servers/server.rb +115 -0
  126. data/pkg/steam-condenser-1.3.5/lib/steam/servers/source_server.rb +117 -0
  127. data/pkg/steam-condenser-1.3.5/lib/steam/sockets/goldsrc_socket.rb +142 -0
  128. data/pkg/steam-condenser-1.3.5/lib/steam/sockets/master_server_socket.rb +34 -0
  129. data/pkg/steam-condenser-1.3.5/lib/steam/sockets/rcon_socket.rb +103 -0
  130. data/pkg/steam-condenser-1.3.5/lib/steam/sockets/source_socket.rb +78 -0
  131. data/pkg/steam-condenser-1.3.5/lib/steam/sockets/steam_socket.rb +91 -0
  132. data/pkg/steam-condenser-1.3.5/lib/steam/steam_player.rb +143 -0
  133. data/pkg/steam-condenser-1.3.5/steam-condenser.gemspec +26 -0
  134. data/pkg/steam-condenser-1.3.5/test/core_ext/test_stringio.rb +59 -0
  135. data/pkg/steam-condenser-1.3.5/test/fixtures/invalid.xml +2 -0
  136. data/pkg/steam-condenser-1.3.5/test/fixtures/sonofthor.xml +882 -0
  137. data/pkg/steam-condenser-1.3.5/test/fixtures/status_goldsrc +3 -0
  138. data/pkg/steam-condenser-1.3.5/test/fixtures/status_source +3 -0
  139. data/pkg/steam-condenser-1.3.5/test/fixtures/valve-members.xml +231 -0
  140. data/pkg/steam-condenser-1.3.5/test/helper.rb +48 -0
  141. data/pkg/steam-condenser-1.3.5/test/steam/community/test_cacheable.rb +100 -0
  142. data/pkg/steam-condenser-1.3.5/test/steam/community/test_steam_group.rb +83 -0
  143. data/pkg/steam-condenser-1.3.5/test/steam/community/test_steam_id.rb +144 -0
  144. data/pkg/steam-condenser-1.3.5/test/steam/community/test_web_api.rb +98 -0
  145. data/pkg/steam-condenser-1.3.5/test/steam/packets/test_steam_packet.rb +37 -0
  146. data/pkg/steam-condenser-1.3.5/test/steam/servers/test_game_server.rb +296 -0
  147. data/pkg/steam-condenser-1.3.5/test/steam/servers/test_goldsrc_server.rb +59 -0
  148. data/pkg/steam-condenser-1.3.5/test/steam/servers/test_master_server.rb +131 -0
  149. data/pkg/steam-condenser-1.3.5/test/steam/servers/test_server.rb +72 -0
  150. data/pkg/steam-condenser-1.3.5/test/steam/servers/test_source_server.rb +134 -0
  151. data/pkg/steam-condenser-1.3.5/test/steam/sockets/test_goldsrc_socket.rb +127 -0
  152. data/pkg/steam-condenser-1.3.5/test/steam/sockets/test_master_server_socket.rb +42 -0
  153. data/pkg/steam-condenser-1.3.5/test/steam/sockets/test_rcon_socket.rb +118 -0
  154. data/pkg/steam-condenser-1.3.5/test/steam/sockets/test_source_socket.rb +77 -0
  155. data/pkg/steam-condenser-1.3.5/test/steam/sockets/test_steam_socket.rb +97 -0
  156. data/steam-condenser.gemspec +2 -3
  157. data/test/steam/community/test_cacheable.rb +3 -0
  158. data/test/steam/community/test_web_api.rb +15 -2
  159. data/test/steam/packets/test_steam_packet.rb +1 -1
  160. data/test/steam/sockets/test_source_socket.rb +1 -1
  161. metadata +151 -27
@@ -0,0 +1,129 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2013, Sebastian Staudt
5
+
6
+ require 'errors/steam_condenser_error'
7
+ require 'steam/community/cacheable'
8
+ require 'steam/community/steam_id'
9
+ require 'steam/community/xml_data'
10
+
11
+ # The SteamGroup class represents a group in the Steam Community
12
+ #
13
+ # @author Sebastian Staudt
14
+ class SteamGroup
15
+
16
+ include Cacheable
17
+ cacheable_with_ids :custom_url, :group_id64
18
+
19
+ include XMLData
20
+
21
+ # Returns the custom URL of this group
22
+ #
23
+ # The custom URL is a admin specified unique string that can be used instead
24
+ # of the 64bit SteamID as an identifier for a group.
25
+ #
26
+ # @return [String] The custom URL of this group
27
+ attr_reader :custom_url
28
+
29
+ # Returns this group's 64bit SteamID
30
+ #
31
+ # @return [Fixnum] This group's 64bit SteamID
32
+ attr_reader :group_id64
33
+
34
+ # Creates a new `SteamGroup` instance for the group with the given ID
35
+ #
36
+ # @param [String, Fixnum] id The custom URL of the group specified by the
37
+ # group admin or the 64bit group ID
38
+ # @macro cacheable
39
+ def initialize(id)
40
+ if id.is_a? Numeric
41
+ @group_id64 = id
42
+ else
43
+ @custom_url = id.downcase
44
+ end
45
+ @members = []
46
+ end
47
+
48
+ # Returns the base URL for this group's page
49
+ #
50
+ # This URL is different for groups having a custom URL.
51
+ #
52
+ # @return [String] The base URL for this group
53
+ def base_url
54
+ if @custom_url.nil?
55
+ "http://steamcommunity.com/gid/#@group_id64"
56
+ else
57
+ "http://steamcommunity.com/groups/#@custom_url"
58
+ end
59
+ end
60
+
61
+ # Loads the members of this group
62
+ #
63
+ # This might take several HTTP requests as the Steam Community splits this
64
+ # data over several XML documents if the group has lots of members.
65
+ #
66
+ # @see Cacheable#fetch
67
+ def fetch
68
+ if @member_count.nil? || @member_count == @members.size
69
+ page = 0
70
+ else
71
+ page = 1
72
+ end
73
+
74
+ begin
75
+ total_pages = fetch_page(page += 1)
76
+ end while page < total_pages
77
+ end
78
+
79
+ # Returns the number of members this group has
80
+ #
81
+ # If the members have already been fetched the size of the member array is
82
+ # returned. Otherwise the the first page of the member listing is fetched and
83
+ # the member count and the first batch of members is stored.
84
+ #
85
+ # @return [Fixnum] The number of this group's members
86
+ def member_count
87
+ if @member_count.nil?
88
+ total_pages = fetch_page(1)
89
+ @fetch_time = Time.now if total_pages == 1
90
+ end
91
+
92
+ @member_count
93
+ end
94
+
95
+ # Returns the members of this group
96
+ #
97
+ # If the members haven't been fetched yet, this is done now.
98
+ #
99
+ # @return [Array<SteamId>] The Steam ID's of the members of this group
100
+ # @see #fetch
101
+ def members
102
+ fetch if @members.size != @member_count
103
+ @members
104
+ end
105
+
106
+ private
107
+
108
+ # Fetches a specific page of the member listing of this group
109
+ #
110
+ # @param [Fixnum] page The member page to fetch
111
+ # @return [Fixnum] The total number of pages of this group's member listing
112
+ def fetch_page(page)
113
+ member_data = parse "#{base_url}/memberslistxml?p=#{page}"
114
+
115
+ @group_id64 = member_data['groupID64'].to_i if page == 1
116
+ @member_count = member_data['memberCount'].to_i
117
+ total_pages = member_data['totalPages'].to_i
118
+
119
+ member_data['members']['steamID64'].each do |member|
120
+ @members << SteamId.new(member.to_i, false)
121
+ end
122
+
123
+ total_pages
124
+ rescue
125
+ raise $! if $!.is_a? SteamCondenserError
126
+ raise SteamCondenserError, 'XML data could not be parsed.'
127
+ end
128
+
129
+ end
@@ -0,0 +1,498 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2013, Sebastian Staudt
5
+
6
+ require 'cgi'
7
+
8
+ require 'errors/steam_condenser_error'
9
+ require 'steam/community/cacheable'
10
+ require 'steam/community/game_stats'
11
+ require 'steam/community/steam_game'
12
+ require 'steam/community/steam_group'
13
+ require 'steam/community/xml_data'
14
+
15
+ # The SteamId class represents a Steam Community profile (also called Steam ID)
16
+ #
17
+ # @author Sebastian Staudt
18
+ class SteamId
19
+
20
+ include Cacheable
21
+ cacheable_with_ids :custom_url, :steam_id64
22
+
23
+ include XMLData
24
+
25
+ # Returns the custom URL of this Steam ID
26
+ #
27
+ # The custom URL is a user specified unique string that can be used instead
28
+ # of the 64bit SteamID as an identifier for a Steam ID.
29
+ #
30
+ # @note The custom URL is not necessarily the same as the user's nickname.
31
+ # @return [String] The custom URL of this Steam ID
32
+ attr_reader :custom_url
33
+
34
+ # Returns the groups this user is a member of
35
+ #
36
+ # @return [Array<SteamGroup>] The groups this user is a member of
37
+ attr_reader :groups
38
+
39
+ # Returns the headline specified by the user
40
+ #
41
+ # @return [String] The headline specified by the user
42
+ attr_reader :head_line
43
+
44
+ # Returns the number of hours that this user played a game in the last two
45
+ # weeks
46
+ #
47
+ # @return [Float] The number of hours the user has played recently
48
+ attr_reader :hours_played
49
+
50
+ # Returns the links that this user has added to his/her Steam ID
51
+ #
52
+ # The keys of the hash contain the titles of the links while the values
53
+ # contain the corresponding URLs.
54
+ #
55
+ # @return [Hash<String, String>] The links of this user
56
+ attr_reader :links
57
+
58
+ # Returns the location of the user
59
+ #
60
+ # @return [String] The location of the user
61
+ attr_reader :location
62
+
63
+ # Returns the date of registration for the Steam account belonging to this
64
+ # SteamID
65
+ #
66
+ # @return [Time] The date of the Steam account registration
67
+ attr_reader :member_since
68
+
69
+ # Returns the games this user has played the most in the last two weeks
70
+ #
71
+ # The keys of the hash contain the names of the games while the values
72
+ # contain the number of hours the corresponding game has been played by the
73
+ # user in the last two weeks.
74
+ #
75
+ # @return [Hash<String, Float>] The games this user has played the most
76
+ # recently
77
+ attr_reader :most_played_games
78
+
79
+ # Returns the Steam nickname of the user
80
+ #
81
+ # @return [String] The Steam nickname of the user
82
+ attr_reader :nickname
83
+
84
+ # Returns the privacy state of this Steam ID
85
+ #
86
+ # @return [String] The privacy state of this Steam ID
87
+ attr_reader :privacy_state
88
+
89
+ # Returns the real name of this user
90
+ #
91
+ # @return [String] The real name of this user
92
+ attr_reader :real_name
93
+
94
+ # Returns the message corresponding to this user's online state
95
+ #
96
+ # @return [String] The message corresponding to this user's online state
97
+ # @see #ingame?
98
+ # @see #online?
99
+ attr_reader :state_message
100
+
101
+ # Returns this user's 64bit SteamID
102
+ #
103
+ # @return [Fixnum] This user's 64bit SteamID
104
+ attr_reader :steam_id64
105
+
106
+ # Returns the Steam rating calculated over the last two weeks' activity
107
+ #
108
+ # @return [Float] The Steam rating of this user
109
+ attr_reader :steam_rating
110
+
111
+ # Returns the summary this user has provided
112
+ #
113
+ # @return [String] This user's summary
114
+ attr_reader :summary
115
+
116
+ # Returns this user's ban state in Steam's trading system
117
+ #
118
+ # @return [String] This user's trading ban state
119
+ attr_reader :trade_ban_state
120
+
121
+ # Returns the visibility state of this Steam ID
122
+ #
123
+ # @return [Fixnum] This Steam ID's visibility State
124
+ attr_reader :visibility_state
125
+
126
+ # Converts a 64bit numeric SteamID as used by the Steam Community to a
127
+ # SteamID as reported by game servers
128
+ #
129
+ # @param [Fixnum] community_id The SteamID string as used by the Steam
130
+ # Community
131
+ # @raise [SteamCondenserError] if the community ID is to small
132
+ # @return [String] The converted SteamID, like `STEAM_0:0:12345`
133
+ def self.community_id_to_steam_id(community_id)
134
+ steam_id1 = community_id % 2
135
+ steam_id2 = community_id - 76561197960265728
136
+
137
+ unless steam_id2 > 0
138
+ raise SteamCondenserError, "SteamID #{community_id} is too small."
139
+ end
140
+
141
+ steam_id2 = (steam_id2 - steam_id1) / 2
142
+
143
+ "STEAM_0:#{steam_id1}:#{steam_id2}"
144
+ end
145
+
146
+ # Resolves a vanity URL of a Steam Community profile to a 64bit numeric
147
+ # SteamID
148
+ #
149
+ # @param [String] vanity_url The vanity URL of a Steam Community profile
150
+ # @return [Fixnum] The 64bit numeric SteamID
151
+ def self.resolve_vanity_url(vanity_url)
152
+ params = { :vanityurl => vanity_url }
153
+ json = WebApi.json 'ISteamUser', 'ResolveVanityURL', 1, params
154
+ result = MultiJson.load(json, :symbolize_keys => true)[:response]
155
+
156
+ return nil if result[:success] != 1
157
+
158
+ result[:steamid].to_i
159
+ end
160
+
161
+ # Converts a SteamID as reported by game servers to a 64bit numeric SteamID
162
+ # as used by the Steam Community
163
+ #
164
+ # @param [String] steam_id The SteamID string as used on servers, like
165
+ # `STEAM_0:0:12345`
166
+ # @raise [SteamCondenserError] if the SteamID doesn't have the correct
167
+ # format
168
+ # @return [Fixnum] The converted 64bit numeric SteamID
169
+ def self.steam_id_to_community_id(steam_id)
170
+ if steam_id == 'STEAM_ID_LAN' || steam_id == 'BOT'
171
+ raise SteamCondenserError, "Cannot convert SteamID \"#{steam_id}\" to a community ID."
172
+ elsif steam_id =~ /^STEAM_[0-1]:[0-1]:[0-9]+$/
173
+ steam_id = steam_id[8..-1].split(':').map! { |s| s.to_i }
174
+ steam_id[0] + steam_id[1] * 2 + 76561197960265728
175
+ elsif steam_id =~ /\[U:[0-1]:[0-9]+\]/
176
+ steam_id = steam_id[3..-2].split(':').map! { |s| s.to_i }
177
+ steam_id[0] + steam_id[1] + 76561197960265727
178
+ else
179
+ raise SteamCondenserError, "SteamID \"#{steam_id}\" doesn't have the correct format."
180
+ end
181
+ end
182
+
183
+ class << self
184
+ alias_method :convert_community_id_to_steam_id, :community_id_to_steam_id
185
+ alias_method :convert_steam_id_to_community_id, :steam_id_to_community_id
186
+ end
187
+
188
+ # Creates a new `SteamId` instance using a SteamID as used on servers
189
+ #
190
+ # The SteamID from the server is converted into a 64bit numeric SteamID first
191
+ # before this is used to retrieve the corresponding Steam Community profile.
192
+ #
193
+ # @param [String] steam_id The SteamID string as used on servers, like
194
+ # `STEAM_0:0:12345`
195
+ # @return [SteamId] The `SteamId` belonging to the given SteamID
196
+ # @see .convert_steam_id_to_community_id
197
+ # @see #initialize
198
+ def self.from_steam_id(steam_id)
199
+ new(convert_steam_id_to_community_id(steam_id))
200
+ end
201
+
202
+ # Creates a new `SteamId` instance for the given Steam ID
203
+ #
204
+ # @param [String, Fixnum] id The custom URL of the Steam ID specified by the
205
+ # user or the 64bit SteamID
206
+ # @macro cacheable
207
+ def initialize(id)
208
+ if id.is_a? Numeric
209
+ @steam_id64 = id
210
+ else
211
+ if id =~ /^STEAM_[0-1]:[0-1]:[0-9]+$/ || id =~ /\[U:[0-1]:[0-9]+\]/
212
+ @steam_id64 = SteamId.steam_id_to_community_id id
213
+ else
214
+ @custom_url = id.downcase
215
+ end
216
+ end
217
+ end
218
+
219
+ # Returns whether the owner of this SteamID is VAC banned
220
+ #
221
+ # @return [Boolean] `true` if the user has been banned by VAC
222
+ def banned?
223
+ @vac_banned
224
+ end
225
+ alias_method :is_banned?, :banned?
226
+
227
+ # Returns the base URL for this Steam ID
228
+ #
229
+ # This URL is different for Steam IDs having a custom URL.
230
+ #
231
+ # @return [String] The base URL for this SteamID
232
+ def base_url
233
+ if @custom_url.nil?
234
+ "http://steamcommunity.com/profiles/#@steam_id64"
235
+ else
236
+ "http://steamcommunity.com/id/#@custom_url"
237
+ end
238
+ end
239
+
240
+ # Fetchs data from the Steam Community by querying the XML version of the
241
+ # profile specified by the ID of this Steam ID
242
+ #
243
+ # @raise SteamCondenserError if the Steam ID data is not available, e.g.
244
+ # when it is private
245
+ # @see Cacheable#fetch
246
+ def fetch
247
+ profile = parse "#{base_url}?xml=1"
248
+
249
+ raise SteamCondenserError, profile['error'] unless profile['error'].nil?
250
+
251
+ unless profile['privacyMessage'].nil?
252
+ raise SteamCondenserError, profile['privacyMessage']
253
+ end
254
+
255
+ @nickname = CGI.unescapeHTML profile['steamID']
256
+ @steam_id64 = profile['steamID64'].to_i
257
+ @limited = (profile['isLimitedAccount'].to_i == 1)
258
+ @trade_ban_state = profile['tradeBanState']
259
+ @vac_banned = (profile['vacBanned'].to_i == 1)
260
+
261
+ @image_url = profile['avatarIcon'][0..-5]
262
+ @online_state = profile['onlineState']
263
+ @privacy_state = profile['privacyState']
264
+ @state_message = profile['stateMessage']
265
+ @visibility_state = profile['visibilityState'].to_i
266
+
267
+ if public?
268
+ @custom_url = (profile['customURL'] || '').downcase
269
+ @custom_url = nil if @custom_url.empty?
270
+
271
+ @head_line = CGI.unescapeHTML profile['headline'] || ''
272
+ @hours_played = profile['hoursPlayed2Wk'].to_f
273
+ @location = profile['location']
274
+ @member_since = Time.parse profile['memberSince']
275
+ @real_name = CGI.unescapeHTML profile['realname'] || ''
276
+ @steam_rating = profile['steamRating'].to_f
277
+ @summary = CGI.unescapeHTML profile['summary'] || ''
278
+
279
+ @most_played_games = {}
280
+ [(profile['mostPlayedGames'] || {})['mostPlayedGame']].compact.flatten.each do |most_played_game|
281
+ @most_played_games[most_played_game['gameName']] = most_played_game['hoursPlayed'].to_f
282
+ end
283
+
284
+ @groups = []
285
+ [(profile['groups'] || {})['group']].compact.flatten.each do |group|
286
+ @groups << SteamGroup.new(group['groupID64'].to_i, false)
287
+ end
288
+
289
+ @links = {}
290
+ [(profile['weblinks'] || {})['weblink']].compact.flatten.each do |link|
291
+ @links[CGI.unescapeHTML link['title']] = link['link']
292
+ end
293
+ end
294
+ rescue
295
+ raise $! if $!.is_a? SteamCondenserError
296
+ raise SteamCondenserError, 'XML data could not be parsed.'
297
+ end
298
+
299
+ # Fetches the friends of this user
300
+ #
301
+ # This creates a new `SteamId` instance for each of the friends without
302
+ # fetching their data.
303
+ #
304
+ # @see #friends
305
+ # @see #initialize
306
+ def fetch_friends
307
+ friends_data = parse "#{base_url}/friends?xml=1"
308
+ @friends = []
309
+ friends_data['friends']['friend'].each do |friend|
310
+ @friends << SteamId.new(friend.to_i, false)
311
+ end
312
+ end
313
+
314
+ # Fetches the games this user owns
315
+ #
316
+ # This fills the game hash with the names of the games as keys. The values
317
+ # will either be `false` if the game does not have stats or the game's
318
+ # "friendly name".
319
+ #
320
+ # @see #games
321
+ def fetch_games
322
+ games_data = parse "#{base_url}/games?xml=1"
323
+ @games = {}
324
+ @recent_playtimes = {}
325
+ @total_playtimes = {}
326
+ games_data['games']['game'].each do |game_data|
327
+ app_id = game_data['appID'].to_i
328
+ @games[app_id] = SteamGame.new app_id, game_data
329
+
330
+ recent = game_data['hoursLast2Weeks'].to_f
331
+ total = (game_data['hoursOnRecord'] || '').delete(',').to_f
332
+
333
+ @recent_playtimes[app_id] = (recent * 60).to_i
334
+ @total_playtimes[app_id] = (total * 60).to_i
335
+ end
336
+
337
+ true
338
+ end
339
+
340
+ # Returns the URL of the full-sized version of this user's avatar
341
+ #
342
+ # @return [String] The URL of the full-sized avatar
343
+ def full_avatar_url
344
+ "#{@image_url}_full.jpg"
345
+ end
346
+
347
+ # Returns the stats for the given game for the owner of this SteamID
348
+ #
349
+ # @param [Fixnum, String] id The full or short name or the application ID of
350
+ # the game stats should be fetched for
351
+ # @return [GameStats] The statistics for the game with the given name
352
+ # @raise [SteamCondenserError] if the user does not own this game or it
353
+ # does not have any stats
354
+ # @see find_game
355
+ # @see SteamGame#has_stats?
356
+ def game_stats(id)
357
+ game = find_game id
358
+
359
+ unless game.has_stats?
360
+ raise SteamCondenserError, "\"#{game.name}\" does not have stats."
361
+ end
362
+
363
+ GameStats.create_game_stats(@custom_url || @steam_id64, game.short_name)
364
+ end
365
+
366
+ # Returns the Steam Community friends of this user
367
+ #
368
+ # If the friends haven't been fetched yet, this is done now.
369
+ #
370
+ # @return [Array<SteamId>] The friends of this user
371
+ # @see #fetch_friends
372
+ def friends
373
+ fetch_friends if @friends.nil?
374
+ @friends
375
+ end
376
+
377
+ # Returns the games this user owns
378
+ #
379
+ # The keys of the hash are the games' application IDs and the values are
380
+ # the corresponding game instances.
381
+ #
382
+ # If the friends haven't been fetched yet, this is done now.
383
+ #
384
+ # @return [Hash<Fixnum, SteamGame>] The games this user owns
385
+ # @see #fetch_games
386
+ def games
387
+ fetch_games if @games.nil?
388
+ @games
389
+ end
390
+
391
+ # Returns the URL of the icon version of this user's avatar
392
+ #
393
+ # @return [String] The URL of the icon-sized avatar
394
+ def icon_url
395
+ "#@image_url.jpg"
396
+ end
397
+
398
+ # Returns a unique identifier for this Steam ID
399
+ #
400
+ # This is either the 64bit numeric SteamID or custom URL
401
+ #
402
+ # @return [Fixnum, String] The 64bit numeric SteamID or the custom URL
403
+ def id
404
+ @custom_url || @steam_id64
405
+ end
406
+
407
+ # Returns whether the owner of this SteamId is playing a game
408
+ #
409
+ # @return [Boolean] `true` if the user is in-game
410
+ def in_game?
411
+ @online_state == 'in-game'
412
+ end
413
+ alias_method :is_in_game?, :in_game?
414
+
415
+ # Returns whether this Steam account is limited
416
+ #
417
+ # @return [Boolean] `true` if this account is limited
418
+ def limited?
419
+ @limited
420
+ end
421
+
422
+ # Returns the URL of the medium-sized version of this user's avatar
423
+ #
424
+ # @return [String] The URL of the medium-sized avatar
425
+ def medium_avatar_url
426
+ "#{@image_url}_medium.jpg"
427
+ end
428
+
429
+ # Returns whether the owner of this SteamID is currently logged into Steam
430
+ #
431
+ # @return [Boolean] `true` if the user is online
432
+ def online?
433
+ @online_state != 'offline'
434
+ end
435
+ alias_method :is_online?, :online?
436
+
437
+ # Returns whether this Steam ID is publicly accessible
438
+ #
439
+ # @return [Boolean] `true` if this Steam ID is public
440
+ def public?
441
+ @privacy_state == 'public'
442
+ end
443
+
444
+ # Returns the time in minutes this user has played this game in the last two
445
+ # weeks
446
+ #
447
+ # @param [Fixnum, String] id The full or short name or the application ID of
448
+ # the game
449
+ # @return [Fixnum] The number of minutes this user played the given game in
450
+ # the last two weeks
451
+ def recent_playtime(id)
452
+ game = find_game id
453
+ @recent_playtimes[game.app_id]
454
+ end
455
+
456
+ # Returns the total time in minutes this user has played this game
457
+ #
458
+ # @param [Fixnum, String] id The full or short name or the application ID of
459
+ # the game
460
+ # @return [Fixnum] The total number of minutes this user played the given
461
+ # game
462
+ def total_playtime(id)
463
+ game = find_game id
464
+ @total_playtimes[game.app_id]
465
+ end
466
+
467
+ private
468
+
469
+ # Tries to find a game instance with the given application ID or full name or
470
+ # short name
471
+ #
472
+ # @param [Fixnum, String] id The full or short name or the application ID of
473
+ # the game
474
+ # @raise [SteamCondenserError] if the user does not own the game or no
475
+ # game with the given ID exists
476
+ # @return [SteamGame] The game found with the given ID
477
+ def find_game(id)
478
+ if id.is_a? Numeric
479
+ game = games[id]
480
+ else
481
+ game = games.values.find do |game|
482
+ game.short_name == id || game.name == id
483
+ end
484
+ end
485
+
486
+ if game.nil?
487
+ if id.is_a? Numeric
488
+ message = "This SteamID does not own a game with application ID #{id}."
489
+ else
490
+ message = "This SteamID does not own the game \"#{id}\"."
491
+ end
492
+ raise SteamCondenserError, message
493
+ end
494
+
495
+ game
496
+ end
497
+
498
+ end