46 Commits

Author SHA1 Message Date
7eee3a13a6 Add Russian localization who (kto) for our russian friends 2025-05-25 11:09:43 +02:00
263cf8e2e4 Add chatSniffer localization and configuration UI 2025-05-25 10:58:08 +02:00
ccbf0f8dc2 Add chatSniffer configuration and integrate into Heimdall initialization 2025-05-25 10:54:01 +02:00
63027c2dcf Update module initialization messages to use consistent format 2025-05-25 10:51:41 +02:00
d46c874604 Add timestamp logging to ChatSniffer events 2025-05-25 10:48:11 +02:00
fd4f707b6c Add ChatSniffer module and update saved variables 2025-05-25 10:47:58 +02:00
1168876dcc Fix FriendsFrame_OnEvent to pass additional parameters so it actually works naturally 2025-05-25 10:45:17 +02:00
bdf5afe436 Comment out debug print statements in Messenger module and fix queue loading 2025-05-25 10:41:02 +02:00
85ff907f05 Reset whoQueryIdx to 1 when no WHO query is found 2025-05-25 10:39:45 +02:00
7ae9db030b Remove redundant event registration for ADDON_LOADED in Heimdall.lua 2025-05-25 10:34:48 +02:00
edf8a12865 Refactor message queuing to use NetworkMessenger and Messenger based on configuration 2025-05-21 01:42:41 +02:00
d081eedd47 Fix debug print statement to include message count 2025-05-20 20:45:42 +02:00
8532db5a25 Rename dumpTable to dump and make it work with any values 2025-05-20 20:40:25 +02:00
26e783ee2e Update subproject commit reference in Meta 2025-05-20 20:20:59 +02:00
4bd237abef Hook friends list show to NOT show while we're waiting for who results 2025-05-20 20:19:41 +02:00
0ab14de0e2 CCP 2025-05-20 20:04:41 +02:00
a25b6a20d5 Release 3.12.0 2025-05-20 20:02:47 +02:00
e85c14ea45 Update StinkyTracker to use ReactiveValue for accessing ignored and stinkies lists 2025-05-18 16:05:12 +02:00
a564178ca2 Fix dumpTable function to ensure keys and values are converted to strings before printing 2025-05-18 16:05:03 +02:00
b4a4011b18 Refactor NetworkMessenger initialization to use ReactiveValue for queue management 2025-05-18 16:00:39 +02:00
3f3d252104 Update subproject commit reference in Meta 2025-05-18 15:54:48 +02:00
287be2a31c Move data definitions into their separate modules 2025-05-18 15:54:38 +02:00
3ef0e4c935 Refactor Heimdall messenger module to improve structure and utilize ReactiveValue for queue management 2025-05-18 15:54:11 +02:00
ce92e8e12c Refactor Commander and StinkyTracker modules for improved clarity and consistency in type annotations 2025-05-18 12:48:26 +02:00
03597d1b5e Update subproject commit reference in Meta 2025-05-18 12:45:11 +02:00
36ad9783e5 Move all config definitions to their respective modules 2025-05-18 12:43:55 +02:00
565db30125 Refactor multiple Heimdall modules to use class-based structure for improved organization and clarity 2025-05-18 12:31:26 +02:00
017cbf01f8 Refactor Heimdall modules to use class-based structure for improved organization and clarity 2025-05-18 12:28:25 +02:00
b16cf762ac Refactor Heimdall modules to improve structure and clarity, including AchievementSniffer, BonkDetector, Bully, Commander, and Config. 2025-05-18 12:27:00 +02:00
0057ac3a5c Refactor CombatAlerter, Commander, Inviter, Macroer, Sniffer modules for improved structure and clarity 2025-05-18 12:12:57 +02:00
0edf0561d8 Refactor DeathReporter, CombatAlerter, Commander, Configurator, and AgentTracker modules 2025-05-18 11:48:50 +02:00
1129d787b5 Refactor BonkDetector and Bully modules for improved structure and clarity 2025-05-18 11:43:40 +02:00
8a24496801 Add scratch.lua to .gitignore 2025-05-18 11:37:05 +02:00
6cb918c13c Refactor Heimdall module fields for improved organization and clarity 2025-05-18 11:36:40 +02:00
e3eefadb75 Refactor AgentTracker and related modules to improve agent management and logging 2025-05-18 11:16:13 +02:00
eab562b36d Enhance dumpTable function to include optional message parameter for improved logging 2025-05-18 11:15:52 +02:00
20a7c0eead Refactor StinkyTracker to improve tracking and ignore functionality 2025-05-18 10:51:33 +02:00
f70c5adfcf Add debug logging for stinky changes and simplify stinky handling 2025-05-18 10:31:36 +02:00
52246e2e16 Update subproject commit reference in Meta 2025-05-18 10:18:45 +02:00
4897a5b1a9 Update ticker field type in HeimdallMessengerData and HeimdallNetworkMessengerData 2025-05-18 10:18:11 +02:00
2381f68912 Updater meta config 2025-05-18 10:10:45 +02:00
ad969e8604 Update subproject commit reference in Meta 2025-05-18 10:09:02 +02:00
31678f9a82 Fix config frame visibility in shared.Config.Init() 2025-05-18 10:08:23 +02:00
1785af4d9d Remove the premature exit 2025-05-05 01:16:57 +02:00
69d7efe6f0 Release 3.11.0 2025-05-05 01:16:31 +02:00
202c5d0f46 Update release script to automatically tag releases
Maybe it's a bit better who knows...........
2025-05-05 01:16:21 +02:00
36 changed files with 6619 additions and 6132 deletions

1
.gitignore vendored
View File

@@ -0,0 +1 @@
scratch.lua

View File

@@ -1 +1,5 @@
C:/Users/Administrator/Seafile/Games-WoW/Ruski/Interface/AddOns/Heimdall/Meta/.luacheckrc globals = { "CykaPersistentData", "CreateFrame", "GetItemInfo", "aura_env" }
unused_args = false
max_line_length = 500
exclude_files = { "Meta/" }
global = false

View File

@@ -1 +1,14 @@
C:/Users/Administrator/Seafile/Games-WoW/Ruski/Interface/AddOns/Heimdall/Meta/.luarc.json {
"workspace": {
"library": [
"./Meta"
]
},
"diagnostics.disable": [
"unused-local",
"unused-vararg"
],
"diagnostics.globals": [
"aura_env"
]
}

View File

