Don't show sales of free VIP Servers under group & user transactions

As a Roblox developer, it is currently too hard to look through your revenue sale list under groups and under your user when you have free VIP servers enabled for a game because it takes up too much room.

If Roblox is able to address this issue, it would improve my development experience because then I wouldn’t have to look through pages and pages of free VIP server sales to get to my actual sales.


Obviously this isn’t as much of an issue for me right now since I haven’t enabled it on my games that are alive, the games that players are purchasing free VIP servers on are dead and never reach 5 players. Just imagine how much this would stack up for games on the front page, and especially if there’s no limit to how many VIP servers you can create. I’m sure that if Adopt Me enabled this setting, the developer(s) of that game would never be able to see any actual game pass & product sales.

44 Likes

Free VIP server sales should not be shown in the sales tab, it serves no useful information to developers

15 Likes

Bump this, please apply this to user transactions too Of course it doesn’t make sense because a free product isn’t a good sell.

5 Likes

Why wouldn’t anyone else bump this? This happens to me every day, and it can get annoying pretty quickly…

2 Likes

Bumping this again, surely this is an easy fix by the developers, sales with 0 basically spam the page it’s been 2 years since I’ve last been able to see my sales properly.

2 Likes

Bumping it again, this is actually getting progressively worse.

2 Likes

Bumping too. This is making me go insane

3 Likes

Bumping this yet again, can we get some form of update

2 Likes

Feature like this would be great, or at least filters for the transaction/sales tab, filter based on user, price ranges, dates, etc, instead of a super long list.

1 Like

The option to hide, or better yet create filters for these results without using third-party software to aggregate and search these would be very nice.

One more bump, a response would be nice

I really need a feature for this that removes sales of 0 Robux from the view. Some sort of filtering would be nice, since when I go ahead to see how many players are actively buying (yes I know I can look at the game stats, but I also like to look in here) I am hit in the face with this and I have to scroll endlessly:

Thank you. :slight_smile:

a whole 4 years later and this still happens lol, please get this removed :pray:

1 Like

1+ year anniversary since i’ve bumped this

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?
  • Yes
  • No

0 voters


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 @HttpPeter @focasds

1 Like

I disagree that they should be removed entirely. It would be better to have a filter or a toggle of sorts.

2 Likes

I agree, as well as this, it would also be cool if you could filter by category. E.g boxes that say “Filter by gamepass, shirts, pants, classic clothing” etc.

1 Like