koishi-plugin-steam-info-check 1.0.7 → 1.0.9

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'),
@@ -65,7 +65,7 @@ function apply(ctx, config) {
65
65
  // Commands and scheduler depend on steam/drawer being ready
66
66
  ctx.using(['steam', 'drawer'], (ctx) => {
67
67
  ctx.command('steam', 'Steam 信息');
68
- ctx.command('steam.bind <steamId:string>', 'Bind Steam ID', { authority: config.commandAuthority.bind })
68
+ ctx.command('steam.bind <steamId:string>', '绑定 Steam ID', { authority: config.commandAuthority.bind })
69
69
  .alias('steambind', '绑定steam')
70
70
  .action(async ({ session }, steamId) => {
71
71
  if (!session)
@@ -75,6 +75,16 @@ function apply(ctx, config) {
75
75
  const targetId = await ctx.steam.getSteamId(steamId);
76
76
  if (!targetId)
77
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.error('检查已绑定状态失败:' + String(e) + ' EEE');
87
+ }
78
88
  await ctx.database.upsert('steam_bind', [
79
89
  {
80
90
  userId: session.userId,
@@ -84,7 +94,7 @@ function apply(ctx, config) {
84
94
  ], ['userId', 'channelId']);
85
95
  return session.text('.bind_success', [targetId]);
86
96
  });
87
- ctx.command('steam.unbind', 'Unbind Steam ID', { authority: config.commandAuthority.unbind })
97
+ ctx.command('steam.unbind', '解绑 Steam ID', { authority: config.commandAuthority.unbind })
88
98
  .alias('steamunbind', '解绑steam')
89
99
  .action(async ({ session }) => {
90
100
  if (!session)
@@ -95,7 +105,7 @@ function apply(ctx, config) {
95
105
  });
96
106
  return result ? session.text('.unbind_success') : session.text('.not_bound');
97
107
  });
98
- 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 })
99
109
  .alias('steaminfo', 'steam信息')
100
110
  .action(async ({ session }, target) => {
101
111
  if (!session)
@@ -136,7 +146,7 @@ function apply(ctx, config) {
136
146
  return session.text('.error');
137
147
  }
138
148
  });
139
- ctx.command('steam.check', 'Check Friends Status', { authority: config.commandAuthority.check })
149
+ ctx.command('steam.check', '查看好友在线状态', { authority: config.commandAuthority.check })
140
150
  .alias('steamcheck', '查看steam', '查steam')
141
151
  .action(async ({ session }) => {
142
152
  if (!session)
@@ -164,7 +174,7 @@ function apply(ctx, config) {
164
174
  return session.text('.error');
165
175
  }
166
176
  });
167
- ctx.command('steam.enable', 'Enable Broadcast', { authority: config.commandAuthority.enable })
177
+ ctx.command('steam.enable', '启用播报', { authority: config.commandAuthority.enable })
168
178
  .alias('steamenable', '启用steam')
169
179
  .action(async ({ session }) => {
170
180
  if (!session)
@@ -177,7 +187,7 @@ function apply(ctx, config) {
177
187
  }]);
178
188
  return session.text('.enable_success');
179
189
  });
180
- ctx.command('steam.disable', 'Disable Broadcast', { authority: config.commandAuthority.disable })
190
+ ctx.command('steam.disable', '禁用播报', { authority: config.commandAuthority.disable })
181
191
  .alias('steamdisable', '禁用steam')
182
192
  .action(async ({ session }) => {
183
193
  if (!session)
@@ -190,7 +200,7 @@ function apply(ctx, config) {
190
200
  }]);
191
201
  return session.text('.disable_success');
192
202
  });
193
- 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 })
194
204
  .alias('steamupdate', '更新群信息')
195
205
  .action(async ({ session }, name, avatar) => {
196
206
  if (!session)
@@ -212,7 +222,7 @@ function apply(ctx, config) {
212
222
  await ctx.database.upsert('steam_channel', [update]);
213
223
  return session.text('.update_success');
214
224
  });
215
- 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 })
216
226
  .alias('steamnickname', 'steam昵称')
