@aalis/plugin-tool-onebot 0.9.3 → 0.11.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/dist/index.js CHANGED
@@ -1,14 +1,11 @@
1
- import { getPlatformAdapters, getPlatformNames } from '@aalis/api-platform';
2
- import { createStorageGateway } from '@aalis/api-storage';
3
- import { toolsWithGroups, useToolService } from '@aalis/api-tools';
1
+ import { media } from '@aalis/api-media';
2
+ import { getPlatformAdapters, getPlatformNames, platform as platformService, } from '@aalis/api-platform';
3
+ import { sessionHistory } from '@aalis/api-session-history';
4
+ import { createStorageGateway, storage } from '@aalis/api-storage';
5
+ import { tools, withToolGroups } from '@aalis/api-tools';
6
+ import { config, definePlugin, events, logger, optional } from '@aalis/core';
4
7
  // ===== 插件元数据 =====
5
- export const name = '@aalis/plugin-tool-onebot';
6
- export const displayName = 'OneBot 工具';
7
- export const subsystem = 'tools';
8
- export const inject = {
9
- optional: ['platform', 'session-history'],
10
- };
11
- export const configSchema = {
8
+ const configSchema = {
12
9
  groupManagement: {
13
10
  label: '群管理工具',
14
11
  fields: {
@@ -43,9 +40,9 @@ export const configSchema = {
43
40
  fields: {
44
41
  enabled: {
45
42
  type: 'boolean',
46
- label: '启用 OneBot 会话历史读取',
43
+ label: '启用 OneBot 专属历史工具',
47
44
  default: true,
48
- description: '允许按群号/QQ 号读取对应 OneBot 会话的近期历史',
45
+ description: '注册 onebot_resolve_session_id / onebot_get_session_history(按群号/QQ 号读取近期历史)。只管这两个工具:下面的访问规则始终生效,同样约束通用的 session_get_history。',
49
46
  },
50
47
  maxLimit: {
51
48
  type: 'number',
@@ -86,6 +83,17 @@ export const configSchema = {
86
83
  },
87
84
  },
88
85
  };
86
+ // ===== 能力声明 =====
87
+ const uses = {
88
+ tools: optional(tools),
89
+ platform: optional(platformService),
90
+ storage: optional(storage),
91
+ media: optional(media),
92
+ sessionHistory: optional(sessionHistory),
93
+ events,
94
+ logger,
95
+ config,
96
+ };
89
97
  // ===== 辅助函数 =====
90
98
  /** 从 sessionId 解析 OneBot 连接信息 */
91
99
  function parseOneBotSession(sessionId) {
@@ -98,16 +106,16 @@ function parseOneBotSession(sessionId) {
98
106
  targetId: parts.slice(3).join(':'),
99
107
  };
100
108
  }
101
- /** 从上下文中找到支持 callAction 的 OneBot 平台适配器 */
102
- function findOneBotAdapter(ctx) {
103
- return getPlatformAdapters(ctx).find(a => a.platform === 'onebot' && typeof a.callAction === 'function');
109
+ /** 在已注册的平台适配器里找出支持 callAction 的 OneBot 适配器 */
110
+ function findOneBotAdapter(platform) {
111
+ return getPlatformAdapters(platform).find(a => a.platform === 'onebot' && typeof a.callAction === 'function');
104
112
  }
105
113
  /** 从已连接的 OneBot 适配器中找出任意一个 selfId(优先 online) */
106
- function getAnyOneBotSelfId(ctx) {
107
- for (const a of getPlatformAdapters(ctx)) {
114
+ function getAnyOneBotSelfId(platform) {
115
+ for (const a of getPlatformAdapters(platform)) {
108
116
  if (a.platform !== 'onebot')
109
117
  continue;
110
- const conns = a.getConnections?.() ?? [];
118
+ const conns = a.getConnections();
111
119
  const online = conns.find(c => c.status === 'online' && c.selfId);
112
120
  if (online?.selfId)
113
121
  return online.selfId;
@@ -122,14 +130,14 @@ function getAnyOneBotSelfId(ctx) {
122
130
  * 优先级:args.self_id → 当前 callCtx 是 OneBot 会话时取它的 selfId → 任意可用 adapter 的第一个 selfId。
123
131
  * 找不到则抛错(无可用 OneBot 连接)。
124
132
  */
125
- function resolveSelfId(ctx, callCtx, args) {
133
+ function resolveSelfId(platform, callCtx, args) {
126
134
  const fromArgs = args.self_id != null && String(args.self_id).trim() ? String(args.self_id).trim() : '';
127
135
  if (fromArgs)
128
136
  return fromArgs;
129
137
  const parsed = parseOneBotSession(callCtx.sessionId);
130
138
  if (parsed?.selfId)
131
139
  return parsed.selfId;
132
- const fallback = getAnyOneBotSelfId(ctx);
140
+ const fallback = getAnyOneBotSelfId(platform);
133
141
  if (fallback)
134
142
  return fallback;
135
143
  throw new Error('未找到可用的 OneBot 连接');
@@ -138,9 +146,9 @@ function resolveSelfId(ctx, callCtx, args) {
138
146
  * 解析"目标群":args.group_id 优先;否则当前会话必须在 OneBot 群里。
139
147
  * 不再限制工具必须在群会话上下文中调用。
140
148
  */
141
- function resolveGroupTarget(ctx, callCtx, args) {
149
+ function resolveGroupTarget(platform, callCtx, args) {
142
150
  const argGroupId = args.group_id != null && String(args.group_id).trim() ? String(args.group_id).trim() : '';
143
- const selfId = resolveSelfId(ctx, callCtx, args);
151
+ const selfId = resolveSelfId(platform, callCtx, args);
144
152
  if (argGroupId)
145
153
  return { selfId, groupId: argGroupId };
146
154
  const parsed = parseOneBotSession(callCtx.sessionId);
@@ -149,20 +157,6 @@ function resolveGroupTarget(ctx, callCtx, args) {
149
157
  }
150
158
  throw new Error('未提供 group_id,且当前不在 OneBot 群会话中。请在参数中显式传入 group_id。');
151
159
  }
152
- /**
153
- * 解析"目标用户":args.user_id 优先;否则当前会话必须是 OneBot 私聊。
154
- */
155
- function _resolveUserTarget(ctx, callCtx, args) {
156
- const argUserId = args.user_id != null && String(args.user_id).trim() ? String(args.user_id).trim() : '';
157
- const selfId = resolveSelfId(ctx, callCtx, args);
158
- if (argUserId)
159
- return { selfId, userId: argUserId };
160
- const parsed = parseOneBotSession(callCtx.sessionId);
161
- if (parsed?.detailType === 'private' && parsed.targetId) {
162
- return { selfId: parsed.selfId, userId: parsed.targetId };
163
- }
164
- throw new Error('未提供 user_id,且当前不在 OneBot 私聊会话中。请在参数中显式传入 user_id。');
165
- }
166
160
  function buildOneBotSessionId(selfId, detailType, targetId) {
167
161
  return `onebot:${selfId}:${detailType}:${targetId}`;
168
162
  }
@@ -171,8 +165,8 @@ function buildOneBotSessionId(selfId, detailType, targetId) {
171
165
  * adapter 内部只用 sessionId 解析 selfId 来定位 WebSocket 连接,
172
166
  * 因此这里用合成 sessionId("onebot:<selfId>:internal:0")即可,与目标会话类型无关。
173
167
  */
174
- async function callAction(ctx, selfId, action, params) {
175
- const adapter = findOneBotAdapter(ctx);
168
+ async function callAction(platform, selfId, action, params) {
169
+ const adapter = findOneBotAdapter(platform);
176
170
  if (!adapter?.callAction)
177
171
  throw new Error('OneBot 适配器不可用或不支持 callAction');
178
172
  return adapter.callAction(buildOneBotSessionId(selfId, 'internal', '0'), action, params);
@@ -234,7 +228,7 @@ function extractCqImageRefs(content) {
234
228
  }
235
229
  const source = imageSourceFromSegment(params);
236
230
  if (source)
237
- refs.push({ source, label: '[图片]', segment: params });
231
+ refs.push({ source, segment: params });
238
232
  match = re.exec(content);
239
233
  }
240
234
  return refs;
@@ -254,7 +248,7 @@ function extractImageRefsFromContent(content) {
254
248
  const data = segment.data ?? {};
255
249
  const source = imageSourceFromSegment(data);
256
250
  if (source)
257
- refs.push({ source, label: '[图片]', segment: data });
251
+ refs.push({ source, segment: data });
258
252
  }
259
253
  return refs;
260
254
  }
@@ -287,11 +281,12 @@ function collectForwardImageRefs(data, limit) {
287
281
  .slice(0, limit)
288
282
  .flatMap(item => extractImageRefsFromContent(getForwardNodeContent(item)));
289
283
  }
290
- async function resolveForwardImageSource(ctx, storage, selfId, ref) {
284
+ async function resolveForwardImageSource(caps, storage, selfId, ref) {
291
285
  if (/^(https?:|data:)/i.test(ref.source))
292
286
  return ref.source;
287
+ const { platform, logger } = caps;
293
288
  try {
294
- const imageData = (await callAction(ctx, selfId, 'get_image', { file: ref.source }));
289
+ const imageData = (await callAction(platform, selfId, 'get_image', { file: ref.source }));
295
290
  const resolvedSource = imageData.url ?? imageData.file ?? ref.source;
296
291
  if (typeof resolvedSource === 'string') {
297
292
  if (/^(https?:|data:)/i.test(resolvedSource))
@@ -303,19 +298,20 @@ async function resolveForwardImageSource(ctx, storage, selfId, ref) {
303
298
  }
304
299
  }
305
300
  catch (err) {
306
- ctx.logger.debug(`get_image 解析转发图片失败 (${ref.source}): ${err}`);
301
+ logger.debug(`get_image 解析转发图片失败 (${ref.source}): ${err}`);
307
302
  }
308
303
  const dataUri = await localImageToDataUri(storage, ref.source);
309
304
  return dataUri ?? ref.source;
310
305
  }
311
- async function recognizeForwardImages(ctx, storage, selfId, data, limit) {
306
+ async function recognizeForwardImages(caps, storage, selfId, data, limit) {
312
307
  const refs = collectForwardImageRefs(data, limit);
313
308
  const imageDescriptions = new Map();
314
309
  if (refs.length === 0)
315
310
  return { imageDescriptions };
316
- const mediaSvc = ctx.getService('media');
317
- if (!mediaSvc?.describeImage) {
318
- ctx.logger.debug(`合并转发包含 ${refs.length} 张图片,但 media 服务不可用`);
311
+ const { logger, media } = caps;
312
+ const mediaService = media.current;
313
+ if (!mediaService) {
314
+ logger.debug(`合并转发包含 ${refs.length} 张图片,但 media 服务不可用`);
319
315
  return { imageDescriptions };
320
316
  }
321
317
  const seen = new Set();
@@ -325,20 +321,18 @@ async function recognizeForwardImages(ctx, storage, selfId, data, limit) {
325
321
  continue;
326
322
  seen.add(key);
327
323
  try {
328
- const imageSource = await resolveForwardImageSource(ctx, storage, selfId, ref);
329
- const description = await mediaSvc.describeImage(imageSource);
324
+ const imageSource = await resolveForwardImageSource(caps, storage, selfId, ref);
325
+ const description = await mediaService.describeImage(imageSource);
330
326
  if (description)
331
327
  imageDescriptions.set(key, description);
332
328
  }
333
329
  catch (err) {
334
- ctx.logger.debug(`合并转发图片识别失败 (${ref.source}): ${err}`);
330
+ logger.debug(`合并转发图片识别失败 (${ref.source}): ${err}`);
335
331
  }
336
332
  }
337
333
  return { imageDescriptions };
338
334
  }
339
335
  function formatMessageContent(content, context) {
340
- if (typeof content === 'string')
341
- return content;
342
336
  if (!Array.isArray(content))
343
337
  return content == null ? '' : JSON.stringify(content);
344
338
  return content
@@ -354,9 +348,7 @@ function formatMessageContent(content, context) {
354
348
  return data.qq === 'all' ? '@全体成员' : `@${String(data.qq ?? '')}`;
355
349
  case 'image': {
356
350
  const source = imageSourceFromSegment(data);
357
- const desc = source
358
- ? context?.imageDescriptions.get(imageRefKey({ source, label: '[图片]', segment: data }))
359
- : undefined;
351
+ const desc = source ? context?.imageDescriptions.get(imageRefKey({ source, segment: data })) : undefined;
360
352
  return desc ? `[图片: ${desc}]` : '[图片]';
361
353
  }
362
354
  case 'face':
@@ -423,9 +415,9 @@ function roleLevel(role) {
423
415
  return ROLE_LEVEL[role] ?? 0;
424
416
  }
425
417
  /** 查询群成员信息(失败返回 null) */
426
- async function getGroupMemberInfo(ctx, selfId, groupId, userId) {
418
+ async function getGroupMemberInfo(platform, selfId, groupId, userId) {
427
419
  try {
428
- return (await callAction(ctx, selfId, 'get_group_member_info', {
420
+ return (await callAction(platform, selfId, 'get_group_member_info', {
429
421
  group_id: Number(groupId),
430
422
  user_id: Number(userId),
431
423
  no_cache: true,
@@ -436,8 +428,8 @@ async function getGroupMemberInfo(ctx, selfId, groupId, userId) {
436
428
  }
437
429
  }
438
430
  /** 检查管理操作权限,返回错误消息(null 表示通过或无法验证) */
439
- async function checkAdminPermission(ctx, selfId, groupId, targetUserId, requireOwner) {
440
- const selfInfo = await getGroupMemberInfo(ctx, selfId, groupId, selfId);
431
+ async function checkAdminPermission(platform, selfId, groupId, targetUserId, requireOwner) {
432
+ const selfInfo = await getGroupMemberInfo(platform, selfId, groupId, selfId);
441
433
  if (!selfInfo)
442
434
  return null; // 无法获取自身信息,跳过权限检查
443
435
  const selfRole = String(selfInfo.role ?? 'member');
@@ -448,7 +440,7 @@ async function checkAdminPermission(ctx, selfId, groupId, targetUserId, requireO
448
440
  return `操作失败:机器人不是管理员(当前角色:${selfRole}),无法执行管理操作`;
449
441
  }
450
442
  if (targetUserId) {
451
- const targetInfo = await getGroupMemberInfo(ctx, selfId, groupId, targetUserId);
443
+ const targetInfo = await getGroupMemberInfo(platform, selfId, groupId, targetUserId);
452
444
  if (!targetInfo) {
453
445
  return `操作失败:无法获取用户 ${targetUserId} 的信息,该用户可能不在群中`;
454
446
  }
@@ -460,38 +452,20 @@ async function checkAdminPermission(ctx, selfId, groupId, targetUserId, requireO
460
452
  }
461
453
  return null;
462
454
  }
463
- export function apply(ctx, config) {
464
- const tools = useToolService(ctx);
465
- const storage = createStorageGateway(ctx);
466
- const bundle = {
467
- daily: toolsWithGroups(tools, ['onebot-daily']),
468
- group: toolsWithGroups(tools, ['onebot-group']),
469
- personal: toolsWithGroups(tools, ['onebot-personal']),
470
- };
471
- // 仅当 OneBot 平台可用时才注册工具
472
- // 使用 app:ready 事件确保平台已加载
473
- ctx.on('app:ready', () => {
474
- if (!getPlatformNames(ctx).includes('onebot')) {
475
- ctx.logger.info('未检测到 OneBot 平台,跳过 OneBot 工具注册');
476
- return;
477
- }
478
- ctx.logger.info('检测到 OneBot 平台,开始注册 OneBot 工具');
479
- // 注册工具分组:按职责语义切分,彼此不重叠
480
- tools.registerGroup({
481
- name: 'onebot-daily',
482
- label: 'OneBot 只读与日常',
483
- description: '只读查询(群信息/成员/好友列表/历史/登录信息)+ 日常低风险互动(戳一戳、好友赞)',
484
- });
485
- tools.registerGroup({
486
- name: 'onebot-group',
487
- label: 'OneBot 群务',
488
- description: '群务管理:禁言/全员禁言/踢人/撤回消息/设置群名片/改群名/设管理员/群打卡/审批加群申请 等',
489
- });
490
- tools.registerGroup({
491
- name: 'onebot-personal',
492
- label: 'OneBot 个人/私聊',
493
- description: '影响 bot 账号本身的人际关系:退群、删好友、处理好友申请、接受/拒绝入群邀请',
494
- });
455
+ export default definePlugin({
456
+ name: '@aalis/plugin-tool-onebot',
457
+ displayName: 'OneBot 工具',
458
+ subsystem: 'tools',
459
+ configSchema,
460
+ uses,
461
+ apply(caps) {
462
+ const { tools, events, logger, platform, config } = caps;
463
+ const storage = createStorageGateway(caps.storage);
464
+ const bundle = {
465
+ daily: withToolGroups(tools, ['onebot-daily']),
466
+ group: withToolGroups(tools, ['onebot-group']),
467
+ personal: withToolGroups(tools, ['onebot-personal']),
468
+ };
495
469
  const cfg = {
496
470
  groupManagement: { enabled: true, ...(config.groupManagement ?? {}) },
497
471
  groupInfo: { enabled: true, ...(config.groupInfo ?? {}) },
@@ -508,33 +482,71 @@ export function apply(ctx, config) {
508
482
  ...(config.sessionHistory ?? {}),
509
483
  },
510
484
  };
511
- if (cfg.groupManagement.enabled)
512
- registerGroupManagementTools(ctx, bundle);
513
- if (cfg.groupInfo.enabled)
514
- registerGroupInfoTools(ctx, storage, bundle);
515
- if (cfg.account.enabled)
516
- registerAccountTools(ctx, bundle);
517
- if (cfg.interaction.enabled)
518
- registerInteractionTools(ctx, bundle);
519
- if (cfg.sessionHistory.enabled) {
520
- const maxLimit = Math.max(1, Math.min(1000, Number(cfg.sessionHistory.maxLimit) || 100));
521
- const defaultLimitRaw = Math.max(1, Math.floor(Number(cfg.sessionHistory.defaultLimit) || 20));
522
- const historyCfg = {
523
- maxLimit,
524
- defaultLimit: Math.min(defaultLimitRaw, maxLimit),
525
- allowGroupReadPrivate: cfg.sessionHistory.allowGroupReadPrivate === true,
526
- allowCrossSelf: cfg.sessionHistory.allowCrossSelf === true,
527
- allowCrossGroup: cfg.sessionHistory.allowCrossGroup !== false,
528
- allowCrossPrivate: cfg.sessionHistory.allowCrossPrivate === true,
529
- };
530
- registerSessionHistoryTools(ctx, bundle, historyCfg);
531
- registerOneBotHistoryAccessChecker(ctx, historyCfg);
532
- }
533
- registerRequestTools(ctx, bundle);
534
- });
535
- }
485
+ const maxLimit = Math.max(1, Math.min(1000, Number(cfg.sessionHistory.maxLimit) || 100));
486
+ const defaultLimitRaw = Math.max(1, Math.floor(Number(cfg.sessionHistory.defaultLimit) || 20));
487
+ const historyCfg = {
488
+ maxLimit,
489
+ defaultLimit: Math.min(defaultLimitRaw, maxLimit),
490
+ allowGroupReadPrivate: cfg.sessionHistory.allowGroupReadPrivate === true,
491
+ allowCrossSelf: cfg.sessionHistory.allowCrossSelf === true,
492
+ allowCrossGroup: cfg.sessionHistory.allowCrossGroup !== false,
493
+ allowCrossPrivate: cfg.sessionHistory.allowCrossPrivate === true,
494
+ };
495
+ // 访问规则始终注册:它同样约束通用的 session_get_history,不随专属工具开关撤掉(关掉工具不能反倒放宽读取)。
496
+ // 规则只对 onebot 会话 id 表态,与 OneBot 平台在不在场无关:不进下面的平台闸。
497
+ registerOneBotHistoryAccessChecker(caps, historyCfg);
498
+ // 仅当 OneBot 平台可用时才注册工具。platform 是可选的多提供者服务,OneBot 适配器可能晚于
499
+ // app:ready 上线(WebUI 启用、市场热装、启动失败后修好),且作为非胜者上线时 follow 不触发:
500
+ // 故在 app:ready 与每次 platform 提供者注册时各判一次,出现 onebot 即注册,之后不再重复。
501
+ // 平台离场不反注册:工具调用时找不到 OneBot 连接本就会报错。
502
+ let toolsRegistered = false;
503
+ const registerToolsOnce = () => {
504
+ if (toolsRegistered || !getPlatformNames(platform).includes('onebot'))
505
+ return;
506
+ toolsRegistered = true;
507
+ logger.info('检测到 OneBot 平台,开始注册 OneBot 工具');
508
+ // 注册工具分组:按职责语义切分,彼此不重叠
509
+ tools.registerGroup({
510
+ name: 'onebot-daily',
511
+ label: 'OneBot 只读与日常',
512
+ description: '只读查询(群信息/成员/好友列表/历史/登录信息)+ 日常低风险互动(戳一戳、好友赞)',
513
+ });
514
+ tools.registerGroup({
515
+ name: 'onebot-group',
516
+ label: 'OneBot 群务',
517
+ description: '群务管理:禁言/全员禁言/踢人/撤回消息/设置群名片/改群名/设管理员/群打卡/审批加群申请 等',
518
+ });
519
+ tools.registerGroup({
520
+ name: 'onebot-personal',
521
+ label: 'OneBot 个人/私聊',
522
+ description: '影响 bot 账号本身的人际关系:退群、删好友、处理好友申请、接受/拒绝入群邀请',
523
+ });
524
+ if (cfg.groupManagement.enabled)
525
+ registerGroupManagementTools(caps, bundle);
526
+ if (cfg.groupInfo.enabled)
527
+ registerGroupInfoTools(caps, storage, bundle);
528
+ if (cfg.account.enabled)
529
+ registerAccountTools(caps, bundle);
530
+ if (cfg.interaction.enabled)
531
+ registerInteractionTools(caps, bundle);
532
+ if (cfg.sessionHistory.enabled)
533
+ registerSessionHistoryTools(caps, bundle, historyCfg);
534
+ registerRequestTools(caps, bundle);
535
+ };
536
+ events.on('app:ready', () => {
537
+ registerToolsOnce();
538
+ if (!toolsRegistered)
539
+ logger.info('未检测到 OneBot 平台,暂不注册 OneBot 工具(平台上线后自动补注册)');
540
+ });
541
+ events.on('service:registered', name => {
542
+ if (name === platformService.name)
543
+ registerToolsOnce();
544
+ });
545
+ },
546
+ });
536
547
  // ===== 群管理工具 =====
537
- function registerGroupManagementTools(ctx, bundle) {
548
+ function registerGroupManagementTools(caps, bundle) {
549
+ const { platform, logger } = caps;
538
550
  const { group, personal } = bundle;
539
551
  // ---- 群禁言(单人)----
540
552
  group.register({
@@ -555,18 +567,18 @@ function registerGroupManagementTools(ctx, bundle) {
555
567
  },
556
568
  },
557
569
  handler: async (args, callCtx) => {
558
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
570
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
559
571
  const duration = typeof args.duration === 'number' ? args.duration : 60;
560
- const permError = await checkAdminPermission(ctx, selfId, groupId, String(args.user_id));
572
+ const permError = await checkAdminPermission(platform, selfId, groupId, String(args.user_id));
561
573
  if (permError)
562
574
  return permError;
563
- await callAction(ctx, selfId, 'set_group_ban', {
575
+ await callAction(platform, selfId, 'set_group_ban', {
564
576
  group_id: Number(groupId),
565
577
  user_id: Number(args.user_id),
566
578
  duration,
567
579
  });
568
580
  // 验证禁言是否生效
569
- const info = await getGroupMemberInfo(ctx, selfId, groupId, String(args.user_id));
581
+ const info = await getGroupMemberInfo(platform, selfId, groupId, String(args.user_id));
570
582
  if (info && 'shut_up_timestamp' in info) {
571
583
  const shutUp = Number(info.shut_up_timestamp);
572
584
  const now = Math.floor(Date.now() / 1000);
@@ -602,11 +614,11 @@ function registerGroupManagementTools(ctx, bundle) {
602
614
  },
603
615
  },
604
616
  handler: async (args, callCtx) => {
605
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
606
- const permError = await checkAdminPermission(ctx, selfId, groupId);
617
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
618
+ const permError = await checkAdminPermission(platform, selfId, groupId);
607
619
  if (permError)
608
620
  return permError;
609
- await callAction(ctx, selfId, 'set_group_whole_ban', {
621
+ await callAction(platform, selfId, 'set_group_whole_ban', {
610
622
  group_id: Number(groupId),
611
623
  enable: !!args.enable,
612
624
  });
@@ -632,11 +644,11 @@ function registerGroupManagementTools(ctx, bundle) {
632
644
  },
633
645
  },
634
646
  handler: async (args, callCtx) => {
635
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
636
- const permError = await checkAdminPermission(ctx, selfId, groupId, String(args.user_id));
647
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
648
+ const permError = await checkAdminPermission(platform, selfId, groupId, String(args.user_id));
637
649
  if (permError)
638
650
  return permError;
639
- await callAction(ctx, selfId, 'set_group_kick', {
651
+ await callAction(platform, selfId, 'set_group_kick', {
640
652
  group_id: Number(groupId),
641
653
  user_id: Number(args.user_id),
642
654
  reject_add_request: !!args.reject_add_request,
@@ -662,12 +674,12 @@ function registerGroupManagementTools(ctx, bundle) {
662
674
  },
663
675
  handler: async (args, callCtx) => {
664
676
  const argGroupId = args.group_id != null && String(args.group_id).trim() ? String(args.group_id).trim() : '';
665
- const selfId = resolveSelfId(ctx, callCtx, args);
677
+ const selfId = resolveSelfId(platform, callCtx, args);
666
678
  const parsed = parseOneBotSession(callCtx.sessionId);
667
679
  const groupId = argGroupId || (parsed?.detailType === 'group' ? parsed.targetId : '');
668
680
  if (!groupId)
669
681
  return '请提供 group_id,或在要退出的群聊中调用此工具';
670
- await callAction(ctx, selfId, 'set_group_leave', {
682
+ await callAction(platform, selfId, 'set_group_leave', {
671
683
  group_id: Number(groupId),
672
684
  is_dismiss: false,
673
685
  });
@@ -693,11 +705,11 @@ function registerGroupManagementTools(ctx, bundle) {
693
705
  },
694
706
  },
695
707
  handler: async (args, callCtx) => {
696
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
697
- const permError = await checkAdminPermission(ctx, selfId, groupId);
708
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
709
+ const permError = await checkAdminPermission(platform, selfId, groupId);
698
710
  if (permError)
699
711
  return permError;
700
- await callAction(ctx, selfId, 'set_group_card', {
712
+ await callAction(platform, selfId, 'set_group_card', {
701
713
  group_id: Number(groupId),
702
714
  user_id: Number(args.user_id),
703
715
  card: String(args.card ?? ''),
@@ -723,11 +735,11 @@ function registerGroupManagementTools(ctx, bundle) {
723
735
  },
724
736
  },
725
737
  handler: async (args, callCtx) => {
726
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
727
- const permError = await checkAdminPermission(ctx, selfId, groupId);
738
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
739
+ const permError = await checkAdminPermission(platform, selfId, groupId);
728
740
  if (permError)
729
741
  return permError;
730
- await callAction(ctx, selfId, 'set_group_name', {
742
+ await callAction(platform, selfId, 'set_group_name', {
731
743
  group_id: Number(groupId),
732
744
  group_name: String(args.group_name),
733
745
  });
@@ -753,11 +765,11 @@ function registerGroupManagementTools(ctx, bundle) {
753
765
  },
754
766
  },
755
767
  handler: async (args, callCtx) => {
756
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
757
- const permError = await checkAdminPermission(ctx, selfId, groupId, undefined, true);
768
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
769
+ const permError = await checkAdminPermission(platform, selfId, groupId, undefined, true);
758
770
  if (permError)
759
771
  return permError;
760
- await callAction(ctx, selfId, 'set_group_special_title', {
772
+ await callAction(platform, selfId, 'set_group_special_title', {
761
773
  group_id: Number(groupId),
762
774
  user_id: Number(args.user_id),
763
775
  special_title: String(args.special_title ?? ''),
@@ -785,11 +797,11 @@ function registerGroupManagementTools(ctx, bundle) {
785
797
  },
786
798
  },
787
799
  handler: async (args, callCtx) => {
788
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
789
- const permError = await checkAdminPermission(ctx, selfId, groupId, undefined, true);
800
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
801
+ const permError = await checkAdminPermission(platform, selfId, groupId, undefined, true);
790
802
  if (permError)
791
803
  return permError;
792
- await callAction(ctx, selfId, 'set_group_admin', {
804
+ await callAction(platform, selfId, 'set_group_admin', {
793
805
  group_id: Number(groupId),
794
806
  user_id: Number(args.user_id),
795
807
  enable: !!args.enable,
@@ -815,8 +827,8 @@ function registerGroupManagementTools(ctx, bundle) {
815
827
  },
816
828
  handler: async (args, callCtx) => {
817
829
  // 不限制必须是群聊;只要能找到可用的 OneBot 账号即可
818
- const selfId = resolveSelfId(ctx, callCtx, args);
819
- await callAction(ctx, selfId, 'delete_msg', {
830
+ const selfId = resolveSelfId(platform, callCtx, args);
831
+ await callAction(platform, selfId, 'delete_msg', {
820
832
  message_id: Number(args.message_id),
821
833
  });
822
834
  return `已撤回消息 ${args.message_id}`;
@@ -844,7 +856,7 @@ function registerGroupManagementTools(ctx, bundle) {
844
856
  },
845
857
  },
846
858
  handler: async (args, callCtx) => {
847
- const selfId = resolveSelfId(ctx, callCtx, args);
859
+ const selfId = resolveSelfId(platform, callCtx, args);
848
860
  const argGroupId = args.group_id != null && String(args.group_id).trim() ? String(args.group_id).trim() : '';
849
861
  const argUserId = args.user_id != null && String(args.user_id).trim() ? String(args.user_id).trim() : '';
850
862
  let targetSessionId;
@@ -858,9 +870,9 @@ function registerGroupManagementTools(ctx, bundle) {
858
870
  return '当前不在 OneBot 会话中,请用 group_id 或 user_id 指定要撤回的目标会话。';
859
871
  targetSessionId = callCtx.sessionId;
860
872
  }
861
- const adapter = findOneBotAdapter(ctx);
873
+ const adapter = findOneBotAdapter(platform);
862
874
  if (!adapter?.getSentMessages) {
863
- return '当前 OneBot 适配器版本未记录机器人发出的消息(不支持 getSentMessages)。如已知 message_id 可改用 onebot_delete_msg。';
875
+ return '未找到支持记录自身发出消息的 OneBot 适配器(getSentMessages 不在平台契约内,第三方适配器可不提供)。如已知 message_id 可改用 onebot_delete_msg。';
864
876
  }
865
877
  const count = Math.max(1, Math.min(10, Math.floor(Number(args.count) || 1)));
866
878
  const recent = adapter.getSentMessages(targetSessionId, 10); // 新→旧
@@ -872,7 +884,7 @@ function registerGroupManagementTools(ctx, bundle) {
872
884
  const failed = [];
873
885
  for (const t of targets) {
874
886
  try {
875
- await callAction(ctx, selfId, 'delete_msg', { message_id: Number(t.messageId) || t.messageId });
887
+ await callAction(platform, selfId, 'delete_msg', { message_id: Number(t.messageId) || t.messageId });
876
888
  adapter.forgetSentMessage?.(targetSessionId, t.messageId);
877
889
  recalled.push(t.preview ? `「${t.preview}」` : `msg=${t.messageId}`);
878
890
  }
@@ -889,10 +901,11 @@ function registerGroupManagementTools(ctx, bundle) {
889
901
  return msg;
890
902
  },
891
903
  });
892
- ctx.logger.info('OneBot 群管理工具已注册');
904
+ logger.info('OneBot 群管理工具已注册');
893
905
  }
894
906
  // ===== 群信息查询工具 =====
895
- function registerGroupInfoTools(ctx, storage, bundle) {
907
+ function registerGroupInfoTools(caps, storage, bundle) {
908
+ const { platform, logger } = caps;
896
909
  const { daily } = bundle;
897
910
  // ---- 查看合并转发 ----
898
911
  daily.register({
@@ -915,7 +928,7 @@ function registerGroupInfoTools(ctx, storage, bundle) {
915
928
  },
916
929
  },
917
930
  handler: async (args, callCtx) => {
918
- const selfId = resolveSelfId(ctx, callCtx, args);
931
+ const selfId = resolveSelfId(platform, callCtx, args);
919
932
  if (!args.id)
920
933
  return '参数错误:缺少 id(合并转发 ID)。请使用 <forward id="..."> 中的 id 字符串,不要使用 message_id。';
921
934
  const forwardId = String(args.id);
@@ -924,7 +937,7 @@ function registerGroupInfoTools(ctx, storage, bundle) {
924
937
  // 并维护了一份接收时即抓取并展开(含摘要与图像识别)的缓存。
925
938
  let data;
926
939
  try {
927
- data = await callAction(ctx, selfId, 'get_forward_msg', { id: forwardId });
940
+ data = await callAction(platform, selfId, 'get_forward_msg', { id: forwardId });
928
941
  }
929
942
  catch (err) {
930
943
  return `合并转发读取失败:${err instanceof Error ? err.message : String(err)}。该转发可能已在协议端过期,或当前 OneBot 实现不支持跨会话读取。`;
@@ -937,7 +950,7 @@ function registerGroupInfoTools(ctx, storage, bundle) {
937
950
  const summaryLine = entry.summary ? `\n摘要:${entry.summary}\n` : '\n';
938
951
  return `${header}${summaryLine}\n原文:\n${entry.fullText}`;
939
952
  }
940
- const formatContext = await recognizeForwardImages(ctx, storage, selfId, data, limit);
953
+ const formatContext = await recognizeForwardImages(caps, storage, selfId, data, limit);
941
954
  return formatForwardMessage(data, limit, formatContext);
942
955
  },
943
956
  });
@@ -958,8 +971,8 @@ function registerGroupInfoTools(ctx, storage, bundle) {
958
971
  },
959
972
  },
960
973
  handler: async (args, callCtx) => {
961
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
962
- const data = await callAction(ctx, selfId, 'get_group_info', {
974
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
975
+ const data = await callAction(platform, selfId, 'get_group_info', {
963
976
  group_id: Number(groupId),
964
977
  });
965
978
  return JSON.stringify(data);
@@ -983,9 +996,9 @@ function registerGroupInfoTools(ctx, storage, bundle) {
983
996
  },
984
997
  },
985
998
  handler: async (args, callCtx) => {
986
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
999
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
987
1000
  const userId = args.user_id ? String(args.user_id) : selfId;
988
- const data = await callAction(ctx, selfId, 'get_group_member_info', {
1001
+ const data = await callAction(platform, selfId, 'get_group_member_info', {
989
1002
  group_id: Number(groupId),
990
1003
  user_id: Number(userId),
991
1004
  no_cache: true,
@@ -1014,8 +1027,8 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1014
1027
  },
1015
1028
  },
1016
1029
  handler: async (args, callCtx) => {
1017
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
1018
- const data = await callAction(ctx, selfId, 'get_group_member_list', {
1030
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
1031
+ const data = await callAction(platform, selfId, 'get_group_member_list', {
1019
1032
  group_id: Number(groupId),
1020
1033
  });
1021
1034
  const list = Array.isArray(data) ? data : [];
@@ -1078,7 +1091,7 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1078
1091
  },
1079
1092
  handler: async (args, callCtx) => {
1080
1093
  const argGroupId = args.group_id != null && String(args.group_id).trim() ? String(args.group_id).trim() : '';
1081
- const selfId = resolveSelfId(ctx, callCtx, args);
1094
+ const selfId = resolveSelfId(platform, callCtx, args);
1082
1095
  const parsed = parseOneBotSession(callCtx.sessionId);
1083
1096
  let groupId = argGroupId;
1084
1097
  if (!groupId) {
@@ -1087,12 +1100,12 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1087
1100
  }
1088
1101
  groupId = parsed.targetId;
1089
1102
  }
1090
- const adapter = findOneBotAdapter(ctx);
1103
+ const adapter = findOneBotAdapter(platform);
1091
1104
  if (!adapter?.callAction)
1092
1105
  return JSON.stringify({ available: false, reason: 'OneBot 适配器不可用' });
1093
1106
  let info = null;
1094
1107
  try {
1095
- info = (await callAction(ctx, selfId, 'get_group_member_info', {
1108
+ info = (await callAction(platform, selfId, 'get_group_member_info', {
1096
1109
  group_id: Number(groupId),
1097
1110
  user_id: Number(selfId),
1098
1111
  no_cache: true,
@@ -1141,9 +1154,12 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1141
1154
  },
1142
1155
  },
1143
1156
  handler: async (args) => {
1144
- const adapter = findOneBotAdapter(ctx);
1157
+ const adapter = findOneBotAdapter(platform);
1145
1158
  if (!adapter?.getSelfMutes) {
1146
- return JSON.stringify({ supported: false, reason: '当前 OneBot 适配器版本不支持 getSelfMutes' });
1159
+ return JSON.stringify({
1160
+ supported: false,
1161
+ reason: '未找到支持记录自身禁言状态的 OneBot 适配器(getSelfMutes 不在平台契约内,第三方适配器可不提供)',
1162
+ });
1147
1163
  }
1148
1164
  const limit = Math.max(1, Math.min(200, Math.floor(Number(args.limit) || 30)));
1149
1165
  const offset = Math.max(0, Math.floor(Number(args.offset) || 0));
@@ -1186,11 +1202,11 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1186
1202
  },
1187
1203
  },
1188
1204
  handler: async (args, callCtx) => {
1189
- const selfId = resolveSelfId(ctx, callCtx, args);
1205
+ const selfId = resolveSelfId(platform, callCtx, args);
1190
1206
  if (!args.message_id)
1191
1207
  return '参数错误:缺少 message_id';
1192
1208
  try {
1193
- const data = await callAction(ctx, selfId, 'get_msg', {
1209
+ const data = await callAction(platform, selfId, 'get_msg', {
1194
1210
  message_id: Number(args.message_id),
1195
1211
  });
1196
1212
  return JSON.stringify(data);
@@ -1222,19 +1238,20 @@ function registerGroupInfoTools(ctx, storage, bundle) {
1222
1238
  },
1223
1239
  },
1224
1240
  handler: async (args, callCtx) => {
1225
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
1241
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
1226
1242
  const honorType = typeof args.type === 'string' ? args.type : 'all';
1227
- const data = await callAction(ctx, selfId, 'get_group_honor_info', {
1243
+ const data = await callAction(platform, selfId, 'get_group_honor_info', {
1228
1244
  group_id: Number(groupId),
1229
1245
  type: honorType,
1230
1246
  });
1231
1247
  return JSON.stringify(data);
1232
1248
  },
1233
1249
  });
1234
- ctx.logger.info('OneBot 群信息查询工具已注册');
1250
+ logger.info('OneBot 群信息查询工具已注册');
1235
1251
  }
1236
1252
  // ===== 账号 / 好友 / 群列表查询工具 =====
1237
- function registerAccountTools(ctx, bundle) {
1253
+ function registerAccountTools(caps, bundle) {
1254
+ const { platform, logger } = caps;
1238
1255
  const { daily, personal } = bundle;
1239
1256
  // ---- 群列表 ----
1240
1257
  daily.register({
@@ -1258,8 +1275,8 @@ function registerAccountTools(ctx, bundle) {
1258
1275
  },
1259
1276
  },
1260
1277
  handler: async (args, callCtx) => {
1261
- const selfId = resolveSelfId(ctx, callCtx, args);
1262
- const data = await callAction(ctx, selfId, 'get_group_list', {});
1278
+ const selfId = resolveSelfId(platform, callCtx, args);
1279
+ const data = await callAction(platform, selfId, 'get_group_list', {});
1263
1280
  const list = Array.isArray(data) ? data : [];
1264
1281
  const keyword = typeof args.keyword === 'string' ? args.keyword.trim().toLowerCase() : '';
1265
1282
  // 上限封顶,避免超大 limit 把整张列表灌进 LLM 上下文(token 浪费)。
@@ -1307,8 +1324,8 @@ function registerAccountTools(ctx, bundle) {
1307
1324
  },
1308
1325
  },
1309
1326
  handler: async (args, callCtx) => {
1310
- const selfId = resolveSelfId(ctx, callCtx, args);
1311
- const data = await callAction(ctx, selfId, 'get_friend_list', {});
1327
+ const selfId = resolveSelfId(platform, callCtx, args);
1328
+ const data = await callAction(platform, selfId, 'get_friend_list', {});
1312
1329
  const list = Array.isArray(data) ? data : [];
1313
1330
  const keyword = typeof args.keyword === 'string' ? args.keyword.trim().toLowerCase() : '';
1314
1331
  // 上限封顶,避免超大 limit 把整张列表灌进 LLM 上下文(token 浪费)。
@@ -1355,8 +1372,8 @@ function registerAccountTools(ctx, bundle) {
1355
1372
  },
1356
1373
  },
1357
1374
  handler: async (args, callCtx) => {
1358
- const selfId = resolveSelfId(ctx, callCtx, args);
1359
- const data = await callAction(ctx, selfId, 'get_stranger_info', {
1375
+ const selfId = resolveSelfId(platform, callCtx, args);
1376
+ const data = await callAction(platform, selfId, 'get_stranger_info', {
1360
1377
  user_id: Number(args.user_id),
1361
1378
  no_cache: !!args.no_cache,
1362
1379
  });
@@ -1374,8 +1391,8 @@ function registerAccountTools(ctx, bundle) {
1374
1391
  },
1375
1392
  },
1376
1393
  handler: async (_args, callCtx) => {
1377
- const selfId = resolveSelfId(ctx, callCtx, _args);
1378
- const data = await callAction(ctx, selfId, 'get_login_info', {});
1394
+ const selfId = resolveSelfId(platform, callCtx, _args);
1395
+ const data = await callAction(platform, selfId, 'get_login_info', {});
1379
1396
  return JSON.stringify(data);
1380
1397
  },
1381
1398
  });
@@ -1397,21 +1414,22 @@ function registerAccountTools(ctx, bundle) {
1397
1414
  },
1398
1415
  handler: async (args, callCtx) => {
1399
1416
  const argUserId = args.user_id != null && String(args.user_id).trim() ? String(args.user_id).trim() : '';
1400
- const selfId = resolveSelfId(ctx, callCtx, args);
1417
+ const selfId = resolveSelfId(platform, callCtx, args);
1401
1418
  const parsed = parseOneBotSession(callCtx.sessionId);
1402
1419
  const userId = argUserId || (parsed?.detailType === 'private' ? parsed.targetId : '');
1403
1420
  if (!userId)
1404
1421
  return '请提供 user_id,或在要删除的好友私聊中调用此工具';
1405
- await callAction(ctx, selfId, 'delete_friend', {
1422
+ await callAction(platform, selfId, 'delete_friend', {
1406
1423
  user_id: Number(userId),
1407
1424
  });
1408
1425
  return `已删除好友 ${userId}`;
1409
1426
  },
1410
1427
  });
1411
- ctx.logger.info('OneBot 账号 / 好友 / 群列表查询工具已注册');
1428
+ logger.info('OneBot 账号 / 好友 / 群列表查询工具已注册');
1412
1429
  }
1413
1430
  // ===== 特殊交互工具 =====
1414
- function registerInteractionTools(ctx, bundle) {
1431
+ function registerInteractionTools(caps, bundle) {
1432
+ const { platform, logger } = caps;
1415
1433
  const { daily, group } = bundle;
1416
1434
  // ---- 戳一戳 ----
1417
1435
  daily.register({
@@ -1436,7 +1454,7 @@ function registerInteractionTools(ctx, bundle) {
1436
1454
  },
1437
1455
  },
1438
1456
  handler: async (args, callCtx) => {
1439
- const selfId = resolveSelfId(ctx, callCtx, args);
1457
+ const selfId = resolveSelfId(platform, callCtx, args);
1440
1458
  const parsed = parseOneBotSession(callCtx.sessionId);
1441
1459
  const argGroupId = args.group_id != null && String(args.group_id).trim() ? String(args.group_id).trim() : '';
1442
1460
  const argUserId = args.user_id != null && String(args.user_id).trim() ? String(args.user_id).trim() : '';
@@ -1447,13 +1465,13 @@ function registerInteractionTools(ctx, bundle) {
1447
1465
  throw new Error('群聊戳一戳必须显式提供 user_id(要戳的对象)。');
1448
1466
  }
1449
1467
  try {
1450
- await callAction(ctx, selfId, 'group_poke', {
1468
+ await callAction(platform, selfId, 'group_poke', {
1451
1469
  group_id: Number(groupId),
1452
1470
  user_id: Number(argUserId),
1453
1471
  });
1454
1472
  }
1455
1473
  catch {
1456
- await callAction(ctx, selfId, 'send_group_poke', {
1474
+ await callAction(platform, selfId, 'send_group_poke', {
1457
1475
  group_id: Number(groupId),
1458
1476
  user_id: Number(argUserId),
1459
1477
  });
@@ -1467,10 +1485,10 @@ function registerInteractionTools(ctx, bundle) {
1467
1485
  }
1468
1486
  // NapCat: friend_poke { user_id };其他实现可能用 send_poke
1469
1487
  try {
1470
- await callAction(ctx, selfId, 'friend_poke', { user_id: Number(userId) });
1488
+ await callAction(platform, selfId, 'friend_poke', { user_id: Number(userId) });
1471
1489
  }
1472
1490
  catch {
1473
- await callAction(ctx, selfId, 'send_poke', { user_id: Number(userId) });
1491
+ await callAction(platform, selfId, 'send_poke', { user_id: Number(userId) });
1474
1492
  }
1475
1493
  return `已戳了 ${userId} 一下`;
1476
1494
  },
@@ -1493,9 +1511,9 @@ function registerInteractionTools(ctx, bundle) {
1493
1511
  },
1494
1512
  },
1495
1513
  handler: async (args, callCtx) => {
1496
- const selfId = resolveSelfId(ctx, callCtx, args);
1514
+ const selfId = resolveSelfId(platform, callCtx, args);
1497
1515
  const times = Math.min(10, Math.max(1, typeof args.times === 'number' ? Math.floor(args.times) : 1));
1498
- await callAction(ctx, selfId, 'send_like', {
1516
+ await callAction(platform, selfId, 'send_like', {
1499
1517
  user_id: Number(args.user_id),
1500
1518
  times,
1501
1519
  });
@@ -1519,66 +1537,63 @@ function registerInteractionTools(ctx, bundle) {
1519
1537
  },
1520
1538
  },
1521
1539
  handler: async (args, callCtx) => {
1522
- const { selfId, groupId } = resolveGroupTarget(ctx, callCtx, args);
1523
- await callAction(ctx, selfId, 'send_group_sign', {
1540
+ const { selfId, groupId } = resolveGroupTarget(platform, callCtx, args);
1541
+ await callAction(platform, selfId, 'send_group_sign', {
1524
1542
  group_id: Number(groupId),
1525
1543
  });
1526
1544
  return '打卡成功';
1527
1545
  },
1528
1546
  });
1529
- ctx.logger.info('OneBot 特殊交互工具已注册');
1547
+ logger.info('OneBot 特殊交互工具已注册');
1530
1548
  }
1531
1549
  /**
1532
1550
  * 把 OneBot 的细粒度访问规则注册到 session-history 服务。
1533
1551
  * 通用工具 session_get_history 和平台专属工具 onebot_get_session_history
1534
1552
  * 调用 service 时都会走这条规则链 —— 不存在绕过路径。
1535
1553
  */
1536
- function registerOneBotHistoryAccessChecker(ctx, cfg) {
1537
- ctx.on('app:ready', () => {
1538
- const historyService = ctx.getService('session-history');
1539
- if (!historyService?.registerAccessChecker) {
1540
- ctx.logger.debug('session-history 服务未提供 registerAccessChecker, 跳过 OneBot 访问规则注册');
1541
- return;
1542
- }
1543
- const checker = {
1544
- platform: 'onebot',
1545
- check({ currentSessionId, targetSessionId }) {
1546
- const target = parseOneBotSession(targetSessionId);
1547
- if (!target)
1548
- return undefined; // 不是合法 onebot 目标, 不表态
1549
- const current = parseOneBotSession(currentSessionId);
1550
- // 跨平台调用(current 不是 onebot)走默认放行: session-history scope 已粗筛
1551
- if (!current)
1552
- return undefined;
1553
- if (current.selfId !== target.selfId && !cfg.allowCrossSelf) {
1554
- return { decision: 'deny', reason: '当前 OneBot 配置不允许跨机器人账号读取会话历史' };
1555
- }
1556
- if (current.detailType === 'group' && target.detailType === 'private' && !cfg.allowGroupReadPrivate) {
1557
- return { decision: 'deny', reason: '当前 OneBot 配置不允许从群聊读取私聊历史' };
1558
- }
1559
- if (current.detailType === 'group' &&
1560
- target.detailType === 'group' &&
1561
- current.targetId !== target.targetId &&
1562
- !cfg.allowCrossGroup) {
1563
- return { decision: 'deny', reason: '当前 OneBot 配置不允许跨群读取其他群历史' };
1564
- }
1565
- if (current.detailType === 'private' &&
1566
- target.detailType === 'private' &&
1567
- current.targetId !== target.targetId &&
1568
- !cfg.allowCrossPrivate) {
1569
- return { decision: 'deny', reason: '当前 OneBot 配置不允许跨私聊读取其他私聊历史' };
1570
- }
1554
+ function registerOneBotHistoryAccessChecker(caps, cfg) {
1555
+ const { logger, sessionHistory } = caps;
1556
+ const checker = {
1557
+ platform: 'onebot',
1558
+ check({ currentSessionId, targetSessionId }) {
1559
+ const target = parseOneBotSession(targetSessionId);
1560
+ if (!target)
1561
+ return undefined; // 不是合法 onebot 目标, 不表态
1562
+ const current = parseOneBotSession(currentSessionId);
1563
+ // 跨平台调用(current 不是 onebot)走默认放行: session-history scope 已粗筛
1564
+ if (!current)
1571
1565
  return undefined;
1572
- },
1573
- };
1574
- const dispose = historyService.registerAccessChecker(checker);
1575
- // 注意:总线上并不存在 'dispose' 事件——此前 ctx.on('dispose', ...) 永远不会
1576
- // 触发,access checker 在插件 bounce 后泄漏。正确挂法是 onDispose 清理链。
1577
- ctx.onDispose(dispose);
1578
- ctx.logger.info('OneBot 会话历史访问规则已注册到 session-history');
1566
+ if (current.selfId !== target.selfId && !cfg.allowCrossSelf) {
1567
+ return { decision: 'deny', reason: '当前 OneBot 配置不允许跨机器人账号读取会话历史' };
1568
+ }
1569
+ if (current.detailType === 'group' && target.detailType === 'private' && !cfg.allowGroupReadPrivate) {
1570
+ return { decision: 'deny', reason: '当前 OneBot 配置不允许从群聊读取私聊历史' };
1571
+ }
1572
+ if (current.detailType === 'group' &&
1573
+ target.detailType === 'group' &&
1574
+ current.targetId !== target.targetId &&
1575
+ !cfg.allowCrossGroup) {
1576
+ return { decision: 'deny', reason: '当前 OneBot 配置不允许跨群读取其他群历史' };
1577
+ }
1578
+ if (current.detailType === 'private' &&
1579
+ target.detailType === 'private' &&
1580
+ current.targetId !== target.targetId &&
1581
+ !cfg.allowCrossPrivate) {
1582
+ return { decision: 'deny', reason: '当前 OneBot 配置不允许跨私聊读取其他私聊历史' };
1583
+ }
1584
+ return undefined;
1585
+ },
1586
+ };
1587
+ // 规则登记在 session-history 提供者实例的局部状态里:提供者重启或换人后,新实例身上没有这条规则,
1588
+ // 须跟随提供者重挂;返回的注销作为跟随清理,本插件 bounce / 卸载时一并撤回,不在下层泄漏。
1589
+ sessionHistory.follow(svc => {
1590
+ const dispose = svc.registerAccessChecker(checker);
1591
+ logger.info('OneBot 会话历史访问规则已注册到 session-history');
1592
+ return dispose;
1579
1593
  });
1580
1594
  }
1581
- function registerSessionHistoryTools(ctx, bundle, cfg) {
1595
+ function registerSessionHistoryTools(caps, bundle, cfg) {
1596
+ const { platform, logger, sessionHistory } = caps;
1582
1597
  const { daily } = bundle;
1583
1598
  daily.register({
1584
1599
  definition: {
@@ -1612,7 +1627,7 @@ function registerSessionHistoryTools(ctx, bundle, cfg) {
1612
1627
  if (!targetId)
1613
1628
  return JSON.stringify({ error: 'target_id 不能为空' });
1614
1629
  const argSelfId = args.self_id ? String(args.self_id).trim() : '';
1615
- const selfId = argSelfId || current?.selfId || getAnyOneBotSelfId(ctx) || '';
1630
+ const selfId = argSelfId || current?.selfId || getAnyOneBotSelfId(platform) || '';
1616
1631
  if (!selfId)
1617
1632
  return JSON.stringify({ error: '无法确定 self_id,且未找到可用的 OneBot 连接' });
1618
1633
  // 跨账号限制:仅在当前也是 OneBot 会话时才作为参考点检查
@@ -1672,10 +1687,10 @@ function registerSessionHistoryTools(ctx, bundle, cfg) {
1672
1687
  if (!targetId)
1673
1688
  return JSON.stringify({ error: 'target_id 不能为空' });
1674
1689
  const argSelfId = args.self_id ? String(args.self_id).trim() : '';
1675
- const selfId = argSelfId || current?.selfId || getAnyOneBotSelfId(ctx) || '';
1690
+ const selfId = argSelfId || current?.selfId || getAnyOneBotSelfId(platform) || '';
1676
1691
  if (!selfId)
1677
1692
  return JSON.stringify({ error: '无法确定 self_id,且未找到可用的 OneBot 连接' });
1678
- const history = ctx.getService('session-history');
1693
+ const history = sessionHistory.current;
1679
1694
  if (!history)
1680
1695
  return JSON.stringify({
1681
1696
  error: 'session-history 服务不可用,请启用 @aalis/plugin-tool-session',
@@ -1698,19 +1713,20 @@ function registerSessionHistoryTools(ctx, bundle, cfg) {
1698
1713
  }
1699
1714
  catch (err) {
1700
1715
  const message = err instanceof Error ? err.message : String(err);
1701
- ctx.logger.warn(`onebot_get_session_history 失败 (${sessionId}): ${message}`);
1716
+ logger.warn(`onebot_get_session_history 失败 (${sessionId}): ${message}`);
1702
1717
  return JSON.stringify({ error: `读取 OneBot 会话历史失败: ${message}` });
1703
1718
  }
1704
1719
  },
1705
1720
  });
1706
- ctx.logger.info('OneBot 会话历史工具已注册');
1721
+ logger.info('OneBot 会话历史工具已注册');
1707
1722
  }
1708
1723
  // ===== 请求处理工具(好友申请 / 群请求)=====
1709
- function registerRequestTools(ctx, bundle) {
1724
+ function registerRequestTools(caps, bundle) {
1725
+ const { platform, logger } = caps;
1710
1726
  const { group, personal } = bundle;
1711
1727
  /** 找到支持 handleFriendRequest 的 OneBot 适配器 */
1712
- function findRequestAdapter(ctx) {
1713
- const adapter = getPlatformAdapters(ctx).find(a => a.platform === 'onebot' && typeof a.handleFriendRequest === 'function');
1728
+ function findRequestAdapter(platform) {
1729
+ const adapter = getPlatformAdapters(platform).find(a => a.platform === 'onebot' && typeof a.handleFriendRequest === 'function');
1714
1730
  return adapter;
1715
1731
  }
1716
1732
  personal.register({
@@ -1731,7 +1747,7 @@ function registerRequestTools(ctx, bundle) {
1731
1747
  },
1732
1748
  },
1733
1749
  handler: async (args) => {
1734
- const adapter = findRequestAdapter(ctx);
1750
+ const adapter = findRequestAdapter(platform);
1735
1751
  if (!adapter)
1736
1752
  return '未找到支持请求处理的 OneBot 适配器';
1737
1753
  return adapter.handleFriendRequest(String(args.user_id), !!args.approve, args.remark ? String(args.remark) : undefined);
@@ -1760,7 +1776,7 @@ function registerRequestTools(ctx, bundle) {
1760
1776
  },
1761
1777
  },
1762
1778
  handler: async (args) => {
1763
- const adapter = findRequestAdapter(ctx);
1779
+ const adapter = findRequestAdapter(platform);
1764
1780
  if (!adapter)
1765
1781
  return '未找到支持请求处理的 OneBot 适配器';
1766
1782
  return adapter.handleGroupRequest(String(args.user_id), String(args.group_id), !!args.approve, args.reason ? String(args.reason) : undefined);
@@ -1785,12 +1801,12 @@ function registerRequestTools(ctx, bundle) {
1785
1801
  },
1786
1802
  },
1787
1803
  handler: async (args) => {
1788
- const adapter = findRequestAdapter(ctx);
1804
+ const adapter = findRequestAdapter(platform);
1789
1805
  if (!adapter)
1790
1806
  return '未找到支持请求处理的 OneBot 适配器';
1791
1807
  return adapter.handleGroupRequest(String(args.user_id), String(args.group_id), !!args.approve, args.reason ? String(args.reason) : undefined);
1792
1808
  },
1793
1809
  });
1794
- ctx.logger.info('OneBot 请求处理工具已注册');
1810
+ logger.info('OneBot 请求处理工具已注册');
1795
1811
  }
1796
1812
  //# sourceMappingURL=index.js.map