@@ -2,7 +2,7 @@ local addonname, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string ---@cast addonname string
local VERSION = "3.10.4" local VERSION = "3.12.0"
shared.VERSION = VERSION shared.VERSION = VERSION
local function init() local function init()
@@ -18,7 +18,8 @@ local function init()
---@field classColors table<string, string> ---@field classColors table<string, string>
---@field messenger HeimdallMessengerData ---@field messenger HeimdallMessengerData
---@field who HeimdallWhoData ---@field who HeimdallWhoData
---@field stinkyTracker HeimdallStinkyTrackerData ---@field stinkyTracker StinkyTrackerData
---@field agentTracker AgentTrackerData
---@field networkNodes string[] ---@field networkNodes string[]
---@field network HeimdallNetworkData ---@field network HeimdallNetworkData
---@field networkMessenger HeimdallNetworkMessengerData ---@field networkMessenger HeimdallNetworkMessengerData
@@ -26,7 +27,7 @@ local function init()
---@field _L fun(key: string, locale: string): string ---@field _L fun(key: string, locale: string): string
---@field _Locale Localization ---@field _Locale Localization
---@field VERSION string ---@field VERSION string
---@field dumpTable fun(table: any, depth?: number): nil ---@field dump fun(table: any, msg?: string, depth?: number): nil
---@field utf8len fun(input: string): number ---@field utf8len fun(input: string): number
---@field padString fun(input: string, targetLength: number, left?: boolean): string ---@field padString fun(input: string, targetLength: number, left?: boolean): string
---@field GetOrDefault fun(table: table<any, any>, keys: string[], default: any): any ---@field GetOrDefault fun(table: table<any, any>, keys: string[], default: any): any
@@ -35,30 +36,31 @@ local function init()
---@field Memoize fun(f: function): function ---@field Memoize fun(f: function): function
---@field GetLocaleForChannel fun(channel: string): string ---@field GetLocaleForChannel fun(channel: string): string
---@field WhoQueryService WhoQueryService ---@field WhoQueryService WhoQueryService
---@field Whoer InitTable|{ShouldNotifyForZone: fun(zone: string): boolean} ---@field AchievementSniffer AchievementSniffer
---@field Messenger InitTable ---@field AgentTracker AgentTracker
---@field Spotter InitTable ---@field BonkDetector BonkDetector
---@field DeathReporter InitTable ---@field Bully Bully
---@field Inviter InitTable ---@field CombatAlerter CombatAlerter
---@field Dueler InitTable ---@field Commander Commander
---@field Bully InitTable ---@field Config Config
---@field AgentTracker InitTable ---@field Configurator Configurator
---@field Emoter InitTable ---@field DeathReporter DeathReporter
---@field Echoer InitTable ---@field Dueler Dueler
---@field Macroer InitTable ---@field Echoer Echoer
---@field Commander InitTable ---@field Emoter Emoter
---@field StinkyTracker InitTable ---@field Inviter Inviter
---@field CombatAlerter InitTable ---@field Macroer Macroer
---@field Config InitTable ---@field Messenger Messenger
---@field Sniffer InitTable ---@field MinimapTagger MinimapTagger
---@field MinimapTagger InitTable ---@field Network Network
---@field BonkDetector InitTable ---@field NetworkMessenger NetworkMessenger
---@field Noter InitTable ---@field Noter Noter
---@field Network InitTable ---@field Sniffer Sniffer
---@field NetworkMessenger InitTable ---@field Spotter Spotter
---@field StinkyCache InitTable ---@field StinkyCache StinkyCache
---@field Configurator InitTable ---@field StinkyTracker StinkyTracker
---@field AchievementSniffer InitTable ---@field Whoer Whoer
---@field ChatSniffer ChatSniffer
--- Config --- --- Config ---
---@class HeimdallConfig ---@class HeimdallConfig
@@ -68,7 +70,6 @@ local function init()
---@field deathReporter HeimdallDeathReporterConfig ---@field deathReporter HeimdallDeathReporterConfig
---@field inviter HeimdallInviterConfig ---@field inviter HeimdallInviterConfig
---@field dueler HeimdallDuelerConfig ---@field dueler HeimdallDuelerConfig
---@field bully HeimdallBullyConfig
---@field agentTracker HeimdallAgentTrackerConfig ---@field agentTracker HeimdallAgentTrackerConfig
---@field emoter HeimdallEmoterConfig ---@field emoter HeimdallEmoterConfig
---@field echoer HeimdallEchoerConfig ---@field echoer HeimdallEchoerConfig
@@ -77,13 +78,13 @@ local function init()
---@field stinkyTracker HeimdallStinkyTrackerConfig ---@field stinkyTracker HeimdallStinkyTrackerConfig
---@field combatAlerter HeimdallCombatAlerterConfig ---@field combatAlerter HeimdallCombatAlerterConfig
---@field sniffer HeimdallSnifferConfig ---@field sniffer HeimdallSnifferConfig
---@field bonkDetector HeimdallBonkDetectorConfig
---@field noter HeimdallNoterConfig ---@field noter HeimdallNoterConfig
---@field network HeimdallNetworkConfig ---@field network HeimdallNetworkConfig
---@field networkMessenger HeimdallNetworkMessengerConfig ---@field networkMessenger HeimdallNetworkMessengerConfig
---@field configurator HeimdallConfiguratorConfig ---@field configurator HeimdallConfiguratorConfig
---@field stinkyCache HeimdallStinkyCacheConfig ---@field stinkyCache HeimdallStinkyCacheConfig
---@field achievementSniffer HeimdallAchievementSnifferConfig ---@field achievementSniffer HeimdallAchievementSnifferConfig
---@field chatSniffer HeimdallChatSnifferConfig
---@field whisperNotify table<string, string> ---@field whisperNotify table<string, string>
---@field addonPrefix string ---@field addonPrefix string
---@field stinkies table<string, boolean> ---@field stinkies table<string, boolean>
@@ -94,203 +95,6 @@ local function init()
---@field locale string ---@field locale string
---@field debug boolean ---@field debug boolean
---@class HeimdallSpotterConfig
---@field enabled boolean
---@field debug boolean
---@field everyone boolean
---@field hostile boolean
---@field alliance boolean
---@field stinky boolean
---@field channels string[]
---@field zoneOverride string?
---@field throttleTime number
---@class HeimdallWhoConfig
---@field enabled boolean
---@field debug boolean
---@field ignored table<string, boolean>
---@field channels string[]
---@field ttl number
---@field doWhisper boolean
---@field zoneNotifyFor table<string, boolean>
---@field queries string
---@class HeimdallMessengerConfig
---@field enabled boolean
---@field debug boolean
---@field interval number
---@class HeimdallDeathReporterConfig
---@field enabled boolean
---@field debug boolean
---@field throttle number
---@field doWhisper boolean
---@field channels string[]
---@field zoneOverride string?
---@field duelThrottle number
---@class HeimdallInviterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field keyword string
---@field allAssist boolean
---@field agentsAssist boolean
---@field throttle number
---@field kickOffline boolean
---@field cleanupInterval number
---@field afkThreshold number
---@field listeningChannel table<string, boolean>
---@class HeimdallDuelerConfig
---@field enabled boolean
---@field debug boolean
---@field declineOther boolean
---@class HeimdallBullyConfig
---@field enabled boolean
---@field debug boolean
---@class HeimdallAgentTrackerConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@class HeimdallEmoterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field prefix string
---@class HeimdallEchoerConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field prefix string
---@class HeimdallMacroerConfig
---@field enabled boolean
---@field debug boolean
---@field priority string[]
---@class HeimdallCommanderConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field commander string
---@field commands table<string, boolean>
---@class HeimdallStinkyTrackerConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@class HeimdallCombatAlerterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@class HeimdallSnifferConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field throttle number
---@field zoneOverride string?
---@field stinky boolean
---@class HeimdallMinimapTaggerConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field throttle number
---@field scale number
---@field tagTTL number
---@field tagSound boolean
---@field tagSoundFile string
---@field tagSoundThrottle number
---@field tagTextureFile string
---@field alertTTL number
---@field alertSound boolean
---@field alertSoundFile string
---@field alertSoundThrottle number
---@field alertTextureFile string
---@field combatTTL number
---@field combatSound boolean
---@field combatSoundFile string
---@field combatSoundThrottle number
---@field combatTextureFile string
---@field helpTTL number
---@field helpSound boolean
---@field helpSoundFile string
---@field helpSoundThrottle number
---@field helpTextureFile string
---@class HeimdallBonkDetectorConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field throttle number
---@class HeimdallNoterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field lastNotes number
---@class HeimdallNetworkConfig
---@field enabled boolean
---@field debug boolean
---@field members string[]
---@field updateInterval number
---@class HeimdallNetworkMessengerConfig
---@field enabled boolean
---@field debug boolean
---@field interval number
---@class HeimdallConfiguratorConfig
---@field enabled boolean
---@field debug boolean
---@class HeimdallStinkyCacheConfig
---@field enabled boolean
---@field debug boolean
---@field commander string
---@field ttl number
---@class HeimdallAchievementSnifferConfig
---@field enabled boolean
---@field debug boolean
-----@field texture string
-----@field offsetX number
-----@field offsetY number
---@field rescan boolean
---@field scanInterval number
-----@field iconScale number
--- Data ---
---@class HeimdallMessengerData
---@field queue table<string, Message>
---@field ticker number?
---@class HeimdallNetworkMessengerData
---@field queue table<string, Message>
---@field ticker number?
---@class HeimdallWhoData
---@field updateTicker number?
---@field whoTicker number?
---@field ignored table<string, boolean>
---@class HeimdallStinkyTrackerData
---@field stinkies ReactiveValue
---@class HeimdallNetworkData
---@field ticker number?
---@class HeimdallStinkyCacheData
---@field stinkies table<string, {value: number, timestamp: number}>
shared.GetOrDefault = function(table, keys, default) shared.GetOrDefault = function(table, keys, default)
local value = default local value = default
if not table then return value end if not table then return value end
@@ -310,16 +114,13 @@ local function init()
return value return value
end end
shared.messenger = {
queue = {},
}
shared.who = {
ignored = {},
}
--/run Heimdall_Data.config.who.queries="g-\"БеспредеЛ\"|ally" --/run Heimdall_Data.config.who.queries="g-\"БеспредеЛ\"|ally"
Heimdall_Data.config = { Heimdall_Data.config = {
debug = shared.GetOrDefault(Heimdall_Data, { "config", "debug" }, false), debug = shared.GetOrDefault(Heimdall_Data, { "config", "debug" }, false),
chatSniffer = {
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "chatSniffer", "enabled" }, false),
debug = shared.GetOrDefault(Heimdall_Data, { "config", "chatSniffer", "debug" }, false),
},
spotter = { spotter = {
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "enabled" }, true), enabled = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "enabled" }, true),
debug = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "debug" }, false), debug = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "debug" }, false),
@@ -418,6 +219,7 @@ local function init()
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "enabled" }, false), enabled = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "enabled" }, false),
debug = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "debug" }, false), debug = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "debug" }, false),
channels = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "channels" }, { "Agent" }), channels = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "channels" }, { "Agent" }),
ignoredTimeout = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "ignoredTimeout" }, 600),
}, },
combatAlerter = { combatAlerter = {
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "enabled" }, false), enabled = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "enabled" }, false),
@@ -670,7 +472,7 @@ local function init()
shared.Memoize = function(f) shared.Memoize = function(f)
local mem = {} -- memoizing table local mem = {} -- memoizing table
setmetatable(mem, { __mode = "kv" }) -- make it weak setmetatable(mem, { __mode = "kv" }) -- make it weak
return function(x) -- new version of f, with memoizing return function(x) -- new version of 'f', with memoizing
if Heimdall_Data.config.debug then print(string.format("[Heimdall] Memoize %s", tostring(x))) end if Heimdall_Data.config.debug then print(string.format("[Heimdall] Memoize %s", tostring(x))) end
local r = mem[x] local r = mem[x]
if r == nil then -- no previous result? if r == nil then -- no previous result?
@@ -737,6 +539,7 @@ local function init()
shared.Configurator.Init() shared.Configurator.Init()
shared.StinkyCache.Init() shared.StinkyCache.Init()
shared.AchievementSniffer.Init() shared.AchievementSniffer.Init()
shared.ChatSniffer.Init()
print("Heimdall loaded!") print("Heimdall loaded!")
end end
@@ -745,3 +548,69 @@ loadedFrame:RegisterEvent("ADDON_LOADED")
loadedFrame:SetScript("OnEvent", function(self, event, addonName) loadedFrame:SetScript("OnEvent", function(self, event, addonName)
if addonName == addonname then init() end if addonName == addonname then init() end
end) end)
-- Create the import/export frame
local ccpFrame = CreateFrame("Frame", "CCPFrame", UIParent)
ccpFrame:SetSize(512 * 1.5, 512 * 1.5)
ccpFrame:SetPoint("CENTER")
ccpFrame:SetFrameStrata("HIGH")
ccpFrame:EnableMouse(true)
ccpFrame:SetMovable(true)
ccpFrame:SetResizable(false)
ccpFrame:SetBackdrop({
bgFile = "Interface/Tooltips/UI-Tooltip-Background",
edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
tile = true,
tileSize = 4,
edgeSize = 4,
insets = {
left = 4,
right = 4,
top = 4,
bottom = 4,
},
})
ccpFrame:SetBackdropColor(0, 0, 0, 0.8)
ccpFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
ccpFrame:SetMovable(true)
ccpFrame:EnableMouse(true)
ccpFrame:RegisterForDrag("LeftButton")
ccpFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
ccpFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
ccpFrame:SetScript("OnShow", function(self) self:SetScale(1) end)
ccpFrame:Hide()
-- Create scroll frame
local scrollFrame = CreateFrame("ScrollFrame", "CCPFrameScrollFrame", ccpFrame, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", ccpFrame, "TOPLEFT", 10, -10)
scrollFrame:SetPoint("BOTTOMRIGHT", ccpFrame, "BOTTOMRIGHT", -30, 10)
-- Create the text box
local ccpFrameTextBox = CreateFrame("EditBox", "CCPFrameTextBox", scrollFrame)
ccpFrameTextBox:SetSize(512 * 1.5 - 40, 512 * 1.5 - 20)
ccpFrameTextBox:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
ccpFrameTextBox:SetFont("Fonts\\FRIZQT__.ttf", 12)
ccpFrameTextBox:SetTextColor(1, 1, 1, 1)
ccpFrameTextBox:SetTextInsets(10, 10, 10, 10)
ccpFrameTextBox:SetMultiLine(true)
ccpFrameTextBox:SetAutoFocus(true)
ccpFrameTextBox:SetMaxLetters(1000000)
ccpFrameTextBox:SetScript("OnEscapePressed", function(self) ccpFrame:Hide() end)
-- Set the scroll frame's scroll child
scrollFrame:SetScrollChild(ccpFrameTextBox)
CCP = function(window)
window = window or 1
local charFrame = _G["ChatFrame" .. window]
local maxLines = charFrame:GetNumMessages() or 0
local chat = {}
for i = 1, maxLines do
local currentMsg = charFrame:GetMessageInfo(i)
chat[#chat + 1] = currentMsg
end
ccpFrameTextBox:SetText(table.concat(chat, "\n"))
ccpFrame:Show()
ccpFrameTextBox:SetFocus()
end

View File

@@ -1,9 +1,9 @@
## Interface: 70300 ## Interface: 70300
## Title: Heimdall ## Title: Heimdall
## Version: 3.10.4 ## Version: 3.12.0
## Notes: Watches over areas and alerts when hostiles spotted ## Notes: Watches over areas and alerts when hostiles spotted
## Author: Cyka ## Author: Cyka
## SavedVariables: Heimdall_Data, Heimdall_Achievements ## SavedVariables: Heimdall_Data, Heimdall_Achievements, Heimdall_Chat
_L.lua _L.lua
Modules/CLEUParser.lua Modules/CLEUParser.lua
@@ -33,4 +33,5 @@ Modules/NetworkMessenger.lua
Modules/StinkyCache.lua Modules/StinkyCache.lua
Modules/Configurator.lua Modules/Configurator.lua
Modules/AchievementSniffer.lua Modules/AchievementSniffer.lua
Modules/ChatSniffer.lua
Heimdall.lua Heimdall.lua

2
Meta

Submodule Meta updated: 6ab88bd8dc...eee043a846

View File

@@ -2,6 +2,16 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "AchievementSniffer" local ModuleName = "AchievementSniffer"
---@class HeimdallAchievementSnifferConfig
---@field enabled boolean
---@field debug boolean
-----@field texture string
-----@field offsetX number
-----@field offsetY number
---@field rescan boolean
---@field scanInterval number
-----@field iconScale number
-- local HeimdallRoot = "Interface\\AddOns\\Heimdall\\" -- local HeimdallRoot = "Interface\\AddOns\\Heimdall\\"
-- local TextureRoot = HeimdallRoot .. "Texture\\" -- local TextureRoot = HeimdallRoot .. "Texture\\"
@@ -74,220 +84,221 @@ local Achievements = {
12448, 12448,
} }
---@diagnostic disable-next-line: missing-fields ---@class AchievementSniffer
shared.AchievementSniffer = {} shared.AchievementSniffer = {
function shared.AchievementSniffer.Init() Init = function()
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Module initialized", ModuleName))
end
local guidMap = {}
---@class AchievementData
---@field id number
---@field date string
---@field completed boolean
---@class Heimdall_Achievements
---@field players table<string, table<number, AchievementData>>
---@field alreadySeen table<string, boolean>
---@field rescan boolean
if not Heimdall_Achievements then Heimdall_Achievements = {} end
if not Heimdall_Achievements.players then Heimdall_Achievements.players = {} end
if not Heimdall_Achievements.alreadySeen then Heimdall_Achievements.alreadySeen = {} end
--local framePool = {}
--for i = 1, 40 do
-- local frame = CreateFrame("Frame", "HeimdallAchievementSnifferNameplate" .. i, UIParent)
-- local texture = frame:CreateTexture(nil, "ARTWORK")
-- texture:SetAllPoints(frame)
-- texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame.texture = texture
-- frame:Hide()
-- table.insert(framePool, frame)
--end
---@param name string
---@return boolean
local function ShouldInspect(name)
local should = false
if not Heimdall_Achievements.players[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s does not have prior achievement data", ModuleName, name))
end
should = true
end
if Heimdall_Achievements.alreadySeen[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s has already been seen", ModuleName, name))
end
-- Save some memory
Heimdall_Achievements.players[name] = nil
should = false
end
if Heimdall_Data.config.achievementSniffer.rescan then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Rescan is enabled", ModuleName))
end
should = true
end
return should
end
-- It's not working well AT ALL
-- I don't know how to do it better
-- It simply just does not work...
--local function UpdateFrames()
-- for i, frame in ipairs(framePool) do
-- local unit = "nameplate" .. i
-- if not UnitExists(unit) then
-- --if Heimdall_Data.config.achievementSniffer.debug then
-- -- print(string.format("[%s] Unit %s does not exist, hiding frame", ModuleName, unit))
-- --end
-- frame:Hide()
-- else
-- --local unitFrame = _G[string.format("ElvUI_NamePlate%dHealthBar", i)]
-- local unitFrame = _G[string.format("NamePlate%d", i)]
-- if unitFrame == nil then
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame for %s not found", ModuleName, unit))
-- end
-- frame:Hide()
-- else
-- local unitName = UnitName(unit)
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame found for %s (%s)", ModuleName, unit, unitName))
-- end
-- frame:Show()
-- frame:SetSize(32, 32)
-- frame:SetFrameStrata("HIGH")
-- frame:SetFrameLevel(100)
-- frame:SetScale(Heimdall_Data.config.achievementSniffer.iconScale)
-- frame.texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame:SetPoint("CENTER", unitFrame, "CENTER",
-- Heimdall_Data.config.achievementSniffer.offsetX,
-- Heimdall_Data.config.achievementSniffer.offsetY)
-- frame:SetParent(unitFrame)
-- frame:SetAlpha(1)
-- local exists = ShouldInspect(unitName)
-- if exists then
-- frame.texture:SetVertexColor(1, 0, 0, 1)
-- else
-- frame.texture:SetVertexColor(0, 1, 0, 1)
-- end
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Frame updated for %s", ModuleName, unitName))
-- end
-- end
-- end
-- end
--end
---@param unit string
local function TryInspect(unit)
local targetPlayer = UnitIsPlayer(unit)
if not targetPlayer then return end
local targetName = UnitName(unit)
local targetGuid = UnitGUID(unit)
guidMap[targetGuid] = targetName
if not ShouldInspect(targetName) then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Not inspecting player: %s", ModuleName, targetName))
end
return
end
local canInspect = CheckInteractDistance(unit, 1)
if canInspect then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Inspecting player: %s", ModuleName, targetName))
end
SetAchievementComparisonUnit(unit)
else
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Cannot inspect player (too far?): %s", ModuleName, targetName))
end
end
end
---@param name string
local function Scan(name)
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Scanning achievements for %s", ModuleName, name)) print(string.format("[%s] Module initialized", ModuleName))
end end
Heimdall_Achievements.players[name] = {} local guidMap = {}
for i, aid in ipairs(Achievements) do
local completed, month, day, year = GetAchievementComparisonInfo(aid)
if completed then
---@type string
local yearstr = "" .. year
if year < 100 then yearstr = "20" .. year end
local date = string.format("%04d-%02d-%02d", yearstr, month, day) ---@class AchievementData
---@field id number
---@field date string
---@field completed boolean
local data = { ---@class Heimdall_Achievements
id = aid, ---@field players table<string, table<number, AchievementData>>
date = date, ---@field alreadySeen table<string, boolean>
completed = completed, ---@field rescan boolean
} if not Heimdall_Achievements then Heimdall_Achievements = {} end
if not Heimdall_Achievements.players then Heimdall_Achievements.players = {} end
if not Heimdall_Achievements.alreadySeen then Heimdall_Achievements.alreadySeen = {} end
--local framePool = {}
--for i = 1, 40 do
-- local frame = CreateFrame("Frame", "HeimdallAchievementSnifferNameplate" .. i, UIParent)
-- local texture = frame:CreateTexture(nil, "ARTWORK")
-- texture:SetAllPoints(frame)
-- texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame.texture = texture
-- frame:Hide()
-- table.insert(framePool, frame)
--end
---@param name string
---@return boolean
local function ShouldInspect(name)
local should = false
if not Heimdall_Achievements.players[name] then
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Achievement %d completed on %s", ModuleName, aid, date)) print(string.format("[%s] Player %s does not have prior achievement data", ModuleName, name))
end
should = true
end
if Heimdall_Achievements.alreadySeen[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s has already been seen", ModuleName, name))
end
-- Save some memory
Heimdall_Achievements.players[name] = nil
should = false
end
if Heimdall_Data.config.achievementSniffer.rescan then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Rescan is enabled", ModuleName))
end
should = true
end
return should
end
-- It's not working well AT ALL
-- I don't know how to do it better
-- It simply just does not work...
--local function UpdateFrames()
-- for i, frame in ipairs(framePool) do
-- local unit = "nameplate" .. i
-- if not UnitExists(unit) then
-- --if Heimdall_Data.config.achievementSniffer.debug then
-- -- print(string.format("[%s] Unit %s does not exist, hiding frame", ModuleName, unit))
-- --end
-- frame:Hide()
-- else
-- --local unitFrame = _G[string.format("ElvUI_NamePlate%dHealthBar", i)]
-- local unitFrame = _G[string.format("NamePlate%d", i)]
-- if unitFrame == nil then
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame for %s not found", ModuleName, unit))
-- end
-- frame:Hide()
-- else
-- local unitName = UnitName(unit)
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame found for %s (%s)", ModuleName, unit, unitName))
-- end
-- frame:Show()
-- frame:SetSize(32, 32)
-- frame:SetFrameStrata("HIGH")
-- frame:SetFrameLevel(100)
-- frame:SetScale(Heimdall_Data.config.achievementSniffer.iconScale)
-- frame.texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame:SetPoint("CENTER", unitFrame, "CENTER",
-- Heimdall_Data.config.achievementSniffer.offsetX,
-- Heimdall_Data.config.achievementSniffer.offsetY)
-- frame:SetParent(unitFrame)
-- frame:SetAlpha(1)
-- local exists = ShouldInspect(unitName)
-- if exists then
-- frame.texture:SetVertexColor(1, 0, 0, 1)
-- else
-- frame.texture:SetVertexColor(0, 1, 0, 1)
-- end
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Frame updated for %s", ModuleName, unitName))
-- end
-- end
-- end
-- end
--end
---@param unit string
local function TryInspect(unit)
local targetPlayer = UnitIsPlayer(unit)
if not targetPlayer then return end
local targetName = UnitName(unit)
local targetGuid = UnitGUID(unit)
guidMap[targetGuid] = targetName
if not ShouldInspect(targetName) then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Not inspecting player: %s", ModuleName, targetName))
end
return
end
local canInspect = CheckInteractDistance(unit, 1)
if canInspect then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Inspecting player: %s", ModuleName, targetName))
end
SetAchievementComparisonUnit(unit)
else
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Cannot inspect player (too far?): %s", ModuleName, targetName))
end end
Heimdall_Achievements.players[name][aid] = data
end end
end end
--UpdateFrames()
end
local nameplateFrame = CreateFrame("Frame") ---@param name string
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED") local function Scan(name)
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplateFrame:SetScript("OnEvent", function(self, event, unit)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for unit: %s", ModuleName, event, unit))
end
if event == "NAME_PLATE_UNIT_ADDED" then TryInspect(unit) end
--UpdateFrames()
end)
local inspectFrame = CreateFrame("Frame")
inspectFrame:RegisterEvent("INSPECT_ACHIEVEMENT_READY")
inspectFrame:SetScript("OnEvent", function(self, event, guid)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
local name = guidMap[guid]
if not name then
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] No name found for guid: %s", ModuleName, guid)) print(string.format("[%s] Scanning achievements for %s", ModuleName, name))
end end
return Heimdall_Achievements.players[name] = {}
end for i, aid in ipairs(Achievements) do
if Heimdall_Data.config.achievementSniffer.debug then local completed, month, day, year = GetAchievementComparisonInfo(aid)
print(string.format("[%s] Event triggered: %s for player: %s", ModuleName, event, name)) if completed then
end ---@type string
Scan(name) local yearstr = "" .. year
end) if year < 100 then yearstr = "20" .. year end
local function Tick() local date = string.format("%04d-%02d-%02d", yearstr, month, day)
C_Timer.NewTimer(Heimdall_Data.config.achievementSniffer.scanInterval, Tick)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then local data = {
print(string.format("[%s] Scanning achievements for everyone on screen", ModuleName)) id = aid,
end date = date,
for i = 1, 40 do completed = completed,
local unit = "nameplate" .. i }
if UnitExists(unit) then if Heimdall_Data.config.achievementSniffer.debug then
TryInspect(unit) print(string.format("[%s] Achievement %d completed on %s", ModuleName, aid, date))
--else end
-- if Heimdall_Data.config.achievementSniffer.debug then Heimdall_Achievements.players[name][aid] = data
-- print(string.format("[%s] Unit %s does not exist, nothing to inspect", ModuleName, unit)) end
-- end
end end
--UpdateFrames()
end end
--UpdateFrames()
end
Tick()
print("[Heimdall] AchievementSniffer loaded") local nameplateFrame = CreateFrame("Frame")
end nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplateFrame:SetScript("OnEvent", function(self, event, unit)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for unit: %s", ModuleName, event, unit))
end
if event == "NAME_PLATE_UNIT_ADDED" then TryInspect(unit) end
--UpdateFrames()
end)
local inspectFrame = CreateFrame("Frame")
inspectFrame:RegisterEvent("INSPECT_ACHIEVEMENT_READY")
inspectFrame:SetScript("OnEvent", function(self, event, guid)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
local name = guidMap[guid]
if not name then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] No name found for guid: %s", ModuleName, guid))
end
return
end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for player: %s", ModuleName, event, name))
end
Scan(name)
end)
local function Tick()
C_Timer.NewTimer(Heimdall_Data.config.achievementSniffer.scanInterval, Tick)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Scanning achievements for everyone on screen", ModuleName))
end
for i = 1, 40 do
local unit = "nameplate" .. i
if UnitExists(unit) then
TryInspect(unit)
--else
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit %s does not exist, nothing to inspect", ModuleName, unit))
-- end
end
end
--UpdateFrames()
end
Tick()
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,118 +2,143 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "AgentTracker" local ModuleName = "AgentTracker"
---@diagnostic disable-next-line: missing-fields ---@class AgentTrackerData
shared.AgentTracker = {} ---@field agents ReactiveValue<table<string, string>>
function shared.AgentTracker.Init()
--/run Heimdall_Data.config.agents["Cyheuraeth"]=date("%Y-%m-%dT%H:%M:%S")
---@type table<string, boolean>
local channelRosterFrame = CreateFrame("Frame")
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel roster update received", ModuleName))
end
if not Heimdall_Data.config.agentTracker.enabled then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Module disabled, ignoring roster update", ModuleName))
end
return
end
local name = GetChannelDisplayInfo(index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing channel update: %s (index: %d)", ModuleName, name or "nil", index))
end
if name ~= Heimdall_Data.config.agentTracker.masterChannel then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Ignoring non-master channel: %s", ModuleName, name or "nil"))
end
return
end
local count = select(5, GetChannelDisplayInfo(index))
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing %d members in channel", ModuleName, count))
end
local newAgents = 0 ---@class HeimdallAgentTrackerConfig
for i = 1, count do ---@field enabled boolean
name = GetChannelRosterInfo(index, i) ---@field debug boolean
if name then ---@field channels string[]
local isNewAgent = not Heimdall_Data.config.agents[name]
Heimdall_Data.config.agents[name] = date("%Y-%m-%dT%H:%M:%S") ---@class AgentTracker
if isNewAgent then newAgents = newAgents + 1 end shared.AgentTracker = {
---@param name string
---@return boolean
Track = function(name)
if not name then return false end
local exists = shared.AgentTracker.IsAgent(name)
if exists then return false end
shared.agentTracker.agents[name] = date("%Y-%m-%dT%H:%M:%S")
-- Heimdall_Data.config.agents[name] = date("%Y-%m-%dT%H:%M:%S")
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Tracking new agent: %s", ModuleName, name))
shared.dump(shared.agentTracker.agents)
end
return true
end,
---@param name string
---@return boolean
IsAgent = function(name)
if not name then return false end
return shared.agentTracker.agents[name] ~= nil
end,
---@param callback fun(agent: string)
OnChange = function(callback) shared.agentTracker.agents:onChange(callback) end,
---@param callback fun(agent: string)
ForEach = function(callback)
---@type table<string, string>
local agents = shared.agentTracker.agents:get()
for name, _ in pairs(agents) do
callback(name)
end
end,
---@return nil
Init = function()
shared.agentTracker = {
agents = ReactiveValue.new(Heimdall_Data.config.agents),
}
--/run Heimdall_Data.config.agents["Cyheuraeth"]=date("%Y-%m-%dT%H:%M:%S")
---@type table<string, boolean>
local channelRosterFrame = CreateFrame("Frame")
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel roster update received", ModuleName))
end
if not Heimdall_Data.config.agentTracker.enabled then
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print( print(string.format("[%s] Module disabled, ignoring roster update", ModuleName))
string.format( end
"[%s] %s agent: %s", return
ModuleName, end
isNewAgent and "Added new" or "Updated existing", local name = GetChannelDisplayInfo(index)
name if Heimdall_Data.config.agentTracker.debug then
) print(string.format("[%s] Processing channel update: %s (index: %d)", ModuleName, name or "nil", index))
) end
if name ~= Heimdall_Data.config.agentTracker.masterChannel then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Ignoring non-master channel: %s", ModuleName, name or "nil"))
end
return
end
local count = select(5, GetChannelDisplayInfo(index))
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing %d members in channel", ModuleName, count))
end
local newAgents = 0
for i = 1, count do
name = GetChannelRosterInfo(index, i)
shared.AgentTracker.Track(name)
end
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Roster update complete - Added %d new agents", ModuleName, newAgents))
end
end)
local agentTrackerChannelSniffer = CreateFrame("Frame")
agentTrackerChannelSniffer:RegisterEvent("CHAT_MSG_CHANNEL")
agentTrackerChannelSniffer:SetScript("OnEvent", function(self, event, msg, sender, ...)
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
-- end
if not Heimdall_Data.config.agentTracker.enabled then
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring channel message", ModuleName))
-- end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.agentTracker.channels) do
if channel == channelname then
ok = true
break
end end
end end
end if not ok then
if Heimdall_Data.config.agentTracker.debug then
if Heimdall_Data.config.agentTracker.debug then print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
print(string.format("[%s] Roster update complete - Added %d new agents", ModuleName, newAgents)) end
end return
end)
local agentTrackerChannelSniffer = CreateFrame("Frame")
agentTrackerChannelSniffer:RegisterEvent("CHAT_MSG_CHANNEL")
agentTrackerChannelSniffer:SetScript("OnEvent", function(self, event, msg, sender, ...)
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
-- end
if not Heimdall_Data.config.agentTracker.enabled then
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring channel message", ModuleName))
-- end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.agentTracker.channels) do
if channel == channelname then
ok = true
break
end end
end
if not ok then
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
end end
return
end
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.agentTracker)
end
sender = string.match(sender, "^[^-]+") sender = string.match(sender, "^[^-]+")
local isNewAgent = not Heimdall_Data.config.agents[sender] local new = shared.AgentTracker.Track(sender)
Heimdall_Data.config.agents[sender] = date("%Y-%m-%dT%H:%M:%S")
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print( print(
string.format( string.format(
"[%s] %s agent from message: %s", "[%s] %s agent from message: %s",
ModuleName, ModuleName,
isNewAgent and "Added new" or "Updated existing", new and "Added new" or "Updated existing",
sender sender
)
) )
) end
end end)
end)
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
local count = 0 print(string.format("[%s] Module initialized", ModuleName))
for _ in pairs(Heimdall_Data.config.agents) do shared.dump(shared.agentTracker.agents:get(), "Agents")
count = count + 1
end end
print(string.format("[%s] Module initialized - Tracking %d agents", ModuleName, count)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] AgentTracker loaded") }
end

View File

