promotional bannermobile promotional banner

TOGTools

ToGTools is a convenient place to aggregate one off tools that don't make sense to have it's own addon.

File Details

TOGTools-v0.3.4

  • R
  • May 22, 2026
  • 1.93 MB
  • 2
  • 12.0.1+7
  • Retail + 3

File Name

TOGTools-TOGTools-v0.3.4.zip

Supported Versions

  • 12.0.1
  • 12.0.0
  • 11.2.7
  • 5.5.3
  • 4.4.2
  • 3.4.5
  • 2.5.5
  • 1.15.8

Changelog

[v0.3.4] (2026-05-22) - TBC /camp /exit /logout Taint Fix

Bug Fixes

  • /camp, /exit, /logout typed in chat tripped ADDON_ACTION_FORBIDDEN on TBC / Anniversary 1.15.x with NamePrefix active — v0.3.3 replaced the per-editbox OnEnterPressed script slot with an addon-Lua closure that invoked the captured original script via securecall(origScript, editBox, ...). The securecall boundary clears taint at its call site, but on TBC / Anniversary the slash-dispatch chain (ChatFrameEditBoxMixin:OnEnterPressedSendTextParseText → slash-command dispatcher → protected Logout()) still failed the secure-execution check for Logout() — confirmed in the field after v0.3.3 shipped, with the failing trace [TOGTools/Modules/NamePrefix/NamePrefix.lua]:161 → [C]: securecall → ChatFrameEditBox.lua:370 → SendText:252 → ParseText:207 → SlashCommands.lua:748 → Logout(). The OnEnterPressed slot itself becomes addon-owned once SetScript runs on it, so the C-level taint check sees addon ownership on the dispatch path regardless of the securecall clear at the boundary. Switched the Classic / older-Retail hook from SetScript("OnEnterPressed", ...) + securecall(origScript) to HookScript("OnKeyDown", ...) keyed on key == "ENTER" or "NUMPADENTER". OnKeyDown fires in its own C dispatch frame, ahead of OnEnterPressed, which is dispatched as a separate C-level call — our hook applies the prefix via SetText and returns to C before OnEnterPressed begins, so addon Lua is never on the call stack when SendText / ParseText / Logout() run. The OnEnterPressed script slot is no longer touched at all and stays bound to the original Blizzard handler, so the entire slash-dispatch chain executes in a fully secure context. HookScript chains alongside any existing OnKeyDown handler instead of claiming the slot; chat editboxes have no default OnKeyDown binding, so our hook is the only one on the editbox. The retail 12.0+ EventRegistry "ChatFrame.OnEditBoxPreSendText" path is unchanged. The ApplyPrefix leading-/ early-exit is unchanged and continues to ensure no SetText is issued for slash commands even though the OnKeyDown hook still runs. Updated the file-header comment block to document the rejected SetScript + securecall approach alongside the previously-rejected SendChatMessage / ChatEdit_SendText / mixin-method-replace wraps, and updated the ModifyMessage doc comment to reference the new OnKeyDown entry point. Removed the v0.3.3 temporary OnKeyDown diagnostic since its purpose (verify OnKeyDown fires on TBC ahead of OnEnterPressed) is now load-bearing in production code. Location: Modules/NamePrefix/NamePrefix.lua.

[v0.3.3] (2026-05-18) - NamePrefix /say Channel + TBC /logout Taint Fix

