restiny 5.0.0 → 5.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 366f1afb92a72c9adbc817eb5e8651fcd2df0656584c138660708525343b8407
4
- data.tar.gz: 7fa7f9df35a0ee12206627a43c7ffb4d299819c5d26340c83186f520e30de99e
3
+ metadata.gz: 19b9e9cb2183fde785169d457628f4f7517c74feed228aacee4d45cdfe901fff
4
+ data.tar.gz: bebc650be6668d5a46a284a2aeb6912f2309cce7a7100ec4de57d3a95b11805e
5
5
  SHA512:
6
- metadata.gz: 8c71e268039ac692949adf704883eda27bc2da5d747dab7d610a09621921ef3e64e4909bca9456431f404a0ff4211a19f31efbdf7eb57701bf852bacf360cf90
7
- data.tar.gz: 77c6af1bf576b0e590a6659565af7739316cbc3a72b159a26c8b3077308c3713f1cb8cf45c103d3a0993c2012957feaeb372c6bdaec0436b85b4af0b2f290261
6
+ metadata.gz: c4fbe785ad70936712f351e1f3453f1915220af599ea9d89a09dc3c67713b208c6232e12d52d20fb25153c6a9b04b706b4bf1b367a2c8ded96a79da10f9764af
7
+ data.tar.gz: ed736dce127c0c76cebec7051b89ba02505452a4f9d2aa6bce6beb836b8fc6169586a6970fea1db3387db7700e3cee2b032edcb9952abd7ba9f85940e1a320e4
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ require 'securerandom'
6
+
7
+ module Restiny
8
+ module Api
9
+ module Authentication
10
+ include Base
11
+
12
+ def get_authorise_url(redirect_url: nil, state: nil)
13
+ check_oauth_client_id
14
+
15
+ @oauth_state = state || SecureRandom.hex(15)
16
+
17
+ params = { response_type: 'code', client_id: @oauth_client_id, state: @oauth_state }
18
+ params['redirect_url'] = redirect_url unless redirect_url.nil?
19
+
20
+ auth_connection.build_url("#{BUNGIE_URL}/en/oauth/authorize/", params).to_s
21
+ end
22
+
23
+ def request_access_token(code:, redirect_url: nil)
24
+ check_oauth_client_id
25
+
26
+ params = { code: code, grant_type: 'authorization_code', client_id: @oauth_client_id }
27
+ params['redirect_url'] = redirect_url unless redirect_url.nil?
28
+
29
+ auth_connection.post('app/oauth/token/', params).body
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'restiny/constants'
4
+ require 'restiny/errors'
5
+ require 'faraday'
6
+ require 'faraday/follow_redirects'
7
+ require 'faraday/destiny/api'
8
+ require 'faraday/destiny/auth'
9
+
10
+ module Restiny
11
+ BUNGIE_URL = 'https://www.bungie.net'
12
+ API_BASE_URL = "#{BUNGIE_URL}/platform".freeze
13
+
14
+ attr_accessor :api_key, :oauth_state, :oauth_client_id, :access_token, :user_agent
15
+
16
+ module Api
17
+ module Base
18
+ def api_get(endpoint:, params: {})
19
+ api_connection.get(endpoint, params, token_header).body
20
+ end
21
+
22
+ def api_post(endpoint:, params: {})
23
+ api_connection.post(endpoint, params, token_header).body
24
+ end
25
+
26
+ private
27
+
28
+ def check_oauth_client_id
29
+ raise Restiny::RequestError, 'You need to set an OAuth client ID' unless @oauth_client_id
30
+ end
31
+
32
+ def default_headers
33
+ { 'User-Agent': @user_agent || "restiny v#{Restiny::VERSION}" }
34
+ end
35
+
36
+ def api_connection
37
+ raise Restiny::InvalidParamsError, 'You need to set an API key' unless @api_key
38
+
39
+ @api_connection ||=
40
+ Faraday.new(
41
+ url: API_BASE_URL,
42
+ headers: default_headers.merge('X-API-KEY': @api_key)
43
+ ) do |faraday|
44
+ faraday.request :json
45
+ faraday.response :follow_redirects
46
+ faraday.response :destiny_api
47
+ faraday.response :json
48
+ end
49
+ end
50
+
51
+ def auth_connection
52
+ @auth_connection ||=
53
+ Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
54
+ faraday.request :url_encoded
55
+ faraday.response :follow_redirects
56
+ faraday.response :destiny_auth
57
+ faraday.response :json
58
+ end
59
+ end
60
+
61
+ def token_header
62
+ {}.tap { |headers| headers['authorization'] = "Bearer #{@access_token}" if @access_token }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ require 'restiny/manifest'
6
+
7
+ require 'down'
8
+ require 'tmpdir'
9
+ require 'zip'
10
+
11
+ module Restiny
12
+ module Api
13
+ module Manifest
14
+ include Base
15
+
16
+ def download_manifest(locale: 'en', force_download: false)
17
+ result = api_get(endpoint: 'Destiny2/Manifest/')
18
+ raise Restiny::ResponseError, 'Unable to determine manifest details' if result.nil?
19
+
20
+ return manifests[locale] if !force_download && manifest_version?(locale, result['version'])
21
+
22
+ manifests[locale] = download_manifest_by_url(result.dig('mobileWorldContentPaths', locale), result['version'])
23
+ end
24
+
25
+ def download_manifest_by_url(url, version)
26
+ raise Restiny::RequestError, 'Unknown locale' if url.nil?
27
+
28
+ database_file_path = extract_manifest_from_zip_file(Down.download(BUNGIE_URL + url), version)
29
+
30
+ Restiny::Manifest.new(database_file_path, version)
31
+ rescue Down::Error => e
32
+ raise Restiny::NetworkError.new('Unable to download the manifest file', e.response.code)
33
+ end
34
+
35
+ def extract_manifest_from_zip_file(source_path, version)
36
+ Zip::File.open(source_path) do |zip_file|
37
+ File.join(Dir.tmpdir, "#{version}.en.content.db").tap do |path|
38
+ zip_file.first.extract(path) unless File.exist?(path)
39
+ end
40
+ end
41
+ rescue Zip::Error => e
42
+ raise Restiny::Error, "Unable to unzip the manifest file (#{e})"
43
+ end
44
+
45
+ def manifests
46
+ @manifests ||= {}
47
+ end
48
+
49
+ def manifest_version?(locale, version)
50
+ manifests[locale] && manifests[locale].version == version
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Restiny
6
+ module Api
7
+ module Membership
8
+ include Base
9
+
10
+ def get_user_memberships_by_id(membership_id:, membership_type: Platform::ALL)
11
+ raise Restiny::InvalidParamsError, 'Please provide a membership ID' if membership_id.nil?
12
+
13
+ api_get(endpoint: "User/GetMembershipsById/#{membership_id}/#{membership_type}/")
14
+ end
15
+
16
+ def get_primary_user_membership(membership_id:, use_fallback: true)
17
+ result = get_user_memberships_by_id(membership_id: membership_id)
18
+ return nil if result.nil? || result['primaryMembershipId'].nil?
19
+
20
+ result['destinyMemberships'].each do |membership|
21
+ return membership if membership['membershipID'] == result['primaryMembershipId']
22
+ end
23
+
24
+ result['destinyMemberships'][0] if use_fallback
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Restiny
6
+ module Api
7
+ module Profile
8
+ include Base
9
+
10
+ def get_profile(membership_id:, membership_type:, components:, type_url: nil)
11
+ if !components.is_a?(Array) || components.empty?
12
+ raise Restiny::InvalidParamsError, 'Please provide at least one component'
13
+ end
14
+
15
+ url = "Destiny2/#{membership_type}/Profile/#{membership_id}/"
16
+ url += type_url if type_url
17
+ url += "?components=#{components.join(',')}"
18
+
19
+ api_get(endpoint: url)
20
+ end
21
+
22
+ def get_character_profile(character_id:, membership_id:, membership_type:, components:)
23
+ get_profile(
24
+ membership_id: membership_id,
25
+ membership_type: membership_type,
26
+ components: components,
27
+ type_url: "Character/#{character_id}/"
28
+ )
29
+ end
30
+
31
+ def get_instanced_item_profile(item_id:, membership_id:, membership_type:, components:)
32
+ get_profile(
33
+ membership_id: membership_id,
34
+ membership_type: membership_type,
35
+ components: components,
36
+ type_url: "Item/#{item_id}/"
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Restiny
6
+ module Api
7
+ module Search
8
+ include Base
9
+
10
+ def search_player_by_bungie_name(name:, membership_type: Platform::ALL)
11
+ display_name, display_name_code = name.split('#')
12
+ if display_name.nil? || display_name_code.nil?
13
+ raise Restiny::InvalidParamsError,
14
+ 'You must provide a valid Bungie name'
15
+ end
16
+
17
+ api_post(
18
+ endpoint: "Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
19
+ params: {
20
+ displayName: display_name,
21
+ displayNameCode: display_name_code
22
+ }
23
+ )
24
+ end
25
+
26
+ def search_users_by_global_name(name:, page: 0)
27
+ api_post(endpoint: "User/Search/GlobalName/#{page}/", params: { displayNamePrefix: name })
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Restiny
6
+ module Api
7
+ module Stats
8
+ include Base
9
+
10
+ def get_post_game_carnage_report(activity_id:)
11
+ raise Restiny::InvalidParamsError, 'Please provide an activity ID' if activity_id.nil?
12
+
13
+ api_get(endpoint: "Destiny2/Stats/PostGameCarnageReport/#{activity_id}/")
14
+ end
15
+
16
+ alias get_pgcr get_post_game_carnage_report
17
+ end
18
+ end
19
+ end
@@ -1,22 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restiny
4
+ # Definitions for the gaming platforms supported by Destiny 2.
4
5
  module Platform
5
6
  ALL = -1
6
7
  XBOX = 1
7
8
  PSN = 2
8
9
  STEAM = 3
9
10
  EPIC = 6
11
+
12
+ def self.names
13
+ {
14
+ ALL => 'All',
15
+ XBOX => 'Xbox',
16
+ PSN => 'PSN',
17
+ STEAM => 'Steam',
18
+ EPIC => 'Epic'
19
+ }
20
+ end
10
21
  end
11
22
 
12
23
  module ItemLocation
24
+ # Definitions for the possible locations an item can be related to a character.
13
25
  UNKNOWN = 0
14
26
  INVENTORY = 1
15
27
  VAULT = 2
16
28
  VENDOR = 3
17
29
  POSTMASTER = 4
30
+
31
+ def self.names
32
+ {
33
+ UNKNOWN => 'Unknown',
34
+ INVENTORY => 'Inventory',
35
+ VAULT => 'Vault',
36
+ VENDOR => 'Vendor',
37
+ POSTMASTER => 'Postmaster'
38
+ }
39
+ end
18
40
  end
19
41
 
42
+ # Definitions for the tier/rarity of a particular item.
20
43
  module TierType
21
44
  UNKNOWN = 0
22
45
  CURRENCY = 1
@@ -25,36 +48,89 @@ module Restiny
25
48
  RARE = 4
26
49
  SUPERIOR = 5
27
50
  EXOTIC = 6
51
+
52
+ def self.names
53
+ {
54
+ UNKNOWN => 'Unknown',
55
+ CURRENCY => 'Currency',
56
+ BASIC => 'Basic',
57
+ COMMON => 'Common',
58
+ RARE => 'Rare',
59
+ SUPERIOR => 'Superior',
60
+ EXOTIC => 'Exotic'
61
+ }
62
+ end
28
63
  end
29
64
 
65
+ # Definitions for a Guardian's class.
30
66
  module GuardianClass
31
67
  TITAN = 0
32
68
  HUNTER = 1
33
69
  WARLOCK = 2
34
70
  UNKNOWN = 3
71
+
72
+ def self.names
73
+ {
74
+ TITAN => 'Titan',
75
+ HUNTER => 'Hunter',
76
+ WARLOCK => 'Warlock',
77
+ UNKNOWN => 'Unknown'
78
+ }
79
+ end
35
80
  end
36
81
 
82
+ # Definitions for a Guardian's race.
37
83
  module Race
38
84
  HUMAN = 0
39
85
  AWOKEN = 1
40
86
  EXO = 2
41
87
  UNKNOWN = 3
88
+
89
+ def self.names
90
+ {
91
+ HUMAN => 'Human',
92
+ AWOKEN => 'Awoken',
93
+ EXO => 'Exo',
94
+ UNKNOWN => 'Unknown'
95
+ }
96
+ end
42
97
  end
43
98
 
99
+ # Definitions for a Guardian's gender.
44
100
  module Gender
45
101
  MASCULINE = 0
46
102
  FEMININE = 1
47
103
  UNKNOWN = 2
104
+
105
+ def self.names
106
+ {
107
+ MASCULINE => 'Masculine',
108
+ FEMININE => 'Feminine',
109
+ UNKNOWN => 'Unknown'
110
+ }
111
+ end
48
112
  end
49
113
 
114
+ # Definitions for the various typos of ammunition in the game.
50
115
  module Ammunition
51
116
  NONE = 0
52
117
  PRIMARY = 1
53
118
  SPECIAL = 2
54
119
  HEAVY = 3
55
120
  UNKNOWN = 4
121
+
122
+ def self.names
123
+ {
124
+ NONE => 'None',
125
+ PRIMARY => 'Primary',
126
+ SPECIAL => 'Special',
127
+ HEAVY => 'Heavy',
128
+ UNKNOWN => 'Unknown'
129
+ }
130
+ end
56
131
  end
57
132
 
133
+ # Definitions for the various component types used when requesting a profile entry.
58
134
  module ComponentType
59
135
  CHARACTERS = 'Characters'
60
136
  CHARACTER_EQUIPMENT = 'CharacterEquipment'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restiny
4
- VERSION = '5.0.0'
4
+ VERSION = '5.0.1'
5
5
  end
data/lib/restiny.rb CHANGED
@@ -3,198 +3,21 @@
3
3
  $LOAD_PATH.unshift(__dir__)
4
4
 
5
5
  require 'restiny/version'
6
- require 'restiny/constants'
7
- require 'restiny/errors'
8
- require 'restiny/manifest'
9
-
10
- require 'down'
11
- require 'faraday'
12
- require 'faraday/follow_redirects'
13
- require 'faraday/destiny/api'
14
- require 'faraday/destiny/auth'
15
- require 'json'
16
- require 'securerandom'
17
- require 'tmpdir'
18
- require 'zip'
19
-
6
+ require 'restiny/api/authentication'
7
+ require 'restiny/api/manifest'
8
+ require 'restiny/api/membership'
9
+ require 'restiny/api/profile'
10
+ require 'restiny/api/search'
11
+ require 'restiny/api/stats'
12
+
13
+ # The main Restiny module.
20
14
  module Restiny
21
15
  extend self
22
16
 
23
- BUNGIE_URL = 'https://www.bungie.net'
24
- API_BASE_URL = "#{BUNGIE_URL}/platform".freeze
25
-
26
- attr_accessor :api_key, :oauth_state, :oauth_client_id, :access_token, :user_agent
27
-
28
- # OAuth methods
29
-
30
- def get_authorise_url(redirect_url: nil, state: nil)
31
- check_oauth_client_id
32
-
33
- @oauth_state = state || SecureRandom.hex(15)
34
-
35
- params = { response_type: 'code', client_id: @oauth_client_id, state: @oauth_state }
36
- params['redirect_url'] = redirect_url unless redirect_url.nil?
37
-
38
- auth_connection.build_url("#{BUNGIE_URL}/en/oauth/authorize/", params).to_s
39
- end
40
-
41
- def request_access_token(code:, redirect_url: nil)
42
- check_oauth_client_id
43
-
44
- params = { code: code, grant_type: 'authorization_code', client_id: @oauth_client_id }
45
- params['redirect_url'] = redirect_url unless redirect_url.nil?
46
-
47
- auth_connection.post('app/oauth/token/', params).body
48
- end
49
-
50
- # Manifest methods
51
-
52
- def download_manifest(locale: 'en', force_download: false)
53
- result = api_get('Destiny2/Manifest/')
54
- raise Restiny::ResponseError, 'Unable to determine manifest details' if result.nil?
55
-
56
- live_version = result['version']
57
-
58
- @manifests ||= {}
59
- @manifest_versions ||= {}
60
-
61
- if force_download || @manifests[locale].nil? || @manifest_versions[locale] != live_version
62
- manifest_db_url = result.dig('mobileWorldContentPaths', locale)
63
- raise Restiny::RequestError, 'Unknown locale' if manifest_db_url.nil?
64
-
65
- database_file_path = Zip::File.open(Down.download(BUNGIE_URL + manifest_db_url)) do |zip_file|
66
- File.join(Dir.tmpdir, "#{live_version}.en.content.db").tap do |path|
67
- zip_file.first.extract(path) unless File.exist?(path)
68
- end
69
- end
70
-
71
- @manifests[locale] = Manifest.new(database_file_path, live_version)
72
- @manifest_versions[locale] = live_version
73
- end
74
-
75
- @manifests[locale]
76
- rescue Down::Error => e
77
- raise Restiny::NetworkError.new('Unable to download the manifest file', e.response.code)
78
- rescue Zip::Error => e
79
- raise Restiny::Error, "Unable to unzip the manifest file (#{e})"
80
- end
81
-
82
- # Profile and related methods
83
-
84
- def get_profile(membership_id:, membership_type:, components:, type_url: nil)
85
- if !components.is_a?(Array) || components.empty?
86
- raise Restiny::InvalidParamsError, 'Please provide at least one component'
87
- end
88
-
89
- url = "Destiny2/#{membership_type}/Profile/#{membership_id}/"
90
- url += type_url if type_url
91
- url += "?components=#{components.join(',')}"
92
-
93
- api_get(url)
94
- end
95
-
96
- def get_character_profile(character_id:, membership_id:, membership_type:, components:)
97
- get_profile(
98
- membership_id: membership_id,
99
- membership_type: membership_type,
100
- components: components,
101
- type_url: "Character/#{character_id}/"
102
- )
103
- end
104
-
105
- def get_instanced_item_profile(item_id:, membership_id:, membership_type:, components:)
106
- get_profile(
107
- membership_id: membership_id,
108
- membership_type: membership_type,
109
- components: components,
110
- type_url: "Item/#{item_id}/"
111
- )
112
- end
113
-
114
- # User methods.
115
-
116
- def get_user_memberships_by_id(membership_id, membership_type: Platform::ALL)
117
- raise Restiny::InvalidParamsError, 'Please provide a membership ID' if membership_id.nil?
118
-
119
- api_get("User/GetMembershipsById/#{membership_id}/#{membership_type}/")
120
- end
121
-
122
- def get_user_primary_membership(parent_membership_id, use_fallback: true)
123
- result = get_user_memberships_by_id(parent_membership_id)
124
- return nil if result.nil? || result['primaryMembershipId'].nil?
125
-
126
- result['destinyMemberships'].each do |membership|
127
- return membership if membership['membershipID'] == result['primaryMembershipId']
128
- end
129
-
130
- result['destinyMemberships'][0] if use_fallback
131
- end
132
-
133
- def search_player_by_bungie_name(name, membership_type: Platform::ALL)
134
- display_name, display_name_code = name.split('#')
135
- if display_name.nil? || display_name_code.nil?
136
- raise Restiny::InvalidParamsError, 'You must provide a valid Bungie name'
137
- end
138
-
139
- api_post(
140
- "Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
141
- params: {
142
- displayName: display_name,
143
- displayNameCode: display_name_code
144
- }
145
- )
146
- end
147
-
148
- def search_users_by_global_name(name, page: 0)
149
- api_post("User/Search/GlobalName/#{page}/", params: { displayNamePrefix: name })
150
- end
151
-
152
- # General request methods
153
-
154
- def api_get(url, params: {})
155
- api_connection.get(url, params, token_header).body
156
- end
157
-
158
- def api_post(url, params: {})
159
- api_connection.post(url, params, token_header).body
160
- end
161
-
162
- private
163
-
164
- def check_oauth_client_id
165
- raise Restiny::RequestError, 'You need to set an OAuth client ID' unless @oauth_client_id
166
- end
167
-
168
- def default_headers
169
- { 'User-Agent': @user_agent || "restiny v#{Restiny::VERSION}" }
170
- end
171
-
172
- def api_connection
173
- raise Restiny::InvalidParamsError, 'You need to set an API key' unless @api_key
174
-
175
- @connection ||=
176
- Faraday.new(
177
- url: API_BASE_URL,
178
- headers: default_headers.merge('X-API-KEY': @api_key)
179
- ) do |faraday|
180
- faraday.request :json
181
- faraday.response :follow_redirects
182
- faraday.response :destiny_api
183
- faraday.response :json
184
- end
185
- end
186
-
187
- def auth_connection
188
- @auth_connection ||=
189
- Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
190
- faraday.request :url_encoded
191
- faraday.response :follow_redirects
192
- faraday.response :destiny_auth
193
- faraday.response :json
194
- end
195
- end
196
-
197
- def token_header
198
- {}.tap { |headers| headers['authorization'] = "Bearer #{@access_token}" if @access_token }
199
- end
17
+ include Api::Authentication
18
+ include Api::Manifest
19
+ include Api::Membership
20
+ include Api::Profile
21
+ include Api::Search
22
+ include Api::Stats
200
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restiny
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Bogan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-04 00:00:00.000000000 Z
11
+ date: 2023-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -90,6 +90,13 @@ files:
90
90
  - lib/faraday/destiny/api.rb
91
91
  - lib/faraday/destiny/auth.rb
92
92
  - lib/restiny.rb
93
+ - lib/restiny/api/authentication.rb
94
+ - lib/restiny/api/base.rb
95
+ - lib/restiny/api/manifest.rb
96
+ - lib/restiny/api/membership.rb
97
+ - lib/restiny/api/profile.rb
98
+ - lib/restiny/api/search.rb
99
+ - lib/restiny/api/stats.rb
93
100
  - lib/restiny/constants.rb
94
101
  - lib/restiny/errors.rb
95
102
  - lib/restiny/manifest.rb