restiny 2.0.0 → 3.0.0

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: b9250ce97e7698687ee6cc64362dc0ff0cfe222115a544f60290dbccbfa4255b
4
- data.tar.gz: a33846e9b1a017a1a357f0ea51aeaa74f71d296d02f61f1a1da68a89bcf90498
3
+ metadata.gz: 64bba6028ea7ef4119e6019011b4d35cf1fb850903b446387b3aae0ba47c03e7
4
+ data.tar.gz: 629969c98d6d518c3cefb7f9c85d177a404408f6d88dd8d979be5e2be75c1027
5
5
  SHA512:
6
- metadata.gz: 06d7bfcdd5a4263a4ac1db64ab4c55bf6986378fe5fa1a592616a2d03662f15f3cce91398b40a2fe3a03e2950c9eb8005bc64202980d1c4d41c4be54c82014d7
7
- data.tar.gz: 7ec6297618ebaa28d3ef6f1ba5b96761a7beb04911201c2f80dd63aa671b48a072d0df9d4e26525ec962379245cbed3ff2d404b8d78efc541233eb0d9d8e12a4
6
+ metadata.gz: 65a76e2738de6da2e4e3dfaf6015f689d8c95d8fea9daa952d153fdf251feec6a145fa16b627bfd2baea1eb3c05cc7d83058e1670b1715d9ea21c18409f081c5
7
+ data.tar.gz: 70565f0ddc5c22d76f32cb1e4883261e3bbf181bd7e330a125a3c6982db1b95f6e14342aae7600379ded28697eef9f369ce4b50896b6388de984eea520aec971
@@ -7,11 +7,10 @@ module Faraday
7
7
 
8
8
  class Api < Middleware
9
9
  def on_complete(env)
10
- return if env["response_body"].empty?
10
+ return if env["response_body"].empty? || !env["response_body"].dig("ErrorCode")
11
11
 
12
- payload = JSON.parse(env["response_body"])
13
- if payload["ErrorCode"] == 1
14
- env[:body] = payload.dig("Response")
12
+ if env["response_body"]["ErrorCode"] == 1
13
+ env[:body] = env["response_body"].dig("Response")
15
14
  return
16
15
  end
17
16
 
@@ -25,9 +24,7 @@ module Faraday
25
24
  ::Restiny::Error
26
25
  end
27
26
 
28
- raise klass.new(payload["Message"], payload["ErrorStatus"])
29
- rescue JSON::ParserError
30
- raise ::Restiny::ResponseError.new("Unable to parse API response")
27
+ raise klass.new(env["response_body"]["Message"], env["response_body"]["ErrorStatus"])
31
28
  end
32
29
  end
33
30
  end
