From 396e4468753ce74d4181fa2be7616f722500a709 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 23:33:28 +0100 Subject: [PATCH] 0.1.159: Brett-Brief ueber den Freewood-Briefkasten (sichtbar, Auto-Senden), dynamisches Sendetempo, Betreff so breit wie Text Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9 --- Freewood-OctoWoW-Mail.toc | 2 +- Locales.lua | 20 ++++++++++ Mail.lua | 77 +++++++++++++++++++++++++++++++++++++-- MailUI.lua | 64 +++++++++++++++++++++++++++++++- 4 files changed, 156 insertions(+), 7 deletions(-) diff --git a/Freewood-OctoWoW-Mail.toc b/Freewood-OctoWoW-Mail.toc index dacc25c..000e33d 100644 --- a/Freewood-OctoWoW-Mail.toc +++ b/Freewood-OctoWoW-Mail.toc @@ -3,7 +3,7 @@ ## Notes: Own mailbox window: open all or selected mails safely, recent recipients, mailing lists with automatic sending, multi-item mails, gold surplus and cash on delivery. ## Notes-deDE: Eigenes Briefkasten-Fenster: alles oder Ausgewähltes sicher öffnen, letzte Empfänger, Mailinglisten mit Automatik, mehrere Items, Gold-Überschuss und Nachnahme. ## Author: Lacustat -## Version: 0.1.158 +## Version: 0.1.159 ## Dependencies: Freewood-OctoWoW-Core ## OptionalDeps: Freewood-OctoWoW-Creator, Freewood-OctoWoW-Brett diff --git a/Locales.lua b/Locales.lua index 42d3af0..f7f7f7f 100644 --- a/Locales.lua +++ b/Locales.lua @@ -56,6 +56,16 @@ FW:AddLocale("enUS", { MAIL_SEND_COD_NEEDS_ITEM = "Cash on delivery needs at least one item.", MAIL_BOARD_BUSY = "A board mission is preparing a mail. Finish it in the mission window first; this window waits.", MAIL_BUSY = "Another action is running. Please wait.", + MAIL_BOARD_TITLE = "Board mission letter", + MAIL_BOARD_FILLING = "The letter is being prepared ...", + MAIL_BOARD_READY_AUTO = "Everything is correct. The letter is sent automatically.", + MAIL_BOARD_READY_MANUAL = "Everything is correct. Click \"Send now\".", + MAIL_BOARD_LOCKED = "The letter was changed and is locked. Close the mailbox and open it again.", + MAIL_BOARD_SEND = "Send now", + MAIL_BOARD_ADD = "Add what is missing", + MAIL_BOARD_SENT = "Board letter sent.", + MAIL_SPEED_ADAPT = "Mailbox: the server is slowing down, the sending speed was adapted.", + MAIL_SET_BOARDAUTO = "Send board mission letters automatically once everything is correct", MAIL_PICK_TITLE = "Choose items", MAIL_PICK_LIST_TITLE = "Items for %s", @@ -196,6 +206,16 @@ FW:AddLocale("deDE", { MAIL_SEND_COD_NEEDS_ITEM = "Nachnahme braucht mindestens ein Item.", MAIL_BOARD_BUSY = "Ein Brett-Auftrag bereitet gerade einen Brief vor. Erst im Auftragsfenster abschließen; dieses Fenster wartet.", MAIL_BUSY = "Es läuft gerade eine andere Aktion. Bitte warten.", + MAIL_BOARD_TITLE = "Brief für einen Brett-Auftrag", + MAIL_BOARD_FILLING = "Der Brief wird vorbereitet ...", + MAIL_BOARD_READY_AUTO = "Alles stimmt. Der Brief wird automatisch gesendet.", + MAIL_BOARD_READY_MANUAL = "Alles stimmt. Klicke auf \"Jetzt senden\".", + MAIL_BOARD_LOCKED = "Der Brief wurde verändert und ist gesperrt. Briefkasten schließen und neu öffnen.", + MAIL_BOARD_SEND = "Jetzt senden", + MAIL_BOARD_ADD = "Fehlendes ergänzen", + MAIL_BOARD_SENT = "Brett-Brief gesendet.", + MAIL_SPEED_ADAPT = "Briefkasten: Der Server bremst, das Sendetempo wurde angepasst.", + MAIL_SET_BOARDAUTO = "Brett-Auftragsbriefe automatisch senden, sobald alles stimmt", MAIL_PICK_TITLE = "Items auswählen", MAIL_PICK_LIST_TITLE = "Items für %s", diff --git a/Mail.lua b/Mail.lua index b6e8b6b..60f191d 100644 --- a/Mail.lua +++ b/Mail.lua @@ -29,7 +29,10 @@ local MAX_JOBS = 120 local STEP = 0.1 -- Takt des Treibers local INBOX_GAP = 0.5 -- Pause nach dem Nehmen local INBOX_TIMEOUT = 2.5 -- so lange auf die Antwort des Servers warten -local SEND_GAP = 1.2 -- Pause nach jedem gesendeten Brief +local GAP_START = 0.4 -- Pause nach jedem gesendeten Brief: Start, wird dynamisch angepasst +local GAP_MIN = 0.25 +local GAP_MAX = 4 +local MAX_RETRY = 3 -- so oft wird ein vom Server abgelehnter Brief mit langsamerem Tempo wiederholt local SEND_TIMEOUT = 8 local POSTAGE = 30 -- Kupfer je Brief (Schaetzung fuer den Gold-Ueberschuss) local AUTO_DELAY = 2.5 @@ -51,7 +54,7 @@ local Mail = FW:NewModule("Mail", { title = L.MAIL_TITLE, description = L.MAIL_DESC, defaults = { - settings = { replace = false, chat = true, cleanup = true, archiveDays = 0 }, + settings = { replace = false, chat = true, cleanup = true, archiveDays = 0, boardAuto = true }, recipients = {}, lists = {}, archive = {}, -- entfernte Briefe, neueste zuerst @@ -64,6 +67,7 @@ FW.Mail = Mail Mail.open = false -- ein Briefkasten ist offen Mail.busy = nil -- nil | "inbox" | "send" Mail.gen = 0 -- zaehlt die Briefkasten-Oeffnungen +Mail.gap = GAP_START -- aktuelle Pause zwischen zwei Briefen (so klein wie moeglich, waechst bei Ablehnung) local function Now() return GetTime and GetTime() or 0 end local function After(t, fn) @@ -360,6 +364,46 @@ function Mail:RemoveMail(m) return true, own and "own" or "archived" end +-- Brett-Brief: Ein Brett-Auftrag bereitet einen Brief vor (Creator befuellt das verdeckte Briefformular). +-- Mit ersetztem Briefkasten zeigt unser Fenster den Stand und sendet selbst, sobald alles stimmt. +function Mail.BoardLetter() + local C = FW.Creator + if not (C and C.GuardState and C.MailCheck) then return nil end + local ok, st = pcall(C.GuardState) + if not ok or not st or st == "off" then return nil end + local ok2, list, all = pcall(C.MailCheck) + if not ok2 then list, all = nil, false end + return { state = st, list = list or {}, all = all and true or false } +end + +function Mail:BoardSend() + local bl = Mail.BoardLetter() + if not bl or bl.state ~= "armed" or not bl.all then return false end + local b = _G.SendMailMailButton + if not (b and b.Click) then return false end + if b.IsEnabled and not b:IsEnabled() then return false end + self.boardSending = true + Trace("Brett-Brief: Senden ueber den Freewood-Briefkasten") + pcall(b.Click, b) + return true +end + +-- Automatisch senden, wenn alles stimmt (Einstellung "boardAuto"), hoechstens 3 Versuche je Oeffnung +function Mail:BoardAuto() + if not (self.open and self.Replacing() and self.db.settings.boardAuto) then return end + if self.boardAutoAt or (self.boardTries or 0) >= 3 then return end + local bl = Mail.BoardLetter() + if not bl or bl.state ~= "armed" or not bl.all then return end + self.boardAutoAt = true + After(0.8, function() + Mail.boardAutoAt = nil + if Mail.open and Mail.Replacing() then + Mail.boardTries = (Mail.boardTries or 0) + 1 + Mail:BoardSend() + end + end) +end + -- Brief an den Absender zurueckschicken (Anhang und Gold gehen mit zurueck). Nicht moeglich bei System-/Auktionsbriefen. function Mail.Returnable(m) if not m or not m.canReply or m.returned then return false end @@ -647,9 +691,24 @@ function Mail:StepSend() if Mail.BoardBusy() then return self:FinishSend("board") end if s.stage == "wait" then if s.ok then - s.stage, s.nextAt = "next", now + SEND_GAP + -- Erfolg: Tempo langsam wieder erhoehen (Pause kleiner), solange der Server mitkommt + s.streak = (s.streak or 0) + 1 + if s.streak >= 3 then s.streak = 0 Mail.gap = math.max(GAP_MIN, Mail.gap * 0.8) end + s.retry = 0 + s.stage, s.nextAt = "next", now + Mail.gap elseif s.err then - return self:FinishSend("error") + -- Der Server hat den Brief abgelehnt (meist zu schnell): Pause verdoppeln und denselben Brief nochmal versuchen + s.retry = (s.retry or 0) + 1 + if s.retry > MAX_RETRY then return self:FinishSend("error") end + s.streak = 0 + Mail.gap = math.min(GAP_MAX, Mail.gap * 2 + 0.3) + Trace(string.format("Server lehnt ab: Pause jetzt %.2f s, Versuch %d von %d fuer Brief %d", Mail.gap, s.retry, MAX_RETRY, s.i)) + if not s.slowSaid then s.slowSaid = true Say(FW.L.MAIL_SPEED_ADAPT) end + pcall(ClearCursor) + ClearAttachment() + s.i = s.i - 1 + s.err, s.stage, s.nextAt = false, "next", now + Mail.gap + 0.5 + return elseif now > s.deadline then return self:FinishSend("timeout") end @@ -881,6 +940,7 @@ function Mail:OnMailShow() pcall(self.Purge, self) self.open = true self.gen = self.gen + 1 + self.boardTries, self.boardAutoAt, self.boardSending = 0, nil, nil self.selected = {} self:ApplyReplace() Trace("Briefkasten geoeffnet") @@ -904,6 +964,13 @@ function Mail:OnInboxUpdate() end function Mail:OnSendSuccess() + if self.boardSending then + self.boardSending = nil + local C = FW.Creator + if C then C.mailChoice = nil end -- Brett-Brief ist raus: unser Fenster ist wieder frei + Say(FW.L.MAIL_BOARD_SENT) + FW:Fire("MAIL_STATE") + end local s = self.send if self.lastTo then self:AddRecipient(self.lastTo) @@ -946,6 +1013,7 @@ function Mail:OnEnable() FW:RegisterEvent("MAIL_INBOX_UPDATE", function() pcall(Mail.OnInboxUpdate, Mail) end, self) FW:RegisterEvent("MAIL_SEND_SUCCESS", function() pcall(Mail.OnSendSuccess, Mail) end, self) FW:RegisterEvent("MAIL_FAILED", function() pcall(Mail.OnSendFailed, Mail) end, self) + FW:On("CREATOR_MAIL_UPDATE", function() pcall(Mail.BoardAuto, Mail) end, self) if not self.hooked then self.hooked = true -- Empfaenger merken, egal wer sendet (auch das normale Fenster und Brett-Auftraege) @@ -974,6 +1042,7 @@ function Mail:OnDisable() for _, ev in ipairs({ "MAIL_SHOW", "MAIL_CLOSED", "MAIL_INBOX_UPDATE", "MAIL_SEND_SUCCESS", "MAIL_FAILED" }) do FW:UnregisterEvent(ev, self) end + pcall(FW.Off, FW, "CREATOR_MAIL_UPDATE", self) if self.driver then self.driver:Hide() end self.run, self.send, self.busy = nil, nil, nil if self.hidden then diff --git a/MailUI.lua b/MailUI.lua index f6ebe67..203648a 100644 --- a/MailUI.lua +++ b/MailUI.lua @@ -606,7 +606,7 @@ local function BuildSend(parent) dd:SetPoint("LEFT", toBox, "RIGHT", 8, 0) Heading(f, T("MAIL_SUBJECT"), 0, -54) - local subjBox = MakeEdit(f, 628, nil, 64) + local subjBox = MakeEdit(f, 616, nil, 64) -- genauso breit wie das Textfeld (dessen Rollbalken nimmt 12 ab) subjBox:SetPoint("TOPLEFT", f, "TOPLEFT", 0, -74) Heading(f, T("MAIL_BODY"), 0, -108) @@ -642,6 +642,57 @@ local function BuildSend(parent) local send = UI.CreateButton(f, T("MAIL_SEND"), 150, 30, nil, "primary") send:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 0) + -- Brett-Brief: sichtbare Vorbereitung im eigenen Fenster (verdeckt das Formular, solange der Auftrag den Brief fuellt) + local bl = UI.CreatePanel(f, "bgDeep", "gold") + bl:SetPoint("TOPLEFT", f, "TOPLEFT", -8, 8) + bl:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 8, -8) + bl:SetFrameLevel(f:GetFrameLevel() + 30) + bl:EnableMouse(true) + bl:Hide() + local blTitle = UI.CreateLabel(bl, T("MAIL_BOARD_TITLE"), { font = "heading", size = 15, color = "goldLight" }) + blTitle:SetPoint("TOPLEFT", bl, "TOPLEFT", 16, -14) + bl.lines = {} + for i = 1, 6 do + local l = UI.CreateLabel(bl, "", { size = 12 }) + l:SetPoint("TOPLEFT", bl, "TOPLEFT", 16, -48 - (i - 1) * 24) + l:SetPoint("TOPRIGHT", bl, "TOPRIGHT", -16, -48 - (i - 1) * 24) + l:SetWordWrap(false) + bl.lines[i] = l + end + bl.status = UI.CreateLabel(bl, "", { size = 12, color = "goldLight", justifyV = "TOP" }) + bl.status:SetPoint("TOPLEFT", bl, "TOPLEFT", 16, -208) + bl.status:SetPoint("TOPRIGHT", bl, "TOPRIGHT", -16, -208) + bl.status:SetHeight(48) + bl.status:SetWordWrap(true) + bl.add = UI.CreateButton(bl, T("MAIL_BOARD_ADD"), 180, 28, function() + if FW.Creator and FW.Creator.MailAdd then pcall(FW.Creator.MailAdd) end + if f.Refresh then f.Refresh() end + end, "default") + bl.add:SetPoint("BOTTOMLEFT", bl, "BOTTOMLEFT", 16, 16) + bl.now = UI.CreateButton(bl, T("MAIL_BOARD_SEND"), 160, 28, function() + Mail:BoardSend() + if f.Refresh then f.Refresh() end + end, "primary") + bl.now:SetPoint("BOTTOMRIGHT", bl, "BOTTOMRIGHT", -16, 16) + + local function BoardRefresh() + local b = Mail.BoardLetter() + if not b then bl:Hide() return false end + bl:Show() + for i, l in ipairs(bl.lines) do + local c = b.list[i] + l:SetText(c and ((c.ok and "|cff6cc070[x] " or "|cffff7060[ ] ") .. c.text .. "|r") or "") + end + local auto = Mail.db.settings.boardAuto + if b.state == "locked" then bl.status:SetText("|cffff7060" .. T("MAIL_BOARD_LOCKED") .. "|r") + elseif b.state == "armed" then bl.status:SetText(auto and T("MAIL_BOARD_READY_AUTO") or T("MAIL_BOARD_READY_MANUAL")) + else bl.status:SetText(T("MAIL_BOARD_FILLING")) end + bl.now:SetShown(b.state == "armed" and b.all) + bl.add:SetShown(b.state == "filling" and not b.all) + Mail:BoardAuto() + return true + end + local function Copper() return (tonumber(goldBox.edit:GetText()) or 0) * 10000 + (tonumber(silverBox.edit:GetText()) or 0) * 100 + (tonumber(copperBox.edit:GetText()) or 0) end @@ -662,6 +713,7 @@ local function BuildSend(parent) end function f.Refresh() + if BoardRefresh() then return end for i, pk in ipairs(picks) do local pk, i = pk, i local row = rows[i] if not row then @@ -1065,6 +1117,7 @@ local function HideWindow() end FW:On("MAIL_WINDOW_SHOW", function() ShowWindow() end, OWNER) +FW:On("CREATOR_MAIL_PREPARING", function() if window and window:IsShown() then SelectTab("send") end end, OWNER) FW:On("MAIL_WINDOW_HIDE", function() HideWindow() end, OWNER) FW:On("MAIL_STATE", function() RefreshCurrent() end, OWNER) FW:On("MAIL_INBOX_CHANGED", function() RefreshCurrent() end, OWNER) @@ -1078,7 +1131,7 @@ FW:On("MAIL_LISTS_CHANGED", function() RefreshCurrent() if picker and picker:IsS if UI.RegisterSettingsSection then UI:RegisterSettingsSection("mail", { order = 118, - height = 190, + height = 220, build = function(parent) local f, stack = UI.CreateSection(parent, T("MAIL_SET_HEADER")) local boxes = {} @@ -1096,6 +1149,13 @@ if UI.RegisterSettingsSection then end) stack:Add(chat, { fit = true, gap = 4 }) boxes[#boxes + 1] = chat + local boardAuto = UI.CreateCheckbox(f, T("MAIL_SET_BOARDAUTO"), function() + return Mail.db.settings.boardAuto + end, function(value) + Mail.db.settings.boardAuto = value and true or false + end) + stack:Add(boardAuto, { fit = true, gap = 4 }) + boxes[#boxes + 1] = boardAuto local cleanup = UI.CreateCheckbox(f, T("MAIL_SET_CLEANUP"), function() return Mail.db.settings.cleanup end, function(value)