mirror of
https://github.com/PaiGramTeam/PamGram.git
synced 2024-11-21 21:58:04 +00:00
♻️ Refactor Map
This commit is contained in:
parent
5945d7451e
commit
f564a1db02
111
modules/apihelper/client/components/map.py
Normal file
111
modules/apihelper/client/components/map.py
Normal 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("遇到未知错误,请稍后再试")
|
23
modules/apihelper/models/genshin/map.py
Normal file
23
modules/apihelper/models/genshin/map.py
Normal 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
198
plugins/genshin/map.py
Normal 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刷新成功")
|
@ -1,5 +0,0 @@
|
|||||||
from .map import Map
|
|
||||||
|
|
||||||
|
|
||||||
class MapPlugins(Map):
|
|
||||||
pass
|
|
@ -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)
|
|
@ -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
93
poetry.lock
generated
@ -1630,97 +1630,6 @@ files = [
|
|||||||
{file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"},
|
{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]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
@ -2995,4 +2904,4 @@ test = ["pytest", "pytest-asyncio", "flaky"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "6ba326b3c3cad07ed09f3fe9c8245534c1d87392a69f36c705e16ae67b560dbd"
|
content-hash = "2be49a675775fb01971640b7bc9dfd22a457a05d0fbb5b3d129100b774499f8e"
|
||||||
|
@ -13,7 +13,6 @@ ujson = "^5.7.0"
|
|||||||
genshin = { git = "https://github.com/thesadru/genshin.py" }
|
genshin = { git = "https://github.com/thesadru/genshin.py" }
|
||||||
Jinja2 = "^3.1.2"
|
Jinja2 = "^3.1.2"
|
||||||
python-telegram-bot = { version = "^20.1", extras = ["ext", "rate-limiter"] }
|
python-telegram-bot = { version = "^20.1", extras = ["ext", "rate-limiter"] }
|
||||||
Pillow = "^9.4.0"
|
|
||||||
sqlmodel = "^0.0.8"
|
sqlmodel = "^0.0.8"
|
||||||
colorlog = "^6.7.0"
|
colorlog = "^6.7.0"
|
||||||
playwright = "^1.27.1"
|
playwright = "^1.27.1"
|
||||||
|
@ -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"
|
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"
|
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"
|
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"
|
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"
|
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"
|
pluggy==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||||
|
BIN
resources/img/map.png
Normal file
BIN
resources/img/map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 260 KiB |
Loading…
Reference in New Issue
Block a user