koishi-plugin-freekill-u 1.0.1 → 1.0.3
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 +719 -55
- package/package.json +1 -2
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
};
|
|
17
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
18
|
|
|
19
|
-
// src/index.ts
|
|
19
|
+
// packages/freekill-u/src/index.ts
|
|
20
20
|
var src_exports = {};
|
|
21
21
|
__export(src_exports, {
|
|
22
22
|
Config: () => Config,
|
|
@@ -29,40 +29,88 @@ var import_koishi = require("koishi");
|
|
|
29
29
|
var { Client } = require("ssh2");
|
|
30
30
|
var name = "freekill-u";
|
|
31
31
|
var Config = import_koishi.Schema.object({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
servers: import_koishi.Schema.array(import_koishi.Schema.object({
|
|
33
|
+
name: import_koishi.Schema.string().description('\u670D\u52A1\u5668\u540D\u79F0\uFF08\u7528\u4F5C\u547D\u4EE4\u524D\u7F00\uFF0C\u5982"\u5B98\u670D"\u5219\u547D\u4EE4\u4E3A \u5B98\u670D.u / \u5B98\u670D.stat \u7B49\uFF09').required(),
|
|
34
|
+
host: import_koishi.Schema.string().description("SSH \u670D\u52A1\u5668 IP").default(""),
|
|
35
|
+
port: import_koishi.Schema.number().description("SSH \u7AEF\u53E3").default(22).min(1).max(65535),
|
|
36
|
+
username: import_koishi.Schema.string().description("SSH \u7528\u6237\u540D").default(""),
|
|
37
|
+
password: import_koishi.Schema.string().role("secret").description("SSH \u5BC6\u7801").default(""),
|
|
38
|
+
screenSession: import_koishi.Schema.string().description("screen \u4F1A\u8BDD\u6807\u8BC6\uFF08\u591A\u4E2A screen \u65F6\u586B\u5199\uFF0C\u7559\u7A7A\u5219\u7528 screen -r \u9ED8\u8BA4\u9009\u62E9\uFF09").default(""),
|
|
39
|
+
restartSequence: import_koishi.Schema.string().role("textarea").description("\u91CD\u542F\u547D\u4EE4\u5E8F\u5217\uFF08quit \u540E\u6267\u884C\uFF09\u3002\u6BCF\u884C\u4E00\u6761\u547D\u4EE4\uFF0C\u7A7A\u884C = \u989D\u5916\u56DE\u8F66\u3002\u4F8B\u5982\uFF1A\nscreen\n\ncd /home/fk/freekill-asio-static\n./freekill-asio").default(""),
|
|
40
|
+
operators: import_koishi.Schema.string().description("\u64CD\u4F5C\u5458 QQ \u53F7\u767D\u540D\u5355\uFF08\u591A\u4E2A\u7528\u9017\u53F7/\u7A7A\u683C\u5206\u9694\uFF0C\u7559\u7A7A\u5219\u4E0D\u9650\u5236\uFF09\u3002\u4E0D\u5728\u767D\u540D\u5355\u5185\u7684\u7528\u6237\u6267\u884C\u547D\u4EE4\u65F6\u9759\u9ED8\u5FFD\u7565").default(""),
|
|
41
|
+
attachDelay: import_koishi.Schema.number().description("screen -r \u540E\u7B49\u5F85 attach \u7684\u6BEB\u79D2\u6570").default(3e3).min(500).max(3e4),
|
|
42
|
+
updateWait: import_koishi.Schema.number().description("\u53D1\u9001\u66F4\u65B0\u547D\u4EE4\u540E\u7B49\u5F85\u8F93\u51FA\u7684\u6BEB\u79D2\u6570").default(3e4).min(5e3).max(3e5),
|
|
43
|
+
quitWait: import_koishi.Schema.number().description("\u53D1\u9001 quit \u540E\u7B49\u5F85\u670D\u52A1\u5668\u5173\u95ED\u7684\u6BEB\u79D2\u6570").default(5e3).min(2e3).max(6e4),
|
|
44
|
+
restartWait: import_koishi.Schema.number().description("\u53D1\u9001\u542F\u52A8\u547D\u4EE4\u540E\u7B49\u5F85\u670D\u52A1\u5668\u542F\u52A8\u7684\u6BEB\u79D2\u6570").default(6e4).min(1e4).max(3e5),
|
|
45
|
+
commandWait: import_koishi.Schema.number().description("\u666E\u901A\u67E5\u8BE2\u547D\u4EE4\u7B49\u5F85\u8F93\u51FA\u7684\u6BEB\u79D2\u6570").default(3e3).min(1e3).max(3e4)
|
|
46
|
+
})).description("\u670D\u52A1\u5668\u5217\u8868\uFF0C\u6BCF\u4E2A\u670D\u52A1\u5668\u5BF9\u5E94\u4E00\u7EC4 SSH \u8FDE\u63A5\u4FE1\u606F\u548C\u547D\u4EE4\u524D\u7F00").default([]),
|
|
47
|
+
authority: import_koishi.Schema.number().description("\u4F7F\u7528\u547D\u4EE4\u6240\u9700\u7684\u6700\u4F4E\u6743\u9650\u7B49\u7EA7").default(4).min(1).max(5),
|
|
41
48
|
debugMode: import_koishi.Schema.boolean().description("\u8C03\u8BD5\u6A21\u5F0F\uFF1A\u663E\u793A\u5B8C\u6574 SSH \u8F93\u51FA\uFF08\u542B\u654F\u611F\u5185\u5BB9\uFF0C\u4EC5\u8C03\u8BD5\u65F6\u5F00\u542F\uFF09").default(false)
|
|
42
49
|
});
|
|
43
50
|
var usage = `
|
|
44
|
-
# \u65B0\u6708\u6740\u8FDC\u7A0B\
|
|
51
|
+
# \u65B0\u6708\u6740\u8FDC\u7A0B\u7BA1\u7406\u63D2\u4EF6
|
|
45
52
|
|
|
46
|
-
\u901A\u8FC7 SSH \u8FDE\u63A5\u670D\u52A1\u5668\uFF0C\u81EA\u52A8\u8FDB\u5165 screen \u4F1A\u8BDD\u6267\u884C\
|
|
53
|
+
\u901A\u8FC7 SSH \u8FDE\u63A5\u670D\u52A1\u5668\uFF0C\u81EA\u52A8\u8FDB\u5165 screen \u4F1A\u8BDD\u6267\u884C\u7BA1\u7406\u547D\u4EE4\u3002
|
|
47
54
|
\u5371\u9669\u64CD\u4F5C\uFF0C\u8BF7\u52A1\u5FC5\u8BBE\u7F6E\u8DB3\u591F\u7684\u6743\u9650\u7B49\u7EA7\u3002
|
|
48
55
|
|
|
49
|
-
## \
|
|
56
|
+
## \u914D\u7F6E\u65B9\u6CD5
|
|
50
57
|
|
|
51
|
-
\u5728\
|
|
58
|
+
\u5728\u63D2\u4EF6\u914D\u7F6E\u4E2D\u6DFB\u52A0\u670D\u52A1\u5668\u5217\u8868\uFF0C\u6BCF\u4E2A\u670D\u52A1\u5668\u914D\u7F6E\uFF1A
|
|
59
|
+
- **\u540D\u79F0**\uFF1A\u7528\u4F5C\u547D\u4EE4\u524D\u7F00\uFF0C\u5982\u586B\u5199"\u5B98\u670D"\uFF0C\u5219\u547D\u4EE4\u4E3A **\u5B98\u670D.u**\u3001**\u5B98\u670D.stat** \u7B49
|
|
60
|
+
- **SSH \u4FE1\u606F**\uFF1A\u4E3B\u673A\u3001\u7AEF\u53E3\u3001\u7528\u6237\u540D\u3001\u5BC6\u7801
|
|
61
|
+
- **screen \u4F1A\u8BDD**\uFF1A\u6709\u591A\u4E2A screen \u65F6\u586B\u5199\u4F1A\u8BDD\u6807\u8BC6
|
|
62
|
+
- **\u64CD\u4F5C\u5458\u767D\u540D\u5355**\uFF1A\u586B\u5199 QQ \u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u4E0D\u5728\u767D\u540D\u5355\u5185\u7684\u7528\u6237\u6267\u884C\u547D\u4EE4\u65F6 bot \u9759\u9ED8\u4E0D\u54CD\u5E94
|
|
63
|
+
- **\u91CD\u542F\u547D\u4EE4\u5E8F\u5217**\uFF1Aquit \u540E\u7684\u91CD\u542F\u547D\u4EE4\uFF0C\u6BCF\u884C\u4E00\u6761\u547D\u4EE4\uFF0C\u7A7A\u884C=\u989D\u5916\u56DE\u8F66\uFF08\u7559\u7A7A\u5219\u7528\u4E0A\u7BAD\u5934\u56DE\u653E\u4E0A\u6B21\u547D\u4EE4\uFF09
|
|
52
64
|
|
|
53
|
-
## \
|
|
65
|
+
## \u91CD\u542F\u5E8F\u5217\u793A\u4F8B
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
\`\`\`
|
|
68
|
+
screen
|
|
69
|
+
|
|
70
|
+
cd /home/fk/freekill-asio-static
|
|
71
|
+
./freekill-asio
|
|
72
|
+
\`\`\`
|
|
73
|
+
\u4E0A\u65B9\u7A7A\u884C = \u591A\u6309\u4E00\u6B21\u56DE\u8F66\uFF1B\u4E0B\u9762\u4E24\u884C = cd \u548C\u542F\u52A8\u547D\u4EE4\u3002
|
|
74
|
+
|
|
75
|
+
## \u547D\u4EE4\u5217\u8868
|
|
76
|
+
|
|
77
|
+
\u6240\u6709\u547D\u4EE4\u683C\u5F0F\uFF1A\`\u670D\u52A1\u5668\u540D.\u5B50\u547D\u4EE4\`
|
|
78
|
+
|
|
79
|
+
| \u547D\u4EE4 | \u522B\u540D | \u8BF4\u660E |
|
|
80
|
+
|------|------|------|
|
|
81
|
+
| \`\u670D\u52A1\u5668.u\` | \u2014 | \u8FDC\u7A0B\u66F4\u65B0\u670D\u52A1\u5668 |
|
|
82
|
+
| \`\u670D\u52A1\u5668.reboot\` | \u2014 | \u8FDC\u7A0B\u91CD\u542F\u670D\u52A1\u5668 |
|
|
83
|
+
| \`\u670D\u52A1\u5668.detached\` | \u2014 | \u5F3A\u884C\u5206\u79BB\u88AB\u5360\u7528\u7684 screen |
|
|
84
|
+
| \`\u670D\u52A1\u5668.stat\` | \u2014 | \u67E5\u770B\u8FD0\u884C\u72B6\u6001 |
|
|
85
|
+
| \`\u670D\u52A1\u5668.reloadconf\` | \`.r\` | \u91CD\u8F7D\u914D\u7F6E\u6587\u4EF6 |
|
|
86
|
+
| \`\u670D\u52A1\u5668.lsplayer\` | \u2014 | \u67E5\u770B\u5728\u7EBF\u73A9\u5BB6 |
|
|
87
|
+
| \`\u670D\u52A1\u5668.lsroom [id]\` | \u2014 | \u67E5\u770B\u623F\u95F4\u5217\u8868 |
|
|
88
|
+
| \`\u670D\u52A1\u5668.msg <\u5185\u5BB9>\` | \`.m\` | \u53D1\u5168\u4F53\u516C\u544A |
|
|
89
|
+
| \`\u670D\u52A1\u5668.msgroom <\u623F\u95F4> <\u5185\u5BB9>\` | \`.mr\` | \u5411\u623F\u95F4\u53D1\u6D88\u606F |
|
|
90
|
+
| \`\u670D\u52A1\u5668.kick <\u73A9\u5BB6>\` | \u2014 | \u8E22\u51FA\u73A9\u5BB6 |
|
|
91
|
+
| \`\u670D\u52A1\u5668.killroom <\u623F\u95F4>\` | \u2014 | \u5E9F\u5F03\u623F\u95F4 |
|
|
92
|
+
| \`\u670D\u52A1\u5668.checklobby\` | \u2014 | \u6E05\u5927\u5385\u50F5\u5C38 |
|
|
93
|
+
| \`\u670D\u52A1\u5668.ban <\u76EE\u6807>\` | \u2014 | \u5C01\u7981\u8D26\u53F7 |
|
|
94
|
+
| \`\u670D\u52A1\u5668.unban <\u76EE\u6807>\` | \u2014 | \u89E3\u5C01\u8D26\u53F7 |
|
|
95
|
+
| \`\u670D\u52A1\u5668.banip <IP>\` | \u2014 | \u5C01\u7981 IP |
|
|
96
|
+
| \`\u670D\u52A1\u5668.unbanip <IP>\` | \u2014 | \u89E3\u5C01 IP |
|
|
97
|
+
| \`\u670D\u52A1\u5668.banuuid <UUID>\` | \u2014 | \u5C01\u7981 UUID |
|
|
98
|
+
| \`\u670D\u52A1\u5668.unbanuuid <UUID>\` | \u2014 | \u89E3\u5C01 UUID |
|
|
99
|
+
| \`\u670D\u52A1\u5668.tempban <\u76EE\u6807> <\u65F6\u957F>\` | \u2014 | \u4E34\u65F6\u5C01\u7981 |
|
|
100
|
+
| \`\u670D\u52A1\u5668.tempmute <\u76EE\u6807> <\u65F6\u957F>\` | \u2014 | \u4E34\u65F6\u7981\u8A00 |
|
|
101
|
+
| \`\u670D\u52A1\u5668.unmute <\u76EE\u6807>\` | \u2014 | \u89E3\u9664\u7981\u8A00 |
|
|
102
|
+
| \`\u670D\u52A1\u5668.whitelist <\u64CD\u4F5C>\` | \u2014 | \u7BA1\u7406\u767D\u540D\u5355 |
|
|
103
|
+
| \`\u670D\u52A1\u5668.resetpassword <\u8D26\u53F7>\` | \`.rp\` | \u91CD\u7F6E\u5BC6\u7801 |
|
|
104
|
+
| \`\u670D\u52A1\u5668.pkgs\` | \u2014 | \u67E5\u770B\u6269\u5C55\u5305 |
|
|
105
|
+
| \`\u670D\u52A1\u5668.syncpkgs\` | \u2014 | \u540C\u6B65\u6269\u5C55\u5305 |
|
|
106
|
+
| \`\u670D\u52A1\u5668.install <URL>\` | \u2014 | \u5B89\u88C5\u6269\u5C55\u5305 |
|
|
107
|
+
| \`\u670D\u52A1\u5668.remove <\u540D\u79F0>\` | \u2014 | \u79FB\u9664\u6269\u5C55\u5305 |
|
|
108
|
+
| \`\u670D\u52A1\u5668.enable <\u540D\u79F0>\` | \u2014 | \u542F\u7528\u6269\u5C55\u5305 |
|
|
109
|
+
| \`\u670D\u52A1\u5668.disable <\u540D\u79F0>\` | \u2014 | \u7981\u7528\u6269\u5C55\u5305 |
|
|
62
110
|
|
|
63
111
|
## \u6CE8\u610F\u4E8B\u9879
|
|
64
112
|
|
|
65
|
-
- screen \u4F1A\u8BDD\u5FC5\u987B\u662F **detached** \u72B6\u6001
|
|
113
|
+
- screen \u4F1A\u8BDD\u5FC5\u987B\u662F **detached** \u72B6\u6001
|
|
66
114
|
- \u672C\u63D2\u4EF6\u6D89\u53CA\u670D\u52A1\u5668\u5B89\u5168\uFF0C\u8BF7\u52A1\u5FC5\u8BBE\u7F6E\u8DB3\u591F\u7684\u6743\u9650\u7B49\u7EA7
|
|
67
115
|
|
|
68
116
|
## \u53CD\u9988\u6E20\u9053
|
|
@@ -70,47 +118,319 @@ QQ \u7FA4\uFF1A1078815356
|
|
|
70
118
|
`;
|
|
71
119
|
function apply(ctx, config) {
|
|
72
120
|
const logger = ctx.logger("freekill-u");
|
|
73
|
-
ctx.
|
|
74
|
-
|
|
75
|
-
|
|
121
|
+
ctx.middleware(async (session, next) => {
|
|
122
|
+
const content = (session.content || "").trim();
|
|
123
|
+
if (!content) return next();
|
|
124
|
+
for (const server of config.servers) {
|
|
125
|
+
const prefix = server.name;
|
|
126
|
+
if (!prefix) continue;
|
|
127
|
+
const lower = content.toLowerCase();
|
|
128
|
+
const pfxLower = prefix.toLowerCase();
|
|
129
|
+
if (!lower.startsWith(pfxLower)) continue;
|
|
130
|
+
const rest = content.slice(prefix.length);
|
|
131
|
+
const isCommand = rest === "" || rest.startsWith(".") || rest.startsWith(" ") || rest === prefix;
|
|
132
|
+
if (!isCommand) continue;
|
|
133
|
+
if (server.operators && server.operators.trim()) {
|
|
134
|
+
const allowedIds = server.operators.split(/[,,\s]+/).map((s) => s.trim()).filter(Boolean);
|
|
135
|
+
if (allowedIds.length > 0) {
|
|
136
|
+
const userId = session.userId || "";
|
|
137
|
+
if (!allowedIds.includes(userId)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return next();
|
|
144
|
+
});
|
|
145
|
+
for (const server of config.servers) {
|
|
146
|
+
registerServerCommands(ctx, config, server, logger);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function registerServerCommands(ctx, config, server, logger) {
|
|
150
|
+
const prefix = server.name;
|
|
151
|
+
function checkAuth(session) {
|
|
152
|
+
if (!server.host || !server.username) return `\u26A0\uFE0F [${prefix}] \u8BF7\u5148\u5728\u63D2\u4EF6\u914D\u7F6E\u4E2D\u586B\u5199 SSH \u8FDE\u63A5\u4FE1\u606F\u3002`;
|
|
153
|
+
if (server.operators && server.operators.trim()) {
|
|
154
|
+
const allowedIds = server.operators.split(/[,,\s]+/).map((s) => s.trim()).filter(Boolean);
|
|
155
|
+
if (allowedIds.length > 0) {
|
|
156
|
+
const userId = session.userId || "";
|
|
157
|
+
if (!allowedIds.includes(userId)) return void 0;
|
|
158
|
+
}
|
|
76
159
|
}
|
|
77
160
|
const userAuthority = session.user?.authority ?? 0;
|
|
78
|
-
if (userAuthority < config.authority) {
|
|
79
|
-
|
|
161
|
+
if (userAuthority < config.authority) return `\u26A0\uFE0F \u6743\u9650\u4E0D\u8DB3\uFF0C\u9700\u8981\u7B49\u7EA7 ${config.authority} \u4EE5\u4E0A\u3002`;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
async function execShellCmd(session, shellCmd, label) {
|
|
165
|
+
const startTime = Date.now();
|
|
166
|
+
try {
|
|
167
|
+
const result = await executeShellCommand(shellCmd, server);
|
|
168
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
169
|
+
const output = smartCleanOutput(result.output, shellCmd);
|
|
170
|
+
return `\u{1F4CB} [${prefix}] ${label}
|
|
171
|
+
\u23F1 ${elapsed}s
|
|
172
|
+
|
|
173
|
+
${output}`;
|
|
174
|
+
} catch (e) {
|
|
175
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
176
|
+
logger.warn("Shell\u547D\u4EE4\u5931\u8D25", { server: prefix, cmd: shellCmd, error: e?.message || String(e) });
|
|
177
|
+
return `\u274C [${prefix}] ${label} \u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`;
|
|
80
178
|
}
|
|
179
|
+
}
|
|
180
|
+
ctx.command(`${prefix}`, `${prefix} \u670D\u52A1\u5668\u7BA1\u7406`);
|
|
181
|
+
ctx.command(`${prefix}.u`, `\u66F4\u65B0 ${prefix} \u670D\u52A1\u5668`).userFields(["authority"]).action(async ({ session }) => {
|
|
182
|
+
const err = checkAuth(session);
|
|
183
|
+
if (err === void 0) return;
|
|
184
|
+
if (err) return err;
|
|
81
185
|
const startTime = Date.now();
|
|
82
|
-
await session.send(
|
|
186
|
+
await session.send(`\u{1F50C} [${prefix}] \u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u5668\u2026`);
|
|
83
187
|
try {
|
|
84
|
-
const result = await executeUpdate(
|
|
188
|
+
const result = await executeUpdate(server);
|
|
85
189
|
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
86
|
-
const lines = [
|
|
87
|
-
if (result.hasError)
|
|
88
|
-
lines.push("\u26A0\uFE0F \u68C0\u6D4B\u5230\u53EF\u80FD\u7684\u9519\u8BEF\uFF0C\u5EFA\u8BAE\u68C0\u67E5\u670D\u52A1\u5668\u65E5\u5FD7\u3002");
|
|
89
|
-
}
|
|
190
|
+
const lines = [`\u2705 [${prefix}] \u66F4\u65B0\u5B8C\u6210\uFF01`, `\u23F1 \u8017\u65F6 ${elapsed} \u79D2`];
|
|
191
|
+
if (result.hasError) lines.push("\u26A0\uFE0F \u68C0\u6D4B\u5230\u53EF\u80FD\u7684\u9519\u8BEF\uFF0C\u5EFA\u8BAE\u68C0\u67E5\u670D\u52A1\u5668\u65E5\u5FD7\u3002");
|
|
90
192
|
if (config.debugMode && result.output) {
|
|
91
193
|
let output = filterSensitive(result.output);
|
|
92
|
-
if (output.length > 2e3)
|
|
93
|
-
output = output.slice(-2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u4EC5\u663E\u793A\u672B\u5C3E 2000 \u5B57\u7B26\uFF09";
|
|
94
|
-
}
|
|
194
|
+
if (output.length > 2e3) output = output.slice(-2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
|
|
95
195
|
lines.push(`
|
|
96
196
|
\u{1F4CB} \u66F4\u65B0\u8F93\u51FA\uFF1A
|
|
97
197
|
${output}`);
|
|
98
198
|
}
|
|
99
|
-
if (result.hasError) {
|
|
100
|
-
|
|
199
|
+
if (result.hasError) logger.warn("\u66F4\u65B0\u53EF\u80FD\u5931\u8D25", { server: prefix, elapsed, lines: result.lineCount });
|
|
200
|
+
await session.send(lines.join("\n"));
|
|
201
|
+
} catch (e) {
|
|
202
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
203
|
+
logger.warn("\u66F4\u65B0\u5931\u8D25", { server: prefix, error: e?.message || String(e), elapsed });
|
|
204
|
+
await session.send(`\u274C [${prefix}] \u66F4\u65B0\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
ctx.command(`${prefix}.reboot`, `\u91CD\u542F ${prefix} \u670D\u52A1\u5668`).userFields(["authority"]).action(async ({ session }) => {
|
|
208
|
+
const err = checkAuth(session);
|
|
209
|
+
if (err === void 0) return;
|
|
210
|
+
if (err) return err;
|
|
211
|
+
const startTime = Date.now();
|
|
212
|
+
await session.send(`\u{1F50C} [${prefix}] \u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u5668\u2026`);
|
|
213
|
+
try {
|
|
214
|
+
const result = await executeRestart(server);
|
|
215
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
216
|
+
const lines = [`\u2705 [${prefix}] \u91CD\u542F\u5B8C\u6210\uFF01`, `\u23F1 \u8017\u65F6 ${elapsed} \u79D2`];
|
|
217
|
+
if (result.hasError) lines.push("\u26A0\uFE0F \u68C0\u6D4B\u5230\u53EF\u80FD\u7684\u9519\u8BEF\uFF0C\u5EFA\u8BAE\u68C0\u67E5\u670D\u52A1\u5668\u65E5\u5FD7\u3002");
|
|
218
|
+
if (config.debugMode && result.output) {
|
|
219
|
+
let output = filterSensitive(result.output);
|
|
220
|
+
if (output.length > 2e3) output = output.slice(-2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
|
|
221
|
+
lines.push(`
|
|
222
|
+
\u{1F4CB} \u91CD\u542F\u8F93\u51FA\uFF1A
|
|
223
|
+
${output}`);
|
|
101
224
|
}
|
|
225
|
+
if (result.hasError) logger.warn("\u91CD\u542F\u53EF\u80FD\u5931\u8D25", { server: prefix, elapsed, lines: result.lineCount });
|
|
102
226
|
await session.send(lines.join("\n"));
|
|
103
227
|
} catch (e) {
|
|
104
228
|
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
105
|
-
logger.warn("\
|
|
106
|
-
await session.send(`\u274C \
|
|
229
|
+
logger.warn("\u91CD\u542F\u5931\u8D25", { server: prefix, error: e?.message || String(e), elapsed });
|
|
230
|
+
await session.send(`\u274C [${prefix}] \u91CD\u542F\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
ctx.command(`${prefix}.detached`, `\u5F3A\u5236\u5206\u79BB ${prefix} \u7684 screen \u4F1A\u8BDD`).userFields(["authority"]).action(async ({ session }) => {
|
|
234
|
+
const err = checkAuth(session);
|
|
235
|
+
if (err === void 0) return;
|
|
236
|
+
if (err) return err;
|
|
237
|
+
await session.send(`\u{1F50C} [${prefix}] \u6B63\u5728\u5206\u79BB screen\u2026`);
|
|
238
|
+
try {
|
|
239
|
+
await executeDetached(server);
|
|
240
|
+
await session.send(`\u2705 [${prefix}] screen \u5DF2\u5206\u79BB\u3002`);
|
|
241
|
+
} catch (e) {
|
|
242
|
+
logger.warn("\u5206\u79BBscreen\u5931\u8D25", { server: prefix, error: e?.message || String(e) });
|
|
243
|
+
await session.send(`\u274C [${prefix}] \u5206\u79BB\u5931\u8D25\uFF1A${e?.message || String(e)}`);
|
|
107
244
|
}
|
|
108
245
|
});
|
|
246
|
+
ctx.command(`${prefix}.stat`, "\u67E5\u770B\u670D\u52A1\u5668\u8FD0\u884C\u72B6\u6001\uFF08GC \u7B49\uFF09").userFields(["authority"]).action(async ({ session }) => {
|
|
247
|
+
const err = checkAuth(session);
|
|
248
|
+
if (err === void 0) return;
|
|
249
|
+
if (err) return err;
|
|
250
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
251
|
+
return execShellCmd(session, "stat", "stat");
|
|
252
|
+
});
|
|
253
|
+
ctx.command(`${prefix}.reloadconf`, "\u91CD\u8F7D\u670D\u52A1\u5668\u914D\u7F6E\u6587\u4EF6").alias(`${prefix}.r`).userFields(["authority"]).action(async ({ session }) => {
|
|
254
|
+
const err = checkAuth(session);
|
|
255
|
+
if (err === void 0) return;
|
|
256
|
+
if (err) return err;
|
|
257
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
258
|
+
return execShellCmd(session, "reloadconf", "reloadconf");
|
|
259
|
+
});
|
|
260
|
+
ctx.command(`${prefix}.lsplayer`, "\u67E5\u770B\u6240\u6709\u5728\u7EBF\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }) => {
|
|
261
|
+
const err = checkAuth(session);
|
|
262
|
+
if (err === void 0) return;
|
|
263
|
+
if (err) return err;
|
|
264
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
265
|
+
return execShellCmd(session, "lsplayer", "lsplayer");
|
|
266
|
+
});
|
|
267
|
+
ctx.command(`${prefix}.lsroom [id]`, "\u67E5\u770B\u8FD0\u884C\u4E2D\u7684\u623F\u95F4\uFF08\u53EF\u6307\u5B9A\u623F\u95F4ID\u67E5\u770B\u8BE6\u60C5\uFF09").userFields(["authority"]).action(async ({ session }, id) => {
|
|
268
|
+
const err = checkAuth(session);
|
|
269
|
+
if (err === void 0) return;
|
|
270
|
+
if (err) return err;
|
|
271
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
272
|
+
const cmd = id ? `lsroom ${id}` : "lsroom";
|
|
273
|
+
return execShellCmd(session, cmd, cmd);
|
|
274
|
+
});
|
|
275
|
+
ctx.command(`${prefix}.msg <message:text>`, "\u5411\u5168\u4F53\u73A9\u5BB6\u53D1\u9001\u516C\u544A").alias(`${prefix}.m`).userFields(["authority"]).action(async ({ session }, message) => {
|
|
276
|
+
const err = checkAuth(session);
|
|
277
|
+
if (err === void 0) return;
|
|
278
|
+
if (err) return err;
|
|
279
|
+
await session.send(`\u{1F50C} [${prefix}] \u53D1\u9001\u4E2D\u2026`);
|
|
280
|
+
return execShellCmd(session, `msg ${message}`, "msg");
|
|
281
|
+
});
|
|
282
|
+
ctx.command(`${prefix}.msgroom <roomId> <message:text>`, "\u5411\u6307\u5B9A\u623F\u95F4\u53D1\u9001\u6D88\u606F").alias(`${prefix}.mr`).userFields(["authority"]).action(async ({ session }, roomId, message) => {
|
|
283
|
+
const err = checkAuth(session);
|
|
284
|
+
if (err === void 0) return;
|
|
285
|
+
if (err) return err;
|
|
286
|
+
await session.send(`\u{1F50C} [${prefix}] \u53D1\u9001\u4E2D\u2026`);
|
|
287
|
+
return execShellCmd(session, `msgroom ${roomId} ${message}`, "msgroom");
|
|
288
|
+
});
|
|
289
|
+
ctx.command(`${prefix}.kick <name:text>`, "\u8E22\u51FA\u6307\u5B9A\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
290
|
+
const err = checkAuth(session);
|
|
291
|
+
if (err === void 0) return;
|
|
292
|
+
if (err) return err;
|
|
293
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
294
|
+
return execShellCmd(session, `kick ${name2}`, "kick");
|
|
295
|
+
});
|
|
296
|
+
ctx.command(`${prefix}.killroom <roomId>`, "\u8E22\u51FA\u623F\u95F4\u6240\u6709\u4EBA\u5E76\u5E9F\u5F03\u623F\u95F4").userFields(["authority"]).action(async ({ session }, roomId) => {
|
|
297
|
+
const err = checkAuth(session);
|
|
298
|
+
if (err === void 0) return;
|
|
299
|
+
if (err) return err;
|
|
300
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
301
|
+
return execShellCmd(session, `killroom ${roomId}`, "killroom");
|
|
302
|
+
});
|
|
303
|
+
ctx.command(`${prefix}.checklobby`, "\u6E05\u9664\u5927\u5385\u4E2D\u7684\u50F5\u5C38\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }) => {
|
|
304
|
+
const err = checkAuth(session);
|
|
305
|
+
if (err === void 0) return;
|
|
306
|
+
if (err) return err;
|
|
307
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
308
|
+
return execShellCmd(session, "checklobby", "checklobby");
|
|
309
|
+
});
|
|
310
|
+
ctx.command(`${prefix}.ban <name:text>`, "\u5C01\u7981\u6307\u5B9A\u8D26\u53F7 / IP / UUID\uFF08\u53EF\u591A\u4E2A\uFF0C\u7A7A\u683C\u5206\u9694\uFF09").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
311
|
+
const err = checkAuth(session);
|
|
312
|
+
if (err === void 0) return;
|
|
313
|
+
if (err) return err;
|
|
314
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
315
|
+
return execShellCmd(session, `ban ${name2}`, "ban");
|
|
316
|
+
});
|
|
317
|
+
ctx.command(`${prefix}.unban <name:text>`, "\u89E3\u5C01\u6307\u5B9A\u8D26\u53F7\uFF08\u53EF\u591A\u4E2A\uFF09").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
318
|
+
const err = checkAuth(session);
|
|
319
|
+
if (err === void 0) return;
|
|
320
|
+
if (err) return err;
|
|
321
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
322
|
+
return execShellCmd(session, `unban ${name2}`, "unban");
|
|
323
|
+
});
|
|
324
|
+
ctx.command(`${prefix}.banip <name:text>`, "\u5C01\u7981 IP \u5730\u5740").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
325
|
+
const err = checkAuth(session);
|
|
326
|
+
if (err === void 0) return;
|
|
327
|
+
if (err) return err;
|
|
328
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
329
|
+
return execShellCmd(session, `banip ${name2}`, "banip");
|
|
330
|
+
});
|
|
331
|
+
ctx.command(`${prefix}.unbanip <name:text>`, "\u89E3\u5C01 IP \u5730\u5740").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
332
|
+
const err = checkAuth(session);
|
|
333
|
+
if (err === void 0) return;
|
|
334
|
+
if (err) return err;
|
|
335
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
336
|
+
return execShellCmd(session, `unbanip ${name2}`, "unbanip");
|
|
337
|
+
});
|
|
338
|
+
ctx.command(`${prefix}.banuuid <name:text>`, "\u5C01\u7981 UUID").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
339
|
+
const err = checkAuth(session);
|
|
340
|
+
if (err === void 0) return;
|
|
341
|
+
if (err) return err;
|
|
342
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
343
|
+
return execShellCmd(session, `banuuid ${name2}`, "banuuid");
|
|
344
|
+
});
|
|
345
|
+
ctx.command(`${prefix}.unbanuuid <name:text>`, "\u89E3\u5C01 UUID").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
346
|
+
const err = checkAuth(session);
|
|
347
|
+
if (err === void 0) return;
|
|
348
|
+
if (err) return err;
|
|
349
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
350
|
+
return execShellCmd(session, `unbanuuid ${name2}`, "unbanuuid");
|
|
351
|
+
});
|
|
352
|
+
ctx.command(`${prefix}.tempban <name:text> <duration:text>`, "\u4E34\u65F6\u5C01\u7981\uFF08\u65F6\u957F\u5982 30m/2h/7d/1mo\uFF09").userFields(["authority"]).action(async ({ session }, name2, duration) => {
|
|
353
|
+
const err = checkAuth(session);
|
|
354
|
+
if (err === void 0) return;
|
|
355
|
+
if (err) return err;
|
|
356
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
357
|
+
return execShellCmd(session, `tempban ${name2} ${duration}`, "tempban");
|
|
358
|
+
});
|
|
359
|
+
ctx.command(`${prefix}.tempmute <name:text> <duration:text>`, "\u4E34\u65F6\u7981\u8A00\uFF08\u65F6\u957F\u5982 30m/2h/7d/1mo\uFF09").userFields(["authority"]).action(async ({ session }, name2, duration) => {
|
|
360
|
+
const err = checkAuth(session);
|
|
361
|
+
if (err === void 0) return;
|
|
362
|
+
if (err) return err;
|
|
363
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
364
|
+
return execShellCmd(session, `tempmute ${name2} ${duration}`, "tempmute");
|
|
365
|
+
});
|
|
366
|
+
ctx.command(`${prefix}.unmute <name:text>`, "\u89E3\u9664\u7981\u8A00\uFF08\u53EF\u591A\u4E2A\uFF09").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
367
|
+
const err = checkAuth(session);
|
|
368
|
+
if (err === void 0) return;
|
|
369
|
+
if (err) return err;
|
|
370
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
371
|
+
return execShellCmd(session, `unmute ${name2}`, "unmute");
|
|
372
|
+
});
|
|
373
|
+
ctx.command(`${prefix}.whitelist <rest:text>`, "\u7BA1\u7406\u767D\u540D\u5355\uFF08add/remove \u540D\u5355\uFF09").userFields(["authority"]).action(async ({ session }, rest) => {
|
|
374
|
+
const err = checkAuth(session);
|
|
375
|
+
if (err === void 0) return;
|
|
376
|
+
if (err) return err;
|
|
377
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
378
|
+
return execShellCmd(session, `whitelist ${rest}`, "whitelist");
|
|
379
|
+
});
|
|
380
|
+
ctx.command(`${prefix}.resetpassword <name:text>`, "\u91CD\u7F6E\u8D26\u53F7\u5BC6\u7801\u4E3A 1234").alias(`${prefix}.rp`).userFields(["authority"]).action(async ({ session }, name2) => {
|
|
381
|
+
const err = checkAuth(session);
|
|
382
|
+
if (err === void 0) return;
|
|
383
|
+
if (err) return err;
|
|
384
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
385
|
+
return execShellCmd(session, `resetpassword ${name2}`, "resetpassword");
|
|
386
|
+
});
|
|
387
|
+
ctx.command(`${prefix}.install <url:text>`, "\u4ECE URL \u5B89\u88C5\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, url) => {
|
|
388
|
+
const err = checkAuth(session);
|
|
389
|
+
if (err === void 0) return;
|
|
390
|
+
if (err) return err;
|
|
391
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
392
|
+
return execShellCmd(session, `install ${url}`, "install");
|
|
393
|
+
});
|
|
394
|
+
ctx.command(`${prefix}.remove <name:text>`, "\u79FB\u9664\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
395
|
+
const err = checkAuth(session);
|
|
396
|
+
if (err === void 0) return;
|
|
397
|
+
if (err) return err;
|
|
398
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
399
|
+
return execShellCmd(session, `remove ${name2}`, "remove");
|
|
400
|
+
});
|
|
401
|
+
ctx.command(`${prefix}.pkgs`, "\u67E5\u770B\u6240\u6709\u5DF2\u5B89\u88C5\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }) => {
|
|
402
|
+
const err = checkAuth(session);
|
|
403
|
+
if (err === void 0) return;
|
|
404
|
+
if (err) return err;
|
|
405
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
406
|
+
return execShellCmd(session, "pkgs", "pkgs");
|
|
407
|
+
});
|
|
408
|
+
ctx.command(`${prefix}.syncpkgs`, "\u4ECE\u6587\u4EF6\u7CFB\u7EDF\u540C\u6B65\u6269\u5C55\u5305\u5230\u6570\u636E\u5E93").userFields(["authority"]).action(async ({ session }) => {
|
|
409
|
+
const err = checkAuth(session);
|
|
410
|
+
if (err === void 0) return;
|
|
411
|
+
if (err) return err;
|
|
412
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
413
|
+
return execShellCmd(session, "syncpkgs", "syncpkgs");
|
|
414
|
+
});
|
|
415
|
+
ctx.command(`${prefix}.enable <name:text>`, "\u542F\u7528\u6307\u5B9A\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
416
|
+
const err = checkAuth(session);
|
|
417
|
+
if (err === void 0) return;
|
|
418
|
+
if (err) return err;
|
|
419
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
420
|
+
return execShellCmd(session, `enable ${name2}`, "enable");
|
|
421
|
+
});
|
|
422
|
+
ctx.command(`${prefix}.disable <name:text>`, "\u7981\u7528\u6307\u5B9A\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
423
|
+
const err = checkAuth(session);
|
|
424
|
+
if (err === void 0) return;
|
|
425
|
+
if (err) return err;
|
|
426
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
427
|
+
return execShellCmd(session, `disable ${name2}`, "disable");
|
|
428
|
+
});
|
|
109
429
|
}
|
|
110
|
-
function executeUpdate(
|
|
430
|
+
function executeUpdate(server) {
|
|
111
431
|
return new Promise((resolve, reject) => {
|
|
112
432
|
const conn = new Client();
|
|
113
|
-
const totalTimeout =
|
|
433
|
+
const totalTimeout = server.attachDelay + server.updateWait + 3e4;
|
|
114
434
|
const timer = setTimeout(() => {
|
|
115
435
|
try {
|
|
116
436
|
conn.end();
|
|
@@ -159,11 +479,11 @@ function executeUpdate(config) {
|
|
|
159
479
|
(async () => {
|
|
160
480
|
try {
|
|
161
481
|
output += await collectOutput(1e3);
|
|
162
|
-
const screenCmd =
|
|
482
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
163
483
|
` : `screen -r
|
|
164
484
|
`;
|
|
165
|
-
output += await sendAndWait(screenCmd,
|
|
166
|
-
const updateOutput = await sendAndWait(
|
|
485
|
+
output += await sendAndWait(screenCmd, server.attachDelay);
|
|
486
|
+
const updateOutput = await sendAndWait("u\n", server.updateWait);
|
|
167
487
|
output += updateOutput;
|
|
168
488
|
checkError(updateOutput);
|
|
169
489
|
stream.write("d");
|
|
@@ -181,11 +501,122 @@ function executeUpdate(config) {
|
|
|
181
501
|
}
|
|
182
502
|
const cleanOutput = stripAnsi(output);
|
|
183
503
|
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
184
|
-
resolve({
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
504
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
505
|
+
} catch (e) {
|
|
506
|
+
clearTimeout(timer);
|
|
507
|
+
try {
|
|
508
|
+
stream.close();
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
conn.end();
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
reject(e);
|
|
516
|
+
}
|
|
517
|
+
})();
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
conn.on("error", (err) => {
|
|
521
|
+
clearTimeout(timer);
|
|
522
|
+
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
523
|
+
});
|
|
524
|
+
conn.connect({
|
|
525
|
+
host: server.host,
|
|
526
|
+
port: server.port,
|
|
527
|
+
username: server.username,
|
|
528
|
+
password: server.password,
|
|
529
|
+
readyTimeout: 15e3
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
function executeRestart(server) {
|
|
534
|
+
return new Promise((resolve, reject) => {
|
|
535
|
+
const conn = new Client();
|
|
536
|
+
const totalTimeout = server.attachDelay + server.quitWait + server.restartWait + 3e4;
|
|
537
|
+
const timer = setTimeout(() => {
|
|
538
|
+
try {
|
|
539
|
+
conn.end();
|
|
540
|
+
} catch {
|
|
541
|
+
}
|
|
542
|
+
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
543
|
+
}, totalTimeout);
|
|
544
|
+
conn.on("ready", () => {
|
|
545
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
546
|
+
if (err) {
|
|
547
|
+
clearTimeout(timer);
|
|
548
|
+
try {
|
|
549
|
+
conn.end();
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
let output = "";
|
|
556
|
+
let hasError = false;
|
|
557
|
+
const collectOutput = (duration) => {
|
|
558
|
+
return new Promise((resolveCollected) => {
|
|
559
|
+
let buf = "";
|
|
560
|
+
const onData = (data) => {
|
|
561
|
+
buf += data.toString("utf8");
|
|
562
|
+
};
|
|
563
|
+
stream.on("data", onData);
|
|
564
|
+
setTimeout(() => {
|
|
565
|
+
stream.removeListener("data", onData);
|
|
566
|
+
resolveCollected(buf);
|
|
567
|
+
}, duration);
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
const sendAndWait = async (cmd, waitMs) => {
|
|
571
|
+
stream.write(cmd);
|
|
572
|
+
return collectOutput(waitMs);
|
|
573
|
+
};
|
|
574
|
+
const checkError = (text) => {
|
|
575
|
+
const lower = text.toLowerCase();
|
|
576
|
+
if (/error|fail|fatal|exception|拒绝|失败|错误/.test(lower)) {
|
|
577
|
+
if (!/0 errors?|no error/i.test(lower)) {
|
|
578
|
+
hasError = true;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
(async () => {
|
|
583
|
+
try {
|
|
584
|
+
output += await collectOutput(1e3);
|
|
585
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
586
|
+
` : `screen -r
|
|
587
|
+
`;
|
|
588
|
+
output += await sendAndWait(screenCmd, server.attachDelay);
|
|
589
|
+
const quitOutput = await sendAndWait("quit\n", server.quitWait);
|
|
590
|
+
output += quitOutput;
|
|
591
|
+
checkError(quitOutput);
|
|
592
|
+
const reSeq = parseRestartSequence(server.restartSequence);
|
|
593
|
+
if (reSeq.length > 0) {
|
|
594
|
+
for (const cmd of reSeq) {
|
|
595
|
+
stream.write(cmd + "\n");
|
|
596
|
+
output += await collectOutput(1e3);
|
|
597
|
+
}
|
|
598
|
+
output += await collectOutput(server.restartWait);
|
|
599
|
+
} else {
|
|
600
|
+
const restartOutput = await sendAndWait("\x1B[A\n", server.restartWait);
|
|
601
|
+
output += restartOutput;
|
|
602
|
+
checkError(restartOutput);
|
|
603
|
+
}
|
|
604
|
+
stream.write("d");
|
|
605
|
+
output += await collectOutput(1e3);
|
|
606
|
+
stream.write("exit\n");
|
|
607
|
+
output += await collectOutput(500);
|
|
608
|
+
clearTimeout(timer);
|
|
609
|
+
try {
|
|
610
|
+
stream.close();
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
conn.end();
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
const cleanOutput = stripAnsi(output);
|
|
618
|
+
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
619
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
189
620
|
} catch (e) {
|
|
190
621
|
clearTimeout(timer);
|
|
191
622
|
try {
|
|
@@ -206,14 +637,247 @@ function executeUpdate(config) {
|
|
|
206
637
|
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
207
638
|
});
|
|
208
639
|
conn.connect({
|
|
209
|
-
host:
|
|
210
|
-
port:
|
|
211
|
-
username:
|
|
212
|
-
password:
|
|
640
|
+
host: server.host,
|
|
641
|
+
port: server.port,
|
|
642
|
+
username: server.username,
|
|
643
|
+
password: server.password,
|
|
213
644
|
readyTimeout: 15e3
|
|
214
645
|
});
|
|
215
646
|
});
|
|
216
647
|
}
|
|
648
|
+
function executeDetached(server) {
|
|
649
|
+
return new Promise((resolve, reject) => {
|
|
650
|
+
const conn = new Client();
|
|
651
|
+
const totalTimeout = server.attachDelay + 15e3;
|
|
652
|
+
const timer = setTimeout(() => {
|
|
653
|
+
try {
|
|
654
|
+
conn.end();
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
658
|
+
}, totalTimeout);
|
|
659
|
+
conn.on("ready", () => {
|
|
660
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
661
|
+
if (err) {
|
|
662
|
+
clearTimeout(timer);
|
|
663
|
+
try {
|
|
664
|
+
conn.end();
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const collectOutput = (duration) => {
|
|
671
|
+
return new Promise((resolveCollected) => {
|
|
672
|
+
let buf = "";
|
|
673
|
+
const onData = (data) => {
|
|
674
|
+
buf += data.toString("utf8");
|
|
675
|
+
};
|
|
676
|
+
stream.on("data", onData);
|
|
677
|
+
setTimeout(() => {
|
|
678
|
+
stream.removeListener("data", onData);
|
|
679
|
+
resolveCollected(buf);
|
|
680
|
+
}, duration);
|
|
681
|
+
});
|
|
682
|
+
};
|
|
683
|
+
(async () => {
|
|
684
|
+
try {
|
|
685
|
+
await collectOutput(500);
|
|
686
|
+
const detachCmd = server.screenSession ? `screen -d ${server.screenSession}
|
|
687
|
+
` : `screen -d
|
|
688
|
+
`;
|
|
689
|
+
stream.write(detachCmd);
|
|
690
|
+
await collectOutput(1e3);
|
|
691
|
+
stream.write("exit\n");
|
|
692
|
+
await collectOutput(300);
|
|
693
|
+
clearTimeout(timer);
|
|
694
|
+
try {
|
|
695
|
+
stream.close();
|
|
696
|
+
} catch {
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
conn.end();
|
|
700
|
+
} catch {
|
|
701
|
+
}
|
|
702
|
+
resolve();
|
|
703
|
+
} catch (e) {
|
|
704
|
+
clearTimeout(timer);
|
|
705
|
+
try {
|
|
706
|
+
stream.close();
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
conn.end();
|
|
711
|
+
} catch {
|
|
712
|
+
}
|
|
713
|
+
reject(e);
|
|
714
|
+
}
|
|
715
|
+
})();
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
conn.on("error", (err) => {
|
|
719
|
+
clearTimeout(timer);
|
|
720
|
+
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
721
|
+
});
|
|
722
|
+
conn.connect({
|
|
723
|
+
host: server.host,
|
|
724
|
+
port: server.port,
|
|
725
|
+
username: server.username,
|
|
726
|
+
password: server.password,
|
|
727
|
+
readyTimeout: 15e3
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function executeShellCommand(shellCmd, server) {
|
|
732
|
+
return new Promise((resolve, reject) => {
|
|
733
|
+
const conn = new Client();
|
|
734
|
+
const totalTimeout = server.attachDelay + server.commandWait + 2e4;
|
|
735
|
+
const timer = setTimeout(() => {
|
|
736
|
+
try {
|
|
737
|
+
conn.end();
|
|
738
|
+
} catch {
|
|
739
|
+
}
|
|
740
|
+
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
741
|
+
}, totalTimeout);
|
|
742
|
+
conn.on("ready", () => {
|
|
743
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
744
|
+
if (err) {
|
|
745
|
+
clearTimeout(timer);
|
|
746
|
+
try {
|
|
747
|
+
conn.end();
|
|
748
|
+
} catch {
|
|
749
|
+
}
|
|
750
|
+
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
let output = "";
|
|
754
|
+
let hasError = false;
|
|
755
|
+
const collectOutput = (duration) => {
|
|
756
|
+
return new Promise((resolveCollected) => {
|
|
757
|
+
let buf = "";
|
|
758
|
+
const onData = (data) => {
|
|
759
|
+
buf += data.toString("utf8");
|
|
760
|
+
};
|
|
761
|
+
stream.on("data", onData);
|
|
762
|
+
setTimeout(() => {
|
|
763
|
+
stream.removeListener("data", onData);
|
|
764
|
+
resolveCollected(buf);
|
|
765
|
+
}, duration);
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
const checkError = (text) => {
|
|
769
|
+
const lower = text.toLowerCase();
|
|
770
|
+
if (/error|fail|fatal|exception|拒绝|失败|错误/.test(lower)) {
|
|
771
|
+
if (!/0 errors?|no error/i.test(lower)) {
|
|
772
|
+
hasError = true;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
(async () => {
|
|
777
|
+
try {
|
|
778
|
+
output += await collectOutput(500);
|
|
779
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
780
|
+
` : `screen -r
|
|
781
|
+
`;
|
|
782
|
+
output += await collectOutput(server.attachDelay);
|
|
783
|
+
stream.write(screenCmd);
|
|
784
|
+
output += await collectOutput(server.attachDelay);
|
|
785
|
+
stream.write(shellCmd + "\n");
|
|
786
|
+
const cmdOutput = await collectOutput(server.commandWait);
|
|
787
|
+
output += cmdOutput;
|
|
788
|
+
checkError(cmdOutput);
|
|
789
|
+
stream.write("d");
|
|
790
|
+
output += await collectOutput(500);
|
|
791
|
+
stream.write("exit\n");
|
|
792
|
+
output += await collectOutput(300);
|
|
793
|
+
clearTimeout(timer);
|
|
794
|
+
try {
|
|
795
|
+
stream.close();
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
conn.end();
|
|
800
|
+
} catch {
|
|
801
|
+
}
|
|
802
|
+
const cleanOutput = stripAnsi(output);
|
|
803
|
+
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
804
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
805
|
+
} catch (e) {
|
|
806
|
+
clearTimeout(timer);
|
|
807
|
+
try {
|
|
808
|
+
stream.close();
|
|
809
|
+
} catch {
|
|
810
|
+
}
|
|
811
|
+
try {
|
|
812
|
+
conn.end();
|
|
813
|
+
} catch {
|
|
814
|
+
}
|
|
815
|
+
reject(e);
|
|
816
|
+
}
|
|
817
|
+
})();
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
conn.on("error", (err) => {
|
|
821
|
+
clearTimeout(timer);
|
|
822
|
+
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
823
|
+
});
|
|
824
|
+
conn.connect({
|
|
825
|
+
host: server.host,
|
|
826
|
+
port: server.port,
|
|
827
|
+
username: server.username,
|
|
828
|
+
password: server.password,
|
|
829
|
+
readyTimeout: 15e3
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function parseRestartSequence(sequence) {
|
|
834
|
+
if (!sequence || !sequence.trim()) return [];
|
|
835
|
+
const lines = sequence.split("\n");
|
|
836
|
+
const commands = [];
|
|
837
|
+
for (const line of lines) {
|
|
838
|
+
const trimmed = line.trim();
|
|
839
|
+
if (trimmed) {
|
|
840
|
+
commands.push(trimmed);
|
|
841
|
+
} else {
|
|
842
|
+
commands.push("");
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return commands;
|
|
846
|
+
}
|
|
847
|
+
function smartCleanOutput(text, shellCmd) {
|
|
848
|
+
let result = text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[()][AB012]/g, "").replace(/\x1b\[\?[0-9]+[hl]/g, "").replace(/\r/g, "");
|
|
849
|
+
const lines = result.split("\n");
|
|
850
|
+
const isNoise = (line) => {
|
|
851
|
+
const trimmed = line.trim();
|
|
852
|
+
if (!trimmed) return true;
|
|
853
|
+
if (/\[detached|\[screen|\[remote/i.test(trimmed)) return true;
|
|
854
|
+
if (/^\d{2}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/.test(trimmed)) return true;
|
|
855
|
+
if ((trimmed.match(/█/g) || []).length >= 5) return true;
|
|
856
|
+
if ((trimmed.match(/═/g) || []).length >= 5) return true;
|
|
857
|
+
if (/^(Git|Doc|Version|Build|Commit):/i.test(trimmed)) return true;
|
|
858
|
+
if (/^\w+@[\w-]+ in ~/.test(trimmed)) return true;
|
|
859
|
+
if (/^(Free|Open|Flexible)/i.test(trimmed) && trimmed.length < 20) return true;
|
|
860
|
+
if (/^\S+>\s*$/.test(trimmed)) return true;
|
|
861
|
+
if (/^\s*[#$%]\s*$/.test(trimmed)) return true;
|
|
862
|
+
if (/^\s*>\s*$/.test(trimmed)) return true;
|
|
863
|
+
if (trimmed.startsWith("fk-asio>")) return true;
|
|
864
|
+
if (/^(exit|logout|Ctrl-a d)\s*$/i.test(trimmed)) return true;
|
|
865
|
+
if (trimmed.startsWith("[?") || trimmed.startsWith("[>")) return true;
|
|
866
|
+
return false;
|
|
867
|
+
};
|
|
868
|
+
const kept = [];
|
|
869
|
+
for (const line of lines) {
|
|
870
|
+
if (!isNoise(line)) {
|
|
871
|
+
kept.push(line);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
result = kept.join("\n").trim();
|
|
875
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
876
|
+
if (result.length > 1500) {
|
|
877
|
+
result = result.slice(-1500) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
|
|
878
|
+
}
|
|
879
|
+
return result || "(\u65E0\u8F93\u51FA)";
|
|
880
|
+
}
|
|
217
881
|
function filterSensitive(text) {
|
|
218
882
|
return text.replace(/\/home\/[^/\s]+/g, "/home/***").replace(/\/root\//g, "/root/***/").replace(/(token|secret|password|passwd|key|apikey)\s*[=:]\s*\S+/gi, "$1=***").replace(/\b(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}\b/g, "$1.$2.*.*");
|
|
219
883
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-freekill-u",
|
|
3
3
|
"description": "新月杀远程更新 — 通过 SSH 连接服务器自动进入 screen 会话执行更新",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"lib"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"scripts": {},
|
|
12
11
|
"keywords": [
|
|
13
12
|
"chatbot",
|
|
14
13
|
"koishi",
|