@@ -2,133 +2,141 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "BonkDetector" local ModuleName = "BonkDetector"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallBonkDetectorConfig
shared.BonkDetector = {} ---@field enabled boolean
function shared.BonkDetector.Init() ---@field debug boolean
---@type table<string, number> ---@field channels string[]
local lastReportTime = {} ---@field throttle number
local frame = CreateFrame("Frame") ---@class BonkDetector
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") shared.BonkDetector = {
frame:SetScript("OnEvent", function(self, event, ...) ---@return nil
-- if Heimdall_Data.config.bonkDetector.debug then Init = function()
-- print(string.format("[%s] Combat log event received", ModuleName)) ---@type table<string, number>
-- end local lastReportTime = {}
if not Heimdall_Data.config.bonkDetector.enabled then
-- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
-- end
return
end
local subevent = select(2, ...) local frame = CreateFrame("Frame")
if not subevent:find("_DAMAGE") then frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
if Heimdall_Data.config.bonkDetector.debug then frame:SetScript("OnEvent", function(self, event, ...)
print(string.format("[%s] Not a damage event, ignoring: %s", ModuleName, subevent)) -- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Combat log event received", ModuleName))
-- end
if not Heimdall_Data.config.bonkDetector.enabled then
-- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
-- end
return
end end
return
end
---@type string|nil, string, string, string, string local subevent = select(2, ...)
local err, source, sourceGUID, destination, destinationGUID if not subevent:find("_DAMAGE") then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Not a damage event, ignoring: %s", ModuleName, subevent))
end
return
end
source, err = CLEUParser.GetSourceName(...) ---@type string|nil, string, string, string, string
if err then local err, source, sourceGUID, destination, destinationGUID
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting source name: %s", ModuleName, err))
end
return
end
sourceGUID, err = CLEUParser.GetSourceGUID(...)
if err then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting source GUID: %s", ModuleName, err))
end
return
end
if not string.find(sourceGUID, "Player") then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Source %s is not a player, nothing to do", ModuleName, source))
end
return
end
destination, err = CLEUParser.GetDestName(...) source, err = CLEUParser.GetSourceName(...)
if err then if err then
if Heimdall_Data.config.bonkDetector.debug then if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting destination name: %s", ModuleName, err)) print(string.format("[%s] Error getting source name: %s", ModuleName, err))
end
return
end end
return sourceGUID, err = CLEUParser.GetSourceGUID(...)
end if err then
destinationGUID, err = CLEUParser.GetDestGUID(...) if Heimdall_Data.config.bonkDetector.debug then
if err then print(string.format("[%s] Error getting source GUID: %s", ModuleName, err))
if Heimdall_Data.config.bonkDetector.debug then end
print(string.format("[%s] Error getting destination GUID: %s", ModuleName, err)) return
end end
return if not string.find(sourceGUID, "Player") then
end if Heimdall_Data.config.bonkDetector.debug then
if not string.find(destinationGUID, "Player") then print(string.format("[%s] Source %s is not a player, nothing to do", ModuleName, source))
if Heimdall_Data.config.bonkDetector.debug then end
print(string.format("[%s] Destination %s is not a player, nothing to do", ModuleName, destination)) return
end end
return
end
if source == destination then destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.bonkDetector.debug then if err then
print(string.format("[%s] Source and destination are the same, ignoring event", ModuleName)) if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting destination name: %s", ModuleName, err))
end
return
end
destinationGUID, err = CLEUParser.GetDestGUID(...)
if err then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting destination GUID: %s", ModuleName, err))
end
return
end
if not string.find(destinationGUID, "Player") then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Destination %s is not a player, nothing to do", ModuleName, destination))
end
return
end end
return
end
local currentTime = GetTime() if source == destination then
local throttle = Heimdall_Data.config.bonkDetector.throttle if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Source and destination are the same, ignoring event", ModuleName))
end
return
end
local currentTime = GetTime()
local throttle = Heimdall_Data.config.bonkDetector.throttle
if lastReportTime[source] and (currentTime - lastReportTime[source]) < throttle then
if Heimdall_Data.config.bonkDetector.debug then
local timeLeft = throttle - (currentTime - lastReportTime[source])
print(
string.format(
"[%s] Damage report throttled for %s (%.1f seconds remaining)",
ModuleName,
source,
timeLeft
)
)
end
return
end
lastReportTime[source] = currentTime
if lastReportTime[source] and (currentTime - lastReportTime[source]) < throttle then
if Heimdall_Data.config.bonkDetector.debug then if Heimdall_Data.config.bonkDetector.debug then
local timeLeft = throttle - (currentTime - lastReportTime[source])
print( print(
string.format( string.format(
"[%s] Damage report throttled for %s (%.1f seconds remaining)", "[%s] Processing damage event - Source: %s, Target: %s, Type: %s",
ModuleName, ModuleName,
source, source,
timeLeft destination,
subevent
) )
) )
end end
return
end
lastReportTime[source] = currentTime for _, channel in pairs(Heimdall_Data.config.bonkDetector.channels) do
local locale = shared.GetLocaleForChannel(channel)
if Heimdall_Data.config.bonkDetector.debug then local msg = string.format(shared._L("bonkDetected", locale), source, destination, subevent)
print( ---@type Message
string.format( local message = {
"[%s] Processing damage event - Source: %s, Target: %s, Type: %s", channel = "C",
ModuleName, data = channel,
source, message = msg,
destination, }
subevent if Heimdall_Data.config.bonkDetector.debug then
) print(string.format("[%s] Queuing bonk detector message", ModuleName))
) shared.dump(message)
end end
table.insert(shared.messenger.queue, message)
for _, channel in pairs(Heimdall_Data.config.bonkDetector.channels) do
local locale = shared.GetLocaleForChannel(channel)
local msg = string.format(shared._L("bonkDetected", locale), source, destination, subevent)
---@type Message
local message = {
channel = "C",
data = channel,
message = msg,
}
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Queuing bonk detector message", ModuleName))
shared.dumpTable(message)
end end
table.insert(shared.messenger.queue, message) end)
end
end)
print("[Heimdall] BonkDetector loaded") print(string.format("[%s] Module initialized", ModuleName))
end end,
}

View File

@@ -2,9 +2,15 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Bully" local ModuleName = "Bully"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallBullyConfig
shared.Bully = {} ---@field enabled boolean
function shared.Bully.Init() ---@field debug boolean
if Heimdall_Data.config.bully.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print("[Heimdall] Bully loaded") ---@class Bully
end shared.Bully = {
---@return nil
Init = function()
if Heimdall_Data.config.bully.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

49
Modules/ChatSniffer.lua Normal file
View File

@@ -0,0 +1,49 @@
local _, shared = ...
---@cast shared HeimdallShared
local ModuleName = "ChatSniffer"
---@class HeimdallChatSnifferConfig
---@field enabled boolean
---@field debug boolean
---@class ChatSniffer
shared.ChatSniffer = {
Init = function()
Heimdall_Chat = Heimdall_Chat or {}
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_SAY")
frame:RegisterEvent("CHAT_MSG_YELL")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:RegisterEvent("CHAT_MSG_WHISPER")
frame:RegisterEvent("CHAT_MSG_CHANNEL_JOIN")
frame:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE")
frame:RegisterEvent("CHAT_MSG_EMOTE")
frame:RegisterEvent("CHAT_MSG_PARTY")
frame:RegisterEvent("CHAT_MSG_PARTY_LEADER")
frame:RegisterEvent("CHAT_MSG_RAID")
frame:RegisterEvent("CHAT_MSG_RAID_LEADER")
frame:RegisterEvent("CHAT_MSG_RAID_WARNING")
frame:RegisterEvent("CHAT_MSG_SYSTEM")
frame:RegisterEvent("CHAT_MSG_TEXT_EMOTE")
frame:RegisterEvent("CHAT_MSG_YELL")
frame:SetScript("OnEvent", function(self, event, msg, sender, language, channel)
if not Heimdall_Data.config.chatSniffer.enabled then return end
if not Heimdall_Data.config.chatSniffer.debug then
shared.dump(string.format("[%s] got message", { event, msg, sender, language, channel }))
end
local timestamp = date("%Y-%m-%d %H:%M:%S")
local log = string.format(
"%s|%s|%s|%s|%s|%s",
tostring(timestamp),
tostring(event),
tostring(sender),
tostring(msg),
tostring(language),
tostring(channel)
)
Heimdall_Chat[#Heimdall_Chat + 1] = log
end)
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,134 +2,140 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "CombatAlerter" local ModuleName = "CombatAlerter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallCombatAlerterConfig
shared.CombatAlerter = {} ---@field enabled boolean
function shared.CombatAlerter.Init() ---@field debug boolean
local alerted = {} ---@field channels string[]
local combatAlerterFrame = CreateFrame("Frame")
combatAlerterFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") ---@class CombatAlerter
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...) shared.CombatAlerter = {
if Heimdall_Data.config.combatAlerter.debug then Init = function()
print(string.format("[%s] Combat log event received", ModuleName)) local alerted = {}
end local combatAlerterFrame = CreateFrame("Frame")
if not Heimdall_Data.config.combatAlerter.enabled then combatAlerterFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Module disabled, ignoring combat event", ModuleName)) print(string.format("[%s] Combat log event received", ModuleName))
end end
return if not Heimdall_Data.config.combatAlerter.enabled then
end if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
---@type string|nil, string, string end
local err, source, destination return
destination, err = CLEUParser.GetDestName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting destination: %s", ModuleName, err))
end end
return
end
if Heimdall_Data.config.combatAlerter.debug then ---@type string|nil, string, string
print(string.format("[%s] Combat event destination: %s", ModuleName, destination)) local err, source, destination
end
if destination ~= UnitName("player") then destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.combatAlerter.debug then if err then
print(string.format("[%s] Ignoring event - not targeted at player", ModuleName)) if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting destination: %s", ModuleName, err))
end
return
end end
return
end
source, err = CLEUParser.GetSourceName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting source, using 'unknown': %s", ModuleName, err)) print(string.format("[%s] Combat event destination: %s", ModuleName, destination))
end end
source = "unknown"
end
if Heimdall_Data.config.combatAlerter.debug then if destination ~= UnitName("player") then
print(string.format("[%s] Combat event source: %s", ModuleName, source)) if Heimdall_Data.config.combatAlerter.debug then
end print(string.format("[%s] Ignoring event - not targeted at player", ModuleName))
end
return
end
source, err = CLEUParser.GetSourceName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting source, using 'unknown': %s", ModuleName, err))
end
source = "unknown"
end
if shared.stinkyTracker.stinkies and shared.stinkyTracker.stinkies[source] then
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print( print(string.format("[%s] Combat event source: %s", ModuleName, source))
string.format( end
"[%s] Source is tracked stinky: %s (Already alerted: %s)",
ModuleName, if shared.StinkyTracker.IsStinky(source) then
source, if Heimdall_Data.config.combatAlerter.debug then
tostring(alerted[source] or false) print(
string.format(
"[%s] Source is tracked stinky: %s (Already alerted: %s)",
ModuleName,
source,
tostring(alerted[source] or false)
)
) )
) end
end if alerted[source] then return end
if alerted[source] then return end
alerted[source] = true alerted[source] = true
local x, y = GetPlayerMapPosition("player") local x, y = GetPlayerMapPosition("player")
local zone, subZone = GetZoneText(), GetSubZoneText() local zone, subZone = GetZoneText(), GetSubZoneText()
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print( print(
string.format( string.format(
"[%s] Player location: %s/%s at %.2f,%.2f", "[%s] Player location: %s/%s at %.2f,%.2f",
ModuleName, ModuleName,
zone, zone,
subZone, subZone,
x * 100,
y * 100
)
)
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.combatAlerter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("combatAlerterInCombat", locale),
source,
shared._L("zone", locale),
shared._L("subZone", locale),
tostring(areaId),
x * 100, x * 100,
y * 100 y * 100
) )
) ---@type Message
end local msg = {
channel = "C",
SetMapToCurrentZone() data = channel,
SetMapByID(GetCurrentMapAreaID()) message = text,
local areaId = GetCurrentMapAreaID() }
if Heimdall_Data.config.combatAlerter.debug then
for _, channel in pairs(Heimdall_Data.config.combatAlerter.channels) do print(string.format("[%s] Queuing alert message", ModuleName))
local locale = shared.GetLocaleForChannel(channel) shared.dump(msg)
local text = string.format( end
shared._L("combatAlerterInCombat", locale), table.insert(shared.messenger.queue, msg)
source,
shared._L("zone", locale),
shared._L("subZone", locale),
tostring(areaId),
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Queuing alert message", ModuleName))
shared.dumpTable(msg)
end end
table.insert(shared.messenger.queue, msg) elseif Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Source not in stinky list, ignoring: %s", ModuleName, source))
end end
elseif Heimdall_Data.config.combatAlerter.debug then end)
print(string.format("[%s] Source not in stinky list, ignoring: %s", ModuleName, source))
end
end)
local combatTriggerFrame = CreateFrame("Frame") local combatTriggerFrame = CreateFrame("Frame")
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_DISABLED") combatTriggerFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_ENABLED") combatTriggerFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatTriggerFrame:SetScript("OnEvent", function(self, event, ...) combatTriggerFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat state changed: %s", ModuleName, event)) print(string.format("[%s] Combat state changed: %s", ModuleName, event))
if event == "PLAYER_REGEN_DISABLED" then if event == "PLAYER_REGEN_DISABLED" then
print(string.format("[%s] Entered combat - Resetting alerts", ModuleName)) print(string.format("[%s] Entered combat - Resetting alerts", ModuleName))
else else
print(string.format("[%s] Left combat - Resetting alerts", ModuleName)) print(string.format("[%s] Left combat - Resetting alerts", ModuleName))
end
end end
end alerted = {}
alerted = {} end)
end)
if Heimdall_Data.config.combatAlerter.debug then print(string.format("[%s] Module initialized", ModuleName)) end if Heimdall_Data.config.combatAlerter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print("[Heimdall] CombatAlerter loaded") print(string.format("[%s] Module initialized", ModuleName))
end end,
}

View File

@@ -2,6 +2,13 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Commander" local ModuleName = "Commander"
---@class HeimdallCommanderConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field commander string
---@field commands table<string, boolean>
local helpMessages = { local helpMessages = {
ru = { ru = {
"1) who - пишет вам никнеймы текущих врагов и локу.", "1) who - пишет вам никнеймы текущих врагов и локу.",
@@ -31,315 +38,401 @@ local helpMessages = {
}, },
} }
---@diagnostic disable-next-line: missing-fields ---@class Commander
shared.Commander = {} shared.Commander = {
function shared.Commander.Init() Init = function()
---@param text string ---@param text string
---@param size number ---@param size number
---@return string[] ---@return string[]
local function Partition(text, size) local function Partition(text, size)
local words = {} local words = {}
for word in text:gmatch("[^,]+") do for word in text:gmatch("[^,]+") do
words[#words + 1] = word words[#words + 1] = word
end
local ret = {}
local currentChunk = ""
for _, word in ipairs(words) do
if #currentChunk + #word + 1 <= size then
currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
else
if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
currentChunk = word
end end
end
if #currentChunk > 0 then ret[#ret + 1] = currentChunk end local ret = {}
local currentChunk = ""
return ret for _, word in ipairs(words) do
end if #currentChunk + #word + 1 <= size then
---@param arr table<string, Player> currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
---@return string[] else
local function Count(arr) if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
local ret = {} currentChunk = word
for _, player in pairs(arr) do end
if shared.Whoer.ShouldNotifyForZone(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
---@param arr table<string, Player>
---@return string[]
local function CountPartitioned(arr)
local count = Count(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(count)), 200)) do
text[#text + 1] = line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function Who(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[#ret + 1] = string.format(
"%s/%s (%s) %s",
player.name,
player.class,
player.zone,
player.stinky and "(!!!!)" or ""
)
end end
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret))))
end
return ret
end
---@param arr table<string, Player>
---@return string[]
local function WhoPartitioned(arr)
local who = Who(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
text[#text + 1] = "who: " .. line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountClass(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(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
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Message text: %s", ModuleName, strjoin(", ", unpack(text))))
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountClassPartitioned(arr)
local countClass = CountClass(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do
text[#text + 1] = line
end
return text
end
local function CountClassPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: CountClassPartitionedStinkies", ModuleName))
end
local res = CountClassPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function WhoPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
shared.dumpTable(HeimdallStinkies)
end
local res = WhoPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function CountPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: CountPartitionedStinkies", ModuleName))
end
local res = CountPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function HelpRu()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpRu", ModuleName)) end
return helpMessages.ru
end
local function HelpEn()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpEn", ModuleName)) end
return helpMessages.en
end
local groupInviteFrame = CreateFrame("Frame")
groupInviteFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Event received", ModuleName)) end
AcceptGroup()
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(0.1, function()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Click event triggered", ModuleName))
end
_G["StaticPopup1Button1"]:Click()
end, 1)
end)
local function JoinGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] JoinGroup command received", ModuleName))
end
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(10, function() groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST") end, 1)
return { "+" }
end
local function LeaveGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] LeaveGroup command received", ModuleName))
end
LeaveParty()
return {}
end
---@param target string
local function FollowTarget(target)
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Following target: %s", ModuleName, target))
end
if not target then return end
FollowUnit(target)
return {}
end
---@param args string[] if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
local function MacroTarget(args)
if Heimdall_Data.config.commander.debug then return ret
end
---@param arr table<string, Player>
---@return string[]
local function Count(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(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
---@param arr table<string, Player>
---@return string[]
local function CountPartitioned(arr)
local count = Count(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args)))) for _, line in pairs(Partition(strjoin(", ", unpack(count)), 200)) do
text[#text + 1] = line
end
return text
end end
if #args < 2 or #args % 2 ~= 0 then ---@param arr table<string, Player>
if #args < 2 or #args % 2 ~= 0 then ---@return string[]
local function Who(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[#ret + 1] = string.format(
"%s/%s (%s) %s",
player.name,
player.class,
player.zone,
player.stinky and "(!!!!)" or ""
)
end
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret))))
end
return ret
end
-- This is really ugly, duplicating methods like this
-- But I have no better idea
-- We would have to drag reference channel all the way here
-- And then in here do some kind of deciding based on the fucking channel locale
-- That's also a nasty solution... I guess adding "kto" is better
---@param arr table<string, Player>
---@return string[]
local function WhoRu(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
shared.dump(player)
ret[#ret + 1] = string.format(
"%s/%s (%s) %s",
player.name,
shared._L(player.class, "ru"),
shared._L(player.zone, "ru"),
player.stinky and "(!!!!)" or ""
)
end
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret))))
end
return ret
end
---@param arr table<string, Player>
---@return string[]
local function WhoPartitioned(arr)
local who = Who(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
text[#text + 1] = "who: " .. line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function WhoPartitionedRu(arr)
local who = WhoRu(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
text[#text + 1] = "кто: " .. line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountClass(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(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
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Message text: %s", ModuleName, strjoin(", ", unpack(text))))
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountClassPartitioned(arr)
local countClass = CountClass(arr)
local text = {}
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do
text[#text + 1] = line
end
return text
end
local function CountClassPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: CountClassPartitionedStinkies", ModuleName))
end
local res = CountClassPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function WhoPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
shared.dump(HeimdallStinkies)
end
local res = WhoPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function WhoPartitionedStinkiesRu()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
shared.dump(HeimdallStinkies)
end
local res = WhoPartitionedRu(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function CountPartitionedStinkies()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Executing: CountPartitionedStinkies", ModuleName))
end
local res = CountPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end
local function HelpRu()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpRu", ModuleName)) end
return helpMessages.ru
end
local function HelpEn()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpEn", ModuleName)) end
return helpMessages.en
end
local groupInviteFrame = CreateFrame("Frame")
groupInviteFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Event received", ModuleName)) end
AcceptGroup()
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(0.1, function()
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Invalid number of arguments for MacroTarget", ModuleName)) print(string.format("[%s] Click event triggered", ModuleName))
end
_G["StaticPopup1Button1"]:Click()
end, 1)
end)
local function JoinGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] JoinGroup command received", ModuleName))
end
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(10, function() groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST") end, 1)
return { "+" }
end
local function LeaveGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] LeaveGroup command received", ModuleName))
end
LeaveParty()
return {}
end
---@param target string
local function FollowTarget(target)
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Following target: %s", ModuleName, target))
end
if not target then return end
FollowUnit(target)
return {}
end
---@param args string[]
local function MacroTarget(args)
if Heimdall_Data.config.commander.debug then
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args))))
end
if #args < 2 or #args % 2 ~= 0 then
if #args < 2 or #args % 2 ~= 0 then
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Invalid number of arguments for MacroTarget", ModuleName))
end
return {}
end
end
table.remove(args, 1)
for i = 1, #args do
local stinky = strtrim(args[i])
local name = stinky:match("([^/]+)")
local class = stinky:match("/([^ $]+)")
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class)))
end
shared.StinkyTracker.Track({
name = name,
class = class or "unknown",
seenAt = GetTime(),
hostile = true,
})
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Added stinky: %s/%s", ModuleName, name, tostring(class)))
end
end
return {}
end
---@param args string[]
local function IgnoreMacroTarget(args)
if Heimdall_Data.config.commander.debug then
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args))))
end
if #args < 1 then
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Invalid number of arguments for IgnoreMacroTarget", ModuleName))
end end
return {} return {}
end end
end table.remove(args, 1)
table.remove(args, 1)
for i = 1, #args do for i = 1, #args do
local stinky = strtrim(args[i]) local stinky = strtrim(args[i])
local name = stinky:match("([^/]+)") local name = stinky:match("([^/]+)")
local class = stinky:match("/([^ $]+)") if Heimdall_Data.config.commander.debug then
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring stinky: %s", ModuleName, name))
print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class))) end
shared.StinkyTracker.Ignore(name)
end end
shared.stinkyTracker.stinkies[name] = { return {}
name = name,
class = class or "unknown",
seenAt = GetTime(),
hostile = true,
}
end end
return {}
end
---@class Command ---@class Command
---@field keywordRe string ---@field keywordRe string
---@field commanderOnly boolean ---@field commanderOnly boolean
---@field callback fun(...: any): string[] ---@field callback fun(...: any): string[]
local commands = { local commands = {
{ keywordRe = "^who$", commanderOnly = false, callback = WhoPartitionedStinkies }, { keywordRe = "^who$", commanderOnly = false, callback = WhoPartitionedStinkies },
{ keywordRe = "^howmany$", commanderOnly = false, callback = CountPartitionedStinkies }, { keywordRe = "^кто$", commanderOnly = false, callback = WhoPartitionedStinkiesRu },
{ keywordRe = "^classes$", commanderOnly = false, callback = CountClassPartitionedStinkies }, { keywordRe = "^howmany$", commanderOnly = false, callback = CountPartitionedStinkies },
{ keywordRe = "^help$", commanderOnly = false, callback = HelpRu }, { keywordRe = "^classes$", commanderOnly = false, callback = CountClassPartitionedStinkies },
{ keywordRe = "^helpen$", commanderOnly = false, callback = HelpEn }, { keywordRe = "^help$", commanderOnly = false, callback = HelpRu },
{ keywordRe = "^joingroup$", commanderOnly = false, callback = JoinGroup }, { keywordRe = "^helpen$", commanderOnly = false, callback = HelpEn },
{ keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup }, { keywordRe = "^joingroup$", commanderOnly = false, callback = JoinGroup },
{ keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget }, { keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup },
{ keywordRe = "^macro", commanderOnly = false, callback = MacroTarget }, { keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget },
} { keywordRe = "^macro", commanderOnly = false, callback = MacroTarget },
{ keywordRe = "^ignore", commanderOnly = false, callback = IgnoreMacroTarget },
}
local commanderChannelFrame = CreateFrame("Frame") local commanderChannelFrame = CreateFrame("Frame")
commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.commander.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dumpTable(Heimdall_Data.config.commander)
--end
if not Heimdall_Data.config.commander.enabled then
--if Heimdall_Data.config.commander.debug then --if Heimdall_Data.config.commander.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) -- print(string.format("[%s] Event received", ModuleName))
-- shared.dump(Heimdall_Data.config.commander)
--end --end
return if not Heimdall_Data.config.commander.enabled then
end --if Heimdall_Data.config.commander.debug then
local channelId = select(6, ...) -- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
local _, channelname = GetChannelName(channelId) --end
local ok = false return
for _, channel in pairs(Heimdall_Data.config.commander.channels) do
if channel == channelname then
ok = true
break
end end
end local channelId = select(6, ...)
if not ok then local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.commander.debug then local ok = false
print( for _, channel in pairs(Heimdall_Data.config.commander.channels) do
string.format( if channel == channelname then
"[%s] Channel name '%s' does not match any of the channels '%s'", ok = true
ModuleName, break
channelname, end
table.concat(Heimdall_Data.config.commander.channels, ", ") end
if not ok then
if Heimdall_Data.config.commander.debug then
print(
string.format(
"[%s] Channel name '%s' does not match any of the channels '%s'",
ModuleName,
channelname,
table.concat(Heimdall_Data.config.commander.channels, ", ")
)
) )
) end
return
end end
return
end
sender = string.match(sender, "^[^-]+") sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.commander)
end
for _, command in ipairs(commands) do
local enabled = Heimdall_Data.config.commander.commands[command.keywordRe] == true or false
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command match: %s = %s", ModuleName, command.keywordRe, tostring(enabled))) print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.commander)
end end
if
enabled for _, command in ipairs(commands) do
and ( local enabled = Heimdall_Data.config.commander.commands[command.keywordRe] == true or false
not command.commanderOnly if Heimdall_Data.config.commander.debug then
or (command.commanderOnly and sender == Heimdall_Data.config.commander.commander) print(
) string.format("[%s] Command match: %s = %s", ModuleName, command.keywordRe, tostring(enabled))
then )
if msg:match(command.keywordRe) then end
---@diagnostic disable-next-line: redundant-parameter Currently luals does not support variadic functions as a @field if
local messages = command.callback({ strsplit(",", msg) }) enabled
if Heimdall_Data.config.commander.debug then and (
---@diagnostic disable-next-line: param-type-mismatch not command.commanderOnly
print(string.format("[%s] Messages to send: %s", ModuleName, strjoin(", ", unpack(messages)))) -- if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring command, sender %s not commander %s", ModuleName, sender, Heimdall_Data.config.commander.commander)) end
end
for _, message in ipairs(messages) do or (command.commanderOnly and sender == Heimdall_Data.config.commander.commander)
---@type Message )
local returnmsg = { then
channel = "C", if msg:match(command.keywordRe) then
data = channelname, ---@diagnostic disable-next-line: redundant-parameter Currently luals does not support variadic functions as a @field
message = message, local messages = command.callback({ strsplit(",", msg) })
}
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Queuing message", ModuleName)) print(string.format("[%s] Messages to send: %s", ModuleName, #messages))
shared.dumpTable(msg) shared.dump(messages)
end
for _, message in ipairs(messages) do
---@type Message
local returnmsg = {
channel = "C",
data = channelname,
message = message,
}
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Queuing message", ModuleName))
shared.dump(msg)
end
if Heimdall_Data.config.networkMessenger.enabled then
shared.NetworkMessenger.Enqueue(returnmsg)
elseif Heimdall_Data.config.messenger.enabled then
shared.Messenger.Enqueue(returnmsg)
end
end end
--table.insert(shared.messenger.queue, msg)
table.insert(shared.networkMessenger.queue, returnmsg)
end end
end end
end end
end end)
end)
print("[Heimdall] Commander module loaded") print(string.format("[%s] Module initialized", ModuleName))
end end,
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,11 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Configurator" local ModuleName = "Configurator"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallConfiguratorConfig
shared.Configurator = {} ---@field enabled boolean
function shared.Configurator.Init() print(string.format("[Heimdall] %s module loaded", ModuleName)) end ---@field debug boolean
---@class Configurator
shared.Configurator = {
Init = function() print(string.format("[%s] Module initialized", ModuleName)) end,
}

