koishi-plugin-csss 1.1.0 → 1.1.1
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/LICENSE +21 -21
- package/lib/index.d.ts +3 -0
- package/lib/index.js +254 -224
- package/package.json +7 -6
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Tang
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/lib/index.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export interface Config {
|
|
|
20
20
|
scheduleEndTime: string;
|
|
21
21
|
scheduleGroups: string[];
|
|
22
22
|
scheduleUseImage: boolean;
|
|
23
|
+
qqAdapterName: string;
|
|
24
|
+
useFullChannelId: boolean;
|
|
23
25
|
}
|
|
24
26
|
export declare const Config: Schema<Config>;
|
|
25
27
|
export declare function apply(ctx: Context, config: Config): void;
|
|
28
|
+
export declare const inject: string[];
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
5
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
6
|
var __export = (target, all) => {
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/index.ts
|
|
@@ -32,11 +22,11 @@ var src_exports = {};
|
|
|
32
22
|
__export(src_exports, {
|
|
33
23
|
Config: () => Config,
|
|
34
24
|
apply: () => apply,
|
|
25
|
+
inject: () => inject,
|
|
35
26
|
name: () => name
|
|
36
27
|
});
|
|
37
28
|
module.exports = __toCommonJS(src_exports);
|
|
38
29
|
var import_koishi = require("koishi");
|
|
39
|
-
var import_canvas = require("canvas");
|
|
40
30
|
var name = "cs-server-status";
|
|
41
31
|
var Config = import_koishi.Schema.object({
|
|
42
32
|
timeout: import_koishi.Schema.number().min(1e3).max(3e4).default(5e3).description("查询超时时间(毫秒)"),
|
|
@@ -57,14 +47,41 @@ var Config = import_koishi.Schema.object({
|
|
|
57
47
|
"edgebug.cn:27018"
|
|
58
48
|
]),
|
|
59
49
|
batchTimeout: import_koishi.Schema.number().min(1e3).max(6e4).default(15e3).description("批量查询总超时时间(毫秒)"),
|
|
60
|
-
//
|
|
50
|
+
// 定时任务配置
|
|
61
51
|
scheduleEnabled: import_koishi.Schema.boolean().default(false).description("是否启用定时自动查询功能"),
|
|
62
52
|
scheduleInterval: import_koishi.Schema.number().min(1).max(1440).default(5).description("定时查询间隔时间(分钟)"),
|
|
63
53
|
scheduleStartTime: import_koishi.Schema.string().pattern(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/).default("08:00").description("定时任务开始时间(24小时制,格式: HH:MM)"),
|
|
64
54
|
scheduleEndTime: import_koishi.Schema.string().pattern(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/).default("23:00").description("定时任务结束时间(24小时制,格式: HH:MM)"),
|
|
65
55
|
scheduleGroups: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").description("定时发送的群组ID列表(每行一个群组ID)").default([]),
|
|
66
|
-
scheduleUseImage: import_koishi.Schema.boolean().default(true).description("定时任务是否使用图片格式输出")
|
|
56
|
+
scheduleUseImage: import_koishi.Schema.boolean().default(true).description("定时任务是否使用图片格式输出"),
|
|
57
|
+
// QQ适配器配置
|
|
58
|
+
qqAdapterName: import_koishi.Schema.string().default("qq").description('QQ适配器名称(默认为"qq",如果在QQ配置中指定了其他名称请修改)'),
|
|
59
|
+
useFullChannelId: import_koishi.Schema.boolean().default(true).description("是否使用完整的频道ID格式(推荐开启)")
|
|
67
60
|
});
|
|
61
|
+
var COLORS = {
|
|
62
|
+
background: "rgba(28,28,31,0.80)",
|
|
63
|
+
text: "rgb(113, 113, 122)",
|
|
64
|
+
textLight: "#aaaaaa",
|
|
65
|
+
textLighter: "#dddddd",
|
|
66
|
+
textWhite: "#ffffff",
|
|
67
|
+
border: "#2e2e33",
|
|
68
|
+
accent: "#fbbf24",
|
|
69
|
+
success: "#4CAF50",
|
|
70
|
+
warning: "#FFC107",
|
|
71
|
+
error: "#c03f36",
|
|
72
|
+
pingGreen: "#4CAF50",
|
|
73
|
+
pingYellow: "#FFC107",
|
|
74
|
+
pingOrange: "#FF9800",
|
|
75
|
+
pingRed: "#c03f36",
|
|
76
|
+
playerOnline: "#4CAF50",
|
|
77
|
+
playerOffline: "#c03f36",
|
|
78
|
+
title: "#71717a",
|
|
79
|
+
highlight: "#fbbf24",
|
|
80
|
+
divider: "#555555",
|
|
81
|
+
timestamp: "#666666",
|
|
82
|
+
gold: "#FFD700",
|
|
83
|
+
playerName: "rgb(252, 248, 222)"
|
|
84
|
+
};
|
|
68
85
|
var utils = {
|
|
69
86
|
formatPing(ping) {
|
|
70
87
|
if (!ping || ping < 0) return "未知";
|
|
@@ -80,37 +97,111 @@ var utils = {
|
|
|
80
97
|
return text.length > maxLength ? text.substring(0, maxLength) + "..." : text;
|
|
81
98
|
},
|
|
82
99
|
getPingColor(ping) {
|
|
83
|
-
if (ping < 50) return
|
|
84
|
-
if (ping < 100) return
|
|
85
|
-
if (ping < 200) return
|
|
86
|
-
return
|
|
100
|
+
if (ping < 50) return COLORS.pingGreen;
|
|
101
|
+
if (ping < 100) return COLORS.pingYellow;
|
|
102
|
+
if (ping < 200) return COLORS.pingOrange;
|
|
103
|
+
return COLORS.pingRed;
|
|
87
104
|
},
|
|
88
105
|
getPlayerColor(count) {
|
|
89
|
-
return count > 0 ?
|
|
106
|
+
return count > 0 ? COLORS.playerOnline : COLORS.playerOffline;
|
|
90
107
|
},
|
|
91
|
-
// 新增:格式化时间
|
|
92
108
|
formatTime(ms) {
|
|
93
109
|
if (ms < 1e3) return `${ms}ms`;
|
|
94
110
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}秒`;
|
|
95
111
|
return `${(ms / 1e3).toFixed(0)}秒`;
|
|
96
112
|
},
|
|
97
|
-
// 新增:解析时间字符串为分钟数
|
|
98
113
|
parseTimeToMinutes(timeStr) {
|
|
99
114
|
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
100
115
|
return hours * 60 + minutes;
|
|
101
116
|
},
|
|
102
|
-
// 新增:检查当前时间是否在定时任务时间范围内
|
|
103
117
|
isWithinScheduleTime(startTime, endTime) {
|
|
104
118
|
const now = /* @__PURE__ */ new Date();
|
|
105
119
|
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
106
120
|
const startMinutes = this.parseTimeToMinutes(startTime);
|
|
107
121
|
const endMinutes = this.parseTimeToMinutes(endTime);
|
|
108
122
|
return currentMinutes >= startMinutes && currentMinutes <= endMinutes;
|
|
123
|
+
},
|
|
124
|
+
formatGroupId(groupId, adapterName, useFullChannelId) {
|
|
125
|
+
if (useFullChannelId) {
|
|
126
|
+
return `${adapterName}:${groupId}`;
|
|
127
|
+
}
|
|
128
|
+
return groupId;
|
|
109
129
|
}
|
|
110
130
|
};
|
|
111
131
|
function apply(ctx, config) {
|
|
112
132
|
const cache = /* @__PURE__ */ new Map();
|
|
113
133
|
let scheduleTimer = null;
|
|
134
|
+
if (!ctx.gamedig) {
|
|
135
|
+
console.error("koishi-plugin-gamedig 未安装或未启用");
|
|
136
|
+
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-gamedig 插件");
|
|
137
|
+
}
|
|
138
|
+
if (!ctx.canvas) {
|
|
139
|
+
console.error("koishi-plugin-canvas 未安装或未启用");
|
|
140
|
+
return ctx.logger("cs-server-status").error("需要安装并启用 koishi-plugin-canvas 插件");
|
|
141
|
+
}
|
|
142
|
+
async function queryServers(serversToQuery) {
|
|
143
|
+
const startTime = Date.now();
|
|
144
|
+
const results = await Promise.allSettled(
|
|
145
|
+
serversToQuery.map(async (server, index) => {
|
|
146
|
+
try {
|
|
147
|
+
const { host, port } = parseAddress(server);
|
|
148
|
+
const data = await queryServer(host, port);
|
|
149
|
+
return {
|
|
150
|
+
index: index + 1,
|
|
151
|
+
server,
|
|
152
|
+
success: true,
|
|
153
|
+
data
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
index: index + 1,
|
|
158
|
+
server,
|
|
159
|
+
success: false,
|
|
160
|
+
error: error.message
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
const endTime = Date.now();
|
|
166
|
+
const queryTime = endTime - startTime;
|
|
167
|
+
return { results, queryTime, serversToQuery };
|
|
168
|
+
}
|
|
169
|
+
__name(queryServers, "queryServers");
|
|
170
|
+
function generateTextTable(results, serversToQuery, queryTime, title = "批量查询结果") {
|
|
171
|
+
const successful = results.filter((r) => r.status === "fulfilled" && r.value.success).length;
|
|
172
|
+
const failed = results.length - successful;
|
|
173
|
+
let message = `📊 ${title} (${utils.formatTime(queryTime)})
|
|
174
|
+
`;
|
|
175
|
+
message += `✅ 成功: ${successful} 个 | ❌ 失败: ${failed} 个
|
|
176
|
+
|
|
177
|
+
`;
|
|
178
|
+
message += "序号 服务器名称 在线人数\n";
|
|
179
|
+
message += "──────────────────────────────\n";
|
|
180
|
+
results.forEach((result, index) => {
|
|
181
|
+
const serverInfo = serversToQuery[index];
|
|
182
|
+
if (result.status === "fulfilled") {
|
|
183
|
+
const { success, data, error } = result.value;
|
|
184
|
+
if (success && data) {
|
|
185
|
+
const { result: serverData } = data;
|
|
186
|
+
const serverName = serverData.name ? utils.cleanName(serverData.name) : "未知";
|
|
187
|
+
const playerCount = serverData.players?.length || 0;
|
|
188
|
+
const maxPlayers = serverData.maxplayers || 0;
|
|
189
|
+
const truncatedName = utils.truncateText(serverName, 20);
|
|
190
|
+
const paddedName = truncatedName.padEnd(20, " ");
|
|
191
|
+
message += `${(index + 1).toString().padStart(2, " ")} ${paddedName} ${playerCount}/${maxPlayers}
|
|
192
|
+
`;
|
|
193
|
+
} else {
|
|
194
|
+
message += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败: ${error}
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
message += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return message;
|
|
203
|
+
}
|
|
204
|
+
__name(generateTextTable, "generateTextTable");
|
|
114
205
|
async function executeScheduleTask() {
|
|
115
206
|
if (!config.scheduleEnabled || config.scheduleGroups.length === 0 || config.serverList.length === 0) {
|
|
116
207
|
return;
|
|
@@ -119,36 +210,13 @@ function apply(ctx, config) {
|
|
|
119
210
|
return;
|
|
120
211
|
}
|
|
121
212
|
try {
|
|
122
|
-
const
|
|
123
|
-
const results = await Promise.allSettled(
|
|
124
|
-
config.serverList.map(async (server, index) => {
|
|
125
|
-
try {
|
|
126
|
-
const { host, port } = parseAddress(server);
|
|
127
|
-
const data = await queryServer(host, port);
|
|
128
|
-
return {
|
|
129
|
-
index: index + 1,
|
|
130
|
-
server,
|
|
131
|
-
success: true,
|
|
132
|
-
data
|
|
133
|
-
};
|
|
134
|
-
} catch (error) {
|
|
135
|
-
return {
|
|
136
|
-
index: index + 1,
|
|
137
|
-
server,
|
|
138
|
-
success: false,
|
|
139
|
-
error: error.message
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
);
|
|
144
|
-
const endTime = Date.now();
|
|
145
|
-
const queryTime = endTime - startTime;
|
|
213
|
+
const { results, queryTime, serversToQuery } = await queryServers(config.serverList);
|
|
146
214
|
const now = /* @__PURE__ */ new Date();
|
|
147
215
|
const timeStr = now.toLocaleString("zh-CN");
|
|
148
216
|
let outputContent;
|
|
149
217
|
if (config.scheduleUseImage) {
|
|
150
218
|
try {
|
|
151
|
-
const imageBuffer = await generateBatchImage(results,
|
|
219
|
+
const imageBuffer = await generateBatchImage(results, serversToQuery, queryTime);
|
|
152
220
|
outputContent = import_koishi.h.image(imageBuffer, "image/png");
|
|
153
221
|
} catch (imageError) {
|
|
154
222
|
console.error("定时任务生成图片失败:", imageError);
|
|
@@ -159,42 +227,16 @@ function apply(ctx, config) {
|
|
|
159
227
|
}
|
|
160
228
|
}
|
|
161
229
|
if (typeof outputContent === "string" || !config.scheduleUseImage) {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
let textMessage = `🕒 ${timeStr} 服务器状态更新 (耗时: ${utils.formatTime(queryTime)})
|
|
165
|
-
`;
|
|
166
|
-
textMessage += `✅ 成功: ${successful} 个 | ❌ 失败: ${failed} 个
|
|
230
|
+
const textMessage = generateTextTable(results, serversToQuery, queryTime, "服务器状态更新");
|
|
231
|
+
outputContent = `🕒 ${timeStr}
|
|
167
232
|
|
|
168
|
-
`;
|
|
169
|
-
textMessage += "序号 服务器名称 在线人数\n";
|
|
170
|
-
textMessage += "──────────────────────────────\n";
|
|
171
|
-
results.forEach((result, index) => {
|
|
172
|
-
const serverInfo = config.serverList[index];
|
|
173
|
-
if (result.status === "fulfilled") {
|
|
174
|
-
const { success, data, error } = result.value;
|
|
175
|
-
if (success && data) {
|
|
176
|
-
const { result: serverData } = data;
|
|
177
|
-
const serverName = serverData.name ? utils.cleanName(serverData.name) : "未知";
|
|
178
|
-
const playerCount = serverData.players?.length || 0;
|
|
179
|
-
const maxPlayers = serverData.maxplayers || 0;
|
|
180
|
-
const truncatedName = utils.truncateText(serverName, 20);
|
|
181
|
-
const paddedName = truncatedName.padEnd(20, " ");
|
|
182
|
-
textMessage += `${(index + 1).toString().padStart(2, " ")} ${paddedName} ${playerCount}/${maxPlayers}
|
|
183
|
-
`;
|
|
184
|
-
} else {
|
|
185
|
-
textMessage += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败: ${error}
|
|
186
|
-
`;
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
textMessage += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败
|
|
190
|
-
`;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
outputContent = typeof outputContent === "string" ? outputContent + textMessage : textMessage;
|
|
233
|
+
${textMessage}`;
|
|
194
234
|
}
|
|
195
235
|
for (const groupId of config.scheduleGroups) {
|
|
196
236
|
try {
|
|
197
|
-
|
|
237
|
+
const formattedGroupId = utils.formatGroupId(groupId, config.qqAdapterName, config.useFullChannelId);
|
|
238
|
+
await ctx.broadcast([formattedGroupId], outputContent);
|
|
239
|
+
console.log(`定时任务消息已发送到群组: ${formattedGroupId}`);
|
|
198
240
|
} catch (error) {
|
|
199
241
|
console.error(`定时任务发送消息到群组 ${groupId} 失败:`, error);
|
|
200
242
|
}
|
|
@@ -231,16 +273,6 @@ function apply(ctx, config) {
|
|
|
231
273
|
stopScheduleTask();
|
|
232
274
|
}
|
|
233
275
|
});
|
|
234
|
-
async function loadGamedig() {
|
|
235
|
-
try {
|
|
236
|
-
const gamedigModule = await import("gamedig");
|
|
237
|
-
return gamedigModule.default || gamedigModule.GameDig || gamedigModule;
|
|
238
|
-
} catch (error) {
|
|
239
|
-
throw new Error(`无法加载 gamedig 模块:${error.message}
|
|
240
|
-
请确保已安装 gamedig:npm install gamedig`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
__name(loadGamedig, "loadGamedig");
|
|
244
276
|
function parseAddress(input) {
|
|
245
277
|
let address = input.replace(/^(http|https|udp|tcp):\/\//, "");
|
|
246
278
|
if (address.includes("[")) {
|
|
@@ -272,11 +304,10 @@ function apply(ctx, config) {
|
|
|
272
304
|
return cached.data;
|
|
273
305
|
}
|
|
274
306
|
}
|
|
275
|
-
const Gamedig = await loadGamedig();
|
|
276
307
|
let lastError;
|
|
277
308
|
for (let i = 0; i <= config.retryCount; i++) {
|
|
278
309
|
try {
|
|
279
|
-
const result = await
|
|
310
|
+
const result = await ctx.gamedig.query({
|
|
280
311
|
type: "csgo",
|
|
281
312
|
host,
|
|
282
313
|
port,
|
|
@@ -357,20 +388,17 @@ function apply(ctx, config) {
|
|
|
357
388
|
needTwoColumns: playerCount > 10
|
|
358
389
|
};
|
|
359
390
|
},
|
|
360
|
-
drawBackground(ctx2, width, height) {
|
|
361
|
-
|
|
362
|
-
gradient.addColorStop(0, "#1a1a2e");
|
|
363
|
-
gradient.addColorStop(1, "#16213e");
|
|
364
|
-
ctx2.fillStyle = gradient;
|
|
391
|
+
drawBackground(ctx2, width, height, color = COLORS.background) {
|
|
392
|
+
ctx2.fillStyle = color;
|
|
365
393
|
ctx2.fillRect(0, 0, width, height);
|
|
366
394
|
},
|
|
367
|
-
drawTitle(ctx2, text, x, y, fontSize, fontFamily, color =
|
|
395
|
+
drawTitle(ctx2, text, x, y, fontSize, fontFamily, color = COLORS.textWhite) {
|
|
368
396
|
ctx2.fillStyle = color;
|
|
369
397
|
ctx2.font = `bold ${fontSize}px ${fontFamily}`;
|
|
370
398
|
ctx2.textAlign = "center";
|
|
371
399
|
ctx2.fillText(text, x, y);
|
|
372
400
|
},
|
|
373
|
-
drawDivider(ctx2, x1, y1, x2, y2, color, width = 2) {
|
|
401
|
+
drawDivider(ctx2, x1, y1, x2, y2, color = COLORS.divider, width = 2) {
|
|
374
402
|
ctx2.strokeStyle = color;
|
|
375
403
|
ctx2.lineWidth = width;
|
|
376
404
|
ctx2.beginPath();
|
|
@@ -380,7 +408,7 @@ function apply(ctx, config) {
|
|
|
380
408
|
},
|
|
381
409
|
drawText(ctx2, text, x, y, options = {}) {
|
|
382
410
|
const {
|
|
383
|
-
color =
|
|
411
|
+
color = COLORS.text,
|
|
384
412
|
fontSize = config.fontSize,
|
|
385
413
|
fontFamily = config.fontFamily,
|
|
386
414
|
align = "left",
|
|
@@ -396,7 +424,7 @@ function apply(ctx, config) {
|
|
|
396
424
|
drawPlayerList(ctx2, players, startY, width, maxHeight, params) {
|
|
397
425
|
let y = startY;
|
|
398
426
|
if (players.length === 0) {
|
|
399
|
-
this.drawText(ctx2, "服务器当前无在线玩家", 80, y, {
|
|
427
|
+
this.drawText(ctx2, "服务器当前无在线玩家", 80, y, { color: COLORS.textLight });
|
|
400
428
|
return { y: y + 35, displayedCount: 0 };
|
|
401
429
|
}
|
|
402
430
|
const sortedPlayers = [...players].sort((a, b) => {
|
|
@@ -417,7 +445,7 @@ function apply(ctx, config) {
|
|
|
417
445
|
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
418
446
|
this.drawText(ctx2, name2, leftColumnX, currentY, {
|
|
419
447
|
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
420
|
-
color:
|
|
448
|
+
color: COLORS.textLighter
|
|
421
449
|
});
|
|
422
450
|
currentY += params.rowHeight;
|
|
423
451
|
displayedCount++;
|
|
@@ -427,7 +455,7 @@ function apply(ctx, config) {
|
|
|
427
455
|
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
428
456
|
this.drawText(ctx2, name2, rightColumnX, currentY, {
|
|
429
457
|
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
430
|
-
color:
|
|
458
|
+
color: COLORS.textLighter
|
|
431
459
|
});
|
|
432
460
|
currentY += params.rowHeight;
|
|
433
461
|
displayedCount++;
|
|
@@ -437,7 +465,7 @@ function apply(ctx, config) {
|
|
|
437
465
|
if (players.length > totalDisplayed) {
|
|
438
466
|
this.drawText(ctx2, `... 还有 ${players.length - totalDisplayed} 位玩家未显示`, leftColumnX, y, {
|
|
439
467
|
fontSize: config.fontSize * 0.8,
|
|
440
|
-
color:
|
|
468
|
+
color: COLORS.textLight,
|
|
441
469
|
italic: true
|
|
442
470
|
});
|
|
443
471
|
y += 30;
|
|
@@ -449,12 +477,29 @@ function apply(ctx, config) {
|
|
|
449
477
|
const name2 = utils.truncateText(utils.cleanName(player.name), params.nameMaxLength);
|
|
450
478
|
this.drawText(ctx2, name2, 80, y, {
|
|
451
479
|
fontSize: config.fontSize * params.fontSizeMultiplier,
|
|
452
|
-
color:
|
|
480
|
+
color: COLORS.textLighter
|
|
453
481
|
});
|
|
454
482
|
y += params.rowHeight;
|
|
455
483
|
});
|
|
456
484
|
return { y, displayedCount: displayPlayers.length };
|
|
457
485
|
}
|
|
486
|
+
},
|
|
487
|
+
// 统一边框绘制函数
|
|
488
|
+
drawBorder(ctx2, width, height) {
|
|
489
|
+
this.drawDivider(ctx2, 1, 1, width - 1, 1, COLORS.border, 2);
|
|
490
|
+
this.drawDivider(ctx2, width - 1, 1, width - 1, height - 1, COLORS.border, 2);
|
|
491
|
+
this.drawDivider(ctx2, width - 1, height - 1, 1, height - 1, COLORS.border, 2);
|
|
492
|
+
this.drawDivider(ctx2, 1, height - 1, 1, 1, COLORS.border, 2);
|
|
493
|
+
this.drawDivider(ctx2, 5, 0.5 * height - 0.05 * height, 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
494
|
+
this.drawDivider(ctx2, width - 5, 0.5 * height - 0.05 * height, width - 5, height - 0.5 * height + 0.05 * height, COLORS.border, 6);
|
|
495
|
+
this.drawDivider(ctx2, 2, 2, 0.025 * width, 2, COLORS.accent, 3);
|
|
496
|
+
this.drawDivider(ctx2, 2, 2, 2, 0.025 * width, COLORS.accent, 3);
|
|
497
|
+
this.drawDivider(ctx2, width - 2, 2, width - 2, 0.025 * width, COLORS.accent, 3);
|
|
498
|
+
this.drawDivider(ctx2, width - 2, 2, width - 0.025 * width, 2, COLORS.accent, 3);
|
|
499
|
+
this.drawDivider(ctx2, width - 2, height - 2, width - 2, height - 0.025 * width, COLORS.accent, 3);
|
|
500
|
+
this.drawDivider(ctx2, width - 2, height - 2, width - 0.025 * width, height - 2, COLORS.accent, 3);
|
|
501
|
+
this.drawDivider(ctx2, 2, height - 2, 0.025 * width, height - 2, COLORS.accent, 3);
|
|
502
|
+
this.drawDivider(ctx2, 2, height - 2, 2, height - 0.025 * width, COLORS.accent, 3);
|
|
458
503
|
}
|
|
459
504
|
};
|
|
460
505
|
function calculateImageHeight(data) {
|
|
@@ -491,52 +536,49 @@ function apply(ctx, config) {
|
|
|
491
536
|
const { result } = data;
|
|
492
537
|
const width = config.imageWidth;
|
|
493
538
|
const height = calculateImageHeight(data);
|
|
494
|
-
const canvas =
|
|
495
|
-
const
|
|
496
|
-
imageUtils.drawBackground(
|
|
539
|
+
const canvas = await ctx.canvas.createCanvas(width, height);
|
|
540
|
+
const ctx2d = canvas.getContext("2d");
|
|
541
|
+
imageUtils.drawBackground(ctx2d, width, height);
|
|
497
542
|
const titleY = 80;
|
|
498
|
-
imageUtils.drawTitle(
|
|
543
|
+
imageUtils.drawTitle(ctx2d, "[服务器状态查询]", width / 2, titleY, config.fontSize * 1.5, config.fontFamily, COLORS.title);
|
|
499
544
|
if (result.name) {
|
|
500
545
|
const cleanName = utils.cleanName(result.name);
|
|
501
|
-
const fontSize = imageUtils.calculateServerNameFontSize(
|
|
502
|
-
imageUtils.drawTitle(
|
|
546
|
+
const fontSize = imageUtils.calculateServerNameFontSize(ctx2d, cleanName, width - 160, config.fontSize);
|
|
547
|
+
imageUtils.drawTitle(ctx2d, cleanName, width / 2, titleY + 50, fontSize * 0.8, config.fontFamily, COLORS.highlight);
|
|
503
548
|
}
|
|
504
|
-
imageUtils.drawDivider(
|
|
549
|
+
imageUtils.drawDivider(ctx2d, 80, titleY + 80, width - 80, titleY + 80, COLORS.border, 2);
|
|
505
550
|
let y = titleY + 120;
|
|
506
551
|
if (result.map) {
|
|
507
|
-
imageUtils.drawText(
|
|
552
|
+
imageUtils.drawText(ctx2d, `地图: ${result.map}`, 80, y);
|
|
508
553
|
}
|
|
509
|
-
imageUtils.drawText(
|
|
554
|
+
imageUtils.drawText(ctx2d, `IP: ${host}:${port}`, width - 80, y, { align: "right" });
|
|
510
555
|
y += 40;
|
|
511
556
|
const playerCount = result.players?.length || 0;
|
|
512
557
|
const botCount = result.bots?.length || 0;
|
|
513
558
|
const maxPlayers = result.maxplayers || 0;
|
|
514
559
|
const playerText = `人数: ${playerCount}/${maxPlayers}${botCount > 0 ? ` (${botCount} Bot)` : ""}`;
|
|
515
|
-
imageUtils.drawText(
|
|
560
|
+
imageUtils.drawText(ctx2d, playerText, 80, y, { color: utils.getPlayerColor(playerCount) });
|
|
516
561
|
if (result.ping) {
|
|
517
|
-
imageUtils.drawText(
|
|
562
|
+
imageUtils.drawText(ctx2d, `Ping: ${result.ping}ms`, width - 80, y, {
|
|
518
563
|
align: "right",
|
|
519
564
|
color: utils.getPingColor(result.ping)
|
|
520
565
|
});
|
|
521
566
|
}
|
|
522
567
|
y += 50;
|
|
523
568
|
const playerParams = imageUtils.calculatePlayerListParams(playerCount);
|
|
524
|
-
imageUtils.drawText(
|
|
569
|
+
imageUtils.drawText(ctx2d, "在线玩家", 80, y, { color: COLORS.playerName, bold: true, fontSize: config.fontSize });
|
|
525
570
|
y += 40;
|
|
526
|
-
imageUtils.drawDivider(
|
|
571
|
+
imageUtils.drawDivider(ctx2d, 80, y - 15, width - 80, y - 15, COLORS.divider, 1.5);
|
|
527
572
|
y += 25;
|
|
528
|
-
const playerListResult = imageUtils.drawPlayerList(
|
|
573
|
+
const playerListResult = imageUtils.drawPlayerList(ctx2d, result.players || [], y, width, height, playerParams);
|
|
529
574
|
y = playerListResult.y;
|
|
530
575
|
y += 30;
|
|
531
576
|
const now = /* @__PURE__ */ new Date();
|
|
532
|
-
imageUtils.drawText(
|
|
577
|
+
imageUtils.drawText(ctx2d, `查询时间: ${now.toLocaleString("zh-CN")}`, 80, height - 20, {
|
|
533
578
|
fontSize: config.fontSize * 0.8,
|
|
534
|
-
color:
|
|
579
|
+
color: COLORS.timestamp
|
|
535
580
|
});
|
|
536
|
-
imageUtils.
|
|
537
|
-
imageUtils.drawDivider(ctx2, width - 8, 8, width - 8, height - 8, "#7D8B92", 4);
|
|
538
|
-
imageUtils.drawDivider(ctx2, width - 8, height - 8, 8, height - 8, "#7D8B92", 4);
|
|
539
|
-
imageUtils.drawDivider(ctx2, 8, height - 8, 8, 8, "#7D8B92", 4);
|
|
581
|
+
imageUtils.drawBorder(ctx2d, width, height);
|
|
540
582
|
return canvas.toBuffer("image/png");
|
|
541
583
|
}
|
|
542
584
|
__name(generateServerImage, "generateServerImage");
|
|
@@ -547,14 +589,14 @@ function apply(ctx, config) {
|
|
|
547
589
|
const serverHeight = 100;
|
|
548
590
|
const height = baseHeight + results.length * serverHeight;
|
|
549
591
|
const width = 1200;
|
|
550
|
-
const canvas =
|
|
551
|
-
const
|
|
552
|
-
imageUtils.drawBackground(
|
|
553
|
-
imageUtils.drawTitle(
|
|
592
|
+
const canvas = await ctx.canvas.createCanvas(width, height);
|
|
593
|
+
const ctx2d = canvas.getContext("2d");
|
|
594
|
+
imageUtils.drawBackground(ctx2d, width, height);
|
|
595
|
+
imageUtils.drawTitle(ctx2d, "[服务器状态批量查询]", width / 2, 100, config.fontSize * 1.8, config.fontFamily, COLORS.title);
|
|
554
596
|
const now = /* @__PURE__ */ new Date();
|
|
555
|
-
imageUtils.drawText(
|
|
556
|
-
imageUtils.drawText(
|
|
557
|
-
imageUtils.drawDivider(
|
|
597
|
+
imageUtils.drawText(ctx2d, `查询时间: ${now.toLocaleString("zh-CN")}`, 80, 150);
|
|
598
|
+
imageUtils.drawText(ctx2d, `耗时: ${utils.formatTime(queryTime)} 成功: ${successful}/${results.length}`, width - 80, 150, { align: "right" });
|
|
599
|
+
imageUtils.drawDivider(ctx2d, 80, 165, width - 80, 165, COLORS.gold, 2);
|
|
558
600
|
let y = 200;
|
|
559
601
|
results.forEach((result, index) => {
|
|
560
602
|
const server = serversToQuery[index];
|
|
@@ -565,62 +607,60 @@ function apply(ctx, config) {
|
|
|
565
607
|
const serverName = serverData.name ? utils.cleanName(serverData.name) : "未知";
|
|
566
608
|
const playerCount = serverData.players?.length || 0;
|
|
567
609
|
const maxPlayers = serverData.maxplayers || 0;
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
color: "#ffffff",
|
|
610
|
+
imageUtils.drawText(ctx2d, `${index + 1}. ${serverName}`, 80, y, {
|
|
611
|
+
color: COLORS.textWhite,
|
|
571
612
|
bold: true,
|
|
572
613
|
fontSize: config.fontSize * 1.1
|
|
573
614
|
});
|
|
574
|
-
imageUtils.drawText(
|
|
615
|
+
imageUtils.drawText(ctx2d, server, 80, y + 30, {
|
|
575
616
|
fontSize: config.fontSize * 0.8,
|
|
576
|
-
color:
|
|
617
|
+
color: COLORS.textLight
|
|
577
618
|
});
|
|
578
619
|
const playerText = `${playerCount}/${maxPlayers}`;
|
|
579
|
-
const playerColor = playerCount > 0 ?
|
|
580
|
-
imageUtils.drawText(
|
|
620
|
+
const playerColor = playerCount > 0 ? COLORS.success : COLORS.error;
|
|
621
|
+
imageUtils.drawText(ctx2d, playerText, width - 80, y, {
|
|
581
622
|
align: "right",
|
|
582
623
|
color: playerColor,
|
|
583
624
|
bold: true
|
|
584
625
|
});
|
|
585
626
|
if (serverData.map) {
|
|
586
|
-
imageUtils.drawText(
|
|
627
|
+
imageUtils.drawText(ctx2d, `地图: ${serverData.map}`, 80, y + 60, {
|
|
587
628
|
fontSize: config.fontSize * 0.8,
|
|
588
|
-
color:
|
|
629
|
+
color: COLORS.textLight
|
|
589
630
|
});
|
|
590
631
|
}
|
|
591
632
|
if (serverData.ping) {
|
|
592
633
|
const pingColor = utils.getPingColor(serverData.ping);
|
|
593
|
-
imageUtils.drawText(
|
|
634
|
+
imageUtils.drawText(ctx2d, `延迟: ${serverData.ping}ms`, width - 80, y + 60, {
|
|
594
635
|
align: "right",
|
|
595
636
|
fontSize: config.fontSize * 0.9,
|
|
596
637
|
color: pingColor
|
|
597
638
|
});
|
|
598
639
|
}
|
|
599
640
|
} else {
|
|
600
|
-
imageUtils.drawText(
|
|
601
|
-
imageUtils.drawText(
|
|
641
|
+
imageUtils.drawText(ctx2d, `${index + 1}. ${server}`, 80, y, { color: COLORS.textWhite, bold: true });
|
|
642
|
+
imageUtils.drawText(ctx2d, `❌ 查询失败: ${error}`, 200, y + 35, { color: COLORS.error });
|
|
602
643
|
}
|
|
603
644
|
} else {
|
|
604
|
-
imageUtils.drawText(
|
|
605
|
-
imageUtils.drawText(
|
|
645
|
+
imageUtils.drawText(ctx2d, `${index + 1}. ${server}`, 80, y, { color: COLORS.textWhite, bold: true });
|
|
646
|
+
imageUtils.drawText(ctx2d, "❌ 查询失败", 200, y + 35, { color: COLORS.error });
|
|
606
647
|
}
|
|
607
648
|
if (index < results.length - 1) {
|
|
608
|
-
imageUtils.drawDivider(
|
|
649
|
+
imageUtils.drawDivider(ctx2d, 80, y + 70, width - 80, y + 70, COLORS.divider, 1);
|
|
609
650
|
}
|
|
610
651
|
y += 100;
|
|
611
652
|
});
|
|
612
|
-
imageUtils.
|
|
613
|
-
imageUtils.drawDivider(ctx2, width - 8, 8, width - 8, height - 8, "#7D8B92", 4);
|
|
614
|
-
imageUtils.drawDivider(ctx2, width - 8, height - 8, 8, height - 8, "#7D8B92", 4);
|
|
615
|
-
imageUtils.drawDivider(ctx2, 8, height - 8, 8, 8, "#7D8B92", 4);
|
|
653
|
+
imageUtils.drawBorder(ctx2d, width, height);
|
|
616
654
|
return canvas.toBuffer("image/png");
|
|
617
655
|
}
|
|
618
656
|
__name(generateBatchImage, "generateBatchImage");
|
|
619
|
-
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 }).action(async ({ session, options }) => {
|
|
657
|
+
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 }) => {
|
|
620
658
|
if (options.status) {
|
|
621
659
|
const status = config.scheduleEnabled ? "✅ 已启用" : "❌ 已禁用";
|
|
622
660
|
const nextRun = scheduleTimer ? "运行中" : "未运行";
|
|
623
661
|
const groups = config.scheduleGroups.length;
|
|
662
|
+
const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
|
|
663
|
+
const qqStatus = qqBots.length > 0 ? `✅ 可用 (${qqBots.length}个)` : "❌ 不可用";
|
|
624
664
|
return `📅 定时任务状态
|
|
625
665
|
状态: ${status}
|
|
626
666
|
定时器: ${nextRun}
|
|
@@ -629,6 +669,8 @@ function apply(ctx, config) {
|
|
|
629
669
|
输出格式: ${config.scheduleUseImage ? "图片" : "文本"}
|
|
630
670
|
监控服务器: ${config.serverList.length}个
|
|
631
671
|
目标群组: ${groups}个
|
|
672
|
+
QQ适配器: ${qqStatus} (名称: ${config.qqAdapterName})
|
|
673
|
+
群组ID格式: ${config.useFullChannelId ? "适配器:群号" : "群号"}
|
|
632
674
|
|
|
633
675
|
使用 cs.schedule -h 查看所有命令选项`;
|
|
634
676
|
}
|
|
@@ -650,12 +692,40 @@ function apply(ctx, config) {
|
|
|
650
692
|
await executeScheduleTask();
|
|
651
693
|
return "✅ 已立即执行一次定时任务";
|
|
652
694
|
}
|
|
695
|
+
if (options.testQQ) {
|
|
696
|
+
const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
|
|
697
|
+
if (qqBots.length === 0) {
|
|
698
|
+
return `❌ 找不到 ${config.qqAdapterName} 适配器的机器人
|
|
699
|
+
请确保已正确配置QQ适配器`;
|
|
700
|
+
}
|
|
701
|
+
let message = `✅ 找到 ${qqBots.length} 个 ${config.qqAdapterName} 适配器机器人:
|
|
702
|
+
`;
|
|
703
|
+
qqBots.forEach((bot, index) => {
|
|
704
|
+
message += `${index + 1}. ${bot.selfId} (在线: ${bot.status})
|
|
705
|
+
`;
|
|
706
|
+
});
|
|
707
|
+
if (session) {
|
|
708
|
+
try {
|
|
709
|
+
await session.send("测试消息: QQ适配器连接正常 ✓");
|
|
710
|
+
message += "\n✅ 当前会话消息发送测试成功";
|
|
711
|
+
} catch (error) {
|
|
712
|
+
message += `
|
|
713
|
+
❌ 当前会话消息发送失败: ${error.message}`;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return message;
|
|
717
|
+
}
|
|
653
718
|
if (options.addGroup) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
719
|
+
let groupId = options.addGroup.trim();
|
|
720
|
+
if (config.useFullChannelId && !groupId.includes(":")) {
|
|
721
|
+
groupId = `${config.qqAdapterName}:${groupId}`;
|
|
722
|
+
}
|
|
723
|
+
if (!config.scheduleGroups.includes(groupId)) {
|
|
724
|
+
config.scheduleGroups.push(groupId);
|
|
725
|
+
return `✅ 已添加群组 ${groupId} 到定时任务
|
|
726
|
+
当前列表: ${config.scheduleGroups.length} 个群组`;
|
|
657
727
|
} else {
|
|
658
|
-
return `❌ 群组 ${
|
|
728
|
+
return `❌ 群组 ${groupId} 已在列表中`;
|
|
659
729
|
}
|
|
660
730
|
}
|
|
661
731
|
if (options.removeGroup) {
|
|
@@ -686,6 +756,7 @@ function apply(ctx, config) {
|
|
|
686
756
|
-T, -stop 停止定时任务
|
|
687
757
|
-t, -test 测试定时任务
|
|
688
758
|
-R, -run 立即执行一次定时任务
|
|
759
|
+
-q, -testQQ 测试QQ适配器连接
|
|
689
760
|
-a, -addGroup 添加群组到定时任务
|
|
690
761
|
-r, -removeGroup 从定时任务移除群组
|
|
691
762
|
-l, -listGroups 列出定时任务群组
|
|
@@ -694,7 +765,8 @@ function apply(ctx, config) {
|
|
|
694
765
|
cs.schedule -s # 查看状态
|
|
695
766
|
cs.schedule -S # 启动定时任务
|
|
696
767
|
cs.schedule -a 123456 # 添加群组123456
|
|
697
|
-
cs.schedule -t #
|
|
768
|
+
cs.schedule -t # 测试执行
|
|
769
|
+
cs.schedule -q # 测试QQ适配器连接`;
|
|
698
770
|
});
|
|
699
771
|
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) => {
|
|
700
772
|
if (!address) return "使用格式: cs [地址:端口]\n示例: cs 127.0.0.1:27015\n示例: cs edgebug.cn";
|
|
@@ -725,9 +797,9 @@ cs.schedule -t # 测试执行`;
|
|
|
725
797
|
|
|
726
798
|
`;
|
|
727
799
|
if (error.message.includes("无法加载 gamedig")) {
|
|
728
|
-
errorMessage += "请确保已安装 gamedig:\n";
|
|
729
|
-
errorMessage += "1.
|
|
730
|
-
errorMessage += "2.
|
|
800
|
+
errorMessage += "请确保已安装 koishi-plugin-gamedig:\n";
|
|
801
|
+
errorMessage += "1. 在插件市场搜索并安装 koishi-plugin-gamedig\n";
|
|
802
|
+
errorMessage += "2. 启用该插件后重启";
|
|
731
803
|
} else if (error.message.includes("无效的地址格式")) {
|
|
732
804
|
errorMessage += "地址格式应为: 地址:端口\n";
|
|
733
805
|
errorMessage += "示例: 127.0.0.1:27015 或 edgebug.cn:27015\n";
|
|
@@ -744,21 +816,28 @@ cs.schedule -t # 测试执行`;
|
|
|
744
816
|
});
|
|
745
817
|
ctx.command("cs.status", "检查插件状态和配置").action(async () => {
|
|
746
818
|
try {
|
|
747
|
-
|
|
748
|
-
const cacheSize = cache.size;
|
|
819
|
+
const gamedigStatus = ctx.gamedig ? "✅ 可用" : "❌ 不可用";
|
|
749
820
|
let canvasStatus = "❌ 不可用";
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
821
|
+
if (ctx.canvas) {
|
|
822
|
+
try {
|
|
823
|
+
const canvas = await ctx.canvas.createCanvas(1, 1);
|
|
824
|
+
const ctx2d = canvas.getContext("2d");
|
|
825
|
+
canvasStatus = "✅ 可用";
|
|
826
|
+
} catch (error) {
|
|
827
|
+
canvasStatus = `❌ 不可用: ${error.message}`;
|
|
828
|
+
}
|
|
755
829
|
}
|
|
830
|
+
const cacheSize = cache.size;
|
|
756
831
|
const scheduleStatus = config.scheduleEnabled ? "✅ 已启用" : "❌ 已禁用";
|
|
757
832
|
const scheduleTimerStatus = scheduleTimer ? "运行中" : "未运行";
|
|
833
|
+
const qqBots = ctx.bots.filter((bot) => bot.platform === config.qqAdapterName);
|
|
834
|
+
const qqStatus = qqBots.length > 0 ? `✅ 可用 (${qqBots.length}个)` : "❌ 不可用";
|
|
758
835
|
return `✅ CS服务器查询插件状态
|
|
759
836
|
💾 缓存数量: ${cacheSize} 条
|
|
760
|
-
|
|
837
|
+
🕹️ Gamedig插件: ${gamedigStatus}
|
|
838
|
+
🖼️ Canvas插件: ${canvasStatus}
|
|
761
839
|
📅 定时任务: ${scheduleStatus} (${scheduleTimerStatus})
|
|
840
|
+
🤖 QQ适配器: ${qqStatus} (名称: ${config.qqAdapterName})
|
|
762
841
|
⚙️ 配置参数:
|
|
763
842
|
超时时间: ${config.timeout}ms
|
|
764
843
|
缓存时间: ${config.cacheTime}ms
|
|
@@ -770,13 +849,14 @@ cs.schedule -t # 测试执行`;
|
|
|
770
849
|
图片宽度: ${config.imageWidth}px
|
|
771
850
|
图片最小高度: ${config.imageHeight}px
|
|
772
851
|
字体大小: ${config.fontSize}px
|
|
852
|
+
群组ID格式: ${config.useFullChannelId ? "适配器:群号" : "群号"}
|
|
773
853
|
|
|
774
854
|
📝 使用: cs [地址:端口]
|
|
775
855
|
📝 选项: -i 生成图片, -t 输出文本, -c 清除缓存
|
|
776
856
|
📅 定时任务: cs.schedule 查看定时任务管理`;
|
|
777
857
|
} catch (error) {
|
|
778
858
|
return `❌ 插件状态异常: ${error.message}
|
|
779
|
-
|
|
859
|
+
请确保已安装并启用 koishi-plugin-gamedig 和 koishi-plugin-canvas 插件`;
|
|
780
860
|
}
|
|
781
861
|
});
|
|
782
862
|
ctx.command("cs.help", "查看帮助").action(() => {
|
|
@@ -804,12 +884,13 @@ cs.help - 显示此帮助
|
|
|
804
884
|
定时自动向指定QQ群组发送服务器状态
|
|
805
885
|
配置: 插件配置面板中设置
|
|
806
886
|
管理: cs.schedule 命令
|
|
807
|
-
群组ID:
|
|
887
|
+
群组ID格式: ${config.useFullChannelId ? "适配器:群号 (如: qq:123456)" : "群号 (如: 123456)"}
|
|
808
888
|
|
|
809
889
|
💡 提示:
|
|
810
890
|
1. 如果不指定端口,默认使用27015
|
|
811
891
|
2. 只支持CS服务器查询
|
|
812
|
-
3. 查询结果缓存${config.cacheTime}ms,使用 -c
|
|
892
|
+
3. 查询结果缓存${config.cacheTime}ms,使用 -c 清除缓存
|
|
893
|
+
4. 需要安装 koishi-plugin-gamedig 和 koishi-plugin-canvas 插件`;
|
|
813
894
|
});
|
|
814
895
|
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) => {
|
|
815
896
|
if (options.list) {
|
|
@@ -860,30 +941,7 @@ cs.help - 显示此帮助
|
|
|
860
941
|
session?.send(`⚠️ 服务器数量超过限制,仅查询前 ${maxServers} 个`);
|
|
861
942
|
}
|
|
862
943
|
try {
|
|
863
|
-
const
|
|
864
|
-
const results = await Promise.allSettled(
|
|
865
|
-
serversToQuery.map(async (server, index) => {
|
|
866
|
-
try {
|
|
867
|
-
const { host, port } = parseAddress(server);
|
|
868
|
-
const data = await queryServer(host, port);
|
|
869
|
-
return {
|
|
870
|
-
index: index + 1,
|
|
871
|
-
server,
|
|
872
|
-
success: true,
|
|
873
|
-
data
|
|
874
|
-
};
|
|
875
|
-
} catch (error) {
|
|
876
|
-
return {
|
|
877
|
-
index: index + 1,
|
|
878
|
-
server,
|
|
879
|
-
success: false,
|
|
880
|
-
error: error.message
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
})
|
|
884
|
-
);
|
|
885
|
-
const endTime = Date.now();
|
|
886
|
-
const queryTime = endTime - startTime;
|
|
944
|
+
const { results, queryTime } = await queryServers(serversToQuery);
|
|
887
945
|
const shouldGenerateImage = options.image || config.generateImage && !options.text;
|
|
888
946
|
if (shouldGenerateImage) {
|
|
889
947
|
try {
|
|
@@ -893,37 +951,7 @@ cs.help - 显示此帮助
|
|
|
893
951
|
console.error("生成批量查询图片失败:", imageError);
|
|
894
952
|
}
|
|
895
953
|
}
|
|
896
|
-
|
|
897
|
-
const failed = serversToQuery.length - successful;
|
|
898
|
-
let message = `📊 批量查询结果 (${utils.formatTime(queryTime)})
|
|
899
|
-
`;
|
|
900
|
-
message += `✅ 成功: ${successful} 个 | ❌ 失败: ${failed} 个
|
|
901
|
-
|
|
902
|
-
`;
|
|
903
|
-
message += "序号 服务器名称 在线人数\n";
|
|
904
|
-
message += "──────────────────────────────\n";
|
|
905
|
-
results.forEach((result, index) => {
|
|
906
|
-
const serverInfo = serversToQuery[index];
|
|
907
|
-
if (result.status === "fulfilled") {
|
|
908
|
-
const { success, data, error } = result.value;
|
|
909
|
-
if (success && data) {
|
|
910
|
-
const { result: serverData } = data;
|
|
911
|
-
const serverName = serverData.name ? utils.cleanName(serverData.name) : "未知";
|
|
912
|
-
const playerCount = serverData.players?.length || 0;
|
|
913
|
-
const maxPlayers = serverData.maxplayers || 0;
|
|
914
|
-
const truncatedName = utils.truncateText(serverName, 20);
|
|
915
|
-
const paddedName = truncatedName.padEnd(20, " ");
|
|
916
|
-
message += `${(index + 1).toString().padStart(2, " ")} ${paddedName} ${playerCount}/${maxPlayers}
|
|
917
|
-
`;
|
|
918
|
-
} else {
|
|
919
|
-
message += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败: ${error}
|
|
920
|
-
`;
|
|
921
|
-
}
|
|
922
|
-
} else {
|
|
923
|
-
message += `${(index + 1).toString().padStart(2, " ")} ${serverInfo} ❌ 查询失败
|
|
924
|
-
`;
|
|
925
|
-
}
|
|
926
|
-
});
|
|
954
|
+
let message = generateTextTable(results, serversToQuery, queryTime, "批量查询结果");
|
|
927
955
|
message += "\n📋 输入 `cs <序号>` 查看服务器详情";
|
|
928
956
|
message += "\n📋 输入 `cs <服务器地址>` 查询单个服务器";
|
|
929
957
|
return message;
|
|
@@ -940,9 +968,11 @@ cs.help - 显示此帮助
|
|
|
940
968
|
});
|
|
941
969
|
}
|
|
942
970
|
__name(apply, "apply");
|
|
971
|
+
var inject = ["canvas", "gamedig"];
|
|
943
972
|
// Annotate the CommonJS export names for ESM import in node:
|
|
944
973
|
0 && (module.exports = {
|
|
945
974
|
Config,
|
|
946
975
|
apply,
|
|
976
|
+
inject,
|
|
947
977
|
name
|
|
948
978
|
});
|
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.1.
|
|
4
|
+
"version": "1.1.1",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -24,14 +24,15 @@
|
|
|
24
24
|
"gamedig"
|
|
25
25
|
]
|
|
26
26
|
},
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"koishi": "^4.18.7",
|
|
29
|
-
"koishi-plugin-canvas": "^0.2.2",
|
|
30
|
-
"koishi-plugin-gamedig": "^1.2.2"
|
|
31
|
-
},
|
|
32
27
|
"devDependencies": {
|
|
28
|
+
"koishi": "^4.18.7",
|
|
33
29
|
"koishi-plugin-canvas": "^0.2.2",
|
|
34
30
|
"koishi-plugin-gamedig": "^1.2.2"
|
|
35
31
|
}
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"koishi": "^4.18.7",
|
|
35
|
+
"koishi-plugin-canvas": "^0.2.2",
|
|
36
|
+
"koishi-plugin-gamedig": "^1.2.2"
|
|
36
37
|
}
|
|
37
38
|
}
|