Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add HTML rendering for /material #508

Merged
merged 1 commit into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions core/services/game/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from core.base_service import BaseService
from core.dependence.redisdb import RedisDB

__all__ = ["GameCache", "GameCacheForStrategy", "GameCacheForMaterial"]
__all__ = ["GameCache", "GameCacheForStrategy"]


class GameCache:
Expand All @@ -27,7 +27,3 @@ async def set_url_list(self, character_name: str, str_list: List[str]):

class GameCacheForStrategy(BaseService.Component, GameCache):
qname = "game:strategy"


class GameCacheForMaterial(BaseService.Component, GameCache):
qname = "game:material"
53 changes: 2 additions & 51 deletions core/services/game/services.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -50,52 +50,3 @@ async def get_strategy(self, character_name: str) -> str:
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]
13 changes: 13 additions & 0 deletions modules/apihelper/client/components/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand All @@ -35,3 +36,15 @@ async def get_remote_birthday() -> Dict[str, List[str]]:
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 changes: 28 additions & 0 deletions modules/material/talent.py
Original file line number Diff line number Diff line change
@@ -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
212 changes: 195 additions & 17 deletions plugins/genshin/material.py
Original file line number Diff line number Diff line change
@@ -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",)
Expand All @@ -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)
Expand All @@ -26,6 +209,9 @@ async def command_start(self, update: Update, context: CallbackContext) -> None:
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)
Expand All @@ -35,23 +221,15 @@ async def command_start(self, update: Update, context: CallbackContext) -> None:
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)
)
if filters.ChatType.GROUPS.filter(reply_message):
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)
Binary file added resources/fonts/SourceHanSerifCN-Heavy.woff
Binary file not shown.
Loading