mioku-plugin-chat 3.0.6 → 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/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?: number;
35
- groupId?: number;
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: number,
42
+ groupId: string,
43
43
  groupSessionId: string,
44
44
  ctx: MiokuContext,
45
45
  historyCount: number,
46
46
  db: ChatDatabase,
47
- selfId: number,
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: number,
54
- selfId: number,
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?: number,
63
+ triggerUserId?: string,
64
64
  ) => Promise<HumanizeContextsResult>;
65
65
 
66
66
  export type SendAIResponse = (
67
67
  options: SendAIResponseOptions,
68
- selfId: number,
68
+ selfId: string,
69
69
  ) => Promise<Array<string | undefined>>;
70
70
 
71
71
  export type SaveBotMessages = (
72
- groupId: number,
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: number,
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: number | undefined,
93
- userId: number,
92
+ groupId: string | undefined,
93
+ userId: string,
94
94
  text: string,
95
95
  config: ChatConfig,
96
- selfId: number,
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: number;
103
+ selfId: string;
104
104
  groupSessionId: string;
105
- groupId?: number;
106
- userId: number;
105
+ groupId?: string;
106
+ userId: string;
107
107
  config: ChatConfig;
108
108
  aiService: AIService;
109
109
  db: ChatDatabase;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-chat",
3
- "version": "3.0.6",
3
+ "version": "3.1.0",
4
4
  "description": "AI 智能聊天插件",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -12,21 +12,21 @@ import { executeChatRuntimeRequest } from "../core/chat-turn";
12
12
 
13
13
  function resolveRuntimeGroupId(
14
14
  options: ChatRuntimeNoticeOptions | ChatRuntimeInformationRequestOptions,
15
- ): number | undefined {
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: unknown = event.group_id;
22
- return typeof groupId === "number" ? groupId : undefined;
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?: number) => Promise<ChatConfig>,
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: number[];
59
+ idleCheckBotIds: string[];
60
60
  }
61
61
 
62
62
  /**
@@ -183,9 +183,9 @@ export interface ChatConfig {
183
183
  webReader: WebReaderConfig;
184
184
  audio: AudioConfig;
185
185
  historyCount: number;
186
- blacklistGroups: number[];
187
- whitelistGroups: number[];
188
- mediaAnalysisBlacklistUsers: number[];
186
+ blacklistGroups: string[];
187
+ whitelistGroups: string[];
188
+ mediaAnalysisBlacklistUsers: string[];
189
189
  maxSessions: number;
190
190
  maxIterations: number;
191
191
  enableExternalSkills: boolean;
@@ -249,7 +249,7 @@ export type SessionType = "group" | "personal";
249
249
  export interface SessionMeta {
250
250
  id: string; // "group:{group_id}" 或 "personal:{user_id}"
251
251
  type: SessionType;
252
- targetId: number; // group_id 或 user_id
252
+ targetId: string; // group_id 或 user_id
253
253
  createdAt: number;
254
254
  updatedAt: number;
255
255
  compressedContext: string | null;
@@ -263,11 +263,11 @@ export interface ChatMessage {
263
263
  sessionId: string;
264
264
  role: "user" | "assistant" | "system";
265
265
  content: string; // 存储时统一为字符串
266
- userId?: number;
266
+ userId?: string;
267
267
  userName?: string;
268
268
  userRole?: string; // "owner" | "admin" | "member"
269
269
  userTitle?: string;
270
- groupId?: number;
270
+ groupId?: string;
271
271
  groupName?: string;
272
272
  timestamp: number;
273
273
  messageId?: string; // 消息 id
@@ -278,7 +278,7 @@ export interface ChatMessage {
278
278
  */
279
279
  export interface TargetMessage {
280
280
  userName: string;
281
- userId: number;
281
+ userId: string;
282
282
  userRole: string;
283
283
  userTitle?: string;
284
284
  content: string;
@@ -303,8 +303,8 @@ export interface ToolContext {
303
303
  ctx: MiokuContext;
304
304
  event: any;
305
305
  sessionId: string;
306
- groupId?: number;
307
- userId: number;
306
+ groupId?: string;
307
+ userId: string;
308
308
  triggerSkillRole: SkillPermissionRole;
309
309
  config: ChatConfig;
310
310
  aiService: AIService;
@@ -364,7 +364,7 @@ export interface TopicRecord {
364
364
  export interface ExpressionRecord {
365
365
  id?: number;
366
366
  sessionId: string;
367
- userId: number;
367
+ userId: string;
368
368
  userName: string;
369
369
  situation: string; // 使用场景
370
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): number[] {
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) => Math.floor(Number(item)))
42
- .filter((id) => Number.isFinite(id) && id > 0),
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
- ): number[] {
49
+ ): string[] {
50
50
  const current = config.mediaAnalysisBlacklistUsers;
51
51
  const legacy = config.imageAnalysisBlacklistUsers;
52
52
  const source =
@@ -70,10 +70,9 @@ export function mergeGroupOverrides(
70
70
  /**
71
71
  * 从 sessionId(如 "group:123")提取群号,个人会话返回 undefined
72
72
  */
73
- export function extractGroupIdFromSession(sessionId: string): number | undefined {
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
- const parsed = parseInt(raw, 10);
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: number, cfg: ChatConfig): boolean {
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: number,
193
+ groupId: string,
194
194
  ctx: MiokuContext,
195
- selfId: number,
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: number;
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: number;
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: Number(msg.user_id || 0),
366
- userName: msg.nickname || String(msg.user_id || "unknown"),
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: number,
504
+ groupId: string,
505
505
  ctx: MiokuContext,
506
506
  count: number = 100,
507
- selfId: number,
507
+ selfId: string,
508
508
  db?: {
509
- getBotMessages(groupId: number, limit: number): ChatMessage[];
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: number,
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: number;
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: number;
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 ?? 0,
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 ?? 0,
567
- userName: msg.userName || String(msg.userId || "unknown"),
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: number | undefined,
655
+ groupId: string | undefined,
656
656
  ): ChatMessage {
657
- const userId: number = e.user_id || e.sender?.user_id;
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): number[] {
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) => Math.floor(Number(item)))
679
- .filter((id) => Number.isFinite(id) && id > 0),
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: number[];
118
- pokeUsers: number[];
117
+ atUsers: string[];
118
+ pokeUsers: string[];
119
119
  quoteId?: string;
120
120
  audioText?: string;
121
121
  } {
122
- const atUsers: number[] = [];
123
- const pokeUsers: number[] = [];
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
- const atPatterns = [/\[at:(\d+)\]/g];
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 = parseInt(match[1], 10);
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:(\d+)\]/g];
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 = parseInt(match[1], 10);
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:\d+\]/g, "")
173
+ .replace(/\[at:[^\]\s]+\]/g, "")
173
174
  .replace(/\[poke:\d+\]/g, "")
174
175
  .replace(/\[reply:[^\]\n]+\]/g, "")
175
176
  .replace(/\[audio:[^\]]+\]/gi, "")