koishi-plugin-cave
Version:
A mysterious cave where the sound is echoed over and over again.
630 lines (624 loc) • 23.4 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,
getFileCountPlusOne: () => getFileCountPlusOne,
getMaxCaveId: () => getMaxCaveId,
name: () => name,
saveImages: () => saveImages
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
var fs = __toESM(require("fs"));
var path = __toESM(require("path"));
var import_util = __toESM(require("util"));
var logger = new import_koishi.Logger("cave");
var name = "cave";
var Config = import_koishi.Schema.intersect([
import_koishi.Schema.object({
manager: import_koishi.Schema.array(import_koishi.Schema.string()).required().description("管理员QQ,一个项目填一个ID"),
consoleinfo: import_koishi.Schema.boolean().default(false).description("日志调试模式"),
consolefor: import_koishi.Schema.boolean().default(false).description("开启-g指令指定回声洞"),
number: import_koishi.Schema.number().default(3).description("群单位回声洞冷却时间,单位为秒"),
helpinfo: import_koishi.Schema.boolean().default(true).description("开启冷却显示"),
nameinfo: import_koishi.Schema.boolean().default(false).description("使用陌生人接口获取昵称")
})
]);
async function saveImages(urls, selectedPath, safeFilename, imageExtension, config, session, ctx) {
let url = urls;
let fileRoot = path.join(selectedPath, safeFilename);
let fileExt = `.${imageExtension}`;
let targetPath = `${fileRoot}${fileExt}`;
let index = 0;
if (config.consoleinfo) {
logger.info("提取到的图片链接:" + url);
}
while (fs.existsSync(targetPath)) {
index++;
targetPath = `${fileRoot}_${index}${fileExt}`;
}
try {
const buffer = await ctx.http.get(url);
if (buffer.byteLength === 0)
throw new Error("下载的数据为空");
await fs.promises.writeFile(targetPath, Buffer.from(buffer));
return targetPath;
} catch (error) {
logger.info("保存图片时出错: " + error.message);
}
if (config.consoleinfo) {
logger.info("提取到的图片链接:" + targetPath);
}
}
__name(saveImages, "saveImages");
async function getFileCountPlusOne(dirPath) {
let maxNumber = 0;
function traverseDir(dir) {
const files = fs.readdirSync(dir);
for (const file of files) {
const baseName = path.basename(file, path.extname(file));
const number = parseInt(baseName, 10);
maxNumber = Math.max(maxNumber, number);
}
}
__name(traverseDir, "traverseDir");
traverseDir(dirPath);
return (maxNumber + 1).toString();
}
__name(getFileCountPlusOne, "getFileCountPlusOne");
var readFile2 = import_util.default.promisify(fs.readFile);
async function getMaxCaveId(filePath) {
try {
const data = await readFile2(filePath, "utf8");
const objs = JSON.parse(data);
if (Array.isArray(objs)) {
if (objs.length > 0 && "cave_id" in objs[0]) {
let maxCaveId = Math.max(...objs.map((obj) => obj.cave_id));
return maxCaveId + 1;
} else {
console.log("Array is empty or objects do not have a cave_id property.");
return 1;
}
} else {
console.log("Data is not an array.");
}
} catch (err) {
console.log(`Error reading file from disk: ${err}`);
}
}
__name(getMaxCaveId, "getMaxCaveId");
function readJsonFile(filePath) {
try {
const data = fs.readFileSync(filePath, "utf8");
return JSON.parse(data);
} catch (error) {
throw new Error(`读取文件出错: ${error.message}`);
}
}
__name(readJsonFile, "readJsonFile");
function writeJsonFile(filePath, data) {
try {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
} catch (error) {
throw new Error(`写入文件出错: ${error.message}`);
}
}
__name(writeJsonFile, "writeJsonFile");
function getRandomObject(data) {
const index = Math.floor(Math.random() * data.length);
return data[index];
}
__name(getRandomObject, "getRandomObject");
function destructureAndPrint(item) {
const { path: path2 } = item;
return path2 || "";
}
__name(destructureAndPrint, "destructureAndPrint");
function destructureAndPrints(item) {
const { text } = item;
return text ? JSON.stringify(text, null, 2) : "";
}
__name(destructureAndPrints, "destructureAndPrints");
function stringToUnicode(str) {
return str.split("").map((char) => {
const hex = char.charCodeAt(0).toString(16).padStart(4, "0");
return `\\u${hex}`;
}).join("");
}
__name(stringToUnicode, "stringToUnicode");
function unicodeToString(str) {
return str.replace(/\\u([\dA-Fa-f]{4})/g, (_, hex) => {
return String.fromCharCode(parseInt(hex, 16));
});
}
__name(unicodeToString, "unicodeToString");
async function deleteFiles(paths) {
for (const filePath of paths) {
try {
await fs.promises.unlink(filePath);
} catch (err) {
console.error(`Error deleting file ${filePath}:`, err);
}
}
}
__name(deleteFiles, "deleteFiles");
async function apply(ctx, config) {
async function ensureDirExists(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
__name(ensureDirExists, "ensureDirExists");
async function writeJSONFile(caveFilePath2, data) {
const jsonData = JSON.stringify(data, null, 2);
fs.writeFileSync(caveFilePath2, jsonData, "utf-8");
}
__name(writeJSONFile, "writeJSONFile");
const caveDirPath = path.join(ctx.baseDir, "data", "cave");
const cavePicturesDirPath = path.join(caveDirPath, "pictures");
const caveFilePath = path.join(caveDirPath, "cave.json");
const caveDataFilePath = path.join(caveDirPath, "data.json");
await ensureDirExists(caveDirPath);
await ensureDirExists(cavePicturesDirPath);
async function ensureFileExists(caveDirPath2) {
if (!fs.existsSync(caveDirPath2)) {
fs.writeFileSync(caveDirPath2, "[]", "utf-8");
}
}
__name(ensureFileExists, "ensureFileExists");
async function ensureFileExistss(caveDirPath2) {
if (!fs.existsSync(caveDirPath2)) {
fs.writeFileSync(caveDirPath2, "{}", "utf-8");
}
}
__name(ensureFileExistss, "ensureFileExistss");
await ensureFileExists(caveFilePath);
await ensureFileExistss(caveDataFilePath);
const lastUsed = /* @__PURE__ */ new Map();
ctx.command("cave [image]", "回声洞").option("a", "-a 添加回声洞").option("g", "-g 查看当前id的回声洞内容").option("r", "-r 删除当前id的回声洞").usage(`注意: 直接使用回声洞指令该为cave,添加回声洞为cave -a,-g为只能管理员使用.当前回声洞全局冷却为${config.number}`).example("cave 随机一条回声洞\ncave -a 回复你要添加的图片或文字添加回声洞(不能是合并转发,视频,音频等)\ncave -g 查看当前id的回声洞内容(只能管理员使用或开启可选项后所有人都能使用)\ncave -r 删除当前id的回声洞").action(async ({ session, options }, image) => {
if (options.a) {
let quote = session.quote;
let imageURL;
let sessioncontent = session.content;
imageURL = import_koishi.h.select(sessioncontent, "img").map((a) => a.attrs.src)[0];
if (!imageURL && !quote) {
return "请输入图片或引用回复一条消息";
}
let elements = quote?.elements;
let textContents = [];
let imgSrcs = [];
let message = [];
if (elements) {
elements.forEach((element) => {
if (element.type === "text") {
textContents.push(stringToUnicode(element.attrs.content));
} else if (element.type === "img") {
imgSrcs.push(element.attrs.src);
}
});
let textMessage = {
"type": "text",
"text": textContents.join(" ")
};
if (textContents.length > 0) {
message.push(textMessage);
}
imageURL = imgSrcs[0];
} else if (image) {
let quotemessage;
quotemessage = session.quote?.content ?? image;
imageURL = import_koishi.h.select(quotemessage, "img").map((a) => a.attrs.src)[0];
}
let selectedPath;
selectedPath = cavePicturesDirPath;
let safeFilename;
const getFile = await getFileCountPlusOne(cavePicturesDirPath);
safeFilename = getFile;
const imageExtension = "png";
if (config.consoleinfo) {
if (imageURL) {
logger.info("用户输入: " + imageURL);
}
}
let getcaveid = await getMaxCaveId(caveFilePath);
let cave_id = getcaveid;
let contributor_id = quote?.user.id;
if (contributor_id === void 0) {
contributor_id = session.userId;
}
let state = 1;
async function sendToManagers(messageElements) {
const bot = ctx.bots[0];
if (!bot) {
logger.warn("未找到 bot 实例,无法发送私信给审核人员。");
return;
}
for (const manager of config.manager) {
await bot.sendPrivateMessage(manager, messageElements);
}
}
__name(sendToManagers, "sendToManagers");
try {
if (imageURL) {
const savedImagePath = await saveImages(imageURL, selectedPath, safeFilename, imageExtension, config, session, ctx);
let imageMessage = {
"type": "image",
"path": savedImagePath
};
if (!savedImagePath) {
return "保存失败,请稍后重试(有多稍后取决于是否真的能下下来)";
}
message.push(imageMessage);
}
if (message.length === 0) {
return `请不要引用合并转发,视频,语音等
或消息在bot重启之前发送,无法寻找上下文`;
}
let caveObject = {
cave_id,
message,
contributor_id,
state
};
let caveObjects = [];
caveObjects.push(caveObject);
let rawdata = fs.readFileSync(caveFilePath);
let existingMessages = JSON.parse(rawdata.toString());
existingMessages.push(...caveObjects);
let json = JSON.stringify(existingMessages, null, 2);
fs.writeFile(caveFilePath, json, "utf8", function(err) {
if (err) {
console.log("写入文件时出错:", err);
} else {
console.log("JSON 文件已成功保存!");
}
});
await session.send(`添加成功,序号为 [${cave_id}]
提交者: ${contributor_id}`);
let textContentss = unicodeToString(textContents.join(" "));
if (imageURL) {
let messageElements = [
`新的待审核回声洞[ ${cave_id} ]
`,
textContentss,
import_koishi.h.image(imageURL),
`—— ${contributor_id}`
];
await sendToManagers(messageElements.join("\n"));
} else {
let messageElements = [
`新的待审核回声洞[ ${cave_id} ]
`,
textContentss,
`—— ${contributor_id}`
];
await sendToManagers(messageElements.join("\n"));
}
} catch (error) {
if (imageURL) {
logger.info(`保存图片出错:${error.message}`);
}
}
}
if (options.r) {
const data = await readJsonFile(caveFilePath);
const imageNumber = Number(image);
const managers = config.manager;
if (!managers.includes(session.userId)) {
await session.send("您没有权限执行此命令。");
return;
}
if (isNaN(imageNumber)) {
await session.send("请输入一个有效的回声洞序号。");
return;
}
const caveToDelete = data.find((item) => item.cave_id === imageNumber);
const updatedData = data.filter((item) => item.cave_id !== imageNumber);
if (updatedData.length === data.length) {
await session.send("未找到对应的回声洞序号。");
return;
}
const messages = caveToDelete.message || [];
const filesToDelete = messages.map((message) => message.path);
await deleteFiles(filesToDelete);
await writeJsonFile(caveFilePath, updatedData);
await session.send(`回声洞序号 ${imageNumber} 已成功删除。`);
}
if (options.g) {
const data = await readJsonFile(caveFilePath);
const imageNumber = Number(image);
const managers = config.manager;
if (!config.consolefor) {
if (!managers.includes(session.userId)) {
await session.send("您没有权限执行此命令。");
return;
}
}
if (isNaN(imageNumber)) {
await session.send("请输入一个有效的回声洞序号。");
return;
}
const caveToFind = data.find((item) => item.cave_id === imageNumber);
if (!caveToFind) {
await session.send("未找到对应的回声洞序号。");
return;
} else {
const { cave_id, message, contributor_id, state } = caveToFind;
let texts = caveToFind.message.map(destructureAndPrints);
let messages = caveToFind.message.map(destructureAndPrint);
let filteredTexts = texts.filter((text) => text !== void 0);
let username = contributor_id;
if (config.nameinfo) {
const user = await ctx.bots[0].getUser(contributor_id);
username = user.name;
}
let chars = filteredTexts.map((text) => {
if (text !== void 0) {
if (text.includes("\\u")) {
let codePoints = text.split("\\u").filter(Boolean).map((hex) => {
let codePoint = parseInt(hex, 16);
if (isNaN(codePoint)) {
return "";
}
return String.fromCodePoint(codePoint);
});
return codePoints.join("");
} else {
try {
return JSON.parse(`"${text.replace(/"/g, '\\"')}"`);
} catch (e) {
console.error(e);
return text;
}
}
}
return "";
});
const file = "file:///";
let str = chars.join("");
if (!str) {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
(0, import_koishi.h)("image", { src: file + messages.filter((msg) => msg).join("\n") }),
`—— ${username}`
];
session.send(messageElements);
} else if (messages.length === 1 && messages[0] !== "") {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
import_koishi.h.text(str),
// 确保文本部分正确显示
"\n\n",
// 手动添加换行符
(0, import_koishi.h)("image", { src: file + messages.filter((msg) => msg).join("\n") }),
// '\n\n', // 手动添加换行符
`—— ${username}`
];
session.send(messageElements);
} else {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
import_koishi.h.text(str),
// 确保文本部分正确显示
"\n\n",
// 手动添加换行符
`—— ${username}`
];
session.send(messageElements);
}
}
}
if (!options.a && !options.r && !options.g) {
const data = readJsonFile(caveFilePath);
const filteredData = data.filter((item) => item.state === 0);
const guildId = session.guildId;
const lastCall = lastUsed.get(guildId) || 0;
const now = Date.now();
if (filteredData.length === 0) {
return "没有找到符合条件的回声洞.";
}
const diff = now - lastCall;
if (diff < config.number * 1e3) {
const timeLeft = Math.ceil((config.number * 1e3 - diff) / 1e3);
if (config.helpinfo) {
return `群回声洞调用的太频繁了,请等待${timeLeft}秒后再试`;
} else {
return;
}
}
lastUsed.set(guildId, now);
const randomObject = getRandomObject(filteredData);
const { cave_id, message, contributor_id, state } = randomObject;
let texts = randomObject.message.map(destructureAndPrints);
let messages = randomObject.message.map(destructureAndPrint);
let filteredTexts = texts.filter((text) => text !== void 0);
console.log(contributor_id);
let username = contributor_id;
if (config.nameinfo) {
const user = await ctx.bots[0].getUser(contributor_id);
username = user.name;
}
let chars = filteredTexts.map((text) => {
if (text !== void 0) {
if (text.includes("\\u")) {
let codePoints = text.split("\\u").filter(Boolean).map((hex) => {
let codePoint = parseInt(hex, 16);
if (isNaN(codePoint)) {
return "";
}
return String.fromCodePoint(codePoint);
});
return codePoints.join("");
} else {
try {
return JSON.parse(`"${text.replace(/"/g, '\\"')}"`);
} catch (e) {
console.error(e);
return text;
}
}
}
return "";
});
const file = "file:///";
let str = chars.join("");
if (!str) {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
(0, import_koishi.h)("image", { src: file + messages.filter((msg) => msg).join("\n") }),
`—— ${username}`
];
session.send(messageElements);
} else if (messages.length === 1 && messages[0] !== "") {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
import_koishi.h.text(str),
// 确保文本部分正确显示
"\n\n",
// 手动添加换行符
(0, import_koishi.h)("image", { src: file + messages.filter((msg) => msg).join("\n") }),
// '\n\n', // 手动添加换行符
`—— ${username}`
];
session.send(messageElements);
} else {
const messageElements = [
`回声洞 —— [ ${cave_id} ]`,
`
`,
import_koishi.h.text(str),
// 确保文本部分正确显示
"\n\n",
// 手动添加换行符
`—— ${username}`
];
session.send(messageElements);
}
}
});
ctx.private().command("setcave <image>", "私聊审核").alias("回声洞审核").option("t", "-t 私聊审核通过回声洞").option("f", "-f 私聊审核不通过回声洞").usage("注意:未审核的回声洞不在可抽取列表,该指令只能在私聊使用,群聊使用无响应属于正常.").example("setcave -t 1/all 审核通过id为1/全部的未审核回声洞\nsetcave -f 1/all 不通过id为1/全部的未审核回声洞").action(async ({ session, options }, image) => {
const all = "all";
const data = readJsonFile(caveFilePath);
let userid = session.event.user, id;
const managers = config.manager;
if (options.t) {
if (!managers.includes(session.userId)) {
await session.send("您没有权限执行此命令。");
return;
}
if (isNaN(Number(image)) && image !== all) {
await session.send("错误:image 参数只接受数字与ALL类型。");
return;
}
try {
if (image === all) {
let updatedCount = 0;
data.forEach((item) => {
if (item.state === 1) {
item.state = 0;
updatedCount++;
}
});
writeJsonFile(caveFilePath, data);
await session.send(`已通过(${updatedCount})个回声洞。`);
} else {
const imageNumber = Number(image);
const targetItem = data.find((item) => item.cave_id === imageNumber);
if (targetItem && targetItem.state === 1) {
targetItem.state = 0;
writeJsonFile(caveFilePath, data);
await session.send(`回声洞序号: [(]${imageNumber}] 已审核`);
} else {
await session.send(`未找到序号为 [(]${imageNumber}] 的未审核回声洞。`);
}
}
} catch (error) {
await session.send(`读取或写入 JSON 文件时出错: ${error.message}`);
}
}
if (options.f) {
if (!managers.includes(session.userId)) {
await session.send("您没有权限执行此命令。");
return;
}
if (isNaN(Number(image)) && image !== all) {
await session.send("错误:image 参数只接受数字与ALL类型。");
return;
}
try {
if (image === all) {
let updatedCount = 0;
data.forEach((item) => {
if (item.state === 1) {
item.state = 2;
updatedCount++;
}
});
writeJsonFile(caveFilePath, data);
await session.send(`已不通过 [${updatedCount}] 个回声洞。`);
} else {
const imageNumber = Number(image);
const targetItem = data.find((item) => item.cave_id === imageNumber);
if (targetItem && targetItem.state === 1) {
targetItem.state = 2;
writeJsonFile(caveFilePath, data);
await session.send(`回声洞序号: [${imageNumber}] 审核不通过`);
} else {
await session.send(`未找到序号为 [${imageNumber}] 的未审核回声洞。`);
}
}
} catch (error) {
await session.send(`读取或写入 JSON 文件时出错: ${error.message}`);
}
}
});
}
__name(apply, "apply");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Config,
apply,
getFileCountPlusOne,
getMaxCaveId,
name,
saveImages
});