local Postal = LibStub("AceAddon-3.0"):GetAddon("Postal") local Postal_OpenAll = Postal:NewModule("OpenAll", "AceEvent-3.0") local L = LibStub("AceLocale-3.0"):GetLocale("Postal") Postal_OpenAll.description = L["A button that collects all attachments and coins from mail."] Postal_OpenAll.description2 = L[ [[|cFFFFCC00*|r Simple filters are available for various mail types. |cFFFFCC00*|r Shift-Click the Open All button to override the filters and take ALL mail. |cFFFFCC00*|r OpenAll will never delete any mail (mail without text is auto-deleted by the game when all attached items and gold are taken). |cFFFFCC00*|r OpenAll will skip CoD mails and mails from Blizzard. |cFFFFCC00*|r Disable the Verbose option to stop the chat spam while opening mail.]] ] -- luacheck: globals InboxFrame local MAX_MAIL_SHOWN = 100 local mailIndex, attachIndex local numUnshownItems local lastItem, lastNumAttach, lastNumGold local wait local button local Postal_OpenAllMenuButton local skipFlag local invFull, invAlmostFull local openAllOverride local firstMailDaysLeft -- Frame to process opening mail local updateFrame = CreateFrame("Frame") updateFrame:Hide() updateFrame:SetScript("OnShow", function(self) self.time = Postal.db.profile.OpenSpeed if invAlmostFull and self.time < 1.0 and not self.lootingMoney then -- Delay opening to 1 second to account for a nearly full -- inventory to respect the KeepFreeSpace setting self.time = 1.0 end self.lootingMoney = nil end) updateFrame:SetScript("OnUpdate", function(self, elapsed) self.time = self.time - elapsed if self.time <= 0 then self:Hide() Postal_OpenAll:ProcessNext() end end) -- Frame to refresh the Inbox -- I'm cheap so instead of trying to track 60 or so seconds since the -- last CheckInbox(), I just call CheckInbox() every 10 seconds local refreshFrame = CreateFrame("Frame", nil, MailFrame) refreshFrame:Hide() refreshFrame:SetScript("OnShow", function(self) self.time = 10 self.mode = nil end) refreshFrame:SetScript("OnUpdate", function(self, elapsed) self.time = self.time - elapsed if self.time <= 0 then if self.mode == nil then self.time = 10 Postal:Print(L["Refreshing mailbox..."]) self:RegisterEvent("MAIL_INBOX_UPDATE") CheckInbox() refreshFrame:OnEvent() else self:Hide() Postal_OpenAll:OpenAll(true) end end end) function refreshFrame:OnEvent(event) local current, total = GetInboxNumItems() if current == MAX_MAIL_SHOWN or current == total then -- If we're here, then mailbox contains a full fresh batch or -- we're showing all the mail we have. Continue OpenAll in -- 3 seconds to allow for other addons to do stuff. self.time = 3 self.mode = 1 self:UnregisterEvent("MAIL_INBOX_UPDATE") end end refreshFrame:SetScript("OnEvent", refreshFrame.OnEvent) function Postal_OpenAll:OnEnable() if not button then button = CreateFrame("Button", "PostalOpenAllButton", InboxFrame, "UIPanelButtonTemplate") button:SetWidth(120) button:SetHeight(25) if GetLocale() == "frFR" then button:SetPoint("CENTER", InboxFrame, "TOP", -46, -399) else button:SetPoint("CENTER", InboxFrame, "TOP", -36, -399) end button:SetText(L["Open All"]) button:SetScript("OnClick", function() Postal_OpenAll:OpenAll() end) button:SetFrameLevel(button:GetFrameLevel() + 1) end if not Postal_OpenAllMenuButton then -- Create the Menu Button Postal_OpenAllMenuButton = CreateFrame("Button", "Postal_OpenAllMenuButton", InboxFrame); Postal_OpenAllMenuButton:SetWidth(30); Postal_OpenAllMenuButton:SetHeight(30); Postal_OpenAllMenuButton:SetPoint("LEFT", button, "RIGHT", -2, 0); Postal_OpenAllMenuButton:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up"); Postal_OpenAllMenuButton:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Round"); Postal_OpenAllMenuButton:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled"); Postal_OpenAllMenuButton:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down"); Postal_OpenAllMenuButton:SetScript("OnClick", function(self, button, down) if Postal_DropDownMenu.initialize ~= Postal_OpenAll.ModuleMenu then CloseDropDownMenus() Postal_DropDownMenu.initialize = Postal_OpenAll.ModuleMenu end ToggleDropDownMenu(1, nil, Postal_DropDownMenu, self:GetName(), 0, 0) end) Postal_OpenAllMenuButton:SetFrameLevel(Postal_OpenAllMenuButton:GetFrameLevel() + 1) end self:RegisterEvent("MAIL_SHOW") -- For enabling after a disable OpenAllMail:Hide() -- hide Blizzard's Open All button button:Show() Postal_OpenAllMenuButton:SetScript("OnHide", Postal_DropDownMenu.HideMenu) Postal_OpenAllMenuButton:Show() end function Postal_OpenAll:OnDisable() self:Reset() button:Hide() OpenAllMail:Show() -- show Blizzard's Open All button Postal_OpenAllMenuButton:SetScript("OnHide", nil) Postal_OpenAllMenuButton:Hide() end function Postal_OpenAll:MAIL_SHOW() self:RegisterEvent("MAIL_CLOSED", "Reset") self:RegisterEvent("PLAYER_LEAVING_WORLD", "Reset") end function Postal_OpenAll:OpenAll(isRecursive) refreshFrame:Hide() mailIndex, numUnshownItems = GetInboxNumItems() numUnshownItems = numUnshownItems - mailIndex attachIndex = ATTACHMENTS_MAX_RECEIVE invFull = nil invAlmostFull = nil skipFlag = false lastItem = false lastNumAttach = nil lastNumGold = nil wait = false if not isRecursive then openAllOverride = IsShiftKeyDown() end if mailIndex == 0 then return end firstMailDaysLeft = select(7, GetInboxHeaderInfo(1)) Postal:DisableInbox(1) button:SetText(L["In Progress"]) self:RegisterEvent("UI_ERROR_MESSAGE") self:ProcessNext() end function Postal_OpenAll:ProcessNext() -- We need this because MAIL_INBOX_UPDATEs can now potentially -- include mailbox refreshes since patch 4.0.3 (that is mail can -- get inserted both at the back (old mail) and at the front -- (new mail received in the last 60 seconds)) local currentFirstMailDaysLeft = select(7, GetInboxHeaderInfo(1)) if currentFirstMailDaysLeft ~= 0 and currentFirstMailDaysLeft ~= firstMailDaysLeft then -- First mail's daysLeft changed, indicating we have a -- fresh MAIL_INBOX_UPDATE that has new data from CheckInbox() -- so we reopen from the last mail return self:OpenAll(true) -- tail call end if mailIndex > 0 then -- Check if we need to wait for the mailbox to change if wait then local attachCount, goldCount = Postal:CountItemsAndMoney() if lastNumGold ~= goldCount then -- Process next mail, gold has been taken wait = false mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call elseif lastNumAttach ~= attachCount then -- Process next item, an attachment has been taken wait = false attachIndex = attachIndex - 1 if lastItem then -- The item taken was the last item, process next mail lastItem = false mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call end else -- Wait longer until something in the mailbox changes updateFrame:Show() return end end local sender, msgSubject, msgMoney, msgCOD, _, msgItem, _, _, msgText, _, isGM = select(3, GetInboxHeaderInfo(mailIndex)) -- Skip mail if it contains a CoD or if its from a GM if (msgCOD and msgCOD > 0) or (isGM) then skipFlag = true mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call end -- Filter by mail type local mailType = Postal:GetMailType(msgSubject) if mailType == "NonAHMail" then -- Skip mail with attachments according to user options if msgItem and Postal.db.profile.OpenAll.Postmaster and sender and (sender:find(L["The Postmaster"]) -- unlooted items (npc=34337) or sender:find(L["Thaumaturge Vashreen"])) -- bonus roll w/ bags full (npc=54441) then -- open attachments below elseif openAllOverride or Postal.db.profile.OpenAll.Attachments then -- open attachments and/or money below else -- skip it mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call end else -- Skip AH mail types according to user options if not (openAllOverride or Postal.db.profile.OpenAll[mailType]) then mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call end end -- Print message on next mail if Postal.db.profile.OpenAll.SpamChat and attachIndex == ATTACHMENTS_MAX_RECEIVE then if not invFull or msgMoney > 0 then local moneyString = msgMoney > 0 and " ["..Postal:GetMoneyString(msgMoney).."]" or "" local playerName if (mailType == "AHSuccess" or mailType == "AHWon") then playerName = select(3,GetInboxInvoiceInfo(mailIndex)) playerName = playerName and (" ("..playerName..")") end Postal:Print(format("%s %d: %s%s%s", L["Processing Message"], mailIndex, msgSubject or "", moneyString, (playerName or ""))) end end -- Find next attachment index backwards while not GetInboxItemLink(mailIndex, attachIndex) and attachIndex > 0 do attachIndex = attachIndex - 1 end -- Check for free bag space if attachIndex > 0 and not invFull and Postal.db.profile.OpenAll.KeepFreeSpace>0 then local free=0 for bag=0,NUM_BAG_SLOTS do local bagFree,bagFam = GetContainerNumFreeSlots(bag) if bagFam==0 then free = free + bagFree end end if free <= Postal.db.profile.OpenAll.KeepFreeSpace then invFull = true invAlmostFull = nil Postal:Print(format(L["Not taking more items as there are now only %d regular bagslots free."], free)) elseif free <= Postal.db.profile.OpenAll.KeepFreeSpace + 1 then invAlmostFull = true end end -- If inventory is full, check if the item to be looted can stack with an existing stack local lootFlag = false if attachIndex > 0 and invFull then local name, itemID, itemTexture, count, quality, canUse = GetInboxItem(mailIndex, attachIndex) local link = GetInboxItemLink(mailIndex, attachIndex) local itemID = strmatch(link, "item:(%d+)") local stackSize = select(8, GetItemInfo(link)) if itemID and stackSize and GetItemCount(itemID) > 0 then for bag = 0, NUM_BAG_SLOTS do for slot = 1, GetContainerNumSlots(bag) do local texture2, count2, locked2, quality2, readable2, lootable2, link2 = GetContainerItemInfo(bag, slot) if link2 then local itemID2 = strmatch(link2, "item:(%d+)") if itemID == itemID2 and count + count2 <= stackSize then lootFlag = true break end end end if lootFlag then break end end end end if attachIndex > 0 and (lootFlag or not invFull) then -- If there's attachments, take the item --Postal:Print("Getting Item from Message "..mailIndex..", "..attachIndex) lastNumAttach, lastNumGold = Postal:CountItemsAndMoney() TakeInboxItem(mailIndex, attachIndex) wait = true -- Find next attachment index backwards local attachIndex2 = attachIndex - 1 while not GetInboxItemLink(mailIndex, attachIndex2) and attachIndex2 > 0 do attachIndex2 = attachIndex2 - 1 end if attachIndex2 == 0 and msgMoney == 0 then lastItem = true end updateFrame:Show() elseif msgMoney > 0 then -- No attachments, but there is money --Postal:Print("Getting Gold from Message "..mailIndex) lastNumAttach, lastNumGold = Postal:CountItemsAndMoney() TakeInboxMoney(mailIndex) wait = true updateFrame.lootingMoney = true updateFrame:Show() else -- Mail has no item or money, go to next mail mailIndex = mailIndex - 1 attachIndex = ATTACHMENTS_MAX_RECEIVE return self:ProcessNext() -- tail call end else -- Reached the end of opening all selected mail local numItems, totalItems = GetInboxNumItems() if numUnshownItems ~= totalItems - numItems then -- We will Open All again if the number of unshown items is different return self:OpenAll(true) -- tail call elseif totalItems > numItems and numItems < MAX_MAIL_SHOWN then -- We only want to refresh if there's more items to show Postal:Print(L["Not all messages are shown, refreshing mailbox soon to continue Open All..."]) refreshFrame:Show() return end if skipFlag then Postal:Print(L["Some Messages May Have Been Skipped."]) end self:Reset() end end function Postal_OpenAll:Reset(event) refreshFrame:Hide() updateFrame:Hide() self:UnregisterEvent("UI_ERROR_MESSAGE") button:SetText(L["Open All"]) Postal:DisableInbox() InboxFrame_Update() if event == "MAIL_CLOSED" or event == "PLAYER_LEAVING_WORLD" then self:UnregisterEvent("MAIL_CLOSED") self:UnregisterEvent("PLAYER_LEAVING_WORLD") end end function Postal_OpenAll:UI_ERROR_MESSAGE(event, error_message) if error_message == ERR_INV_FULL then invFull = true wait = false elseif error_message == ERR_ITEM_MAX_COUNT then attachIndex = attachIndex - 1 wait = false end end function Postal_OpenAll.SetKeepFreeSpace(dropdownbutton, arg1) Postal.db.profile.OpenAll.KeepFreeSpace = arg1 end function Postal_OpenAll.ModuleMenu(self, level) if not level then return end local info = self.info wipe(info) info.isNotRadio = 1 local db = Postal.db.profile.OpenAll if level == 1 + self.levelAdjust then info.hasArrow = 1 info.keepShownOnClick = 1 info.func = self.UncheckHack info.notCheckable = 1 info.text = L["AH-related mail"] info.value = "AHMail" UIDropDownMenu_AddButton(info, level) info.text = L["Non-AH related mail"] info.value = "NonAHMail" UIDropDownMenu_AddButton(info, level) info.text = L["Other options"] info.value = "OtherOptions" UIDropDownMenu_AddButton(info, level) elseif level == 2 + self.levelAdjust then info.keepShownOnClick = 1 info.func = Postal.SaveOption info.arg1 = "OpenAll" if UIDROPDOWNMENU_MENU_VALUE == "AHMail" then info.text = L["Open all Auction cancelled mail"] info.arg2 = "AHCancelled" info.checked = db.AHCancelled UIDropDownMenu_AddButton(info, level) info.text = L["Open all Auction expired mail"] info.arg2 = "AHExpired" info.checked = db.AHExpired UIDropDownMenu_AddButton(info, level) info.text = L["Open all Outbid on mail"] info.arg2 = "AHOutbid" info.checked = db.AHOutbid UIDropDownMenu_AddButton(info, level) info.text = L["Open all Auction successful mail"] info.arg2 = "AHSuccess" info.checked = db.AHSuccess UIDropDownMenu_AddButton(info, level) info.text = L["Open all Auction won mail"] info.arg2 = "AHWon" info.checked = db.AHWon UIDropDownMenu_AddButton(info, level) elseif UIDROPDOWNMENU_MENU_VALUE == "NonAHMail" then info.text = L["Open mail from the Postmaster"] info.arg2 = "Postmaster" info.checked = db.Postmaster UIDropDownMenu_AddButton(info, level) info.text = L["Open all mail with attachments"] info.arg2 = "Attachments" info.checked = db.Attachments UIDropDownMenu_AddButton(info, level) elseif UIDROPDOWNMENU_MENU_VALUE == "OtherOptions" then info.text = L["Keep free space"] info.hasArrow = 1 info.value = "KeepFreeSpace" info.func = self.UncheckHack UIDropDownMenu_AddButton(info, level) local listFrame = _G["DropDownList"..level] self.UncheckHack(_G[listFrame:GetName().."Button"..listFrame.numButtons]) info.text = L["Verbose mode"] info.hasArrow = nil info.value = nil info.func = Postal.SaveOption info.arg2 = "SpamChat" info.checked = db.SpamChat UIDropDownMenu_AddButton(info, level) end elseif level == 3 + self.levelAdjust then if UIDROPDOWNMENU_MENU_VALUE == "KeepFreeSpace" then local keepFree = db.KeepFreeSpace info.func = Postal_OpenAll.SetKeepFreeSpace info.isNotRadio = nil for _, v in ipairs(Postal.keepFreeOptions) do info.text = v info.checked = v == keepFree info.arg1 = v UIDropDownMenu_AddButton(info, level) end end end end