Bad argument #2 to 'gsub' (string/function/table expected)

I’m trying to update my local copy of Macro-Talk and for some reason the following substitution patterns are causing the following error to be triggered.

bad argument #2 to 'gsub' (string/function/table expected)

Simplest example:

	["[Hh]"] = function(unit)
		return UnitHealth(unit).."/"..UnitHealthMax(unit)
	end,

the rest of them are commented out in the full doc below (238-294):

--[[ Text substitutions ]]
local L = MacroTalk_Locals
local SendChatMessage = C_ChatInfo.SendChatMessage or SendChatMessage
local wowmainline = (WOW_PROJECT_ID == WOW_PROJECT_MAINLINE)

local subsBase = {
	["%%[Nn]"] = function() return UnitName("player") end,
	["%%[Zz]"] = GetRealZoneText,
	["%%[Ss][Zz]"] = function() 
		local subZ
		if GetSubZoneText() ~= "" then
			subZ = GetSubZoneText()
		else
			subZ = GetRealZoneText()
		end
		return subZ
	end,
	
	["%%[Ll][Oo][Cc]"] = function()
		local mapID = C_Map.GetBestMapForUnit("player")
		if mapID == nil then
			return;
		end
		local position = C_Map.GetPlayerMapPosition(mapID,"player")
		if not position then
			return;
		end
		local x, y = position:GetXY() 
		return ("%0.1f, %0.1f"):format(x*100, y*100)
	end,
	
	["%%[Ww][Pp]"] = function()
		local mapID = C_Map.GetBestMapForUnit("player")
		if mapID == nil then
			return;
		end
		local position = C_Map.GetPlayerMapPosition(mapID,"player")
		if not position then
			return;
		end
		if C_Map.GetUserWaypointHyperlink() == nil then
			C_Map.SetUserWaypoint(UiMapPoint.CreateFromCoordinates(mapID,position:GetXY()))
			return C_Map.GetUserWaypointHyperlink(), C_Map.ClearUserWaypoint()
		else
			return ""
		end
	end,
	
	["%%[Pp][Ii][Nn]"] = function()
		local mapID = C_Map.GetBestMapForUnit("player")
		if mapID == nil then
			return;
		end
		local position = C_Map.GetPlayerMapPosition(mapID,"player")
		if not position then
			return;
		end
		if C_Map.GetUserWaypointHyperlink() == nil then
			return ""
		else
			return C_Map.GetUserWaypointHyperlink()
		end
	end,
	
	["%%[Cc][Oo][Vv]"] = function()
		local covenantID = C_Covenants.GetActiveCovenantID()
		if covenantID == nil then
			return;
		end
		if covenantID == 0 then 
			return "<no covenant>"
		end
		local covenantData = C_Covenants.GetCovenantData(covenantID)
		if covenantData == nil then
			return;
		else
			return covenantData.name
		end
	end,
	
	["%%[Cc][Oo][Vv][Bb]"] = function()
		local covenantID = C_Covenants.GetActiveCovenantID()
		if covenantID == nil then
			return;
		end
		if covenantID == 0 then 
			return;
		end
		local covenantData = C_Covenants.GetCovenantData(covenantID)
		if covenantData == nil then
			return;
		else
			return covenantData.name
		end
	end,
	
	["%%[Rr][Nn][Ll]"] = function()
		local covenantID = C_Covenants.GetActiveCovenantID()
		if covenantID == nil then
			return;
		end
		if covenantID == 0 then 
			return "<no covenant>"
		end
		local renownLevel = C_CovenantSanctumUI.GetRenownLevel()
		if renownLevel == nil then
			return;
		else
			return renownLevel
		end
	end,
	
	["%%[Rr][Nn][Ll][Bb]"] = function()
		local covenantID = C_Covenants.GetActiveCovenantID()
		if covenantID == nil then
			return;
		end
		if covenantID == 0 then 
			return;
		end
		local renownLevel = C_CovenantSanctumUI.GetRenownLevel()
		if renownLevel == nil then
			return;
		else
			return renownLevel
		end
	end,
	
	["%%[Rr][Tt]"] = function()
		local datetime = C_DateAndTime.GetCurrentCalendarTime()
		if datetime == nil then
			return;
		else
			local hours = datetime.hour
			local minutes = datetime.minute
			if hours < 10 then
				hours = "0"..hours
			end
			if minutes < 10 then
				minutes = "0"..minutes
			end
			return hours..":"..minutes
		end
	end,
	
	["%%[Ll][Tt]"] = function()
		local datetime = date("%H:%M")
		if datetime == nil then
			return;
		else
			return datetime
		end
	end,
	
	--[[
	You can add straight text substitutions
	to this table. See the example below. Remove the -- from the front to use
	it. Note also that it's case sensitive unless you write both upper- and
	lower-case letters inside brackets like the substitutions above.]]
	
	--["%%tsinfo"] = "TeamSpeak info: Server: host.domain.com, Password: 12345",
}

