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.
@@ -1,4 +1,4 @@
1
- import { logger } from "mioki";
1
+ import {logger} from "mioku";
2
2
  import type { AITool } from "mioku";
3
3
  import { TOOL_RESULT_FOLLOWUP_KEY } from "mioku";
4
4
  import type { ToolContext } from "../../types";
@@ -73,9 +73,10 @@ export function createInfoTools(toolCtx: ToolContext): AITool[] {
73
73
  },
74
74
  handler: async (args) => {
75
75
  try {
76
- const info = await toolCtx.ctx
77
- .pickBot(toolCtx.event.self_id)
78
- .getGroupMemberInfo(toolCtx.groupId!, args.user_id);
76
+ const bot = toolCtx.event?.bot;
77
+ if (!bot) return { error: `Failed to get member info: bot not found` };
78
+ const info = await bot.getMemberInfo(toolCtx.groupId!, args.user_id);
79
+ if (!info) return { error: `Failed to get member info: member not found` };
79
80
  return {
80
81
  nickname: info.nickname,
81
82
  card: info.card,
@@ -107,9 +108,9 @@ export function createInfoTools(toolCtx: ToolContext): AITool[] {
107
108
  },
108
109
  handler: async (args) => {
109
110
  try {
110
- const list = await toolCtx.ctx
111
- .pickBot(toolCtx.event.self_id)
112
- .getGroupMemberList(toolCtx.groupId!);
111
+ const bot = toolCtx.event?.bot;
112
+ if (!bot) return { error: `Failed to get member list: bot not found` };
113
+ const list = await bot.getGroupMembers(toolCtx.groupId!);
113
114
  const members = (list as any[]).map((m) => ({
114
115
  user_id: m.user_id,
115
116
  nickname: m.card || m.nickname,
package/core/tools/web.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { logger } from "mioki";
1
+ import { HistoryMessage, logger } from "mioku";
2
2
  import type { AITool } from "mioku";
3
3
  import type { ChatMessage, ToolContext } from "../../types";
4
4
  import { searchWebWithSearxng } from "../web/searxng";
@@ -47,7 +47,8 @@ function extractGroupHistoryText(raw: any): string {
47
47
  continue;
48
48
  }
49
49
  if (seg?.type === "at") {
50
- const target = seg?.qq || seg?.data?.qq || seg?.data?.id || seg?.data?.user_id;
50
+ const target =
51
+ seg?.qq || seg?.data?.qq || seg?.data?.id || seg?.data?.user_id;
51
52
  if (target === "all" || target === "everyone") parts.push("@全体成员");
52
53
  else if (target) parts.push(`@${target}`);
53
54
  continue;
@@ -68,15 +69,18 @@ async function fetchGroupHistoryByMessageIdPaging(
68
69
  ): Promise<ChatMessage[]> {
69
70
  if (!toolCtx.groupId || limit <= 0) return [];
70
71
 
71
- const selfId = Number(toolCtx.event?.self_id || 0);
72
- if (!selfId) return [];
73
-
74
- const bot = toolCtx.ctx.pickBot(selfId);
72
+ const bot = toolCtx.event?.bot;
75
73
  if (!bot) return [];
76
74
 
75
+ const otherBotIds = new Set<string>(
76
+ toolCtx.ctx.bots
77
+ .map((b) => String(b.bot_id))
78
+ .filter((id) => id !== String(bot.bot_id)),
79
+ );
80
+
77
81
  const collected: ChatMessage[] = [];
78
82
  const seenMessageIds = new Set<string>();
79
- let cursorMessageId = 0;
83
+ let cursorMessageId = "";
80
84
  const maxPages = Math.max(1, Math.ceil(limit / 200) + 5);
81
85
  let page = 0;
82
86
 
@@ -84,79 +88,84 @@ async function fetchGroupHistoryByMessageIdPaging(
84
88
  const remaining = limit - collected.length;
85
89
  const pageSize = Math.min(200, remaining);
86
90
 
87
- let response: any;
91
+ let history: HistoryMessage[];
88
92
  try {
89
- response = await (bot as any).api("get_group_msg_history", {
90
- group_id: String(toolCtx.groupId),
91
- message_seq: String(cursorMessageId),
92
- count: pageSize,
93
- reverse_order: false,
94
- disable_get_url: true,
95
- parse_mult_msg: false,
96
- quick_reply: false,
97
- });
93
+ history = await bot.getHistory(
94
+ { type: "group", group_id: toolCtx.groupId },
95
+ cursorMessageId || undefined,
96
+ pageSize,
97
+ );
98
98
  } catch (err) {
99
99
  const errText = String(err);
100
100
  if (
101
- cursorMessageId > 0 &&
102
- (errText.includes("不存在") || errText.toLowerCase().includes("not exist"))
101
+ cursorMessageId !== "" &&
102
+ (errText.includes("不存在") ||
103
+ errText.toLowerCase().includes("not exist"))
103
104
  ) {
104
- logger.info(`[recall_memory] get_group_msg_history stop at cursor ${cursorMessageId}: ${errText}`);
105
+ logger.info(
106
+ `[recall_memory] get_group_msg_history stop at cursor ${cursorMessageId}: ${errText}`,
107
+ );
105
108
  } else {
106
- logger.warn(`[recall_memory] get_group_msg_history failed at cursor ${cursorMessageId}: ${errText}`);
109
+ logger.warn(
110
+ `[recall_memory] get_group_msg_history failed at cursor ${cursorMessageId}: ${errText}`,
111
+ );
107
112
  }
108
113
  break;
109
114
  }
110
115
 
111
- const rawMessages = response?.messages || response?.data?.messages || [];
112
- if (!Array.isArray(rawMessages) || rawMessages.length === 0) break;
116
+ if (!Array.isArray(history) || history.length === 0) break;
113
117
 
114
- let oldestMessageId: number | null = null;
118
+ let oldestMessageId: string | null = null;
115
119
  let newAdded = 0;
116
120
 
117
- for (const raw of rawMessages) {
118
- const messageId = Number(raw?.message_id || raw?.message_seq || 0);
121
+ for (const raw of history) {
122
+ const rawUserId = String(raw.user_id || "");
123
+ if (rawUserId !== "" && otherBotIds.has(rawUserId)) continue;
124
+
125
+ const messageId = String(raw.message_id || "");
119
126
  const key =
120
- messageId > 0
127
+ messageId !== ""
121
128
  ? `mid:${messageId}`
122
- : `${String(raw?.user_id || "unknown")}:${String(raw?.time || "0")}:${String(raw?.raw_message || "")}`;
129
+ : `${String(raw.user_id || "unknown")}:${String(raw.time || "0")}:${extractGroupHistoryText(raw)}`;
123
130
  if (seenMessageIds.has(key)) continue;
124
131
  seenMessageIds.add(key);
125
132
 
126
133
  const content = extractGroupHistoryText(raw);
127
134
  if (!content.trim()) continue;
128
135
 
129
- const ts = typeof raw?.time === "number" ? raw.time * 1000 : Date.now();
136
+ const ts = raw.time ?? Date.now();
130
137
  collected.push({
131
138
  sessionId: toolCtx.sessionId,
132
- role: String(raw?.user_id) === String(selfId) ? "assistant" : "user",
139
+ role: String(raw.user_id) === String(bot.bot_id) ? "assistant" : "user",
133
140
  content,
134
- userId: typeof raw?.user_id === "number" ? raw.user_id : Number(raw?.user_id),
135
- userName: raw?.sender?.card || raw?.sender?.nickname || String(raw?.user_id || "unknown"),
136
- userRole: raw?.sender?.role || "member",
141
+ userId: Number(raw.user_id || 0),
142
+ userName: raw.nickname || String(raw.user_id || "unknown"),
143
+ userRole: "member",
137
144
  groupId: toolCtx.groupId,
138
145
  timestamp: ts,
139
- messageId: messageId > 0 ? messageId : undefined,
146
+ messageId: messageId !== "" ? messageId : undefined,
140
147
  });
141
148
  newAdded += 1;
142
149
 
143
- if (messageId > 0 && (oldestMessageId === null || messageId < oldestMessageId)) {
150
+ if (
151
+ messageId !== "" &&
152
+ (oldestMessageId === null || messageId < oldestMessageId)
153
+ ) {
144
154
  oldestMessageId = messageId;
145
155
  }
146
156
  }
147
157
 
148
- if (newAdded === 0 || oldestMessageId === null || oldestMessageId <= 1) break;
158
+ if (newAdded === 0 || oldestMessageId === null || oldestMessageId === "")
159
+ break;
149
160
 
150
- // NapCat message_seq expects an existing id; don't subtract 1 (IDs may be non-contiguous).
151
- const nextCursor = oldestMessageId;
152
- if (nextCursor === cursorMessageId) break;
153
- cursorMessageId = nextCursor;
161
+ if (oldestMessageId === cursorMessageId) break;
162
+ cursorMessageId = oldestMessageId;
154
163
  page += 1;
155
164
  }
156
165
 
157
166
  collected.sort((a, b) => {
158
167
  if (a.timestamp !== b.timestamp) return a.timestamp - b.timestamp;
159
- return (a.messageId || 0) - (b.messageId || 0);
168
+ return String(a.messageId ?? "").localeCompare(String(b.messageId ?? ""));
160
169
  });
161
170
 
162
171
  return collected.length > limit ? collected.slice(-limit) : collected;
@@ -182,7 +191,8 @@ export function createWebSearchTool(toolCtx: ToolContext): AITool {
182
191
  },
183
192
  limit: {
184
193
  type: "number",
185
- description: "Max number of results to return. Will be clamped by config maxLimit.",
194
+ description:
195
+ "Max number of results to return. Will be clamped by config maxLimit.",
186
196
  },
187
197
  time_range: {
188
198
  type: "string",
@@ -192,17 +202,20 @@ export function createWebSearchTool(toolCtx: ToolContext): AITool {
192
202
  categories: {
193
203
  type: "array",
194
204
  items: { type: "string" },
195
- description: 'Optional categories, e.g. ["general"], ["news"], ["science"]',
205
+ description:
206
+ 'Optional categories, e.g. ["general"], ["news"], ["science"]',
196
207
  },
197
208
  engines: {
198
209
  type: "array",
199
210
  items: { type: "string" },
200
- description: 'Optional engines, e.g. ["google"], ["bing"], ["duckduckgo"]',
211
+ description:
212
+ 'Optional engines, e.g. ["google"], ["bing"], ["duckduckgo"]',
201
213
  },
202
214
  },
203
215
  required: [],
204
216
  },
205
- handler: async (args) => searchWebWithSearxng(toolCtx.config.searxng, args || {}),
217
+ handler: async (args) =>
218
+ searchWebWithSearxng(toolCtx.config.searxng, args || {}),
206
219
  };
207
220
  }
208
221
 
@@ -217,7 +230,10 @@ export function createWebReadPageTool(toolCtx: ToolContext): AITool {
217
230
  parameters: {
218
231
  type: "object",
219
232
  properties: {
220
- url: { type: "string", description: "The http/https URL of the webpage to read" },
233
+ url: {
234
+ type: "string",
235
+ description: "The http/https URL of the webpage to read",
236
+ },
221
237
  render_js: {
222
238
  type: "boolean",
223
239
  description:
@@ -234,7 +250,8 @@ export function createWebReadPageTool(toolCtx: ToolContext): AITool {
234
250
  handler: async (args) => {
235
251
  try {
236
252
  const ai = toolCtx.config.webReader.useWorkingModel
237
- ? (toolCtx.aiService.getInstanceByRole?.("working") ?? toolCtx.aiService.getDefault())
253
+ ? (toolCtx.aiService.getInstanceByRole?.("working") ??
254
+ toolCtx.aiService.getDefault())
238
255
  : undefined;
239
256
  if (toolCtx.config.webReader.useWorkingModel && !ai) {
240
257
  return { success: false, error: "AI instance not available" };
@@ -275,19 +292,41 @@ export function createRecallMemoryTool(toolCtx: ToolContext): AITool {
275
292
  const question = String(args?.question || "").trim();
276
293
  if (!question) return { success: false, error: "question is required" };
277
294
 
278
- const ai = toolCtx.aiService.getInstanceByRole?.("working") ?? toolCtx.aiService.getDefault();
295
+ const ai =
296
+ toolCtx.aiService.getInstanceByRole?.("working") ??
297
+ toolCtx.aiService.getDefault();
279
298
  if (!ai) return { success: false, error: "AI instance not available" };
280
299
 
281
- const groupHistoryLimit = resolveGroupRecallLimit(toolCtx.config.memory?.groupHistoryLimit);
282
- const userHistoryLimit = resolveUserHistoryLimit(toolCtx.config.memory?.userHistoryLimit);
283
- const groupHistoryMessages = await fetchGroupHistoryByMessageIdPaging(toolCtx, groupHistoryLimit);
284
- const targetUserIds = extractTargetUserIdsFromQuestion(question, toolCtx.userId);
285
- const userHistories: MemoryUserHistoryChunk[] = targetUserIds.map((userId) => ({
286
- userId,
287
- messages: toolCtx.db.getMessagesByUser(userId, toolCtx.sessionId, userHistoryLimit),
288
- }));
300
+ const groupHistoryLimit = resolveGroupRecallLimit(
301
+ toolCtx.config.memory?.groupHistoryLimit,
302
+ );
303
+ const userHistoryLimit = resolveUserHistoryLimit(
304
+ toolCtx.config.memory?.userHistoryLimit,
305
+ );
306
+ const groupHistoryMessages = await fetchGroupHistoryByMessageIdPaging(
307
+ toolCtx,
308
+ groupHistoryLimit,
309
+ );
310
+ const targetUserIds = extractTargetUserIdsFromQuestion(
311
+ question,
312
+ toolCtx.userId,
313
+ );
314
+ const userHistories: MemoryUserHistoryChunk[] = targetUserIds.map(
315
+ (userId) => ({
316
+ userId,
317
+ messages: toolCtx.db.getMessagesByUser(
318
+ userId,
319
+ toolCtx.sessionId,
320
+ userHistoryLimit,
321
+ ),
322
+ }),
323
+ );
289
324
 
290
- const retriever = new MemoryRetrieval(ai, () => toolCtx.config, toolCtx.db);
325
+ const retriever = new MemoryRetrieval(
326
+ ai,
327
+ () => toolCtx.config,
328
+ toolCtx.db,
329
+ );
291
330
  const answer = await retriever.retrieveByQuestion({
292
331
  sessionId: toolCtx.sessionId,
293
332
  question,
package/core/tools.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { logger } from "mioki";
1
+ import { logger } from "mioku";
2
2
  import type { AITool } from "mioku";
3
3
  import type { ToolContext } from "../types";
4
4
  import type { SkillSessionManager } from "../manage/skill-session";
@@ -1,4 +1,4 @@
1
- import { logger } from "mioki";
1
+ import { logger } from "mioku";
2
2
  import type { SearxngConfig } from "../../types";
3
3
 
4
4
  type TimeRange = "day" | "month" | "year";
@@ -1,4 +1,4 @@
1
- import { logger } from "mioki";
1
+ import { logger } from "mioku";
2
2
  import puppeteer from "puppeteer";
3
3
  import type { AIInstance } from "mioku";
4
4
  import type { WebReaderConfig } from "../../types";
@@ -266,7 +266,7 @@ function pickBestParagraphs(text: string, maxChars: number): string {
266
266
 
267
267
  const scored = paragraphs.map((paragraph) => {
268
268
  const value = paragraph.text;
269
- const punctuationCount = (value.match(/[,。!?;:,.!?;:]/g) || [])
269
+ const punctuationCount = (value.match(/[,。!?;:,.!?);:]/g) || [])
270
270
  .length;
271
271
  const sentenceCount = (value.match(/[。!?.!?]/g) || []).length;
272
272
  const linkLikeCount = (value.match(/https?:\/\/|www\./g) || []).length;
package/db.ts CHANGED
@@ -24,7 +24,7 @@ function toChatMessage(row: any): ChatMessage {
24
24
  groupId: row.group_id,
25
25
  groupName: row.group_name,
26
26
  timestamp: row.timestamp,
27
- messageId: row.message_id,
27
+ messageId: row.message_id != null ? String(row.message_id) : undefined,
28
28
  };
29
29
  }
30
30
 
@@ -174,7 +174,7 @@ export async function initDatabase(): Promise<ChatDatabase> {
174
174
  group_id INTEGER,
175
175
  group_name TEXT,
176
176
  timestamp INTEGER NOT NULL,
177
- message_id INTEGER
177
+ message_id TEXT
178
178
  );
179
179
 
180
180
  CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, timestamp);
@@ -452,7 +452,10 @@ export async function initDatabase(): Promise<ChatDatabase> {
452
452
  $groupId: msg.groupId ?? null,
453
453
  $groupName: msg.groupName ?? null,
454
454
  $timestamp: msg.timestamp,
455
- $messageId: msg.messageId ?? null,
455
+ $messageId:
456
+ msg.messageId == null || msg.messageId === ""
457
+ ? null
458
+ : String(msg.messageId),
456
459
  });
457
460
  },
458
461
 
@@ -1,4 +1,4 @@
1
- import type { MiokiContext } from "mioki";
1
+ import type { MiokuContext } from "mioku";
2
2
  import type { AIService } from "mioku";
3
3
  import type { ChatPluginContext } from "../context";
4
4
  import type { ChatConfig, TargetMessage } from "../types";
@@ -10,7 +10,7 @@ export async function handleIdleCheckDebug(
10
10
  e: any,
11
11
  cfg: ChatConfig,
12
12
  ): Promise<void> {
13
- const ctx: MiokiContext = pluginCtx.ctx;
13
+ const ctx: MiokuContext = pluginCtx.ctx;
14
14
  const isOwner = ctx.isOwner?.(e) ?? false;
15
15
  if (!isOwner) {
16
16
  await e.reply("只有主人才能使用这个指令~");
@@ -32,7 +32,7 @@ export async function handleIdleCheckDebug(
32
32
  async () => {
33
33
  const now = Date.now();
34
34
  const botNickname =
35
- cfg.nicknames[0] || ctx.pickBot(e.self_id).nickname || "Bot";
35
+ cfg.nicknames[0] || e.bot?.nickname || "Bot";
36
36
  ctx.logger.info(`[Debug] Manual idle check: group ${targetGroupId}`);
37
37
 
38
38
  const { history } = await pluginCtx.getGroupHistoryMessages(
@@ -64,7 +64,7 @@ export async function handleIdleCheckDebug(
64
64
  userId: 0,
65
65
  userRole: "member",
66
66
  content: "[No one in the group is talking? I'll answer!]",
67
- messageId: 0,
67
+ messageId: "",
68
68
  timestamp: now,
69
69
  };
70
70
  const botRole = await getBotRole(targetGroupId, ctx, e.self_id);
@@ -1,8 +1,5 @@
1
1
  import type { ChatPluginContext, ChatHandlerState } from "../context";
2
- import type {
3
- GroupMessageEvent,
4
- PrivateMessageEvent,
5
- } from "napcat-sdk";
2
+ import type { MessageEvent } from "mioku";
6
3
  import {
7
4
  isGroupAllowed,
8
5
  shouldTrigger,
@@ -37,17 +34,19 @@ export function createMessageHandler(
37
34
  const { ctx } = pluginCtx;
38
35
  const { getConfig, matchMessageCommands, runtimeState } = state;
39
36
 
40
- return async (e: GroupMessageEvent | PrivateMessageEvent) => {
37
+ return async (e: MessageEvent) => {
41
38
  const isGroup = e.message_type === "group";
42
- const groupId: number | undefined = isGroup ? e.group_id : undefined;
39
+ const groupId: number | undefined = isGroup
40
+ ? Number(e.group_id)
41
+ : undefined;
43
42
  const cfg = await getConfig(groupId);
44
43
  if (!cfg.model && !cfg.apiKey) return;
45
44
  if (!e?.message || !Array.isArray(e.message)) return;
46
45
 
47
46
  const text = ctx.text(e) || "";
48
- const userId: number = e.user_id || e.sender?.user_id;
47
+ const userId: number = Number(e.user_id || e.sender?.user_id || 0);
49
48
 
50
- if (userId === e.self_id) return;
49
+ if (userId === Number(e.self_id || 0)) return;
51
50
 
52
51
  if (matchMessageCommands && matchMessageCommands(text).length > 0) return;
53
52
 
@@ -78,9 +77,15 @@ export function createMessageHandler(
78
77
  if (groupId && !isGroupAllowed(groupId, cfg)) return;
79
78
 
80
79
  // 媒体分析
81
- if (isGroup && groupId && e.message && !isMediaAnalysisBlocked(cfg, userId)) {
82
- const visionAI = pluginCtx.visionAIInstance ?? pluginCtx.aiService.getDefault();
83
- const bot = ctx.pickBot(e.self_id) as any;
80
+ if (
81
+ isGroup &&
82
+ groupId &&
83
+ e.message &&
84
+ !isMediaAnalysisBlocked(cfg, userId)
85
+ ) {
86
+ const visionAI =
87
+ pluginCtx.visionAIInstance ?? pluginCtx.aiService.getDefault();
88
+ const bot = e.bot;
84
89
  const mediaOptions = visionAI
85
90
  ? buildHistoryMediaProcessingOptions(
86
91
  visionAI,
@@ -109,15 +114,21 @@ export function createMessageHandler(
109
114
  if (seg.type === "image") {
110
115
  const imageUrl = getSegmentUrl(seg);
111
116
  if (imageUrl) {
112
- processImage(visionAI, imageUrl, cfg.multimodalWorkingModel, pluginCtx.db, {
113
- runAIRequest: (request) =>
114
- pluginCtx.runWithRateLimitGuard(request, {
115
- userId,
116
- groupId,
117
- label: "image-analysis",
118
- skipRetryOnRateLimit: true,
119
- }),
120
- }).catch((err) =>
117
+ processImage(
118
+ visionAI,
119
+ imageUrl,
120
+ cfg.multimodalWorkingModel,
121
+ pluginCtx.db,
122
+ {
123
+ runAIRequest: (request) =>
124
+ pluginCtx.runWithRateLimitGuard(request, {
125
+ userId,
126
+ groupId,
127
+ label: "image-analysis",
128
+ skipRetryOnRateLimit: true,
129
+ }),
130
+ },
131
+ ).catch((err) =>
121
132
  ctx.logger.error(`[image-analyzer] Failed: ${err}`),
122
133
  );
123
134
  }
@@ -162,7 +173,9 @@ export function createMessageHandler(
162
173
  }
163
174
  }
164
175
  }
165
- if (e.sub_type === "notice") {
176
+ if (
177
+ (e.raw as { sub_type?: string } | undefined)?.sub_type === "notice"
178
+ ) {
166
179
  summarizeGroupNotice(e, mediaOptions)
167
180
  .then((noticeMessage) => {
168
181
  if (!noticeMessage) return;
@@ -192,8 +205,7 @@ export function createMessageHandler(
192
205
  const learnSessionId = `group:${groupId}`;
193
206
  const learnMsg = buildChatMessageFromEvent(e, text, true, groupId);
194
207
  const hasText = !!learnMsg.content?.trim();
195
- const hasMultimodal =
196
- !!cfg.isMultimodal && !!cfg.multimodalWorkingModel;
208
+ const hasMultimodal = !!cfg.isMultimodal && !!cfg.multimodalWorkingModel;
197
209
  if (hasText || hasMultimodal) {
198
210
  pluginCtx
199
211
  .recordGroupMessageForLearning(learnMsg, learnSessionId)
@@ -235,7 +247,11 @@ export function createMessageHandler(
235
247
  if (pluginCtx.queueProcessor.isInDynamicDelay(groupSessionId)) {
236
248
  if (atBot && !runtimeState.isRateLimitBlocked()) {
237
249
  pluginCtx.rateLimiter.recordInteraction(groupId, userId);
238
- pluginCtx.queueProcessor.collectDynamicDelayMessage(groupSessionId, e, text);
250
+ pluginCtx.queueProcessor.collectDynamicDelayMessage(
251
+ groupSessionId,
252
+ e,
253
+ text,
254
+ );
239
255
  }
240
256
  return;
241
257
  }
@@ -259,7 +275,7 @@ export function createMessageHandler(
259
275
  groupSessionId,
260
276
  groupId,
261
277
  delayInfo.delayMs,
262
- e.self_id,
278
+ Number(e.self_id || 0),
263
279
  );
264
280
  return;
265
281
  }
@@ -278,11 +294,10 @@ export function createMessageHandler(
278
294
  ctx,
279
295
  cfg.historyCount,
280
296
  pluginCtx.db,
281
- e.self_id,
297
+ Number(e.self_id || 0),
282
298
  pluginCtx.buildHistoryMediaOptions(pluginCtx.aiInstance, cfg),
283
299
  );
284
- const botNickname =
285
- cfg.nicknames[0] || ctx.pickBot(e.self_id).nickname || "Bot";
300
+ const botNickname = cfg.nicknames[0] || e.bot?.nickname || "Bot";
286
301
  const planResult = await pluginCtx.humanize.actionPlanner.plan(
287
302
  groupSessionId,
288
303
  botNickname,
@@ -304,7 +319,7 @@ export function createMessageHandler(
304
319
  pluginCtx.rateLimiter.recordInteraction(groupId, userId);
305
320
  pluginCtx.queueProcessor.scheduleQueuedMessages(
306
321
  groupSessionId,
307
- e.self_id,
322
+ Number(e.self_id || 0),
308
323
  );
309
324
  }
310
325
  return;
package/handlers/poke.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ChatPluginContext, ChatHandlerState } from "../context";
2
2
  import type { TargetMessage } from "../types";
3
- import type { GroupPokeNoticeEvent } from "napcat-sdk";
3
+ import type { Bot, NoticeEvent } from "mioku";
4
+
4
5
  import { getBotRole, isGroupAllowed } from "../utils";
5
6
  import { buildStructuredUserInputFromTarget } from "../manage/group-structured-history";
6
7
  import { finalizeChatTurn } from "../core/chat-turn";
@@ -13,9 +14,13 @@ export function createPokeHandler(
13
14
  const { ctx } = pluginCtx;
14
15
  const { getConfig, runtimeState, pokeCooldowns } = state;
15
16
 
16
- return async (e: GroupPokeNoticeEvent) => {
17
- if (e.target_id !== e.self_id) return;
18
- const groupId = e.group_id;
17
+ return async (e: NoticeEvent) => {
18
+ const selfId = Number(e.self_id || 0);
19
+ const targetId = Number(
20
+ (e.raw as { target_id?: string } | undefined)?.target_id ?? 0,
21
+ );
22
+ if (targetId !== selfId) return;
23
+ const groupId = Number(e.group_id || 0);
19
24
  const cfg = groupId ? await getConfig(groupId) : await getConfig();
20
25
  if (!cfg.model && !cfg.apiKey) return;
21
26
  if (!groupId || !isGroupAllowed(groupId, cfg)) return;
@@ -37,19 +42,20 @@ export function createPokeHandler(
37
42
  "group",
38
43
  groupId,
39
44
  );
40
- const userId = e.user_id;
41
- const botRole = await getBotRole(groupId, ctx, e.self_id);
45
+ const userId = Number(e.user_id || 0);
46
+ const botRole = await getBotRole(groupId, ctx, selfId);
42
47
  const botNickname =
43
- cfg.nicknames[0] || ctx.pickBot(e.self_id).nickname || "Bot";
48
+ cfg.nicknames[0] || e.bot?.nickname || "Bot";
44
49
 
45
50
  let senderName = String(userId);
46
51
  try {
47
- const memberInfo = await ctx
48
- .pickBot(e.self_id)
49
- .getGroupMemberInfo(groupId, userId);
52
+ const bot: Bot | undefined = e.bot;
53
+ const memberInfo = bot
54
+ ? await bot.getMemberInfo(groupId, userId)
55
+ : undefined;
50
56
  senderName =
51
- (memberInfo as any).card ||
52
- (memberInfo as any).nickname ||
57
+ memberInfo?.card ||
58
+ memberInfo?.nickname ||
53
59
  String(userId);
54
60
  } catch {}
55
61
 
@@ -67,11 +73,11 @@ export function createPokeHandler(
67
73
  ctx,
68
74
  cfg.historyCount,
69
75
  pluginCtx.db,
70
- e.self_id,
76
+ selfId,
71
77
  pluginCtx.buildHistoryMediaOptions(pluginCtx.aiInstance, cfg),
72
78
  );
73
79
  const { groupName, memberCount } =
74
- await pluginCtx.getGroupInfoData(ctx, groupId, e.self_id);
80
+ await pluginCtx.getGroupInfoData(ctx, groupId, selfId);
75
81
 
76
82
  const toolCtx = pluginCtx.buildToolContext({
77
83
  ctx,
@@ -85,7 +91,7 @@ export function createPokeHandler(
85
91
  botRole,
86
92
  humanize: pluginCtx.humanize,
87
93
  targetMessage,
88
- selfId: e.self_id,
94
+ selfId,
89
95
  audioService: pluginCtx.audioService,
90
96
  });
91
97
 
@@ -131,7 +137,7 @@ export function createPokeHandler(
131
137
  groupId,
132
138
  groupSessionId,
133
139
  userId,
134
- selfId: e.self_id,
140
+ selfId,
135
141
  toolCtx,
136
142
  send: true,
137
143
  isLive: true,
@@ -2,7 +2,7 @@ import { existsSync, readdirSync } from "fs";
2
2
  import * as fs from "fs/promises";
3
3
  import * as path from "path";
4
4
  import { getPluginDataDir, type AIInstance } from "mioku";
5
- import { logger } from "mioki";
5
+ import { logger } from "mioku";
6
6
  import type { ChatConfig, ChatMessage, TargetMessage } from "../types";
7
7
  import { extractGroupIdFromSession } from "../utils/group-config";
8
8
  import { extractJsonObject } from "../utils/json";
@@ -1,5 +1,5 @@
1
1
  import type { AIInstance } from "mioku";
2
- import { logger } from "mioki";
2
+ import { logger } from "mioku";
3
3
  import { extractJsonObject } from "../utils/json";
4
4
  import type { ChatConfig, ChatMessage, TargetMessage } from "../types";
5
5
  import { extractGroupIdFromSession } from "../utils/group-config";
@@ -1,5 +1,5 @@
1
1
  import type { AIInstance } from "mioku";
2
- import { logger } from "mioki";
2
+ import { logger } from "mioku";
3
3
  import { extractJsonObject } from "../utils/json";
4
4
  import type { ChatDatabase } from "../db";
5
5
  import type { ChatMessage } from "../types";
@@ -1,5 +1,5 @@
1
1
  import type { AIInstance } from "mioku";
2
- import { logger } from "mioki";
2
+ import { logger } from "mioku";
3
3
  import type { ChatDatabase } from "../db";
4
4
  import type { ChatMessage } from "../types";
5
5
  import { extractGroupIdFromSession } from "../utils/group-config";
@@ -1,5 +1,5 @@
1
1
  import type { AIInstance } from "mioku";
2
- import { logger } from "mioki";
2
+ import { logger } from "mioku";
3
3
  import type {
4
4
  ChatMessage,
5
5
  PlannerAction,