koishi-plugin-chatluna-character 0.0.76 → 0.0.78
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 +123 -93
- package/lib/index.d.ts +10 -1
- package/lib/index.mjs +123 -93
- package/package.json +1 -1
- package/resources/presets/default.yml +3 -3
package/lib/index.cjs
CHANGED
|
@@ -49,8 +49,8 @@ var import_koishi = require("koishi");
|
|
|
49
49
|
var import_marked = require("marked");
|
|
50
50
|
var import_he = __toESM(require("he"), 1);
|
|
51
51
|
function isEmoticonStatement(text, elements) {
|
|
52
|
-
if (elements.length === 1 && elements[0].attrs["
|
|
53
|
-
return "
|
|
52
|
+
if (elements.length === 1 && elements[0].attrs["span"]) {
|
|
53
|
+
return "span";
|
|
54
54
|
}
|
|
55
55
|
const regex = /^[\p{P}\p{S}\p{Z}\p{M}\p{N}\p{L}\s]*\p{So}[\p{P}\p{S}\p{Z}\p{M}\p{N}\p{L}\s]*$/u;
|
|
56
56
|
return regex.test(text) ? "emoji" : "text";
|
|
@@ -61,107 +61,126 @@ function isOnlyPunctuation(text) {
|
|
|
61
61
|
return regex.test(text);
|
|
62
62
|
}
|
|
63
63
|
__name(isOnlyPunctuation, "isOnlyPunctuation");
|
|
64
|
-
function
|
|
65
|
-
let rawMessage
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)?.[1];
|
|
74
|
-
status = response.match(/<status>(.*?)<\/status>/s)?.[1];
|
|
75
|
-
if (rawMessage == null) {
|
|
76
|
-
rawMessage = response.match(/<message[\s\S]*?<\/message>/)?.[0];
|
|
77
|
-
}
|
|
78
|
-
if (rawMessage == null) {
|
|
79
|
-
throw new Error("Failed to parse response: " + response);
|
|
80
|
-
}
|
|
81
|
-
const tempJson = parseXmlToObject(rawMessage);
|
|
82
|
-
rawMessage = tempJson.content;
|
|
83
|
-
messageType = tempJson.type;
|
|
84
|
-
sticker = tempJson.sticker;
|
|
85
|
-
if (typeof rawMessage !== "string") {
|
|
86
|
-
throw new Error("Failed to parse response: " + response);
|
|
87
|
-
}
|
|
88
|
-
} catch (e) {
|
|
89
|
-
logger.error(e);
|
|
64
|
+
function parseMessageContent(response) {
|
|
65
|
+
let rawMessage = response.match(
|
|
66
|
+
/<message_part>\s*(.*?)\s*<\/message_part>/s
|
|
67
|
+
)?.[1];
|
|
68
|
+
const status = response.match(/<status>(.*?)<\/status>/s)?.[1];
|
|
69
|
+
if (rawMessage == null) {
|
|
70
|
+
rawMessage = response.match(/<message[\s\S]*?<\/message>/)?.[0];
|
|
71
|
+
}
|
|
72
|
+
if (rawMessage == null) {
|
|
90
73
|
throw new Error("Failed to parse response: " + response);
|
|
91
74
|
}
|
|
75
|
+
const tempJson = parseXmlToObject(rawMessage);
|
|
76
|
+
return {
|
|
77
|
+
rawMessage: tempJson.content,
|
|
78
|
+
messageType: tempJson.type,
|
|
79
|
+
status,
|
|
80
|
+
sticker: tempJson.sticker
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
__name(parseMessageContent, "parseMessageContent");
|
|
84
|
+
function processElements(elements) {
|
|
92
85
|
const resultElements = [];
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
let lastAtIndex = 0;
|
|
97
|
-
for (const at of atMatch) {
|
|
98
|
-
const before = rawMessage.substring(lastAtIndex, at.start);
|
|
99
|
-
if (before.length > 0) {
|
|
100
|
-
parsedMessage += before;
|
|
101
|
-
currentElements.push(...transform(before));
|
|
102
|
-
}
|
|
103
|
-
if (useAt) {
|
|
104
|
-
currentElements.push(import_koishi.h.at(at.at));
|
|
105
|
-
}
|
|
106
|
-
lastAtIndex = at.end;
|
|
107
|
-
}
|
|
108
|
-
const after = rawMessage.substring(lastAtIndex);
|
|
109
|
-
if (after.length > 0) {
|
|
110
|
-
parsedMessage += after;
|
|
111
|
-
currentElements.push(...transform(after));
|
|
112
|
-
}
|
|
113
|
-
} else {
|
|
114
|
-
parsedMessage = rawMessage;
|
|
115
|
-
currentElements.push(...transform(rawMessage));
|
|
116
|
-
}
|
|
117
|
-
const forEachElement = /* @__PURE__ */ __name((elements) => {
|
|
118
|
-
for (let i = 0; i < elements.length; i++) {
|
|
119
|
-
const element = elements[i];
|
|
86
|
+
const forEachElement = /* @__PURE__ */ __name((elements2) => {
|
|
87
|
+
for (let i = 0; i < elements2.length; i++) {
|
|
88
|
+
const element = elements2[i];
|
|
120
89
|
if (element.type === "text") {
|
|
121
|
-
|
|
122
|
-
if (text.endsWith("<emo>")) {
|
|
123
|
-
const nextElement = elements[i + 1];
|
|
124
|
-
const endElement = elements[i + 2];
|
|
125
|
-
const endElementText = endElement.attrs.content;
|
|
126
|
-
if (endElementText.endsWith("</emo>")) {
|
|
127
|
-
nextElement.attrs["emo"] = true;
|
|
128
|
-
resultElements.push([nextElement]);
|
|
129
|
-
i += 2;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
console.log(element);
|
|
134
|
-
if (element.attrs["code"]) {
|
|
90
|
+
if (element.attrs["code"] || element.attrs["span"]) {
|
|
135
91
|
resultElements.push([element]);
|
|
136
92
|
continue;
|
|
137
93
|
}
|
|
138
|
-
const matchArray = splitSentence(
|
|
139
|
-
(
|
|
140
|
-
);
|
|
94
|
+
const matchArray = splitSentence(
|
|
95
|
+
import_he.default.decode(element.attrs.content)
|
|
96
|
+
).filter((x) => x.length > 0);
|
|
141
97
|
for (const match of matchArray) {
|
|
142
|
-
|
|
143
|
-
resultElements.push([newElement]);
|
|
98
|
+
resultElements.push([import_koishi.h.text(match)]);
|
|
144
99
|
}
|
|
145
|
-
} else if (
|
|
100
|
+
} else if (["em", "strong", "del", "p"].includes(element.type)) {
|
|
146
101
|
forEachElement(element.children);
|
|
147
102
|
} else {
|
|
148
103
|
resultElements.push([element]);
|
|
149
104
|
}
|
|
150
105
|
}
|
|
151
106
|
}, "forEachElement");
|
|
152
|
-
forEachElement(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
107
|
+
forEachElement(elements);
|
|
108
|
+
return resultElements;
|
|
109
|
+
}
|
|
110
|
+
__name(processElements, "processElements");
|
|
111
|
+
function processTextMatches(rawMessage, useAt = true) {
|
|
112
|
+
const currentElements = [];
|
|
113
|
+
let parsedMessage = "";
|
|
114
|
+
const matches = [
|
|
115
|
+
...matchAt(rawMessage).map((m) => ({
|
|
116
|
+
type: "at",
|
|
117
|
+
content: m.at,
|
|
118
|
+
start: m.start,
|
|
119
|
+
end: m.end
|
|
120
|
+
})),
|
|
121
|
+
...matchPre(rawMessage).map((m) => ({
|
|
122
|
+
type: "pre",
|
|
123
|
+
content: m.pre,
|
|
124
|
+
start: m.start,
|
|
125
|
+
end: m.end
|
|
126
|
+
}))
|
|
127
|
+
].sort((a, b) => a.start - b.start);
|
|
128
|
+
if (matches.length === 0) {
|
|
129
|
+
parsedMessage = rawMessage;
|
|
130
|
+
currentElements.push(...transform(rawMessage));
|
|
131
|
+
return { currentElements, parsedMessage };
|
|
132
|
+
}
|
|
133
|
+
let lastIndex = 0;
|
|
134
|
+
for (const match of matches) {
|
|
135
|
+
const before = rawMessage.substring(lastIndex, match.start);
|
|
136
|
+
if (before.length > 0) {
|
|
137
|
+
parsedMessage += before;
|
|
138
|
+
currentElements.push(...transform(before));
|
|
139
|
+
}
|
|
140
|
+
if (match.type === "at") {
|
|
141
|
+
if (useAt) {
|
|
142
|
+
currentElements.push(import_koishi.h.at(match.content));
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
parsedMessage += match.content;
|
|
146
|
+
currentElements.push(
|
|
147
|
+
(0, import_koishi.h)("text", { span: true, content: match.content })
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
lastIndex = match.end;
|
|
151
|
+
}
|
|
152
|
+
const after = rawMessage.substring(lastIndex);
|
|
153
|
+
if (after.length > 0) {
|
|
154
|
+
parsedMessage += after;
|
|
155
|
+
currentElements.push(...transform(after));
|
|
156
|
+
}
|
|
157
|
+
return { currentElements, parsedMessage };
|
|
158
|
+
}
|
|
159
|
+
__name(processTextMatches, "processTextMatches");
|
|
160
|
+
function parseResponse(response, useAt = true) {
|
|
161
|
+
try {
|
|
162
|
+
const { rawMessage, messageType, status, sticker } = parseMessageContent(response);
|
|
163
|
+
const { currentElements, parsedMessage } = processTextMatches(
|
|
164
|
+
rawMessage,
|
|
165
|
+
useAt
|
|
166
|
+
);
|
|
167
|
+
const resultElements = processElements(currentElements);
|
|
168
|
+
if (resultElements[0]?.[0]?.type === "at" && resultElements.length > 1) {
|
|
169
|
+
resultElements[1].unshift(import_koishi.h.text(" "));
|
|
170
|
+
resultElements[1].unshift(resultElements[0][0]);
|
|
171
|
+
resultElements.shift();
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
elements: resultElements,
|
|
175
|
+
rawMessage: parsedMessage,
|
|
176
|
+
status,
|
|
177
|
+
sticker,
|
|
178
|
+
messageType
|
|
179
|
+
};
|
|
180
|
+
} catch (e) {
|
|
181
|
+
logger.error(e);
|
|
182
|
+
throw new Error("Failed to parse response: " + response);
|
|
157
183
|
}
|
|
158
|
-
return {
|
|
159
|
-
elements: resultElements,
|
|
160
|
-
rawMessage: parsedMessage,
|
|
161
|
-
status,
|
|
162
|
-
sticker,
|
|
163
|
-
messageType
|
|
164
|
-
};
|
|
165
184
|
}
|
|
166
185
|
__name(parseResponse, "parseResponse");
|
|
167
186
|
function splitSentence(text) {
|
|
@@ -245,7 +264,7 @@ function splitSentence(text) {
|
|
|
245
264
|
}
|
|
246
265
|
__name(splitSentence, "splitSentence");
|
|
247
266
|
function matchAt(str) {
|
|
248
|
-
const atRegex = /<at[^>]*>(.*?)<\/at
|
|
267
|
+
const atRegex = /<at[^>]*>(.*?)<\/at>/;
|
|
249
268
|
return [...str.matchAll(atRegex)].map((item) => {
|
|
250
269
|
return {
|
|
251
270
|
at: item[1],
|
|
@@ -255,6 +274,17 @@ function matchAt(str) {
|
|
|
255
274
|
});
|
|
256
275
|
}
|
|
257
276
|
__name(matchAt, "matchAt");
|
|
277
|
+
function matchPre(str) {
|
|
278
|
+
const preRegex = /<pre>(.*?)<\/pre>/gs;
|
|
279
|
+
return [...str.matchAll(preRegex)].map((item) => {
|
|
280
|
+
return {
|
|
281
|
+
pre: item[1],
|
|
282
|
+
start: item.index,
|
|
283
|
+
end: item.index + item[0].length
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
__name(matchPre, "matchPre");
|
|
258
288
|
async function formatMessage(messages, config, model, systemPrompt, historyPrompt) {
|
|
259
289
|
const maxTokens = config.maxTokens - 300;
|
|
260
290
|
let currentTokens = 0;
|
|
@@ -332,7 +362,7 @@ function parseXmlToObject(xml) {
|
|
|
332
362
|
return { name: name2, id, type, sticker, content };
|
|
333
363
|
}
|
|
334
364
|
__name(parseXmlToObject, "parseXmlToObject");
|
|
335
|
-
var tagRegExp =
|
|
365
|
+
var tagRegExp = /<(\/?)([^!\s>/]+)([^>]*?)\s*(\/?)>/;
|
|
336
366
|
function renderToken(token) {
|
|
337
367
|
if (token.type === "code") {
|
|
338
368
|
return (0, import_koishi.h)("text", { code: true, content: token.text + "\n" });
|
|
@@ -519,7 +549,7 @@ async function apply(ctx, config) {
|
|
|
519
549
|
}
|
|
520
550
|
let maxTime = text.length * copyOfConfig.typingTime + 100;
|
|
521
551
|
if (elements.length === 1 && elements[0].attrs["code"] === true) {
|
|
522
|
-
maxTime =
|
|
552
|
+
maxTime = maxTime * 0.1;
|
|
523
553
|
}
|
|
524
554
|
if (parsedResponse.messageType === "voice" && emoticonStatement !== "text") {
|
|
525
555
|
continue;
|
|
@@ -538,10 +568,10 @@ async function apply(ctx, config) {
|
|
|
538
568
|
continue;
|
|
539
569
|
}
|
|
540
570
|
try {
|
|
541
|
-
if (emoticonStatement !== "
|
|
571
|
+
if (emoticonStatement !== "span") {
|
|
542
572
|
await (0, import_koishi2.sleep)(random.int(maxTime / 2, maxTime));
|
|
543
573
|
} else {
|
|
544
|
-
await (0, import_koishi2.sleep)(random.int(maxTime /
|
|
574
|
+
await (0, import_koishi2.sleep)(random.int(maxTime / 12, maxTime / 4));
|
|
545
575
|
}
|
|
546
576
|
switch (parsedResponse.messageType) {
|
|
547
577
|
case "text":
|
|
@@ -1314,7 +1344,7 @@ var Config = import_koishi7.Schema.intersect([
|
|
|
1314
1344
|
"是否启用强制禁言(当聊天涉及到关键词时则会禁言,关键词需要在预设文件里配置)"
|
|
1315
1345
|
).default(true),
|
|
1316
1346
|
isAt: import_koishi7.Schema.boolean().description("是否允许 bot 艾特他人").default(true),
|
|
1317
|
-
splitVoice: import_koishi7.Schema.boolean().description("
|
|
1347
|
+
splitVoice: import_koishi7.Schema.boolean().description("是否分段发送语音").default(false),
|
|
1318
1348
|
messageInterval: import_koishi7.Schema.number().default(14).min(0).role("slider").max(100).description("随机发送消息的间隔"),
|
|
1319
1349
|
messageProbability: import_koishi7.Schema.number().default(0.1).min(0).max(4).role("slider").step(1e-5).description("发送消息的叠加概率(线性增长)"),
|
|
1320
1350
|
coolDownTime: import_koishi7.Schema.number().default(10).min(1).max(60 * 24).description("冷却发言时间(秒)"),
|
package/lib/index.d.ts
CHANGED
|
@@ -41,8 +41,12 @@ export interface GroupInfo {
|
|
|
41
41
|
messageCount: number;
|
|
42
42
|
messageSendProbability: number;
|
|
43
43
|
}
|
|
44
|
-
export function isEmoticonStatement(text: string, elements: Element[]): 'emoji' | 'text' | '
|
|
44
|
+
export function isEmoticonStatement(text: string, elements: Element[]): 'emoji' | 'text' | 'span';
|
|
45
45
|
export function isOnlyPunctuation(text: string): boolean;
|
|
46
|
+
export function processTextMatches(rawMessage: string, useAt?: boolean): {
|
|
47
|
+
currentElements: h[];
|
|
48
|
+
parsedMessage: string;
|
|
49
|
+
};
|
|
46
50
|
export function parseResponse(response: string, useAt?: boolean): {
|
|
47
51
|
elements: h[][];
|
|
48
52
|
rawMessage: string;
|
|
@@ -56,6 +60,11 @@ export function matchAt(str: string): {
|
|
|
56
60
|
start: number;
|
|
57
61
|
end: number;
|
|
58
62
|
}[];
|
|
63
|
+
export function matchPre(str: string): {
|
|
64
|
+
pre: string;
|
|
65
|
+
start: number;
|
|
66
|
+
end: number;
|
|
67
|
+
}[];
|
|
59
68
|
export function formatMessage(messages: Message[], config: Config, model: ChatLunaChatModel, systemPrompt: string, historyPrompt: string): Promise<(string | string[])[]>;
|
|
60
69
|
export function formatCompletionMessages(messages: BaseMessage[], humanMessage: BaseMessage, config: Config, model: ChatLunaChatModel): Promise<BaseMessage[]>;
|
|
61
70
|
export function parseXmlToObject(xml: string): {
|
package/lib/index.mjs
CHANGED
|
@@ -17,8 +17,8 @@ import { h, Random } from "koishi";
|
|
|
17
17
|
import { marked } from "marked";
|
|
18
18
|
import he from "he";
|
|
19
19
|
function isEmoticonStatement(text, elements) {
|
|
20
|
-
if (elements.length === 1 && elements[0].attrs["
|
|
21
|
-
return "
|
|
20
|
+
if (elements.length === 1 && elements[0].attrs["span"]) {
|
|
21
|
+
return "span";
|
|
22
22
|
}
|
|
23
23
|
const regex = /^[\p{P}\p{S}\p{Z}\p{M}\p{N}\p{L}\s]*\p{So}[\p{P}\p{S}\p{Z}\p{M}\p{N}\p{L}\s]*$/u;
|
|
24
24
|
return regex.test(text) ? "emoji" : "text";
|
|
@@ -29,107 +29,126 @@ function isOnlyPunctuation(text) {
|
|
|
29
29
|
return regex.test(text);
|
|
30
30
|
}
|
|
31
31
|
__name(isOnlyPunctuation, "isOnlyPunctuation");
|
|
32
|
-
function
|
|
33
|
-
let rawMessage
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)?.[1];
|
|
42
|
-
status = response.match(/<status>(.*?)<\/status>/s)?.[1];
|
|
43
|
-
if (rawMessage == null) {
|
|
44
|
-
rawMessage = response.match(/<message[\s\S]*?<\/message>/)?.[0];
|
|
45
|
-
}
|
|
46
|
-
if (rawMessage == null) {
|
|
47
|
-
throw new Error("Failed to parse response: " + response);
|
|
48
|
-
}
|
|
49
|
-
const tempJson = parseXmlToObject(rawMessage);
|
|
50
|
-
rawMessage = tempJson.content;
|
|
51
|
-
messageType = tempJson.type;
|
|
52
|
-
sticker = tempJson.sticker;
|
|
53
|
-
if (typeof rawMessage !== "string") {
|
|
54
|
-
throw new Error("Failed to parse response: " + response);
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
logger.error(e);
|
|
32
|
+
function parseMessageContent(response) {
|
|
33
|
+
let rawMessage = response.match(
|
|
34
|
+
/<message_part>\s*(.*?)\s*<\/message_part>/s
|
|
35
|
+
)?.[1];
|
|
36
|
+
const status = response.match(/<status>(.*?)<\/status>/s)?.[1];
|
|
37
|
+
if (rawMessage == null) {
|
|
38
|
+
rawMessage = response.match(/<message[\s\S]*?<\/message>/)?.[0];
|
|
39
|
+
}
|
|
40
|
+
if (rawMessage == null) {
|
|
58
41
|
throw new Error("Failed to parse response: " + response);
|
|
59
42
|
}
|
|
43
|
+
const tempJson = parseXmlToObject(rawMessage);
|
|
44
|
+
return {
|
|
45
|
+
rawMessage: tempJson.content,
|
|
46
|
+
messageType: tempJson.type,
|
|
47
|
+
status,
|
|
48
|
+
sticker: tempJson.sticker
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
__name(parseMessageContent, "parseMessageContent");
|
|
52
|
+
function processElements(elements) {
|
|
60
53
|
const resultElements = [];
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let lastAtIndex = 0;
|
|
65
|
-
for (const at of atMatch) {
|
|
66
|
-
const before = rawMessage.substring(lastAtIndex, at.start);
|
|
67
|
-
if (before.length > 0) {
|
|
68
|
-
parsedMessage += before;
|
|
69
|
-
currentElements.push(...transform(before));
|
|
70
|
-
}
|
|
71
|
-
if (useAt) {
|
|
72
|
-
currentElements.push(h.at(at.at));
|
|
73
|
-
}
|
|
74
|
-
lastAtIndex = at.end;
|
|
75
|
-
}
|
|
76
|
-
const after = rawMessage.substring(lastAtIndex);
|
|
77
|
-
if (after.length > 0) {
|
|
78
|
-
parsedMessage += after;
|
|
79
|
-
currentElements.push(...transform(after));
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
parsedMessage = rawMessage;
|
|
83
|
-
currentElements.push(...transform(rawMessage));
|
|
84
|
-
}
|
|
85
|
-
const forEachElement = /* @__PURE__ */ __name((elements) => {
|
|
86
|
-
for (let i = 0; i < elements.length; i++) {
|
|
87
|
-
const element = elements[i];
|
|
54
|
+
const forEachElement = /* @__PURE__ */ __name((elements2) => {
|
|
55
|
+
for (let i = 0; i < elements2.length; i++) {
|
|
56
|
+
const element = elements2[i];
|
|
88
57
|
if (element.type === "text") {
|
|
89
|
-
|
|
90
|
-
if (text.endsWith("<emo>")) {
|
|
91
|
-
const nextElement = elements[i + 1];
|
|
92
|
-
const endElement = elements[i + 2];
|
|
93
|
-
const endElementText = endElement.attrs.content;
|
|
94
|
-
if (endElementText.endsWith("</emo>")) {
|
|
95
|
-
nextElement.attrs["emo"] = true;
|
|
96
|
-
resultElements.push([nextElement]);
|
|
97
|
-
i += 2;
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
console.log(element);
|
|
102
|
-
if (element.attrs["code"]) {
|
|
58
|
+
if (element.attrs["code"] || element.attrs["span"]) {
|
|
103
59
|
resultElements.push([element]);
|
|
104
60
|
continue;
|
|
105
61
|
}
|
|
106
|
-
const matchArray = splitSentence(
|
|
107
|
-
(
|
|
108
|
-
);
|
|
62
|
+
const matchArray = splitSentence(
|
|
63
|
+
he.decode(element.attrs.content)
|
|
64
|
+
).filter((x) => x.length > 0);
|
|
109
65
|
for (const match of matchArray) {
|
|
110
|
-
|
|
111
|
-
resultElements.push([newElement]);
|
|
66
|
+
resultElements.push([h.text(match)]);
|
|
112
67
|
}
|
|
113
|
-
} else if (
|
|
68
|
+
} else if (["em", "strong", "del", "p"].includes(element.type)) {
|
|
114
69
|
forEachElement(element.children);
|
|
115
70
|
} else {
|
|
116
71
|
resultElements.push([element]);
|
|
117
72
|
}
|
|
118
73
|
}
|
|
119
74
|
}, "forEachElement");
|
|
120
|
-
forEachElement(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
75
|
+
forEachElement(elements);
|
|
76
|
+
return resultElements;
|
|
77
|
+
}
|
|
78
|
+
__name(processElements, "processElements");
|
|
79
|
+
function processTextMatches(rawMessage, useAt = true) {
|
|
80
|
+
const currentElements = [];
|
|
81
|
+
let parsedMessage = "";
|
|
82
|
+
const matches = [
|
|
83
|
+
...matchAt(rawMessage).map((m) => ({
|
|
84
|
+
type: "at",
|
|
85
|
+
content: m.at,
|
|
86
|
+
start: m.start,
|
|
87
|
+
end: m.end
|
|
88
|
+
})),
|
|
89
|
+
...matchPre(rawMessage).map((m) => ({
|
|
90
|
+
type: "pre",
|
|
91
|
+
content: m.pre,
|
|
92
|
+
start: m.start,
|
|
93
|
+
end: m.end
|
|
94
|
+
}))
|
|
95
|
+
].sort((a, b) => a.start - b.start);
|
|
96
|
+
if (matches.length === 0) {
|
|
97
|
+
parsedMessage = rawMessage;
|
|
98
|
+
currentElements.push(...transform(rawMessage));
|
|
99
|
+
return { currentElements, parsedMessage };
|
|
100
|
+
}
|
|
101
|
+
let lastIndex = 0;
|
|
102
|
+
for (const match of matches) {
|
|
103
|
+
const before = rawMessage.substring(lastIndex, match.start);
|
|
104
|
+
if (before.length > 0) {
|
|
105
|
+
parsedMessage += before;
|
|
106
|
+
currentElements.push(...transform(before));
|
|
107
|
+
}
|
|
108
|
+
if (match.type === "at") {
|
|
109
|
+
if (useAt) {
|
|
110
|
+
currentElements.push(h.at(match.content));
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
parsedMessage += match.content;
|
|
114
|
+
currentElements.push(
|
|
115
|
+
h("text", { span: true, content: match.content })
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
lastIndex = match.end;
|
|
119
|
+
}
|
|
120
|
+
const after = rawMessage.substring(lastIndex);
|
|
121
|
+
if (after.length > 0) {
|
|
122
|
+
parsedMessage += after;
|
|
123
|
+
currentElements.push(...transform(after));
|
|
124
|
+
}
|
|
125
|
+
return { currentElements, parsedMessage };
|
|
126
|
+
}
|
|
127
|
+
__name(processTextMatches, "processTextMatches");
|
|
128
|
+
function parseResponse(response, useAt = true) {
|
|
129
|
+
try {
|
|
130
|
+
const { rawMessage, messageType, status, sticker } = parseMessageContent(response);
|
|
131
|
+
const { currentElements, parsedMessage } = processTextMatches(
|
|
132
|
+
rawMessage,
|
|
133
|
+
useAt
|
|
134
|
+
);
|
|
135
|
+
const resultElements = processElements(currentElements);
|
|
136
|
+
if (resultElements[0]?.[0]?.type === "at" && resultElements.length > 1) {
|
|
137
|
+
resultElements[1].unshift(h.text(" "));
|
|
138
|
+
resultElements[1].unshift(resultElements[0][0]);
|
|
139
|
+
resultElements.shift();
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
elements: resultElements,
|
|
143
|
+
rawMessage: parsedMessage,
|
|
144
|
+
status,
|
|
145
|
+
sticker,
|
|
146
|
+
messageType
|
|
147
|
+
};
|
|
148
|
+
} catch (e) {
|
|
149
|
+
logger.error(e);
|
|
150
|
+
throw new Error("Failed to parse response: " + response);
|
|
125
151
|
}
|
|
126
|
-
return {
|
|
127
|
-
elements: resultElements,
|
|
128
|
-
rawMessage: parsedMessage,
|
|
129
|
-
status,
|
|
130
|
-
sticker,
|
|
131
|
-
messageType
|
|
132
|
-
};
|
|
133
152
|
}
|
|
134
153
|
__name(parseResponse, "parseResponse");
|
|
135
154
|
function splitSentence(text) {
|
|
@@ -213,7 +232,7 @@ function splitSentence(text) {
|
|
|
213
232
|
}
|
|
214
233
|
__name(splitSentence, "splitSentence");
|
|
215
234
|
function matchAt(str) {
|
|
216
|
-
const atRegex = /<at[^>]*>(.*?)<\/at
|
|
235
|
+
const atRegex = /<at[^>]*>(.*?)<\/at>/;
|
|
217
236
|
return [...str.matchAll(atRegex)].map((item) => {
|
|
218
237
|
return {
|
|
219
238
|
at: item[1],
|
|
@@ -223,6 +242,17 @@ function matchAt(str) {
|
|
|
223
242
|
});
|
|
224
243
|
}
|
|
225
244
|
__name(matchAt, "matchAt");
|
|
245
|
+
function matchPre(str) {
|
|
246
|
+
const preRegex = /<pre>(.*?)<\/pre>/gs;
|
|
247
|
+
return [...str.matchAll(preRegex)].map((item) => {
|
|
248
|
+
return {
|
|
249
|
+
pre: item[1],
|
|
250
|
+
start: item.index,
|
|
251
|
+
end: item.index + item[0].length
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
__name(matchPre, "matchPre");
|
|
226
256
|
async function formatMessage(messages, config, model, systemPrompt, historyPrompt) {
|
|
227
257
|
const maxTokens = config.maxTokens - 300;
|
|
228
258
|
let currentTokens = 0;
|
|
@@ -300,7 +330,7 @@ function parseXmlToObject(xml) {
|
|
|
300
330
|
return { name: name2, id, type, sticker, content };
|
|
301
331
|
}
|
|
302
332
|
__name(parseXmlToObject, "parseXmlToObject");
|
|
303
|
-
var tagRegExp =
|
|
333
|
+
var tagRegExp = /<(\/?)([^!\s>/]+)([^>]*?)\s*(\/?)>/;
|
|
304
334
|
function renderToken(token) {
|
|
305
335
|
if (token.type === "code") {
|
|
306
336
|
return h("text", { code: true, content: token.text + "\n" });
|
|
@@ -487,7 +517,7 @@ async function apply(ctx, config) {
|
|
|
487
517
|
}
|
|
488
518
|
let maxTime = text.length * copyOfConfig.typingTime + 100;
|
|
489
519
|
if (elements.length === 1 && elements[0].attrs["code"] === true) {
|
|
490
|
-
maxTime =
|
|
520
|
+
maxTime = maxTime * 0.1;
|
|
491
521
|
}
|
|
492
522
|
if (parsedResponse.messageType === "voice" && emoticonStatement !== "text") {
|
|
493
523
|
continue;
|
|
@@ -506,10 +536,10 @@ async function apply(ctx, config) {
|
|
|
506
536
|
continue;
|
|
507
537
|
}
|
|
508
538
|
try {
|
|
509
|
-
if (emoticonStatement !== "
|
|
539
|
+
if (emoticonStatement !== "span") {
|
|
510
540
|
await sleep(random.int(maxTime / 2, maxTime));
|
|
511
541
|
} else {
|
|
512
|
-
await sleep(random.int(maxTime /
|
|
542
|
+
await sleep(random.int(maxTime / 12, maxTime / 4));
|
|
513
543
|
}
|
|
514
544
|
switch (parsedResponse.messageType) {
|
|
515
545
|
case "text":
|
|
@@ -1283,7 +1313,7 @@ var Config = Schema3.intersect([
|
|
|
1283
1313
|
"是否启用强制禁言(当聊天涉及到关键词时则会禁言,关键词需要在预设文件里配置)"
|
|
1284
1314
|
).default(true),
|
|
1285
1315
|
isAt: Schema3.boolean().description("是否允许 bot 艾特他人").default(true),
|
|
1286
|
-
splitVoice: Schema3.boolean().description("
|
|
1316
|
+
splitVoice: Schema3.boolean().description("是否分段发送语音").default(false),
|
|
1287
1317
|
messageInterval: Schema3.number().default(14).min(0).role("slider").max(100).description("随机发送消息的间隔"),
|
|
1288
1318
|
messageProbability: Schema3.number().default(0.1).min(0).max(4).role("slider").step(1e-5).description("发送消息的叠加概率(线性增长)"),
|
|
1289
1319
|
coolDownTime: Schema3.number().default(10).min(1).max(60 * 24).description("冷却发言时间(秒)"),
|
package/package.json
CHANGED
|
@@ -140,7 +140,7 @@ system: |
|
|
|
140
140
|
|
|
141
141
|
特殊元素: {{
|
|
142
142
|
at: "<at name='name'>id</at>"
|
|
143
|
-
颜文字: "<
|
|
143
|
+
颜文字: "<pre>emo</pre>"
|
|
144
144
|
}}
|
|
145
145
|
|
|
146
146
|
sticker: 从 {stickers} 中选择合适的表情包类型
|
|
@@ -148,14 +148,14 @@ system: |
|
|
|
148
148
|
示例: {{
|
|
149
149
|
普通回复: "<message name='煕' id='0' type='text' sticker='表情包类型'>回复内容</message>",
|
|
150
150
|
At回复: "<message name='煕' id='0' type='text' sticker='表情包类型'><at name='用户'>123</at>回复内容</message>",
|
|
151
|
-
带颜文字: "<message name='煕' id='0' type='text' sticker='表情包类型'><
|
|
151
|
+
带颜文字: "<message name='煕' id='0' type='text' sticker='表情包类型'><pre>(づ。◕‿‿◕。)づ</pre> 回复内容 <pre>(✿◠‿◠)</pre></message>",
|
|
152
152
|
语音回复: "<message name='煕' id='0' type='voice' sticker='表情包类型'>语音内容</message>",
|
|
153
153
|
无需回复: "<message name='煕' id='0' type='text' sticker='表情包类型'></message>"
|
|
154
154
|
}}
|
|
155
155
|
|
|
156
156
|
注意事项: {{
|
|
157
157
|
1. sticker 必须从指定的 {stickers} 中选择
|
|
158
|
-
2. 颜文字使用 <
|
|
158
|
+
2. 颜文字使用 <p> 标签包裹,多个颜文字间用空格分隔
|
|
159
159
|
3. At 功能可在回复内容中使用多次
|
|
160
160
|
4. 如不需要回复,返回空内容的消息
|
|
161
161
|
}}
|