推荐三个深渊队伍

Co-authored-by: Li Chuangbo <im@chuangbo.li>
Co-authored-by: SiHuaN <sihuan@sakuya.love>
This commit is contained in:
xtaodada 2022-10-10 18:43:18 +08:00
parent a03e226448
commit c44b785118
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
5 changed files with 163 additions and 130 deletions

View File

@ -1,4 +1,3 @@
import secrets
import time
from typing import List, Optional, Any
@ -11,15 +10,19 @@ class Member(BaseModel):
attr: str
name: str
@validator("name")
def name_validator(cls, v): # pylint: disable=R0201
return "" if v == "旅行者" else v
class TeamRate(BaseModel):
rate: float
formation: List[Member]
owner_num: Optional[int]
@validator('rate', pre=True)
@validator("rate", pre=True)
def str2float(cls, v): # pylint: disable=R0201
return float(v.replace('%', '')) / 100.0 if isinstance(v, str) else v
return float(v.replace("%", "")) / 100.0 if isinstance(v, str) else v
class FullTeamRate(BaseModel):
@ -30,10 +33,11 @@ class FullTeamRate(BaseModel):
@property
def rate(self) -> float:
return self.up.rate + self.down.rate
return (self.up.rate + self.down.rate) / 2
class TeamRateResult(BaseModel):
version: str
rate_list_up: List[TeamRate]
rate_list_down: List[TeamRate]
rate_list_full: List[FullTeamRate] = []
@ -50,28 +54,35 @@ class TeamRateResult(BaseModel):
def sort(self, characters: List[str]):
for team in self.rate_list_full:
team.owner_num = sum(member.name in characters for member in team.up.formation + team.down.formation)
team.nice = team.owner_num / 8 * team.rate
team.nice = team.owner_num / 8 + team.rate
self.rate_list_full.sort(key=lambda x: x.nice, reverse=True)
def random_team(self, characters: List[str]) -> FullTeamRate:
self.sort(characters)
max_nice = self.rate_list_full[0].nice
nice_teams: List[FullTeamRate] = []
def random_team(self) -> List[FullTeamRate]:
data: List[FullTeamRate] = []
for team in self.rate_list_full:
if team.nice < max_nice:
break
nice_teams.append(team)
return secrets.choice(nice_teams)
add = True
for team_ in data:
if {member.name for member in team.up.formation} & {member.name for member in team_.up.formation}:
add = False
break
if {member.name for member in team.down.formation} & {member.name for member in team_.down.formation}:
add = False
break
if add:
data.append(team)
if len(data) >= 3:
break
return data
class AbyssTeamData:
TEAM_RATE_API = "https://www.youchuang.fun/gamerole/formationRate"
HEADERS = {
'Host': 'www.youchuang.fun',
'Referer': 'https://servicewechat.com/wxce4dbe0cb0f764b3/91/page-frame.html',
'User-Agent': 'Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) '
'Mobile/15E148 MicroMessenger/8.0.20(0x1800142f) NetType/WIFI Language/zh_CN',
'content-type': 'application/json'
"Host": "www.youchuang.fun",
"Referer": "https://servicewechat.com/wxce4dbe0cb0f764b3/91/page-frame.html",
"User-Agent": "Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) "
"Mobile/15E148 MicroMessenger/8.0.20(0x1800142f) NetType/WIFI Language/zh_CN",
"content-type": "application/json",
}
VERSION = "3.1"
@ -87,9 +98,12 @@ class AbyssTeamData:
data_up_json = data_up.json()["result"]
data_down = await self.client.post(self.TEAM_RATE_API, json={"version": self.VERSION, "layer": 2})
data_down_json = data_down.json()["result"]
self.data = TeamRateResult(rate_list_up=parse_obj_as(List[TeamRate], data_up_json["rateList"]),
rate_list_down=parse_obj_as(List[TeamRate], data_down_json["rateList"]),
user_count=data_up_json["userCount"])
self.data = TeamRateResult(
version=self.VERSION,
rate_list_up=parse_obj_as(List[TeamRate], data_up_json["rateList"]),
rate_list_down=parse_obj_as(List[TeamRate], data_down_json["rateList"]),
user_count=data_up_json["userCount"],
)
self.time = time.time()
return self.data.copy(deep=True)

View File

