Asa@3: local addonName, addonTable = ...; Asa@16: _G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceEvent-3.0", "AceBucket-3.0") Asa@3: local addon = _G[addonName] Asa@9: addonTable.ItemAuditor = addon Asa@0: Asa@0: local WHITE = "|cFFFFFFFF" Asa@0: local RED = "|cFFFF0000" Asa@0: local GREEN = "|cFF00FF00" Asa@0: local YELLOW = "|cFFFFFF00" Asa@0: local ORANGE = "|cFFFF7F00" Asa@0: local TEAL = "|cFF00FF9A" Asa@0: local GOLD = "|cFFFFD700" Asa@0: Asa@0: function addon:OnInitialize() Asa@0: local DB_defaults = { Asa@0: char = { Asa@13: ah = 1, Asa@13: use_quick_auctions = false, Asa@20: crafting_threshold = 1, Asa@20: auction_threshold = 0.15, Asa@55: output_chat_frame = nil, Asa@0: }, Asa@16: profile = { Asa@16: messages = { Asa@16: cost_updates = true, Asa@20: queue_skip = false, Asa@23: }, Asa@38: addon_enabled = true, Asa@23: -- This is for development, so I have no plans to turn it into an option. Asa@23: show_debug_frame_on_startup = false, Asa@16: }, Asa@0: factionrealm = { Asa@8: item_account = {}, Asa@8: items = {}, Asa@39: outbound_cod = {}, Asa@0: }, Asa@0: } Asa@0: self.db = LibStub("AceDB-3.0"):New("ItemAuditorDB", DB_defaults, true) Asa@8: addonTable.db= self.db Asa@8: self.items = self.db.factionrealm.items Asa@0: Asa@0: self:RegisterOptions() Asa@38: ItemAuditor:RegisterFrame(ItemAuditor_DebugFrame) Asa@23: Asa@23: -- /run ItemAuditor.db.profile.show_debug_frame_on_startup = true Asa@23: if self.db.profile.show_debug_frame_on_startup then Asa@23: ItemAuditor_DebugFrame:Show() Asa@28: self:CreateFrames() Asa@23: end Asa@0: end Asa@0: Asa@38: local registeredEvents = {} Asa@38: local originalRegisterEvent = addon.RegisterEvent Asa@38: function addon:RegisterEvent(event, callback, arg) Asa@38: registeredEvents[event] = true Asa@38: if arg ~= nil then Asa@38: return originalRegisterEvent(self, event, callback, arg) Asa@38: elseif callback ~= nil then Asa@38: return originalRegisterEvent(self, event, callback) Asa@38: else Asa@38: return originalRegisterEvent(self, event) Asa@38: end Asa@38: end Asa@38: Asa@38: local originalUnregisterEvent = addon.UnregisterEvent Asa@38: function addon:UnregisterEvent(event) Asa@38: registeredEvents[event] = nil Asa@38: return originalUnregisterEvent(self, event) Asa@38: end Asa@38: Asa@38: function addon:UnregisterAllEvents() Asa@38: for event in pairs(registeredEvents) do Asa@38: self:UnregisterEvent(event) Asa@38: end Asa@38: end Asa@38: Asa@38: local registeredFrames = {} Asa@38: function addon:RegisterFrame(frame) Asa@38: tinsert(registeredFrames, frame) Asa@38: end Asa@38: Asa@38: function addon:HideAllFrames() Asa@38: for key, frame in pairs(registeredFrames) do Asa@38: if frame then Asa@38: frame:Hide() Asa@38: end Asa@38: end Asa@38: end Asa@38: Asa@8: function addon:ConvertItems() Asa@8: for itemName, value in pairs(self.db.factionrealm.item_account) do Asa@15: local itemID = self:GetItemID(itemName) Asa@8: if itemID ~= nil then Asa@8: self:GetItem('item:' .. itemID) Asa@8: end Asa@8: if value == 0 then Asa@8: self.db.factionrealm.item_account[itemName] = nil Asa@8: end Asa@8: end Asa@8: Asa@8: for link, data in pairs(self.db.factionrealm.items) do Asa@8: if self:GetItem(link).count == 0 or self:GetItem(link).invested == 0 then Asa@8: self:RemoveItem(link) Asa@8: end Asa@10: end Asa@10: Asa@12: self:RefreshQAGroups() Asa@12: end Asa@12: Asa@24: local printPrefix = "|cFFA3CEFFItemAuditor|r: " Asa@24: function addon:Print(message, ...) Asa@24: message = format(message, ...) Asa@55: self:GetSelectedChatWindow():AddMessage( printPrefix .. tostring(message)) Asa@16: end Asa@16: Asa@0: function addon:GetCurrentInventory() Asa@8: local i = {} Asa@8: local bagID Asa@8: local slotID Asa@8: Asa@8: for bagID = 0, NUM_BAG_SLOTS do Asa@8: bagSize=GetContainerNumSlots(bagID) Asa@8: for slotID = 0, bagSize do Asa@8: local link= GetContainerItemLink(bagID, slotID); Asa@10: link = link and self:GetSafeLink(link) Asa@8: Asa@8: if link ~= nil and i[link] == nil then Asa@8: i[link] = GetItemCount(link); Asa@8: end Asa@8: end Asa@8: Asa@8: end Asa@8: return {items = i, money = GetMoney()} Asa@0: end Asa@0: Asa@0: function addon:GetInventoryDiff(pastInventory, current) Asa@8: if current == nil then Asa@8: current = self:GetCurrentInventory() Asa@8: end Asa@8: local diff = {} Asa@8: Asa@8: for link, count in pairs(current.items) do Asa@8: if pastInventory.items[link] == nil then Asa@8: diff[link] = count Asa@23: self:Debug("1 diff[" .. link .. "]=" .. diff[link]) Asa@8: elseif count - pastInventory.items[link] ~= 0 then Asa@8: diff[link] = count - pastInventory.items[link] Asa@23: self:Debug("2 diff[" .. link .. "]=" .. diff[link]) Asa@8: end Asa@8: end Asa@8: Asa@8: for link, count in pairs(pastInventory.items) do Asa@8: if current.items[link] == nil then Asa@8: diff[link] = -count Asa@23: self:Debug("3 diff[" .. link .. "]=" .. diff[link]) Asa@8: elseif current.items[link] - count ~= 0 then Asa@8: diff[link] = current.items[link] - pastInventory.items[link] Asa@23: self:Debug("4 diff[" .. link .. "]=" .. diff[link]) Asa@8: end Asa@8: end Asa@8: Asa@8: local moneyDiff = current.money - pastInventory.money Asa@23: if abs(moneyDiff) > 0 then Asa@23: self:Debug("moneyDiff: " .. moneyDiff) Asa@23: end Asa@8: Asa@8: return {items = diff, money = moneyDiff} Asa@0: end Asa@0: Asa@39: local inboundCOD = {} Asa@39: local skipMail = {} Asa@0: function addon:ScanMail() Asa@0: local results = {} Asa@39: local CODPaymentRegex = gsub(COD_PAYMENT, "%%s", "(.*)") Asa@39: Asa@0: for mailIndex = 1, GetInboxNumItems() or 0 do Asa@39: local sender, msgSubject, msgMoney, msgCOD, daysLeft, msgItem, _, _, msgText, _, isGM = select(3, GetInboxHeaderInfo(mailIndex)) Asa@15: local mailType = self:GetMailType(msgSubject) Asa@6: Asa@39: local mailSignature = msgSubject .. '-' .. msgMoney .. '-' .. msgCOD .. '-' .. daysLeft Asa@39: Asa@6: results[mailType] = (results[mailType] or {}) Asa@6: Asa@39: if skipMail[mailSignature] ~= nil then Asa@39: -- do nothing Asa@39: elseif mailType == "NonAHMail" and msgCOD > 0 then Asa@6: mailType = 'COD' Asa@6: results[mailType] = (results[mailType] or {}) Asa@5: Asa@5: local itemTypes = {} Asa@5: for itemIndex = 1, ATTACHMENTS_MAX_RECEIVE do Asa@5: local itemName, _, count, _, _= GetInboxItem(mailIndex, itemIndex) Asa@5: if itemName ~= nil then Asa@39: itemTypes[itemName] = (itemTypes[itemName] or 0) + count Asa@5: end Asa@5: end Asa@5: Asa@15: if self:tcount(itemTypes) == 1 then Asa@5: for itemName, count in pairs(itemTypes) do Asa@39: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@39: results[mailType][itemName].total = results[mailType][itemName].total + msgCOD Asa@39: Asa@39: if inboundCOD[mailSignature] == nil then Asa@39: results[mailType][itemName].count = results[mailType][itemName].count + count Asa@39: inboundCOD[mailSignature] = (inboundCOD[mailSignature] or 0) + count Asa@39: else Asa@39: results[mailType][itemName].count = inboundCOD[mailSignature] Asa@39: end Asa@39: Asa@39: Asa@5: end Asa@5: else Asa@5: self:Debug("Don't know what to do with more than one item type on COD mail.") Asa@5: end Asa@6: elseif mailType == "CODPayment" then Asa@39: -- /dump ItemAuditor.db.factionrealm.outbound_cod Asa@39: self:Debug(msgSubject) Asa@39: self:Debug(CODPaymentRegex) Asa@39: local outboundSubject = select(3, msgSubject:find(CODPaymentRegex)) Asa@39: local trackID Asa@39: if outboundSubject ~= nil then Asa@39: self:Debug(outboundSubject) Asa@45: trackID = select(3, outboundSubject:find('[[]IA: (%d*)[]]')) Asa@39: Asa@39: if trackID ~= nil then Asa@45: trackID = tonumber(trackID) Asa@45: self:Debug('COD ID: %s', trackID) Asa@39: local cod = self.db.factionrealm.outbound_cod[trackID] Asa@39: if cod == nil then Asa@39: skipMail[mailSignature] = true Asa@39: self:Print("WARNING: {%s} has an invalid ItemAuditor tracking number.", msgSubject) Asa@39: else Asa@39: itemName = trackID .. "|" .. cod['link'] Asa@39: Asa@39: Asa@39: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@39: results[mailType][itemName].total = results[mailType][itemName].total - msgMoney Asa@39: results[mailType][itemName].count = results[mailType][itemName].count - cod.count Asa@39: end Asa@39: end Asa@39: end Asa@5: Asa@39: if trackID == nil then Asa@39: skipMail[mailSignature] = true Asa@39: self:Print("WARNING: {%s} is a COD payment but doesn't have an ItemAuditor tracking number.", msgSubject) Asa@39: end Asa@5: Asa@0: elseif mailType == "AHSuccess" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@26: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@26: results[mailType][itemName].total = results[mailType][itemName].total - deposit - buyout + consignment Asa@26: Asa@0: Asa@0: elseif mailType == "AHWon" then Asa@0: local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); Asa@26: results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) Asa@26: results[mailType][itemName].total = results[mailType][itemName].total + bid Asa@26: Asa@26: local count = select(3, GetInboxItem(1,1)) Asa@26: results[mailType][itemName].count = results[mailType][itemName].count + count Asa@5: elseif mailType == "AHExpired" or mailType == "AHCancelled" or mailType == "AHOutbid" then Asa@0: -- These should be handled when you pay the deposit at the AH Asa@0: else Asa@24: -- self:Debug("Unhandled mail type: " .. mailType) Asa@24: -- self:Debug(msgSubject) Asa@0: end Asa@0: Asa@0: end Asa@23: Asa@23: for mailType, collection in pairs(results) do Asa@26: for item, data in pairs(collection) do Asa@26: self:Debug(format("|cFF00FF00MailScan|r: %s - %s - %s x %s", mailType, item, data.total, data.count)) Asa@23: end Asa@23: end Asa@23: Asa@0: return results Asa@0: end Asa@0: Asa@9: function addon:GetItem(link, viewOnly) Asa@9: if viewOnly == nil then Asa@9: viewOnly = false Asa@9: end Asa@8: Asa@9: local itemName = nil Asa@9: if self:GetSafeLink(link) == nil then Asa@9: itemName = link Asa@9: else Asa@9: link = self:GetSafeLink(link) Asa@9: itemName = GetItemInfo(link) Asa@9: end Asa@9: Asa@12: Asa@9: if self.db.factionrealm.item_account[itemName] ~= nil then Asa@8: self.items[link] = { Asa@12: count = Altoholic:GetItemCount(self:GetIDFromLink(link)), Asa@8: invested = abs(self.db.factionrealm.item_account[itemName] or 0), Asa@8: } Asa@8: self.db.factionrealm.item_account[itemName] = nil Asa@8: end Asa@8: Asa@9: if viewOnly == false and self.items[link] == nil then Asa@24: Asa@9: self.items[link] = { Asa@10: count = Altoholic:GetItemCount(self:GetIDFromLink(link)), Asa@9: invested = abs(self.db.factionrealm.item_account[itemName] or 0), Asa@9: } Asa@9: Asa@9: end Asa@9: Asa@37: if self.items[link] ~= nil then Asa@37: self.items[link].count = Altoholic:GetItemCount(self:GetIDFromLink(link)) Asa@45: Asa@45: if self.items[link].invested == nil then Asa@45: self.items[link].invested = 0 Asa@45: end Asa@37: end Asa@37: Asa@9: if viewOnly == true and self.items[link] == nil then Asa@9: return {count = 0, invested = 0} Asa@9: elseif viewOnly == true then Asa@28: Asa@9: return {count = self.items[link].count, invested = self.items[link].invested} Asa@9: end Asa@37: Asa@28: Asa@28: Asa@8: return self.items[link] Asa@8: end Asa@8: Asa@8: function addon:RemoveItem(link) Asa@9: self.db.factionrealm.item_account[link] = nil Asa@9: link = self:GetSafeLink(link) Asa@9: if link ~= nil then Asa@35: local item = addon:GetItem(link) Asa@35: item.invested = 0 Asa@24: else Asa@24: self:Debug('Failed to convert link' .. tostring(link)) Asa@9: end Asa@8: end Asa@8: Asa@26: function addon:SaveValue(link, value, countChange) Asa@26: self:Debug("SaveValue(%s, %s, %s)", tostring(link), value, (countChange or 'default')) Asa@26: countChange = countChange or 0 Asa@9: local item = nil Asa@9: local realLink = self:GetSafeLink(link) Asa@9: local itemName = nil Asa@9: if realLink == nil then Asa@26: itemName = link Asa@23: self:Debug('SaveValue: GetSafeLink failed, falling back to storing by name: ' .. tostring(itemName)) Asa@9: self.db.factionrealm.item_account[itemName] = (self.db.factionrealm.item_account[itemName] or 0) + value Asa@9: item = {invested = self.db.factionrealm.item_account[itemName], count = 1} Asa@9: else Asa@23: Asa@9: item = self:GetItem(realLink) Asa@9: item.invested = item.invested + value Asa@9: itemName = GetItemInfo(realLink) Asa@9: end Asa@8: Asa@26: if value > 0 and countChange > 0 and item.invested == value and item.count ~= countChange then Asa@26: local costPerItem = value / countChange Asa@26: value = costPerItem * item.count Asa@26: item.invested = value Asa@26: self:Print("You already owned %s %s with an unknown price, so they have also been updated to %s each", (item.count - countChange), itemName, self:FormatMoney(costPerItem)) Asa@26: end Asa@26: Asa@7: if abs(value) > 0 then Asa@22: if item.invested < 0 then Asa@16: if self.db.profile.messages.cost_updates then Asa@16: self:Print(format("Updated price of %s from %s to %s. %sYou just made a profit of %s.", itemName, self:FormatMoney(item.invested - value), self:FormatMoney(0), GREEN, self:FormatMoney(abs(item.invested)))) Asa@16: end Asa@12: self:RemoveItem(link) Asa@12: -- This doesn't work when you mail the only copy of an item you have to another character. Asa@12: --[[ Asa@12: elseif item.count == 0 and realLink and Altoholic:GetItemCount(self:GetIDFromLink(realLink)) then Asa@15: self:Print("You ran out of " .. itemName .. " and never recovered " .. self:FormatMoney(item.invested)) Asa@12: self:RemoveItem(link) Asa@12: ]] Asa@16: else Asa@16: if self.db.profile.messages.cost_updates then Asa@16: self:Print(format("Updated price of %s from %s to %s. (total change:%s)", itemName, self:FormatMoney(item.invested - value), self:FormatMoney(item.invested), self:FormatMoney(value))) Asa@16: end Asa@12: end Asa@0: end Asa@10: Asa@10: if realLink ~= nil then Asa@10: addon:UpdateQAThreshold(realLink) Asa@10: end Asa@35: UpdateInvestedData() Asa@10: end Asa@12: Asa@0: Asa@23: function addon:WatchBags() Asa@4: if self.watch_handle == nil then Asa@4: addon:UpdateCurrentInventory() Asa@23: self.watch_handle = self:RegisterBucketEvent({"BAG_UPDATE", "PLAYER_MONEY"}, 0.3, "UpdateAudit") Asa@4: end Asa@0: end Asa@0: Asa@0: function addon:UnwatchBags() Asa@4: if self.watch_handle ~= nil then Asa@4: self:UnregisterBucket(self.watch_handle) Asa@4: self.watch_handle = nil Asa@4: end Asa@0: end Asa@0: Asa@9: Asa@9: function addon:GetSafeLink(link) Asa@9: local newLink = nil Asa@9: Asa@24: if link and link == string.match(link, '.-:[-0-9]+[:0-9]*') then Asa@24: newLink = link Asa@24: elseif link then Asa@9: newLink = link and string.match(link, "|H(.-):([-0-9]+):([0-9]+)|h") Asa@9: end Asa@9: if newLink == nil then Asa@9: local itemID = self:GetItemID(link) Asa@9: if itemID ~= nil then Asa@9: _, newLink = GetItemInfo(itemID) Asa@9: return self:GetSafeLink(newLink) Asa@9: end Asa@9: end Asa@9: return newLink and string.gsub(newLink, ":0:0:0:0:0:0", "") Asa@9: end Asa@9: Asa@9: function addon:GetIDFromLink(link) Asa@9: local _, _, _, _, Id = string.find(link, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?") Asa@9: return tonumber(Id) Asa@9: end Asa@9: Asa@8: function addon:GetItemCost(link, countModifier) Asa@9: local item = self:GetItem(link, true) Asa@8: Asa@9: if item.invested > 0 then Asa@9: local count = item.count Asa@9: Asa@9: if countModifier ~= nil then Asa@9: count = count - countModifier Asa@0: end Asa@9: if count > 0 then Asa@45: return ceil(item.invested), ceil(item.invested/count), count Asa@9: end Asa@9: Asa@0: end Asa@35: return 0, 0, Altoholic:GetItemCount(ItemAuditor:GetIDFromLink(link)) Asa@0: end