koishi-plugin-minecraft-search 0.1.2 → 1.0.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/lib/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export interface Config {
10
10
  servers: ServerConfig[];
11
11
  querySettings: QuerySettings;
12
12
  minekuaiSettings: MinekuaiSettings;
13
+ apiSettings: ApiSettings;
13
14
  }
14
15
  export interface QuerySettings {
15
16
  defaultTimeout: number;
@@ -24,5 +25,12 @@ export interface MinekuaiSettings {
24
25
  apiUrl: string;
25
26
  apiKey: string;
26
27
  }
28
+ export interface ApiSettings {
29
+ apiProvider: 'mcstatus' | 'lazy';
30
+ lazyApiUrl: string;
31
+ useBackup: boolean;
32
+ returnType: 'json' | 'image' | 'html';
33
+ autoDetectBedrock: boolean;
34
+ }
27
35
  export declare const Config: Schema<Config>;
28
36
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -28,6 +28,23 @@ module.exports = __toCommonJS(src_exports);
28
28
  var import_koishi = require("koishi");
29
29
  var name = "minecraft-status";
30
30
  var Config = import_koishi.Schema.intersect([
31
+ // 新增:API配置分类
32
+ import_koishi.Schema.object({
33
+ apiSettings: import_koishi.Schema.object({
34
+ apiProvider: import_koishi.Schema.union([
35
+ import_koishi.Schema.const("mcstatus").description("mcstatus.io API (默认)"),
36
+ import_koishi.Schema.const("lazy").description("Lazy Minecraft API")
37
+ ]).description("API提供商选择").default("mcstatus"),
38
+ lazyApiUrl: import_koishi.Schema.string().description("Lazy API地址").default("https://api.imlazy.ink/mcapi"),
39
+ useBackup: import_koishi.Schema.boolean().description("使用备用API地址").default(false),
40
+ returnType: import_koishi.Schema.union([
41
+ import_koishi.Schema.const("json").description("JSON格式"),
42
+ import_koishi.Schema.const("image").description("图片格式"),
43
+ import_koishi.Schema.const("html").description("网页格式")
44
+ ]).description("返回类型 (仅Lazy API)").default("json"),
45
+ autoDetectBedrock: import_koishi.Schema.boolean().description("自动检测基岩版服务器").default(true)
46
+ })
47
+ }).description("API设置"),
31
48
  import_koishi.Schema.object({
32
49
  servers: import_koishi.Schema.array(import_koishi.Schema.object({
33
50
  id: import_koishi.Schema.number().required().description("服务器ID (数字)"),
@@ -131,7 +148,25 @@ function apply(ctx, config) {
131
148
  return `❌ 重启服务器 ${server.name} 失败: ${error.message}`;
132
149
  }
133
150
  });
134
- ctx.command("mcstatus [server]", "查询 Minecraft 服务器状态").alias("服务器状态", "查服").option("list", "-l 查看服务器列表").option("info", "-i <id> 查看服务器详细信息", { type: "number" }).option("timeout", "-t <seconds> 设置超时时间", { type: "number" }).option("force", "-f 强制刷新缓存").action(async ({ session, options }, server) => {
151
+ ctx.command("mcstatus [server]", "查询 Minecraft 服务器状态").alias("服务器状态", "查服").option("list", "-l 查看服务器列表").option("info", "-i <id> 查看服务器详细信息", { type: "number" }).option("timeout", "-t <seconds> 设置超时时间", { type: "number" }).option("force", "-f 强制刷新缓存").option("api", "-a <provider> 临时切换API提供商", { type: "string" }).action(async ({ session, options }, server) => {
152
+ if (options.api) {
153
+ const tempProvider = options.api.toLowerCase();
154
+ if (["mcstatus", "lazy"].includes(tempProvider)) {
155
+ const originalProvider = config.apiSettings.apiProvider;
156
+ config.apiSettings.apiProvider = tempProvider;
157
+ try {
158
+ const result = await handleMcStatusCommand(server, options);
159
+ return result;
160
+ } finally {
161
+ config.apiSettings.apiProvider = originalProvider;
162
+ }
163
+ } else {
164
+ return "❌ 无效的API提供商,可选: mcstatus, lazy";
165
+ }
166
+ }
167
+ return await handleMcStatusCommand(server, options);
168
+ });
169
+ async function handleMcStatusCommand(server, options) {
135
170
  if (options.list) {
136
171
  return getServerList(config.servers);
137
172
  }
@@ -150,7 +185,8 @@ function apply(ctx, config) {
150
185
  return getServerInfo(serverConfig, config, options.force, options.timeout || config.querySettings.defaultTimeout);
151
186
  }
152
187
  return getDirectServerStatus(server, config, options.force, options.timeout || config.querySettings.defaultTimeout);
153
- });
188
+ }
189
+ __name(handleMcStatusCommand, "handleMcStatusCommand");
154
190
  function getServerList(servers) {
155
191
  if (servers.length === 0) {
156
192
  return "暂无服务器配置,请在插件配置中添加服务器。";
@@ -231,12 +267,18 @@ function apply(ctx, config) {
231
267
  iconElement ? (0, import_koishi.h)("span", [iconElement, " "]) : "",
232
268
  `🟢 ${server.name}`
233
269
  ]),
234
- (0, import_koishi.h)("p", `📍 地址: ${server.host}`),
235
- (0, import_koishi.h)("p", `🎮 版本: ${status.version.name_clean} (协议: ${status.version.protocol})`),
270
+ (0, import_koishi.h)("p", `📍 地址: ${server.host}`)
271
+ );
272
+ let versionDisplay = `🎮 版本: ${status.version.name_clean}`;
273
+ if (status.version.protocol && status.version.protocol !== 0) {
274
+ versionDisplay += ` (协议: ${status.version.protocol})`;
275
+ }
276
+ message.children.push((0, import_koishi.h)("p", versionDisplay));
277
+ message.children.push(
236
278
  (0, import_koishi.h)("p", `📅 状态获取时间: ${new Date(status.retrieved_at).toLocaleString("zh-CN")}`)
237
279
  );
238
280
  if (status.motd) {
239
- const cleanMotd = status.motd.clean.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
281
+ let cleanMotd = status.motd.clean.replace(/\n/g, " ").replace(/\s+/g, " ").trim().replace(/§./g, "");
240
282
  if (cleanMotd) {
241
283
  message.children.push(
242
284
  (0, import_koishi.h)("p", "📋 MOTD: " + cleanMotd.substring(0, 100) + (cleanMotd.length > 100 ? "..." : ""))
@@ -254,11 +296,6 @@ function apply(ctx, config) {
254
296
  );
255
297
  }
256
298
  }
257
- if (status.software) {
258
- message.children.push(
259
- (0, import_koishi.h)("p", `💻 核心: ${status.software}`)
260
- );
261
- }
262
299
  if (config2.querySettings.showPlugins && status.plugins && status.plugins.length > 0) {
263
300
  const pluginCount = status.plugins.length;
264
301
  const pluginList = status.plugins.slice(0, 5).map((p) => p.version ? `${p.name} v${p.version}` : p.name).join(", ");
@@ -273,11 +310,6 @@ function apply(ctx, config) {
273
310
  (0, import_koishi.h)("p", `⚙️ 模组 (${modCount}个): ${modList}`)
274
311
  );
275
312
  }
276
- if (status.srv_record) {
277
- message.children.push(
278
- (0, import_koishi.h)("p", `🔗 SRV记录: ${status.srv_record.host}:${status.srv_record.port}`)
279
- );
280
- }
281
313
  if (status.expires_at) {
282
314
  const cacheTime = Math.max(0, Math.floor((status.expires_at - Date.now()) / 1e3));
283
315
  message.children.push(
@@ -322,31 +354,143 @@ function apply(ctx, config) {
322
354
  if (!address || address.trim() === "") {
323
355
  throw new Error("服务器地址不能为空");
324
356
  }
325
- const cacheKey = `mcstatus:${address}:${enableQuery}`;
357
+ const cacheKey = `mcstatus:${address}:${enableQuery}:${config.apiSettings.apiProvider}`;
326
358
  if (!force) {
327
359
  const cached = cache.get(cacheKey);
328
360
  if (cached && Date.now() - cached.timestamp < config.querySettings.cacheTime * 1e3) {
329
361
  return cached.data;
330
362
  }
331
363
  }
332
- const url = `https://api.mcstatus.io/v2/status/java/${encodeURIComponent(address)}`;
333
- const params = {
334
- query: enableQuery.toString(),
335
- timeout: timeout.toString()
336
- };
364
+ let response;
365
+ const { apiProvider } = config.apiSettings;
337
366
  try {
338
- const response = await ctx.http.get(url, { params });
367
+ if (apiProvider === "lazy") {
368
+ response = await queryWithLazyApi(address, timeout);
369
+ } else {
370
+ const url = `https://api.mcstatus.io/v2/status/java/${encodeURIComponent(address)}`;
371
+ const params = {
372
+ query: enableQuery.toString()
373
+ };
374
+ response = await ctx.http.get(url, {
375
+ params,
376
+ timeout: timeout * 1e3 + 5e3
377
+ });
378
+ }
339
379
  cache.set(cacheKey, {
340
380
  data: response,
341
381
  timestamp: Date.now()
342
382
  });
383
+ ctx.logger.debug(`${apiProvider.toUpperCase()} API查询成功: ${address}`);
343
384
  return response;
344
385
  } catch (error) {
345
- ctx.logger.error(`查询服务器状态失败: ${address}`, error);
346
- throw new Error(`查询服务器状态失败: ${address}`);
386
+ ctx.logger.error(`${apiProvider.toUpperCase()} API查询失败: ${address}`, error);
387
+ throw new Error(`查询服务器状态失败: ${address} (${error.message})`);
347
388
  }
348
389
  }
349
390
  __name(getServerStatus, "getServerStatus");
391
+ async function queryWithLazyApi(host, timeout) {
392
+ const { lazyApiUrl, useBackup, returnType, autoDetectBedrock } = config.apiSettings;
393
+ const baseUrl = useBackup ? "https://api.lazy.ink/mcapi" : lazyApiUrl;
394
+ const params = new URLSearchParams({
395
+ type: returnType,
396
+ host
397
+ });
398
+ if (autoDetectBedrock) {
399
+ params.append("be", "false");
400
+ }
401
+ const url = `${baseUrl}?${params.toString()}`;
402
+ try {
403
+ const response = await ctx.http.get(url, { timeout: timeout * 1e3 });
404
+ return transformLazyResponse(response, host);
405
+ } catch (error) {
406
+ ctx.logger.error("Lazy API查询失败:", error);
407
+ throw new Error(`Lazy API查询失败: ${error.message}`);
408
+ }
409
+ }
410
+ __name(queryWithLazyApi, "queryWithLazyApi");
411
+ function transformLazyResponse(lazyData, host) {
412
+ let cleanMotd = "";
413
+ if (lazyData.motd && lazyData.motd.extra) {
414
+ cleanMotd = lazyData.motd.extra.map((item) => item.text).join("");
415
+ cleanMotd = cleanMotd.replace(/§./g, "");
416
+ }
417
+ let versionName = lazyData.version || "Unknown";
418
+ if (versionName === "Unknown" || !versionName) {
419
+ versionName = "未知";
420
+ }
421
+ return {
422
+ online: lazyData.status === "在线",
423
+ host: lazyData.host || host,
424
+ version: {
425
+ name_clean: versionName,
426
+ protocol: lazyData.protocol || 0
427
+ },
428
+ players: {
429
+ online: lazyData.players_online || 0,
430
+ max: lazyData.players_max || 0,
431
+ list: (lazyData.players || []).map((p) => ({
432
+ name_clean: p.name
433
+ }))
434
+ },
435
+ motd: {
436
+ clean: cleanMotd || lazyData.motd?.text || "A Minecraft Server"
437
+ },
438
+ icon: lazyData.favicon || null,
439
+ software: lazyData.software || "",
440
+ plugins: lazyData.plugins || [],
441
+ mods: lazyData.mods || [],
442
+ retrieved_at: Date.now(),
443
+ expires_at: Date.now() + config.querySettings.cacheTime * 1e3
444
+ };
445
+ }
446
+ __name(transformLazyResponse, "transformLazyResponse");
447
+ ctx.command("mcstatus.api", "检查API状态").option("switch", "-s <provider> 切换API提供商", { type: "string" }).option("test", "-t 测试所有API").action(async ({ session, options }) => {
448
+ if (options.switch) {
449
+ if (["mcstatus", "lazy"].includes(options.switch.toLowerCase())) {
450
+ config.apiSettings.apiProvider = options.switch.toLowerCase();
451
+ return `✅ 已切换API提供商为: ${options.switch.toUpperCase()}`;
452
+ } else {
453
+ return "❌ 无效的API提供商,可选: mcstatus, lazy";
454
+ }
455
+ }
456
+ if (options.test) {
457
+ const testServers = [
458
+ { name: "Hypixel", host: "mc.hypixel.net" },
459
+ { name: "演示服务器", host: "demo.mcstatus.io" }
460
+ ];
461
+ const results = [];
462
+ for (const server of testServers) {
463
+ try {
464
+ const startTime = Date.now();
465
+ await getServerStatus(server.host, 5, false, true);
466
+ const responseTime = Date.now() - startTime;
467
+ results.push(`🟢 ${server.name}: ${responseTime}ms`);
468
+ } catch (error) {
469
+ results.push(`🔴 ${server.name}: 失败`);
470
+ }
471
+ }
472
+ return (0, import_koishi.h)("message", [
473
+ (0, import_koishi.h)("p", `当前API提供商: ${config.apiSettings.apiProvider.toUpperCase()}`),
474
+ (0, import_koishi.h)("p", "API测试结果:"),
475
+ ...results.map((r) => (0, import_koishi.h)("p", r)),
476
+ (0, import_koishi.h)(
477
+ "p",
478
+ { style: { fontSize: "12px", color: "#888" } },
479
+ '使用 "mcstatus.api -s <provider>" 切换API'
480
+ )
481
+ ]);
482
+ }
483
+ return (0, import_koishi.h)("message", [
484
+ (0, import_koishi.h)("p", `当前API提供商: ${config.apiSettings.apiProvider.toUpperCase()}`),
485
+ (0, import_koishi.h)("p", `Lazy API地址: ${config.apiSettings.useBackup ? "备用地址" : config.apiSettings.lazyApiUrl}`),
486
+ (0, import_koishi.h)("p", `返回类型: ${config.apiSettings.returnType.toUpperCase()}`),
487
+ (0, import_koishi.h)(
488
+ "p",
489
+ { style: { fontSize: "12px", color: "#888" } },
490
+ '使用 "mcstatus.api -t" 测试API或 "mcstatus.api -s <provider>" 切换'
491
+ )
492
+ ]);
493
+ });
350
494
  setInterval(() => {
351
495
  const now = Date.now();
352
496
  for (const [key, value] of cache.entries()) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-minecraft-search",
3
3
  "description": "使用API查询基础的mc服务器信息",
4
- "version": "0.1.2",
4
+ "version": "1.0.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [