Socket
Socket
Sign inDemoInstall

noblox.js-server

Package Overview
Dependencies
Maintainers
0
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

noblox.js-server - npm Package Compare versions

Comparing version 1.3.1 to 2.0.1

.env.sample

36

package.json
{
"name": "noblox.js-server",
"version": "1.3.1",
"description": "An example server that uses the noblox.js module.",
"version": "2.0.1",
"description": "A RESTful API for using noblox.js",
"main": "server.js",
"author": "suufi <suufi@mit.edu>",
"license": "MIT",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "standard .",
"start": "node server.js"
},
"repository": {
"type": "git",
"url": "https://github.com/suufi/noblox.js-server.git"
},
"author": "Suufi <sd.atrillion@gmail.com>",
"license": "MIT",
"dependencies": {
"bluebird": "^3.4.6",
"body-parser": "^1.15.2",
"express": "^4.14.0",
"noblox.js": "^4.1.2",
"validator": "^6.1.0"
"@hapi/boom": "^10.0.1",
"@koa/router": "^13.0.0",
"dotenv": "^16.4.5",
"koa": "^2.13.1",
"koa-404-handler": "^0.1.0",
"koa-better-error-handler": "^11.0.4",
"koa-bodyparser": "^4.3.0",
"koa-joi-router": "^8.0.0",
"noblox.js": "^6.0.2"
},
"devDependencies": {
"standard": "^17.1.0"
},
"bugs": {
"url": "https://github.com/suufi/noblox.js-server/issues"
"url": "https://github.com/noblox/noblox.js/issues"
},
"homepage": "https://github.com/suufi/noblox.js-server"
"homepage": "https://github.com/noblox/noblox.js-server",
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
}

@@ -1,201 +0,57 @@

