koishi-plugin-chatluna-affinity 0.0.3 → 0.0.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/affinity-store.js +22 -5
- package/lib/index.d.ts +3 -0
- package/lib/index.js +140 -1
- package/lib/middleware.js +5 -3
- package/lib/schema.js +6 -3
- package/lib/tools.js +38 -3
- package/package.json +2 -2
- package/readme.md +8 -1
package/lib/affinity-store.js
CHANGED
|
@@ -46,6 +46,7 @@ function extendModel(ctx) {
|
|
|
46
46
|
platform: 'string',
|
|
47
47
|
selfId: { type: 'string', nullable: true },
|
|
48
48
|
userId: 'string',
|
|
49
|
+
nickname: { type: 'string', nullable: true },
|
|
49
50
|
affinity: 'integer',
|
|
50
51
|
affinityInited: 'boolean',
|
|
51
52
|
relation: { type: 'string', nullable: true },
|
|
@@ -142,7 +143,15 @@ function createAffinityStore(ctx, config, log) {
|
|
|
142
143
|
const existing = await ctx.database.get(MODEL_NAME, { id });
|
|
143
144
|
const current = existing?.[0];
|
|
144
145
|
const now = new Date();
|
|
145
|
-
const
|
|
146
|
+
const manual = findManualRelationship(identity.platform, identity.userId);
|
|
147
|
+
let relationText;
|
|
148
|
+
if (relation === undefined || relation === null) {
|
|
149
|
+
relationText = manual?.relation ?? current?.relation ?? '';
|
|
150
|
+
} else {
|
|
151
|
+
const trimmed = String(relation).trim();
|
|
152
|
+
relationText = trimmed ? trimmed : manual?.relation ?? current?.relation ?? '';
|
|
153
|
+
}
|
|
154
|
+
const nickname = seed?.nickname || seed?.authorNickname || seed?.session?.nickname || seed?.session?.author?.nickname || seed?.session?.author?.name || '';
|
|
146
155
|
|
|
147
156
|
const payload = {
|
|
148
157
|
id,
|
|
@@ -151,11 +160,16 @@ function createAffinityStore(ctx, config, log) {
|
|
|
151
160
|
userId: identity.userId,
|
|
152
161
|
affinity: typeof value === 'number' ? value : current?.affinity ?? defaultInitialValue(),
|
|
153
162
|
affinityInited: inited ?? current?.affinityInited ?? false,
|
|
154
|
-
relation: relationText
|
|
163
|
+
relation: relationText,
|
|
164
|
+
nickname: nickname || current?.nickname || manual?.relationNickname || '',
|
|
155
165
|
updatedAt: now,
|
|
156
166
|
relationUpdatedAt: relationText !== undefined ? now : current?.relationUpdatedAt ?? now
|
|
157
167
|
};
|
|
158
168
|
|
|
169
|
+
if (nickname && (!current || nickname !== current.nickname)) {
|
|
170
|
+
payload.nickname = nickname;
|
|
171
|
+
}
|
|
172
|
+
|
|
159
173
|
await ctx.database.upsert(MODEL_NAME, [payload]);
|
|
160
174
|
return payload;
|
|
161
175
|
};
|
|
@@ -271,14 +285,17 @@ function createAffinityStore(ctx, config, log) {
|
|
|
271
285
|
: manual?.initialAffinity ?? rollInitial();
|
|
272
286
|
const initial = clampFn(base, config.min, config.max);
|
|
273
287
|
const level = resolveLevelByAffinity(initial);
|
|
274
|
-
|
|
288
|
+
const desiredRelation = manual?.relation ?? level?.relation ?? '';
|
|
289
|
+
await save({ platform, userId, selfId: session?.selfId, session }, initial, true, desiredRelation);
|
|
275
290
|
return { affinity: initial, isNew: true };
|
|
276
291
|
}
|
|
277
292
|
|
|
278
293
|
const normalized = clampFn(record.affinity ?? defaultInitialValue(), config.min, config.max);
|
|
279
294
|
if (normalized !== record.affinity) {
|
|
280
295
|
const level = resolveLevelByAffinity(normalized);
|
|
281
|
-
|
|
296
|
+
const manual = findManualRelationship(platform, userId);
|
|
297
|
+
const desiredRelation = manual?.relation ?? level?.relation ?? record.relation ?? '';
|
|
298
|
+
await save({ platform, userId, selfId: session?.selfId, session }, normalized, record.affinityInited, desiredRelation);
|
|
282
299
|
}
|
|
283
300
|
return { affinity: normalized, isNew: false };
|
|
284
301
|
};
|
|
@@ -301,4 +318,4 @@ function createAffinityStore(ctx, config, log) {
|
|
|
301
318
|
};
|
|
302
319
|
}
|
|
303
320
|
|
|
304
|
-
module.exports = { createAffinityStore };
|
|
321
|
+
module.exports = { createAffinityStore, MODEL_NAME };
|
package/lib/index.d.ts
CHANGED
|
@@ -38,7 +38,10 @@ export interface Config {
|
|
|
38
38
|
note: string;
|
|
39
39
|
}>;
|
|
40
40
|
registerAffinityTool: boolean;
|
|
41
|
+
registerBlacklistTool: boolean;
|
|
41
42
|
registerRelationshipTool: boolean;
|
|
43
|
+
rankDefaultLimit: number;
|
|
44
|
+
rankRenderAsImage: boolean;
|
|
42
45
|
}
|
|
43
46
|
export declare const Config: Schema<Config>;
|
|
44
47
|
export declare const inject: string[];
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { h } = require('koishi');
|
|
1
2
|
const { modelSchema } = require('koishi-plugin-chatluna/utils/schema');
|
|
2
3
|
const { ChatLunaPlugin } = require('koishi-plugin-chatluna/services/chat');
|
|
3
4
|
const { getMessageContent } = require('koishi-plugin-chatluna/utils/string');
|
|
@@ -5,7 +6,7 @@ const { getMessageContent } = require('koishi-plugin-chatluna/utils/string');
|
|
|
5
6
|
const { Config, inject, name } = require('./schema');
|
|
6
7
|
const { createLogger } = require('./logger');
|
|
7
8
|
const { renderTemplate } = require('./template');
|
|
8
|
-
const { createAffinityStore } = require('./affinity-store');
|
|
9
|
+
const { createAffinityStore, MODEL_NAME } = require('./affinity-store');
|
|
9
10
|
const { createHistoryManager } = require('./history');
|
|
10
11
|
const { createAffinityCache } = require('./cache');
|
|
11
12
|
const { createAffinityProvider, createRelationshipProvider } = require('./providers');
|
|
@@ -63,6 +64,12 @@ function apply(ctx, config) {
|
|
|
63
64
|
createTool: registry.createRelationshipTool
|
|
64
65
|
});
|
|
65
66
|
}
|
|
67
|
+
if (config.registerBlacklistTool) {
|
|
68
|
+
plugin.registerTool('adjust_blacklist', {
|
|
69
|
+
selector: registry.blacklistSelector,
|
|
70
|
+
createTool: registry.createBlacklistTool
|
|
71
|
+
});
|
|
72
|
+
}
|
|
66
73
|
});
|
|
67
74
|
|
|
68
75
|
const middleware = createAnalysisMiddleware(ctx, config, {
|
|
@@ -76,6 +83,138 @@ function apply(ctx, config) {
|
|
|
76
83
|
});
|
|
77
84
|
|
|
78
85
|
ctx.middleware(middleware);
|
|
86
|
+
|
|
87
|
+
const renderRankImage = async (lines) => {
|
|
88
|
+
const puppeteer = ctx.puppeteer;
|
|
89
|
+
if (!puppeteer?.page) return null;
|
|
90
|
+
const html = `<!DOCTYPE html>
|
|
91
|
+
<html lang="zh-CN">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="utf-8" />
|
|
94
|
+
<style>
|
|
95
|
+
body {
|
|
96
|
+
margin: 0;
|
|
97
|
+
font-family: "Segoe UI", "Helvetica Neue", PingFangSC, "Microsoft Yahei", sans-serif;
|
|
98
|
+
background: #ffffff;
|
|
99
|
+
color: #111111;
|
|
100
|
+
}
|
|
101
|
+
.container {
|
|
102
|
+
padding: 20px 24px;
|
|
103
|
+
}
|
|
104
|
+
h1 {
|
|
105
|
+
font-size: 18px;
|
|
106
|
+
margin: 0 0 16px;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
}
|
|
109
|
+
table {
|
|
110
|
+
border-collapse: collapse;
|
|
111
|
+
width: 100%;
|
|
112
|
+
min-width: 360px;
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
}
|
|
115
|
+
th, td {
|
|
116
|
+
padding: 8px 12px;
|
|
117
|
+
border-bottom: 1px solid #e5e5e5;
|
|
118
|
+
text-align: left;
|
|
119
|
+
white-space: nowrap;
|
|
120
|
+
}
|
|
121
|
+
th {
|
|
122
|
+
background: #f5f7fa;
|
|
123
|
+
font-weight: 600;
|
|
124
|
+
}
|
|
125
|
+
tr:nth-child(odd) td {
|
|
126
|
+
background: #fbfcfe;
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<div class="container" id="rank-root">
|
|
132
|
+
<h1>好感度排行</h1>
|
|
133
|
+
<table>
|
|
134
|
+
<thead>
|
|
135
|
+
<tr><th>排名</th><th>用户名</th><th>关系</th><th>好感度</th></tr>
|
|
136
|
+
</thead>
|
|
137
|
+
<tbody>
|
|
138
|
+
${lines.map((line, index) => {
|
|
139
|
+
const [name, relation, affinity] = line;
|
|
140
|
+
return `<tr><td>${index + 1}</td><td>${name}</td><td>${relation}</td><td>${affinity}</td></tr>`;
|
|
141
|
+
}).join('')}
|
|
142
|
+
</tbody>
|
|
143
|
+
</table>
|
|
144
|
+
</div>
|
|
145
|
+
</body>
|
|
146
|
+
</html>`;
|
|
147
|
+
|
|
148
|
+
let page;
|
|
149
|
+
try {
|
|
150
|
+
page = await puppeteer.page();
|
|
151
|
+
await page.setViewport({ width: 600, height: 200 + lines.length * 32 });
|
|
152
|
+
await page.setContent(html, { waitUntil: 'networkidle0' });
|
|
153
|
+
const element = await page.$('#rank-root');
|
|
154
|
+
if (!element) return null;
|
|
155
|
+
const buffer = await element.screenshot({ omitBackground: false });
|
|
156
|
+
return buffer;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
ctx.logger?.warn?.('排行榜图片渲染失败', error);
|
|
159
|
+
return null;
|
|
160
|
+
} finally {
|
|
161
|
+
try {
|
|
162
|
+
await page?.close();
|
|
163
|
+
} catch (_) {
|
|
164
|
+
// ignore
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
ctx.command('affinity.rank [limit:number] [platform:string] [image]', '查看当前好感度排行', { authority: 1 })
|
|
170
|
+
.alias('好感度排行')
|
|
171
|
+
.usage('affinity.rank [人数] [平台] [image|text]')
|
|
172
|
+
.action(async ({ session }, limitArg, platformArg, imageArg) => {
|
|
173
|
+
const parsedLimit = Number(limitArg);
|
|
174
|
+
const limit = Math.max(1, Math.min(Number.isFinite(parsedLimit) ? parsedLimit : config.rankDefaultLimit, 50));
|
|
175
|
+
|
|
176
|
+
const shouldRenderImage = (() => {
|
|
177
|
+
if (imageArg === undefined) return !!config.rankRenderAsImage;
|
|
178
|
+
const lower = String(imageArg).toLowerCase();
|
|
179
|
+
if (['0', 'false', 'text', 'no', 'n'].includes(lower)) return false;
|
|
180
|
+
if (['1', 'true', 'image', 'img', 'yes', 'y'].includes(lower)) return true;
|
|
181
|
+
return !!config.rankRenderAsImage;
|
|
182
|
+
})();
|
|
183
|
+
|
|
184
|
+
if (shouldRenderImage && (!ctx.puppeteer || typeof ctx.puppeteer.page !== 'function')) {
|
|
185
|
+
return '当前环境未启用 puppeteer,已改为文本模式(可安装 koishi-plugin-puppeteer 或传入 text)。';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const conditions = {};
|
|
189
|
+
const platform = platformArg || session?.platform;
|
|
190
|
+
if (platform) conditions.platform = platform;
|
|
191
|
+
if (session?.selfId) conditions.selfId = session.selfId;
|
|
192
|
+
|
|
193
|
+
const query = ctx.database
|
|
194
|
+
.select(MODEL_NAME)
|
|
195
|
+
.project(['platform', 'selfId', 'userId', 'nickname', 'affinity', 'relation'])
|
|
196
|
+
.orderBy('affinity', 'desc')
|
|
197
|
+
.limit(limit);
|
|
198
|
+
if (Object.keys(conditions).length) query.where(conditions);
|
|
199
|
+
|
|
200
|
+
const rows = await query.execute();
|
|
201
|
+
if (!rows.length) return '当前暂无好感度记录。';
|
|
202
|
+
|
|
203
|
+
const lines = rows.map((row) => {
|
|
204
|
+
const name = row.nickname || row.userId;
|
|
205
|
+
const relation = row.relation || '——';
|
|
206
|
+
return [name, relation, row.affinity];
|
|
207
|
+
});
|
|
208
|
+
const textLines = ['用户名 关系 好感度', ...lines.map((item, index) => `${index + 1}. ${item[0]} ${item[1]} ${item[2]}`)];
|
|
209
|
+
|
|
210
|
+
if (shouldRenderImage) {
|
|
211
|
+
const buffer = await renderRankImage(lines);
|
|
212
|
+
if (buffer) return h.image(buffer, 'image/png');
|
|
213
|
+
ctx.logger?.warn?.('排行榜图片渲染失败或服务缺失,已改为文本输出');
|
|
214
|
+
return textLines.join('\n');
|
|
215
|
+
}
|
|
216
|
+
return textLines.join('\n');
|
|
217
|
+
});
|
|
79
218
|
}
|
|
80
219
|
|
|
81
220
|
module.exports = { apply, Config, inject, name };
|
package/lib/middleware.js
CHANGED
|
@@ -128,6 +128,7 @@ function createAnalysisMiddleware(ctx, config, { store, history, cache, getModel
|
|
|
128
128
|
const jsonCandidate = typeof text === 'string' ? text : String(text ?? '');
|
|
129
129
|
const match = jsonCandidate.match(/\{[\s\S]*\}/);
|
|
130
130
|
let delta = 0;
|
|
131
|
+
const nickname = session?.author?.nickname || session?.author?.name || session?.user?.nickname || session?.user?.name || session?.username || session?.nickname || '';
|
|
131
132
|
if (match) {
|
|
132
133
|
try {
|
|
133
134
|
const parsed = JSON.parse(match[0]);
|
|
@@ -137,7 +138,7 @@ function createAnalysisMiddleware(ctx, config, { store, history, cache, getModel
|
|
|
137
138
|
if (action === 'increase' && delta <= 0) delta = Math.max(1, Math.abs(delta));
|
|
138
139
|
if (action === 'decrease' && delta >= 0) delta = -Math.max(1, Math.abs(delta));
|
|
139
140
|
if (action === 'hold') delta = 0;
|
|
140
|
-
if (debugEnabled) log('info', '模型返回', { raw: parsed, parsedDelta: delta, action, userId: session.userId, platform: session.platform });
|
|
141
|
+
if (debugEnabled) log('info', '模型返回', { raw: parsed, parsedDelta: delta, action, userId: session.userId, platform: session.platform, nickname });
|
|
141
142
|
} catch (error) {
|
|
142
143
|
log('warn', '解析模型响应失败', { text: jsonCandidate, error });
|
|
143
144
|
}
|
|
@@ -149,11 +150,12 @@ function createAnalysisMiddleware(ctx, config, { store, history, cache, getModel
|
|
|
149
150
|
? Math.min(delta, positiveLimit)
|
|
150
151
|
: Math.max(delta, -negativeLimit);
|
|
151
152
|
const nextAffinity = clampValue(oldAffinity + limitedDelta, config.min, config.max);
|
|
153
|
+
|
|
152
154
|
if (nextAffinity !== oldAffinity) {
|
|
153
155
|
const level = store.resolveLevelByAffinity(nextAffinity);
|
|
154
|
-
await store.save({ platform: session.platform, userId: session.userId, selfId: session?.selfId }, nextAffinity, true, level?.relation ?? '');
|
|
156
|
+
await store.save({ platform: session.platform, userId: session.userId, selfId: session?.selfId, session }, nextAffinity, true, level?.relation ?? '');
|
|
155
157
|
cache.set(session.platform, session.userId, nextAffinity);
|
|
156
|
-
log('info', '好感度已更新', { oldAffinity, delta: limitedDelta, nextAffinity, userId: session.userId, platform: session.platform });
|
|
158
|
+
log('info', '好感度已更新', { oldAffinity, delta: limitedDelta, nextAffinity, userId: session.userId, platform: session.platform, nickname });
|
|
157
159
|
} else if (config.useLastAffinity) {
|
|
158
160
|
cache.set(session.platform, session.userId, oldAffinity);
|
|
159
161
|
}
|
package/lib/schema.js
CHANGED
|
@@ -4,7 +4,7 @@ const name = 'chatluna-affinity';
|
|
|
4
4
|
|
|
5
5
|
const inject = {
|
|
6
6
|
required: ['chatluna', 'database'],
|
|
7
|
-
optional: ['
|
|
7
|
+
optional: ['puppeteer']
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
const AffinitySchema = Schema.object({
|
|
@@ -36,7 +36,7 @@ const AffinitySchema = Schema.object({
|
|
|
36
36
|
analysisPrompt: Schema.string()
|
|
37
37
|
.role('textarea')
|
|
38
38
|
.default(
|
|
39
|
-
'你是好感度管家,需要根据上下文评估是否调整好感度。\n-
|
|
39
|
+
'你是好感度管家,需要根据上下文评估是否调整好感度。\n- 仔细阅读最近若干条群聊消息,聚焦语气、内容、语境与互动意图;\n- 只有当用户展现出明确、具体的善意或贡献时(如感谢、称赞、帮助解决问题、分享价值信息、表达真诚关心),才考虑增加;\n- 若用户只是礼貌问候、例行回应、轻度调侃或重复旧话题,可保持不变;\n- 对于冒犯、敷衍、抄袭、频繁打扰、传播负面情绪或破坏氛围的行为,应减少;\n- 增加幅度不超过 {{maxIncreasePerMessage}} ,减少幅度不超过 {{maxDecreasePerMessage}} ,并保持在提供的范围内;\n- 在总结理由时,指明触发增减的具体言辞或行为;\n- 使用 action 表示行为:increase 增加、decrease 减少、hold 保持。\n\n角色设定:{{personaPrompt}}\n\n当前好感度:{{currentAffinity}} (范围 {{minAffinity}} ~ {{maxAffinity}})\n最近 {{historyCount}} 条消息(旧 -> 新):\n{{historyText}}\n\n本次用户消息:\n{{userMessage}}\n\n请仅输出 JSON:{"delta": 整数, "action": "increase|decrease|hold", "reason": "简短中文原因"}。'
|
|
40
40
|
)
|
|
41
41
|
.description('好感度分析主提示词'),
|
|
42
42
|
personaPrompt: Schema.string()
|
|
@@ -44,7 +44,10 @@ const AffinitySchema = Schema.object({
|
|
|
44
44
|
.default('你是一位温暖可靠的伙伴,会根据好感度高低调整语气:好感度越高越亲近,越低越保持礼貌。')
|
|
45
45
|
.description('补充的人设提示词,会注入到分析提示词中'),
|
|
46
46
|
debugLogging: Schema.boolean().default(false).description('输出调试日志'),
|
|
47
|
-
registerAffinityTool: Schema.boolean().default(false).description('注册 ChatLuna 工具:调整好感度')
|
|
47
|
+
registerAffinityTool: Schema.boolean().default(false).description('注册 ChatLuna 工具:调整好感度'),
|
|
48
|
+
registerBlacklistTool: Schema.boolean().default(false).description('注册 ChatLuna 工具:管理黑名单'),
|
|
49
|
+
rankDefaultLimit: Schema.number().default(10).min(1).max(50).description('排行榜默认展示人数'),
|
|
50
|
+
rankRenderAsImage: Schema.boolean().default(false).description('排行榜默认渲染为图片')
|
|
48
51
|
}).description('好感度设置');
|
|
49
52
|
|
|
50
53
|
const RelationshipSchema = Schema.object({
|
package/lib/tools.js
CHANGED
|
@@ -20,7 +20,7 @@ function createAffinityTool(options) {
|
|
|
20
20
|
if (!platform || !userId) return 'Missing platform or user ID. Unable to adjust affinity.';
|
|
21
21
|
const value = options.clamp(input.affinity);
|
|
22
22
|
const level = options.resolveLevelByAffinity(value);
|
|
23
|
-
await options.save({ platform, userId, selfId: session?.selfId }, value, true, level?.relation ?? options.defaultRelation);
|
|
23
|
+
await options.save({ platform, userId, selfId: session?.selfId, session }, value, true, level?.relation ?? options.defaultRelation);
|
|
24
24
|
options.cache.set(platform, userId, value);
|
|
25
25
|
if (level?.relation) {
|
|
26
26
|
return `Affinity for ${platform}/${userId} set to ${value}. Relationship updated to ${level.relation}.`;
|
|
@@ -58,7 +58,7 @@ function createRelationshipTool(options) {
|
|
|
58
58
|
}
|
|
59
59
|
const baseValue = level ? level.min : options.defaultInitial();
|
|
60
60
|
const base = options.clamp(baseValue);
|
|
61
|
-
await options.save({ platform, userId, selfId: session?.selfId }, base, true, relationName);
|
|
61
|
+
await options.save({ platform, userId, selfId: session?.selfId, session }, base, true, relationName);
|
|
62
62
|
options.cache.set(platform, userId, base);
|
|
63
63
|
options.updateRelationshipConfig(userId, relationName, base);
|
|
64
64
|
if (level) {
|
|
@@ -69,6 +69,38 @@ function createRelationshipTool(options) {
|
|
|
69
69
|
})();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function createBlacklistTool(options) {
|
|
73
|
+
return new (class extends StructuredTool {
|
|
74
|
+
constructor() {
|
|
75
|
+
super({});
|
|
76
|
+
this.name = 'adjust_blacklist';
|
|
77
|
+
this.description = 'Add or remove a user from the affinity blacklist.';
|
|
78
|
+
this.schema = z.object({
|
|
79
|
+
action: z.enum(['add', 'remove']).describe('Add or remove the user from blacklist'),
|
|
80
|
+
targetUserId: z.string().describe('Target user ID'),
|
|
81
|
+
platform: z.string().optional().describe('Target platform; defaults to current session'),
|
|
82
|
+
note: z.string().optional().describe('Optional note when adding to blacklist')
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async _call(input, _manager, runnable) {
|
|
86
|
+
const session = runnable?.configurable?.session;
|
|
87
|
+
const platform = input.platform || session?.platform;
|
|
88
|
+
const userId = input.targetUserId;
|
|
89
|
+
if (!platform || !userId) return 'Missing platform or user ID. Unable to adjust blacklist.';
|
|
90
|
+
if (input.action === 'add') {
|
|
91
|
+
options.store.recordBlacklist(platform, userId, input.note ?? 'tool');
|
|
92
|
+
options.cache.clear(platform, userId);
|
|
93
|
+
return `User ${platform}/${userId} added to blacklist.`;
|
|
94
|
+
}
|
|
95
|
+
const removed = options.store.removeBlacklist(platform, userId);
|
|
96
|
+
options.cache.clear(platform, userId);
|
|
97
|
+
return removed
|
|
98
|
+
? `User ${platform}/${userId} removed from blacklist.`
|
|
99
|
+
: `User ${platform}/${userId} not found in blacklist.`;
|
|
100
|
+
}
|
|
101
|
+
})();
|
|
102
|
+
}
|
|
103
|
+
|
|
72
104
|
function createToolRegistry(config, store, cache) {
|
|
73
105
|
const options = {
|
|
74
106
|
clamp: store.clamp,
|
|
@@ -80,6 +112,7 @@ function createToolRegistry(config, store, cache) {
|
|
|
80
112
|
save: store.save,
|
|
81
113
|
cache,
|
|
82
114
|
updateRelationshipConfig: store.updateRelationshipConfig,
|
|
115
|
+
store,
|
|
83
116
|
min: config.min,
|
|
84
117
|
max: config.max
|
|
85
118
|
};
|
|
@@ -87,8 +120,10 @@ function createToolRegistry(config, store, cache) {
|
|
|
87
120
|
return {
|
|
88
121
|
affinitySelector: () => true,
|
|
89
122
|
relationshipSelector: () => true,
|
|
123
|
+
blacklistSelector: () => true,
|
|
90
124
|
createAffinityTool: () => createAffinityTool(options),
|
|
91
|
-
createRelationshipTool: () => createRelationshipTool(options)
|
|
125
|
+
createRelationshipTool: () => createRelationshipTool(options),
|
|
126
|
+
createBlacklistTool: () => createBlacklistTool(options)
|
|
92
127
|
};
|
|
93
128
|
}
|
|
94
129
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-chatluna-affinity",
|
|
3
|
-
"description": "为 ChatLuna 提供{好感度}与{关系}
|
|
4
|
-
"version": "0.0.
|
|
3
|
+
"description": "为 ChatLuna 提供{好感度}与{关系}变量并提供对应的工具调用和低好感自动拉黑功能。仅测试过伪装插件。",
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
package/readme.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- 调整工具:
|
|
14
14
|
- `adjust_affinity` 将好感度设为指定数值,并同步关系区间。
|
|
15
15
|
- `adjust_relationship` 将关系切换为自定义称谓,同时把好感度调至对应区间的下限。
|
|
16
|
+
- `adjust_blacklist` 管理自动拉黑名单,可快速拉黑或解除指定用户。
|
|
16
17
|
- 特殊关系:可为指定用户记录“初始好感度 + 自定义称谓”,工具操作会实时写回配置。
|
|
17
18
|
|
|
18
19
|
## 主要配置
|
|
@@ -35,7 +36,13 @@
|
|
|
35
36
|
| `autoBlacklist` | 自动拉黑记录列表(自动维护,可在控制台查看与编辑) |
|
|
36
37
|
| `relationships` | 特殊关系配置:`userId`、`initialAffinity`、`relation`、`note` |
|
|
37
38
|
| `relationshipAffinityLevels` | 区间 → 称谓映射,默认提供“陌生人/友好/亲近/挚友” |
|
|
38
|
-
| `registerAffinityTool` / `registerRelationshipTool` | 是否注册对应 ChatLuna 工具 |
|
|
39
|
+
| `registerAffinityTool` / `registerRelationshipTool` / `registerBlacklistTool` | 是否注册对应 ChatLuna 工具 |
|
|
40
|
+
| `rankDefaultLimit` | 指令默认展示的排行榜人数 |
|
|
41
|
+
| `rankRenderAsImage` | 指令默认是否渲染排行榜为图片(需 `koishi-plugin-puppeteer`) |
|
|
42
|
+
|
|
43
|
+
## 指令
|
|
44
|
+
|
|
45
|
+
- `affinity.rank [limit] [platform] [image|text]`:查看好感度排行,可指定人数、平台,并选择文本或图片输出(图片模式需 `koishi-plugin-puppeteer`)。
|
|
39
46
|
|
|
40
47
|
## 工具调用
|
|
41
48
|
|