mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
原神黄历改为PIL
This commit is contained in:
parent
2070014021
commit
b0606feff3
2
.gitignore
vendored
2
.gitignore
vendored
@ -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/
|
||||||
|
|||||||
@ -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]
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
129
plugins/genshin/almanac/_data_source.py
Normal file
129
plugins/genshin/almanac/_data_source.py
Normal 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()
|
||||||
@ -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"
|
|
||||||
)
|
|
||||||
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user