Bug Fixes

  • NamePrefix did not fire on TBC / Anniversary 1.15.x in the v0.3.2 working tree — During pre-release work toward this version the Classic hook was experimentally rewritten twice — first to wrap the global SendChatMessage, then to wrap the global ChatEdit_SendText (Name2Chat's pattern). Both wraps installed cleanly (verified with a temporary install-time print) but neither fire-time path was ever entered for user-typed chat on TBC / Anniversary 1.15.x — confirmed empirically with a temporary fire-time print inside ModifyMessage. On those clients ChatFrameEditBoxMixin:OnEnterPressed dispatches via self:SendText() using a captured local reference, so addon-level global reassignment never intercepts user chat. On Classic Era 1.15.x the global ChatEdit_SendText path is still active and did fire during testing, which initially masked the TBC/Anniversary regression. Restored v0.3.2's per-editbox SetScript("OnEnterPressed", ...) wrap that walks ChatFrame1EditBox..ChatFrameN.EditBox via NUM_CHAT_WINDOWS — this is the only entry point that reliably fires on every Classic flavor. Verified empirically on TBC (Anniversary, Wowhead Looter interface 20505) and Classic Era (interface 11508). Location: Modules/NamePrefix/NamePrefix.lua.
  • Per-editbox OnEnterPressed wrap tripped ADDON_ACTION_FORBIDDEN on /logoutSetScript("OnEnterPressed", addonFn) leaves an addon-Lua handler on the C call stack whenever Enter is pressed in a chat editbox. When the user types /logout, the original OnEnterPressed we invoke from inside our handler runs through ChatEdit_SendTextChatEdit_ParseText → slash-command dispatcher → protected Logout(). Because addon Lua is still on the stack, taint propagates the whole way and the secure-execution layer blocks Logout() on TBC and Anniversary. Fix: invoke the captured original script via securecall(origScript, editBox, ...) instead of a direct call. securecall is the canonical taint-clearing dispatcher (see WoWWiki "Secure Execution and Tainting") — it saves and clears the current taint flag around the call so ParseText → slash-handler → Logout() runs untainted regardless of what sits on the C stack above. Belt-and-suspenders: ApplyPrefix already early-exits on a leading /, so the editbox text is never modified for slash commands and its GetText result stays untainted on the secure dispatch path. Location: Modules/NamePrefix/NamePrefix.lua.

New Features

  • NamePrefix Say (/s) channel toggle — New checkbox at the top of the NamePrefix tab's Active Channels list. When enabled, the configured nickname is prepended to outgoing /say messages alongside the existing guild / officer / party / raid / instance toggles. SAY case added to the ApplyPrefix chatType dispatch table; say = false added to DB_DEFAULTS.global.namePrefix. Location: Modules/NamePrefix/NamePrefix.lua, GUI/NamePrefixTab.lua, TOGTools.lua.
  • Account-wide debug flag + addon:Debug() helper — New Debug section in the Settings panel (ESC → Options → Addons → TOG Tools, or right-click the minimap button) with a Verbose debug output toggle. Stored at db.global.debug, default false. addon:Debug(msg) prints |cffFF8000TOGTools|r <msg> only when the flag is true; modules call it for hook-install confirmations and fire-time diagnostics that should be silent in normal play. NamePrefix uses it for two diagnostics: the install confirmation NamePrefix: hook installed (OnEnterPressed xN) (one-shot at addon load — requires /reload to re-fire after toggling the flag) and the per-send fire line NamePrefix fire: chatType=X prefixed=true/false (no message echo, takes effect on the next chat send without /reload). Location: TOGTools.lua, GUI/Settings.lua, Modules/NamePrefix/NamePrefix.lua.

Improvements

  • NamePrefix channel defaults are now all falsesay, guild, officer, party, raid, instance all default to false in DB_DEFAULTS.global.namePrefix. Previously guild and officer defaulted to true. Fresh installs now opt-in per channel; existing saved settings are unaffected because AceDB merges defaults without overwriting saved keys. Location: TOGTools.lua.
  • .luarc.json / TOGTools.code-workspace — Added securecall, rawget, rawset to diagnostics.globals for Lua LSP coverage of the new hook code and the v0.3.2 account-wide migration block.

[v0.3.2] (2026-05-17) - NamePrefix BCC / Anniversary Fix

Bug Fixes

  • NamePrefix did nothing on BCC and Anniversary (Classic Era 1.15.x) — v0.3.0 relocated the Classic hook from ChatEdit_SendText to the global ChatEdit_OnEnterPressed to avoid tainting OPie's securecall(ChatEdit_SendText, ...) macrotext dispatch. On modern Classic builds (BCC, Anniversary 1.15.x) the chat editbox's OnEnterPressed script is the ChatFrameEditBoxMixin:OnEnterPressed method, NOT the global — so the wrapped global never fired and outgoing messages went un-prefixed. Replaced the global-wrap with a per-editbox SetScript("OnEnterPressed", ...) wrap that walks ChatFrame1EditBox..ChatFrameN EditBox (NUM_CHAT_WINDOWS) and prepends ModifyMessage to each editbox's existing script. This catches both the legacy ChatEdit_OnEnterPressed script binding and the modern mixin-method binding without touching any global function reference. Verified safe for OPie: the wrap only touches user-visible ChatFrameN editboxes, not OPie's synthetic Rewire editboxes, and never replaces ChatEdit_SendText or ChatFrameEditBoxMixin.SendText. Confirmed against Name2Chat's own analysis that EventRegistry "ChatFrame.OnEditBoxPreSendText" is not fired on Classic 1.15.x despite EventRegistry being backported — the existing gv.isRetail120Plus gate stays correct. Location: Modules/NamePrefix/NamePrefix.lua.
  • .luarc.json — Added NUM_CHAT_WINDOWS to diagnostics.globals.

Improvements

  • MainWindow remembers last-selected tab — Opening the main window (minimap button, /togt, or Toggle() with no arg) now restores the tab that was active the last time the user closed the window. Stored as db.char.lastTab; per-character so different alts can have different defaults. Resolution order in MainWindow:Open(tabKey) is: explicit caller arg → db.char.lastTab (if the module still exists) → alphabetically first tab. Slash commands that pass an explicit tab (/togt np, /togt mb, etc.) still win, and clicking a different tab updates the saved value via OnGroupSelected. Location: GUI/MainWindow.lua, TOGTools.lua.
  • NamePrefix is now account-wide — Moved namePrefix from DB_DEFAULTS.char to DB_DEFAULTS.global so the nickname, format string, per-channel toggles, and hideIfCharName are configured once and shared across every alt. Toons that shouldn't self-prefix rely on hideIfCharName (now defaulting to true) to suppress the prefix when the nickname matches the character's own name. One-shot migration in Ace:OnInitialize copies any pre-upgrade db.char.namePrefix with a non-empty nickname into db.global.namePrefix the first time an upgraded character logs in (only if the global table is still at defaults); subsequent alts inherit the now-global config. Leftover per-char entries are left in place since AceDB ignores keys not in the defaults. Location: TOGTools.lua, Modules/NamePrefix/NamePrefix.lua, GUI/NamePrefixTab.lua.
  • NamePrefix defaulthideIfCharName now defaults to true in DB_DEFAULTS.global.namePrefix. New accounts no longer self-prefix on the character whose name matches the configured nickname (the common case where the nickname IS the main's name). Existing saved-variables are unaffected. Location: TOGTools.lua.

[v0.3.1] (2026-05-06) - TBC Compatibility Fix

Bug Fixes

  • Addon Load tab crashed on TBC (and Wrath/Cata/Mists/Retail)GetNumAddOns, GetAddOnInfo, IsAddOnLoaded, GetAddOnMemoryUsage, and UpdateAddOnMemoryUsage were called as bare globals, but TBC+ moved them all into C_AddOns.*. Added five compat locals at the top of the module that prefer C_AddOns.* when available and fall back to the bare globals for Classic Era, matching the pattern already used elsewhere in the addon. Location: Modules/AddonLoad/AddonLoad.lua.

[v0.3.0] (2026-05-04) - Mailbox Stale Watcher

New Features

  • Mailbox Stale Watcher — New tab (/togt mail / mb / mailbox) tracking mail expiration across every alt on the account so mail never gets auto-deleted at the 30-day mark for an unvisited character. Captures a per-alt inbox snapshot on each MAIL_INBOX_UPDATE event, stamping absolute expiresAt = GetServerTime() + (daysLeft * 86400) per item so the time-remaining math stays accurate even when the alt does not re-visit a mailbox for several days (same content-derived-timestamp pattern TOGPM uses). Login chat alert prints once when any alt has mail expiring within the configured threshold (default 2 days, slider-configurable 1–7 in the tab). Tab lists each alt with a row-per-mail breakdown of expiring items (sender, subject, time remaining), snapshot age display, and a - stale flag for snapshots older than 7 days. Live-refresh on MAIL_INBOX_UPDATE while the tab is active, so opening a mailbox in-game while the Mailbox tab is showing updates the rows in real time without forcing a manual tab-switch (addon.MainWindow:Refresh() is called from the engine's event handler when addon.MainWindow.activeTab == "mailbox"). Account-wide storage in db.global.mailbox.snapshots[Name-Realm] so every alt's data is visible from any character, full Name-Realm keys throughout to prevent collisions across connected-realm clusters. Location: Modules/Mailbox/Mailbox.lua, GUI/MailboxTab.lua.

Improvements

  • AceDB schema — Added global namespace to DB_DEFAULTS with mailbox = { thresholdDays, snapshots }. Account-wide scope so cross-alt mail data persists across every physical realm in a connected-realm cluster (per-db.realm would silo each physical realm of the cluster into a separate notes table — wrong for our use case). Location: TOGTools.lua.
  • Slash commands/togt mb / /togt mail / /togt mailbox open the Mailbox tab directly; help output updated. Location: SlashCommands.lua.
  • .luarc.json — Added GetInboxNumItems, GetInboxHeaderInfo, and ChatEdit_OnEnterPressed to diagnostics.globals so the new mail API calls and the relocated NamePrefix hook target resolve cleanly under the Lua LSP.
  • TOC files — All six TOC variants (Vanilla, BCC, Wrath, Cata, Mists, Mainline) updated to load Modules\Mailbox\Mailbox.lua and GUI\MailboxTab.lua after the AddonLoad pair.

Bug Fixes

  • NamePrefix broke OPie macrotext ring entries (custom mounts, /cast macros) — On Classic clients, NamePrefix wrapped the global ChatEdit_SendText to enable pre-send text modification. OPie's Libs/ActionBook/Rewire.lua dispatches each line of macro ring entries via securecall(ChatEdit_SendText, box, false). Replacing the global with a wrapper created in our (insecure) addon loading context tainted that function reference, so OPie's secure dispatch path picked up taint when calling it — failing for any ring slot delivered as macrotext (custom mount macros, /cast slots), while direct spell-cast slots that bypass macrotext (most tradeskills) kept working. Fix: relocated the Classic hook target from ChatEdit_SendText to ChatEdit_OnEnterPressed, which is upstream of ChatEdit_SendText in the user-typed Enter-key flow but is not on OPie's Rewire dispatch path. The user-facing prefixing behavior is unchanged. The replaced-global pattern itself stays (hooksecurefunc fires post-call, so it can't be used to modify text before send); we just no longer taint the function OPie depends on. Location: Modules/NamePrefix/NamePrefix.lua.

[v0.2.0] (2026-05-04) - Addon Load Monitor

New Features

  • Addon Load Monitor — New tab (/togt al) showing every installed addon's memory usage, load status, and load timing. Sortable by name, memory, load time, and status (failures-first when descending). Summary bar shows totals (loaded / failed / disabled / on-demand) and total memory. Requires the !TOGT companion addon for full timing coverage across all addons. Location: Modules/AddonLoad/AddonLoad.lua, GUI/AddonLoadTab.lua.
  • !TOGT companion integration!TOGT (companion addon) added as a required dependency. Loads before all other addons at the very start of the loading screen (the ! prefix sorts before all letters) and records load order and per-addon offset into the TOGToolsEarlyData global. The Addon Load Monitor reads it at display time and shows a status-bar indicator (!TOGT active / !TOGT missing).
  • Shared addon.RowList component — New file GUI/RowList.lua, ported and trimmed from FastGuildInvite's GUI/RowList.lua. Reusable sortable-row list for any tab that needs a tabular data view: brand-colored clickable header bar, click-toggle ASC/DESC sort with per-column sortKey/sortDescDefault/sortable/gapBefore opts and tie-break on entry.name, alternating row banding, virtual-scroll pool that grows with the parent's height, custom Blizzard-textured scrollbar, mouse-wheel scroll. Cells use SetWordWrap(false) + SetMaxLines(1) and anchor LEFT/RIGHT to the parent so column layouts compact elastically when the user shrinks the window — no row-wrap. Public API: :New(parent, opts), :SetData(arr), :SetSort(key, desc), :Refresh(), :Detach(). Registered to all six TOC files after Compat.lua, before MainWindow.lua.
  • Compat.lua shared GUI helpersaddon.Tooltip.Owner / AnchorFrame (smart anchor flipping based on screen half), addon.AceGUIFrameScripts (leak-safe raw frame scripts that restore prior handlers on pooled-widget release), addon.GUI.AttachTooltip, addon.GUI.MakeColumnHeader, addon.GUI.ApplyMinResize, addon.GUI.DetachPool. Used by every tab.

Bug Fixes

  • Load times reporting 0.000sGetTime() is frame-locked, so every ADDON_LOADED event fired in the same loading-screen frame returned the same timestamp. Per-addon deltas collapsed to zero. Switched the timing capture in !TOGT.lua and the fallback capture in Modules/AddonLoad/AddonLoad.lua to debugprofilestop(), which has sub-millisecond precision. Offsets are stored in seconds (ms / 1000) so downstream consumers are unchanged.
  • Load Time column sort produced random-looking order — The sort comparator in GUI/AddonLoadTab.lua was reading row.offset (cumulative seconds since the first event) instead of row.loadTime (per-addon delta). Because offset is monotonic with load order, clicking the header effectively sorted by load order regardless of direction. Comparator now reads row.loadTime.
  • Addon Load tab wrapped to multiple lines on horizontal shrink — The tab used AceGUI Flow with widget:SetWidth(...) per cell, so when the user shrunk the window narrower than the column-width sum AceGUI moved cells to a new line and the layout fell apart. Replaced the row-rendering path with addon.RowList, whose anchor-based cells truncate instead of wrapping. The window's minWidth was tightened from 640 to 520 now that compaction is graceful.
  • Tab status bar leaked between tabsMainWindow:DrawTab now resets the status text to the version string before delegating to the tab module, so a tab that overrides the status (e.g. Addon Load showing !TOGT active/missing) doesn't bleed that text into the next tab the user opens.
  • Tab content didn't reflow on tab switchMainWindow:ApplyTabSize now calls f:DoLayout() after sizing, so when switching between tabs whose WINDOW_SIZE differs, the new tab's content reflows immediately instead of inheriting stale layout.

Improvements

  • Addon Load tab column widths tightened — Status reduced from 175 → 85 px (fits "Wrong Version"); Memory reduced from 110 → 65 px (fits "999.9 MB"); Load Time reduced from 110 → 60 px. The auto-width Addon Name column reclaims the freed space. RowList's SCROLLBAR_GUTTER reduced from SCROLLBAR_WIDTH + 6 to SCROLLBAR_WIDTH + 2 so the rightmost column sits ~5 px from the scrollbar. Status column gets gapBefore = 5 to break the visual collision between right-justified Load Time text and left-justified Status text.
  • Sort indicator removed from active column headerRowList:_updateHeaderText no longer appends a glyph (v / ^) to the active sort column; WoW's default font doesn't render unicode triangles cleanly and ASCII letters look like typos. Click affordance is communicated via the column tooltip.
  • AceGUI pool-bleed protectionRowList:Detach() orphans the row pool, header, and scrollbar to UIParent on host-widget release; a _detached guard on :Refresh() makes sure a recycled host doesn't trigger a pool grow that would re-attach rows into the next addon's widget. Wired via body:SetCallback("OnRelease", ...) in GUI/AddonLoadTab.lua.
  • .luarc.json updated — Added debugprofilestop to diagnostics.globals in both TOGTools/.luarc.json and !TOGT/.luarc.json so the new timer call resolves cleanly under the Lua LSP.

[v0.1.0] (2026-05-04) - Initial Release

New Features

  • Project scaffolding — TOC files for all WoW versions (Vanilla 11508, TBC 20505, Wrath 30405, Cata 40402, Mists 50503, Retail 110207/120001/120000), .pkgmeta with BigWigs packager config, .luarc.json (Lua 5.1 LSP), .markdownlint.json, and .github/workflows/release.yml for tag-triggered CurseForge releases.
  • Core addonTOGTools.lua: AceAddon-3.0 instance with AceConsole-3.0, AceEvent-3.0, AceHook-3.0 mixins; AceDB-3.0 schema (char scope); version detection flags (gv.isVanilla, gv.isTBC, gv.isWrath, gv.isCata, gv.isMists, gv.isClassic, gv.isRetail, gv.isRetail120Plus).
  • Slash commandsSlashCommands.lua: all /togt command registration and handling in a dedicated file, separate from core addon logic. Subcommands: /togt (toggle window), /togt np (Name Prefix tab), /togt settings (open settings), /togt vc (version check).
  • Tabbed main windowGUI/MainWindow.lua: dynamic AceGUI Frame + TabGroup; tabs auto-populate from addon.modules sorted alphabetically by label; per-tab locked/unlocked window sizing via WINDOW_SIZE; ESC-to-close proxy; position persistence via AceDB.
  • Name Prefix moduleModules/NamePrefix/NamePrefix.lua + GUI/NamePrefixTab.lua: wraps ChatEdit_SendText (Classic) or hooks EventRegistry (Retail 12.0+) to prepend a configurable nickname to outgoing chat messages. Settings: enable/disable, nickname, format string with live preview, per-channel toggles (Guild, Officer, Party, Raid, Instance, custom channel), skip-exclamation option, suppress-if-char-name option. Per-character DB scope. Skips messages beginning with / (slash commands/macros) unconditionally.
  • Minimap buttonGUI/MinimapButton.lua: LibDataBroker-1.1 launcher + LibDBIcon-1.0 registration. Left-click toggles the main window; right-click opens the native addon settings panel. Icon: textures/ToGTools_PH_MMB.tga. Button show/hide state and position persisted in DB.char.
  • Addon settings panelGUI/Settings.lua: AceConfig-3.0 options table registered under ESC → Interface → Addons → TOG Tools (native Blizzard panel). addon:OpenSettings() uses Settings.OpenToCategory (Classic Era 1.15+ / Retail 10+), InterfaceOptionsFrame_OpenToCategory (older builds), or InterfaceOptionsFrame:Show() as a final fallback — detected by API presence, not version flag.
  • VersionCheck-1.0 integrationVC:Enable(Ace) called on all non-Retail versions in OnInitialize; /togt vc broadcasts a guild-wide version check and prints responses after 21 seconds.
  • Embedded libslibs/LibDataBroker-1.1.lua, libs/LibDBIcon-1.0.lua (single-file embeds, copied from TOGProfessionMaster).
  • MIT LicenseLICENSE file added; referenced in .pkgmeta via license-output.
  • CurseForge project — Project ID 1533830 set in .pkgmeta and all TOC files.
  • Governance files.github/copilot-instructions.md and CLAUDE.md with full project rules (module pattern, commit/tag process, changelog format, HTML doc rules, no-tag rule).

Bug Fixes

  • Name Prefix not reaching other playershooksecurefunc fires after SendChatMessage has already been called. Fixed by wrapping ChatEdit_SendText directly (local orig = ...; ChatEdit_SendText = function(...) ... return orig(...) end) so the prefix is applied before the message is sent. Location: Modules/NamePrefix/NamePrefix.lua.
  • Macros/slash commands being prefixed — Added an unconditional early-return in ModifyMessage when the message starts with /. Location: Modules/NamePrefix/NamePrefix.lua.