koishi-plugin-node-async-bot-all 2.31.0 → 3.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.js CHANGED
@@ -34,7 +34,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
34
34
  // src/locales/zh-CN.yml
35
35
  var require_zh_CN = __commonJS({
36
36
  "src/locales/zh-CN.yml"(exports2, module2) {
37
- module2.exports = { noApi: "未指定 API", noop: "无", unknownError: "未知错误。", failed: "{quote}{time}\n{error}", "failed-md": "**{time}**\n\n---\n\n{error}", commands: { na: { description: "NodeAsync Bot" }, cxgame: { description: "查询服务器当前人数。", messages: { msg: "{time}{list}\n进服指南请在群公告中查看。", "msg-md": "*{time}*\n\n---\n{list}\n\n---\n\n进服指南请在群公告中查看。", list: "【{name} 服务器 {count}】\n➣ {version}:{players}\n➣ {ne}:{list}\n➣ 备注:{note}", "list-md": "## {name} 服务器 {count}\n\n- {version}:{players}\n- {ne}:{list}\n- 备注:{note}", listFailed: "【{name} 服务器 {count}】\n➣ 查询失败:{data}\n➣ 请稍后重试。\n➣ 备注:{note}", "listFailed-md": "## {name} 服务器 {count}\n\n- 查询失败:{data}\n- 请稍后重试。\n- 备注:{note}", timeout: "请求超时。", fewData: "服务端返回的数据过少。", close: "服务器已关闭。", host: "没有到主机的路由。", dns: "解析失败。" } }, status: { description: "查询机器人状态。", messages: { msg: "{time}\n--- 系统状态 ---\n系统名称:{name}\nCPU使用率:{cpu}\n内存使用率:{memory}\n--- 机器人状态 ---\n昨日收/发消息数量:{msgCount}\n机器人版本:{version}\n运行时间:{online}", "msg-md": "*{time}*\n\n---\n\n## 系统状态\n\n- 系统名称:{name}\n- CPU使用率:{cpu}\n- 内存使用率:{memory}\n\n## 机器人状态\n\n- 昨日收/发消息数量:{msgCount}\n- 机器人版本:{version}\n- 运行时间:{online}" } }, random: { description: "随机数生成器。", usage: "缺少参数时默认生成 0-10000 的随机数。\n使用示例:", examples: "random 1 128 生成1到128范围的随机数", messages: { msg: "{time}\n生成了一个随机数:{data}", "msg-md": "*{time}*\n\n---\n\n生成了一个随机数:{data}" } }, info: { description: "查询机器人信息。", messages: { msg: "{time}\n--- NodeAsync {version} ---\n私有机器人,服务器专用。\n--- 开发者 ---\n德二吹风机(3112836258)\n--- 其他 ---\nNode.js 版本:{nodeVersion}\nKoishi 版本:{koishiVersion}\n开源地址:https://github.com/ccd2s/node-async-bot-all\n官网:https://www.tasaed.top", "msg-md": "*{time}*\n\n---\n\n## NodeAsync {version}\n\n私有机器人,服务器专用。\n\n## 开发者\n\n- 德二吹风机(3112836258)\n\n## 其他\n\n- Node.js 版本:{nodeVersion}\n- Koishi 版本:{koishiVersion}\n- 开源地址:https://github.com/ccd2s/node-async-bot-all\n- 官网:https://www.tasaed.top" } }, rw: { description: "随机名言名句。", messages: null }, randomba: { description: "随机BA图。", messages: { msg: "{quote}{image}" } }, centerservertest: { description: "查看中心服务器状态。", messages: { msg: "{time}{list}", "msg-md": "*{time}*\n\n---\n{list}", list: "== {name} ==\n状态:{status}\n存活率:{uptime}\n检测时间:{time}", "list-md": "## {name}\n\n- 状态:{status}\n- 存活率:{uptime}\n- 检测时间:{time}", listFailed: "== {name} ==\n查看失败:{data}", "listFailed-md": "## {name}\n\n查看失败:{data}", statusLive: "✅ 正常", statusDie: "❌ 故障", dataFail: "未能获取到此服务器的状态信息。" } }, meme: { description: "群友的怪话!", usage: "不带参数时随机查看\n带参数时为查看指定 meme", examples: "meme\nmeme 1145", messages: { msg: "{quote}{time}\n{image}\n{title}", "msg-qq": "{quote}{image}\n{time}\n{title}" } }, randomcat: { description: "随机猫猫图。", messages: { msg: "{quote}{image}" } }, getqqinfo: { description: "获取 QQ 号的信息。", usage: "获取 QQ 号的信息。", examples: "getqqinfo 10001", messages: { msg: "{quote}{image}", failed: "{quote}{time}\n获取失败:{data}", command: "QQ 号不正确。" } }, msg2img: { description: "引用一个消息并使用此指令,即可将消息转图。", usage: "引用一条消息,/m。(不要带@)", messages: { msg: "{quote}{image}", failed: "{quote}{time}\n获取失败:{data}", null: "未引用任何消息。", matroska: "{quote}禁止套娃!" } }, slnews: { description: "抓取 NorthWood 的 Steam 最新新闻。" }, use: { description: "使用 TA !", usage: "use @用户", examples: "use @用户", messages: { command: "请 @ 一个用户。", msg: "{at} {desc}了 {at2} !" } } } };
37
+ module2.exports = { noApi: "未指定 API", noop: "无", unknownError: "未知错误。", failed: "{quote}{time}\n{error}", "failed-md": "**{time}**\n{error}", commands: { na: { description: "NodeAsync Bot" }, cxgame: { description: "查询服务器当前人数。", messages: { msg: "{time}{list}\n进服指南请在群公告中查看。", "msg-md": "*{time}*\n\n---\n{list}\n\n---\n\n进服指南请在群公告中查看。", list: "【{name} 服务器 {count}】\n➣ {version}:{players}\n➣ {ne}:{list}\n➣ 备注:{note}", "list-md": "## {name} 服务器 {count}\n\n- {version}:{players}\n- {ne}:{list}\n- 备注:{note}", listFailed: "【{name} 服务器 {count}】\n➣ 查询失败:{data}\n➣ 请稍后重试。\n➣ 备注:{note}", "listFailed-md": "## {name} 服务器 {count}\n\n- 查询失败:{data}\n- 请稍后重试。\n- 备注:{note}", timeout: "请求超时。", fewData: "服务端返回的数据过少。", close: "服务器已关闭。", host: "没有到主机的路由。", dns: "解析失败。", "ne-a2s": "机器人", "ne-mc": "玩家列表", forbidden: "此指令不允许在本群使用。", failed: "查询失败:" } }, status: { description: "查询机器人状态。", messages: { msg: "{time}\n--- 系统状态 ---\n系统名称:{name}\nCPU使用率:{cpu}\n内存使用率:{memory}\n--- 机器人状态 ---\n昨日收/发消息数量:{msgCount}\n机器人版本:{version}\n运行时间:{online}", "msg-md": "*{time}*\n\n---\n\n## 系统状态\n\n- 系统名称:{name}\n- CPU使用率:{cpu}\n- 内存使用率:{memory}\n\n## 机器人状态\n\n- 昨日收/发消息数量:{msgCount}\n- 机器人版本:{version}\n- 运行时间:{online}", error: "状态获取失败。" } }, random: { description: "随机数生成器。", usage: "缺少参数时默认生成 0-10000 的随机数。\n使用示例:", examples: "random 1 128 生成1到128范围的随机数", messages: { msg: "{time}\n生成了一个随机数:{data}", "msg-md": "*{time}*\n\n---\n\n生成了一个随机数:{data}" } }, info: { description: "查询机器人信息。", messages: { msg: "{time}\n--- NodeAsync {version} ---\n私有机器人,服务器专用。\n--- 开发者 ---\n德二吹风机(3112836258)\n--- 其他 ---\nNode.js 版本:{nodeVersion}\nKoishi 版本:{koishiVersion}\n开源地址:https://github.com/ccd2s/node-async-bot-all\n官网:https://www.tasaed.top", "msg-md": "*{time}*\n\n---\n\n## NodeAsync {version}\n\n私有机器人,服务器专用。\n\n## 开发者\n\n- 德二吹风机(3112836258)\n\n## 其他\n\n- Node.js 版本:{nodeVersion}\n- Koishi 版本:{koishiVersion}\n- 开源地址:https://github.com/ccd2s/node-async-bot-all\n- 官网:https://www.tasaed.top", error: "读取信息失败。" } }, rw: { description: "随机名言名句。", messages: null }, randomba: { description: "随机BA图。", messages: { msg: "{quote}{image}" } }, centerservertest: { description: "查看中心服务器状态。", messages: { msg: "{time}{list}", "msg-md": "*{time}*\n\n---\n{list}", list: "== {name} ==\n状态:{status}\n存活率:{uptime}\n检测时间:{time}", "list-md": "## {name}\n\n- 状态:{status}\n- 存活率:{uptime}\n- 检测时间:{time}", listFailed: "== {name} ==\n查看失败:{data}", "listFailed-md": "## {name}\n\n查看失败:{data}", statusLive: "✅ 正常", statusDie: "❌ 故障", dataFail: "未能获取到此服务器的状态信息。" } }, meme: { description: "群友的怪话!", usage: "不带参数时随机查看\n带参数时为查看指定 meme", examples: "meme\nmeme 1145", messages: { msg: "{quote}{time}\n{image}\n{title}", "msg-qq": "{quote}{image}\n{time}\n{title}" } }, randomcat: { description: "随机猫猫图。", messages: { msg: "{quote}{image}" } }, getqqinfo: { description: "获取 QQ 号的信息。", usage: "获取 QQ 号的信息。", examples: "getqqinfo 10001", messages: { msg: "{quote}{image}", failed: "{quote}{time}\n获取失败:{data}", command: "QQ 号不正确。" } }, msg2img: { description: "引用一个消息并使用此指令,即可将消息转图。", usage: "引用一条消息,/m。(不要带@)", messages: { msg: "{quote}{image}", failed: "{quote}{time}\n获取失败:{data}", null: "未引用任何消息。", matroska: "{quote}禁止套娃!" } }, steamnews: { description: "抓取 Steam 游戏的最新新闻。" }, use: { description: "使用 TA !", usage: "use @用户", examples: "use @用户", messages: { command: "请 @ 一个用户。", msg: "{at} {desc}了 {at2} !" } } } };
38
38
  }
39
39
  });