View File

@@ -2,93 +2,65 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "DeathReporter" local ModuleName = "DeathReporter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallDeathReporterConfig
shared.DeathReporter = {} ---@field enabled boolean
function shared.DeathReporter.Init() ---@field debug boolean
---@type table<string, number> ---@field throttle number
local recentDeaths = {} ---@field doWhisper boolean
---@type table<string, number> ---@field channels string[]
local recentDuels = {} ---@field zoneOverride string?
---@field duelThrottle number
---@param source string ---@class DeathReporter
---@param destination string shared.DeathReporter = {
---@param spellName string Init = function()
local function RegisterDeath(source, destination, spellName) ---@type table<string, number>
if Heimdall_Data.config.deathReporter.debug then local recentDeaths = {}
print( ---@type table<string, number>
string.format( local recentDuels = {}
"[%s] Processing death event - Source: %s, Target: %s, Spell: %s",
ModuleName,
source,
destination,
spellName
)
)
end
if not Heimdall_Data.config.deathReporter.enabled then ---@param source string
---@param destination string
---@param spellName string
local function RegisterDeath(source, destination, spellName)
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Module disabled, ignoring death event", ModuleName))
end
return
end
if
recentDeaths[destination]
and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle
then
if Heimdall_Data.config.deathReporter.debug then
local timeLeft = Heimdall_Data.config.deathReporter.throttle - (GetTime() - recentDeaths[destination])
print( print(
string.format( string.format(
"[%s] Death report throttled for %s (%.1f seconds remaining)", "[%s] Processing death event - Source: %s, Target: %s, Spell: %s",
ModuleName, ModuleName,
source,
destination, destination,
timeLeft spellName
) )
) )
end end
return
end
if if not Heimdall_Data.config.deathReporter.enabled then
recentDuels[destination] if Heimdall_Data.config.deathReporter.debug then
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle print(string.format("[%s] Module disabled, ignoring death event", ModuleName))
then end
if Heimdall_Data.config.deathReporter.debug then return
print(
string.format(
"[%s] Ignoring death report - Recent duel detected for target: %s",
ModuleName,
destination
)
)
end end
return
end
if if
recentDuels[source] recentDeaths[destination]
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle
then then
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print( local timeLeft = Heimdall_Data.config.deathReporter.throttle
string.format( - (GetTime() - recentDeaths[destination])
"[%s] Ignoring death report - Recent duel detected for source: %s", print(
ModuleName, string.format(
source "[%s] Death report throttled for %s (%.1f seconds remaining)",
ModuleName,
destination,
timeLeft
)
) )
) end
return
end end
return
end
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Recording death for %s", ModuleName, destination))
end
recentDeaths[destination] = GetTime()
C_Timer.NewTimer(3, function()
if if
recentDuels[destination] recentDuels[destination]
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle
@@ -96,7 +68,7 @@ function shared.DeathReporter.Init()
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print( print(
string.format( string.format(
"[%s] Cancelling delayed death report - Recent duel detected for: %s", "[%s] Ignoring death report - Recent duel detected for target: %s",
ModuleName, ModuleName,
destination destination
) )
@@ -112,7 +84,7 @@ function shared.DeathReporter.Init()
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print( print(
string.format( string.format(
"[%s] Cancelling delayed death report - Recent duel detected for: %s", "[%s] Ignoring death report - Recent duel detected for source: %s",
ModuleName, ModuleName,
source source
) )
@@ -122,131 +94,175 @@ function shared.DeathReporter.Init()
end end
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print( print(string.format("[%s] Recording death for %s", ModuleName, destination))
string.format( end
"[%s] Sending death report - %s killed %s with %s", recentDeaths[destination] = GetTime()
ModuleName,
C_Timer.NewTimer(3, function()
if
recentDuels[destination]
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle
then
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Cancelling delayed death report - Recent duel detected for: %s",
ModuleName,
destination
)
)
end
return
end
if
recentDuels[source]
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle
then
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Cancelling delayed death report - Recent duel detected for: %s",
ModuleName,
source
)
)
end
return
end
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Sending death report - %s killed %s with %s",
ModuleName,
source,
destination,
spellName
)
)
end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown"
if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Player coordinates: %.2f, %.2f", ModuleName, x * 100, y * 100))
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local zoneId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.deathReporter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("killed", locale),
source, source,
destination, destination,
spellName shared._L(spellName, locale),
shared._L(zone, locale),
shared._L(subzone, locale),
zoneId,
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Queuing death report message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end
end)
end
local cleuFrame = CreateFrame("Frame")
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
cleuFrame:SetScript("OnEvent", function(self, event, ...)
-- if Heimdall_Data.config.deathReporter.debug then
-- print(string.format("[%s] Received combat log event", ModuleName))
-- end
if not Heimdall_Data.config.deathReporter.enabled then return end
local overkill, source, destination, spellName, sourceGUID, destinationGUID, err
overkill, err = CLEUParser.GetOverkill(...)
if not err and overkill > 0 then
source, err = CLEUParser.GetSourceName(...)
if err then
source = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting source name", ModuleName))
end
end
destination, err = CLEUParser.GetDestName(...)
if err then
destination = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting destination name", ModuleName))
end
end
spellName, err = CLEUParser.GetSpellName(...)
if err then
spellName = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting spell name", ModuleName))
end
end
sourceGUID, err = CLEUParser.GetSourceGUID(...)
if err or not string.match(sourceGUID, "Player") then return end
destinationGUID, err = CLEUParser.GetDestGUID(...)
if err or not string.match(destinationGUID, "Player") then return end
RegisterDeath(source, destination, spellName)
end
end)
local systemMessageFrame = CreateFrame("Frame")
systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM")
systemMessageFrame:SetScript("OnEvent", function(self, event, msg)
if not Heimdall_Data.config.deathReporter.enabled then return end
local source, destination = string.match(msg, "([^ ]+) has defeated ([^ ]+) in a duel")
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Received system message: %s", ModuleName, msg))
print(
string.format(
"[%s] Source: %s, Destination: %s",
ModuleName,
tostring(source),
tostring(destination)
) )
) )
end end
if not source or not destination then return end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown" source = string.match(source, "([^-]+)")
if Heimdall_Data.config.spotter.zoneOverride then destination = string.match(destination, "([^-]+)")
zone = Heimdall_Data.config.spotter.zoneOverride or "" if source and destination then
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Player coordinates: %.2f, %.2f", ModuleName, x * 100, y * 100))
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local zoneId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.deathReporter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("killed", locale),
source,
destination,
shared._L(spellName, locale),
shared._L(zone, locale),
shared._L(subzone, locale),
zoneId,
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Queuing death report message", ModuleName)) print(string.format("[%s] Detected duel between %s and %s", ModuleName, source, destination))
shared.dumpTable(msg)
end end
table.insert(shared.messenger.queue, msg) local now = GetTime()
recentDuels[source] = now
recentDuels[destination] = now
end end
end) end)
end
local cleuFrame = CreateFrame("Frame")
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
cleuFrame:SetScript("OnEvent", function(self, event, ...)
-- if Heimdall_Data.config.deathReporter.debug then
-- print(string.format("[%s] Received combat log event", ModuleName))
-- end
if not Heimdall_Data.config.deathReporter.enabled then return end
local overkill, source, destination, spellName, sourceGUID, destinationGUID, err
overkill, err = CLEUParser.GetOverkill(...)
if not err and overkill > 0 then
source, err = CLEUParser.GetSourceName(...)
if err then
source = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting source name", ModuleName))
end
end
destination, err = CLEUParser.GetDestName(...)
if err then
destination = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting destination name", ModuleName))
end
end
spellName, err = CLEUParser.GetSpellName(...)
if err then
spellName = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting spell name", ModuleName))
end
end
sourceGUID, err = CLEUParser.GetSourceGUID(...)
if err or not string.match(sourceGUID, "Player") then return end
destinationGUID, err = CLEUParser.GetDestGUID(...)
if err or not string.match(destinationGUID, "Player") then return end
RegisterDeath(source, destination, spellName)
end
end)
local systemMessageFrame = CreateFrame("Frame")
systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM")
systemMessageFrame:SetScript("OnEvent", function(self, event, msg)
if not Heimdall_Data.config.deathReporter.enabled then return end
local source, destination = string.match(msg, "([^ ]+) has defeated ([^ ]+) in a duel")
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Received system message: %s", ModuleName, msg))
print( print(
string.format("[%s] Source: %s, Destination: %s", ModuleName, tostring(source), tostring(destination)) string.format(
"[%s] Module initialized with throttle: %.1fs, duel throttle: %.1fs",
ModuleName,
Heimdall_Data.config.deathReporter.throttle,
Heimdall_Data.config.deathReporter.duelThrottle
)
) )
end end
if not source or not destination then return end print(string.format("[%s] Module initialized", ModuleName))
source = string.match(source, "([^-]+)") end,
destination = string.match(destination, "([^-]+)") }
if source and destination then
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Detected duel between %s and %s", ModuleName, source, destination))
end
local now = GetTime()
recentDuels[source] = now
recentDuels[destination] = now
end
end)
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Module initialized with throttle: %.1fs, duel throttle: %.1fs",
ModuleName,
Heimdall_Data.config.deathReporter.throttle,
Heimdall_Data.config.deathReporter.duelThrottle
)
)
end
print("[Heimdall] DeathReporter loaded")
end

View File

@@ -2,54 +2,62 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Dueler" local ModuleName = "Dueler"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallDuelerConfig
shared.Dueler = {} ---@field enabled boolean
function shared.Dueler.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field declineOther boolean
frame:RegisterEvent("DUEL_REQUESTED")
frame:SetScript("OnEvent", function(self, event, sender)
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Duel request received from: %s", ModuleName, sender))
end
if not Heimdall_Data.config.dueler.enabled then
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Module disabled, ignoring duel request", ModuleName))
end
return
end
if Heimdall_Data.config.dueler.debug then ---@class Dueler
print(string.format("[%s] Checking if sender '%s' is in agents list", ModuleName, sender)) shared.Dueler = {
end Init = function()
local frame = CreateFrame("Frame")
local allow = Heimdall_Data.config.agents[sender] frame:RegisterEvent("DUEL_REQUESTED")
if allow then frame:SetScript("OnEvent", function(self, event, sender)
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Accepting duel from trusted agent: %s", ModuleName, sender)) print(string.format("[%s] Duel request received from: %s", ModuleName, sender))
end end
AcceptDuel() if not Heimdall_Data.config.dueler.enabled then
else
if Heimdall_Data.config.dueler.declineOther then
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Auto-declining duel from untrusted sender: %s", ModuleName, sender)) print(string.format("[%s] Module disabled, ignoring duel request", ModuleName))
end end
CancelDuel() return
end
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Checking if sender '%s' is in agents list", ModuleName, sender))
end
local allow = shared.AgentTracker.IsAgent(sender)
if allow then
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Accepting duel from trusted agent: %s", ModuleName, sender))
end
AcceptDuel()
else else
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.declineOther then
print(string.format("[%s] Leaving duel request from %s for manual response", ModuleName, sender)) if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Auto-declining duel from untrusted sender: %s", ModuleName, sender))
end
CancelDuel()
else
if Heimdall_Data.config.dueler.debug then
print(
string.format("[%s] Leaving duel request from %s for manual response", ModuleName, sender)
)
end
end end
end end
end end)
end)
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print( print(
string.format( string.format(
"[%s] Module initialized with auto-decline: %s", "[%s] Module initialized with auto-decline: %s",
ModuleName, ModuleName,
tostring(Heimdall_Data.config.dueler.declineOther) tostring(Heimdall_Data.config.dueler.declineOther)
)
) )
) end
end print(string.format("[%s] Module initialized", ModuleName))
print("[Heimdall] Dueler loaded") end,
end }

View File

@@ -1,25 +1,31 @@
local _, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
if not shared.dumpTable then if not shared.dump then
---@param table table ---@param value any
---@param msg string?
---@param depth number? ---@param depth number?
shared.dumpTable = function(table, depth) shared.dump = function(value, msg, depth)
if not table then if not value then
print(tostring(table)) print(tostring(value))
return return
end end
if type(value) ~= "table" then
print(tostring(value))
return
end
if msg then print(msg) end
if depth == nil then depth = 0 end if depth == nil then depth = 0 end
if depth > 200 then if depth > 200 then
print("Error: Depth > 200 in dumpTable()") print("Error: Depth > 200 in dump()")
return return
end end
for k, v in pairs(table) do for k, v in pairs(value) do
if type(v) == "table" then if type(v) == "table" then
print(string.rep(" ", depth) .. k .. ":") print(string.rep(" ", depth) .. tostring(k) .. ":")
shared.dumpTable(v, depth + 1) shared.dump(v, msg, depth + 1)
else else
print(string.rep(" ", depth) .. k .. ": ", v) print(string.rep(" ", depth) .. tostring(k) .. ": " .. tostring(v))
end end
end end
end end

View File