local sexes = {
	L["<no %s>"]:format(L["gender"]),
	MALE:lower(),
	FEMALE:lower()
}

local unitInfoUnits = {
	[""] = "player",
	["[Tt]"] = "target",
	["[Ff]"] = "focus",
	["[Mm]"] = "mouseover",
	["[Pp]"] = "pet",
	["[Tt][Tt]"] = "targettarget",
}

local unitInfoSuffixes = {
	[""] = UnitName,
	
	["[Ll]"] = function(unit)
		local level = UnitLevel(unit)
		return level > 0 and level or level < 0 and "??"
	end,
	
	["[Cc]"] =  UnitClass,
	
	["[Cc][Ll]"] = UnitClassification,		
	
	["[Gg]"] = function(unit)
		return sexes[UnitSex(unit)]
	end,
	
	["[Gg][Bb]"] = function(unit)
		local sex = UnitSex(unit)
		return sex > 1 and sexes[UnitSex(unit)] or ""
	end,
	
	["[Rr]"] = function(unit)
		local race
		if UnitIsPlayer(unit) then
			race = UnitRace(unit)
		else
			race = UnitCreatureType(unit)
		end
		return race or L["<no %s>"]:format(RACE:lower())
	end,
	
	["[Rr][Bb]"] = function(unit)
		local race
		if UnitIsPlayer(unit) then
			race = UnitRace(unit)
		else
			race = UnitCreatureType(unit)
		end
		return race or ""
	end,
	
	["[Gg][Uu]"] = function(unit)
		local guild
		guild = GetGuildInfo(unit)
		return guild or "<no guild>"
	end,
	
	["[Gg][Uu][Bb]"] = function(unit)
		local guild
		guild = GetGuildInfo(unit)
		return guild or ""
	end,
	
	["[Rr][Mm]"] = function(unit)
		local nam, realm
		name, realm = UnitName(unit)
		return realm or GetRealmName()
	end,
		
	-- ["[Hh]"] = function(unit)
	-- 	return UnitHealth(unit).."/"..UnitHealthMax(unit)
	-- end,
	
	-- ["[Hh][Pp]"] = function(unit)
	-- 	if (wowmainline) then
	-- 		return ("%.0f%%%%"):format(UnitHealthPercent(unit))
	-- 	else
	-- 		return ("%.0f%%%%"):format(UnitHealth(unit)/UnitHealthMax(unit)*100)
	-- 	end
	-- end,
	
	-- ["[Pp][Ww]"] = function(unit)
	-- 	local pwType, pwTypeTxt = UnitPowerType(unit)
	-- 	local max = UnitPowerMax(unit,pwType)
	-- 	if max > 0 then
	-- 		return UnitPower(unit,pwType).."/"..max
	-- 	else
	-- 		return L["<no %s>"]:format(pwTypeTxt:lower())
	-- 	end
	-- end,
	
	-- ["[Pp][Ww][Bb]"] = function(unit)
	-- 	local max = UnitPowerMax(unit)
	-- 	if max > 0 then
	-- 		return UnitPower(unit).."/"..max
	-- 	else
	-- 		return ""
	-- 	end
	-- end,
	
	-- ["[Pp][Ww][Pp]"] = function(unit)
	-- 	local pwType, pwTypeTxt = UnitPowerType(unit)
	-- 	local max = UnitPowerMax(unit,0)
	-- 	if max > 0 then
	-- 		if (wowmainline) then
	-- 			return ("%.0f%%%%"):format(UnitPowerPercent(unit,pwType))
	-- 		else
	-- 			return ("%.0f%%%%"):format(UnitPower(unit,pwType)/max*100)
	-- 		end
	-- 	else
	-- 		return L["<no %s>"]:format(pwTypeTxt:lower())
	-- 	end
	-- end,
	
	-- ["[Pp][Ww][Pp][Bb]"] = function(unit)
	-- 	local max = UnitPowerMax(unit,0)
	-- 	if max > 0 then
	-- 		if (wowmainline) then
	-- 			return ("%.0f%%%%"):format(UnitPowerPercent(unit))
	-- 		else
	-- 			return ("%.0f%%%%"):format(UnitPower(unit)/max*100)
	-- 		end
	-- 	else
	-- 		return ""
	-- 	end
	-- end,
	
	["[Pp][Ww][Tt]"] = function(unit)
		local pwType, pwTypeTxt = UnitPowerType(unit)
		if pwType == nil then
			return L["<no power type>"]
		else
			return pwTypeTxt:lower()
		end
	end,
	
	["[Pp][Ww][Tt][Bb]"] = function(unit)
		local pwType, pwTypeTxt = UnitPowerType(unit)
		if pwType == nil then
			return ""
		else
			return pwTypeTxt:lower()
		end
	end,
	
	["[Ii][Cc]"] = function(unit)
		local index = GetRaidTargetIndex(unit)
		return index and "{"..(_G["RAID_TARGET_"..index]):lower().."}" or 
			L["<no %s>"]:format(EMBLEM_SYMBOL:lower())
	end,
	
	["[Ii][Cc][Bb]"] = function(unit)
		local index = GetRaidTargetIndex(unit)
		return index and "{"..(_G["RAID_TARGET_"..index]):lower().."}" or ""
	end,
	
	["[Gg][Nn]"] = function(unit)
		local raidN = UnitInRaid(unit) 
		if raidN == nil then
			if unit == "player" then
				return "<not in raid>"
			else
				name, realm = UnitName(unit)
				if realm == nil then
					return "< "..name.." is not in your raid>"
				else
					return "< "..name.."-"..realm.." is not in your raid>"
				end
			end
		else
			local name, rank, subgroup = GetRaidRosterInfo(raidN)
			return subgroup
		end
	end,
	
	["[Gg][Nn][Bb]"] = function(unit)
		local raidN = UnitInRaid(unit) 
		if raidN == nil then
			return ""
		else
			local name, rank, subgroup = GetRaidRosterInfo(raidN)
			return subgroup
		end
	end,
	
	["[Nn][Tt]"] = function(unit)
		if UnitIsVisible(unit) == nil then
			return ""
		else
			local titleName = UnitPVPName(unit)
			return titleName or ""
		end
	end,
	
	["[Uu][Tt]"] = function(unit)
		if UnitIsVisible(unit) == nil then
			return ""
		else
			local titleName = UnitPVPName(unit)
			if titleName == nil then
				return ""
			end	
			local name = UnitName(unit)
			if name == nil then 
				return ""
			end		
			if 	name == titleName then 
				return "<no title>"
			end
			local title = string.gsub(titleName, name, "")
			title = string.gsub(title,",","")
			if string.sub(title,1,1) == " " then
				title = string.sub(title,2)
			end
			if string.sub(title,string.len(title)) == " " then
				title = string.sub(title,1,string.len(title)-1)
			end
			return title
		end
	end,
	
	["[Uu][Tt][Bb]"] = function(unit)
		if UnitIsVisible(unit) == nil then
			return ""
		else
			local titleName = UnitPVPName(unit)
			if titleName == nil then
				return ""
			end	
			local name = UnitName(unit)
			if name == nil then 
				return ""
			end		
			if 	name == titleName then 
				return ""
			end
			local title = string.gsub(titleName, name, "")
			title = string.gsub(title,",","")
			if string.sub(title,1,1) == " " then
				title = string.sub(title,2)
			end
			if string.sub(title,string.len(title)) == " " then
				title = string.sub(title,1,string.len(title)-1)
			end
			return title
		end
	end,
		
}

