Webhook sending multiple times & one other bug

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I would like my script to work and to send a message to my discord server whenever someone completes the level.
  2. What is the issue? Include screenshots / videos if possible!

    As you can see, it sends multiple times, and it is very annoying.

Also here, i cannot find the problem and it is very confusing me!

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    My tried solutions are in the text block
local teleportPart = script.Parent
local teleportPlaceId = 128946683861217 -- Replace with your target game Place ID
local levelCap = 1
local value = 0
local httpservice = game:GetService("HttpService")
local url = "exampleURL"
local leavingplayers = {}
local notifiedPlayers = {}
local colors = {
	["join"] = 4660456,
	["leave"] = 16718924,
	["go"] = 63882, 
	["achieve"] = 16769357,
}

local function datasend(player, action, color)
	if player and not notifiedPlayers[player.UserId] then
	local tries = 0
	if player.UserId == 331550209 then
		return
	end
	if player.UserId == 1 then
		return
	end
	local description = player.Name..action.."\nhttps://www.roblox.com/users/"..player.UserId.."/profile/"
	local data = {
		['embeds'] = {{
			['title'] = player.Name,
			['description'] = description,
			['color'] = color, 
			['footer'] = { 
				['text'] = os.date("%x").. "    " .. os.date("%X"), 
				["icon_url"] = "https://images-ext-1.discordapp.net/external/2dZVVL6feMSM7lxfFkKVW__LToSOzmToSEmocJV5vcA/https/cdn.discordapp.com/embed/avatars/0.png",
			}
		}}
	}
	local data_completed = httpservice:JSONEncode(data) 
	local success = pcall(function()
		httpservice:PostAsync(url,data_completed) 
	end)
	if not success and tries < 5 then 
		repeat
			tries += 1
			data_completed = httpservice:JSONEncode(data)
			httpservice:PostAsync(url,data_completed) 
		until success or tries == 5
	end
end

local TeleportService = game:GetService("TeleportService")
local privateServerCode = TeleportService:ReserveServer(teleportPlaceId)


local function onTouch(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)


	if player and not notifiedPlayers[player.UserId] then
		if player and not notifiedPlayers[player.UserId] and value == 0 then -- tried doing this If statement twice
			datasend(player, " has beaten 1-1!", colors.achieve)
			value = 1 -- tried doing a debounce
			wait(0.3)
			value = 0
		end
		notifiedPlayers[player.UserId] = true
		for i,v in pairs(player.Quests:GetChildren()) do
			if v.Value == "Beat level 1-1 In Platformer!" then
				v.Completion.Value = v.Completion.AmountNeeded.Value
			end
		end

		TeleportService:TeleportToPrivateServer(teleportPlaceId, privateServerCode, {player}, nil, {
			PlayerTeleportTimeout = 10
		})
		teleportPart.crumble.Enabled = true
		teleportPart.crumble2.Enabled = true
		teleportPart.crumble3.Enabled = true
		teleportPart.Sound:Play()
		wait(3)
		teleportPart.crumble.Enabled = false
		teleportPart.crumble2.Enabled = false
		teleportPart.crumble3.Enabled = false
		local leaderstats = player:FindFirstChild("leaderstats")
		if leaderstats then
			local level = leaderstats:FindFirstChild("Level")
			if level and level.Value < levelCap then
				level.Value = level.Value + 1
			end
		end

	end
end

teleportPart.Touched:Connect(onTouch)

Try adding this before the second if statement:

notifiedPlayers[player.UserId] = true

You can instead remove the second if statement and add this right after the first one.


Does the code not run because of an error?

.Touched fires for every limb & can retrigger while the part you’re using for this is still inside the players limb / hitbox, so its going to fire multiple times with your current setup in the same frame

This is also partially due to you only setting notifiedPlayers[player.UserId] = true after sending the message, which leaves a window for duplicates and due to your touched event firing for every part it hits, this is naturally going to happen

Also, Discord only allows ~30 requests /min per webhook; if you send them faster then it will queue the requests

Example:

local HttpService = game:GetService("HttpService")
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")

local url = "exampleURL"
local placeId = 128946683861217
local colors = { achieve = 16769357 }

local notifiedPlayers  = {}

local function post(body)
	for i = 1,5 do
		local ok,err = pcall(HttpService.PostAsync, HttpService, url, body)
		if ok then return end
		task.wait(2^i * 0.3)
	end
end

local function sendEmbed(plr, text, colour)
	local payload = HttpService:JSONEncode({
		embeds = {{
			title = plr.Name,
			description = plr.Name..text..
				"\nhttps://www.roblox.com/users/"..plr.UserId.."/profile/",
			color = colour,
			footer = { text = os.date("%x  %X") }
		}}
	})
	post(payload)
end

local teleportPart = script.Parent
local serverCode

teleportPart.Touched:Connect(function(hit)
	local plr = Players:GetPlayerFromCharacter(hit.Parent)
	if not plr or notifiedPlayers[plr.UserId] then return end
	
	notifiedPlayers[plr.UserId] = true

	sendEmbed(plr," has beaten 1-1!",colors.achieve)

	-- award quest / level here ...

	serverCode = serverCode or TeleportService:ReserveServer(placeId)
	TeleportService:TeleportToPrivateServer(placeId, serverCode, {plr}, nil, {PlayerTeleportTimeout = 10})
end)