From 2be43434f12346f55599e550258057fe5d90da7c Mon Sep 17 00:00:00 2001 From: Alan Bixby Date: Fri, 16 Aug 2024 09:59:40 -0400 Subject: [PATCH] Migrate `setCookie` from `/mobileapi/userinfo` to `/v1/users/authenticated`; add `getUserFunds` method (#824) * feat(breaking)!: add getUserFunds(); migrate setCookie() from getCurrentUser() to getAuthenticatedUser(); polyfill and deprecate getCurrentUser() + add console warnings for getCurrentUser usage BREAKING CHANGE: getCurrentUser's /mobileinfo api endpoint is being deprecated by Roblox on August 27th (https://devforum.roblox.com/t/official-list-of-deprecated-web-endpoints/62889/66), setCookie will now have a new response type * misc: promisify secondary calls, remove github PR hyperlink, fix setOptions typing * lint: apply linting rules --- lib/client/setCookie.js | 6 ++--- lib/economy/getUserFunds.js | 44 +++++++++++++++++++++++++++++++++++++ lib/index.js | 1 + lib/util/getCurrentUser.js | 42 +++++++++++++++++++++++------------ lib/util/setOptions.js | 2 +- settings.json | 3 +++ typings/index.d.ts | 15 +++++++++---- typings/jsDocs.ts | 6 +++-- 8 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 lib/economy/getUserFunds.js diff --git a/lib/client/setCookie.js b/lib/client/setCookie.js index 0c5506255..778c80ddc 100644 --- a/lib/client/setCookie.js +++ b/lib/client/setCookie.js @@ -1,5 +1,5 @@ const options = require('../options.js') -const getCurrentUser = require('../util/getCurrentUser.js').func +const getAuthenticatedUser = require('../util/getAuthenticatedUser.js').func exports.required = ['cookie'] exports.optional = ['validate'] @@ -11,7 +11,7 @@ exports.optional = ['validate'] * @alias setCookie * @param {string} cookie - The cookie to sign in with. * @param {boolean=} [validate=true] - Whether to validate the cookie or not. - * @returns {Promise} + * @returns {Promise} * @example const noblox = require("noblox.js") * noblox.setCookie("cookie").then(function() { * //your code here @@ -28,7 +28,7 @@ exports.func = async function (args) { return false } try { - const res = await getCurrentUser({ jar: { session: args.cookie } }) + const res = await getAuthenticatedUser({ jar: { session: args.cookie } }) options.jar.session = args.cookie return res } catch (error) { diff --git a/lib/economy/getUserFunds.js b/lib/economy/getUserFunds.js new file mode 100644 index 000000000..d73fc94b1 --- /dev/null +++ b/lib/economy/getUserFunds.js @@ -0,0 +1,44 @@ +// Includes +const http = require('../util/http.js').func + +// Args +exports.required = ['userId'] +exports.optional = ['jar'] + +// Docs +/** + * 🔓 Gets the amount of robux for the authenticated user. + * @category User + * @param {number} userId - Must match the userId of the authenticated user + * @alias getUserFunds + * @returns {Promise} + * @example const noblox = require("noblox.js") + * // Login using your cookie + * const currentUser = await noblox.setCookie(process.env.ROBLOXCOOKIE) + * const robux = await noblox.getUserFunds(currentUser.id) + */ + +// Define +function getUserFunds (userId, jar) { + return http({ + url: `//economy.roblox.com/v1/users/${userId}/currency`, + options: { + jar, + resolveWithFullResponse: true + } + }) + .then(({ statusCode, body }) => { + const { robux, errors } = JSON.parse(body) + if (statusCode === 200) { + return robux + } else if (statusCode === 400 || statusCode === 403) { + throw new Error(`${errors[0].message} | userId: ${userId}`) + } else { + throw new Error(`An unknown error occurred with getUserFunds() | [${statusCode}] userId: ${userId}`) + } + }) +} + +exports.func = function ({ userId, jar }) { + return getUserFunds(userId, jar) +} diff --git a/lib/index.js b/lib/index.js index 341ca5ace..bd6063b3a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -71,6 +71,7 @@ noblox.getGroupRevenueSummary = require('./economy/getGroupRevenueSummary.js') noblox.getGroupTransactions = require('./economy/getGroupTransactions.js') noblox.getResaleData = require('./economy/getResaleData.js') noblox.getResellers = require('./economy/getResellers.js') +noblox.getUserFunds = require('./economy/getUserFunds.js') noblox.getUserTransactions = require('./economy/getUserTransactions.js') noblox.onGroupTransaction = require('./economy/onGroupTransaction.js') noblox.acceptFriendRequest = require('./friends/acceptFriendRequest.js') diff --git a/lib/util/getCurrentUser.js b/lib/util/getCurrentUser.js index 6c3b05e3e..79cad8f7f 100644 --- a/lib/util/getCurrentUser.js +++ b/lib/util/getCurrentUser.js @@ -1,7 +1,8 @@ // Includes -const http = require('./http.js').func +const settings = require('../../settings.json') + +const noblox = require('../index.js') -// Args exports.optional = ['option', 'jar'] // Docs @@ -12,6 +13,7 @@ exports.optional = ['option', 'jar'] * @param {string=} option - A specific option to return. * @returns {LoggedInUserData} * @example const noblox = require("noblox.js") + * @deprecated getCurrentUser() is deprecated; see getAuthenticatedUser(), getPremium(), getThumbnails(), getUserFunds() instead | August 27, 2024 - https://devforum.roblox.com/t/official-list-of-deprecated-web-endpoints/62889/66 * // Login using your cookie. * const user = await noblox.getCurrentUser() **/ @@ -20,23 +22,35 @@ exports.optional = ['option', 'jar'] exports.func = async function (args) { const jar = args.jar const option = args.option - const httpOpt = { - url: '//www.roblox.com/mobileapi/userinfo', - options: { - resolveWithFullResponse: true, - method: 'GET', - followRedirect: true, - jar - } + if (settings.show_deprecation_warnings) { + console.warn('[DEPRECATED]: getCurrentUser() is deprecated by Roblox; use getAuthenticatedUser(), getPremium(), getThumbnails(), or getUserFunds() instead!') + console.warn(' > Opt out of these warnings using noblox.setOptions({ show_deprecation_warnings: false })') } - const res = await http(httpOpt) - if (res.statusCode !== 200) { - throw new Error('You are not logged in.') + + const currentUser = await noblox.getAuthenticatedUser(jar) + const [premiumStatus, thumbnailResponse, robuxBalance] = await Promise.all( + [ + noblox.getPremium(currentUser.id, jar), + noblox.getPlayerThumbnail(currentUser.id, '352x352', 'png', false, 'Body', jar), + noblox.getUserFunds(currentUser.id, jar) + ] + ) + + const json = { + UserID: currentUser.id, + UserName: currentUser.name, + RobuxBalance: robuxBalance, + ThumbnailUrl: thumbnailResponse[0].imageUrl, + IsAnyBuildersClubMember: false, + IsPremium: premiumStatus, + DEPRECATION_WARNING: '[DEPRECATED]: noblox.getCurrentUser() is deprecated; use getAuthenticatedUser(), getPremium(), getThumbnails(), or getUserFunds() instead!' } - const json = JSON.parse(res.body) + if (!option) { return json } + + // Support queried rgequests `getCurrentUser('UserID') -> only UserID` const searchKey = Object.keys(json).filter((key) => { return option.toLowerCase() === key.toLowerCase() })[0] diff --git a/lib/util/setOptions.js b/lib/util/setOptions.js index c78254d24..d8c683c3f 100644 --- a/lib/util/setOptions.js +++ b/lib/util/setOptions.js @@ -6,7 +6,7 @@ const settings = require('../../settings.json') * altering the settings.json file. Objects passed to this function should match the format of the settings.json file. * Unknown keys, or malformed options will be rejected with an error. * @category Utility - * @param {NobloxOptions} newOptions - The new options to set, structured as per [settings.json](https://github.com/noblox/noblox.js/blob/master/settings.json) + * @param {Partial} newOptions - The new options to set, structured as per [settings.json](https://github.com/noblox/noblox.js/blob/master/settings.json) * @returns void * @see [settings.json](https://github.com/noblox/noblox.js/blob/master/settings.json) - default package settings * @example const noblox = require("noblox.js") diff --git a/settings.json b/settings.json index b07e23fe3..6111247f3 100644 --- a/settings.json +++ b/settings.json @@ -1,4 +1,7 @@ { + "show_deprecation_warnings": true, + "show_deprecation_warnings_desc": "Prints console warnings for functions that are being polyfilled by newer methods due to upstream Roblox API changes", + "session_only": true, "session_only_desc": "Minimizes data usage and speed up requests by only saving session cookies, disable if you need other cookies to be saved as well.", diff --git a/typings/index.d.ts b/typings/index.d.ts index 3fed27fc3..10652c4c4 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -17,6 +17,9 @@ declare module "noblox.js" { * NobloxOptions for setOptions, based from settings.json */ interface NobloxOptions { + /** Prints console warnings for functions that are being polyfilled by newer methods due to upstream Roblox API changes */ + show_deprecation_warnings: boolean; + /** Minimizes data usage and speed up requests by only saving session cookies, disable if you need other cookies to be saved as well. (Default: true) */ session_only: boolean; @@ -996,9 +999,8 @@ declare module "noblox.js" { UserID: number, UserName: string, RobuxBalance: number, - TicketsBalance: number, ThumbnailUrl: string, - IsAnyBuildersClubMember: boolean, + IsAnyBuildersClubMember: false, IsPremium: boolean } @@ -1670,7 +1672,7 @@ declare module "noblox.js" { * 🔐 Allows the user to login with a provided cookie string, bypassing the username/password captcha issues. * By default, the provided cookie will be validated by making a HTTP request. To disable this behaviour, pass false as the second optional parameter (shouldValidate). */ - function setCookie(cookie: string, shouldValidate?: B): B extends false ? boolean : Promise + function setCookie(cookie: string, shouldValidate?: B): B extends false ? boolean : Promise /// DataStores @@ -1770,6 +1772,11 @@ declare module "noblox.js" { */ function getUserTransactions(transactionType?: "Sale" | "Purchase" | "AffiliateSale" | "DevEx" | "GroupPayout" | "AdImpressionPayout", limit?: number, sortOrder?: SortOrder, jar?: CookieJar): Promise; + /** + * 🔐 Returns the current user's robux balance + */ + function getUserFunds(userId?: number, jar?: CookieJar): Promise; + /// Friends /** @@ -2297,7 +2304,7 @@ declare module "noblox.js" { * @param newOptions - The new options to set, structured as per settings.json * @see https://github.com/noblox/noblox.js/blob/master/settings.json */ - function setOptions(newOptions: NobloxOptions): void + function setOptions(newOptions: Partial): void // Events diff --git a/typings/jsDocs.ts b/typings/jsDocs.ts index 5660344a4..6a85a8a3a 100644 --- a/typings/jsDocs.ts +++ b/typings/jsDocs.ts @@ -14,6 +14,9 @@ type CookieJar = { * NobloxOptions for setOptions, based from settings.json */ type NobloxOptions = { + /** Prints console warnings for functions that are being polyfilled by newer methods due to upstream Roblox API changes */ + show_deprecation_warnings: boolean; + /** Minimizes data usage and speed up requests by only saving session cookies, disable if you need other cookies to be saved as well. (Default: true) */ session_only: boolean; @@ -1374,9 +1377,8 @@ type LoggedInUserData = { UserID: number, UserName: string, RobuxBalance: number, - TicketsBalance: number, ThumbnailUrl: string, - IsAnyBuildersClubMember: boolean, + IsAnyBuildersClubMember: false, IsPremium: boolean }