@@ -2,57 +2,64 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Echoer" local ModuleName = "Echoer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallEchoerConfig
shared.Echoer = {} ---@field enabled boolean
function shared.Echoer.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field channels string[]
frame:RegisterEvent("CHAT_MSG_CHANNEL") ---@field prefix string
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.echoer.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end
if not Heimdall_Data.config.echoer.enabled then ---@class Echoer
shared.Echoer = {
Init = function()
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.echoer.debug then --if Heimdall_Data.config.echoer.debug then
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end --end
return
end
local channelId = select(6, ...) if not Heimdall_Data.config.echoer.enabled then
local _, channelname = GetChannelName(channelId) --if Heimdall_Data.config.echoer.debug then
local ok = false -- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
for _, channel in pairs(Heimdall_Data.config.echoer.channels) do --end
if channel == channelname then return
ok = true
break
end end
end
if not ok then
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.echoer)
end
if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then local channelId = select(6, ...)
if Heimdall_Data.config.echoer.debug then local _, channelname = GetChannelName(channelId)
print(string.format("[%s] Found echo command in message: %s", ModuleName, msg)) local ok = false
for _, channel in pairs(Heimdall_Data.config.echoer.channels) do
if channel == channelname then
ok = true
break
end
end end
local echomsg = string.sub(msg, string.len(Heimdall_Data.config.echoer.prefix) + 1) if not ok then
if Heimdall_Data.config.echoer.debug then if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Echoing message: %s", ModuleName, echomsg)) print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.echoer)
end end
SendChatMessage(echomsg, "SAY")
elseif Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Message does not start with echo prefix", ModuleName))
end
end)
if Heimdall_Data.config.echoer.debug then print(string.format("[%s] Module initialized", ModuleName)) end if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then
print("[Heimdall] Echoer loaded") if Heimdall_Data.config.echoer.debug then
end print(string.format("[%s] Found echo command in message: %s", ModuleName, msg))
end
local echomsg = string.sub(msg, string.len(Heimdall_Data.config.echoer.prefix) + 1)
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Echoing message: %s", ModuleName, echomsg))
end
SendChatMessage(echomsg, "SAY")
elseif Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Message does not start with echo prefix", ModuleName))
end
end)
if Heimdall_Data.config.echoer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,58 +2,65 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Emoter" local ModuleName = "Emoter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallEmoterConfig
shared.Emoter = {} ---@field enabled boolean
function shared.Emoter.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field channels string[]
frame:RegisterEvent("CHAT_MSG_CHANNEL") ---@field prefix string
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.emoter.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end
if not Heimdall_Data.config.emoter.enabled then ---@class Emoter
shared.Emoter = {
Init = function()
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.emoter.debug then --if Heimdall_Data.config.emoter.debug then
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end --end
return
end
local channelId = select(6, ...) if not Heimdall_Data.config.emoter.enabled then
local _, channelname = GetChannelName(channelId) --if Heimdall_Data.config.emoter.debug then
local ok = false -- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
for _, channel in pairs(Heimdall_Data.config.emoter.channels) do --end
if channel == channelname then return
ok = true
break
end end
end
if not ok then local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.emoter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.emoter.debug then if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.emoter)
end end
return
end
if Heimdall_Data.config.emoter.debug then if string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) if Heimdall_Data.config.emoter.debug then
shared.dumpTable(Heimdall_Data.config.emoter) print(string.format("[%s] Found emote command in message: %s", ModuleName, msg))
end end
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1)
if string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then if Heimdall_Data.config.emoter.debug then
if Heimdall_Data.config.emoter.debug then print(string.format("[%s] Performing emote: %s", ModuleName, emote))
print(string.format("[%s] Found emote command in message: %s", ModuleName, msg)) end
DoEmote(emote)
elseif Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Message does not start with emote prefix", ModuleName))
end end
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1) end)
if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Performing emote: %s", ModuleName, emote))
end
DoEmote(emote)
elseif Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Message does not start with emote prefix", ModuleName))
end
end)
if Heimdall_Data.config.emoter.debug then print(string.format("[%s] Module initialized", ModuleName)) end if Heimdall_Data.config.emoter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print("[Heimdall] Emoter loaded") print(string.format("[%s] Module initialized", ModuleName))
end end,
}

View File

@@ -2,262 +2,274 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Inviter" local ModuleName = "Inviter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallInviterConfig
shared.Inviter = {} ---@field enabled boolean
function shared.Inviter.Init() ---@field debug boolean
-- Fallback for old config ---@field channels string[]
if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then ---@field keyword string
Heimdall_Data.config.inviter.listeningChannel = { ---@field allAssist boolean
[Heimdall_Data.config.inviter.listeningChannel] = true, ---@field agentsAssist boolean
} ---@field throttle number
end ---@field kickOffline boolean
---@type Timer ---@field cleanupInterval number
local updateTimer = nil ---@field afkThreshold number
---@field listeningChannel table<string, boolean>
local function FixGroup() ---@class Inviter
if Heimdall_Data.config.inviter.debug then shared.Inviter = {
print(string.format("[%s] Checking and fixing group configuration", ModuleName)) Init = function()
-- Fallback for old config
if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then
Heimdall_Data.config.inviter.listeningChannel = {
[Heimdall_Data.config.inviter.listeningChannel] = true,
}
end end
---@type Timer
local updateTimer = nil
if not IsInRaid() then local function FixGroup()
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Converting party to raid", ModuleName)) print(string.format("[%s] Checking and fixing group configuration", ModuleName))
end
ConvertToRaid()
end
if Heimdall_Data.config.inviter.allAssist then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Setting all members to assistant", ModuleName))
end
SetEveryoneIsAssistant()
end
if Heimdall_Data.config.inviter.agentsAssist then
if Heimdall_Data.config.inviter.debug then
local agentCount = 0
for _ in pairs(Heimdall_Data.config.agents) do
agentCount = agentCount + 1
end
print(string.format("[%s] Processing %d agents for assistant promotion", ModuleName, agentCount))
end end
for name, _ in pairs(Heimdall_Data.config.agents) do if not IsInRaid() then
if UnitInParty(name) and not UnitIsGroupLeader(name) and not UnitIsRaidOfficer(name) then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Promoting agent to assistant: %s", ModuleName, name))
end
PromoteToAssistant(name, true)
elseif Heimdall_Data.config.inviter.debug then
if not UnitInParty(name) then
print(string.format("[%s] Agent not in party: %s", ModuleName, name))
elseif UnitIsGroupLeader(name) then
print(string.format("[%s] Agent is already leader: %s", ModuleName, name))
elseif UnitIsRaidOfficer(name) then
print(string.format("[%s] Agent is already assistant: %s", ModuleName, name))
end
end
end
end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group configuration update complete", ModuleName))
end
end
---@param name string
---@return Frame?
local function FindPlayerRaidFrame(name)
for group = 1, 8 do
for player = 1, 5 do
local button = _G[string.format("ElvUF_RaidGroup%dUnitButton%d", group, player)]
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] button = %s", ModuleName, tostring(button))) print(string.format("[%s] Converting party to raid", ModuleName))
end
local unitName = button and button.unit and UnitName(button.unit)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName = %s", ModuleName, tostring(unitName)))
end
if unitName == name then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName == name", ModuleName))
end
return button
end end
ConvertToRaid()
end end
end
return nil
end
local framePool = {} if Heimdall_Data.config.inviter.allAssist then
local playerButtons = {} if Heimdall_Data.config.inviter.debug then
setmetatable(playerButtons, { __mode = "kv" }) print(string.format("[%s] Setting all members to assistant", ModuleName))
---@param players string[] end
local function OverlayKickButtons(players) SetEveryoneIsAssistant()
for _, frame in pairs(framePool) do end
frame:Hide()
end
for _, name in pairs(players) do
local frame = FindPlayerRaidFrame(name)
if frame then
playerButtons[name] = frame
-- All of these are ELVUI specific so they won't be in our meta...
---@diagnostic disable-next-line: undefined-field
local button = framePool[frame.unit]
or CreateFrame(
"Button",
---@diagnostic disable-next-line: undefined-field
string.format("HeimdallKickButton%s", frame.unit, frame, "SecureActionButtonTemplate")
)
---@diagnostic disable-next-line: undefined-field
framePool[frame.unit] = button
---@diagnostic disable-next-line: undefined-field if Heimdall_Data.config.inviter.agentsAssist then
button:SetSize(frame.UNIT_WIDTH / 2, frame.UNIT_HEIGHT / 2) if Heimdall_Data.config.inviter.debug then
button:SetPoint("CENTER", frame, "CENTER", 0, 0) print(string.format("[%s] Processing agents for assistant promotion", ModuleName))
button:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") end
button:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") shared.AgentTracker.ForEach(function(agent)
button:SetDisabledTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") if UnitInParty(agent) and not UnitIsGroupLeader(agent) and not UnitIsRaidOfficer(agent) then
button:SetAlpha(0.5) if Heimdall_Data.config.inviter.debug then
button:Show() print(string.format("[%s] Promoting agent to assistant: %s", ModuleName, agent))
button:SetScript("OnClick", function() end
UninviteUnit(name) PromoteToAssistant(agent, true)
button:Hide() elseif Heimdall_Data.config.inviter.debug then
if not UnitInParty(agent) then
print(string.format("[%s] Agent not in party: %s", ModuleName, agent))
elseif UnitIsGroupLeader(agent) then
print(string.format("[%s] Agent is already leader: %s", ModuleName, agent))
elseif UnitIsRaidOfficer(agent) then
print(string.format("[%s] Agent is already assistant: %s", ModuleName, agent))
end
end
end) end)
else end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Frame for player %s not found", ModuleName, name)) if Heimdall_Data.config.inviter.debug then
end print(string.format("[%s] Group configuration update complete", ModuleName))
end end
end end
end
---@type table<string, number> ---@param name string
local groupMembers = {} ---@return Frame?
local function CleanGroups() local function FindPlayerRaidFrame(name)
if not Heimdall_Data.config.inviter.kickOffline then return end for group = 1, 8 do
print("Cleaning groups") for player = 1, 5 do
local now = GetTime() local button = _G[string.format("ElvUF_RaidGroup%dUnitButton%d", group, player)]
for i = 1, 40 do if Heimdall_Data.config.inviter.debug then
local unit = "raid" .. i print(string.format("[%s] button = %s", ModuleName, tostring(button)))
if UnitExists(unit) then end
local name = UnitName(unit)
if name then local unitName = button and button.unit and UnitName(button.unit)
-- When we load (into game) we want to consider everyone "online" if Heimdall_Data.config.inviter.debug then
-- In other words everyone we haven't seen before is "online" the first time we see them print(string.format("[%s] unitName = %s", ModuleName, tostring(unitName)))
-- This is to avoid kicking people who might not be offline which we don't know because we just logged in end
if not groupMembers[name] then if unitName == name then
groupMembers[name] = now if Heimdall_Data.config.inviter.debug then
else print(string.format("[%s] unitName == name", ModuleName))
local online = UnitIsConnected(unit) end
if online then groupMembers[name] = now end return button
end
end
end
return nil
end
local framePool = {}
local playerButtons = {}
setmetatable(playerButtons, { __mode = "kv" })
---@param players string[]
local function OverlayKickButtons(players)
for _, frame in pairs(framePool) do
frame:Hide()
end
for _, name in pairs(players) do
local frame = FindPlayerRaidFrame(name)
if frame then
playerButtons[name] = frame
-- All of these are ELVUI specific so they won't be in our meta...
---@diagnostic disable-next-line: undefined-field
local button = framePool[frame.unit]
or CreateFrame(
"Button",
---@diagnostic disable-next-line: undefined-field
string.format("HeimdallKickButton%s", frame.unit, frame, "SecureActionButtonTemplate")
)
---@diagnostic disable-next-line: undefined-field
framePool[frame.unit] = button
---@diagnostic disable-next-line: undefined-field
button:SetSize(frame.UNIT_WIDTH / 2, frame.UNIT_HEIGHT / 2)
button:SetPoint("CENTER", frame, "CENTER", 0, 0)
button:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetDisabledTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetAlpha(0.5)
button:Show()
button:SetScript("OnClick", function()
UninviteUnit(name)
button:Hide()
end)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Frame for player %s not found", ModuleName, name))
end end
end end
end end
end end
local afkPlayers = {}
for name, time in pairs(groupMembers) do ---@type table<string, number>
if not UnitInParty(name) then local groupMembers = {}
print(string.format("%s no longer in party", name)) local function CleanGroups()
groupMembers[name] = nil if not Heimdall_Data.config.inviter.kickOffline then return end
else print("Cleaning groups")
if time < now - Heimdall_Data.config.inviter.afkThreshold then local now = GetTime()
print(string.format("Kicking %s for being offline", name)) for i = 1, 40 do
afkPlayers[#afkPlayers + 1] = name local unit = "raid" .. i
-- Blyat this is protected... if UnitExists(unit) then
-- UninviteUnit(name) local name = UnitName(unit)
if name then
-- When we load (into game) we want to consider everyone "online"
-- In other words everyone we haven't seen before is "online" the first time we see them
-- This is to avoid kicking people who might not be offline which we don't know because we just logged in
if not groupMembers[name] then
groupMembers[name] = now
else
local online = UnitIsConnected(unit)
if online then groupMembers[name] = now end
end
end
end end
end end
end local afkPlayers = {}
OverlayKickButtons(afkPlayers) for name, time in pairs(groupMembers) do
end if not UnitInParty(name) then
local function Tick() print(string.format("%s no longer in party", name))
CleanGroups() groupMembers[name] = nil
C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1) else
end if time < now - Heimdall_Data.config.inviter.afkThreshold then
Tick() print(string.format("Kicking %s for being offline", name))
afkPlayers[#afkPlayers + 1] = name
local groupRosterUpdateFrame = CreateFrame("Frame") -- Blyat this is protected...
groupRosterUpdateFrame:RegisterEvent("GROUP_ROSTER_UPDATE") -- UninviteUnit(name)
groupRosterUpdateFrame:SetScript("OnEvent", function(self, event, ...) end
if Heimdall_Data.config.inviter.debug then end
print(string.format("[%s] Event received: %s", ModuleName, event))
end
if not Heimdall_Data.config.inviter.enabled then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end end
return OverlayKickButtons(afkPlayers)
end end
local function Tick()
CleanGroups()
C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1)
end
Tick()
local groupRosterUpdateFrame = CreateFrame("Frame")
groupRosterUpdateFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
groupRosterUpdateFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Event received: %s", ModuleName, event))
end
if not Heimdall_Data.config.inviter.enabled then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end
return
end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group roster changed - Checking configuration", ModuleName))
end
if updateTimer then updateTimer:Cancel() end
updateTimer = C_Timer.NewTimer(Heimdall_Data.config.inviter.throttle, FixGroup)
end)
local inviterChannelFrame = CreateFrame("Frame")
inviterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
inviterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.inviter.debug then
-- print(string.format("[%s] Chat message received: %s", ModuleName, msg))
-- shared.dump(Heimdall_Data.config.inviter)
--end
if not Heimdall_Data.config.inviter.enabled then return end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name: %s", ModuleName, channelname))
end
local ok = false
for _, channel in pairs(Heimdall_Data.config.inviter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if msg == Heimdall_Data.config.inviter.keyword then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Inviting %s", ModuleName, sender))
end
InviteUnit(sender)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Message does not match keyword", ModuleName))
end
end
end)
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group roster changed - Checking configuration", ModuleName)) print(
end string.format(
if updateTimer then updateTimer:Cancel() end "[%s] Module initialized - All assist: %s, Agents assist: %s",
updateTimer = C_Timer.NewTimer(Heimdall_Data.config.inviter.throttle, FixGroup) ModuleName,
end) tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)
local inviterChannelFrame = CreateFrame("Frame") )
inviterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
inviterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.inviter.debug then
-- print(string.format("[%s] Chat message received: %s", ModuleName, msg))
-- shared.dumpTable(Heimdall_Data.config.inviter)
--end
if not Heimdall_Data.config.inviter.enabled then return end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name: %s", ModuleName, channelname))
end
local ok = false
for _, channel in pairs(Heimdall_Data.config.inviter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if msg == Heimdall_Data.config.inviter.keyword then
if Heimdall_Data.config.inviter.debug then print(string.format("[%s] Inviting %s", ModuleName, sender)) end
InviteUnit(sender)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Message does not match keyword", ModuleName))
end
end
end)
if Heimdall_Data.config.inviter.debug then
print(
string.format(
"[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)
) )
) end
end
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print( print(
string.format( string.format(
"[%s] Module initialized - All assist: %s, Agents assist: %s", "[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName, ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist), tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist) tostring(Heimdall_Data.config.inviter.agentsAssist)
)
) )
) end
end print(string.format("[%s] Module initialized", ModuleName))
print("[Heimdall] Inviter loaded") end,
end }

View File

