koishi-plugin-mai-bridge 0.1.4 → 0.1.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/README.md CHANGED
@@ -60,6 +60,7 @@ webuiPort: 8002
60
60
  messageMode: coexist
61
61
  groupMessageTriggerCount: 1
62
62
  directMessageTriggerCount: 1
63
+ commandResultMode: source
63
64
  ```
64
65
 
65
66
  说明:
@@ -71,6 +72,8 @@ directMessageTriggerCount: 1
71
72
  - `webuiPublicUrl` 可填写反代或宿主机映射后的 WebUI 地址,用于 Koishi 控制台显示入口。
72
73
  - `groupMessageTriggerCount` 控制群聊累计多少条消息后批量转发并强制触发 maibot 思考。设为 `3` 时,同一群前两条先缓存,第 3 条会连同前两条一起转发。
73
74
  - `directMessageTriggerCount` 控制私聊累计多少条消息后批量转发并强制触发 maibot 思考。默认 `1`,表示每条私信都会触发;设为 `3` 时,同一私聊前两条只缓存,第 3 条统一转发并触发回复。
75
+ - `commandResultMode` 控制聊天中执行 `mai.ko.*` 管理指令后的结果发送方式。`source` 发回原群聊/私聊,`admin` 发到管理员私聊,`silent` 不发送结果。
76
+ - `commandResultAdminUserId` 仅在 `commandResultMode: admin` 时使用。填写管理员 QQ 号;留空时发给指令调用者。
74
77
 
75
78
  ## 消息模式
76
79
 
@@ -1,5 +1,6 @@
1
- import type { Context } from 'koishi';
1
+ import { type Context, type Fragment, type Session } from 'koishi';
2
2
  import type { Config } from '../config';
3
3
  import type { RuntimeStatus } from '../types';
4
4
  export declare function renderStatus(status: RuntimeStatus): string;
5
+ export declare function sendCommandResult(session: Session, config: Config, content: string): Promise<Fragment | void>;
5
6
  export declare function registerCommands(ctx: Context, config: Config): void;
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderStatus = renderStatus;
4
+ exports.sendCommandResult = sendCommandResult;
4
5
  exports.registerCommands = registerCommands;
6
+ const koishi_1 = require("koishi");
7
+ const logger = new koishi_1.Logger('mai.ko/commands');
5
8
  function formatTime(value) {
6
9
  return value ? new Date(value).toLocaleString() : '-';
7
10
  }
@@ -39,27 +42,53 @@ function renderStatus(status) {
39
42
  }
40
43
  return lines.join('\n');
41
44
  }
45
+ async function sendCommandResult(session, config, content) {
46
+ if (config.commandResultMode === 'silent')
47
+ return;
48
+ if (config.commandResultMode !== 'admin')
49
+ return content;
50
+ const userId = config.commandResultAdminUserId.trim() || session.userId;
51
+ if (!userId)
52
+ return 'mai.ko 指令结果发送失败:没有可用的管理员用户 ID。';
53
+ if (!session.bot?.sendPrivateMessage)
54
+ return 'mai.ko 指令结果发送失败:当前平台不支持私聊发送。';
55
+ try {
56
+ const guildId = config.commandResultAdminGuildId.trim() || undefined;
57
+ await session.bot.sendPrivateMessage(userId, content, guildId, { session });
58
+ if (config.commandResultNotifySource)
59
+ return 'mai.ko 指令结果已发送给管理员。';
60
+ }
61
+ catch (error) {
62
+ const message = error instanceof Error ? error.message : String(error);
63
+ logger.warn(`send command result failed: user=${userId} error=${message}`);
64
+ return `mai.ko 指令结果发送失败:${message}`;
65
+ }
66
+ }
67
+ async function handleCommandResult(session, config, status) {
68
+ const content = renderStatus(await status);
69
+ return session ? sendCommandResult(session, config, content) : content;
70
+ }
42
71
  function registerCommands(ctx, config) {
43
72
  const options = { authority: config.commandAuthority };
44
73
  ctx.command('mai.ko', '查看 mai.ko 托管状态', options)
45
74
  .alias('maibot')
46
- .action(() => renderStatus(ctx.maimai.getStatus()));
75
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.getStatus()));
47
76
  ctx.command('mai.ko.status', '查看 mai.ko 托管状态', options)
48
- .action(() => renderStatus(ctx.maimai.getStatus()));
77
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.getStatus()));
49
78
  ctx.command('mai.ko.start', '启动 mai.ko', options)
50
- .action(async () => renderStatus(await ctx.maimai.launch()));
79
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.launch()));
51
80
  ctx.command('mai.ko.stop', '停止 mai.ko', options)
52
- .action(async () => renderStatus(await ctx.maimai.shutdown()));
81
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.shutdown()));
53
82
  ctx.command('mai.ko.restart', '重启 mai.ko', options)
54
- .action(async () => renderStatus(await ctx.maimai.restart()));
83
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.restart()));
55
84
  ctx.command('mai.ko.reconnect', '重连 mai.ko Bridge', options)
56
- .action(async () => renderStatus(await ctx.maimai.reconnect()));
85
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.reconnect()));
57
86
  ctx.command('mai.ko.prepare', '准备 MaiBot 源码、补丁与 Docker 镜像', options)
58
- .action(async () => renderStatus(await ctx.maimai.prepare()));
87
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.prepare()));
59
88
  ctx.command('mai.ko.docker.start', '启动 maimai-ko Docker 容器', options)
60
- .action(async () => renderStatus(await ctx.maimai.dockerStart()));
89
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerStart()));
61
90
  ctx.command('mai.ko.docker.stop', '停止 maimai-ko Docker 容器', options)
62
- .action(async () => renderStatus(await ctx.maimai.dockerStop()));
91
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerStop()));
63
92
  ctx.command('mai.ko.docker.restart', '重建并重启 maimai-ko Docker 容器', options)
64
- .action(async () => renderStatus(await ctx.maimai.dockerRestart()));
93
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerRestart()));
65
94
  }
package/out/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Schema } from 'koishi';
2
- import type { MessageLogLevel, MessageMode, ProcessMode } from './types';
2
+ import type { CommandResultMode, MessageLogLevel, MessageMode, ProcessMode } from './types';
3
3
  export interface Config {
4
4
  processMode: ProcessMode;
5
5
  maibotRoot: string;
@@ -30,6 +30,10 @@ export interface Config {
30
30
  imageDownloadMaxBytes: number;
31
31
  messageLogLevel: MessageLogLevel;
32
32
  commandAuthority: number;
33
+ commandResultMode: CommandResultMode;
34
+ commandResultAdminUserId: string;
35
+ commandResultAdminGuildId: string;
36
+ commandResultNotifySource: boolean;
33
37
  acceptMaibotAgreements: boolean;
34
38
  startupTimeout: number;
35
39
  shutdownTimeout: number;
package/out/config.js CHANGED
@@ -54,6 +54,14 @@ exports.Config = koishi_1.Schema.intersect([
54
54
  koishi_1.Schema.const('detail').description('详细:输出每一步消息中转细节。'),
55
55
  ]).default('summary').description('mai.ko 消息中转日志详细程度。'),
56
56
  commandAuthority: koishi_1.Schema.number().min(0).max(5).default(3).description('管理指令需要的权限等级。'),
57
+ commandResultMode: koishi_1.Schema.union([
58
+ koishi_1.Schema.const('source').description('原会话:指令结果发送到触发指令的群聊或私聊。'),
59
+ koishi_1.Schema.const('admin').description('管理员私聊:指令结果发送到指定管理员;未填写管理员时发送给指令调用者。'),
60
+ koishi_1.Schema.const('silent').description('静默:执行指令但不发送结果。'),
61
+ ]).default('source').description('聊天中执行 mai.ko 管理指令后的结果发送方式。'),
62
+ commandResultAdminUserId: koishi_1.Schema.string().default('').description('管理员用户 ID。仅 commandResultMode=admin 时使用;留空时发送给指令调用者。'),
63
+ commandResultAdminGuildId: koishi_1.Schema.string().default('').description('发送管理员私聊时附带的群组/频道 ID。通常可留空。'),
64
+ commandResultNotifySource: koishi_1.Schema.boolean().default(false).description('结果发送给管理员后,是否在原会话发送一条简短提示。'),
57
65
  }).description('消息路由'),
58
66
  koishi_1.Schema.object({
59
67
  startupTimeout: koishi_1.Schema.number().min(1000).default(60000).description('等待 mai.ko API 可连接的超时时间,单位毫秒。'),
package/out/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Awaitable, Context, Fragment, Session } from 'koishi';
2
2
  export type MessageMode = 'coexist' | 'exclusive' | 'command';
3
+ export type CommandResultMode = 'source' | 'admin' | 'silent';
3
4
  export type ProcessMode = 'docker' | 'managed' | 'external';
4
5
  export type ProcessState = 'idle' | 'starting' | 'running' | 'stopping' | 'stopped' | 'blocked' | 'error';
5
6
  export type TransportState = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-mai-bridge",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "将 maibot 作为 docker 托管的消息回复后端运行,使用 mai.ko 桥接消息收发。",
5
5
  "author": "Jelly10086 <wow40469600@gmail.com>",
6
6
  "contributors": [
@@ -1,7 +1,9 @@
1
- import type { Context } from 'koishi'
1
+ import { Logger, type Awaitable, type Context, type Fragment, type Session } from 'koishi'
2
2
  import type { Config } from '../config'
3
3
  import type { RuntimeStatus } from '../types'
4
4
 
5
+ const logger = new Logger('mai.ko/commands')
6
+
5
7
  function formatTime(value?: number) {
6
8
  return value ? new Date(value).toLocaleString() : '-'
7
9
  }
@@ -34,37 +36,61 @@ export function renderStatus(status: RuntimeStatus) {
34
36
  return lines.join('\n')
35
37
  }
36
38
 
39
+ export async function sendCommandResult(session: Session, config: Config, content: string): Promise<Fragment | void> {
40
+ if (config.commandResultMode === 'silent') return
41
+ if (config.commandResultMode !== 'admin') return content
42
+
43
+ const userId = config.commandResultAdminUserId.trim() || session.userId
44
+ if (!userId) return 'mai.ko 指令结果发送失败:没有可用的管理员用户 ID。'
45
+ if (!session.bot?.sendPrivateMessage) return 'mai.ko 指令结果发送失败:当前平台不支持私聊发送。'
46
+
47
+ try {
48
+ const guildId = config.commandResultAdminGuildId.trim() || undefined
49
+ await session.bot.sendPrivateMessage(userId, content, guildId, { session })
50
+ if (config.commandResultNotifySource) return 'mai.ko 指令结果已发送给管理员。'
51
+ } catch (error) {
52
+ const message = error instanceof Error ? error.message : String(error)
53
+ logger.warn(`send command result failed: user=${userId} error=${message}`)
54
+ return `mai.ko 指令结果发送失败:${message}`
55
+ }
56
+ }
57
+
58
+ async function handleCommandResult(session: Session | undefined, config: Config, status: Awaitable<RuntimeStatus>) {
59
+ const content = renderStatus(await status)
60
+ return session ? sendCommandResult(session, config, content) : content
61
+ }
62
+
37
63
  export function registerCommands(ctx: Context, config: Config) {
38
64
  const options = { authority: config.commandAuthority }
39
65
 
40
66
  ctx.command('mai.ko', '查看 mai.ko 托管状态', options)
41
67
  .alias('maibot')
42
- .action(() => renderStatus(ctx.maimai.getStatus()))
68
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.getStatus()))
43
69
 
44
70
  ctx.command('mai.ko.status', '查看 mai.ko 托管状态', options)
45
- .action(() => renderStatus(ctx.maimai.getStatus()))
71
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.getStatus()))
46
72
 
47
73
  ctx.command('mai.ko.start', '启动 mai.ko', options)
48
- .action(async () => renderStatus(await ctx.maimai.launch()))
74
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.launch()))
49
75
 
50
76
  ctx.command('mai.ko.stop', '停止 mai.ko', options)
51
- .action(async () => renderStatus(await ctx.maimai.shutdown()))
77
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.shutdown()))
52
78
 
53
79
  ctx.command('mai.ko.restart', '重启 mai.ko', options)
54
- .action(async () => renderStatus(await ctx.maimai.restart()))
80
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.restart()))
55
81
 
56
82
  ctx.command('mai.ko.reconnect', '重连 mai.ko Bridge', options)
57
- .action(async () => renderStatus(await ctx.maimai.reconnect()))
83
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.reconnect()))
58
84
 
59
85
  ctx.command('mai.ko.prepare', '准备 MaiBot 源码、补丁与 Docker 镜像', options)
60
- .action(async () => renderStatus(await ctx.maimai.prepare()))
86
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.prepare()))
61
87
 
62
88
  ctx.command('mai.ko.docker.start', '启动 maimai-ko Docker 容器', options)
63
- .action(async () => renderStatus(await ctx.maimai.dockerStart()))
89
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerStart()))
64
90
 
65
91
  ctx.command('mai.ko.docker.stop', '停止 maimai-ko Docker 容器', options)
66
- .action(async () => renderStatus(await ctx.maimai.dockerStop()))
92
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerStop()))
67
93
 
68
94
  ctx.command('mai.ko.docker.restart', '重建并重启 maimai-ko Docker 容器', options)
69
- .action(async () => renderStatus(await ctx.maimai.dockerRestart()))
95
+ .action((argv) => handleCommandResult(argv.session, config, ctx.maimai.dockerRestart()))
70
96
  }
package/src/config.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Schema } from 'koishi'
2
- import type { MessageLogLevel, MessageMode, ProcessMode } from './types'
2
+ import type { CommandResultMode, MessageLogLevel, MessageMode, ProcessMode } from './types'
3
3
 
4
4
  export interface Config {
5
5
  processMode: ProcessMode
@@ -31,6 +31,10 @@ export interface Config {
31
31
  imageDownloadMaxBytes: number
32
32
  messageLogLevel: MessageLogLevel
33
33
  commandAuthority: number
34
+ commandResultMode: CommandResultMode
35
+ commandResultAdminUserId: string
36
+ commandResultAdminGuildId: string
37
+ commandResultNotifySource: boolean
34
38
  acceptMaibotAgreements: boolean
35
39
  startupTimeout: number
36
40
  shutdownTimeout: number
@@ -105,6 +109,14 @@ export const Config: Schema<Config> = Schema.intersect([
105
109
  Schema.const('detail').description('详细:输出每一步消息中转细节。'),
106
110
  ]).default('summary').description('mai.ko 消息中转日志详细程度。'),
107
111
  commandAuthority: Schema.number().min(0).max(5).default(3).description('管理指令需要的权限等级。'),
112
+ commandResultMode: Schema.union([
113
+ Schema.const('source').description('原会话:指令结果发送到触发指令的群聊或私聊。'),
114
+ Schema.const('admin').description('管理员私聊:指令结果发送到指定管理员;未填写管理员时发送给指令调用者。'),
115
+ Schema.const('silent').description('静默:执行指令但不发送结果。'),
116
+ ]).default('source').description('聊天中执行 mai.ko 管理指令后的结果发送方式。'),
117
+ commandResultAdminUserId: Schema.string().default('').description('管理员用户 ID。仅 commandResultMode=admin 时使用;留空时发送给指令调用者。'),
118
+ commandResultAdminGuildId: Schema.string().default('').description('发送管理员私聊时附带的群组/频道 ID。通常可留空。'),
119
+ commandResultNotifySource: Schema.boolean().default(false).description('结果发送给管理员后,是否在原会话发送一条简短提示。'),
108
120
  }).description('消息路由'),
109
121
  Schema.object({
110
122
  startupTimeout: Schema.number().min(1000).default(60000).description('等待 mai.ko API 可连接的超时时间,单位毫秒。'),
package/src/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Awaitable, Context, Fragment, Session } from 'koishi'
2
2
 
3
3
  export type MessageMode = 'coexist' | 'exclusive' | 'command'
4
+ export type CommandResultMode = 'source' | 'admin' | 'silent'
4
5
  export type ProcessMode = 'docker' | 'managed' | 'external'
5
6
  export type ProcessState = 'idle' | 'starting' | 'running' | 'stopping' | 'stopped' | 'blocked' | 'error'
6
7
  export type TransportState = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error'