Support genshin Chronicled wish

This commit is contained in:
omg-xtao 2024-03-13 15:38:49 +08:00 committed by GitHub
parent fd2cee4767
commit 451a062d6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 200 additions and 34 deletions

View File

@ -1,6 +1,7 @@
from metadata.pool.pool_200 import POOL_200
from metadata.pool.pool_301 import POOL_301
from metadata.pool.pool_302 import POOL_302
from metadata.pool.pool_500 import POOL_500
def get_pool_by_id(pool_type):
@ -10,4 +11,6 @@ def get_pool_by_id(pool_type):
return POOL_301
if pool_type == 302:
return POOL_302
if pool_type == 500:
return POOL_500
return None

View File

@ -0,0 +1,9 @@
POOL_500 = [
{
"five": ["晨风之诗"],
"four": [],
"name": "晨风之诗",
"from": "2024-03-13 06:00:00",
"to": "2024-04-02 17:59:59",
},
]

View File

@ -1,7 +1,7 @@
from simnet.models.genshin.wish import BannerType
PAIMONMOE_VERSION = 3
UIGF_VERSION = "v2.4"
UIGF_VERSION = "v3.0"
GACHA_TYPE_LIST = {
@ -10,4 +10,6 @@ GACHA_TYPE_LIST = {
BannerType.WEAPON: "武器祈愿",
BannerType.CHARACTER1: "角色祈愿",
BannerType.CHARACTER2: "角色祈愿",
BannerType.CHRONICLED: "集录祈愿",
}
GACHA_TYPE_LIST_REVERSE = {v: k for k, v in GACHA_TYPE_LIST.items()}

View File

@ -189,6 +189,10 @@ class GachaLog:
new_num = 0
for item_info in all_items:
pool_name = GACHA_TYPE_LIST[BannerType(int(item_info.gacha_type))]
if pool_name not in temp_id_data:
temp_id_data[pool_name] = []
if pool_name not in gacha_log.item_list:
gacha_log.item_list[pool_name] = []
if item_info.id not in temp_id_data[pool_name]:
gacha_log.item_list[pool_name].append(item_info)
temp_id_data[pool_name].append(item_info.id)
@ -282,6 +286,10 @@ class GachaLog:
),
)
if pool_name not in temp_id_data:
temp_id_data[pool_name] = []
if pool_name not in gacha_log.item_list:
gacha_log.item_list[pool_name] = []
if item.id not in temp_id_data[pool_name]:
gacha_log.item_list[pool_name].append(item)
temp_id_data[pool_name].append(item.id)
@ -328,7 +336,7 @@ class GachaLog:
for item in data:
count += 1
if item.rank_type == "5":
if item.item_type == "角色" and pool_name in {"角色祈愿", "常驻祈愿"}:
if item.item_type == "角色" and pool_name in {"角色祈愿", "常驻祈愿", "集录祈愿"}:
data = {
"name": item.name,
"icon": (await assets.avatar(roleToId(item.name)).icon()).as_uri(),
@ -339,7 +347,7 @@ class GachaLog:
"time": item.time,
}
result.append(FiveStarItem.construct(**data))
elif item.item_type == "武器" and pool_name in {"武器祈愿", "常驻祈愿"}:
elif item.item_type == "武器" and pool_name in {"武器祈愿", "常驻祈愿", "集录祈愿"}:
data = {
"name": item.name,
"icon": (await assets.weapon(weaponToId(item.name)).icon()).as_uri(),
@ -489,6 +497,37 @@ class GachaLog:
],
]
@staticmethod
def get_500_pool_data(
total: int, all_five: List[FiveStarItem], all_four: List[FourStarItem], no_five_star: int, no_four_star: int
):
# 总共五星
five_star = len(all_five)
# 五星平均
five_star_avg = round((total - no_five_star) / five_star, 2) if five_star != 0 else 0
# 四星角色
four_star_character = len([i for i in all_four if i.type == "角色"])
# 总共四星
four_star = len(all_four)
# 四星平均
four_star_avg = round((total - no_four_star) / four_star, 2) if four_star != 0 else 0
# 四星最多
four_star_name_list = [i.name for i in all_four]
four_star_max = max(four_star_name_list, key=four_star_name_list.count) if four_star_name_list else ""
four_star_max_count = four_star_name_list.count(four_star_max)
return [
[
{"num": no_five_star, "unit": "", "lable": "未出五星"},
{"num": five_star, "unit": "", "lable": "五星"},
{"num": five_star_avg, "unit": "", "lable": "五星平均"},
{"num": four_star_character, "unit": "", "lable": "四星角色"},
{"num": no_four_star, "unit": "", "lable": "未出四星"},
{"num": four_star, "unit": "", "lable": "四星"},
{"num": four_star_avg, "unit": "", "lable": "四星平均"},
{"num": four_star_max_count, "unit": four_star_max, "lable": "四星最多"},
],
]
@staticmethod
def count_fortune(pool_name: str, summon_data, weapon: bool = False):
"""
@ -534,7 +573,7 @@ class GachaLog:
all_five, no_five_star = await self.get_all_5_star_items(data, assets, pool_name)
all_four, no_four_star = await self.get_all_4_star_items(data, assets)
summon_data = None
if pool == BannerType.CHARACTER1:
if pool in [BannerType.CHARACTER1, BannerType.CHARACTER2]:
summon_data = self.get_301_pool_data(total, all_five, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data)
elif pool == BannerType.WEAPON:
@ -543,6 +582,9 @@ class GachaLog:
elif pool == BannerType.PERMANENT:
summon_data = self.get_200_pool_data(total, all_five, all_four, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data)
elif pool == BannerType.CHRONICLED:
summon_data = self.get_500_pool_data(total, all_five, all_four, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data)
last_time = data[0].time.strftime("%Y-%m-%d %H:%M")
first_time = data[-1].time.strftime("%Y-%m-%d %H:%M")
return {

View File

@ -51,8 +51,8 @@ class GachaItem(BaseModel):
@validator("gacha_type")
def check_gacha_type(cls, v):
if v not in {"100", "200", "301", "302", "400"}:
raise ValueError(f"gacha_type must be 200, 301, 302 or 400, invalid value: {v}")
if v not in {"100", "200", "301", "302", "400", "500"}:
raise ValueError(f"gacha_type must be 200, 301, 302, 400, 500, invalid value: {v}")
return v
@validator("item_type")
@ -78,6 +78,7 @@ class GachaLogInfo(BaseModel):
"武器祈愿": [],
"常驻祈愿": [],
"新手祈愿": [],
"集录祈愿": [],
}
@property
@ -140,6 +141,7 @@ class UIGFGachaType(Enum):
CHARACTER = "301"
WEAPON = "302"
CHARACTER2 = "400"
CHRONICLED = "500"
class UIGFItem(BaseModel):

View File

@ -1,5 +1,5 @@
from io import BytesIO
from typing import Optional, TYPE_CHECKING, List
from typing import Optional, TYPE_CHECKING, List, Union, Tuple
from urllib.parse import urlencode
from aiofiles import open as async_open
@ -18,8 +18,9 @@ from core.services.cookies import CookiesService
from core.services.players import PlayersService
from core.services.template.models import FileType
from core.services.template.services import TemplateService
from gram_core.config import config
from metadata.scripts.paimon_moe import GACHA_LOG_PAIMON_MOE_PATH, update_paimon_moe_zh
from modules.gacha_log.const import UIGF_VERSION
from modules.gacha_log.const import UIGF_VERSION, GACHA_TYPE_LIST_REVERSE
from modules.gacha_log.error import (
GachaLogAccountNotFound,
GachaLogAuthkeyTimeout,
@ -32,6 +33,7 @@ from modules.gacha_log.error import (
from modules.gacha_log.helpers import from_url_get_authkey
from modules.gacha_log.log import GachaLog
from modules.gacha_log.migrate import GachaLogMigrate
from modules.gacha_log.models import GachaLogInfo
from plugins.tools.genshin import PlayerNotFoundError
from plugins.tools.player_info import PlayerInfoSystem
from utils.log import logger
@ -47,6 +49,7 @@ if TYPE_CHECKING:
from telegram import Update, Message, User, Document
from telegram.ext import ContextTypes
from gram_core.services.players.models import Player
from gram_core.services.template.models import RenderResult
INPUT_URL, INPUT_FILE, CONFIRM_DELETE = range(10100, 10103)
@ -78,6 +81,7 @@ class WishLogPlugin(Plugin.Conversation):
self.zh_dict = None
self.gacha_log = GachaLog()
self.player_info = player_info
self.wish_photo = None
async def initialize(self) -> None:
await update_paimon_moe_zh(False)
@ -321,7 +325,7 @@ class WishLogPlugin(Plugin.Conversation):
await message.reply_chat_action(ChatAction.TYPING)
path = await self.gacha_log.gacha_log_to_uigf(str(user.id), str(player_id))
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
await message.reply_document(document=open(path, "rb+"), caption="抽卡记录导出文件 - UIGF V2.3")
await message.reply_document(document=open(path, "rb+"), caption=f"抽卡记录导出文件 - UIGF {UIGF_VERSION}")
except GachaLogNotFound:
logger.info("未找到用户 %s[%s] 的抽卡记录", user.full_name, user.id)
buttons = [
@ -356,43 +360,102 @@ class WishLogPlugin(Plugin.Conversation):
}
await message.reply_text(f"{url}?{urlencode(params)}", disable_web_page_preview=True)
async def rander_wish_log_analysis(
self, user_id: int, player_id: int, pool_type: BannerType
) -> Union[str, "RenderResult"]:
data = await self.gacha_log.get_analysis(user_id, player_id, pool_type, self.assets_service)
if isinstance(data, str):
return data
name_card = await self.player_info.get_name_card(player_id, user_id)
data["name_card"] = name_card
png_data = await self.template_service.render(
"genshin/wish_log/wish_log.jinja2",
data,
full_page=True,
file_type=FileType.DOCUMENT if len(data.get("fiveLog")) > 300 else FileType.PHOTO,
query_selector=".body_box",
)
return png_data
@staticmethod
def gen_button(user_id: int, uid: int, info: "GachaLogInfo") -> List[List[InlineKeyboardButton]]:
buttons = []
pools = []
skip_pools = ["新手祈愿"]
for k, v in info.item_list.items():
if k in skip_pools:
continue
if not v:
continue
pools.append(k)
# 2 个一组
for i in range(0, len(pools), 2):
row = []
for pool in pools[i : i + 2]:
row.append(
InlineKeyboardButton(
pool,
callback_data=f"get_wish_log|{user_id}|{uid}|{pool}",
)
)
buttons.append(row)
return buttons
async def wish_log_pool_choose(self, user_id: int, message: "Message"):
await message.reply_chat_action(ChatAction.TYPING)
player_id = await self.get_player_id(user_id)
gacha_log, status = await self.gacha_log.load_history_info(str(user_id), str(player_id))
if not status:
raise GachaLogNotFound
buttons = self.gen_button(user_id, player_id, gacha_log)
if isinstance(self.wish_photo, str):
photo = self.wish_photo
else:
photo = open("resources/img/wish.jpg", "rb")
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
reply_message = await message.reply_photo(
photo=photo,
caption="请选择你要查询的卡池",
reply_markup=InlineKeyboardMarkup(buttons),
)
if reply_message.photo:
self.wish_photo = reply_message.photo[-1].file_id
async def wish_log_pool_send(self, user_id: int, uid: int, pool_type: "BannerType", message: "Message"):
await message.reply_chat_action(ChatAction.TYPING)
uid = await self.get_player_id(user_id)
png_data = await self.rander_wish_log_analysis(user_id, uid, pool_type)
if isinstance(png_data, str):
reply = await message.reply_text(png_data)
else:
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
reply = await png_data.reply_photo(message)
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply)
self.add_delete_message_job(message)
@handler.command(command="wish_log", block=False)
@handler.command(command="gacha_log", block=False)
@handler.message(filters=filters.Regex("^抽卡记录?(武器|角色|常驻|)$"), block=False)
async def command_start_analysis(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user_id = await self.get_real_user_id(update)
message = update.effective_message
pool_type = BannerType.CHARACTER1
pool_type = None
if args := self.get_args(context):
if "武器" in args:
if "角色" in args:
pool_type = BannerType.CHARACTER1
elif "武器" in args:
pool_type = BannerType.WEAPON
elif "常驻" in args:
pool_type = BannerType.STANDARD
self.log_user(update, logger.info, "抽卡记录命令请求 || 参数 %s", pool_type.name)
elif "集录" in args:
pool_type = BannerType.CHRONICLED
self.log_user(update, logger.info, "抽卡记录命令请求 || 参数 %s", pool_type.name if pool_type else None)
try:
player_id = await self.get_player_id(user_id)
await message.reply_chat_action(ChatAction.TYPING)
data = await self.gacha_log.get_analysis(user_id, player_id, pool_type, self.assets_service)
if isinstance(data, str):
reply_message = await message.reply_text(data)
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=300)
self.add_delete_message_job(message, delay=300)
if pool_type is None:
await self.wish_log_pool_choose(user_id, message)
else:
name_card = await self.player_info.get_name_card(player_id, user_id)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
data["name_card"] = name_card
png_data = await self.template_service.render(
"genshin/wish_log/wish_log.jinja2",
data,
full_page=True,
file_type=FileType.DOCUMENT if len(data.get("fiveLog")) > 300 else FileType.PHOTO,
query_selector=".body_box",
)
if png_data.file_type == FileType.DOCUMENT:
await png_data.reply_document(message, filename="抽卡记录.png")
else:
await png_data.reply_photo(message)
await self.wish_log_pool_send(user_id, user_id, pool_type, message)
except PlayerNotFoundError:
await message.reply_text("该用户暂未绑定账号")
except GachaLogNotFound:
@ -402,6 +465,45 @@ class WishLogPlugin(Plugin.Conversation):
]
await message.reply_text("派蒙没有找到你的抽卡记录,快来点击按钮私聊派蒙导入吧~", reply_markup=InlineKeyboardMarkup(buttons))
@handler.callback_query(pattern=r"^get_wish_log\|", block=False)
async def get_wish_log(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
callback_query = update.callback_query
user = callback_query.from_user
message = callback_query.message
async def get_wish_log_callback(
callback_query_data: str,
) -> Tuple[str, int, int]:
_data = callback_query_data.split("|")
_user_id = int(_data[1])
_uid = int(_data[2])
_result = _data[3]
logger.debug(
"callback_query_data函数返回 result[%s] user_id[%s] uid[%s]",
_result,
_user_id,
_uid,
)
return _result, _user_id, _uid
pool, user_id, uid = await get_wish_log_callback(callback_query.data)
if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True)
return
pool_type = GACHA_TYPE_LIST_REVERSE.get(pool)
await message.reply_chat_action(ChatAction.TYPING)
try:
png_data = await self.rander_wish_log_analysis(user_id, uid, pool_type)
except GachaLogNotFound:
png_data = "未找到抽卡记录"
if isinstance(png_data, str):
await callback_query.answer(png_data, show_alert=True)
self.add_delete_message_job(message, delay=1)
else:
await callback_query.answer(text="正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await png_data.edit_media(message)
@handler.command(command="wish_count", block=False)
@handler.command(command="gacha_count", block=False)
@handler.message(filters=filters.Regex("^抽卡统计?(武器|角色|常驻|仅五星|)$"), block=False)
@ -415,6 +517,8 @@ class WishLogPlugin(Plugin.Conversation):
pool_type = BannerType.WEAPON
elif "常驻" in args:
pool_type = BannerType.STANDARD
elif "集录" in args:
pool_type = BannerType.CHRONICLED
elif "仅五星" in args:
all_five = True
self.log_user(update, logger.info, "抽卡统计命令请求 || 参数 %s || 仅五星 %s", pool_type.name, all_five)

View File

@ -222,6 +222,10 @@ body {
background-color: #757cc8;
}
.label_500 {
background-color: #757cc8;
}
.label {
color: #fff;
border-radius: 10px;

BIN
resources/img/wish.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB