diff --git a/.gitignore b/.gitignore index feb467d..becf730 100644 --- a/.gitignore +++ b/.gitignore @@ -666,6 +666,7 @@ config.json res_data GsData.db data +core_help.jpg gsuid_core/plugins/* !gsuid_core/plugins/core_command !gsuid_core/plugins/gs_test.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 185c1fa..d2f4dc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: - id: flake8 - repo: https://github.com/hadialqattan/pycln - rev: v2.1.3 + rev: v2.1.5 hooks: - id: pycln @@ -34,7 +34,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-poetry/poetry - rev: 1.4.0 + rev: 1.5.0 hooks: - id: poetry-check - id: poetry-lock diff --git a/README.md b/README.md index 9da8580..3404151 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ [KimigaiiWuyi/GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) 的核心部分,平台无关,支持 HTTP/WS 形式调用,便于移植到其他平台以及框架。 -目前仍在开发中。 +**🎉[详细文档](https://docs.gsuid.gbots.work/#/)** ## 安装Core 1. git clone gsuid-core本体 ```shell -git clone https://ghproxy.com/https://github.com/Genshin-bots/gsuid-core.git --depth=1 --single-branch +git clone https://ghproxy.com/https://github.com/Genshin-bots/gsuid_core.git --depth=1 --single-branch ``` 2. 安装poetry @@ -26,28 +26,34 @@ pip install poetry 3. 安装所需依赖 ```shell +# cd进入clone好的文件夹内 +cd gsuid_core +# 安装依赖 poetry install ``` -4. 安装所需插件 +4. 安装所需插件(可选) ```shell -# 安装v4 GenshinUID +# cd进入插件文件夹内 cd plugins +# 安装v4 GenshinUID git clone -b v4 https://ghproxy.com/https://github.com/KimigaiiWuyi/GenshinUID.git --depth=1 --single-branch ``` -5. 启动gsuid-core +5. 启动gsuid_core(早柚核心) ```shell # 在gsuid_core/genshin_core文件夹内 poetry run python core.py +# 或者(二选一即可) +poetry run core ``` 6. 链接其他适配端 + 默认core将运行在`localhost:8765`端口上,如有需要可至`config.json`修改。 -+ 在支持的Bot上(例如NoneBot2、HoshinoBot),安装相应适配插件,启动Bot(如果有修改端口,则需要在启动Bot前修改适配插件相应端口),即可自动连接Core端。 ++ 在支持的Bot上(例如NoneBot2、HoshinoBot、ZeroBot、YunZaiBot等),安装相应适配插件,启动Bot(如果有修改端口,则需要在启动Bot前修改适配插件相应端口),即可自动连接Core端。 ## Docker部署Core(可选) @@ -56,7 +62,15 @@ poetry run python core.py 1. git clone gsuid-core本体 ```shell -git clone https://ghproxy.com/https://github.com/Genshin-bots/gsuid-core.git --depth=1 --single-branch +git clone https://ghproxy.com/https://github.com/Genshin-bots/gsuid_core.git --depth=1 --single-branch +``` + +2. 安装所需插件 + +```shell +# 安装v4 GenshinUID +cd plugins +git clone -b v4 https://ghproxy.com/https://github.com/KimigaiiWuyi/GenshinUID.git --depth=1 --single-branch ``` 2. 安装所需插件 @@ -77,6 +91,42 @@ docker-compose up -d - 默认core将运行在`localhost:8765`端口上,Docker部署必须修改`config.json`,如`0.0.0.0:8765` - 如果Bot(例如NoneBot2、HoshinoBot)也是Docker部署的,Core或其插件更新后,可能需要将Core和Bot的容器都重启才生效 +## 配置文件 + +修改`gsuid_core/gsuid_core/config.json`,参考如下 + +**(注意json不支持`#`,所以不要复制下面的配置到自己的文件中)** + +```json +{ + "HOST": "localhost", # 如需挂载公网修改为`0.0.0.0` + "PORT": "8765", # core端口 + "masters": ["444835641", "111"], # Bot主人,pm为0 + "superusers": ["123456789"], # 超管,pm为1 + "sv": { + "Core管理": { + "priority": 5, # 某个服务的优先级 + "enabled": true, # 某个服务是否启动 + "pm": 1, # 某个服务要求的权限等级 + "black_list": [], # 某个服务的黑名单 + "area": "ALL", # 某个服务的触发范围 + "white_list": [] # 某个服务的白名单 + }, + }, + "log": { + "level": "DEBUG" # log等级 + }, + "command_start": ["/", "*"], # core内所有插件的要求前缀 + "misfire_grace_time": 90 +} +``` + +> 黑名单一旦设置,黑名单中的用户ID将无法访问该服务 +> +> 白名单一旦设置,只有白名单的用户ID能访问该服务 +> +> 服务配置可以通过[网页控制台](https://docs.gsuid.gbots.work/#/WebConsole)实时修改, 如果手动修改`config.json`需要**重启** + ## 编写插件 @@ -101,7 +151,9 @@ sv=SV( pm=2, # 权限 0为master,1为superuser,2为群的群主&管理员,3为普通 priority=5, # 整组服务的优先级 enabled=True, # 是否启用 - black_list=[] # 黑名单 + area= 'ALL', # 群聊和私聊均可触发 + black_list=[], # 黑名单 + white_list=[], # 白名单 ) @sv.on_prefix('测试') diff --git a/gsuid_core/bot.py b/gsuid_core/bot.py index be38989..b7700b1 100644 --- a/gsuid_core/bot.py +++ b/gsuid_core/bot.py @@ -1,3 +1,4 @@ +import random import asyncio from typing import List, Union, Literal, Optional @@ -7,7 +8,14 @@ from msgspec import json as msgjson from gsuid_core.logger import logger from gsuid_core.gs_logger import GsLogger from gsuid_core.segment import MessageSegment +from gsuid_core.utils.image.convert import text2pic from gsuid_core.models import Event, Message, MessageSend +from gsuid_core.utils.plugins_config.gs_config import core_plugins_config + +R_enabled = core_plugins_config.get_config('AutoAddRandomText').data +R_text = core_plugins_config.get_config('RandomText').data +is_text2pic = core_plugins_config.get_config('AutoTextToPic').data +text2pic_limit = core_plugins_config.get_config('TextToPicThreshold').data class _Bot: @@ -39,13 +47,33 @@ class _Bot: elif isinstance(message, bytes): message = [MessageSegment.image(message)] elif isinstance(message, List): - message = [MessageSegment.node(message)] + if all(isinstance(x, str) for x in message): + message = [MessageSegment.node(message)] + + _message: List[Message] = message # type: ignore if at_sender and sender_id: - message.append(MessageSegment.at(sender_id)) + _message.append(MessageSegment.at(sender_id)) + + if R_enabled: + result = ''.join( + random.choice(R_text) + for _ in range(random.randint(1, len(R_text))) + ) + _message.append(MessageSegment.text(result)) + + if is_text2pic: + if ( + len(_message) == 1 + and _message[0].type == 'text' + and isinstance(_message[0].data, str) + and len(_message[0].data) >= int(text2pic_limit) + ): + img = await text2pic(_message[0].data) + _message = [MessageSegment.image(img)] send = MessageSend( - content=message, + content=_message, bot_id=bot_id, bot_self_id=bot_self_id, target_type=target_type, diff --git a/gsuid_core/data_store.py b/gsuid_core/data_store.py index 1e45bfa..e1a9e80 100644 --- a/gsuid_core/data_store.py +++ b/gsuid_core/data_store.py @@ -1,16 +1,19 @@ from pathlib import Path -from typing import Optional +from typing import List, Union, Optional gs_data_path = Path(__file__).parents[1] / 'data' -def get_res_path(_path: Optional[str] = None) -> Path: +def get_res_path(_path: Optional[Union[str, List]] = None) -> Path: if _path: - path = gs_data_path / _path + if isinstance(_path, str): + path = gs_data_path / _path + else: + path = gs_data_path.joinpath(*_path) else: path = gs_data_path if not path.exists(): - path.mkdir() + path.mkdir(parents=True) return path diff --git a/gsuid_core/gss.py b/gsuid_core/gss.py index 4cd56b8..ed7e926 100644 --- a/gsuid_core/gss.py +++ b/gsuid_core/gss.py @@ -1,13 +1,16 @@ +import asyncio import inspect from gsuid_core.aps import scheduler from gsuid_core.logger import logger from gsuid_core.server import GsServer +from gsuid_core.help.draw_help import get_help_img gss = GsServer() if not gss.is_load: gss.is_load = True gss.load_plugins() + asyncio.run(get_help_img()) repeat_jobs = {} for i in scheduler.get_jobs(): diff --git a/gsuid_core/handler.py b/gsuid_core/handler.py index 702dea9..0a40dd8 100644 --- a/gsuid_core/handler.py +++ b/gsuid_core/handler.py @@ -20,7 +20,7 @@ async def get_user_pml(msg: MessageReceive) -> int: elif msg.user_id in config_superusers: return 1 else: - return msg.user_pm + return msg.user_pm if msg.user_pm >= 1 else 2 async def msg_process(msg: MessageReceive) -> Event: diff --git a/gsuid_core/help/draw_help.py b/gsuid_core/help/draw_help.py new file mode 100644 index 0000000..4495e58 --- /dev/null +++ b/gsuid_core/help/draw_help.py @@ -0,0 +1,197 @@ +from pathlib import Path +from typing import Dict, List, Tuple, Union, Optional + +from PIL import Image, ImageDraw + +from gsuid_core.sv import SV + +# from gsuid_core.utils.image.image_tools import get_color_bg +from gsuid_core.utils.fonts.fonts import core_font + +TEXT_PATH = Path(__file__).parent / 'texture2d' +CORE_HELP_IMG = Path(__file__).parent / 'core_help.jpg' + +plugin_title = 92 +sv_title = 67 + +tag_color = { + 'prefix': (137, 228, 124), + 'suffix': (124, 180, 228), + 'file': (190, 228, 217), + 'keyword': (217, 228, 254), + 'fullmatch': (228, 124, 124), + 'regex': (225, 228, 124), + 'command': (228, 124, 124), + 'other': (228, 190, 191), +} + +tag_text: Dict[str, str] = { + 'prefix': '前缀', + 'suffix': '后缀', + 'file': '文件', + 'keyword': '包含', + 'fullmatch': '完全', + 'regex': '正则', + 'command': '命令', + 'other': '其他', +} + +tags: Dict[str, Optional[Image.Image]] = { + 'prefix': None, + 'suffix': None, + 'file': None, + 'keyword': None, + 'fullmatch': None, + 'regex': None, + 'command': None, + 'other': None, +} + + +def get_tag(tag_type: str) -> Image.Image: + cache = tags[tag_type] + if cache is not None: + return cache + text = tag_text[tag_type] + tag = Image.new('RGBA', (60, 40)) + tag_draw = ImageDraw.Draw(tag) + tag_draw.rounded_rectangle((7, 5, 53, 35), 10, tag_color[tag_type]) + tag_draw.text((30, 20), text, (62, 62, 62), core_font(22), 'mm') + tags[tag_type] = tag + return tag + + +def get_command_bg(command: str, tag_type: str): + img = Image.new('RGBA', (220, 40)) + img_draw = ImageDraw.Draw(img) + img_draw.rounded_rectangle((6, 5, 160, 35), 10, (230, 202, 167)) + img_draw.text((83, 20), command, (62, 62, 62), core_font(20), 'mm') + tag = get_tag(tag_type) + img.paste(tag, (160, 0), tag) + return img + + +def _c(data: Union[int, str, bool]) -> Tuple[int, int, int]: + gray_color = (184, 184, 184) + + if isinstance(data, bool): + color = tag_color['prefix'] if data else gray_color + elif isinstance(data, str): + color = ( + tag_color['prefix'] + if data == 'ALL' + else tag_color['command'] + if data == 'GROUP' + else tag_color['file'] + ) + else: + colors = list(tag_color.values()) + if data <= len(colors) and data >= 0: + color = colors[data] + else: + color = tag_color['other'] + return color + + +def _t(data: Union[int, str, bool]) -> str: + if isinstance(data, bool): + text = '开启' if data else '关闭' + elif isinstance(data, str): + text = '不限' if data == 'ALL' else '群聊' if data == 'GROUP' else '私聊' + else: + texts = ['主人', '超管', '群主', '管理', '频管', '子管', '正常', '低', '黑'] + if data <= len(texts) and data >= 0: + text = ['主人', '超管', '群主', '管理', '频管', '子管', '正常', '低', '黑'][data] + else: + text = '最低' + return text + + +def get_plugin_bg(plugin_name: str, sv_list: List[SV]): + img_list: List[Image.Image] = [] + + for sv in sv_list: + sv_img = Image.new( + 'RGBA', + ( + 900, + sv_title + ((len(sv.TL) + 3) // 4) * 40, + ), + ) + sv_img_draw = ImageDraw.Draw(sv_img) + for index, trigger_name in enumerate(sv.TL): + tg_img = get_command_bg(trigger_name, sv.TL[trigger_name].type) + sv_img.paste( + tg_img, (6 + 220 * (index % 4), 67 + 40 * (index // 4)), tg_img + ) + + sv_img_draw.rounded_rectangle((15, 19, 25, 50), 10, (62, 62, 62)) + sv_img_draw.text((45, 31), sv.name, (62, 62, 62), core_font(36), 'lm') + + sv_img_draw.rounded_rectangle((710, 15, 760, 50), 10, _c(sv.enabled)) + sv_img_draw.rounded_rectangle((770, 15, 820, 50), 10, _c(sv.pm)) + sv_img_draw.rounded_rectangle((830, 15, 880, 50), 10, _c(sv.area)) + + sv_img_draw.text( + (735, 32), _t(sv.enabled), (62, 62, 62), core_font(22), 'mm' + ) + sv_img_draw.text( + (795, 32), _t(sv.pm), (62, 62, 62), core_font(22), 'mm' + ) + sv_img_draw.text( + (855, 32), _t(sv.area), (62, 62, 62), core_font(22), 'mm' + ) + img_list.append(sv_img) + + img = Image.new( + 'RGBA', + ( + 900, + plugin_title + sum([i.size[1] for i in img_list]), + ), + ) + img_draw = ImageDraw.Draw(img) + img_draw.rounded_rectangle((10, 26, 890, 76), 10, (230, 202, 167)) + img_draw.text((450, 51), plugin_name, (62, 62, 62), core_font(42), 'mm') + + temp = 0 + for _img in img_list: + img.paste(_img, (0, 92 + temp), _img) + temp += _img.size[1] + + return img + + +async def get_help_img() -> Image.Image: + from gsuid_core.sv import SL + + content = SL.detail_lst + img_list: List[Image.Image] = [] + for plugin_name in content: + plugin_img = get_plugin_bg(plugin_name, content[plugin_name]) + img_list.append(plugin_img) + + x = 900 + y = 200 + sum([i.size[1] for i in img_list]) + # img = await get_color_bg(x, y) + img = Image.new('RGBA', (x, y), (255, 255, 255)) + title = Image.open(TEXT_PATH / 'title.png') + + # white = Image.new('RGBA', img.size, (255, 255, 255, 120)) + # img.paste(white, (0, 0), white) + img.paste(title, (0, 50), title) + + temp = 0 + for _img in img_list: + img.paste(_img, (0, 340 + temp), _img) + temp += _img.size[1] + + img = img.convert('RGB') + img.save( + CORE_HELP_IMG, + format='JPEG', + quality=80, + subsampling=0, + ) + + return img diff --git a/gsuid_core/help/draw_plugin_help.py b/gsuid_core/help/draw_plugin_help.py new file mode 100644 index 0000000..0b78894 --- /dev/null +++ b/gsuid_core/help/draw_plugin_help.py @@ -0,0 +1,107 @@ +from copy import deepcopy +from typing import Dict, List, Tuple, Callable, Optional + +from PIL import Image, ImageDraw, ImageFont + +from gsuid_core.data_store import get_res_path +from gsuid_core.utils.image.convert import convert_img +from gsuid_core.utils.image.image_tools import ( + crop_center_img, + easy_alpha_composite, +) + +from .model import PluginHelp + +cache: Dict[str, int] = {} + + +async def get_help( + name: str, + sub_text: str, + help_data: Dict[str, PluginHelp], + bg: Image.Image, + icon: Image.Image, + badge: Image.Image, + banner: Image.Image, + button: Image.Image, + font: Callable[[int], ImageFont.FreeTypeFont], + is_dark: bool = True, + text_color: Tuple[int, int, int] = (250, 250, 250), + sub_color: Optional[Tuple[int, int, int]] = None, +) -> bytes: + help_path = get_res_path('help') / f'{name}.jpg' + + if help_path.exists() and name in cache and cache[name]: + return await convert_img(Image.open(help_path)) + + if sub_color is None and is_dark: + sub_color = tuple(x - 50 for x in text_color if x > 50) + elif sub_color is None and not is_dark: + sub_color = tuple(x + 50 for x in text_color if x < 205) + + title = Image.new('RGBA', (900, 600)) + icon = icon.resize((300, 300)) + title.paste(icon, (300, 89), icon) + title.paste(badge, (0, 390), badge) + badge_s = badge.resize((720, 80)) + title.paste(badge_s, (90, 480), badge_s) + title_draw = ImageDraw.Draw(title) + + title_draw.text((450, 440), f'{name} 帮助', text_color, font(36), 'mm') + title_draw.text((450, 520), sub_text, sub_color, font(26), 'mm') + + w, h = 900, 630 + + sv_img_list: List[Image.Image] = [] + for sv_name in help_data: + tr_size = len(help_data[sv_name]['data']) + y = 100 + ((tr_size + 3) // 4) * 80 + h += y + sv_img = Image.new('RGBA', (900, y)) + sv_data = help_data[sv_name]['data'] + sv_desc = help_data[sv_name]['desc'] + + bc = deepcopy(banner) + bc_draw = ImageDraw.Draw(bc) + bc_draw.text((30, 25), sv_name, text_color, font(35), 'lm') + size, _ = font(35).getsize(sv_name) + bc_draw.text((42 + size, 30), sv_desc, sub_color, font(20), 'lm') + sv_img = easy_alpha_composite(sv_img, bc, (0, 10)) + # sv_img.paste(bc, (0, 10), bc) + + for index, tr in enumerate(sv_data): + bt = deepcopy(button) + bt_draw = ImageDraw.Draw(bt) + if len(tr['name']) > 8: + tr_name = tr['name'][:5] + '..' + else: + tr_name = tr['name'] + + bt_draw.text((105, 28), tr_name, text_color, font(26), 'mm') + bt_draw.text((105, 51), tr['eg'], sub_color, font(17), 'mm') + offset_x = 210 * (index % 4) + offset_y = 80 * (index // 4) + sv_img = easy_alpha_composite( + sv_img, bt, (26 + offset_x, 83 + offset_y) + ) + # sv_img.paste(bt, (26 + offset_x, 83 + offset_y), bt) + + sv_img_list.append(sv_img) + + img = crop_center_img(bg, w, h) + img.paste(title, (0, 0), title) + temp = 0 + for _sm in sv_img_list: + img.paste(_sm, (0, 600 + temp), _sm) + temp += _sm.size[1] + + img = img.convert('RGB') + help_path = get_res_path('help') / f'{name}.jpg' + img.save( + help_path, + 'JPEG', + quality=85, + subsampling=0, + ) + cache[name] = 1 + return await convert_img(img) diff --git a/gsuid_core/help/model.py b/gsuid_core/help/model.py new file mode 100644 index 0000000..15283b3 --- /dev/null +++ b/gsuid_core/help/model.py @@ -0,0 +1,15 @@ +from typing import List, TypedDict + + +class PluginSV(TypedDict): + name: str + desc: str + eg: str + need_ck: bool + need_sk: bool + need_admin: bool + + +class PluginHelp(TypedDict): + desc: str + data: List[PluginSV] diff --git a/gsuid_core/help/texture2d/title.png b/gsuid_core/help/texture2d/title.png new file mode 100644 index 0000000..7fc2adf Binary files /dev/null and b/gsuid_core/help/texture2d/title.png differ diff --git a/gsuid_core/plugins/core_command/auto_update/__init__.py b/gsuid_core/plugins/core_command/auto_update/__init__.py index e633cb6..ed167cb 100644 --- a/gsuid_core/plugins/core_command/auto_update/__init__.py +++ b/gsuid_core/plugins/core_command/auto_update/__init__.py @@ -1,8 +1,9 @@ from gsuid_core.aps import scheduler from gsuid_core.logger import logger +from gsuid_core.utils.plugins_update._plugins import update_all_plugins from gsuid_core.utils.plugins_config.gs_config import core_plugins_config -from .auto_task import update_core, restart_core, update_all_plugins +from .auto_task import update_core, restart_core config = core_plugins_config diff --git a/gsuid_core/plugins/core_command/auto_update/auto_task.py b/gsuid_core/plugins/core_command/auto_update/auto_task.py index 7310b3b..03332fb 100644 --- a/gsuid_core/plugins/core_command/auto_update/auto_task.py +++ b/gsuid_core/plugins/core_command/auto_update/auto_task.py @@ -1,6 +1,5 @@ from typing import List -from gsuid_core.utils.plugins_update.api import PLUGINS_PATH from gsuid_core.utils.plugins_update._plugins import update_from_git from gsuid_core.plugins.core_command.core_restart.restart import ( restart_genshinuid, @@ -11,12 +10,5 @@ async def update_core() -> List[str]: return update_from_git() -async def update_all_plugins() -> List[str]: - log_list = [] - for plugin in PLUGINS_PATH.iterdir(): - log_list.extend(update_from_git(0, plugin)) - return log_list - - async def restart_core(): await restart_genshinuid('', '', '', False) diff --git a/gsuid_core/plugins/core_command/core_help/__init__.py b/gsuid_core/plugins/core_command/core_help/__init__.py new file mode 100644 index 0000000..0ded94b --- /dev/null +++ b/gsuid_core/plugins/core_command/core_help/__init__.py @@ -0,0 +1,20 @@ +from gsuid_core.sv import SV +from gsuid_core.bot import Bot +from gsuid_core.models import Event +from gsuid_core.logger import logger +from gsuid_core.utils.image.convert import convert_img +from gsuid_core.help.draw_help import CORE_HELP_IMG, get_help_img + +sv_core_help_img = SV('Core帮助') + + +@sv_core_help_img.on_fullmatch(('core帮助', 'Core帮助')) +async def send_core_htlp_msg(bot: Bot, ev: Event): + logger.info('[早柚核心] 开始执行[帮助图]') + if CORE_HELP_IMG.exists(): + img = await convert_img(CORE_HELP_IMG) + else: + img = await get_help_img() + img = await convert_img(img) + logger.info('[早柚核心] 帮助图获取成功!') + await bot.send(img) diff --git a/gsuid_core/plugins/core_command/core_restart/__init__.py b/gsuid_core/plugins/core_command/core_restart/__init__.py index e2a447e..06e8ce2 100644 --- a/gsuid_core/plugins/core_command/core_restart/__init__.py +++ b/gsuid_core/plugins/core_command/core_restart/__init__.py @@ -52,7 +52,7 @@ async def send_restart_msg(bot: Bot, ev: Event): else: send_id = ev.user_id send_type = 'direct' - await bot.send('正在执行[gs重启]...') + await bot.send('正在执行[core重启]...') await restart_genshinuid(bot.bot_id, send_type, str(send_id)) diff --git a/gsuid_core/plugins/core_command/core_restart/restart.py b/gsuid_core/plugins/core_command/core_restart/restart.py index f970568..2e4f577 100644 --- a/gsuid_core/plugins/core_command/core_restart/restart.py +++ b/gsuid_core/plugins/core_command/core_restart/restart.py @@ -7,7 +7,7 @@ from pathlib import Path from gsuid_core.utils.plugins_config.gs_config import core_plugins_config -bot_start = Path(__file__).parents[4] / 'core.py' +bot_start = Path(__file__).parents[3] / 'core.py' restart_sh_path = Path().cwd() / 'gs_restart.sh' update_log_path = Path(__file__).parent / 'update_log.json' diff --git a/gsuid_core/plugins/core_command/core_update/__init__.py b/gsuid_core/plugins/core_command/core_update/__init__.py new file mode 100644 index 0000000..c9516f0 --- /dev/null +++ b/gsuid_core/plugins/core_command/core_update/__init__.py @@ -0,0 +1,25 @@ +from gsuid_core.sv import SV +from gsuid_core.bot import Bot +from gsuid_core.models import Event +from gsuid_core.logger import logger +from gsuid_core.utils.plugins_update._plugins import ( + update_from_git, + update_all_plugins, +) + +sv_core_config = SV('Core管理', pm=0) + + +@sv_core_config.on_fullmatch(('core更新')) +async def send_core_update_msg(bot: Bot, ev: Event): + logger.info('开始执行[更新] 早柚核心') + log_list = update_from_git() + await bot.send(log_list) + + +@sv_core_config.on_fullmatch(('core全部更新')) +async def send_core_all_update_msg(bot: Bot, ev: Event): + logger.info('开始执行[更新] 全部更新') + log_list = update_from_git() + log_list.extend(await update_all_plugins()) + await bot.send(log_list) diff --git a/gsuid_core/plugins/core_command/core_user/__init__.py b/gsuid_core/plugins/core_command/core_user/__init__.py new file mode 100644 index 0000000..c639662 --- /dev/null +++ b/gsuid_core/plugins/core_command/core_user/__init__.py @@ -0,0 +1,15 @@ +from gsuid_core.sv import SV +from gsuid_core.bot import Bot +from gsuid_core.models import Event + +from .draw_user_card import get_user_card + +core_user_info = SV('core用户信息') + + +@core_user_info.on_fullmatch(('绑定信息')) +async def send_bind_card(bot: Bot, ev: Event): + await bot.logger.info('开始执行[查询用户绑定状态]') + im = await get_user_card(ev.bot_id, ev.user_id) + await bot.logger.info('[查询用户绑定状态]完成!等待图片发送中...') + await bot.send(im) diff --git a/gsuid_core/plugins/core_command/core_user/draw_user_card.py b/gsuid_core/plugins/core_command/core_user/draw_user_card.py new file mode 100644 index 0000000..d60f1fd --- /dev/null +++ b/gsuid_core/plugins/core_command/core_user/draw_user_card.py @@ -0,0 +1,133 @@ +from pathlib import Path +from typing import List, Tuple, Union, Optional + +from PIL import Image, ImageDraw + +from gsuid_core.utils.database.api import DBSqla +from gsuid_core.utils.fonts.fonts import core_font +from gsuid_core.utils.database.models import GsPush +from gsuid_core.utils.image.convert import convert_img +from gsuid_core.utils.image.image_tools import ( + get_color_bg, + get_qq_avatar, + draw_pic_with_ring, + easy_alpha_composite, +) + +TEXT_PATH = Path(__file__).parent / 'texture2d' + +status_off = Image.open(TEXT_PATH / 'status_off.png') +status_on = Image.open(TEXT_PATH / 'status_on.png') + +EN_MAP = {'coin': '宝钱', 'resin': '体力', 'go': '派遣', 'transform': '质变仪'} + + +async def get_user_card(bot_id: str, user_id: str) -> Union[bytes, str]: + get_sqla = DBSqla().get_sqla + sqla = get_sqla(bot_id) + uid_list: List = await sqla.get_bind_uid_list(user_id) + sr_uid_list = await sqla.get_bind_sruid_list(user_id) + user_list = await sqla.select_user_all_data_by_user_id(user_id) + + if user_list is None: + return '你还没有绑定过UID或者CK!' + + w, h = 750, len(max(uid_list, sr_uid_list)) * 900 + 470 + + # 获取背景图片各项参数 + _id = str(user_id) + if _id.startswith('http'): + char_pic = await get_qq_avatar(avatar_url=_id) + else: + char_pic = await get_qq_avatar(qid=_id) + char_pic = await draw_pic_with_ring(char_pic, 290) + + img = await get_color_bg(w, h) + img_mask = Image.new('RGBA', img.size, (255, 255, 255)) + title = Image.open(TEXT_PATH / 'user_title.png') + title.paste(char_pic, (241, 40), char_pic) + + title_draw = ImageDraw.Draw(title) + title_draw.text( + (375, 444), f'{bot_id} - {user_id}', (29, 29, 29), core_font(30), 'mm' + ) + img.paste(title, (0, 0), title) + + for index, user_data in enumerate(user_list): + user_card = Image.open(TEXT_PATH / 'user_bg.png') + user_draw = ImageDraw.Draw(user_card) + + if user_data.uid is not None and user_data.uid != '0': + uid_text = f'原神UID {user_data.uid}' + user_push_data = await sqla.select_push_data(user_data.uid) + else: + uid_text = '未发现原神UID' + user_push_data = GsPush(bot_id='TEMP') + + user_draw.text( + (375, 58), + uid_text, + (29, 29, 29), + font=core_font(36), + anchor='mm', + ) + + if user_data.sr_uid: + sruid_text = f'星铁UID {user_data.sr_uid}' + else: + sruid_text = '未发现星铁UID' + + user_draw.text( + (375, 119), + sruid_text, + (29, 29, 29), + font=core_font(36), + anchor='mm', + ) + + x, y = 331, 112 + b = 175 + paste_switch(user_card, user_data.cookie, (241, b)) + paste_switch(user_card, user_data.stoken, (241 + x, b)) + paste_switch(user_card, user_data.sign_switch, (241, b + y)) + paste_switch(user_card, user_data.bbs_switch, (241 + x, b + y)) + paste_switch(user_card, user_data.push_switch, (241, b + 2 * y)) + paste_switch(user_card, user_data.status, (241 + x, b + 2 * y), True) + + for _index, mode in enumerate(['coin', 'resin', 'go', 'transform']): + paste_switch( + user_card, + getattr(user_push_data, f'{mode}_push'), + (241 + _index % 2 * x, b + (_index // 2 + 3) * y), + ) + if getattr(user_push_data, f'{mode}_push') != 'off': + user_draw.text( + (268 + _index % 2 * x, 168 + 47 + (_index // 2 + 3) * y), + f'{getattr(user_push_data, f"{mode}_value")}', + (35, 35, 35), + font=core_font(15), + anchor='lm', + ) + + sr_sign = user_data.sr_sign_switch + sr_push = user_data.sr_push_switch + paste_switch(user_card, sr_sign, (241, b + 5 * y)) + paste_switch(user_card, sr_push, (241 + x, b + 5 * y)) + + img.paste(user_card, (0, 500 + index * 870), user_card) + + img = easy_alpha_composite(img_mask, img, (0, 0)) + return await convert_img(img) + + +def paste_switch( + card: Image.Image, + status: Optional[str], + pos: Tuple[int, int], + is_status: bool = False, +): + if is_status: + pic = status_off if status else status_on + else: + pic = status_on if status != 'off' and status else status_off + card.paste(pic, pos, pic) diff --git a/gsuid_core/plugins/core_command/core_user/texture2d/status_off.png b/gsuid_core/plugins/core_command/core_user/texture2d/status_off.png new file mode 100644 index 0000000..7d3b8f0 Binary files /dev/null and b/gsuid_core/plugins/core_command/core_user/texture2d/status_off.png differ diff --git a/gsuid_core/plugins/core_command/core_user/texture2d/status_on.png b/gsuid_core/plugins/core_command/core_user/texture2d/status_on.png new file mode 100644 index 0000000..f289fe8 Binary files /dev/null and b/gsuid_core/plugins/core_command/core_user/texture2d/status_on.png differ diff --git a/gsuid_core/plugins/core_command/core_user/texture2d/user_bg.png b/gsuid_core/plugins/core_command/core_user/texture2d/user_bg.png new file mode 100644 index 0000000..56e89eb Binary files /dev/null and b/gsuid_core/plugins/core_command/core_user/texture2d/user_bg.png differ diff --git a/gsuid_core/plugins/core_command/core_user/texture2d/user_title.png b/gsuid_core/plugins/core_command/core_user/texture2d/user_title.png new file mode 100644 index 0000000..d969e3b Binary files /dev/null and b/gsuid_core/plugins/core_command/core_user/texture2d/user_title.png differ diff --git a/gsuid_core/segment.py b/gsuid_core/segment.py index 82adea2..adef5be 100644 --- a/gsuid_core/segment.py +++ b/gsuid_core/segment.py @@ -25,6 +25,8 @@ class MessageSegment: with open(str(img), 'rb') as fp: img = fp.read() else: + if img.startswith('http'): + return Message(type='image', data=f'link://{img}') if img.startswith('base64://'): return Message(type='image', data=img) with open(img, 'rb') as fp: @@ -53,6 +55,10 @@ class MessageSegment: else: if msg.startswith('base64://'): msg_list.append(Message(type='image', data=msg)) + elif msg.startswith('http'): + msg_list.append( + Message(type='image', data=f'link://{msg}') + ) else: msg_list.append(MessageSegment.text(msg)) return Message(type='node', data=msg_list) @@ -79,8 +85,15 @@ class MessageSegment: elif isinstance(content, bytes): file = content else: - with open(content, 'rb') as fp: - file = fp.read() + if content.startswith('http'): + link = content + return Message( + type='file', + data=f'{file_name}|link://{link}', + ) + else: + with open(content, 'rb') as fp: + file = fp.read() return Message( type='file', data=f'{file_name}|{b64encode(file).decode()}', diff --git a/gsuid_core/sv.py b/gsuid_core/sv.py index 85f1039..d5514b5 100644 --- a/gsuid_core/sv.py +++ b/gsuid_core/sv.py @@ -1,5 +1,7 @@ from __future__ import annotations +import traceback +from pathlib import Path from functools import wraps from typing import Dict, List, Tuple, Union, Literal, Callable, Optional @@ -11,6 +13,7 @@ from gsuid_core.config import core_config class SVList: def __init__(self): self.lst: Dict[str, SV] = {} + self.detail_lst: Dict[str, List[SV]] = {} @property def get_lst(self): @@ -58,6 +61,16 @@ class SV: # sv内包含的触发器 self.TL: Dict[str, Trigger] = {} self.is_initialized = True + stack = traceback.extract_stack() + file = stack[-2].filename + path = Path(file) + parts = path.parts + i = parts.index('plugins') + plugins_name = parts[i + 1] + if plugins_name not in SL.detail_lst: + SL.detail_lst[plugins_name] = [self] + else: + SL.detail_lst[plugins_name].append(self) # 判断sv是否已持久化 if name in config_sv: diff --git a/gsuid_core/tools/gen_help.py b/gsuid_core/tools/gen_help.py new file mode 100644 index 0000000..6a3d89c --- /dev/null +++ b/gsuid_core/tools/gen_help.py @@ -0,0 +1,65 @@ +import copy +import json +from pathlib import Path + +from openpyxl import load_workbook + +sample = { + 'name': '', + 'desc': '', + 'eg': '', + 'need_ck': False, + 'need_sk': False, + 'need_admin': False, +} + +result = {} + +HELP_PATH = Path(__file__).parent / 'Help.xlsx' +OUTPUT_PATH = Path(__file__).parent / 'Help.json' + +wb = load_workbook(str(HELP_PATH)) +ws = wb.active + +module_name_str = '' +for row in range(2, 999): + # 跳过空白行 + if not ws.cell(row, 2).value: + continue + + _sample = copy.deepcopy(sample) + + # 将第一列读取为模块名 + if ws.cell(row, 1): + if ws.cell(row, 1).value is not None: + module_name_str = ws.cell(row, 1).value + + # if module_name_str is None and not isinstance(module_name_str, str): + # continue + + # 第二列为功能名 + _sample['name'] = ws.cell(row, 2).value + # 第三列为详细信息 + _sample['desc'] = ws.cell(row, 3).value + # 第四列为使用例 + _sample['eg'] = ws.cell(row, 4).value + + if ws.cell(row, 5).value == '是': + _sample['need_ck'] = True + + if ws.cell(row, 6).value == '是': + _sample['need_sk'] = True + + if ws.cell(row, 7).value == '是': + _sample['need_admin'] = True + + if isinstance(module_name_str, str): + module_name = module_name_str.split(' | ')[0] + module_desc = module_name_str.split(' | ')[1] + if module_name not in result: + result[module_name] = {'desc': module_desc, 'data': []} + + result[module_name]['data'].append(_sample) + +with open(OUTPUT_PATH, 'w', encoding='utf-8') as f: + json.dump(result, f, indent=2, ensure_ascii=False) diff --git a/gsuid_core/utils/api/mys/api.py b/gsuid_core/utils/api/mys/api.py index e8598e0..2995b58 100644 --- a/gsuid_core/utils/api/mys/api.py +++ b/gsuid_core/utils/api/mys/api.py @@ -134,4 +134,6 @@ CALENDAR_URL = f'{DRAW_BASE_URL}/calendar' RECEIVE_URL = f'{DRAW_BASE_URL}/post_my_draw' BS_INDEX_URL = f'{DRAW_BASE_URL}/index' +GET_FP_URL = 'https://public-data-api.mihoyo.com/device-fp/api/getFp' + _API = locals() diff --git a/gsuid_core/utils/api/mys/models.py b/gsuid_core/utils/api/mys/models.py index d841e64..2dea2c3 100644 --- a/gsuid_core/utils/api/mys/models.py +++ b/gsuid_core/utils/api/mys/models.py @@ -761,20 +761,24 @@ class RoleCalendar(TypedDict): is_subscribe: bool +class RoleCalendarList(TypedDict): + calendar_role: List[RoleCalendar] + + MonthlyRoleCalendar = TypedDict( 'MonthlyRoleCalendar', { - '1': List[RoleCalendar], - '2': List[RoleCalendar], - '3': List[RoleCalendar], - '4': List[RoleCalendar], - '5': List[RoleCalendar], - '6': List[RoleCalendar], - '7': List[RoleCalendar], - '8': List[RoleCalendar], - '9': List[RoleCalendar], - '10': List[RoleCalendar], - '11': List[RoleCalendar], - '12': List[RoleCalendar], + '1': RoleCalendarList, + '2': RoleCalendarList, + '3': RoleCalendarList, + '4': RoleCalendarList, + '5': RoleCalendarList, + '6': RoleCalendarList, + '7': RoleCalendarList, + '8': RoleCalendarList, + '9': RoleCalendarList, + '10': RoleCalendarList, + '11': RoleCalendarList, + '12': RoleCalendarList, }, ) diff --git a/gsuid_core/utils/api/mys/request.py b/gsuid_core/utils/api/mys/request.py index 614fd43..81b0e28 100644 --- a/gsuid_core/utils/api/mys/request.py +++ b/gsuid_core/utils/api/mys/request.py @@ -9,11 +9,12 @@ import uuid import random from abc import abstractmethod from string import digits, ascii_letters -from typing import Any, Dict, List, Union, Literal, Optional, cast +from typing import Any, Dict, List, Tuple, Union, Literal, Optional, cast from aiohttp import TCPConnector, ClientSession, ContentTypeError from gsuid_core.logger import logger +from gsuid_core.utils.database.api import DBSqla from gsuid_core.utils.plugins_config.gs_config import core_plugins_config from .api import _API @@ -87,13 +88,17 @@ class BaseMysApi: MAPI = _API is_sr = False RECOGNIZE_SERVER = RECOGNIZE_SERVER + chs = {} + dbsqla: DBSqla = DBSqla() @abstractmethod - async def _upass(self, header: Dict): + async def _upass(self, header: Dict) -> str: ... @abstractmethod - async def _pass(self, gt: str, ch: str, header: Dict): + async def _pass( + self, gt: str, ch: str, header: Dict + ) -> Tuple[Optional[str], Optional[str]]: ... @abstractmethod @@ -106,6 +111,59 @@ class BaseMysApi: async def get_stoken(self, uid: str) -> Optional[str]: ... + @abstractmethod + async def get_user_fp(self, uid: str) -> Optional[str]: + ... + + @abstractmethod + async def get_user_device_id(self, uid: str) -> Optional[str]: + ... + + def get_device_id(self) -> str: + device_id = str(uuid.uuid4()).upper() + return device_id + + def generate_seed(self, length: int): + characters = '0123456789abcdef' + result = ''.join(random.choices(characters, k=length)) + return result + + async def generate_fp_by_uid(self, uid: str) -> str: + seed_id = self.generate_seed(16) + seed_time = str(int(time.time() * 1000)) + ext_fields = f'{{"userAgent":"{self._HEADER["User-Agent"]}",\ +"browserScreenSize":281520,"maxTouchPoints":5,\ +"isTouchSupported":true,"browserLanguage":"zh-CN","browserPlat":"iPhone",\ +"browserTimeZone":"Asia/Shanghai","webGlRender":"Apple GPU",\ +"webGlVendor":"Apple Inc.",\ +"numOfPlugins":0,"listOfPlugins":"unknown","screenRatio":3,"deviceMemory":"unknown",\ +"hardwareConcurrency":"4","cpuClass":"unknown","ifNotTrack":"unknown","ifAdBlock":0,\ +"hasLiedResolution":1,"hasLiedOs":0,"hasLiedBrowser":0}}' + body = { + 'seed_id': seed_id, + 'device_id': await self.get_user_device_id(uid), + 'platform': '5', + 'seed_time': seed_time, + 'ext_fields': ext_fields, + 'app_name': 'account_cn', + 'device_fp': '38d7ee834d1e9', + } + HEADER = copy.deepcopy(self._HEADER) + res = await self._mys_request( + url=self.MAPI['GET_FP_URL'], + method='POST', + header=HEADER, + data=body, + ) + if not isinstance(res, Dict): + logger.error(f"获取fp连接失败{res}") + return random_hex(13).lower() + elif res["data"]["code"] != 200: + logger.error(f"获取fp参数不正确{res['data']['msg']}") + return random_hex(13).lower() + else: + return res["data"]["device_fp"] + async def simple_mys_req( self, URL: str, @@ -196,6 +254,113 @@ class BaseMysApi: async with ClientSession( connector=TCPConnector(verify_ssl=ssl_verify) ) as client: + raw_data = {} + uid = None + if params and 'role_id' in params: + uid = params['role_id'] + header['x-rpc-device_id'] = await self.get_user_device_id(uid) + header['x-rpc-device_fp'] = await self.get_user_fp(uid) + + for _ in range(3): + if 'Cookie' in header and header['Cookie'] in self.chs: + # header['x-rpc-challenge']=self.chs.pop(header['Cookie']) + if self.is_sr: + header['x-rpc-challenge'] = self.chs.pop( + header['Cookie'] + ) + if isinstance(params, Dict): + header['DS'] = get_ds_token( + '&'.join( + [f'{k}={v}' for k, v in params.items()] + ) + ) + + header['x-rpc-challenge_game'] = '6' if self.is_sr else '2' + header['x-rpc-page'] = ( + '3.1.3_#/rpg' if self.is_sr else '3.1.3_#/ys' + ) + + if ( + 'x-rpc-challenge' in header + and not header['x-rpc-challenge'] + ): + del header['x-rpc-challenge'] + del header['x-rpc-page'] + del header['x-rpc-challenge_game'] + + print(header) + async with client.request( + method, + url=url, + headers=header, + params=params, + json=data, + proxy=self.proxy_url if use_proxy else None, + timeout=300, + ) as resp: + try: + raw_data = await resp.json() + except ContentTypeError: + _raw_data = await resp.text() + raw_data = {'retcode': -999, 'data': _raw_data} + logger.debug(raw_data) + + # 判断retcode + if 'retcode' in raw_data: + retcode: int = raw_data['retcode'] + elif 'code' in raw_data: + retcode: int = raw_data['code'] + else: + retcode = 0 + + # 针对1034做特殊处理 + if retcode == 1034: + if uid and self.is_sr and _ == 0: + sqla = self.dbsqla.get_sqla('TEMP') + new_fp = await self.generate_fp_by_uid(uid) + await sqla.update_user_data(uid, {'fp': new_fp}) + header['x-rpc-device_fp'] = new_fp + if isinstance(params, Dict): + header['DS'] = get_ds_token( + '&'.join( + [f'{k}={v}' for k, v in params.items()] + ) + ) + else: + ch = await self._upass(header) + self.chs[header['Cookie']] = ch + elif retcode == -10001 and uid: + sqla = self.dbsqla.get_sqla('TEMP') + new_fp = await self.generate_fp_by_uid(uid) + await sqla.update_user_data(uid, {'fp': new_fp}) + header['x-rpc-device_fp'] = new_fp + elif retcode != 0: + return retcode + else: + return raw_data + else: + return -999 + + ''' + async def _mys_request( + self, + url: str, + method: Literal['GET', 'POST'] = 'GET', + header: Dict[str, Any] = _HEADER, + params: Optional[Dict[str, Any]] = None, + data: Optional[Dict[str, Any]] = None, + use_proxy: Optional[bool] = False, + ) -> Union[Dict, int]: + import types + import inspect + + async with ClientSession( + connector=TCPConnector(verify_ssl=ssl_verify) + ) as client: + if 'Cookie' in header: + if header['Cookie'] in self.chs: + header['x-rpc-challenge'] = self.chs.pop(header["Cookie"]) + async with client.request( method, url=url, @@ -211,22 +376,73 @@ class BaseMysApi: _raw_data = await resp.text() raw_data = {'retcode': -999, 'data': _raw_data} logger.debug(raw_data) + + # 判断retcode if 'retcode' in raw_data: retcode: int = raw_data['retcode'] elif 'code' in raw_data: retcode: int = raw_data['code'] else: retcode = 0 + + # 针对1034做特殊处理 if retcode == 1034: - await self._upass(header) - return retcode + try: + # 获取ch + ch = await self._upass(header) + # 记录ck -> ch的对照表 + if "Cookie" in header: + self.chs[header["Cookie"]] = ch + # 获取当前的栈帧 + curframe = inspect.currentframe() + # 确保栈帧存在 + assert curframe + # 获取调用者的栈帧 + calframe = curframe.f_back + # 确保调用者的栈帧存在 + assert calframe + # 获取调用者的函数名 + caller_name = calframe.f_code.co_name + # 获取调用者函数的局部变量字典 + caller_args = inspect.getargvalues(calframe).locals + # 获取调用者的参数列表 + caller_args2 = inspect.getargvalues(calframe).args + # # 生成一个字典,键为调用者的参数名,值为对应的局部变量值,如果不存在则为None + caller_args3 = { + k: caller_args.get(k, None) for k in caller_args2 + } + if caller_name != '_mys_req_get': + return await types.FunctionType( + calframe.f_code, globals() + )(**caller_args3) + else: + curframe = calframe + calframe = curframe.f_back + assert calframe + caller_name = calframe.f_code.co_name + caller_args = inspect.getargvalues(calframe).locals + caller_args2 = inspect.getargvalues(calframe).args + caller_args3 = { + k: caller_args.get(k, None) + for k in caller_args2 + } + return await types.FunctionType( + calframe.f_code, globals() + )(**caller_args3) + except Exception as e: + logger.error(e) + traceback.print_exc() + return -999 elif retcode != 0: return retcode return raw_data + ''' class MysApi(BaseMysApi): - async def _pass(self, gt: str, ch: str, header: Dict): + async def _pass( + self, gt: str, ch: str, header: Dict + ) -> Tuple[Optional[str], Optional[str]]: # 警告:使用该服务(例如某RR等)需要注意风险问题 # 本项目不以任何形式提供相关接口 # 代码来源:GITHUB项目MIT开源 @@ -247,13 +463,14 @@ class MysApi(BaseMysApi): return validate, ch - async def _upass(self, header: Dict, is_bbs: bool = False): + async def _upass(self, header: Dict, is_bbs: bool = False) -> str: + logger.info('[upass] 进入处理...') if is_bbs: raw_data = await self.get_bbs_upass_link(header) else: raw_data = await self.get_upass_link(header) if isinstance(raw_data, int): - return False + return '' gt = raw_data['data']['gt'] ch = raw_data['data']['challenge'] @@ -261,8 +478,13 @@ class MysApi(BaseMysApi): if vl: await self.get_header_and_vl(header, ch, vl) + if ch: + logger.info(f'[upass] 获取ch -> {ch}') + return ch + else: + return '' else: - return True + return '' async def get_upass_link(self, header: Dict) -> Union[int, Dict]: header['DS'] = get_ds_token('is_high=false') @@ -958,7 +1180,11 @@ class MysApi(BaseMysApi): 'currency': 'CNY', 'pay_plat': method, } - data = {'order': order, 'sign': gen_payment_sign(order)} + data = { + 'order': order, + 'special_info': 'topup_center', + 'sign': gen_payment_sign(order), + } HEADER['x-rpc-device_id'] = device_id HEADER['x-rpc-client_type'] = '4' resp = await self._mys_request( diff --git a/gsuid_core/utils/api/mys_api.py b/gsuid_core/utils/api/mys_api.py index 42af8c6..ea3ac9b 100644 --- a/gsuid_core/utils/api/mys_api.py +++ b/gsuid_core/utils/api/mys_api.py @@ -1,56 +1,15 @@ -from typing import Dict, Literal, Optional +from typing import Literal, Optional from gsuid_core.utils.api.mys import MysApi -from gsuid_core.utils.database.api import DBSqla from gsuid_core.utils.plugins_config.gs_config import core_plugins_config gsconfig = core_plugins_config class _MysApi(MysApi): - dbsqla: DBSqla = DBSqla() - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - async def _pass(self, gt: str, ch: str, header: Dict): - # 警告:使用该服务(例如某RR等)需要注意风险问题 - # 本项目不以任何形式提供相关接口 - # 代码来源:GITHUB项目MIT开源 - _pass_api = gsconfig.get_config('_pass_API').data - if _pass_api: - data = await self._mys_request( - url=f'{_pass_api}>={gt}&challenge={ch}', - method='GET', - header=header, - ) - if isinstance(data, int): - return None, None - else: - validate = data['data']['validate'] - ch = data['data']['challenge'] - else: - validate = None - - return validate, ch - - async def _upass(self, header: Dict, is_bbs: bool = False): - if is_bbs: - raw_data = await self.get_bbs_upass_link(header) - else: - raw_data = await self.get_upass_link(header) - if isinstance(raw_data, int): - return False - gt = raw_data['data']['gt'] - ch = raw_data['data']['challenge'] - - vl, ch = await self._pass(gt, ch, header) - - if vl: - await self.get_header_and_vl(header, ch, vl) - else: - return True - async def get_ck( self, uid: str, mode: Literal['OWNER', 'RANDOM'] = 'RANDOM' ) -> Optional[str]: @@ -62,5 +21,23 @@ class _MysApi(MysApi): async def get_stoken(self, uid: str) -> Optional[str]: return await self.dbsqla.get_sqla('TEMP').get_user_stoken(uid) + async def get_user_fp(self, uid: str) -> Optional[str]: + data = await self.dbsqla.get_sqla('TEMP').get_user_fp(uid) + if data is None: + data = await self.generate_fp_by_uid(uid) + await self.dbsqla.get_sqla('TEMP').update_user_data( + uid, {'fp': data} + ) + return data + + async def get_user_device_id(self, uid: str) -> Optional[str]: + data = await self.dbsqla.get_sqla('TEMP').get_user_device_id(uid) + if data is None: + data = self.get_device_id() + await self.dbsqla.get_sqla('TEMP').update_user_data( + uid, {'device_id': data} + ) + return data + mys_api = _MysApi() diff --git a/gsuid_core/utils/cookie_manager/add_ck.py b/gsuid_core/utils/cookie_manager/add_ck.py index d032a9a..d790146 100644 --- a/gsuid_core/utils/cookie_manager/add_ck.py +++ b/gsuid_core/utils/cookie_manager/add_ck.py @@ -217,7 +217,7 @@ async def _deal_ck(bot_id: str, mes: str, user_id: str) -> str: uid_bind = i['game_role_id'] elif i['game_id'] == 6: sr_uid_bind = i['game_role_id'] - if uid and sr_uid: + if uid_bind and sr_uid_bind: break else: if not (uid_bind or sr_uid_bind): @@ -236,8 +236,16 @@ async def _deal_ck(bot_id: str, mes: str, user_id: str) -> str: if uid is None: uid = '0' + device_id = mys_api.get_device_id() + fp = await mys_api.generate_fp_by_uid(uid) await sqla.insert_user_data( - user_id, uid_bind, sr_uid_bind, account_cookie, app_cookie + user_id, + uid_bind, + sr_uid_bind, + account_cookie, + app_cookie, + fp, + device_id, ) im_list.append( diff --git a/gsuid_core/utils/cookie_manager/qrlogin.py b/gsuid_core/utils/cookie_manager/qrlogin.py index 80ce805..0a7564f 100644 --- a/gsuid_core/utils/cookie_manager/qrlogin.py +++ b/gsuid_core/utils/cookie_manager/qrlogin.py @@ -2,10 +2,13 @@ import io import json import base64 import asyncio +from pathlib import Path from http.cookies import SimpleCookie from typing import Any, List, Tuple, Union, Literal import qrcode +import aiofiles +from qrcode.image.pil import PilImage from qrcode.constants import ERROR_CORRECT_L from gsuid_core.bot import Bot @@ -15,17 +18,10 @@ from gsuid_core.segment import MessageSegment from gsuid_core.utils.api.mys_api import mys_api from gsuid_core.utils.database.api import DBSqla -disnote = '''免责声明:您将通过扫码完成获取米游社sk以及ck。 -本Bot将不会保存您的登录状态。 -我方仅提供米游社查询及相关游戏内容服务 -若您的账号封禁、被盗等处罚与我方无关。 -害怕风险请勿扫码! -''' - get_sqla = DBSqla().get_sqla -def get_qrcode_base64(url): +async def get_qrcode_base64(url: str, path: Path, bot_id: str) -> bytes: qr = qrcode.QRCode( version=1, error_correction=ERROR_CORRECT_L, @@ -34,11 +30,33 @@ def get_qrcode_base64(url): ) qr.add_data(url) qr.make(fit=True) - img = qr.make_image(fill_color='black', back_color='white') - img_byte = io.BytesIO() - img.save(img_byte, format='PNG') # type: ignore - img_byte = img_byte.getvalue() - return base64.b64encode(img_byte).decode() + img = qr.make_image(fill_color=(255, 134, 36), back_color='white') + assert isinstance(img, PilImage) + + if bot_id == 'onebot': + img = img.resize((700, 700)) # type: ignore + img.save( # type: ignore + path, + format='PNG', + save_all=True, + append_images=[img], + duration=100, + loop=0, + ) + async with aiofiles.open(path, 'rb') as fp: + img = await fp.read() + elif bot_id == 'onebot_v12': + img_byte = io.BytesIO() + img.save(img_byte, format='PNG') # type: ignore + img_byte = img_byte.getvalue() + img = f'base64://{base64.b64encode(img_byte).decode()}' + return img # type: ignore + else: + img_byte = io.BytesIO() + img.save(img_byte, format='PNG') # type: ignore + img = img_byte.getvalue() + + return img async def refresh( @@ -51,15 +69,15 @@ async def refresh( code_data['app_id'], code_data['ticket'], code_data['device'] ) if isinstance(status_data, int): - logger.warning('二维码已过期') + logger.warning('[登录]二维码已过期') return False, None if status_data['stat'] == 'Scanned': if not scanned: - logger.info('二维码已扫描') + logger.info('[登录]二维码已扫描') scanned = True continue if status_data['stat'] == 'Confirmed': - logger.info('二维码已确认') + logger.info('[登录]二维码已确认') break return True, json.loads(status_data['payload']['raw']) @@ -73,39 +91,41 @@ async def qrcode_login(bot: Bot, ev: Event, user_id: str) -> str: code_data = await mys_api.create_qrcode_url() if isinstance(code_data, int): - return await send_msg('链接创建失败...') - try: - im = [] - im.append(MessageSegment.text('请使用米游社扫描下方二维码登录:')) - im.append( - MessageSegment.image( - f'base64://{get_qrcode_base64(code_data["url"])}' - ) + return await send_msg('[登录]链接创建失败...') + + path = Path(__file__).parent / f'{user_id}.gif' + + im = [] + im.append(MessageSegment.text('请使用米游社扫描下方二维码登录:')) + im.append( + MessageSegment.image( + await get_qrcode_base64(code_data['url'], path, ev.bot_id) ) - im.append( - MessageSegment.text( - '免责声明:您将通过扫码完成获取米游社sk以及ck。\n' - '本Bot将不会保存您的登录状态。\n' - '我方仅提供米游社查询及相关游戏内容服务,\n' - '若您的账号封禁、被盗等处罚与我方无关。\n' - '害怕风险请勿扫码~' - ) + ) + im.append( + MessageSegment.text( + '免责声明:您将通过扫码完成获取米游社sk以及ck。\n' + '我方仅提供米游社查询及相关游戏内容服务,\n' + '若您的账号封禁、被盗等处罚与我方无关。\n' + '害怕风险请勿扫码~' ) - await bot.send(MessageSegment.node(im)) - except Exception as e: - logger.error(e) - logger.warning(f'[扫码登录] {user_id} 图片发送失败') + ) + await bot.send(MessageSegment.node(im)) + + if path.exists(): + path.unlink() + status, game_token_data = await refresh(code_data) if status: assert game_token_data is not None # 骗过 pyright - logger.info('game_token获取成功') + logger.info('[登录]game_token获取成功') cookie_token = await mys_api.get_cookie_token(**game_token_data) stoken_data = await mys_api.get_stoken_by_game_token( account_id=int(game_token_data['uid']), game_token=game_token_data['token'], ) if isinstance(stoken_data, int): - return await send_msg('获取SK失败...') + return await send_msg('[登录]获取SK失败...') account_id = game_token_data['uid'] stoken = stoken_data['token']['token'] mid = stoken_data['user_info']['mid'] @@ -114,7 +134,7 @@ async def qrcode_login(bot: Bot, ev: Event, user_id: str) -> str: stoken, account_id, app_cookie ) if isinstance(ck, int): - return await send_msg('获取CK失败...') + return await send_msg('[登录]获取CK失败...') ck = ck['cookie_token'] cookie_check = f'account_id={account_id};cookie_token={ck}' get_uid = await mys_api.get_mihoyo_bbs_info(account_id, cookie_check) @@ -131,26 +151,26 @@ async def qrcode_login(bot: Bot, ev: Event, user_id: str) -> str: if uid_check or sruid_check: pass else: - im = f'你的米游社账号{account_id}尚未绑定原神账号,请前往米游社操作!' + im = f'[登录]你的米游社账号{account_id}尚未绑定原神账号,请前往米游社操作!' return await send_msg(im) else: - im = '请求失败, 请稍后再试...' + im = '[登录]请求失败, 请稍后再试...' return await send_msg(im) - uid_bind = await sqla.get_bind_uid(user_id) - sruid_bind = await sqla.get_bind_sruid(user_id) + uid_bind_list = await sqla.get_bind_uid_list(user_id) + sruid_bind_list = await sqla.get_bind_sruid_list(user_id) # 没有在gsuid绑定uid的情况 - if not (uid_bind or sruid_bind): - logger.warning('game_token获取失败') + if not (uid_bind_list or sruid_bind_list): + logger.warning('[登录]game_token获取失败') im = '你还没有绑定uid, 请输入[绑定uid123456]绑定你的uid, 再发送[扫码登录]进行绑定' return await send_msg(im) if isinstance(cookie_token, int): - return await send_msg('获取CK失败...') + return await send_msg('[登录]获取CK失败...') # 比对gsuid数据库和扫码登陆获取到的uid if ( - str(uid_bind) == uid_check - or str(sruid_bind) == str(sruid_check) - or str(uid_bind) == account_id + uid_check in uid_bind_list + or sruid_check in sruid_bind_list + or account_id in uid_bind_list ): return SimpleCookie( { @@ -161,12 +181,12 @@ async def qrcode_login(bot: Bot, ev: Event, user_id: str) -> str: } ).output(header='', sep=';') else: - logger.warning('game_token获取失败') + logger.warning('[登录]game_token获取失败') im = ( - f'检测到扫码登录UID{uid_check}与绑定UID{uid_bind}不同, ' + f'检测到扫码登录UID{uid_check}与绑定UID不同, ' 'gametoken获取失败, 请重新发送[扫码登录]进行登录!' ) else: - logger.warning('game_token获取失败') - im = 'game_token获取失败: 二维码已过期' + logger.warning('[登录]game_token获取失败') + im = '[登录]game_token获取失败: 二维码已过期' return await send_msg(im) diff --git a/gsuid_core/utils/database/dal.py b/gsuid_core/utils/database/dal.py index c5c4a45..8f5288f 100644 --- a/gsuid_core/utils/database/dal.py +++ b/gsuid_core/utils/database/dal.py @@ -1,6 +1,5 @@ import re import asyncio -import contextlib from typing import Dict, List, Literal, Optional from sqlmodel import SQLModel @@ -43,13 +42,20 @@ class SQLA: 'ALTER TABLE GsBind ADD COLUMN sr_uid TEXT', 'ALTER TABLE GsUser ADD COLUMN sr_uid TEXT', 'ALTER TABLE GsUser ADD COLUMN sr_region TEXT', + 'ALTER TABLE GsUser ADD COLUMN fp TEXT', + 'ALTER TABLE GsUser ADD COLUMN device_id TEXT', + 'ALTER TABLE GsUser ADD COLUMN sr_sign_switch TEXT DEFAULT "off"', + 'ALTER TABLE GsUser ADD COLUMN sr_push_switch TEXT DEFAULT "off"', + 'ALTER TABLE GsUser ADD COLUMN draw_switch TEXT DEFAULT "off"', 'ALTER TABLE GsCache ADD COLUMN sr_uid TEXT', ] - with contextlib.suppress(Exception): - async with self.async_session() as session: - for _t in exec_list: + async with self.async_session() as session: + for _t in exec_list: + try: await session.execute(text(_t)) - await session.commit() + await session.commit() + except: # noqa: E722 + pass ##################### # GsBind 部分 # @@ -207,6 +213,22 @@ class SQLA: result = await session.execute(sql) return data[0] if (data := result.scalars().all()) else None + async def select_user_all_data_by_user_id( + self, user_id: str + ) -> Optional[List[GsUser]]: + async with self.async_session() as session: + async with session.begin(): + sql = select(GsUser).where(GsUser.user_id == user_id) + result = await session.execute(sql) + data = result.scalars().all() + return data if data else None + + async def select_user_data_by_user_id( + self, user_id: str + ) -> Optional[GsUser]: + data = await self.select_user_all_data_by_user_id(user_id) + return data[0] if data else None + async def select_cache_cookie(self, uid: str) -> Optional[str]: async with self.async_session() as session: async with session.begin(): @@ -228,6 +250,14 @@ class SQLA: await session.execute(sql) return True + async def get_user_fp(self, uid: str) -> Optional[str]: + data = await self.select_user_data(uid) + return data.fp if data else None + + async def get_user_device_id(self, uid: str) -> Optional[str]: + data = await self.select_user_data(uid) + return data.device_id if data else None + async def insert_cache_data( self, cookie: str, @@ -251,6 +281,8 @@ class SQLA: sr_uid: Optional[str] = None, cookie: Optional[str] = None, stoken: Optional[str] = None, + fp: Optional[str] = None, + device_id: Optional[str] = None, ) -> bool: async with self.async_session() as session: async with session.begin(): @@ -265,6 +297,7 @@ class SQLA: bot_id=self.bot_id, user_id=user_id, sr_uid=sr_uid, + fp=fp, ) ) await session.execute(sql) @@ -279,6 +312,7 @@ class SQLA: bot_id=self.bot_id, user_id=user_id, uid=uid, + fp=fp, ) ) await session.execute(sql) @@ -301,10 +335,15 @@ class SQLA: sign_switch='off', push_switch='off', bbs_switch='off', + draw_switch='off', region=SERVER.get(uid[0], 'cn_gf01') if uid else None, sr_region=SR_SERVER.get(sr_uid[0], None) if sr_uid else None, + fp=fp, + device_id=device_id, + sr_push_switch='off', + sr_sign_switch='off', ) session.add(user_data) await session.commit() @@ -314,13 +353,9 @@ class SQLA: async with self.async_session() as session: async with session.begin(): sql = ( - update(GsUser).where( - GsUser.sr_uid == uid, GsUser.bot_id == self.bot_id - ) + update(GsUser).where(GsUser.sr_uid == uid) if self.is_sr - else update(GsUser).where( - GsUser.uid == uid, GsUser.bot_id == self.bot_id - ) + else update(GsUser).where(GsUser.uid == uid) ) if data is not None: query = sql.values(**data) @@ -456,10 +491,18 @@ class SQLA: data = await self.select_user_data(uid) return data.cookie if data else None + async def get_user_cookie_by_user_id(self, user_id: str) -> Optional[str]: + data = await self.select_user_data_by_user_id(user_id) + return data.cookie if data else None + async def cookie_validate(self, uid: str) -> bool: data = await self.select_user_data(uid) return True if data and data.status is None else False + async def get_user_stoken_by_user_id(self, user_id: str) -> Optional[str]: + data = await self.select_user_data_by_user_id(user_id) + return data.stoken if data and data.stoken else None + async def get_user_stoken(self, uid: str) -> Optional[str]: data = await self.select_user_data(uid) return data.stoken if data and data.stoken else None @@ -528,7 +571,7 @@ class SQLA: return None async def get_switch_status_list( - self, switch: Literal['push', 'sign', 'bbs'] + self, switch: Literal['push', 'sign', 'bbs', 'sr_push', 'sr_sign'] ) -> List[GsUser]: async with self.async_session() as session: async with session.begin(): @@ -567,11 +610,7 @@ class SQLA: async with self.async_session() as session: async with session.begin(): await self.push_exists(uid) - sql = ( - update(GsPush) - .where(GsPush.uid == uid, GsPush.bot_id == self.bot_id) - .values(**data) - ) + sql = update(GsPush).where(GsPush.uid == uid).values(**data) await session.execute(sql) await session.commit() return True @@ -588,9 +627,7 @@ class SQLA: async with self.async_session() as session: async with session.begin(): await self.push_exists(uid) - sql = select(GsPush).where( - GsPush.uid == uid, GsPush.bot_id == self.bot_id - ) + sql = select(GsPush).where(GsPush.uid == uid) result = await session.execute(sql) data = result.scalars().all() return data[0] if len(data) >= 1 else None @@ -598,9 +635,7 @@ class SQLA: async def push_exists(self, uid: str) -> bool: async with self.async_session() as session: async with session.begin(): - sql = select(GsPush).where( - GsPush.uid == uid, GsPush.bot_id == self.bot_id - ) + sql = select(GsPush).where(GsPush.uid == uid) result = await session.execute(sql) data = result.scalars().all() if not data: diff --git a/gsuid_core/utils/database/models.py b/gsuid_core/utils/database/models.py index eddff00..fe97724 100644 --- a/gsuid_core/utils/database/models.py +++ b/gsuid_core/utils/database/models.py @@ -28,7 +28,12 @@ class GsUser(SQLModel, table=True): push_switch: str = Field(title='全局推送开关') sign_switch: str = Field(title='自动签到') bbs_switch: str = Field(title='自动米游币') + draw_switch: str = Field(title='自动留影叙佳期') + sr_push_switch: str = Field(title='星铁全局推送开关') + sr_sign_switch: str = Field(title='星铁自动签到') status: Optional[str] = Field(default=None, title='状态') + fp: Optional[str] = Field(default=None, title='Fingerprint') + device_id: Optional[str] = Field(default=None, title='设备ID') class GsCache(SQLModel, table=True): @@ -45,15 +50,15 @@ class GsPush(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True, title='序号') bot_id: str = Field(title='平台') uid: str = Field(default=None, title='原神UID') - coin_push: Optional[str] = Field(title='洞天宝钱推送') - coin_value: Optional[int] = Field(title='洞天宝钱阈值') - coin_is_push: Optional[str] = Field(title='洞天宝钱是否已推送') - resin_push: Optional[str] = Field(title='体力推送') - resin_value: Optional[int] = Field(title='体力阈值') - resin_is_push: Optional[str] = Field(title='体力是否已推送') - go_push: Optional[str] = Field(title='派遣推送') - go_value: Optional[int] = Field(title='派遣阈值') - go_is_push: Optional[str] = Field(title='派遣是否已推送') - transform_push: Optional[str] = Field(title='质变仪推送') - transform_value: Optional[int] = Field(title='质变仪阈值') - transform_is_push: Optional[str] = Field(title='质变仪是否已推送') + coin_push: Optional[str] = Field(title='洞天宝钱推送', default='off') + coin_value: Optional[int] = Field(title='洞天宝钱阈值', default=2100) + coin_is_push: Optional[str] = Field(title='洞天宝钱是否已推送', default='off') + resin_push: Optional[str] = Field(title='体力推送', default='off') + resin_value: Optional[int] = Field(title='体力阈值', default=140) + resin_is_push: Optional[str] = Field(title='体力是否已推送', default='off') + go_push: Optional[str] = Field(title='派遣推送', default='off') + go_value: Optional[int] = Field(title='派遣阈值', default=300) + go_is_push: Optional[str] = Field(title='派遣是否已推送', default='off') + transform_push: Optional[str] = Field(title='质变仪推送', default='off') + transform_value: Optional[int] = Field(title='质变仪阈值', default=1000) + transform_is_push: Optional[str] = Field(title='质变仪是否已推送', default='off') diff --git a/gsuid_core/utils/default_bg/bg.jpg b/gsuid_core/utils/default_bg/bg.jpg new file mode 100644 index 0000000..76042e0 Binary files /dev/null and b/gsuid_core/utils/default_bg/bg.jpg differ diff --git a/gsuid_core/utils/error_reply.py b/gsuid_core/utils/error_reply.py index 8a89398..7f3f0ec 100644 --- a/gsuid_core/utils/error_reply.py +++ b/gsuid_core/utils/error_reply.py @@ -1,4 +1,15 @@ -from typing import Union +from pathlib import Path +from typing import Union, Optional + +from PIL import Image, ImageDraw + +from gsuid_core.utils.fonts.fonts import core_font +from gsuid_core.utils.image.convert import convert_img +from gsuid_core.utils.plugins_config.gs_config import core_plugins_config +from gsuid_core.utils.image.image_tools import ( + get_color_bg, + draw_center_text_by_line, +) UID_HINT = '你还没有绑定过uid哦!\n请使用[绑定uid123456]命令绑定!' MYS_HINT = '你还没有绑定过mysid哦!\n请使用[绑定mys1234]命令绑定!' @@ -16,6 +27,9 @@ UPDATE_HINT = '''更新失败!更多错误信息请查看控制台... >> [gs强制更新](危险) >> [gs强行强制更新](超级危险)!''' +TEXT_PATH = Path(__file__).parent / 'image' / 'texture2d' +is_pic_error = core_plugins_config.get_config('ChangeErrorToPic').data + def get_error(retcode: Union[int, str]) -> str: if retcode == -51: @@ -48,5 +62,54 @@ def get_error(retcode: Union[int, str]) -> str: return '该API需要CK, 查询的用户/UID未绑定CK...' elif retcode == 10104: return 'CK与用户信息不符, 请检查代码实现...' + elif retcode == -999: + return VERIFY_HINT + elif retcode == 125: + return '该充值方式暂时不可用!' + elif retcode == 126: + return '该充值方式不正确!' else: - return f'API报错, 错误码为{retcode}!' + return f'未知错误, 错误码为{retcode}!' + + +def get_error_type(retcode: Union[int, str]) -> str: + retcode = int(retcode) + if retcode in [-51, 10104]: + return '绑定信息错误' + elif retcode in [-400, 400]: + return 'MGGApi错误' + else: + return 'Api错误' + + +async def get_error_img(retcode: Union[int, str]) -> Union[bytes, str]: + error_message = get_error(retcode) + if is_pic_error: + error_type = get_error_type(retcode) + return await draw_error_img(retcode, error_message, error_type) + else: + return error_message + + +async def draw_error_img( + retcode: Union[int, str] = 51233, + error_message: Optional[str] = None, + error_type: Optional[str] = None, +) -> bytes: + if error_type is None: + error_type = 'API报错' + if error_message is None: + error_message = '未知错误, 请检查控制台输出...' + + error_img = Image.open(TEXT_PATH / 'error_img.png') + img = await get_color_bg( + *error_img.size, is_full=True, color=(228, 222, 210) + ) + img.paste(error_img, (0, 0), error_img) + img_draw = ImageDraw.Draw(img) + img_draw.text((350, 646), error_type, 'white', core_font(26), 'mm') + img_draw.text((350, 695), f'错误码 {retcode}', 'white', core_font(36), 'mm') + draw_center_text_by_line( + img_draw, (350, 750), error_message, core_font(30), 'black', 440 + ) + return await convert_img(img) diff --git a/gsuid_core/utils/fonts/fonts.py b/gsuid_core/utils/fonts/fonts.py new file mode 100644 index 0000000..298c49b --- /dev/null +++ b/gsuid_core/utils/fonts/fonts.py @@ -0,0 +1,9 @@ +from pathlib import Path + +from PIL import ImageFont + +FONT_ORIGIN_PATH = Path(__file__).parent / 'yuanshen_origin.ttf' + + +def core_font(size: int) -> ImageFont.FreeTypeFont: + return ImageFont.truetype(str(FONT_ORIGIN_PATH), size=size) diff --git a/gsuid_core/utils/fonts/yuanshen_origin.ttf b/gsuid_core/utils/fonts/yuanshen_origin.ttf new file mode 100644 index 0000000..cd3ecd4 Binary files /dev/null and b/gsuid_core/utils/fonts/yuanshen_origin.ttf differ diff --git a/gsuid_core/utils/image/convert.py b/gsuid_core/utils/image/convert.py index c3e3c31..a075433 100644 --- a/gsuid_core/utils/image/convert.py +++ b/gsuid_core/utils/image/convert.py @@ -4,7 +4,10 @@ from base64 import b64encode from typing import Union, overload import aiofiles -from PIL import Image, ImageFont +from PIL import Image, ImageDraw, ImageFont + +from gsuid_core.utils.fonts.fonts import core_font +from gsuid_core.utils.image.image_tools import draw_center_text_by_line @overload @@ -107,3 +110,18 @@ def get_str_size( def get_height(content: str, size: int) -> int: line_count = content.count('\n') return (line_count + 1) * size + + +async def text2pic(text: str, max_size: int = 600, font_size: int = 24): + if text.endswith('\n'): + text = text[:-1] + + img = Image.new( + 'RGB', (max_size, len(text) * font_size // 5), (228, 222, 210) + ) + img_draw = ImageDraw.ImageDraw(img) + y = draw_center_text_by_line( + img_draw, (50, 0), text, core_font(font_size), 'black', 500, True + ) + img = img.crop((0, 0, 600, int(y + 30))) + return await convert_img(img) diff --git a/gsuid_core/utils/image/image_tools.py b/gsuid_core/utils/image/image_tools.py index b6c469e..1ab2387 100644 --- a/gsuid_core/utils/image/image_tools.py +++ b/gsuid_core/utils/image/image_tools.py @@ -8,7 +8,10 @@ import httpx from httpx import get from PIL import Image, ImageDraw, ImageFont +from gsuid_core.data_store import get_res_path + TEXT_PATH = Path(__file__).parent / 'texture2d' +BG_PATH = Path(__file__).parents[1] / 'default_bg' async def get_pic(url, size: Optional[Tuple[int, int]] = None) -> Image.Image: @@ -28,6 +31,36 @@ async def get_pic(url, size: Optional[Tuple[int, int]] = None) -> Image.Image: return pic +def draw_center_text_by_line( + img: ImageDraw.ImageDraw, + pos: Tuple[int, int], + text: str, + font: ImageFont.FreeTypeFont, + fill: Union[Tuple[int, int, int, int], str], + max_length: float, + not_center: bool = False, +) -> float: + pun = "。!?;!?" + x, y = pos + _, h = font.getsize('X') + line = '' + lenth = 0 + anchor = 'la' if not_center else 'mm' + for char in text: + size, _ = font.getsize(char) # 获取当前字符的宽度 + lenth += size + line += char + if lenth < max_length and char not in pun and char != '\n': + pass + else: + img.text((x, y), line, fill, font, anchor) + line, lenth = '', 0 + y += h * 1.55 + else: + img.text((x, y), line, fill, font, anchor) + return y + + def draw_text_by_line( img: Image.Image, pos: Tuple[int, int], @@ -37,7 +70,7 @@ def draw_text_by_line( max_length: float, center=False, line_space: Optional[float] = None, -): +) -> float: """ 在图片上写长段文字, 自动换行 max_length单行最大长度, 单位像素 @@ -63,7 +96,7 @@ def draw_text_by_line( font_size = font.getsize(row) x = math.ceil((img.size[0] - font_size[0]) / 2) draw.text((x, y), row, font=font, fill=fill) - row = "" + row = '' length = 0 y += y_add if row != "": @@ -71,6 +104,7 @@ def draw_text_by_line( font_size = font.getsize(row) x = math.ceil((img.size[0] - font_size[0]) / 2) draw.text((x, y), row, font=font, fill=fill) + return y def easy_paste( @@ -174,12 +208,42 @@ def crop_center_img( return crop_img +async def get_color_bg( + based_w: int, + based_h: int, + bg_path: Optional[Path] = None, + without_mask: bool = False, + is_full: bool = False, + color: Optional[Tuple[int, int, int]] = None, + full_opacity: int = 200, +) -> Image.Image: + if bg_path is None: + bg_path = get_res_path(['GsCore', 'bg']) + CI_img = CustomizeImage(bg_path) + img = CI_img.get_image(None, based_w, based_h) + if color is None: + color = CI_img.get_bg_color(img) + if is_full: + color_img = Image.new('RGBA', (based_w, based_h), color) + mask = Image.new( + 'RGBA', (based_w, based_h), (255, 255, 255, full_opacity) + ) + img.paste(color_img, (0, 0), mask) + elif not without_mask: + color_mask = Image.new('RGBA', (based_w, based_h), color) + enka_mask = Image.open(TEXT_PATH / 'bg_mask.png').resize( + (based_w, based_h) + ) + img.paste(color_mask, (0, 0), enka_mask) + return img + + class CustomizeImage: def __init__(self, bg_path: Path) -> None: self.bg_path = bg_path def get_image( - self, image: Union[str, Image.Image], based_w: int, based_h: int + self, image: Union[str, Image.Image, None], based_w: int, based_h: int ) -> Image.Image: # 获取背景图片 if isinstance(image, Image.Image): @@ -187,7 +251,11 @@ class CustomizeImage: elif image: edit_bg = Image.open(BytesIO(get(image).content)).convert('RGBA') else: - path = random.choice(list(self.bg_path.iterdir())) + _lst = list(self.bg_path.iterdir()) + if _lst: + path = random.choice(list(self.bg_path.iterdir())) + else: + path = random.choice(list(BG_PATH.iterdir())) edit_bg = Image.open(path).convert('RGBA') # 确定图片的长宽 diff --git a/gsuid_core/utils/image/texture2d/bg_mask.png b/gsuid_core/utils/image/texture2d/bg_mask.png new file mode 100644 index 0000000..d425828 Binary files /dev/null and b/gsuid_core/utils/image/texture2d/bg_mask.png differ diff --git a/gsuid_core/utils/image/texture2d/error_img.png b/gsuid_core/utils/image/texture2d/error_img.png new file mode 100644 index 0000000..48899d6 Binary files /dev/null and b/gsuid_core/utils/image/texture2d/error_img.png differ diff --git a/gsuid_core/utils/plugins_config/config_default.py b/gsuid_core/utils/plugins_config/config_default.py index 9f03ba2..f19d9cf 100644 --- a/gsuid_core/utils/plugins_config/config_default.py +++ b/gsuid_core/utils/plugins_config/config_default.py @@ -49,4 +49,11 @@ CONIFG_DEFAULT: Dict[str, GSC] = { 'AutoRestartCoreTime': GsListStrConfig( '自动重启Core时间设置', '每晚自动重启Core时间设置(时, 分)', ['4', '40'] ), + 'AutoAddRandomText': GsBoolConfig('自动加入随机字符串', '自动加入随机字符串', False), + 'RandomText': GsStrConfig( + '随机字符串列表', '随机字符串列表', 'abcdefghijklmnopqrstuvwxyz' + ), + 'ChangeErrorToPic': GsBoolConfig('错误提示转换为图片', '将一部分报错提示转换为图片', True), + 'AutoTextToPic': GsBoolConfig('自动文字转图', '将所有发送的文字转图', True), + 'TextToPicThreshold': GsStrConfig('文转图阈值', '开启自动转图后超过该阈值的文字会转成图片', '20'), } diff --git a/gsuid_core/utils/plugins_update/_plugins.py b/gsuid_core/utils/plugins_update/_plugins.py index 4272712..e721ad6 100644 --- a/gsuid_core/utils/plugins_update/_plugins.py +++ b/gsuid_core/utils/plugins_update/_plugins.py @@ -13,6 +13,14 @@ from .api import CORE_PATH, PLUGINS_PATH, proxy_url, plugins_lib plugins_list: Dict[str, Dict[str, str]] = {} +async def update_all_plugins() -> List[str]: + log_list = [] + for plugin in PLUGINS_PATH.iterdir(): + if plugin.is_dir(): + log_list.extend(update_from_git(0, plugin)) + return log_list + + async def refresh_list() -> List[str]: refresh_list = [] async with aiohttp.ClientSession() as session: @@ -57,7 +65,12 @@ def install_plugins(plugins: Dict[str, str]) -> str: path = PLUGINS_PATH / plugin_name if path.exists(): return '该插件已经安装过了!' - Repo.clone_from(git_path, path, single_branch=True, depth=1) + config = {'single_branch': True, 'depth': 1} + + if plugins['branch'] != 'main': + config['branch'] = plugins['branch'] + + Repo.clone_from(git_path, path, **config) logger.info(f'插件{plugin_name}安装成功!') return f'插件{plugin_name}安装成功!发送[gs重启]以应用!' diff --git a/gsuid_core/webconsole/mount_app.py b/gsuid_core/webconsole/mount_app.py index e9ffef2..de65650 100644 --- a/gsuid_core/webconsole/mount_app.py +++ b/gsuid_core/webconsole/mount_app.py @@ -11,11 +11,11 @@ from fastapi_amis_admin.crud import BaseApiOut from sqlalchemy.ext.asyncio import AsyncEngine from fastapi_user_auth.site import AuthAdminSite from fastapi_amis_admin.models.fields import Field -from fastapi_amis_admin.admin.site import APIDocsApp from fastapi_amis_admin.admin.settings import Settings from fastapi_user_auth.auth.models import UserRoleLink from fastapi_amis_admin.utils.translation import i18n as _ from fastapi import Depends, FastAPI, Request, HTTPException +from fastapi_amis_admin.admin.site import FileAdmin, APIDocsApp from fastapi_amis_admin.amis.constants import LevelEnum, DisplayModeEnum from fastapi_user_auth.admin import ( FormAdmin, @@ -417,4 +417,4 @@ class PluginsManagePage(GsAdminPage): # 取消注册默认管理类 -site.unregister_admin(admin.HomeAdmin, APIDocsApp) +site.unregister_admin(admin.HomeAdmin, APIDocsApp, FileAdmin) diff --git a/poetry.lock b/poetry.lock index d7c9831..5f78021 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "aiofiles" version = "23.1.0" description = "File support for asyncio." -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -21,7 +20,6 @@ reference = "mirrors" name = "aiohttp" version = "3.8.4" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -135,7 +133,6 @@ reference = "mirrors" name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -155,7 +152,6 @@ reference = "mirrors" name = "aiosqlite" version = "0.19.0" description = "asyncio bridge to the standard sqlite3 module" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -174,24 +170,24 @@ reference = "mirrors" [[package]] name = "anyio" -version = "3.6.2" +version = "3.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" files = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, + {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"}, + {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"}, ] [package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] +doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] [package.source] type = "legacy" @@ -202,7 +198,6 @@ reference = "mirrors" name = "apscheduler" version = "3.10.1" description = "In-process task scheduler with Cron-like capabilities" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -214,7 +209,7 @@ files = [ pytz = "*" setuptools = ">=0.7" six = ">=1.4.0" -tzlocal = ">=2.0,<3.0.0 || >=4.0.0" +tzlocal = ">=2.0,<3.dev0 || >=4.dev0" [package.extras] doc = ["sphinx", "sphinx-rtd-theme"] @@ -237,7 +232,6 @@ reference = "mirrors" name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -254,7 +248,6 @@ reference = "mirrors" name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -278,7 +271,6 @@ reference = "mirrors" name = "backports-zoneinfo" version = "0.2.1" description = "Backport of the standard library zoneinfo module" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -312,7 +304,6 @@ reference = "mirrors" name = "bcrypt" version = "4.0.1" description = "Modern password hashing for your software and your servers" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -352,7 +343,6 @@ reference = "mirrors" name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" -category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -376,7 +366,6 @@ reference = "mirrors" name = "black" version = "22.12.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -417,7 +406,6 @@ reference = "mirrors" name = "certifi" version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -434,7 +422,6 @@ reference = "mirrors" name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -451,7 +438,6 @@ reference = "mirrors" name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -541,7 +527,6 @@ reference = "mirrors" name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -561,7 +546,6 @@ reference = "mirrors" name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -578,7 +562,6 @@ reference = "mirrors" name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -595,7 +578,6 @@ reference = "mirrors" name = "dnspython" version = "2.3.0" description = "DNS toolkit" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -621,7 +603,6 @@ reference = "mirrors" name = "email-validator" version = "2.0.0.post2" description = "A robust email address syntax and deliverability validation library." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -639,26 +620,41 @@ url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" reference = "mirrors" [[package]] -name = "fastapi" -version = "0.95.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi-0.95.1-py3-none-any.whl", hash = "sha256:a870d443e5405982e1667dfe372663abf10754f246866056336d7f01c21dab07"}, - {file = "fastapi-0.95.1.tar.gz", hash = "sha256:9569f0a381f8a457ec479d90fa01005cfddaae07546eb1f3fa035bc4797ae7d5"}, + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[package.source] +type = "legacy" +url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" +reference = "mirrors" + +[[package]] +name = "fastapi" +version = "0.97.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.97.0-py3-none-any.whl", hash = "sha256:95d757511c596409930bd20673358d4a4d709004edb85c5d24d6ffc48fabcbf2"}, + {file = "fastapi-0.97.0.tar.gz", hash = "sha256:b53248ee45f64f19bb7600953696e3edf94b0f7de94df1e5433fc5c6136fa986"}, ] [package.dependencies] -pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" -starlette = ">=0.26.1,<0.27.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = ">=0.27.0,<0.28.0" [package.extras] all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] -test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] [package.source] type = "legacy" @@ -667,14 +663,13 @@ reference = "mirrors" [[package]] name = "fastapi-amis-admin" -version = "0.5.6" +version = "0.5.7" description = "FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by Django-admin, and has as many powerful functions as Django-admin." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi_amis_admin-0.5.6-py3-none-any.whl", hash = "sha256:df9160d4b28f2a2165c17ec678eddc2bfe7323c77c9dfdd8217a7e8b6895e99d"}, - {file = "fastapi_amis_admin-0.5.6.tar.gz", hash = "sha256:8bcb74d11d4e1b605b2d3eccecf308a6c5ffce65d4d8bef300de6764f0b64107"}, + {file = "fastapi_amis_admin-0.5.7-py3-none-any.whl", hash = "sha256:b4d2e859b40b69bb7bb9a6c1957af77a347b1c3ccca4b4a1c3313a706df1d5ef"}, + {file = "fastapi_amis_admin-0.5.7.tar.gz", hash = "sha256:c99d9e5c1f4d9b913b7a7bfdccb9fc6d5edfc5bff709388161cc7bdcbca64339"}, ] [package.dependencies] @@ -686,9 +681,9 @@ sqlmodel = ">=0.0.7" [package.extras] all = ["fastapi-amis-admin[dev]"] -cli = ["fastapi-amis-admin-cli (>=0.1.0,<0.2.0)"] +cli = ["fastapi-amis-admin-cli (>=0.1.1,<0.2.0)"] dev = ["fastapi-amis-admin[test]", "pre-commit (>=2.20.0)", "ruff (>=0.0.261)"] -standard = ["fastapi-amis-admin-cli (>=0.1.0,<0.2.0)", "uvicorn[standard] (>=0.19.0,<1.0)"] +standard = ["fastapi-amis-admin-cli (>=0.1.1,<0.2.0)", "uvicorn[standard] (>=0.19.0,<1.0)"] test = ["aiosqlite (>=0.15.0)", "fastapi-amis-admin[standard]", "httpx (>=0.23.0,<1.0)", "jinja2 (>=2.11.2,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-asyncio (>=0.17)", "requests (>=2.28.1)", "ujson (>=4.0.1)"] [package.source] @@ -700,7 +695,6 @@ reference = "mirrors" name = "fastapi-user-auth" version = "0.5.0" description = "FastAPI-User-Auth is a simple and powerful FastAPI user RBAC authentication and authorization library. Based on FastAPI-Amis-Admin and provides a freely extensible visual management interface." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -732,7 +726,6 @@ reference = "mirrors" name = "filelock" version = "3.12.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -753,7 +746,6 @@ reference = "mirrors" name = "flake8" version = "6.0.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -775,7 +767,6 @@ reference = "mirrors" name = "frozenlist" version = "1.3.3" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -864,7 +855,6 @@ reference = "mirrors" name = "gitdb" version = "4.0.10" description = "Git Object Database" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -884,7 +874,6 @@ reference = "mirrors" name = "gitpython" version = "3.1.31" description = "GitPython is a Python library used to interact with Git repositories" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -904,7 +893,6 @@ reference = "mirrors" name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -983,7 +971,6 @@ reference = "mirrors" name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -998,25 +985,24 @@ reference = "mirrors" [[package]] name = "httpcore" -version = "0.17.0" +version = "0.17.2" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "httpcore-0.17.0-py3-none-any.whl", hash = "sha256:0fdfea45e94f0c9fd96eab9286077f9ff788dd186635ae61b312693e4d943599"}, - {file = "httpcore-0.17.0.tar.gz", hash = "sha256:cc045a3241afbf60ce056202301b4d8b6af08845e3294055eb26b09913ef903c"}, + {file = "httpcore-0.17.2-py3-none-any.whl", hash = "sha256:5581b9c12379c4288fe70f43c710d16060c10080617001e6b22a3b6dbcbefd36"}, + {file = "httpcore-0.17.2.tar.gz", hash = "sha256:125f8375ab60036db632f34f4b627a9ad085048eef7cb7d2616fea0f739f98af"}, ] [package.dependencies] anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [package.source] type = "legacy" @@ -1025,14 +1011,13 @@ reference = "mirrors" [[package]] name = "httpx" -version = "0.24.0" +version = "0.24.1" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "httpx-0.24.0-py3-none-any.whl", hash = "sha256:447556b50c1921c351ea54b4fe79d91b724ed2b027462ab9a329465d147d5a4e"}, - {file = "httpx-0.24.0.tar.gz", hash = "sha256:507d676fc3e26110d41df7d35ebd8b3b8585052450f4097401c9be59d928c63e"}, + {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, + {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, ] [package.dependencies] @@ -1043,9 +1028,9 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [package.source] type = "legacy" @@ -1056,7 +1041,6 @@ reference = "mirrors" name = "identify" version = "2.5.24" description = "File identification library for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1076,7 +1060,6 @@ reference = "mirrors" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1093,7 +1076,6 @@ reference = "mirrors" name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1114,42 +1096,41 @@ reference = "mirrors" [[package]] name = "libcst" -version = "0.4.9" +version = "0.4.10" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7, 3.8, 3.9, and 3.10 programs." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "libcst-0.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f9e42085c403e22201e5c41e707ef73e4ea910ad9fc67983ceee2368097f54e"}, - {file = "libcst-0.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1266530bf840cc40633a04feb578bb4cac1aa3aea058cc3729e24eab09a8e996"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9679177391ccb9b0cdde3185c22bf366cb672457c4b7f4031fcb3b5e739fbd6"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d67bc87e0d8db9434f2ea063734938a320f541f4c6da1074001e372f840f385d"}, - {file = "libcst-0.4.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e316da5a126f2a9e1d7680f95f907b575f082a35e2f8bd5620c59b2aaaebfe0a"}, - {file = "libcst-0.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:7415569ab998a85b0fc9af3a204611ea7fadb2d719a12532c448f8fc98f5aca4"}, - {file = "libcst-0.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:15ded11ff7f4572f91635e02b519ae959f782689fdb4445bbebb7a3cc5c71d75"}, - {file = "libcst-0.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b266867b712a120fad93983de432ddb2ccb062eb5fd2bea748c9a94cb200c36"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045b3b0b06413cdae6e9751b5f417f789ffa410f2cb2815e3e0e0ea6bef10ec0"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e799add8fba4976628b9c1a6768d73178bf898f0ed1bd1322930c2d3db9063ba"}, - {file = "libcst-0.4.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10479371d04ee8dc978c889c1774bbf6a83df88fa055fcb0159a606f6679c565"}, - {file = "libcst-0.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:7a98286cbbfa90a42d376900c875161ad02a5a2a6b7c94c0f7afd9075e329ce4"}, - {file = "libcst-0.4.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:400166fc4efb9aa06ce44498d443aa78519082695b1894202dd73cd507d2d712"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46123863fba35cc84f7b54dd68826419cabfd9504d8a101c7fe3313ea03776f9"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27be8db54c0e5fe440021a771a38b81a7dbc23cd630eb8b0e9828b7717f9b702"}, - {file = "libcst-0.4.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:132bec627b064bd567e7e4cd6c89524d02842151eb0d8f5f3f7ffd2579ec1b09"}, - {file = "libcst-0.4.9-cp37-cp37m-win_amd64.whl", hash = "sha256:596860090aeed3ee6ad1e59c35c6c4110a57e4e896abf51b91cae003ec720a11"}, - {file = "libcst-0.4.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4487608258109f774300466d4ca97353df29ae6ac23d1502e13e5509423c9d5"}, - {file = "libcst-0.4.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa53993e9a2853efb3ed3605da39f2e7125df6430f613eb67ef886c1ce4f94b5"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6ce794483d4c605ef0f5b199a49fb6996f9586ca938b7bfef213bd13858d7ab"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786e562b54bbcd17a060d1244deeef466b7ee07fe544074c252c4a169e38f1ee"}, - {file = "libcst-0.4.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794250d2359edd518fb698e5d21c38a5bdfc5e4a75d0407b4c19818271ce6742"}, - {file = "libcst-0.4.9-cp38-cp38-win_amd64.whl", hash = "sha256:76491f67431318c3145442e97dddcead7075b074c59eac51be7cc9e3fffec6ee"}, - {file = "libcst-0.4.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3cf48d7aec6dc54b02aec0b1bb413c5bb3b02d852fd6facf1f05c7213e61a176"}, - {file = "libcst-0.4.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b3348c6b7711a5235b133bd8e11d22e903c388db42485b8ceb5f2aa0fae9b9f"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e33b66762efaa014c38819efae5d8f726dd823e32d5d691035484411d2a2a69"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1350d375d3fb9b20a6cf10c09b2964baca9be753a033dde7c1aced49d8e58387"}, - {file = "libcst-0.4.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3822056dc13326082362db35b3f649e0f4a97e36ddb4e487441da8e0fb9db7b3"}, - {file = "libcst-0.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:183636141b839aa35b639e100883813744523bc7c12528906621121731b28443"}, - {file = "libcst-0.4.9.tar.gz", hash = "sha256:01786c403348f76f274dbaf3888ae237ffb73e6ed6973e65eba5c1fc389861dd"}, + {file = "libcst-0.4.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8fa0ec646ed7bce984d0ee9dbf514af278050bdb16a4fb986e916ace534eebc6"}, + {file = "libcst-0.4.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3cb3b7821eac00713844cda079583230c546a589b22ed5f03f2ddc4f985c384b"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7acfa747112ae40b032739661abd7c81aff37191294f7c2dab8bbd72372e78f"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1312e293b864ef3cb4b09534ed5f104c2dc45b680233c68bf76237295041c781"}, + {file = "libcst-0.4.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76884b1afe475e8e68e704bf26eb9f9a2867029643e58f2f26a0286e3b6e998e"}, + {file = "libcst-0.4.10-cp310-cp310-win_amd64.whl", hash = "sha256:1069b808a711db5cd47538f27eb2c73206317aa0d8b5a3500b23aab24f86eb2e"}, + {file = "libcst-0.4.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:50be085346a35812535c7f876319689e15a7bfd1bd8efae8fd70589281d944b6"}, + {file = "libcst-0.4.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb9f10e5763e361e8bd8ff765fc0f1bcf744f242ff8b6d3e50ffec4dda3972ac"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfeeabb528b5df7b4be1817b584ce79e9a1a66687bd72f6de9c22272462812f1"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5648aeae8c90a2abab1f7b1bf205769a0179ed2cfe1ea7f681f6885e87b8b193"}, + {file = "libcst-0.4.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a144f20aff4643b00374facf8409d30c7935db8176e5b2a07e1fd44004db2c1f"}, + {file = "libcst-0.4.10-cp311-cp311-win_amd64.whl", hash = "sha256:a10adc2e8ea2dda2b70eabec631ead2fc4a7a7ab633d6c2b690823c698b8431a"}, + {file = "libcst-0.4.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58fe90458a26a55358207f74abf8a05dff51d662069f070b4bd308a000a80c09"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:999fbbe467f61cbce9e6e054f86cd1c5ffa3740fd3dc8ebdd600db379f699256"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ee7e7be4efac4c140a97d772e1f6b3553f98fa5f46ad78df5dfe51e5a4aa4d"}, + {file = "libcst-0.4.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:158478e8f45578fb26621b3dc0fe275f9e004297e9afdcf08936ecda05681174"}, + {file = "libcst-0.4.10-cp37-cp37m-win_amd64.whl", hash = "sha256:5ed101fee1af7abea3684fcff7fab5b170ceea4040756f54c15c870539daec66"}, + {file = "libcst-0.4.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:349f2b4ee4b982fe254c65c78d941fc96299f3c422b79f95ef8c7bba2b7f0f90"}, + {file = "libcst-0.4.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7cfa4d4beb84d0d63247aca27f1a15c63984512274c5b23040f8b4ba511036d7"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24582506da24e31f2644f862f11413a6b80fbad68d15194bfcc3f7dfebf2ec5e"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cdf2d0157438d3d52d310b0b6be31ff99bed19de489b2ebd3e2a4cd9946da45"}, + {file = "libcst-0.4.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a677103d2f1ab0e50bc3a7cc6c96c7d64bcbac826d785e4cbf5ee9aaa9fcfa25"}, + {file = "libcst-0.4.10-cp38-cp38-win_amd64.whl", hash = "sha256:a8fdfd4a7d301adb785aa4b98e4a7cca45c5ff8cfb460b485d081efcfaaeeab7"}, + {file = "libcst-0.4.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b1569d87536bed4e9c11dd5c94a137dc0bce2a2b05961489c6016bf4521bb7cf"}, + {file = "libcst-0.4.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:72dff8783ac79cd10f2bd2fde0b28f262e9a22718ae26990948ba6131b85ca8b"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76adc53660ef094ff83f77a2550a7e00d1cab8e5e63336e071c17c09b5a89fe2"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3e9d9fdd9a9b9b8991936ff1c07527ce7ef396c8233280ba9a7137e72c2e48e"}, + {file = "libcst-0.4.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e1b4cbaf7b1cdad5fa3eababe42d5b46c0d52afe13c5ba4eac2495fc57630ea"}, + {file = "libcst-0.4.10-cp39-cp39-win_amd64.whl", hash = "sha256:bcbd07cec3d7a7be6f0299b0c246e085e3d6cc8af367e2c96059183b97c2e2fe"}, + {file = "libcst-0.4.10.tar.gz", hash = "sha256:b98a829d96e8b209fb761b00cd1bacc27c70eae77d00e57976e5ae2c718c3f81"}, ] [package.dependencies] @@ -1158,7 +1139,7 @@ typing-extensions = ">=3.7.4.2" typing-inspect = ">=0.4.0" [package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==22.10.0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8,<5)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.2)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<0.14)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.9)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.0.1)", "usort (==1.0.5)"] +dev = ["Sphinx (>=5.1.1)", "black (==23.1.0)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8,<5)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.2)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<0.14)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.10)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.1.0)", "usort (==1.0.6)"] [package.source] type = "legacy" @@ -1169,7 +1150,6 @@ reference = "mirrors" name = "loguru" version = "0.6.0" description = "Python logging made (stupidly) simple" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1193,7 +1173,6 @@ reference = "mirrors" name = "lxml" version = "4.9.2" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -1291,7 +1270,6 @@ reference = "mirrors" name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1304,43 +1282,64 @@ type = "legacy" url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" reference = "mirrors" +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + +[package.source] +type = "legacy" +url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" +reference = "mirrors" + [[package]] name = "msgspec" -version = "0.15.0" +version = "0.16.0" description = "A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML." -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "msgspec-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:415519f68cd3f1a224f87ed415459ac3b86e4f6e82815a036e4238c62006f696"}, - {file = "msgspec-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b57b6869ef1717c0343465198e19284d1e6aa5f292af2726284e4dfedfedeef"}, - {file = "msgspec-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff80bd40469915cc61686086a2503b901e17040f8a191099d8ccaa45dc72df8c"}, - {file = "msgspec-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beb3789519253b22338cca48053ba5ac8b442633e3af8f58e264d776a98ff6d0"}, - {file = "msgspec-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf3ab3f8d5752dbe68babc77d21b42575b916793515442e3890aef680e212154"}, - {file = "msgspec-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:05fc603508e0c8021249d3e531fa4bb72d167bdfa76d869d48f96a5b8f9b50bf"}, - {file = "msgspec-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:42c2f9fe0b58dc6f2b15720490c67554b5ba0007d3ee94340ca4448bda917287"}, - {file = "msgspec-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0c75fd847709e30265f050375c408fec1c07797694162834aa86ab3b3cf055da"}, - {file = "msgspec-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:381c7a891adcc741e617956ba987912bc21864f9dd27b8cfb03bfb0aded5e1fd"}, - {file = "msgspec-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a46a9818570362d4022161684cdb97ecd102953043059ee4902862940f48f8d"}, - {file = "msgspec-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20bf8018bff6bb85f5315ba6fd47b2f9373ab67e8bb59b0d7a7def22bbbf9f70"}, - {file = "msgspec-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8cee590163788fce21c5998d09198ef08ec06c1ca68ef50f2d5ed9e54d308538"}, - {file = "msgspec-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9d61de44b248feef82c8979a1e9912c923527cfb1d01c93b7bb5d6ca93ed09d6"}, - {file = "msgspec-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b7e0354c37b742e1c02fe0cd3ced97db516c8da62ac5a408609e9f5858aaf24"}, - {file = "msgspec-0.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b5b7c1b69416eab3ab2ad1c9593b749226b80555532292fae5fe9d154794089"}, - {file = "msgspec-0.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc253e4ad51d360590358ab2cee5a6139f04ad994e0fcbff52e7f61fca475c3e"}, - {file = "msgspec-0.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:490c88d76d573cce16653434ace3d9a8a8675ef6e350f114752fe60e69b6a232"}, - {file = "msgspec-0.15.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffa7ec85f27577f7f5471b390bf902d58ccd89b3612cf40bfb92f4ba75e6c95"}, - {file = "msgspec-0.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:22713a1f618b4094c0268c6fbeef530397e5f3fa5292e4afd51caddad645843f"}, - {file = "msgspec-0.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c4c7e0abfac3a67f3e1d51e1d1313fd4205528e17663ff264b1945c3370b18bd"}, - {file = "msgspec-0.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd198ed4445914ebc25a24b6cc6020902bb6b888fc9b39500ef4500841b1b437"}, - {file = "msgspec-0.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddef8fdc06676dd1bec9fe67b3128f84079469ee6424384cbb29a90c9033b559"}, - {file = "msgspec-0.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346960762d648a6512b51f30be7c1267630e0bbc6fd65e8b23a3f54e5f562656"}, - {file = "msgspec-0.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07ee1d1a15e3b319dcd7326470216928a7b58d47460b253577ccd0ab5dcf5c3c"}, - {file = "msgspec-0.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550b359c49562d52849103b87b4f7381fbc0adf958afa316599befb9a3e3379e"}, - {file = "msgspec-0.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9178a7550a5516295c682e6e5c143782503719b4c816496349a4a0f1b62397ef"}, - {file = "msgspec-0.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d3cde786110a92f764666b9f963b4389d5d1798bf1aca2422a59931d8d1f694"}, - {file = "msgspec-0.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:f77cc91d3cb8a7bccfba28fe4aec537178384509bfce222f8eca287b7e5d0214"}, - {file = "msgspec-0.15.0.tar.gz", hash = "sha256:d760ff747165d84965791bfcd14588f61f111708036d80f1980387e3760035e7"}, + {file = "msgspec-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f06692e976bbb89d1c1eb95109679195a4ec172fbec73dee5027af1450f46b59"}, + {file = "msgspec-0.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:77b19814d7206542927c46e0c7807955739c181fef71f973e96c4e47a14c5893"}, + {file = "msgspec-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0cda74ffda2b2757eadf2259f8a68a5321f4fb8423bff26fa9e28eaaf8720d6"}, + {file = "msgspec-0.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddebe801459cd6f67e4279b3c679dc731729fabf64f42d7a4bd567ca3eb56377"}, + {file = "msgspec-0.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c0458cf8a44f630d372348d95b3b536a52412d4e61a53a3f3f31f070c95eb461"}, + {file = "msgspec-0.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ce21b56ecb462abb5291863c2e29dc58177da3c8f43f3d0edf69009daca05b66"}, + {file = "msgspec-0.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:cbd657cc2e2840f86a75c1fee265854835e2196d12502a64ce1390239cca58a9"}, + {file = "msgspec-0.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7cdfad50f388d1c1a933d9239913cb3bd993d4b631011df34d893fb3011971e0"}, + {file = "msgspec-0.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a4f641e10d4ef70a77184c002ec1512c0b83ddbb6c21314c85f9507c029b997"}, + {file = "msgspec-0.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9075d40d7739228b6158969239ad7708f483bbd4e8eb09c92c95c6062b470617"}, + {file = "msgspec-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f710d8b1992cf0690c9feeebd741d69c3627bace3f16e09e8556d65eb012fe"}, + {file = "msgspec-0.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1b6412c20bd687df6fb7c72a8a1bbc1a5da1be948bc01ce3d21e645263cddb6c"}, + {file = "msgspec-0.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e1a6708408cd5a44e39aa268086fe0992001e5881282c178a158af86727ddfa3"}, + {file = "msgspec-0.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:ccd842d25593fbff6505e77e0a3701c89bf3a1c260247e4e541e4e58dc81a6cc"}, + {file = "msgspec-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbc137f037c2cb4ee731ef5066d3cb85a639b5d805df7f4c96aaefd914c7c5af"}, + {file = "msgspec-0.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:611c90eff0e2dd19b53e93bf8040450404f262aa05eee27089c8d29e92031db6"}, + {file = "msgspec-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36e177b0f05f05321e415d42a30f854df47452c973e18957899410163da5c88c"}, + {file = "msgspec-0.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86db3b2e32be73d155525e0886764963379eef8d6f7a16da6cd023516aed01ee"}, + {file = "msgspec-0.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa4bb83597ad8fce23b53ff16acd7931a55bf4ee2197c0282f077e5caacd5ee2"}, + {file = "msgspec-0.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:15c64bbefd34a5beb0da9eb22bff3ba0aab296f9828084998cd7716b5c1e2964"}, + {file = "msgspec-0.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:4111ab4373c185df543248d86eeb885c623319f82f4256164617beb8fbfa5071"}, + {file = "msgspec-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:24809b66ef632f1ae91af7d281dd78eec2f516ad9963b3e9e61cb7b34495875d"}, + {file = "msgspec-0.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f14e3b1df80967aef772c9ac083df56ecf067f7cad7d291180f2733449e83a5"}, + {file = "msgspec-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78f3a914a356daf334f9dc7e72fb55025b39c65b6fcec507b18cdca7e65b97f6"}, + {file = "msgspec-0.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4830b073860e05d2cf1ef56d610035402f83b129a2742032ef2492d093385ef"}, + {file = "msgspec-0.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fea1bc172bd07a427ee538169b6447433dee018624f1e43ab7d046ccfbffb66f"}, + {file = "msgspec-0.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d80d2c1a8e3ee2b991d7fcf8d8b0904cb4fa68fe4d5abf8739453cffdde418c4"}, + {file = "msgspec-0.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:21095c687ae624a812a13a7e5d4ea6f3039c8768052ac0fb2818b8744779872a"}, + {file = "msgspec-0.16.0.tar.gz", hash = "sha256:0a3d5441cc8bda37957a1edb52c6f6ff4fcfebcaf20c771ad4cd4eade75c0f1a"}, ] [package.extras] @@ -1359,7 +1358,6 @@ reference = "mirrors" name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1448,7 +1446,6 @@ reference = "mirrors" name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1463,14 +1460,13 @@ reference = "mirrors" [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] @@ -1485,7 +1481,6 @@ reference = "mirrors" name = "passlib" version = "1.7.4" description = "comprehensive password hashing framework supporting over 30 schemes" -category = "main" optional = false python-versions = "*" files = [ @@ -1506,14 +1501,13 @@ reference = "mirrors" [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, - {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] [package.source] @@ -1525,7 +1519,6 @@ reference = "mirrors" name = "pillow" version = "9.5.0" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1608,18 +1601,17 @@ reference = "mirrors" [[package]] name = "platformdirs" -version = "3.5.0" +version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.0-py3-none-any.whl", hash = "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4"}, - {file = "platformdirs-3.5.0.tar.gz", hash = "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"}, + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [package.source] @@ -1631,7 +1623,6 @@ reference = "mirrors" name = "pre-commit" version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1653,22 +1644,21 @@ reference = "mirrors" [[package]] name = "pycln" -version = "2.1.3" +version = "2.1.5" description = "A formatter for finding and removing unused import statements." -category = "dev" optional = false python-versions = ">=3.6.2,<4" files = [ - {file = "pycln-2.1.3-py3-none-any.whl", hash = "sha256:161142502e4ff9853cd462a38401e29eb56235919856df2cb7fa4c84e463717f"}, - {file = "pycln-2.1.3.tar.gz", hash = "sha256:a33bfc64ded74a623b7cf49eca38b58db4348facc60c35af26d45de149b256f5"}, + {file = "pycln-2.1.5-py3-none-any.whl", hash = "sha256:1e1f2542aabc8942fd945bbecd39b55ed5f25cd9a70fa116a554cceaab4fdc3b"}, + {file = "pycln-2.1.5.tar.gz", hash = "sha256:5029007881d00b87bfc8831ef7cf59c90cc214fbbcc8773f0a9560ddef8d150a"}, ] [package.dependencies] libcst = {version = ">=0.3.10,<0.5.0", markers = "python_version >= \"3.7\""} -pathspec = ">=0.9.0,<0.11.0" +pathspec = ">=0.9.0,<0.12.0" pyyaml = ">=5.3.1,<7.0.0" tomlkit = ">=0.11.1,<0.12.0" -typer = ">=0.4.1,<0.8.0" +typer = ">=0.4.1,<0.10.0" [package.source] type = "legacy" @@ -1679,7 +1669,6 @@ reference = "mirrors" name = "pycodestyle" version = "2.10.0" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1694,48 +1683,47 @@ reference = "mirrors" [[package]] name = "pydantic" -version = "1.10.7" +version = "1.10.9" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, - {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, - {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, - {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, - {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, - {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, - {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, - {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, - {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, - {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, - {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, - {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, - {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, - {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, - {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, - {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, - {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, - {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, - {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, - {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, - {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, - {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, - {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, + {file = "pydantic-1.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e692dec4a40bfb40ca530e07805b1208c1de071a18d26af4a2a0d79015b352ca"}, + {file = "pydantic-1.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c52eb595db83e189419bf337b59154bdcca642ee4b2a09e5d7797e41ace783f"}, + {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939328fd539b8d0edf244327398a667b6b140afd3bf7e347cf9813c736211896"}, + {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b48d3d634bca23b172f47f2335c617d3fcb4b3ba18481c96b7943a4c634f5c8d"}, + {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f0b7628fb8efe60fe66fd4adadd7ad2304014770cdc1f4934db41fe46cc8825f"}, + {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1aa5c2410769ca28aa9a7841b80d9d9a1c5f223928ca8bec7e7c9a34d26b1d4"}, + {file = "pydantic-1.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:eec39224b2b2e861259d6f3c8b6290d4e0fbdce147adb797484a42278a1a486f"}, + {file = "pydantic-1.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d111a21bbbfd85c17248130deac02bbd9b5e20b303338e0dbe0faa78330e37e0"}, + {file = "pydantic-1.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e9aec8627a1a6823fc62fb96480abe3eb10168fd0d859ee3d3b395105ae19a7"}, + {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07293ab08e7b4d3c9d7de4949a0ea571f11e4557d19ea24dd3ae0c524c0c334d"}, + {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee829b86ce984261d99ff2fd6e88f2230068d96c2a582f29583ed602ef3fc2c"}, + {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b466a23009ff5cdd7076eb56aca537c745ca491293cc38e72bf1e0e00de5b91"}, + {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7847ca62e581e6088d9000f3c497267868ca2fa89432714e21a4fb33a04d52e8"}, + {file = "pydantic-1.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:7845b31959468bc5b78d7b95ec52fe5be32b55d0d09983a877cca6aedc51068f"}, + {file = "pydantic-1.10.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:517a681919bf880ce1dac7e5bc0c3af1e58ba118fd774da2ffcd93c5f96eaece"}, + {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67195274fd27780f15c4c372f4ba9a5c02dad6d50647b917b6a92bf00b3d301a"}, + {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2196c06484da2b3fded1ab6dbe182bdabeb09f6318b7fdc412609ee2b564c49a"}, + {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6257bb45ad78abacda13f15bde5886efd6bf549dd71085e64b8dcf9919c38b60"}, + {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3283b574b01e8dbc982080d8287c968489d25329a463b29a90d4157de4f2baaf"}, + {file = "pydantic-1.10.9-cp37-cp37m-win_amd64.whl", hash = "sha256:5f8bbaf4013b9a50e8100333cc4e3fa2f81214033e05ac5aa44fa24a98670a29"}, + {file = "pydantic-1.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9cd67fb763248cbe38f0593cd8611bfe4b8ad82acb3bdf2b0898c23415a1f82"}, + {file = "pydantic-1.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f50e1764ce9353be67267e7fd0da08349397c7db17a562ad036aa7c8f4adfdb6"}, + {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73ef93e5e1d3c8e83f1ff2e7fdd026d9e063c7e089394869a6e2985696693766"}, + {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128d9453d92e6e81e881dd7e2484e08d8b164da5507f62d06ceecf84bf2e21d3"}, + {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad428e92ab68798d9326bb3e5515bc927444a3d71a93b4a2ca02a8a5d795c572"}, + {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fab81a92f42d6d525dd47ced310b0c3e10c416bbfae5d59523e63ea22f82b31e"}, + {file = "pydantic-1.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:963671eda0b6ba6926d8fc759e3e10335e1dc1b71ff2a43ed2efd6996634dafb"}, + {file = "pydantic-1.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:970b1bdc6243ef663ba5c7e36ac9ab1f2bfecb8ad297c9824b542d41a750b298"}, + {file = "pydantic-1.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7e1d5290044f620f80cf1c969c542a5468f3656de47b41aa78100c5baa2b8276"}, + {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83fcff3c7df7adff880622a98022626f4f6dbce6639a88a15a3ce0f96466cb60"}, + {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0da48717dc9495d3a8f215e0d012599db6b8092db02acac5e0d58a65248ec5bc"}, + {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0a2aabdc73c2a5960e87c3ffebca6ccde88665616d1fd6d3db3178ef427b267a"}, + {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9863b9420d99dfa9c064042304868e8ba08e89081428a1c471858aa2af6f57c4"}, + {file = "pydantic-1.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:e7c9900b43ac14110efa977be3da28931ffc74c27e96ee89fbcaaf0b0fe338e1"}, + {file = "pydantic-1.10.9-py3-none-any.whl", hash = "sha256:6cafde02f6699ce4ff643417d1a9223716ec25e228ddc3b436fe7e2d25a1f305"}, + {file = "pydantic-1.10.9.tar.gz", hash = "sha256:95c70da2cd3b6ddf3b9645ecaa8d98f3d80c606624b6d245558d202cd23ea3be"}, ] [package.dependencies] @@ -1754,7 +1742,6 @@ reference = "mirrors" name = "pyflakes" version = "3.0.1" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1771,7 +1758,6 @@ reference = "mirrors" name = "pypng" version = "0.20220715.0" description = "Pure Python library for saving and loading PNG images" -category = "main" optional = false python-versions = "*" files = [ @@ -1788,7 +1774,6 @@ reference = "mirrors" name = "python-multipart" version = "0.0.6" description = "A streaming multipart parser for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1808,7 +1793,6 @@ reference = "mirrors" name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -1821,32 +1805,10 @@ type = "legacy" url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" reference = "mirrors" -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version >= \"3.6\" and python_version < \"3.9\""} -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - -[package.source] -type = "legacy" -url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" -reference = "mirrors" - [[package]] name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1901,7 +1863,6 @@ reference = "mirrors" name = "qrcode" version = "7.4.2" description = "QR Code image generator" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1929,19 +1890,18 @@ reference = "mirrors" [[package]] name = "setuptools" -version = "67.7.2" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, - {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [package.source] @@ -1953,7 +1913,6 @@ reference = "mirrors" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1970,7 +1929,6 @@ reference = "mirrors" name = "smmap" version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1987,7 +1945,6 @@ reference = "mirrors" name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2004,7 +1961,6 @@ reference = "mirrors" name = "soupsieve" version = "2.4.1" description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2021,7 +1977,6 @@ reference = "mirrors" name = "sqlalchemy" version = "1.4.41" description = "Database Abstraction Library" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -2069,7 +2024,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and platform_machine == \"aarch64\" or python_version >= \"3\" and platform_machine == \"ppc64le\" or python_version >= \"3\" and platform_machine == \"x86_64\" or python_version >= \"3\" and platform_machine == \"amd64\" or python_version >= \"3\" and platform_machine == \"AMD64\" or python_version >= \"3\" and platform_machine == \"win32\" or python_version >= \"3\" and platform_machine == \"WIN32\""} +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"} [package.extras] aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] @@ -2101,7 +2056,6 @@ reference = "mirrors" name = "sqlalchemy-database" version = "0.1.0" description = "SQLAlchemy-Database provides shortcut functions to common database operations for SQLAlchemy ORM." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2126,7 +2080,6 @@ reference = "mirrors" name = "sqlalchemy2-stubs" version = "0.0.2a34" description = "Typing Stubs for SQLAlchemy 1.4" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2146,7 +2099,6 @@ reference = "mirrors" name = "sqlmodel" version = "0.0.8" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." -category = "main" optional = false python-versions = ">=3.6.1,<4.0.0" files = [ @@ -2168,7 +2120,6 @@ reference = "mirrors" name = "sqlmodelx" version = "0.0.5" description = "SQLModelX is an extension of the SQLModel library." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2189,14 +2140,13 @@ reference = "mirrors" [[package]] name = "starlette" -version = "0.26.1" +version = "0.27.0" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "starlette-0.26.1-py3-none-any.whl", hash = "sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e"}, - {file = "starlette-0.26.1.tar.gz", hash = "sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd"}, + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, ] [package.dependencies] @@ -2215,7 +2165,6 @@ reference = "mirrors" name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2232,7 +2181,6 @@ reference = "mirrors" name = "tomlkit" version = "0.11.8" description = "Style preserving TOML library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2247,24 +2195,24 @@ reference = "mirrors" [[package]] name = "typer" -version = "0.7.0" +version = "0.9.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "typer-0.7.0-py3-none-any.whl", hash = "sha256:b5e704f4e48ec263de1c0b3a2387cd405a13767d2f907f44c1a08cbad96f606d"}, - {file = "typer-0.7.0.tar.gz", hash = "sha256:ff797846578a9f2a201b53442aedeb543319466870fbe1c701eab66dd7681165"}, + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, ] [package.dependencies] click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" [package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] [package.source] type = "legacy" @@ -2273,14 +2221,13 @@ reference = "mirrors" [[package]] name = "typing-extensions" -version = "4.5.0" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] [package.source] @@ -2290,14 +2237,13 @@ reference = "mirrors" [[package]] name = "typing-inspect" -version = "0.8.0" +version = "0.9.0" description = "Runtime inspection utilities for typing module." -category = "dev" optional = false python-versions = "*" files = [ - {file = "typing_inspect-0.8.0-py3-none-any.whl", hash = "sha256:5fbf9c1e65d4fa01e701fe12a5bca6c6e08a4ffd5bc60bfac028253a447c5188"}, - {file = "typing_inspect-0.8.0.tar.gz", hash = "sha256:8b1ff0c400943b6145df8119c41c244ca8207f1f10c9c057aeed1560e4806e3d"}, + {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, + {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, ] [package.dependencies] @@ -2313,7 +2259,6 @@ reference = "mirrors" name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -2328,19 +2273,17 @@ reference = "mirrors" [[package]] name = "tzlocal" -version = "4.3" +version = "5.0.1" description = "tzinfo object for the local timezone" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "tzlocal-4.3-py3-none-any.whl", hash = "sha256:b44c4388f3d34f25862cfbb387578a4d70fec417649da694a132f628a23367e2"}, - {file = "tzlocal-4.3.tar.gz", hash = "sha256:3f21d09e1b2aa9f2dacca12da240ca37de3ba5237a93addfd6d593afe9073355"}, + {file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"}, + {file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"}, ] [package.dependencies] "backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} -pytz-deprecation-shim = "*" tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] @@ -2353,14 +2296,13 @@ reference = "mirrors" [[package]] name = "urllib3" -version = "1.26.15" +version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, ] [package.extras] @@ -2377,7 +2319,6 @@ reference = "mirrors" name = "uvicorn" version = "0.22.0" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2401,7 +2342,6 @@ reference = "mirrors" name = "virtualenv" version = "20.23.0" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2427,7 +2367,6 @@ reference = "mirrors" name = "websockets" version = "10.4" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2511,7 +2450,6 @@ reference = "mirrors" name = "win32-setctime" version = "1.1.0" description = "A small Python utility to set file creation time on Windows" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2531,7 +2469,6 @@ reference = "mirrors" name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2623,4 +2560,4 @@ reference = "mirrors" [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "8540b0f00e1f77e7adb32353cb16fa617b144380b307ffdcc36a3aebd402f3b6" +content-hash = "5e8223cc0dba8826053cb728d301392626a09d9d06ee0801e23751b184f60563" diff --git a/pyproject.toml b/pyproject.toml index 3beb61b..8344953 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ uvicorn = ">=0.20.0" websockets = "^10.4" loguru = "^0.6.0" urllib3 = "^1.26.15" +mpmath = "^1.3.0" [tool.poetry.group.dev.dependencies] flake8 = "^6.0.0" @@ -42,11 +43,14 @@ pre-commit = "^2.21.0" pycln = "^2.1.2" +[tool.poetry.scripts] +core = 'gsuid_core.core:main' + + [[tool.poetry.source]] name = "mirrors" url = "https://mirrors.bfsu.edu.cn/pypi/web/simple/" -default = true -secondary = false +priority = "default" [build-system] requires = ["poetry-core"] diff --git a/requirements.txt b/requirements.txt index a0015ff..29d8b3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ aiofiles==23.1.0 ; python_full_version >= "3.8.1" and python_version < "4.0" aiohttp==3.8.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" aiosqlite==0.19.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -anyio==3.6.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +anyio==3.7.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" apscheduler==3.10.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" attrs==23.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" @@ -14,33 +14,34 @@ beautifulsoup4==4.12.2 ; python_full_version >= "3.8.1" and python_full_version certifi==2023.5.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" charset-normalizer==3.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" click==8.1.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and platform_system == "Windows" or python_full_version >= "3.8.1" and python_full_version < "4.0.0" and sys_platform == "win32" +colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and (platform_system == "Windows" or sys_platform == "win32") dnspython==2.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" email-validator==2.0.0.post2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -fastapi-amis-admin==0.5.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +exceptiongroup==1.1.1 ; python_full_version >= "3.8.1" and python_version < "3.11" +fastapi-amis-admin==0.5.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" fastapi-user-auth==0.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -fastapi==0.95.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +fastapi==0.97.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" frozenlist==1.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" gitpython==3.1.31 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -greenlet==2.0.2 ; python_full_version >= "3.8.1" and platform_machine == "aarch64" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "ppc64le" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "x86_64" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "amd64" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "AMD64" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "win32" and python_full_version < "4.0.0" or python_full_version >= "3.8.1" and platform_machine == "WIN32" and python_full_version < "4.0.0" +greenlet==2.0.2 ; python_full_version >= "3.8.1" and (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64") and python_full_version < "4.0.0" h11==0.14.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -httpcore==0.17.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -httpx==0.24.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +httpcore==0.17.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +httpx==0.24.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" idna==3.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" loguru==0.6.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" lxml==4.9.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -msgspec==0.15.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +mpmath==1.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +msgspec==0.16.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" multidict==6.0.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" passlib==1.7.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" pillow==9.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -pydantic==1.10.7 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +pydantic==1.10.9 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" pypng==0.20220715.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" python-multipart==0.0.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" qrcode[pil]==7.4.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -setuptools==67.7.2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +setuptools==67.8.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" six==1.16.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" @@ -50,11 +51,11 @@ sqlalchemy2-stubs==0.0.2a34 ; python_full_version >= "3.8.1" and python_full_ver sqlalchemy==1.4.41 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" sqlmodel==0.0.8 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" sqlmodelx==0.0.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -starlette==0.26.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -typing-extensions==4.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -tzlocal==4.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -urllib3==1.26.15 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +starlette==0.27.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +typing-extensions==4.6.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and platform_system == "Windows" +tzlocal==5.0.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +urllib3==1.26.16 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" uvicorn==0.22.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" websockets==10.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and sys_platform == "win32"