restiny 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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