131 lines
4.0 KiB
Lua
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
|