for initial, unit in pairs(unitInfoUnits) do
	local noUnit = L["<no %s>"]:format(unit)
	for suffix, func in pairs(unitInfoSuffixes) do
		if initial ~= "" or suffix ~= "" then
			local code = "%%"..initial..suffix
			if subsBase[code] then
				error(L["MacroTalk: Conflicting substitutions: "..code])
			end
			subsBase[code] = function()
				return UnitExists(unit) and func(unit) or noUnit
			end
		end
	end
end

local substitutions = {}
local i = 1
for k, v in pairs(subsBase) do
	substitutions[i] = { code = k, func = v }
	i = i + 1
end

sort(substitutions, function(subs1, subs2)
	return subs2.code:len() < subs1.code:len()
end)

local OrigSendChatMessage = SendChatMessage
function C_ChatInfo.SendChatMessage(text, ...)
	for _, substitution in ipairs(substitutions) do
		local func = substitution.func
		text = text:gsub(substitution.code,
			type(func) == "function" and func() or func)
	end
	return OrigSendChatMessage(text, ...)
end

I tried checking type on the return and it shows it’s a string.
I assume it’s something related to secrets but I’m not sure how to go about resolving it.

gsub and .. don’t work with secrets.

try format and https://warcraft.wiki.gg/wiki/API_string.concat

