koishi-plugin-temporaryban 1.0.7 → 1.1.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.
@@ -11,7 +11,7 @@ const info_1 = require("./info");
11
11
  function registerCommands(ctx, config, detector, mailer, userRecords, history, whitelistService) {
12
12
  (0, admin_1.registerAdminCommands)(ctx, config, mailer);
13
13
  (0, dictionary_1.registerDictionaryCommands)(ctx, config, detector);
14
- (0, whitelist_1.registerWhitelistCommands)(ctx, config, whitelistService);
14
+ (0, whitelist_1.registerWhitelistCommands)(ctx, config, whitelistService, detector);
15
15
  (0, stats_1.registerStatsCommands)(ctx, config, userRecords);
16
16
  (0, check_1.registerCheckCommands)(ctx, config, detector);
17
17
  (0, history_1.registerHistoryCommands)(ctx, config, history);
@@ -16,7 +16,7 @@ function registerInfoCommands(ctx, config, whitelistService) {
16
16
  const groupConfig = config.groups.find(g => g.groupId === session.guildId);
17
17
  if (!groupConfig)
18
18
  return session.text('commands.temporaryban.messages.group_not_configured');
19
- const whitelistCount = whitelistService.getList(session.guildId).length;
19
+ const whitelistCount = whitelistService.getWhitelist(session.guildId).length;
20
20
  const methods = groupConfig.detectionMethods.join(', ') || 'None';
21
21
  const smartVer = groupConfig.smartVerification ? 'ON' : 'OFF';
22
22
  return session.text('commands.temporaryban.messages.group_info', [
@@ -25,14 +25,27 @@ function registerStatsCommands(ctx, config, userRecords) {
25
25
  return session.text('commands.temporaryban.messages.stats_header', [violators]);
26
26
  });
27
27
  // 8. Clean
28
- cmd.subcommand('.clean <user:string>')
29
- .action(async ({ session }, user) => {
28
+ cmd.subcommand('.clean [user:string]')
29
+ .option('all', '-a 清除本群所有违规记录')
30
+ .action(async ({ session, options }, user) => {
30
31
  if (!session)
31
32
  return;
32
33
  if (!(0, permission_1.checkPermission)(session, config))
33
34
  return session.text('commands.temporaryban.messages.permission_denied');
34
35
  if (!session.guildId)
35
36
  return session.text('commands.temporaryban.messages.group_only');
37
+ // Bulk Clean
38
+ if (options?.all) {
39
+ const prefix = `${session.guildId}-`;
40
+ let removedCount = 0;
41
+ for (const key of userRecords.keys()) {
42
+ if (key.startsWith(prefix)) {
43
+ userRecords.delete(key);
44
+ removedCount++;
45
+ }
46
+ }
47
+ return session.text('commands.temporaryban.messages.all_records_cleared', [removedCount]);
48
+ }
36
49
  if (!user)
37
50
  return session.text('commands.temporaryban.messages.specify_user_id');
38
51
  const key = `${session.guildId}-${user}`;
@@ -1,4 +1,5 @@
1
1
  import { Context } from 'koishi';
2
2
  import { Config } from '../config';
3
3
  import { WhitelistService } from '../services/whitelist';
4
- export declare function registerWhitelistCommands(ctx: Context, config: Config, whitelistService: WhitelistService): void;
4
+ import { DetectorService } from '../services/detector';
5
+ export declare function registerWhitelistCommands(ctx: Context, config: Config, whitelistService: WhitelistService, detector: DetectorService): void;
@@ -2,10 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerWhitelistCommands = registerWhitelistCommands;
4
4
  const permission_1 = require("../utils/permission");
5
- function registerWhitelistCommands(ctx, config, whitelistService) {
5
+ function registerWhitelistCommands(ctx, config, whitelistService, detector) {
6
6
  const cmd = ctx.command('temporaryban');
7
- // 5. Whitelist Add
8
- cmd.subcommand('.whitelist.add <user:string>')
7
+ // --- Whitelist User Management ---
8
+ const whitelistCmd = cmd.subcommand('.whitelist');
9
+ // 10. Whitelist User List
10
+ whitelistCmd.subcommand('.list')
11
+ .action(async ({ session }) => {
12
+ if (!session)
13
+ return;
14
+ if (!(0, permission_1.checkPermission)(session, config))
15
+ return session.text('commands.temporaryban.messages.permission_denied');
16
+ if (!session.guildId)
17
+ return session.text('commands.temporaryban.messages.group_only');
18
+ const groupConfig = config.groups.find(g => g.groupId === session.guildId);
19
+ if (!groupConfig)
20
+ return session.text('commands.temporaryban.messages.group_not_configured');
21
+ const items = whitelistService.getWhitelist(session.guildId);
22
+ if (items.length === 0)
23
+ return session.text('commands.temporaryban.messages.no_whitelist_users');
24
+ return session.text('commands.temporaryban.messages.whitelist_users_list', [items.length, items.join(', ')]);
25
+ });
26
+ // 5. Whitelist Add User
27
+ whitelistCmd.subcommand('.add <user:string>')
9
28
  .action(async ({ session }, user) => {
10
29
  if (!session)
11
30
  return;
@@ -13,18 +32,28 @@ function registerWhitelistCommands(ctx, config, whitelistService) {
13
32
  return session.text('commands.temporaryban.messages.permission_denied');
14
33
  if (!session.guildId)
15
34
  return session.text('commands.temporaryban.messages.group_only');
16
- if (!user)
17
- return session.text('commands.temporaryban.messages.specify_user_id');
35
+ if (!user) {
36
+ await session.send(session.text('commands.temporaryban.messages.specify_user_id'));
37
+ user = await session.prompt();
38
+ if (!user)
39
+ return;
40
+ }
18
41
  const groupConfig = config.groups.find(g => g.groupId === session.guildId);
19
42
  if (!groupConfig)
20
43
  return session.text('commands.temporaryban.messages.group_not_configured');
44
+ // Support At-element (e.g. <at id="123"/>)
45
+ if (user.includes('<at')) {
46
+ const match = user.match(/id="([^"]+)"/);
47
+ if (match)
48
+ user = match[1];
49
+ }
21
50
  const success = await whitelistService.add(session.guildId, user);
22
51
  if (!success)
23
52
  return session.text('commands.temporaryban.messages.already_whitelisted');
24
53
  return session.text('commands.temporaryban.messages.user_added_whitelist', [user]);
25
54
  });
26
- // 6. Whitelist Remove
27
- cmd.subcommand('.whitelist.remove <user:string>')
55
+ // 6. Whitelist Remove User
56
+ whitelistCmd.subcommand('.remove <user:string>')
28
57
  .action(async ({ session }, user) => {
29
58
  if (!session)
30
59
  return;
@@ -32,6 +61,18 @@ function registerWhitelistCommands(ctx, config, whitelistService) {
32
61
  return session.text('commands.temporaryban.messages.permission_denied');
33
62
  if (!session.guildId)
34
63
  return session.text('commands.temporaryban.messages.group_only');
64
+ if (!user) {
65
+ await session.send(session.text('commands.temporaryban.messages.specify_user_id'));
66
+ user = await session.prompt();
67
+ if (!user)
68
+ return;
69
+ }
70
+ // Support At-element
71
+ if (user.includes('<at')) {
72
+ const match = user.match(/id="([^"]+)"/);
73
+ if (match)
74
+ user = match[1];
75
+ }
35
76
  const groupConfig = config.groups.find(g => g.groupId === session.guildId);
36
77
  if (!groupConfig)
37
78
  return session.text('commands.temporaryban.messages.group_not_configured');
@@ -40,4 +81,82 @@ function registerWhitelistCommands(ctx, config, whitelistService) {
40
81
  return session.text('commands.temporaryban.messages.not_in_whitelist');
41
82
  return session.text('commands.temporaryban.messages.user_removed_whitelist', [user]);
42
83
  });
84
+ // --- Whitelist Word Management ---
85
+ const wordCmd = whitelistCmd.subcommand('.word');
86
+ // 7. Whitelist Word Add
87
+ wordCmd.subcommand('.add <text:text>')
88
+ .action(async ({ session }, text) => {
89
+ if (!session)
90
+ return;
91
+ if (!(0, permission_1.checkPermission)(session, config))
92
+ return session.text('commands.temporaryban.messages.permission_denied');
93
+ if (!session.guildId)
94
+ return session.text('commands.temporaryban.messages.group_only');
95
+ if (!text) {
96
+ await session.send(session.text('commands.temporaryban.messages.specify_word'));
97
+ text = await session.prompt();
98
+ if (!text)
99
+ return;
100
+ }
101
+ const groupConfig = config.groups.find(g => g.groupId === session.guildId);
102
+ if (!groupConfig)
103
+ return session.text('commands.temporaryban.messages.group_not_configured');
104
+ // Support batch add (comma or newline separated)
105
+ const words = text.split(/[,,\n]/).map(w => w.trim()).filter(w => w);
106
+ const added = [];
107
+ for (const w of words) {
108
+ const success = await detector.addIgnoredWord(session.guildId, w);
109
+ if (success)
110
+ added.push(w);
111
+ }
112
+ if (added.length === 0)
113
+ return session.text('commands.temporaryban.messages.word_exists');
114
+ return session.text('commands.temporaryban.messages.ignored_word_added', [added.join(', ')]);
115
+ });
116
+ // 8. Whitelist Word Remove
117
+ wordCmd.subcommand('.remove <text:text>')
118
+ .action(async ({ session }, text) => {
119
+ if (!session)
120
+ return;
121
+ if (!(0, permission_1.checkPermission)(session, config))
122
+ return session.text('commands.temporaryban.messages.permission_denied');
123
+ if (!session.guildId)
124
+ return session.text('commands.temporaryban.messages.group_only');
125
+ if (!text) {
126
+ await session.send(session.text('commands.temporaryban.messages.specify_word'));
127
+ text = await session.prompt();
128
+ if (!text)
129
+ return;
130
+ }
131
+ const groupConfig = config.groups.find(g => g.groupId === session.guildId);
132
+ if (!groupConfig)
133
+ return session.text('commands.temporaryban.messages.group_not_configured');
134
+ const words = text.split(/[,,\n]/).map(w => w.trim()).filter(w => w);
135
+ const removed = [];
136
+ for (const w of words) {
137
+ const success = await detector.removeIgnoredWord(session.guildId, w);
138
+ if (success)
139
+ removed.push(w);
140
+ }
141
+ if (removed.length === 0)
142
+ return session.text('commands.temporaryban.messages.word_not_found');
143
+ return session.text('commands.temporaryban.messages.ignored_word_removed', [removed.join(', ')]);
144
+ });
145
+ // 9. Whitelist Word List
146
+ wordCmd.subcommand('.list')
147
+ .action(async ({ session }) => {
148
+ if (!session)
149
+ return;
150
+ if (!(0, permission_1.checkPermission)(session, config))
151
+ return session.text('commands.temporaryban.messages.permission_denied');
152
+ if (!session.guildId)
153
+ return session.text('commands.temporaryban.messages.group_only');
154
+ const groupConfig = config.groups.find(g => g.groupId === session.guildId);
155
+ if (!groupConfig)
156
+ return session.text('commands.temporaryban.messages.group_not_configured');
157
+ const items = detector.getIgnoredWords(session.guildId);
158
+ if (items.length === 0)
159
+ return session.text('commands.temporaryban.messages.no_ignored_words');
160
+ return session.text('commands.temporaryban.messages.ignored_words_list', [items.length, items.join(', ')]);
161
+ });
43
162
  }
package/lib/config.d.ts CHANGED
@@ -36,12 +36,14 @@ export interface GroupConfig {
36
36
  smartVerification: boolean;
37
37
  contextMsgCount: number;
38
38
  aiThreshold?: number;
39
+ showCensoredWord?: boolean;
39
40
  localBadWordDict: string;
40
41
  whitelist: WhitelistItem[];
41
42
  triggerThreshold?: number;
42
43
  triggerWindowMinutes?: number;
43
44
  muteMinutes?: number;
44
45
  checkProbability?: number;
46
+ warningTemplate?: string;
45
47
  detailedLog: boolean;
46
48
  }
47
49
  export interface BaiduConfig {
@@ -66,17 +68,24 @@ export interface OpenAIConfig {
66
68
  export interface Config {
67
69
  debug: boolean;
68
70
  adminList: string[];
69
- smtp: SmtpConfig;
70
- api: ApiConfig;
71
- baidu: BaiduConfig;
72
- aliyun: AliyunConfig;
73
- tencent: TencentConfig;
74
- openai: OpenAIConfig;
71
+ useApi: boolean;
72
+ useBaidu: boolean;
73
+ useAliyun: boolean;
74
+ useTencent: boolean;
75
+ useOpenAI: boolean;
76
+ useEmail: boolean;
77
+ smtp?: SmtpConfig;
78
+ api?: ApiConfig;
79
+ baidu?: BaiduConfig;
80
+ aliyun?: AliyunConfig;
81
+ tencent?: TencentConfig;
82
+ openai?: OpenAIConfig;
75
83
  defaultMuteMinutes: number;
76
84
  defaultTriggerThreshold: number;
77
85
  defaultAiThreshold: number;
78
86
  defaultCheckProbability: number;
79
87
  checkAdmin: boolean;
88
+ defaultShowCensoredWord: boolean;
80
89
  groups: GroupConfig[];
81
90
  }
82
91
  export declare const Config: Schema<Config>;
package/lib/config.js CHANGED
@@ -2,74 +2,145 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Config = void 0;
4
4
  const koishi_1 = require("koishi");
5
- exports.Config = koishi_1.Schema.object({
6
- debug: koishi_1.Schema.boolean().description('开启全局调试日志。开启后,控制台将输出详细的消息处理流程和错误堆栈,建议仅在排查问题时启用。').default(false),
7
- adminList: koishi_1.Schema.array(String).description('全局管理员列表 (OneBot 用户ID)。在此列表中的用户可以使用 `temporaryban.report` 等高级管理指令,拥有最高权限。').role('table'),
8
- checkAdmin: koishi_1.Schema.boolean().description('是否检查机器人在群内的管理权限。开启后,如果机器人不是群主或管理员,将不执行违禁词检查。').default(true),
9
- defaultMuteMinutes: koishi_1.Schema.number().description('全局默认禁言时长 (分钟)。当群组未配置时使用。').default(10).min(0.1),
10
- defaultTriggerThreshold: koishi_1.Schema.number().description('全局默认触发阈值 (次数)。当群组未配置时使用。').default(3).min(1),
11
- defaultAiThreshold: koishi_1.Schema.number().description('全局默认 AI 判定阈值 (0.0 - 1.0)。当群组未配置时使用。').default(0.6).min(0).max(1).step(0.1),
12
- defaultCheckProbability: koishi_1.Schema.number().description('全局默认检查概率 (0.0 - 1.0)。1.0 表示检查所有消息。当群组未配置时使用。').default(1.0).min(0).max(1).step(0.1),
13
- smtp: koishi_1.Schema.object({
14
- host: koishi_1.Schema.string().description('SMTP 服务器地址 (例如 smtp.qq.com, smtp.163.com)').default('smtp.example.com'),
15
- port: koishi_1.Schema.number().description('SMTP 端口 (SSL通常为465, 非SSL通常为25)').default(465),
16
- secure: koishi_1.Schema.boolean().description('启用 SSL/TLS 加密链接。如果端口是465,通常需要开启此项。').default(true),
17
- user: koishi_1.Schema.string().description('SMTP 用户名 (通常是您的邮箱地址)').default('user@example.com'),
18
- pass: koishi_1.Schema.string().role('secret').description('SMTP 密码或授权码。注意:QQ邮箱/163邮箱等通常需要使用生成的授权码,而非登录密码。').default('password'),
19
- senderName: koishi_1.Schema.string().description('邮件发件人显示名称').default('Koishi Bot'),
20
- senderEmail: koishi_1.Schema.string().description('邮件发件人地址 (必须与SMTP用户名匹配)').default('bot@example.com'),
21
- receivers: koishi_1.Schema.array(String).description('接收违规通知的管理员邮箱列表').role('table'),
22
- summaryIntervalDays: koishi_1.Schema.number().description('邮件汇总周期 (天)。设置为 0 时,每条违规都会立即发送邮件;设置为 >0 时 (如 1),将每隔 N 天发送一次违规汇总报告。').default(1).min(0),
23
- }).description('邮件通知设置 (SMTP)'),
24
- api: koishi_1.Schema.object({
25
- apiUrl: koishi_1.Schema.string().description('在线检测 API 地址 (默认使用 ApiHz 接口)').default('https://cn.apihz.cn/api/zici/mgc.php'),
26
- apiId: koishi_1.Schema.string().description('ApiHz 开发者 ID (必填,否则无法使用 API 检测)').default(''),
27
- apiKey: koishi_1.Schema.string().role('secret').description('ApiHz 开发者 Key (必填)').default(''),
28
- }).description('在线检测设置 (ApiHz)'),
29
- openai: koishi_1.Schema.object({
30
- apiKey: koishi_1.Schema.string().role('secret').description('OpenAI/SiliconFlow API Key').default(''),
31
- baseUrl: koishi_1.Schema.string().description('API Base URL (默认为 SiliconFlow)').default('https://api.siliconflow.cn/v1'),
32
- model: koishi_1.Schema.string().description('模型 ID').default('deepseek-ai/DeepSeek-V2.5'),
33
- }).description('AI 检测设置 (OpenAI/SiliconFlow)'),
34
- baidu: koishi_1.Schema.object({
35
- apiKey: koishi_1.Schema.string().description('百度智能云 API Key').default(''),
36
- secretKey: koishi_1.Schema.string().role('secret').description('百度智能云 Secret Key').default(''),
37
- }).description('百度智能云设置'),
38
- aliyun: koishi_1.Schema.object({
39
- accessKeyId: koishi_1.Schema.string().description('阿里云 AccessKey ID').default(''),
40
- accessKeySecret: koishi_1.Schema.string().role('secret').description('阿里云 AccessKey Secret').default(''),
41
- endpoint: koishi_1.Schema.string().description('阿里云内容安全 Endpoint').default('green-cip.cn-shanghai.aliyuncs.com'),
42
- }).description('阿里云内容安全设置'),
43
- tencent: koishi_1.Schema.object({
44
- secretId: koishi_1.Schema.string().description('腾讯云 SecretId').default(''),
45
- secretKey: koishi_1.Schema.string().role('secret').description('腾讯云 SecretKey').default(''),
46
- region: koishi_1.Schema.string().description('腾讯云地域 (例如 ap-shanghai)').default('ap-shanghai'),
47
- }).description('腾讯云内容安全设置'),
48
- groups: koishi_1.Schema.array(koishi_1.Schema.object({
49
- id: koishi_1.Schema.string().hidden().default(''),
50
- groupId: koishi_1.Schema.string().description('群组 ID (群号)。机器人将监控此群内的消息。').required(),
51
- enable: koishi_1.Schema.boolean().description('是否启用对该群组的监控。关闭后插件将忽略该群的所有消息。').default(true),
52
- detectionMethods: koishi_1.Schema.array(koishi_1.Schema.union([
53
- koishi_1.Schema.const('local').description('本地词库 (数据库)'),
54
- koishi_1.Schema.const('ai').description('AI 模型检测 (OpenAI/SiliconFlow)'),
55
- koishi_1.Schema.const('api').description('在线 API (ApiHz)'),
56
- koishi_1.Schema.const('baidu').description('百度智能云'),
57
- koishi_1.Schema.const('aliyun').description('阿里云 (内容安全增强版)'),
58
- koishi_1.Schema.const('tencent').description('腾讯云 (TMS)'),
59
- ])).role('checkbox').description('启用的检测方式 (多选)。若开启多个,只要有任意一个检测到违规即视为违规 (除非开启了智能验证)。').default(['local']),
60
- smartVerification: koishi_1.Schema.boolean().description('开启智能验证 (Smart Verification)。开启后,当【本地词库】或【API】检测到违规时,不会立即惩罚,而是将该用户的最近几条聊天记录发送给 AI 进行二次确认。只有 AI 也判定违规时才执行惩罚。需确保【AI 模型检测】已配置 API Key。').default(false),
61
- contextMsgCount: koishi_1.Schema.number().description('智能验证时的上下文消息数量。仅在开启智能验证时生效。').default(3).min(1).max(10),
62
- aiThreshold: koishi_1.Schema.number().description('AI 违规判定阈值 (0.0 - 1.0)。留空则使用全局默认值。').min(0).max(1).step(0.1),
63
- checkProbability: koishi_1.Schema.number().description('消息检查概率 (0.0 - 1.0)。留空则使用全局默认值。').min(0).max(1).step(0.1),
64
- localBadWordDict: koishi_1.Schema.string()
65
- .description('【初始导入/Legacy】本地违禁词库配置。插件现已使用数据库存储词库。首次启动时,若数据库为空,将自动导入此处的词汇。之后的增删操作请使用指令 `temporaryban.add/remove`,此配置项将不再生效。')
66
- .default(''),
67
- whitelist: koishi_1.Schema.array(koishi_1.Schema.object({
68
- userId: koishi_1.Schema.string().description('用户 ID (QQ号)')
69
- })).description('白名单用户列表。列表中的用户触发违禁词时不会受到惩罚。注:群管理员和群主会自动获得白名单豁免,无需在此手动添加。').role('table'),
70
- triggerThreshold: koishi_1.Schema.number().description('触发禁言的累计违规次数。留空则使用全局默认值。').min(1),
71
- triggerWindowMinutes: koishi_1.Schema.number().description('违规计数的时间窗口 (分钟)。在此时间内累计的违规次数达到阈值即触发禁言。超过此时间窗口后,计数将重置。').default(5).min(0.1),
72
- muteMinutes: koishi_1.Schema.number().description('禁言时长 (分钟)。留空则使用全局默认值。').min(0.1),
73
- detailedLog: koishi_1.Schema.boolean().description('开启此群组的详细日志。用于调试特定群组的检测逻辑。').default(false),
74
- }).description('群组配置')).description('监控群组列表').role('list').default([])
75
- }).description('违禁词检测插件配置');
5
+ exports.Config = koishi_1.Schema.intersect([
6
+ koishi_1.Schema.object({
7
+ debug: koishi_1.Schema.boolean().description('开启全局调试日志。').default(false),
8
+ adminList: koishi_1.Schema.array(String).description('全局管理员列表 (OneBot 用户ID)。').role('table'),
9
+ checkAdmin: koishi_1.Schema.boolean().description('是否检查机器人在群内的管理权限。').default(true),
10
+ defaultMuteMinutes: koishi_1.Schema.number().description('全局默认禁言时长 (分钟)').default(10).min(0.1),
11
+ defaultTriggerThreshold: koishi_1.Schema.number().description('全局默认触发阈值 (次数)').default(3).min(1),
12
+ defaultAiThreshold: koishi_1.Schema.number().description('全局默认 AI 判定阈值 (0.0 - 1.0)。').default(0.6).min(0).max(1).step(0.1),
13
+ defaultCheckProbability: koishi_1.Schema.number().description('全局默认检查概率 (0.0 - 1.0)。').default(1.0).min(0).max(1).step(0.1),
14
+ defaultShowCensoredWord: koishi_1.Schema.boolean().description('全局默认是否在警告中显示触发的违禁词。').default(true),
15
+ }).description('基础设置'),
16
+ koishi_1.Schema.intersect([
17
+ koishi_1.Schema.object({
18
+ useEmail: koishi_1.Schema.boolean().description('启用邮件通知系统').default(false),
19
+ }).description('邮件通知开关'),
20
+ koishi_1.Schema.union([
21
+ koishi_1.Schema.object({
22
+ useEmail: koishi_1.Schema.const(true).required(),
23
+ smtp: koishi_1.Schema.object({
24
+ host: koishi_1.Schema.string().description('SMTP 服务器地址').default('smtp.example.com'),
25
+ port: koishi_1.Schema.number().description('SMTP 端口').default(465),
26
+ secure: koishi_1.Schema.boolean().description('启用 SSL/TLS 加密链接').default(true),
27
+ user: koishi_1.Schema.string().description('SMTP 用户名').default('user@example.com'),
28
+ pass: koishi_1.Schema.string().role('secret').description('SMTP 密码或授权码').default('password'),
29
+ senderName: koishi_1.Schema.string().description('邮件发件人显示名称').default('Koishi Bot'),
30
+ senderEmail: koishi_1.Schema.string().description('邮件发件人地址').default('bot@example.com'),
31
+ receivers: koishi_1.Schema.array(String).description('接收违规通知的管理员邮箱列表').role('table'),
32
+ summaryIntervalDays: koishi_1.Schema.number().description('邮件汇总周期 (天)').default(1).min(0),
33
+ }).description('邮件通知设置 (SMTP)'),
34
+ }),
35
+ koishi_1.Schema.object({}),
36
+ ]),
37
+ ]),
38
+ koishi_1.Schema.intersect([
39
+ koishi_1.Schema.object({
40
+ useApi: koishi_1.Schema.boolean().description('启用在线 API 检测 (ApiHz)').default(false),
41
+ }).description('在线 API 开关'),
42
+ koishi_1.Schema.union([
43
+ koishi_1.Schema.object({
44
+ useApi: koishi_1.Schema.const(true).required(),
45
+ api: koishi_1.Schema.object({
46
+ apiUrl: koishi_1.Schema.string().description('在线检测 API 地址').default('https://cn.apihz.cn/api/zici/mgc.php'),
47
+ apiId: koishi_1.Schema.string().description('ApiHz 开发者 ID').default(''),
48
+ apiKey: koishi_1.Schema.string().role('secret').description('ApiHz 开发者 Key').default(''),
49
+ }).description('在线检测设置 (ApiHz)'),
50
+ }),
51
+ koishi_1.Schema.object({}),
52
+ ]),
53
+ ]),
54
+ koishi_1.Schema.intersect([
55
+ koishi_1.Schema.object({
56
+ useOpenAI: koishi_1.Schema.boolean().description('启用 AI 模型检测 (OpenAI/SiliconFlow)').default(false),
57
+ }).description('AI 检测开关'),
58
+ koishi_1.Schema.union([
59
+ koishi_1.Schema.object({
60
+ useOpenAI: koishi_1.Schema.const(true).required(),
61
+ openai: koishi_1.Schema.object({
62
+ apiKey: koishi_1.Schema.string().role('secret').description('API Key').default(''),
63
+ baseUrl: koishi_1.Schema.string().description('API Base URL').default('https://api.siliconflow.cn/v1'),
64
+ model: koishi_1.Schema.string().description('模型 ID').default('deepseek-ai/DeepSeek-V2.5'),
65
+ }).description('AI 检测设置'),
66
+ }),
67
+ koishi_1.Schema.object({}),
68
+ ]),
69
+ ]),
70
+ koishi_1.Schema.intersect([
71
+ koishi_1.Schema.object({
72
+ useBaidu: koishi_1.Schema.boolean().description('启用百度智能云检测').default(false),
73
+ }).description('百度智能云开关'),
74
+ koishi_1.Schema.union([
75
+ koishi_1.Schema.object({
76
+ useBaidu: koishi_1.Schema.const(true).required(),
77
+ baidu: koishi_1.Schema.object({
78
+ apiKey: koishi_1.Schema.string().description('API Key').default(''),
79
+ secretKey: koishi_1.Schema.string().role('secret').description('Secret Key').default(''),
80
+ }).description('百度智能云设置'),
81
+ }),
82
+ koishi_1.Schema.object({}),
83
+ ]),
84
+ ]),
85
+ koishi_1.Schema.intersect([
86
+ koishi_1.Schema.object({
87
+ useAliyun: koishi_1.Schema.boolean().description('启用阿里云内容安全检测').default(false),
88
+ }).description('阿里云开关'),
89
+ koishi_1.Schema.union([
90
+ koishi_1.Schema.object({
91
+ useAliyun: koishi_1.Schema.const(true).required(),
92
+ aliyun: koishi_1.Schema.object({
93
+ accessKeyId: koishi_1.Schema.string().description('AccessKey ID').default(''),
94
+ accessKeySecret: koishi_1.Schema.string().role('secret').description('AccessKey Secret').default(''),
95
+ endpoint: koishi_1.Schema.string().description('Endpoint').default('green-cip.cn-shanghai.aliyuncs.com'),
96
+ }).description('阿里云设置'),
97
+ }),
98
+ koishi_1.Schema.object({}),
99
+ ]),
100
+ ]),
101
+ koishi_1.Schema.intersect([
102
+ koishi_1.Schema.object({
103
+ useTencent: koishi_1.Schema.boolean().description('启用腾讯云检测').default(false),
104
+ }).description('腾讯云开关'),
105
+ koishi_1.Schema.union([
106
+ koishi_1.Schema.object({
107
+ useTencent: koishi_1.Schema.const(true).required(),
108
+ tencent: koishi_1.Schema.object({
109
+ secretId: koishi_1.Schema.string().description('SecretId').default(''),
110
+ secretKey: koishi_1.Schema.string().role('secret').description('SecretKey').default(''),
111
+ region: koishi_1.Schema.string().description('Region').default('ap-shanghai'),
112
+ }).description('腾讯云设置'),
113
+ }),
114
+ koishi_1.Schema.object({}),
115
+ ]),
116
+ ]),
117
+ koishi_1.Schema.object({
118
+ groups: koishi_1.Schema.array(koishi_1.Schema.object({
119
+ id: koishi_1.Schema.string().hidden().default(''),
120
+ groupId: koishi_1.Schema.string().description('群组 ID (群号)').required(),
121
+ enable: koishi_1.Schema.boolean().description('是否启用监控').default(true),
122
+ detectionMethods: koishi_1.Schema.array(koishi_1.Schema.union([
123
+ koishi_1.Schema.const('local').description('本地词库 (数据库)'),
124
+ koishi_1.Schema.const('ai').description('AI 模型检测'),
125
+ koishi_1.Schema.const('api').description('在线 API'),
126
+ koishi_1.Schema.const('baidu').description('百度智能云'),
127
+ koishi_1.Schema.const('aliyun').description('阿里云'),
128
+ koishi_1.Schema.const('tencent').description('腾讯云'),
129
+ ])).role('checkbox').description('启用的检测方式').default(['local']),
130
+ smartVerification: koishi_1.Schema.boolean().description('开启智能验证').default(false),
131
+ contextMsgCount: koishi_1.Schema.number().description('智能验证上下文数量').default(3).min(1).max(10),
132
+ aiThreshold: koishi_1.Schema.number().description('AI 判定阈值 (留空用默认)').min(0).max(1).step(0.1),
133
+ checkProbability: koishi_1.Schema.number().description('检查概率 (留空用默认)').min(0).max(1).step(0.1),
134
+ showCensoredWord: koishi_1.Schema.boolean().description('是否显示触发的违禁词 (留空用默认)'),
135
+ localBadWordDict: koishi_1.Schema.string().description('【Legacy】本地违禁词库 (初始导入)').default(''),
136
+ whitelist: koishi_1.Schema.array(koishi_1.Schema.object({
137
+ userId: koishi_1.Schema.string().required().description('白名单用户 ID'),
138
+ })).description('白名单用户').role('table'),
139
+ warningTemplate: koishi_1.Schema.string().role('textarea').description('自定义警告语模板。支持变量: {at} (At用户), {userId}, {nick}, {words} (触发词), {count} (当前次数), {maxCount} (阈值), {muteMinutes} (禁言时长)'),
140
+ triggerThreshold: koishi_1.Schema.number().description('触发阈值 (次数)。覆盖全局设置。').min(1),
141
+ triggerWindowMinutes: koishi_1.Schema.number().description('触发窗口期 (分钟)。').default(5).min(1),
142
+ muteMinutes: koishi_1.Schema.number().description('禁言时长 (分钟, 留空用默认)').min(0.1),
143
+ detailedLog: koishi_1.Schema.boolean().description('开启详细日志').default(false),
144
+ }).description('群组配置')).description('监控群组列表').role('list').default([])
145
+ }).description('群组监控设置')
146
+ ]);
package/lib/index.d.ts CHANGED
@@ -4,11 +4,30 @@ import { WhitelistTable } from './services/whitelist';
4
4
  export * from './config';
5
5
  export declare const name = "koishi-plugin-temporaryban";
6
6
  export declare const inject: string[];
7
+ export interface IgnoredWordTable {
8
+ id: number;
9
+ groupId: string;
10
+ word: string;
11
+ createdAt: Date;
12
+ }
13
+ export interface ViolationTable {
14
+ id: number;
15
+ userId: string;
16
+ groupId: string;
17
+ words: string[];
18
+ content: string;
19
+ timestamp: Date;
20
+ }
7
21
  declare module 'koishi' {
8
22
  interface Tables {
9
23
  temporaryban_badwords: BadWordTable;
10
24
  temporaryban_message_history: MessageHistoryTable;
11
25
  temporaryban_whitelist: WhitelistTable;
26
+ temporaryban_ignored_words: IgnoredWordTable;
27
+ temporaryban_violations: ViolationTable;
28
+ }
29
+ interface Context {
30
+ console: any;
12
31
  }
13
32
  }
14
33
  export interface MessageHistoryTable {