mirror of
https://github.com/PaiGramTeam/PamGram.git
synced 2024-11-21 13:48:19 +00:00
✨ 角色练度查询
This commit is contained in:
parent
4eace26e77
commit
b27f8af9a4
200
plugins/genshin/avatar_list.py
Normal file
200
plugins/genshin/avatar_list.py
Normal file
@ -0,0 +1,200 @@
|
||||
"""练度统计"""
|
||||
from typing import Iterable, List, Optional, Sequence
|
||||
|
||||
from arkowrapper import ArkoWrapper
|
||||
from enkanetwork import Assets as EnkaAssets, EnkaNetworkAPI
|
||||
from genshin import Client
|
||||
from genshin.models import CalculatorCharacterDetails, CalculatorTalent, Character
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputFile, Message, Update, User
|
||||
from telegram.constants import ChatAction, ParseMode
|
||||
from telegram.ext import CallbackContext, filters
|
||||
|
||||
from core.base.assets import AssetsService
|
||||
from core.baseplugin import BasePlugin
|
||||
from core.config import config
|
||||
from core.cookies.error import CookiesNotFoundError
|
||||
from core.cookies.services import CookiesService
|
||||
from core.plugin import Plugin, handler
|
||||
from core.template import TemplateService
|
||||
from core.user.error import UserNotFoundError
|
||||
from metadata.genshin import AVATAR_DATA, NAMECARD_DATA
|
||||
from modules.wiki.base import Model
|
||||
from utils.decorators.error import error_callable
|
||||
from utils.decorators.restricts import restricts
|
||||
from utils.helpers import get_genshin_client
|
||||
from utils.log import logger
|
||||
|
||||
|
||||
class AvatarListPlugin(Plugin, BasePlugin):
|
||||
def __init__(
|
||||
self, cookies_service: CookiesService, assets_service: AssetsService, template_service: TemplateService
|
||||
) -> None:
|
||||
self.cookies_service = cookies_service
|
||||
self.assets_service = assets_service
|
||||
self.template_service = template_service
|
||||
self.enka_client = EnkaNetworkAPI(lang="chs", agent=config.enka_network_api_agent)
|
||||
self.enka_assets = EnkaAssets(lang="chs")
|
||||
|
||||
async def get_user_client(self, user: User, message: Message, context: CallbackContext) -> Optional[Client]:
|
||||
try:
|
||||
return await get_genshin_client(user.id)
|
||||
except UserNotFoundError: # 若未找到账号
|
||||
if filters.ChatType.GROUPS.filter(message):
|
||||
buttons = [[InlineKeyboardButton("点我私聊", url=f"https://t.me/{context.bot.username}?start=set_uid")]]
|
||||
reply_msg = await message.reply_text(
|
||||
"未查询到您所绑定的账号信息,请先私聊派蒙绑定账号", reply_markup=InlineKeyboardMarkup(buttons)
|
||||
)
|
||||
self._add_delete_message_job(context, reply_msg.chat_id, reply_msg.message_id, 30)
|
||||
self._add_delete_message_job(context, message.chat_id, message.message_id, 30)
|
||||
else:
|
||||
await message.reply_text("未查询到您所绑定的账号信息,请先私聊派蒙绑定账号")
|
||||
except CookiesNotFoundError:
|
||||
if filters.ChatType.GROUPS.filter(message):
|
||||
buttons = [[InlineKeyboardButton("点我私聊", url=f"https://t.me/{context.bot.username}?start=set_uid")]]
|
||||
reply_msg = await message.reply_text(
|
||||
"此功能需要绑定<code>cookie</code>后使用,请先私聊派蒙绑定账号",
|
||||
reply_markup=InlineKeyboardMarkup(buttons),
|
||||
parse_mode=ParseMode.HTML,
|
||||
)
|
||||
self._add_delete_message_job(context, reply_msg.chat_id, reply_msg.message_id, 30)
|
||||
self._add_delete_message_job(context, message.chat_id, message.message_id, 30)
|
||||
else:
|
||||
await message.reply_text("此功能需要绑定<code>cookie</code>后使用,请先私聊派蒙进行绑定", parse_mode=ParseMode.HTML)
|
||||
|
||||
async def get_avatars_data(self, characters: Sequence[Character], client: Client, max_length: int = None):
|
||||
avatar_datas: List[AvatarData] = []
|
||||
for num, character in enumerate(characters):
|
||||
if num == max_length: # 若已经有 max_length 个角色
|
||||
break
|
||||
detail = await client.get_character_details(character)
|
||||
if character.id == 10000005: # 针对男草主
|
||||
talents = []
|
||||
for talent in detail.talents:
|
||||
if "普通攻击" in talent.name:
|
||||
talent.Config.allow_mutation = True
|
||||
# noinspection Pydantic
|
||||
talent.group_id = 1131
|
||||
if talent.type in ["attack", "skill", "burst"]:
|
||||
talents.append(talent)
|
||||
else:
|
||||
talents = [t for t in detail.talents if t.type in ["attack", "skill", "burst"]]
|
||||
buffed_talents = []
|
||||
for constellation in filter(lambda x: x.pos in [3, 5], character.constellations[: character.constellation]):
|
||||
if result := list(
|
||||
filter(lambda x: all([x.name in constellation.effect]), talents) # pylint: disable=W0640
|
||||
):
|
||||
buffed_talents.append(result[0].type)
|
||||
avatar_datas.append(
|
||||
AvatarData(
|
||||
avatar=character,
|
||||
detail=detail,
|
||||
icon=(await self.assets_service.avatar(character.id).side()).as_uri(),
|
||||
weapon=(
|
||||
await self.assets_service.weapon(character.weapon.id).__getattr__(
|
||||
"icon" if character.weapon.ascension < 2 else "awaken"
|
||||
)()
|
||||
).as_uri(),
|
||||
skills=[
|
||||
SkillData(skill=s, buffed=s.type in buffed_talents)
|
||||
for s in sorted(talents, key=lambda x: ["attack", "skill", "burst"].index(x.type))
|
||||
],
|
||||
)
|
||||
)
|
||||
return avatar_datas
|
||||
|
||||
async def get_final_data(self, client: Client, characters: Sequence[Character], update: Update):
|
||||
try:
|
||||
response = await self.enka_client.fetch_user(client.uid)
|
||||
namecard = (await self.assets_service.namecard(response.player.namecard.id).navbar()).as_uri()
|
||||
avatar = (await self.assets_service.avatar(response.player.icon.id).icon()).as_uri()
|
||||
nickname = response.player.nickname
|
||||
rarity = {k: v["rank"] for k, v in AVATAR_DATA.items()}[str(response.player.icon.id)]
|
||||
except Exception as e: # pylint: disable=W0703
|
||||
logger.debug(f"enka 请求失败: {e}")
|
||||
choices = ArkoWrapper(characters).filter(lambda x: x.friendship == 10) # 筛选出好感满了的角色
|
||||
if not choices: # 若没有满好感角色、则以好感等级排序
|
||||
choices = ArkoWrapper(characters).sort(lambda x: x.friendship, reverse=True)
|
||||
namecard_choices = ( # 找到与角色对应的满好感名片ID
|
||||
ArkoWrapper(choices)
|
||||
.map(lambda x: next(filter(lambda y: y["name"].split(".")[0] == x.name, NAMECARD_DATA.values()), None))
|
||||
.filter(lambda x: x)
|
||||
.map(lambda x: x["id"])
|
||||
)
|
||||
namecard = (await self.assets_service.namecard(namecard_choices[0]).navbar()).as_uri()
|
||||
avatar = (await self.assets_service.avatar(cid := choices[0].id).icon()).as_uri()
|
||||
nickname = update.effective_user.full_name
|
||||
rarity = {k: v["rank"] for k, v in AVATAR_DATA.items()}[str(cid)]
|
||||
return namecard, avatar, nickname, rarity
|
||||
|
||||
@handler.command("avatars", filters.Regex(r"^/avatars\s*(?:(\d+)|(all))?$"))
|
||||
@handler.message(filters.Regex(r"^(全部)?练度统计$"))
|
||||
@restricts(30)
|
||||
@error_callable
|
||||
async def avatar_list(self, update: Update, context: CallbackContext):
|
||||
user = update.effective_user
|
||||
message = update.effective_message
|
||||
|
||||
args = context.match
|
||||
|
||||
all_avatars = any(["all" in args.groups(), "全部" in args.groups()]) # 是否发送全部角色
|
||||
|
||||
logger.info(f"用户 {user.full_name}[{user.id}] [bold]练度统计[/bold]: all={all_avatars}", extra={"markup": True})
|
||||
|
||||
client = await self.get_user_client(user, message, context)
|
||||
if not client:
|
||||
return
|
||||
|
||||
notice = await message.reply_text("派蒙需要收集整理数据,还请耐心等待哦~")
|
||||
await message.reply_chat_action(ChatAction.TYPING)
|
||||
|
||||
characters = await client.get_genshin_characters(client.uid)
|
||||
|
||||
avatar_datas: List[AvatarData] = await self.get_avatars_data(characters, client, None if all_avatars else 20)
|
||||
|
||||
namecard, avatar, nickname, rarity = await self.get_final_data(client, characters, update)
|
||||
|
||||
render_data = {
|
||||
"uid": client.uid, # 玩家uid
|
||||
"nickname": nickname, # 玩家昵称
|
||||
"avatar": avatar, # 玩家头像
|
||||
"rarity": rarity, # 玩家头像对应的角色星级
|
||||
"namecard": namecard, # 玩家名片
|
||||
"avatar_datas": avatar_datas, # 角色数据
|
||||
"has_more": len(characters) != len(avatar_datas), # 是否显示了全部角色
|
||||
}
|
||||
|
||||
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT if all_avatars else ChatAction.UPLOAD_PHOTO)
|
||||
|
||||
image = await self.template_service.render(
|
||||
"genshin/avatar_list/main.html",
|
||||
render_data,
|
||||
viewport={"width": 1040, "height": 500},
|
||||
full_page=True,
|
||||
query_selector=".container",
|
||||
)
|
||||
self._add_delete_message_job(context, notice.chat_id, notice.message_id, 5)
|
||||
if all_avatars and len(characters) > 20:
|
||||
await message.reply_document(InputFile(image, filename="练度统计.png"))
|
||||
else:
|
||||
await message.reply_photo(image)
|
||||
|
||||
logger.info(
|
||||
f"用户 {user.full_name}[{user.id}] [bold]练度统计[/bold]发送{'文件' if all_avatars else '图片'}成功",
|
||||
extra={"markup": True},
|
||||
)
|
||||
|
||||
|
||||
class SkillData(Model):
|
||||
"""天赋数据"""
|
||||
|
||||
skill: CalculatorTalent
|
||||
buffed: bool = False
|
||||
"""是否得到了命座加成"""
|
||||
|
||||
|
||||
class AvatarData(Model):
|
||||
avatar: Character
|
||||
detail: CalculatorCharacterDetails
|
||||
icon: str
|
||||
weapon: str
|
||||
skills: Iterable[SkillData]
|
24
poetry.lock
generated
24
poetry.lock
generated
@ -159,7 +159,7 @@ python-versions = ">=3.5"
|
||||
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
|
||||
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "backports.zoneinfo"
|
||||
@ -234,7 +234,7 @@ optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
|
||||
[package.extras]
|
||||
unicode-backport = ["unicodedata2"]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
@ -366,7 +366,7 @@ python-versions = ">=3.7"
|
||||
[[package]]
|
||||
name = "genshin"
|
||||
version = "1.2.4"
|
||||
description = ""
|
||||
description = "An API wrapper for Genshin Impact."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
@ -386,7 +386,7 @@ geetest = ["rsa"]
|
||||
type = "git"
|
||||
url = "https://github.com/thesadru/genshin.py"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "d6aa54384cbcb260adab1b221d0f8673ac0caf4a"
|
||||
resolved_reference = "7b3a4a71bfdf84d9f1bf984e91c0bcf73f9dfa7f"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
@ -960,19 +960,19 @@ aiomysql = ["aiomysql", "greenlet (!=0.4.17)"]
|
||||
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (!=0.4.17)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mssql_pymssql = ["pymssql"]
|
||||
mssql_pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
|
||||
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
mysql_connector = ["mysql-connector-python"]
|
||||
oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql_asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
|
||||
postgresql_psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql_psycopg2cffi = ["psycopg2cffi"]
|
||||
pymysql = ["pymysql", "pymysql (<1)"]
|
||||
sqlcipher = ["sqlcipher3_binary"]
|
||||
|
||||
|
144
resources/genshin/avatar_list/example.html
Normal file
144
resources/genshin/avatar_list/example.html
Normal file
@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Avatar List</title>
|
||||
<link type="text/css" href="./style.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="../../styles/public.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.avatar > div::after {
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-image: url("../../background/rarity/half/5.png");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="head" style="background-image: url('../../assets/namecard/210081/navbar.png')">
|
||||
<div class="avatar">
|
||||
<div><img src="../../assets/avatar/10000002/icon.png" alt="avatar"></div>
|
||||
</div>
|
||||
<div class="player">
|
||||
<div>
|
||||
<div class="nickname">Karako</div>
|
||||
<div class="uid">UID: 100206192</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logo"></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
<div>#</div>
|
||||
<div style="flex: 4">角色</div>
|
||||
<div>等级</div>
|
||||
<div>好感</div>
|
||||
<div>命座</div>
|
||||
<div class="talent">普攻</div>
|
||||
<div class="talent">战技</div>
|
||||
<div class="talent">爆发</div>
|
||||
<div style="flex: 6">武器</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div style="background-color: rgb(240 226 179)">1</div>
|
||||
|
||||
<div class="role" style="flex: 4;background-color: rgb(240 226 179)">
|
||||
<div class="role-icon" style="flex: 1.5;background-color: rgb(240 226 179)">
|
||||
<img src="../../assets/avatar/10000002/side.png" alt="side icon"/>
|
||||
</div>
|
||||
<div class="role-name" style="background-color: rgb(240 226 179);">神里绫华</div>
|
||||
</div>
|
||||
|
||||
<div>90</div>
|
||||
<div class="full-friendship">10</div>
|
||||
<div class="color red">
|
||||
<div class="number">6</div>
|
||||
</div>
|
||||
|
||||
<div class="talent red-bg talent-level-max talent-buffed">13</div>
|
||||
<div class="talent talent-level-max">13</div>
|
||||
<div class="talent talent-level-max">13</div>
|
||||
|
||||
<div class="weapon weapon-5-star" style="flex: 6">
|
||||
<div>Lv.90</div>
|
||||
<div class="color red">
|
||||
<div class="number">5</div>
|
||||
</div>
|
||||
<div><img src="../../assets/weapon/11509/awaken.png" alt="weapon"></div>
|
||||
<div>雾切之回光</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row second-row">
|
||||
<div style="background-color: rgb(240 226 179)">
|
||||
2
|
||||
</div>
|
||||
|
||||
<div class="role" style="flex: 4;background-color: rgb(240 226 179)">
|
||||
<div class="role-icon" style="flex: 1.5;">
|
||||
<img src="../../assets/avatar/10000042/side.png" alt="side icon"/>
|
||||
</div>
|
||||
<div class="role-name" style="flex: 2.5">刻晴</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>90</div>
|
||||
<div class="full-friendship">10</div>
|
||||
<div class="color purple">
|
||||
<div class="number">5</div>
|
||||
</div>
|
||||
|
||||
<div class="talent talent-level-max talent-buffed">13</div>
|
||||
<div class="talent talent-level-2 talent-buffed">13</div>
|
||||
<div class="talent talent-level-4 talent-buffed">13</div>
|
||||
|
||||
<div class="weapon weapon-4-star" style="flex: 6">
|
||||
<div>Lv.90</div>
|
||||
<div class="color red">
|
||||
<div class="number">5</div>
|
||||
</div>
|
||||
<div><img src="../../assets/weapon/11409/awaken.png" alt="weapon"></div>
|
||||
<div>黑剑</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div style="background-color: rgb(229 171 229/70%)">
|
||||
3
|
||||
</div>
|
||||
|
||||
<div class="role" style="flex: 4;background-color: rgb(240 226 179)">
|
||||
<div class="role-icon" style="flex: 1.5;">
|
||||
<img src="../../assets/avatar/10000016/side.png" alt="side icon"/>
|
||||
</div>
|
||||
<div class="role-name" style="flex: 2.5">迪卢克</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>90</div>
|
||||
<div class="full-friendship">10</div>
|
||||
<div class="color purple">
|
||||
<div class="number">5</div>
|
||||
</div>
|
||||
|
||||
<div class="talent talent-level-4">9</div>
|
||||
<div class="talent talent-level-4 talent-buffed">12</div>
|
||||
<div class="talent talent-level-4">9</div>
|
||||
|
||||
<div class="weapon weapon-5-star" style="flex: 6">
|
||||
<div>Lv.9 </div>
|
||||
<div class="color green">
|
||||
<div class="number">1</div>
|
||||
</div>
|
||||
<div><img src="../../assets/weapon/12502/awaken.png" alt="weapon"></div>
|
||||
<div>狼的末路</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice">
|
||||
<div>
|
||||
*想查看完整数据请在指令中加上<code>all</code>或者<code>全部</code>: <code>/avatars all</code>、<code>全部练度查询</code>
|
||||
</div>
|
||||
<div>※技能列表每 <span style="font-weight: bold">6</span> 个小时更新一次</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
157
resources/genshin/avatar_list/main.html
Normal file
157
resources/genshin/avatar_list/main.html
Normal file
@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Avatar List</title>
|
||||
<link type="text/css" href="./style.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="../../styles/public.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.avatar > div::after {
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-image: url("../../background/rarity/half/{{ rarity }}.png");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="head" style="background-image: url('{{ namecard }}')">
|
||||
<div class="avatar">
|
||||
<div><img src="{{ avatar }}" alt="avatar"></div>
|
||||
</div>
|
||||
<div class="player">
|
||||
<div>
|
||||
<div class="nickname">{{ nickname }}</div>
|
||||
<div class="uid">UID: {{ uid }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logo"></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
<div>#</div>
|
||||
<div style="flex: 4">角色</div>
|
||||
<div>等级</div>
|
||||
<div>好感</div>
|
||||
<div>命座</div>
|
||||
<div class="talent">普攻</div>
|
||||
<div class="talent">战技</div>
|
||||
<div class="talent">爆发</div>
|
||||
<div style="flex: 6">武器</div>
|
||||
</div>
|
||||
{% for avatar_data in avatar_datas %}
|
||||
{% set avatar = avatar_data.avatar %}
|
||||
{% set weapon = avatar.weapon %}
|
||||
{% set skill_datas = avatar_data.skills %}
|
||||
{% set is_traveler = avatar.name == '旅行者' %}
|
||||
{% if avatar.rarity == 5 %}
|
||||
{% set row_bg = 'rgb(240 226 179)' %}
|
||||
{% else %}
|
||||
{% set row_bg = 'rgb(229 171 229/70%)' %}
|
||||
{% endif %}
|
||||
<div
|
||||
{% if loop.index is even %}
|
||||
class="row second-row"
|
||||
{% else %}
|
||||
class="row"
|
||||
{% endif %}
|
||||
>
|
||||
<div style="background-color: {{ row_bg }}">{{ loop.index }}</div>
|
||||
|
||||
<div class="role" style="flex: 4;background-color: {{ row_bg }}">
|
||||
<div class="role-icon" style="flex: 1.5;">
|
||||
<img src="{{ avatar_data.icon }}" alt="side icon"/>
|
||||
</div>
|
||||
<div class="role-name">
|
||||
{% if is_traveler %}
|
||||
{% if avatar.id == 10000007 %}
|
||||
萤
|
||||
{% else %}
|
||||
空
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ avatar.name }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>{{ avatar.level }}</div>
|
||||
<div
|
||||
{% if avatar.friendship == 10 %}
|
||||
class="full-friendship"
|
||||
{% endif %}
|
||||
>
|
||||
{% if is_traveler %}
|
||||
/
|
||||
{% else %}
|
||||
{{ avatar.friendship }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div
|
||||
{% set constellation = avatar.constellation %}
|
||||
{% if constellation != 0 %}
|
||||
class="color {{ ['green', 'cyan', 'blue', 'purple', 'pink', 'red'][constellation - 1] }}"
|
||||
{% endif %}
|
||||
>
|
||||
<div class="number">{{ constellation }}</div>
|
||||
</div>
|
||||
|
||||
{% for skill_data in skill_datas %}
|
||||
{% set skill = skill_data.skill %}
|
||||
{% set talent_style = 'talent' %}
|
||||
{% set skill_level = skill.level %}
|
||||
|
||||
{% if skill_level < 4 %}
|
||||
{% set talent_style = talent_style + ' talent-level-first' %}
|
||||
{% endif %}
|
||||
|
||||
{% if skill.max_level == skill.level %}
|
||||
{% set talent_style = talent_style + ' talent-level-max' %}
|
||||
{% endif %}
|
||||
|
||||
{% if skill_data.buffed %}
|
||||
{% set talent_style = talent_style + ' talent-buffed' %}
|
||||
{% set skill_level = skill_level + 3 %}
|
||||
{% endif %}
|
||||
{% if skill.max_level != skill.level %}
|
||||
{% if skill_level < 4 %}
|
||||
{% set talent_style = talent_style + ' talent-level-1' %}
|
||||
{% elif skill_level < 6 %}
|
||||
{% set talent_style = talent_style + ' talent-level-2' %}
|
||||
{% elif skill_level < 9 %}
|
||||
{% set talent_style = talent_style + ' talent-level-3' %}
|
||||
{% else %}
|
||||
{% set talent_style = talent_style + ' talent-level-4' %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="{{ talent_style }}">{{ skill_level }}</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="weapon weapon-{{ weapon.rarity }}-star" style="flex: 6">
|
||||
<div>
|
||||
{% if weapon.level < 10 %}
|
||||
Lv.{{ weapon.level }}
|
||||
{% else %}
|
||||
Lv.{{ weapon.level }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="color {{ ['green', 'cyan', 'blue', 'purple', 'red'][weapon.refinement - 1] }}">
|
||||
<div class="number">{{ weapon.refinement }}</div>
|
||||
</div>
|
||||
<div><img src="{{ avatar_data.weapon }}" alt="weapon"></div>
|
||||
<div>{{ weapon.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="notice">
|
||||
{% if has_more %}
|
||||
<div>
|
||||
*想查看完整数据请在指令中加上<code>all</code>或者<code>全部</code>: <code>/avatars all</code>、<code>全部练度查询</code>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>※技能列表每 <span style="font-weight: bold">6</span> 个小时更新一次</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
429
resources/genshin/avatar_list/style.css
Normal file
429
resources/genshin/avatar_list/style.css
Normal file
@ -0,0 +1,429 @@
|
||||
:root {
|
||||
--white: rgb(246 248 249);
|
||||
--bg-color: rgb(233 229 220);
|
||||
--h-color: rgb(203 189 162);
|
||||
--red: rgb(255 86 33/ 80%);
|
||||
--pink: rgb(215 57 203/80%);
|
||||
--purple: rgb(159 68 211/80%);
|
||||
--blue: rgb(98 168 233/ 80%);
|
||||
--cyan: rgb(4 150 255/80%);
|
||||
--green: rgb(67 185 124/ 80%);
|
||||
--grey: rgb(189 191 190);
|
||||
}
|
||||
|
||||
.color::before {
|
||||
content: '';
|
||||
width: calc(1em + 3px);
|
||||
height: calc(1em + 12px);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 1;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
border-radius: 8px;
|
||||
box-shadow: 1px 1px 10px rgb(0 0 0/20%);
|
||||
}
|
||||
|
||||
.green::before {
|
||||
background-image: linear-gradient(135deg, rgb(129, 251, 184) 10%, rgb(40, 199, 111) 100%);
|
||||
}
|
||||
|
||||
.cyan::before {
|
||||
background-image: linear-gradient(135deg, rgb(144, 247, 236) 10%, rgb(50, 204, 188) 100%);
|
||||
}
|
||||
|
||||
.blue::before {
|
||||
background-image: linear-gradient(135deg, rgb(171, 220, 255) 10%, rgb(3, 150, 255) 100%);
|
||||
}
|
||||
|
||||
.purple::before {
|
||||
background-image: linear-gradient(135deg, rgb(206, 159, 252) 10%, rgb(115, 103, 240) 100%);
|
||||
}
|
||||
|
||||
.pink::before {
|
||||
background-image: linear-gradient(135deg, rgb(246, 206, 236) 10%, rgb(217, 57, 205) 100%);
|
||||
}
|
||||
|
||||
.red::before {
|
||||
background-image: linear-gradient(to top left, rgb(255, 8, 68) 0%, rgb(255, 177, 153) 100%);
|
||||
}
|
||||
|
||||
/* stylelint-disable */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: rgb(236, 236, 236);
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 1000px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container > div {
|
||||
box-shadow: 1px 1px 15px rgb(0 0 0 /60%);
|
||||
}
|
||||
|
||||
.head {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
margin-bottom: 40px;
|
||||
background-color: rgb(236, 229, 216);
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto calc(100% + 2px);
|
||||
background-position: 0 -1px;
|
||||
border-radius: 50px 100px 100px 50px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin: 0 60px 0 70px;
|
||||
filter: drop-shadow(1px 1px 10px rgb(0 0 0/50%));
|
||||
}
|
||||
|
||||
.avatar > div {
|
||||
width: inherit;
|
||||
height: 200%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
border-radius: 0 0 200px 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar > div::before {
|
||||
content: '';
|
||||
width: calc(100% - 6px);
|
||||
height: calc((100% / 2 - 6px) / 2);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 0 0 200px 200px;
|
||||
border-bottom: 3px solid var(--white);
|
||||
border-right: 3px solid var(--white);
|
||||
border-left: 3px solid var(--white);
|
||||
}
|
||||
|
||||
.avatar > div::after {
|
||||
content: '';
|
||||
width: calc(100% - 6px);
|
||||
height: calc(100% / 2 - 6px);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 50%;
|
||||
border-top: 3px solid var(--white);
|
||||
border-right: 3px solid var(--white);
|
||||
border-left: 3px solid var(--white);
|
||||
}
|
||||
|
||||
.avatar > div > img {
|
||||
width: inherit;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.player {
|
||||
text-shadow: 1px 1px 5px rgb(0 0 0/10%);
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 40px;
|
||||
font-weight: bolder;
|
||||
color: var(--white);
|
||||
text-shadow: 1px 1px 10px rgb(0 0 0/30%);
|
||||
}
|
||||
|
||||
.uid {
|
||||
font-size: 20px;
|
||||
color: var(--white);
|
||||
text-shadow: 1px 1px 10px rgb(0 0 0/30%);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: 8%;
|
||||
background-image: url("../../img/logo.png");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
filter: drop-shadow(5px 5px 10px rgb(0 0 0/50%));
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
background-color: var(--white);
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-items: center;
|
||||
overflow: hidden;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.second-row::before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
background-color: rgb(0 0 0/10%);
|
||||
}
|
||||
|
||||
.second-row > div:first-child::before,
|
||||
.second-row > div:nth-child(2)::before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
background-color: rgb(200 200 200 /30%);
|
||||
}
|
||||
|
||||
.content > .row:first-child {
|
||||
background-color: rgb(204, 204, 204);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.row > div {
|
||||
padding: 10px 0;
|
||||
height: calc(1em + 4px);
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-style: solid;
|
||||
border-width: 0 1px 1px 0;
|
||||
border-color: rgb(208, 208, 208);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.row > div:last-child {
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
.row > div:first-child:not(.content > .row:first-child > div:first-child),
|
||||
.row > div:nth-child(2):not(.content > .row:first-child > div:nth-child(2)) {
|
||||
border-right-color: rgb(203, 190, 148);
|
||||
border-left-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.number {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: rgb(102, 102, 102);
|
||||
}
|
||||
|
||||
.color > .number {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.role {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.role-icon {
|
||||
border-right-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
|
||||
.role-icon > img {
|
||||
height: calc(100% + 10px);
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
bottom: 4px;
|
||||
filter: drop-shadow(0 0 2px rgb(0 0 0/50%));
|
||||
}
|
||||
|
||||
.role-name {
|
||||
flex: 2.5 !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.weapon {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.weapon > div {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.weapon > div:has(.number) {
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
.weapon > div:first-child {
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.weapon > div:last-child {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.weapon > div:has(img) {
|
||||
filter: drop-shadow(1px 1px 2px rgb(0 0 0/80%));
|
||||
}
|
||||
|
||||
.weapon > div > img {
|
||||
height: 40px;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
.weapon-1-star {
|
||||
background-color: rgb(250 250 250);
|
||||
box-shadow: inset 0 0 10px 2px rgb(220 220 220);
|
||||
}
|
||||
|
||||
.weapon-2-star {
|
||||
background-color: rgb(250 250 250);
|
||||
box-shadow: inset 0 0 10px 2px rgb(195, 237, 183);
|
||||
}
|
||||
|
||||
.weapon-3-star {
|
||||
background-color: rgb(228, 237, 252);
|
||||
box-shadow: inset 0 0 10px 2px rgb(183, 190, 237);
|
||||
}
|
||||
|
||||
.weapon-4-star {
|
||||
background-color: rgb(250, 228, 241);
|
||||
box-shadow: inset 0 0 10px 2px rgb(233, 182, 221);
|
||||
}
|
||||
|
||||
.weapon-5-star {
|
||||
background-color: rgb(255, 246, 221);
|
||||
box-shadow: inset 0 0 10px 2px rgb(239, 215, 153);
|
||||
}
|
||||
|
||||
.full-friendship {
|
||||
color: var(--white);
|
||||
background-size: 85%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB0PSIxNjY1NzE2OTc4NTM2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI3MTEiCiAgICAgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiPgogICAgPHBhdGggZD0iTTUzMy41MDQgMjY4LjI4OHEzMy43OTItNDEuOTg0IDcxLjY4LTc1Ljc3NiAzMi43NjgtMjcuNjQ4IDc0LjI0LTUwLjE3NnQ4Ni41MjgtMTkuNDU2cTYzLjQ4OCA1LjEyIDEwNS45ODQgMzAuMjA4dDY3LjU4NCA2My40ODggMzQuMzA0IDg3LjA0IDYuMTQ0IDk5Ljg0LTE3LjkyIDk3Ljc5Mi0zNi44NjQgODcuMDQtNDguNjQgNzQuNzUyLTUzLjI0OCA2MS45NTJxLTQwLjk2IDQxLjk4NC04NS41MDQgNzguMzM2dC04NC45OTIgNjIuNDY0LTczLjcyOCA0MS40NzItNTEuNzEyIDE1LjM2cS0yMC40OCAxLjAyNC01Mi4yMjQtMTQuMzM2dC02OS42MzItNDEuNDcyLTc5Ljg3Mi02MS45NTItODIuOTQ0LTc1Ljc3NnEtMjYuNjI0LTI1LjYtNTcuMzQ0LTU5LjM5MnQtNTcuODU2LTc0LjI0LTQ2LjU5Mi04Ny41NTItMjEuNTA0LTEwMC4zNTIgMTEuMjY0LTk5Ljg0IDM5LjkzNi04My40NTYgNjUuNTM2LTYxLjk1MiA4OC4wNjQtMzUuMzI4cTI0LjU3Ni01LjEyIDQ5LjE1Mi0xLjUzNnQ0OC4xMjggMTIuMjg4IDQ1LjA1NiAyMi4wMTYgNDAuOTYgMjcuNjQ4cTQ1LjA1NiAzMy43OTIgODYuMDE2IDgwLjg5NnoiCiAgICAgICAgICBwLWlkPSIyNzEyIiBmaWxsPSIjZGUyOTEwIj48L3BhdGg+Cjwvc3ZnPg==);
|
||||
filter: drop-shadow(1px 1px 5px rgb(0 0 0/20%));
|
||||
}
|
||||
|
||||
.talent {
|
||||
position: absolute;
|
||||
background-size: contain, 1.6em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
text-shadow: 1px 1px 2px rgb(0 0 0 /20%);
|
||||
z-index: -1 !important;
|
||||
border-right-width: 0 !important;
|
||||
border-left-width: 0 !important;
|
||||
}
|
||||
|
||||
.talent-buffed {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.talent-level-first {
|
||||
background-color: rgb(189, 191, 190) !important;
|
||||
}
|
||||
|
||||
.talent-level-1 {
|
||||
background-color: rgb(189, 191, 190);
|
||||
}
|
||||
|
||||
.talent-level-first.talent-level-2.talent-buffed {
|
||||
color: rgb(0, 108, 199);
|
||||
}
|
||||
|
||||
.talent-level-2 {
|
||||
background-color: var(--green);
|
||||
}
|
||||
|
||||
.talent-level-3 {
|
||||
background-color: var(--blue);
|
||||
}
|
||||
|
||||
.talent-level-4 {
|
||||
background-color: rgb(190, 160, 250);
|
||||
}
|
||||
|
||||
.talent-level-max {
|
||||
background-image: linear-gradient(90deg, rgba(251, 129, 124, 0.8) 0%, rgba(255, 93, 85, 0.65) 50%, rgba(251, 129, 124, 0.8) 100%), url("../../img/crown.png") !important;
|
||||
}
|
||||
|
||||
.talent-level-1.talent-buffed {
|
||||
color: rgb(0, 108, 199);
|
||||
}
|
||||
|
||||
.talent-level-2.talent-buffed {
|
||||
color: rgb(0, 88, 0);
|
||||
}
|
||||
|
||||
.talent-level-3.talent-buffed {
|
||||
color: rgb(0, 108, 199);
|
||||
}
|
||||
|
||||
.talent-level-4.talent-buffed {
|
||||
color: rgb(114, 4, 101);
|
||||
}
|
||||
|
||||
.talent-level-max.talent-buffed {
|
||||
color: rgb(183, 0, 0) !important;
|
||||
text-shadow: 0 0 4px white !important;
|
||||
}
|
||||
|
||||
.content > .row:nth-last-child(2) > div {
|
||||
border-bottom-width: 0 !important;
|
||||
}
|
||||
|
||||
.notice {
|
||||
padding: 5px 0;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
background-color: rgb(204, 204, 204);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.notice > div {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.notice > div:last-child {
|
||||
text-align: right;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 5px;
|
||||
background-color: rgb(0 0 0/10%);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* stylelint-enable */
|
BIN
resources/img/crown.png
Normal file
BIN
resources/img/crown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
BIN
resources/img/logo.png
Normal file
BIN
resources/img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
Loading…
Reference in New Issue
Block a user