# noblox.js-server
<h1 align="center">
<img src="https://raw.githubusercontent.com/noblox/noblox.js-server/master/img/noblox.js-server-logo.png" alt="noblox.js-server" width="400"/>
<br>
</h1>
[![ROBLOX API Discord](https://img.shields.io/badge/discord-roblox%20api%20chat-blue.svg?style=flat-square)](https://discord.gg/EDXNdAT)
<h4 align="center">A RESTful API using <a href="https://github.com/suufi/noblox.js">noblox.js</a> and <a href="https://koajs.com/">Koa</a>.</h4>
This is a primitive example server that uses my [noblox.js](https://github.com/suufi/noblox.js) library, allowing users to execute site actions from in-game via HttpService.
<p align="center">
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-blue.svg?style=flat-square" alt="JavaScript Style Guide"/></a>
<a href="https://discord.gg/R5GVSyTVGv"><img src="https://img.shields.io/badge/discord-noblox.js-blue.svg?style=flat-square" alt="noblox.js Discord"/></a>
<a href="https://travis-ci.org/noblox/noblox.js-server"><img src="https://img.shields.io/travis/noblox/noblox.js-server/master.svg?style=flat-square" alt="Travis Build Status"/></a></a>
</p>
## Instructions
<p align="center">
<a href="#about">About</a> •
<a href="#prerequisites">Prerequisites</a> •
<a href="#configuration">Configuration</a> •
<a href="https://github.com/suufi/noblox.js/tree/master/examples">Examples</a> •
<a href="https://www.youtube.com/playlist?list=PLEW4K4VqMUb_VMA3Yp9LI4gReRyVWGTnU">YouTube Series</a> •
<a href="#credits">Credits</a> •
<a href="#license">License</a>
</p>
Go to settings.json and set `cookie` to the .ROBLOSECURITY cookie of the ROBLOX account you want to use. The `key` field is essentially a password for the site (to prevent strangers from accessing account functions). There is also an optional setting `maximumRank` which can be used to prevent attacks. User's above this rank are immune from having their rank changed and attempts to change a user's rank to something above this will be rejected. I recommend generating a random string or just smashing your keyboard since this will typically be accessed by another script that doesn't have to memorize said key.
## About
This repository hosts the code for a working [RESTful API](https://restfulapi.net/) that utilizes Koa.js to expose [noblox.js](https://github.com/suufi/noblox.js) functions on the internet. Essentially, with this project, you can host it on your own server and interact with the Roblox API through your own Roblox game.
## Free Host Tutorial
1. Go to [heroku.com](https://heroku.com/) and sign up for an account.
2. Install [heroku command line tools](https://devcenter.heroku.com/articles/heroku-command-line#download-and-install).
3. [Download](https://github.com/sentanos/roblox-js-server/archive/master.zip) this repository and unzip.
4. Open the settings.json file and fill in the fields "username", "password", and "key" in the quotes after each.
5. Open a terminal or command prompt and type `cd `, then drag the folder into the window and release. It should fill in the path name afterwards. Hit enter.
6. Type in `heroku login` and type in the username and password of the account you made in step 1.
7. Type in `git init` [Enter] followed by `git add --all` [Enter] and then `git commit -am "Initial"` [Enter]
8. Type in `heroku create` [Enter]; you can put in a custom name after the command as well. eg. `heroku create roblox-js-server`
9. Finally type `git push heroku master` [Enter] and let it go through. If all goes well it will deploy after a minute or two and will tell you the url of your server around the end of the process.
## Prerequisites
## Updating
To update the server files on heroku (esp. server.js):
- [**node.js**](https://nodejs.org/en/download/current/)
- a virtual private server (VPS)
- To have your code running on a 24/7 basis, you need to use a VPS. We recommend using DigitalOcean for its ease of use and. [This referral link](https://m.do.co/c/14822e4e2d63) provides you with a $100 credit which can be used over 60 days. Other options include Amazon Web Services, Microsoft Azure, and Google Compute Engine.
## Configuration
### server.js
After installing this repository on your server, start by creating an `.env` file to house your configuration settings. You can duplicate the `.env.sample` file and fill in the missing details.
- Unless you know what you are doing, leave the `PORT` number the same.
- `MAX_RANK` refers to the highest rank (1-254) the logged in account is allowed to promote users to.
- `API_TOKEN` refers to a secret key to secure your RESTful API to avoid your API being accessed by unauthorized users. It is best to generate a key that isn't easy to guess. You can use [this](https://randomkeygen.com/) website to use an automatically generated key. You need not memorize this key.
- `COOKIE` refers to the cookie of the logged-in user account the API will execute functions from. To find your cookie, please read [this](https://github.com/suufi/noblox.js#getting-your-cookie-chrome).
1. Go to your original roblox-js-server folder and delete all files EXCEPT settings.json (unless you want to reenter info)
2. [Redownload](https://github.com/sentanos/roblox-js-server/archive/master.zip) the repository and unzip
3. Drag all the files in the new folder you downloaded into the old one EXCEPT for settings.json
4. Open a terminal or command prompt and type `cd `, then drag the folder into the window and release. Hit enter.
5. Type in `git add --all` [Enter]
6. Type in `git commit -am "Update"` [Enter]
7. Type in `git push heroku master` [Enter] and let it run.
After your file is configured, use a process manager like [pm2](https://pm2.keymetrics.io/) to have your script run 24/7. We do not provide support for VPS, network, and domain configuration.
Sometimes you also have to update dependency files like roblox-js which the module requires:
1. Open a terminal or command prompt and type `cd `, then drag the folder into the window and release. Hit enter.
2. Type in `heroku config:set NODE_MODULES_CACHE=false` [Enter]
3. Type in `git commit --allow-empty -m "Rebuild"` [Enter]
4. Type in `git push heroku master` [Enter] and let it run
5. Type in `heroku config:unset NODE_MODULES_CACHE` [Enter]
### noblox.lua
If you plan on using the provided Lua module (ModuleScript) in this project, please do the following:
- Place the script only in `ServerScriptService`.
- Update the `DOMAIN` value in `CONFIGURATION` to reflect your server's IP address/domain & port. (e.g. if your domain name is noblox.io and this is running on port 3000, your value here would be `https://noblox.io:3000`)
- Update the `API_TOKEN` value in `CONFIGURATION` so that it matches what you put earlier in `server.js`.
- Optional: provide a `DEFAULT_GROUP_ID` to default to having noblox.js functions run on a single group when not specified.
## Lua Example
## Credits
A module script is available in [lua/server.mod.lua](./lua/server.mod.lua) that allows you to use functions to send commands to the server. An initializer function is returned by the module and requires the arguments `domain` and `key`. If the third `group` argument is provided, the returned table will automatically call group-related functions with that groupId, otherwise it has to be the first argument.
* [suufi](https://github.com/suufi) - Lead maintainer
The commands `promote`, `demote`, `setRank`, `shout`, `post`, `handleJoinRequest`, `forumPostNew`, `forumPostReply`, and `message` are available, all arguments are in the same order as they are in the documentation, with parameters first and then each body argument in order (excluding key). The return value of the function is the decoded table that the API returns.
## License
Example usage, assuming ModuleScript is named "Server" and is in the same directory as the script (eg. both ServerScriptService):
```lua
local server = require(script.Parent.Server)
local domain = 'rbx-js.herokuapp.com' -- Make sure there is no http:// in here!
local key = '/UAO9lTOYapr8ecV8cs/t3cP9c7na6rKHfRn7M6GDct+PdJyQJ40Jebe+CKZDgKV8TRLtbBqfhJc/eHNC7RHA8BCKkrFOkaIKC9/ripy34QzLq3m2qqy/GdyCg/5KHFUPbsuRNetr52ZP+6E2puKWrR9XvuAMG9bq+X02luwmID6aU7YBpq7sALl21Pv0OB4wy43VhuI3esN8w/Rl0ZC3LiJWwMv8PnwCKqgmq9L9UXLVBEPNJ9Plcv73+QqArHqiZ/qtrJO88='
local groupId = 18
local userId = game:GetService'Players':GetUserIdFromNameAsync'Shedletsky'
local api = server(domain, key, groupId)
print(api.promote(userId).message)
print(api.message(userId, 'Subject', 'Body').message)
print(api.getBlurb(1).data.blurb)
```
## Documentation
### METHOD /example/{argument1: type}/{argument2: type}/[optional argument: type]?[optional_argument: type]&[optional_argument2: type]
```http
Example usage
```
{argument3: type}
Description
Post data is under the header, in this example is starts with argument 3. This should be sent in json format or it will not be read correctly.
All functions respond with a json table containing the fields `error`, `message`, and `data`. The `error` field is always visible but may be null, the `message` field contains a human-readable message summarizing the pages action, and `data` contains response data from the API is applicable.
[response1: type, reponse2: type]
### POST /demote/{group: number}/{target: number}
```http
/demote/18/2470023
{"key": "hunter2"}
```
{key: string}
Sets the role of the player to the adjacent lower-rank role.
[newRankName: string, newRank: number, newRoleSetId: number]
### GET /getBlurb/{userId: number}
```http
/getBlurb/2470023
```
Gets the blurb of the user with `userId`.
[blurb: string]
### POST /getPlayers/delete/{uid: string}
```http
/getPlayers/delete/2f08e9796a
{"key": "hunter2"}
```
{key: string}
Deletes the getPlayers job with id `uid` from the filesystem if complete or the list if not. Note that if it is not complete it will still be running on the server though it cannot be accessed.
### POST /getPlayers/make/{group: number}/[rank: number]?[limit: number]&[online: boolean]
```http
/getPlayers/make/147864?limit=1&online=false
{"key": "hunter2"}
```
{key: string}
Gets the players in group with group ID `group`. If `rank` is not specified it gets all players from all ranks, otherwise it gets all players from the role with that rank. If `online` is true only online users will be collected. If `limit` is enabled users will be returned in the same order they are visible in the group pages to the set number and forever if the number is -1. Note that this slows down the function considerably. The function returns a `uid` that can eventually be used to get the resulting player list (if the page just waited it could time out since this action can take a while). The file containing players is stored on the file system in the folder `players` and is not cleared by this script, therefore the result will be usable across restarts. See below for retrieving.
[uid: number]
### GET /getPlayers/retrieve/{uid: string}
```http
/getPlayers/retrieve/2f08e9796a
```
Gets the result of the getPlayers job, returning `progress` in percent when not complete while `complete` denotes whether or not it is. The players are in json object `players`.
[progress: number,
complete: boolean,
players (object): {username (string): userId (number)}]
### POST /handleJoinRequest/{group: number}/{username: string}/{accept: boolean}
```http
/handleJoinRequest/18/Froast/true
{"key": "hunter2"}
```
{key: string}
Searches for the join request of user with username `username` in the group with group ID `group` and accepts them if `accept` is true and denies them if it is false (note that for either case you still need the parameter in the url)
### POST /message/{recipient: number}
```http
/message/2470023
{"subject": "Test", "body": "Test", "key": "hunter2"}
```
{subject: string,
body: string,
key: string}
Messages user with ID `recipient` with a message that has subject `subject` and body `body`.
### POST /promote/{group: number}/{target: number}
```http
/promote/18/2470023
{"key": "hunter2"}
```
{key: string}
Sets the role of the player to the adjacent higher-rank role.
[newRankName: string, newRank: number, newRoleSetId: number]
### POST /setRank/{group: number}/{target: number}/{rank: number}
```http
/setRank/18/2470023/2
{"key": "hunter2"}
```
{key: string}
Sets rank of player with user ID `target` to rank with rank number `rank` in group with group ID `group`. Responds with the role set ID of the user's updated rank, `newRoleSetId`.
[newRoleSetId: number]
### POST /shout/{group: number}
```http
/shout/18
{"message": "Test", "key": "hunter2"}
```
{message: string,
key: string}
Shouts in group with group ID `group` and the message `message`.
### POST /post/{group: number}
```http
/post/18
{"message": "Test", "key": "hunter2"}
```
{message: string,
key: string}
Posts a message to the wall in group with group ID `group` and the message `message`.
## Credits
Credits to sentanos for the original roblox-js-server which this project is based off of.
MIT

@@ -1,462 +0,269 @@

var express = require('express')
var rbx = require('noblox.js')
var fs = require('fs')
var crypto = require('crypto')
var validator = require('validator')
var bodyParser = require('body-parser')
var Promise = require('bluebird')
require('dotenv').config()
var app = express()
var port = process.env.PORT || 8080
var settings = require('./settings.json')
var key = settings.key
var maximumRank = settings.maximumRank || 255
const COOKIE = settings.cookie
const Koa = require('koa')
const Router = require('koa-joi-router')
const Joi = Router.Joi
app.set('env', 'production')
const errorHandler = require('koa-better-error-handler')
const koa404Handler = require('koa-404-handler')
const Boom = require('@hapi/boom')
const bodyParser = require('koa-bodyparser')
var _setRank = rbx.setRank
const nbx = require('noblox.js')
rbx.setRank = function (opt) {
var rank = opt.rank
if (rank > maximumRank) {
return Promise.reject(new Error('New rank ' + rank + ' is above rank limit ' + maximumRank))
} else {
return _setRank(opt)
}
}
const app = new Koa()
const router = Router()
var inProgress = {}
var completed = {}
const { COOKIE, API_TOKEN, MAX_RANK, PORT } = process.env
var dir = './players'
app.context.onerror = errorHandler()
app.context.api = true
app.use(koa404Handler)
app.use(bodyParser())
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
let loggedIn = false
fs.readdirSync('./players').forEach(function (file) { // This is considered a part of server startup and following functions could error anyways if it isn't complete, so using synchronous instead of asynchronous is very much intended.
completed[file] = true
})
function sendErr (res, json, status) {
res.json(json)
async function authenticate (ctx) {
if (ctx.request.headers.authorization !== API_TOKEN) {
ctx.throw(Boom.unauthorized('Authorization header does not match API_TOKEN.'))
}
}
function validatorType (type) {
switch (type) {
case 'int':
return validator.isInt
case 'safe_string':
return validator.isAlphanumeric
case 'boolean':
return validator.isBoolean
case 'string':
return function (value) {
return typeof value === 'string'
}
default:
return function () {
return true
}
async function nbxAuthenticate (ctx) {
if (!loggedIn) {
ctx.throw(Boom.unauthorized('You are not logged into a Roblox account, please update COOKIE.'))
}
}
function processType (type, value) {
switch (type) {
case 'int':
return parseInt(value, 10)
case 'boolean':
return (value === 'true')
default:
return value
}
async function throwError (ctx, error) {
const boomResponse = error
ctx.status = boomResponse.output.statusCode
ctx.body = boomResponse.output.payload
}
function verifyParameters (res, validate, requiredFields, optionalFields) {
var result = {}
if (requiredFields) {
for (var index in requiredFields) {
var type = requiredFields[index]
var use = validatorType(type)
router.get('/', async (ctx) => {
ctx.status = 200
})
var found = false
for (var i = 0; i < validate.length; i++) {
var value = validate[i][index]
if (value) {
if (use(value)) {
result[index] = processType(type, value)
found = true
} else {
sendErr(res, {error: 'Parameter "' + index + '" is not the correct data type.', id: null})
return false
}
break
}
// GET username from ID route
router.route({
method: 'get',
path: '/get-username-from-id/:id',
handler: async (ctx) => {
await nbx.getUsernameFromId(ctx.params.id).then(function (username) {
ctx.body = {
success: true,
data: username
}
if (!found) {
sendErr(res, {error: 'Parameter "' + index + '" is required.', id: null})
return false
}
}
}).catch(function (err) {
ctx.throw(Boom.notFound(err))
})
}
if (optionalFields) {
for (index in optionalFields) {
type = optionalFields[index]
use = validatorType(type)
for (i = 0; i < validate.length; i++) {
value = validate[i][index]
if (value) {
if (use(value)) {
result[index] = processType(type, value)
} else {
sendErr(res, {error: 'Parameter "' + index + '" is not the correct data type.', id: null})
return false
}
break
}
}
}
}
return result
}
})
function authenticate (req, res, next) {
if (req.body.key === key) {
next()
} else {
sendErr(res, {error: 'Incorrect authentication key', id: null}, 401)
}
}
function checkRank (opt) {
var group = opt.group
var target = opt.target
return rbx.getRankInGroup(group, target)
.then(function (rank) {
if (rank === 0) {
throw new Error('Target user ' + target + ' is not in group ' + group)
// GET ID from username route
router.route({
method: 'get',
path: '/get-id-from-username/:username',
handler: async (ctx) => {
await nbx.getIdFromUsername(ctx.params.username).then(function (username) {
ctx.body = {
success: true,
data: username
}
if (rank > maximumRank) {
throw new Error('Original rank ' + rank + ' is above rank limit ' + maximumRank)
}
return rank
}).catch(function (err) {
ctx.throw(Boom.notFound(err))
})
}
}
})
function changeRank (amount) {
return function (req, res, next) {
var requiredFields = {
'group': 'int',
'target': 'int'
// GET a player's information
router.route({
method: 'get',
path: '/user/:id',
pre: async (ctx, next) => {
await authenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
var validate = [req.params]
return nbx.getPlayerInfo(ctx.params.id).then((playerInfo) => {
ctx.status = 200
ctx.body = playerInfo
}).catch((err) => {
return throwError(ctx, Boom.badRequest(err.message))
})
}
})
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
// POST a response to a user join request
router.route({
method: 'post',
path: '/group/:group/handle-join-request',
validate: {
type: 'json',
body: Joi.object({
target: Joi.number().required(),
accept: Joi.boolean().required()
}),
continueOnError: true
},
pre: async (ctx, next) => {
await authenticate(ctx, next)
await nbxAuthenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
var group = opt.group
checkRank(opt)
.then(function (rank) {
return rbx.getRoles(group)
.then(function (roles) {
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) {
sendErr(res, {error: 'Rank change is out of range'})
return
}
foundRank = found.Rank
var up = roles[change + 1]
var down = roles[change - 1]
if ((up && up.Rank === foundRank) || (down && down.Rank === foundRank)) {
sendErr(res, {error: 'There are two or more roles with the same rank number, please change or commit manually.'})
return
}
var name = found.Name
opt.rank = foundRank
return rbx.setRank(opt)
.then(function (roleset) {
res.json({error: null, data: {newRoleSetId: roleset, newRankName: name, newRank: foundRank}, message: 'Successfully changed rank of user ' + opt.target + ' to rank "' + name + '" in group ' + opt.group})
})
}
}
})
})
.catch(function (err) {
sendErr(res, {error: 'Change rank failed: ' + err.message})
})
return nbx.handleJoinRequest(ctx.request.params.group, ctx.request.body.target, ctx.request.body.accept).then(() => {
ctx.status = 200
ctx.body = {
success: true,
message: `User's join request was ${ctx.request.body.accept ? 'accepted' : 'declined'} successfully.`
}
}).catch((err) => {
console.log(err)
return throwError(ctx, Boom.unauthorized(err.message))
})
}
}
})
function getPlayersWithOpt (req, res, next) {
var uid = crypto.randomBytes(5).toString('hex')
var requiredFields = {
'group': 'int'
}
var optionalFields = {
'rank': 'int',
'limit': 'int',
'online': 'boolean'
}
var validate = [req.params, req.query]
// POST a group shout
router.route({
method: 'post',
path: '/group/:group/shout',
validate: {
type: 'json',
body: Joi.object({
message: Joi.string().required()
}),
continueOnError: true
},
pre: async (ctx, next) => {
await authenticate(ctx, next)
await nbxAuthenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
var opt = verifyParameters(res, validate, requiredFields, optionalFields)
if (!opt) {
return
return nbx.shout(ctx.request.params.group, ctx.request.body.message).then((res) => {
ctx.status = 200
ctx.body = res
}).catch((err) => {
return throwError(ctx, Boom.unauthorized(err.message))
})
}
})
inProgress[uid] = 0
var players = rbx.getPlayers(opt)
inProgress[uid] = players.getStatus
players.promise.then(function (info) {
if (inProgress[uid]) { // Check if job was deleted
completed[uid] = true
var file = fs.createWriteStream('./players/' + uid)
file.write(JSON.stringify(info, null, ' '))
// DELETE a user (exile)
router.route({
method: 'delete',
path: '/group/:group/member/:target',
pre: async (ctx, next) => {
await authenticate(ctx, next)
await nbxAuthenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
info = null // Bye, bye
})
res.json({error: null, data: {uid: uid}})
}
app.use(bodyParser.json())
app.post('/setRank/:group/:target/:rank', authenticate, function (req, res, next) {
var requiredFields = {
'group': 'int',
'rank': 'int',
'target': 'int'
}
var validate = [req.params]
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
return
}
// This gets the rank manually instead of letting setRank do it because it needs the role's name.
var rank = opt.rank
checkRank(opt)
.then(function () {
return rbx.getRoles(opt.group)
.then(function (roles) {
var role = rbx.getRole(roles, rank)
if (!role) {
sendErr(res, {error: 'Role does not exist'})
return
}
var name = role.Name
return rbx.setRank(opt)
.then(function (roleset) {
res.json({error: null, data: {newRoleSetId: roleset, newRankName: name, newRank: rank}, message: 'Successfully changed rank of user ' + opt.target + ' to rank "' + name + '" in group ' + opt.group})
})
})
return nbx.exile(ctx.params.group, ctx.params.target).then(() => {
ctx.status = 200
ctx.body = {
success: true
}
}).catch((err) => {
return throwError(ctx, Boom.badRequest(err.message))
})
.catch(function (err) {
sendErr(res, {error: 'Set rank failed: ' + err.message})
})
})
app.post('/handleJoinRequest/:group/:username/:accept', authenticate, function (req, res, next) {
var requiredFields = {
'group': 'int',
'username': 'string',
'accept': 'boolean'
}
var validate = [req.params]
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
return
}
rbx.handleJoinRequest(opt)
.then(function () {
res.json({error: null, message: 'Successfully ' + (opt.accept ? 'accepted' : 'declined') + ' ' + opt.username})
})
.catch(function (err) {
sendErr(res, {error: 'Handle join request failed: ' + err.message})
})
})
app.post('/message/:recipient/', authenticate, function (req, res, next) {
var requiredFields = {
'recipient': 'int',
'subject': 'string',
'body': 'string'
}
var validate = [req.params, req.body]
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
return
}
rbx.message(opt)
.then(function () {
res.json({error: null, message: 'Messaged user ' + opt.recipient + ' with subject "' + opt.subject + '"'})
})
.catch(function (err) {
sendErr(res, {error: 'Message failed: ' + err.message})
})
})
// POST to the rank of a user
router.route({
method: 'post',
path: '/group/:group/member/:target/rank',
validate: {
type: 'json',
body: Joi.object({
rank: Joi.number().min(1).max(Number(MAX_RANK) || 254),
role: Joi.string()
}).xor('rank', 'role'),
continueOnError: true
},
pre: async (ctx, next) => {
await authenticate(ctx, next)
await nbxAuthenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
app.post('/shout/:group', authenticate, function (req, res, next) {
var requiredFields = {
'group': 'int'
}
var optionalFields = {
'message': 'string'
}
var validate = [req.params, req.body]
var opt = verifyParameters(res, validate, requiredFields, optionalFields)
if (!opt) {
return
}
rbx.shout(opt)
.then(function () {
res.json({error: null, message: 'Shouted in group ' + opt.group})
return nbx.setRank(ctx.request.params.group, ctx.request.params.target, ctx.request.body.rank || ctx.request.body.role).then((res) => {
ctx.status = 200
ctx.body = res
}).catch((err) => {
return throwError(ctx, Boom.badRequest(err.message))
})
.catch(function (err) {
sendErr(res, {error: 'Error: ' + err.message})
})
})
app.post('/post/:group', authenticate, function (req, res, next) {
var requiredFields = {
'group': 'int',
'message': 'string'
}
var validate = [req.params, req.body]
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
return
}
rbx.post(opt)
.then(function () {
res.json({error: null, message: 'Posted in group ' + opt.group})
})
.catch(function (err) {
sendErr(res, {error: 'Error: ' + err.message})
})
})
app.get('/getBlurb/:userId', function (req, res, next) {
var requiredFields = {
'userId': 'int'
}
var validate = [req.params]
var opt = verifyParameters(res, validate, requiredFields)
if (!opt) {
return
}
rbx.getBlurb(opt)
.then(function (blurb) {
res.json({error: null, data: {blurb: blurb}})
})
.catch(function (err) {
sendErr(res, {error: 'Error: ' + err.message})
})
})
app.post('/promote/:group/:target', authenticate, changeRank(1))
app.post('/demote/:group/:target', authenticate, changeRank(-1))
app.post('/getPlayers/make/:group/:rank', getPlayersWithOpt)
app.post('/getPlayers/make/:group', getPlayersWithOpt)
app.post('/getPlayers/delete/:uid', authenticate, function (req, res, next) {
var uid = req.params.uid
function fail () {
sendErr(res, {error: 'Invalid ID or the job is not complete'})
}
if (uid.length === 10 && validator.isHexadecimal(uid)) {
var path = './players/' + uid
if (completed[uid]) {
completed[uid] = false
inProgress[uid] = null
fs.unlink(path, function (err) { // Since the uid was verified to be hex this shouldn't be a security issue
if (err) {
next(err)
} else {
res.json({error: null, message: 'File deleted'})
}
})
// POST to the rank of a user by providing a value to change it by
router.route({
method: 'post',
path: '/group/:group/member/:target/rank-change',
validate: {
type: 'json',
body: Joi.object({
change: Joi.number().required()
}),
continueOnError: true
},
pre: async (ctx, next) => {
await authenticate(ctx, next)
await nbxAuthenticate(ctx, next)
return next()
},
handler: async (ctx) => {
if (ctx.invalid) {
await throwError(ctx, Boom.badRequest(ctx.invalid.body))
return
}
} else if (inProgress[uid]) {
inProgress[uid] = null
res.json({error: null, message: 'Removed from list, the job itself has not been stopped'})
} else {
fail()
}
})
app.get('/getPlayers/retrieve/:uid', function (req, res, next) {
var uid = req.params.uid
function fail () {
sendErr(res, {error: 'Invalid ID'})
return nbx.changeRank(ctx.request.params.group, ctx.request.params.target, ctx.request.body.change).then((res) => {
ctx.status = 200
ctx.body = res
}).catch((err) => {
return throwError(ctx, Boom.badRequest(err.message))
})
}
if (uid.length === 10 && validator.isHexadecimal(uid)) {
var path = './players/' + uid
var complete = completed[uid]
var progress = inProgress[uid]
if (complete) {
fs.stat(path, function (err) {
if (err) {
next(err)
} else {
res.append('Content-Type', 'application/json')
res.write('{"error":null,"data":{"progress":100,"complete":true,')
var stream = fs.createReadStream(path)
var first = true
stream.on('data', function (data) {
if (first) {
first = false
res.write(data.toString().substring(1))
} else {
res.write(data)
}
})
stream.on('end', function () {
res.end('}')
})
}
})
} else if (progress) {
sendErr(res, {error: 'Job is still processing', data: {complete: false, progress: progress()}}, 200)
} else {
fail()
}
} else {
fail()
}
})
app.use(function (err, req, res, next) {
console.error(err.stack)
sendErr(res, {error: 'Internal server error'})
})
app.use(router.middleware())
function login () {
return rbx.cookieLogin(COOKIE)
if (COOKIE) {
nbx.setCookie(COOKIE).then((currentUser) => {
loggedIn = true
app.listen(PORT)
console.log(`Listening on port ${PORT},`, `logged into ${currentUser.name}#${currentUser.id}`)
})
} else {
app.listen(PORT)
console.log(`Listening on port ${PORT},`, 'not logged in.')
}
login().then(function () {
app.listen(port, function () {
console.log('Listening on port ' + port)
})
})
.catch(function (err) {
var errorApp = express()
errorApp.get('/*', function (req, res, next) {
res.json({error: 'Server configuration error: ' + err.message})
})
errorApp.listen(port, function () {
console.log('Configuration error page listening')
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc