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.
- package/lib/index.js +197 -0
- package/package.json +32 -0
- 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
|
+
}
|