mioku-plugin-deerpipe 2.0.1 → 2.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
@@ -21,13 +21,13 @@
21
21
 
22
22
  | 指令 | 说明 | 权限 |
23
23
  |-------------------|---------------------|------|
24
- | `🦌` / `鹿` | 自🦌一次 | 全员 |
25
- | `🦌 @某人` | 帮某人🦌一次(仅群组) | 全员 |
26
- | `补🦌 <日>` | 补签本月某天 | 全员 |
27
- | `🦌历` / `🦌历 @某人` | 查看本月🦌日历 | 全员 |
28
- | `🦌榜` | 查看本月本群🦌排行榜(仅群组) | 全员 |
29
- | `帮🦌 on/off` | 允许/禁止别人帮自己🦌 | 全员 |
30
- | `帮🦌 on/off @某人` | 允许/禁止帮某人🦌 | 群管理员 |
31
- | `禁🦌 @某人 [时长]` | 禁止某人🦌一段时间,省略时长视为解禁 | 群管理员 |
24
+ | `.🦌` / `.鹿` | 自🦌一次 | 全员 |
25
+ | `.🦌 @某人` | 帮某人🦌一次(仅群组) | 全员 |
26
+ | `.补🦌 <日>` | 补签本月某天 | 全员 |
27
+ | `.🦌历` / `.🦌历 @某人` | 查看本月🦌日历 | 全员 |
28
+ | `.🦌榜` | 查看本月本群🦌排行榜(仅群组) | 全员 |
29
+ | `.帮🦌 on/off` | 允许/禁止别人帮自己🦌 | 全员 |
30
+ | `.帮🦌 on/off @某人` | 允许/禁止帮某人🦌 | 群管理员 |
31
+ | `.禁🦌 @某人 [时长]` | 禁止某人🦌一段时间,省略时长视为解禁 | 群管理员 |
32
32
 
33
33
  时长支持 `30s` / `5m` / `2h` / `1d` / `1w` 等写法,最长 30 天。
package/commands.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  getAtUserId,
8
8
  parseDuration,
9
9
  replyImage,
10
+ resolveAvatarUrl,
10
11
  resolveScene,
11
12
  resolveUserName,
12
13
  } from "./utils";
@@ -30,12 +31,12 @@ function isGroupAdmin(event: any): boolean {
30
31
  }
31
32
 
32
33
  export type DeerCommand =
33
- | { type: "deer"; targetUserId?: number }
34
+ | { type: "deer"; targetUserId?: string }
34
35
  | { type: "past"; day: number }
35
- | { type: "calendar"; targetUserId?: number }
36
+ | { type: "calendar"; targetUserId?: string }
36
37
  | { type: "rank" }
37
- | { type: "set_can_be_helped"; allowed: boolean; targetUserId?: number }
38
- | { type: "set_no_deer"; targetUserId: number; durationText?: string }
38
+ | { type: "set_can_be_helped"; allowed: boolean; targetUserId?: string }
39
+ | { type: "set_no_deer"; targetUserId: string; durationText?: string }
39
40
  | { type: "invalid_past" }
40
41
  | { type: "none" };
41
42
 
