mioku-plugin-mc 4.0.0 → 4.0.2

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/core/rcon.ts CHANGED
@@ -24,7 +24,7 @@ export function isCommandAllowed(
24
24
  text: string,
25
25
  serverItem: ServerConfig,
26
26
  isMaster: boolean,
27
- userId: number | string,
27
+ userId: string,
28
28
  ): boolean {
29
29
  if (isMaster) return true;
30
30
 
package/index.ts CHANGED
@@ -6,7 +6,6 @@ import { createPlayManager } from "./play";
6
6
  import { createMcSkill } from "./skills/mc";
7
7
  import { handleDebugCommand } from "./play/debug/commands";
8
8
  import { createServerManager } from "./utils/server-manager";
9
- import { parseMcCommand } from "./utils/command-router";
10
9
  import { handleStatus } from "./handlers/status";
11
10
  import { handleSync } from "./handlers/sync";
12
11
  import { handleReconnect } from "./handlers/reconnect";
@@ -21,8 +20,6 @@ import type { McConfig } from "./types";
21
20
 
22
21
  export default definePlugin({
23
22
  name: "mc",
24
- version: "1.0.0",
25
- description: "Minecraft服务器与QQ群消息互通插件",
26
23
 
27
24
  async setup(ctx: MiokuContext) {
28
25
  const configService = getService(ctx, Services.Config);
@@ -66,18 +63,15 @@ export default definePlugin({
66
63
 
67
64
  ctx.handle("message", async (event) => {
68
65
  const text = ctx.text(event).trim();
69
- const groupId =
70
- "group_id" in event && typeof event.group_id === "number"
71
- ? event.group_id
72
- : undefined;
66
+ const groupId = String(event.group_id ?? "").trim() || undefined;
73
67
 
74
68
  if (groupId) {
75
69
  const reply = await handleDebugCommand({
76
70
  text,
77
- isOwner: ctx.isOwner?.(event) ?? false,
71
+ isMaster: ctx.isMaster?.(event) ?? false,
78
72
  debugEnabled: playConfigHandler.getConfig().debug.enabled,
79
73
  playManager,
80
- groupId: Number(groupId),
74
+ groupId,
81
75
  });
82
76
  if (reply !== null) {
83
77
  await event.reply(reply);
@@ -85,60 +79,64 @@ export default definePlugin({
85
79
  }
86
80
  }
87
81
 
88
- const parsed = parseMcCommand(text);
89
-
90
- if (!parsed || parsed.action === "") {
91
- if (groupId) {
92
- playManager.onQqMessage(event);
93
- await forwardToMc(ctx, event, config, configHandler, serverManager);
94
- }
95
- return;
82
+ if (groupId) {
83
+ await forwardToMc(ctx, event, config, configHandler, serverManager);
96
84
  }
85
+ });
97
86
 
98
- switch (parsed.action) {
99
- case "状态": {
100
- await handleStatus(serverManager, config, async (msg) => {
101
- await event.reply(msg);
102
- });
103
- break;
104
- }
105
- case "开启同步": {
106
- const serverName = parsed.args[0];
107
- await handleSync(
108
- serverName,
109
- true,
110
- serverManager,
111
- configHandler,
112
- config,
113
- async (msg) => {
114
- await event.reply(msg);
115
- },
116
- );
117
- break;
118
- }
119
- case "关闭同步": {
120
- const serverName = parsed.args[0];
121
- await handleSync(
122
- serverName,
123
- false,
124
- serverManager,
125
- configHandler,
126
- config,
127
- async (msg) => {
128
- await event.reply(msg);
129
- },
130
- );
131
- break;
132
- }
133
- case "重连": {
134
- if (ctx.isOwner?.(event)) {
135
- await handleReconnect(serverManager, async (msg) => {
136
- await event.reply(msg);
137
- });
138
- }
139
- break;
140
- }
141
- }
87
+ const replyWithEvent = (event: import("mioku").MessageEvent) =>
88
+ async (msg: string) => {
89
+ await event.reply(msg);
90
+ };
91
+
92
+ ctx.command({
93
+ id: "mc-status",
94
+ name: "mc-status",
95
+ prefixes: ["/", "."],
96
+ match: /^mc\s+状态\s*$/,
97
+ permission: "master",
98
+ description: "查看所有已配置服务器的WebSocket连接状态,包括连接中、已断开等",
99
+ async handler({ event }) {
100
+ await handleStatus(serverManager, config, replyWithEvent(event));
101
+ },
102
+ });
103
+
104
+ ctx.command({
105
+ id: "mc-sync-on",
106
+ name: "mc-sync-on",
107
+ prefixes: ["/", "."],
108
+ match: /^mc\s+开启同步(?:\s+(.+))?$/,
109
+ description: "开启指定服务器的群聊消息同步功能,开启后该服务器可接收和发送群聊消息",
110
+ usage: ".mc 开启同步 <服务器名称>",
111
+ permission: "master",
112
+ async handler({ event, args }) {
113
+ await handleSync(args[0], true, serverManager, configHandler, config, replyWithEvent(event));
114
+ },
115
+ });
116
+
117
+ ctx.command({
118
+ id: "mc-sync-off",
119
+ name: "mc-sync-off",
120
+ prefixes: ["/", "."],
121
+ match: /^mc\s+关闭同步(?:\s+(.+))?$/,
122
+ description: "关闭指定服务器的群聊消息同步功能,关闭后该服务器不再接收和发送群聊消息",
123
+ usage: ".mc 关闭同步 <服务器名称>",
124
+ permission: "master",
125
+ async handler({ event, args }) {
126
+ await handleSync(args[0], false, serverManager, configHandler, config, replyWithEvent(event));
127
+ },
128
+ });
129
+
130
+ ctx.command({
131
+ id: "mc-reconnect",
132
+ name: "mc-reconnect",
133
+ prefixes: ["/", "."],
134
+ match: /^mc\s+重连\s*$/,
135
+ permission: "master",
136
+ description: "断开并重新建立所有服务器的WebSocket连接,用于连接异常时手动恢复",
137
+ async handler({ event }) {
138
+ await handleReconnect(serverManager, replyWithEvent(event));
139
+ },
142
140
  });
143
141
 
144
142
  ctx.logger.info("Minecraft插件加载成功");
@@ -197,12 +195,10 @@ async function forwardToMc(
197
195
  const text = ctx.text(event);
198
196
  ctx.logger.debug(`[MC] 收到群消息 group=${event.group_id} text=${text}`);
199
197
 
200
- // 检查是否为群消息
201
- if (!event.group_id) {
202
- return;
203
- }
198
+ const groupId = String(event.group_id ?? "").trim();
199
+ if (!groupId) return;
204
200
 
205
- const servers = configHandler.getServersForGroup(event.group_id);
201
+ const servers = configHandler.getServersForGroup(groupId);
206
202
  if (servers.length === 0) return;
207
203
 
208
204
  const msgList = Array.isArray(event.message)
@@ -219,7 +215,7 @@ async function forwardToMc(
219
215
  const isAllowed = isCommandAllowed(
220
216
  commandText,
221
217
  server,
222
- ctx.isOwner?.(event) ?? false,
218
+ ctx.isMaster?.(event) ?? false,
223
219
  event.user_id ?? "",
224
220
  );
225
221
 
@@ -252,4 +248,4 @@ async function forwardToMc(
252
248
  ctx.logger.error(`[MC] 发送到服务器 ${server.server_name} 失败: ${err}`);
253
249
  }
254
250
  }
255
- }
251
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-mc",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Minecraft与QQ群消息互通插件,支持服务器状态监控与RCON命令",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -15,53 +15,7 @@
15
15
  "services": [
16
16
  "config",
17
17
  "ai"
18
- ],
19
- "accessHooks": [
20
- {
21
- "id": "/mc 状态",
22
- "match": "/^\\/mc\\s*状态/"
23
- },
24
- {
25
- "id": "/mc 开启同步",
26
- "match": "/^\\/mc\\s*开启同步/"
27
- },
28
- {
29
- "id": "/mc 关闭同步",
30
- "match": "/^\\/mc\\s*关闭同步/"
31
- },
32
- {
33
- "id": "/mc 重连",
34
- "match": "/^\\/mc\\s*重连/"
35
- }
36
- ],
37
- "help": {
38
- "title": "Minecraft",
39
- "description": "Minecraft服务器与QQ群消息互通插件,支持服务器状态监控与RCON命令",
40
- "commands": [
41
- {
42
- "cmd": "/mc 状态",
43
- "desc": "查看所有已配置服务器的WebSocket连接状态,包括连接中、已断开等",
44
- "role": "master"
45
- },
46
- {
47
- "cmd": "/mc 开启同步",
48
- "desc": "开启指定服务器的群聊消息同步功能,开启后该服务器可接收和发送群聊消息",
49
- "usage": "/mc 开启同步 <服务器名称>",
50
- "role": "master"
51
- },
52
- {
53
- "cmd": "/mc 关闭同步",
54
- "desc": "关闭指定服务器的群聊消息同步功能,关闭后该服务器不再接收和发送群聊消息",
55
- "usage": "/mc 关闭同步 <服务器名称>",
56
- "role": "master"
57
- },
58
- {
59
- "cmd": "/mc 重连",
60
- "desc": "断开并重新建立所有服务器的WebSocket连接,用于连接异常时手动恢复",
61
- "role": "master"
62
- }
63
- ]
64
- }
18
+ ]
65
19
  },
66
20
  "dependencies": {
67
21
  "@cikeyqi/queqiao-node-sdk": "^0.1.3",
package/play/ai/prompt.ts CHANGED
@@ -126,7 +126,7 @@ function describeTerminator(t: WorkTerminator): string {
126
126
  export function buildSessionFacts(input: {
127
127
  serverName: string;
128
128
  username: string;
129
- groupId: number;
129
+ groupId: string;
130
130
  maxPlayMs: number;
131
131
  allowedCommands: string[];
132
132
  }): string {
package/play/config.ts CHANGED
@@ -27,10 +27,12 @@ export function createPlayConfigHandler(configService: ConfigService | undefined
27
27
  return currentConfig.servers.find((s) => s.id === id) ?? null;
28
28
  };
29
29
 
30
- const findBinding = (groupId: number | string): GroupBinding | null => {
31
- const gid = Number(groupId);
32
- if (!Number.isFinite(gid) || gid <= 0) return null;
33
- return currentConfig.groups.find((g) => g.groupId === gid) ?? null;
30
+ const findBinding = (groupId: string | number): GroupBinding | null => {
31
+ const gid = String(groupId ?? "").trim();
32
+ if (!gid) return null;
33
+ return (
34
+ currentConfig.groups.find((g) => String(g.groupId) === gid) ?? null
35
+ );
34
36
  };
35
37
 
36
38
  return { register, getConfig, findServer, findBinding };
@@ -2,10 +2,10 @@ import type { PlayManager } from "../index";
2
2
 
3
3
  export interface DebugCommandContext {
4
4
  text: string;
5
- isOwner: boolean;
5
+ isMaster: boolean;
6
6
  debugEnabled: boolean;
7
7
  playManager: PlayManager;
8
- groupId: number;
8
+ groupId: string;
9
9
  }
10
10
 
11
11
  const OVERLAY_NAMES = ["defend", "auto_eat"];
@@ -78,7 +78,7 @@ function parseBundleArgs(arg: string): ParsedBundleArgs | null {
78
78
  export async function handleDebugCommand(
79
79
  ctx: DebugCommandContext,
80
80
  ): Promise<string | null> {
81
- if (!ctx.debugEnabled || !ctx.isOwner) return null;
81
+ if (!ctx.debugEnabled || !ctx.isMaster) return null;
82
82
  const text = ctx.text.trim();
83
83
  if (!text.startsWith("/")) return null;
84
84
  const head = text.split(/\s+/)[0]?.toLowerCase();
package/play/index.ts CHANGED
@@ -41,7 +41,7 @@ export class PlayManager {
41
41
  private readonly configService: ConfigService | undefined;
42
42
  private readonly playConfigHandler: PlayConfigHandler;
43
43
  private readonly syncConfigHandler: ConfigHandler;
44
- private readonly sessions = new Map<number, PlaySession>();
44
+ private readonly sessions = new Map<string, PlaySession>();
45
45
  private mainInstance?: AIInstance;
46
46
  private workInstance?: AIInstance;
47
47
 
@@ -100,7 +100,7 @@ export class PlayManager {
100
100
  }
101
101
 
102
102
  async enter(
103
- groupId: number,
103
+ groupId: string,
104
104
  serverId: string,
105
105
  opts: { debug?: boolean } = {},
106
106
  ): Promise<PlayEnterResult> {
@@ -180,7 +180,7 @@ export class PlayManager {
180
180
  return { success: true, serverId: server.id, message: `已进入 ${server.name}${mode}` };
181
181
  }
182
182
 
183
- async exit(groupId: number): Promise<PlayExitResult> {
183
+ async exit(groupId: string): Promise<PlayExitResult> {
184
184
  const session = this.sessions.get(groupId);
185
185
  if (!session || session.isStopped) {
186
186
  return { success: false, message: "当前没有进行中的 mc 会话" };
@@ -191,8 +191,8 @@ export class PlayManager {
191
191
  }
192
192
 
193
193
  onQqMessage(event: any): void {
194
- const groupId = Number(event?.group_id);
195
- if (!Number.isFinite(groupId) || groupId <= 0) return;
194
+ const groupId = String(event?.group_id ?? "").trim();
195
+ if (!groupId) return;
196
196
  const session = this.sessions.get(groupId);
197
197
  if (!session || session.isStopped) return;
198
198
  const text = String(event?.raw_message ?? event?.message ?? "").trim();
@@ -206,7 +206,7 @@ export class PlayManager {
206
206
  return [...this.sessions.values()].map((s) => s.getStatus());
207
207
  }
208
208
 
209
- getActiveSession(groupId: number): PlaySession | undefined {
209
+ getActiveSession(groupId: string): PlaySession | undefined {
210
210
  const s = this.sessions.get(groupId);
211
211
  return s && !s.isStopped ? s : undefined;
212
212
  }
@@ -231,7 +231,7 @@ export function createPlayManager(opts: PlayManagerOptions): PlayManager {
231
231
  function detectQqAtBot(
232
232
  event: any,
233
233
  botName: string,
234
- botSelfId: number | undefined,
234
+ botSelfId: string | undefined,
235
235
  ): boolean {
236
236
  const lower = String(event?.raw_message ?? event?.message ?? "").toLowerCase();
237
237
  if (botName && lower.includes(botName.toLowerCase())) return true;
@@ -239,8 +239,8 @@ function detectQqAtBot(
239
239
  for (const segment of event.message) {
240
240
  if (!segment || typeof segment !== "object") continue;
241
241
  if (segment.type !== "at") continue;
242
- const qq = Number(segment.data?.qq ?? 0);
243
- if (qq > 0 && qq === botSelfId) return true;
242
+ const target = String(segment.data?.qq ?? segment.data?.target ?? "").trim();
243
+ if (target && target === botSelfId) return true;
244
244
  }
245
245
  }
246
246
  return false;
package/play/types.ts CHANGED
@@ -12,8 +12,8 @@ export interface PlayServerConfig {
12
12
  }
13
13
 
14
14
  export interface GroupBinding {
15
- groupId: number;
16
- botSelfId: number;
15
+ groupId: string;
16
+ botSelfId: string;
17
17
  allowedServerIds: string[];
18
18
  }
19
19
 
@@ -73,8 +73,8 @@ export type MainLoopTrigger =
73
73
  export interface PlaySessionStatus {
74
74
  serverId: string;
75
75
  serverName: string;
76
- groupId: number;
77
- botSelfId: number;
76
+ groupId: string;
77
+ botSelfId: string;
78
78
  startedAt: number;
79
79
  connected: boolean;
80
80
  currentBehavior: string | null;
@@ -182,6 +182,11 @@ function asStringList(value: unknown): string[] {
182
182
  return [];
183
183
  }
184
184
 
185
+ function asId(value: unknown, fallback = ""): string {
186
+ const text = String(value ?? "").trim();
187
+ return text.length > 0 ? text : fallback;
188
+ }
189
+
185
190
  function asNumber(value: unknown, fallback: number): number {
186
191
  const n = Number(value);
187
192
  return Number.isFinite(n) ? n : fallback;
@@ -212,8 +217,8 @@ function normalizeServer(raw: any): PlayServerConfig {
212
217
 
213
218
  function normalizeBinding(raw: any): GroupBinding {
214
219
  return {
215
- groupId: asNumber(raw?.groupId ?? raw?.group_id, 0),
216
- botSelfId: asNumber(raw?.botSelfId ?? raw?.bot_self_id, 0),
220
+ groupId: asId(raw?.groupId ?? raw?.group_id),
221
+ botSelfId: asId(raw?.botSelfId ?? raw?.bot_self_id),
217
222
  allowedServerIds: asStringList(raw?.allowedServerIds ?? raw?.allowed_server_ids),
218
223
  };
219
224
  }
@@ -225,7 +230,7 @@ export function normalizePlayConfig(raw: any): PlayConfig {
225
230
  const groups = Array.isArray(raw?.groups)
226
231
  ? raw?.groups
227
232
  .map(normalizeBinding)
228
- .filter((g: GroupBinding) => g.groupId > 0 && g.botSelfId > 0)
233
+ .filter((g: GroupBinding) => g.groupId.length > 0 && g.botSelfId.length > 0)
229
234
  : [];
230
235
  const perm: PlayToolPermission =
231
236
  raw?.toolPermission === "owner" ||
package/skills/mc.ts CHANGED
@@ -29,8 +29,8 @@ export function createMcSkill(playManager: PlayManager): AISkill {
29
29
  },
30
30
  handler: async (args: any, runtimeCtx?: any) => {
31
31
  const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
32
- const groupId = Number(event?.group_id);
33
- if (!Number.isFinite(groupId) || groupId <= 0) {
32
+ const groupId = String(event?.group_id ?? "").trim();
33
+ if (!groupId) {
34
34
  return { error: "无法识别当前群,请在群聊中调用" };
35
35
  }
36
36
 
@@ -1,51 +0,0 @@
1
- export interface ParsedCommand {
2
- action: string;
3
- args: string[];
4
- raw: string;
5
- }
6
-
7
- export function parseMcCommand(text: string): ParsedCommand | null {
8
- const trimmed = text.trim();
9
- if (!trimmed.startsWith("/mc")) return null;
10
-
11
- const remainder = trimmed.slice(3).trim();
12
- if (!remainder) {
13
- return { action: "", args: [], raw: "" };
14
- }
15
-
16
- // split into action and args
17
- const parts = remainder.split(/\s+/);
18
- const action = parts[0] || "";
19
- const args = parts.slice(1);
20
-
21
- return { action, args, raw: remainder };
22
- }
23
-
24
- export function matchMcCommand(
25
- text: string,
26
- pattern: RegExp
27
- ): RegExpMatchArray | null {
28
- const parsed = parseMcCommand(text);
29
- if (!parsed) return null;
30
- return parsed.raw.match(pattern);
31
- }
32
-
33
- export function buildStatusReply(
34
- serverName: string,
35
- status: string,
36
- displayName?: string
37
- ): string {
38
- const name = displayName || serverName;
39
- const icon = status === "connected" ? "在线" : status === "connecting" ? "连接中" : "离线";
40
- return `[${name}] ${icon}`;
41
- }
42
-
43
- export function buildSyncReply(
44
- serverName: string,
45
- enabled: boolean,
46
- displayName?: string
47
- ): string {
48
- const name = displayName || serverName;
49
- const action = enabled ? "开启" : "关闭";
50
- return `${action}了服务器 ${name} 的同步`;
51
- }