Auto Ranking Bots for Clothing Groups

Introduction

In this article, I am going to explain and teach you how to create an auto-ranking JavaScript bot for clothing groups (Based off of clothes owned in the group)

First Steps

Once you are in studio, go straight to “Game Settings” on the top of the screen, then navigate to “Security”. Here, you are going to want to Allow HTTP Requests. Once this is done, make a new script inside of ServerScriptService and name it whatever you like. For this example, I am going to name it “getCatalog”.

Creating your webserver

Once you have set up your Roblox game, you are going to want to make a webserver. This can be done with any hosting provider which allows PHP files to be hosted. I recommend using 000webhost as it is free and allows PHP hosting. Once you have created an account on 000webhost, you can navigate to the file manager, which can be found on your website list.

Screenshot_119
(Click manage website)

Screenshot_120
(Click file manager)

Once in your file manager, you want to double click on the public_html folder.

Click the new file button and call it “CatalogReceiver.php”
image_2021-03-26_123658

Understanding the Catalog API

Roblox has their own API for their catalog, in which the documentation can be found here:

There you can find all of the parameters for your URL you will be using to get your group’s catalog. For getting all of the clothing in a group’s catalog, you can use this URL:

https://catalog.roblox.com/v1/search/items/details?Category=1&CreatorType=2&CreatorTargetId=7115611&Limit=30&Cursor=

Replace the CreatorTargetId with your group’s ID.

Now that you have your link you will be using for, copy and paste this code into your PHP file on your webserver.

if (isset($_GET['cursor'])) {
    echo file_get_contents('your url' . $_GET['cursor']);
} else {
    echo file_get_contents("your url");
}
?>

You will notice that there is no cursor parameter for the second “file_get_contents” function. The cursor parameter allows you to navigate through the different pages of the groups catalog. For the one without the cursor parameter, delete ‘&Cursor=’ from your URL. It should look like this:

if (isset($_GET['cursor'])) {
    echo file_get_contents('https://catalog.roblox.com/v1/search/items/details?Category=1&CreatorType=2&CreatorTargetId=7115611&Limit=30&Cursor=' . $_GET['cursor']);
} else {
    echo file_get_contents("https://catalog.roblox.com/v1/search/items/details?Category=1&CreatorType=2&CreatorTargetId=7115611&Limit=30");
}
?>

Save and close your PHP file, right click on it, and click View. You should now see a long JSON text on your webpage, meaning you have successfully made your webserver.

Connecting To Your Webserver in Game

In your getCatalog script under ServerScriptService, start off with calling HttpService and MarketplaceService. Then, make a function and connect player added to it. Example below:

local HttpService = game:GetService("HttpService")
local MarketplaceService = game:GetService("MarketplaceService")

game.Players.PlayerAdded:Connect(function(plr)
     
end)

Next, you want to create your variables and a ‘while’ loop to run, so you can go through every page in your catalog.

game.Players.PlayerAdded:Connect(function(plr)
     local cursorAvailable = true
     local cursor = ""
     local assetIds = {}
     while cursorAvailable do
          
     end
end)

Next, you want to connect to your webserver, get the JSON file it is receiving, and turn that into a table on roblox. Replace the url with the url of your PHP file, as seen below.

local url
		if cursor == "" then
			url = "https://**********.000webhostapp.com/CatalogReceiver.php"
		else
			url = "https://**********.000webhostapp.com/CatalogReceiver.php?cursor=" .. cursor
		end
		local response
		response = HttpService:GetAsync(url)
		if not response:match("creatorType") then
			repeat
				response = HttpService:GetAsync(url)
				print("Request Failed. Trying Again in 15 Seconds")
				wait(15)
			until response:match("creatorType")
		end
		local data = HttpService:JSONDecode(response)

You will see a repeat loop in the middle of this code. This checks whether the connection to the catalog API was successful, as Roblox rate limits their API. It then retries until it successfully grabs the catalog.

Next, you want to get all of the asset ids from the table and check if a player owns them. The code for this can be seen below