@ -1,4 +1,4 @@
from telegram import Update, User
from telegram import Update
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filters
@ -20,69 +20,67 @@ from utils.log import logger
class AbyssTeam(Plugin, BasePlugin):
"""深境螺旋推荐配队查询"""
def __init__(self, user_service: UserService = None, template_service: TemplateService = None,
assets: AssetsService = None):
def __init__(
self, user_service: UserService = None, template_service: TemplateService = None, assets: AssetsService = None
):
self.template_service = template_service
self.user_service = user_service
self.assets_service = assets
self.team_data = AbyssTeamData()
@staticmethod
def _get_role_star_bg(value: int):
if value == 4:
return "./../abyss/background/roleStarBg4.png"
elif value == 5:
return "./../abyss/background/roleStarBg5.png"
else:
raise ValueError("错误的数据")
@staticmethod
async def _get_data_from_user(user: User):
try:
logger.debug("尝试获取已绑定的原神账号")
client = await get_genshin_client(user.id)
logger.debug(f"获取成功, UID: {client.uid}")
characters = await client.get_genshin_characters(client.uid)
return [character.name for character in characters]
except (UserNotFoundError, CookiesNotFoundError):
logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息")
return []
@handler(CommandHandler, command="abyss_team", block=False)
@handler(MessageHandler, filters=filters.Regex("^深渊推荐配队(.*)"), block=False)
@restricts()
@error_callable
async def command_start(self, update: Update, _: CallbackContext) -> None:
async def command_start(self, update: Update, context: CallbackContext) -> None:
user = update.effective_user
message = update.effective_message
logger.info(f"用户 {user.full_name}[{user.id}] 查深渊推荐配队命令请求")
try:
client = await get_genshin_client(user.id)
except (CookiesNotFoundError, UserNotFoundError):
reply_message = await message.reply_text("未查询到账号信息,请先私聊派蒙绑定账号")
if filters.ChatType.GROUPS.filter(message):
self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id, 10)
self._add_delete_message_job(context, message.chat_id, message.message_id, 10)
return
await message.reply_chat_action(ChatAction.TYPING)
team_data = await self.team_data.get_data()
# 尝试获取用户已绑定的原神账号信息
user_data = await self._get_data_from_user(user)
random_team = team_data.random_team(user_data)
abyss_team_data = {
"up": [],
"down": []
}
for i in random_team.up.formation:
temp = {
"icon": (await self.assets_service.avatar(roleToId(i.name)).icon()).as_uri(),
"name": i.name,
"background": self._get_role_star_bg(i.star),
"hava": (i.name in user_data) if user_data else True,
characters = await client.get_genshin_characters(client.uid)
user_data = [character.name for character in characters]
team_data.sort(user_data)
random_team = team_data.random_team()
abyss_teams_data = {"uid": client.uid, "version": team_data.version, "teams": []}
for i in random_team:
team = {
"up": [],
"up_rate": f"{i.up.rate * 100: .2f}%",
"down": [],
"down_rate": f"{i.down.rate * 100: .2f}%",
}
abyss_team_data["up"].append(temp)
for i in random_team.down.formation:
temp = {
"icon": (await self.assets_service.avatar(roleToId(i.name)).icon()).as_uri(),
"name": i.name,
"background": self._get_role_star_bg(i.star),
"hava": (i.name in user_data) if user_data else True,
}
abyss_team_data["down"].append(temp)
for lane in ["up", "down"]:
for member in getattr(i, lane).formation:
temp = {
"icon": (await self.assets_service.avatar(roleToId(member.name)).icon()).as_uri(),
"name": member.name.replace("", "旅行者"),
"star": member.star,
"hava": (member.name in user_data) if user_data else True,
}
team[lane].append(temp)
abyss_teams_data["teams"].append(team)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
png_data = await self.template_service.render('genshin/abyss_team', "abyss_team.html", abyss_team_data,
{"width": 865, "height": 504}, full_page=False)
await message.reply_photo(png_data, filename=f"abyss_team_{user.id}.png",
allow_sending_without_reply=True)
png_data = await self.template_service.render(
"genshin/abyss_team",
"abyss_team.html",
abyss_teams_data,
{"width": 785, "height": 800},
full_page=True,
query_selector=".bg-contain",
)
await message.reply_photo(png_data, filename=f"abyss_team_{user.id}.png", allow_sending_without_reply=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

View File

@ -1,69 +1,90 @@
<!DOCTYPE html>
<html lang="zh-ch">
<head>
<meta charset="UTF-8">
<head>
<meta charset="UTF-8" />
<title>abyss</title>
<link type="text/css" href="../../styles/tailwind.min.css" rel="stylesheet">
<link type="text/css" href="../../styles/public.css" rel="stylesheet">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<script src="../../js/tailwindcss-3.1.8.js"></script>
<link type="text/css" href="../../styles/public.css" rel="stylesheet" />
<style>
#container {
width: 865px;
height: 504px;
background: url("./../abyss/background/lookback-bg.png");
background-size: cover;
}
#container {
max-width: 865px;
background-image: url("./../abyss/background/abyss-bg-grad.png");
background-color: rgb(11, 23, 44);
}
.character-icon {
width: 128px;
height: 128px;
}
.item-not-owned {
position: relative;
}
.character-side-icon {
width: 32px;
height: 32px;
}
.item-not-owned:before {
content: "";
position: absolute;
width: 128px;
height: 152px;
background-color: rgb(0 0 0 / 50%);
z-index: 2;
border-radius: 5px;
}
.item-not-owned::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgb(0 0 0 / 50%);
border-radius: 5px;
}
</style>
</head>
<body>
<div class="mx-auto flex flex-col h-full bg-no-repeat bg-cover" id="container">
<div class="title text-2xl my-4 text-yellow-500 mx-auto">深境螺旋 - 推荐配队</div>
<div class="base-info flex flex-col px-20 py-1 text-black my-1">
<div class="text-center mr-auto text-yellow-500">第 12 层上半</div>
<div class="mx-auto flex my-2">
{% for i in up %}
<div class="rounded-lg mx-2 overflow-hidden {% if not i.hava %}item-not-owned{% endif %}" style="background-color: rgb(233, 229, 220)">
<div class="character-icon rounded-br-3xl bg-cover overflow-hidden"
style="background: url({{i.background}});background-size: cover;">
<img src="{{i.icon}}" alt=""></div>
<div class="text-center">{{i.name}}</div>
</div>
{% endfor %}
</head>
<body>
<div
class="mx-auto flex flex-col h-full bg-contain bg-no-repeat py-6"
id="container"
>
<div class="title text-2xl my-4 text-yellow-500 text-center">
深境螺旋 - 推荐配队
</div>
<div
class="flex flex-row px-6 py-1 my-1 text-neutral-100 bg-white bg-opacity-10 justify-evenly text-sm"
>
<div>UID {{ uid }}</div>
<div>版本 {{ version }}</div>
</div>
{% for team in teams %}
<div class="mx-auto flex flex-col px-6 py-1 text-black my-1">
<div class="text-center mr-auto text-yellow-500">
推荐配队 {{ loop.index }}
</div>
</div>
<div class="base-info flex flex-col px-20 py-1 text-black my-1">
<div class="text-center mr-auto text-yellow-500">第 12 层下半</div>
<div class="mx-auto flex my-2">
{% for i in down %}
<div class="rounded-lg mx-2 overflow-hidden {% if not i.hava %}item-not-owned{% endif %}" style="background-color: rgb(233, 229, 220)">
<div class="character-icon rounded-br-3xl bg-cover overflow-hidden"
style="background: url({{i.background}});background-size: cover;">
<img src="{{i.icon}}" alt=""></div>
<div class="text-center">{{i.name}}</div>
{% for lane in ["up", "down"] %}
<div class="flex my-2 space-x-4">
{% for i in team[lane] %}
<div
class="bg-neutral-200 flex-shrink-0 rounded-lg overflow-hidden {% if not i.hava %}item-not-owned{% endif %}"
>
<div
class="w-32 h-32 rounded-br-2xl bg-cover overflow-hidden"
style="background-image: url('./../abyss/background/roleStarBg{{ i.star }}.png');"
>
<img src="{{ i.icon }}" alt="" />
</div>
{% endfor %}
<div class="text-center">{{ i.name }}</div>
</div>
{% endfor %}
<div>
<div class="text-neutral-300">
{{ lane == 'up' and '上' or '下' }}半
</div>
<div class="text-neutral-400 text-sm">
使用率 {{ team[lane + "_rate"] }}
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
<div class="mt-6 text-center p-1 text-neutral-400 text-xs">
数据来源:游创工坊
</div>
</div>
<div class="my-2"></div>
</div>
</body>
</body>
</html>

View File

@ -30,7 +30,7 @@ async def test_abyss_team_data(abyss_team_data: AbyssTeamData):
team_data.sort(["迪奥娜", "芭芭拉", "凯亚", ""])
assert isinstance(team_data.rate_list_full[0], FullTeamRate)
assert isinstance(team_data.rate_list_full[-1], FullTeamRate)
random_team = team_data.random_team(["迪奥娜", "芭芭拉", "凯亚", ""])
random_team = team_data.random_team()[0]
assert isinstance(random_team, FullTeamRate)
member_up = {i.name for i in random_team.up.formation}
member_down = {i.name for i in random_team.down.formation}