Skip to content

Commit

Permalink
Merge branch 'noblox:master' into addDeveloperProductFix
Browse files Browse the repository at this point in the history
  • Loading branch information
alanbixby authored Aug 1, 2024
2 parents 863fbbc + 2bc2f3b commit 22fcfc9
Show file tree
Hide file tree
Showing 20 changed files with 2,415 additions and 13,848 deletions.
21 changes: 0 additions & 21 deletions .eslintrc.js

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/doc-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up node
uses: actions/setup-node@v1
with:
node-version: 14
node-version: 20

- name: Install yarn
run: npm install -g yarn
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/npmpublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up node
uses: actions/setup-node@v1
with:
node-version: 14
node-version: 20

- name: Install yarn
run: npm install -g yarn
Expand Down Expand Up @@ -51,22 +51,22 @@ jobs:
- name: Set up node
uses: actions/setup-node@v1
with:
node-version: 14
node-version: 20

- name: Install yarn
run: npm install -g yarn

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: yarn-deps-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-deps-${{ hashFiles('yarn.lock') }}
- name: Create Release
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Run tests

on:
workflow_dispatch


jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up node
uses: actions/setup-node@v1
with:
node-version: 20

- name: Install yarn
run: npm install -g yarn

- name: Install dependencies
run: yarn install

- name: Lint lib/
run: yarn lint
- name: Run tests
env:
COOKIE: ${{ secrets.COOKIE }}
COOKIE_2: ${{ secrets.COOKIE_2 }}
run: yarn test

13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<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://npmjs.org/noblox.js"><img src="https://img.shields.io/npm/v/noblox.js.svg?style=flat-square" alt="NPM package"/>
<a href="https://travis-ci.org/noblox/noblox.js"><img src="https://img.shields.io/travis/noblox/noblox.js/master.svg?style=flat-square" alt="Travis Build Status"/></a></a>
</p>

<p align="center">
Expand Down Expand Up @@ -38,7 +37,7 @@ If you are looking for more information on how to create something like this, ch

## Prerequisites

- [**Node.js®**](https://nodejs.org/en/download/current/)
- [**Node.js®**](https://nodejs.org/en/download/current/) v18.18 or later

---

Expand Down
34 changes: 34 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import jest from "eslint-plugin-jest";
import globals from "globals";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});

