reve 0.0.74

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 (100) hide show
  1. data/ChangeLog +155 -0
  2. data/Rakefile +43 -0
  3. data/init.rb +1 -0
  4. data/lib/reve.rb +793 -0
  5. data/reve.rb +1 -0
  6. data/test/test_reve.rb +700 -0
  7. data/test/xml/alliances.xml +171 -0
  8. data/test/xml/assets.xml +32 -0
  9. data/test/xml/badxml.xml +6 -0
  10. data/test/xml/character_sheet.xml +37 -0
  11. data/test/xml/characterid.xml +11 -0
  12. data/test/xml/charactername.xml +11 -0
  13. data/test/xml/characters.xml +13 -0
  14. data/test/xml/conqurable_stations.xml +11 -0
  15. data/test/xml/corporation_sheet.xml +52 -0
  16. data/test/xml/errors/error_100.xml +5 -0
  17. data/test/xml/errors/error_101.xml +5 -0
  18. data/test/xml/errors/error_102.xml +5 -0
  19. data/test/xml/errors/error_103.xml +5 -0
  20. data/test/xml/errors/error_104.xml +5 -0
  21. data/test/xml/errors/error_105.xml +5 -0
  22. data/test/xml/errors/error_106.xml +5 -0
  23. data/test/xml/errors/error_107.xml +5 -0
  24. data/test/xml/errors/error_108.xml +5 -0
  25. data/test/xml/errors/error_109.xml +5 -0
  26. data/test/xml/errors/error_110.xml +5 -0
  27. data/test/xml/errors/error_111.xml +5 -0
  28. data/test/xml/errors/error_112.xml +5 -0
  29. data/test/xml/errors/error_113.xml +5 -0
  30. data/test/xml/errors/error_114.xml +5 -0
  31. data/test/xml/errors/error_115.xml +5 -0
  32. data/test/xml/errors/error_116.xml +5 -0
  33. data/test/xml/errors/error_117.xml +5 -0
  34. data/test/xml/errors/error_118.xml +5 -0
  35. data/test/xml/errors/error_119.xml +5 -0
  36. data/test/xml/errors/error_120.xml +5 -0
  37. data/test/xml/errors/error_121.xml +5 -0
  38. data/test/xml/errors/error_200.xml +5 -0
  39. data/test/xml/errors/error_201.xml +5 -0
  40. data/test/xml/errors/error_202.xml +5 -0
  41. data/test/xml/errors/error_203.xml +5 -0
  42. data/test/xml/errors/error_204.xml +5 -0
  43. data/test/xml/errors/error_205.xml +5 -0
  44. data/test/xml/errors/error_206.xml +5 -0
  45. data/test/xml/errors/error_207.xml +5 -0
  46. data/test/xml/errors/error_208.xml +5 -0
  47. data/test/xml/errors/error_209.xml +5 -0
  48. data/test/xml/errors/error_210.xml +5 -0
  49. data/test/xml/errors/error_211.xml +5 -0
  50. data/test/xml/errors/error_212.xml +5 -0
  51. data/test/xml/errors/error_213.xml +5 -0
  52. data/test/xml/errors/error_214.xml +5 -0
  53. data/test/xml/errors/error_500.xml +5 -0
  54. data/test/xml/errors/error_501.xml +5 -0
  55. data/test/xml/errors/error_502.xml +5 -0
  56. data/test/xml/errors/error_503.xml +5 -0
  57. data/test/xml/errors/error_504.xml +5 -0
  58. data/test/xml/errors/error_505.xml +5 -0
  59. data/test/xml/errors/error_506.xml +5 -0
  60. data/test/xml/errors/error_507.xml +5 -0
  61. data/test/xml/errors/error_508.xml +5 -0
  62. data/test/xml/errors/error_509.xml +5 -0
  63. data/test/xml/errors/error_510.xml +5 -0
  64. data/test/xml/errors/error_511.xml +5 -0
  65. data/test/xml/errors/error_512.xml +5 -0
  66. data/test/xml/errors/error_513.xml +5 -0
  67. data/test/xml/errors/error_514.xml +5 -0
  68. data/test/xml/errors/error_515.xml +5 -0
  69. data/test/xml/errors/error_516.xml +5 -0
  70. data/test/xml/errors/error_517.xml +5 -0
  71. data/test/xml/errors/error_518.xml +5 -0
  72. data/test/xml/errors/error_519.xml +5 -0
  73. data/test/xml/errors/error_520.xml +5 -0
  74. data/test/xml/errors/error_521.xml +5 -0
  75. data/test/xml/errors/error_522.xml +5 -0
  76. data/test/xml/errors/error_523.xml +5 -0
  77. data/test/xml/errors/error_900.xml +5 -0
  78. data/test/xml/errors/error_901.xml +5 -0
  79. data/test/xml/errors/error_902.xml +5 -0
  80. data/test/xml/errors/error_903.xml +5 -0
  81. data/test/xml/errors/error_999.xml +5 -0
  82. data/test/xml/industryjobs.xml +11 -0
  83. data/test/xml/kills.xml +163 -0
  84. data/test/xml/mapjumps.xml +15 -0
  85. data/test/xml/mapkills.xml +16 -0
  86. data/test/xml/market_transactions.xml +79 -0
  87. data/test/xml/marketorders.xml +37 -0
  88. data/test/xml/member_tracking.xml +22 -0
  89. data/test/xml/nonmember_corpsheet.xml +30 -0
  90. data/test/xml/reftypes.xml +14 -0
  91. data/test/xml/skill_in_training-amarr-titan.xml +13 -0
  92. data/test/xml/skill_in_training-none.xml +7 -0
  93. data/test/xml/skilltree.xml +40 -0
  94. data/test/xml/sovereignty.xml +29 -0
  95. data/test/xml/starbase_fuel.xml +23 -0
  96. data/test/xml/starbases.xml +12 -0
  97. data/test/xml/wallet_balance.xml +17 -0
  98. data/test/xml/wallet_journal.xml +48 -0
  99. data/tester.rb +17 -0
  100. metadata +151 -0
