@libertytools/libertyjs 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +32 -27
  2. package/readme.md +363 -358
  3. package/src/index.js +308 -308
package/src/index.js CHANGED
@@ -1,309 +1,309 @@
1
- export default class LibertyJS {
2
- #SERVER_KEY;
3
- #PRIVATE_SERVER_API;
4
- #useWebhook;
5
- #WEBHOOK_URL;
6
- #WEBHOOK_TOKEN;
7
- #rateLimits;
8
- #resStatus;
9
-
10
- constructor({
11
- SERVER_KEY,
12
- PRIVATE_SERVER_API = "https://api.policeroleplay.community/v2/",
13
- WEBHOOK_URL,
14
- WEBHOOK_TOKEN
15
- } = {}) {
16
- if (!SERVER_KEY) {
17
- throw new Error("[LibertyJS]: SERVER_KEY is required");
18
- }
19
-
20
- this.#SERVER_KEY = SERVER_KEY;
21
- this.#PRIVATE_SERVER_API = PRIVATE_SERVER_API;
22
-
23
- if (WEBHOOK_URL && !WEBHOOK_TOKEN) {
24
- throw new Error("[LibertyJS]: WEBHOOK_TOKEN is required if you are using a webhook");
25
- }
26
-
27
- this.#useWebhook = Boolean(WEBHOOK_URL && WEBHOOK_TOKEN);
28
-
29
- if (WEBHOOK_URL && WEBHOOK_TOKEN) {
30
- this.#WEBHOOK_URL = WEBHOOK_URL;
31
- this.#WEBHOOK_TOKEN = WEBHOOK_TOKEN;
32
- }
33
-
34
- this.#rateLimits = {
35
- get: { limit: null, remaining: null, reset: null },
36
- post: { limit: null, remaining: null, reset: null }
37
- };
38
-
39
- this.#resStatus = {
40
- forbiddenErrors: 0
41
- };
42
- }
43
-
44
- #wait(seconds) {
45
- return new Promise(resolve => setTimeout(resolve, seconds * 1000));
46
- }
47
-
48
- async #fetchAPI(url, options = {}) {
49
- if (!this.#SERVER_KEY) {
50
- return {
51
- error: "invalid_env",
52
- message: "[LibertyJS]: SERVER_KEY was not provided in a .env file"
53
- };
54
- }
55
-
56
- if (this.#resStatus.forbiddenErrors >= 2) {
57
- return {
58
- error: "forbidden",
59
- message: "[LibertyJS]: Received a 403 error 2 times, suspending API calls as the server key may be invalid"
60
- };
61
- }
62
-
63
- const isPRC = url.startsWith("https://api.policeroleplay.community/");
64
- const method = (options.method || "GET").toUpperCase();
65
-
66
- const headers = { ...(options.headers || {}) };
67
-
68
- let body = options.body;
69
- if (method === "POST" && body !== undefined) {
70
- headers["Content-Type"] = "application/json";
71
- body = typeof body === "string" ? body : JSON.stringify(body);
72
- }
73
-
74
- if (isPRC) {
75
- headers["server-key"] = this.#SERVER_KEY;
76
- }
77
-
78
- const currentTime = Math.floor(Date.now() / 1000);
79
-
80
- const handleRateLimit = async (rl) => {
81
- if (rl.reset && rl.remaining === 0 && currentTime < rl.reset) {
82
- const seconds = Math.max(0, rl.reset - currentTime);
83
- console.log(`[LibertyJS]: Rate limited (${method}). Waiting ${seconds}s`);
84
- await this.#wait(seconds);
85
- }
86
- };
87
-
88
- if (isPRC) {
89
- if (method === "GET") await handleRateLimit(this.#rateLimits.get);
90
- if (method === "POST") await handleRateLimit(this.#rateLimits.post);
91
- }
92
-
93
- const res = await fetch(url, {
94
- ...options,
95
- method,
96
- headers,
97
- body
98
- });
99
-
100
- let data;
101
- try {
102
- data = await res.json();
103
- } catch {
104
- data = null;
105
- }
106
-
107
- if (!res.ok) {
108
- if (res.status === 403 && isPRC) {
109
- this.#resStatus.forbiddenErrors++;
110
- }
111
-
112
- return {
113
- error: "api-error",
114
- message: `[LibertyJS]: Encountered an error while attempting to fetch ${url}`,
115
- apiResponse: data
116
- };
117
- }
118
-
119
- if (isPRC) {
120
- const target = method === "GET"
121
- ? this.#rateLimits.get
122
- : this.#rateLimits.post;
123
-
124
- target.reset = Number(res.headers.get("X-RateLimit-Reset"));
125
- target.limit = Number(res.headers.get("X-RateLimit-Limit"));
126
- target.remaining = Number(res.headers.get("X-RateLimit-Remaining"));
127
- }
128
-
129
- return data;
130
- }
131
-
132
- async getPrivateServerAPI(options = [], includeInvalid = false) {
133
- const valid = [
134
- "Players",
135
- "Staff",
136
- "JoinLogs",
137
- "Queue",
138
- "KillLogs",
139
- "CommandLogs",
140
- "ModCalls",
141
- "EmergencyCalls",
142
- "Vehicles"
143
- ];
144
-
145
- if (!Array.isArray(options)) {
146
- console.error("[LibertyJS.getPrivateServerAPI]: Options must be an array");
147
- return {
148
- error: "invalid_input",
149
- message: "[LibertyJS.getPrivateServerAPI]: Options must be an array"
150
- };
151
- }
152
-
153
- const params = [];
154
- const invalidOptions = [];
155
-
156
- for (const opt of options) {
157
- if (valid.includes(opt)) {
158
- params.push(`${opt}=true`);
159
- } else {
160
- console.log(`[LibertyJS]: Invalid option "${String(opt)}"`);
161
- invalidOptions.push(opt);
162
- }
163
- }
164
-
165
- const query = params.length ? `?${params.join("&")}` : "";
166
- const url = this.#PRIVATE_SERVER_API + "server" + query;
167
-
168
- if (!includeInvalid) {
169
- return await this.#fetchAPI(url);
170
- }
171
-
172
- return {
173
- data: await this.#fetchAPI(url),
174
- invalidOptions
175
- };
176
- }
177
-
178
- async sendPrivateServerCommand(options = []) {
179
- const url = this.#PRIVATE_SERVER_API + "server/command";
180
-
181
- const valid = {
182
- ":wanted": ["Player"],
183
- ":time": ["Number"],
184
- ":stopfire": [],
185
- ":respawn": ["Player"],
186
- ":tp": ["Player", "Player"],
187
- ":startnearfire": ["String"],
188
- ":jail": ["Player"],
189
- ":pt": ["Number"],
190
- ":h": ["String"],
191
- ":m": ["String"],
192
- ":pm": ["Player", "String"],
193
- ":refresh": ["Player"],
194
- ":bring": ["Player"],
195
- ":heal": ["Player"],
196
- ":kick": ["Player", "String"],
197
- ":startfire": ["String"],
198
- ":unwanted": ["Player"],
199
- ":prty": ["Number"],
200
- ":stopdumpsterfire": [],
201
- ":helper": ["Player/UserId"],
202
- ":shutdown": [],
203
- ":weather": ["String"],
204
- ":unmod": ["Player/UserId"],
205
- ":unloadlayout": ["String"],
206
- ":unban": ["String"],
207
- ":mod": ["Player/UserId"],
208
- ":ban": ["Player/UserId"],
209
- ":unhelper": ["Player/UserId"],
210
- ":log": ["String"],
211
- ":kill": ["Player"],
212
- ":unadmin": ["Player/UserId"],
213
- ":admin": ["Player/UserId"],
214
- ":loadlayout": ["String"]
215
- };
216
-
217
- if (!Array.isArray(options)) {
218
- console.error("[LibertyJS.sendPrivateServerCommand]: Options must be an array");
219
- return {
220
- error: "invalid_input",
221
- message: "[LibertyJS.sendPrivateServerCommand]: Options must be an array"
222
- };
223
- }
224
-
225
- if (options.length === 0) {
226
- console.error("[LibertyJS.sendPrivateServerCommand]: Options must have at least one item");
227
- return {
228
- error: "invalid_input",
229
- message: "[LibertyJS.sendPrivateServerCommand]: Options must have at least one item"
230
- };
231
- }
232
-
233
- const commands = [];
234
-
235
- for (const cmd of options) {
236
- if (!cmd.startsWith(":")) continue;
237
-
238
- const parts = cmd.trim().split(" ");
239
- const name = parts[0];
240
- const args = parts.slice(1);
241
- const schema = valid[name];
242
-
243
- if (!schema) {
244
- console.error(`[LibertyJS]: Unsupported command "${name}"`);
245
- continue;
246
- }
247
-
248
- const validArgs =
249
- (schema.at(-1) === "String" && args.length >= schema.length) ||
250
- (schema.at(-1) !== "String" && args.length === schema.length);
251
-
252
- if (validArgs) {
253
- commands.push(cmd);
254
- } else {
255
- console.log(`[LibertyJS]: "${name}" requires ${schema.length}${schema.at(-1) === "String" ? "+" : ""} args`);
256
- }
257
- }
258
-
259
- let successes = 0;
260
- let failures = 0;
261
- const failureReasons = [];
262
-
263
- for (const command of commands) {
264
- const res = await this.#fetchAPI(url, {
265
- method: "POST",
266
- body: command
267
- });
268
-
269
- if (!res?.error) {
270
- successes++;
271
- } else {
272
- failures++;
273
- failureReasons.push({
274
- command,
275
- apiResponse: res.apiResponse
276
- });
277
- }
278
- }
279
-
280
- return { successes, failures, failureReasons };
281
- }
282
-
283
- webhook = {
284
- status: async () => {
285
- if (!this.#useWebhook) {
286
- return {
287
- error: "webhook_disabled",
288
- message: "[LibertyJS.webhook.status]: Webhook is not configured"
289
- };
290
- }
291
-
292
- const url = `${this.#WEBHOOK_URL}health`;
293
-
294
- return await this.#fetchAPI(url, { method: "GET" });
295
- },
296
- events: async () => {
297
- if (!this.#useWebhook) {
298
- return {
299
- error: "webhook_disabled",
300
- message: "[LibertyJS.webhook.events]: Webhook is not configured"
301
- };
302
- }
303
-
304
- const url = `${this.#WEBHOOK_URL}webhook/${this.#WEBHOOK_TOKEN}/events`;
305
-
306
- return await this.#fetchAPI(url, { method: "GET" });
307
- }
308
- };
1
+ export default class LibertyJS {
2
+ #SERVER_KEY;
3
+ #PRIVATE_SERVER_API;
4
+ #useWebhook;
5
+ #WEBHOOK_URL;
6
+ #WEBHOOK_TOKEN;
7
+ #rateLimits;
8
+ #resStatus;
9
+
10
+ constructor({
11
+ SERVER_KEY,
12
+ PRIVATE_SERVER_API = "https://api.policeroleplay.community/v2/",
13
+ WEBHOOK_URL,
14
+ WEBHOOK_TOKEN
15
+ } = {}) {
16
+ if (!SERVER_KEY) {
17
+ throw new Error("[LibertyJS]: SERVER_KEY is required");
18
+ }
19
+
20
+ this.#SERVER_KEY = SERVER_KEY;
21
+ this.#PRIVATE_SERVER_API = PRIVATE_SERVER_API;
22
+
23
+ if (WEBHOOK_URL && !WEBHOOK_TOKEN) {
24
+ throw new Error("[LibertyJS]: WEBHOOK_TOKEN is required if you are using a webhook");
25
+ }
26
+
27
+ this.#useWebhook = Boolean(WEBHOOK_URL && WEBHOOK_TOKEN);
28
+
29
+ if (WEBHOOK_URL && WEBHOOK_TOKEN) {
30
+ this.#WEBHOOK_URL = WEBHOOK_URL;
31
+ this.#WEBHOOK_TOKEN = WEBHOOK_TOKEN;
32
+ }
33
+
34
+ this.#rateLimits = {
35
+ get: { limit: null, remaining: null, reset: null },
36
+ post: { limit: null, remaining: null, reset: null }
37
+ };
38
+
39
+ this.#resStatus = {
40
+ forbiddenErrors: 0
41
+ };
42
+ }
43
+
44
+ #wait(seconds) {
45
+ return new Promise(resolve => setTimeout(resolve, seconds * 1000));
46
+ }
47
+
48
+ async #fetchAPI(url, options = {}) {
49
+ if (!this.#SERVER_KEY) {
50
+ return {
51
+ error: "invalid_env",
52
+ message: "[LibertyJS]: SERVER_KEY was not provided in a .env file"
53
+ };
54
+ }
55
+
56
+ if (this.#resStatus.forbiddenErrors >= 2) {
57
+ return {
58
+ error: "forbidden",
59
+ message: "[LibertyJS]: Received a 403 error 2 times, suspending API calls as the server key may be invalid"
60
+ };
61
+ }
62
+
63
+ const isPRC = url.startsWith("https://api.policeroleplay.community/");
64
+ const method = (options.method || "GET").toUpperCase();
65
+
66
+ const headers = { ...(options.headers || {}) };
67
+
68
+ let body = options.body;
69
+ if (method === "POST" && body !== undefined) {
70
+ headers["Content-Type"] = "application/json";
71
+ body = typeof body === "string" ? body : JSON.stringify(body);
72
+ }
73
+
74
+ if (isPRC) {
75
+ headers["server-key"] = this.#SERVER_KEY;
76
+ }
77
+
78
+ const currentTime = Math.floor(Date.now() / 1000);
79
+
80
+ const handleRateLimit = async (rl) => {
81
+ if (rl.reset && rl.remaining === 0 && currentTime < rl.reset) {
82
+ const seconds = Math.max(0, rl.reset - currentTime);
83
+ console.log(`[LibertyJS]: Rate limited (${method}). Waiting ${seconds}s`);
84
+ await this.#wait(seconds);
85
+ }
86
+ };
87
+
88
+ if (isPRC) {
89
+ if (method === "GET") await handleRateLimit(this.#rateLimits.get);
90
+ if (method === "POST") await handleRateLimit(this.#rateLimits.post);
91
+ }
92
+
93
+ const res = await fetch(url, {
94
+ ...options,
95
+ method,
96
+ headers,
97
+ body
98
+ });
99
+
100
+ let data;
101
+ try {
102
+ data = await res.json();
103
+ } catch {
104
+ data = null;
105
+ }
106
+
107
+ if (!res.ok) {
108
+ if (res.status === 403 && isPRC) {
109
+ this.#resStatus.forbiddenErrors++;
110
+ }
111
+
112
+ return {
113
+ error: "api-error",
114
+ message: `[LibertyJS]: Encountered an error while attempting to fetch ${url}`,
115
+ apiResponse: data
116
+ };
117
+ }
118
+
119
+ if (isPRC) {
120
+ const target = method === "GET"
121
+ ? this.#rateLimits.get
122
+ : this.#rateLimits.post;
123
+
124
+ target.reset = Number(res.headers.get("X-RateLimit-Reset"));
125
+ target.limit = Number(res.headers.get("X-RateLimit-Limit"));
126
+ target.remaining = Number(res.headers.get("X-RateLimit-Remaining"));
127
+ }
128
+
129
+ return data;
130
+ }
131
+
132
+ async getPrivateServerAPI(options = [], includeInvalid = false) {
133
+ const valid = [
134
+ "Players",
135
+ "Staff",
136
+ "JoinLogs",
137
+ "Queue",
138
+ "KillLogs",
139
+ "CommandLogs",
140
+ "ModCalls",
141
+ "EmergencyCalls",
142
+ "Vehicles"
143
+ ];
144
+
145
+ if (!Array.isArray(options)) {
146
+ console.error("[LibertyJS.getPrivateServerAPI]: Options must be an array");
147
+ return {
148
+ error: "invalid_input",
149
+ message: "[LibertyJS.getPrivateServerAPI]: Options must be an array"
150
+ };
151
+ }
152
+
153
+ const params = [];
154
+ const invalidOptions = [];
155
+
156
+ for (const opt of options) {
157
+ if (valid.includes(opt)) {
158
+ params.push(`${opt}=true`);
159
+ } else {
160
+ console.log(`[LibertyJS]: Invalid option "${String(opt)}"`);
161
+ invalidOptions.push(opt);
162
+ }
163
+ }
164
+
165
+ const query = params.length ? `?${params.join("&")}` : "";
166
+ const url = this.#PRIVATE_SERVER_API + "server" + query;
167
+
168
+ if (!includeInvalid) {
169
+ return await this.#fetchAPI(url);
170
+ }
171
+
172
+ return {
173
+ data: await this.#fetchAPI(url),
174
+ invalidOptions
175
+ };
176
+ }
177
+
178
+ async sendPrivateServerCommand(options = []) {
179
+ const url = this.#PRIVATE_SERVER_API + "server/command";
180
+
181
+ const valid = {
182
+ ":wanted": ["Player"],
183
+ ":time": ["Number"],
184
+ ":stopfire": [],
185
+ ":respawn": ["Player"],
186
+ ":tp": ["Player", "Player"],
187
+ ":startnearfire": ["String"],
188
+ ":jail": ["Player"],
189
+ ":pt": ["Number"],
190
+ ":h": ["String"],
191
+ ":m": ["String"],
192
+ ":pm": ["Player", "String"],
193
+ ":refresh": ["Player"],
194
+ ":bring": ["Player"],
195
+ ":heal": ["Player"],
196
+ ":kick": ["Player", "String"],
197
+ ":startfire": ["String"],
198
+ ":unwanted": ["Player"],
199
+ ":prty": ["Number"],
200
+ ":stopdumpsterfire": [],
201
+ ":helper": ["Player/UserId"],
202
+ ":shutdown": [],
203
+ ":weather": ["String"],
204
+ ":unmod": ["Player/UserId"],
205
+ ":unloadlayout": ["String"],
206
+ ":unban": ["String"],
207
+ ":mod": ["Player/UserId"],
208
+ ":ban": ["Player/UserId"],
209
+ ":unhelper": ["Player/UserId"],
210
+ ":log": ["String"],
211
+ ":kill": ["Player"],
212
+ ":unadmin": ["Player/UserId"],
213
+ ":admin": ["Player/UserId"],
214
+ ":loadlayout": ["String"]
215
+ };
216
+
217
+ if (!Array.isArray(options)) {
218
+ console.error("[LibertyJS.sendPrivateServerCommand]: Options must be an array");
219
+ return {
220
+ error: "invalid_input",
221
+ message: "[LibertyJS.sendPrivateServerCommand]: Options must be an array"
222
+ };
223
+ }
224
+
225
+ if (options.length === 0) {
226
+ console.error("[LibertyJS.sendPrivateServerCommand]: Options must have at least one item");
227
+ return {
228
+ error: "invalid_input",
229
+ message: "[LibertyJS.sendPrivateServerCommand]: Options must have at least one item"
230
+ };
231
+ }
232
+
233
+ const commands = [];
234
+
235
+ for (const cmd of options) {
236
+ if (!cmd.startsWith(":")) continue;
237
+
238
+ const parts = cmd.trim().split(" ");
239
+ const name = parts[0];
240
+ const args = parts.slice(1);
241
+ const schema = valid[name];
242
+
243
+ if (!schema) {
244
+ console.error(`[LibertyJS]: Unsupported command "${name}"`);
245
+ continue;
246
+ }
247
+
248
+ const validArgs =
249
+ (schema.at(-1) === "String" && args.length >= schema.length) ||
250
+ (schema.at(-1) !== "String" && args.length === schema.length);
251
+
252
+ if (validArgs) {
253
+ commands.push(cmd);
254
+ } else {
255
+ console.log(`[LibertyJS]: "${name}" requires ${schema.length}${schema.at(-1) === "String" ? "+" : ""} args`);
256
+ }
257
+ }
258
+
259
+ let successes = 0;
260
+ let failures = 0;
261
+ const failureReasons = [];
262
+
263
+ for (const command of commands) {
264
+ const res = await this.#fetchAPI(url, {
265
+ method: "POST",
266
+ body: command
267
+ });
268
+
269
+ if (!res?.error) {
270
+ successes++;
271
+ } else {
272
+ failures++;
273
+ failureReasons.push({
274
+ command,
275
+ apiResponse: res.apiResponse
276
+ });
277
+ }
278
+ }
279
+
280
+ return { successes, failures, failureReasons };
281
+ }
282
+
283
+ webhook = {
284
+ status: async () => {
285
+ if (!this.#useWebhook) {
286
+ return {
287
+ error: "webhook_disabled",
288
+ message: "[LibertyJS.webhook.status]: Webhook is not configured"
289
+ };
290
+ }
291
+
292
+ const url = `${this.#WEBHOOK_URL}health`;
293
+
294
+ return await this.#fetchAPI(url, { method: "GET" });
295
+ },
296
+ events: async () => {
297
+ if (!this.#useWebhook) {
298
+ return {
299
+ error: "webhook_disabled",
300
+ message: "[LibertyJS.webhook.events]: Webhook is not configured"
301
+ };
302
+ }
303
+
304
+ const url = `${this.#WEBHOOK_URL}webhook/${this.#WEBHOOK_TOKEN}/events`;
305
+
306
+ return await this.#fetchAPI(url, { method: "GET" });
307
+ }
308
+ };
309
309
  }