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 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 = "cs-server-status";
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("批量查询服务器列表(格式: 地址:端口,每行一个)").default([
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: "rgba(28,28,31,0.80)",
64
- text: "rgb(113, 113, 122)",
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: "rgb(252, 248, 222)"
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.canvas) {
140
- console.error("koishi-plugin-canvas 未安装或未启用");
141
- return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-canvas 插件");
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 += "序号 服务器名称 在线人数\n";
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
- const imageUtils = {
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 playerParams = imageUtils.calculatePlayerListParams(playerCount);
520
- let baseHeight = 280;
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
- baseHeight += 60;
283
+ playersHTML = `<div class="player-row" style="color: ${COLORS.textLight};">服务器当前无玩家在线</div>`;
523
284
  } else {
524
- baseHeight += 90;
525
- if (playerParams.needTwoColumns) {
526
- const rows = Math.ceil(Math.min(playerCount, config.maxPlayers) / 2);
527
- baseHeight += rows * playerParams.rowHeight;
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
- const rows = Math.min(playerCount, config.maxPlayers);
530
- baseHeight += rows * playerParams.rowHeight;
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
- baseHeight += 40;
308
+ playersHTML += `<div class="player-row" style="color: ${COLORS.textLight}; font-style: italic;">... 还有 ${playerCount - config.maxPlayers} 位玩家未显示</div>`;
534
309
  }
535
310
  }
536
- if (config.showPassword && result.password !== void 0) {
537
- baseHeight += 35;
538
- }
539
- if (config.showVAC && result.raw?.secure !== void 0) {
540
- baseHeight += 35;
541
- }
542
- const height = Math.max(baseHeight, config.imageHeight);
543
- return Math.min(height, 2500);
544
- }
545
- __name(calculateImageHeight, "calculateImageHeight");
546
- async function generateServerImage(data, host, port) {
547
- const { result } = data;
548
- const width = config.imageWidth;
549
- const height = calculateImageHeight(data);
550
- const canvas = await ctx.canvas.createCanvas(width, height);
551
- const ctx2d = canvas.getContext("2d");
552
- imageUtils.drawBackground(ctx2d, width, height);
553
- const titleY = 80;
554
- imageUtils.drawTitle(ctx2d, "[服务器状态查询]", width / 2, titleY, config.fontSize * 1.5, config.fontFamily, COLORS.title);
555
- if (result.name) {
556
- const cleanName = utils.cleanName(result.name);
557
- const fontSize = imageUtils.calculateServerNameFontSize(ctx2d, cleanName, width - 160, config.fontSize);
558
- imageUtils.drawTitle(ctx2d, cleanName, width / 2, titleY + 50, fontSize * 1.8, config.fontFamily, COLORS.highlight);
559
- }
560
- imageUtils.drawDivider(ctx2d, 80, titleY + 80, width - 80, titleY + 80, COLORS.border, 2);
561
- let y = titleY + 120;
562
- if (result.map) {
563
- imageUtils.drawText(ctx2d, `地图: ${result.map}`, 80, y);
564
- }
565
- imageUtils.drawText(ctx2d, `IP: ${host}:${port}`, width - 80, y, { align: "right" });
566
- y += 40;
567
- const playerCount = result.players?.length || 0;
568
- const botCount = result.bots?.length || 0;
569
- const maxPlayers = result.maxplayers || 0;
570
- const playerText = `人数: ${playerCount}/${maxPlayers}${botCount > 0 ? ` (${botCount} Bot)` : ""}`;
571
- imageUtils.drawText(ctx2d, playerText, 80, y, { color: utils.getPlayerColor(playerCount) });
572
- if (result.ping) {
573
- imageUtils.drawText(ctx2d, `Ping: ${result.ping}ms`, width - 80, y, {
574
- align: "right",
575
- color: utils.getPingColor(result.ping)
576
- });
577
- }
578
- y += 50;
579
- const playerParams = imageUtils.calculatePlayerListParams(playerCount);
580
- imageUtils.drawText(ctx2d, "在线玩家", 80, y, { color: COLORS.playerName, bold: true, fontSize: config.fontSize });
581
- y += 40;
582
- imageUtils.drawDivider(ctx2d, 80, y - 15, width - 80, y - 15, COLORS.divider, 1.5);
583
- y += 25;
584
- const playerListResult = imageUtils.drawPlayerList(ctx2d, result.players || [], y, width, height, playerParams);
585
- y = playerListResult.y;
586
- y += 30;
587
- const now = /* @__PURE__ */ new Date();
588
- imageUtils.drawText(ctx2d, `查询时间: ${now.toLocaleString("zh-CN")}`, 80, height - 20, {
589
- fontSize: config.fontSize * 0.8,
590
- color: COLORS.timestamp
591
- });
592
- imageUtils.drawBorder(ctx2d, width, height);
593
- return canvas.toBuffer("image/png");
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(generateServerImage, "generateServerImage");
596
- async function generateBatchImage(results, serversToQuery, queryTime) {
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 baseHeight = 200;
600
- const serverHeight = 100;
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 { success, data, error } = result.value;
616
- if (success && data) {
617
- const serverData = data.result;
618
- const serverName = serverData.name ? utils.cleanName(serverData.name) : "未知";
619
- const playerCount = serverData.players?.length || 0;
620
- const maxPlayers = serverData.maxplayers || 0;
621
- imageUtils.drawText(ctx2d, `${index + 1}. ${serverName}`, 80, y, {
622
- color: COLORS.textWhite,
623
- bold: true,
624
- fontSize: config.fontSize * 1.1
625
- });
626
- imageUtils.drawText(ctx2d, server, 80, y + 30, {
627
- fontSize: config.fontSize * 0.8,
628
- color: COLORS.textLight
629
- });
630
- const playerText = `${playerCount}/${maxPlayers}`;
631
- const playerColor = playerCount > 0 ? COLORS.success : COLORS.error;
632
- imageUtils.drawText(ctx2d, playerText, width - 80, y, {
633
- align: "right",
634
- color: playerColor,
635
- bold: true
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
- imageUtils.drawText(ctx2d, `${index + 1}. ${server}`, 80, y, { color: COLORS.textWhite, bold: true });
657
- imageUtils.drawText(ctx2d, "❌ 查询失败", 200, y + 35, { color: COLORS.error });
658
- }
659
- if (index < results.length - 1) {
660
- imageUtils.drawDivider(ctx2d, 80, y + 70, width - 80, y + 70, COLORS.divider, 1);
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
- imageUtils.drawBorder(ctx2d, width, height);
665
- return canvas.toBuffer("image/png");
666
- }
667
- __name(generateBatchImage, "generateBatchImage");
668
- ctx.command("cs.schedule", "定时任务管理").alias("定时任务").option("status", "-s 查看定时任务状态", { type: Boolean, fallback: false }).option("start", "-S 启动定时任务", { type: Boolean, fallback: false }).option("stop", "-T 停止定时任务", { type: Boolean, fallback: false }).option("test", "-t 测试定时任务", { type: Boolean, fallback: false }).option("addGroup", "-a <groupId> 添加群组到定时任务", { type: String }).option("removeGroup", "-r <groupId> 从定时任务移除群组", { type: String }).option("listGroups", "-l 列出定时任务群组", { type: Boolean, fallback: false }).option("run", "-R 立即执行一次定时任务", { type: Boolean, fallback: false }).option("testQQ", "-q 测试QQ适配器连接", { type: Boolean, fallback: false }).action(async ({ session, options }) => {
669
- if (options.status) {
670
- const status = config.scheduleEnabled ? "✅ 已启用" : "❌ 已禁用";
671
- const nextRun = scheduleTimer ? "运行中" : "未运行";
672
- const groups = config.scheduleGroups.length;
673
- const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
674
- const qqStatus = qqBots.length > 0 ? `✅ 可用 (${qqBots.length}个)` : "❌ 不可用";
675
- return `📅 定时任务状态
676
- 状态: ${status}
677
- 定时器: ${nextRun}
678
- 间隔: ${config.scheduleInterval}分钟
679
- 时间范围: ${config.scheduleStartTime} - ${config.scheduleEndTime}
680
- 输出格式: ${config.scheduleUseImage ? "图片" : "文本"}
681
- 监控服务器: ${config.serverList.length}个
682
- 目标群组: ${groups}个
683
- QQ适配器: ${qqStatus} (名称: ${config.qqAdapterName})
684
- 群组ID格式: ${config.useFullChannelId ? "适配器:群号" : "群号"}
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
- 使用 cs.schedule -h 查看所有命令选项`;
687
- }
688
- if (options.start) {
689
- config.scheduleEnabled = true;
690
- startScheduleTask();
691
- return "✅ 定时任务已启动";
692
- }
693
- if (options.stop) {
694
- config.scheduleEnabled = false;
695
- stopScheduleTask();
696
- return "✅ 定时任务已停止";
697
- }
698
- if (options.test) {
699
- await executeScheduleTask();
700
- return "✅ 定时任务测试执行完成";
701
- }
702
- if (options.run) {
703
- await executeScheduleTask();
704
- return "✅ 已立即执行一次定时任务";
705
- }
706
- if (options.testQQ) {
707
- const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
708
- if (qqBots.length === 0) {
709
- return `❌ 找不到 ${config.qqAdapterName} 适配器的机器人
710
- 请确保已正确配置QQ适配器`;
711
- }
712
- let message = `✅ 找到 ${qqBots.length} 个 ${config.qqAdapterName} 适配器机器人:
713
- `;
714
- qqBots.forEach((bot, index) => {
715
- message += `${index + 1}. ${bot.selfId} (在线: ${bot.status})
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
- if (options.listGroups) {
752
- if (config.scheduleGroups.length === 0) {
753
- return "📋 定时任务群组列表为空\n使用 cs.schedule -a <群组ID> 添加群组";
754
- }
755
- let message = "📋 定时任务群组列表:\n";
756
- config.scheduleGroups.forEach((groupId, index) => {
757
- message += `${index + 1}. ${groupId}
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
- return `📅 定时任务管理命令
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\n示例: cs edgebug.cn";
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 canvasStatus = "❌ 不可用";
831
- if (ctx.canvas) {
671
+ let puppeteerStatus = "❌ 不可用";
672
+ if (ctx.puppeteer) {
832
673
  try {
833
- const canvas = await ctx.canvas.createCanvas(1, 1);
834
- const ctx2d = canvas.getContext("2d");
835
- canvasStatus = "✅ 可用";
836
- } catch (error) {
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
- 🖼️ Canvas插件: ${canvasStatus}
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
- 群组ID格式: ${config.useFullChannelId ? "适配器:群号" : "群号"}
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-canvas 插件`;
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-canvas 插件`;
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": "1.2.1",
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-canvas": "^0.2.2",
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-canvas": "^0.2.2",
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
- "canvas",
38
+ "puppeteer",
34
39
  "gamedig"
35
40
  ]
36
41
  }
package/readme.md CHANGED
@@ -3,3 +3,18 @@
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-csss?style=flat-square)](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[];