@@ -0,0 +1,21 @@
1
+ require "faraday"
2
+ require "restiny/errors"
3
+
4
+ module Faraday
5
+ module Restiny
6
+ Faraday::Response.register_middleware(destiny_auth: "Faraday::Restiny::Auth")
7
+
8
+ class Auth < Middleware
9
+ def on_complete(env)
10
+ return if env["response_body"].empty? || env["url"].to_s !~ /oauth/
11
+
12
+ if env["response_body"]["error"]
13
+ raise ::Restiny::AuthenticationError.new(
14
+ env["response_body"]["error_description"],
15
+ env["response_body"]["error"]
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -9,20 +9,6 @@ module Restiny
9
9
  EPIC = 6
10
10
  end
11
11
 
12
- module ComponentType
13
- CHARACTERS = "Characters"
14
- CHARACTER_EQUIPMENT = "CharacterEquipment"
15
- CHARACTER_INVENTORIES = "CharacterInventories"
16
- CHARACTER_LOADOUTS = "CharacterLoadouts"
17
- PROFILES = "Profiles"
18
- PROFILE_INVENTORIES = "ProfileInventories"
19
- ITEM_INSTANCES = "ItemInstances"
20
- ITEM_SOCKETS = "ItemSockets"
21
- ITEM_COMMON_DATA = "ItemCommonData"
22
- ITEM_PLUG_STATES = "ItemPlugStates"
23
- ITEM_REUSABLE_PLUGS = "ItemReusablePlugs"
24
- end
25
-
26
12
  module ItemLocation
27
13
  UNKNOWN = 0
28
14
  INVENTORY = 1
@@ -41,7 +27,7 @@ module Restiny
41
27
  EXOTIC = 6
42
28
  end
43
29
 
44
- module Class
30
+ module GuardianClass
45
31
  TITAN = 0
46
32
  HUNTER = 1
47
33
  WARLOCK = 2
@@ -68,4 +54,18 @@ module Restiny
68
54
  HEAVY = 3
69
55
  UNKNOWN = 4
70
56
  end
57
+
58
+ module ComponentType
59
+ CHARACTERS = "Characters"
60
+ CHARACTER_EQUIPMENT = "CharacterEquipment"
61
+ CHARACTER_INVENTORIES = "CharacterInventories"
62
+ CHARACTER_LOADOUTS = "CharacterLoadouts"
63
+ PROFILES = "Profiles"
64
+ PROFILE_INVENTORIES = "ProfileInventories"
65
+ ITEM_INSTANCES = "ItemInstances"
66
+ ITEM_SOCKETS = "ItemSockets"
67
+ ITEM_COMMON_DATA = "ItemCommonData"
68
+ ITEM_PLUG_STATES = "ItemPlugStates"
69
+ ITEM_REUSABLE_PLUGS = "ItemReusablePlugs"
70
+ end
71
71
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string/literal: true
2
- require "down"
3
- require "json"
2
+
4
3
  require "sqlite3"
5
- require "zip"
6
4
 
7
5
  module Restiny
8
6
  class Manifest
@@ -72,7 +70,7 @@ module Restiny
72
70
  VendorGroup: %w[vendor_group vendor_groups]
73
71
  }
74
72
 
75
- attr_reader :file_path
73
+ attr_reader :file_path, :version
76
74
 
77
75
  ENTITIES.each do |entity, method_names|
78
76
  full_table_name = "Destiny#{entity}Definition"
@@ -87,26 +85,14 @@ module Restiny
87
85
  end
88
86
  end
89
87
 
90
- def self.download_by_url(url)
91
- zipped_file = Down.download(url)
92
- manifest_path = zipped_file.path + ".db"
93
-
94
- Zip::File.open(zipped_file) { |file| file.first.extract(manifest_path) }
95
-
96
- new(manifest_path)
97
- rescue Down::ResponseError => error
98
- raise Restiny::NetworkError.new("Unable to download the manifest file", error.response.code)
99
- rescue Zip::Error => error
100
- raise Restiny::Error.new("Unable to unzip the manifest file (#{error})")
101
- end
102
-
103
- def initialize(file_path)
88
+ def initialize(file_path, version)
104
89
  if file_path.empty? || !File.exist?(file_path) || !File.file?(file_path)
105
90
  raise Restiny::InvalidParamsError.new("You must provide a valid path for the manifest file")
106
91
  end
107
92
 
108
93
  @database = SQLite3::Database.new(file_path, results_as_hash: true)
109
94
  @file_path = file_path
95
+ @version = version
110
96
  end
111
97
 
112
98
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restiny
4
- VERSION = "2.0.0"
4
+ VERSION = "3.0.0"
5
5
  end
data/lib/restiny.rb CHANGED
@@ -10,7 +10,12 @@ require "restiny/manifest"
10
10
  require "faraday"
11
11
  require "faraday/follow_redirects"
12
12
  require "faraday/destiny/api"
13
+ require "faraday/destiny/auth"
14
+
15
+ require "down"
16
+ require "json"
13
17
  require "securerandom"
18
+ require "zip"
14
19
 
15
20
  module Restiny
16
21
  extend self
@@ -18,7 +23,7 @@ module Restiny
18
23
  BUNGIE_URL = "https://www.bungie.net"
19
24
  API_BASE_URL = BUNGIE_URL + "/platform"
20
25
 
21
- attr_accessor :api_key, :oauth_state, :oauth_client_id, :access_token, :refresh_token, :manifest
26
+ attr_accessor :api_key, :oauth_state, :oauth_client_id, :access_token
22
27
 
23
28
  # OAuth methods
24
29
 
@@ -28,36 +33,45 @@ module Restiny
28
33
  @oauth_state = state || SecureRandom.hex(15)
29
34
 
30
35
  params = { response_type: "code", client_id: @oauth_client_id, state: @oauth_state }
31
- params[:redirect_url] = redirect_url unless redirect_url.nil?
36
+ params["redirect_url"] = redirect_url unless redirect_url.nil?
32
37
 
33
- connection.build_url(BUNGIE_URL + "/en/oauth/authorize", params).to_s
38
+ auth_connection.build_url(BUNGIE_URL + "/en/oauth/authorize/", params).to_s
34
39
  end
35
40
 
36
41
  def request_access_token(code:, redirect_url: nil)
37
42
  check_oauth_client_id
38
43
 
39
44
  params = { code: code, grant_type: "authorization_code", client_id: @oauth_client_id }
40
- params[:redirect_url] = redirect_url unless redirect_url.nil?
45
+ params["redirect_url"] = redirect_url unless redirect_url.nil?
41
46
 
42
- connection.post(
43
- "app/oauth/token",
44
- params,
45
- "Content-Type" => "application/x-www-form-urlencoded"
46
- ).body
47
+ auth_post("app/oauth/token/", params)
47
48
  end
48
49
 
49
50
  # Manifest methods
50
51
 
51
- def get_manifest_url(locale: "en")
52
- result = connection.get("Destiny2/Manifest/").body.dig("mobileWorldContentPaths", locale)
53
- BUNGIE_URL + result
54
- end
52
+ def get_manifest(locale: "en", force_download: false)
53
+ result = api_get("Destiny2/Manifest/")
54
+ raise Restiny::ResponseError.new("Unable to determine manifest details") if result.nil?
55
+
56
+ live_version = result.dig("version")
57
+
58
+ if force_download || @manifest.nil? || @manifest_version != live_version
59
+ url = BUNGIE_URL + result.dig("mobileWorldContentPaths", locale)
60
+
61
+ zipped_file = Down.download(url)
62
+ database_file_path = zipped_file.path + ".db"
55
63
 
56
- def download_manifest(locale: "en")
57
- manifest_url = get_manifest_url
58
- raise Restiny::ResponseError.new("Unable to determine manifest URL") if manifest_url.nil?
64
+ Zip::File.open(zipped_file) { |file| file.first.extract(database_file_path) }
59
65
 
60
- Manifest.download_by_url(BUNGIE_URL + manifest_url)
66
+ @manifest = Manifest.new(database_file_path, live_version)
67
+ @manifest_version = live_version
68
+ end
69
+
70
+ @manifest
71
+ rescue Down::Error => e
72
+ raise Restiny::NetworkError.new("Unable to download the manifest file", error.response.code)
73
+ rescue Zip::Error => error
74
+ raise Restiny::Error.new("Unable to unzip the manifest file (#{error})")
61
75
  end
62
76
 
63
77
  # Profile and related methods
@@ -71,7 +85,7 @@ module Restiny
71
85
  url += type_url if type_url
72
86
  url += "?components=#{components.join(",")}"
73
87
 
74
- connection.get(url).body
88
+ api_get(url)
75
89
  end
76
90
 
77
91
  def get_character_profile(character_id:, membership_id:, membership_type:, components:)
@@ -79,7 +93,7 @@ module Restiny
79
93
  membership_id: membership_id,
80
94
  membership_type: membership_type,
81
95
  components: components,
82
- type_url: "Character/#{character_id}"
96
+ type_url: "Character/#{character_id}/"
83
97
  )
84
98
  end
85
99
 
@@ -88,7 +102,7 @@ module Restiny
88
102
  membership_id: membership_id,
89
103
  membership_type: membership_type,
90
104
  components: components,
91
- type_url: "Item/#{item_id}"
105
+ type_url: "Item/#{item_id}/"
92
106
  )
93
107
  end
94
108
 
@@ -96,8 +110,7 @@ module Restiny
96
110
 
