From 63bc75ccc33d251847368b8e54bc6d0e1365bcf7 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Thu, 27 Aug 2026 21:40:06 +0300 Subject: [PATCH 1/5] Port "Use Currency Exchange API instead of poe.ninja in trader" #10077 --- spec/System/TestTradeQueryCurrency_spec.lua | 231 ++++++++-- src/Classes/TradeQuery.lua | 246 ++++++----- src/Data/CurrencyNames.lua | 457 ++++++++++++++++++++ src/Export/Scripts/miscdata.lua | 9 + 4 files changed, 799 insertions(+), 144 deletions(-) create mode 100644 src/Data/CurrencyNames.lua diff --git a/spec/System/TestTradeQueryCurrency_spec.lua b/spec/System/TestTradeQueryCurrency_spec.lua index fe6de69d40..6d61a0e1c8 100644 --- a/spec/System/TestTradeQueryCurrency_spec.lua +++ b/spec/System/TestTradeQueryCurrency_spec.lua @@ -9,7 +9,8 @@ describe("TradeQuery Currency Conversion", function() -- Pass: Calculates price in divs -- Fail: Wrong value or nil, indicating broken rounding/baseline logic it("handles chaos currency", function() - mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 0.1 } } + mock_tradeQuery.pbCurrencyConversion = { realm = { league = { chaos = 0.1 } } } + mock_tradeQuery.pbRealm = "realm" mock_tradeQuery.pbLeague = "league" local result = mock_tradeQuery:ConvertCurrencyToDivs("chaos", 5) assert.are.equal(result, 0.5) @@ -23,40 +24,202 @@ describe("TradeQuery Currency Conversion", function() end) end) - describe("PriceBuilderProcessPoENinjaResponse", function() - -- Pass: Processes without error, restoring map while adding a notice - -- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions - it("handles empty response", function() - local orig_conv = mock_tradeQuery.currencyConversionTradeMap - mock_tradeQuery.currencyConversionTradeMap = { div = "id" } - mock_tradeQuery.pbLeague = "league" - mock_tradeQuery.pbCurrencyConversion = { league = {} } - mock_tradeQuery.controls.pbNotice = { label = "" } - local resp = { lines = { }} - mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp.lines) - -- No crash expected - assert.is_true(true) - assert.is_true(mock_tradeQuery.controls.pbNotice.label == "No currencies received from PoE Ninja") - mock_tradeQuery.currencyConversionTradeMap = orig_conv + it("applies the Stat Value fallback when currency rates are missing", function() + mock_tradeQuery.sortModes = { Price = "price", StatValue = "stat" } + mock_tradeQuery.itemSortSelectionList = { "price" } + mock_tradeQuery.pbItemSortSelectionIndex = 1 + mock_tradeQuery.resultTbl = { [1] = { { currency = "chaos", amount = 1 } } } + mock_tradeQuery.sortedResultTbl = {} + mock_tradeQuery.itemIndexTbl = {} + mock_tradeQuery.totalPrice = {} + mock_tradeQuery.controls.pbNotice = {} + mock_tradeQuery.controls.priceButton1 = {} + mock_tradeQuery.controls.fullPrice = {} + mock_tradeQuery.SortFetchResults = function(_, _, mode) + if mode == "price" then + return nil, "MissingConversionRates" + end + return { { index = 1 } } + end + mock_tradeQuery.UpdateDropdownList = function() end + + mock_tradeQuery:UpdateControlsWithItems(1) + + assert.are.equals(1, mock_tradeQuery.sortedResultTbl[1][1].index) + assert.are.equals(1, mock_tradeQuery.itemIndexTbl[1]) + end) + + + describe("PullCXData", function() + local dkjson = require("dkjson") + + -- base type ids the currency exchange uses + local DIVINE = "Metadata/Items/Currency/CurrencyModValues" + local CHAOS = "Metadata/Items/Currency/CurrencyRerollRare" + local EXALT = "Metadata/Items/Currency/CurrencyAddModToRare" + local ALCH = "Metadata/Items/Currency/CurrencyUpgradeToRare" + + -- static trade data: maps display name -> short trade id + local static = { + result = { { + entries = { + { id = "sep", text = "" }, -- separator, must be ignored + { id = "divine", text = "Divine Orb" }, + { id = "chaos", text = "Chaos Orb" }, + { id = "exalt", text = "Exalted Orb" }, + { id = "alch", text = "Orb of Alchemy" }, + } + } } + } + + local origDownloadPage, cxResponse + + before_each(function() + mock_tradeQuery.pbRealm = "poe2" + mock_tradeQuery.controls.pbNotice = {} + origDownloadPage = launch.DownloadPage + cxResponse = nil + launch.DownloadPage = function(_, url, callback) + if url == "https://www.pathofexile.com/api/trade2/data/static" then + callback({ body = dkjson.encode(static) }) + elseif cxResponse then + callback({ body = dkjson.encode(cxResponse) }) + end + end end) - -- Pass: Processes without error, restoring map while adding a notice - -- Fail: Corrupts map or crashes, indicating fragile API response handling, breaking future conversions - it("handles empty response", function() - local orig_conv = mock_tradeQuery.currencyConversionTradeMap - mock_tradeQuery.currencyConversionTradeMap = { div = "id" } - mock_tradeQuery.pbLeague = "league" - mock_tradeQuery.pbCurrencyConversion = { league = {} } - mock_tradeQuery.controls.pbNotice = { label = "" } - local resp = { lines = { { malformedLine = "lol"} }} - mock_tradeQuery:PriceBuilderProcessPoENinjaResponse(resp.lines) - -- No crash expected - assert.is_true(true) - assert.is_true(mock_tradeQuery.controls.pbNotice.label == "Currencies not updated: malformed PoE Ninja response") - mock_tradeQuery.currencyConversionTradeMap = orig_conv + after_each(function() + launch.DownloadPage = origDownloadPage end) - end) + it("fetches currency exchange data without authentication", function() + local requestedUrl + local requestedParams + launch.DownloadPage = function(_, url, callback, params) + if url == "https://www.pathofexile.com/api/trade2/data/static" then + callback({ body = dkjson.encode(static) }) + else + requestedUrl = url + requestedParams = params + callback({ body = dkjson.encode({ markets = {} }) }) + end + end + + mock_tradeQuery.pbRealm = "poe2" + mock_tradeQuery:PullCXData() + + assert.is_truthy(requestedUrl:match("^https://web%.poecdn%.com/api/currency%-exchange/poe2/%d+$")) + assert.is_nil(requestedParams) + end) + + it("waits until a realm is selected", function() + local fetched = false + mock_tradeQuery.pbRealm = "" + launch.DownloadPage = function() + fetched = true + end + + mock_tradeQuery:PullCXData() + + assert.is_false(fetched) + end) + + -- Provide the currency exchange response returned by the download mock. + local function mockCX(markets) + cxResponse = { markets = markets } + end + + it("converts a chained market to divine values", function() + mockCX({ + -- exalt -> divine directly (1 exalt = 0.1 div) + { + league = "Standard", + market_pair = { EXALT, DIVINE }, + lowest_ratio = { [EXALT] = 10, [DIVINE] = 1 }, + highest_stock = { [EXALT] = 100, [DIVINE] = 100 }, + }, + -- chaos -> divine directly (1 chaos = 0.005 div) + { + league = "Standard", + market_pair = { CHAOS, DIVINE }, + lowest_ratio = { [CHAOS] = 200, [DIVINE] = 1 }, + highest_stock = { [CHAOS] = 500, [DIVINE] = 500 }, + }, + -- alch -> chaos (1 alch = 0.2 chaos), needs a second hop to divine + { + league = "Standard", + market_pair = { ALCH, CHAOS }, + lowest_ratio = { [ALCH] = 5, [CHAOS] = 1 }, + highest_stock = { [ALCH] = 1000, [CHAOS] = 1000 }, + }, + }) + + mock_tradeQuery:PullCXData() + + local rates = mock_tradeQuery.pbCurrencyConversion.poe2.Standard + assert.is_not_nil(rates) + assert.are.equal(1, rates.divine) + assert.are.equal(0.1, rates.exalt) + assert.are.equal(0.005, rates.chaos) + -- 0.2 chaos * 0.005 div/chaos = 0.001 div + assert.are.equal(0.001, rates.alch) + end) + + it("keeps the highest-stock listing for a currency", function() + mockCX({ + { + league = "Standard", + market_pair = { EXALT, DIVINE }, + lowest_ratio = { [EXALT] = 10, [DIVINE] = 1 }, -- 0.1 div + highest_stock = { [EXALT] = 50, [DIVINE] = 5 }, + }, + { + league = "Standard", + market_pair = { EXALT, DIVINE }, + lowest_ratio = { [EXALT] = 5, [DIVINE] = 1 }, -- 0.2 div + highest_stock = { [EXALT] = 200, [DIVINE] = 40 }, -- more stock, wins + }, + }) + + mock_tradeQuery:PullCXData() + + assert.are.equal(0.2, mock_tradeQuery.pbCurrencyConversion.poe2.Standard.exalt) + end) + + it("skips listings with a zero ratio", function() + mockCX({ + { + league = "Standard", + market_pair = { EXALT, DIVINE }, + lowest_ratio = { [EXALT] = 0, [DIVINE] = 1 }, + highest_stock = { [EXALT] = 100, [DIVINE] = 100 }, + }, + }) + + mock_tradeQuery:PullCXData() + + -- league had no usable listings, so it should not appear + assert.is_nil(mock_tradeQuery.pbCurrencyConversion.poe2.Standard) + end) + it("shows a notice on an API error response", function() + cxResponse = { error = { message = "kaput" } } + + mock_tradeQuery:PullCXData() + + assert.are.equal("CX error: kaput", mock_tradeQuery.controls.pbNotice.label) + assert.is_nil(mock_tradeQuery.pbCurrencyConversion.poe2) + end) + + it("does not refetch within the rate-limit window", function() + mock_tradeQuery.pbCurrencyConversion.poe2 = { timestamp = os.time() } + local fetched = false + launch.DownloadPage = function() fetched = true end + + mock_tradeQuery:PullCXData() + + assert.is_false(fetched) + end) + end) describe("GetTotalPriceString", function() -- Pass: Sums and formats correctly (e.g., "5 chaos, 10 div", should be most valuable currency first) -- Fail: Wrong string (e.g., unsorted/missing sums), indicating aggregation bug, misleading users on totals @@ -67,14 +230,14 @@ describe("TradeQuery Currency Conversion", function() assert.are.equal(result, "1 exalted, 10 div, 5 chaos") -- check if they're sorted according to currency value + mock_tradeQuery.pbRealm = "realm" mock_tradeQuery.pbLeague = "league" - mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 0.1, exalted = 0.05, div = 1, mirror = 700} } + mock_tradeQuery.pbCurrencyConversion = { realm = { league = { chaos = 0.1, exalted = 0.05, div = 1, mirror = 700 } } } local result = mock_tradeQuery:GetTotalPriceString() assert.are.equal(result, "10 div, 5 chaos, 1 exalted") -- check that missing currency values don't crash - mock_tradeQuery.pbLeague = "league" - mock_tradeQuery.pbCurrencyConversion = { league = { chaos = 0.1, exalted = 0.05, mirror = 700 } } + mock_tradeQuery.pbCurrencyConversion = { realm = { league = { chaos = 0.1, exalted = 0.05, mirror = 700 } } } local result = mock_tradeQuery:GetTotalPriceString() assert.True(true) end) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 89a722e675..c41147d238 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -38,10 +38,9 @@ function TradeQueryClass:TradeQuery(itemsTab) -- default set of trade item sort selection self.slotTables = { } self.pbItemSortSelectionIndex = 1 - -- for each league, a table of values of each currency in div - --- @type table> - self.pbCurrencyConversion = { } - self.lastCurrencyConversionRequest = 0 + -- for each realm and league, a table of values of each currency in div + --- @type table>> + self.pbCurrencyConversion = {} self.lastCurrencyFileTime = { } self.pbFileTimestampDiff = { } self.pbRealm = "" @@ -98,7 +97,6 @@ function TradeQueryClass:PullLeagueList() self.controls.league:SetList(self.itemsTab.leagueDropList) self.controls.league.selIndex = 1 self.pbLeague = self.itemsTab.leagueDropList[self.controls.league.selIndex] - self:SetCurrencyConversionButton() end end) end @@ -107,77 +105,148 @@ end --- @param amount integer --- @return number? function TradeQueryClass:ConvertCurrencyToDivs(currencyId, amount) - local map = self.pbCurrencyConversion[self.pbLeague] + local map = self.pbCurrencyConversion[self.pbRealm] and self.pbCurrencyConversion[self.pbRealm][self.pbLeague] if map and map[currencyId] then return amount * map[currencyId] end end --- Method to pull down and interpret the PoE.Ninja JSON endpoint data ---- @param league string -function TradeQueryClass:PullPoENinjaCurrencyConversion(league) +local generalCurrencies = { + ["Metadata/Items/Currency/CurrencyModValues"] = true, + ["Metadata/Items/Currency/CurrencyRerollRare"] = true +} + +-- Method to pull down and interpret the Currency Exchange JSON endpoint data +function TradeQueryClass:PullCXData() + local realm = self.pbRealm + if realm == "" then + return + end local now = get_time() - -- Limit PoE Ninja Currency Conversion request to 1 per hour - if (now - self.lastCurrencyConversionRequest) < 3600 then - self:SetNotice(self.controls.pbNotice, "PoE Ninja Rate Limit Exceeded: " .. tostring(3600 - (now - self.lastCurrencyConversionRequest))) + -- Limit Currency Conversion request to 1 per hour + if self.pbCurrencyConversion[realm] and ((now - self.pbCurrencyConversion[realm].timestamp) < 61 * 60) then return end - self.pbCurrencyConversion[league] = { } - self.lastCurrencyConversionRequest = now - launch:DownloadPage( - "https://poe.ninja/poe2/api/economy/exchange/current/overview?type=Currency&league=" .. urlEncode(league), - function(response, errMsg) + -- download json containing short names for each item id + launch:DownloadPage("https://www.pathofexile.com/api/trade2/data/static", function(response, errMsg) + if errMsg then + self:SetNotice(self.controls.pbNotice, "Error: " .. tostring(errMsg)) + return + end + + local static = dkjson.decode(response.body) + if not static then + self:SetNotice(self.controls.pbNotice, "Could not decode static trade data") + return + end + local url = "https://web.poecdn.com/api/currency-exchange" + if realm ~= "pc" then + url = url .. "/" .. realm + end + local hourSeconds = 60 * 60 + url = url .. "/" .. ((math.floor(now / hourSeconds) - 1) * hourSeconds) + launch:DownloadPage(url, function(response, errMsg) if errMsg then self:SetNotice(self.controls.pbNotice, "Error: " .. tostring(errMsg)) return end - local json_data = dkjson.decode(response.body) - if not json_data or not json_data.lines then - self:SetNotice(self.controls.pbNotice, "Failed to Get PoE Ninja response") + local json = dkjson.decode(response.body) + if not json then + self:SetNotice(self.controls.pbNotice, "Malformed CX API response") return end - if not self:PriceBuilderProcessPoENinjaResponse(json_data.lines) then - -- don't edit json on failure + if json.error then + self:SetNotice(self.controls.pbNotice, "CX error: " .. json.error.message) return end - local print_str = "" - for key, value in pairs(self.pbCurrencyConversion[self.pbLeague]) do - print_str = print_str .. '"'..key..'": '..tostring(value)..',' - end - local foo = io.open("../"..self.pbLeague.."_currency_values.json", "w") - foo:write("{" .. print_str .. '"updateTime": ' .. tostring(get_time()) .. "}") - foo:close() - self:SetCurrencyConversionButton() - end) + local success, result = pcall(function() + -- short currency names for each base item type id + local currencyNames = {} + local currencyIdMap = {} + for id, name in pairs(require("Data.CurrencyNames")) do + currencyIdMap[name] = id + end + for _, entry in ipairs(static.result[1].entries) do + if entry.id ~= "sep" then + local itemID = currencyIdMap[entry.text] + -- Not every bulk trade item is exported as currency. + if itemID then + currencyNames[itemID] = entry.id + end + end + end -end + local out = {} + for _, entry in ipairs(json.markets) do + local league = entry.league + if not out[league] then + out[league] = {} + end + local leagueOut = out[league] --- Method to process the PoE.Ninja response ---- @param responseLines table[] ---- @return bool -function TradeQueryClass:PriceBuilderProcessPoENinjaResponse(responseLines) - -- Populate the divine-converted values for each tradeId - for _, currencyDetails in ipairs(responseLines) do - -- these use the same ids as the trade site, which are also short - -- readable names, like "transmute" or "aug", which means there's no - -- need for conversion. - local id = currencyDetails.id - -- poe.ninja uses divs as the primary currency, and as far as I know, - -- this figure is equivalent to the best ratio in equivalent divs - local divs = currencyDetails.primaryValue - if not id or not divs then - self:SetNotice(self.controls.pbNotice, "Currencies not updated: malformed PoE Ninja response") - return false - end - self.pbCurrencyConversion[self.pbLeague][id] = divs - end - -- if nothing was actually found, we should add a notice - if next(self.pbCurrencyConversion[self.pbLeague]) == nil then - self:SetNotice(self.controls.pbNotice, "No currencies received from PoE Ninja") - return false - end - return true + -- Base type IDs are in the form Metadata/Items/.../CurrencyModValues. + local fromID = entry.market_pair[1] + local toID = entry.market_pair[2] + + -- Normalize entries to price each currency in chaos or divines. + if generalCurrencies[fromID] and toID ~= "Metadata/Items/Currency/CurrencyModValues" then + fromID, toID = toID, fromID + end + + local fromShort = currencyNames[fromID] + local toShort = currencyNames[toID] + if not fromShort or not generalCurrencies[toID] or entry.lowest_ratio[fromID] == 0 then + goto CXContinue + end + + local newEntry = { + currency = toShort, + price = entry.lowest_ratio[toID] / entry.lowest_ratio[fromID], + stock = entry.highest_stock[fromID] + } + -- Only keep the most popular option. + if not leagueOut[fromShort] or leagueOut[fromShort].stock < newEntry.stock then + leagueOut[fromShort] = newEntry + end + ::CXContinue:: + end + + -- Convert any chaos prices to divine equivalent prices. + for leagueName, leagueEntries in pairs(out) do + for from, to in pairs(leagueEntries) do + if to.currency ~= "divine" then + local divEntry = leagueEntries[to.currency] + if not divEntry then + leagueEntries[from] = nil + else + leagueEntries[from] = divEntry.price * to.price + end + end + end + for from, to in pairs(leagueEntries) do + if type(to) == "table" then + leagueEntries[from] = to.price + end + end + if not next(leagueEntries) then + out[leagueName] = nil + else + leagueEntries.divine = 1 + end + end + + return out + end) + if not success then + self:SetNotice(self.controls.pbNotice, "Failed to process CX response") + ConPrintf("CX error: %s", result) + return + end + result.timestamp = now + self.pbCurrencyConversion[realm] = result + end) + end) end local function initStatSortSelectionList(list) @@ -386,14 +455,16 @@ Highest Weight - Displays the order retrieved from trade]] self.controls.realmLabel = new("LabelControl"):LabelControl({ "LEFT", self.controls.setSelect, "RIGHT" }, { 18, 0, 20, row_height - 4 }, "^7Realm:") self.controls.realm = new("DropDownControl"):DropDownControl({ "LEFT", self.controls.realmLabel, "RIGHT" }, { 6, 0, 150, row_height }, self.realmDropList, function(index, value) self.pbRealmIndex = index - self.pbRealm = self.realmIds[value] + if self.pbRealm ~= self.realmIds[value] then + self.pbRealm = self.realmIds[value] + self:PullCXData() + end local function setLeagueDropList() self.itemsTab.leagueDropList = copyTable(self.allLeagues[self.pbRealm]) self.controls.league:SetList(self.itemsTab.leagueDropList) -- invalidate selIndex to trigger select function call in the SetSel self.controls.league.selIndex = nil self.controls.league:SetSel(self.pbLeagueIndex) - self:SetCurrencyConversionButton() end if self.allLeagues[self.pbRealm] then setLeagueDropList() @@ -426,7 +497,6 @@ Highest Weight - Displays the order retrieved from trade]] self.controls.league = new("DropDownControl"):DropDownControl({ "LEFT", self.controls.leagueLabel, "RIGHT" }, { 6, 0, 150, row_height }, self.itemsTab.leagueDropList, function(index, value) self.pbLeagueIndex = index self.pbLeague = value - self:SetCurrencyConversionButton() end) self.controls.league:SetSel(self.pbLeagueIndex) self.controls.league.enabled = function() @@ -581,11 +651,7 @@ Highest Weight - Displays the order retrieved from trade]] main:ClosePopup() end) - self.controls.updateCurrencyConversion = new("ButtonControl"):ButtonControl({ "BOTTOMLEFT", nil, "BOTTOMLEFT" }, { pane_margins_horizontal, -pane_margins_vertical, 240, row_height }, "Get Currency Conversion Rates", function() - self:PullPoENinjaCurrencyConversion(self.pbLeague) - end) self.controls.pbNotice = new("LabelControl"):LabelControl({ "BOTTOMRIGHT", nil, "BOTTOMRIGHT" }, { -row_height - pane_margins_vertical - row_vertical_padding, -pane_margins_vertical, 300, row_height }, "") - self:SetCurrencyConversionButton() -- used in PopupDialog:Draw() local function scrollBarFunc() @@ -619,6 +685,7 @@ Highest Weight - Displays the order retrieved from trade]] end end end + self:PullCXData() main:OpenPopup(pane_width, self.pane_height, "Trader", self.controls, nil, nil, "close", (scrollBarShown and scrollBarFunc or nil)) end @@ -728,48 +795,6 @@ function TradeQueryClass:SetStatWeights(previousSelectionList) main:OpenPopup(420, popupHeight, "Stat Weight Multipliers", controls) end --- Method to update the Currency Conversion button label -function TradeQueryClass:SetCurrencyConversionButton() - local currencyLabel = "Update Currency Conversion Rates" - self.pbFileTimestampDiff[self.controls.league.selIndex] = nil - if self.pbLeague == nil then - return - end - local values_file = io.open("../"..self.pbLeague.."_currency_values.json", "r") - if values_file then - local lines = values_file:read "*a" - values_file:close() - self.pbCurrencyConversion[self.pbLeague] = dkjson.decode(lines) - self.lastCurrencyFileTime[self.controls.league.selIndex] = self.pbCurrencyConversion[self.pbLeague]["updateTime"] - self.pbFileTimestampDiff[self.controls.league.selIndex] = get_time() - self.lastCurrencyFileTime[self.controls.league.selIndex] - if self.pbFileTimestampDiff[self.controls.league.selIndex] < 3600 then - -- Less than 1 hour (60 * 60 = 3600) - currencyLabel = "Currency Rates are very recent" - elseif self.pbFileTimestampDiff[self.controls.league.selIndex] < (24 * 3600) then - -- Less than 1 day - currencyLabel = "Currency Rates are recent" - end - else - currencyLabel = "Get Currency Conversion Rates" - end - self.controls.updateCurrencyConversion.label = currencyLabel - self.controls.updateCurrencyConversion.enabled = function() - return self.pbFileTimestampDiff[self.controls.league.selIndex] == nil or self.pbFileTimestampDiff[self.controls.league.selIndex] >= 3600 - end - self.controls.updateCurrencyConversion.tooltipFunc = function(tooltip) - tooltip:Clear() - if self.lastCurrencyFileTime[self.controls.league.selIndex] ~= nil then - self.pbFileTimestampDiff[self.controls.league.selIndex] = get_time() - self.lastCurrencyFileTime[self.controls.league.selIndex] - end - if self.pbFileTimestampDiff[self.controls.league.selIndex] == nil or self.pbFileTimestampDiff[self.controls.league.selIndex] >= 3600 then - tooltip:AddLine(16, "Currency Conversion rates are pulled from PoE Ninja") - tooltip:AddLine(16, "Updates are limited to once per hour and not necessary more than once per day") - elseif self.pbFileTimestampDiff[self.controls.league.selIndex] ~= nil and self.pbFileTimestampDiff[self.controls.league.selIndex] < 3600 then - tooltip:AddLine(16, "Conversion Rates are less than an hour old (" .. tostring(self.pbFileTimestampDiff[self.controls.league.selIndex]) .. " seconds old)") - end - end -end - -- Method to set the notice message in upper right of PoB Trader pane function TradeQueryClass:SetNotice(notice_control, msg) if msg:find("No Matching Results") then @@ -868,9 +893,8 @@ function TradeQueryClass:UpdateControlsWithItems(row_idx) local sortMode = self.itemSortSelectionList[self.pbItemSortSelectionIndex] local sortedItems, errMsg = self:SortFetchResults(row_idx, sortMode) if errMsg == "MissingConversionRates" then - self:SetNotice(self.controls.pbNotice, "^4Please update currency rates to sort by price. Falling back to Stat Value sort.") + self:SetNotice(self.controls.pbNotice, "^4Currency rates unavailable. Falling back to Stat Value sort.") sortedItems, errMsg = self:SortFetchResults(row_idx, self.sortModes.StatValue) - return elseif errMsg then self:SetNotice(self.controls.pbNotice, "Error: " .. errMsg) return @@ -1301,8 +1325,10 @@ function TradeQueryClass:GetTotalPriceString() for currency, _ in pairs(prices) do table.insert(currencies, currency) end - local currencyMap = self.pbCurrencyConversion[self.pbLeague] or {} - table.sort(currencies, function (a, b) + local currencyMap = self.pbCurrencyConversion[self.pbRealm] and + self.pbCurrencyConversion[self.pbRealm][self.pbLeague] + or {} + table.sort(currencies, function(a, b) if currencyMap[a] and currencyMap[b] then return currencyMap[a] > currencyMap[b] else diff --git a/src/Data/CurrencyNames.lua b/src/Data/CurrencyNames.lua new file mode 100644 index 0000000000..9ef00b81f0 --- /dev/null +++ b/src/Data/CurrencyNames.lua @@ -0,0 +1,457 @@ +-- This file is automatically generated, do not edit! +-- Game data (c) Grinding Gear Games + +-- This file contains mapping item names for every currency base item type ID. +-- Used for working with the currency exchange which uses item type IDs. + +-- spell-checker: disable +return { + ["Metadata/Items/AtlasExiles/AddModToRareCrusader"] = "Crusader's Exalted Orb", + ["Metadata/Items/AtlasExiles/AddModToRareHunter"] = "Hunter's Exalted Orb", + ["Metadata/Items/AtlasExiles/AddModToRareRedeemer"] = "Redeemer's Exalted Orb", + ["Metadata/Items/AtlasExiles/AddModToRareWarlord"] = "Warlord's Exalted Orb", + ["Metadata/Items/AtlasExiles/ApplyInfluence"] = "Awakener's Orb", + ["Metadata/Items/Currency/AbyssalBenchTicketArmour"] = "Preserved Rib", + ["Metadata/Items/Currency/AbyssalBenchTicketArmourHigh"] = "Ancient Rib", + ["Metadata/Items/Currency/AbyssalBenchTicketArmourLow"] = "Gnawed Rib", + ["Metadata/Items/Currency/AbyssalBenchTicketBreach"] = "Altered Collarbone", + ["Metadata/Items/Currency/AbyssalBenchTicketJewel"] = "Preserved Cranium", + ["Metadata/Items/Currency/AbyssalBenchTicketJewellery"] = "Preserved Collarbone", + ["Metadata/Items/Currency/AbyssalBenchTicketJewelleryHigh"] = "Ancient Collarbone", + ["Metadata/Items/Currency/AbyssalBenchTicketJewelleryLow"] = "Gnawed Collarbone", + ["Metadata/Items/Currency/AbyssalBenchTicketWaystone"] = "Preserved Vertebrae", + ["Metadata/Items/Currency/AbyssalBenchTicketWeapon"] = "Preserved Jawbone", + ["Metadata/Items/Currency/AbyssalBenchTicketWeaponHigh"] = "Ancient Jawbone", + ["Metadata/Items/Currency/AbyssalBenchTicketWeaponLow"] = "Gnawed Jawbone", + ["Metadata/Items/Currency/Bestiary/BestiaryNet1"] = "Simple Rope Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet10"] = "Thaumaturgical Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet11"] = "Necromancy Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet2"] = "Reinforced Rope Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet3"] = "Strong Rope Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet4"] = "Simple Iron Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet5"] = "Reinforced Iron Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet6"] = "Strong Iron Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet7"] = "Simple Steel Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet8"] = "Reinforced Steel Net", + ["Metadata/Items/Currency/Bestiary/BestiaryNet9"] = "Strong Steel Net", + ["Metadata/Items/Currency/CurrencyAddAtlasMod"] = "Simple Sextant", + ["Metadata/Items/Currency/CurrencyAddAtlasModHigh"] = "Awakened Sextant", + ["Metadata/Items/Currency/CurrencyAddAtlasModMaven"] = "Elevated Sextant", + ["Metadata/Items/Currency/CurrencyAddAtlasModMid"] = "Prime Sextant", + ["Metadata/Items/Currency/CurrencyAddEquipmentSocket"] = "Artificer's Orb", + ["Metadata/Items/Currency/CurrencyAddEquipmentSocketShard"] = "Artificer's Shard", + ["Metadata/Items/Currency/CurrencyAddGemExperience"] = "Facetor's Lens", + ["Metadata/Items/Currency/CurrencyAddModToMagic"] = "Orb of Augmentation", + ["Metadata/Items/Currency/CurrencyAddModToMagic2"] = "Greater Orb of Augmentation", + ["Metadata/Items/Currency/CurrencyAddModToMagic3"] = "Perfect Orb of Augmentation", + ["Metadata/Items/Currency/CurrencyAddModToRare"] = "Exalted Orb", + ["Metadata/Items/Currency/CurrencyAddModToRare2"] = "Greater Exalted Orb", + ["Metadata/Items/Currency/CurrencyAddModToRare3"] = "Perfect Exalted Orb", + ["Metadata/Items/Currency/CurrencyAddModToRareShard"] = "Exalted Shard", + ["Metadata/Items/Currency/CurrencyAddSkillGemSocket3"] = "Lesser Jeweller's Orb", + ["Metadata/Items/Currency/CurrencyAddSkillGemSocket4"] = "Greater Jeweller's Orb", + ["Metadata/Items/Currency/CurrencyAddSkillGemSocket5"] = "Perfect Jeweller's Orb", + ["Metadata/Items/Currency/CurrencyAfflictionShard"] = "Simulacrum Splinter", + ["Metadata/Items/Currency/CurrencyArcaneFluxChaos"] = "Void Flux", + ["Metadata/Items/Currency/CurrencyArcaneFluxCold"] = "Chilling Flux", + ["Metadata/Items/Currency/CurrencyArcaneFluxFire"] = "Blazing Flux", + ["Metadata/Items/Currency/CurrencyArcaneFluxLightning"] = "Crackling Flux", + ["Metadata/Items/Currency/CurrencyArmourQuality"] = "Armourer's Scrap", + ["Metadata/Items/Currency/CurrencyAtlasPassiveRefund"] = "Orb of Unmaking", + ["Metadata/Items/Currency/CurrencyBreachChaosShard"] = "Splinter of Chayula", + ["Metadata/Items/Currency/CurrencyBreachColdShard"] = "Splinter of Tul", + ["Metadata/Items/Currency/CurrencyBreachFireShard"] = "Splinter of Xoph", + ["Metadata/Items/Currency/CurrencyBreachLightningShard"] = "Splinter of Esh", + ["Metadata/Items/Currency/CurrencyBreachPhysicalShard"] = "Splinter of Uul-Netol", + ["Metadata/Items/Currency/CurrencyBreachShard"] = "Breach Splinter", + ["Metadata/Items/Currency/CurrencyBreachUpgradeUniqueChaos"] = "Blessing of Chayula", + ["Metadata/Items/Currency/CurrencyBreachUpgradeUniqueCold"] = "Blessing of Tul", + ["Metadata/Items/Currency/CurrencyBreachUpgradeUniqueFire"] = "Blessing of Xoph", + ["Metadata/Items/Currency/CurrencyBreachUpgradeUniqueLightning"] = "Blessing of Esh", + ["Metadata/Items/Currency/CurrencyBreachUpgradeUniquePhysical"] = "Blessing of Uul-Netol", + ["Metadata/Items/Currency/CurrencyConflictOrb"] = "Orb of Conflict", + ["Metadata/Items/Currency/CurrencyConvertStrongboxToNormal"] = "[DNT] Box Scour", + ["Metadata/Items/Currency/CurrencyConvertToNormal"] = "Orb of Scouring", + ["Metadata/Items/Currency/CurrencyCorrupt"] = "Vaal Orb", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceAbyss"] = "Essence of the Abyss", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceBreach"] = "Essence of the Breach", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceDelirium"] = "Essence of Delirium", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceHorror"] = "Essence of Horror", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceHysteria"] = "Essence of Hysteria", + ["Metadata/Items/Currency/CurrencyCorruptedEssenceInsanity"] = "Essence of Insanity", + ["Metadata/Items/Currency/CurrencyDelveCraftingAbyss"] = "Hollow Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingAttackMods"] = "Serrated Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingBleedPoison"] = "Corroded Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingCasterMods"] = "Aetheric Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingChaos"] = "Aberrant Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingCold"] = "Frigid Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingCorruptEssence"] = "Glyphic Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingDefences"] = "Dense Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingElemental"] = "Prismatic Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingEnchant"] = "Deft Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingFire"] = "Scorched Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingGemLevel"] = "Faceted Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingLife"] = "Pristine Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingLightning"] = "Metallic Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingLuckyModRolls"] = "Sanctified Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingMana"] = "Lucent Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingMinionsAuras"] = "Bound Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingMirror"] = "Fractured Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingPhysical"] = "Jagged Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingQuality"] = "Perfect Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingRandom"] = "Tangled Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingSellPrice"] = "Gilded Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingSockets"] = "Fundamental Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingSpeed"] = "Shuddering Fossil", + ["Metadata/Items/Currency/CurrencyDelveCraftingVaal"] = "Bloodstained Fossil", + ["Metadata/Items/Currency/CurrencyDuplicate"] = "Mirror of Kalandra", + ["Metadata/Items/Currency/CurrencyDuplicateShard"] = "Mirror Shard", + ["Metadata/Items/Currency/CurrencyEldritchAddModToRare"] = "Eldritch Exalted Orb", + ["Metadata/Items/Currency/CurrencyEldritchEmber1"] = "Lesser Eldritch Ember", + ["Metadata/Items/Currency/CurrencyEldritchEmber2"] = "Greater Eldritch Ember", + ["Metadata/Items/Currency/CurrencyEldritchEmber3"] = "Grand Eldritch Ember", + ["Metadata/Items/Currency/CurrencyEldritchEmber4"] = "Exceptional Eldritch Ember", + ["Metadata/Items/Currency/CurrencyEldritchIchor1"] = "Lesser Eldritch Ichor", + ["Metadata/Items/Currency/CurrencyEldritchIchor2"] = "Greater Eldritch Ichor", + ["Metadata/Items/Currency/CurrencyEldritchIchor3"] = "Grand Eldritch Ichor", + ["Metadata/Items/Currency/CurrencyEldritchIchor4"] = "Exceptional Eldritch Ichor", + ["Metadata/Items/Currency/CurrencyEldritchRemoveMod"] = "Eldritch Orb of Annulment", + ["Metadata/Items/Currency/CurrencyEldritchRerollRare"] = "Eldritch Chaos Orb", + ["Metadata/Items/Currency/CurrencyEnkindlingOrb"] = "Enkindling Orb", + ["Metadata/Items/Currency/CurrencyEssenceAlly"] = "Essence of Command", + ["Metadata/Items/Currency/CurrencyEssenceAttack"] = "Essence of Battle", + ["Metadata/Items/Currency/CurrencyEssenceAttribute"] = "Essence of the Infinite", + ["Metadata/Items/Currency/CurrencyEssenceCaster"] = "Essence of Sorcery", + ["Metadata/Items/Currency/CurrencyEssenceChaos"] = "Essence of Ruin", + ["Metadata/Items/Currency/CurrencyEssenceCold"] = "Essence of Ice", + ["Metadata/Items/Currency/CurrencyEssenceColdResist"] = "Essence of Thawing", + ["Metadata/Items/Currency/CurrencyEssenceCritical"] = "Essence of Seeking", + ["Metadata/Items/Currency/CurrencyEssenceDefences"] = "Essence of Enhancement", + ["Metadata/Items/Currency/CurrencyEssenceFire"] = "Essence of Flames", + ["Metadata/Items/Currency/CurrencyEssenceFireResist"] = "Essence of Insulation", + ["Metadata/Items/Currency/CurrencyEssenceLife"] = "Essence of the Body", + ["Metadata/Items/Currency/CurrencyEssenceLightning"] = "Essence of Electricity", + ["Metadata/Items/Currency/CurrencyEssenceLightningResist"] = "Essence of Grounding", + ["Metadata/Items/Currency/CurrencyEssenceMana"] = "Essence of the Mind", + ["Metadata/Items/Currency/CurrencyEssencePhysical"] = "Essence of Abrasion", + ["Metadata/Items/Currency/CurrencyEssenceRarity"] = "Essence of Opulence", + ["Metadata/Items/Currency/CurrencyEssenceSpeed"] = "Essence of Haste", + ["Metadata/Items/Currency/CurrencyEssenceSpeedCaster"] = "Essence of Alacrity", + ["Metadata/Items/Currency/CurrencyExpeditionShard"] = "Runic Splinter", + ["Metadata/Items/Currency/CurrencyExtractOil"] = "Oil Extractor", + ["Metadata/Items/Currency/CurrencyFlaskQuality"] = "Glassblower's Bauble", + ["Metadata/Items/Currency/CurrencyFractureRare"] = "Fracturing Orb", + ["Metadata/Items/Currency/CurrencyFractureRareShard"] = "Fracturing Shard", + ["Metadata/Items/Currency/CurrencyGemQuality"] = "Gemcutter's Prism", + ["Metadata/Items/Currency/CurrencyGreaterEssenceAlly"] = "Greater Essence of Command", + ["Metadata/Items/Currency/CurrencyGreaterEssenceAttack"] = "Greater Essence of Battle", + ["Metadata/Items/Currency/CurrencyGreaterEssenceAttribute"] = "Greater Essence of the Infinite", + ["Metadata/Items/Currency/CurrencyGreaterEssenceCaster"] = "Greater Essence of Sorcery", + ["Metadata/Items/Currency/CurrencyGreaterEssenceChaos"] = "Greater Essence of Ruin", + ["Metadata/Items/Currency/CurrencyGreaterEssenceCold"] = "Greater Essence of Ice", + ["Metadata/Items/Currency/CurrencyGreaterEssenceColdResist"] = "Greater Essence of Thawing", + ["Metadata/Items/Currency/CurrencyGreaterEssenceCritical"] = "Greater Essence of Seeking", + ["Metadata/Items/Currency/CurrencyGreaterEssenceDefences"] = "Greater Essence of Enhancement", + ["Metadata/Items/Currency/CurrencyGreaterEssenceFire"] = "Greater Essence of Flames", + ["Metadata/Items/Currency/CurrencyGreaterEssenceFireResist"] = "Greater Essence of Insulation", + ["Metadata/Items/Currency/CurrencyGreaterEssenceLife"] = "Greater Essence of the Body", + ["Metadata/Items/Currency/CurrencyGreaterEssenceLightning"] = "Greater Essence of Electricity", + ["Metadata/Items/Currency/CurrencyGreaterEssenceLightningResist"] = "Greater Essence of Grounding", + ["Metadata/Items/Currency/CurrencyGreaterEssenceMana"] = "Greater Essence of the Mind", + ["Metadata/Items/Currency/CurrencyGreaterEssencePhysical"] = "Greater Essence of Abrasion", + ["Metadata/Items/Currency/CurrencyGreaterEssenceRarity"] = "Greater Essence of Opulence", + ["Metadata/Items/Currency/CurrencyGreaterEssenceSpeed"] = "Greater Essence of Haste", + ["Metadata/Items/Currency/CurrencyGreaterEssenceSpeedCaster"] = "Greater Essence of Alacrity", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingBelt"] = "Time-light Scroll", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingHelmet"] = "Deregulation Scroll", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingQuiver"] = "Fragmentation Scroll", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingShield"] = "Specularity Scroll", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingStaff"] = "Haemocombustion Scroll", + ["Metadata/Items/Currency/CurrencyHarbingerBlessingSword"] = "Electroshock Scroll", + ["Metadata/Items/Currency/CurrencyHeistArmourEnchant"] = "Tailoring Orb", + ["Metadata/Items/Currency/CurrencyHeistWeaponEnchant"] = "Tempering Orb", + ["Metadata/Items/Currency/CurrencyHellscapeAddModToRare"] = "Tainted Exalted Orb", + ["Metadata/Items/Currency/CurrencyHellscapeArmourQuality"] = "Tainted Armourer's Scrap", + ["Metadata/Items/Currency/CurrencyHellscapeRerollRare"] = "Tainted Chaos Orb", + ["Metadata/Items/Currency/CurrencyHellscapeRerollSocketColours"] = "Tainted Chromatic Orb", + ["Metadata/Items/Currency/CurrencyHellscapeRerollSocketLinks"] = "Tainted Orb of Fusing", + ["Metadata/Items/Currency/CurrencyHellscapeRerollSocketNumbers"] = "Tainted Jeweller's Orb", + ["Metadata/Items/Currency/CurrencyHellscapeUpgradeModTier"] = "Tainted Divine Teardrop", + ["Metadata/Items/Currency/CurrencyHellscapeUpgradeToUnique"] = "Tainted Mythic Orb", + ["Metadata/Items/Currency/CurrencyHellscapeWeaponQuality"] = "Tainted Blacksmith's Whetstone", + ["Metadata/Items/Currency/CurrencyHinekorasLock"] = "Hinekora's Lock", + ["Metadata/Items/Currency/CurrencyIdentification"] = "Scroll of Wisdom", + ["Metadata/Items/Currency/CurrencyIdentificationShard"] = "Scroll Fragment", + ["Metadata/Items/Currency/CurrencyImprint"] = "Imprint", + ["Metadata/Items/Currency/CurrencyImprintOrb"] = "Eternal Orb", + ["Metadata/Items/Currency/CurrencyIncursionAddModToRareEquipment"] = "[DNT] Not Shown To Players", + ["Metadata/Items/Currency/CurrencyIncursionBetterGemQuality"] = "[DNT] Incursion Gemcutter's Prism (not visible to players)", + ["Metadata/Items/Currency/CurrencyIncursionCasterWeaponQuality"] = "Vaal Arcanist's Infuser", + ["Metadata/Items/Currency/CurrencyIncursionCorrupt1"] = "Corrupt", + ["Metadata/Items/Currency/CurrencyIncursionCorruptTablet"] = "Ancient Infuser", + ["Metadata/Items/Currency/CurrencyIncursionDoubleCorrupt"] = "Architect's Orb", + ["Metadata/Items/Currency/CurrencyIncursionDoubleCorruptGem"] = "Crystallised Corruption", + ["Metadata/Items/Currency/CurrencyIncursionExtractAllSocketablesBench"] = "Orb of Extraction", + ["Metadata/Items/Currency/CurrencyIncursionExtractAllSocketablesCurrency"] = "Orb of Extraction", + ["Metadata/Items/Currency/CurrencyIncursionJewelleryQuality"] = "Vaal Catalysing Infuser", + ["Metadata/Items/Currency/CurrencyIncursionMartialWeaponQuality"] = "Vaal Blacksmith's Infuser", + ["Metadata/Items/Currency/CurrencyIncursionModifySoulCore"] = "Core Destabiliser", + ["Metadata/Items/Currency/CurrencyIncursionMutateCorruptionEnchantArmour"] = "Kopec's Orb of Sacrifice", + ["Metadata/Items/Currency/CurrencyIncursionMutateCorruptionEnchantJewel"] = "Yugul's Orb of Sacrifice", + ["Metadata/Items/Currency/CurrencyIncursionMutateCorruptionEnchantJewellery"] = "Kamasa's Orb of Sacrifice", + ["Metadata/Items/Currency/CurrencyIncursionMutateCorruptionEnchantWeapon"] = "Yaomac's Orb of Sacrifice", + ["Metadata/Items/Currency/CurrencyIncursionMutateUnique"] = "Vaal Cultivation Orb", + ["Metadata/Items/Currency/CurrencyIncursionUpgradeMagicToRareEquipment"] = "[DNT] Not Shown To Players", + ["Metadata/Items/Currency/CurrencyIncursionUpgradeToRareEquipment"] = "[DNT] Not Shown To Players", + ["Metadata/Items/Currency/CurrencyIncursionVialBossAmulet"] = "Vial of Sacrifice", + ["Metadata/Items/Currency/CurrencyIncursionVialBossFlask"] = "Vial of the Ghost", + ["Metadata/Items/Currency/CurrencyIncursionVialBossJewel"] = "Vial of Transcendence", + ["Metadata/Items/Currency/CurrencyIncursionVialFire"] = "Vial of Fate", + ["Metadata/Items/Currency/CurrencyIncursionVialHealing"] = "Vial of Summoning", + ["Metadata/Items/Currency/CurrencyIncursionVialLightning"] = "Vial of the Ritual", + ["Metadata/Items/Currency/CurrencyIncursionVialMinion"] = "Vial of Consequence", + ["Metadata/Items/Currency/CurrencyIncursionVialPoison"] = "Vial of Awakening", + ["Metadata/Items/Currency/CurrencyIncursionVialTrap"] = "Vial of Dominance", + ["Metadata/Items/Currency/CurrencyIncursionWeaponOrArmourQualityHigh"] = "Vaal Armourer's Infuser", + ["Metadata/Items/Currency/CurrencyIncursionWeaponOrArmourQualityLow"] = "[DNT] Not Shown To Players", + ["Metadata/Items/Currency/CurrencyIncursionWeaponOrArmourQualityMid"] = "[DNT] Not Shown To Players", + ["Metadata/Items/Currency/CurrencyInstillingOrb"] = "Instilling Orb", + ["Metadata/Items/Currency/CurrencyItemiseCapturedMonster"] = "Bestiary Orb", + ["Metadata/Items/Currency/CurrencyItemiseSextantModifier"] = "Surveyor's Compass", + ["Metadata/Items/Currency/CurrencyItemisedCapturedMonster"] = "Imprinted Bestiary Orb", + ["Metadata/Items/Currency/CurrencyItemisedProphecy"] = "Prophecy", + ["Metadata/Items/Currency/CurrencyItemisedSextantModifier"] = "Charged Compass", + ["Metadata/Items/Currency/CurrencyJewelQualityAttack"] = "Refined Reaver Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityAttribute"] = "Refined Adaptive Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityCaster"] = "Refined Sibilant Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityChaos"] = "Refined Chayula's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityCold"] = "Refined Tul's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityDefences"] = "Refined Carapace Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityFire"] = "Refined Xoph's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityLife"] = "Refined Flesh Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityLightning"] = "Refined Esh's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityMana"] = "Refined Neural Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityMinion"] = "Refined Necrotic Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualityPhysical"] = "Refined Uul-Netol's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelQualitySpeed"] = "Refined Skittering Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityAttack"] = "Reaver Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityAttribute"] = "Adaptive Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityCaster"] = "Sibilant Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityChaos"] = "Chayula's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityCold"] = "Tul's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityDefences"] = "Carapace Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityFire"] = "Xoph's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityLife"] = "Flesh Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityLightning"] = "Esh's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityMana"] = "Neural Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityMinion"] = "Necrotic Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualityPhysical"] = "Uul-Netol's Catalyst", + ["Metadata/Items/Currency/CurrencyJewelleryQualitySpeed"] = "Skittering Catalyst", + ["Metadata/Items/Currency/CurrencyKiwiFeather"] = "Jade Kiwi Feather", + ["Metadata/Items/Currency/CurrencyLegionEternalEmpireShard"] = "Timeless Eternal Empire Splinter", + ["Metadata/Items/Currency/CurrencyLegionKaruiShard"] = "Timeless Karui Splinter", + ["Metadata/Items/Currency/CurrencyLegionMarakethShard"] = "Timeless Maraketh Splinter", + ["Metadata/Items/Currency/CurrencyLegionTemplarShard"] = "Timeless Templar Splinter", + ["Metadata/Items/Currency/CurrencyLegionVaalShard"] = "Timeless Vaal Splinter", + ["Metadata/Items/Currency/CurrencyLesserEssenceAlly"] = "Lesser Essence of Command", + ["Metadata/Items/Currency/CurrencyLesserEssenceAttack"] = "Lesser Essence of Battle", + ["Metadata/Items/Currency/CurrencyLesserEssenceAttribute"] = "Lesser Essence of the Infinite", + ["Metadata/Items/Currency/CurrencyLesserEssenceCaster"] = "Lesser Essence of Sorcery", + ["Metadata/Items/Currency/CurrencyLesserEssenceChaos"] = "Lesser Essence of Ruin", + ["Metadata/Items/Currency/CurrencyLesserEssenceCold"] = "Lesser Essence of Ice", + ["Metadata/Items/Currency/CurrencyLesserEssenceColdResist"] = "Lesser Essence of Thawing", + ["Metadata/Items/Currency/CurrencyLesserEssenceCritical"] = "Lesser Essence of Seeking", + ["Metadata/Items/Currency/CurrencyLesserEssenceDefences"] = "Lesser Essence of Enhancement", + ["Metadata/Items/Currency/CurrencyLesserEssenceFire"] = "Lesser Essence of Flames", + ["Metadata/Items/Currency/CurrencyLesserEssenceFireResist"] = "Lesser Essence of Insulation", + ["Metadata/Items/Currency/CurrencyLesserEssenceLife"] = "Lesser Essence of the Body", + ["Metadata/Items/Currency/CurrencyLesserEssenceLightning"] = "Lesser Essence of Electricity", + ["Metadata/Items/Currency/CurrencyLesserEssenceLightningResist"] = "Lesser Essence of Grounding", + ["Metadata/Items/Currency/CurrencyLesserEssenceMana"] = "Lesser Essence of the Mind", + ["Metadata/Items/Currency/CurrencyLesserEssencePhysical"] = "Lesser Essence of Abrasion", + ["Metadata/Items/Currency/CurrencyLesserEssenceRarity"] = "Lesser Essence of Opulence", + ["Metadata/Items/Currency/CurrencyLesserEssenceSpeed"] = "Lesser Essence of Haste", + ["Metadata/Items/Currency/CurrencyLesserEssenceSpeedCaster"] = "Lesser Essence of Alacrity", + ["Metadata/Items/Currency/CurrencyMagicQuality"] = "Arcanist's Etcher", + ["Metadata/Items/Currency/CurrencyMapQuality"] = "Cartographer's Chisel", + ["Metadata/Items/Currency/CurrencyModValues"] = "Divine Orb", + ["Metadata/Items/Currency/CurrencyPassiveRefund"] = "Orb of Regret", + ["Metadata/Items/Currency/CurrencyPeacockFeather"] = "Stygian Peacock Feather", + ["Metadata/Items/Currency/CurrencyPerandusCoin"] = "Perandus Coin", + ["Metadata/Items/Currency/CurrencyPerfectEssenceAlly"] = "Perfect Essence of Command", + ["Metadata/Items/Currency/CurrencyPerfectEssenceAttack"] = "Perfect Essence of Battle", + ["Metadata/Items/Currency/CurrencyPerfectEssenceAttribute"] = "Perfect Essence of the Infinite", + ["Metadata/Items/Currency/CurrencyPerfectEssenceCaster"] = "Perfect Essence of Sorcery", + ["Metadata/Items/Currency/CurrencyPerfectEssenceChaos"] = "Perfect Essence of Ruin", + ["Metadata/Items/Currency/CurrencyPerfectEssenceCold"] = "Perfect Essence of Ice", + ["Metadata/Items/Currency/CurrencyPerfectEssenceColdResist"] = "Perfect Essence of Thawing", + ["Metadata/Items/Currency/CurrencyPerfectEssenceCritical"] = "Perfect Essence of Seeking", + ["Metadata/Items/Currency/CurrencyPerfectEssenceDefences"] = "Perfect Essence of Enhancement", + ["Metadata/Items/Currency/CurrencyPerfectEssenceFire"] = "Perfect Essence of Flames", + ["Metadata/Items/Currency/CurrencyPerfectEssenceFireResist"] = "Perfect Essence of Insulation", + ["Metadata/Items/Currency/CurrencyPerfectEssenceLife"] = "Perfect Essence of the Body", + ["Metadata/Items/Currency/CurrencyPerfectEssenceLightning"] = "Perfect Essence of Electricity", + ["Metadata/Items/Currency/CurrencyPerfectEssenceLightningResist"] = "Perfect Essence of Grounding", + ["Metadata/Items/Currency/CurrencyPerfectEssenceMana"] = "Perfect Essence of the Mind", + ["Metadata/Items/Currency/CurrencyPerfectEssencePhysical"] = "Perfect Essence of Abrasion", + ["Metadata/Items/Currency/CurrencyPerfectEssenceRarity"] = "Perfect Essence of Opulence", + ["Metadata/Items/Currency/CurrencyPerfectEssenceSpeed"] = "Perfect Essence of Haste", + ["Metadata/Items/Currency/CurrencyPerfectEssenceSpeedCaster"] = "Perfect Essence of Alacrity", + ["Metadata/Items/Currency/CurrencyPortal"] = "Portal Scroll", + ["Metadata/Items/Currency/CurrencyRefreshExpedition"] = "Exotic Coinage", + ["Metadata/Items/Currency/CurrencyRemoveMod"] = "Orb of Annulment", + ["Metadata/Items/Currency/CurrencyRemoveModShard"] = "Annulment Shard", + ["Metadata/Items/Currency/CurrencyRerollDefences"] = "Sacred Orb", + ["Metadata/Items/Currency/CurrencyRerollImplicit"] = "Blessed Orb", + ["Metadata/Items/Currency/CurrencyRerollMagic"] = "Orb of Alteration", + ["Metadata/Items/Currency/CurrencyRerollMagicShard"] = "Alteration Shard", + ["Metadata/Items/Currency/CurrencyRerollMapType"] = "Orb of Horizons", + ["Metadata/Items/Currency/CurrencyRerollMapTypeShard"] = "Horizon Shard", + ["Metadata/Items/Currency/CurrencyRerollRare"] = "Chaos Orb", + ["Metadata/Items/Currency/CurrencyRerollRare2"] = "Greater Chaos Orb", + ["Metadata/Items/Currency/CurrencyRerollRare3"] = "Perfect Chaos Orb", + ["Metadata/Items/Currency/CurrencyRerollRareShard"] = "Chaos Shard", + ["Metadata/Items/Currency/CurrencyRerollRemnant"] = "Liquid Verisium", + ["Metadata/Items/Currency/CurrencyRerollSkillQualityType"] = "Prime Regrading Lens", + ["Metadata/Items/Currency/CurrencyRerollSocketColours"] = "Chromatic Orb", + ["Metadata/Items/Currency/CurrencyRerollSocketLinks"] = "Orb of Fusing", + ["Metadata/Items/Currency/CurrencyRerollSupportQualityType"] = "Secondary Regrading Lens", + ["Metadata/Items/Currency/CurrencyRerollUnique"] = "Ancient Orb", + ["Metadata/Items/Currency/CurrencyRerollUniqueShard"] = "Ancient Shard", + ["Metadata/Items/Currency/CurrencyRespecShapersOrb"] = "Unshaping Orb", + ["Metadata/Items/Currency/CurrencyRhoaFeather"] = "Albino Rhoa Feather", + ["Metadata/Items/Currency/CurrencyRitualShard"] = "Petition Splinter", + ["Metadata/Items/Currency/CurrencyRitualSplinter"] = "Ritual Splinter", + ["Metadata/Items/Currency/CurrencyRitualStone"] = "Ritual Vessel", + ["Metadata/Items/Currency/CurrencySealMapHigh"] = "Master Cartographer's Seal", + ["Metadata/Items/Currency/CurrencySealMapLow"] = "Apprentice Cartographer's Seal", + ["Metadata/Items/Currency/CurrencySealMapMid"] = "Journeyman Cartographer's Seal", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel1"] = "Thaumaturgic Flux (Level 1)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel10"] = "Thaumaturgic Flux (Level 10)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel11"] = "Thaumaturgic Flux (Level 11)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel12"] = "Thaumaturgic Flux (Level 12)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel13"] = "Thaumaturgic Flux (Level 13)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel14"] = "Thaumaturgic Flux (Level 14)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel15"] = "Thaumaturgic Flux (Level 15)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel16"] = "Thaumaturgic Flux (Level 16)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel17"] = "Thaumaturgic Flux (Level 17)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel18"] = "Thaumaturgic Flux (Level 18)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel19"] = "Thaumaturgic Flux (Level 19)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel2"] = "Thaumaturgic Flux (Level 2)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel20"] = "Thaumaturgic Flux (Level 20)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel3"] = "Thaumaturgic Flux (Level 3)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel4"] = "Thaumaturgic Flux (Level 4)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel5"] = "Thaumaturgic Flux (Level 5)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel6"] = "Thaumaturgic Flux (Level 6)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel7"] = "Thaumaturgic Flux (Level 7)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel8"] = "Thaumaturgic Flux (Level 8)", + ["Metadata/Items/Currency/CurrencySetKalguuranSkillGemLevel9"] = "Thaumaturgic Flux (Level 9)", + ["Metadata/Items/Currency/CurrencySilverCoin"] = "Silver Coin", + ["Metadata/Items/Currency/CurrencySkillGemToken"] = "Uncarved Gemstone", + ["Metadata/Items/Currency/CurrencyStrongboxQuality"] = "Engineer's Orb", + ["Metadata/Items/Currency/CurrencyStrongboxQualityInfused"] = "Infused Engineer's Orb", + ["Metadata/Items/Currency/CurrencyStrongboxQualityShard"] = "Engineer's Shard", + ["Metadata/Items/Currency/CurrencyToucanFeather"] = "Rainbow Toucan Feather", + ["Metadata/Items/Currency/CurrencyUpgradeInfluenceMod"] = "Orb of Dominance", + ["Metadata/Items/Currency/CurrencyUpgradeInherentTo20"] = "Perfect Flux", + ["Metadata/Items/Currency/CurrencyUpgradeMagicToRare"] = "Regal Orb", + ["Metadata/Items/Currency/CurrencyUpgradeMagicToRare2"] = "Greater Regal Orb", + ["Metadata/Items/Currency/CurrencyUpgradeMagicToRare3"] = "Perfect Regal Orb", + ["Metadata/Items/Currency/CurrencyUpgradeMagicToRareShard"] = "Regal Shard", + ["Metadata/Items/Currency/CurrencyUpgradeMapTier"] = "Harbinger's Orb", + ["Metadata/Items/Currency/CurrencyUpgradeMapTierShard"] = "Harbinger's Shard", + ["Metadata/Items/Currency/CurrencyUpgradeRandomly"] = "Orb of Chance", + ["Metadata/Items/Currency/CurrencyUpgradeRandomlyShard"] = "Chance Shard", + ["Metadata/Items/Currency/CurrencyUpgradeToMagic"] = "Orb of Transmutation", + ["Metadata/Items/Currency/CurrencyUpgradeToMagic2"] = "Greater Orb of Transmutation", + ["Metadata/Items/Currency/CurrencyUpgradeToMagic3"] = "Perfect Orb of Transmutation", + ["Metadata/Items/Currency/CurrencyUpgradeToMagicShard"] = "Transmutation Shard", + ["Metadata/Items/Currency/CurrencyUpgradeToRare"] = "Orb of Alchemy", + ["Metadata/Items/Currency/CurrencyUpgradeToRareAndSetSockets"] = "Orb of Binding", + ["Metadata/Items/Currency/CurrencyUpgradeToRareAndSetSocketsShard"] = "Binding Shard", + ["Metadata/Items/Currency/CurrencyUpgradeToRareShard"] = "Alchemy Shard", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy1"] = "Runic Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy10"] = "Celestial Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy11"] = "Transcendent Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy12"] = "The Runebinder's Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy13"] = "The Runefather's Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy2"] = "Adaptive Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy3"] = "Protective Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy4"] = "Expansive Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy5"] = "Swift Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy6"] = "Cyclonic Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy7"] = "Prismatic Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy8"] = "Mystic Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumAlloy9"] = "Sovereign Alloy", + ["Metadata/Items/Currency/CurrencyVerisiumMetal1"] = "Verisium", + ["Metadata/Items/Currency/CurrencyVerisiumMetal2"] = "Exceptional Verisium", + ["Metadata/Items/Currency/CurrencyVerisiumMetalBreach1"] = "Mutated Verisium", + ["Metadata/Items/Currency/CurrencyVerisiumMetalKalguur1"] = "Founders Verisium", + ["Metadata/Items/Currency/CurrencyVerisiumMetalUnique1"] = "Medved's Crest of the Circle", + ["Metadata/Items/Currency/CurrencyVerisiumMetalUnique2"] = "Vorana's Crest of the Scythe", + ["Metadata/Items/Currency/CurrencyVerisiumMetalUnique3"] = "Uhtred's Crest of the Chalice", + ["Metadata/Items/Currency/CurrencyVerisiumMetalUnique4"] = "Olroth's Crest of the Sun", + ["Metadata/Items/Currency/CurrencyVerisiumMetalVaal1"] = "Corrupted Verisium", + ["Metadata/Items/Currency/CurrencyVerisiumOreUniqueEventidePetals"] = "Veridical Starlit Ore", + ["Metadata/Items/Currency/CurrencyVerisiumOreUniqueEyesOfTheRunefather"] = "Venerable Starlit Ore", + ["Metadata/Items/Currency/CurrencyVerisiumOreUniqueSerlesGrit"] = "Starlit Ore", + ["Metadata/Items/Currency/CurrencyVerisiumOreUniqueStolvarheim"] = "Warding Starlit Ore", + ["Metadata/Items/Currency/CurrencyVerisiumOreUniqueTheUnleashed"] = "Revered Starlit Ore", + ["Metadata/Items/Currency/CurrencyVultureFeather"] = "Golden Vulture Feather", + ["Metadata/Items/Currency/CurrencyWeaponQuality"] = "Blacksmith's Whetstone", + ["Metadata/Items/Currency/DistilledEmotion1"] = "Diluted Liquid Ire", + ["Metadata/Items/Currency/DistilledEmotion10"] = "Concentrated Liquid Isolation", + ["Metadata/Items/Currency/DistilledEmotion2"] = "Diluted Liquid Guilt", + ["Metadata/Items/Currency/DistilledEmotion3"] = "Diluted Liquid Greed", + ["Metadata/Items/Currency/DistilledEmotion4"] = "Liquid Paranoia", + ["Metadata/Items/Currency/DistilledEmotion5"] = "Liquid Envy", + ["Metadata/Items/Currency/DistilledEmotion6"] = "Liquid Disgust", + ["Metadata/Items/Currency/DistilledEmotion7"] = "Liquid Despair", + ["Metadata/Items/Currency/DistilledEmotion8"] = "Concentrated Liquid Fear", + ["Metadata/Items/Currency/DistilledEmotion9"] = "Concentrated Liquid Suffering", + ["Metadata/Items/Currency/DistilledEmotionTimeLost1"] = "Ancient Diluted Liquid Ire", + ["Metadata/Items/Currency/DistilledEmotionTimeLost10"] = "Ancient Concentrated Liquid Isolation", + ["Metadata/Items/Currency/DistilledEmotionTimeLost2"] = "Ancient Diluted Liquid Guilt", + ["Metadata/Items/Currency/DistilledEmotionTimeLost3"] = "Ancient Diluted Liquid Greed", + ["Metadata/Items/Currency/DistilledEmotionTimeLost4"] = "Ancient Liquid Paranoia", + ["Metadata/Items/Currency/DistilledEmotionTimeLost5"] = "Ancient Liquid Envy", + ["Metadata/Items/Currency/DistilledEmotionTimeLost6"] = "Ancient Liquid Disgust", + ["Metadata/Items/Currency/DistilledEmotionTimeLost7"] = "Ancient Liquid Despair", + ["Metadata/Items/Currency/DistilledEmotionTimeLost8"] = "Ancient Concentrated Liquid Fear", + ["Metadata/Items/Currency/DistilledEmotionTimeLost9"] = "Ancient Concentrated Liquid Suffering", + ["Metadata/Items/Currency/EndgameDistilledEmotion1"] = "Potent Liquid Melancholy", + ["Metadata/Items/Currency/EndgameDistilledEmotion2"] = "Potent Liquid Ferocity", + ["Metadata/Items/Currency/EndgameDistilledEmotion3"] = "Potent Liquid Contempt", + ["Metadata/Items/Currency/EndgameDistilledEmotionTimeLost1"] = "Ancient Potent Liquid Melancholy", + ["Metadata/Items/Currency/EndgameDistilledEmotionTimeLost2"] = "Ancient Potent Liquid Ferocity", + ["Metadata/Items/Currency/EndgameDistilledEmotionTimeLost3"] = "Ancient Potent Liquid Contempt", + ["Metadata/Items/Currency/Expedition/ExpeditionPinnacleKeyShard"] = "Shattered Triskelion", + ["Metadata/Items/Currency/GoldCoin"] = "Gold", + ["Metadata/Items/Currency/HarvestSeedBlue"] = "Primal Crystallised Lifeforce", + ["Metadata/Items/Currency/HarvestSeedBoss"] = "Sacred Crystallised Lifeforce", + ["Metadata/Items/Currency/HarvestSeedGreen"] = "Vivid Crystallised Lifeforce", + ["Metadata/Items/Currency/HarvestSeedRed"] = "Wild Crystallised Lifeforce", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportBlighted"] = "Blighted Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportBreachstone"] = "Otherworldly Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportCorrupted"] = "Vaal Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportDelirium"] = "Delirious Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportExplorers"] = "Explorer's Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportGuardian"] = "Influenced Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportJuiced"] = "Operative's Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportMoreHidden"] = "Comprehensive Scouting Report", + ["Metadata/Items/Currency/ScoutingReports/AtlasScoutingReportUnique"] = "Singular Scouting Report", + ["Metadata/Items/Currency/StrongboxKey"] = "Cryptic Key", + ["Metadata/Items/Expedition/ExpeditionVendorCurrencyFaction1"] = "Broken Circle Artifact", + ["Metadata/Items/Expedition/ExpeditionVendorCurrencyFaction2"] = "Black Scythe Artifact", + ["Metadata/Items/Expedition/ExpeditionVendorCurrencyFaction3"] = "Order Artifact", + ["Metadata/Items/Expedition/ExpeditionVendorCurrencyFaction4"] = "Sun Artifact", + ["Metadata/Items/Heist/HeistCoin"] = "Rogue's Marker", + ["Metadata/Items/MapFragments/CurrencyMavenKeyFragment"] = "Crescent Splinter", + ["Metadata/Items/MicrotransactionCurrency/MicrotransactionTencentEventCoin4"] = "Red Packet", + ["Metadata/Items/Sanctum/SanctumBronzeKey"] = "Bronze Key", + ["Metadata/Items/Sanctum/SanctumBronzeKeyDrop"] = "Bronze Key", + ["Metadata/Items/Sanctum/SanctumGoldKey"] = "Gold Key", + ["Metadata/Items/Sanctum/SanctumGoldKeyDrop"] = "Gold Key", + ["Metadata/Items/Sanctum/SanctumSilverKey"] = "Silver Key", + ["Metadata/Items/Sanctum/SanctumSilverKeyDrop"] = "Silver Key", +} diff --git a/src/Export/Scripts/miscdata.lua b/src/Export/Scripts/miscdata.lua index 5c95c538d7..d096a5a7b7 100644 --- a/src/Export/Scripts/miscdata.lua +++ b/src/Export/Scripts/miscdata.lua @@ -1,3 +1,5 @@ +---@module "Modules.Utils" +local utils = LoadModule("../Modules/Utils") local out = io.open("../Data/Misc.lua", "w") out:write("-- This file is automatically generated, do not edit!\n\n") out:write('---@class MiscDataExport\n') @@ -170,4 +172,11 @@ out:write('}\n') out:write('return data\n') out:close() +local currencies = {} +for row in dat("BaseItemTypes"):Rows() do + if row.ItemClass.Id == "StackableCurrency" and row.Name ~= "" then + currencies[row.Id] = row.Name + end +end +utils.saveTableToFile("../Data/CurrencyNames.lua", currencies, "This file contains mapping item names for every currency base item type ID.\nUsed for working with the currency exchange which uses item type IDs.") print("Misc data exported.") From 847aa25cc9065c68bf6c2396cb8a383ae0456044 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Thu, 27 Aug 2026 21:54:55 +0300 Subject: [PATCH 2/5] Fix item parsing to actually add the parsed line flags --- src/Classes/TradeQueryRequests.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/TradeQueryRequests.lua b/src/Classes/TradeQueryRequests.lua index 2290bfcf26..56e3720b71 100644 --- a/src/Classes/TradeQueryRequests.lua +++ b/src/Classes/TradeQueryRequests.lua @@ -408,7 +408,7 @@ function TradeQueryRequestsClass:FetchResultBlock(url, callback) s = s .. string.format("{%s}", flagName) end end - return escapeGGGString(modLine.description) + return s .. escapeGGGString(modLine.description) end t_insert(rawLines, "Implicits: " .. (#item.enchantMods + #item.runeMods + #item.implicitMods)) for _, modLine in ipairs(item.enchantMods or {}) do From 8721461800873ad4dfb7ba4a5926ea74108a57ef Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Fri, 28 Aug 2026 05:25:07 +1000 Subject: [PATCH 3/5] Fix exalts not being used --- spec/System/TestTradeQueryCurrency_spec.lua | 12 ++++++------ src/Classes/TradeQuery.lua | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/spec/System/TestTradeQueryCurrency_spec.lua b/spec/System/TestTradeQueryCurrency_spec.lua index 6d61a0e1c8..d69920c761 100644 --- a/spec/System/TestTradeQueryCurrency_spec.lua +++ b/spec/System/TestTradeQueryCurrency_spec.lua @@ -145,12 +145,12 @@ describe("TradeQuery Currency Conversion", function() lowest_ratio = { [CHAOS] = 200, [DIVINE] = 1 }, highest_stock = { [CHAOS] = 500, [DIVINE] = 500 }, }, - -- alch -> chaos (1 alch = 0.2 chaos), needs a second hop to divine + -- alch -> exalt (1 alch = 0.2 exalt), needs a second hop to divine { league = "Standard", - market_pair = { ALCH, CHAOS }, - lowest_ratio = { [ALCH] = 5, [CHAOS] = 1 }, - highest_stock = { [ALCH] = 1000, [CHAOS] = 1000 }, + market_pair = { ALCH, EXALT }, + lowest_ratio = { [ALCH] = 5, [EXALT] = 1 }, + highest_stock = { [ALCH] = 1000, [EXALT] = 1000 }, }, }) @@ -161,8 +161,8 @@ describe("TradeQuery Currency Conversion", function() assert.are.equal(1, rates.divine) assert.are.equal(0.1, rates.exalt) assert.are.equal(0.005, rates.chaos) - -- 0.2 chaos * 0.005 div/chaos = 0.001 div - assert.are.equal(0.001, rates.alch) + -- 0.2 exalt * 0.1 div/exalt = 0.02 div + assert.are.near(0.02, rates.alch, 0.0001) end) it("keeps the highest-stock listing for a currency", function() diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index c41147d238..612e30c75e 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -113,7 +113,8 @@ end local generalCurrencies = { ["Metadata/Items/Currency/CurrencyModValues"] = true, - ["Metadata/Items/Currency/CurrencyRerollRare"] = true + ["Metadata/Items/Currency/CurrencyRerollRare"] = true, + ["Metadata/Items/Currency/CurrencyAddModToRare"] = true } -- Method to pull down and interpret the Currency Exchange JSON endpoint data @@ -189,7 +190,7 @@ function TradeQueryClass:PullCXData() local fromID = entry.market_pair[1] local toID = entry.market_pair[2] - -- Normalize entries to price each currency in chaos or divines. + -- Normalize entries to price each currency in chaos, exalts or divines. if generalCurrencies[fromID] and toID ~= "Metadata/Items/Currency/CurrencyModValues" then fromID, toID = toID, fromID end From 373b0c3637e29c727a44b19b5a3d42765d852ebb Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Fri, 28 Aug 2026 05:44:57 +1000 Subject: [PATCH 4/5] Remove old table + fix exalt name --- spec/System/TestTradeQueryCurrency_spec.lua | 6 +++--- src/Classes/TradeQuery.lua | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/spec/System/TestTradeQueryCurrency_spec.lua b/spec/System/TestTradeQueryCurrency_spec.lua index d69920c761..b9b530d551 100644 --- a/spec/System/TestTradeQueryCurrency_spec.lua +++ b/spec/System/TestTradeQueryCurrency_spec.lua @@ -66,7 +66,7 @@ describe("TradeQuery Currency Conversion", function() { id = "sep", text = "" }, -- separator, must be ignored { id = "divine", text = "Divine Orb" }, { id = "chaos", text = "Chaos Orb" }, - { id = "exalt", text = "Exalted Orb" }, + { id = "exalted", text = "Exalted Orb" }, { id = "alch", text = "Orb of Alchemy" }, } } } @@ -159,7 +159,7 @@ describe("TradeQuery Currency Conversion", function() local rates = mock_tradeQuery.pbCurrencyConversion.poe2.Standard assert.is_not_nil(rates) assert.are.equal(1, rates.divine) - assert.are.equal(0.1, rates.exalt) + assert.are.equal(0.1, rates.exalted) assert.are.equal(0.005, rates.chaos) -- 0.2 exalt * 0.1 div/exalt = 0.02 div assert.are.near(0.02, rates.alch, 0.0001) @@ -183,7 +183,7 @@ describe("TradeQuery Currency Conversion", function() mock_tradeQuery:PullCXData() - assert.are.equal(0.2, mock_tradeQuery.pbCurrencyConversion.poe2.Standard.exalt) + assert.are.equal(0.2, mock_tradeQuery.pbCurrencyConversion.poe2.Standard.exalted) end) it("skips listings with a zero ratio", function() diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 612e30c75e..5f81f6e230 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -41,8 +41,6 @@ function TradeQueryClass:TradeQuery(itemsTab) -- for each realm and league, a table of values of each currency in div --- @type table>> self.pbCurrencyConversion = {} - self.lastCurrencyFileTime = { } - self.pbFileTimestampDiff = { } self.pbRealm = "" self.pbRealmIndex = 1 self.pbLeagueIndex = 1 From d58dbd4195fa5bdbc51135329c928c82b017cff2 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Fri, 28 Aug 2026 05:52:57 +1000 Subject: [PATCH 5/5] Revert exalt change --- spec/System/TestTradeQueryCurrency_spec.lua | 12 ++++++------ src/Classes/TradeQuery.lua | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/spec/System/TestTradeQueryCurrency_spec.lua b/spec/System/TestTradeQueryCurrency_spec.lua index b9b530d551..5e378b7f0d 100644 --- a/spec/System/TestTradeQueryCurrency_spec.lua +++ b/spec/System/TestTradeQueryCurrency_spec.lua @@ -145,12 +145,12 @@ describe("TradeQuery Currency Conversion", function() lowest_ratio = { [CHAOS] = 200, [DIVINE] = 1 }, highest_stock = { [CHAOS] = 500, [DIVINE] = 500 }, }, - -- alch -> exalt (1 alch = 0.2 exalt), needs a second hop to divine + -- alch -> chaos (1 alch = 0.2 chaos), needs a second hop to divine { league = "Standard", - market_pair = { ALCH, EXALT }, - lowest_ratio = { [ALCH] = 5, [EXALT] = 1 }, - highest_stock = { [ALCH] = 1000, [EXALT] = 1000 }, + market_pair = { ALCH, CHAOS }, + lowest_ratio = { [ALCH] = 5, [CHAOS] = 1 }, + highest_stock = { [ALCH] = 1000, [CHAOS] = 1000 }, }, }) @@ -161,8 +161,8 @@ describe("TradeQuery Currency Conversion", function() assert.are.equal(1, rates.divine) assert.are.equal(0.1, rates.exalted) assert.are.equal(0.005, rates.chaos) - -- 0.2 exalt * 0.1 div/exalt = 0.02 div - assert.are.near(0.02, rates.alch, 0.0001) + -- 0.2 chaos * 0.005 div/chaos = 0.001 div + assert.are.equal(0.001, rates.alch) end) it("keeps the highest-stock listing for a currency", function() diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 5f81f6e230..0a568b5eff 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -111,8 +111,7 @@ end local generalCurrencies = { ["Metadata/Items/Currency/CurrencyModValues"] = true, - ["Metadata/Items/Currency/CurrencyRerollRare"] = true, - ["Metadata/Items/Currency/CurrencyAddModToRare"] = true + ["Metadata/Items/Currency/CurrencyRerollRare"] = true } -- Method to pull down and interpret the Currency Exchange JSON endpoint data @@ -188,7 +187,7 @@ function TradeQueryClass:PullCXData() local fromID = entry.market_pair[1] local toID = entry.market_pair[2] - -- Normalize entries to price each currency in chaos, exalts or divines. + -- Normalize entries to price each currency in chaos or divines. if generalCurrencies[fromID] and toID ~= "Metadata/Items/Currency/CurrencyModValues" then fromID, toID = toID, fromID end