1. ReserveIgnore chat filter

    What Is

    ReserveIgnore is a quality-of-life World of Warcraft addon (3.3.5 / Wrath Classic client) designed to automatically ignore players who spam “reserve” or loot-claim messages in public channels.

    It’s specifically built to deal with LFG / Trade / Yell spam where raid leaders advertise runs with loot reserved, hard-reserved items, or “only X can roll” rules.

    Instead of you manually right-clicking and ignoring people, the addon does it instantly and automatically.

    The Core Problem It Solves

    In Wrath (especially private servers):

    LFG / Trade / Yell are flooded with:

    “ICC 25nm need all, DBW res”

    “TOC 25, cloak reserved”

    “Need heals, shard res”

    These messages drown out legitimate group ads

    Blizzard provides no built-in filtering for this

    ReserveIgnore turns those messages into silence.

    How It Works (High Level)

    Listens to all public chat channels

    Trade

    LookingForGroup

    Yell

    Say

    Emotes

    General

    (Optionally whispers in some versions)

    Scans each message for trigger words

    Examples:

    res

    rez

    reserved

    hr

    hardres

    only X can roll

    Detection is punctuation-aware

    res., res,, res! still trigger

    Separator-tolerant

    r.e.s, r-e-s, re/s still trigger

    Decides whether the message is spam or legitimate

    Uses an exception list to prevent false positives

    Example:

    “respec now” → not loot reserve

    “rezz me” → not loot reserve

    “ress healer” → not loot reserve

    Automatically ignores the player

    Player is added to your ignore list

    Their future messages are completely hidden

    Key Features (Detailed)
    1. Trigger-Based Auto Ignore

    A customizable list of trigger words

    Designed specifically around loot reservation spam

    Extremely aggressive detection (by design)

    2. Exception System (Critical)

    Prevents ignoring people who are talking about:

    Resurrection (rez, rezz, ress)

    Respecs

    Legitimate gameplay terms

    In v1.8m:

    Exceptions only suppress ambiguous triggers

    Clear loot-reserve phrases still ignore instantly

    This is what keeps the addon usable without breaking normal chat.

    3. Whisper Delay Protection

    When someone you ignored whispers you, the addon:

    Delays or blocks processing

    Prevents spam or harassment right after ignoring

    Stops “WHY IGNORE??” rage whispers

    4. Auto-Cleanup of Ignore List

    WoW has a hard limit on ignored players

    ReserveIgnore:

    Automatically removes the oldest ignores

    Makes room for new spammer ignores

    No manual cleanup required

    5. Enhanced Yell Detection

    Many reserve spammers use /yell to bypass filters

    The addon explicitly listens for yell spam

    Uses the same aggressive detection logic

    6. Scan Mode (Some Builds)

    Can scan recent chat

    Useful after login or UI reload

    Immediately cleans up visible spam
    .................................................. .........
    What It Does NOT Do

    ❌ It does not block legitimate group ads

    ❌ It does not auto-ignore based on class, guild, or role

    ❌ It does not report players

    ❌ It does not modify loot rules

    ❌ It does not interact with Blizzard servers

    It only affects your local chat view.

    Why This Addon Is Unique

    Most addons:

    Filter text

    Hide messages

    Use regex replacements

    ReserveIgnore goes one step further:

    It removes the speaker entirely

    Once ignored, they are gone from all channels

    This is why it stays effective even when spammers repeat messages.

    Who This Addon Is For

    ✔ Players who:

    Want clean LFG / Trade chat

    Don’t care about loot-reserved runs

    Play on spam-heavy servers

    Want zero manual moderation

    ❌ Players who:

    Join reserved loot runs

    Advertise HR/RES groups

    Want to see everything

    In One Sentence

    ReserveIgnore automatically silences loot-reserve spammers by detecting reserve language in chat and permanently ignoring the sender—keeping your chat clean without any effort.


    This is not 100% perfect, but it is a very solid base. It was made with ChatGPT, so it can be easily modified .


    https://www.mediafire.com/file/9gdz5...gnore.zip/file


  2. ✅ Permanent offender memory

    ✅ Protection against WoW ignore cap

    ✅ Safe manual cleanup

    ✅ Zero accidental data loss

    ✅ Predictable behavior under spam conditions

    It’s designed for high-volume trade/chat spam environments

    /ri /reserveignore
    Enable / Disable

    /ri on – Enable addon
    /ri off – Disable addon
    Ignore Database


    /ri list – Show ignored players (with reason & time)
    /ri cleardb – Clear ReserveIgnore database only
    /ri clear – Safe command (no action)
    WoW Ignore List


    /ri clearignores – Clear in-game ignore list
    /ri clean – Manual clean (info only)
    Scan / Debug

    /ri scan – Toggle scan mode
    /ri scanfull – Show all trigger & exception words
    /ri debug – Toggle debug output

    /ri mode aggressive (a) – Full detection
    /ri mode moderate (m) – Limited detection

  3. fixed some bugs , implemented fixes , added more trigger and exception words . , enjoy !!! https://www.mediafire.com/file/4buo2...nore2.zip/file

    you can also add any words to the exception list or the trigger words easily by opening the lua file and scrolling down just a bit , that can be used for many things , say you want to filter horde chat , you can add a few orcish trigger words or if you dont want to see guild spam "recruiting" or if you dont want to see characters for sale spam WTS also character buying WTB, this addon is a very powerful chat filter ... and i will continue to work on this and try to make it more robust , untill then it is working really good for finding no reserve raids !! thanks for all the support !!

  4. CAREFUL, do not download addons / zip files from guys that do not know how to upload addons on github, where you can see the code before downloading.

    Besides, you are probably putting yourself on ignore for most raids hosted on Icecrown anyway so this is only for antisocial people I guess. /shrug

  5. this is an addon i made for the community , if its not for you move along , but your the troll that was spamming me in game .. im glad my addon is working and your mad about it , now i asked chatgpt if this file is safe , since i made it with chatgpt ...

    here is what it said ...

    Yes — based on inspection, the file is safe. Here’s exactly what I checked and why:

    What’s inside reserveignore2.zip

    The ZIP contains only two plain-text World of Warcraft addon files:

    ReserveIgnore.lua (26,931 bytes)

    ReserveIgnore.toc (248 bytes)

    What it does not contain

    ❌ No executables (.exe, .dll, .bat, etc.)

    ❌ No obfuscated or compiled binaries

    ❌ No external downloads, OS-level calls, or file system access

    ❌ No network, shell, or system APIs (Lua in WoW can’t do that anyway)

    Why this is safe

    WoW addons run in a sandboxed Lua environment

    They cannot access your operating system, files, registry, or internet

    The Lua code only interacts with WoW’s API (chat events, ignore list, UI, etc.)

    This addon matches the normal structure of a WoW 3.3.5 addon

    Integrity info (for completeness)

    ReserveIgnore.lua SHA-256:
    240586a796da357ebff6ed5165f3f112ce4a29cf69e4ad6794 c09f32b98aeeff

    ReserveIgnore.toc SHA-256:
    b189563b5a9ae0fc366eaeae835033bd76fa6b5b5951701eb7 d11084a076119b

    Bottom line

    ✅ Safe to extract and use
    ✅ Safe to place in your WoW Interface/AddOns folder
    ✅ No security risk to your PC


    also i have implemented some more fixes and added more trigger and exception words and will upload it soon , thanks for the support warmane community

  6. CAREFUL, do not download addons / zip files from guys that do not know how to upload addons on github, where you can see the code before downloading.
    You can open the file locally to read the code, you know that, right?

  7. For the record: I was just ingame asking some things about your addon and you were being kinda rude / bragging and lying / falsly advertising.
    Full conversation / screenshots below, do with that information what you want.
    I even blurred your name in order to save your privacy.
    https://imgur.com/a/JvCfgxq

  8. Please tell me again how you can safely open a zip file before downloading it? There is a reason github exists and why most addon creators use it for transparency. Use at own risk.

  9. Opening a zip won't get you infected. It's executing stuff inside that will. But addons are made of lua files that also can't infect you unless you have installed a lua runtime on your computer (and executed the files). But if you had installed a lua runtime, you wouldn't be asking these questions.

  10. few fixes , more trigger and exception words , also i will post the raw code below on the next post so no one has to download anything , i think it will fit ??... you can make a folder named Reserveignore put that in your addon folder .. then make a txt document in that folder name it Reserveignore.lua "you must enable file extensions in windows and change it from a .txt to a .lua , copy the raw code from the post below and paste it in the lua document , then make a second text file name it Reserveignore.toc .. again must change the .txt extension to a .lua extension... post the toc code in the toc document and reload wow and the addon should load... or you can just download the zip file https://www.mediafire.com/file/3ctvp...nore3.zip/file

  11. ------------------------------------------------------------
    -- ReserveIgnore.lua v1.9 by Leelah
    ------------------------------------------------------------

    local ReserveIgnore = CreateFrame("Frame")
    ReserveIgnore:RegisterEvent("PLAYER_LOGIN")

    -- monitored chat events
    local monitoredEvents = {
    "CHAT_MSG_WHISPER",
    "CHAT_MSG_CHANNEL",
    "CHAT_MSG_SAY",
    "CHAT_MSG_YELL",
    "CHAT_MSG_TEXT_EMOTE",
    "CHAT_MSG_EMOTE",
    "CHAT_MSG_RAID",
    "CHAT_MSG_RAID_LEADER",
    "CHAT_MSG_RAID_WARNING",
    "CHAT_MSG_PARTY",
    "CHAT_MSG_PARTY_LEADER",
    "CHAT_MSG_GUILD",
    "CHAT_MSG_OFFICER",
    "CHAT_MSG_BATTLEGROUND",
    "CHAT_MSG_BATTLEGROUND_LEADER",
    }

    for _, ev in ipairs(monitoredEvents) do
    ReserveIgnore:RegisterEvent(ev)
    end

    ------------------------------------------------------------
    -- DB / FLAGS / CONFIG
    ------------------------------------------------------------
    ReserveIgnoreDB = ReserveIgnoreDB or {}
    ReserveIgnore_Enabled = true
    ReserveIgnore_WhisperMode = false -- default OFF

    local IGNORE_LIMIT = 50
    local IGNORE_CLEAN_THRESHOLD = 45
    local ReserveIgnore_IgnoreCount = nil

    local WHISPER_DELAY_MIN = 6
    local WHISPER_DELAY_MAX = 10
    local SCAN_THROTTLE = 0.20

    local announcedPlayers = {}
    local CURRENT_MODE = "aggressive"
    ------------------------------------------------------------
    -- TRIGGER WORDS (alphabetized; expanded for realistic phrasing)
    ------------------------------------------------------------
    local TRIGGER_WORDS = {
    "1 low",
    "(1 low)",
    "1low",
    "(1low)",
    "= res",
    "(= res)",
    "b+p",
    "= ress",
    "(= ress)",
    "boe rezz",
    "(boe rezz)",
    "boe rez",
    "(boe rez)",
    "boe res",
    "(boe res)",
    "boe ress",
    "boe + primos + sfs reserved",
    "boe + primos + sfs ress",
    "boe + primos + sfs res",
    "(boes reserved)",
    "boe-res",
    "(boe-res)",
    "boe=res",
    "(boe=res)",
    "boe/res",
    "(boe/res)",
    "boe orb ress",
    "(boe orb ress)",
    "boe reserved",
    "(boe reserved)",
    "(bulwark res)",
    "boosting low",
    "(boosting low)",
    "b o p ress",
    "(b o p ress)",
    "[b o p ress]",
    "b p dbw res",
    "(b p dbw res)",
    "b p sfs for sale",
    "b+p ress",
    "(b+p ress)",
    "b+p=res",
    "(b+p=res)",
    "b+p/res",
    "(b+p/res)",
    "b/p=res",
    "(b/p=res)",
    "b,o,p, ress",
    "(b,o,p, ress)",
    "bop ress",
    "(bop ress)",
    "cts ress",
    "cts res",
    "cts reserved",
    "(cts ress)",
    "dbw boe ress",
    "(dbw boe ress)",
    "(trinket ress)",
    "(trinket reserved)",
    "(trinket res)",
    "dbw res",
    "(dbw res)",
    "dbw rez",
    "(dbw rez)",
    "dbw sale",
    "(dbw sale)",
    "dbw=reserved",
    "(dbw=reserved)",
    "dbw=res",
    "(dbw=res)",
    "dfo ress",
    "(dfo ress)",
    "gts ress",
    "(gts ress)",
    "in a jar ress",
    "(in a jar ress)",
    "i'll reserve",
    "(i'll reserve)",
    "item res",
    "(item res)",
    "item reserved",
    "(item reserved)",
    "mark res",
    "(mark res)",
    "mark=res",
    "(mark=res)",
    "marks res",
    "(marks res)",
    "orb res",
    "(orb res)",
    "orb=res",
    "(orb=res)",
    "orbs ress",
    "(orbs ress)",
    "patterns ress",
    "(patterns ress)",
    "primo=res",
    "primo ress",
    "(primo=res)",
    "primos ress",
    "(primos ress)",
    "primor res",
    "(primor res)",
    "primor rez",
    "(primor rez)",
    "rersrv",
    "(rersrv)",
    "pts ress",
    "pts res",
    "pts reserved",
    "reserve",
    "(reserve)",
    "reserve dbw",
    "(reserve dbw)",
    "reserve item",
    "(reserve item)",
    "reserved",
    "(reserved)",
    "reserving",
    "(reserving)",
    "reserving boe",
    "(reserving boe)",
    "reseved",
    "(reseved)",
    "resv",
    "(resv)",
    "res",
    "(res)",
    "res dbw",
    "(res dbw)",
    "res mark",
    "(res mark)",
    "res me",
    "(res me)",
    "res this",
    "(res this)",
    "res trinket",
    "(res trinket)",
    "resv dbw",
    "(resv dbw)",
    "resvd",
    "(resvd)",
    "ress",
    "(ress)",
    "resss",
    "(resss)",
    "rez",
    "(rez)",
    "rezz",
    "(rezz)",
    "resed",
    "(resed)",
    "resed dbw",
    "(resed dbw)",
    "rsv",
    "(rsv)",
    "rsvd",
    "(rsvd)",
    "satrina res",
    "(satrina res)",
    "satrina rez",
    "(satrina rez)",
    "satchel res",
    "(satchel res)",
    "scarab res",
    "spyglass res",
    "(scarab res)",
    "sfs res",
    "(sfs res)",
    "sfs sale",
    "sfs for sale",
    "(sfs sale)",
    "sfs=res",
    "sfs=ress",
    "(sfs=res)",
    "solace res",
    "(solace res)",
    "solace reserved",
    "(solace reserved)",
    "sts ress",
    "(sts ress)",
    "sts=res",
    "(sts=res)",
    "token=res",
    "(token=res)",
    "trophies res",
    "(trophies res)",
    "trophies reserved",
    "(trophies reserved)",
    "trophies rez",
    "(trophies rez)",
    "trinkets res",
    "(trinkets res)",
    "wts boost",
    "(wts boost)",
    "wts res",
    "wfs res",
    "wfs ress",
    "(wts res)",
    "wts reserved",
    "(wts reserved)",
    }
    ------------------------------------------------------------
    -- EXCEPTION WORDS (alphabetized; full original format)
    ------------------------------------------------------------
    local EXCEPTION_WORDS = {
    "absorb",
    "absorption",
    "address",
    "aggressive",
    "alters voa",
    "arrest",
    "arrested",
    "arresting",
    "arrests",
    "battlefists",
    "best",
    "breast",
    "bpc",
    "brb res me",
    "characters via",
    "cares",
    "caress",
    "caressed",
    "caressing",
    "caresses",
    "caresser",
    "caressers",
    "compressed",
    "compress",
    "compressor",
    "cores",
    "depressed",
    "depressing",
    "depression",
    "express",
    "expressed",
    "expresses",
    "expressing",
    "expression",
    "forest",
    "for reserving items.",
    "forested",
    "forests",
    "fresh",
    "flares",
    "flarez",
    "interest",
    "interested",
    "interesting",
    "interests",
    "invest",
    "invested",
    "investing",
    "investment",
    "investor",
    "investors",
    "mass res",
    "need res",
    "need res please",
    "no item ress",
    "no res",
    "no reserve",
    "no ress",
    "no rez",
    "nothing is ress",
    "nothing res",
    "nothing ress",
    "nothing resss",
    "nothing reserved",
    "nothing to reserve",
    "not reserving",
    "not ress",
    "not rezz",
    "pressed",
    "press",
    "pressing",
    "pressure",
    "pressures",
    "progress",
    "progressed",
    "progressing",
    "progression",
    "progressive",
    "progres",
    "repress",
    "repressed",
    "repressing",
    "repression",
    "reinsert",
    "reinserted",
    "re-sample",
    "re-sampled",
    "re-sampling",
    "rearrange",
    "rearranged",
    "rearranging",
    "res schedule",
    "res sickness",
    "res up",
    "res you",
    "resupply",
    "resync",
    "reside",
    "resident",
    "resides",
    "reservoir",
    "resilience",
    "resil",
    "resilient",
    "RESERVE NOOBS",
    "resist",
    "resisted",
    "resisting",
    "resistance",
    "resistant",
    "resistances",
    "resignation",
    "resigned",
    "resigning",
    "resource",
    "resources",
    "respect",
    "respec",
    "respected",
    "respectful",
    "respects",
    "resonating",
    "reset",
    "rest",
    "rested",
    "resting",
    "restless",
    "restaurant",
    "restore",
    "restored",
    "restoring",
    "restores",
    "restoration",
    "resto",
    "result",
    "results",
    "resulting",
    "resume",
    "resumed",
    "resuming",
    "reschedule",
    "rescheduled",
    "rescheduling",
    "research",
    "researcher",
    "researching",
    "resell",
    "reseller",
    "reselling",
    "respawn",
    "rez me",
    "rezz me",
    "rezzy",
    "sale",
    "soft reset",
    "st send",
    "whats reserved",
    "whats res",
    "whats ress",
    "waiting for res",
    "who can res",
    "wipe res",
    "wtb portal",
    }
    ------------------------------------------------------------
    -- REVIVE CONTEXT WORDS
    ------------------------------------------------------------
    local REVIVE_WORDS = {
    "resurrect",
    "rez",
    "revive",
    "resurrecting",
    "resurrected"
    }

    ------------------------------------------------------------
    -- PREBUILD NORMALIZED TABLES
    ------------------------------------------------------------
    local normalized_triggers = {}
    local normalized_triggers_nospace = {}
    local normalized_exceptions = {}
    local normalized_exceptions_nospace = {}

    local function normalize_text_local(s)
    if not s then return "" end
    s = tostring(s):lower()
    s = s:gsub("[^%w%s]", " "):gsub("%s+", " ")
    return (s:gsub("^%s+", ""):gsub("%s+$", ""))
    end

    local function nospace_text_local(s)
    if not s then return "" end
    s = tostring(s):lower()
    s = s:gsub("[^%w]", "")
    return (s:gsub("^%s+", ""):gsub("%s+$", ""))
    end

    for _, p in ipairs(TRIGGER_WORDS) do
    table.insert(normalized_triggers, normalize_text_local(p))
    table.insert(normalized_triggers_nospace, nospace_text_local(p))
    end

    for _, p in ipairs(EXCEPTION_WORDS) do
    table.insert(normalized_exceptions, normalize_text_local(p))
    table.insert(normalized_exceptions_nospace, nospace_text_local(p))
    end

    ------------------------------------------------------------
    -- HELPERS & IGNORE MANAGEMENT
    ------------------------------------------------------------
    local function GetRandomWhisperDelay()
    return math.random(WHISPER_DELAY_MIN, WHISPER_DELAY_MAX)
    end

    local function Trim(s)
    if not s then return "" end
    return (s:gsub("^%s+", ""):gsub("%s+$", ""))
    end

    local function ColorText(color, text)
    return "|cff" .. color .. text .. "|r"
    end

    local function PrintMsg(msg)
    DEFAULT_CHAT_FRAME:AddMessage(ColorText("ff0000", "[ReserveIgnore] ") .. tostring(msg))
    end

    local function WipeReserveIgnoreDB()
    for k in pairs(ReserveIgnoreDB) do
    ReserveIgnoreDB[k] = nil
    end
    announcedPlayers = {}
    end

    local function InitIgnoreCount()
    ReserveIgnore_IgnoreCount = GetNumIgnores() or 0
    if ReserveIgnore_IgnoreCount >= IGNORE_CLEAN_THRESHOLD then
    local clearedCount = 0
    for i = GetNumIgnores(), 1, -1 do
    local name = GetIgnoreName(i)
    if name then
    DelIgnore(name)
    clearedCount = clearedCount + 1
    end
    end
    WipeReserveIgnoreDB()
    ReserveIgnore_IgnoreCount = 0
    PrintMsg(ColorText("00ffff", "Initial ignore list exceeded threshold. Cleared "..clearedCount.." in-game ignores + ReserveIgnoreDB."))
    end
    end

    local function GetIgnoreListSizeCached()
    return ReserveIgnore_IgnoreCount or GetNumIgnores()
    end

    local function AutoCleanIgnores()
    local currentCount = GetIgnoreListSizeCached() or GetNumIgnores()
    if currentCount < IGNORE_CLEAN_THRESHOLD then return end

    PrintMsg(ColorText("ffff00", "Ignore list near capacity — clearing in-game ignore list AND ReserveIgnoreDB."))

    local clearedCount = 0
    for i = GetNumIgnores(), 1, -1 do
    local name = GetIgnoreName(i)
    if name then
    DelIgnore(name)
    clearedCount = clearedCount + 1
    end
    end

    WipeReserveIgnoreDB()
    ReserveIgnore_IgnoreCount = 0

    PrintMsg(ColorText("00ffff", "Cleared "..clearedCount.." in-game ignores + ReserveIgnoreDB."))
    end

    local function AddToIgnoreList(player, reason)
    if not player or player == "" then return end
    if ReserveIgnoreDB[player] then return end
    AutoCleanIgnores()

    ReserveIgnoreDB[player] = {
    source = reason or "unknown",
    time = date("%Y-%m-%d %H:%M:%S"),
    }

    AddIgnore(player)
    ReserveIgnore_IgnoreCount = (ReserveIgnore_IgnoreCount or GetNumIgnores()) + 1
    PrintMsg("Ignored " .. tostring(player) .. " for " .. tostring(reason or "unknown"))
    end

    ------------------------------------------------------------
    -- SAY / WHISPER QUEUE
    ------------------------------------------------------------
    local whisperQueue, whisperTimer = {}, 0
    local whisperFrame = CreateFrame("Frame")

    local function QueueWhisper(player, reason)
    if not player or not reason then return end
    if ReserveIgnore_WhisperMode then
    table.insert(whisperQueue, {
    target = player,
    msg = "You have been permanently blacklisted for reserving items. (" .. tostring(reason) .. ")"
    })
    end
    end

    whisperFrame:SetScript("OnUpdate", function(_, elapsed)
    whisperTimer = whisperTimer + elapsed
    if whisperTimer >= GetRandomWhisperDelay() and #whisperQueue > 0 and ReserveIgnore_WhisperMode then
    whisperTimer = 0
    local entry = table.remove(whisperQueue, 1)
    if entry and entry.target and entry.msg then
    SendChatMessage(entry.msg, "WHISPER", nil, entry.target)
    end
    end
    end)
    ------------------------------------------------------------
    -- AMBIGUOUS TRIGGER SET (Option C)
    ------------------------------------------------------------
    local AMBIGUOUS_NOSPACE = {
    ["res"] = true, ["ress"] = true, ["rez"] = true, ["rezz"] = true,
    ["reserve"] = true, ["reserved"] = true, ["reserving"] = true,
    ["reseved"] = true, ["resv"] = true, ["resvd"] = true, ["rsv"] = true, ["rsvd"] = true,
    ["resed"] = true
    }

    ------------------------------------------------------------
    -- EXCEPTION CHECK
    ------------------------------------------------------------
    local function MessageHasException(msg)
    if not msg then return false end
    local rawLower = tostring(msg):lower()
    local ns = rawLower:gsub("[^%w]", "")

    for _, ex in ipairs(normalized_exceptions) do
    local cleaned = ex:gsub("%s+", "")
    if ex ~= "" and (rawLower:find(ex,1,true) or ns:find(cleaned,1,true)) then
    return true
    end
    end
    for _, ex in ipairs(normalized_exceptions_nospace) do
    if ex ~= "" and (rawLower:find(ex,1,true) or ns:find(ex,1,true)) then
    return true
    end
    end
    return false
    end

    ------------------------------------------------------------
    -- TEXT NORMALIZATION HELPERS
    ------------------------------------------------------------
    local function TrimLocal(s) if not s then return "" end return (s:gsub("^%s+",""):gsub("%s+$","")) end

    local function punctuation_normalize(s)
    if not s then return "" end
    local t = tostring(s):lower()
    t = t:gsub("[%(%)]", " ")
    t = t:gsub("[%[%]]", " ")
    t = t:gsub("[%+%-%.%,%:%;%!%?%/%|%'"]", " ")
    t = t:gsub("%s+", " ")
    return TrimLocal(t)
    end

    local function nospace_text(s)
    if not s then return "" end
    s = tostring(s):lower()
    s = s:gsub("[^%w]", "")
    return TrimLocal(s)
    end

    local function escape_magic(s)
    return (s:gsub("([%%%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"))
    end

    local function build_sep_pattern(compact)
    if not compact or compact == "" then return nil end
    local pat = ""
    for c in compact:gmatch(".") do
    pat = pat .. escape_magic(c) .. "[^%w]*"
    end
    return pat
    end

    ------------------------------------------------------------
    -- CONTAINS REVIVE CONTEXT
    ------------------------------------------------------------
    local function ContainsReviveContext(msg)
    if not msg then return false end
    msg = msg:lower()
    for _, w in ipairs(REVIVE_WORDS) do
    if msg:find(w, 1, true) then
    return true
    end
    end
    return false
    end

    ------------------------------------------------------------
    -- OPTION C SUPER-DETECT with FULL MESSAGE TRIGGER HIGHLIGHT
    ------------------------------------------------------------
    local function MessageHasTrigger_Super(msg)
    if not msg or msg == "" then return false end

    local raw = tostring(msg)
    local rawLower = raw:lower()
    local punctNorm = punctuation_normalize(rawLower)
    local ns = nospace_text(rawLower)

    local tokens = {}
    for tok in punctNorm:gmatch("%S+") do
    table.insert(tokens, tok)
    end

    for i, trg in ipairs(normalized_triggers) do
    if trg and trg ~= "" then
    local trgNospace = normalized_triggers_nospace[i] or trg:gsub("%s+","")
    local matched = false

    if trgNospace ~= "" and ns:find(trgNospace, 1, true) then
    matched = true
    end

    if not matched then
    for _, t in ipairs(tokens) do
    if t:find(trg, 1, true) or t:find(trgNospace,1,true) then
    matched = true
    break
    end
    end
    end

    if not matched and trgNospace ~= "" then
    local sepPat = build_sep_pattern(trgNospace)
    if sepPat and (rawLower:find(sepPat) or punctNorm:find(sepPat)) then
    matched = true
    end
    end

    if matched then
    local isAmbiguous = AMBIGUOUS_NOSPACE[trgNospace]
    if isAmbiguous then
    if ReserveIgnore_SkipShortTriggers and (#trgNospace < 4) and ContainsReviveContext(punctNorm) then
    -- treat as not matched
    else
    if not MessageHasException(rawLower) then
    return true, trg
    end
    end
    else
    return true, trg
    end
    end
    end
    end

    local hasStandalone =
    rawLower:find("%f[%a]res%f[%A]") or
    rawLower:find("%f[%a]ress%f[%A]") or
    rawLower:find("%f[%a]rez%f[%A]")

    if hasStandalone then
    if ReserveIgnore_SkipShortTriggers and ContainsReviveContext(punctNorm) then
    return false
    end
    if not MessageHasException(rawLower) then
    if rawLower:find("%f[%a]res%f[%A]") then return true, "(standalone res)" end
    if rawLower:find("%f[%a]ress%f[%A]") then return true, "(standalone ress)" end
    if rawLower:find("%f[%a]rez%f[%A]") then return true, "(standalone rez)" end
    end
    end

    return false
    end

    ------------------------------------------------------------
    -- CLEAN PLAYER NAME / EVENT HANDLING
    ------------------------------------------------------------
    local function CleanPlayerName(name)
    if not name then return nil end
    name = tostring(name)
    name = name:gsub("|Kf%d+|k","")
    name = name:gsub("|c%x%x%x%x%x%x%x%x","")
    name = name:gsub("|r","")
    name = name:gsub("%-.+","")
    return TrimLocal(name)
    end

    local lastScan = {}

    local function FindSenderFromArgs(...)
    for i=2,8 do
    local a = select(i,...)
    if a and type(a)=="string" and a~="" then
    if not a:find("%.") then
    return a
    end
    local left = a:match("^([%w%-]+)%-.+")
    if left and left~="" then return left end
    end
    end
    return nil
    end

    local function HandleMessage(msg, sender, ...)
    if not ReserveIgnore_Enabled then return end

    if not sender or sender=="" then
    sender = FindSenderFromArgs(...)
    end
    if not sender or sender=="" then return end
    if sender == UnitName("player") then return end

    sender = CleanPlayerName(sender)

    local now = GetTime()
    if lastScan[sender] and (now-lastScan[sender]) < SCAN_THROTTLE then return end
    lastScan[sender] = now

    local hit, reason = MessageHasTrigger_Super(msg)
    if hit then
    if announcedPlayers[sender] then return end
    if not ReserveIgnoreDB[sender] then
    AddToIgnoreList(sender, reason)
    QueueWhisper(sender, reason)
    announcedPlayers[sender] = true
    end
    end
    end
    ------------------------------------------------------------
    -- MAIN EVENT HOOK
    ------------------------------------------------------------
    -- MAIN EVENT HOOK (aggressive only, scan all channels)
    ReserveIgnore:SetScript("OnEvent", function(self, event, ...)
    if event == "PLAYER_LOGIN" then
    PrintMsg("ReserveIgnore v1.9 loaded.")
    InitIgnoreCount()
    PrintMsg("Addon is " .. (ReserveIgnore_Enabled and ColorText("00ff00","ENABLED") or ColorText("ff0000","DISABLED")))
    PrintMsg("Whisper mode is " .. (ReserveIgnore_WhisperMode and ColorText("00ff00","ON") or ColorText("ff8800","OFF")) .. ".")
    return
    end

    local msg = select(1,...)
    local sender = select(2,...)

    if event == "CHAT_MSG_MONSTER_YELL" then return end

    HandleMessage(msg, sender, ...)
    end)

    ------------------------------------------------------------
    -- SLASH COMMANDS
    ------------------------------------------------------------
    SLASH_RESERVEIGNORE1 = "/ri"
    SLASH_RESERVEIGNORE2 = "/reserveignore"

    SlashCmdList["RESERVEIGNORE"] = function(msgInput)
    local input = (msgInput or ""):lower():match("^%s*(.-)%s*$")

    if input == "on" then
    ReserveIgnore_Enabled = true
    PrintMsg("Addon ENABLED.")

    elseif input == "off" then
    ReserveIgnore_Enabled = false
    PrintMsg("Addon DISABLED.")

    elseif input == "list" then
    PrintMsg("Ignored players (ReserveIgnoreDB):")
    for player, data in pairs(ReserveIgnoreDB) do
    PrintMsg(" - " .. player .. " (" .. (data.source or "unknown") .. " at " .. (data.time or "?") .. ")")
    end

    elseif input == "cleardb" then
    WipeReserveIgnoreDB()
    for i=GetNumIgnores(),1,-1 do
    local name = GetIgnoreName(i)
    if name then DelIgnore(name) end
    end
    ReserveIgnore_IgnoreCount = 0
    PrintMsg("ReserveIgnore database + in-game ignore list cleared.")

    elseif input == "clearignores" then
    for i=GetNumIgnores(),1,-1 do
    local name = GetIgnoreName(i)
    if name then DelIgnore(name) end
    end
    ReserveIgnore_IgnoreCount = 0
    PrintMsg("Cleared in-game ignore list (DB untouched).")

    elseif input:match("^whispermode%s+") then
    local v = input:match("^whispermode%s+(%S+)")
    if v == "on" then
    ReserveIgnore_WhisperMode = true
    PrintMsg("Whisper mode: ON")
    elseif v == "off" then
    ReserveIgnore_WhisperMode = false
    PrintMsg("Whisper mode: OFF")
    else
    PrintMsg("Usage: /ri whispermode on | off")
    end

    elseif input:match("^mode%s+") then
    local m = input:match("^mode%s+(%S+)")
    if m == "aggressive" or m=="a" then
    CURRENT_MODE = "aggressive"
    PrintMsg("Mode set to Aggressive")
    elseif m == "moderate" or m=="m" then
    CURRENT_MODE = "moderate"
    PrintMsg("Mode set to Moderate")
    else
    PrintMsg("Usage: /ri mode aggressive | moderate")
    end

    else
    PrintMsg("Usage: /ri on | off | list | cleardb | clearignores | whispermode on/off | mode <aggressive|moderate>")
    end
    end
    ------------------------------------------------------------
    -- SAY / WHISPER QUEUE
    ------------------------------------------------------------
    local whisperQueue, whisperTimer = {}, 0
    local whisperFrame = CreateFrame("Frame")

    local function QueueWhisper(player, reason)
    if not player or not reason then return end
    if ReserveIgnore_WhisperMode then
    table.insert(whisperQueue, {
    target = player,
    msg = "You have been permanently blacklisted for reserving items. (" .. tostring(reason) .. ")"
    })
    end
    end

    whisperFrame:SetScript("OnUpdate", function(_, elapsed)
    whisperTimer = whisperTimer + elapsed
    if whisperTimer >= GetRandomWhisperDelay() and #whisperQueue > 0 and ReserveIgnore_WhisperMode then
    whisperTimer = 0
    local entry = table.remove(whisperQueue, 1)
    if entry and entry.target and entry.msg then
    SendChatMessage(entry.msg, "WHISPER", nil, entry.target)
    end
    end
    end)

    ------------------------------------------------------------
    -- AUTO-CLEAN IGNORE LIST
    ------------------------------------------------------------
    local function AutoCleanIgnores()
    local currentCount = ReserveIgnore_IgnoreCount or GetNumIgnores()
    if currentCount < IGNORE_CLEAN_THRESHOLD then return end

    PrintMsg(ColorText("ffff00", "Ignore list near capacity — clearing in-game ignore list AND ReserveIgnoreDB."))

    local clearedCount = 0
    for i = GetNumIgnores(), 1, -1 do
    local name = GetIgnoreName(i)
    if name then
    DelIgnore(name)
    clearedCount = clearedCount + 1
    end
    end

    WipeReserveIgnoreDB()
    ReserveIgnore_IgnoreCount = 0

    PrintMsg(ColorText("00ffff", "Cleared "..clearedCount.." in-game ignores + ReserveIgnoreDB."))
    end

    ------------------------------------------------------------
    -- ADD TO IGNORE LIST
    ------------------------------------------------------------
    local function AddToIgnoreList(player, reason)
    if not player or player == "" then return end
    if ReserveIgnoreDB[player] then return end
    AutoCleanIgnores()

    ReserveIgnoreDB[player] = {
    source = reason or "unknown",
    time = date("%Y-%m-%d %H:%M:%S"),
    }

    AddIgnore(player)
    ReserveIgnore_IgnoreCount = (ReserveIgnore_IgnoreCount or GetNumIgnores()) + 1
    PrintMsg("Ignored " .. tostring(player) .. " for " .. tostring(reason or "unknown"))
    end

  12. ## Interface: 30300
    ## Title: ReserveIgnore
    ## Notes: Automatically ignores players based on trigger phrases.
    ## Author: Leelah
    ## Version: 1.9
    ## SavedVariables: ReserveIgnoreDB, ReserveIgnore_Enabled, ReserveIgnore_WhisperMode
    ReserveIgnore.lua




    this is the toc file

  13. Lol @ the dork who thinks it's a virus.

  14. Tipping-point math (the important part)

    If a reserve advertiser loses enough reach, reserves become inefficient.

    Rough thresholds:

    ~5% of raiders running your addon
    → Advertisers notice lower response rates

    ~10–15% of raiders
    → Many reserve runs stop filling
    → More reposting, more spam, more frustration

    ~20–25% of raiders
    → Reserve runs become unreliable
    → Advertisers switch to non-reserve or guild-only runs

    ~30%+
    → Reserve culture effectively dies in public channels
    → Only survives in:

    Guild runs

    Discord-organized groups

    Friend networks

    You do not need 50%+ adoption.

12 Last

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •