2.92

git [03-31-18 - 17:47]
2.92
Filename
DASData.lua
DASHelper.lua
DASMenu.lua
DASUserSettingsAdapter.lua
DailyAutoShare.txt
DasGui.lua
DasGuiStringBuilder.lua
DasTooltip.lua
locale/de.lua
locale/en.lua
locale/fr.lua
locale/jp.lua
locale/ru.lua
questData/ClockworkCity.lua
questData/Cyrodiil.lua
questData/GoldCoast.lua
questData/HewsBane.lua
questData/Morrowind.lua
questData/Wrothgar.lua
startup.lua
xml/VirtualButtons.xml
diff --git a/DASData.lua b/DASData.lua
index 2170089..7939413 100644
--- a/DASData.lua
+++ b/DASData.lua
@@ -1,6 +1,7 @@
 local DAS = DailyAutoShare
 local locale

+local p = DAS.debug

 DAS.subzones = {
 	-- Morrowind
@@ -33,7 +34,7 @@ function DAS.IsFestivalZone()
 end

 function DAS.GetZoneId()
-	return (GetZoneId(GetUnitZoneIndex('player')))
+	return GetZoneId(GetUnitZoneIndex('player'))
 end
 function PrintZoneId()
 	d(GetZoneId(GetUnitZoneIndex('player')))
@@ -45,36 +46,17 @@ function DAS.GetTranslationString(key, bool)
 	return DAS_STRINGS[key]
 end

-function DAS.GetZoneQuests()
-	local zoneId = DAS.GetZoneId()
+function DAS.GetZoneQuests(zoneId)
+	zoneId = zoneId or DAS.GetZoneId()
 	if nil ~= DAS.subzones[zoneId] then zoneId = DAS.subzones[zoneId] end
 	if nil ~= DAS.festivals[zoneId] then zoneId = DAS.festivals[zoneId] end
-	return DAS.shareables[zoneId] or {}
-
-
-end
-
-
-
-function DAS.GetBingoValues(bingoIndex)
-
-	if (nil == bingoIndex) then return {} end
-
-	local ret = {}
-	local bingoArray = DAS.bingo[DAS.GetZoneId()]
-
-	for key, index in pairs(bingoArray) do
-		if index == bingoIndex then
-			table.insert(ret, key)
-		end
-	end
-
-	return ret
+	return DAS.shareables[zoneId] or {}
 end

 function DAS.GetBingoIndexFromMessage(messageText)
 	local bingoArray = DAS.bingo[DAS.GetZoneId()]
 	if nil == bingoArray then return end
+
 	for bingo, questindex in pairs(bingoArray) do
 		if messageText:match(bingo) then
 			return questindex
@@ -98,27 +80,28 @@ end

 function DAS.GetBingoIndexFromQuestName(questName)
 	for questIndex, checkQuestName in pairs(DAS.GetZoneQuests()) do
-		if DAS.IsMatch(questName, checkQuestName) then
+		if questName == checkQuestName then
 			return questIndex
 		end
 	end
 	return 99
 end

+
 function DAS.GetBingoStringFromQuestName(questName)
-
-	local ret, index = "", DAS.GetBingoIndexFromQuestName(questName)
-	-- local zoneName = DAS.GetZoneId()
+
+    local index = DAS.GetBingoIndexFromQuestName(questName)
+    local ret = ""
 	local zoneId = DAS.GetZoneId()
-	local bingoArray = DAS.bingo[zoneId] or {}
-
-	if index == 99 then return "" end
+	if index == 99 then return ret end

-	for bingoString, bingoIndex in pairs(bingoArray) do
-		if tonumber(index) == tonumber(bingoIndex) then ret = bingoString end
-	end
-
-	ret = DAS.bingoClean[ret] or ret
+	local bingoArray = DAS.bingo[zoneId] or {}
+    for bingoString, bingoIndex in pairs(bingoArray) do
+        if bingoIndex == index then ret = bingoString end
+    end
+
+    ret = DAS.bingoFallback[zoneId][ret] or ret
+
 	if ret ~= "" then
 		if not(string.find(ret, "%+")) then ret = "+" .. ret end
 		if (string.find(ret, "%+%+"))  then ret.gsub("%+%+", "%+") end
@@ -161,7 +144,9 @@ function DAS.GetOpenQuestNames()
 	end
 	return ret
 end
-function DAS.GetActiveQuestIndices()
+function DAS.GetActiveQuestIndices(zoneId)
+    zoneId = zoneId or DAS.GetZoneId()
+    local zoneQuests = DAS.GetZoneQuests(zoneId)
 	local ret = {}
 	local questLabel
 	for i=1, #DAS.labels do
diff --git a/DASHelper.lua b/DASHelper.lua
index ba489c4..3e154f2 100644
--- a/DASHelper.lua
+++ b/DASHelper.lua
@@ -61,11 +61,10 @@ function DAS.TryDisableAutoShare(fromName, messageText)
 end

 local sharingCooldown = false
-function DAS.TryShareActiveDaily(unitTag)
-
+function DAS.TryShareActiveDaily(unitZone)
 	if not DAS.GetAutoShare() then return end
-	if not DAS.GetActiveIn() then return end
-	for _, questIndex in ipairs(DAS.GetActiveQuestIndices()) do
+	if not DAS.GetActiveIn(unitZone) then return end
+	for _, questIndex in ipairs(DAS.GetActiveQuestIndices(unitZone)) do
 		if IsValidQuestIndex(questIndex) then ShareQuest(questIndex) end
 	end
  end
@@ -81,29 +80,19 @@ local function EscapeString(text)
 	text = text:gsub("%%", "%%%%")
 	-- escape dashes
 	text = text:gsub("-", "")
-	return text
+	return text or ""
  end

 function DAS.IsMatch(param1, param2)
-
-	local ret = false
+
 	string1 = EscapeString(tostring(param1):lower())
 	string2 = EscapeString(tostring(param2):lower())

-	if	((string1 ~= "") and (string2 ~= "")) then
-
-		if string.match(string1, string2) then
-			ret =  ret or true
-			if ret then return ret end
-		end
-		if string.match(string2, string1) then
-			ret =  ret or true
-			if ret then return ret end
-		end
-
-	end
-	return  ret or (string1 == string2)
-
+	if #string1 == 0 or #string2 == 0 then return false end
+
+	return string.match(string1, string2) or string.match(string2, string2) or string1 == string2
+
+
  end

 function DAS.FindInList(array, item)
@@ -124,8 +113,7 @@ function DAS.TryTriggerAutoAcceptInvite()
 end


-function DAS.HandleGroupMessage(fromDisplayName, messageText)
-
+function DAS.HandleGroupMessage(fromDisplayName, messageText)
 	if DAS.IsMatch(messageText, "stop") then
 		DAS.TryDisableAutoShare(fromDisplayName, messageText)
 	end
@@ -156,8 +144,8 @@ end

 function DAS.OpenDailyPresent()

-	local numCompleted = NonContiguousCount(DAS.GetShareableLog())
-	local numOpen = NonContiguousCount(DAS.GetZoneQuests())
+	local numCompleted 	= NonContiguousCount(DAS.GetShareableLog())
+	local numOpen 		= NonContiguousCount(DAS.GetZoneQuests())

 	if  (numCompleted < numOpen) then return true end

@@ -168,3 +156,8 @@ function DAS.OpenDailyPresent()
 	return false
 end

+
+function DAS.HasActiveDaily()
+	return #DAS.GetActiveQuestNames() > 0
+end
+
diff --git a/DASMenu.lua b/DASMenu.lua
index 22da5a4..0ff33d5 100644
--- a/DASMenu.lua
+++ b/DASMenu.lua
@@ -15,7 +15,13 @@ function DAS.CreateMenu(savedVars, defaults)
 	LAM:RegisterAddonPanel("DailyAutoShare_OptionsPanel", panelData)

 	local optionsData = { -- optionsData
-
+		{ -- Use global configuration?
+			type = "checkbox",
+			name = "Turn on debugging?",
+			getFunc = function() return DAS.GetDebugMode() end,
+			setFunc = function(value) DAS.SetDebugMode(value) end
+		},
+
 		{ -- header: Use global variables?
 			type = "header",
 			name = "Use global variables?"
@@ -344,6 +350,13 @@ function DAS.CreateMenu(savedVars, defaults)
 			getFunc = function() return DAS.GetAutoInvite() end,
 			setFunc = function(value) DAS.SetAutoInvite(value) end
 		},
+		{ --auto-invite
+			type = "checkbox",
+			tooltip = "Stop inviting when you leave a group?",
+			name = "stop inviting when the group disbands",
+			getFunc = function() return DAS.GetStopInviteOnDegroup() end,
+			setFunc = function(value) DAS.SetStopInviteOnDegroup(value) end
+		},

 		{ --auto-leave
 			type = "checkbox",
diff --git a/DASUserSettingsAdapter.lua b/DASUserSettingsAdapter.lua
index 097f2e6..e030677 100644
--- a/DASUserSettingsAdapter.lua
+++ b/DASUserSettingsAdapter.lua
@@ -9,6 +9,8 @@ function DAS.SetUseGlobalSettings(value)
 	DAS.settings.useGlobalSettings = value
 end

+
+
 -- called internally a lot
 local function GetSettings()
 	if DAS.GetUseGlobalSettings() then
@@ -31,20 +33,19 @@ end


 function DAS.GetSpeakStupid()
-	return DAS.settings.speakStupid
+	return GetSettings().speakStupid
 end
 function DAS.SetSpeakStupid(value)
-	DAS.settings.speakStupid = value
+	GetSettings().speakStupid = value
 end

-function DAS.GetCurrentlyWithQuest()
-	return DAS.settings.currentlyWithQuest
+function DAS.GetDebugMode()
+	return GetSettings().debugging
 end
-function DAS.SetCurrentlyWithQuest(value)
-	DAS.settings.currentlyWithQuest = value
+function DAS.SetDebugMode(value)
+	GetSettings().debugging = value
 end

-
 -- called from settings: GUI
 function DAS.GetShutUp()
 	return GetSettings().shutUp
@@ -80,13 +81,6 @@ function DAS.SetHidden(hidden)
 	if not hidden then DAS.RefreshControl() end
 end

-function DAS.GetMuteGreetings()
-	return GetSettings().muteGreetings
-end
-function DAS.SetMuteGreetings(value)
-	GetSettings().muteGreetings = value
-end
-
 function DAS.GetTooltipRight()
 	return GetSettings().tooltipRight
 end
@@ -133,8 +127,14 @@ function DAS.SetAutoAcceptShared(value)
 	DAS.SetButtonStates()
 end

+function DAS.GetStopInviteOnDegroup()
+	return GetSettings().keepInviteUpOnDegroup
+end
+function DAS.SetStopInviteOnDegroup(value)
+	GetSettings().keepInviteUpOnDegroup = value
+end
 function DAS.GetAutoAcceptInviteInterval()
-	return GetSettings().autoAcceptInviteInterval
+	return GetSettings().autoAcceptInviteInterval or 0
 end
 function DAS.SetAutoAcceptInviteInterval(value)
 	GetSettings().autoAcceptInviteInterval = value
@@ -192,52 +192,52 @@ function DAS.SetAutoShare(value)
 end

 function DAS.GetAutoLeave()
-	return DAS.settings.autoLeave
+	return GetSettings().autoLeave
 end
 function DAS.SetAutoLeave(value)
-	DAS.settings.autoLeave = value
+	GetSettings().autoLeave = value
 end

 function DAS.GetUpsideDown()
-	return DAS.settings.upsideDown
+	return GetSettings().upsideDown
 end
 function DAS.SetUpsideDown(value)
-	DAS.settings.upsideDown = value
+	GetSettings().upsideDown = value
 	DAS.AnchorList()
 end

 function DAS.GetAutoHide()
-	return DAS.settings.autoHide
+	return GetSettings().autoHide
 end
 function DAS.SetAutoHide(value)
-	DAS.settings.autoHide = value
+	GetSettings().autoHide = value
 	DAS.RefreshGui()
 end

 function DAS.GetAutoMinimize()
-	return DAS.settings.autoMinimize
+	return GetSettings().autoMinimize
 end
 function DAS.SetAutoMinimize(value)
-	DAS.settings.autoMinimize = value
+	GetSettings().autoMinimize = value
 	DAS.RefreshGui()
 end

 function DAS.GetHiddenInInactiveZones()
-	return DAS.settings.inactiveZones.hide
+	return GetSettings().inactiveZones.hide
 end

 function DAS.SetHiddenInInactiveZones(value)
-	DAS.settings.inactiveZones.hide = value
+	GetSettings().inactiveZones.hide = value
 	DasControl:SetHidden(value and DAS.GetActiveIn())
 end


 function DAS.GetFontSize()
-	return DAS.settings.fontScale or 1.0
+	return GetSettings().fontScale or 1.0
 end

 function DAS.SetFontSize(value)
-	DAS.settings.fontScale = value
+	GetSettings().fontScale = value
 	DAS.RefreshControl()
 end
 -- called from gui
@@ -259,24 +259,24 @@ function DAS.SetY(controlname, value)
 end

 function DAS.GetGuildInviteNumber()
-	return (tonumber(DAS.settings.guildInviteNumber) or 0)
+	return (tonumber(GetSettings().guildInviteNumber) or 0)
 end
 function DAS.SetGuildInviteNumber(value)
-	DAS.settings.guildInviteNumber = value
+	GetSettings().guildInviteNumber = value
 end

 function DAS.GetListenInGuilds()
-	return DAS.settings.listenInGuilds
+	return GetSettings().listenInGuilds
 end
 function DAS.SetListenInGuilds(value)
-	DAS.settings.listenInGuilds = value
+	GetSettings().listenInGuilds = value
 end

 function DAS.GetGuildInviteText()
-	return DAS.settings.guildInviteText
+	return GetSettings().guildInviteText
 end
 function DAS.SetGuildInviteText(value)
-	DAS.settings.guildInviteText = value
+	GetSettings().guildInviteText = value
 end

 function DAS.SaveControlLocation(control)
@@ -295,17 +295,17 @@ function DAS.LoadControlLocation(control)
 end

 function DAS.GetHideCompleted()
-	return DAS.settings.hideCompleted
+	return GetSettings().hideCompleted
 end
 function DAS.SetHideCompleted(value)
-	DAS.settings.hideCompleted = value
+	GetSettings().hideCompleted = value
 end

 function DAS.GetUserMinimised()
-	return DAS.settings.userMinimised
+	return GetSettings().userMinimised
 end
 function DAS.SetUserMinimised(value)
-	DAS.settings.userMinimised = value
+	GetSettings().userMinimised = value
 end


@@ -322,12 +322,12 @@ local function assertSettingArray(settings, dateNumber, characterName)
 end

 function DAS.GetSetting(settingsArray, arrayKey)
-	if not DAS.settings[settingsArray] then return false end
-	return DAS.settings[settingsArray][arrayKey]
+	if not GetSettings()[settingsArray] then return false end
+	return GetSettings()[settingsArray][arrayKey]
 end
 function DAS.SetSetting(settingsArray, arrayKey, arrayValue)
-	DAS.settings[settingsArray] = DAS.settings[settingsArray] or {}
-	DAS.settings[settingsArray][arrayKey] = arrayValue
+	GetSettings()[settingsArray] = DAS.settings[settingsArray] or {}
+	GetSettings()[settingsArray][arrayKey] = arrayValue
 end


diff --git a/DailyAutoShare.txt b/DailyAutoShare.txt
index 6d41682..c9ce33f 100644
--- a/DailyAutoShare.txt
+++ b/DailyAutoShare.txt
@@ -1,7 +1,7 @@
 ## Title: DailyAutoShare
 ## Author: manavortex
-## Version: 2.85
-## APIVersion: 100020 100021
+## Version: 2.92
+## APIVersion: 100022
 ## SavedVariables: DAS_Settings DAS_Globals
 ## OptionalDependsOn: LibStub LibAddonMenu-2.0 LibMediaProvider-1.0

@@ -34,6 +34,7 @@ locale/$(language).lua

 startup.lua

+questData/BingoClean.lua
 questData/Festival.lua
 questData/Wrothgar.lua
 questData/Morrowind.lua
diff --git a/DasGui.lua b/DasGui.lua
index e88111d..e37ab8f 100644
--- a/DasGui.lua
+++ b/DasGui.lua
@@ -23,6 +23,10 @@ end
 DAS.cacheVisibilityStatus = cacheVisibilityStatus

 function DAS.RefreshControl()
+	if not DAS.HasActiveDaily() then
+		DAS.SetAutoInvite(false)
+	end
+
 	cacheVisibilityStatus(true)
 	DasHandle:SetHidden(  stateIsHidden)
 	DasControl:SetHidden( stateIsHidden)
diff --git a/DasGuiStringBuilder.lua b/DasGuiStringBuilder.lua
index 3c96f6a..ba9a6db 100644
--- a/DasGuiStringBuilder.lua
+++ b/DasGuiStringBuilder.lua
@@ -3,6 +3,8 @@ local DAS = DailyAutoShare
 DAS.QuestIndexTable = {}
 DAS.QuestNameTable = {}

+local p = DAS.DebugOut
+
 function DAS.GetLogIndex(questName)
 	return DAS.QuestNameTable[questName] or 0
 end
@@ -40,9 +42,11 @@ local function getEnglishQuestNames(activeQuestNames)
 	if DAS.locale == "en" then return activeQuestNames end
 	local ret = {}
 	for index, questName in pairs(activeQuestNames) do
-		for key, value in pairs(DAS_STRINGS_LOCALE[DAS.locale]) do
-			if DAS.IsMatch(questName, value) then
-				table.insert(ret, DAS_STRINGS_LOCALE.en[key])
+		if nil ~= DAS_STRINGS_LOCALE and nil ~= DAS.locale and nil ~= DAS_STRINGS_LOCALE.en then
+			for key, value in pairs(DAS_STRINGS_LOCALE[DAS.locale]) do
+				if DAS.IsMatch(questName, value) then
+					table.insert(ret, DAS_STRINGS_LOCALE.en[key])
+				end
 			end
 		end
 	end
@@ -50,39 +54,40 @@ local function getEnglishQuestNames(activeQuestNames)
 	return ret
 end

-local function GenerateBingoString(activeQuestNames, verbose)
-
-	activeQuestNames = getEnglishQuestNames(activeQuestNames)
-	local function generateQuestSpam(questNames)
-		local ret = ""
-		for _, questName in ipairs(questNames) do
-			bingoString = (verbose and questName) or DAS.GetBingoStringFromQuestName(questName)
-			ret = ret .. ((("" == bingoString) and "") or bingoString .. " ")
-		end
-		return ret
-	end
-	local function askForQuest(questNames)
-		local ret = ""
-		for _, questName in ipairs(questNames) do
+local function askForQuest(questNames)
+    local ret = ""
+    for _, questName in ipairs(questNames) do
       if "" ~= questName then
         ret = ret .. questName .. ", "
       end
 		end
     if ret == "" then return ret end
-		return ret:sub(1, -3)
-	end
+    return ret:sub(1, -3)
+end
+local function generateQuestSpam(questNames)
+    local ret = ""
+    for _, questName in ipairs(questNames) do
+        bingoString = DAS.GetBingoStringFromQuestName(questName)
+        ret = ret .. ((("" == bingoString) and "") or bingoString .. " ")
+    end
+    return ret
+end

+local function GenerateBingoString(activeQuestNames, verbose)
+
+	activeQuestNames = getEnglishQuestNames(activeQuestNames)
+
 	local bingoCodes = {}
 	local ret = ""
 	if DAS.GetAutoInvite() then
-		ret = "I can give a DailyAutoShare for "
+		ret = "I can give a DailyAutoShare for "

 		for _, questName in ipairs(activeQuestNames) do
 			if DAS.IsQuestActive(questName) then
 				ret = ret .. questName .. ", "
 				table.insert(bingoCodes, DAS.GetBingoStringFromQuestName(questName))
 			end
-		end
+		end
 		if #bingoCodes > 0 then
 			local eitherOf = (#bingoCodes > 1 and "either of ") or ""
 			ret = ret .. "type " .. tostring(eitherOf)
@@ -93,29 +98,28 @@ local function GenerateBingoString(activeQuestNames, verbose)
 		else
 			ret = ret .. "can share"
 		end
-	else
-		if NonContiguousCount(DAS.GetShareableLog()) == 0 and #activeQuestNames == 0 then
-			if not verbose then return "+any" end
-			return GetString(DAS_SI_VERBOSE_ANY)
-		end
-		if verbose then ret =  "Looking for the following quest shares: " else ret = "" end
-		local questList = (#activeQuestNames > 0 and activeQuestNames) or DAS.GetOpenQuestNames()
-
-		return ret .. (( verbose and askForQuest(questList)) or generateQuestSpam(questList))
-
-	end
+
+        return ret
+    end

-	return ret
+    if NonContiguousCount(DAS.GetShareableLog()) == 0 and #activeQuestNames == 0 then
+        if not verbose then return "+any" end
+        return GetString(DAS_SI_VERBOSE_ANY)
+    end
+    if verbose then ret =  "Looking for " else ret = "" end
+    local questList = (#activeQuestNames > 0 and activeQuestNames) or DAS.GetOpenQuestNames()
+
+    return ret .. ((verbose and askForQuest(questList)) or "") .. generateQuestSpam(questList)

 end
+DAS.GenerateBingoString = GenerateBingoString

 local function SpamChat(verbose, questName)
-
 	if CHAT_SYSTEM.textEntry.editControl:HasFocus() then
 		CHAT_SYSTEM.textEntry.editControl:Clear()
 	end
 	local activeQuestNames = {}
-	if nil == questName then
+	if nil == questName then
 		activeQuestNames = DAS.GetActiveQuestNames()
 	else
 		table.insert(activeQuestNames, questName)
@@ -123,17 +127,15 @@ local function SpamChat(verbose, questName)
 	if #activeQuestNames == 0 then
 		DAS.SetAutoInvite(false)
 	end
-	StartChatInput(GenerateBingoString(activeQuestNames, verbose), CHAT_CHANNEL_ZONE)
+	StartChatInput(DAS.GenerateBingoString(activeQuestNames, verbose), CHAT_CHANNEL_ZONE)

 end
-function DAS.SpamChat(verbose, questName)
-	return SpamChat(verbose, questName)
-end
+DAS.SpamChat = SpamChat

 function DAS.SpamForSingle(questName, bingoString)
 	if nil == questName and nil == bingoString then return end
-	-- bingoString = bingoString 	or GetBingoStringFromQuestName(questName)
-	-- questName	= questName		or DAS.GetQuestNameFromBingoString(bingoString)
+	questName	= questName		or DAS.GetQuestNameFromBingoString(bingoString)
+	bingoString = bingoString 	or GetBingoStringFromQuestName(questName)

 	local lftext = bingoString

@@ -148,7 +150,7 @@ function DAS.SettingsButton(control, mouseButton)
 	elseif 	name == "Invite" 	then DAS.SetAutoInvite(not DAS.GetAutoInvite())
 	elseif 	name == "Share" 	then
 		if mouseButton == 2 then
-			DAS.TryShareActiveDaily('player')
+			DAS.TryShareActiveDaily()
 		else
 			DAS.SetAutoShare(not DAS.GetAutoShare())
 		end
diff --git a/DasTooltip.lua b/DasTooltip.lua
index 565778b..5209cff 100644
--- a/DasTooltip.lua
+++ b/DasTooltip.lua
@@ -4,12 +4,12 @@ local function GenerateTooltipText(control)

 	local key = control:GetName()

-	if 	DAS.IsMatch(key, "Invite")		then return GetString((DAS.GetAutoInvite() and DAS_SI_INVITE_TRUE) or DAS_SI_INVITE_FALSE)
-	elseif DAS.IsMatch(key, "Accept")	then return GetString((DAS.GetAutoAcceptShared() and DAS_SI_ACCEPT_TRUE) or DAS_SI_ACCEPT_FALSE)
-	elseif DAS.IsMatch(key, "Share") 	then return GetString((DAS.GetAutoShare() and DAS_SI_SHARE_TRUE) or DAS_SI_SHARE_FALSE)
-	elseif DAS.IsMatch(key, "Spam") 	then return GetString(DAS_SI_SPAM)
-	elseif DAS.IsMatch(key, "Donate") 	then return GetString(DAS_SI_DONATE)
-	elseif DAS.IsMatch(key, "Refresh") 	then return GetString(DAS_SI_REFRESH)
+	if 	    string.match(key, "Invite")	then return GetString((DAS.GetAutoInvite() and DAS_SI_INVITE_TRUE) or DAS_SI_INVITE_FALSE)
+	elseif string.match(key, "Accept")	then return GetString((DAS.GetAutoAcceptShared() and DAS_SI_ACCEPT_TRUE) or DAS_SI_ACCEPT_FALSE)
+	elseif string.match(key, "Share") 	then return GetString((DAS.GetAutoShare() and DAS_SI_SHARE_TRUE) or DAS_SI_SHARE_FALSE)
+	elseif string.match(key, "Spam") 	then return GetString(DAS_SI_SPAM)
+	elseif string.match(key, "Donate") 	then return GetString(DAS_SI_DONATE)
+	elseif string.match(key, "Refresh") then return GetString(DAS_SI_REFRESH)
 	end

 end
diff --git a/locale/de.lua b/locale/de.lua
index 672844d..3a9c24c 100644
--- a/locale/de.lua
+++ b/locale/de.lua
@@ -1,3 +1,5 @@
+DAS_STRINGS_LOCALE = DAS_STRINGS_LOCALE or {}
+
 local strings  = {


@@ -152,7 +154,7 @@ local strings  = {


 }
-
+DAS_STRINGS_LOCALE.de = strings

 for stringId, stringValue in pairs(strings) do
 	ZO_CreateStringId(stringId, stringValue)
diff --git a/locale/en.lua b/locale/en.lua
index 8f786a3..bf57e0d 100644
--- a/locale/en.lua
+++ b/locale/en.lua
@@ -1,5 +1,5 @@
 DailyAutoShare = DailyAutoShare or {}
-
+DAS_STRINGS_LOCALE = DAS_STRINGS_LOCALE or {}
 local strings  = {
 	-- UI stuffs
 	DAS_SI_INVITE_TRUE 		= "Invite is now on",
@@ -102,15 +102,12 @@ local strings  = {
 	DAS_W_WEREWOLVES	 = "The Skin Trade",
 	DAS_W_THAT_OTHER	 = "Fire in the Hold",

-
-
+
 	-- gold coast
 	DAS_DB_MINO			 = "Looming Shadows",
-	DAS_DB_ARENA 		 = "The Roar of the Crowd",
-	DAS_DB_GOOD 		 = "Common Good",
-	DAS_DB_EVIL 		 = "Buried Evil",
-
-
+	DAS_DB_ARENA 		 = "The Roar of the Crowds",
+	DAS_DB_GOOD 		 = "The Common Good",
+	DAS_DB_EVIL 		 = "Buried Evil",

 	-- new life
 	DAS_NL_STORMHAVEN	 = "Castle Charm Challenge",
@@ -145,6 +142,7 @@ local strings  = {

 }
 DailyAutoShare.EnglishQuestNames = strings
+DAS_STRINGS_LOCALE.en = strings

 for stringId, stringValue in pairs(strings) do
 	ZO_CreateStringId(stringId, stringValue)
diff --git a/locale/fr.lua b/locale/fr.lua
index 8b367ed..385f046 100644
--- a/locale/fr.lua
+++ b/locale/fr.lua
@@ -1,4 +1,6 @@
-local strings  = {
+DAS_STRINGS_LOCALE = DAS_STRINGS_LOCALE or {}
+
+local strings  = {

 	DAS_SI_INVITE_TRUE 		= "Invite auto activé",
 	DAS_SI_INVITE_FALSE 	= "Invite auto désactivé",
@@ -140,6 +142,7 @@


 }
+DAS_STRINGS_LOCALE.fr = strings

 for stringId, stringValue in pairs(strings) do
 	ZO_CreateStringId(stringId, stringValue)
diff --git a/locale/jp.lua b/locale/jp.lua
index ffa31ba..57d5e40 100644
--- a/locale/jp.lua
+++ b/locale/jp.lua
@@ -1,3 +1,5 @@
+DAS_STRINGS_LOCALE = DAS_STRINGS_LOCALE or {}
+
 local strings  = {
 	-- UI stuffs
 	DAS_SI_INVITE_TRUE 		= "Invite is now on",
@@ -142,6 +144,7 @@ local strings  = {
 	DAS_CRAG_DUNGEON 	 = "Uncaged",

 }
+DAS_STRINGS_LOCALE.jp = strings

 for stringId, stringValue in pairs(strings) do
 	ZO_CreateStringId(stringId, stringValue)
diff --git a/locale/ru.lua b/locale/ru.lua
index 60ec824..18599e4 100644
--- a/locale/ru.lua
+++ b/locale/ru.lua
@@ -1,3 +1,5 @@
+DAS_STRINGS_LOCALE = DAS_STRINGS_LOCALE or {}
+
 local strings  = {

 	DAS_SI_INVITE_TRUE 		= "Invite is now on",
@@ -23,105 +25,127 @@ local strings  = {
 	DAS_SI_SETOPEN_TRUE	 	= "Toggle open",
 	DAS_SI_SETOPEN_FALSE	= "Toggle complete",

+	-- Clockwork City
+	DAS_CLOCK_IMP			 = "Пробуждение Несовершенства",
+	DAS_CLOCK_FOE			 = "Враг в прекрасном оперении",
+
+	DAS_CLOCK_CRAFT_CLOTH	 = "Свободные нити",
+	DAS_CLOCK_CRAFT_WATER	 = "Липкое решение",
+	DAS_CLOCK_CRAFT_RUNE	 = "Сбор зачарований",
+	DAS_CLOCK_CRAFT_SMITH	 = "Ежедневная молотилка",
+	DAS_CLOCK_CRAFT_ALCH	 = "Горькая пилюля",
+	DAS_CLOCK_CRAFT_WOOD	 = "Масло в огонь",
+
+	DAS_CLOCK_DELVE_FILT	 = "Замена фильтров",
+	DAS_CLOCK_DELVE_FANS	 = "Смазывание вентиляторов",
+	DAS_CLOCK_DELVE_COMM	 = "Замена преобразователей",
+	DAS_CLOCK_DELVE_MALF	 = "Сумрачная неисправность",
+	DAS_CLOCK_DELVE_MISP	 = "Пропавшая тень",
+	DAS_CLOCK_DELVE_AGAI	 = "Обратно в тень",
+
+	DAS_CLOCK_CROW_GLIT		 = "Шик и блеск",
+	DAS_CLOCK_CROW_TRIB		 = "Вопрос о подношениях",
+	DAS_CLOCK_CROW_NIBB		 = "Кусочки и частички",
+	DAS_CLOCK_CROW_MORS		 = "Лакомые кусочки",
+	DAS_CLOCK_CROW_RESP		 = "Вопрос уважения",
+	DAS_CLOCK_CROW_LEIS		 = "Вопрос о свободном времени",


-	DAS_M_REL_ASHAL =  "Relics of Ashalmawia",
-	DAS_M_REL_ASSAR =  "Relics of Assarnatamat",
-	DAS_M_REL_ASHUR =  "Relics of Ashurnabitashpi",
-	DAS_M_REL_DUSHA =  "Relics of Dushariran",
-	DAS_M_REL_EBERN =  "Relics of Ebernanit",
-	DAS_M_REL_MAELK =  "Relics of Maelkashishi",
-	DAS_M_REL_YASAM =  "Relics of Yasammidan",
+	-- Morrowind dailies
+	DAS_M_REL_ASHAL =  "Реликвии Ашалмавии",
+	DAS_M_REL_ASSAR =  "Реликвии Ассарнатамата",
+	DAS_M_REL_ASHUR =  "Реликвии Ассурнабиташпи",
+	DAS_M_REL_DUSHA =  "Реликвии Душарирана",
+	DAS_M_REL_EBERN =  "Реликвии Эбернанита",
+	DAS_M_REL_MAELK =  "Реликвии Мелкашиши",
+	DAS_M_REL_YASAM =  "Реликвии Ясаммидана",

-	DAS_M_HUNT_EATER =  "Ash-Eater Hunt",
-	DAS_M_HUNT_ZEXXI =  "Great Zexxin Hunt",
-	DAS_M_HUNT_RAZOR =  "King Razor-Tusk Hunt",
-	DAS_M_HUNT_JAGGE =  "Mother Jagged-Claw Hunt",
-	DAS_M_HUNT_STOMP =  "Old Stomper Hunt",
-	DAS_M_HUNT_TARRA =  "Tarra-Suj Hunt",
-	DAS_M_HUNT_SVEET =  "Writhing Sveeth Hunt",
+	DAS_M_HUNT_EATER =  "Охота на Пеплоеда",
+	DAS_M_HUNT_ZEXXI =  "Охота на Великого Зексина",
+	DAS_M_HUNT_RAZOR =  "Охота на Короля Острый Клык",
+	DAS_M_HUNT_JAGGE =  "Охота на Мать Зазубренная Клешня",
+	DAS_M_HUNT_STOMP =  "Охота на Старого Топотуна",
+	DAS_M_HUNT_TARRA =  "Охота на Тарра-Судж",
+	DAS_M_HUNT_SVEET =  "Охота на Извивающегося Свита",

 	-- Cave dailies (Hall of Justice)
-	DAS_M_DELVE_DAEDR =  "Daedric Disruptions",
-	DAS_M_DELVE_KWAMA =  "Kwama Conundrum",
-	DAS_M_DELVE_MISIN =  "Planting Misinformation",
-	DAS_M_DELVE_TAXES =  "Tax Deduction",
-	DAS_M_DELVE_TRIBA =  "Tribal Troubles",
-	DAS_M_DELVE_SYNDI =  "Unsettled Syndicate",
-
-	-- World boss dailies (Hall of Justice)
-	DAS_M_BOSS_WUYWU =  "A Creeping Hunger",
-	DAS_M_BOSS_SWARM =  "Culling the Swarm",
-	DAS_M_BOSS_NILTH =  "Oxen Free",
-	DAS_M_BOSS_SALOT =  "Salothan's Curse",
-	DAS_M_BOSS_SIREN =  "Siren's Song",
-	DAS_M_BOSS_APPRE =  "The Anxious Apprentice",
+	DAS_M_DELVE_DAEDR =  "Даэдрический срыв",
+	DAS_M_DELVE_KWAMA =  "Задачка с квама",
+	DAS_M_DELVE_MISIN =  "Насаждение дезинформации",
+	DAS_M_DELVE_TAXES =  "Налоговый вычет",
+	DAS_M_DELVE_TRIBA =  "Проблемы племени",
+	DAS_M_DELVE_SYNDI =  "Беспокойный синдикат",

+	-- World boss dailies (Hall of Justice)
+	DAS_M_BOSS_WUYWU =  "Затаившийся алчущий",
+	DAS_M_BOSS_SWARM =  "Отбраковка колонии",
+	DAS_M_BOSS_NILTH =  "Волам здесь не место",
+	DAS_M_BOSS_SALOT =  "Проклятье Салотанов",
+	DAS_M_BOSS_SIREN =  "Песня сирены",
+	DAS_M_BOSS_APPRE =  "Обеспокоенная ученица",


 	-- wrothgar dailies
-    DAS_W_POACHERS       = "Мясо в массы",
-    DAS_W_EDU            = "Запах нечестной игры",
-    DAS_W_NYZ            = "Снег и пар",
-    DAS_W_CORI           = "Щедрость природы",
-    DAS_W_DOLMEN         = "Ересь невежества",
-    DAS_W_OGRE           = "Спасение во имя знаний",
-                             = "Спасение во имя знаний",
-
+    DAS_W_POACHERS        = "Мясо в массы",
+    DAS_W_EDU             = "Запах нечестной игры",
+    DAS_W_NYZ             = "Снег и пар",
+    DAS_W_CORI            = "Щедрость природы",
+    DAS_W_DOLMEN          = "Ересь невежества",
+    DAS_W_OGRE            = "Спасение во имя знаний",
+
+
 	-- wrothgar single
-	DAS_W_HARPIES 		 = "Завтрак чудака",
+	DAS_W_HARPIES 		 = "Завтрак чудака",
 	DAS_W_SPIRITS 		 = "Свободные духи",
 	DAS_W_DURZOGS 		 = "До отвала",
 	DAS_W_DWEMER 		 = "Части целого",
 	DAS_W_WEREWOLVES	 = "Торговля кожей",
-	DAS_W_THAT_OTHER	 = "Пожар во владении",
+	DAS_W_THAT_OTHER	 = "Пожар во владении",
+
+

 	-- gold coast
-	DAS_DB_MINO			 = "Сгущающиеся тени",
-	DAS_DB_ARENA 		 = "Рев толпы"
+	DAS_DB_MINO			 = "Надвигающиеся тени",
+	DAS_DB_ARENA 		 = "Рев толпы",
+	DAS_DB_GOOD 		 = "Всеобщее благо",
+	DAS_DB_EVIL 		 = "Захороненное зло",
+
+

 	-- new life
-	DAS_NL_STORMHAVEN	 = "Castle Charm Challenge",
-	DAS_NL_STONEFALLS	 = "Lava Foot Stomp",
-	DAS_NL_ALIKR		 = "Signal Fire Sprint",
-	DAS_NL_SHADOWFEN	 = "Fish Boon Feast",
-	DAS_NL_GRAHTWOOD	 = "War Orphan's Sojourn",
-	DAS_NL_REAPERSMARCH	 = "The Trial of Five-Clawed Guile",
-	DAS_NL_BETNIKH		 = "Stonetooth Bash",
-	DAS_NL_AURIDON		 = "Mud Ball Merriment",
-	DAS_NL_EASTMARCH	 = "Snow Bear Plunge",
+	DAS_NL_STORMHAVEN	 = "Замковое состязание очарования",
+	DAS_NL_STONEFALLS	 = "Пляска лавовых ног",
+	DAS_NL_ALIKR		 = "Забег сигнальных огней",
+	DAS_NL_SHADOWFEN	 = "Пир рыбьего блага",
+	DAS_NL_GRAHTWOOD	 = "Пребывание сирот войны",
+	DAS_NL_REAPERSMARCH	 = "Испытание пятипалого коварства",
+	DAS_NL_BETNIKH		 = "Удар Каменного зуба",
+	DAS_NL_AURIDON		 = "Развлечение с комками грязи",
+	DAS_NL_EASTMARCH	 = "Ныряние снежного медведя",
+

 	-- craglorn
 	-- lower
-	DAS_CRAG_SARA 		 = "Critical Mass",
-	DAS_CRAG_SHADA 		 = "The Fallen City of Shada",
-	DAS_CRAG_NEDE 		 = "The Reason We Fight",
-	DAS_CRAG_HERMY 		 = "The Seeker’s Archive",
-	DAS_CRAG_ELINHIR 	 = "Supreme Power",
-	DAS_CRAG_TUWHACCA 	 = "The Trials of Rahni’Za",
-	DAS_CRAG_NEREID 	 = "Waters Run Foul",
+	DAS_CRAG_SARA 		 = "Критическая масса",
+	DAS_CRAG_SHADA 		 = "Павший город Шады",
+	DAS_CRAG_NEDE 		 = "Причина, по которой мы сражаемся",
+	DAS_CRAG_HERMY 		 = "Архив искателя",
+	DAS_CRAG_ELINHIR 	 = "Высшая сила",
+	DAS_CRAG_TUWHACCA 	 = "Испытания Рахни'За",
+	DAS_CRAG_NEREID 	 = "Испорченная вода",

 	-- upper
-	DAS_CRAG_NIRNCRUX	 = "The Blood of Nirn",
-	DAS_CRAG_WORLDTRIP 	 = "The Gray Passage",
-	DAS_CRAG_SCALES 	 = "Iron and Scales",
-	DAS_CRAG_NECRO 		 = "Souls of the Betrayed",
-	DAS_CRAG_KIDNAP 	 = "Taken Alive",
-	DAS_CRAG_HITMAN 	 = "The Truer Fangs",
-	DAS_CRAG_DUNGEON 	 = "Uncaged",
-
-	-- Clockwork City
-	DAS_CLOCK_IMP		 = "Inciting the Imperfect",
-	DAS_CLOCK_FOE		 = "A Fine-Feathered Foe",
-
-	DAS_CLOCK_STRANDS	 = "Loose Strands",
-	DAS_CLOCK_STICKY	 = "A Sticky Solution",
-	DAS_CLOCK_ACCU		 = "Enchanted Accumulation",
-	DAS_CLOCK_GRIND		 = "A Daily Grind",
-
-
+	DAS_CRAG_NIRNCRUX	 = "Кровь Нирна",
+	DAS_CRAG_WORLDTRIP 	 = "Серый проход",
+	DAS_CRAG_SCALES 	 = "Железо и чешуя",
+	DAS_CRAG_NECRO 		 = "Души преданных",
+	DAS_CRAG_KIDNAP 	 = "Живые пленники",
+	DAS_CRAG_HITMAN 	 = "Настоящие клыки",
+	DAS_CRAG_DUNGEON 	 = "Освобожденные",
 }

+DAS_STRINGS_LOCALE.ru = strings
+
 for stringId, stringValue in pairs(strings) do
 	ZO_CreateStringId(stringId, stringValue)
 	SafeAddVersion(stringId, 2)
diff --git a/questData/ClockworkCity.lua b/questData/ClockworkCity.lua
index da89859..0bac546 100644
--- a/questData/ClockworkCity.lua
+++ b/questData/ClockworkCity.lua
@@ -1,4 +1,7 @@
-local DAS = DailyAutoShare
+DAS.shareables 	    = DAS.shareables    or {}
+DAS.bingo 		    = DAS.bingo 	    or {}
+
+
 local zoneId	= 980
 local zoneId2	= 981
 local zoneId3	= 983
@@ -108,4 +111,5 @@ DAS.makeBingoTable(shadow_cleft_id, bingo)
 DAS.shareables[zoneId2] = DAS.shareables[zoneId]
 DAS.shareables[zoneId3] = DAS.shareables[zoneId]
 DAS.bingo[zoneId2] = DAS.bingo[zoneId]
-DAS.bingo[zoneId3] = DAS.bingo[zoneId]
\ No newline at end of file
+DAS.bingo[zoneId3] = DAS.bingo[zoneId]
+
diff --git a/questData/Cyrodiil.lua b/questData/Cyrodiil.lua
index 1a72b3b..15465dd 100644
--- a/questData/Cyrodiil.lua
+++ b/questData/Cyrodiil.lua
@@ -1,9 +1,8 @@
 local zoneId	= 181

-DAS.shareables[zoneId] = {
-
+DAS.shareables[zoneId] = {
 }

-DAS.bingo[zoneId] = {
+local tbl2 = {}

-}
\ No newline at end of file
+DAS.makeBingoTable(zoneId, tbl2)
\ No newline at end of file
diff --git a/questData/GoldCoast.lua b/questData/GoldCoast.lua
index 95b76d9..926ea6e 100644
--- a/questData/GoldCoast.lua
+++ b/questData/GoldCoast.lua
@@ -1,5 +1,6 @@
-DAS.shareables 	= DAS.shareables or {}
-DAS.bingo 		= DAS.bingo 	 or {}
+DAS.shareables 	    = DAS.shareables    or {}
+DAS.bingo 		    = DAS.bingo 	    or {}
+

 local zoneId	= 823

@@ -19,8 +20,4 @@ table.insert(tbl2, "arena")
 table.insert(tbl2, "good")
 table.insert(tbl2, "evil")

-DAS.bingo[zoneId] = {}
-
-for key, value in pairs(tbl2) do
-	DAS.bingo[zoneId][key] = value
-end
+DAS.makeBingoTable(zoneId, tbl2)
diff --git a/questData/HewsBane.lua b/questData/HewsBane.lua
index 54a9d0c..1e69747 100644
--- a/questData/HewsBane.lua
+++ b/questData/HewsBane.lua
@@ -1,12 +1,12 @@
+DAS.shareables 	    = DAS.shareables    or {}
+DAS.bingo 		    = DAS.bingo 	    or {}
+
+
 local zoneId	= 816

 DAS.shareables[zoneId] = {
-	["en"] = {},
-	["de"] = {},
-	["fr"] = {},
-	["jp"] = {},
-	["ru"] = {},
 }
-DAS.bingo[zoneId] = {

-}
\ No newline at end of file
+local tbl2 = {}
+
+DAS.makeBingoTable(zoneId, tbl2)
\ No newline at end of file
diff --git a/questData/Morrowind.lua b/questData/Morrowind.lua
index 666e0f6..32b3f15 100644
--- a/questData/Morrowind.lua
+++ b/questData/Morrowind.lua
@@ -1,3 +1,7 @@
+DAS.shareables 	    = DAS.shareables    or {}
+DAS.bingo 		    = DAS.bingo 	    or {}
+
+
 local zoneId	= 849

 local tbl = {}
@@ -49,23 +53,20 @@ table.insert(tbl2, "tribe")
 table.insert(tbl, GetString(DAS_M_DELVE_SYNDI))
 table.insert(tbl2, "syndicate")

-
-
 table.insert(tbl, GetString(DAS_M_BOSS_WUYWU))
-table.insert(tbl2, "wuyu")
+table.insert(tbl2, {"wuyu", "wyu", "wuyuvus"})
 table.insert(tbl, GetString(DAS_M_BOSS_SWARM))
-table.insert(tbl2, "swarm")
+table.insert(tbl2, {"queen", "swarm", "kwama"})
 table.insert(tbl, GetString(DAS_M_BOSS_NILTH))
-table.insert(tbl2, "nilthog")
+table.insert(tbl2, {"nil", "nilthog", "oxen"})
 table.insert(tbl, GetString(DAS_M_BOSS_SALOT))
-table.insert(tbl2, "salo")
+table.insert(tbl2, {"salo", "salothan"})
 table.insert(tbl, GetString(DAS_M_BOSS_SIREN))
-table.insert(tbl2, "siren")
+table.insert(tbl2, {"siren", "song", "songbird"})
 table.insert(tbl, GetString(DAS_M_BOSS_APPRE))
-table.insert(tbl2, "dubdil")
-
-DAS.shareables[zoneId] = tbl
+table.insert(tbl2, {"dubdil", "dub"})

+DAS.shareables[zoneId]      = tbl
 DAS.QuestLists = DAS.QuestLists or {}
 DAS.QuestLists[zoneId] = {
 	["relic"] = {
@@ -125,31 +126,5 @@ DAS.shareables[925] = {
 	[1] = DAS.shareables[zoneId][19],
 }

-DAS.bingo[zoneId] = {}
-
-
-for key, value in pairs(tbl2) do
-	DAS.bingo[zoneId][value] = key
-end
-
-local function addEntryForSameIndex(tbl, questName, additionalValue)
-	for key, value in pairs(tbl) do
-		if key == questName then
-			tbl[additionalValue] = value
-		end
-	end
-end
-
-addEntryForSameIndex(DAS.bingo[zoneId], "wuyu", "wyu")
-addEntryForSameIndex(DAS.bingo[zoneId], "wuyu", "wuyuvus")
-addEntryForSameIndex(DAS.bingo[zoneId], "queen", "swarm")
-addEntryForSameIndex(DAS.bingo[zoneId], "nilthog", "nil")
-addEntryForSameIndex(DAS.bingo[zoneId], "nilthog", "oxen")
-addEntryForSameIndex(DAS.bingo[zoneId], "salo", "salothan")
-addEntryForSameIndex(DAS.bingo[zoneId], "siren", "songbird")
-addEntryForSameIndex(DAS.bingo[zoneId], "siren", "song")
-addEntryForSameIndex(DAS.bingo[zoneId], "ash", "eater")
-addEntryForSameIndex(DAS.bingo[zoneId], "dubdil", "dub")
-
-
+DAS.makeBingoTable(zoneId, tbl2)

\ No newline at end of file
diff --git a/questData/Wrothgar.lua b/questData/Wrothgar.lua
index 0bf5679..69e7bef 100644
--- a/questData/Wrothgar.lua
+++ b/questData/Wrothgar.lua
@@ -1,51 +1,40 @@
-DAS.shareables 	= DAS.shareables or {}
-DAS.bingo 		= DAS.bingo 	 or {}
+DAS.shareables 	    = DAS.shareables    or {}
+DAS.bingo 		    = DAS.bingo 	    or {}
+
+
 local zoneId	= 684

 DAS.shareables[zoneId] = {

-		[1] = GetString(DAS_W_POACHERS),
-		[2] = GetString(DAS_W_EDU),
-		[3] = GetString(DAS_W_NYZ),
-		[4] = GetString(DAS_W_CORI),
-		[5] = GetString(DAS_W_DOLMEN),
-		[6] = GetString(DAS_W_OGRE),
-
-		[7] = GetString(DAS_W_HARPIES),
-		[8] = GetString(DAS_W_SPIRITS),
-		[9] = GetString(DAS_W_DURZOGS),
-		[10] = GetString(DAS_W_DWEMER),
-		[11] = GetString(DAS_W_WEREWOLVES),
-	    [12] = GetString(DAS_W_THAT_OTHER),
+    [1] = GetString(DAS_W_POACHERS),
+    [2] = GetString(DAS_W_EDU),
+    [3] = GetString(DAS_W_NYZ),
+    [4] = GetString(DAS_W_CORI),
+    [5] = GetString(DAS_W_DOLMEN),
+    [6] = GetString(DAS_W_OGRE),
+
+    [7] = GetString(DAS_W_HARPIES),
+    [8] = GetString(DAS_W_SPIRITS),
+    [9] = GetString(DAS_W_DURZOGS),
+    [10] = GetString(DAS_W_DWEMER),
+    [11] = GetString(DAS_W_WEREWOLVES),
+    [12] = GetString(DAS_W_THAT_OTHER),
 }

-DAS.bingo[zoneId] = { -- Wrothgar
-	-- the poachers
-	["poa"] 	= 1,
-	-- the megalomaniac riekling king
-	["edu"] 	= 2,
-	-- snow and steam, the centurion
-	["nyz"] 	= 3,
-	-- cori the abomination
-	["cori"] 	= 4,
-	-- dolmen
-	["dolmen"] = 5,
-	["dol"] = 5,
-	["zan"] 	= 5,
-	-- shrek the angry literate ogre
-	["ogre"] 	= 6,
-	["mad"] 	= 6,
-	["shrek"] 	= 6,
+local tbl2 = {}

-
-	["harpy"] 	= 7,
-	["spirit"] 	= 8,
-	["durzog"] 	= 9,
-	["dwemer"] 	= 10,
-	["wolf"] 	= 11,
-	["doggo"] 	= 11,
-	["woffie"] 	= 11,
-	["bandit"] 	= 12,
-
-
-}
+table.insert(tbl2, {"poa", "poacher"})
+table.insert(tbl2, "edu")
+table.insert(tbl2, "nyz")
+table.insert(tbl2, "cori")
+table.insert(tbl2, {"dolmen", "zan"})
+table.insert(tbl2, {"ogre", "mad", "shrek"})
+
+table.insert(tbl2, "harpy")
+table.insert(tbl2, "spirit")
+table.insert(tbl2, "durzog")
+table.insert(tbl2, "dwemer")
+table.insert(tbl2, "wolf")
+table.insert(tbl2, "bandit")
+
+DAS.makeBingoTable(zoneId, tbl2)
diff --git a/startup.lua b/startup.lua
index f8f529f..1798010 100644
--- a/startup.lua
+++ b/startup.lua
@@ -43,9 +43,11 @@ local defaults = {
 		[181] = false,
 	},

-	["singleDailies"] = {},
-	["shareableDailies"] = {},
-	["speakStupid"] = false,
+	["singleDailies"]               = {},
+	["shareableDailies"]            = {},
+	["speakStupid"]                 = false,
+	["debug"] 		                = false,
+	["keepInviteUpOnDegroup"] 		= false,

 	["DasControl"] = {
 		["x"] = 0,
@@ -107,45 +109,65 @@ local defaults = {

 }

-local lastUnitTag = nil
+local function p(p1, p2, p3, p4, p5, p6)
+	if not DAS.GetDebugMode() or not p1 then return end
+    local s = zo_strformat(p1, p2, p3, p4, p5, p6)
+    if not p2 then
+        d(p1)
+        return
+    end
+	d(s)
+end
+DAS.debug = p
+
+local cachedDisplayName = GetUnitDisplayName('player')
+
+
 local lastUnitName = nil
 local em = EVENT_MANAGER

 local playerName		 	= nil
-local playerDisplayName		= nil
-local playerGroupStatus 	= false
 local playerGroupLeadStatus = false

+local function debugOut(p1, p2, p3, p4, p5, p6, p7, p8)
+    if (not p2) then
+        d(p1)
+        return
+    end
+    if p8 then
+        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>> <<7>> <<8>>", p1, p2, p3, p4, p5, p6, p7, p8))
+    elseif p7 then
+        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>> <<7>>", p1, p2, p3, p4, p5, p6, p7))
+    elseif p6 then
+        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>>", p1, p2, p3, p4, p5, p6))
+    elseif p5 then
+        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>>", p1, p2, p3, p4, p5))
+    elseif p4 then
+        d(zo_strformat("<<1>> <<2>> <<3>> <<4>>", p1, p2, p3, p4))
+    elseif p3 then
+        d(zo_strformat("<<1>> <<2>> <<3>>", p1, p2, p3))
+    else
+        d(zo_strformat("<<1>> <<2>>", p1, p2))
+    end
+end
+DAS.DebugOut = debugOut
 function DAS.Report(text)
 	if DAS.GetShutUp() then return end
 	d(text)
 end

-local function GetBingoMatch(messageText)
-
-	if DAS.IsMatch(messageText,"LFM") then return false end
-	if DAS.IsMatch(messageText,"can share") then return false end
-	if not (
-		DAS.IsMatch(messageText,"+") or
-		DAS.IsMatch(messageText,"%?$") or
-		DAS.IsMatch(messageText,"any group for") or
-		DAS.IsMatch(messageText,"can someone share")
-	) then
-		return false
-	end
-
-	words = {}
-	for word in messageText:gmatch("%w+") do table.insert(words, word) end
-
-	local bingoIndex
-
-	for i=1, #words do
-		bingoIndex = DAS.GetBingoIndexFromMessage(words[i])
-		if DAS.GetQuestStatus(DAS.GetQuestNameFromIndex(bingoIndex)) == DAS_STATUS_ACTIVE then return true end
-	end
-
-	return false
-
+local function GetBingoMatch(messageText)
+    for bingoIndex, bingoWord in pairs(DAS.getBingoTable()) do
+        local stringmatch = string.match(messageText, bingoWord)
+        if stringmatch and #stringmatch > 0 then
+            local ret = DAS.GetQuestStatus(DAS.GetQuestNameFromIndex(bingoIndex)) == DAS_STATUS_ACTIVE
+            if ret then
+                -- p("bingo match found: <<1>> -> <<2>>", bingoWord, messageText)
+                return true;
+            end
+        end
+    end
+	return false
 end
 function DAS.GetBingoMatch(messageText)
 	return GetBingoMatch(messageText)
@@ -155,19 +177,16 @@ end
 --======= Event hooks  =========
 --==============================

-local function OnGroupTypeChanged(eventCode, unitTag)
-	if not IsUnitGrouped("player") then
-		playerGroupStatus 			= false
-		playerGroupLeadStatus 		= true
-		DAS.SetAutoInvite(false)
-	end
+local function OnGroupTypeChanged(eventCode, unitTag) --
+	if IsUnitGrouped("player") or not DAS.GetStopInviteOnDegroup() then return end
+	DAS.SetAutoInvite(false)
 end
+
 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 not GetIsQuestSharable(journalIndex) then return end

 	if nil ~= DAS.GetArrayEntry(DAS.shareables[zoneId], questName) or
 	nil ~= DAS.GetArrayEntry(DAS.single[zoneId], questName) then
@@ -176,7 +195,6 @@ local function OnQuestAdded(eventCode, journalIndex, questName, objectiveName)
 	end
 end

-
 local function OnQuestShared(eventCode, questId)
 	if not DAS_QUEST_IDS[questId] then return end
 	local questName, _, _, displayName = GetOfferedQuestShareInfo(questId)
@@ -202,46 +220,37 @@ local function isPublicChannel(channelType)
 	)
 end

-local function OnChatMessage(eventCode, channelType, fromName, messageText, isCustomerService, fromDisplayName)
-
-
-
-
-	if not DAS.GetActiveIn() then return end
-
-	-- react to the group asking for shares
+local function OnChatMessage(eventCode, channelType, fromName, messageText, _, fromDisplayName)
+
+    -- don't react on player's messages, those are handled in a different function
+    if fromDisplayName == cachedDisplayName then return end
+
+    -- react to the group asking for shares
 	if (channelType == CHAT_CHANNEL_PARTY) then
-		if 	string.match(messageText, "share") or
-			string.match(messageText, "quest") or
-			string.match(messageText, "please") then
-			DAS.TryShareActiveDaily(fromDisplayName)
+		if 	string.match(messageText, "share") or
+			string.match(messageText, "quest") or
+			string.match(messageText, "please") then
+			DAS.TryShareActiveDaily()
+        elseif string.match(messageText, "stop") and string.match(messageText, "sharing") then
+            DAS.SetAutoShare(false)
 		end
 	end
-	fromName = zo_strformat(fromName)
-	-- if the message doesn't include +, no bingo stuff, nothing to be done here
-	if not string.match(messageText, "+") then return end
+    messageText = messageText:lower()
+    -- if someone's not looking for / offering
+	if not (string.match(messageText, "%+") or string.match(messageText, "looking for") or string.match(messageText, "need"))
+    or string.match(messageText, "can share") or string.match(messageText, "have") then return end
+

-	local isPublicChannel 	= isPublicChannel(channelType)
 	local isGuildMessage	= (channelType > 11 and channelType < 16)
-	if not (
-		isPublicChannel or
-		isGuildMessage and (DAS.GetListenInGuilds() or DAS.GetGuildInviteNumber() == channelType -11)
-	) then return end
+	if not (isPublicChannel or
+        isGuildMessage and (DAS.GetListenInGuilds() or DAS.GetGuildInviteNumber() == channelType -11)) then return end

 	if not GetBingoMatch(messageText) then return end

 	-- try invite if we are group lead
-	if IsUnitSoloOrGroupLeader('player') then return DAS.TryInvite(fromDisplayName, fromName) end
-
-	-- react to my own message
-	if DAS.IsPlayerName(fromName) and isPublicChannel then
-		if IsUnitGrouped('player') and DAS.GetAutoLeave() then
-			GroupLeave()
-		end
-		DAS.TryTriggerAutoAcceptInvite()
-	end
-
-
+	if IsUnitSoloOrGroupLeader('player') and DAS.GetAutoInvite() then
+        GroupInviteByName(fromDisplayName)
+    end
 end

 function DAS.SetChatListenerStatus(status)
@@ -253,40 +262,59 @@ function DAS.SetChatListenerStatus(status)
 end


+EVENT_MANAGER:RegisterForEvent("DASPlayerMessage", EVENT_CHAT_MESSAGE_CHANNEL,
+	function(eventCode, channelType, fromName, messageText, _, fromDisplayName)
+        if fromDisplayName == cachedDisplayName then return end
+        if not isPublicChannel(channelType) then return end
+        if not messageText or not string.match(messageText, "%+") then return end
+        if IsUnitGrouped('player') and DAS.GetAutoLeave() then GroupLeave()	end
+        DAS.TryTriggerAutoAcceptInvite()
+	end
+)
+EVENT_MANAGER:AddFilterForEvent("DASPlayerMessage", EVENT_CHAT_MESSAGE_CHANNEL, REGISTER_FILTER_UNIT_TAG, "player")
+
+DAS.bingoFallback = {}
 function DAS.makeBingoTable(zoneId, tbl)
-	DAS.bingo[zoneId] = {}
+	DAS.bingo[zoneId] = {}
+    DAS.bingoFallback[zoneId] = {}
 	for key, value in pairs(tbl) do
-		DAS.bingo[zoneId][value] = key
+        if type(value) == "table" then
+            local best = value[0]
+            for _, actualValue in pairs(value) do
+                DAS.bingo[zoneId][actualValue] = key
+                DAS.bingoFallback[zoneId][actualValue] = best
+            end
+        else
+            DAS.bingo[zoneId][value] = key
+        end
 	end
 	return DAS.bingo[zoneId]
 end
+function DAS.getBingoTable(zoneId)
+    zoneId = zoneId or DAS.GetZoneId()
+    return DAS.bingo[zoneId] or {}
+end

 local function OnPlayerActivated(eventCode)
-	local active 		= DAS.GetActiveIn()
-
+	local active 		= DAS.GetActiveIn()
 	DAS.SetHidden(not active)
 	DAS.SetChatListenerStatus(active)
 	DAS.RefreshControl(true)
-
+	bingoTable = (active and DAS.getBingoTable) or {}
 end

 local function OnGroupMemberAdded(eventCode, memberName)
-	DAS.TryShareActiveDaily(memberName)
+	DAS.TryShareActiveDaily()
 end

 local function OnUnitCreated(eventCode, unitTag)
-	OnGroupMemberAdded(nil, lastUnitName)
+    local unitZone = GetZoneId(GetUnitZoneIndex(unitTag))
+    if not DAS.GetActiveIn(unitZone) then return end
+    if GetUnitDisplayName(unitTag) == cachedDisplayName then return end
+    DAS.TryShareActiveDaily(unitZone)
 end
 local function OnGroupInvite(eventCode, inviterCharacterName, inviterDisplayName)
-	if DAS.GetAutoAcceptInvite() then
-		playerGroupStatus = true
-		AcceptGroupInvite()
-	end
-end
-
-local function OnGroupLeaderUpdate(eventCode, leaderTag)
-	leaderTag = zo_strformat(leaderTag)
-	playerGroupLeadStatus = (leaderTag == playerName or leaderTag == playerDisplayName)
+	if DAS.GetAutoAcceptInvite() then AcceptGroupInvite() end
 end

 local function OnQuestToolUpdate()
@@ -294,7 +322,13 @@ local function OnQuestToolUpdate()
 end

 local function OnQuestRemoved(eventCode, isCompleted, journalIndex, questName, zoneIndex, poiIndex, questID)
+	if not DAS.GetActiveIn() then return end
+
 	DAS.LogQuest(questName, isCompleted)
+	if not DAS.HasActiveDaily() then
+		DAS.SetAutoInvite(false)
+	end
+
 	DAS.RefreshControl(true)
 end

@@ -308,8 +342,6 @@ local function deleteYesterdaysLog()
 	end
 end

-
-
 local function hookQuestTracker()
 	local function refreshLabels()
 		DAS.RefreshLabels(false, true)
@@ -346,7 +378,6 @@ local function RegisterEventHooks()
 	em:RegisterForEvent(ADDON_NAME, EVENT_GROUP_MEMBER_JOINED,		OnGroupMemberAdded)
 	em:RegisterForEvent(ADDON_NAME, EVENT_UNIT_CREATED,	 			OnUnitCreated)
 	em:RegisterForEvent(ADDON_NAME, EVENT_UNIT_DESTROYED, 			OnGroupTypeChanged)
-	em:RegisterForEvent(ADDON_NAME, EVENT_LEADER_UPDATE, 			OnGroupLeaderUpdate)

 	-- DasControl:OnMoveStop
 	-- DailyAutoShare.SaveControlLocation(self)
@@ -360,7 +391,6 @@ function DailyAutoShare_Initialize(eventCode, addonName)

 	if addonName ~="DailyAutoShare" then return end

-
 	DailyAutoShare.settings = ZO_SavedVars:New("DAS_Settings", 0.2, nil, defaults)
 	DailyAutoShare.globalSettings = ZO_SavedVars:NewAccountWide("DAS_Globals", 0.2, "DAS_Global", defaults)

@@ -370,7 +400,6 @@ function DailyAutoShare_Initialize(eventCode, addonName)
 	DailyAutoShare.CreateMenu(DailyAutoShare.settings, defaults)
 	DAS.CreateGui()

-
 	EVENT_MANAGER:UnregisterForEvent("DailyAutoShare", EVENT_ADD_ON_LOADED)

 end
diff --git a/xml/VirtualButtons.xml b/xml/VirtualButtons.xml
deleted file mode 100644
index e69de29..0000000