Skip to content

Commit d5c9120

Browse files
fixed issues with cloud group api and csrf tokens
1 parent 289645e commit d5c9120

14 files changed

Lines changed: 236 additions & 409 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,6 @@ dist
176176
# Finder (MacOS) folder config
177177
.DS_Store
178178

179-
docs_site/
179+
docs_site/
180+
181+
/graph.png

build.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import * as esbuild from 'esbuild';
2+
import { globby } from 'globby';
3+
4+
const entryPoints = await globby(['src/**/*.{js,ts}']);
25

36
const settings = {
4-
entryPoints: ["src/**/*"],
7+
entryPoints,
58
outdir: "dist",
69
//minify: true,
710
//keepNames: true,

bun.lockb

4.43 KB
Binary file not shown.

package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "openblox",
33
"description": "Roblox API Wrapper For Both Classic And OpenCloud APIs.",
44
"type": "commonjs",
5-
"version": "1.0.59",
5+
"version": "1.0.60",
66
"license": "MIT",
77
"bugs": {
88
"url": "https://github.com/MightyPart/openblox/issues"
@@ -95,12 +95,13 @@
9595
"devDependencies": {
9696
"@types/lodash": "^4.17.0",
9797
"@types/node": "^22.1.0",
98-
"esbuild": "^0.21.5",
98+
"delete-cli": "^0.1.3",
99+
"esbuild": "^0.25.3",
100+
"globby": "^14.1.0",
99101
"prettier": "^3.2.5",
100102
"tablemark": "^3.1.0",
101103
"ts-arithmetic": "^0.1.1",
102-
"ts-morph": "^22.0.0",
103-
"delete-cli": "^0.1.3"
104+
"ts-morph": "^22.0.0"
104105
},
105106
"peerDependencies": {
106107
"typescript": "^5.0.0",
@@ -109,6 +110,10 @@
109110
"scripts": {
110111
"build:code": "delete dist && bun run ./build.ts && tsc --emitDeclarationOnly",
111112
"build:docs": "bun run ./docs/buildDocs.ts",
113+
"graph": "madge --image graph.png ./src/**/*",
114+
"graph:circular": "madge --image graph.png --circular ./src/**/*",
115+
"graph:dist": "madge --image graph.png ./dist/**/*",
116+
"graph:dist:circular": "madge --image graph.png --circular ./dist/**/*",
112117
"build": "bun run build:docs && bun run build:code"
113118
}
114119
}

src/apis/apiGroup/apiGroup.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// [ Modules ] ///////////////////////////////////////////////////////////////////
2-
import { config } from "../../config";
2+
import { defaultOpenbloxConfig, OpenbloxConfig } from "../../config";
33
import { HttpHandler, isOpenCloudUrl } from "../../http/httpHandler";
44
import { isObject, objectToFieldMask } from "../../utils/utils";
55
import { HttpResponse } from "../../http/http.utils";
@@ -68,7 +68,7 @@ const isCursorEmpty = (cursor: Cursor) => (!cursor || (typeof cursor == "string"
6868
const paginate = (
6969
initialResponse: ApiMethodResponse,
7070
callApiMethod: CallApiMethod<any, any, true>,
71-
args: Record<any, any>, overrides: any,
71+
args: Record<any, any>, config: any,
7272
handlerFnCursorArg: "cursor" | "startRowIndex" | "pageNumber"
7373
) => (
7474
async function* () {
@@ -79,7 +79,7 @@ const paginate = (
7979
if (isCursorEmpty(nextCursor)) return
8080

8181
while (true) {
82-
const newValue = await callApiMethod.call(overrides, { ...args, [handlerFnCursorArg]: nextCursor })
82+
const newValue = await callApiMethod.call(config, { ...args, [handlerFnCursorArg]: nextCursor })
8383
if (isNoMoreData(newValue.data)) return
8484
yield newValue
8585

@@ -89,7 +89,7 @@ const paginate = (
8989
}
9090
)
9191

92-
const pollForResponse = async (url: string, operationPath: string, cloudKey: string) => {
92+
const pollForResponse = async (config: OpenbloxConfig, url: string, operationPath: string, cloudKey: string) => {
9393
const operationPrefix = operationPath.match(/^(\/?)cloud\/v[1-9]+(\/?)/)
9494
? operationPrefixRegexWithoutVersion.exec(url)?.[1] as UrlSecure
9595
: operationPrefixRegexWithVersion.exec(url)?.[1] as UrlSecure
@@ -121,11 +121,11 @@ export const createApiGroup: CreateApiGroupFn = ({ name:groupName, baseUrl, defa
121121

122122
const thisDefaultGetCursors = groupDefaultGetCursors ?? defaultGetCursors
123123

124-
const createCallApiMethod = (_baseUrl: UrlSecure = baseUrl): CallApiMethod<any, any, boolean> => async function(args) {
125-
const overrides = this
126-
const cookie = overrides?.cookie || config?.cookie
127-
const cloudKey = overrides?.cloudKey || config?.cloudKey
128-
const oauthToken = overrides?.oauthToken
124+
const createCallApiMethod = (_baseUrl: UrlSecure = baseUrl): CallApiMethod<any, any, boolean> => async function (args) {
125+
const config = this || defaultOpenbloxConfig
126+
const cookie = config?.cookie
127+
const cloudKey = config?.cloudKey
128+
const oauthToken = config?.oauthToken
129129

130130
const handlerFnData = await handlerFn(args as any)
131131
let { path, method, searchParams, applyFieldMask, body, formData, headers, getCursorsFn, pathToPoll, name } = handlerFnData
@@ -147,29 +147,37 @@ export const createApiGroup: CreateApiGroupFn = ({ name:groupName, baseUrl, defa
147147

148148
let main: () => Promise<any>
149149
main = async () => {
150-
let response: HttpResponse = await HttpHandler({ method, url, body, formData, headers }) as any // TODO
150+
let response: HttpResponse = await HttpHandler(config, { method, url, body, formData, headers }) as any // TODO
151151
if (!(response instanceof HttpResponse)) throw response
152152
let rawData = response.body
153153

154154
// Uncompleted long running operation.
155155
let opPath = rawData?.path
156156
if (opPath && rawData?.done === false && isOpenCloudUrl(url)) {
157157
console.warn(`Polling '${groupName}.${name}' (Please be patient)...`)
158-
response = await pollForResponse(url, pathToPoll ? pathToPoll(rawData) : opPath, cloudKey)
158+
response = await pollForResponse(config, url, pathToPoll ? pathToPoll(rawData) : opPath, cloudKey)
159159
rawData = response.body
160160
}
161161

162+
let cachedData: any
163+
162164
let apiMethodResult: ApiMethodResponse<any, any> = formatRawDataFn
163-
? { response, again: main, get data() { return formatRawDataFn(rawData, response) } }
164-
: { response, again: main, data: rawData }
165+
? { response, again: main, get data() {
166+
if (cachedData) return cachedData
167+
else {
168+
cachedData = formatRawDataFn(rawData, response)
169+
return cachedData
170+
}
171+
}, configUsed: config }
172+
: { response, again: main, data: rawData, configUsed: config }
165173

166174
// Applies async iterator if method is paginated.
167175
if (handlerFnCursorArg) {
168176
let [ previousCursor, nextCursor ] = (getCursorsFn ?? thisDefaultGetCursors)(rawData);
169177
apiMethodResult.cursors = { previous: previousCursor, next: nextCursor }
170178
if (args && !("__notRoot" in args)) {
171179
(apiMethodResult as any as ApiMethodResponse<any, any, true>)[Symbol.asyncIterator] = paginate(
172-
apiMethodResult, callApiMethod as CallApiMethod<any, any, true>, args as Record<any, any>, overrides, handlerFnCursorArg
180+
apiMethodResult, callApiMethod as CallApiMethod<any, any, true>, args as Record<any, any>, config, handlerFnCursorArg
173181
) as any
174182
}
175183
}
@@ -180,7 +188,7 @@ export const createApiGroup: CreateApiGroupFn = ({ name:groupName, baseUrl, defa
180188
return await main()
181189
}
182190

183-
const callApiMethod = createCallApiMethod();
191+
const callApiMethod = createCallApiMethod().bind(undefined);
184192

185193
// To handle legacy open cloud endpoints which use classic endpoints but with different base urls.
186194
(callApiMethod as any)._deriveWithDifferentBaseUrl = (baseUrl: UrlSecure) => createCallApiMethod(baseUrl)

src/apis/apiGroup/apiGroup.types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ObjectPrettify, Prettify, StringIsLiteral } from "typeforge"
22
import type { RestMethod } from "../../utils/utils.types"
33
import type { HttpResponse, RobloxCookie } from "../../http/http.utils"
4+
import { OpenbloxConfig } from "../../config"
45

56

67
// Create Api Group Types ---------------------------------------------------------------------------------------------
@@ -113,7 +114,8 @@ export type ApiMethodResponse<
113114
type ApiMethodResponse_WithoutPagination<RawData = any, PrettifiedData = any> = ObjectPrettify<
114115
{
115116
data: PrettifiedData,
116-
response: ObjectPrettify<HttpResponse<RawData>>
117+
response: ObjectPrettify<HttpResponse<RawData>>,
118+
configUsed: OpenbloxConfig,
117119
}
118120
>
119121

src/apis/classic/users/users.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export const userInfo = createApiMethod(async <UserId extends Identifier>(
185185
* @exampleData { id: 45348281, name: "MightyPart", displayName: "MightyPart" }
186186
* @exampleRawBody { id: 45348281, name: "MightyPart", displayName: "MightyPart" }
187187
*/
188-
export const authenticatedUserInfo = createApiMethod(async (
188+
export const authenticatedUserInfo = createApiMethod(async (
189189
): ApiMethod<AuthenticatedUserInfoData> => ({
190190
path: `/v1/users/authenticated`,
191191
method: "GET",

src/apis/cloud/groups/groups.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const formatGroupMembersFilters = (groupId: Identifier | "-", filter: GroupMembe
3030

3131
else {
3232
const roleIdFilter = (filter as unknown as GroupMembers_Filter)?.roleId
33-
if (roleIdFilter) formattedFilter = `group == 'groups/${groupId}/roles/${roleIdFilter}'`
33+
if (roleIdFilter) formattedFilter = `role == 'groups/${groupId}/roles/${roleIdFilter}'`
3434
}
3535
}
3636

@@ -88,11 +88,17 @@ export const groupMembers = createApiMethod(async <
8888
: "userId" extends keyof Filter
8989
? Filter["userId"] extends Identifier ? Filter["userId"] : Identifier
9090
: Identifier
91+
),
92+
93+
const RoleId extends Identifier = (
94+
"roleId" extends keyof Filter
95+
? Filter["roleId"] extends Identifier ? Filter["roleId"] : Identifier
96+
: Identifier
9197
)
9298
>(
9399
{ groupId, limit, filter, cursor }
94100
: { groupId: GroupId, limit?: UserId, filter?: Filter, cursor?: string }
95-
): ApiMethod<RawGroupMembersData<GroupId, UserId>, PrettifiedGroupMembersData<GroupId, UserId>> => ({
101+
): ApiMethod<RawGroupMembersData<GroupId, UserId>, PrettifiedGroupMembersData<GroupId, UserId, RoleId>> => ({
96102
path: `/v2/groups/${groupId}/memberships`,
97103
method: "GET",
98104
searchParams: {

src/apis/cloud/groups/groups.types.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,29 @@ export type GroupMembers_Filter = ObjectEither<
4747

4848
export type GroupMembers_WildcardFilter = ObjectPrettify<{ userIds: Identifier[] }>
4949

50-
type GroupMember<GroupId extends Identifier | "-", TemporalType, UserId extends Identifier = Identifier> = ObjectPrettify<{
50+
type GroupMember<
51+
GroupId extends Identifier | "-",
52+
TemporalType,
53+
UserId extends Identifier = Identifier,
54+
RoleId extends Identifier = Identifier,
55+
> = ObjectPrettify<{
5156
path: `groups/${GroupId extends "-" ? Identifier : GroupId}/memberships/${string}`,
5257
createTime: TemporalType,
5358
updateTime: TemporalType,
5459
user: `users/${UserId}`,
55-
role: `groups/${GroupId extends "-" ? Identifier : GroupId}/roles/${string}`
60+
role: `groups/${GroupId extends "-" ? Identifier : GroupId}/roles/${RoleId}`
5661
}>
5762

5863
export type RawGroupMembersData<GroupId extends Identifier | "-", UserId extends Identifier = Identifier> = ObjectPrettify<{
5964
groupMemberships: GroupMember<GroupId, ISODateTime, UserId>[],
6065
nextPageToken: string
6166
}>
6267

63-
export type PrettifiedGroupMembersData<GroupId extends Identifier | "-", UserId extends Identifier = Identifier> = GroupMember<GroupId, Date, UserId>[]
68+
export type PrettifiedGroupMembersData<
69+
GroupId extends Identifier | "-",
70+
UserId extends Identifier = Identifier,
71+
RoleId extends Identifier = Identifier,
72+
> = GroupMember<GroupId, Date, UserId, RoleId>[]
6473
// -------------------------------------------------------------------------------------------------------------------
6574

6675

src/config/config.ts

Lines changed: 39 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,67 @@
11
// [ Modules ] ///////////////////////////////////////////////////////////////////
2-
import { isObject } from "lodash"
3-
4-
import { mergeDeep } from "../utils/utils"
52
import { TtlCacheAdapter } from "../cache/cacheAdapters"
3+
import { RobloxCookie } from "../http/http.utils"
64
//////////////////////////////////////////////////////////////////////////////////
75

86

97
// [ Types ] /////////////////////////////////////////////////////////////////////
108
import type { HttpAdapter } from "../http/httpAdapters"
119
import type { CacheAdapter } from "../cache/cacheAdapters/cacheAdapters"
12-
import type { RobloxCookie } from "../http/http.utils"
13-
14-
export type OpenbloxConfig = {
15-
cookie?: RobloxCookie,
16-
cloudKey?: string,
17-
18-
http?: {
19-
adapter?: HttpAdapter,
20-
csrfMaxAttempts?: number,
21-
22-
polling?: {
23-
disabled?: boolean,
24-
iterations?: number,
25-
multiplyer?: number,
26-
retryOffset?: number,
27-
debugMessages?: boolean
10+
import type { ObjectPrettify } from "typeforge"
11+
12+
export type OpenbloxConfig<Cookie = FormattedRobloxCookie> = ObjectPrettify<{
13+
cookie?: Cookie,
14+
cloudKey?: string,
15+
16+
http?: {
17+
adapter?: HttpAdapter,
18+
csrfMaxAttempts?: number,
19+
csrfToken?: string,
20+
21+
polling?: {
22+
disabled?: boolean,
23+
iterations?: number,
24+
multiplyer?: number,
25+
retryOffset?: number,
26+
debugMessages?: boolean
27+
}
2828
},
29-
},
3029

31-
cache?: ReturnType<CacheAdapter<any, any>>[]
32-
}
30+
cache?: ReturnType<CacheAdapter<any, any>>[]
31+
}>
3332
//////////////////////////////////////////////////////////////////////////////////
3433

3534

35+
const formatRobloxCookie = (cookie: string) => `.ROBLOSECURITY=${cookie}; RBXEventTrackerV2=CreateDate=1/1/1 1:1:1 PM&rbxid=1&browserid=1;` as FormattedRobloxCookie & { _phantom_isValid: true }
36+
3637
// [ Variables ] /////////////////////////////////////////////////////////////////
3738
const initialCookie = process.env.ROBLOX_COOKIE
3839

39-
export const config: OpenbloxConfig = {
40-
cookie: (initialCookie && `.ROBLOSECURITY=${initialCookie}; RBXEventTrackerV2=CreateDate=1/1/1 1:1:1 PM&rbxid=1&browserid=1;`) as any as RobloxCookie | undefined,
41-
cloudKey: process.env.ROBLOX_CLOUD_KEY,
40+
export const defaultOpenbloxConfig: OpenbloxConfig = {
41+
cookie: initialCookie ? formatRobloxCookie(initialCookie) : undefined,
42+
cloudKey: process.env.ROBLOX_CLOUD_KEY,
4243

43-
cache: [ TtlCacheAdapter({ included: { lifetime: 300 } }) ]
44+
cache: [ TtlCacheAdapter({ included: { lifetime: 300 } }) ],
45+
46+
http: {}
4447
};
4548
//////////////////////////////////////////////////////////////////////////////////
4649

4750

48-
// [ Private Functions ] /////////////////////////////////////////////////////////
49-
/**
50-
* Deep merge two configs.
51-
* @param target
52-
* @param ...sources
53-
*/
54-
function mergeDeepConfigs(target: Record<any, any>, ...sources: Record<any, any>[]) {
55-
if (!sources.length) return target;
56-
const source = sources.shift();
57-
58-
if (isObject(target) && isObject(source)) {
59-
for (const key in source) {
60-
if (isObject(source[key])) {
61-
if (!target[key]) Object.assign(target, { [key]: {} });
62-
if (key == "cache") {
63-
Object.assign(target[key], source[key])
64-
} else {
65-
mergeDeep(target[key], source[key]);
66-
}
67-
} else {
68-
Object.assign(target, { [key]: source[key] });
69-
}
70-
}
71-
}
51+
export type FormattedRobloxCookie = `.ROBLOSECURITY=${RobloxCookie}; RBXEventTrackerV2=CreateDate=1/1/1 1:1:1 PM&rbxid=1&browserid=1;`
7252

73-
return mergeDeep(target, ...sources);
53+
export const setDefaultOpenbloxConfig = (newConfig: OpenbloxConfig & { _phantom_isValid: true }) => {
54+
for (var key in defaultOpenbloxConfig) { delete defaultOpenbloxConfig[key as keyof OpenbloxConfig] }
55+
Object.assign(defaultOpenbloxConfig, newConfig)
7456
}
75-
//////////////////////////////////////////////////////////////////////////////////
7657

58+
export const createOpenbloxConfig = (newConfig: OpenbloxConfig<RobloxCookie>) => {
59+
if (!newConfig?.http) newConfig.http = {}
7760

78-
export const setConfig = (newConfig: OpenbloxConfig) => {
79-
const newConfigCookie = newConfig?.cookie
80-
if (newConfigCookie) newConfig.cookie = `.ROBLOSECURITY=${newConfigCookie}; RBXEventTrackerV2=CreateDate=1/1/1 1:1:1 PM&rbxid=1&browserid=1;` as any
81-
82-
Object.keys(config).forEach(key => delete config[key as keyof OpenbloxConfig])
83-
Object.assign(config, newConfig)
84-
}
61+
const newConfigCookie = newConfig?.cookie
62+
if (newConfigCookie) newConfig.cookie = formatRobloxCookie(newConfigCookie) as any
8563

86-
export const updateConfig = (updateConfigWith: OpenbloxConfig) => {
87-
const updateConfigWithCookie = updateConfigWith?.cookie
88-
if (updateConfigWithCookie) updateConfigWith.cookie = `.ROBLOSECURITY=${updateConfigWithCookie}; RBXEventTrackerV2=CreateDate=1/1/1 1:1:1 PM&rbxid=1&browserid=1;` as any
64+
console.log("-->", newConfig)
8965

90-
mergeDeepConfigs(config, updateConfigWith)
66+
return newConfig as OpenbloxConfig & { _phantom_isValid: true }
9167
}

0 commit comments

Comments
 (0)