for i,v in pairs(data.data) do
			wait(math.random(0, 0.2))
			table.insert(assetIds, #assetIds + 1, v.id)
			plr:WaitForChild("PlayerGui"):WaitForChild("mainUi"):WaitForChild("scannedIds").Text = "Scanned Ids: " .. #assetIds
		end
		if data.nextPageCursor then
			cursor = data.nextPageCursor
			cursorAvailable = true
		else
			cursorAvailable = false
		end
		wait(2)
end
local totalOwned = 0
for i,v in pairs(assetIds) do
		local success, playerOwnsAsset = pcall(MarketplaceService.PlayerOwnsAsset, MarketplaceService, plr, v)
		if playerOwnsAsset then
			totalOwned = totalOwned + 1
		end
	end
print("You own " .. totalOwned .. " item(s).")

Your final script should now look like this:

local HttpService = game:GetService("HttpService")
local MarketplaceService = game:GetService("MarketplaceService")

game.Players.PlayerAdded:Connect(function(plr)
	local cursorAvailable = true
	local cursor = ""
	local assetIds = {}
	while cursorAvailable do
		local url
		if cursor == "" then
			url = "https://**********.000webhostapp.com/CatalogReceiver.php"
		else
			url = "https://***********.000webhostapp.com/CatalogReceiver.php?cursor=" .. cursor
		end
		local response
		response = HttpService:GetAsync(url)
		if not response:match("creatorType") then
			repeat
				response = HttpService:GetAsync(url)
				print("Request Failed. Trying Again in 15 Seconds")
				wait(15)
			until response:match("creatorType")
		end
		local data = HttpService:JSONDecode(response)
		for i,v in pairs(data.data) do
			wait(math.random(0, 0.2))
			table.insert(assetIds, #assetIds + 1, v.id)
		end
		if data.nextPageCursor then
			cursor = data.nextPageCursor
			cursorAvailable = true
		else
			cursorAvailable = false
		end
		wait(2)
	end
	local totalOwned = 0
	for i,v in pairs(assetIds) do
		local success, playerOwnsAsset = pcall(MarketplaceService.PlayerOwnsAsset, MarketplaceService, plr, v)
		if playerOwnsAsset then
			totalOwned = totalOwned + 1
		end
	end
print("You own " .. totalOwned .. " item(s).")
end)

Note: You can use the totalOwned variable for anything after checking.

Setting up your auto-ranking bot

This auto-ranking bot will be using noblox.js which is a library that connects to Roblox, allowing you to rank people in your group. For our bot, we will be hosting on glitch.com, which hosts for free. After making an account on glitch, head over to Glitch :・゚✧

Here, you want to click on the name in the top right and select “Remix Project”. This will clone the project and allow you to edit the files. Once you are in the remix, head over to the config.json file. Here, you need to add your Cookie and an Authentication Key.

Getting your cookie

To get your cookie, install the chrome extension “EditThisCookie”, open it on the Roblox website, go to .ROBLOSECURITY, and copy and paste the text. You then can paste that into where the cookie goes in the config.json file. MAKE SURE THAT YOU ARE ON AN ALT ACCOUNT AND HAVE A HIGH RANK IN YOUR GROUP SO YOU CAN CHANGE ROLES.

Next, you can make a random key for security reasons. Put this under “auth_key”, and make sure it is long and complex. (Not something simple such as 12345)

It should look like this
image_2021-03-26_131602

Now, check the logs under tools and see if you are logged into your account.

image_2021-03-26_131753

If it says you are logged in, you are all set! If you aren’t follow these steps again and feel free to ask for help.

Connecting your bot to roblox.

In Roblox, create a ModuleScript called “Server” under your “getCatalog” script. Inside, paste this code:

local groups = {}
local module = {}
local setmetatable = setmetatable
local error = error
local wait = wait
local httpService = game:GetService'HttpService'
local postAsync = httpService.PostAsync
local getAsync = httpService.GetAsync
local jsonEncode = httpService.JSONEncode
local jsonDecode = httpService.JSONDecode
local base, key

local function encode (tab)
  return jsonEncode(httpService, tab)
end

local function decode (tab)
  return jsonDecode(httpService, tab)
end

local function request (url, method, suppress, data)
  local body = method(httpService, url, data)
  local success, response = pcall(decode, body)
  local err
  if success then
    err = response.error
  else
    err = 'Response was not valid json, full body: '..body
  end
  if not err and suppress then
    return true, response
  elseif not err then
    return response
  elseif suppress then
    return false, err
  else
    error(err)
  end
end

local http = {
  post = function (path, data, suppress)
    if not data then
      data = {}
    end
    data.key = key
    return request(base..path, postAsync, suppress, encode(data))
  end,
  get = function (path, suppress)
    return request(base..path, getAsync, suppress)
  end
}

function groups.promote (group, userId)
  return http.post('/promote/'..group..'/'..userId)
end

function groups.demote (group, userId)
  return http.post('/demote/'..group..'/'..userId)
end

function groups.setRank (group, userId, rank)
  return http.post('/setRank/'..group..'/'..userId..'/'..rank)
end

function groups.shout (group, message)
  return http.post('/shout/'..group, {message = message})
end

function groups.post (group, message)
  return http.post('/post/'..group, {message = message})
end

function groups.handleJoinRequest (group, username, accept)
  local acceptString = accept and 'true' or 'false'
  return http.post('/handleJoinRequest/'..group..'/'..username..'/'..acceptString)
end

function groups.getPlayers (group, rank, limit, online)
  local job = http.post('/getPlayers/make/'..group..(rank and '/'..rank or '')..'?limit='..(limit and limit or '-2')..'&online='..(online and 'true' or 'false')).data.uid
  local complete, response = false
  repeat
    wait(0.5)
    local success
    success, response = http.get('/getPlayers/retrieve/'..job, true)
    if not success and response:match('$Response was not valid json') then
      error(response)
    elseif success then
      complete = response.data.complete
    end
  until complete
  return response
end

function module.message (userId, subject, message)
  return http.post('/message/'..userId, {subject = subject, body = message})
end

function module.getBlurb (userId)
  return http.get('/getBlurb/'..userId)
end

return function (domain, newKey, group)
  local isString = (type(domain) == 'string')
  if (not domain) or (not isString) or (isString and #domain <= 0) then
    error('Url is required and must be a string greater than length 0')
  end
  isString = (type(newKey) == 'string')
  if (not newKey) or (not isString) or (isString and #newKey <= 0) then
    error('Key is required and must be a string greater than length 0')
  end

  base = 'http://'..domain
  key = newKey

  if group then
    local isNumber = (type(group) == 'number')
    if (not isNumber) or (group <= 0) then
      error('If group is provided it must be a number greater than 0')
    end

    for name, func in next, groups do
      module[name] = function (...)
        return func(group, ...)
      end
    end
    return module
  end

  for name, func in next, groups do
    module[name] = func
  end
  return module
end

You then want to create a ModuleScript called “Configs” under the “getCatalog” script as well. Inside, paste this code and change it to your config.

return {
	-- NOTE: The final '/' in the URL is very important!
	["BaseUrl"] = ""; -- The base URL of your deployed server. Example: http://test-app.glitch.me/ 
	["AUTH_KEY"] = ""; -- Secret Key defined as 'auth_key' in the config.json file of your server
}

Your explorer should look like this:

image_2021-03-26_132459

Once done creating your new scripts, go back into your getCatalog script. At the bottom of your playerAdded function, add these variables.

local Server = require(script.Server)
local domain = "https://tin-chartreuse-motorcycle.glitch.me/"
local key = ""
local GroupId = *Your group id*

Change the key to your authentication key, and replace the domain with your live app url from glitch. You can get this by clicking share and then copying the “live site” link on glitch. Then, change the GroupId variable to your group id.

image_2021-03-26_132817

Under these variables, you can make your conditional statements to decide which rank you want to rank people. These format can be found here:

if totalOwned > 5 and totalOwned < 25 then
     local roleNumber = ""
     Server.SetRank(GroupId, plr.UserId, roleNumber)
end

Replace roleNumber with the role you want to promote your group member to. This should send a request to your noblox server and rank the player.

Conclusions

Thats all for this project, if you have any questions or concerns feel free to ask below. If I messed up, let me know! I’d be happy to fix it.

17 Likes

Thats pretty cool, I didnt use it just watched it :wink:

1 Like

Trying this out and for some reason I get a log error on glitch. Not sure why, I did everything and made sure everything was right. Not sure why it keeps saying error :frowning:. Is there any fix for this?

Well, Glitch changed their ToS so you can not host free on their platform anymore.

1 Like

Use replit.com its free just make sure to hide all important info

Oh sorry I wasn’t aware of any changes, thank you for informing me!

1 Like

Thank you, I will try this out.

Well, repl.it is indeed free but you can not use it for free hosting alone. You will need and uptime robot if I am correct.

I would not recommend using a free hosting as your roblox cookie can be leaked very easily. I would recommend purchasing your own VPS such as https://www.digitalocean.com/ in order to host this properly.

2 Likes

True
Like I said with replit.com you’ll need to hide the cookie via env’s

1 Like

ur fine using free hosts as long as you use envs