UNPKG

koishi-plugin-mc-status-bot

Version:
566 lines (558 loc) 16.7 kB
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 });