Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix addDeveloperProduct & checkDeveloperProductName. addGamepass feature #785

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/games/addDeveloperProduct.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ const nextFunction = (jar, token, universeId, name, priceInRobux, description) =
return checkProductName({
universeId,
productName: name
}).then((res) => {
if (res.Success && res.Message === 'Name available') {
}).then((exists) => {
if (!exists) {
return http({
url: '//apis.roblox.com/developer-products/v1/universes/' + universeId + '/developerproducts?name=' + name + '&description=' + description + '&priceInRobux=' + priceInRobux,
url: `//apis.roblox.com/developer-products/v1/universes/${universeId}/developerproducts?name=${name}&description=${description}&priceInRobux=${priceInRobux}`,
options: {
method: 'POST',
jar,
jar: jar,
headers: {
'X-CSRF-TOKEN': token
},
Expand All @@ -47,7 +47,7 @@ const nextFunction = (jar, token, universeId, name, priceInRobux, description) =
productId: typeof res.body === 'object' ? res.body.id : JSON.parse(res.body).id
}
} else {
throw new Error('Create product failed, ' + res.statusCode + ' ' + res.statusMessage + '')
throw new Error(`Create product failed, ${res.statusCode} ${res.statusMessage}`)
}
})
} else {
Expand All @@ -59,7 +59,7 @@ const nextFunction = (jar, token, universeId, name, priceInRobux, description) =
exports.func = (args) => {
const jar = args.jar

return getGeneralToken({ jar }).then((xcsrf) => {
return getGeneralToken({ jar: jar }).then((xcsrf) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the point of this change is

return nextFunction(jar, xcsrf, args.universeId, args.name, args.priceInRobux, args.description)
})
}
64 changes: 64 additions & 0 deletions lib/games/addGamepass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const http = require('../util/http.js').func
const getGeneralToken = require('../util/getGeneralToken.js').func

exports.required = ['universeId', 'name', 'priceInRobux']
exports.optional = ['description', 'jar']

// Docs
/**
* 🔐 Create a gamepass.
* @category Game
* @alias addGamepass
* @param {number} universeId - The id of the universe.
* @param {string} name - The name of the gamepass.
* @param {number} priceInRobux - The price of the product.
* @param {string=} description - The description of the gamepass.
* @returns {Promise<GamepassAddResult>}
* @example const noblox = require("noblox.js")
* // Login using your cookie
* noblox.addGamepass(1, "A Gamepass", 100, "A cool item.")
**/

const nextFunction = (jar, token, universeId, name, priceInRobux, description) => {
const FormData = require('form-data');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't require modules in functions like this.
This does not need a new dependency - I believe the new fetch API has a built in way of building these, and they are built manually elsewhere in the library.

const goofy = new FormData()
goofy.append("Name", name)
goofy.append("Description", description)
goofy.append("UniverseId", universeId)
goofy.append("IsForSale", 'true')
goofy.append("Price", priceInRobux)
return http({
url: `//apis.roblox.com/game-passes/v1/game-passes`,
options: {
method: 'POST',
jar: jar,
body: goofy,
headers: {
...goofy.getHeaders(),
'X-CSRF-TOKEN': token
},
resolveWithFullResponse: true
}
}).then((res) => {
console.log(res)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

console.log shouldn't be there

if (res.statusCode === 200) {
return {
universeId,
name,
priceInRobux,
description,
gamepassId: typeof res.body === 'object' ? res.body.id : JSON.parse(res.body).id
}
} else {
throw new Error(`Create gamepass failed, ${res.statusCode} ${res.statusMessage}`)
}
}).catch(e => {})
}

exports.func = (args) => {
const jar = args.jar

return getGeneralToken({ jar: jar }).then((xcsrf) => {
return nextFunction(jar, xcsrf, args.universeId, args.name, args.priceInRobux, args.description)
})
}
18 changes: 13 additions & 5 deletions lib/games/checkDeveloperProductName.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ exports.optional = ['jar', 'productId']
* @param {number} universeId - The id of the universe.
* @param {string} productName - The name of the developer product.
* @param {number=} productId - The id of the developer product.
* @returns {Promise<CheckDeveloperProductNameResult>}
* @returns {Promise<Boolean>}
* @example const noblox = require("noblox.js")
* // Login using your cookie
* const productInfo = await noblox.checkDeveloperProductName(1, "A Developer Product")
* const exists = await noblox.checkDeveloperProductName(1, "A Developer Product")
**/

exports.func = (args) => {
Expand All @@ -23,17 +23,25 @@ exports.func = (args) => {
const productId = parseInt(args.productId) ? parseInt(args.productId) : 0

return http({
url: '//www.roblox.com/places/check-developerproduct-name?universeId=' + universeId + '&developerProductId=' + productId + '&developerProductName=' + args.productName + '&_=1515792139751',
url: `//apis.roblox.com/developer-products/v1/universes/${universeId}/developerproducts?pageNumber=1&pageSize=2147483647`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the meaning of the page size here? Why is it a large arbitrary number? how does this work when the number of pages is greater than 1? Could this use a pagination wrapper

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per my testing, Roblox does not use cursors for this endpoint. The number specified for the page size is (2^31)-1 (the largest possible signed 32bit int), which happens to be the largest page size Roblox supports. Now, whether the api will actually return that many results, I don't know.

I agree that a pagination wrapper would be preferable to this though, given this is already undocumented to start with and I don't really expect this to work in practice (but maybe somehow it does).

options: {
method: 'GET',
jar,
jar: jar,
resolveWithFullResponse: true
}
}).then((res) => {
if (res.statusCode !== 200) {
throw new Error('You are not logged in')
} else {
return JSON.parse(res.body)
const data = JSON.parse(res.body)
let exists = false
data.forEach(product => {
if(product.name == args.productName || product.id == productId){
exists = true
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns undefined and the typing says it returns a boolean.
Would a better approach be:

for (const product of data) {
   if (...) {
       return true

}
}
return false
```?

}
})
return exists
}
})
}
21 changes: 13 additions & 8 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,13 +631,12 @@ declare module "noblox.js" {
productId: string
}

interface CheckDeveloperProductNameResult {
Success: boolean;
/**
* When success is true: "Name available"
* When success is false, you can get: "Product name already exists"
*/
Message: string;
interface GamepassAddResult {
universeId: number,
name: string,
priceInRobux: number,
description?: string,
productId: string
}

interface GamePassData {
Expand Down Expand Up @@ -1826,12 +1825,18 @@ declare module "noblox.js" {
*/
function addDeveloperProduct(universeId: number, name: string, priceInRobux: number, description?: string, jar?: CookieJar): Promise<DeveloperProductAddResult>;

/**
* 🔐 Adds a gamepass to the specified universe.
* Warning: The `gamepassId` returned by this function does not match the `gamepassId` used by other endpoints.
*/
function addGamepass(universeId: number, name: string, priceInRobux: number, description?: string, jar?: CookieJar): Promise<GamepassAddResult>;

/**
* 🔐 Checks to see if the provided `produceName` is able to be used on `productId`.
*
* NOTE: You actually need a valid `productId` and `universeId` otherwise, the http request returns a `404 Not Found` response.
*/
function checkDeveloperProductName(universeId: number, productName: string, jar?: CookieJar, productId?: number): Promise<CheckDeveloperProductNameResult>;
function checkDeveloperProductName(universeId: number, productName: string, jar?: CookieJar, productId?: number): Promise<Boolean>;

/**
* 🔐 Configures a game pass with the id `gamePassId` to have a `name`, `description`, `price` in Robux, and `icon` image. If `name` is an empty string, only `price` is changed. Setting `price` to false, 0, or a negative value will place the game pass off-sale.
Expand Down