- Add account lookup for tells

Sasky [09-16-14 - 09:14]
- Add account lookup for tells
- Add in queue process to main tick
- Handle response for sent invites
- Misc cleanup from splitting into modules
Filename
AutoInvite.lua
lua/guild.lua
lua/kick.lua
lua/queue.lua
diff --git a/AutoInvite.lua b/AutoInvite.lua
index 9a5f61f..bcdd83e 100644
--- a/AutoInvite.lua
+++ b/AutoInvite.lua
@@ -35,37 +35,39 @@ end
 ------------------------------------------------
 --- Event handlers
 ------------------------------------------------
+
+--Main callback fired on chat message
 AutoInvite.callback = function(_, messageType, from, message)
 	if not AutoInvite.enabled or not AutoInvite.listening then
 		return
 	end

-    --TODO: Switch to queue-based with timeouts so don't send out too many invites
+    --TODO: Move this to the actual invite send so not per-message
 	if GetGroupSize() >= AutoInvite.cfg.maxSize then
         echo("Group full. Disabling AutoInvite")
         AutoInvite.stopListening()
 	end

 	if string.lower(message) == AutoInvite.cfg.watchStr and from ~= nil and from ~= "" then
-		if (messageType >= CHAT_CHANNEL_GUILD_1 and messageType <= CHAT_CHANNEL_OFFICER_5) then
-			from = AutoInvite.guildLookup(messageType, from)
-			if from == "" then return end
+		if (messageType >= CHAT_CHANNEL_GUILD_1 and messageType <= CHAT_CHANNEL_OFFICER_5) or messageType == CHAT_CHANNEL_WHISPER then
+			from = AutoInvite.accountNameLookup(messageType, from)
+			if from == "" or from == nil then return end
         end
-
-        --TODO: Add friends list lookup

 		from = from:gsub("%^.+", "")
         echo("Sending invite to " .. from)
-		GroupInvite(from)
-		GroupInviteByName(from)
+        AutoInvite:invitePlayer(from)
 	end

 	--d("Checking message '" .. string.lower(message) .."' ?= '" .. AutoInvite.cfg.watchStr .."'")
 end

-AutoInvite.playerLeave = function()
-    if AutoInvite.enabled and AutoInvite.cfg.restart and GetGroupSize() < AutoInvite.cfg.maxSize then
-        echo("Now space in group. Restarted listening.")
+--
+AutoInvite.playerLeave = function(_, unitTag, connectStatus)
+    if AutoInvite.enabled and AutoInvite.cfg.restart then
+        if not AutoInvite.listening then
+            echo("Now space in group. Restarted listening.")
+        end
         AutoInvite.startListening()
     end

@@ -86,24 +88,35 @@ end

 function AutoInvite.disbandEvent()
     if AutoInvite.enabled and AutoInvite.cfg.restart then
-        echo("Group disbanded. Restarted listening.")
-        AutoInvite.startListening()
+        --Group size checks in the listening function
+        AutoInvite.startListening(true)
     end
 end

--- tick function: called every 15s
+-- tick function: called every 1s
 function AutoInvite.tick()
-    AutoInvite.kickCheck()
+    local self = AutoInvite
+    self.kickCheck()
+
+    if self.listening then
+        if GetGroupSize() >= self.cfg.maxSize then
+            self.stopListening()
+        else
+            self:checkSentInvites()
+            self:processQueue()
+        end
+    end
 end

 ------------------------------------------------
---- Main interface
+--- Event control
 ------------------------------------------------
 AutoInvite.disable = function()
 	AutoInvite.enabled = false
     AutoInvite.stopListening()
-    EVENT_MANAGER:UnregisterForUpdate("AutoInviteKickCheck")
+    EVENT_MANAGER:UnregisterForUpdate(AutoInvite.AddonId)
     EVENT_MANAGER:UnregisterForEvent(AutoInvite.AddonId, EVENT_GROUP_DISBANDED)
+    EVENT_MANAGER:UnregisterForEvent(AutoInvite.AddonId, EVENT_GROUP_INVITE_RESPONSE)
 end

 AutoInvite.stopListening = function()
@@ -111,22 +124,27 @@ AutoInvite.stopListening = function()
     AutoInvite.listening = false
 end

