@karinjs/plugin-basic 1.2.1 → 1.3.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.
@@ -0,0 +1,6 @@
1
+ import * as node_karin from 'node-karin';
2
+
3
+ declare const logViewer: node_karin.Command<keyof node_karin.MessageEventMap>;
4
+ declare const errorLogViewer: node_karin.Command<keyof node_karin.MessageEventMap>;
5
+
6
+ export { errorLogViewer, logViewer };
@@ -0,0 +1,229 @@
1
+ import {
2
+ render
3
+ } from "../chunk-QUWYQJAF.js";
4
+
5
+ // src/apps/logger.ts
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { karin, logger } from "node-karin";
9
+ var ansiToHtml = (text) => {
10
+ if (!text) return "";
11
+ const ESC = "\x1B";
12
+ const ansiRegex = new RegExp(`${ESC}\\[([0-9;]+)m`, "g");
13
+ const escapeHtml = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14
+ let result = "";
15
+ let lastIndex = 0;
16
+ let openSpan = "";
17
+ const colorMap = {
18
+ 30: "#020617",
19
+ 31: "#ef4444",
20
+ 32: "#22c55e",
21
+ 33: "#eab308",
22
+ 34: "#3b82f6",
23
+ 35: "#ec4899",
24
+ 36: "#06b6d4",
25
+ 37: "#e5e7eb",
26
+ 90: "#6b7280",
27
+ 91: "#f97373",
28
+ 92: "#4ade80",
29
+ 93: "#fde047",
30
+ 94: "#60a5fa",
31
+ 95: "#a855f7",
32
+ 96: "#38bdf8",
33
+ 97: "#f9fafb"
34
+ };
35
+ text.replace(ansiRegex, (match, codesStr, offset) => {
36
+ const chunk = text.slice(lastIndex, offset);
37
+ if (chunk) {
38
+ result += escapeHtml(chunk);
39
+ }
40
+ lastIndex = offset + match.length;
41
+ const codes = codesStr.split(";").map((n) => Number(n) || 0);
42
+ if (openSpan) {
43
+ result += "</span>";
44
+ openSpan = "";
45
+ }
46
+ if (codes.includes(0) || codes.includes(39)) {
47
+ return "";
48
+ }
49
+ if (codes[0] === 38 && codes[1] === 2 && codes.length >= 5) {
50
+ const r = codes[2];
51
+ const g = codes[3];
52
+ const b = codes[4];
53
+ openSpan = `color: rgb(${r}, ${g}, ${b})`;
54
+ result += `<span style="${openSpan}">`;
55
+ return "";
56
+ }
57
+ const colorCode = codes.find((c) => colorMap[c]);
58
+ if (colorCode !== void 0) {
59
+ const color = colorMap[colorCode];
60
+ openSpan = `color: ${color}`;
61
+ result += `<span style="${openSpan}">`;
62
+ return "";
63
+ }
64
+ return "";
65
+ });
66
+ const tail = text.slice(lastIndex);
67
+ if (tail) {
68
+ result += escapeHtml(tail);
69
+ }
70
+ if (openSpan) {
71
+ result += "</span>";
72
+ }
73
+ return result;
74
+ };
75
+ var trimEmptyLines = (text) => {
76
+ const lines = text.split("\n");
77
+ while (lines.length && !lines[0].trim()) lines.shift();
78
+ while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
79
+ return lines.join("\n");
80
+ };
81
+ var groupLogLines = (lines) => {
82
+ const groups = [];
83
+ let current = [];
84
+ const isNewEntry = (line) => {
85
+ const trimmed = line.trim();
86
+ if (!trimmed) return false;
87
+ return /^\[\d{2}:\d{2}:\d{2}\.\d{3}\]\[[A-Z]+]/.test(trimmed);
88
+ };
89
+ for (const line of lines) {
90
+ if (isNewEntry(line)) {
91
+ if (current.length) groups.push(current);
92
+ current = [line];
93
+ } else {
94
+ if (current.length) {
95
+ current.push(line);
96
+ } else {
97
+ current = [line];
98
+ }
99
+ }
100
+ }
101
+ if (current.length) groups.push(current);
102
+ return groups.map((item) => item.join("\n"));
103
+ };
104
+ var getTodayLogs = async (limit = 50) => {
105
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
106
+ const logDir = path.join(process.cwd(), "@karinjs", "logs");
107
+ const logFile = path.join(logDir, `logger.${today}.log`);
108
+ try {
109
+ if (!fs.existsSync(logFile)) {
110
+ const files = fs.readdirSync(logDir).filter((f) => f.startsWith("logger.") && f.endsWith(".log") && !f.includes("error")).sort().reverse();
111
+ if (files.length === 0) {
112
+ return ["\u6682\u65E0\u65E5\u5FD7\u8BB0\u5F55"];
113
+ }
114
+ const latestLog = path.join(logDir, files[0]);
115
+ const content2 = fs.readFileSync(latestLog, "utf-8");
116
+ const lines2 = content2.split("\n").filter((line) => line.trim());
117
+ const groups2 = groupLogLines(lines2);
118
+ return groups2.slice(-limit).reverse();
119
+ }
120
+ const content = fs.readFileSync(logFile, "utf-8");
121
+ const lines = content.split("\n").filter((line) => line.trim());
122
+ const groups = groupLogLines(lines);
123
+ return groups.slice(-limit).reverse();
124
+ } catch (error) {
125
+ logger.error("\u8BFB\u53D6\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
126
+ return ["\u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25"];
127
+ }
128
+ };
129
+ var getErrorLogs = async (limit = 50) => {
130
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
131
+ const errorDir = path.join(process.cwd(), "@karinjs", "logs", "error");
132
+ if (!fs.existsSync(errorDir)) {
133
+ return [];
134
+ }
135
+ let logFile = null;
136
+ try {
137
+ const todayLoggerError = path.join(errorDir, `logger.error.${today}.log`);
138
+ const todayError = path.join(errorDir, `error.${today}.log`);
139
+ if (fs.existsSync(todayLoggerError)) {
140
+ logFile = todayLoggerError;
141
+ } else if (fs.existsSync(todayError)) {
142
+ logFile = todayError;
143
+ } else {
144
+ const files = fs.readdirSync(errorDir).filter((f) => f.endsWith(".log")).sort().reverse();
145
+ if (files.length) {
146
+ logFile = path.join(errorDir, files[0]);
147
+ }
148
+ }
149
+ if (!logFile) return [];
150
+ const content = fs.readFileSync(logFile, "utf-8");
151
+ const lines = content.split("\n").filter((line) => line.trim());
152
+ const groups = groupLogLines(lines);
153
+ return groups.slice(-limit).reverse();
154
+ } catch (error) {
155
+ logger.error("\u8BFB\u53D6\u9519\u8BEF\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
156
+ return [];
157
+ }
158
+ };
159
+ var parseLogLine = (line) => {
160
+ const [firstLine, ...rest] = line.split("\n");
161
+ let time = "";
162
+ let levelRaw = "INFO";
163
+ const timeMatch = firstLine.match(/^\[(\d{2}:\d{2}:\d{2}\.\d{3})]/);
164
+ const levelMatch = firstLine.match(/\[([A-Z]+)]/);
165
+ if (timeMatch) time = timeMatch[1];
166
+ if (levelMatch) levelRaw = levelMatch[1];
167
+ const levelKey = levelRaw.toLowerCase();
168
+ const level = levelKey === "erro" ? "error" : levelKey;
169
+ const messageHead = firstLine.replace(/^\[\d{2}:\d{2}:\d{2}\.\d{3}]\[[A-Z]+]\s*/, "");
170
+ const messageTail = rest.length ? "\n" + rest.join("\n") : "";
171
+ const fullMessage = trimEmptyLines(`${messageHead}${messageTail}`);
172
+ const plainMessage = fullMessage.replace(/\u001b\[[0-9;]+m/g, "");
173
+ return {
174
+ time,
175
+ level,
176
+ message: plainMessage,
177
+ messageHtml: ansiToHtml(fullMessage)
178
+ };
179
+ };
180
+ var logViewer = karin.command(/^#日志\s*(\d+)?$/, async (e) => {
181
+ const match = e.msg.match(/^#日志\s*(\d+)?$/);
182
+ let limit = match && match[1] ? Number(match[1]) : 50;
183
+ if (!Number.isFinite(limit) || limit <= 0) limit = 50;
184
+ if (limit > 200) limit = 200;
185
+ const logs = await getTodayLogs(limit);
186
+ const parsedLogs = logs.map(parseLogLine);
187
+ try {
188
+ const img = await render("logger/index", {
189
+ logs: parsedLogs,
190
+ total: parsedLogs.length,
191
+ date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
192
+ });
193
+ await e.reply(img);
194
+ return true;
195
+ } catch (error) {
196
+ logger.error("\u6E32\u67D3\u65E5\u5FD7\u5931\u8D25:", error);
197
+ await e.reply("\u65E5\u5FD7\u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u9519\u8BEF\u4FE1\u606F");
198
+ return false;
199
+ }
200
+ }, { name: "\u65E5\u5FD7\u67E5\u770B\u5668", perm: "admin" });
201
+ var errorLogViewer = karin.command(/^#错误日志\s*(\d+)?$/, async (e) => {
202
+ const match = e.msg.match(/^#错误日志\s*(\d+)?$/);
203
+ let limit = match && match[1] ? Number(match[1]) : 50;
204
+ if (!Number.isFinite(limit) || limit <= 0) limit = 50;
205
+ if (limit > 200) limit = 200;
206
+ const logs = await getErrorLogs(limit);
207
+ if (!logs.length) {
208
+ await e.reply("\u6682\u65E0\u9519\u8BEF\u65E5\u5FD7");
209
+ return true;
210
+ }
211
+ const parsedLogs = logs.map(parseLogLine);
212
+ try {
213
+ const img = await render("logger/index", {
214
+ logs: parsedLogs,
215
+ total: parsedLogs.length,
216
+ date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
217
+ });
218
+ await e.reply(img);
219
+ return true;
220
+ } catch (error) {
221
+ logger.error("\u6E32\u67D3\u9519\u8BEF\u65E5\u5FD7\u5931\u8D25:", error);
222
+ await e.reply("\u9519\u8BEF\u65E5\u5FD7\u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u9519\u8BEF\u4FE1\u606F");
223
+ return false;
224
+ }
225
+ }, { name: "\u9519\u8BEF\u65E5\u5FD7\u67E5\u770B\u5668", perm: "admin" });
226
+ export {
227
+ errorLogViewer,
228
+ logViewer
229
+ };
@@ -1,6 +1,7 @@
1
1
  import {
2
- config
3
- } from "../chunk-MNDJRJH2.js";
2
+ cfg
3
+ } from "../chunk-PHDIUMOH.js";
4
+ import "../chunk-QUWYQJAF.js";
4
5
 
5
6
  // src/apps/login.ts
6
7
  import karin, { common, contactFriend, logger, segment } from "node-karin";
@@ -9,7 +10,7 @@ import os from "os";
9
10
  var url = { IPv4: ["https://4.ipw.cn"], IPv6: ["https://6.ipw.cn"] };
10
11
  var login = karin.command(/#?(面板|web)登录$/i, async (e) => {
11
12
  const net = os.networkInterfaces();
12
- const cfg = config();
13
+ const config = cfg.get();
13
14
  const IP = { lan: { ipv4: null, ipv6: null }, net: { ipv4: null, ipv6: null } };
14
15
  for (const i in net) {
15
16
  for (const iface of net[i]) {
@@ -32,7 +33,7 @@ var login = karin.command(/#?(面板|web)登录$/i, async (e) => {
32
33
  const port = process.env.HTTP_PORT;
33
34
  const token = process.env.HTTP_AUTH_KEY;
34
35
  const msg = [segment.text("\u9762\u677F\u767B\u5F55\u5730\u5740\uFF1A")];
35
- if (cfg.domain) msg.push(segment.text(`- \u81EA\u5B9A\u4E49\u57DF\u540D: ${cfg.domain}/web/login?token=${token}`));
36
+ if (config.domain) msg.push(segment.text(`- \u81EA\u5B9A\u4E49\u57DF\u540D: ${config.domain}/web/login?token=${token}`));
36
37
  msg.push(segment.text(`- \u5185\u7F51\u5730\u5740: ${IP.lan.ipv4 ? `http://${IP.lan.ipv4}:${port}/web/login?token=${token}` : `http://${IP.lan.ipv4}:${port}/web/login?token=${token}`}`));
37
38
  if (IP.net.ipv4) msg.push(segment.text(`- \u5916\u7F51IPv4\u5730\u5740: http://${IP.net.ipv4}:${port}/web/login?token=${token}`));
38
39
  if (IP.net.ipv6) msg.push(segment.text(`- \u5916\u7F51IPv6\u5730\u5740: http://${IP.net.ipv6}:${port}/web/login?token=${token}`));
@@ -3,5 +3,7 @@ import * as node_karin from 'node-karin';
3
3
  declare const Master: node_karin.Command<keyof node_karin.MessageEventMap>;
4
4
  declare const addMaster: node_karin.Command<keyof node_karin.MessageEventMap>;
5
5
  declare const delMaster: node_karin.Command<keyof node_karin.MessageEventMap>;
6
+ declare const listMaster: node_karin.Command<keyof node_karin.MessageEventMap>;
7
+ declare const setMasterCaptcha: node_karin.Command<keyof node_karin.MessageEventMap>;
6
8
 
7
- export { Master, addMaster, delMaster };
9
+ export { Master, addMaster, delMaster, listMaster, setMasterCaptcha };
@@ -1,7 +1,8 @@
1
1
  // src/apps/master.ts
2
2
  import crypto from "crypto";
3
- import { karin, logger, config } from "node-karin";
4
- var Master = karin.command(/^#设置主人/, async (e) => {
3
+ import { karin, logger, config, segment } from "node-karin";
4
+ var CAPTCHA = /* @__PURE__ */ new Map();
5
+ var Master = karin.command(/^#设置主人$/, async (e) => {
5
6
  if (e.isMaster) {
6
7
  await e.reply(`
7
8
  [${e.userId}] \u5DF2\u7ECF\u662F\u4E3B\u4EBA`, { at: true });
@@ -9,6 +10,7 @@ var Master = karin.command(/^#设置主人/, async (e) => {
9
10
  }
10
11
  const sign = crypto.randomUUID();
11
12
  logger.mark(`\u8BBE\u7F6E\u4E3B\u4EBA\u9A8C\u8BC1\u7801\uFF1A${logger.green(sign)}`);
13
+ CAPTCHA.set(e.userId, sign);
12
14
  await e.reply("\n\u8BF7\u8F93\u5165\u63A7\u5236\u53F0\u9A8C\u8BC1\u7801", { at: true });
13
15
  const event = await karin.ctx(e);
14
16
  if (sign !== event.msg.trim()) {
@@ -62,8 +64,24 @@ var delMaster = karin.command(/^#删除主人/, async (e) => {
62
64
  \u5220\u9664\u4E3B\u4EBA: ${userId}`, { at: true });
63
65
  return true;
64
66
  }, { name: "\u5220\u9664\u4E3B\u4EBA", priority: -1, permission: "master" });
67
+ var listMaster = karin.command(/^#主人列表$/, async (e) => {
68
+ const masters = config.master();
69
+ await e.reply(`\u4E3B\u4EBA\u5217\u8868:
70
+ ${masters.map((v) => `- ${v}`).join("\n")}`, { reply: true });
71
+ return true;
72
+ }, { name: "\u4E3B\u4EBA\u5217\u8868", priority: -1, permission: "master" });
73
+ var setMasterCaptcha = karin.command(/^#设置主人验证码$/, async (e) => {
74
+ const msg = [];
75
+ CAPTCHA.forEach((v, k) => {
76
+ msg.push(segment.text(`- \u7528\u6237: ${k} \u9A8C\u8BC1\u7801: ${v}`));
77
+ });
78
+ if (msg.length === 0) return e.reply("\u6682\u65E0\u9A8C\u8BC1\u7801", { reply: true });
79
+ await e.reply(["\u4E3B\u4EBA\u9A8C\u8BC1\u7801\u5217\u8868:\n", ...msg], { reply: true });
80
+ }, { name: "\u8BBE\u7F6E\u4E3B\u4EBA\u9A8C\u8BC1\u7801", priority: -1, permission: "master" });
65
81
  export {
66
82
  Master,
67
83
  addMaster,
68
- delMaster
84
+ delMaster,
85
+ listMaster,
86
+ setMasterCaptcha
69
87
  };
@@ -1,13 +1,14 @@
1
1
  import {
2
- config
3
- } from "../chunk-MNDJRJH2.js";
2
+ cfg
3
+ } from "../chunk-PHDIUMOH.js";
4
+ import "../chunk-QUWYQJAF.js";
4
5
 
5
6
  // src/apps/restart.ts
6
7
  import { common, karin, logger, restart } from "node-karin";
7
8
  var restarts = karin.command(/^#重启$/, async (e) => {
8
9
  try {
9
10
  await e.reply(`\u5F00\u59CB\u91CD\u542F \u672C\u6B21\u8FD0\u884C\u65F6\u95F4: ${common.uptime()}`, { at: true });
10
- const { status, data } = await restart(e.selfId, e.contact, e.messageId, config().restartMode);
11
+ const { status, data } = await restart(e.selfId, e.contact, e.messageId, cfg.get().restartMode);
11
12
  if (status === "failed") throw data;
12
13
  return true;
13
14
  } catch (error) {
@@ -6,12 +6,12 @@ import {
6
6
  uptime
7
7
  } from "../chunk-ODFXVVIE.js";
8
8
  import {
9
- config,
10
- info
11
- } from "../chunk-MNDJRJH2.js";
9
+ cfg
10
+ } from "../chunk-PHDIUMOH.js";
11
+ import "../chunk-QUWYQJAF.js";
12
12
 
13
13
  // src/apps/status.ts
14
- import { karin, logger } from "node-karin";
14
+ import { karin } from "node-karin";
15
15
  var status = karin.command(/^#状态$/, async (e) => {
16
16
  const { send, recv, event } = await getTodayStat();
17
17
  const { send: sendMonth, recv: recvMonth, event: eventMonth } = await getMonthStat();
@@ -31,8 +31,7 @@ var status = karin.command(/^#状态$/, async (e) => {
31
31
  ].join("\n"));
32
32
  return true;
33
33
  }, { name: "\u72B6\u6001\u7EDF\u8BA1" });
34
- if (config().status) initStat();
35
- logger.info(`${logger.violet(`[\u63D2\u4EF6:${info.version}]`)} ${logger.green(info.pkg.name)} \u521D\u59CB\u5316\u5B8C\u6210~`);
34
+ if (cfg.get().status) initStat();
36
35
  export {
37
36
  status
38
37
  };
@@ -2,13 +2,12 @@ import * as node_karin from 'node-karin';
2
2
 
3
3
  /** 插件列表 */
4
4
  declare const plugins: node_karin.Command<keyof node_karin.MessageEventMap>;
5
+ /** 更新插件 */
6
+ declare const updatePlugin: node_karin.Command<keyof node_karin.MessageEventMap>;
5
7
  /** 检查更新 */
6
8
  declare const check: node_karin.Command<keyof node_karin.MessageEventMap>;
7
- /** 更新插件 */
8
- declare const update: node_karin.Command<keyof node_karin.MessageEventMap>;
9
9
  /** 更新日志 */
10
10
  declare const log: node_karin.Command<keyof node_karin.MessageEventMap>;
11
- /** 全部更新 */
12
- declare const updateAll: node_karin.Command<keyof node_karin.MessageEventMap>;
11
+ declare const TaskUpdate: node_karin.Task;
13
12
 
14
- export { check, log, plugins, update, updateAll };
13
+ export { TaskUpdate, check, log, plugins, updatePlugin };
@@ -1,7 +1,16 @@
1
+ import {
2
+ cfg,
3
+ sendToFirstAdmin
4
+ } from "../chunk-PHDIUMOH.js";
5
+ import {
6
+ render
7
+ } from "../chunk-QUWYQJAF.js";
8
+
1
9
  // src/apps/update.ts
2
10
  import fs from "fs";
3
11
  import path from "path";
4
- import { changelog, checkGitPluginUpdate, checkPkgUpdate, getCommit, getPlugins, getPkgVersion, karin, updateAllGitPlugin, updateAllPkg, updateGitPlugin, updatePkg } from "node-karin";
12
+ import { changelog, checkGitPluginUpdate, checkPkgUpdate, getCommit, getPlugins, getPkgVersion, karin, updateAllGitPlugin, updateAllPkg, updateGitPlugin, updatePkg, segment, restartDirect, db, logger, getPluginInfo } from "node-karin";
13
+ var NODE_KARIN_UPDATE_KEY = "basic:update:node-karin";
5
14
  var cache = [];
6
15
  var getAll = async () => {
7
16
  if (cache.length) return cache;
@@ -19,19 +28,119 @@ var getAll = async () => {
19
28
  return list;
20
29
  };
21
30
  var plugins = karin.command(/^#插件列表$/, async (e) => {
22
- const list = await getAll();
23
- list.forEach((item, index) => {
24
- item += `${index + 1}. ${item}`;
25
- });
26
- await e.reply([
27
- "\n\u63D2\u4EF6\u5217\u8868\uFF1A",
28
- "\u66F4\u65B0\uFF1A#\u66F4\u65B0\u63D2\u4EF6 \u5E8F\u53F7\u6216\u540D\u79F0",
29
- "\u68C0\u67E5\u66F4\u65B0\uFF1A#\u68C0\u67E5\u66F4\u65B0 \u5E8F\u53F7\u6216\u540D\u79F0",
30
- "\u65E5\u5FD7\uFF1A#\u66F4\u65B0\u65E5\u5FD7 \u6761\u6570 \u5E8F\u53F7\u6216\u540D\u79F0",
31
- ...list
32
- ].join("\n"), { at: true });
33
- return true;
31
+ try {
32
+ const allPlugins = await getPlugins("all", true);
33
+ const pluginsData = [];
34
+ let index = 1;
35
+ for (const plugin of allPlugins) {
36
+ if (plugin.type === "app") {
37
+ const apps = plugin.apps || [];
38
+ for (const appPath of apps) {
39
+ const fileName = path.basename(appPath, path.extname(appPath));
40
+ pluginsData.push({
41
+ index: index++,
42
+ name: fileName,
43
+ type: "app",
44
+ typeIcon: "\u{1F50C}",
45
+ version: "",
46
+ description: "",
47
+ author: "\u672A\u77E5",
48
+ dir: appPath
49
+ });
50
+ }
51
+ } else {
52
+ const pkgData = plugin.pkgData || {};
53
+ let author = "\u672A\u77E5";
54
+ if (typeof pkgData.author === "string") {
55
+ author = pkgData.author;
56
+ } else if (pkgData.author && typeof pkgData.author === "object" && "name" in pkgData.author) {
57
+ author = pkgData.author.name || "\u672A\u77E5";
58
+ }
59
+ const description = pkgData.description || "";
60
+ const version = pkgData.version || "";
61
+ const typeIcon = plugin.type === "npm" ? "\u{1F4E6}" : "\u{1F527}";
62
+ pluginsData.push({
63
+ index: index++,
64
+ name: plugin.name,
65
+ type: plugin.type,
66
+ typeIcon,
67
+ version,
68
+ description,
69
+ author,
70
+ dir: plugin.dir
71
+ });
72
+ }
73
+ }
74
+ if (pluginsData.length === 0) {
75
+ await e.reply("\n\u6682\u65E0\u63D2\u4EF6", { at: true });
76
+ return true;
77
+ }
78
+ const img = await render("plugins/index", {
79
+ plugins: pluginsData,
80
+ total: pluginsData.length,
81
+ date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
82
+ });
83
+ await e.reply(img);
84
+ return true;
85
+ } catch (error) {
86
+ logger.error("\u6E32\u67D3\u63D2\u4EF6\u5217\u8868\u5931\u8D25:", error);
87
+ try {
88
+ const list = await getAll();
89
+ const textList = list.map((item, index) => `${index + 1}. ${item}`);
90
+ await e.reply([
91
+ "\n\u63D2\u4EF6\u5217\u8868\uFF1A",
92
+ "\u66F4\u65B0\uFF1A#\u66F4\u65B0\u63D2\u4EF6 \u540D\u79F0",
93
+ "\u68C0\u67E5\u66F4\u65B0\uFF1A#\u68C0\u67E5\u66F4\u65B0 \u5E8F\u53F7\u6216\u540D\u79F0",
94
+ "\u65E5\u5FD7\uFF1A#\u66F4\u65B0\u65E5\u5FD7 \u6761\u6570 \u5E8F\u53F7\u6216\u540D\u79F0",
95
+ ...textList
96
+ ].join("\n"), { at: true });
97
+ } catch (fallbackError) {
98
+ logger.error("\u53D1\u9001\u6587\u672C\u63D2\u4EF6\u5217\u8868\u5931\u8D25:", fallbackError);
99
+ await e.reply("\n\u83B7\u53D6\u63D2\u4EF6\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u65E5\u5FD7", { at: true });
100
+ }
101
+ return true;
102
+ }
34
103
  }, { name: "\u63D2\u4EF6\u5217\u8868", perm: "admin" });
104
+ var updatePlugin = karin.command(/^#(全部)?(强制)?更新(.*)?$/, async (e) => {
105
+ const [, all, force, name = "node-karin"] = e.msg.match(/^#(全部)?(强制)?更新(.*)?$/);
106
+ const cmd = force ? "git reset --hard && git pull --allow-unrelated-histories" : "git pull";
107
+ if (all) {
108
+ try {
109
+ const git = await updateAllGitPlugin(cmd);
110
+ const npm = await updateAllPkg();
111
+ await e.reply([
112
+ "\n\u5168\u90E8\u66F4\u65B0\u5B8C\u6210",
113
+ "-----",
114
+ git,
115
+ "-----",
116
+ npm
117
+ ].join("\n"), { at: true });
118
+ } catch (error) {
119
+ await e.reply(`
120
+ \u66F4\u65B0\u5168\u90E8\u63D2\u4EF6\u5931\u8D25: ${error.message || "\u672A\u77E5\u9519\u8BEF"}`, { at: true });
121
+ }
122
+ } else {
123
+ let res;
124
+ if (name !== "node-karin") {
125
+ const info = getPluginInfo(name.trim());
126
+ if (!info) return await e.reply("\u63D2\u4EF6\u672A\u5B89\u88C5~", { reply: true });
127
+ if (info.type === "app") return await e.reply("\u5E94\u7528\u63D2\u4EF6\u4E0D\u652F\u6301\u66F4\u65B0~", { reply: true });
128
+ res = info.type === "git" ? await updateGitPlugin(info.dir, cmd, 120) : await updatePkg(info.name);
129
+ } else {
130
+ res = await updatePkg("node-karin");
131
+ }
132
+ if (res.status === "failed") {
133
+ const { data } = res;
134
+ const msg = typeof data === "string" ? data : `\u83B7\u53D6\u66F4\u65B0\u4FE1\u606F\u5931\u8D25: ${data.message || "\u672A\u77E5\u9519\u8BEF"}`;
135
+ await e.reply(`
136
+ ${msg}`, { at: true });
137
+ return true;
138
+ }
139
+ return await e.reply(`
140
+ \u66F4\u65B0\u6210\u529F
141
+ ${res.data}`, { at: true });
142
+ }
143
+ });
35
144
  var check = karin.command(/^#检查更新/, async (e) => {
36
145
  let name = e.msg.replace(/^#检查更新/, "").trim();
37
146
  const index = Number(name);
@@ -94,54 +203,6 @@ ${tips}`, { at: true });
94
203
  await e.reply("\n\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u63D2\u4EF6\u540D\u79F0\u6216\u5E8F\u53F7~", { at: true });
95
204
  return true;
96
205
  }, { name: "\u68C0\u67E5\u66F4\u65B0", perm: "admin" });
97
- var update = karin.command(/^#(强制)?更新(插件)?(?!列表|日志)/, async (e) => {
98
- let name = e.msg.replace(/^#(强制)?更新(插件)?(?!列表|日志)/, "").trim();
99
- const index = Number(name);
100
- if (index && typeof index === "number") {
101
- const list = await getAll();
102
- name = list[index - 1];
103
- }
104
- if (!name) {
105
- await e.reply("\n\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u63D2\u4EF6\u540D\u79F0\u6216\u5E8F\u53F7~", { at: true });
106
- return true;
107
- }
108
- if (name.includes("git:")) {
109
- name = name.replace("git:", "");
110
- const file = path.join(process.cwd(), "plugins", name.replace("git:", ""));
111
- let cmd = "git pull";
112
- if (e.msg.includes("\u5F3A\u5236")) cmd = "git reset --hard && git pull --allow-unrelated-histories";
113
- const result = await updateGitPlugin(file, cmd, 120);
114
- if (result.status === "failed") {
115
- const { data } = result;
116
- const msg = typeof data === "string" ? data : `\u83B7\u53D6\u66F4\u65B0\u4FE1\u606F\u5931\u8D25: ${data.message || "\u672A\u77E5\u9519\u8BEF"}`;
117
- await e.reply(msg, { at: true });
118
- return true;
119
- }
120
- await e.reply(`
121
- ${result.data}`, { at: true });
122
- return true;
123
- }
124
- if (name.includes("npm:")) {
125
- name = name.replace("npm:", "");
126
- const result = await updatePkg(name);
127
- if (result.status === "failed") {
128
- const { data } = result;
129
- const msg = typeof data === "string" ? data : `\u83B7\u53D6\u66F4\u65B0\u4FE1\u606F\u5931\u8D25: ${data.message || "\u672A\u77E5\u9519\u8BEF"}`;
130
- await e.reply(`
131
- ${msg}`, { at: true });
132
- return true;
133
- }
134
- const log2 = parseLog(name, result.local, result.remote);
135
- await e.reply(`
136
- \u66F4\u65B0\u6210\u529F
137
- \u5F53\u524D\u7248\u672C: ${result.remote}
138
- \u66F4\u65B0\u65E5\u5FD7:
139
- ${log2}`, { at: true });
140
- return true;
141
- }
142
- await e.reply("\n\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u63D2\u4EF6\u540D\u79F0\u6216\u5E8F\u53F7~", { at: true });
143
- return true;
144
- }, { name: "\u66F4\u65B0\u63D2\u4EF6", perm: "admin" });
145
206
  var log = karin.command(/^#更新日志/, async (e) => {
146
207
  const [index, num] = e.msg.replace(/^#更新日志/, "").trim().split(" ");
147
208
  if (!index || !num) {
@@ -181,24 +242,6 @@ ${result}`, { at: true });
181
242
  await e.reply("\n\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u63D2\u4EF6\u540D\u79F0\u6216\u5E8F\u53F7~", { at: true });
182
243
  return true;
183
244
  }, { name: "\u66F4\u65B0\u65E5\u5FD7", perm: "admin" });
184
- var updateAll = karin.command(/^#全部(强制)?更新$/, async (e) => {
185
- const cmd = e.msg.includes("\u5F3A\u5236") ? "git reset --hard && git pull --allow-unrelated-histories" : "git pull";
186
- try {
187
- const git = await updateAllGitPlugin(cmd);
188
- const npm = await updateAllPkg();
189
- await e.reply([
190
- "\n\u5168\u90E8\u66F4\u65B0\u5B8C\u6210",
191
- "-----",
192
- git,
193
- "-----",
194
- npm
195
- ].join("\n"), { at: true });
196
- } catch (error) {
197
- await e.reply(`
198
- \u5168\u90E8\u66F4\u65B0\u5931\u8D25: ${error.message || "\u672A\u77E5\u9519\u8BEF"}`, { at: true });
199
- }
200
- return true;
201
- }, { name: "\u5168\u90E8\u66F4\u65B0", perm: "admin" });
202
245
  var parseLog = (pkg, local, count) => {
203
246
  const file = path.join(process.cwd(), "node_modules", pkg, "CHANGELOG.md");
204
247
  if (!fs.existsSync(file)) return "\u63D2\u4EF6\u672A\u63D0\u4F9B`CHANGELOG.md`\u6587\u4EF6";
@@ -208,10 +251,51 @@ var parseLog = (pkg, local, count) => {
208
251
  }
209
252
  return changelog.range(data, local, count) || "\u672A\u627E\u5230\u5BF9\u5E94\u7684\u66F4\u65B0\u65E5\u5FD7";
210
253
  };
254
+ var TaskUpdate = karin.task("Karin-\u5B9A\u65F6\u66F4\u65B0\u68C0\u67E5", "*/10 * * * *", async () => {
255
+ if (process.env.NODE_ENV === "development") return true;
256
+ const res = await checkPkgUpdate("node-karin");
257
+ if (res.status !== "yes") return true;
258
+ const botIds = karin.getAllBotID();
259
+ const selfId = botIds.find((id) => id.toString() !== "console");
260
+ if (!selfId) return true;
261
+ const config = cfg.get();
262
+ if (config.autoupdate) {
263
+ const up = await updatePkg("node-karin");
264
+ if (up.status === "failed") {
265
+ await sendToFirstAdmin(selfId, [segment.text(`\u81EA\u52A8\u66F4\u65B0 node-karin \u5931\u8D25: ${String(up.data)}`)]);
266
+ return true;
267
+ }
268
+ await sendToFirstAdmin(selfId, [
269
+ segment.text(
270
+ `\u68C0\u6D4B\u5230\u63D2\u4EF6 [node-karin] \u6709\u65B0\u7248\u672C~
271
+ \u5DF2\u81EA\u52A8\u66F4\u65B0 (v${up.local} \u2192 v${up.remote})\uFF0C\u5373\u5C06\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0`
272
+ )
273
+ ]);
274
+ await restartDirect({ isPm2: false, reloadDeps: true });
275
+ return true;
276
+ }
277
+ const last = await db.get(NODE_KARIN_UPDATE_KEY);
278
+ if (last && last.lastRemote === res.remote) return true;
279
+ const msg = [
280
+ segment.text(
281
+ `\u68C0\u6D4B\u5230\u63D2\u4EF6 [node-karin] \u6709\u65B0\u7248\u672C~
282
+ \u5F53\u524D\u7248\u672C: v${res.local}
283
+ \u6700\u65B0\u7248\u672C: v${res.remote}
284
+ \u8BF7\u53D1\u9001 #\u66F4\u65B0 \u8FDB\u884C\u66F4\u65B0\u3002`
285
+ )
286
+ ];
287
+ const messageId = await sendToFirstAdmin(selfId, msg);
288
+ if (!messageId) return false;
289
+ await db.set(NODE_KARIN_UPDATE_KEY, {
290
+ lastRemote: res.remote,
291
+ lastLocal: res.local
292
+ });
293
+ return true;
294
+ }, { name: "Karin-\u5B9A\u65F6\u66F4\u65B0\u68C0\u67E5", log: false });
211
295
  export {
296
+ TaskUpdate,
212
297
  check,
213
298
  log,
214
299
  plugins,
215
- update,
216
- updateAll
300
+ updatePlugin
217
301
  };