export default [...compat.extends("standard", "plugin:jest/recommended"), {
plugins: {
jest,
},

languageOptions: {
globals: {
...globals.commonjs,
...globals.node,
Atomics: "readonly",
SharedArrayBuffer: "readonly",
},

ecmaVersion: 2018,
sourceType: "commonjs",
},

rules: {},
}];
42 changes: 13 additions & 29 deletions lib/games/configureGamePass.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,19 @@ exports.optional = ['description', 'price', 'icon', 'jar']
// Define
function configureGamePass (gamePassId, name, description, price, icon, jar, token) {
return new Promise((resolve, reject) => {
const file = icon && {
value: icon,
options: {
filename: 'icon.png',
contentType: 'image/png'
}
}

const httpOpt = {
url: '//www.roblox.com/game-pass/update',
url: `//apis.roblox.com/game-passes/v1/game-passes/${gamePassId}/details`,
options: {
method: 'POST',
jar,
headers: {
'X-CSRF-TOKEN': token,
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryKMFaNaAn4j7XeMO'
'X-CSRF-TOKEN': token
},
resolveWithFullResponse: true,
formData: {
id: gamePassId,
name,
description,
file
Name: name,
Description: description,
File: icon
}
}
}
Expand All @@ -62,21 +52,18 @@ function configureGamePass (gamePassId, name, description, price, icon, jar, tok
}

return http(httpOpt).then(function (res) {
const json = JSON.parse(res.body)
if (json.isValid) {
if (res.statusCode === 200) {
resolve({
gamePassId,
name,
description: description || '',
...price,
iconChanged: !!file // Boolean Cast
iconChanged: !!icon // Boolean Cast
})
} else {
const priceComment = (typeof (price) === 'number') ? ` | NOTE: Price has successfully been changed to ${price}R.` : ''
if (res.statusCode === 403) {
reject(new Error(`You do not have permission to edit this game pass.${priceComment}`))
} else if (json.error) {
reject(new Error(json.error + priceComment)) // 'The name or description contains inappropriate text.' or 'Text filtering service is unavailable at this time.'
} else {
reject(new Error(`An unexpected error occurred with status code ${res.statusCode}.${priceComment}`))
}
Expand All @@ -88,23 +75,22 @@ function configureGamePass (gamePassId, name, description, price, icon, jar, tok
// Configuring the name/description and Robux must be done in separate calls, albeit to the same end-point.
function configureRobux (args, token) {
const httpOpt = {
url: '//www.roblox.com/game-pass/update',
url: `//apis.roblox.com/game-passes/v1/game-passes/${args.gamePassId}/details`,
options: {
method: 'POST',
jar: args.jar,
headers: {
'X-CSRF-TOKEN': token
},
resolveWithFullResponse: true,
json: {
id: args.gamePassId,
price: Math.floor(args.price || 0), // Prevent Decimals
isForSale: !!Math.max(args.price, 0) // Boolean Cast
formData: {
IsForSale: (!!Math.max(args.price, 0)).toString(), // Boolean Cast
Price: Math.floor(args.price || 0).toString() // Prevent Decimals
}
}
}
return http(httpOpt).then(function (res) {
if (res.body.isValid) {
if (res.statusCode === 200) {
// Passing price as an object, so they can be omitted if configureRobux is not run.
return configureGamePass(
args.gamePassId,
Expand All @@ -121,10 +107,8 @@ function configureRobux (args, token) {
} else {
if (res.statusCode === 403) {
throw new Error('You do not have permission to edit this game pass.')
} else if (res.body.error) {
throw new Error(res.body.error)
} else {
throw new Error(`An unexpected error occurred with status code ${res.statusCode}.`)
throw new Error(res.body.errors || 'An unknown error occurred with status code ' + res.statusCode)
}
}
})
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ noblox.onBlurbChange = require('./users/onBlurbChange.js')
noblox.clearSession = require('./util/clearSession.js')
noblox.generalRequest = require('./util/generalRequest.js')
noblox.getAction = require('./util/getAction.js')
noblox.getAuthenticatedUser = require('./util/getAuthenticatedUser.js')
noblox.getCurrentUser = require('./util/getCurrentUser.js')
noblox.getGeneralToken = require('./util/getGeneralToken.js')
noblox.getHash = require('./util/getHash.js')
Expand Down
2 changes: 1 addition & 1 deletion lib/privatemessages/getMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function getMessages (jar, pageNumber, pageSize, messageTab) {
url: `//privatemessages.roblox.com/v1/messages?pageNumber=${pageNumber}&pageSize=${pageSize}&messageTab=${messageTab}`,
options: {
method: 'GET',
jar: jar,
jar,
resolveWithFullResponse: true
}
}
Expand Down
6 changes: 3 additions & 3 deletions lib/privatemessages/onMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ exports.func = function (args) {
const onMessage = new events.EventEmitter()
let waitingForRequest = false
let latest
getMessages({ jar: jar, messageTab: 'Inbox', pageNumber: 0, pageSize: 1 })
getMessages({ jar, messageTab: 'Inbox', pageNumber: 0, pageSize: 1 })
.then(function (initial) {
latest = initial.collection[0] ? initial.collection[0].id : 0
const notifications = onNotification({ jar: jar })
const notifications = onNotification({ jar })
notifications.on('data', function (name, message) {
if (name === 'MessageNotification' && message.Type === 'Created') {
if (waitingForRequest) {
waitingForRequest = false
} else {
getMessages({
jar: jar,
jar,
messageTab: 'Inbox',
pageNumber: 0,
pageSize: 1
Expand Down
41 changes: 41 additions & 0 deletions lib/util/getAuthenticatedUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Includes
const http = require('./http.js').func

// Args
exports.optional = ['jar']

// Docs
/**
* 🔐 Get the current authenticated user.
* @category Utility
* @alias getAuthenticatedUser
* @returns {AuthenticatedUserData}
* @example const noblox = require("noblox.js")
* // Login using your cookie.
* const user = await noblox.getAuthenticatedUser()
**/

// Define
exports.func = async function (args) {
const jar = args.jar
const httpOpt = {
url: '//users.roblox.com/v1/users/authenticated',
options: {
method: 'GET',
followRedirect: false,
jar,
json: true,
resolveWithFullResponse: true
}
}

const res = await http(httpOpt)

if (res.statusCode === 401) {
throw new Error('You are not logged in.')
} else if (res.statusCode !== 200) {
throw new Error(JSON.stringify(res.body))
}

return res.body
}
40 changes: 27 additions & 13 deletions lib/util/http.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Dependencies
let request = require('request-promise')
const util = require('util')
let request = util.promisify(require('postman-request'))

// Includes
const options = require('../options.js')
Expand Down Expand Up @@ -82,19 +83,32 @@ exports.func = function (args) {
}
return http(args.url, opt).then(function (res) {
if (opt && opt.headers && opt.headers['X-CSRF-TOKEN']) {
if (res.statusCode === 403 && (res.statusMessage === 'XSRF Token Validation Failed' || res.statusMessage === 'Token Validation Failed')) {
depth++
if (depth >= 3) {
throw new Error('Tried ' + depth + ' times and could not refresh XCSRF token successfully')
if (res.statusCode === 403) {
let message

try {
message = typeof res.body === 'string' ? JSON.parse(res.body).message : res.body.message
} catch (_) {
// Roblox didn't send back a properly formed json object
}
const token = res.headers['x-csrf-token']
if (token) {
opt.headers['X-CSRF-TOKEN'] = token
opt.jar = jar
args.depth = depth + 1
return exports.func(args)
} else {
throw new Error('Could not refresh X-CSRF-TOKEN')

if (message === 'XSRF Token Validation Failed' || message === 'Token Validation Failed') {
depth++

if (depth >= 3) {
throw new Error('Tried ' + depth + ' times and could not refresh XCSRF token successfully')
}

const token = res.headers['x-csrf-token']

if (token) {
opt.headers['X-CSRF-TOKEN'] = token
opt.jar = jar
args.depth = depth + 1
return exports.func(args)
} else {
throw new Error('Could not refresh X-CSRF-TOKEN')
}
}
} else {
if (depth > 0) {
Expand Down
Loading

0 comments on commit 22fcfc9

Please sign in to comment.