Given that Roblox isn’t implementing this apparently:
I’ve spent a few hours writing some javascript code that gets rid of all the free items (e.g. free private servers) that show in the first page (100 items) for group sales…
I’ll continue to work on the code, and notify anyone who clicks Yes to the poll below when I release new versions of it (to include things like allow for viewing multiple pages worth of items)
Want to be notified about updates to this?
To use the code, you will need to download an extension such as TamperMonkey or ViolentMonkey, and save the following code:
// ==UserScript==
// @name Remove free items from group sales
// @namespace Violentmonkey Scripts
// @match https://www.roblox.com/groups/configure?id=*/revenue/sales
// @grant none
// @version 1.0
// @author SeargentAUS
// @description 11/13/2024, 1:32:15 PM
// ==/UserScript==
// Config:
const RemoveFree = true // Setting this to false loads 100 items, without removing the free ones.
// Don't touch the below code (or do, I don't mind. Just know that it may break if you do something wrong!)
// Got a question about it? You can DM me on the Roblox devforum, @SeargentAUS!
Group = document.URL.split("id=")[1].split("#")[0]
console.log(Group)
const URL = "https://economy.roblox.com/v2/groups/" + Group + "/transactions?cursor=&limit=100&transactionType=Sale"
const Thumbnails = "https://thumbnails.roblox.com/v1/batch"
cursor = null
const item = `<tr class="ISPENDING">
<td class="date"><div>DATE</div><div>TIME</div></td>
<td class="user"><div class="avatar-card"><div class="avatar avatar-headshot-xs avatar-headshot">
<a class="avatar-card-link" href="https://www.roblox.com/users/USERID/profile">
<span class="thumbnail-2d-container avatar-card-image"><img class=""
src="USERAVATAR" alt="" title=""></span></span>
</div><div class="avatar-card-caption"><a class="avatar-card-name text-name text-overflow"
href="https://www.roblox.com/users/USERID/profile">USERNAME</a></div></div></td><td class="item">
<div class="item-format item-sale-format"><span class="item-card-image"><span class="thumbnail-2d-container">
<img class="" src="PURCHICON" alt="" title="">
</span></span><div class="item-description"><div>Sold <span class="text-overflow">PURCHASE</span></div>
<a class="item-card-label text-link text-overflow" href="https://www.roblox.com/games/PLACEID">PLACENAME</a>
</div></div></td><td class="amount icon-robux-container"><span></span><span class="icon-robux-16x16">
</span><span class="">AMOUNT</span>PENDINGICON</td></tr>`.replaceAll("\n", "")
window.onload = () => {
const req = new XMLHttpRequest()
req.withCredentials = true
req.addEventListener("load", function () {
const responseRAW = req.response
const response = JSON.parse(responseRAW)
cursor = response["nextPageCursor"]
function processTable() {
console.log("Found table")
const table = document.getElementsByClassName("table table-striped transactions")[0].children[0]
let children = table.childNodes
while (children.length > 1) {
table.removeChild(children[1])
children = table.childNodes
}
let base = table.innerHTML
let avatarsToLoad = [] // Contains the user ID
let gamesToLoad = []
let devProductsToLoad = []
let gamePassesToLoad = []
for (let i = 0; i < response["data"].length; i++) {
let data = response["data"][i]//console.log(response["data"][i])
let newItem = item
if (RemoveFree && data["currency"]["amount"] == 0) {
continue;
}
// Date and time:
let DT = data["created"]
let Date = DT.split("T")[0]
Date = Date.substring(5, 7) + "/" + Date.substring(8, 10) + "/" + Date.substring(2, 4)
let Time = DT.split("T")[1].substring(0, 5)
let Hour = Time.substring(0, 2)
let TimeSuffix = Number(Hour) >= 12 ? "PM" : "AM"
Hour = String(Number(Hour))
Time = Hour + ":" + Time.substring(3, 5) + " " + TimeSuffix
newItem = newItem.replaceAll("DATE", Date)
newItem = newItem.replaceAll("TIME", Time)
// User ID and User Name:
let UserID = data["agent"]["id"]
newItem = newItem.replaceAll("USERID", UserID)
newItem = newItem.replaceAll("USERNAME", data["agent"]["name"])
// Place ID and Place Name:
newItem = newItem.replaceAll("PLACEID", data["details"]["place"]["placeId"])
newItem = newItem.replaceAll("PLACENAME", data["details"]["place"]["name"])
// Amount and Pending:
newItem = newItem.replaceAll("AMOUNT", data["currency"]["amount"])
newItem = newItem.replaceAll("ISPENDING", data["isPending"] ? "pending" : "")
newItem = newItem.replaceAll("PENDINGICON", data["isPending"] ? `<span class="tooltip-container"><span class="icon-clock"></span></span>` : "")
// Purchase:
newItem = newItem.replaceAll("PURCHASE", data["details"]["name"])
// AVATARS:
if (!avatarsToLoad.includes(UserID)) {
avatarsToLoad.push(UserID)
}
newItem = newItem.replaceAll("USERAVATAR", "{" + String(UserID) + "}")
// GAME ICONS:
let GameID = data["details"]["universeId"]
let ID = data["details"]["id"]
switch (data["details"]["type"]) {
case ("PrivateServer"):
if (!gamesToLoad.includes(GameID)) {
gamesToLoad.push(GameID)
}
newItem = newItem.replaceAll("PURCHICON", "{" + String(GameID) + "}")
break
case ("GamePass"):
if (!gamePassesToLoad.includes(ID)) {
gamePassesToLoad.push(ID)
}
newItem = newItem.replaceAll("PURCHICON", "{" + String(ID) + "}")
break
case ("DeveloperProduct"):
if (!devProductsToLoad.includes(ID)) {
devProductsToLoad.push(ID)
}
newItem = newItem.replaceAll("PURCHICON", "{" + String(ID) + "}")
break
default:
console.log(data["details"]["type"])
break
}
// Add it to the table:
base += newItem
}
// AVATARS:
function loadImages(type, request, array) {
let Payload = []
for (let i = 0; i < array.length; i++) {
let item = array[i]
Payload.push({
"format": "webp",
"requestId": String(item) + "::" + request + ":150x150:webp:regular",
"size": "150x150",
"targetId": item,
"token": "",
"type": type
})
}
let req = new XMLHttpRequest();
req.withCredentials = true
req.onload = () => {
let resp = JSON.parse(req.response)["data"]
for (let i = 0; i < array.length; i++) {
base = base.replaceAll("{" + String(array[i]) + "}", resp[i]["imageUrl"])
}
table.innerHTML = base
}
req.open("POST", Thumbnails)
req.send(JSON.stringify(Payload))
}
loadImages("AvatarHeadShot", "AvatarHeadshot", avatarsToLoad)
loadImages("DeveloperProduct", "DeveloperProduct", devProductsToLoad)
loadImages("GameIcon", "GameIcon", gamesToLoad)
loadImages("GamePass", "GamePass", gamePassesToLoad)
table.innerHTML = base
}
function waitForTable() {
const table = document.getElementsByClassName("table table-striped transactions")[0]
if (table == null || table.children.length == 0) {
window.setTimeout(waitForTable, 100)
console.log("Looping")
return
}
processTable()
}
waitForTable()
})
req.open("GET", URL)
req.send();
}
Before / After using the code I wrote
Before
After
Currently, it shows the time as UTC time. I’ll probably add some code later on so that it adjusts for the user’s timezone.
If you have any questions, feel free to DM me.
Pings
@BladianMC @TheAlmightyForehead @focasds