restiny 3.1.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: b1d08e2f02105ea2a6330679fe9cae0534bbb4a7d3c0fb7803d43fc70ae4ebdd
4
- data.tar.gz: 80ab03d4fdbbf65ae276265f89e7c00d9a4f55870fdc0398893e45d429f44179
3
+ metadata.gz: 19b9e9cb2183fde785169d457628f4f7517c74feed228aacee4d45cdfe901fff
4
+ data.tar.gz: bebc650be6668d5a46a284a2aeb6912f2309cce7a7100ec4de57d3a95b11805e
5
5
  SHA512:
6
- metadata.gz: 4abf818ba76d6f6fefbf2070a1d56162462ac2d5a22dae7725383c34ae884d901be3539b78342510beaa93ddc9c3a554e90fcee473825bdcad144ab9a88af8c5
7
- data.tar.gz: 78f09f843a2e5c00067b95e1e5aad5a938d181c56081b744e1ae0638605af65548de6d4b117d3667e326bc9ed2ef61aaa80b673990f15cc490045aa2aa9f95ca
6
+ metadata.gz: c4fbe785ad70936712f351e1f3453f1915220af599ea9d89a09dc3c67713b208c6232e12d52d20fb25153c6a9b04b706b4bf1b367a2c8ded96a79da10f9764af
7
+ data.tar.gz: ed736dce127c0c76cebec7051b89ba02505452a4f9d2aa6bce6beb836b8fc6169586a6970fea1db3387db7700e3cee2b032edcb9952abd7ba9f85940e1a320e4
@@ -1,21 +1,23 @@
1
- require "faraday"
2
- require "restiny/errors"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'restiny/errors'
3
5
 
4
6
  module Faraday
5
7
  module Restiny
6
- Faraday::Response.register_middleware(destiny_api: "Faraday::Restiny::Api")
8
+ Faraday::Response.register_middleware(destiny_api: 'Faraday::Restiny::Api')
7
9
 
8
10
  class Api < Middleware
9
11
  def on_complete(env)
10
- return if env["response_body"].empty? || !env["response_body"].dig("ErrorCode")
12
+ return if env['response_body'].empty? || !env['response_body']['ErrorCode']
11
13
 
12
- if env["response_body"]["ErrorCode"] == 1
13
- env[:body] = env["response_body"].dig("Response")
14
+ if env['response_body']['ErrorCode'] == 1
15
+ env[:body] = env['response_body']['Response']
14
16
  return
15
17
  end
16
18
 
17
19
  klass =
18
- case env["status"]
20
+ case env['status']
19
21
  when 400..499
20
22
  ::Restiny::RequestError
21
23
  when 500..599
@@ -24,7 +26,7 @@ module Faraday
24
26
  ::Restiny::Error
25
27
  end
26
28
 
27
- raise klass.new(env["response_body"]["Message"], env["response_body"]["ErrorStatus"])
29
+ raise klass.new(env['response_body']['Message'], env['response_body']['ErrorStatus'])
28
30
  end
29
31
  end
30
32
  end
@@ -1,20 +1,22 @@
1
- require "faraday"
2
- require "restiny/errors"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'restiny/errors'
3
5
 
4
6
  module Faraday
5
7
  module Restiny
6
- Faraday::Response.register_middleware(destiny_auth: "Faraday::Restiny::Auth")
8
+ Faraday::Response.register_middleware(destiny_auth: 'Faraday::Restiny::Auth')
7
9
 
8
10
  class Auth < Middleware
9
11
  def on_complete(env)
10
- return if env["response_body"].empty? || env["url"].to_s !~ /oauth/
12
+ return if env['response_body'].empty? || env['url'].to_s !~ /oauth/
13
+
14
+ return unless env['response_body']['error']
11
15
 
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
16
+ raise ::Restiny::AuthenticationError.new(
17
+ env['response_body']['error_description'],
18
+ env['response_body']['error']
19
+ )
18
20
  end
19
21
  end
20
22
  end
@@ -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,47 +48,100 @@ 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
- 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"
135
+ CHARACTERS = 'Characters'
136
+ CHARACTER_EQUIPMENT = 'CharacterEquipment'
137
+ CHARACTER_INVENTORIES = 'CharacterInventories'
138
+ CHARACTER_LOADOUTS = 'CharacterLoadouts'
139
+ PROFILES = 'Profiles'
140
+ PROFILE_INVENTORIES = 'ProfileInventories'
141
+ ITEM_INSTANCES = 'ItemInstances'
142
+ ITEM_SOCKETS = 'ItemSockets'
143
+ ITEM_COMMON_DATA = 'ItemCommonData'
144
+ ITEM_PLUG_STATES = 'ItemPlugStates'
145
+ ITEM_REUSABLE_PLUGS = 'ItemReusablePlugs'
70
146
  end
