diff --git a/lib/friends/getFollowerCount.js b/lib/friends/getFollowerCount.js new file mode 100644 index 000000000..cef07304a --- /dev/null +++ b/lib/friends/getFollowerCount.js @@ -0,0 +1,36 @@ +// Includes +const http = require('../util/http.js').func + +// Args +exports.required = ['userId'] + +// Docs +/** + * ✅ Gets the number of followers a user has. + * @category User + * @alias getFollowerCount + * @param { number } userId + * @returns Promise + * @example const noblox = require("noblox.js") + * const numberOfFollowers = await noblox.getFollowerCount(55549140) +**/ + +// Define +exports.func = function (userId) { + const httpOpt = { + url: `//friends.roblox.com/v1/users/${userId}/followers/count`, + options: { + json: true, + method: 'GET', + resolveWithFullResponse: true + } + } + + return http(httpOpt).then(function (res) { + if (res.statusCode === 200) { return res.body.count } + + throw new Error( + `Failed to retrieve follower count: (${res.statusCode}) ${res.body}` + ) + }) +} diff --git a/lib/friends/getFollowingCount.js b/lib/friends/getFollowingCount.js new file mode 100644 index 000000000..bd43cc941 --- /dev/null +++ b/lib/friends/getFollowingCount.js @@ -0,0 +1,36 @@ +// Includes +const http = require('../util/http.js').func + +// Args +exports.required = ['userId'] + +// Docs +/** + * ✅ Gets the number of users a user is following. + * @category User + * @alias getFollowingCount + * @param { number } userId + * @returns Promise + * @example const noblox = require("noblox.js") + * const numberOfFollowings = await noblox.getFollowingCount(55549140) +**/ + +// Define +exports.func = function (userId) { + const httpOpt = { + url: `//friends.roblox.com/v1/users/${userId}/followings/count`, + options: { + json: true, + method: 'GET', + resolveWithFullResponse: true + } + } + + return http(httpOpt).then(function (res) { + if (res.statusCode === 200) { return res.body.count } + + throw new Error( + `Failed to retrieve following count: (${res.statusCode}) ${res.body}` + ) + }) +} diff --git a/lib/friends/getFriendCount.js b/lib/friends/getFriendCount.js new file mode 100644 index 000000000..72ceb1cb7 --- /dev/null +++ b/lib/friends/getFriendCount.js @@ -0,0 +1,36 @@ +// Includes +const http = require('../util/http.js').func + +// Args +exports.required = ['userId'] + +// Docs +/** + * ✅ Gets the number of friends a user has. + * @category User + * @alias getFriendCount + * @param { number } userId + * @returns Promise + * @example const noblox = require("noblox.js") + * const numberOfFriends = await noblox.getFriendCount(55549140) +**/ + +// Define +exports.func = function (userId) { + const httpOpt = { + url: `//friends.roblox.com/v1/users/${userId}/friends/count`, + options: { + json: true, + method: 'GET', + resolveWithFullResponse: true + } + } + + return http(httpOpt).then(function (res) { + if (res.statusCode === 200) { return res.body.count } + + throw new Error( + `Failed to retrieve friend count: (${res.statusCode}) ${res.body}` + ) + }) +} diff --git a/lib/index.js b/lib/index.js index 431ce54a4..68c2b4bf2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -77,8 +77,11 @@ noblox.onGroupTransaction = require('./economy/onGroupTransaction.js') noblox.acceptFriendRequest = require('./friends/acceptFriendRequest.js') noblox.declineAllFriendRequests = require('./friends/declineAllFriendRequests.js') noblox.declineFriendRequest = require('./friends/declineFriendRequest.js') +noblox.getFollowerCount = require('./friends/getFollowerCount.js') noblox.getFollowers = require('./friends/getFollowers.js') +noblox.getFollowingCount = require('./friends/getFollowingCount.js') noblox.getFollowings = require('./friends/getFollowings.js') +noblox.getFriendCount = require('./friends/getFriendCount.js') noblox.getFriendRequests = require('./friends/getFriendRequests.js') noblox.getFriends = require('./friends/getFriends.js') noblox.onFriendRequest = require('./friends/onFriendRequest.js') @@ -167,7 +170,9 @@ noblox.sendTrade = require('./trades/sendTrade.js') noblox.getBlurb = require('./users/getBlurb.js') noblox.getIdFromUsername = require('./users/getIdFromUsername.js') noblox.getPlayerInfo = require('./users/getPlayerInfo.js') +noblox.getUserInfo = require('./users/getUserInfo.js') noblox.getUsernameFromId = require('./users/getUsernameFromId.js') +noblox.getUsernameHistory = require('./users/getUsernameHistory.js') noblox.onBlurbChange = require('./users/onBlurbChange.js') noblox.searchUsers = require('./users/searchUsers.js') noblox.clearSession = require('./util/clearSession.js') diff --git a/lib/users/getPlayerInfo.js b/lib/users/getPlayerInfo.js index 0fe59c75f..4b0c3d6be 100644 --- a/lib/users/getPlayerInfo.js +++ b/lib/users/getPlayerInfo.js @@ -1,6 +1,9 @@ // Includes -const http = require('../util/http.js').func -const getPageResults = require('../util/getPageResults.js').func +const getFollowingCount = require('../friends/getFollowingCount.js').func +const getFollowerCount = require('../friends/getFollowerCount.js').func +const getFriendCount = require('../friends/getFriendCount.js').func +const getUserInfo = require('../users/getUserInfo.js').func +const getUsernameHistory = require('../users/getUsernameHistory.js').func // Args exports.required = ['userId'] @@ -20,11 +23,11 @@ exports.required = ['userId'] function getPlayerInfo (userId) { return new Promise((resolve, reject) => { const requests = [ - constructRequest(`//users.roblox.com/v1/users/${userId}`), - constructRequest(`//friends.roblox.com/v1/users/${userId}/friends/count`), - constructRequest(`//friends.roblox.com/v1/users/${userId}/followings/count`), - constructRequest(`//friends.roblox.com/v1/users/${userId}/followers/count`), - getPageResults({ url: `//users.roblox.com/v1/users/${userId}/username-history`, query: {}, limit: 1000 }) + getUserInfo(userId), + getFriendCount(userId), + getFollowingCount(userId), + getFollowerCount(userId), + getUsernameHistory({ userId }) ].map(promise => promise.then( val => ({ status: 'fulfilled', value: val }), rej => ({ status: 'rejected', reason: rej }) @@ -53,11 +56,11 @@ function getPlayerInfo (userId) { } else if (failedResponse) { reject(new Error('User does not exist.')) } else { - const responseBodies = responses.map(res => res.body) + const responseBodies = responses.map(res => res.body ?? res) const oldNames = responses[4].map(nameObject => nameObject.name) || [] - const friendCount = responseBodies[1].count - const followerCount = responseBodies[3].count - const followingCount = responseBodies[2].count + const friendCount = responseBodies[1] + const followerCount = responseBodies[3] + const followingCount = responseBodies[2] const joinDate = new Date(userBody.created) const blurb = userBody.description const isBanned = userBody.isBanned @@ -84,17 +87,6 @@ function getPlayerInfo (userId) { }) } -function constructRequest (url) { - return http({ - url, - options: { - resolveWithFullResponse: true, - followRedirect: false, - json: true - } - }) -} - exports.func = (args) => { return getPlayerInfo(args.userId) } diff --git a/lib/users/getUserInfo.js b/lib/users/getUserInfo.js new file mode 100644 index 000000000..edb94ab64 --- /dev/null +++ b/lib/users/getUserInfo.js @@ -0,0 +1,34 @@ +// Includes +const http = require('../util/http.js').func + +// Args +exports.required = ['userId'] + +// Docs +/** + * ✅ Get base-level user profile information + * @category User + * @alias getUserInfo + * @param { number } userId + * @returns {Promise} +**/ + +// Define +exports.func = function (userId) { + const httpOpt = { + url: `//users.roblox.com/v1/users/${userId}`, + options: { + json: true, + method: 'GET', + resolveWithFullResponse: true + } + } + + return http(httpOpt).then(function (res) { + if (res.statusCode !== 200) { throw new Error(`Failed to fetch user information: ${res.body.errors?.join(', ')}`) } + + res.body.created = new Date(res.body.created) + + return res.body + }) +} diff --git a/lib/users/getUsernameHistory.js b/lib/users/getUsernameHistory.js new file mode 100644 index 000000000..a62889e64 --- /dev/null +++ b/lib/users/getUsernameHistory.js @@ -0,0 +1,35 @@ +// Includes +const getPageResults = require('../util/getPageResults.js').func + +// Args +exports.required = ['userId'] +exports.optional = ['limit', 'sortOrder', 'pageCursor'] + +// Docs +/** + * ✅ Get a user's username history. + * @category User + * @alias getUsernameHistory + * @param {number} userId + * @param {Limit=} [limit=10] + * @param {SortOrder=} [sortOrder=Asc] + * @param {string} cursor + * @returns {Promise} + * @example const noblox = require("noblox.js") + * const history = await noblox.getUsernameHistory({ userId: 1, limit: 10, sortOrder: "Asc", cursor: "somecursorstring" }) +**/ + +// Define +function getUsernameHistory (userId, limit, sortOrder, cursor) { + return getPageResults({ + url: `//users.roblox.com/v1/users/${userId}/username-history`, + query: {}, + limit, + pageCursor: cursor, + sortOrder + }) +} + +exports.func = function (args) { + return getUsernameHistory(args.userId, args.limit, args.sortOrder, args.cursor) +} diff --git a/test/friends.test.js b/test/friends.test.js index fa734f5d0..69bced3bb 100644 --- a/test/friends.test.js +++ b/test/friends.test.js @@ -1,4 +1,4 @@ -const { acceptFriendRequest, declineAllFriendRequests, declineFriendRequest, getFollowers, getFollowings, getFriendRequests, getFriends, removeFriend, sendFriendRequest, unfollow, setCookie } = require('../lib') +const { acceptFriendRequest, declineAllFriendRequests, declineFriendRequest, getFollowerCount, getFollowers, getFollowingCount, getFollowings, getFriendCount, getFriendRequests, getFriends, removeFriend, sendFriendRequest, unfollow, setCookie } = require('../lib') beforeAll(() => { return new Promise(resolve => { @@ -32,6 +32,12 @@ describe('Friends Methods', () => { return await expect(unfollow(55549140)).resolves.not.toThrow() }) + it('getFollowerCount() returns the number of user\'s followers', () => { + return getFollowerCount(55549140).then((res) => { + return expect(res).toBeGreaterThanOrEqual(0) + }) + }) + it('getFollowers() returns a user\'s followers', () => { return getFollowers(55549140).then((res) => { return expect(res).toMatchObject({ @@ -48,6 +54,12 @@ describe('Friends Methods', () => { }) }) + it('getFollowingCount() returns the number of users that are following the user', () => { + return getFollowingCount(55549140).then((res) => { + return expect(res).toBeGreaterThanOrEqual(0) + }) + }) + it('getFollowings() returns which users are being followed by the specified user', () => { return getFollowings(55549140).then((res) => { return expect(res).toMatchObject({ @@ -64,6 +76,12 @@ describe('Friends Methods', () => { }) }) + it('getFriendCount() returns the number of friends the specified user has', () => { + return getFriendCount(55549140).then((res) => { + return expect(res).toBeGreaterThanOrEqual(0) + }) + }) + it('getFriendRequests() returns the logged in user\'s incoming friend requests', () => { return getFriendRequests().then((res) => { return expect(res).toMatchObject({ diff --git a/test/users.test.js b/test/users.test.js index 8f78ad0c2..e362f066b 100644 --- a/test/users.test.js +++ b/test/users.test.js @@ -1,4 +1,4 @@ -const { getBlurb, getIdFromUsername, getPlayerInfo, getUsernameFromId, searchUsers, setCookie } = require('../lib') +const { getBlurb, getIdFromUsername, getPlayerInfo, getUserInfo, getUsernameFromId, getUsernameHistory, searchUsers, setCookie } = require('../lib') beforeAll(() => { return new Promise(resolve => { @@ -60,22 +60,49 @@ describe('Users Methods', () => { }) }) + it('getUserInfo() returns profile information on the specified user', () => { + return getUserInfo(55549140).then((res) => { + return expect(res).toMatchObject({ + description: expect.any(String), + created: expect.any(Date), + isBanned: expect.any(Boolean), + externalAppDisplayName: expect.any(null), + hasVerifiedBadge: expect.any(Boolean), + id: expect.any(Number), + name: expect.any(String), + displayName: expect.any(String) + }) + }) + }) + it('getUsernameFromId() returns a player\'s username given an ID', () => { return getUsernameFromId(1).then((res) => { return expect(res).toEqual(expect.any(String)) }) }) + it('getUsernameHistory() returns a player\'s username history', () => { + return getUsernameHistory(55549140).then((res) => { + return expect(res).toMatchObject( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.any(String) + }) + ]) + ) + }) + }) + it('searchUsers() returns results for the specified keyword', () => { return searchUsers('roblox').then((res) => { return expect(res).toMatchObject( expect.arrayContaining([ expect.objectContaining({ - previousUsernames: expect.any(Array), - hasVerifiedBadge: expect.any(Boolean), - id: expect.any(Number), - name: expect.any(String), - displayName: expect.any(String) + previousUsernames: expect.any(Array), + hasVerifiedBadge: expect.any(Boolean), + id: expect.any(Number), + name: expect.any(String), + displayName: expect.any(String) }) ]) ) diff --git a/typings/index.d.ts b/typings/index.d.ts index d39a4eac7..83d4c203b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1096,6 +1096,10 @@ declare module "noblox.js" { displayName: string; } + interface UsernameHistoryEntry { + name: string; + } + interface PrivateMessageParent { page: number; } @@ -1144,6 +1148,17 @@ declare module "noblox.js" { oldNames?: string[]; isBanned: boolean; } + + interface UserInfo { + description: string; + created: Date; + isBanned: boolean; + hasVerifiedBadge: boolean; + id: number; + name: string; + displayName: string; + } + interface Presences { userPresences: UserPresence[] } @@ -1802,16 +1817,31 @@ declare module "noblox.js" { */ function declineFriendRequest(userId: number, jar?: CookieJar): Promise; + /** + * ✅ Gets the number of followers a user has. + */ + function getFollowerCount(userId: number): Promise; + /** * ✅ Get the followers of a user (users who follow the specified person) */ function getFollowers(userId: number, sortOrder?: SortOrder, limit?: Limit, cursor?: string, jar?: CookieJar): Promise; + /** + * ✅ Gets the number of followings a user has (users who have been followed by the specified person). + */ + function getFollowingCount(userId: number): Promise; + /** * ✅ Get the followings of a user (users who have been followed by the specified person) */ function getFollowings(userId: number, sortOrder?: SortOrder, limit?: Limit, cursor?: string, jar?: CookieJar): Promise; + /** + * ✅ Get the number of friends a user has. + */ + function getFriendCount(userId: number): Promise; + /** * 🔐 Gets the pending friend requests of the logged in user. */ @@ -2172,15 +2202,25 @@ declare module "noblox.js" { */ function getPlayerInfo(userId: number): Promise; + /** + * ✅ Gets basic user information. + */ + function getUserInfo(userId: number): Promise; + /** * ✅ Gets `username` of user with `id` and caches according to settings. */ function getUsernameFromId(id: number): Promise; + /** + * ✅ Gets a list of usernames the specified user has used. + */ + function getUsernameHistory(userId: number, limit?: Limit, sortOrder?: SortOrder, cursor?: string): Promise; + /** * ✅ Gets user search results for a keyword. */ - function searchUsers(keyword: string, limit: number, cursor: string): Promise; + function searchUsers(keyword: string, limit: number, cursor: string, jar?: CookieJar): Promise; /// Utility diff --git a/typings/jsDocs.ts b/typings/jsDocs.ts index 25d38ec33..eeab7e74a 100644 --- a/typings/jsDocs.ts +++ b/typings/jsDocs.ts @@ -1509,6 +1509,13 @@ type UserEntry = { displayName: string; } +/** + * @typedef +*/ +type UsernameHistoryEntry = { + name: string; +} + /** * @typedef */ @@ -1576,6 +1583,19 @@ type PlayerInfo = { isBanned: boolean; } +/** + * @typedef +*/ +type UserInfo = { + description: string; + created: Date; + isBanned: boolean; + hasVerifiedBadge: boolean; + id: number; + name: string; + displayName: string; +} + /** * @typedef */