Files
Freewood-OctoWoW-Core/Core/Channels.lua
T

339 lines
11 KiB
Lua

-- Freewood-OctoWoW-Core: Kanaele und ihre Mitglieder (nur lesen, alles UNGEPRUEFT im Spiel)
-- Datenquelle: GetChannelList() + ListChannelByName(n) -> Ereignis CHAT_MSG_CHANNEL_LIST (arg1 = Namensliste).
-- Alle Aufrufe laufen geschuetzt (pcall). /fwchans zeigt Rohdaten zur Pruefung.
local ADDON, FW = ...
local G = _G
local Ch = {}
FW.Channels = Ch
Ch.list = {} -- { {num=, name=, members={...}, count=, stamp=} }
Ch.byNum = {}
Ch.queue = {}
Ch.current = nil
Ch.lastSend = 0
Ch.probe = false
Ch.version = 0 -- steigt bei jeder Aenderung, die UI prueft das
local SPACING = 0.8 -- Sekunden zwischen zwei Abfragen
local TIMEOUT = 4 -- Sekunden bis eine Abfrage als verloren gilt
local SUPPRESS = 5 -- so lange nach einer Abfrage wird die Chatausgabe der Liste unterdrueckt
local function P(t)
if G.DEFAULT_CHAT_FRAME then
G.DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffFWChans|r " .. tostring(t))
end
end
local function Short(s, n)
s = tostring(s)
n = n or 160
if string.len(s) > n then return string.sub(s, 1, n) .. "..." end
return s
end
-- Kanalliste lesen: 1.12 liefert num, name, num, name ... (spaeter evtl. mit Zusatzwert)
function Ch.ReadList()
local out = {}
if type(G.GetChannelList) ~= "function" then return out end
local res = { pcall(G.GetChannelList) }
if not res[1] then return out end
local i = 2
local n = table.getn(res)
local lastNum
while i <= n do
local v = res[i]
if type(v) == "number" then
lastNum = v
elseif type(v) == "string" and lastNum then
out[table.getn(out) + 1] = { num = lastNum, name = v }
lastNum = nil
end
i = i + 1
end
return out
end
-- eigenes Entfernen (table.remove zusammen mit table.getn verhaelt sich auf dem Client eigenartig)
local function RemoveAt(t, idx)
local n = table.getn(t)
local k = idx
while k < n do t[k] = t[k + 1]; k = k + 1 end
t[n] = nil
end
local function Bump() Ch.version = Ch.version + 1 end
-- Liste neu einlesen, bekannte Mitglieder behalten
function Ch.Rescan()
local fresh = Ch.ReadList()
local old = Ch.byNum
Ch.byNum = {}
Ch.list = {}
local k = 1
while k <= table.getn(fresh) do
local e = fresh[k]
local prev = old[e.num]
if prev and prev.name == e.name then
e.members = prev.members
e.count = prev.count
e.stamp = prev.stamp
else
e.members = {}
e.count = 0
end
Ch.byNum[e.num] = e
Ch.list[table.getn(Ch.list) + 1] = e
k = k + 1
end
Bump()
end
-- Mitglieder aller Kanaele abfragen (nacheinander)
function Ch.RefreshAll()
Ch.Rescan()
Ch.queue = {}
local k = 1
while k <= table.getn(Ch.list) do
Ch.queue[table.getn(Ch.queue) + 1] = Ch.list[k].num
k = k + 1
end
end
function Ch.RefreshOne(num)
Ch.queue[table.getn(Ch.queue) + 1] = num
end
function Ch.Pending()
return table.getn(Ch.queue) + (Ch.current and 1 or 0)
end
local function Send(num)
Ch.current = num
Ch.sentAt = (G.GetTime and G.GetTime()) or 0
Ch.lastSend = Ch.sentAt
local ok, err = pcall(G.ListChannelByName, num)
if not ok and Ch.probe then P("ListChannelByName(" .. tostring(num) .. ") Fehler: " .. Short(err)) end
end
local pump = CreateFrame("Frame")
pump:SetScript("OnUpdate", function()
local now = (G.GetTime and G.GetTime()) or 0
if Ch.current and now - Ch.sentAt > TIMEOUT then
Ch.current = nil
Ch.timeouts = (Ch.timeouts or 0) + 1
end
if not Ch.current and table.getn(Ch.queue) > 0 and now - Ch.lastSend >= SPACING then
local num = Ch.queue[1]
RemoveAt(Ch.queue, 1)
if Ch.byNum[num] then Send(num) end
Bump()
end
end)
-- Namensliste aus arg1 zerlegen: "Name1, Name2*, Name3" -> Liste (Markierungen entfernt)
function Ch.ParseNames(text)
local out = {}
if type(text) ~= "string" then return out end
local rest = text
-- evtl. Kopf "Players in channel ...:" abtrennen
local pos = string.find(rest, ":", 1, true)
if pos and pos < 60 and string.find(string.sub(rest, 1, pos), " ", 1, true) then
rest = string.sub(rest, pos + 1)
end
local iter = string.gmatch or string.gfind
for part in iter(rest, "[^,]+") do
local name = string.gsub(part, "^%s+", "")
name = string.gsub(name, "%s+$", "")
name = string.gsub(name, "%*", "")
name = string.gsub(name, "|c%x%x%x%x%x%x%x%x", "")
name = string.gsub(name, "|r", "")
if name ~= "" then out[table.getn(out) + 1] = name end
end
return out
end
local function OnList(a1, a2, a3, a4, a5, a6, a7, a8, a9)
if Ch.probe then
P("CHAT_MSG_CHANNEL_LIST arg1=" .. Short(a1) .. " | arg2=" .. tostring(a2) .. " arg4=" .. tostring(a4) ..
" arg7=" .. tostring(a7) .. " arg8=" .. tostring(a8) .. " arg9=" .. tostring(a9))
end
local num = tonumber(a8)
local e = num and Ch.byNum[num]
if not e and Ch.current then e = Ch.byNum[Ch.current] end
if not e and a9 then
local k = 1
while k <= table.getn(Ch.list) do
if Ch.list[k].name == a9 then e = Ch.list[k] end
k = k + 1
end
end
if not e then return end
e.members = Ch.ParseNames(a1)
e.count = table.getn(e.members)
e.stamp = (G.GetTime and G.GetTime()) or 0
if Ch.current == e.num then Ch.current = nil end
Bump()
end
local function OnJoin(a1, a2, a3, a4, a5, a6, a7, a8, a9)
if Ch.probe then P("CHANNEL_JOIN arg2=" .. tostring(a2) .. " arg8=" .. tostring(a8) .. " arg9=" .. tostring(a9)) end
local e = tonumber(a8) and Ch.byNum[tonumber(a8)]
if e and type(a2) == "string" and a2 ~= "" then
local k = 1
while k <= table.getn(e.members) do
if e.members[k] == a2 then return end
k = k + 1
end
e.members[table.getn(e.members) + 1] = a2
e.count = table.getn(e.members)
Bump()
end
end
local function OnLeave(a1, a2, a3, a4, a5, a6, a7, a8, a9)
if Ch.probe then P("CHANNEL_LEAVE arg2=" .. tostring(a2) .. " arg8=" .. tostring(a8) .. " arg9=" .. tostring(a9)) end
local e = tonumber(a8) and Ch.byNum[tonumber(a8)]
if e and type(a2) == "string" then
local k = 1
while k <= table.getn(e.members) do
if e.members[k] == a2 then
RemoveAt(e.members, k)
e.count = table.getn(e.members)
Bump()
return
end
k = k + 1
end
end
end
local function OnNotice(a1, a2, a3, a4, a5, a6, a7, a8, a9)
if Ch.probe then P("CHANNEL_NOTICE arg1=" .. tostring(a1) .. " arg4=" .. tostring(a4) .. " arg8=" .. tostring(a8) .. " arg9=" .. tostring(a9)) end
if a1 == "YOU_JOINED" or a1 == "YOU_LEFT" or a1 == "YOU_CHANGED" then
Ch.dirty = true
end
end
FW:RegisterEvent("CHAT_MSG_CHANNEL_LIST", OnList, Ch)
FW:RegisterEvent("CHAT_MSG_CHANNEL_JOIN", OnJoin, Ch)
FW:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE", OnLeave, Ch)
FW:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", OnNotice, Ch)
-- Die Listenausgabe des Spiels soll nicht im Chat landen, solange wir selbst abfragen
if type(G.ChatFrame_AddMessageEventFilter) == "function" then
local function Hide()
if Ch.probe then return false end
local now = (G.GetTime and G.GetTime()) or 0
if Ch.current or (now - Ch.lastSend) < SUPPRESS then return true end
return false
end
pcall(G.ChatFrame_AddMessageEventFilter, "CHAT_MSG_CHANNEL_LIST", Hide)
Ch.filter = true
end
-- Aktionen (geschuetzt)
function Ch.Whisper(name)
if type(G.ChatFrame_SendTell) == "function" then pcall(G.ChatFrame_SendTell, name) end
end
function Ch.Invite(name)
if type(G.InviteByName) == "function" then pcall(G.InviteByName, name)
elseif type(G.InviteUnit) == "function" then pcall(G.InviteUnit, name) end
end
function Ch.Ignore(name)
if type(G.AddIgnore) == "function" then pcall(G.AddIgnore, name) end
end
-- Suche ueber alle Kanaele: Liste { {name=, chans={Kanalnamen}} } (Teiltreffer, ohne Gross/Klein)
function Ch.Find(text, limit)
local out = {}
text = string.lower(text or "")
if text == "" then return out end
local byName = {}
local k = 1
while k <= table.getn(Ch.list) do
local e = Ch.list[k]
local m = 1
while m <= table.getn(e.members) do
local nm = e.members[m]
if string.find(string.lower(nm), text, 1, true) then
local r = byName[nm]
if not r then
if limit and table.getn(out) >= limit then
Ch.moreFound = true
else
r = { name = nm, chans = {} }
byName[nm] = r
out[table.getn(out) + 1] = r
end
end
if r then r.chans[table.getn(r.chans) + 1] = e.num .. ". " .. e.name end
end
m = m + 1
end
k = k + 1
end
table.sort(out, function(a, b) return a.name < b.name end)
return out
end
-- /fwchans [find <name>|probe|list]
G.SLASH_FWCHANS1 = "/fwchans"
G.SlashCmdList = G.SlashCmdList or {}
G.SlashCmdList["FWCHANS"] = function(msg)
msg = msg or ""
local cmd, rest = string.match and string.match(msg, "^(%S*)%s*(.*)$")
if not cmd then
local _, _, c, r = string.find(msg, "^(%S*)%s*(.*)$")
cmd, rest = c or "", r or ""
end
cmd = string.lower(cmd or "")
if cmd == "find" then
local res = Ch.Find(rest, 20)
if table.getn(res) == 0 then P("keine Treffer (Mitglieder bekannt: " .. Ch.TotalMembers() .. ")") end
local k = 1
while k <= table.getn(res) do
P(res[k].name .. ": " .. table.concat(res[k].chans, ", "))
k = k + 1
end
return
end
if cmd == "list" then
Ch.Rescan()
local k = 1
while k <= table.getn(Ch.list) do
local e = Ch.list[k]
P(e.num .. ". " .. e.name .. " - " .. e.count .. " Mitglieder" .. (e.stamp and "" or " (noch nicht abgefragt)"))
k = k + 1
end
return
end
-- Standard: Rohdaten zeigen und Abfrage mit Rohausgabe starten
Ch.probe = true
local raw = { pcall(G.GetChannelList) }
local parts = {}
local i = 2
while i <= table.getn(raw) do parts[table.getn(parts) + 1] = type(raw[i]) .. ":" .. tostring(raw[i]); i = i + 1 end
P("GetChannelList: " .. table.concat(parts, " | "))
P("Filter=" .. tostring(Ch.filter) .. " ListChannelByName=" .. type(G.ListChannelByName))
Ch.RefreshAll()
P("Abfrage laeuft (" .. Ch.Pending() .. " Kanaele). Rohdaten erscheinen unten. '/fwchans list' zeigt danach die Zahlen, '/fwchans find <Name>' sucht.")
Ch.probeUntil = (G.GetTime and G.GetTime() or 0) + 60
end
function Ch.TotalMembers()
local n, k = 0, 1
while k <= table.getn(Ch.list) do n = n + Ch.list[k].count; k = k + 1 end
return n
end
-- Rohausgabe nach 60 s wieder abschalten
local off = CreateFrame("Frame")
off:SetScript("OnUpdate", function()
if Ch.probe and Ch.probeUntil and G.GetTime and G.GetTime() > Ch.probeUntil then
Ch.probe = false
P("Rohausgabe aus.")
end
end)