koishi-plugin-freekill-u 1.0.0

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.
Files changed (3) hide show
  1. package/lib/index.js +197 -0
  2. package/package.json +32 -0
  3. package/readme.md +5 -0
package/lib/index.js ADDED
@@ -0,0 +1,197 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name2 in all)
7
+ __defProp(target, name2, { get: all[name2], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var src_exports = {};
21
+ __export(src_exports, {
22
+ Config: () => Config,
23
+ apply: () => apply,
24
+ name: () => name,
25
+ usage: () => usage
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+ var import_koishi = require("koishi");
29
+ var { Client } = require("ssh2");
30
+ var name = "freekill-u";
31
+ var Config = import_koishi.Schema.object({
32
+ host: import_koishi.Schema.string().description("SSH \u670D\u52A1\u5668 IP").default(""),
33
+ port: import_koishi.Schema.number().description("SSH \u7AEF\u53E3").default(22).min(1).max(65535),
34
+ username: import_koishi.Schema.string().description("SSH \u7528\u6237\u540D").default(""),
35
+ password: import_koishi.Schema.string().role("secret").description("SSH \u5BC6\u7801").default(""),
36
+ screenSession: import_koishi.Schema.string().description("screen \u4F1A\u8BDD\u6807\u8BC6\uFF08\u591A\u4E2A screen \u65F6\u586B\u5199\uFF0C\u5982 1234 \u6216 freekill\uFF1B\u7559\u7A7A\u5219\u7528 screen -r \u9ED8\u8BA4\u9009\u62E9\uFF09").default(""),
37
+ 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"),
38
+ attachDelay: import_koishi.Schema.number().description("screen -r \u540E\u7B49\u5F85 attach \u7684\u6BEB\u79D2\u6570").default(3e3).min(500).max(3e4),
39
+ 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),
40
+ authority: import_koishi.Schema.number().description("\u4F7F\u7528\u8BE5\u547D\u4EE4\u6240\u9700\u7684\u6700\u4F4E\u6743\u9650\u7B49\u7EA7").default(4).min(1).max(5)
41
+ });
42
+ var usage = `
43
+ # \u65B0\u6708\u6740\u8FDC\u7A0B\u66F4\u65B0\u63D2\u4EF6
44
+
45
+ \u901A\u8FC7 SSH \u8FDE\u63A5\u670D\u52A1\u5668\uFF0C\u81EA\u52A8\u8FDB\u5165 screen \u4F1A\u8BDD\u6267\u884C\u66F4\u65B0\u547D\u4EE4\u3002
46
+
47
+ ## \u4F7F\u7528\u65B9\u6CD5
48
+
49
+ \u5728\u7FA4\u91CC\u53D1\u9001 **\u66F4\u65B0\u65B0\u6708\u6740** \u5373\u53EF\u89E6\u53D1\u66F4\u65B0\u6D41\u7A0B\u3002
50
+
51
+ ## \u5DE5\u4F5C\u6D41\u7A0B
52
+
53
+ 1. SSH \u8FDE\u63A5\u5230\u670D\u52A1\u5668
54
+ 2. \u6253\u5F00\u5E26 PTY \u7684\u4EA4\u4E92\u5F0F shell
55
+ 3. \u53D1\u9001 \`screen -r [\u4F1A\u8BDDID]\` \u8FDB\u5165\u65B0\u6708\u6740 screen \u4F1A\u8BDD
56
+ 4. \u53D1\u9001\u66F4\u65B0\u547D\u4EE4\uFF08\u9ED8\u8BA4 \`u\`\uFF09
57
+ 5. \u7B49\u5F85\u66F4\u65B0\u8F93\u51FA
58
+ 6. \u53D1\u9001 \`Ctrl-a d\` \u5206\u79BB screen \u4F1A\u8BDD
59
+ 7. \u5173\u95ED SSH \u8FDE\u63A5\uFF0C\u8FD4\u56DE\u66F4\u65B0\u65E5\u5FD7
60
+
61
+ ## \u6CE8\u610F\u4E8B\u9879
62
+
63
+ - screen \u4F1A\u8BDD\u5FC5\u987B\u662F **detached** \u72B6\u6001\uFF0C\u5982\u679C\u5DF2\u88AB\u4EBA attach\uFF0C\u9700\u8981\u5148\u7528 \`screen -d -r\`
64
+ - \u672C\u63D2\u4EF6\u6D89\u53CA\u670D\u52A1\u5668\u5B89\u5168\uFF0C\u8BF7\u52A1\u5FC5\u8BBE\u7F6E\u8DB3\u591F\u7684\u6743\u9650\u7B49\u7EA7
65
+ `;
66
+ function apply(ctx, config) {
67
+ const logger = ctx.logger("freekill-u");
68
+ ctx.command("\u66F4\u65B0\u65B0\u6708\u6740", "\u8FDC\u7A0B\u66F4\u65B0\u65B0\u6708\u6740\u670D\u52A1\u5668").userFields(["authority"]).action(async ({ session }) => {
69
+ if (!config.host || !config.username) {
70
+ return "\u26A0\uFE0F \u8BF7\u5148\u5728\u63D2\u4EF6\u914D\u7F6E\u4E2D\u586B\u5199 SSH \u8FDE\u63A5\u4FE1\u606F\u3002";
71
+ }
72
+ const userAuthority = session.user?.authority ?? 0;
73
+ if (userAuthority < config.authority) {
74
+ return `\u26A0\uFE0F \u6743\u9650\u4E0D\u8DB3\uFF0C\u9700\u8981\u7B49\u7EA7 ${config.authority} \u4EE5\u4E0A\u3002`;
75
+ }
76
+ await session.send("\u{1F50C} \u6B63\u5728\u8FDE\u63A5\u670D\u52A1\u5668\u2026");
77
+ try {
78
+ const result = await executeUpdate(config, logger);
79
+ let output = result.trim();
80
+ if (output.length > 2e3) {
81
+ output = output.slice(0, 2e3) + "\n\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF09";
82
+ }
83
+ await session.send(`\u2705 \u66F4\u65B0\u5B8C\u6210\uFF01
84
+
85
+ ${output}`);
86
+ } catch (e) {
87
+ logger.error("\u66F4\u65B0\u5931\u8D25", e);
88
+ await session.send(`\u274C \u66F4\u65B0\u5931\u8D25\uFF1A${e?.message || String(e)}`);
89
+ }
90
+ });
91
+ }
92
+ function executeUpdate(config, logger) {
93
+ return new Promise((resolve, reject) => {
94
+ const conn = new Client();
95
+ const totalTimeout = config.attachDelay + config.updateWait + 3e4;
96
+ const timer = setTimeout(() => {
97
+ try {
98
+ conn.end();
99
+ } catch {
100
+ }
101
+ reject(new Error("SSH \u64CD\u4F5C\u8D85\u65F6"));
102
+ }, totalTimeout);
103
+ conn.on("ready", () => {
104
+ logger.info("SSH \u5DF2\u8FDE\u63A5");
105
+ conn.shell({ term: "xterm-256color", cols: 200, rows: 50 }, async (err, stream) => {
106
+ if (err) {
107
+ clearTimeout(timer);
108
+ try {
109
+ conn.end();
110
+ } catch {
111
+ }
112
+ reject(new Error(`\u6253\u5F00 shell \u5931\u8D25: ${err.message}`));
113
+ return;
114
+ }
115
+ try {
116
+ let output = "";
117
+ const collectOutput = (duration) => {
118
+ return new Promise((resolveCollected) => {
119
+ let buf = "";
120
+ const onData = (data) => {
121
+ buf += data.toString("utf8");
122
+ };
123
+ stream.on("data", onData);
124
+ setTimeout(() => {
125
+ stream.removeListener("data", onData);
126
+ resolveCollected(buf);
127
+ }, duration);
128
+ });
129
+ };
130
+ const sendAndWait = async (cmd, waitMs) => {
131
+ stream.write(cmd);
132
+ return collectOutput(waitMs);
133
+ };
134
+ output += await collectOutput(1e3);
135
+ const screenCmd = config.screenSession ? `screen -r ${config.screenSession}
136
+ ` : `screen -r
137
+ `;
138
+ logger.info("\u53D1\u9001\u547D\u4EE4: screen -r");
139
+ output += await sendAndWait(screenCmd, config.attachDelay);
140
+ logger.info(`\u53D1\u9001\u66F4\u65B0\u547D\u4EE4: ${config.updateCommand}`);
141
+ output += await sendAndWait(config.updateCommand + "\n", config.updateWait);
142
+ logger.info("\u5206\u79BB screen \u4F1A\u8BDD");
143
+ stream.write("d");
144
+ output += await collectOutput(1e3);
145
+ stream.write("exit\n");
146
+ output += await collectOutput(500);
147
+ clearTimeout(timer);
148
+ try {
149
+ stream.close();
150
+ } catch {
151
+ }
152
+ try {
153
+ conn.end();
154
+ } catch {
155
+ }
156
+ const cleanOutput = stripAnsi(output);
157
+ resolve(cleanOutput);
158
+ } catch (e) {
159
+ clearTimeout(timer);
160
+ try {
161
+ stream.close();
162
+ } catch {
163
+ }
164
+ try {
165
+ conn.end();
166
+ } catch {
167
+ }
168
+ reject(e);
169
+ }
170
+ });
171
+ });
172
+ conn.on("error", (err) => {
173
+ clearTimeout(timer);
174
+ reject(new Error(`SSH \u8FDE\u63A5\u9519\u8BEF: ${err.message}`));
175
+ });
176
+ conn.on("close", () => {
177
+ logger.info("SSH \u8FDE\u63A5\u5DF2\u5173\u95ED");
178
+ });
179
+ conn.connect({
180
+ host: config.host,
181
+ port: config.port,
182
+ username: config.username,
183
+ password: config.password,
184
+ readyTimeout: 15e3
185
+ });
186
+ });
187
+ }
188
+ function stripAnsi(text) {
189
+ 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
+ }
191
+ // Annotate the CommonJS export names for ESM import in node:
192
+ 0 && (module.exports = {
193
+ Config,
194
+ apply,
195
+ name,
196
+ usage
197
+ });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "koishi-plugin-freekill-u",
3
+ "description": "新月杀远程更新 — 通过 SSH 连接服务器自动进入 screen 会话执行更新",
4
+ "version": "1.0.0",
5
+ "main": "lib/index.js",
6
+ "typings": "lib/index.d.ts",
7
+ "files": [
8
+ "lib"
9
+ ],
10
+ "license": "MIT",
11
+ "scripts": {},
12
+ "keywords": [
13
+ "chatbot",
14
+ "koishi",
15
+ "plugin",
16
+ "freekill",
17
+ "ssh",
18
+ "screen"
19
+ ],
20
+ "peerDependencies": {
21
+ "koishi": "^4.18.7"
22
+ },
23
+ "dependencies": {
24
+ "ssh2": "^1.17.0"
25
+ },
26
+ "koishi": {
27
+ "description": {
28
+ "zh": "新月杀更新 — SSH 远程更新新月杀服务器",
29
+ "en": "FreeKill Update — Remote SSH update for FreeKill server"
30
+ }
31
+ }
32
+ }
package/readme.md ADDED
@@ -0,0 +1,5 @@
1
+ # koishi-plugin-freekill-u
2
+
3
+ [![npm](https://img.shields.io/npm/v/koishi-plugin-freekill-u?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-freekill-u)
4
+
5
+