mirror of
https://github.com/Lacustat/Freewood-OctoWoW-Mail.git
synced 2026-11-07 12:08:58 +00:00
0.1.155: Mail: Lesefenster (Linksklick), Loeschen, Archiv mit Suche, Aufraeumen nach Alle oeffnen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9
This commit is contained in:
@@ -51,9 +51,11 @@ local Mail = FW:NewModule("Mail", {
|
||||
title = L.MAIL_TITLE,
|
||||
description = L.MAIL_DESC,
|
||||
defaults = {
|
||||
settings = { replace = false, chat = true },
|
||||
settings = { replace = false, chat = true, cleanup = true, archiveDays = 0 },
|
||||
recipients = {},
|
||||
lists = {},
|
||||
archive = {}, -- entfernte Briefe, neueste zuerst
|
||||
sentSubjects = {}, -- Betreffe, die wir selbst ohne Text gesendet haben (Betreff -> Zeit)
|
||||
},
|
||||
})
|
||||
NS.Mail = Mail
|
||||
@@ -258,6 +260,141 @@ function Mail.ReadInbox()
|
||||
return list
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Brieftext, Archiv, einzelne Briefe
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local MAX_ARCHIVE = 500
|
||||
Mail.taken = {} -- "Absender|Betreff" -> { money, items = { "Name xN" } }: was aus dem Brief genommen wurde
|
||||
|
||||
local function Pair(m) return tostring(m.sender) .. "|" .. tostring(m.subject) end
|
||||
Mail.Pair = Pair
|
||||
|
||||
-- Text eines Briefs (markiert ihn als gelesen). Rueckgabe: Text, istRechnung
|
||||
function Mail.ReadText(i)
|
||||
if not GetInboxText then return "", false end
|
||||
local ok, body, _, _, invoice = pcall(GetInboxText, i)
|
||||
if not ok then return "", false end
|
||||
body = type(body) == "string" and body or ""
|
||||
if invoice and GetInboxInvoiceInfo then
|
||||
local ok2, kind, item, who, bid = pcall(GetInboxInvoiceInfo, i)
|
||||
if ok2 and item then
|
||||
local parts = { tostring(kind or "invoice"), tostring(item) }
|
||||
if who then parts[#parts + 1] = tostring(who) end
|
||||
if bid then parts[#parts + 1] = Mail.Money(bid) end
|
||||
body = (body ~= "" and (body .. "\n") or "") .. table.concat(parts, " - ")
|
||||
end
|
||||
end
|
||||
return body, invoice and true or false
|
||||
end
|
||||
|
||||
-- Nichts mehr angehaengt, kein Gold, keine Nachnahme: dann gehen beim Loeschen keine Werte verloren
|
||||
function Mail.Deletable(m)
|
||||
return m ~= nil and not m.hasItem and m.money == 0 and m.cod == 0
|
||||
end
|
||||
|
||||
local function NoteTaken(m, what)
|
||||
local p = Pair(m)
|
||||
local t = Mail.taken[p] or { money = 0, items = {} }
|
||||
Mail.taken[p] = t
|
||||
if what == "money" then
|
||||
t.money = t.money + m.money
|
||||
else
|
||||
t.items[#t.items + 1] = (m.itemName or "?") .. ((m.itemCount or 1) > 1 and (" x" .. m.itemCount) or "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Eigene Briefe ohne Text (Betreff steht alles): werden ohne Archiv entfernt
|
||||
function Mail:NoteSent(subject, body)
|
||||
if (body or "") ~= "" or not subject or subject == "" then return end
|
||||
local t = self.db.sentSubjects
|
||||
local now = time()
|
||||
t[subject] = now
|
||||
local n = 0
|
||||
for k, v in pairs(t) do
|
||||
n = n + 1
|
||||
if now - (tonumber(v) or 0) > 30 * 86400 then t[k] = nil end
|
||||
end
|
||||
end
|
||||
|
||||
function Mail:IsOwnSent(subject, body)
|
||||
return (body or "") == "" and self.db.sentSubjects[subject] ~= nil
|
||||
end
|
||||
|
||||
function Mail:AddArchive(m, body)
|
||||
local info = Mail.taken[Pair(m)]
|
||||
local e = {
|
||||
t = time(), sender = m.sender, subject = m.subject, body = body or "",
|
||||
money = info and info.money or 0, items = info and info.items or {},
|
||||
char = UnitName and UnitName("player") or "?",
|
||||
}
|
||||
table.insert(self.db.archive, 1, e)
|
||||
while #self.db.archive > MAX_ARCHIVE do table.remove(self.db.archive) end
|
||||
FW:Fire("MAIL_ARCHIVE_CHANGED")
|
||||
end
|
||||
|
||||
-- Archiv-Eintraege nach der eingestellten Zeit loeschen (0 = nie)
|
||||
function Mail:Purge()
|
||||
local days = tonumber(self.db.settings.archiveDays) or 0
|
||||
if days <= 0 then return 0 end
|
||||
local limit = time() - days * 86400
|
||||
local a, n = self.db.archive, 0
|
||||
for i = #a, 1, -1 do
|
||||
if (a[i].t or 0) < limit then table.remove(a, i) n = n + 1 end
|
||||
end
|
||||
if n > 0 then FW:Fire("MAIL_ARCHIVE_CHANGED") end
|
||||
return n
|
||||
end
|
||||
|
||||
-- Leeren Brief entfernen: erst ins Archiv (ausser unseren eigenen Textlosen), dann loeschen.
|
||||
-- Rueckgabe: true, "archived"|"own" oder false, Grund
|
||||
function Mail:RemoveMail(m)
|
||||
if not Mail.Deletable(m) then return false, "notempty" end
|
||||
local body = Mail.ReadText(m.i)
|
||||
if body:find("FW%d+:[a-z0-9]+:%x+:") then return false, "data" end -- Freewood-Daten: bleiben fuer die Annahme
|
||||
local own = self:IsOwnSent(m.subject, body)
|
||||
if not own then self:AddArchive(m, body) end
|
||||
pcall(DeleteInboxItem, m.i)
|
||||
Mail.taken[Pair(m)] = nil
|
||||
return true, own and "own" or "archived"
|
||||
end
|
||||
|
||||
function Mail:FindMail(key, pair)
|
||||
local list = Mail.ReadInbox()
|
||||
for _, m in ipairs(list) do
|
||||
if m.key == key then return m end
|
||||
end
|
||||
if pair then
|
||||
for _, m in ipairs(list) do
|
||||
if Pair(m) == pair then return m end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Inhalt eines einzelnen Briefs nehmen (erst Gold, dann Item). Nachnahme zahlt der Spieler mit einem zweiten Klick (UI).
|
||||
function Mail:TakeOne(m)
|
||||
if self.busy then return false, "busy" end
|
||||
if m.cod > 0 then
|
||||
local B = FW.Brett
|
||||
if B and B.InspectMail then
|
||||
local ok, st = pcall(B.InspectMail, m.i)
|
||||
if ok and (st == "high" or st == "nodeal") then return false, "codbad" end
|
||||
end
|
||||
if ((GetMoney and GetMoney()) or 0) < m.cod then return false, "codmoney" end
|
||||
end
|
||||
if m.money > 0 and m.cod == 0 then
|
||||
NoteTaken(m, "money")
|
||||
pcall(TakeInboxMoney, m.i)
|
||||
return true
|
||||
elseif m.hasItem then
|
||||
if Mail.FreeSlots() <= KEEP_FREE then return false, "full" end
|
||||
NoteTaken(m, "item")
|
||||
pcall(TakeInboxItem, m.i)
|
||||
return true
|
||||
end
|
||||
return false, "empty"
|
||||
end
|
||||
|
||||
Mail.selected = {} -- key -> true
|
||||
|
||||
function Mail:SelectedCount()
|
||||
@@ -291,7 +428,7 @@ function Mail:StartOpen(onlySelected)
|
||||
for k in pairs(self.selected) do only[k] = true end
|
||||
if next(only) == nil then return false end
|
||||
end
|
||||
self.run = { only = only, tries = {}, money = 0, items = 0, pending = nil }
|
||||
self.run = { only = only, tries = {}, money = 0, items = 0, pending = nil, removed = 0, archived = 0, touched = {} }
|
||||
self.busy = "inbox"
|
||||
self.driver:Show()
|
||||
Trace("Posteingang: Start" .. (only and " (nur Ausgewaehlte)" or " (alle)"))
|
||||
@@ -315,7 +452,8 @@ local function FinishOpen(self, reason)
|
||||
else
|
||||
Say(string.format(FW.L.MAIL_RUN_DONE, run.money, run.items, skipped > 0 and string.format(FW.L.MAIL_RUN_SKIP_FULL, skipped) or ""))
|
||||
end
|
||||
self.lastRun = { money = run.money, items = run.items, skipped = skipped }
|
||||
if run.removed > 0 then Say(string.format(FW.L.MAIL_RUN_CLEANED, run.removed, run.archived)) end
|
||||
self.lastRun = { money = run.money, items = run.items, skipped = skipped, removed = run.removed }
|
||||
self.selected = {}
|
||||
FW:Fire("MAIL_STATE")
|
||||
FW:Fire("MAIL_SELECTION_CHANGED")
|
||||
@@ -344,6 +482,8 @@ function Mail:StepOpen()
|
||||
if m.money > 0 then
|
||||
run.tries[m.sig] = (run.tries[m.sig] or 0) + 1
|
||||
run.money = run.money + 1
|
||||
run.touched[Pair(m)] = true
|
||||
NoteTaken(m, "money")
|
||||
run.pending, run.updated = now, false
|
||||
pcall(TakeInboxMoney, m.i)
|
||||
Trace(string.format("Brief %d von %s: Gold %d genommen", m.i, m.sender, m.money))
|
||||
@@ -355,6 +495,8 @@ function Mail:StepOpen()
|
||||
else
|
||||
run.tries[m.sig] = (run.tries[m.sig] or 0) + 1
|
||||
run.items = run.items + 1
|
||||
run.touched[Pair(m)] = true
|
||||
NoteTaken(m, "item")
|
||||
run.pending, run.updated = now, false
|
||||
pcall(TakeInboxItem, m.i)
|
||||
Trace(string.format("Brief %d von %s: Item %s genommen (frei vorher %d)", m.i, m.sender, tostring(m.itemName), free))
|
||||
@@ -364,6 +506,24 @@ function Mail:StepOpen()
|
||||
end
|
||||
end
|
||||
end
|
||||
-- nichts mehr zu nehmen: leere Briefe entfernen (mit Text und fremde kommen vorher ins Archiv)
|
||||
if self.db.settings.cleanup then
|
||||
for idx = #list, 1, -1 do
|
||||
local m = list[idx]
|
||||
local key = "del|" .. m.sig
|
||||
if Mail.Deletable(m) and (not run.only or run.touched[Pair(m)]) and (run.tries[key] or 0) < 2 then
|
||||
run.tries[key] = (run.tries[key] or 0) + 1
|
||||
local ok, how = self:RemoveMail(m)
|
||||
if ok then
|
||||
run.removed = run.removed + 1
|
||||
if how == "archived" then run.archived = run.archived + 1 end
|
||||
run.pending, run.updated = now, false
|
||||
FW:Fire("MAIL_STATE")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
FinishOpen(self, "done")
|
||||
end
|
||||
|
||||
@@ -699,6 +859,7 @@ end
|
||||
------------------------------------------------------------------------
|
||||
|
||||
function Mail:OnMailShow()
|
||||
pcall(self.Purge, self)
|
||||
self.open = true
|
||||
self.gen = self.gen + 1
|
||||
self.selected = {}
|
||||
@@ -770,8 +931,11 @@ function Mail:OnEnable()
|
||||
self.hooked = true
|
||||
-- Empfaenger merken, egal wer sendet (auch das normale Fenster und Brett-Auftraege)
|
||||
if hooksecurefunc and _G.SendMail then
|
||||
pcall(hooksecurefunc, "SendMail", function(to)
|
||||
if Mail.db then Mail.lastTo = to end
|
||||
pcall(hooksecurefunc, "SendMail", function(to, subject, body)
|
||||
if Mail.db then
|
||||
Mail.lastTo = to
|
||||
pcall(Mail.NoteSent, Mail, subject, body)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if _G.MailFrame and MailFrame.HookScript then
|
||||
@@ -780,6 +944,7 @@ function Mail:OnEnable()
|
||||
pcall(MailFrame.HookScript, MailFrame, "OnHide", function() pcall(Mail.OnMailClosed, Mail) end)
|
||||
end
|
||||
end
|
||||
After(3, function() pcall(Mail.Purge, Mail) end)
|
||||
-- Briefkasten schon offen (z. B. nach /reload)?
|
||||
if _G.MailFrame and MailFrame.IsShown and MailFrame:IsShown() then
|
||||
After(0.5, function() pcall(Mail.OnMailShow, Mail) end)
|
||||
|
||||
Reference in New Issue
Block a user