217
227
  .action(async ({ session }, nickname) => {
218
228
  if (!session)
@@ -247,13 +257,52 @@ async function ensureChannelMeta(ctx, session) {
247
257
  }
248
258
  // OneBot 群名补充
249
259
  if (!name && session.platform?.includes('onebot') && session.bot?.internal?.getGroupInfo) {
250
- try {
251
- const info = await session.bot.internal.getGroupInfo({ group_id: channelId });
252
- if (info?.group_name)
253
- name = info.group_name;
254
- }
255
- catch {
256
- /* ignore */
260
+ // 柔性尝试多种参数传递方式,记录每次调用结果以便定位 napcat/onebot 的参数格式差异
261
+ const argVariants = [
262
+ { group_id: channelId },
263
+ { group_id: { group_id: channelId } },
264
+ { group_id: Number(channelId) },
265
+ { group_id: { group_id: Number(channelId) } },
266
+ ];
267
+ for (const args of argVariants) {
268
+ try {
269
+ exports.logger.error(`getGroupInfo 尝试 args=${JSON.stringify(args)}`);
270
+ const info = await session.bot.internal.getGroupInfo(args);
271
+ exports.logger.error(`getGroupInfo 返回: ${JSON.stringify(info)}`);
272
+ if (!info)
273
+ continue;
274
+ // 支持多种返回结构:直接 group_name、data.group_name(napcat)、或嵌套情况
275
+ if (info.group_name) {
276
+ name = info.group_name;
277
+ break;
278
+ }
279
+ if (info.data && info.data.group_name) {
280
+ name = info.data.group_name;
281
+ break;
282
+ }
283
+ if (info.data && info.data.group && info.data.group.group_name) {
284
+ name = info.data.group.group_name;
285
+ break;
286
+ }
287
+ // 某些实现会把返回包在 ret.data 或直接在 ret
288
+ if (info.ret && info.ret.data && info.ret.data.group_name) {
289
+ name = info.ret.data.group_name;
290
+ break;
291
+ }
292
+ // 如果未识别结构,记录并继续尝试下一个参数形态
293
+ exports.logger.error('getGroupInfo 返回了未识别结构(尝试 参数:' + JSON.stringify(args) + '): ' + JSON.stringify(info) + ' EEE');
294
+ }
295
+ catch (err) {
296
+ // 记录详细错误以便分析 retcode/args
297
+ try {
298
+ exports.logger.error('getGroupInfo 调用失败,args=' + JSON.stringify(args) + ',错误:' + String(err) + ' EEE');
299
+ }
300
+ catch (e) {
301
+ exports.logger.error('getGroupInfo 调用失败但记录 args 时出错:' + String(e) + ' EEE');
302
+ }
303
+ // 如果错误中包含 retcode,记录方便排查
304
+ // 继续尝试下一个参数变体
305
+ }
257
306
  }
258
307
  }
259
308
  let avatar = current.avatar;
@@ -278,79 +327,92 @@ async function ensureChannelMeta(ctx, session) {
278
327
  }
279
328
  async function seedStatusCache(ctx) {
280
329
  const binds = await ctx.database.get('steam_bind', {});
330
+ exports.logger.error(`seedStatusCache: load binds count=${binds.length}`);
281
331
  if (!binds.length)
282
332
  return;
283
333
  const steamIds = [...new Set(binds.map(b => b.steamId))];
284
334
  const summaries = await ctx.steam.getPlayerSummaries(steamIds);
335
+ exports.logger.error(`seedStatusCache: fetched summaries=${summaries.length}`);
285
336
  for (const player of summaries) {
286
337
  statusCache.set(player.steamid, player);
287
338
  }
288
339
  }
289
340
  async function broadcast(ctx, config) {
290
- const channels = await ctx.database.get('steam_channel', { enable: true });
291
- if (channels.length === 0)
292
- return;
293
- const channelIds = channels.map(c => c.id);
294
- const binds = await ctx.database.get('steam_bind', { channelId: channelIds });
295
- if (binds.length === 0)
296
- return;
297
- const steamIds = [...new Set(binds.map(b => b.steamId))];
298
- const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds);
299
- const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]));
300
- for (const channel of channels) {
301
- const channelBinds = binds.filter(b => b.channelId === channel.id);
302
- const msgs = [];
303
- const startGamingPlayers = [];
304
- for (const bind of channelBinds) {
305
- const current = currentMap.get(bind.steamId);
306
- const old = statusCache.get(bind.steamId);
307
- if (!current)
308
- continue;
309
- if (!old)
310
- continue;
311
- const oldGame = old.gameextrainfo;
312
- const newGame = current.gameextrainfo;
313
- const name = bind.nickname || current.personaname;
314
- if (newGame && !oldGame) {
315
- msgs.push(`${name} 开始玩 ${newGame} 了`);
316
- startGamingPlayers.push({ ...current, nickname: bind.nickname });
317
- }
318
- else if (!newGame && oldGame) {
319
- msgs.push(`${name} 玩了 ${oldGame} 后不玩了`);
320
- }
321
- else if (newGame && oldGame && newGame !== oldGame) {
322
- msgs.push(`${name} 停止玩 ${oldGame},开始玩 ${newGame} 了`);
323
- }
324
- }
325
- if (msgs.length > 0) {
326
- const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined;
327
- const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0];
328
- if (!bot)
329
- continue;
330
- if (config.steamBroadcastType === 'none') {
331
- await bot.sendMessage(channel.id, msgs.join('\n'));
332
- }
333
- else if (config.steamBroadcastType === 'part') {
334
- if (startGamingPlayers.length > 0) {
335
- const images = await Promise.all(startGamingPlayers.map(p => ctx.drawer.drawStartGaming(p, p.nickname)));
336
- const combined = await ctx.drawer.concatImages(images);
337
- const img = combined ? (typeof combined === 'string' ? combined : koishi_1.h.image(combined, 'image/png')) : '';
338
- await bot.sendMessage(channel.id, msgs.join('\n') + img);
341
+ try {
342
+ const channels = await ctx.database.get('steam_channel', { enable: true });
343
+ exports.logger.error(`broadcast: enabled channels=${channels.length}`);
344
+ if (channels.length === 0)
345
+ return;
346
+ const channelIds = channels.map(c => c.id);
347
+ const binds = await ctx.database.get('steam_bind', { channelId: channelIds });
348
+ exports.logger.error(`broadcast: binds total=${binds.length}`);
349
+ if (binds.length === 0)
350
+ return;
351
+ const steamIds = [...new Set(binds.map(b => b.steamId))];
352
+ exports.logger.error(`broadcast: unique steamIds=${steamIds.length}`);
353
+ const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds);
354
+ exports.logger.error(`broadcast: fetched summaries=${currentSummaries.length}`);
355
+ const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]));
356
+ for (const channel of channels) {
357
+ exports.logger.error(`broadcast: channel=${channel.id} processing`);
358
+ const channelBinds = binds.filter(b => b.channelId === channel.id);
359
+ const msgs = [];
360
+ const startGamingPlayers = [];
361
+ for (const bind of channelBinds) {
362
+ const current = currentMap.get(bind.steamId);
363
+ const old = statusCache.get(bind.steamId);
364
+ if (!current)
365
+ continue;
366
+ if (!old)
367
+ continue;
368
+ const oldGame = old.gameextrainfo;
369
+ const newGame = current.gameextrainfo;
370
+ const name = bind.nickname || current.personaname;
371
+ if (newGame && !oldGame) {
372
+ msgs.push(`${name} 开始玩 ${newGame} 了`);
373
+ startGamingPlayers.push({ ...current, nickname: bind.nickname });
339
374
  }
340
- else {
341
- await bot.sendMessage(channel.id, msgs.join('\n'));
375
+ else if (!newGame && oldGame) {
376
+ msgs.push(`${name} 玩了 ${oldGame} 后不玩了`);
377
+ }
378
+ else if (newGame && oldGame && newGame !== oldGame) {
379
+ msgs.push(`${name} 停止玩 ${oldGame},开始玩 ${newGame} 了`);
342
380
  }
343
381
  }
344
- else if (config.steamBroadcastType === 'all') {
345
- const channelPlayers = channelBinds.map(b => currentMap.get(b.steamId)).filter(Boolean);
346
- const parentAvatar = channel.avatar ? Buffer.from(channel.avatar, 'base64') : await ctx.drawer.getDefaultAvatar();
347
- const image = await ctx.drawer.drawFriendsStatus(parentAvatar, channel.name || channel.id, channelPlayers, channelBinds);
348
- const img = image ? (typeof image === 'string' ? image : koishi_1.h.image(image, 'image/png')) : '';
349
- await bot.sendMessage(channel.id, msgs.join('\n') + img);
382
+ if (msgs.length > 0) {
383
+ exports.logger.error(`broadcast: channel=${channel.id} msgs=${msgs.length}`);
384
+ const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined;
385
+ const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0];
386
+ if (!bot)
387
+ continue;
388
+ if (config.steamBroadcastType === 'none') {
389
+ await bot.sendMessage(channel.id, msgs.join('\n'));
390
+ }
391
+ else if (config.steamBroadcastType === 'part') {
392
+ if (startGamingPlayers.length > 0) {
393
+ const images = await Promise.all(startGamingPlayers.map(p => ctx.drawer.drawStartGaming(p, p.nickname)));
394
+ const combined = await ctx.drawer.concatImages(images);
395
+ const img = combined ? (typeof combined === 'string' ? combined : koishi_1.h.image(combined, 'image/png')) : '';
396
+ await bot.sendMessage(channel.id, msgs.join('\n') + img);
397
+ }
398
+ else {
399
+ await bot.sendMessage(channel.id, msgs.join('\n'));
400
+ }
401
+ }
402
+ else if (config.steamBroadcastType === 'all') {
403
+ const channelPlayers = channelBinds.map(b => currentMap.get(b.steamId)).filter(Boolean);
404
+ const parentAvatar = channel.avatar ? Buffer.from(channel.avatar, 'base64') : await ctx.drawer.getDefaultAvatar();
405
+ const image = await ctx.drawer.drawFriendsStatus(parentAvatar, channel.name || channel.id, channelPlayers, channelBinds);
406
+ const img = image ? (typeof image === 'string' ? image : koishi_1.h.image(image, 'image/png')) : '';
407
+ await bot.sendMessage(channel.id, msgs.join('\n') + img);
408
+ }
350
409
  }
351
410
  }
411
+ for (const p of currentSummaries) {
412
+ statusCache.set(p.steamid, p);
413
+ }
352
414
  }
