koishi-plugin-csss 2.0.0 → 3.1.0
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/lib/index.js +346 -292
- package/package.json +7 -6
- package/readme.md +182 -1
- package/lib/index.d.ts +0 -20
package/lib/index.js
CHANGED
|
@@ -28,7 +28,7 @@ __export(src_exports, {
|
|
|
28
28
|
module.exports = __toCommonJS(src_exports);
|
|
29
29
|
var import_koishi = require("koishi");
|
|
30
30
|
var name = "csss";
|
|
31
|
-
var inject = ["
|
|
31
|
+
var inject = ["puppeteer", "gamedig", "database"];
|
|
32
32
|
var Config = import_koishi.Schema.object({
|
|
33
33
|
timeout: import_koishi.Schema.number().min(1e3).max(3e4).default(5e3).description("查询超时时间(毫秒)"),
|
|
34
34
|
cacheTime: import_koishi.Schema.number().min(0).max(3e5).default(3e4).description("缓存时间(毫秒,0为禁用缓存)"),
|
|
@@ -51,8 +51,8 @@ var Config = import_koishi.Schema.object({
|
|
|
51
51
|
batchTimeout: import_koishi.Schema.number().min(1e3).max(6e4).default(15e3).description("批量查询总超时时间(毫秒)")
|
|
52
52
|
});
|
|
53
53
|
var COLORS = {
|
|
54
|
-
background: "
|
|
55
|
-
text: "
|
|
54
|
+
background: "#1c1c1fcc",
|
|
55
|
+
text: "#71717a",
|
|
56
56
|
textLight: "#aaaaaa",
|
|
57
57
|
textLighter: "#dddddd",
|
|
58
58
|
textWhite: "#ffffff",
|
|
@@ -72,7 +72,7 @@ var COLORS = {
|
|
|
72
72
|
divider: "#555555",
|
|
73
73
|
timestamp: "#666666",
|
|
74
74
|
gold: "#FFD700",
|
|
75
|
-
playerName: "
|
|
75
|
+
playerName: "#fcf8de"
|
|
76
76
|
};
|
|
77
77
|
var utils = {
|
|
78
78
|
formatPing(ping) {
|
|
@@ -109,9 +109,9 @@ function apply(ctx, config) {
|
|
|
109
109
|
console.error("koishi-plugin-gamedig 未安装或未启用");
|
|
110
110
|
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-gamedig 插件");
|
|
111
111
|
}
|
|
112
|
-
if (!ctx.
|
|
113
|
-
console.error("koishi-plugin-
|
|
114
|
-
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-
|
|
112
|
+
if (!ctx.puppeteer) {
|
|
113
|
+
console.error("koishi-plugin-puppeteer 未安装或未启用");
|
|
114
|
+
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-puppeteer 插件");
|
|
115
115
|
}
|
|
116
116
|
async function queryServers(serversToQuery) {
|
|
117
117
|
const startTime = Date.now();
|
|
@@ -271,300 +271,349 @@ function apply(ctx, config) {
|
|
|
271
271
|
return message.trim();
|
|
272
272
|
}
|
|
273
273
|
__name(formatPlayers, "formatPlayers");
|
|
274
|
-
|
|
275
|
-
calculateServerNameFontSize(ctx2, name2, maxWidth, baseFontSize) {
|
|
276
|
-
try {
|
|
277
|
-
if (!ctx2 || typeof ctx2.measureText !== "function") {
|
|
278
|
-
console.warn("Canvas context not available, returning default font size");
|
|
279
|
-
return baseFontSize * 1.5;
|
|
280
|
-
}
|
|
281
|
-
let fontSize = baseFontSize * 1.5;
|
|
282
|
-
while (fontSize > baseFontSize * 0.8) {
|
|
283
|
-
ctx2.font = `bold ${fontSize}px ${config.fontFamily}`;
|
|
284
|
-
const measurement = ctx2.measureText(name2);
|
|
285
|
-
if (measurement && measurement.width <= maxWidth) break;
|
|
286
|
-
fontSize -= 1;
|
|
287
|
-
}
|
|
288
|
-
return fontSize;
|
|
289
|
-
} catch (error) {
|
|
290
|
-
console.error("Error in calculateServerNameFontSize:", error);
|
|
291
|
-
return baseFontSize * 1.5;
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
calculatePlayerListParams(playerCount) {
|
|
295
|
-
const shouldEnlarge = playerCount > 0 && playerCount < 10;
|
|
296
|
-
return {
|
|
297
|
-
shouldEnlarge,
|
|
298
|
-
fontSizeMultiplier: shouldEnlarge ? 1.2 : 0.9,
|
|
299
|
-
rowHeight: shouldEnlarge ? 40 : 30,
|
|
300
|
-
nameMaxLength: shouldEnlarge ? 40 : 30,
|
|
301
|
-
needTwoColumns: playerCount > 10
|
|
302
|
-
};
|
|
303
|
-
},
|
|
304
|
-
drawBackground(ctx2, width, height, color = COLORS.background) {
|
|
305
|
-
ctx2.fillStyle = color;
|
|
306
|
-
ctx2.fillRect(0, 0, width, height);
|
|
307
|
-
},
|
|
308
|
-
drawTitle(ctx2, text, x, y, fontSize, fontFamily, color = COLORS.textWhite) {
|
|
309
|
-
ctx2.fillStyle = color;
|
|
310
|
-
ctx2.font = `bold ${fontSize}px ${fontFamily}`;
|
|
311
|
-
ctx2.textAlign = "center";
|
|
312
|
-
ctx2.fillText(text, x, y);
|
|
313
|
-
},
|
|
314
|
-
drawDivider(ctx2, x1, y1, x2, y2, color = COLORS.divider, width = 2) {
|
|
315
|
-
ctx2.strokeStyle = color;
|
|
316
|
-
ctx2.lineWidth = width;
|
|
317
|
-
ctx2.beginPath();
|
|
318
|
-
ctx2.moveTo(x1, y1);
|
|
319
|
-
ctx2.lineTo(x2, y2);
|
|
320
|
-
ctx2.stroke();
|
|
321
|
-
},
|
|
322
|
-
drawText(ctx2, text, x, y, options = {}) {
|
|
323
|
-
const {
|
|
324
|
-
color = COLORS.text,
|
|
325
|
-
fontSize = config.fontSize,
|
|
326
|
-
fontFamily = config.fontFamily,
|
|
327
|
-
align = "left",
|
|
328
|
-
bold = false,
|
|
329
|
-
italic = false
|
|
330
|
-
} = options;
|
|
331
|
-
ctx2.fillStyle = color;
|
|
332
|
-
ctx2.textAlign = align;
|
|
333
|
-
const fontStyle = `${bold ? "bold" : ""} ${italic ? "italic" : ""} ${fontSize}px ${fontFamily}`;
|
|
334
|
-
ctx2.font = fontStyle.trim() || `${fontSize}px ${fontFamily}`;
|
|
335
|
-
ctx2.fillText(text, x, y);
|
|
336
|
-
},
|
|
337
|
-
drawPlayerList(ctx2, players, startY, width, maxHeight, params) {
|
|
338
|
-
let y = startY;
|
|
339
|
-
if (players.length === 0) {
|
|
340
|
-
this.drawText(ctx2, "服务器当前无玩家在线", 80, y, { color: COLORS.textLight });
|
|
341
|
-
return { y: y + 35, displayedCount: 0 };
|
|
342
|
-
}
|
|
343
|
-
const sortedPlayers = [...players].sort((a, b) => {
|
|
344
|
-
const nameA = utils.cleanName(a.name).toLowerCase();
|
|
345
|
-
const nameB = utils.cleanName(b.name).toLowerCase();
|
|
346
|
-
return nameA.localeCompare(nameB);
|
|
347
|
-
});
|
|
348
|
-
if (params.needTwoColumns) {
|
|
349
|
-
const leftColumnX = 80;
|
|
350
|
-
const rightColumnX = width / 2 + 80;
|
|
351
|
-
const playersPerColumn = Math.ceil(players.length / 2);
|
|
352
|
-
const displayPerColumn = Math.min(playersPerColumn, Math.ceil(config.maxPlayers / 2));
|
|
353
|
-
const leftPlayers = sortedPlayers.slice(0, displayPerColumn);
|
|
354
|
-
const rightPlayers = sortedPlayers.slice(displayPerColumn, displayPerColumn * 2);
|
|
355
|
-
let currentY = y;
|
|
356
|
-
let displayedCount = 0;
|
|
357
|
-
leftPlayers.forEach((player) => {
|
|
358
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
359
|
-
this.drawText(ctx2, name2, leftColumnX, currentY, {
|
|
360
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
361
|
-
color: COLORS.textLighter
|
|
362
|
-
});
|
|
363
|
-
currentY += params.rowHeight;
|
|
364
|
-
displayedCount++;
|
|
365
|
-
});
|
|
366
|
-
currentY = y;
|
|
367
|
-
rightPlayers.forEach((player) => {
|
|
368
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
369
|
-
this.drawText(ctx2, name2, rightColumnX, currentY, {
|
|
370
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
371
|
-
color: COLORS.textLighter
|
|
372
|
-
});
|
|
373
|
-
currentY += params.rowHeight;
|
|
374
|
-
displayedCount++;
|
|
375
|
-
});
|
|
376
|
-
y = Math.max(currentY + 15, y);
|
|
377
|
-
const totalDisplayed = leftPlayers.length + rightPlayers.length;
|
|
378
|
-
if (players.length > totalDisplayed) {
|
|
379
|
-
this.drawText(ctx2, `... 还有 ${players.length - totalDisplayed} 位玩家未显示`, leftColumnX, y, {
|
|
380
|
-
fontSize: config.fontSize * 0.8,
|
|
381
|
-
color: COLORS.textLight,
|
|
382
|
-
italic: true
|
|
383
|
-
});
|
|
384
|
-
y += 30;
|
|
385
|
-
}
|
|
386
|
-
return { y, displayedCount };
|
|
387
|
-
} else {
|
|
388
|
-
const displayPlayers = sortedPlayers.slice(0, config.maxPlayers);
|
|
389
|
-
displayPlayers.forEach((player) => {
|
|
390
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
391
|
-
this.drawText(ctx2, name2, 80, y, {
|
|
392
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
393
|
-
color: COLORS.textLighter
|
|
394
|
-
});
|
|
395
|
-
y += params.rowHeight;
|
|
396
|
-
});
|
|
397
|
-
return { y, displayedCount: displayPlayers.length };
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
// 边框绘制函数
|
|
401
|
-
drawBorder(ctx2, width, height) {
|
|
402
|
-
this.drawDivider(ctx2, 1, 1, width - 1, 1, COLORS.border, 2);
|
|
403
|
-
this.drawDivider(ctx2, width - 1, 1, width - 1, height - 1, COLORS.border, 2);
|
|
404
|
-
this.drawDivider(ctx2, width - 1, height - 1, 1, height - 1, COLORS.border, 2);
|
|
405
|
-
this.drawDivider(ctx2, 1, height - 1, 1, 1, COLORS.border, 2);
|
|
406
|
-
this.drawDivider(ctx2, 5, 0.5 * height - 0.05 * height, 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
407
|
-
this.drawDivider(ctx2, width - 5, 0.5 * height - 0.05 * height, width - 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
408
|
-
this.drawDivider(ctx2, 2, 2, 0.025 * width, 2, COLORS.accent, 3);
|
|
409
|
-
this.drawDivider(ctx2, 2, 2, 2, 0.025 * width, COLORS.accent, 3);
|
|
410
|
-
this.drawDivider(ctx2, width - 2, 2, width - 2, 0.025 * width, COLORS.accent, 3);
|
|
411
|
-
this.drawDivider(ctx2, width - 2, 2, width - 0.025 * width, 2, COLORS.accent, 3);
|
|
412
|
-
this.drawDivider(ctx2, width - 2, height - 2, width - 2, height - 0.025 * width, COLORS.accent, 3);
|
|
413
|
-
this.drawDivider(ctx2, width - 2, height - 2, width - 0.025 * width, height - 2, COLORS.accent, 3);
|
|
414
|
-
this.drawDivider(ctx2, 2, height - 2, 0.025 * width, height - 2, COLORS.accent, 3);
|
|
415
|
-
this.drawDivider(ctx2, 2, height - 2, 2, height - 0.025 * width, COLORS.accent, 3);
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
function calculateImageHeight(data) {
|
|
274
|
+
function generateServerHTML(data, host, port) {
|
|
419
275
|
const { result } = data;
|
|
420
276
|
const playerCount = result.players?.length || 0;
|
|
421
|
-
const
|
|
422
|
-
|
|
277
|
+
const botCount = result.bots?.length || 0;
|
|
278
|
+
const maxPlayers = result.maxplayers || 0;
|
|
279
|
+
const cleanName = result.name ? utils.cleanName(result.name) : "未知服务器";
|
|
280
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
|
|
281
|
+
let playersHTML = "";
|
|
423
282
|
if (playerCount === 0) {
|
|
424
|
-
|
|
283
|
+
playersHTML = `<div class="player-row" style="color: ${COLORS.textLight};">服务器当前无玩家在线</div>`;
|
|
425
284
|
} else {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
285
|
+
const sortedPlayers = [...result.players].sort(
|
|
286
|
+
(a, b) => utils.cleanName(a.name).localeCompare(utils.cleanName(b.name))
|
|
287
|
+
);
|
|
288
|
+
const displayPlayers = sortedPlayers.slice(0, config.maxPlayers);
|
|
289
|
+
const needTwoColumns = playerCount > 10;
|
|
290
|
+
if (needTwoColumns) {
|
|
291
|
+
const half = Math.ceil(displayPlayers.length / 2);
|
|
292
|
+
const left = displayPlayers.slice(0, half);
|
|
293
|
+
const right = displayPlayers.slice(half, half * 2);
|
|
294
|
+
playersHTML = '<div style="display: flex; gap: 40px;">';
|
|
295
|
+
playersHTML += "<div>" + left.map(
|
|
296
|
+
(p) => `<div class="player-row">${utils.truncateText(utils.cleanName(p.name), 30)}</div>`
|
|
297
|
+
).join("") + "</div>";
|
|
298
|
+
playersHTML += "<div>" + right.map(
|
|
299
|
+
(p) => `<div class="player-row">${utils.truncateText(utils.cleanName(p.name), 30)}</div>`
|
|
300
|
+
).join("") + "</div>";
|
|
301
|
+
playersHTML += "</div>";
|
|
430
302
|
} else {
|
|
431
|
-
|
|
432
|
-
|
|
303
|
+
playersHTML = displayPlayers.map(
|
|
304
|
+
(p) => `<div class="player-row">${utils.truncateText(utils.cleanName(p.name), 40)}</div>`
|
|
305
|
+
).join("");
|
|
433
306
|
}
|
|
434
307
|
if (playerCount > config.maxPlayers) {
|
|
435
|
-
|
|
308
|
+
playersHTML += `<div class="player-row" style="color: ${COLORS.textLight}; font-style: italic;">... 还有 ${playerCount - config.maxPlayers} 位玩家未显示</div>`;
|
|
436
309
|
}
|
|
437
310
|
}
|
|
438
|
-
|
|
439
|
-
|
|
311
|
+
return `<!DOCTYPE html>
|
|
312
|
+
<html>
|
|
313
|
+
<head>
|
|
314
|
+
<meta charset="UTF-8">
|
|
315
|
+
<style>
|
|
316
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
317
|
+
body {
|
|
318
|
+
background: rgba(28,28,31,0.8);
|
|
319
|
+
font-family: ${config.fontFamily};
|
|
320
|
+
width: ${config.imageWidth}px;
|
|
321
|
+
min-height: ${config.imageHeight}px;
|
|
322
|
+
padding: 40px;
|
|
323
|
+
color: ${COLORS.text};
|
|
324
|
+
position: relative;
|
|
325
|
+
border: 2px solid ${COLORS.border};
|
|
440
326
|
}
|
|
441
|
-
|
|
442
|
-
|
|
327
|
+
/* 边框装饰 */
|
|
328
|
+
.corner {
|
|
329
|
+
position: absolute;
|
|
330
|
+
width: 25px;
|
|
331
|
+
height: 25px;
|
|
332
|
+
border-color: ${COLORS.accent};
|
|
333
|
+
border-style: solid;
|
|
334
|
+
border-width: 0;
|
|
443
335
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const titleY = 80;
|
|
456
|
-
imageUtils.drawTitle(ctx2d, "[服务器状态查询]", width / 2, titleY, config.fontSize * 1.5, config.fontFamily, COLORS.title);
|
|
457
|
-
if (result.name) {
|
|
458
|
-
const cleanName = utils.cleanName(result.name);
|
|
459
|
-
const fontSize = imageUtils.calculateServerNameFontSize(ctx2d, cleanName, width - 160, config.fontSize);
|
|
460
|
-
imageUtils.drawTitle(ctx2d, cleanName, width / 2, titleY + 50, fontSize * 1.8, config.fontFamily, COLORS.highlight);
|
|
461
|
-
}
|
|
462
|
-
imageUtils.drawDivider(ctx2d, 80, titleY + 80, width - 80, titleY + 80, COLORS.border, 2);
|
|
463
|
-
let y = titleY + 120;
|
|
464
|
-
if (result.map) {
|
|
465
|
-
imageUtils.drawText(ctx2d, `地图: ${result.map}`, 80, y);
|
|
466
|
-
}
|
|
467
|
-
imageUtils.drawText(ctx2d, `IP: ${host}:${port}`, width - 80, y, { align: "right" });
|
|
468
|
-
y += 40;
|
|
469
|
-
const playerCount = result.players?.length || 0;
|
|
470
|
-
const botCount = result.bots?.length || 0;
|
|
471
|
-
const maxPlayers = result.maxplayers || 0;
|
|
472
|
-
const playerText = `人数: ${playerCount}/${maxPlayers}${botCount > 0 ? ` (${botCount} Bot)` : ""}`;
|
|
473
|
-
imageUtils.drawText(ctx2d, playerText, 80, y, { color: utils.getPlayerColor(playerCount) });
|
|
474
|
-
if (result.ping) {
|
|
475
|
-
imageUtils.drawText(ctx2d, `Ping: ${result.ping}ms`, width - 80, y, {
|
|
476
|
-
align: "right",
|
|
477
|
-
color: utils.getPingColor(result.ping)
|
|
478
|
-
});
|
|
336
|
+
.corner-tl { top: 2px; left: 2px; border-top-width: 3px; border-left-width: 3px; }
|
|
337
|
+
.corner-tr { top: 2px; right: 2px; border-top-width: 3px; border-right-width: 3px; }
|
|
338
|
+
.corner-bl { bottom: 2px; left: 2px; border-bottom-width: 3px; border-left-width: 3px; }
|
|
339
|
+
.corner-br { bottom: 2px; right: 2px; border-bottom-width: 3px; border-right-width: 3px; }
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
.title {
|
|
343
|
+
text-align: center;
|
|
344
|
+
font-size: ${config.fontSize * 1.5}px;
|
|
345
|
+
color: ${COLORS.title};
|
|
346
|
+
margin-bottom: 20px;
|
|
479
347
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
348
|
+
.server-name {
|
|
349
|
+
text-align: center;
|
|
350
|
+
font-size: ${config.fontSize * 1.8}px;
|
|
351
|
+
font-weight: bold;
|
|
352
|
+
color: ${COLORS.highlight};
|
|
353
|
+
margin: 10px 0 20px;
|
|
354
|
+
word-break: break-word;
|
|
355
|
+
}
|
|
356
|
+
.divider {
|
|
357
|
+
height: 2px;
|
|
358
|
+
background: ${COLORS.border};
|
|
359
|
+
margin: 20px 0;
|
|
360
|
+
}
|
|
361
|
+
.info-row {
|
|
362
|
+
display: flex;
|
|
363
|
+
justify-content: space-between;
|
|
364
|
+
margin: 15px 0;
|
|
365
|
+
font-size: ${config.fontSize}px;
|
|
366
|
+
}
|
|
367
|
+
.player-section {
|
|
368
|
+
margin-top: 20px;
|
|
369
|
+
}
|
|
370
|
+
.player-section-title {
|
|
371
|
+
font-size: ${config.fontSize}px;
|
|
372
|
+
font-weight: bold;
|
|
373
|
+
color: ${COLORS.playerName};
|
|
374
|
+
margin-bottom: 10px;
|
|
375
|
+
}
|
|
376
|
+
.player-row {
|
|
377
|
+
font-size: ${config.fontSize * 0.9}px;
|
|
378
|
+
color: ${COLORS.textLighter};
|
|
379
|
+
line-height: 1.8;
|
|
380
|
+
}
|
|
381
|
+
.timestamp {
|
|
382
|
+
margin-top: 30px;
|
|
383
|
+
font-size: ${config.fontSize * 0.8}px;
|
|
384
|
+
color: ${COLORS.timestamp};
|
|
385
|
+
text-align: left;
|
|
386
|
+
}
|
|
387
|
+
</style>
|
|
388
|
+
</head>
|
|
389
|
+
<body>
|
|
390
|
+
<div class="corner corner-tl"></div>
|
|
391
|
+
<div class="corner corner-tr"></div>
|
|
392
|
+
<div class="corner corner-bl"></div>
|
|
393
|
+
<div class="corner corner-br"></div>
|
|
394
|
+
|
|
395
|
+
<div class="title">[服务器状态查询]</div>
|
|
396
|
+
<div class="server-name">${cleanName}</div>
|
|
397
|
+
<div class="divider"></div>
|
|
398
|
+
|
|
399
|
+
<div class="info-row">
|
|
400
|
+
<span>地图: ${result.map || "未知"}</span>
|
|
401
|
+
<span>IP: ${host}:${port}</span>
|
|
402
|
+
</div>
|
|
403
|
+
<div class="info-row">
|
|
404
|
+
<span style="color: ${utils.getPlayerColor(playerCount)};">人数: ${playerCount}/${maxPlayers}${botCount ? ` (${botCount} Bot)` : ""}</span>
|
|
405
|
+
<span style="color: ${utils.getPingColor(result.ping)};">Ping: ${result.ping ? result.ping + "ms" : "未知"}</span>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
<div class="player-section">
|
|
409
|
+
<div class="player-section-title">在线玩家</div>
|
|
410
|
+
<div class="divider" style="margin: 5px 0 15px;"></div>
|
|
411
|
+
${playersHTML}
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
<div class="timestamp">查询时间: ${now}</div>
|
|
415
|
+
</body>
|
|
416
|
+
</html>`;
|
|
496
417
|
}
|
|
497
|
-
__name(
|
|
498
|
-
|
|
418
|
+
__name(generateServerHTML, "generateServerHTML");
|
|
419
|
+
function generateBatchHTML(results, serversToQuery, queryTime) {
|
|
499
420
|
const successful = results.filter((r) => r.status === "fulfilled" && r.value.success).length;
|
|
500
421
|
const failed = results.length - successful;
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
const width = config.imageWidth;
|
|
504
|
-
const height = baseHeight + results.length * serverHeight;
|
|
505
|
-
const canvas = await ctx.canvas.createCanvas(width, height);
|
|
506
|
-
const ctx2d = canvas.getContext("2d");
|
|
507
|
-
imageUtils.drawBackground(ctx2d, width, height);
|
|
508
|
-
imageUtils.drawTitle(ctx2d, "[服务器状态批量查询]", width / 2, 100, config.fontSize * 1.8, config.fontFamily, COLORS.title);
|
|
509
|
-
const now = /* @__PURE__ */ new Date();
|
|
510
|
-
imageUtils.drawText(ctx2d, `查询时间: ${now.toLocaleString("zh-CN")}`, 80, 150);
|
|
511
|
-
imageUtils.drawText(ctx2d, `耗时: ${utils.formatTime(queryTime)} 成功: ${successful}/${results.length}`, width - 80, 150, { align: "right" });
|
|
512
|
-
imageUtils.drawDivider(ctx2d, 80, 165, width - 80, 165, COLORS.gold, 2);
|
|
513
|
-
let y = 200;
|
|
422
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
|
|
423
|
+
let serversHTML = "";
|
|
514
424
|
results.forEach((result, index) => {
|
|
515
425
|
const server = serversToQuery[index];
|
|
516
|
-
if (result.status === "fulfilled") {
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
color:
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (serverData.map) {
|
|
540
|
-
imageUtils.drawText(ctx2d, `地图: ${serverData.map}`, 80, y + 60, {
|
|
541
|
-
fontSize: config.fontSize * 0.8,
|
|
542
|
-
color: COLORS.textLight
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
if (serverData.ping) {
|
|
546
|
-
const pingColor = utils.getPingColor(serverData.ping);
|
|
547
|
-
imageUtils.drawText(ctx2d, `延迟: ${serverData.ping}ms`, width - 80, y + 60, {
|
|
548
|
-
align: "right",
|
|
549
|
-
fontSize: config.fontSize * 0.9,
|
|
550
|
-
color: pingColor
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
} else {
|
|
554
|
-
imageUtils.drawText(ctx2d, `${index + 1}. ${server}`, 80, y, { color: COLORS.textWhite, bold: true });
|
|
555
|
-
imageUtils.drawText(ctx2d, `❌ 查询失败: ${error}`, 200, y + 35, { color: COLORS.error });
|
|
556
|
-
}
|
|
426
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
427
|
+
const data = result.value.data.result;
|
|
428
|
+
const name2 = data.name ? utils.cleanName(data.name) : "未知";
|
|
429
|
+
const playerCount = data.players?.length || 0;
|
|
430
|
+
const maxPlayers = data.maxplayers || 0;
|
|
431
|
+
const map = data.map || "";
|
|
432
|
+
const ping = data.ping || "?";
|
|
433
|
+
const pingColor = utils.getPingColor(ping);
|
|
434
|
+
const playerColor = playerCount > 0 ? COLORS.success : COLORS.error;
|
|
435
|
+
serversHTML += `
|
|
436
|
+
<div class="server-item">
|
|
437
|
+
<div class="server-header">
|
|
438
|
+
<span class="server-index">${index + 1}.</span>
|
|
439
|
+
<span class="server-name">${name2}</span>
|
|
440
|
+
<span class="server-players" style="color: ${playerColor};">${playerCount}/${maxPlayers}</span>
|
|
441
|
+
</div>
|
|
442
|
+
<div class="server-details">
|
|
443
|
+
<span class="server-addr">${server}</span>
|
|
444
|
+
<span class="server-map">地图: ${map}</span>
|
|
445
|
+
<span class="server-ping" style="color: ${pingColor};">延迟: ${ping}ms</span>
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
`;
|
|
557
449
|
} else {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
450
|
+
const errorMsg = result.value?.error || "未知错误";
|
|
451
|
+
serversHTML += `
|
|
452
|
+
<div class="server-item error">
|
|
453
|
+
<div class="server-header">
|
|
454
|
+
<span class="server-index">${index + 1}.</span>
|
|
455
|
+
<span class="server-name">${server}</span>
|
|
456
|
+
<span class="server-status">❌ 查询失败</span>
|
|
457
|
+
</div>
|
|
458
|
+
<div class="server-details error-msg">${errorMsg}</div>
|
|
459
|
+
</div>
|
|
460
|
+
`;
|
|
563
461
|
}
|
|
564
|
-
y += 100;
|
|
565
462
|
});
|
|
566
|
-
|
|
567
|
-
|
|
463
|
+
return `<!DOCTYPE html>
|
|
464
|
+
<html>
|
|
465
|
+
<head>
|
|
466
|
+
<meta charset="UTF-8">
|
|
467
|
+
<style>
|
|
468
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
469
|
+
body {
|
|
470
|
+
background: rgba(28,28,31,0.8);
|
|
471
|
+
font-family: ${config.fontFamily};
|
|
472
|
+
width: ${config.imageWidth}px;
|
|
473
|
+
min-height: ${config.imageHeight}px;
|
|
474
|
+
padding: 40px;
|
|
475
|
+
color: ${COLORS.text};
|
|
476
|
+
position: relative;
|
|
477
|
+
border: 2px solid ${COLORS.border};
|
|
478
|
+
}
|
|
479
|
+
.corner {
|
|
480
|
+
position: absolute;
|
|
481
|
+
width: 25px;
|
|
482
|
+
height: 25px;
|
|
483
|
+
border-color: ${COLORS.accent};
|
|
484
|
+
border-style: solid;
|
|
485
|
+
border-width: 0;
|
|
486
|
+
}
|
|
487
|
+
.corner-tl { top: 2px; left: 2px; border-top-width: 3px; border-left-width: 3px; }
|
|
488
|
+
.corner-tr { top: 2px; right: 2px; border-top-width: 3px; border-right-width: 3px; }
|
|
489
|
+
.corner-bl { bottom: 2px; left: 2px; border-bottom-width: 3px; border-left-width: 3px; }
|
|
490
|
+
.corner-br { bottom: 2px; right: 2px; border-bottom-width: 3px; border-right-width: 3px; }
|
|
491
|
+
|
|
492
|
+
.title {
|
|
493
|
+
text-align: center;
|
|
494
|
+
font-size: ${config.fontSize * 1.8}px;
|
|
495
|
+
color: ${COLORS.title};
|
|
496
|
+
margin-bottom: 20px;
|
|
497
|
+
}
|
|
498
|
+
.stats {
|
|
499
|
+
display: flex;
|
|
500
|
+
justify-content: space-between;
|
|
501
|
+
font-size: ${config.fontSize}px;
|
|
502
|
+
margin-bottom: 10px;
|
|
503
|
+
}
|
|
504
|
+
.divider {
|
|
505
|
+
height: 2px;
|
|
506
|
+
background: ${COLORS.gold};
|
|
507
|
+
margin: 15px 0 30px;
|
|
508
|
+
}
|
|
509
|
+
.server-item {
|
|
510
|
+
margin-bottom: 30px;
|
|
511
|
+
border-bottom: 1px solid ${COLORS.divider};
|
|
512
|
+
padding-bottom: 20px;
|
|
513
|
+
}
|
|
514
|
+
.server-item:last-child {
|
|
515
|
+
border-bottom: none;
|
|
516
|
+
}
|
|
517
|
+
.server-header {
|
|
518
|
+
display: flex;
|
|
519
|
+
align-items: center;
|
|
520
|
+
gap: 10px;
|
|
521
|
+
font-size: ${config.fontSize * 1.1}px;
|
|
522
|
+
font-weight: bold;
|
|
523
|
+
color: ${COLORS.textWhite};
|
|
524
|
+
margin-bottom: 8px;
|
|
525
|
+
}
|
|
526
|
+
.server-index {
|
|
527
|
+
color: ${COLORS.accent};
|
|
528
|
+
}
|
|
529
|
+
.server-players {
|
|
530
|
+
margin-left: auto;
|
|
531
|
+
}
|
|
532
|
+
.server-details {
|
|
533
|
+
display: flex;
|
|
534
|
+
flex-wrap: wrap;
|
|
535
|
+
gap: 20px;
|
|
536
|
+
font-size: ${config.fontSize * 0.9}px;
|
|
537
|
+
color: ${COLORS.textLight};
|
|
538
|
+
}
|
|
539
|
+
.server-details span {
|
|
540
|
+
white-space: nowrap;
|
|
541
|
+
}
|
|
542
|
+
.error .server-name {
|
|
543
|
+
color: ${COLORS.error};
|
|
544
|
+
}
|
|
545
|
+
.error-msg {
|
|
546
|
+
color: ${COLORS.error};
|
|
547
|
+
font-size: ${config.fontSize}px;
|
|
548
|
+
}
|
|
549
|
+
.timestamp {
|
|
550
|
+
margin-top: 20px;
|
|
551
|
+
font-size: ${config.fontSize * 0.8}px;
|
|
552
|
+
color: ${COLORS.timestamp};
|
|
553
|
+
}
|
|
554
|
+
</style>
|
|
555
|
+
</head>
|
|
556
|
+
<body>
|
|
557
|
+
<!-- 边框装饰 -->
|
|
558
|
+
<div class="corner corner-tl"></div>
|
|
559
|
+
<div class="corner corner-tr"></div>
|
|
560
|
+
<div class="corner corner-bl"></div>
|
|
561
|
+
<div class="corner corner-br"></div>
|
|
562
|
+
|
|
563
|
+
<div class="title">[服务器状态批量查询]</div>
|
|
564
|
+
<div class="stats">
|
|
565
|
+
<span>查询时间: ${now}</span>
|
|
566
|
+
<span>耗时: ${utils.formatTime(queryTime)} 成功: ${successful}/${results.length}</span>
|
|
567
|
+
</div>
|
|
568
|
+
<div class="divider"></div>
|
|
569
|
+
|
|
570
|
+
${serversHTML}
|
|
571
|
+
|
|
572
|
+
<div class="timestamp">📋 输入 \`cs <服务器地址>\` 查询单个服务器</div>
|
|
573
|
+
</body>
|
|
574
|
+
</html>`;
|
|
575
|
+
}
|
|
576
|
+
__name(generateBatchHTML, "generateBatchHTML");
|
|
577
|
+
async function generateServerImage(data, host, port) {
|
|
578
|
+
const html = generateServerHTML(data, host, port);
|
|
579
|
+
const page = await ctx.puppeteer.page();
|
|
580
|
+
try {
|
|
581
|
+
await page.setViewport({
|
|
582
|
+
width: config.imageWidth,
|
|
583
|
+
height: config.imageHeight,
|
|
584
|
+
deviceScaleFactor: 2
|
|
585
|
+
});
|
|
586
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
587
|
+
const buffer = await page.screenshot({
|
|
588
|
+
fullPage: true,
|
|
589
|
+
type: "png"
|
|
590
|
+
});
|
|
591
|
+
return buffer;
|
|
592
|
+
} finally {
|
|
593
|
+
await page.close().catch(() => {
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
__name(generateServerImage, "generateServerImage");
|
|
598
|
+
async function generateBatchImage(results, serversToQuery, queryTime) {
|
|
599
|
+
const html = generateBatchHTML(results, serversToQuery, queryTime);
|
|
600
|
+
const page = await ctx.puppeteer.page();
|
|
601
|
+
try {
|
|
602
|
+
await page.setViewport({
|
|
603
|
+
width: config.imageWidth,
|
|
604
|
+
height: config.imageHeight,
|
|
605
|
+
deviceScaleFactor: 2
|
|
606
|
+
});
|
|
607
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
608
|
+
const buffer = await page.screenshot({
|
|
609
|
+
fullPage: true,
|
|
610
|
+
type: "png"
|
|
611
|
+
});
|
|
612
|
+
return buffer;
|
|
613
|
+
} finally {
|
|
614
|
+
await page.close().catch(() => {
|
|
615
|
+
});
|
|
616
|
+
}
|
|
568
617
|
}
|
|
569
618
|
__name(generateBatchImage, "generateBatchImage");
|
|
570
619
|
ctx.command("cs <address>", "查询服务器状态").alias("查询").alias("server").option("noPlayers", "-n 隐藏玩家列表", { type: Boolean, fallback: false }).option("image", "-i 生成图片横幅", { type: Boolean, fallback: false }).option("text", "-t 输出文本信息", { type: Boolean, fallback: false }).option("clear", "-c 清除缓存", { type: Boolean, fallback: false }).action(async ({ session, options }, address) => {
|
|
@@ -584,7 +633,11 @@ function apply(ctx, config) {
|
|
|
584
633
|
return import_koishi.h.image(imageBuffer, "image/png");
|
|
585
634
|
} catch (imageError) {
|
|
586
635
|
console.error("生成图片失败:", imageError);
|
|
587
|
-
return `生成图片失败: ${imageError.message}
|
|
636
|
+
return `生成图片失败: ${imageError.message},已转为文本输出。
|
|
637
|
+
|
|
638
|
+
${formatServerInfo(data)}
|
|
639
|
+
|
|
640
|
+
${formatPlayers(data.result.players || [])}`;
|
|
588
641
|
}
|
|
589
642
|
}
|
|
590
643
|
let message = formatServerInfo(data);
|
|
@@ -615,21 +668,20 @@ function apply(ctx, config) {
|
|
|
615
668
|
ctx.command("cs.status", "检查插件状态和配置").action(async () => {
|
|
616
669
|
try {
|
|
617
670
|
const gamedigStatus = ctx.gamedig ? "✅ 可用" : "❌ 不可用";
|
|
618
|
-
let
|
|
619
|
-
if (ctx.
|
|
671
|
+
let puppeteerStatus = "❌ 不可用";
|
|
672
|
+
if (ctx.puppeteer) {
|
|
620
673
|
try {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
canvasStatus = `❌ 不可用: ${error.message}`;
|
|
674
|
+
await ctx.puppeteer.render("<div>test</div>");
|
|
675
|
+
puppeteerStatus = "✅ 可用";
|
|
676
|
+
} catch (e) {
|
|
677
|
+
puppeteerStatus = `❌ 不可用: ${e.message}`;
|
|
626
678
|
}
|
|
627
679
|
}
|
|
628
680
|
const cacheSize = cache.size;
|
|
629
681
|
return `✅ CS服务器查询插件状态
|
|
630
682
|
💾 缓存数量: ${cacheSize} 条
|
|
631
683
|
🕹️ Gamedig插件: ${gamedigStatus}
|
|
632
|
-
🖼️
|
|
684
|
+
🖼️ Puppeteer插件: ${puppeteerStatus}
|
|
633
685
|
⚙️ 配置参数:
|
|
634
686
|
超时时间: ${config.timeout}ms
|
|
635
687
|
缓存时间: ${config.cacheTime}ms
|
|
@@ -638,14 +690,16 @@ function apply(ctx, config) {
|
|
|
638
690
|
显示VAC状态: ${config.showVAC ? "是" : "否"}
|
|
639
691
|
显示密码保护: ${config.showPassword ? "是" : "否"}
|
|
640
692
|
生成图片横幅: ${config.generateImage ? "是" : "否"}
|
|
693
|
+
图片宽度: ${config.imageWidth}px
|
|
641
694
|
图片最小高度: ${config.imageHeight}px
|
|
642
695
|
字体大小: ${config.fontSize}px
|
|
696
|
+
字体: ${config.fontFamily}
|
|
643
697
|
|
|
644
698
|
📝 使用: cs [地址:端口]
|
|
645
699
|
📝 选项: -i 生成图片, -t 输出文本, -c 清除缓存`;
|
|
646
700
|
} catch (error) {
|
|
647
701
|
return `❌ 插件状态异常: ${error.message}
|
|
648
|
-
请确保已安装并启用 koishi-plugin-gamedig 和 koishi-plugin-
|
|
702
|
+
请确保已安装并启用 koishi-plugin-gamedig 和 koishi-plugin-puppeteer`;
|
|
649
703
|
}
|
|
650
704
|
});
|
|
651
705
|
ctx.command("cs.help", "查看帮助").action(() => {
|
|
@@ -670,7 +724,7 @@ cs.help - 显示此帮助
|
|
|
670
724
|
1. 如果不指定端口,默认使用27015
|
|
671
725
|
2. 只支持CS服务器查询
|
|
672
726
|
3. 查询结果缓存${config.cacheTime}ms,使用 -c 清除缓存
|
|
673
|
-
4. 需要安装 koishi-plugin-gamedig 和 koishi-plugin-
|
|
727
|
+
4. 需要安装 koishi-plugin-gamedig 和 koishi-plugin-puppeteer 插件`;
|
|
674
728
|
});
|
|
675
729
|
ctx.command("csss", "批量查询服务器状态").alias("batch").alias("multi").alias("批量查询").option("list", "-l 显示配置的服务器列表", { type: Boolean, fallback: false }).option("add", "-a <address> 添加服务器到列表", { type: String }).option("remove", "-r <index> 从列表中移除服务器", { type: Number }).option("clear", "-c 清空服务器列表", { type: Boolean, fallback: false }).option("image", "-i 生成图片横幅", { type: Boolean, fallback: false }).option("text", "-t 输出文本信息", { type: Boolean, fallback: false }).action(async ({ session, options }, ...addresses) => {
|
|
676
730
|
if (options.list) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-csss",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "一个用于 Koishi 的 CS:GO / CS2 服务器状态查询插件,支持单个/批量查询、服务器列表管理,并可将结果渲染为图片",
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
],
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"koishi": "^4.18.7",
|
|
24
|
-
"koishi-plugin-
|
|
24
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
25
25
|
"koishi-plugin-gamedig": "^1.2.2"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"koishi": "^4.18.7",
|
|
29
|
-
"koishi-plugin-
|
|
29
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
30
30
|
"koishi-plugin-gamedig": "^1.2.2"
|
|
31
31
|
},
|
|
32
32
|
"koishi": {
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
},
|
|
36
36
|
"service": {
|
|
37
37
|
"required": [
|
|
38
|
-
"
|
|
39
|
-
"gamedig"
|
|
38
|
+
"puppeteer",
|
|
39
|
+
"gamedig",
|
|
40
|
+
"database"
|
|
40
41
|
]
|
|
41
42
|
}
|
|
42
43
|
}
|
package/readme.md
CHANGED
|
@@ -2,4 +2,185 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/koishi-plugin-csss)
|
|
4
4
|
|
|
5
|
-
cs server status
|
|
5
|
+
cs server status - 一个用于 Koishi 的 CS:GO / CS2 服务器状态查询插件,支持单个/批量查询、服务器列表管理,并可将结果渲染为图片。
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
- **单个/批量服务器查询**:查询指定/多个IP的 CS 服务器信息,支持从数据库读取预设列表或临时输入(基于gamedig)。
|
|
10
|
+
- **服务器列表管理**:在数据库中添加、移除、查看和清空常用服务器地址。
|
|
11
|
+
- **图片渲染**:将查询结果生成为游戏风格的图片横幅(基于 Puppeteer)。
|
|
12
|
+
|
|
13
|
+
## 依赖插件
|
|
14
|
+
|
|
15
|
+
本插件强依赖以下插件,请确保它们已安装并启用:
|
|
16
|
+
|
|
17
|
+
- `koishi-plugin-gamedig`:用于查询 Source 引擎服务器。
|
|
18
|
+
- `koishi-plugin-puppeteer`:用于将 HTML 渲染为图片。
|
|
19
|
+
- `database`:用于存储服务器列表。
|
|
20
|
+
|
|
21
|
+
## 命令列表
|
|
22
|
+
|
|
23
|
+
### `cs <address:string>` - 查询单个服务器
|
|
24
|
+
|
|
25
|
+
查询指定地址的 CS 服务器状态。
|
|
26
|
+
|
|
27
|
+
**别名**:`查询`、`server`
|
|
28
|
+
|
|
29
|
+
**选项**:
|
|
30
|
+
|
|
31
|
+
- `-i, --image`:强制生成图片横幅(即使配置中关闭了图片生成)。
|
|
32
|
+
|
|
33
|
+
- `-t, --text`:强制输出文本格式(即使配置中开启了图片生成)。
|
|
34
|
+
|
|
35
|
+
- `-c, --clear`:清除插件运行时的内存缓存。
|
|
36
|
+
|
|
37
|
+
**用法**:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cs 127.0.0.1:27015
|
|
41
|
+
cs edgebug.cn
|
|
42
|
+
cs [::1]:27015 -i
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `cs.status` - 检查插件状态
|
|
46
|
+
|
|
47
|
+
显示插件当前运行状态、缓存数量和依赖可用性。
|
|
48
|
+
|
|
49
|
+
**用法**:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cs.status
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `cs.help` - 查看帮助信息
|
|
56
|
+
|
|
57
|
+
显示本插件的命令帮助。
|
|
58
|
+
|
|
59
|
+
### `csss [...addresses]` - 批量查询服务器
|
|
60
|
+
|
|
61
|
+
批量查询多个服务器。如果不指定地址,将查询数据库中存储的服务器列表。
|
|
62
|
+
|
|
63
|
+
**别名**:`批量查询`
|
|
64
|
+
|
|
65
|
+
**选项**:
|
|
66
|
+
|
|
67
|
+
- `-l, --list`:显示数据库中保存的服务器列表。
|
|
68
|
+
|
|
69
|
+
- `-a <address>, --add <address>`:添加一个服务器到数据库列表。
|
|
70
|
+
|
|
71
|
+
- `-r <index>, --remove <index>`:从数据库列表中移除指定序号的服务器。
|
|
72
|
+
|
|
73
|
+
- `-c, --clear`:清空数据库中的服务器列表。
|
|
74
|
+
|
|
75
|
+
- `-i, --image`:强制生成图片横幅。
|
|
76
|
+
|
|
77
|
+
- `-t, --text`:强制输出文本格式。
|
|
78
|
+
|
|
79
|
+
**用法**:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# 查询数据库中的所有服务器
|
|
83
|
+
csss
|
|
84
|
+
|
|
85
|
+
# 临时查询指定的几个服务器
|
|
86
|
+
csss 192.168.1.1:27015 game.example.com:27016
|
|
87
|
+
|
|
88
|
+
# 管理数据库列表
|
|
89
|
+
csss -l #显示数据库中保存的服务器列表
|
|
90
|
+
csss -a 127.0.0.1:27015 #将127.0.0.1:27015添加到数据库列表
|
|
91
|
+
csss -r 1 #从数据库列表中移除序号为1的服务器
|
|
92
|
+
csss -c #清空数据库中的服务器列表
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 自定义样式指南
|
|
96
|
+
|
|
97
|
+
`koishi-plugin-csss` 支持通过 `customCSS` 配置项注入自定义 CSS,让您能自由调整生成图片的视觉效果。本指南将说明可用的 HTML 结构、类名和修改示例。
|
|
98
|
+
|
|
99
|
+
### HTML 结构概览
|
|
100
|
+
|
|
101
|
+
### 单个服务器查询 (cs)
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<body>
|
|
105
|
+
<div class="corner corner-tl"></div>
|
|
106
|
+
<div class="corner corner-tr"></div>
|
|
107
|
+
<div class="corner corner-bl"></div>
|
|
108
|
+
<div class="corner corner-br"></div>
|
|
109
|
+
|
|
110
|
+
<div class="title">[服务器状态查询]</div>
|
|
111
|
+
<div class="server-name">服务器名称</div>
|
|
112
|
+
<div class="divider"></div>
|
|
113
|
+
|
|
114
|
+
<div class="info-row">
|
|
115
|
+
<span>地图: de_dust2</span>
|
|
116
|
+
<span>IP: 127.0.0.1:27015</span>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="info-row">
|
|
119
|
+
<span style="color: #4CAF50;">人数: 12/32</span>
|
|
120
|
+
<span style="color: #4CAF50;">Ping: 45ms</span>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="player-section">
|
|
124
|
+
<div class="player-section-title">在线玩家</div>
|
|
125
|
+
<div class="divider" style="margin: 5px 0 15px;"></div>
|
|
126
|
+
<!-- 玩家列表 -->
|
|
127
|
+
<div class="player-row">玩家名1</div>
|
|
128
|
+
<div class="player-row">玩家名2</div>
|
|
129
|
+
...
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="timestamp">查询时间: 2024/1/1 12:00:00</div>
|
|
133
|
+
</body>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 批量服务器查询 (csss)
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<body>
|
|
140
|
+
<!-- 四角边框装饰 -->
|
|
141
|
+
<div class="corner corner-tl"></div> ...
|
|
142
|
+
|
|
143
|
+
<div class="title">[服务器状态批量查询]</div>
|
|
144
|
+
<div class="stats">
|
|
145
|
+
<span>查询时间: ...</span>
|
|
146
|
+
<span>耗时: 2.1秒 | 成功: 3/5</span>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="divider"></div>
|
|
149
|
+
|
|
150
|
+
<!-- 每个服务器一个 item -->
|
|
151
|
+
<div class="server-item">
|
|
152
|
+
<div class="server-header">
|
|
153
|
+
<span class="server-index">1.</span>
|
|
154
|
+
<span class="server-name">服务器A</span>
|
|
155
|
+
<span class="server-players" style="color: #4CAF50;">12/32</span>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="server-details">
|
|
158
|
+
<span class="server-addr">127.0.0.1:27015</span>
|
|
159
|
+
<span class="server-ping" style="color: #4CAF50;">延迟: 45ms</span>
|
|
160
|
+
</div>
|
|
161
|
+
<div class="server-details">
|
|
162
|
+
<span class="server-map">地图: de_dust2</span>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- 查询失败的条目 -->
|
|
167
|
+
<div class="server-item error">
|
|
168
|
+
<div class="server-header">
|
|
169
|
+
<span class="server-index">2.</span>
|
|
170
|
+
<span class="server-name">192.168.1.1:27016</span>
|
|
171
|
+
<span class="server-status">❌ 查询失败</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="server-details error-msg">连接超时</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="timestamp">📋 输入 `cs 服务器地址` 查询单个服务器</div>
|
|
177
|
+
</body>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 注意事项
|
|
181
|
+
|
|
182
|
+
- 使用`!important`:默认样式通常带有较高优先级,自定义时建议加`!important`确保覆盖。
|
|
183
|
+
|
|
184
|
+
- 字体支持:请确保运行环境已安装您指定的字体,否则将回退到默认等宽字体。
|
|
185
|
+
|
|
186
|
+
- 图片尺寸:`imageWidth` 和 `imageHeight` 在配置中设定,但可通过 CSS 覆盖 `body` 的 `width` 和 `min-height`。
|
package/lib/index.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Context, Schema } from 'koishi';
|
|
2
|
-
export declare const name = "csss";
|
|
3
|
-
export declare const inject: string[];
|
|
4
|
-
export interface Config {
|
|
5
|
-
timeout: number;
|
|
6
|
-
cacheTime: number;
|
|
7
|
-
maxPlayers: number;
|
|
8
|
-
retryCount: number;
|
|
9
|
-
showVAC: boolean;
|
|
10
|
-
showPassword: boolean;
|
|
11
|
-
generateImage: boolean;
|
|
12
|
-
imageWidth: number;
|
|
13
|
-
imageHeight: number;
|
|
14
|
-
fontSize: number;
|
|
15
|
-
fontFamily: string;
|
|
16
|
-
serverList: string[];
|
|
17
|
-
batchTimeout: number;
|
|
18
|
-
}
|
|
19
|
-
export declare const Config: Schema<Config>;
|
|
20
|
-
export declare function apply(ctx: Context, config: Config): void;
|