- hexi core: message handling, rate limiting, cooldown, plugin manager - Custom plugins: BF stats, daily check-in, quotes, persona cards, etc. - Community plugins vendored under hexi/plugins with local fixes - Web admin frontends (learning-chat, persona-admin), unified hexi/web - Tests for rate_limit/cooldown/memes/persona; poetry.lock Co-Authored-By: Claude <noreply@anthropic.com>
285 lines
11 KiB
Python
285 lines
11 KiB
Python
import httpx
|
|
import re
|
|
from typing import Any, Dict, Optional
|
|
from .get_bf6_data import *
|
|
from .tracker_data import *
|
|
|
|
|
|
async def get_data_bf3(player, player_id, user_id, platform):
|
|
url = f"https://api.gametools.network/bf3/all/?format_values=true&playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
url = f"https://api.gametools.network/bf3/all/?format_values=true&name={player}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# 创建两个并发任务
|
|
task_id = asyncio.create_task(client.get(url_by_id))
|
|
task_name = asyncio.create_task(client.get(url_by_name))
|
|
|
|
# 等待第一个完成的任务
|
|
done, pending = await asyncio.wait(
|
|
[task_id, task_name],
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
# 取消未完成的任务
|
|
for task in pending:
|
|
task.cancel()
|
|
# 检查已完成的任务是否成功且无 errors
|
|
for task in done:
|
|
try:
|
|
response = await task
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if 'errors' not in data:
|
|
logger.info(f"成功获取数据({'ID' if task is task_id else 'Name'}方式)")
|
|
return data
|
|
except (httpx.RequestError, ValueError) as e:
|
|
logger.warning(f"请求或解析失败: {e}")
|
|
if pending:
|
|
pass
|
|
# 如果所有尝试都失败,返回错误
|
|
return {"errors": ["未找到玩家"]}
|
|
|
|
|
|
async def get_data_bf4(player, player_id, user_id, platform):
|
|
url_by_id = f"https://api.gametools.network/bf4/all/?format_values=true&playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
url_by_name = f"https://api.gametools.network/bf4/all/?format_values=true&name={player}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# 创建两个并发任务
|
|
task_id = asyncio.create_task(client.get(url_by_id))
|
|
task_name = asyncio.create_task(client.get(url_by_name))
|
|
|
|
# 等待第一个完成的任务
|
|
done, pending = await asyncio.wait(
|
|
[task_id, task_name],
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
# 取消未完成的任务
|
|
for task in pending:
|
|
task.cancel()
|
|
# 检查已完成的任务是否成功且无 errors
|
|
for task in done:
|
|
try:
|
|
response = await task
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if 'errors' not in data:
|
|
logger.info(f"成功获取数据({'ID' if task is task_id else 'Name'}方式)")
|
|
return data
|
|
except (httpx.RequestError, ValueError) as e:
|
|
logger.warning(f"请求或解析失败: {e}")
|
|
if pending:
|
|
pass
|
|
# 如果所有尝试都失败,返回错误
|
|
return {"errors": ["未找到玩家"]}
|
|
|
|
|
|
async def get_data_bf1(player, player_id, user_id, platform):
|
|
url_by_id = f"https://api.gametools.network/bf1/all/?format_values=true&playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
url_by_name = f"https://api.gametools.network/bf1/all/?format_values=true&name={player}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# 创建两个并发任务
|
|
task_id = asyncio.create_task(client.get(url_by_id))
|
|
task_name = asyncio.create_task(client.get(url_by_name))
|
|
|
|
# 等待第一个完成的任务
|
|
done, pending = await asyncio.wait(
|
|
[task_id, task_name],
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
# 取消未完成的任务
|
|
for task in pending:
|
|
task.cancel()
|
|
# 检查已完成的任务是否成功且无 errors
|
|
for task in done:
|
|
try:
|
|
response = await task
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if 'errors' not in data:
|
|
logger.info(f"成功获取数据({'ID' if task is task_id else 'Name'}方式)")
|
|
return data
|
|
except (httpx.RequestError, ValueError) as e:
|
|
logger.warning(f"请求或解析失败: {e}")
|
|
if pending:
|
|
pass
|
|
# 如果所有尝试都失败,返回错误
|
|
return {"errors": ["未找到玩家"]}
|
|
|
|
|
|
async def get_data_bfv(player, player_id, user_id, platform):
|
|
url_by_id = f"https://api.gametools.network/bfv/all/?format_values=true&playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
url_by_name = f"https://api.gametools.network/bfv/all/?format_values=true&name={player}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# 创建两个并发任务
|
|
task_id = asyncio.create_task(client.get(url_by_id))
|
|
task_name = asyncio.create_task(client.get(url_by_name))
|
|
|
|
# 等待第一个完成的任务
|
|
done, pending = await asyncio.wait(
|
|
[task_id, task_name],
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
# 取消未完成的任务
|
|
for task in pending:
|
|
task.cancel()
|
|
# 检查已完成的任务是否成功且无 errors
|
|
for task in done:
|
|
try:
|
|
response = await task
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if 'errors' not in data:
|
|
logger.info(f"成功获取数据({'ID' if task is task_id else 'Name'}方式)")
|
|
return data
|
|
except (httpx.RequestError, ValueError) as e:
|
|
logger.warning(f"请求或解析失败: {e}")
|
|
if pending:
|
|
pass
|
|
# 如果所有尝试都失败,返回错误
|
|
return {"errors": ["未找到玩家"]}
|
|
|
|
|
|
async def get_data_bf6(player_name, player_id, user_id, platform):
|
|
# 构造两个 URL
|
|
url_by_id = f"https://api.gametools.network/bf6/all/?format_values=true&playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
url_by_name = f"https://api.gametools.network/bf6/all/?format_values=true&name={player_name}&platform={platform}&skip_battlelog=false&lang=en-us"
|
|
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# 创建两个并发任务
|
|
task_id = asyncio.create_task(client.get(url_by_id))
|
|
task_name = asyncio.create_task(client.get(url_by_name))
|
|
|
|
# 等待第一个完成的任务
|
|
done, pending = await asyncio.wait(
|
|
[task_id, task_name],
|
|
return_when=asyncio.FIRST_COMPLETED
|
|
)
|
|
# 取消未完成的任务
|
|
for task in pending:
|
|
task.cancel()
|
|
# 检查已完成的任务是否成功且无 errors
|
|
for task in done:
|
|
try:
|
|
response = await task
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if 'errors' not in data:
|
|
logger.info(f"成功获取数据({'ID' if task is task_id else 'Name'}方式)")
|
|
return data
|
|
except (httpx.RequestError, ValueError) as e:
|
|
logger.warning(f"请求或解析失败: {e}")
|
|
if pending:
|
|
pass
|
|
# 如果所有尝试都失败,返回错误
|
|
return {"errors": ["未找到玩家"]}
|
|
|
|
|
|
async def get_player_info_by_name(user_name, platform):
|
|
user_info_url = f"https://api.gametools.network/bfglobal/player/?name={user_name}&platform={platform}&skip_battlelog=false"
|
|
|
|
payload = {}
|
|
headers = {
|
|
'accept': 'application/json'
|
|
}
|
|
logger.info(f"查询用户请求url:{user_info_url}")
|
|
response = requests.request("GET", user_info_url, headers=headers, data=payload)
|
|
|
|
user_info_json = json.loads(response.text)
|
|
return user_info_json
|
|
|
|
|
|
async def get_player_info_by_ea_id(player_id, user_id, platform):
|
|
url = f"https://api.gametools.network/bfglobal/player/?playerid={player_id}&oid={user_id}&platform={platform}&skip_battlelog=false"
|
|
|
|
payload = {}
|
|
headers = {
|
|
'accept': 'application/json'
|
|
}
|
|
|
|
response = requests.request("GET", url, headers=headers, data=payload)
|
|
|
|
user_info_json = json.loads(response.text)
|
|
return user_info_json
|
|
|
|
|
|
def normalize_name(name: str) -> str:
|
|
"""统一用户名格式(大小写 + 空格)"""
|
|
return (name or "").strip().lower()
|
|
|
|
|
|
def extract_rank(status: str) -> int:
|
|
"""从 status 中提取 Rank 数值"""
|
|
if not status:
|
|
return 0
|
|
match = re.search(r'Rank\s+(\d+)', status)
|
|
return int(match.group(1)) if match else 0
|
|
|
|
async def get_bf6_rank(player,player_id, user_id, platform):
|
|
url_by_name = f"https://api.gametools.network/bf6/profile/?name={player}&playerid=1234&nucleus_id=1234&platform=ea&skip_battlelog=true&lang=en-us"
|
|
url_by_id = f"https://api.gametools.network/bf6/profile/?name={player}&playerid=1234&nucleus_id=1234&platform=ea&skip_battlelog=true&lang=en-us"
|
|
payload = {}
|
|
headers = {
|
|
'accept': 'application/json'
|
|
}
|
|
|
|
response = requests.request("GET", url, headers=headers, data=payload)
|
|
result: Dict[str, Any] = response.json().get("playerProfiles")
|
|
logger.info(f"Tracker用户信息:{result}")
|
|
|
|
if not result or "errors" in result:
|
|
logger.warning(f"查询失败或返回错误: {player}")
|
|
return 0
|
|
|
|
data = result.get("data") or []
|
|
if not data:
|
|
logger.info(f"未搜索到用户: {player}")
|
|
return 0
|
|
|
|
norm_player = normalize_name(player)
|
|
|
|
# 评分函数(核心策略)
|
|
def score(r: Dict[str, Any]) -> int:
|
|
if not r or not r.get("status"):
|
|
return -1
|
|
|
|
name = normalize_name(r.get("platformUserHandle"))
|
|
platform = r.get("platformSlug")
|
|
|
|
s = 0
|
|
|
|
# 用户名匹配权重(最高优先级)
|
|
if name == norm_player:
|
|
s += 100
|
|
elif norm_player in name:
|
|
s += 50
|
|
|
|
# 平台权重(次优先级)
|
|
if platform == "origin":
|
|
s += 20
|
|
|
|
return s
|
|
|
|
# 选出最优 candidate
|
|
target: Optional[Dict[str, Any]] = max(data, key=score, default=None)
|
|
|
|
if not target or score(target) <= 0:
|
|
logger.info(f"未匹配到有效用户: {player}")
|
|
return 0
|
|
|
|
# 提取信息
|
|
name = target.get("platformUserHandle")
|
|
uid = target.get("titleUserId")
|
|
status = target.get("status", "")
|
|
|
|
rank = extract_rank(status)
|
|
|
|
user_info = {
|
|
"name": name,
|
|
"uid": uid,
|
|
"rank": rank,
|
|
"platform": target.get("platformSlug"),
|
|
}
|
|
|
|
logger.info(f"最终匹配用户: {user_info}")
|
|
|
|
return rank
|