-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local Mail = TSM.Accounting:NewPackage("Mail") local Event = TSM.Include("Util.Event") local Delay = TSM.Include("Util.Delay") local String = TSM.Include("Util.String") local ItemString = TSM.Include("Util.ItemString") local ItemInfo = TSM.Include("Service.ItemInfo") local InventoryInfo = TSM.Include("Service.InventoryInfo") local AuctionTracking = TSM.Include("Service.AuctionTracking") local Inventory = TSM.Include("Service.Inventory") local private = { hooks = {}, } local SECONDS_PER_DAY = 24 * 60 * 60 local EXPIRED_MATCH_TEXT = AUCTION_EXPIRED_MAIL_SUBJECT:gsub("%%s", "") local CANCELLED_MATCH_TEXT = AUCTION_REMOVED_MAIL_SUBJECT:gsub("%%s", "") local OUTBID_MATCH_TEXT = AUCTION_OUTBID_MAIL_SUBJECT:gsub("%%s", "(.+)") -- ============================================================================ -- Module Functions -- ============================================================================ function Mail.OnInitialize() Event.Register("MAIL_SHOW", function() Delay.AfterTime("ACCOUNTING_GET_SELLERS", 0.1, private.RequestSellerInfo, 0.1) end) Event.Register("MAIL_CLOSED", function() Delay.Cancel("ACCOUNTING_GET_SELLERS") end) -- hook certain mail functions private.hooks.TakeInboxItem = TakeInboxItem TakeInboxItem = function(...) Mail:ScanCollectedMail("TakeInboxItem", 1, ...) end private.hooks.TakeInboxMoney = TakeInboxMoney TakeInboxMoney = function(...) Mail:ScanCollectedMail("TakeInboxMoney", 1, ...) end private.hooks.AutoLootMailItem = AutoLootMailItem AutoLootMailItem = function(...) Mail:ScanCollectedMail("AutoLootMailItem", 1, ...) end private.hooks.SendMail = SendMail SendMail = private.CheckSendMail end -- ============================================================================ -- Inbox Functions -- ============================================================================ function private.RequestSellerInfo() local isDone = true for i = 1, GetInboxNumItems() do local invoiceType, _, seller = GetInboxInvoiceInfo(i) if invoiceType and seller == "" then isDone = false end end if isDone and GetInboxNumItems() > 0 then Delay.Cancel("ACCOUNTING_GET_SELLERS") end end function private.CanLootMailIndex(index, copper) local currentMoney = GetMoney() assert(currentMoney <= MAXIMUM_BID_PRICE) -- check if this would put them over the gold cap if currentMoney + copper > MAXIMUM_BID_PRICE then return end local _, _, _, _, _, _, _, itemCount = GetInboxHeaderInfo(index) if not itemCount or itemCount == 0 then return true end for j = 1, ATTACHMENTS_MAX_RECEIVE do -- TODO: prevent items that you can't loot because of internal mail error if CalculateTotalNumberOfFreeBagSlots() <= 0 then return end local link = GetInboxItemLink(index, j) local itemString = ItemString.Get(link) local _, _, _, count = GetInboxItem(index, j) local quantity = count or 0 local maxUnique = private.GetInboxMaxUnique(index, j) -- dont record unique items that we can't loot local playerQty = Inventory.GetBagQuantity(itemString) + Inventory.GetBankQuantity(itemString) + Inventory.GetReagentBankQuantity(itemString) if maxUnique > 0 and maxUnique < playerQty + quantity then return end if itemString then for bag = 0, NUM_BAG_SLOTS do if InventoryInfo.ItemWillGoInBag(link, bag) then for slot = 1, GetContainerNumSlots(bag) do local iString = ItemString.Get(GetContainerItemLink(bag, slot)) if iString == itemString then local _, stackSize = GetContainerItemInfo(bag, slot) local maxStackSize = ItemInfo.GetMaxStack(itemString) or 1 if (maxStackSize - stackSize) >= quantity then return true end elseif not iString then return true end end end end end end end function private.GetInboxMaxUnique(index, num) if not num then num = 1 end if not TSMScanTooltip then CreateFrame("GameTooltip", "TSMScanTooltip", UIParent, "GameTooltipTemplate") end TSMScanTooltip:SetOwner(UIParent, "ANCHOR_NONE") TSMScanTooltip:ClearLines() local _, speciesId = TSMScanTooltip:SetInboxItem(index, num) if (speciesId or 0) > 0 then return 0 else for id = 2, TSMScanTooltip:NumLines() do local text = private.GetTooltipText(_G["TSMScanTooltipTextLeft"..id]) if text then if text == ITEM_UNIQUE then return 1 else local match = text and strmatch(text, "^"..ITEM_UNIQUE.." %((%d+)%)$") if match then return tonumber(match) end end end end end return 0 end function private.GetTooltipText(text) local textStr = strtrim(text and text:GetText() or "") if textStr == "" then return end return textStr end -- scans the mail that the player just attempted to collected (Pre-Hook) function Mail:ScanCollectedMail(oFunc, attempt, index, subIndex) local invoiceType, itemName, buyer, bid, _, _, ahcut, _, _, _, quantity = GetInboxInvoiceInfo(index) buyer = buyer or (invoiceType == "buyer" and AUCTION_HOUSE_MAIL_MULTIPLE_SELLERS or AUCTION_HOUSE_MAIL_MULTIPLE_BUYERS) local _, stationeryIcon, sender, subject, money, codAmount, daysLeft = GetInboxHeaderInfo(index) if not subject then return end if attempt > 2 then if buyer == "" then buyer = "?" elseif sender == "" then sender = "?" end end local success = false if invoiceType == "seller" and buyer and buyer ~= "" then -- AH Sales local saleTime = (time() + (daysLeft - 30) * SECONDS_PER_DAY) local itemString = ItemInfo.ItemNameToItemString(itemName) if not itemString or itemString == ItemString.GetUnknown() then itemString = AuctionTracking.GetSaleHintItemString(itemName, quantity, bid) end if private.CanLootMailIndex(index, (bid - ahcut)) then if itemString then local copper = floor((bid - ahcut) / quantity + 0.5) TSM.Accounting.Transactions.InsertAuctionSale(itemString, quantity, copper, buyer, saleTime) end success = true end elseif invoiceType == "buyer" and buyer and buyer ~= "" then -- AH Buys local copper = floor(bid / quantity + 0.5) if not TSM.IsWowClassic() then if subIndex then quantity = select(4, GetInboxItem(index, subIndex)) else quantity = 0 for i = 1, ATTACHMENTS_MAX do quantity = quantity + (select(4, GetInboxItem(index, i)) or 0) end end end local link = (subIndex or 1) == 1 and private.GetFirstInboxItemLink(index) or GetInboxItemLink(index, subIndex or 1) local itemString = ItemString.Get(link) if itemString and private.CanLootMailIndex(index, 0) then local buyTime = (time() + (daysLeft - 30) * SECONDS_PER_DAY) TSM.Accounting.Transactions.InsertAuctionBuy(itemString, quantity, copper, buyer, buyTime) success = true end elseif codAmount > 0 then -- COD Buys (only if all attachments are same item) local link = (subIndex or 1) == 1 and private.GetFirstInboxItemLink(index) or GetInboxItemLink(index, subIndex or 1) local itemString = ItemString.Get(link) if itemString and sender then local name = ItemInfo.GetName(link) local total = 0 local stacks = 0 local ignore = false for i = 1, ATTACHMENTS_MAX_RECEIVE do local nameCheck, _, _, count = GetInboxItem(index, i) if nameCheck and count then if nameCheck == name then total = total + count stacks = stacks + 1 else ignore = true end end end if total ~= 0 and not ignore and private.CanLootMailIndex(index, codAmount) then local copper = floor(codAmount / total + 0.5) local buyTime = (time() + (daysLeft - 3) * SECONDS_PER_DAY) local maxStack = ItemInfo.GetMaxStack(link) for _ = 1, stacks do local stackSize = (total >= maxStack) and maxStack or total TSM.Accounting.Transactions.InsertCODBuy(itemString, stackSize, copper, sender, buyTime) total = total - stackSize if total <= 0 then break end end end success = true end elseif money > 0 and invoiceType ~= "seller" and not strfind(subject, OUTBID_MATCH_TEXT) then local str = nil if GetLocale() == "deDE" then str = gsub(subject, gsub(COD_PAYMENT, String.Escape("%1$s"), ""), "") else str = gsub(subject, gsub(COD_PAYMENT, String.Escape("%s"), ""), "") end local saleTime = (time() + (daysLeft - 31) * SECONDS_PER_DAY) if sender and private.CanLootMailIndex(index, money) then if str and strfind(str, "TSM$") then -- payment for a COD the player sent local codName = strtrim(strmatch(str, "([^%(]+)")) local qty = strmatch(str, "%(([0-9]+)%)") qty = tonumber(qty) local itemString = ItemInfo.ItemNameToItemString(codName) if itemString then local copper = floor(money / qty + 0.5) local maxStack = ItemInfo.GetMaxStack(itemString) or 1 local stacks = ceil(qty / maxStack) for _ = 1, stacks do local stackSize = (qty >= maxStack) and maxStack or qty TSM.Accounting.Transactions.InsertCODSale(itemString, stackSize, copper, sender, saleTime) qty = qty - stackSize if qty <= 0 then break end end end else -- record a money transfer TSM.Accounting.Money.InsertMoneyTransferIncome(money, sender, saleTime) end success = true end elseif strfind(subject, EXPIRED_MATCH_TEXT) then -- expired auction local expiredTime = (time() + (daysLeft - 30) * SECONDS_PER_DAY) local link = (subIndex or 1) == 1 and private.GetFirstInboxItemLink(index) or GetInboxItemLink(index, subIndex or 1) local _, _, _, count = GetInboxItem(index, subIndex or 1) if TSM.IsWowClassic() then quantity = count or 0 else if subIndex then quantity = select(4, GetInboxItem(index, subIndex)) else quantity = 0 for i = 1, ATTACHMENTS_MAX do quantity = quantity + (select(4, GetInboxItem(index, i)) or 0) end end end local itemString = ItemString.Get(link) if private.CanLootMailIndex(index, 0) and itemString and quantity then TSM.Accounting.Auctions.InsertExpire(itemString, quantity, expiredTime) success = true end elseif strfind(subject, CANCELLED_MATCH_TEXT) then -- cancelled auction local cancelledTime = (time() + (daysLeft - 30) * SECONDS_PER_DAY) local link = (subIndex or 1) == 1 and private.GetFirstInboxItemLink(index) or GetInboxItemLink(index, subIndex or 1) local _, _, _, count = GetInboxItem(index, subIndex or 1) if TSM.IsWowClassic() then quantity = count or 0 else if subIndex then quantity = select(4, GetInboxItem(index, subIndex)) else quantity = 0 for i = 1, ATTACHMENTS_MAX do quantity = quantity + (select(4, GetInboxItem(index, i)) or 0) end end end local itemString = ItemString.Get(link) if private.CanLootMailIndex(index, 0) and itemString and quantity then TSM.Accounting.Auctions.InsertCancel(itemString, quantity, cancelledTime) success = true end end if success then private.hooks[oFunc](index, subIndex) elseif (not stationeryIcon or (invoiceType and (not buyer or buyer == ""))) and attempt <= 5 then Delay.AfterTime("accountingHookDelay", 0.2, function() Mail:ScanCollectedMail(oFunc, attempt + 1, index, subIndex) end) elseif attempt > 5 then private.hooks[oFunc](index, subIndex) else private.hooks[oFunc](index, subIndex) end end -- ============================================================================ -- Sending Functions -- ============================================================================ -- scans the mail that the player just attempted to send (Pre-Hook) to see if COD function private.CheckSendMail(destination, currentSubject, ...) local codAmount = GetSendMailCOD() local moneyAmount = GetSendMailMoney() local mailCost = GetSendMailPrice() local subject local total = 0 local ignore = false if codAmount ~= 0 then for i = 1, 12 do local itemName, _, _, count = GetSendMailItem(i) if itemName and count then if not subject then subject = itemName end if subject == itemName then total = total + count else ignore = true end end end else ignore = true end if moneyAmount > 0 then -- add a record for the money transfer TSM.Accounting.Money.InsertMoneyTransferExpense(moneyAmount, destination) mailCost = mailCost - moneyAmount end TSM.Accounting.Money.InsertPostageExpense(mailCost, destination) if not ignore then private.hooks.SendMail(destination, subject .. " (" .. total .. ") TSM", ...) else private.hooks.SendMail(destination, currentSubject, ...) end end function private.GetFirstInboxItemLink(index) if not TSMAccountingMailTooltip then CreateFrame("GameTooltip", "TSMAccountingMailTooltip", UIParent, "GameTooltipTemplate") end TSMAccountingMailTooltip:SetOwner(UIParent, "ANCHOR_NONE") TSMAccountingMailTooltip:ClearLines() local _, speciesId, level, breedQuality, maxHealth, power, speed = TSMAccountingMailTooltip:SetInboxItem(index) local link = nil if (speciesId or 0) > 0 then link = ItemInfo.GetLink(strjoin(":", "p", speciesId, level, breedQuality, maxHealth, power, speed)) else link = GetInboxItemLink(index, 1) end TSMAccountingMailTooltip:Hide() return link end