diff --git a/modules/apihelper/client/components/map.py b/modules/apihelper/client/components/map.py new file mode 100644 index 0000000..45f9a92 --- /dev/null +++ b/modules/apihelper/client/components/map.py @@ -0,0 +1,111 @@ +import json +from pathlib import Path +from typing import Dict, Union + +from httpx import AsyncClient, HTTPError + +from modules.apihelper.models.genshin.map import LabelTree, ListData +from utils.const import PROJECT_ROOT + +MAP_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "map") +MAP_PATH.mkdir(parents=True, exist_ok=True) + + +class MapException(Exception): + """提瓦特地图异常""" + + def __init__(self, message: str): + self.message = message + + +class MapHelper: + """提瓦特大地图""" + + MAP_API_URL = "https://map.minigg.cn/map/get_map" + LABEL_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc" + COUNT_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list" + COUNT_PARAMS = {"app_sn": "ys_obc", "map_id": "2"} + MAP_ID_LIST = [ + "2", + "7", + "9", + ] + MAP_NAME_LIST = [ + "提瓦特大陆", + "渊下宫", + "层岩巨渊·地下矿区", + ] + + def __init__(self): + self.client = AsyncClient() + self.query_map_path = MAP_PATH / "query_map.json" + self.label_count_path = MAP_PATH / "label_count.json" + self.query_map: Dict[str, str] = {} + self.label_count: Dict[str, Dict[str, int]] = {} + self.load(self.query_map, self.query_map_path) + self.load(self.label_count, self.label_count_path) + + @staticmethod + def load(data: Dict, path: Path) -> None: + """加载文件""" + if not path.exists(): + return + data.clear() + with open(path, "r", encoding="utf-8") as f: + data.update(json.load(f)) + + def save(self, data: Dict, path: Path) -> None: + """保存查询映射""" + if path not in [self.query_map_path, self.label_count_path]: + return + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) + + async def refresh_query_map(self) -> None: + """刷新查询映射""" + data = {} + label_data = await self.client.get(self.LABEL_URL) + for label_tree_source in label_data.json().get("data", {}).get("tree", []): + label_tree = LabelTree(**label_tree_source) + for child in label_tree.children: + data[child.name] = str(child.id) + self.query_map = data + self.save(data, self.query_map_path) + + async def refresh_label_count(self) -> None: + """刷新标签数量""" + data = self.label_count + for map_id in self.MAP_ID_LIST: + data[map_id] = {} + params = self.COUNT_PARAMS.copy() + params["map_id"] = map_id + count_data = await self.client.get(self.COUNT_URL, params=params) + list_data = ListData(**count_data.json().get("data", {})) + for label in list_data.label_list: + if label.depth == 2: + data[map_id][label.name] = len([i for i in list_data.point_list if i.label_id == label.id]) + self.save(data, self.label_count_path) + + def get_label_count(self, map_id: Union[str, int], label_name: str) -> int: + """获取标签数量""" + return self.label_count.get(str(map_id), {}).get(label_name, 0) + + async def get_map(self, map_id: Union[str, int], name: str) -> bytes: + """获取资源图片""" + try: + req = await self.client.get( + self.MAP_API_URL, + params={ + "resource_name": name, + "map_id": str(map_id), + "is_cluster": False, + }, + timeout=60, + ) + except HTTPError as e: + raise MapException("请求超时,请稍后再试") from e + if req.headers.get("content-type") == "image/jpeg": + return req.content + if req.headers.get("content-type") == "application/json": + raise MapException(req.json().get("message", "遇到未知错误,请稍后再试")) + raise MapException("遇到未知错误,请稍后再试") diff --git a/modules/apihelper/models/genshin/map.py b/modules/apihelper/models/genshin/map.py new file mode 100644 index 0000000..ef3b958 --- /dev/null +++ b/modules/apihelper/models/genshin/map.py @@ -0,0 +1,23 @@ +from typing import List + +from pydantic import BaseModel + + +class Label(BaseModel): + id: int + name: str + depth: int + + +class LabelTree(Label): + children: List[Label] + + +class Point(BaseModel): + id: int + label_id: int + + +class ListData(BaseModel): + point_list: List[Point] + label_list: List[Label] diff --git a/plugins/genshin/map.py b/plugins/genshin/map.py new file mode 100644 index 0000000..33cd117 --- /dev/null +++ b/plugins/genshin/map.py @@ -0,0 +1,198 @@ +from io import BytesIO +from typing import Union, Optional, List, Tuple + +from telegram import Update, Message, InputMediaDocument, InputMediaPhoto, InlineKeyboardButton, InlineKeyboardMarkup +from telegram.constants import ChatAction +from telegram.ext import CommandHandler, MessageHandler, filters, CallbackContext, CallbackQueryHandler + +from core.base.redisdb import RedisDB +from core.baseplugin import BasePlugin +from core.config import config +from core.plugin import handler, Plugin +from modules.apihelper.client.components.map import MapHelper, MapException +from utils.decorators.admins import bot_admins_rights_check +from utils.decorators.error import error_callable +from utils.decorators.restricts import restricts +from utils.log import logger + + +class Map(Plugin, BasePlugin): + """资源点查询""" + + def __init__(self, redis: RedisDB = None): + self.cache = redis.client + self.cache_photo_key = "plugin:map:photo:" + self.cache_doc_key = "plugin:map:doc:" + self.map_helper = MapHelper() + self.temp_photo_path = "resources/img/map.png" + self.temp_photo = None + + async def get_photo_cache(self, map_id: Union[str, int], name: str) -> Optional[str]: + if file_id := await self.cache.get(f"{self.cache_photo_key}{map_id}:{name}"): + return file_id.decode("utf-8") + return None + + async def get_doc_cache(self, map_id: Union[str, int], name: str) -> Optional[str]: + if file_id := await self.cache.get(f"{self.cache_doc_key}{map_id}:{name}"): + return file_id.decode("utf-8") + return None + + async def set_photo_cache(self, map_id: Union[str, int], name: str, file_id: str) -> None: + await self.cache.set(f"{self.cache_photo_key}{map_id}:{name}", file_id) + + async def set_doc_cache(self, map_id: Union[str, int], name: str, file_id: str) -> None: + await self.cache.set(f"{self.cache_doc_key}{map_id}:{name}", file_id) + + async def clear_cache(self) -> None: + for i in await self.cache.keys(f"{self.cache_photo_key}*"): + await self.cache.delete(i) + for i in await self.cache.keys(f"{self.cache_doc_key}*"): + await self.cache.delete(i) + + async def edit_media(self, message: Message, map_id: str, name: str) -> None: + caption = self.gen_caption(map_id, name) + if cache := await self.get_photo_cache(map_id, name): + media = InputMediaPhoto(media=cache, caption=caption) + await message.edit_media(media) + return + if cache := await self.get_doc_cache(map_id, name): + media = InputMediaDocument(media=cache, caption=caption) + await message.edit_media(media) + return + data = await self.map_helper.get_map(map_id, name) + if len(data) > (1024 * 1024): + data = BytesIO(data) + data.name = "map.jpg" + media = InputMediaDocument(media=data, caption=caption) + msg = await message.edit_media(media) + await self.set_doc_cache(map_id, name, msg.document.file_id) + else: + media = InputMediaPhoto(media=data, caption=caption) + msg = await message.edit_media(media) + await self.set_photo_cache(map_id, name, msg.photo[0].file_id) + + def get_show_map(self, name: str) -> List[int]: + return [ + idx + for idx, map_id in enumerate(self.map_helper.MAP_ID_LIST) + if self.map_helper.get_label_count(map_id, name) > 0 + ] + + async def gen_map_button( + self, maps: List[int], user_id: Union[str, int], name: str + ) -> List[List[InlineKeyboardButton]]: + return [ + [ + InlineKeyboardButton( + self.map_helper.MAP_NAME_LIST[idx], + callback_data=f"get_map|{user_id}|{self.map_helper.MAP_ID_LIST[idx]}|{name}", + ) + for idx in maps + ] + ] + + async def send_media(self, message: Message, map_id: Union[str, int], name: str) -> None: + caption = self.gen_caption(map_id, name) + if cache := await self.get_photo_cache(map_id, name): + await message.reply_photo(photo=cache, caption=caption) + return + if cache := await self.get_doc_cache(map_id, name): + await message.reply_document(document=cache, caption=caption) + return + try: + data = await self.map_helper.get_map(map_id, name) + except MapException as e: + await message.reply_text(e.message) + return + if len(data) > (1024 * 1024): + data = BytesIO(data) + data.name = "map.jpg" + msg = await message.reply_document(document=data, caption=caption) + await self.set_doc_cache(map_id, name, msg.document.file_id) + else: + msg = await message.reply_photo(photo=data, caption=caption) + await self.set_photo_cache(map_id, name, msg.photo[0].file_id) + + def gen_caption(self, map_id: Union[int, str], name: str) -> str: + count = self.map_helper.get_label_count(map_id, name) + return f"派蒙一共找到了 {name} 的 {count} 个位置点\n* 数据来源于米游社wiki" + + @handler(CommandHandler, command="map", block=False) + @handler(MessageHandler, filters=filters.Regex("^(?P.*)(在哪里|在哪|哪里有|哪儿有|哪有|在哪儿)$"), block=False) + @handler(MessageHandler, filters=filters.Regex("^(哪里有|哪儿有|哪有)(?P.*)$"), block=False) + @error_callable + @restricts(restricts_time=20) + async def command_start(self, update: Update, context: CallbackContext): + message = update.effective_message + args = context.args + group_dict = context.match and context.match.groupdict() + user = update.effective_user + resource_name = None + await message.reply_chat_action(ChatAction.TYPING) + if args and len(args) >= 1: + resource_name = args[0] + elif group_dict: + resource_name = group_dict.get("name", None) + if not resource_name: + if group_dict: + return + await message.reply_text("请指定要查找的资源名称。", parse_mode="Markdown") + return + logger.info("用户: %s [%s] 使用 map 命令查询了 %s", user.username, user.id, resource_name) + if resource_name not in self.map_helper.query_map: + await message.reply_text("没有找到该资源。", parse_mode="Markdown") + return + maps = self.get_show_map(resource_name) + if len(maps) == 0: + await message.reply_text("没有找到该资源。", parse_mode="Markdown") + return + if len(maps) == 1: + map_id = self.map_helper.MAP_ID_LIST[maps[0]] + await self.send_media(message, map_id, resource_name) + return + buttons = await self.gen_map_button(maps, user.id, resource_name) + if isinstance(self.temp_photo, str): + photo = self.temp_photo + else: + photo = open(self.temp_photo_path, "rb") + reply_message = await message.reply_photo( + photo=photo, caption="请选择你要查询的地图", reply_markup=InlineKeyboardMarkup(buttons) + ) + if reply_message.photo: + self.temp_photo = reply_message.photo[-1].file_id + + @handler(CallbackQueryHandler, pattern=r"^get_map\|", block=False) + @restricts(restricts_time=3, without_overlapping=True) + @error_callable + async def get_maps(self, update: Update, _: CallbackContext) -> None: + callback_query = update.callback_query + user = callback_query.from_user + message = callback_query.message + + async def get_map_callback(callback_query_data: str) -> Tuple[int, str, str]: + _data = callback_query_data.split("|") + _user_id = int(_data[1]) + _map_id = _data[2] + _name = _data[3] + logger.debug("callback_query_data 函数返回 user_id[%s] map_id[%s] name[%s]", _user_id, _map_id, _name) + return _user_id, _map_id, _name + + user_id, map_id, name = await get_map_callback(callback_query.data) + if user.id != user_id: + await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True) + return + await callback_query.answer(text="正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False) + try: + await self.edit_media(message, map_id, name) + except MapException as e: + await message.reply_text(e.message) + + @handler.command("refresh_map") + @bot_admins_rights_check + async def refresh_map(self, update: Update, _: CallbackContext): + message = update.effective_message + msg = await message.reply_text("正在刷新地图数据,请耐心等待...") + await self.map_helper.refresh_query_map() + await self.map_helper.refresh_label_count() + await self.clear_cache() + await msg.edit_text("正在刷新地图数据,请耐心等待...\n刷新成功") diff --git a/plugins/genshin/map/__init__.py b/plugins/genshin/map/__init__.py deleted file mode 100644 index cf3e8b3..0000000 --- a/plugins/genshin/map/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .map import Map - - -class MapPlugins(Map): - pass diff --git a/plugins/genshin/map/map.py b/plugins/genshin/map/map.py deleted file mode 100644 index 56a8f74..0000000 --- a/plugins/genshin/map/map.py +++ /dev/null @@ -1,67 +0,0 @@ -from os import sep - -from PIL import Image -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.constants import ChatAction -from telegram.ext import CommandHandler, MessageHandler, filters, CallbackContext -from telegram.helpers import create_deep_linked_url - -from core.baseplugin import BasePlugin -from core.plugin import handler, Plugin -from utils.decorators.error import error_callable -from utils.decorators.restricts import restricts -from utils.log import logger -from .model import MapHelper - - -class Map(Plugin, BasePlugin): - """支持资源点查询""" - - def __init__(self): - self.init_resource_map = False - self.map_helper = MapHelper() - - async def init_point_list_and_map(self): - logger.info("正在初始化地图资源节点") - if not self.init_resource_map: - await self.map_helper.init_point_list_and_map() - self.init_resource_map = True - - @handler(CommandHandler, command="map", block=False) - @handler(MessageHandler, filters=filters.Regex("^资源点查询(.*)"), block=False) - @error_callable - @restricts(restricts_time=20) - async def command_start(self, update: Update, context: CallbackContext): - message = update.effective_message - args = context.args - user = update.effective_user - if not self.init_resource_map: - await self.init_point_list_and_map() - await message.reply_chat_action(ChatAction.TYPING) - if len(args) >= 1: - resource_name = args[0] - else: - logger.info(f"用户: {user.full_name} [{user.id}] 使用了 map 命令") - await message.reply_text("请输入要查找的资源,或私聊派蒙发送 `/map list` 查看资源列表", parse_mode="Markdown") - return - if resource_name in ("list", "列表"): - if filters.ChatType.GROUPS.filter(message): - buttons = [[InlineKeyboardButton("点我私聊", url=create_deep_linked_url(context.bot.username))]] - reply_message = await message.reply_text("请私聊派蒙使用该命令", reply_markup=InlineKeyboardMarkup(buttons)) - self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id, 300) - self._add_delete_message_job(context, message.chat_id, message.message_id, 300) - return - logger.info(f"用户: {user.full_name} [{user.id}] 使用 map 命令查询了 资源列表") - text = self.map_helper.get_resource_list_mes() - await message.reply_text(text) - return - logger.info(f"用户: {user.full_name} [{user.id}] 使用 map 命令查询了 {resource_name}") - text = await self.map_helper.get_resource_map_mes(resource_name) - if "不知道" in text or "没有找到" in text: - await message.reply_text(text, parse_mode="Markdown") - return - img = Image.open(f"cache{sep}map.jpg") - if img.size[0] > 2048 or img.size[1] > 2048: - await message.reply_document(open(f"cache{sep}map.jpg", mode="rb+"), caption=text) - else: - await message.reply_photo(open(f"cache{sep}map.jpg", mode="rb+"), caption=text) diff --git a/plugins/genshin/map/model.py b/plugins/genshin/map/model.py deleted file mode 100644 index 02f4844..0000000 --- a/plugins/genshin/map/model.py +++ /dev/null @@ -1,306 +0,0 @@ -import os -import time -from io import BytesIO -from typing import Optional, List - -import httpx -import ujson -from PIL import Image, ImageMath - -from utils.helpers import REQUEST_HEADERS - -Image.MAX_IMAGE_PIXELS = None - -ZOOM = 0.5 -RESOURCE_ICON_OFFSET = (-int(150 * 0.5 * ZOOM), -int(150 * ZOOM)) - - -class MapHelper: - LABEL_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc" - POINT_LIST_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list?map_id=2&app_sn=ys_obc" - MAP_URL = "https://api-static.mihoyo.com/common/map_user/ys_obc/v1/map/info?map_id=2&app_sn=ys_obc&lang=zh-cn" - - def __init__(self, cache_dir_name: str = "cache"): - self._current_dir = os.getcwd() - self._output_dir = os.path.join(self._current_dir, cache_dir_name) - self._resources_icon_dir = os.path.join(self._current_dir, "resources", "icon") - self._cache_dir = os.path.join(self._current_dir, "cache") - self._map_dir = os.path.join(self._cache_dir, "map_icon.jpg") - self.client = httpx.AsyncClient(headers=REQUEST_HEADERS, timeout=10.0) - self.all_resource_type: dict = {} - """这个字典保存所有资源类型 - "1": { - "id": 1, - "name": "传送点", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - } - """ - self.can_query_type_list: dict = {} - """这个字典保存所有可以查询的资源类型名称和ID,这个字典只有名称和ID - 上边字典里"depth": 2的类型才可以查询,"depth": 1的是1级目录,不能查询 - - "七天神像":"2" - - "风神瞳":"5" - """ - self.all_resource_point_list: list = [] - """这个列表保存所有资源点的数据 - { - "id": 2740, - "label_id": 68, - "x_pos": -1789, - "y_pos": 2628, - "author_name": "作者名称", - "ctime": "更新时间", - "display_state": 1 - } - """ - self.date: str = "" - """记录上次更新"all_resource_point_list"的日期 - """ - - self.center: Optional[List[float]] = None - """center - """ - - self.map_icon: Optional[Image] = None - """map_icon - """ - - async def download_icon(self, url): - """下载图片 返回Image对象 - :param url: - :return: - """ - resp = await self.client.get(url=url) - if resp.status_code != 200: - raise ValueError(f"获取图片数据失败,错误代码 {resp.status_code}") - icon = resp.content - return Image.open(BytesIO(icon)) - - async def download_json(self, url): - """ - 获取资源数据,返回 JSON - :param url: - :return: dict - """ - resp = await self.client.get(url=url) - if resp.status_code != 200: - raise RuntimeError(f"获取资源点数据失败,错误代码 {resp.status_code}") - return resp.json() - - async def init_point_list_and_map(self): - await self.up_label_and_point_list() - await self.up_map() - - async def up_map(self): - """更新地图文件 并按照资源点的范围自动裁切掉不需要的地方 - 裁切地图需要最新的资源点位置,所以要先调用 up_label_and_point_list 再更新地图 - :return: None - """ - map_info = await self.download_json(self.MAP_URL) - map_info = map_info["data"]["info"]["detail"] - map_info = ujson.loads(map_info) - - map_url_list = map_info["slices"][0] - origin = map_info["origin"] - - x_start = map_info["total_size"][1] - y_start = map_info["total_size"][1] - x_end = 0 - y_end = 0 - for resource_point in self.all_resource_point_list: - x_pos = resource_point["x_pos"] + origin[0] - y_pos = resource_point["y_pos"] + origin[1] - x_start = min(x_start, x_pos) - y_start = min(y_start, y_pos) - x_end = max(x_end, x_pos) - y_end = max(y_end, y_pos) - - x_start -= 200 - y_start -= 200 - x_end += 200 - y_end += 200 - - self.center = [origin[0] - x_start, origin[1] - y_start] - x = int(x_end - x_start) - y = int(y_end - y_start) - self.map_icon = Image.new("RGB", (x, y)) - x_offset = 0 - for i in map_url_list: - map_url = i["url"] - map_icon = await self.download_icon(map_url) - self.map_icon.paste(map_icon, (int(-x_start) + x_offset, int(-y_start))) - x_offset += map_icon.size[0] - - async def up_label_and_point_list(self): - """更新label列表和资源点列表 - :return: - """ - label_data = await self.download_json(self.LABEL_URL) - for label in label_data["data"]["tree"]: - self.all_resource_type[str(label["id"])] = label - for sublist in label["children"]: - self.all_resource_type[str(sublist["id"])] = sublist - self.can_query_type_list[sublist["name"]] = str(sublist["id"]) - await self.up_icon_image(sublist) - label["children"] = [] - test = await self.download_json(self.POINT_LIST_URL) - self.all_resource_point_list = test["data"]["point_list"] - self.date = time.strftime("%d") - - async def up_icon_image(self, sublist: dict): - """检查是否有图标,没有图标下载保存到本地 - :param sublist: - :return: - """ - icon_id = sublist["id"] - icon_path = os.path.join(self._cache_dir, f"{icon_id}.png") - - if not os.path.exists(icon_path): - icon_url = sublist["icon"] - icon = await self.download_icon(icon_url) - icon = icon.resize((150, 150)) - - box_alpha = Image.open(f"resources{os.sep}icon{os.sep}box_alpha.png").getchannel("A") - box = Image.open(f"resources{os.sep}icon{os.sep}box.png") - - try: - icon_alpha = icon.getchannel("A") - icon_alpha = ImageMath.eval("convert(a*b/256, 'L')", a=icon_alpha, b=box_alpha) - except ValueError: - # 米游社的图有时候会没有alpha导致报错,这时候直接使用box_alpha当做alpha就行 - icon_alpha = box_alpha - - icon2 = Image.new("RGBA", (150, 150), "#00000000") - icon2.paste(icon, (0, -10)) - - bg = Image.new("RGBA", (150, 150), "#00000000") - bg.paste(icon2, mask=icon_alpha) - bg.paste(box, mask=box) - - with open(icon_path, "wb") as icon_file: - bg.save(icon_file) - - async def get_resource_map_mes(self, name): - if self.date != time.strftime("%d"): - await self.init_point_list_and_map() - if name not in self.can_query_type_list: - return f"派蒙还不知道 {name} 在哪里呢,可以发送 `/map list` 查看资源列表" - resource_id = self.can_query_type_list[name] - map_res = ResourceMap(self.all_resource_point_list, self.map_icon, self.center, resource_id) - count = map_res.get_resource_count() - if not count: - return f"派蒙没有找到 {name} 的位置,可能米游社wiki还没更新" - map_res.gen_jpg() - return f"派蒙一共找到 {name} 的 {count} 个位置点\n* 数据来源于米游社wiki" - - def get_resource_list_mes(self): - temp = {list_id: [] for list_id in self.all_resource_type if self.all_resource_type[list_id]["depth"] == 1} - - for list_id in self.all_resource_type: - # 再找2级目录 - if self.all_resource_type[list_id]["depth"] == 2: - temp[str(self.all_resource_type[list_id]["parent_id"])].append(list_id) - mes = "当前资源列表如下:\n" - - for resource_type_id, value in temp.items(): - if resource_type_id in ["1", "12", "50", "51", "95", "131"]: - # 在游戏里能查到的数据这里就不列举了,不然消息太长了 - continue - mes += f"{self.all_resource_type[resource_type_id]['name']}:" - for resource_id in value: - mes += f"{self.all_resource_type[resource_id]['name']}," - mes += "\n" - return mes - - -class ResourceMap: - def __init__(self, all_resource_point_list: List[dict], map_icon: Image, center: List[float], resource_id: int): - self.all_resource_point_list = all_resource_point_list - self.resource_id = resource_id - self.center = center - self.map_image = map_icon.copy() - self.map_size = self.map_image.size - # 地图要要裁切的左上角和右下角坐标 - # 这里初始化为地图的大小 - self.x_start = self.map_size[0] - self.y_start = self.map_size[1] - self.x_end = 0 - self.y_end = 0 - resource_icon = Image.open(self.get_icon_path()) - self.resource_icon = resource_icon.resize((int(150 * ZOOM), int(150 * ZOOM))) - self.resource_xy_list = self.get_resource_point_list() - - def get_icon_path(self): - # 检查有没有图标,有返回正确图标,没有返回默认图标 - icon_path = os.path.join(f"resources{os.sep}icon", f"{self.resource_id}.png") - if os.path.exists(icon_path): - return icon_path - return os.path.join(f"resources{os.sep}icon{os.sep}0.png") - - def get_resource_point_list(self): - temp_list = [] - for resource_point in self.all_resource_point_list: - if str(resource_point["label_id"]) == self.resource_id: - # 获取xy坐标,然后加上中心点的坐标完成坐标转换 - x = resource_point["x_pos"] + self.center[0] - y = resource_point["y_pos"] + self.center[1] - temp_list.append((int(x), int(y))) - return temp_list - - def paste(self): - for x, y in self.resource_xy_list: - # 把资源图片贴到地图上 - # 这时地图已经裁切过了,要以裁切后的地图左上角为中心再转换一次坐标 - x -= self.x_start - y -= self.y_start - self.map_image.paste( - self.resource_icon, (x + RESOURCE_ICON_OFFSET[0], y + RESOURCE_ICON_OFFSET[1]), self.resource_icon - ) - - def crop(self): - # 把大地图裁切到只保留资源图标位置 - for x, y in self.resource_xy_list: - # 找出4个方向最远的坐标,用于后边裁切 - self.x_start = min(x, self.x_start) - self.y_start = min(y, self.y_start) - self.x_end = max(x, self.x_end) - self.y_end = max(y, self.y_end) - - # 先把4个方向扩展150像素防止把资源图标裁掉 - self.x_start -= 150 - self.y_start -= 150 - self.x_end += 150 - self.y_end += 150 - - # 如果图片裁切得太小会看不出资源的位置在哪,检查图片裁切的长和宽看够不够1000,不到1000的按1000裁切 - if (self.x_end - self.x_start) < 1000: - center = int((self.x_end + self.x_start) / 2) - self.x_start = center - 500 - self.x_end = center + 500 - if (self.y_end - self.y_start) < 1000: - center = int((self.y_end + self.y_start) / 2) - self.y_start = center - 500 - self.y_end = center + 500 - - self.map_image = self.map_image.crop((self.x_start, self.y_start, self.x_end, self.y_end)) - - def gen_jpg(self): - if not self.resource_xy_list: - return "没有这个资源的信息" - if not os.path.exists("cache"): - os.mkdir("cache") # 查找 cache 目录 (缓存目录) 是否存在,如果不存在则创建 - self.crop() - self.paste() - self.map_image.save(f"cache{os.sep}map.jpg", format="JPEG") - - def get_resource_count(self): - return len(self.resource_xy_list) diff --git a/poetry.lock b/poetry.lock index 5b338e5..8882f87 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1630,97 +1630,6 @@ files = [ {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, ] -[[package]] -name = "pillow" -version = "9.4.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, - {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, - {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, - {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, - {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, - {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, - {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, - {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, - {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, - {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, - {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, - {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, - {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, - {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, - {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, - {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"}, - {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"}, - {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"}, - {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"}, - {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"}, - {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"}, - {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"}, - {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"}, - {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"}, - {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"}, - {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"}, - {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"}, - {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"}, - {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"}, - {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"}, - {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"}, - {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"}, - {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"}, - {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"}, - {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"}, - {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"}, - {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"}, - {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"}, - {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"}, - {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"}, - {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, - {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - [[package]] name = "platformdirs" version = "3.0.0" @@ -2995,4 +2904,4 @@ test = ["pytest", "pytest-asyncio", "flaky"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "6ba326b3c3cad07ed09f3fe9c8245534c1d87392a69f36c705e16ae67b560dbd" +content-hash = "2be49a675775fb01971640b7bc9dfd22a457a05d0fbb5b3d129100b774499f8e" diff --git a/pyproject.toml b/pyproject.toml index f17307f..2c701fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ ujson = "^5.7.0" genshin = { git = "https://github.com/thesadru/genshin.py" } Jinja2 = "^3.1.2" python-telegram-bot = { version = "^20.1", extras = ["ext", "rate-limiter"] } -Pillow = "^9.4.0" sqlmodel = "^0.0.8" colorlog = "^6.7.0" playwright = "^1.27.1" diff --git a/requirements.txt b/requirements.txt index 8d2312a..9466417 100644 --- a/requirements.txt +++ b/requirements.txt @@ -56,7 +56,6 @@ mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "4.0" openpyxl==3.1.1 ; python_version >= "3.8" and python_version < "4.0" packaging==23.0 ; python_version >= "3.8" and python_version < "4.0" pathspec==0.11.0 ; python_version >= "3.8" and python_version < "4.0" -pillow==9.4.0 ; python_version >= "3.8" and python_version < "4.0" platformdirs==3.0.0 ; python_version >= "3.8" and python_version < "4.0" playwright==1.27.1 ; python_version >= "3.8" and python_version < "4.0" pluggy==1.0.0 ; python_version >= "3.8" and python_version < "4.0" diff --git a/resources/img/map.png b/resources/img/map.png new file mode 100644 index 0000000..1374780 Binary files /dev/null and b/resources/img/map.png differ