zhenxun_bot/zhenxun/plugins/draw_card/handles/guardian_handle.py
2024-07-28 03:37:37 +08:00

400 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import re
from datetime import datetime
from urllib.parse import unquote
import dateparser
import ujson as json
from lxml import etree
from nonebot_plugin_saa import Image, MessageFactory, Text
from PIL import ImageDraw
from pydantic import ValidationError
from zhenxun.services.log import logger
from zhenxun.utils.image_utils import BuildImage
from ..config import draw_config
from ..util import cn2py, load_font, remove_prohibited_str
from .base_handle import BaseData, BaseHandle, UpChar, UpEvent
class GuardianData(BaseData):
pass
class GuardianChar(GuardianData):
pass
class GuardianArms(GuardianData):
pass
class GuardianHandle(BaseHandle[GuardianData]):
def __init__(self):
super().__init__("guardian", "坎公骑冠剑")
self.data_files.append("guardian_arms.json")
self.config = draw_config.guardian
self.ALL_CHAR: list[GuardianChar] = []
self.ALL_ARMS: list[GuardianArms] = []
self.UP_CHAR: UpEvent | None = None
self.UP_ARMS: UpEvent | None = None
def get_card(self, pool_name: str, mode: int = 1) -> GuardianData:
if pool_name == "char":
if mode == 1:
star = self.get_star(
[3, 2, 1],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
self.config.GUARDIAN_ONE_CHAR_P,
],
)
else:
star = self.get_star(
[3, 2],
[
self.config.GUARDIAN_THREE_CHAR_P,
self.config.GUARDIAN_TWO_CHAR_P,
],
)
up_event = self.UP_CHAR
self.max_star = 3
all_data = self.ALL_CHAR
else:
if mode == 1:
star = self.get_star(
[5, 4, 3, 2],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
self.config.GUARDIAN_THREE_ARMS_P,
self.config.GUARDIAN_TWO_ARMS_P,
],
)
else:
star = self.get_star(
[5, 4],
[
self.config.GUARDIAN_FIVE_ARMS_P,
self.config.GUARDIAN_FOUR_ARMS_P,
],
)
up_event = self.UP_ARMS
self.max_star = 5
all_data = self.ALL_ARMS
acquire_char = None
# 是否UP
if up_event and star == self.max_star and pool_name:
# 获取up角色列表
up_list = [x.name for x in up_event.up_char if x.star == star]
# 成功获取up角色
if random.random() < 0.5:
up_name = random.choice(up_list)
try:
acquire_char = [x for x in all_data if x.name == up_name][0]
except IndexError:
pass
if not acquire_char:
chars = [x for x in all_data if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char
def get_cards(self, count: int, pool_name: str) -> list[tuple[GuardianData, int]]:
card_list = []
card_count = 0 # 保底计算
for i in range(count):
card_count += 1
# 十连保底
if card_count == 10:
card = self.get_card(pool_name, 2)
card_count = 0
else:
card = self.get_card(pool_name, 1)
if card.star > self.max_star - 2:
card_count = 0
card_list.append((card, i + 1))
return card_list
def format_pool_info(self, pool_name: str) -> str:
info = ""
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
if up_event:
if pool_name == "char":
up_list = [x.name for x in up_event.up_char if x.star == 3]
info += f'三星UP{" ".join(up_list)}\n'
else:
up_list = [x.name for x in up_event.up_char if x.star == 5]
info += f'五星UP{" ".join(up_list)}\n'
info = f"当前up池{up_event.title}\n{info}"
return info.strip()
async def draw(self, count: int, pool_name: str, **kwargs) -> MessageFactory:
index2card = self.get_cards(count, pool_name)
cards = [card[0] for card in index2card]
up_event = self.UP_CHAR if pool_name == "char" else self.UP_ARMS
up_list = [x.name for x in up_event.up_char] if up_event else []
result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info(pool_name)
img = await self.generate_img(cards)
return MessageFactory([Text(pool_info), Image(img.pic2bytes()), Text(result)])
async def generate_card_img(self, card: GuardianData) -> BuildImage:
sep_w = 1
sep_h = 1
block_w = 170
block_h = 90
img_w = 90
img_h = 90
if isinstance(card, GuardianChar):
block_color = "#2e2923"
font_color = "#e2ccad"
star_w = 90
star_h = 30
star_name = f"{card.star}_star.png"
frame_path = ""
else:
block_color = "#EEE4D5"
font_color = "#A65400"
star_w = 45
star_h = 45
star_name = f"{card.star}_star_rank.png"
frame_path = str(self.img_path / "avatar_frame.png")
bg = BuildImage(block_w + sep_w * 2, block_h + sep_h * 2, color="#F6F4ED")
block = BuildImage(block_w, block_h, color=block_color)
star_path = str(self.img_path / star_name)
star = BuildImage(star_w, star_h, background=star_path)
img_path = str(self.img_path / f"{cn2py(card.name)}.png")
img = BuildImage(img_w, img_h, background=img_path)
await block.paste(img, (0, 0))
if frame_path:
frame = BuildImage(img_w, img_h, background=frame_path)
await block.paste(frame, (0, 0))
await block.paste(
star,
(int((block_w + img_w - star_w) / 2), block_h - star_h - 30),
)
# 加名字
text = card.name[:4] + "..." if len(card.name) > 5 else card.name
font = load_font(fontsize=14)
text_w, _ = BuildImage.get_text_size(text, font)
draw = ImageDraw.Draw(block.markImg)
draw.text(
((block_w + img_w - text_w) / 2, 55),
text,
font=font,
fill=font_color,
)
await bg.paste(block, (sep_w, sep_h))
return bg
def _init_data(self):
self.ALL_CHAR = [
GuardianChar(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data().values()
]
self.ALL_ARMS = [
GuardianArms(name=value["名称"], star=int(value["星级"]), limited=False)
for value in self.load_data("guardian_arms.json").values()
]
self.load_up_char()
def load_up_char(self):
try:
data = self.load_data(f"draw_card_up/{self.game_name}_up_char.json")
self.UP_CHAR = UpEvent.parse_obj(data.get("char", {}))
self.UP_ARMS = UpEvent.parse_obj(data.get("arms", {}))
except ValidationError:
logger.warning(f"{self.game_name}_up_char 解析出错")
def dump_up_char(self):
if self.UP_CHAR and self.UP_ARMS:
data = {
"char": json.loads(self.UP_CHAR.json()),
"arms": json.loads(self.UP_ARMS.json()),
}
self.dump_data(data, f"draw_card_up/{self.game_name}_up_char.json")
async def _update_info(self):
# guardian.json
guardian_info = {}
url = "https://wiki.biligame.com/gt/英雄筛选表"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
# name = char.xpath("./td[1]/a/@title")[0]
# avatar = char.xpath("./td[1]/a/img/@src")[0]
# star = char.xpath("./td[1]/span/img/@alt")[0]
name = char.xpath("./th[1]/a[1]/@title")[0]
avatar = char.xpath("./th[1]/a/img/@src")[0]
star = char.xpath("./th[1]/span/img/@alt")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).split(" ")[0].replace("Rank", "")),
}
guardian_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_info)
logger.info(f"{self.game_name_cn} 更新成功")
# guardian_arms.json
guardian_arms_info = {}
url = "https://wiki.biligame.com/gt/武器"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 武器出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath("//table[@id='CardSelectTr']/tbody/tr")
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
url = char.xpath("./td[3]/img/@srcset")[0]
if r := re.search(r"Rank-mini-star_(\d).png", url):
star = r.group(1)
else:
continue
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 武器更新成功")
url = "https://wiki.biligame.com/gt/盾牌"
result = await self.get_url(url)
if not result:
logger.warning(f"更新 {self.game_name_cn} 盾牌出错")
else:
dom = etree.HTML(result, etree.HTMLParser())
char_list = dom.xpath(
"//div[@class='resp-tabs-container']/div[2]/div/table[1]/tbody/tr"
)
for char in char_list:
try:
name = char.xpath("./td[2]/a/@title")[0]
avatar = char.xpath("./td[1]/div/div/div/a/img/@src")[0]
star = char.xpath("./td[3]/text()")[0]
except IndexError:
continue
member_dict = {
"头像": unquote(str(avatar)),
"名称": remove_prohibited_str(name),
"星级": int(str(star).strip()),
}
guardian_arms_info[member_dict["名称"]] = member_dict
self.dump_data(guardian_arms_info, "guardian_arms.json")
logger.info(f"{self.game_name_cn} 盾牌更新成功")
# 下载头像
for value in guardian_info.values():
await self.download_img(value["头像"], value["名称"])
for value in guardian_arms_info.values():
await self.download_img(value["头像"], value["名称"])
# 下载星星
idx = 1
GT_URL = "https://patchwiki.biligame.com/images/gt"
for url in [
"/4/4b/ardr3bi2yf95u4zomm263tc1vke6i3i.png",
"/5/55/6vow7lh76gzus6b2g9cfn325d1sugca.png",
"/b/b9/du8egrd2vyewg0cuyra9t8jh0srl0ds.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star")
idx += 1
# 另一种星星
idx = 1
for url in [
"/6/66/4e2tfa9kvhfcbikzlyei76i9crva145.png",
"/1/10/r9ihsuvycgvsseyneqz4xs22t53026m.png",
"/7/7a/o0k86ru9k915y04azc26hilxead7xp1.png",
"/c/c9/rxz99asysz0rg391j3b02ta09mnpa7v.png",
"/2/2a/sfxz0ucv1s6ewxveycz9mnmrqs2rw60.png",
]:
await self.download_img(GT_URL + url, f"{idx}_star_rank")
idx += 1
# 头像框
await self.download_img(
GT_URL + "/8/8e/ogbqslbhuykjhnc8trtoa0p0nhfzohs.png", f"avatar_frame"
)
await self.update_up_char()
async def update_up_char(self):
url = "https://wiki.biligame.com/gt/首页"
result = await self.get_url(url)
if not result:
logger.warning(f"{self.game_name_cn}获取公告出错")
return
try:
dom = etree.HTML(result, etree.HTMLParser())
announcement = dom.xpath(
"//div[@class='mw-parser-output']/div/div[3]/div[2]/div/div[2]/div[3]"
)[0]
title = announcement.xpath("./font/p/b/text()")[0]
match = re.search(r"从(.*?)开始.*?至(.*?)结束", title)
if not match:
logger.warning(f"{self.game_name_cn}找不到UP时间")
return
start, end = match.groups()
start_time = dateparser.parse(start.replace("", "/").replace("", ""))
end_time = dateparser.parse(end.replace("", "/").replace("", ""))
if not (start_time and end_time) or not (
start_time <= datetime.now() <= end_time
):
return
divs = announcement.xpath("./font/div")
char_index = 0
arms_index = 0
for index, div in enumerate(divs):
if div.xpath("string(.)") == "角色":
char_index = index
elif div.xpath("string(.)") == "武器":
arms_index = index
chars = divs[char_index + 1 : arms_index]
arms = divs[arms_index + 1 :]
up_chars = []
up_arms = []
for char in chars:
name = char.xpath("./p/a/@title")[0]
up_chars.append(UpChar(name=name, star=3, limited=False, zoom=0))
for arm in arms:
name = arm.xpath("./p/a/@title")[0]
up_arms.append(UpChar(name=name, star=5, limited=False, zoom=0))
self.UP_CHAR = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_chars,
)
self.UP_ARMS = UpEvent(
title=title,
pool_img="",
start_time=start_time,
end_time=end_time,
up_char=up_arms,
)
self.dump_up_char()
logger.info(f"成功获取{self.game_name_cn}当前up信息...当前up池: {title}")
except Exception as e:
logger.warning(f"{self.game_name_cn}UP更新出错 {type(e)}{e}")
async def _reload_pool(self) -> MessageFactory | None:
await self.update_up_char()
self.load_up_char()
if self.UP_CHAR and self.UP_ARMS:
return MessageFactory(
[Text(f"重载成功!\n当前UP池子{self.UP_CHAR.title}")]
)