blizzard_api 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.gitlab-ci.yml +28 -0
  4. data/.rubocop.yml +13 -0
  5. data/.rubocop_todo.yml +7 -0
  6. data/.travis.yml +7 -0
  7. data/CHANGELOG.md +4 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +44 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +208 -0
  12. data/Rakefile +12 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/blizzard_api.gemspec +42 -0
  16. data/lib/blizzard_api.rb +14 -0
  17. data/lib/blizzard_api/configuration.rb +82 -0
  18. data/lib/blizzard_api/diablo.rb +76 -0
  19. data/lib/blizzard_api/diablo/community/act.rb +35 -0
  20. data/lib/blizzard_api/diablo/community/artisan.rb +38 -0
  21. data/lib/blizzard_api/diablo/community/character.rb +38 -0
  22. data/lib/blizzard_api/diablo/community/follower.rb +25 -0
  23. data/lib/blizzard_api/diablo/community/item.rb +26 -0
  24. data/lib/blizzard_api/diablo/community/item_type.rb +35 -0
  25. data/lib/blizzard_api/diablo/community/profile.rb +77 -0
  26. data/lib/blizzard_api/diablo/game_data/era.rb +21 -0
  27. data/lib/blizzard_api/diablo/game_data/generic_data_endpoint.rb +58 -0
  28. data/lib/blizzard_api/diablo/game_data/season.rb +21 -0
  29. data/lib/blizzard_api/diablo/request.rb +15 -0
  30. data/lib/blizzard_api/exception.rb +12 -0
  31. data/lib/blizzard_api/request.rb +182 -0
  32. data/lib/blizzard_api/starcraft.rb +40 -0
  33. data/lib/blizzard_api/starcraft/community/account.rb +23 -0
  34. data/lib/blizzard_api/starcraft/community/ladder.rb +34 -0
  35. data/lib/blizzard_api/starcraft/community/profile.rb +76 -0
  36. data/lib/blizzard_api/starcraft/game_data/league.rb +27 -0
  37. data/lib/blizzard_api/starcraft/request.rb +36 -0
  38. data/lib/blizzard_api/version.rb +6 -0
  39. data/lib/blizzard_api/wow.rb +167 -0
  40. data/lib/blizzard_api/wow/community/achievements.rb +45 -0
  41. data/lib/blizzard_api/wow/community/auction.rb +25 -0
  42. data/lib/blizzard_api/wow/community/boss.rb +35 -0
  43. data/lib/blizzard_api/wow/community/challenge.rb +35 -0
  44. data/lib/blizzard_api/wow/community/character.rb +103 -0
  45. data/lib/blizzard_api/wow/community/guild.rb +67 -0
  46. data/lib/blizzard_api/wow/community/item.rb +46 -0
  47. data/lib/blizzard_api/wow/community/mount.rb +24 -0
  48. data/lib/blizzard_api/wow/community/pets.rb +85 -0
  49. data/lib/blizzard_api/wow/community/pvp.rb +34 -0
  50. data/lib/blizzard_api/wow/community/quest.rb +25 -0
  51. data/lib/blizzard_api/wow/community/recipe.rb +25 -0
  52. data/lib/blizzard_api/wow/community/spell.rb +25 -0
  53. data/lib/blizzard_api/wow/community/zone.rb +35 -0
  54. data/lib/blizzard_api/wow/game_data/connected_realm.rb +37 -0
  55. data/lib/blizzard_api/wow/game_data/generic_data_endpoint.rb +58 -0
  56. data/lib/blizzard_api/wow/game_data/mythic_keystone_affix.rb +23 -0
  57. data/lib/blizzard_api/wow/game_data/playable_class.rb +62 -0
  58. data/lib/blizzard_api/wow/game_data/playable_specialization.rb +70 -0
  59. data/lib/blizzard_api/wow/game_data/power_type.rb +23 -0
  60. data/lib/blizzard_api/wow/game_data/race.rb +51 -0
  61. data/lib/blizzard_api/wow/game_data/realm.rb +48 -0
  62. data/lib/blizzard_api/wow/game_data/region.rb +49 -0
  63. data/lib/blizzard_api/wow/request.rb +16 -0
  64. metadata +198 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Diablo
