koishi-plugin-nezha-api
Version:
866 lines (853 loc) • 35.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Config: () => Config,
apply: () => apply,
inject: () => inject,
logger: () => logger,
name: () => name,
usage: () => usage
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
var name = "nezha-api";
var inject = {
required: ["database"],
optional: ["server"]
};
var logger = new import_koishi.Logger("nezha-api");
var Config = import_koishi.Schema.object({
responseTimeout: import_koishi.Schema.number().default(15e3).min(5e3).max(6e4).step(1e3).description("交互式输入时,等待消息回复的时间(单位为毫秒)"),
showChangedData: import_koishi.Schema.boolean().default(true).description("站点数据发生改动时,返回的消息中是否包含数据改动"),
channelRecall: import_koishi.Schema.boolean().default(true).description("是否开启群聊自动撤回"),
recallTime: import_koishi.Schema.number().default(15e3).min(5e3).max(6e4).step(1e3).description("群聊自动撤回的延迟时间(单位为毫秒)"),
aliveThreshold: import_koishi.Schema.number().default(300).min(5).max(3600).step(1).description("判断服务器是否在线的时间间隔(单位为秒)"),
alertNotify: import_koishi.Schema.object({
enable: import_koishi.Schema.boolean().default(true).description("是否开启告警通知监听"),
path: import_koishi.Schema.string().default("/nezha/notify").description("告警通知监听路径"),
bodyContent: import_koishi.Schema.string().default(`# 探针通知\\n\\n时间:#DATETIME#\\n来自: #SERVER.NAME#\\n\\n#NEZHA#`).description("告警通知请求的body参数内容")
}).description("告警通知")
});
var usage = `
## 使用说明
### 指令:nezha
* 基本语法:\`nezha\`
* 指令功能:输出插件的简易信息
### 指令:nezha help
* 基本语法:\`nezha help\`
* 指令功能:等价于 \`help nezha\`
### 指令:nezha add
* 基本语法:\`nezha add [url:string] [token:string]\`
* 指令功能:添加哪吒站点的url和token至数据库,请确保url和token均有效
* 使用限制:**仅私聊可用**,支持交互式输入
### 指令:nezha url
* 基本语法:\`nezha url [url:string]\`
* 指令功能:修改数据库中记录的站点url,请确保已使用 \`nezha add\` 添加过数据
* 使用限制:**仅私聊可用**,支持交互式输入
### 指令:nezha token
* 基本语法:\`nezha url [token:string]\`
* 指令功能:修改数据库中记录的站点token,请确保已使用 \`nezha add\` 添加过数据
* 使用限制:**仅私聊可用**,支持交互式输入
### 指令:nezha info
* 基本语法:\`nezha info\`
* 指令功能:查看数据库中记录的站点url和token
* 使用限制:**仅私聊可用**
### 指令:nezha all
* 基本语法:\`nezha all [tag:string]\`
* 指令功能:获取 \`tag\` 分组下所有服务器的统计数据摘要,留空则返回所有数据,当且仅当 \`tag\` 为 \`untagged\` 时返回未分组的数据
### 指令:nezha list
* 基本语法:\`nezha list [tag:string]\`
* 指令功能:获取 \`tag\` 分组下所有服务器的状态信息摘要,留空则返回所有数据,当且仅当 \`tag\` 为 \`untagged\` 时返回未分组的数据
### 指令:nezha id
* 基本语法:\`nezha id [id:number]\`
* 指令功能:获取ID为 \`id\` 的服务器详细信息
### 指令:nezha search
* 基本语法:\`nezha search [name:string]\`
* 指令功能:搜索名称包含关键字 \`name\` 的服务器状态信息摘要
### 指令:nezha notify
* 基本语法:\`nezha notify\`
* 指令功能:**需要公网部署**,获取告警通知请求的部分参数,便于新增通知方式
`;
function apply(ctx, config) {
ctx.model.extend("nezha_site", {
userId: "unsigned",
url: "string",
token: "string"
}, {
primary: ["userId"]
});
const layerName = "nezha-notify";
const processRequest = /* @__PURE__ */ __name(async (_ctx, next) => {
_ctx.body = "OK";
const { platform, userId, groupId, content } = _ctx.request.body;
if (platform && (userId || groupId) && content) {
for (let bot of ctx.bots) {
if (bot.platform === platform) {
try {
if (groupId && userId) {
await bot.sendMessage(groupId, (0, import_koishi.h)("at", { id: userId }) + "<br/>" + content);
} else if (userId) {
await bot.sendPrivateMessage(userId, import_koishi.h.parse(content));
}
} catch (error) {
logger.error(error);
}
}
}
}
return next();
}, "processRequest");
ctx.on("ready", () => {
if (config.alertNotify.enable && ctx.server) {
ctx.server.post(layerName, config.alertNotify.path, processRequest);
}
});
ctx.on("dispose", () => {
if (ctx.server) {
ctx.server.stack = ctx.server.stack.filter((layer) => layer.name !== layerName);
}
});
const truncationUrl = /* @__PURE__ */ __name((url) => {
const siteReg = new RegExp("^https?://", "i");
url = url.replace(siteReg, "");
while (url.endsWith("/")) {
url = url.substring(0, url.length - 1);
}
return url;
}, "truncationUrl");
const mainCmd = ctx.command("nezha", "用于查询哪吒站点服务器详细信息").action(async ({ session }) => {
const { id, name: name2 } = await ctx.database.getUser(session.platform, session.userId, ["id", "name"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
let details = [
`Hi ${name2 || session.author.nick || session.author.name || session.username}!`,
"此插件用于查询哪吒面板服务器详细信息",
"免责声明:",
"本机器人保证您敏感信息的安全性。",
"请自行知悉潜在风险。",
"==========================="
];
if (data !== void 0) {
details.push("您的哪吒面板是:");
details.push(`${truncationUrl(data.url)}`);
details.push("使用 nezha all 开始统计数据摘要吧!");
} else {
details.push("没有保存的数据。");
details.push("使用 nezha add 开始添加你的站点数据!");
}
const [msgId] = await session.sendQueued(details.join("\n"));
if (await inChannel(session.platform, session.channelId) && config.channelRecall) {
ctx.setTimeout(async () => {
try {
await session.bot.deleteMessage(session.channelId, msgId);
} catch (error) {
logger.warn(error);
}
}, config.recallTime);
}
return;
});
mainCmd.subcommand(".help", "获取 nezha 相关指令的帮助信息").action(async ({ session }) => {
return session.execute("help nezha");
});
const checkValid = /* @__PURE__ */ __name((input) => {
return typeof input === "string" && input.length !== 0;
}, "checkValid");
const processUrl = /* @__PURE__ */ __name(async (session, inputUrl) => {
if (!checkValid(inputUrl)) {
session.sendQueued("站点地址无效,请重新输入站点地址");
inputUrl = await session.prompt(config.responseTimeout);
if (!checkValid(inputUrl)) {
return "站点地址输入超时";
}
}
const siteReg = new RegExp("^https?://", "g");
if (!siteReg.test(inputUrl)) {
return "站点地址必须以 http:// 或 https:// 开头";
}
return { url: inputUrl };
}, "processUrl");
const processToken = /* @__PURE__ */ __name(async (session, inputToken) => {
if (!checkValid(inputToken)) {
session.sendQueued("请输入站点Token");
inputToken = await session.prompt(config.responseTimeout);
if (!checkValid(inputToken)) {
return "站点Token输入超时";
}
}
return { token: inputToken };
}, "processToken");
const inChannel = /* @__PURE__ */ __name(async (platform, channelId) => {
const channelData = await ctx.database.getChannel(platform, channelId);
return channelData !== void 0;
}, "inChannel");
mainCmd.subcommand(".add [url:string] [token:string]", "添加站点数据").example("nezha add YOUR_URL YOUR_API_TOKEN").action(async ({ session }, url, token) => {
if (await inChannel(session.platform, session.channelId)) {
return "该指令仅限私聊可用";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
const procUrlRes = await processUrl(session, url);
if (typeof procUrlRes === "string") {
return procUrlRes;
}
const procTokenRes = await processToken(session, token);
if (typeof procTokenRes === "string") {
return procTokenRes;
}
if (data === void 0) {
await ctx.database.create("nezha_site", {
userId: id,
url: procUrlRes.url,
token: procTokenRes.token
});
let retMsg = "站点数据添加成功";
if (config.showChangedData) {
retMsg += `
🔗保存的站点地址:${procUrlRes.url}
🔑保存的站点Token:${procTokenRes.token}`;
}
return retMsg;
} else {
session.sendQueued("检测到站点数据已存在,是否覆盖原有数据(Y/n)?");
const confirmRes = await session.prompt(config.responseTimeout);
if (checkValid(confirmRes) && (confirmRes === "Y" || confirmRes === "y")) {
await ctx.database.set("nezha_site", { userId: id }, {
url: procUrlRes.url,
token: procTokenRes.token
});
let retMsg = "站点数据修改成功";
if (config.showChangedData) {
retMsg += `
🔗站点地址:${truncationUrl(data.url)} ➡ ${procUrlRes.url}
🔑站点Token:${data.token} ➡ ${procTokenRes.token}`;
}
return retMsg;
} else {
return "操作已取消";
}
}
});
mainCmd.subcommand(".delete", "删除已保存的站点数据").alias(".del").action(async ({ session }) => {
if (await inChannel(session.platform, session.channelId)) {
return "该指令仅限私聊可用";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
await ctx.database.remove("nezha_site", { userId: id });
let retMsg = "站点数据删除成功";
if (config.showChangedData) {
retMsg += `
🔗删除的站点地址:${truncationUrl(data.url)}
🔑删除的站点Token:${data.token}`;
}
return retMsg;
} else {
return "没有站点数据可供删除,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".url", "更新站点地址").option("url", "站点地址").action(async ({ session }, url) => {
const channelData = await ctx.database.getChannel(session.platform, session.channelId);
if (channelData !== void 0) {
return "该指令仅限私聊可用";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const procUrlRes = await processUrl(session, url);
if (typeof procUrlRes === "string") {
return procUrlRes;
} else {
await ctx.database.set("nezha_site", { userId: id }, { url: procUrlRes.url });
let retMsg = "站点地址更新成功";
if (config.showChangedData) {
retMsg += `
🔗站点地址:${truncationUrl(data.url)} ➡ ${procUrlRes.url}`;
}
return retMsg;
}
} else {
return "没有站点数据可供更新,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".token", "更新站点Token").option("token", "站点Token").action(async ({ session }, token) => {
const channelData = await ctx.database.getChannel(session.platform, session.channelId);
if (channelData !== void 0) {
return "该指令仅限私聊可用";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const procTokenRes = await processToken(session, token);
if (typeof procTokenRes === "string") {
return procTokenRes;
} else {
await ctx.database.set("nezha_site", { userId: id }, { token: procTokenRes.token });
let retMsg = "站点Token更新成功";
if (config.showChangedData) {
retMsg += `
🔑站点Token:${data.token} ➡ ${procTokenRes.token}`;
}
return retMsg;
}
} else {
return "没有站点数据可供更新,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".info", "查看站点地址和Token").action(async ({ session }) => {
if (await inChannel(session.platform, session.channelId)) {
return "该指令仅限私聊可用";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
return `这是您保存的站点数据:
🔗站点地址:${truncationUrl(data.url)}
🔑站点Token:${data.token}`;
} else {
return "没有站点数据可供查询,请先使用 nezha add 添加站点数据";
}
});
const listPath = "/api/v1/server/list";
const detailsPath = "/api/v1/server/details";
const untagged = "untagged";
const buildUrl = /* @__PURE__ */ __name((baseUrl, path) => {
baseUrl = baseUrl.trim();
while (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
}
return baseUrl + path;
}, "buildUrl");
const buildHeader = /* @__PURE__ */ __name((token) => {
return {
"Authorization": token,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
};
}, "buildHeader");
const isServerAlive = /* @__PURE__ */ __name((lastActive) => {
let timeNow = Math.floor(Date.now() / 1e3);
return timeNow - lastActive < config.aliveThreshold;
}, "isServerAlive");
const getCpuCoreNum = /* @__PURE__ */ __name((serverInfo) => {
const cpuInfos = serverInfo.host.CPU;
let totalCoreNum = 0;
if (cpuInfos && cpuInfos.length !== 0) {
for (let i = 0; i < cpuInfos.length; i++) {
const cpuInfo = cpuInfos[i];
const cpuInfoArr = cpuInfo.split(" ");
if (cpuInfoArr.length >= 3) {
const coreNum = Number(cpuInfoArr[cpuInfoArr.length - 3]);
if (!Number.isNaN(coreNum)) {
totalCoreNum += coreNum;
}
}
}
}
return totalCoreNum;
}, "getCpuCoreNum");
const naturalsize = /* @__PURE__ */ __name((value, fractionDigits = 1) => {
const base = 1024;
const suffixes = "KMGTPEZY";
const absValue = Math.abs(value);
let unit, suffix;
for (let i = 0; i < suffixes.length; i++) {
unit = base ** (i + 2);
suffix = suffixes[i];
if (absValue < unit)
break;
}
return (base * value / unit).toFixed(fractionDigits) + suffix;
}, "naturalsize");
const percentage = /* @__PURE__ */ __name((value) => {
value = Math.min(Math.abs(value), 1);
return (value * 100).toFixed(2) + "%";
}, "percentage");
const getNow = /* @__PURE__ */ __name(() => {
return new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
hour12: false,
timeZone: "Asia/Shanghai",
timeZoneName: "longOffset"
}).format(new Date(Date.now())).replaceAll("/", "-");
}, "getNow");
mainCmd.subcommand(".all [tag:string]", "获取所有服务器的统计数据摘要").action(async ({ session }, tag) => {
if (tag === void 0) {
tag = "";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const res = await ctx.http.get(buildUrl(data.url, detailsPath), { headers: buildHeader(data.token), params: { tag: tag === untagged ? "" : tag } }).catch((err) => {
return { error: err.message };
});
if (res !== void 0) {
if ("error" in res) {
return `访问站点失败,错误信息:${res.error}`;
}
if ("code" in res && "message" in res) {
if (res.code !== 0) {
return res.message;
}
if ("result" in res) {
if (res.result.length === 0) {
return "未检测到服务器";
}
let serverNum, onlineNum, cpuTotal, memTotal, memUsed, swapTotal, swapUsed, diskTotal, diskUsed;
serverNum = onlineNum = cpuTotal = memTotal = memUsed = swapTotal = swapUsed = diskTotal = diskUsed = 0;
let netInTransfer, netOutTransfer, netInSpeed, netOutSpeed;
netInTransfer = netOutTransfer = netInSpeed = netOutSpeed = 0;
for (let i = 0; i < res.result.length; i++) {
const serverInfo = res.result[i];
if (tag !== "" && serverInfo.tag !== tag && (tag === untagged && serverInfo.tag !== "")) {
continue;
}
serverNum += 1;
if (isServerAlive(serverInfo.last_active)) {
onlineNum += 1;
}
cpuTotal += getCpuCoreNum(serverInfo);
memTotal += serverInfo.host.MemTotal;
memUsed += serverInfo.status.MemUsed;
swapTotal += serverInfo.host.SwapTotal;
swapUsed += serverInfo.status.SwapUsed;
diskTotal += serverInfo.host.DiskTotal;
diskUsed += serverInfo.status.DiskUsed;
netInTransfer += serverInfo.status.NetInTransfer;
netOutTransfer += serverInfo.status.NetOutTransfer;
netInSpeed += serverInfo.status.NetInSpeed;
netOutSpeed += serverInfo.status.NetOutSpeed;
}
let memUsage = memTotal !== 0 ? memUsed / memTotal : 0;
let swapUsage = swapTotal !== 0 ? swapUsed / swapTotal : 0;
let diskUsage = diskTotal !== 0 ? diskUsed / diskTotal : 0;
let transParity;
if (netOutTransfer * netInTransfer === 0) {
transParity = 0;
} else if (netOutTransfer >= netInTransfer) {
transParity = netInTransfer / netOutTransfer;
} else {
transParity = netOutTransfer / netInTransfer;
}
let titlePrefix = "";
if (tag !== "") {
if (tag === untagged) {
titlePrefix = "[默认]分组的";
} else {
titlePrefix = `[${tag}]分组的`;
}
}
let details = [
`${titlePrefix}服务器统计数据摘要`,
"===========================",
`服务器数量: ${serverNum}`,
`在线服务器: ${onlineNum}`,
`CPU核心数: ${cpuTotal}`,
`内存: ${percentage(memUsage)} [${naturalsize(memUsed)}/${naturalsize(memTotal)}]`,
`交换: ${percentage(swapUsage)} [${naturalsize(swapUsed)}/${naturalsize(swapTotal)}]`,
`磁盘: ${percentage(diskUsage)} [${naturalsize(diskUsed)}/${naturalsize(diskTotal)}]`,
`下行速度: ↓${naturalsize(netInSpeed)}/s`,
`上行速度: ↑${naturalsize(netOutSpeed)}/s`,
`下行流量: ↓${naturalsize(netInTransfer)}`,
`上行流量: ↑${naturalsize(netOutTransfer)}`,
`流量对等性: ${percentage(transParity)}`,
`
更新于: ${getNow()}`
];
const [msgId] = await session.sendQueued(details.join("\n"));
if (await inChannel(session.platform, session.channelId) && config.channelRecall) {
ctx.setTimeout(async () => {
try {
await session.bot.deleteMessage(session.channelId, msgId);
} catch (error) {
logger.warn(error);
}
}, config.recallTime);
}
return;
}
}
}
return `访问站点失败,请联系管理员确认错误信息`;
} else {
return "没有站点数据可供使用,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".list [tag:string]", "获取所有服务器的状态信息摘要").action(async ({ session }, tag) => {
if (tag === void 0) {
tag = "";
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const res = await ctx.http.get(buildUrl(data.url, listPath), { headers: buildHeader(data.token), params: { tag: tag === untagged ? "" : tag } }).catch((err) => {
return { error: err.message };
});
if (res !== void 0) {
if ("error" in res) {
return `访问站点失败,错误信息:${res.error}`;
}
if ("code" in res && "message" in res) {
if (res.code !== 0) {
return res.message;
}
if ("result" in res) {
if (res.result.length === 0) {
return "未检测到服务器";
}
let titlePrefix = "";
if (tag !== "") {
if (tag === untagged) {
titlePrefix = "[默认]分组的";
} else {
titlePrefix = `[${tag}]分组的`;
}
}
let details = [
`${titlePrefix}服务器状态信息摘要`,
"===========================",
"===========================",
"ID 状态 分组 服务器名"
];
let tagArr = [];
let serverNum, offlineNum, dualNum;
serverNum = offlineNum = dualNum = 0;
res.result.sort((a, b) => {
return a.id - b.id;
});
for (let i = 0; i < res.result.length; i++) {
const serverInfo = res.result[i];
if (tag !== "" && serverInfo.tag !== tag && (tag === untagged && serverInfo.tag !== "")) {
continue;
}
tagArr.push(serverInfo.tag);
const alive = isServerAlive(serverInfo.last_active);
serverNum += 1;
offlineNum += alive ? 0 : 1;
dualNum += serverInfo.ipv4 !== "" && serverInfo.ipv6 !== "" ? 1 : 0;
const status = alive ? "❇️在线" : "☠️离线";
details.push(`${serverInfo.id} ${status} ${serverInfo.tag === "" ? "🈳" : serverInfo.tag} ${serverInfo.name}`);
}
let startPos = 2;
details.splice(startPos++, 0, `服务器数量: ${serverNum}`);
if (tag === "") {
details.splice(startPos++, 0, `分组的数量: ${Array.from(new Set(tagArr)).length}`);
}
details.splice(startPos++, 0, `离线服务器:${offlineNum}`);
details.splice(startPos++, 0, `双栈服务器:${dualNum}`);
const [msgId] = await session.sendQueued(details.join("\n"));
if (await inChannel(session.platform, session.channelId) && config.channelRecall) {
ctx.setTimeout(async () => {
try {
await session.bot.deleteMessage(session.channelId, msgId);
} catch (error) {
logger.warn(error);
}
}, config.recallTime);
}
return;
}
}
}
return `访问站点失败,请联系管理员确认错误信息`;
} else {
return "没有站点数据可供使用,请先使用 nezha add 添加站点数据";
}
});
const getCountryFlag = /* @__PURE__ */ __name((countryCode) => {
const symbols = [
"🇦",
"🇧",
"🇨",
"🇩",
"🇪",
"🇫",
"🇬",
"🇭",
"🇮",
"🇯",
"🇰",
"🇱",
"🇲",
"🇳",
"🇴",
"🇵",
"🇶",
"🇷",
"🇸",
"🇹",
"🇺",
"🇻",
"🇼",
"🇽",
"🇾",
"🇿"
];
const BASE = "A".charCodeAt(0);
let res = [];
for (let i = 0; i < countryCode.length; i++) {
res.push(symbols[countryCode[i].toUpperCase().charCodeAt(0) - BASE]);
}
return res.join("");
}, "getCountryFlag");
const maskIPv4 = /* @__PURE__ */ __name((ipv4) => {
if (typeof ipv4 !== "string" || ipv4 === "" || ipv4.split(".").length !== 4) {
return "🈳";
}
let ipv4Arr = ipv4.split(".");
for (let i = 2; i < ipv4Arr.length; i++) {
ipv4Arr[i] = "**";
}
return ipv4Arr.join(".");
}, "maskIPv4");
const maskIPv6 = /* @__PURE__ */ __name((ipv6) => {
if (typeof ipv6 !== "string" || ipv6 === "") {
return "🈳";
}
let ipv6Arr = ipv6.split("::");
if (ipv6Arr.length === 1) {
ipv6Arr = ipv6.split(":");
if (ipv6.length !== 8) {
return "🈳";
}
ipv6Arr.splice(4, 4, "**", "**", "**", "**");
return ipv6Arr.join(":");
} else if (ipv6Arr.length === 2) {
let front = ipv6Arr[0].split(":");
let end = ipv6Arr[1].split(":");
let maskCount = Math.floor((front.length + end.length) / 2);
for (let i = end.length - 1; i >= 0; i--) {
if (maskCount === 0) {
break;
}
end[i] = "**";
maskCount--;
}
for (let i = front.length - 1; i >= 0; i--) {
if (maskCount === 0) {
break;
}
front[i] = "**";
maskCount--;
}
return front.join(":") + "::" + end.join(":");
} else {
return "🈳";
}
}, "maskIPv6");
const convertTime = /* @__PURE__ */ __name((bootTime) => {
if (bootTime === 0) {
return "未运行";
}
let timeNow = Math.floor(Date.now() / 1e3);
let time = timeNow - bootTime;
const day = 24 * 60 * 60;
const hour = 60 * 60;
return `${Math.floor(time / day)}天${Math.floor(time % day / hour)}小时`;
}, "convertTime");
mainCmd.subcommand(".id <serverId:integer>", "通过id查询服务器详细信息").action(async ({ session }, serverId) => {
if (serverId === void 0) {
session.sendQueued("请输入服务器id");
serverId = Number(await session.prompt(config.responseTimeout));
if (!Number.isSafeInteger(serverId)) {
return "参数 id 输入无效,请提供一个整数。";
}
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const res = await ctx.http.get(buildUrl(data.url, detailsPath), { headers: buildHeader(data.token), params: { id: serverId } }).catch((err) => {
return { error: err.message };
});
if (res !== void 0) {
if ("error" in res) {
return `访问站点失败,错误信息:${res.error}`;
}
if ("code" in res && "message" in res) {
if (res.code !== 0) {
return res.message;
}
if ("result" in res) {
if (res.result.length === 0) {
return "未检测到服务器";
}
const serverInfo = res.result[0];
const alive = isServerAlive(serverInfo.last_active);
const status = alive ? "❇️在线" : "☠️离线";
let cpuInfo = "";
if (serverInfo.host.CPU !== null && serverInfo.host.CPU.length !== 0) {
cpuInfo = serverInfo.host.CPU[0];
}
const memTotal = serverInfo.host.MemTotal;
const memUsed = serverInfo.status.MemUsed;
const swapTotal = serverInfo.host.SwapTotal;
const swapUsed = serverInfo.status.SwapUsed;
const diskTotal = serverInfo.host.DiskTotal;
const diskUsed = serverInfo.status.DiskUsed;
const netInTransfer = serverInfo.status.NetInTransfer;
const netOutTransfer = serverInfo.status.NetOutTransfer;
const netInSpeed = serverInfo.status.NetInSpeed;
const netOutSpeed = serverInfo.status.NetOutSpeed;
const memUsage = memTotal !== 0 ? memUsed / memTotal : 0;
const swapUsage = swapTotal !== 0 ? swapUsed / swapTotal : 0;
const diskUsage = diskTotal !== 0 ? diskUsed / diskTotal : 0;
let details = [
`${getCountryFlag(serverInfo.host.CountryCode)} ${serverInfo.name} ${status}`,
"===========================",
`id: ${serverId}`,
`tag: ${serverInfo.tag === "" ? "🈳" : serverInfo.tag}`,
`ipv4: ${maskIPv4(serverInfo.ipv4)}`,
`ipv6: ${maskIPv6(serverInfo.ipv6)}`,
`平台: ${serverInfo.host.Platform} ${serverInfo.host.PlatformVersion}`,
`CPU信息: ${cpuInfo}`,
`运行时间: ${convertTime(serverInfo.host.BootTime)}`,
`负载: ${serverInfo.status.Load1.toFixed(2)} ${serverInfo.status.Load5.toFixed(2)} ${serverInfo.status.Load15.toFixed(2)}`,
`CPU: ${serverInfo.status.CPU.toFixed(2)}% [${serverInfo.host.Arch}]`,
`内存: ${percentage(memUsage)} [${naturalsize(memUsed)}/${naturalsize(memTotal)}]`,
`交换: ${percentage(swapUsage)} [${naturalsize(swapUsed)}/${naturalsize(swapTotal)}]`,
`磁盘: ${percentage(diskUsage)} [${naturalsize(diskUsed)}/${naturalsize(diskTotal)}]`,
`流量: ↓${naturalsize(netInTransfer)} ↑${naturalsize(netOutTransfer)}`,
`网速: ↓${naturalsize(netInSpeed)}/s ↑${naturalsize(netOutSpeed)}/s`,
`
更新于: ${getNow()}`
];
const [msgId] = await session.sendQueued(details.join("\n"));
if (await inChannel(session.platform, session.channelId) && config.channelRecall) {
ctx.setTimeout(async () => {
try {
await session.bot.deleteMessage(session.channelId, msgId);
} catch (error) {
logger.warn(error);
}
}, config.recallTime);
}
return;
}
}
}
return `访问站点失败,请联系管理员确认错误信息`;
} else {
return "没有站点数据可供使用,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".search <name:string>", "搜索服务器名称").action(async ({ session }, name2) => {
if (name2 === void 0) {
session.sendQueued("请输入搜索关键词");
name2 = await session.prompt(config.responseTimeout);
if (!checkValid(name2)) {
return "关键词输入超时";
}
}
const { id } = await ctx.database.getUser(session.platform, session.userId, ["id"]);
const [data] = await ctx.database.get("nezha_site", { userId: id });
if (data !== void 0) {
const res = await ctx.http.get(buildUrl(data.url, listPath), { headers: buildHeader(data.token) }).catch((err) => {
return { error: err.message };
});
if (res !== void 0) {
if ("error" in res) {
return `访问站点失败,错误信息:${res.error}`;
}
if ("code" in res && "message" in res) {
if (res.code !== 0) {
return res.message;
}
if ("result" in res) {
if (res.result.length === 0) {
return "未检测到服务器";
}
let details = [
`服务器搜索结果`,
"===========================",
"ID 状态 分组 服务器名"
];
res.result.sort((a, b) => {
return a.id - b.id;
});
let findCount = 0;
for (let i = 0; i < res.result.length; i++) {
const serverInfo = res.result[i];
if (serverInfo.name.indexOf(name2) === -1) {
continue;
}
findCount++;
const alive = isServerAlive(serverInfo.last_active);
const status = alive ? "❇️在线" : "☠️离线";
details.push(`${serverInfo.id} ${status} ${serverInfo.tag === "" ? "🈳" : serverInfo.tag} ${serverInfo.name}`);
}
if (findCount === 0) {
return `没有找到名称包含"${name2}"的服务器`;
}
const [msgId] = await session.sendQueued(details.join("\n"));
if (await inChannel(session.platform, session.channelId) && config.channelRecall) {
ctx.setTimeout(async () => {
try {
await session.bot.deleteMessage(session.channelId, msgId);
} catch (error) {
logger.warn(error);
}
}, config.recallTime);
}
return;
}
}
}
return `访问站点失败,请联系管理员确认错误信息`;
} else {
return "没有站点数据可供使用,请先使用 nezha add 添加站点数据";
}
});
mainCmd.subcommand(".notify", "获取告警通知请求的部分参数").action(async ({ session }) => {
if (!config.alertNotify.enable) {
return "告警通知未启用";
}
const message = [
`URL:http(s)://YOUR_KOISHI_SITE${config.alertNotify.path}`,
"请求方式:POST",
"请求类型:JSON",
"Body:",
"{",
` "platform": "${session.platform}",`,
` "userId": "${session.userId}",`,
` "groupId": "${session.guildId}",`,
` "content": "${config.alertNotify.bodyContent}"`,
"}"
];
if (!session.guildId) {
message.splice(7, 1);
}
return message.join("\n");
});
}
__name(apply, "apply");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Config,
apply,
inject,
logger,
name,
usage
});