| Asa@63 | 1 local ItemAuditor = select(2, ...) | 
| Asa@137 | 2 local Utils | 
| Asa@144 | 3 ItemAuditor = LibStub("AceAddon-3.0"):NewAddon(ItemAuditor, "ItemAuditor", "AceEvent-3.0", "AceBucket-3.0", "AceSerializer-3.0", "AceTimer-3.0") | 
| Asa@65 | 4 --@debug@ | 
| Asa@65 | 5 	_G['ItemAuditor'] = ItemAuditor | 
| Asa@65 | 6 --@end-debug@ | 
| Asa@0 | 7 | 
| Asa@87 | 8 if not DevTools_Dump then | 
| Asa@87 | 9 	function DevTools_Dump() | 
| Asa@87 | 10 	end | 
| Asa@87 | 11 end | 
| Asa@87 | 12 | 
| Asa@144 | 13 ItemAuditor.TRACKING_DATA_DIVIDER = "\n==ItemAuditor Tracking Data==\n" | 
| Asa@144 | 14 | 
| Asa@98 | 15 local allMailboxes = {} | 
| Asa@98 | 16 local myMailbox = {} | 
| Asa@67 | 17 | 
| Asa@67 | 18 ItemAuditor.Options = { | 
| Asa@67 | 19 	handler = ItemAuditor, | 
| Asa@67 | 20 	name = "ItemAuditor @project-version@", | 
| Asa@67 | 21 	type = 'group', | 
| Asa@67 | 22 	args = { | 
| Asa@67 | 23 		options = { | 
| Asa@67 | 24 			type = "execute", | 
| Asa@67 | 25 			name = "options", | 
| Asa@67 | 26 			desc = "Show Blizzard's options GUI", | 
| Asa@67 | 27 			func = "ShowOptionsGUI", | 
| Asa@67 | 28 			guiHidden = true, | 
| Asa@67 | 29 		}, | 
| Asa@67 | 30 		debug = { | 
| Asa@67 | 31 			type = "execute", | 
| Asa@67 | 32 			name = "debug", | 
| Asa@67 | 33 			desc = "Shows the debug frame", | 
| Asa@67 | 34 			func = function() ItemAuditor_DebugFrame:Show() end, | 
| Asa@67 | 35 			guiHidden = true, | 
| Asa@67 | 36 		}, | 
| Asa@67 | 37 		suspend = { | 
| Asa@67 | 38 			type = "toggle", | 
| Asa@67 | 39 			name = "suspend", | 
| Asa@67 | 40 			desc = "Suspends ItemAuditor", | 
| Asa@67 | 41 			get = "IsEnabled", | 
| Asa@67 | 42 			set = "SetEnabled", | 
| Asa@67 | 43 			guiHidden = true, | 
| Asa@67 | 44 		}, | 
| Asa@67 | 45 	}, | 
| Asa@67 | 46 } | 
| Asa@67 | 47 | 
| Asa@100 | 48 ItemAuditor.DB_defaults = { | 
| Asa@100 | 49 	char = { | 
| Asa@100 | 50 		ah = 1, | 
| Asa@100 | 51 		use_quick_auctions = false, | 
| Asa@101 | 52 		profitable_threshold = 10000, | 
| Asa@100 | 53 		auction_threshold = 0.15, | 
| Asa@111 | 54 		auction_threshold_value = 0, | 
| Asa@100 | 55 		qa_extra = 0, | 
| Asa@100 | 56 		output_chat_frame = nil, | 
| Asa@109 | 57 		cod_warnings = true, | 
| Asa@144 | 58 		incoming_tracking_ids = {} | 
| Asa@100 | 59 	}, | 
| Asa@100 | 60 	profile = { | 
| Asa@100 | 61 		messages = { | 
| Asa@100 | 62 			cost_updates = true, | 
| Asa@100 | 63 			queue_skip = false, | 
| Asa@100 | 64 		}, | 
| Asa@100 | 65 		ItemAuditor_enabled = true, | 
| Asa@100 | 66 		queue_destination = nil, | 
| Asa@100 | 67 		disabled_deciders = {}, | 
| Asa@100 | 68 		pricing_method = 'low', | 
| Asa@100 | 69 	}, | 
| Asa@100 | 70 	factionrealm = { | 
| Asa@100 | 71 		item_account = {}, | 
| Asa@100 | 72 		items = {}, | 
| Asa@100 | 73 		outbound_cod = {}, | 
| Asa@100 | 74 		mailbox = {}, | 
| Asa@132 | 75 		enabled_guilds = {}, | 
| Asa@100 | 76 	}, | 
| Asa@100 | 77 } | 
| Asa@100 | 78 | 
| Asa@63 | 79 function ItemAuditor:OnInitialize() | 
| Asa@137 | 80 	Utils = self:GetModule("Utils") | 
| Asa@100 | 81 	self.db = LibStub("AceDB-3.0"):New("ItemAuditorDB", ItemAuditor.DB_defaults, true) | 
| Asa@98 | 82 | 
| Asa@98 | 83 	allMailboxes = self.db.factionrealm.mailbox | 
| Asa@98 | 84 	if not allMailboxes[UnitName("player")] then | 
| Asa@98 | 85 		allMailboxes[UnitName("player")] = {} | 
| Asa@98 | 86 	end | 
| Asa@98 | 87 	myMailbox = allMailboxes[UnitName("player")] | 
| Asa@98 | 88 | 
| Asa@67 | 89 	self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ItemAuditor", "ItemAuditor") | 
| Asa@133 | 90 | 
| Asa@67 | 91 	LibStub("AceConfig-3.0"):RegisterOptionsTable("ItemAuditor", ItemAuditor.Options, {"ia"}) | 
| Asa@38 | 92 	ItemAuditor:RegisterFrame(ItemAuditor_DebugFrame) | 
| Asa@133 | 93 | 
| Asa@86 | 94 	LibStub("AceConsole-3.0"):RegisterChatCommand('rl', ReloadUI) | 
| Asa@100 | 95 | 
| Asa@101 | 96 	if self.db.char.crafting_threshold then | 
| Asa@101 | 97 		local threshold = self.db.char.crafting_threshold | 
| Asa@101 | 98 		if threshold == 1 then | 
| Asa@101 | 99 			self.db.char.profitable_threshold = 5000 | 
| Asa@101 | 100 		elseif threshold == 2 then | 
| Asa@101 | 101 			self.db.char.profitable_threshold = 10000 | 
| Asa@101 | 102 		elseif threshold == 3 then | 
| Asa@101 | 103 			self.db.char.profitable_threshold = 50000 | 
| Asa@101 | 104 		end | 
| Asa@133 | 105 | 
| Asa@101 | 106 		self.db.char.crafting_threshold = nil | 
| Asa@101 | 107 	end | 
| Asa@101 | 108 | 
| Asa@119 | 109 	ItemAuditor:IsQAEnabled() | 
| Asa@112 | 110 | 
| Asa@65 | 111 	--@debug@ | 
| Asa@59 | 112 		-- ItemAuditor_DebugFrame:Show() | 
| Asa@59 | 113 		-- self:CreateFrame('tab_crafting') | 
| Asa@74 | 114 		self:RegisterEvent("TRADE_SKILL_SHOW", function() | 
| Asa@93 | 115 			ItemAuditor:DisplayCrafting() | 
| Asa@74 | 116 		end) | 
| Asa@65 | 117 	--@end-debug@ | 
| Asa@0 | 118 end | 
| Asa@0 | 119 | 
| Asa@67 | 120 | 
| Asa@67 | 121 | 
| Asa@38 | 122 local registeredEvents = {} | 
| Asa@133 | 123 local originalRegisterEvent = ItemAuditor.RegisterEvent | 
| Asa@63 | 124 function ItemAuditor:RegisterEvent(event, callback, arg) | 
| Asa@38 | 125 	registeredEvents[event] = true | 
| Asa@38 | 126 	if arg ~= nil then | 
| Asa@38 | 127 		return originalRegisterEvent(self, event, callback, arg) | 
| Asa@38 | 128 	elseif callback ~= nil then | 
| Asa@38 | 129 		return originalRegisterEvent(self, event, callback) | 
| Asa@38 | 130 	else | 
| Asa@38 | 131 		return originalRegisterEvent(self, event) | 
| Asa@38 | 132 	end | 
| Asa@38 | 133 end | 
| Asa@38 | 134 | 
| Asa@63 | 135 local originalUnregisterEvent = ItemAuditor.UnregisterEvent | 
| Asa@63 | 136 function ItemAuditor:UnregisterEvent(event) | 
| Asa@38 | 137 	registeredEvents[event] = nil | 
| Asa@38 | 138         return originalUnregisterEvent(self, event) | 
| Asa@38 | 139 end | 
| Asa@38 | 140 | 
| Asa@63 | 141 function ItemAuditor:UnregisterAllEvents() | 
| Asa@38 | 142 	for event in pairs(registeredEvents) do | 
| Asa@38 | 143 		self:UnregisterEvent(event) | 
| Asa@38 | 144 	end | 
| Asa@38 | 145 end | 
| Asa@38 | 146 | 
| Asa@38 | 147 local registeredFrames = {} | 
| Asa@63 | 148 function ItemAuditor:RegisterFrame(frame) | 
| Asa@38 | 149 	tinsert(registeredFrames, frame) | 
| Asa@38 | 150 end | 
| Asa@38 | 151 | 
| Asa@63 | 152 function ItemAuditor:HideAllFrames() | 
| Asa@38 | 153 	for key, frame in pairs(registeredFrames) do | 
| Asa@38 | 154 		if frame then | 
| Asa@38 | 155 			frame:Hide() | 
| Asa@38 | 156 		end | 
| Asa@38 | 157 	end | 
| Asa@38 | 158 end | 
| Asa@38 | 159 | 
| Asa@63 | 160 function ItemAuditor:ConvertItems() | 
| Asa@8 | 161 	for itemName, value in pairs(self.db.factionrealm.item_account) do | 
| Asa@15 | 162 		local itemID = self:GetItemID(itemName) | 
| Asa@8 | 163 		if itemID ~= nil then | 
| Asa@8 | 164 			self:GetItem('item:' .. itemID) | 
| Asa@8 | 165 		end | 
| Asa@8 | 166 		if value == 0 then | 
| Asa@8 | 167 			self.db.factionrealm.item_account[itemName] = nil | 
| Asa@8 | 168 		end | 
| Asa@8 | 169 	end | 
| Asa@133 | 170 | 
| Asa@8 | 171 	for link, data in pairs(self.db.factionrealm.items) do | 
| Asa@8 | 172 		if self:GetItem(link).count == 0 or self:GetItem(link).invested == 0 then | 
| Asa@8 | 173 			self:RemoveItem(link) | 
| Asa@8 | 174 		end | 
| Asa@10 | 175 	end | 
| Asa@133 | 176 | 
| Asa@12 | 177 	self:RefreshQAGroups() | 
| Asa@12 | 178 end | 
| Asa@12 | 179 | 
| Asa@133 | 180 -- Options doesn't exist when this file is created the first time, so getOptions will | 
| Asa@133 | 181 -- make one call to :GetModule and return the result and replace itself with a | 
| Asa@65 | 182 -- function that simply returns the same object. The permanent solution will probably be | 
| Asa@65 | 183 -- to move :Print to a different module. | 
| Asa@65 | 184 local function getOptions() | 
| Asa@65 | 185 	local Options = ItemAuditor:GetModule("Options") | 
| Asa@65 | 186 	getOptions = function() return Options end | 
| Asa@65 | 187 	return Options | 
| Asa@65 | 188 end | 
| Asa@65 | 189 | 
| Asa@24 | 190 local printPrefix = "|cFFA3CEFFItemAuditor|r: " | 
| Asa@63 | 191 function ItemAuditor:Print(message, ...) | 
| Asa@24 | 192 	message = format(message, ...) | 
| Asa@65 | 193 	getOptions().GetSelectedChatWindow():AddMessage( printPrefix .. tostring(message)) | 
| Asa@16 | 194 end | 
| Asa@16 | 195 | 
| Asa@81 | 196 local bankOpen = false | 
| Asa@81 | 197 | 
| Asa@81 | 198 function ItemAuditor:BankFrameChanged(event) | 
| Asa@81 | 199 	bankOpen = (event == 'BANKFRAME_OPENED') | 
| Asa@81 | 200 	ItemAuditor:UpdateCurrentInventory() | 
| Asa@81 | 201 end | 
| Asa@81 | 202 | 
| Asa@80 | 203 local function scanBag(bagID, i) | 
| Asa@80 | 204 	bagSize=GetContainerNumSlots(bagID) | 
| Asa@80 | 205 	for slotID = 0, bagSize do | 
| Asa@80 | 206 		local link= GetContainerItemLink(bagID, slotID); | 
| Asa@137 | 207 		itemID = link and  Utils.GetItemID(link) | 
| Asa@80 | 208 | 
| Asa@137 | 209 		if link and itemID and i[itemID] == nil then | 
| Asa@137 | 210 			i[itemID] = GetItemCount(itemID, bankOpen); | 
| Asa@80 | 211 		end | 
| Asa@80 | 212 	end | 
| Asa@80 | 213 end | 
| Asa@80 | 214 | 
| Asa@63 | 215 function ItemAuditor:GetCurrentInventory() | 
| Asa@8 | 216 	local i = {} | 
| Asa@8 | 217 	local bagID | 
| Asa@8 | 218 	local slotID | 
| Asa@133 | 219 | 
| Asa@8 | 220 	for bagID = 0, NUM_BAG_SLOTS do | 
| Asa@80 | 221 		scanBag(bagID, i) | 
| Asa@8 | 222 	end | 
| Asa@133 | 223 | 
| Asa@81 | 224 	if bankOpen then | 
| Asa@81 | 225 		scanBag(BANK_CONTAINER, i) | 
| Asa@81 | 226 		for bagID = NUM_BAG_SLOTS+1, NUM_BANKBAGSLOTS do | 
| Asa@81 | 227 			scanBag(bagID, i) | 
| Asa@81 | 228 		end | 
| Asa@80 | 229 	end | 
| Asa@133 | 230 | 
| Asa@8 | 231 	return {items = i, money = GetMoney()} | 
| Asa@0 | 232 end | 
| Asa@0 | 233 | 
| Asa@63 | 234 function ItemAuditor:GetInventoryDiff(pastInventory, current) | 
| Asa@8 | 235 	if current == nil then | 
| Asa@8 | 236 		current = self:GetCurrentInventory() | 
| Asa@8 | 237 	end | 
| Asa@8 | 238 	local diff = {} | 
| Asa@8 | 239 | 
| Asa@137 | 240 	for itemID, count in pairs(current.items) do | 
| Asa@137 | 241 		if pastInventory.items[itemID] == nil then | 
| Asa@137 | 242 			diff[itemID] = count | 
| Asa@137 | 243 			self:Debug("1 diff[" .. itemID .. "]=" .. diff[itemID]) | 
| Asa@137 | 244 		elseif count - pastInventory.items[itemID] ~= 0 then | 
| Asa@137 | 245 			diff[itemID] = count - pastInventory.items[itemID] | 
| Asa@137 | 246 			self:Debug("2 diff[" .. itemID .. "]=" .. diff[itemID]) | 
| Asa@133 | 247 		end | 
| Asa@8 | 248 	end | 
| Asa@8 | 249 | 
| Asa@137 | 250 	for itemID, count in pairs(pastInventory.items) do | 
| Asa@137 | 251 		if current.items[itemID] == nil then | 
| Asa@137 | 252 			diff[itemID] = -count | 
| Asa@139 | 253 			self:Debug("3 diff[" .. itemID .. "]=" .. diff[itemID]) | 
| Asa@137 | 254 		elseif current.items[itemID] - count ~= 0 then | 
| Asa@137 | 255 			diff[itemID] = current.items[itemID] - pastInventory.items[itemID] | 
| Asa@137 | 256 			self:Debug("4 diff[" .. itemID .. "]=" .. diff[itemID]) | 
| Asa@8 | 257 		end | 
| Asa@8 | 258 	end | 
| Asa@8 | 259 | 
| Asa@8 | 260 	local moneyDiff = current.money - pastInventory.money | 
| Asa@23 | 261 	if abs(moneyDiff) > 0 then | 
| Asa@23 | 262 		self:Debug("moneyDiff: " .. moneyDiff) | 
| Asa@23 | 263 	end | 
| Asa@8 | 264 | 
| Asa@8 | 265 	return {items = diff, money = moneyDiff} | 
| Asa@0 | 266 end | 
| Asa@0 | 267 | 
| Asa@39 | 268 local inboundCOD = {} | 
| Asa@39 | 269 local skipMail = {} | 
| Asa@63 | 270 function ItemAuditor:ScanMail() | 
| Asa@0 | 271 	local results = {} | 
| Asa@39 | 272 	local CODPaymentRegex = gsub(COD_PAYMENT, "%%s", "(.*)") | 
| Asa@144 | 273 	local ia_tracking = {} | 
| Asa@133 | 274 | 
| Asa@0 | 275 	for mailIndex = 1, GetInboxNumItems() or 0 do | 
| Asa@39 | 276 		local sender, msgSubject, msgMoney, msgCOD, daysLeft, msgItem, _, _, msgText, _, isGM = select(3, GetInboxHeaderInfo(mailIndex)) | 
| Asa@15 | 277 		local mailType = self:GetMailType(msgSubject) | 
| Asa@133 | 278 | 
| Asa@39 | 279 		local mailSignature = msgSubject .. '-' .. msgMoney .. '-' .. msgCOD .. '-' .. daysLeft | 
| Asa@133 | 280 | 
| Asa@6 | 281 		results[mailType] = (results[mailType] or {}) | 
| Asa@133 | 282 | 
| Asa@39 | 283 		if skipMail[mailSignature] ~= nil then | 
| Asa@39 | 284 			-- do nothing | 
| Asa@39 | 285 		elseif mailType == "NonAHMail" and msgCOD > 0 then | 
| Asa@6 | 286 			mailType = 'COD' | 
| Asa@6 | 287 			results[mailType] = (results[mailType] or {}) | 
| Asa@133 | 288 | 
| Asa@5 | 289 			local itemTypes = {} | 
| Asa@5 | 290 			for itemIndex = 1, ATTACHMENTS_MAX_RECEIVE do | 
| Asa@5 | 291 				local itemName, _, count, _, _= GetInboxItem(mailIndex, itemIndex) | 
| Asa@5 | 292 				if itemName ~= nil then | 
| Asa@39 | 293 					itemTypes[itemName] = (itemTypes[itemName] or 0) + count | 
| Asa@5 | 294 				end | 
| Asa@5 | 295 			end | 
| Asa@133 | 296 | 
| Asa@15 | 297 			if self:tcount(itemTypes) == 1 then | 
| Asa@5 | 298 				for itemName, count in pairs(itemTypes) do | 
| Asa@39 | 299 					results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) | 
| Asa@39 | 300 					results[mailType][itemName].total = results[mailType][itemName].total + msgCOD | 
| Asa@133 | 301 | 
| Asa@39 | 302 					if inboundCOD[mailSignature] == nil then | 
| Asa@39 | 303 						results[mailType][itemName].count = results[mailType][itemName].count + count | 
| Asa@39 | 304 						inboundCOD[mailSignature] = (inboundCOD[mailSignature] or 0) + count | 
| Asa@39 | 305 					else | 
| Asa@39 | 306 						results[mailType][itemName].count = inboundCOD[mailSignature] | 
| Asa@39 | 307 					end | 
| Asa@133 | 308 | 
| Asa@133 | 309 | 
| Asa@5 | 310 				end | 
| Asa@5 | 311 			else | 
| Asa@5 | 312 				self:Debug("Don't know what to do with more than one item type on COD mail.") | 
| Asa@5 | 313 			end | 
| Asa@133 | 314 		elseif mailType == "CODPayment" then | 
| Asa@39 | 315 			-- /dump ItemAuditor.db.factionrealm.outbound_cod | 
| Asa@39 | 316 			self:Debug(msgSubject) | 
| Asa@39 | 317 			self:Debug(CODPaymentRegex) | 
| Asa@39 | 318 			local outboundSubject = select(3, msgSubject:find(CODPaymentRegex)) | 
| Asa@39 | 319 			local trackID | 
| Asa@39 | 320 			if outboundSubject ~= nil then | 
| Asa@39 | 321 				self:Debug(outboundSubject) | 
| Asa@45 | 322 				trackID = select(3, outboundSubject:find('[[]IA: (%d*)[]]')) | 
| Asa@133 | 323 | 
| Asa@39 | 324 				if trackID ~= nil then | 
| Asa@45 | 325 					trackID = tonumber(trackID) | 
| Asa@45 | 326 					self:Debug('COD ID: %s', trackID) | 
| Asa@39 | 327 					local cod = self.db.factionrealm.outbound_cod[trackID] | 
| Asa@39 | 328 					if cod == nil then | 
| Asa@39 | 329 						skipMail[mailSignature] = true | 
| Asa@39 | 330 						self:Print("WARNING: {%s} has an invalid ItemAuditor tracking number.", msgSubject) | 
| Asa@39 | 331 					else | 
| Asa@39 | 332 						itemName = trackID .. "|" .. cod['link'] | 
| Asa@133 | 333 | 
| Asa@133 | 334 | 
| Asa@39 | 335 						results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) | 
| Asa@39 | 336 						results[mailType][itemName].total = results[mailType][itemName].total - msgMoney | 
| Asa@39 | 337 						results[mailType][itemName].count = results[mailType][itemName].count - cod.count | 
| Asa@39 | 338 					end | 
| Asa@39 | 339 				end | 
| Asa@39 | 340 			end | 
| Asa@133 | 341 | 
| Asa@39 | 342 			if trackID == nil then | 
| Asa@39 | 343 				skipMail[mailSignature] = true | 
| Asa@39 | 344 				self:Print("WARNING: {%s} is a COD payment but doesn't have an ItemAuditor tracking number.", msgSubject) | 
| Asa@39 | 345 			end | 
| Asa@133 | 346 | 
| Asa@0 | 347 		elseif mailType == "AHSuccess" then | 
| Asa@0 | 348 			local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); | 
| Asa@26 | 349 			results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) | 
| Asa@26 | 350 			results[mailType][itemName].total = results[mailType][itemName].total - deposit - buyout + consignment | 
| Asa@133 | 351 | 
| Asa@0 | 352 | 
| Asa@0 | 353 		elseif mailType == "AHWon" then | 
| Asa@0 | 354 			local invoiceType, itemName, playerName, bid, buyout, deposit, consignment = GetInboxInvoiceInfo(mailIndex); | 
| Asa@26 | 355 			results[mailType][itemName] = (results[mailType][itemName] or {total=0,count=0}) | 
| Asa@26 | 356 			results[mailType][itemName].total = results[mailType][itemName].total + bid | 
| Asa@133 | 357 | 
| Asa@98 | 358 			local count = select(3, GetInboxItem(mailIndex,1)) | 
| Asa@26 | 359 			results[mailType][itemName].count = results[mailType][itemName].count + count | 
| Asa@5 | 360 		elseif mailType == "AHExpired" or mailType == "AHCancelled" or mailType == "AHOutbid" then | 
| Asa@0 | 361 			-- These should be handled when you pay the deposit at the AH | 
| Asa@0 | 362 		else | 
| Asa@144 | 363 | 
| Asa@144 | 364 			local body = GetInboxText(mailIndex) | 
| Asa@144 | 365 			if body and body:find(ItemAuditor.TRACKING_DATA_DIVIDER) then | 
| Asa@144 | 366 				local serialized = body:gsub('.*'..ItemAuditor.TRACKING_DATA_DIVIDER, '') | 
| Asa@144 | 367 				local body = body:gsub(ItemAuditor.TRACKING_DATA_DIVIDER..'.*', '') | 
| Asa@144 | 368 				local success, trackingID, data = ItemAuditor:Deserialize(serialized) | 
| Asa@144 | 369 				if success then | 
| Asa@144 | 370 					if not self.db.char.incoming_tracking_ids[trackingID] then | 
| Asa@144 | 371 						for link, d in pairs(data) do | 
| Asa@144 | 372 							self:SaveValue(link, d.costEach * d.count, d.count) | 
| Asa@144 | 373 						end | 
| Asa@144 | 374 					end | 
| Asa@144 | 375 					if body == '' and msgItem == nil then | 
| Asa@144 | 376 						self.deleteQueue = self.deleteQueue or {} | 
| Asa@144 | 377 						local ni = #(self.deleteQueue)+1 | 
| Asa@144 | 378 						print(deleteQueue, ni, mailIndex) | 
| Asa@144 | 379 						self.deleteQueue[ni] = mailIndex | 
| Asa@144 | 380 					end | 
| Asa@144 | 381 					ia_tracking[trackingID] = true | 
| Asa@144 | 382 					self.db.char.incoming_tracking_ids[trackingID] = true | 
| Asa@144 | 383 				end | 
| Asa@144 | 384 			end | 
| Asa@144 | 385 | 
| Asa@0 | 386 		end | 
| Asa@0 | 387 | 
| Asa@0 | 388 	end | 
| Asa@98 | 389 | 
| Asa@98 | 390 	wipe(myMailbox) | 
| Asa@23 | 391 	for mailType, collection in pairs(results) do | 
| Asa@98 | 392 		myMailbox[mailType] = {} | 
| Asa@26 | 393 		for item, data in pairs(collection) do | 
| Asa@98 | 394 			myMailbox[mailType][item] = { | 
| Asa@98 | 395 				total = data.total, | 
| Asa@98 | 396 				count = data.count, | 
| Asa@98 | 397 			} | 
| Asa@98 | 398 			-- self:Print(format("|cFF00FF00MailScan|r: %s - %s - %s x %s", mailType, item, data.total, data.count)) | 
| Asa@23 | 399 		end | 
| Asa@23 | 400 	end | 
| Asa@144 | 401 	if self:tcount(ia_tracking) > 0 then | 
| Asa@144 | 402 		for id, _ in pairs(self.db.char.incoming_tracking_ids) do | 
| Asa@144 | 403 			self.db.char.incoming_tracking_ids[id] = ia_tracking[id] | 
| Asa@144 | 404 		end | 
| Asa@144 | 405 	end | 
| Asa@144 | 406 | 
| Asa@133 | 407 	return results | 
| Asa@0 | 408 end | 
| Asa@0 | 409 | 
| Asa@105 | 410 local realm = GetRealmName() | 
| Asa@105 | 411 local ds_account = 'Default' | 
| Asa@82 | 412 function ItemAuditor:GetItemCount(searchID) | 
| Asa@141 | 413 	self:Debug('ItemAuditor:GetItemCount(%s)', tostring(searchID)) | 
| Asa@105 | 414 	local count = 0 | 
| Asa@105 | 415 	for _, character in pairs(DataStore:GetCharacters(realm, ds_account)) do | 
| Asa@136 | 416 		if DataStore:GetCharacterFaction(character) == UnitFactionGroup("player") then | 
| Asa@136 | 417 			local bag, bank = DataStore:GetContainerItemCount(character, searchID) | 
| Asa@136 | 418 			count = count + (bag or 0) + (bank or 0) | 
| Asa@136 | 419 			count = count + (DataStore:GetAuctionHouseItemCount(character, searchID) or 0) | 
| Asa@136 | 420 			count = count + (DataStore:GetInventoryItemCount(character, searchID) or 0) | 
| Asa@136 | 421 			count = count + (DataStore:GetMailItemCount(character, searchID) or 0) | 
| Asa@136 | 422 			count = count + (DataStore:GetCurrencyItemCount(character, searchID) or 0) | 
| Asa@141 | 423 		else | 
| Asa@141 | 424 			self:Debug('Skipping %s', character) | 
| Asa@136 | 425 		end | 
| Asa@105 | 426 	end | 
| Asa@141 | 427 	self:Debug('before guild count: %s', count) | 
| Asa@132 | 428 	for guildName in pairs(self.db.factionrealm.enabled_guilds) do | 
| Asa@132 | 429 		count = count + DataStore:GetGuildBankItemCount(DataStore:GetGuilds()[guildName], searchID) | 
| Asa@132 | 430 	end | 
| Asa@141 | 431 	self:Debug('after guild count: %s', count) | 
| Asa@132 | 432 | 
| Asa@98 | 433 	local itemName = GetItemInfo(searchID) | 
| Asa@98 | 434 	for character, mailbox in pairs(allMailboxes) do | 
| Asa@98 | 435 		for type, items in pairs(mailbox) do | 
| Asa@98 | 436 			if type == 'AHWon' or type == 'COD' then | 
| Asa@98 | 437 				for name, data in pairs(items) do | 
| Asa@98 | 438 					if name == itemName then | 
| Asa@98 | 439 						count = count - data.count | 
| Asa@141 | 440 						self:Debug('removing mail %s %s %s', character, type, data.count) | 
| Asa@98 | 441 					end | 
| Asa@98 | 442 				end | 
| Asa@98 | 443 			end | 
| Asa@98 | 444 		end | 
| Asa@82 | 445 	end | 
| Asa@82 | 446 	return count | 
| Asa@82 | 447 end | 
| Asa@82 | 448 | 
| Asa@63 | 449 function ItemAuditor:GetItem(link, viewOnly) | 
| Asa@9 | 450 	if viewOnly == nil then | 
| Asa@9 | 451 		viewOnly = false | 
| Asa@9 | 452 	end | 
| Asa@133 | 453 | 
| Asa@9 | 454 	local itemName = nil | 
| Asa@9 | 455 	if self:GetSafeLink(link) == nil then | 
| Asa@9 | 456 		itemName = link | 
| Asa@9 | 457 	else | 
| Asa@9 | 458 		link = self:GetSafeLink(link) | 
| Asa@9 | 459 		itemName = GetItemInfo(link) | 
| Asa@9 | 460 	end | 
| Asa@133 | 461 | 
| Asa@133 | 462 | 
| Asa@9 | 463 	if self.db.factionrealm.item_account[itemName] ~= nil then | 
| Asa@65 | 464 		self.db.factionrealm.items[link] = { | 
| Asa@82 | 465 			count = ItemAuditor:GetItemCount(self:GetIDFromLink(link)), | 
| Asa@8 | 466 			invested = abs(self.db.factionrealm.item_account[itemName] or 0), | 
| Asa@8 | 467 		} | 
| Asa@8 | 468 		self.db.factionrealm.item_account[itemName] = nil | 
| Asa@8 | 469 	end | 
| Asa@133 | 470 | 
| Asa@65 | 471 	if viewOnly == false and self.db.factionrealm.items[link] == nil then | 
| Asa@133 | 472 | 
| Asa@65 | 473 		self.db.factionrealm.items[link] = { | 
| Asa@82 | 474 			count =  ItemAuditor:GetItemCount(self:GetIDFromLink(link)), | 
| Asa@9 | 475 			invested = abs(self.db.factionrealm.item_account[itemName] or 0), | 
| Asa@9 | 476 		} | 
| Asa@133 | 477 | 
| Asa@9 | 478 	end | 
| Asa@133 | 479 | 
| Asa@65 | 480 	if self.db.factionrealm.items[link] ~= nil then | 
| Asa@82 | 481 		self.db.factionrealm.items[link].count =  ItemAuditor:GetItemCount(self:GetIDFromLink(link)) | 
| Asa@133 | 482 | 
| Asa@65 | 483 		if self.db.factionrealm.items[link].invested == nil then | 
| Asa@65 | 484 			self.db.factionrealm.items[link].invested = 0 | 
| Asa@45 | 485 		end | 
| Asa@37 | 486 	end | 
| Asa@133 | 487 | 
| Asa@65 | 488 	if viewOnly == true and self.db.factionrealm.items[link] == nil then | 
| Asa@9 | 489 		return {count = 0, invested = 0} | 
| Asa@9 | 490 	elseif viewOnly == true then | 
| Asa@133 | 491 | 
| Asa@65 | 492 		return {count = self.db.factionrealm.items[link].count, invested = self.db.factionrealm.items[link].invested} | 
| Asa@9 | 493 	end | 
| Asa@133 | 494 | 
| Asa@133 | 495 | 
| Asa@133 | 496 | 
| Asa@65 | 497 	return self.db.factionrealm.items[link] | 
| Asa@8 | 498 end | 
| Asa@8 | 499 | 
| Asa@63 | 500 function ItemAuditor:RemoveItem(link) | 
| Asa@9 | 501 	self.db.factionrealm.item_account[link] = nil | 
| Asa@9 | 502 	link = self:GetSafeLink(link) | 
| Asa@9 | 503 	if link ~= nil then | 
| Asa@63 | 504 		local item = ItemAuditor:GetItem(link) | 
| Asa@35 | 505 		item.invested = 0 | 
| Asa@24 | 506 	else | 
| Asa@24 | 507 		self:Debug('Failed to convert link' .. tostring(link)) | 
| Asa@9 | 508 	end | 
| Asa@8 | 509 end | 
| Asa@8 | 510 | 
| Asa@63 | 511 function ItemAuditor:SaveValue(link, value, countChange) | 
| Asa@26 | 512 	self:Debug("SaveValue(%s, %s, %s)", tostring(link), value, (countChange or 'default')) | 
| Asa@26 | 513 	countChange = countChange or 0 | 
| Asa@9 | 514 	local item = nil | 
| Asa@9 | 515 	local realLink = self:GetSafeLink(link) | 
| Asa@9 | 516 	local itemName = nil | 
| Asa@9 | 517 	if realLink == nil then | 
| Asa@26 | 518 		itemName = link | 
| Asa@23 | 519 		self:Debug('SaveValue: GetSafeLink failed, falling back to storing by name: ' .. tostring(itemName)) | 
| Asa@9 | 520 		self.db.factionrealm.item_account[itemName] = (self.db.factionrealm.item_account[itemName] or 0) + value | 
| Asa@9 | 521 		item = {invested = self.db.factionrealm.item_account[itemName], count = 1} | 
| Asa@9 | 522 	else | 
| Asa@133 | 523 | 
| Asa@9 | 524 		item = self:GetItem(realLink) | 
| Asa@9 | 525 		item.invested = item.invested + value | 
| Asa@9 | 526 		itemName = GetItemInfo(realLink) | 
| Asa@9 | 527 	end | 
| Asa@133 | 528 | 
| Asa@26 | 529 	if value > 0 and countChange > 0 and item.invested == value and item.count ~= countChange then | 
| Asa@26 | 530 		local costPerItem = value / countChange | 
| Asa@26 | 531 		value = costPerItem * item.count | 
| Asa@26 | 532 		item.invested = value | 
| Asa@26 | 533 		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 | 534 	end | 
| Asa@133 | 535 | 
| Asa@7 | 536 	if abs(value) > 0 then | 
| Asa@22 | 537 		if  item.invested < 0 then | 
| Asa@16 | 538 			if self.db.profile.messages.cost_updates then | 
| Asa@103 | 539 				self:Print(format("Updated price of %s from %s to %s. |cFF00FF00You just made a profit of %s.", itemName, self:FormatMoney(item.invested - value), self:FormatMoney(0), self:FormatMoney(abs(item.invested)))) | 
| Asa@16 | 540 			end | 
| Asa@12 | 541 			self:RemoveItem(link) | 
| Asa@12 | 542 		-- This doesn't work when you mail the only copy of an item you have to another character. | 
| Asa@12 | 543 		--[[ | 
| Asa@133 | 544 		elseif item.count == 0 and realLink and ItemAuditor:GetItemCount(self:GetIDFromLink(realLink)) then | 
| Asa@15 | 545 			self:Print("You ran out of " .. itemName .. " and never recovered " .. self:FormatMoney(item.invested)) | 
| Asa@12 | 546 			self:RemoveItem(link) | 
| Asa@12 | 547 		]] | 
| Asa@16 | 548 		else | 
| Asa@16 | 549 			if self.db.profile.messages.cost_updates then | 
| Asa@16 | 550 				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 | 551 			end | 
| Asa@12 | 552 		end | 
| Asa@0 | 553 	end | 
| Asa@133 | 554 | 
| Asa@10 | 555 	if realLink ~= nil then | 
| Asa@63 | 556 		ItemAuditor:UpdateQAThreshold(realLink) | 
| Asa@112 | 557 		self:SendMessage("IA_COST_CHANGED", realLink, unpack({ItemAuditor:GetItemCost(realLink)})) | 
| Asa@10 | 558 	end | 
| Asa@35 | 559 	UpdateInvestedData() | 
| Asa@10 | 560 end | 
| Asa@12 | 561 | 
| Asa@0 | 562 | 
| Asa@63 | 563 function ItemAuditor:WatchBags() | 
| Asa@4 | 564 	if self.watch_handle == nil then | 
| Asa@63 | 565 		ItemAuditor:UpdateCurrentInventory() | 
| Asa@23 | 566 		self.watch_handle = self:RegisterBucketEvent({"BAG_UPDATE", "PLAYER_MONEY"}, 0.3, "UpdateAudit") | 
| Asa@4 | 567 	end | 
| Asa@0 | 568 end | 
| Asa@0 | 569 | 
| Asa@63 | 570 function ItemAuditor:UnwatchBags() | 
| Asa@4 | 571 	if self.watch_handle ~= nil then | 
| Asa@4 | 572 		self:UnregisterBucket(self.watch_handle) | 
| Asa@4 | 573 		self.watch_handle = nil | 
| Asa@4 | 574 	end | 
| Asa@0 | 575 end | 
| Asa@0 | 576 | 
| Asa@9 | 577 | 
| Asa@63 | 578 function ItemAuditor:GetSafeLink(link) | 
| Asa@9 | 579 	local newLink = nil | 
| Asa@9 | 580 | 
| Asa@24 | 581 	if link and link == string.match(link, '.-:[-0-9]+[:0-9]*') then | 
| Asa@24 | 582 		newLink = link | 
| Asa@24 | 583 	elseif link then | 
| Asa@9 | 584 		newLink = link and string.match(link, "|H(.-):([-0-9]+):([0-9]+)|h") | 
| Asa@9 | 585 	end | 
| Asa@9 | 586 	if newLink == nil then | 
| Asa@9 | 587 		local itemID = self:GetItemID(link) | 
| Asa@9 | 588 		if itemID ~= nil then | 
| Asa@9 | 589 			_, newLink = GetItemInfo(itemID) | 
| Asa@9 | 590 			return self:GetSafeLink(newLink) | 
| Asa@9 | 591 		end | 
| Asa@9 | 592 	end | 
| Asa@145 | 593 	   return newLink and string.gsub(newLink, ":0:0:0:0:0:0", ""):gsub(":[0-9]+$", "") | 
| Asa@9 | 594 end | 
| Asa@9 | 595 | 
| Asa@63 | 596 function ItemAuditor:GetIDFromLink(link) | 
| Asa@9 | 597 	local _, _, _, _, Id = string.find(link, "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?") | 
| Asa@9 | 598 	return tonumber(Id) | 
| Asa@9 | 599 end | 
| Asa@9 | 600 | 
| Asa@63 | 601 function ItemAuditor:GetItemCost(link, countModifier) | 
| Asa@9 | 602 	local item = self:GetItem(link, true) | 
| Asa@8 | 603 | 
| Asa@9 | 604 	if item.invested > 0 then | 
| Asa@9 | 605 		local count = item.count | 
| Asa@133 | 606 | 
| Asa@9 | 607 		if countModifier ~= nil then | 
| Asa@9 | 608 			count = count - countModifier | 
| Asa@0 | 609 		end | 
| Asa@133 | 610 		if count > 0 then | 
| Asa@45 | 611 			return ceil(item.invested), ceil(item.invested/count), count | 
| Asa@9 | 612 		end | 
| Asa@121 | 613 		return ceil(item.invested), 0, count | 
| Asa@0 | 614 	end | 
| Asa@82 | 615 	return 0, 0, ItemAuditor:GetItemCount(ItemAuditor:GetIDFromLink(link)) | 
| Asa@0 | 616 end | 
| Asa@132 | 617 | 
| Asa@132 | 618 ItemAuditor.Options.args.misc= { | 
| Asa@132 | 619 	name = "Misc", | 
| Asa@132 | 620 	type = 'group', | 
| Asa@132 | 621 	args = { | 
| Asa@132 | 622 	}, | 
| Asa@132 | 623 } | 
| Asa@132 | 624 local function GetGuild(info) | 
| Asa@132 | 625 	local guildName = info[#(info)] | 
| Asa@132 | 626 	return (ItemAuditor.db.factionrealm.enabled_guilds[guildName] == true) | 
| Asa@132 | 627 end | 
| Asa@132 | 628 | 
| Asa@132 | 629 local function SetGuild(info, value) | 
| Asa@132 | 630 	local guildName = info[#(info)] | 
| Asa@132 | 631 	ItemAuditor.db.factionrealm.enabled_guilds[guildName] = value or nil | 
| Asa@132 | 632 end | 
| Asa@132 | 633 | 
| Asa@132 | 634 for guildName in pairs(DataStore:GetGuilds()) do | 
| Asa@132 | 635 	ItemAuditor.Options.args.misc.args[guildName] = { | 
| Asa@132 | 636 		type = "toggle", | 
| Asa@132 | 637 		name = "Count "..guildName.." Guild Bank", | 
| Asa@132 | 638 		get = GetGuild, | 
| Asa@132 | 639 		set = SetGuild, | 
| Asa@132 | 640 		order = 11, | 
| Asa@132 | 641 	} | 
| Asa@132 | 642 end |