Great idea, I decided to create an entirely new system & gui to achieve that.
badges.rbxl (43.5 KB)
This seldomly used API endpoint proved its usefulness.
https://badges.roblox.com/v1/users/{userId}/badges/awarded-dates/{badgeIds}
--SERVER
local players = game:GetService("Players")
local replicated = game:GetService("ReplicatedStorage")
local badgeRemote = replicated.BadgeRemote
local http = game:GetService("HttpService")
local getAsync = http.GetAsync
local jsonDecode = http.JSONDecode
local urlEncode = http.UrlEncode
local proxy = "roproxy.com"
local badgesUrl = "https://badges.%s/v1/universes/%s/badges?limit=100&sortOrder=Asc&cursor=%s"
local badgeTimestampsUrl = "https://badges.%s/v1/users/%s/badges/awarded-dates?badgeIds=%s"
local playerDebounces = {}
local badgeIds
local function getBadgesRecursive(cursor, badges, retries)
cursor = cursor or ""
badges = badges or {}
retries = retries or 0
if retries >= 10 then return {} end
local requestUrl = badgesUrl:format(proxy, game.GameId, cursor)
local success, result = pcall(getAsync, http, requestUrl)
if success then
if result then
local success2, result2 = pcall(jsonDecode, http, result)
if success2 then
if result2 then
for _, badgeItem in ipairs(result2.data) do
table.insert(badges, badgeItem.id)
end
cursor = result2.nextPageCursor
if cursor then
return getBadgesRecursive(cursor, badges, retries)
else
return badges
end
end
else
task.wait()
warn(result2)
retries += 1
return getBadgesRecursive(cursor, badges, retries)
end
end
else
task.wait()
warn(result)
retries += 1
return getBadgesRecursive(cursor, badges, retries)
end
end
local function getBadgeTimestampsRecursive(userId, pageIndex, timestamps, retries)
pageIndex = pageIndex or 1
timestamps = timestamps or {}
retries = retries or 0
if retries >= 10 then return {} end
local badgeList = ""
for badgeIndex = ((pageIndex - 1) * 100) + 1, math.min(#badgeIds, pageIndex * 100) do
badgeList..=badgeIds[badgeIndex]..","
end
badgeList = badgeList:gsub(",$", "")
local success, result = pcall(urlEncode, http, badgeList)
if success then
if result then
local requestUrl = badgeTimestampsUrl:format(proxy, userId, result)
local success2, result2 = pcall(getAsync, http, requestUrl)
if success2 then
if result2 then
local success3, result3 = pcall(jsonDecode, http, result2)
if success3 then
if result3 then
for _, badgeTimestamp in ipairs(result3.data) do
timestamps[badgeTimestamp.badgeId] = badgeTimestamp.awardedDate
end
if pageIndex <= #badgeIds/100 then
pageIndex += 1
return getBadgeTimestampsRecursive(userId, pageIndex, timestamps, retries)
else
return timestamps
end
end
else
task.wait()
warn(result3)
retries += 1
return getBadgeTimestampsRecursive(userId, pageIndex, timestamps, retries)
end
end
else
task.wait()
warn(result2)
retries += 1
return getBadgeTimestampsRecursive(userId, pageIndex, timestamps, retries)
end
end
else
task.wait()
warn(result)
retries += 1
return getBadgeTimestampsRecursive(userId, pageIndex, timestamps, retries)
end
end
local function onBadgeRemoteFired(player)
repeat task.wait() until badgeIds
if tick() - (playerDebounces[player] or 0) <= 20 then return end
playerDebounces[player] = tick()
local badgeTimestamps = getBadgeTimestampsRecursive(player.UserId)
badgeRemote:FireClient(player, badgeTimestamps)
end
local function onPlayerAdded(player)
repeat task.wait() until badgeIds
local badgeTimestamps = getBadgeTimestampsRecursive(player.UserId)
badgeRemote:FireClient(player, badgeTimestamps, badgeIds)
end
badgeRemote.OnServerEvent:Connect(onBadgeRemoteFired)
players.PlayerAdded:Connect(onPlayerAdded)
badgeIds = getBadgesRecursive()
--LOCAL
local tweens = game:GetService("TweenService")
local badges = game:GetService("BadgeService")
local getBadgeInfoAsync = badges.GetBadgeInfoAsync
local replicated = game:GetService("ReplicatedStorage")
local badgeRemote = replicated:WaitForChild("BadgeRemote")
local badgesGui = script.Parent
local badgesFrame = badgesGui:WaitForChild("BadgesFrame")
local reloadButton = badgesFrame:WaitForChild("ReloadButton")
local badgesList = badgesFrame:WaitForChild("BadgesList")
local badgeFrameTemplate = script:WaitForChild("BadgeFrame")
local toggleButton = badgesGui:WaitForChild("ToggleButton")
local guiToggle = true
local reloadToggle = true
local function onBadgeRemoteFired(badgeTimestamps, badgeIds)
if badgeIds then
for _, badgeId in ipairs(badgeIds) do
local success, result = pcall(getBadgeInfoAsync, badges, badgeId)
if success then
if result then
local badgeFrame = badgeFrameTemplate:Clone()
badgeFrame.Name = badgeId.."Frame"
badgeFrame.BadgeName.Text = result.Name
badgeFrame.BadgeImage.Image = "rbxassetid://"..result.IconImageId
local badgeTimestamp = badgeTimestamps[tostring(badgeId)]
if badgeTimestamp then
badgeFrame.LayoutOrder = 1
badgeFrame.BadgeDate.Text = badgeTimestamp:match("^(%d+%-%d+%-%d+)T")
badgeFrame.BadgeTime.Text = badgeTimestamp:match("T(%d+:%d+:%d+)%.")
badgeFrame.BadgeCheckImage.Image = "rbxassetid://9038877993"
else
badgeFrame.BadgeCheckImage.Image = "rbxassetid://9038877823"
end
badgeFrame.Parent = badgesList
end
end
end
reloadButton.Image = "rbxassetid://9082930345"
reloadToggle = false
else
for _, badgeFrame in ipairs(badgesList:GetChildren()) do
if badgeFrame:IsA("Frame") then
local badgeId = badgeFrame.Name:gsub("Frame", "")
local badgeTimestamp = badgeTimestamps[badgeId]
if badgeTimestamp then
badgeFrame.LayoutOrder = 1
badgeFrame.BadgeDate.Text = badgeTimestamp:match("^(%d+%-%d+%-%d+)T")
badgeFrame.BadgeTime.Text = badgeTimestamp:match("T(%d+:%d+:%d+)%.")
badgeFrame.BadgeCheckImage.Image = "rbxassetid://9038877993"
else
badgeFrame.LayoutOrder = 0
badgeFrame.BadgeDate.Text = "Date: N/A"
badgeFrame.BadgeTime.Text = "Time: N/A"
badgeFrame.BadgeCheckImage.Image = "rbxassetid://9038877823"
end
end
end
end
end
local function onToggleButtonClicked()
guiToggle = not guiToggle
local guiPosition = if guiToggle then 0.025 else -0.2
toggleButton.Text = if guiToggle then "<\n<\n<" else ">\n>\n>"
local tween = tweens:Create(badgesFrame, TweenInfo.new(0.3, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {Position = UDim2.fromScale(guiPosition, 0.5)})
tween:Play()
tween.Completed:Wait()
end
local function onReloadButtonClicked()
if reloadToggle then return end
reloadToggle = not reloadToggle
reloadButton.Image = "rbxassetid://9083079276"
badgeRemote:FireServer()
task.wait(20)
reloadButton.Image = "rbxassetid://9082930345"
reloadToggle = false
end
toggleButton.MouseButton1Click:Connect(onToggleButtonClicked)
reloadButton.MouseButton1Click:Connect(onReloadButtonClicked)
badgeRemote.OnClientEvent:Connect(onBadgeRemoteFired)