restiny 0.5.0 → 0.6.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: b64ce91b4d100a18724e027008c5f22feaea7749e99ed3903f881cdf57dd514e
4
- data.tar.gz: 4494857bb6bcc684b92fa8215eebbaad2f1406d4236cb8be4e22f956eb98d8e6
3
+ metadata.gz: 865963597c492b37f7aedac0bcf200bf7e10cce108c1b14489cd21c2ccad4ece
4
+ data.tar.gz: fe4a007164b081c203c82082c169aa27e8e9764d5bfc6dfd1efe4fb66c05429e
5
5
  SHA512:
6
- metadata.gz: af7e65d052b3860b97fee42d8c38430e61aed8ab2e6e0435ff25ab74aa27433aebbf6e5267bd13b800d53559e9a6d99a3cc2e9e08972d1fbb8c5097d10bdc484
7
- data.tar.gz: 96d3dff6141def2d34948409525a9d66a80c3c4ed394c922fe44eb20b9026b59486eea93dec3962d94438fcb7ff641821ce091aeeebf3ef1bb9617569856de44
6
+ metadata.gz: 0ec8eb535c734d258806e3c990d175b7aa9a7706ff3cca36e5dca0d8f6647bc5bd4a0ef1fa97427b9fc83bca46445d70a03632e1fd5479e20f72647a3a252e0d
7
+ data.tar.gz: 83498d058cef2500cb75f43a0b14abdb8672c4f43a7c1a118c4ff5602c3d6f1bc705acc8df2e132d6bde9a8e342f1d942620747a6aa0e5c59052bae31aa0f6a9
@@ -10,10 +10,17 @@ module Restiny
10
10
  end
11
11
 
12
12
  module ComponentType
13
- PROFILES = "Profiles"
14
- PROFILE_INVENTORIES = "ProfileInventories"
15
13
  CHARACTERS = "Characters"
14
+ CHARACTER_EQUIPMENT = "CharacterEquipment"
16
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"
17
24
  end
18
25
 
19
26
  module ItemLocation
@@ -34,8 +41,3 @@ module Restiny
34
41
  EXOTIC = 6
35
42
  end
36
43
  end
37
-
38
- COMPONENT_TYPE_PROFILES = "Profiles"
39
- COMPONENT_TYPE_PROFILE_INVENTORIES = "ProfileInventories"
40
- COMPONENT_TYPE_CHARACTERS = "Characters"
41
- COMPONENT_TYPE_CHARACTER_INVENTORIES = "CharacterInventories"
@@ -2,17 +2,21 @@
2
2
 
3
3
  module Restiny
4
4
  class Error < StandardError
5
- def initialize(message, details = nil)
6
- @details = details
5
+ def initialize(message, status = nil)
6
+ @status = status
7
7
  super(message)
8
8
  end
9
9
  end
10
10
 
11
- class RequestError < Error; end
11
+ class RequestError < Error
12
+ end
12
13
 
13
- class InvalidParamsError < RequestError; end
14
+ class InvalidParamsError < RequestError
15
+ end
14
16
 
15
- class RateLimitedError < RequestError; end
17
+ class RateLimitedError < RequestError
18
+ end
16
19
 
17
- class ResponseError < Error; end
20
+ class ResponseError < Error
21
+ end
18
22
  end
@@ -6,75 +6,84 @@ require "zip"
6
6
 
7
7
  module Restiny
8
8
  class Manifest
