bdoap 0.0.4 → 0.0.5

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: a4336121be8b520924050e57400f46e24661e9c9dc4156def8b08802e6eec2e2
4
- data.tar.gz: 5011af8aa6f7f7a044786ca134ca8d4cce0bb3ae076409de70cc69a1adf8d6f9
3
+ metadata.gz: d3741f33638b6173720937040a4345070a3e34dbbbf5605946a331d80e221a11
4
+ data.tar.gz: a1c0f458b655495ba8a23b92c611346fac20850d7894704f48719ce16d4746e2
5
5
  SHA512:
6
- metadata.gz: 0b055768a4bad1ebc9b244574d546dd859c4db6f7475d72afd7352d24b3aa9db5a311f6c74b6ce160ae86756ebbbc249df87c8d17b2294bba1a575bec8b6e96d
7
- data.tar.gz: f5537518eebae1c5f62d63d9daaa0b2ee29d1570940a165e0c7dcee1b033ed728909f6f7f3940edc51d5dc00672363f8639adb8f8a64780ecc3ed9706716ffa5
6
+ metadata.gz: 97061a30ef4e2b951d6ee26eabb51e28ba1170fa4208d0a3d1507ca3c29956fd0ac871e712df2bd06d4af480026203ae49b06109b1c335608631f6212dbad550
7
+ data.tar.gz: 00a37e50db4bb906d4a90c749e0614a37fc1f8216075c5dd5f6a6d8c8283ccffd5295b27380c0d159008ca978c4d2bce0348c3c2f7607342bdf7a82f5516db69
@@ -58,6 +58,13 @@ module BDOAP
58
58
 
59
59
  return unless resolved
60
60
 
61
+ level_map_to_bdo = {
62
+ 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5,
63
+ 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11,
64
+ 12 => 12, 13 => 13, 14 => 14, 15 => 15,
65
+ 16 => 'PRI', 17 => 'DUO', 18 => 'TRI', 19 => 'TET', 20 => 'PEN',
66
+ }.freeze
67
+
61
68
  resolved.each do |item|