5
+ ##
6
+ # This class allows access to Diablo III era data
7
+ #
8
+ # @see https://develop.battle.net/documentation/api-reference/diablo-3-game-data-api
9
+ #
10
+ # You can get an instance of this class using the default region as follows:
11
+ # api_instance = BlizzardApi::Diablo.era
12
+ class Era < Diablo::GenericDataEndpoint
13
+ protected
14
+
15
+ def endpoint_setup
16
+ @endpoint = 'era'
17
+ @ttl = CACHE_TRIMESTER
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Diablo
5
+ # Generic endpoint to support most data requests with minor configurations
6
+ class GenericDataEndpoint < Diablo::Request
7
+ def initialize(region = nil)
8
+ super region
9
+ endpoint_setup
10
+ @ttl ||= CACHE_DAY
11
+ end
12
+
13
+ ##
14
+ # Get information about the resource
15
+ #
16
+ # @!macro request_options
17
+ #
18
+ # @!macro response
19
+ def index(options = {})
20
+ api_request "#{base_url(:game_data)}/#{@endpoint}/", default_options.merge(options)
21
+ end
22
+
23
+ ##
24
+ # Fetch all possible data for one of items listed by the {#index} using its *id*
25
+ #
26
+ # @param [Integer] id One of the IDs returned by the {#index}
27
+ # @!macro request_options
28
+ #
29
+ # @!macro response
30
+ def get(id, options = {})
31
+ api_request "#{base_url(:game_data)}/#{@endpoint}/#{id}", default_options.merge(options)
32
+ end
33
+
34
+ ##
35
+ # Fetch leaderboard data for the current endpoint
36
+ #
37
+ # @param [Integer] id One of the IDs returned by the {index}
38
+ # @param [Integer] leaderboard_id Leaderboard id
39
+ # @!macro request_options
40
+ #
41
+ # @!macro response
42
+ def leaderboard(id, leaderboard_id, options = {})
43
+ opts = default_options.merge(options)
44
+ api_request "#{base_url(:game_data)}/#{@endpoint}/#{id}/leaderboard/#{leaderboard_id}", opts
45
+ end
46
+
47
+ protected
48
+
49
+ def endpoint_setup
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def default_options
54
+ { ttl: @ttl }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Diablo
5
+ ##
6
+ # This class allows access to Diablo III season data
7
+ #
8
+ # @see https://develop.battle.net/documentation/api-reference/diablo-3-game-data-api
9
+ #
10
+ # You can get an instance of this class using the default region as follows:
11
+ # api_instance = BlizzardApi::Diablo.season
12
+ class Season < Diablo::GenericDataEndpoint
13
+ protected
14
+
15
+ def endpoint_setup
16
+ @endpoint = 'season'
17
+ @ttl = CACHE_TRIMESTER
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Diablo
5
+ # World of Warcraft requests
6
+ class Request < BlizzardApi::Request
7
+ ##
8
+ # @!macro regions
9
+ def initialize(region = nil)
10
+ super region
11
+ @game = 'd3'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # API Exception
5
+ class ApiException < RuntimeError
6
+ attr_reader :code
7
+
8
+ def initialize(msg = '', code = nil)
9
+ @code = code
10
+ super msg
11
+ end
12
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # @!macro [new] request_options
5
+ # @param {Hash} options You can specify some options
6
+ # @option options [String] :region Overrides the default region for a single call
7
+ # @option options [String] :locale Overrides the default locale for a single call
8
+ # @option options [String] :namespace Overrides the default namespace for a single call
9
+ # @option options [String] :access_token Overrides the access_token for a single call
10
+ # @option options [Boolean] :ignore_cache If set to true the request will not use the cache
11
+ # @option options [Integer] :ttl Override the default time (in seconds) a request should be cached
12
+
13
+ ##
14
+ # @!macro [new] regions
15
+ # @param {Symbol} region One of the valid API regions *:us*, *:eu*, *:ko*, and *:tw*
16
+ # @note This gem do not support nor will support China endpoints
17
+
18
+ ##
19
+ # @!macro [new] response
20
+ # @return [OpenStruct,Array] API Response. The actual type of the returned object depends on the *format* option
21
+ # in the configuration module
22
+
23
+ ##
24
+ # @!macro [new] complete
25
+ # Iterates through the {index} response data and fetch additional information using {get}, it results in a more
26
+ # complete set of data
27
+ # @note IT MAY PERFORM MANY REQUESTS TO FETCH ALL DATA
28
+ # @!macro request_options
29
+ # @!macro response
30
+
31
+ module BlizzardApi
32
+ ##
33
+ # Simplifies the requests to Blizzard APIS
34
+ class Request
35
+ # One minute cache
36
+ CACHE_MINUTE = 60
37
+ # One hour cache
38
+ CACHE_HOUR = 60 * CACHE_MINUTE
39
+ # One day cache
40
+ CACHE_DAY = 24 * CACHE_HOUR
41
+ # One week cache
42
+ CACHE_WEEK = CACHE_DAY * 7
43
+ # One (commercial) month cache
44
+ CACHE_MONTH = CACHE_DAY * 30
45
+ # Three (commercial) months cache
46
+ CACHE_TRIMESTER = CACHE_MONTH * 3
47
+
48
+ ##
49
+ # @!attribute region
50
+ # @return [String] Api region
51
+ attr_accessor :region
52
+
53
+ ##
54
+ # @!macro regions
55
+ def initialize(region = nil)
56
+ self.region = region || BlizzardApi.region
57
+ @redis = Redis.new(host: BlizzardApi.redis_host, port: BlizzardApi.redis_port) if BlizzardApi.use_cache
58
+ # Use the shared access_token, or create one if it doesn't exists. This avoids unnecessary calls to create tokens.
59
+ @access_token = BlizzardApi.access_token || create_access_token
60
+ end
61
+
62
+ require 'net/http'
63
+ require 'uri'
64
+ require 'json'
65
+ require 'redis'
66
+
67
+ protected
68
+
69
+ def base_url(scope)
70
+ case scope
71
+ when :game_data
72
+ "https://#{region}.api.blizzard.com/data/#{@game}"
73
+ when :community
74
+ "https://#{region}.api.blizzard.com/#{@game}"
75
+ when :profile
76
+ "https://#{region}.api.blizzard.com/profile/#{@game}"
77
+ else
78
+ raise ArgumentError
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Returns a valid namespace string for consuming the api endpoints
84
+ #
85
+ # @param [Symbol] scope Scope of the namespace to be used. *:dynamic* or *:static*
86
+ def endpoint_namespace(scope)
87
+ case scope
88
+ when :dynamic
89
+ "dynamic-#{region}"
90
+ when :static
91
+ "static-#{region}"
92
+ else
93
+ raise ArgumentError, 'Invalid namespace scope'
94
+ end
95
+ end
96
+
97
+ def create_access_token
98
+ uri = URI.parse("https://#{BlizzardApi.region}.battle.net/oauth/token")
99
+
100
+ http = Net::HTTP.new(uri.host, uri.port)
101
+ http.use_ssl = true
102
+
103
+ request = Net::HTTP::Post.new(uri.path)
104
+ request.basic_auth(BlizzardApi.app_id, BlizzardApi.app_secret)
105
+ request['Content-Type'] = 'application/x-www-form-urlencoded'
106
+ request.set_form_data grant_type: 'client_credentials'
107
+
108
+ response = http.request(request)
109
+ BlizzardApi.access_token = JSON.parse(response.body)['access_token']
110
+ end
111
+
112
+ def request(url, options = {})
113
+ # Creates the whole url for request
114
+ parsed_url = URI.parse(url)
115
+
116
+ data = options[:ignore_cache] ? nil : find_in_cache(parsed_url.to_s)
117
+ # If data was found that means cache is enabled and valid
118
+ return format_response data if data
119
+
120
+ # Override access_token
121
+ @access_token = options[:access_token] if options.include? :access_token
122
+
123
+ response = consume_api parsed_url
124
+
125
+ unless options[:ignore_cache]
126
+ ttl = options[:ttl] || CACHE_DAY
127
+ save_in_cache parsed_url.to_s, response.body, ttl
128
+ end
129
+
130
+ format_response response.body
131
+ end
132
+
133
+ def api_request(uri, query_string = {})
134
+ # List of request options
135
+ options_key = %i[ignore_cache ttl format access_token]
136
+
137
+ # Separates request options from api fields and options. Any user-defined option will be treated as api field.
138
+ options = query_string.select { |k, _v| query_string.delete(k) || true if options_key.include? k }
139
+
140
+ # In case uri already have query string parameters joins them with &
141
+ if query_string.size.positive?
142
+ query_string = URI.encode_www_form(query_string, false)
143
+ uri = uri.include?('?') ? "#{uri}&#{query_string}" : "#{uri}?#{query_string}"
144
+ end
145
+
146
+ request uri, options
147
+ end
148
+
149
+ private
150
+
151
+ def consume_api(url)
152
+ # Creates a HTTP connection and request to ensure thread safety
153
+ http = Net::HTTP.new(url.host, url.port)
154
+ http.use_ssl = true
155
+ request = Net::HTTP::Get.new(url)
156
+
157
+ # Blizzard API documentation states the preferred way to send the access_token is using Bearer token on header
158
+ request['Authorization'] = "Bearer #{@access_token}"
159
+
160
+ # Executes the request
161
+ http.request(request).tap do |response|
162
+ raise ApiException.new 'Request failed', response.code unless response.code.to_i == 200
163
+ end
164
+ end
165
+
166
+ def save_in_cache(resource_url, data, ttl)
167
+ return nil unless BlizzardApi.use_cache
168
+
169
+ @redis.setex resource_url, ttl, data
170
+ end
171
+
172
+ def find_in_cache(resource_url)
173
+ return false unless BlizzardApi.use_cache
174
+
175
+ @redis.get resource_url if @redis.exists resource_url
176
+ end
177
+
178
+ def format_response(data)
179
+ JSON.parse(data, symbolize_names: true)
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ # Starcraft II related classes
5
+ module Starcraft
6
+ require_relative 'starcraft/request'
7
+
8
+ # Starcraft II data api
9
+ require_relative 'starcraft/game_data/league'
10
+
11
+ ##
12
+ # @return {League}
13
+ def league
14
+ BlizzardApi::Starcraft::League.new
15
+ end
16
+
17
+ # Starcraft community api
18
+ require_relative 'starcraft/community/profile'
19
+ require_relative 'starcraft/community/ladder'
20
+ require_relative 'starcraft/community/account'
21
+
22
+ ##
23
+ # @return {Profile}
24
+ def profile
25
+ BlizzardApi::Starcraft::Profile.new
26
+ end
27
+
28
+ ##
29
+ # @return {Ladder}
30
+ def ladder
31
+ BlizzardApi::Starcraft::Ladder.new
32
+ end
33
+
34
+ ##
35
+ # @return {Account}
36
+ def account
37
+ BlizzardApi::Starcraft::Account.new
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Starcraft
5
+ ##
6
+ # This class allows access to Starcraft II league data
7
+ #
8
+ # @see https://develop.battle.net/documentation/api-reference/starcraft-2-game-data-api
9
+ #
10
+ # You can get an instance of this class using the default region as follows:
11
+ # api_instance = BlizzardApi::Starcraft.account
12
+ class Account < Starcraft::Request
13
+ ##
14
+ # Returns information about a player account
15
+ #
16
+ # @param [Integer] account_id Account ID
17
+ # @!macro request_options
18
+ def player(account_id, options = {})
19
+ api_request "#{base_url(:community)}/player/#{account_id}", { ttl: CACHE_DAY }.merge(options)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Starcraft
5
+ ##
6
+ # This class allows access to Starcraft II ladder data
7
+ #
8
+ # @see https://develop.battle.net/documentation/api-reference/starcraft-2-game-data-api
9
+ #
10
+ # You can get an instance of this class using the default region as follows:
11
+ # api_instance = BlizzardApi::Starcraft.ladder
12
+ class Ladder < Starcraft::Request
13
+ ##
14
+ # Grandmaster endpoint
15
+ #
16
+ # @!macro sc2_regions
17
+ # @!macro request_options
18
+ def grandmaster(region_id, options = {})
19
+ reg = resolve_region(region_id)
20
+ api_request "#{base_url(:community)}/ladder/grandmaster/#{reg}", { ttl: CACHE_DAY }.merge(options)
21
+ end
22
+
23
+ ##
24
+ # Season endpoint
25
+ #
26
+ # @!macro sc2_regions
27
+ # @!macro request_options
28
+ def season(region_id, options = {})
29
+ reg = resolve_region(region_id)
30
+ api_request "#{base_url(:community)}/ladder/season/#{reg}", { ttl: CACHE_DAY }.merge(options)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlizzardApi
4
+ module Starcraft
5
+ ##
6
+ # This class allows access to Starcraft II profile data
7
+ #
8
+ # @see https://develop.battle.net/documentation/api-reference/starcraft-2-game-data-api
9
+ #
10
+ # You can get an instance of this class using the default region as follows:
11
+ # api_instance = BlizzardApi::Starcraft.profile
12
+ class Profile < Starcraft::Request
13
+ ##
14
+ # Static profile data
15
+ #
16
+ # @!macro sc2_regions
17
+ # @!macro request_options
18
+ def static(region_id, options = {})
19
+ reg = resolve_region(region_id)
20
+ api_request "#{base_url(:community)}/static/profile/#{reg}", { ttl: CACHE_DAY }.merge(options)
21
+ end
22
+
23
+ ##
24
+ # Metadata
25
+ #
26
+ # @!macro sc2_regions
27
+ # @param [Integer] realm_id Realm ID
28
+ # @param [Integer] profile_id Profile ID
29
+ # @!macro request_options
30
+ def metadata(region_id, realm_id, profile_id, options = {})
31
+ reg = resolve_region(region_id)
32
+ opts = { ttl: CACHE_DAY }.merge(options)
33
+ api_request "#{base_url(:community)}/metadata/profile/#{reg}/#{realm_id}/#{profile_id}", opts
34
+ end
35
+
36
+ ##
37
+ # Profile data
38
+ #
39
+ # @!macro sc2_regions
40
+ # @param [Integer] realm_id Realm ID
41
+ # @param [Integer] profile_id Profile ID
42
+ # @!macro request_options
43
+ def profile(region_id, realm_id, profile_id, options = {})
44
+ reg = resolve_region(region_id)
45
+ opts = { ttl: CACHE_DAY }.merge(options)
46
+ api_request "#{base_url(:community)}/profile/#{reg}/#{realm_id}/#{profile_id}", opts
47
+ end
48
+
49
+ ##
50
+ # Ladder summary
51
+ #
52
+ # @!macro sc2_regions
53
+ # @param [Integer] realm_id Realm ID
54
+ # @param [Integer] profile_id Profile ID
55
+ # @!macro request_options
56
+ def ladder_summary(region_id, realm_id, profile_id, options = {})
57
+ reg = resolve_region(region_id)
58
+ opts = { ttl: CACHE_DAY }.merge(options)
59
+ api_request "#{base_url(:community)}profile/#{reg}/#{realm_id}/#{profile_id}/ladder/summary ", opts
60
+ end
61
+
62
+ ##
63
+ # Ladder data
64
+ #
65
+ # @!macro sc2_regions
66
+ # @param [Integer] realm_id Realm ID
67
+ # @param [Integer] profile_id Profile ID
68
+ # @!macro request_options
69
+ def ladder(region_id, realm_id, profile_id, ladder_id, options = {})
70
+ reg = resolve_region(region_id)
71
+ opts = { ttl: CACHE_DAY }.merge(options)
72
+ api_request "#{base_url(:community)}/profile/#{reg}/#{realm_id}/#{profile_id}/ladder/#{ladder_id}", opts
73
+ end
74
+ end
75
+ end
76
+ end