@@ -2,100 +2,100 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Macroer" local ModuleName = "Macroer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallMacroerConfig
shared.Macroer = {} ---@field enabled boolean
function shared.Macroer.Init() ---@field debug boolean
---@class stinky ---@field priority string[]
---@field name string
---@field class string
---@field seenAt number
---@field hostile boolean
local function FindOrCreateMacro(macroName) ---@class Macroer
if Heimdall_Data.config.macroer.debug then shared.Macroer = {
print(string.format("[%s] Finding or creating macro: %s", ModuleName, macroName)) Init = function()
end local function FindOrCreateMacro(macroName)
local idx = GetMacroIndexByName(macroName)
if idx == 0 then
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Creating new macro: %s", ModuleName, macroName)) print(string.format("[%s] Finding or creating macro: %s", ModuleName, macroName))
end end
CreateMacro(macroName, "INV_Misc_QuestionMark", "") local idx = GetMacroIndexByName(macroName)
end if idx == 0 then
idx = GetMacroIndexByName(macroName)
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Macro index: %d", ModuleName, idx)) end
return idx
end
---@param stinkies table<string, stinky>
local function FixMacro(stinkies)
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Fixing macro with %d stinkies", ModuleName, #stinkies))
end
if not Heimdall_Data.config.macroer.enabled then
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Module disabled, skipping macro update", ModuleName))
end
return
end
if InCombatLockdown() then
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] In combat, skipping macro update", ModuleName))
end
return
end
local priorityMap = {}
for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do
priorityMap[className] = priority
end
local minPriority = #Heimdall_Data.config.macroer.priority + 1
local sortedStinkies = {}
for _, stinky in pairs(stinkies) do
if not Heimdall_Data.config.agents[stinky.name] then sortedStinkies[#sortedStinkies + 1] = stinky end
end
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Processing %d non-agent stinkies", ModuleName, #sortedStinkies))
end
table.sort(sortedStinkies, function(a, b)
local aPriority = priorityMap[a.class] or minPriority
local bPriority = priorityMap[b.class] or minPriority
return aPriority > bPriority
end)
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Sorted stinkies: %d", ModuleName, #sortedStinkies))
shared.dumpTable(sortedStinkies)
end
local lines = { "/targetenemy" }
for _, stinky in pairs(sortedStinkies) do
if stinky.seenAt > GetTime() - 600 then
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Adding target macro for: %s", ModuleName, stinky.name)) print(string.format("[%s] Creating new macro: %s", ModuleName, macroName))
end end
lines[#lines + 1] = string.format("/tar %s", stinky.name) CreateMacro(macroName, "INV_Misc_QuestionMark", "")
end end
idx = GetMacroIndexByName(macroName)
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Macro index: %d", ModuleName, idx)) end
return idx
end end
local idx = FindOrCreateMacro("HeimdallTarget") ---@param stinkies table<string, Stinky>
---@diagnostic disable-next-line: param-type-mismatch local function FixMacro(stinkies)
local body = strjoin("\n", unpack(lines)) if Heimdall_Data.config.macroer.debug then
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Fixing macro with %d stinkies", ModuleName, #stinkies))
print(string.format("[%s] Updating macro with %d lines", ModuleName, #lines)) end
end if not Heimdall_Data.config.macroer.enabled then
EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body) if Heimdall_Data.config.macroer.debug then
end print(string.format("[%s] Module disabled, skipping macro update", ModuleName))
end
return
end
if InCombatLockdown() then
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] In combat, skipping macro update", ModuleName))
end
return
end
shared.stinkyTracker.stinkies:onChange(function(value) local priorityMap = {}
if Heimdall_Data.config.macroer.debug then for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do
print(string.format("[%s] Stinkies changed, updating macro", ModuleName)) priorityMap[className] = priority
end end
FixMacro(value) local minPriority = #Heimdall_Data.config.macroer.priority + 1
end)
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Module initialized", ModuleName)) end local sortedStinkies = {}
print("[Heimdall] Macroer loaded") for _, stinky in pairs(stinkies) do
end if not shared.AgentTracker.IsAgent(stinky.name) then sortedStinkies[#sortedStinkies + 1] = stinky end
end
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Processing %d non-agent stinkies", ModuleName, #sortedStinkies))
end
table.sort(sortedStinkies, function(a, b)
local aPriority = priorityMap[a.class] or minPriority
local bPriority = priorityMap[b.class] or minPriority
return aPriority > bPriority
end)
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Sorted stinkies: %d", ModuleName, #sortedStinkies))
shared.dump(sortedStinkies)
end
local lines = { "/targetenemy" }
for _, stinky in pairs(sortedStinkies) do
if stinky.seenAt > GetTime() - 600 then
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Adding target macro for: %s", ModuleName, stinky.name))
end
lines[#lines + 1] = string.format("/tar %s", stinky.name)
end
end
local idx = FindOrCreateMacro("HeimdallTarget")
---@diagnostic disable-next-line: param-type-mismatch
local body = strjoin("\n", unpack(lines))
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Updating macro with %d lines", ModuleName, #lines))
end
EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body)
end
shared.StinkyTracker.OnChange(function(stinkies)
if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Stinkies changed, updating macro", ModuleName))
shared.dump(stinkies)
end
FixMacro(stinkies)
end)
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,175 +2,175 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Messenger" local ModuleName = "Messenger"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallMessengerConfig
shared.Messenger = {} ---@field enabled boolean
function shared.Messenger.Init() ---@field debug boolean
---@class Message ---@field interval number
---@field message string
---@field channel string
---@field data string
local function FindOrJoinChannel(channelName, password) ---@class HeimdallMessengerData
local channelId = GetChannelName(channelName) ---@field queue ReactiveValue<table<string, Message>>
if channelId == 0 then ---@field ticker Timer?
---@class Message
---@field message string
---@field channel string
---@field data string
---@class Messenger
shared.Messenger = {
---@param message Message
Enqueue = function(message) table.insert(shared.messenger.queue, message) end,
Init = function()
shared.messenger = {
queue = ReactiveValue.new({}),
}
local function FindOrJoinChannel(channelName, password)
local channelId = GetChannelName(channelName)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName))
end
if password then
JoinPermanentChannel(channelName, password)
else
JoinPermanentChannel(channelName)
end
end
channelId = GetChannelName(channelName)
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName)) print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName))
end
if password then
JoinPermanentChannel(channelName, password)
else
JoinPermanentChannel(channelName)
end end
return channelId
end end
channelId = GetChannelName(channelName)
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName))
end
return channelId
end
---@diagnostic disable-next-line: missing-fields if not shared.messenger.ticker then
if not shared.messenger then shared.messenger = {} end local function DoMessage()
if not shared.messenger.queue then shared.messenger.queue = {} end -- if Heimdall_Data.config.messenger.debug then
if not shared.messenger.ticker then -- print(
local function DoMessage() -- string.format(
if Heimdall_Data.config.messenger.debug then -- "[%s] Processing message queue - Size: %d",
print(string.format("[%s] Processing message queue - Size: %d", ModuleName, #shared.messenger.queue)) -- ModuleName,
end -- #shared.messenger.queue:get()
-- )
-- )
-- end
if not Heimdall_Data.config.messenger.enabled then if not Heimdall_Data.config.messenger.enabled then
if Heimdall_Data.config.messenger.debug then -- if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Module disabled, skipping message processing", ModuleName)) -- print(string.format("[%s] Module disabled, skipping message processing", ModuleName))
-- end
return
end end
return
end
---@type Message ---@type Message
local message = shared.messenger.queue[1] local message = shared.messenger.queue[1]
if not message then if not message then
if Heimdall_Data.config.messenger.debug then -- if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Message queue empty", ModuleName)) -- print(string.format("[%s] Message queue empty", ModuleName))
-- end
return
end end
return
end
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then shared.dump(message, "[%s] Processing message:") end
print(
string.format(
"[%s] Processing message - Channel: %s, Data: %s",
ModuleName,
message.channel or "nil",
message.data or "nil"
)
)
print(string.format("[%s] Message content: %s", ModuleName, message.message or "nil"))
end
if not message.message or message.message == "" then if not message.message or message.message == "" then
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Invalid message: empty content", ModuleName)) shared.dump(message, string.format("[%s] Invalid message: empty content", ModuleName))
end
return
end end
return
end
if not message.channel or message.channel == "" then if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Invalid message: no channel specified", ModuleName)) shared.dump(message, string.format("[%s] Invalid message: no channel specified", ModuleName))
end
return
end end
return
end
if string.find(message.channel, "^C") then if string.find(message.channel, "^C") then
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Converting channel type from C to CHANNEL", ModuleName)) shared.dump(
end message,
message.channel = "CHANNEL" string.format("[%s] Converting channel type from C to CHANNEL", ModuleName)
elseif string.find(message.channel, "^W") then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Converting channel type from W to WHISPER", ModuleName))
end
message.channel = "WHISPER"
end
if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then
if Heimdall_Data.config.messenger.debug then
print(
string.format(
"[%s] Processing channel message: '%s' to '%s'",
ModuleName,
message.message,
message.data
) )
)
end
local channelId = GetChannelName(message.data)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, attempting to join: %s", ModuleName, message.data))
end end
channelId = FindOrJoinChannel(message.data) message.channel = "CHANNEL"
elseif string.find(message.channel, "^W") then
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId)) shared.dump(
message,
string.format("[%s] Converting channel type from W to WHISPER", ModuleName)
)
end end
message.channel = "WHISPER"
end end
message.data = tostring(channelId)
end
table.remove(shared.messenger.queue, 1) if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then
if not message.message or message.message == "" then if Heimdall_Data.config.messenger.debug then
if Heimdall_Data.config.messenger.debug then shared.dump(message, string.format("[%s] Processing channel message:", ModuleName))
print(string.format("[%s] Skipping empty message", ModuleName)) end
local channelId = GetChannelName(message.data)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Channel not found, joining:", ModuleName))
end
channelId = FindOrJoinChannel(message.data)
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId))
end
end
message.data = tostring(channelId)
end end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Skipping message with no channel", ModuleName))
end
return
end
if not message.data or message.data == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Skipping message with no data", ModuleName))
end
return
end
if Heimdall_Data.config.messenger.debug then table.remove(shared.messenger.queue, 1)
print( if not message.message or message.message == "" then
string.format( if Heimdall_Data.config.messenger.debug then
"[%s] Sending message: '%s' to %s:%s", shared.dump(message, string.format("[%s] Skipping empty message", ModuleName))
ModuleName, end
message.message, return
message.channel, end
message.data if not message.channel or message.channel == "" then
) if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Skipping message with no channel", ModuleName))
end
return
end
if not message.data or message.data == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Skipping message with no data", ModuleName))
end
return
end
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Sending message:", ModuleName))
end
if string.len(message.message) > 255 then
shared.dump(message, string.format("[%s] Message too long!!!!: %s", ModuleName, message.message))
return
end
SendChatMessage(message.message, message.channel, nil, message.data)
end
local function Tick()
-- if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Tick - Queue size: %d", ModuleName, #shared.messenger.queue:get()))
-- end
DoMessage()
shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1)
end
Tick()
end
if Heimdall_Data.config.messenger.debug then
print(
string.format(
"[%s] Module initialized with interval: %s",
ModuleName,
Heimdall_Data.config.messenger.interval
) )
end
if string.len(message.message) > 255 then
print(string.format("[%s] Message too long!!!!: %s", ModuleName, message.message))
return
end
SendChatMessage(message.message, message.channel, nil, message.data)
end
local function Tick()
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Tick - Queue size: %d", ModuleName, #shared.messenger.queue))
end
DoMessage()
shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1)
end
Tick()
end
if Heimdall_Data.config.messenger.debug then
print(
string.format(
"[%s] Module initialized with interval: %s",
ModuleName,
Heimdall_Data.config.messenger.interval
) )
) end
end print(string.format("[%s] Module initialized", ModuleName))
print("[Heimdall] Messenger loaded") end,
end }

File diff suppressed because it is too large Load Diff

View File

@@ -2,72 +2,84 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Network" local ModuleName = "Network"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallNetworkConfig
shared.Network = {} ---@field enabled boolean
function shared.Network.Init() ---@field debug boolean
if not shared.network then shared.network = {} end ---@field members string[]
local updatePending = false ---@field updateInterval number
local function FriendListUpdate() ---@class HeimdallNetworkData
updatePending = false ---@field ticker Timer?
if not Heimdall_Data.config.network.enabled then return end
---@type table<string, boolean> ---@class Network
local friends = {} shared.Network = {
for i = 1, GetNumFriends() do Init = function()
local name, _, _, _, connected, _, _, _ = GetFriendInfo(i) if not shared.network then shared.network = {} end
if name then local updatePending = false
friends[name] = connected
if Heimdall_Data.config.network.debug then local function FriendListUpdate()
print(string.format("[%s] Friend %s is %s", ModuleName, name, connected and "online" or "offline")) updatePending = false
if not Heimdall_Data.config.network.enabled then return end
---@type table<string, boolean>
local friends = {}
for i = 1, GetNumFriends() do
local name, _, _, _, connected, _, _, _ = GetFriendInfo(i)
if name then
friends[name] = connected
if Heimdall_Data.config.network.debug then
print(
string.format("[%s] Friend %s is %s", ModuleName, name, connected and "online" or "offline")
)
end
else
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Friend %s is nil", ModuleName, i))
end
end end
else end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Friend %s is nil", ModuleName, i)) for _, member in ipairs(Heimdall_Data.config.network.members) do
if friends[member] == nil and member ~= UnitName("player") then
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Adding friend %s", ModuleName, member))
end
AddFriend(member)
end end
end end
friends[UnitName("player")] = true
shared.networkNodes = {}
-- Why are we skipping this again...?
-- if false then shared.networkNodes[#shared.networkNodes + 1] = UnitName("player") end
for _, player in ipairs(Heimdall_Data.config.network.members) do
if friends[player] then
shared.networkNodes[#shared.networkNodes + 1] = player
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Adding network node %s", ModuleName, player))
end
end
end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Network nodes:", ModuleName))
shared.dump(shared.networkNodes)
end
end end
for _, member in ipairs(Heimdall_Data.config.network.members) do local friendsFrame = CreateFrame("Frame")
if friends[member] == nil and member ~= UnitName("player") then friendsFrame:RegisterEvent("FRIENDLIST_UPDATE")
if Heimdall_Data.config.network.debug then friendsFrame:SetScript("OnEvent", function(self, event, ...) end)
print(string.format("[%s] Adding friend %s", ModuleName, member))
end local function NetworkTick()
AddFriend(member) if Heimdall_Data.config.network.debug then print("Network module is updating.") end
end ShowFriends()
updatePending = true
C_Timer.After(1, function()
if updatePending then FriendListUpdate() end
end)
shared.network.ticker = C_Timer.NewTimer(Heimdall_Data.config.network.updateInterval, NetworkTick, 1)
end end
friends[UnitName("player")] = true
shared.networkNodes = {} NetworkTick()
-- Why are we skipping this again...? print(string.format("[%s] Module initialized", ModuleName))
-- if false then shared.networkNodes[#shared.networkNodes + 1] = UnitName("player") end end,
for _, player in ipairs(Heimdall_Data.config.network.members) do }
if friends[player] then
shared.networkNodes[#shared.networkNodes + 1] = player
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Adding network node %s", ModuleName, player))
end
end
end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Network nodes:", ModuleName))
shared.dumpTable(shared.networkNodes)
end
end
local friendsFrame = CreateFrame("Frame")
friendsFrame:RegisterEvent("FRIENDLIST_UPDATE")
friendsFrame:SetScript("OnEvent", function(self, event, ...) end)
local function NetworkTick()
if Heimdall_Data.config.network.debug then print("Network module is updating.") end
ShowFriends()
updatePending = true
C_Timer.After(1, function()
if updatePending then FriendListUpdate() end
end)
shared.network.ticker = C_Timer.NewTimer(Heimdall_Data.config.network.updateInterval, NetworkTick, 1)
end
NetworkTick()
print("[Heimdall] Network module loaded")
end

View File

@@ -2,182 +2,203 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "NetworkMessenger" local ModuleName = "NetworkMessenger"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallNetworkMessengerConfig
shared.NetworkMessenger = {} ---@field enabled boolean
function shared.NetworkMessenger.Init() ---@field debug boolean
RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix) ---@field interval number
if not shared.networkMessenger then shared.networkMessenger = {} end ---@class HeimdallNetworkMessengerData
if not shared.networkMessenger.queue then shared.networkMessenger.queue = {} end ---@field queue table<string, Message>
if not shared.networkMessenger.ticker then ---@field ticker Timer?
local function DoMessage()
--if Heimdall_Data.config.networkMessenger.debug then ---@class NetworkMessenger
-- print(string.format("[%s] Processing network message queue", ModuleName)) shared.NetworkMessenger = {
--end ---@param message Message
if not Heimdall_Data.config.networkMessenger.enabled then Enqueue = function(message) table.insert(shared.networkMessenger.queue, message) end,
Init = function()
RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix)
shared.networkMessenger = {
queue = ReactiveValue.new({}),
}
if not shared.networkMessenger.ticker then
local function DoMessage()
--if Heimdall_Data.config.networkMessenger.debug then --if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName)) -- print(string.format("[%s] Processing network message queue", ModuleName))
--end --end
return if not Heimdall_Data.config.networkMessenger.enabled then
end --if Heimdall_Data.config.networkMessenger.debug then
---@type Message -- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName))
local message = shared.networkMessenger.queue[1] --end
if not message then return
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Network message queue empty", ModuleName))
--end
return
end
if not message.message or message.message == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: empty content", ModuleName))
end end
return ---@type Message
end local message = shared.networkMessenger.queue[1]
if not message.channel or message.channel == "" then if not message then
if Heimdall_Data.config.networkMessenger.debug then --if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: no channel specified", ModuleName)) -- print(string.format("[%s] Network message queue empty", ModuleName))
--end
return
end
if not message.message or message.message == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: empty content", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: no channel specified", ModuleName))
end
return
end end
return
end
table.remove(shared.networkMessenger.queue, 1) table.remove(shared.networkMessenger.queue, 1)
if not message.message or message.message == "" then if not message.message or message.message == "" then
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Skipping empty network message", ModuleName)) print(string.format("[%s] Skipping empty network message", ModuleName))
end
return
end end
return if not message.channel or message.channel == "" then
end if Heimdall_Data.config.networkMessenger.debug then
if not message.channel or message.channel == "" then print(string.format("[%s] Skipping network message with no channel", ModuleName))
if Heimdall_Data.config.networkMessenger.debug then end
print(string.format("[%s] Skipping network message with no channel", ModuleName)) return
end end
return if not message.data or message.data == "" then
end if Heimdall_Data.config.networkMessenger.debug then
if not message.data or message.data == "" then print(string.format("[%s] Skipping network message with no data", ModuleName))
if Heimdall_Data.config.networkMessenger.debug then end
print(string.format("[%s] Skipping network message with no data", ModuleName)) return
end end
return
end
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print( print(
string.format( string.format(
"[%s] Sending network message: '%s' to %s:%s", "[%s] Sending network message: '%s' to %s:%s",
ModuleName, ModuleName,
message.message, message.message,
message.channel, message.channel,
message.data message.data
)
) )
) end
local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data)
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Payload: %s", ModuleName, payload))
end
if not shared.networkNodes or #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName))
end
return
end
local target = shared.networkNodes[1]
SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target)
end end
local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data) local function Tick()
--if Heimdall_Data.config.networkMessenger.debug then
-- local queueSize = #shared.networkMessenger.queue
-- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize))
--end
DoMessage()
shared.networkMessenger.ticker =
C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1)
end
Tick()
end
-- If we are the leader then we delegate messages (dmessage)
-- If we get a "message" command from leader then we send the message
local nextIdx = 1
local addonMsgFrame = CreateFrame("Frame")
addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON")
addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source)
if not Heimdall_Data.config.networkMessenger.enabled then return end
if prefix ~= Heimdall_Data.config.addonPrefix then return end
source = string.match(source, "[^%-]+")
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Payload: %s", ModuleName, payload)) print(string.format("[%s] Received message from %s: %s", ModuleName, source, message))
end end
if not shared.networkNodes or #shared.networkNodes == 0 then if #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName)) print(string.format("[%s] No network nodes found, wtf????", ModuleName))
end end
return return
end end
local target = shared.networkNodes[1]
SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target) -- There should always be at least one network node ergo should always exist a leader
end -- Because the us, the player, is also a node
local function Tick() --local networkLeader = shared.networkNodes[1]
--if Heimdall_Data.config.networkMessenger.debug then --if source ~= networkLeader then
-- local queueSize = #shared.networkMessenger.queue -- if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize)) -- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source,
-- networkLeader))
-- end
-- return
--end --end
DoMessage()
shared.networkMessenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1)
end
Tick()
end
-- If we are the leader then we delegate messages (dmessage) local parts = shared.Split(message, "|")
-- If we get a "message" command from leader then we send the message
local nextIdx = 1
local addonMsgFrame = CreateFrame("Frame")
addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON")
addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source)
if not Heimdall_Data.config.networkMessenger.enabled then return end
if prefix ~= Heimdall_Data.config.addonPrefix then return end
source = string.match(source, "[^%-]+")
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received message from %s: %s", ModuleName, source, message))
end
if #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName)) print(string.format("[%s] Received message parts:", ModuleName))
shared.dump(parts)
end end
return local command = strtrim(parts[1])
end if command == "message" then
local content = strtrim(tostring(parts[2]))
local targetchannel = strtrim(tostring(parts[3]))
local target = strtrim(tostring(parts[4]))
if Heimdall_Data.config.networkMessenger.debug then
print(
string.format(
"[%s] Received message command: %s %s %s",
ModuleName,
content,
targetchannel,
target
)
)
end
---@type Message
local msg = {
channel = targetchannel,
message = content,
data = target,
}
table.insert(shared.messenger.queue, msg)
elseif command == "dmessage" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received dmessage command", ModuleName))
end
parts[1] = "message"
local content = table.concat(parts, "|")
-- There should always be at least one network node ergo should always exist a leader if nextIdx > #shared.networkNodes then nextIdx = 1 end
-- Because the us, the player, is also a node local recipient = shared.networkNodes[nextIdx]
--local networkLeader = shared.networkNodes[1] nextIdx = nextIdx + 1
--if source ~= networkLeader then if Heimdall_Data.config.networkMessenger.debug then
-- if Heimdall_Data.config.networkMessenger.debug then print(string.format("[%s] Sending message %s to %s", ModuleName, content, recipient))
-- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source, end
-- networkLeader)) SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient)
-- end
-- return
--end
local parts = shared.Split(message, "|")
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received message parts:", ModuleName))
shared.dumpTable(parts)
end
local command = strtrim(parts[1])
if command == "message" then
local content = strtrim(tostring(parts[2]))
local targetchannel = strtrim(tostring(parts[3]))
local target = strtrim(tostring(parts[4]))
if Heimdall_Data.config.networkMessenger.debug then
print(
string.format("[%s] Received message command: %s %s %s", ModuleName, content, targetchannel, target)
)
end end
---@type Message end)
local msg = {
channel = targetchannel, --/run Heimdall_Data.Test()
message = content, Heimdall_Data.Test = function()
data = target, local testmsg = {
channel = "W",
message = "Hi, mom!",
data = "Secundus",
} }
table.insert(shared.messenger.queue, msg) for i = 1, 36 do
elseif command == "dmessage" then table.insert(shared.networkMessenger.queue, testmsg)
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received dmessage command", ModuleName))
end end
parts[1] = "message"
local content = table.concat(parts, "|")
if nextIdx > #shared.networkNodes then nextIdx = 1 end
local recipient = shared.networkNodes[nextIdx]
nextIdx = nextIdx + 1
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Sending message %s to %s", ModuleName, content, recipient))
end
SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient)
end end
end)
--/run Heimdall_Data.Test() print(string.format("[%s] Module initialized", ModuleName))
Heimdall_Data.Test = function() end,
local testmsg = { }
channel = "W",
message = "Hi, mom!",
data = "Secundus",
}
for i = 1, 36 do
table.insert(shared.networkMessenger.queue, testmsg)
end
end
print("[Heimdall] NetworkMessenger module loaded")
end

View File

