koishi-plugin-onebot-group-manage 0.0.3 → 0.0.5

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/lib/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface Config {
17
17
  kick: ActionRule;
18
18
  title: ActionRule;
19
19
  selfMute: SelfMuteConfig;
20
+ chatlunaTools?: boolean;
20
21
  debug?: DebugOptions;
21
22
  }
22
23
  export declare const Config: Schema<Config>;
package/lib/index.js CHANGED
@@ -26,6 +26,92 @@ __export(src_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(src_exports);
28
28
  var import_koishi = require("koishi");
29
+
30
+ // src/tools.ts
31
+ var import_tools = require("@langchain/core/tools");
32
+ var import_zod = require("zod");
33
+ var GroupMuteTool = class extends import_tools.StructuredTool {
34
+ constructor(runMute, verbose) {
35
+ super();
36
+ this.runMute = runMute;
37
+ this.verbose = verbose;
38
+ }
39
+ static {
40
+ __name(this, "GroupMuteTool");
41
+ }
42
+ name = "group_mute";
43
+ description = "在群内禁言成员";
44
+ schema = import_zod.z.object({
45
+ groupId: import_zod.z.string().trim().min(1).describe("目标群号,不填则使用当前会话群").optional(),
46
+ userId: import_zod.z.string().trim().min(1).describe("要禁言的成员 QQ 号"),
47
+ duration: import_zod.z.number().int().positive().describe("禁言时长(秒),0秒为解除禁言")
48
+ });
49
+ async _call(input, _runManager, runnableConfig) {
50
+ const session = runnableConfig?.configurable?.session;
51
+ if (!session) return "缺少 session,无法执行禁言";
52
+ const result = await this.runMute(session, {
53
+ targetId: input.userId,
54
+ duration: input.duration,
55
+ guildId: input.groupId
56
+ });
57
+ return typeof result === "string" ? result : JSON.stringify(result);
58
+ }
59
+ };
60
+ var GroupTitleTool = class extends import_tools.StructuredTool {
61
+ constructor(runTitle, verbose) {
62
+ super();
63
+ this.runTitle = runTitle;
64
+ this.verbose = verbose;
65
+ }
66
+ static {
67
+ __name(this, "GroupTitleTool");
68
+ }
69
+ name = "group_set_title";
70
+ description = "为成员设置群头衔(需群主权限)";
71
+ schema = import_zod.z.object({
72
+ groupId: import_zod.z.string().trim().min(1).describe("目标群号,不填则使用当前会话群").optional(),
73
+ userId: import_zod.z.string().trim().min(1).describe("要设置头衔的成员 QQ 号"),
74
+ title: import_zod.z.string().trim().min(1).describe("头衔文案")
75
+ });
76
+ async _call(input, _runManager, runnableConfig) {
77
+ const session = runnableConfig?.configurable?.session;
78
+ if (!session) return "缺少 session,无法设置头衔";
79
+ const result = await this.runTitle(session, {
80
+ targetId: input.userId,
81
+ title: input.title,
82
+ guildId: input.groupId
83
+ });
84
+ return typeof result === "string" ? result : JSON.stringify(result);
85
+ }
86
+ };
87
+ function registerChatlunaTools(ctx, config, logger, verbose, handlers) {
88
+ const platform = ctx.chatluna?.platform;
89
+ if (!platform) {
90
+ logger.debug("chatluna 未安装,跳过群管工具注册");
91
+ return;
92
+ }
93
+ ctx.effect(
94
+ () => platform.registerTool("group_mute", {
95
+ createTool: /* @__PURE__ */ __name(() => new GroupMuteTool(
96
+ (session, params) => handlers.performMute(ctx, session, params, config, logger, verbose),
97
+ verbose
98
+ ), "createTool"),
99
+ selector: /* @__PURE__ */ __name(() => true, "selector")
100
+ })
101
+ );
102
+ ctx.effect(
103
+ () => platform.registerTool("group_set_title", {
104
+ createTool: /* @__PURE__ */ __name(() => new GroupTitleTool(
105
+ (session, params) => handlers.performTitle(ctx, session, params, config, logger, verbose),
106
+ verbose
107
+ ), "createTool"),
108
+ selector: /* @__PURE__ */ __name(() => true, "selector")
109
+ })
110
+ );
111
+ }
112
+ __name(registerChatlunaTools, "registerChatlunaTools");
113
+
114
+ // src/index.ts
29
115
  var name = "onebot-group-manage";
30
116
  var ROLE_CACHE_TTL = 3e4;
31
117
  var ROLE_QUERY_TIMEOUT = 1e4;
@@ -43,6 +129,7 @@ var Config = import_koishi.Schema.object({
43
129
  minSeconds: import_koishi.Schema.number().min(1).default(60).description("“禁言我”随机最小秒数"),
44
130
  maxSeconds: import_koishi.Schema.number().min(1).default(600).description("“禁言我”随机最大秒数")
45
131
  }).description("自助禁言配置"),
132
+ chatlunaTools: import_koishi.Schema.boolean().default(true).description("启用 ChatLuna 工具注册(需安装 chatluna)"),
46
133
  debug: import_koishi.Schema.object({
47
134
  verbose: import_koishi.Schema.boolean().default(false).description("开启详细日志(仅建议排查问题时启用)")
48
135
  }).default({ verbose: false }).description("调试选项")
@@ -166,13 +253,15 @@ async function getMemberRole(probeOnebot, targetBot, guildId, userId, logger, ve
166
253
  }
167
254
  }