-AutoInvite.startListening = function()
+--@param restart: (boolean) - true if restarted listening due to space open up
+--currently only used for different print strings
+AutoInvite.startListening = function(restart)
     if not AutoInvite.enabled then
         AutoInvite.enabled = true
         AutoInvite.checkOffline()
-        EVENT_MANAGER:RegisterForUpdate("AutoInviteKickCheck", 1000, AutoInvite.tick)
-        EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_DISBANDED)
+        EVENT_MANAGER:RegisterForUpdate(AutoInvite.AddonId, 1000, AutoInvite.tick)
+        EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_DISBANDED, AutoInvite.disbandEvent)
+        EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_INVITE_RESPONSE, AutoInvite.inviteResponse)
     end

 	if not AutoInvite.listening and GetGroupSize() < AutoInvite.cfg.maxSize then
 		--Add handler
 		EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_CHAT_MESSAGE_CHANNEL, AutoInvite.callback)
 		AutoInvite.listening = true
+        if restart ~= nil then
+            echo("Now space in group. AutoInvite restarted listening on '" ..  AutoInvite.cfg.watchStr .. "'")
+        else
+            echo("AutoInvite listening on string '" .. AutoInvite.cfg.watchStr .. "'")
+        end
     end
-
-    --First check. When option, will have to check option for disable
-	echo("AutoInvite listening on string '" .. AutoInvite.cfg.watchStr .. "'")
 end

 ------------------------------------------------
@@ -149,6 +167,10 @@ AutoInvite.init = function()
     AutoInvite.cfg = ZO_SavedVars:NewAccountWide("AutoInviteSettings", 1.0, "config", def)
     EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_MEMBER_LEFT, AutoInvite.playerLeave)
     EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_MEMBER_CONNECTED_STATUS, AutoInvite.offlineEvent)
