koishi-plugin-freekill-u 1.0.2 → 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 +199 -20
- package/package.json +1 -2
package/lib/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var Config = import_koishi.Schema.object({
|
|
|
36
36
|
username: import_koishi.Schema.string().description("SSH \u7528\u6237\u540D").default(""),
|
|
37
37
|
password: import_koishi.Schema.string().role("secret").description("SSH \u5BC6\u7801").default(""),
|
|
38
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
|
-
|
|
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
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
41
|
attachDelay: import_koishi.Schema.number().description("screen -r \u540E\u7B49\u5F85 attach \u7684\u6BEB\u79D2\u6570").default(3e3).min(500).max(3e4),
|
|
42
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),
|
|
@@ -60,6 +60,17 @@ var usage = `
|
|
|
60
60
|
- **SSH \u4FE1\u606F**\uFF1A\u4E3B\u673A\u3001\u7AEF\u53E3\u3001\u7528\u6237\u540D\u3001\u5BC6\u7801
|
|
61
61
|
- **screen \u4F1A\u8BDD**\uFF1A\u6709\u591A\u4E2A screen \u65F6\u586B\u5199\u4F1A\u8BDD\u6807\u8BC6
|
|
62
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
|
|
64
|
+
|
|
65
|
+
## \u91CD\u542F\u5E8F\u5217\u793A\u4F8B
|
|
66
|
+
|
|
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
|
|
63
74
|
|
|
64
75
|
## \u547D\u4EE4\u5217\u8868
|
|
65
76
|
|
|
@@ -68,9 +79,10 @@ var usage = `
|
|
|
68
79
|
| \u547D\u4EE4 | \u522B\u540D | \u8BF4\u660E |
|
|
69
80
|
|------|------|------|
|
|
70
81
|
| \`\u670D\u52A1\u5668.u\` | \u2014 | \u8FDC\u7A0B\u66F4\u65B0\u670D\u52A1\u5668 |
|
|
71
|
-
| \`\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 |
|
|
72
84
|
| \`\u670D\u52A1\u5668.stat\` | \u2014 | \u67E5\u770B\u8FD0\u884C\u72B6\u6001 |
|
|
73
|
-
| \`\u670D\u52A1\u5668.reloadconf\` | \`.r\`
|
|
85
|
+
| \`\u670D\u52A1\u5668.reloadconf\` | \`.r\` | \u91CD\u8F7D\u914D\u7F6E\u6587\u4EF6 |
|
|
74
86
|
| \`\u670D\u52A1\u5668.lsplayer\` | \u2014 | \u67E5\u770B\u5728\u7EBF\u73A9\u5BB6 |
|
|
75
87
|
| \`\u670D\u52A1\u5668.lsroom [id]\` | \u2014 | \u67E5\u770B\u623F\u95F4\u5217\u8868 |
|
|
76
88
|
| \`\u670D\u52A1\u5668.msg <\u5185\u5BB9>\` | \`.m\` | \u53D1\u5168\u4F53\u516C\u544A |
|
|
@@ -106,6 +118,30 @@ QQ \u7FA4\uFF1A1078815356
|
|
|
106
118
|
`;
|
|
107
119
|
function apply(ctx, config) {
|
|
108
120
|
const logger = ctx.logger("freekill-u");
|
|
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
|
+
});
|
|
109
145
|
for (const server of config.servers) {
|
|
110
146
|
registerServerCommands(ctx, config, server, logger);
|
|
111
147
|
}
|
|
@@ -130,18 +166,11 @@ function registerServerCommands(ctx, config, server, logger) {
|
|
|
130
166
|
try {
|
|
131
167
|
const result = await executeShellCommand(shellCmd, server);
|
|
132
168
|
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
133
|
-
const 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`;
|
|
140
|
-
}
|
|
169
|
+
const output = smartCleanOutput(result.output, shellCmd);
|
|
141
170
|
return `\u{1F4CB} [${prefix}] ${label}
|
|
142
171
|
\u23F1 ${elapsed}s
|
|
143
172
|
|
|
144
|
-
${output
|
|
173
|
+
${output}`;
|
|
145
174
|
} catch (e) {
|
|
146
175
|
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
147
176
|
logger.warn("Shell\u547D\u4EE4\u5931\u8D25", { server: prefix, cmd: shellCmd, error: e?.message || String(e) });
|
|
@@ -175,7 +204,7 @@ ${output}`);
|
|
|
175
204
|
await session.send(`\u274C [${prefix}] \u66F4\u65B0\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
176
205
|
}
|
|
177
206
|
});
|
|
178
|
-
ctx.command(`${prefix}.
|
|
207
|
+
ctx.command(`${prefix}.reboot`, `\u91CD\u542F ${prefix} \u670D\u52A1\u5668`).userFields(["authority"]).action(async ({ session }) => {
|
|
179
208
|
const err = checkAuth(session);
|
|
180
209
|
if (err === void 0) return;
|
|
181
210
|
if (err) return err;
|
|
@@ -201,6 +230,19 @@ ${output}`);
|
|
|
201
230
|
await session.send(`\u274C [${prefix}] \u91CD\u542F\u5931\u8D25\uFF08${elapsed}s\uFF09\uFF1A${e?.message || String(e)}`);
|
|
202
231
|
}
|
|
203
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)}`);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
204
246
|
ctx.command(`${prefix}.stat`, "\u67E5\u770B\u670D\u52A1\u5668\u8FD0\u884C\u72B6\u6001\uFF08GC \u7B49\uFF09").userFields(["authority"]).action(async ({ session }) => {
|
|
205
247
|
const err = checkAuth(session);
|
|
206
248
|
if (err === void 0) return;
|
|
@@ -208,7 +250,7 @@ ${output}`);
|
|
|
208
250
|
await session.send(`\u{1F50C} [${prefix}] \u67E5\u8BE2\u4E2D\u2026`);
|
|
209
251
|
return execShellCmd(session, "stat", "stat");
|
|
210
252
|
});
|
|
211
|
-
ctx.command(`${prefix}.reloadconf`, "\u91CD\u8F7D\u670D\u52A1\u5668\u914D\u7F6E\u6587\u4EF6").userFields(["authority"]).action(async ({ session }) => {
|
|
253
|
+
ctx.command(`${prefix}.reloadconf`, "\u91CD\u8F7D\u670D\u52A1\u5668\u914D\u7F6E\u6587\u4EF6").alias(`${prefix}.r`).userFields(["authority"]).action(async ({ session }) => {
|
|
212
254
|
const err = checkAuth(session);
|
|
213
255
|
if (err === void 0) return;
|
|
214
256
|
if (err) return err;
|
|
@@ -441,7 +483,7 @@ function executeUpdate(server) {
|
|
|
441
483
|
` : `screen -r
|
|
442
484
|
`;
|
|
443
485
|
output += await sendAndWait(screenCmd, server.attachDelay);
|
|
444
|
-
const updateOutput = await sendAndWait(
|
|
486
|
+
const updateOutput = await sendAndWait("u\n", server.updateWait);
|
|
445
487
|
output += updateOutput;
|
|
446
488
|
checkError(updateOutput);
|
|
447
489
|
stream.write("d");
|
|
@@ -547,9 +589,18 @@ function executeRestart(server) {
|
|
|
547
589
|
const quitOutput = await sendAndWait("quit\n", server.quitWait);
|
|
548
590
|
output += quitOutput;
|
|
549
591
|
checkError(quitOutput);
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
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
|
+
}
|
|
553
604
|
stream.write("d");
|
|
554
605
|
output += await collectOutput(1e3);
|
|
555
606
|
stream.write("exit\n");
|
|
@@ -594,6 +645,89 @@ function executeRestart(server) {
|
|
|
594
645
|
});
|
|
595
646
|
});
|
|
596
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
|
+
}
|
|
597
731
|
function executeShellCommand(shellCmd, server) {
|
|
598
732
|
return new Promise((resolve, reject) => {
|
|
599
733
|
const conn = new Client();
|
|
@@ -696,8 +830,53 @@ function executeShellCommand(shellCmd, server) {
|
|
|
696
830
|
});
|
|
697
831
|
});
|
|
698
832
|
}
|
|
699
|
-
function
|
|
700
|
-
|
|
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)";
|
|
701
880
|
}
|
|
702
881
|
function filterSensitive(text) {
|
|
703
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.*.*");
|
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",
|