- 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>
201 lines
8.6 KiB
Python
201 lines
8.6 KiB
Python
import datetime
|
|
import io
|
|
|
|
import cv2
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
from . import add_commas_with_padding, add_commas
|
|
from .data_utils import *
|
|
from .img_utils import *
|
|
from .param import *
|
|
|
|
# from img_utils import *
|
|
# from data_utils import *
|
|
# from param import *
|
|
|
|
filepath = os.path.dirname(__file__).replace("\\", "/")
|
|
|
|
# 字体
|
|
font_XXL = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 72)
|
|
font_XL = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 64)
|
|
font_L = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 48)
|
|
font_ML = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 42)
|
|
font_M = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 36)
|
|
font_MS = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 32)
|
|
font_S = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 28)
|
|
font_XS = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 24)
|
|
font_XXS = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 18)
|
|
font_XXXS = ImageFont.truetype(f"{filepath}/font/bf-sub-headline-bold 等宽数字.ttf", 16)
|
|
|
|
font_XXL_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 72)
|
|
font_XL_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 64)
|
|
font_L_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 48)
|
|
font_ML_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 42)
|
|
font_M_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 36)
|
|
font_MS_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 32)
|
|
font_S_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 28)
|
|
font_XS_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 24)
|
|
font_XXS_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 18)
|
|
font_XXXS_CH = ImageFont.truetype(f"{filepath}/font/演示创黑FLY.ttf", 16)
|
|
|
|
|
|
async def build_bf6_horizon_stat(game="bfv",
|
|
qq_id=2931,
|
|
player="Player001",
|
|
pid=1145141919,
|
|
rank=114,
|
|
kd=1.25,
|
|
kpm=2.3,
|
|
spm=500,
|
|
acc=25.6,
|
|
head_shots=10,
|
|
time_play=100,
|
|
kills=100,
|
|
kill_assists=150,
|
|
revives=114,
|
|
repairs=1000,
|
|
captured=300,
|
|
score=145674,
|
|
wins=10,
|
|
loses=10,
|
|
destroyed=514,
|
|
best_weapon="M1907 SF",
|
|
best_vehicle="坦克",
|
|
best_class="Support", ):
|
|
# 获取当前事件并且格式化
|
|
now = datetime.datetime.now()
|
|
formatted_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
# ---------------- OpenCV 部分 ----------------
|
|
# 载入模板
|
|
img = cv2.imread(f"{filepath}/template/horizon/{game}.png", cv2.IMREAD_COLOR)
|
|
|
|
# ---------------- 头像 ----------------
|
|
avatar = cv2.imread(f"{filepath}/avatar_cache/0.png", cv2.IMREAD_UNCHANGED)
|
|
avatar = cv2.resize(avatar, (134, 134), interpolation=cv2.INTER_AREA)
|
|
|
|
# 粘贴头像(保持原有坐标)
|
|
y, x = 65, 105
|
|
h, w = avatar.shape[:2]
|
|
img[y:y + h, x:x + w] = avatar
|
|
|
|
# ---------------- 勋章 ----------------
|
|
|
|
# ---------------- PIL 部分 ----------------
|
|
pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
|
draw = ImageDraw.Draw(pil_img)
|
|
|
|
# 玩家信息 105+160
|
|
draw.text((265, 80), f"{player}", fill="white", font=font_XL)
|
|
draw.text((265, 140), f"UID: {pid}", fill="#CCCCCC", font=font_L)
|
|
|
|
# 等级
|
|
pil_img = paste_rank_icon(int(rank), pil_img)
|
|
draw_centered_text(draw=draw, text=f"{rank}", x_left=950, x_right=1075, y=205,
|
|
font=font_M,
|
|
fill="white")
|
|
# draw.text((740, 212), f"{rank}", fill="white", font=font_M)
|
|
|
|
# 游玩时长
|
|
draw.text((2300, 350), f"{time_play:.1f} H", fill="white", font=font_L)
|
|
# 场次
|
|
draw.text((2300, 450), f"{wins + loses}", fill="white", font=font_L)
|
|
|
|
# 生涯总览
|
|
# 命中率
|
|
acc_format = round(float(acc.replace("%", "")), 1)
|
|
draw_centered_text(draw=draw, text=f"{acc_format}%", x_left=1210, x_right=1303, y=363,
|
|
font=font_M,
|
|
fill="white")
|
|
# 爆头率
|
|
if head_shots:
|
|
head_shots = head_shots.replace("%", "")
|
|
else:
|
|
head_shots = 0
|
|
draw_centered_text(draw=draw, text=f"{round(float(head_shots), 1)}%", x_left=1210, x_right=1303, y=458,
|
|
font=font_M,
|
|
fill="white")
|
|
# kd
|
|
draw.text((1350, 355), text=str(kd), fill="white", font=font_XXL)
|
|
# kpm
|
|
draw.text((1610, 355), text=f"{kpm:.2f}", fill="white", font=font_XXL)
|
|
# spm
|
|
draw.text((1870, 355), text=f"{int(spm)}", fill="white", font=font_XXL)
|
|
|
|
# ---------------- 数据 ----------------
|
|
# 击杀
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1165, x_right=1455, y=805,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(kills)), x_left=1165, x_right=1455, y=805,
|
|
font=font_L, fill="white")
|
|
|
|
# 急救
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1165, x_right=1455, y=1080,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(revives)), x_left=1165, x_right=1455, y=1080,
|
|
font=font_L, fill="white")
|
|
|
|
# 摧毁
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1165, x_right=1455, y=1350,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(destroyed)), x_left=1165, x_right=1455, y=1350,
|
|
font=font_L, fill="white")
|
|
|
|
# 右侧
|
|
# 占领
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1485, x_right=1780, y=745,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(captured)), x_left=1485, x_right=1780, y=745,
|
|
font=font_L, fill="white")
|
|
|
|
# 得分
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1485, x_right=1780, y=955,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(score)), x_left=1485, x_right=1780, y=955,
|
|
font=font_L, fill="white")
|
|
|
|
# 助攻
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1485, x_right=1780, y=1160,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(kill_assists)), x_left=1485, x_right=1780, y=1160,
|
|
font=font_L, fill="white")
|
|
|
|
# 修理
|
|
draw_right_aligned_text(draw=draw, text=add_commas_with_padding("0", 10), x_left=1485, x_right=1780, y=1370,
|
|
font=font_L,
|
|
fill="#444445")
|
|
draw_right_aligned_text(draw=draw, text=add_commas(str(repairs)), x_left=1485, x_right=1780, y=1370,
|
|
font=font_L, fill="white")
|
|
|
|
# 底部栏
|
|
|
|
# 最佳⭐
|
|
pil_img = build_best_bf6_horizon(draw, pil_img, best_weapon, best_vehicle, best_class, game)
|
|
|
|
# 生成时间
|
|
draw.text((1750, 10), f"{formatted_time}/战地中文社区/维护 SANSENHOSHI", fill=(154, 132, 149), font=font_XS_CH)
|
|
|
|
# ---------------- 转回 OpenCV ----------------
|
|
img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
|
|
|
|
# ---------------- 雷达图 ----------------
|
|
|
|
# 保存
|
|
success, buffer = cv2.imencode(".png", img)
|
|
if success:
|
|
b_io = io.BytesIO(buffer)
|
|
return b_io # 就可以直接 return 二进制流
|
|
else:
|
|
return None
|
|
|
|
|
|
def build_best_bf6_horizon(draw, pil_img, best_weapon, best_vehicle, best_class, game):
|
|
return 1
|