@@ -2,294 +2,306 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Noter" local ModuleName = "Noter"
---@class HeimdallNoterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field lastNotes number
---@class Note ---@class Note
---@field source string ---@field source string
---@field for string ---@field for string
---@field date string ---@field date string
---@field note string ---@field note string
---@diagnostic disable-next-line: missing-fields ---@class Noter
shared.Noter = {} shared.Noter = {
function shared.Noter.Init() Init = function()
-- ---Hopefully this will not be necessary -- ---Hopefully this will not be necessary
-- ---@param text string -- ---@param text string
-- ---@param size number -- ---@param size number
-- ---@return string[] -- ---@return string[]
-- local function Partition(text, size) -- local function Partition(text, size)
-- local words = {} -- local words = {}
-- for word in text:gmatch("[^,]+") do -- for word in text:gmatch("[^,]+") do
-- words[#words + 1] = word -- words[#words + 1] = word
-- end -- end
-- local ret = {} -- local ret = {}
-- local currentChunk = "" -- local currentChunk = ""
-- for _, word in ipairs(words) do -- for _, word in ipairs(words) do
-- if #currentChunk + #word + 1 <= size then -- if #currentChunk + #word + 1 <= size then
-- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word) -- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
-- else -- else
-- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
-- currentChunk = word -- currentChunk = word
-- end -- end
-- end -- end
-- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
-- return ret -- return ret
-- end -- end
---@param array any[] ---@param array any[]
---@return any[] ---@return any[]
local function Compact(array) local function Compact(array)
local compacted = {} local compacted = {}
for _, v in pairs(array) do for _, v in pairs(array) do
compacted[#compacted + 1] = v compacted[#compacted + 1] = v
end
return compacted
end
---@param name string
---@param args string[]
local function DeleteNotes(name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Delete note command received for: %s", ModuleName, name))
end
local range = args[4]
if range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for delete note: %s", ModuleName, range))
end end
local indices = shared.Split(range, "..") return compacted
if Heimdall_Data.config.noter.debug then end
print(string.format("[%s] Indices for range deletion: %s", ModuleName, table.concat(indices, ", ")))
shared.dumpTable(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then ---@param name string
---@param args string[]
local function DeleteNotes(name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Delete note command received for: %s", ModuleName, name))
end
local range = args[4]
if range then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for delete note: %s", ModuleName, tostring(start))) print(string.format("[%s] Range received for delete note: %s", ModuleName, range))
end end
return local indices = shared.Split(range, "..")
end if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range deletion: %s", ModuleName, table.concat(indices, ", ")))
if not finish then finish = start end shared.dump(indices)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name))
end
-- Here, because we are deleting random notes, we lose the "iterative" index property
-- Ie it's not longer 1..100, it might be 1..47, 50, 68..100
-- Which means that we cannot use ipairs, bad!
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i))
shared.dumpTable(Heimdall_Data.config.notes[name][i])
end
Heimdall_Data.config.notes[name][i] = nil
end end
end local start = tonumber(indices[1])
Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name]) local finish = tonumber(indices[2])
end
end
---@param channel string if not start then
---@param index number if Heimdall_Data.config.noter.debug then
---@param note Note print(
local function PrintNote(channel, index, note) string.format("[%s] Invalid start range for delete note: %s", ModuleName, tostring(start))
if Heimdall_Data.config.noter.debug then )
print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source)) end
print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note)) return
end
if not finish then finish = start end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name))
end
-- Here, because we are deleting random notes, we lose the "iterative" index property
-- Ie it's not longer 1..100, it might be 1..47, 50, 68..100
-- Which means that we cannot use ipairs, bad!
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i))
shared.dump(Heimdall_Data.config.notes[name][i])
end
Heimdall_Data.config.notes[name][i] = nil
end
end
Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name])
end
end end
---@type Message
local msg = { ---@param channel string
channel = "C", ---@param index number
data = channel, ---@param note Note
message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note), local function PrintNote(channel, index, note)
}
--table.insert(shared.messenger.queue, msg)
table.insert(shared.networkMessenger.queue, msg)
end
---@param name string
---@param args string[]
local function PrintNotes(channel, name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Print note command received for: %s", ModuleName, name))
end
local range = args[3]
if not range then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print( print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source))
string.format( print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note))
"[%s] No range specified for print note, defaulting to last %d notes", end
ModuleName, ---@type Message
Heimdall_Data.config.noter.lastNotes local msg = {
channel = "C",
data = channel,
message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note),
}
if Heimdall_Data.config.networkMessenger.enabled then
shared.NetworkMessenger.Enqueue(msg)
elseif Heimdall_Data.config.messenger.enabled then
shared.Messenger.Enqueue(msg)
end
end
---@param name string
---@param args string[]
local function PrintNotes(channel, name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Print note command received for: %s", ModuleName, name))
end
local range = args[3]
if not range then
if Heimdall_Data.config.noter.debug then
print(
string.format(
"[%s] No range specified for print note, defaulting to last %d notes",
ModuleName,
Heimdall_Data.config.noter.lastNotes
)
) )
) end
end local notes = Heimdall_Data.config.notes[name] or {}
local notes = Heimdall_Data.config.notes[name] or {} local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1)
local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1) local finish = #notes
local finish = #notes
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
PrintNote(channel, i, notes[i])
end
return
end
if range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for print note: %s", ModuleName, range))
end
local indices = shared.Split(range, "..")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range printing: %s", ModuleName, table.concat(indices, ", ")))
shared.dumpTable(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for print note: %s", ModuleName, tostring(start))) print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
PrintNote(channel, i, notes[i])
end
return
end
if range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for print note: %s", ModuleName, range))
end
local indices = shared.Split(range, "..")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range printing: %s", ModuleName, table.concat(indices, ", ")))
shared.dump(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for print note: %s", ModuleName, tostring(start)))
end
return
end
if not finish then finish = start end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i))
shared.dump(Heimdall_Data.config.notes[name][i])
end
PrintNote(channel, i, Heimdall_Data.config.notes[name][i])
end
end
end
end
---@param name string
---@param sender string
---@param args string[]
local function AddNote(name, sender, args)
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
shared.dump(args)
end
local msgparts = {}
for i = 3, #args do
msgparts[#msgparts + 1] = args[i]
end
local msg = table.concat(msgparts, " ")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
print(string.format("[%s] Note: %s", ModuleName, msg))
end
local note = {
source = sender,
date = date("%Y-%m-%dT%H:%M:%S"),
note = msg,
}
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note", ModuleName))
shared.dump(note)
end
table.insert(Heimdall_Data.config.notes[name], note)
end
-- Here's the plan:
-- Implement a "note" command, that will do everything
-- Saying "note <name> <note>" will add a note to the list for the character
-- Saying "note <name>" will list last N notes
-- Saying "note <name> i" will list the i-th note
-- Saying "note <name> i..j" will list notes from i to j
-- Saying "note <name> delete i" will delete the i-th note
-- Saying "note <name> delete i..j" will delete notes from i to j
local noterChannelFrame = CreateFrame("Frame")
noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dump(Heimdall_Data.config.noter)
--end
if not Heimdall_Data.config.noter.enabled then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
--end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.noter.channels) do
if channelname == channel then
ok = true
break
end
end
if not ok then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel))
--end
return
end
sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.noter)
end
if not msg or msg == "" then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Empty message, ignoring", ModuleName))
end end
return return
end end
if not finish then finish = start end local args = { strsplit(" ", msg) }
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name)) print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", ")))
shared.dump(args)
end end
local command = args[1]
for i = start, finish do if command == "note" then
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end local name = strtrim(string.lower(args[2] or ""))
if not Heimdall_Data.config.notes[name][i] then if Heimdall_Data.config.noter.debug then
if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note command received for: %s", ModuleName, name))
print(string.format("[%s] Note at index %s does not exist", ModuleName, i)) end
end local note = strtrim(args[3] or "")
if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note: %s", ModuleName, note)) end
if note == "delete" then
DeleteNotes(name, args)
elseif string.find(note, "^[%d%.]*$") then
PrintNotes(channelname, name, args)
else else
if Heimdall_Data.config.noter.debug then AddNote(name, sender, args)
print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i))
shared.dumpTable(Heimdall_Data.config.notes[name][i])
end
PrintNote(channel, i, Heimdall_Data.config.notes[name][i])
end end
end end
end end)
end
---@param name string print(string.format("[%s] Module initialized", ModuleName))
---@param sender string end,
---@param args string[] }
local function AddNote(name, sender, args)
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
shared.dumpTable(args)
end
local msgparts = {}
for i = 3, #args do
msgparts[#msgparts + 1] = args[i]
end
local msg = table.concat(msgparts, " ")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
print(string.format("[%s] Note: %s", ModuleName, msg))
end
local note = {
source = sender,
date = date("%Y-%m-%dT%H:%M:%S"),
note = msg,
}
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note", ModuleName))
shared.dumpTable(note)
end
table.insert(Heimdall_Data.config.notes[name], note)
end
-- Here's the plan:
-- Implement a "note" command, that will do everything
-- Saying "note <name> <note>" will add a note to the list for the character
-- Saying "note <name>" will list last N notes
-- Saying "note <name> i" will list the i-th note
-- Saying "note <name> i..j" will list notes from i to j
-- Saying "note <name> delete i" will delete the i-th note
-- Saying "note <name> delete i..j" will delete notes from i to j
local noterChannelFrame = CreateFrame("Frame")
noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dumpTable(Heimdall_Data.config.noter)
--end
if not Heimdall_Data.config.noter.enabled then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
--end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.noter.channels) do
if channelname == channel then
ok = true
break
end
end
if not ok then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel))
--end
return
end
sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.noter)
end
if not msg or msg == "" then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Empty message, ignoring", ModuleName))
end
return
end
local args = { strsplit(" ", msg) }
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", ")))
shared.dumpTable(args)
end
local command = args[1]
if command == "note" then
local name = strtrim(string.lower(args[2] or ""))
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note command received for: %s", ModuleName, name))
end
local note = strtrim(args[3] or "")
if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note: %s", ModuleName, note)) end
if note == "delete" then
DeleteNotes(name, args)
elseif string.find(note, "^[%d%.]*$") then
PrintNotes(channelname, name, args)
else
AddNote(name, sender, args)
end
end
end)
print("[Heimdall] Commander module loaded")
end

View File

