koishi-plugin-ccb-plus 0.1.6 → 0.1.7-beta

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.
Files changed (2) hide show
  1. package/lib/index.js +222 -172
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -72,7 +72,7 @@ function apply(ctx, config) {
72
72
  if (error.code === "ENOENT") {
73
73
  return {};
74
74
  }
75
- console.error("Error reading CCB data:", error);
75
+ console.error("读取CCB数据出错:", error);
76
76
  return {};
77
77
  }
78
78
  }
@@ -83,7 +83,7 @@ function apply(ctx, config) {
83
83
  await import_fs.promises.mkdir(dataDir, { recursive: true });
84
84
  await import_fs.promises.writeFile(DATA_FILE, JSON.stringify(data, null, 2), "utf-8");
85
85
  } catch (error) {
86
- console.error("Error writing CCB data:", error);
86
+ console.error("写入CCB数据出错:", error);
87
87
  }
88
88
  }
89
89
  __name(writeData, "writeData");
@@ -98,7 +98,7 @@ function apply(ctx, config) {
98
98
  }
99
99
  } catch (error) {
100
100
  if (error.code !== "ENOENT") {
101
- console.error("Error reading CCB log:", error);
101
+ console.error("读取CCB日志出错:", error);
102
102
  }
103
103
  }
104
104
  const entry = {
@@ -124,52 +124,59 @@ function apply(ctx, config) {
124
124
  if (cached && now - cached.timestamp < CACHE_DURATION) {
125
125
  return cached.name;
126
126
  }
127
- try {
128
- const memberInfo = await session.bot.getGuildMember(session.guildId, userId);
129
- const nickname = memberInfo?.nick || memberInfo?.name || `用户${userId}`;
130
- nicknameCache.set(cacheKey, { name: nickname, timestamp: now });
131
- return nickname;
132
- } catch (error) {
127
+ const setAndReturnName = /* @__PURE__ */ __name((name2) => {
128
+ if (name2 && name2 !== userId) {
129
+ const actualName = name2.trim();
130
+ if (actualName) {
131
+ nicknameCache.set(cacheKey, { name: actualName, timestamp: now });
132
+ return actualName;
133
+ }
134
+ }
135
+ return null;
136
+ }, "setAndReturnName");
137
+ if (session.guildId && userId) {
133
138
  try {
134
- const userInfo = await session.bot.getUser(userId);
135
- const nickname = userInfo?.name || `用户${userId}`;
136
- nicknameCache.set(cacheKey, { name: nickname, timestamp: now });
137
- return nickname;
138
- } catch (e) {
139
- const friendlyName = `用户${userId}`;
140
- nicknameCache.set(cacheKey, { name: friendlyName, timestamp: now });
141
- return friendlyName;
139
+ const memberInfo = await session.bot.getGuildMember(session.guildId, userId);
140
+ const displayName = memberInfo?.nick || memberInfo?.user?.name || memberInfo?.name;
141
+ const result = setAndReturnName(displayName);
142
+ if (result) return result;
143
+ } catch (error) {
142
144
  }
143
145
  }
146
+ try {
147
+ const userInfo = await session.bot.getUser(userId);
148
+ const displayName = userInfo?.name || userInfo?.nick || userInfo?.nickname;
149
+ const result = setAndReturnName(displayName);
150
+ if (result) return result;
151
+ } catch (e) {
152
+ }
153
+ try {
154
+ if (session.event?.user?.id === userId) {
155
+ const result = setAndReturnName(session.event?.user?.name);
156
+ if (result) return result;
157
+ }
158
+ } catch (nestedError) {
159
+ }
160
+ try {
161
+ const userData = await session.getUser(userId);
162
+ const result = setAndReturnName(userData?.name);
163
+ if (result) return result;
164
+ } catch (dbError) {
165
+ }
166
+ const friendlyName = `用户${userId}`;
167
+ nicknameCache.set(cacheKey, { name: friendlyName, timestamp: now });
168
+ return friendlyName;
144
169
  }
145
170
  __name(getUserNickname, "getUserNickname");
146
- ctx.command("ccb [target:user]", "和群友赛博sex的插件PLUS", { authority: 1 }).action(async ({ session }, target) => {
147
- const groupId = session.guildId;
148
- if (!groupId) {
171
+ function checkGroupCommand(session) {
172
+ if (!session.guildId) {
149
173
  return "此命令只能在群聊中使用。";
150
174
  }
151
- const senderId = session.userId;
152
- const actorId = senderId;
153
- const now = Date.now() / 1e3;
154
- const banEnd = banList[actorId] || 0;
155
- if (now < banEnd) {
156
- const remain = Math.floor(banEnd - now);
157
- const m = Math.floor(remain / 60);
158
- const s = remain % 60;
159
- return `嘻嘻,你已经一滴不剩了,养胃还剩 ${m}分${s}秒`;
160
- }
161
- const times = actionTimes[actorId] = actionTimes[actorId] || [];
162
- const cutoff = now - config.ywWindow;
163
- while (times.length > 0 && times[0] < cutoff) {
164
- times.shift();
165
- }
166
- times.push(now);
167
- if (times.length > config.ywThreshold) {
168
- banList[actorId] = now + config.ywBanDuration;
169
- actionTimes[actorId] = [];
170
- return "冲得出来吗你就冲,再冲就给你折了";
171
- }
172
- let targetUserId = senderId;
175
+ return null;
176
+ }
177
+ __name(checkGroupCommand, "checkGroupCommand");
178
+ async function validateTargetUser(session, target) {
179
+ let targetUserId = session.userId;
173
180
  if (target) {
174
181
  const match = target.match(/^[^:]+:(.+)$/);
175
182
  if (match) {
@@ -194,6 +201,147 @@ function apply(ctx, config) {
194
201
  return "无法找到指定用户,请检查输入是否正确。";
195
202
  }
196
203
  }
204
+ return targetUserId;
205
+ }
206
+ __name(validateTargetUser, "validateTargetUser");
207
+ async function updateCCBRecord(session, groupId, targetUserId, duration, V, nickname, crit, pic) {
208
+ const allData = await readData();
209
+ const groupData = allData[groupId] || [];
210
+ const recordIndex = groupData.findIndex((item) => item.id === targetUserId);
211
+ if (recordIndex !== -1) {
212
+ const item = groupData[recordIndex];
213
+ const senderId = session.userId;
214
+ item.num = (item.num || 0) + 1;
215
+ item.vol = parseFloat((item.vol + V).toFixed(2));
216
+ let ccb_by = item.ccb_by || {};
217
+ if (senderId in ccb_by) {
218
+ const current = ccb_by[senderId];
219
+ ccb_by[senderId] = {
220
+ count: (current?.count || 0) + 1,
221
+ first: current?.first || false,
222
+ max: current?.max || false
223
+ };
224
+ } else {
225
+ ccb_by[senderId] = { count: 1, first: false, max: false };
226
+ }
227
+ item.ccb_by = ccb_by;
228
+ let prev_max = item.max || 0;
229
+ if (prev_max === 0) {
230
+ const total_vol = item.vol || 0;
231
+ const total_num = item.num || 0;
232
+ if (total_num > 0) {
233
+ prev_max = parseFloat((total_vol / total_num).toFixed(2));
234
+ }
235
+ }
236
+ if (V > prev_max) {
237
+ item.max = V;
238
+ for (const k in ccb_by) {
239
+ const current = ccb_by[k];
240
+ ccb_by[k] = {
241
+ count: current?.count || 0,
242
+ first: current?.first || false,
243
+ max: false
244
+ };
245
+ }
246
+ const senderData = ccb_by[senderId];
247
+ ccb_by[senderId] = {
248
+ count: senderData?.count || 0,
249
+ first: senderData?.first || false,
250
+ max: true
251
+ };
252
+ } else {
253
+ for (const k in ccb_by) {
254
+ const current = ccb_by[k];
255
+ if (!(current && "max" in current)) {
256
+ ccb_by[k] = {
257
+ count: current?.count || 0,
258
+ first: current?.first || false,
259
+ max: false
260
+ };
261
+ }
262
+ }
263
+ }
264
+ item.ccb_by = ccb_by;
265
+ let resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
266
+ const message = [
267
+ resultMessage,
268
+ import_koishi.segment.image(pic),
269
+ `这是ta的第${item.num}次。`
270
+ ].join("\n");
271
+ allData[groupId] = groupData;
272
+ await writeData(allData);
273
+ if (config.isLog) {
274
+ try {
275
+ await appendLog(groupId, session.userId, targetUserId, duration, V);
276
+ } catch (e) {
277
+ console.warn("记录日志失败:", e);
278
+ }
279
+ }
280
+ return message;
281
+ } else {
282
+ return "对方拒绝了和你ccb";
283
+ }
284
+ }
285
+ __name(updateCCBRecord, "updateCCBRecord");
286
+ async function createNewCCBRecord(session, groupId, targetUserId, duration, V, nickname, pic) {
287
+ const allData = await readData();
288
+ const groupData = allData[groupId] || [];
289
+ const newRecord = {
290
+ id: targetUserId,
291
+ num: 1,
292
+ vol: V,
293
+ ccb_by: { [session.userId]: { count: 1, first: true, max: true } },
294
+ max: V
295
+ };
296
+ groupData.push(newRecord);
297
+ allData[groupId] = groupData;
298
+ await writeData(allData);
299
+ const resultMessage = `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
300
+ const message = [
301
+ resultMessage,
302
+ import_koishi.segment.image(pic),
303
+ "这是ta的初体验。"
304
+ ].join("\n");
305
+ if (config.isLog) {
306
+ try {
307
+ await appendLog(groupId, session.userId, targetUserId, duration, V);
308
+ } catch (e) {
309
+ console.warn("记录日志失败:", e);
310
+ }
311
+ }
312
+ return message;
313
+ }
314
+ __name(createNewCCBRecord, "createNewCCBRecord");
315
+ ctx.command("ccb [target:user]", "和群友赛博sex的插件PLUS", { authority: 1 }).action(async ({ session }, target) => {
316
+ const checkResult = checkGroupCommand(session);
317
+ if (checkResult) {
318
+ return checkResult;
319
+ }
320
+ const senderId = session.userId;
321
+ const actorId = senderId;
322
+ const now = Date.now() / 1e3;
323
+ const banEnd = banList[actorId] || 0;
324
+ if (now < banEnd) {
325
+ const remain = Math.floor(banEnd - now);
326
+ const m = Math.floor(remain / 60);
327
+ const s = remain % 60;
328
+ return `嘻嘻,你已经一滴不剩了,养胃还剩 ${m}分${s}秒`;
329
+ }
330
+ const times = actionTimes[actorId] = actionTimes[actorId] || [];
331
+ const cutoff = now - config.ywWindow;
332
+ while (times.length > 0 && times[0] < cutoff) {
333
+ times.shift();
334
+ }
335
+ times.push(now);
336
+ if (times.length > config.ywThreshold) {
337
+ banList[actorId] = now + config.ywBanDuration;
338
+ actionTimes[actorId] = [];
339
+ return "冲得出来吗你就冲,再冲就给你折了";
340
+ }
341
+ let targetUserId = await validateTargetUser(session, target);
342
+ if (targetUserId.startsWith("无法找到")) {
343
+ return targetUserId;
344
+ }
197
345
  if (config.whiteList.includes(targetUserId)) {
198
346
  const nickname = await getUserNickname(session, targetUserId) || targetUserId;
199
347
  return `${nickname} 的后门被后户之神霸占了,不能ccb(悲`;
@@ -211,89 +359,13 @@ function apply(ctx, config) {
211
359
  }
212
360
  const pic = getAvatar(targetUserId);
213
361
  const allData = await readData();
214
- const groupData = allData[groupId] || [];
362
+ const groupData = allData[session.guildId] || [];
215
363
  const mode = makeit(groupData, targetUserId);
364
+ let message;
216
365
  if (mode === 1) {
217
366
  try {
218
- const recordIndex = groupData.findIndex((item) => item.id === targetUserId);
219
- if (recordIndex !== -1) {
220
- const item = groupData[recordIndex];
221
- const nickname = await getUserNickname(session, targetUserId);
222
- item.num = (item.num || 0) + 1;
223
- item.vol = parseFloat((item.vol + V).toFixed(2));
224
- let ccb_by = item.ccb_by || {};
225
- if (senderId in ccb_by) {
226
- const current = ccb_by[senderId];
227
- ccb_by[senderId] = {
228
- count: (current?.count || 0) + 1,
229
- first: current?.first || false,
230
- max: current?.max || false
231
- };
232
- } else {
233
- ccb_by[senderId] = { count: 1, first: false, max: false };
234
- }
235
- item.ccb_by = ccb_by;
236
- let prev_max = item.max || 0;
237
- if (prev_max === 0) {
238
- const total_vol = item.vol || 0;
239
- const total_num = item.num || 0;
240
- if (total_num > 0) {
241
- prev_max = parseFloat((total_vol / total_num).toFixed(2));
242
- }
243
- }
244
- if (V > prev_max) {
245
- item.max = V;
246
- for (const k in ccb_by) {
247
- const current = ccb_by[k];
248
- ccb_by[k] = {
249
- count: current?.count || 0,
250
- first: current?.first || false,
251
- max: false
252
- };
253
- }
254
- const senderData = ccb_by[senderId];
255
- ccb_by[senderId] = {
256
- count: senderData?.count || 0,
257
- first: senderData?.first || false,
258
- max: true
259
- };
260
- } else {
261
- for (const k in ccb_by) {
262
- const current = ccb_by[k];
263
- if (!(current && "max" in current)) {
264
- ccb_by[k] = {
265
- count: current?.count || 0,
266
- first: current?.first || false,
267
- max: false
268
- };
269
- }
270
- }
271
- }
272
- item.ccb_by = ccb_by;
273
- let resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
274
- const message = [
275
- resultMessage,
276
- import_koishi.segment.image(pic),
277
- `这是ta的第${item.num}次。`
278
- ].join("\n");
279
- allData[groupId] = groupData;
280
- await writeData(allData);
281
- if (config.isLog) {
282
- try {
283
- await appendLog(groupId, senderId, targetUserId, duration, V);
284
- } catch (e) {
285
- console.warn("记录日志失败:", e);
286
- }
287
- }
288
- if (Math.random() < config.ywProbability) {
289
- banList[actorId] = now + config.ywBanDuration;
290
- await session.send(message);
291
- return "💥你的牛牛炸膛了!满身疮痍,再起不能(悲)";
292
- }
293
- return message;
294
- } else {
295
- return "对方拒绝了和你ccb";
296
- }
367
+ const nickname = await getUserNickname(session, targetUserId);
368
+ message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
297
369
  } catch (e) {
298
370
  console.error(`报错: ${e}`);
299
371
  return "对方拒绝了和你ccb";
@@ -301,47 +373,25 @@ function apply(ctx, config) {
301
373
  } else {
302
374
  try {
303
375
  const nickname = await getUserNickname(session, targetUserId);
304
- const resultMessage = `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
305
- const message = [
306
- resultMessage,
307
- import_koishi.segment.image(getAvatar(targetUserId)),
308
- "这是ta的初体验。"
309
- ].join("\n");
310
- const newRecord = {
311
- id: targetUserId,
312
- num: 1,
313
- vol: V,
314
- ccb_by: { [senderId]: { count: 1, first: true, max: true } },
315
- max: V
316
- };
317
- groupData.push(newRecord);
318
- allData[groupId] = groupData;
319
- await writeData(allData);
320
- if (config.isLog) {
321
- try {
322
- await appendLog(groupId, senderId, targetUserId, duration, V);
323
- } catch (e) {
324
- console.warn("记录日志失败:", e);
325
- }
326
- }
327
- if (Math.random() < config.ywProbability) {
328
- banList[actorId] = now + config.ywBanDuration;
329
- await session.send(message);
330
- return "💥你的牛牛炸膛了!满身疮痍,再起不能(悲)";
331
- }
332
- return message;
376
+ message = await createNewCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, pic);
333
377
  } catch (e) {
334
378
  console.error(`报错: ${e}`);
335
379
  return "对方拒绝了和你ccb";
336
380
  }
337
381
  }
382
+ if (Math.random() < config.ywProbability) {
383
+ banList[actorId] = now + config.ywBanDuration;
384
+ await session.send(message);
385
+ return "💥你的牛牛炸膛了!满身疮痍,再起不能(悲)";
386
+ }
387
+ return message;
338
388
  });
339
389
  ctx.command("ccbtop", "按次数排行", { authority: 1 }).action(async ({ session }) => {
340
- const groupId = session.guildId;
341
- if (!groupId) {
342
- return "此命令只能在群聊中使用。";
390
+ const checkResult = checkGroupCommand(session);
391
+ if (checkResult) {
392
+ return checkResult;
343
393
  }
344
- const groupData = (await readData())[groupId] || [];
394
+ const groupData = (await readData())[session.guildId] || [];
345
395
  if (!groupData.length) {
346
396
  return "当前群暂无ccb记录。";
347
397
  }
@@ -358,11 +408,11 @@ function apply(ctx, config) {
358
408
  return msg.trim();
359
409
  });
360
410
  ctx.command("ccbvol", "按注入量排行", { authority: 1 }).action(async ({ session }) => {
361
- const groupId = session.guildId;
362
- if (!groupId) {
363
- return "此命令只能在群聊中使用。";
411
+ const checkResult = checkGroupCommand(session);
412
+ if (checkResult) {
413
+ return checkResult;
364
414
  }
365
- const groupData = (await readData())[groupId] || [];
415
+ const groupData = (await readData())[session.guildId] || [];
366
416
  if (!groupData.length) {
367
417
  return "当前群暂无ccb记录。";
368
418
  }
@@ -379,11 +429,11 @@ function apply(ctx, config) {
379
429
  return msg.trim();
380
430
  });
381
431
  ctx.command("ccbmax", "按max值排行并输出产生者", { authority: 1 }).action(async ({ session }) => {
382
- const groupId = session.guildId;
383
- if (!groupId) {
384
- return "此命令只能在群聊中使用。";
432
+ const checkResult = checkGroupCommand(session);
433
+ if (checkResult) {
434
+ return checkResult;
385
435
  }
386
- const groupData = (await readData())[groupId] || [];
436
+ const groupData = (await readData())[session.guildId] || [];
387
437
  if (!groupData.length) {
388
438
  return "当前群暂无ccb记录。";
389
439
  }
@@ -456,9 +506,9 @@ function apply(ctx, config) {
456
506
  return msg.trim();
457
507
  });
458
508
  ctx.command("ccbinfo [target:user]", "查询某人ccb信息:第一次对他ccb的人,被ccb的总次数,注入总量", { authority: 1 }).action(async ({ session }, target) => {
459
- const groupId = session.guildId;
460
- if (!groupId) {
461
- return "此命令只能在群聊中使用。";
509
+ const checkResult = checkGroupCommand(session);
510
+ if (checkResult) {
511
+ return checkResult;
462
512
  }
463
513
  let targetUserId = session.userId;
464
514
  if (target) {
@@ -476,7 +526,7 @@ function apply(ctx, config) {
476
526
  }
477
527
  }
478
528
  const allData = await readData();
479
- const groupData = allData[groupId] || [];
529
+ const groupData = allData[session.guildId] || [];
480
530
  const record = groupData.find((r) => r.id === targetUserId);
481
531
  if (!record) {
482
532
  return "该用户暂无ccb记录。";
@@ -545,15 +595,15 @@ function apply(ctx, config) {
545
595
  return msg;
546
596
  });
547
597
  ctx.command("xnn", "XNN榜 - 计算群中最xnn特质的群友", { authority: 1 }).action(async ({ session }) => {
598
+ const checkResult = checkGroupCommand(session);
599
+ if (checkResult) {
600
+ return checkResult;
601
+ }
548
602
  const w_num = 1;
549
603
  const w_vol = 0.1;
550
604
  const w_action = 0.5;
551
- const groupId = session.guildId;
552
- if (!groupId) {
553
- return "此命令只能在群聊中使用。";
554
- }
555
605
  const allData = await readData();
556
- const groupData = allData[groupId] || [];
606
+ const groupData = allData[session.guildId] || [];
557
607
  if (!groupData.length) {
558
608
  return "当前群暂无ccb记录。";
559
609
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ccb-plus",
3
3
  "description": "Koishi插件,与群友发生ccb行为。(移植自astrbot_plugin_ccb_plus)",
4
- "version": "0.1.6",
4
+ "version": "0.1.7-beta",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [