koishi-plugin-group-control 0.2.5 → 0.2.7-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/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-group-control?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-group-control)
4
4
 
5
- Koishi群聊自管理插件,支持自定义机器人进群消息、被踢出自动拉黑、刷屏自动屏蔽功能和主动退群指令。
5
+ Koishi 插件,一个多功能的群聊自管理工具。支持被踢出自动拉黑、刷屏自动屏蔽、开关控制等功能。(仅支持 OneBot 适配器)
6
6
 
7
- #### 仅支持Onebot协议
7
+ #### 仅支持 OneBot 协议
8
8
 
9
- > 使用 Qwen3-Coder & Gemini-3-Pro-Preview 协助完成
9
+ > 使用 Qwen3-Coder & Gemini-3.1-Pro-Preview 协助完成
@@ -0,0 +1,48 @@
1
+ import { Schema } from 'koishi';
2
+ export interface GroupConfig {
3
+ welcomeMessage: string;
4
+ blacklistMessage: string;
5
+ quitMessage: string;
6
+ enableBlacklist: boolean;
7
+ quitCommandEnabled: boolean;
8
+ quitCommandAuthority: number;
9
+ notifyAdminOnKick: boolean;
10
+ kickNotificationMessage: string;
11
+ smallGroupAutoQuit: boolean;
12
+ smallGroupThreshold: number;
13
+ smallGroupQuitMessage: string;
14
+ smallGroupNotifyAdmin: boolean;
15
+ }
16
+ export interface GroupInviteConfig {
17
+ enabled: boolean;
18
+ adminQQs: string[];
19
+ notificationGroupId: string;
20
+ inviteWaitMessage: string;
21
+ inviteRequestMessage: string;
22
+ autoApprove: boolean;
23
+ showDetailedLog: boolean;
24
+ }
25
+ export interface FrequencyConfig {
26
+ enabled: boolean;
27
+ limit: number;
28
+ window: number;
29
+ warnDelay: number;
30
+ blockDur: number;
31
+ warnMsg: string;
32
+ blockMsg: string;
33
+ blockedMsg: string;
34
+ whitelist: string[];
35
+ }
36
+ export interface BotSwitchConfig {
37
+ enabled: boolean;
38
+ defaultState: boolean;
39
+ disabledMessage: string;
40
+ toggleAuthority: number;
41
+ }
42
+ export interface Config {
43
+ basic: GroupConfig;
44
+ frequency: FrequencyConfig;
45
+ invite: GroupInviteConfig;
46
+ botSwitch: BotSwitchConfig;
47
+ }
48
+ export declare const Config: Schema<Config>;
@@ -0,0 +1,40 @@
1
+ import { Context } from 'koishi';
2
+ export interface BlacklistedGuild {
3
+ platform: string;
4
+ guildId: string;
5
+ timestamp: number;
6
+ reason: string;
7
+ }
8
+ export interface CommandFrequencyRecord {
9
+ platform: string;
10
+ guildId: string;
11
+ commandCount: number;
12
+ lastCommandTime: number;
13
+ warningSent: boolean;
14
+ blockExpiryTime: number;
15
+ firstWarningTime: number;
16
+ }
17
+ export interface GroupBotStatus {
18
+ platform: string;
19
+ guildId: string;
20
+ botEnabled: boolean;
21
+ }
22
+ declare module 'koishi' {
23
+ interface Tables {
24
+ blacklisted_guild: BlacklistedGuild;
25
+ command_frequency_record: CommandFrequencyRecord;
26
+ group_bot_status: GroupBotStatus;
27
+ }
28
+ }
29
+ export declare const name = "group-control-database";
30
+ export declare function apply(ctx: Context): void;
31
+ export declare const BLACKLIST_PLATFORM = "onebot";
32
+ export declare function getBlacklistedGuild(ctx: Context, guildId: string): Promise<BlacklistedGuild[]>;
33
+ export declare function removeBlacklistedGuild(ctx: Context, guildId: string): Promise<import("minato").Driver.WriteResult>;
34
+ export declare function createBlacklistedGuild(ctx: Context, guildId: string, reason: string): Promise<BlacklistedGuild>;
35
+ export declare function getAllBlacklistedGuilds(ctx: Context): Promise<BlacklistedGuild[]>;
36
+ export declare function clearBlacklistedGuilds(ctx: Context): Promise<import("minato").Driver.WriteResult>;
37
+ export declare function getCommandFrequencyRecord(ctx: Context, platform: string, guildId: string): Promise<CommandFrequencyRecord>;
38
+ export declare function updateCommandFrequencyRecord(ctx: Context, platform: string, guildId: string, data: Partial<CommandFrequencyRecord>): Promise<void>;
39
+ export declare function getGroupBotStatus(ctx: Context, platform: string, guildId: string): Promise<GroupBotStatus | null>;
40
+ export declare function setGroupBotStatus(ctx: Context, platform: string, guildId: string, botEnabled: boolean): Promise<void>;
package/lib/index.d.ts CHANGED
@@ -1,75 +1,5 @@
1
- import { Context, Schema } from 'koishi';
1
+ import { Context } from 'koishi';
2
+ import { Config } from './config';
3
+ export * from './config';
2
4
  export declare const name = "group-control";
3
- export interface Config {
4
- welcomeMessage: string;
5
- blacklistMessage: string;
6
- quitMessage: string;
7
- enableBlacklist: boolean;
8
- quitCommandEnabled: boolean;
9
- quitCommandAuthority: number;
10
- enabled: boolean;
11
- limit: number;
12
- window: number;
13
- warnDelay: number;
14
- blockDur: number;
15
- warnMsg: string;
16
- blockMsg: string;
17
- blockedMsg: string;
18
- whitelist: string[];
19
- }
20
- export interface BlacklistedGuild {
21
- platform: string;
22
- guildId: string;
23
- timestamp: number;
24
- reason: string;
25
- }
26
- export interface CommandFrequencyRecord {
27
- platform: string;
28
- guildId: string;
29
- commandCount: number;
30
- lastCommandTime: number;
31
- warningSent: boolean;
32
- blockExpiryTime: number;
33
- firstWarningTime: number;
34
- }
35
- declare module 'koishi' {
36
- interface Tables {
37
- blacklisted_guild: BlacklistedGuild;
38
- command_frequency_record: CommandFrequencyRecord;
39
- }
40
- }
41
- export interface GroupConfig {
42
- welcomeMessage: string;
43
- blacklistMessage: string;
44
- quitMessage: string;
45
- enableBlacklist: boolean;
46
- quitCommandEnabled: boolean;
47
- quitCommandAuthority: number;
48
- }
49
- export interface GroupInviteConfig {
50
- enabled: boolean;
51
- adminQQs: string[];
52
- notificationGroupId: string;
53
- inviteWaitMessage: string;
54
- inviteRequestMessage: string;
55
- autoApprove: boolean;
56
- showDetailedLog: boolean;
57
- }
58
- export interface FrequencyConfig {
59
- enabled: boolean;
60
- limit: number;
61
- window: number;
62
- warnDelay: number;
63
- blockDur: number;
64
- warnMsg: string;
65
- blockMsg: string;
66
- blockedMsg: string;
67
- whitelist: string[];
68
- }
69
- export interface Config {
70
- basic: GroupConfig;
71
- frequency: FrequencyConfig;
72
- invite: GroupInviteConfig;
73
- }
74
- export declare const Config: Schema<Config>;
75
5
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -4,8 +4,8 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
5
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
6
  var __export = (target, all) => {
7
- for (var name2 in all)
8
- __defProp(target, name2, { get: all[name2], enumerable: true });
7
+ for (var name8 in all)
8
+ __defProp(target, name8, { get: all[name8], enumerable: true });
9
9
  };