@@ -483,7 +483,7 @@ local function Init()
-- S local clbk = test:onChange(function(value) -- S local clbk = test:onChange(function(value)
-- S invocations = invocations + 1 -- S invocations = invocations + 1
-- S print("test changed to") -- S print("test changed to")
-- S dumpTable(value, 0) -- S dump(value, 0)
-- S end) -- S end)
-- S test:set({1, 2, 3, 4}) -- S test:set({1, 2, 3, 4})
-- S assert(invocations == 1) -- S assert(invocations == 1)
@@ -516,7 +516,7 @@ local function Init()
-- S test:once(function(value) -- S test:once(function(value)
-- S invocations = invocations + 1 -- S invocations = invocations + 1
-- S print("test changed to") -- S print("test changed to")
-- S dumpTable(value, 0) -- S dump(value, 0)
-- S end) -- S end)
-- S test:set({3, 2, 1}) -- S test:set({3, 2, 1})
-- S assert(invocations == 1) -- S assert(invocations == 1)
@@ -527,7 +527,7 @@ local function Init()
-- S test:onChange(function(value) -- S test:onChange(function(value)
-- S invocations = invocations + 1 -- S invocations = invocations + 1
-- S print("test changed to") -- S print("test changed to")
-- S dumpTable(value, 0) -- S dump(value, 0)
-- S end) -- S end)
-- S test:onAnyFieldChange(function(field, value) -- S test:onAnyFieldChange(function(field, value)
-- S invocations = invocations + 1 -- S invocations = invocations + 1

View File

@@ -2,82 +2,91 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Sniffer" local ModuleName = "Sniffer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallSnifferConfig
shared.Sniffer = {} ---@field enabled boolean
function shared.Sniffer.Init() ---@field debug boolean
if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initializing", ModuleName)) end ---@field channels string[]
local smellThrottle = {} ---@field throttle number -- throttleTime in the original code, matching config name now
local SmellStinky = function(stinky) ---@field zoneOverride string?
if Heimdall_Data.config.sniffer.debug then ---@field stinky boolean
print(string.format("%s: SmellStinky", ModuleName))
shared.dumpTable(Heimdall_Data.config.sniffer)
end
if not Heimdall_Data.config.sniffer.enabled then return end
if Heimdall_Data.config.sniffer.stinky and not shared.IsStinky(stinky) then
if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: Stinky not found in config", ModuleName))
end
return
end
if smellThrottle[stinky] and GetTime() - smellThrottle[stinky] < Heimdall_Data.config.sniffer.throttleTime then
if Heimdall_Data.config.sniffer.debug then print(string.format("%s: Throttled", ModuleName)) end
return
end
smellThrottle[stinky] = GetTime()
for _, channel in pairs(Heimdall_Data.config.sniffer.channels) do ---@class Sniffer
local locale = shared.GetLocaleForChannel(channel) shared.Sniffer = {
local text = string.format(shared._L("snifferStinky", locale), stinky) Init = function()
---@type Message if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initializing", ModuleName)) end
local msg = { local smellThrottle = {}
channel = "C", local SmellStinky = function(stinky)
data = channel,
message = text,
}
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Queuing sniffer message", ModuleName)) print(string.format("%s: SmellStinky", ModuleName))
shared.dumpTable(msg) shared.dump(Heimdall_Data.config.sniffer)
end end
table.insert(shared.messenger.queue, msg) if not Heimdall_Data.config.sniffer.enabled then return end
end if Heimdall_Data.config.sniffer.stinky and not shared.IsStinky(stinky) then
end if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: Stinky not found in config", ModuleName))
end
return
end
if smellThrottle[stinky] and GetTime() - smellThrottle[stinky] < Heimdall_Data.config.sniffer.throttle then
if Heimdall_Data.config.sniffer.debug then print(string.format("%s: Throttled", ModuleName)) end
return
end
smellThrottle[stinky] = GetTime()
local cleuFrame = CreateFrame("Frame") for _, channel in pairs(Heimdall_Data.config.sniffer.channels) do
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") local locale = shared.GetLocaleForChannel(channel)
cleuFrame:SetScript("OnEvent", function(self, event, ...) local text = string.format(shared._L("snifferStinky", locale), stinky)
if Heimdall_Data.config.sniffer.debug then ---@type Message
print(string.format("[%s] Received event: %s", ModuleName, event)) local msg = {
end channel = "C",
if not Heimdall_Data.config.sniffer.enabled then data = channel,
if Heimdall_Data.config.sniffer.debug then message = text,
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) }
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Queuing sniffer message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end end
return
end end
local source, destination, err
source, err = CLEUParser.GetSourceName(...) local cleuFrame = CreateFrame("Frame")
if Heimdall_Data.config.sniffer.debug then cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
print(string.format("[%s] Processing source: %s", ModuleName, source)) cleuFrame:SetScript("OnEvent", function(self, event, ...)
end
if err then
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing source: %s", ModuleName, err)) print(string.format("[%s] Received event: %s", ModuleName, event))
end end
return if not Heimdall_Data.config.sniffer.enabled then
end if Heimdall_Data.config.sniffer.debug then
SmellStinky(source) print(string.format("[%s] Module disabled, ignoring event", ModuleName))
destination, err = CLEUParser.GetDestName(...) end
if Heimdall_Data.config.sniffer.debug then return
print(string.format("[%s] Processing destination: %s", ModuleName, destination)) end
end local source, destination, err
if err then source, err = CLEUParser.GetSourceName(...)
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing destination: %s", ModuleName, err)) print(string.format("[%s] Processing source: %s", ModuleName, source))
end end
return if err then
end if Heimdall_Data.config.sniffer.debug then
SmellStinky(destination) print(string.format("[%s] Error parsing source: %s", ModuleName, err))
end) end
if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initialized", ModuleName)) end return
print("[Heimdall] Sniffer loaded") end
end SmellStinky(source)
destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Processing destination: %s", ModuleName, destination))
end
if err then
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing destination: %s", ModuleName, err))
end
return
end
SmellStinky(destination)
end)
if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,223 +2,237 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "Spotter" local ModuleName = "Spotter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallSpotterConfig
shared.Spotter = {} ---@field enabled boolean
function shared.Spotter.Init() ---@field debug boolean
local function FormatHP(hp) ---@field everyone boolean
if hp > 1e9 then ---@field hostile boolean
return string.format("%.1fB", hp / 1e9) ---@field alliance boolean
elseif hp > 1e6 then ---@field stinky boolean
return string.format("%.1fM", hp / 1e6) ---@field channels string[]
elseif hp > 1e3 then ---@field zoneOverride string?
return string.format("%.1fK", hp / 1e3) ---@field throttleTime number
else
return hp
end
end
---@type table<string, number> ---@class Spotter
local throttleTable = {} shared.Spotter = {
Init = function()
---@param unit string local function FormatHP(hp)
---@param name string if hp > 1e9 then
---@param faction string return string.format("%.1fB", hp / 1e9)
---@param hostile boolean elseif hp > 1e6 then
---@return boolean return string.format("%.1fM", hp / 1e6)
---@return string? error elseif hp > 1e3 then
local function ShouldNotify(unit, name, faction, hostile) return string.format("%.1fK", hp / 1e3)
if Heimdall_Data.config.spotter.debug then else
print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction)) return hp
end
end end
if Heimdall_Data.config.agents[name] then ---@type table<string, number>
local throttleTable = {}
---@param unit string
---@param name string
---@param faction string
---@param hostile boolean
---@return boolean
---@return string? error
local function ShouldNotify(unit, name, faction, hostile)
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Skipping agent: %s", ModuleName, name)) print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction))
end end
return false
end
if Heimdall_Data.config.spotter.stinky then if shared.AgentTracker.IsAgent(name) then
if shared.IsStinky(name) then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name)) print(string.format("[%s] Skipping agent: %s", ModuleName, name))
end end
return true return false
end end
end
if Heimdall_Data.config.spotter.alliance then if Heimdall_Data.config.spotter.stinky then
if faction == "Alliance" then if shared.IsStinky(name) then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name)) print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name))
end
return true
end end
return true
end end
end
if Heimdall_Data.config.spotter.hostile then if Heimdall_Data.config.spotter.alliance then
if hostile then if faction == "Alliance" then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name)) print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name))
end
return true
end end
return true
end end
end
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.hostile then
print( if hostile then
string.format( if Heimdall_Data.config.spotter.debug then
"[%s] Using everyone setting: %s", print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name))
ModuleName, end
tostring(Heimdall_Data.config.spotter.everyone) return true
end
end
if Heimdall_Data.config.spotter.debug then
print(
string.format(
"[%s] Using everyone setting: %s",
ModuleName,
tostring(Heimdall_Data.config.spotter.everyone)
)
) )
)
end
return Heimdall_Data.config.spotter.everyone
end
---@param unit string
---@return string?
local function NotifySpotted(unit)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit))
end
if not unit then return string.format("Could not find unit %s", tostring(unit)) end
if not UnitIsPlayer(unit) then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit))
end end
return nil return Heimdall_Data.config.spotter.everyone
end end
local name = UnitName(unit) ---@param unit string
if not name then return string.format("Could not find name for unit %s", tostring(unit)) end ---@return string?
if Heimdall_Data.config.spotter.debug then local function NotifySpotted(unit)
print(string.format("[%s] Processing player: %s", ModuleName, name))
end
local time = GetTime()
if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name]) print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit))
print(string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime))
end end
return string.format("Throttled %s", tostring(name))
end
throttleTable[name] = time
local race = UnitRace(unit) if not unit then return string.format("Could not find unit %s", tostring(unit)) end
if not race then return string.format("Could not find race for unit %s", tostring(unit)) end if not UnitIsPlayer(unit) then
local faction = shared.raceMap[race] if Heimdall_Data.config.spotter.debug then
if not faction then return string.format("Could not find faction for race %s", tostring(race)) end print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit))
if Heimdall_Data.config.spotter.debug then end
print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction)) return nil
end
local hostile = UnitCanAttack("player", unit)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly"))
end
local doNotify = ShouldNotify(unit, name, faction, hostile)
if not doNotify then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Skipping notification for %s", ModuleName, name))
end end
return string.format("Not notifying for %s", tostring(name))
end
local hp = UnitHealth(unit) local name = UnitName(unit)
if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end if not name then return string.format("Could not find name for unit %s", tostring(unit)) end
local maxHp = UnitHealthMax(unit)
if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp)))
end
local class = UnitClass(unit)
if not class then return string.format("Could not find class for unit %s", tostring(unit)) end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown"
if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100))
end
local pvpOn = UnitIsPVP(unit)
local stinky = shared.IsStinky(name) or false
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = tostring(GetCurrentMapAreaID())
for _, channel in pairs(Heimdall_Data.config.spotter.channels) do
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing channel: %s", ModuleName, channel)) print(string.format("[%s] Processing player: %s", ModuleName, name))
end end
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("spotterSpotted", locale),
hostile and shared._L("hostile", locale) or shared._L("friendly", locale),
name,
shared._L(class, locale),
stinky and string.format("(%s)", "!!!!") or "",
shared._L(race, locale),
shared._L(faction, locale),
pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale),
string.gsub(FormatHP(hp), "M", "kk"),
string.gsub(FormatHP(maxHp), "M", "kk"),
shared._L(zone, locale),
shared._L(subzone, locale),
areaId,
x * 100,
y * 100
)
---@type Message local time = GetTime()
local msg = { if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then
channel = "C", if Heimdall_Data.config.spotter.debug then
data = channel, local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name])
message = text, print(
} string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime)
if Heimdall_Data.config.spotter.debug then )
print(string.format("[%s] Queuing spotter message", ModuleName)) end
shared.dumpTable(msg) return string.format("Throttled %s", tostring(name))
end end
table.insert(shared.messenger.queue, msg) throttleTable[name] = time
end
end
local frame = CreateFrame("Frame") local race = UnitRace(unit)
frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") if not race then return string.format("Could not find race for unit %s", tostring(unit)) end
frame:RegisterEvent("UNIT_TARGET") local faction = shared.raceMap[race]
frame:SetScript("OnEvent", function(self, event, unit) if not faction then return string.format("Could not find faction for race %s", tostring(race)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
end
if not Heimdall_Data.config.spotter.enabled then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction))
end end
return
end
if event == "UNIT_TARGET" then unit = "target" end local hostile = UnitCanAttack("player", unit)
local err = NotifySpotted(unit)
if err then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err)) print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly"))
end
local doNotify = ShouldNotify(unit, name, faction, hostile)
if not doNotify then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Skipping notification for %s", ModuleName, name))
end
return string.format("Not notifying for %s", tostring(name))
end
local hp = UnitHealth(unit)
if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end
local maxHp = UnitHealthMax(unit)
if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp)))
end
local class = UnitClass(unit)
if not class then return string.format("Could not find class for unit %s", tostring(unit)) end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown"
if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100))
end
local pvpOn = UnitIsPVP(unit)
local stinky = shared.IsStinky(name) or false
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = tostring(GetCurrentMapAreaID())
for _, channel in pairs(Heimdall_Data.config.spotter.channels) do
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing channel: %s", ModuleName, channel))
end
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("spotterSpotted", locale),
hostile and shared._L("hostile", locale) or shared._L("friendly", locale),
name,
shared._L(class, locale),
stinky and string.format("(%s)", "!!!!") or "",
shared._L(race, locale),
shared._L(faction, locale),
pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale),
string.gsub(FormatHP(hp), "M", "kk"),
string.gsub(FormatHP(maxHp), "M", "kk"),
shared._L(zone, locale),
shared._L(subzone, locale),
areaId,
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Queuing spotter message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end end
end end
end)
if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end local frame = CreateFrame("Frame")
print("[Heimdall] Spotter loaded") frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
end frame:RegisterEvent("UNIT_TARGET")
frame:SetScript("OnEvent", function(self, event, unit)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
end
if not Heimdall_Data.config.spotter.enabled then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end
return
end
if event == "UNIT_TARGET" then unit = "target" end
local err = NotifySpotted(unit)
if err then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err))
end
end
end)
if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,72 +1,83 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "StinkyCache" local ModuleName = "StinkyCache"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallStinkyCacheConfig
shared.StinkyCache = {} ---@field enabled boolean
function shared.StinkyCache.Init() ---@field debug boolean
shared.stinkyCache = { ---@field commander string
stinkies = {}, ---@field ttl number
}
---@param name string ---@class HeimdallStinkyCacheData
local function AskCommander(name) ---@field stinkies table<string, {value: number, timestamp: number}>
if Heimdall_Data.config.stinkyCache.debug then
print(
string.format(
"[%s] Asking commander %s about %s",
ModuleName,
Heimdall_Data.config.stinkyCache.commander,
name
)
)
end
local messageParts = { "isstinky", name }
local message = table.concat(messageParts, "|")
SendAddonMessage(
Heimdall_Data.config.addonPrefix,
message,
"WHISPER",
Heimdall_Data.config.stinkyCache.commander
)
return
end
local addonMessageFrame = CreateFrame("Frame") ---@class StinkyCache
addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON") shared.StinkyCache = {
addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) Init = function()
if sender == Heimdall_Data.config.stinkyCache.commander then shared.stinkyCache = {
stinkies = {},
}
---@param name string
local function AskCommander(name)
if Heimdall_Data.config.stinkyCache.debug then if Heimdall_Data.config.stinkyCache.debug then
print( print(
string.format( string.format(
"[%s] Received stinky from commander %s: %s", "[%s] Asking commander %s about %s",
ModuleName, ModuleName,
Heimdall_Data.config.stinkyCache.commander, Heimdall_Data.config.stinkyCache.commander,
msg name
) )
) )
end end
local parts = { strsplit("|", msg) } local messageParts = { "isstinky", name }
local name, value = parts[1], parts[2] local message = table.concat(messageParts, "|")
shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() } SendAddonMessage(
else Heimdall_Data.config.addonPrefix,
if Heimdall_Data.config.stinkyCache.debug then message,
print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg)) "WHISPER",
end Heimdall_Data.config.stinkyCache.commander
local parts = { strsplit("|", msg) } )
local command, name = parts[1], parts[2] return
if parts[1] == "isstinky" then local res = Heimdall_Data.config.stinkies[parts[2]] end
end end
end)
setmetatable(shared.stinkyCache.stinkies, { local addonMessageFrame = CreateFrame("Frame")
__index = function(self, key) addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON")
local value = rawget(self, key) addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
local now = GetTime() if sender == Heimdall_Data.config.stinkyCache.commander then
if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then AskCommander(key) end if Heimdall_Data.config.stinkyCache.debug then
return rawget(self, key) print(
end, string.format(
}) "[%s] Received stinky from commander %s: %s",
print("[Heimdall] StinkyCache module loaded") ModuleName,
end Heimdall_Data.config.stinkyCache.commander,
msg
)
)
end
local parts = { strsplit("|", msg) }
local name, value = parts[1], parts[2]
shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() }
else
if Heimdall_Data.config.stinkyCache.debug then
print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg))
end
local parts = { strsplit("|", msg) }
local command, name = parts[1], parts[2]
if parts[1] == "isstinky" then local res = Heimdall_Data.config.stinkies[parts[2]] end
end
end)
setmetatable(shared.stinkyCache.stinkies, {
__index = function(self, key)
local value = rawget(self, key)
local now = GetTime()
if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then
AskCommander(key)
end
return rawget(self, key)
end,
})
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -2,276 +2,363 @@ local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
local ModuleName = "StinkyTracker" local ModuleName = "StinkyTracker"
---@diagnostic disable-next-line: missing-fields ---@class Stinky
shared.StinkyTracker = {} ---@field name string
function shared.StinkyTracker.Init() ---@field class string
shared.stinkyTracker = { ---@field seenAt number
stinkies = ReactiveValue.new({}), ---@field hostile boolean
}
local whoRegex = "([^ -/]+)-?%w*/(%w+)" ---@class HeimdallStinkyTrackerConfig
---@param msg string ---@field enabled boolean
---@return table<string, stinky> ---@field debug boolean
local function ParseWho(msg) ---@field ignoredTimeout number
---@field channels string[]
---@class StinkyTrackerData
---@field stinkies ReactiveValue<table<string, Stinky>>
---@field ignored ReactiveValue<table<string, number>>
---@class StinkyTracker
shared.StinkyTracker = {
---@param stinky Stinky
---@return boolean
Track = function(stinky)
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg)) print(string.format("[%s] Request to track stinky: %s (%s)", ModuleName, stinky.name, stinky.class))
end end
local stinkies = {} local ignored = shared.stinkyTracker.ignored[stinky.name]
for name, class in string.gmatch(msg, whoRegex) do -- TODO: Add a config option for the ignored timeout
stinkies[name] = { if ignored and ignored > GetTime() - 60 then
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Stinky is ignored, not tracking: %s (%s)",
ModuleName,
stinky.name,
stinky.class
)
)
shared.dump(shared.stinkyTracker.ignored:get())
shared.dump(shared.stinkyTracker.stinkies:get())
end
return false
else
-- Timed out or was never ignored
shared.stinkyTracker.stinkies[stinky.name] = nil
end
shared.stinkyTracker.stinkies[stinky.name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Stinky is now tracked: %s (%s)", ModuleName, stinky.name, stinky.class))
shared.dump(shared.stinkyTracker.stinkies:get())
shared.dump(shared.stinkyTracker.ignored:get())
end
return true
end,
---@param name string
---@return nil
Ignore = function(name)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Request to ignore stinky: %s", ModuleName, name))
end
shared.stinkyTracker.ignored[name] = GetTime()
shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Stinky is now ignored: %s", ModuleName, name))
shared.dump(shared.stinkyTracker.ignored:get())
shared.dump(shared.stinkyTracker.stinkies:get())
end
end,
---@param name string
---@return boolean
IsStinky = function(name)
if not shared.stinkyTracker.stinkies then return false end
if not shared.stinkyTracker.stinkies[name] then return false end
if shared.stinkyTracker.ignored[name] then return false end
return true
end,
---@param callback fun(stinkies: table<string, Stinky>)
---@return nil
OnChange = function(callback) shared.stinkyTracker.stinkies:onChange(callback) end,
---@param callback fun(name: string, stinky: Stinky)
---@return nil
ForEach = function(callback)
---@type table<string, Stinky>
local stinkies = shared.stinkyTracker.stinkies:get()
for name, stinky in pairs(stinkies) do
callback(name, stinky)
end
end,
Init = function()
shared.stinkyTracker = {
stinkies = ReactiveValue.new({}),
ignored = ReactiveValue.new({}),
}
local whoRegex = "([^ -/]+)-?%w*/(%w+)"
---@param msg string
---@return table<string, Stinky>
local function ParseWho(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg))
end
local stinkies = {}
for name, class in string.gmatch(msg, whoRegex) do
stinkies[name] = {
name = name,
class = class,
seenAt = GetTime(),
hostile = true,
}
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Found hostile player: %s (%s) at %s",
ModuleName,
name,
class,
date("%H:%M:%S", time())
)
)
shared.dump(stinkies)
end
end
return stinkies
end
local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)"
---@param msg string
---@return table<string, Stinky>
local function ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg))
end
local stinkies = {}
local aggression, name, class = string.match(msg, seeRegex)
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Error: Invalid SEE message format", ModuleName))
end
return stinkies
end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = aggression == "hostile",
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Found stinky in SEE: %s (%s) - %s at %s",
ModuleName,
name,
class,
aggression,
date("%H:%M:%S", time())
)
)
shared.dump(stinkies)
end
return stinkies
end
local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)"
local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)"
---@param msg string
---@return table<string, Stinky>
local function ParseArrived(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Parsing arrived message: %s", ModuleName, msg))
end
local stinkies = {}
local name, class = string.match(msg, arrivedRegex)
if not name or not class then
name, class = string.match(msg, arrivedRegexAlt)
end
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: No valid stinky found in arrived message", ModuleName))
end
return stinkies
end
local stinky = {
name = name, name = name,
class = class, class = class,
seenAt = GetTime(), seenAt = GetTime(),
hostile = true, hostile = true,
} }
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print( print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class))
string.format( shared.dump(stinkies)
"[%s] Found hostile player: %s (%s) at %s",
ModuleName,
name,
class,
date("%H:%M:%S", time())
)
)
shared.dumpTable(stinkies)
end
end
return stinkies
end
local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)"
---@param msg string
---@return table<string, stinky>
local function ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg))
end
local stinkies = {}
local aggression, name, class = string.match(msg, seeRegex)
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Error: Invalid SEE message format", ModuleName))
end end
return stinkies return stinkies
end end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = aggression == "hostile",
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Found stinky in SEE: %s (%s) - %s at %s",
ModuleName,
name,
class,
aggression,
date("%H:%M:%S", time())
)
)
shared.dumpTable(stinkies)
end
return stinkies
end
local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)" local frame = CreateFrame("Frame")
local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)" frame:RegisterEvent("CHAT_MSG_CHANNEL")
---@param msg string frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
---@return table<string, stinky>
local function ParseArrived(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Parsing arrived message: %s", ModuleName, msg))
end
local stinkies = {}
local name, class = string.match(msg, arrivedRegex)
if not name or not class then
name, class = string.match(msg, arrivedRegexAlt)
end
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: No valid stinky found in arrived message", ModuleName))
end
return stinkies
end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = true,
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class))
shared.dumpTable(stinkies)
end
return stinkies
end
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.stinkyTracker.debug then
-- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender))
--end
if not Heimdall_Data.config.stinkyTracker.enabled then
--if Heimdall_Data.config.stinkyTracker.debug then --if Heimdall_Data.config.stinkyTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) -- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender))
--end --end
return if not Heimdall_Data.config.stinkyTracker.enabled then
end --if Heimdall_Data.config.stinkyTracker.debug then
local channelId = select(6, ...) -- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
local _, channelname = GetChannelName(channelId) --end
local ok = false return
for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do end
if channel == channelname then local channelId = select(6, ...)
ok = true local _, channelname = GetChannelName(channelId)
break local ok = false
for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname))
end
return
end end
end
if not ok then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.stinkyTracker)
end end
return
end
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.stinkyTracker)
end
if string.find(msg, "^who:") then local stinkies = {}
if Heimdall_Data.config.stinkyTracker.debug then if string.find(msg, "^who:") then
print(string.format("[%s] Processing WHO message from %s", ModuleName, sender)) if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing WHO message from %s", ModuleName, sender))
end
local whoStinkies = ParseWho(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in WHO message", ModuleName))
shared.dump(whoStinkies)
end
for name, stinky in pairs(whoStinkies) do
stinkies[name] = stinky
end
end end
local whoStinkies = ParseWho(msg) if string.find(msg, "^I see") then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in WHO message", ModuleName)) print(string.format("[%s] Processing SEE message from %s", ModuleName, sender))
end
local seeStinkies = ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in SEE message", ModuleName))
shared.dump(seeStinkies)
end
for name, stinky in pairs(seeStinkies) do
stinkies[name] = stinky
end
end end
for name, stinky in pairs(whoStinkies) do if string.find(msg, "arrived to") or string.find(msg, "moved to") then
if stinky.hostile then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender))
end
local arrivedStinkies = ParseArrived(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName))
shared.dump(arrivedStinkies)
end
for name, stinky in pairs(arrivedStinkies) do
stinkies[name] = stinky
end
end
for name, stinky in pairs(stinkies) do
if shared.stinkyTracker.ignored[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring stinky: %s (%s)", ModuleName, name, stinky.class))
end
shared.stinkyTracker.ignored[name] = nil
else
shared.stinkyTracker.stinkies[name] = stinky shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print( print(string.format("[%s] Added stinky: %s (%s)", ModuleName, name, stinky.class))
string.format("[%s] Added hostile stinky from WHO: %s (%s)", ModuleName, name, stinky.class)
)
end end
end end
end end
end
if string.find(msg, "^I see") then -- Log total stinky count after processing
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing SEE message from %s", ModuleName, sender)) local count = 0
end for _ in pairs(shared.stinkyTracker.stinkies:get()) do
local seeStinkies = ParseSee(msg) count = count + 1
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in SEE message", ModuleName))
end
for name, stinky in pairs(seeStinkies) do
if stinky.hostile then
shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format("[%s] Added hostile stinky from SEE: %s (%s)", ModuleName, name, stinky.class)
)
end
end end
if not stinky.hostile then print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count))
end
shared.StinkyTracker.ForEach(function(name, stinky)
if shared.AgentTracker.IsAgent(name) then
shared.stinkyTracker.stinkies[name] = nil shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Removed non-hostile stinky from SEE: %s", ModuleName, name)) print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name))
end end
end end
end end)
end end)
if string.find(msg, "arrived to") or string.find(msg, "moved to") then
local targetFrame = CreateFrame("Frame")
targetFrame:RegisterEvent("UNIT_TARGET")
targetFrame:SetScript("OnEvent", function(self, event, unit)
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender)) print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
end end
local arrivedStinkies = ParseArrived(msg) unit = "target"
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName)) if not Heimdall_Data.config.stinkyTracker.enabled then
end
for name, stinky in pairs(arrivedStinkies) do
shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Added stinky from ARRIVED: %s (%s)", ModuleName, name, stinky.class)) print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end end
return
end end
end
-- Log total stinky count after processing local name = UnitName(unit)
if Heimdall_Data.config.stinkyTracker.debug then if not UnitIsPlayer(unit) then
local count = 0
for _ in pairs(shared.stinkyTracker.stinkies:get()) do
count = count + 1
end
print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count))
end
for name, stinky in pairs(shared.stinkyTracker.stinkies) do
if Heimdall_Data.config.agents[name] then
shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name)) print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name))
end end
return
end end
end
end)
local targetFrame = CreateFrame("Frame") local enemy = UnitCanAttack("player", unit)
targetFrame:RegisterEvent("UNIT_TARGET") if enemy then
targetFrame:SetScript("OnEvent", function(self, event, unit) if Heimdall_Data.config.stinkyTracker.debug then
if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name))
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) end
end shared.stinkyTracker.stinkies[name] = {
unit = "target" name = name,
class = UnitClass(unit),
seenAt = GetTime(),
hostile = true,
}
return
end
if not shared.stinkyTracker.stinkies[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name))
end
return
end
if not Heimdall_Data.config.stinkyTracker.enabled then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name))
end end
return shared.stinkyTracker.stinkies[name] = nil
end end)
local name = UnitName(unit) if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Module initialized", ModuleName)) end
if not UnitIsPlayer(unit) then print(string.format("[%s] Module initialized", ModuleName))
if Heimdall_Data.config.stinkyTracker.debug then end,
print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name)) }
end
return
end
local enemy = UnitCanAttack("player", unit)
if enemy then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name))
end
shared.stinkyTracker.stinkies[name] = {
name = name,
class = UnitClass(unit),
seenAt = GetTime(),
hostile = true,
}
return
end
if not shared.stinkyTracker.stinkies[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name))
end
return
end
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name))
end
shared.stinkyTracker.stinkies[name] = nil
end)
if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print("[Heimdall] StinkyTracker loaded")
end

File diff suppressed because it is too large Load Diff

2
_L.lua
View File

@@ -90,6 +90,7 @@ shared._Locale = {
updateInterval = "Update Interval", updateInterval = "Update Interval",
networkMessenger = "Network Messenger", networkMessenger = "Network Messenger",
queries = "Who queries", queries = "Who queries",
chatSniffer = "Chat Sniffer",
}, },
ru = { ru = {
bonkDetected = "%s ударил %s (%s)", bonkDetected = "%s ударил %s (%s)",
@@ -175,6 +176,7 @@ shared._Locale = {
updateInterval = "Интервал Обновления", updateInterval = "Интервал Обновления",
networkMessenger = "Сетевой Мессенджер", networkMessenger = "Сетевой Мессенджер",
queries = "Запросы Who", queries = "Запросы Who",
chatSniffer = "Сниффер Чата",
["Orgrimmar"] = "Оргриммар", ["Orgrimmar"] = "Оргриммар",
["Valley of Strength"] = "Долина Силы", ["Valley of Strength"] = "Долина Силы",
["Valley of Trials"] = "Долина Испытаний", ["Valley of Trials"] = "Долина Испытаний",

View File

@@ -11,13 +11,16 @@ if [ -z "$TAG" ]; then
TAG="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}" TAG="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}"
# Create a new tag # Create a new tag
git tag $TAG git tag $TAG
git push origin $TAG
fi fi
echo "Tag: $TAG" echo "Tag: $TAG"
echo "Building the thing..." echo "Building the thing..."
sed -i "s/## Version: $CURRENT_VERSION/## Version: $NEW_VERSION/" Heimdall.toc sed -i "s/## Version: .*/## Version: $TAG/" Heimdall.toc
sed -i "s/local VERSION = \"$CURRENT_VERSION\"/local VERSION = \"$NEW_VERSION\"/" Heimdall.lua sed -i "s/local VERSION = .*/local VERSION = \"$TAG\"/" Heimdall.lua
git add Heimdall.toc Heimdall.lua
git commit -m "Release $TAG"
git tag -f $TAG
git push origin $TAG
rm Heimdall-${TAG}.zip rm Heimdall-${TAG}.zip
mkdir Heimdall mkdir Heimdall
@@ -25,7 +28,7 @@ cp *.lua *.toc Heimdall
cp -r Modules Heimdall cp -r Modules Heimdall
cp -r Sounds Heimdall cp -r Sounds Heimdall
cp -r Texture Heimdall cp -r Texture Heimdall
zip -r Heimdall-${TAG}.zip Heimdall 7z a Heimdall-${TAG}.zip Heimdall
rm -rf Heimdall rm -rf Heimdall
echo "Creating a release..." echo "Creating a release..."

View File

@@ -1 +1,12 @@
C:/Users/Administrator/Seafile/Games-WoW/Ruski/Interface/AddOns/Heimdall/Meta/stylua.toml syntax = "All" # Specify a disambiguation for the style of Lua syntax being formatted. Possible options: All (default), Lua51, Lua52, Lua53, Lua54, LuaJIT, Luau, CfxLua
column_width = 120 # Approximate line length for printing. Used as a guide for line wrapping - this is not a hard requirement: lines may fall under or over the limit.
line_endings = "Windows" # Line endings type. Possible options: Unix (LF) or Windows (CRLF)
indent_type = "Tabs" # Indent type. Possible options: Tabs or Spaces
indent_width = 4 # Character size of single indentation. If indent_type is set to Tabs, this option is used as a heuristic to determine column width only.
quote_style = "AutoPreferDouble" # Quote style for string literals. Possible options: AutoPreferDouble, AutoPreferSingle, ForceDouble, ForceSingle. AutoPrefer styles will prefer the specified quote style, but fall back to the alternative if it has fewer string escapes. Force styles always use the specified style regardless of escapes.
call_parentheses = "Always" # Whether parentheses should be applied on function calls with a single string/table argument. Possible options: Always, NoSingleString, NoSingleTable, None, Input. Always applies parentheses in all cases. NoSingleString omits parentheses on calls with a single string argument. Similarly, NoSingleTable omits parentheses on calls with a single table argument. None omits parentheses in both cases. Note: parentheses are still kept in situations where removal can lead to obscurity (e.g. foo "bar".setup -> foo("bar").setup, since the index is on the call result, not the string). Input removes all automation and preserves parentheses only if they were present in input code: consistency is not enforced.
space_after_function_names = "Never" # Specify whether to add a space between the function name and parentheses. Possible options: Never, Definitions, Calls, or Always
collapse_simple_statement = "Always" # Specify whether to collapse simple statements. Possible options: Never, FunctionOnly, ConditionalOnly, or Always
[sort_requires]
enabled = false