+
+    --Make sure Offline is updated after player zones (is offline for a bit
+    EVENT_MANAGER:RegisterForEvent("AutoInviteInit", EVENT_PLAYER_ACTIVATED, AutoInvite.checkOffline)
+
     AutoInvite.listening = false
     AutoInvite.enabled = false
     AutoInviteUI.init()
diff --git a/lua/guild.lua b/lua/guild.lua
index 38d77d6..6032f8a 100644
--- a/lua/guild.lua
+++ b/lua/guild.lua
@@ -22,39 +22,68 @@ local function echo(msg) CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("

 AutoInvite = AutoInvite or {}

-AutoInvite.guildLookup = function(channel, acctName)
-    local guildId = 0
-    if channel == CHAT_CHANNEL_GUILD_1 or channel == CHAT_CHANNEL_OFFICER_1 then guildId = GetGuildId(1) end
-    if channel == CHAT_CHANNEL_GUILD_2 or channel == CHAT_CHANNEL_OFFICER_2 then guildId = GetGuildId(2) end
-    if channel == CHAT_CHANNEL_GUILD_3 or channel == CHAT_CHANNEL_OFFICER_3 then guildId = GetGuildId(3) end
-    if channel == CHAT_CHANNEL_GUILD_4 or channel == CHAT_CHANNEL_OFFICER_4 then guildId = GetGuildId(4) end
-    if channel == CHAT_CHANNEL_GUILD_5 or channel == CHAT_CHANNEL_OFFICER_5 then guildId = GetGuildId(5) end
+function AutoInvite.executeNameLookup(hasChar, charName, zone)
+    if not hasChar then
+        echo("Could not find player name for " .. acctName .. ". Please manually invite.")
+        return ""
+    end
+
+    charName = charName:gsub("%^.+", "")
+
+    if AutoInvite.cfg.cyrCheck then
+        dbg("In Cyrodiil? " .. b(AutoInvite.isCyrodiil()) .. " / Zone: " .. zone)
+
+        if AutoInvite.isCyrodiil() and zone ~= "Cyrodiil" then
+            echo("Player " .. charName .. " is not in Cyrodiil but in " .. zone)
+            echo("Blocking invite to prevent crashes.")
+            return ""
+        end
+    end

-    if guildId == 0 then d("Error - couldn't invite on channel: " .. channel) end
+    return charName
+end

+function AutoInvite.guildLookup(guildId, acctName)
     local aName
     for i=1,GetNumGuildMembers(guildId) do
         aName = GetGuildMemberInfo(guildId,i)
         if aName == acctName then
-            local hasChar, charName, zone = GetGuildMemberCharacterInfo(guildId,i)
-            if not hasChar then
-                echo("Could not find player name for " .. acctName .. ". Please manually invite.")
-                return ""
-            end
+            return AutoInvite.executeNameLookup(GetGuildMemberCharacterInfo(guildId,i))
+        end
+    end
+end

-            charName = charName:gsub("%^.+", "")
+function AutoInvite.friendLookup(acctName)
+    for i=1,GetNumFriends() do
+        local aName = GetFriendInfo(i)
+        if aName == acctName then
+            return AutoInvite.executeNameLookup(GetFriendCharacterInfo(i))
+        end
+    end
+    return nil
+end

-            if AutoInvite.cfg.cyrCheck then
-                dbg("In Cyrodiil? " .. b(AutoInvite.isCyrodiil()) .. " / Zone: " .. zone)
+function AutoInvite.accountNameLookup(channel, acctName)
+    local guildId = 0
+    if channel == CHAT_CHANNEL_GUILD_1 or channel == CHAT_CHANNEL_OFFICER_1 then guildId = GetGuildId(1) end
+    if channel == CHAT_CHANNEL_GUILD_2 or channel == CHAT_CHANNEL_OFFICER_2 then guildId = GetGuildId(2) end
+    if channel == CHAT_CHANNEL_GUILD_3 or channel == CHAT_CHANNEL_OFFICER_3 then guildId = GetGuildId(3) end
+    if channel == CHAT_CHANNEL_GUILD_4 or channel == CHAT_CHANNEL_OFFICER_4 then guildId = GetGuildId(4) end
+    if channel == CHAT_CHANNEL_GUILD_5 or channel == CHAT_CHANNEL_OFFICER_5 then guildId = GetGuildId(5) end

-                if AutoInvite.isCyrodiil() and zone ~= "Cyrodiil" then
-                    echo("Player " .. charName .. " is not in Cyrodiil but in " .. zone)
-                    echo("Blocking invite to prevent crashes.")
-                    return ""
-                end
-            end
+    if guildId > 0 then
+        AutoInvite.guildLookup(guildId, acctName)
+    else
+        --Came in on whisper channel, so try friends then move to all guilds
+        local charName = AutoInvite.friendLookup(acctName)
+        if charName then return charName end

-            return charName
+        for i=1,5 do
+            guildId = GetGuildId(i)
+            charName = AutoInvite.guildLookup(guildId, acctName)
+            if charName then return charName end
         end
+
+        d("Error - couldn't invite on channel: " .. channel)
     end
 end
\ No newline at end of file
diff --git a/lua/kick.lua b/lua/kick.lua
index 83421da..39a9ec5 100644
--- a/lua/kick.lua
+++ b/lua/kick.lua
@@ -16,6 +16,10 @@
 -- along with this program.  If not, see <http://www.gnu.org/licenses/>.

 AutoInvite = AutoInvite or {}
+local function b(v) if v then return "T" else return "F" end end
+local function nn(val) if val == nil then return "NIL" else return val end end
+local function dbg(msg) if AutoInvite.debug then d("|c999999" .. msg) end end
+local function echo(msg) CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("|CFFFF00"..msg) end

 AutoInvite.kickTable = {}
 function AutoInvite.checkOffline()
diff --git a/lua/queue.lua b/lua/queue.lua
index 90b0319..f33aa97 100644
--- a/lua/queue.lua
+++ b/lua/queue.lua
@@ -16,6 +16,10 @@
 -- along with this program.  If not, see <http://www.gnu.org/licenses/>.

 AutoInvite = AutoInvite or {}
+local function b(v) if v then return "T" else return "F" end end
+local function nn(val) if val == nil then return "NIL" else return val end end
+local function dbg(msg) if AutoInvite.debug then d("|c999999" .. msg) end end
+local function echo(msg) CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("|CFFFF00"..msg) end

 -- FIFO invite queue
 local queue = {
@@ -29,9 +33,10 @@ function queue:size()
 end

 function queue:push(val)
+    AI_SmallGroupListing:updateSingle(val)
     local back = self.back
     self.vals[back] = val
-    queue.back = back + 1
+    self.back = back + 1
 end

 function queue:pop()
@@ -65,9 +70,14 @@ function AutoInvite:processQueue()
         self.sentInvites = self.sentInvites + 1
         self.sentInvite[name] = now
         GroupInviteByName(name)
+        AI_SmallGroupListing:updateSingle(name)
     end
 end

+function AutoInvite:IsInviteSent(name)
+    return AutoInvite.sentInvite[name]
+end
+
 function AutoInvite:checkSentInvites()
     local now = GetTimeStamp()

@@ -89,16 +99,70 @@ function AutoInvite:resetQueues()
     queue:reset()
 end

+function AutoInvite:IsInQueue(name)
+    for i = queue.front, queue.back do
+        if queue.vals[i] == name then
+            return i
+        end
+    end
+    return nil
+end
+
+function AutoInvite.__getQueue()
+    return queue.vals
+end

 function AutoInvite:resetGroup()
     self:resetQueues()
     for i=1,GetGroupSize() do
         local tag = GetGroupUnitTagByIndex(i)
         local name = GetUnitName(tag)
-        table.insert(self.toInvite, name)
+        queue:push(name)
         GroupDisband()
         GroupLeave() --for group leader bug
     end
+    self:processQueue()
+end
+
+local responseCodes = {
+    [GROUP_INVITE_RESPONSE_ACCEPTED] = "accept",
+    [GROUP_INVITE_RESPONSE_ALREADY_GROUPED] = "inGroup",
+    [GROUP_INVITE_RESPONSE_CONSIDERING_OTHER] = "hasInv",
+    [GROUP_INVITE_RESPONSE_DECLINED] = "decline",
+    [GROUP_INVITE_RESPONSE_GROUP_FULL] = "full",
+    [GROUP_INVITE_RESPONSE_IGNORED] = "ignored",
+    [GROUP_INVITE_RESPONSE_INVITED] = "invited?",
+    [GROUP_INVITE_RESPONSE_ONLY_LEADER_CAN_INVITE] = "notLead",
+    [GROUP_INVITE_RESPONSE_OTHER_ALLIANCE] = "otherAlly",
+    [GROUP_INVITE_RESPONSE_PLAYER_NOT_FOUND] = "noPlayer",
+    [GROUP_INVITE_RESPONSE_SELF_INVITE] = "self",
+}
+
+local responseCodeInGroup = {
+    [GROUP_INVITE_RESPONSE_ACCEPTED] = true,
+    [GROUP_INVITE_RESPONSE_ALREADY_GROUPED] = true, --Maybe
+    [GROUP_INVITE_RESPONSE_CONSIDERING_OTHER] = true, --Maybe
+    [GROUP_INVITE_RESPONSE_DECLINED] = false,
+    [GROUP_INVITE_RESPONSE_GROUP_FULL] = false,
+    [GROUP_INVITE_RESPONSE_IGNORED] = false,
+    [GROUP_INVITE_RESPONSE_INVITED] = true, --Maybe
+    [GROUP_INVITE_RESPONSE_ONLY_LEADER_CAN_INVITE] = true,
+    [GROUP_INVITE_RESPONSE_OTHER_ALLIANCE] = false,
+    [GROUP_INVITE_RESPONSE_PLAYER_NOT_FOUND] = false,
+    [GROUP_INVITE_RESPONSE_SELF_INVITE] = true,
+}
+
+function AutoInvite.inviteResponse(_, name, responseCode)
+    dbg("Invite response: " .. name .. " : (" .. responseCode .. ") " .. nn(responseCodes[responseCode]))
+    if AutoInvite.sentInvite[name] ~= nil then
+        --TODO: Build in a retry invite for some options
+        AutoInvite.sentInvite[name] = nil
+        AutoInvite.sentInvites = math.max(AutoInvite.sentInvites - 1, 0)
+
+        if not responseCodeInGroup[responseCode] then
+            AI_SmallGroupListing:removeSingle(name)
+        end
+    end
 end

 --Interface to queue