10
10
  var __copyProps = (to, from, except, desc) => {
11
11
  if (from && typeof from === "object" || typeof from === "function") {
@@ -21,58 +21,51 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Config: () => Config,
24
- apply: () => apply,
25
- name: () => name
24
+ apply: () => apply7,
25
+ name: () => name7
26
26
  });
27
27
  module.exports = __toCommonJS(src_exports);
28
- var import_koishi = require("koishi");
29
- var name = "group-control";
30
- var Config = import_koishi.Schema.intersect([
31
- import_koishi.Schema.object({
32
- basic: import_koishi.Schema.object({
33
- welcomeMessage: import_koishi.Schema.string().default("你好,我是机器人。感谢邀请我加入!").description("机器人加入群聊时发送的欢迎消息"),
34
- blacklistMessage: import_koishi.Schema.string().default("此群聊已被拉黑,机器人将自动退出,请联系管理员移出黑名单。").description("被拉入黑名单群后在群内发送的提示"),
35
- quitMessage: import_koishi.Schema.string().default("收到来自{userId}的指令,即将退出群聊。").description("用户发送quit指令后在群内发送的提示,支持变量{userId}"),
36
- enableBlacklist: import_koishi.Schema.boolean().default(true).description('启用"被踢出自动拉黑"功能'),
37
- quitCommandEnabled: import_koishi.Schema.boolean().default(true).description("启用quit"),
38
- quitCommandAuthority: import_koishi.Schema.number().default(3).description("quit指令所需权限")
39
- }).description("基础群组管理")
40
- }),
41
- import_koishi.Schema.object({
42
- frequency: import_koishi.Schema.object({
43
- enabled: import_koishi.Schema.boolean().default(false).description("启用频率控制(对所有指令生效)"),
44
- limit: import_koishi.Schema.number().default(5).description("时间窗口内允许的最大指令次数"),
45
- window: import_koishi.Schema.number().default(60).description("频率检测时间窗口(秒)"),
46
- warnDelay: import_koishi.Schema.number().default(30).description("发出警告后,再次触发的时间阈值(秒),在此时间内再次触发则进入屏蔽状态"),
47
- blockDur: import_koishi.Schema.number().default(300).description("触发频率限制后屏蔽的时长(秒)"),
48
- warnMsg: import_koishi.Schema.string().default("指令频率过高,请慢一点~").description("频率过高时发送的警告消息"),
49
- blockMsg: import_koishi.Schema.string().default("指令频率过高,本群指令已被禁用 {duration} 秒。").description("触发频率限制后发送的屏蔽通知消息,支持变量{duration}"),
50
- blockedMsg: import_koishi.Schema.string().default("指令暂时被禁用,还有 {time} 秒解禁。").description("屏蔽期间接收到指令时的提示消息,支持变量{time}"),
51
- whitelist: import_koishi.Schema.array(String).default([]).description("频率控制白名单群号列表,白名单内的群聊不受频率限制")
52
- }).description("指令频率控制")
53
- }),
54
- import_koishi.Schema.object({
55
- invite: import_koishi.Schema.object({
56
- enabled: import_koishi.Schema.boolean().default(false).description("启用群聊邀请审核功能"),
57
- adminQQs: import_koishi.Schema.array(String).default([]).description("管理员QQ号列表(用于权限验证)"),
58
- notificationGroupId: import_koishi.Schema.string().description("通知群号(可选:若填写,邀请请求将发送到此群;若不填,则发送私聊给管理员)"),
59
- inviteWaitMessage: import_koishi.Schema.string().default("已收到您的群聊邀请,正在等待管理员审核,请耐心等待。").description("发送给邀请者的等待审核提示消息"),
60
- inviteRequestMessage: import_koishi.Schema.string().default('收到新的群聊邀请请求:\n群名称:{groupName}\n群号:{groupId}\n邀请者:{userName} (QQ: {userId})\n\n请管理员引用此消息回复"同意"或"拒绝"。').description("发送给管理员的邀请请求消息模板,支持变量{groupName}, {groupId}, {userName}, {userId}"),
61
- autoApprove: import_koishi.Schema.boolean().default(false).description("是否自动同意邀请(仅在没有指定管理员时)"),
62
- showDetailedLog: import_koishi.Schema.boolean().default(false).description("是否显示详细日志")
63
- }).description("群聊邀请审核")
64
- })
65
- ]);
66
- function isBlacklistEnabled(config) {
67
- if (!config.enableBlacklist) return "黑名单功能未启用。";
68
- return null;
69
- }
70
- __name(isBlacklistEnabled, "isBlacklistEnabled");
71
- function parseGuildId(input) {
72
- const match = input.trim().match(/^onebot:(\d+)$/);
73
- return match ? match[1] : /^\d+$/.test(input.trim()) ? input.trim() : null;
28
+
29
+ // src/database.ts
30
+ var database_exports = {};
31
+ __export(database_exports, {
32
+ BLACKLIST_PLATFORM: () => BLACKLIST_PLATFORM,
33
+ apply: () => apply,
34
+ clearBlacklistedGuilds: () => clearBlacklistedGuilds,
35
+ createBlacklistedGuild: () => createBlacklistedGuild,
36
+ getAllBlacklistedGuilds: () => getAllBlacklistedGuilds,
37
+ getBlacklistedGuild: () => getBlacklistedGuild,
38
+ getCommandFrequencyRecord: () => getCommandFrequencyRecord,
39
+ getGroupBotStatus: () => getGroupBotStatus,
40
+ name: () => name,
41
+ removeBlacklistedGuild: () => removeBlacklistedGuild,
42
+ setGroupBotStatus: () => setGroupBotStatus,
43
+ updateCommandFrequencyRecord: () => updateCommandFrequencyRecord
44
+ });
45
+ var name = "group-control-database";
46
+ function apply(ctx) {
47
+ ctx.model.extend("blacklisted_guild", {
48
+ platform: "string",
49
+ guildId: "string",
50
+ timestamp: "integer",
51
+ reason: "string"
52
+ }, { primary: ["platform", "guildId"] });
53
+ ctx.model.extend("command_frequency_record", {
54
+ platform: "string",
55
+ guildId: "string",
56
+ commandCount: "integer",
57
+ lastCommandTime: "integer",
58
+ warningSent: "boolean",
59
+ blockExpiryTime: "integer",
60
+ firstWarningTime: "integer"
61
+ }, { primary: ["platform", "guildId"] });
62
+ ctx.model.extend("group_bot_status", {
63
+ platform: "string",
64
+ guildId: "string",
65
+ botEnabled: "boolean"
66
+ }, { primary: ["platform", "guildId"] });
74
67
  }
75
- __name(parseGuildId, "parseGuildId");
68
+ __name(apply, "apply");
76
69
  var BLACKLIST_PLATFORM = "onebot";
77
70
  async function getBlacklistedGuild(ctx, guildId) {
78
71
  return await ctx.model.get("blacklisted_guild", { platform: BLACKLIST_PLATFORM, guildId });
@@ -99,10 +92,6 @@ async function clearBlacklistedGuilds(ctx) {
99
92
  return await ctx.model.remove("blacklisted_guild", { platform: BLACKLIST_PLATFORM });
100
93
  }
101
94
  __name(clearBlacklistedGuilds, "clearBlacklistedGuilds");
102
- function formatDate(timestamp) {
103
- return new Date(timestamp * 1e3).toLocaleString();
104
- }
105
- __name(formatDate, "formatDate");
106
95
  async function getCommandFrequencyRecord(ctx, platform, guildId) {
107
96
  const records = await ctx.model.get("command_frequency_record", { platform, guildId });
108
97
  return records.length > 0 ? records[0] : null;
@@ -116,311 +105,452 @@ async function updateCommandFrequencyRecord(ctx, platform, guildId, data) {
116
105
  }]);
117
106
  }
