pwood-wowr 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,41 @@
1
+ Wowr
2
+ ----
3
+ Ruby Library for the World of Warcraft Armory
4
+
5
+
6
+
7
+ Introduction
8
+ ------------
9
+ Wowr is an API for accessing data in the World of Warcraft Armory. Using the XML data provided by the armory, it allows developers to query World of Warcraft items, characters, guilds and arena teams. It is designed to support developers that wish to add WoW information to their Ruby-powered site, as well as larger guild or portal sites for many users.
10
+
11
+
12
+
13
+ Usage
14
+ -----
15
+ See example.rb
16
+
17
+
18
+
19
+ Documentation
20
+ -------------
21
+ Please view the RubyDoc for more information on usage. http://wowr.rubyforge.org/doc/
22
+
23
+
24
+
25
+ Installation
26
+ ------------
27
+ Wowr can be installed through RubyGems using the command below:
28
+ gem install wowr
29
+
30
+ Alternatively, the latest version can be downloaded from SVN with:
31
+ svn checkout http://wowr.rubyforge.org/svn/trunk/lib/
32
+
33
+
34
+
35
+ Author
36
+ ------
37
+ Originally written by Ben Humphreys benhumphreys[at]gmail.com
38
+ Maintained by Peter Wood peter+wowr[at]alastria.net
39
+ Patches from Renaud Chaput and Michael Chen
40
+
41
+ Apologies to anyone missed, please let Peter Wood know if this is the case.
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 5
3
+ :patch: 1
4
+ :major: 0
@@ -0,0 +1,1253 @@
1
+ #
2
+ # Wowr - Ruby library for the World of Warcraft Armory
3
+ # http://wowr.rubyforge.org/
4
+ # Written by Ben Humphreys
5
+ # http://benhumphreys.co.uk/
6
+ # Matained By Peter Wood
7
+ # http://narwar.net/
8
+ #
9
+ # Author:: Ben Humphreys
10
+ # Author:: Peter Wood
11
+
12
+ begin
13
+ require 'hpricot' # version 0.6
14
+ rescue LoadError
15
+ require 'rubygems'
16
+ require 'hpricot'
17
+ end
18
+ require 'net/http'
19
+ require 'net/https'
20
+ require 'cgi'
21
+ require 'fileutils'
22
+ require 'json'
23
+
24
+
25
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
26
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
27
+
28
+ require 'wowr/exceptions.rb'
29
+ require 'wowr/extensions.rb'
30
+
31
+ require 'wowr/calendar.rb'
32
+ require 'wowr/character.rb'
33
+ require 'wowr/achievements.rb'
34
+ require 'wowr/guild.rb'
35
+ require 'wowr/item.rb'
36
+ require 'wowr/arena_team.rb'
37
+ require 'wowr/dungeon.rb'
38
+ require 'wowr/guild_bank.rb'
39
+
40
+ module Wowr
41
+ class API
42
+ VERSION = '0.4.1'
43
+
44
+ @@armory_base_url = 'wowarmory.com/'
45
+ @@login_base_url = 'battle.net/'
46
+
47
+ @@persistant_cookie = 'COM-warcraft'
48
+ @@temporary_cookie = 'JSESSIONID'
49
+
50
+ @@search_url = 'search.xml'
51
+
52
+ @@character_sheet_url = 'character-sheet.xml'
53
+ @@character_talents_url = 'character-talents.xml'
54
+ @@character_reputation_url = 'character-reputation.xml'
55
+
56
+ @@character_achievements_url = 'character-achievements.xml'
57
+
58
+ @@calendar_user_url = 'vault/calendar/month-user.json'
59
+ @@calendar_world_url = 'vault/calendar/month-world.json'
60
+ @@calendar_detail_url = 'vault/calendar/detail.json'
61
+
62
+ @@guild_info_url = 'guild-info.xml'
63
+
64
+ @@item_info_url = 'item-info.xml'
65
+ @@item_tooltip_url = 'item-tooltip.xml'
66
+
67
+ @@arena_team_url = 'team-info.xml'
68
+
69
+ @@guild_bank_contents_url = 'vault/guild-bank-contents.xml'
70
+ @@guild_bank_log_url = 'vault/guild-bank-log.xml'
71
+
72
+ @@login_url = 'login/login.xml'
73
+
74
+ @@dungeons_url = 'data/dungeons.xml'
75
+ @@dungeons_strings_url = 'data/dungeonStrings.xml'
76
+
77
+ @@max_connection_tries = 10
78
+
79
+ @@cache_directory_path = 'cache/'
80
+
81
+ @@user_agent = 'Mozilla/5.0 Gecko/20070219 Firefox/2.0.0.2'
82
+
83
+ @@default_cache_timeout = (7*24*60*60)
84
+ @@failed_cache_timeout = (60*60*24)
85
+ @@cache_failed_requests = true # cache requests that resulted in an error from the armory
86
+
87
+ cattr_accessor :armory_base_url, :search_url,
88
+ :character_sheet_url, :character_talents_url, :character_reputation_url,
89
+ :guild_info_url,
90
+ :item_info_url, :item_tooltip_url,
91
+ :arena_team_url,
92
+ :guild_bank_contents_url, :guild_bank_log_url,
93
+ :login_url,
94
+ :dungeons_url, :dungeons_strings_url,
95
+ :max_connection_tries,
96
+ :cache_directory_path,
97
+ :default_cache_timeout, :failed_cache_timeout, :cache_failed_requests,
98
+ :calendar_user_url, :calendar_world_url, :calendar_detail_url, :persistant_cookie, :temporary_cookie
99
+
100
+ @@search_types = {
101
+ :item => 'items',
102
+ :character => 'characters',
103
+ :guild => 'guilds',
104
+ :arena_team => 'arenateams'
105
+ }
106
+
107
+ @@arena_team_sizes = [2, 3, 5]
108
+
109
+ @@calendar_world_types = ['player', 'holiday', 'bg', 'darkmoon', 'raidLockout', 'raidReset', 'holidayWeekly']
110
+ @@calendar_user_types = ['raid', 'dungeon', 'pvp', 'meeting', 'other']
111
+
112
+ attr_accessor :character_name, :guild_name, :realm, :locale, :lang, :caching, :cache_timeout, :debug
113
+
114
+
115
+ # Constructor
116
+ # Accepts an optional hash of parameters to create defaults for all API requests
117
+ # * options (Hash) - Hash used to set default values for all API requests
118
+ def initialize(options = {})
119
+ @character_name = options[:character_name]
120
+ @guild_name = options[:guild_name]
121
+ @realm = options[:realm]
122
+ @locale = options[:locale] || 'us'
123
+ @lang = options[:lang].nil? ? 'default' : options[:lang]
124
+ @caching = options[:caching].nil? ? true : options[:caching]
125
+ @cache_timeout = options[:cache_timeout] || @@default_cache_timeout
126
+ @debug = options[:debug] || false
127
+ end
128
+
129
+
130
+ # General-purpose search
131
+ # All specific searches are wrappers around this method. Best to use those instead.
132
+ # Returns an array of results of the type requested (Wowr::Classes::SearchCharacter etc.) or an empty array.
133
+ # Searches across all realms.
134
+ # Caching is disabled for searching.
135
+ # * string (String) Search string
136
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
137
+ def search(string, options = {})
138
+ if (string.is_a?(Hash))
139
+ options = string
140
+ else
141
+ options.merge!(:search => string)
142
+ end
143
+
144
+ options = merge_defaults(options)
145
+
146
+ if options[:search].nil? || options[:search].empty?
147
+ raise Wowr::Exceptions::NoSearchString.new
148
+ end
149
+
150
+ if !@@search_types.has_value?(options[:type])
151
+ raise Wowr::Exceptions::InvalidSearchType.new(options[:type])
152
+ end
153
+
154
+ options.merge!(:caching => false)
155
+ options.delete(:realm) # all searches are across realms
156
+
157
+ xml = get_xml(@@search_url, options)
158
+
159
+ results = []
160
+
161
+ if (xml) && (xml%'armorySearch') && (xml%'armorySearch'%'searchResults')
162
+ case options[:type]
163
+
164
+ when @@search_types[:item]
165
+ (xml%'armorySearch'%'searchResults'%'items'/:item).each do |item|
166
+ results << Wowr::Classes::SearchItem.new(item)
167
+ end
168
+
169
+ when @@search_types[:character]
170
+ (xml%'armorySearch'%'searchResults'%'characters'/:character).each do |char|
171
+ results << Wowr::Classes::SearchCharacter.new(char, self)
172
+ end
173
+
174
+ when @@search_types[:guild]
175
+ (xml%'armorySearch'%'searchResults'%'guilds'/:guild).each do |guild|
176
+ results << Wowr::Classes::SearchGuild.new(guild)
177
+ end
178
+
179
+ when @@search_types[:arena_team]
180
+ (xml%'armorySearch'%'searchResults'%'arenaTeams'/:arenaTeam).each do |team|
181
+ results << Wowr::Classes::SearchArenaTeam.new(team)
182
+ end
183
+ end
184
+ end
185
+
186
+ return results
187
+ end
188
+
189
+
190
+ # Characters
191
+ # Returns an array of results of Wowr::Classes::SearchCharacter or an empty array.
192
+ # Searches across all realms.
193
+ # Caching is disabled for searching.
194
+ # Parameters
195
+ # * name (String) Name of the character to search for
196
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
197
+ def search_characters(name, options = {})
198
+ if (name.is_a?(Hash))
199
+ options = name
200
+ else
201
+ options.merge!(:search => name)
202
+ end
203
+
204
+ options.merge!(:type => @@search_types[:character])
205
+ return search(options)
206
+ end
207
+
208
+
209
+ # Get the full details of a character.
210
+ # Requires realm.
211
+ # * name (String) Name of the character to get, defaults to that specified in constructor
212
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
213
+ def get_character(name = @character_name, options = {})
214
+ options = character_options(name, options)
215
+ character_sheet = get_xml(@@character_sheet_url, options)
216
+ character_reputation = get_xml(@@character_reputation_url, options)
217
+
218
+ # FIXME
219
+ if true
220
+ return Wowr::Classes::FullCharacter.new(character_sheet,
221
+ character_reputation,
222
+ self)
223
+ else
224
+ raise Wowr::Excceptions::CharacterNotFound.new(options[:character_name])
225
+ end
226
+ end
227
+
228
+
229
+ # DEPRECATED
230
+ # See get_character
231
+ def get_character_sheet(name = @character_name, options = {})
232
+ return get_character(name, options)
233
+ end
234
+
235
+ # Get achievement infos for a character.
236
+ # Requires realm.
237
+ # * name (String) Name of the character to get, defaults to that specified in constructor
238
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
239
+ def get_character_achievements(name = @character_name, options = {})
240
+ options = character_options(name, options)
241
+
242
+ character_achievements = get_xml(@@character_achievements_url, options)
243
+
244
+ return Wowr::Classes::CharacterAchievementsInfo.new(character_achievements, self)
245
+ end
246
+
247
+ # Get details for all achievements in a category for a character.
248
+ # Requires realm.
249
+ # * achievement_category (Integer) ID of the achievement category
250
+ # * name (String) Name of the character to get, defaults to that specified in constructor
251
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
252
+ def get_character_achievements_category(achievement_category, name = @character_name, options = {})
253
+ options = character_options(name, options)
254
+ options[:achievement_category] = achievement_category.to_i
255
+
256
+ character_achievements_category = get_xml(@@character_achievements_url, options)
257
+
258
+ return Wowr::Classes::AchievementsList.new(character_achievements_category, self)
259
+ end
260
+
261
+
262
+ # Find all guilds with the given string, return array of Wowr::Classes::SearchGuild.
263
+ # Searches across all realms.
264
+ # Caching is disabled for searching.
265
+ # * name (String) Name of the guild to search for
266
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
267
+ def search_guilds(name, options = {})
268
+ if (name.is_a?(Hash))
269
+ options = name
270
+ else
271
+ options.merge!(:search => name)
272
+ end
273
+ options.delete(:realm)
274
+
275
+ options.merge!(:type => @@search_types[:guild])
276
+ return search(options)
277
+ end
278
+
279
+
280
+ # Get the guild details.
281
+ # Guild name is optional, assuming it's set in the api constructor.
282
+ # Requires realm.
283
+ # * name (String) Name of the guild to retrieve, defaults to that specified in constructor
284
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
285
+ def get_guild(name = @guild_name, options = {})
286
+ if (name.is_a?(Hash))
287
+ options = name
288
+ else
289
+ options.merge!(:guild_name => name)
290
+ end
291
+
292
+ options = merge_defaults(options)
293
+
294
+ if options[:guild_name].nil? || options[:guild_name] == ""
295
+ raise Wowr::Exceptions::GuildNameNotSet.new
296
+ elsif options[:realm].nil? || options[:realm].empty?
297
+ raise Wowr::Exceptions::RealmNotSet.new
298
+ end
299
+
300
+ xml = get_xml(@@guild_info_url, options)
301
+
302
+ if !(xml%'guildInfo').children.empty?
303
+ return Wowr::Classes::FullGuild.new(xml)
304
+ else
305
+ raise Wowr::Exceptions::GuildNotFound.new(options[:guild_name])
306
+ end
307
+ end
308
+
309
+
310
+ # Search for items with the specified name.
311
+ # Returns an array of Wowr::Classes::SearchItem.
312
+ # Searches across all realms.
313
+ # Caching is disabled for searching.
314
+ # * name (String) Name of the item
315
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
316
+ def search_items(name, options = {})
317
+ if (name.is_a?(Hash))
318
+ options = name
319
+ else
320
+ options.merge!(:search => name)
321
+ end
322
+
323
+ options.merge!(:type => @@search_types[:item])
324
+ return search(options)
325
+ end
326
+
327
+
328
+ # Get the full item details (Wowr::Classes::FullItem) with the given id.
329
+ # Composite of Wowr::Classes::ItemInfo and Wowr::Classes::ItemTooltip data.
330
+ # Item requests are identical across realms.
331
+ # * id (Fixnum) ID of the item
332
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
333
+ def get_item(id, options = {})
334
+ if (id.is_a?(Hash))
335
+ options = id
336
+ else
337
+ options.merge!(:item_id => id)
338
+ end
339
+
340
+ options = merge_defaults(options)
341
+ options.delete(:realm)
342
+
343
+ info = get_xml(@@item_info_url, options)
344
+ tooltip = get_xml(@@item_tooltip_url, options)
345
+
346
+ if (info%'itemInfo'%'item') && !tooltip.nil?
347
+ return Wowr::Classes::FullItem.new(info%'itemInfo'%'item', tooltip%'itemTooltip', self)
348
+ else
349
+ raise Wowr::Exceptions::ItemNotFound.new(options[:item_id])
350
+ end
351
+ end
352
+
353
+
354
+ # Get the basic item information Wowr::Classes::ItemInfo.
355
+ # Item requests are identical across realms.
356
+ # * id (Fixnum) ID of the item
357
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
358
+ def get_item_info(id, options = {})
359
+ if (id.is_a?(Hash))
360
+ options = id
361
+ else
362
+ options.merge!(:item_id => id)
363
+ end
364
+
365
+ options = merge_defaults(options)
366
+ options.delete(:realm)
367
+
368
+ xml = get_xml(@@item_info_url, options)
369
+
370
+ if (xml%'itemInfo'%'item')
371
+ return Wowr::Classes::ItemInfo.new(xml%'itemInfo'%'item', self)
372
+ else
373
+ raise Wowr::Exceptions::ItemNotFound.new(options[:item_id])
374
+ end
375
+ end
376
+
377
+
378
+ # Get full item details including stats Wowr::Classes::ItemTooltip.
379
+ # Item requests are identical across realms.
380
+ # * id (Fixnum) ID of the item
381
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
382
+ def get_item_tooltip(id, options = {})
383
+ if (id.is_a?(Hash))
384
+ options = id
385
+ else
386
+ options.merge!(:item_id => id)
387
+ end
388
+
389
+ options = merge_defaults(options)
390
+ options.delete(:realm)
391
+
392
+ xml = get_xml(@@item_tooltip_url, options)
393
+
394
+ if !xml.nil?
395
+ return Wowr::Classes::ItemTooltip.new(xml%'itemTooltip')
396
+ else
397
+ raise Wowr::Exceptions::ItemNotFound.new(options[:item_id])
398
+ end
399
+ end
400
+
401
+
402
+ # Search for arena teams with the given name of any size.
403
+ # Returns an array of Wowr::Classes::SearchArenaTeam
404
+ # Searches across all realms.
405
+ # Caching is disabled for searching.
406
+ # * name (String) Name of the arena team to seach for
407
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
408
+ def search_arena_teams(name, options = {})
409
+ if (name.is_a?(Hash))
410
+ options = name
411
+ else
412
+ options.merge!(:search => name)
413
+ end
414
+
415
+ options.merge!(:type => @@search_types[:arena_team])
416
+ return search(options)
417
+ end
418
+
419
+
420
+ # Get the arena team of the given name and size, on the specified realm.
421
+ # Returns Wowr::Classes::FullArenaTeam
422
+ # Requires realm.
423
+ # * name (String) Team arena name
424
+ # * size (Fixnum) Must be 2, 3 or 5
425
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
426
+ def get_arena_team(name, size = nil, options = {})
427
+ if name.is_a?(Hash)
428
+ options = name
429
+ elsif size.is_a?(Hash)
430
+ options = size
431
+ options.merge!(:team_name => name)
432
+ else
433
+ options.merge!(:team_name => name, :team_size => size)
434
+ end
435
+
436
+ options = merge_defaults(options)
437
+
438
+ if options[:team_name].nil? || options[:team_name].empty?
439
+ raise Wowr::Exceptions::ArenaTeamNameNotSet.new
440
+ end
441
+
442
+ if options[:realm].nil? || options[:realm].empty?
443
+ raise Wowr::Exceptions::RealmNotSet.new
444
+ end
445
+
446
+ if !@@arena_team_sizes.include?(options[:team_size])
447
+ raise Wowr::Exceptions::InvalidArenaTeamSize.new("Arena teams size must be: #{@@arena_team_sizes.inspect}")
448
+ end
449
+
450
+ xml = get_xml(@@arena_team_url, options)
451
+
452
+ if !(xml%'arenaTeam').children.empty?
453
+ return Wowr::Classes::ArenaTeam.new(xml%'arenaTeam')
454
+ else
455
+ raise Wowr::Exceptions::ArenaTeamNotFound.new(options[:team_name])
456
+ end
457
+ end
458
+
459
+
460
+ # Get the current items within the guild bank.
461
+ # Note that the bags and items the user can see is dependent on their privileges.
462
+ # Requires realm.
463
+ # * cookie (String) Cookie data returned by the login function.
464
+ # * guild_name (String) Guild name
465
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
466
+ def get_guild_bank_contents(cookie, guild_name = @guild_name, options = {})
467
+ if (cookie.is_a?(Hash))
468
+ options = cookie
469
+ elsif (guild_name.is_a?(Hash))
470
+ options = guild_name
471
+ options.merge!(:cookie => cookie)
472
+ options.merge!(:guild_name => @guild_name)
473
+ else
474
+ options.merge!(:cookie => cookie)
475
+ options.merge!(:guild_name => guild_name)
476
+ end
477
+ options = merge_defaults(options)
478
+
479
+ if options[:cookie].nil? || options[:cookie] == ""
480
+ raise Wowr::Exceptions::CookieNotSet.new
481
+ elsif options[:guild_name].nil? || options[:guild_name] == ""
482
+ raise Wowr::Exceptions::GuildNameNotSet.new
483
+ elsif options[:realm].nil? || options[:realm] == ""
484
+ raise Wowr::Exceptions::RealmNotSet.new
485
+ end
486
+
487
+ options.merge!(:secure => true)
488
+
489
+ xml = get_xml(@@guild_bank_contents_url, options)
490
+
491
+ if !(xml%'guildBank').children.empty?
492
+ return Wowr::Classes::GuildBankContents.new(xml, self)
493
+ else
494
+ raise Wowr::Exceptions::GuildBankNotFound.new(options[:guild_name])
495
+ end
496
+ end
497
+
498
+
499
+ # Get a particular page of the guild bank transaction log.
500
+ # Each page contains up to 1000 transactions, other pages can be specified using :group in the options hash.
501
+ # Note that data returned is specific to the logged in user's privileges.
502
+ # Requires realm.
503
+ # * cookie (String) Cookie data returned by the login function
504
+ # * guild_name (String) Guild name
505
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
506
+ def get_guild_bank_log(cookie, name = @guild_name, options = {})
507
+ if (cookie.is_a?(Hash))
508
+ options = cookie
509
+ elsif (name.is_a?(Hash))
510
+ options = name
511
+ options.merge!(:cookie => cookie)
512
+ options.merge!(:guild_name => @guild_name)
513
+ else
514
+ options.merge!(:cookie => cookie)
515
+ options.merge!(:guild_name => name)
516
+ end
517
+
518
+ options = merge_defaults(options)
519
+
520
+ if options[:cookie].nil? || options[:cookie] == ""
521
+ raise Wowr::Exceptions::CookieNotSet.new
522
+ elsif options[:guild_name].nil? || options[:guild_name] == ""
523
+ raise Wowr::Exceptions::GuildNameNotSet.new
524
+ elsif options[:realm].nil? || options[:realm] == ""
525
+ raise Wowr::Exceptions::RealmNotSet.new
526
+ end
527
+
528
+ options.merge!(:secure => true)
529
+
530
+ xml = get_xml(@@guild_bank_log_url, options)
531
+
532
+ if !(xml%'guildBank').children.empty?
533
+ return Wowr::Classes::GuildBankLog.new(xml, self)
534
+ else
535
+ raise Wowr::Exceptions::GuildBankNotFound.new(options[:guild_name])
536
+ end
537
+ end
538
+
539
+
540
+ def get_complete_world_calendar(cookie, name = @character_name, realm = @realm, options = {})
541
+ if (cookie.is_a?(Hash))
542
+ options = cookie
543
+ elsif (name.is_a?(Hash))
544
+ options = name
545
+ options.merge!(:cookie => cookie)
546
+ options.merge!(:character_name => @character_name)
547
+ options.merge!(:realm => @realm)
548
+ elsif (realm.is_a?(Hash))
549
+ options = realm
550
+ options.merge!(:cookie => cookie)
551
+ options.merge!(:character_name => name)
552
+ options.merge!(:realm => @realm)
553
+ else
554
+ options.merge!(:cookie => cookie)
555
+ options.merge!(:character_name => name)
556
+ options.merge!(:realm => realm)
557
+ end
558
+
559
+ options = merge_defaults(options)
560
+
561
+ events = []
562
+
563
+ @@calendar_world_types.each do |type|
564
+ options.merge!(:calendar_type => type)
565
+ events = events.concat(get_world_calendar(options))
566
+ end
567
+
568
+ events.sort! { |a,b| a.start <=> b.start }
569
+
570
+ return events
571
+ end
572
+
573
+
574
+ def get_world_calendar(cookie, name = @character_name, realm = @realm, options = {})
575
+ if (cookie.is_a?(Hash))
576
+ options = cookie
577
+ elsif (name.is_a?(Hash))
578
+ options = name
579
+ options.merge!(:cookie => cookie)
580
+ options.merge!(:character_name => @character_name)
581
+ options.merge!(:realm => @realm)
582
+ elsif (realm.is_a?(Hash))
583
+ options = realm
584
+ options.merge!(:cookie => cookie)
585
+ options.merge!(:character_name => name)
586
+ options.merge!(:realm => @realm)
587
+ else
588
+ options.merge!(:cookie => cookie)
589
+ options.merge!(:character_name => name)
590
+ options.merge!(:realm => realm)
591
+ end
592
+
593
+ options = merge_defaults(options)
594
+
595
+ if options[:cookie].nil? || options[:cookie] == ""
596
+ raise Wowr::Exceptions::CookieNotSet.new
597
+ elsif options[:character_name].nil? || options[:guild_name] == ""
598
+ raise Wowr::Exceptions::CharacterNameNotSet.new
599
+ elsif options[:realm].nil? || options[:realm] == ""
600
+ raise Wowr::Exceptions::RealmNotSet.new
601
+ end
602
+
603
+ options.merge!(:secure => true)
604
+
605
+ json = get_json(@@calendar_world_url, options)
606
+
607
+ if (!json["events"])
608
+ raise Wowr::Exceptions::EmptyPage
609
+ end
610
+
611
+ events = []
612
+
613
+ json["events"].each do |event|
614
+ events << Wowr::Classes::WorldCalendar.new(event, nil)
615
+ end
616
+
617
+ return events
618
+ end
619
+
620
+
621
+ def get_full_user_calendar(cookie, name = @character_name, realm = @realm, options = {})
622
+ if (cookie.is_a?(Hash))
623
+ options = cookie
624
+ elsif (name.is_a?(Hash))
625
+ options = name
626
+ options.merge!(:cookie => cookie)
627
+ options.merge!(:character_name => @character_name)
628
+ options.merge!(:realm => @realm)
629
+ elsif (realm.is_a?(Hash))
630
+ options = realm
631
+ options.merge!(:cookie => cookie)
632
+ options.merge!(:character_name => name)
633
+ options.merge!(:realm => @realm)
634
+ else
635
+ options.merge!(:cookie => cookie)
636
+ options.merge!(:character_name => name)
637
+ options.merge!(:realm => realm)
638
+ end
639
+
640
+ options = merge_defaults(options)
641
+
642
+ skel_events = get_user_calendar(options)
643
+ full_events = []
644
+
645
+ skel_events.each do |se|
646
+ options.merge!(:event => se.id)
647
+ full_events << get_calendar_event(options)
648
+ end
649
+
650
+ full_events.sort! { |a,b| a.start <=> b.start }
651
+
652
+ return full_events
653
+ end
654
+
655
+
656
+ def get_user_calendar(cookie, name = @character_name, realm = @realm, options = {})
657
+ if (cookie.is_a?(Hash))
658
+ options = cookie
659
+ elsif (name.is_a?(Hash))
660
+ options = name
661
+ options.merge!(:cookie => cookie)
662
+ options.merge!(:character_name => @character_name)
663
+ options.merge!(:realm => @realm)
664
+ elsif (realm.is_a?(Hash))
665
+ options = realm
666
+ options.merge!(:cookie => cookie)
667
+ options.merge!(:character_name => name)
668
+ options.merge!(:realm => @realm)
669
+ else
670
+ options.merge!(:cookie => cookie)
671
+ options.merge!(:character_name => name)
672
+ options.merge!(:realm => realm)
673
+ end
674
+
675
+ options = merge_defaults(options)
676
+
677
+ if options[:cookie].nil? || options[:cookie] == ""
678
+ raise Wowr::Exceptions::CookieNotSet.new
679
+ elsif options[:character_name].nil? || options[:guild_name] == ""
680
+ raise Wowr::Exceptions::CharacterNameNotSet.new
681
+ elsif options[:realm].nil? || options[:realm] == ""
682
+ raise Wowr::Exceptions::RealmNotSet.new
683
+ end
684
+
685
+ options.merge!(:secure => true)
686
+
687
+ json = get_json(@@calendar_user_url, options)
688
+
689
+ if (!json["events"])
690
+ raise Wowr::Exceptions::EmptyPage
691
+ end
692
+
693
+ events = []
694
+
695
+ json["events"].each do |event|
696
+ events << Wowr::Classes::UserCalendar.new(event, nil)
697
+ end
698
+
699
+ return events
700
+ end
701
+
702
+
703
+ def get_calendar_event (cookie, event = nil, name = @character_name, realm = @realm, options = {})
704
+ if (cookie.is_a?(Hash))
705
+ options = cookie
706
+ elsif (event.is_a?(Hash))
707
+ options = event
708
+ options.merge!(:cookie => cookie)
709
+ options.merge!(:event => nil)
710
+ options.merge!(:character_name => @character_name)
711
+ options.merge!(:realm => @realm)
712
+ elsif (name.is_a?(Hash))
713
+ options = name
714
+ options.merge!(:cookie => cookie)
715
+ options.merge!(:event => event)
716
+ options.merge!(:character_name => @character_name)
717
+ options.merge!(:realm => @realm)
718
+ elsif (realm.is_a?(Hash))
719
+ options = realm
720
+ options.merge!(:cookie => cookie)
721
+ options.merge!(:event => event)
722
+ options.merge!(:character_name => name)
723
+ options.merge!(:realm => @realm)
724
+ else
725
+ options.merge!(:cookie => cookie)
726
+ options.merge!(:event => event)
727
+ options.merge!(:character_name => name)
728
+ options.merge!(:realm => realm)
729
+ end
730
+
731
+ options = merge_defaults(options)
732
+
733
+ if options[:cookie].nil? || options[:cookie] == ""
734
+ raise Wowr::Exceptions::CookieNotSet.new
735
+ elsif options[:character_name].nil? || options[:guild_name] == ""
736
+ raise Wowr::Exceptions::CharacterNameNotSet.new
737
+ elsif options[:realm].nil? || options[:realm] == ""
738
+ raise Wowr::Exceptions::RealmNotSet.new
739
+ elsif options[:event].nil? || options[:event] == ""
740
+ raise Wowr::Exceptions::EventNotSet.new
741
+ end
742
+
743
+ options.merge!(:secure => true)
744
+
745
+ json = get_json(@@calendar_detail_url, options)
746
+
747
+ if (!json.is_a?(Hash))
748
+ raise Wowr::Exceptions::EmptyPage
749
+ end
750
+
751
+ return Wowr::Classes::UserDetailCalendar.new(json, nil)
752
+ end
753
+
754
+
755
+ # Get complete list of dungeons.
756
+ # WARNING: This gets two 6k xml files so it's not that fast
757
+ # Takes 0.2s with cache, 2s without
758
+ # New approach: Instead of passing the XML around and performing multiple
759
+ # search lookups to find the elements, run through each XML file once
760
+ # adding data to classes as they appear using hash lookup.
761
+ # Went from 14s to 2s :)
762
+ # * options (Hash) Optional hash of arguments identical to those used in the API constructor (realm, debug, cache etc.)
763
+ def get_dungeons(options = {})
764
+ options = merge_defaults(options)
765
+
766
+ # dungeon_strings contains names for ids
767
+ dungeon_xml = get_xml(@@dungeons_url, options)%'dungeons'
768
+
769
+ dungeon_strings_xml = get_xml(@@dungeons_strings_url, options)
770
+
771
+ results = {}
772
+
773
+ # TODO: Pass the correct part of dungeon_strings_xml to each dungeon?
774
+ if dungeon_xml && !dungeon_xml.children.empty?
775
+ (dungeon_xml/:dungeon).each do |elem|
776
+ dungeon = Wowr::Classes::Dungeon.new(elem)
777
+ results[dungeon.id] = dungeon if dungeon.id
778
+ results[dungeon.key] = dungeon if dungeon.key
779
+ end
780
+
781
+ (dungeon_strings_xml/:dungeon).each do |elem|
782
+ id = elem[:id].to_i
783
+ key = elem[:key]
784
+
785
+ if (results[id])
786
+ results[id].add_name_data(elem)
787
+ elsif (results[key])
788
+ results[key].add_name_data(elem)
789
+ end
790
+ end
791
+ else
792
+ raise Wowr::Exceptions::InvalidXML.new()
793
+ end
794
+
795
+ return results
796
+ end
797
+
798
+
799
+ # Logs the user into the armory using their main world of warcraft username, password and authenticator if given/required.
800
+ # Uses SSL to send details to the login page. Both must be set to true in order to recieve the long life cookie as the second value.
801
+ #
802
+ # short = api.login("username", "password")
803
+ # short, long = api.login("username", "password", nil, true)
804
+ #
805
+ def login(username, password, authenticator = nil, both = false)
806
+ # Create the base URL we will be POSTing to.
807
+ authentication_url = base_url(@locale, {:secure => true, :login => true}) + @@login_url + "?app=armory"
808
+
809
+ # Ensure we add the correct bounce point.
810
+ if (@locale == "www")
811
+ authentication_url += "&ref=http://www.wowarmory.com/index.xml"
812
+ else
813
+ authentication_url += "&ref=http://#{@locale}.wowarmory.com/index.xml"
814
+ end
815
+
816
+ # Ensure we have no final stage.
817
+ redirectstage = nil
818
+
819
+ # Post the first stage
820
+ stage1 = login_http(authentication_url, true, nil, { 'accountName' => username, 'password' => password }, true)
821
+
822
+ # Check what happened.
823
+ if (stage1.code == "200")
824
+ # We didn't pass, but we didn't fail yet if we need an authenticator.
825
+ stage1doc = Hpricot.XML(stage1.body)
826
+
827
+ # Check to see if our details were incorrect.
828
+ if (stage1doc.at("tas:accountName")['error'])
829
+ # We have had an error returned to us with regards to our username or password.
830
+ raise Wowr::Exceptions::InvalidLoginDetails
831
+ end
832
+
833
+ # Okey do we require an authenticator?
834
+ if (!stage1doc.at("tas:authType")['value'] || stage1doc.at("tas:authType")['value'] != "BA")
835
+ # Ummm, we're not invalid nor do we have no clue about the authType required now.
836
+ raise Wowr::Exceptions::LoginBroken
837
+ end
838
+
839
+ # Do we have an authenticator code to use?
840
+ raise Wowr::Exceptions::LoginRequiresAuthenticator if (!authenticator)
841
+
842
+ stage1cookie = nil
843
+
844
+ # Get the *authentication sites* JSESSIONID.
845
+ stage1.header['set-cookie'].scan(/JSESSIONID=(.*?);/) {
846
+ stage1cookie = $1
847
+ }
848
+
849
+ # Let's post the authenticator and the session for this login.
850
+ stage2 = login_http(authentication_url, true, { 'JSESSIONID' => stage1cookie }, { 'authValue' => authenticator }, true)
851
+
852
+ # So now we check what happened.
853
+ if (stage2.code == "200")
854
+ # This isn't a good sign, we should have redirected now.
855
+ stage2doc = Hpricot.XML(stage2.body)
856
+
857
+ # Error is obvious
858
+ if (stage2doc.at("tas:accountName")['error'])
859
+ # We have had an error returned to us with regards to our username or password.
860
+ raise Wowr::Exceptions::InvalidLoginDetails
861
+ end
862
+
863
+ # Error isn't obvious, we can't continue.
864
+ raise Wowr::Exceptions::LoginBroken
865
+ elsif (stage2.code == "302")
866
+ redirectstage = stage2
867
+ end
868
+ elsif (stage1.code == "302")
869
+ redirectstage = stage1
870
+ end
871
+
872
+ # We should have been redirected by now.
873
+ if (!redirectstage)
874
+ raise Wowr::Exceptions::LoginBroken
875
+ end
876
+
877
+ # Time to obtain our next URL and our long term cookie.
878
+ long_cookie = nil
879
+
880
+ redirectstage.header['set-cookie'].scan(/#{@@persistant_cookie}=(.*?);/) {
881
+ long_cookie = $1
882
+ }
883
+
884
+ # Let's bounce to our page that will give us our short term cookie, URL has Kerbrose style ticket.
885
+ short_cookie = login_final_bounce(redirectstage.header['location'])
886
+
887
+ # So what does the user want?
888
+ if (both)
889
+ return short_cookie, long_cookie
890
+ else
891
+ return short_cookie
892
+ end
893
+ end
894
+
895
+ # Reobtains a short term cookie by using the given long life cookie.
896
+ def refresh_login(long_life_cookie)
897
+ # Create the base URL we will be POSTing to.
898
+ authentication_url = base_url(@locale, {:secure => true, :login => true}) + @@login_url + "?app=armory"
899
+
900
+ # Ensure we add the correct bounce point.
901
+ if (@locale == "www")
902
+ authentication_url += "&ref=http://www.wowarmory.com/index.xml"
903
+ else
904
+ authentication_url += "&ref=http://#{@locale}.wowarmory.com/index.xml"
905
+ end
906
+
907
+ # All we need to do is goto the armory login page passing our long life cookie, we should get 302 instantly.
908
+ stage1 = login_http(authentication_url, true, { @@persistant_cookie => long_life_cookie })
909
+
910
+ # Let's see
911
+ if (stage1.code == "200")
912
+ # It's no good, our cookie doesn't work anymore.
913
+ raise Wowr::Exceptions::InvalidLoginDetails
914
+ elsif (stage1.code == "302")
915
+ # Let's bounce to our page that will give us our short term cookie, URL has Kerbrose style ticket.
916
+ return login_final_bounce(stage1.header['location'])
917
+ end
918
+
919
+ # Finally we didn't get 302 or 200?
920
+ raise Wowr::Exceptions::LoginBroken
921
+ end
922
+
923
+ # Clear the cache, optional directory name.
924
+ # * cache_path (String) Relative path of the cache directory to be deleted
925
+ def clear_cache(cache_path = @@cache_directory_path)
926
+ begin
927
+ FileUtils.remove_dir(cache_path)
928
+ rescue Exception => e
929
+
930
+ end
931
+ end
932
+
933
+
934
+ # Return the base url for the armory, e.g. http://eu.wowarmory.com/
935
+ # * locale (String) The locale, defaults to that specified in the API constructor
936
+ def base_url(locale = @locale, options = {})
937
+ str = ""
938
+
939
+ if (options[:secure] == true)
940
+ str += 'https://'
941
+ else
942
+ str += 'http://'
943
+ end
944
+
945
+ if (locale == 'us')
946
+ str += 'www.'
947
+ else
948
+ str += locale + "."
949
+ end
950
+
951
+ if (options[:login] == true)
952
+ str += @@login_base_url
953
+ else
954
+ str += @@armory_base_url
955
+ end
956
+
957
+ return str
958
+ end
959
+
960
+
961
+ protected
962
+
963
+ # Merge the defaults specified in the constructor with those supplied,
964
+ # overriding any defaults with those supplied
965
+ def merge_defaults(options = {})
966
+ defaults = {}
967
+ # defaults[:character_name] = @charater_name if @charater_name
968
+ # defaults[:guild_name] = @guild_name if @guild_name
969
+ defaults[:realm] = @realm if @realm
970
+ defaults[:locale] = @locale if @locale
971
+ defaults[:lang] = @lang if @lang
972
+ defaults[:caching] = @caching if @caching
973
+ defaults[:cache_timeout] = @cache_timeout if @cache_timeout
974
+ defaults[:debug] = @debug if @debug
975
+
976
+ # overwrite defaults with any given options
977
+ defaults.merge!(options)
978
+ end
979
+
980
+ # Returns an option array from character_name and defaults
981
+ def character_options(name, options = {})
982
+ if (name.is_a?(Hash))
983
+ options = name
984
+ else
985
+ options.merge!(:character_name => name)
986
+
987
+ # TODO check
988
+ options = {:character_name => @character_name}.merge(options) if (!@character_name.nil?)
989
+ end
990
+
991
+ options = merge_defaults(options)
992
+
993
+ if options[:character_name].nil? || options[:chracter_name] == ""
994
+ raise Wowr::Exceptions::CharacterNameNotSet.new
995
+ elsif options[:realm].nil? || options[:realm] == ""
996
+ raise Wowr::Exceptions::RealmNotSet.new
997
+ end
998
+
999
+ return options
1000
+ end
1001
+
1002
+
1003
+ # Return an Hpricot document for the given URL
1004
+ def get_xml(url, options = {})
1005
+ response = get_file(url, options)
1006
+ doc = Hpricot.XML(response)
1007
+ errors = doc.search("*[@errCode]")
1008
+ if errors.size > 0
1009
+ errors.each do |error|
1010
+ raise Wowr::Exceptions::raise_me(error[:errCode], options)
1011
+ end
1012
+
1013
+ elsif (doc%'achievements')
1014
+ return doc
1015
+ elsif (doc%'dungeons')
1016
+ return doc
1017
+ elsif (doc%'page').nil?
1018
+ raise Wowr::Exceptions::EmptyPage
1019
+ else
1020
+ return (doc%'page')
1021
+ end
1022
+ end
1023
+
1024
+ # Return an array of hashes for the given URL
1025
+ def get_json(url, options = {})
1026
+ response = get_file(url, options)
1027
+ raw_json = response.scan(/\w+\((.+)\);\z/)[0][0]
1028
+ return JSON.parse(raw_json)
1029
+ end
1030
+
1031
+
1032
+ # Return an raw document for the given URL
1033
+ # TODO: Tidy up?
1034
+ def get_file(url, options = {})
1035
+
1036
+ # better way of doing this?
1037
+ # Map custom keys to the HTTP request values
1038
+ reqs = {
1039
+ :character_name => 'n',
1040
+ :realm => 'r',
1041
+ :search => 'searchQuery',
1042
+ :type => 'searchType',
1043
+ :guild_name => 'gn',
1044
+ :item_id => 'i',
1045
+ :team_size => 'ts',
1046
+ :team_name => 't',
1047
+ :group => 'group',
1048
+ :callback => 'callback',
1049
+ :calendar_type => 'type',
1050
+ :month => 'month',
1051
+ :year => 'year',
1052
+ :event => 'e',
1053
+ :now => 'now',
1054
+ :achievement_category => 'c'
1055
+ }
1056
+
1057
+ params = []
1058
+ options.each do |key, value|
1059
+ params << "#{reqs[key]}=#{u(value)}" if reqs[key]
1060
+ end
1061
+
1062
+ query = ''
1063
+ query = query + '?' + params.join('&') if params.size > 0
1064
+ #query = '?' + params.join('&') if params.size > 0
1065
+
1066
+ base = self.base_url(options[:locale], options)
1067
+ full_query = base + url + query
1068
+
1069
+ if options[:caching]
1070
+ response = get_cache(full_query, options)
1071
+ else
1072
+ response = http_request(full_query, options)
1073
+ end
1074
+ end
1075
+
1076
+
1077
+ # Perform an HTTP request and return the contents of the document
1078
+ def http_request(url, options = {})
1079
+ req = Net::HTTP::Get.new(url)
1080
+ req["user-agent"] = @@user_agent # ensure returns XML
1081
+ req["cookie"] = "cookieMenu=all; cookieLangId=" + options[:lang] + "; cookies=true;"
1082
+
1083
+ req["cookie"] += options[:cookie] if options[:cookie]
1084
+
1085
+ uri = URI.parse(url)
1086
+
1087
+ http = Net::HTTP.new(uri.host, uri.port)
1088
+
1089
+ if (options[:secure])
1090
+ puts "Secure authentication" if options[:debug]
1091
+
1092
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
1093
+ http.use_ssl = true
1094
+ end
1095
+
1096
+
1097
+ begin
1098
+ tries = 0
1099
+ http.start do
1100
+ puts "Get URL "+url if options[:debug]
1101
+ res = http.request req
1102
+ # response = res.body
1103
+
1104
+ response = case res
1105
+ when Net::HTTPSuccess, Net::HTTPRedirection
1106
+ res.body
1107
+ else
1108
+ tries += 1
1109
+ if tries > @@max_connection_tries
1110
+ raise Wowr::Exceptions::NetworkTimeout.new('Timed out')
1111
+ else
1112
+ retry
1113
+ end
1114
+ end
1115
+ end
1116
+ rescue Timeout::Error => e
1117
+ raise Wowr::Exceptions::NetworkTimeout.new('Timed out - Timeout::Error Exception')
1118
+ rescue Net::HTTPExceptions => e
1119
+ raise Wowr::Exceptions::ServerDoesNotExist.new('Specified server at ' + url + ' does not exist.')
1120
+ end
1121
+ end
1122
+
1123
+
1124
+ # Translate the specified URL to the cache location, and return the file
1125
+ # If the cache does not exist, get the contents using http_request and create it
1126
+ def get_cache(url, options = {})
1127
+ path = cache_path(url, options)
1128
+
1129
+ # file doesn't exist, make it
1130
+ if !File.exists?(path) ||
1131
+ options[:refresh_cache] ||
1132
+ (File.mtime(path) < Time.now - @cache_timeout)
1133
+
1134
+ if options[:debug]
1135
+ if !File.exists?(path)
1136
+ puts 'Cache doesn\'t exist, making: ' + path
1137
+ elsif (File.mtime(path) < Time.now - @cache_timeout)
1138
+ puts 'Cache has expired, making again, making: ' + path
1139
+ elsif options[:refresh_cache]
1140
+ puts 'Forced refresh of cache, making: ' + path
1141
+ end
1142
+ end
1143
+
1144
+ # make sure dir exists
1145
+ FileUtils.mkdir_p(localised_cache_path(options[:lang])) unless File.directory?(localised_cache_path(options[:lang]))
1146
+
1147
+ xml_content = http_request(url, options)
1148
+
1149
+ # write the cache
1150
+ file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
1151
+ file.write(xml_content)
1152
+ file.close
1153
+
1154
+ # file exists, return the contents
1155
+ else
1156
+ puts 'Cache already exists, read: ' + path if options[:debug]
1157
+
1158
+ file = File.open(path, 'r')
1159
+ xml_content = file.read
1160
+ file.close
1161
+ end
1162
+ return xml_content
1163
+ end
1164
+
1165
+
1166
+ def cache_path(url, options)
1167
+ @@cache_directory_path + options[:lang] + '/' + url_to_filename(url)
1168
+ end
1169
+
1170
+
1171
+ # remove http://*.wowarmory.com/ leaving just xml file part and request parameters
1172
+ # Kind of assuming incoming URL is the same as the current locale
1173
+ def url_to_filename(url) #:nodoc:
1174
+ temp = url.gsub(base_url(), '')
1175
+ temp.gsub!('/', '.')
1176
+ return temp
1177
+ end
1178
+
1179
+
1180
+
1181
+ def localised_cache_path(lang = @lang) #:nodoc:
1182
+ return @@cache_directory_path + lang
1183
+ end
1184
+
1185
+
1186
+
1187
+ def u(str) #:nodoc:
1188
+ if str.instance_of?(String)
1189
+ return CGI.escape(str)
1190
+ else
1191
+ return str
1192
+ end
1193
+ end
1194
+
1195
+ def login_final_bounce(url)
1196
+ # Let's bounce to our page that will give us our short term cookie, URL has Kerbrose style ticket.
1197
+ finalstage = login_http(url)
1198
+
1199
+ # Did we get a 200?
1200
+ if (finalstage.code == "200")
1201
+ # Get the short term cookie at last
1202
+ short_cookie = nil
1203
+ finalstage.header['set-cookie'].scan(/#{@@temporary_cookie}=(.*?);/) {
1204
+ short_cookie = $1
1205
+ }
1206
+
1207
+ return short_cookie
1208
+ end
1209
+
1210
+ # Finally we didn't get 200?
1211
+ raise Wowr::Exceptions::LoginBroken
1212
+ end
1213
+
1214
+ def login_http(url, ssl = false, cookie = nil, data = nil, post = false)
1215
+ if (post)
1216
+ req = Net::HTTP::Post.new(url)
1217
+ else
1218
+ req = Net::HTTP::Get.new(url)
1219
+ end
1220
+
1221
+ req["user-agent"] = "Mozilla/5.0 Gecko/20070219 Firefox/2.0.0.2" # ensure returns XML
1222
+ req["cookie"] = "cookieMenu=all; cookies=true;"
1223
+ req["cookie"] += cookie.collect { |key, value| "#{key}=#{value};"}.join(" ") if cookie
1224
+
1225
+ uri = URI.parse(url)
1226
+ http = Net::HTTP.new(uri.host, uri.port)
1227
+
1228
+ if (ssl)
1229
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
1230
+ http.use_ssl = true
1231
+ end
1232
+
1233
+ req.set_form_data(data, '&') if data
1234
+
1235
+ http.start do
1236
+ res = http.request(req)
1237
+
1238
+ tries = 0
1239
+ response = case res
1240
+ when Net::HTTPSuccess, Net::HTTPRedirection
1241
+ return res
1242
+ else
1243
+ tries += 1
1244
+ if tries > @@max_connection_tries
1245
+ raise Wowr::Exceptions::NetworkTimeout.new('Timed out')
1246
+ else
1247
+ retry
1248
+ end
1249
+ end
1250
+ end
1251
+ end
1252
+ end
1253
+ end