40
40
 
@@ -42,13 +42,14 @@ var require_zh_CN = __commonJS({
42
42
  var src_exports = {};
43
43
  __export(src_exports, {
44
44
  Config: () => Config,
45
+ NodeAsyncBot: () => NodeAsyncBot,
45
46
  apply: () => apply,
46
47
  inject: () => inject,
47
48
  name: () => name,
48
49
  usage: () => usage
49
50
  });
50
51
  module.exports = __toCommonJS(src_exports);
51
- var import_koishi3 = require("koishi");
52
+ var import_koishi4 = require("koishi");
52
53
 
53
54
  // src/commands.ts
54
55
  var import_koishi2 = require("koishi");
@@ -59,9 +60,7 @@ var import_fs = __toESM(require("fs"));
59
60
  var import_path = __toESM(require("path"));
60
61
  var import_koishi = require("koishi");
61
62
  var import_steam_server_query = require("steam-server-query");
62
- var import_html = __toESM(require("@bbob/html"));
63
- var import_preset_html5 = __toESM(require("@bbob/preset-html5"));
64
- var import_bing_translate_api = require("bing-translate-api");
63
+ var import_feedsmith = require("feedsmith");
65
64
  var import_minecraft_server_util = require("minecraft-server-util");
66
65
  function getSystemName() {
67
66
  return import_os.default.type() + " " + import_os.default.release();
@@ -101,15 +100,15 @@ __name(getCpuUsage, "getCpuUsage");
101
100
  async function getSystemUsage() {
102
101
  try {
103
102
  return {
104
- "name": getSystemName(),
105
- "cpu": await getCpuUsage(),
106
- "memory": getMemoryUsage(),
107
- "success": 0
103
+ name: getSystemName(),
104
+ cpu: await getCpuUsage(),
105
+ memory: getMemoryUsage(),
106
+ success: 0
108
107
  };
109
108
  } catch (error) {
110
109
  return {
111
- "data": error.message,
112
- "success": 1
110
+ data: error.message,
111
+ success: 1
113
112
  };
114
113
  }
115
114
  }
@@ -158,7 +157,10 @@ function formatTimestampDiff(start, end) {
158
157
  }
159
158
  __name(formatTimestampDiff, "formatTimestampDiff");
160
159
  async function getMsgCount(ctx) {
161
- const array = await ctx.database.get("analytics.message", { date: import_koishi.Time.getDateNumber() - 1 }, ["type", "count"]);
160
+ const array = await ctx.database.get("analytics.message", { date: import_koishi.Time.getDateNumber() - 1 }, [
161
+ "type",
162
+ "count"
163
+ ]);
162
164
  let receive = 0;
163
165
  let send = 0;
164
166
  array.forEach((item) => {
@@ -168,7 +170,7 @@ async function getMsgCount(ctx) {
168
170
  send = send + item.count;
169
171
  }
170
172
  });
171
- return { "receive": receive, "send": send };
173
+ return { receive, send };
172
174
  }
173
175
  __name(getMsgCount, "getMsgCount");
174
176
  function random(type = 0, data, data2) {
@@ -188,43 +190,51 @@ function random(type = 0, data, data2) {
188
190
  }
189
191
  }
190
192
  __name(random, "random");
191
- async function request(url, options = {}, timeout = 8e3, log) {
192
- const signal = AbortSignal.timeout(timeout);
193
+ async function request(url, ctx, options = { method: "GET", timeout: 8e3 }, logger) {
194
+ const log = logger ?? ctx.logger("http");
193
195
  try {
194
- const response = await fetch(url, { ...options, signal });
196
+ const response = await ctx.http(url, options);
195
197
  let responseData;
196
- let isJson;
197
- const text = await response.text();
198
+ let isObj;
199
+ const text = await response.data;
198
200
  try {
199
201
  responseData = JSON.parse(text);
200
- isJson = true;
202
+ isObj = true;
201
203
  } catch {
202
204
  responseData = text;
203
- isJson = false;
205
+ isObj = false;
204
206
  }
205
- if (!response.ok) {
206
- log?.error(`HTTP Error ${response.status}: ${url}`, responseData);
207
- return {
207
+ if (response.status !== 200) {
208
+ log.error(`HTTP Error ${response.status}: ${url}`, responseData);
209
+ return isObj ? {
210
+ success: false,
211
+ code: response.status,
212
+ error: responseData,
213
+ isObj: true,
214
+ isError: false
215
+ } : {
208
216
  success: false,
209
217
  code: response.status,
210
- error: responseData || `HTTP ${response.status}`,
211
- isJson
218
+ error: responseData ?? `HTTP ${response.status}`,
219
+ isObj: false,
220
+ isError: false
212
221
  };
213
222
  }
214
- log?.info(`HTTP ${response.status}: ${url}`);
223
+ log.info(`HTTP ${response.status}: ${url}`);
215
224
  return {
216
225
  success: true,
217
226
  data: responseData
218
227
  };
219
228
  } catch (error) {
220
- const isTimeout = error.name === "TimeoutError" || error.name === "AbortError";
221
- const errorMessage = isTimeout ? `请求超时。(${timeout}ms)` : error.message;
222
- log?.error(url);
223
- log?.error(`Request Failed: ${errorMessage}`);
229
+ const { name: name2, message } = error instanceof Error ? error : { name: "UnknownError", message: "unknown message" };
230
+ const isTimeout = name2 === "TimeoutError" || name2 === "AbortError";
231
+ const errorMessage = isTimeout ? `请求超时。(${options?.timeout}ms)` : message;
232
+ log.error(url);
233
+ log.error(`Request Failed:`, error);
224
234
  return {
225
235
  success: false,
226
- error: { name: error.name, message: errorMessage },
227
- isJson: false
236
+ error: { name: name2, message: errorMessage },
237
+ isError: true
228
238
  };
229
239
  }
230
240
  }
@@ -250,456 +260,570 @@ async function queryA2S(host, log) {
250
260
  }
251
261
  }
252
262
  __name(queryA2S, "queryA2S");
253
- async function readNewsFile(info, log) {
254
- let html;
263
+ async function parseNewsRssToHtml(rss, log, count) {
255
264
  try {
256
- const aPath = import_path.default.resolve(__dirname, "..") + import_path.default.sep + "res" + import_path.default.sep + "slNews.html";
257
- html = await import_fs.default.promises.readFile(aPath, "utf8");
258
- const content = info.appnews.newsitems[0].contents.replace("[spoiler]", "").replace("[/spoiler]", "").replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").split("\n\n").map((p) => p.trim()).filter(Boolean);
259
- const bilingualParagraphs = [];
260
- for (const paragraph of content) {
261
- const zh = await translateAPI(log, paragraph);
262
- bilingualParagraphs.push(
263
- paragraph,
264
- zh,
265
- ""
266
- // 空行用于分隔段落
267
- );
268
- }
269
- const finalText = bilingualParagraphs.join("<br />").replace(/<br \/>{2,}/g, "<br />");
270
- const contentHtml = (0, import_html.default)(finalText, (0, import_preset_html5.default)());
271
- const date = Number(info.appnews.newsitems[0].date + "000");
272
- html = html.toString().replace("{date}", new Date(date).toLocaleString()).replace("{title}", info.appnews.newsitems[0].title).replace("{content}", contentHtml);
265
+ const aPath = import_path.default.resolve(__dirname, "..") + import_path.default.sep + "res" + import_path.default.sep + "steamNews.html";
266
+ let html = await import_fs.default.promises.readFile(aPath, "utf8");
267
+ const content = (0, import_feedsmith.parseRssFeed)(rss);
268
+ if (!content?.items) return { error: new Error("响应不正确") };
269
+ const item = content.items[count ?? 0];
270
+ if (!item || !item.guid) return { error: new Error("文章不存在") };
271
+ if (item.enclosures && item.enclosures[0] && item.enclosures[0].url)
272
+ html = html.replace("<!--!", "").replace("!!-->", "").replace("{imgUrl}", item.enclosures[0].url);
273
+ return {
274
+ data: html.replace("{date}", new Date(item.pubDate ?? 0).toLocaleString()).replace("{title}", item.title ?? "无").replace("{content}", item.description ?? "无"),
275
+ guid: item.guid?.value
276
+ };
273
277
  } catch (error) {
274
278
  log.error(error);
275
279
  log.error(error.message);
276
- return [error, error.message];
280
+ return { error };
277
281
  }
278
- return [html];
279
282
  }
280
- __name(readNewsFile, "readNewsFile");
281
- async function translateAPI(log, text) {
282
- const ms = random(0, 0, 250);
283
- await (0, import_koishi.sleep)(ms);
284
- await (0, import_bing_translate_api.translate)(text, "en", "zh-Hans").then((result) => {
285
- text = result?.translation;
286
- }).catch((err) => {
287
- log.error(err);
288
- });
289
- return text;
290
- }
291
- __name(translateAPI, "translateAPI");
283
+ __name(parseNewsRssToHtml, "parseNewsRssToHtml");
292
284
  async function slpInfo(log, host, port, timeout) {
293
285
  try {
294
286
  const info = await (0, import_minecraft_server_util.status)(host, port, { timeout });
295
287
  log.info(info);
296
288
  return {
297
- "success": true,
298
- "data": info
289
+ success: true,
290
+ data: info
299
291
  };
300
292
  } catch (error) {
301
293
  log.error(error);
302
294
  return {
303
- "success": false,
304
- "data": error.message
295
+ success: false,
296
+ data: error.message
305
297
  };
306
298
  }
307
299
  }
308
300
  __name(slpInfo, "slpInfo");
309
301
 
310
302
  // src/commands.ts
311
- async function getServer(ctx, session) {
312
- const log = ctx.logger("cx");
313
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
314
- let msg;
315
- const time = getHongKongTime();
316
- const index = ctx.config.cxV3.findIndex((item) => item.id === session.event.guild?.id);
317
- if (index !== -1) {
318
- const server = ctx.config.cxV3[index].server;
319
- if (server == void 0) {
320
- msg = {
321
- "time": time,
322
- "error": "查询失败:" + session.text("noApi"),
323
- "success": 2
324
- };
325
- log.warn("Sent:");
326
- log.warn(msg);
327
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi2.h)("qq:markdown", {
328
- content: session?.text("failed-md", msg)
329
- }) : session?.text("failed", msg));
330
- return 1;
331
- }
332
- let count = 0;
333
- let list = "";
334
- for (const item of server) {
335
- const note = item.note;
336
- const type = item.type;
337
- const api = item.api;
338
- count++;
339
- if (type == "a2s") {
340
- const info = await queryA2S(api, log);
341
- if (info.success) {
342
- log.debug(info);
343
- const temp = {
344
- "count": count,
345
- "players": info.players,
346
- "version": info.version,
347
- "list": info.bots,
348
- "note": note ?? session.text("noop"),
349
- "name": "A2S",
350
- "ne": "机器人"
351
- };
352
- log.info(`Server ${count}:`);
353
- log.info(temp);
354
- list = list + "\n" + session.text(session?.bot.adapterName == "qq" ? ".list-md" : ".list", temp);
355
- } else {
356
- let err;
357
- if (info.error.toString().includes("Timeout reached. Possible reasons: You are being rate limited; Timeout too short; Wrong server host configured")) {
358
- err = session.text(".timeout");
359
- } else {
360
- err = session.text("unknown");
361
- }
362
- const temp = {
363
- "count": count,
364
- "data": err,
365
- "note": note,
366
- "name": "A2S"
367
- };
368
- log.error(`Server ${count}:`);
369
- log.error(temp);
370
- list = list + "\n" + session.text(session?.bot.adapterName == "qq" ? ".listFailed-md" : ".listFailed", temp);
371
- }
372
- } else {
373
- const host = api.split(":");
374
- const serverInfo = await slpInfo(log, host[0], Number(host[1]), ctx.config.timeout);
375
- if (serverInfo.success) {
376
- log.debug(serverInfo);
377
- if (serverInfo.data.players.sample == null) {
303
+ var CommandHandler = class {
304
+ static {
305
+ __name(this, "CommandHandler");
306
+ }
307
+ ctx;
308
+ session;
309
+ log;
310
+ time;
311
+ isQQ;
312
+ constructor(ctx, session, loggerName) {
313
+ this.ctx = ctx;
314
+ this.session = session;
315
+ this.log = ctx.logger(loggerName);
316
+ this.time = getHongKongTime();
317
+ this.isQQ = session?.bot.adapterName == "qq";
318
+ this.log.debug(
319
+ `Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`
320
+ );
321
+ }
322
+ // 发送普通消息(自动判断 QQ markdown)
323
+ async sendMsg(msgKey, data) {
324
+ await this.session?.send(
325
+ this.isQQ ? (0, import_koishi2.h)("qq:markdown", { content: this.session?.text(msgKey, data) }) : this.session?.text(msgKey, data)
326
+ );
327
+ }
328
+ // 发送失败消息
329
+ async sendFailed(data) {
330
+ await this.sendMsg(this.isQQ ? "failed-md" : "failed", data);
331
+ }
332
+ // 指令 cx
333
+ async server() {
334
+ const { ctx, session, log, time, isQQ } = this;
335
+ let msg;
336
+ const index = ctx.config.cxV3.findIndex(
337
+ (item) => item.id === session.event.guild?.id
338
+ );
339
+ if (index !== -1) {
340
+ const server = ctx.config.cxV3[index].server;
341
+ if (server == void 0) {
342
+ msg = {
343
+ time,
344
+ error: session.text(".failed") + session.text("noApi"),
345
+ success: 2
346
+ };
347
+ log.warn("Sent:");
348
+ log.warn(msg);
349
+ await this.sendFailed(msg);
350
+ return 1;
351
+ }
352
+ let count = 0;
353
+ let list = "";
354
+ for (const item of server) {
355
+ const note = item.note;
356
+ const type = item.type;
357
+ const api = item.api;
358
+ count++;
359
+ if (type == "a2s") {
360
+ const info = await queryA2S(api, log);
361
+ if (info.success) {
362
+ log.debug(info);
378
363
  const temp = {
379
- "count": count,
380
- "players": serverInfo.data.players.online + "/" + serverInfo.data.players.max,
381
- "version": serverInfo.data.version.name,
382
- "note": note ?? session.text("noop"),
383
- "ne": "玩家列表",
384
- "list": "[]",
385
- "name": "MC"
364
+ count,
365
+ players: info.players,
366
+ version: info.version,
367
+ list: info.bots,
368
+ note: note ?? session.text("noop"),
369
+ name: "A2S",
370
+ ne: session.text(".ne-a2s")
386
371
  };
387
372
  log.info(`Server ${count}:`);
388
373
  log.info(temp);
389
- list = list + "\n" + session.text(session?.bot.adapterName == "qq" ? ".list-md" : ".list", temp);
374
+ list = list + "\n" + session.text(isQQ ? ".list-md" : ".list", temp);
390
375
  } else {
376
+ let err;
377
+ if (info.error.toString().includes(
378
+ "Timeout reached. Possible reasons: You are being rate limited; Timeout too short; Wrong server host configured"
379
+ )) {
380
+ err = session.text(".timeout");
381
+ } else {
382
+ err = session.text("unknown");
383
+ }
391
384
  const temp = {
392
- "count": count,
393
- "players": serverInfo.data.players.online + "/" + serverInfo.data.players.max,
394
- "version": serverInfo.data.version.name,
395
- "list": serverInfo.data.players.sample.map((item2) => item2.name).join(", "),
396
- "note": note ?? session.text("noop"),
397
- "ne": "玩家列表",
398
- "name": "MC"
385
+ count,
386
+ data: err,
387
+ note,
388
+ name: "A2S"
399
389
  };
400
- log.info(`Server ${count}:`);
401
- log.info(temp);
402
- list = list + "\n" + session.text(session?.bot.adapterName == "qq" ? ".list-md" : ".list", temp);
390
+ log.error(`Server ${count}:`);
391
+ log.error(temp);
392
+ list = list + "\n" + session.text(isQQ ? ".listFailed-md" : ".listFailed", temp);
403
393
  }
404
394
  } else {
405
- let err = serverInfo.data;
406
- if (err.includes("connect ECONNREFUSED") || err.includes("Server is offline or unreachable")) {
407
- err = session.text(".close");
408
- } else if (err.includes("connect EHOSTUNREACH")) {
409
- err = session.text(".host");
410
- } else if (err.includes("connect ETIMEDOUT")) {
411
- err = session.text(".timeout");
412
- } else if (err.includes("Ping payload did not match received payload")) {
413
- err = session.text(".fewData");
414
- } else if (err.includes("Expected server to send packet type")) {
415
- err = session.text(".fewData");
416
- } else if (err.includes("getaddrinfo")) {
417
- err = session.text(".dns");
395
+ const host = api.split(":");
396
+ const serverInfo = await slpInfo(log, host[0], Number(host[1]), ctx.config.timeout);
397
+ if (serverInfo.success) {
398
+ log.debug(serverInfo);
399
+ if (serverInfo.data.players.sample == null) {
400
+ const temp = {
401
+ count,
402
+ players: serverInfo.data.players.online + "/" + serverInfo.data.players.max,
403
+ version: serverInfo.data.version.name,
404
+ note: note ?? session.text("noop"),
405
+ ne: session.text(".ne-mc"),
406
+ list: "[]",
407
+ name: "MC"
408
+ };
409
+ log.info(`Server ${count}:`);
410
+ log.info(temp);
411
+ list = list + "\n" + session.text(isQQ ? ".list-md" : ".list", temp);
412
+ } else {
413
+ const temp = {
414
+ count,
415
+ players: serverInfo.data.players.online + "/" + serverInfo.data.players.max,
416
+ version: serverInfo.data.version.name,
417
+ list: serverInfo.data.players.sample.map((item2) => item2.name).join(", "),
418
+ note: note ?? session.text("noop"),
419
+ ne: session.text(".ne-mc"),
420
+ name: "MC"
421
+ };
422
+ log.info(`Server ${count}:`);
423
+ log.info(temp);
424
+ list = list + "\n" + session.text(isQQ ? ".list-md" : ".list", temp);
425
+ }
426
+ } else {
427
+ let err = serverInfo.data;
428
+ if (err.includes("connect ECONNREFUSED") || err.includes("Server is offline or unreachable")) {
429
+ err = session.text(".close");
430
+ } else if (err.includes("connect EHOSTUNREACH")) {
431
+ err = session.text(".host");
432
+ } else if (err.includes("connect ETIMEDOUT")) {
433
+ err = session.text(".timeout");
434
+ } else if (err.includes("Ping payload did not match received payload")) {
435
+ err = session.text(".fewData");
436
+ } else if (err.includes("Expected server to send packet type")) {
437
+ err = session.text(".fewData");
438
+ } else if (err.includes("getaddrinfo")) {
439
+ err = session.text(".dns");
440
+ }
441
+ const temp = {
442
+ count,
443
+ data: err,
444
+ note,
445
+ name: "MC"
446
+ };
447
+ log.error(`Server ${count}:`);
448
+ log.error(temp);
449
+ list = list + "\n" + session.text(isQQ ? ".listFailed-md" : ".listFailed", temp);
418
450
  }
419
- const temp = {
420
- "count": count,
421
- "data": err,
422
- "note": note,
423
- "name": "MC"
424
- };
425
- log.error(`Server ${count}:`);
426
- log.error(temp);
427
- list = list + "\n" + session.text(session?.bot.adapterName == "qq" ? ".listFailed-md" : ".listFailed", temp);
428
451
  }
429
452
  }
453
+ msg = {
454
+ time,
455
+ list,
456
+ success: 0
457
+ };
458
+ await this.sendMsg(isQQ ? ".msg-md" : ".msg", msg);
459
+ return 0;
460
+ } else {
461
+ msg = {
462
+ time,
463
+ error: session.text(".forbidden"),
464
+ success: 1
465
+ };
466
+ log.info("Sent:");
467
+ log.info(msg);
468
+ await this.sendFailed(msg);
469
+ return 1;
430
470
  }
431
- msg = {
432
- "time": time,
433
- "list": list,
434
- "success": 0
435
- };
436
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi2.h)("qq:markdown", {
437
- content: session?.text(".msg-md", msg)
438
- }) : session?.text(".msg", msg));
439
- return 0;
440
- } else {
441
- msg = {
442
- "time": time,
443
- "error": "此指令不允许在本群使用。",
444
- "success": 1
445
- };
446
- log.info("Sent:");
447
- log.info(msg);
448
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi2.h)("qq:markdown", {
449
- content: session?.text("failed-md", msg)
450
- }) : session?.text("failed", msg));
451
- return 1;
452
- }
453
- }
454
- __name(getServer, "getServer");
455
- async function getStatus(ctx, session) {
456
- const log = ctx.logger("status");
457
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
458
- const time = getHongKongTime();
459
- let msg;
460
- const vMsg = await getSystemUsage();
461
- if (vMsg["success"] == 1) {
462
- log.error(vMsg);
463
- msg = {
464
- "time": time,
465
- "data": vMsg["data"],
466
- "error": "状态获取失败。",
467
- "quote": import_koishi2.h.quote(session.messageId),
468
- "success": 1
469
- };
470
- } else {
471
- const msgCount = await getMsgCount(ctx);
472
- msg = {
473
- "time": time,
474
- "name": vMsg["name"],
475
- "cpu": vMsg["cpu"],
476
- "memory": vMsg["memory"],
477
- "online": formatTimestampDiff(
478
- Number((await ctx.database.get("botData", "uptime"))[0].data),
479
- Number(session.event.timestamp.toString().substring(0, 10))
480
- ),
481
- "msgCount": `${msgCount["receive"]}/${msgCount["send"]}`,
482
- "version": (await ctx.database.get("botData", "version"))[0].data,
483
- "success": 0
484
- };
485
471
  }
486
- log.debug("Sent:");
487
- log.debug(msg);
488
- return msg;
489
- }
490
- __name(getStatus, "getStatus");
491
- async function getRandom(ctx, session, min, max) {
492
- const log = ctx.logger("random");
493
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
494
- const time = getHongKongTime();
495
- let msg;
496
- let data;
497
- if (min == void 0 || max == void 0) {
498
- min = 0;
499
- max = 1e4;
500
- }
501
- min = Math.ceil(min);
502
- max = Math.floor(max);
503
- data = Math.floor(Math.random() * (max - min + 1)) + min + `(${min},${max})`;
504
- msg = {
505
- "time": time,
506
- "data": data,
507
- "success": 0
508
- };
509
- log.debug("Sent:");
510
- log.debug(msg);
511
- return msg;
512
- }
513
- __name(getRandom, "getRandom");
514
- async function getInfo(ctx, session) {
515
- const log = ctx.logger("info");
516
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
517
- const time = getHongKongTime();
518
- let msg;
519
- let data = await readInfo(ctx);
520
- if (typeof data == "string") {
521
- log.error("Error:", data);
522
- msg = {
523
- "time": time,
524
- "data": data,
525
- "error": "读取信息失败。",
526
- "quote": import_koishi2.h.quote(session.messageId),
527
- "success": 1
528
- };
529
- } else {
530
- msg = {
531
- "time": time,
532
- ...data,
533
- "success": 0
534
- };
535
- }
536
- log.debug("Sent:");
537
- log.debug(msg);
538
- return msg;
539
- }
540
- __name(getInfo, "getInfo");
541
- async function getRandomWord(ctx, session) {
542
- const log = ctx.logger("rw");
543
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
544
- let msg;
545
- const time = getHongKongTime();
546
- if (ctx.config.rwAPI == void 0) {
547
- msg = {
548
- "time": time,
549
- "error": session.text("noApi"),
550
- "quote": import_koishi2.h.quote(session.messageId),
551
- "success": 2
552
- };
553
- log.warn("Sent:");
554
- log.warn(msg);
472
+ // 指令 Status
473
+ async status() {
474
+ const { ctx, session, log, time } = this;
475
+ let msg;
476
+ const vMsg = await getSystemUsage();
477
+ if (vMsg["success"] == 1) {
478
+ log.error(vMsg);
479
+ msg = {
480
+ time,
481
+ data: vMsg["data"],
482
+ error: session.text(".error"),
483
+ quote: import_koishi2.h.quote(session.messageId),
484
+ success: 1
485
+ };
486
+ } else {
487
+ const msgCount = await getMsgCount(ctx);
488
+ msg = {
489
+ time,
490
+ name: vMsg["name"],
491
+ cpu: vMsg["cpu"],
492
+ memory: vMsg["memory"],
493
+ online: formatTimestampDiff(
494
+ Number((await ctx.database.get("botData", "uptime"))[0].data),
495
+ Number(session.event.timestamp.toString().substring(0, 10))
496
+ ),
497
+ msgCount: `${msgCount.receive}/${msgCount.send}`,
498
+ version: (await ctx.database.get("botData", "version"))[0].data,
499
+ success: 0
500
+ };
501
+ }
502
+ log.debug("Sent:");
503
+ log.debug(msg);
555
504
  return msg;
556
505
  }
557
- const response = await request(ctx.config.rwAPI + "?format=json", {}, ctx.config.timeout, log);
558
- if (response.success) {
559
- log.debug(response.data);
506
+ // 指令 Random
507
+ async random(min, max) {
508
+ const { log, time } = this;
509
+ let msg;
510
+ let data;
511
+ if (min == void 0 || max == void 0) {
512
+ min = 0;
513
+ max = 1e4;
514
+ }
515
+ min = Math.ceil(min);
516
+ max = Math.floor(max);
517
+ data = Math.floor(Math.random() * (max - min + 1)) + min + `(${min},${max})`;
560
518
  msg = {
561
- "time": time,
562
- "error": response.data.data,
563
- "quote": import_koishi2.h.quote(session.messageId),
564
- "success": 0
519
+ time,
520
+ data,
521
+ success: 0
565
522
  };
566
523
  log.debug("Sent:");
567
524
  log.debug(msg);
568
- } else {
569
- let err;
570
- if (response.code) {
571
- err = response.isJson ? response.error["data"] : response.error;
525
+ return msg;
526
+ }
527
+ // 指令 Info
528
+ async info() {
529
+ const { ctx, session, log, time } = this;
530
+ let msg;
531
+ let data = await readInfo(ctx);
532
+ if (typeof data == "string") {
533
+ log.error("Error:", data);
534
+ msg = {
535
+ time,
536
+ data,
537
+ error: session.text(".error"),
538
+ quote: import_koishi2.h.quote(session.messageId),
539
+ success: 1
540
+ };
572
541
  } else {
573
- err = response.error.message;
542
+ msg = {
543
+ time,
544
+ ...data,
545
+ success: 0
546
+ };
574
547
  }
575
- msg = {
576
- "time": time,
577
- "error": err,
578
- "quote": import_koishi2.h.quote(session.messageId),
579
- "success": 1
580
- };
581
- log.warn("Sent:");
582
- log.warn(msg);
583
- }
584
- return msg;
585
- }
586
- __name(getRandomWord, "getRandomWord");
587
- async function getBlueArchive(ctx, session) {
588
- const log = ctx.logger("ba");
589
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
590
- if (ctx.config.baAPI == void 0) {
591
- await session.send(session.text(".msg", { "quote": import_koishi2.h.quote(session.messageId), "image": session.text("noApi") }));
592
- return 1;
593
- }
594
- const ms = random(0, 0, 1500);
595
- const link = random(2, ctx.config.baAPI) + `?cacheBuster=${random(1, 1, 2147483647)}`;
596
- log.debug(`Link: ${link}`);
597
- await (0, import_koishi2.sleep)(ms);
598
- const status2 = await session.send(session.text(".msg", { "quote": import_koishi2.h.quote(session.messageId), "image": import_koishi2.h.image(link) }));
599
- if (!status2) await session.send(session.text(".msg", { "quote": import_koishi2.h.quote(session.messageId), "image": import_koishi2.h.image(link) }));
600
- return 0;
601
- }
602
- __name(getBlueArchive, "getBlueArchive");
603
- async function getCat(ctx, session) {
604
- const log = ctx.logger("cat");
605
- log.debug(`Got: {"form":"${session.platform}:${session.event.guild?.id}","user":"${session.event.user?.id}","timestamp":${session.event.timestamp},"messageId":"${session.event.message?.id}"}`);
606
- const time = getHongKongTime();
607
- if (ctx.config.catAPI == void 0) {
608
- await session.send(session.text(".failed", { "quote": import_koishi2.h.quote(session.messageId), "data": "未指定 API", "time": time }));
609
- log.warn("未指定 API");
610
- return 1;
611
- }
612
- const response = await request(ctx.config.catAPI, {}, ctx.config.timeout, log);
613
- if (response.success) {
614
- log.debug(response.data);
615
- const msg = { "quote": import_koishi2.h.quote(session.messageId), "image": import_koishi2.h.image(response.data[0].url) };
616
- await session.send(session.text(".msg", msg));
617
548
  log.debug("Sent:");
618
- log.debug(response.data[0].url);
619
- } else {
620
- if (response.code) {
621
- const msg = {
622
- "quote": import_koishi2.h.quote(session.messageId),
623
- "data": response.isJson ? "获取失败:" + response.error["error"] : "获取失败:" + response.error,
624
- "time": time
549
+ log.debug(msg);
550
+ return msg;
551
+ }
552
+ // 指令 RW
553
+ async randomWord() {
554
+ const { ctx, session, log, time } = this;
555
+ let msg;
556
+ if (ctx.config.rwAPI == void 0) {
557
+ msg = {
558
+ time,
559
+ error: session.text("noApi"),
560
+ quote: import_koishi2.h.quote(session.messageId),
561
+ success: 2
625
562
  };
626
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi2.h)("qq:markdown", {
627
- content: session?.text("failed-md", msg)
628
- }) : session?.text("failed", msg));
629
563
  log.warn("Sent:");
630
- log.warn(response.error);
564
+ log.warn(msg);
565
+ return msg;
566
+ }
567
+ const response = await request(
568
+ ctx.config.rwAPI + "?format=json",
569
+ ctx,
570
+ { method: "GET", timeout: ctx.config.timeout },
571
+ log
572
+ );
573
+ if (response.success) {
574
+ log.debug(response.data);
575
+ msg = {
576
+ time,
577
+ error: response.data.data,
578
+ quote: import_koishi2.h.quote(session.messageId),
579
+ success: 0
580
+ };
581
+ log.debug("Sent:");
582
+ log.debug(msg);
631
583
  } else {
632
- const msg = { "quote": import_koishi2.h.quote(session.messageId), "data": "获取失败:" + response.error.message, "time": time };
633
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi2.h)("qq:markdown", {
634
- content: session?.text("failed-md", msg)
635
- }) : session?.text("failed", msg));
584
+ let err;
585
+ if (!response.isError && response.isObj) {
586
+ err = response.error["data"];
587
+ } else if (response.isError) {
588
+ err = response.error.message;
589
+ } else {
590
+ err = response.error;
591
+ }
592
+ msg = {
593
+ time,
594
+ error: err,
595
+ quote: import_koishi2.h.quote(session.messageId),
596
+ success: 1
597
+ };
636
598
  log.warn("Sent:");
637
- log.warn(response.error);
599
+ log.warn(msg);
638
600
  }
601
+ return msg;
639
602
  }
640
- return 0;
641
- }
642
- __name(getCat, "getCat");
643
- async function getNewsMsg(ctx, type) {
644
- const log = ctx.logger("getNewsMsg");
645
- const response = await request(ctx.config.newsAPI, {}, ctx.config.timeout, log);
646
- if (response.success) {
647
- if ((await ctx.database.get("botData", "newsId"))[0]?.data == response.data.appnews.newsitems[0].gid && type != 1) {
648
- log.debug("无新闻");
649
- return { success: false, msg: "无可用新闻" };
603
+ // 指令 BA
604
+ async blueArchive() {
605
+ const { ctx, session, log } = this;
606
+ if (ctx.config.baAPI == void 0) {
607
+ await session.send(
608
+ session.text(".msg", { quote: import_koishi2.h.quote(session.messageId), image: session.text("noApi") })
609
+ );
610
+ return 1;
650
611
  }
651
- const db = await ctx.database.get("botData", response.data.appnews.newsitems[0].gid);
652
- let html = [];
653
- if (db[0]) {
654
- html[0] = db[0].data;
612
+ const ms = random(0, 0, 1500);
613
+ const link = random(2, ctx.config.baAPI) + `?cacheBuster=${random(1, 1, 2147483647)}`;
614
+ log.debug(`Link: ${link}`);
615
+ await (0, import_koishi2.sleep)(ms);
616
+ const status2 = await session.send(
617
+ session.text(".msg", { quote: import_koishi2.h.quote(session.messageId), image: import_koishi2.h.image(link) })
618
+ );
619
+ if (!status2)
620
+ await session.send(
621
+ session.text(".msg", { quote: import_koishi2.h.quote(session.messageId), image: import_koishi2.h.image(link) })
622
+ );
623
+ return 0;
624
+ }
625
+ // 指令 centerServerTest
626
+ async centerServerTest() {
627
+ const { ctx, session, log, time } = this;
628
+ let msg;
629
+ let list = "";
630
+ const timeFormatter = new Intl.DateTimeFormat("zh-CN", {
631
+ timeZone: "Asia/Shanghai",
632
+ year: "numeric",
633
+ month: "2-digit",
634
+ day: "2-digit",
635
+ hour: "2-digit",
636
+ minute: "2-digit",
637
+ second: "2-digit",
638
+ hour12: false
639
+ });
640
+ const response = await request(
641
+ "https://status.scpslgame.com/api/status-page/heartbeat/nw",
642
+ ctx,
643
+ { method: "GET", timeout: ctx.config.timeout },
644
+ log
645
+ );
646
+ if (response.success) {
647
+ log.debug(response.data);
648
+ for (const server of ctx.config.slTest) {
649
+ const lastTime = response.data.heartbeatList[server.id].at(-1);
650
+ if (lastTime) {
651
+ const uptime24 = (response.data.uptimeList[server.id + "_24"] * 100).toFixed(2) + "%";
652
+ const status2 = lastTime?.status == 1 ? session.text(".statusLive") : session.text(".statusDie");
653
+ const testTime = timeFormatter.format(/* @__PURE__ */ new Date(lastTime?.time.replace(" ", "T") + "Z"));
654
+ list = list + "\n" + session.text(this.isQQ ? ".list-md" : ".list", {
655
+ name: server.name,
656
+ status: status2,
657
+ uptime: uptime24,
658
+ time: testTime
659
+ });
660
+ } else {
661
+ list = list + "\n" + session.text(this.isQQ ? ".listFailed-md" : ".listFailed", {
662
+ name: server.name,
663
+ data: session.text(".dataFail")
664
+ });
665
+ }
666
+ }
667
+ msg = {
668
+ data: { list, time },
669
+ success: ".msg"
670
+ };
671
+ await this.sendMsg(".msg", msg.data);
672
+ log.debug("Sent:");
673
+ log.debug(msg);
674
+ return 0;
655
675
  } else {
656
- html = await readNewsFile(response.data, log);
657
- if (html[1]) return { success: false, msg: `渲染图片失败` };
658
- await ctx.database.upsert("botData", [
659
- { id: response.data.appnews.newsitems[0].gid, data: html[0] }
660
- ]);
676
+ let err;
677
+ if (!response.isError && response.isObj) {
678
+ err = response.error["data"];
679
+ } else if (response.isError) {
680
+ err = response.error.message;
681
+ } else {
682
+ err = response.error;
683
+ }
684
+ msg = {
685
+ data: { error: "查看失败:" + err, time },
686
+ success: ".failed"
687
+ };
688
+ await this.sendFailed(msg.data);
689
+ log.warn("Sent:");
690
+ log.warn(msg);
691
+ return 1;
661
692
  }
662
- log.debug(html);
663
- const page = await ctx.puppeteer.page();
664
- try {
665
- await page.setViewport({
666
- width: 800,
667
- height: 800,
668
- deviceScaleFactor: 2
669
- });
670
- await page.setContent(html[0], { timeout: ctx.config.htmlTimeout, waitUntil: "networkidle0" });
671
- const { width, height } = await page.evaluate(() => ({
672
- width: document.body.scrollWidth,
673
- height: document.body.scrollHeight
674
- }));
675
- await page.setViewport({ width, height, deviceScaleFactor: 2 });
676
- const image = await page.screenshot({
677
- type: "png",
678
- fullPage: true,
679
- omitBackground: true
680
- // 使得 CSS 中未定义的背景部分透明
681
- });
682
- if (type == 0) await ctx.database.upsert("botData", [
683
- { id: "newsId", data: response.data.appnews.newsitems[0].gid }
684
- ]);
685
- return { success: true, data: image, msg: "NorthWood 发布了一个新闻(原文+机翻):" + response.data.appnews.newsitems[0].title };
686
- } catch (err) {
687
- log.error("图片渲染失败:", err);
688
- return { success: false, msg: "图片渲染失败" };
689
- } finally {
690
- if (page && !page.isClosed()) await page.close();
693
+ }
694
+ // 指令 Cat
695
+ async cat() {
696
+ const { ctx, session, log, time } = this;
697
+ if (ctx.config.catAPI == void 0) {
698
+ await session.send(
699
+ session.text(".failed", {
700
+ quote: import_koishi2.h.quote(session.messageId),
701
+ data: session.text("noApi"),
702
+ time
703
+ })
704
+ );
705
+ log.warn("未指定 API");
706
+ return 1;
707
+ }
708
+ const response = await request(
709
+ ctx.config.catAPI,
710
+ ctx,
711
+ { method: "GET", timeout: ctx.config.timeout },
712
+ log
713
+ );
714
+ if (response.success) {
715
+ log.debug(response.data);
716
+ const msg = { quote: import_koishi2.h.quote(session.messageId), image: import_koishi2.h.image(response.data[0].url) };
717
+ await session.send(session.text(".msg", msg));
718
+ log.debug("Sent:");
719
+ log.debug(response.data[0].url);
720
+ } else {
721
+ if (!response.isError) {
722
+ const msg = {
723
+ quote: import_koishi2.h.quote(session.messageId),
724
+ data: response.isObj ? "获取失败:" + response.error["error"] : "获取失败:" + response.error,
725
+ time
726
+ };
727
+ await this.sendFailed(msg);
728
+ log.warn("Sent:");
729
+ log.warn(response.error);
730
+ } else {
731
+ const msg = {
732
+ quote: import_koishi2.h.quote(session.messageId),
733
+ data: "获取失败:" + response.error?.message,
734
+ time
735
+ };
736
+ await this.sendFailed(msg);
737
+ log.warn("Sent:");
738
+ log.warn(response.error);
739
+ }
740
+ }
741
+ return 0;
742
+ }
743
+ // 定时任务 SL News(静态方法,无 session)
744
+ static async getNewsMsg(ctx, type) {
745
+ const log = ctx.logger("getNewsMsg");
746
+ const results = [];
747
+ const latestIds = [];
748
+ const storedIds = (await ctx.database.get("botData", "newsId"))[0]?.data?.split(",") ?? [];
749
+ for (const item of ctx.config.newsAPI) {
750
+ const rssUrl = `https://store.steampowered.com/feeds/news/app/${item.appUrl}`;
751
+ log.info(`获取新闻: ${item.name} -> ${rssUrl}`);
752
+ const response = await request(
753
+ rssUrl,
754
+ ctx,
755
+ { method: "GET", timeout: ctx.config.timeout },
756
+ log
757
+ );
758
+ if (!response.success) {
759
+ log.error(`请求失败: ${item.name}`);
760
+ results.push({ success: false, msg: `请求 ${item.name} 新闻失败` });
761
+ continue;
762
+ }
763
+ const parsed = await parseNewsRssToHtml(response.data, log);
764
+ if (parsed.error || !parsed.guid) {
765
+ log.error(`解析失败: ${item.name}`, parsed.error);
766
+ results.push({ success: false, msg: `解析 ${item.name} 新闻失败` });
767
+ continue;
768
+ }
769
+ latestIds.push(parsed.guid);
770
+ if (storedIds.includes(parsed.guid) && type != 1) {
771
+ log.debug(`无新闻更新: ${item.name}`);
772
+ continue;
773
+ }
774
+ const db = await ctx.database.get("botData", parsed.guid);
775
+ let html;
776
+ if (db[0]) {
777
+ html = db[0].data;
778
+ } else {
779
+ html = parsed.data;
780
+ await ctx.database.upsert("botData", [{ id: parsed.guid, data: html }]);
781
+ }
782
+ const page = await ctx.puppeteer.page();
783
+ try {
784
+ await page.setViewport({
785
+ width: 800,
786
+ height: 800,
787
+ deviceScaleFactor: 2
788
+ });
789
+ await page.setContent(html, {
790
+ timeout: ctx.config.htmlTimeout,
791
+ waitUntil: "networkidle0"
792
+ });
793
+ const { width, height } = await page.evaluate(() => ({
794
+ width: document.body.scrollWidth,
795
+ height: document.body.scrollHeight
796
+ }));
797
+ await page.setViewport({ width, height, deviceScaleFactor: 2 });
798
+ const image = await page.screenshot({
799
+ type: "png",
800
+ fullPage: true,
801
+ omitBackground: true
802
+ });
803
+ results.push({
804
+ success: true,
805
+ data: image,
806
+ msg: `抓取到了新的 Steam 新闻!${item.name} - ${parsed.guid}`
807
+ });
808
+ } catch (err) {
809
+ log.error(`${item.name} 图片渲染失败:`, err);
810
+ results.push({ success: false, msg: `${item.name} 图片渲染失败` });
811
+ } finally {
812
+ if (page && !page.isClosed()) await page.close();
813
+ }
691
814
  }
692
- } else {
693
- return { success: false, msg: "请求 Steam API 失败" };
815
+ if (type == 0 && latestIds.length > 0) {
816
+ await ctx.database.upsert("botData", [{ id: "newsId", data: latestIds.join(",") }]);
817
+ }
818
+ return results;
694
819
  }
695
- }
696
- __name(getNewsMsg, "getNewsMsg");
820
+ };
697
821
 
698
822
  // package.json
699
- var version = "2.31.0";
823
+ var version = "3.0.1";
700
824
 
701
- // src/index.ts
702
- var inject = ["database", "installer", "puppeteer", "cron"];
825
+ // src/config.ts
826
+ var import_koishi3 = require("koishi");
703
827
  var name = "node-async-bot-all";
704
828
  var usage = "这是一个私有插件。";
705
829
  var Config = import_koishi3.Schema.intersect([
@@ -731,154 +855,240 @@ var Config = import_koishi3.Schema.intersect([
731
855
  catAPI: import_koishi3.Schema.string().default("https://api.thecatapi.com/v1/images/search").description("随机猫猫图 API")
732
856
  }).description("随机猫猫图"),
733
857
  import_koishi3.Schema.object({
734
- slNews: import_koishi3.Schema.array(String).default([""]).description("{platform}:{channelId}"),
735
- newsAPI: import_koishi3.Schema.string().default("https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid=700330&count=1").description("新闻 API")
736
- }).description("SL新闻列表"),
858
+ steamNews: import_koishi3.Schema.array(String).default([""]).description("{platform}:{channelId}"),
859
+ newsAPI: import_koishi3.Schema.array(
860
+ import_koishi3.Schema.object({
861
+ name: import_koishi3.Schema.string().description("新闻抓取的 App 名称,随便写"),
862
+ appUrl: import_koishi3.Schema.string().description(
863
+ "新闻抓取的 AppId URL,可加参数,如 3629270/?l=schinese"
864
+ )
865
+ })
866
+ ).default([{ name: "SCP SL", appUrl: "700330/?l=schinese" }]).description("新闻抓取配置")
867
+ }).description("Steam 新闻抓取列表"),
737
868
  import_koishi3.Schema.object({
738
869
  specialMsg: import_koishi3.Schema.array(String).default([]).description("特殊消息"),
739
870
  reactionId: import_koishi3.Schema.array(Number).default([]).description("回应表情 ID")
740
871
  }).description("特殊消息回应")
741
872
  ]).description("基础设置");
742
- async function startReaction(session) {
743
- if (session.bot.createReaction) await session.bot.createReaction(session.channelId, session.messageId, `face|424`);
744
- }
745
- __name(startReaction, "startReaction");
746
- async function endReaction(session) {
747
- if (session.bot.deleteReaction) await session.bot.deleteReaction(session.channelId, session.messageId, `face|424`);
748
- if (session.bot.createReaction) await session.bot.createReaction(session.channelId, session.messageId, `face|144`);
749
- }
750
- __name(endReaction, "endReaction");
751
- async function endReactionFailed(session) {
752
- if (session.bot.deleteReaction) await session.bot.deleteReaction(session.channelId, session.messageId, `face|424`);
753
- if (session.bot.createReaction) await session.bot.createReaction(session.channelId, session.messageId, `face|38`);
754
- }
755
- __name(endReactionFailed, "endReactionFailed");
873
+
874
+ // src/index.ts
875
+ var inject = ["database", "installer", "puppeteer", "cron"];
756
876
  function apply(ctx) {
757
877
  ctx.i18n.define("zh-CN", require_zh_CN());
758
878
  ctx.on("ready", async () => {
759
- ctx.model.extend("botData", {
879
+ const naBot = new NodeAsyncBot();
880
+ await naBot.init(ctx);
881
+ await naBot.registerNews();
882
+ await naBot.registerCommand();
883
+ });
884
+ }
885
+ __name(apply, "apply");
886
+ var NodeAsyncBot = class {
887
+ static {
888
+ __name(this, "NodeAsyncBot");
889
+ }
890
+ _ctx;
891
+ _botData;
892
+ _na;
893
+ _registeredNews = false;
894
+ _registeredCommand = false;
895
+ get ctx() {
896
+ if (!this._ctx) throw new Error("未初始化");
897
+ return this._ctx;
898
+ }
899
+ set ctx(value) {
900
+ this._ctx = value;
901
+ }
902
+ get botData() {
903
+ if (!this._botData) throw new Error("未初始化");
904
+ return this._botData;
905
+ }
906
+ set botData(value) {
907
+ this._botData = value;
908
+ }
909
+ get na() {
910
+ if (!this._na) throw new Error("未初始化");
911
+ return this._na;
912
+ }
913
+ set na(value) {
914
+ this._na = value;
915
+ }
916
+ async init(ct) {
917
+ this.ctx = ct;
918
+ const date = /* @__PURE__ */ new Date();
919
+ this.botData = {
920
+ version,
921
+ uptime: date.getTime().toString().substring(0, 10)
922
+ };
923
+ this.ctx.model.extend("botData", {
760
924
  // 向表中注入字符串
761
925
  id: "string",
762
926
  data: "string"
763
927
  });
764
- const date = /* @__PURE__ */ new Date();
765
- await ctx.database.upsert("botData", [
766
- { id: "uptime", data: date.getTime().toString().substring(0, 10) },
767
- { id: "version", data: version }
928
+ await this.ctx.database.upsert("botData", [
929
+ { id: "uptime", data: this.botData.uptime },
930
+ { id: "version", data: this.botData.version }
768
931
  ]);
769
- });
770
- const na = ctx.command("na");
771
- na.subcommand("slnews").action(async () => {
772
- const log = ctx.logger("slnews");
773
- const outMsg = await getNewsMsg(ctx, 1);
774
- if (outMsg.data) {
775
- return `${outMsg.msg}
776
- ${import_koishi3.h.image(outMsg.data, "image/png")}`;
777
- } else {
778
- log.warn(outMsg);
779
- return outMsg.msg;
780
- }
781
- });
782
- ctx.cron("0 * * * *", async () => {
783
- ctx.emit("node-async/news");
784
- });
785
- ctx.cron("30 * * * *", async () => {
786
- ctx.emit("node-async/news");
787
- });
788
- ctx.on("node-async/news", async () => {
789
- const outMsg = await getNewsMsg(ctx, 0);
790
- if (outMsg.data) {
791
- await ctx.broadcast(ctx.config.slNews, `${outMsg.msg}
792
- ${import_koishi3.h.image(outMsg.data, "image/png")}`);
932
+ this.na = this.ctx.command("na");
933
+ }
934
+ // reaction 辅助方法
935
+ async startReaction(session) {
936
+ if (session.bot.createReaction)
937
+ await session.bot.createReaction(
938
+ session.channelId,
939
+ session.messageId,
940
+ `face|424`
941
+ );
942
+ }
943
+ async endReaction(session) {
944
+ if (session.bot.deleteReaction)
945
+ await session.bot.deleteReaction(
946
+ session.channelId,
947
+ session.messageId,
948
+ `face|424`
949
+ );
950
+ if (session.bot.createReaction)
951
+ await session.bot.createReaction(
952
+ session.channelId,
953
+ session.messageId,
954
+ `face|144`
955
+ );
956
+ }
957
+ async endReactionFailed(session) {
958
+ if (session.bot.deleteReaction)
959
+ await session.bot.deleteReaction(
960
+ session.channelId,
961
+ session.messageId,
962
+ `face|424`
963
+ );
964
+ if (session.bot.createReaction)
965
+ await session.bot.createReaction(
966
+ session.channelId,
967
+ session.messageId,
968
+ `face|38`
969
+ );
970
+ }
971
+ // 执行指令的通用流程:reaction + 命令执行
972
+ async execCommand(session, loggerName, fn) {
973
+ const handler = new CommandHandler(this.ctx, session, loggerName);
974
+ await this.startReaction(session);
975
+ const result = await fn(handler);
976
+ if (result === 0 || result === void 0) {
977
+ await this.endReaction(session);
793
978
  } else {
794
- if (outMsg.msg == "无可用新闻") return;
795
- await ctx.broadcast(ctx.config.slNews, outMsg.msg);
979
+ await this.endReactionFailed(session);
796
980
  }
797
- });
798
- ctx.on("message", async (session) => {
799
- for (const content of ctx.config.specialMsg) {
800
- if (session.content === content) {
801
- await session.bot.createReaction(session.channelId, session.messageId, `face|${String(random(2, ctx.config.reactionId))}`);
981
+ }
982
+ async registerNews() {
983
+ if (this._registeredNews) return;
984
+ this._registeredNews = true;
985
+ this.na.subcommand("steamNews").action(async ({ session }) => {
986
+ const log = this.ctx.logger("steamNews");
987
+ const results = await CommandHandler.getNewsMsg(this.ctx, 1);
988
+ for (const outMsg of results) {
989
+ if (outMsg.data) {
990
+ await session?.send(`${outMsg.msg}
991
+ ${import_koishi4.h.image(outMsg.data, "image/png")}`);
992
+ } else {
993
+ log.error(outMsg);
994
+ await session?.send(outMsg.msg);
995
+ }
802
996
  }
803
- }
804
- });
805
- na.subcommand("cxGame").action(async ({ session }) => {
806
- await startReaction(session);
807
- const cx = await getServer(ctx, session);
808
- if (cx == 0) {
809
- await endReaction(session);
810
- } else {
811
- await endReactionFailed(session);
812
- }
813
- });
814
- na.subcommand("status").alias("stats").alias("状态").action(async ({ session }) => {
815
- await startReaction(session);
816
- const status2 = await getStatus(ctx, session);
817
- if (status2["success"] == 0) {
818
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
819
- content: session?.text(".msg-md", status2)
820
- }) : session?.text(".msg", status2));
821
- await endReaction(session);
822
- } else {
823
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
824
- content: session?.text("failed-md", status2)
825
- }) : session?.text("failed", status2));
826
- await endReactionFailed(session);
827
- }
828
- });
829
- na.subcommand("random [最小数:number] [最大数:number]").alias("随机数").action(async ({ session }, min, max) => {
830
- await startReaction(session);
831
- const random2 = await getRandom(ctx, session, min, max);
832
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
833
- content: session?.text(".msg-md", random2)
834
- }) : session?.text(".msg", random2));
835
- await endReaction(session);
836
- });
837
- na.subcommand("info").action(async ({ session }) => {
838
- await startReaction(session);
839
- const info = await getInfo(ctx, session);
840
- if (info["success"] == 0) {
841
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
842
- content: session?.text(".msg-md", info)
843
- }) : session?.text(".msg", info));
844
- await endReaction(session);
845
- } else {
846
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
847
- content: session?.text("failed-md", info)
848
- }) : session?.text("failed", info));
849
- await endReactionFailed(session);
850
- }
851
- });
852
- na.subcommand("rw").action(async ({ session }) => {
853
- await startReaction(session);
854
- const rw = await getRandomWord(ctx, session);
855
- if (rw["success"] == 0) {
856
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
857
- content: session?.text("failed-md", rw)
858
- }) : session?.text("failed", rw));
859
- await endReaction(session);
860
- } else {
861
- await session?.send(session?.bot.adapterName == "qq" ? (0, import_koishi3.h)("qq:markdown", {
862
- content: session?.text("failed-md", rw)
863
- }) : session?.text("failed", rw));
864
- await endReactionFailed(session);
865
- }
866
- });
867
- na.subcommand("randomBA").alias("随机ba图").action(async ({ session }) => {
868
- await startReaction(session);
869
- await getBlueArchive(ctx, session);
870
- await endReaction(session);
871
- });
872
- na.subcommand("randomCat").alias("随机猫猫图").alias("随机猫猫").action(async ({ session }) => {
873
- await startReaction(session);
874
- await getCat(ctx, session);
875
- await endReaction(session);
876
- });
877
- }
878
- __name(apply, "apply");
997
+ });
998
+ this.ctx.cron("0 * * * *", async () => {
999
+ this.ctx.emit("node-async/news");
1000
+ });
1001
+ this.ctx.cron("30 * * * *", async () => {
1002
+ this.ctx.emit("node-async/news");
1003
+ });
1004
+ this.ctx.on("node-async/news", async () => {
1005
+ const results = await CommandHandler.getNewsMsg(this.ctx, 0);
1006
+ if (results.length === 0) return;
1007
+ for (const outMsg of results) {
1008
+ if (outMsg.data) {
1009
+ await this.ctx.broadcast(
1010
+ this.ctx.config.steamNews,
1011
+ `${outMsg.msg}
1012
+ ${import_koishi4.h.image(outMsg.data, "image/png")}`
1013
+ );
1014
+ }
1015
+ }
1016
+ });
1017
+ this.ctx.on("message", async (session) => {
1018
+ if (session.bot.createReaction) {
1019
+ for (const content of this.ctx.config.specialMsg) {
1020
+ if (session.content === content) {
1021
+ await session.bot.createReaction(
1022
+ session.channelId,
1023
+ session.messageId,
1024
+ `face|${String(random(2, this.ctx.config.reactionId))}`
1025
+ );
1026
+ }
1027
+ }
1028
+ }
1029
+ });
1030
+ }
1031
+ async registerCommand() {
1032
+ if (this._registeredCommand) return;
1033
+ this._registeredCommand = true;
1034
+ this.na.subcommand("cxGame").action(async ({ session }) => {
1035
+ await this.execCommand(session, "cx", (handler) => handler.server());
1036
+ });
1037
+ this.na.subcommand("status").alias("stats").alias("状态").action(async ({ session }) => {
1038
+ await this.execCommand(session, "status", async (handler) => {
1039
+ const status2 = await handler.status();
1040
+ await session?.send(
1041
+ session?.bot.adapterName == "qq" ? (0, import_koishi4.h)("qq:markdown", {
1042
+ content: session?.text(status2["success"] == 0 ? ".msg-md" : "failed-md", status2)
1043
+ }) : session?.text(status2["success"] == 0 ? ".msg" : "failed", status2)
1044
+ );
1045
+ return status2["success"] == 0 ? 0 : 1;
1046
+ });
1047
+ });
1048
+ this.na.subcommand("random [最小数:number] [最大数:number]").alias("随机数").action(async ({ session }, min, max) => {
1049
+ await this.execCommand(session, "random", async (handler) => {
1050
+ const random2 = await handler.random(min, max);
1051
+ await session?.send(
1052
+ session?.bot.adapterName == "qq" ? (0, import_koishi4.h)("qq:markdown", {
1053
+ content: session?.text(".msg-md", random2)
1054
+ }) : session?.text(".msg", random2)
1055
+ );
1056
+ });
1057
+ });
1058
+ this.na.subcommand("info").action(async ({ session }) => {
1059
+ await this.execCommand(session, "info", async (handler) => {
1060
+ const info = await handler.info();
1061
+ await session?.send(
1062
+ session?.bot.adapterName == "qq" ? (0, import_koishi4.h)("qq:markdown", {
1063
+ content: session?.text(info["success"] == 0 ? ".msg-md" : "failed-md", info)
1064
+ }) : session?.text(info["success"] == 0 ? ".msg" : "failed", info)
1065
+ );
1066
+ return info["success"] == 0 ? 0 : 1;
1067
+ });
1068
+ });
1069
+ this.na.subcommand("rw").action(async ({ session }) => {
1070
+ await this.execCommand(session, "rw", async (handler) => {
1071
+ const rw = await handler.randomWord();
1072
+ await session?.send(
1073
+ session?.bot.adapterName == "qq" ? (0, import_koishi4.h)("qq:markdown", {
1074
+ content: session?.text(rw["success"] == 0 ? "failed-md" : "failed-md", rw)
1075
+ }) : session?.text(rw["success"] == 0 ? "failed" : "failed", rw)
1076
+ );
1077
+ return rw["success"] == 0 ? 0 : 1;
1078
+ });
1079
+ });
1080
+ this.na.subcommand("randomBA").alias("随机ba图").action(async ({ session }) => {
1081
+ await this.execCommand(session, "ba", (handler) => handler.blueArchive());
1082
+ });
1083
+ this.na.subcommand("randomCat").alias("随机猫猫图").alias("随机猫猫").action(async ({ session }) => {
1084
+ await this.execCommand(session, "cat", (handler) => handler.cat());
1085
+ });
1086
+ }
1087
+ };
879
1088
  // Annotate the CommonJS export names for ESM import in node:
880
1089
  0 && (module.exports = {
881
1090
  Config,
1091
+ NodeAsyncBot,
882
1092
  apply,
883
1093
  inject,
884
1094
  name,