koishi-plugin-csss 1.2.1 → 2.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 +355 -536
- package/package.json +9 -4
- package/readme.md +15 -0
- package/lib/index.d.ts +0 -28
package/lib/index.js
CHANGED
|
@@ -27,7 +27,8 @@ __export(src_exports, {
|
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(src_exports);
|
|
29
29
|
var import_koishi = require("koishi");
|
|
30
|
-
var name = "
|
|
30
|
+
var name = "csss";
|
|
31
|
+
var inject = ["puppeteer", "gamedig", "database"];
|
|
31
32
|
var Config = import_koishi.Schema.object({
|
|
32
33
|
timeout: import_koishi.Schema.number().min(1e3).max(3e4).default(5e3).description("查询超时时间(毫秒)"),
|
|
33
34
|
cacheTime: import_koishi.Schema.number().min(0).max(3e5).default(3e4).description("缓存时间(毫秒,0为禁用缓存)"),
|
|
@@ -39,29 +40,19 @@ var Config = import_koishi.Schema.object({
|
|
|
39
40
|
imageWidth: import_koishi.Schema.number().min(600).max(2e3).default(1200).description("图片宽度(像素)"),
|
|
40
41
|
imageHeight: import_koishi.Schema.number().min(200).max(2500).default(500).description("图片最小高度(像素),实际高度会根据内容自适应"),
|
|
41
42
|
fontSize: import_koishi.Schema.number().min(12).max(48).default(24).description("字体大小"),
|
|
42
|
-
fontFamily: import_koishi.Schema.string().default('"JetBrains Mono", monospace').description("
|
|
43
|
-
serverList: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").description("批量查询服务器列表(格式:
|
|
43
|
+
fontFamily: import_koishi.Schema.string().default('"JetBrains Mono", monospace').description("字体"),
|
|
44
|
+
serverList: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").description("批量查询服务器列表(格式: [地址]:[端口],每行一个)").default([
|
|
44
45
|
"edgebug.cn:27015",
|
|
45
46
|
"edgebug.cn:27016",
|
|
46
47
|
"edgebug.cn:27017",
|
|
47
48
|
"edgebug.cn:27018",
|
|
48
49
|
"edgebug.cn:27019"
|
|
49
50
|
]),
|
|
50
|
-
batchTimeout: import_koishi.Schema.number().min(1e3).max(6e4).default(15e3).description("批量查询总超时时间(毫秒)")
|
|
51
|
-
// 定时任务配置
|
|
52
|
-
scheduleEnabled: import_koishi.Schema.boolean().default(false).description("是否启用定时自动查询功能"),
|
|
53
|
-
scheduleInterval: import_koishi.Schema.number().min(1).max(1440).default(5).description("定时查询间隔时间(分钟)"),
|
|
54
|
-
scheduleStartTime: import_koishi.Schema.string().pattern(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/).default("08:00").description("定时任务开始时间(24小时制,格式: HH:MM)"),
|
|
55
|
-
scheduleEndTime: import_koishi.Schema.string().pattern(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/).default("23:00").description("定时任务结束时间(24小时制,格式: HH:MM)"),
|
|
56
|
-
scheduleGroups: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").description("定时发送的群组ID列表(每行一个群组ID)").default([]),
|
|
57
|
-
scheduleUseImage: import_koishi.Schema.boolean().default(true).description("定时任务是否使用图片格式输出"),
|
|
58
|
-
// QQ适配器配置
|
|
59
|
-
qqAdapterName: import_koishi.Schema.string().default("qq").description('QQ适配器名称(默认为"qq",如果在QQ配置中指定了其他名称请修改)'),
|
|
60
|
-
useFullChannelId: import_koishi.Schema.boolean().default(true).description("是否使用完整的频道ID格式(推荐开启)")
|
|
51
|
+
batchTimeout: import_koishi.Schema.number().min(1e3).max(6e4).default(15e3).description("批量查询总超时时间(毫秒)")
|
|
61
52
|
});
|
|
62
53
|
var COLORS = {
|
|
63
|
-
background: "
|
|
64
|
-
text: "
|
|
54
|
+
background: "#1c1c1fcc",
|
|
55
|
+
text: "#71717a",
|
|
65
56
|
textLight: "#aaaaaa",
|
|
66
57
|
textLighter: "#dddddd",
|
|
67
58
|
textWhite: "#ffffff",
|
|
@@ -81,7 +72,7 @@ var COLORS = {
|
|
|
81
72
|
divider: "#555555",
|
|
82
73
|
timestamp: "#666666",
|
|
83
74
|
gold: "#FFD700",
|
|
84
|
-
playerName: "
|
|
75
|
+
playerName: "#fcf8de"
|
|
85
76
|
};
|
|
86
77
|
var utils = {
|
|
87
78
|
formatPing(ping) {
|
|
@@ -110,35 +101,17 @@ var utils = {
|
|
|
110
101
|
if (ms < 1e3) return `${ms}ms`;
|
|
111
102
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}秒`;
|
|
112
103
|
return `${(ms / 1e3).toFixed(0)}秒`;
|
|
113
|
-
},
|
|
114
|
-
parseTimeToMinutes(timeStr) {
|
|
115
|
-
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
116
|
-
return hours * 60 + minutes;
|
|
117
|
-
},
|
|
118
|
-
isWithinScheduleTime(startTime, endTime) {
|
|
119
|
-
const now = /* @__PURE__ */ new Date();
|
|
120
|
-
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
121
|
-
const startMinutes = this.parseTimeToMinutes(startTime);
|
|
122
|
-
const endMinutes = this.parseTimeToMinutes(endTime);
|
|
123
|
-
return currentMinutes >= startMinutes && currentMinutes <= endMinutes;
|
|
124
|
-
},
|
|
125
|
-
formatGroupId(groupId, adapterName, useFullChannelId) {
|
|
126
|
-
if (useFullChannelId) {
|
|
127
|
-
return `${adapterName}:${groupId}`;
|
|
128
|
-
}
|
|
129
|
-
return groupId;
|
|
130
104
|
}
|
|
131
105
|
};
|
|
132
106
|
function apply(ctx, config) {
|
|
133
107
|
const cache = /* @__PURE__ */ new Map();
|
|
134
|
-
let scheduleTimer = null;
|
|
135
108
|
if (!ctx.gamedig) {
|
|
136
109
|
console.error("koishi-plugin-gamedig 未安装或未启用");
|
|
137
110
|
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-gamedig 插件");
|
|
138
111
|
}
|
|
139
|
-
if (!ctx.
|
|
140
|
-
console.error("koishi-plugin-
|
|
141
|
-
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 插件");
|
|
142
115
|
}
|
|
143
116
|
async function queryServers(serversToQuery) {
|
|
144
117
|
const startTime = Date.now();
|
|
@@ -176,7 +149,7 @@ function apply(ctx, config) {
|
|
|
176
149
|
message += `✅ 成功: ${successful} 个 | ❌ 失败: ${failed} 个
|
|
177
150
|
|
|
178
151
|
`;
|
|
179
|
-
message += "序号 服务器名称
|
|
152
|
+
message += "序号 服务器名称 在线人数\n";
|
|
180
153
|
message += "──────────────────────────────\n";
|
|
181
154
|
results.forEach((result, index) => {
|
|
182
155
|
const serverInfo = serversToQuery[index];
|
|
@@ -203,77 +176,6 @@ function apply(ctx, config) {
|
|
|
203
176
|
return message;
|
|
204
177
|
}
|
|
205
178
|
__name(generateTextTable, "generateTextTable");
|
|
206
|
-
async function executeScheduleTask() {
|
|
207
|
-
if (!config.scheduleEnabled || config.scheduleGroups.length === 0 || config.serverList.length === 0) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (!utils.isWithinScheduleTime(config.scheduleStartTime, config.scheduleEndTime)) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
try {
|
|
214
|
-
const { results, queryTime, serversToQuery } = await queryServers(config.serverList);
|
|
215
|
-
const now = /* @__PURE__ */ new Date();
|
|
216
|
-
const timeStr = now.toLocaleString("zh-CN");
|
|
217
|
-
let outputContent;
|
|
218
|
-
if (config.scheduleUseImage) {
|
|
219
|
-
try {
|
|
220
|
-
const imageBuffer = await generateBatchImage(results, serversToQuery, queryTime);
|
|
221
|
-
outputContent = import_koishi.h.image(imageBuffer, "image/png");
|
|
222
|
-
} catch (imageError) {
|
|
223
|
-
console.error("定时任务生成图片失败:", imageError);
|
|
224
|
-
outputContent = `🕒 ${timeStr} 服务器状态更新
|
|
225
|
-
生成图片失败,使用文本格式:
|
|
226
|
-
|
|
227
|
-
`;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (typeof outputContent === "string" || !config.scheduleUseImage) {
|
|
231
|
-
const textMessage = generateTextTable(results, serversToQuery, queryTime, "服务器状态更新");
|
|
232
|
-
outputContent = `🕒 ${timeStr}
|
|
233
|
-
|
|
234
|
-
${textMessage}`;
|
|
235
|
-
}
|
|
236
|
-
for (const groupId of config.scheduleGroups) {
|
|
237
|
-
try {
|
|
238
|
-
const formattedGroupId = utils.formatGroupId(groupId, config.qqAdapterName, config.useFullChannelId);
|
|
239
|
-
await ctx.broadcast([formattedGroupId], outputContent);
|
|
240
|
-
console.log(`定时任务消息已发送到群组: ${formattedGroupId}`);
|
|
241
|
-
} catch (error) {
|
|
242
|
-
console.error(`定时任务发送消息到群组 ${groupId} 失败:`, error);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error("定时任务执行失败:", error);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
__name(executeScheduleTask, "executeScheduleTask");
|
|
250
|
-
function startScheduleTask() {
|
|
251
|
-
if (scheduleTimer) {
|
|
252
|
-
clearInterval(scheduleTimer);
|
|
253
|
-
}
|
|
254
|
-
if (config.scheduleEnabled && config.scheduleInterval > 0) {
|
|
255
|
-
const intervalMs = config.scheduleInterval * 60 * 1e3;
|
|
256
|
-
executeScheduleTask();
|
|
257
|
-
scheduleTimer = setInterval(executeScheduleTask, intervalMs);
|
|
258
|
-
console.log(`定时任务已启动,间隔: ${config.scheduleInterval}分钟,时间范围: ${config.scheduleStartTime}-${config.scheduleEndTime}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
__name(startScheduleTask, "startScheduleTask");
|
|
262
|
-
function stopScheduleTask() {
|
|
263
|
-
if (scheduleTimer) {
|
|
264
|
-
clearInterval(scheduleTimer);
|
|
265
|
-
scheduleTimer = null;
|
|
266
|
-
console.log("定时任务已停止");
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
__name(stopScheduleTask, "stopScheduleTask");
|
|
270
|
-
ctx.on("config", () => {
|
|
271
|
-
if (config.scheduleEnabled) {
|
|
272
|
-
startScheduleTask();
|
|
273
|
-
} else {
|
|
274
|
-
stopScheduleTask();
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
179
|
function parseAddress(input) {
|
|
278
180
|
let address = input.replace(/^(http|https|udp|tcp):\/\//, "");
|
|
279
181
|
if (address.includes("[")) {
|
|
@@ -293,7 +195,7 @@ ${textMessage}`;
|
|
|
293
195
|
return { host: parts[0], port: 27015 };
|
|
294
196
|
}
|
|
295
197
|
throw new Error(`无效的地址格式: ${input}
|
|
296
|
-
正确格式:
|
|
198
|
+
正确格式: [地址]:[端口] 或 [地址]`);
|
|
297
199
|
}
|
|
298
200
|
__name(parseAddress, "parseAddress");
|
|
299
201
|
async function queryServer(host, port) {
|
|
@@ -369,418 +271,353 @@ ${textMessage}`;
|
|
|
369
271
|
return message.trim();
|
|
370
272
|
}
|
|
371
273
|
__name(formatPlayers, "formatPlayers");
|
|
372
|
-
|
|
373
|
-
calculateServerNameFontSize(ctx2, name2, maxWidth, baseFontSize) {
|
|
374
|
-
try {
|
|
375
|
-
if (!ctx2 || typeof ctx2.measureText !== "function") {
|
|
376
|
-
console.warn("Canvas context not available, returning default font size");
|
|
377
|
-
return baseFontSize * 1.5;
|
|
378
|
-
}
|
|
379
|
-
let fontSize = baseFontSize * 1.5;
|
|
380
|
-
while (fontSize > baseFontSize * 0.8) {
|
|
381
|
-
ctx2.font = `bold ${fontSize}px ${config.fontFamily}`;
|
|
382
|
-
const measurement = ctx2.measureText(name2);
|
|
383
|
-
if (measurement && measurement.width <= maxWidth) break;
|
|
384
|
-
fontSize -= 1;
|
|
385
|
-
}
|
|
386
|
-
return fontSize;
|
|
387
|
-
} catch (error) {
|
|
388
|
-
console.error("Error in calculateServerNameFontSize:", error);
|
|
389
|
-
return baseFontSize * 1.5;
|
|
390
|
-
}
|
|
391
|
-
},
|
|
392
|
-
calculatePlayerListParams(playerCount) {
|
|
393
|
-
const shouldEnlarge = playerCount > 0 && playerCount < 10;
|
|
394
|
-
return {
|
|
395
|
-
shouldEnlarge,
|
|
396
|
-
fontSizeMultiplier: shouldEnlarge ? 1.2 : 0.9,
|
|
397
|
-
rowHeight: shouldEnlarge ? 40 : 30,
|
|
398
|
-
nameMaxLength: shouldEnlarge ? 40 : 30,
|
|
399
|
-
needTwoColumns: playerCount > 10
|
|
400
|
-
};
|
|
401
|
-
},
|
|
402
|
-
drawBackground(ctx2, width, height, color = COLORS.background) {
|
|
403
|
-
ctx2.fillStyle = color;
|
|
404
|
-
ctx2.fillRect(0, 0, width, height);
|
|
405
|
-
},
|
|
406
|
-
drawTitle(ctx2, text, x, y, fontSize, fontFamily, color = COLORS.textWhite) {
|
|
407
|
-
ctx2.fillStyle = color;
|
|
408
|
-
ctx2.font = `bold ${fontSize}px ${fontFamily}`;
|
|
409
|
-
ctx2.textAlign = "center";
|
|
410
|
-
ctx2.fillText(text, x, y);
|
|
411
|
-
},
|
|
412
|
-
drawDivider(ctx2, x1, y1, x2, y2, color = COLORS.divider, width = 2) {
|
|
413
|
-
ctx2.strokeStyle = color;
|
|
414
|
-
ctx2.lineWidth = width;
|
|
415
|
-
ctx2.beginPath();
|
|
416
|
-
ctx2.moveTo(x1, y1);
|
|
417
|
-
ctx2.lineTo(x2, y2);
|
|
418
|
-
ctx2.stroke();
|
|
419
|
-
},
|
|
420
|
-
drawText(ctx2, text, x, y, options = {}) {
|
|
421
|
-
const {
|
|
422
|
-
color = COLORS.text,
|
|
423
|
-
fontSize = config.fontSize,
|
|
424
|
-
fontFamily = config.fontFamily,
|
|
425
|
-
align = "left",
|
|
426
|
-
bold = false,
|
|
427
|
-
italic = false
|
|
428
|
-
} = options;
|
|
429
|
-
ctx2.fillStyle = color;
|
|
430
|
-
ctx2.textAlign = align;
|
|
431
|
-
const fontStyle = `${bold ? "bold" : ""} ${italic ? "italic" : ""} ${fontSize}px ${fontFamily}`;
|
|
432
|
-
ctx2.font = fontStyle.trim() || `${fontSize}px ${fontFamily}`;
|
|
433
|
-
ctx2.fillText(text, x, y);
|
|
434
|
-
},
|
|
435
|
-
drawPlayerList(ctx2, players, startY, width, maxHeight, params) {
|
|
436
|
-
let y = startY;
|
|
437
|
-
if (players.length === 0) {
|
|
438
|
-
this.drawText(ctx2, "服务器当前无在线玩家", 80, y, { color: COLORS.textLight });
|
|
439
|
-
return { y: y + 35, displayedCount: 0 };
|
|
440
|
-
}
|
|
441
|
-
const sortedPlayers = [...players].sort((a, b) => {
|
|
442
|
-
const nameA = utils.cleanName(a.name).toLowerCase();
|
|
443
|
-
const nameB = utils.cleanName(b.name).toLowerCase();
|
|
444
|
-
return nameA.localeCompare(nameB);
|
|
445
|
-
});
|
|
446
|
-
if (params.needTwoColumns) {
|
|
447
|
-
const leftColumnX = 80;
|
|
448
|
-
const rightColumnX = width / 2 + 80;
|
|
449
|
-
const playersPerColumn = Math.ceil(players.length / 2);
|
|
450
|
-
const displayPerColumn = Math.min(playersPerColumn, Math.ceil(config.maxPlayers / 2));
|
|
451
|
-
const leftPlayers = sortedPlayers.slice(0, displayPerColumn);
|
|
452
|
-
const rightPlayers = sortedPlayers.slice(displayPerColumn, displayPerColumn * 2);
|
|
453
|
-
let currentY = y;
|
|
454
|
-
let displayedCount = 0;
|
|
455
|
-
leftPlayers.forEach((player) => {
|
|
456
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
457
|
-
this.drawText(ctx2, name2, leftColumnX, currentY, {
|
|
458
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
459
|
-
color: COLORS.textLighter
|
|
460
|
-
});
|
|
461
|
-
currentY += params.rowHeight;
|
|
462
|
-
displayedCount++;
|
|
463
|
-
});
|
|
464
|
-
currentY = y;
|
|
465
|
-
rightPlayers.forEach((player) => {
|
|
466
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
467
|
-
this.drawText(ctx2, name2, rightColumnX, currentY, {
|
|
468
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
469
|
-
color: COLORS.textLighter
|
|
470
|
-
});
|
|
471
|
-
currentY += params.rowHeight;
|
|
472
|
-
displayedCount++;
|
|
473
|
-
});
|
|
474
|
-
y = Math.max(currentY + 15, y);
|
|
475
|
-
const totalDisplayed = leftPlayers.length + rightPlayers.length;
|
|
476
|
-
if (players.length > totalDisplayed) {
|
|
477
|
-
this.drawText(ctx2, `... 还有 ${players.length - totalDisplayed} 位玩家未显示`, leftColumnX, y, {
|
|
478
|
-
fontSize: config.fontSize * 0.8,
|
|
479
|
-
color: COLORS.textLight,
|
|
480
|
-
italic: true
|
|
481
|
-
});
|
|
482
|
-
y += 30;
|
|
483
|
-
}
|
|
484
|
-
return { y, displayedCount };
|
|
485
|
-
} else {
|
|
486
|
-
const displayPlayers = sortedPlayers.slice(0, config.maxPlayers);
|
|
487
|
-
displayPlayers.forEach((player) => {
|
|
488
|
-
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
489
|
-
this.drawText(ctx2, name2, 80, y, {
|
|
490
|
-
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
491
|
-
color: COLORS.textLighter
|
|
492
|
-
});
|
|
493
|
-
y += params.rowHeight;
|
|
494
|
-
});
|
|
495
|
-
return { y, displayedCount: displayPlayers.length };
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
// 统一边框绘制函数
|
|
499
|
-
drawBorder(ctx2, width, height) {
|
|
500
|
-
this.drawDivider(ctx2, 1, 1, width - 1, 1, COLORS.border, 2);
|
|
501
|
-
this.drawDivider(ctx2, width - 1, 1, width - 1, height - 1, COLORS.border, 2);
|
|
502
|
-
this.drawDivider(ctx2, width - 1, height - 1, 1, height - 1, COLORS.border, 2);
|
|
503
|
-
this.drawDivider(ctx2, 1, height - 1, 1, 1, COLORS.border, 2);
|
|
504
|
-
this.drawDivider(ctx2, 5, 0.5 * height - 0.05 * height, 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
505
|
-
this.drawDivider(ctx2, width - 5, 0.5 * height - 0.05 * height, width - 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
506
|
-
this.drawDivider(ctx2, 2, 2, 0.025 * width, 2, COLORS.accent, 3);
|
|
507
|
-
this.drawDivider(ctx2, 2, 2, 2, 0.025 * width, COLORS.accent, 3);
|
|
508
|
-
this.drawDivider(ctx2, width - 2, 2, width - 2, 0.025 * width, COLORS.accent, 3);
|
|
509
|
-
this.drawDivider(ctx2, width - 2, 2, width - 0.025 * width, 2, COLORS.accent, 3);
|
|
510
|
-
this.drawDivider(ctx2, width - 2, height - 2, width - 2, height - 0.025 * width, COLORS.accent, 3);
|
|
511
|
-
this.drawDivider(ctx2, width - 2, height - 2, width - 0.025 * width, height - 2, COLORS.accent, 3);
|
|
512
|
-
this.drawDivider(ctx2, 2, height - 2, 0.025 * width, height - 2, COLORS.accent, 3);
|
|
513
|
-
this.drawDivider(ctx2, 2, height - 2, 2, height - 0.025 * width, COLORS.accent, 3);
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
function calculateImageHeight(data) {
|
|
274
|
+
function generateServerHTML(data, host, port) {
|
|
517
275
|
const { result } = data;
|
|
518
276
|
const playerCount = result.players?.length || 0;
|
|
519
|
-
const
|
|
520
|
-
|
|
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 = "";
|
|
521
282
|
if (playerCount === 0) {
|
|
522
|
-
|
|
283
|
+
playersHTML = `<div class="player-row" style="color: ${COLORS.textLight};">服务器当前无玩家在线</div>`;
|
|
523
284
|
} else {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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>";
|
|
528
302
|
} else {
|
|
529
|
-
|
|
530
|
-
|
|
303
|
+
playersHTML = displayPlayers.map(
|
|
304
|
+
(p) => `<div class="player-row">${utils.truncateText(utils.cleanName(p.name), 40)}</div>`
|
|
305
|
+
).join("");
|
|
531
306
|
}
|
|
532
307
|
if (playerCount > config.maxPlayers) {
|
|
533
|
-
|
|
308
|
+
playersHTML += `<div class="player-row" style="color: ${COLORS.textLight}; font-style: italic;">... 还有 ${playerCount - config.maxPlayers} 位玩家未显示</div>`;
|
|
534
309
|
}
|
|
535
310
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
|
|
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};
|
|
326
|
+
}
|
|
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;
|
|
335
|
+
}
|
|
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;
|
|
347
|
+
}
|
|
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>`;
|
|
594
417
|
}
|
|
595
|
-
__name(
|
|
596
|
-
|
|
418
|
+
__name(generateServerHTML, "generateServerHTML");
|
|
419
|
+
function generateBatchHTML(results, serversToQuery, queryTime) {
|
|
597
420
|
const successful = results.filter((r) => r.status === "fulfilled" && r.value.success).length;
|
|
598
421
|
const failed = results.length - successful;
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
const width = config.imageWidth;
|
|
602
|
-
const height = baseHeight + results.length * serverHeight;
|
|
603
|
-
const canvas = await ctx.canvas.createCanvas(width, height);
|
|
604
|
-
const ctx2d = canvas.getContext("2d");
|
|
605
|
-
imageUtils.drawBackground(ctx2d, width, height);
|
|
606
|
-
imageUtils.drawTitle(ctx2d, "[服务器状态批量查询]", width / 2, 100, config.fontSize * 1.8, config.fontFamily, COLORS.title);
|
|
607
|
-
const now = /* @__PURE__ */ new Date();
|
|
608
|
-
imageUtils.drawText(ctx2d, `查询时间: ${now.toLocaleString("zh-CN")}`, 80, 150);
|
|
609
|
-
imageUtils.drawText(ctx2d, `耗时: ${utils.formatTime(queryTime)} 成功: ${successful}/${results.length}`, width - 80, 150, { align: "right" });
|
|
610
|
-
imageUtils.drawDivider(ctx2d, 80, 165, width - 80, 165, COLORS.gold, 2);
|
|
611
|
-
let y = 200;
|
|
422
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
|
|
423
|
+
let serversHTML = "";
|
|
612
424
|
results.forEach((result, index) => {
|
|
613
425
|
const server = serversToQuery[index];
|
|
614
|
-
if (result.status === "fulfilled") {
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
color:
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if (serverData.map) {
|
|
638
|
-
imageUtils.drawText(ctx2d, `地图: ${serverData.map}`, 80, y + 60, {
|
|
639
|
-
fontSize: config.fontSize * 0.8,
|
|
640
|
-
color: COLORS.textLight
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
if (serverData.ping) {
|
|
644
|
-
const pingColor = utils.getPingColor(serverData.ping);
|
|
645
|
-
imageUtils.drawText(ctx2d, `延迟: ${serverData.ping}ms`, width - 80, y + 60, {
|
|
646
|
-
align: "right",
|
|
647
|
-
fontSize: config.fontSize * 0.9,
|
|
648
|
-
color: pingColor
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
imageUtils.drawText(ctx2d, `${index + 1}. ${server}`, 80, y, { color: COLORS.textWhite, bold: true });
|
|
653
|
-
imageUtils.drawText(ctx2d, `❌ 查询失败: ${error}`, 200, y + 35, { color: COLORS.error });
|
|
654
|
-
}
|
|
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
|
+
`;
|
|
655
449
|
} else {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
+
`;
|
|
661
461
|
}
|
|
662
|
-
y += 100;
|
|
663
462
|
});
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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; }
|
|
685
491
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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(() => {
|
|
717
594
|
});
|
|
718
|
-
if (session) {
|
|
719
|
-
try {
|
|
720
|
-
await session.send("测试消息: QQ适配器连接正常 ✓");
|
|
721
|
-
message += "\n✅ 当前会话消息发送测试成功";
|
|
722
|
-
} catch (error) {
|
|
723
|
-
message += `
|
|
724
|
-
❌ 当前会话消息发送失败: ${error.message}`;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
return message;
|
|
728
|
-
}
|
|
729
|
-
if (options.addGroup) {
|
|
730
|
-
let groupId = options.addGroup.trim();
|
|
731
|
-
if (config.useFullChannelId && !groupId.includes(":")) {
|
|
732
|
-
groupId = `${config.qqAdapterName}:${groupId}`;
|
|
733
|
-
}
|
|
734
|
-
if (!config.scheduleGroups.includes(groupId)) {
|
|
735
|
-
config.scheduleGroups.push(groupId);
|
|
736
|
-
return `✅ 已添加群组 ${groupId} 到定时任务
|
|
737
|
-
当前列表: ${config.scheduleGroups.length} 个群组`;
|
|
738
|
-
} else {
|
|
739
|
-
return `❌ 群组 ${groupId} 已在列表中`;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
if (options.removeGroup) {
|
|
743
|
-
const index = config.scheduleGroups.indexOf(options.removeGroup);
|
|
744
|
-
if (index !== -1) {
|
|
745
|
-
config.scheduleGroups.splice(index, 1);
|
|
746
|
-
return `✅ 已从定时任务移除群组 ${options.removeGroup}`;
|
|
747
|
-
} else {
|
|
748
|
-
return `❌ 群组 ${options.removeGroup} 不在列表中`;
|
|
749
|
-
}
|
|
750
595
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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(() => {
|
|
759
615
|
});
|
|
760
|
-
return message;
|
|
761
616
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
选项:
|
|
765
|
-
-s, -status 查看定时任务状态
|
|
766
|
-
-S, -start 启动定时任务
|
|
767
|
-
-T, -stop 停止定时任务
|
|
768
|
-
-t, -test 测试定时任务
|
|
769
|
-
-R, -run 立即执行一次定时任务
|
|
770
|
-
-q, -testQQ 测试QQ适配器连接
|
|
771
|
-
-a, -addGroup 添加群组到定时任务
|
|
772
|
-
-r, -removeGroup 从定时任务移除群组
|
|
773
|
-
-l, -listGroups 列出定时任务群组
|
|
774
|
-
|
|
775
|
-
示例:
|
|
776
|
-
cs.schedule -s # 查看状态
|
|
777
|
-
cs.schedule -S # 启动定时任务
|
|
778
|
-
cs.schedule -a 123456 # 添加群组123456
|
|
779
|
-
cs.schedule -t # 测试执行
|
|
780
|
-
cs.schedule -q # 测试QQ适配器连接`;
|
|
781
|
-
});
|
|
617
|
+
}
|
|
618
|
+
__name(generateBatchImage, "generateBatchImage");
|
|
782
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) => {
|
|
783
|
-
if (!address) return "使用格式: cs [地址:端口]\n示例: cs 127.0.0.1:27015
|
|
620
|
+
if (!address) return "使用格式: cs [地址:端口]\n示例: cs 127.0.0.1:27015 / cs edgebug.cn";
|
|
784
621
|
if (options.clear) {
|
|
785
622
|
const count = cache.size;
|
|
786
623
|
cache.clear();
|
|
@@ -796,7 +633,11 @@ cs.schedule -q # 测试QQ适配器连接`;
|
|
|
796
633
|
return import_koishi.h.image(imageBuffer, "image/png");
|
|
797
634
|
} catch (imageError) {
|
|
798
635
|
console.error("生成图片失败:", imageError);
|
|
799
|
-
return `生成图片失败: ${imageError.message}
|
|
636
|
+
return `生成图片失败: ${imageError.message},已转为文本输出。
|
|
637
|
+
|
|
638
|
+
${formatServerInfo(data)}
|
|
639
|
+
|
|
640
|
+
${formatPlayers(data.result.players || [])}`;
|
|
800
641
|
}
|
|
801
642
|
}
|
|
802
643
|
let message = formatServerInfo(data);
|
|
@@ -827,27 +668,20 @@ cs.schedule -q # 测试QQ适配器连接`;
|
|
|
827
668
|
ctx.command("cs.status", "检查插件状态和配置").action(async () => {
|
|
828
669
|
try {
|
|
829
670
|
const gamedigStatus = ctx.gamedig ? "✅ 可用" : "❌ 不可用";
|
|
830
|
-
let
|
|
831
|
-
if (ctx.
|
|
671
|
+
let puppeteerStatus = "❌ 不可用";
|
|
672
|
+
if (ctx.puppeteer) {
|
|
832
673
|
try {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
canvasStatus = `❌ 不可用: ${error.message}`;
|
|
674
|
+
await ctx.puppeteer.render("<div>test</div>");
|
|
675
|
+
puppeteerStatus = "✅ 可用";
|
|
676
|
+
} catch (e) {
|
|
677
|
+
puppeteerStatus = `❌ 不可用: ${e.message}`;
|
|
838
678
|
}
|
|
839
679
|
}
|
|
840
680
|
const cacheSize = cache.size;
|
|
841
|
-
const scheduleStatus = config.scheduleEnabled ? "✅ 已启用" : "❌ 已禁用";
|
|
842
|
-
const scheduleTimerStatus = scheduleTimer ? "运行中" : "未运行";
|
|
843
|
-
const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
|
|
844
|
-
const qqStatus = qqBots.length > 0 ? `✅ 可用 (${qqBots.length}个)` : "❌ 不可用";
|
|
845
681
|
return `✅ CS服务器查询插件状态
|
|
846
682
|
💾 缓存数量: ${cacheSize} 条
|
|
847
683
|
🕹️ Gamedig插件: ${gamedigStatus}
|
|
848
|
-
🖼️
|
|
849
|
-
📅 定时任务: ${scheduleStatus} (${scheduleTimerStatus})
|
|
850
|
-
🤖 QQ适配器: ${qqStatus} (名称: ${config.qqAdapterName})
|
|
684
|
+
🖼️ Puppeteer插件: ${puppeteerStatus}
|
|
851
685
|
⚙️ 配置参数:
|
|
852
686
|
超时时间: ${config.timeout}ms
|
|
853
687
|
缓存时间: ${config.cacheTime}ms
|
|
@@ -856,16 +690,16 @@ cs.schedule -q # 测试QQ适配器连接`;
|
|
|
856
690
|
显示VAC状态: ${config.showVAC ? "是" : "否"}
|
|
857
691
|
显示密码保护: ${config.showPassword ? "是" : "否"}
|
|
858
692
|
生成图片横幅: ${config.generateImage ? "是" : "否"}
|
|
693
|
+
图片宽度: ${config.imageWidth}px
|
|
859
694
|
图片最小高度: ${config.imageHeight}px
|
|
860
695
|
字体大小: ${config.fontSize}px
|
|
861
|
-
|
|
696
|
+
字体: ${config.fontFamily}
|
|
862
697
|
|
|
863
698
|
📝 使用: cs [地址:端口]
|
|
864
|
-
📝 选项: -i 生成图片, -t 输出文本, -c
|
|
865
|
-
📅 定时任务: cs.schedule 查看定时任务管理`;
|
|
699
|
+
📝 选项: -i 生成图片, -t 输出文本, -c 清除缓存`;
|
|
866
700
|
} catch (error) {
|
|
867
701
|
return `❌ 插件状态异常: ${error.message}
|
|
868
|
-
请确保已安装并启用 koishi-plugin-gamedig 和 koishi-plugin-
|
|
702
|
+
请确保已安装并启用 koishi-plugin-gamedig 和 koishi-plugin-puppeteer`;
|
|
869
703
|
}
|
|
870
704
|
});
|
|
871
705
|
ctx.command("cs.help", "查看帮助").action(() => {
|
|
@@ -873,9 +707,7 @@ cs.schedule -q # 测试QQ适配器连接`;
|
|
|
873
707
|
|
|
874
708
|
📝 基本用法:
|
|
875
709
|
cs [地址:端口]
|
|
876
|
-
示例: cs 127.0.0.1:27015
|
|
877
|
-
示例: cs edgebug.cn
|
|
878
|
-
|
|
710
|
+
示例: cs 127.0.0.1:27015 / cs edgebug.cn
|
|
879
711
|
🔧 选项:
|
|
880
712
|
-i 生成图片横幅
|
|
881
713
|
-t 输出文本信息
|
|
@@ -883,23 +715,16 @@ cs [地址:端口]
|
|
|
883
715
|
|
|
884
716
|
🎯 快捷命令:
|
|
885
717
|
csss - 批量查询服务器状态
|
|
886
|
-
cs.schedule - 定时任务管理
|
|
887
718
|
|
|
888
719
|
📋 其他命令:
|
|
889
720
|
cs.status - 检查插件状态和配置
|
|
890
721
|
cs.help - 显示此帮助
|
|
891
722
|
|
|
892
|
-
📅 定时任务:
|
|
893
|
-
定时自动向指定QQ群组发送服务器状态
|
|
894
|
-
配置: 插件配置面板中设置
|
|
895
|
-
管理: cs.schedule 命令
|
|
896
|
-
群组ID格式: ${config.useFullChannelId ? "适配器:群号 (如: qq:123456)" : "群号 (如: 123456)"}
|
|
897
|
-
|
|
898
723
|
💡 提示:
|
|
899
724
|
1. 如果不指定端口,默认使用27015
|
|
900
725
|
2. 只支持CS服务器查询
|
|
901
726
|
3. 查询结果缓存${config.cacheTime}ms,使用 -c 清除缓存
|
|
902
|
-
4. 需要安装 koishi-plugin-gamedig 和 koishi-plugin-
|
|
727
|
+
4. 需要安装 koishi-plugin-gamedig 和 koishi-plugin-puppeteer 插件`;
|
|
903
728
|
});
|
|
904
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) => {
|
|
905
730
|
if (options.list) {
|
|
@@ -961,23 +786,17 @@ cs.help - 显示此帮助
|
|
|
961
786
|
}
|
|
962
787
|
}
|
|
963
788
|
let message = generateTextTable(results, serversToQuery, queryTime, "批量查询结果");
|
|
964
|
-
message += "\n📋 输入 `cs <序号>` 查看服务器详情";
|
|
965
789
|
message += "\n📋 输入 `cs <服务器地址>` 查询单个服务器";
|
|
966
790
|
return message;
|
|
967
791
|
} catch (error) {
|
|
968
792
|
return `❌ 批量查询失败: ${error.message}`;
|
|
969
793
|
}
|
|
970
794
|
});
|
|
971
|
-
if (config.scheduleEnabled) {
|
|
972
|
-
startScheduleTask();
|
|
973
|
-
}
|
|
974
795
|
ctx.on("dispose", () => {
|
|
975
796
|
cache.clear();
|
|
976
|
-
stopScheduleTask();
|
|
977
797
|
});
|
|
978
798
|
}
|
|
979
799
|
__name(apply, "apply");
|
|
980
|
-
var inject = ["canvas", "gamedig", "database"];
|
|
981
800
|
// Annotate the CommonJS export names for ESM import in node:
|
|
982
801
|
0 && (module.exports = {
|
|
983
802
|
Config,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-csss",
|
|
3
3
|
"description": "Check the status of the CS server",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
+
"homepage": "https://github.com/sanksu/koishi-plugin-csss",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/sanksu/koishi-plugin-csss.git"
|
|
16
|
+
},
|
|
12
17
|
"keywords": [
|
|
13
18
|
"chatbot",
|
|
14
19
|
"koishi",
|
|
@@ -16,12 +21,12 @@
|
|
|
16
21
|
],
|
|
17
22
|
"devDependencies": {
|
|
18
23
|
"koishi": "^4.18.7",
|
|
19
|
-
"koishi-plugin-
|
|
24
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
20
25
|
"koishi-plugin-gamedig": "^1.2.2"
|
|
21
26
|
},
|
|
22
27
|
"peerDependencies": {
|
|
23
28
|
"koishi": "^4.18.7",
|
|
24
|
-
"koishi-plugin-
|
|
29
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
25
30
|
"koishi-plugin-gamedig": "^1.2.2"
|
|
26
31
|
},
|
|
27
32
|
"koishi": {
|
|
@@ -30,7 +35,7 @@
|
|
|
30
35
|
},
|
|
31
36
|
"service": {
|
|
32
37
|
"required": [
|
|
33
|
-
"
|
|
38
|
+
"puppeteer",
|
|
34
39
|
"gamedig"
|
|
35
40
|
]
|
|
36
41
|
}
|
package/readme.md
CHANGED
|
@@ -3,3 +3,18 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/koishi-plugin-csss)
|
|
4
4
|
|
|
5
5
|
cs server status
|
|
6
|
+
|
|
7
|
+
## 提供指令
|
|
8
|
+
- ```cs [地址:端口] <-i | -t | -c>```
|
|
9
|
+
|
|
10
|
+
> [ ]: 必填 < >: 选填
|
|
11
|
+
> -i 生成图片横幅
|
|
12
|
+
> -t 输出文本信息
|
|
13
|
+
> -c 清除缓存
|
|
14
|
+
|
|
15
|
+
- `csss`
|
|
16
|
+
|
|
17
|
+
> 选项:
|
|
18
|
+
> -i 生成图片横幅
|
|
19
|
+
> -t 输出文本信息
|
|
20
|
+
> -c 清除缓存
|
package/lib/index.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Context, Schema } from 'koishi';
|
|
2
|
-
export declare const name = "cs-server-status";
|
|
3
|
-
export interface Config {
|
|
4
|
-
timeout: number;
|
|
5
|
-
cacheTime: number;
|
|
6
|
-
maxPlayers: number;
|
|
7
|
-
retryCount: number;
|
|
8
|
-
showVAC: boolean;
|
|
9
|
-
showPassword: boolean;
|
|
10
|
-
generateImage: boolean;
|
|
11
|
-
imageWidth: number;
|
|
12
|
-
imageHeight: number;
|
|
13
|
-
fontSize: number;
|
|
14
|
-
fontFamily: string;
|
|
15
|
-
serverList: string[];
|
|
16
|
-
batchTimeout: number;
|
|
17
|
-
scheduleEnabled: boolean;
|
|
18
|
-
scheduleInterval: number;
|
|
19
|
-
scheduleStartTime: string;
|
|
20
|
-
scheduleEndTime: string;
|
|
21
|
-
scheduleGroups: string[];
|
|
22
|
-
scheduleUseImage: boolean;
|
|
23
|
-
qqAdapterName: string;
|
|
24
|
-
useFullChannelId: boolean;
|
|
25
|
-
}
|
|
26
|
-
export declare const Config: Schema<Config>;
|
|
27
|
-
export declare function apply(ctx: Context, config: Config): void;
|
|
28
|
-
export declare const inject: string[];
|