62
69
  subdata = HTTParty.post(
63
70
  URI(sub_url),
@@ -69,55 +76,7 @@ module BDOAP
69
76
  sleep rand
70
77
 
71
78
  if subdata&.dig('detailList')
72
- level_map = {
73
- 0 => 0,
74
- 1 => 1,
75
- 2 => 2,
76
- 3 => 3,
77
- 4 => 4,
78
- 5 => 5,
79
- 6 => 6,
80
- 7 => 7,
81
- 8 => 8,
82
- 9 => 9,
83
- 10 => 10,
84
- 11 => 11,
85
- 12 => 12,
86
- 13 => 13,
87
- 14 => 14,
88
- 15 => 15,
89
- 16 => 16,
90
- 17 => 17,
91
- 18 => 18,
92
- 19 => 19,
93
- 20 => 20,
94
- }
95
-
96
- level_map_to_bdo = {
97
- 0 => 0,
98
- 1 => 1,
99
- 2 => 2,
100
- 3 => 3,
101
- 4 => 4,
102
- 5 => 5,
103
- 6 => 6,
104
- 7 => 7,
105
- 8 => 8,
106
- 9 => 9,
107
- 10 => 10,
108
- 11 => 11,
109
- 12 => 12,
110
- 13 => 13,
111
- 14 => 14,
112
- 15 => 15,
113
- 16 => 'PRI',
114
- 17 => 'DUO',
115
- 18 => 'TRI',
116
- 19 => 'TET',
117
- 20 => 'PEN',
118
- }
119
-
120
- result = subdata['detailList'] .find { |e| e['subKey'].to_s == level_map[enhance_starting_level.to_i].to_s }
79
+ result = subdata['detailList'].find { |e| e['subKey'].to_s == enhance_starting_level.to_s }
121
80
  if result
122
81
  constructed = " #{result['count']} #{cli.yellow result['name'].downcase} @ #{level_map_to_bdo[result['subKey']]}"
123
82
  items_at_level.push constructed unless result['count'].to_i == 0
@@ -184,11 +143,10 @@ module BDOAP
184
143
 
185
144
  recipe_prices = market_searcher.get_all_recipe_prices item_codex_data, category
186
145
 
187
- mapped_prices = recipe_prices.reverse.sort_by { |recipe| recipe[:gain].to_i }.map { |recipe| recipe[:information] }
146
+ mapped_prices = recipe_prices.sort_by { |recipe| recipe[:gain].to_i }.map { |recipe| recipe[:information] }
188
147
 
189
148
  out_of_stock = recipe_prices.dig(0, :out_of_stock) || []
190
- out_of_stock_list = ""
191
- out_of_stock.each { |item| out_of_stock_list += "\n\t #{cli.yellow item}" }
149
+ out_of_stock_list = out_of_stock.map { |item| "\n\t #{cli.yellow item}" }.join
192
150
 
193
151
  if mapped_prices.length > 0
194
152
  cli.vipiko_overwrite "done!"
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
4
  require 'httparty'
@@ -46,6 +46,14 @@ class BDOCodexSearcher
46
46
  @hyper_aggressive = hyper_aggressive
47
47
  end
48
48
 
49
+ def log_error(error, message = nil)
50
+ puts @cli.red(message) if message
51
+ File.open(ENVData::ERROR_LOG, 'a+') do |file|
52
+ file.write(error&.full_message || error.to_s)
53
+ file.write("\n\r")
54
+ end
55
+ end
56
+
49
57
  def get_recipe_url(url, item_name)
50
58
  begin
51
59
  data = HTTParty.get(
@@ -66,9 +74,8 @@ class BDOCodexSearcher
66
74
  all_recipe_substitutions = []
67
75
  all_potion_recipes.each do |recipe|
68
76
  original_recipe = recipe[RECIPE_INGREDIENTS_INDEX]
69
- original_ingredient_indices = original_recipe.map do |item|
70
- recipe_with_substitute_ids.find_index { |id| id == item[:id] }
71
- end
77
+ sub_id_index = recipe_with_substitute_ids.each_with_index.to_h { |id, i| [id, i] }
78
+ original_ingredient_indices = original_recipe.map { |item| sub_id_index[item[:id]] }
72
79
 
73
80
  chunked_by_substitution_groups = []
74
81
 
@@ -197,13 +204,7 @@ class BDOCodexSearcher
197
204
  parsed
198
205
  end
199
206
  rescue StandardError => error
200
- puts @cli.red("if you're not messing with the code, you should never see this. get_item_recipes broke.")
201
-
202
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
203
- file.write(error.full_message)
204
- file.write("\n\r")
205
- end
206
-
207
+ log_error error, "if you're not messing with the code, you should never see this. get_item_recipes broke."
207
208
  []
208
209
  end
209
210
  end
@@ -267,9 +268,7 @@ class BDOCodexSearcher
267
268
  # newline because vipiko is about to start carriage returning
268
269
  puts "\n"
269
270
  item_list.each.with_index do |item_hash, index|
270
- item = item_hash.transform_keys { |key|
271
- key.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
272
- }
271
+ item = item_hash.transform_keys { |key| Utils.snake_sym(key) }
273
272
 
274
273
  next unless item[:main_key]
275
274
 
@@ -302,13 +301,7 @@ class BDOCodexSearcher
302
301
  recipes.push recipe_hash
303
302
  end
304
303
  rescue StandardError => error
305
- puts @cli.red("if you're not messing with the code, you should never see this. get_item_codex_data broke.")
306
-
307
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
308
- file.write(error.full_message)
309
- file.write("\n\r")
310
- end
311
-
304
+ log_error error, "if you're not messing with the code, you should never see this. get_item_codex_data broke."
312
305
  next
313
306
  end
314
307
  end
@@ -30,17 +30,30 @@ class MarketSearcher
30
30
  @out_of_stock_items = []
31
31
  end
32
32
 
33
+ def market_post(url, body)
34
+ HTTParty.post(
35
+ URI(url),
36
+ headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
37
+ body: body,
38
+ content_type: 'application/x-www-form-urlencoded'
39
+ )
40
+ end
41
+
42
+ # DRY helper: log an error message to the CLI and append to the error log file
43
+ def log_error(error, message = nil)
44
+ puts @cli.red(message) if message
45
+ File.open(ENVData::ERROR_LOG, 'a+') do |file|
46
+ file.write(error&.full_message || error.to_s)
47
+ file.write("\n\r")
48
+ end
49
+ end
50
+
33
51
  def get_alchemy_market_data(category)
34
52
  construct_item_data(category, category == 'all')
35
53
  end
36
54
 
37
55
  def get_price_data(elem)
38
- data = HTTParty.post(
39
- URI(@market_sub_url),
40
- headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
41
- body: "#{ENVData::RVT}&mainKey=#{elem['mainKey']}&usingCleint=0",
42
- content_type: 'application/x-www-form-urlencoded'
43
- )
56
+ data = market_post(@market_sub_url, "#{ENVData::RVT}&mainKey=#{elem['mainKey']}&usingCleint=0")
44
57
 
45
58
  sleep 1
46
59
 
@@ -58,9 +71,7 @@ class MarketSearcher
58
71
  def construct_item_data(subcategory, all_subcategories)
59
72
  aggregate = aggregate_category_data(@market_list_url, @market_search_url, subcategory, all_subcategories)
60
73
 
61
- filtered_aggregate = aggregate.filter do |elem|
62
- elem != nil
63
- end
74
+ filtered_aggregate = aggregate.compact
64
75
 
65
76
  # TODO: this is probably not a smart way to do this type of retry logic
66
77
  puts
@@ -78,13 +89,7 @@ class MarketSearcher
78
89
  begin
79
90
  get_price_data elem
80
91
  rescue StandardError => error
81
- puts @cli.red("this could be a network failure. construct_item_data broke.")
82
-
83
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
84
- file.write(error.full_message)
85
- file.write("\n\r")
86
- end
87
-
92
+ log_error error, "this could be a network failure. construct_item_data broke."
88
93
  []
89
94
  end
90
95
  end
@@ -92,9 +97,7 @@ class MarketSearcher
92
97
 
93
98
  puts "\n\n" unless mapped_aggregate.empty?
94
99
 
95
- mapped_aggregate.sort do |a, b|
96
- b['pricePerOne'] - a['pricePerOne']
97
- end
100
+ mapped_aggregate.sort_by { |a| -a['pricePerOne'] }
98
101
  end
99
102
 
100
103
  def aggregate_category_data(url, search_url, subcategory, all_subcategories)
@@ -111,24 +114,13 @@ class MarketSearcher
111
114
  category_search_options(url, search_url).each do |category_opts|
112
115
  do_if_category_matches(make_match_options.call(category_opts[:name])) do
113
116
  begin
114
- data = HTTParty.post(
115
- URI(category_opts[:url]),
116
- headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
117
- body: category_opts[:query_string],
118
- content_type: 'application/x-www-form-urlencoded'
119
- )
117
+ data = market_post(category_opts[:url], category_opts[:query_string])
120
118
 
121
119
  sleep 1
122
120
 
123
121
  aggregate_response.push category_opts[:update].call(data) if data
124
122
  rescue StandardError => error
125
- puts @cli.red("this could be a network failure. aggregate_category_data broke.")
126
-
127
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
128
- file.write(error.full_message)
129
- file.write("\n\r")
130
- end
131
-
123
+ log_error error, "this could be a network failure. aggregate_category_data broke."
132
124
  []
133
125
  end
134
126
  end
@@ -147,22 +139,12 @@ class MarketSearcher
147
139
  ingredient_data = {}
148
140
 
149
141
  begin
150
- ingredient_data = HTTParty.post(
151
- URI(@market_sub_url),
152
- headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
153
- body: "#{ENVData::RVT}&mainKey=#{ingredient_id}&usingCleint=0",
154
- content_type: 'application/x-www-form-urlencoded'
155
- )
142
+ ingredient_data = market_post(@market_sub_url, "#{ENVData::RVT}&mainKey=#{ingredient_id}&usingCleint=0")
156
143
 
157
144
  sleep rand
158
145
  rescue StandardError => error
159
- puts @cli.red("this could be a network failure. get_item_price_info broke.")
160
-
161
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
162
- file.write(error.full_message)
163
- file.write("\n\r")
146
+ log_error error, "this could be a network failure. get_item_price_info broke."
164
147
  ingredient_data = {}
165
- end
166
148
  end
167
149
 
168
150
  # TODO: actually implement a normal way of handling malformed results
@@ -178,31 +160,19 @@ class MarketSearcher
178
160
  detailed_price_list = {}
179
161
 
180
162
  begin
181
- detailed_price_list = HTTParty.post(
182
- URI(@market_sell_buy_url),
183
- headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
184
- body: body_string,
185
- content_type: 'application/x-www-form-urlencoded'
186
- )
163
+ detailed_price_list = market_post(@market_sell_buy_url, body_string)
187
164
 
188
165
  detailed_price_list = {} if detailed_price_list.to_s.downcase.include? 'incapsula incident'
189
166
 
190
167
  sleep rand
191
168
  rescue StandardError => error
192
- puts @cli.red("this could be a network failure. get_item_price_info broke.")
193
-
194
- File.open(ENVData::ERROR_LOG, 'a+') do |file|
195
- file.write(error&.full_message || error)
196
- file.write("\n\r")
197
- detailed_price_list = {}
198
- end
169
+ log_error error, "this could be a network failure. get_item_price_info broke."
170
+ detailed_price_list = {}
199
171
  end
200
172
 
201
- optimal_prices = detailed_price_list&.dig('marketConditionList')&.sort do |a, b|
202
- b['sellCount'] - a['sellCount']
203
- end
173
+ optimal_prices = detailed_price_list&.dig('marketConditionList')&.sort_by { |p| -p['sellCount'] }
204
174
 
205
- total_stock = optimal_prices.to_a.map { |price| price["sellCount"] }.sum
175
+ total_stock = optimal_prices.to_a.sum { |price| price["sellCount"] }
206
176
 
207
177
  optimal_price = optimal_prices.to_a.first
208
178
 
@@ -235,12 +205,10 @@ class MarketSearcher
235
205
  potential_recipe = []
236
206
 
237
207
  recipe.each.with_index do |ingredient, ing_index|
238
- ingredient_id = ingredient['id'] ? ingredient['id'] : ingredient[:id]
239
- quant = ingredient['quant'] ? ingredient['quant'] : ingredient[:quant]
240
- enhance_level = ingredient['enhance_level'] ? ingredient['enhance_level'] : ingredient[:enhance_level]
241
- is_m_recipe = ingredient['is_m_recipe'] ? ingredient['is_m_recipe'] : ingredient[:is_m_recipe]
242
- quant = 1 if quant.nil?
243
- enhance_level = 0 if enhance_level.nil?
208
+ ingredient_id = ingredient['id'] || ingredient[:id]
209
+ quant = ingredient['quant'] || ingredient[:quant] || 1
210
+ enhance_level = ingredient['enhance_level']|| ingredient[:enhance_level] || 0
211
+ is_m_recipe = ingredient['is_m_recipe'] || ingredient[:is_m_recipe]
244
212
 
245
213
  if @ingredient_cache[ingredient_id] && EXCHANGE_ITEMS[ingredient_id].nil? == true
246
214
  cached_ingredient = { **@ingredient_cache[ingredient_id], quant: quant, for_recipe_id: recipe_id, is_m_recipe: is_m_recipe }
@@ -259,9 +227,7 @@ class MarketSearcher
259
227
  item_price_info_hash = exchange_info
260
228
  end
261
229
 
262
- item_price_info = item_price_info_hash.transform_keys { |key|
263
- key.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
264
- }
230
+ item_price_info = item_price_info_hash.transform_keys { |key| Utils.snake_sym(key) }
265
231
 
266
232
  # gathering out of stock items to show to the user
267
233
  stock_string = "#{quant}x [#{item_price_info[:main_key]}] #{item_price_info[:name].downcase}"
@@ -314,7 +280,7 @@ class MarketSearcher
314
280
  mapped_recipe_prices.push map_recipe_prices(potential_recipes, item_with_recipe, subcategory)
315
281
  end
316
282
 
317
- mapped_recipe_prices.filter { |e| !e.nil? && !!e }
283
+ mapped_recipe_prices.compact
318
284
  end
319
285
 
320
286
  def map_recipe_prices(potential_recipes, item, category)
@@ -370,7 +336,7 @@ class MarketSearcher
370
336
  # are some seriously mysterious alchemy recipes out there...
371
337
  ingredients_already_appeared = []
372
338
  filtered_selected_recipe = selected_recipe.filter do |ingredient|
373
- return false if ingredients_already_appeared.include? ingredient[:name]
339
+ next false if ingredients_already_appeared.include? ingredient[:name]
374
340
  ingredients_already_appeared.push(ingredient[:name])
375
341
  true
376
342
  end
@@ -383,12 +349,7 @@ class MarketSearcher
383
349
 
384
350
  body_string = "#{ENVData::RVT}&mainKey=#{item[:id]}&subKey=0&chooseKey=0&isUp=true&keyType=0&name=#{URI.encode_www_form_component(item[:name])}"
385
351
 
386
- item_price_data = HTTParty.post(
387
- URI(@market_sell_buy_url),
388
- headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
389
- body: body_string,
390
- content_type: 'application/x-www-form-urlencoded'
391
- )
352
+ item_price_data = market_post(@market_sell_buy_url, body_string)
392
353
 
393
354
  item_price_data = {} if item_price_data.to_s.downcase.include? 'incapsula incident'
394
355
 
@@ -449,7 +410,6 @@ class MarketSearcher
449
410
  end
450
411
 
451
412
  def do_if_category_matches(options, &procedure)
452
- procedure.call if options[:all_subcategories]
453
- procedure.call if options[:subcategory] == options[:subcat_to_match]
413
+ procedure.call if options[:all_subcategories] || (options[:subcategory] == options[:subcat_to_match])
454
414
  end
455
415
  end
@@ -18,4 +18,8 @@ module Utils
18
18
  make_permutations input, limit
19
19
  end
20
20
  end
21
+
22
+ def self.snake_sym(key)
23
+ key.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym
24
+ end
21
25
  end
@@ -23,12 +23,8 @@ module Utils
23
23
  end
24
24
 
25
25
  def write(data)
26
- item_map = read_all
27
- File.open(@cache_file, 'w') do |file|
28
- new_data = { **item_map, **data }
29
-
30
- file.write new_data.to_json
31
- end
26
+ @all_content.merge!(data)
27
+ File.write(@cache_file, @all_content.to_json)
32
28
  end
33
29
  end
34
30
  end
@@ -75,7 +75,7 @@ module Utils
75
75
  },
76
76
  7304 => {
77
77
  name: 'Strawberry',
78
- id: 9008,
78
+ id: 7304,
79
79
  price: 700,
80
80
  price_per_one: 700,
81
81
  count: Float::INFINITY,
@@ -472,7 +472,7 @@ module Utils
472
472
  is_npc_item: true,
473
473
  npc_type: 'general goods',
474
474
  main_category: 25,
475
- sub_category_: 8,
475
+ sub_category: 8,
476
476
  },
477
477
  517 => {
478
478
  name: 'HP Potion (Small)',
@@ -483,29 +483,29 @@ module Utils
483
483
  is_npc_item: true,
484
484
  npc_type: 'general goods',
485
485
  main_category: 35,
486
- sub_category_: 5,
486
+ sub_category: 5,
487
487
  },
488
488
  521 => {
489
489
  name: 'HP Potion (Medium)',
490
- id: 517,
490
+ id: 521,
491
491
  price: 200,
492
492
  price_per_one: 200,
493
493
  count: Float::INFINITY,
494
494
  is_npc_item: true,
495
495
  npc_type: 'general goods',
496
496
  main_category: 35,
497
- sub_category_: 5,
497
+ sub_category: 5,
498
498
  },
499
499
  519 => {
500
500
  name: 'HP Potion (Large)',
501
- id: 517,
501
+ id: 519,
502
502
  price: 500,
503
503
  price_per_one: 500,
504
504
  count: Float::INFINITY,
505
505
  is_npc_item: true,
506
506
  npc_type: 'general goods',
507
507
  main_category: 35,
508
- sub_category_: 5,
508
+ sub_category: 5,
509
509
  },
510
510
  524 => {
511
511
  name: 'HP Potion (Extra Large)',
@@ -516,7 +516,7 @@ module Utils
516
516
  is_npc_item: true,
517
517
  npc_type: 'general goods',
518
518
  main_category: 35,
519
- sub_category_: 5,
519
+ sub_category: 5,
520
520
  },
521
521
  520 => {
522
522
  name: 'MP Potion (Small)',
@@ -527,7 +527,7 @@ module Utils
527
527
  is_npc_item: true,
528
528
  npc_type: 'general goods',
529
529
  main_category: 35,
530
- sub_category_: 5,
530
+ sub_category: 5,
531
531
  },
532
532
  522 => {
533
533
  name: 'MP Potion (Large)',
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BDOAP
4
- VERSION = "0.0.4"
4
+ VERSION = "0.0.5"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bdoap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - jpegzilla
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-21 00:00:00.000000000 Z
10
+ date: 2026-09-27 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: this script allows you to find alchemy recipes from bdocodex.com, compare
13
13
  them with live central market prices, and use that information to determine what
@@ -50,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
50
  - !ruby/object:Gem::Version
51
51
  version: '0'
52
52
  requirements: []
53
- rubygems_version: 3.6.7
53
+ rubygems_version: 3.6.9
54
54
  specification_version: 4
55
55
  summary: this is a tool used to aggregate price and profit data for black desert alchemists.
56
56
  test_files: []