Servername aus dem Spiel und ein Schloss fuer Passwort oder Whitelist
Der Name im Panel ist der, den DU vergibst — wie der Server sich im Spiel nennt, stand nirgends. Steht jetzt darunter, aber nur wenn es etwas anderes ist als der eingetragene Name; sonst haette dieselbe Zeile zweimal dagestanden. Beim Schloss war die Frage, wie viel die Abfrage ueberhaupt hergibt. Nachgesehen statt geraten: 31 der 358 gamedig-Protokolle melden ein Passwort. valve gehoert dazu, deckt also CS2, Rust, ARK, Squad, 7DTD, Zomboid, Gmod und DayZ ab. Minecraft nicht, Farming Simulator auch nicht — im LS-XML kommt das Wort Passwort kein einziges Mal vor. Und eine Whitelist meldet grundsaetzlich kein Spiel, die steht in einer Datei auf dem Server. Also beides: automatisch, wo es geht, und eine Einstellung, die es schlaegt. Wer "Whitelist" eintraegt, bekommt sie angezeigt, auch wenn der Server schweigt. Wichtig dabei ist der dritte Zustand. "Sagt nichts" ist nicht "offen" — deshalb `null` und kein `false`. Ein Schloss, das bei jedem schweigenden Server fehlt, waere eine Aussage, die wir nicht belegen koennen; umgekehrt waere ein Schloss ohne Grundlage eine Luege. Bei unbekannt steht schlicht nichts da. Geprueft: Einstellung schlaegt Messung in allen vier Faellen, automatisch mit true/false/null, fehlender Server, unbekannter Wert faellt auf automatisch zurueck statt zu sperren, Schloss im Embed-Titel nur wenn belegt, Spielname nur bei echtem Unterschied (auch mit Leerzeichen drumherum). Panel und /server-Seite im laufenden Frontend angesehen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,9 @@ export async function queryServer(server) {
|
||||
const base = {
|
||||
...server, online: false, players: null, max: null, map: null,
|
||||
playerNames: [], ping: null, version: null, mods: null,
|
||||
// Wie der Server sich selbst nennt, und ob er ein Passwort verlangt.
|
||||
// `null` heisst „sagt er nicht" — das ist etwas anderes als „nein".
|
||||
spielname: null, passwort: null,
|
||||
};
|
||||
const started = Date.now();
|
||||
try {
|
||||
@@ -91,6 +94,7 @@ export async function queryServer(server) {
|
||||
players: Number(data.clients) || 0,
|
||||
max: Number(data.sv_maxclients) || 0,
|
||||
map: data.mapname ?? null,
|
||||
spielname: data.hostname ?? null,
|
||||
};
|
||||
}
|
||||
if (server.type === 'http') {
|
||||
@@ -118,6 +122,11 @@ export async function queryServer(server) {
|
||||
max: state.maxplayers ?? null,
|
||||
map: state.map || null,
|
||||
version: state.version || null,
|
||||
spielname: state.name || null,
|
||||
// 31 von 358 Protokollen melden das — valve (CS2, Rust, ARK,
|
||||
// Squad, Zomboid …) gehoert dazu, Minecraft und Farming Simulator
|
||||
// nicht. Wo nichts kommt, bleibt es null statt false.
|
||||
passwort: typeof state.password === 'boolean' ? state.password : null,
|
||||
// Nur Farming Simulator liefert die Modliste mit; anderswo bleibt
|
||||
// das Feld leer statt auf 0 zu stehen und eine Aussage zu machen.
|
||||
mods: Array.isArray(state.raw?.mods) ? state.raw.mods.length : null,
|
||||
@@ -128,6 +137,34 @@ export async function queryServer(server) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wer darf auf den Server?
|
||||
*
|
||||
* Nur 31 der 358 Protokolle melden überhaupt ein Passwort, und eine Whitelist
|
||||
* meldet gar keins — die steckt in einer Datei auf dem Server, nicht in der
|
||||
* Abfrage. Deshalb schlägt die Einstellung die Messung: wer „Whitelist"
|
||||
* einträgt, bekommt sie angezeigt, auch wenn der Server schweigt.
|
||||
*
|
||||
* `null` heisst „unbekannt" und ist etwas anderes als „offen". Ein Schloss,
|
||||
* das bei jedem schweigenden Server fehlt, wäre eine Aussage, die wir nicht
|
||||
* belegen können.
|
||||
*
|
||||
* @returns {{ zu: boolean|null, grund: string|null }}
|
||||
*/
|
||||
export function zugangStand(server, ergebnis = {}) {
|
||||
switch (server?.zugang) {
|
||||
case 'passwort': return { zu: true, grund: 'Passwort' };
|
||||
case 'whitelist': return { zu: true, grund: 'Whitelist' };
|
||||
case 'offen': return { zu: false, grund: null };
|
||||
default: {
|
||||
const p = ergebnis.passwort;
|
||||
if (p === true) return { zu: true, grund: 'Passwort' };
|
||||
if (p === false) return { zu: false, grund: null };
|
||||
return { zu: null, grund: null };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Nur http(s) darf in einen Link-Knopf — Discord weist alles andere ab */
|
||||
export function istWebLink(url) {
|
||||
return /^https?:\/\/\S+$/i.test(String(url ?? '').trim());
|
||||
@@ -182,10 +219,15 @@ export function buildServerNachricht(r) {
|
||||
/** GSM-Style-Embed für ein Query-Ergebnis */
|
||||
export function buildServerEmbed(r) {
|
||||
const icon = GAME_ICONS[r.type] ?? '🎮';
|
||||
const zugang = zugangStand(r, r);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(r.online ? GREEN : RED)
|
||||
.setTitle(`${icon} ${r.name}`)
|
||||
.addFields({ name: 'Status', value: r.online ? '🟢 **Online**' : '🔴 **Offline**', inline: true })
|
||||
.setTitle(`${icon} ${r.name}${zugang.zu ? ' 🔒' : ''}`)
|
||||
.addFields({
|
||||
name: 'Status',
|
||||
value: `${r.online ? '🟢 **Online**' : '🔴 **Offline**'}${zugang.grund ? `\n🔒 ${zugang.grund}` : ''}`,
|
||||
inline: true,
|
||||
})
|
||||
// Intervall aus den Werten lesen, nicht festschreiben — sonst behauptet
|
||||
// das Embed weiter „alle 2 min", nachdem im Panel etwas anderes steht.
|
||||
.setFooter({
|
||||
@@ -194,6 +236,11 @@ export function buildServerEmbed(r) {
|
||||
})
|
||||
.setTimestamp();
|
||||
if (r.image_url) embed.setThumbnail(r.image_url);
|
||||
// Wie der Server sich im Spiel nennt. Nur wenn es etwas anderes ist als
|
||||
// der Name im Panel — sonst stünde dieselbe Zeile zweimal da.
|
||||
if (r.spielname && r.spielname.trim() && r.spielname.trim() !== r.name?.trim()) {
|
||||
embed.setDescription(`*${r.spielname.trim().slice(0, 200)}*`);
|
||||
}
|
||||
|
||||
if (r.online && r.players != null && r.max) {
|
||||
const pct = Math.min(1, r.players / r.max);
|
||||
|
||||
@@ -931,6 +931,9 @@ db.exec(`
|
||||
// weil sonst für jeden neuen Zweck eine Spalte dazukäme — und genau so
|
||||
// sind connect_url und mods_url entstanden.
|
||||
if (!cols.includes('links')) db.exec(`ALTER TABLE gameservers ADD COLUMN links TEXT NOT NULL DEFAULT '[]'`);
|
||||
// Wer darf drauf: 'auto' nimmt, was die Abfrage sagt. Nur die wenigsten
|
||||
// Spiele melden das — und eine Whitelist meldet gar keins.
|
||||
if (!cols.includes('zugang')) db.exec(`ALTER TABLE gameservers ADD COLUMN zugang TEXT NOT NULL DEFAULT 'auto'`);
|
||||
}
|
||||
// Die Auswahl im Panel bot mal Spiel-IDs an, die es bei gamedig gar nicht gibt.
|
||||
// Wer eine davon gewählt hat, hatte einen Server, der dauerhaft offline stand,
|
||||
@@ -944,13 +947,13 @@ db.exec(`
|
||||
for (const [alt, neu] of Object.entries(echteIds)) um.run(neu, alt);
|
||||
}
|
||||
const insertGameserver = db.prepare(`
|
||||
INSERT INTO gameservers (name, type, query_url, address, host, port, connect_url, image_url, token, mods_url, links)
|
||||
VALUES (@name, @type, @query_url, @address, @host, @port, @connect_url, @image_url, @token, @mods_url, @links)
|
||||
INSERT INTO gameservers (name, type, query_url, address, host, port, connect_url, image_url, token, mods_url, links, zugang)
|
||||
VALUES (@name, @type, @query_url, @address, @host, @port, @connect_url, @image_url, @token, @mods_url, @links, @zugang)
|
||||
`);
|
||||
const updateGameserverStmt = db.prepare(`
|
||||
UPDATE gameservers SET name = @name, type = @type, query_url = @query_url, address = @address,
|
||||
host = @host, port = @port, connect_url = @connect_url, image_url = @image_url, token = @token,
|
||||
mods_url = @mods_url, links = @links
|
||||
mods_url = @mods_url, links = @links, zugang = @zugang
|
||||
WHERE id = @id
|
||||
`);
|
||||
const setGameserverMessageStmt = db.prepare('UPDATE gameservers SET message_id = ? WHERE id = ?');
|
||||
@@ -962,7 +965,7 @@ const deleteGameserverStmt = db.prepare('DELETE FROM gameservers WHERE id = ?');
|
||||
// auffüllen ist billiger, als das an jeder Aufrufstelle nachzuziehen.
|
||||
const gameserverFelder = {
|
||||
name: '', type: 'fivem', query_url: '', address: '', host: '', port: null,
|
||||
connect_url: '', image_url: '', token: '', mods_url: '', links: '[]',
|
||||
connect_url: '', image_url: '', token: '', mods_url: '', links: '[]', zugang: 'auto',
|
||||
};
|
||||
// Links dürfen als Liste hereinkommen — so gibt getGameserver sie ja auch
|
||||
// heraus. better-sqlite3 kann kein Array binden und würde sonst werfen.
|
||||
|
||||
+10
-1
@@ -32,7 +32,7 @@ import crypto from 'node:crypto';
|
||||
import { moduleStates, moduleDetail, setModuleEnabled, moduleEnabled, moduleFavorites, setModuleFavorites, moduleSettingKeys, MODULE_GROUPS, MODULES, MAX_FAVORITES } from '../modules.js';
|
||||
import { templateStates, setTemplate, TEMPLATE_GROUPS } from '../templates.js';
|
||||
import { tuningStates, setTuning, tuning, TUNING_GROUPS } from '../tuning.js';
|
||||
import { lastResults, GAME_ICONS, spielName, spielListe } from '../bot/server-monitor.js';
|
||||
import { lastResults, GAME_ICONS, spielName, spielListe, zugangStand } from '../bot/server-monitor.js';
|
||||
import { watchdogState, watchedServices } from '../bot/watchdog.js';
|
||||
import { listRules, setRuleEnabled } from '../bot/automod.js';
|
||||
import {
|
||||
@@ -227,6 +227,10 @@ export function registerApiRoutes(app, client) {
|
||||
connect_url: s.connect_url || null,
|
||||
mods_url: s.mods_url || null,
|
||||
links: s.links ?? [],
|
||||
zugang: zugangStand(s, r ?? {}),
|
||||
// Wie der Server sich im Spiel nennt, falls anders als hier
|
||||
spielname: r?.spielname && r.spielname.trim() !== s.name.trim()
|
||||
? r.spielname.trim() : null,
|
||||
online: r?.online ?? null, // null = noch nie gecheckt
|
||||
players: r?.players ?? null,
|
||||
max: r?.max ?? null,
|
||||
@@ -1611,6 +1615,11 @@ ${rssItems}
|
||||
? String(body.mods_url).trim().slice(0, 300)
|
||||
: '',
|
||||
links: freieLinks(body.links),
|
||||
// Nur bekannte Werte — sonst stuende irgendwann "asdf" in der
|
||||
// Spalte und zugangStand faellt still auf 'auto' zurueck.
|
||||
zugang: ['auto', 'offen', 'passwort', 'whitelist'].includes(String(body.zugang ?? ''))
|
||||
? String(body.zugang)
|
||||
: 'auto',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user