353
- for (const p of currentSummaries) {
354
- statusCache.set(p.steamid, p);
415
+ catch (err) {
416
+ exports.logger.error('broadcast 发生异常:' + String(err) + ' EEE');
355
417
  }
356
418
  }
@@ -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/dist/service.js CHANGED
@@ -43,7 +43,7 @@ class SteamService extends koishi_1.Service {
43
43
  }
44
44
  }
45
45
  catch (e) {
46
- this.ctx.logger('steam').warn(`API key ${key} failed: ${e}`);
46
+ this.ctx.logger('steam').error(`API key ${key} failed: ${e} EEE`);
47
47
  }
48
48
  }
49
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-steam-info-check",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
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'),
@@ -98,15 +98,25 @@ export function apply(ctx: Context, config: Config) {
98
98
  ctx.using(['steam', 'drawer'], (ctx) => {
99
99
  ctx.command('steam', 'Steam 信息')
100
100
 
101
- 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 })
102
102
  .alias('steambind', '绑定steam')
103
103
  .action(async ({ session }, steamId) => {
104
104
  if (!session) return
105
105
  if (!steamId || !/^\d+$/.test(steamId)) return session.text('.invalid_id')
106
-
106
+
107
107
  const targetId = await ctx.steam.getSteamId(steamId)
108
108
  if (!targetId) return session.text('.id_not_found')
109
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.error('检查已绑定状态失败:' + String(e) + ' EEE')
118
+ }
119
+
110
120
  await ctx.database.upsert('steam_bind', [
111
121
  {
112
122
  userId: session.userId,
@@ -118,7 +128,7 @@ export function apply(ctx: Context, config: Config) {
118
128
  return session.text('.bind_success', [targetId])
119
129
  })
120
130
 
121
- ctx.command('steam.unbind', 'Unbind Steam ID', { authority: config.commandAuthority.unbind })
131
+ ctx.command('steam.unbind', '解绑 Steam ID', { authority: config.commandAuthority.unbind })
122
132
  .alias('steamunbind', '解绑steam')
123
133
  .action(async ({ session }) => {
124
134
  if (!session) return
@@ -129,7 +139,7 @@ export function apply(ctx: Context, config: Config) {
129
139
  return result ? session.text('.unbind_success') : session.text('.not_bound')
130
140
  })
131
141
 
132
- 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 })
133
143
  .alias('steaminfo', 'steam信息')
134
144
  .action(async ({ session }, target) => {
135
145
  if (!session) return
@@ -166,7 +176,7 @@ export function apply(ctx: Context, config: Config) {
166
176
  }
167
177
  })
168
178
 
169
- ctx.command('steam.check', 'Check Friends Status', { authority: config.commandAuthority.check })
179
+ ctx.command('steam.check', '查看好友在线状态', { authority: config.commandAuthority.check })
170
180
  .alias('steamcheck', '查看steam', '查steam')
171
181
  .action(async ({ session }) => {
172
182
  if (!session) return
@@ -194,7 +204,7 @@ export function apply(ctx: Context, config: Config) {
194
204
  }
195
205
  })
196
206
 
197
- ctx.command('steam.enable', 'Enable Broadcast', { authority: config.commandAuthority.enable })
207
+ ctx.command('steam.enable', '启用播报', { authority: config.commandAuthority.enable })
198
208
  .alias('steamenable', '启用steam')
199
209
  .action(async ({ session }) => {
200
210
  if (!session) return
@@ -207,7 +217,7 @@ export function apply(ctx: Context, config: Config) {
207
217
  return session.text('.enable_success')
208
218
  })
209
219
 
210
- ctx.command('steam.disable', 'Disable Broadcast', { authority: config.commandAuthority.disable })
220
+ ctx.command('steam.disable', '禁用播报', { authority: config.commandAuthority.disable })
211
221
  .alias('steamdisable', '禁用steam')
212
222
  .action(async ({ session }) => {
213
223
  if (!session) return
@@ -220,7 +230,7 @@ export function apply(ctx: Context, config: Config) {
220
230
  return session.text('.disable_success')
221
231
  })
222
232
 
223
- 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 })
224
234
  .alias('steamupdate', '更新群信息')
225
235
  .action(async ({ session }, name, avatar) => {
226
236
  if (!session) return
@@ -243,7 +253,7 @@ export function apply(ctx: Context, config: Config) {
243
253
  return session.text('.update_success')
244
254
  })
245
255
 
246
- 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 })
247
257
  .alias('steamnickname', 'steam昵称')
248
258
  .action(async ({ session }, nickname) => {
249
259
  if (!session) return
@@ -281,11 +291,54 @@ async function ensureChannelMeta(ctx: Context, session: Session) {
281
291
  }
282
292
  // OneBot 群名补充
283
293
  if (!name && session.platform?.includes('onebot') && session.bot?.internal?.getGroupInfo) {
284
- try {
285
- const info = await session.bot.internal.getGroupInfo({ group_id: channelId })
286
- if (info?.group_name) name = info.group_name
287
- } catch {
288
- /* ignore */
294
+ // 柔性尝试多种参数传递方式,记录每次调用结果以便定位 napcat/onebot 的参数格式差异
295
+ const argVariants = [
296
+ { group_id: channelId },
297
+ { group_id: { group_id: channelId } },
298
+ { group_id: Number(channelId) },
299
+ { group_id: { group_id: Number(channelId) } },
300
+ ]
301
+
302
+ for (const args of argVariants) {
303
+ try {
304
+ logger.error(`getGroupInfo 尝试 args=${JSON.stringify(args)}`)
305
+ const info = await session.bot.internal.getGroupInfo(args)
306
+ logger.error(`getGroupInfo 返回: ${JSON.stringify(info)}`)
307
+
308
+ if (!info) continue
309
+
310
+ // 支持多种返回结构:直接 group_name、data.group_name(napcat)、或嵌套情况
311
+ if (info.group_name) {
312
+ name = info.group_name
313
+ break
314
+ }
315
+ if (info.data && info.data.group_name) {
316
+ name = info.data.group_name
317
+ break
318
+ }
319
+ if (info.data && info.data.group && info.data.group.group_name) {
320
+ name = info.data.group.group_name
321
+ break
322
+ }
323
+
324
+ // 某些实现会把返回包在 ret.data 或直接在 ret
325
+ if (info.ret && info.ret.data && info.ret.data.group_name) {
326
+ name = info.ret.data.group_name
327
+ break
328
+ }
329
+
330
+ // 如果未识别结构,记录并继续尝试下一个参数形态
331
+ logger.error('getGroupInfo 返回了未识别结构(尝试 参数:' + JSON.stringify(args) + '): ' + JSON.stringify(info) + ' EEE')
332
+ } catch (err) {
333
+ // 记录详细错误以便分析 retcode/args
334
+ try {
335
+ logger.error('getGroupInfo 调用失败,args=' + JSON.stringify(args) + ',错误:' + String(err) + ' EEE')
336
+ } catch (e) {
337
+ logger.error('getGroupInfo 调用失败但记录 args 时出错:' + String(e) + ' EEE')
338
+ }
339
+ // 如果错误中包含 retcode,记录方便排查
340
+ // 继续尝试下一个参数变体
341
+ }
289
342
  }
290
343
  }
291
344
 
@@ -311,28 +364,36 @@ async function ensureChannelMeta(ctx: Context, session: Session) {
311
364
 
312
365
  async function seedStatusCache(ctx: Context) {
313
366
  const binds = await ctx.database.get('steam_bind', {})
367
+ logger.error(`seedStatusCache: load binds count=${binds.length}`)
314
368
  if (!binds.length) return
315
369
  const steamIds = [...new Set(binds.map(b => b.steamId))]
316
370
  const summaries = await ctx.steam.getPlayerSummaries(steamIds)
371
+ logger.error(`seedStatusCache: fetched summaries=${summaries.length}`)
317
372
  for (const player of summaries) {
318
373
  statusCache.set(player.steamid, player)
319
374
  }
320
375
  }
321
376
 
322
377
  async function broadcast(ctx: Context, config: Config) {
323
- const channels = await ctx.database.get('steam_channel', { enable: true })
378
+ try {
379
+ const channels = await ctx.database.get('steam_channel', { enable: true })
380
+ logger.error(`broadcast: enabled channels=${channels.length}`)
324
381
  if (channels.length === 0) return
325
382
 
326
- const channelIds = channels.map(c => c.id)
383
+ const channelIds = channels.map(c => c.id)
327
384
  const binds = await ctx.database.get('steam_bind', { channelId: channelIds })
385
+ logger.error(`broadcast: binds total=${binds.length}`)
328
386
  if (binds.length === 0) return
329
387
 
330
388
  const steamIds = [...new Set(binds.map(b => b.steamId))]
331
-
332
- const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds)
389
+ logger.error(`broadcast: unique steamIds=${steamIds.length}`)
390
+
391
+ const currentSummaries = await ctx.steam.getPlayerSummaries(steamIds)
392
+ logger.error(`broadcast: fetched summaries=${currentSummaries.length}`)
333
393
  const currentMap = new Map(currentSummaries.map(p => [p.steamid, p]))
334
394
 
335
395
  for (const channel of channels) {
396
+ logger.error(`broadcast: channel=${channel.id} processing`)
336
397
  const channelBinds = binds.filter(b => b.channelId === channel.id)
337
398
  const msgs: string[] = []
338
399
  const startGamingPlayers: any[] = []
@@ -360,6 +421,7 @@ async function broadcast(ctx: Context, config: Config) {
360
421
  }
361
422
 
362
423
  if (msgs.length > 0) {
424
+ logger.error(`broadcast: channel=${channel.id} msgs=${msgs.length}`)
363
425
  const botKey = channel.platform && channel.assignee ? `${channel.platform}:${channel.assignee}` : undefined
364
426
  const bot = botKey ? ctx.bots[botKey] : Object.values(ctx.bots)[0]
365
427
  if (!bot) continue
@@ -388,4 +450,7 @@ async function broadcast(ctx: Context, config: Config) {
388
450
  for (const p of currentSummaries) {
389
451
  statusCache.set(p.steamid, p)
390
452
  }
453
+ } catch (err) {
454
+ logger.error('broadcast 发生异常:' + String(err) + ' EEE')
455
+ }
391
456
  }
@@ -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}',
@@ -11,4 +11,24 @@ enable_success: "已开启本群播报。"
11
11
  disable_success: "已关闭本群播报。"
12
12
  args_missing: "参数缺失。"
13
13
  update_success: "更新群信息成功。"
14
- nickname_set: "昵称已设置为 {0}。"
14
+ nickname_set: "昵称已设置为 {0}。"
15
+
16
+ # 配置面板本地化
17
+ fonts:
18
+ regular: "常规字体路径"
19
+ light: "细体字体路径"
20
+ bold: "粗体字体路径"
21
+
22
+ steamDisableBroadcastOnStartup: "启动时禁用首次播报(仅预热缓存)"
23
+ steamRequestInterval: "轮询间隔(秒)"
24
+ steamBroadcastType: "播报类型(all/part/none)"
25
+
26
+ commandAuthority:
27
+ bind: "绑定命令权限"
28
+ unbind: "解绑命令权限"
29
+ info: "查看资料命令权限"
30
+ check: "查看状态命令权限"
31
+ enable: "启用播报命令权限"
32
+ disable: "禁用播报命令权限"
33
+ update: "更新群信息命令权限"
34
+ nickname: "设置昵称命令权限"
package/src/service.ts CHANGED
@@ -77,7 +77,7 @@ export class SteamService extends Service {
77
77
  break // Success for this chunk
78
78
  }
79
79
  } catch (e) {
80
- this.ctx.logger('steam').warn(`API key ${key} failed: ${e}`)
80
+ this.ctx.logger('steam').error(`API key ${key} failed: ${e} EEE`)
81
81
  }
82
82
  }
83
83
  }