Files
ShaguTweaks-ClassicAPI/mods/chat-spam-filter.lua
T
2026-08-12 15:56:32 +02:00

131 lines
4.0 KiB
Lua

local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["Chat Spam Filter"],
description = T["Hides repeated messages in say/yell/channel chat (70s cooldown per unique message). Also suppresses BigWigs cast spam and #showtooltip errors."],
expansions = { ["vanilla"] = true },
category = T["Social & Chat"],
enabled = nil,
})
-- how long (in seconds) a given message text stays "known" and gets suppressed on repeat
local COOLDOWN = 70
-- how often (in seconds) the message cache gets pruned of stale entries
local CLEAN_INTERVAL = 30
-- double-buffered cache of [frame] = {message = last_seen_time}, keyed per
-- chat frame so that a message shown in one tab (e.g. "General") doesn't
-- get treated as a duplicate when it also needs to show in another tab
-- (e.g. a custom "World" tab)
local cache = { {}, {}, INDEX = 1 }
local function IsGuildMate(name)
if not name or not IsInGuild() then return false end
for i = 1, GetNumGuildMembers() do
local rname = GetGuildRosterInfo(i)
if rname and strlower(rname) == strlower(name) then
return true
end
end
return false
end
local function IsFriendOf(name)
if not name then return false end
for i = 1, GetNumFriends() do
local fname = GetFriendInfo(i)
if fname and strlower(fname) == strlower(name) then
return true
end
end
return false
end
-- returns true (and records the message) if this exact text was already seen
-- on this specific chat frame within the cooldown window
local function IsDuplicate(frame, msg)
local time = GetTime()
local index = cache.INDEX
local frameCache = cache[index][frame]
if not frameCache then
frameCache = {}
cache[index][frame] = frameCache
end
local seen_at = frameCache[msg]
if seen_at and (seen_at + COOLDOWN) > time then
return true
end
frameCache[msg] = time
return false
end
-- SuperWoW exposes GetUnitName (faster / more reliable than UnitName),
-- fall back to the vanilla API if it's not loaded
local GetPlayerName = GetUnitName or UnitName
module.enable = function(self)
-- force an early guild roster sync: GetNumGuildMembers() returns 0 until
-- the roster has been fetched at least once, which would otherwise let
-- guildmate emotes get filtered right after login/reload
if IsInGuild() then
GuildRoster()
end
-- periodically swap/prune the cache so it never grows forever
local cleaner = CreateFrame("Frame")
local elapsed_total = 0
cleaner:SetScript("OnUpdate", function()
elapsed_total = elapsed_total + arg1
if elapsed_total < CLEAN_INTERVAL then return end
elapsed_total = 0
local time = GetTime()
local index = cache.INDEX
local newindex = (index == 1) and 2 or 1
for frame, frameCache in pairs(cache[index]) do
for msg, seen_at in pairs(frameCache) do
if (seen_at + COOLDOWN) > time then
cache[newindex][frame] = cache[newindex][frame] or {}
cache[newindex][frame][msg] = seen_at
end
end
end
cache[index] = {}
cache.INDEX = newindex
end)
local Original_ChatFrame_OnEvent = ChatFrame_OnEvent
ChatFrame_OnEvent = function(event)
-- suppress "#showtooltip" macro errors leaking into chat
if strfind(arg1 or "", "^#showtooltip") then
return
end
-- suppress BigWigs "Casted X on Y" cast announcements
if event == "CHAT_MSG_SAY" and strfind(arg1 or "", "^Casted %u[%a%s]+ on %u[%a%s]+") then
return
end
-- hide repeated messages in say/yell/channel chat, and emotes from
-- strangers (guildmates/friends are never filtered on emote)
if arg1 and arg2 and arg2 ~= GetPlayerName("player") then
local isChatType = event == "CHAT_MSG_SAY" or event == "CHAT_MSG_CHANNEL" or event == "CHAT_MSG_YELL"
local isStrangerEmote = event == "CHAT_MSG_EMOTE" and not (IsGuildMate(arg2) or IsFriendOf(arg2))
if (isChatType or isStrangerEmote) and IsDuplicate(this, arg1) then
return
end
end
return Original_ChatFrame_OnEvent(event)
end
end