@nestim/koishi-plugin-qq-group-manager 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands.js +4 -23
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -2
- package/lib/service.d.ts +0 -1
- package/lib/service.js +41 -46
- package/package.json +1 -1
package/lib/commands.js
CHANGED
|
@@ -105,17 +105,16 @@ function registerCommands(ctx, service, config) {
|
|
|
105
105
|
return formatAction(result, config, displayTarget);
|
|
106
106
|
});
|
|
107
107
|
root
|
|
108
|
-
.subcommand('gag [target:string]', '
|
|
109
|
-
.alias('
|
|
110
|
-
.alias('口气')
|
|
108
|
+
.subcommand('gag [target:string]', '自我约束(随机 1~60 分钟禁言)')
|
|
109
|
+
.alias('自我约束')
|
|
111
110
|
.option('reason', '-r <reason:string> 操作原因')
|
|
112
111
|
.action(async ({ session, options }, target) => {
|
|
113
112
|
if (!config.enableSelfGag)
|
|
114
|
-
return '
|
|
113
|
+
return '自我约束功能未启用。';
|
|
115
114
|
if (!session?.userId)
|
|
116
115
|
return '无法识别当前账号。';
|
|
117
116
|
if (config.allowedUserIds.includes(session.userId))
|
|
118
|
-
return '喵~
|
|
117
|
+
return '喵~ 白名单用户不参与自我约束。';
|
|
119
118
|
const resolvedTarget = target || session.userId;
|
|
120
119
|
if (await service.shouldSelfGag(session, resolvedTarget)) {
|
|
121
120
|
const result = await service.selfGag(session, 'gag-trigger');
|
|
@@ -168,22 +167,4 @@ function registerCommands(ctx, service, config) {
|
|
|
168
167
|
const displayTarget = await service.formatTargetDisplay(session, result.plan?.targetId, target);
|
|
169
168
|
return formatAction(result, config, displayTarget);
|
|
170
169
|
});
|
|
171
|
-
root
|
|
172
|
-
.subcommand('审核 <code:string> <state:string>', '审核入群申请(同意/拒绝)')
|
|
173
|
-
.option('reason', '-r <reason:string> 审核理由')
|
|
174
|
-
.action(async ({ session, options }, code, state) => {
|
|
175
|
-
if (!code || !state)
|
|
176
|
-
return '请提供审核编号与状态(同意/拒绝)。';
|
|
177
|
-
const auth = await service.authorizeCommand('review-join-request', session);
|
|
178
|
-
if (!auth.ok)
|
|
179
|
-
return auth.userMessage ?? '权限不足或上下文不符合要求。';
|
|
180
|
-
const normalized = state.trim().toLowerCase();
|
|
181
|
-
const approve = ['同意', '通过', '放行', '批准', 'approve', 'pass', 'ok', 'yes'].includes(normalized);
|
|
182
|
-
const reject = ['拒绝', '驳回', 'deny', 'reject', 'no', 'refuse'].includes(normalized);
|
|
183
|
-
if (!approve && !reject)
|
|
184
|
-
return '状态仅支持:同意/拒绝。';
|
|
185
|
-
const result = await service.reviewJoinRequestDecision(session, code, approve, options.reason);
|
|
186
|
-
service.logCommandResult('review-join-request', result, session, { code, approve, source: 'command' });
|
|
187
|
-
return formatAction(result, config);
|
|
188
|
-
});
|
|
189
170
|
}
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -55,9 +55,8 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
55
55
|
}).description('自动管控'),
|
|
56
56
|
koishi_1.Schema.object({
|
|
57
57
|
enableMemory: koishi_1.Schema.boolean().default(true).description('启用记忆库:群内发送 [记忆]+内容 保存到 Memory.md,[记忆查询]/[记忆删除] 管理,AI 回复可引用。'),
|
|
58
|
-
memoryFileName: koishi_1.Schema.string().default('Memory.md').description('记忆库文件名(保存在 Koishi
|
|
58
|
+
memoryFileName: koishi_1.Schema.string().default('Memory.md').description('记忆库文件名(保存在 Koishi 数据目录)。请直接使用控制台左侧「探索器 Explorer」打开该文件查看/编辑记忆库内容。'),
|
|
59
59
|
memoryInAi: koishi_1.Schema.boolean().default(true).description('AI 回复/识图时是否参考记忆库内容。'),
|
|
60
|
-
memoryContent: koishi_1.Schema.string().role('textarea').default('').description('记忆库内容(可直接在此查看/编辑 Memory.md,保存后会自动写回文件)。'),
|
|
61
60
|
}).description('记忆库'),
|
|
62
61
|
koishi_1.Schema.object({
|
|
63
62
|
enableSelfGag: koishi_1.Schema.boolean().default(true).description('启用口球娱乐:普通成员对自己使用 mute/gag 时随机 1~60 分钟禁言。'),
|
package/lib/service.d.ts
CHANGED
|
@@ -122,7 +122,6 @@ export declare class QQGroupManagerService extends Service {
|
|
|
122
122
|
private searchMemory;
|
|
123
123
|
private decoratePromptWithMemory;
|
|
124
124
|
private handleMemoryCommand;
|
|
125
|
-
private _syncMemoryConfig;
|
|
126
125
|
private isImageSendableUrl;
|
|
127
126
|
describeImageMessage(session?: Session): {
|
|
128
127
|
hasCqImage: boolean;
|
package/lib/service.js
CHANGED
|
@@ -36,26 +36,6 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
36
36
|
this._logger = ctx.logger('nestim-qq-group-manager');
|
|
37
37
|
ctx.on('message', (session) => this.handleGroupModeration(session));
|
|
38
38
|
ctx.on('guild-member-request', (session) => this.handleGuildMemberRequest(session));
|
|
39
|
-
// 让配置里的 memoryContent 编辑框与 Memory.md 双向同步
|
|
40
|
-
this._syncMemoryConfig().catch((error) => this._logger.warn(`[memory] sync failed: ${String(error)}`));
|
|
41
|
-
}
|
|
42
|
-
async _syncMemoryConfig() {
|
|
43
|
-
try {
|
|
44
|
-
const fileContent = await this.readMemoryFile();
|
|
45
|
-
const cfgContent = typeof this.config.memoryContent === 'string' ? this.config.memoryContent : '';
|
|
46
|
-
if (cfgContent && cfgContent !== fileContent) {
|
|
47
|
-
// 用户在配置编辑框里改过内容 → 写回文件
|
|
48
|
-
await this.writeMemoryFile(cfgContent);
|
|
49
|
-
this._logger.info('[memory] 配置内容已写回 Memory.md');
|
|
50
|
-
}
|
|
51
|
-
else if (!cfgContent) {
|
|
52
|
-
// 首次:把文件内容填充到编辑框,便于查看/编辑
|
|
53
|
-
this.config.memoryContent = fileContent;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
this._logger.warn(`[memory] sync failed: ${String(error)}`);
|
|
58
|
-
}
|
|
59
39
|
}
|
|
60
40
|
isPlatformAllowed(platform) {
|
|
61
41
|
return !!platform && this.config.platformFilter.includes(platform);
|
|
@@ -2889,16 +2869,19 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
2889
2869
|
return `${Date.now().toString(36).toUpperCase().slice(-6)}`;
|
|
2890
2870
|
}
|
|
2891
2871
|
parseJoinRequestReviewText(text) {
|
|
2892
|
-
|
|
2872
|
+
let source = String(text).trim();
|
|
2893
2873
|
if (!source)
|
|
2894
2874
|
return null;
|
|
2895
|
-
|
|
2875
|
+
// 去掉可能的命令前缀(如 [bot])
|
|
2876
|
+
source = source.replace(/^\[[^\]]+\]\s*/, '').trim();
|
|
2877
|
+
// 支持 同意/拒绝 [编号] [理由](编号可省略)
|
|
2878
|
+
const approveMatch = source.match(/^(同意|通过|放行|批准|approve|ok|yes)\s*(?:入群|申请)?\s*#?([A-Za-z0-9]{4,12})?\s*([\s\S]*)$/i);
|
|
2896
2879
|
if (approveMatch) {
|
|
2897
|
-
return { approve: true, code: approveMatch[2].toUpperCase(), reason: (approveMatch[3] || '').trim() };
|
|
2880
|
+
return { approve: true, code: approveMatch[2] ? approveMatch[2].toUpperCase() : '', reason: (approveMatch[3] || '').trim() };
|
|
2898
2881
|
}
|
|
2899
|
-
const rejectMatch = source.match(/^(拒绝|驳回|拒|deny|reject)\s*(?:入群|申请)?\s*#?([A-Za-z0-9]{4,12})(
|
|
2882
|
+
const rejectMatch = source.match(/^(拒绝|驳回|拒|deny|reject|no|refuse)\s*(?:入群|申请)?\s*#?([A-Za-z0-9]{4,12})?\s*([\s\S]*)$/i);
|
|
2900
2883
|
if (rejectMatch) {
|
|
2901
|
-
return { approve: false, code: rejectMatch[2].toUpperCase(), reason: (rejectMatch[3] || '').trim() };
|
|
2884
|
+
return { approve: false, code: rejectMatch[2] ? rejectMatch[2].toUpperCase() : '', reason: (rejectMatch[3] || '').trim() };
|
|
2902
2885
|
}
|
|
2903
2886
|
return null;
|
|
2904
2887
|
}
|
|
@@ -2941,10 +2924,9 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
2941
2924
|
catch { /* ignore */ }
|
|
2942
2925
|
return null;
|
|
2943
2926
|
}
|
|
2944
|
-
buildJoinRequestPrompt(
|
|
2927
|
+
buildJoinRequestPrompt(applicant, extra) {
|
|
2945
2928
|
const lines = [
|
|
2946
2929
|
'检测到新的入群申请,请管理员审核:',
|
|
2947
|
-
`审核编号:${code}`,
|
|
2948
2930
|
`QQ号:${applicant.userId}`,
|
|
2949
2931
|
];
|
|
2950
2932
|
if (extra?.nickname)
|
|
@@ -2955,12 +2937,9 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
2955
2937
|
lines.push(`年龄:${extra.age}`);
|
|
2956
2938
|
if (extra?.level)
|
|
2957
2939
|
lines.push(`QQ等级:${extra.level}`);
|
|
2958
|
-
if (extra?.avatarUrl)
|
|
2959
|
-
lines.push(`头像:${extra.avatarUrl}`);
|
|
2960
2940
|
lines.push(`验证信息:${applicant.comment || '(无)'}`);
|
|
2961
|
-
lines.push(`同意:${this.config.command}
|
|
2962
|
-
lines.push(`拒绝:${this.config.command}
|
|
2963
|
-
lines.push(`快捷回复:同意入群 ${code} / 拒绝入群 ${code}`);
|
|
2941
|
+
lines.push(`同意:${this.config.command} 同意 [理由]`);
|
|
2942
|
+
lines.push(`拒绝:${this.config.command} 拒绝 [理由]`);
|
|
2964
2943
|
return lines.join('\n');
|
|
2965
2944
|
}
|
|
2966
2945
|
async handleGuildMemberRequest(session) {
|
|
@@ -3008,28 +2987,44 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
3008
2987
|
avatarUrl: `https://q1.qlogo.cn/g?b=qq&nk=${encodeURIComponent(session.userId)}&s=640`,
|
|
3009
2988
|
};
|
|
3010
2989
|
}
|
|
3011
|
-
const prompt = this.buildJoinRequestPrompt(
|
|
2990
|
+
const prompt = this.buildJoinRequestPrompt(item, extra);
|
|
3012
2991
|
try {
|
|
3013
|
-
|
|
3014
|
-
|
|
2992
|
+
if (extra?.avatarUrl) {
|
|
2993
|
+
// 直接把头像图片发到群里,而不是给链接
|
|
2994
|
+
await session.send([koishi_1.h.text(this.styleText(prompt)), koishi_1.h.image(extra.avatarUrl)]);
|
|
2995
|
+
}
|
|
2996
|
+
else {
|
|
2997
|
+
await session.send(this.styleText(prompt));
|
|
2998
|
+
}
|
|
2999
|
+
this.logCommandResult('join-request', { ok: true, message: 'join request notice sent' }, session, { target: item.userId });
|
|
3015
3000
|
}
|
|
3016
3001
|
catch (error) {
|
|
3017
|
-
this.logCommandResult('join-request', { ok: false, message: `join request notice failed: ${String(error)}` }, session, {
|
|
3002
|
+
this.logCommandResult('join-request', { ok: false, message: `join request notice failed: ${String(error)}` }, session, { target: item.userId });
|
|
3018
3003
|
}
|
|
3019
3004
|
}
|
|
3020
3005
|
async reviewJoinRequestDecision(session, code, approve, reason) {
|
|
3021
3006
|
this.cleanupPendingJoinRequests();
|
|
3022
|
-
|
|
3023
|
-
const
|
|
3024
|
-
if (
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3007
|
+
let item;
|
|
3008
|
+
const key = (code || '').trim().toUpperCase();
|
|
3009
|
+
if (key) {
|
|
3010
|
+
item = this.pendingJoinRequests.get(key);
|
|
3011
|
+
if (!item)
|
|
3012
|
+
return { ok: false, message: `未找到审核编号 ${key},可能已处理或已过期。` };
|
|
3013
|
+
if (session.guildId !== item.guildId)
|
|
3014
|
+
return { ok: false, message: `审核编号 ${key} 不属于当前群。` };
|
|
3015
|
+
}
|
|
3016
|
+
else {
|
|
3017
|
+
// 无编号:取当前群最近一条待审核申请
|
|
3018
|
+
const candidates = [...this.pendingJoinRequests.values()].filter((x) => x.guildId === session.guildId);
|
|
3019
|
+
if (!candidates.length)
|
|
3020
|
+
return { ok: false, message: '当前没有待审核的入群申请。' };
|
|
3021
|
+
item = candidates[candidates.length - 1];
|
|
3022
|
+
}
|
|
3028
3023
|
const plan = {
|
|
3029
3024
|
action: approve ? 'approve-join-request' : 'reject-join-request',
|
|
3030
3025
|
groupId: item.guildId,
|
|
3031
3026
|
targetId: item.userId,
|
|
3032
|
-
reason:
|
|
3027
|
+
reason: `${reason || ''}`,
|
|
3033
3028
|
};
|
|
3034
3029
|
if (this.config.dryRun)
|
|
3035
3030
|
return this.createDryRunResult(plan);
|
|
@@ -3039,13 +3034,13 @@ class QQGroupManagerService extends koishi_1.Service {
|
|
|
3039
3034
|
}
|
|
3040
3035
|
try {
|
|
3041
3036
|
await bot.handleGuildMemberRequest(item.flag, approve, reason || '');
|
|
3042
|
-
this.pendingJoinRequests.delete(
|
|
3037
|
+
this.pendingJoinRequests.delete(item.code);
|
|
3043
3038
|
this.pendingJoinRequestFlags.delete(item.flag);
|
|
3044
3039
|
return {
|
|
3045
3040
|
ok: true,
|
|
3046
3041
|
message: approve
|
|
3047
|
-
? `已放行入群申请:${item.userId}
|
|
3048
|
-
: `已拒绝入群申请:${item.userId}
|
|
3042
|
+
? `已放行入群申请:${item.userId}`
|
|
3043
|
+
: `已拒绝入群申请:${item.userId}`,
|
|
3049
3044
|
plan,
|
|
3050
3045
|
};
|
|
3051
3046
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestim/koishi-plugin-qq-group-manager",
|
|
3
3
|
"description": "Koishi QQ群管理插件(OneBot/LLOneBot):群管命令、权限校验、图片菜单、状态卡片、复读、AI群聊回复、记忆库、识图与自动管控(禁言/踢人)。",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|