287 lines
12 KiB
Python
287 lines
12 KiB
Python
"""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)
|