koishi-plugin-steam-info-check 1.0.6 → 1.0.8

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/dist/index.js CHANGED
@@ -12,11 +12,11 @@ const zh_CN_1 = __importDefault(require("./locales/zh-CN"));
12
12
  exports.name = 'steam-info';
13
13
  exports.inject = ['model', 'http', 'puppeteer', 'database'];
14
14
  exports.Config = koishi_1.Schema.object({
15
- steamApiKey: koishi_1.Schema.array(String).required().description('Steam API Key (supports multiple)'),
16
- proxy: koishi_1.Schema.string().description('Proxy URL (e.g., http://127.0.0.1:7890)'),
17
- steamRequestInterval: koishi_1.Schema.number().default(300).description('Polling interval in seconds'),
18
- steamBroadcastType: koishi_1.Schema.union(['all', 'part', 'none']).default('part').description('Broadcast type: all (list), part (gaming only), none (text only)'),
19
- steamDisableBroadcastOnStartup: koishi_1.Schema.boolean().default(false).description('Disable broadcast on startup'),
15
+ steamApiKey: koishi_1.Schema.array(String).required().description('Steam API Key(支持多个)'),
16
+ proxy: koishi_1.Schema.string().description('代理地址,例如 http://127.0.0.1:7890'),
17
+ steamRequestInterval: koishi_1.Schema.number().default(300).description('轮询间隔(秒)'),
18
+ steamBroadcastType: koishi_1.Schema.union(['all', 'part', 'none']).default('part').description('播报类型:all(全部图片列表)、part(仅开始游戏时图片)、none(仅文字)'),
19
+ steamDisableBroadcastOnStartup: koishi_1.Schema.boolean().default(false).description('启动时禁用首次播报(仅预热缓存)'),
20
20
  fonts: koishi_1.Schema.object({
21
21
  regular: koishi_1.Schema.string().default('fonts/MiSans-Regular.ttf'),
22
22
  light: koishi_1.Schema.string().default('fonts/MiSans-Light.ttf'),
@@ -37,6 +37,7 @@ exports.logger = new koishi_1.Logger('steam-info');
37
37
  function apply(ctx, config) {
38
38
  // Localization
39
39
  ctx.i18n.define('zh-CN', zh_CN_1.default);
40
+ ctx.i18n.define('zh', zh_CN_1.default);
40
41
  // Services
41
42
  ctx.plugin(service_1.SteamService, config);
42
43
  ctx.plugin(drawer_1.DrawService, config);
@@ -63,8 +64,8 @@ function apply(ctx, config) {
63
64
  });
64
65
  // Commands and scheduler depend on steam/drawer being ready
65
66
  ctx.using(['steam', 'drawer'], (ctx) => {
66
- ctx.command('steam', 'Steam Information');
67
- ctx.command('steam.bind <steamId:string>', 'Bind Steam ID', { authority: config.commandAuthority.bind })
67
+ ctx.command('steam', 'Steam 信息');
68
+ ctx.command('steam.bind <steamId:string>', '绑定 Steam ID', { authority: config.commandAuthority.bind })
68
69
  .alias('steambind', '绑定steam')
69
70
  .action(async ({ session }, steamId) => {
70
71
  if (!session)
@@ -74,6 +75,16 @@ function apply(ctx, config) {
74
75
  const targetId = await ctx.steam.getSteamId(steamId);
75
76
  if (!targetId)
76
77
  return session.text('.id_not_found');
78
+ // 检查是否已绑定
79
+ try {
80
+ const existing = await ctx.database.get('steam_bind', { userId: session.userId, channelId: session.channelId });
81
+ if (existing.length) {
82
+ return session.text('.already_bound');
83
+ }
84
+ }
85
+ catch (e) {
86
+ exports.logger.warn('检查已绑定状态失败:' + String(e));
87
+ }
77
88
  await ctx.database.upsert('steam_bind', [
78
89
  {
79
90
  userId: session.userId,
@@ -83,7 +94,7 @@ function apply(ctx, config) {
83
94
  ], ['userId', 'channelId']);
84
95
  return session.text('.bind_success', [targetId]);
85
96
  });
86
- ctx.command('steam.unbind', 'Unbind Steam ID', { authority: config.commandAuthority.unbind })
97
+ ctx.command('steam.unbind', '解绑 Steam ID', { authority: config.commandAuthority.unbind })
87
98
  .alias('steamunbind', '解绑steam')
88
99
  .action(async ({ session }) => {
89
100
  if (!session)
@@ -94,7 +105,7 @@ function apply(ctx, config) {
94
105
  });
95
106
  return result ? session.text('.unbind_success') : session.text('.not_bound');
96
107
  });
97
- ctx.command('steam.info [target:text]', 'View Steam Profile', { authority: config.commandAuthority.info })
108
+ ctx.command('steam.info [target:text]', '查看 Steam 资料', { authority: config.commandAuthority.info })
98
109
  .alias('steaminfo', 'steam信息')
99
110
  .action(async ({ session }, target) => {
100
111
  if (!session)
@@ -135,7 +146,7 @@ function apply(ctx, config) {
135
146
  return session.text('.error');
136
147
  }
137
148
  });
138
- ctx.command('steam.check', 'Check Friends Status', { authority: config.commandAuthority.check })
149
+ ctx.command('steam.check', '查看好友在线状态', { authority: config.commandAuthority.check })
139
150
  .alias('steamcheck', '查看steam', '查steam')
140
151
  .action(async ({ session }) => {
141
152
  if (!session)
@@ -163,7 +174,7 @@ function apply(ctx, config) {
163
174
  return session.text('.error');
164
175
  }
165
176
  });
166
- ctx.command('steam.enable', 'Enable Broadcast', { authority: config.commandAuthority.enable })
177
+ ctx.command('steam.enable', '启用播报', { authority: config.commandAuthority.enable })
167
178
  .alias('steamenable', '启用steam')
168
179
  .action(async ({ session }) => {
169
180
  if (!session)
@@ -176,7 +187,7 @@ function apply(ctx, config) {
176
187
  }]);
177
188
  return session.text('.enable_success');
178
189
  });
179
- ctx.command('steam.disable', 'Disable Broadcast', { authority: config.commandAuthority.disable })
190
+ ctx.command('steam.disable', '禁用播报', { authority: config.commandAuthority.disable })
180
191
  .alias('steamdisable', '禁用steam')
181
192
  .action(async ({ session }) => {
182
193
  if (!session)
@@ -189,7 +200,7 @@ function apply(ctx, config) {
189
200
  }]);
190
201
  return session.text('.disable_success');
191
202
  });
192
- ctx.command('steam.update [name:string] [avatar:image]', 'Update Group Info', { authority: config.commandAuthority.update })
203
+ ctx.command('steam.update [name:string] [avatar:image]', '更新群信息', { authority: config.commandAuthority.update })
193
204
  .alias('steamupdate', '更新群信息')
194
205
  .action(async ({ session }, name, avatar) => {
195
206
  if (!session)
@@ -211,7 +222,7 @@ function apply(ctx, config) {
211
222
  await ctx.database.upsert('steam_channel', [update]);
212
223
  return session.text('.update_success');
213
224
  });
214
- ctx.command('steam.nickname <nickname:string>', 'Set Steam Nickname', { authority: config.commandAuthority.nickname })
225
+ ctx.command('steam.nickname <nickname:string>', '设置 Steam 昵称', { authority: config.commandAuthority.nickname })
215
226
  .alias('steamnickname', 'steam昵称')
216
227
  .action(async ({ session }, nickname) => {
217
228
  if (!session)
@@ -244,6 +255,35 @@ async function ensureChannelMeta(ctx, session) {
244
255
  if (!name && session.event?.channel?.name) {
245
256
  name = session.event.channel.name;
246
257
  }
258
+ // OneBot 群名补充
259
+ if (!name && session.platform?.includes('onebot') && session.bot?.internal?.getGroupInfo) {
260
+ try {
261
+ const info = await session.bot.internal.getGroupInfo({ group_id: channelId });
262
+ // 支持多种返回结构:直接 group_name、data.group_name(napcat)、或嵌套情况
263
+ if (info) {
264
+ try {
265
+ if (info.group_name) {
266
+ name = info.group_name;
267
+ }
268
+ else if (info.data && info.data.group_name) {
269
+ name = info.data.group_name;
270
+ }
271
+ else if (info.data && info.data.group && info.data.group.group_name) {
272
+ name = info.data.group.group_name;
273
+ }
274
+ else {
275
+ exports.logger.warn('getGroupInfo 返回了未识别结构:' + JSON.stringify(info));
276
+ }
277
+ }
278
+ catch (e) {
279
+ exports.logger.warn('解析 getGroupInfo 返回值时出错:' + String(e));
280
+ }
281
+ }
282
+ }
283
+ catch (err) {
284
+ exports.logger.warn('getGroupInfo 调用失败:' + String(err));
285
+ }
286
+ }
247
287
  let avatar = current.avatar;
248
288
  if (!avatar) {
249
289
  try {
@@ -266,79 +306,92 @@ async function ensureChannelMeta(ctx, session) {
266
306
  }
267
307
  async function seedStatusCache(ctx) {
268
308
  const binds = await ctx.database.get('steam_bind', {});
309
+ exports.logger.debug && exports.logger.debug(`seedStatusCache: load binds count=${binds.length}`);
269
310
  if (!binds.length)
270
311
  return;
271
312
  const steamIds = [...new Set(binds.map(b => b.steamId))];
272
313
  const summaries = await ctx.steam.getPlayerSummaries(steamIds);
314
+ exports.logger.debug && exports.logger.debug(`seedStatusCache: fetched summaries=${summaries.length}`);
273
315
  for (const player of summaries) {
274
316
  statusCache.set(player.steamid, player);
275
317
  }
276
318
  }
277
319
  async function broadcast(ctx, config) {
278
- const channels = await ctx.database.get('steam_channel', { enable: true });
279
- if (channels.length === 0)
280
- return;
281
- const channelIds = channels.map(c => c.id);
282
- const binds = await ctx.database.get('steam_bind', { channelId: channelIds });
283
- if (binds.length === 0)
284
- return;
285
- const steamIds = [...new Set(binds.map(b => b.steamId))];
286
- const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds);
287
- const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]));
288
- for (const channel of channels) {
289
- const channelBinds = binds.filter(b => b.channelId === channel.id);
290
- const msgs = [];
291
- const startGamingPlayers = [];
292
- for (const bind of channelBinds) {
293
- const current = currentMap.get(bind.steamId);
294
- const old = statusCache.get(bind.steamId);
295
- if (!current)
296
- continue;
297
- if (!old)
298
- continue;
299
- const oldGame = old.gameextrainfo;
300
- const newGame = current.gameextrainfo;
301
- const name = bind.nickname || current.personaname;
302
- if (newGame && !oldGame) {
303
- msgs.push(`${name} 开始玩 ${newGame} 了`);
304
- startGamingPlayers.push({ ...current, nickname: bind.nickname });
305
- }
306
- else if (!newGame && oldGame) {
307
- msgs.push(`${name} 玩了 ${oldGame} 后不玩了`);
308
- }
309
- else if (newGame && oldGame && newGame !== oldGame) {
310
- msgs.push(`${name} 停止玩 ${oldGame},开始玩 ${newGame} 了`);
311
- }
312
- }
313
- if (msgs.length > 0) {
314
- const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined;
315
- const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0];
316
- if (!bot)
317
- continue;
318
- if (config.steamBroadcastType === 'none') {
319
- await bot.sendMessage(channel.id, msgs.join('\n'));
320
- }
321
- else if (config.steamBroadcastType === 'part') {
322
- if (startGamingPlayers.length > 0) {
323
- const images = await Promise.all(startGamingPlayers.map(p => ctx.drawer.drawStartGaming(p, p.nickname)));
324
- const combined = await ctx.drawer.concatImages(images);
325
- const img = combined ? (typeof combined === 'string' ? combined : koishi_1.h.image(combined, 'image/png')) : '';
326
- await bot.sendMessage(channel.id, msgs.join('\n') + img);
320
+ try {
321
+ const channels = await ctx.database.get('steam_channel', { enable: true });
322
+ exports.logger.debug && exports.logger.debug(`broadcast: enabled channels=${channels.length}`);
323
+ if (channels.length === 0)
324
+ return;
325
+ const channelIds = channels.map(c => c.id);
326
+ const binds = await ctx.database.get('steam_bind', { channelId: channelIds });
327
+ exports.logger.debug && exports.logger.debug(`broadcast: binds total=${binds.length}`);
328
+ if (binds.length === 0)
329
+ return;
330
+ const steamIds = [...new Set(binds.map(b => b.steamId))];
331
+ exports.logger.debug && exports.logger.debug(`broadcast: unique steamIds=${steamIds.length}`);
332
+ const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds);
333
+ exports.logger.debug && exports.logger.debug(`broadcast: fetched summaries=${currentSummaries.length}`);
334
+ const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]));
335
+ for (const channel of channels) {
336
+ exports.logger.debug && exports.logger.debug(`broadcast: channel=${channel.id} processing`);
337
+ const channelBinds = binds.filter(b => b.channelId === channel.id);
338
+ const msgs = [];
339
+ const startGamingPlayers = [];
340
+ for (const bind of channelBinds) {
341
+ const current = currentMap.get(bind.steamId);
342
+ const old = statusCache.get(bind.steamId);
343
+ if (!current)
344
+ continue;
345
+ if (!old)
346
+ continue;
347
+ const oldGame = old.gameextrainfo;
348
+ const newGame = current.gameextrainfo;
349
+ const name = bind.nickname || current.personaname;
350
+ if (newGame && !oldGame) {
351
+ msgs.push(`${name} 开始玩 ${newGame} 了`);
352
+ startGamingPlayers.push({ ...current, nickname: bind.nickname });
327
353
  }
328
- else {
329
- await bot.sendMessage(channel.id, msgs.join('\n'));
354
+ else if (!newGame && oldGame) {
355
+ msgs.push(`${name} 玩了 ${oldGame} 后不玩了`);
356
+ }
357
+ else if (newGame && oldGame && newGame !== oldGame) {
358
+ msgs.push(`${name} 停止玩 ${oldGame},开始玩 ${newGame} 了`);
330
359
  }
331
360
  }
332
- else if (config.steamBroadcastType === 'all') {
333
- const channelPlayers = channelBinds.map(b => currentMap.get(b.steamId)).filter(Boolean);
334
- const parentAvatar = channel.avatar ? Buffer.from(channel.avatar, 'base64') : await ctx.drawer.getDefaultAvatar();
335
- const image = await ctx.drawer.drawFriendsStatus(parentAvatar, channel.name || channel.id, channelPlayers, channelBinds);
336
- const img = image ? (typeof image === 'string' ? image : koishi_1.h.image(image, 'image/png')) : '';
337
- await bot.sendMessage(channel.id, msgs.join('\n') + img);
361
+ if (msgs.length > 0) {
362
+ exports.logger.debug && exports.logger.debug(`broadcast: channel=${channel.id} msgs=${msgs.length}`);
363
+ const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined;
364
+ const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0];
365
+ if (!bot)
366
+ continue;
367
+ if (config.steamBroadcastType === 'none') {
368
+ await bot.sendMessage(channel.id, msgs.join('\n'));
369
+ }
370
+ else if (config.steamBroadcastType === 'part') {
371
+ if (startGamingPlayers.length > 0) {
372
+ const images = await Promise.all(startGamingPlayers.map(p => ctx.drawer.drawStartGaming(p, p.nickname)));
373
+ const combined = await ctx.drawer.concatImages(images);
374
+ const img = combined ? (typeof combined === 'string' ? combined : koishi_1.h.image(combined, 'image/png')) : '';
375
+ await bot.sendMessage(channel.id, msgs.join('\n') + img);
376
+ }
377
+ else {
378
+ await bot.sendMessage(channel.id, msgs.join('\n'));
379
+ }
380
+ }
381
+ else if (config.steamBroadcastType === 'all') {
382
+ const channelPlayers = channelBinds.map(b => currentMap.get(b.steamId)).filter(Boolean);
383
+ const parentAvatar = channel.avatar ? Buffer.from(channel.avatar, 'base64') : await ctx.drawer.getDefaultAvatar();
384
+ const image = await ctx.drawer.drawFriendsStatus(parentAvatar, channel.name || channel.id, channelPlayers, channelBinds);
385
+ const img = image ? (typeof image === 'string' ? image : koishi_1.h.image(image, 'image/png')) : '';
386
+ await bot.sendMessage(channel.id, msgs.join('\n') + img);
387
+ }
338
388
  }
339
389
  }
390
+ for (const p of currentSummaries) {
391
+ statusCache.set(p.steamid, p);
392
+ }
340
393
  }
341
- for (const p of currentSummaries) {
342
- statusCache.set(p.steamid, p);
394
+ catch (err) {
395
+ exports.logger.warn('broadcast 发生异常:' + String(err));
343
396
  }
344
397
  }
@@ -3,10 +3,12 @@ declare const _default: {
3
3
  steam: {
4
4
  bind: {
5
5
  bind_success: string;
6
+ already_bound: string;
6
7
  invalid_id: string;
7
8
  id_not_found: string;
8
9
  error: string;
9
10
  messages: {
11
+ already_bound: string;
10
12
  invalid_id: string;
11
13
  id_not_found: string;
12
14
  bind_success: string;
@@ -5,10 +5,12 @@ exports.default = {
5
5
  steam: {
6
6
  bind: {
7
7
  bind_success: '绑定成功!Steam ID: {0}',
8
+ already_bound: '您已经绑定过帐号了!',
8
9
  invalid_id: '请输入有效的 Steam ID。',
9
10
  id_not_found: '无法找到该 Steam ID。',
10
11
  error: '发生错误。',
11
12
  messages: {
13
+ already_bound: '您已经绑定过帐号了!',
12
14
  invalid_id: '请输入有效的 Steam ID。',
13
15
  id_not_found: '无法找到该 Steam ID。',
14
16
  bind_success: '绑定成功!Steam ID: {0}',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-steam-info-check",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Steam friends status broadcast plugin for Koishi",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -31,11 +31,11 @@ export interface Config {
31
31
  }
32
32
 
33
33
  export const Config: Schema<Config> = Schema.object({
34
- steamApiKey: Schema.array(String).required().description('Steam API Key (supports multiple)'),
35
- proxy: Schema.string().description('Proxy URL (e.g., http://127.0.0.1:7890)'),
36
- steamRequestInterval: Schema.number().default(300).description('Polling interval in seconds'),
37
- steamBroadcastType: Schema.union(['all', 'part', 'none']).default('part').description('Broadcast type: all (list), part (gaming only), none (text only)'),
38
- steamDisableBroadcastOnStartup: Schema.boolean().default(false).description('Disable broadcast on startup'),
34
+ steamApiKey: Schema.array(String).required().description('Steam API Key(支持多个)'),
35
+ proxy: Schema.string().description('代理地址,例如 http://127.0.0.1:7890'),
36
+ steamRequestInterval: Schema.number().default(300).description('轮询间隔(秒)'),
37
+ steamBroadcastType: Schema.union(['all', 'part', 'none']).default('part').description('播报类型:all(全部图片列表)、part(仅开始游戏时图片)、none(仅文字)'),
38
+ steamDisableBroadcastOnStartup: Schema.boolean().default(false).description('启动时禁用首次播报(仅预热缓存)'),
39
39
  fonts: Schema.object({
40
40
  regular: Schema.string().default('fonts/MiSans-Regular.ttf'),
41
41
  light: Schema.string().default('fonts/MiSans-Light.ttf'),
@@ -65,6 +65,7 @@ declare module 'koishi' {
65
65
  export function apply(ctx: Context, config: Config) {
66
66
  // Localization
67
67
  ctx.i18n.define('zh-CN', zhCN)
68
+ ctx.i18n.define('zh', zhCN)
68
69
 
69
70
  // Services
70
71
  ctx.plugin(SteamService, config)
@@ -95,17 +96,27 @@ export function apply(ctx: Context, config: Config) {
95
96
 
96
97
  // Commands and scheduler depend on steam/drawer being ready
97
98
  ctx.using(['steam', 'drawer'], (ctx) => {
98
- ctx.command('steam', 'Steam Information')
99
+ ctx.command('steam', 'Steam 信息')
99
100
 
100
- ctx.command('steam.bind <steamId:string>', 'Bind Steam ID', { authority: config.commandAuthority.bind })
101
+ ctx.command('steam.bind <steamId:string>', '绑定 Steam ID', { authority: config.commandAuthority.bind })
101
102
  .alias('steambind', '绑定steam')
102
103
  .action(async ({ session }, steamId) => {
103
104
  if (!session) return
104
105
  if (!steamId || !/^\d+$/.test(steamId)) return session.text('.invalid_id')
105
-
106
+
106
107
  const targetId = await ctx.steam.getSteamId(steamId)
107
108
  if (!targetId) return session.text('.id_not_found')
108
109
 
110
+ // 检查是否已绑定
111
+ try {
112
+ const existing = await ctx.database.get('steam_bind', { userId: session.userId, channelId: session.channelId })
113
+ if (existing.length) {
114
+ return session.text('.already_bound')
115
+ }
116
+ } catch (e) {
117
+ logger.warn('检查已绑定状态失败:' + String(e))
118
+ }
119
+
109
120
  await ctx.database.upsert('steam_bind', [
110
121
  {
111
122
  userId: session.userId,
@@ -117,7 +128,7 @@ export function apply(ctx: Context, config: Config) {
117
128
  return session.text('.bind_success', [targetId])
118
129
  })
119
130
 
120
- ctx.command('steam.unbind', 'Unbind Steam ID', { authority: config.commandAuthority.unbind })
131
+ ctx.command('steam.unbind', '解绑 Steam ID', { authority: config.commandAuthority.unbind })
121
132
  .alias('steamunbind', '解绑steam')
122
133
  .action(async ({ session }) => {
123
134
  if (!session) return
@@ -128,7 +139,7 @@ export function apply(ctx: Context, config: Config) {
128
139
  return result ? session.text('.unbind_success') : session.text('.not_bound')
129
140
  })
130
141
 
131
- ctx.command('steam.info [target:text]', 'View Steam Profile', { authority: config.commandAuthority.info })
142
+ ctx.command('steam.info [target:text]', '查看 Steam 资料', { authority: config.commandAuthority.info })
132
143
  .alias('steaminfo', 'steam信息')
133
144
  .action(async ({ session }, target) => {
134
145
  if (!session) return
@@ -165,7 +176,7 @@ export function apply(ctx: Context, config: Config) {
165
176
  }
166
177
  })
167
178
 
168
- ctx.command('steam.check', 'Check Friends Status', { authority: config.commandAuthority.check })
179
+ ctx.command('steam.check', '查看好友在线状态', { authority: config.commandAuthority.check })
169
180
  .alias('steamcheck', '查看steam', '查steam')
170
181
  .action(async ({ session }) => {
171
182
  if (!session) return
@@ -193,7 +204,7 @@ export function apply(ctx: Context, config: Config) {
193
204
  }
194
205
  })
195
206
 
196
- ctx.command('steam.enable', 'Enable Broadcast', { authority: config.commandAuthority.enable })
207
+ ctx.command('steam.enable', '启用播报', { authority: config.commandAuthority.enable })
197
208
  .alias('steamenable', '启用steam')
198
209
  .action(async ({ session }) => {
199
210
  if (!session) return
@@ -206,7 +217,7 @@ export function apply(ctx: Context, config: Config) {
206
217
  return session.text('.enable_success')
207
218
  })
208
219
 
209
- ctx.command('steam.disable', 'Disable Broadcast', { authority: config.commandAuthority.disable })
220
+ ctx.command('steam.disable', '禁用播报', { authority: config.commandAuthority.disable })
210
221
  .alias('steamdisable', '禁用steam')
211
222
  .action(async ({ session }) => {
212
223
  if (!session) return
@@ -219,7 +230,7 @@ export function apply(ctx: Context, config: Config) {
219
230
  return session.text('.disable_success')
220
231
  })
221
232
 
222
- ctx.command('steam.update [name:string] [avatar:image]', 'Update Group Info', { authority: config.commandAuthority.update })
233
+ ctx.command('steam.update [name:string] [avatar:image]', '更新群信息', { authority: config.commandAuthority.update })
223
234
  .alias('steamupdate', '更新群信息')
224
235
  .action(async ({ session }, name, avatar) => {
225
236
  if (!session) return
@@ -242,7 +253,7 @@ export function apply(ctx: Context, config: Config) {
242
253
  return session.text('.update_success')
243
254
  })
244
255
 
245
- ctx.command('steam.nickname <nickname:string>', 'Set Steam Nickname', { authority: config.commandAuthority.nickname })
256
+ ctx.command('steam.nickname <nickname:string>', '设置 Steam 昵称', { authority: config.commandAuthority.nickname })
246
257
  .alias('steamnickname', 'steam昵称')
247
258
  .action(async ({ session }, nickname) => {
248
259
  if (!session) return
@@ -278,6 +289,30 @@ async function ensureChannelMeta(ctx: Context, session: Session) {
278
289
  if (!name && session.event?.channel?.name) {
279
290
  name = session.event.channel.name
280
291
  }
292
+ // OneBot 群名补充
293
+ if (!name && session.platform?.includes('onebot') && session.bot?.internal?.getGroupInfo) {
294
+ try {
295
+ const info = await session.bot.internal.getGroupInfo({ group_id: channelId })
296
+ // 支持多种返回结构:直接 group_name、data.group_name(napcat)、或嵌套情况
297
+ if (info) {
298
+ try {
299
+ if (info.group_name) {
300
+ name = info.group_name
301
+ } else if (info.data && info.data.group_name) {
302
+ name = info.data.group_name
303
+ } else if (info.data && info.data.group && info.data.group.group_name) {
304
+ name = info.data.group.group_name
305
+ } else {
306
+ logger.warn('getGroupInfo 返回了未识别结构:' + JSON.stringify(info))
307
+ }
308
+ } catch (e) {
309
+ logger.warn('解析 getGroupInfo 返回值时出错:' + String(e))
310
+ }
311
+ }
312
+ } catch (err) {
313
+ logger.warn('getGroupInfo 调用失败:' + String(err))
314
+ }
315
+ }
281
316
 
282
317
  let avatar = current.avatar
283
318
  if (!avatar) {
@@ -301,28 +336,36 @@ async function ensureChannelMeta(ctx: Context, session: Session) {
301
336
 
302
337
  async function seedStatusCache(ctx: Context) {
303
338
  const binds = await ctx.database.get('steam_bind', {})
339
+ logger.debug && logger.debug(`seedStatusCache: load binds count=${binds.length}`)
304
340
  if (!binds.length) return
305
341
  const steamIds = [...new Set(binds.map(b => b.steamId))]
306
342
  const summaries = await ctx.steam.getPlayerSummaries(steamIds)
343
+ logger.debug && logger.debug(`seedStatusCache: fetched summaries=${summaries.length}`)
307
344
  for (const player of summaries) {
308
345
  statusCache.set(player.steamid, player)
309
346
  }
310
347
  }
311
348
 
312
349
  async function broadcast(ctx: Context, config: Config) {
313
- const channels = await ctx.database.get('steam_channel', { enable: true })
350
+ try {
351
+ const channels = await ctx.database.get('steam_channel', { enable: true })
352
+ logger.debug && logger.debug(`broadcast: enabled channels=${channels.length}`)
314
353
  if (channels.length === 0) return
315
354
 
316
- const channelIds = channels.map(c => c.id)
355
+ const channelIds = channels.map(c => c.id)
317
356
  const binds = await ctx.database.get('steam_bind', { channelId: channelIds })
357
+ logger.debug && logger.debug(`broadcast: binds total=${binds.length}`)
318
358
  if (binds.length === 0) return
319
359
 
320
360
  const steamIds = [...new Set(binds.map(b => b.steamId))]
321
-
322
- const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds)
361
+ logger.debug && logger.debug(`broadcast: unique steamIds=${steamIds.length}`)
362
+
363
+ const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds)
364
+ logger.debug && logger.debug(`broadcast: fetched summaries=${currentSummaries.length}`)
323
365
  const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]))
324
366
 
325
367
  for (const channel of channels) {
368
+ logger.debug && logger.debug(`broadcast: channel=${channel.id} processing`)
326
369
  const channelBinds = binds.filter(b => b.channelId === channel.id)
327
370
  const msgs: string[] = []
328
371
  const startGamingPlayers: any[] = []
@@ -350,6 +393,7 @@ async function broadcast(ctx: Context, config: Config) {
350
393
  }
351
394
 
352
395
  if (msgs.length > 0) {
396
+ logger.debug && logger.debug(`broadcast: channel=${channel.id} msgs=${msgs.length}`)
353
397
  const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined
354
398
  const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0]
355
399
  if (!bot) continue
@@ -378,4 +422,7 @@ async function broadcast(ctx: Context, config: Config) {
378
422
  for (const p of currentSummaries) {
379
423
  statusCache.set(p.steamid, p)
380
424
  }
425
+ } catch (err) {
426
+ logger.warn('broadcast 发生异常:' + String(err))
427
+ }
381
428
  }
@@ -3,10 +3,12 @@ export default {
3
3
  steam: {
4
4
  bind: {
5
5
  bind_success: '绑定成功!Steam ID: {0}',
6
+ already_bound: '您已经绑定过帐号了!',
6
7
  invalid_id: '请输入有效的 Steam ID。',
7
8
  id_not_found: '无法找到该 Steam ID。',
8
9
  error: '发生错误。',
9
10
  messages: {
11
+ already_bound: '您已经绑定过帐号了!',
10
12
  invalid_id: '请输入有效的 Steam ID。',
11
13
  id_not_found: '无法找到该 Steam ID。',
12
14
  bind_success: '绑定成功!Steam ID: {0}',