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 +4 -4
- data/lib/faraday/destiny/api.rb +4 -7
- data/lib/faraday/destiny/auth.rb +21 -0
- data/lib/restiny/constants.rb +15 -15
- data/lib/restiny/manifest.rb +4 -18
- data/lib/restiny/version.rb +1 -1
- data/lib/restiny.rb +81 -41
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64bba6028ea7ef4119e6019011b4d35cf1fb850903b446387b3aae0ba47c03e7
|
4
|
+
data.tar.gz: 629969c98d6d518c3cefb7f9c85d177a404408f6d88dd8d979be5e2be75c1027
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65a76e2738de6da2e4e3dfaf6015f689d8c95d8fea9daa952d153fdf251feec6a145fa16b627bfd2baea1eb3c05cc7d83058e1670b1715d9ea21c18409f081c5
|
7
|
+
data.tar.gz: 70565f0ddc5c22d76f32cb1e4883261e3bbf181bd7e330a125a3c6982db1b95f6e14342aae7600379ded28697eef9f369ce4b50896b6388de984eea520aec971
|
data/lib/faraday/destiny/api.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
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(
|
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
|
data/lib/restiny/constants.rb
CHANGED
@@ -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
|
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
|
data/lib/restiny/manifest.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string/literal: true
|
2
|
-
|
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
|
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
|
data/lib/restiny/version.rb
CHANGED
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
|
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[
|
36
|
+
params["redirect_url"] = redirect_url unless redirect_url.nil?
|
32
37
|
|
33
|
-
|
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[
|
45
|
+
params["redirect_url"] = redirect_url unless redirect_url.nil?
|
41
46
|
|
42
|
-
|
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
|
52
|
-
result =
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
122
|
+
api_post(
|
110
123
|
"Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
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
|
159
|
+
def api_connection
|
137
160
|
raise Restiny::InvalidParamsError.new("You need to set an API key") unless @api_key
|
138
161
|
|
139
|
-
|
140
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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:
|
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-
|
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
|