"""Steam 信息 —— 触发器(Trigger)层。""" from __future__ import annotations import asyncio from nonebot import logger, on_command from nonebot.adapters import Message, Event from nonebot.adapters.onebot.v11 import Bot, MessageEvent, MessageSegment from nonebot.params import CommandArg, Depends from nonebot.permission import SUPERUSER from nonebot_plugin_alconna import At, Image, Target, UniMessage from hexi.core.cooldown import cooldown # 业务/状态在插件根 __init__ 中(本插件为社区插件,服务层保留在根模块) from .. import ( __plugin_meta__, STEAM_ID_OFFSET, avatar_path, bind_data, cache_path, config, db_path, disable_parent_data, draw_friends_status, draw_player_status, fetch_play_time, get_game_cn_names, get_game_icons, get_group_nickname, get_parent_info, get_steam_id, get_steam_users_info, get_target, get_user_data, image_to_bytes, key_select, localize_game_name, simplize_steam_player_data, _fill_missing_icon_hashes, _query_game_names, _upsert_game_icon_hashes, update_steamdb_apps, ) help = on_command("steamhelp", aliases={"steam帮助"}, priority=10) bind = on_command("steambind", aliases={"绑定steam"}, priority=10) unbind = on_command("steamunbind", aliases={"解绑steam"}, priority=10) info = on_command("steaminfo", aliases={"steam信息"}, priority=10) check = cooldown(30, scope="group", hint="群里刚有人查过,{secs} 秒后再试")( on_command("steamcheck", aliases={"查看steam", "查steam", "谁在玩游戏"}, priority=10) ) enable = on_command("steamenable", aliases={"启用steam"}, priority=10) disable = on_command("steamdisable", aliases={"禁用steam"}, priority=10) get_play_time = cooldown(15, hint="查询太频繁了,{secs} 秒后再试")( on_command("steamplaytime", aliases={"游戏时长表"}, priority=10) ) update_steamdb = on_command( "steamdbupdate", aliases={"更新steamdb"}, priority=10, permission=SUPERUSER ) @help.handle() async def help_handle(): await help.finish(__plugin_meta__.usage) @bind.handle() async def bind_handle(event: Event, target: Target = Depends(get_target), cmd_arg: Message = CommandArg()): parent_id = target.parent_id or target.id arg = cmd_arg.extract_plain_text() if not arg.isdigit(): await bind.finish( "请输入正确的 Steam ID 或 Steam好友代码,格式: steambind [Steam ID 或 Steam好友代码]" ) steam_id = get_steam_id(arg) if user_data := bind_data.get(parent_id, event.get_user_id()): user_data["steam_id"] = steam_id bind_data.save() await bind.finish(f"已更新你的 Steam ID 为 {steam_id}") else: bind_data.add( parent_id, {"user_id": event.get_user_id(), "steam_id": steam_id, "nickname": None}, ) bind_data.save() await bind.finish(f"已绑定你的 Steam ID 为 {steam_id}") @unbind.handle() async def unbind_handle(event: Event, target: Target = Depends(get_target)): parent_id = target.parent_id or target.id user_id = event.get_user_id() if bind_data.get(parent_id, user_id) is not None: bind_data.remove(parent_id, user_id) bind_data.save() await unbind.finish("已解绑 Steam ID") else: await unbind.finish("未绑定 Steam ID") @info.handle() async def info_handle(bot: Bot, event: Event, target: Target = Depends(get_target), arg: Message = CommandArg()): parent_id = target.parent_id or target.id uni_arg = await UniMessage.generate(message=arg, event=event, bot=bot) at = uni_arg[At] if len(at) != 0: user_id = at[0].target user_data = bind_data.get(parent_id, user_id) if user_data is None: await info.finish("该用户未绑定 Steam ID") steam_id = user_data["steam_id"] steam_friend_code = str(int(steam_id) - STEAM_ID_OFFSET) elif arg.extract_plain_text().strip() != "": steam_id = int(arg.extract_plain_text().strip()) if steam_id < STEAM_ID_OFFSET: steam_friend_code = steam_id steam_id += STEAM_ID_OFFSET else: steam_friend_code = steam_id - STEAM_ID_OFFSET else: user_data = bind_data.get(parent_id, event.get_user_id()) if user_data is None: await info.finish( "未绑定 Steam ID, 请使用 “steambind [Steam ID 或 Steam好友代码]” 绑定 Steam ID" ) steam_id = user_data["steam_id"] steam_friend_code = str(int(steam_id) - STEAM_ID_OFFSET) player_data = await get_user_data(steam_id, cache_path, config.proxy) names = await get_game_cn_names( [g.get("appid") for g in player_data["game_data"] if g.get("appid")], db_path, ) for g in player_data["game_data"]: g["game_name"] = localize_game_name(g["game_name"], g.get("appid"), names) draw_data = [ { "game_header": game["game_image"], "game_name": game["game_name"], "game_time": f"{game['play_time']} 小时", "last_play_time": game["last_played"], "achievements": game["achievements"], "completed_achievement_number": game.get("completed_achievement_number"), "total_achievement_number": game.get("total_achievement_number"), } for game in player_data["game_data"] ] image = draw_player_status( player_data["background"], player_data["avatar"], player_data["player_name"], str(steam_friend_code), player_data["description"], player_data["recent_2_week_play_time"], draw_data, ) await info.finish(await UniMessage(Image(raw=image_to_bytes(image))).export(bot)) @check.handle() async def check_handle(bot: Bot, target: Target = Depends(get_target), arg: Message = CommandArg()): if arg.extract_plain_text().strip() != "": return None parent_id = target.parent_id or target.id steam_ids = bind_data.get_all(parent_id) current_key = key_select() logger.info(f"当前调用的key:{current_key}") steam_info = await get_steam_users_info(steam_ids, current_key, config.proxy, wait=False) if steam_info is None: await check.finish("Steam API 请求过于频繁,请稍后再试") players = steam_info["response"]["players"] names = await get_game_cn_names( [int(p["gameid"]) for p in players if p.get("gameid")], db_path, ) for p in players: if p.get("gameid"): p["gameextrainfo"] = localize_game_name(p.get("gameextrainfo", ""), int(p["gameid"]), names) game_ids: list[int] = [] seen: set[int] = set() for p in players: if p.get("gameid"): gid = int(p["gameid"]) if gid not in seen: seen.add(gid) game_ids.append(gid) game_icons = await get_game_icons(game_ids, config.proxy, db_path) if game_ids else {} asyncio.create_task(_fill_missing_icon_hashes(players)) parent_avatar, parent_name = await get_parent_info(bot, parent_id) steam_status_data = [] for player in players: data = await simplize_steam_player_data(player, config.proxy, avatar_path, names) entry = bind_data.get_by_steam_id(parent_id, player["steamid"]) data["nickname"] = await get_group_nickname(bot, parent_id, entry["user_id"]) if entry is not None else None data["game_icon"] = game_icons.get(data["gameid"]) steam_status_data.append(data) image = draw_friends_status(parent_avatar, parent_name, steam_status_data) await target.send(UniMessage(Image(raw=image_to_bytes(image)))) @enable.handle() async def enable_handle(target: Target = Depends(get_target)): parent_id = target.parent_id or target.id disable_parent_data.remove(parent_id) disable_parent_data.save() await enable.finish("已启用 Steam 播报") @disable.handle() async def disable_handle(target: Target = Depends(get_target)): parent_id = target.parent_id or target.id disable_parent_data.add(parent_id) disable_parent_data.save() await disable.finish("已禁用 Steam 播报") @update_steamdb.handle() async def update_steamdb_handle(): await update_steamdb.send("正在拉取 Steam 全量 app 列表...") try: api_key = config.steam_api_key[0] count = await update_steamdb_apps(db_path, api_key) except Exception as e: logger.error(f"SteamDB 数据更新失败: {e}") await update_steamdb.finish(f"SteamDB 数据更新失败: {e}") if count: await update_steamdb.finish(f"Steam 数据更新完成,共 {count} 个游戏") else: await update_steamdb.finish("Steam 数据拉取失败,稍后重试") @get_play_time.handle() async def get_play_time_handle(bot: Bot, event: MessageEvent, target: Target = Depends(get_target), cmd_arg: Message = CommandArg()): parent_id = target.parent_id or target.id user_data = None logger.info(f"游玩时长表:进入处理, parent={parent_id}") arg_text = cmd_arg.extract_plain_text().strip() if arg_text: if not arg_text.isdigit(): await get_play_time.finish( "请输入正确的 Steam ID 或 Steam好友代码,格式: steamplaytime [Steam ID 或 Steam好友代码]" ) steam_id = get_steam_id(arg_text) else: user_data = bind_data.get(parent_id, event.get_user_id()) if user_data is None: await get_play_time.finish( "未绑定 Steam ID,请使用 steamplaytime [Steam ID 或 Steam好友代码] 查询,或先使用 steambind 绑定" ) steam_id = user_data["steam_id"] await get_play_time.send("正在获取数据,请耐心等待") logger.info(f"游玩时长表:开始获取数据, steam_id={steam_id}") games = await fetch_play_time(steam_id) if games is None: await get_play_time.finish("Steam API 请求过于频繁,请稍后再试") if not games: await get_play_time.finish("没有获取到游玩数据(游戏可能未公开)") icon_entries = {int(g["appid"]): g.get("clienticon") for g in games if g.get("appid") and g.get("clienticon")} if icon_entries: _upsert_game_icon_hashes(db_path, icon_entries) appids = [g["appid"] for g in games if g.get("appid")] game_map = _query_game_names(db_path, appids) if appids else {} names = {int(aid): entry["name_ch"] for aid, entry in game_map.items() if entry.get("name_ch")} for g in games: g["name"] = localize_game_name(g["name"], g.get("appid"), names) played = [g for g in games if g["playtime_hours"] > 0] if not played: await get_play_time.finish("没有游玩记录(游戏可能未公开或全部未游玩)") for i, g in enumerate(played, 1): g["rank"] = i try: from ..html_playtime import render_play_time_table steam_name, plugin_nick, avatar = "", "", None try: player_data = await get_user_data(steam_id, cache_path, config.proxy) steam_name = player_data.get("player_name", "") avatar = player_data.get("avatar") except Exception as e: logger.warning(f"获取玩家资料失败,省略头像昵称: {e}") if user_data: plugin_nick = await get_group_nickname(bot, parent_id, user_data["user_id"]) img_bytes = await render_play_time_table( played, games, steam_name, plugin_nick, avatar, theme=config.steam_playtime_theme, ) except Exception as e: logger.error(f"渲染游玩时长表图片失败: {e}", exc_info=True) await get_play_time.finish("游玩时长表渲染失败,请稍后重试") uni_msg = MessageSegment.reply(event.message_id) + await UniMessage(Image(raw=img_bytes)).export(bot) await get_play_time.finish(uni_msg)