data/lib/reve.rb ADDED
@@ -0,0 +1,793 @@
1
+ #--
2
+ # Code copyright Lisa Seelye, 2007-2008. www.crudvision.com
3
+ # Reve is not licensed for commercial use. For other uses there are no
4
+ # restrictions.
5
+ #
6
+ # The author is not adverse to tokens of appreciation in the form of Eve ISK,
7
+ # ships, and feedback. Please use
8
+ # http://www.crudvision.com/reve-ruby-eve-online-api-library/ to provide
9
+ # feedback or send ISK to Raquel Smith in Eve. :-)
10
+ #++
11
+
12
+ begin
13
+ require 'hpricot'
14
+ rescue LoadError
15
+ require 'rubygems'
16
+ require 'hpricot'
17
+ end
18
+ require 'net/http'
19
+ require 'uri'
20
+ require 'cgi'
21
+ require 'fileutils'
22
+
23
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
24
+
25
+ require 'lib/reve/exceptions'
26
+ require 'lib/reve/extensions'
27
+ require 'lib/reve/classes'
28
+
29
+
30
+ module Reve
31
+ # API Class.
32
+ # Basic Usage:
33
+ # api = Reve::API.new('my_UserID', 'my_apiKey')
34
+ # alliances = api.alliances # Returns an array of Reve::Classes::Alliance
35
+ #
36
+ # api.personal_wallet_blanace(:characterid => 892008733) # Returns an array of
37
+ # Reve::Classes::WalletBalance. Note that the CharacterID Number is required
38
+ # here.
39
+ #
40
+ # api.sovereignty :just_hash => true # Returns the hash for this call with no
41
+ # Alliance data with it.
42
+ #
43
+ # As of Revision 22 (28 August 2007) all API calls take a parameter,
44
+ # :just_hash, to just get the hash that represents that particular API call;
45
+ # No data related to the call is returned if :just_hash is present
46
+ #
47
+ # All API methods have the functionality to read XML from an arbitrary location. This could be another webserver, or a XML file on disk.
48
+ # To use this pass the hash option :url => +location+ where +location+ is a String or URI class. See format_url_request documentation for more details.
49
+ class API
50
+
51
+ @@alliances_url = 'http://api.eve-online.com/eve/AllianceList.xml.aspx'
52
+ @@sovereignty_url = 'http://api.eve-online.com/map/Sovereignty.xml.aspx'
53
+ @@reftypes_url = 'http://api.eve-online.com/eve/RefTypes.xml.aspx'
54
+ @@skill_tree_url = 'http://api.eve-online.com/eve/SkillTree.xml.aspx'
55
+ @@member_tracking_url = 'http://api.eve-online.com/corp/MemberTracking.xml.aspx'
56
+ @@corporate_wallet_balance_url = 'http://api.eve-online.com/corp/AccountBalance.xml.aspx'
57
+ @@personal_wallet_balance_url = 'http://api.eve-online.com/char/AccountBalance.xml.aspx'
58
+ @@corporate_wallet_trans_url = 'http://api.eve-online.com/corp/WalletTransactions.xml.aspx'
59
+ @@personal_wallet_trans_url = 'http://api.eve-online.com/char/WalletTransactions.xml.aspx'
60
+ @@corporate_wallet_journal_url = 'http://api.eve-online.com/corp/WalletJournal.xml.aspx'
61
+ @@personal_wallet_journal_url = 'http://api.eve-online.com/char/WalletJournal.xml.aspx'
62
+ @@characters_url = 'http://api.eve-online.com/account/Characters.xml.aspx'
63
+ @@training_skill_url = 'http://api.eve-online.com/char/SkillInTraining.xml.aspx'
64
+ @@character_sheet_url = 'http://api.eve-online.com/char/CharacterSheet.xml.aspx'
65
+ @@starbases_url = 'http://api.eve-online.com/corp/StarbaseList.xml.aspx'
66
+ @@starbasedetail_url = 'http://api.eve-online.com/corp/StarbaseDetail.xml.aspx'
67
+ @@conqurable_outposts_url = 'http://api.eve-online.com/eve/ConquerableStationList.xml.aspx'
68
+ @@corporation_sheet_url = 'http://api.eve-online.com/corp/CorporationSheet.xml.aspx'
69
+ @@errors_url = 'http://api.eve-online.com/eve/ErrorList.xml.aspx'
70
+ @@map_jumps_url = 'http://api.eve-online.com/map/Jumps.xml.aspx'
71
+ @@map_kills_url = 'http://api.eve-online.com/map/Kills.xml.aspx'
72
+ @@personal_market_orders_url = 'http://api.eve-online.com/char/MarketOrders.xml.aspx'
73
+ @@corporate_market_orders_url = 'http://api.eve-online.com/corp/MarketOrders.xml.aspx'
74
+ @@personal_industry_jobs_url = 'http://api.eve-online.com/char/IndustryJobs.xml.aspx'
75
+ @@corporate_industry_jobs_url = 'http://api.eve-online.com/corp/IndustryJobs.xml.aspx'
76
+ @@personal_assets_url = 'http://api.eve-online.com/char/AssetList.xml.aspx'
77
+ @@corporate_assets_url = 'http://api.eve-online.com/corp/AssetList.xml.aspx'
78
+ @@personal_kills_url = 'http://api.eve-online.com/char/KillLog.xml.aspx'
79
+ @@corporate_kills_url = 'http://api.eve-online.com/corp/KillLog.xml.aspx'
80
+ @@character_id_url = 'http://api.eve-online.com/eve/CharacterID.xml.aspx' # ?names=CCP%20Garthagk,Raquel%20Smith
81
+ @@character_name_url = 'http://api.eve-online.com/eve/CharacterName.xml.aspx' # ?ids=797400947,892008733
82
+
83
+ cattr_accessor :character_sheet_url, :training_skill_url, :characters_url, :personal_wallet_journal_url,
84
+ :corporate_wallet_journal_url, :personal_wallet_trans_url, :corporate_wallet_trans_url,
85
+ :personal_wallet_balance_url, :corporate_wallet_balance_url, :member_tracking_url,
86
+ :skill_tree_url, :reftypes_url, :sovereignty_url, :alliances_url, :starbases_url,
87
+ :starbasedetail_url, :conqurable_outposts_url, :corporation_sheet_url, :map_jumps_url,
88
+ :map_kills_url, :personal_market_orders_url, :corporate_market_orders_url,
89
+ :personal_industry_jobs_url, :corporate_industry_jobs_url, :personal_assets_url,
90
+ :corporate_assets_url, :personal_kills_url, :corporate_kills_url
91
+
92
+
93
+
94
+ attr_accessor :key, :userid, :charid
95
+ attr_accessor :http_user_agent, :save_path
96
+ attr_reader :current_time, :cached_until, :last_hash
97
+
98
+ # Create a new API instance.
99
+ # current_time and cached_until are meaningful only for the LAST call made.
100
+ # Expects:
101
+ # * userid ( Integer | String ) - Your API userID
102
+ # * key ( String ) - Your API key (Full key or restricted key)
103
+ # * charid ( Integer | String ) - Default characterID for calls requiring it.
104
+ # NOTE: All values passed to the constructor are typecasted to a String for safety.
105
+ def initialize(userid = "", key = "", charid = "")
106
+ @userid = (userid || "").to_s
107
+ @key = (key || "").to_s
108
+ @charid = (charid || "").to_s
109
+
110
+ @save_path = nil
111
+
112
+ @http_user_agent = "Reve"
113
+ @max_tries = 3
114
+
115
+ @current_time = nil
116
+ @cached_until = nil
117
+ @last_hash = nil
118
+ end
119
+ # Save XML to this directory with the format:
120
+ # :save_path/:userid/:method/:expires_at_in_unixtime.xml
121
+ # eg: ./xml/12345/characters/1200228878.xml
122
+ # or: ./xml/alliances/1200228878.xml
123
+ # If @save_path is nil then XML is not saved.
124
+ def save_path=(p)
125
+ @save_path = p
126
+ end
127
+
128
+ # Convert Character names to Character ids.
129
+ # Expects a Hash as a parameter with these keys:
130
+ # * names ( Array ) - An Array of Character names to fetch the IDs of.
131
+ # See Also: character_name, Reve::Classes::Character, character_sheet
132
+ #--
133
+ # This is an odd method because the XML from this looks like:
134
+ # <rowset ...>
135
+ # <row:name name="CCP Garthagk" characterID="797400947" xmlns:row="characterID" />
136
+ # </rowset>
137
+ # I have to hack the Hpricot::Doc document coming from process_query to
138
+ # gsub out the row:name -> row to make the XPath work. Hope it doesn't
139
+ # perform too slowly! (We don't, of course, care about the xmlns:row bit)
140
+ #++
141
+ def character_id(opts = {} )
142
+ names = opts[:names] || []
143
+ return [] if names.empty? # No names were passed.
144
+ opts[:names] = names.join(',')
145
+ args = postfields(opts)
146
+ h = compute_hash( opts.merge(:url => @@character_id_url) )
147
+ return h if h
148
+ xml = process_query(nil,opts[:url] || @@character_id_url, true,opts)
149
+ xml = Hpricot(xml.to_s.gsub('row:name','row')) # Namespaces are evil!!
150
+ ret = []
151
+ xml.search("//rowset/row").each do |elem|
152
+ ret << Reve::Classes::Character.new(elem)
153
+ end
154
+ ret
155
+ end
156
+
157
+ # Convert Character names to Character names.
158
+ # Expects a Hash as a parameter with these keys:
159
+ # * ids ( Array ) - An Array of Character IDs to fetch the names of.
160
+ # See Also: character_name, Reve::Classes::Character, character_sheet
161
+ def character_name(opts = {})
162
+ ids = opts[:ids] || []
163
+ return [] if ids.empty?
164
+ opts[:ids] = ids.join(',')
165
+ compute_hash( opts.merge(:url => @@character_name_url) ) ||
166
+ process_query(Reve::Classes::Character,opts[:url] || @@character_name_url,false,opts)
167
+ end
168
+
169
+ # Return a list of Alliances and member Corporations from
170
+ # http://api.eve-online.com/eve/AllianceList.xml.aspx
171
+ # Use the corporation_sheet method to get information for each member
172
+ # Corporation
173
+ # See also: Reve::Classes::Alliance, Reve::Classes::Corporation and
174
+ # corporation_sheet
175
+ def alliances(opts = {})
176
+ args = postfields(opts)
177
+ h = compute_hash(args.merge(:url => @@alliances_url))
178
+ return h if h
179
+ xml = process_query(nil,opts[:url] || @@alliances_url,true,args)
180
+ alliances = []
181
+ xml.search("/eveapi/result/rowset[@name='alliances']/row").each do |alliance|
182
+ alliance_obj = Reve::Classes::Alliance.new(alliance)
183
+ alliance.search("rowset[@name='memberCorporations']/row").each do |corporation|
184
+ alliance_obj.member_corporations << Reve::Classes::Corporation.new(corporation)
185
+ end
186
+ alliances << alliance_obj
187
+ end
188
+ alliances
189
+ end
190
+
191
+ # Returns a list of the number of jumps for each system. If there are no
192
+ # jumps for a system it will not be included. See also Reve::Classes::MapJump
193
+ def map_jumps(opts = {})
194
+ compute_hash( opts.merge(:url => @@map_jumps_url) ) ||
195
+ process_query(Reve::Classes::MapJump,opts[:url] || @@map_jumps_url,false)
196
+ end
197
+
198
+ # Returns a list of the number of kills for each system. If there are no
199
+ # kills for a system it will not be included. See also Reve::Classes::MapKill
200
+ def map_kills(opts = {})
201
+ compute_hash( opts.merge(:url => @@map_kills_url) ) ||
202
+ process_query(Reve::Classes::MapKill,opts[:url] || @@map_kills_url,false)
203
+ end
204
+
205
+ # Returns a list of API Errors
206
+ def errors(opts = {})
207
+ compute_hash( opts.merge(:url => @@errors_url) ) ||
208
+ process_query(Reve::Classes::APIError,opts[:url] || @@errors_url,false)
209
+ end
210
+
211
+ # Returns the Sovereignty list from
212
+ # http://api.eve-online.com/map/Sovereignty.xml.aspx
213
+ # See also: Reve::Classes::Sovereignty
214
+ def sovereignty(opts = {})
215
+ compute_hash( opts.merge(:url => @@sovereignty_url) ) ||
216
+ process_query(Reve::Classes::Sovereignty,opts[:url] || @@sovereignty_url,false)
217
+ end
218
+
219
+ # Returns a RefType list (whatever they are) from
220
+ # http://api.eve-online.com/eve/RefTypes.xml.aspx
221
+ # See also: Reve::Classes::RefType
222
+ def ref_types(opts = {})
223
+ compute_hash( opts.merge(:url => @@reftypes_url) ) ||
224
+ process_query(Reve::Classes::RefType,opts[:url] || @@reftypes_url,false)
225
+ end
226
+
227
+ # Returns a list of ConqurableStations and outposts from
228
+ # http://api.eve-online.com/eve/ConquerableStationList.xml.aspx
229
+ # See also: Reve::Classes::ConqurableStation
230
+ def conqurable_stations(opts = {})
231
+ compute_hash( opts.merge(:url => @@conqurable_outposts_url) ) ||
232
+ process_query(Reve::Classes::ConqurableStation, opts[:url] || @@conqurable_outposts_url, false)
233
+ end
234
+
235
+ # Returns a list of Reve::Classes::MarketOrder objects for market orders that are up
236
+ # Pass the characterid of the Character to check for
237
+ def personal_market_orders(opts = {:characterid => nil})
238
+ args = postfields(opts)
239
+ h = compute_hash(args.merge(:url => @@personal_market_orders_url))
240
+ return h if h
241
+ process_query(Reve::Classes::PersonalMarketOrder, opts[:url] || @@personal_market_orders_url, false, args)
242
+ end
243
+
244
+ # Returns a list of Reve::Classes::MarketOrder objects for market orders that are up on behalf of a Corporation
245
+ # Pass the characterid of the Character of whose corporation to check for
246
+ def corporate_market_orders(opts = {:characterid => nil})
247
+ args = postfields(opts)
248
+ h = compute_hash(args.merge(:url => @@corporate_market_orders_url))
249
+ return h if h
250
+ process_query(Reve::Classes::PersonalMarketOrder, opts[:url] || @@corporate_market_orders_url, false, args)
251
+ end
252
+
253
+ # Returns a list of Reve::Classes::PersonalIndustryJob objects.
254
+ def personal_industry_jobs(opts = {:characterid => nil})
255
+ args = postfields(opts)
256
+ h = compute_hash(args.merge(:url => @@personal_industry_jobs_url))
257
+ return h if h
258
+ process_query(Reve::Classes::PersonalIndustryJob, opts[:url] || @@personal_industry_jobs_url,false,args)
259
+ end
260
+
261
+ # Returns a list of Reve::Classes::CorporateIndustryJob objects.
262
+ def corporate_industry_jobs(opts = {:characterid => nil})
263
+ args = postfields(opts)
264
+ h = compute_hash(args.merge(:url => @@corporate_industry_jobs_url))
265
+ return h if h
266
+ process_query(Reve::Classes::CorporateIndustryJob, opts[:url] || @@corporate_industry_jobs_url,false,args)
267
+ end
268
+
269
+ # Returns the SkillTree from
270
+ # http://api.eve-online.com/eve/SkillTree.xml.aspx
271
+ # See also: Reve::Classes::SkillTree
272
+ # NOTE: This doesn't actually return a 'tree' yet.
273
+ def skill_tree(opts = {})
274
+ h = compute_hash(opts.merge(:url => @@skill_tree_url) )
275
+ return h if h
276
+ doc = process_query(nil,opts[:url] || @@skill_tree_url,true)
277
+ skills = []
278
+ (doc/'rowset[@name=skills]/row').each do |skill|
279
+ name = skill['typename']
280
+ type_id = skill['typeid']
281
+ group_id = skill['groupid']
282
+ rank = (skill/:rank).inner_html
283
+ desc = (skill/:description).inner_html
284
+ required_skills = []
285
+ reqs = (skill/'rowset@name=[requiredskills]/row')
286
+ reqs.each do |required|
287
+ next if required.kind_of? Hpricot::Text # why is this needed? Why is this returned? How can I only get stuff with typeid and skilllevel?
288
+ required_skills << Reve::Classes::SkillRequirement.new(required) if required['typeid'] && required['skilllevel']
289
+ end
290
+ required_attribs = []
291
+ (skill/'requiredattributes').each do |req|
292
+ pri = doc.at(req.xpath + "/primaryattribute")
293
+ sec = doc.at(req.xpath + "/secondaryattribute")
294
+ required_attribs << Reve::Classes::PrimaryAttribute.new(pri.inner_html)
295
+ required_attribs << Reve::Classes::SecondaryAttribute.new(sec.inner_html)
296
+ end
297
+ bonuses = []
298
+ res = (skill/'rowset@name=[skillbonuscollection]/row')
299
+ res.each do |bonus|
300
+ next if bonus.kind_of? Hpricot::Text
301
+ bonuses << Reve::Classes::SkillBonus.new(bonus) if bonus['bonustype'] && bonus['bonusvalue']
302
+ end
303
+ skills << Reve::Classes::SkillTree.new(name,type_id,group_id,desc,rank,required_attribs,required_skills,bonuses)
304
+ end
305
+ skills
306
+ end
307
+
308
+ # Does big brother tracking from
309
+ # http://api.eve-online.com/corp/MemberTracking.xml.aspx
310
+ # Expects:
311
+ # * charid ( Integer | String ) - Look at players in this Character's Corporation
312
+ # See also: Reve::Classes::MemberTracking
313
+ def member_tracking(opts = {:characterid => nil})
314
+ args = postfields(opts)
315
+ h = compute_hash(args.merge(:url => @@member_tracking_url))
316
+ return h if h
317
+ process_query(Reve::Classes::MemberTracking,opts[:url] || @@member_tracking_url,false,args)
318
+ end
319
+
320
+ # Gets one's own personal WalletBalance from
321
+ # http://api.eve-online.com/char/AccountBalance.xml.aspx
322
+ # Expects:
323
+ # * charid ( Integer | String ) - Look at this player's WalletBalance
324
+ # See also: Reve::Classes::WalletBalance and corporate_wallet_balance
325
+ def personal_wallet_balance(opts = { :characterid => nil })
326
+ args = postfields(opts)
327
+ h = compute_hash(args.merge(:url => @@personal_wallet_balance_url))
328
+ return h if h
329
+ process_query(Reve::Classes::WalletBalance,opts[:url] || @@personal_wallet_balance_url,false,args)
330
+ end
331
+
332
+ # Gets one's corporate WalletBalance from
333
+ # http://api.eve-online.com/corp/AccountBalance.xml.aspx
334
+ # Expects:
335
+ # * charid ( Integer | String ) - Look at WalletBalance objects from this Character's Corporation
336
+ # See also: Reve::Classes::WalletBalance and personal_wallet_balance
337
+ def corporate_wallet_balance(opts = { :characterd => nil })
338
+ args = postfields(opts)
339
+ h = compute_hash(args.merge(:url => @@corporate_wallet_balance_url))
340
+ return h if h
341
+ process_query(Reve::Classes::WalletBalance,opts[:url] || @@corporate_wallet_balance_url,false,args)
342
+ end
343
+
344
+ # Gets one's own personal WalletTransaction list from
345
+ # http://api.eve-online.com/char/WalletTransactions.xml.aspx
346
+ # Expects:
347
+ # * characterid ( Integer | String ) - Look at this player's WalletTransaction list
348
+ # * before_trans_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this Transaction ID.
349
+ # See also: Reve::Classes::WalletTransaction and
350
+ # corporate_wallet_transactions
351
+ def personal_wallet_transactions(opts = { :characterid => nil, :before_trans_id => nil })
352
+ args = postfields(opts)
353
+ h = compute_hash(args.merge(:url => @@personal_wallet_trans_url) )
354
+ return h if h
355
+ process_query(Reve::Classes::PersonalWalletTransaction,opts[:url] || @@personal_wallet_trans_url,false,opts)
356
+ end
357
+
358
+ # Gets one's corporate WalletTransaction list from
359
+ # http://api.eve-online.com/corp/WalletTransactions.xml.aspx
360
+ # Expects:
361
+ # * account_key ( Integer | String ) - Account key (1000-1006) to look at.
362
+ # * charid ( Integer | String ) - Look at WalletTransaction objects from this Character's Corporation
363
+ # * before_trans_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this Transaction ID.
364
+ # See also: Reve::Classes::WalletTransaction and
365
+ # personal_wallet_transactions
366
+ def corporate_wallet_transactions(opts = {:accountkey => nil, :characterid => nil, :beforerefid => nil})
367
+ args = postfields(opts)
368
+ h = compute_hash(args.merge(:url => @@corporate_wallet_trans_url))
369
+ return h if h
370
+ process_query(Reve::Classes::CorporateWalletTransaction,opts[:url] || @@corporate_wallet_trans_url,false,args)
371
+ end
372
+
373
+ # Gets one's own corporate WalletJournal list from
374
+ # http://api.eve-online.com/corp/WalletJournal.xml.aspx
375
+ # Expects:
376
+ # * account_key ( Integer | String ) - Account key (1000-1006) to look at.
377
+ # * charid ( Integer | String ) - Look at WalletJournal objects from this Character's Corporation
378
+ # * before_ref_id ( Integer | String ) - Gets a list of WalletTransaction objects from before this RefID.
379
+ # See also: Reve::Classes::WalletJournal and personal_wallet_journal
380
+ def corporate_wallet_journal(opts = {:accountkey => nil, :characterid => nil, :beforerefid => nil})
381
+ args = postfields(opts)
382
+ h = compute_hash(args.merge(:url => @@corporate_wallet_journal_url))
383
+ return h if h
384
+ process_query(Reve::Classes::WalletJournal,opts[:url] || @@corporate_wallet_journal_url,false,args)
385
+ end
386
+
387
+ # Gets one's own personal WalletJournal list from
388
+ # http://api.eve-online.com/char/WalletJournal.xml.aspx
389
+ # Expects:
390
+ # * charid ( Integer | String ) - Look at this player's WalletJournal list
391
+ # * before_ref_id ( Integer | String ) - Gets a list of WalletJournal objects from before this RefID.
392
+ # See also: Reve::Classes::WalletJournal and corporate_wallet_journal
393
+ def personal_wallet_journal(opts = { :characterid => nil, :beforerefid => nil} )
394
+ args = postfields(opts)
395
+ h = compute_hash(args.merge(:url => @@personal_wallet_journal_url))
396
+ return h if h
397
+ process_query(Reve::Classes::WalletJournal,opts[:url] || @@personal_wallet_journal_url,false,args)
398
+ end
399
+
400
+ # Get a list of personal assets for the characterid.
401
+ # See the Reve::Classes::Asset and Reve::Classes::AssetContainer classes
402
+ # for attributes available.
403
+ def personal_assets_list(opts = { :characterid => nil })
404
+ args = postfields(opts)
405
+ h = compute_hash(args.merge(:url => @@personal_assets_url))
406
+ return h if h
407
+ xml = process_query(nil,opts[:url] || @@personal_assets_url,true,args)
408
+ assets = []
409
+ xml.search("/eveapi/result/rowset[@name='assets']/row").each do |container|
410
+ asset_container = Reve::Classes::AssetContainer.new(container)
411
+ container.search("rowset[@name='contents']/row").each do |asset|
412
+ asset_container.assets << Reve::Classes::Asset.new(asset)
413
+ end
414
+ assets << asset_container
415
+ end
416
+ assets
417
+ end
418
+
419
+ # Get a list of the Corporate Assets. Pass the characterid of the Corporate member See also assets_list method
420
+ def corporate_assets_list(opts = { :characterid => nil})
421
+ args = postfields(opts)
422
+ h = compute_hash(args.merge(:url => @@corporate_assets_url))
423
+ return h if h
424
+ xml = process_query(nil,opts[:url] || @@corporate_assets_url,true,args)
425
+ assets = []
426
+ xml.search("/eveapi/result/rowset/row").each do |container|
427
+ asset_container = Reve::Classes::AssetContainer.new(container)
428
+ container.search("rowset[@name='contents']/row").each do |asset|
429
+ asset_container.assets << Reve::Classes::Asset.new(asset)
430
+ end
431
+ assets << asset_container
432
+ end
433
+ assets
434
+ end
435
+
436
+ # Returns a Character list for the associated key and userid from
437
+ # http://api.eve-online.com/account/Characters.xml.aspx
438
+ # See also: Reve::Classes::Character
439
+ def characters(opts = {})
440
+ args = postfields(opts)
441
+ h = compute_hash(args.merge(:url => @@characters_url))
442
+ return h if h
443
+ process_query(Reve::Classes::Character,opts[:url] || @@characters_url,false,args)
444
+ end
445
+
446
+ # Gets the SkillInTraining from
447
+ # http://api.eve-online.com/char/SkillInTraining.xml.aspx
448
+ # Expects:
449
+ # * charid ( Integer | String ) - Get the SkillInTraining for this Character
450
+ # See also: Reve::Classes::SkillInTraining
451
+ def skill_in_training(opts = {:characterid => nil})
452
+ args = postfields(opts)
453
+ ch = compute_hash(args.merge(:url => @@training_skill_url))
454
+ return ch if ch
455
+ h = {}
456
+ xml = process_query(nil,opts[:url] || @@training_skill_url,true,args)
457
+ xml.search("//result").each do |elem|
458
+ for field in [ 'currentTQTime', 'trainingEndTime','trainingStartTime','trainingTypeID','trainingStartSP','trainingDestinationSP','trainingToLevel','skillInTraining' ]
459
+ field.downcase!
460
+ h[field] = (elem/field.intern).inner_html
461
+ end
462
+ end
463
+ Reve::Classes::SkillInTraining.new(h)
464
+ end
465
+
466
+ # Returns a list of Reve::Classes::Starbase for charid's Corporation.
467
+ # http://api.eve-online.com/corp/StarbaseList.xml.aspx
468
+ # Expects:
469
+ # * charid ( Integer | String ) - Get the Starbase list for this character's Corporation
470
+ # See also Reve::Classes::Starbase
471
+ def starbases(opts = { :characterid => nil})
472
+ args = postfields(opts)
473
+ h = compute_hash(args.merge(:url => @@starbases_url))
474
+ return h if h
475
+ process_query(Reve::Classes::Starbase,opts[:url] || @@starbases_url,false,args)
476
+ end
477
+
478
+ # Returns the fuel status for the Starbase whose item id is starbase_id
479
+ # http://api.eve-online.com/corp/StarbaseDetail.xml.aspx
480
+ # Expects:
481
+ # * charid ( Integer | String ) - Get the Starbase associated wih this character's Corporation
482
+ # * starbase_id ( Integer ) - Get the fuel for this Starbase. This is the Starbase's itemid.
483
+ # See also Reve::Classes::StarbaseFuel
484
+ def starbase_fuel(opts = { :characterid => nil, :starbaseid => nil })
485
+ args = postfields(opts)
486
+ h = compute_hash(args.merge(:url => @@starbasedetail_url))
487
+ return h if h
488
+ ret = process_query(Reve::Classes::StarbaseFuel,opts[:url] || @@starbasedetail_url, false, args)
489
+ ret.each { |r| r.starbase_id = opts[:starbaseid] }
490
+ ret
491
+ end
492
+
493
+
494
+ # Get the last kills for the characterid passed.
495
+ # Expects:
496
+ # * Hash of arguments
497
+ # * * characterid ( Integer ) - The Character whose Kills to retrieve
498
+ # * * beforekillid ( Integer ) - (Optional) - Return the most recent kills before this killid.
499
+ def personal_kills(opts = { :characterid => nil })
500
+ args = postfields(opts)
501
+ h = compute_hash(args.merge(:url => @@personal_kills_url))
502
+ return h if h
503
+ xml = process_query(nil,opts[:url] || @@personal_kills_url,true,args)
504
+ kills = []
505
+ xml.search("/eveapi/result/rowset/row").each do |e|
506
+ victim = Reve::Classes::KillVictim.new(e.search("victim").first) rescue next # cant find victim
507
+ attackers = []
508
+ losses = []
509
+ e.search("rowset[@name='attackers']/row").each do |attacker|
510
+ attackers << Reve::Classes::KillAttacker.new(attacker)
511
+ end
512
+ e.search("rowset[@name='items']/row").each do |lost_item|
513
+ lost = Reve::Classes::KillLoss.new(lost_item)
514
+ lost_item.search("rowset[@name='items']/row").each do |contained|
515
+ lost.contained_losses << Reve::Classes::KillLoss.new(contained)
516
+ end
517
+ losses << lost
518
+ end
519
+ kills << Reve::Classes::Kill.new(e, victim, attackers, losses)
520
+ end
521
+ kills
522
+ end
523
+
524
+ # See the options for personal_kills
525
+ def corporate_kills(opts = { :characterid => nil })
526
+ args = postfields(opts)
527
+ h = compute_hash(args.merge(:url => @@corporate_kills_url))
528
+ return h if h
529
+ xml = process_query(nil,opts[:url] || @@corporate_kills_url,true,args)
530
+ kills = []
531
+ xml.search("/eveapi/result/rowset/row").each do |e|
532
+ victim = Reve::Classes::KillVictim.new(e.search("victim").first) rescue next # cant find victim
533
+ attackers = []
534
+ losses = []
535
+ e.search("rowset[@name='attackers']/row").each do |attacker|
536
+ attackers << Reve::Classes::KillAttacker.new(attacker)
537
+ end
538
+ e.search("rowset[@name='items']/row").each do |lost_item|
539
+ lost = Reve::Classes::KillLoss.new(lost_item)
540
+ lost_item.search("rowset[@name='items']/row").each do |contained|
541
+ lost.contained_losses << Reve::Classes::KillLoss.new(contained)
542
+ end
543
+ losses << lost
544
+ end
545
+ kills << Reve::Classes::Kill.new(e, victim, attackers, losses)
546
+ end
547
+ kills
548
+ end
549
+
550
+ # Gets the CorporationSheet from http://api.eve-online.com/corp/CorporationSheet.xml.aspx
551
+ # Expects:
552
+ # * Hash of arguments:
553
+ # * * characterid ( Integer | String ) - Gets the CorporationSheet for this Character
554
+ # * * corporationid ( Integer ) - If the characterid isn't passed then send the corporation's id
555
+ # (See the alliances method for a list) to get the details of a Corporation that belongs to an Alliance.
556
+ # See also: Reve::Classes::CorporationSheet
557
+ def corporation_sheet(opts = { :characterid => nil })
558
+ args = postfields(opts)
559
+ h = compute_hash(args.merge(:url => @@corporation_sheet_url))
560
+ return h if h
561
+ xml = process_query(nil,opts[:url] || @@corporation_sheet_url,true,args)
562
+
563
+ h = { 'graphicid' => 0, 'shape1' => 0, 'shape2' => 0, 'shape3' => 0, 'color1' => 0, 'color2' => 0, 'color3' => 0, }
564
+ h.keys.each { |k| h[k] = xml.search("//result/logo/" + k + "/").to_s.to_i }
565
+ corporate_logo = Reve::Classes::CorporateLogo.new h
566
+
567
+ wallet_divisions = xml.search("//result/rowset[@name='walletDivisions']/").collect { |k| k if k.kind_of? Hpricot::Elem } - [ nil ]
568
+ divisions = xml.search("//result/rowset[@name='divisions']/").collect { |k| k if k.kind_of? Hpricot::Elem } - [ nil ]
569
+ divisions.collect! { |d| Reve::Classes::CorporateDivision.new(d) }
570
+ wallet_divisions.collect! { |w| Reve::Classes::WalletDivision.new(w) }
571
+
572
+ # Map the XML names to our own names and assign them to the temporary
573
+ # hash +res+ to pass to Reve::Classes::CorporationSheet#new
574
+ res = Hash.new
575
+ { :corporationid => :id, :corporationname => :name, :ticker => :ticker, :ceoid => :ceo_id,
576
+ :ceoname => :ceo_name, :stationid => :station_id, :stationname => :station_name,
577
+ :description => :description, :url => :url, :allianceid => :alliance_id,
578
+ :alliancename => :alliance_name, :taxrate => :tax_rate, :membercount => :member_count,
579
+ :memberlimit => :member_limit }.each do |k,v|
580
+ res[v] = xml.search("//result/#{k.to_s}/").first.to_s.strip
581
+ end
582
+
583
+ Reve::Classes::CorporationSheet.new res, divisions, wallet_divisions, corporate_logo
584
+ end
585
+
586
+ # Gets the CharacterSheet from
587
+ # http://api.eve-online.com/char/CharacterSheet.xml.aspx
588
+ # Expects:
589
+ # * characterid ( Fixnum ) - Get the CharacterSheet for this Character
590
+ # See also: Reve::Classes::CharacterSheet
591
+ def character_sheet(opts = { :characterid => nil })
592
+ args = postfields(opts)
593
+ h = compute_hash(args.merge(:url => @@character_sheet_url))
594
+ return h if h
595
+
596
+ xml = process_query(nil,opts[:url] || @@character_sheet_url,true,args)
597
+ cs = Reve::Classes::CharacterSheet.new
598
+
599
+ (xml/:result/:attributeenhancers).each do |enh|
600
+ for kind in ['intelligenceBonus', 'memoryBonus', 'charismaBonus', 'perceptionBonus','willpowerBonus']
601
+ thing = nil
602
+ case kind
603
+ when 'intelligenceBonus'
604
+ thing = Reve::Classes::IntelligenceEnhancer
605
+ when 'memoryBonus'
606
+ thing = Reve::Classes::MemoryEnhancer
607
+ when 'charismaBonus'
608
+ thing = Reve::Classes::CharismaEnhancer
609
+ when 'perceptionBonus'
610
+ thing = Reve::Classes::PerceptionEnhancer
611
+ when 'willpowerBonus'
612
+ thing = Reve::Classes::WillpowerEnhancer
613
+ else
614
+ thing = Reve::Classes::AttributeEnhancer
615
+ end
616
+ (enh/kind.downcase.intern).each do |b|
617
+ name = (b/:augmentatorname).inner_html
618
+ value = (b/:augmentatorvalue).inner_html
619
+ cs.enhancers << thing.new(name,value)
620
+ end
621
+ end
622
+ end
623
+
624
+ (xml/:result).each do |elem|
625
+ for field in [ 'characterid', 'name', 'race', 'bloodline', 'gender','corporationname','corporationid','balance' ]
626
+ cs.send("#{field}=",(elem/field.intern).first.inner_html)
627
+ end
628
+ end
629
+ (xml/:result/:attributes).each do |elem|
630
+ for attrib in [ 'intelligence','memory','charisma','perception','willpower' ]
631
+ cs.send("#{attrib}=",(elem/attrib.intern).first.inner_html)
632
+ end
633
+ end
634
+ (xml/:result/:rowset/:row).each do |elem|
635
+ cs.skills << Reve::Classes::Skill.new(elem)
636
+ end
637
+ cs
638
+ end
639
+
640
+
641
+ protected
642
+ # Sets up the post fields for Net::HTTP::Get hash for process_query method.
643
+ # See also format_url_request
644
+ # TODO: Consider moving this whole thing into process_query to avoid
645
+ # calling this in every method!
646
+ def postfields(opts = {})
647
+ ret = { "userID" => @userid, "apiKey" => @key }.merge(opts)
648
+ ret.inject({}) do |n, (k,v)|
649
+ n[k] = v.to_s if v
650
+ n
651
+ end
652
+ end
653
+
654
+ # Creates a hash for some hash of postfields. For each API method pass
655
+ # :just_hash => to something to return a hash that can be matched to
656
+ # the last_hash instance method created in process_query.
657
+ # This method is called in each API method before process_query and if
658
+ # :just_hash was passed in args then a String will be returned, otherwise
659
+ # nil will be returned
660
+ # TODO: Consider moving this whole thing into process_query before the URI parsing
661
+ def compute_hash(args = {})
662
+ return nil unless args.include?(:just_hash)
663
+ args.delete(:just_hash)
664
+ url = args[:url].kind_of?(URI) ? args[:url].path : args[:url]
665
+ args.delete(:url)
666
+ spl = url.split '/'
667
+ ret = (spl[-2] + '/' + spl[-1]) + ':'
668
+ args.delete_if { |k,v| (v || "").to_s.length == 0 } # Delete keys if the value is nil
669
+ h = args.stringify_keys
670
+ ret += h.sort.flatten.collect{ |e| e.to_s }.join(':')
671
+ ret.gsub(/:$/,'')
672
+ end
673
+
674
+ # Processes a URL and for simple <rowset><row /><row /></rowset> results
675
+ # create an array of objects of type klass. Or just return the XML if
676
+ # just_xml is set true. args is from postfields
677
+ # This method will call check_exception to see if an Exception came from
678
+ # CCP.
679
+ # Expects:
680
+ # * klass ( Class ) - The class container for parsing. An array of these is returned in default behaviour.
681
+ # * url ( String ) - API URL
682
+ # * just_xml ( Boolean ) - Return only the XML and not attempt to parse //rowset/row. Useful if the XML is not in that form.
683
+ # * args ( Hash ) - Hash of arguments for the request. See postfields method.
684
+ def process_query(klass, url, just_xml = false, opts = {})
685
+
686
+ #args = postfields(opts)
687
+ #h = compute_hash(args.merge(:url => url))
688
+ #return h if h
689
+
690
+ @last_hash = compute_hash(opts.merge({:url => url, :just_hash => true })) # compute hash
691
+
692
+ xml = check_exception(get_xml(url,opts))
693
+ save_xml(xml) if @save_path
694
+
695
+ return xml if just_xml
696
+ return [] if xml.nil? # No XML document returned. We should panic.
697
+ ret = []
698
+ xml.search("//rowset/row").each do |elem|
699
+ ret << klass.new(elem)
700
+ end
701
+ ret
702
+ end
703
+
704
+ # Turns a hash into ?var=baz&bam=boo
705
+ def format_url_request(opts)
706
+ req = "?"
707
+
708
+ opts.stringify_keys!
709
+ opts.keys.sort.each do |key|
710
+ req += "#{CGI.escape(key.to_s)}=#{CGI.escape(opts[key].to_s)}&" if opts[key]
711
+ end
712
+ req.chop # We are lazy and append a & to each pair even if it's the last one. FIXME: Don't do this.
713
+ end
714
+
715
+
716
+ # Gets the XML from a source.
717
+ # Expects:
718
+ # * source ( String | URI ) - If the +source+ is a String Reve will attempt to load the XML file from the local filesystem by the path specified as +source+. If the +source+ is a URI or is a String starting with http (lowercase) Reve will fetch it from that URI on the web.
719
+ # * opts ( Hash ) - Hash of parameters for the request, such as userid, apikey and such.
720
+ # NOTE: To override the lowercase http -> URI rule make the HTTP part uppercase.
721
+ def get_xml(source,opts)
722
+ xml = ""
723
+
724
+ # Let people still pass Strings starting with http.
725
+ if source =~ /^http/
726
+ source = URI.parse(source)
727
+ end
728
+
729
+ if source.kind_of?(URI)
730
+ opts.merge({ :version => 2, :url => nil }) #the uri bit will now ignored in format_url_request
731
+ req_args = format_url_request(opts)
732
+ req = Net::HTTP::Get.new(source.path + req_args)
733
+ req['User-Agent'] = @http_referer_agent || "Reve"
734
+
735
+ res = nil
736
+ response = nil
737
+ 1.upto(@max_tries) do |try|
738
+ begin
739
+ # ||= to prevent making a new Net::HTTP object, the res = nil above should reset this for the next request.
740
+ # the request needs to be here to rescue exceptions from it.
741
+ res ||= Net::HTTP.new(source.host, source.port).start {|http| http.request(req) }
742
+ case res
743
+ when Net::HTTPSuccess, Net::HTTPRedirection
744
+ response = res.body
745
+ end
746
+ rescue Exception
747
+ sleep 5
748
+ next
749
+ end
750
+ break if response
751
+ end
752
+ raise Reve::Exceptions::ReveNetworkStatusException.new( (res.body rescue "No Response Body!") ) unless response
753
+
754
+ xml = response
755
+
756
+ # here ends test for URI
757
+ elsif source.kind_of?(String)
758
+ xml = File.open(source).read
759
+ else
760
+ raise Reve::Exceptions::ReveNetworkStatusException.new("Don't know how to deal with a #{source.class} XML source. I expect a URI or String")
761
+ end
762
+ xml
763
+ end
764
+
765
+ # Raises the proper exception (if there is one), otherwise it returns the
766
+ # XML response.
767
+ def check_exception(xml)
768
+ x = Hpricot(xml)
769
+ begin
770
+ out = x.search("//error") # If this fails then there are some big problems with Hpricot#search ?
771
+ rescue Exception => e
772
+ $stderr.puts "Fatal error ((#{e.to_s})): Couldn't search the XML document ((#{xml})) for any potential error messages! Is your Hpricot broken?"
773
+ exit 1
774
+ end
775
+ @current_time = (x/:currenttime).inner_html.to_time rescue Time.now.utc # Shouldn't need to rescue this but one never knows
776
+ @cached_until = (x/:cacheduntil).inner_html.to_time rescue nil # Errors aren't always cached
777
+ return x if out.size < 1
778
+ code = out.first['code'].to_i
779
+ str = out.first.inner_html
780
+ Reve::Exceptions.raise_it(code,str)
781
+ end
782
+
783
+ def save_xml(xml)
784
+ path = build_save_filename
785
+ FileUtils.mkdir_p(File.dirname(path))
786
+ File.open(path,'w') { |f| f.print xml.to_s }
787
+ end
788
+ def build_save_filename
789
+ method = caller(3).first.match(/\`(.+)'/)[1] # Get the API method that's being called. This is called from save_xml -> process_query -> :real_method
790
+ File.join(@save_path,@userid.to_s,method,( @cached_until || Time.now.utc).to_i.to_s + '.xml')
791
+ end
792
+ end
793
+ end