bdoap 0.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 +7 -0
- data/bin/bdoap +8 -0
- data/lib/bdo_alchemy_profits.rb +94 -0
- data/lib/bdo_codex/bdo_codex_searcher.rb +269 -0
- data/lib/central_market/category_search_options.rb +120 -0
- data/lib/central_market/market_searcher.rb +414 -0
- data/lib/utils/array_utils.rb +21 -0
- data/lib/utils/constants.rb +135 -0
- data/lib/utils/hash_cache.rb +34 -0
- data/lib/utils/npc_item_index.rb +526 -0
- data/lib/utils/price_calculator.rb +26 -0
- data/lib/utils/recipe_logger.rb +71 -0
- data/lib/utils/user_cli.rb +110 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a2c463012b75ab5e6a2ad6d2558aaf0d13d4ca815b0092d7384efa44903c124d
|
4
|
+
data.tar.gz: ffd6889945d22837ba488133f5d364d086352dd945adf95cb4adb6512036963e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf5c5d4011b0febc18953536d8bd94d90fa1df257386685dbce8e6d1dbc745e8ed4afbab2f079be71e1f2d13767802cf8d054ffeb90f2c2342f2dc9093e39c29
|
7
|
+
data.tar.gz: 2c6626ef2c4222dc002db072403d55bec2c06c7b87a860795b739d1b312419283ab6fdc3b6ace8a31f1da9374131a6045d3fd8ce3b2dbd581c40add167cf1ba5
|
data/bin/bdoap
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# MIT License
|
5
|
+
#
|
6
|
+
# Copyright (c) 2025 jpegzilla
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be included in all
|
16
|
+
# copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
|
26
|
+
require 'optparse'
|
27
|
+
|
28
|
+
require_relative './utils/user_cli'
|
29
|
+
require_relative './central_market/market_searcher'
|
30
|
+
require_relative './bdo_codex/bdo_codex_searcher'
|
31
|
+
|
32
|
+
class BDOAlchemyProfits
|
33
|
+
include Utils
|
34
|
+
|
35
|
+
def start_cli
|
36
|
+
options = {}
|
37
|
+
|
38
|
+
OptionParser.new do |opt|
|
39
|
+
opt.on('--silent', '-s') { options[:silent] = true }
|
40
|
+
end.parse!
|
41
|
+
|
42
|
+
cli = UserCLI.new options
|
43
|
+
|
44
|
+
# option setup
|
45
|
+
|
46
|
+
category = cli.choose_category
|
47
|
+
|
48
|
+
cli.end_cli if category == 'exit'
|
49
|
+
|
50
|
+
region = cli.choose_region
|
51
|
+
|
52
|
+
cli.end_cli if region == 'exit'
|
53
|
+
|
54
|
+
lang = cli.choose_lang
|
55
|
+
|
56
|
+
cli.end_cli if lang == 'exit'
|
57
|
+
|
58
|
+
aggression = cli.choose_aggression
|
59
|
+
|
60
|
+
cli.end_cli if aggression == 'exit'
|
61
|
+
|
62
|
+
if aggression == 'hyperaggressive'
|
63
|
+
puts cli.orange("\nWARN: hyperagressive mode is RISKY AND SLOW. this will evaluate every substitution for every recipe. hammers apis violently. you will get rate limited. you will get IP blocked. her royal holiness imperva incapsula WILL get you. select if you know what all that stuff means and you are ok with waiting 20 minutes.")
|
64
|
+
end
|
65
|
+
|
66
|
+
# start searching
|
67
|
+
|
68
|
+
cli.vipiko("\n♫ let's see if #{cli.yellow category} alchemy items are profitable in #{cli.yellow region}!")
|
69
|
+
|
70
|
+
market_searcher = MarketSearcher.new region, cli
|
71
|
+
|
72
|
+
market_item_list = market_searcher.get_alchemy_market_data category
|
73
|
+
|
74
|
+
cli.vipiko("I'll look for #{cli.yellow(market_item_list.length)} item#{
|
75
|
+
market_item_list.empty? || market_item_list.length > 1 ? 's' : ''
|
76
|
+
} in #{category == 'all' ? cli.yellow('all categories'): "the #{cli.yellow category} category"}!")
|
77
|
+
|
78
|
+
bdo_codex_searcher = BDOCodexSearcher.new(region, lang, cli, aggression == 'hyperaggressive')
|
79
|
+
|
80
|
+
item_codex_data = bdo_codex_searcher.get_item_codex_data market_item_list
|
81
|
+
|
82
|
+
recipe_prices = market_searcher.get_all_recipe_prices item_codex_data, category
|
83
|
+
|
84
|
+
mapped_prices = recipe_prices.sort_by { |recipe| recipe[:silver_per_hour].to_i }.map { |recipe| recipe[:information] }
|
85
|
+
|
86
|
+
if mapped_prices.length > 0
|
87
|
+
cli.vipiko_overwrite "done!"
|
88
|
+
puts "\n\n"
|
89
|
+
puts mapped_prices
|
90
|
+
else
|
91
|
+
cli.vipiko_overwrite "none of those recipes look profitable right now...let's go gathering!"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'httparty'
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'awesome_print'
|
7
|
+
|
8
|
+
require_relative '../utils/user_cli'
|
9
|
+
require_relative '../utils/array_utils'
|
10
|
+
require_relative '../utils/hash_cache'
|
11
|
+
|
12
|
+
# some variables that are necessary for parsing bdocodex data
|
13
|
+
class BDO_CODEX_UTILS
|
14
|
+
BDOCODEX_QUERY_DATA_KEY = 'aaData'
|
15
|
+
RECIPE_COLUMNS = [
|
16
|
+
'id',
|
17
|
+
'icon',
|
18
|
+
'title',
|
19
|
+
'type',
|
20
|
+
'skill level',
|
21
|
+
'exp',
|
22
|
+
'materials',
|
23
|
+
'total weight of materials',
|
24
|
+
'products',
|
25
|
+
'all ingredients',
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
# used to retrieve items from BDOCodex
|
30
|
+
class BDOCodexSearcher
|
31
|
+
include Utils
|
32
|
+
|
33
|
+
RECIPE_INGREDIENTS_INDEX = 2
|
34
|
+
|
35
|
+
def initialize(region, lang, cli, hyper_aggressive = false)
|
36
|
+
@region = region
|
37
|
+
@root_url = ENVData.get_root_url region
|
38
|
+
@cli = cli
|
39
|
+
@region_lang = lang
|
40
|
+
@cache = HashCache.new ENVData::BDO_CODEX_CACHE
|
41
|
+
@hyper_aggressive = hyper_aggressive
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_recipe_url(url)
|
45
|
+
begin
|
46
|
+
data = HTTParty.get(
|
47
|
+
URI(url),
|
48
|
+
headers: ENVData::REQUEST_OPTS[:bdo_codex_headers],
|
49
|
+
content_type: 'application/x-www-form-urlencoded'
|
50
|
+
)
|
51
|
+
|
52
|
+
JSON.parse data unless data.body.nil? or data.body.empty?
|
53
|
+
rescue
|
54
|
+
{}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: is there a way to get rid of all these nested loops in this class?
|
59
|
+
def get_recipe_substitutions(recipe_with_substitute_ids, all_potion_recipes, name)
|
60
|
+
all_recipe_substitutions = []
|
61
|
+
all_potion_recipes.each do |recipe|
|
62
|
+
original_recipe = recipe[RECIPE_INGREDIENTS_INDEX]
|
63
|
+
original_ingredient_indices = original_recipe.map do |item|
|
64
|
+
recipe_with_substitute_ids.find_index { |id| id == item[:id] }
|
65
|
+
end
|
66
|
+
|
67
|
+
chunked_by_substitution_groups = []
|
68
|
+
|
69
|
+
original_ingredient_indices.each.with_index do |_arr_index, idx|
|
70
|
+
slice_from = [0, original_ingredient_indices[idx].to_i].max
|
71
|
+
slice_to = slice_from + 1
|
72
|
+
slice_to = original_ingredient_indices[idx + 1] if @hyper_aggressive
|
73
|
+
# set hyper_aggressive to true if you want to check every
|
74
|
+
# permutation of this recipe, with all substitutions considered
|
75
|
+
# this will be exceedingly slow and generate hundreds and hundreds
|
76
|
+
# of post requests, hammering the black desert market api and
|
77
|
+
# potentially causing incapsula to GET YOU (block your IP)
|
78
|
+
|
79
|
+
chunked_by_substitution_groups.push(
|
80
|
+
recipe_with_substitute_ids[slice_from..(slice_to ? slice_to - 1 : -1)]
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
original_recipe_length = original_recipe.length
|
85
|
+
permutated_chunks = ArrayUtils.deep_permute chunked_by_substitution_groups, original_recipe_length
|
86
|
+
|
87
|
+
permutated_chunks.each do |id_list|
|
88
|
+
recipe_with_new_items = [*recipe]
|
89
|
+
recipe_with_new_items[RECIPE_INGREDIENTS_INDEX] = [*recipe][RECIPE_INGREDIENTS_INDEX].map.with_index do |recipe_list, idx|
|
90
|
+
{ **recipe_list, id: id_list[idx] }
|
91
|
+
end
|
92
|
+
|
93
|
+
all_recipe_substitutions.push recipe_with_new_items
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# 1 in elem[1] is the index of the recipe name
|
98
|
+
all_recipe_substitutions.filter { |elem| elem[1].downcase == name.downcase }
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_raw_recipe(recipe_data, item_name)
|
102
|
+
item_with_ingredients = recipe_data.dig(BDO_CODEX_UTILS::BDOCODEX_QUERY_DATA_KEY)
|
103
|
+
|
104
|
+
if item_with_ingredients
|
105
|
+
recipe_with_substitute_ids = item_with_ingredients.map do |arr|
|
106
|
+
arr.filter.with_index { |_, idx| idx == 9 }
|
107
|
+
.map { |item| JSON.parse(item) }
|
108
|
+
.first
|
109
|
+
end.first
|
110
|
+
|
111
|
+
all_potion_recipes = item_with_ingredients.map do |arr|
|
112
|
+
mapped_item_data = arr
|
113
|
+
.filter.with_index { |_, idx | !BDO_CODEX_UTILS::RECIPE_COLUMNS[idx].nil? }
|
114
|
+
.map.with_index do |raw_element, idx|
|
115
|
+
category = BDO_CODEX_UTILS::RECIPE_COLUMNS[idx]
|
116
|
+
|
117
|
+
next if ['skill level', 'exp', 'type', 'icon', 'total weight of materials'].include? category
|
118
|
+
|
119
|
+
element = Nokogiri::HTML5 raw_element.to_s
|
120
|
+
|
121
|
+
result = {
|
122
|
+
:category => category,
|
123
|
+
:element => element
|
124
|
+
}
|
125
|
+
|
126
|
+
result[:element] = element.text.downcase if category == 'title'
|
127
|
+
|
128
|
+
result[:element] = element.text.downcase if category == 'id'
|
129
|
+
|
130
|
+
if %w[materials products].include? category
|
131
|
+
quants = element.text.scan(/\](\d+)/im).map { |e| e[0].to_i }
|
132
|
+
|
133
|
+
ids = element.to_s.scan(/#{@region_lang}\/item\/(\d+)/).map { |e| e[0].to_i }
|
134
|
+
result[:element] = ids.map.with_index { |id, i| { id: id, quant: quants[i]} }.flatten
|
135
|
+
end
|
136
|
+
|
137
|
+
result
|
138
|
+
end
|
139
|
+
|
140
|
+
filtered_item_data = mapped_item_data
|
141
|
+
.compact
|
142
|
+
.filter { |e| %w[id title materials products].include? e[:category] }
|
143
|
+
.map { |e| e[:element] }
|
144
|
+
|
145
|
+
filtered_item_data
|
146
|
+
end
|
147
|
+
|
148
|
+
get_recipe_substitutions recipe_with_substitute_ids, all_potion_recipes, item_name
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_item_recipes(item_id, item_name)
|
153
|
+
recipe_direct_url = "https://bdocodex.com/query.php?a=recipes&type=product&item_id=#{item_id}&l=#{@region_lang}"
|
154
|
+
mrecipe_direct_url = "https://bdocodex.com/query.php?a=mrecipes&type=product&item_id=#{item_id}&l=#{@region_lang}"
|
155
|
+
# houserecipe_direct_url = "https://bdocodex.com/query.php?a=designs&type=product&item_id=#{item_id}&l=#{@region_lang}"
|
156
|
+
|
157
|
+
# TODO: there MUST be a better way to determine which recipe to use, rather than just trying them both.
|
158
|
+
begin
|
159
|
+
direct_data = get_recipe_url recipe_direct_url
|
160
|
+
|
161
|
+
if !direct_data || direct_data.empty?
|
162
|
+
mrecipe_data = get_recipe_url mrecipe_direct_url
|
163
|
+
|
164
|
+
return parse_raw_recipe mrecipe_data, item_name
|
165
|
+
else
|
166
|
+
parsed = parse_raw_recipe direct_data, item_name
|
167
|
+
|
168
|
+
if !parsed || parsed.empty?
|
169
|
+
mrecipe_data = get_recipe_url mrecipe_direct_url
|
170
|
+
|
171
|
+
return parse_raw_recipe mrecipe_data, item_name
|
172
|
+
end
|
173
|
+
|
174
|
+
parsed
|
175
|
+
end
|
176
|
+
rescue StandardError => error
|
177
|
+
puts @cli.red("if you're not messing with the code, you should never see this. get_item_recipes broke.")
|
178
|
+
|
179
|
+
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
180
|
+
file.write(error.full_message)
|
181
|
+
file.write("\n\r")
|
182
|
+
end
|
183
|
+
|
184
|
+
[]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# m_recipes_first may be useful if a lot of recipes aren't working
|
189
|
+
# refer to the original javascript implementation of
|
190
|
+
# searchCodexForRecipes
|
191
|
+
# m_recipes_first should be used to hit the /mrecipe version of a recipe
|
192
|
+
# for manufacturing-type recipes
|
193
|
+
def search_codex_for_recipes(item, m_recipes_first)
|
194
|
+
item_id = item[:main_key]
|
195
|
+
item_name = item[:name]
|
196
|
+
item_index = "#{item_id} #{item[:name]}"
|
197
|
+
potential_cached_recipes = @cache.read item_index
|
198
|
+
cache_data = {}
|
199
|
+
|
200
|
+
# TODO: remove this check
|
201
|
+
# return unless item_name.downcase == 'harmony draught'
|
202
|
+
|
203
|
+
unless potential_cached_recipes.to_a.empty?
|
204
|
+
recipe_to_maybe_select = potential_cached_recipes.filter { |elem| elem[1].downcase == item_name.downcase }
|
205
|
+
return recipe_to_maybe_select unless recipe_to_maybe_select.empty?
|
206
|
+
end
|
207
|
+
|
208
|
+
recipes = get_item_recipes item_id, item_name
|
209
|
+
cache_data[item_index] = recipes
|
210
|
+
|
211
|
+
@cache.write cache_data
|
212
|
+
|
213
|
+
recipes
|
214
|
+
end
|
215
|
+
|
216
|
+
# pass in a list of items as retrieved from the central market API
|
217
|
+
def get_item_codex_data(item_list)
|
218
|
+
recipes = []
|
219
|
+
|
220
|
+
# newline because vipiko is about to start carriage returning
|
221
|
+
puts "\n"
|
222
|
+
item_list.each.with_index do |item_hash, index|
|
223
|
+
item = item_hash.transform_keys { |key|
|
224
|
+
key.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
|
225
|
+
}
|
226
|
+
|
227
|
+
next unless item[:main_key]
|
228
|
+
|
229
|
+
begin
|
230
|
+
search_results = search_codex_for_recipes item, false
|
231
|
+
|
232
|
+
if search_results
|
233
|
+
# res[0] is the recipe ID
|
234
|
+
all_recipes_for_item = search_results.map { |res|
|
235
|
+
recipe_array = [res[0], res[RECIPE_INGREDIENTS_INDEX]]
|
236
|
+
recipe_array
|
237
|
+
}
|
238
|
+
|
239
|
+
@cli.vipiko_overwrite "(#{index + 1} / #{item_list.length}) let's read the recipe for #{@cli.yellow "[#{item[:name].downcase}]"}. hmm..."
|
240
|
+
|
241
|
+
stock_count = item[:total_in_stock].to_i.zero? ? item[:count].to_i : item[:total_in_stock].to_i
|
242
|
+
recipe_hash = {
|
243
|
+
name: item[:name],
|
244
|
+
recipe_list: all_recipes_for_item,
|
245
|
+
price: item[:price_per_one],
|
246
|
+
id: item[:main_key],
|
247
|
+
total_trade_count: item[:total_trade_count],
|
248
|
+
total_in_stock: stock_count,
|
249
|
+
main_category: item[:main_category],
|
250
|
+
sub_category: item[:sub_category]
|
251
|
+
}
|
252
|
+
|
253
|
+
recipes.push recipe_hash
|
254
|
+
end
|
255
|
+
rescue StandardError => error
|
256
|
+
puts @cli.red("if you're not messing with the code, you should never see this. get_item_codex_data broke.")
|
257
|
+
|
258
|
+
File.open(ENVData::ERROR_LOG, 'a+') do |file|
|
259
|
+
file.write(error.full_message)
|
260
|
+
file.write("\n\r")
|
261
|
+
end
|
262
|
+
|
263
|
+
next
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
recipes
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../utils/constants'
|
4
|
+
|
5
|
+
module MarketSearchTools
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
CONSUMABLE_CATEGORY = 35
|
9
|
+
CONSUMABLE_SUBCATEGORIES = {
|
10
|
+
offensive: 1,
|
11
|
+
defensive: 2,
|
12
|
+
functional: 3,
|
13
|
+
potion: 5,
|
14
|
+
other: 8,
|
15
|
+
all: [1, 2, 3, 5, 8]
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# get information used for searching specific categories
|
19
|
+
def category_search_options(url, search_url) # rubocop:disable Metrics/AbcSize
|
20
|
+
# TODO: there's probably a smart / concise way to construct this array
|
21
|
+
[
|
22
|
+
{
|
23
|
+
name: 'black stone',
|
24
|
+
url: url,
|
25
|
+
query_string: "#{ENVData::RVT}&mainCategory=30&subCategory=1",
|
26
|
+
# update: ->(data) { { blackStoneResponse: data['list'] } },
|
27
|
+
update: ->(data) { data['list'] }
|
28
|
+
},
|
29
|
+
{
|
30
|
+
name: 'misc',
|
31
|
+
url: url,
|
32
|
+
query_string: "#{ENVData::RVT}&mainCategory=25&subCategory=8",
|
33
|
+
# update: ->(data) { { blackStoneResponse: data['list'] } },
|
34
|
+
update: ->(data) { data['list'] }
|
35
|
+
},
|
36
|
+
{
|
37
|
+
name: 'other tools',
|
38
|
+
url: url,
|
39
|
+
query_string: "#{ENVData::RVT}&mainCategory=40&subCategory=10",
|
40
|
+
# update: ->(data) { { blackStoneResponse: data['list'] } },
|
41
|
+
update: ->(data) { data['list'] }
|
42
|
+
},
|
43
|
+
{
|
44
|
+
name: 'blood',
|
45
|
+
url: search_url,
|
46
|
+
query_string: "#{ENVData::RVT}&searchText='s+blood",
|
47
|
+
# update: ->(data) { { bloodResponse: data['list'] } },
|
48
|
+
update: ->(data) { data['list'] }
|
49
|
+
},
|
50
|
+
{
|
51
|
+
name: 'reagent',
|
52
|
+
url: search_url,
|
53
|
+
query_string: "#{ENVData::RVT}&searchText=reagent",
|
54
|
+
# update: ->(data) { { reagentResponse: data['list'] } },
|
55
|
+
update: ->(data) { data['list'] }
|
56
|
+
},
|
57
|
+
{
|
58
|
+
name: 'oil',
|
59
|
+
url: search_url,
|
60
|
+
query_string: "#{ENVData::RVT}&searchText=oil+of",
|
61
|
+
# update: ->(data) { { oilResponse: data['list'] } },
|
62
|
+
update: ->(data) { data['list'] }
|
63
|
+
},
|
64
|
+
{
|
65
|
+
name: 'alchemy stone',
|
66
|
+
url: search_url,
|
67
|
+
query_string: "#{ENVData::RVT}&searchText=stone+of",
|
68
|
+
# update: ->(data) { { alchemyStoneResponse: data['list'] } },
|
69
|
+
update: ->(data) { data['list'].filter { |i| i['grade'] == 0 } }
|
70
|
+
},
|
71
|
+
# {
|
72
|
+
# name: 'magic crystal',
|
73
|
+
# url: search_url,
|
74
|
+
# query_string: "#{ENVData::RVT}&searchText=magic+crystal",
|
75
|
+
# # update: ->(data) { { magicCrystalResponse: data['list'] } },
|
76
|
+
# update: ->(data) { data['list'] }
|
77
|
+
# },
|
78
|
+
{
|
79
|
+
name: 'offensive',
|
80
|
+
url: url,
|
81
|
+
query_string:
|
82
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:offensive]}",
|
83
|
+
# update: ->(data) { { offensiveResponse: data['marketList'] } },
|
84
|
+
update: ->(data) { data['marketList'] }
|
85
|
+
},
|
86
|
+
{
|
87
|
+
name: 'defensive',
|
88
|
+
url: url,
|
89
|
+
query_string:
|
90
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:defensive]}",
|
91
|
+
# update: ->(data) { { defensiveResponse: data['marketList'] } },
|
92
|
+
update: ->(data) { data['marketList'] }
|
93
|
+
},
|
94
|
+
{
|
95
|
+
name: 'functional',
|
96
|
+
url: url,
|
97
|
+
query_string:
|
98
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:functional]}",
|
99
|
+
# update: ->(data) { { functionalResponse: data['marketList'] } },
|
100
|
+
update: ->(data) { data['marketList'] }
|
101
|
+
},
|
102
|
+
{
|
103
|
+
name: 'potion',
|
104
|
+
url: url,
|
105
|
+
query_string:
|
106
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:potion]}",
|
107
|
+
# update: ->(data) { { potionResponse: data['marketList'] } },
|
108
|
+
update: ->(data) { data['marketList'] }
|
109
|
+
},
|
110
|
+
{
|
111
|
+
name: 'other',
|
112
|
+
url: url,
|
113
|
+
query_string:
|
114
|
+
"#{ENVData::RVT}&mainCategory=#{CONSUMABLE_CATEGORY}&subCategory=#{CONSUMABLE_SUBCATEGORIES[:other]}",
|
115
|
+
# update: ->(data) { { otherResponse: data['marketList'] } },
|
116
|
+
update: ->(data) { data['marketList'] }
|
117
|
+
}
|
118
|
+
]
|
119
|
+
end
|
120
|
+
end
|