koishi-plugin-best-cave 0.0.1 → 1.0.1
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.d.ts +17 -2
- package/lib/index.js +423 -302
- package/package.json +1 -1
- package/readme.md +11 -9
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
2
|
export declare const name = "cave";
|
|
3
3
|
export declare const inject: string[];
|
|
4
|
+
export declare const Config: Schema<Config>;
|
|
5
|
+
export declare function apply(ctx: Context, config: Config): Promise<void>;
|
|
4
6
|
export interface User {
|
|
5
7
|
userId: string;
|
|
6
8
|
username: string;
|
|
@@ -14,6 +16,19 @@ export interface Config {
|
|
|
14
16
|
manager: string[];
|
|
15
17
|
number: number;
|
|
16
18
|
enableAudit: boolean;
|
|
19
|
+
allowVideo: boolean;
|
|
20
|
+
videoMaxSize: number;
|
|
21
|
+
imageMaxSize: number;
|
|
22
|
+
blacklist: string[];
|
|
23
|
+
whitelist: string[];
|
|
24
|
+
enablePagination: boolean;
|
|
25
|
+
itemsPerPage: number;
|
|
17
26
|
}
|
|
18
|
-
export declare
|
|
19
|
-
|
|
27
|
+
export declare function initCavePaths(ctx: Context): Promise<{
|
|
28
|
+
dataDir: string;
|
|
29
|
+
caveDir: string;
|
|
30
|
+
caveFilePath: string;
|
|
31
|
+
resourceDir: string;
|
|
32
|
+
pendingFilePath: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function handleCaveAction(ctx: Context, config: Config, session: any, options: any, content: string[], lastUsed: Map<string, number>): Promise<string | void>;
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,8 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
34
|
apply: () => apply,
|
|
35
|
+
handleCaveAction: () => handleCaveAction,
|
|
36
|
+
initCavePaths: () => initCavePaths,
|
|
35
37
|
inject: () => inject,
|
|
36
38
|
name: () => name
|
|
37
39
|
});
|
|
@@ -39,96 +41,134 @@ module.exports = __toCommonJS(src_exports);
|
|
|
39
41
|
var import_koishi = require("koishi");
|
|
40
42
|
var fs = __toESM(require("fs"));
|
|
41
43
|
var path = __toESM(require("path"));
|
|
42
|
-
var logger = new import_koishi.Logger("cave");
|
|
43
44
|
var name = "cave";
|
|
44
45
|
var inject = ["database"];
|
|
45
46
|
var Config = import_koishi.Schema.object({
|
|
46
|
-
manager: import_koishi.Schema.array(import_koishi.Schema.string()).required().description("
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
manager: import_koishi.Schema.array(import_koishi.Schema.string()).required().description("管理员"),
|
|
48
|
+
blacklist: import_koishi.Schema.array(import_koishi.Schema.string()).default([]).description("黑名单"),
|
|
49
|
+
number: import_koishi.Schema.number().default(60).description("调用冷却时间(秒)"),
|
|
50
|
+
enableAudit: import_koishi.Schema.boolean().default(false).description("审核功能"),
|
|
51
|
+
allowVideo: import_koishi.Schema.boolean().default(true).description("允许添加视频"),
|
|
52
|
+
videoMaxSize: import_koishi.Schema.number().default(10).description("视频最大大小(MB)"),
|
|
53
|
+
imageMaxSize: import_koishi.Schema.number().default(5).description("图片最大大小(MB)")
|
|
49
54
|
});
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
55
|
+
async function apply(ctx, config) {
|
|
56
|
+
const { caveFilePath, resourceDir, pendingFilePath } = await initCavePaths(ctx);
|
|
57
|
+
const lastUsed = /* @__PURE__ */ new Map();
|
|
58
|
+
ctx.command("cave [message:text]", "回声洞").usage("支持添加、抽取、查看、查询回声洞").example("cave 随机抽取回声洞").example("cave -a 内容 添加新回声洞").example("cave -g/r x 查看/删除指定回声洞").example("cave -p/d x/all 通过/拒绝待审回声洞").example("cave -l x 查询投稿者投稿列表").option("a", "添加回声洞").option("g", "查看回声洞", { type: "string" }).option("r", "删除回声洞", { type: "string" }).option("p", "通过审核", { type: "string" }).option("d", "拒绝审核", { type: "string" }).option("l", "查询投稿统计", { type: "string" }).before(async ({ session, options }) => {
|
|
59
|
+
if (config.blacklist.includes(session.userId)) return "你已被列入黑名单";
|
|
60
|
+
if (session.content && session.content.includes("-help")) return;
|
|
61
|
+
if ((options.l || options.p || options.d) && !config.manager.includes(session.userId)) {
|
|
62
|
+
return "此操作仅管理员可用";
|
|
63
|
+
}
|
|
64
|
+
}).action(async ({ session, options }, ...content) => {
|
|
65
|
+
return await handleCaveAction(ctx, config, session, options, content, lastUsed);
|
|
66
|
+
});
|
|
60
67
|
}
|
|
61
|
-
__name(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
logger.error(`写入文件失败: ${error.message}`);
|
|
67
|
-
throw error;
|
|
68
|
+
__name(apply, "apply");
|
|
69
|
+
var logger = new import_koishi.Logger("cave");
|
|
70
|
+
var FileHandler = class {
|
|
71
|
+
static {
|
|
72
|
+
__name(this, "FileHandler");
|
|
68
73
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
static readJsonData(filePath, validator) {
|
|
75
|
+
try {
|
|
76
|
+
const data = fs.readFileSync(filePath, "utf8");
|
|
77
|
+
const parsed = JSON.parse(data || "[]");
|
|
78
|
+
if (!Array.isArray(parsed)) return [];
|
|
79
|
+
return validator ? parsed.filter(validator) : parsed;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
75
82
|
}
|
|
76
|
-
} catch (error) {
|
|
77
|
-
logger.error(`创建目录失败 ${dir}: ${error.message}`);
|
|
78
|
-
throw error;
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await fs.promises.writeFile(filePath, defaultContent, "utf8");
|
|
84
|
+
static writeJsonData(filePath, data) {
|
|
85
|
+
try {
|
|
86
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
86
89
|
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
logger.error(`创建文件失败 ${filePath}: ${error.message}`);
|
|
89
|
-
throw error;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
static async ensureDirectory(dir) {
|
|
92
|
+
try {
|
|
93
|
+
if (!fs.existsSync(dir)) {
|
|
94
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
static async ensureJsonFile(filePath, defaultContent = "[]") {
|
|
101
|
+
try {
|
|
102
|
+
if (!fs.existsSync(filePath)) {
|
|
103
|
+
await fs.promises.writeFile(filePath, defaultContent, "utf8");
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
async function saveMedia(urls, fileSuggestions, resourceDir, caveId, config, ctx, mediaType) {
|
|
94
111
|
const savedFiles = [];
|
|
112
|
+
const defaults = mediaType === "img" ? { ext: "png", accept: "image/*", maxSize: config.imageMaxSize } : { ext: "mp4", accept: "video/*", maxSize: config.videoMaxSize };
|
|
113
|
+
const extPattern = /\.[a-zA-Z0-9]+$/;
|
|
95
114
|
for (let i = 0; i < urls.length; i++) {
|
|
96
115
|
try {
|
|
97
116
|
const url = urls[i];
|
|
98
117
|
const processedUrl = (() => {
|
|
99
118
|
try {
|
|
100
119
|
const decodedUrl = decodeURIComponent(url);
|
|
101
|
-
|
|
102
|
-
return decodedUrl.replace(/&/g, "&");
|
|
103
|
-
}
|
|
104
|
-
return url;
|
|
120
|
+
return decodedUrl.includes("multimedia.nt.qq.com.cn") ? decodedUrl.replace(/&/g, "&") : url;
|
|
105
121
|
} catch {
|
|
106
122
|
return url;
|
|
107
123
|
}
|
|
108
124
|
})();
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
let ext = defaults.ext;
|
|
126
|
+
const suggestion = fileSuggestions[i];
|
|
127
|
+
if (suggestion && suggestion.includes(";")) {
|
|
128
|
+
const parts = suggestion.split(";");
|
|
129
|
+
const filenameCandidate = parts[0];
|
|
130
|
+
const sizeCandidate = parseInt(parts[1]);
|
|
131
|
+
if (extPattern.test(filenameCandidate)) {
|
|
132
|
+
ext = filenameCandidate.match(extPattern)[0].slice(1);
|
|
133
|
+
}
|
|
134
|
+
if (sizeCandidate > defaults.maxSize * 1024 * 1024) {
|
|
135
|
+
throw new Error(`${mediaType === "img" ? "图片" : "视频"}超出大小限制 (${defaults.maxSize}MB),实际大小为 ${(sizeCandidate / (1024 * 1024)).toFixed(2)}MB`);
|
|
136
|
+
}
|
|
137
|
+
} else if (suggestion && extPattern.test(suggestion)) {
|
|
138
|
+
ext = suggestion.match(extPattern)[0].slice(1);
|
|
139
|
+
}
|
|
140
|
+
let filename;
|
|
141
|
+
if (suggestion && suggestion.includes(";")) {
|
|
142
|
+
const parts = suggestion.split(";");
|
|
143
|
+
const srcFilename = path.basename(parts[0]);
|
|
144
|
+
filename = `${caveId}_${srcFilename}`;
|
|
145
|
+
} else if (suggestion && extPattern.test(suggestion)) {
|
|
146
|
+
const srcFilename = path.basename(suggestion);
|
|
147
|
+
filename = `${caveId}_${srcFilename}`;
|
|
148
|
+
} else {
|
|
149
|
+
filename = `${caveId}_${i + 1}.${ext}`;
|
|
150
|
+
}
|
|
151
|
+
const targetPath = path.join(resourceDir, filename);
|
|
152
|
+
const response = await ctx.http(processedUrl, {
|
|
153
|
+
method: "GET",
|
|
113
154
|
responseType: "arraybuffer",
|
|
114
155
|
timeout: 3e4,
|
|
115
156
|
headers: {
|
|
116
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
|
|
117
|
-
"Accept":
|
|
157
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
|
|
158
|
+
"Accept": defaults.accept,
|
|
118
159
|
"Referer": "https://qq.com"
|
|
119
160
|
}
|
|
120
161
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
162
|
+
const fileBuffer = Buffer.from(response.data);
|
|
163
|
+
await fs.promises.writeFile(targetPath, fileBuffer);
|
|
164
|
+
savedFiles.push(filename);
|
|
125
165
|
} catch (error) {
|
|
126
|
-
|
|
166
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
127
167
|
}
|
|
128
168
|
}
|
|
129
169
|
return savedFiles;
|
|
130
170
|
}
|
|
131
|
-
__name(
|
|
171
|
+
__name(saveMedia, "saveMedia");
|
|
132
172
|
async function sendAuditMessage(ctx, config, cave, content) {
|
|
133
173
|
const auditMessage = `待审核回声洞:
|
|
134
174
|
${content}
|
|
@@ -137,12 +177,12 @@ ${content}
|
|
|
137
177
|
try {
|
|
138
178
|
await ctx.bots[0]?.sendPrivateMessage(managerId, auditMessage);
|
|
139
179
|
} catch (error) {
|
|
140
|
-
logger.error(
|
|
180
|
+
logger.error(`操作失败: ${error.message}`);
|
|
141
181
|
}
|
|
142
182
|
}
|
|
143
183
|
}
|
|
144
184
|
__name(sendAuditMessage, "sendAuditMessage");
|
|
145
|
-
async function handleSingleCaveAudit(ctx, cave, isApprove,
|
|
185
|
+
async function handleSingleCaveAudit(ctx, cave, isApprove, resourceDir, data) {
|
|
146
186
|
try {
|
|
147
187
|
if (isApprove && data) {
|
|
148
188
|
const caveWithoutIndex = {
|
|
@@ -150,108 +190,159 @@ async function handleSingleCaveAudit(ctx, cave, isApprove, imageDir, data) {
|
|
|
150
190
|
elements: cleanElementsForSave(cave.elements, false)
|
|
151
191
|
};
|
|
152
192
|
data.push(caveWithoutIndex);
|
|
153
|
-
logger.info(`审核通过回声洞(${cave.cave_id})`);
|
|
154
193
|
} else if (!isApprove && cave.elements) {
|
|
155
194
|
for (const element of cave.elements) {
|
|
156
195
|
if (element.type === "img" && element.file) {
|
|
157
|
-
const fullPath = path.join(
|
|
196
|
+
const fullPath = path.join(resourceDir, element.file);
|
|
158
197
|
if (fs.existsSync(fullPath)) fs.unlinkSync(fullPath);
|
|
159
198
|
}
|
|
160
199
|
}
|
|
161
|
-
logger.info(`审核失败回声洞(${cave.cave_id})`);
|
|
162
200
|
}
|
|
163
201
|
return true;
|
|
164
202
|
} catch (error) {
|
|
165
|
-
|
|
166
|
-
return false;
|
|
203
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
167
204
|
}
|
|
168
205
|
}
|
|
169
206
|
__name(handleSingleCaveAudit, "handleSingleCaveAudit");
|
|
170
|
-
async function handleAudit(ctx, pendingData, isApprove, caveFilePath,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
207
|
+
async function handleAudit(ctx, pendingData, isApprove, caveFilePath, resourceDir, pendingFilePath, targetId) {
|
|
208
|
+
try {
|
|
209
|
+
if (pendingData.length === 0) return "没有待审核回声洞";
|
|
210
|
+
if (typeof targetId === "number") {
|
|
211
|
+
const pendingIndex = pendingData.findIndex((item) => item.cave_id === targetId);
|
|
212
|
+
if (pendingIndex === -1) return "未找到该待审核回声洞";
|
|
213
|
+
const cave = pendingData[pendingIndex];
|
|
214
|
+
const data2 = isApprove ? FileHandler.readJsonData(caveFilePath) : null;
|
|
215
|
+
const success = await handleSingleCaveAudit(ctx, cave, isApprove, resourceDir, data2);
|
|
216
|
+
if (!success) return "处理失败,请稍后重试";
|
|
217
|
+
if (isApprove && data2) FileHandler.writeJsonData(caveFilePath, data2);
|
|
218
|
+
pendingData.splice(pendingIndex, 1);
|
|
219
|
+
FileHandler.writeJsonData(pendingFilePath, pendingData);
|
|
220
|
+
const remainingCount = pendingData.length;
|
|
221
|
+
if (remainingCount > 0) {
|
|
222
|
+
const remainingIds = pendingData.map((c) => c.cave_id).join(", ");
|
|
223
|
+
return `${isApprove ? "审核通过" : "拒绝"}成功,还有 ${remainingCount} 条待审核:[${remainingIds}]`;
|
|
224
|
+
}
|
|
225
|
+
return isApprove ? "已通过该回声洞" : "已拒绝该回声洞";
|
|
186
226
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (
|
|
227
|
+
const data = isApprove ? FileHandler.readJsonData(caveFilePath) : null;
|
|
228
|
+
let processedCount = 0;
|
|
229
|
+
for (const cave of pendingData) {
|
|
230
|
+
const success = await handleSingleCaveAudit(ctx, cave, isApprove, resourceDir, data);
|
|
231
|
+
if (success) processedCount++;
|
|
232
|
+
}
|
|
233
|
+
if (isApprove && data) FileHandler.writeJsonData(caveFilePath, data);
|
|
234
|
+
FileHandler.writeJsonData(pendingFilePath, []);
|
|
235
|
+
return isApprove ? `已通过 ${processedCount}/${pendingData.length} 条回声洞` : `已拒绝 ${processedCount}/${pendingData.length} 条回声洞`;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
194
238
|
}
|
|
195
|
-
if (isApprove && data) writeJsonData(caveFilePath, data);
|
|
196
|
-
writeJsonData(pendingFilePath, []);
|
|
197
|
-
return isApprove ? `✅ 已通过 ${processedCount}/${pendingData.length} 条回声洞` : `❌ 已拒绝 ${processedCount}/${pendingData.length} 条回声洞`;
|
|
198
239
|
}
|
|
199
240
|
__name(handleAudit, "handleAudit");
|
|
200
|
-
function
|
|
241
|
+
function cleanElementsForSave(elements, keepIndex = false) {
|
|
242
|
+
const sorted = elements.sort((a, b) => a.index - b.index);
|
|
243
|
+
return sorted.map(({ type, content, file, index }) => ({
|
|
244
|
+
type,
|
|
245
|
+
...keepIndex && { index },
|
|
246
|
+
...content && { content },
|
|
247
|
+
...file && { file }
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
__name(cleanElementsForSave, "cleanElementsForSave");
|
|
251
|
+
async function extractMediaContent(originalContent) {
|
|
252
|
+
const parsedTexts = originalContent.split(/<img[^>]+>|<video[^>]+>/g).map((t) => t.trim()).filter((t) => t);
|
|
253
|
+
const textParts = [];
|
|
254
|
+
parsedTexts.forEach((text, idx) => {
|
|
255
|
+
textParts.push({ type: "text", content: text, index: idx * 3 });
|
|
256
|
+
});
|
|
257
|
+
const imageUrls = [];
|
|
258
|
+
const imageElements = [];
|
|
259
|
+
const videoUrls = [];
|
|
260
|
+
const videoElements = [];
|
|
261
|
+
const imgMatches = originalContent.match(/<img[^>]+src="([^"]+)"[^>]*>/g) || [];
|
|
262
|
+
imgMatches.forEach((img, idx) => {
|
|
263
|
+
const srcMatch = img.match(/src="([^"]+)"/);
|
|
264
|
+
const fileMatch = img.match(/file="([^"]+)"(?:\s+fileSize="([^"]+)")?/);
|
|
265
|
+
if (srcMatch?.[1]) {
|
|
266
|
+
imageUrls.push(srcMatch[1]);
|
|
267
|
+
const suggestion = fileMatch ? fileMatch[2] ? `${fileMatch[1]};${fileMatch[2]}` : fileMatch[1] : void 0;
|
|
268
|
+
imageElements.push({ type: "img", index: idx * 3 + 1, fileAttr: suggestion });
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
const videoMatches = originalContent.match(/<video[^>]+src="([^"]+)"[^>]*>/g) || [];
|
|
272
|
+
videoMatches.forEach((video, idx) => {
|
|
273
|
+
const srcMatch = video.match(/src="([^"]+)"/);
|
|
274
|
+
const fileMatch = video.match(/file="([^"]+)"(?:\s+fileSize="([^"]+)")?/);
|
|
275
|
+
if (srcMatch?.[1]) {
|
|
276
|
+
videoUrls.push(srcMatch[1]);
|
|
277
|
+
const suggestion = fileMatch ? fileMatch[2] ? `${fileMatch[1]};${fileMatch[2]}` : fileMatch[1] : void 0;
|
|
278
|
+
videoElements.push({ type: "video", index: idx * 3 + 2, fileAttr: suggestion });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
return { imageUrls, imageElements, videoUrls, videoElements, textParts };
|
|
282
|
+
}
|
|
283
|
+
__name(extractMediaContent, "extractMediaContent");
|
|
284
|
+
async function buildMessage(cave, resourceDir, session) {
|
|
201
285
|
let content = `回声洞 ——(${cave.cave_id})
|
|
202
286
|
`;
|
|
287
|
+
const videoElements = [];
|
|
203
288
|
for (const element of cave.elements) {
|
|
204
289
|
if (element.type === "text") {
|
|
205
290
|
content += element.content + "\n";
|
|
206
291
|
} else if (element.type === "img" && element.file) {
|
|
207
292
|
try {
|
|
208
|
-
const fullImagePath = path.join(
|
|
293
|
+
const fullImagePath = path.join(resourceDir, element.file);
|
|
209
294
|
if (fs.existsSync(fullImagePath)) {
|
|
210
295
|
const imageBuffer = fs.readFileSync(fullImagePath);
|
|
211
296
|
const base64Image = imageBuffer.toString("base64");
|
|
212
297
|
content += (0, import_koishi.h)("image", { src: `data:image/png;base64,${base64Image}` }) + "\n";
|
|
213
298
|
}
|
|
214
299
|
} catch (error) {
|
|
215
|
-
|
|
300
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
216
301
|
}
|
|
302
|
+
} else if (element.type === "video" && element.file) {
|
|
303
|
+
videoElements.push({ file: element.file });
|
|
217
304
|
}
|
|
218
305
|
}
|
|
219
|
-
|
|
306
|
+
if (videoElements.length > 0 && session) {
|
|
307
|
+
content += `[视频已发送]
|
|
308
|
+
`;
|
|
309
|
+
for (const video of videoElements) {
|
|
310
|
+
try {
|
|
311
|
+
const fullVideoPath = path.join(resourceDir, video.file);
|
|
312
|
+
if (fs.existsSync(fullVideoPath)) {
|
|
313
|
+
const videoBuffer = fs.readFileSync(fullVideoPath);
|
|
314
|
+
const base64Video = videoBuffer.toString("base64");
|
|
315
|
+
session.send((0, import_koishi.h)("video", { src: `data:video/mp4;base64,${base64Video}` })).catch((error) => {
|
|
316
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
content += `—— ${cave.contributor_name}`;
|
|
325
|
+
return content;
|
|
220
326
|
}
|
|
221
327
|
__name(buildMessage, "buildMessage");
|
|
222
|
-
function
|
|
223
|
-
const sorted = elements.sort((a, b) => a.index - b.index);
|
|
224
|
-
return sorted.map(({ type, content, file, index }) => ({
|
|
225
|
-
type,
|
|
226
|
-
...keepIndex && { index },
|
|
227
|
-
...content && { content },
|
|
228
|
-
...file && { file }
|
|
229
|
-
}));
|
|
230
|
-
}
|
|
231
|
-
__name(cleanElementsForSave, "cleanElementsForSave");
|
|
232
|
-
async function apply(ctx, config) {
|
|
328
|
+
async function initCavePaths(ctx) {
|
|
233
329
|
const dataDir = path.join(ctx.baseDir, "data");
|
|
234
330
|
const caveDir = path.join(dataDir, "cave");
|
|
235
331
|
const caveFilePath = path.join(caveDir, "cave.json");
|
|
236
|
-
const
|
|
332
|
+
const resourceDir = path.join(caveDir, "resources");
|
|
237
333
|
const pendingFilePath = path.join(caveDir, "pending.json");
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if ((options.l || options.p || options.d) && !config.manager.includes(session.userId)) {
|
|
251
|
-
return "只有管理员才能执行此操作";
|
|
252
|
-
}
|
|
253
|
-
}).action(async ({ session, options }, ...content) => {
|
|
254
|
-
if (options.l !== void 0) {
|
|
334
|
+
await FileHandler.ensureDirectory(dataDir);
|
|
335
|
+
await FileHandler.ensureDirectory(caveDir);
|
|
336
|
+
await FileHandler.ensureDirectory(resourceDir);
|
|
337
|
+
await FileHandler.ensureJsonFile(caveFilePath);
|
|
338
|
+
await FileHandler.ensureJsonFile(pendingFilePath);
|
|
339
|
+
return { dataDir, caveDir, caveFilePath, resourceDir, pendingFilePath };
|
|
340
|
+
}
|
|
341
|
+
__name(initCavePaths, "initCavePaths");
|
|
342
|
+
async function handleCaveAction(ctx, config, session, options, content, lastUsed) {
|
|
343
|
+
const { caveFilePath, resourceDir, pendingFilePath } = await initCavePaths(ctx);
|
|
344
|
+
async function processList() {
|
|
345
|
+
try {
|
|
255
346
|
let formatIds = function(ids) {
|
|
256
347
|
const lines = [];
|
|
257
348
|
for (let i = 0; i < ids.length; i += 10) {
|
|
@@ -260,30 +351,27 @@ async function apply(ctx, config) {
|
|
|
260
351
|
return lines.join("\n");
|
|
261
352
|
};
|
|
262
353
|
__name(formatIds, "formatIds");
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
const caveData = readJsonData(caveFilePath2);
|
|
354
|
+
const caveData = FileHandler.readJsonData(caveFilePath);
|
|
355
|
+
const caveDir = path.dirname(caveFilePath);
|
|
266
356
|
const stats = {};
|
|
267
357
|
for (const cave of caveData) {
|
|
268
358
|
if (cave.contributor_number === "10000") continue;
|
|
269
359
|
if (!stats[cave.contributor_number]) stats[cave.contributor_number] = [];
|
|
270
360
|
stats[cave.contributor_number].push(cave.cave_id);
|
|
271
361
|
}
|
|
272
|
-
const statFilePath = path.join(
|
|
362
|
+
const statFilePath = path.join(caveDir, "stat.json");
|
|
273
363
|
try {
|
|
274
364
|
fs.writeFileSync(statFilePath, JSON.stringify(stats, null, 2), "utf8");
|
|
275
365
|
} catch (error) {
|
|
276
|
-
|
|
366
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
277
367
|
}
|
|
278
368
|
let queryId = null;
|
|
279
369
|
if (typeof options.l === "string") {
|
|
280
370
|
const match = String(options.l).match(/\d+/);
|
|
281
371
|
if (match) queryId = match[0];
|
|
282
|
-
} else if (
|
|
372
|
+
} else if (content.length > 0) {
|
|
283
373
|
const numberMatch = content.join(" ").match(/\d+/);
|
|
284
|
-
if (numberMatch)
|
|
285
|
-
queryId = numberMatch[0];
|
|
286
|
-
}
|
|
374
|
+
if (numberMatch) queryId = numberMatch[0];
|
|
287
375
|
}
|
|
288
376
|
if (queryId) {
|
|
289
377
|
if (stats[queryId]) {
|
|
@@ -300,203 +388,236 @@ async function apply(ctx, config) {
|
|
|
300
388
|
return `${cid} 共计投稿 ${ids.length} 项回声洞:
|
|
301
389
|
` + formatIds(ids);
|
|
302
390
|
});
|
|
303
|
-
return
|
|
391
|
+
return `==回声洞共计投稿 ${total} 项==
|
|
304
392
|
` + lines.join("\n");
|
|
305
393
|
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
return `操作失败: ${error.message}`;
|
|
306
396
|
}
|
|
397
|
+
}
|
|
398
|
+
__name(processList, "processList");
|
|
399
|
+
async function processAudit() {
|
|
307
400
|
try {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return await handleAudit(ctx, pendingData, isApprove, caveFilePath, imageDir, pendingFilePath);
|
|
313
|
-
}
|
|
314
|
-
const id = parseInt(content[0] || (typeof options.p === "string" ? options.p : "") || (typeof options.d === "string" ? options.d : ""));
|
|
315
|
-
if (isNaN(id)) return "请输入正确的回声洞序号";
|
|
316
|
-
return await handleAudit(ctx, pendingData, isApprove, caveFilePath, imageDir, pendingFilePath, id);
|
|
401
|
+
const pendingData = FileHandler.readJsonData(pendingFilePath);
|
|
402
|
+
const isApprove = Boolean(options.p);
|
|
403
|
+
if (options.p === true && content[0] === "all" || options.d === true && content[0] === "all") {
|
|
404
|
+
return await handleAudit(ctx, pendingData, isApprove, caveFilePath, resourceDir, pendingFilePath);
|
|
317
405
|
}
|
|
318
|
-
const
|
|
406
|
+
const id = parseInt(content[0] || (typeof options.p === "string" ? options.p : "") || (typeof options.d === "string" ? options.d : ""));
|
|
407
|
+
if (isNaN(id)) return "请输入正确的回声洞序号";
|
|
408
|
+
return await handleAudit(ctx, pendingData, isApprove, caveFilePath, resourceDir, pendingFilePath, id);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
return `操作失败: ${error.message}`;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
__name(processAudit, "processAudit");
|
|
414
|
+
async function processView() {
|
|
415
|
+
try {
|
|
416
|
+
const caveId = parseInt(content[0] || (typeof options.g === "string" ? options.g : ""));
|
|
417
|
+
if (isNaN(caveId)) return "请输入正确的回声洞序号";
|
|
418
|
+
const data = FileHandler.readJsonData(
|
|
319
419
|
caveFilePath,
|
|
320
420
|
(item) => item && typeof item.cave_id === "number" && Array.isArray(item.elements) && item.elements.every(
|
|
321
|
-
(el) => el.type === "text" && typeof el.content === "string" || el.type === "img" && typeof el.file === "string"
|
|
421
|
+
(el) => el.type === "text" && typeof el.content === "string" || el.type === "img" && typeof el.file === "string" || el.type === "video" && typeof el.file === "string"
|
|
322
422
|
) && typeof item.contributor_number === "string" && typeof item.contributor_name === "string"
|
|
323
423
|
);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
424
|
+
const cave = data.find((item) => item.cave_id === caveId);
|
|
425
|
+
if (!cave) return "未找到该序号的回声洞";
|
|
426
|
+
const caveContent = await buildMessage(cave, resourceDir, session);
|
|
427
|
+
return caveContent;
|
|
428
|
+
} catch (error) {
|
|
429
|
+
return `操作失败: ${error.message}`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
__name(processView, "processView");
|
|
433
|
+
async function processRandom() {
|
|
434
|
+
const data = FileHandler.readJsonData(
|
|
435
|
+
caveFilePath,
|
|
436
|
+
(item) => item && typeof item.cave_id === "number" && Array.isArray(item.elements) && item.elements.every(
|
|
437
|
+
(el) => el.type === "text" && typeof el.content === "string" || el.type === "img" && typeof el.file === "string"
|
|
438
|
+
) && typeof item.contributor_number === "string" && typeof item.contributor_name === "string"
|
|
439
|
+
);
|
|
440
|
+
if (data.length === 0) return "暂无回声洞可用";
|
|
441
|
+
const guildId = session.guildId;
|
|
442
|
+
const now = Date.now();
|
|
443
|
+
const lastCall = lastUsed.get(guildId) || 0;
|
|
444
|
+
const isManager = config.manager.includes(session.userId);
|
|
445
|
+
if (!isManager && now - lastCall < config.number * 1e3) {
|
|
446
|
+
const waitTime = Math.ceil((config.number * 1e3 - (now - lastCall)) / 1e3);
|
|
447
|
+
return `群聊冷却中...请${waitTime}秒后再试`;
|
|
448
|
+
}
|
|
449
|
+
if (!isManager) lastUsed.set(guildId, now);
|
|
450
|
+
const cave = (() => {
|
|
451
|
+
const validCaves = data.filter((cave2) => cave2.elements && cave2.elements.length > 0);
|
|
452
|
+
if (!validCaves.length) return void 0;
|
|
453
|
+
const randomIndex = Math.floor(Math.random() * validCaves.length);
|
|
454
|
+
return validCaves[randomIndex];
|
|
455
|
+
})();
|
|
456
|
+
return cave ? buildMessage(cave, resourceDir, session) : "获取回声洞失败";
|
|
457
|
+
}
|
|
458
|
+
__name(processRandom, "processRandom");
|
|
459
|
+
async function processDelete() {
|
|
460
|
+
try {
|
|
461
|
+
const caveId = parseInt(content[0] || (typeof options.r === "string" ? options.r : ""));
|
|
462
|
+
if (isNaN(caveId)) return "请输入正确的回声洞序号";
|
|
463
|
+
const data = FileHandler.readJsonData(
|
|
464
|
+
caveFilePath,
|
|
465
|
+
(item) => item && typeof item.cave_id === "number"
|
|
466
|
+
);
|
|
467
|
+
const pendingData = FileHandler.readJsonData(pendingFilePath);
|
|
468
|
+
const index = data.findIndex((item) => item.cave_id === caveId);
|
|
469
|
+
const pendingIndex = pendingData.findIndex((item) => item.cave_id === caveId);
|
|
470
|
+
if (index === -1 && pendingIndex === -1) return "未找到该序号的回声洞";
|
|
471
|
+
let targetCave;
|
|
472
|
+
let isPending = false;
|
|
473
|
+
if (index !== -1) {
|
|
474
|
+
targetCave = data[index];
|
|
475
|
+
} else {
|
|
476
|
+
targetCave = pendingData[pendingIndex];
|
|
477
|
+
isPending = true;
|
|
478
|
+
}
|
|
479
|
+
if (targetCave.contributor_number !== session.userId && !config.manager.includes(session.userId)) {
|
|
480
|
+
return "不可删除他人添加的回声洞!";
|
|
481
|
+
}
|
|
482
|
+
const caveContent = await buildMessage(targetCave, resourceDir, session);
|
|
483
|
+
if (targetCave.elements) {
|
|
484
|
+
try {
|
|
485
|
+
for (const element of targetCave.elements) {
|
|
486
|
+
if ((element.type === "img" || element.type === "video") && element.file) {
|
|
487
|
+
const fullPath = path.join(resourceDir, element.file);
|
|
488
|
+
if (fs.existsSync(fullPath)) fs.unlinkSync(fullPath);
|
|
489
|
+
}
|
|
385
490
|
}
|
|
491
|
+
} catch (error) {
|
|
492
|
+
return `操作失败: ${error.message}`;
|
|
386
493
|
}
|
|
387
|
-
const newCave = {
|
|
388
|
-
cave_id: caveId,
|
|
389
|
-
elements: cleanElementsForSave(elements, true),
|
|
390
|
-
contributor_number: session.userId,
|
|
391
|
-
contributor_name: contributorName
|
|
392
|
-
};
|
|
393
|
-
if (config.enableAudit) {
|
|
394
|
-
pendingData.push({
|
|
395
|
-
...newCave,
|
|
396
|
-
elements: cleanElementsForSave(elements, true)
|
|
397
|
-
});
|
|
398
|
-
writeJsonData(pendingFilePath, pendingData);
|
|
399
|
-
await sendAuditMessage(ctx, config, newCave, buildMessage(newCave, imageDir));
|
|
400
|
-
return `✨ 已提交审核,序号为 (${caveId})`;
|
|
401
|
-
}
|
|
402
|
-
const caveWithoutIndex = {
|
|
403
|
-
...newCave,
|
|
404
|
-
elements: cleanElementsForSave(elements, false)
|
|
405
|
-
};
|
|
406
|
-
data.push(caveWithoutIndex);
|
|
407
|
-
writeJsonData(caveFilePath, data);
|
|
408
|
-
return `✨ 添加成功!序号为 (${caveId})`;
|
|
409
494
|
}
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
495
|
+
if (isPending) {
|
|
496
|
+
pendingData.splice(pendingIndex, 1);
|
|
497
|
+
FileHandler.writeJsonData(pendingFilePath, pendingData);
|
|
498
|
+
return `已删除(待审核)
|
|
499
|
+
${caveContent}`;
|
|
500
|
+
} else {
|
|
501
|
+
data.splice(index, 1);
|
|
502
|
+
FileHandler.writeJsonData(caveFilePath, data);
|
|
503
|
+
return `已删除
|
|
504
|
+
${caveContent}`;
|
|
420
505
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
506
|
+
} catch (error) {
|
|
507
|
+
return `操作失败: ${error.message}`;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
__name(processDelete, "processDelete");
|
|
511
|
+
async function processAdd() {
|
|
512
|
+
try {
|
|
513
|
+
let originalContent = session.quote?.content || session.content;
|
|
514
|
+
const prefixes = Array.isArray(session.app.config.prefix) ? session.app.config.prefix : [session.app.config.prefix];
|
|
515
|
+
const nicknames = Array.isArray(session.app.config.nickname) ? session.app.config.nickname : session.app.config.nickname ? [session.app.config.nickname] : [];
|
|
516
|
+
const allTriggers = [...prefixes, ...nicknames];
|
|
517
|
+
const triggerPattern = allTriggers.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
518
|
+
const commandPattern = new RegExp(`^(?:${triggerPattern}).*?-a\\s*`);
|
|
519
|
+
originalContent = originalContent.replace(commandPattern, "");
|
|
520
|
+
let { imageUrls, imageElements, videoUrls, videoElements, textParts } = await extractMediaContent(originalContent);
|
|
521
|
+
if (textParts.length === 0 && imageUrls.length === 0 && videoUrls.length === 0) {
|
|
522
|
+
await session.send("请在一分钟内发送你要添加的内容");
|
|
523
|
+
const reply = await session.prompt({ timeout: 6e4 });
|
|
524
|
+
if (!reply || reply.trim() === "") {
|
|
525
|
+
return "操作超时,放弃本次添加";
|
|
433
526
|
}
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (!cave) return "获取回声洞失败";
|
|
441
|
-
return buildMessage(cave, imageDir);
|
|
527
|
+
const replyResult = await extractMediaContent(reply);
|
|
528
|
+
imageUrls = replyResult.imageUrls;
|
|
529
|
+
imageElements = replyResult.imageElements;
|
|
530
|
+
videoUrls = replyResult.videoUrls;
|
|
531
|
+
videoElements = replyResult.videoElements;
|
|
532
|
+
textParts = replyResult.textParts;
|
|
442
533
|
}
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
534
|
+
if (videoUrls.length > 0 && !config.allowVideo) {
|
|
535
|
+
return "已关闭上传视频功能";
|
|
536
|
+
}
|
|
537
|
+
const pendingData = FileHandler.readJsonData(pendingFilePath);
|
|
538
|
+
const data = FileHandler.readJsonData(caveFilePath, (item) => item && typeof item.cave_id === "number");
|
|
539
|
+
const maxDataId = data.length > 0 ? Math.max(...data.map((item) => item.cave_id)) : 0;
|
|
540
|
+
const maxPendingId = pendingData.length > 0 ? Math.max(...pendingData.map((item) => item.cave_id)) : 0;
|
|
541
|
+
const caveId = Math.max(maxDataId, maxPendingId) + 1;
|
|
542
|
+
let savedImages = [];
|
|
543
|
+
if (imageUrls.length > 0) {
|
|
544
|
+
try {
|
|
545
|
+
const fileSuggestions = imageElements.map((el) => el.fileAttr);
|
|
546
|
+
savedImages = await saveMedia(imageUrls, fileSuggestions, resourceDir, caveId, config, ctx, "img");
|
|
547
|
+
} catch (error) {
|
|
548
|
+
return `操作失败: ${error.message}`;
|
|
453
549
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
550
|
+
}
|
|
551
|
+
let savedVideos = [];
|
|
552
|
+
if (videoUrls.length > 0) {
|
|
553
|
+
try {
|
|
554
|
+
const fileSuggestions = videoElements.map((el) => el.fileAttr);
|
|
555
|
+
savedVideos = await saveMedia(videoUrls, fileSuggestions, resourceDir, caveId, config, ctx, "video");
|
|
556
|
+
} catch (error) {
|
|
557
|
+
return `操作失败: ${error.message}`;
|
|
461
558
|
}
|
|
462
|
-
|
|
463
|
-
|
|
559
|
+
}
|
|
560
|
+
const elements = [];
|
|
561
|
+
elements.push(...textParts);
|
|
562
|
+
savedImages.forEach((file, idx) => {
|
|
563
|
+
if (imageElements[idx]) {
|
|
564
|
+
elements.push({ ...imageElements[idx], type: "img", file });
|
|
464
565
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const fullPath = path.join(imageDir, element.file);
|
|
470
|
-
if (fs.existsSync(fullPath)) {
|
|
471
|
-
fs.unlinkSync(fullPath);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
} catch (error) {
|
|
476
|
-
logger.error(`删除图片失败: ${error.message}`);
|
|
477
|
-
}
|
|
566
|
+
});
|
|
567
|
+
savedVideos.forEach((file, idx) => {
|
|
568
|
+
if (videoElements[idx]) {
|
|
569
|
+
elements.push({ ...videoElements[idx], type: "video", file });
|
|
478
570
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
571
|
+
});
|
|
572
|
+
elements.sort((a, b) => a.index - b.index);
|
|
573
|
+
let contributorName = session.username;
|
|
574
|
+
if (ctx.database) {
|
|
575
|
+
try {
|
|
576
|
+
const userInfo = await ctx.database.getUser(session.platform, session.userId);
|
|
577
|
+
contributorName = userInfo?.nickname || session.username;
|
|
578
|
+
} catch (error) {
|
|
579
|
+
throw new Error(`操作失败: ${error.message}`);
|
|
487
580
|
}
|
|
488
581
|
}
|
|
582
|
+
const newCave = {
|
|
583
|
+
cave_id: caveId,
|
|
584
|
+
elements: cleanElementsForSave(elements, true),
|
|
585
|
+
contributor_number: session.userId,
|
|
586
|
+
contributor_name: contributorName
|
|
587
|
+
};
|
|
588
|
+
if (config.enableAudit) {
|
|
589
|
+
pendingData.push({ ...newCave, elements: cleanElementsForSave(elements, true) });
|
|
590
|
+
FileHandler.writeJsonData(pendingFilePath, pendingData);
|
|
591
|
+
await sendAuditMessage(ctx, config, newCave, await buildMessage(newCave, resourceDir, session));
|
|
592
|
+
return `已提交审核,序号为 (${caveId})`;
|
|
593
|
+
}
|
|
594
|
+
const caveWithoutIndex = { ...newCave, elements: cleanElementsForSave(elements, false) };
|
|
595
|
+
data.push(caveWithoutIndex);
|
|
596
|
+
FileHandler.writeJsonData(caveFilePath, data);
|
|
597
|
+
return `添加成功!序号为 (${caveId})`;
|
|
489
598
|
} catch (error) {
|
|
490
|
-
|
|
491
|
-
return "操作失败,请稍后重试";
|
|
599
|
+
return `操作失败: ${error.message}`;
|
|
492
600
|
}
|
|
493
|
-
}
|
|
601
|
+
}
|
|
602
|
+
__name(processAdd, "processAdd");
|
|
603
|
+
try {
|
|
604
|
+
if (options.l !== void 0) return await processList();
|
|
605
|
+
if (options.p || options.d) return await processAudit();
|
|
606
|
+
if (options.g) return await processView();
|
|
607
|
+
if (options.r) return await processDelete();
|
|
608
|
+
if (options.a) return await processAdd();
|
|
609
|
+
return await processRandom();
|
|
610
|
+
} catch (error) {
|
|
611
|
+
return `操作失败: ${error.message.replace(/^操作失败: /, "")}`;
|
|
612
|
+
}
|
|
494
613
|
}
|
|
495
|
-
__name(
|
|
614
|
+
__name(handleCaveAction, "handleCaveAction");
|
|
496
615
|
// Annotate the CommonJS export names for ESM import in node:
|
|
497
616
|
0 && (module.exports = {
|
|
498
617
|
Config,
|
|
499
618
|
apply,
|
|
619
|
+
handleCaveAction,
|
|
620
|
+
initCavePaths,
|
|
500
621
|
inject,
|
|
501
622
|
name
|
|
502
623
|
});
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -6,24 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
最好的 cave 插件,可开关的审核系统,可引用添加,支持图文混合内容,可查阅投稿列表,完美复刻你的 .cave 体验!
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### 核心功能
|
|
10
10
|
|
|
11
11
|
- 支持文字与图片混合保存
|
|
12
12
|
- 智能处理各类图片链接
|
|
13
|
+
- 可选的视频内容支持
|
|
13
14
|
- 完整的权限管理系统
|
|
14
15
|
- 可选的内容审核流程
|
|
15
16
|
- 群组调用冷却机制
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
### 指令
|
|
18
19
|
|
|
19
20
|
| 指令 | 说明 | 权限 |
|
|
20
21
|
|------|------|------|
|
|
21
22
|
| `cave` | 随机展示一条回声洞 | 所有人 |
|
|
22
|
-
| `cave -a <内容>` |
|
|
23
|
+
| `cave -a <内容>` | 添加新回声洞(支持文字、图片与视频) | 所有人 |
|
|
23
24
|
| `cave -g <编号>` | 查看指定回声洞 | 所有人 |
|
|
24
25
|
| `cave -r <编号>` | 删除指定回声洞 | 内容贡献者/管理员 |
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
#### 管理指令
|
|
27
28
|
|
|
28
29
|
| 指令 | 说明 | 权限 |
|
|
29
30
|
|------|------|------|
|
|
@@ -31,9 +32,10 @@
|
|
|
31
32
|
| `cave -p <编号/all>` | 通过待审核内容 | 管理员 |
|
|
32
33
|
| `cave -d <编号/all>` | 拒绝待审核内容 | 管理员 |
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
### 注意事项
|
|
35
36
|
|
|
36
|
-
1.
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
4.
|
|
37
|
+
1. 图片以及视频会自动保存到本地,请确保存储空间充足。
|
|
38
|
+
2. 管理员不受群组冷却时间限制。
|
|
39
|
+
3. 开启审核模式后,新内容需要审核才能生效。
|
|
40
|
+
4. 引用消息添加支持保留原消息格式。
|
|
41
|
+
5. 配置文件中可分别调整是否允许添加视频,以及各自的文件大小限制。
|