diff --git a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py deleted file mode 100644 index c2437128..00000000 --- a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from nonebot.plugin import PluginMetadata - -from liteyuki import get_bot -from .crt_matchers import * # type: ignore -from .rt_guide import * # type: ignore - -__plugin_meta__ = PluginMetadata( - - name="CRT生成工具", - description="一些CRT牌子生成器", - usage="我觉得你应该会用", - type="application", - homepage="https://github.com/snowykami/LiteyukiBot", - extra={ - "liteyuki" : True, - "toggleable" : True, - "default_enable": True, - } -) \ No newline at end of file diff --git a/src/nonebot_plugins/liteyuki_crt_utils/canvas.py b/src/nonebot_plugins/liteyuki_crt_utils/canvas.py deleted file mode 100644 index 02e6e3df..00000000 --- a/src/nonebot_plugins/liteyuki_crt_utils/canvas.py +++ /dev/null @@ -1,575 +0,0 @@ -import os -import uuid -from typing import Tuple, Union, List - -import nonebot -from PIL import Image, ImageFont, ImageDraw - -default_color = (255, 255, 255, 255) -default_font = "resources/fonts/MiSans-Semibold.ttf" - - -def render_canvas_from_json(file: str, background: Image) -> "Canvas": - pass - - -class BasePanel: - def __init__(self, - uv_size: Tuple[Union[int, float], Union[int, float]] = (1.0, 1.0), - box_size: Tuple[Union[int, float], Union[int, float]] = (1.0, 1.0), - parent_point: Tuple[float, float] = (0.5, 0.5), - point: Tuple[float, float] = (0.5, 0.5)): - """ - :param uv_size: 底面板大小 - :param box_size: 子(自身)面板大小 - :param parent_point: 底面板锚点 - :param point: 子(自身)面板锚点 - """ - self.canvas: Canvas | None = None - self.uv_size = uv_size - self.box_size = box_size - self.parent_point = parent_point - self.point = point - self.parent: BasePanel | None = None - self.canvas_box: Tuple[float, float, float, float] = (0.0, 0.0, 1.0, 1.0) - # 此节点在父节点上的盒子 - self.box = ( - self.parent_point[0] - self.point[0] * self.box_size[0] / self.uv_size[0], - self.parent_point[1] - self.point[1] * self.box_size[1] / self.uv_size[1], - self.parent_point[0] + (1 - self.point[0]) * self.box_size[0] / self.uv_size[0], - self.parent_point[1] + (1 - self.point[1]) * self.box_size[1] / self.uv_size[1] - ) - - def load(self, only_calculate=False): - """ - 将对象写入画布 - 此处仅作声明 - 由各子类重写 - - :return: - """ - self.actual_pos = self.canvas_box - - def save_as(self, canvas_box, only_calculate=False): - """ - 此函数执行时间较长,建议异步运行 - :param only_calculate: - :param canvas_box 此节点在画布上的盒子,并不是在父节点上的盒子 - :return: - """ - for name, child in self.__dict__.items(): - # 此节点在画布上的盒子 - if isinstance(child, BasePanel) and name not in ["canvas", "parent"]: - child.parent = self - if isinstance(self, Canvas): - child.canvas = self - else: - child.canvas = self.canvas - dxc = canvas_box[2] - canvas_box[0] - dyc = canvas_box[3] - canvas_box[1] - child.canvas_box = ( - canvas_box[0] + dxc * child.box[0], - canvas_box[1] + dyc * child.box[1], - canvas_box[0] + dxc * child.box[2], - canvas_box[1] + dyc * child.box[3] - ) - child.load(only_calculate) - child.save_as(child.canvas_box, only_calculate) - - -class Canvas(BasePanel): - def __init__(self, base_img: Image.Image): - self.base_img = base_img - self.canvas = self - super(Canvas, self).__init__() - self.draw_line_list = [] - - def export(self, file, alpha=False): - self.base_img = self.base_img.convert("RGBA") - self.save_as((0, 0, 1, 1)) - draw = ImageDraw.Draw(self.base_img) - for line in self.draw_line_list: - draw.line(*line) - if not alpha: - self.base_img = self.base_img.convert("RGB") - self.base_img.save(file) - - def delete(self): - os.remove(self.file) - - def get_actual_box(self, path: str) -> Union[None, Tuple[float, float, float, float]]: - """ - 获取控件实际相对大小 - 函数执行时间较长 - - :param path: 控件路径 - :return: - """ - sub_obj = self - self.save_as((0, 0, 1, 1), True) - control_path = "" - for i, seq in enumerate(path.split(".")): - if seq not in sub_obj.__dict__: - raise KeyError(f"在{control_path}中找不到控件:{seq}") - control_path += f".{seq}" - sub_obj = sub_obj.__dict__[seq] - return sub_obj.actual_pos - - def get_actual_pixel_size(self, path: str) -> Union[None, Tuple[int, int]]: - """ - 获取控件实际像素长宽 - 函数执行时间较长 - :param path: 控件路径 - :return: - """ - sub_obj = self - self.save_as((0, 0, 1, 1), True) - control_path = "" - for i, seq in enumerate(path.split(".")): - if seq not in sub_obj.__dict__: - raise KeyError(f"在{control_path}中找不到控件:{seq}") - control_path += f".{seq}" - sub_obj = sub_obj.__dict__[seq] - dx = int(sub_obj.canvas.base_img.size[0] * (sub_obj.actual_pos[2] - sub_obj.actual_pos[0])) - dy = int(sub_obj.canvas.base_img.size[1] * (sub_obj.actual_pos[3] - sub_obj.actual_pos[1])) - return dx, dy - - def get_actual_pixel_box(self, path: str) -> Union[None, Tuple[int, int, int, int]]: - """ - 获取控件实际像素大小盒子 - 函数执行时间较长 - :param path: 控件路径 - :return: - """ - sub_obj = self - self.save_as((0, 0, 1, 1), True) - control_path = "" - for i, seq in enumerate(path.split(".")): - if seq not in sub_obj.__dict__: - raise KeyError(f"在{control_path}中找不到控件:{seq}") - control_path += f".{seq}" - sub_obj = sub_obj.__dict__[seq] - x1 = int(sub_obj.canvas.base_img.size[0] * sub_obj.actual_pos[0]) - y1 = int(sub_obj.canvas.base_img.size[1] * sub_obj.actual_pos[1]) - x2 = int(sub_obj.canvas.base_img.size[2] * sub_obj.actual_pos[2]) - y2 = int(sub_obj.canvas.base_img.size[3] * sub_obj.actual_pos[3]) - return x1, y1, x2, y2 - - def get_parent_box(self, path: str) -> Union[None, Tuple[float, float, float, float]]: - """ - 获取控件在父节点的大小 - 函数执行时间较长 - - :param path: 控件路径 - :return: - """ - sub_obj = self.get_control_by_path(path) - on_parent_pos = ( - (sub_obj.actual_pos[0] - sub_obj.parent.actual_pos[0]) / (sub_obj.parent.actual_pos[2] - sub_obj.parent.actual_pos[0]), - (sub_obj.actual_pos[1] - sub_obj.parent.actual_pos[1]) / (sub_obj.parent.actual_pos[3] - sub_obj.parent.actual_pos[1]), - (sub_obj.actual_pos[2] - sub_obj.parent.actual_pos[0]) / (sub_obj.parent.actual_pos[2] - sub_obj.parent.actual_pos[0]), - (sub_obj.actual_pos[3] - sub_obj.parent.actual_pos[1]) / (sub_obj.parent.actual_pos[3] - sub_obj.parent.actual_pos[1]) - ) - return on_parent_pos - - def get_control_by_path(self, path: str) -> Union[BasePanel, "Img", "Rectangle", "Text"]: - sub_obj = self - self.save_as((0, 0, 1, 1), True) - control_path = "" - for i, seq in enumerate(path.split(".")): - if seq not in sub_obj.__dict__: - raise KeyError(f"在{control_path}中找不到控件:{seq}") - control_path += f".{seq}" - sub_obj = sub_obj.__dict__[seq] - return sub_obj - - def draw_line(self, path: str, p1: Tuple[float, float], p2: Tuple[float, float], color, width): - """ - 画线 - - :param color: - :param width: - :param path: - :param p1: - :param p2: - :return: - """ - ac_pos = self.get_actual_box(path) - control = self.get_control_by_path(path) - dx = ac_pos[2] - ac_pos[0] - dy = ac_pos[3] - ac_pos[1] - xy_box = int((ac_pos[0] + dx * p1[0]) * control.canvas.base_img.size[0]), int((ac_pos[1] + dy * p1[1]) * control.canvas.base_img.size[1]), int( - (ac_pos[0] + dx * p2[0]) * control.canvas.base_img.size[0]), int((ac_pos[1] + dy * p2[1]) * control.canvas.base_img.size[1]) - self.draw_line_list.append((xy_box, color, width)) - - -class Panel(BasePanel): - def __init__(self, uv_size, box_size, parent_point, point): - super(Panel, self).__init__(uv_size, box_size, parent_point, point) - - -class TextSegment: - def __init__(self, text, **kwargs): - if not isinstance(text, str): - raise TypeError("请输入字符串") - self.text = text - self.color = kwargs.get("color", None) - self.font = kwargs.get("font", None) - - @staticmethod - def text2text_segment_list(text: str): - """ - 暂时没写好 - - :param text: %FFFFFFFF%1123%FFFFFFFF%21323 - :return: - """ - pass - - -class Text(BasePanel): - def __init__(self, uv_size, box_size, parent_point, point, text: Union[str, list], font=default_font, color=(255, 255, 255, 255), vertical=False, - line_feed=False, force_size=False, fill=(0, 0, 0, 0), fillet=0, outline=(0, 0, 0, 0), outline_width=0, rectangle_side=0, font_size=None, dp: int = 5, - anchor: str = "la"): - """ - :param uv_size: - :param box_size: - :param parent_point: - :param point: - :param text: list[TextSegment] | str - :param font: - :param color: - :param vertical: 是否竖直 - :param line_feed: 是否换行 - :param force_size: 强制大小 - :param dp: 字体大小递减精度 - :param anchor : https://www.zhihu.com/question/474216280 - :param fill: 底部填充颜色 - :param fillet: 填充圆角 - :param rectangle_side: 边框宽度 - :param outline: 填充矩形边框颜色 - :param outline_width: 填充矩形边框宽度 - """ - self.actual_pos = None - self.outline_width = outline_width - self.outline = outline - self.fill = fill - self.fillet = fillet - self.font = font - self.text = text - self.color = color - self.force_size = force_size - self.vertical = vertical - self.line_feed = line_feed - self.dp = dp - self.font_size = font_size - self.rectangle_side = rectangle_side - self.anchor = anchor - super(Text, self).__init__(uv_size, box_size, parent_point, point) - - def load(self, only_calculate=False): - """限制区域像素大小""" - if isinstance(self.text, str): - self.text = [ - TextSegment(text=self.text, color=self.color, font=self.font) - ] - all_text = str() - for text in self.text: - all_text += text.text - limited_size = int((self.canvas_box[2] - self.canvas_box[0]) * self.canvas.base_img.size[0]), int((self.canvas_box[3] - self.canvas_box[1]) * self.canvas.base_img.size[1]) - font_size = limited_size[1] if self.font_size is None else self.font_size - image_font = ImageFont.truetype(self.font, font_size) - actual_size = image_font.getsize(all_text) - while (actual_size[0] > limited_size[0] or actual_size[1] > limited_size[1]) and not self.force_size: - font_size -= self.dp - image_font = ImageFont.truetype(self.font, font_size) - actual_size = image_font.getsize(all_text) - draw = ImageDraw.Draw(self.canvas.base_img) - if isinstance(self.parent, Img) or isinstance(self.parent, Text): - self.parent.canvas_box = self.parent.actual_pos - dx0 = self.parent.canvas_box[2] - self.parent.canvas_box[0] - dy0 = self.parent.canvas_box[3] - self.parent.canvas_box[1] - dx1 = actual_size[0] / self.canvas.base_img.size[0] - dy1 = actual_size[1] / self.canvas.base_img.size[1] - start_point = [ - int((self.parent.canvas_box[0] + dx0 * self.parent_point[0] - dx1 * self.point[0]) * self.canvas.base_img.size[0]), - int((self.parent.canvas_box[1] + dy0 * self.parent_point[1] - dy1 * self.point[1]) * self.canvas.base_img.size[1]) - ] - self.actual_pos = ( - start_point[0] / self.canvas.base_img.size[0], - start_point[1] / self.canvas.base_img.size[1], - (start_point[0] + actual_size[0]) / self.canvas.base_img.size[0], - (start_point[1] + actual_size[1]) / self.canvas.base_img.size[1], - ) - self.font_size = font_size - if not only_calculate: - for text_segment in self.text: - if text_segment.color is None: - text_segment.color = self.color - if text_segment.font is None: - text_segment.font = self.font - image_font = ImageFont.truetype(font=text_segment.font, size=font_size) - if self.fill[-1] > 0: - rectangle = Shape.rectangle(size=(actual_size[0] + 2 * self.rectangle_side, actual_size[1] + 2 * self.rectangle_side), fillet=self.fillet, fill=self.fill, - width=self.outline_width, outline=self.outline) - self.canvas.base_img.paste(im=rectangle, box=(start_point[0] - self.rectangle_side, - start_point[1] - self.rectangle_side, - start_point[0] + actual_size[0] + self.rectangle_side, - start_point[1] + actual_size[1] + self.rectangle_side), - mask=rectangle.split()[-1]) - draw.text((start_point[0] - self.rectangle_side, start_point[1] - self.rectangle_side), - text_segment.text, text_segment.color, font=image_font, anchor=self.anchor) - text_width = image_font.getsize(text_segment.text) - start_point[0] += text_width[0] - - -class Img(BasePanel): - def __init__(self, uv_size, box_size, parent_point, point, img: Image.Image, keep_ratio=True): - self.img_base_img = img - self.keep_ratio = keep_ratio - super(Img, self).__init__(uv_size, box_size, parent_point, point) - - def load(self, only_calculate=False): - self.preprocess() - self.img_base_img = self.img_base_img.convert("RGBA") - limited_size = int((self.canvas_box[2] - self.canvas_box[0]) * self.canvas.base_img.size[0]), \ - int((self.canvas_box[3] - self.canvas_box[1]) * self.canvas.base_img.size[1]) - - if self.keep_ratio: - """保持比例""" - actual_ratio = self.img_base_img.size[0] / self.img_base_img.size[1] - limited_ratio = limited_size[0] / limited_size[1] - if actual_ratio >= limited_ratio: - # 图片过长 - self.img_base_img = self.img_base_img.resize( - (int(self.img_base_img.size[0] * limited_size[0] / self.img_base_img.size[0]), - int(self.img_base_img.size[1] * limited_size[0] / self.img_base_img.size[0])) - ) - else: - self.img_base_img = self.img_base_img.resize( - (int(self.img_base_img.size[0] * limited_size[1] / self.img_base_img.size[1]), - int(self.img_base_img.size[1] * limited_size[1] / self.img_base_img.size[1])) - ) - - else: - """不保持比例""" - self.img_base_img = self.img_base_img.resize(limited_size) - - # 占比长度 - if isinstance(self.parent, Img) or isinstance(self.parent, Text): - self.parent.canvas_box = self.parent.actual_pos - - dx0 = self.parent.canvas_box[2] - self.parent.canvas_box[0] - dy0 = self.parent.canvas_box[3] - self.parent.canvas_box[1] - - dx1 = self.img_base_img.size[0] / self.canvas.base_img.size[0] - dy1 = self.img_base_img.size[1] / self.canvas.base_img.size[1] - start_point = ( - int((self.parent.canvas_box[0] + dx0 * self.parent_point[0] - dx1 * self.point[0]) * self.canvas.base_img.size[0]), - int((self.parent.canvas_box[1] + dy0 * self.parent_point[1] - dy1 * self.point[1]) * self.canvas.base_img.size[1]) - ) - alpha = self.img_base_img.split()[3] - self.actual_pos = ( - start_point[0] / self.canvas.base_img.size[0], - start_point[1] / self.canvas.base_img.size[1], - (start_point[0] + self.img_base_img.size[0]) / self.canvas.base_img.size[0], - (start_point[1] + self.img_base_img.size[1]) / self.canvas.base_img.size[1], - ) - if not only_calculate: - self.canvas.base_img.paste(self.img_base_img, start_point, alpha) - - def preprocess(self): - pass - - -class Rectangle(Img): - def __init__(self, uv_size, box_size, parent_point, point, fillet: Union[int, float] = 0, img: Union[Image.Image] = None, keep_ratio=True, - color=default_color, outline_width=0, outline_color=default_color): - """ - 圆角图 - :param uv_size: - :param box_size: - :param parent_point: - :param point: - :param fillet: 圆角半径浮点或整数 - :param img: - :param keep_ratio: - """ - self.fillet = fillet - self.color = color - self.outline_width = outline_width - self.outline_color = outline_color - super(Rectangle, self).__init__(uv_size, box_size, parent_point, point, img, keep_ratio) - - def preprocess(self): - limited_size = (int(self.canvas.base_img.size[0] * (self.canvas_box[2] - self.canvas_box[0])), - int(self.canvas.base_img.size[1] * (self.canvas_box[3] - self.canvas_box[1]))) - if not self.keep_ratio and self.img_base_img is not None and self.img_base_img.size[0] / self.img_base_img.size[1] != limited_size[0] / limited_size[1]: - self.img_base_img = self.img_base_img.resize(limited_size) - self.img_base_img = Shape.rectangle(size=limited_size, fillet=self.fillet, fill=self.color, width=self.outline_width, outline=self.outline_color) - - -class Color: - GREY = (128, 128, 128, 255) - RED = (255, 0, 0, 255) - GREEN = (0, 255, 0, 255) - BLUE = (0, 0, 255, 255) - YELLOW = (255, 255, 0, 255) - PURPLE = (255, 0, 255, 255) - CYAN = (0, 255, 255, 255) - WHITE = (255, 255, 255, 255) - BLACK = (0, 0, 0, 255) - - @staticmethod - def hex2dec(colorHex: str) -> Tuple[int, int, int, int]: - """ - :param colorHex: FFFFFFFF (ARGB)-> (R, G, B, A) - :return: - """ - return int(colorHex[2:4], 16), int(colorHex[4:6], 16), int(colorHex[6:8], 16), int(colorHex[0:2], 16) - - -class Shape: - @staticmethod - def circular(radius: int, fill: tuple, width: int = 0, outline: tuple = Color.BLACK) -> Image.Image: - """ - :param radius: 半径(像素) - :param fill: 填充颜色 - :param width: 轮廓粗细(像素) - :param outline: 轮廓颜色 - :return: 圆形Image对象 - """ - img = Image.new("RGBA", (radius * 2, radius * 2), color=radius) - draw = ImageDraw.Draw(img) - draw.ellipse(xy=(0, 0, radius * 2, radius * 2), fill=fill, outline=outline, width=width) - return img - - @staticmethod - def rectangle(size: Tuple[int, int], fill: tuple, width: int = 0, outline: tuple = Color.BLACK, fillet: int = 0) -> Image.Image: - """ - :param fillet: 圆角半径(像素) - :param size: 长宽(像素) - :param fill: 填充颜色 - :param width: 轮廓粗细(像素) - :param outline: 轮廓颜色 - :return: 矩形Image对象 - """ - img = Image.new("RGBA", size, color=fill) - draw = ImageDraw.Draw(img) - draw.rounded_rectangle(xy=(0, 0, size[0], size[1]), fill=fill, outline=outline, width=width, radius=fillet) - return img - - @staticmethod - def ellipse(size: Tuple[int, int], fill: tuple, outline: int = 0, outline_color: tuple = Color.BLACK) -> Image.Image: - """ - :param size: 长宽(像素) - :param fill: 填充颜色 - :param outline: 轮廓粗细(像素) - :param outline_color: 轮廓颜色 - :return: 椭圆Image对象 - """ - img = Image.new("RGBA", size, color=fill) - draw = ImageDraw.Draw(img) - draw.ellipse(xy=(0, 0, size[0], size[1]), fill=fill, outline=outline_color, width=outline) - return img - - @staticmethod - def polygon(points: List[Tuple[int, int]], fill: tuple, outline: int, outline_color: tuple) -> Image.Image: - """ - :param points: 多边形顶点列表 - :param fill: 填充颜色 - :param outline: 轮廓粗细(像素) - :param outline_color: 轮廓颜色 - :return: 多边形Image对象 - """ - img = Image.new("RGBA", (max(points)[0], max(points)[1]), color=fill) - draw = ImageDraw.Draw(img) - draw.polygon(xy=points, fill=fill, outline=outline_color, width=outline) - return img - - @staticmethod - def line(points: List[Tuple[int, int]], fill: tuple, width: int) -> Image: - """ - :param points: 线段顶点列表 - :param fill: 填充颜色 - :param width: 线段粗细(像素) - :return: 线段Image对象 - """ - img = Image.new("RGBA", (max(points)[0], max(points)[1]), color=fill) - draw = ImageDraw.Draw(img) - draw.line(xy=points, fill=fill, width=width) - return img - - -class Utils: - - @staticmethod - def central_clip_by_ratio(img: Image.Image, size: Tuple, use_cache=True): - """ - :param use_cache: 是否使用缓存,剪切过一次后默认生成缓存 - :param img: - :param size: 仅为比例,满填充裁剪 - :return: - """ - cache_file_path = str() - if use_cache: - filename_without_end = ".".join(os.path.basename(img.fp.name).split(".")[0:-1]) + f"_{size[0]}x{size[1]}" + ".png" - cache_file_path = os.path.join(".cache", filename_without_end) - if os.path.exists(cache_file_path): - nonebot.logger.info("本次使用缓存加载图片,不裁剪") - return Image.open(os.path.join(".cache", filename_without_end)) - img_ratio = img.size[0] / img.size[1] - limited_ratio = size[0] / size[1] - if limited_ratio > img_ratio: - actual_size = ( - img.size[0], - img.size[0] / size[0] * size[1] - ) - box = ( - 0, (img.size[1] - actual_size[1]) // 2, - img.size[0], img.size[1] - (img.size[1] - actual_size[1]) // 2 - ) - else: - actual_size = ( - img.size[1] / size[1] * size[0], - img.size[1], - ) - box = ( - (img.size[0] - actual_size[0]) // 2, 0, - img.size[0] - (img.size[0] - actual_size[0]) // 2, img.size[1] - ) - img = img.crop(box).resize(size) - if use_cache: - img.save(cache_file_path) - return img - - @staticmethod - def circular_clip(img: Image.Image): - """ - 裁剪为alpha圆形 - - :param img: - :return: - """ - length = min(img.size) - alpha_cover = Image.new("RGBA", (length, length), color=(0, 0, 0, 0)) - if img.size[0] > img.size[1]: - box = ( - (img.size[0] - img[1]) // 2, 0, - (img.size[0] - img[1]) // 2 + img.size[1], img.size[1] - ) - else: - box = ( - 0, (img.size[1] - img.size[0]) // 2, - img.size[0], (img.size[1] - img.size[0]) // 2 + img.size[0] - ) - img = img.crop(box).resize((length, length)) - draw = ImageDraw.Draw(alpha_cover) - draw.ellipse(xy=(0, 0, length, length), fill=(255, 255, 255, 255)) - alpha = alpha_cover.split()[-1] - img.putalpha(alpha) - return img - - @staticmethod - def open_img(path) -> Image.Image: - return Image.open(path, "RGBA") diff --git a/src/nonebot_plugins/liteyuki_crt_utils/crt.py b/src/nonebot_plugins/liteyuki_crt_utils/crt.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/nonebot_plugins/liteyuki_crt_utils/crt_matchers.py b/src/nonebot_plugins/liteyuki_crt_utils/crt_matchers.py deleted file mode 100644 index 0a4fa646..00000000 --- a/src/nonebot_plugins/liteyuki_crt_utils/crt_matchers.py +++ /dev/null @@ -1,78 +0,0 @@ -from urllib.parse import quote - -import aiohttp -from nonebot import require - -from src.utils.event import get_user_id -from src.utils.base.language import Language -from src.utils.base.ly_typing import T_MessageEvent -from src.utils.base.resource import get_path -from src.utils.message.html_tool import template2image - -require("nonebot_plugin_alconna") - -from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, Option - -crt_cmd = on_alconna( - Alconna( - "crt", - Subcommand( - "route", - Args["start", str, "沙坪坝"]["end", str, "上新街"], - alias=("r",), - help_text="查询两地之间的地铁路线" - ), - ) -) - - -@crt_cmd.assign("route") -async def _(result: Arparma, event: T_MessageEvent): - # 获取语言 - ulang = Language(get_user_id(event)) - - # 获取参数 - # 你也别问我为什么要quote两次,问就是CRT官网的锅,只有这样才可以运行 - start = quote(quote(result.other_args.get("start"))) - end = quote(quote(result.other_args.get("end"))) - - # 判断参数语言 - query_lang_code = "" - if start.isalpha() and end.isalpha(): - query_lang_code = "Eng" - - # 构造请求 URL - url = f"https://www.cqmetro.cn/Front/html/TakeLine!queryYs{query_lang_code}TakeLine.action?entity.startStaName={start}&entity.endStaName={end}" - - # 请求数据 - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - result = await resp.json() - - # 检查结果/无则终止 - if not result.get("result"): - await crt_cmd.send(ulang.get("crt.no_result")) - return - - # 模板传参定义 - templates = { - "data" : { - "result": result["result"], - }, - "localization": ulang.get_many( - "crt.station", - "crt.hour", - "crt.minute", - ) - - } - - # 生成图片 - image = await template2image( - template=get_path("templates/crt_route.html"), - templates=templates, - debug=True - ) - - # 发送图片 - await crt_cmd.send(UniMessage.image(raw=image)) diff --git a/src/nonebot_plugins/liteyuki_crt_utils/rt_guide.py b/src/nonebot_plugins/liteyuki_crt_utils/rt_guide.py deleted file mode 100644 index 4167cbf7..00000000 --- a/src/nonebot_plugins/liteyuki_crt_utils/rt_guide.py +++ /dev/null @@ -1,419 +0,0 @@ -import json -from typing import List, Any - -from PIL import Image -from arclet.alconna import Alconna -from nb_cli import run_sync -from nonebot import on_command -from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiVar, Arparma, UniMessage -from pydantic import BaseModel - -from .canvas import * -from ...utils.base.resource import get_path - -resolution = 256 - - -class Entrance(BaseModel): - identifier: str - size: tuple[int, int] - dest: List[str] - - -class Station(BaseModel): - identifier: str - chineseName: str - englishName: str - position: tuple[int, int] - - -class Line(BaseModel): - identifier: str - chineseName: str - englishName: str - color: Any - stations: List["Station"] - - -font_light = get_path("templates/fonts/MiSans/MiSans-Light.woff2") -font_bold = get_path("templates/fonts/MiSans/MiSans-Bold.woff2") - -@run_sync -def generate_entrance_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float], - reso: int = resolution): - """ - Generates an entrance sign for the ride. - """ - width, height = ratio[0] * reso, ratio[1] * reso - baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.WHITE)) - # 加黑色图框 - baseCanvas.outline = Img( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0, 0), - point=(0, 0), - img=Shape.rectangle( - size=(width, height), - fillet=0, - fill=(0, 0, 0, 0), - width=15, - outline=Color.BLACK - ) - ) - - baseCanvas.contentPanel = Panel( - uv_size=(width, height), - box_size=(width - 28, height - 28), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - ) - - linePanelHeight = 0.7 * ratio[1] - linePanelWidth = linePanelHeight * 1.3 - - # 画线路面板部分 - - for i, line in enumerate(lineInfo): - linePanel = baseCanvas.contentPanel.__dict__[f"Line_{i}_Panel"] = Panel( - uv_size=ratio, - box_size=(linePanelWidth, linePanelHeight), - parent_point=(i * linePanelWidth / ratio[0], 1), - point=(0, 1), - ) - - linePanel.colorCube = Img( - uv_size=(1, 1), - box_size=(0.15, 1), - parent_point=(0.125, 1), - point=(0, 1), - img=Shape.rectangle( - size=(100, 100), - fillet=0, - fill=line.color, - ), - keep_ratio=False - ) - - textPanel = linePanel.TextPanel = Panel( - uv_size=(1, 1), - box_size=(0.625, 1), - parent_point=(1, 1), - point=(1, 1) - ) - - # 中文线路名 - textPanel.namePanel = Panel( - uv_size=(1, 1), - box_size=(1, 2 / 3), - parent_point=(0, 0), - point=(0, 0), - ) - nameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.namePanel".format(i)) - textPanel.namePanel.text = Text( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - text=line.chineseName, - color=Color.BLACK, - font_size=int(nameSize[1] * 0.5), - force_size=True, - font=font_bold - - ) - - # 英文线路名 - textPanel.englishNamePanel = Panel( - uv_size=(1, 1), - box_size=(1, 1 / 3), - parent_point=(0, 1), - point=(0, 1), - ) - englishNameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.englishNamePanel".format(i)) - textPanel.englishNamePanel.text = Text( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - text=line.englishName, - color=Color.BLACK, - font_size=int(englishNameSize[1] * 0.6), - force_size=True, - font=font_light - ) - - # 画名称部分 - namePanel = baseCanvas.contentPanel.namePanel = Panel( - uv_size=(1, 1), - box_size=(1, 0.4), - parent_point=(0.5, 0), - point=(0.5, 0), - ) - - namePanel.text = Text( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - text=name, - color=Color.BLACK, - font_size=int(height * 0.3), - force_size=True, - font=font_bold - ) - - aliasesPanel = baseCanvas.contentPanel.aliasesPanel = Panel( - uv_size=(1, 1), - box_size=(1, 0.5), - parent_point=(0.5, 1), - point=(0.5, 1), - - ) - for j, alias in enumerate(aliases): - aliasesPanel.__dict__[alias] = Text( - uv_size=(1, 1), - box_size=(0.35, 0.5), - parent_point=(0.5, 0.5 * j), - point=(0.5, 0), - text=alias, - color=Color.BLACK, - font_size=int(height * 0.15), - font=font_light - ) - - # 画入口标识 - entrancePanel = baseCanvas.contentPanel.entrancePanel = Panel( - uv_size=(1, 1), - box_size=(0.2, 1), - parent_point=(1, 0.5), - point=(1, 0.5), - ) - # 中文文本 - entrancePanel.namePanel = Panel( - uv_size=(1, 1), - box_size=(1, 0.5), - parent_point=(1, 0), - point=(1, 0), - ) - entrancePanel.namePanel.text = Text( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0, 0.5), - point=(0, 0.5), - text=f"{entranceIdentifier}出入口", - color=Color.BLACK, - font_size=int(height * 0.2), - force_size=True, - font=font_bold - ) - # 英文文本 - entrancePanel.englishNamePanel = Panel( - uv_size=(1, 1), - box_size=(1, 0.5), - parent_point=(1, 1), - point=(1, 1), - ) - entrancePanel.englishNamePanel.text = Text( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0, 0.5), - point=(0, 0.5), - text=f"Entrance {entranceIdentifier}", - color=Color.BLACK, - font_size=int(height * 0.15), - force_size=True, - font=font_light - ) - - return baseCanvas.base_img.tobytes() - - -crt_alc = on_alconna( - Alconna( - "crt", - Subcommand( - "entrance", - Args["name", str]["lines", str, ""]["entrance", int, 1], # /crt entrance 璧山&Bishan 1号线&Line1&#ff0000,27号线&Line1&#ff0000 1A - ) - ) -) - - -@crt_alc.assign("entrance") -async def _(result: Arparma): - args = result.subcommands.get("entrance").args - name = args["name"] - lines = args["lines"] - entrance = args["entrance"] - line_info = [] - for line in lines.split(","): - line_args = line.split("&") - line_info.append(Line( - identifier=1, - chineseName=line_args[0], - englishName=line_args[1], - color=line_args[2], - stations=[] - )) - img_bytes = await generate_entrance_sign( - name=name, - aliases=name.split("&"), - lineInfo=line_info, - entranceIdentifier=entrance, - ratio=(8, 1), - reso=256, - ) - await crt_alc.finish( - UniMessage.image(raw=img_bytes) - ) - - -def generate_platform_line_pic(line: Line, station: Station, ratio=None, reso: int = resolution): - """ - 生成站台线路图 - :param line: 线路对象 - :param station: 本站点对象 - :param ratio: 比例 - :param reso: 分辨率,1:reso - :return: 两个方向的站牌 - """ - if ratio is None: - ratio = [4, 1] - width, height = ratio[0] * reso, ratio[1] * reso - baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.YELLOW)) - # 加黑色图框 - baseCanvas.linePanel = Panel( - uv_size=(1, 1), - box_size=(0.8, 0.15), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - ) - - # 直线块 - baseCanvas.linePanel.recLine = Img( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - img=Shape.rectangle( - size=(10, 10), - fill=line.color, - ), - keep_ratio=False - ) - # 灰色直线块 - baseCanvas.linePanel.recLineGrey = Img( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - img=Shape.rectangle( - size=(10, 10), - fill=Color.GREY, - ), - keep_ratio=False - ) - # 生成各站圆点 - outline_width = 40 - circleForward = Shape.circular( - radius=200, - fill=Color.WHITE, - width=outline_width, - outline=line.color, - ) - - circleThisPanel = Canvas(Image.new("RGBA", (200, 200), (0, 0, 0, 0))) - circleThisPanel.circleOuter = Img( - uv_size=(1, 1), - box_size=(1, 1), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - img=Shape.circular( - radius=200, - fill=Color.WHITE, - width=outline_width, - outline=line.color, - ), - ) - circleThisPanel.circleOuter.circleInner = Img( - uv_size=(1, 1), - box_size=(0.7, 0.7), - parent_point=(0.5, 0.5), - point=(0.5, 0.5), - img=Shape.circular( - radius=200, - fill=line.color, - width=0, - outline=line.color, - ), - ) - - circleThisPanel.export("a.png", alpha=True) - circleThis = circleThisPanel.base_img - - circlePassed = Shape.circular( - radius=200, - fill=Color.WHITE, - width=outline_width, - outline=Color.GREY, - ) - - arrival = False - distance = 1 / (len(line.stations) - 1) - for i, sta in enumerate(line.stations): - box_size = (1.618, 1.618) - if sta.identifier == station.identifier: - arrival = True - baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img( - uv_size=(1, 1), - box_size=(1.8, 1.8), - parent_point=(distance * i, 0.5), - point=(0.5, 0.5), - img=circleThis, - keep_ratio=True - ) - continue - if arrival: - # 后方站绘制 - baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img( - uv_size=(1, 1), - box_size=box_size, - parent_point=(distance * i, 0.5), - point=(0.5, 0.5), - img=circleForward, - keep_ratio=True - ) - else: - # 前方站绘制 - baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img( - uv_size=(1, 1), - box_size=box_size, - parent_point=(distance * i, 0.5), - point=(0.5, 0.5), - img=circlePassed, - keep_ratio=True - ) - return baseCanvas - - -def generate_platform_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float], - reso: int = resolution - ): - pass - -# def main(): -# generate_entrance_sign( -# "璧山", -# aliases=["Bishan"], -# lineInfo=[ -# -# Line(identifier="2", chineseName="1号线", englishName="Line 1", color=Color.RED, stations=[]), -# Line(identifier="3", chineseName="27号线", englishName="Line 27", color="#685bc7", stations=[]), -# Line(identifier="1", chineseName="璧铜线", englishName="BT Line", color="#685BC7", stations=[]), -# ], -# entranceIdentifier="1", -# ratio=(8, 1) -# ) -# -# -# main()