3.1.0 asynchroneous goodness

git [04-14-18 - 16:36]
3.1.0 asynchroneous goodness
Filename
DailyAutoShare/DASHelper.lua
DailyAutoShare/DASMenu.lua
DailyAutoShare/DASUserSettingsAdapter.lua
DailyAutoShare/DailyAutoShare.txt
DailyAutoShare/DasChatMessage.lua
DailyAutoShare/DasGui.lua
DailyAutoShare/libs/LibAsync/LibAsync.lua
DailyAutoShare/questData/GoldCoast.lua
DailyAutoShare/questData/Morrowind.lua
DailyAutoShare/questData/Wrothgar.lua
DailyAutoShare/startup.lua
diff --git a/DailyAutoShare/DASHelper.lua b/DailyAutoShare/DASHelper.lua
index e146723..6a5b50f 100644
--- a/DailyAutoShare/DASHelper.lua
+++ b/DailyAutoShare/DASHelper.lua
@@ -18,13 +18,33 @@ function DAS.TryDisableAutoShare(fromName, messageText)
 	end
 end

+local alreadySharing = false
+local questQueue = {}
+local function shareQuestQueue()
+    -- d("shareQuestQueue called with " .. tostring(#questQueue) .. " entries")
+    if #questQueue == 0 then
+        alreadySharing = false
+        return
+    end
+    alreadySharing = true
+    local questIndex = table.remove(questQueue, 1)
+    ShareQuest(questIndex)
+    zo_callLater(shareQuestQueue, 250)
+end

 function DAS.TryShareActiveDaily()
-  if not DAS.GetAutoShare() then return end
-  for _, questIndex in ipairs(DAS.GetActiveQuestIndices()) do
-        if IsValidQuestIndex(questIndex) then ShareQuest(questIndex) end
+    if not DAS.GetAutoShare() then return end
+    local activeQuestIndices = DAS.GetActiveQuestIndices()
+    for _, questIndex in ipairs(activeQuestIndices) do
+        if IsValidQuestIndex(questIndex) and not table.contains(questQueue, questIndex) then
+           table.insert(questQueue, questIndex)
+        end
+    end
+    if not alreadySharing then
+        shareQuestQueue()
     end
  end
+

 local function EscapeString(text)
 	if nil == text then return "" end
@@ -63,9 +83,9 @@ function DAS.FindInList(array, item)
 end

 function DAS.TryTriggerAutoAcceptInvite()
-	if tonumber(DAS.GetAutoAcceptInviteInterval()) > 0 then
+	if DAS.GetAutoAcceptInviteInterval() then
 		DAS.SetAutoAcceptInvite(true)
-		zo_callLater(function() DAS.SetAutoAcceptInvite(false) end, (tonumber(DAS.GetAutoAcceptInviteInterval())*1000))
+		zo_callLater(DAS.SetAutoAcceptInvite, DAS.GetAutoAcceptInviteInterval()*1000)
 	end
 end

diff --git a/DailyAutoShare/DASMenu.lua b/DailyAutoShare/DASMenu.lua
index b315c13..9b64689 100644
--- a/DailyAutoShare/DASMenu.lua
+++ b/DailyAutoShare/DASMenu.lua
@@ -1,7 +1,11 @@
 local DAS = DailyAutoShare
-local questShareDefault = "I can give a DailyAutoShare for <<1>>, type <<2>> for an instant invite"
-function DAS.CreateMenu(savedVars, defaults)

+local optionsData
+local questShareDefault
+
+function DAS.CreateMenu(savedVars, defaults)
+
+    questShareDefault = defaults.questShareString
 	local LAM = LibStub:GetLibrary("LibAddonMenu-2.0")
 	local panelData = {
 		type    = "panel",
@@ -14,7 +18,7 @@ function DAS.CreateMenu(savedVars, defaults)

 	LAM:RegisterAddonPanel("DailyAutoShare_OptionsPanel", panelData)

-	local optionsData = { -- optionsData
+	optionsData = { -- optionsData
 		{ -- Use global configuration?
 			type    = "checkbox",
 			name    = "Turn on debugging?",
@@ -286,13 +290,14 @@ function DAS.CreateMenu(savedVars, defaults)
 			name        = "Look and feel and behavior",
 			controls    = {
 				{   -- editbox: Quest share text
-                    type    = "editbox",
-                    name    = "Quest share text",
-                    tooltip = ("Text to generate when you spam quest shares.\n"
+                    type        = "editbox",
+                    isExtraWide = true,
+                    name        = "Quest share text",
+                    tooltip     = ("Text to generate when you spam quest shares.\n"
                                 .. "<<1>> will be replaced with the quest names, <<2>> with the bingo codes.\n"
                                 .. "Omit either to remove parameter. Include neither and sound like a fool."),
-                    getFunc = function() return DAS.GetSettings().questShareString end,
-                    setFunc = function(value) DAS.GetSettings().questShareString = value end,
+                    getFunc     = function() return DAS.GetSettings().questShareString end,
+                    setFunc     = function(value) DAS.GetSettings().questShareString = value end,
                 },
 				{   -- editbox: Quest share text
                     type    = "button",
@@ -300,6 +305,7 @@ function DAS.CreateMenu(savedVars, defaults)
                     tooltip = "Reset quest share text to default value",
                     getFunc = function() return questShareDefault end,
                     setFunc = function(value) DAS.GetSettings().questShareString = questShareDefault end,
+                    reference = "qsButton",
                 },
 				{ -- checkbox: Lock UI window
 					type    = "checkbox",
@@ -321,6 +327,13 @@ function DAS.CreateMenu(savedVars, defaults)
 					getFunc = function() return DAS.GetUpsideDown() end,
 					setFunc = function(value) DAS.SetUpsideDown(value) end
 				},
+				{ -- checkbox: Start up minimized?
+					type    = "checkbox",
+					name    = "Start up minimized?",
+					tooltip = "Always minimize AddOn on first startup",
+					getFunc = function() return DAS.GetSettings().startupMinimized end,
+					setFunc = function(value) DAS.GetSettings().startupMinimized = value end
+				},
 				{ -- checkbox: AutoHide
 					type    = "checkbox",
 					name    = "Auto-hide when no open daily present?",
diff --git a/DailyAutoShare/DASUserSettingsAdapter.lua b/DailyAutoShare/DASUserSettingsAdapter.lua
index 418257b..bf0a1e6 100644
--- a/DailyAutoShare/DASUserSettingsAdapter.lua
+++ b/DailyAutoShare/DASUserSettingsAdapter.lua
@@ -106,6 +106,7 @@ local function OnGroupInvite()
 end

 function DAS.SetAutoAcceptInvite(value)
+    d("DAS.SetAutoAcceptInvite(" .. tostring(value)..")")
 	DAS.settings.autoAcceptInvite = value
     if value then
         EVENT_MANAGER:RegisterForEvent("DailyAutoshare", EVENT_GROUP_INVITE_RECEIVED, AcceptGroupInvite)
diff --git a/DailyAutoShare/DailyAutoShare.txt b/DailyAutoShare/DailyAutoShare.txt
index 8ad84c4..e375e31 100644
--- a/DailyAutoShare/DailyAutoShare.txt
+++ b/DailyAutoShare/DailyAutoShare.txt
@@ -1,12 +1,11 @@
 ## Title: DailyAutoShare
 ## Author: manavortex
-## Version: 3.0.7b
+## Version: 3.1.0
 ## APIVersion: 100022
 ## SavedVariables: DAS_Settings DAS_Globals
-## OptionalDependsOn: LibStub LibAddonMenu-2.0 LibMediaProvider-1.0 LibBinaryEncode pchat
+## OptionalDependsOn: LibStub LibAddonMenu-2.0 LibMediaProvider-1.0 pchat

 libs\LibStub\LibStub.lua
-libs\LibAsync\LibAsync.lua

 libs\LibAddonMenu-2.0\LibAddonMenu-2.0.lua
 libs\LibAddonMenu-2.0\controls\panel.lua
diff --git a/DailyAutoShare/DasChatMessage.lua b/DailyAutoShare/DasChatMessage.lua
index 6782520..1d732df 100644
--- a/DailyAutoShare/DasChatMessage.lua
+++ b/DailyAutoShare/DasChatMessage.lua
@@ -1,17 +1,18 @@
 local messageQueue              = {}
 local unittagplayer             = 'player'
-local cachedDisplayName         = DAS.pdn or GetUnitDisplayName('player')
 local share                     = "share"
 local stopsharing               = "stop sharing"

-local groupDelay                = 150
-local zoneDelay                 = 150
-
 local inviteQueue               = {}
-
+local alreadyInviting           = false
 local function popInviteQueue()
-    if #inviteQueue == 0 then return end
+    -- d("popInviteQueue called with " .. tostring(#inviteQueue) .. " entries")
+    if #inviteQueue == 0 then
+        alreadyInviting = false
+        return
+    end
     local playerName = table.remove(inviteQueue, 1)
+    -- d("inviting " .. playerName)
     GroupInviteByName(playerName)
     zo_callLater(popInviteQueue, 500)
 end
@@ -37,51 +38,75 @@ local function HandleGroupMessage(messageText, fromDisplayName)
 end

 local channelTypes = DAS.channelTypes
-local function HandleChatMessage(messageText, fromDisplayName)
+local stringPlus = "+"
+local stringAny = "+any"
+local function HandleChatMessage(messageText, fromDisplayName, calledRecursively)

     if not DAS.autoInviting then return end

-    local _, bingoCode = pcall(string.match, messageText, "[%+/]+%s?(%S%S%S+)%s?[%+/]?")
-    if not DAS.fullBingoString or not bingoCode then return end
-
-    local _, found = pcall(string.find, DAS.fullBingoString, bingoCode)
-    if found and not table.contains(inviteQueue, fromDisplayName) then
+    local found = stringAny == messageText -- it's +any
+
+    -- lower case regex
+    local _, bingoCode = pcall(string.match, messageText, "[%+/]+%s*(%w+)%s?[%+/]?")
+    if not found and not bingoCode then return end
+    local bingoIndex = DAS.getBingoTable()[bingoCode]
+    found = found or (nil ~= bingoIndex and DAS.activeZoneQuests[bingoIndex])
+
+    if not found then return HandleChatMessage(messageText:gsub(bingoCode, ""), fromDisplayName, true) end
+    if found and not table.contains(inviteQueue, fromDisplayName) then
+        -- d("found bingo " .. tostring(bingoCode) .. " (" .. tostring(bingoIndex) .. "), inviting " .. tostring(fromDisplayName))
         table.insert(inviteQueue, fromDisplayName)
-    elseif not found then
-        return HandleChatMessage(messageText:gsub(bingoCode, ""), fromDisplayName)
+        if not alreadyInviting then
+            popInviteQueue()
+        end
     end
-    zo_callLater(popInviteQueue,500)
 end

+local stringShare = "share"
+local stringQuest = "quest"
 function DAS.OnChatMessage(eventCode, channelType, fromName, messageText, _, fromDisplayName)
-    local isPlayerName

-    -- react to the group asking for shares
-    if (channelType == CHAT_CHANNEL_PARTY) and (messageText:find("share") or messageText:find("quest")) then
+    -- ignore all chat channels that aren't set
+    if nil == channelTypes[channelType] then return end
+
+    local isPlayerName = fromDisplayName:find(DAS.pdn)
+
+    -- if we aren't listening, or if we are listening and the message's from us, ignore it
+    if not channelTypes[channelType] or (channelTypes[channelType] and isPlayerName) then return end
+
+    -- if it's a group message, react to the group message
+    if (channelType == CHAT_CHANNEL_PARTY) and (messageText:find(stringShare) or messageText:find(stringQuest)) then
        DAS.TryShareActiveDaily()
-    elseif channelType == CHAT_CHANNEL_ZONE then
-        local isPlayerName = fromDisplayName:find(cachedDisplayName)
-        if isPlayerName and channelTypes[channelType] then return end
+       return
     end

-    if not (isPlayerName or channelTypes[channelType]) then return end
+    --  d(zo_strformat("[OnChatMessage] <<1>>: <<2>>, isPlayerName: <<3>>", fromDisplayName, messageText, tostring(isPlayerName)))

-    -- d(zo_strformat("[OnChatMessage] <<1>>: <<2>>", fromDisplayName, messageText))
-    local status, result = pcall(string.find, messageText, "%+")
-    if not result then return end
+    local _, result = pcall(string.find, messageText, "%+")
+    if not (result or #messageText <= 3) then return end

-    if isPlayerName then
-        if IsUnitGrouped('player') then
-            if DAS.GetGroupLeaveOnNewSearch() then GroupLeave() end
-        else
-            DAS.TryTriggerAutoAcceptInvite()
-        end
+    if isPlayerName then
+        local groupStatus = IsUnitGrouped(unittagplayer)
+        if groupStatus and DAS.GetGroupLeaveOnNewSearch() then
+            GroupLeave()
+        elseif groupStatus then
+            DAS.TryTriggerAutoAcceptInvite()
+        end
         return
     end
+
+
+    -- we're not auto inviting, nothing to do
+    if not DAS.autoInviting then return end
+

-    -- we don't have quests to share
-    if not DAS.autoInviting or #DAS.fullBingoString == 0 then return end
+    if #messageText == 1 and messageText == stringPlus then
+        table.insert(inviteQueue, fromDisplayName)
+        if not alreadyInviting then
+            popInviteQueue()
+        end
+        return
+    end

     HandleChatMessage(messageText:lower(), fromDisplayName)
-    zo_callLater(popInviteQueue,500)
 end
\ No newline at end of file
diff --git a/DailyAutoShare/DasGui.lua b/DailyAutoShare/DasGui.lua
index e2a0391..995855b 100644
--- a/DailyAutoShare/DasGui.lua
+++ b/DailyAutoShare/DasGui.lua
@@ -35,7 +35,7 @@ function DAS.RefreshControl(refreshQuestCache)
 	DasList:SetHidden(    stateIsMinimised or stateIsHidden)
 	if stateIsMinimised or stateIsHidden then return end

-	DAS.RefreshLabels(true)
+	DAS.RefreshLabels(refreshQuestCache)

 end
 local function SetAlpha(control, value)
@@ -158,6 +158,7 @@ function DAS.RefreshLabels(forceQuestRefresh, forceSkipQuestRefresh)
 	cacheVisibilityStatus(true)
 	setButtonStates()

+    DAS.activeZoneQuests = {}
 	local trackedIndex = 0
 	if QUEST_TRACKER and QUEST_TRACKER.assistedData then
 		trackedIndex = QUEST_TRACKER.assistedData.arg1
@@ -173,38 +174,38 @@ function DAS.RefreshLabels(forceQuestRefresh, forceSkipQuestRefresh)
 	end
 	local zoneId = DAS.GetZoneId()
 	local questList = DAS.QuestLists[zoneId]
-	for index, questName in pairs(DAS.GetZoneQuests()) do
+	for index, questName in pairs(DAS.GetZoneQuests()) do
 		label = DAS.labels[buttonIndex] -- despite the name these are actually buttons

 		if nil ~= label then
 			local status 	= DAS.GetQuestStatus(questName, questList, zoneId)
 			local hideLabel = hidden or (hideCompleted and status == DAS_STATUS_COMPLETE) or shouldHideLabel(questName, questList, zoneId)
 			-- d(zo_strformat("DAS: <<1>> shoud be hidden <<2>>", questName, tostring(hideLabel)))
-			if hideLabel then
-				label:SetHidden(true)
-				label:SetText("")
-			else
-				label:SetHidden(false)
-				visibleButtonIndex 			= visibleButtonIndex +1
-				-- d( tostring(status) .. " - " .. tostring(questName))
-				label["dataJournalIndex"] 	= DAS.GetLogIndex(questName)
-				label["dataBingoString"] 	= DAS.GetBingoStringFromQuestName(questName)
-				label["dataQuestName"] 		= questName
-				label["dataQuestState"] 	= status
-				if label.dataJournalIndex == trackedIndex then
-					label:SetText("* " .. questName)
-				else
-					label:SetText(questName)
-				end
-				if status == DAS_STATUS_COMPLETE then
-					label:SetState(BSTATE_DISABLED)
-				elseif status == DAS_STATUS_ACTIVE then
-					label:SetState(BSTATE_PRESSED)
-				elseif status == DAS_STATUS_OPEN then
-					label:SetState(BSTATE_NORMAL)
-				end
-			end
-			buttonIndex = buttonIndex+1
+            label:SetHidden(hideLabel)
+            visibleButtonIndex 			= visibleButtonIndex +1
+            -- d( tostring(status) .. " - " .. tostring(questName))
+            label["dataJournalIndex"] 	= DAS.GetLogIndex(questName)
+            label["dataBingoString"] 	= DAS.GetBingoStringFromQuestName(questName)
+            label["dataQuestName"] 		= questName
+            label["dataQuestState"] 	= status
+            if label.dataJournalIndex == trackedIndex then
+                label:SetText("* " .. questName)
+            elseif hideLabel then
+                label:SetText("")
+            else
+                label:SetText(questName)
+            end
+
+            if status == DAS_STATUS_COMPLETE then
+                label:SetState(BSTATE_DISABLED)
+            elseif status == DAS_STATUS_ACTIVE then
+                DAS.activeZoneQuests[index] = true
+                label:SetState(BSTATE_PRESSED)
+            elseif status == DAS_STATUS_OPEN then
+                label:SetState(BSTATE_NORMAL)
+            end
+
+			buttonIndex = buttonIndex + 1
 		end -- nil check end

 	end -- for loop end
diff --git a/DailyAutoShare/libs/LibAsync/LibAsync.lua b/DailyAutoShare/libs/LibAsync/LibAsync.lua
deleted file mode 100644
index 77100f0..0000000
--- a/DailyAutoShare/libs/LibAsync/LibAsync.lua
+++ /dev/null
@@ -1,340 +0,0 @@
-local MAJOR, MINOR = "LibAsync", 1.4
-local async, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-if not async then return end -- the same or newer version of this lib is already loaded into memory
-
-if async.Unload then
-	async:Unload()
-end
-
-local em = GetEventManager()
-local remove, min = table.remove, math.min
-
-local function RemoveCall(job, callstackIndex)
-	remove(job.callstack, callstackIndex)
-	job.lastCallIndex = min(job.lastCallIndex, #job.callstack)
-end
-
-local current, call
-local function safeCall() return call(current) end
-
-local function DoCallback(job, callstackIndex)
-	local success, shouldContinue = pcall(safeCall)
-	if success then
-		-- If the call returns true, the call wants to be called again
-		if not shouldContinue then RemoveCall(job, callstackIndex) end
-	else
-		-- shouldContinue is the value returned by error or assert
-		job.Error = shouldContinue
-		RemoveCall(job, callstackIndex)
-
-		call = job.onError
-		if call then
-			pcall(safeCall)
-		else
-			job:Suspend()
-			error(job.Error)
-		end
-	end
-end
-
-local jobs = async.jobs or { }
-async.jobs = jobs
--- async.registered = { }
-
-local function DoJob(job)
-	current = job
-	local index = #job.callstack
-	call = job.callstack[index]
-	if call then
-		DoCallback(job, index)
-	else
-		assert(index == 0, "No call on non-empty stack?!")
-		jobs[job.name] = nil
-		call = job.finally
-		if call then pcall(safeCall) end
-	end
-	current, call = nil, nil
-end
-
--- time we can spend until the next frame must be shown
-local frameTimeTarget = 13
--- we allow a function to use 25% of the frame time before it gets critical
-local spendTimeDef = frameTimeTarget * 0.75
-local spendTimeDefNoHUD = spendTimeDef * 1.54
-local spendTime = spendTimeDef
-
-local debug = false
-
-local GetFrameTimeMilliseconds, GetGameTimeMilliseconds = GetFrameTimeMilliseconds, GetGameTimeMilliseconds
-local identifier = "ASYNCTASKS_JOBS"
-
-local function GetThreshold()
-	return(HUD_SCENE:IsShowing() or HUD_UI_SCENE:IsShowing()) and spendTimeDef or spendTimeDefNoHUD
-end
-
-local job = nil
-local function Scheduler()
-	local start = GetFrameTimeMilliseconds()
-	local runTime = start
-	if (GetGameTimeMilliseconds() - start) > spendTime then
-		spendTime = 750 / GetFramerate()
-		if debug then
-			df("initial gap: %ims. skip. new threshold: %ims", GetGameTimeMilliseconds() - start, spendTime)
-		end
-		return
-	end
-	if debug then
-		df("initial gap: %ims", GetGameTimeMilliseconds() - start)
-	end
-	local name
-	while (GetGameTimeMilliseconds() - start) <= spendTime do
-		name, job = next(jobs)
-		if job then
-			runTime = GetGameTimeMilliseconds()
-			DoJob(job)
-		else
-			-- Finished
-			em:UnregisterForUpdate(identifier)
-			spendTime = GetThreshold()
-		return
-		end
-	end
-	spendTime = GetThreshold()
-	if debug and job then
-		local now = GetGameTimeMilliseconds()
-		local freezeTime = now - start
-		if freezeTime >= 16 then
-			runTime = now - runTime
-			df("%s freeze. used %ims, resulting frametime %ims.", job.name, runTime, freezeTime)
-		end
-	end
-end
-
-function async:GetDebug()
-	return debug
-end
-
-function async:SetDebug(enabled)
-	debug = enabled
-end
-
--- Class task
-
-local task = async.task or ZO_Object:Subclass()
-async.task = task
-
--- Called from async:Create()
-function task:New(name)
-	local instance = ZO_Object.New(self)
-	instance.name = name or tostring(instance)
-	instance:Initialize()
-	return instance
-end
-
-function task:Initialize()
-	self.callstack = { }
-	self.lastCallIndex = 0
-	-- async.registered[#async.registered + 1] = self
-end
-
--- Resume the execution context.
-function task:Resume()
-	jobs[self.name] = self
-	em:RegisterForUpdate(identifier, 0, Scheduler)
-	return self
-end
-
--- Suspend the execution context and allow to resume anytime later.
-function task:Suspend()
-	jobs[self.name] = nil
-	return self
-end
-
--- Interupt and fully stop the execution context. Can be called from outside to stop everything.
-function task:Cancel()
-	ZO_ClearNumericallyIndexedTable(self.callstack)
-	self.lastCallIndex = 0
-	if jobs[self.name] then
-		if not self.finally then
-			jobs[self.name] = nil
-			-- else run job with empty callstack to run finalizer
-		end
-	end
-	return self
-end
-
-do
-	-- Run the given FuncOfTask in your task context execution.
-	function task:Call(funcOfTask)
-		self.lastCallIndex = #self.callstack + 1
-		self.callstack[self.lastCallIndex] = funcOfTask
-		return self:Resume()
-	end
-
-	local insert = table.insert
-	-- Continue your task context execution with the given FuncOfTask after the previous as finished.
-	function task:Then(funcOfTask)
-		assert(self.lastCallIndex > 0 and self.lastCallIndex <= #self.callstack, "cap!")
-		insert(self.callstack, self.lastCallIndex, funcOfTask)
-		return self
-	end
-end
-
--- Start an interruptible for-loop.
-function task:For(p1, p2, p3)
-	-- If called as a normal job, false will prevent it is kept in callstack doing an endless loop
-	self.callstack[#self.callstack + 1] = function() return false, p1, p2, p3 end
-	return self
-end
-
-do
-	local function ForConditionAlreadyFalse() end
-	local function ContinueForward(index, endIndex) return index <= endIndex end
-	local function ContinueBackward(index, endIndex) return index >= endIndex end
-
-	local function asyncForWithStep(self, func, index, endIndex, step)
-		step = step or 1
-		if step == 0 then error("step is zero") end
-
-		local ShouldContinue
-		if step > 0 then
-			if index > endIndex then return ForConditionAlreadyFalse end
-			ShouldContinue = ContinueForward
-		else
-			if index < endIndex then return ForConditionAlreadyFalse end
-			ShouldContinue = ContinueBackward
-		end
-		return function()
-			if func(index) ~= async.BREAK then
-				index = index + step
-				return ShouldContinue(index, endIndex)
-			end
-		end
-	end
-
-	local function asyncForPairs(self, func, iter, list, key)
-		return function()
-			local value
-			key, value = iter(list, key)
-			return key and func(key, value) ~= async.BREAK
-		end
-	end
-
-	-- Execute the async-for with the given step-function. The parameters of the step-function are those you would use in your for body.
-	function task:Do(func)
-		local callstackIndex = #self.callstack
-		local shouldBeFalse, p1, p2, p3 = self.callstack[callstackIndex]()
-		assert(shouldBeFalse == false and p1, "Do without For")
-		remove(self.callstack, callstackIndex)
-
-		local DoLoop = type(p1) == "number" and
-		asyncForWithStep(self, func, p1, p2, p3) or
-		asyncForPairs(self, func, p1, p2, p3)
-
-		if current or #self.callstack == 0 then return self:Call(DoLoop) else return self:Then(DoLoop) end
-	end
-end
-
--- Suspend the execution of your task context for the given delay in milliseconds and then call the given FuncOfTask to continue.
-function task:Delay(delay, funcOfTask)
-	self:StopTimer()
-	if delay < 10 then return self:Call(funcOfTask) end
-	self:Suspend()
-	em:RegisterForUpdate(self.name, delay, function()
-		em:UnregisterForUpdate(self.name)
-		self:Call(funcOfTask)
-	end )
-	return self
-end
-
--- Stop the delay created by task:Delay or task:Interval.
-function task:StopTimer()
-	em:UnregisterForUpdate(self.name)
-	return self
-end
-
--- Set a FuncOfTask as a final handler. If you call Called if something went wrong in your context.
-function task:Finally(funcOfTask)
-	self.finally = funcOfTask
-	return self
-end
-
--- Set a FuncOfTask as an error handler. Called if something went wrong in your context.
-function task:OnError(funcOfTask)
-	self.onError = funcOfTask
-	return self
-end
-
-do
-	-- Thanks to: https://de.wikipedia.org/wiki/Quicksort
-
-	local function simpleCompare(a, b) return a < b end
-	local function sort(task, array, compare)
-		local function quicksort(left, right)
-			if left >= right then return end
-
-			-- partition
-			local i, j, pivot = left, right - 1, array[right]
-
-			task:Call( function()
-				while i < right and compare(array[i], pivot) do i = i + 1 end
-				while j > left and not compare(array[j], pivot) do j = j - 1 end
-				if i < j then
-					array[i], array[j] = array[j], array[i]
-					-- repeatly call this function until i >= j
-					return true
-				end
-			end )
-			task:Then( function()
-				if compare(pivot, array[i]) then array[i], array[right] = array[right], array[i] end
-				quicksort(left, i - 1)
-				quicksort(i + 1, right)
-			end )
-		end
-		quicksort(1, #array)
-	end
-
-	-- This sort function works like table.sort(). The compare function is optional.
-	function task:Sort(array, compare)
-		local sortJob = function(task) sort(task, array, compare or simpleCompare) end
-		if current or #self.callstack == 0 then return self:Call(sortJob) else return self:Then(sortJob) end
-	end
-end
-
--- Class async
-
--- Get the current context, if you are within a FuncOfTask or nil.
-function async:GetCurrent()
-	return current
-end
-
--- Create an interruptible task context.
-function async:Create(name)
-	return task:New(name)
-end
-
-do
-	local Default = task:New("*Default Task*")
-	function Default:Cancel() error("Not allowed on default task. Use your_lib_var:Create(optional_name) for an interruptible task context.") end
-	Default.Finally = Default.Cancel
-	Default.OnError = Default.Cancel
-
-	-- Start a non-interruptible task or start a nested call in the current context.
-	function async:Call(funcOfTask)
-		-- if async:Call is called from within a task callback (the moment where GetCurrent() is not nil) use it for nested calls
-		return(async:GetCurrent() or Default):Call(funcOfTask)
-	end
-	-- Start a non-interruptible for-loop or start a nested for-loop in the current context.
-	function async:For(p1, p2, p3)
-		return(self:GetCurrent() or Default):For(p1, p2, p3)
-	end
-
-	-- Start a non-interruptible sort or start a nested sort in the current context.
-	function async:Sort(array, compare)
-		return(self:GetCurrent() or Default):Sort(array, compare)
-	end
-end
-
--- async.BREAK is the new 'break' for breaking loops. As Lua would not allowed the keyword 'break' in that context.
--- To break a for-loop, return async.BREAK
-async.BREAK = true
diff --git a/DailyAutoShare/questData/GoldCoast.lua b/DailyAutoShare/questData/GoldCoast.lua
index 132855b..d3f5091 100644
--- a/DailyAutoShare/questData/GoldCoast.lua
+++ b/DailyAutoShare/questData/GoldCoast.lua
@@ -16,10 +16,10 @@ DAS.shareables[zoneId] = tbl
 DAS.shareables[825] = DAS.shareables[zoneId]

 local tbl2 = {}
-table.insert(tbl2, "mino")
-table.insert(tbl2, "arena")
-table.insert(tbl2, {[1] = "good", [2] = "common"})
-table.insert(tbl2, {[1] = "evil", [2] = "buried"})
+table.insert(tbl2, {[1] = "mino", [2] = "m"})
+table.insert(tbl2, {[1] = "arena",[2] = "a"})
+table.insert(tbl2, {[1] = "good", [2] = "common", [3] = "cg"})
+table.insert(tbl2, {[1] = "evil", [2] = "buried", [3] = "be"})

 DAS.makeBingoTable(zoneId, tbl2)
 DAS.bingo[825] = DAS.bingo[zoneId]
diff --git a/DailyAutoShare/questData/Morrowind.lua b/DailyAutoShare/questData/Morrowind.lua
index aa4fecf..5054974 100644
--- a/DailyAutoShare/questData/Morrowind.lua
+++ b/DailyAutoShare/questData/Morrowind.lua
@@ -54,15 +54,15 @@ table.insert(tbl, GetString(DAS_M_DELVE_SYNDI))
 table.insert(tbl2, "syndicate")

 table.insert(tbl, GetString(DAS_M_BOSS_WUYWU))
-table.insert(tbl2, {[1] = "wuyu", [2] = "wyu", [3] = "wuyuvus", [4] = "wuju"})
+table.insert(tbl2, {[1] = "wuyu", [2] = "wyu", [3] = "wuyuvus", [4] = "wuju", [5] = "sul", [6] = "sulipund"})
 table.insert(tbl, GetString(DAS_M_BOSS_SWARM))
-table.insert(tbl2, {[1] ="queen", [2] = "swarm"})
+table.insert(tbl2, {[1] ="queen", [2] = "swarm", [3] = "consort", [4] = "qc"})
 table.insert(tbl, GetString(DAS_M_BOSS_NILTH))
 table.insert(tbl2, {[1] ="nil", [2] = "nilthog", [3] = "oxen"})
 table.insert(tbl, GetString(DAS_M_BOSS_SALOT))
-table.insert(tbl2, {[1] ="salo", [2] = "salothan"})
+table.insert(tbl2, {[1] ="salo", [2] = "salothan", [3] = "sal"})
 table.insert(tbl, GetString(DAS_M_BOSS_SIREN))
-table.insert(tbl2, {[1] ="siren", [2] = "song", [3] = "songbird"})
+table.insert(tbl2, {[1] ="siren", [2] = "song", [3] = "songbird", [4] = "ss"})
 table.insert(tbl, GetString(DAS_M_BOSS_APPRE))
 table.insert(tbl2, {[1] = "dub", [2] = "dubdil" })

diff --git a/DailyAutoShare/questData/Wrothgar.lua b/DailyAutoShare/questData/Wrothgar.lua
index eba19d5..08b37a4 100644
--- a/DailyAutoShare/questData/Wrothgar.lua
+++ b/DailyAutoShare/questData/Wrothgar.lua
@@ -23,18 +23,18 @@ DAS.shareables[zoneId] = {
 }
 local tbl2 = {}

-table.insert(tbl2, {[1] = "poa", [2] = "poacher"})
+table.insert(tbl2, {[1] = "poa",    [2] = "poacher"})
 table.insert(tbl2, "edu")
 table.insert(tbl2, "nyz")
-table.insert(tbl2, "cori")
-table.insert(tbl2, {[1] ="dolmen", [2] = "zan"})
-table.insert(tbl2, {[1] = "ogre", [2] = "mad", [3] = "shrek"})
+table.insert(tbl2, {[1] = "cori",   [2] = "nb"})
+table.insert(tbl2, {[1] = "dolmen", [2] = "zan", [3] = "dol",   [4] = "ud"})
+table.insert(tbl2, {[1] = "ogre",   [2] = "mad", [3] = "shrek"})

 table.insert(tbl2, "harpy")
 table.insert(tbl2, "spirits")
 table.insert(tbl2, "durzog")
 table.insert(tbl2, "dwemer")
-table.insert(tbl2, {[1] = "wolf", [2] = "skintrade"})
+table.insert(tbl2, {[1] = "wolf",   [2] = "skintrade"})
 table.insert(tbl2, {[1] = "bandit", [2] = "fire"})

 DAS.makeBingoTable(zoneId, tbl2)
@@ -45,7 +45,6 @@ DAS.questStarter[zoneId] = {
 }

 DAS.questFinisher[zoneId] = {
-
     [GetString(DAS_QUEST_W_OUFA      )] = true,
     [GetString(DAS_QUEST_W_USHANG    )] = true,

diff --git a/DailyAutoShare/startup.lua b/DailyAutoShare/startup.lua
index fc82407..86155d8 100644
--- a/DailyAutoShare/startup.lua
+++ b/DailyAutoShare/startup.lua
@@ -20,6 +20,7 @@ DAS.channelTypes 	        = {
     [CHAT_CHANNEL_SAY ]     = false,
     [CHAT_CHANNEL_YELL]     = false,
     [CHAT_CHANNEL_ZONE]     = false,
+    [CHAT_CHANNEL_WHISPER]  = false,
 }

 DAS.locale 			    = GetCVar("language.2")
@@ -37,8 +38,6 @@ local fullBingoString       = DAS.fullBingoString

 local defaults = {

-
-
 	["singleDailies"]               = {},
 	["shareableDailies"]            = {},
 	["speakStupid"]                 = false,
@@ -117,9 +116,11 @@ local defaults = {
 	autoMinimize 				= false,
 	autoShare 					= true,
 	hideCompleted				= false,
+	startupMinimized			= true,
 	lastLookingFor 				= "",
 	guildInviteNumber 			= 1,
 	guildInviteText,
+    questShareString            = "I can give a DailyAutoShare for <<1>>, type <<2>> for an instant invite",
 	listenInGuilds,
     ["tracked"] = {
 		[684] = true,
@@ -206,6 +207,7 @@ function DAS.Report(text)
 	if not DAS.GetShutUp() then d(text) end
 end

+DAS.activeZoneQuests = {}

 --==============================
 --======= Event hooks  =========
@@ -221,8 +223,9 @@ local function OnQuestAdded(eventCode, journalIndex, questName, objectiveName)
 	local zoneId = DAS.GetZoneId()
 	if not DAS.GetActiveIn(zoneId) 			then return end
 	if not GetIsQuestSharable(journalIndex) then return end
-
-	if nil ~= DAS.GetArrayEntry(DAS.shareables[zoneId], questName) then
+	local shareables = DAS.shareables[zoneId] or {}
+
+	if nil ~= shareables[questName] then
 		DAS.LogQuest(questName, false)
 		zo_callLater(function() DAS.RefreshControl(true) end, 700)
 	end
@@ -270,17 +273,13 @@ function DAS.getBingoTable(zoneId)
     return DAS.bingo[zoneId] or {}
 end

-
 function DAS.SetChatListenerStatus(status)

     DAS.channelTypes[CHAT_CHANNEL_SAY ]     = status
     DAS.channelTypes[CHAT_CHANNEL_YELL]     = status
     DAS.channelTypes[CHAT_CHANNEL_ZONE]     = status
-	if status then
-		em:RegisterForEvent("DailyAutoShare", EVENT_CHAT_MESSAGE_CHANNEL, OnChatMessage)
-	else
-		em:UnregisterForEvent("DailyAutoShare", EVENT_CHAT_MESSAGE_CHANNEL, OnChatMessage)
-	end
+    DAS.channelTypes[CHAT_CHANNEL_WHISPER]  = status
+
 end


@@ -294,10 +293,6 @@ local function OnPlayerActivated(eventCode)
     DAS.cacheChatterData()
 end

--- local function OnGroupMemberAdded(eventCode, memberName)
-	-- DAS.TryShareActiveDaily()
--- end
-
 local function OnUnitCreated(eventCode, unitTag)
     local unitZone = GetZoneId(GetUnitZoneIndex(unitTag))
     if not DAS.GetActiveIn(unitZone) then return end
@@ -323,9 +318,7 @@ local function OnQuestRemoved(eventCode, isCompleted, journalIndex, questName, z
     -- set auto invite off until the questlog has refreshed
 	local autoInvite = DAS.GetAutoInvite()
     DAS.SetAutoInvite(false)
-
-    local timetoreset = (GetTimeStamp() - 1523512800)%86400
-    zo_callLater(resetQuests, timetoreset)
+

     zo_callLater(function()
         DAS.SetAutoInvite(autoInvite)
@@ -375,8 +368,9 @@ local function RegisterEventHooks()


 	em:RegisterForEvent("DailyAutoshare", EVENT_UNIT_CREATED,	 		OnUnitCreated)
-	em:RegisterForEvent("DailyAutoshare", EVENT_UNIT_DESTROYED, 			OnGroupTypeChanged)
+	em:RegisterForEvent("DailyAutoshare", EVENT_UNIT_DESTROYED, 		OnGroupTypeChanged)

+	em:RegisterForEvent("DailyAutoShare", EVENT_CHAT_MESSAGE_CHANNEL,   OnChatMessage)
 	-- DasControl:OnMoveStop
 	-- DailyAutoShare.SaveControlLocation(self)
 end
@@ -409,6 +403,10 @@ local function handleLog(forceReset)
     DAS.globalSettings.completionLog = completionLog
 end
 DAS.handleLog = handleLog
+
+local function minimiseOnStartup()
+	DasList:SetHidden(DAS.GetSettings().startupMinimized)
+end
 --==============================
 --===== Rise, my minion!  ======
 --==============================
@@ -424,10 +422,18 @@ function DailyAutoShare_Initialize(eventCode, addonName)
 	RegisterEventHooks()

 	DailyAutoShare.CreateMenu(DailyAutoShare.settings, defaults)
-	DAS.CreateGui()
+	DAS.CreateGui()
+
+    local timetoreset = (GetTimeStamp() - 1523512800)%86400
+    zo_callLater(resetQuests, timetoreset)

+    -- remove this soon
+    if DAS.GetSettings().questShareString:find("<<3>>") then
+        DAS.GetSettings().questShareString = defaults.questShareString
+    end

     zo_callLater(OnPlayerActivated, 5000)
+    zo_callLater(minimiseOnStartup, 5500)
     -- handleLog()
 	EVENT_MANAGER:UnregisterForEvent("DailyAutoShare", EVENT_ADD_ON_LOADED)