71
147
  end
@@ -1,6 +1,6 @@
1
- # frozen_string/literal: true
1
+ # frozen_string_literal: true
2
2
 
3
- require "sqlite3"
3
+ require 'sqlite3'
4
4
 
5
5
  module Restiny
6
6
  class Manifest
@@ -68,49 +68,36 @@ module Restiny
68
68
  Unlock: %w[unlock unlocks],
69
69
  Vendor: %w[vendor vendors],
70
70
  VendorGroup: %w[vendor_group vendor_groups]
71
- }
71
+ }.freeze
72
72
 
73
- attr_reader :file_path, :version
73
+ attr_reader :version
74
74
 
75
75
  ENTITIES.each do |entity, method_names|
76
76
  full_table_name = "Destiny#{entity}Definition"
77
77
  single_method_name, plural_method_name = method_names
78
78
 
79
- define_method single_method_name do |id|
80
- fetch_item(table_name: full_table_name, id: id)
81
- end
82
-
83
- define_method plural_method_name do |limit: nil|
84
- fetch_items(table_name: full_table_name, limit: limit)
85
- end
79
+ define_method(single_method_name) { |id| fetch_item(table_name: full_table_name, id: id) }
80
+ define_method(plural_method_name) { |limit: nil| fetch_items(table_name: full_table_name, limit: limit) }
86
81
  end
87
82
 
88
83
  def initialize(file_path, version)
89
84
  if file_path.empty? || !File.exist?(file_path) || !File.file?(file_path)
90
- raise Restiny::InvalidParamsError.new("You must provide a valid path for the manifest file")
85
+ raise Restiny::InvalidParamsError, 'You must provide a valid path for the manifest file'
91
86
  end
92
87
 
93
88
  @database = SQLite3::Database.new(file_path, results_as_hash: true)
94
- @file_path = file_path
95
89
  @version = version
96
90
  end
97
91
 
98
92
  private
99
93
 
100
- def get_entity_names
101
- query = "SELECT name from sqlite_schema WHERE name LIKE 'Destiny%'"
102
- @database.execute(query).map { |row| row["name"].gsub(/(Destiny|Definition)/, "") }
103
- end
104
-
105
94
  def fetch_item(table_name:, id:)
106
95
  query = "SELECT json FROM #{table_name} WHERE json_extract(json, '$.hash')=?"
107
96
  result = @database.execute(query, id)
108
97
 
109
- return nil if result.nil? || result.count < 1 || !result[0].include?("json")
110
-
111
- JSON.parse(result[0]["json"])
98
+ JSON.parse(result[0]['json']) unless result.nil? || result.count < 1 || !result[0].include?('json')
112
99
  rescue SQLite3::Exception => e
113
- raise Restiny::RequestError.new("Error while fetching item (#{e})")
100
+ raise Restiny::RequestError, "Error while fetching item (#{e})"
114
101
  end
115
102
 
116
103
  def fetch_items(table_name:, limit: nil)
@@ -119,14 +106,14 @@ module Restiny
119
106
  query = "SELECT json FROM #{table_name} ORDER BY json_extract(json, '$.index')"
120
107
 
121
108
  if limit
122
- query << " LIMIT ?"
109
+ query << ' LIMIT ?'
123
110
  bindings << limit
124
111
  end
125
112
 
126
113
  items = []
127
114
 
128
115
  @database.execute(query, bindings) do |row|
129
- item = JSON.parse(row["json"])
116
+ item = JSON.parse(row['json'])
130
117
  yield item if block_given?
131
118
 
132
119
  items << item
@@ -134,7 +121,7 @@ module Restiny
134
121
 
135
122
  items unless block_given?
136
123
  rescue SQLite3::Exception => e
137
- raise Restiny::RequestError.new("Error while fetching items (#{e})")
124
+ raise Restiny::RequestError, "Error while fetching items (#{e})"
138
125
  end
139
126
  end
140
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restiny
4
- VERSION = "3.1.0"
4
+ VERSION = '5.0.1'
5
5
  end
data/lib/restiny.rb CHANGED
@@ -2,202 +2,22 @@
2
2
 
3
3
  $LOAD_PATH.unshift(__dir__)
4
4
 
