diff --git a/.gitignore b/.gitignore index 3bc4add0..9a886520 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,5 @@ configs/config.yaml ./.env.dev plugins/csgo_server/ plugins/activity/ +!/resources/image/genshin/alc/back.png +!/data/genshin_alc/ diff --git a/README.md b/README.md index 4836af52..9af219fd 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能 * 修复epic报错,优化简介 [@pull/1226](https://github.com/HibiKier/zhenxun_bot/pull/1226) * 修复词条在某些回答下出错 +* 原神黄历改为PIL ### 2022/12/27 \[v0.1.6.6] diff --git a/plugins/genshin/almanac/__init__.py b/plugins/genshin/almanac/__init__.py index 7a971297..a28a33ba 100755 --- a/plugins/genshin/almanac/__init__.py +++ b/plugins/genshin/almanac/__init__.py @@ -1,11 +1,13 @@ -from utils.utils import get_bot, scheduler +from configs.config import Config +from configs.path_config import IMAGE_PATH from nonebot import on_command from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent from services.log import logger -from configs.path_config import IMAGE_PATH -from .data_source import get_alc_image from utils.manager import group_manager -from configs.config import Config +from utils.message_builder import image +from utils.utils import get_bot, scheduler + +from ._data_source import build_alc_image __zx_plugin_name__ = "原神老黄历" __plugin_usage__ = """ @@ -35,25 +37,16 @@ Config.add_plugin_config( default_value=True, ) -almanac = on_command("原神黄历", priority=15, block=True) - - -ALC_PATH = IMAGE_PATH / "genshin" / "alc" -ALC_PATH.mkdir(parents=True, exist_ok=True) +almanac = on_command("原神黄历", priority=5, block=True) @almanac.handle() -async def _(event: MessageEvent,): - alc_img = await get_alc_image(ALC_PATH) - if alc_img: - mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" - await almanac.send(mes) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送查看原神黄历" - ) - else: - await almanac.send("黄历图片下载失败...") +async def _(event: MessageEvent): + await almanac.send(image(b64=await build_alc_image())) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送查看原神黄历" + ) @scheduler.scheduled_job( @@ -67,9 +60,9 @@ async def _(): if bot: gl = await bot.get_group_list() gl = [g["group_id"] for g in gl] - alc_img = await get_alc_image(ALC_PATH) + alc_img = image(b64=await build_alc_image()) if alc_img: - mes = "[[_task|genshin_alc]]" + alc_img + "\n ※ 黄历数据来源于 genshin.pub" + mes = "[[_task|genshin_alc]]" + alc_img for gid in gl: if group_manager.check_group_task_status(gid, "genshin_alc"): await bot.send_group_msg(group_id=int(gid), message="" + mes) diff --git a/plugins/genshin/almanac/_data_source.py b/plugins/genshin/almanac/_data_source.py new file mode 100644 index 00000000..15c5bc5f --- /dev/null +++ b/plugins/genshin/almanac/_data_source.py @@ -0,0 +1,129 @@ +import os +import random +from dataclasses import dataclass +from datetime import datetime +from typing import List, Tuple, Union + +import ujson as json +from configs.path_config import DATA_PATH, IMAGE_PATH +from utils.image_utils import BuildImage + +CONFIG_PATH = DATA_PATH / "genshin_alc" / "config.json" + +ALC_PATH = IMAGE_PATH / "genshin" / "alc" + +ALC_PATH.mkdir(exist_ok=True, parents=True) + +BACKGROUND_PATH = ALC_PATH / "back.png" + +chinese = { + "0": "十", + "1": "一", + "2": "二", + "3": "三", + "4": "四", + "5": "五", + "6": "六", + "7": "七", + "8": "八", + "9": "九", +} + + +@dataclass +class Fortune: + title: str + desc: str + + +def random_fortune() -> Tuple[List[Fortune], List[Fortune]]: + """ + 说明: + 随机运势 + """ + data = json.load(CONFIG_PATH.open("r", encoding="utf8")) + fortune_data = {} + good_fortune = [] + bad_fortune = [] + while len(fortune_data) < 6: + r = random.choice(list(data.keys())) + if r not in fortune_data: + fortune_data[r] = data[r] + for i, k in enumerate(fortune_data): + if i < 3: + good_fortune.append( + Fortune(title=k, desc=random.choice(fortune_data[k]["buff"])) + ) + else: + bad_fortune.append( + Fortune(title=k, desc=random.choice(fortune_data[k]["debuff"])) + ) + return good_fortune, bad_fortune + + +def int2cn(v: Union[str, int]): + """ + 说明: + 数字转中文 + 参数: + :param v: str + """ + return "".join([chinese[x] for x in str(v)]) + + +async def build_alc_image() -> str: + """ + 说明: + 构造今日运势图片 + """ + for file in os.listdir(ALC_PATH): + if file not in ["back.png", f"{datetime.now().date()}"]: + (ALC_PATH / file).unlink() + path = ALC_PATH / f"{datetime.now().date()}.png" + if path.exists(): + return BuildImage(0, 0, background=path).pic2bs4() + good_fortune, bad_fortune = random_fortune() + background = BuildImage( + 0, 0, background=BACKGROUND_PATH, font="HYWenHei-85W.ttf", font_size=30 + ) + now = datetime.now() + await background.atext((78, 145), str(now.year), fill="#8d7650ff") + month = str(now.month) + month_w = 358 + if now.month < 10: + month_w = 373 + elif now.month != 10: + month = "0" + month[-1] + await background.atext((month_w, 145), f"{int2cn(month)}月", fill="#8d7650ff") + day = str(now.day) + if now.day > 10 and day[-1] != "0": + day = day[0] + "0" + day[-1] + day_str = f"{int2cn(day)}日" + day_w = 193 + if (n := len(day_str)) == 3: + day_w = 207 + elif n == 2: + day_w = 228 + await background.atext( + (day_w, 145), f"{int2cn(day)}日", fill="#f7f8f2ff", font_size=35 + ) + fortune_h = 230 + for fortune in good_fortune: + await background.atext( + (150, fortune_h), fortune.title, fill="#756141ff", font_size=25 + ) + await background.atext( + (150, fortune_h + 28), fortune.desc, fill="#b5b3acff", font_size=19 + ) + fortune_h += 55 + fortune_h += 4 + for fortune in bad_fortune: + await background.atext( + (150, fortune_h), fortune.title, fill="#756141ff", font_size=25 + ) + await background.atext( + (150, fortune_h + 28), fortune.desc, fill="#b5b3acff", font_size=19 + ) + fortune_h += 55 + await background.asave(path) + return background.pic2bs4() diff --git a/plugins/genshin/almanac/data_source.py b/plugins/genshin/almanac/data_source.py deleted file mode 100755 index 4c0544f0..00000000 --- a/plugins/genshin/almanac/data_source.py +++ /dev/null @@ -1,26 +0,0 @@ -from utils.message_builder import image -from datetime import datetime -from pathlib import Path -from utils.http_utils import AsyncPlaywright -from nonebot.adapters.onebot.v11 import MessageSegment -from typing import Optional -import os - -url = "https://genshin.pub" - - -async def get_alc_image(path: Path) -> Optional[MessageSegment]: - """ - 截取黄历 - :param path: 存储路径 - """ - date = datetime.now().date() - for file in os.listdir(path): - if f"{date}.png" != file: - file = path / file - file.unlink() - if f"{date}.png" in os.listdir(path): - return image(f"{date}.png", "genshin/alc") - return await AsyncPlaywright.screenshot( - url, path / f"{date}.png", ".GSAlmanacs_gs_almanacs__3qT_A" - ) diff --git a/utils/image_utils.py b/utils/image_utils.py index 4f48d684..a3087b44 100755 --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -8,6 +8,8 @@ from io import BytesIO from math import ceil from pathlib import Path from typing import List, Literal, Optional, Tuple, Union, Callable, Awaitable + +from PIL.ImageFont import FreeTypeFont from nonebot.utils import is_coroutine_callable import cv2 @@ -184,6 +186,7 @@ class BuildImage: self._current_w = 0 self._current_h = 0 self.uid = uuid.uuid1() + self.font_name = font self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size)) if not plain_text and not color: color = (255, 255, 255) @@ -237,6 +240,17 @@ class BuildImage: asyncio.set_event_loop(new_loop) self.loop = asyncio.get_event_loop() + @classmethod + def load_font(cls, font: str, font_size: int) -> FreeTypeFont: + """ + 说明: + 加载字体 + 参数: + :param font: 字体名称 + :param font_size: 字体大小 + """ + return ImageFont.truetype(str(FONT_PATH / font), font_size) + async def apaste( self, img: "BuildImage" or Image, @@ -379,6 +393,9 @@ class BuildImage: text: str, fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), center_type: Optional[Literal["center", "by_height", "by_width"]] = None, + font: Union[FreeTypeFont, str] = None, + font_size: Optional[int] = None, + **kwargs ): """ 说明: @@ -388,8 +405,10 @@ class BuildImage: :param text: 文字内容 :param fill: 文字颜色 :param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中 + :param font: 字体 + :param font_size: 字体大小 """ - await self.loop.run_in_executor(None, self.text, pos, text, fill, center_type) + await self.loop.run_in_executor(None, self.text, pos, text, fill, center_type, font, font_size, **kwargs) def text( self, @@ -397,6 +416,9 @@ class BuildImage: text: str, fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), center_type: Optional[Literal["center", "by_height", "by_width"]] = None, + font: Union[FreeTypeFont, str] = None, + font_size: Optional[int] = None, + **kwargs ): """ 说明: @@ -406,6 +428,8 @@ class BuildImage: :param text: 文字内容 :param fill: 文字颜色 :param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中 + :param font: 字体 + :param font_size: 字体大小 """ if center_type: if center_type not in ["center", "by_height", "by_width"]: @@ -424,7 +448,12 @@ class BuildImage: h = int((h - ttf_h) / 2) w = pos[0] pos = (w, h) - self.draw.text(pos, text, fill=fill, font=self.font) + if font: + if isinstance(font, str): + font = self.load_font(font, font_size) + elif font_size: + font = self.load_font(self.font_name, font_size) + self.draw.text(pos, text, fill=fill, font=font or self.font, **kwargs) async def asave(self, path: Optional[Union[str, Path]] = None): """