koishi-plugin-chatluna-character 0.0.74 → 0.0.76
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/index.cjs +370 -301
- package/lib/index.d.ts +29 -1
- package/lib/index.mjs +311 -242
- package/package.json +3 -2
package/lib/index.cjs
CHANGED
|
@@ -37,200 +37,17 @@ __export(src_exports, {
|
|
|
37
37
|
name: () => name
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(src_exports);
|
|
40
|
-
var
|
|
40
|
+
var import_koishi7 = require("koishi");
|
|
41
41
|
|
|
42
42
|
// src/plugins/chat.ts
|
|
43
43
|
var import_messages = require("@langchain/core/messages");
|
|
44
|
-
var
|
|
44
|
+
var import_koishi2 = require("koishi");
|
|
45
45
|
var import_count_tokens = require("koishi-plugin-chatluna/llm-core/utils/count_tokens");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
logger = service.logger;
|
|
52
|
-
const modelPool = {};
|
|
53
|
-
const [platform, modelName] = (0, import_count_tokens.parseRawModelName)(config.model);
|
|
54
|
-
await ctx.chatluna.awaitLoadPlatform(platform);
|
|
55
|
-
const globalModel = await ctx.chatluna.createChatModel(
|
|
56
|
-
platform,
|
|
57
|
-
modelName
|
|
58
|
-
);
|
|
59
|
-
logger.info("global model loaded %c", config.model);
|
|
60
|
-
if (config.modelOverride?.length > 0) {
|
|
61
|
-
for (const override of config.modelOverride) {
|
|
62
|
-
modelPool[override.groupId] = (async () => {
|
|
63
|
-
const [platform2, modelName2] = (0, import_count_tokens.parseRawModelName)(override.model);
|
|
64
|
-
await ctx.chatluna.awaitLoadPlatform(platform2);
|
|
65
|
-
const loadedModel = await ctx.chatluna.createChatModel(
|
|
66
|
-
platform2,
|
|
67
|
-
modelName2
|
|
68
|
-
);
|
|
69
|
-
logger.info(
|
|
70
|
-
"override model loaded %c for group %c",
|
|
71
|
-
override.model,
|
|
72
|
-
override.groupId
|
|
73
|
-
);
|
|
74
|
-
modelPool[override.groupId] = Promise.resolve(loadedModel);
|
|
75
|
-
return loadedModel;
|
|
76
|
-
})();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
let globalPreset = preset.getPresetForCache(config.defaultPreset);
|
|
80
|
-
let presetPool = {};
|
|
81
|
-
ctx.on("chatluna_character/preset_updated", () => {
|
|
82
|
-
globalPreset = preset.getPresetForCache(config.defaultPreset);
|
|
83
|
-
presetPool = {};
|
|
84
|
-
});
|
|
85
|
-
service.collect(async (session, messages) => {
|
|
86
|
-
const guildId = session.event.guild?.id ?? session.guildId;
|
|
87
|
-
const model = await (modelPool[guildId] ?? Promise.resolve(globalModel));
|
|
88
|
-
const currentGuildConfig = config.configs[guildId];
|
|
89
|
-
let copyOfConfig = Object.assign({}, config);
|
|
90
|
-
let currentPreset = globalPreset;
|
|
91
|
-
if (currentGuildConfig != null) {
|
|
92
|
-
copyOfConfig = Object.assign({}, copyOfConfig, currentGuildConfig);
|
|
93
|
-
currentPreset = presetPool[guildId] ?? await (async () => {
|
|
94
|
-
const template = preset.getPresetForCache(
|
|
95
|
-
currentGuildConfig.preset
|
|
96
|
-
);
|
|
97
|
-
presetPool[guildId] = template;
|
|
98
|
-
return template;
|
|
99
|
-
})();
|
|
100
|
-
}
|
|
101
|
-
const [recentMessage, lastMessage] = await formatMessage(
|
|
102
|
-
messages,
|
|
103
|
-
copyOfConfig,
|
|
104
|
-
model,
|
|
105
|
-
currentPreset.system.template,
|
|
106
|
-
currentPreset.system.template
|
|
107
|
-
);
|
|
108
|
-
const temp = await service.getTemp(session);
|
|
109
|
-
const formattedSystemPrompt = await currentPreset.system.format({
|
|
110
|
-
time: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
111
|
-
status: temp.status ?? currentPreset.status ?? "",
|
|
112
|
-
stickers: JSON.stringify(stickerService.getAllStickTypes())
|
|
113
|
-
});
|
|
114
|
-
logger.debug("messages_new: " + JSON.stringify(recentMessage));
|
|
115
|
-
logger.debug("messages_last: " + JSON.stringify(lastMessage));
|
|
116
|
-
const humanMessage = new import_messages.HumanMessage(
|
|
117
|
-
await currentPreset.input.format({
|
|
118
|
-
history_new: recentMessage,
|
|
119
|
-
history_last: lastMessage,
|
|
120
|
-
time: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
121
|
-
stickers: JSON.stringify(stickerService.getAllStickTypes()),
|
|
122
|
-
status: temp.status ?? currentPreset.status ?? ""
|
|
123
|
-
})
|
|
124
|
-
);
|
|
125
|
-
const completionMessages = await formatCompletionMessages(
|
|
126
|
-
[new import_messages.SystemMessage(formattedSystemPrompt)].concat(
|
|
127
|
-
temp.completionMessages
|
|
128
|
-
),
|
|
129
|
-
humanMessage,
|
|
130
|
-
copyOfConfig,
|
|
131
|
-
model
|
|
132
|
-
);
|
|
133
|
-
logger.debug(
|
|
134
|
-
"completion message: " + JSON.stringify(completionMessages.map((it) => it.content))
|
|
135
|
-
);
|
|
136
|
-
let responseMessage;
|
|
137
|
-
let parsedResponse;
|
|
138
|
-
let retryCount = 0;
|
|
139
|
-
while (retryCount < 3) {
|
|
140
|
-
retryCount++;
|
|
141
|
-
try {
|
|
142
|
-
responseMessage = await model.invoke(completionMessages);
|
|
143
|
-
} catch (e) {
|
|
144
|
-
logger.error("model requests failed", e);
|
|
145
|
-
retryCount = 3;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
logger.debug("model response: " + responseMessage.content);
|
|
150
|
-
parsedResponse = parseResponse(
|
|
151
|
-
responseMessage.content,
|
|
152
|
-
copyOfConfig.isAt
|
|
153
|
-
);
|
|
154
|
-
break;
|
|
155
|
-
} catch (e) {
|
|
156
|
-
await (0, import_koishi.sleep)(3e3);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (retryCount >= 3) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
temp.status = parsedResponse.status;
|
|
163
|
-
if (parsedResponse.elements.length < 1) {
|
|
164
|
-
service.mute(session, copyOfConfig.muteTime);
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
temp.completionMessages.push(humanMessage, responseMessage);
|
|
168
|
-
if (temp.completionMessages.length > 5) {
|
|
169
|
-
temp.completionMessages.length = 0;
|
|
170
|
-
}
|
|
171
|
-
const random = new import_koishi.Random();
|
|
172
|
-
for (const elements of parsedResponse.elements) {
|
|
173
|
-
const text = elements.map((element) => element.attrs.content ?? "").join("");
|
|
174
|
-
const emoticonStatement = isEmoticonStatement(text, elements);
|
|
175
|
-
if (elements.length < 1) {
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
let maxTime = text.length * copyOfConfig.typingTime + 100;
|
|
179
|
-
if (parsedResponse.messageType === "voice" && emoticonStatement !== "text") {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (config.splitVoice !== true && parsedResponse.messageType === "voice") {
|
|
183
|
-
maxTime = parsedResponse.rawMessage.length * copyOfConfig.typingTime + 100;
|
|
184
|
-
await (0, import_koishi.sleep)(random.int(maxTime / 4, maxTime / 2));
|
|
185
|
-
try {
|
|
186
|
-
await session.send(
|
|
187
|
-
await ctx.vits.say({ input: parsedResponse.rawMessage })
|
|
188
|
-
);
|
|
189
|
-
} catch (e) {
|
|
190
|
-
logger.error(e);
|
|
191
|
-
await session.send(elements);
|
|
192
|
-
}
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
if (emoticonStatement !== "emo") {
|
|
197
|
-
await (0, import_koishi.sleep)(random.int(maxTime / 2, maxTime));
|
|
198
|
-
} else {
|
|
199
|
-
await (0, import_koishi.sleep)(random.int(maxTime / 8, maxTime / 2));
|
|
200
|
-
}
|
|
201
|
-
switch (parsedResponse.messageType) {
|
|
202
|
-
case "text":
|
|
203
|
-
await session.send(elements);
|
|
204
|
-
break;
|
|
205
|
-
case "voice":
|
|
206
|
-
await session.send(
|
|
207
|
-
await ctx.vits.say({
|
|
208
|
-
input: text
|
|
209
|
-
})
|
|
210
|
-
);
|
|
211
|
-
break;
|
|
212
|
-
default:
|
|
213
|
-
await session.send(elements);
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
} catch (e) {
|
|
217
|
-
logger.error(e);
|
|
218
|
-
await session.send(elements);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const randomNumber = Math.random();
|
|
222
|
-
if (randomNumber < config.sendStickerProbability) {
|
|
223
|
-
const sticker = await stickerService.randomStickByType(
|
|
224
|
-
parsedResponse.sticker
|
|
225
|
-
);
|
|
226
|
-
await (0, import_koishi.sleep)(random.int(500, 2e3));
|
|
227
|
-
await session.send(sticker);
|
|
228
|
-
}
|
|
229
|
-
service.mute(session, copyOfConfig.coolDownTime * 1e3);
|
|
230
|
-
await service.broadcastOnBot(session, parsedResponse.elements.flat());
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
__name(apply, "apply");
|
|
46
|
+
|
|
47
|
+
// src/utils.ts
|
|
48
|
+
var import_koishi = require("koishi");
|
|
49
|
+
var import_marked = require("marked");
|
|
50
|
+
var import_he = __toESM(require("he"), 1);
|
|
234
51
|
function isEmoticonStatement(text, elements) {
|
|
235
52
|
if (elements.length === 1 && elements[0].attrs["emo"]) {
|
|
236
53
|
return "emo";
|
|
@@ -255,6 +72,9 @@ function parseResponse(response, useAt = true) {
|
|
|
255
72
|
/<message_part>\s*(.*?)\s*<\/message_part>/s
|
|
256
73
|
)?.[1];
|
|
257
74
|
status = response.match(/<status>(.*?)<\/status>/s)?.[1];
|
|
75
|
+
if (rawMessage == null) {
|
|
76
|
+
rawMessage = response.match(/<message[\s\S]*?<\/message>/)?.[0];
|
|
77
|
+
}
|
|
258
78
|
if (rawMessage == null) {
|
|
259
79
|
throw new Error("Failed to parse response: " + response);
|
|
260
80
|
}
|
|
@@ -278,7 +98,7 @@ function parseResponse(response, useAt = true) {
|
|
|
278
98
|
const before = rawMessage.substring(lastAtIndex, at.start);
|
|
279
99
|
if (before.length > 0) {
|
|
280
100
|
parsedMessage += before;
|
|
281
|
-
currentElements.push(...
|
|
101
|
+
currentElements.push(...transform(before));
|
|
282
102
|
}
|
|
283
103
|
if (useAt) {
|
|
284
104
|
currentElements.push(import_koishi.h.at(at.at));
|
|
@@ -288,11 +108,11 @@ function parseResponse(response, useAt = true) {
|
|
|
288
108
|
const after = rawMessage.substring(lastAtIndex);
|
|
289
109
|
if (after.length > 0) {
|
|
290
110
|
parsedMessage += after;
|
|
291
|
-
currentElements.push(...
|
|
111
|
+
currentElements.push(...transform(after));
|
|
292
112
|
}
|
|
293
113
|
} else {
|
|
294
114
|
parsedMessage = rawMessage;
|
|
295
|
-
currentElements.push(...
|
|
115
|
+
currentElements.push(...transform(rawMessage));
|
|
296
116
|
}
|
|
297
117
|
const forEachElement = /* @__PURE__ */ __name((elements) => {
|
|
298
118
|
for (let i = 0; i < elements.length; i++) {
|
|
@@ -310,7 +130,12 @@ function parseResponse(response, useAt = true) {
|
|
|
310
130
|
continue;
|
|
311
131
|
}
|
|
312
132
|
}
|
|
313
|
-
|
|
133
|
+
console.log(element);
|
|
134
|
+
if (element.attrs["code"]) {
|
|
135
|
+
resultElements.push([element]);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const matchArray = splitSentence(import_he.default.decode(text)).filter(
|
|
314
139
|
(x) => x.length > 0
|
|
315
140
|
);
|
|
316
141
|
for (const match of matchArray) {
|
|
@@ -339,32 +164,6 @@ function parseResponse(response, useAt = true) {
|
|
|
339
164
|
};
|
|
340
165
|
}
|
|
341
166
|
__name(parseResponse, "parseResponse");
|
|
342
|
-
function parseXmlToObject(xml) {
|
|
343
|
-
const messageRegex = /<message\s+(.*?)>(.*?)<\/message>/s;
|
|
344
|
-
const match = xml.match(messageRegex);
|
|
345
|
-
if (!match) {
|
|
346
|
-
throw new Error("Failed to parse response: " + xml);
|
|
347
|
-
}
|
|
348
|
-
const [, attributes, content] = match;
|
|
349
|
-
const getAttr = /* @__PURE__ */ __name((name3) => {
|
|
350
|
-
const attrRegex = new RegExp(`${name3}=['"]?([^'"]+)['"]?`);
|
|
351
|
-
const attrMatch = attributes.match(attrRegex);
|
|
352
|
-
if (!attrMatch) {
|
|
353
|
-
logger.warn(`Failed to parse ${name3} attribute: ${xml}`);
|
|
354
|
-
return "";
|
|
355
|
-
}
|
|
356
|
-
return attrMatch[1];
|
|
357
|
-
}, "getAttr");
|
|
358
|
-
const name2 = getAttr("name");
|
|
359
|
-
const id = getAttr("id");
|
|
360
|
-
const type = getAttr("type") || "text";
|
|
361
|
-
const sticker = getAttr("sticker");
|
|
362
|
-
if (content === void 0) {
|
|
363
|
-
throw new Error("Failed to parse content: " + xml);
|
|
364
|
-
}
|
|
365
|
-
return { name: name2, id, type, sticker, content };
|
|
366
|
-
}
|
|
367
|
-
__name(parseXmlToObject, "parseXmlToObject");
|
|
368
167
|
function splitSentence(text) {
|
|
369
168
|
if (isOnlyPunctuation(text)) {
|
|
370
169
|
return [text];
|
|
@@ -456,6 +255,33 @@ function matchAt(str) {
|
|
|
456
255
|
});
|
|
457
256
|
}
|
|
458
257
|
__name(matchAt, "matchAt");
|
|
258
|
+
async function formatMessage(messages, config, model, systemPrompt, historyPrompt) {
|
|
259
|
+
const maxTokens = config.maxTokens - 300;
|
|
260
|
+
let currentTokens = 0;
|
|
261
|
+
currentTokens += await model.getNumTokens(systemPrompt);
|
|
262
|
+
currentTokens += await model.getNumTokens(historyPrompt);
|
|
263
|
+
const calculatedMessages = [];
|
|
264
|
+
const random = new import_koishi.Random();
|
|
265
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
266
|
+
const message = messages[i];
|
|
267
|
+
const voiceProbability = random.int(1, 10) > 8 ? "voice" : "text";
|
|
268
|
+
const xmlMessage = `<message type='${voiceProbability}' name='${message.name}' id='${message.id}'>${message.content}</message>`;
|
|
269
|
+
const xmlMessageToken = await model.getNumTokens(xmlMessage);
|
|
270
|
+
if (currentTokens + xmlMessageToken > maxTokens - 4) {
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
currentTokens += xmlMessageToken;
|
|
274
|
+
calculatedMessages.unshift(xmlMessage);
|
|
275
|
+
}
|
|
276
|
+
const lastMessage = calculatedMessages.pop();
|
|
277
|
+
if (lastMessage === void 0) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
"lastMessage is undefined, please set the max token to be bigger"
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return [calculatedMessages, lastMessage];
|
|
283
|
+
}
|
|
284
|
+
__name(formatMessage, "formatMessage");
|
|
459
285
|
async function formatCompletionMessages(messages, humanMessage, config, model) {
|
|
460
286
|
const maxTokens = config.maxTokens - 600;
|
|
461
287
|
const systemMessage = messages.shift();
|
|
@@ -480,34 +306,277 @@ async function formatCompletionMessages(messages, humanMessage, config, model) {
|
|
|
480
306
|
return result;
|
|
481
307
|
}
|
|
482
308
|
__name(formatCompletionMessages, "formatCompletionMessages");
|
|
483
|
-
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
break;
|
|
309
|
+
function parseXmlToObject(xml) {
|
|
310
|
+
const messageRegex = /<message\s+(.*?)>(.*?)<\/message>/s;
|
|
311
|
+
const match = xml.match(messageRegex);
|
|
312
|
+
if (!match) {
|
|
313
|
+
throw new Error("Failed to parse response: " + xml);
|
|
314
|
+
}
|
|
315
|
+
const [, attributes, content] = match;
|
|
316
|
+
const getAttr = /* @__PURE__ */ __name((name3) => {
|
|
317
|
+
const attrRegex = new RegExp(`${name3}=['"]?([^'"]+)['"]?`);
|
|
318
|
+
const attrMatch = attributes.match(attrRegex);
|
|
319
|
+
if (!attrMatch) {
|
|
320
|
+
logger.warn(`Failed to parse ${name3} attribute: ${xml}`);
|
|
321
|
+
return "";
|
|
497
322
|
}
|
|
498
|
-
|
|
499
|
-
|
|
323
|
+
return attrMatch[1];
|
|
324
|
+
}, "getAttr");
|
|
325
|
+
const name2 = getAttr("name");
|
|
326
|
+
const id = getAttr("id");
|
|
327
|
+
const type = getAttr("type") || "text";
|
|
328
|
+
const sticker = getAttr("sticker");
|
|
329
|
+
if (content === void 0) {
|
|
330
|
+
throw new Error("Failed to parse content: " + xml);
|
|
500
331
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
332
|
+
return { name: name2, id, type, sticker, content };
|
|
333
|
+
}
|
|
334
|
+
__name(parseXmlToObject, "parseXmlToObject");
|
|
335
|
+
var tagRegExp = /^<(\/?)([^!\s>/]+)([^>]*?)\s*(\/?)>$/;
|
|
336
|
+
function renderToken(token) {
|
|
337
|
+
if (token.type === "code") {
|
|
338
|
+
return (0, import_koishi.h)("text", { code: true, content: token.text + "\n" });
|
|
339
|
+
} else if (token.type === "paragraph") {
|
|
340
|
+
return (0, import_koishi.h)("p", render(token.tokens));
|
|
341
|
+
} else if (token.type === "image") {
|
|
342
|
+
return import_koishi.h.image(token.href);
|
|
343
|
+
} else if (token.type === "blockquote") {
|
|
344
|
+
return (0, import_koishi.h)("text", { content: token.text + "\n" });
|
|
345
|
+
} else if (token.type === "text") {
|
|
346
|
+
return (0, import_koishi.h)("text", { content: token.text });
|
|
347
|
+
} else if (token.type === "em") {
|
|
348
|
+
return (0, import_koishi.h)("em", render(token.tokens));
|
|
349
|
+
} else if (token.type === "strong") {
|
|
350
|
+
return (0, import_koishi.h)("strong", render(token.tokens));
|
|
351
|
+
} else if (token.type === "del") {
|
|
352
|
+
return (0, import_koishi.h)("del", render(token.tokens));
|
|
353
|
+
} else if (token.type === "link") {
|
|
354
|
+
return (0, import_koishi.h)("a", { href: token.href }, render(token.tokens));
|
|
355
|
+
} else if (token.type === "html") {
|
|
356
|
+
const cap = tagRegExp.exec(token.text);
|
|
357
|
+
if (!cap) {
|
|
358
|
+
return (0, import_koishi.h)("text", { content: token.text });
|
|
359
|
+
}
|
|
360
|
+
if (cap[2] === "img") {
|
|
361
|
+
if (cap[1]) return;
|
|
362
|
+
const src = cap[3].match(/src="([^"]+)"/);
|
|
363
|
+
if (src) return import_koishi.h.image(src[1]);
|
|
364
|
+
}
|
|
506
365
|
}
|
|
507
|
-
return
|
|
366
|
+
return (0, import_koishi.h)("text", { content: token.raw });
|
|
508
367
|
}
|
|
509
|
-
__name(
|
|
368
|
+
__name(renderToken, "renderToken");
|
|
369
|
+
function render(tokens) {
|
|
370
|
+
return tokens.map(renderToken).filter(Boolean);
|
|
371
|
+
}
|
|
372
|
+
__name(render, "render");
|
|
373
|
+
function transform(source, ...args) {
|
|
374
|
+
if (!source) return [];
|
|
375
|
+
if (Array.isArray(source)) {
|
|
376
|
+
source = args.map((arg, index) => source[index] + arg).join("") + source[args.length];
|
|
377
|
+
}
|
|
378
|
+
return render(import_marked.marked.lexer(source));
|
|
379
|
+
}
|
|
380
|
+
__name(transform, "transform");
|
|
510
381
|
var logger;
|
|
382
|
+
function setLogger(setLogger2) {
|
|
383
|
+
logger = setLogger2;
|
|
384
|
+
}
|
|
385
|
+
__name(setLogger, "setLogger");
|
|
386
|
+
|
|
387
|
+
// src/plugins/chat.ts
|
|
388
|
+
async function apply(ctx, config) {
|
|
389
|
+
const service = ctx.chatluna_character;
|
|
390
|
+
const preset = service.preset;
|
|
391
|
+
const stickerService = service.stickerService;
|
|
392
|
+
logger2 = service.logger;
|
|
393
|
+
setLogger(logger2);
|
|
394
|
+
const modelPool = {};
|
|
395
|
+
const [platform, modelName] = (0, import_count_tokens.parseRawModelName)(config.model);
|
|
396
|
+
await ctx.chatluna.awaitLoadPlatform(platform);
|
|
397
|
+
const globalModel = await ctx.chatluna.createChatModel(
|
|
398
|
+
platform,
|
|
399
|
+
modelName
|
|
400
|
+
);
|
|
401
|
+
logger2.info("global model loaded %c", config.model);
|
|
402
|
+
if (config.modelOverride?.length > 0) {
|
|
403
|
+
for (const override of config.modelOverride) {
|
|
404
|
+
modelPool[override.groupId] = (async () => {
|
|
405
|
+
const [platform2, modelName2] = (0, import_count_tokens.parseRawModelName)(override.model);
|
|
406
|
+
await ctx.chatluna.awaitLoadPlatform(platform2);
|
|
407
|
+
const loadedModel = await ctx.chatluna.createChatModel(
|
|
408
|
+
platform2,
|
|
409
|
+
modelName2
|
|
410
|
+
);
|
|
411
|
+
logger2.info(
|
|
412
|
+
"override model loaded %c for group %c",
|
|
413
|
+
override.model,
|
|
414
|
+
override.groupId
|
|
415
|
+
);
|
|
416
|
+
modelPool[override.groupId] = Promise.resolve(loadedModel);
|
|
417
|
+
return loadedModel;
|
|
418
|
+
})();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let globalPreset = preset.getPresetForCache(config.defaultPreset);
|
|
422
|
+
let presetPool = {};
|
|
423
|
+
ctx.on("chatluna_character/preset_updated", () => {
|
|
424
|
+
globalPreset = preset.getPresetForCache(config.defaultPreset);
|
|
425
|
+
presetPool = {};
|
|
426
|
+
});
|
|
427
|
+
service.collect(async (session, messages) => {
|
|
428
|
+
const guildId = session.event.guild?.id ?? session.guildId;
|
|
429
|
+
const model = await (modelPool[guildId] ?? Promise.resolve(globalModel));
|
|
430
|
+
const currentGuildConfig = config.configs[guildId];
|
|
431
|
+
let copyOfConfig = Object.assign({}, config);
|
|
432
|
+
let currentPreset = globalPreset;
|
|
433
|
+
if (currentGuildConfig != null) {
|
|
434
|
+
copyOfConfig = Object.assign({}, copyOfConfig, currentGuildConfig);
|
|
435
|
+
currentPreset = presetPool[guildId] ?? await (async () => {
|
|
436
|
+
const template = preset.getPresetForCache(
|
|
437
|
+
currentGuildConfig.preset
|
|
438
|
+
);
|
|
439
|
+
presetPool[guildId] = template;
|
|
440
|
+
return template;
|
|
441
|
+
})();
|
|
442
|
+
}
|
|
443
|
+
const [recentMessage, lastMessage] = await formatMessage(
|
|
444
|
+
messages,
|
|
445
|
+
copyOfConfig,
|
|
446
|
+
model,
|
|
447
|
+
currentPreset.system.template,
|
|
448
|
+
currentPreset.system.template
|
|
449
|
+
);
|
|
450
|
+
const temp = await service.getTemp(session);
|
|
451
|
+
const formattedSystemPrompt = await currentPreset.system.format({
|
|
452
|
+
time: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
453
|
+
status: temp.status ?? currentPreset.status ?? "",
|
|
454
|
+
stickers: JSON.stringify(stickerService.getAllStickTypes())
|
|
455
|
+
});
|
|
456
|
+
logger2.debug("messages_new: " + JSON.stringify(recentMessage));
|
|
457
|
+
logger2.debug("messages_last: " + JSON.stringify(lastMessage));
|
|
458
|
+
const humanMessage = new import_messages.HumanMessage(
|
|
459
|
+
await currentPreset.input.format({
|
|
460
|
+
history_new: recentMessage,
|
|
461
|
+
history_last: lastMessage,
|
|
462
|
+
time: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
463
|
+
stickers: JSON.stringify(stickerService.getAllStickTypes()),
|
|
464
|
+
status: temp.status ?? currentPreset.status ?? ""
|
|
465
|
+
})
|
|
466
|
+
);
|
|
467
|
+
const completionMessages = await formatCompletionMessages(
|
|
468
|
+
[new import_messages.SystemMessage(formattedSystemPrompt)].concat(
|
|
469
|
+
temp.completionMessages
|
|
470
|
+
),
|
|
471
|
+
humanMessage,
|
|
472
|
+
copyOfConfig,
|
|
473
|
+
model
|
|
474
|
+
);
|
|
475
|
+
logger2.debug(
|
|
476
|
+
"completion message: " + JSON.stringify(completionMessages.map((it) => it.content))
|
|
477
|
+
);
|
|
478
|
+
let responseMessage;
|
|
479
|
+
let parsedResponse;
|
|
480
|
+
let retryCount = 0;
|
|
481
|
+
while (retryCount < 3) {
|
|
482
|
+
retryCount++;
|
|
483
|
+
try {
|
|
484
|
+
responseMessage = await model.invoke(completionMessages);
|
|
485
|
+
} catch (e) {
|
|
486
|
+
logger2.error("model requests failed", e);
|
|
487
|
+
retryCount = 3;
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
logger2.debug("model response: " + responseMessage.content);
|
|
492
|
+
parsedResponse = parseResponse(
|
|
493
|
+
responseMessage.content,
|
|
494
|
+
copyOfConfig.isAt
|
|
495
|
+
);
|
|
496
|
+
break;
|
|
497
|
+
} catch (e) {
|
|
498
|
+
await (0, import_koishi2.sleep)(3e3);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (retryCount >= 3) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
temp.status = parsedResponse.status;
|
|
505
|
+
if (parsedResponse.elements.length < 1) {
|
|
506
|
+
service.mute(session, copyOfConfig.muteTime);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
temp.completionMessages.push(humanMessage, responseMessage);
|
|
510
|
+
if (temp.completionMessages.length > 5) {
|
|
511
|
+
temp.completionMessages.length = 0;
|
|
512
|
+
}
|
|
513
|
+
const random = new import_koishi2.Random();
|
|
514
|
+
for (const elements of parsedResponse.elements) {
|
|
515
|
+
const text = elements.map((element) => element.attrs.content ?? "").join("");
|
|
516
|
+
const emoticonStatement = isEmoticonStatement(text, elements);
|
|
517
|
+
if (elements.length < 1) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
let maxTime = text.length * copyOfConfig.typingTime + 100;
|
|
521
|
+
if (elements.length === 1 && elements[0].attrs["code"] === true) {
|
|
522
|
+
maxTime = 10;
|
|
523
|
+
}
|
|
524
|
+
if (parsedResponse.messageType === "voice" && emoticonStatement !== "text") {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
if (config.splitVoice !== true && parsedResponse.messageType === "voice") {
|
|
528
|
+
maxTime = parsedResponse.rawMessage.length * copyOfConfig.typingTime + 100;
|
|
529
|
+
await (0, import_koishi2.sleep)(random.int(maxTime / 4, maxTime / 2));
|
|
530
|
+
try {
|
|
531
|
+
await session.send(
|
|
532
|
+
await ctx.vits.say({ input: parsedResponse.rawMessage })
|
|
533
|
+
);
|
|
534
|
+
} catch (e) {
|
|
535
|
+
logger2.error(e);
|
|
536
|
+
await session.send(elements);
|
|
537
|
+
}
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
if (emoticonStatement !== "emo") {
|
|
542
|
+
await (0, import_koishi2.sleep)(random.int(maxTime / 2, maxTime));
|
|
543
|
+
} else {
|
|
544
|
+
await (0, import_koishi2.sleep)(random.int(maxTime / 8, maxTime / 2));
|
|
545
|
+
}
|
|
546
|
+
switch (parsedResponse.messageType) {
|
|
547
|
+
case "text":
|
|
548
|
+
await session.send(elements);
|
|
549
|
+
break;
|
|
550
|
+
case "voice":
|
|
551
|
+
await session.send(
|
|
552
|
+
await ctx.vits.say({
|
|
553
|
+
input: text
|
|
554
|
+
})
|
|
555
|
+
);
|
|
556
|
+
break;
|
|
557
|
+
default:
|
|
558
|
+
await session.send(elements);
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
} catch (e) {
|
|
562
|
+
logger2.error(e);
|
|
563
|
+
await session.send(elements);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const randomNumber = Math.random();
|
|
567
|
+
if (randomNumber < config.sendStickerProbability) {
|
|
568
|
+
const sticker = await stickerService.randomStickByType(
|
|
569
|
+
parsedResponse.sticker
|
|
570
|
+
);
|
|
571
|
+
await (0, import_koishi2.sleep)(random.int(500, 2e3));
|
|
572
|
+
await session.send(sticker);
|
|
573
|
+
}
|
|
574
|
+
service.mute(session, copyOfConfig.coolDownTime * 1e3);
|
|
575
|
+
await service.broadcastOnBot(session, parsedResponse.elements.flat());
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
__name(apply, "apply");
|
|
579
|
+
var logger2;
|
|
511
580
|
|
|
512
581
|
// src/plugins/filter.ts
|
|
513
582
|
var groupInfos = {};
|
|
@@ -515,7 +584,7 @@ async function apply2(ctx, config) {
|
|
|
515
584
|
const maxMessages = config.messageInterval;
|
|
516
585
|
const service = ctx.chatluna_character;
|
|
517
586
|
const preset = service.preset;
|
|
518
|
-
const
|
|
587
|
+
const logger3 = service.logger;
|
|
519
588
|
const globalPreset = await preset.getPreset(config.defaultPreset);
|
|
520
589
|
const presetPool = {};
|
|
521
590
|
service.addFilter((session, message) => {
|
|
@@ -538,7 +607,7 @@ async function apply2(ctx, config) {
|
|
|
538
607
|
})();
|
|
539
608
|
}
|
|
540
609
|
let { messageCount, messageSendProbability } = info;
|
|
541
|
-
|
|
610
|
+
logger3.debug(
|
|
542
611
|
`messageCount: ${messageCount}, messageSendProbability: ${messageSendProbability}. content: ${JSON.stringify(
|
|
543
612
|
message
|
|
544
613
|
)}`
|
|
@@ -566,7 +635,7 @@ async function apply2(ctx, config) {
|
|
|
566
635
|
(value) => message.content.includes(value)
|
|
567
636
|
);
|
|
568
637
|
if (needMute) {
|
|
569
|
-
|
|
638
|
+
logger3.debug(`mute content: ${message.content}`);
|
|
570
639
|
service.mute(session, config.muteTime);
|
|
571
640
|
}
|
|
572
641
|
}
|
|
@@ -622,20 +691,20 @@ function apply3(ctx, config) {
|
|
|
622
691
|
__name(apply3, "apply");
|
|
623
692
|
|
|
624
693
|
// src/plugins/config.ts
|
|
625
|
-
var
|
|
694
|
+
var import_koishi3 = require("koishi");
|
|
626
695
|
var import_types = require("koishi-plugin-chatluna/llm-core/platform/types");
|
|
627
696
|
async function apply4(ctx, config) {
|
|
628
697
|
ctx.on("chatluna/model-added", (service) => {
|
|
629
|
-
ctx.schema.set("model",
|
|
698
|
+
ctx.schema.set("model", import_koishi3.Schema.union(getModelNames(service)));
|
|
630
699
|
});
|
|
631
700
|
ctx.on("chatluna/model-removed", (service) => {
|
|
632
|
-
ctx.schema.set("model",
|
|
701
|
+
ctx.schema.set("model", import_koishi3.Schema.union(getModelNames(service)));
|
|
633
702
|
});
|
|
634
|
-
ctx.schema.set("model",
|
|
703
|
+
ctx.schema.set("model", import_koishi3.Schema.union(getModelNames(ctx.chatluna.platform)));
|
|
635
704
|
}
|
|
636
705
|
__name(apply4, "apply");
|
|
637
706
|
function getModelNames(service) {
|
|
638
|
-
return service.getAllModels(import_types.ModelType.llm).map((m) =>
|
|
707
|
+
return service.getAllModels(import_types.ModelType.llm).map((m) => import_koishi3.Schema.const(m));
|
|
639
708
|
}
|
|
640
709
|
__name(getModelNames, "getModelNames");
|
|
641
710
|
|
|
@@ -683,14 +752,14 @@ __name(plugins, "plugins");
|
|
|
683
752
|
|
|
684
753
|
// src/service/message.ts
|
|
685
754
|
var import_events = __toESM(require("events"), 1);
|
|
686
|
-
var
|
|
755
|
+
var import_koishi6 = require("koishi");
|
|
687
756
|
var import_logger = require("koishi-plugin-chatluna/utils/logger");
|
|
688
757
|
|
|
689
758
|
// src/preset.ts
|
|
690
759
|
var import_prompts = require("@langchain/core/prompts");
|
|
691
760
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
692
761
|
var import_js_yaml = require("js-yaml");
|
|
693
|
-
var
|
|
762
|
+
var import_koishi4 = require("koishi");
|
|
694
763
|
var import_error = require("koishi-plugin-chatluna/utils/error");
|
|
695
764
|
var import_path = __toESM(require("path"), 1);
|
|
696
765
|
var import_url = require("url");
|
|
@@ -728,8 +797,8 @@ var Preset = class {
|
|
|
728
797
|
}
|
|
729
798
|
this.ctx.schema.set(
|
|
730
799
|
"character-preset",
|
|
731
|
-
|
|
732
|
-
this._presets.map((preset) => preset.name).concat("无").map((name2) =>
|
|
800
|
+
import_koishi4.Schema.union(
|
|
801
|
+
this._presets.map((preset) => preset.name).concat("无").map((name2) => import_koishi4.Schema.const(name2))
|
|
733
802
|
)
|
|
734
803
|
);
|
|
735
804
|
this.ctx.emit("chatluna_character/preset_updated");
|
|
@@ -856,7 +925,7 @@ function loadPreset(text) {
|
|
|
856
925
|
__name(loadPreset, "loadPreset");
|
|
857
926
|
|
|
858
927
|
// src/service/sticker.ts
|
|
859
|
-
var
|
|
928
|
+
var import_koishi5 = require("koishi");
|
|
860
929
|
var import_path2 = __toESM(require("path"), 1);
|
|
861
930
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
862
931
|
var import_url2 = require("url");
|
|
@@ -916,7 +985,7 @@ var StickerService = class {
|
|
|
916
985
|
this._ctx.root.chatluna_character.logger.debug(
|
|
917
986
|
`send sticker: ${sticker}`
|
|
918
987
|
);
|
|
919
|
-
return
|
|
988
|
+
return import_koishi5.h.image(await (0, import_promises2.readFile)(sticker), `image/${getFileType(sticker)}`);
|
|
920
989
|
}
|
|
921
990
|
async randomStick() {
|
|
922
991
|
const allStickers = Object.values(this._stickers).flat();
|
|
@@ -928,7 +997,7 @@ var StickerService = class {
|
|
|
928
997
|
this._ctx.root.chatluna_character.logger.debug(
|
|
929
998
|
`send sticker: ${sticker}`
|
|
930
999
|
);
|
|
931
|
-
return
|
|
1000
|
+
return import_koishi5.h.image(await (0, import_promises2.readFile)(sticker), `image/${getFileType(sticker)}`);
|
|
932
1001
|
}
|
|
933
1002
|
};
|
|
934
1003
|
function getFileType(path3) {
|
|
@@ -941,7 +1010,7 @@ function getFileType(path3) {
|
|
|
941
1010
|
__name(getFileType, "getFileType");
|
|
942
1011
|
|
|
943
1012
|
// src/service/message.ts
|
|
944
|
-
var MessageCollector = class extends
|
|
1013
|
+
var MessageCollector = class extends import_koishi6.Service {
|
|
945
1014
|
constructor(ctx, _config) {
|
|
946
1015
|
super(ctx, "chatluna_character");
|
|
947
1016
|
this.ctx = ctx;
|
|
@@ -1077,7 +1146,7 @@ var MessageCollector = class extends import_koishi5.Service {
|
|
|
1077
1146
|
const groupId = session.guildId;
|
|
1078
1147
|
const maxMessageSize = this._config.maxMessages;
|
|
1079
1148
|
const groupArray = this._messages[groupId] ? this._messages[groupId] : [];
|
|
1080
|
-
const elements = session.elements ? session.elements : [
|
|
1149
|
+
const elements = session.elements ? session.elements : [import_koishi6.h.text(session.content)];
|
|
1081
1150
|
const content = mapElementToString(session, session.content, elements);
|
|
1082
1151
|
if (content.length < 1) {
|
|
1083
1152
|
await this._unlock(session);
|
|
@@ -1098,7 +1167,7 @@ var MessageCollector = class extends import_koishi5.Service {
|
|
|
1098
1167
|
session,
|
|
1099
1168
|
session.quote.content,
|
|
1100
1169
|
session.quote.elements ?? [
|
|
1101
|
-
|
|
1170
|
+
import_koishi6.h.text(session.quote.content)
|
|
1102
1171
|
]
|
|
1103
1172
|
),
|
|
1104
1173
|
name: session.quote.user.name,
|
|
@@ -1220,57 +1289,57 @@ var inject2 = {
|
|
|
1220
1289
|
required: false
|
|
1221
1290
|
}
|
|
1222
1291
|
};
|
|
1223
|
-
var Config =
|
|
1224
|
-
|
|
1225
|
-
applyGroup:
|
|
1226
|
-
maxMessages:
|
|
1227
|
-
disableChatLuna:
|
|
1228
|
-
whiteListDisableChatLuna:
|
|
1292
|
+
var Config = import_koishi7.Schema.intersect([
|
|
1293
|
+
import_koishi7.Schema.object({
|
|
1294
|
+
applyGroup: import_koishi7.Schema.array(import_koishi7.Schema.string()).description("应用到的群组"),
|
|
1295
|
+
maxMessages: import_koishi7.Schema.number().description("存储在内存里的最大消息数量").default(10).min(3).role("slider").max(100),
|
|
1296
|
+
disableChatLuna: import_koishi7.Schema.boolean().default(true).description("在使用此插件的群聊里,是否禁用 ChatLuna 主功能"),
|
|
1297
|
+
whiteListDisableChatLuna: import_koishi7.Schema.array(import_koishi7.Schema.string()).description(
|
|
1229
1298
|
"在使用此插件时,不禁用 ChatLuna 主功能的群聊列表"
|
|
1230
1299
|
)
|
|
1231
1300
|
}).description("基础配置"),
|
|
1232
|
-
|
|
1233
|
-
model:
|
|
1234
|
-
modelOverride:
|
|
1235
|
-
|
|
1236
|
-
groupId:
|
|
1237
|
-
model:
|
|
1301
|
+
import_koishi7.Schema.object({
|
|
1302
|
+
model: import_koishi7.Schema.dynamic("model").description("使用的模型"),
|
|
1303
|
+
modelOverride: import_koishi7.Schema.array(
|
|
1304
|
+
import_koishi7.Schema.object({
|
|
1305
|
+
groupId: import_koishi7.Schema.string().required().description("群组 ID"),
|
|
1306
|
+
model: import_koishi7.Schema.dynamic("model").description("模型")
|
|
1238
1307
|
})
|
|
1239
1308
|
).description("针对某个群的模型设置,会覆盖上面的配置"),
|
|
1240
|
-
maxTokens:
|
|
1309
|
+
maxTokens: import_koishi7.Schema.number().default(5e3).min(1024).max(32e3).description("使用聊天的最大 token 数")
|
|
1241
1310
|
}).description("模型配置"),
|
|
1242
|
-
|
|
1243
|
-
isNickname:
|
|
1244
|
-
isForceMute:
|
|
1311
|
+
import_koishi7.Schema.object({
|
|
1312
|
+
isNickname: import_koishi7.Schema.boolean().description("允许 bot 配置中的昵称引发回复").default(true),
|
|
1313
|
+
isForceMute: import_koishi7.Schema.boolean().description(
|
|
1245
1314
|
"是否启用强制禁言(当聊天涉及到关键词时则会禁言,关键词需要在预设文件里配置)"
|
|
1246
1315
|
).default(true),
|
|
1247
|
-
isAt:
|
|
1248
|
-
splitVoice:
|
|
1249
|
-
messageInterval:
|
|
1250
|
-
messageProbability:
|
|
1251
|
-
coolDownTime:
|
|
1252
|
-
typingTime:
|
|
1253
|
-
muteTime:
|
|
1254
|
-
sendStickerProbability:
|
|
1255
|
-
defaultPreset:
|
|
1316
|
+
isAt: import_koishi7.Schema.boolean().description("是否允许 bot 艾特他人").default(true),
|
|
1317
|
+
splitVoice: import_koishi7.Schema.boolean().description("是否分段发送语言").default(false),
|
|
1318
|
+
messageInterval: import_koishi7.Schema.number().default(14).min(0).role("slider").max(100).description("随机发送消息的间隔"),
|
|
1319
|
+
messageProbability: import_koishi7.Schema.number().default(0.1).min(0).max(4).role("slider").step(1e-5).description("发送消息的叠加概率(线性增长)"),
|
|
1320
|
+
coolDownTime: import_koishi7.Schema.number().default(10).min(1).max(60 * 24).description("冷却发言时间(秒)"),
|
|
1321
|
+
typingTime: import_koishi7.Schema.number().default(440).min(100).role("slider").max(1500).description("模拟打字时的间隔(毫秒)"),
|
|
1322
|
+
muteTime: import_koishi7.Schema.number().default(1e3 * 60).min(1e3).max(1e3 * 60 * 10 * 10).description("闭嘴时的禁言时间(毫秒)"),
|
|
1323
|
+
sendStickerProbability: import_koishi7.Schema.number().default(0.6).min(0).max(1).role("slider").step(0.01).description("发送表情的概率"),
|
|
1324
|
+
defaultPreset: import_koishi7.Schema.dynamic("character-preset").description("使用的伪装预设").default("旧梦旧念")
|
|
1256
1325
|
}).description("对话设置"),
|
|
1257
|
-
|
|
1258
|
-
configs:
|
|
1259
|
-
|
|
1260
|
-
maxTokens:
|
|
1261
|
-
isAt:
|
|
1262
|
-
splitVoice:
|
|
1263
|
-
isNickname:
|
|
1264
|
-
isForceMute:
|
|
1326
|
+
import_koishi7.Schema.object({
|
|
1327
|
+
configs: import_koishi7.Schema.dict(
|
|
1328
|
+
import_koishi7.Schema.object({
|
|
1329
|
+
maxTokens: import_koishi7.Schema.number().default(4e3).min(1024).max(2e4).description("使用聊天的最大 token 数"),
|
|
1330
|
+
isAt: import_koishi7.Schema.boolean().description("是否启用@").default(true),
|
|
1331
|
+
splitVoice: import_koishi7.Schema.boolean().description("是否分段发送语言").default(false),
|
|
1332
|
+
isNickname: import_koishi7.Schema.boolean().description("允许 bot 配置中的昵称引发回复").default(true),
|
|
1333
|
+
isForceMute: import_koishi7.Schema.boolean().description(
|
|
1265
1334
|
"是否启用强制禁言(当聊天涉及到关键词时则会禁言,关键词需要在预设文件里配置)"
|
|
1266
1335
|
).default(true),
|
|
1267
|
-
messageInterval:
|
|
1268
|
-
messageProbability:
|
|
1269
|
-
coolDownTime:
|
|
1270
|
-
typingTime:
|
|
1271
|
-
muteTime:
|
|
1272
|
-
sendStickerProbability:
|
|
1273
|
-
preset:
|
|
1336
|
+
messageInterval: import_koishi7.Schema.number().default(10).min(0).role("slider").max(50).description("随机发送消息的间隔"),
|
|
1337
|
+
messageProbability: import_koishi7.Schema.number().default(0.1).min(0).max(1).role("slider").step(1e-5).description("发送消息的叠加概率(线性增长)"),
|
|
1338
|
+
coolDownTime: import_koishi7.Schema.number().default(10).min(1).max(60 * 24 * 24).description("冷却发言时间(秒)"),
|
|
1339
|
+
typingTime: import_koishi7.Schema.number().default(440).min(100).role("slider").max(1700).description("模拟打字时的间隔(毫秒)"),
|
|
1340
|
+
muteTime: import_koishi7.Schema.number().default(1e3 * 60).min(1e3).max(1e3 * 60 * 10 * 10).description("闭嘴时的禁言时间(毫秒)"),
|
|
1341
|
+
sendStickerProbability: import_koishi7.Schema.number().default(0.2).min(0).max(1).role("slider").step(0.01).description("发送表情的概率"),
|
|
1342
|
+
preset: import_koishi7.Schema.dynamic("character-preset").description("使用的伪装预设").default("煕")
|
|
1274
1343
|
})
|
|
1275
1344
|
).role("table").description("分群配置,会覆盖上面的默认配置(键填写群号)")
|
|
1276
1345
|
}).description("分群配置")
|