cubyz-discord-relay 2.4.0 → 2.4.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 +12 -7
- package/config.example.json +2 -0
- package/dist/config.js +60 -16
- package/dist/integrations/cubyzListSite.d.ts +2 -1
- package/dist/integrations/cubyzListSite.js +21 -7
- package/dist/types.d.ts +3 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- Provides a `/list` Discord command to show the players currently online
|
|
15
15
|
- Cleans Cubyz markdown-style usernames and censors configurable words
|
|
16
16
|
- Automatic reconnection with exponential backoff and retry limits
|
|
17
|
-
- Optional integration advertises your server on [
|
|
17
|
+
- Optional integration advertises your server on [servers.ashframe.net](https://servers.ashframe.net)
|
|
18
18
|
|
|
19
19
|
## Prerequisites
|
|
20
20
|
|
|
@@ -79,15 +79,20 @@ npm install
|
|
|
79
79
|
- `connection.reconnect`: enable/disable automatic reconnect attempts
|
|
80
80
|
- `connection.maxRetries`: maximum reconnect attempts (`0` = infinite)
|
|
81
81
|
- `connection.retryDelayMs`: initial delay before retrying (milliseconds)
|
|
82
|
-
- `integration.cubyzlistSite.enabled`: toggle advertising to the community server list
|
|
83
|
-
- `integration.cubyzlistSite.serverName`: required display name shown on cubyzlist.site, must be unique
|
|
84
|
-
- `integration.cubyzlistSite.serverIp`: public hostname or IP that players should connect to
|
|
85
|
-
- `integration.cubyzlistSite.serverPort`: public port exposed for players (defaults to `47649` if omitted)
|
|
86
|
-
- `integration.cubyzlistSite.iconUrl`: optional URL for the list thumbnail
|
|
87
|
-
- `integration.cubyzlistSite.customClientDownloadUrl`: optional link to a custom client build
|
|
88
82
|
|
|
89
83
|
> First run convenience: if `config.json` is missing, the application writes a fresh template in your working directory and exits so you can fill it in before retrying.
|
|
90
84
|
|
|
85
|
+
### CubyzListSite Integration
|
|
86
|
+
|
|
87
|
+
- `integration.cubyzlistSite.enabled`: toggle advertising to the community server list
|
|
88
|
+
- `integration.cubyzlistSite.serverName`: required display name shown on servers.ashframe.net, must be unique; if missing while enabled, the integration logs a warning and stays disabled
|
|
89
|
+
- `integration.cubyzlistSite.serverIp`: required public hostname or IP that players should connect to; if missing while enabled, the integration logs a warning and stays disabled
|
|
90
|
+
- `integration.cubyzlistSite.description`: optional short description shown on servers.ashframe.net
|
|
91
|
+
- `integration.cubyzlistSite.serverPort`: optional public port exposed for players
|
|
92
|
+
- `integration.cubyzlistSite.iconUrl`: optional URL for the list thumbnail
|
|
93
|
+
- `integration.cubyzlistSite.discordServer`: optional Discord invite or server URL
|
|
94
|
+
- `integration.cubyzlistSite.customClientDownloadUrl`: optional link to a custom client build
|
|
95
|
+
|
|
91
96
|
### Usage
|
|
92
97
|
|
|
93
98
|
```bash
|
package/config.example.json
CHANGED
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"serverName": "My Cubyz Server",
|
|
32
32
|
"serverIp": "play.yourserver.com",
|
|
33
33
|
"serverPort": 47649,
|
|
34
|
+
"description": "A friendly public survival server",
|
|
34
35
|
"iconUrl": "https://github.com/PixelGuys/Cubyz/blob/master/assets/cubyz/logo.png?raw=true",
|
|
36
|
+
"discordServer": "https://discord.gg/your-server",
|
|
35
37
|
"customClientDownloadUrl": "https://github.com/PixelGuys/Cubyz/releases/tag/0.0.0"
|
|
36
38
|
}
|
|
37
39
|
}
|
package/dist/config.js
CHANGED
|
@@ -2,6 +2,7 @@ import { access, copyFile, mkdir, readFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createLogger } from "./logger.js";
|
|
5
6
|
const DEFAULT_EVENTS = ["join", "leave", "death", "chat"];
|
|
6
7
|
const SUPPORTED_EVENTS = [...DEFAULT_EVENTS];
|
|
7
8
|
const DEFAULT_CENSORLIST = [];
|
|
@@ -32,8 +33,10 @@ const DEFAULT_CUBYZLIST_SITE = {
|
|
|
32
33
|
enabled: false,
|
|
33
34
|
serverName: "",
|
|
34
35
|
serverIp: "",
|
|
35
|
-
|
|
36
|
+
description: undefined,
|
|
37
|
+
serverPort: undefined,
|
|
36
38
|
iconUrl: undefined,
|
|
39
|
+
discordServer: undefined,
|
|
37
40
|
customClientDownloadUrl: undefined,
|
|
38
41
|
};
|
|
39
42
|
const CONFIG_TEMPLATE_PATH = fileURLToPath(new URL("../config.example.json", import.meta.url));
|
|
@@ -65,6 +68,18 @@ function coercePort(value, fallback) {
|
|
|
65
68
|
}
|
|
66
69
|
return fallback;
|
|
67
70
|
}
|
|
71
|
+
function coerceOptionalPort(value) {
|
|
72
|
+
if (value === undefined || value === null || value === "") {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "number" &&
|
|
76
|
+
Number.isInteger(value) &&
|
|
77
|
+
value > 0 &&
|
|
78
|
+
value <= 65535) {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
68
83
|
function coerceString(value, fallback) {
|
|
69
84
|
return typeof value === "string" && value.trim().length > 0
|
|
70
85
|
? value.trim()
|
|
@@ -124,8 +139,10 @@ function applyDefaults(partial) {
|
|
|
124
139
|
: DEFAULT_CUBYZLIST_SITE.enabled,
|
|
125
140
|
serverName: coerceString(partial.integration?.cubyzlistSite?.serverName, DEFAULT_CUBYZLIST_SITE.serverName),
|
|
126
141
|
serverIp: coerceString(partial.integration?.cubyzlistSite?.serverIp, DEFAULT_CUBYZLIST_SITE.serverIp),
|
|
127
|
-
|
|
142
|
+
description: coerceString(partial.integration?.cubyzlistSite?.description, DEFAULT_CUBYZLIST_SITE.description ?? "") || undefined,
|
|
143
|
+
serverPort: coerceOptionalPort(partial.integration?.cubyzlistSite?.serverPort),
|
|
128
144
|
iconUrl: coerceString(partial.integration?.cubyzlistSite?.iconUrl, DEFAULT_CUBYZLIST_SITE.iconUrl ?? "") || undefined,
|
|
145
|
+
discordServer: coerceString(partial.integration?.cubyzlistSite?.discordServer, DEFAULT_CUBYZLIST_SITE.discordServer ?? "") || undefined,
|
|
129
146
|
customClientDownloadUrl: coerceString(partial.integration?.cubyzlistSite?.customClientDownloadUrl, DEFAULT_CUBYZLIST_SITE.customClientDownloadUrl ?? "") || undefined,
|
|
130
147
|
};
|
|
131
148
|
const integration = {
|
|
@@ -172,6 +189,25 @@ function applyDefaults(partial) {
|
|
|
172
189
|
integration,
|
|
173
190
|
};
|
|
174
191
|
}
|
|
192
|
+
function finalizeConfig(config) {
|
|
193
|
+
const cubyzlist = config.integration.cubyzlistSite;
|
|
194
|
+
if (!cubyzlist.enabled) {
|
|
195
|
+
return config;
|
|
196
|
+
}
|
|
197
|
+
const missingFields = [];
|
|
198
|
+
if (cubyzlist.serverName.length === 0) {
|
|
199
|
+
missingFields.push("integration.cubyzlistSite.serverName");
|
|
200
|
+
}
|
|
201
|
+
if (cubyzlist.serverIp.length === 0) {
|
|
202
|
+
missingFields.push("integration.cubyzlistSite.serverIp");
|
|
203
|
+
}
|
|
204
|
+
if (missingFields.length === 0) {
|
|
205
|
+
return config;
|
|
206
|
+
}
|
|
207
|
+
createLogger(config.logLevel)("warn", `[CubyzListSite] Disabled integration because required config field(s) are missing: ${missingFields.join(", ")}`);
|
|
208
|
+
cubyzlist.enabled = false;
|
|
209
|
+
return config;
|
|
210
|
+
}
|
|
175
211
|
async function ensureConfigFile(resolvedPath) {
|
|
176
212
|
try {
|
|
177
213
|
await access(resolvedPath);
|
|
@@ -285,25 +321,33 @@ export function validateConfig(config) {
|
|
|
285
321
|
if (typeof cubyzlist.enabled !== "boolean") {
|
|
286
322
|
throw new Error('Configuration error: "integration.cubyzlistSite.enabled" must be a boolean.');
|
|
287
323
|
}
|
|
288
|
-
|
|
324
|
+
if (typeof cubyzlist.serverName !== "string") {
|
|
325
|
+
throw new Error('Configuration error: "integration.cubyzlistSite.serverName" must be a string.');
|
|
326
|
+
}
|
|
327
|
+
if (typeof cubyzlist.serverIp !== "string") {
|
|
328
|
+
throw new Error('Configuration error: "integration.cubyzlistSite.serverIp" must be a string.');
|
|
329
|
+
}
|
|
330
|
+
if (cubyzlist.description !== undefined &&
|
|
331
|
+
(typeof cubyzlist.description !== "string" || cubyzlist.description === "")) {
|
|
332
|
+
throw new Error('Configuration error: "integration.cubyzlistSite.description" must be a non-empty string or undefined.');
|
|
333
|
+
}
|
|
289
334
|
if (cubyzlist.enabled) {
|
|
290
|
-
if (
|
|
291
|
-
cubyzlist.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
throw new Error('Configuration error: "integration.cubyzlistSite.
|
|
296
|
-
}
|
|
297
|
-
if (typeof cubyzlist.serverPort !== "number" ||
|
|
298
|
-
!Number.isInteger(cubyzlist.serverPort) ||
|
|
299
|
-
cubyzlist.serverPort <= 0 ||
|
|
300
|
-
cubyzlist.serverPort > 65535) {
|
|
301
|
-
throw new Error('Configuration error: "integration.cubyzlistSite.serverPort" must be an integer between 1 and 65535.');
|
|
335
|
+
if (cubyzlist.serverPort !== undefined &&
|
|
336
|
+
(typeof cubyzlist.serverPort !== "number" ||
|
|
337
|
+
!Number.isInteger(cubyzlist.serverPort) ||
|
|
338
|
+
cubyzlist.serverPort <= 0 ||
|
|
339
|
+
cubyzlist.serverPort > 65535)) {
|
|
340
|
+
throw new Error('Configuration error: "integration.cubyzlistSite.serverPort" must be an integer between 1 and 65535 or undefined.');
|
|
302
341
|
}
|
|
303
342
|
if (cubyzlist.iconUrl !== undefined &&
|
|
304
343
|
(typeof cubyzlist.iconUrl !== "string" || cubyzlist.iconUrl === "")) {
|
|
305
344
|
throw new Error('Configuration error: "integration.cubyzlistSite.iconUrl" must be a non-empty string or undefined.');
|
|
306
345
|
}
|
|
346
|
+
if (cubyzlist.discordServer !== undefined &&
|
|
347
|
+
(typeof cubyzlist.discordServer !== "string" ||
|
|
348
|
+
cubyzlist.discordServer === "")) {
|
|
349
|
+
throw new Error('Configuration error: "integration.cubyzlistSite.discordServer" must be a non-empty string or undefined.');
|
|
350
|
+
}
|
|
307
351
|
if (cubyzlist.customClientDownloadUrl !== undefined &&
|
|
308
352
|
(typeof cubyzlist.customClientDownloadUrl !== "string" ||
|
|
309
353
|
cubyzlist.customClientDownloadUrl === "")) {
|
|
@@ -319,7 +363,7 @@ export async function loadConfig(configPath) {
|
|
|
319
363
|
if ("cubyzLogPath" in parsedUnknown) {
|
|
320
364
|
throw new Error("Configuration error: detected legacy log-based settings. Update the configuration file to use the bot connection schema.");
|
|
321
365
|
}
|
|
322
|
-
const config = applyDefaults(parsedUnknown);
|
|
366
|
+
const config = finalizeConfig(applyDefaults(parsedUnknown));
|
|
323
367
|
validateConfig(config);
|
|
324
368
|
return config;
|
|
325
369
|
}
|
|
@@ -6,7 +6,7 @@ import type { BaseIntegration, IntegrationStatusContext } from "./base.js";
|
|
|
6
6
|
* Integration that sends server status updates to the Cubyz list site
|
|
7
7
|
* via TCP socket connection to api.ashframe.net:5001.
|
|
8
8
|
* Sends periodic updates every 5 minutes to keep the listing fresh.
|
|
9
|
-
* @link https://
|
|
9
|
+
* @link https://servers.ashframe.net
|
|
10
10
|
*/
|
|
11
11
|
export declare class CubyzListSiteIntegration implements BaseIntegration {
|
|
12
12
|
readonly name = "CubyzListSite";
|
|
@@ -18,6 +18,7 @@ export declare class CubyzListSiteIntegration implements BaseIntegration {
|
|
|
18
18
|
private readonly UPDATE_INTERVAL_MS;
|
|
19
19
|
private isReady;
|
|
20
20
|
private readonly config;
|
|
21
|
+
private readonly version;
|
|
21
22
|
private readonly logger;
|
|
22
23
|
constructor(config: Config);
|
|
23
24
|
private log;
|
|
@@ -4,7 +4,7 @@ import { createLogger } from "../logger.js";
|
|
|
4
4
|
* Integration that sends server status updates to the Cubyz list site
|
|
5
5
|
* via TCP socket connection to api.ashframe.net:5001.
|
|
6
6
|
* Sends periodic updates every 5 minutes to keep the listing fresh.
|
|
7
|
-
* @link https://
|
|
7
|
+
* @link https://servers.ashframe.net
|
|
8
8
|
*/
|
|
9
9
|
export class CubyzListSiteIntegration {
|
|
10
10
|
name = "CubyzListSite";
|
|
@@ -16,9 +16,11 @@ export class CubyzListSiteIntegration {
|
|
|
16
16
|
UPDATE_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
17
17
|
isReady = false;
|
|
18
18
|
config;
|
|
19
|
+
version;
|
|
19
20
|
logger;
|
|
20
21
|
constructor(config) {
|
|
21
22
|
this.config = config.integration.cubyzlistSite;
|
|
23
|
+
this.version = config.cubyz.version;
|
|
22
24
|
this.logger = createLogger(config.logLevel);
|
|
23
25
|
}
|
|
24
26
|
log(level, ...args) {
|
|
@@ -90,14 +92,26 @@ export class CubyzListSiteIntegration {
|
|
|
90
92
|
const payload = {
|
|
91
93
|
server_id: this.config.serverName,
|
|
92
94
|
player_count: this.currentPlayers.size,
|
|
95
|
+
player_list: Array.from(this.currentPlayers),
|
|
93
96
|
status: this.currentStatus,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
icon: this.config.iconUrl ?? "",
|
|
98
|
-
client_download: this.config.customClientDownloadUrl ?? "",
|
|
99
|
-
script_version: "1.4",
|
|
97
|
+
ip: this.config.serverIp,
|
|
98
|
+
version: this.version,
|
|
99
|
+
api_version: "1",
|
|
100
100
|
timestamp: Math.floor(Date.now() / 1000),
|
|
101
|
+
...(this.config.serverPort != null
|
|
102
|
+
? { port: this.config.serverPort }
|
|
103
|
+
: {}),
|
|
104
|
+
...(this.gamemode != null ? { gamemode: this.gamemode } : {}),
|
|
105
|
+
...(this.config.description != null
|
|
106
|
+
? { description: this.config.description }
|
|
107
|
+
: {}),
|
|
108
|
+
...(this.config.iconUrl != null ? { icon: this.config.iconUrl } : {}),
|
|
109
|
+
...(this.config.discordServer != null
|
|
110
|
+
? { discord_server: this.config.discordServer }
|
|
111
|
+
: {}),
|
|
112
|
+
...(this.config.customClientDownloadUrl != null
|
|
113
|
+
? { client_download: this.config.customClientDownloadUrl }
|
|
114
|
+
: {}),
|
|
101
115
|
};
|
|
102
116
|
const dataString = JSON.stringify(payload);
|
|
103
117
|
return new Promise((resolve, reject) => {
|
package/dist/types.d.ts
CHANGED
|
@@ -16,8 +16,10 @@ export interface CubyzListSiteConfig {
|
|
|
16
16
|
enabled: boolean;
|
|
17
17
|
serverName: string;
|
|
18
18
|
serverIp: string;
|
|
19
|
-
|
|
19
|
+
description?: string;
|
|
20
|
+
serverPort?: number;
|
|
20
21
|
iconUrl?: string;
|
|
22
|
+
discordServer?: string;
|
|
21
23
|
customClientDownloadUrl?: string;
|
|
22
24
|
}
|
|
23
25
|
export interface IntegrationConfig {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cubyz-discord-relay",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "Bot that relays messages between a Cubyz server and a Discord channel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -40,4 +40,4 @@
|
|
|
40
40
|
"tsx": "^4.20.6",
|
|
41
41
|
"typescript": "^5.9.3"
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|