♻️ Refactor Map

This commit is contained in:
omg-xtao 2023-02-19 18:12:47 +08:00 committed by GitHub
parent 5945d7451e
commit f564a1db02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 333 additions and 472 deletions

View File

@ -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("遇到未知错误,请稍后再试")

View File

@ -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]

198
plugins/genshin/map.py Normal file
View File

@ -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<name>.*)(在哪里|在哪|哪里有|哪儿有|哪有|在哪儿)$"), block=False)
@handler(MessageHandler, filters=filters.Regex("^(哪里有|哪儿有|哪有)(?P<name>.*)$"), 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刷新成功")

View File

@ -1,5 +0,0 @@
from .map import Map
class MapPlugins(Map):
pass

View File

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

View File

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

93
poetry.lock generated
View File

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

View File

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

View File

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

BIN
resources/img/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB