if not WeakAurasSaved.Cyka then WeakAurasSaved.Cyka = {} end if not WeakAurasSaved.Cyka.WhoSniffer then WeakAurasSaved.Cyka.WhoSniffer = {} end aura_env.ignored = { "Maritza", "Goodbones" } ---@class aura_env ---@field raceMap table ---@field stinkies table ---@field classColors table ---@field whoQueries table ---@field ttl number ---@field messageQueue Message[] ---@field UpdateMacro fun() ---@field Notify fun(Player) ---@field NotifyGone fun(Player) ---@field queryPending boolean ---@field lastQuery WHOQuery ---@param input string ---@return number local function utf8len(input) if not input then return 0 end local len = 0 local i = 1 local n = #input while i <= n do local c = input:byte(i) if c >= 0 and c <= 127 then i = i + 1 elseif c >= 194 and c <= 223 then i = i + 2 elseif c >= 224 and c <= 239 then i = i + 3 elseif c >= 240 and c <= 244 then i = i + 4 else i = i + 1 end len = len + 1 end return len end ---@param input string ---@param targetLength number ---@param left boolean ---@return string local function padString(input, targetLength, left) left = left or false local len = utf8len(input) if len < targetLength then if left then input = input .. string.rep(" ", targetLength - len) else input = string.rep(" ", targetLength - len) .. input end end return input end ---@class WHOQuery ---@field query string ---@field filters WHOFilter[] WHOQuery = { ---@param query string ---@param filters WHOFilter[] ---@return WHOQuery new = function(query, filters) local self = setmetatable({}, { __index = WHOQuery, }) self.query = query self.filters = filters return self end, } ---@alias WHOFilter fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean ---@type WHOFilter local NotSiegeOfOrgrimmarFilter = function(name, guild, level, race, class, zone) if not zone then return false end return zone ~= "Siege of Orgrimmar" end ---@type WHOFilter local AllianceFilter = function(name, guild, level, race, class, zone) if not race then return false end if not aura_env.raceMap[race] then return false end return aura_env.raceMap[race] == "Alliance" end ---@class Message ---@field message string ---@field channel string ---@field data string if not WeakAurasSaved.Cyka.MessageQueue then WeakAurasSaved.Cyka.MessageQueue = {} end aura_env.whoQueryIdx = 1 ---@type table aura_env.whoQueries = {} aura_env.whoQueries[1] = WHOQuery.new('g-"БеспредеЛ"', {}) aura_env.whoQueries[2] = WHOQuery.new( 'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Human" r-"Dwarf" r-"Night Elf"', { NotSiegeOfOrgrimmarFilter, AllianceFilter } ) aura_env.whoQueries[3] = WHOQuery.new( 'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Gnome" r-"Draenei" r-"Worgen"', { NotSiegeOfOrgrimmarFilter, AllianceFilter } ) aura_env.whoQueries[4] = WHOQuery.new( 'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Kul Tiran" r-"Dark Iron Dwarf" r-"Void Elf"', { NotSiegeOfOrgrimmarFilter, AllianceFilter } ) aura_env.whoQueries[5] = WHOQuery.new( 'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Lightforged Draenei" r-"Mechagnome"', { NotSiegeOfOrgrimmarFilter, AllianceFilter } ) aura_env.whoQueries[6] = WHOQuery.new("Kekv Demonboo Dotmada Firobot Verminal", {}) aura_env.queryPending = false aura_env.ttl = #aura_env.whoQueries * 2 aura_env.lastQuery = nil ---@type table aura_env.raceMap = { ["Orc"] = "Horde", ["Undead"] = "Horde", ["Tauren"] = "Horde", ["Troll"] = "Horde", ["Blood Elf"] = "Horde", ["Goblin"] = "Horde", ["Human"] = "Alliance", ["Dwarf"] = "Alliance", ["Night Elf"] = "Alliance", ["Gnome"] = "Alliance", ["Draenei"] = "Alliance", ["Worgen"] = "Alliance", ["Vulpera"] = "Horde", ["Nightborne"] = "Horde", ["Zandalari Troll"] = "Horde", ["Kul Tiran"] = "Alliance", ["Dark Iron Dwarf"] = "Alliance", ["Void Elf"] = "Alliance", ["Lightforged Draenei"] = "Alliance", ["Mechagnome"] = "Alliance", ["Mag'har Orc"] = "Horde", } ---@type table aura_env.stinkies = {} ---@class Player ---@field name string ---@field guild string ---@field race string ---@field class string ---@field zone string ---@field lastSeenInternal number ---@field lastSeen string ---@field firstSeen string ---@field seenCount number Player = { ---@param name string ---@param guild string ---@param race string ---@param class string ---@param zone string ---@return Player new = function(name, guild, race, class, zone) local self = setmetatable({}, { __index = Player, }) self.name = name self.guild = guild self.race = race self.class = class self.zone = zone self.lastSeenInternal = GetTime() self.lastSeen = "never" self.firstSeen = "never" self.seenCount = 0 return self end, ToString = function(self) local out = string.format( "%s %s %s\nFirst: %s Last: %s Seen: %3d", padString(self.name, 16, true), padString(self.guild, 26, false), padString(self.zone, 26, false), padString(self.firstSeen, 10, true), padString(self.lastSeen, 10, true), self.seenCount ) return string.format("|cFF%s%s|r", aura_env.classColors[self.class], out) end, } ---@type table aura_env.classColors = { ["Warrior"] = "C69B6D", ["Paladin"] = "F48CBA", ["Hunter"] = "AAD372", ["Rogue"] = "FFF468", ["Priest"] = "FFFFFF", ["Death Knight"] = "C41E3A", ["Shaman"] = "0070DD", ["Mage"] = "3FC7EB", ["Warlock"] = "8788EE", ["Monk"] = "00FF98", ["Druid"] = "FF7C0A", ["Demon Hunter"] = "A330C9", } ---@param input string ---@param deliminer string ---@return string[], string|nil local function StrSplit(input, deliminer) if not deliminer then return {}, "deliminer is nil" end if not input then return {}, "input is nil" end local parts = {} for part in string.gmatch(input, "([^" .. deliminer .. "]+)") do table.insert(parts, strtrim(part)) end return parts, nil end ---@type string[] local toNotify = StrSplit(aura_env.config.notify, ",") for i, part in ipairs(toNotify) do toNotify[i] = strtrim(part) end ---@type table local notifyFor = {} local notifyForD = StrSplit(aura_env.config.notifyFor, ",") for i, part in ipairs(notifyForD) do notifyFor[part] = true end ---@param player Player ---@return string aura_env.MakeNotifyMessage = function(player) return string.format( "%s of class %s and guild %s in %s, first seen: %s, last seen: %s, times seen: %d", player.name, player.class, player.guild, player.zone, player.firstSeen, player.lastSeen, player.seenCount ) end ---@param msg string local NotifyAll = function(msg) for _, rec in ipairs(toNotify) do ---@type Message local message = { channel = "WHISPER", data = rec, message = msg, } table.insert(WeakAurasSaved.Cyka.MessageQueue, message) end end ---@param msg string local NotifyChannel = function(msg) local message = { channel = "CHANNEL", data = aura_env.config.channel, message = msg, } table.insert(WeakAurasSaved.Cyka.MessageQueue, message) end ---@param player Player ---@return nil aura_env.Notify = function(player) if not notifyFor[player.zone] then return end local msg = aura_env.MakeNotifyMessage(player) NotifyAll(msg) aura_env.NotifyChannel(player) end ---@param player Player ---@param newzone string ---@return nil aura_env.NotifyZoneChanged = function(player, newzone) if not notifyFor[newzone] then return end local msg = string.format("%s of class %s and guild %s moved to %s", player.name, player.class, player.guild, newzone) NotifyAll(msg) NotifyChannel(msg) end ---@param player Player ---@return nil aura_env.NotifyGone = function(player) if not notifyFor[player.zone] then return end local msg = string.format("%s of class %s and guild %s left %s", player.name, player.class, player.guild, player.zone) NotifyAll(msg) aura_env.NotifyChannelGone(player) end ---@param player Player ---@return nil aura_env.NotifyChannel = function(player) local msg = aura_env.MakeNotifyMessage(player) NotifyChannel(msg) end ---@param player Player ---@return nil aura_env.NotifyChannelGone = function(player) local msg = string.format("%s of class %s and guild %s left %s", player.name, player.class, player.guild, player.zone) NotifyChannel(msg) end aura_env.GetChannelName = function(channelId) local channels = { GetChannelList() } for i = 1, #channels, 2 do local id = channels[i] local name = channels[i + 1] if id == channelId then return name end end return nil end