mioku-plugin-chat 3.0.5 → 3.1.0
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 -0
- package/context.ts +5 -5
- package/core/base.ts +20 -20
- package/core/chat-engine.ts +6 -6
- package/core/chat-turn.ts +30 -26
- package/core/media/history-media.ts +13 -13
- package/core/media/segment.ts +2 -2
- package/core/prompt.ts +1 -1
- package/core/tools/info.ts +9 -4
- package/core/tools/web.ts +6 -9
- package/db.ts +13 -13
- package/handlers/idle-debug.ts +7 -7
- package/handlers/message.ts +7 -8
- package/handlers/poke.ts +15 -14
- package/humanize/emotion-agent.ts +5 -5
- package/humanize/expression.ts +6 -6
- package/humanize/index.ts +1 -1
- package/humanize/memory.ts +1 -1
- package/index.ts +30 -22
- package/manage/cooldown.ts +8 -8
- package/manage/group-structured-history.ts +2 -2
- package/manage/idle-check.ts +12 -11
- package/manage/queue-processor.ts +9 -8
- package/manage/rate-limit-guard.ts +2 -2
- package/manage/rate-limiter.ts +21 -21
- package/manage/session.ts +1 -1
- package/manage/types.ts +16 -16
- package/package.json +1 -1
- package/runtime/chat-runtime.ts +4 -4
- package/types.ts +12 -11
- package/utils/config.ts +4 -4
- package/utils/group-config.ts +3 -4
- package/utils/message.ts +21 -21
- package/utils/queue.ts +12 -11
package/manage/types.ts
CHANGED
|
@@ -31,27 +31,27 @@ export type HistoryMediaOptions = {
|
|
|
31
31
|
export type RunRateLimitGuard = <T>(
|
|
32
32
|
request: () => Promise<T>,
|
|
33
33
|
opts?: {
|
|
34
|
-
userId?:
|
|
35
|
-
groupId?:
|
|
34
|
+
userId?: string;
|
|
35
|
+
groupId?: string;
|
|
36
36
|
label?: string;
|
|
37
37
|
skipRetryOnRateLimit?: boolean;
|
|
38
38
|
},
|
|
39
39
|
) => Promise<T | null>;
|
|
40
40
|
|
|
41
41
|
export type GetGroupHistoryMessages = (
|
|
42
|
-
groupId:
|
|
42
|
+
groupId: string,
|
|
43
43
|
groupSessionId: string,
|
|
44
44
|
ctx: MiokuContext,
|
|
45
45
|
historyCount: number,
|
|
46
46
|
db: ChatDatabase,
|
|
47
|
-
selfId:
|
|
47
|
+
selfId: string,
|
|
48
48
|
mediaOptions?: HistoryMediaOptions,
|
|
49
49
|
) => Promise<{ history: ChatMessage[] }>;
|
|
50
50
|
|
|
51
51
|
export type GetGroupInfoData = (
|
|
52
52
|
ctx: MiokuContext,
|
|
53
|
-
groupId:
|
|
54
|
-
selfId:
|
|
53
|
+
groupId: string,
|
|
54
|
+
selfId: string,
|
|
55
55
|
fallbackGroupName?: string,
|
|
56
56
|
) => Promise<GroupInfoResult>;
|
|
57
57
|
|
|
@@ -60,16 +60,16 @@ export type GetHumanizeContexts = (
|
|
|
60
60
|
groupSessionId: string,
|
|
61
61
|
userName: string,
|
|
62
62
|
history: ChatMessage[],
|
|
63
|
-
triggerUserId?:
|
|
63
|
+
triggerUserId?: string,
|
|
64
64
|
) => Promise<HumanizeContextsResult>;
|
|
65
65
|
|
|
66
66
|
export type SendAIResponse = (
|
|
67
67
|
options: SendAIResponseOptions,
|
|
68
|
-
selfId:
|
|
68
|
+
selfId: string,
|
|
69
69
|
) => Promise<Array<string | undefined>>;
|
|
70
70
|
|
|
71
71
|
export type SaveBotMessages = (
|
|
72
|
-
groupId:
|
|
72
|
+
groupId: string,
|
|
73
73
|
groupSessionId: string,
|
|
74
74
|
messages: string[],
|
|
75
75
|
timestamp: number,
|
|
@@ -82,28 +82,28 @@ export type SaveBotMessages = (
|
|
|
82
82
|
|
|
83
83
|
export type SendEmoji = (
|
|
84
84
|
ctx: MiokuContext,
|
|
85
|
-
groupId:
|
|
85
|
+
groupId: string,
|
|
86
86
|
emojiPath: string | null | undefined,
|
|
87
87
|
bot?: import("mioku").Bot,
|
|
88
88
|
) => Promise<void>;
|
|
89
89
|
|
|
90
90
|
export type SendMessage = (
|
|
91
91
|
ctx: MiokuContext,
|
|
92
|
-
groupId:
|
|
93
|
-
userId:
|
|
92
|
+
groupId: string | undefined,
|
|
93
|
+
userId: string,
|
|
94
94
|
text: string,
|
|
95
95
|
config: ChatConfig,
|
|
96
|
-
selfId:
|
|
96
|
+
selfId: string,
|
|
97
97
|
audioService?: import("mioku-service-audio").AudioServiceApi,
|
|
98
98
|
) => Promise<void>;
|
|
99
99
|
|
|
100
100
|
export type BuildToolContext = (options: {
|
|
101
101
|
ctx: MiokuContext;
|
|
102
102
|
event: any;
|
|
103
|
-
selfId:
|
|
103
|
+
selfId: string;
|
|
104
104
|
groupSessionId: string;
|
|
105
|
-
groupId?:
|
|
106
|
-
userId:
|
|
105
|
+
groupId?: string;
|
|
106
|
+
userId: string;
|
|
107
107
|
config: ChatConfig;
|
|
108
108
|
aiService: AIService;
|
|
109
109
|
db: ChatDatabase;
|
package/package.json
CHANGED
package/runtime/chat-runtime.ts
CHANGED
|
@@ -12,21 +12,21 @@ import { executeChatRuntimeRequest } from "../core/chat-turn";
|
|
|
12
12
|
|
|
13
13
|
function resolveRuntimeGroupId(
|
|
14
14
|
options: ChatRuntimeNoticeOptions | ChatRuntimeInformationRequestOptions,
|
|
15
|
-
):
|
|
15
|
+
): string | undefined {
|
|
16
16
|
if ("event" in options) {
|
|
17
17
|
const event = options.event;
|
|
18
18
|
if (event?.kind !== "message" || event.message_type !== "group") {
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
21
|
-
const groupId
|
|
22
|
-
return
|
|
21
|
+
const groupId = String(event.group_id ?? "").trim();
|
|
22
|
+
return groupId || undefined;
|
|
23
23
|
}
|
|
24
24
|
return "groupId" in options ? options.groupId : undefined;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function createChatRuntime(
|
|
28
28
|
pluginCtx: ChatPluginContext,
|
|
29
|
-
getConfig: (groupId?:
|
|
29
|
+
getConfig: (groupId?: string) => Promise<ChatConfig>,
|
|
30
30
|
): ChatRuntime {
|
|
31
31
|
return {
|
|
32
32
|
async requestInformation(
|
package/types.ts
CHANGED
|
@@ -56,7 +56,7 @@ export interface PlannerConfig {
|
|
|
56
56
|
// 群聊记录保底消息数量
|
|
57
57
|
idleMessageCount: number;
|
|
58
58
|
// 空闲检查的 bot ID 列表
|
|
59
|
-
idleCheckBotIds:
|
|
59
|
+
idleCheckBotIds: string[];
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -174,6 +174,7 @@ export interface ChatConfig {
|
|
|
174
174
|
multimodalWorkingModel: string;
|
|
175
175
|
isMultimodal: boolean;
|
|
176
176
|
enableMediaRecognition: boolean;
|
|
177
|
+
ignorePrivateChat: boolean;
|
|
177
178
|
nicknames: string[];
|
|
178
179
|
persona: string;
|
|
179
180
|
maxContextTokens: number;
|
|
@@ -182,9 +183,9 @@ export interface ChatConfig {
|
|
|
182
183
|
webReader: WebReaderConfig;
|
|
183
184
|
audio: AudioConfig;
|
|
184
185
|
historyCount: number;
|
|
185
|
-
blacklistGroups:
|
|
186
|
-
whitelistGroups:
|
|
187
|
-
mediaAnalysisBlacklistUsers:
|
|
186
|
+
blacklistGroups: string[];
|
|
187
|
+
whitelistGroups: string[];
|
|
188
|
+
mediaAnalysisBlacklistUsers: string[];
|
|
188
189
|
maxSessions: number;
|
|
189
190
|
maxIterations: number;
|
|
190
191
|
enableExternalSkills: boolean;
|
|
@@ -248,7 +249,7 @@ export type SessionType = "group" | "personal";
|
|
|
248
249
|
export interface SessionMeta {
|
|
249
250
|
id: string; // "group:{group_id}" 或 "personal:{user_id}"
|
|
250
251
|
type: SessionType;
|
|
251
|
-
targetId:
|
|
252
|
+
targetId: string; // group_id 或 user_id
|
|
252
253
|
createdAt: number;
|
|
253
254
|
updatedAt: number;
|
|
254
255
|
compressedContext: string | null;
|
|
@@ -262,11 +263,11 @@ export interface ChatMessage {
|
|
|
262
263
|
sessionId: string;
|
|
263
264
|
role: "user" | "assistant" | "system";
|
|
264
265
|
content: string; // 存储时统一为字符串
|
|
265
|
-
userId?:
|
|
266
|
+
userId?: string;
|
|
266
267
|
userName?: string;
|
|
267
268
|
userRole?: string; // "owner" | "admin" | "member"
|
|
268
269
|
userTitle?: string;
|
|
269
|
-
groupId?:
|
|
270
|
+
groupId?: string;
|
|
270
271
|
groupName?: string;
|
|
271
272
|
timestamp: number;
|
|
272
273
|
messageId?: string; // 消息 id
|
|
@@ -277,7 +278,7 @@ export interface ChatMessage {
|
|
|
277
278
|
*/
|
|
278
279
|
export interface TargetMessage {
|
|
279
280
|
userName: string;
|
|
280
|
-
userId:
|
|
281
|
+
userId: string;
|
|
281
282
|
userRole: string;
|
|
282
283
|
userTitle?: string;
|
|
283
284
|
content: string;
|
|
@@ -302,8 +303,8 @@ export interface ToolContext {
|
|
|
302
303
|
ctx: MiokuContext;
|
|
303
304
|
event: any;
|
|
304
305
|
sessionId: string;
|
|
305
|
-
groupId?:
|
|
306
|
-
userId:
|
|
306
|
+
groupId?: string;
|
|
307
|
+
userId: string;
|
|
307
308
|
triggerSkillRole: SkillPermissionRole;
|
|
308
309
|
config: ChatConfig;
|
|
309
310
|
aiService: AIService;
|
|
@@ -363,7 +364,7 @@ export interface TopicRecord {
|
|
|
363
364
|
export interface ExpressionRecord {
|
|
364
365
|
id?: number;
|
|
365
366
|
sessionId: string;
|
|
366
|
-
userId:
|
|
367
|
+
userId: string;
|
|
367
368
|
userName: string;
|
|
368
369
|
situation: string; // 使用场景
|
|
369
370
|
style: string; // 表达风格
|
package/utils/config.ts
CHANGED
|
@@ -33,20 +33,20 @@ export function mergeChatConfig<T extends Record<string, any>>(
|
|
|
33
33
|
return result as T;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function normalizeIdList(input: unknown):
|
|
36
|
+
export function normalizeIdList(input: unknown): string[] {
|
|
37
37
|
if (!Array.isArray(input)) return [];
|
|
38
38
|
return Array.from(
|
|
39
39
|
new Set(
|
|
40
40
|
input
|
|
41
|
-
.map((item) =>
|
|
42
|
-
.filter((id) =>
|
|
41
|
+
.map((item) => String(item ?? "").trim())
|
|
42
|
+
.filter((id) => id.length > 0),
|
|
43
43
|
),
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export function normalizeMediaAnalysisBlacklist(
|
|
48
48
|
config: Record<string, any>,
|
|
49
|
-
):
|
|
49
|
+
): string[] {
|
|
50
50
|
const current = config.mediaAnalysisBlacklistUsers;
|
|
51
51
|
const legacy = config.imageAnalysisBlacklistUsers;
|
|
52
52
|
const source =
|
package/utils/group-config.ts
CHANGED
|
@@ -70,10 +70,9 @@ export function mergeGroupOverrides(
|
|
|
70
70
|
/**
|
|
71
71
|
* 从 sessionId(如 "group:123")提取群号,个人会话返回 undefined
|
|
72
72
|
*/
|
|
73
|
-
export function extractGroupIdFromSession(sessionId: string):
|
|
73
|
+
export function extractGroupIdFromSession(sessionId: string): string | undefined {
|
|
74
74
|
if (!sessionId || typeof sessionId !== "string") return undefined;
|
|
75
75
|
if (!sessionId.startsWith("group:")) return undefined;
|
|
76
|
-
const raw = sessionId.slice("group:".length);
|
|
77
|
-
|
|
78
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
76
|
+
const raw = sessionId.slice("group:".length).trim();
|
|
77
|
+
return raw.length > 0 ? raw : undefined;
|
|
79
78
|
}
|
package/utils/message.ts
CHANGED
|
@@ -111,7 +111,7 @@ export async function getQuotedContent(
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
export function isGroupAllowed(groupId:
|
|
114
|
+
export function isGroupAllowed(groupId: string, cfg: ChatConfig): boolean {
|
|
115
115
|
if (cfg.whitelistGroups.length > 0) {
|
|
116
116
|
return cfg.whitelistGroups.includes(groupId);
|
|
117
117
|
}
|
|
@@ -190,9 +190,9 @@ async function mapWithConcurrency<T, R>(
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export async function getBotRole(
|
|
193
|
-
groupId:
|
|
193
|
+
groupId: string,
|
|
194
194
|
ctx: MiokuContext,
|
|
195
|
-
selfId:
|
|
195
|
+
selfId: string,
|
|
196
196
|
): Promise<"owner" | "admin" | "member"> {
|
|
197
197
|
try {
|
|
198
198
|
const bot = ctx.pickBot(selfId);
|
|
@@ -205,7 +205,7 @@ export async function getBotRole(
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
interface FormattedHistoryMessage {
|
|
208
|
-
userId:
|
|
208
|
+
userId: string;
|
|
209
209
|
userName: string;
|
|
210
210
|
userRole: string;
|
|
211
211
|
content: string;
|
|
@@ -223,7 +223,7 @@ interface HistoryFormatContext {
|
|
|
223
223
|
}
|
|
224
224
|
| undefined;
|
|
225
225
|
historyMediaOptions: HistoryMediaProcessingOptions;
|
|
226
|
-
botUin:
|
|
226
|
+
botUin: string;
|
|
227
227
|
/** 本运行时全部已连接 bot 的 id:这些账号的消息一律不作为用户历史(防 bot 互触循环) */
|
|
228
228
|
botIds: Set<string>;
|
|
229
229
|
memberNameCache: Map<string, string>;
|
|
@@ -362,8 +362,8 @@ async function formatHistoryMessage(
|
|
|
362
362
|
if (!content.trim()) return null;
|
|
363
363
|
|
|
364
364
|
return {
|
|
365
|
-
userId:
|
|
366
|
-
userName: msg.nickname || String(msg.user_id
|
|
365
|
+
userId: String(msg.user_id ?? "").trim(),
|
|
366
|
+
userName: msg.nickname || String(msg.user_id ?? "unknown"),
|
|
367
367
|
userRole: "member",
|
|
368
368
|
content,
|
|
369
369
|
messageId:
|
|
@@ -501,12 +501,12 @@ async function buildMessageContent(
|
|
|
501
501
|
* 返回格式化为 ChatMessage 数组
|
|
502
502
|
*/
|
|
503
503
|
export async function getGroupHistory(
|
|
504
|
-
groupId:
|
|
504
|
+
groupId: string,
|
|
505
505
|
ctx: MiokuContext,
|
|
506
506
|
count: number = 100,
|
|
507
|
-
selfId:
|
|
507
|
+
selfId: string,
|
|
508
508
|
db?: {
|
|
509
|
-
getBotMessages(groupId:
|
|
509
|
+
getBotMessages(groupId: string, limit: number): ChatMessage[];
|
|
510
510
|
getImageByHash?(hash: string): ImageRecord | null;
|
|
511
511
|
getImageByUrl?(url: string): ImageRecord | null;
|
|
512
512
|
getMediaSummary?(key: string): MediaSummaryRecord | null;
|
|
@@ -514,7 +514,7 @@ export async function getGroupHistory(
|
|
|
514
514
|
getMediaSummaryBySource?(sourceKey: string): MediaSummaryRecord | null;
|
|
515
515
|
saveMediaSummarySource?(sourceKey: string, summaryKey: string): void;
|
|
516
516
|
getStoredGroupNoticeMessages?(
|
|
517
|
-
groupId:
|
|
517
|
+
groupId: string,
|
|
518
518
|
limit?: number,
|
|
519
519
|
): ChatMessage[];
|
|
520
520
|
},
|
|
@@ -525,7 +525,7 @@ export async function getGroupHistory(
|
|
|
525
525
|
},
|
|
526
526
|
): Promise<
|
|
527
527
|
Array<{
|
|
528
|
-
userId:
|
|
528
|
+
userId: string;
|
|
529
529
|
userName: string;
|
|
530
530
|
userRole: string;
|
|
531
531
|
content: string;
|
|
@@ -536,7 +536,7 @@ export async function getGroupHistory(
|
|
|
536
536
|
> {
|
|
537
537
|
// 先获取 bot 从数据库发送的消息
|
|
538
538
|
const botMessages: Array<{
|
|
539
|
-
userId:
|
|
539
|
+
userId: string;
|
|
540
540
|
userName: string;
|
|
541
541
|
userRole: string;
|
|
542
542
|
content: string;
|
|
@@ -549,7 +549,7 @@ export async function getGroupHistory(
|
|
|
549
549
|
const storedBotMessages = db.getBotMessages(groupId, count);
|
|
550
550
|
for (const msg of storedBotMessages) {
|
|
551
551
|
botMessages.push({
|
|
552
|
-
userId: msg.userId ??
|
|
552
|
+
userId: String(msg.userId ?? ""),
|
|
553
553
|
userName: msg.userName || "Miku",
|
|
554
554
|
userRole: msg.userRole || "member",
|
|
555
555
|
content: msg.content,
|
|
@@ -563,8 +563,8 @@ export async function getGroupHistory(
|
|
|
563
563
|
db.getStoredGroupNoticeMessages?.(groupId, Math.min(count, 20)) || [];
|
|
564
564
|
for (const msg of storedNoticeMessages) {
|
|
565
565
|
botMessages.push({
|
|
566
|
-
userId: msg.userId ??
|
|
567
|
-
userName: msg.userName || String(msg.userId
|
|
566
|
+
userId: String(msg.userId ?? ""),
|
|
567
|
+
userName: msg.userName || String(msg.userId ?? "unknown"),
|
|
568
568
|
userRole: msg.userRole || "member",
|
|
569
569
|
content: msg.content,
|
|
570
570
|
messageId: msg.messageId ?? "",
|
|
@@ -652,9 +652,9 @@ export function buildChatMessageFromEvent(
|
|
|
652
652
|
e: any,
|
|
653
653
|
text: string,
|
|
654
654
|
isGroup: boolean,
|
|
655
|
-
groupId:
|
|
655
|
+
groupId: string | undefined,
|
|
656
656
|
): ChatMessage {
|
|
657
|
-
const userId:
|
|
657
|
+
const userId: string = e.user_id || e.sender?.user_id;
|
|
658
658
|
return {
|
|
659
659
|
sessionId: groupId ? `group:${groupId}` : `personal:${userId}`,
|
|
660
660
|
role: "user" as const,
|
|
@@ -670,13 +670,13 @@ export function buildChatMessageFromEvent(
|
|
|
670
670
|
};
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
-
export function normalizeIdList(input: unknown):
|
|
673
|
+
export function normalizeIdList(input: unknown): string[] {
|
|
674
674
|
if (!Array.isArray(input)) return [];
|
|
675
675
|
return Array.from(
|
|
676
676
|
new Set(
|
|
677
677
|
input
|
|
678
|
-
.map((item) =>
|
|
679
|
-
.filter((id) =>
|
|
678
|
+
.map((item) => String(item ?? "").trim())
|
|
679
|
+
.filter((id) => id.length > 0),
|
|
680
680
|
),
|
|
681
681
|
);
|
|
682
682
|
}
|
package/utils/queue.ts
CHANGED
|
@@ -114,33 +114,34 @@ export function parseLineMarkers(
|
|
|
114
114
|
quoteMode?: "skip",
|
|
115
115
|
): {
|
|
116
116
|
cleanText: string;
|
|
117
|
-
atUsers:
|
|
118
|
-
pokeUsers:
|
|
117
|
+
atUsers: string[];
|
|
118
|
+
pokeUsers: string[];
|
|
119
119
|
quoteId?: string;
|
|
120
120
|
audioText?: string;
|
|
121
121
|
} {
|
|
122
|
-
const atUsers:
|
|
123
|
-
const pokeUsers:
|
|
122
|
+
const atUsers: string[] = [];
|
|
123
|
+
const pokeUsers: string[] = [];
|
|
124
124
|
let quoteId: string | undefined;
|
|
125
125
|
let audioText: string | undefined;
|
|
126
126
|
|
|
127
127
|
// 提取 AT 标记
|
|
128
|
-
|
|
128
|
+
// 标记里是平台用户 id:QQ 号或 openid 都可能出现
|
|
129
|
+
const atPatterns = [/\[at:([^\]\s]+)\]/g];
|
|
129
130
|
for (const pattern of atPatterns) {
|
|
130
131
|
const matches = [...line.matchAll(pattern)];
|
|
131
132
|
for (const match of matches) {
|
|
132
|
-
const userId =
|
|
133
|
-
atUsers.push(userId);
|
|
133
|
+
const userId = String(match[1]).trim();
|
|
134
|
+
if (userId) atUsers.push(userId);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
// 提取戳人标记
|
|
138
|
-
const pokePatterns = [/\[poke:(\
|
|
139
|
+
const pokePatterns = [/\[poke:([^\]\s]+)\]/g];
|
|
139
140
|
for (const pattern of pokePatterns) {
|
|
140
141
|
const matches = [...line.matchAll(pattern)];
|
|
141
142
|
for (const match of matches) {
|
|
142
|
-
const userId =
|
|
143
|
-
pokeUsers.push(userId);
|
|
143
|
+
const userId = String(match[1]).trim();
|
|
144
|
+
if (userId) pokeUsers.push(userId);
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
|
|
@@ -169,7 +170,7 @@ export function parseLineMarkers(
|
|
|
169
170
|
|
|
170
171
|
// 清理标记
|
|
171
172
|
let cleanText = line
|
|
172
|
-
.replace(/\[at
|
|
173
|
+
.replace(/\[at:[^\]\s]+\]/g, "")
|
|
173
174
|
.replace(/\[poke:\d+\]/g, "")
|
|
174
175
|
.replace(/\[reply:[^\]\n]+\]/g, "")
|
|
175
176
|
.replace(/\[audio:[^\]]+\]/gi, "")
|