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:
Claude
2026-10-28 22:43:09 +01:00
parent 6797506a5f
commit 7138eaf572
5 changed files with 677 additions and 23 deletions
+170 -5
View File
@@ -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)