annotate Modules/Events.lua @ 150:47885ba83d38 Release 2010-10-30

Removed some debugging code that caused characters on your own account to use the cross-account mail. Addes support GnomeWorks and ATSW queues.
author Asa Ayers <Asa.Ayers@Gmail.com>
date Mon, 18 Oct 2010 19:25:44 -0700
parents 03e108d12ef1
children 091bae7bfca0
rev   line source
Asa@63 1 local ItemAuditor = select(2, ...)
Asa@63 2 local Events = ItemAuditor:NewModule("Events", "AceEvent-3.0")
Asa@3 3
Asa@92 4 local Utils = ItemAuditor:GetModule("Utils")
Asa@92 5
Asa@63 6 function ItemAuditor:OnEnable()
Asa@3 7 self:RegisterEvent("MAIL_SHOW")
Asa@4 8 self:RegisterEvent("UNIT_SPELLCAST_START")
Asa@81 9
Asa@81 10 self:RegisterEvent("BANKFRAME_OPENED", 'BankFrameChanged')
Asa@81 11 self:RegisterEvent("BANKFRAME_CLOSED", 'BankFrameChanged')
Asa@81 12
Asa@63 13 ItemAuditor:UpdateCurrentInventory()
Asa@3 14 self:WatchBags()
Asa@9 15
Asa@63 16 self:SetEnabled(nil, self.db.profile.ItemAuditor_enabled)
Asa@38 17 end
Asa@38 18
Asa@63 19 function ItemAuditor:OnDisable()
Asa@38 20 self:UnwatchBags()
Asa@38 21 self:UnregisterAllEvents()
Asa@63 22 ItemAuditor:HideAllFrames()
Asa@3 23 end
Asa@3 24
Asa@63 25 function ItemAuditor:MAIL_SHOW()
Asa@3 26 self:Debug("MAIL_SHOW")
Asa@113 27 self.mailOpen = true
Asa@63 28 ItemAuditor:UpdateCurrentInventory()
Asa@3 29 self.lastMailScan = self:ScanMail()
Asa@7 30
Asa@3 31 self:UnregisterEvent("MAIL_SHOW")
Asa@3 32 self:RegisterEvent("MAIL_CLOSED")
Asa@3 33 self:RegisterEvent("MAIL_INBOX_UPDATE")
Asa@39 34
Asa@39 35 self:GenerateBlankOutbox()
Asa@39 36
Asa@39 37 self:RegisterEvent("MAIL_SUCCESS")
Asa@39 38 end
Asa@39 39
Asa@63 40 function ItemAuditor:GenerateBlankOutbox()
Asa@39 41 self.mailOutbox = {
Asa@39 42 from = UnitName("player"),
Asa@39 43 to = "",
Asa@39 44 subject = "",
Asa@39 45 link = '',
Asa@39 46 count = 0,
Asa@39 47 COD = 0,
Asa@39 48 key = random(10000),
Asa@39 49 sent = 0,
Asa@39 50 }
Asa@39 51
Asa@39 52 if self.db.factionrealm.outbound_cod[self.mailOutbox.key] ~= nil then
Asa@39 53 return self:GenerateBlankOutbox()
Asa@39 54 end
Asa@39 55 end
Asa@39 56
Asa@40 57 local attachedItems = {}
Asa@39 58 local Orig_SendMail = SendMail
Asa@42 59 local skipCODTracking = false
Asa@92 60 local skipCODCheck = false
Asa@42 61
Asa@42 62 StaticPopupDialogs["ItemAuditor_Send_COD_without_tracking_number"] = {
Asa@42 63 text = "ItemAuditor cannot track COD mail with multiple item types attached. Do you want to send this mail without tracking?",
Asa@42 64 button1 = "Yes",
Asa@42 65 button2 = "No",
Asa@42 66 OnAccept = function()
Asa@42 67 skipCODTracking = true
Asa@42 68 end,
Asa@42 69 timeout = 0,
Asa@42 70 whileDead = true,
Asa@42 71 hideOnEscape = true,
Asa@42 72 }
Asa@39 73
Asa@92 74 StaticPopupDialogs["ItemAuditor_Insufficient_COD"] = {
Asa@92 75 text = "The COD on this mail is less than the value of items attached. Are you sure you want to send this?|nTotal value (including postage): %s",
Asa@92 76 button1 = "Yes",
Asa@92 77 button2 = "No",
Asa@92 78 OnAccept = function()
Asa@92 79 skipCODCheck = true
Asa@92 80 end,
Asa@92 81 timeout = 0,
Asa@92 82 whileDead = true,
Asa@92 83 hideOnEscape = true,
Asa@92 84 }
Asa@92 85
Asa@39 86 function SendMail(recipient, subject, body, ...)
Asa@39 87 local self = ItemAuditor
Asa@39 88 self:GenerateBlankOutbox()
Asa@39 89
Asa@39 90 self:Debug(format("[To: %s] [Subject: %s]", recipient, subject))
Asa@39 91
Asa@39 92 self.mailOutbox.COD = GetSendMailCOD()
Asa@39 93
Asa@92 94 wipe(attachedItems)
Asa@40 95 local totalStacks = 0
Asa@39 96 local link
Asa@40 97 for index = 1, ATTACHMENTS_MAX_SEND do
Asa@39 98 local itemName, _, itemCount = GetSendMailItem(index)
Asa@39 99 local newLink = GetSendMailItemLink(index)
Asa@39 100
Asa@40 101 if newLink ~= nil then
Asa@40 102 newLink = self:GetSafeLink(newLink)
Asa@40 103 totalStacks = totalStacks + 1
Asa@40 104 attachedItems[newLink] = (attachedItems[newLink] or {stacks = 0, count = 0})
Asa@40 105 attachedItems[newLink].stacks = attachedItems[newLink].stacks + 1
Asa@40 106 attachedItems[newLink].count = attachedItems[newLink].count + itemCount
Asa@40 107 attachedItems[newLink].price = 0 -- This is a placeholder for below.
Asa@144 108 attachedItems[newLink].costEach = select(2, ItemAuditor:GetItemCost(newLink))
Asa@39 109 end
Asa@40 110 end
Asa@92 111 local attachedValue = 0
Asa@40 112 for link, data in pairs(attachedItems) do
Asa@53 113 data.price = 30 * data.stacks
Asa@144 114 attachedValue = attachedValue + data.price + (data.costEach * data.count)
Asa@40 115 end
Asa@144 116
Asa@144 117 local destinationType = 'unknown'
Asa@144 118 local realm = GetRealmName()
Asa@144 119 for account in pairs(DataStore:GetAccounts()) do
Asa@144 120 for character in pairs(DataStore:GetCharacters(realm, account)) do
Asa@144 121 if strlower(recipient) == strlower(character) then
Asa@144 122 destinationType = (account == 'Default') and 'same_account' or 'owned_account'
Asa@144 123 break
Asa@144 124 end
Asa@92 125 end
Asa@92 126 end
Asa@144 127 self.mailOutbox.destinationType = destinationType
Asa@144 128 if destinationType == 'unknown' and attachedValue > self.mailOutbox.COD and not skipCODCheck and ItemAuditor.db.char.cod_warnings then
Asa@92 129 self:GenerateBlankOutbox()
Asa@92 130 skipCODCheck = false;
Asa@92 131 local vararg = ...
Asa@92 132 StaticPopupDialogs["ItemAuditor_Insufficient_COD"].OnAccept = function()
Asa@92 133 skipCODCheck = true
Asa@92 134 SendMail(recipient, subject, body, vararg)
Asa@92 135 skipCODCheck = false
Asa@92 136 end
Asa@92 137 StaticPopup_Show ("ItemAuditor_Insufficient_COD", Utils.FormatMoney(attachedValue));
Asa@92 138 return
Asa@144 139 elseif destinationType == 'owned_account' then
Asa@144 140 -- If we are mailing to an alt on a different account, a uniqueue tracking number
Asa@144 141 -- is generated and all of the needed data is attached to the message.
Asa@144 142 -- The tracking number is only used to make sure the other character doesn't count the
Asa@144 143 -- mail more than once.
Asa@144 144 local key = time()..":"..random(10000)
Asa@144 145 self.mailOutbox.attachedItems = attachedItems
Asa@144 146 body = body .. ItemAuditor.TRACKING_DATA_DIVIDER .. ItemAuditor:Serialize(key, attachedItems)
Asa@92 147 elseif self.mailOutbox.COD > 0 and skipCODTracking then
Asa@42 148
Asa@42 149 elseif self.mailOutbox.COD > 0 then
Asa@40 150 if self:tcount(attachedItems) > 1 then
Asa@39 151 self:GenerateBlankOutbox()
Asa@42 152 local vararg = ...
Asa@42 153 StaticPopupDialogs["ItemAuditor_Send_COD_without_tracking_number"].OnAccept = function()
Asa@42 154 skipCODTracking = true
Asa@42 155 SendMail(recipient, subject, body, vararg)
Asa@42 156 skipCODTracking = false
Asa@42 157 end
Asa@42 158 StaticPopup_Show ("ItemAuditor_Send_COD_without_tracking_number");
Asa@39 159 return
Asa@39 160 end
Asa@40 161 self:Debug("COD mail")
Asa@39 162
Asa@40 163 subject = format("[IA: %s] %s", self.mailOutbox.key, subject)
Asa@40 164 self.mailOutbox.subject = subject
Asa@40 165 self.mailOutbox.to = recipient
Asa@40 166
Asa@40 167 -- At this point we know there is only one item
Asa@40 168 for link, data in pairs(attachedItems) do
Asa@40 169 self.mailOutbox.link = link
Asa@40 170 self.mailOutbox.count = data.count
Asa@40 171 end
Asa@40 172 else
Asa@40 173 self:Debug("Non-COD mail")
Asa@39 174 end
Asa@40 175
Asa@39 176 return Orig_SendMail(recipient, subject, body, ...)
Asa@39 177 end
Asa@39 178
Asa@63 179 function ItemAuditor:MAIL_SUCCESS(event)
Asa@42 180 skipCODTracking = false
Asa@92 181 skipCODCheck = false
Asa@92 182
Asa@40 183 for link, data in pairs(attachedItems) do
Asa@144 184 -- When mailing to an alt on a different account, we still
Asa@144 185 -- should add the price of postage, but need to subtract the
Asa@144 186 -- cost of the items. This will simulate CODing the mail and
Asa@144 187 -- getting the money back, except that postage is paid by the
Asa@144 188 -- sender
Asa@144 189 if self.mailOutbox.destinationType == 'owned_account' then
Asa@144 190 data.price = data.price - (data.costEach * data.count)
Asa@144 191 end
Asa@40 192 self:SaveValue(link, data.price, data.count)
Asa@40 193 end
Asa@39 194 if self.mailOutbox.COD > 0 then
Asa@39 195 self:Debug(format("MAIL_SUCCESS %d [To: %s] [Subject: %s] [COD: %s]", self.mailOutbox.key, self.mailOutbox.to, self.mailOutbox.subject, self.mailOutbox.COD))
Asa@39 196
Asa@39 197 self.mailOutbox.sent = time()
Asa@39 198 self.db.factionrealm.outbound_cod[self.mailOutbox.key] = self.mailOutbox
Asa@39 199 end
Asa@92 200
Asa@92 201 wipe(attachedItems)
Asa@40 202 self:GenerateBlankOutbox()
Asa@3 203 end
Asa@3 204
Asa@63 205 function ItemAuditor:MAIL_CLOSED()
Asa@23 206 self:Debug("MAIL_CLOSED")
Asa@63 207 ItemAuditor:UnregisterEvent("MAIL_CLOSED")
Asa@7 208 self:MAIL_INBOX_UPDATE()
Asa@3 209 self:UnregisterEvent("MAIL_INBOX_UPDATE")
Asa@3 210 self:RegisterEvent("MAIL_SHOW")
Asa@113 211 self.mailOpen = nil
Asa@3 212 end
Asa@3 213
Asa@144 214 local function CanMailBeDeleted(mailIndex)
Asa@144 215 local msgMoney, _, _, msgItem = select(5, GetInboxHeaderInfo(mailIndex))
Asa@144 216 local body = GetInboxText(mailIndex)
Asa@144 217 if msgMoney == 0 and msgItem == nil and body and body:find(ItemAuditor.TRACKING_DATA_DIVIDER) then
Asa@144 218 local serialized = body:gsub('.*'..ItemAuditor.TRACKING_DATA_DIVIDER, '')
Asa@144 219 local body = body:gsub(ItemAuditor.TRACKING_DATA_DIVIDER..'.*', '')
Asa@144 220 local success, trackingID, data = ItemAuditor:Deserialize(serialized)
Asa@144 221 if success and body == '' then
Asa@144 222 return true
Asa@144 223 end
Asa@144 224 end
Asa@144 225 return false
Asa@144 226 end
Asa@144 227
Asa@144 228 local Postal_L
Asa@144 229 local function blockMailOperations()
Asa@144 230 if MailAddonBusy == 'ItemAuditor' then
Asa@144 231 return false
Asa@144 232 end
Asa@144 233 if Postal_L == nil then
Asa@144 234 local locale = LibStub("AceLocale-3.0", true)
Asa@144 235 Postal_L = locale and locale:GetLocale("Postal", true)
Asa@144 236 end
Asa@144 237 return MailAddonBusy or PostalOpenAllButton and Postal_L and PostalOpenAllButton:GetText() == Postal_L["In Progress"]
Asa@144 238 end
Asa@144 239
Asa@26 240 local storedCountDiff
Asa@63 241 function ItemAuditor:MAIL_INBOX_UPDATE()
Asa@23 242 self:Debug("MAIL_INBOX_UPDATE")
Asa@144 243 self.deleteQueue = nil
Asa@144 244 local newScan = self:ScanMail()
Asa@3 245 local diff
Asa@39 246
Asa@6 247 for mailType, collection in pairs(self.lastMailScan) do
Asa@7 248 newScan[mailType] = (newScan[mailType] or {})
Asa@26 249 for itemName, data in pairs(collection) do
Asa@26 250 newScan[mailType][itemName] = (newScan[mailType][itemName] or {total=0,count=0})
Asa@26 251 local totalDiff = data.total - newScan[mailType][itemName].total
Asa@26 252 local countDiff = data.count - newScan[mailType][itemName].count
Asa@26 253 --[[
Asa@26 254 In one update the item will be taken and in the following update the invoice
Asa@26 255 will be gone. I need to store the item difference in order ot pass it into
Asa@26 256 SaveValue.
Asa@26 257 ]]
Asa@26 258 if countDiff ~= 0 then
Asa@26 259 storedCountDiff = countDiff
Asa@26 260 end
Asa@26 261
Asa@26 262 if totalDiff ~= 0 then
Asa@39 263 if mailType == "CODPayment" then
Asa@39 264 local trackID
Asa@39 265 trackID, itemName= strsplit("|", itemName, 2)
Asa@39 266 self.db.factionrealm.outbound_cod[tonumber(trackID)] = nil
Asa@39 267 self:Debug("Removing COD Tracker: " .. trackID)
Asa@39 268 end
Asa@26 269 self:SaveValue(itemName, totalDiff, storedCountDiff)
Asa@26 270 storedCountDiff = 0
Asa@6 271 end
Asa@6 272
Asa@3 273 end
Asa@3 274 end
Asa@3 275
Asa@3 276 self.lastMailScan = newScan
Asa@144 277
Asa@144 278 if self.deleteQueue and not self.deleteScheduled then
Asa@144 279 -- For some reason DeleteInboxItem will not trigger a MAIL_INBOX_UPDATE
Asa@144 280 -- if it is called from here, so I have to use a timer to get it
Asa@144 281 -- to run outside of this function.
Asa@144 282
Asa@144 283 -- If the mailbox is full of items to be deleted, this will speed up because
Asa@144 284 -- postal shouldn't be running at this point. Keeping at 0.1 breaks postal.
Asa@144 285 local delay = (GetInboxNumItems() > #(self.deleteQueue)) and 1 or 0.1
Asa@144 286 self:ScheduleTimer("ProcessDeleteQueue", delay)
Asa@144 287 self.deleteScheduled = true
Asa@144 288 elseif MailAddonBusy == 'ItemAuditor' then
Asa@144 289 MailAddonBusy = nil
Asa@144 290 end
Asa@144 291 end
Asa@144 292
Asa@144 293 function ItemAuditor:ProcessDeleteQueue()
Asa@144 294 if blockMailOperations() then
Asa@144 295 self:ScheduleTimer("ProcessDeleteQueue", 1)
Asa@144 296 return
Asa@144 297 end
Asa@144 298 self.deleteScheduled = false
Asa@144 299 if self.deleteQueue then
Asa@144 300 MailAddonBusy = 'ItemAuditor'
Asa@144 301 while #(self.deleteQueue) > 0 do
Asa@144 302 local mailIndex = table.remove(self.deleteQueue)
Asa@144 303 if CanMailBeDeleted(mailIndex) then
Asa@144 304 DeleteInboxItem(mailIndex)
Asa@144 305 -- This returns after the first item because you can't delete
Asa@144 306 -- all mail at once and this is in a loop so that if for some
Asa@144 307 -- reason CanMailBeDeleted returns false, we can delete the next
Asa@144 308 -- mail in the queue instead.
Asa@144 309 return
Asa@144 310 end
Asa@144 311 end
Asa@144 312 else
Asa@144 313 MailAddonBusy = nil
Asa@144 314 end
Asa@3 315 end
Asa@3 316
Asa@63 317 function ItemAuditor:UNIT_SPELLCAST_START(event, target, spell)
Asa@5 318 if target == "player" and spell == "Milling" or spell == "Prospecting" or spell == "Disenchanting" then
Asa@23 319 self:Debug(event .. " " .. spell)
Asa@4 320 self:UnwatchBags()
Asa@4 321 self:UpdateCurrentInventory()
Asa@4 322 self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
Asa@4 323 self:RegisterEvent("LOOT_CLOSED")
Asa@3 324 end
Asa@3 325 end
Asa@3 326
Asa@4 327 --[[
Asa@4 328 The item should be destroyed before this point, so the last inventory check
Asa@4 329 needs to be kept so it can be combined with the up coming loot.
Asa@4 330 ]]
Asa@63 331 function ItemAuditor:LOOT_CLOSED()
Asa@23 332 self:Debug("LOOT_CLOSED")
Asa@4 333 self:UnregisterEvent("LOOT_CLOSED")
Asa@4 334 self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED")
Asa@4 335 local inventory = self.lastInventory
Asa@4 336 self:WatchBags()
Asa@4 337 self.lastInventory = inventory
Asa@4 338 end
Asa@3 339
Asa@63 340 function ItemAuditor:UNIT_SPELLCAST_INTERRUPTED(event, target, spell)
Asa@5 341 if target == "player" and spell == "Milling" or spell == "Prospecting" or spell == "Disenchanting" then
Asa@23 342 self:Debug(event .. " " .. spell)
Asa@4 343 self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED")
Asa@4 344 self:UnregisterEvent("LOOT_CLOSED")
Asa@4 345 self:WatchBags()
Asa@4 346 end
Asa@4 347 end
Asa@4 348
Asa@63 349 function ItemAuditor:UpdateCurrentInventory()
Asa@4 350 self.lastInventory = self:GetCurrentInventory()
Asa@3 351 end
Asa@3 352
Asa@49 353 local function distributeValue(self, totalValue, targetItems)
Asa@46 354
Asa@46 355 local weights = {}
Asa@46 356 local totalWeight = 0
Asa@137 357 for itemID, change in pairs(targetItems) do
Asa@46 358 --[[
Asa@46 359 If something has never been seen on the AH, it must not be very valuable.
Asa@46 360 I'm using 1c so it doesn't have much weight and I can't get a devided by zero error.
Asa@46 361 The only time I know that this is a problem is when crafting a BOP item, and it
Asa@46 362 is always crafted 1 at a time, so a weight of 1 will work.
Asa@46 363 ]]
Asa@137 364 local ap = (ItemAuditor:GetAuctionPrice(itemID) or 1) * change
Asa@46 365 totalWeight = totalWeight + ap
Asa@137 366 weights[itemID] = ap
Asa@46 367 end
Asa@46 368
Asa@137 369 for itemID, change in pairs(targetItems) do
Asa@137 370 local value = totalValue * (weights[itemID]/totalWeight)
Asa@137 371 self:SaveValue(itemID, value, change)
Asa@46 372 end
Asa@46 373 end
Asa@46 374
Asa@63 375 function ItemAuditor:UpdateAudit()
Asa@23 376 -- self:Debug("UpdateAudit " .. event)
Asa@3 377 local currentInventory = self:GetCurrentInventory()
Asa@63 378 local diff = ItemAuditor:GetInventoryDiff(self.lastInventory, currentInventory)
Asa@3 379
Asa@5 380 local positive, negative = {}, {}
Asa@5 381 local positiveCount, negativeCount = 0, 0
Asa@137 382 for itemID, count in pairs(diff.items) do
Asa@5 383 if count > 0 then
Asa@137 384 positive[itemID] = count
Asa@5 385 positiveCount = positiveCount + count
Asa@5 386 elseif count < 0 then
Asa@137 387 negative[itemID] = count
Asa@5 388 negativeCount = negativeCount + abs(count)
Asa@5 389 end
Asa@5 390 end
Asa@5 391
Asa@23 392 if positiveCount + negativeCount == 0 then
Asa@33 393 --[[
Asa@33 394 Nothing needs to be done, but this will prevent mistakenly attributing
Asa@33 395 the cost of flights to the first item you pick up.
Asa@33 396 ]]
Asa@33 397 elseif diff.money > 0 and self:tcount(positive) > 0 and self:tcount(negative) == 0 then
Asa@15 398 self:Debug("loot")
Asa@113 399 elseif abs(diff.money) > 0 and self:tcount(diff.items) == 1 and not self.mailOpen then
Asa@15 400 self:Debug("purchase or sale")
Asa@3 401
Asa@137 402 for itemID, count in pairs(diff.items) do
Asa@140 403 self:SaveValue(itemID, 0 - diff.money, itemID)
Asa@3 404 end
Asa@23 405 elseif self:tcount(diff.items) > 1 and self:tcount(positive) > 0 and self:tcount(negative) > 0 then
Asa@23 406 -- we must have created/converted something
Asa@23 407 self:Debug("conversion")
Asa@3 408
Asa@23 409 local totalChange = 0
Asa@137 410 for itemID, change in pairs(negative) do
Asa@137 411 local _, itemCost, count = self:GetItemCost(itemID, change)
Asa@137 412 self:SaveValue(itemID, itemCost * change, change)
Asa@10 413
Asa@23 414 totalChange = totalChange + (itemCost * abs(change))
Asa@3 415 end
Asa@91 416 totalChange = totalChange - diff.money
Asa@23 417
Asa@46 418 distributeValue(self, totalChange, positive)
Asa@23 419 else
Asa@23 420 self:Debug("No match in UpdateAudit.")
Asa@3 421 end
Asa@3 422
Asa@3 423 self.lastInventory = currentInventory
Asa@63 424 ItemAuditor:WatchBags()
Asa@3 425 end