bdoap 0.0.3 → 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 +4 -4
- data/lib/bdo_alchemy_profits.rb +69 -6
- data/lib/bdo_codex/bdo_codex_searcher.rb +14 -21
- data/lib/central_market/category_search_options.rb +16 -1
- data/lib/central_market/market_searcher.rb +42 -81
- data/lib/utils/array_utils.rb +4 -0
- data/lib/utils/constants.rb +19 -8
- data/lib/utils/hash_cache.rb +2 -6
- data/lib/utils/npc_item_index.rb +9 -9
- data/lib/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3741f33638b6173720937040a4345070a3e34dbbbf5605946a331d80e221a11
|
|
4
|
+
data.tar.gz: a1c0f458b655495ba8a23b92c611346fac20850d7894704f48719ce16d4746e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 97061a30ef4e2b951d6ee26eabb51e28ba1170fa4208d0a3d1507ca3c29956fd0ac871e712df2bd06d4af480026203ae49b06109b1c335608631f6212dbad550
|
|
7
|
+
data.tar.gz: 00a37e50db4bb906d4a90c749e0614a37fc1f8216075c5dd5f6a6d8c8283ccffd5295b27380c0d159008ca978c4d2bce0348c3c2f7607342bdf7a82f5516db69
|
data/lib/bdo_alchemy_profits.rb
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
# SOFTWARE.
|
|
25
25
|
|
|
26
26
|
require 'optparse'
|
|
27
|
+
require 'httparty'
|
|
27
28
|
|
|
28
29
|
require_relative './utils/user_cli'
|
|
29
30
|
require_relative './central_market/market_searcher'
|
|
@@ -35,8 +36,61 @@ module BDOAP
|
|
|
35
36
|
class BDOAlchemyProfits
|
|
36
37
|
include Utils
|
|
37
38
|
|
|
39
|
+
def start_enhance(search_string, enhance_starting_level, region, cli)
|
|
40
|
+
items_at_level = []
|
|
41
|
+
root_url = ENVData.get_root_url region
|
|
42
|
+
search_url = "#{root_url}#{ENVData::MARKET_SEARCH_LIST}"
|
|
43
|
+
sub_url = "#{root_url}#{ENVData::MARKET_SUB_LIST}"
|
|
44
|
+
category_opts = {
|
|
45
|
+
url: search_url,
|
|
46
|
+
query_string: "#{ENVData::RVT}&searchText=#{URI.encode_www_form_component search_string}",
|
|
47
|
+
update: ->(data) { data['list'] }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
data = HTTParty.post(
|
|
51
|
+
URI(category_opts[:url].to_s),
|
|
52
|
+
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
53
|
+
body: category_opts[:query_string],
|
|
54
|
+
content_type: 'application/x-www-form-urlencoded'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
resolved = category_opts[:update].call(data) if data
|
|
58
|
+
|
|
59
|
+
return unless resolved
|
|
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
|
+
|
|
68
|
+
resolved.each do |item|
|
|
69
|
+
subdata = HTTParty.post(
|
|
70
|
+
URI(sub_url),
|
|
71
|
+
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
72
|
+
body: "#{ENVData::RVT}&mainKey=#{item['mainKey']}&usingCleint=0",
|
|
73
|
+
content_type: 'application/x-www-form-urlencoded'
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
sleep rand
|
|
77
|
+
|
|
78
|
+
if subdata&.dig('detailList')
|
|
79
|
+
result = subdata['detailList'].find { |e| e['subKey'].to_s == enhance_starting_level.to_s }
|
|
80
|
+
if result
|
|
81
|
+
constructed = " #{result['count']} #{cli.yellow result['name'].downcase} @ #{level_map_to_bdo[result['subKey']]}"
|
|
82
|
+
items_at_level.push constructed unless result['count'].to_i == 0
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts "\nthe results are in!\n"
|
|
88
|
+
items_at_level.each { |item| puts item }
|
|
89
|
+
puts
|
|
90
|
+
end
|
|
91
|
+
|
|
38
92
|
# begin the configuration and search process
|
|
39
|
-
def start_cli
|
|
93
|
+
def start_cli(search_string = nil, enhance_starting_level = nil, enhancing = false)
|
|
40
94
|
begin
|
|
41
95
|
options = {}
|
|
42
96
|
|
|
@@ -47,10 +101,20 @@ module BDOAP
|
|
|
47
101
|
# option setup
|
|
48
102
|
cli = UserCLI.new options
|
|
49
103
|
|
|
50
|
-
|
|
51
|
-
|
|
104
|
+
unless enhancing
|
|
105
|
+
category = cli.choose_category
|
|
106
|
+
cli.end_cli if category == 'exit'
|
|
107
|
+
end
|
|
108
|
+
|
|
52
109
|
region = cli.choose_region
|
|
53
110
|
cli.end_cli if region == 'exit'
|
|
111
|
+
|
|
112
|
+
# start enhancing
|
|
113
|
+
if enhancing && search_string && enhance_starting_level
|
|
114
|
+
start_enhance(search_string, enhance_starting_level, region, cli)
|
|
115
|
+
return
|
|
116
|
+
end
|
|
117
|
+
|
|
54
118
|
lang = cli.choose_lang
|
|
55
119
|
cli.end_cli if lang == 'exit'
|
|
56
120
|
aggression = cli.choose_aggression
|
|
@@ -79,11 +143,10 @@ module BDOAP
|
|
|
79
143
|
|
|
80
144
|
recipe_prices = market_searcher.get_all_recipe_prices item_codex_data, category
|
|
81
145
|
|
|
82
|
-
mapped_prices = recipe_prices.
|
|
146
|
+
mapped_prices = recipe_prices.sort_by { |recipe| recipe[:gain].to_i }.map { |recipe| recipe[:information] }
|
|
83
147
|
|
|
84
148
|
out_of_stock = recipe_prices.dig(0, :out_of_stock) || []
|
|
85
|
-
out_of_stock_list = ""
|
|
86
|
-
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
|
|
87
150
|
|
|
88
151
|
if mapped_prices.length > 0
|
|
89
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -10,6 +10,7 @@ module MarketSearchTools
|
|
|
10
10
|
offensive: 1,
|
|
11
11
|
defensive: 2,
|
|
12
12
|
functional: 3,
|
|
13
|
+
food: 4,
|
|
13
14
|
potion: 5,
|
|
14
15
|
other: 8,
|
|
15
16
|
all: [1, 2, 3, 5, 8]
|
|
@@ -87,6 +88,13 @@ module MarketSearchTools
|
|
|
87
88
|
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:offensive]}",
|
|
88
89
|
update: ->(data) { data['marketList'] }
|
|
89
90
|
},
|
|
91
|
+
{
|
|
92
|
+
name: 'food',
|
|
93
|
+
url: url,
|
|
94
|
+
query_string:
|
|
95
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:food]}",
|
|
96
|
+
update: ->(data) { data['marketList'] }
|
|
97
|
+
},
|
|
90
98
|
{
|
|
91
99
|
name: 'defensive',
|
|
92
100
|
url: url,
|
|
@@ -132,7 +140,14 @@ module MarketSearchTools
|
|
|
132
140
|
query_string: "#{ENVData::RVT}&searchText=purified+lightstone+of",
|
|
133
141
|
# update: ->(data) { { otherResponse: data['marketList'] } },
|
|
134
142
|
update: ->(data) { data['list'] }
|
|
135
|
-
}
|
|
143
|
+
},
|
|
144
|
+
# {
|
|
145
|
+
# name: 'stuffed animals',
|
|
146
|
+
# url: search_url,
|
|
147
|
+
# query_string: "#{ENVData::RVT}&searchText=stuffed",
|
|
148
|
+
# # update: ->(data) { { otherResponse: data['marketList'] } },
|
|
149
|
+
# update: ->(data) { data['list'] }
|
|
150
|
+
# }
|
|
136
151
|
]
|
|
137
152
|
end
|
|
138
153
|
end
|
|
@@ -24,22 +24,36 @@ class MarketSearcher
|
|
|
24
24
|
@market_sub_url = "#{@root_url}#{ENVData::MARKET_SUB_LIST}"
|
|
25
25
|
@market_sell_buy_url = "#{@root_url}#{ENVData::MARKET_SELL_BUY_INFO}"
|
|
26
26
|
@cli = cli
|
|
27
|
+
# cache used to store all ingredients for which the price has already been checked
|
|
27
28
|
@ingredient_cache = {}
|
|
28
29
|
@free_ingredients = free_ingredients
|
|
29
30
|
@out_of_stock_items = []
|
|
30
31
|
end
|
|
31
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
|
+
|
|
32
51
|
def get_alchemy_market_data(category)
|
|
33
52
|
construct_item_data(category, category == 'all')
|
|
34
53
|
end
|
|
35
54
|
|
|
36
55
|
def get_price_data(elem)
|
|
37
|
-
data =
|
|
38
|
-
URI(@market_sub_url),
|
|
39
|
-
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
40
|
-
body: "#{ENVData::RVT}&mainKey=#{elem['mainKey']}&usingCleint=0",
|
|
41
|
-
content_type: 'application/x-www-form-urlencoded'
|
|
42
|
-
)
|
|
56
|
+
data = market_post(@market_sub_url, "#{ENVData::RVT}&mainKey=#{elem['mainKey']}&usingCleint=0")
|
|
43
57
|
|
|
44
58
|
sleep 1
|
|
45
59
|
|
|
@@ -57,9 +71,7 @@ class MarketSearcher
|
|
|
57
71
|
def construct_item_data(subcategory, all_subcategories)
|
|
58
72
|
aggregate = aggregate_category_data(@market_list_url, @market_search_url, subcategory, all_subcategories)
|
|
59
73
|
|
|
60
|
-
filtered_aggregate = aggregate.
|
|
61
|
-
elem != nil
|
|
62
|
-
end
|
|
74
|
+
filtered_aggregate = aggregate.compact
|
|
63
75
|
|
|
64
76
|
# TODO: this is probably not a smart way to do this type of retry logic
|
|
65
77
|
puts
|
|
@@ -77,13 +89,7 @@ class MarketSearcher
|
|
|
77
89
|
begin
|
|
78
90
|
get_price_data elem
|
|
79
91
|
rescue StandardError => error
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
|
83
|
-
file.write(error.full_message)
|
|
84
|
-
file.write("\n\r")
|
|
85
|
-
end
|
|
86
|
-
|
|
92
|
+
log_error error, "this could be a network failure. construct_item_data broke."
|
|
87
93
|
[]
|
|
88
94
|
end
|
|
89
95
|
end
|
|
@@ -91,9 +97,7 @@ class MarketSearcher
|
|
|
91
97
|
|
|
92
98
|
puts "\n\n" unless mapped_aggregate.empty?
|
|
93
99
|
|
|
94
|
-
mapped_aggregate.
|
|
95
|
-
b['pricePerOne'] - a['pricePerOne']
|
|
96
|
-
end
|
|
100
|
+
mapped_aggregate.sort_by { |a| -a['pricePerOne'] }
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
def aggregate_category_data(url, search_url, subcategory, all_subcategories)
|
|
@@ -110,24 +114,13 @@ class MarketSearcher
|
|
|
110
114
|
category_search_options(url, search_url).each do |category_opts|
|
|
111
115
|
do_if_category_matches(make_match_options.call(category_opts[:name])) do
|
|
112
116
|
begin
|
|
113
|
-
data =
|
|
114
|
-
URI(category_opts[:url]),
|
|
115
|
-
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
116
|
-
body: category_opts[:query_string],
|
|
117
|
-
content_type: 'application/x-www-form-urlencoded'
|
|
118
|
-
)
|
|
117
|
+
data = market_post(category_opts[:url], category_opts[:query_string])
|
|
119
118
|
|
|
120
119
|
sleep 1
|
|
121
120
|
|
|
122
121
|
aggregate_response.push category_opts[:update].call(data) if data
|
|
123
122
|
rescue StandardError => error
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
|
127
|
-
file.write(error.full_message)
|
|
128
|
-
file.write("\n\r")
|
|
129
|
-
end
|
|
130
|
-
|
|
123
|
+
log_error error, "this could be a network failure. aggregate_category_data broke."
|
|
131
124
|
[]
|
|
132
125
|
end
|
|
133
126
|
end
|
|
@@ -146,22 +139,12 @@ class MarketSearcher
|
|
|
146
139
|
ingredient_data = {}
|
|
147
140
|
|
|
148
141
|
begin
|
|
149
|
-
ingredient_data =
|
|
150
|
-
URI(@market_sub_url),
|
|
151
|
-
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
152
|
-
body: "#{ENVData::RVT}&mainKey=#{ingredient_id}&usingCleint=0",
|
|
153
|
-
content_type: 'application/x-www-form-urlencoded'
|
|
154
|
-
)
|
|
142
|
+
ingredient_data = market_post(@market_sub_url, "#{ENVData::RVT}&mainKey=#{ingredient_id}&usingCleint=0")
|
|
155
143
|
|
|
156
144
|
sleep rand
|
|
157
145
|
rescue StandardError => error
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
|
161
|
-
file.write(error.full_message)
|
|
162
|
-
file.write("\n\r")
|
|
146
|
+
log_error error, "this could be a network failure. get_item_price_info broke."
|
|
163
147
|
ingredient_data = {}
|
|
164
|
-
end
|
|
165
148
|
end
|
|
166
149
|
|
|
167
150
|
# TODO: actually implement a normal way of handling malformed results
|
|
@@ -177,31 +160,19 @@ class MarketSearcher
|
|
|
177
160
|
detailed_price_list = {}
|
|
178
161
|
|
|
179
162
|
begin
|
|
180
|
-
detailed_price_list =
|
|
181
|
-
URI(@market_sell_buy_url),
|
|
182
|
-
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
183
|
-
body: body_string,
|
|
184
|
-
content_type: 'application/x-www-form-urlencoded'
|
|
185
|
-
)
|
|
163
|
+
detailed_price_list = market_post(@market_sell_buy_url, body_string)
|
|
186
164
|
|
|
187
165
|
detailed_price_list = {} if detailed_price_list.to_s.downcase.include? 'incapsula incident'
|
|
188
166
|
|
|
189
167
|
sleep rand
|
|
190
168
|
rescue StandardError => error
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
|
194
|
-
file.write(error&.full_message || error)
|
|
195
|
-
file.write("\n\r")
|
|
196
|
-
detailed_price_list = {}
|
|
197
|
-
end
|
|
169
|
+
log_error error, "this could be a network failure. get_item_price_info broke."
|
|
170
|
+
detailed_price_list = {}
|
|
198
171
|
end
|
|
199
172
|
|
|
200
|
-
optimal_prices = detailed_price_list&.dig('marketConditionList')&.
|
|
201
|
-
b['sellCount'] - a['sellCount']
|
|
202
|
-
end
|
|
173
|
+
optimal_prices = detailed_price_list&.dig('marketConditionList')&.sort_by { |p| -p['sellCount'] }
|
|
203
174
|
|
|
204
|
-
total_stock = optimal_prices.to_a.
|
|
175
|
+
total_stock = optimal_prices.to_a.sum { |price| price["sellCount"] }
|
|
205
176
|
|
|
206
177
|
optimal_price = optimal_prices.to_a.first
|
|
207
178
|
|
|
@@ -234,12 +205,10 @@ class MarketSearcher
|
|
|
234
205
|
potential_recipe = []
|
|
235
206
|
|
|
236
207
|
recipe.each.with_index do |ingredient, ing_index|
|
|
237
|
-
ingredient_id = ingredient['id']
|
|
238
|
-
quant
|
|
239
|
-
enhance_level = ingredient['enhance_level']
|
|
240
|
-
is_m_recipe
|
|
241
|
-
quant = 1 if quant.nil?
|
|
242
|
-
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]
|
|
243
212
|
|
|
244
213
|
if @ingredient_cache[ingredient_id] && EXCHANGE_ITEMS[ingredient_id].nil? == true
|
|
245
214
|
cached_ingredient = { **@ingredient_cache[ingredient_id], quant: quant, for_recipe_id: recipe_id, is_m_recipe: is_m_recipe }
|
|
@@ -258,9 +227,7 @@ class MarketSearcher
|
|
|
258
227
|
item_price_info_hash = exchange_info
|
|
259
228
|
end
|
|
260
229
|
|
|
261
|
-
item_price_info = item_price_info_hash.transform_keys { |key|
|
|
262
|
-
key.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
|
|
263
|
-
}
|
|
230
|
+
item_price_info = item_price_info_hash.transform_keys { |key| Utils.snake_sym(key) }
|
|
264
231
|
|
|
265
232
|
# gathering out of stock items to show to the user
|
|
266
233
|
stock_string = "#{quant}x [#{item_price_info[:main_key]}] #{item_price_info[:name].downcase}"
|
|
@@ -313,14 +280,14 @@ class MarketSearcher
|
|
|
313
280
|
mapped_recipe_prices.push map_recipe_prices(potential_recipes, item_with_recipe, subcategory)
|
|
314
281
|
end
|
|
315
282
|
|
|
316
|
-
mapped_recipe_prices.
|
|
283
|
+
mapped_recipe_prices.compact
|
|
317
284
|
end
|
|
318
285
|
|
|
319
286
|
def map_recipe_prices(potential_recipes, item, category)
|
|
320
287
|
average_procs = 1
|
|
321
288
|
|
|
322
289
|
if [25, 35].include? item[:main_category]
|
|
323
|
-
unless /oil of|draught|\[mix\]|\[party\]|immortal:|perfume|indignation|flame of|essence of dawn/im.match item[:name].downcase
|
|
290
|
+
unless /oil of|draught|\[mix\]|\[party\]|immortal:|perfume|indignation|flame of|essence of dawn|mystical cleaning oil|whale tendon|leather glaze/im.match item[:name].downcase
|
|
324
291
|
average_procs = 2.5
|
|
325
292
|
end
|
|
326
293
|
|
|
@@ -369,7 +336,7 @@ class MarketSearcher
|
|
|
369
336
|
# are some seriously mysterious alchemy recipes out there...
|
|
370
337
|
ingredients_already_appeared = []
|
|
371
338
|
filtered_selected_recipe = selected_recipe.filter do |ingredient|
|
|
372
|
-
|
|
339
|
+
next false if ingredients_already_appeared.include? ingredient[:name]
|
|
373
340
|
ingredients_already_appeared.push(ingredient[:name])
|
|
374
341
|
true
|
|
375
342
|
end
|
|
@@ -382,12 +349,7 @@ class MarketSearcher
|
|
|
382
349
|
|
|
383
350
|
body_string = "#{ENVData::RVT}&mainKey=#{item[:id]}&subKey=0&chooseKey=0&isUp=true&keyType=0&name=#{URI.encode_www_form_component(item[:name])}"
|
|
384
351
|
|
|
385
|
-
item_price_data =
|
|
386
|
-
URI(@market_sell_buy_url),
|
|
387
|
-
headers: ENVData.get_central_market_headers(ENVData.get_incap_cookie(@region_subdomain)),
|
|
388
|
-
body: body_string,
|
|
389
|
-
content_type: 'application/x-www-form-urlencoded'
|
|
390
|
-
)
|
|
352
|
+
item_price_data = market_post(@market_sell_buy_url, body_string)
|
|
391
353
|
|
|
392
354
|
item_price_data = {} if item_price_data.to_s.downcase.include? 'incapsula incident'
|
|
393
355
|
|
|
@@ -448,7 +410,6 @@ class MarketSearcher
|
|
|
448
410
|
end
|
|
449
411
|
|
|
450
412
|
def do_if_category_matches(options, &procedure)
|
|
451
|
-
procedure.call if options[:all_subcategories]
|
|
452
|
-
procedure.call if options[:subcategory] == options[:subcat_to_match]
|
|
413
|
+
procedure.call if options[:all_subcategories] || (options[:subcategory] == options[:subcat_to_match])
|
|
453
414
|
end
|
|
454
415
|
end
|
data/lib/utils/array_utils.rb
CHANGED
data/lib/utils/constants.rb
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require 'date'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
require_relative '../../environment'
|
|
6
|
+
|
|
7
|
+
if ENV['rvt_dom'].length == 0
|
|
8
|
+
puts "empty DOM environment variable! set ENV['rvt_dom'] in environment.rb."
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if ENV['rvt_cookie'].length == 0
|
|
12
|
+
puts "empty cookie environment variable! set ENV['rvt_cookie'] in environment.rb."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if ENV['rvt_dom'].length == 0 || ENV['rvt_cookie'].length == 0
|
|
16
|
+
puts ''
|
|
17
|
+
exit
|
|
18
|
+
end
|
|
6
19
|
|
|
7
20
|
module Utils
|
|
8
21
|
# constants to use when user is configuring the tool
|
|
@@ -19,12 +32,14 @@ module Utils
|
|
|
19
32
|
'alchemy stone': "searches 'imperfect alchemy stone of'",
|
|
20
33
|
reagent: "searches 'reagent'",
|
|
21
34
|
'black stone': 'category 30, subcategory 1',
|
|
35
|
+
'food': 'category 35, subcategory 4',
|
|
22
36
|
'misc': 'category 25, subcategory 8',
|
|
23
37
|
'other tools': 'category 40, subcategory 10',
|
|
24
38
|
'manos': "searches 'manos'",
|
|
25
39
|
'purified lightstone': "searches 'purified lightstone of' (requires guru 1 alchemy)",
|
|
26
40
|
'combined crystals': 'category 50, subcategory 4',
|
|
27
41
|
'essences of dawn': "searches 'essence of dawn'",
|
|
42
|
+
# 'stuffed animals': "searches 'stuffed'",
|
|
28
43
|
# 'magic crystal': "searches 'magic crystal'",
|
|
29
44
|
exit: 'stops the search',
|
|
30
45
|
}.freeze
|
|
@@ -42,7 +57,7 @@ module Utils
|
|
|
42
57
|
jp: 'trade.jp.playblackdesert.com',
|
|
43
58
|
th: 'trade.th.playblackdesert.com',
|
|
44
59
|
tw: 'trade.tw.playblackdesert.com',
|
|
45
|
-
sa: '
|
|
60
|
+
sa: 'trade.sa.playblackdesert.com',
|
|
46
61
|
exit: 'stops the search',
|
|
47
62
|
}.freeze
|
|
48
63
|
|
|
@@ -76,10 +91,6 @@ module Utils
|
|
|
76
91
|
no: false,
|
|
77
92
|
yes: true,
|
|
78
93
|
}.freeze
|
|
79
|
-
|
|
80
|
-
def self.set_rvt(rvt)
|
|
81
|
-
$request_verification_token = rvt
|
|
82
|
-
end
|
|
83
94
|
end
|
|
84
95
|
|
|
85
96
|
# constants that will be used in by search / scraping scripts
|
|
@@ -89,8 +100,8 @@ module Utils
|
|
|
89
100
|
ERROR_LOG = './error.log'
|
|
90
101
|
# note that the following two tokens are different!! the first one is from a request cookie
|
|
91
102
|
# the second is from the dom of the actual BDO central market interface
|
|
92
|
-
COOKIE = '
|
|
93
|
-
RVT = '
|
|
103
|
+
COOKIE = ENV['rvt_cookie']
|
|
104
|
+
RVT = ENV['rvt_dom']
|
|
94
105
|
WORLD_MARKET_LIST = '/GetWorldMarketList'
|
|
95
106
|
MARKET_SUB_LIST = '/GetWorldMarketSubList'
|
|
96
107
|
MARKET_SEARCH_LIST = '/GetWorldMarketSearchList'
|
data/lib/utils/hash_cache.rb
CHANGED
|
@@ -23,12 +23,8 @@ module Utils
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def write(data)
|
|
26
|
-
|
|
27
|
-
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
|
data/lib/utils/npc_item_index.rb
CHANGED
|
@@ -75,7 +75,7 @@ module Utils
|
|
|
75
75
|
},
|
|
76
76
|
7304 => {
|
|
77
77
|
name: 'Strawberry',
|
|
78
|
-
id:
|
|
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
|
-
|
|
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
|
-
|
|
486
|
+
sub_category: 5,
|
|
487
487
|
},
|
|
488
488
|
521 => {
|
|
489
489
|
name: 'HP Potion (Medium)',
|
|
490
|
-
id:
|
|
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
|
-
|
|
497
|
+
sub_category: 5,
|
|
498
498
|
},
|
|
499
499
|
519 => {
|
|
500
500
|
name: 'HP Potion (Large)',
|
|
501
|
-
id:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
530
|
+
sub_category: 5,
|
|
531
531
|
},
|
|
532
532
|
522 => {
|
|
533
533
|
name: 'MP Potion (Large)',
|
data/lib/version.rb
CHANGED
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
|
+
version: 0.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- jpegzilla
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
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.
|
|
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: []
|