koishi-plugin-lili-hub 0.1.0

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 (3) hide show
  1. package/lib/index.js +454 -0
  2. package/package.json +34 -0
  3. package/readme.md +5 -0
package/lib/index.js ADDED
@@ -0,0 +1,454 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name2 in all)
7
+ __defProp(target, name2, { get: all[name2], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // external/lili-hub/packages/lili-hub/src/index.ts
20
+ var src_exports = {};
21
+ __export(src_exports, {
22
+ Config: () => Config,
23
+ apply: () => apply,
24
+ inject: () => inject,
25
+ name: () => name
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+ var import_koishi = require("koishi");
29
+ var name = "lili-hub";
30
+ var inject = ["database"];
31
+ var VOICE_BASE = "https://vo.kaikai.men/p510/cn/";
32
+ var VOICE = {
33
+ /** 装弹 —— "我的脚都痒了!" */
34
+ spin: VOICE_BASE + "LiLi_IntroRespond00.mp3",
35
+ /** 开枪 —— "嘿!尝尝这个!" */
36
+ shoot: VOICE_BASE + "LiLi_Attack04.mp3",
37
+ /** 打中 —— "我打!" */
38
+ hit: VOICE_BASE + "LiLi_Kill03.mp3",
39
+ /** 打偏/没中 —— "喵了个咪呀,太糟了。" */
40
+ misfire: VOICE_BASE + "LiLi_AI_Uhoh01.mp3",
41
+ /** 庆祝 —— "我们赢了!" */
42
+ celebrate: VOICE_BASE + "LiLi_Celebrate00.mp3",
43
+ /** 喝酒 —— "小心别喝太多的保健酒……" */
44
+ drink: VOICE_BASE + "LiLi_Pissed04.mp3",
45
+ /** 喝醉了 —— "哇……还有点头晕晕的呢。" */
46
+ drunk: VOICE_BASE + "LiLi_Revive02.mp3",
47
+ /** 发酒疯/连续开枪 —— "打死你!" */
48
+ rampage: VOICE_BASE + "LiLi_Attack05.mp3",
49
+ /** 报仇 —— "哈,这下报仇啦!" */
50
+ revenge: VOICE_BASE + "LiLi_RevengeEnd02.mp3",
51
+ /** 嘲讽 —— "哇...你好呆!" */
52
+ taunt: VOICE_BASE + "LiLi_Social_Taunt00.mp3",
53
+ /** 道歉 —— "哦不,我十分非常抱歉!" */
54
+ sorry: VOICE_BASE + "LiLi_Social_Sorry00.mp3"
55
+ };
56
+ function voiceSeg(url) {
57
+ return (0, import_koishi.segment)("record", { file: url });
58
+ }
59
+ var Config = import_koishi.Schema.object({
60
+ // 轮盘设置
61
+ rouletteHitRate: import_koishi.Schema.number().description("\u5F00\u67AA\u540E\u5B50\u5F39\u6253\u4E2D\u4EBA\u7684\u6982\u7387 (0-1)").default(0.7).min(0).max(1).step(0.01),
62
+ muteDuration: import_koishi.Schema.number().description("\u6253\u4E2D\u540E\u7684\u7981\u8A00\u65F6\u957F\uFF08\u79D2\uFF09").default(300).min(0).max(2592e3),
63
+ drunkDuration: import_koishi.Schema.number().description("\u4E3D\u4E3D\u559D\u9152\u540E\u7684\u9189\u9152\u6301\u7EED\u65F6\u95F4\uFF08\u5206\u949F\uFF09").default(5).min(1).max(1440),
64
+ drunkMisfireRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u5B50\u5F39\u6253\u504F\u7684\u6982\u7387 (0-1)").default(0.35).min(0).max(1).step(0.01),
65
+ drunkRampageRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u8FDE\u7EED\u5F00\u67AA\uFF08\u591A\u7981\u8A00\u51E0\u4E2A\uFF09\u7684\u6982\u7387 (0-1)").default(0.3).min(0).max(1).step(0.01),
66
+ drunkRevengeRate: import_koishi.Schema.number().description("\u9189\u9152\u540E\u6CE2\u53CA\u5386\u53F2\u53C2\u4E0E\u8005\u7684\u6982\u7387 (0-1)").default(0.2).min(0).max(1).step(0.01),
67
+ maxHistoryRounds: import_koishi.Schema.number().description("\u4FDD\u7559\u591A\u5C11\u8F6E\u5386\u53F2\u8BB0\u5F55\uFF08\u7528\u4E8E\u62A5\u4EC7\u673A\u5236\uFF09").default(3).min(1).max(20),
68
+ voiceEnabled: import_koishi.Schema.boolean().description("\u662F\u5426\u64AD\u653E\u4E3D\u4E3D\u8BED\u97F3").default(true),
69
+ // 模仿群友
70
+ imitationEnabled: import_koishi.Schema.boolean().description("\u542F\u7528\u6A21\u4EFF\u7FA4\u53CB\u8BF4\u8BDD\u529F\u80FD").default(false),
71
+ imitationInterval: import_koishi.Schema.number().description("\u6BCF\u591A\u5C11\u6761\u6D88\u606F\u68C0\u67E5\u4E00\u6B21\u6A21\u4EFF").default(50).min(1).max(1e4),
72
+ imitationRate: import_koishi.Schema.number().description("\u6A21\u4EFF\u89E6\u53D1\u6982\u7387 (0-1)").default(0.05).min(0).max(1).step(0.01),
73
+ imitationGroups: import_koishi.Schema.string().description("\u6A21\u4EFF\u529F\u80FD\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A\u8868\u793A\u4E0D\u542F\u7528\uFF09").default(""),
74
+ // 查重
75
+ dedupEnabled: import_koishi.Schema.boolean().description("\u542F\u7528\u5408\u5E76\u6D88\u606F\u67E5\u91CD\u529F\u80FD\uFF08\u6C34\u8FC7\u4E86\uFF09").default(false),
76
+ dedupGroups: import_koishi.Schema.string().description("\u67E5\u91CD\u529F\u80FD\u767D\u540D\u5355\u7FA4\u53F7\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A\u8868\u793A\u4E0D\u542F\u7528\uFF09").default("")
77
+ });
78
+ function getGroupId(session) {
79
+ return session.guildId || session.channelId;
80
+ }
81
+ function randFloat() {
82
+ return Math.random();
83
+ }
84
+ function randInt(min, max) {
85
+ return Math.floor(Math.random() * (max - min + 1)) + min;
86
+ }
87
+ function parseGroupList(raw) {
88
+ if (!raw || !raw.trim()) return [];
89
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
90
+ }
91
+ function isForwardMessage(session) {
92
+ if (session.subtype === "forward") return true;
93
+ if (session.elements) {
94
+ return session.elements.some((el) => el.type === "forward" || el.type === "node");
95
+ }
96
+ return false;
97
+ }
98
+ function extractForwardText(session) {
99
+ let text = session.content || "";
100
+ text = text.replace(/<[^>]+>/g, " ").trim();
101
+ return text;
102
+ }
103
+ function apply(ctx, config) {
104
+ ctx.model.extend("lili_message", {
105
+ id: "unsigned",
106
+ gid: "string",
107
+ userId: "string",
108
+ userName: "string",
109
+ content: "string",
110
+ timestamp: "unsigned",
111
+ messageId: "string"
112
+ }, { autoInc: true });
113
+ ctx.model.extend("lili_roulette", {
114
+ id: "unsigned",
115
+ gid: "string",
116
+ hitUserId: "string",
117
+ timestamp: "unsigned"
118
+ }, { autoInc: true });
119
+ const drunkStates = /* @__PURE__ */ new Map();
120
+ const rouletteActive = /* @__PURE__ */ new Map();
121
+ const imitationCounters = /* @__PURE__ */ new Map();
122
+ function isDrunk(gid) {
123
+ return Date.now() < (drunkStates.get(gid) || 0);
124
+ }
125
+ async function tryMute(session, userId, durationSec) {
126
+ try {
127
+ await session.bot.setGroupBan(getGroupId(session), userId, durationSec * 1e3);
128
+ return true;
129
+ } catch {
130
+ }
131
+ try {
132
+ await session.bot.muteGuildMember(session.guildId, userId, durationSec * 1e3);
133
+ return true;
134
+ } catch {
135
+ }
136
+ return false;
137
+ }
138
+ async function pickRandomTarget(gid) {
139
+ try {
140
+ const messages = await ctx.database.get("lili_message", { gid }, {
141
+ sort: { timestamp: "desc" },
142
+ limit: 50,
143
+ fields: ["userId", "userName"]
144
+ });
145
+ if (!messages || messages.length === 0) return null;
146
+ const seen = /* @__PURE__ */ new Set();
147
+ const unique = [];
148
+ for (const m of messages) {
149
+ if (!seen.has(m.userId)) {
150
+ seen.add(m.userId);
151
+ unique.push({ userId: m.userId, userName: m.userName });
152
+ }
153
+ }
154
+ if (unique.length === 0) return null;
155
+ return unique[randInt(0, unique.length - 1)];
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
160
+ async function send(session, text, voice) {
161
+ const parts = [text];
162
+ if (config.voiceEnabled && voice) {
163
+ parts.push(voiceSeg(voice));
164
+ }
165
+ await session.send(parts.join(""));
166
+ }
167
+ async function drunkPostEffects(session, gid) {
168
+ if (!isDrunk(gid)) return;
169
+ if (randFloat() < config.drunkRampageRate) {
170
+ const target = await pickRandomTarget(gid);
171
+ if (target) {
172
+ const count = randInt(1, 3);
173
+ const victims = /* @__PURE__ */ new Set();
174
+ victims.add(target.userId);
175
+ for (let i = 0; i < count - 1; i++) {
176
+ const extra = await pickRandomTarget(gid);
177
+ if (extra) victims.add(extra.userId);
178
+ }
179
+ await session.sendQueued(
180
+ `\u{1F37A} \u4E3D\u4E3D\u559D\u9189\u4E86\u5F00\u59CB\u53D1\u9152\u75AF\uFF01`,
181
+ 500
182
+ );
183
+ if (config.voiceEnabled) {
184
+ await session.sendQueued(voiceSeg(VOICE.rampage), 300);
185
+ }
186
+ for (const v of victims) {
187
+ const muted = await tryMute(session, v, config.muteDuration);
188
+ await session.sendQueued(
189
+ muted ? `\u{1F52B} ${import_koishi.segment.at(v)} \u4E5F\u88AB\u6253\u4E86\uFF01\u7981\u8A00 ${config.muteDuration} \u79D2\uFF01` : `\u{1F52B} ${import_koishi.segment.at(v)} \u4E5F\u88AB\u6253\u4E86\uFF01(\u7981\u8A00\u5931\u8D25)`,
190
+ 500
191
+ );
192
+ }
193
+ }
194
+ }
195
+ if (randFloat() < config.drunkRevengeRate) {
196
+ try {
197
+ const history = await ctx.database.get("lili_roulette", { gid }, {
198
+ sort: { timestamp: "desc" },
199
+ limit: config.maxHistoryRounds
200
+ });
201
+ const allHitUsers = /* @__PURE__ */ new Set();
202
+ for (const h of history) {
203
+ if (h.hitUserId) allHitUsers.add(h.hitUserId);
204
+ }
205
+ const pool = [...allHitUsers].filter((u) => u !== session.userId);
206
+ if (pool.length > 0) {
207
+ const victim = pool[randInt(0, pool.length - 1)];
208
+ const muted = await tryMute(session, victim, config.muteDuration);
209
+ await session.sendQueued(
210
+ `\u{1F37A} \u4E3D\u4E3D\u9189\u91BA\u91BA\u5730\u60F3\u8D77\u4E86\u4EC0\u4E48\u2026\u2026
211
+ \u{1F52B} ${import_koishi.segment.at(victim)} \u4E3D\u4E3D\u60F3\u8D77\u4F60\u4E0A\u6B21\u7684\u4E8B\u4E86\uFF01\u7830\uFF01${muted ? `\u7981\u8A00 ${config.muteDuration} \u79D2\uFF01` : "(\u7981\u8A00\u5931\u8D25)"}`,
212
+ 500
213
+ );
214
+ if (config.voiceEnabled) {
215
+ await session.sendQueued(voiceSeg(VOICE.revenge), 300);
216
+ }
217
+ }
218
+ } catch {
219
+ }
220
+ }
221
+ }
222
+ async function executeRoulette(session) {
223
+ const gid = getGroupId(session);
224
+ if (config.voiceEnabled) {
225
+ await session.sendQueued(voiceSeg(VOICE.shoot), 300);
226
+ }
227
+ const target = await pickRandomTarget(gid);
228
+ if (!target) {
229
+ await session.sendQueued(
230
+ `\u{1F52B} \u4E3D\u4E3D\u8F6C\u4E86\u4E00\u5708\uFF0C\u6CA1\u627E\u5230\u76EE\u6807\u2026\u2026\u7B97\u4E86\uFF01`,
231
+ 500
232
+ );
233
+ return;
234
+ }
235
+ const drunk = isDrunk(gid);
236
+ if (drunk && randFloat() < config.drunkMisfireRate) {
237
+ await session.sendQueued(
238
+ `\u{1F52B} \u7830\uFF01\u4E3D\u4E3D\u7784\u51C6\u4E86 ${import_koishi.segment.at(target.userId)}\u2026\u2026\u{1F4A8} \u6253\u504F\u4E86\uFF01${import_koishi.segment.at(target.userId)} \u9003\u8FC7\u4E00\u52AB\uFF01`,
239
+ 500
240
+ );
241
+ if (config.voiceEnabled) {
242
+ await session.sendQueued(voiceSeg(VOICE.misfire), 300);
243
+ }
244
+ try {
245
+ await ctx.database.create("lili_roulette", { gid, hitUserId: "", timestamp: Date.now() });
246
+ } catch {
247
+ }
248
+ await drunkPostEffects(session, gid);
249
+ return;
250
+ }
251
+ if (randFloat() < config.rouletteHitRate) {
252
+ const muted = await tryMute(session, target.userId, config.muteDuration);
253
+ if (muted) {
254
+ await session.sendQueued(
255
+ `\u{1F52B} \u7830\uFF01\u{1F4A5}
256
+ \u4E3D\u4E3D\u6253\u4E2D\u4E86 ${import_koishi.segment.at(target.userId)}\uFF01\u7981\u8A00 ${config.muteDuration} \u79D2\uFF01`,
257
+ 500
258
+ );
259
+ } else {
260
+ await session.sendQueued(
261
+ `\u{1F52B} \u7830\uFF01\u{1F4A5}
262
+ \u4E3D\u4E3D\u6253\u4E2D\u4E86 ${import_koishi.segment.at(target.userId)}\uFF01(\u7981\u8A00\u5931\u8D25\uFF0C\u53EF\u80FD\u6743\u9650\u4E0D\u8DB3)`,
263
+ 500
264
+ );
265
+ }
266
+ if (config.voiceEnabled) {
267
+ await session.sendQueued(voiceSeg(VOICE.hit), 300);
268
+ }
269
+ try {
270
+ await ctx.database.create("lili_roulette", { gid, hitUserId: target.userId, timestamp: Date.now() });
271
+ } catch {
272
+ }
273
+ await drunkPostEffects(session, gid);
274
+ } else {
275
+ await session.sendQueued(
276
+ `\u{1F52B} \u7830\uFF01\u{1F4A8}
277
+ \u4E3D\u4E3D\u671D ${import_koishi.segment.at(target.userId)} \u5F00\u4E86\u4E00\u67AA\u2026\u2026\u6253\u504F\u4E86\uFF01${import_koishi.segment.at(target.userId)} \u9003\u8FC7\u4E00\u52AB\uFF01`,
278
+ 500
279
+ );
280
+ if (config.voiceEnabled) {
281
+ await session.sendQueued(voiceSeg(VOICE.misfire), 300);
282
+ }
283
+ try {
284
+ await ctx.database.create("lili_roulette", { gid, hitUserId: "", timestamp: Date.now() });
285
+ } catch {
286
+ }
287
+ }
288
+ }
289
+ ctx.command("\u4E3D\u4E3D\u8F6E\u76D8", "\u4FC4\u7F57\u65AF\u8F6E\u76D8\u8D4C\uFF1A\u5F00\u59CB\u4E00\u8F6E\u8F6E\u76D8\uFF0C\u53D1\u9001\u300C\u5F00\u67AA\u300D\u5C04\u51FB").action(async ({ session }) => {
290
+ if (!session || !session.userId) return;
291
+ const gid = getGroupId(session);
292
+ if (rouletteActive.has(gid)) {
293
+ return "\u{1F52B} \u8F6E\u76D8\u5DF2\u7ECF\u5728\u8F6C\u4E86\uFF01\u53D1\u9001\u300C\u5F00\u67AA\u300D\u6765\u5C04\u51FB\uFF01";
294
+ }
295
+ rouletteActive.set(gid, Date.now());
296
+ const drunkTag = isDrunk(gid) ? " \u{1F37A}" : "";
297
+ await send(
298
+ session,
299
+ `\u{1F52B} \u4E3D\u4E3D\u8F6C\u8D77\u4E86\u5DE6\u8F6E\u624B\u67AA\uFF01${drunkTag}
300
+ \u4E3D\u4E3D\uFF1A"\u6211\u7684\u811A\u90FD\u75D2\u4E86\uFF01"
301
+ \u53D1\u9001\u300C\u5F00\u67AA\u300D\u6765\u5C04\u51FB\uFF01`,
302
+ VOICE.spin
303
+ );
304
+ setTimeout(() => {
305
+ if (rouletteActive.get(gid) && Date.now() - (rouletteActive.get(gid) || 0) > 28e3) {
306
+ rouletteActive.delete(gid);
307
+ }
308
+ }, 3e4);
309
+ });
310
+ ctx.command("\u5F00\u67AA", "\u5BF9\u7740\u8F6E\u76D8\u5F00\u67AA").action(async ({ session }) => {
311
+ if (!session || !session.userId) return;
312
+ const gid = getGroupId(session);
313
+ if (!rouletteActive.has(gid)) {
314
+ return "\u{1F52B} \u8FD8\u6CA1\u6709\u5F00\u59CB\u8F6E\u76D8\uFF01\u5148\u53D1\u9001\u300C\u4E3D\u4E3D\u8F6E\u76D8\u300D\u6765\u8F6C\u5DE6\u8F6E\u5427\u3002";
315
+ }
316
+ rouletteActive.delete(gid);
317
+ await executeRoulette(session);
318
+ try {
319
+ const all = await ctx.database.get("lili_roulette", { gid }, {
320
+ sort: { timestamp: "desc" }
321
+ });
322
+ if (all.length > config.maxHistoryRounds) {
323
+ const toDelete = all.slice(config.maxHistoryRounds);
324
+ for (const r of toDelete) {
325
+ await ctx.database.remove("lili_roulette", { id: r.id });
326
+ }
327
+ }
328
+ } catch {
329
+ }
330
+ });
331
+ ctx.command("\u4E3D\u4E3D\u559D\u9152", "\u4E3D\u4E3D\u5F00\u59CB\u559D\u9152\uFF0C\u63A5\u4E0B\u6765\u4E00\u6BB5\u65F6\u95F4\u8F6E\u76D8\u4F1A\u51FA\u73B0\u66F4\u591A\u610F\u5916").action(async ({ session }) => {
332
+ if (!session || !session.userId) return;
333
+ const gid = getGroupId(session);
334
+ if (isDrunk(gid)) {
335
+ const remaining = Math.ceil(((drunkStates.get(gid) || 0) - Date.now()) / 6e4);
336
+ return `\u{1F37A} \u4E3D\u4E3D\u8FD8\u5728\u9189\u9152\u4E2D\uFF01\u8FD8\u6709\u7EA6 ${remaining} \u5206\u949F\u624D\u4F1A\u6E05\u9192\u2026\u2026`;
337
+ }
338
+ drunkStates.set(gid, Date.now() + config.drunkDuration * 60 * 1e3);
339
+ await send(
340
+ session,
341
+ `\u{1F37A} \u4E3D\u4E3D\u559D\u4E86\u4E00\u5927\u676F\u4FDD\u5065\u9152\uFF01
342
+ \u63A5\u4E0B\u6765 ${config.drunkDuration} \u5206\u949F\u5185\uFF0C\u4E3D\u4E3D\u8F6E\u76D8\u4F1A\u51FA\u73B0\u66F4\u591A\u610F\u5916\uFF1A
343
+ \xB7 \u8F6E\u76D8\u53EF\u80FD\u6253\u504F
344
+ \xB7 \u53EF\u80FD\u8FDE\u7EED\u5F00\u67AA\u7981\u8A00\u591A\u4EBA
345
+ \xB7 \u53EF\u80FD\u6CE2\u53CA\u5386\u53F2\u73A9\u5BB6
346
+
347
+ \u4E3D\u4E3D\uFF1A"\u5C0F\u5FC3\u522B\u559D\u592A\u591A\u7684\u4FDD\u5065\u9152\u2026\u2026\u559D\u592A\u591A\u4F1A\u8BA9\u4F60\u53D8\u767D\u75F4\u7684\u2026\u2026"`,
348
+ VOICE.drink
349
+ );
350
+ if (config.voiceEnabled) {
351
+ setTimeout(async () => {
352
+ try {
353
+ await session.sendQueued(voiceSeg(VOICE.drunk), 500);
354
+ } catch {
355
+ }
356
+ }, 3e3);
357
+ }
358
+ setTimeout(async () => {
359
+ if (drunkStates.get(gid) && Date.now() >= (drunkStates.get(gid) || 0) - 1e3) {
360
+ drunkStates.set(gid, 0);
361
+ try {
362
+ await session.sendQueued("\u{1F37A} \u4E3D\u4E3D\u6E05\u9192\u4E86\uFF01\u5934\u8FD8\u6709\u70B9\u6655\u2026\u2026", 500);
363
+ } catch {
364
+ }
365
+ }
366
+ }, config.drunkDuration * 60 * 1e3);
367
+ });
368
+ ctx.middleware(async (session, next) => {
369
+ const gid = getGroupId(session);
370
+ const content = session.content || "";
371
+ const plainText = session.elements ? session.elements.filter((el) => el.type === "text").map((el) => el.attrs?.content || "").join("").trim() : content.replace(/<[^>]+>/g, "").trim();
372
+ const textLen = plainText.length;
373
+ const shouldRecord = textLen >= 15 && textLen <= 60 && plainText;
374
+ if (config.dedupEnabled && isForwardMessage(session)) {
375
+ const dedupGroups2 = parseGroupList(config.dedupGroups);
376
+ if (dedupGroups2.length === 0 || dedupGroups2.includes(gid)) {
377
+ const fwdText = extractForwardText(session);
378
+ if (fwdText) {
379
+ try {
380
+ const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
381
+ const matches = await ctx.database.get("lili_message", {
382
+ gid,
383
+ content: fwdText
384
+ }, {
385
+ sort: { timestamp: "asc" },
386
+ limit: 1
387
+ });
388
+ if (matches.length > 0 && matches[0].timestamp >= sevenDaysAgo) {
389
+ const original = matches[0];
390
+ try {
391
+ await session.bot.sendMessage(gid, `\u{1F4A7} \u6C34\u8FC7\u4E86\uFF01\u8FD9\u6761\u6D88\u606F ${import_koishi.segment.at(original.userId)} \u5728 ${new Date(original.timestamp).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })} \u5C31\u53D1\u8FC7\u4E86\uFF01`, void 0);
392
+ } catch {
393
+ await session.send(`\u{1F4A7} \u6C34\u8FC7\u4E86\uFF01\u8FD9\u6761\u6D88\u606F ${import_koishi.segment.at(original.userId)} \u65E9\u5C31\u53D1\u8FC7\u4E86\uFF01\uFF08${new Date(original.timestamp).toLocaleDateString("zh-CN")}\uFF09`);
394
+ }
395
+ }
396
+ } catch {
397
+ }
398
+ }
399
+ }
400
+ }
401
+ const imitationGroups = parseGroupList(config.imitationGroups);
402
+ const dedupGroups = parseGroupList(config.dedupGroups);
403
+ const recordGroups = /* @__PURE__ */ new Set([...imitationGroups, ...dedupGroups]);
404
+ if (shouldRecord && (recordGroups.size === 0 || recordGroups.has(gid))) {
405
+ try {
406
+ await ctx.database.create("lili_message", {
407
+ gid,
408
+ userId: session.userId,
409
+ userName: session.username || session.userId,
410
+ content: plainText,
411
+ timestamp: Date.now(),
412
+ messageId: session.messageId || ""
413
+ });
414
+ } catch {
415
+ }
416
+ }
417
+ if (config.imitationEnabled && (imitationGroups.length === 0 || imitationGroups.includes(gid))) {
418
+ let count = imitationCounters.get(gid) || 0;
419
+ count++;
420
+ imitationCounters.set(gid, count);
421
+ if (count >= config.imitationInterval) {
422
+ imitationCounters.set(gid, 0);
423
+ if (randFloat() < config.imitationRate) {
424
+ try {
425
+ const allMessages = await ctx.database.get("lili_message", { gid }, {
426
+ sort: { timestamp: "desc" },
427
+ limit: 200
428
+ });
429
+ if (allMessages.length > 0) {
430
+ const picked = allMessages[randInt(0, allMessages.length - 1)];
431
+ await session.sendQueued(picked.content, 500);
432
+ }
433
+ } catch {
434
+ }
435
+ }
436
+ }
437
+ }
438
+ return next();
439
+ });
440
+ ctx.setInterval(async () => {
441
+ try {
442
+ const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
443
+ await ctx.database.remove("lili_message", { timestamp: { $lt: thirtyDaysAgo } });
444
+ } catch {
445
+ }
446
+ }, 36e5);
447
+ }
448
+ // Annotate the CommonJS export names for ESM import in node:
449
+ 0 && (module.exports = {
450
+ Config,
451
+ apply,
452
+ inject,
453
+ name
454
+ });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "koishi-plugin-lili-hub",
3
+ "description": "丽丽Hub — 丽丽主题QQ群娱乐插件:俄罗斯轮盘赌、喝酒发酒疯、模仿群友说话、合并消息查重(水过了)",
4
+ "version": "0.1.0",
5
+ "main": "lib/index.js",
6
+ "typings": "lib/index.d.ts",
7
+ "files": [
8
+ "lib"
9
+ ],
10
+ "license": "MIT",
11
+ "scripts": {},
12
+ "keywords": [
13
+ "chatbot",
14
+ "koishi",
15
+ "plugin",
16
+ "roulette",
17
+ "lili",
18
+ "丽丽",
19
+ "imitation",
20
+ "dedup"
21
+ ],
22
+ "peerDependencies": {
23
+ "koishi": "^4.18.7"
24
+ },
25
+ "koishi": {
26
+ "description": {
27
+ "zh": "丽丽Hub — 丽丽轮盘、丽丽喝酒、模仿群友说话、合并消息查重",
28
+ "en": "Lili Hub — Russian roulette, drinking games, speech imitation, and message dedup"
29
+ },
30
+ "service": {
31
+ "required": ["database"]
32
+ }
33
+ }
34
+ }
package/readme.md ADDED
@@ -0,0 +1,5 @@
1
+ # koishi-plugin-lili-hub
2
+
3
+ [![npm](https://img.shields.io/npm/v/koishi-plugin-lili-hub?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-lili-hub)
4
+
5
+