local addonname, shared = ... ---@cast shared HeimdallShared ---@cast addonname string local ModuleName = "Inviter" ---@diagnostic disable-next-line: missing-fields shared.Inviter = {} function shared.Inviter.Init() if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then Heimdall_Data.config.inviter.listeningChannel = { [Heimdall_Data.config.inviter.listeningChannel] = true } end ---@type Timer local updateTimer = nil local function FixGroup() if Heimdall_Data.config.inviter.debug then print(string.format("[%s] Checking and fixing group configuration", ModuleName)) end if not IsInRaid() then if Heimdall_Data.config.inviter.debug then print(string.format("[%s] Converting party to raid", 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 for name, _ in pairs(Heimdall_Data.config.agents) do 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 print(string.format("[%s] button = %s", ModuleName, tostring(button))) 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 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 local button = framePool[frame.unit] or CreateFrame("Button", string.format("HeimdallKickButton%s", frame.unit, frame, "SecureActionButtonTemplate")) framePool[frame.unit] = button 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 ---@type table local groupMembers = {} local function CleanGroups() if not Heimdall_Data.config.inviter.kickOffline then return end print("Cleaning groups") local now = GetTime() for i = 1, 40 do local unit = "raid" .. i if UnitExists(unit) then 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 local afkPlayers = {} for name, time in pairs(groupMembers) do if not UnitInParty(name) then print(string.format("%s no longer in party", name)) groupMembers[name] = nil else if time < now - Heimdall_Data.config.inviter.afkThreshold then print(string.format("Kicking %s for being offline", name)) afkPlayers[#afkPlayers + 1] = name -- Blyat this is protected... -- UninviteUnit(name) end end end OverlayKickButtons(afkPlayers) 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.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 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 print("[Heimdall] Inviter loaded") end