mioku-plugin-chat 2.10.1 → 3.0.1
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/configs/base.ts +1 -1
- package/context.ts +2 -2
- package/core/base.ts +113 -82
- package/core/chat-engine.ts +44 -23
- package/core/chat-turn.ts +11 -11
- package/core/media/audio.ts +4 -4
- package/core/media/gif-extractor.ts +4 -4
- package/core/media/history-media.ts +11 -16
- package/core/media/image-analyzer.ts +1 -1
- package/core/media/image-compress.ts +1 -1
- package/core/media/markdown-message.ts +22 -0
- package/core/media/segment.ts +10 -14
- package/core/multimodal.ts +18 -13
- package/core/prompt.ts +12 -10
- package/core/tools/info.ts +8 -7
- package/core/tools/web.ts +96 -57
- package/core/tools.ts +1 -1
- package/core/web/searxng.ts +1 -1
- package/core/web/web-reader.ts +2 -2
- package/db.ts +6 -3
- package/handlers/idle-debug.ts +4 -4
- package/handlers/message.ts +44 -29
- package/handlers/poke.ts +22 -16
- package/humanize/emoji-agent.ts +1 -1
- package/humanize/emotion-agent.ts +1 -1
- package/humanize/expression.ts +1 -1
- package/humanize/memory.ts +1 -1
- package/humanize/planner.ts +1 -1
- package/humanize/topic.ts +1 -1
- package/index.ts +3 -3
- package/manage/cleanup.ts +1 -1
- package/manage/cooldown.ts +5 -5
- package/manage/group-structured-history.ts +1 -1
- package/manage/idle-check.ts +5 -5
- package/manage/queue-processor.ts +5 -5
- package/manage/types.ts +39 -15
- package/package.json +3 -5
- package/types.ts +5 -5
- package/utils/message.ts +111 -66
- package/utils/queue.ts +7 -6
package/utils/message.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { logger,
|
|
2
|
-
import type { AIInstance } from "mioku";
|
|
1
|
+
import { HistoryMessage, logger, type MiokuContext } from "mioku";
|
|
2
|
+
import type { AIInstance, Bot } from "mioku";
|
|
3
|
+
|
|
3
4
|
import type {
|
|
4
5
|
ChatConfig,
|
|
5
6
|
ChatMessage,
|
|
@@ -22,13 +23,15 @@ export function shouldTrigger(
|
|
|
22
23
|
e: any,
|
|
23
24
|
text: string,
|
|
24
25
|
cfg: ChatConfig,
|
|
25
|
-
ctx:
|
|
26
|
+
ctx: MiokuContext,
|
|
26
27
|
): boolean {
|
|
27
28
|
if (e.message_type === "private") return false;
|
|
28
29
|
|
|
29
|
-
// Only check if message @s the bot (seg format: {type: "at", qq: "123456"})
|
|
30
30
|
const atSeg = e.message?.find((seg: any) => seg.type === "at");
|
|
31
|
-
|
|
31
|
+
if (!atSeg) return false;
|
|
32
|
+
const data = (atSeg.data ?? {}) as { qq?: unknown; target?: unknown };
|
|
33
|
+
const qq = data.qq ?? data.target;
|
|
34
|
+
return String(qq ?? "") === String(e.self_id ?? "");
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
/**
|
|
@@ -37,15 +40,15 @@ export function shouldTrigger(
|
|
|
37
40
|
*/
|
|
38
41
|
export async function isQuotingBot(
|
|
39
42
|
e: any,
|
|
40
|
-
ctx:
|
|
43
|
+
ctx: MiokuContext,
|
|
41
44
|
): Promise<{ quoted: true; messageId: string; content: string } | null> {
|
|
42
45
|
if (e.quote_id) {
|
|
43
46
|
try {
|
|
44
|
-
const quoteMsg = await
|
|
45
|
-
if (quoteMsg && String(quoteMsg.sender
|
|
47
|
+
const quoteMsg = await fetchQuotedMessage(e, ctx);
|
|
48
|
+
if (quoteMsg && String(quoteMsg.sender?.user_id) === String(e.self_id)) {
|
|
46
49
|
const quotedText = quoteMsg.message
|
|
47
50
|
?.filter((s: any) => s.type === "text")
|
|
48
|
-
.map((s: any) => s.text)
|
|
51
|
+
.map((s: any) => String(s.data?.text ?? s.text ?? ""))
|
|
49
52
|
.join("");
|
|
50
53
|
if (quotedText) {
|
|
51
54
|
return { quoted: true, messageId: e.quote_id, content: quotedText };
|
|
@@ -53,7 +56,7 @@ export async function isQuotingBot(
|
|
|
53
56
|
return null;
|
|
54
57
|
}
|
|
55
58
|
} catch (err) {
|
|
56
|
-
logger.error(err);
|
|
59
|
+
ctx.logger.error(err);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
return null;
|
|
@@ -65,7 +68,7 @@ export async function isQuotingBot(
|
|
|
65
68
|
*/
|
|
66
69
|
export async function getQuotedContent(
|
|
67
70
|
e: any,
|
|
68
|
-
ctx:
|
|
71
|
+
ctx: MiokuContext,
|
|
69
72
|
): Promise<
|
|
70
73
|
| {
|
|
71
74
|
messageId: string;
|
|
@@ -78,13 +81,13 @@ export async function getQuotedContent(
|
|
|
78
81
|
> {
|
|
79
82
|
if (e.quote_id) {
|
|
80
83
|
try {
|
|
81
|
-
const quotedMsg = await
|
|
84
|
+
const quotedMsg = await fetchQuotedMessage(e, ctx);
|
|
82
85
|
if (quotedMsg && quotedMsg.message) {
|
|
83
|
-
const senderName = quotedMsg.sender
|
|
86
|
+
const senderName = quotedMsg.sender?.nickname || "";
|
|
84
87
|
// 提取文本内容
|
|
85
88
|
const textContent = quotedMsg.message
|
|
86
89
|
.filter((s: any) => s.type === "text")
|
|
87
|
-
.map((s: any) => s.text
|
|
90
|
+
.map((s: any) => String(s.data?.text ?? s.text ?? ""))
|
|
88
91
|
.join("");
|
|
89
92
|
|
|
90
93
|
// 检测是否有图片
|
|
@@ -120,7 +123,7 @@ export function isGroupAllowed(groupId: number, cfg: ChatConfig): boolean {
|
|
|
120
123
|
export function extractContent(
|
|
121
124
|
e: any,
|
|
122
125
|
cfg: ChatConfig,
|
|
123
|
-
ctx:
|
|
126
|
+
ctx: MiokuContext,
|
|
124
127
|
): { text: string; multimodal: any[] | null } {
|
|
125
128
|
let text = "";
|
|
126
129
|
try {
|
|
@@ -129,9 +132,12 @@ export function extractContent(
|
|
|
129
132
|
|
|
130
133
|
// If text is empty but user @'d the bot, describe the action
|
|
131
134
|
if (!text.trim() && e.message) {
|
|
132
|
-
const hasAt = e.message.some(
|
|
133
|
-
(seg
|
|
134
|
-
|
|
135
|
+
const hasAt = e.message.some((seg: any) => {
|
|
136
|
+
if (seg.type !== "at") return false;
|
|
137
|
+
const data = (seg.data ?? {}) as { qq?: unknown; target?: unknown };
|
|
138
|
+
const qq = data.qq ?? data.target;
|
|
139
|
+
return String(qq ?? "") === String(e.self_id ?? "");
|
|
140
|
+
});
|
|
135
141
|
if (hasAt) {
|
|
136
142
|
text = "[@you with no text]";
|
|
137
143
|
}
|
|
@@ -190,14 +196,14 @@ async function mapWithConcurrency<T, R>(
|
|
|
190
196
|
|
|
191
197
|
export async function getBotRole(
|
|
192
198
|
groupId: number,
|
|
193
|
-
ctx:
|
|
199
|
+
ctx: MiokuContext,
|
|
194
200
|
selfId: number,
|
|
195
201
|
): Promise<"owner" | "admin" | "member"> {
|
|
196
202
|
try {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return (memberInfo
|
|
203
|
+
const bot = ctx.pickBot(selfId);
|
|
204
|
+
if (!bot) return "member";
|
|
205
|
+
const memberInfo = await bot.getMemberInfo(groupId, selfId);
|
|
206
|
+
return (memberInfo?.role as "owner" | "admin" | "member") || "member";
|
|
201
207
|
} catch {
|
|
202
208
|
return "member";
|
|
203
209
|
}
|
|
@@ -208,7 +214,7 @@ interface FormattedHistoryMessage {
|
|
|
208
214
|
userName: string;
|
|
209
215
|
userRole: string;
|
|
210
216
|
content: string;
|
|
211
|
-
messageId:
|
|
217
|
+
messageId: string;
|
|
212
218
|
timestamp: number;
|
|
213
219
|
role: "user" | "assistant";
|
|
214
220
|
}
|
|
@@ -223,6 +229,8 @@ interface HistoryFormatContext {
|
|
|
223
229
|
| undefined;
|
|
224
230
|
historyMediaOptions: HistoryMediaProcessingOptions;
|
|
225
231
|
botUin: number;
|
|
232
|
+
/** 本运行时全部已连接 bot 的 id:这些账号的消息一律不作为用户历史(防 bot 互触循环) */
|
|
233
|
+
botIds: Set<string>;
|
|
226
234
|
memberNameCache: Map<string, string>;
|
|
227
235
|
hashLookupCache: Map<string, string | null>;
|
|
228
236
|
}
|
|
@@ -237,11 +245,11 @@ async function resolveMemberName(
|
|
|
237
245
|
}
|
|
238
246
|
ctx.memberNameCache.set(key, key);
|
|
239
247
|
try {
|
|
240
|
-
const bot = ctx.historyMediaOptions.bot as
|
|
241
|
-
if (!bot
|
|
242
|
-
const info = await bot.
|
|
243
|
-
ctx.historyMediaOptions.groupId
|
|
244
|
-
|
|
248
|
+
const bot = ctx.historyMediaOptions.bot as Bot | undefined;
|
|
249
|
+
if (!bot) return key;
|
|
250
|
+
const info = await bot.getMemberInfo(
|
|
251
|
+
ctx.historyMediaOptions.groupId!,
|
|
252
|
+
userId,
|
|
245
253
|
);
|
|
246
254
|
const name = info?.card || info?.nickname || key;
|
|
247
255
|
ctx.memberNameCache.set(key, name);
|
|
@@ -252,6 +260,8 @@ async function resolveMemberName(
|
|
|
252
260
|
}
|
|
253
261
|
|
|
254
262
|
function extractQuotedText(messageSegs: any): string {
|
|
263
|
+
// icqq 的 source.message 是 brief 字符串;onebot 是 segment 数组
|
|
264
|
+
if (typeof messageSegs === "string") return messageSegs.trim();
|
|
255
265
|
if (!Array.isArray(messageSegs) || messageSegs.length === 0) return "";
|
|
256
266
|
const parts: string[] = [];
|
|
257
267
|
for (const seg of messageSegs) {
|
|
@@ -278,7 +288,10 @@ function extractQuotedText(messageSegs: any): string {
|
|
|
278
288
|
return parts.join(" ").trim();
|
|
279
289
|
}
|
|
280
290
|
|
|
281
|
-
function buildReplyAnnotation(
|
|
291
|
+
function buildReplyAnnotation(
|
|
292
|
+
source: any,
|
|
293
|
+
ctx: HistoryFormatContext,
|
|
294
|
+
): string | null {
|
|
282
295
|
if (!source || typeof source !== "object") return null;
|
|
283
296
|
const sourceId = source.id ?? source.message_id ?? source.message_seq;
|
|
284
297
|
const sourceUserId = source.user_id;
|
|
@@ -291,7 +304,8 @@ function buildReplyAnnotation(source: any, ctx: HistoryFormatContext): string |
|
|
|
291
304
|
const idStr = sourceId != null ? String(sourceId) : "?";
|
|
292
305
|
let displayName: string | undefined;
|
|
293
306
|
if (sourceUserId != null) {
|
|
294
|
-
displayName =
|
|
307
|
+
displayName =
|
|
308
|
+
ctx.memberNameCache.get(String(sourceUserId)) || String(sourceUserId);
|
|
295
309
|
}
|
|
296
310
|
if (!displayName) displayName = sourceNickname || "unknown";
|
|
297
311
|
|
|
@@ -334,10 +348,13 @@ async function getImageTagWithHashCache(
|
|
|
334
348
|
}
|
|
335
349
|
|
|
336
350
|
async function formatHistoryMessage(
|
|
337
|
-
msg:
|
|
351
|
+
msg: HistoryMessage,
|
|
338
352
|
ctx: HistoryFormatContext,
|
|
339
353
|
): Promise<FormattedHistoryMessage | null> {
|
|
354
|
+
// 自己或本运行时其它 bot 的消息不作为用户历史,
|
|
355
|
+
// 否则多 bot 同群时会把彼此的消息当作用户接话,形成循环触发
|
|
340
356
|
if (String(msg.user_id) === String(ctx.botUin)) return null;
|
|
357
|
+
if (ctx.botIds.has(String(msg.user_id))) return null;
|
|
341
358
|
|
|
342
359
|
let content = "";
|
|
343
360
|
try {
|
|
@@ -350,12 +367,15 @@ async function formatHistoryMessage(
|
|
|
350
367
|
if (!content.trim()) return null;
|
|
351
368
|
|
|
352
369
|
return {
|
|
353
|
-
userId: msg.user_id,
|
|
354
|
-
userName: msg.
|
|
355
|
-
userRole:
|
|
370
|
+
userId: Number(msg.user_id || 0),
|
|
371
|
+
userName: msg.nickname || String(msg.user_id || "unknown"),
|
|
372
|
+
userRole: "member",
|
|
356
373
|
content,
|
|
357
|
-
messageId:
|
|
358
|
-
|
|
374
|
+
messageId:
|
|
375
|
+
msg.message_id != null && msg.message_id !== ""
|
|
376
|
+
? String(msg.message_id)
|
|
377
|
+
: "",
|
|
378
|
+
timestamp: msg.time ?? Date.now(),
|
|
359
379
|
role: "user",
|
|
360
380
|
};
|
|
361
381
|
}
|
|
@@ -427,17 +447,24 @@ async function buildMessageContent(
|
|
|
427
447
|
)) {
|
|
428
448
|
const videoSources = getSegmentSourceCandidates(videoSeg);
|
|
429
449
|
if (videoSources.length > 0) {
|
|
430
|
-
parts.push(
|
|
450
|
+
parts.push(
|
|
451
|
+
await getCachedHistoryVideoTag(videoSources, historyMediaOptions),
|
|
452
|
+
);
|
|
431
453
|
} else {
|
|
432
454
|
parts.push("[video]");
|
|
433
455
|
}
|
|
434
456
|
}
|
|
435
457
|
|
|
436
|
-
for (const forwardSeg of msg.message.filter(
|
|
458
|
+
for (const forwardSeg of msg.message.filter(
|
|
459
|
+
(seg: any) => seg.type === "forward",
|
|
460
|
+
)) {
|
|
437
461
|
const forwardId = forwardSeg.id || forwardSeg.data?.id;
|
|
438
462
|
if (forwardId) {
|
|
439
463
|
parts.push(
|
|
440
|
-
await getCachedHistoryForwardTag(
|
|
464
|
+
await getCachedHistoryForwardTag(
|
|
465
|
+
String(forwardId),
|
|
466
|
+
historyMediaOptions,
|
|
467
|
+
),
|
|
441
468
|
);
|
|
442
469
|
} else {
|
|
443
470
|
parts.push("[forward]");
|
|
@@ -448,7 +475,11 @@ async function buildMessageContent(
|
|
|
448
475
|
["xml", "json", "lightapp", "ark"].includes(seg.type),
|
|
449
476
|
)) {
|
|
450
477
|
const cardData =
|
|
451
|
-
cardSeg.data?.data ||
|
|
478
|
+
cardSeg.data?.data ||
|
|
479
|
+
cardSeg.data?.xml ||
|
|
480
|
+
cardSeg.data ||
|
|
481
|
+
cardSeg.xml ||
|
|
482
|
+
"";
|
|
452
483
|
if (cardData) {
|
|
453
484
|
parts.push(
|
|
454
485
|
getCachedHistoryCardTag(
|
|
@@ -464,7 +495,9 @@ async function buildMessageContent(
|
|
|
464
495
|
if (parts.length > 0) return parts.join(" ");
|
|
465
496
|
|
|
466
497
|
const segTypes = msg.message.map((seg: any) => seg.type);
|
|
467
|
-
const nonTextTypes = segTypes.filter(
|
|
498
|
+
const nonTextTypes = segTypes.filter(
|
|
499
|
+
(t: string) => t !== "text" && t !== "at",
|
|
500
|
+
);
|
|
468
501
|
return nonTextTypes.length > 0 ? `[${nonTextTypes.join(", ")}]` : "";
|
|
469
502
|
}
|
|
470
503
|
|
|
@@ -474,7 +507,7 @@ async function buildMessageContent(
|
|
|
474
507
|
*/
|
|
475
508
|
export async function getGroupHistory(
|
|
476
509
|
groupId: number,
|
|
477
|
-
ctx:
|
|
510
|
+
ctx: MiokuContext,
|
|
478
511
|
count: number = 100,
|
|
479
512
|
selfId: number,
|
|
480
513
|
db?: {
|
|
@@ -501,7 +534,7 @@ export async function getGroupHistory(
|
|
|
501
534
|
userName: string;
|
|
502
535
|
userRole: string;
|
|
503
536
|
content: string;
|
|
504
|
-
messageId:
|
|
537
|
+
messageId: string;
|
|
505
538
|
timestamp: number;
|
|
506
539
|
role: "user" | "assistant";
|
|
507
540
|
}>
|
|
@@ -512,7 +545,7 @@ export async function getGroupHistory(
|
|
|
512
545
|
userName: string;
|
|
513
546
|
userRole: string;
|
|
514
547
|
content: string;
|
|
515
|
-
messageId:
|
|
548
|
+
messageId: string;
|
|
516
549
|
timestamp: number;
|
|
517
550
|
role: "user" | "assistant";
|
|
518
551
|
}> = [];
|
|
@@ -525,7 +558,7 @@ export async function getGroupHistory(
|
|
|
525
558
|
userName: msg.userName || "Miku",
|
|
526
559
|
userRole: msg.userRole || "member",
|
|
527
560
|
content: msg.content,
|
|
528
|
-
messageId: msg.messageId ??
|
|
561
|
+
messageId: msg.messageId ?? "",
|
|
529
562
|
timestamp: msg.timestamp,
|
|
530
563
|
role: "assistant",
|
|
531
564
|
});
|
|
@@ -539,7 +572,7 @@ export async function getGroupHistory(
|
|
|
539
572
|
userName: msg.userName || String(msg.userId || "unknown"),
|
|
540
573
|
userRole: msg.userRole || "member",
|
|
541
574
|
content: msg.content,
|
|
542
|
-
messageId: msg.messageId ??
|
|
575
|
+
messageId: msg.messageId ?? "",
|
|
543
576
|
timestamp: msg.timestamp,
|
|
544
577
|
role: "user",
|
|
545
578
|
});
|
|
@@ -547,8 +580,9 @@ export async function getGroupHistory(
|
|
|
547
580
|
}
|
|
548
581
|
|
|
549
582
|
try {
|
|
550
|
-
const bot = ctx.pickBot(selfId)
|
|
583
|
+
const bot = ctx.pickBot(selfId);
|
|
551
584
|
const historyMediaOptions: HistoryMediaProcessingOptions = {
|
|
585
|
+
bot,
|
|
552
586
|
ai: mediaOptions?.ai,
|
|
553
587
|
workingModel: mediaOptions?.workingModel,
|
|
554
588
|
multimodalWorkingModel: mediaOptions?.multimodalWorkingModel,
|
|
@@ -561,32 +595,25 @@ export async function getGroupHistory(
|
|
|
561
595
|
saveMediaSummarySource: db.saveMediaSummarySource?.bind(db),
|
|
562
596
|
}
|
|
563
597
|
: undefined,
|
|
564
|
-
bot,
|
|
565
598
|
groupId,
|
|
566
599
|
};
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
parse_mult_msg: true,
|
|
575
|
-
quick_reply: false,
|
|
576
|
-
});
|
|
577
|
-
const messages = result?.messages || result?.data?.messages || [];
|
|
578
|
-
if (!Array.isArray(messages)) {
|
|
579
|
-
logger.warn("[getGroupHistory] API 返回格式异常:", result);
|
|
580
|
-
return botMessages;
|
|
581
|
-
}
|
|
600
|
+
const messages = bot
|
|
601
|
+
? await bot.getHistory(
|
|
602
|
+
{ type: "group", group_id: groupId },
|
|
603
|
+
undefined,
|
|
604
|
+
Math.min(count, 200),
|
|
605
|
+
)
|
|
606
|
+
: [];
|
|
582
607
|
|
|
583
608
|
const botUin = selfId;
|
|
609
|
+
const botIds = new Set<string>(ctx.bots.map((b) => String(b.bot_id)));
|
|
610
|
+
botIds.add(String(botUin));
|
|
584
611
|
const memberNameCache = new Map<string, string>();
|
|
585
612
|
const hashLookupCache = new Map<string, string | null>();
|
|
586
613
|
|
|
587
614
|
for (const m of messages) {
|
|
588
|
-
const uid = m
|
|
589
|
-
const senderName = m
|
|
615
|
+
const uid = m.user_id;
|
|
616
|
+
const senderName = m.nickname;
|
|
590
617
|
if (uid != null && senderName && !memberNameCache.has(String(uid))) {
|
|
591
618
|
memberNameCache.set(String(uid), String(senderName));
|
|
592
619
|
}
|
|
@@ -596,6 +623,7 @@ export async function getGroupHistory(
|
|
|
596
623
|
db,
|
|
597
624
|
historyMediaOptions,
|
|
598
625
|
botUin,
|
|
626
|
+
botIds,
|
|
599
627
|
memberNameCache,
|
|
600
628
|
hashLookupCache,
|
|
601
629
|
};
|
|
@@ -657,3 +685,20 @@ export function normalizeIdList(input: unknown): number[] {
|
|
|
657
685
|
),
|
|
658
686
|
);
|
|
659
687
|
}
|
|
688
|
+
|
|
689
|
+
async function fetchQuotedMessage(
|
|
690
|
+
e: any,
|
|
691
|
+
ctx: MiokuContext,
|
|
692
|
+
): Promise<{
|
|
693
|
+
message: any[];
|
|
694
|
+
sender?: { user_id?: string; nickname?: string };
|
|
695
|
+
} | null> {
|
|
696
|
+
const bot = e?.bot ?? ctx.pickBot(e?.self_id ?? "");
|
|
697
|
+
if (!bot || !e?.quote_id) return null;
|
|
698
|
+
try {
|
|
699
|
+
const result = await bot.getMessage(e.quote_id);
|
|
700
|
+
return result ?? null;
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
}
|
package/utils/queue.ts
CHANGED
|
@@ -100,7 +100,7 @@ export class MessageQueueManager {
|
|
|
100
100
|
* 例如 "[reply:1]文字A[reply:2]文字B" → ["[reply:1]文字A", "[reply:2]文字B"]
|
|
101
101
|
*/
|
|
102
102
|
export function splitByReplyMarkers(line: string): string[] {
|
|
103
|
-
const parts = line.split(/(?=\[reply
|
|
103
|
+
const parts = line.split(/(?=\[reply:[^\]\n]+\])/);
|
|
104
104
|
return parts.filter((p) => p.trim());
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -116,12 +116,12 @@ export function parseLineMarkers(
|
|
|
116
116
|
cleanText: string;
|
|
117
117
|
atUsers: number[];
|
|
118
118
|
pokeUsers: number[];
|
|
119
|
-
quoteId?:
|
|
119
|
+
quoteId?: string;
|
|
120
120
|
audioText?: string;
|
|
121
121
|
} {
|
|
122
122
|
const atUsers: number[] = [];
|
|
123
123
|
const pokeUsers: number[] = [];
|
|
124
|
-
let quoteId:
|
|
124
|
+
let quoteId: string | undefined;
|
|
125
125
|
let audioText: string | undefined;
|
|
126
126
|
|
|
127
127
|
// 提取 AT 标记
|
|
@@ -146,12 +146,13 @@ export function parseLineMarkers(
|
|
|
146
146
|
|
|
147
147
|
// 提取引用标记(仅在允许时)
|
|
148
148
|
if (quoteMode !== "skip") {
|
|
149
|
-
const replyPatterns = [/\[reply:(
|
|
149
|
+
const replyPatterns = [/\[reply:([^\]\n]+)\]/g];
|
|
150
150
|
for (const pattern of replyPatterns) {
|
|
151
151
|
const matches = [...line.matchAll(pattern)];
|
|
152
152
|
for (const match of matches) {
|
|
153
153
|
if (quoteId === undefined) {
|
|
154
|
-
|
|
154
|
+
const value = match[1].trim();
|
|
155
|
+
if (value) quoteId = value;
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
}
|
|
@@ -170,7 +171,7 @@ export function parseLineMarkers(
|
|
|
170
171
|
let cleanText = line
|
|
171
172
|
.replace(/\[at:\d+\]/g, "")
|
|
172
173
|
.replace(/\[poke:\d+\]/g, "")
|
|
173
|
-
.replace(/\[reply
|
|
174
|
+
.replace(/\[reply:[^\]\n]+\]/g, "")
|
|
174
175
|
.replace(/\[audio:[^\]]+\]/gi, "")
|
|
175
176
|
.trim();
|
|
176
177
|
|