["[Hh]"] = function(unit)
		return format("%s/%s", UnitHealth(unit), UnitHealthMax(unit))
end,

search “concat” at:
https://warcraft.wiki.gg/wiki/Patch_12.0.0/API_changes

That whole restrictions section is a must read :wink:

2 Likes

This is what has killed and will kill a lot of addons. If an addon needs to process, manipulate, or test a value Blizzard has made a secret, there’s not much you can do about it.

It requires extra effort which people will have to evaluate their desire/capicity to put in.

1 Like

Figure it was something like that. Sadly even using format it still breaks, presumably because of the whole gsub thing.

What’s the code?

local OrigSendChatMessage = SendChatMessage
function C_ChatInfo.SendChatMessage(text, ...)
	for _, substitution in ipairs(substitutions) do
		local func = substitution.func
		text = text:gsub(substitution.code,
			type(func) == "function" and func() or func)
	end
	return OrigSendChatMessage(text, ...)
end

It’s at the bottom of the last code bock in the OP

local substitutions = {}
local i = 1
for k, v in pairs(subsBase) do
	substitutions[i] = { code = k, func = v }
	i = i + 1
end

sort(substitutions, function(subs1, subs2)
	return subs2.code:len() < subs1.code:len()
end)

local OrigSendChatMessage = SendChatMessage
function C_ChatInfo.SendChatMessage(text, ...)
	for _, substitution in ipairs(substitutions) do
		local func = substitution.func
		text = text:gsub(substitution.code,
			type(func) == "function" and func() or func)
	end
	return OrigSendChatMessage(text, ...)
end

Probably have to replace the gsub with a string.find, string.sub, string.concat loop or some such shenanigans.

It’s a brave new world.

1 Like

That sounds… not worth the effort.

I’m going through that a tiny bit with my addon, wiki gg and you got me caught up mostly so far!

for

try these API on the vars values and functions:

--//some API to go about resolving it check wiki.gg for more details 
--//https://warcraft.wiki.gg/wiki/Patch_12.0.0/API_changes#Secret_values

--// API issecure
secure = issecure()
isSecure, taint = issecurevariable(tbl, variable)
isSecure, taint = issecurevalue(value)
--// API issecret
isSecret = issecretvalue(value)
isSecretOrContentsSecret = issecrettable(table)
1 Like