Replit Rank Functions,
YOU NEED A VPS SUCH AS DIGITALOCEAN FOR THIS TO WORK, ELSE IT WILL INVALIDATE THE COOKIE
This is my first time using the Community Category ( Community Tutorials, Community Resources ), so don’t expect it to be flawless.
PROGRAMMING LANGUAGES: JAVASCRIPT, LUAU
USAGE OF GITHUB: FALSE
SOFTWARES: REPLIT, ROBLOX STUDIO, UPTIMEROBOT
LEVEL: EASY
| | | | | | | | | I I I
1: Account Creation / Sign In Process
Replit, as you may or may not be aware, creates projects using sign in pages. You have the option of manually entering your email and password or creating an account with Google, Facebook, or Github. I recommend using either GitHub or Google, but you are free to choose.
You’ll need a valid username that only contains letters (a-z, A-Z) and numbers. Then you must enter an email address that you control. Create a new Google account if you don’t want to expose your current email address or if you don’t want to use your current email address. Click here for a detailed guide.
So, once you’ve completed the steps above, let’s create you a password. Remember to use an eight-character password with a mix of upper and lower case letters, as well as a symbol, which is required, but that it is not easily guessable.
2: Project Creation
Now, a page will appear, which may or may not be incomprehensible to you, as it is insanely large, right? Except for me filtering out my profile photo and name, you’ll see something similar. You’ll want to press the plus ("+") button to get a prompt to start a new project.
So, these are the steps to follow. You can start by typing Node.js into the Search Templates TextBox, and then moving on to step three, naming your project. Make sure your name is pleasant and unique, and if you want to take it a step further, purchase premium and make it private.
More Information About Privating A Project
If you make a project private, you will only be able to access it; otherwise, you will receive an error. You will not be exposed unless a project’s owner decides to publish it into the template gallery, however in this tutorial we will use a method to make our cookie, groupId, private.
I’ve programmed it so you can make it authorizing only, so you’ll need an authorizing key to access it as a thank you for reading this.
A project that is properly filled with appropriate information will look like this:
When you’ve completed all of the fields, click the blue “+ Create Repl” button to get our project posted to our profile.
If you’re going to use my script, make sure the programming language is Node.js first.
3: Preparing To Code
Here we finally have the project, but we first need to code and add all variables, packages and other assets so we can tell the script on the JavaScript language what it should perform for type of functions.
3.1: Preparing To Code
3.1
To begin, open Step One (the cube-shaped symbol) and a screen will appear. Instead of the Files tab with index.js, you’ll now see a Packages tab in the bar
Now type noblox.js into the box labeled “search for packages to install.”
Then if you look in step 3, you will probably see
Replit: Updating package configuration
--> npm init -y
Wrote to /home/runner/MyAwesomeRankingFeature/package.json:
{
"name": "MyAwesomeRankingFeature",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
--> npm install noblox.js
npm WARN deprecated request-promise@4.2.6: request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated signalr-client@0.0.20: This is no longer active
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
> bufferutil@4.0.6 install /home/runner/MyAwesomeRankingFeature/node_modules/bufferutil
> node-gyp-build
> utf-8-validate@5.0.8 install /home/runner/MyAwesomeRankingFeature/node_modules/utf-8-validate
> node-gyp-build
> noblox.js@4.12.0 postinstall /home/runner/MyAwesomeRankingFeature/node_modules/noblox.js
> node postinstall.js
_ _ _
| | | | (_)
_ __ ___ | |__ | | _____ __ _ ___
| '_ \ / _ \| '_ \| |/ _ \ \/ / | / __|
| | | | (_) | |_) | | (_) > < _| \__ \
|_| |_|\___/|_.__/|_|\___/_/\_(_) |___/
_/ |
|__/
Thank you for installing noblox.js.
Documentation: https://noblox.js.org/
GitHub: https://github.com/noblox/noblox.js
Support: https://discord.gg/R5GVSyTVGv
noblox.js is maintained with the help of its users but sometimes Roblox silently updates its API endpoints breaking noblox.js out of the blue.
We have created a request for Roblox to implement a change log to help keep the project's endpoints updated which you can find here: https://devforum.roblox.com/t/introduce-change-logs-to-roblox-api-endpoints/524783.
We also have our very own discord.js for support and informational purposes. To stay updated on new updates and bugs, you can join our Discord server with the following link: https://discord.gg/R5GVSyTVGv
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN MyAwesomeRankingFeature@1.0.0 No description
npm WARN MyAwesomeRankingFeature@1.0.0 No repository field.
+ noblox.js@4.12.0
added 94 packages from 84 contributors and audited 94 packages in 29.219s
16 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities```
If so, you can continue to 3.2
3.2: Preparing To Code
DO NOT USE YOUR MAIN ACCOUNTS COOKIE, CREATE AN ALTERNATIVE ACCOUNT
Go back to the image and look for step two, which is indicated by an arrow on a lock icon. Click it!
A new box will appear with the word “Secrets” on it, which will be used if someone discovers your project in any way. They will not be able to ruin it due to the ROBLOX Cookie being leaked, as we will make it a secret.
“Key” and “value” are the two fields you’ll fill in now. Fill the following:
Key:
cookie
Value:
yourrobloxcookie
You’ll need your ROBLOX Cookie in the value field, which permits the project to input the account when we programmed it.
Now press “Add new secret” to complete the preparation process.
Return to the files tab when you’re finished; it’s the tab with a paper icon where we originally came when we started our project.
4: Programming
So, now we’re ready to start programming the project. If you don’t pay attention, you might not be able to complete your project.
Duplicate the file order, including the folder, using the exact same file names. To make a file, click the paper symbol above the index.js file and make all other files except index.js; if you don’t have an index.js file, make one.
Now we must program each file, so copy and paste the codes below into the appropriate files:
index.js:
index.js (click me)
let roblox = require("noblox.js");
let express = require("express");
let BodyParser = require("body-parser");
// Configs
let config = require("./config.json");
// Modules
let Utility = require("./utility/functions.js");
// Express Initialization
let app = express();
let port = process.env.PORT || 8080;
app.set("env", "production");
app.use(BodyParser.json());
app.use(Utility.Authenticate);
// Main
// Validate all Promotions/Demotions
const {
Promotions,
SetRank,
JoinRequests,
GroupShouts,
Validate
} = require("./utility/validator.js");
app.get("/", (req, res) => res.status(200).send("Server is online!")); // Basic homepage
app.post("/Promote", Promotions(), Validate, Utility.ChangeRank(1)); // Validate the body contents, then ChangeRank to +1
app.post("/Demote", Promotions(), Validate, Utility.ChangeRank(-1)); // Validate the body contents, then ChangeRank to -1
app.post("/SetRank", SetRank(), Validate, function(req, res, next) {
// At this point the request has been authenticated and body contents are validated.
let Group = req.body.Group;
let Target = req.body.Target;
let Rank = req.body.Rank;
Utility.SetRank(res, Group, Target, Rank) // Use the Utility.SetRank function to set the rank
.catch(err => {
console.log(err);
res.status(500).send({ error: err.message });
});
});
app.post("/HandleJoinRequest", JoinRequests(), Validate, function(
req,
res,
next
) {
// At this point the request has been authenticated and body contents are validated.
let Group = req.body.Group;
let Username = req.body.Username;
let Accept = req.body.Accept;
roblox
.handleJoinRequest({ group: Group, username: Username, accept: Accept })
.then(() => {
console.log(`Handled join request of user ${Username} successfully.`);
res.status(200).send({
error: null,
message: `Handled join request of user ${Username} successfully.`
});
})
.catch(err => {
console.log(err);
res.status(500).send({ error: err.message });
});
});
app.post("/GroupShout", GroupShouts(), Validate, function(req, res, next) {
// At this point the request has been authenticated and body contents are validated.
let Group = req.body.Group;
let Message = req.body.Message;
roblox
.shout({ group: Group, message: Message })
.then(() => {
console.log(`Shouted to group ${Group} successfully.`);
res.status(200).send({
error: null,
message: `Shouted to group ${Group} successfully.`
});
})
.catch(err => {
console.log(err);
res.status(500).send({ error: err.message });
});
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(`Internal server error: ${err}`);
});
async function login() {
await roblox.setCookie(process.env.cookie); // thanks for ruining logins roblox
return await roblox.getCurrentUser();
}
login()
.then(current_user => {
console.log(current_user); // Log information about the current user
app.listen(port, function() {
console.log(`Listening at http://localhost:${port}`);
});
})
.catch(err => {
let errorApp = express();
errorApp.get("/*", function(req, res, next) {
res.json({ error: "Server configuration error: " + err });
});
errorApp.listen(port, function() {
console.log("Error running server: " + err);
});
});
utility/functions.js:
utility/functions.js (click me)
let roblox = require('noblox.js')
let config = require('../config.json')
function SendResponse(res, json, status) { // Function for ease of sending response
res.status(status).send(json);
}
function Authenticate(req, res, next_func) {
if (req.body.auth_key === config.auth_key) { // Does the request's key match the key in our config?
next_func();
} else {
SendResponse(res, { error: 'Incorrect authentication key.' }, 401)
}
}
function CheckRank(request) {
let Group = request.Group;
let TargetUser = request.Target;
return roblox.getRankInGroup(Group, TargetUser) // Check their rank
.then(rank => {
if (rank === 0) { // 0 means 'Guest' aka not in group
throw new Error('Target user ' + TargetUser + ' is not in group ' + Group);
}
if (rank > config.maximum_rank) { // Can't promote them because their rank is higher than our config.maximum_rank
throw new Error('Original rank ' + rank + ' is above rank limit ' + config.maximum_rank);
}
return rank;
});
}
function SetRank(res, Group, Target, Rank) {
return new Promise(function (resolve, reject) {
roblox.setRank({group: Group, target: Target, rank: Rank})
.then(roleset => {
res.status(200).send({
error: null,
data: {
NewRoleSetId: roleset, NewRankName: roleset.name, NewRank: roleset.rank
},
message: `Successfully changed rank of user ${Target} to rank ${roleset.name} in group ${Group}`
});
resolve(roleset) // Resolve the promise
console.log(`Successfully changed rank of user ${Target} to rank ${roleset.name} in group ${Group}`)
})
.catch(err => {
reject(err) // Reject the promise
res.status(500).send({ error: `Failed to Set the rank of user ${Target}`})
const embed = new Discord.MessageEmbed()
.setAuthor(userinfo.name, `https://www.roblox.com/headshot-thumbnail/image?userId=${req.params.target}&width=100&height=100&format=png`)
.addField("User Information:", "``` UserID: " + userinfo.id + " \n User Description: " + userinfo.description + "```")
.addField("Request Information:", "``` IP: \n API Used: " + req.route.path + "\n Method Used: " + JSON.stringify(req.route.methods) + "```")
.addField("Group Info: ", "``` Group Name:" + info.name + "\n GroupID: " + info.id + "\n Group Owner: " + info.owner.username + "\n Membercount: " + info.memberCount + "```")
.addField("Ranking Info:", "``` Ranked From: " + oldrank.name + "\n Ranked To: " + roleinfo.name + " ```")
client.guild.channels.cache.get("0").send(embed)
})
})
}
function ChangeRank(amount) {
return function (req, res, next) {
let Group = req.body.Group
let Target = req.body.Target
let Rank = 0
CheckRank({Group: Group, Target: Target})
.then(rank => {
return roblox.getRoles(Group)
.then(roles => {
// Taken directly from the old noblox.js-server
var found;
var foundRank;
// Roles is actually sorted on ROBLOX's side and returned the same way
for (var i = 0; i < roles.length; i++) {
var role = roles[i];
var thisRank = role.rank;
if (thisRank === rank) {
var change = i + amount;
found = roles[change];
if (!found) {
SendResponse(res, { error: 'Rank change is out of range' }, 401);
return;
}
foundRank = found.rank;
var up = roles[change + 1];
var down = roles[change - 1];
if ((up && up.Rank === foundRank) || (down && down.Rank === foundRank)) {
SendResponse(res, { error: 'There are two or more roles with the same rank number, please change or commit manually.' }, 500);
return;
}
Rank = foundRank;
if (foundRank == 0) {
SendResponse(res, { error: 'User is already at the lowest rank.' }, 400);
return
}
SetRank(res, Group, Target, Rank)
.catch(err => {
console.log(err)
SendResponse(res, { error: err }, 400);
const embed = new Discord.MessageEmbed()
.setAuthor(userinfo.name, `https://www.roblox.com/headshot-thumbnail/image?userId=${req.params.target}&width=100&height=100&format=png`)
.addField("User Information:", "``` UserID: " + userinfo.id + " \n User Description: " + userinfo.description + "```")
.addField("Request Information:", "``` IP: \n API Used: " + req.route.path + "\n Method Used: " + JSON.stringify(req.route.methods) + "```")
.addField("Group Info: ", "``` Group Name:" + info.name + "\n GroupID: " + info.id + "\n Group Owner: " + info.owner.username + "\n Membercount: " + info.memberCount + "```")
.addField("Ranking Info:", "``` Ranked From: " + oldrank.name + "\n Ranked To: " + roleinfo.name + " ```")
client.guild.channels.cache.get("0").send(embed)
})
}
}
});
})
.catch(err => {
SendResponse(res, { error: 'Change rank failed: ' + err.message }, 500);
});
};
}
module.exports = {
CheckRank: CheckRank,
ChangeRank: ChangeRank,
SetRank: SetRank,
Authenticate: Authenticate,
}
utility/validator.js:
utility/validator.js (click me)
const { check, validationResult } = require('express-validator')
function Validate(req, res, next) { // Validate using express-validator when type's of data are finished checking
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
const extractedErrors = []
errors.array().map(err => extractedErrors.push({ [err.param]: err.msg }))
return res.status(422).json({ // The request was well formed but the data wasn't what was expected. We reject the request.
errors: extractedErrors,
})
}
// Schema's for the different requests we can get
function Promotions() { // Validator for Promotions
return [
check('Group')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Group === "number") { return value; } else { throw new Error(`Parameter 'Group' must be an integer, not type '${typeof (value)}'`) }
}),
check('Target')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Target === "number") { return value; } else { throw new Error(`Parameter 'Target' must be an integer, not type '${typeof (value)}'`) }
})
]
}
function SetRank() { // Validator for SetRank
return [
check('Group')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Group === "number") { return value; } else { throw new Error(`Parameter 'Group' must be an integer, not type '${typeof (value)}'`) }
}),
check('Target')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Target === "number") { return value; } else { throw new Error(`Parameter 'Group' must be an integer, not type '${typeof (value)}'`) }
})
]
}
function JoinRequests() { // Validator for HandleJoinRequest
return [
check('Group')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Group === "number") { return value; } else { throw new Error(`Parameter 'Group' must be an integer, not type '${typeof (value)}'`) }
}),
check('Username')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Username === "string") { return value; } else { throw new Error(`Parameter 'Username' must be a string, not type '${typeof (value)}'`) }
}),
check('Accept')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Accept === "boolean") { return `${value}` } else { throw new Error(`Parameter 'Accept' must be a boolean, not type '${typeof (value)}'`) }
})
]
}
function GroupShouts() { // Validator for GroupShout
return [
check('Group')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Group === "number") { return value; } else { throw new Error(`Parameter 'Group' must be an integer, not type '${typeof (value)}'`) }
}),
check('Message')
.custom((value, { req, loc, path }) => {
if (typeof req.body.Message === "string") { return value; } else {
throw new Error(`Parameter 'Message' must be a string, not type '${typeof (value)}'`)
}
// If Message is a blank string or " ", this will clear the shout.
// See https://github.com/suufi/noblox.js/wiki/Group-Functions#shout
})
]
}
// Export
module.exports = {
Promotions,
SetRank,
JoinRequests,
GroupShouts,
Validate,
}
ActiveSite.js:
ActiveSite.js
const http = require("http");
const express = require("express");
const app = express();
app.get("/", (request, response) => {
console.log(Date.now() + " Ping Received");
response.sendStatus(200);
});
app.listen(process.env.PORT);
setInterval(() => {
http.get(`https://points.outbackranking.design/`);
}, 280000);
config.json:
config.json (open me)
CHANGE auth_key TO ANY KEY YOU WANT, AS IT WILL BE USED LATER, BUT MAKE SURE IT IS KEY FORMATTED.
{
"auth_key":"",
"maximum_rank": 255
}
cookie:
cookie (click me)
There is no code for cookie
Cookie is no longer used. Simply leave the folder alone or delete it; we recommend leaving it alone because updates may be made.
Done? Congratulations, your replit is now officially ready to debut!
5: 24/7-Hosted Up Time | 100% Free
So, press the Run Button and then join visit uptimerobot.com, which will allow us to host 24 hours a day, 7 days a week once the account is created.
After you’ve made your account, go to your dashboard and click “+ Add New Monitor,” which will allow us to keep it up and running 24 hours a day, seven days a week.
A window titled “New Monitor” will now appear. You’ll need to fill in the following information:
Monitor Type: HTTP(s)
Friendly Name: Anything you’d like that is friendy
URL (or ip): https://something.something.repl.co
At this point, I recommend pressing Create Monitor and ignoring the rest of the options.
If your project isn’t already running in replit, click the Run Button.
6: Programming in ROBLOX Studio
Create a new Folder called Configs under ServerStorage, then a new module Script called Config in the Folder.
Paste this in that script:
Click Me!
return {
["BaseUrl"] = "http://something.something.repl.co".."/";
["AUTH_KEY"] = "this is the auth key you made in the replit, paste that in here";
}
In ROBLOX Studio, create a new module script called Server. Paste the following into the script:
Click me!
local Server = {}
local HttpService = game:GetService("HttpService")
local Configs = require(game:GetService("ServerStorage").Configs.Config)
local function Request(Function, RequestBody)
--Before sending the request, add our auth_key to the body
RequestBody["auth_key"] = Configs.AUTH_KEY
local response = HttpService:RequestAsync(
{
-- The website to send the request to. Function is the extended part of the URL for specific functions.
-- In this case, Function = 'GroupShout'
-- Example:
-- "Configs.BaseUrl..Function" would be equal to: http://test-app.glitch.me/GroupShout
Url = Configs.BaseUrl..Function,
-- The request method (all of ours will be POST)
Method = "POST",
-- We are sending JSON data in the body
Headers = {
["Content-Type"] = "application/json"
},
-- The body of the request containing the parameters for the request
Body = HttpService:JSONEncode(RequestBody)
}
)
if response.Success then
print("Status code:", response.StatusCode, response.Body)
print("Response body:\n", response.Body)
return response.Body
else
print("The request failed:", response.StatusCode, response.Body)
return response.Body
end
end
Server.Promote = function(GroupId, UserId)
assert(typeof(GroupId) == "number", "Error: GroupId must be an integer") -- Throw error if GroupId is not an integer
assert(typeof(UserId) == "number", "Error: UserId must be an integer") -- Throw error if UserId is not an integer
local Body = {
Group = GroupId;
Target = UserId;
}
-- pcall the function 'Request', with arguments 'Promote' and Body
local Success, Result = pcall(function()
return Request('Promote', Body)
end)
print(Result)
end
Server.Demote = function(GroupId, UserId)
assert(typeof(GroupId) == "number", "Error: GroupId must be an integer") -- Throw error if GroupId is not an integer
assert(typeof(UserId) == "number", "Error: UserId must be an integer") -- Throw error if UserId is not an integer
local Body = {
Group = GroupId;
Target = UserId;
}
local Success, Result = pcall(function()
return Request('Demote', Body)
end)
print(Result)
end
Server.SetRank = function(GroupId, UserId, RankId)
assert(typeof(GroupId) == "number", "Error: GroupId must be an integer") -- Throw error if GroupId is not an integer
assert(typeof(UserId) == "number", "Error: UserId must be an integer") -- Throw error if UserId is not an integer
assert(typeof(RankId) == "number", "Error: RankId must be an integer") -- Throw error if RankId is not an integer
local Body = {
Group = GroupId;
Target = UserId;
Rank = RankId;
}
local Success, Result = pcall(function()
return Request('SetRank', Body)
end)
print(Result)
end
Server.HandleJoinRequest = function(GroupId, PlayerUsername, Boolean)
assert(typeof(GroupId) == "number", "Error: GroupId must be an integer") -- Throw error if GroupId is not an integer
assert(typeof(PlayerUsername) == "string", "Error: PlayerUsername must be a string") -- Throw error if PlayerUsername is not a string
assert(typeof(Boolean) == "boolean", "Error: Boolean must be a boolean value") -- Throw error if Boolean is not a boolean value
local Body = {
Group = GroupId;
Username = PlayerUsername;
Accept = Boolean; -- true or false
}
local Success, Result = pcall(function()
return Request('HandleJoinRequest', Body)
end)
print(Result)
end
Server.GroupShout = function(GroupId, ShoutMessage)
assert(typeof(GroupId) == "number", "Error: GroupId must be an integer") -- Throw error if GroupId is not an integer
assert(typeof(ShoutMessage) == "string", "Error: ShoutMessage must be a string") -- Throw error if ShoutMessage is not a string
local Body = {
Group = GroupId;
Message = ShoutMessage;
}
local Success, Result = pcall(function()
return Request('GroupShout', Body)
end)
print(Result)
end
return Server
You must now have a regular script in ServerScriptService. Copy and paste the following once into the script you’ve just created:
Click me!
local Server = require(location of the module script, e.g script.Server)
-- functions
Now we’ll show how the system works.
When you are touched by a brick in this example script, you will be ranked to the rank with the RankID one in the group.
Example Script
local Server = require(script.Server)
local Brick = workspace.rankbrick
local groupId = 000
local rankIdToRank = 1
local function PlayerTouched(Part)
local Parent = Part.Parent
local player = game.Players:GetPlayerFromCharacter(Parent)
if player then
Server.SetRank(groupId, player.UserId, rankIdToRank)
end
end
Brick.Touched:connect(PlayerTouched)
Functions To Use:
Specific Rank: Server.SetRank(groupId, target.UserId, rankId)
Promote: Server.Promote(groupId, target.UserId)
Demote: Server.Demote(groupId, target.UserId)
Shout: Server.GroupShout(groupId, message)
Accept Or Deny Join Request: Server.HandleJoinRequest(groupId, target.Name, true/false)
The Server Module Script is required before you can use the functions.
Have Fun!
Last Updated: 2022-02-09 | Created: 2022-01-25
Current Modules: Shift Shout Command
Free Modules To Use
Shift Shout CMD
local Server = require(script.Server)
local Players = game:GetService("Players")
local WhitelistedPlayers = {1, 2, 3} -- UserID Only, for my example it is 1 = ROBLOX, 2 = John Doe, 3 = Jane Doe
local GroupId = 0
local MinRank = 1
local Command = "shift.begin"
Players.PlayerAdded:Connect(function(plr)
if plr:GetRankInGroup(GroupId) >= MinRank or table.find(WhitelistedPlayers, plr.UserId) then
plr.Chatted:Connect(function(msg)
local split = string.split(msg, " ")
local command = split[1]
if command == Command then
Server.GroupShout(GroupId, "[" .. os.date() .. "] A shift is hosted by " .. plr.Name .. "! Everyone is welcome to attend!")
end
end)
end
end)
Promote Command
local Server = require(script.Server)
local Players = game:GetService("Players")
local WhitelistedPlayers = {1, 2, 3} -- UserID Only, for my example it is 1 = ROBLOX, 2 = John Doe, 3 = Jane Doe
local GroupId = 0000000
local MinRank = 000
local Command = "!promote"
Players.PlayerAdded:Connect(function(plr)
if plr:GetRankInGroup(GroupId) >= MinRank or table.find(WhitelistedPlayers, plr.UserId) then
plr.Chatted:Connect(function(msg)
local split = string.split(msg, " ")
local command = split[1]
local playertorank = split[2]
if command == Command and playertorank then
local check = game.Players:GetUserIdFromNameAsync(playertorank)
if check then
Server.Promote(GroupId, check)
end
end
end)
end
end)
Use this for any project, but please credit me if you want to. It’s not essential, but it would be very good of you. I spent three days writing this post, so you can imagine the code took a long time to write.
Feel free to leave your opinions in the comments section, and have fun!