@nestim/koishi-plugin-qqgm-chat-style 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/index.js +266 -204
- package/lib/registry.js +14 -2
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -4,6 +4,20 @@ Koishi 聊天风格插件,配合 [`@nestim/koishi-plugin-qq-group-manager`](ht
|
|
|
4
4
|
|
|
5
5
|
**一个主风格决定它是什么人,若干追加风格按概率随机加味,主风格永远优先。**
|
|
6
6
|
|
|
7
|
+
## 相关插件
|
|
8
|
+
|
|
9
|
+
| 插件 | 说明 |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [`@nestim/koishi-plugin-qq-group-manager`](https://github.com/Miseat/koishi-plugin-qq-group-manager) | **主插件**:群管、自动管控、AI 回复、记忆库、识图、定时提醒 |
|
|
12
|
+
| **`@nestim/koishi-plugin-qqgm-chat-style`** | 本插件:聊天风格 |
|
|
13
|
+
| [`@nestim/koishi-plugin-qqgm-meme`](https://github.com/Miseat/koishi-plugin-qqgm-meme) | 表情包:按情绪标签抽图,按概率补发 |
|
|
14
|
+
| [`@nestim/koishi-plugin-qqgm-private`](https://github.com/Miseat/koishi-plugin-qqgm-private) | 私聊:AI 自动回复 + 定时主动私发 |
|
|
15
|
+
|
|
16
|
+
配套 skill(可直接放进 `data/chat-styles/`):
|
|
17
|
+
|
|
18
|
+
- [northern-netizen-chat](https://github.com/Miseat/northern-netizen-chat) —— 中文北方网友,短句、带刺、归因降维
|
|
19
|
+
- [naruto-mobile-chat](https://github.com/Miseat/naruto-mobile-chat) —— 火影忍者手游圈口癖,适合当追加风格
|
|
20
|
+
|
|
7
21
|
## 特性
|
|
8
22
|
|
|
9
23
|
| 能力 | 说明 |
|
package/index.js
CHANGED
|
@@ -1,204 +1,266 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* qqgm-chat-style:依附于 @nestim/koishi-plugin-qq-group-manager 的聊天风格插件。
|
|
3
|
-
*
|
|
4
|
-
* 做三件事:
|
|
5
|
-
* 1. 读取一个主风格 .skill,整段并入 AI 系统提示词(决定基本人格与说话方式);
|
|
6
|
-
* 2. 读取任意多个追加风格,按各自概率随机叠加,只作“加味”,不得压过主风格;
|
|
7
|
-
* 3. 发送前统一做文本规范:去 Markdown/CQ/URL、删禁词、限行数/字数。
|
|
8
|
-
*
|
|
9
|
-
* 不注册群指令、不碰触发与上下文;所有随机都在插件内部完成。
|
|
10
|
-
*/
|
|
11
|
-
const path = require('node:path');
|
|
12
|
-
const { Schema } = require('koishi');
|
|
13
|
-
const { createOutputFilter } = require('./lib/filter');
|
|
14
|
-
const {
|
|
15
|
-
RollCache,
|
|
16
|
-
buildStyleSet,
|
|
17
|
-
listAvailable,
|
|
18
|
-
primarySection,
|
|
19
|
-
extraSection,
|
|
20
|
-
} = require('./lib/registry');
|
|
21
|
-
|
|
22
|
-
const name = 'qqgm-chat-style';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Schema.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.description('
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
.description('
|
|
82
|
-
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
logger.warn(
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
1
|
+
/**
|
|
2
|
+
* qqgm-chat-style:依附于 @nestim/koishi-plugin-qq-group-manager 的聊天风格插件。
|
|
3
|
+
*
|
|
4
|
+
* 做三件事:
|
|
5
|
+
* 1. 读取一个主风格 .skill,整段并入 AI 系统提示词(决定基本人格与说话方式);
|
|
6
|
+
* 2. 读取任意多个追加风格,按各自概率随机叠加,只作“加味”,不得压过主风格;
|
|
7
|
+
* 3. 发送前统一做文本规范:去 Markdown/CQ/URL、删禁词、限行数/字数。
|
|
8
|
+
*
|
|
9
|
+
* 不注册群指令、不碰触发与上下文;所有随机都在插件内部完成。
|
|
10
|
+
*/
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
const { Schema } = require('koishi');
|
|
13
|
+
const { createOutputFilter } = require('./lib/filter');
|
|
14
|
+
const {
|
|
15
|
+
RollCache,
|
|
16
|
+
buildStyleSet,
|
|
17
|
+
listAvailable,
|
|
18
|
+
primarySection,
|
|
19
|
+
extraSection,
|
|
20
|
+
} = require('./lib/registry');
|
|
21
|
+
|
|
22
|
+
const name = 'qqgm-chat-style';
|
|
23
|
+
/**
|
|
24
|
+
* 不写 inject。
|
|
25
|
+
*
|
|
26
|
+
* 原因:主插件把 qqgmAiHooks 用 `ctx.root.set()` 挂到 root,它不是 Koishi 的 Service,
|
|
27
|
+
* 而 inject 只解析 Service——写了 inject 反而会让 Koishi 一直等一个永远不会出现的服务,
|
|
28
|
+
* 配置页因此显示“未安装/未就绪”。这里改为运行时探测:拿不到就安静退出并给出日志。
|
|
29
|
+
*/
|
|
30
|
+
const inject = [];
|
|
31
|
+
|
|
32
|
+
const DEFAULT_BANNED = [
|
|
33
|
+
'首先',
|
|
34
|
+
'其次',
|
|
35
|
+
'综上所述',
|
|
36
|
+
'值得注意的是',
|
|
37
|
+
'总的来说',
|
|
38
|
+
'希望这对你有帮助',
|
|
39
|
+
'作为AI',
|
|
40
|
+
'作为一个AI',
|
|
41
|
+
'你细品',
|
|
42
|
+
'懂得都懂',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** 追加风格:一条一条往下加,字段名尽量短,配置页只剩“几个字 + 一个框”。 */
|
|
46
|
+
const ExtraStyle = Schema.object({
|
|
47
|
+
style: Schema.string().default('')
|
|
48
|
+
.description('技能名,填 data/chat-styles 下的目录名,例如 naruto-mobile-chat。留空表示这条不生效。'),
|
|
49
|
+
probability: Schema.percent().default(0.3)
|
|
50
|
+
.description('每轮触发的概率(内部随机数)。0 等于永远不叠,1 等于每次都叠。默认 0.3 = 三成概率。'),
|
|
51
|
+
enabled: Schema.boolean().default(true)
|
|
52
|
+
.description('启用这条追加风格。勾选 = 生效,取消勾选 = 暂时停用(配置保留,随时可再勾回来)。'),
|
|
53
|
+
maxChars: Schema.natural().min(10).max(500).default(60)
|
|
54
|
+
.description('叠加时的字数上限;留默认即可,通常沿用主风格。'),
|
|
55
|
+
maxLines: Schema.natural().min(1).max(10).default(3)
|
|
56
|
+
.description('叠加时的行数上限;留默认即可。'),
|
|
57
|
+
}).description('追加风格');
|
|
58
|
+
|
|
59
|
+
const Config = Schema.intersect([
|
|
60
|
+
Schema.object({
|
|
61
|
+
primaryStyle: Schema.string().default('')
|
|
62
|
+
.description('填技能目录名,例如 northern-netizen-chat。留空则用目录里第一个可用 skill。'),
|
|
63
|
+
skillDir: Schema.string().default('data/chat-styles')
|
|
64
|
+
.description('风格目录(相对 Koishi 数据目录),一般不用改。'),
|
|
65
|
+
}).description('主风格'),
|
|
66
|
+
Schema.object({
|
|
67
|
+
extraStyles: Schema.array(ExtraStyle).role('table').default([])
|
|
68
|
+
.description('可无限添加;每轮按各自概率随机叠加,只加味道,不盖主风格。'),
|
|
69
|
+
}).description('追加风格'),
|
|
70
|
+
Schema.object({
|
|
71
|
+
maxChars: Schema.natural().min(10).max(500).default(60)
|
|
72
|
+
.description('单条回复最大字符数,超出按标点截断。'),
|
|
73
|
+
maxLines: Schema.natural().min(1).max(10).default(3)
|
|
74
|
+
.description('单条回复最大行数,多出的行会被丢弃。'),
|
|
75
|
+
bannedPatterns: Schema.array(String).role('table').default(DEFAULT_BANNED)
|
|
76
|
+
.description('命中即从输出中删除的词/短语。'),
|
|
77
|
+
replacements: Schema.array(Schema.object({
|
|
78
|
+
from: Schema.string().required(),
|
|
79
|
+
to: Schema.string().default(''),
|
|
80
|
+
})).role('table').default([{ from: '……', to: '...' }])
|
|
81
|
+
.description('输出中的字面替换。'),
|
|
82
|
+
glossary: Schema.array(Schema.object({
|
|
83
|
+
term: Schema.string().required(),
|
|
84
|
+
meaning: Schema.string().required(),
|
|
85
|
+
})).role('table').default([])
|
|
86
|
+
.description('群聊术语解释,避免缩写歧义。'),
|
|
87
|
+
stripMarkdown: Schema.boolean().default(true)
|
|
88
|
+
.description('去掉 Markdown 标记、链接、代码块。'),
|
|
89
|
+
stripTrailingPeriod: Schema.boolean().default(true)
|
|
90
|
+
.description('去掉回复末尾的句号。'),
|
|
91
|
+
debug: Schema.boolean().default(false)
|
|
92
|
+
.description('日志里打印风格组合、随机结果与过滤细节。'),
|
|
93
|
+
}).description('输出规范'),
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
function resolveSkillRoot(ctx, config) {
|
|
97
|
+
const base = ctx.baseDir || process.cwd();
|
|
98
|
+
return path.resolve(base, String(config.skillDir || 'data/chat-styles'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 运行时挂载。
|
|
103
|
+
*
|
|
104
|
+
* 主插件的 qqgmAiHooks 不是 Koishi Service(它是 ctx.root.set 上去的普通对象),
|
|
105
|
+
* 所以不能用 inject 声明依赖——那样 Koishi 会一直等一个永不到来的服务。
|
|
106
|
+
* 这里改成:启动时探测一次,拿不到就在 ready 时再试一次。
|
|
107
|
+
*/
|
|
108
|
+
function apply(ctx, config) {
|
|
109
|
+
const logger = ctx.logger('qqgm-chat-style');
|
|
110
|
+
const root = resolveSkillRoot(ctx, config);
|
|
111
|
+
|
|
112
|
+
let set;
|
|
113
|
+
try {
|
|
114
|
+
set = buildStyleSet(root, config);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.warn(`读取风格目录失败:${String(error)}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const warning of set.warnings) logger.warn(warning);
|
|
120
|
+
|
|
121
|
+
if (!set.primary) {
|
|
122
|
+
logger.warn(`未找到主风格 skill(目录:${root}),插件不启用`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 只用 get() 读取:直接访问未注册属性会触发 Koishi 的 "not registered" 告警
|
|
127
|
+
const findHooks = () => {
|
|
128
|
+
const read = (target, key) => {
|
|
129
|
+
try {
|
|
130
|
+
return target && typeof target.get === 'function' ? target.get(key) : null;
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return read(ctx.root, 'qqgmAiHooks')
|
|
136
|
+
|| read(ctx, 'qqgmAiHooks')
|
|
137
|
+
|| (read(ctx.root, 'nestimQqGroupManager') || {}).aiHooks
|
|
138
|
+
|| (read(ctx, 'nestimQqGroupManager') || {}).aiHooks
|
|
139
|
+
|| null;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// [bot] style —— 列出可用风格,省得用户去翻文件目录
|
|
143
|
+
ctx.command('qqgm-style', '列出可用的聊天风格')
|
|
144
|
+
.alias('style')
|
|
145
|
+
.action(() => {
|
|
146
|
+
const available = listAvailable(root);
|
|
147
|
+
if (!available.length) return `目录里没有可用风格:${root}`;
|
|
148
|
+
const lines = ['可用风格:'];
|
|
149
|
+
for (const item of available) {
|
|
150
|
+
const mark = item.id === set.primary.id ? ' ← 当前主风格' : '';
|
|
151
|
+
lines.push(item.ok ? `- ${item.id}(${item.name})${mark}` : `- ${item.id}(不可用:${item.reason})`);
|
|
152
|
+
}
|
|
153
|
+
if (set.extras.length) {
|
|
154
|
+
lines.push('', '当前追加风格:');
|
|
155
|
+
for (const extra of set.extras) {
|
|
156
|
+
lines.push(`- ${extra.id} · ${Math.round(extra.probability * 100)}%`);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
lines.push('', '当前追加风格:(无)');
|
|
160
|
+
}
|
|
161
|
+
if (set.disabled && set.disabled.length) {
|
|
162
|
+
lines.push('', '已关闭的追加风格:');
|
|
163
|
+
for (const item of set.disabled) {
|
|
164
|
+
lines.push(`- ${item.style}(enabled=false,不会叠加)`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return lines.join('\n');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const rolls = new RollCache();
|
|
171
|
+
const offs = [];
|
|
172
|
+
let mounted = false;
|
|
173
|
+
let tried = false;
|
|
174
|
+
|
|
175
|
+
function mount(hooks) {
|
|
176
|
+
mounted = true;
|
|
177
|
+
|
|
178
|
+
// 主风格:整段并入,任何情况下都在
|
|
179
|
+
offs.push(hooks.registerPromptSection(async (hookCtx) => {
|
|
180
|
+
const parts = [primarySection(set.primary, config, { direct: !!hookCtx?.session?.isDirect })];
|
|
181
|
+
for (const extra of set.extras) {
|
|
182
|
+
if (extra.probability <= 0) continue;
|
|
183
|
+
if (!rolls.roll(hookCtx, extra.id, extra.probability)) continue;
|
|
184
|
+
if (config.debug) {
|
|
185
|
+
logger.info(`[style] 追加风格命中 ${extra.id} @ ${hookCtx?.groupId || 'direct'}`);
|
|
186
|
+
}
|
|
187
|
+
parts.push(extraSection(extra, extra.probability));
|
|
188
|
+
}
|
|
189
|
+
return parts.join('\n\n');
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
// 输出规范:字数上限取“本轮真正叠上的最宽值”,避免追加风格被主风格的长度截断
|
|
193
|
+
const baseFilter = createOutputFilter(config, logger);
|
|
194
|
+
offs.push(hooks.registerOutputFilter((text, hookCtx) => {
|
|
195
|
+
let limit = null;
|
|
196
|
+
for (const extra of set.extras) {
|
|
197
|
+
if (extra.probability <= 0) continue;
|
|
198
|
+
if (!rolls.roll(hookCtx, extra.id, extra.probability)) continue;
|
|
199
|
+
limit = limit
|
|
200
|
+
? {
|
|
201
|
+
maxChars: Math.max(limit.maxChars, extra.minChars),
|
|
202
|
+
maxLines: Math.max(limit.maxLines, extra.maxLines),
|
|
203
|
+
}
|
|
204
|
+
: { maxChars: extra.minChars, maxLines: extra.maxLines };
|
|
205
|
+
}
|
|
206
|
+
if (!limit) return baseFilter(text);
|
|
207
|
+
const merged = {
|
|
208
|
+
...config,
|
|
209
|
+
maxChars: Math.max(Number(config.maxChars) || 60, limit.maxChars),
|
|
210
|
+
maxLines: Math.max(Number(config.maxLines) || 3, limit.maxLines),
|
|
211
|
+
};
|
|
212
|
+
return createOutputFilter(merged, logger)(text);
|
|
213
|
+
}));
|
|
214
|
+
|
|
215
|
+
const extraDesc = set.extras.length
|
|
216
|
+
? set.extras.map((item) => `${item.id}(${Math.round(item.probability * 100)}%)`).join('、')
|
|
217
|
+
: '无';
|
|
218
|
+
logger.info(
|
|
219
|
+
`主风格「${set.primary.name}」(${set.primary.id}):正文 ${set.primary.skill.body.length} 字 | 追加风格:${extraDesc}`,
|
|
220
|
+
);
|
|
221
|
+
if (config.debug) {
|
|
222
|
+
logger.info(
|
|
223
|
+
`[debug] skillDir=${root} 可用=${set.available.filter((i) => i.ok).map((i) => i.id).join(',')}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function tryMount() {
|
|
229
|
+
if (mounted) return true;
|
|
230
|
+
const hooks = findHooks();
|
|
231
|
+
if (!hooks || typeof hooks.registerPromptSection !== 'function') {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
if (Number(hooks.version) < 1) {
|
|
235
|
+
logger.warn(`aiHooks 版本过低(${hooks.version}),风格插件不启用`);
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
mount(hooks);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!tryMount()) {
|
|
243
|
+
// 主插件可能还没把扩展点挂上,等 ready 再试一次
|
|
244
|
+
ctx.on('ready', () => {
|
|
245
|
+
if (mounted) return;
|
|
246
|
+
if (!tryMount()) {
|
|
247
|
+
tried = true;
|
|
248
|
+
logger.warn(
|
|
249
|
+
'未找到 qqgmAiHooks 扩展点:请确认已启用并与本插件同时加载 @nestim/koishi-plugin-qq-group-manager(>= 1.2.5)',
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
ctx.on('dispose', () => {
|
|
256
|
+
for (const off of offs) {
|
|
257
|
+
try {
|
|
258
|
+
off();
|
|
259
|
+
} catch { /* 忽略 */ }
|
|
260
|
+
}
|
|
261
|
+
offs.length = 0;
|
|
262
|
+
mounted = false;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = { name, inject, Config, apply };
|
package/lib/registry.js
CHANGED
|
@@ -102,13 +102,25 @@ function buildStyleSet(rootDir, config = {}) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const extras = [];
|
|
105
|
+
const disabled = [];
|
|
105
106
|
const seen = new Set(primary ? [primary.id] : []);
|
|
106
107
|
const rawExtras = Array.isArray(config.extraStyles) ? config.extraStyles : [];
|
|
107
108
|
rawExtras.forEach((entry, index) => {
|
|
108
109
|
if (!entry || typeof entry !== 'object') return;
|
|
109
|
-
if (entry.enabled === false) return;
|
|
110
110
|
const wanted = String(entry.style || '').trim();
|
|
111
111
|
if (!wanted) return;
|
|
112
|
+
|
|
113
|
+
// enabled 未填(undefined / null)一律视为启用,只有显式 false 才算关闭。
|
|
114
|
+
// 以前这里直接 return 且不记日志,用户完全看不出为什么没生效。
|
|
115
|
+
const isOff = entry.enabled === false;
|
|
116
|
+
if (isOff) {
|
|
117
|
+
disabled.push({ index, style: wanted });
|
|
118
|
+
warnings.push(
|
|
119
|
+
`追加风格 #${index + 1}「${wanted}」已关闭(enabled=false),本次不会叠加;想启用请把该项勾上`,
|
|
120
|
+
);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
112
124
|
const found = pickByName(available, wanted);
|
|
113
125
|
if (!found) {
|
|
114
126
|
warnings.push(`追加风格 #${index + 1}「${wanted}」不可用,已跳过`);
|
|
@@ -133,7 +145,7 @@ function buildStyleSet(rootDir, config = {}) {
|
|
|
133
145
|
});
|
|
134
146
|
});
|
|
135
147
|
|
|
136
|
-
return { primary, extras, warnings, available };
|
|
148
|
+
return { primary, extras, disabled, warnings, available };
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
/** 主风格段:与旧版行为一致,整段进入提示词。 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestim/koishi-plugin-qqgm-chat-style",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Koishi 聊天风格插件(依附 @nestim/koishi-plugin-qq-group-manager):一个主风格决定人格,多个追加风格按概率随机加味,主风格永远优先。",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,22 +19,20 @@
|
|
|
19
19
|
"qqgm"
|
|
20
20
|
],
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"koishi": "^4.18.7"
|
|
22
|
+
"koishi": "^4.18.7",
|
|
23
|
+
"@nestim/koishi-plugin-qq-group-manager": ">=1.2.5"
|
|
23
24
|
},
|
|
24
25
|
"peerDependenciesMeta": {
|
|
25
26
|
"koishi": {
|
|
26
27
|
"optional": false
|
|
28
|
+
},
|
|
29
|
+
"@nestim/koishi-plugin-qq-group-manager": {
|
|
30
|
+
"optional": false
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"koishi": {
|
|
30
34
|
"description": "聊天风格:主风格 + 多条按概率随机叠加的追加风格",
|
|
31
|
-
"category": "manage"
|
|
32
|
-
"service": {
|
|
33
|
-
"required": [
|
|
34
|
-
"qqgmAiHooks"
|
|
35
|
-
],
|
|
36
|
-
"optional": []
|
|
37
|
-
}
|
|
35
|
+
"category": "manage"
|
|
38
36
|
},
|
|
39
37
|
"scripts": {
|
|
40
38
|
"test": "node --test \"test/*.test.mjs\""
|