koishi-plugin-ccb-plus 0.2.6 → 0.2.8-beta.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.js +150 -104
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -42,10 +42,10 @@ var path = __toESM(require("path"));
|
|
|
42
42
|
var name = "ccb-plus";
|
|
43
43
|
var inject = ["database"];
|
|
44
44
|
var Config = import_koishi.Schema.object({
|
|
45
|
-
ywWindow: import_koishi.Schema.number().default(60).description("
|
|
45
|
+
ywWindow: import_koishi.Schema.number().default(60).description("全局触发冷却的窗口时间(秒)"),
|
|
46
46
|
ywThreshold: import_koishi.Schema.number().default(5).description("全局窗口时间内最大ccb数"),
|
|
47
|
-
ywBanDuration: import_koishi.Schema.number().default(900).description("
|
|
48
|
-
ywProbability: import_koishi.Schema.number().default(0.1).min(0).max(1).description("
|
|
47
|
+
ywBanDuration: import_koishi.Schema.number().default(900).description("全局冷却时长(秒)"),
|
|
48
|
+
ywProbability: import_koishi.Schema.number().default(0.1).min(0).max(1).description("全局随机冷却概率"),
|
|
49
49
|
whiteList: import_koishi.Schema.array(String).default([]).description("全局配置的黑名单"),
|
|
50
50
|
selfCcb: import_koishi.Schema.boolean().default(false).description("是否允许对自己ccb"),
|
|
51
51
|
critProb: import_koishi.Schema.number().default(0.2).min(0).max(1).description("全局暴击概率"),
|
|
@@ -54,9 +54,9 @@ var Config = import_koishi.Schema.object({
|
|
|
54
54
|
userId: import_koishi.Schema.string().required().description("用户ID"),
|
|
55
55
|
ywWindow: import_koishi.Schema.number().default(10).description("特权窗口时间(秒)"),
|
|
56
56
|
ywThreshold: import_koishi.Schema.number().default(999).description("特权窗口内最大次数"),
|
|
57
|
-
ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("
|
|
57
|
+
ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("特权冷却概率"),
|
|
58
58
|
critProb: import_koishi.Schema.number().default(0.8).min(0).max(1).description("特权暴击概率"),
|
|
59
|
-
ywBanDuration: import_koishi.Schema.number().default(60).description("
|
|
59
|
+
ywBanDuration: import_koishi.Schema.number().default(60).description("特权冷却时长(秒)")
|
|
60
60
|
})).role("table").description("开挂名单(优先级高于全局设置)")
|
|
61
61
|
});
|
|
62
62
|
function apply(ctx, config) {
|
|
@@ -82,6 +82,7 @@ function apply(ctx, config) {
|
|
|
82
82
|
const actionTimes = {};
|
|
83
83
|
const banList = {};
|
|
84
84
|
const nicknameCache = /* @__PURE__ */ new Map();
|
|
85
|
+
const MAX_CACHE_SIZE = 2e3;
|
|
85
86
|
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
86
87
|
ctx.on("ready", async () => {
|
|
87
88
|
const DATA_FILE = path.join(ctx.baseDir, "data", "ccb.json");
|
|
@@ -118,6 +119,27 @@ function apply(ctx, config) {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
});
|
|
122
|
+
const CLEANUP_INTERVAL = 10 * 60 * 1e3;
|
|
123
|
+
const cleanupTimer = setInterval(() => {
|
|
124
|
+
const now = Date.now() / 1e3;
|
|
125
|
+
for (const userId in banList) {
|
|
126
|
+
if (banList[userId] < now) delete banList[userId];
|
|
127
|
+
}
|
|
128
|
+
for (const userId in actionTimes) {
|
|
129
|
+
if (!actionTimes[userId] || actionTimes[userId].length === 0) {
|
|
130
|
+
delete actionTimes[userId];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const cacheNow = Date.now();
|
|
134
|
+
for (const [key, value] of nicknameCache) {
|
|
135
|
+
if (cacheNow - value.timestamp > CACHE_DURATION) {
|
|
136
|
+
nicknameCache.delete(key);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, CLEANUP_INTERVAL);
|
|
140
|
+
ctx.on("dispose", () => {
|
|
141
|
+
clearInterval(cleanupTimer);
|
|
142
|
+
});
|
|
121
143
|
function getAvatar(userId) {
|
|
122
144
|
return `https://q4.qlogo.cn/headimg_dl?dst_uin=${userId}&spec=640`;
|
|
123
145
|
}
|
|
@@ -133,6 +155,10 @@ function apply(ctx, config) {
|
|
|
133
155
|
if (name2 && name2 !== userId) {
|
|
134
156
|
const actualName = name2.trim();
|
|
135
157
|
if (actualName) {
|
|
158
|
+
if (nicknameCache.size >= MAX_CACHE_SIZE) {
|
|
159
|
+
const oldestKey = nicknameCache.keys().next().value;
|
|
160
|
+
if (oldestKey) nicknameCache.delete(oldestKey);
|
|
161
|
+
}
|
|
136
162
|
nicknameCache.set(cacheKey, { name: actualName, timestamp: now });
|
|
137
163
|
return actualName;
|
|
138
164
|
}
|
|
@@ -150,7 +176,7 @@ function apply(ctx, config) {
|
|
|
150
176
|
}
|
|
151
177
|
try {
|
|
152
178
|
const userInfo = await session.bot.getUser(userId);
|
|
153
|
-
const displayName = userInfo?.name || userInfo?.nick
|
|
179
|
+
const displayName = userInfo?.name || userInfo?.nick;
|
|
154
180
|
const result = setAndReturnName(displayName);
|
|
155
181
|
if (result) return result;
|
|
156
182
|
} catch (e) {
|
|
@@ -174,39 +200,62 @@ function apply(ctx, config) {
|
|
|
174
200
|
return null;
|
|
175
201
|
}
|
|
176
202
|
__name(checkGroupCommand, "checkGroupCommand");
|
|
203
|
+
async function findTargetUser(session, input) {
|
|
204
|
+
if (!input) return null;
|
|
205
|
+
const atMatch = input.match(/<at\s[^>]*id="([^"]+)"/);
|
|
206
|
+
if (atMatch) return atMatch[1];
|
|
207
|
+
const unionMatch = input.match(/^[^:]+:(.+)$/);
|
|
208
|
+
if (unionMatch) {
|
|
209
|
+
return unionMatch[1];
|
|
210
|
+
}
|
|
211
|
+
if (/^\d+$/.test(input)) {
|
|
212
|
+
return input;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const list = await session.bot.getGuildMemberList(session.guildId);
|
|
216
|
+
const members = list?.data || [];
|
|
217
|
+
const clean = /* @__PURE__ */ __name((s) => s.replace(/\s/g, "").toLowerCase(), "clean");
|
|
218
|
+
const targetName = clean(input);
|
|
219
|
+
let found = members.find((m) => {
|
|
220
|
+
const nick = m.nick || m.user?.name || m.name || "";
|
|
221
|
+
return clean(nick) === targetName;
|
|
222
|
+
});
|
|
223
|
+
if (!found) {
|
|
224
|
+
found = members.find((m) => {
|
|
225
|
+
const nick = m.nick || m.user?.name || m.name || "";
|
|
226
|
+
return clean(nick).includes(targetName);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (found) return found.user?.id;
|
|
230
|
+
} catch (e) {
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
__name(findTargetUser, "findTargetUser");
|
|
177
235
|
async function validateTargetUser(session, target) {
|
|
178
|
-
let targetUserId = session.userId;
|
|
179
236
|
if (target) {
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
targetUserId = match[1];
|
|
237
|
+
const foundId = await findTargetUser(session, target);
|
|
238
|
+
if (foundId) {
|
|
183
239
|
try {
|
|
184
|
-
const
|
|
185
|
-
if (!
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
} catch (error) {
|
|
240
|
+
const member = await session.bot.getGuildMember(session.guildId, foundId);
|
|
241
|
+
if (!member) return "无法找到指定用户,请检查输入是否正确。";
|
|
242
|
+
} catch {
|
|
189
243
|
return "无法找到指定用户,请检查输入是否正确。";
|
|
190
244
|
}
|
|
245
|
+
return foundId;
|
|
191
246
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (!memberInfo) {
|
|
197
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
198
|
-
}
|
|
199
|
-
} catch (error) {
|
|
200
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
201
|
-
}
|
|
247
|
+
return "无法找到指定用户,请检查输入是否正确。";
|
|
248
|
+
}
|
|
249
|
+
if (session.quote?.user?.id) {
|
|
250
|
+
return session.quote.user.id;
|
|
202
251
|
}
|
|
203
|
-
return
|
|
252
|
+
return session.userId;
|
|
204
253
|
}
|
|
205
254
|
__name(validateTargetUser, "validateTargetUser");
|
|
206
255
|
async function updateCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic) {
|
|
207
256
|
const [record] = await ctx.database.get("ccb_record", { groupId, userId: targetUserId });
|
|
208
257
|
if (!record) {
|
|
209
|
-
return await createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, pic);
|
|
258
|
+
return await createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic);
|
|
210
259
|
}
|
|
211
260
|
const senderId = session.userId;
|
|
212
261
|
const newNum = (record.num || 0) + 1;
|
|
@@ -234,12 +283,6 @@ function apply(ctx, config) {
|
|
|
234
283
|
if (ccb_by[k]) ccb_by[k].max = false;
|
|
235
284
|
}
|
|
236
285
|
if (ccb_by[senderId]) ccb_by[senderId].max = true;
|
|
237
|
-
} else {
|
|
238
|
-
for (const k in ccb_by) {
|
|
239
|
-
if (ccb_by[k] && !ccb_by[k].max) {
|
|
240
|
-
ccb_by[k].max = false;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
286
|
}
|
|
244
287
|
await ctx.database.set("ccb_record", { groupId, userId: targetUserId }, {
|
|
245
288
|
num: newNum,
|
|
@@ -256,7 +299,7 @@ function apply(ctx, config) {
|
|
|
256
299
|
return message;
|
|
257
300
|
}
|
|
258
301
|
__name(updateCCBRecord, "updateCCBRecord");
|
|
259
|
-
async function createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, pic) {
|
|
302
|
+
async function createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic) {
|
|
260
303
|
const newRecord = {
|
|
261
304
|
groupId,
|
|
262
305
|
userId: targetUserId,
|
|
@@ -266,7 +309,7 @@ function apply(ctx, config) {
|
|
|
266
309
|
ccb_by: { [session.userId]: { count: 1, first: true, max: true } }
|
|
267
310
|
};
|
|
268
311
|
await ctx.database.upsert("ccb_record", [newRecord]);
|
|
269
|
-
const resultMessage = `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
|
|
312
|
+
const resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
|
|
270
313
|
const message = [
|
|
271
314
|
resultMessage,
|
|
272
315
|
import_koishi.segment.image(pic),
|
|
@@ -279,6 +322,17 @@ function apply(ctx, config) {
|
|
|
279
322
|
const checkResult = checkGroupCommand(session);
|
|
280
323
|
if (checkResult) return checkResult;
|
|
281
324
|
const senderId = session.userId;
|
|
325
|
+
const checkCooldown = /* @__PURE__ */ __name((lastToggle) => {
|
|
326
|
+
const now2 = Date.now();
|
|
327
|
+
const cooldownMs = config.toggleCooldown * 1e3;
|
|
328
|
+
if (now2 - lastToggle < cooldownMs) {
|
|
329
|
+
const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
|
|
330
|
+
const m = Math.floor(remain / 60);
|
|
331
|
+
const s = remain % 60;
|
|
332
|
+
return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}, "checkCooldown");
|
|
282
336
|
const hasOff = "off" in options;
|
|
283
337
|
const hasOn = "on" in options;
|
|
284
338
|
if (hasOff || hasOn) {
|
|
@@ -286,13 +340,7 @@ function apply(ctx, config) {
|
|
|
286
340
|
const optionVal = isOff ? options.off : options.on;
|
|
287
341
|
let targetUserStr = null;
|
|
288
342
|
if (typeof optionVal === "string" && optionVal.trim()) {
|
|
289
|
-
|
|
290
|
-
const atIdMatch = val.match(/<at\s[^>]*id="([^"]+)"/);
|
|
291
|
-
if (atIdMatch) {
|
|
292
|
-
targetUserStr = atIdMatch[1];
|
|
293
|
-
} else {
|
|
294
|
-
targetUserStr = val;
|
|
295
|
-
}
|
|
343
|
+
targetUserStr = await findTargetUser(session, optionVal.trim());
|
|
296
344
|
}
|
|
297
345
|
if (!targetUserStr) {
|
|
298
346
|
const atEl = session.elements?.find((el) => el.type === "at");
|
|
@@ -301,16 +349,14 @@ function apply(ctx, config) {
|
|
|
301
349
|
}
|
|
302
350
|
}
|
|
303
351
|
if (!targetUserStr) {
|
|
352
|
+
if (typeof optionVal === "string" && optionVal.trim()) {
|
|
353
|
+
return `无法找到用户「${optionVal}」,请检查输入是否正确。`;
|
|
354
|
+
}
|
|
304
355
|
const now2 = Date.now();
|
|
305
356
|
const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
|
|
306
357
|
const lastToggle = userSetting?.lastToggleTime || 0;
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
309
|
-
const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
|
|
310
|
-
const m = Math.floor(remain / 60);
|
|
311
|
-
const s = remain % 60;
|
|
312
|
-
return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
|
|
313
|
-
}
|
|
358
|
+
const cooldownResult = checkCooldown(lastToggle);
|
|
359
|
+
if (cooldownResult) return cooldownResult;
|
|
314
360
|
const newOptOut = !!isOff;
|
|
315
361
|
await ctx.database.upsert("ccb_setting", [{
|
|
316
362
|
userId: senderId,
|
|
@@ -320,30 +366,27 @@ function apply(ctx, config) {
|
|
|
320
366
|
}]);
|
|
321
367
|
return newOptOut ? "已开启全局保护模式,阻止你被ccb。" : "已关闭全局保护模式,允许你被ccb。";
|
|
322
368
|
} else {
|
|
323
|
-
let targetId = targetUserStr;
|
|
324
|
-
const match = targetUserStr.match(/^[^:]+:(.+)$/);
|
|
325
|
-
if (match) targetId = match[1];
|
|
326
369
|
try {
|
|
327
|
-
const memberInfo = await session.bot.getGuildMember(session.guildId,
|
|
370
|
+
const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserStr);
|
|
328
371
|
if (!memberInfo) {
|
|
329
372
|
return "无法找到指定用户,请检查输入是否正确。";
|
|
330
373
|
}
|
|
331
374
|
} catch (error) {
|
|
332
375
|
return "无法找到指定用户,请检查输入是否正确。";
|
|
333
376
|
}
|
|
377
|
+
const targetId = targetUserStr;
|
|
378
|
+
const now2 = Date.now();
|
|
334
379
|
const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
|
|
380
|
+
const lastToggle = userSetting?.lastToggleTime || 0;
|
|
381
|
+
const cooldownResult = checkCooldown(lastToggle);
|
|
382
|
+
if (cooldownResult) return cooldownResult;
|
|
335
383
|
const overrides = userSetting?.overrides || {};
|
|
336
|
-
|
|
337
|
-
overrides[targetId] = false;
|
|
338
|
-
} else {
|
|
339
|
-
overrides[targetId] = true;
|
|
340
|
-
}
|
|
384
|
+
overrides[targetId] = !isOff;
|
|
341
385
|
await ctx.database.upsert("ccb_setting", [{
|
|
342
386
|
userId: senderId,
|
|
343
387
|
overrides,
|
|
344
|
-
// 保持其他字段默认值或原值
|
|
345
388
|
optOut: userSetting?.optOut ?? false,
|
|
346
|
-
lastToggleTime:
|
|
389
|
+
lastToggleTime: now2
|
|
347
390
|
}]);
|
|
348
391
|
const targetNick = await getUserNickname(session, targetId).catch(() => targetId) || targetId;
|
|
349
392
|
return isOff ? `已禁止用户 ${targetNick} 对你ccb。` : `已允许用户 ${targetNick} 对你ccb。`;
|
|
@@ -389,6 +432,10 @@ function apply(ctx, config) {
|
|
|
389
432
|
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
390
433
|
return `${nickname} 拒绝了和你ccb。`;
|
|
391
434
|
}
|
|
435
|
+
if (senderSetting?.overrides?.[targetUserId] === false) {
|
|
436
|
+
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
437
|
+
return `你已禁止与 ${nickname} 进行ccb。`;
|
|
438
|
+
}
|
|
392
439
|
const [targetSetting] = await ctx.database.get("ccb_setting", { userId: targetUserId });
|
|
393
440
|
if (targetSetting) {
|
|
394
441
|
const overrides = targetSetting.overrides || {};
|
|
@@ -402,7 +449,7 @@ function apply(ctx, config) {
|
|
|
402
449
|
}
|
|
403
450
|
}
|
|
404
451
|
if (targetUserId === actorId && !config.selfCcb) {
|
|
405
|
-
return "
|
|
452
|
+
return "怎么还能对自己下手啊(恼)";
|
|
406
453
|
}
|
|
407
454
|
const duration = parseFloat((Math.random() * 59 + 1).toFixed(2));
|
|
408
455
|
let V = parseFloat((Math.random() * 99 + 1).toFixed(2));
|
|
@@ -413,18 +460,10 @@ function apply(ctx, config) {
|
|
|
413
460
|
crit = true;
|
|
414
461
|
}
|
|
415
462
|
const pic = getAvatar(targetUserId);
|
|
416
|
-
const exists = await ctx.database.get("ccb_record", {
|
|
417
|
-
groupId: session.guildId,
|
|
418
|
-
userId: targetUserId
|
|
419
|
-
});
|
|
420
463
|
let message;
|
|
421
464
|
try {
|
|
422
465
|
const nickname = await getUserNickname(session, targetUserId);
|
|
423
|
-
|
|
424
|
-
message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
|
|
425
|
-
} else {
|
|
426
|
-
message = await createNewCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, pic);
|
|
427
|
-
}
|
|
466
|
+
message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
|
|
428
467
|
} catch (e) {
|
|
429
468
|
console.error(`报错: ${e}`);
|
|
430
469
|
return "对方拒绝了和你ccb";
|
|
@@ -432,26 +471,37 @@ function apply(ctx, config) {
|
|
|
432
471
|
if (Math.random() < currentConfig.ywProbability) {
|
|
433
472
|
banList[actorId] = now + currentConfig.ywBanDuration;
|
|
434
473
|
await session.send(message);
|
|
435
|
-
return "
|
|
474
|
+
return "💥你炸膛了!不能ccb了(悲)";
|
|
436
475
|
}
|
|
437
476
|
return message;
|
|
438
477
|
});
|
|
478
|
+
async function buildRanking(session, title, data, formatLine) {
|
|
479
|
+
const nicknameMap = /* @__PURE__ */ new Map();
|
|
480
|
+
await Promise.all(data.map(async (r) => {
|
|
481
|
+
nicknameMap.set(r.userId, await getUserNickname(session, r.userId));
|
|
482
|
+
}));
|
|
483
|
+
let msg = `${title}
|
|
484
|
+
`;
|
|
485
|
+
for (let i = 0; i < data.length; i++) {
|
|
486
|
+
const nick = nicknameMap.get(data[i].userId) || data[i].userId;
|
|
487
|
+
msg += formatLine(data[i], nick, i);
|
|
488
|
+
}
|
|
489
|
+
return msg.trim();
|
|
490
|
+
}
|
|
491
|
+
__name(buildRanking, "buildRanking");
|
|
439
492
|
ctx.command("ccbtop", "按次数排行").action(async ({ session }) => {
|
|
440
493
|
const checkResult = checkGroupCommand(session);
|
|
441
494
|
if (checkResult) return checkResult;
|
|
442
495
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
443
496
|
if (!groupData.length) return "当前群暂无ccb记录。";
|
|
444
497
|
const top5 = groupData.sort((a, b) => b.num - a.num).slice(0, 5);
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
`;
|
|
453
|
-
}
|
|
454
|
-
return msg.trim();
|
|
498
|
+
return buildRanking(
|
|
499
|
+
session,
|
|
500
|
+
"被ccb排行榜 TOP5:",
|
|
501
|
+
top5,
|
|
502
|
+
(r, nick, i) => `${i + 1}. ${nick} - 次数:${r.num}
|
|
503
|
+
`
|
|
504
|
+
);
|
|
455
505
|
});
|
|
456
506
|
ctx.command("ccbvol", "按注入量排行").action(async ({ session }) => {
|
|
457
507
|
const checkResult = checkGroupCommand(session);
|
|
@@ -459,16 +509,13 @@ function apply(ctx, config) {
|
|
|
459
509
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
460
510
|
if (!groupData.length) return "当前群暂无ccb记录。";
|
|
461
511
|
const top5 = groupData.sort((a, b) => b.vol - a.vol).slice(0, 5);
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
`;
|
|
470
|
-
}
|
|
471
|
-
return msg.trim();
|
|
512
|
+
return buildRanking(
|
|
513
|
+
session,
|
|
514
|
+
"被注入量排行榜 TOP5:",
|
|
515
|
+
top5,
|
|
516
|
+
(r, nick, i) => `${i + 1}. ${nick} - 累计注入:${r.vol.toFixed(2)}ml
|
|
517
|
+
`
|
|
518
|
+
);
|
|
472
519
|
});
|
|
473
520
|
ctx.command("ccbmax", "按max值排行并输出产生者").action(async ({ session }) => {
|
|
474
521
|
const checkResult = checkGroupCommand(session);
|
|
@@ -527,16 +574,15 @@ function apply(ctx, config) {
|
|
|
527
574
|
ctx.command("ccbinfo [target:user]", "查询某人ccb信息").action(async ({ session }, target) => {
|
|
528
575
|
const checkResult = checkGroupCommand(session);
|
|
529
576
|
if (checkResult) return checkResult;
|
|
530
|
-
let targetUserId = session
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
if (match) targetUserId = match[1];
|
|
577
|
+
let targetUserId = await validateTargetUser(session, target);
|
|
578
|
+
if (targetUserId.startsWith("无法找到")) {
|
|
579
|
+
return targetUserId;
|
|
534
580
|
}
|
|
535
581
|
const [record] = await ctx.database.get("ccb_record", { groupId: session.guildId, userId: targetUserId });
|
|
536
582
|
if (!record) return "该用户暂无ccb记录。";
|
|
537
583
|
const total_num = record.num;
|
|
538
584
|
const total_vol = record.vol;
|
|
539
|
-
|
|
585
|
+
const max_val = record.max || (total_num > 0 ? total_vol / total_num : 0);
|
|
540
586
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
541
587
|
let cb_total = 0;
|
|
542
588
|
for (const r of groupData) {
|
|
@@ -564,15 +610,15 @@ function apply(ctx, config) {
|
|
|
564
610
|
const first_nick = first_actor ? await getUserNickname(session, first_actor) : "未知";
|
|
565
611
|
const msg = [
|
|
566
612
|
`【${target_nick} 】`,
|
|
567
|
-
`•
|
|
568
|
-
`•
|
|
569
|
-
`•
|
|
570
|
-
`•
|
|
571
|
-
`•
|
|
613
|
+
`• 开拓者:${first_nick}`,
|
|
614
|
+
`• 被注入次数:${total_num}`,
|
|
615
|
+
`• 主动出击:${cb_total}`,
|
|
616
|
+
`• 累计容量:${total_vol.toFixed(2)}ml`,
|
|
617
|
+
`• 单次最高:${max_val.toFixed(2)}ml`
|
|
572
618
|
].join("\n");
|
|
573
619
|
return msg;
|
|
574
620
|
});
|
|
575
|
-
ctx.command("
|
|
621
|
+
ctx.command("ccbcharm", "魅力榜 - 计算群中最受欢迎的群友").action(async ({ session }) => {
|
|
576
622
|
const checkResult = checkGroupCommand(session);
|
|
577
623
|
if (checkResult) return checkResult;
|
|
578
624
|
const w_num = 1;
|
|
@@ -596,11 +642,11 @@ function apply(ctx, config) {
|
|
|
596
642
|
await Promise.all(ranking.map(async (r) => {
|
|
597
643
|
nicknameMap.set(r.userId, await getUserNickname(session, r.userId));
|
|
598
644
|
}));
|
|
599
|
-
let msg = "💎
|
|
645
|
+
let msg = "💎 魅力榜 TOP5 💎\n";
|
|
600
646
|
for (let i = 0; i < ranking.length; i++) {
|
|
601
647
|
const { userId, val } = ranking[i];
|
|
602
648
|
const nick = nicknameMap.get(userId) || userId;
|
|
603
|
-
msg += `${i + 1}. ${nick} -
|
|
649
|
+
msg += `${i + 1}. ${nick} - 魅力值:${val.toFixed(2)}
|
|
604
650
|
`;
|
|
605
651
|
}
|
|
606
652
|
return msg.trim();
|