mioku-plugin-music 3.0.1 → 3.0.3

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
@@ -2,10 +2,10 @@
2
2
 
3
3
  ## 功能
4
4
 
5
- - 点歌搜索:`点歌 晴天`
5
+ - 点歌搜索:`.点歌 晴天`
6
6
  - 搜索结果截图列表
7
- - 编号听歌(语音):`听1`
8
- - 编号听歌(原曲文件,群聊也直接发文件):`原曲1`
7
+ - 编号听歌(语音):`.听1`
8
+ - 编号听歌(原曲文件,群聊也直接发文件):`.原曲1`
9
9
  - AI skills:搜索、发送歌曲
10
10
 
11
11
  ## 已适配 Provider
package/index.ts CHANGED
@@ -13,8 +13,6 @@ function cloneConfig<T>(value: T): T {
13
13
 
14
14
  export default definePlugin({
15
15
  name: "music",
16
- version: "1.0.0",
17
- description: "点歌与听歌插件",
18
16
  async setup(ctx) {
19
17
  const configService = getService(ctx, Services.Config);
20
18
  const aiService = getService(ctx, Services.AI);
@@ -56,8 +54,40 @@ export default definePlugin({
56
54
  );
57
55
  }
58
56
 
59
- ctx.handle("message", async (event) => {
60
- await runtime.handleMessage(ctx, event);
57
+ const react = (event: any) => runtime.tryReactToCommandMessage(ctx, event);
58
+
59
+ ctx.command({
60
+ prefixes: false,
61
+ name: "点歌",
62
+ match: /^点歌\s*(.+)$/,
63
+ description: "搜索歌曲/歌手/专辑,返回最多 15 条结果图片列表",
64
+ usage: ".点歌 晴天",
65
+ handler: async ({ event, match }) => {
66
+ await react(event);
67
+ await runtime.searchAndSendList(ctx, event, match![1].trim());
68
+ },
69
+ });
70
+ ctx.command({
71
+ prefixes: false,
72
+ name: "听",
73
+ match: /^听\s*(\d{1,2})$/,
74
+ description: "发送上次搜索列表中的指定歌曲语音",
75
+ usage: ".听1",
76
+ handler: async ({ event, match }) => {
77
+ await react(event);
78
+ await runtime.sendByIndex(ctx, event, Number(match![1]), false);
79
+ },
80
+ });
81
+ ctx.command({
82
+ prefixes: false,
83
+ name: "原曲",
84
+ match: /^原曲\s*(\d{1,2})$/,
85
+ description: "发送上次搜索列表中的指定歌曲原曲文件",
86
+ usage: ".原曲1",
87
+ handler: async ({ event, match }) => {
88
+ await react(event);
89
+ await runtime.sendByIndex(ctx, event, Number(match![1]), true);
90
+ },
61
91
  });
62
92
 
63
93
  return () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-music",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "音乐插件:点歌、搜索音乐、听歌语音发送",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -16,45 +16,7 @@
16
16
  "config",
17
17
  "ai",
18
18
  "screenshot"
19
- ],
20
- "accessHooks": [
21
- {
22
- "id": "点歌",
23
- "match": "/^\\/?点歌\\s+/"
24
- },
25
- {
26
- "id": "听",
27
- "match": "/^听\\d+$/"
28
- },
29
- {
30
- "id": "原曲",
31
- "match": "/^原曲\\d+$/"
32
- }
33
- ],
34
- "help": {
35
- "title": "音乐",
36
- "description": "点歌并发送语音,支持搜索结果图片列表",
37
- "commands": [
38
- {
39
- "cmd": "点歌 <歌曲名>",
40
- "desc": "搜索歌曲/歌手/专辑,返回最多 15 条结果图片列表",
41
- "usage": "/点歌 晴天",
42
- "role": "member"
43
- },
44
- {
45
- "cmd": "听 <编号>",
46
- "desc": "发送上次搜索列表中的指定歌曲语音",
47
- "usage": "听1",
48
- "role": "member"
49
- },
50
- {
51
- "cmd": "原曲 <编号>",
52
- "desc": "发送上次搜索列表中的指定歌曲原曲文件",
53
- "usage": "原曲1",
54
- "role": "member"
55
- }
56
- ]
57
- }
19
+ ]
58
20
  },
59
21
  "peerDependencies": {
60
22
  "mioku": "^1.0.0",
@@ -26,7 +26,8 @@ export async function notifyFallback(options: {
26
26
  }
27
27
 
28
28
  const text = options.ctx.text(options.event)?.trim() ?? "";
29
- if (!text.startsWith("/")) {
29
+ // 只有像命令的消息才触发 AI 兜底提示(命令支持裸写与 . 前缀)
30
+ if (!/^[./]/.test(text)) {
30
31
  return;
31
32
  }
32
33
 
@@ -22,15 +22,17 @@ function normalizeFileSource(file: string): string {
22
22
 
23
23
  function getBotAndTarget(ctx: any, event: any): {
24
24
  bot: any;
25
- groupId?: number;
26
- userId?: number;
25
+ groupId?: string;
26
+ userId?: string;
27
27
  } {
28
28
  const bot = event?.bot;
29
+ const groupId = String(event?.group_id ?? "").trim();
30
+ const userId = String(event?.user_id ?? "").trim();
29
31
 
30
32
  return {
31
33
  bot,
32
- groupId: event?.message_type === "group" ? event.group_id : undefined,
33
- userId: event?.message_type !== "group" ? event?.user_id : undefined,
34
+ groupId: event?.message_type === "group" ? groupId : undefined,
35
+ userId: event?.message_type !== "group" ? userId : undefined,
34
36
  };
35
37
  }
36
38
 
@@ -44,11 +46,11 @@ export async function sendTextMessage(
44
46
 
45
47
  payload.push(ctx.segment.text(text));
46
48
 
47
- if (bot && groupId != null) {
49
+ if (bot && groupId != null && groupId !== "") {
48
50
  await bot.sendGroupMsg(groupId, payload);
49
51
  return;
50
52
  }
51
- if (bot && userId != null) {
53
+ if (bot && userId != null && userId !== "") {
52
54
  await bot.sendPrivateMsg(userId, payload);
53
55
  return;
54
56
  }
@@ -69,11 +71,11 @@ export async function sendImageMessage(
69
71
  const payload: any[] = [];
70
72
  payload.push(ctx.segment.image(normalizeFileSource(source)));
71
73
 
72
- if (bot && groupId != null) {
74
+ if (bot && groupId != null && groupId !== "") {
73
75
  await bot.sendGroupMsg(groupId, payload);
74
76
  return;
75
77
  }
76
- if (bot && userId != null) {
78
+ if (bot && userId != null && userId !== "") {
77
79
  await bot.sendPrivateMsg(userId, payload);
78
80
  return;
79
81
  }
@@ -106,11 +108,11 @@ export async function sendFileMessage(
106
108
  const payload: any[] = [];
107
109
  payload.push(ctx.segment.file(normalizeFileSource(source), { name: fileName }));
108
110
 
109
- if (bot && groupId != null) {
111
+ if (bot && groupId != null && groupId !== "") {
110
112
  await bot.sendGroupMsg(groupId, payload);
111
113
  return;
112
114
  }
113
- if (bot && userId != null) {
115
+ if (bot && userId != null && userId !== "") {
114
116
  await bot.sendPrivateMsg(userId, payload);
115
117
  return;
116
118
  }
@@ -174,11 +176,11 @@ export async function sendRecordMessage(
174
176
  const payload: any[] = [];
175
177
  payload.push(ctx.segment.record(normalizeFileSource(source)));
176
178
 
177
- if (bot && groupId != null) {
179
+ if (bot && groupId != null && groupId !== "") {
178
180
  await bot.sendGroupMsg(groupId, payload);
179
181
  return;
180
182
  }
181
- if (bot && userId != null) {
183
+ if (bot && userId != null && userId !== "") {
182
184
  await bot.sendPrivateMsg(userId, payload);
183
185
  return;
184
186
  }
@@ -32,39 +32,6 @@ interface MusicPluginRuntimeDeps {
32
32
  neteaseService?: NeteaseServiceApi;
33
33
  }
34
34
 
35
- function parseListenIndex(text: string): number | null {
36
- const match = text.match(/^\/?听\s*(\d{1,2})$/);
37
- if (!match) {
38
- return null;
39
- }
40
- const idx = Number(match[1]);
41
- if (!Number.isFinite(idx) || idx <= 0) {
42
- return null;
43
- }
44
- return idx;
45
- }
46
-
47
- function parseOriginalIndex(text: string): number | null {
48
- const match = text.match(/^\/?原曲\s*(\d{1,2})$/);
49
- if (!match) {
50
- return null;
51
- }
52
- const idx = Number(match[1]);
53
- if (!Number.isFinite(idx) || idx <= 0) {
54
- return null;
55
- }
56
- return idx;
57
- }
58
-
59
- function parseSearchKeyword(text: string): string | null {
60
- const match = text.match(/^\/?点歌\s*(.+)$/);
61
- if (!match) {
62
- return null;
63
- }
64
- const value = String(match[1] || "").trim();
65
- return value ? value : null;
66
- }
67
-
68
35
  export class MusicPluginRuntime {
69
36
  private readonly deps: MusicPluginRuntimeDeps;
70
37
  private readonly sessions = new MusicSessionStore();
@@ -110,36 +77,6 @@ export class MusicPluginRuntime {
110
77
  return this.sessions.get(event);
111
78
  }
112
79
 
113
- async handleMessage(ctx: any, event: any): Promise<boolean> {
114
- const text = String(ctx.text(event) || "").trim();
115
- if (!text) {
116
- return false;
117
- }
118
-
119
- const searchKeyword = parseSearchKeyword(text);
120
- if (searchKeyword) {
121
- await this.tryReactToCommandMessage(ctx, event);
122
- await this.searchAndSendList(ctx, event, searchKeyword);
123
- return true;
124
- }
125
-
126
- const listenIndex = parseListenIndex(text);
127
- if (listenIndex != null) {
128
- await this.tryReactToCommandMessage(ctx, event);
129
- await this.sendByIndex(ctx, event, listenIndex, false);
130
- return true;
131
- }
132
-
133
- const originalIndex = parseOriginalIndex(text);
134
- if (originalIndex != null) {
135
- await this.tryReactToCommandMessage(ctx, event);
136
- await this.sendByIndex(ctx, event, originalIndex, true);
137
- return true;
138
- }
139
-
140
- return false;
141
- }
142
-
143
80
  async searchSongs(event: any, query: string): Promise<MusicSearchResult> {
144
81
  const session = this.getOrCreateSession(event);
145
82
  const provider = await this.createProvider(
@@ -245,7 +182,7 @@ export class MusicPluginRuntime {
245
182
  await this.sendSongById(ctx, event, first.id, first.title, options);
246
183
  }
247
184
 
248
- private async searchAndSendList(
185
+ async searchAndSendList(
249
186
  ctx: any,
250
187
  event: any,
251
188
  keyword: string,
@@ -296,7 +233,7 @@ export class MusicPluginRuntime {
296
233
  }
297
234
  }
298
235
 
299
- private async sendByIndex(
236
+ async sendByIndex(
300
237
  ctx: any,
301
238
  event: any,
302
239
  index: number,
@@ -404,13 +341,13 @@ export class MusicPluginRuntime {
404
341
  };
405
342
  }
406
343
 
407
- private async tryReactToCommandMessage(ctx: any, event: any): Promise<void> {
344
+ async tryReactToCommandMessage(ctx: any, event: any): Promise<void> {
408
345
  if (event?.message_type === "group") {
409
346
  return;
410
347
  }
411
348
 
412
- const messageId = Number(event?.message_id);
413
- if (!Number.isFinite(messageId) || messageId <= 0) {
349
+ const messageId = String(event?.message_id ?? "").trim();
350
+ if (!messageId) {
414
351
  return;
415
352
  }
416
353