mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-21 14:48:20 +00:00
✨ Add HTML rendering for /material
This commit is contained in:
parent
115532b698
commit
5f33c5fc3a
@ -3,7 +3,7 @@ from typing import List
|
||||
from core.base_service import BaseService
|
||||
from core.dependence.redisdb import RedisDB
|
||||
|
||||
__all__ = ["GameCache", "GameCacheForStrategy", "GameCacheForMaterial"]
|
||||
__all__ = ["GameCache", "GameCacheForStrategy"]
|
||||
|
||||
|
||||
class GameCache:
|
||||
@ -27,7 +27,3 @@ class GameCache:
|
||||
|
||||
class GameCacheForStrategy(BaseService.Component, GameCache):
|
||||
qname = "game:strategy"
|
||||
|
||||
|
||||
class GameCacheForMaterial(BaseService.Component, GameCache):
|
||||
qname = "game:material"
|
||||
|
@ -1,10 +1,10 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from core.base_service import BaseService
|
||||
from core.services.game.cache import GameCacheForMaterial, GameCacheForStrategy
|
||||
from core.services.game.cache import GameCacheForStrategy
|
||||
from modules.apihelper.client.components.hyperion import Hyperion
|
||||
|
||||
__all__ = ("GameMaterialService", "GameStrategyService")
|
||||
__all__ = "GameStrategyService"
|
||||
|
||||
|
||||
class GameStrategyService(BaseService):
|
||||
@ -50,52 +50,3 @@ class GameStrategyService(BaseService):
|
||||
artwork_info = await self._hyperion.get_post_info(2, post_id)
|
||||
await self._cache.set_url_list(character_name, artwork_info.image_urls)
|
||||
return artwork_info.image_urls[0]
|
||||
|
||||
|
||||
class GameMaterialService(BaseService):
|
||||
def __init__(self, cache: GameCacheForMaterial, collections: Optional[List[int]] = None):
|
||||
self._cache = cache
|
||||
self._hyperion = Hyperion()
|
||||
self._collections = [428421, 1362644] if collections is None else collections
|
||||
self._special = ["雷电将军", "珊瑚宫心海", "菲谢尔", "托马", "八重神子", "九条裟罗", "辛焱", "神里绫华"]
|
||||
|
||||
async def _get_material_from_hyperion(self, collection_id: int, character_name: str) -> int:
|
||||
post_id: int = -1
|
||||
post_full_in_collection = await self._hyperion.get_post_full_in_collection(collection_id)
|
||||
for post_data in post_full_in_collection["posts"]:
|
||||
topics = post_data["topics"]
|
||||
for topic in topics:
|
||||
if character_name == topic["name"]:
|
||||
post_id = int(post_data["post"]["post_id"])
|
||||
break
|
||||
if post_id != -1:
|
||||
break
|
||||
subject = post_data["post"]["subject"]
|
||||
if character_name in subject:
|
||||
post_id = int(post_data["post"]["post_id"])
|
||||
if post_id != -1:
|
||||
break
|
||||
return post_id
|
||||
|
||||
async def get_material(self, character_name: str) -> str:
|
||||
cache = await self._cache.get_url_list(character_name)
|
||||
if len(cache) >= 1:
|
||||
image_url_list = cache
|
||||
else:
|
||||
for collection_id in self._collections:
|
||||
post_id = await self._get_material_from_hyperion(collection_id, character_name)
|
||||
if post_id != -1:
|
||||
break
|
||||
else:
|
||||
return ""
|
||||
|
||||
artwork_info = await self._hyperion.get_post_info(2, post_id)
|
||||
image_url_list = artwork_info.image_urls
|
||||
if collection_id == 1362644 or character_name in self._special:
|
||||
image_url_list.pop(0)
|
||||
await self._cache.set_url_list(character_name, image_url_list)
|
||||
if len(image_url_list) == 0:
|
||||
return ""
|
||||
if len(image_url_list) == 1:
|
||||
return image_url_list[0]
|
||||
return image_url_list[1]
|
||||
|
@ -11,6 +11,7 @@ class Remote:
|
||||
BASE_URL = f"https://raw.githubusercontent.com/{RESOURCE_DEFAULT_PATH}"
|
||||
CALENDAR = f"{BASE_URL}calendar.json"
|
||||
BIRTHDAY = f"{BASE_URL}birthday.json"
|
||||
MATERIAL = f"{BASE_URL}roles_material.json"
|
||||
|
||||
@staticmethod
|
||||
async def get_remote_calendar() -> Dict[str, Dict]:
|
||||
@ -35,3 +36,15 @@ class Remote:
|
||||
return {}
|
||||
except HTTPError:
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
async def get_remote_material() -> Dict[str, List[str]]:
|
||||
"""获取云端角色材料"""
|
||||
try:
|
||||
async with AsyncClient() as client:
|
||||
req = await client.get(Remote.MATERIAL)
|
||||
if req.status_code == 200:
|
||||
return req.json()
|
||||
return {}
|
||||
except HTTPError:
|
||||
return {}
|
||||
|
28
modules/material/talent.py
Normal file
28
modules/material/talent.py
Normal file
@ -0,0 +1,28 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
class TalentMaterials:
|
||||
def __init__(self, amount: List[int]):
|
||||
self.amount = amount
|
||||
|
||||
def cal_materials(self) -> List[int]:
|
||||
"""
|
||||
:return: [摩拉,天赋书x3,怪物素材x3,皇冠,周本素材]
|
||||
"""
|
||||
cost = [0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
cost_list = [
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[12500, 3, 0, 0, 6, 0, 0, 0, 0],
|
||||
[17500, 0, 2, 0, 0, 3, 0, 0, 0],
|
||||
[25000, 0, 4, 0, 0, 4, 0, 0, 0],
|
||||
[30000, 0, 6, 0, 0, 6, 0, 0, 0],
|
||||
[37500, 0, 9, 0, 0, 9, 0, 0, 0],
|
||||
[120000, 0, 0, 4, 0, 0, 4, 0, 1],
|
||||
[260000, 0, 0, 6, 0, 0, 6, 0, 1],
|
||||
[450000, 0, 0, 12, 0, 0, 9, 0, 2],
|
||||
[700000, 0, 0, 16, 0, 0, 12, 1, 2],
|
||||
]
|
||||
for i in self.amount:
|
||||
for level in range(1, i):
|
||||
cost = list(map(lambda x: x[0] + x[1], zip(cost, cost_list[level])))
|
||||
return cost
|
@ -1,10 +1,17 @@
|
||||
import re
|
||||
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
|
||||
from telegram.constants import ChatAction, ParseMode
|
||||
from telegram.constants import ChatAction
|
||||
from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filters
|
||||
|
||||
from core.dependence.assets import AssetsService
|
||||
from core.plugin import Plugin, handler
|
||||
from core.services.game.services import GameMaterialService
|
||||
from core.services.template.services import TemplateService
|
||||
from metadata.genshin import MATERIAL_DATA
|
||||
from metadata.shortname import roleToName
|
||||
from modules.apihelper.client.components.remote import Remote
|
||||
from modules.material.talent import TalentMaterials
|
||||
from modules.wiki.character import Character
|
||||
from utils.log import logger
|
||||
|
||||
__all__ = ("MaterialPlugin",)
|
||||
@ -15,8 +22,184 @@ class MaterialPlugin(Plugin):
|
||||
|
||||
KEYBOARD = [[InlineKeyboardButton(text="查看角色培养素材列表并查询", switch_inline_query_current_chat="查看角色培养素材列表并查询")]]
|
||||
|
||||
def __init__(self, game_material_service: GameMaterialService = None):
|
||||
self.game_material_service = game_material_service
|
||||
def __init__(
|
||||
self,
|
||||
template_service: TemplateService,
|
||||
assets_service: AssetsService,
|
||||
):
|
||||
self.roles_material = {}
|
||||
self.assets_service = assets_service
|
||||
self.template_service = template_service
|
||||
|
||||
async def initialize(self):
|
||||
await self._refresh()
|
||||
|
||||
async def _refresh(self):
|
||||
self.roles_material = await Remote.get_remote_material()
|
||||
|
||||
async def _parse_material(self, data: dict, character_name: str, talent_level: str) -> dict:
|
||||
data = data["data"]
|
||||
if character_name not in data.keys():
|
||||
return {}
|
||||
character = self.assets_service.avatar(character_name)
|
||||
level_up_material = self.assets_service.material(data[character_name]["level_up_materials"])
|
||||
ascension_material = self.assets_service.material(data[character_name]["ascension_materials"])
|
||||
local_material = self.assets_service.material(data[character_name]["materials"][0])
|
||||
enemy_material = self.assets_service.material(data[character_name]["materials"][1])
|
||||
level_up_materials = [
|
||||
{
|
||||
"num": 46,
|
||||
"rarity": MATERIAL_DATA[str(level_up_material.id)]["rank"],
|
||||
"icon": (await level_up_material.icon()).as_uri(),
|
||||
"name": data[character_name]["level_up_materials"],
|
||||
},
|
||||
{
|
||||
"num": 419,
|
||||
"rarity": 4,
|
||||
"icon": (await self.assets_service.material(104003).icon()).as_uri(),
|
||||
"name": "大英雄的经验",
|
||||
},
|
||||
{
|
||||
"num": 1,
|
||||
"rarity": 2,
|
||||
"icon": (await ascension_material.icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(ascension_material.id)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 9,
|
||||
"rarity": 3,
|
||||
"icon": (await self.assets_service.material(ascension_material.id - 1).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(ascension_material.id - 1)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 9,
|
||||
"rarity": 4,
|
||||
"icon": (await self.assets_service.material(str(ascension_material.id - 2)).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(ascension_material.id - 2)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 6,
|
||||
"rarity": 5,
|
||||
"icon": (await self.assets_service.material(ascension_material.id - 3).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(ascension_material.id - 3)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 168,
|
||||
"rarity": MATERIAL_DATA[str(local_material.id)]["rank"],
|
||||
"icon": (await local_material.icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(local_material.id)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 18,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id)]["rank"],
|
||||
"icon": (await self.assets_service.material(enemy_material.id).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 30,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id + 1)]["rank"],
|
||||
"icon": (await self.assets_service.material(enemy_material.id + 1).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id + 1)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 36,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id + 2)]["rank"],
|
||||
"icon": (await self.assets_service.material(str(enemy_material.id + 2)).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id + 2)]["name"],
|
||||
},
|
||||
]
|
||||
talent_book = self.assets_service.material(f"「{data[character_name]['talent'][0]}」的教导")
|
||||
weekly_talent_material = self.assets_service.material(data[character_name]["talent"][1])
|
||||
talent_materials = [
|
||||
{
|
||||
"num": 9,
|
||||
"rarity": MATERIAL_DATA[str(talent_book.id)]["rank"],
|
||||
"icon": (await self.assets_service.material(talent_book.id).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(talent_book.id)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 63,
|
||||
"rarity": MATERIAL_DATA[str(talent_book.id + 1)]["rank"],
|
||||
"icon": (await self.assets_service.material(talent_book.id + 1).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(talent_book.id + 1)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 114,
|
||||
"rarity": MATERIAL_DATA[str(talent_book.id + 2)]["rank"],
|
||||
"icon": (await self.assets_service.material(str(talent_book.id + 2)).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(talent_book.id + 2)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 18,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id)]["rank"],
|
||||
"icon": (await self.assets_service.material(enemy_material.id).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 66,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id + 1)]["rank"],
|
||||
"icon": (await self.assets_service.material(enemy_material.id + 1).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id + 1)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 93,
|
||||
"rarity": MATERIAL_DATA[str(enemy_material.id + 2)]["rank"],
|
||||
"icon": (await self.assets_service.material(str(enemy_material.id + 2)).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(enemy_material.id + 2)]["name"],
|
||||
},
|
||||
{
|
||||
"num": 3,
|
||||
"rarity": 5,
|
||||
"icon": (await self.assets_service.material(104319).icon()).as_uri(),
|
||||
"name": "智识之冕",
|
||||
},
|
||||
{
|
||||
"num": 18,
|
||||
"rarity": MATERIAL_DATA[str(weekly_talent_material.id)]["rank"],
|
||||
"icon": (await self.assets_service.material(weekly_talent_material.id).icon()).as_uri(),
|
||||
"name": MATERIAL_DATA[str(weekly_talent_material.id)]["name"],
|
||||
},
|
||||
]
|
||||
|
||||
return {
|
||||
"bot_username": self.application.bot.username,
|
||||
"character": {
|
||||
"element": character.enka.element.name,
|
||||
"image": character.enka.images.banner.url,
|
||||
"name": character_name,
|
||||
"association": (await Character.get_by_name(character_name)).association.name,
|
||||
},
|
||||
"level_up_materials": level_up_materials,
|
||||
"talent_materials": talent_materials,
|
||||
"talent_level": talent_level,
|
||||
"talent_amount": TalentMaterials(list(map(int, talent_level.split("/")))).cal_materials(),
|
||||
}
|
||||
|
||||
async def render(self, character_name: str, talent_amount: str):
|
||||
if not self.roles_material:
|
||||
await self._refresh()
|
||||
data = await self._parse_material(self.roles_material, character_name, talent_amount)
|
||||
if not data:
|
||||
return
|
||||
return await self.template_service.render(
|
||||
"genshin/material/roles_material.html",
|
||||
data,
|
||||
{"width": 960, "height": 1460},
|
||||
full_page=True,
|
||||
ttl=7 * 24 * 60 * 60,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_valid(string: str):
|
||||
"""
|
||||
判断字符串是否符合`8/9/10`的格式并保证每个数字都在[1,10]
|
||||
"""
|
||||
return bool(
|
||||
re.match(r"^\d+/\d+/\d+$", string)
|
||||
and all(1 <= int(num) <= 10 for num in string.split("/"))
|
||||
and string != "1/1/1"
|
||||
and string != "10/10/10"
|
||||
)
|
||||
|
||||
@handler(CommandHandler, command="material", block=False)
|
||||
@handler(MessageHandler, filters=filters.Regex("^角色培养素材查询(.*)"), block=False)
|
||||
@ -26,6 +209,9 @@ class MaterialPlugin(Plugin):
|
||||
args = self.get_args(context)
|
||||
if len(args) >= 1:
|
||||
character_name = args[0]
|
||||
material_count = "8/8/8"
|
||||
if len(args) >= 2 and self._is_valid(args[1]):
|
||||
material_count = args[1]
|
||||
else:
|
||||
reply_message = await message.reply_text(
|
||||
"请回复你要查询的培养素材的角色名", reply_markup=InlineKeyboardMarkup(self.KEYBOARD)
|
||||
@ -35,8 +221,10 @@ class MaterialPlugin(Plugin):
|
||||
self.add_delete_message_job(reply_message)
|
||||
return
|
||||
character_name = roleToName(character_name)
|
||||
url = await self.game_material_service.get_material(character_name)
|
||||
if not url:
|
||||
logger.info("用户 %s[%s] 查询角色培养素材命令请求 || 参数 %s", user.full_name, user.id, character_name)
|
||||
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
|
||||
result = await self.render(character_name, material_count)
|
||||
if not result:
|
||||
reply_message = await message.reply_text(
|
||||
f"没有找到 {character_name} 的培养素材", reply_markup=InlineKeyboardMarkup(self.KEYBOARD)
|
||||
)
|
||||
@ -44,14 +232,4 @@ class MaterialPlugin(Plugin):
|
||||
self.add_delete_message_job(message)
|
||||
self.add_delete_message_job(reply_message)
|
||||
return
|
||||
logger.info("用户 %s[%s] 查询角色培养素材命令请求 || 参数 %s", user.full_name, user.id, character_name)
|
||||
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
|
||||
file_path = await self.download_resource(url, return_path=True)
|
||||
caption = "From 米游社 " f"查看 [原图]({url})"
|
||||
await message.reply_photo(
|
||||
photo=open(file_path, "rb"),
|
||||
caption=caption,
|
||||
filename=f"{character_name}.png",
|
||||
allow_sending_without_reply=True,
|
||||
parse_mode=ParseMode.MARKDOWN_V2,
|
||||
)
|
||||
await result.reply_photo(message)
|
||||
|
BIN
resources/fonts/SourceHanSerifCN-Heavy.woff
Normal file
BIN
resources/fonts/SourceHanSerifCN-Heavy.woff
Normal file
Binary file not shown.
363
resources/genshin/material/example.html
Normal file
363
resources/genshin/material/example.html
Normal file
@ -0,0 +1,363 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Title</title>
|
||||
<script src="../../js/tailwindcss-3.1.8.js"></script>
|
||||
<link href="../../styles/public.css" rel="stylesheet" type="text/css"/>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: siyuan;
|
||||
src: url(../../fonts/SourceHanSerifCN-Heavy.woff) format("woff");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 0 0.08em 0.1em #000, 0 0.1em 0.3em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.num {
|
||||
color: white;
|
||||
text-shadow: black 2px 0 0, black 0 2px 0, black -2px 0 0, black 0 -2px 0;
|
||||
}
|
||||
|
||||
.title-fieldset {
|
||||
border: none;
|
||||
border-top: 2px solid;
|
||||
}
|
||||
|
||||
.title-fieldset .inner {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-no-repeat bg-cover overflow-hidden" style="background-image: url('../player_card/img/bg-anemo.jpg')">
|
||||
<div class="relative round-l-3xl">
|
||||
<div
|
||||
class="absolute w-full h-full -left-1/4 -bottom-32 opacity-80 bg-no-repeat bg-center"
|
||||
style="background-image: url('https://enka.network/ui/UI_Gacha_AvatarImg_Wanderer.png'); background-size: auto 100%; transform: scale(3); "
|
||||
></div>
|
||||
<div class="relative">
|
||||
<div class="flex-1">
|
||||
<div class="text-7xl text-right text-neutral-200 text-shadow p-10 tracking-wide"
|
||||
style="font-family: siyuan,serif">
|
||||
<div class="mb-2 leading-relaxed">
|
||||
流浪者
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<div class="text-4xl">角色培养素材一览</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative bg-amber-50 text-center text-yellow-900 p-5 m-4 rounded-3xl text-3xl bg-no-repeat bg-center"
|
||||
style="background-image: url('img/sumeru.webp'); background-size: auto 65%;">
|
||||
<div class="font-shadow">
|
||||
<div class="">★等级突破★</div>
|
||||
<div class="text-left m-10">
|
||||
<span style="border-left: 8px solid;"> </span>
|
||||
<span> 角色90级升级材料</span>
|
||||
<div class="text-right -mt-1.5" style="float: right">
|
||||
<span>摩拉消耗</span>
|
||||
<span class="text-4xl">x2,092,530</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-5 font-shadow ml-14" style="justify-content: left;">
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative text-2xl mt-5 font-shadow">
|
||||
<div class="text-2xl text-gray-700" style="font-family: HYWH,serif">
|
||||
<span>突破至81级(其它材料数量不变): 摩拉 x</span>
|
||||
<span class="text-3xl">1,444,540 </span>
|
||||
<span>大英雄的经验</span>
|
||||
<span class="text-3xl">x257<br></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset class="title-fieldset bg-yellow-900 mt-6">
|
||||
<legend class="inner"></legend>
|
||||
</fieldset>
|
||||
|
||||
<div class="font-shadow">
|
||||
<div class="mt-4">★天赋升级★</div>
|
||||
<div class="text-left m-10">
|
||||
<span style="border-left: 8px solid;"> </span>
|
||||
<span> 满级天赋升级材料</span>
|
||||
<div class="text-right -mt-1.5" style="float: right">
|
||||
<span>摩拉消耗</span>
|
||||
<span class="text-4xl">x4,957,500</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-5 font-shadow ml-14" style="justify-content: left;">
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">20</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/5.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('../../assets/material/100021/icon.png'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">Philosophies of Resistance</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative text-center text-gray-700 text-2xl mt-5 font-shadow" style="font-family: HYWH,serif">
|
||||
<div style="display: flex;justify-content: center;align-content: center">
|
||||
<table class="table-auto" style="font-family: HYWH,serif">
|
||||
<caption>将角色天赋升至8/8/8时所需材料:</caption>
|
||||
<tr>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-1">「诤言」的教导 x9</td>
|
||||
<td class="px-6 py-1">「诤言」的指引 x63</td>
|
||||
<td class="px-6 py-1">「诤言」的哲学 x30</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-1">蕈兽孢子 x18</td>
|
||||
<td class="px-6 py-1">荧光孢粉 x66</td>
|
||||
<td class="px-6 py-1">孢囊晶尘 x30</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-1">万劫之真意 x6</td>
|
||||
<td class="px-6 py-1">摩拉 x1,507,500</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xl text-center my-5 num">
|
||||
Created by @{{ bot_username }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
resources/genshin/material/img/inazuma.webp
Normal file
BIN
resources/genshin/material/img/inazuma.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
BIN
resources/genshin/material/img/liyue.webp
Normal file
BIN
resources/genshin/material/img/liyue.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
resources/genshin/material/img/mondstadt.webp
Normal file
BIN
resources/genshin/material/img/mondstadt.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
resources/genshin/material/img/sumeru.webp
Normal file
BIN
resources/genshin/material/img/sumeru.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
167
resources/genshin/material/roles_material.html
Normal file
167
resources/genshin/material/roles_material.html
Normal file
@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Title</title>
|
||||
<script src="../../js/tailwindcss-3.1.8.js"></script>
|
||||
<link href="../../styles/public.css" rel="stylesheet" type="text/css"/>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: siyuan;
|
||||
src: url(../../fonts/SourceHanSerifCN-Heavy.woff) format("woff");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 0 0.08em 0.1em #000, 0 0.1em 0.3em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.num {
|
||||
color: white;
|
||||
text-shadow: black 2px 0 0, black 0 2px 0, black -2px 0 0, black 0 -2px 0;
|
||||
}
|
||||
|
||||
.title-fieldset {
|
||||
border: none;
|
||||
border-top: 2px solid;
|
||||
}
|
||||
|
||||
.title-fieldset .inner {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-no-repeat bg-cover overflow-hidden" style="background-image: url('../player_card/img/bg-{{ character.element | lower }}.jpg')">
|
||||
<div class="relative round-l-3xl">
|
||||
<div
|
||||
class="absolute w-full h-full -left-1/4 -bottom-32 opacity-80 bg-no-repeat bg-center"
|
||||
style="background-image: url('{{ character.image }}'); background-size: auto 100%; transform: scale(3); "
|
||||
></div>
|
||||
<div class="relative">
|
||||
<div class="flex-1">
|
||||
<div class="text-7xl text-right text-neutral-200 text-shadow p-10 tracking-wide"
|
||||
style="font-family: siyuan,serif">
|
||||
<div class="mb-2 leading-relaxed">
|
||||
{{ character.name }}
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<div class="text-4xl">角色培养素材一览</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative bg-amber-50 text-center text-yellow-900 p-5 m-4 rounded-3xl text-3xl bg-no-repeat bg-center"
|
||||
style="background-image: url('img/{{ character.association | lower}}.webp'); background-size: auto 65%;">
|
||||
<div class="font-shadow">
|
||||
<div class="">★等级突破★</div>
|
||||
<div class="text-left m-10">
|
||||
<span style="border-left: 8px solid;"> </span>
|
||||
<span> 角色90级升级材料</span>
|
||||
<div class="text-right -mt-1.5" style="float: right">
|
||||
<span>摩拉消耗</span>
|
||||
<span class="text-4xl">x2,092,530</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-5 font-shadow ml-14" style="justify-content: left;">
|
||||
{% for material in level_up_materials %}
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">{{ material.num }}</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/{{ material.rarity }}.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('{{ material.icon }}'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">{{ material.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="relative text-2xl mt-5 font-shadow">
|
||||
<div class="text-2xl text-gray-700" style="font-family: HYWH,serif">
|
||||
<span>突破至81级(其它材料数量不变): 摩拉 x</span>
|
||||
<span class="text-3xl">1,444,540 </span>
|
||||
<span>大英雄的经验</span>
|
||||
<span class="text-3xl">x257<br></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset class="title-fieldset bg-yellow-900 mt-6">
|
||||
<legend class="inner"></legend>
|
||||
</fieldset>
|
||||
|
||||
<div class="font-shadow">
|
||||
<div class="mt-4">★天赋升级★</div>
|
||||
<div class="text-left m-10">
|
||||
<span style="border-left: 8px solid;"> </span>
|
||||
<span> 满级天赋升级材料</span>
|
||||
<div class="text-right -mt-1.5" style="float: right">
|
||||
<span>摩拉消耗</span>
|
||||
<span class="text-4xl">x4,957,500</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-5 font-shadow ml-14" style="justify-content: left;">
|
||||
{% for material in talent_materials %}
|
||||
<div class="text-center w-28">
|
||||
<div class="w-28 h-28">
|
||||
<div class="text-right text-2xl mr-1 num">{{ material.num }}</div>
|
||||
<div class="rounded-lg shadow-lg bg-contain bg-no-repeat"
|
||||
style="background-image: url('../../background/rarity/half/{{ material.rarity }}.png')">
|
||||
<div class="w-28 h-28 -my-8"
|
||||
style="background-image: url('{{ material.icon }}'); background-size: auto 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
<div style="word-wrap:break-word;">{{ material.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="relative text-center text-gray-700 text-2xl mt-5 font-shadow" style="font-family: HYWH,serif">
|
||||
<div style="display: flex;justify-content: center;align-content: center">
|
||||
<table class="table-auto" style="font-family: HYWH,serif">
|
||||
<caption>将角色天赋升至{{ talent_level }}时所需材料:</caption>
|
||||
<tr><th></th></tr>
|
||||
<tr>
|
||||
{% for i in range(1, 4) %}
|
||||
{% if talent_amount[i] > 0 %}
|
||||
<td class="px-6 py-1">{{ talent_materials[i - 1].name }} x{{ talent_amount[i] }}</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for i in range(4, 7) %}
|
||||
{% if talent_amount[i] > 0 %}
|
||||
<td class="px-6 py-1">{{ talent_materials[i - 1].name }} x{{ talent_amount[i] }}</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for i in range(7, 9) %}
|
||||
{% if talent_amount[i] > 0 %}
|
||||
<td class="px-6 py-1">{{ talent_materials[i - 1].name }} x{{ talent_amount[i] }}</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if talent_amount[0] > 0 %}
|
||||
<td class="px-6 py-1">摩拉 x{{ talent_amount[0] }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xl text-center my-5 num">
|
||||
Created by @{{ bot_username }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user