9
- TABLES = {
10
- Achievement: {item: "achievement", items: "achievements"},
11
- Activity: {item: "activity", items: "activities"},
12
- ActivityGraph: {item: "activity_graph", items: "activity_graphs"},
13
- ActivityMode: {item: "activity_mode", items: "activity_modes"},
14
- ActivityModifier: {item: "activity_modifier", items: "activity_modifiers"},
15
- ActivityType: {item: "activity_type", items: "activity_types"},
16
- Artifact: {item: "artifact", items: "artifacts"},
17
- Bond: {item: "bond", items: "bonds"},
18
- BreakerType: {item: "breaker_type", items: "breaker_types"},
19
- Checklist: {item: "checklist", items: "checklists"},
20
- Class: {item: "guardian_class", items: "guardian_classes"},
21
- Collectible: {item: "collectible", items: "collectibles"},
22
- DamageType: {item: "damage_type", items: "damage_types"},
23
- Destination: {item: "destination", items: "destinations"},
24
- EnergyType: {item: "energy_type", items: "energy_types"},
25
- EquipmentSlot: {item: "equipment_slot", items: "equipment_slots"},
26
- EventCard: {item: "event_card", items: "event_cards"},
27
- Faction: {item: "faction", items: "factions"},
28
- Gender: {item: "gender", items: "genders"},
29
- HistoricalStats: {item: "historical_stat", items: "historical_stats"},
30
- InventoryBucket: {item: "inventory_bucket", items: "inventory_buckets"},
31
- InventoryItem: {item: "inventory_item", items: "inventory_items"},
32
- ItemCategory: {item: "item_category", items: "item_categories"},
33
- ItemTierType: {item: "item_tier_type", items: "item_tier_types"},
34
- Location: {item: "location", items: "locations"},
35
- Lore: {item: "lore", items: "lore_entries"},
36
- MaterialRequirementSet: {item: "material_requirement_set", items: "material_requirement_sets"},
37
- MedalTier: {item: "medal_tier", items: "medal_tiers"},
38
- Metric: {item: "metric", items: "metrics"},
39
- Milestone: {item: "milestone", items: "milestones"},
40
- Objective: {item: "objective", items: "objectives"},
41
- Place: {item: "place", items: "places"},
42
- PlugSet: {item: "plug_set", items: "plug_sets"},
43
- PowerCap: {item: "power_cap", items: "power_caps"},
44
- PresentationNode: {item: "presentation_node", items: "presentation_nodes"},
45
- Progression: {item: "progression", items: "progression_data"},
46
- ProgressionLevelRequirement: {item: "progression_level_requirement", items: "progression_level_requirements"},
47
- Race: {item: "race", items: "races"},
48
- Record: {item: "record", items: "records"},
49
- ReportReasonCategory: {item: "report_reason_category", items: "report_reason_categories"},
50
- RewardSource: {item: "reward_source", items: "reward_sources"},
51
- SackRewardItemList: {item: "sack_reward_item_list", items: "sack_reward_item_lists"},
52
- SandboxPattern: {item: "sandbox_pattern", items: "sandbox_patterns"},
53
- SandboxPerk: {item: "sandbox_perk", items: "sandbox_perks"},
54
- Season: {item: "season", items: "seasons"},
55
- SeasonPass: {item: "season_pass", items: "season_passes"},
56
- SocketCategory: {item: "socket_category", items: "socket_categories"},
57
- SocketType: {item: "socket_type", items: "socket_types"},
58
- Stat: {item: "stat", items: "stats"},
59
- StatGroup: {item: "stat_group", items: "stat_groups"},
60
- TalentGrid: {item: "talent_grid", items: "talent_grids"},
61
- Trait: {item: "trait", items: "traits"},
62
- Unlock: {item: "unlock", items: "unlocks"},
63
- Vendor: {item: "vendor", items: "vendors"},
64
- VendorGroup: {item: "vendor_group", items: "vendor_groups"}
9
+ ENTITIES = {
10
+ Achievement: %w[achievement achievements],
11
+ Activity: %w[activity activities],
12
+ ActivityGraph: %w[activity_graph activity_graphs],
13
+ ActivityMode: %w[activity_mode activity_modes],
14
+ ActivityModifier: %w[activity_modifier activity_modifiers],
15
+ ActivityType: %w[activity_type activity_types],
16
+ Artifact: %w[artifact artifacts],
17
+ Bond: %w[bonds bonds],
18
+ BreakerType: %w[breaker_type breaker_types],
19
+ Checklist: %w[checklist checklists],
20
+ Class: %w[guardian_class guardian_classes],
21
+ Collectible: %w[collectible collectibles],
22
+ DamageType: %w[damage_type damage_types],
23
+ Destination: %w[destination destinations],
24
+ EnergyType: %w[energy_type energy_types],
25
+ EquipmentSlot: %w[equipment_slot equipment_slots],
26
+ EventCard: %w[event_card event_cards],
27
+ Faction: %w[faction factions],
28
+ Gender: %w[guardian_gender guardian_genders],
29
+ GuardianRank: %w[guardian_rank guardian_ranks],
30
+ GuardianRankConstants: %w[guardian_rank_constant guardian_rank_constants],
31
+ HistoricalStats: %w[historical_stat historical_stats],
32
+ InventoryBucket: %w[inventory_bucket inventory_buckets],
33
+ InventoryItem: %w[inventory_item inventory_items],
34
+ ItemCategory: %w[item_category item_categories],
35
+ ItemTierType: %w[item_tier_type item_tier_types],
36
+ LoadoutColor: %w[loadout_color loadout_colors],
37
+ LoadoutConstants: %w[loadout_constant loadout_constants],
38
+ LoadoutIcon: %w[loadout_icon loadout_icons],
39
+ LoadoutName: %w[loadout_name loadout_names],
40
+ Location: %w[location locations],
41
+ Lore: %w[lore_entry lore_entries],
42
+ MaterialRequirementSet: %w[material_requirement_set material_requirement_sets],
43
+ MedalTier: %w[medal_tier medal_tiers],
44
+ Metric: %w[metric metrics],
45
+ Milestone: %w[milestone milestones],
46
+ Objective: %w[objective objectives],
47
+ Place: %w[place places],
48
+ PlugSet: %w[plug_set plug_sets],
49
+ PowerCap: %w[power_cap power_caps],
50
+ PresentationNode: %w[presentation_node presentation_nodes],
51
+ Progression: %w[progression progressions],
52
+ ProgressionLevelRequirement: %w[progression_level_requirement progression_level_requirements],
53
+ Race: %w[guardian_race guardian_races],
54
+ Record: %w[record records],
55
+ ReportReasonCategory: %w[report_reason_category report_reason_categories],
56
+ RewardSource: %w[reward_source reward_sources],
57
+ SackRewardItemList: %w[sack_reward_item_list sack_reward_item_lists],
58
+ SandboxPattern: %w[sandbox_pattern sandbox_patterns],
59
+ SandboxPerk: %w[sandbox_perk sandbox_perks],
60
+ Season: %w[season seasons],
61
+ SeasonPass: %w[season_pass season_passes],
62
+ SocialCommendation: %w[commendation commendations],
63
+ SocialCommendationNode: %w[commendation_node commendation_nodes],
64
+ SocketCategory: %w[socket_category socket_categories],
65
+ SocketType: %w[socket_type socket_types],
66
+ Stat: %w[stat stats],
67
+ StatGroup: %w[stat_group stat_groups],
68
+ TalentGrid: %w[talent_grid talent_grids],
69
+ Trait: %w[trait traits],
70
+ Unlock: %w[unlock unlocks],
71
+ Vendor: %w[vendor vendors],
72
+ VendorGroup: %w[vendor_group vendor_groups]
65
73
  }
66
74
 
67
75
  attr_reader :file_path
68
76
 
69
- TABLES.each do |table_name, method_names|
70
- full_table_name = "Destiny#{table_name}Definition"
77
+ ENTITIES.each do |entity, method_names|
78
+ full_table_name = "Destiny#{entity}Definition"
79
+ single_method_name, plural_method_name = method_names
71
80
 
72
- define_method method_names[:item] do |hash|
73
- fetch_item(full_table_name, hash)
81
+ define_method single_method_name do |id|
82
+ fetch_item(full_table_name, id)
74
83
  end
75
84
 
76
- define_method method_names[:items] do |options = {}|
77
- fetch_items(full_table_name, options)
85
+ define_method plural_method_name do |limit = nil|
86
+ fetch_items(full_table_name, limit)
78
87
  end
79
88
  end
80
89
 
@@ -102,9 +111,14 @@ module Restiny
102
111
 
103
112
  private
104
113
 
105
- def fetch_item(table_name, hash)
114
+ def get_entity_names
115
+ query = "SELECT name from sqlite_schema WHERE name LIKE 'Destiny%'"
116
+ @database.execute(query).map { |row| row["name"].gsub(/(Destiny|Definition)/, "") }
117
+ end
118
+
119
+ def fetch_item(table_name, id)
106
120
  query = "SELECT json FROM #{table_name} WHERE json_extract(json, '$.hash')=?"
107
- result = @database.execute(query, hash)
121
+ result = @database.execute(query, id)
108
122
 
109
123
  return nil if result.nil? || result.count < 1 || !result[0].include?("json")
110
124
 
@@ -113,33 +127,28 @@ module Restiny
113
127
  raise Restiny::RequestError.new("Error while fetching item (#{e})")
114
128
  end
115
129
 
116
- def fetch_items(table_name, options = {})
130
+ def fetch_items(table_name, limit = nil)
117
131
  bindings = []
118
132
 
119
- query = "SELECT json FROM #{table_name} "
120
- query << "WHERE json_extract(json, '$.displayProperties.name') != '' " if options[:filter_empty]
121
- query << "ORDER BY json_extract(json, '$.index')"
133
+ query = "SELECT json FROM #{table_name} ORDER BY json_extract(json, '$.index')"
122
134
 
123
- if options[:limit]
135
+ if limit
124
136
  query << " LIMIT ?"
125
- bindings << options[:limit]
137
+ bindings << limit
126
138
  end
127
139
 
128
- result = []
140
+ items = []
129
141
 
130
142
  @database.execute(query, bindings) do |row|
131
143
  item = JSON.parse(row["json"])
144
+ yield item if block_given?
132
145
 
133
- if block_given?
134
- yield item
135
- else
136
- result << item
137
- end
146
+ items << item
138
147
  end
139
148
 
140
- result unless block_given?
149
+ items unless block_given?
141
150
  rescue SQLite3::Exception => e
142
- raise Restiny::RequestError.new("Error while fetching item (#{e})")
151
+ raise Restiny::RequestError.new("Error while fetching items (#{e})")
143
152
  end
144
153
  end
145
154
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restiny
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/restiny.rb CHANGED
@@ -26,12 +26,7 @@ module Restiny
26
26
 
27
27
  @oauth_state = state || SecureRandom.hex(15)
28
28
 
29
- params = {
30
- response_type: "code",
31
- client_id: @oauth_client_id,
32
- state: @oauth_state
33
- }
34
-
29
+ params = { response_type: "code", client_id: @oauth_client_id, state: @oauth_state }
35
30
  params[:redirect_url] = redirect_url unless redirect_url.nil?
36
31
 
37
32
  connection.build_url(BUNGIE_URL + "/en/oauth/authorize", params).to_s
@@ -40,23 +35,23 @@ module Restiny
40
35
  def request_access_token(code, redirect_url = nil)
41
36
  check_oauth_client_id
42
37
 
43
- params = {
44
- code: code,
45
- grant_type: "authorization_code",
46
- client_id: @oauth_client_id
47
- }
48
-
38
+ params = { code: code, grant_type: "authorization_code", client_id: @oauth_client_id }
49
39
  params[:redirect_url] = redirect_url unless redirect_url.nil?
50
40
 
51
- post("/platform/app/oauth/token/", params, "Content-Type" => "application/x-www-form-urlencoded")
41
+ make_api_request(
42
+ :post,
43
+ "app/oauth/token/",
44
+ params,
45
+ "Content-Type" => "application/x-www-form-urlencoded"
46
+ )
52
47
  end
53
48
 
54
49
  # Manifest methods
55
50
 
56
51
  def download_manifest(locale = "en")
57
- response = get("/platform/Destiny2/Manifest/")
52
+ response = get("Destiny2/Manifest/")
58
53
 
59
- manifest_path = response.dig("Response", "mobileWorldContentPaths", locale)
54
+ manifest_path = response.dig("mobileWorldContentPaths", locale)
60
55
  raise Restiny::ResponseError.new("Unable to determine manifest URL") if manifest_path.nil?
61
56
 
62
57
  Manifest.download(BUNGIE_URL + manifest_path)
@@ -65,92 +60,98 @@ module Restiny
65
60
  # Profile methods
66
61
 
67
62
  def get_profile(membership_id, membership_type, components = [])
68
- raise Restiny::InvalidParamsError.new("You must provide at least one component") if components.empty?
69
- get("/platform/Destiny2/#{membership_type}/Profile/#{membership_id}?components=#{components.join(",")}")
63
+ if components.empty?
64
+ raise Restiny::InvalidParamsError.new("Please provide at least one component")
65
+ end
66
+
67
+ get("Destiny2/#{membership_type}/Profile/#{membership_id}?components=#{components.join(",")}")
70
68
  end
71
69
 
72
70
  # Account methods
73
71
 
74
- def get_user_by_membership_id(membership_id, membership_type = PLATFORM_ALL)
75
- raise Restiny::InvalidParamsError.new("You must provide a valid membership ID") if membership_id.nil?
76
- get("/platform/User/GetMembershipsById/#{membership_id}/#{membership_type}/")
72
+ def get_user_by_membership_id(membership_id, membership_type = Platform::ALL)
73
+ raise Restiny::InvalidParamsError.new("Please provide a membership ID") if membership_id.nil?
74
+
75
+ get("User/GetMembershipsById/#{membership_id}/#{membership_type}/")
77
76
  end
78
77
 
79
- def get_user_by_bungie_name(full_display_name, membership_type = PLATFORM_ALL)
78
+ def get_user_by_bungie_name(full_display_name, membership_type = Platform::ALL)
80
79
  display_name, display_name_code = full_display_name.split("#")
81
- raise Restiny::InvalidParamsError.new("You must provide a valid Bungie name") if display_name.nil? || display_name_code.nil?
82
-
83
- params = {
84
- displayName: display_name,
85
- displayNameCode: display_name_code
86
- }
80
+ if display_name.nil? || display_name_code.nil?
81
+ raise Restiny::InvalidParamsError.new("You must provide a valid Bungie name")
82
+ end
87
83
 
88
- post("/platform/Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/", params)
84
+ post(
85
+ "Destiny2/SearchDestinyPlayerByBungieName/#{membership_type}/",
86
+ { displayName: display_name, displayNameCode: display_name_code }
87
+ )
89
88
  end
90
89
 
91
90
  def search_users(name, page = 0)
92
- post("/platform/User/Search/GlobalName/#{page}", displayNamePrefix: name)
91
+ post("User/Search/GlobalName/#{page}", displayNamePrefix: name)
93
92
  end
94
93
 
95
94
  private
96
95
 
97
96
  def get(endpoint_url, params = {}, headers = {})
98
- make_api_request(:get, endpoint_url, params, headers)
97
+ make_api_request(:get, endpoint_url, params, headers).dig("Response")
99
98
  end
100
99
 
101
100
  def post(endpoint_url, body, headers = {})
102
- make_api_request(:post, endpoint_url, body, headers)
101
+ make_api_request(:post, endpoint_url, body, headers).dig("Response")
103
102
  end
104
103
 
105
104
  def make_api_request(type, url, params, headers = {})
106
- raise Restiny::InvalidParamsError.new("You need to set an API key (Restiny.api_key = XXX)") unless @api_key
105
+ raise Restiny::InvalidParamsError.new("You need to set an API key") unless @api_key
107
106
 
108
107
  headers[:authorization] = "Bearer #{@oauth_token}" if @oauth_token
109
108
 
110
- response = case type
111
- when :get
112
- connection.get(url, params, headers)
113
- when :post
114
- connection.post(url, params, headers)
115
- end
109
+ response =
110
+ case type
111
+ when :get
112
+ connection.get(url, params, headers)
113
+ when :post
114
+ connection.post(url, params, headers)
115
+ end
116
116
 
117
- response.body&.dig("Response")
117
+ response.body
118
118
  rescue Faraday::Error => error
119
- message = if error.response_body && error.response_headers["content-type"] =~ /application\/json;/i
120
- error_response = JSON.parse(error.response_body)
121
- "#{error_response["error_description"]} (#{error_response["error"]})"
122
- else
123
- error.message
119
+ begin
120
+ error_body = JSON.parse(error.response_body)
121
+ status, message = error_body["ErrorStatus"], error_body["Message"]
122
+ rescue JSON::ParserError
123
+ status, message = error.response_status, error.message
124
124
  end
125
125
 
126
- case error
127
- when Faraday::ClientError
128
- raise Restiny::RequestError.new(message, error.response_status)
129
- when Faraday::ServerError
130
- raise Restiny::ResponseError.new(message, error.response_status)
131
- else
132
- raise Restiny::Error.new(message)
133
- end
126
+ klass =
127
+ case error
128
+ when Faraday::ClientError
129
+ Restiny::RequestError
130
+ when Faraday::ServerError
131
+ Restiny::ResponseError
132
+ else
133
+ Restiny::Error
134
+ end
135
+
136
+ raise klass.new(message, status)
134
137
  end
135
138
 
136
139
  def check_oauth_client_id
137
- raise Restiny::RequestError.new("You need to set an OAuth client ID (Restiny.oauth_client_id = XXX)") unless @oauth_client_id
140
+ raise Restiny::RequestError.new("You need to set an OAuth client ID") unless @oauth_client_id
138
141
  end
139
142
 
140
143
  def default_headers
141
- {
142
- "User-Agent": "restiny v#{Restiny::VERSION}",
143
- "X-API-KEY": @api_key
144
- }
144
+ { "User-Agent": "restiny v#{Restiny::VERSION}", "X-API-KEY": @api_key }
145
145
  end
146
146
 
147
147
  def connection
148
- @connection ||= Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
149
- faraday.request :json
150
- faraday.request :url_encoded
151
- faraday.response :json
152
- faraday.response :follow_redirects
153
- faraday.response :raise_error
154
- end
148
+ @connection ||=
149
+ Faraday.new(url: API_BASE_URL, headers: default_headers) do |faraday|
150
+ faraday.request :json
151
+ faraday.request :url_encoded
152
+ faraday.response :json
153
+ faraday.response :follow_redirects
154
+ faraday.response :raise_error
155
+ end
155
156
  end
156
157
  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: 0.5.0
4
+ version: 0.6.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-05-26 00:00:00.000000000 Z
11
+ date: 2023-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday