erlc-v2 1.1.0 → 1.1.2
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.
- package/README.md +575 -572
- package/index.d.ts +429 -430
- package/package.json +7 -7
- package/src/Client.js +460 -465
- package/src/api/LocalApiServer.js +530 -533
- package/src/map/renderPlayerMap.js +2 -2
package/src/Client.js
CHANGED
|
@@ -1,480 +1,475 @@
|
|
|
1
|
-
const { EventEmitter } = require("events");
|
|
2
|
-
const Cache = require("./cache/Cache");
|
|
3
|
-
const RequestManager = require("./rest/RequestManager");
|
|
4
|
-
const Poller = require("./events/Poller");
|
|
5
|
-
const LocalApiServer = require("./api/LocalApiServer");
|
|
6
|
-
const { DEFAULT_OPTIONS, QUERY_FLAG_MAP } = require("./util/constants");
|
|
7
|
-
const { mergeOptions } = require("./util/options");
|
|
8
|
-
const { createLogger } = require("./util/logger");
|
|
9
|
-
const { normalizeServerResponse } = require("./util/normalize");
|
|
10
|
-
const { renderPlayerMap } = require("./map/renderPlayerMap");
|
|
11
|
-
const { searchVehicles, findVehicleByPlate } = require("./util/vehicleSearch");
|
|
12
|
-
const { ERLCError } = require("./errors");
|
|
13
|
-
|
|
14
|
-
const BASE_URL = "https://api.
|
|
15
|
-
const BLOCKED_COMMANDS = new Set([
|
|
16
|
-
":view",
|
|
17
|
-
":to",
|
|
18
|
-
":tocar",
|
|
19
|
-
":toatv",
|
|
20
|
-
":logs",
|
|
21
|
-
":mods",
|
|
22
|
-
":admins",
|
|
23
|
-
"helpers",
|
|
24
|
-
":helpers",
|
|
25
|
-
":administrators",
|
|
26
|
-
":moderators",
|
|
27
|
-
":killlogs",
|
|
28
|
-
":kl",
|
|
29
|
-
":cmds",
|
|
30
|
-
":commands",
|
|
31
|
-
]);
|
|
32
|
-
const EVENT_ALIASES = {
|
|
33
|
-
onReady: "ready",
|
|
34
|
-
onJoin: "playerJoin",
|
|
35
|
-
onLeave: "playerLeave",
|
|
36
|
-
onKill: "kill",
|
|
37
|
-
onVehicleSpawn: "vehicleSpawn",
|
|
38
|
-
onVehicleDespawn: "vehicleDespawn",
|
|
39
|
-
onQueueUpdate: "queueUpdate",
|
|
40
|
-
onStaffUpdate: "staffUpdate",
|
|
41
|
-
onModCall: "modCall",
|
|
42
|
-
onEmergencyCall: "emergencyCall",
|
|
43
|
-
onCommandLog: "commandLog",
|
|
1
|
+
const { EventEmitter } = require("events");
|
|
2
|
+
const Cache = require("./cache/Cache");
|
|
3
|
+
const RequestManager = require("./rest/RequestManager");
|
|
4
|
+
const Poller = require("./events/Poller");
|
|
5
|
+
const LocalApiServer = require("./api/LocalApiServer");
|
|
6
|
+
const { DEFAULT_OPTIONS, QUERY_FLAG_MAP } = require("./util/constants");
|
|
7
|
+
const { mergeOptions } = require("./util/options");
|
|
8
|
+
const { createLogger } = require("./util/logger");
|
|
9
|
+
const { normalizeServerResponse } = require("./util/normalize");
|
|
10
|
+
const { renderPlayerMap } = require("./map/renderPlayerMap");
|
|
11
|
+
const { searchVehicles, findVehicleByPlate } = require("./util/vehicleSearch");
|
|
12
|
+
const { ERLCError } = require("./errors");
|
|
13
|
+
|
|
14
|
+
const BASE_URL = "https://api.erlc.gg";
|
|
15
|
+
const BLOCKED_COMMANDS = new Set([
|
|
16
|
+
":view",
|
|
17
|
+
":to",
|
|
18
|
+
":tocar",
|
|
19
|
+
":toatv",
|
|
20
|
+
":logs",
|
|
21
|
+
":mods",
|
|
22
|
+
":admins",
|
|
23
|
+
"helpers",
|
|
24
|
+
":helpers",
|
|
25
|
+
":administrators",
|
|
26
|
+
":moderators",
|
|
27
|
+
":killlogs",
|
|
28
|
+
":kl",
|
|
29
|
+
":cmds",
|
|
30
|
+
":commands",
|
|
31
|
+
]);
|
|
32
|
+
const EVENT_ALIASES = {
|
|
33
|
+
onReady: "ready",
|
|
34
|
+
onJoin: "playerJoin",
|
|
35
|
+
onLeave: "playerLeave",
|
|
36
|
+
onKill: "kill",
|
|
37
|
+
onVehicleSpawn: "vehicleSpawn",
|
|
38
|
+
onVehicleDespawn: "vehicleDespawn",
|
|
39
|
+
onQueueUpdate: "queueUpdate",
|
|
40
|
+
onStaffUpdate: "staffUpdate",
|
|
41
|
+
onModCall: "modCall",
|
|
42
|
+
onEmergencyCall: "emergencyCall",
|
|
43
|
+
onCommandLog: "commandLog",
|
|
44
44
|
onLogCommand: "logCommand",
|
|
45
45
|
onServerUpdate: "serverUpdate",
|
|
46
46
|
onApiRequest: "apiRequest",
|
|
47
47
|
onWebhook: "webhook",
|
|
48
|
-
onWebhookCommand: "webhookCommand",
|
|
49
48
|
onWebhookEmergencyCall: "webhookEmergencyCall",
|
|
50
49
|
onError: "error",
|
|
51
50
|
onDisconnect: "disconnect",
|
|
52
51
|
};
|
|
53
|
-
|
|
54
|
-
function resolveEventName(eventName) {
|
|
55
|
-
if (typeof eventName !== "string") return eventName;
|
|
56
|
-
return EVENT_ALIASES[eventName] || eventName;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function getCommandKeyword(command) {
|
|
60
|
-
const raw = String(command ?? "").trim();
|
|
61
|
-
if (!raw) return "";
|
|
62
|
-
return raw.split(/\s+/)[0].toLowerCase();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isPromiseLike(value) {
|
|
66
|
-
return (
|
|
67
|
-
value !== null &&
|
|
68
|
-
typeof value === "object" &&
|
|
69
|
-
typeof value.then === "function"
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
class Client extends EventEmitter {
|
|
74
|
-
constructor(options = {}) {
|
|
75
|
-
super();
|
|
76
|
-
|
|
77
|
-
this.options = mergeOptions(DEFAULT_OPTIONS, options);
|
|
78
|
-
if (!this.options.serverKey || typeof this.options.serverKey !== "string") {
|
|
79
|
-
throw new ERLCError("Client requires a valid serverKey");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this.logger = createLogger(this.options.logging, this.options.logger);
|
|
83
|
-
this.cache = new Cache(this.options.cache, this.logger);
|
|
84
|
-
|
|
85
|
-
this.state = {
|
|
86
|
-
disconnected: false,
|
|
87
|
-
destroyed: false,
|
|
88
|
-
disconnectReason: null,
|
|
89
|
-
disconnectError: null,
|
|
90
|
-
};
|
|
91
|
-
this.commandQueue = Promise.resolve();
|
|
92
|
-
|
|
93
|
-
this.requestManager = new RequestManager({
|
|
94
|
-
baseURL: BASE_URL,
|
|
95
|
-
serverKey: this.options.serverKey,
|
|
96
|
-
globalKey: this.options.globalKey,
|
|
97
|
-
cache: this.cache,
|
|
98
|
-
rateLimit: this.options.rateLimit,
|
|
99
|
-
logger: this.logger,
|
|
100
|
-
onDisconnect: (reason, error) => this._handleDisconnect(reason, error),
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
this.server = {
|
|
104
|
-
fetch: (flags = {}, requestOptions = {}) =>
|
|
105
|
-
this._fetchServer(flags, requestOptions),
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
this.players = {
|
|
109
|
-
list: (requestOptions = {}) =>
|
|
110
|
-
this.server
|
|
111
|
-
.fetch({ players: true }, requestOptions)
|
|
112
|
-
.then((d) => d.players),
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
this.map = {
|
|
116
|
-
render: (options = {}, requestOptions = {}) =>
|
|
117
|
-
this._renderMap(options, requestOptions),
|
|
118
|
-
renderUser: (userId, options = {}, requestOptions = {}) =>
|
|
119
|
-
this._renderMap({ ...options, userId }, requestOptions),
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
this.staff = {
|
|
123
|
-
list: (requestOptions = {}) =>
|
|
124
|
-
this.server.fetch({ staff: true }, requestOptions).then((d) => d.staff),
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
this.logs = {
|
|
128
|
-
kills: (requestOptions = {}) =>
|
|
129
|
-
this.server
|
|
130
|
-
.fetch({ killLogs: true }, requestOptions)
|
|
131
|
-
.then((d) => d.killLogs),
|
|
132
|
-
joins: (requestOptions = {}) =>
|
|
133
|
-
this.server
|
|
134
|
-
.fetch({ joinLogs: true }, requestOptions)
|
|
135
|
-
.then((d) => d.joinLogs),
|
|
136
|
-
commands: (requestOptions = {}) =>
|
|
137
|
-
this.server
|
|
138
|
-
.fetch({ commandLogs: true }, requestOptions)
|
|
139
|
-
.then((d) => d.commandLogs),
|
|
140
|
-
modCalls: (requestOptions = {}) =>
|
|
141
|
-
this.server
|
|
142
|
-
.fetch({ modCalls: true }, requestOptions)
|
|
143
|
-
.then((d) => d.modCalls),
|
|
144
|
-
emergencyCalls: (requestOptions = {}) =>
|
|
145
|
-
this.server
|
|
146
|
-
.fetch({ emergencyCalls: true }, requestOptions)
|
|
147
|
-
.then((d) => d.emergencyCalls),
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
this.commands = {
|
|
151
|
-
execute: (command, requestOptions = {}) =>
|
|
152
|
-
this._executeCommand(command, requestOptions),
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
this.vehicles = {
|
|
156
|
-
list: (requestOptions = {}) =>
|
|
157
|
-
this.server
|
|
158
|
-
.fetch({ vehicles: true }, requestOptions)
|
|
159
|
-
.then((d) => d.vehicles),
|
|
160
|
-
search: (filters = {}, requestOptions = {}) =>
|
|
161
|
-
this.server
|
|
162
|
-
.fetch({ vehicles: true }, requestOptions)
|
|
163
|
-
.then((d) => searchVehicles(d.vehicles, filters)),
|
|
164
|
-
findByPlate: (plate, requestOptions = {}) =>
|
|
165
|
-
this.server
|
|
166
|
-
.fetch({ vehicles: true }, requestOptions)
|
|
167
|
-
.then((d) => findVehicleByPlate(d.vehicles, plate)),
|
|
168
|
-
findByOwner: (owner, requestOptions = {}) =>
|
|
169
|
-
this.server
|
|
170
|
-
.fetch({ vehicles: true }, requestOptions)
|
|
171
|
-
.then((d) => searchVehicles(d.vehicles, { owner })),
|
|
172
|
-
findOne: (filters = {}, requestOptions = {}) =>
|
|
173
|
-
this.server
|
|
174
|
-
.fetch({ vehicles: true }, requestOptions)
|
|
175
|
-
.then((d) =>
|
|
176
|
-
searchVehicles(
|
|
177
|
-
d.vehicles,
|
|
178
|
-
typeof filters === "string"
|
|
179
|
-
? { query: filters, limit: 1 }
|
|
180
|
-
: { ...filters, limit: 1 },
|
|
181
|
-
)[0] ?? null,
|
|
182
|
-
),
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
this.queue = {
|
|
186
|
-
get: (requestOptions = {}) =>
|
|
187
|
-
this.server.fetch({ queue: true }, requestOptions).then((d) => d.queue),
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
this.api = new LocalApiServer(this, this.options.api);
|
|
191
|
-
|
|
192
|
-
this.poller = new Poller(this, this.options.polling);
|
|
193
|
-
if (this.options.polling?.enabled !== false) {
|
|
194
|
-
this.poller.start();
|
|
195
|
-
}
|
|
196
|
-
if (this.options.api?.enabled || this.options.api?.port) {
|
|
197
|
-
this.api.start().catch((error) => {
|
|
198
|
-
this.logger.error({
|
|
199
|
-
msg: "local_api_start_failed",
|
|
200
|
-
error: error?.message || String(error),
|
|
201
|
-
});
|
|
202
|
-
if (this.listenerCount("error") > 0) {
|
|
203
|
-
this.emit("error", error);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
on(eventName, listener) {
|
|
210
|
-
return super.on(resolveEventName(eventName), listener);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
once(eventName, listener) {
|
|
214
|
-
return super.once(resolveEventName(eventName), listener);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
addListener(eventName, listener) {
|
|
218
|
-
return super.addListener(resolveEventName(eventName), listener);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
prependListener(eventName, listener) {
|
|
222
|
-
return super.prependListener(resolveEventName(eventName), listener);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
prependOnceListener(eventName, listener) {
|
|
226
|
-
return super.prependOnceListener(resolveEventName(eventName), listener);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
off(eventName, listener) {
|
|
230
|
-
return super.off(resolveEventName(eventName), listener);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
removeListener(eventName, listener) {
|
|
234
|
-
return super.removeListener(resolveEventName(eventName), listener);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
onReady(listener) {
|
|
238
|
-
return this.on("ready", listener);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
onJoin(listener) {
|
|
242
|
-
return this.on("playerJoin", listener);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
onLeave(listener) {
|
|
246
|
-
return this.on("playerLeave", listener);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
onKill(listener) {
|
|
250
|
-
return this.on("kill", listener);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
onVehicleSpawn(listener) {
|
|
254
|
-
return this.on("vehicleSpawn", listener);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
onVehicleDespawn(listener) {
|
|
258
|
-
return this.on("vehicleDespawn", listener);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
onQueueUpdate(listener) {
|
|
262
|
-
return this.on("queueUpdate", listener);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
onStaffUpdate(listener) {
|
|
266
|
-
return this.on("staffUpdate", listener);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
onModCall(listener) {
|
|
270
|
-
return this.on("modCall", listener);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
onCommandLog(listener) {
|
|
274
|
-
return this.on("commandLog", listener);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
onEmergencyCall(listener) {
|
|
278
|
-
return this.on("emergencyCall", listener);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
onLogCommand(listener) {
|
|
282
|
-
return this.on("logCommand", listener);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
onServerUpdate(listener) {
|
|
286
|
-
return this.on("serverUpdate", listener);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
onApiRequest(listener) {
|
|
290
|
-
return this.on("apiRequest", listener);
|
|
291
|
-
}
|
|
292
|
-
|
|
52
|
+
|
|
53
|
+
function resolveEventName(eventName) {
|
|
54
|
+
if (typeof eventName !== "string") return eventName;
|
|
55
|
+
return EVENT_ALIASES[eventName] || eventName;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getCommandKeyword(command) {
|
|
59
|
+
const raw = String(command ?? "").trim();
|
|
60
|
+
if (!raw) return "";
|
|
61
|
+
return raw.split(/\s+/)[0].toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isPromiseLike(value) {
|
|
65
|
+
return (
|
|
66
|
+
value !== null &&
|
|
67
|
+
typeof value === "object" &&
|
|
68
|
+
typeof value.then === "function"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class Client extends EventEmitter {
|
|
73
|
+
constructor(options = {}) {
|
|
74
|
+
super();
|
|
75
|
+
|
|
76
|
+
this.options = mergeOptions(DEFAULT_OPTIONS, options);
|
|
77
|
+
if (!this.options.serverKey || typeof this.options.serverKey !== "string") {
|
|
78
|
+
throw new ERLCError("Client requires a valid serverKey");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.logger = createLogger(this.options.logging, this.options.logger);
|
|
82
|
+
this.cache = new Cache(this.options.cache, this.logger);
|
|
83
|
+
|
|
84
|
+
this.state = {
|
|
85
|
+
disconnected: false,
|
|
86
|
+
destroyed: false,
|
|
87
|
+
disconnectReason: null,
|
|
88
|
+
disconnectError: null,
|
|
89
|
+
};
|
|
90
|
+
this.commandQueue = Promise.resolve();
|
|
91
|
+
|
|
92
|
+
this.requestManager = new RequestManager({
|
|
93
|
+
baseURL: BASE_URL,
|
|
94
|
+
serverKey: this.options.serverKey,
|
|
95
|
+
globalKey: this.options.globalKey,
|
|
96
|
+
cache: this.cache,
|
|
97
|
+
rateLimit: this.options.rateLimit,
|
|
98
|
+
logger: this.logger,
|
|
99
|
+
onDisconnect: (reason, error) => this._handleDisconnect(reason, error),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.server = {
|
|
103
|
+
fetch: (flags = {}, requestOptions = {}) =>
|
|
104
|
+
this._fetchServer(flags, requestOptions),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
this.players = {
|
|
108
|
+
list: (requestOptions = {}) =>
|
|
109
|
+
this.server
|
|
110
|
+
.fetch({ players: true }, requestOptions)
|
|
111
|
+
.then((d) => d.players),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
this.map = {
|
|
115
|
+
render: (options = {}, requestOptions = {}) =>
|
|
116
|
+
this._renderMap(options, requestOptions),
|
|
117
|
+
renderUser: (userId, options = {}, requestOptions = {}) =>
|
|
118
|
+
this._renderMap({ ...options, userId }, requestOptions),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
this.staff = {
|
|
122
|
+
list: (requestOptions = {}) =>
|
|
123
|
+
this.server.fetch({ staff: true }, requestOptions).then((d) => d.staff),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
this.logs = {
|
|
127
|
+
kills: (requestOptions = {}) =>
|
|
128
|
+
this.server
|
|
129
|
+
.fetch({ killLogs: true }, requestOptions)
|
|
130
|
+
.then((d) => d.killLogs),
|
|
131
|
+
joins: (requestOptions = {}) =>
|
|
132
|
+
this.server
|
|
133
|
+
.fetch({ joinLogs: true }, requestOptions)
|
|
134
|
+
.then((d) => d.joinLogs),
|
|
135
|
+
commands: (requestOptions = {}) =>
|
|
136
|
+
this.server
|
|
137
|
+
.fetch({ commandLogs: true }, requestOptions)
|
|
138
|
+
.then((d) => d.commandLogs),
|
|
139
|
+
modCalls: (requestOptions = {}) =>
|
|
140
|
+
this.server
|
|
141
|
+
.fetch({ modCalls: true }, requestOptions)
|
|
142
|
+
.then((d) => d.modCalls),
|
|
143
|
+
emergencyCalls: (requestOptions = {}) =>
|
|
144
|
+
this.server
|
|
145
|
+
.fetch({ emergencyCalls: true }, requestOptions)
|
|
146
|
+
.then((d) => d.emergencyCalls),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
this.commands = {
|
|
150
|
+
execute: (command, requestOptions = {}) =>
|
|
151
|
+
this._executeCommand(command, requestOptions),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
this.vehicles = {
|
|
155
|
+
list: (requestOptions = {}) =>
|
|
156
|
+
this.server
|
|
157
|
+
.fetch({ vehicles: true }, requestOptions)
|
|
158
|
+
.then((d) => d.vehicles),
|
|
159
|
+
search: (filters = {}, requestOptions = {}) =>
|
|
160
|
+
this.server
|
|
161
|
+
.fetch({ vehicles: true }, requestOptions)
|
|
162
|
+
.then((d) => searchVehicles(d.vehicles, filters)),
|
|
163
|
+
findByPlate: (plate, requestOptions = {}) =>
|
|
164
|
+
this.server
|
|
165
|
+
.fetch({ vehicles: true }, requestOptions)
|
|
166
|
+
.then((d) => findVehicleByPlate(d.vehicles, plate)),
|
|
167
|
+
findByOwner: (owner, requestOptions = {}) =>
|
|
168
|
+
this.server
|
|
169
|
+
.fetch({ vehicles: true }, requestOptions)
|
|
170
|
+
.then((d) => searchVehicles(d.vehicles, { owner })),
|
|
171
|
+
findOne: (filters = {}, requestOptions = {}) =>
|
|
172
|
+
this.server
|
|
173
|
+
.fetch({ vehicles: true }, requestOptions)
|
|
174
|
+
.then((d) =>
|
|
175
|
+
searchVehicles(
|
|
176
|
+
d.vehicles,
|
|
177
|
+
typeof filters === "string"
|
|
178
|
+
? { query: filters, limit: 1 }
|
|
179
|
+
: { ...filters, limit: 1 },
|
|
180
|
+
)[0] ?? null,
|
|
181
|
+
),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
this.queue = {
|
|
185
|
+
get: (requestOptions = {}) =>
|
|
186
|
+
this.server.fetch({ queue: true }, requestOptions).then((d) => d.queue),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
this.api = new LocalApiServer(this, this.options.api);
|
|
190
|
+
|
|
191
|
+
this.poller = new Poller(this, this.options.polling);
|
|
192
|
+
if (this.options.polling?.enabled !== false) {
|
|
193
|
+
this.poller.start();
|
|
194
|
+
}
|
|
195
|
+
if (this.options.api?.enabled || this.options.api?.port) {
|
|
196
|
+
this.api.start().catch((error) => {
|
|
197
|
+
this.logger.error({
|
|
198
|
+
msg: "local_api_start_failed",
|
|
199
|
+
error: error?.message || String(error),
|
|
200
|
+
});
|
|
201
|
+
if (this.listenerCount("error") > 0) {
|
|
202
|
+
this.emit("error", error);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
on(eventName, listener) {
|
|
209
|
+
return super.on(resolveEventName(eventName), listener);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
once(eventName, listener) {
|
|
213
|
+
return super.once(resolveEventName(eventName), listener);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
addListener(eventName, listener) {
|
|
217
|
+
return super.addListener(resolveEventName(eventName), listener);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
prependListener(eventName, listener) {
|
|
221
|
+
return super.prependListener(resolveEventName(eventName), listener);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
prependOnceListener(eventName, listener) {
|
|
225
|
+
return super.prependOnceListener(resolveEventName(eventName), listener);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
off(eventName, listener) {
|
|
229
|
+
return super.off(resolveEventName(eventName), listener);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
removeListener(eventName, listener) {
|
|
233
|
+
return super.removeListener(resolveEventName(eventName), listener);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
onReady(listener) {
|
|
237
|
+
return this.on("ready", listener);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
onJoin(listener) {
|
|
241
|
+
return this.on("playerJoin", listener);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
onLeave(listener) {
|
|
245
|
+
return this.on("playerLeave", listener);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
onKill(listener) {
|
|
249
|
+
return this.on("kill", listener);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
onVehicleSpawn(listener) {
|
|
253
|
+
return this.on("vehicleSpawn", listener);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
onVehicleDespawn(listener) {
|
|
257
|
+
return this.on("vehicleDespawn", listener);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
onQueueUpdate(listener) {
|
|
261
|
+
return this.on("queueUpdate", listener);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
onStaffUpdate(listener) {
|
|
265
|
+
return this.on("staffUpdate", listener);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
onModCall(listener) {
|
|
269
|
+
return this.on("modCall", listener);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
onCommandLog(listener) {
|
|
273
|
+
return this.on("commandLog", listener);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
onEmergencyCall(listener) {
|
|
277
|
+
return this.on("emergencyCall", listener);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
onLogCommand(listener) {
|
|
281
|
+
return this.on("logCommand", listener);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
onServerUpdate(listener) {
|
|
285
|
+
return this.on("serverUpdate", listener);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
onApiRequest(listener) {
|
|
289
|
+
return this.on("apiRequest", listener);
|
|
290
|
+
}
|
|
291
|
+
|
|
293
292
|
onWebhook(listener) {
|
|
294
293
|
return this.on("webhook", listener);
|
|
295
294
|
}
|
|
296
295
|
|
|
297
|
-
onWebhookCommand(listener) {
|
|
298
|
-
return this.on("webhookCommand", listener);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
296
|
onWebhookEmergencyCall(listener) {
|
|
302
297
|
return this.on("webhookEmergencyCall", listener);
|
|
303
298
|
}
|
|
304
|
-
|
|
305
|
-
onError(listener) {
|
|
306
|
-
return this.on("error", listener);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
onDisconnect(listener) {
|
|
310
|
-
return this.on("disconnect", listener);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
_handleDisconnect(reason, error) {
|
|
314
|
-
if (this.state.disconnected) return;
|
|
315
|
-
this.state.disconnected = true;
|
|
316
|
-
this.state.disconnectReason = reason;
|
|
317
|
-
this.state.disconnectError = error;
|
|
318
|
-
|
|
319
|
-
this.logger.warn({
|
|
320
|
-
msg: "client_disconnected",
|
|
321
|
-
reason,
|
|
322
|
-
error: error?.message,
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
this.poller.stop();
|
|
326
|
-
this.emit("disconnect", { reason, error });
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
_buildQueryFlags(flags) {
|
|
330
|
-
const query = {};
|
|
331
|
-
if (!flags || typeof flags !== "object") return query;
|
|
332
|
-
|
|
333
|
-
for (const [key, apiKey] of Object.entries(QUERY_FLAG_MAP)) {
|
|
334
|
-
if (flags[key] === true) {
|
|
335
|
-
query[apiKey] = true;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
for (const apiKey of Object.values(QUERY_FLAG_MAP)) {
|
|
340
|
-
if (flags[apiKey] === true) {
|
|
341
|
-
query[apiKey] = true;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return query;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
async _fetchServer(flags = {}, requestOptions = {}) {
|
|
349
|
-
if (this.state.disconnected && this.state.disconnectError) {
|
|
350
|
-
throw this.state.disconnectError;
|
|
351
|
-
}
|
|
352
|
-
if (this.state.destroyed) {
|
|
353
|
-
throw new ERLCError("Client has been destroyed");
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const query = this._buildQueryFlags(flags);
|
|
357
|
-
const response = await this.requestManager.request({
|
|
358
|
-
method: "GET",
|
|
359
|
-
path: "/v2/server",
|
|
360
|
-
query,
|
|
361
|
-
useCache: requestOptions.bypassCache !== true,
|
|
362
|
-
cacheTtlMs: requestOptions.cacheTtlMs,
|
|
363
|
-
dedupe: requestOptions.dedupe !== false,
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
const normalized = normalizeServerResponse(response.data);
|
|
367
|
-
normalized.meta = {
|
|
368
|
-
status: response.status,
|
|
369
|
-
endpoint: response.endpoint,
|
|
370
|
-
bucket: response.bucket,
|
|
371
|
-
rateLimit: response.rateLimit,
|
|
372
|
-
};
|
|
373
|
-
return normalized;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async _executeCommand(command, requestOptions = {}) {
|
|
377
|
-
if (this.state.disconnected && this.state.disconnectError) {
|
|
378
|
-
throw this.state.disconnectError;
|
|
379
|
-
}
|
|
380
|
-
if (this.state.destroyed) {
|
|
381
|
-
throw new ERLCError("Client has been destroyed");
|
|
382
|
-
}
|
|
383
|
-
if (typeof command !== "string" || !command.trim()) {
|
|
384
|
-
throw new ERLCError("Command must be a non-empty string");
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const normalizedCommand = command.trim();
|
|
388
|
-
const keyword = getCommandKeyword(normalizedCommand);
|
|
389
|
-
if (BLOCKED_COMMANDS.has(keyword)) {
|
|
390
|
-
throw new ERLCError(`Command "${keyword}" is blocked by client policy`);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return this._queueCommand(async () => {
|
|
394
|
-
if (this.state.disconnected && this.state.disconnectError) {
|
|
395
|
-
throw this.state.disconnectError;
|
|
396
|
-
}
|
|
397
|
-
if (this.state.destroyed) {
|
|
398
|
-
throw new ERLCError("Client has been destroyed");
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const response = await this.requestManager.request({
|
|
402
|
-
method: "POST",
|
|
403
|
-
path: "/v2/server/command",
|
|
404
|
-
body: { command: normalizedCommand },
|
|
405
|
-
useCache: false,
|
|
406
|
-
dedupe: requestOptions.dedupe === true,
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
return {
|
|
410
|
-
ok: response.status >= 200 && response.status < 300,
|
|
411
|
-
status: response.status,
|
|
412
|
-
endpoint: response.endpoint,
|
|
413
|
-
bucket: response.bucket,
|
|
414
|
-
rateLimit: response.rateLimit,
|
|
415
|
-
message: response.data?.message ?? null,
|
|
416
|
-
commandId:
|
|
417
|
-
response.data?.commandId ??
|
|
418
|
-
response.data?.CommandId ??
|
|
419
|
-
null,
|
|
420
|
-
};
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
async _renderMap(options = {}, requestOptions = {}) {
|
|
425
|
-
if (this.state.disconnected && this.state.disconnectError) {
|
|
426
|
-
throw this.state.disconnectError;
|
|
427
|
-
}
|
|
428
|
-
if (this.state.destroyed) {
|
|
429
|
-
throw new ERLCError("Client has been destroyed");
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return renderPlayerMap(this, options, requestOptions);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
_queueCommand(task) {
|
|
436
|
-
const run = this.commandQueue.then(task, task);
|
|
437
|
-
this.commandQueue = run.catch(() => {});
|
|
438
|
-
return run;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
destroy() {
|
|
442
|
-
if (this.state.destroyed) return;
|
|
443
|
-
this.state.destroyed = true;
|
|
444
|
-
this.poller.destroy();
|
|
445
|
-
this.api.stop();
|
|
446
|
-
this.requestManager.destroy(
|
|
447
|
-
this.state.disconnectError || new ERLCError("Client destroyed"),
|
|
448
|
-
);
|
|
449
|
-
const clearResult = this.cache.clear();
|
|
450
|
-
const maybeLog = (promiseLike, msg) => {
|
|
451
|
-
if (!isPromiseLike(promiseLike)) return;
|
|
452
|
-
promiseLike.catch((error) => {
|
|
453
|
-
this.logger.warn({
|
|
454
|
-
msg,
|
|
455
|
-
error: error?.message || String(error),
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
if (isPromiseLike(clearResult)) {
|
|
461
|
-
maybeLog(
|
|
462
|
-
clearResult
|
|
463
|
-
.catch((error) => {
|
|
464
|
-
this.logger.warn({
|
|
465
|
-
msg: "cache_clear_failed",
|
|
466
|
-
error: error?.message || String(error),
|
|
467
|
-
});
|
|
468
|
-
})
|
|
469
|
-
.finally(() => this.cache.destroy?.()),
|
|
470
|
-
"cache_destroy_failed",
|
|
471
|
-
);
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const destroyResult = this.cache.destroy?.();
|
|
476
|
-
maybeLog(destroyResult, "cache_destroy_failed");
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
module.exports = Client;
|
|
299
|
+
|
|
300
|
+
onError(listener) {
|
|
301
|
+
return this.on("error", listener);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
onDisconnect(listener) {
|
|
305
|
+
return this.on("disconnect", listener);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
_handleDisconnect(reason, error) {
|
|
309
|
+
if (this.state.disconnected) return;
|
|
310
|
+
this.state.disconnected = true;
|
|
311
|
+
this.state.disconnectReason = reason;
|
|
312
|
+
this.state.disconnectError = error;
|
|
313
|
+
|
|
314
|
+
this.logger.warn({
|
|
315
|
+
msg: "client_disconnected",
|
|
316
|
+
reason,
|
|
317
|
+
error: error?.message,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
this.poller.stop();
|
|
321
|
+
this.emit("disconnect", { reason, error });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
_buildQueryFlags(flags) {
|
|
325
|
+
const query = {};
|
|
326
|
+
if (!flags || typeof flags !== "object") return query;
|
|
327
|
+
|
|
328
|
+
for (const [key, apiKey] of Object.entries(QUERY_FLAG_MAP)) {
|
|
329
|
+
if (flags[key] === true) {
|
|
330
|
+
query[apiKey] = true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
for (const apiKey of Object.values(QUERY_FLAG_MAP)) {
|
|
335
|
+
if (flags[apiKey] === true) {
|
|
336
|
+
query[apiKey] = true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return query;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async _fetchServer(flags = {}, requestOptions = {}) {
|
|
344
|
+
if (this.state.disconnected && this.state.disconnectError) {
|
|
345
|
+
throw this.state.disconnectError;
|
|
346
|
+
}
|
|
347
|
+
if (this.state.destroyed) {
|
|
348
|
+
throw new ERLCError("Client has been destroyed");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const query = this._buildQueryFlags(flags);
|
|
352
|
+
const response = await this.requestManager.request({
|
|
353
|
+
method: "GET",
|
|
354
|
+
path: "/v2/server",
|
|
355
|
+
query,
|
|
356
|
+
useCache: requestOptions.bypassCache !== true,
|
|
357
|
+
cacheTtlMs: requestOptions.cacheTtlMs,
|
|
358
|
+
dedupe: requestOptions.dedupe !== false,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const normalized = normalizeServerResponse(response.data);
|
|
362
|
+
normalized.meta = {
|
|
363
|
+
status: response.status,
|
|
364
|
+
endpoint: response.endpoint,
|
|
365
|
+
bucket: response.bucket,
|
|
366
|
+
rateLimit: response.rateLimit,
|
|
367
|
+
};
|
|
368
|
+
return normalized;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async _executeCommand(command, requestOptions = {}) {
|
|
372
|
+
if (this.state.disconnected && this.state.disconnectError) {
|
|
373
|
+
throw this.state.disconnectError;
|
|
374
|
+
}
|
|
375
|
+
if (this.state.destroyed) {
|
|
376
|
+
throw new ERLCError("Client has been destroyed");
|
|
377
|
+
}
|
|
378
|
+
if (typeof command !== "string" || !command.trim()) {
|
|
379
|
+
throw new ERLCError("Command must be a non-empty string");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const normalizedCommand = command.trim();
|
|
383
|
+
const keyword = getCommandKeyword(normalizedCommand);
|
|
384
|
+
if (BLOCKED_COMMANDS.has(keyword)) {
|
|
385
|
+
throw new ERLCError(`Command "${keyword}" is blocked by client policy`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return this._queueCommand(async () => {
|
|
389
|
+
if (this.state.disconnected && this.state.disconnectError) {
|
|
390
|
+
throw this.state.disconnectError;
|
|
391
|
+
}
|
|
392
|
+
if (this.state.destroyed) {
|
|
393
|
+
throw new ERLCError("Client has been destroyed");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const response = await this.requestManager.request({
|
|
397
|
+
method: "POST",
|
|
398
|
+
path: "/v2/server/command",
|
|
399
|
+
body: { command: normalizedCommand },
|
|
400
|
+
useCache: false,
|
|
401
|
+
dedupe: requestOptions.dedupe === true,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
ok: response.status >= 200 && response.status < 300,
|
|
406
|
+
status: response.status,
|
|
407
|
+
endpoint: response.endpoint,
|
|
408
|
+
bucket: response.bucket,
|
|
409
|
+
rateLimit: response.rateLimit,
|
|
410
|
+
message: response.data?.message ?? null,
|
|
411
|
+
commandId:
|
|
412
|
+
response.data?.commandId ??
|
|
413
|
+
response.data?.CommandId ??
|
|
414
|
+
null,
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async _renderMap(options = {}, requestOptions = {}) {
|
|
420
|
+
if (this.state.disconnected && this.state.disconnectError) {
|
|
421
|
+
throw this.state.disconnectError;
|
|
422
|
+
}
|
|
423
|
+
if (this.state.destroyed) {
|
|
424
|
+
throw new ERLCError("Client has been destroyed");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return renderPlayerMap(this, options, requestOptions);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
_queueCommand(task) {
|
|
431
|
+
const run = this.commandQueue.then(task, task);
|
|
432
|
+
this.commandQueue = run.catch(() => {});
|
|
433
|
+
return run;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
destroy() {
|
|
437
|
+
if (this.state.destroyed) return;
|
|
438
|
+
this.state.destroyed = true;
|
|
439
|
+
this.poller.destroy();
|
|
440
|
+
this.api.stop();
|
|
441
|
+
this.requestManager.destroy(
|
|
442
|
+
this.state.disconnectError || new ERLCError("Client destroyed"),
|
|
443
|
+
);
|
|
444
|
+
const clearResult = this.cache.clear();
|
|
445
|
+
const maybeLog = (promiseLike, msg) => {
|
|
446
|
+
if (!isPromiseLike(promiseLike)) return;
|
|
447
|
+
promiseLike.catch((error) => {
|
|
448
|
+
this.logger.warn({
|
|
449
|
+
msg,
|
|
450
|
+
error: error?.message || String(error),
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
if (isPromiseLike(clearResult)) {
|
|
456
|
+
maybeLog(
|
|
457
|
+
clearResult
|
|
458
|
+
.catch((error) => {
|
|
459
|
+
this.logger.warn({
|
|
460
|
+
msg: "cache_clear_failed",
|
|
461
|
+
error: error?.message || String(error),
|
|
462
|
+
});
|
|
463
|
+
})
|
|
464
|
+
.finally(() => this.cache.destroy?.()),
|
|
465
|
+
"cache_destroy_failed",
|
|
466
|
+
);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const destroyResult = this.cache.destroy?.();
|
|
471
|
+
maybeLog(destroyResult, "cache_destroy_failed");
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
module.exports = Client;
|