Donation Chat Tag with Dev Products/TextChatService

For my game I wanted to make a chat tag for those who donated, I have 7 dev products that can give that chat tag. The file I’ve attached below is the Developer & tester tag I edited, I’m just unsure how to make something similar to this that works for the donators. I don’t have any experience with the dev product system.

Developer/Game Tester tags

2 Likes

To do this you would have to use a purchase history table alongside all the other values you are saving to your datastore for each player. When a player purchases something, you should add the receipt id and some details about the product into the table (key is receipt id, details is the value). When a player loads into the game, you need to loop through this table and check if any of the product IDs match with the donation product IDs (would be in the details stored), and if so, set an attribute for the player to say that they have donated. So in the chat tag method, you can check this value and add a donation tag accordingly.

You don’t have to use a purchase history table, but I’m giving you a solution using one because it is good practise to use purchase history tables regardless, and this is to help prevent session locking. You can instead just save a value for the player that indicates if they have donated, and this can be set in the process receipt function depending on the product id of the purchase. And of course load this value and use it to add a chat tag if need be.

3 Likes

I’m assuming you mean something like this? How would I go about adding the chat tag to this, and does this require a different script/location or is the local script in StarterPlayerScripts still a good place for it?

Yes exactly, however, this code should be on the server side instead. Also, rember to set the player product key value into the purchase history data store before you return purchase granted. You can use receiptInfo.ProductId to get the product id, and then you can simply use if devProducts[receiptInfo.ProductId] to determine wether the purchase was a donation (assuming all those ids in the table are donation products), and if so, add a new attribute to the player, call it ‘donated’ and set it to true. Back in the chat tag script, just use if player:GetAttribute(‘Donated’) to add the custom donation chat tag.

1 Like

I’m a little bit confused, again I have no experience with dev products so I don’t really know what goes where in the script. The donations are the only dev products in the game so would the table be unnecessary in this case? I also don’t know where the attribute would go in the script without getting errors.

here’s the client script that would work with a ChatTag attribute on the player:

-- script by bestspyboy/devforum

local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")

TextChatService.OnIncomingMessage = function(message)
	local props = Instance.new("TextChatMessageProperties")
	local tagString = ""

	if message.TextSource then
		-- get the player that sent the message
		local player = Players:GetPlayerByUserId(message.TextSource.UserId)
		if player then
			local tagText = player:GetAttribute("ChatTag")
			tagString = tagText  -- or add some text, or whatever you need to do
		end
	end

	props.PrefixText = tagString .. message.PrefixText

	return props
end

also that processReceipt has to be on a server script, as does the datastore code. looks like you’re already storing the player’s history so you can just add this to where you handle the ProcessReceipt:

-- at top of file
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")

-- ...

-- where you process the payment
donatorStore:SetAsync(receiptInfo.PlayerId)

and create a new server script:

-- script by bestspyboy/devforum

local Players = game:GetService("Players")
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")

Players.PlayerAdded(function (player)
	local success, error = pcall(function()
		purchased = donatorStore:GetAsync(player.UserId)
	end)

	if success and purchased then  -- the player has donated before
		player:SetAttribute("ChatTag", "<font>Donator</font>")
	elseif error then
		warn("DataStore error: " .. error)
	end
end)

this also means that your existing logic for checking if a user is a developer can be moved to the server if you want. it makes no real difference for precoded values.

1 Like

This had ended up breaking the chat and messages could not be sent. ^^;

huh.

you can’t have two TextChatService.OnIncomingMessage scripts if that’s what’s breaking, you’ll have to disable your old script for this to work (you can move things across if you want!)

if there’s an error message when the game loads, please send it :‎D

1 Like

From what I’m seeing the error comes from the ending on this script here.

I didn’t see it at first but this as well.

looks like there’s no end for the end of that processReceipt function. could be just the screenshot, but if there’s no end you need to finish the function, and add the donatorStore:SetAsync line.

it’s a linting problem (an accidental global variable), not an error, but you can add this on the line before the pcall if you want.

local purchased
1 Like

I have the line right above the product key, and I’m not sure how to finish the function I’m genuinely confused.

it’s hard to debug code with a screenshot. can you copy the full script into a reply?

you can format code with ``` backticks ```.

My bad

--LocalScript
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local tcs = game:GetService("TextChatService")

local devProducts = {"1898110950", "1898111128", "1898111346", "1898111537", "1898111705", "1898111875", "1898112079"}

-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory")

local function processReceipt(receiptInfo)
	-- Determine if the product was already granted by checking the data store
	donatorStore:SetAsync(receiptInfo.PlayerId)
	local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
	local purchased = false
	local success, result, errorMessage

	success, errorMessage = pcall(function()
		purchased = purchaseHistoryStore:GetAsync(playerProductKey)
	end)
	-- If purchase was recorded, the product was already granted
	if success and purchased then
		return Enum.ProductPurchaseDecision.PurchaseGranted
	elseif not success then
		error("Data store error:" .. errorMessage)
	end)

i’ve cleaned up the code, fixed the formatting and added the required code to save the user’s donation. copy this whole thing and replace the existing script.

