Use Replit to Create Rank Management Functions

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. :slight_smile:


PROGRAMMING LANGUAGES: JAVASCRIPT, LUAU
USAGE OF GITHUB: FALSE
SOFTWARES: REPLIT, ROBLOX STUDIO, UPTIMEROBOT
LEVEL: EASY


| | | image | | | | | | image 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:
image


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.


image


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.


image


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.


image

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! :partying_face:


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.

image

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! :smile:


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!

11 Likes

Also, in case anyone is curious, the other packages will be installed automatically.

1 Like

Interesting tutorial. I’m honestly very excited and curious to see if it works!

I’ve worked with Replit so I’ll be able to give more feedback later.

Thanks!

1 Like

I hope it also works for you! If you encounter any bugs, please let me know and I’ll fix them!

1 Like

Also, on the cookie part, I’m going to put my alt’s cookie just in case.

1 Like

Well, you should NOT use your main accounts cookie.

1 Like

Decent tutorial, however, Replit does have some hosting issues even with UptimeRobot (I have a year of experience in the Replit + UptimeRobot matter). The server may go down every few days and sometimes requires a manual restart.

In addition, you should NEVER advise others to store a cookie on a json file. Use .env files since Replit hides them from the public.

3 Likes

You should absolutely mention that in the tutorial then.

1 Like

Yes, but everything appears to be in order. Since October, my server has been up and running.

Thank you; I’ll take care of it right away.

2 Likes

It depends on a variety of factors, like what cluster your repl is running on, whether you have Hacker plan, etc.

3 Likes

In this case, I had the free plan, so everything appeared to be alright.

1 Like

Thats pretty cool, wow!

Replit is kind of meh in my opinion i barely use it, and sometimes things does not work as expected.
2 Likes

The config.json should be switched in exchange for the repl.it SECRETS, so nobody can just nab your auth key and token and make themselves a high rank

1 Like

Yeah, but it is hard to find the project unless you publish it to the template gallery.

Storing your key in .Json means your allowing people that will fork your code, will see the Json file containing the token, you should probably change it to .env instead. Or as .env is deprecated, use the repl it secrets thing

2 Likes

How do you convert it to a .env or whatever it is @regexman ?

1 Like

You can just rename the file to .env or just go to replit secrets and put the token there

1 Like

you use the ‘secrets’ tab in repl

1 Like

oh didn’t know that. thx for the info

2 Likes