koishi-plugin-mc-status-bot
Version:
566 lines (558 loc) • 16.7 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Config: () => Config,
apply: () => apply,
inject: () => inject,
name: () => name
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
// src/model/server.ts
var initDataBase = /* @__PURE__ */ __name((ctx) => {
ctx.model.extend(
"mcServerList",
{
id: {
type: "integer",
length: 100
},
name: {
type: "string",
length: 20,
nullable: false
},
groupId: {
type: "string",
length: 20,
nullable: false
},
ip: {
type: "string",
length: 20,
nullable: false,
initial: ""
},
port: {
type: "integer",
length: 5,
initial: 25565
}
},
{
primary: "id",
autoInc: true,
unique: ["name"]
}
);
}, "initDataBase");
// src/utils/ping.ts
var import_varint = __toESM(require("varint"));
var import_net = __toESM(require("net"));
var encoded = Buffer.from(import_varint.default.encode(255));
var defavlueMajorVersion = "1.21";
var defaultProtocol = 767;
function mcPing(options, cb) {
const pingPromise = ping(options);
if (cb) {
pingPromise.then((d) => {
cb(null, d);
}).catch((err) => {
cb(err, null);
});
}
return pingPromise;
}
__name(mcPing, "mcPing");
function encodeString(str) {
const strBuf = Buffer.from(str, "utf8");
const lenBuf = Buffer.from(import_varint.default.encode(strBuf.length));
return Buffer.concat([lenBuf, strBuf]);
}
__name(encodeString, "encodeString");
function encodeVarInt(value) {
return Buffer.from(import_varint.default.encode(value));
}
__name(encodeVarInt, "encodeVarInt");
function createPacket(id, data) {
const idBuf = encodeVarInt(id);
const packetData = Buffer.concat([idBuf, data]);
const lengthBuf = encodeVarInt(packetData.length);
return Buffer.concat([lengthBuf, packetData]);
}
__name(createPacket, "createPacket");
function ping(options) {
const host = options.host || "localhost";
const port = options.port || 25565;
options.majorVersion = options.version || defavlueMajorVersion;
const protocolVersion = options.protocol || defaultProtocol;
const closeTimeout = options.closeTimeout || 20 * 1e3;
return new Promise((resolve, reject) => {
const client = import_net.default.createConnection({
host,
port
});
let buffer = Buffer.alloc(0);
let startTime = null;
const onData = /* @__PURE__ */ __name((chunk) => {
buffer = Buffer.concat([buffer, chunk]);
try {
const length = import_varint.default.decode(buffer);
const offset1 = import_varint.default.decode.bytes;
const packetId = import_varint.default.decode(buffer.slice(offset1));
const offset2 = offset1 + import_varint.default.decode.bytes;
const stringLength = import_varint.default.decode(buffer.slice(offset2));
const offset3 = offset2 + import_varint.default.decode.bytes;
const jsonString = buffer.slice(offset3, offset3 + stringLength).toString();
const status = JSON.parse(jsonString);
const latency = Date.now() - startTime;
client.end();
resolve({
...status,
latency
});
} catch (e) {
}
}, "onData");
client.setTimeout(closeTimeout, () => {
client.destroy();
reject(new Error("连接超时"));
});
client.on("error", reject);
client.on("data", onData);
client.once("connect", () => {
const handshakeData = Buffer.concat([
encodeVarInt(protocolVersion),
// 协议版本
encodeString(host),
// 主机名
Buffer.from([port >> 8 & 255, port & 255]),
// 端口(2 字节)
Buffer.from([1])
// 下一状态:1 = status
]);
const handshakePacket = createPacket(0, handshakeData);
const requestPacket = createPacket(0, Buffer.alloc(0));
startTime = Date.now();
client.write(Buffer.concat([handshakePacket, requestPacket]));
});
});
}
__name(ping, "ping");
// src/utils/index.ts
var varkeys = [
"name",
"list",
"update",
"add",
"delete",
"remove",
"inset",
"upsert",
"set",
"help"
];
var initMcBot = class _initMcBot {
static {
__name(this, "initMcBot");
}
ctx;
static instance;
constructor(ctx) {
this.ctx = ctx;
}
static getInstance(ctx) {
if (!_initMcBot.instance) {
if (!ctx) {
throw new Error("实例初始化失败:缺少ctx");
}
_initMcBot.instance = new _initMcBot(ctx);
}
return _initMcBot.instance;
}
findServer(findArg) {
return this.ctx.database.get("mcServerList", findArg);
}
delteByName(findArg) {
return this.ctx.database.remove("mcServerList", findArg);
}
upsert(server) {
return this.ctx.database.upsert("mcServerList", [server]);
}
async pingServerList(findArg) {
const serverList = await this.findServer(findArg);
const promiseList = [];
let result = [];
for (let item of serverList) {
promiseList.push(
mcPing(
{
host: item.ip,
port: item.port
},
(error, result2) => {
if (error)
throw error;
return Promise.resolve(result2);
}
)
);
}
await Promise.allSettled(promiseList).then((res) => {
res.forEach((resArg, index) => {
if (resArg.status === "fulfilled") {
result.push({
name: serverList[index].name,
address: `${serverList[index].ip}:${serverList[index].port}`,
latency: resArg.value.latency,
...resArg.value
});
} else {
result.push({
name: serverList[index].name,
address: `${serverList[index].ip}:${serverList[index].port}`,
status: {
rejected: true,
...resArg.reason
}
});
}
});
});
return result;
}
async pingOneServer(opt) {
return mcPing({
host: opt.ip,
port: opt.port
});
}
};
var guoupArg = /* @__PURE__ */ __name((groupKeep, arg = {}, obj = {}) => {
if (groupKeep)
return { ...arg, ...obj };
return arg;
}, "guoupArg");
function removeMinecraftFormatting(text) {
if (typeof text !== "string")
return "";
text = text.replace(/§[0-9a-fk-or]/gi, "");
text = text.replace(/<[^>]*>|{[^}]*}|%[^%]+%/g, "");
text = text.replace(
/[\u0000-\u001F\u007F\u0080-\u009F\u200B-\u200D\uFEFF]/g,
""
);
return text.trim();
}
__name(removeMinecraftFormatting, "removeMinecraftFormatting");
// src/utils/mcFormat.ts
var transformText = /* @__PURE__ */ __name((obj, opt) => {
let str = "";
const handlDescriptionFormatting = opt && opt.descriptionFormatting ? removeMinecraftFormatting : (t) => t;
if (obj.favicon)
str += `<image src="${obj.favicon}"/>`;
if (obj.name)
str = str + `${obj.name}[${obj.address}]
`;
if (obj.description && obj.description.text) {
str = str + `描述:${handlDescriptionFormatting(obj.description.text)}
`;
} else if (typeof obj.description === "string") {
str = str + `描述:${handlDescriptionFormatting(obj.description)}
`;
}
if (obj.latency)
str = str + `延迟:${obj.latency}ms
`;
if (obj.version && obj.version.name)
str = str + `版本:${obj.version.name}
`;
if (obj.players) {
const players = obj.players;
if (players.max)
str += `在线人数:${players.online}/${players.max}
`;
if (players.sample)
str += `当前在线:${players.sample.map((item) => `${item.name}`).join(",")}
`;
}
if (obj.modinfo && obj.modinfo.modList && obj.modinfo.modList.length) {
str = str + `mod数:${obj.modinfo.modList.length}
`;
} else if (obj.forgeData && obj.forgeData.mods && obj.forgeData.mods.length) {
str = str + `mod数:${obj.forgeData.mods.length}
`;
}
if (obj.rejected) {
if (obj.code)
str = str + `请求失败:${obj.code}
`;
if (obj.errno)
str = str + `错误代码:${obj.errno}
`;
str = str + `服务器地址错误或者服务器未启动
`;
}
return str;
}, "transformText");
var mcFormat = /* @__PURE__ */ __name((name2, address, server, options) => {
if (!server)
return "";
return transformText(
{
name: name2,
address,
...server
},
options
);
}, "mcFormat");
var serverListFormat = /* @__PURE__ */ __name((serverList) => {
const list = serverList.map((item) => {
let str = "";
str += `服务器名称: ${item.name}
`;
str += `服务器ip: ${item.ip}
`;
str += `服务器端口: ${item.port}
`;
return str;
});
return list.join("***********************\n");
}, "serverListFormat");
// src/command/index.ts
var registerCommands = /* @__PURE__ */ __name((ctx, config) => {
const mcBot = initMcBot.getInstance();
const { groupKeep } = config;
const mcComand = ctx.command("mc", {
authority: 1
});
mcComand.option("name", "[服务器名称]").action(async (_, arg) => {
try {
if (arg) {
if (!_.session.guildId)
return;
const findArg = guoupArg(
groupKeep,
{ name: arg },
{ groupId: _.session.event.channel.id }
);
const server = await mcBot.findServer(findArg);
const oneServer = server[0];
if (!oneServer)
return `未找到${arg}服务器`;
const address = `${oneServer.ip}:${oneServer.port}`;
let resultText = "";
await mcBot.pingOneServer(oneServer).then((res) => {
resultText = mcFormat(
oneServer.name,
address,
{
...res,
latency: res.latency
},
config
);
}).catch((err) => {
resultText = mcFormat(oneServer.name, address, {
rejected: true,
...err
});
});
return resultText;
} else {
const findArg = guoupArg(
groupKeep,
{},
{ groupId: _.session.event.channel.id }
);
const data = await mcBot.pingServerList(findArg);
if (!data.length)
return "您还没添加任何服务器";
let serverStrlist = [];
data.forEach((item) => {
const { name: name2, address, latency, ...res } = item;
serverStrlist.push(
mcFormat(
name2,
address,
{
...res,
latency
},
config
)
);
});
return serverStrlist.join("***********************\n");
}
} catch (e) {
console.log(e, "出现错误");
}
});
mcComand.subcommand(".set").option("name", "<名称>").option("address", "<地址:端口>").action(async (_, name2, address) => {
if (!_.session.guildId)
return;
if (!config.adminUsers.includes(String(_.session.event.user.id)))
return "您没有操作服务器的权限";
if (varkeys.includes(name2))
return "服务器名称不合法";
if (_.session.content)
try {
if (!name2 || !address)
return "请提供服务器名称和地址:端口";
const [ip, port] = address.split(":");
let formatPort = 25565;
if (port)
formatPort = Number(port);
if (!formatPort || formatPort < 0 || formatPort > 65536)
return "服务器端口不合法";
const findArg = guoupArg(
groupKeep,
{ name: name2 },
{ groupId: _.session.event.channel.id }
);
const findServer = await mcBot.findServer(findArg);
let server = null;
if (!findServer.length) {
server = await mcBot.upsert({
name: name2,
ip,
groupId: _.session.event.channel.id,
port: formatPort
});
} else {
server = await mcBot.upsert({
id: findServer[0].id,
name: name2,
ip,
groupId: _.session.event.channel.id,
port: formatPort
});
}
if (!server) {
return "操作失败";
} else if (server.inserted) {
return "新增成功";
} else if (server.matched) {
return "修改成功";
}
} catch (err) {
console.log(err, "出现错误");
}
});
mcComand.subcommand(".del").option("name", "<名称>").action(async (_, name2) => {
try {
if (!_.session.guildId)
return;
if (!config.adminUsers.includes(String(_.session.event.user.id)))
return "您没有操作服务器的权限";
if (!name2)
return "请提供服务器名称";
const findArg = { name: name2 };
if (groupKeep)
findArg.groupId = _.session.event.channel.id;
const { matched } = await mcBot.delteByName(findArg);
if (matched) {
return "删除成功";
} else {
return "没有此名称的服务器";
}
} catch (err) {
console.log(err, "出现错误");
}
});
mcComand.subcommand(".list").action(async (_) => {
try {
if (!_.session.guildId)
return;
const data = await mcBot.findServer({
groupId: _.session.guildId
});
return serverListFormat(data);
} catch (err) {
console.log(err, "出现错误");
}
});
mcComand.subcommand(".ping").option("address", "<地址:端口>").action(async (_, address) => {
try {
if (!address)
return "请提供服务器的地址:端口";
const [ip, port] = address.split(":");
let formatPort = 25565;
if (port)
formatPort = Number(port);
let resultText = "";
await mcBot.pingOneServer({ ip, port: formatPort }).then((res) => {
resultText = mcFormat(
"",
address,
{
...res,
latency: res.latency
},
config
);
}).catch((err) => {
resultText = mcFormat("", address, {
rejected: true,
...err
});
});
return resultText;
} catch (err) {
console.log(err, "出现错误");
}
});
}, "registerCommands");
// src/index.ts
var name = "mc-status-bot";
var inject = ["database"];
var Config = import_koishi.Schema.object({
adminUsers: import_koishi.Schema.array(import_koishi.Schema.string()).description(
"配置能够操作mc服务器的用户id"
),
groupKeep: import_koishi.Schema.boolean().default(false).description("开启后在哪里添加的服务器就只能在哪里看到"),
descriptionFormatting: import_koishi.Schema.boolean().default(true).description("启用后,将移除描述中的颜色代码、插件标签等非纯文本内容。")
});
async function apply(ctx, config) {
initDataBase(ctx);
initMcBot.getInstance(ctx);
registerCommands(ctx, config);
}
__name(apply, "apply");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Config,
apply,
inject,
name
});