@@ -124,7 +125,7 @@ export async function handleDeerCommand(
124
125
 
125
126
  async function handleDeer(
126
127
  cmdCtx: CommandContext,
127
- targetUserId?: number,
128
+ targetUserId?: string,
128
129
  ): Promise<void> {
129
130
  const { ctx, db, screenshot, event } = cmdCtx;
130
131
  const scene = resolveScene(event);
@@ -136,7 +137,7 @@ async function handleDeer(
136
137
  }
137
138
 
138
139
  const userId =
139
- targetUserId != null ? targetUserId : Number(event.user_id);
140
+ targetUserId != null ? targetUserId : String(event.user_id ?? "").trim();
140
141
  const user = db.getOrCreateUser(scene.key, userId);
141
142
 
142
143
  if (targetUserId && !user.canBeHelped) {
@@ -173,10 +174,11 @@ async function handleDeer(
173
174
  records: result.records,
174
175
  name,
175
176
  userId,
177
+ avatar: resolveAvatarUrl(event, userId),
176
178
  });
177
179
 
178
180
  const prefix = targetUserId
179
- ? [{ type: "text", data: { text: "成功帮 " } }, ctx.segment.at(String(targetUserId)), { type: "text", data: { text: " 🦌了\n" } }]
181
+ ? [{ type: "text", data: { text: "成功帮 " } }, ctx.segment.at(targetUserId), { type: "text", data: { text: " 🦌了\n" } }]
180
182
  : [{ type: "text", data: { text: "成功🦌了\n" } }];
181
183
 
182
184
  await replyImage(event, ctx.segment, imagePath, prefix);
@@ -195,7 +197,7 @@ async function handlePast(
195
197
  return;
196
198
  }
197
199
 
198
- const userId = Number(event.user_id);
200
+ const userId = String(event.user_id ?? "").trim();
199
201
  db.getOrCreateUser(scene.key, userId);
200
202
 
201
203
  const result = await db.checkIn(
@@ -215,6 +217,7 @@ async function handlePast(
215
217
  records: result.records,
216
218
  name,
217
219
  userId,
220
+ avatar: resolveAvatarUrl(event, userId),
218
221
  });
219
222
 
220
223
  const text = result.ok ? "成功补🦌\n" : "不能补🦌已经🦌过的日子捏\n";
@@ -228,14 +231,14 @@ async function handlePast(
228
231
 
229
232
  async function handleCalendar(
230
233
  cmdCtx: CommandContext,
231
- targetUserId?: number,
234
+ targetUserId?: string,
232
235
  ): Promise<void> {
233
236
  const { ctx, db, screenshot, event } = cmdCtx;
234
237
  const scene = resolveScene(event);
235
238
  if (targetUserId && !scene.isGroup) return;
236
239
 
237
240
  const now = new Date();
238
- const userId = targetUserId ?? Number(event.user_id);
241
+ const userId = targetUserId ?? String(event.user_id ?? "").trim();
239
242
  db.getOrCreateUser(scene.key, userId);
240
243
  const records = db.getRecords(
241
244
  scene.key,
@@ -252,6 +255,7 @@ async function handleCalendar(
252
255
  records,
253
256
  name,
254
257
  userId,
258
+ avatar: resolveAvatarUrl(event, userId),
255
259
  });
256
260
 
257
261
  await replyImage(event, ctx.segment, imagePath);
@@ -275,6 +279,7 @@ async function handleRank(cmdCtx: CommandContext): Promise<void> {
275
279
  rank: idx + 1,
276
280
  userId: entry.userId,
277
281
  name: await resolveUserName(ctx, event, entry.userId),
282
+ avatar: resolveAvatarUrl(event, entry.userId),
278
283
  count: entry.count,
279
284
  })),
280
285
  );
@@ -292,7 +297,7 @@ async function handleRank(cmdCtx: CommandContext): Promise<void> {
292
297
  async function handleSetCanBeHelped(
293
298
  cmdCtx: CommandContext,
294
299
  allowed: boolean,
295
- targetUserId?: number,
300
+ targetUserId?: string,
296
301
  ): Promise<void> {
297
302
  const { ctx, db, event } = cmdCtx;
298
303
  const scene = resolveScene(event);
@@ -303,7 +308,7 @@ async function handleSetCanBeHelped(
303
308
  return;
304
309
  }
305
310
 
306
- const userId = targetUserId ?? Number(event.user_id);
311
+ const userId = targetUserId ?? String(event.user_id ?? "").trim();
307
312
  const user = db.getOrCreateUser(scene.key, userId);
308
313
  user.canBeHelped = allowed;
309
314
  await db.updateUser(user);
@@ -312,7 +317,7 @@ async function handleSetCanBeHelped(
312
317
  await event.reply(
313
318
  [
314
319
  { type: "text", data: { text: `已${allowed ? "允许" : "禁止"}帮 ` } },
315
- ctx.segment.at(String(targetUserId)),
320
+ ctx.segment.at(targetUserId),
316
321
  { type: "text", data: { text: " 🦌" } },
317
322
  ],
318
323
  true,
@@ -324,7 +329,7 @@ async function handleSetCanBeHelped(
324
329
 
325
330
  async function handleSetNoDeer(
326
331
  cmdCtx: CommandContext,
327
- targetUserId: number,
332
+ targetUserId: string,
328
333
  durationText?: string,
329
334
  ): Promise<void> {
330
335
  const { ctx, db, event } = cmdCtx;
@@ -361,7 +366,7 @@ async function handleSetNoDeer(
361
366
  await event.reply(
362
367
  [
363
368
  { type: "text", data: { text: "已解禁 " } },
364
- ctx.segment.at(String(targetUserId)),
369
+ ctx.segment.at(targetUserId),
365
370
  { type: "text", data: { text: " 的🦌权" } },
366
371
  ],
367
372
  true,
@@ -370,7 +375,7 @@ async function handleSetNoDeer(
370
375
  await event.reply(
371
376
  [
372
377
  { type: "text", data: { text: "已禁止 " } },
373
- ctx.segment.at(String(targetUserId)),
378
+ ctx.segment.at(targetUserId),
374
379
  {
375
380
  type: "text",
376
381
  data: {
package/db.ts CHANGED
@@ -18,17 +18,17 @@ interface RecordRow {
18
18
  }
19
19
 
20
20
  export interface DeerDatabase {
21
- getOrCreateUser(scene: string, userId: number): DeerUser;
21
+ getOrCreateUser(scene: string, userId: string): DeerUser;
22
22
  updateUser(user: DeerUser): Promise<void>;
23
23
  getRecords(
24
24
  scene: string,
25
- userId: number,
25
+ userId: string,
26
26
  year: number,
27
27
  month: number,
28
28
  ): Map<number, number>;
29
29
  checkIn(
30
30
  scene: string,
31
- userId: number,
31
+ userId: string,
32
32
  year: number,
33
33
  month: number,
34
34
  day: number,
@@ -44,7 +44,7 @@ export interface DeerDatabase {
44
44
  close(): void;
45
45
  }
46
46
 
47
- function userKey(scene: string, userId: number): string {
47
+ function userKey(scene: string, userId: string): string {
48
48
  return `${scene}:${userId}`;
49
49
  }
50
50
 
@@ -125,7 +125,7 @@ export async function initDeerDatabase(): Promise<DeerDatabase> {
125
125
 
126
126
  function loadRecords(
127
127
  scene: string,
128
- userId: number,
128
+ userId: string,
129
129
  year: number,
130
130
  month: number,
131
131
  ): Map<number, number> {
@@ -214,9 +214,9 @@ export async function initDeerDatabase(): Promise<DeerDatabase> {
214
214
  $scene: scene,
215
215
  $monthKey: monthKey(year, month),
216
216
  $limit: limit,
217
- }) as Array<{ user_id: number; total: number }>;
217
+ }) as Array<{ user_id: string; total: number }>;
218
218
  return rows.map((row) => ({
219
- userId: row.user_id,
219
+ userId: String(row.user_id),
220
220
  count: row.total,
221
221
  }));
222
222
  },
package/image.ts CHANGED
@@ -2,7 +2,7 @@ import * as fs from "fs/promises";
2
2
  import * as path from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import type { ScreenshotService } from "mioku";
5
- import { escapeHtml, getAvatarUrl, isNightMode } from "./utils";
5
+ import { escapeHtml, isNightMode } from "./utils";
6
6
 
7
7
  interface CalendarOptions {
8
8
  year: number;
@@ -10,13 +10,15 @@ interface CalendarOptions {
10
10
  todayDay: number;
11
11
  records: Map<number, number>;
12
12
  name: string;
13
- userId: number;
13
+ userId: string;
14
+ avatar?: string;
14
15
  }
15
16
 
16
17
  interface RankRow {
17
18
  rank: number;
18
19
  name: string;
19
- userId: number;
20
+ userId: string;
21
+ avatar?: string;
20
22
  count: number;
21
23
  }
22
24
 
@@ -127,7 +129,7 @@ export async function generateCalendarImage(
127
129
  screenshotService: ScreenshotService,
128
130
  options: CalendarOptions,
129
131
  ): Promise<string> {
130
- const { year, month, records, name, userId } = options;
132
+ const { year, month, records, name, avatar } = options;
131
133
  const assets = await loadAssets();
132
134
  // 日历始终使用白天主题(按用户要求 🦌 / 🦌历 / 补🦌 不适配夜间模式)
133
135
  const theme = DAY_THEME;
@@ -237,7 +239,7 @@ export async function generateCalendarImage(
237
239
  </head>
238
240
  <body>
239
241
  <div class="canvas">
240
- <img class="avatar" src="${escapeHtml(getAvatarUrl(userId))}" onerror="this.onerror=null;this.src='${assets.defaultAvatar}'" />
242
+ <img class="avatar" src="${escapeHtml(String(avatar ?? "").trim() || assets.defaultAvatar)}" onerror="this.onerror=null;this.src='${assets.defaultAvatar}'" />
241
243
  <div class="title">${year}-${String(month).padStart(2, "0")} 🦌签到日历</div>
242
244
  <div class="subtitle">@${escapeHtml(name)}</div>
243
245
  ${cellsHtml}
@@ -271,7 +273,7 @@ export async function generateRankImage(
271
273
  .map((row, idx) => {
272
274
  const y = headerHeight + idx * rowHeight;
273
275
  return `
274
- <img class="rank-avatar" src="${escapeHtml(getAvatarUrl(row.userId))}" onerror="this.onerror=null;this.src='${assets.defaultAvatar}'" style="top:${y + 10}px" />
276
+ <img class="rank-avatar" src="${escapeHtml(String(row.avatar ?? "").trim() || assets.defaultAvatar)}" onerror="this.onerror=null;this.src='${assets.defaultAvatar}'" style="top:${y + 10}px" />
275
277
  <div class="rank-name" style="top:${y + 10}px">@${escapeHtml(row.name)}</div>
276
278
  <div class="rank-count" style="top:${y + 50}px">x${row.count}</div>
277
279
  `;
package/index.ts CHANGED
@@ -1,12 +1,15 @@
1
- import { definePlugin, type MiokuContext } from "mioku";
1
+ import {
2
+ definePlugin,
3
+ type CommandDefinition,
4
+ type MessageEvent,
5
+ type MiokuContext,
6
+ } from "mioku";
2
7
  import { getService, Services } from "mioku";
3
8
  import { initDeerDatabase, type DeerDatabase } from "./db";
4
9
  import { handleDeerCommand, parseDeerCommand } from "./commands";
5
10
 
6
11
  const deerpipePlugin = definePlugin({
7
12
  name: "deerpipe",
8
- version: "1.0.0",
9
- description: "🦌管签到插件,支持自🦌、帮🦌、补🦌、🦌历、🦌榜",
10
13
 
11
14
  async setup(ctx: MiokuContext) {
12
15
  ctx.logger.info("deerpipe 插件正在初始化...");
@@ -30,7 +33,6 @@ const deerpipePlugin = definePlugin({
30
33
  };
31
34
  }
32
35
 
33
- // 启动时清掉非本月的旧数据,与原版 cleanup 行为一致
34
36
  try {
35
37
  const now = new Date();
36
38
  await db.cleanupOtherMonths(now.getFullYear(), now.getMonth() + 1);
@@ -38,7 +40,6 @@ const deerpipePlugin = definePlugin({
38
40
  ctx.logger.warn(`deerpipe 启动期数据清理失败: ${error}`);
39
41
  }
40
42
 
41
- // 每周一 4:00 清理跨月数据
42
43
  ctx.cron("0 4 * * 1", async () => {
43
44
  try {
44
45
  const now = new Date();
@@ -49,13 +50,9 @@ const deerpipePlugin = definePlugin({
49
50
  }
50
51
  });
51
52
 
52
- ctx.handle("message", async (event) => {
53
- const text = ctx.text(event);
54
- if (!text) return;
55
-
53
+ const run = async (event: MessageEvent, text: string) => {
56
54
  const cmd = parseDeerCommand(text, Array.from(event.message ?? []));
57
55
  if (cmd.type === "none") return;
58
-
59
56
  try {
60
57
  await handleDeerCommand(cmd, {
61
58
  ctx,
@@ -71,6 +68,52 @@ const deerpipePlugin = definePlugin({
71
68
  // 忽略二次失败
72
69
  }
73
70
  }
71
+ };
72
+
73
+ const cmd = (command: CommandDefinition) =>
74
+ ctx.command({ ...command, prefixes: false });
75
+
76
+ cmd({
77
+ name: "🦌",
78
+ match: /^(?:🦌|鹿)(?:\s|$|@)/,
79
+ description: "签到一次(可加 @某人 帮其签到)",
80
+ usage: ".🦌 / .🦌 @张三",
81
+ handler: ({ event, body }) => run(event, body),
82
+ });
83
+ cmd({
84
+ name: "补🦌",
85
+ match: /^补(?:🦌|鹿)(?:\s|\d|$)/,
86
+ description: "补签本月之前未签到的某天",
87
+ usage: ".补🦌 5",
88
+ handler: ({ event, body }) => run(event, body),
89
+ });
90
+ cmd({
91
+ name: "🦌历",
92
+ match: /^(?:🦌|鹿)历(?:\s|$|@)/,
93
+ description: "查看本月🦌签到日历(可加 @某人)",
94
+ usage: ".🦌历 / .🦌历 @张三",
95
+ handler: ({ event, body }) => run(event, body),
96
+ });
97
+ cmd({
98
+ name: "🦌榜",
99
+ match: /^(?:🦌|鹿)榜(?:\s|$)/,
100
+ description: "查看本月本群🦌签到排行榜(仅群组)",
101
+ usage: ".🦌榜",
102
+ handler: ({ event, body }) => run(event, body),
103
+ });
104
+ cmd({
105
+ name: "帮🦌",
106
+ match: /^帮(?:🦌|鹿)(?:\s|$|@)/,
107
+ description: "允许/禁止被别人帮🦌;@某人需管理员",
108
+ usage: ".帮🦌 off",
109
+ handler: ({ event, body }) => run(event, body),
110
+ });
111
+ cmd({
112
+ name: "禁🦌",
113
+ match: /^禁(?:🦌|鹿)(?:\s|@|$)/,
114
+ description: "禁止某人在一段时间内🦌,省略时长视为解禁(仅管理员)",
115
+ usage: ".禁🦌 @张三 1d",
116
+ handler: ({ event, body }) => run(event, body),
74
117
  });
75
118
 
76
119
  ctx.logger.info("deerpipe 插件初始化完成");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-deerpipe",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "🦌管签到插件,支持自🦌、帮🦌、补🦌、🦌历、🦌榜",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -15,81 +15,7 @@
15
15
  "services": [
16
16
  "screenshot",
17
17
  "help"
18
- ],
19
- "accessHooks": [
20
- {
21
- "id": "🦌",
22
- "match": "/^(?:🦌|鹿)(?:\\s|$|@)/",
23
- "description": "自🦌或帮🦌"
24
- },
25
- {
26
- "id": "补🦌",
27
- "match": "/^补(?:🦌|鹿)(?:\\s|\\d|$)/",
28
- "description": "补签本月之前未签到的某天"
29
- },
30
- {
31
- "id": "🦌历",
32
- "match": "/^(?:🦌|鹿)历(?:\\s|$|@)/",
33
- "description": "查看本月🦌签到日历"
34
- },
35
- {
36
- "id": "🦌榜",
37
- "match": "/^(?:🦌|鹿)榜(?:\\s|$)/",
38
- "description": "查看本月🦌签到排行榜"
39
- },
40
- {
41
- "id": "帮🦌",
42
- "match": "/^帮(?:🦌|鹿)(?:\\s|$|@)/",
43
- "description": "允许/禁止被别人帮🦌"
44
- },
45
- {
46
- "id": "禁🦌",
47
- "match": "/^禁(?:🦌|鹿)(?:\\s|@|$)/",
48
- "description": "禁止某人在一段时间内🦌"
49
- }
50
- ],
51
- "help": {
52
- "title": "🦌管签到",
53
- "description": "每月🦌管签到,含日历与排行榜",
54
- "commands": [
55
- {
56
- "cmd": "🦌",
57
- "desc": "签到一次(可加 @某人 帮其签到)",
58
- "usage": "🦌 / 🦌 @张三",
59
- "role": "member"
60
- },
61
- {
62
- "cmd": "补🦌 <日>",
63
- "desc": "补签本月之前未签到的某天",
64
- "usage": "补🦌 5",
65
- "role": "member"
66
- },
67
- {
68
- "cmd": "🦌历",
69
- "desc": "查看本月🦌签到日历(可加 @某人)",
70
- "usage": "🦌历 / 🦌历 @张三",
71
- "role": "member"
72
- },
73
- {
74
- "cmd": "🦌榜",
75
- "desc": "查看本月本群🦌签到排行榜(仅群组)",
76
- "usage": "🦌榜",
77
- "role": "member"
78
- },
79
- {
80
- "cmd": "帮🦌 <on|off> [@某人]",
81
- "desc": "允许/禁止被别人帮🦌;@某人需管理员",
82
- "usage": "帮🦌 off",
83
- "role": "member"
84
- },
85
- {
86
- "cmd": "禁🦌 @某人 [时长]",
87
- "desc": "禁止某人在一段时间内🦌,省略时长视为解禁(仅管理员)",
88
- "usage": "禁🦌 @张三 1d",
89
- "role": "admin"
90
- }
91
- ]
92
- }
18
+ ]
93
19
  },
94
20
  "peerDependencies": {
95
21
  "mioku": "^1.0.0"
package/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export interface DeerUser {
2
2
  scene: string;
3
- userId: number;
3
+ userId: string;
4
4
  canBeHelped: boolean;
5
5
  noDeerUntil: number | null;
6
6
  }
@@ -11,13 +11,13 @@ export interface DeerCheckInResult {
11
11
  }
12
12
 
13
13
  export interface DeerRankEntry {
14
- userId: number;
14
+ userId: string;
15
15
  count: number;
16
16
  }
17
17
 
18
18
  export interface DeerScene {
19
19
  key: string;
20
20
  isGroup: boolean;
21
- groupId?: number;
22
- privateUserId?: number;
21
+ groupId?: string;
22
+ privateUserId?: string;
23
23
  }
package/utils.ts CHANGED
@@ -2,18 +2,41 @@ import { createGroupRef, type MiokuContext } from "mioku";
2
2
  import * as fs from "fs/promises";
3
3
  import type { DeerScene } from "./types";
4
4
 
5
- export function getAvatarUrl(userId: number): string {
6
- return `https://q1.qlogo.cn/g?b=qq&nk=${userId}&s=640`;
5
+ const QQ_AVATAR_ADAPTERS = new Set(["onebotv11", "icqq"]);
6
+ const QQ_ID_RE = /^\d{4,12}$/;
7
+
8
+ /**
9
+ * 生成头像地址。仅在 QQ 系适配器且 id 是 QQ 号时才回退到 qlogo,
10
+ * 其他平台(如 qq-official 的 openid)没有通用头像接口,返回空串由调用方降级。
11
+ */
12
+ export function getAvatarUrl(
13
+ userId: string,
14
+ options?: { avatar?: unknown; adapter?: unknown },
15
+ ): string {
16
+ const direct = String(options?.avatar ?? "").trim();
17
+ if (direct) return direct;
18
+ const id = String(userId ?? "").trim();
19
+ const adapter = String(options?.adapter ?? "").trim().toLowerCase();
20
+ if (adapter && !QQ_AVATAR_ADAPTERS.has(adapter)) return "";
21
+ return QQ_ID_RE.test(id) ? `https://q1.qlogo.cn/g?b=qq&nk=${id}&s=640` : "";
22
+ }
23
+
24
+ export function resolveAvatarUrl(event: any, userId: string): string {
25
+ const id = String(userId ?? "").trim();
26
+ const isSelf = id !== "" && id === String(event?.user_id ?? "").trim();
27
+ const avatar = isSelf ? event?.sender?.avatar ?? event?.avatar : undefined;
28
+ return getAvatarUrl(id, { avatar, adapter: event?.bot?.adapter });
7
29
  }
8
30
 
9
- export function getAtUserId(message: any[]): number | undefined {
31
+ export function getAtUserId(message: any[]): string | undefined {
10
32
  if (!Array.isArray(message)) return undefined;
11
33
  for (const seg of message) {
12
34
  if (seg?.type !== "at") continue;
13
- const raw = seg?.qq ?? seg?.data?.qq;
14
- if (raw === "all" || raw == null) continue;
15
- const qq = Number(raw);
16
- if (Number.isFinite(qq)) return qq;
35
+ const raw = seg?.data?.qq ?? seg?.qq ?? seg?.data?.target ?? seg?.target;
36
+ if (raw == null) continue;
37
+ const id = String(raw).trim();
38
+ if (!id || id === "all") continue;
39
+ return id;
17
40
  }
18
41
  return undefined;
19
42
  }
@@ -21,22 +44,22 @@ export function getAtUserId(message: any[]): number | undefined {
21
44
  export function resolveScene(event: any): DeerScene {
22
45
  if (event?.message_type === "group" && event?.group_id != null) {
23
46
  return {
24
- key: `g:${event.group_id}`,
47
+ key: `g:${String(event.group_id)}`,
25
48
  isGroup: true,
26
- groupId: Number(event.group_id),
49
+ groupId: String(event.group_id),
27
50
  };
28
51
  }
29
52
  return {
30
- key: `p:${event.user_id}`,
53
+ key: `p:${String(event.user_id ?? "")}`,
31
54
  isGroup: false,
32
- privateUserId: Number(event.user_id),
55
+ privateUserId: String(event?.user_id ?? ""),
33
56
  };
34
57
  }
35
58
 
36
59
  export async function resolveUserName(
37
60
  ctx: MiokuContext,
38
61
  event: any,
39
- userId: number,
62
+ userId: string,
40
63
  ): Promise<string> {
41
64
  const bot = event?.bot;
42
65
  if (event?.message_type === "group" && event?.group_id != null && bot) {
@@ -50,7 +73,7 @@ export async function resolveUserName(
50
73
  // fall through
51
74
  }
52
75
  }
53
- if (Number(event?.user_id) === userId && event?.sender) {
76
+ if (String(event?.user_id ?? "") === userId && event?.sender) {
54
77
  const name =
55
78
  String(event.sender?.card || "").trim() ||
56
79
  String(event.sender?.nickname || "").trim();