AuctioneerSuite/BeanCounter/BeanCounterMail.lua
2026-04-13 17:48:13 -04:00

655 lines
32 KiB
Lua

--[[
Auctioneer Addon for World of Warcraft(tm).
Version: 5.9.4961 (WhackyWallaby)
Revision: $Id: BeanCounterMail.lua 4933 2010-10-13 17:16:14Z Nechckn $
URL: http://auctioneeraddon.com/
BeanCounterMail - Handles recording of all auction house related mail
License:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program(see GPL.txt); if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Note:
This AddOn's source code is specifically designed to work with
World of Warcraft's interpreted AddOn system.
You have an implicit license to use this AddOn with these facilities
since that is it's designated purpose as per:
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
]]
LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterMail.lua $","$Rev: 4933 $","5.1.DEV.", 'auctioneer', 'libs')
local lib = BeanCounter
local private, print, get, set, _BC = lib.getLocals() --_BC localization function
local _G = _G
local pairs,ipairs,select,type = pairs,ipairs,select,type
local tonumber,tostring,format = tonumber,tostring,format
local tinsert,tremove = tinsert,tremove
local strsplit = strsplit
local time = time
local GetTime = GetTime
local floor = floor
local function debugPrint(...)
if get("util.beancounter.debugMail") then
private.debugPrint("BeanCounterMail",...)
end
end
local expiredLocale = AUCTION_EXPIRED_MAIL_SUBJECT:gsub("(.+)%%s", "%1")
local salePendingLocale = AUCTION_INVOICE_MAIL_SUBJECT:gsub("(.+)%%s", "%1") --sale pending
local outbidLocale = AUCTION_OUTBID_MAIL_SUBJECT:gsub("(.+)%%s", "%1")
local cancelledLocale = AUCTION_REMOVED_MAIL_SUBJECT:gsub("(.+)%%s", "%1")
local successLocale = AUCTION_SOLD_MAIL_SUBJECT:gsub("(.+)%%s", "%1")
local wonLocale = AUCTION_WON_MAIL_SUBJECT:gsub("(.+)%%s", "%1")
--[[
Essamyns improved parser. Need to test before comiting
local expiredLocale = AUCTION_EXPIRED_MAIL_SUBJECT:gsub("%%s", "(.+)")
local salePendingLocale = AUCTION_INVOICE_MAIL_SUBJECT:gsub("%%s", "(.+)") --sale pending
local outbidLocale = AUCTION_OUTBID_MAIL_SUBJECT:gsub("%%s", "(.+)")
local cancelledLocale = AUCTION_REMOVED_MAIL_SUBJECT:gsub("%%s", "(.+)")
local successLocale = AUCTION_SOLD_MAIL_SUBJECT:gsub("%%s", "(.+)")
local wonLocale = AUCTION_WON_MAIL_SUBJECT:gsub("%%s", "(.+)")
]]
local reportTotalMail, reportReadMail = 0, 0 --Used as a debug check on mail scanning engine
local registeredAltaholicHook = false
local registeredInboxFrameHook = false
function private.mailMonitor(event,arg1)
if (event == "MAIL_INBOX_UPDATE") then
private.updateInboxStart()
elseif (event == "MAIL_SHOW") then
private.inboxStart = {} --clear the inbox list, if we errored out this should give us a fresh start.
if not registeredInboxFrameHook then --make sure we only ever register this hook once
registeredInboxFrameHook = true
hooksecurefunc("InboxFrame_OnClick", private.mailFrameClick)
hooksecurefunc("InboxFrame_Update", private.mailFrameUpdate)
end
--We cannot use mail show since the GetInboxNumItems() returns 0 till the first "MAIL_INBOX_UPDATE"
elseif (event == "MAIL_CLOSED") then
private.HideMailGUI()
private.sumDatabase() --Sum total fo DB for the display on browse pane
end
end
--[[Watch who reads the mail. and what they read. Use this to play nicely with altaholic and other addons]]
private.mailReadOveride = {}
function private.PreGetInboxTextHook(n, ...)
if n and n > 0 then
local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n)
if sender and subject and not wasRead then
--print("they read", n, sender, subject)
private.mailReadOveride[n] = sender..n
elseif wasRead then
--print("Already read", n, sender, subject)
end
end
return private.GetInboxText(n, ...)
end
--hook and replace GetInboxText()
private.GetInboxText = GetInboxText
GetInboxText = private.PreGetInboxTextHook
--New function to hide/unhide mail GUI.
local HideMailGUI
function private.HideMailGUI( hide )
if hide then
HideMailGUI = true
InboxCloseButton:Hide()
InboxFrame:Hide()
MailFrameTab2:Hide()
private.MailGUI:Show()
private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated
else
HideMailGUI = false
InboxCloseButton:Show()
InboxFrame:Show()
MailFrameTab2:Show()
private.MailGUI:Hide()
private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated
end
end
--Mailbox Snapshots
function private.updateInboxStart()
reportTotalMail = GetInboxNumItems()
for n = reportTotalMail, 1, -1 do
local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n)
if sender and subject and (not wasRead or private.mailReadOveride[n]) then
local auctionHouse --A, H, N flag for which AH the trxn came from
if sender ==_BC('MailAllianceAuctionHouse') then
auctionHouse = "A"
elseif sender == _BC('MailHordeAuctionHouse') then
auctionHouse = "H"
elseif sender == _BC('MailNeutralAuctionHouse') then
auctionHouse = "N"
end
if subject == "" then debugPrint("Skipping mail #", n, "The server is not sending the subject data. Mail will be left unread and we will retry") end
if auctionHouse and subject ~= "" then -- subject ~= "" when the server fails, this will prevent us from reading the mail giving the server more time to get its shit togather
private.HideMailGUI(true)
wasRead = wasRead or 0 --its nil unless its has been read
local itemLink = GetInboxItemLink(n, 1)
local _, _, stack, _, _ = GetInboxItem(n)
local invoiceType, itemName, playerName, bid, buyout, deposit, consignment, retrieved, startTime = private.getInvoice(n,sender, subject)
tinsert(private.inboxStart, {["n"] = n, ["sender"]=sender, ["subject"]=subject,["money"]=money, ["read"]=wasRead, ["age"] = daysLeft,
["invoiceType"] = invoiceType, ["itemName"] = itemName, ["Seller/buyer"] = playerName, ['bid'] = bid, ["buyout"] = buyout,
["deposit"] = deposit, ["fee"] = consignment, ["retrieved"] = retrieved, ["startTime"] = startTime, ["itemLink"] = itemLink, ["stack"] = stack, ["auctionHouse"] = auctionHouse,
})
private.GetInboxText(n) --read message
reportReadMail = reportReadMail + 1
end
end
private.lastCheckedMail = GetTime() --this keeps us from hiding the mail UI to early and causing flicker
end
private.mailReadOveride = {}
private.wipeSearchCache() --clear the search cache, we are updating data so it is now outdated
end
function private.getInvoice(n, sender, subject)
if sender:match(_BC('MailAllianceAuctionHouse')) or sender:match(_BC('MailHordeAuctionHouse')) or sender:match(_BC('MailNeutralAuctionHouse')) then
if subject:match(successLocale) or subject:match(wonLocale) then
local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(n)
if invoiceType and playerName and playerName ~= "" and bid and bid > 0 then --Silly name throttling lead to missed invoice lookups
--debugPrint("getInvoice", invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "yes")
return invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "yes", time()
else
--debugPrint("getInvoice", invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "no")
return invoiceType, itemName, playerName, bid, buyout, deposit, consignment, "no", time()
end
end
end
return
end
private.lastCheckedMail = GetTime()
function private.mailonUpdate()
local total = #private.inboxStart
if total > 0 then
for i = total, 1, -1 do -- in pairs(private.inboxStart) do
--update mail GUI Count
local count = #private.inboxStart
--private.CountGUI:SetText("Recording: "..total-count.." of "..total.." items")
private.CountGUI:SetText("Recording: "..reportReadMail.." items, Please wait")--not happy, would like a better count
local data = private.inboxStart[i]
if not data.retrieved then --Send non invoiceable mails through
tinsert(private.reconcilePending, data)
--private.inboxStart[i] = nil
tremove(private.inboxStart, i)
--debugPrint("not a invoice mail type", i)
elseif data.retrieved == "failed" then
tinsert(private.reconcilePending, data)
--private.inboxStart[i] = nil
tremove(private.inboxStart, i)
--debugPrint("data.retrieved == failed", i)
elseif data.retrieved == "yes" then
tinsert(private.reconcilePending, data)
--private.inboxStart[i] = nil
tremove(private.inboxStart, i)
--debugPrint("data.retrieved == yes", i)
elseif time() - data.startTime > get("util.beacounter.invoicetime") then --time exceded so fail it and process on next update
debugPrint("time to retrieve invoice exceeded, most likely waiting on players name if this is blank>..", data["Seller/buyer"], i)
data["retrieved"] = "failed" --time to get invoice exceded
else
--debugPrint("Invoice retieve attempt",data["subject"])
data["invoiceType"], data["itemName"], data["Seller/buyer"], data['bid'], data["buyout"] , data["deposit"] , data["fee"], data["retrieved"], _ = private.getInvoice(data.n, data.sender, data.subject)
end
end
end
if (#private.inboxStart == 0) and (HideMailGUI == true) and (private.lastCheckedMail + 1 < GetTime() ) then --time delay added to prevent possible flicker
debugPrint("Total Mail in inbox:{{", reportTotalMail, "}}Reading:{{",reportReadMail, "}}new AH mails")
reportTotalMail, reportReadMail = 0, 0
private.HideMailGUI( )
private.mailBoxColorStart() --delay recolor system till we have had a chance to read the mail
end
if (#private.reconcilePending > 0) then
private.mailSort()
end
end
function private.mailSort()
for i in pairs(private.reconcilePending) do
--Get Age of message for timestamp
local messageAgeInSeconds = floor((30 - private.reconcilePending[i]["age"]) * 24 * 60 * 60)
private.reconcilePending[i]["time"] = (time() - messageAgeInSeconds)
if private.reconcilePending[i]["sender"]:match(_BC('MailAllianceAuctionHouse')) or private.reconcilePending[i]["sender"]:match(_BC('MailHordeAuctionHouse')) or private.reconcilePending[i]["sender"]:match(_BC('MailNeutralAuctionHouse')) then
if private.reconcilePending[i].subject:match(successLocale) and (private.reconcilePending[i].retrieved == "yes" or private.reconcilePending[i].retrieved == "failed") then
private.sortCompletedAuctions( i )
elseif private.reconcilePending[i].subject:match(expiredLocale)then
private.sortFailedAuctions( i )
elseif private.reconcilePending[i].subject:match(wonLocale) and (private.reconcilePending[i].retrieved == "yes" or private.reconcilePending[i].retrieved == "failed") then
private.sortCompletedBidsBuyouts( i )
elseif private.reconcilePending[i].subject:match(outbidLocale) then
private.sortFailedBids( i )
elseif private.reconcilePending[i].subject:match(cancelledLocale) then
private.sortCancelledAuctions( i )
elseif private.reconcilePending[i].subject:match(salePendingLocale) then
--ignore We dont care about this message
tremove(private.reconcilePending,i)
else
debugPrint("We had an Auction mail that failed mailsort(). Subject:", private.reconcilePending[i].subject, "at index", i)
tremove(private.reconcilePending,i)
end
else --if its not AH do we care? We need to record cash arrival from other toons
debugPrint("OTHER", private.reconcilePending[i].subject)
tremove(private.reconcilePending, i)
end
end
end
--retrieves the itemID from the DB
function private.matchDB(text)
local itemID
for itemKey, data in pairs(BeanCounterDBNames) do
local _, name = strsplit(";", data)
if text == name then
itemID = string.split(":", itemKey)
local itemLink = lib.API.createItemLinkFromArray(itemKey)
--debugPrint("|CFFFFFF00Searching",data,"for",text,"Sucess: link is",itemLink)
return itemID, itemLink
end
end
debugPrint("Searching DB for ItemID..", text, "Failed Item does not exist in the name array")
return nil
end
function private.sortCompletedAuctions( i )
--Get itemID from database
local itemName = private.reconcilePending[i].subject:match(successLocale.."(.*)")
local itemID, itemLink = private.matchDB(itemName)
if itemID then --Get the Bid and stack size if possible
local stack, bid = private.findStackcompletedAuctions("postedAuctions", itemID, itemLink, private.reconcilePending[i].deposit, private.reconcilePending[i]["buyout"], private.reconcilePending[i]["time"])
if stack then
local value = private.packString(stack, private.reconcilePending[i]["money"], private.reconcilePending[i]["deposit"], private.reconcilePending[i]["fee"], private.reconcilePending[i]["buyout"], bid, private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["time"], "", private.reconcilePending[i]["auctionHouse"])
if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then
private.databaseAdd("completedAuctions", itemLink, nil, value)
--debugPrint("databaseAdd completedAuctions", itemID, itemLink)
else
private.databaseAdd("completedAuctionsNeutral", itemLink, nil, value)
end
else
debugPrint("Failure for completedAuctions", itemID, itemLink, "index", private.reconcilePending[i].n)
end
end
tremove(private.reconcilePending, i)
end
--Find the stack information in postedAuctions to add into the completedAuctions DB on mail arrivial
function private.findStackcompletedAuctions(key, itemID, itemLink, soldDeposit, soldBuy, soldTime)
if not private.playerData[key][itemID] then return end --if no keys present abort
local soldDeposit, soldBuy, soldTime ,oldestPossible = tonumber(soldDeposit), tonumber(soldBuy), tonumber(soldTime), tonumber(soldTime - 208800) --58H 15min oldest we will go back
--ItemLink will be used minus its unique ID
local itemString = lib.API.getItemString(itemLink)
itemString = itemString:match("(item:.+):.-:.-")-- ignore Unique ID
for i,v in pairs (private.playerData[key][itemID]) do
if i:match(itemString) or i == itemString then
for index, text in pairs(v) do
if not text:match(".*USED.*") then
local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index])
postDeposit, postBuy, postBid, postTime = tonumber(postDeposit), tonumber(postBuy), tonumber(postBid), tonumber(postTime)
--if the deposits and buyouts match, check if time range would make this a possible match
if postDeposit == soldDeposit and postBuy >= soldBuy and postBid <= soldBuy then --We may have sold it on a bid so we need to loosen this search
if (soldTime > postTime) and (oldestPossible < postTime) then
tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB
--private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Sold"
--debugPrint("postedAuction removed as sold", itemID, itemLink)
return tonumber(postStack), tonumber(postBid)
end
end
end
end
end
end
--return 1 if the item is nonstackable and no match was found
if private.getItemInfo(itemID, "stack") == 1 then return 1 end
end
function private.sortFailedAuctions( i )
local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"])
if itemID then
local stack, bid, buyout, deposit = private.findStackfailedAuctions("postedAuctions", itemID, private.reconcilePending[i]["itemLink"], private.reconcilePending[i]["stack"], private.reconcilePending[i]["time"])
if stack then
local value = private.packString(stack, "", deposit , "", buyout, bid, "", private.reconcilePending[i]["time"], "", private.reconcilePending[i]["auctionHouse"])
if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then
private.databaseAdd("failedAuctions", private.reconcilePending[i]["itemLink"], nil, value)
--debugPrint("databaseAdd failedAuctions", itemID, private.reconcilePending[i]["itemLink"])
else
private.databaseAdd("failedAuctionsNeutral", private.reconcilePending[i]["itemLink"], nil, value)
end
else
debugPrint("Failure for failedAuctions", itemID, private.reconcilePending[i]["itemLink"], "index", private.reconcilePending[i].n)
end
end
tremove(private.reconcilePending, i, private.reconcilePending[i]["itemLink"])
end
--find stack, bid and buy info for failedauctions
function private.findStackfailedAuctions(key, itemID, itemLink, returnedStack, expiredTime)
if not private.playerData[key][itemID] then return end --if no keys present abort
local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this
for i,v in pairs (private.playerData[key][itemID]) do
if i:match(itemString) or i == itemString then --we still stack check and data range check but match should be assured by now
for index, text in pairs(v) do
if not text:match(".*USED.*") then
local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index])
if returnedStack == tonumber(postStack) then --stacks same see if we can match time
local timeAuctionPosted, timeFailedAuctionStarted = tonumber(postTime), tonumber(expiredTime - (postRunTime * 60)) --Time this message should have been posted
if (timeAuctionPosted - 21600) <= timeFailedAuctionStarted and timeFailedAuctionStarted <= (timeAuctionPosted + 21600) then
tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB
--private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Failed"
--debugPrint("postedAuction removed as Failed", itemID, itemLink )
return postStack, postBid, postBuy, postDeposit
end
end
end
end
end
end
end
--Cancled auctions are stored and treated as failed auctions with just cancelled added as the reason tag
function private.sortCancelledAuctions( i )
local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"])
if itemID then
local stack, bid, buyout, deposit = private.findStackCancelledAuctions("postedAuctions", itemID, private.reconcilePending[i]["itemLink"], private.reconcilePending[i]["stack"], private.reconcilePending[i]["time"])
if stack then
local value = private.packString(stack, "", deposit , "", buyout, bid, "", private.reconcilePending[i]["time"], _BC('Cancelled'), private.reconcilePending[i]["auctionHouse"])
if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then
private.databaseAdd("failedAuctions", private.reconcilePending[i]["itemLink"], nil, value)
--debugPrint("databaseAdd failedAuctions", itemID, private.reconcilePending[i]["itemLink"])
else
private.databaseAdd("failedAuctionsNeutral", private.reconcilePending[i]["itemLink"], nil, value)
end
else
debugPrint("Failure for cancelledAuctions", itemID, private.reconcilePending[i]["itemLink"], "index", private.reconcilePending[i].n)
end
end
tremove(private.reconcilePending, i, private.reconcilePending[i]["itemLink"])
end
--find stack, bid and buy info for Cancelledauctions
function private.findStackCancelledAuctions(key, itemID, itemLink, returnedStack, expiredTime)
if not private.playerData[key][itemID] then return end --if no keys present abort
local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this
for i,v in pairs (private.playerData[key][itemID]) do
if i:match(itemString) or i == itemString then --we still stack check and data range check but match should be assured by now
for index, text in pairs(v) do
if not text:match(".*USED.*") then
local postStack, postBid, postBuy, postRunTime, postDeposit, postTime, postReason = strsplit(";", private.playerData[key][itemID][i][index])
if returnedStack == tonumber(postStack) then --stacks same see if we can match time
local timeAuctionPosted, timeCancelledAuctionStarted = tonumber(postTime), tonumber(expiredTime - (postRunTime * 60)) --Earrliest time we could have posted the auction
if (timeAuctionPosted - 21600) > (timeCancelledAuctionStarted) then --cancelled auctions could have just been posted so no way to age check beyond oldest possible
tremove(private.playerData[key][itemID][i], index) --remove the matched item From postedAuctions DB
--private.playerData[key][itemID][i][index] = private.playerData[key][itemID][i][index]..";USED Cancelled"
--debugPrint("postedAuction removed as Cancelled", itemID, itemLink )
return postStack, postBid, postBuy, postDeposit
end
end
end
end
end
end
end
--No need to reconcile, all needed data has been provided in the invoice We do need to clear entries so outbid has less to wade through
function private.sortCompletedBidsBuyouts( i )
local itemID = lib.API.decodeLink(private.reconcilePending[i]["itemLink"])
local reason = private.findCompletedBids(itemID, private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["bid"], private.reconcilePending[i]["itemLink"])
if itemID then
--For a Won Auction money, deposit, fee are always 0 so we can use them as placeholders for BeanCounter Data
local value = private.packString(private.reconcilePending[i]["stack"], private.reconcilePending[i]["money"], deposite, private.reconcilePending[i]["fee"], private.reconcilePending[i]["buyout"], private.reconcilePending[i]["bid"], private.reconcilePending[i]["Seller/buyer"], private.reconcilePending[i]["time"], reason, private.reconcilePending[i]["auctionHouse"])
if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then
private.databaseAdd("completedBidsBuyouts", private.reconcilePending[i]["itemLink"], nil, value)
else
private.databaseAdd("completedBidsBuyoutsNeutral", private.reconcilePending[i]["itemLink"], nil, value)
end
--debugPrint("databaseAdd completedBidsBuyouts", itemID, private.reconcilePending[i]["itemLink"])
else
debugPrint("Failure for completedBidsBuyouts", itemID, private.reconcilePending[i]["itemLink"], value, "index", private.reconcilePending[i].n)
end
tremove(private.reconcilePending,i)
end
--Used only to clear postedBid entries so failed bids is less likely to miss
function private.findCompletedBids(itemID, seller, bid, itemLink)
local bid = tonumber(bid)
local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this
--debugPrint("Starting search to remove posted Bid")
if private.playerData["postedBids"][itemID] and private.playerData["postedBids"][itemID][itemString] then
for index, text in pairs(private.playerData["postedBids"][itemID][itemString]) do
if not text:match(".*USED.*") then
local postStack, postBid, postSeller, isBuyout, postTimeLeft, postTime, reason = private.unpackString(text)
postStack, postBid = tonumber(postStack), tonumber(postBid)
--if seller == postSeller and postBid == bid then --Seller is mostly useless thanks to blizzards item name cahce chamges. Can often be nil esp after a getall
if postBid == bid then
tremove(private.playerData["postedBids"][itemID][itemString], index) --remove the matched item From postedBids DB
--private.playerData["postedBids"][itemID][itemString][index] = private.playerData["postedBids"][itemID][itemString][index] ..";USED WON"
--debugPrint("posted Bid removed as Won", itemString, index, reason)
return reason --return the reason code provided for why we bid/bought item
end
end
end
end
end
function private.sortFailedBids( i )
local itemName = private.reconcilePending[i].subject:match(outbidLocale.."(.*)")
local itemID, itemLink = private.matchDB(itemName)
local postStack, postSeller, reason = private.findFailedBids(itemID, itemLink, private.reconcilePending[i]["money"])
if itemID then
local value = private.packString(postStack, "", "", "", "", private.reconcilePending[i]["money"], postSeller, private.reconcilePending[i]["time"], reason, private.reconcilePending[i]["auctionHouse"])
if private.reconcilePending[i]["auctionHouse"] == "A" or private.reconcilePending[i]["auctionHouse"] == "H" then
private.databaseAdd("failedBids", itemLink, nil, value)
else
private.databaseAdd("failedBidsNeutral", itemLink, nil, value)
end
--debugPrint("databaseAdd failedBids", itemID, itemLink, value)
else
debugPrint("Failure for failedBids", itemID, itemLink, "index", private.reconcilePending[i].n)
end
tremove(private.reconcilePending,i)
end
function private.findFailedBids(itemID, itemLink, gold)
gold = tonumber(gold)
if not itemLink then debugPrint("Failed auction ItemStrig nil", itemID, itemLink) return end
local itemString = lib.API.getItemString(itemLink) --use the UniqueID stored to match this
if private.playerData["postedBids"][itemID] and private.playerData["postedBids"][itemID][itemString] then
for index, text in pairs(private.playerData["postedBids"][itemID][itemString]) do
if not text:match(".*USED.*") then
local postStack, postBid, postSeller, isBuyout, postTimeLeft, postTime, reason = private.unpackString(text)
if tonumber(postBid) == gold then
tremove(private.playerData["postedBids"][itemID][itemString], index) --remove the matched item From postedBids DB
--private.playerData["postedBids"][itemID][itemString][index] = private.playerData["postedBids"][itemID][itemString][index] ..";USED FAILED"
--debugPrint("posted Bid removed as Failed", itemString, index)
return postStack, postSeller, reason
end
end
end
end
end
--Hook, take money event, if this still has an unretrieved invoice we delay X sec or invoice retrieved
local inboxHookMessage = false --Stops spam of the message.
function private.PreTakeInboxMoneyHook(funcArgs, retVal, index, ignore)
if #private.inboxStart > 0 or HideMailGUI then
if not inboxHookMessage then
print("Please allow BeanCounter time to reconcile the mail box")
inboxHookMessage = true
end
return "abort"
end
end
--Hook, take item event, if this still has an unretrieved invoice we delay X sec or invoice retrieved
function private.PreTakeInboxItemHook( ignore, retVal, index)
if #private.inboxStart > 0 or HideMailGUI then
if not inboxHookMessage then
print("Please allow BeanCounter time to reconcile the mail box")
inboxHookMessage = true
end
return "abort"
end
end
--[[
The below code manages the mailboxes Icon color /read/unread status
]]--
function private.mailFrameClick(self, index)
if private.playerSettings["mailbox"][index] then
private.playerSettings["mailbox"][index]["read"] = 2
end
end
local NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR = NORMAL_FONT_COLOR, HIGHLIGHT_FONT_COLOR
function private.mailFrameUpdate()
--Change Icon back color if only addon read
local db = private.playerSettings
if not db["mailbox"] then return end --we havn't read mail yet
if get("util.beancounter.mailrecolor") == "off" then return end
local numItems = GetInboxNumItems()
local index
if (InboxFrame.pageNum * 7) < numItems then
index = 7
else
index = 7 - ((InboxFrame.pageNum * 7) - numItems)
end
for i = 1, index do
local basename=format("MailItem%d",i)
local button = _G[basename.."Button"]
local buttonIcon = _G[basename.."ButtonIcon"]
local senderText = _G[basename.."Sender"]
local subjectText = _G[basename.."Subject"]
button:Show()
local itemindex = ((InboxFrame.pageNum * 7) - 7 + i) --this gives us the actual itemindex as oposed to teh 1-7 button index
local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(itemindex)
if db["mailbox"][itemindex] then
local sender = db["mailbox"][itemindex]["sender"]
if sender and (sender:match(_BC('MailHordeAuctionHouse')) or sender:match(_BC('MailAllianceAuctionHouse')) or sender:match(_BC('MailNeutralAuctionHouse'))) then
if (db["mailbox"][itemindex]["read"] < 2) then
if get("util.beancounter.mailrecolor") == "icon" or get("util.beancounter.mailrecolor") == "both" then
_G[basename.."ButtonSlot"]:SetVertexColor(1.0, 0.82, 0)
SetDesaturation(buttonIcon, nil)
end
if get("util.beancounter.mailrecolor") == "text" or get("util.beancounter.mailrecolor") == "both" then
senderText:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
subjectText:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
end
end
end
end
end
end
--CHANGE THIS TO REVERSE ORDER
local mailCurrent
local group = {["n"] = "", ["start"] = 1, ["end"] = 1} --stores the start and end locations for a group of same name items
function private.mailBoxColorStart()
mailCurrent = {} --clean table every update
local db=BeanCounterDBSettings[private.realmName][private.playerName]
for n = 1,GetInboxNumItems() do
local _, _, sender, subject, money, _, daysLeft, _, wasRead, _, _, _ = GetInboxHeaderInfo(n);
mailCurrent[n] = {["time"] = daysLeft ,["sender"] = sender, ["subject"] = subject, ["read"] = wasRead or 0 }
end
--Fix reported errors of mail DB not existing for some reason.
if not db["mailbox"] then db["mailbox"] = {} end
--Create Characters Mailbox, or resync if we get more that 5 mails out of tune
if #db["mailbox"] > (#mailCurrent+2) or #db["mailbox"] == 0 then
--debugPrint("Mail tables too far out of sync, resyncing #mailCurrent", #mailCurrent,"#mailData" ,#BeanCounterDB[private.realmName][private.playerName]["mailbox"])
db["mailbox"] = {}
for i, v in pairs(mailCurrent) do
db["mailbox"][i] = v
end
end
if #db["mailbox"] >= #mailCurrent then --mail removed or same
for i in ipairs(mailCurrent) do
if db["mailbox"][i]["subject"] == group["n"] then
if group["start"] then group["end"] = i else group["start"] = i end
else
group["n"], group["start"], group["end"] = db["mailbox"][i]["subject"], i, i
end
if mailCurrent[i]["subject"] ~= db["mailbox"][i]["subject"] then
--debugPrint("group = ",group["n"], group["start"], group["end"])
if db["mailbox"][i]["read"] == 2 then
--debugPrint("This is marked read so removing ", i)
tremove(db["mailbox"], i)
break
elseif db["mailbox"][i]["read"] < 2 then
--This message has not been read, so we have a sequence of messages with the same name. Need to go back recursivly till we find the "Real read" message that need removal
for V = group["end"], group["start"], -1 do
if db["mailbox"][V]["read"] == 2 then
--debugPrint("recursive read group",group["end"] ,"--",group["start"], "found read at",V )
tremove(db["mailbox"], V)
break
end
end
end
break
end
end
elseif #db["mailbox"] < #mailCurrent then --mail added
for i,v in ipairs(mailCurrent) do
if db["mailbox"][i] then
if mailCurrent[i]["subject"] ~= db["mailbox"][i]["subject"] then
--debugPrint("#private.mailData < #mailCurrent adding", i, mailCurrent[i]["subject"])
tinsert(db["mailbox"], i, v)
end
else
--debugPrint("need to add key ", i)
tinsert(db["mailbox"], i, v)
end
end
end
private.mailFrameUpdate()
private.hasUnreadMail()
end
function private.hasUnreadMail()
--[[if HasNewMail() then MiniMapMailFrame:Show() debugPrint("We have real unread mail, mail icon show/hide code bypassed") return end --no need to process if we have real unread messages waiting
if not get("util.beancounter.mailrecolor") then MiniMapMailFrame:Hide() return end --no need to do this if user isn't using recolor system, and mail icon should not show since HasnewMail() failed
local mailunread = false
for i,v in pairs(BeanCounterDB[private.realmName][private.playerName]["mailbox"]) do
if BeanCounterDB[private.realmName][private.playerName]["mailbox"][i]["read"] < 2 then
mailunread = true
end
end
if mailunread then
lib.SetSetting("util.beancounter.hasUnreadMail", true)
MiniMapMailFrame:Show()
else
lib.SetSetting("util.beancounter.hasUnreadMail", false)
MiniMapMailFrame:Hide()
end]]
end