Files
wow-Heimdall/Modules/Whoer.lua
2025-01-02 11:06:45 +01:00

517 lines
15 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local addonname, shared = ...
---@cast shared HeimdallShared
---@cast addonname string
---@diagnostic disable-next-line: missing-fields
shared.Whoer = {}
function shared.Whoer.Init()
if not Heimdall_Data.who then Heimdall_Data.who = {} end
if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end
local helpMessages = {
"1) who - пишет вам никнеймы текущих врагов в отслеживаемых локациях по порядку и локацию.",
"2) classes = пишет какие классы. (например 1 паладин 1 прист 1 рога.",
"3) howmany - общее число врагов (например дуротар 4 . оргримар 2 ) ",
"4) + - автоматическое приглашение в группу с дуельным рогой для сброса кд и общего сбора. ",
"5) ++ - автоматическое приглашение в группу альянса. (если нужен рефрак)",
}
---@type table<string, Player>
HeimdallStinkies = {}
---@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
---@field stinky boolean?
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,
---@return string
ToString = function(self)
local out = string.format("%s %s %s\nFirst: %s Last: %s Seen: %3d",
shared.padString(self.name, 16, true),
shared.padString(self.guild, 26, false),
shared.padString(self.zone, 26, false),
shared.padString(self.firstSeen, 10, true),
shared.padString(self.lastSeen, 10, true),
self.seenCount)
return string.format("|cFF%s%s|r", shared.classColors[self.class], out)
end,
---@return string
NotifyMessage = function(self)
local text = string.format(
"%s %s of class %s, race %s (%s) and guild %s in %s, first seen: %s, last seen: %s, times seen: %d",
self.name,
self.stinky and "(!!!!)" or "",
self.class,
self.race,
tostring(shared.raceMap[self.race]),
self.guild,
self.zone,
self.firstSeen,
self.lastSeen,
self.seenCount)
return text
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 shared.raceMap[race] then return false end
return shared.raceMap[race] == "Alliance"
end
local whoQueryIdx = 1
---@type WHOQuery[]
local whoQueries = {
WHOQuery.new("g-\"БеспредеЛ\"", {}),
WHOQuery.new(
"z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Human\" r-\"Dwarf\" r-\"Night Elf\"",
{ NotSiegeOfOrgrimmarFilter, AllianceFilter }),
WHOQuery.new(
"z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Gnome\" r-\"Draenei\" r-\"Worgen\"",
{ NotSiegeOfOrgrimmarFilter, AllianceFilter }),
WHOQuery.new(
"z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Kul Tiran\" r-\"Dark Iron Dwarf\" r-\"Void Elf\"",
{ NotSiegeOfOrgrimmarFilter, AllianceFilter }),
WHOQuery.new(
"z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Lightforged Draenei\" r-\"Mechagnome\"",
{ NotSiegeOfOrgrimmarFilter, AllianceFilter }),
WHOQuery.new("Kekv Demonboo Dotmada Firobot Verminal Amaterasu Freexe Tomoki", {})
}
local ttl = #whoQueries * 2
---@type WHOQuery?
local lastQuery = nil
---@param player Player
---@return string?
local function Notify(player)
if not Heimdall_Data.config.who.enabled then return end
if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end
if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
return string.format("Not notifying for zone %s",
tostring(player.zone))
end
local text = player:NotifyMessage()
---@type Message
local msg = {
channel = "CHANNEL",
data = Heimdall_Data.config.who.notifyChannel,
message = text
}
table.insert(shared.messenger.queue, msg)
if Heimdall_Data.config.who.doWhisper then
for _, name in pairs(Heimdall_Data.config.whisperNotify) do
---@type Message
local msg = {
channel = "WHISPER",
data = name,
message = text
}
table.insert(shared.messenger.queue, msg)
end
end
return nil
end
---@param player Player
---@param zone string
---@return string?
local function NotifyZoneChanged(player, zone)
if not Heimdall_Data.config.who.enabled then return end
if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end
if not Heimdall_Data.config.who.zoneNotifyFor[zone]
and not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
return string.format("Not notifying for zones %s and %s", tostring(zone), tostring(player.zone))
end
local text = string.format("%s of class %s (%s - %s) and guild %s moved to %s",
player.name,
player.class,
player.race,
shared.raceMap[player.race] or "Unknown",
player.guild,
zone)
---@type Message
local msg = {
channel = "CHANNEL",
data = Heimdall_Data.config.who.notifyChannel,
message = text
}
table.insert(shared.messenger.queue, msg)
if Heimdall_Data.config.who.doWhisper then
for _, name in pairs(Heimdall_Data.config.whisperNotify) do
---@type Message
local msg = {
channel = "WHISPER",
data = name,
message = text
}
table.insert(shared.messenger.queue, msg)
end
end
return nil
end
---@param player Player
---@return string?
local function NotifyGone(player)
if not Heimdall_Data.config.who.enabled then return end
if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end
if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
return string.format("Not notifying for zone %s",
tostring(player.zone))
end
local text = string.format("%s of class %s and guild %s left %s",
player.name,
player.class,
player.guild,
player.zone)
---@type Message
local msg = {
channel = "CHANNEL",
data = Heimdall_Data.config.who.notifyChannel,
message = text
}
table.insert(shared.messenger.queue, msg)
if Heimdall_Data.config.who.doWhisper then
for _, name in pairs(Heimdall_Data.config.whisperNotify) do
---@type Message
local msg = {
channel = "WHISPER",
data = name,
message = text
}
table.insert(shared.messenger.queue, msg)
end
end
return nil
end
local frame = CreateFrame("Frame")
frame:RegisterEvent("WHO_LIST_UPDATE")
frame:SetScript("OnEvent", function(self, event, ...)
if not Heimdall_Data.config.who.enabled then return end
---@type WHOQuery?
local query = lastQuery
if not query then
print("No query wtf?")
return
end
for i = 1, GetNumWhoResults() do
local name, guild, level, race, class, zone = GetWhoInfo(i)
local continue = false
--print(name, guild, level, race, class, zone)
---@type WHOFilter[]
local filters = query.filters
for _, filter in pairs(filters) do
if not filter(name, guild, level, race, class, zone) then
-- Mega scuffed, yes...
-- But wow does not have gotos
continue = true
end
end
if Heimdall_Data.config.who.ignored[name] then continue = true end
if not continue then
local timestamp = date("%Y-%m-%dT%H:%M:%S")
local player = HeimdallStinkies[name]
if not player then
player = Player.new(name, guild, race, class, zone)
if not Heimdall_Data.who then Heimdall_Data.who = {} end
if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end
local existing = Heimdall_Data.who.data[name]
if existing then
player.lastSeen = existing.lastSeen or "never"
player.firstSeen = existing.firstSeen or "never"
player.seenCount = existing.seenCount or 0
end
if player.firstSeen == "never" then
player.firstSeen = timestamp
end
local stinky = Heimdall_Data.config.stinkies[name]
if stinky then
player.stinky = true
PlaySoundFile("Interface\\Sounds\\Domination.ogg", "Master")
else
--PlaySoundFile("Interface\\Sounds\\Cloak.ogg", "Master")
end
local err = Notify(player)
if err then
print(string.format("Error notifying for %s: %s", tostring(name), tostring(err)))
end
player.lastSeen = timestamp
player.seenCount = player.seenCount + 1
HeimdallStinkies[name] = player
end
player.lastSeenInternal = GetTime()
if player.zone ~= zone then
local err = NotifyZoneChanged(player, zone)
if err then
print(string.format("Error notifying for %s: %s", tostring(name), tostring(err)))
end
end
player.zone = zone
player.lastSeen = timestamp
HeimdallStinkies[name] = player
if not Heimdall_Data.who then Heimdall_Data.who = {} end
if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end
Heimdall_Data.who.data[name] = player
end
end
-- Turns out WA cannot do this (
-- aura_env.UpdateMacro()
--_G["FriendsFrameCloseButton"]:Click()
end)
do
local function UpdateStinkies()
for name, player in pairs(HeimdallStinkies) do
if player.lastSeenInternal + Heimdall_Data.config.who.ttl < GetTime() then
NotifyGone(player)
--PlaySoundFile("Interface\\Sounds\\Uncloak.ogg", "Master")
HeimdallStinkies[name] = nil
end
end
end
local function Tick()
UpdateStinkies()
C_Timer.NewTimer(0.5, Tick, 1)
end
Tick()
end
do
local function DoQuery()
if not Heimdall_Data.config.who.enabled then return end
local query = whoQueries[whoQueryIdx]
whoQueryIdx = whoQueryIdx + 1
if whoQueryIdx > #whoQueries then
whoQueryIdx = 1
end
lastQuery = query
--print(string.format("Running who query: %s", tostring(query.query)))
---@diagnostic disable-next-line: param-type-mismatch
SetWhoToUI(1)
SendWho(query.query)
end
local function Tick()
DoQuery()
C_Timer.NewTimer(1, Tick, 1)
end
Tick()
end
---@return string[]
local function Count()
local ret = {}
for _, player in pairs(HeimdallStinkies) do
if Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
ret[player.zone] = (ret[player.zone] or 0) + 1
end
end
local text = {}
for zone, count in pairs(ret) do
text[#text + 1] = string.format("%s: %d", zone, count)
end
return text
end
---@return string[]
local function Who()
local ret = {}
for _, player in pairs(HeimdallStinkies) do
if Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
ret[#ret + 1] = string.format("%s/%s (%s) %s", player.name, player.class, player.zone,
player.stinky and "(!!!!)" or "")
end
end
return ret
end
---@return string[]
local function CountClass()
local ret = {}
for _, player in pairs(HeimdallStinkies) do
if Heimdall_Data.config.who.zoneNotifyFor[player.zone] then
ret[player.class] = (ret[player.class] or 0) + 1
end
end
local text = {}
for class, count in pairs(ret) do
text[#text + 1] = string.format("%s: %d", class, count)
end
return text
end
local whoQueryWhisperFrame = CreateFrame("Frame")
whoQueryWhisperFrame:RegisterEvent("CHAT_MSG_WHISPER")
whoQueryWhisperFrame:SetScript("OnEvent", function(self, event, msg, sender)
if not Heimdall_Data.config.who.enabled then return end
if msg == "who" then
---@type Message
local msg = {
channel = "WHISPER",
data = sender,
message = strjoin(", ", unpack(Who()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "howmany" then
---@type Message
local msg = {
channel = "WHISPER",
data = sender,
message = strjoin(", ", unpack(Count()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "classes" then
---@type Message
local msg = {
channel = "WHISPER",
data = sender,
message = strjoin(", ", unpack(CountClass()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "help" then
for _, helpMessage in pairs(helpMessages) do
---@type Message
local msg = {
channel = "WHISPER",
data = sender,
message = helpMessage
}
table.insert(shared.messenger.queue, msg)
end
end
end)
local whoQueryChannelFrame = CreateFrame("Frame")
whoQueryChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
whoQueryChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
if not Heimdall_Data.config.who.enabled then return end
local channelId = select(6, ...)
local channelname = ""
---@type any[]
local channels = { GetChannelList() }
for i = 1, #channels, 2 do
---@type number
local id = channels[i]
---@type string
local name = channels[i + 1]
if id == channelId then
channelname = name
end
end
if channelname ~= Heimdall_Data.config.who.notifyChannel then return end
if msg == "who" then
---@type Message
local msg = {
channel = "CHANNEL",
data = channelname,
message = "who: " .. strjoin(", ", unpack(Who()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "howmany" then
---@type Message
local msg = {
channel = "CHANNEL",
data = channelname,
message = "howmany: " .. strjoin(", ", unpack(Count()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "classes" then
---@type Message
local msg = {
channel = "CHANNEL",
data = channelname,
message = "classes: " .. strjoin(", ", unpack(CountClass()))
}
table.insert(shared.messenger.queue, msg)
end
if msg == "help" then
for _, helpMessage in pairs(helpMessages) do
---@type Message
local msg = {
channel = "CHANNEL",
data = channelname,
message = helpMessage
}
table.insert(shared.messenger.queue, msg)
end
end
end)
print("Heimdall - Whoer loaded")
end