5
- require "restiny/version"
6
- require "restiny/constants"
7
- require "restiny/errors"
8
- require "restiny/manifest"
9
-
10
- require "faraday"
11
- require "faraday/follow_redirects"
12
- require "faraday/destiny/api"
13
- require "faraday/destiny/auth"
14
-
15
- require "down"
16
- require "json"
17
- require "securerandom"
18
- require "zip"
19
-
5
+ require 'restiny/version'
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"
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_post("app/oauth/token/", params)
48
- end
49
-
50
- # Manifest methods
51
-
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
- @manifests ||= {}
59
- @manifest_versions ||= {}
60
-
61
- if force_download || @manifests[locale].nil? || @manifest_versions[locale] != live_version
62
- url = BUNGIE_URL + result.dig("mobileWorldContentPaths", locale)
63
-
64
- zipped_file = Down.download(url)
65
- database_file_path = zipped_file.path + ".db"
66
-
67
- Zip::File.open(zipped_file) { |file| file.first.extract(database_file_path) }
68
-
69
- @manifests[locale] = Manifest.new(database_file_path, live_version)
70
- @manifest_versions[locale] = live_version
71
- end
72
-
73
- @manifests[locale]
74
- rescue Down::Error => error
75
- raise Restiny::NetworkError.new("Unable to download the manifest file", error.response.code)
76
- rescue Zip::Error => error
77
- raise Restiny::Error.new("Unable to unzip the manifest file (#{error})")
78
- end
79
-
80
- # Profile and related methods
81
-
82
- def get_profile(membership_id:, membership_type:, components:, type_url: nil)
83
- if !components.is_a?(Array) || components.empty?
84
- raise Restiny::InvalidParamsError.new("Please provide at least one component")
85
- end
86
-
87
- url = "Destiny2/#{membership_type}/Profile/#{membership_id}/"
88
- url += type_url if type_url
89
- url += "?components=#{components.join(",")}"
90
-
91
- api_get(url)
92
- end
93
-
94
- def get_character_profile(character_id:, membership_id:, membership_type:, components:)
95
- get_profile(
96
- membership_id: membership_id,
97
- membership_type: membership_type,
98
- components: components,
99
- type_url: "Character/#{character_id}/"
100
- )
101
- end
102
-
103
- def get_instanced_item_profile(item_id:, membership_id:, membership_type:, components:)
104
- get_profile(
105
- membership_id: membership_id,
106
- membership_type: membership_type,
107
- components: components,
108
- type_url: "Item/#{item_id}/"
109
- )
110
- end
111
-
112
- # User methods.
113
-
114
- def get_user_memberships_by_id(membership_id, membership_type: Platform::ALL)
115
- raise Restiny::InvalidParamsError.new("Please provide a membership ID") if membership_id.nil?
116
- api_get("User/GetMembershipsById/#{membership_id}/#{membership_type}/")
117
- end
118
-
119
- def get_primary_membership(parent_membership_id, use_fallback: true)
120
- result = get_user_memberships_by_id(parent_membership_id)
121
- return nil if result.nil? || result["primaryMembershipId"].nil?
122
-
123
- result["destinyMemberships"].each do |membership|
124
- return membership if membership["membershipID"] == result["primaryMembershipId"]
125
- end
126
-
127
- return result["destinyMemberships"][0] if use_fallback
128
- end
129
-
130
- def search_player_by_bungie_name(name, membership_type: Platform::ALL)
131
- display_name, display_name_code = name.split("#")
132
- if display_name.nil? || display_name_code.nil?
133
- raise Restiny::InvalidParamsError.new("You must provide a valid Bungie name")
134
- end
135
-
136
- api_post(
137
- "Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
138
- params: {
139
- displayName: display_name,
140
- displayNameCode: display_name_code
141
- }
142
- )
143
- end
144
-
145
- def search_users_by_global_name(name:, page: 0)
146
- api_post("User/Search/GlobalName/#{page}/", params: { displayNamePrefix: name })
147
- end
148
-
149
- # General request methods
150
-
151
- def api_get(url, params: {})
152
- api_connection.get(url, params, token_header).body
153
- end
154
-
155
- def api_post(url, params: {})
156
- api_connection.post(url, params, token_header).body
157
- end
158
-
159
- def auth_post(url, params)
160
- auth_connection.post(url, params, "Content-Type" => "application/x-www-form-urlencoded").body
161
- end
162
-
163
- private
164
-
165
- def check_oauth_client_id
166
- raise Restiny::RequestError.new("You need to set an OAuth client ID") unless @oauth_client_id
167
- end
168
-
169
- def default_headers
170
- { "User-Agent": @user_agent || "restiny v#{Restiny::VERSION}" }
171
- end
172
-
173
- def api_connection
174
- raise Restiny::InvalidParamsError.new("You need to set an API key") unless @api_key
175
-
176
- @connection ||=
177
- Faraday.new(
178
- url: API_BASE_URL,
179
- headers: default_headers.merge("X-API-KEY": @api_key)
180
- ) do |faraday|
181
- faraday.request :url_encoded
182
- faraday.request :json
183
- faraday.response :follow_redirects
184
- faraday.response :destiny_api
185
- faraday.response :json
186
- end
187
- end
188
-
189
- def auth_connection
190
- @auth_connection ||=
191
- Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
192
- faraday.request :url_encoded
193
- faraday.request :json
194
- faraday.response :follow_redirects
195
- faraday.response :destiny_auth
196
- faraday.response :json
197
- end
198
- end
199
-
200
- def token_header
201
- {}.tap { |headers| headers["authorization"] = "Bearer #{@oauth_token}" if @oauth_token }
202
- 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
203
23
  end
metadata CHANGED
@@ -1,71 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restiny
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Bogan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-18 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
- name: faraday
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.0'
27
- - !ruby/object:Gem::Dependency
28
- name: faraday-follow_redirects
14
+ name: down
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '0.3'
19
+ version: '5.4'
34
20
  type: :runtime
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '0.3'
26
+ version: '5.4'
41
27
  - !ruby/object:Gem::Dependency
42
- name: sqlite3
28
+ name: faraday
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '1.3'
33
+ version: '2.0'
48
34
  type: :runtime
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '1.3'
40
+ version: '2.0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: down
42
+ name: faraday-follow_redirects
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '5.4'
47
+ version: '0.3'
62
48
  type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '5.4'
54
+ version: '0.3'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rubyzip
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -81,75 +67,19 @@ dependencies:
81
67
  - !ruby/object:Gem::Version
82
68
  version: '2.3'
83
69
  - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '13.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '13.0'
97
- - !ruby/object:Gem::Dependency
98
- name: rspec
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '3.12'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '3.12'
111
- - !ruby/object:Gem::Dependency
112
- name: rubocop-rspec
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '2.22'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '2.22'
125
- - !ruby/object:Gem::Dependency
126
- name: standard
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '1.28'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '1.28'
139
- - !ruby/object:Gem::Dependency
140
- name: vcr
70
+ name: sqlite3
141
71
  requirement: !ruby/object:Gem::Requirement
142
72
  requirements:
143
73
  - - "~>"
144
74
  - !ruby/object:Gem::Version
145
- version: '6.1'
146
- type: :development
75
+ version: '1.3'
76
+ type: :runtime
147
77
  prerelease: false
148
78
  version_requirements: !ruby/object:Gem::Requirement
149
79
  requirements:
150
80
  - - "~>"
151
81
  - !ruby/object:Gem::Version
152
- version: '6.1'
82
+ version: '1.3'
153
83
  description: A gem for interacting with Bungie's Destiny API.
154
84
  email:
155
85
  - d+restiny@waferbaby.com
@@ -160,6 +90,13 @@ files:
160
90
  - lib/faraday/destiny/api.rb
161
91
  - lib/faraday/destiny/auth.rb
162
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
163
100
  - lib/restiny/constants.rb
164
101
  - lib/restiny/errors.rb
165
102
  - lib/restiny/manifest.rb
@@ -167,24 +104,25 @@ files:
167
104
  homepage: http://github.com/waferbaby/restiny
168
105
  licenses:
169
106
  - MIT
170
- metadata: {}
171
- post_install_message:
107
+ metadata:
108
+ rubygems_mfa_required: 'true'
109
+ post_install_message:
172
110
  rdoc_options: []
173
111
  require_paths:
174
112
  - lib
175
113
  required_ruby_version: !ruby/object:Gem::Requirement
176
114
  requirements:
177
- - - ">="
115
+ - - ">"
178
116
  - !ruby/object:Gem::Version
179
- version: '0'
117
+ version: '3.0'
180
118
  required_rubygems_version: !ruby/object:Gem::Requirement
181
119
  requirements:
182
120
  - - ">="
183
121
  - !ruby/object:Gem::Version
184
122
  version: '0'
185
123
  requirements: []
186
- rubygems_version: 3.3.7
187
- signing_key:
124
+ rubygems_version: 3.2.33
125
+ signing_key:
188
126
  specification_version: 4
189
127
  summary: A Destiny API gem
190
128
  test_files: []