koishi-plugin-freekill-u 1.0.0 → 1.0.2
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 +606 -89
- package/package.json +1 -1
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,70 +29,366 @@ 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
|
+
updateCommand: import_koishi.Schema.string().description("\u66F4\u65B0\u547D\u4EE4\uFF08\u5728\u65B0\u6708\u6740\u547D\u4EE4\u884C\u8F93\u5165\u7684\u6307\u4EE4\uFF09").default("u"),
|
|
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),
|
|
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)
|
|
41
49
|
});
|
|
42
50
|
var usage = `
|
|
43
|
-
# \u65B0\u6708\u6740\u8FDC\u7A0B\
|
|
51
|
+
# \u65B0\u6708\u6740\u8FDC\u7A0B\u7BA1\u7406\u63D2\u4EF6
|
|
44
52
|
|
|
45
|
-
\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
|
|
54
|
+
\u5371\u9669\u64CD\u4F5C\uFF0C\u8BF7\u52A1\u5FC5\u8BBE\u7F6E\u8DB3\u591F\u7684\u6743\u9650\u7B49\u7EA7\u3002
|
|
46
55
|
|
|
47
|
-
## \
|
|
56
|
+
## \u914D\u7F6E\u65B9\u6CD5
|
|
48
57
|
|
|
49
|
-
\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
|
|
50
63
|
|
|
51
|
-
## \
|
|
64
|
+
## \u547D\u4EE4\u5217\u8868
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
\u6240\u6709\u547D\u4EE4\u683C\u5F0F\uFF1A\`\u670D\u52A1\u5668\u540D.\u5B50\u547D\u4EE4\`
|
|
67
|
+
|
|
68
|
+
| \u547D\u4EE4 | \u522B\u540D | \u8BF4\u660E |
|
|
69
|
+
|------|------|------|
|
|
70
|
+
| \`\u670D\u52A1\u5668.u\` | \u2014 | \u8FDC\u7A0B\u66F4\u65B0\u670D\u52A1\u5668 |
|
|
71
|
+
| \`\u670D\u52A1\u5668.r\` | \u2014 | \u8FDC\u7A0B\u91CD\u542F\u670D\u52A1\u5668 |
|
|
72
|
+
| \`\u670D\u52A1\u5668.stat\` | \u2014 | \u67E5\u770B\u8FD0\u884C\u72B6\u6001 |
|
|
73
|
+
| \`\u670D\u52A1\u5668.reloadconf\` | \`.r\` (\u91CD\u8F7D) | \u91CD\u8F7D\u914D\u7F6E\u6587\u4EF6 |
|
|
74
|
+
| \`\u670D\u52A1\u5668.lsplayer\` | \u2014 | \u67E5\u770B\u5728\u7EBF\u73A9\u5BB6 |
|
|
75
|
+
| \`\u670D\u52A1\u5668.lsroom [id]\` | \u2014 | \u67E5\u770B\u623F\u95F4\u5217\u8868 |
|
|
76
|
+
| \`\u670D\u52A1\u5668.msg <\u5185\u5BB9>\` | \`.m\` | \u53D1\u5168\u4F53\u516C\u544A |
|
|
77
|
+
| \`\u670D\u52A1\u5668.msgroom <\u623F\u95F4> <\u5185\u5BB9>\` | \`.mr\` | \u5411\u623F\u95F4\u53D1\u6D88\u606F |
|
|
78
|
+
| \`\u670D\u52A1\u5668.kick <\u73A9\u5BB6>\` | \u2014 | \u8E22\u51FA\u73A9\u5BB6 |
|
|
79
|
+
| \`\u670D\u52A1\u5668.killroom <\u623F\u95F4>\` | \u2014 | \u5E9F\u5F03\u623F\u95F4 |
|
|
80
|
+
| \`\u670D\u52A1\u5668.checklobby\` | \u2014 | \u6E05\u5927\u5385\u50F5\u5C38 |
|
|
81
|
+
| \`\u670D\u52A1\u5668.ban <\u76EE\u6807>\` | \u2014 | \u5C01\u7981\u8D26\u53F7 |
|
|
82
|
+
| \`\u670D\u52A1\u5668.unban <\u76EE\u6807>\` | \u2014 | \u89E3\u5C01\u8D26\u53F7 |
|
|
83
|
+
| \`\u670D\u52A1\u5668.banip <IP>\` | \u2014 | \u5C01\u7981 IP |
|
|
84
|
+
| \`\u670D\u52A1\u5668.unbanip <IP>\` | \u2014 | \u89E3\u5C01 IP |
|
|
85
|
+
| \`\u670D\u52A1\u5668.banuuid <UUID>\` | \u2014 | \u5C01\u7981 UUID |
|
|
86
|
+
| \`\u670D\u52A1\u5668.unbanuuid <UUID>\` | \u2014 | \u89E3\u5C01 UUID |
|
|
87
|
+
| \`\u670D\u52A1\u5668.tempban <\u76EE\u6807> <\u65F6\u957F>\` | \u2014 | \u4E34\u65F6\u5C01\u7981 |
|
|
88
|
+
| \`\u670D\u52A1\u5668.tempmute <\u76EE\u6807> <\u65F6\u957F>\` | \u2014 | \u4E34\u65F6\u7981\u8A00 |
|
|
89
|
+
| \`\u670D\u52A1\u5668.unmute <\u76EE\u6807>\` | \u2014 | \u89E3\u9664\u7981\u8A00 |
|
|
90
|
+
| \`\u670D\u52A1\u5668.whitelist <\u64CD\u4F5C>\` | \u2014 | \u7BA1\u7406\u767D\u540D\u5355 |
|
|
91
|
+
| \`\u670D\u52A1\u5668.resetpassword <\u8D26\u53F7>\` | \`.rp\` | \u91CD\u7F6E\u5BC6\u7801 |
|
|
92
|
+
| \`\u670D\u52A1\u5668.pkgs\` | \u2014 | \u67E5\u770B\u6269\u5C55\u5305 |
|
|
93
|
+
| \`\u670D\u52A1\u5668.syncpkgs\` | \u2014 | \u540C\u6B65\u6269\u5C55\u5305 |
|
|
94
|
+
| \`\u670D\u52A1\u5668.install <URL>\` | \u2014 | \u5B89\u88C5\u6269\u5C55\u5305 |
|
|
95
|
+
| \`\u670D\u52A1\u5668.remove <\u540D\u79F0>\` | \u2014 | \u79FB\u9664\u6269\u5C55\u5305 |
|
|
96
|
+
| \`\u670D\u52A1\u5668.enable <\u540D\u79F0>\` | \u2014 | \u542F\u7528\u6269\u5C55\u5305 |
|
|
97
|
+
| \`\u670D\u52A1\u5668.disable <\u540D\u79F0>\` | \u2014 | \u7981\u7528\u6269\u5C55\u5305 |
|
|
60
98
|
|
|
61
99
|
## \u6CE8\u610F\u4E8B\u9879
|
|
62
100
|
|
|
63
|
-
- screen \u4F1A\u8BDD\u5FC5\u987B\u662F **detached** \u72B6\u6001
|
|
101
|
+
- screen \u4F1A\u8BDD\u5FC5\u987B\u662F **detached** \u72B6\u6001
|
|
64
102
|
- \u672C\u63D2\u4EF6\u6D89\u53CA\u670D\u52A1\u5668\u5B89\u5168\uFF0C\u8BF7\u52A1\u5FC5\u8BBE\u7F6E\u8DB3\u591F\u7684\u6743\u9650\u7B49\u7EA7
|
|
103
|
+
|
|
104
|
+
## \u53CD\u9988\u6E20\u9053
|
|
105
|
+
QQ \u7FA4\uFF1A1078815356
|
|
65
106
|
`;
|
|
66
107
|
function apply(ctx, config) {
|
|
67
108
|
const logger = ctx.logger("freekill-u");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
for (const server of config.servers) {
|
|
110
|
+
registerServerCommands(ctx, config, server, logger);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function registerServerCommands(ctx, config, server, logger) {
|
|
114
|
+
const prefix = server.name;
|
|
115
|
+
function checkAuth(session) {
|
|
116
|
+
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`;
|
|
117
|
+
if (server.operators && server.operators.trim()) {
|
|
118
|
+
const allowedIds = server.operators.split(/[,,\s]+/).map((s) => s.trim()).filter(Boolean);
|
|
119
|
+
if (allowedIds.length > 0) {
|
|
120
|
+
const userId = session.userId || "";
|
|
121
|
+
if (!allowedIds.includes(userId)) return void 0;
|
|
122
|
+
}
|
|
71
123
|
}
|
|
72
124
|
const userAuthority = session.user?.authority ?? 0;
|
|
73
|
-
if (userAuthority < config.authority) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
125
|
+
if (userAuthority < config.authority) return `\u26A0\uFE0F \u6743\u9650\u4E0D\u8DB3\uFF0C\u9700\u8981\u7B49\u7EA7 ${config.authority} \u4EE5\u4E0A\u3002`;
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
async function execShellCmd(session, shellCmd, label) {
|
|
129
|
+
const startTime = Date.now();
|
|
77
130
|
try {
|
|
78
|
-
const result = await
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
const result = await executeShellCommand(shellCmd, server);
|
|
132
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
133
|
+
const output = cleanShellOutput(result.output);
|
|
134
|
+
if (output.length > 1500) {
|
|
135
|
+
return `\u{1F4CB} [${prefix}] ${label}
|
|
136
|
+
\u23F1 ${elapsed}s
|
|
137
|
+
|
|
138
|
+
${output.slice(-1500)}
|
|
139
|
+
\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09`;
|
|
82
140
|
}
|
|
83
|
-
|
|
141
|
+
return `\u{1F4CB} [${prefix}] ${label}
|
|
142
|
+
\u23F1 ${elapsed}s
|
|
84
143
|
|
|
144
|
+
${output || "(\u65E0\u8F93\u51FA)"}`;
|
|
145
|
+
} catch (e) {
|
|
146
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
147
|
+
logger.warn("Shell\u547D\u4EE4\u5931\u8D25", { server: prefix, cmd: shellCmd, error: e?.message || String(e) });
|
|
148
|
+
return `\u274C [${prefix}] ${label} \u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
ctx.command(`${prefix}`, `${prefix} \u670D\u52A1\u5668\u7BA1\u7406`);
|
|
152
|
+
ctx.command(`${prefix}.u`, `\u66F4\u65B0 ${prefix} \u670D\u52A1\u5668`).userFields(["authority"]).action(async ({ session }) => {
|
|
153
|
+
const err = checkAuth(session);
|
|
154
|
+
if (err === void 0) return;
|
|
155
|
+
if (err) return err;
|
|
156
|
+
const startTime = Date.now();
|
|
157
|
+
await session.send(`\u{1F50C} [${prefix}] \u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u5668\u2026`);
|
|
158
|
+
try {
|
|
159
|
+
const result = await executeUpdate(server);
|
|
160
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
161
|
+
const lines = [`\u2705 [${prefix}] \u66F4\u65B0\u5B8C\u6210\uFF01`, `\u23F1 \u8017\u65F6 ${elapsed} \u79D2`];
|
|
162
|
+
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");
|
|
163
|
+
if (config.debugMode && result.output) {
|
|
164
|
+
let output = filterSensitive(result.output);
|
|
165
|
+
if (output.length > 2e3) output = output.slice(-2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
|
|
166
|
+
lines.push(`
|
|
167
|
+
\u{1F4CB} \u66F4\u65B0\u8F93\u51FA\uFF1A
|
|
85
168
|
${output}`);
|
|
169
|
+
}
|
|
170
|
+
if (result.hasError) logger.warn("\u66F4\u65B0\u53EF\u80FD\u5931\u8D25", { server: prefix, elapsed, lines: result.lineCount });
|
|
171
|
+
await session.send(lines.join("\n"));
|
|
86
172
|
} catch (e) {
|
|
87
|
-
|
|
88
|
-
|
|
173
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
174
|
+
logger.warn("\u66F4\u65B0\u5931\u8D25", { server: prefix, error: e?.message || String(e), elapsed });
|
|
175
|
+
await session.send(`\u274C [${prefix}] \u66F4\u65B0\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
89
176
|
}
|
|
90
177
|
});
|
|
178
|
+
ctx.command(`${prefix}.r`, `\u91CD\u542F ${prefix} \u670D\u52A1\u5668`).userFields(["authority"]).action(async ({ session }) => {
|
|
179
|
+
const err = checkAuth(session);
|
|
180
|
+
if (err === void 0) return;
|
|
181
|
+
if (err) return err;
|
|
182
|
+
const startTime = Date.now();
|
|
183
|
+
await session.send(`\u{1F50C} [${prefix}] \u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u5668\u2026`);
|
|
184
|
+
try {
|
|
185
|
+
const result = await executeRestart(server);
|
|
186
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
187
|
+
const lines = [`\u2705 [${prefix}] \u91CD\u542F\u5B8C\u6210\uFF01`, `\u23F1 \u8017\u65F6 ${elapsed} \u79D2`];
|
|
188
|
+
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");
|
|
189
|
+
if (config.debugMode && result.output) {
|
|
190
|
+
let output = filterSensitive(result.output);
|
|
191
|
+
if (output.length > 2e3) output = output.slice(-2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
|
|
192
|
+
lines.push(`
|
|
193
|
+
\u{1F4CB} \u91CD\u542F\u8F93\u51FA\uFF1A
|
|
194
|
+
${output}`);
|
|
195
|
+
}
|
|
196
|
+
if (result.hasError) logger.warn("\u91CD\u542F\u53EF\u80FD\u5931\u8D25", { server: prefix, elapsed, lines: result.lineCount });
|
|
197
|
+
await session.send(lines.join("\n"));
|
|
198
|
+
} catch (e) {
|
|
199
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
200
|
+
logger.warn("\u91CD\u542F\u5931\u8D25", { server: prefix, error: e?.message || String(e), elapsed });
|
|
201
|
+
await session.send(`\u274C [${prefix}] \u91CD\u542F\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
ctx.command(`${prefix}.stat`, "\u67E5\u770B\u670D\u52A1\u5668\u8FD0\u884C\u72B6\u6001\uFF08GC \u7B49\uFF09").userFields(["authority"]).action(async ({ session }) => {
|
|
205
|
+
const err = checkAuth(session);
|
|
206
|
+
if (err === void 0) return;
|
|
207
|
+
if (err) return err;
|
|
208
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
209
|
+
return execShellCmd(session, "stat", "stat");
|
|
210
|
+
});
|
|
211
|
+
ctx.command(`${prefix}.reloadconf`, "\u91CD\u8F7D\u670D\u52A1\u5668\u914D\u7F6E\u6587\u4EF6").userFields(["authority"]).action(async ({ session }) => {
|
|
212
|
+
const err = checkAuth(session);
|
|
213
|
+
if (err === void 0) return;
|
|
214
|
+
if (err) return err;
|
|
215
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
216
|
+
return execShellCmd(session, "reloadconf", "reloadconf");
|
|
217
|
+
});
|
|
218
|
+
ctx.command(`${prefix}.lsplayer`, "\u67E5\u770B\u6240\u6709\u5728\u7EBF\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }) => {
|
|
219
|
+
const err = checkAuth(session);
|
|
220
|
+
if (err === void 0) return;
|
|
221
|
+
if (err) return err;
|
|
222
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
223
|
+
return execShellCmd(session, "lsplayer", "lsplayer");
|
|
224
|
+
});
|
|
225
|
+
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) => {
|
|
226
|
+
const err = checkAuth(session);
|
|
227
|
+
if (err === void 0) return;
|
|
228
|
+
if (err) return err;
|
|
229
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
230
|
+
const cmd = id ? `lsroom ${id}` : "lsroom";
|
|
231
|
+
return execShellCmd(session, cmd, cmd);
|
|
232
|
+
});
|
|
233
|
+
ctx.command(`${prefix}.msg <message:text>`, "\u5411\u5168\u4F53\u73A9\u5BB6\u53D1\u9001\u516C\u544A").alias(`${prefix}.m`).userFields(["authority"]).action(async ({ session }, message) => {
|
|
234
|
+
const err = checkAuth(session);
|
|
235
|
+
if (err === void 0) return;
|
|
236
|
+
if (err) return err;
|
|
237
|
+
await session.send(`\u{1F50C} [${prefix}] \u53D1\u9001\u4E2D\u2026`);
|
|
238
|
+
return execShellCmd(session, `msg ${message}`, "msg");
|
|
239
|
+
});
|
|
240
|
+
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) => {
|
|
241
|
+
const err = checkAuth(session);
|
|
242
|
+
if (err === void 0) return;
|
|
243
|
+
if (err) return err;
|
|
244
|
+
await session.send(`\u{1F50C} [${prefix}] \u53D1\u9001\u4E2D\u2026`);
|
|
245
|
+
return execShellCmd(session, `msgroom ${roomId} ${message}`, "msgroom");
|
|
246
|
+
});
|
|
247
|
+
ctx.command(`${prefix}.kick <name:text>`, "\u8E22\u51FA\u6307\u5B9A\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
248
|
+
const err = checkAuth(session);
|
|
249
|
+
if (err === void 0) return;
|
|
250
|
+
if (err) return err;
|
|
251
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
252
|
+
return execShellCmd(session, `kick ${name2}`, "kick");
|
|
253
|
+
});
|
|
254
|
+
ctx.command(`${prefix}.killroom <roomId>`, "\u8E22\u51FA\u623F\u95F4\u6240\u6709\u4EBA\u5E76\u5E9F\u5F03\u623F\u95F4").userFields(["authority"]).action(async ({ session }, roomId) => {
|
|
255
|
+
const err = checkAuth(session);
|
|
256
|
+
if (err === void 0) return;
|
|
257
|
+
if (err) return err;
|
|
258
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
259
|
+
return execShellCmd(session, `killroom ${roomId}`, "killroom");
|
|
260
|
+
});
|
|
261
|
+
ctx.command(`${prefix}.checklobby`, "\u6E05\u9664\u5927\u5385\u4E2D\u7684\u50F5\u5C38\u73A9\u5BB6").userFields(["authority"]).action(async ({ session }) => {
|
|
262
|
+
const err = checkAuth(session);
|
|
263
|
+
if (err === void 0) return;
|
|
264
|
+
if (err) return err;
|
|
265
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
266
|
+
return execShellCmd(session, "checklobby", "checklobby");
|
|
267
|
+
});
|
|
268
|
+
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) => {
|
|
269
|
+
const err = checkAuth(session);
|
|
270
|
+
if (err === void 0) return;
|
|
271
|
+
if (err) return err;
|
|
272
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
273
|
+
return execShellCmd(session, `ban ${name2}`, "ban");
|
|
274
|
+
});
|
|
275
|
+
ctx.command(`${prefix}.unban <name:text>`, "\u89E3\u5C01\u6307\u5B9A\u8D26\u53F7\uFF08\u53EF\u591A\u4E2A\uFF09").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
276
|
+
const err = checkAuth(session);
|
|
277
|
+
if (err === void 0) return;
|
|
278
|
+
if (err) return err;
|
|
279
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
280
|
+
return execShellCmd(session, `unban ${name2}`, "unban");
|
|
281
|
+
});
|
|
282
|
+
ctx.command(`${prefix}.banip <name:text>`, "\u5C01\u7981 IP \u5730\u5740").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
283
|
+
const err = checkAuth(session);
|
|
284
|
+
if (err === void 0) return;
|
|
285
|
+
if (err) return err;
|
|
286
|
+
await session.send(`\u{1F50C} [${prefix}] \u6267\u884C\u4E2D\u2026`);
|
|
287
|
+
return execShellCmd(session, `banip ${name2}`, "banip");
|
|
288
|
+
});
|
|
289
|
+
ctx.command(`${prefix}.unbanip <name:text>`, "\u89E3\u5C01 IP \u5730\u5740").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, `unbanip ${name2}`, "unbanip");
|
|
295
|
+
});
|
|
296
|
+
ctx.command(`${prefix}.banuuid <name:text>`, "\u5C01\u7981 UUID").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
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, `banuuid ${name2}`, "banuuid");
|
|
302
|
+
});
|
|
303
|
+
ctx.command(`${prefix}.unbanuuid <name:text>`, "\u89E3\u5C01 UUID").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
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, `unbanuuid ${name2}`, "unbanuuid");
|
|
309
|
+
});
|
|
310
|
+
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) => {
|
|
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, `tempban ${name2} ${duration}`, "tempban");
|
|
316
|
+
});
|
|
317
|
+
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) => {
|
|
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, `tempmute ${name2} ${duration}`, "tempmute");
|
|
323
|
+
});
|
|
324
|
+
ctx.command(`${prefix}.unmute <name:text>`, "\u89E3\u9664\u7981\u8A00\uFF08\u53EF\u591A\u4E2A\uFF09").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, `unmute ${name2}`, "unmute");
|
|
330
|
+
});
|
|
331
|
+
ctx.command(`${prefix}.whitelist <rest:text>`, "\u7BA1\u7406\u767D\u540D\u5355\uFF08add/remove \u540D\u5355\uFF09").userFields(["authority"]).action(async ({ session }, rest) => {
|
|
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, `whitelist ${rest}`, "whitelist");
|
|
337
|
+
});
|
|
338
|
+
ctx.command(`${prefix}.resetpassword <name:text>`, "\u91CD\u7F6E\u8D26\u53F7\u5BC6\u7801\u4E3A 1234").alias(`${prefix}.rp`).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, `resetpassword ${name2}`, "resetpassword");
|
|
344
|
+
});
|
|
345
|
+
ctx.command(`${prefix}.install <url:text>`, "\u4ECE URL \u5B89\u88C5\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, url) => {
|
|
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, `install ${url}`, "install");
|
|
351
|
+
});
|
|
352
|
+
ctx.command(`${prefix}.remove <name:text>`, "\u79FB\u9664\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
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, `remove ${name2}`, "remove");
|
|
358
|
+
});
|
|
359
|
+
ctx.command(`${prefix}.pkgs`, "\u67E5\u770B\u6240\u6709\u5DF2\u5B89\u88C5\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }) => {
|
|
360
|
+
const err = checkAuth(session);
|
|
361
|
+
if (err === void 0) return;
|
|
362
|
+
if (err) return err;
|
|
363
|
+
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
364
|
+
return execShellCmd(session, "pkgs", "pkgs");
|
|
365
|
+
});
|
|
366
|
+
ctx.command(`${prefix}.syncpkgs`, "\u4ECE\u6587\u4EF6\u7CFB\u7EDF\u540C\u6B65\u6269\u5C55\u5305\u5230\u6570\u636E\u5E93").userFields(["authority"]).action(async ({ session }) => {
|
|
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, "syncpkgs", "syncpkgs");
|
|
372
|
+
});
|
|
373
|
+
ctx.command(`${prefix}.enable <name:text>`, "\u542F\u7528\u6307\u5B9A\u6269\u5C55\u5305").userFields(["authority"]).action(async ({ session }, name2) => {
|
|
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, `enable ${name2}`, "enable");
|
|
379
|
+
});
|
|
380
|
+
ctx.command(`${prefix}.disable <name:text>`, "\u7981\u7528\u6307\u5B9A\u6269\u5C55\u5305").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, `disable ${name2}`, "disable");
|
|
386
|
+
});
|
|
91
387
|
}
|
|
92
|
-
function executeUpdate(
|
|
388
|
+
function executeUpdate(server) {
|
|
93
389
|
return new Promise((resolve, reject) => {
|
|
94
390
|
const conn = new Client();
|
|
95
|
-
const totalTimeout =
|
|
391
|
+
const totalTimeout = server.attachDelay + server.updateWait + 3e4;
|
|
96
392
|
const timer = setTimeout(() => {
|
|
97
393
|
try {
|
|
98
394
|
conn.end();
|
|
@@ -101,8 +397,7 @@ function executeUpdate(config, logger) {
|
|
|
101
397
|
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
102
398
|
}, totalTimeout);
|
|
103
399
|
conn.on("ready", () => {
|
|
104
|
-
|
|
105
|
-
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, async (err, stream) => {
|
|
400
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
106
401
|
if (err) {
|
|
107
402
|
clearTimeout(timer);
|
|
108
403
|
try {
|
|
@@ -112,79 +407,301 @@ function executeUpdate(config, logger) {
|
|
|
112
407
|
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
113
408
|
return;
|
|
114
409
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
410
|
+
let output = "";
|
|
411
|
+
let hasError = false;
|
|
412
|
+
const collectOutput = (duration) => {
|
|
413
|
+
return new Promise((resolveCollected) => {
|
|
414
|
+
let buf = "";
|
|
415
|
+
const onData = (data) => {
|
|
416
|
+
buf += data.toString("utf8");
|
|
417
|
+
};
|
|
418
|
+
stream.on("data", onData);
|
|
419
|
+
setTimeout(() => {
|
|
420
|
+
stream.removeListener("data", onData);
|
|
421
|
+
resolveCollected(buf);
|
|
422
|
+
}, duration);
|
|
423
|
+
});
|
|
424
|
+
};
|
|
425
|
+
const sendAndWait = async (cmd, waitMs) => {
|
|
426
|
+
stream.write(cmd);
|
|
427
|
+
return collectOutput(waitMs);
|
|
428
|
+
};
|
|
429
|
+
const checkError = (text) => {
|
|
430
|
+
const lower = text.toLowerCase();
|
|
431
|
+
if (/error|fail|fatal|exception|拒绝|失败|错误/.test(lower)) {
|
|
432
|
+
if (!/0 errors?|no error/i.test(lower)) {
|
|
433
|
+
hasError = true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
(async () => {
|
|
438
|
+
try {
|
|
439
|
+
output += await collectOutput(1e3);
|
|
440
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
136
441
|
` : `screen -r
|
|
137
442
|
`;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
443
|
+
output += await sendAndWait(screenCmd, server.attachDelay);
|
|
444
|
+
const updateOutput = await sendAndWait(server.updateCommand + "\n", server.updateWait);
|
|
445
|
+
output += updateOutput;
|
|
446
|
+
checkError(updateOutput);
|
|
447
|
+
stream.write("d");
|
|
448
|
+
output += await collectOutput(1e3);
|
|
449
|
+
stream.write("exit\n");
|
|
450
|
+
output += await collectOutput(500);
|
|
451
|
+
clearTimeout(timer);
|
|
452
|
+
try {
|
|
453
|
+
stream.close();
|
|
454
|
+
} catch {
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
conn.end();
|
|
458
|
+
} catch {
|
|
459
|
+
}
|
|
460
|
+
const cleanOutput = stripAnsi(output);
|
|
461
|
+
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
462
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
463
|
+
} catch (e) {
|
|
464
|
+
clearTimeout(timer);
|
|
465
|
+
try {
|
|
466
|
+
stream.close();
|
|
467
|
+
} catch {
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
conn.end();
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
reject(e);
|
|
151
474
|
}
|
|
475
|
+
})();
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
conn.on("error", (err) => {
|
|
479
|
+
clearTimeout(timer);
|
|
480
|
+
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
481
|
+
});
|
|
482
|
+
conn.connect({
|
|
483
|
+
host: server.host,
|
|
484
|
+
port: server.port,
|
|
485
|
+
username: server.username,
|
|
486
|
+
password: server.password,
|
|
487
|
+
readyTimeout: 15e3
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
function executeRestart(server) {
|
|
492
|
+
return new Promise((resolve, reject) => {
|
|
493
|
+
const conn = new Client();
|
|
494
|
+
const totalTimeout = server.attachDelay + server.quitWait + server.restartWait + 3e4;
|
|
495
|
+
const timer = setTimeout(() => {
|
|
496
|
+
try {
|
|
497
|
+
conn.end();
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
501
|
+
}, totalTimeout);
|
|
502
|
+
conn.on("ready", () => {
|
|
503
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
504
|
+
if (err) {
|
|
505
|
+
clearTimeout(timer);
|
|
152
506
|
try {
|
|
153
507
|
conn.end();
|
|
154
508
|
} catch {
|
|
155
509
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
510
|
+
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
let output = "";
|
|
514
|
+
let hasError = false;
|
|
515
|
+
const collectOutput = (duration) => {
|
|
516
|
+
return new Promise((resolveCollected) => {
|
|
517
|
+
let buf = "";
|
|
518
|
+
const onData = (data) => {
|
|
519
|
+
buf += data.toString("utf8");
|
|
520
|
+
};
|
|
521
|
+
stream.on("data", onData);
|
|
522
|
+
setTimeout(() => {
|
|
523
|
+
stream.removeListener("data", onData);
|
|
524
|
+
resolveCollected(buf);
|
|
525
|
+
}, duration);
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
const sendAndWait = async (cmd, waitMs) => {
|
|
529
|
+
stream.write(cmd);
|
|
530
|
+
return collectOutput(waitMs);
|
|
531
|
+
};
|
|
532
|
+
const checkError = (text) => {
|
|
533
|
+
const lower = text.toLowerCase();
|
|
534
|
+
if (/error|fail|fatal|exception|拒绝|失败|错误/.test(lower)) {
|
|
535
|
+
if (!/0 errors?|no error/i.test(lower)) {
|
|
536
|
+
hasError = true;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
(async () => {
|
|
160
541
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
542
|
+
output += await collectOutput(1e3);
|
|
543
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
544
|
+
` : `screen -r
|
|
545
|
+
`;
|
|
546
|
+
output += await sendAndWait(screenCmd, server.attachDelay);
|
|
547
|
+
const quitOutput = await sendAndWait("quit\n", server.quitWait);
|
|
548
|
+
output += quitOutput;
|
|
549
|
+
checkError(quitOutput);
|
|
550
|
+
const restartOutput = await sendAndWait("\x1B[A\n", server.restartWait);
|
|
551
|
+
output += restartOutput;
|
|
552
|
+
checkError(restartOutput);
|
|
553
|
+
stream.write("d");
|
|
554
|
+
output += await collectOutput(1e3);
|
|
555
|
+
stream.write("exit\n");
|
|
556
|
+
output += await collectOutput(500);
|
|
557
|
+
clearTimeout(timer);
|
|
558
|
+
try {
|
|
559
|
+
stream.close();
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
conn.end();
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
const cleanOutput = stripAnsi(output);
|
|
567
|
+
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
568
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
569
|
+
} catch (e) {
|
|
570
|
+
clearTimeout(timer);
|
|
571
|
+
try {
|
|
572
|
+
stream.close();
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
conn.end();
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
reject(e);
|
|
163
580
|
}
|
|
581
|
+
})();
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
conn.on("error", (err) => {
|
|
585
|
+
clearTimeout(timer);
|
|
586
|
+
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
587
|
+
});
|
|
588
|
+
conn.connect({
|
|
589
|
+
host: server.host,
|
|
590
|
+
port: server.port,
|
|
591
|
+
username: server.username,
|
|
592
|
+
password: server.password,
|
|
593
|
+
readyTimeout: 15e3
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
function executeShellCommand(shellCmd, server) {
|
|
598
|
+
return new Promise((resolve, reject) => {
|
|
599
|
+
const conn = new Client();
|
|
600
|
+
const totalTimeout = server.attachDelay + server.commandWait + 2e4;
|
|
601
|
+
const timer = setTimeout(() => {
|
|
602
|
+
try {
|
|
603
|
+
conn.end();
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
|
|
607
|
+
}, totalTimeout);
|
|
608
|
+
conn.on("ready", () => {
|
|
609
|
+
conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, (err, stream) => {
|
|
610
|
+
if (err) {
|
|
611
|
+
clearTimeout(timer);
|
|
164
612
|
try {
|
|
165
613
|
conn.end();
|
|
166
614
|
} catch {
|
|
167
615
|
}
|
|
168
|
-
reject(
|
|
616
|
+
reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
|
|
617
|
+
return;
|
|
169
618
|
}
|
|
619
|
+
let output = "";
|
|
620
|
+
let hasError = false;
|
|
621
|
+
const collectOutput = (duration) => {
|
|
622
|
+
return new Promise((resolveCollected) => {
|
|
623
|
+
let buf = "";
|
|
624
|
+
const onData = (data) => {
|
|
625
|
+
buf += data.toString("utf8");
|
|
626
|
+
};
|
|
627
|
+
stream.on("data", onData);
|
|
628
|
+
setTimeout(() => {
|
|
629
|
+
stream.removeListener("data", onData);
|
|
630
|
+
resolveCollected(buf);
|
|
631
|
+
}, duration);
|
|
632
|
+
});
|
|
633
|
+
};
|
|
634
|
+
const checkError = (text) => {
|
|
635
|
+
const lower = text.toLowerCase();
|
|
636
|
+
if (/error|fail|fatal|exception|拒绝|失败|错误/.test(lower)) {
|
|
637
|
+
if (!/0 errors?|no error/i.test(lower)) {
|
|
638
|
+
hasError = true;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
(async () => {
|
|
643
|
+
try {
|
|
644
|
+
output += await collectOutput(500);
|
|
645
|
+
const screenCmd = server.screenSession ? `screen -r ${server.screenSession}
|
|
646
|
+
` : `screen -r
|
|
647
|
+
`;
|
|
648
|
+
output += await collectOutput(server.attachDelay);
|
|
649
|
+
stream.write(screenCmd);
|
|
650
|
+
output += await collectOutput(server.attachDelay);
|
|
651
|
+
stream.write(shellCmd + "\n");
|
|
652
|
+
const cmdOutput = await collectOutput(server.commandWait);
|
|
653
|
+
output += cmdOutput;
|
|
654
|
+
checkError(cmdOutput);
|
|
655
|
+
stream.write("d");
|
|
656
|
+
output += await collectOutput(500);
|
|
657
|
+
stream.write("exit\n");
|
|
658
|
+
output += await collectOutput(300);
|
|
659
|
+
clearTimeout(timer);
|
|
660
|
+
try {
|
|
661
|
+
stream.close();
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
conn.end();
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
const cleanOutput = stripAnsi(output);
|
|
669
|
+
const lines = cleanOutput.split("\n").filter((l) => l.trim()).length;
|
|
670
|
+
resolve({ output: cleanOutput, lineCount: lines, hasError });
|
|
671
|
+
} catch (e) {
|
|
672
|
+
clearTimeout(timer);
|
|
673
|
+
try {
|
|
674
|
+
stream.close();
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
conn.end();
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
reject(e);
|
|
682
|
+
}
|
|
683
|
+
})();
|
|
170
684
|
});
|
|
171
685
|
});
|
|
172
686
|
conn.on("error", (err) => {
|
|
173
687
|
clearTimeout(timer);
|
|
174
688
|
reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
|
|
175
689
|
});
|
|
176
|
-
conn.on("close", () => {
|
|
177
|
-
logger.info("SSH \u8FDE\u63A5\u5DF2\u5173\u95ED");
|
|
178
|
-
});
|
|
179
690
|
conn.connect({
|
|
180
|
-
host:
|
|
181
|
-
port:
|
|
182
|
-
username:
|
|
183
|
-
password:
|
|
691
|
+
host: server.host,
|
|
692
|
+
port: server.port,
|
|
693
|
+
username: server.username,
|
|
694
|
+
password: server.password,
|
|
184
695
|
readyTimeout: 15e3
|
|
185
696
|
});
|
|
186
697
|
});
|
|
187
698
|
}
|
|
699
|
+
function cleanShellOutput(text) {
|
|
700
|
+
return text.replace(/\[detached[^\n]*\n?/gi, "").replace(/\[screen[^\n]*\n?/gi, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
701
|
+
}
|
|
702
|
+
function filterSensitive(text) {
|
|
703
|
+
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.*.*");
|
|
704
|
+
}
|
|
188
705
|
function stripAnsi(text) {
|
|
189
706
|
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[()][AB012]/g, "").replace(/\r/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
190
707
|
}
|