168
255
  __name(getMemberRole, "getMemberRole");
169
- async function verifyCaller(session, rule, logger, verbose) {
256
+ async function verifyCaller(session, rule, logger, verbose, guildIdOverride) {
170
257
  const authority = Number(session.user?.authority ?? 0);
171
258
  if (!rule.requireGroupAdmin && rule.minAuthority <= 0) return null;
172
259
  if (rule.minAuthority > 0 && authority >= rule.minAuthority) return null;
173
- if (rule.requireGroupAdmin && session.guildId) {
260
+ const guildId = guildIdOverride ?? session.guildId;
261
+ if (rule.requireGroupAdmin && !guildId) return "仅限群聊使用";
262
+ if (rule.requireGroupAdmin && guildId) {
174
263
  const probe = session.onebot || getBotOnebot(session.bot);
175
- const role = await getMemberRole(probe, session.bot, session.guildId, session.userId, logger, verbose);
264
+ const role = await getMemberRole(probe, session.bot, guildId, session.userId, logger, verbose);
176
265
  if (role === "owner" || role === "admin") return null;
177
266
  }
178
267
  if (rule.requireGroupAdmin && rule.minAuthority > 0) {
@@ -246,9 +335,88 @@ function randomInt(min, max) {
246
335
  return Math.floor(Math.random() * (max - min + 1)) + min;
247
336
  }
248
337
  __name(randomInt, "randomInt");
338
+ async function performMute(ctx, session, params, config, logger, verbose) {
339
+ const guildId = params.guildId ?? session.guildId;
340
+ if (!guildId) return "仅限群聊使用";
341
+ const targetId = params.targetId ?? parseTargetId(params.targetRaw || "", session);
342
+ if (!targetId) return "请提供要禁言的用户(@ 或 QQ 号)";
343
+ const seconds = Number(params.duration);
344
+ if (!Number.isFinite(seconds) || seconds <= 0) return "禁言时长必须为大于 0 的整数(秒)";
345
+ const msg = await verifyCaller(session, config.mute, logger, verbose, guildId);
346
+ if (msg) return msg;
347
+ verbose && logger.info(
348
+ `mute: guild=${guildId} caller=${session.userId} bot=${botKey(session.bot)} target=${targetId} seconds=${seconds}`
349
+ );
350
+ const picked = await pickManageBot(ctx, guildId, false, logger, session.bot, verbose, session.onebot);
351
+ if (!picked) return "没有可用且有群管权限的 Bot";
352
+ const targetRole = await getMemberRole(picked.probeOnebot, picked.bot, guildId, targetId, logger, verbose);
353
+ if (targetRole === "owner") return "无法对群主执行该操作";
354
+ const actionOnebot = getBotOnebot(picked.bot);
355
+ if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
356
+ try {
357
+ verbose && logger.info(`mute: use bot=${botKey(picked.bot)} role=${picked.role} duration=${seconds}`);
358
+ if (typeof actionOnebot.setGroupBan === "function") {
359
+ await actionOnebot.setGroupBan(Number(guildId), Number(targetId), Number(seconds));
360
+ } else {
361
+ await callOneBotApi(picked.bot, "set_group_ban", {
362
+ group_id: Number(guildId),
363
+ user_id: Number(targetId),
364
+ duration: Number(seconds)
365
+ });
366
+ }
367
+ roleCache.clear();
368
+ return `已使用 ${picked.bot.selfId} 禁言 ${targetId} ${seconds} 秒`;
369
+ } catch (error) {
370
+ logger.warn(error);
371
+ return "禁言失败,检查 Bot 权限或参数";
372
+ }
373
+ }
374
+ __name(performMute, "performMute");
375
+ async function performTitle(ctx, session, params, config, logger, verbose) {
376
+ const guildId = params.guildId ?? session.guildId;
377
+ if (!guildId) return "仅限群聊使用";
378
+ const title = params.title?.trim();
379
+ if (!title) return "请提供要设置的头衔";
380
+ const userId = params.targetId ?? parseTargetId(params.targetRaw || "", session);
381
+ if (!userId) return "请提供要设置头衔的用户(@ 或 QQ 号)";
382
+ const msg = await verifyCaller(session, config.title, logger, verbose, guildId);
383
+ if (msg) return msg;
384
+ verbose && logger.info(
385
+ `title: guild=${guildId} caller=${session.userId} bot=${botKey(session.bot)} target=${userId} title=${title}`
386
+ );
387
+ const picked = await pickManageBot(ctx, guildId, true, logger, session.bot, verbose, session.onebot);
388
+ if (!picked) return "没有具备群主权限的 Bot 可用";
389
+ const actionOnebot = getBotOnebot(picked.bot);
390
+ if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
391
+ try {
392
+ verbose && logger.info(`title: use bot=${botKey(picked.bot)} role=${picked.role}`);
393
+ if (typeof actionOnebot.setGroupSpecialTitle === "function") {
394
+ await actionOnebot.setGroupSpecialTitle(Number(guildId), Number(userId), title, -1);
395
+ } else {
396
+ await callOneBotApi(picked.bot, "set_group_special_title", {
397
+ group_id: Number(guildId),
398
+ user_id: Number(userId),
399
+ special_title: title,
400
+ duration: -1
401
+ });
402
+ }
403
+ roleCache.clear();
404
+ return `已使用 ${picked.bot.selfId} 为 ${userId} 设置头衔:${title}`;
405
+ } catch (error) {
406
+ logger.warn(error);
407
+ return "设置头衔失败,检查 Bot 权限或参数";
408
+ }
409
+ }
410
+ __name(performTitle, "performTitle");
249
411
  function apply(ctx, config) {
250
412
  const logger = ctx.logger(name);
251
413
  const verbose = config.debug?.verbose ?? false;
414
+ if (config.chatlunaTools) {
415
+ registerChatlunaTools(ctx, config, logger, verbose, {
416
+ performMute,
417
+ performTitle
418
+ });
419
+ }
252
420
  ctx.command("group.manage", "OneBot 群管", { authority: 0 }).channelFields(["platform"]);
253
421
  ctx.command("group.manage/check <target:string>", "调试:查询 get_group_member_info").alias("gcheck").action(async ({ session }, target) => {
254
422
  if (!session?.guildId) return "仅限群聊使用";
@@ -289,37 +457,15 @@ function apply(ctx, config) {
289
457
  return lines.join("\n");
290
458
  });
291
459
  ctx.command("group.manage/mute <target:string> <duration:number>", "禁言目标(秒,必须指定)").alias("gmute").action(async ({ session }, target, duration) => {
292
- if (!session?.guildId) return "仅限群聊使用";
293
- const msg = await verifyCaller(session, config.mute, logger, verbose);
294
- if (msg) return msg;
295
- const userId = parseTargetId(target, session);
296
- if (!userId) return "请提供要禁言的用户(@ QQ 号)";
297
- const seconds = Number(duration);
298
- if (!Number.isFinite(seconds) || seconds <= 0) return "禁言时长必须为大于 0 的整数(秒)";
299
- verbose && logger.info(`mute: guild=${session.guildId} caller=${session.userId} bot=${botKey(session.bot)} target=${userId} seconds=${seconds}`);
300
- const picked = await pickManageBot(ctx, session.guildId, false, logger, session.bot, verbose, session.onebot);
301
- if (!picked) return "没有可用且有群管权限的 Bot";
302
- const targetRole = await getMemberRole(picked.probeOnebot, picked.bot, session.guildId, userId, logger, verbose);
303
- if (targetRole === "owner") return "无法对群主执行该操作";
304
- const actionOnebot = getBotOnebot(picked.bot);
305
- if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
306
- try {
307
- verbose && logger.info(`mute: use bot=${botKey(picked.bot)} role=${picked.role} duration=${seconds}`);
308
- if (typeof actionOnebot.setGroupBan === "function") {
309
- await actionOnebot.setGroupBan(Number(session.guildId), Number(userId), Number(seconds));
310
- } else {
311
- await callOneBotApi(picked.bot, "set_group_ban", {
312
- group_id: Number(session.guildId),
313
- user_id: Number(userId),
314
- duration: Number(seconds)
315
- });
316
- }
317
- roleCache.clear();
318
- return `已使用 ${picked.bot.selfId} 禁言 ${userId} ${seconds} 秒`;
319
- } catch (error) {
320
- logger.warn(error);
321
- return "禁言失败,检查 Bot 权限或参数";
322
- }
460
+ if (!session) return "缺少 session";
461
+ return performMute(
462
+ ctx,
463
+ session,
464
+ { targetRaw: target, duration: Number(duration) },
465
+ config,
466
+ logger,
467
+ verbose
468
+ );
323
469
  });
324
470
  ctx.command("group.manage/unmute <target:string>", "解除禁言").alias("gunmute").action(async ({ session }, target) => {
325
471
  if (!session?.guildId) return "仅限群聊使用";
@@ -385,97 +531,48 @@ function apply(ctx, config) {
385
531
  }
386
532
  });
387
533
  ctx.command("group.manage/title <target:string> <title:text>", "设置群头衔(需群主 Bot)").alias("gtitle").action(async ({ session }, target, title) => {
388
- if (!session?.guildId) return "仅限群聊使用";
389
- const msg = await verifyCaller(session, config.title, logger, verbose);
390
- if (msg) return msg;
391
- if (!title?.trim()) return "请提供要设置的头衔";
392
- const userId = parseTargetId(target, session);
393
- if (!userId) return "请提供要设置头衔的用户(@ 或 QQ 号)";
394
- verbose && logger.info(`title: guild=${session.guildId} caller=${session.userId} bot=${botKey(session.bot)} target=${userId} title=${title.trim()}`);
395
- const picked = await pickManageBot(ctx, session.guildId, true, logger, session.bot, verbose, session.onebot);
396
- if (!picked) return "没有具备群主权限的 Bot 可用";
397
- const actionOnebot = getBotOnebot(picked.bot);
398
- if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
399
- try {
400
- verbose && logger.info(`title: use bot=${botKey(picked.bot)} role=${picked.role}`);
401
- if (typeof actionOnebot.setGroupSpecialTitle === "function") {
402
- await actionOnebot.setGroupSpecialTitle(Number(session.guildId), Number(userId), title.trim(), -1);
403
- } else {
404
- await callOneBotApi(picked.bot, "set_group_special_title", {
405
- group_id: Number(session.guildId),
406
- user_id: Number(userId),
407
- special_title: title.trim(),
408
- duration: -1
409
- });
410
- }
411
- roleCache.clear();
412
- return `已使用 ${picked.bot.selfId} 为 ${userId} 设置头衔:${title.trim()}`;
413
- } catch (error) {
414
- logger.warn(error);
415
- return "设置头衔失败,检查 Bot 权限或参数";
416
- }
534
+ if (!session) return "缺少 session";
535
+ return performTitle(
536
+ ctx,
537
+ session,
538
+ { targetRaw: target, title: title || "", guildId: session.guildId },
539
+ config,
540
+ logger,
541
+ verbose
542
+ );
417
543
  });
418
544
  ctx.command("group.manage/mute.me", "禁言我(随机时长)").alias("gmute.me").action(async ({ session }) => {
419
545
  if (!session?.guildId) return "仅限群聊使用";
420
- const msg = await verifyCaller(session, config.mute, logger, verbose);
421
- if (msg) return msg;
422
546
  const minS = Math.max(1, Math.floor(Number(config.selfMute?.minSeconds ?? 60)));
423
547
  const maxS = Math.max(1, Math.floor(Number(config.selfMute?.maxSeconds ?? 600)));
424
548
  const minSeconds = Math.min(minS, maxS);
425
549
  const maxSeconds = Math.max(minS, maxS);
426
550
  const seconds = randomInt(minSeconds, maxSeconds);
427
- verbose && logger.info(`mute.me: guild=${session.guildId} caller=${session.userId} seconds=${seconds}`);
428
- const picked = await pickManageBot(ctx, session.guildId, false, logger, session.bot, verbose, session.onebot);
429
- if (!picked) return "没有可用且有群管权限的 Bot";
430
- const myRole = await getMemberRole(picked.probeOnebot, picked.bot, session.guildId, session.userId, logger, verbose);
431
- if (myRole === "owner") return "群主不能禁言自己";
432
- const actionOnebot = getBotOnebot(picked.bot);
433
- if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
434
- try {
435
- if (typeof actionOnebot.setGroupBan === "function") {
436
- await actionOnebot.setGroupBan(Number(session.guildId), Number(session.userId), Number(seconds));
437
- } else {
438
- await callOneBotApi(picked.bot, "set_group_ban", {
439
- group_id: Number(session.guildId),
440
- user_id: Number(session.userId),
441
- duration: Number(seconds)
442
- });
443
- }
444
- roleCache.clear();
445
- return `你已被禁言 ${seconds} 秒`;
446
- } catch (error) {
447
- logger.warn(error);
448
- return "禁言失败,检查 Bot 权限或参数";
449
- }
551
+ const result = await performMute(
552
+ ctx,
553
+ session,
554
+ { targetId: session.userId, duration: seconds, guildId: session.guildId },
555
+ config,
556
+ logger,
557
+ verbose
558
+ );
559
+ if (typeof result === "string" && result.startsWith("已使用")) return `你已被禁言 ${seconds} 秒`;
560
+ return result;
450
561
  });
451
562
  ctx.command("group.manage/title.apply <title:text>", "申请群头衔(给自己)").alias("gtitle.apply").action(async ({ session }, title) => {
452
563
  if (!session?.guildId) return "仅限群聊使用";
453
- const t = title?.trim();
454
- if (!t) return "请提供要申请的头衔";
455
- const msg = await verifyCaller(session, config.title, logger, verbose);
456
- if (msg) return msg;
457
- verbose && logger.info(`title.apply: guild=${session.guildId} caller=${session.userId} title=${t}`);
458
- const picked = await pickManageBot(ctx, session.guildId, true, logger, session.bot, verbose, session.onebot);
459
- if (!picked) return "没有具备群主权限的 Bot 可用";
460
- const actionOnebot = getBotOnebot(picked.bot);
461
- if (!actionOnebot) return "选中的 Bot 缺少 onebot 实例,无法执行";
462
- try {
463
- if (typeof actionOnebot.setGroupSpecialTitle === "function") {
464
- await actionOnebot.setGroupSpecialTitle(Number(session.guildId), Number(session.userId), t, -1);
465
- } else {
466
- await callOneBotApi(picked.bot, "set_group_special_title", {
467
- group_id: Number(session.guildId),
468
- user_id: Number(session.userId),
469
- special_title: t,
470
- duration: -1
471
- });
472
- }
473
- roleCache.clear();
474
- return `已为你设置头衔:${t}`;
475
- } catch (error) {
476
- logger.warn(error);
477
- return "设置头衔失败,检查 Bot 权限或参数";
564
+ const result = await performTitle(
565
+ ctx,
566
+ session,
567
+ { targetId: session.userId, title: title || "", guildId: session.guildId },
568
+ config,
569
+ logger,
570
+ verbose
571
+ );
572
+ if (typeof result === "string" && result.startsWith("已使用")) {
573
+ return `已为你设置头衔:${(title || "").trim()}`;
478
574
  }
575
+ return result;
479
576
  });
480
577
  }
481
578
  __name(apply, "apply");
package/lib/tools.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { Context, Session } from 'koishi';
2
+ interface Handlers {
3
+ performMute: (ctx: Context, session: Session, params: {
4
+ targetId: string;
5
+ duration: number;
6
+ guildId?: string;
7
+ }, ...rest: any[]) => Promise<string>;
8
+ performTitle: (ctx: Context, session: Session, params: {
9
+ targetId: string;
10
+ title: string;
11
+ guildId?: string;
12
+ }, ...rest: any[]) => Promise<string>;
13
+ }
14
+ export declare function registerChatlunaTools(ctx: Context, config: {
15
+ chatlunaTools?: boolean;
16
+ }, logger: ReturnType<Context['logger']>, verbose: boolean | undefined, handlers: Handlers): void;
17
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-onebot-group-manage",
3
3
  "description": "主要针对多bot环境的群管插件,自动使用有权限的bot",
4
- "version": "0.0.3",
4
+ "version": "0.0.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -15,6 +15,9 @@
15
15
  "plugin"
16
16
  ],
17
17
  "peerDependencies": {
18
- "koishi": "^4.18.7"
18
+ "koishi": "^4.18.7",
19
+ "@langchain/core": "^0.3.0",
20
+ "koishi-plugin-chatluna": "^1.3.0",
21
+ "zod": "^3.23.8"
19
22
  }
20
23
  }