import re import httpx from pathlib import Path from bs4 import BeautifulSoup from nonebot.log import logger from typing import List, Optional from datetime import datetime, timezone import asyncio import requests import csv from .models import PlayerSummaries, PlayerData STEAM_ID_OFFSET = 76561197960265728 # 全局变量,用于记录当前使用的 API Key 的索引 current_api_key_index = 0 def get_steam_id(steam_id_or_steam_friends_code: str) -> str: if not steam_id_or_steam_friends_code.isdigit(): return None id_ = int(steam_id_or_steam_friends_code) if id_ < STEAM_ID_OFFSET: return str(id_ + STEAM_ID_OFFSET) return steam_id_or_steam_friends_code async def get_steam_users_info( steam_ids: List[str], steam_api_key: List[str], proxy: str = None ) -> PlayerSummaries: if len(steam_ids) == 0: return {"response": {"players": []}} if len(steam_ids) > 100: # 分批获取 result = {"response": {"players": []}} for i in range(0, len(steam_ids), 100): batch_result = await get_steam_users_info( steam_ids[i: i + 100], steam_api_key, proxy ) result["response"]["players"].extend(batch_result["response"]["players"]) return result for api_key in steam_api_key: try: async with httpx.AsyncClient(proxy=proxy) as client: response = await client.get( f'https://community.steam-api.com/ISteamUser/GetPlayerSummaries/v0002/?key={api_key}&steamids={",".join(steam_ids)}' ) logger.info(f"本次请求的响应码:{response.status_code}") if response.status_code == 200: return response.json() else: logger.warning(f"API key {api_key} failed to get steam users info.") except httpx.RequestError as exc: async with httpx.AsyncClient(proxy=proxy) as client: response = await client.get( f'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={api_key}&steamids={",".join(steam_ids)}' ) logger.info(f"本次请求的响应码:{response.status_code}") if response.status_code == 200: return response.json() else: logger.warning(f"API key {api_key} failed to get steam users info.") logger.warning(f"API key {api_key} encountered an error: {exc}") logger.error("All API keys failed to get steam users info.") return {"response": {"players": []}} async def _fetch( url: str, default: bytes, cache_file: Optional[Path] = None, proxy: str = None ) -> bytes: if cache_file is not None and cache_file.exists(): return cache_file.read_bytes() try: async with httpx.AsyncClient(proxy=proxy) as client: response = await client.get(url) if response.status_code == 200: if cache_file is not None: cache_file.write_bytes(response.content) return response.content else: response.raise_for_status() except Exception as exc: logger.error(f"Failed to get image: {exc}") return default async def get_user_data( steam_id: int, cache_path: Path, proxy: str = None ) -> PlayerData: url = f"https://steamcommunity.com/profiles/{steam_id}" default_background = (Path(__file__).parent / "res/bg_dots.png").read_bytes() default_avatar = (Path(__file__).parent / "res/unknown_avatar.jpg").read_bytes() default_achievement_image = ( Path(__file__).parent / "res/default_achievement_image.png" ).read_bytes() default_header_image = ( Path(__file__).parent / "res/default_header_image.jpg" ).read_bytes() result = { "description": "No information given.", "background": default_background, "avatar": default_avatar, "player_name": "Unknown", "recent_2_week_play_time": None, "game_data": [], } local_time = datetime.now(timezone.utc).astimezone() utc_offset_minutes = int(local_time.utcoffset().total_seconds()) timezone_cookie_value = f"{utc_offset_minutes},0" try: async with httpx.AsyncClient( proxy=proxy, headers={ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" }, cookies={"timezoneOffset": timezone_cookie_value}, ) as client: response = await client.get(url) if response.status_code == 200: html = response.text elif response.status_code == 302: url = response.headers["Location"] response = await client.get(url) if response.status_code == 200: html = response.text else: response.raise_for_status() except httpx.RequestError as exc: logger.error(f"Failed to get user data: {exc}") return result # player name player_name = re.search(r"Steam 社区 :: (.*?)", html) if player_name: result["player_name"] = player_name.group(1) # description t
\r\n\t\t\t\t\t\t\t\t風が雨が激しくても
思いだすんだ 僕らを照らす光があるよ
今日もいっぱい
明日もいっぱい 力を出しきってみるよ\t\t\t\t\t\t\t
description = re.search( r'
(.*?)
', html, re.DOTALL | re.MULTILINE ) if description: description = description.group(1) description = re.sub(r"
", "\n", description) description = re.sub(r"\t", "", description) result["description"] = description.strip() # remove emoji result["description"] = re.sub(r"ː.*?ː", "", result["description"]) # remove xml result["description"] = re.sub(r"<.*?>", "", result["description"]) # background background_url = re.search(r"background-image: url\( \'(.*?)\' \)", html) if background_url: background_url = background_url.group(1) result["background"] = await _fetch( background_url, default_background, proxy=proxy ) # avatar # \t avatar_url = re.search(r'\r\n\t\t\t\t\t\t\t\t\t
15.5 小时(过去 2 周)
play_time_text = re.search( r'