118
107
  __name(updateCommandFrequencyRecord, "updateCommandFrequencyRecord");
119
- function isCurrentlyBlocked(record) {
120
- if (!record || !record.blockExpiryTime) return false;
121
- return Date.now() < record.blockExpiryTime * 1e3;
108
+ async function getGroupBotStatus(ctx, platform, guildId) {
109
+ const records = await ctx.model.get("group_bot_status", { platform, guildId });
110
+ return records.length > 0 ? records[0] : null;
122
111
  }
123
- __name(isCurrentlyBlocked, "isCurrentlyBlocked");
124
- function apply(ctx, config) {
125
- const quittingGuilds = /* @__PURE__ */ new Set();
126
- ctx.model.extend("blacklisted_guild", {
127
- platform: "string",
128
- guildId: "string",
129
- timestamp: "integer",
130
- reason: "string"
131
- }, { primary: ["platform", "guildId"] });
132
- ctx.model.extend("command_frequency_record", {
133
- platform: "string",
134
- guildId: "string",
135
- commandCount: "integer",
136
- lastCommandTime: "integer",
137
- warningSent: "boolean",
138
- blockExpiryTime: "integer",
139
- firstWarningTime: "integer"
140
- }, { primary: ["platform", "guildId"] });
141
- if (config.invite.enabled) {
142
- const pendingInvites = /* @__PURE__ */ new Map();
143
- ctx.on("guild-request", async (session) => {
144
- const raw = session.original || session.raw || session.event?._data || {};
145
- const flag = raw.flag || session.flag || session.messageId;
146
- const rawUserId = raw.user_id ? String(raw.user_id) : session.userId;
147
- const rawGroupId = raw.group_id ? String(raw.group_id) : session.guildId;
148
- const { platform } = session;
149
- if (!flag && config.invite.showDetailedLog) {
150
- console.warn("Group Control: 未能提取到邀请 flag,可能导致无法处理邀请。Raw event:", JSON.stringify(raw));
112
+ __name(getGroupBotStatus, "getGroupBotStatus");
113
+ async function setGroupBotStatus(ctx, platform, guildId, botEnabled) {
114
+ await ctx.model.upsert("group_bot_status", [{ platform, guildId, botEnabled }]);
115
+ }
116
+ __name(setGroupBotStatus, "setGroupBotStatus");
117
+
118
+ // src/modules/basic.ts
119
+ var basic_exports = {};
120
+ __export(basic_exports, {
121
+ apply: () => apply2,
122
+ name: () => name2
123
+ });
124
+
125
+ // src/utils.ts
126
+ function isBlacklistEnabled(config) {
127
+ if (!config.enableBlacklist) return "黑名单功能未启用。";
128
+ return null;
129
+ }
130
+ __name(isBlacklistEnabled, "isBlacklistEnabled");
131
+ function parseGuildId(input) {
132
+ const match = input.trim().match(/^onebot:(\d+)$/);
133
+ return match ? match[1] : /^\d+$/.test(input.trim()) ? input.trim() : null;
134
+ }
135
+ __name(parseGuildId, "parseGuildId");
136
+ function formatDate(timestamp) {
137
+ return new Date(timestamp * 1e3).toLocaleString();
138
+ }
139
+ __name(formatDate, "formatDate");
140
+ async function notifyAdmins(bot, config, message) {
141
+ if (config.invite.notificationGroupId) {
142
+ try {
143
+ await bot.sendMessage(config.invite.notificationGroupId, message);
144
+ return;
145
+ } catch (error) {
146
+ console.error(`发送通知到通知群 ${config.invite.notificationGroupId} 失败:`, error);
147
+ }
148
+ }
149
+ if (config.invite.adminQQs?.length > 0) {
150
+ for (const adminQQ of config.invite.adminQQs) {
151
+ try {
152
+ await bot.sendPrivateMessage(adminQQ, message);
153
+ } catch (error) {
154
+ console.error(`发送通知给管理员 ${adminQQ} 失败:`, error);
151
155
  }
152
- if (config.invite.showDetailedLog) {
153
- console.log(`收到群邀请事件 - 原始数据: UserID=${raw.user_id}, GroupID=${raw.group_id}, Flag=${flag}`);
156
+ }
157
+ }
158
+ }
159
+ __name(notifyAdmins, "notifyAdmins");
160
+
161
+ // src/modules/basic.ts
162
+ var name2 = "group-control-basic";
163
+ function apply2(ctx, config) {
164
+ const quittingGuilds = /* @__PURE__ */ new Set();
165
+ ctx.on("guild-added", async (session) => {
166
+ const { guildId, platform } = session;
167
+ if (config.basic.enableBlacklist) {
168
+ const [blacklisted] = await ctx.model.get("blacklisted_guild", { platform, guildId });
169
+ if (blacklisted) {
170
+ try {
171
+ await session.bot.sendMessage(guildId, config.basic.blacklistMessage, platform);
172
+ } catch (e) {
173
+ }
174
+ quittingGuilds.add(`${platform}:${guildId}`);
175
+ try {
176
+ await session.bot.internal.setGroupLeave(parseInt(guildId));
177
+ } catch (e) {
178
+ }
179
+ return;
154
180
  }
155
- let userName = rawUserId;
181
+ }
182
+ if (config.basic.smallGroupAutoQuit) {
156
183
  try {
157
- const userInfo = await session.bot.getUser(rawUserId);
158
- userName = userInfo?.nickname || userInfo?.name || rawUserId;
184
+ const guildInfo = await session.bot.getGuild(guildId);
185
+ const memberCount = guildInfo?.member_count || guildInfo?.memberCount || 0;
186
+ if (memberCount > 0 && memberCount <= config.basic.smallGroupThreshold) {
187
+ const quitMsg = config.basic.smallGroupQuitMessage.replace("{memberCount}", memberCount.toString()).replace("{threshold}", config.basic.smallGroupThreshold.toString());
188
+ try {
189
+ await session.bot.sendMessage(guildId, quitMsg, platform);
190
+ } catch (e) {
191
+ }
192
+ if (config.basic.smallGroupNotifyAdmin) {
193
+ const adminMsg = `小群自动退群
194
+ 群号:${guildId}
195
+ 群成员数:${memberCount}人(阈值:${config.basic.smallGroupThreshold}人)
196
+ 机器人已自动退出该群。`;
197
+ await notifyAdmins(session.bot, config, adminMsg);
198
+ }
199
+ quittingGuilds.add(`${platform}:${guildId}`);
200
+ try {
201
+ await session.bot.internal.setGroupLeave(parseInt(guildId));
202
+ } catch (e) {
203
+ console.error(`小群自动退群失败 (群号: ${guildId}):`, e);
204
+ quittingGuilds.delete(`${platform}:${guildId}`);
205
+ }
206
+ return;
207
+ }
159
208
  } catch (error) {
160
- console.error("获取用户信息失败:", error);
209
+ console.error(`获取群信息失败 (群号: ${guildId}):`, error);
161
210
  }
162
- let groupName = rawGroupId;
211
+ }
212
+ if (config.basic.welcomeMessage) {
163
213
  try {
164
- const guildInfo = await session.bot.getGuild(rawGroupId);
165
- groupName = guildInfo?.name || guildInfo?.group_name || rawGroupId;
166
- } catch (error) {
167
- console.error("获取群信息失败:", error);
214
+ await session.bot.sendMessage(guildId, config.basic.welcomeMessage, platform);
215
+ } catch (e) {
216
+ }
217
+ }
218
+ });
219
+ ctx.on("guild-removed", async (session) => {
220
+ const { guildId, platform } = session;
221
+ const quittingKey = `${platform}:${guildId}`;
222
+ if (quittingGuilds.has(quittingKey)) {
223
+ quittingGuilds.delete(quittingKey);
224
+ return;
225
+ }
226
+ if (config.basic.enableBlacklist) {
227
+ await ctx.model.upsert("blacklisted_guild", [{
228
+ platform,
229
+ guildId,
230
+ timestamp: Math.floor(Date.now() / 1e3),
231
+ reason: "kicked"
232
+ }]);
233
+ }
234
+ if (config.basic.notifyAdminOnKick) {
235
+ const kickMsg = config.basic.kickNotificationMessage.replace("{groupId}", guildId);
236
+ await notifyAdmins(session.bot, config, kickMsg);
237
+ }
238
+ });
239
+ if (config.basic.quitCommandEnabled) {
240
+ ctx.command("quit", "让机器人主动退出当前群聊", { authority: config.basic.quitCommandAuthority }).action(async ({ session }) => {
241
+ if (!session.guildId) return "quit 指令只能在群聊中使用。";
242
+ const { guildId, platform, userId } = session;
243
+ quittingGuilds.add(`${platform}:${guildId}`);
244
+ try {
245
+ await session.bot.sendMessage(session.guildId, config.basic.quitMessage.replace("{userId}", userId), platform);
246
+ } catch (e) {
168
247
  }
169
248
  try {
170
- const waitMessage = config.invite.inviteWaitMessage.replace("{groupName}", groupName).replace("{groupId}", rawGroupId).replace("{userName}", userName).replace("{userId}", rawUserId);
171
- await session.bot.sendPrivateMessage(rawUserId, waitMessage);
172
- } catch (error) {
173
- console.error(`发送等待审核提示给 ${rawUserId} 失败:`, error);
249
+ await session.bot.internal.setGroupLeave(parseInt(guildId));
250
+ } catch (e) {
251
+ quittingGuilds.delete(`${platform}:${guildId}`);
252
+ return `退出失败: ${e.message}`;
174
253
  }
175
- if (!config.invite.adminQQs || config.invite.adminQQs.length === 0) {
176
- if (config.invite.autoApprove) {
177
- try {
178
- await session.bot.internal.setGroupAddRequest({
179
- flag,
180
- sub_type: "invite",
181
- approve: true,
182
- reason: ""
183
- });
184
- if (config.invite.showDetailedLog) {
185
- console.log(`自动同意群聊邀请: 群号 ${rawGroupId}, 邀请者 ${rawUserId}`);
186
- }
187
- } catch (error) {
188
- console.error("自动同意群聊邀请失败:", error);
254
+ return "";
255
+ });
256
+ }
257
+ }
258
+ __name(apply2, "apply");
259
+
260
+ // src/modules/invite.ts
261
+ var invite_exports = {};
262
+ __export(invite_exports, {
263
+ apply: () => apply3,
264
+ name: () => name3
265
+ });
266
+ var name3 = "group-control-invite";
267
+ function apply3(ctx, config) {
268
+ if (!config.invite.enabled) return;
269
+ const pendingInvites = /* @__PURE__ */ new Map();
270
+ ctx.on("guild-request", async (session) => {
271
+ const raw = session.original || session.raw || session.event?._data || {};
272
+ const flag = raw.flag || session.flag || session.messageId;
273
+ const rawUserId = raw.user_id ? String(raw.user_id) : session.userId;
274
+ const rawGroupId = raw.group_id ? String(raw.group_id) : session.guildId;
275
+ const { platform } = session;
276
+ if (!flag && config.invite.showDetailedLog) {
277
+ console.warn("未能提取到邀请 flag,可能导致无法处理邀请。Raw event:", JSON.stringify(raw));
278
+ }
279
+ if (config.invite.showDetailedLog) {
280
+ console.log(`收到群邀请事件 - 原始数据: UserID=${raw.user_id}, GroupID=${raw.group_id}, Flag=${flag}`);
281
+ }
282
+ let userName = rawUserId;
283
+ try {
284
+ const userInfo = await session.bot.getUser(rawUserId);
285
+ userName = userInfo?.nickname || userInfo?.name || rawUserId;
286
+ } catch (error) {
287
+ console.error("获取用户信息失败:", error);
288
+ }
289
+ let groupName = rawGroupId;
290
+ try {
291
+ const guildInfo = await session.bot.getGuild(rawGroupId);
292
+ groupName = guildInfo?.name || guildInfo?.group_name || rawGroupId;
293
+ } catch (error) {
294
+ console.error("获取群信息失败:", error);
295
+ }
296
+ try {
297
+ const waitMessage = config.invite.inviteWaitMessage.replace("{groupName}", groupName).replace("{groupId}", rawGroupId).replace("{userName}", userName).replace("{userId}", rawUserId);
298
+ await session.bot.sendPrivateMessage(rawUserId, waitMessage);
299
+ } catch (error) {
300
+ console.error(`发送等待审核提示给 ${rawUserId} 失败:`, error);
301
+ }
302
+ if (!config.invite.adminQQs || config.invite.adminQQs.length === 0) {
303
+ if (config.invite.autoApprove) {
304
+ try {
305
+ await session.bot.internal.setGroupAddRequest({
306
+ flag,
307
+ sub_type: "invite",
308
+ approve: true,
309
+ reason: ""
310
+ });
311
+ if (config.invite.showDetailedLog) {
312
+ console.log(`自动同意群聊邀请: 群号 ${rawGroupId}, 邀请者 ${rawUserId}`);
189
313
  }
314
+ } catch (error) {
315
+ console.error("自动同意群聊邀请失败:", error);
190
316
  }
191
- return;
192
317
  }
193
- const inviteId = `${rawGroupId}_${rawUserId}_${Date.now()}`;
194
- pendingInvites.set(inviteId, {
195
- groupId: rawGroupId,
196
- userId: rawUserId,
197
- userName,
198
- time: Date.now(),
199
- flag
200
- });
201
- const requestMessage = config.invite.inviteRequestMessage.replace("{groupName}", groupName).replace("{groupId}", rawGroupId).replace("{userName}", userName).replace("{userId}", rawUserId);
202
- let requestSent = false;
203
- if (config.invite.notificationGroupId) {
318
+ return;
319
+ }
320
+ const inviteId = `${rawGroupId}_${rawUserId}_${Date.now()}`;
321
+ pendingInvites.set(inviteId, {
322
+ groupId: rawGroupId,
323
+ userId: rawUserId,
324
+ userName,
325
+ time: Date.now(),
326
+ flag
327
+ });
328
+ const requestMessage = config.invite.inviteRequestMessage.replace("{groupName}", groupName).replace("{groupId}", rawGroupId).replace("{userName}", userName).replace("{userId}", rawUserId);
329
+ let requestSent = false;
330
+ if (config.invite.notificationGroupId) {
331
+ try {
332
+ await session.bot.sendMessage(config.invite.notificationGroupId, requestMessage);
333
+ requestSent = true;
334
+ if (config.invite.showDetailedLog) {
335
+ console.log(`发送邀请请求到通知群 ${config.invite.notificationGroupId}`);
336
+ }
337
+ } catch (error) {
338
+ console.error(`发送邀请请求到通知群 ${config.invite.notificationGroupId} 失败:`, error);
339
+ }
340
+ }
341
+ if (!config.invite.notificationGroupId) {
342
+ for (const adminQQ of config.invite.adminQQs) {
204
343
  try {
205
- await session.bot.sendMessage(config.invite.notificationGroupId, requestMessage);
344
+ await session.bot.sendPrivateMessage(adminQQ, requestMessage);
206
345
  requestSent = true;
207
346
  if (config.invite.showDetailedLog) {
208
- console.log(`发送邀请请求到通知群 ${config.invite.notificationGroupId}`);
347
+ console.log(`发送邀请请求给管理员 ${adminQQ}`);
209
348
  }
210
349
  } catch (error) {
211
- console.error(`发送邀请请求到通知群 ${config.invite.notificationGroupId} 失败:`, error);
350
+ console.error(`发送邀请请求给管理员 ${adminQQ} 失败:`, error);
212
351
  }
213
352
  }
214
- if (!config.invite.notificationGroupId) {
215
- for (const adminQQ of config.invite.adminQQs) {
216
- try {
217
- await session.bot.sendPrivateMessage(adminQQ, requestMessage);
218
- requestSent = true;
219
- if (config.invite.showDetailedLog) {
220
- console.log(`发送邀请请求给管理员 ${adminQQ}`);
353
+ }
354
+ if (!requestSent && config.invite.showDetailedLog) {
355
+ console.warn("群邀请请求发送失败:未配置通知群且管理员私聊发送失败");
356
+ }
357
+ });
358
+ ctx.on("message", async (session) => {
359
+ const { userId, guildId } = session;
360
+ if (!config.invite.adminQQs.includes(userId)) return;
361
+ const isNotificationGroup = config.invite.notificationGroupId && guildId === config.invite.notificationGroupId;
362
+ const isPrivate = !guildId;
363
+ if (!isNotificationGroup && !isPrivate && config.invite.notificationGroupId) return;
364
+ const hasQuote = session.elements.some((element) => element.type === "quote");
365
+ if (!hasQuote) return;
366
+ const textContent = session.elements.filter((element) => element.type === "text").map((element) => element.attrs?.content || "").join("").trim();
367
+ if (config.invite.showDetailedLog) {
368
+ console.log(`管理员审核回复 - 原始content: "${session.content}", 提取文本: "${textContent}"`);
369
+ }
370
+ if (!["同意", "拒绝", "accept", "reject"].includes(textContent)) return;
371
+ const quoteElement = session.elements.find((element) => element.type === "quote");
372
+ if (!quoteElement) return;
373
+ let quoteMessageContent = "";
374
+ if (session.quote?.content) {
375
+ quoteMessageContent = session.quote.content;
376
+ }
377
+ if (!quoteMessageContent) {
378
+ quoteMessageContent = quoteElement.attrs?.content || quoteElement.attrs?.text || "";
379
+ }
380
+ if (!quoteMessageContent && quoteElement.children?.length > 0) {
381
+ quoteMessageContent = quoteElement.children.filter((child) => child.type === "text").map((child) => child.attrs?.content || "").join("");
382
+ }
383
+ if (!quoteMessageContent) {
384
+ const quoteId = quoteElement.attrs?.id || session.quote?.id;
385
+ if (quoteId) {
386
+ try {
387
+ const channelId = guildId || session.channelId;
388
+ if (channelId) {
389
+ const originalMsg = await session.bot.getMessage(channelId, quoteId);
390
+ if (originalMsg?.content) {
391
+ quoteMessageContent = originalMsg.content;
221
392
  }
222
- } catch (error) {
223
- console.error(`发送邀请请求给管理员 ${adminQQ} 失败:`, error);
393
+ }
394
+ } catch (error) {
395
+ if (config.invite.showDetailedLog) {
396
+ console.error("通过消息ID获取引用消息内容失败:", error);
224
397
  }
225
398
  }
226
399
  }
227
- if (!requestSent && config.invite.showDetailedLog) {
228
- console.warn("群邀请请求发送失败:未配置通知群且管理员私聊发送失败");
400
+ }
401
+ if (config.invite.showDetailedLog) {
402
+ console.log(`引用消息内容: "${quoteMessageContent}"`);
403
+ }
404
+ const groupIdMatch = quoteMessageContent.match(/群号[::]\s*(\d+)/i);
405
+ const userIdMatch = quoteMessageContent.match(/QQ[::]\s*(\d+)/i);
406
+ if (groupIdMatch && userIdMatch) {
407
+ const extractedGroupId = groupIdMatch[1];
408
+ const extractedUserId = userIdMatch[1];
409
+ if (config.invite.showDetailedLog) {
410
+ console.log(`提取到群号: ${extractedGroupId}, QQ: ${extractedUserId}`);
229
411
  }
230
- });
231
- ctx.on("message", async (session) => {
232
- const { userId, content, guildId } = session;
233
- if (!config.invite.adminQQs.includes(userId)) return;
234
- const isNotificationGroup = config.invite.notificationGroupId && guildId === config.invite.notificationGroupId;
235
- const isPrivate = !guildId;
236
- if (!isNotificationGroup && !isPrivate && config.invite.notificationGroupId) return;
237
- const hasQuote = session.elements.some((element) => element.type === "quote");
238
- if (!hasQuote) return;
239
- const trimmedContent = content.trim();
240
- if (!["同意", "拒绝", "accept", "reject"].includes(trimmedContent)) return;
241
- const quoteElement = session.elements.find((element) => element.type === "quote");
242
- if (!quoteElement) return;
243
- const quoteMessageContent = quoteElement.attrs.content || quoteElement.attrs.text || "";
244
- const groupIdMatch = quoteMessageContent.match(/群号:(\d+)/i);
245
- const userIdMatch = quoteMessageContent.match(/QQ:\s*(\d+)/i);
246
- if (groupIdMatch && userIdMatch) {
247
- const extractedGroupId = groupIdMatch[1];
248
- const extractedUserId = userIdMatch[1];
249
- let targetInviteId = null;
250
- for (const [inviteId, inviteData] of pendingInvites) {
251
- if (inviteData.groupId === extractedGroupId && inviteData.userId === extractedUserId) {
252
- targetInviteId = inviteId;
253
- break;
254
- }
412
+ let targetInviteId = null;
413
+ for (const [inviteId, inviteData] of pendingInvites) {
414
+ if (inviteData.groupId === extractedGroupId && inviteData.userId === extractedUserId) {
415
+ targetInviteId = inviteId;
416
+ break;
255
417
  }
256
- if (targetInviteId) {
257
- const inviteData = pendingInvites.get(targetInviteId);
258
- if (inviteData) {
259
- if (trimmedContent === "同意" || trimmedContent === "accept") {
418
+ }
419
+ if (targetInviteId) {
420
+ const inviteData = pendingInvites.get(targetInviteId);
421
+ if (inviteData) {
422
+ if (textContent === "同意" || textContent === "accept") {
423
+ try {
424
+ await session.bot.internal.setGroupAddRequest({
425
+ flag: inviteData.flag,
426
+ sub_type: "invite",
427
+ approve: true,
428
+ reason: ""
429
+ });
430
+ await session.send(`已同意加入群 ${inviteData.groupId}`);
260
431
  try {
261
- await session.bot.internal.setGroupAddRequest({
262
- flag: inviteData.flag,
263
- sub_type: "invite",
264
- approve: true,
265
- reason: ""
266
- });
267
- await session.send(`已同意加入群 ${inviteData.groupId}`);
268
- try {
269
- await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请已通过管理员审核,机器人已加入群聊。`);
270
- } catch (error) {
271
- console.error("通知邀请者失败:", error);
272
- }
432
+ await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请已通过管理员审核,机器人已加入群聊。`);
273
433
  } catch (error) {
274
- console.error("处理同意邀请失败:", error);
275
- await session.send(`处理同意邀请失败: ${error.message}`);
434
+ console.error("通知邀请者失败:", error);
276
435
  }
277
- } else {
436
+ } catch (error) {
437
+ console.error("处理同意邀请失败:", error);
438
+ await session.send(`处理同意邀请失败: ${error.message}`);
439
+ }
440
+ } else {
441
+ try {
442
+ await session.bot.internal.setGroupAddRequest({
443
+ flag: inviteData.flag,
444
+ sub_type: "invite",
445
+ approve: false,
446
+ reason: "已拒绝"
447
+ });
448
+ await session.send(`已拒绝加入群 ${inviteData.groupId}`);
278
449
  try {
279
- await session.bot.internal.setGroupAddRequest({
280
- flag: inviteData.flag,
281
- sub_type: "invite",
282
- approve: false,
283
- reason: "已拒绝"
284
- });
285
- await session.send(`已拒绝加入群 ${inviteData.groupId}`);
286
- try {
287
- await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请未通过管理员审核,机器人将不会加入该群聊。`);
288
- } catch (error) {
289
- console.error("通知邀请者失败:", error);
290
- }
450
+ await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请未通过管理员审核,机器人将不会加入该群聊。`);
291
451
  } catch (error) {
292
- console.error("处理拒绝邀请失败:", error);
293
- await session.send(`处理拒绝邀请失败: ${error.message}`);
452
+ console.error("通知邀请者失败:", error);
294
453
  }
454
+ } catch (error) {
455
+ console.error("处理拒绝邀请失败:", error);
456
+ await session.send(`处理拒绝邀请失败: ${error.message}`);
295
457
  }
296
- pendingInvites.delete(targetInviteId);
297
458
  }
459
+ pendingInvites.delete(targetInviteId);
298
460
  }
461
+ } else if (config.invite.showDetailedLog) {
462
+ console.log(`未找到匹配的待处理邀请: 群号=${extractedGroupId}, QQ=${extractedUserId}`);
463
+ console.log(`当前待处理邀请列表:`, Array.from(pendingInvites.entries()));
299
464
  }
300
- });
301
- }
302
- ctx.on("guild-added", async (session) => {
303
- const { guildId, platform } = session;
304
- if (!config.basic.enableBlacklist) {
305
- if (config.basic.welcomeMessage) {
306
- try {
307
- await session.bot.sendMessage(guildId, config.basic.welcomeMessage, platform);
308
- } catch (e) {
309
- }
310
- }
311
- return;
465
+ } else if (config.invite.showDetailedLog) {
466
+ console.log(`无法从引用消息中提取群号或QQ号,引用内容: "${quoteMessageContent}"`);
312
467
  }
313
- const [blacklisted] = await ctx.model.get("blacklisted_guild", { platform, guildId });
314
- if (blacklisted) {
315
- try {
316
- await session.bot.sendMessage(guildId, config.basic.blacklistMessage, platform);
317
- } catch (e) {
318
- }
319
- try {
320
- await session.bot.internal.setGroupLeave(parseInt(guildId));
321
- } catch (e) {
468
+ });
469
+ }
470
+ __name(apply3, "apply");
471
+
472
+ // src/modules/frequency.ts
473
+ var frequency_exports = {};
474
+ __export(frequency_exports, {
475
+ apply: () => apply4,
476
+ name: () => name4
477
+ });
478
+ var name4 = "group-control-frequency";
479
+ function isCurrentlyBlocked(record) {
480
+ if (!record || !record.blockExpiryTime) return false;
481
+ return Date.now() < record.blockExpiryTime * 1e3;
482
+ }
483
+ __name(isCurrentlyBlocked, "isCurrentlyBlocked");
484
+ function apply4(ctx, config) {
485
+ if (!config.frequency.enabled) return;
486
+ ctx.on("command/before-execute", async (argv) => {
487
+ const session = argv.session;
488
+ if (!session.guildId || !config.frequency.enabled) return;
489
+ const { guildId, platform } = session;
490
+ if (config.frequency.whitelist && config.frequency.whitelist.includes(guildId)) return;
491
+ let record = await getCommandFrequencyRecord(ctx, platform, guildId);
492
+ const now = Math.floor(Date.now() / 1e3);
493
+ const windowStart = now - config.frequency.window;
494
+ if (record && record.lastCommandTime < windowStart) {
495
+ if (record.warningSent && record.firstWarningTime > 0 && now - record.firstWarningTime <= config.frequency.warnDelay) {
496
+ record.commandCount = 1;
497
+ record.lastCommandTime = now;
498
+ } else if (isCurrentlyBlocked(record) && Date.now() < record.blockExpiryTime * 1e3) {
499
+ } else {
500
+ record = { platform, guildId, commandCount: 1, lastCommandTime: now, warningSent: false, blockExpiryTime: 0, firstWarningTime: 0 };
322
501
  }
323
- return;
502
+ } else if (!record) {
503
+ record = { platform, guildId, commandCount: 1, lastCommandTime: now, warningSent: false, blockExpiryTime: 0, firstWarningTime: 0 };
504
+ } else {
505
+ record.commandCount += 1;
506
+ record.lastCommandTime = now;
324
507
  }
325
- if (config.basic.welcomeMessage) {
508
+ if (isCurrentlyBlocked(record)) {
326
509
  try {
327
- await session.bot.sendMessage(guildId, config.basic.welcomeMessage, platform);
510
+ const remainingTime = Math.ceil((record.blockExpiryTime * 1e3 - Date.now()) / 1e3);
511
+ await session.bot.sendMessage(guildId, config.frequency.blockedMsg.replace("{time}", remainingTime.toString()), platform);
328
512
  } catch (e) {
329
513
  }
514
+ throw new Error("Blocked");
330
515
  }
331
- });
332
- ctx.on("guild-removed", async (session) => {
333
- const { guildId, platform } = session;
334
- if (!config.basic.enableBlacklist) return;
335
- const quittingKey = `${platform}:${guildId}`;
336
- if (quittingGuilds.has(quittingKey)) {
337
- quittingGuilds.delete(quittingKey);
338
- return;
339
- }
340
- await ctx.model.upsert("blacklisted_guild", [{
341
- platform,
342
- guildId,
343
- timestamp: Math.floor(Date.now() / 1e3),
344
- reason: "kicked"
345
- }]);
346
- });
347
- if (config.frequency.enabled) {
348
- ctx.on("command/before-execute", async (argv) => {
349
- const session = argv.session;
350
- if (!session.guildId || !config.frequency.enabled) return;
351
- const { guildId, platform } = session;
352
- if (config.frequency.whitelist && config.frequency.whitelist.includes(guildId)) return;
353
- let record = await getCommandFrequencyRecord(ctx, platform, guildId);
354
- const now = Math.floor(Date.now() / 1e3);
355
- const windowStart = now - config.frequency.window;
356
- if (record && record.lastCommandTime < windowStart) {
357
- if (record.warningSent && record.firstWarningTime > 0 && now - record.firstWarningTime <= config.frequency.warnDelay) {
358
- record.commandCount = 1;
359
- record.lastCommandTime = now;
360
- } else if (isCurrentlyBlocked(record) && Date.now() < record.blockExpiryTime * 1e3) {
361
- } else {
362
- record = { platform, guildId, commandCount: 1, lastCommandTime: now, warningSent: false, blockExpiryTime: 0, firstWarningTime: 0 };
516
+ if (record.commandCount > config.frequency.limit) {
517
+ if (!record.warningSent) {
518
+ try {
519
+ await session.bot.sendMessage(guildId, config.frequency.warnMsg, platform);
520
+ } catch (e) {
363
521
  }
364
- } else if (!record) {
365
- record = { platform, guildId, commandCount: 1, lastCommandTime: now, warningSent: false, blockExpiryTime: 0, firstWarningTime: 0 };
366
- } else {
367
- record.commandCount += 1;
522
+ record.warningSent = true;
523
+ record.commandCount = 1;
368
524
  record.lastCommandTime = now;
369
- }
370
- if (isCurrentlyBlocked(record)) {
525
+ record.firstWarningTime = now;
526
+ await updateCommandFrequencyRecord(ctx, platform, guildId, record);
527
+ throw new Error("Warning");
528
+ } else {
529
+ record.blockExpiryTime = now + config.frequency.blockDur;
530
+ record.warningSent = false;
531
+ record.commandCount = 0;
532
+ record.firstWarningTime = 0;
533
+ await updateCommandFrequencyRecord(ctx, platform, guildId, record);
371
534
  try {
372
- const remainingTime = Math.ceil((record.blockExpiryTime * 1e3 - Date.now()) / 1e3);
373
- await session.bot.sendMessage(guildId, config.frequency.blockedMsg.replace("{time}", remainingTime.toString()), platform);
535
+ await session.bot.sendMessage(guildId, config.frequency.blockMsg.replace("{duration}", config.frequency.blockDur.toString()), platform);
374
536
  } catch (e) {
375
537
  }
376
538
  throw new Error("Blocked");
377
539
  }
378
- if (record.commandCount > config.frequency.limit) {
379
- if (!record.warningSent) {
380
- try {
381
- await session.bot.sendMessage(guildId, config.frequency.warnMsg, platform);
382
- } catch (e) {
383
- }
384
- record.warningSent = true;
385
- record.commandCount = 1;
386
- record.lastCommandTime = now;
387
- record.firstWarningTime = now;
388
- await updateCommandFrequencyRecord(ctx, platform, guildId, record);
389
- throw new Error("Warning");
390
- } else {
391
- record.blockExpiryTime = now + config.frequency.blockDur;
392
- record.warningSent = false;
393
- record.commandCount = 0;
394
- record.firstWarningTime = 0;
395
- await updateCommandFrequencyRecord(ctx, platform, guildId, record);
396
- try {
397
- await session.bot.sendMessage(guildId, config.frequency.blockMsg.replace("{duration}", config.frequency.blockDur.toString()), platform);
398
- } catch (e) {
399
- }
400
- throw new Error("Blocked");
401
- }
402
- }
403
- await updateCommandFrequencyRecord(ctx, platform, guildId, record);
404
- });
405
- }
406
- if (config.basic.quitCommandEnabled) {
407
- ctx.command("quit", "让机器人主动退出当前群聊", { authority: config.basic.quitCommandAuthority }).action(async ({ session }) => {
408
- if (!session.guildId) return "quit 指令只能在群聊中使用。";
409
- const { guildId, platform, userId } = session;
410
- quittingGuilds.add(`${platform}:${guildId}`);
411
- try {
412
- await session.bot.sendMessage(session.guildId, config.basic.quitMessage.replace("{userId}", userId), platform);
413
- } catch (e) {
414
- }
415
- try {
416
- await session.bot.internal.setGroupLeave(parseInt(guildId));
417
- } catch (e) {
418
- quittingGuilds.delete(`${platform}:${guildId}`);
419
- return `退出失败: ${e.message}`;
420
- }
421
- return "";
422
- });
423
- }
540
+ }
541
+ await updateCommandFrequencyRecord(ctx, platform, guildId, record);
542
+ });
543
+ }
544
+ __name(apply4, "apply");
545
+
546
+ // src/modules/commands.ts
547
+ var commands_exports = {};
548
+ __export(commands_exports, {
549
+ apply: () => apply5,
550
+ name: () => name5
551
+ });
552
+ var name5 = "group-control-commands";
553
+ function apply5(ctx, config) {
424
554
  async function viewBlacklist() {
425
555
  const errorMsg = isBlacklistEnabled(config.basic);
426
556
  if (errorMsg) return errorMsg;
@@ -463,7 +593,135 @@ function apply(ctx, config) {
463
593
  __name(clearBlacklist, "clearBlacklist");
464
594
  ctx.command("clear-blacklist", "清空黑名单", { authority: 4 }).action(clearBlacklist);
465
595
  }
466
- __name(apply, "apply");
596
+ __name(apply5, "apply");
597
+
598
+ // src/modules/switch.ts
599
+ var switch_exports = {};
600
+ __export(switch_exports, {
601
+ apply: () => apply6,
602
+ name: () => name6
603
+ });
604
+ var name6 = "group-control-switch";
605
+ function apply6(ctx, config) {
606
+ if (!config.botSwitch?.enabled) return;
607
+ ctx.command("bot-on", "开启机器人", { authority: config.botSwitch.toggleAuthority }).action(async ({ session }) => {
608
+ if (!session.guildId) return "该指令只能在群聊中使用。";
609
+ await setGroupBotStatus(ctx, session.platform, session.guildId, true);
610
+ return "机器人已在此群开启。";
611
+ });
612
+ ctx.command("bot-off", "关闭机器人", { authority: config.botSwitch.toggleAuthority }).action(async ({ session }) => {
613
+ if (!session.guildId) return "该指令只能在群聊中使用。";
614
+ await setGroupBotStatus(ctx, session.platform, session.guildId, false);
615
+ return "机器人已在此群关闭。";
616
+ });
617
+ ctx.on(
618
+ "command/before-execute",
619
+ async (argv) => {
620
+ const session = argv.session;
621
+ if (!session.guildId) return;
622
+ if (argv.command.name === "bot-on" || argv.command.name === "bot-off") {
623
+ return;
624
+ }
625
+ const status = await getGroupBotStatus(ctx, session.platform, session.guildId);
626
+ const isBotEnabled = status ? status.botEnabled : config.botSwitch.defaultState;
627
+ if (!isBotEnabled) {
628
+ const isMentioned = session.elements?.some((e) => e.type === "at" && e.attrs.id === session.bot.userId);
629
+ if (isMentioned && config.botSwitch.disabledMessage) {
630
+ try {
631
+ await session.send(config.botSwitch.disabledMessage);
632
+ } catch (e) {
633
+ ctx.logger("group-control-switch").warn("发送关闭提示失败", e);
634
+ }
635
+ }
636
+ return "";
637
+ }
638
+ },
639
+ true
640
+ /* append,在其他验证之后执行 */
641
+ );
642
+ ctx.middleware(async (session, next) => {
643
+ if (!session.guildId) return next();
644
+ const status = await getGroupBotStatus(ctx, session.platform, session.guildId);
645
+ const isBotEnabled = status ? status.botEnabled : config.botSwitch.defaultState;
646
+ if (isBotEnabled) {
647
+ return next();
648
+ }
649
+ const isMentioned = session.elements?.some((e) => e.type === "at" && e.attrs.id === session.bot.userId);
650
+ if (isMentioned && config.botSwitch.disabledMessage) {
651
+ try {
652
+ await session.send(config.botSwitch.disabledMessage);
653
+ } catch (e) {
654
+ }
655
+ }
656
+ return;
657
+ });
658
+ }
659
+ __name(apply6, "apply");
660
+
661
+ // src/config.ts
662
+ var import_koishi = require("koishi");
663
+ var Config = import_koishi.Schema.intersect([
664
+ import_koishi.Schema.object({
665
+ basic: import_koishi.Schema.object({
666
+ welcomeMessage: import_koishi.Schema.string().default("你好,我是机器人。").description("机器人加入群聊时发送的欢迎消息"),
667
+ blacklistMessage: import_koishi.Schema.string().default("此群聊已被拉黑,机器人将自动退出,请联系管理员移出黑名单。").description("被拉入黑名单群后在群内发送的提示"),
668
+ quitMessage: import_koishi.Schema.string().default("收到来自{userId}的指令,即将退出群聊。").description("用户发送quit指令后在群内发送的提示,支持变量{userId}"),
669
+ enableBlacklist: import_koishi.Schema.boolean().default(true).description('启用"被踢出自动拉黑"功能'),
670
+ notifyAdminOnKick: import_koishi.Schema.boolean().default(true).description("被踢出群时通知管理员(需要在群聊邀请审核中配置管理员QQ)"),
671
+ kickNotificationMessage: import_koishi.Schema.string().default("机器人已被踢出群聊\n群号:{groupId}\n该群已被自动加入黑名单。").description("被踢出群通知消息模板,支持变量{groupId}"),
672
+ smallGroupAutoQuit: import_koishi.Schema.boolean().default(false).description("启用小群自动退群功能"),
673
+ smallGroupThreshold: import_koishi.Schema.number().default(30).description("小群人数阈值(群成员数小于等于此值时自动退群)"),
674
+ smallGroupQuitMessage: import_koishi.Schema.string().default("该群人数过少({memberCount}人),不满足最低人数要求({threshold}人),机器人将自动退出。").description("小群自动退群时在群内发送的提示,支持变量{memberCount}, {threshold}"),
675
+ smallGroupNotifyAdmin: import_koishi.Schema.boolean().default(true).description("小群自动退群时通知管理员"),
676
+ quitCommandEnabled: import_koishi.Schema.boolean().default(true).description("启用quit"),
677
+ quitCommandAuthority: import_koishi.Schema.number().default(3).description("quit指令所需权限")
678
+ }).description("基础群组管理")
679
+ }),
680
+ import_koishi.Schema.object({
681
+ frequency: import_koishi.Schema.object({
682
+ enabled: import_koishi.Schema.boolean().default(false).description("启用频率控制(对所有指令生效)"),
683
+ limit: import_koishi.Schema.number().default(5).description("时间窗口内允许的最大指令次数"),
684
+ window: import_koishi.Schema.number().default(60).description("频率检测时间窗口(秒)"),
685
+ warnDelay: import_koishi.Schema.number().default(30).description("发出警告后,再次触发的时间阈值(秒),在此时间内再次触发则进入屏蔽状态"),
686
+ blockDur: import_koishi.Schema.number().default(300).description("触发频率限制后屏蔽的时长(秒)"),
687
+ warnMsg: import_koishi.Schema.string().default("指令频率过高,请慢一点~").description("频率过高时发送的警告消息"),
688
+ blockMsg: import_koishi.Schema.string().default("指令频率过高,本群指令已被禁用 {duration} 秒。").description("触发频率限制后发送的屏蔽通知消息,支持变量{duration}"),
689
+ blockedMsg: import_koishi.Schema.string().default("指令暂时被禁用,还有 {time} 秒解禁。").description("屏蔽期间接收到指令时的提示消息,支持变量{time}"),
690
+ whitelist: import_koishi.Schema.array(String).default([]).description("频率控制白名单群号列表,白名单内的群聊不受频率限制")
691
+ }).description("指令频率控制")
692
+ }),
693
+ import_koishi.Schema.object({
694
+ invite: import_koishi.Schema.object({
695
+ enabled: import_koishi.Schema.boolean().default(false).description("启用群聊邀请审核功能"),
696
+ adminQQs: import_koishi.Schema.array(String).default([]).description("管理员QQ号列表(用于权限验证)"),
697
+ notificationGroupId: import_koishi.Schema.string().description("通知群号(可选:若填写,邀请请求将发送到此群;若不填,则发送私聊给管理员)"),
698
+ inviteWaitMessage: import_koishi.Schema.string().default("已收到您的群聊邀请,正在等待管理员审核,请耐心等待。").description("发送给邀请者的等待审核提示消息"),
699
+ inviteRequestMessage: import_koishi.Schema.string().default('收到新的群聊邀请请求:\n群名称:{groupName}\n群号:{groupId}\n邀请者:{userName} (QQ: {userId})\n\n请管理员引用此消息回复"同意"或"拒绝"。').description("发送给管理员的邀请请求消息模板,支持变量{groupName}, {groupId}, {userName}, {userId}"),
700
+ autoApprove: import_koishi.Schema.boolean().default(false).description("是否自动同意邀请(仅在没有指定管理员时)"),
701
+ showDetailedLog: import_koishi.Schema.boolean().default(false).description("是否显示详细日志")
702
+ }).description("群聊邀请审核")
703
+ }),
704
+ import_koishi.Schema.object({
705
+ botSwitch: import_koishi.Schema.object({
706
+ enabled: import_koishi.Schema.boolean().default(true).description("启用独立的群聊bot开关功能"),
707
+ defaultState: import_koishi.Schema.boolean().default(true).description("群聊中的默认开启状态"),
708
+ disabledMessage: import_koishi.Schema.string().default("机器人当前在此群处于关闭状态,请使用bot-on开启。").description("机器人在关闭状态下被@时的提示消息"),
709
+ toggleAuthority: import_koishi.Schema.number().default(3).description("开关Bot指令(bot-on/bot-off)所需权限")
710
+ }).description("机器人开关控制")
711
+ })
712
+ ]);
713
+
714
+ // src/index.ts
715
+ var name7 = "group-control";
716
+ function apply7(ctx, config) {
717
+ ctx.plugin(database_exports);
718
+ ctx.plugin(basic_exports, config);
719
+ ctx.plugin(invite_exports, config);
720
+ ctx.plugin(frequency_exports, config);
721
+ ctx.plugin(commands_exports, config);
722
+ ctx.plugin(switch_exports, config);
723
+ }
724
+ __name(apply7, "apply");
467
725
  // Annotate the CommonJS export names for ESM import in node:
468
726
  0 && (module.exports = {
469
727
  Config,
@@ -0,0 +1,4 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare const name = "group-control-basic";
4
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,4 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare const name = "group-control-commands";
4
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,4 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare const name = "group-control-frequency";
4
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,4 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare const name = "group-control-invite";
4
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,4 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare const name = "group-control-switch";
4
+ export declare function apply(ctx: Context, config: Config): void;
package/lib/utils.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Config } from './config';
2
+ export declare function isBlacklistEnabled(config: Config['basic']): string | null;
3
+ export declare function parseGuildId(input: string): string | null;
4
+ export declare function formatDate(timestamp: number): string;
5
+ export declare function notifyAdmins(bot: any, config: Config, message: string): Promise<void>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-group-control",
3
- "description": "Koishi群聊自管理插件,支持自定义机器人进群消息、被踢出自动拉黑、刷屏自动屏蔽功能和主动退群指令等。(仅支持OneBot适配器)",
4
- "version": "0.2.5",
3
+ "description": "Koishi 插件,一个多功能的群聊自管理工具。支持被踢出自动拉黑、刷屏自动屏蔽、开关控制等功能。(仅支持 OneBot 适配器)",
4
+ "version": "0.2.7-beta.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -23,4 +23,4 @@
23
23
  "peerDependencies": {
24
24
  "koishi": "^4.18.7"
25
25
  }
26
- }
26
+ }