koishi-plugin-ccb-plus 0.2.7 → 0.2.8-beta.2
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/commands/ccb.d.ts +4 -0
- package/lib/commands/charm.d.ts +3 -0
- package/lib/commands/index.d.ts +4 -0
- package/lib/commands/info.d.ts +3 -0
- package/lib/commands/rank.d.ts +3 -0
- package/lib/config.d.ts +21 -0
- package/lib/core.d.ts +3 -0
- package/lib/index.d.ts +5 -47
- package/lib/index.js +323 -246
- package/lib/model.d.ts +28 -0
- package/lib/utils.d.ts +19 -0
- package/package.json +3 -3
- package/readme.md +1 -1
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Schema } from 'koishi';
|
|
2
|
+
export interface CheatConfig {
|
|
3
|
+
userId: string;
|
|
4
|
+
ywWindow: number;
|
|
5
|
+
ywThreshold: number;
|
|
6
|
+
ywProbability: number;
|
|
7
|
+
critProb: number;
|
|
8
|
+
ywBanDuration: number;
|
|
9
|
+
}
|
|
10
|
+
export interface CCBConfig {
|
|
11
|
+
ywWindow: number;
|
|
12
|
+
ywThreshold: number;
|
|
13
|
+
ywBanDuration: number;
|
|
14
|
+
ywProbability: number;
|
|
15
|
+
whiteList: string[];
|
|
16
|
+
selfCcb: boolean;
|
|
17
|
+
critProb: number;
|
|
18
|
+
toggleCooldown: number;
|
|
19
|
+
cheatList: CheatConfig[];
|
|
20
|
+
}
|
|
21
|
+
export declare const Config: Schema<CCBConfig>;
|
package/lib/core.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Context, Session } from 'koishi';
|
|
2
|
+
export declare function createNewCCBRecord(ctx: Context, session: Session, groupId: string, targetUserId: string, duration: number, V: number, nickname: string, crit: boolean, pic: string): Promise<string>;
|
|
3
|
+
export declare function updateCCBRecord(ctx: Context, session: Session, groupId: string, targetUserId: string, duration: number, V: number, nickname: string, crit: boolean, pic: string): Promise<string>;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,50 +1,8 @@
|
|
|
1
|
-
import { Context
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config, CCBConfig } from './config';
|
|
2
3
|
export declare const name = "ccb-plus";
|
|
3
4
|
export declare const inject: string[];
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
ywThreshold: number;
|
|
8
|
-
ywProbability: number;
|
|
9
|
-
critProb: number;
|
|
10
|
-
ywBanDuration: number;
|
|
11
|
-
}
|
|
12
|
-
export interface CCBConfig {
|
|
13
|
-
ywWindow: number;
|
|
14
|
-
ywThreshold: number;
|
|
15
|
-
ywBanDuration: number;
|
|
16
|
-
ywProbability: number;
|
|
17
|
-
whiteList: string[];
|
|
18
|
-
selfCcb: boolean;
|
|
19
|
-
critProb: number;
|
|
20
|
-
toggleCooldown: number;
|
|
21
|
-
cheatList: CheatConfig[];
|
|
22
|
-
}
|
|
23
|
-
export interface CCBRecord {
|
|
24
|
-
groupId: string;
|
|
25
|
-
userId: string;
|
|
26
|
-
num: number;
|
|
27
|
-
vol: number;
|
|
28
|
-
max: number;
|
|
29
|
-
ccb_by: {
|
|
30
|
-
[actorId: string]: {
|
|
31
|
-
count: number;
|
|
32
|
-
first: boolean;
|
|
33
|
-
max: boolean;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export interface CCBUserSetting {
|
|
38
|
-
userId: string;
|
|
39
|
-
optOut: boolean;
|
|
40
|
-
lastToggleTime: number;
|
|
41
|
-
overrides: Record<string, boolean>;
|
|
42
|
-
}
|
|
43
|
-
declare module 'koishi' {
|
|
44
|
-
interface Tables {
|
|
45
|
-
ccb_record: CCBRecord;
|
|
46
|
-
ccb_setting: CCBUserSetting;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
export declare const Config: Schema<CCBConfig>;
|
|
5
|
+
export { Config };
|
|
6
|
+
export * from './config';
|
|
7
|
+
export * from './model';
|
|
50
8
|
export declare function apply(ctx: Context, config: CCBConfig): void;
|
package/lib/index.js
CHANGED
|
@@ -32,20 +32,19 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
34
|
apply: () => apply,
|
|
35
|
+
applyDatabase: () => applyDatabase,
|
|
35
36
|
inject: () => inject,
|
|
36
37
|
name: () => name
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(src_exports);
|
|
40
|
+
|
|
41
|
+
// src/config.ts
|
|
39
42
|
var import_koishi = require("koishi");
|
|
40
|
-
var import_fs = require("fs");
|
|
41
|
-
var path = __toESM(require("path"));
|
|
42
|
-
var name = "ccb-plus";
|
|
43
|
-
var inject = ["database"];
|
|
44
43
|
var Config = import_koishi.Schema.object({
|
|
45
|
-
ywWindow: import_koishi.Schema.number().default(60).description("
|
|
44
|
+
ywWindow: import_koishi.Schema.number().default(60).description("全局触发冷却的窗口时间(秒)"),
|
|
46
45
|
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("
|
|
46
|
+
ywBanDuration: import_koishi.Schema.number().default(900).description("全局冷却时长(秒)"),
|
|
47
|
+
ywProbability: import_koishi.Schema.number().default(0.1).min(0).max(1).description("全局随机冷却概率"),
|
|
49
48
|
whiteList: import_koishi.Schema.array(String).default([]).description("全局配置的黑名单"),
|
|
50
49
|
selfCcb: import_koishi.Schema.boolean().default(false).description("是否允许对自己ccb"),
|
|
51
50
|
critProb: import_koishi.Schema.number().default(0.2).min(0).max(1).description("全局暴击概率"),
|
|
@@ -54,12 +53,16 @@ var Config = import_koishi.Schema.object({
|
|
|
54
53
|
userId: import_koishi.Schema.string().required().description("用户ID"),
|
|
55
54
|
ywWindow: import_koishi.Schema.number().default(10).description("特权窗口时间(秒)"),
|
|
56
55
|
ywThreshold: import_koishi.Schema.number().default(999).description("特权窗口内最大次数"),
|
|
57
|
-
ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("
|
|
56
|
+
ywProbability: import_koishi.Schema.number().default(0).min(0).max(1).description("特权冷却概率"),
|
|
58
57
|
critProb: import_koishi.Schema.number().default(0.8).min(0).max(1).description("特权暴击概率"),
|
|
59
|
-
ywBanDuration: import_koishi.Schema.number().default(60).description("
|
|
58
|
+
ywBanDuration: import_koishi.Schema.number().default(60).description("特权冷却时长(秒)")
|
|
60
59
|
})).role("table").description("开挂名单(优先级高于全局设置)")
|
|
61
60
|
});
|
|
62
|
-
|
|
61
|
+
|
|
62
|
+
// src/model.ts
|
|
63
|
+
var import_fs = require("fs");
|
|
64
|
+
var path = __toESM(require("path"));
|
|
65
|
+
function applyDatabase(ctx) {
|
|
63
66
|
ctx.model.extend("ccb_record", {
|
|
64
67
|
groupId: "string",
|
|
65
68
|
userId: "string",
|
|
@@ -79,10 +82,6 @@ function apply(ctx, config) {
|
|
|
79
82
|
}, {
|
|
80
83
|
primary: "userId"
|
|
81
84
|
});
|
|
82
|
-
const actionTimes = {};
|
|
83
|
-
const banList = {};
|
|
84
|
-
const nicknameCache = /* @__PURE__ */ new Map();
|
|
85
|
-
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
86
85
|
ctx.on("ready", async () => {
|
|
87
86
|
const DATA_FILE = path.join(ctx.baseDir, "data", "ccb.json");
|
|
88
87
|
try {
|
|
@@ -118,22 +117,66 @@ function apply(ctx, config) {
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
});
|
|
121
|
-
|
|
120
|
+
}
|
|
121
|
+
__name(applyDatabase, "applyDatabase");
|
|
122
|
+
|
|
123
|
+
// src/utils.ts
|
|
124
|
+
var import_koishi2 = require("koishi");
|
|
125
|
+
var CcbState = class _CcbState {
|
|
126
|
+
static {
|
|
127
|
+
__name(this, "CcbState");
|
|
128
|
+
}
|
|
129
|
+
actionTimes = {};
|
|
130
|
+
banList = {};
|
|
131
|
+
nicknameCache = /* @__PURE__ */ new Map();
|
|
132
|
+
static MAX_CACHE_SIZE = 2e3;
|
|
133
|
+
static CACHE_DURATION = 5 * 60 * 1e3;
|
|
134
|
+
cleanupTimer;
|
|
135
|
+
constructor(ctx) {
|
|
136
|
+
const CLEANUP_INTERVAL = 10 * 60 * 1e3;
|
|
137
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL);
|
|
138
|
+
ctx.on("dispose", () => {
|
|
139
|
+
clearInterval(this.cleanupTimer);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
cleanup() {
|
|
143
|
+
const nowMs = Date.now();
|
|
144
|
+
const nowSec = nowMs / 1e3;
|
|
145
|
+
for (const userId in this.banList) {
|
|
146
|
+
if (this.banList[userId] < nowSec) {
|
|
147
|
+
delete this.banList[userId];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const userId in this.actionTimes) {
|
|
151
|
+
if (!this.actionTimes[userId]?.length) {
|
|
152
|
+
delete this.actionTimes[userId];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const [key, value] of this.nicknameCache) {
|
|
156
|
+
if (nowMs - value.timestamp > _CcbState.CACHE_DURATION) {
|
|
157
|
+
this.nicknameCache.delete(key);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
getAvatar(userId) {
|
|
122
162
|
return `https://q4.qlogo.cn/headimg_dl?dst_uin=${userId}&spec=640`;
|
|
123
163
|
}
|
|
124
|
-
|
|
125
|
-
async function getUserNickname(session, userId) {
|
|
164
|
+
async getUserNickname(session, userId) {
|
|
126
165
|
const cacheKey = `${session.guildId}:${userId}`;
|
|
127
|
-
const cached = nicknameCache.get(cacheKey);
|
|
166
|
+
const cached = this.nicknameCache.get(cacheKey);
|
|
128
167
|
const now = Date.now();
|
|
129
|
-
if (cached && now - cached.timestamp < CACHE_DURATION) {
|
|
168
|
+
if (cached && now - cached.timestamp < _CcbState.CACHE_DURATION) {
|
|
130
169
|
return cached.name;
|
|
131
170
|
}
|
|
132
171
|
const setAndReturnName = /* @__PURE__ */ __name((name2) => {
|
|
133
172
|
if (name2 && name2 !== userId) {
|
|
134
173
|
const actualName = name2.trim();
|
|
135
174
|
if (actualName) {
|
|
136
|
-
nicknameCache.
|
|
175
|
+
if (this.nicknameCache.size >= _CcbState.MAX_CACHE_SIZE) {
|
|
176
|
+
const oldestKey = this.nicknameCache.keys().next().value;
|
|
177
|
+
if (oldestKey) this.nicknameCache.delete(oldestKey);
|
|
178
|
+
}
|
|
179
|
+
this.nicknameCache.set(cacheKey, { name: actualName, timestamp: now });
|
|
137
180
|
return actualName;
|
|
138
181
|
}
|
|
139
182
|
}
|
|
@@ -150,7 +193,7 @@ function apply(ctx, config) {
|
|
|
150
193
|
}
|
|
151
194
|
try {
|
|
152
195
|
const userInfo = await session.bot.getUser(userId);
|
|
153
|
-
const displayName = userInfo?.name || userInfo?.nick
|
|
196
|
+
const displayName = userInfo?.name || userInfo?.nick;
|
|
154
197
|
const result = setAndReturnName(displayName);
|
|
155
198
|
if (result) return result;
|
|
156
199
|
} catch (e) {
|
|
@@ -163,126 +206,166 @@ function apply(ctx, config) {
|
|
|
163
206
|
} catch (nestedError) {
|
|
164
207
|
}
|
|
165
208
|
const friendlyName = `用户${userId}`;
|
|
166
|
-
nicknameCache.set(cacheKey, { name: friendlyName, timestamp: now });
|
|
209
|
+
this.nicknameCache.set(cacheKey, { name: friendlyName, timestamp: now });
|
|
167
210
|
return friendlyName;
|
|
168
211
|
}
|
|
169
|
-
|
|
170
|
-
function checkGroupCommand(session) {
|
|
212
|
+
checkGroupCommand(session) {
|
|
171
213
|
if (!session.guildId) {
|
|
172
214
|
return "此命令只能在群聊中使用。";
|
|
173
215
|
}
|
|
174
216
|
return null;
|
|
175
217
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
} else if (/^\d+$/.test(target)) {
|
|
184
|
-
targetUserId = target;
|
|
218
|
+
async findTargetUser(session, input) {
|
|
219
|
+
if (!input) return null;
|
|
220
|
+
try {
|
|
221
|
+
const elements = import_koishi2.h.parse(input);
|
|
222
|
+
const atEl = elements.find((el) => el.type === "at");
|
|
223
|
+
if (atEl?.attrs?.id) {
|
|
224
|
+
return String(atEl.attrs.id);
|
|
185
225
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
226
|
+
} catch (e) {
|
|
227
|
+
const atMatch = input.match(/<at\s+(?:.*?\s+)?id=(["'])(.*?)\1/i);
|
|
228
|
+
if (atMatch) return atMatch[2];
|
|
229
|
+
}
|
|
230
|
+
const colonIndex = input.indexOf(":");
|
|
231
|
+
if (colonIndex > 0 && colonIndex < input.length - 1) {
|
|
232
|
+
return input.slice(colonIndex + 1);
|
|
233
|
+
}
|
|
234
|
+
if (/^\d+$/.test(input)) {
|
|
235
|
+
return input;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const list = await session.bot.getGuildMemberList(session.guildId);
|
|
239
|
+
const members = list?.data;
|
|
240
|
+
if (!members?.length) return null;
|
|
241
|
+
const targetName = input.replace(/\s/g, "").toLowerCase();
|
|
242
|
+
let exactMatchId;
|
|
243
|
+
let partialMatchId;
|
|
244
|
+
for (const m of members) {
|
|
245
|
+
const nick = m.nick || m.user?.name || m.name || "";
|
|
246
|
+
if (!nick) continue;
|
|
247
|
+
const cleanNick = nick.replace(/\s/g, "").toLowerCase();
|
|
248
|
+
if (cleanNick === targetName) {
|
|
249
|
+
exactMatchId = m.user?.id;
|
|
250
|
+
break;
|
|
194
251
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
targetUserId = session.quote.user.id;
|
|
198
|
-
try {
|
|
199
|
-
const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserId);
|
|
200
|
-
if (!memberInfo) {
|
|
201
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
252
|
+
if (!partialMatchId && cleanNick.includes(targetName)) {
|
|
253
|
+
partialMatchId = m.user?.id;
|
|
202
254
|
}
|
|
203
|
-
} catch (error) {
|
|
204
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
205
255
|
}
|
|
256
|
+
const finalMatch = exactMatchId || partialMatchId;
|
|
257
|
+
if (finalMatch) return finalMatch;
|
|
258
|
+
} catch (e) {
|
|
206
259
|
}
|
|
207
|
-
return
|
|
260
|
+
return null;
|
|
208
261
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
let ccb_by = record.ccb_by || {};
|
|
219
|
-
ccb_by = JSON.parse(JSON.stringify(ccb_by));
|
|
220
|
-
if (senderId in ccb_by) {
|
|
221
|
-
const current = ccb_by[senderId];
|
|
222
|
-
ccb_by[senderId] = {
|
|
223
|
-
count: (current?.count || 0) + 1,
|
|
224
|
-
first: current?.first || false,
|
|
225
|
-
max: current?.max || false
|
|
226
|
-
};
|
|
227
|
-
} else {
|
|
228
|
-
ccb_by[senderId] = { count: 1, first: false, max: false };
|
|
229
|
-
}
|
|
230
|
-
let prev_max = record.max || 0;
|
|
231
|
-
if (prev_max === 0 && (record.num || 0) > 0) {
|
|
232
|
-
prev_max = parseFloat(((record.vol || 0) / (record.num || 0)).toFixed(2));
|
|
233
|
-
}
|
|
234
|
-
let newMax = prev_max;
|
|
235
|
-
if (V > prev_max) {
|
|
236
|
-
newMax = V;
|
|
237
|
-
for (const k in ccb_by) {
|
|
238
|
-
if (ccb_by[k]) ccb_by[k].max = false;
|
|
239
|
-
}
|
|
240
|
-
if (ccb_by[senderId]) ccb_by[senderId].max = true;
|
|
241
|
-
} else {
|
|
242
|
-
for (const k in ccb_by) {
|
|
243
|
-
if (ccb_by[k] && !ccb_by[k].max) {
|
|
244
|
-
ccb_by[k].max = false;
|
|
262
|
+
async validateTargetUser(session, target) {
|
|
263
|
+
if (target) {
|
|
264
|
+
const foundId = await this.findTargetUser(session, target);
|
|
265
|
+
if (foundId) {
|
|
266
|
+
try {
|
|
267
|
+
const member = await session.bot.getGuildMember(session.guildId, foundId);
|
|
268
|
+
if (!member) return "无法找到指定用户,请检查输入是否正确。";
|
|
269
|
+
} catch {
|
|
270
|
+
return "无法找到指定用户,请检查输入是否正确。";
|
|
245
271
|
}
|
|
272
|
+
return foundId;
|
|
246
273
|
}
|
|
274
|
+
return "无法找到指定用户,请检查输入是否正确。";
|
|
247
275
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
ccb_by
|
|
253
|
-
});
|
|
254
|
-
const resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
|
|
255
|
-
const message = [
|
|
256
|
-
resultMessage,
|
|
257
|
-
import_koishi.segment.image(pic),
|
|
258
|
-
`这是ta的第${newNum}次。`
|
|
259
|
-
].join("\n");
|
|
260
|
-
return message;
|
|
276
|
+
if (session.quote?.user?.id) {
|
|
277
|
+
return session.quote.user.id;
|
|
278
|
+
}
|
|
279
|
+
return session.userId;
|
|
261
280
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/core.ts
|
|
284
|
+
var import_koishi3 = require("koishi");
|
|
285
|
+
async function createNewCCBRecord(ctx, session, groupId, targetUserId, duration, V, nickname, crit, pic) {
|
|
286
|
+
const newRecord = {
|
|
287
|
+
groupId,
|
|
288
|
+
userId: targetUserId,
|
|
289
|
+
num: 1,
|
|
290
|
+
vol: V,
|
|
291
|
+
max: V,
|
|
292
|
+
ccb_by: { [session.userId]: { count: 1, first: true, max: true } }
|
|
293
|
+
};
|
|
294
|
+
await ctx.database.upsert("ccb_record", [newRecord]);
|
|
295
|
+
const resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
|
|
296
|
+
const message = [
|
|
297
|
+
resultMessage,
|
|
298
|
+
import_koishi3.segment.image(pic),
|
|
299
|
+
"这是ta的初体验。"
|
|
300
|
+
].join("\n");
|
|
301
|
+
return message;
|
|
302
|
+
}
|
|
303
|
+
__name(createNewCCBRecord, "createNewCCBRecord");
|
|
304
|
+
async function updateCCBRecord(ctx, session, groupId, targetUserId, duration, V, nickname, crit, pic) {
|
|
305
|
+
const [record] = await ctx.database.get("ccb_record", { groupId, userId: targetUserId });
|
|
306
|
+
if (!record) {
|
|
307
|
+
return await createNewCCBRecord(ctx, session, groupId, targetUserId, duration, V, nickname, crit, pic);
|
|
308
|
+
}
|
|
309
|
+
const senderId = session.userId;
|
|
310
|
+
const newNum = (record.num || 0) + 1;
|
|
311
|
+
const newVol = parseFloat(((record.vol || 0) + V).toFixed(2));
|
|
312
|
+
let ccb_by = record.ccb_by || {};
|
|
313
|
+
ccb_by = JSON.parse(JSON.stringify(ccb_by));
|
|
314
|
+
if (senderId in ccb_by) {
|
|
315
|
+
const current = ccb_by[senderId];
|
|
316
|
+
ccb_by[senderId] = {
|
|
317
|
+
count: (current?.count || 0) + 1,
|
|
318
|
+
first: current?.first || false,
|
|
319
|
+
max: current?.max || false
|
|
271
320
|
};
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const message = [
|
|
275
|
-
resultMessage,
|
|
276
|
-
import_koishi.segment.image(pic),
|
|
277
|
-
"这是ta的初体验。"
|
|
278
|
-
].join("\n");
|
|
279
|
-
return message;
|
|
321
|
+
} else {
|
|
322
|
+
ccb_by[senderId] = { count: 1, first: false, max: false };
|
|
280
323
|
}
|
|
281
|
-
|
|
324
|
+
let prev_max = record.max || 0;
|
|
325
|
+
if (prev_max === 0 && (record.num || 0) > 0) {
|
|
326
|
+
prev_max = parseFloat(((record.vol || 0) / (record.num || 0)).toFixed(2));
|
|
327
|
+
}
|
|
328
|
+
let newMax = prev_max;
|
|
329
|
+
if (V > prev_max) {
|
|
330
|
+
newMax = V;
|
|
331
|
+
for (const k in ccb_by) {
|
|
332
|
+
if (ccb_by[k]) ccb_by[k].max = false;
|
|
333
|
+
}
|
|
334
|
+
if (ccb_by[senderId]) ccb_by[senderId].max = true;
|
|
335
|
+
}
|
|
336
|
+
await ctx.database.set("ccb_record", { groupId, userId: targetUserId }, {
|
|
337
|
+
num: newNum,
|
|
338
|
+
vol: newVol,
|
|
339
|
+
max: newMax,
|
|
340
|
+
ccb_by
|
|
341
|
+
});
|
|
342
|
+
const resultMessage = crit ? `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了 💥 暴击!${V.toFixed(2)}ml的生命因子` : `你和${nickname}发生了${duration}min长的ccb行为,向ta注入了${V.toFixed(2)}ml的生命因子`;
|
|
343
|
+
const message = [
|
|
344
|
+
resultMessage,
|
|
345
|
+
import_koishi3.segment.image(pic),
|
|
346
|
+
`这是ta的第${newNum}次。`
|
|
347
|
+
].join("\n");
|
|
348
|
+
return message;
|
|
349
|
+
}
|
|
350
|
+
__name(updateCCBRecord, "updateCCBRecord");
|
|
351
|
+
|
|
352
|
+
// src/commands/ccb.ts
|
|
353
|
+
function applyCcbCommand(ctx, config, state) {
|
|
282
354
|
ctx.command("ccb [target:user]", "给群友注入生命因子").option("off", "--off [user:string] 将自己加入白名单(禁止被人ccb),可指定用户").option("on", "--on [user:string] 将自己移出白名单(允许被人ccb),可指定用户").action(async ({ session, options }, target) => {
|
|
283
|
-
const checkResult = checkGroupCommand(session);
|
|
355
|
+
const checkResult = state.checkGroupCommand(session);
|
|
284
356
|
if (checkResult) return checkResult;
|
|
285
357
|
const senderId = session.userId;
|
|
358
|
+
const checkCooldown = /* @__PURE__ */ __name((lastToggle) => {
|
|
359
|
+
const now2 = Date.now();
|
|
360
|
+
const cooldownMs = config.toggleCooldown * 1e3;
|
|
361
|
+
if (now2 - lastToggle < cooldownMs) {
|
|
362
|
+
const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
|
|
363
|
+
const m = Math.floor(remain / 60);
|
|
364
|
+
const s = remain % 60;
|
|
365
|
+
return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}, "checkCooldown");
|
|
286
369
|
const hasOff = "off" in options;
|
|
287
370
|
const hasOn = "on" in options;
|
|
288
371
|
if (hasOff || hasOn) {
|
|
@@ -290,29 +373,7 @@ function apply(ctx, config) {
|
|
|
290
373
|
const optionVal = isOff ? options.off : options.on;
|
|
291
374
|
let targetUserStr = null;
|
|
292
375
|
if (typeof optionVal === "string" && optionVal.trim()) {
|
|
293
|
-
|
|
294
|
-
const atIdMatch = val.match(/<at\s[^>]*id="([^"]+)"/);
|
|
295
|
-
if (atIdMatch) {
|
|
296
|
-
targetUserStr = atIdMatch[1];
|
|
297
|
-
} else if (/^\d+$/.test(val) || /^[^:]+:.+$/.test(val)) {
|
|
298
|
-
targetUserStr = val;
|
|
299
|
-
} else {
|
|
300
|
-
try {
|
|
301
|
-
const memberList = await session.bot.getGuildMemberList(session.guildId);
|
|
302
|
-
const members = memberList?.data || [];
|
|
303
|
-
const found = members.find((m) => {
|
|
304
|
-
const nick = m.nick || m.user?.name || m.name || "";
|
|
305
|
-
return nick.trim() === val || nick === val;
|
|
306
|
-
});
|
|
307
|
-
if (found) {
|
|
308
|
-
targetUserStr = found.user?.id;
|
|
309
|
-
} else {
|
|
310
|
-
return `无法通过昵称「${val}」找到群成员,请尝试使用QQ号。`;
|
|
311
|
-
}
|
|
312
|
-
} catch (e) {
|
|
313
|
-
return `无法搜索群成员,请尝试使用QQ号。`;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
376
|
+
targetUserStr = await state.findTargetUser(session, optionVal.trim());
|
|
316
377
|
}
|
|
317
378
|
if (!targetUserStr) {
|
|
318
379
|
const atEl = session.elements?.find((el) => el.type === "at");
|
|
@@ -320,60 +381,42 @@ function apply(ctx, config) {
|
|
|
320
381
|
targetUserStr = String(atEl.attrs.id);
|
|
321
382
|
}
|
|
322
383
|
}
|
|
323
|
-
if (!targetUserStr) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
|
|
384
|
+
if (!targetUserStr && typeof optionVal === "string" && optionVal.trim()) {
|
|
385
|
+
return `无法找到用户「${optionVal}」,请检查输入是否正确。`;
|
|
386
|
+
}
|
|
387
|
+
if (targetUserStr) {
|
|
388
|
+
try {
|
|
389
|
+
const memberInfo = await session.bot.getGuildMember(session.guildId, targetUserStr);
|
|
390
|
+
if (!memberInfo) return "无法找到指定用户,请检查输入是否正确。";
|
|
391
|
+
} catch (error) {
|
|
392
|
+
return "无法找到指定用户,请检查输入是否正确。";
|
|
333
393
|
}
|
|
394
|
+
}
|
|
395
|
+
const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
|
|
396
|
+
const lastToggle = userSetting?.lastToggleTime || 0;
|
|
397
|
+
const cooldownResult = checkCooldown(lastToggle);
|
|
398
|
+
if (cooldownResult) return cooldownResult;
|
|
399
|
+
const nowMs = Date.now();
|
|
400
|
+
if (!targetUserStr) {
|
|
334
401
|
const newOptOut = !!isOff;
|
|
335
402
|
await ctx.database.upsert("ccb_setting", [{
|
|
336
403
|
userId: senderId,
|
|
337
404
|
optOut: newOptOut,
|
|
338
|
-
lastToggleTime:
|
|
405
|
+
lastToggleTime: nowMs,
|
|
339
406
|
overrides: userSetting?.overrides || {}
|
|
340
407
|
}]);
|
|
341
408
|
return newOptOut ? "已开启全局保护模式,阻止你被ccb。" : "已关闭全局保护模式,允许你被ccb。";
|
|
342
409
|
} else {
|
|
343
|
-
|
|
344
|
-
const match = targetUserStr.match(/^[^:]+:(.+)$/);
|
|
345
|
-
if (match) targetId = match[1];
|
|
346
|
-
try {
|
|
347
|
-
const memberInfo = await session.bot.getGuildMember(session.guildId, targetId);
|
|
348
|
-
if (!memberInfo) {
|
|
349
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
350
|
-
}
|
|
351
|
-
} catch (error) {
|
|
352
|
-
return "无法找到指定用户,请检查输入是否正确。";
|
|
353
|
-
}
|
|
354
|
-
const now2 = Date.now();
|
|
355
|
-
const [userSetting] = await ctx.database.get("ccb_setting", { userId: senderId });
|
|
356
|
-
const lastToggle = userSetting?.lastToggleTime || 0;
|
|
357
|
-
const cooldownMs = config.toggleCooldown * 1e3;
|
|
358
|
-
if (now2 - lastToggle < cooldownMs) {
|
|
359
|
-
const remain = Math.ceil((cooldownMs - (now2 - lastToggle)) / 1e3);
|
|
360
|
-
const m = Math.floor(remain / 60);
|
|
361
|
-
const s = remain % 60;
|
|
362
|
-
return `操作太频繁了,请等待 ${m}分${s}秒 后再试。`;
|
|
363
|
-
}
|
|
410
|
+
const targetId = targetUserStr;
|
|
364
411
|
const overrides = userSetting?.overrides || {};
|
|
365
|
-
|
|
366
|
-
overrides[targetId] = false;
|
|
367
|
-
} else {
|
|
368
|
-
overrides[targetId] = true;
|
|
369
|
-
}
|
|
412
|
+
overrides[targetId] = !isOff;
|
|
370
413
|
await ctx.database.upsert("ccb_setting", [{
|
|
371
414
|
userId: senderId,
|
|
372
415
|
overrides,
|
|
373
416
|
optOut: userSetting?.optOut ?? false,
|
|
374
|
-
lastToggleTime:
|
|
417
|
+
lastToggleTime: nowMs
|
|
375
418
|
}]);
|
|
376
|
-
const targetNick = await getUserNickname(session, targetId).catch(() => targetId) || targetId;
|
|
419
|
+
const targetNick = await state.getUserNickname(session, targetId).catch(() => targetId) || targetId;
|
|
377
420
|
return isOff ? `已禁止用户 ${targetNick} 对你ccb。` : `已允许用户 ${targetNick} 对你ccb。`;
|
|
378
421
|
}
|
|
379
422
|
}
|
|
@@ -391,50 +434,50 @@ function apply(ctx, config) {
|
|
|
391
434
|
ywProbability: cheatSetting ? cheatSetting.ywProbability : config.ywProbability,
|
|
392
435
|
critProb: cheatSetting ? cheatSetting.critProb : config.critProb
|
|
393
436
|
};
|
|
394
|
-
const banEnd = banList[actorId] || 0;
|
|
437
|
+
const banEnd = state.banList[actorId] || 0;
|
|
395
438
|
if (now < banEnd) {
|
|
396
439
|
const remain = Math.floor(banEnd - now);
|
|
397
440
|
const m = Math.floor(remain / 60);
|
|
398
441
|
const s = remain % 60;
|
|
399
442
|
return `嘻嘻,你已经一滴不剩了,填充还剩 ${m}分${s}秒`;
|
|
400
443
|
}
|
|
401
|
-
const times = actionTimes[actorId] = actionTimes[actorId] || [];
|
|
444
|
+
const times = state.actionTimes[actorId] = state.actionTimes[actorId] || [];
|
|
402
445
|
const cutoff = now - currentConfig.ywWindow;
|
|
403
446
|
while (times.length > 0 && times[0] < cutoff) {
|
|
404
447
|
times.shift();
|
|
405
448
|
}
|
|
406
449
|
times.push(now);
|
|
407
450
|
if (times.length > currentConfig.ywThreshold) {
|
|
408
|
-
banList[actorId] = now + currentConfig.ywBanDuration;
|
|
409
|
-
actionTimes[actorId] = [];
|
|
451
|
+
state.banList[actorId] = now + currentConfig.ywBanDuration;
|
|
452
|
+
state.actionTimes[actorId] = [];
|
|
410
453
|
return "冲得出来吗你就冲,再冲就给你折了";
|
|
411
454
|
}
|
|
412
|
-
let targetUserId = await validateTargetUser(session, target);
|
|
455
|
+
let targetUserId = await state.validateTargetUser(session, target);
|
|
413
456
|
if (targetUserId.startsWith("无法找到")) {
|
|
414
457
|
return targetUserId;
|
|
415
458
|
}
|
|
416
459
|
if (config.whiteList.includes(targetUserId)) {
|
|
417
|
-
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
460
|
+
const nickname = await state.getUserNickname(session, targetUserId) || targetUserId;
|
|
418
461
|
return `${nickname} 拒绝了和你ccb。`;
|
|
419
462
|
}
|
|
420
463
|
if (senderSetting?.overrides?.[targetUserId] === false) {
|
|
421
|
-
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
464
|
+
const nickname = await state.getUserNickname(session, targetUserId) || targetUserId;
|
|
422
465
|
return `你已禁止与 ${nickname} 进行ccb。`;
|
|
423
466
|
}
|
|
424
467
|
const [targetSetting] = await ctx.database.get("ccb_setting", { userId: targetUserId });
|
|
425
468
|
if (targetSetting) {
|
|
426
469
|
const overrides = targetSetting.overrides || {};
|
|
427
470
|
if (overrides[actorId] === false) {
|
|
428
|
-
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
471
|
+
const nickname = await state.getUserNickname(session, targetUserId) || targetUserId;
|
|
429
472
|
return `${nickname} 拒绝了和你ccb`;
|
|
430
473
|
}
|
|
431
474
|
if (overrides[actorId] !== true && targetSetting.optOut) {
|
|
432
|
-
const nickname = await getUserNickname(session, targetUserId) || targetUserId;
|
|
475
|
+
const nickname = await state.getUserNickname(session, targetUserId) || targetUserId;
|
|
433
476
|
return `${nickname} 拒绝了和你ccb`;
|
|
434
477
|
}
|
|
435
478
|
}
|
|
436
479
|
if (targetUserId === actorId && !config.selfCcb) {
|
|
437
|
-
return "
|
|
480
|
+
return "怎么还能对自己下手啊(恼)";
|
|
438
481
|
}
|
|
439
482
|
const duration = parseFloat((Math.random() * 59 + 1).toFixed(2));
|
|
440
483
|
let V = parseFloat((Math.random() * 99 + 1).toFixed(2));
|
|
@@ -444,66 +487,71 @@ function apply(ctx, config) {
|
|
|
444
487
|
V = parseFloat((V * 2).toFixed(2));
|
|
445
488
|
crit = true;
|
|
446
489
|
}
|
|
447
|
-
const pic = getAvatar(targetUserId);
|
|
448
|
-
const exists = await ctx.database.get("ccb_record", {
|
|
449
|
-
groupId: session.guildId,
|
|
450
|
-
userId: targetUserId
|
|
451
|
-
});
|
|
490
|
+
const pic = state.getAvatar(targetUserId);
|
|
452
491
|
let message;
|
|
453
492
|
try {
|
|
454
|
-
const nickname = await getUserNickname(session, targetUserId);
|
|
455
|
-
|
|
456
|
-
message = await updateCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
|
|
457
|
-
} else {
|
|
458
|
-
message = await createNewCCBRecord(session, session.guildId, targetUserId, duration, V, nickname, pic);
|
|
459
|
-
}
|
|
493
|
+
const nickname = await state.getUserNickname(session, targetUserId);
|
|
494
|
+
message = await updateCCBRecord(ctx, session, session.guildId, targetUserId, duration, V, nickname, crit, pic);
|
|
460
495
|
} catch (e) {
|
|
461
496
|
console.error(`报错: ${e}`);
|
|
462
497
|
return "对方拒绝了和你ccb";
|
|
463
498
|
}
|
|
464
499
|
if (Math.random() < currentConfig.ywProbability) {
|
|
465
|
-
banList[actorId] = now + currentConfig.ywBanDuration;
|
|
500
|
+
state.banList[actorId] = now + currentConfig.ywBanDuration;
|
|
466
501
|
await session.send(message);
|
|
467
|
-
return "
|
|
502
|
+
return "💥你炸膛了!不能ccb了(悲)";
|
|
468
503
|
}
|
|
469
504
|
return message;
|
|
470
505
|
});
|
|
506
|
+
}
|
|
507
|
+
__name(applyCcbCommand, "applyCcbCommand");
|
|
508
|
+
|
|
509
|
+
// src/commands/rank.ts
|
|
510
|
+
function applyRankCommands(ctx, state) {
|
|
511
|
+
async function buildRanking(session, title, data, formatLine) {
|
|
512
|
+
const nicknameMap = /* @__PURE__ */ new Map();
|
|
513
|
+
await Promise.all(data.map(async (r) => {
|
|
514
|
+
nicknameMap.set(r.userId, await state.getUserNickname(session, r.userId));
|
|
515
|
+
}));
|
|
516
|
+
let msg = `${title}
|
|
517
|
+
`;
|
|
518
|
+
for (let i = 0; i < data.length; i++) {
|
|
519
|
+
const nick = nicknameMap.get(data[i].userId) || data[i].userId;
|
|
520
|
+
msg += formatLine(data[i], nick, i);
|
|
521
|
+
}
|
|
522
|
+
return msg.trim();
|
|
523
|
+
}
|
|
524
|
+
__name(buildRanking, "buildRanking");
|
|
471
525
|
ctx.command("ccbtop", "按次数排行").action(async ({ session }) => {
|
|
472
|
-
const checkResult = checkGroupCommand(session);
|
|
526
|
+
const checkResult = state.checkGroupCommand(session);
|
|
473
527
|
if (checkResult) return checkResult;
|
|
474
528
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
475
529
|
if (!groupData.length) return "当前群暂无ccb记录。";
|
|
476
530
|
const top5 = groupData.sort((a, b) => b.num - a.num).slice(0, 5);
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
`;
|
|
485
|
-
}
|
|
486
|
-
return msg.trim();
|
|
531
|
+
return buildRanking(
|
|
532
|
+
session,
|
|
533
|
+
"被ccb排行榜 TOP5:",
|
|
534
|
+
top5,
|
|
535
|
+
(r, nick, i) => `${i + 1}. ${nick} - 次数:${r.num}
|
|
536
|
+
`
|
|
537
|
+
);
|
|
487
538
|
});
|
|
488
539
|
ctx.command("ccbvol", "按注入量排行").action(async ({ session }) => {
|
|
489
|
-
const checkResult = checkGroupCommand(session);
|
|
540
|
+
const checkResult = state.checkGroupCommand(session);
|
|
490
541
|
if (checkResult) return checkResult;
|
|
491
542
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
492
543
|
if (!groupData.length) return "当前群暂无ccb记录。";
|
|
493
544
|
const top5 = groupData.sort((a, b) => b.vol - a.vol).slice(0, 5);
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
`;
|
|
502
|
-
}
|
|
503
|
-
return msg.trim();
|
|
545
|
+
return buildRanking(
|
|
546
|
+
session,
|
|
547
|
+
"被注入量排行榜 TOP5:",
|
|
548
|
+
top5,
|
|
549
|
+
(r, nick, i) => `${i + 1}. ${nick} - 累计注入:${r.vol.toFixed(2)}ml
|
|
550
|
+
`
|
|
551
|
+
);
|
|
504
552
|
});
|
|
505
553
|
ctx.command("ccbmax", "按max值排行并输出产生者").action(async ({ session }) => {
|
|
506
|
-
const checkResult = checkGroupCommand(session);
|
|
554
|
+
const checkResult = state.checkGroupCommand(session);
|
|
507
555
|
if (checkResult) return checkResult;
|
|
508
556
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
509
557
|
if (!groupData.length) return "当前群暂无ccb记录。";
|
|
@@ -543,7 +591,7 @@ function apply(ctx, config) {
|
|
|
543
591
|
const uniqueUserIds = [...new Set(userIds)];
|
|
544
592
|
const nicknameMap = /* @__PURE__ */ new Map();
|
|
545
593
|
await Promise.all(uniqueUserIds.map(async (uid) => {
|
|
546
|
-
nicknameMap.set(uid, await getUserNickname(session, uid));
|
|
594
|
+
nicknameMap.set(uid, await state.getUserNickname(session, uid));
|
|
547
595
|
}));
|
|
548
596
|
let msg = "单次最大注入排行榜 TOP5:\n";
|
|
549
597
|
for (let i = 0; i < entries.length; i++) {
|
|
@@ -556,19 +604,23 @@ function apply(ctx, config) {
|
|
|
556
604
|
}
|
|
557
605
|
return msg.trim();
|
|
558
606
|
});
|
|
607
|
+
}
|
|
608
|
+
__name(applyRankCommands, "applyRankCommands");
|
|
609
|
+
|
|
610
|
+
// src/commands/info.ts
|
|
611
|
+
function applyInfoCommand(ctx, state) {
|
|
559
612
|
ctx.command("ccbinfo [target:user]", "查询某人ccb信息").action(async ({ session }, target) => {
|
|
560
|
-
const checkResult = checkGroupCommand(session);
|
|
613
|
+
const checkResult = state.checkGroupCommand(session);
|
|
561
614
|
if (checkResult) return checkResult;
|
|
562
|
-
let targetUserId = session
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
if (match) targetUserId = match[1];
|
|
615
|
+
let targetUserId = await state.validateTargetUser(session, target);
|
|
616
|
+
if (targetUserId.startsWith("无法找到")) {
|
|
617
|
+
return targetUserId;
|
|
566
618
|
}
|
|
567
619
|
const [record] = await ctx.database.get("ccb_record", { groupId: session.guildId, userId: targetUserId });
|
|
568
620
|
if (!record) return "该用户暂无ccb记录。";
|
|
569
621
|
const total_num = record.num;
|
|
570
622
|
const total_vol = record.vol;
|
|
571
|
-
|
|
623
|
+
const max_val = record.max || (total_num > 0 ? total_vol / total_num : 0);
|
|
572
624
|
const groupData = await ctx.database.get("ccb_record", { groupId: session.guildId });
|
|
573
625
|
let cb_total = 0;
|
|
574
626
|
for (const r of groupData) {
|
|
@@ -592,20 +644,25 @@ function apply(ctx, config) {
|
|
|
592
644
|
}
|
|
593
645
|
}
|
|
594
646
|
}
|
|
595
|
-
const target_nick = await getUserNickname(session, targetUserId);
|
|
596
|
-
const first_nick = first_actor ? await getUserNickname(session, first_actor) : "未知";
|
|
647
|
+
const target_nick = await state.getUserNickname(session, targetUserId);
|
|
648
|
+
const first_nick = first_actor ? await state.getUserNickname(session, first_actor) : "未知";
|
|
597
649
|
const msg = [
|
|
598
650
|
`【${target_nick} 】`,
|
|
599
|
-
`•
|
|
600
|
-
`•
|
|
601
|
-
`•
|
|
602
|
-
`•
|
|
603
|
-
`•
|
|
651
|
+
`• 开拓者:${first_nick}`,
|
|
652
|
+
`• 被注入次数:${total_num}`,
|
|
653
|
+
`• 主动出击:${cb_total}`,
|
|
654
|
+
`• 累计容量:${total_vol.toFixed(2)}ml`,
|
|
655
|
+
`• 单次最高:${max_val.toFixed(2)}ml`
|
|
604
656
|
].join("\n");
|
|
605
657
|
return msg;
|
|
606
658
|
});
|
|
607
|
-
|
|
608
|
-
|
|
659
|
+
}
|
|
660
|
+
__name(applyInfoCommand, "applyInfoCommand");
|
|
661
|
+
|
|
662
|
+
// src/commands/charm.ts
|
|
663
|
+
function applyCharmCommand(ctx, state) {
|
|
664
|
+
ctx.command("ccbcharm", "魅力榜 - 计算群中最受欢迎的群友").action(async ({ session }) => {
|
|
665
|
+
const checkResult = state.checkGroupCommand(session);
|
|
609
666
|
if (checkResult) return checkResult;
|
|
610
667
|
const w_num = 1;
|
|
611
668
|
const w_vol = 0.1;
|
|
@@ -626,23 +683,43 @@ function apply(ctx, config) {
|
|
|
626
683
|
}).sort((a, b) => b.val - a.val).slice(0, 5);
|
|
627
684
|
const nicknameMap = /* @__PURE__ */ new Map();
|
|
628
685
|
await Promise.all(ranking.map(async (r) => {
|
|
629
|
-
nicknameMap.set(r.userId, await getUserNickname(session, r.userId));
|
|
686
|
+
nicknameMap.set(r.userId, await state.getUserNickname(session, r.userId));
|
|
630
687
|
}));
|
|
631
|
-
let msg = "💎
|
|
688
|
+
let msg = "💎 魅力榜 TOP5 💎\n";
|
|
632
689
|
for (let i = 0; i < ranking.length; i++) {
|
|
633
690
|
const { userId, val } = ranking[i];
|
|
634
691
|
const nick = nicknameMap.get(userId) || userId;
|
|
635
|
-
msg += `${i + 1}. ${nick} -
|
|
692
|
+
msg += `${i + 1}. ${nick} - 魅力值:${val.toFixed(2)}
|
|
636
693
|
`;
|
|
637
694
|
}
|
|
638
695
|
return msg.trim();
|
|
639
696
|
});
|
|
640
697
|
}
|
|
698
|
+
__name(applyCharmCommand, "applyCharmCommand");
|
|
699
|
+
|
|
700
|
+
// src/commands/index.ts
|
|
701
|
+
function applyCommands(ctx, config, state) {
|
|
702
|
+
applyCcbCommand(ctx, config, state);
|
|
703
|
+
applyRankCommands(ctx, state);
|
|
704
|
+
applyInfoCommand(ctx, state);
|
|
705
|
+
applyCharmCommand(ctx, state);
|
|
706
|
+
}
|
|
707
|
+
__name(applyCommands, "applyCommands");
|
|
708
|
+
|
|
709
|
+
// src/index.ts
|
|
710
|
+
var name = "ccb-plus";
|
|
711
|
+
var inject = ["database"];
|
|
712
|
+
function apply(ctx, config) {
|
|
713
|
+
applyDatabase(ctx);
|
|
714
|
+
const state = new CcbState(ctx);
|
|
715
|
+
applyCommands(ctx, config, state);
|
|
716
|
+
}
|
|
641
717
|
__name(apply, "apply");
|
|
642
718
|
// Annotate the CommonJS export names for ESM import in node:
|
|
643
719
|
0 && (module.exports = {
|
|
644
720
|
Config,
|
|
645
721
|
apply,
|
|
722
|
+
applyDatabase,
|
|
646
723
|
inject,
|
|
647
724
|
name
|
|
648
725
|
});
|
package/lib/model.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
export interface CCBRecord {
|
|
3
|
+
groupId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
num: number;
|
|
6
|
+
vol: number;
|
|
7
|
+
max: number;
|
|
8
|
+
ccb_by: {
|
|
9
|
+
[actorId: string]: {
|
|
10
|
+
count: number;
|
|
11
|
+
first: boolean;
|
|
12
|
+
max: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface CCBUserSetting {
|
|
17
|
+
userId: string;
|
|
18
|
+
optOut: boolean;
|
|
19
|
+
lastToggleTime: number;
|
|
20
|
+
overrides: Record<string, boolean>;
|
|
21
|
+
}
|
|
22
|
+
declare module 'koishi' {
|
|
23
|
+
interface Tables {
|
|
24
|
+
ccb_record: CCBRecord;
|
|
25
|
+
ccb_setting: CCBUserSetting;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export declare function applyDatabase(ctx: Context): void;
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Context, Session } from 'koishi';
|
|
2
|
+
export declare class CcbState {
|
|
3
|
+
actionTimes: Record<string, number[]>;
|
|
4
|
+
banList: Record<string, number>;
|
|
5
|
+
nicknameCache: Map<string, {
|
|
6
|
+
name: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}>;
|
|
9
|
+
private static MAX_CACHE_SIZE;
|
|
10
|
+
private static CACHE_DURATION;
|
|
11
|
+
private cleanupTimer;
|
|
12
|
+
constructor(ctx: Context);
|
|
13
|
+
private cleanup;
|
|
14
|
+
getAvatar(userId: string): string;
|
|
15
|
+
getUserNickname(session: Session, userId: string): Promise<string>;
|
|
16
|
+
checkGroupCommand(session: Session): string | null;
|
|
17
|
+
findTargetUser(session: Session, input: string): Promise<string | null>;
|
|
18
|
+
validateTargetUser(session: Session, target: string): Promise<string>;
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-ccb-plus",
|
|
3
|
-
"description": "Koishi插件,与群友发生ccb行为。(移植自astrbot_plugin_ccb_plus)",
|
|
4
|
-
"version": "0.2.
|
|
3
|
+
"description": "Koishi 插件,与群友发生 ccb 行为。(移植自 astrbot_plugin_ccb_plus )",
|
|
4
|
+
"version": "0.2.8-beta.2",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -20,4 +20,4 @@
|
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"koishi": "^4.18.7"
|
|
22
22
|
}
|
|
23
|
-
}
|
|
23
|
+
}
|
package/readme.md
CHANGED