原神黄历改为PIL

This commit is contained in:
HibiKier 2022-12-31 17:15:14 +08:00
parent 2070014021
commit b0606feff3
6 changed files with 178 additions and 50 deletions

2
.gitignore vendored
View File

@ -163,3 +163,5 @@ configs/config.yaml
./.env.dev ./.env.dev
plugins/csgo_server/ plugins/csgo_server/
plugins/activity/ plugins/activity/
!/resources/image/genshin/alc/back.png
!/data/genshin_alc/

View File

@ -303,6 +303,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
* 修复epic报错优化简介 [@pull/1226](https://github.com/HibiKier/zhenxun_bot/pull/1226) * 修复epic报错优化简介 [@pull/1226](https://github.com/HibiKier/zhenxun_bot/pull/1226)
* 修复词条在某些回答下出错 * 修复词条在某些回答下出错
* 原神黄历改为PIL
### 2022/12/27 \[v0.1.6.6] ### 2022/12/27 \[v0.1.6.6]

View File

@ -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 import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from services.log import logger 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 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__ = "原神老黄历" __zx_plugin_name__ = "原神老黄历"
__plugin_usage__ = """ __plugin_usage__ = """
@ -35,25 +37,16 @@ Config.add_plugin_config(
default_value=True, default_value=True,
) )
almanac = on_command("原神黄历", priority=15, block=True) almanac = on_command("原神黄历", priority=5, block=True)
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
ALC_PATH.mkdir(parents=True, exist_ok=True)
@almanac.handle() @almanac.handle()
async def _(event: MessageEvent,): async def _(event: MessageEvent):
alc_img = await get_alc_image(ALC_PATH) await almanac.send(image(b64=await build_alc_image()))
if alc_img: logger.info(
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
await almanac.send(mes) f" 发送查看原神黄历"
logger.info( )
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看原神黄历"
)
else:
await almanac.send("黄历图片下载失败...")
@scheduler.scheduled_job( @scheduler.scheduled_job(
@ -67,9 +60,9 @@ async def _():
if bot: if bot:
gl = await bot.get_group_list() gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl] 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: if alc_img:
mes = "[[_task|genshin_alc]]" + alc_img + "\n ※ 黄历数据来源于 genshin.pub" mes = "[[_task|genshin_alc]]" + alc_img
for gid in gl: for gid in gl:
if group_manager.check_group_task_status(gid, "genshin_alc"): if group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message="" + mes) await bot.send_group_msg(group_id=int(gid), message="" + mes)

View File

@ -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()

View File

@ -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"
)

View File

@ -8,6 +8,8 @@ from io import BytesIO
from math import ceil from math import ceil
from pathlib import Path from pathlib import Path
from typing import List, Literal, Optional, Tuple, Union, Callable, Awaitable from typing import List, Literal, Optional, Tuple, Union, Callable, Awaitable
from PIL.ImageFont import FreeTypeFont
from nonebot.utils import is_coroutine_callable from nonebot.utils import is_coroutine_callable
import cv2 import cv2
@ -184,6 +186,7 @@ class BuildImage:
self._current_w = 0 self._current_w = 0
self._current_h = 0 self._current_h = 0
self.uid = uuid.uuid1() self.uid = uuid.uuid1()
self.font_name = font
self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size)) self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size))
if not plain_text and not color: if not plain_text and not color:
color = (255, 255, 255) color = (255, 255, 255)
@ -237,6 +240,17 @@ class BuildImage:
asyncio.set_event_loop(new_loop) asyncio.set_event_loop(new_loop)
self.loop = asyncio.get_event_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( async def apaste(
self, self,
img: "BuildImage" or Image, img: "BuildImage" or Image,
@ -379,6 +393,9 @@ class BuildImage:
text: str, text: str,
fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), fill: Union[str, Tuple[int, int, int]] = (0, 0, 0),
center_type: Optional[Literal["center", "by_height", "by_width"]] = None, 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 text: 文字内容
:param fill: 文字颜色 :param fill: 文字颜色
:param center_type: 居中类型可能的值 center: 完全居中by_width: 水平居中by_height: 垂直居中 :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( def text(
self, self,
@ -397,6 +416,9 @@ class BuildImage:
text: str, text: str,
fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), fill: Union[str, Tuple[int, int, int]] = (0, 0, 0),
center_type: Optional[Literal["center", "by_height", "by_width"]] = None, 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 text: 文字内容
:param fill: 文字颜色 :param fill: 文字颜色
:param center_type: 居中类型可能的值 center: 完全居中by_width: 水平居中by_height: 垂直居中 :param center_type: 居中类型可能的值 center: 完全居中by_width: 水平居中by_height: 垂直居中
:param font: 字体
:param font_size: 字体大小
""" """
if center_type: if center_type:
if center_type not in ["center", "by_height", "by_width"]: if center_type not in ["center", "by_height", "by_width"]:
@ -424,7 +448,12 @@ class BuildImage:
h = int((h - ttf_h) / 2) h = int((h - ttf_h) / 2)
w = pos[0] w = pos[0]
pos = (w, h) 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): async def asave(self, path: Optional[Union[str, Path]] = None):
""" """