97
111
  def get_user_memberships_by_id(membership_id, membership_type: Platform::ALL)
98
112
  raise Restiny::InvalidParamsError.new("Please provide a membership ID") if membership_id.nil?
99
-
100
- connection.get("User/GetMembershipsById/#{membership_id}/#{membership_type}/").body
113
+ api_get("User/GetMembershipsById/#{membership_id}/#{membership_type}/")
101
114
  end
102
115
 
103
116
  def search_player_by_bungie_name(name, membership_type: Platform::ALL)
@@ -106,19 +119,33 @@ module Restiny
106
119
  raise Restiny::InvalidParamsError.new("You must provide a valid Bungie name")
107
120
  end
108
121
 
109
- connection.post(
122
+ api_post(
110
123
  "Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
111
- displayName: display_name,
112
- displayNameCode: display_name_code
113
- ).body
124
+ params: {
125
+ displayName: display_name,
126
+ displayNameCode: display_name_code
127
+ }
128
+ )
114
129
  end
115
130
 
116
131
  def search_users_by_global_name(name:, page: 0)
117
- connection.post("User/Search/GlobalName/#{page}", displayNamePrefix: name).body
132
+ api_post("User/Search/GlobalName/#{page}/", params: { displayNamePrefix: name })
118
133
  end
119
134
 
120
135
  # General request methods
121
136
 
137
+ def api_get(url, params: {})
138
+ api_connection.get(url, params, token_header).body
139
+ end
140
+
141
+ def api_post(url, params: {})
142
+ api_connection.post(url, params, token_header).body
143
+ end
144
+
145
+ def auth_post(url, params)
146
+ auth_connection.post(url, params, "Content-Type" => "application/x-www-form-urlencoded").body
147
+ end
148
+
122
149
  private
123
150
 
124
151
  def check_oauth_client_id
@@ -126,24 +153,37 @@ module Restiny
126
153
  end
127
154
 
128
155
  def default_headers
129
- {
130
- "User-Agent": "restiny v#{Restiny::VERSION}",
131
- "X-API-KEY": @api_key,
132
- "Content-Type": "application/json"
133
- }
156
+ { "User-Agent": "restiny v#{Restiny::VERSION}" }
134
157
  end
135
158
 
136
- def connection
159
+ def api_connection
137
160
  raise Restiny::InvalidParamsError.new("You need to set an API key") unless @api_key
138
161
 
139
- headers = default_headers
140
- headers["authorization"] = "Bearer #{@oauth_token}" if @oauth_token
162
+ @connection ||=
163
+ Faraday.new(
164
+ url: API_BASE_URL,
165
+ headers: default_headers.merge("X-API-KEY": @api_key)
166
+ ) do |faraday|
167
+ faraday.request :url_encoded
168
+ faraday.request :json
169
+ faraday.response :follow_redirects
170
+ faraday.response :destiny_api
171
+ faraday.response :json
172
+ end
173
+ end
141
174
 
142
- Faraday.new(url: API_BASE_URL, headers: headers) do |faraday|
143
- faraday.request :url_encoded
144
- faraday.request :json
145
- faraday.response :follow_redirects
146
- faraday.response :destiny_api
147
- end
175
+ def auth_connection
176
+ @auth_connection ||=
177
+ Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
178
+ faraday.request :url_encoded
179
+ faraday.request :json
180
+ faraday.response :follow_redirects
181
+ faraday.response :destiny_auth
182
+ faraday.response :json
183
+ end
184
+ end
185
+
186
+ def token_header
187
+ {}.tap { |headers| headers["authorization"] = "Bearer #{@oauth_token}" if @oauth_token }
148
188
  end
149
189
  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: 2.0.0
4
+ version: 3.0.0
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-06-30 00:00:00.000000000 Z
11
+ date: 2023-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -158,6 +158,7 @@ extensions: []
158
158
  extra_rdoc_files: []
159
159
  files:
160
160
  - lib/faraday/destiny/api.rb
161
+ - lib/faraday/destiny/auth.rb
161
162
  - lib/restiny.rb
162
163
  - lib/restiny/constants.rb
163
164
  - lib/restiny/errors.rb