-- edited by bestspyboy/devforum

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local tcs = game:GetService("TextChatService")

local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")

-- you don't need this line, keeping in case you do something else
local devProducts = {"1898110950", "1898111128", "1898111346", "1898111537", "1898111705", "1898111875", "1898112079"}

-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory")


local function processReceipt(receiptInfo)
	-- Determine if the product was already granted by checking the data store
	local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
	local purchased = false
	local success, result, errorMessage

	success, errorMessage = pcall(function()
		purchased = purchaseHistoryStore:GetAsync(playerProductKey)
	end)
	
	-- If purchase was recorded, the product was already granted
	if success and purchased then
		return Enum.ProductPurchaseDecision.PurchaseGranted
	elseif not success then
		error("Data store error:" .. errorMessage)
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end
	
	local success2, errorMessage2 = pcall(function()
		purchased = donatorStore:SetAsync(receiptInfo.PlayerId, true)  -- store the user as a donator
	end)
	
	if not success2 then
		error("Failed to process receipt due to data store error: " .. errorMessage2)
		return Enum.ProductPurchaseDecision.NotProcessedYet
	else	
		-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
		return Enum.ProductPurchaseDecision.PurchaseGranted
	end
end

MarketplaceService.ProcessReceipt = processReceipt
1 Like

This one also appears to have an issue, I removed the developer tag to see if the donation one worked (the previous script was disabled and moved to workspace), this one seems to break the chat. Only when the developer tag code is removed though.

-- script by bestspyboy/devforum

local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")

TextChatService.OnIncomingMessage = function(message)
	local props = Instance.new("TextChatMessageProperties")
	local tagString = ""

	if message.TextSource then
		-- get the player that sent the message
		local player = Players:GetPlayerByUserId(message.TextSource.UserId)
		if player then
			local tagText = player:GetAttribute("ChatTag")
			tagString = tagText  -- or add some text, or whatever you need to do
		end
	end

	props.PrefixText = tagString .. message.PrefixText

	return props
end

local tcs = game:GetService("TextChatService")
local players = game:GetService("Players")
local developers = {'YTFuntimeFoxyIsPro'}
local testers = {'CarsBoi101'}

--process a message
local function addTag(message:TextChatMessage): TextChatMessageProperties|nil
	--create new properties
	local properties = Instance.new("TextChatMessageProperties")

	if not message.TextSource then return nil end
	local player = players:GetPlayerByUserId(message.TextSource.UserId)

	--developer tag as example
	if table.find(developers, player.Name) then
		properties.PrefixText = "<font color = \"rgb(0, 251, 255)\">[Developer]</font> " .. message.PrefixText

	elseif
		table.find(testers, player.Name) then
		properties.PrefixText = "<font color = \"rgb(255, 0, 0)\">[Game Tester]</font> " .. message.PrefixText

	end

	return properties --return the properties to be used
end

--set the callback - this can only be done once
tcs.OnIncomingMessage = addTag

this is registering TextChatService.OnIncomingMessage twice. you can only do that once. you’ll have to merge the scripts like below - don’t try to combine anything. this is a complete script. you don’t have to edit anything, just paste it exactly like this.

-- script by bestspyboy/devforum

local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")

local developers = {'YTFuntimeFoxyIsPro'}
local testers = {'CarsBoi101'}

-- check if the user is a developer or tester
local function getDevTag(player)
	if table.find(developers, player.Name) then
		return "<font color = \"rgb(0, 251, 255)\">[Developer]</font> " .. message.PrefixText
	elseif table.find(testers, player.Name) then
		return "<font color = \"rgb(255, 0, 0)\">[Game Tester]</font> " .. message.PrefixText
	else
		return false
	end
end

TextChatService.OnIncomingMessage = function(message)
	local props = Instance.new("TextChatMessageProperties")
	local tagString = ""

	if message.TextSource then
		-- get the player that sent the message
		local player = Players:GetPlayerByUserId(message.TextSource.UserId)
		if player then
			-- check if the player is a developer/tester
			local devTag = getDevTag(player)

			-- check if the player has another assigned ChatTag from server
			local tagText = player:GetAttribute("ChatTag")

			if devTag then
				tagString = devTag
			elseif tagText then
				tagString = tagText
			else
				-- no chat tag, empty string
				tagString = ""
			end
		end
	end

	-- add the tagString to the message
	props.PrefixText = tagString .. message.PrefixText

	return props
end

1 Like

Okay, I didn’t realize that. This is the only script giving me issues now

-- script by bestspyboy/devforum

local Players = game:GetService("Players")
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")

Players.PlayerAdded(function (player)
	purchased = donatorStore:GetAsync(player.UserId)
	local success, error = pcall(function()
	end)

	if success and purchased then  -- the player has donated before
		player:SetAttribute("ChatTag", "<font color = \"rgb(128, 0, 255)\">[Donator]</font> ")
	elseif error then
		warn("DataStore error: " .. error)
	end
end)

my bad, was coding through devforum.

replace this line:

Players.PlayerAdded(function (player)

with this:

Players.PlayerAdded:Connect(function(player)
1 Like

Donation tag still doesn’t seem to work. ^^; I’m not seeing any errors in the logs either.