-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local Util = TSM.Auctioning:NewPackage("Util") local TempTable = TSM.Include("Util.TempTable") local Vararg = TSM.Include("Util.Vararg") local String = TSM.Include("Util.String") local Math = TSM.Include("Util.Math") local CustomPrice = TSM.Include("Service.CustomPrice") local PlayerInfo = TSM.Include("Service.PlayerInfo") local private = { priceCache = {}, } local INVALID_PRICE = {} local VALID_PRICE_KEYS = { minPrice = true, normalPrice = true, maxPrice = true, undercut = true, cancelRepostThreshold = true, priceReset = true, aboveMax = true, postCap = true, stackSize = true, keepQuantity = true, maxExpires = true, } local IS_GOLD_PRICE_KEY = { minPrice = true, normalPrice = true, maxPrice = true, undercut = TSM.IsWowClassic(), priceReset = true, aboveMax = true, } -- ============================================================================ -- Module Functions -- ============================================================================ function Util.GetPrice(key, operation, itemString) assert(VALID_PRICE_KEYS[key]) local cacheKey = key..tostring(operation)..itemString if private.priceCache.updateTime ~= GetTime() then wipe(private.priceCache) private.priceCache.updateTime = GetTime() end if not private.priceCache[cacheKey] then local value = nil if key == "aboveMax" or key == "priceReset" then -- redirect to the selected price (if applicable) local priceKey = operation[key] if VALID_PRICE_KEYS[priceKey] then value = Util.GetPrice(priceKey, operation, itemString) end else value = CustomPrice.GetValue(operation[key], itemString, not IS_GOLD_PRICE_KEY[key]) end if not TSM.IsWowClassic() and IS_GOLD_PRICE_KEY[key] then value = value and Math.Ceil(value, COPPER_PER_SILVER) or nil else value = value and Math.Round(value) or nil end local minValue, maxValue = TSM.Operations.Auctioning.GetMinMaxValues(key) private.priceCache[cacheKey] = (value and value >= minValue and value <= maxValue) and value or INVALID_PRICE end if private.priceCache[cacheKey] == INVALID_PRICE then return nil end return private.priceCache[cacheKey] end function Util.GetLowestAuction(subRows, itemString, operationSettings, resultTbl) if not TSM.IsWowClassic() then local foundLowest = false for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft = subRow:GetListingInfo() if not foundLowest and not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) then local ownerStr = subRow:GetOwnerInfo() local _, auctionId = subRow:GetListingInfo() local _, itemMinBid = subRow:GetBidInfo() local firstSeller = strsplit(",", ownerStr) resultTbl.buyout = itemBuyout resultTbl.bid = itemMinBid resultTbl.seller = firstSeller resultTbl.auctionId = auctionId resultTbl.isWhitelist = TSM.db.factionrealm.auctioningOptions.whitelist[strlower(firstSeller)] and true or false resultTbl.isPlayer = PlayerInfo.IsPlayer(firstSeller, true, true, true) if not subRow:HasOwners() then resultTbl.hasInvalidSeller = true end foundLowest = true end end return foundLowest else local hasInvalidSeller = nil local ignoreWhitelist = nil local lowestItemBuyout = nil local lowestAuction = nil for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft = subRow:GetListingInfo() if not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) then assert(itemBuyout and itemBuyout > 0) lowestItemBuyout = lowestItemBuyout or itemBuyout if itemBuyout == lowestItemBuyout then local ownerStr = subRow:GetOwnerInfo() local _, auctionId = subRow:GetListingInfo() local _, itemMinBid = subRow:GetBidInfo() local temp = TempTable.Acquire() temp.buyout = itemBuyout temp.bid = itemMinBid temp.seller = ownerStr temp.auctionId = auctionId temp.isWhitelist = TSM.db.factionrealm.auctioningOptions.whitelist[strlower(ownerStr)] and true or false temp.isPlayer = PlayerInfo.IsPlayer(ownerStr, true, true, true) if not temp.isWhitelist and not temp.isPlayer then -- there is a non-whitelisted competitor, so we don't care if a whitelisted competitor also posts at this price ignoreWhitelist = true end if not subRow:HasOwners() and next(TSM.db.factionrealm.auctioningOptions.whitelist) then hasInvalidSeller = true end if operationSettings.blacklist then for _, player in Vararg.Iterator(strsplit(",", operationSettings.blacklist)) do if String.SeparatedContains(strlower(ownerStr), ",", strlower(strtrim(player))) then temp.isBlacklist = true end end end if not lowestAuction then lowestAuction = temp elseif private.LowestAuctionCompare(temp, lowestAuction) then TempTable.Release(lowestAuction) lowestAuction = temp else TempTable.Release(temp) end end end end if not lowestAuction then return false end for k, v in pairs(lowestAuction) do resultTbl[k] = v end TempTable.Release(lowestAuction) if resultTbl.isWhitelist and ignoreWhitelist then resultTbl.isWhitelist = false end resultTbl.hasInvalidSeller = hasInvalidSeller return true end end function Util.GetPlayerAuctionCount(subRows, itemString, operationSettings, findBid, findBuyout, findStackSize) local playerQuantity = 0 for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft = subRow:GetListingInfo() if not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) then local _, itemMinBid = subRow:GetBidInfo() if itemMinBid == findBid and itemBuyout == findBuyout and (not TSM.IsWowClassic() or quantity == findStackSize) then local count = private.GetPlayerAuctionCount(subRow) if not TSM.IsWowClassic() and count == 0 and playerQuantity > 0 then -- there's another player's auction after ours, so stop counting break end playerQuantity = playerQuantity + count end end end return playerQuantity end function Util.GetPlayerLowestBuyout(subRows, itemString, operationSettings) local lowestItemBuyout, lowestItemAuctionId = nil, nil for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft, auctionId = subRow:GetListingInfo() if not lowestItemBuyout and not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) and private.GetPlayerAuctionCount(subRow) > 0 then lowestItemBuyout = itemBuyout lowestItemAuctionId = auctionId end end return lowestItemBuyout, lowestItemAuctionId end function Util.GetLowestNonPlayerAuctionId(subRows, itemString, operationSettings, lowestItemBuyout) local lowestItemAuctionId = nil for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft, auctionId = subRow:GetListingInfo() if not lowestItemAuctionId and not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) and private.GetPlayerAuctionCount(subRow) == 0 and itemBuyout == lowestItemBuyout then lowestItemAuctionId = auctionId end end return lowestItemAuctionId end function Util.IsPlayerOnlySeller(subRows, itemString, operationSettings) local isOnly = true for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft = subRow:GetListingInfo() if isOnly and not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) and private.GetPlayerAuctionCount(subRow) < (TSM.IsWowClassic() and 1 or quantity) then isOnly = false end end return isOnly end function Util.GetNextLowestItemBuyout(subRows, itemString, lowestAuction, operationSettings) local nextLowestItemBuyout = nil for _, subRow in ipairs(subRows) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft, auctionId = subRow:GetListingInfo() local isLower = itemBuyout > lowestAuction.buyout or (itemBuyout == lowestAuction.buyout and auctionId < lowestAuction.auctionId) if not nextLowestItemBuyout and not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) and isLower then nextLowestItemBuyout = itemBuyout end end return nextLowestItemBuyout end function Util.GetQueueStatus(query) local numProcessed, numConfirmed, numFailed, totalNum = 0, 0, 0, 0 query:OrderBy("auctionId", true) for _, row in query:Iterator() do local rowNumStacks, rowNumProcessed, rowNumConfirmed, rowNumFailed = row:GetFields("numStacks", "numProcessed", "numConfirmed", "numFailed") totalNum = totalNum + rowNumStacks numProcessed = numProcessed + rowNumProcessed numConfirmed = numConfirmed + rowNumConfirmed numFailed = numFailed + rowNumFailed end query:Release() return numProcessed, numConfirmed, numFailed, totalNum end function Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) if timeLeft <= operationSettings.ignoreLowDuration then -- ignoring low duration return true elseif TSM.IsWowClassic() and operationSettings.matchStackSize and quantity ~= Util.GetPrice("stackSize", operationSettings, itemString) then -- matching stack size return true elseif operationSettings.priceReset == "ignore" then local minPrice = Util.GetPrice("minPrice", operationSettings, itemString) local undercut = Util.GetPrice("undercut", operationSettings, itemString) if minPrice and itemBuyout - undercut < minPrice then -- ignoring auctions below threshold return true end end return false end function Util.GetFilteredSubRows(query, itemString, operationSettings, result) for _, subRow in query:ItemSubRowIterator(itemString) do local _, itemBuyout = subRow:GetBuyouts() local quantity = subRow:GetQuantities() local timeLeft = subRow:GetListingInfo() if not Util.IsFiltered(itemString, operationSettings, itemBuyout, quantity, timeLeft) then tinsert(result, subRow) end end sort(result, private.SubRowSortHelper) end -- ============================================================================ -- Private Helper Functions -- ============================================================================ function private.SubRowSortHelper(a, b) local _, aItemBuyout = a:GetBuyouts() local _, bItemBuyout = b:GetBuyouts() if aItemBuyout ~= bItemBuyout then return aItemBuyout < bItemBuyout end local _, aAuctionId = a:GetListingInfo() local _, bAuctionId = b:GetListingInfo() return aAuctionId > bAuctionId end function private.LowestAuctionCompare(a, b) if a.isBlacklist ~= b.isBlacklist then return a.isBlacklist end if a.isWhitelist ~= b.isWhitelist then return a.isWhitelist end if a.auctionId ~= b.auctionId then return a.auctionId > b.auctionId end if a.isPlayer ~= b.isPlayer then return b.isPlayer end return tostring(a) < tostring(b) end function private.GetPlayerAuctionCount(subRow) local ownerStr, numOwnerItems = subRow:GetOwnerInfo() if TSM.IsWowClassic() then return PlayerInfo.IsPlayer(ownerStr, true, true, true) and select(2, subRow:GetQuantities()) or 0 else return numOwnerItems end end