From 345edb9fe55fb91f9f2b2dbd93c9fedbb9f371c2 Mon Sep 17 00:00:00 2001 From: Chuangbo Li Date: Mon, 10 Oct 2022 19:07:28 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BD=BF=E7=94=A8=20black=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E6=89=80=E6=9C=89=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic/env.py | 4 +- core/admin/models.py | 3 +- core/base/aiobrowser.py | 11 +- core/base/mtproto.py | 14 +- core/base/mysql.py | 3 +- core/base/redisdb.py | 8 +- core/baseplugin.py | 18 +- core/bot.py | 82 ++-- core/cookies/models.py | 6 +- core/error.py | 1 - core/game/services.py | 2 +- core/plugin.py | 205 ++++---- core/quiz/cache.py | 4 +- core/quiz/models.py | 13 +- core/service.py | 5 +- core/sign/models.py | 2 +- core/template/services.py | 13 +- core/user/models.py | 2 +- core/user/repositories.py | 2 +- core/user/services.py | 1 - core/wiki/services.py | 1 - metadata/pool/pool_200.py | 12 +- metadata/pool/pool_301.py | 446 +++++------------ metadata/pool/pool_302.py | 529 +++++---------------- metadata/scripts/honey.py | 73 +-- metadata/scripts/metadatas.py | 50 +- metadata/shortname.py | 372 ++++++++++----- modules/apihelper/artifact.py | 94 ++-- modules/apihelper/base.py | 10 +- modules/apihelper/helpers.py | 6 +- modules/apihelper/hyperion.py | 85 ++-- modules/apihelper/request/hoyorequest.py | 6 +- modules/apihelper/request/httpxrequest.py | 6 +- modules/playercards/helpers.py | 22 +- modules/wiki/base.py | 26 +- modules/wiki/character.py | 73 ++- modules/wiki/material.py | 50 +- modules/wiki/other.py | 108 +++-- modules/wiki/weapon.py | 65 +-- plugins/genshin/abyss.py | 32 +- plugins/genshin/artifact_rate.py | 57 ++- plugins/genshin/cookies.py | 94 ++-- plugins/genshin/daily/material.py | 196 ++++---- plugins/genshin/daily_note.py | 35 +- plugins/genshin/gacha/gacha.py | 5 +- plugins/genshin/ledger.py | 44 +- plugins/genshin/map/map.py | 4 +- plugins/genshin/map/model.py | 23 +- plugins/genshin/material.py | 26 +- plugins/genshin/quiz.py | 11 +- plugins/genshin/strategy.py | 20 +- plugins/genshin/uid.py | 57 ++- plugins/genshin/userstats.py | 14 +- plugins/genshin/weapon.py | 48 +- plugins/jobs/public_cookies.py | 1 - plugins/other/post.py | 33 +- plugins/system/auth.py | 136 +++--- plugins/system/errorhandler.py | 27 +- plugins/system/get_chat.py | 16 +- plugins/system/inline.py | 27 +- plugins/system/log.py | 5 +- plugins/system/new_member.py | 4 +- plugins/system/refresh_metadata.py | 7 +- plugins/system/set_quiz.py | 69 ++- plugins/system/start.py | 7 +- plugins/system/update.py | 3 +- resources/genshin/abyss_team/example2.html | 477 +++++++++++++++++++ run.py | 2 +- tests/test_artifact.py | 19 +- tests/test_hyperion_bbs.py | 4 +- tests/test_wiki.py | 94 ++-- utils/baseobject.py | 9 +- utils/const.py | 19 +- utils/decorators/error.py | 9 +- utils/decorators/restricts.py | 8 +- utils/helpers.py | 41 +- utils/log/_file.py | 21 +- utils/log/_logger.py | 149 +++--- utils/log/_style.py | 61 +-- utils/models/base.py | 23 +- utils/patch/methods.py | 4 +- utils/typedefs.py | 11 +- 82 files changed, 2313 insertions(+), 2072 deletions(-) create mode 100644 resources/genshin/abyss_team/example2.html diff --git a/alembic/env.py b/alembic/env.py index dc1a58e4..37692427 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -42,9 +42,7 @@ def import_models(): try: import_module(pkg) # 导入 models except Exception as e: # pylint: disable=W0703 - logger.error( - f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]' - ) + logger.error(f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]') # register our models for alembic to auto-generate migrations diff --git a/core/admin/models.py b/core/admin/models.py index c9a3fdd5..e4bf7c16 100644 --- a/core/admin/models.py +++ b/core/admin/models.py @@ -1,9 +1,8 @@ - from sqlmodel import SQLModel, Field class Admin(SQLModel, table=True): - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) user_id: int = Field(foreign_key="user.user_id") diff --git a/core/base/aiobrowser.py b/core/base/aiobrowser.py index 39210c83..ff7b5f5a 100644 --- a/core/base/aiobrowser.py +++ b/core/base/aiobrowser.py @@ -7,7 +7,6 @@ from utils.log import logger class AioBrowser(Service): - def __init__(self, loop=None): self.browser: Optional[Browser] = None self._playwright: Optional[Playwright] = None @@ -15,16 +14,16 @@ class AioBrowser(Service): async def start(self): if self._playwright is None: - logger.info("正在尝试启动 [blue]Playwright[/]", extra={'markup': True}) + logger.info("正在尝试启动 [blue]Playwright[/]", extra={"markup": True}) self._playwright = await async_playwright().start() - logger.success("[blue]Playwright[/] 启动成功", extra={'markup': True}) + logger.success("[blue]Playwright[/] 启动成功", extra={"markup": True}) if self.browser is None: - logger.info("正在尝试启动 [blue]Browser[/]", extra={'markup': True}) + logger.info("正在尝试启动 [blue]Browser[/]", extra={"markup": True}) try: self.browser = await self._playwright.chromium.launch(timeout=5000) - logger.success("[blue]Browser[/] 启动成功", extra={'markup': True}) + logger.success("[blue]Browser[/] 启动成功", extra={"markup": True}) except TimeoutError as err: - logger.warning("[blue]Browser[/] 启动失败", extra={'markup': True}) + logger.warning("[blue]Browser[/] 启动失败", extra={"markup": True}) raise err return self.browser diff --git a/core/base/mtproto.py b/core/base/mtproto.py index 4f4d77f2..d491aac2 100644 --- a/core/base/mtproto.py +++ b/core/base/mtproto.py @@ -20,13 +20,12 @@ from core.service import Service class MTProto(Service): - async def get_session(self): - async with aiofiles.open(self.session_path, mode='r') as f: + async with aiofiles.open(self.session_path, mode="r") as f: return await f.read() async def set_session(self, b: str): - async with aiofiles.open(self.session_path, mode='w+') as f: + async with aiofiles.open(self.session_path, mode="w+") as f: await f.write(b) def session_exists(self): @@ -53,8 +52,13 @@ class MTProto(Service): if bot.config.mtproto.api_hash is None: logger.info("MTProto 服务需要的 api_hash 未配置 本次服务 client 为 None") return - self.client = Client(api_id=bot.config.mtproto.api_id, api_hash=bot.config.mtproto.api_hash, name=self.name, - bot_token=bot.config.bot_token, proxy=self.proxy) + self.client = Client( + api_id=bot.config.mtproto.api_id, + api_hash=bot.config.mtproto.api_hash, + name=self.name, + bot_token=bot.config.bot_token, + proxy=self.proxy, + ) await self.client.start() async def stop(self): # pylint: disable=W0221 diff --git a/core/base/mysql.py b/core/base/mysql.py index 1b898130..64952e4b 100644 --- a/core/base/mysql.py +++ b/core/base/mysql.py @@ -12,8 +12,7 @@ class MySQL(Service): def from_config(cls, config: BotConfig) -> Self: return cls(**config.mysql.dict()) - def __init__(self, host: str = "127.0.0.1", port: int = 3306, username: str = "root", # nosec B107 - password: str = "", database: str = ""): # nosec B107 + def __init__(self, host: str, port: int, username: str, password: str, database: str): self.database = database self.password = password self.user = username diff --git a/core/base/redisdb.py b/core/base/redisdb.py index aa9f8669..ddd4303a 100644 --- a/core/base/redisdb.py +++ b/core/base/redisdb.py @@ -22,21 +22,21 @@ class RedisDB(Service): async def ping(self): if await self.client.ping(): - logger.info("连接 [red]Redis[/] 成功", extra={'markup': True}) + logger.info("连接 [red]Redis[/] 成功", extra={"markup": True}) else: - logger.info("连接 [red]Redis[/] 失败", extra={'markup': True}) + logger.info("连接 [red]Redis[/] 失败", extra={"markup": True}) raise RuntimeError("连接 Redis 失败") async def start(self): # pylint: disable=W0221 if self._loop is None: self._loop = asyncio.get_running_loop() - logger.info("正在尝试建立与 [red]Redis[/] 连接", extra={'markup': True}) + logger.info("正在尝试建立与 [red]Redis[/] 连接", extra={"markup": True}) try: await self.ping() except (KeyboardInterrupt, SystemExit): pass except Exception as exc: - logger.exception("尝试连接 [red]Redis[/] 失败,使用 [red]fakeredis[/] 模拟", exc_info=exc, extra={'markup': True}) + logger.exception("尝试连接 [red]Redis[/] 失败,使用 [red]fakeredis[/] 模拟", exc_info=exc, extra={"markup": True}) self.client = fakeredis.aioredis.FakeRedis() await self.ping() diff --git a/core/baseplugin.py b/core/baseplugin.py index 73b853ff..2eeb6453 100644 --- a/core/baseplugin.py +++ b/core/baseplugin.py @@ -16,8 +16,7 @@ async def clean_message(context: CallbackContext): if "not found" in str(exc): logger.warning(f"删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息不存在") elif "Message can't be deleted" in str(exc): - logger.warning( - f"删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息无法删除 可能是没有授权") + logger.warning(f"删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息无法删除 可能是没有授权") else: logger.error(f"删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败") logger.exception(exc) @@ -30,10 +29,14 @@ async def clean_message(context: CallbackContext): def add_delete_message_job(context: CallbackContext, chat_id: int, message_id: int, delete_seconds: int): - context.job_queue.run_once(callback=clean_message, when=delete_seconds, data=message_id, - name=f"{chat_id}|{message_id}|clean_message", chat_id=chat_id, - job_kwargs={"replace_existing": True, - "id": f"{chat_id}|{message_id}|clean_message"}) + context.job_queue.run_once( + callback=clean_message, + when=delete_seconds, + data=message_id, + name=f"{chat_id}|{message_id}|clean_message", + chat_id=chat_id, + job_kwargs={"replace_existing": True, "id": f"{chat_id}|{message_id}|clean_message"}, + ) class _BasePlugin: @@ -43,9 +46,8 @@ class _BasePlugin: class _Conversation(_BasePlugin): - @conversation.fallback - @handler.command(command='cancel', block=True) + @handler.command(command="cancel", block=True) async def cancel(self, update: Update, _: CallbackContext) -> int: await update.effective_message.reply_text("退出命令", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END diff --git a/core/bot.py b/core/bot.py index 02752630..3245aaee 100644 --- a/core/bot.py +++ b/core/bot.py @@ -23,6 +23,7 @@ from telegram.ext.filters import StatusUpdate from core.config import BotConfig, config # pylint: disable=W0611 from core.error import ServiceNotFoundError + # noinspection PyProtectedMember from core.plugin import Plugin, _Plugin from core.service import Service @@ -32,10 +33,10 @@ from utils.log import logger if TYPE_CHECKING: from telegram import Update -__all__ = ['bot'] +__all__ = ["bot"] -T = TypeVar('T') -PluginType = TypeVar('PluginType', bound=_Plugin) +T = TypeVar("T") +PluginType = TypeVar("PluginType", bound=_Plugin) class Bot: @@ -57,7 +58,7 @@ class Bot: def _inject(self, signature: inspect.Signature, target: Callable[..., T]) -> T: kwargs = {} for name, parameter in signature.parameters.items(): - if name != 'self' and parameter.annotation != inspect.Parameter.empty: + if name != "self" and parameter.annotation != inspect.Parameter.empty: if value := self._services.get(parameter.annotation): kwargs[name] = value return target(**kwargs) @@ -76,11 +77,11 @@ class Bot: def _gen_pkg(self, root: Path) -> Iterator[str]: """生成可以用于 import_module 导入的字符串""" for path in root.iterdir(): - if not path.name.startswith('_'): + if not path.name.startswith("_"): if path.is_dir(): yield from self._gen_pkg(path) - elif path.suffix == '.py': - yield str(path.relative_to(PROJECT_ROOT).with_suffix('')).replace(os.sep, '.') + elif path.suffix == ".py": + yield str(path.relative_to(PROJECT_ROOT).with_suffix("")).replace(os.sep, ".") async def install_plugins(self): """安装插件""" @@ -89,8 +90,7 @@ class Bot: import_module(pkg) # 导入插件 except Exception as e: # pylint: disable=W0703 logger.exception( - f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', - extra={'markup': True} + f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', extra={"markup": True} ) continue # 如有错误则继续 callback_dict: Dict[int, List[Callable]] = {} @@ -98,7 +98,7 @@ class Bot: path = f"{plugin_cls.__module__}.{plugin_cls.__name__}" try: plugin: PluginType = self.init_inject(plugin_cls) - if hasattr(plugin, '__async_init__'): + if hasattr(plugin, "__async_init__"): await self.async_inject(plugin.__async_init__) handlers = plugin.handlers self.app.add_handlers(handlers) @@ -115,46 +115,44 @@ class Bot: for callback, block in error_handlers.items(): self.app.add_error_handler(callback, block) if error_handlers: - logger.debug(f"插件 \"{path}\" 添加了 {len(error_handlers)} 个 error handler") + logger.debug(f'插件 "{path}" 添加了 {len(error_handlers)} 个 error handler') if jobs := plugin.jobs: logger.debug(f'插件 "{path}" 添加了 {len(jobs)} 个任务') logger.success(f'插件 "{path}" 载入成功') except Exception as e: # pylint: disable=W0703 logger.exception( - f'在安装插件 \"{path}\" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', - extra={'markup': True} + f'在安装插件 "{path}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', extra={"markup": True} ) if callback_dict: num = sum(len(callback_dict[i]) for i in callback_dict) - async def _new_chat_member_callback(update: 'Update', context: 'CallbackContext'): + async def _new_chat_member_callback(update: "Update", context: "CallbackContext"): nonlocal callback for _, value in callback_dict.items(): for callback in value: await callback(update, context) - self.app.add_handler(MessageHandler( - callback=_new_chat_member_callback, filters=StatusUpdate.NEW_CHAT_MEMBERS, block=False - )) + self.app.add_handler( + MessageHandler(callback=_new_chat_member_callback, filters=StatusUpdate.NEW_CHAT_MEMBERS, block=False) + ) logger.success( - f'成功添加了 {num} 个针对 [blue]{StatusUpdate.NEW_CHAT_MEMBERS}[/] 的 [blue]MessageHandler[/]', - extra={'markup': True} + f"成功添加了 {num} 个针对 [blue]{StatusUpdate.NEW_CHAT_MEMBERS}[/] 的 [blue]MessageHandler[/]", + extra={"markup": True}, ) async def _start_base_services(self): - for pkg in self._gen_pkg(PROJECT_ROOT / 'core/base'): + for pkg in self._gen_pkg(PROJECT_ROOT / "core/base"): try: import_module(pkg) except Exception as e: # pylint: disable=W0703 logger.exception( - f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', - extra={'markup': True} + f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', extra={"markup": True} ) continue for base_service_cls in Service.__subclasses__(): try: - if hasattr(base_service_cls, 'from_config'): + if hasattr(base_service_cls, "from_config"): instance = base_service_cls.from_config(self._config) else: instance = self.init_inject(base_service_cls) @@ -168,15 +166,14 @@ class Bot: async def start_services(self): """启动服务""" await self._start_base_services() - for path in (PROJECT_ROOT / 'core').iterdir(): - if not path.name.startswith('_') and path.is_dir() and path.name != 'base': - pkg = str(path.relative_to(PROJECT_ROOT).with_suffix('')).replace(os.sep, '.') + for path in (PROJECT_ROOT / "core").iterdir(): + if not path.name.startswith("_") and path.is_dir() and path.name != "base": + pkg = str(path.relative_to(PROJECT_ROOT).with_suffix("")).replace(os.sep, ".") try: import_module(pkg) except Exception as e: # pylint: disable=W0703 logger.exception( - f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', - extra={'markup': True} + f'在导入文件 "{pkg}" 的过程中遇到了错误: \n[red bold]{type(e).__name__}: {e}[/]', extra={"markup": True} ) continue @@ -184,11 +181,11 @@ class Bot: """关闭服务""" if not self._services: return - logger.info('正在关闭服务') + logger.info("正在关闭服务") for _, service in filter(lambda x: not isinstance(x[1], TgApplication), self._services.items()): async with timeout(5): try: - if hasattr(service, 'stop'): + if hasattr(service, "stop"): if inspect.iscoroutinefunction(service.stop): await service.stop() else: @@ -197,16 +194,19 @@ class Bot: except CancelledError: logger.warning(f'服务 "{service.__class__.__name__}" 关闭超时') except Exception as e: # pylint: disable=W0703 - logger.exception(f"服务 \"{service.__class__.__name__}\" 关闭失败: \n{type(e).__name__}: {e}") + logger.exception(f'服务 "{service.__class__.__name__}" 关闭失败: \n{type(e).__name__}: {e}') async def _post_init(self, context: CallbackContext) -> NoReturn: - logger.info('开始初始化 genshin.py 相关资源') + logger.info("开始初始化 genshin.py 相关资源") try: # 替换为 fastgit 镜像源 for i in dir(genshin.utility.extdb): if "_URL" in i: - setattr(genshin.utility.extdb, i, - getattr(genshin.utility.extdb, i).replace("githubusercontent.com", "fastgit.org")) + setattr( + genshin.utility.extdb, + i, + getattr(genshin.utility.extdb, i).replace("githubusercontent.com", "fastgit.org"), + ) await genshin.utility.update_characters_enka() except Exception as exc: logger.error("初始化 genshin.py 相关资源失败") @@ -214,16 +214,16 @@ class Bot: else: logger.success("初始化 genshin.py 相关资源成功") self._services.update({CallbackContext: context}) - logger.info('开始初始化服务') + logger.info("开始初始化服务") await self.start_services() - logger.info('开始安装插件') + logger.info("开始安装插件") await self.install_plugins() - logger.info('BOT 初始化成功') + logger.info("BOT 初始化成功") def launch(self) -> NoReturn: """启动机器人""" self._running = True - logger.info('正在初始化BOT') + logger.info("正在初始化BOT") self.app = ( TgApplication.builder() .rate_limiter(AIORateLimiter()) @@ -238,11 +238,11 @@ class Bot: self.app.run_polling(close_loop=False) break except TimedOut: - logger.warning("连接至 [blue]telegram[/] 服务器失败,正在重试", extra={'markup': True}) + logger.warning("连接至 [blue]telegram[/] 服务器失败,正在重试", extra={"markup": True}) continue except NetworkError as e: logger.exception() - if 'SSLZeroReturnError' in str(e): + if "SSLZeroReturnError" in str(e): logger.error("代理服务出现异常, 请检查您的代理服务是否配置成功.") else: logger.error("网络连接出现问题, 请检查您的网络状况.") @@ -267,7 +267,7 @@ class Bot: def add_service(self, service: T) -> NoReturn: """添加服务。若已经有同类型的服务,则会抛出异常""" if type(service) in self._services: - raise ValueError(f"Service \"{type(service)}\" is already existed.") + raise ValueError(f'Service "{type(service)}" is already existed.') self.update_service(service) def update_service(self, service: T): diff --git a/core/cookies/models.py b/core/cookies/models.py index 0a55b785..93010da5 100644 --- a/core/cookies/models.py +++ b/core/cookies/models.py @@ -11,7 +11,7 @@ class CookiesStatusEnum(int, enum.Enum): class Cookies(SQLModel): - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) user_id: Optional[int] = Field(foreign_key="user.user_id") @@ -20,8 +20,8 @@ class Cookies(SQLModel): class HyperionCookie(Cookies, table=True): - __tablename__ = 'mihoyo_cookies' + __tablename__ = "mihoyo_cookies" class HoyolabCookie(Cookies, table=True): - __tablename__ = 'hoyoverse_cookies' + __tablename__ = "hoyoverse_cookies" diff --git a/core/error.py b/core/error.py index 8b2f25d0..344b6842 100644 --- a/core/error.py +++ b/core/error.py @@ -3,6 +3,5 @@ from typing import Union class ServiceNotFoundError(Exception): - def __init__(self, name: Union[str, type]): super().__init__(f"No service named '{name if isinstance(name, str) else name.__name__}'") diff --git a/core/game/services.py b/core/game/services.py index 535ac053..b17b9c15 100644 --- a/core/game/services.py +++ b/core/game/services.py @@ -48,7 +48,7 @@ class GameMaterialService: self._cache = cache self._hyperion = Hyperion() self._collections = [428421, 1164644] if collections is None else collections - self._special = ['雷电将军', '珊瑚宫心海', '菲谢尔', '托马', '八重神子', '九条裟罗', '辛焱', '神里绫华'] + self._special = ["雷电将军", "珊瑚宫心海", "菲谢尔", "托马", "八重神子", "九条裟罗", "辛焱", "神里绫华"] async def _get_material_from_hyperion(self, collection_id: int, character_name: str) -> int: post_id: int = -1 diff --git a/core/plugin.py b/core/plugin.py index 31e2d650..599cb71c 100644 --- a/core/plugin.py +++ b/core/plugin.py @@ -7,43 +7,42 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Un # noinspection PyProtectedMember from telegram._utils.defaultvalue import DEFAULT_TRUE + # noinspection PyProtectedMember from telegram._utils.types import DVInput, JSONDict from telegram.ext import BaseHandler, ConversationHandler, Job + # noinspection PyProtectedMember from telegram.ext._utils.types import JobCallback from telegram.ext.filters import BaseFilter from typing_extensions import ParamSpec -__all__ = [ - 'Plugin', 'handler', 'conversation', 'job', 'error_handler' -] +__all__ = ["Plugin", "handler", "conversation", "job", "error_handler"] -P = ParamSpec('P') -T = TypeVar('T') -HandlerType = TypeVar('HandlerType', bound=BaseHandler) +P = ParamSpec("P") +T = TypeVar("T") +HandlerType = TypeVar("HandlerType", bound=BaseHandler) TimeType = Union[float, datetime.timedelta, datetime.datetime, datetime.time] -_Module = import_module('telegram.ext') +_Module = import_module("telegram.ext") _NORMAL_HANDLER_ATTR_NAME = "_handler_data" _CONVERSATION_HANDLER_ATTR_NAME = "_conversation_data" _JOB_ATTR_NAME = "_job_data" -_EXCLUDE_ATTRS = ['handlers', 'jobs', 'error_handlers'] +_EXCLUDE_ATTRS = ["handlers", "jobs", "error_handlers"] class _Plugin: - def _make_handler(self, datas: Union[List[Dict], Dict]) -> List[HandlerType]: result = [] if isinstance(datas, list): for data in filter(lambda x: x, datas): - func = getattr(self, data.pop('func')) - result.append(data.pop('type')(callback=func, **data.pop('kwargs'))) + func = getattr(self, data.pop("func")) + result.append(data.pop("type")(callback=func, **data.pop("kwargs"))) else: - func = getattr(self, datas.pop('func')) - result.append(datas.pop('type')(callback=func, **datas.pop('kwargs'))) + func = getattr(self, datas.pop("func")) + result.append(datas.pop("type")(callback=func, **datas.pop("kwargs"))) return result @property @@ -52,14 +51,12 @@ class _Plugin: for attr in dir(self): # noinspection PyUnboundLocalVariable if ( - not (attr.startswith('_') or attr in _EXCLUDE_ATTRS) - and - isinstance(func := getattr(self, attr), MethodType) - and - (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) + not (attr.startswith("_") or attr in _EXCLUDE_ATTRS) + and isinstance(func := getattr(self, attr), MethodType) + and (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) ): for data in datas: - if data['type'] not in ['error', 'new_chat_member']: + if data["type"] not in ["error", "new_chat_member"]: result.extend(self._make_handler(data)) return result @@ -69,15 +66,13 @@ class _Plugin: for attr in dir(self): # noinspection PyUnboundLocalVariable if ( - not (attr.startswith('_') or attr in _EXCLUDE_ATTRS) - and - isinstance(func := getattr(self, attr), MethodType) - and - (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) + not (attr.startswith("_") or attr in _EXCLUDE_ATTRS) + and isinstance(func := getattr(self, attr), MethodType) + and (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) ): for data in datas: - if data and data['type'] == 'new_chat_member': - result.append((data['priority'], func)) + if data and data["type"] == "new_chat_member": + result.append((data["priority"], func)) return result @@ -87,34 +82,30 @@ class _Plugin: for attr in dir(self): # noinspection PyUnboundLocalVariable if ( - not (attr.startswith('_') or attr in _EXCLUDE_ATTRS) - and - isinstance(func := getattr(self, attr), MethodType) - and - (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) + not (attr.startswith("_") or attr in _EXCLUDE_ATTRS) + and isinstance(func := getattr(self, attr), MethodType) + and (datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) ): for data in datas: - if data and data['type'] == 'error': - result.update({func: data['block']}) + if data and data["type"] == "error": + result.update({func: data["block"]}) return result @property def jobs(self) -> List[Job]: from core.bot import bot + result = [] for attr in dir(self): # noinspection PyUnboundLocalVariable if ( - not (attr.startswith('_') or attr in _EXCLUDE_ATTRS) - and - isinstance(func := getattr(self, attr), MethodType) - and - (datas := getattr(func, _JOB_ATTR_NAME, None)) + not (attr.startswith("_") or attr in _EXCLUDE_ATTRS) + and isinstance(func := getattr(self, attr), MethodType) + and (datas := getattr(func, _JOB_ATTR_NAME, None)) ): for data in datas: - _job = getattr(bot.job_queue, data.pop('type'))( - callback=func, **data.pop('kwargs'), - **{key: data.pop(key) for key in list(data.keys())} + _job = getattr(bot.job_queue, data.pop("type"))( + callback=func, **data.pop("kwargs"), **{key: data.pop(key) for key in list(data.keys())} ) result.append(_job) return result @@ -138,30 +129,27 @@ class _Conversation(_Plugin): for attr in dir(self): # noinspection PyUnboundLocalVariable if ( - not (attr.startswith('_') or attr == 'handlers') - and - isinstance(func := getattr(self, attr), Callable) - and - (handler_datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) + not (attr.startswith("_") or attr == "handlers") + and isinstance(func := getattr(self, attr), Callable) + and (handler_datas := getattr(func, _NORMAL_HANDLER_ATTR_NAME, None)) ): _handlers = self._make_handler(handler_datas) if conversation_data := getattr(func, _CONVERSATION_HANDLER_ATTR_NAME, None): - if (_type := conversation_data.pop('type')) == 'entry': + if (_type := conversation_data.pop("type")) == "entry": entry_points.extend(_handlers) - elif _type == 'state': - if (key := conversation_data.pop('state')) in states: + elif _type == "state": + if (key := conversation_data.pop("state")) in states: states[key].extend(_handlers) else: states[key] = _handlers - elif _type == 'fallback': + elif _type == "fallback": fallbacks.extend(_handlers) else: result.extend(_handlers) if entry_points or states or fallbacks: result.append( ConversationHandler( - entry_points, states, fallbacks, - **self.__class__._conversation_kwargs # pylint: disable=W0212 + entry_points, states, fallbacks, **self.__class__._conversation_kwargs # pylint: disable=W0212 ) ) return result @@ -180,7 +168,7 @@ class _Handler: return getattr(_Module, f"{self.__class__.__name__.strip('_')}Handler") def __call__(self, func: Callable[P, T]) -> Callable[P, T]: - data = {'type': self._type, 'func': func.__name__, 'kwargs': self.kwargs} + data = {"type": self._type, "func": func.__name__, "kwargs": self.kwargs} if hasattr(func, _NORMAL_HANDLER_ATTR_NAME): handler_datas = getattr(func, _NORMAL_HANDLER_ATTR_NAME) handler_datas.append(data) @@ -192,9 +180,9 @@ class _Handler: class _CallbackQuery(_Handler): def __init__( - self, - pattern: Union[str, Pattern, type, Callable[[object], Optional[bool]]] = None, - block: DVInput[bool] = DEFAULT_TRUE, + self, + pattern: Union[str, Pattern, type, Callable[[object], Optional[bool]]] = None, + block: DVInput[bool] = DEFAULT_TRUE, ): super(_CallbackQuery, self).__init__(pattern=pattern, block=block) @@ -221,10 +209,7 @@ class _Command(_Handler): class _InlineQuery(_Handler): def __init__( - self, - pattern: Union[str, Pattern] = None, - block: DVInput[bool] = DEFAULT_TRUE, - chat_types: List[str] = None + self, pattern: Union[str, Pattern] = None, block: DVInput[bool] = DEFAULT_TRUE, chat_types: List[str] = None ): super().__init__(pattern=pattern, block=block, chat_types=chat_types) @@ -237,7 +222,7 @@ class _MessageNewChatMembers(_Handler): def __call__(self, func: Callable[P, T] = None) -> Callable[P, T]: self.func = self.func or func - data = {'type': 'new_chat_member', 'priority': self.priority} + data = {"type": "new_chat_member", "priority": self.priority} if hasattr(func, _NORMAL_HANDLER_ATTR_NAME): handler_datas = getattr(func, _NORMAL_HANDLER_ATTR_NAME) handler_datas.append(data) @@ -248,7 +233,11 @@ class _MessageNewChatMembers(_Handler): class _Message(_Handler): - def __init__(self, filters: "BaseFilter", block: DVInput[bool] = DEFAULT_TRUE, ): + def __init__( + self, + filters: "BaseFilter", + block: DVInput[bool] = DEFAULT_TRUE, + ): super(_Message, self).__init__(filters=filters, block=block) new_chat_members = _MessageNewChatMembers @@ -271,11 +260,11 @@ class _PreCheckoutQuery(_Handler): class _Prefix(_Handler): def __init__( - self, - prefix: str, - command: str, - filters: BaseFilter = None, - block: DVInput[bool] = DEFAULT_TRUE, + self, + prefix: str, + command: str, + filters: BaseFilter = None, + block: DVInput[bool] = DEFAULT_TRUE, ): super(_Prefix, self).__init__(prefix=prefix, command=command, filters=filters, block=block) @@ -298,10 +287,7 @@ class _StringRegex(_Handler): class _Type(_Handler): # noinspection PyShadowingBuiltins def __init__( - self, - type: Type, # pylint: disable=redefined-builtin - strict: bool = False, - block: DVInput[bool] = DEFAULT_TRUE + self, type: Type, strict: bool = False, block: DVInput[bool] = DEFAULT_TRUE # pylint: disable=redefined-builtin ): super(_Type, self).__init__(type=type, strict=strict, block=block) @@ -342,7 +328,7 @@ class error_handler: def __call__(self, func: Callable[P, T] = None) -> Callable[P, T]: self._func = func or self._func - data = {'type': 'error', 'block': self._block} + data = {"type": "error", "block": self._block} if hasattr(func, _NORMAL_HANDLER_ATTR_NAME): handler_datas = getattr(func, _NORMAL_HANDLER_ATTR_NAME) handler_datas.append(data) @@ -353,7 +339,7 @@ class error_handler: def _entry(func: Callable[P, T]) -> Callable[P, T]: - setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {'type': 'entry'}) + setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {"type": "entry"}) return func @@ -362,12 +348,12 @@ class _State: self.state = state def __call__(self, func: Callable[P, T] = None) -> Callable[P, T]: - setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {'type': 'state', 'state': self.state}) + setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {"type": "state", "state": self.state}) return func def _fallback(func: Callable[P, T]) -> Callable[P, T]: - setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {'type': 'fallback'}) + setattr(func, _CONVERSATION_HANDLER_ATTR_NAME, {"type": "fallback"}) return func @@ -382,8 +368,13 @@ class _Job: kwargs: Dict = {} def __init__( - self, name: str = None, data: object = None, chat_id: int = None, - user_id: int = None, job_kwargs: JSONDict = None, **kwargs + self, + name: str = None, + data: object = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, + **kwargs, ): self.name = name self.data = data @@ -394,9 +385,13 @@ class _Job: def __call__(self, func: JobCallback) -> JobCallback: data = { - 'name': self.name, 'data': self.data, 'chat_id': self.chat_id, 'user_id': self.user_id, - 'job_kwargs': self.job_kwargs, 'kwargs': self.kwargs, - 'type': re.sub(r'([A-Z])', lambda x: '_' + x.group().lower(), self.__class__.__name__).lstrip('_') + "name": self.name, + "data": self.data, + "chat_id": self.chat_id, + "user_id": self.user_id, + "job_kwargs": self.job_kwargs, + "kwargs": self.kwargs, + "type": re.sub(r"([A-Z])", lambda x: "_" + x.group().lower(), self.__class__.__name__).lstrip("_"), } if hasattr(func, _JOB_ATTR_NAME): job_datas = getattr(func, _JOB_ATTR_NAME) @@ -409,39 +404,69 @@ class _Job: class _RunOnce(_Job): def __init__( - self, when: TimeType, - data: object = None, name: str = None, chat_id: int = None, user_id: int = None, job_kwargs: JSONDict = None + self, + when: TimeType, + data: object = None, + name: str = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, ): super().__init__(name, data, chat_id, user_id, job_kwargs, when=when) class _RunRepeating(_Job): def __init__( - self, interval: Union[float, datetime.timedelta], first: TimeType = None, last: TimeType = None, - data: object = None, name: str = None, chat_id: int = None, user_id: int = None, job_kwargs: JSONDict = None + self, + interval: Union[float, datetime.timedelta], + first: TimeType = None, + last: TimeType = None, + data: object = None, + name: str = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, ): super().__init__(name, data, chat_id, user_id, job_kwargs, interval=interval, first=first, last=last) class _RunMonthly(_Job): def __init__( - self, when: datetime.time, day: int, - data: object = None, name: str = None, chat_id: int = None, user_id: int = None, job_kwargs: JSONDict = None + self, + when: datetime.time, + day: int, + data: object = None, + name: str = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, ): super().__init__(name, data, chat_id, user_id, job_kwargs, when=when, day=day) class _RunDaily(_Job): def __init__( - self, time: datetime.time, days: Tuple[int, ...] = tuple(range(7)), - data: object = None, name: str = None, chat_id: int = None, user_id: int = None, job_kwargs: JSONDict = None + self, + time: datetime.time, + days: Tuple[int, ...] = tuple(range(7)), + data: object = None, + name: str = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, ): super().__init__(name, data, chat_id, user_id, job_kwargs, time=time, days=days) class _RunCustom(_Job): - def __init__(self, data: object = None, name: str = None, chat_id: int = None, user_id: int = None, - job_kwargs: JSONDict = None): + def __init__( + self, + data: object = None, + name: str = None, + chat_id: int = None, + user_id: int = None, + job_kwargs: JSONDict = None, + ): super().__init__(name, data, chat_id, user_id, job_kwargs) diff --git a/core/quiz/cache.py b/core/quiz/cache.py index 61cd209f..867d87cf 100644 --- a/core/quiz/cache.py +++ b/core/quiz/cache.py @@ -7,7 +7,6 @@ from .models import Answer, Question class QuizCache: - def __init__(self, redis: RedisDB): self.client = redis.client self.question_qname = "quiz:question" @@ -16,8 +15,7 @@ class QuizCache: async def get_all_question(self) -> List[Question]: temp_list = [] qname = self.question_qname + "id_list" - data_list = [self.question_qname + f":{question_id}" for question_id in - await self.client.lrange(qname, 0, -1)] + data_list = [self.question_qname + f":{question_id}" for question_id in await self.client.lrange(qname, 0, -1)] data = await self.client.mget(data_list) for i in data: temp_list.append(Question.de_json(ujson.loads(i))) diff --git a/core/quiz/models.py b/core/quiz/models.py index 982f7a42..ad41a38a 100644 --- a/core/quiz/models.py +++ b/core/quiz/models.py @@ -7,23 +7,20 @@ from utils.typedefs import JSONDict class AnswerDB(SQLModel, table=True): - __tablename__ = 'answer' - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __tablename__ = "answer" + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) question_id: Optional[int] = Field( - sa_column=Column( - Integer, - ForeignKey("question.id", ondelete="RESTRICT", onupdate="RESTRICT") - ) + sa_column=Column(Integer, ForeignKey("question.id", ondelete="RESTRICT", onupdate="RESTRICT")) ) is_correct: Optional[bool] = Field() text: Optional[str] = Field() class QuestionDB(SQLModel, table=True): - __tablename__ = 'question' - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __tablename__ = "question" + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) text: Optional[str] = Field() diff --git a/core/service.py b/core/service.py index 1085a9f9..a7e02a4a 100644 --- a/core/service.py +++ b/core/service.py @@ -3,7 +3,7 @@ from types import FunctionType from utils.log import logger -__all__ = ['Service', 'init_service'] +__all__ = ["Service", "init_service"] class Service(ABC): @@ -20,11 +20,12 @@ class Service(ABC): def init_service(func: FunctionType): from core.bot import bot + if bot.is_running: try: service = bot.init_inject(func) logger.success(f'服务 "{service.__class__.__name__}" 初始化成功') bot.add_service(service) except Exception as e: # pylint: disable=W0703 - logger.exception(f'来自{func.__module__}的服务初始化失败:{e}') + logger.exception(f"来自{func.__module__}的服务初始化失败:{e}") return func diff --git a/core/sign/models.py b/core/sign/models.py index 6f9d8c62..4a46aece 100644 --- a/core/sign/models.py +++ b/core/sign/models.py @@ -18,7 +18,7 @@ class SignStatusEnum(int, enum.Enum): class Sign(SQLModel, table=True): - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) user_id: int = Field(foreign_key="user.user_id") diff --git a/core/template/services.py b/core/template/services.py index 119910eb..a9f459d8 100644 --- a/core/template/services.py +++ b/core/template/services.py @@ -53,9 +53,16 @@ class TemplateService: logger.debug(f"{template_name} 模板渲染使用了 {str(time.time() - start_time)}") return html - async def render(self, template_path: str, template_name: str, template_data: dict, - viewport: ViewportSize = None, full_page: bool = True, evaluate: Optional[str] = None, - query_selector: str = None) -> bytes: + async def render( + self, + template_path: str, + template_name: str, + template_data: dict, + viewport: ViewportSize = None, + full_page: bool = True, + evaluate: Optional[str] = None, + query_selector: str = None, + ) -> bytes: """模板渲染成图片 :param template_path: 模板目录 :param template_name: 模板文件名 diff --git a/core/user/models.py b/core/user/models.py index 94d8157e..f796b9d3 100644 --- a/core/user/models.py +++ b/core/user/models.py @@ -6,7 +6,7 @@ from utils.models.base import RegionEnum class User(SQLModel, table=True): - __table_args__ = dict(mysql_charset='utf8mb4', mysql_collate="utf8mb4_general_ci") + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") id: int = Field(primary_key=True) user_id: int = Field(unique=True) diff --git a/core/user/repositories.py b/core/user/repositories.py index 694775ca..8b149d25 100644 --- a/core/user/repositories.py +++ b/core/user/repositories.py @@ -33,4 +33,4 @@ class UserRepository: async with self.mysql.Session() as session: session = cast(AsyncSession, session) session.add(user) - await session.commit() \ No newline at end of file + await session.commit() diff --git a/core/user/services.py b/core/user/services.py index f7a5ca84..6e89d05e 100644 --- a/core/user/services.py +++ b/core/user/services.py @@ -3,7 +3,6 @@ from .repositories import UserRepository class UserService: - def __init__(self, user_repository: UserRepository) -> None: self._repository: UserRepository = user_repository diff --git a/core/wiki/services.py b/core/wiki/services.py index 31c953d3..891fc08c 100644 --- a/core/wiki/services.py +++ b/core/wiki/services.py @@ -7,7 +7,6 @@ from utils.log import logger class WikiService: - def __init__(self, cache: WikiCache): self._cache = cache """Redis 在这里的作用是作为持久化""" diff --git a/metadata/pool/pool_200.py b/metadata/pool/pool_200.py index 004e4a13..3bc83392 100644 --- a/metadata/pool/pool_200.py +++ b/metadata/pool/pool_200.py @@ -1,11 +1 @@ -POOL_200 = [ - { - "five": [ - "常驻池" - ], - "four": [], - "from": "2020-09-15 06:00:00", - "name": "常驻池", - "to": "2050-09-15 17:59:59" - } -] \ No newline at end of file +POOL_200 = [{"five": ["常驻池"], "four": [], "from": "2020-09-15 06:00:00", "name": "常驻池", "to": "2050-09-15 17:59:59"}] diff --git a/metadata/pool/pool_301.py b/metadata/pool/pool_301.py index d9757902..c1ee55fc 100644 --- a/metadata/pool/pool_301.py +++ b/metadata/pool/pool_301.py @@ -1,480 +1,254 @@ POOL_301 = [ { - "five": [ - "赛诺", - "温迪" - ], - "four": [ - "久岐忍", - "早柚", - "坎蒂丝" - ], + "five": ["赛诺", "温迪"], + "four": ["久岐忍", "早柚", "坎蒂丝"], "from": "2022-09-28 06:00:00", "name": "劈裁冥昭|杯装之诗", - "to": "2022-10-14 17:59:59" + "to": "2022-10-14 17:59:59", }, { - "five": [ - "甘雨", - "心海" - ], - "four": [ - "行秋", - "砂糖", - "多莉" - ], + "five": ["甘雨", "心海"], + "four": ["行秋", "砂糖", "多莉"], "from": "2022-09-09 18:00:00", "name": "浮生孰来|浮岳虹珠", - "to": "2022-09-27 14:59:59" + "to": "2022-09-27 14:59:59", }, { - "five": [ - "提纳里", - "钟离" - ], - "four": [ - "云堇", - "辛焱", - "班尼特" - ], + "five": ["提纳里", "钟离"], + "four": ["云堇", "辛焱", "班尼特"], "from": "2022-08-24 06:00:00", "name": "巡御蘙荟|陵薮市朝", - "to": "2022-09-09 17:59:59" + "to": "2022-09-09 17:59:59", }, { - "five": [ - "宵宫" - ], - "four": [ - "云堇", - "辛焱", - "班尼特" - ], + "five": ["宵宫"], + "four": ["云堇", "辛焱", "班尼特"], "from": "2022-08-02 18:00:00", "name": "焰色天河", - "to": "2022-08-23 14:59:59" + "to": "2022-08-23 14:59:59", }, { - "five": [ - "枫原万叶", - "可莉" - ], - "four": [ - "凝光", - "鹿野院平藏", - "托马" - ], + "five": ["枫原万叶", "可莉"], + "four": ["凝光", "鹿野院平藏", "托马"], "from": "2022-07-13 06:00:00", "name": "红叶逐荒波", - "to": "2022-08-02 17:59:59" + "to": "2022-08-02 17:59:59", }, { - "five": [ - "荒泷一斗" - ], - "four": [ - "烟绯", - "芭芭拉", - "诺艾尔" - ], + "five": ["荒泷一斗"], + "four": ["烟绯", "芭芭拉", "诺艾尔"], "from": "2022-06-21 18:00:00", "name": "鬼门斗宴", - "to": "2022-07-12 14:59:59" + "to": "2022-07-12 14:59:59", }, { - "five": [ - "夜兰", - "魈" - ], - "four": [ - "烟绯", - "芭芭拉", - "诺艾尔" - ], + "five": ["夜兰", "魈"], + "four": ["烟绯", "芭芭拉", "诺艾尔"], "from": "2022-05-31 06:00:00", "name": "素霓伣天|烟火之邀", - "to": "2022-06-21 17:59:59" + "to": "2022-06-21 17:59:59", }, { - "five": [ - "神里绫华" - ], - "four": [ - "罗莎莉亚", - "早柚", - "雷泽" - ], + "five": ["神里绫华"], + "four": ["罗莎莉亚", "早柚", "雷泽"], "from": "2022-04-19 17:59:59", "name": "白鹭之庭", - "to": "2022-05-31 05:59:59" + "to": "2022-05-31 05:59:59", }, { - "five": [ - "神里绫人", - "温迪" - ], - "four": [ - "香菱", - "砂糖", - "云堇" - ], + "five": ["神里绫人", "温迪"], + "four": ["香菱", "砂糖", "云堇"], "from": "2022-03-30 06:00:00", "name": "苍流踏花|杯装之诗", - "to": "2022-04-19 17:59:59" + "to": "2022-04-19 17:59:59", }, { - "five": [ - "雷电将军", - "珊瑚宫心海" - ], - "four": [ - "辛焱", - "九条裟罗", - "班尼特" - ], + "five": ["雷电将军", "珊瑚宫心海"], + "four": ["辛焱", "九条裟罗", "班尼特"], "from": "2022-03-08 18:00:00", "name": "影寂天下人|浮岳虹珠", - "to": "2022-03-29 14:59:59" + "to": "2022-03-29 14:59:59", }, { - "five": [ - "八重神子" - ], - "four": [ - "菲谢尔", - "迪奥娜", - "托马" - ], + "five": ["八重神子"], + "four": ["菲谢尔", "迪奥娜", "托马"], "from": "2022-02-16 06:00:00", "name": "华紫樱绯", - "to": "2022-03-08 17:59:59" + "to": "2022-03-08 17:59:59", }, { - "five": [ - "甘雨", - "钟离" - ], - "four": [ - "行秋", - "北斗", - "烟绯" - ], + "five": ["甘雨", "钟离"], + "four": ["行秋", "北斗", "烟绯"], "from": "2022-01-25 18:00:00", "name": "浮生孰来|陵薮市朝", - "to": "2022-02-15 14:59:59" + "to": "2022-02-15 14:59:59", }, { - "five": [ - "申鹤", - "魈" - ], - "four": [ - "云堇", - "凝光", - "重云" - ], + "five": ["申鹤", "魈"], + "four": ["云堇", "凝光", "重云"], "from": "2022-01-05 06:00:00", "name": "出尘入世|烟火之邀", - "to": "2022-01-25 17:59:59" + "to": "2022-01-25 17:59:59", }, { - "five": [ - "荒泷一斗" - ], - "four": [ - "五郎", - "芭芭拉", - "香菱" - ], + "five": ["荒泷一斗"], + "four": ["五郎", "芭芭拉", "香菱"], "from": "2021-12-14 18:00:00", "name": "鬼门斗宴", - "to": "2022-01-04 14:59:59" + "to": "2022-01-04 14:59:59", }, { - "five": [ - "阿贝多", - "优菈" - ], - "four": [ - "班尼特", - "诺艾尔", - "罗莎莉亚" - ], + "five": ["阿贝多", "优菈"], + "four": ["班尼特", "诺艾尔", "罗莎莉亚"], "from": "2021-11-24 06:00:00", "name": "深秘之息|浪涌之瞬", - "to": "2021-12-14 17:59:59" + "to": "2021-12-14 17:59:59", }, { - "five": [ - "胡桃" - ], - "four": [ - "托马", - "迪奥娜", - "早柚" - ], + "five": ["胡桃"], + "four": ["托马", "迪奥娜", "早柚"], "from": "2021-11-02 18:00:00", "name": "赤团开时", - "to": "2021-11-23 14:59:59" + "to": "2021-11-23 14:59:59", }, { - "five": [ - "达达利亚" - ], - "four": [ - "凝光", - "重云", - "烟绯" - ], + "five": ["达达利亚"], + "four": ["凝光", "重云", "烟绯"], "from": "2021-10-13 06:00:00", "name": "暂别冬都", - "to": "2021-11-02 17:59:59" + "to": "2021-11-02 17:59:59", }, { - "five": [ - "珊瑚宫心海" - ], - "four": [ - "罗莎莉亚", - "北斗", - "行秋" - ], + "five": ["珊瑚宫心海"], + "four": ["罗莎莉亚", "北斗", "行秋"], "from": "2021-09-21 18:00:00", "name": "浮岳虹珠", - "to": "2021-10-12 14:59:59" + "to": "2021-10-12 14:59:59", }, { - "five": [ - "雷电将军" - ], - "four": [ - "九条裟罗", - "香菱", - "砂糖" - ], + "five": ["雷电将军"], + "four": ["九条裟罗", "香菱", "砂糖"], "from": "2021-09-01 06:00:00", "name": "影寂天下人", - "to": "2021-09-21 17:59:59" + "to": "2021-09-21 17:59:59", }, { - "five": [ - "宵宫" - ], - "four": [ - "早柚", - "迪奥娜", - "辛焱" - ], + "five": ["宵宫"], + "four": ["早柚", "迪奥娜", "辛焱"], "from": "2021-08-10 18:00:00", "name": "焰色天河", - "to": "2021-08-31 14:59:59" + "to": "2021-08-31 14:59:59", }, { - "five": [ - "神里绫华" - ], - "four": [ - "凝光", - "重云", - "烟绯" - ], + "five": ["神里绫华"], + "four": ["凝光", "重云", "烟绯"], "from": "2021-07-21 06:00:00", "name": "白鹭之庭", - "to": "2021-08-10 17:59:59" + "to": "2021-08-10 17:59:59", }, { - "five": [ - "枫原万叶" - ], - "four": [ - "罗莎莉亚", - "班尼特", - "雷泽" - ], + "five": ["枫原万叶"], + "four": ["罗莎莉亚", "班尼特", "雷泽"], "from": "2021-06-29 18:00:00", "name": "红叶逐荒波", - "to": "2021-07-20 14:59:59" + "to": "2021-07-20 14:59:59", }, { - "five": [ - "可莉" - ], - "four": [ - "芭芭拉", - "砂糖", - "菲谢尔" - ], + "five": ["可莉"], + "four": ["芭芭拉", "砂糖", "菲谢尔"], "from": "2021-06-09 06:00:00", "name": "逃跑的太阳", - "to": "2021-06-29 17:59:59" + "to": "2021-06-29 17:59:59", }, { - "five": [ - "优菈" - ], - "four": [ - "辛焱", - "行秋", - "北斗" - ], + "five": ["优菈"], + "four": ["辛焱", "行秋", "北斗"], "from": "2021-05-18 18:00:00", "name": "浪沫的旋舞", - "to": "2021-06-08 14:59:59" + "to": "2021-06-08 14:59:59", }, { - "five": [ - "钟离" - ], - "four": [ - "烟绯", - "诺艾尔", - "迪奥娜" - ], + "five": ["钟离"], + "four": ["烟绯", "诺艾尔", "迪奥娜"], "from": "2021-04-28 06:00:00", "name": "陵薮市朝", - "to": "2021-05-18 17:59:59" + "to": "2021-05-18 17:59:59", }, { - "five": [ - "达达利亚" - ], - "four": [ - "罗莎莉亚", - "芭芭拉", - "菲谢尔" - ], + "five": ["达达利亚"], + "four": ["罗莎莉亚", "芭芭拉", "菲谢尔"], "from": "2021-04-06 18:00:00", "name": "暂别冬都", - "to": "2021-04-27 14:59:59" + "to": "2021-04-27 14:59:59", }, { - "five": [ - "温迪" - ], - "four": [ - "砂糖", - "雷泽", - "诺艾尔" - ], + "five": ["温迪"], + "four": ["砂糖", "雷泽", "诺艾尔"], "from": "2021-03-17 06:00:00", "name": "杯装之诗", - "to": "2021-04-06 15:59:59" + "to": "2021-04-06 15:59:59", }, { - "five": [ - "胡桃" - ], - "four": [ - "行秋", - "香菱", - "重云" - ], + "five": ["胡桃"], + "four": ["行秋", "香菱", "重云"], "from": "2021-03-02 18:00:00", "name": "赤团开时", - "to": "2021-03-16 14:59:59" + "to": "2021-03-16 14:59:59", }, { - "five": [ - "刻晴" - ], - "four": [ - "凝光", - "班尼特", - "芭芭拉" - ], + "five": ["刻晴"], + "four": ["凝光", "班尼特", "芭芭拉"], "from": "2021-02-17 18:00:00", "name": "鱼龙灯昼", - "to": "2021-03-02 15:59:59" + "to": "2021-03-02 15:59:59", }, { - "five": [ - "魈" - ], - "four": [ - "迪奥娜", - "北斗", - "辛焱" - ], + "five": ["魈"], + "four": ["迪奥娜", "北斗", "辛焱"], "from": "2021-02-03 06:00:00", "name": "烟火之邀", - "to": "2021-02-17 15:59:59" + "to": "2021-02-17 15:59:59", }, { - "five": [ - "甘雨" - ], - "four": [ - "香菱", - "行秋", - "诺艾尔" - ], + "five": ["甘雨"], + "four": ["香菱", "行秋", "诺艾尔"], "from": "2021-01-12 18:00:00", "name": "浮生孰来", - "to": "2021-02-02 14:59:59" + "to": "2021-02-02 14:59:59", }, { - "five": [ - "阿贝多" - ], - "four": [ - "菲谢尔", - "砂糖", - "班尼特" - ], + "five": ["阿贝多"], + "four": ["菲谢尔", "砂糖", "班尼特"], "from": "2020-12-23 06:00:00", "name": "深秘之息", - "to": "2021-01-12 15:59:59" + "to": "2021-01-12 15:59:59", }, { - "five": [ - "钟离" - ], - "four": [ - "辛焱", - "雷泽", - "重云" - ], + "five": ["钟离"], + "four": ["辛焱", "雷泽", "重云"], "from": "2020-12-01 18:00:00", "name": "陵薮市朝", - "to": "2020-12-22 14:59:59" + "to": "2020-12-22 14:59:59", }, { - "five": [ - "达达利亚" - ], - "four": [ - "迪奥娜", - "北斗", - "凝光" - ], + "five": ["达达利亚"], + "four": ["迪奥娜", "北斗", "凝光"], "from": "2020-11-11 06:00:00", "name": "暂别冬都", - "to": "2020-12-01 15:59:59" + "to": "2020-12-01 15:59:59", }, { - "five": [ - "可莉" - ], - "four": [ - "行秋", - "诺艾尔", - "砂糖" - ], + "five": ["可莉"], + "four": ["行秋", "诺艾尔", "砂糖"], "from": "2020-10-20 18:00:00", "name": "闪焰的驻足", - "to": "2020-11-10 14:59:59" + "to": "2020-11-10 14:59:59", }, { - "five": [ - "温迪" - ], - "four": [ - "芭芭拉", - "菲谢尔", - "香菱" - ], + "five": ["温迪"], + "four": ["芭芭拉", "菲谢尔", "香菱"], "from": "2020-9-28 06:00:00", "name": "杯装之诗", - "to": "2020-10-18 17:59:59" - } -] \ No newline at end of file + "to": "2020-10-18 17:59:59", + }, +] diff --git a/metadata/pool/pool_302.py b/metadata/pool/pool_302.py index 4653e896..5c04b2c5 100644 --- a/metadata/pool/pool_302.py +++ b/metadata/pool/pool_302.py @@ -1,562 +1,247 @@ POOL_302 = [ { - "five": [ - "赤沙之杖", - "终末嗟叹之诗" - ], - "four": [ - "匣里龙吟", - "玛海菈的水色", - "西风长枪", - "祭礼残章", - "西风猎弓" - ], + "five": ["赤沙之杖", "终末嗟叹之诗"], + "four": ["匣里龙吟", "玛海菈的水色", "西风长枪", "祭礼残章", "西风猎弓"], "from": "2022-09-28 06:00:00", "name": "神铸赋形", - "to": "2022-10-14 17:59:59" + "to": "2022-10-14 17:59:59", }, { - "five": [ - "阿莫斯之弓", - "不灭月华" - ], - "four": [ - "祭礼剑", - "西风大剑", - "匣里灭辰", - "昭心", - "弓藏" - ], + "five": ["阿莫斯之弓", "不灭月华"], + "four": ["祭礼剑", "西风大剑", "匣里灭辰", "昭心", "弓藏"], "from": "2022-09-09 18:00:00", "name": "神铸赋形", - "to": "2022-09-27 14:59:59" + "to": "2022-09-27 14:59:59", }, { - "five": [ - "猎人之径", - "贯虹之槊" - ], - "four": [ - "西风剑", - "钟剑", - "西风长枪", - "西风秘典", - "绝弦" - ], + "five": ["猎人之径", "贯虹之槊"], + "four": ["西风剑", "钟剑", "西风长枪", "西风秘典", "绝弦"], "from": "2022-08-24 06:00:00", "name": "神铸赋形", - "to": "2022-09-09 17:59:59" + "to": "2022-09-09 17:59:59", }, { - "five": [ - "飞雷之弦振", - "斫峰之刃" - ], - "four": [ - "暗巷的酒与诗", - "暗巷猎手", - "笛剑", - "祭礼大剑", - "匣里灭辰" - ], + "five": ["飞雷之弦振", "斫峰之刃"], + "four": ["暗巷的酒与诗", "暗巷猎手", "笛剑", "祭礼大剑", "匣里灭辰"], "from": "2022-08-02 18:00:00", "name": "神铸赋形", - "to": "2022-08-23 14:59:59" + "to": "2022-08-23 14:59:59", }, { - "five": [ - "苍古自由之誓", - "四风原典" - ], - "four": [ - "千岩古剑", - "匣里龙吟", - "匣里灭辰", - "祭礼残章", - "绝弦" - ], + "five": ["苍古自由之誓", "四风原典"], + "four": ["千岩古剑", "匣里龙吟", "匣里灭辰", "祭礼残章", "绝弦"], "from": "2022-07-13 06:00:00", "name": "神铸赋形", - "to": "2022-08-02 17:59:59" + "to": "2022-08-02 17:59:59", }, { - "five": [ - "赤角石溃杵", - "尘世之锁" - ], - "four": [ - "千岩古剑", - "匣里龙吟", - "匣里灭辰", - "祭礼残章", - "绝弦" - ], + "five": ["赤角石溃杵", "尘世之锁"], + "four": ["千岩古剑", "匣里龙吟", "匣里灭辰", "祭礼残章", "绝弦"], "from": "2022-06-21 18:00:00", "name": "神铸赋形", - "to": "2022-07-12 14:59:59" + "to": "2022-07-12 14:59:59", }, { - "five": [ - "若水", - "和璞鸢" - ], - "four": [ - "千岩长枪", - "祭礼剑", - "西风大剑", - "昭心", - "祭礼弓" - ], + "five": ["若水", "和璞鸢"], + "four": ["千岩长枪", "祭礼剑", "西风大剑", "昭心", "祭礼弓"], "from": "2022-05-31 06:00:00", "name": "神铸赋形", - "to": "2022-06-21 17:59:59" + "to": "2022-06-21 17:59:59", }, { - "five": [ - "雾切之回光", - "无工之剑" - ], - "four": [ - "西风剑", - "钟剑", - "西风长枪", - "西风秘典", - "西风猎弓" - ], + "five": ["雾切之回光", "无工之剑"], + "four": ["西风剑", "钟剑", "西风长枪", "西风秘典", "西风猎弓"], "from": "2022-04-19 17:59:59", "name": "神铸赋形", - "to": "2022-05-31 05:59:59" + "to": "2022-05-31 05:59:59", }, { - "five": [ - "波乱月白经津", - "终末嗟叹之诗" - ], - "four": [ - "弓藏", - "笛剑", - "流浪乐章", - "匣里灭辰", - "祭礼大剑" - ], + "five": ["波乱月白经津", "终末嗟叹之诗"], + "four": ["弓藏", "笛剑", "流浪乐章", "匣里灭辰", "祭礼大剑"], "from": "2022-03-30 06:00:00", "name": "神铸赋形", - "to": "2022-04-19 17:59:59" + "to": "2022-04-19 17:59:59", }, { - "five": [ - "薙草之稻光", - "不灭月华" - ], - "four": [ - "恶王丸", - "曚云之月", - "匣里龙吟", - "西风长枪", - "祭礼残章" - ], + "five": ["薙草之稻光", "不灭月华"], + "four": ["恶王丸", "曚云之月", "匣里龙吟", "西风长枪", "祭礼残章"], "from": "2022-03-08 18:00:00", "name": "神铸赋形", - "to": "2022-03-29 14:59:59" + "to": "2022-03-29 14:59:59", }, { - "five": [ - "神乐之真意", - "磐岩结绿" - ], - "four": [ - "祭礼剑", - "雨裁", - "断浪长鳍", - "昭心", - "绝弦" - ], + "five": ["神乐之真意", "磐岩结绿"], + "four": ["祭礼剑", "雨裁", "断浪长鳍", "昭心", "绝弦"], "from": "2022-02-16 06:00:00", "name": "神铸赋形", - "to": "2022-03-08 17:59:59" + "to": "2022-03-08 17:59:59", }, { - "five": [ - "贯虹之槊", - "阿莫斯之弓" - ], - "four": [ - "西风剑", - "千岩古剑", - "匣里灭辰", - "西风秘典", - "祭礼弓" - ], + "five": ["贯虹之槊", "阿莫斯之弓"], + "four": ["西风剑", "千岩古剑", "匣里灭辰", "西风秘典", "祭礼弓"], "from": "2022-01-25 18:00:00", "name": "神铸赋形", - "to": "2022-02-15 14:59:59" + "to": "2022-02-15 14:59:59", }, { - "five": [ - "息灾", - "和璞鸢" - ], - "four": [ - "笛剑", - "西风大剑", - "千岩长枪", - "流浪乐章", - "西风猎弓" - ], + "five": ["息灾", "和璞鸢"], + "four": ["笛剑", "西风大剑", "千岩长枪", "流浪乐章", "西风猎弓"], "from": "2022-01-05 06:00:00", "name": "神铸赋形", - "to": "2022-01-25 17:59:59" + "to": "2022-01-25 17:59:59", }, { - "five": [ - "赤角石溃杵", - "天空之翼" - ], - "four": [ - "暗巷闪光", - "钟剑", - "西风长枪", - "祭礼残章", - "幽夜华尔兹" - ], + "five": ["赤角石溃杵", "天空之翼"], + "four": ["暗巷闪光", "钟剑", "西风长枪", "祭礼残章", "幽夜华尔兹"], "from": "2021-12-14 18:00:00", "name": "神铸赋形", - "to": "2022-01-04 14:59:59" + "to": "2022-01-04 14:59:59", }, { - "five": [ - "苍古自由之誓", - "松籁响起之时" - ], - "four": [ - "匣里龙吟", - "祭礼大剑", - "匣里灭辰", - "暗巷的酒与诗", - "暗巷猎手" - ], + "five": ["苍古自由之誓", "松籁响起之时"], + "four": ["匣里龙吟", "祭礼大剑", "匣里灭辰", "暗巷的酒与诗", "暗巷猎手"], "from": "2021-11-24 06:00:00", "name": "神铸赋形", - "to": "2021-12-14 17:59:59" + "to": "2021-12-14 17:59:59", }, { - "five": [ - "护摩之杖", - "终末嗟叹之诗" - ], - "four": [ - "祭礼剑", - "雨裁", - "断浪长鳍", - "流浪乐章", - "曚云之月" - ], + "five": ["护摩之杖", "终末嗟叹之诗"], + "four": ["祭礼剑", "雨裁", "断浪长鳍", "流浪乐章", "曚云之月"], "from": "2021-11-02 18:00:00", "name": "神铸赋形", - "to": "2021-11-23 14:59:59" + "to": "2021-11-23 14:59:59", }, { - "five": [ - "冬极白星", - "尘世之锁" - ], - "four": [ - "西风剑", - "恶王丸", - "西风长枪", - "昭心", - "弓藏" - ], + "five": ["冬极白星", "尘世之锁"], + "four": ["西风剑", "恶王丸", "西风长枪", "昭心", "弓藏"], "from": "2021-10-13 06:00:00", "name": "神铸赋形", - "to": "2021-11-02 17:59:59" + "to": "2021-11-02 17:59:59", }, { - "five": [ - "不灭月华", - "磐岩结绿" - ], - "four": [ - "笛剑", - "西风大剑", - "匣里灭辰", - "西风秘典", - "绝弦" - ], + "five": ["不灭月华", "磐岩结绿"], + "four": ["笛剑", "西风大剑", "匣里灭辰", "西风秘典", "绝弦"], "from": "2021-09-21 18:00:00", "name": "神铸赋形", - "to": "2021-10-12 14:59:59" + "to": "2021-10-12 14:59:59", }, { - "five": [ - "薙草之稻光", - "无工之剑" - ], - "four": [ - "匣里龙吟", - "钟剑", - "西风长枪", - "流浪乐章", - "祭礼弓" - ], + "five": ["薙草之稻光", "无工之剑"], + "four": ["匣里龙吟", "钟剑", "西风长枪", "流浪乐章", "祭礼弓"], "from": "2021-09-01 06:00:00", "name": "神铸赋形", - "to": "2021-09-21 17:59:59" + "to": "2021-09-21 17:59:59", }, { - "five": [ - "飞雷之弦振", - "天空之刃" - ], - "four": [ - "祭礼剑", - "雨裁", - "匣里灭辰", - "祭礼残章", - "西风猎弓" - ], + "five": ["飞雷之弦振", "天空之刃"], + "four": ["祭礼剑", "雨裁", "匣里灭辰", "祭礼残章", "西风猎弓"], "from": "2021-08-10 18:00:00", "name": "神铸赋形", - "to": "2021-08-31 14:59:59" + "to": "2021-08-31 14:59:59", }, { - "five": [ - "雾切之回光", - "天空之脊" - ], - "four": [ - "西风剑", - "祭礼大剑", - "西风长枪", - "西风秘典", - "绝弦" - ], + "five": ["雾切之回光", "天空之脊"], + "four": ["西风剑", "祭礼大剑", "西风长枪", "西风秘典", "绝弦"], "from": "2021-07-21 06:00:00", "name": "神铸赋形", - "to": "2021-08-10 17:59:59" + "to": "2021-08-10 17:59:59", }, { - "five": [ - "苍古自由之誓", - "天空之卷" - ], - "four": [ - "暗巷闪光", - "西风大剑", - "匣里灭辰", - "暗巷的酒与诗", - "暗巷猎手" - ], + "five": ["苍古自由之誓", "天空之卷"], + "four": ["暗巷闪光", "西风大剑", "匣里灭辰", "暗巷的酒与诗", "暗巷猎手"], "from": "2021-06-29 18:00:00", "name": "神铸赋形", - "to": "2021-07-20 14:59:59" + "to": "2021-07-20 14:59:59", }, { - "five": [ - "天空之傲", - "四风原典" - ], - "four": [ - "匣里龙吟", - "钟剑", - "西风长枪", - "流浪乐章", - "幽夜华尔兹" - ], + "five": ["天空之傲", "四风原典"], + "four": ["匣里龙吟", "钟剑", "西风长枪", "流浪乐章", "幽夜华尔兹"], "from": "2021-06-09 06:00:00", "name": "神铸赋形", - "to": "2021-06-29 17:59:59" + "to": "2021-06-29 17:59:59", }, { - "five": [ - "松籁响起之时", - "风鹰剑" - ], - "four": [ - "祭礼剑", - "雨裁", - "匣里灭辰", - "祭礼残章", - "弓藏" - ], + "five": ["松籁响起之时", "风鹰剑"], + "four": ["祭礼剑", "雨裁", "匣里灭辰", "祭礼残章", "弓藏"], "from": "2021-05-18 18:00:00", "name": "神铸赋形", - "to": "2021-06-08 14:59:59" + "to": "2021-06-08 14:59:59", }, { - "five": [ - "斫峰之刃", - "尘世之锁" - ], - "four": [ - "笛剑", - "千岩古剑", - "祭礼弓", - "昭心", - "千岩长枪" - ], + "five": ["斫峰之刃", "尘世之锁"], + "four": ["笛剑", "千岩古剑", "祭礼弓", "昭心", "千岩长枪"], "from": "2021-04-28 06:00:00", "name": "神铸赋形", - "to": "2021-05-18 17:59:59" + "to": "2021-05-18 17:59:59", }, { - "five": [ - "天空之翼", - "四风原典" - ], - "four": [ - "西风剑", - "祭礼大剑", - "暗巷猎手", - "西风秘典", - "西风长枪" - ], + "five": ["天空之翼", "四风原典"], + "four": ["西风剑", "祭礼大剑", "暗巷猎手", "西风秘典", "西风长枪"], "from": "2021-04-06 18:00:00", "name": "神铸赋形", - "to": "2021-04-27 14:59:59" + "to": "2021-04-27 14:59:59", }, { - "five": [ - "终末嗟叹之诗", - "天空之刃" - ], - "four": [ - "暗巷闪光", - "西风大剑", - "西风猎弓", - "暗巷的酒与诗", - "匣里灭辰" - ], + "five": ["终末嗟叹之诗", "天空之刃"], + "four": ["暗巷闪光", "西风大剑", "西风猎弓", "暗巷的酒与诗", "匣里灭辰"], "from": "2021-03-17 06:00:00", "name": "神铸赋形", - "to": "2021-04-06 15:59:59" + "to": "2021-04-06 15:59:59", }, { - "five": [ - "护摩之杖", - "狼的末路" - ], - "four": [ - "匣里龙吟", - "千岩古剑", - "祭礼弓", - "流浪乐章", - "千岩长枪" - ], + "five": ["护摩之杖", "狼的末路"], + "four": ["匣里龙吟", "千岩古剑", "祭礼弓", "流浪乐章", "千岩长枪"], "from": "2021-02-23 18:00:00", "name": "神铸赋形", - "to": "2021-03-16 14:59:59" + "to": "2021-03-16 14:59:59", }, { - "five": [ - "磐岩结绿", - "和璞鸢" - ], - "four": [ - "笛剑", - "祭礼大剑", - "弓藏", - "昭心", - "西风长枪" - ], + "five": ["磐岩结绿", "和璞鸢"], + "four": ["笛剑", "祭礼大剑", "弓藏", "昭心", "西风长枪"], "from": "2021-02-03 06:00:00", "name": "神铸赋形", - "to": "2021-02-23 15:59:59" + "to": "2021-02-23 15:59:59", }, { - "five": [ - "阿莫斯之弓", - "天空之傲" - ], - "four": [ - "祭礼剑", - "钟剑", - "匣里灭辰", - "昭心", - "西风猎弓" - ], + "five": ["阿莫斯之弓", "天空之傲"], + "four": ["祭礼剑", "钟剑", "匣里灭辰", "昭心", "西风猎弓"], "from": "2021-01-12 18:00:00", "name": "神铸赋形", - "to": "2021-02-02 14:59:59" + "to": "2021-02-02 14:59:59", }, { - "five": [ - "斫峰之刃", - "天空之卷" - ], - "four": [ - "西风剑", - "西风大剑", - "西风长枪", - "祭礼残章", - "绝弦" - ], + "five": ["斫峰之刃", "天空之卷"], + "four": ["西风剑", "西风大剑", "西风长枪", "祭礼残章", "绝弦"], "from": "2020-12-23 06:00:00", "name": "神铸赋形", - "to": "2021-01-12 15:59:59" + "to": "2021-01-12 15:59:59", }, { - "five": [ - "贯虹之槊", - "无工之剑" - ], - "four": [ - "匣里龙吟", - "钟剑", - "西风秘典", - "西风猎弓", - "匣里灭辰" - ], + "five": ["贯虹之槊", "无工之剑"], + "four": ["匣里龙吟", "钟剑", "西风秘典", "西风猎弓", "匣里灭辰"], "from": "2020-12-01 18:00:00", "name": "神铸赋形", - "to": "2020-12-22 14:59:59" + "to": "2020-12-22 14:59:59", }, { - "five": [ - "天空之翼", - "尘世之锁" - ], - "four": [ - "笛剑", - "雨裁", - "昭心", - "弓藏", - "西风长枪" - ], + "five": ["天空之翼", "尘世之锁"], + "four": ["笛剑", "雨裁", "昭心", "弓藏", "西风长枪"], "from": "2020-11-11 06:00:00", "name": "神铸赋形", - "to": "2020-12-01 15:59:59" + "to": "2020-12-01 15:59:59", }, { - "five": [ - "四风原典", - "狼的末路" - ], - "four": [ - "祭礼剑", - "祭礼大剑", - "祭礼残章", - "祭礼弓", - "匣里灭辰" - ], + "five": ["四风原典", "狼的末路"], + "four": ["祭礼剑", "祭礼大剑", "祭礼残章", "祭礼弓", "匣里灭辰"], "from": "2020-10-20 18:00:00", "name": "神铸赋形", - "to": "2020-11-10 14:59:59" + "to": "2020-11-10 14:59:59", }, { - "five": [ - "风鹰剑", - "阿莫斯之弓" - ], - "four": [ - "祭礼剑", - "祭礼大剑", - "祭礼残章", - "祭礼弓", - "匣里灭辰" - ], + "five": ["风鹰剑", "阿莫斯之弓"], + "four": ["祭礼剑", "祭礼大剑", "祭礼残章", "祭礼弓", "匣里灭辰"], "from": "2020-09-28 06:00:00", "name": "神铸赋形", - "to": "2020-10-18 17:59:59" - } -] \ No newline at end of file + "to": "2020-10-18 17:59:59", + }, +] diff --git a/metadata/scripts/honey.py b/metadata/scripts/honey.py index b6328544..478f565f 100644 --- a/metadata/scripts/honey.py +++ b/metadata/scripts/honey.py @@ -14,8 +14,12 @@ from utils.log import logger from utils.typedefs import StrOrInt __all__ = [ - 'get_avatar_data', 'get_artifact_data', 'get_material_data', 'get_namecard_data', 'get_weapon_data', - 'update_honey_metadata', + "get_avatar_data", + "get_artifact_data", + "get_material_data", + "get_namecard_data", + "get_weapon_data", + "update_honey_metadata", ] DATA_TYPE = Dict[StrOrInt, List[str]] @@ -41,12 +45,12 @@ async def get_avatar_data() -> DATA_TYPE: result = {} url = "https://genshin.honeyhunterworld.com/fam_chars/?lang=CHS" response = await request(url) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) # 转为 json for data in json_data: - cid = int("10000" + re.findall(r'\d+', data[1])[0]) + cid = int("10000" + re.findall(r"\d+", data[1])[0]) honey_id = re.findall(r"/(.*?)/", data[1])[0] - name = re.findall(r'>(.*)<', data[1])[0] + name = re.findall(r">(.*)<", data[1])[0] rarity = int(re.findall(r">(\d)<", data[2])[0]) result[cid] = [honey_id, name, rarity] return result @@ -59,13 +63,13 @@ async def get_weapon_data() -> DATA_TYPE: urls = [HONEY_HOST.join(f"fam_{i.lower()}/?lang=CHS") for i in WeaponType.__members__] for url in urls: response = await request(url) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) # 转为 json for data in json_data: - name = re.findall(r'>(.*)<', data[1])[0] - if name in ['「一心传」名刀', '石英大剑', '琥珀玥', '黑檀弓']: # 跳过特殊的武器 + name = re.findall(r">(.*)<", data[1])[0] + if name in ["「一心传」名刀", "石英大剑", "琥珀玥", "黑檀弓"]: # 跳过特殊的武器 continue - wid = int(re.findall(r'\d+', data[1])[0]) + wid = int(re.findall(r"\d+", data[1])[0]) honey_id = re.findall(r"/(.*?)/", data[1])[0] rarity = int(re.findall(r">(\d)<", data[2])[0]) result[wid] = [honey_id, name, rarity] @@ -75,27 +79,27 @@ async def get_weapon_data() -> DATA_TYPE: async def get_material_data() -> DATA_TYPE: result = {} - weapon = [HONEY_HOST.join(f'fam_wep_{i}/?lang=CHS') for i in ['primary', 'secondary', 'common']] - talent = [HONEY_HOST.join(f'fam_talent_{i}/?lang=CHS') for i in ['book', 'boss', 'common', 'reward']] + weapon = [HONEY_HOST.join(f"fam_wep_{i}/?lang=CHS") for i in ["primary", "secondary", "common"]] + talent = [HONEY_HOST.join(f"fam_talent_{i}/?lang=CHS") for i in ["book", "boss", "common", "reward"]] namecard = [HONEY_HOST.join("fam_nameplate/?lang=CHS")] urls = weapon + talent + namecard response = await request("https://api.ambr.top/v2/chs/material") - ambr_data = json.loads(response.text)['data']['items'] + ambr_data = json.loads(response.text)["data"]["items"] for url in urls: response = await request(url) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) # 转为 json for data in json_data: - honey_id = re.findall(r'/(.*?)/', data[1])[0] - name = re.findall(r'>(.*)<', data[1])[0] + honey_id = re.findall(r"/(.*?)/", data[1])[0] + name = re.findall(r">(.*)<", data[1])[0] rarity = int(re.findall(r">(\d)<", data[2])[0]) mid = None for mid, item in ambr_data.items(): - if name == item['name']: + if name == item["name"]: break - mid = int(mid) or int(re.findall(r'\d+', data[1])[0]) + mid = int(mid) or int(re.findall(r"\d+", data[1])[0]) result[mid] = [honey_id, name, rarity] return result @@ -103,7 +107,7 @@ async def get_material_data() -> DATA_TYPE: async def get_artifact_data() -> DATA_TYPE: async def get_first_id(_link) -> str: _response = await request(_link) - _chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', _response.text)[0] + _chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", _response.text)[0] _json_data = json.loads(_chaos_data) return re.findall(r"/(.*?)/", _json_data[-1][1])[0] @@ -111,21 +115,21 @@ async def get_artifact_data() -> DATA_TYPE: url = "https://genshin.honeyhunterworld.com/fam_art_set/?lang=CHS" response = await request("https://api.ambr.top/v2/chs/reliquary") - ambr_data = json.loads(response.text)['data']['items'] + ambr_data = json.loads(response.text)["data"]["items"] response = await request(url) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) # 转为 json for data in json_data: - honey_id = re.findall(r'/(.*?)/', data[1])[0] + honey_id = re.findall(r"/(.*?)/", data[1])[0] name = re.findall(r"alt=\"(.*?)\"", data[0])[0] link = HONEY_HOST.join(re.findall(r'href="(.*?)"', data[0])[0]) first_id = await get_first_id(link) aid = None for aid, item in ambr_data.items(): - if name == item['name']: + if name == item["name"]: break - aid = aid or re.findall(r'\d+', data[1])[0] + aid = aid or re.findall(r"\d+", data[1])[0] result[aid] = [honey_id, name, first_id] return result @@ -138,20 +142,21 @@ async def get_namecard_data() -> DATA_TYPE: # noinspection PyProtectedMember from metadata.genshin import Data from metadata.scripts.metadatas import update_metadata_from_github + await update_metadata_from_github() # noinspection PyPep8Naming - NAMECARD_DATA = Data('namecard') + NAMECARD_DATA = Data("namecard") url = HONEY_HOST.join("fam_nameplate/?lang=CHS") result = {} response = await request(url) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) for data in json_data: - honey_id = re.findall(r'/(.*?)/', data[1])[0] + honey_id = re.findall(r"/(.*?)/", data[1])[0] name = re.findall(r"alt=\"(.*?)\"", data[0])[0] try: - nid = [key for key, value in NAMECARD_DATA.items() if value['name'] == name][0] + nid = [key for key, value in NAMECARD_DATA.items() if value["name"] == name][0] except IndexError: # 暂不支持 beta 的名片 continue rarity = int(re.findall(r">(\d)<", data[2])[0]) @@ -161,7 +166,7 @@ async def get_namecard_data() -> DATA_TYPE: async def update_honey_metadata(overwrite: bool = True) -> FULL_DATA_TYPE | None: - path = PROJECT_ROOT.joinpath('metadata/data/honey.json') + path = PROJECT_ROOT.joinpath("metadata/data/honey.json") if not overwrite and path.exists(): return avatar_data = await get_avatar_data() @@ -176,13 +181,13 @@ async def update_honey_metadata(overwrite: bool = True) -> FULL_DATA_TYPE | None logger.success("Namecard data is done.") result = { - 'avatar': avatar_data, - 'weapon': weapon_data, - 'material': material_data, - 'artifact': artifact_data, - 'namecard': namecard_data, + "avatar": avatar_data, + "weapon": weapon_data, + "material": material_data, + "artifact": artifact_data, + "namecard": namecard_data, } path.parent.mkdir(parents=True, exist_ok=True) - async with async_open(path, mode='w', encoding='utf-8') as file: + async with async_open(path, mode="w", encoding="utf-8") as file: await file.write(json.dumps(result, ensure_ascii=False)) return result diff --git a/metadata/scripts/metadatas.py b/metadata/scripts/metadatas.py index cdea1ce0..9f06d73a 100644 --- a/metadata/scripts/metadatas.py +++ b/metadata/scripts/metadatas.py @@ -4,23 +4,23 @@ from httpx import AsyncClient, URL from utils.const import AMBR_HOST, PROJECT_ROOT -__all__ = ['update_metadata_from_ambr', 'update_metadata_from_github'] +__all__ = ["update_metadata_from_ambr", "update_metadata_from_github"] client = AsyncClient() async def update_metadata_from_ambr(overwrite: bool = True): result = [] - targets = ['material', 'weapon', 'avatar', 'reliquary'] + targets = ["material", "weapon", "avatar", "reliquary"] for target in targets: - path = PROJECT_ROOT.joinpath(f'metadata/data/{target}.json') + path = PROJECT_ROOT.joinpath(f"metadata/data/{target}.json") if not overwrite and path.exists(): continue url = AMBR_HOST.join(f"v2/chs/{target}") path.parent.mkdir(parents=True, exist_ok=True) response = await client.get(url) - json_data = json.loads(response.text)['data']['items'] - async with async_open(path, mode='w', encoding='utf-8') as file: + json_data = json.loads(response.text)["data"]["items"] + async with async_open(path, mode="w", encoding="utf-8") as file: data = json.dumps(json_data, ensure_ascii=False) await file.write(data) result.append(json_data) @@ -28,7 +28,7 @@ async def update_metadata_from_ambr(overwrite: bool = True): async def update_metadata_from_github(overwrite: bool = True): - path = PROJECT_ROOT.joinpath('metadata/data/namecard.json') + path = PROJECT_ROOT.joinpath("metadata/data/namecard.json") if not overwrite and path.exists(): return @@ -41,25 +41,27 @@ async def update_metadata_from_github(overwrite: bool = True): material_json_data = json.loads((await client.get(material_url)).text) data = {} - for namecard_data in filter(lambda x: x.get('materialType', None) == 'MATERIAL_NAMECARD', material_json_data): - name = text_map_json_data[str(namecard_data['nameTextMapHash'])] - icon = namecard_data['icon'] - navbar = namecard_data['picPath'][0] - banner = namecard_data['picPath'][1] - rank = namecard_data['rankLevel'] - description = text_map_json_data[str(namecard_data['descTextMapHash'])].replace('\\n', '\n') - data.update({ - str(namecard_data['id']): { - "id": namecard_data['id'], - "name": name, - "rank": rank, - "icon": icon, - "navbar": navbar, - "profile": banner, - "description": description, + for namecard_data in filter(lambda x: x.get("materialType", None) == "MATERIAL_NAMECARD", material_json_data): + name = text_map_json_data[str(namecard_data["nameTextMapHash"])] + icon = namecard_data["icon"] + navbar = namecard_data["picPath"][0] + banner = namecard_data["picPath"][1] + rank = namecard_data["rankLevel"] + description = text_map_json_data[str(namecard_data["descTextMapHash"])].replace("\\n", "\n") + data.update( + { + str(namecard_data["id"]): { + "id": namecard_data["id"], + "name": name, + "rank": rank, + "icon": icon, + "navbar": navbar, + "profile": banner, + "description": description, + } } - }) - async with async_open(path, mode='w', encoding='utf-8') as file: + ) + async with async_open(path, mode="w", encoding="utf-8") as file: data = json.dumps(data, ensure_ascii=False) await file.write(data) return data diff --git a/metadata/shortname.py b/metadata/shortname.py index bcf8e415..f5a84d03 100644 --- a/metadata/shortname.py +++ b/metadata/shortname.py @@ -4,103 +4,274 @@ import functools from metadata.genshin import WEAPON_DATA -__all__ = [ - 'roles', 'weapons', - 'roleToId', 'roleToName', 'weaponToName', 'weaponToId' -] +__all__ = ["roles", "weapons", "roleToId", "roleToName", "weaponToName", "weaponToId"] # noinspection SpellCheckingInspection roles = { - 20000000: [ - '主角', '旅行者', '卑鄙的外乡人', '荣誉骑士', '爷', '风主', '岩主', '雷主', '草主', '履刑者', '抽卡不歪真君' + 20000000: ["主角", "旅行者", "卑鄙的外乡人", "荣誉骑士", "爷", "风主", "岩主", "雷主", "草主", "履刑者", "抽卡不歪真君"], + 10000002: ["神里绫华", "Ayaka", "ayaka", "Kamisato Ayaka", "神里", "绫华", "神里凌华", "凌华", "白鹭公主", "神里大小 姐"], + 10000003: ["琴", "Jean", "jean", "团长", "代理团长", "琴团长", "蒲公英骑士"], + 10000005: ["空", "Aether", "aether", "男主", "男主角", "龙哥", "空哥"], + 10000006: ["丽莎", "Lisa", "lisa", "图书管理员", "图书馆管理员", "蔷薇魔女"], + 10000007: ["荧", "Lumine", "lumine", "女主", "女主角", "莹", "萤", "黄毛阿姨", "荧妹"], + 10000014: ["芭芭拉", "Barbara", "barbara", "巴巴拉", "拉粑粑", "拉巴巴", "内鬼", "加湿器", "闪耀偶像", "偶像"], + 10000015: ["凯亚", "Kaeya", "kaeya", "盖亚", "凯子哥", "凯鸭", "矿工", "矿工头子", "骑兵队长", "凯子", "凝冰渡海真君"], + 10000016: [ + "迪卢克", + "Diluc", + "diluc", + "卢姥爷", + "姥爷", + "卢老爷", + "卢锅巴", + "正义人", + "正e人", + "正E人", + "卢本伟", + "暗夜英雄", + "卢卢伯爵", + "落魄了", + "落魄了家人们", ], - 10000002: ['神里绫华', 'Ayaka', 'ayaka', 'Kamisato Ayaka', '神里', '绫华', '神里凌华', '凌华', '白鹭公主', - '神里大小 姐'], - 10000003: ['琴', 'Jean', 'jean', '团长', '代理团长', '琴团长', '蒲公英骑士'], - 10000005: ['空', 'Aether', 'aether', '男主', '男主角', '龙哥', '空哥'], - 10000006: ['丽莎', 'Lisa', 'lisa', '图书管理员', '图书馆管理员', '蔷薇魔女'], - 10000007: ['荧', 'Lumine', 'lumine', '女主', '女主角', '莹', '萤', '黄毛阿姨', '荧妹'], - 10000014: ['芭芭拉', 'Barbara', 'barbara', '巴巴拉', '拉粑粑', '拉巴巴', '内鬼', '加湿器', '闪耀偶像', '偶像'], - 10000015: ['凯亚', 'Kaeya', 'kaeya', '盖亚', '凯子哥', '凯鸭', '矿工', '矿工头子', '骑兵队长', '凯子', - '凝冰渡海真君'], - 10000016: ['迪卢克', 'Diluc', 'diluc', '卢姥爷', '姥爷', '卢老爷', '卢锅巴', '正义人', '正e人', '正E人', '卢本伟', - '暗夜英雄', '卢卢伯爵', '落魄了', '落魄了家人们'], - 10000020: ['雷泽', 'Razor', 'razor', '狼少年', '狼崽子', '狼崽', '卢皮卡', '小狼', '小狼狗'], - 10000021: ['安柏', 'Amber', 'amber', '安伯', '兔兔伯爵', '飞行冠军', '侦查骑士', '点火姬', '点火机', '打火机', - '打火姬'], - 10000022: ['温迪', 'Venti', 'venti', '温蒂', '风神', '卖唱的', '巴巴托斯', '巴巴脱丝', '芭芭托斯', '芭芭脱丝', - '干点正事', '不干正事', '吟游诗人', '诶嘿', '唉嘿', '摸鱼'], - 10000023: ['香菱', 'Xiangling', 'xiangling', '香玲', '锅巴', '厨师', '万民堂厨师', '香师傅'], - 10000024: ['北斗', 'Beidou', 'beidou', '大姐头', '大姐', '无冕的龙王', '龙王'], - 10000025: ['行秋', 'Xingqiu', 'xingqiu', '秋秋人', '秋妹妹', '书呆子', '水神', '飞云商会二少爷'], - 10000026: ['魈', 'Xiao', 'xiao', '杏仁豆腐', '打桩机', '插秧', '三眼五显仙人', '三眼五显真人', '降魔大圣', - '护法夜叉', '快乐风男', '无聊', '靖妖傩舞', '矮子仙人', '三点五尺仙人', '跳跳虎'], - 10000027: ['凝光', 'Ningguang', 'ningguang', '富婆', '天权星'], - 10000029: ['可莉', 'Klee', 'klee', '嘟嘟可', '火花骑士', '蹦蹦炸弹', '炸鱼', '放火烧山', '放火烧山真君', - '蒙德最强战力', '逃跑的太阳', '啦啦啦', '哒哒哒', '炸弹人', '禁闭室'], - 10000030: ['钟离', 'Zhongli', 'zhongli', '摩拉克斯', '岩王爷', '岩神', '钟师傅', '天动万象', '岩王帝君', '未来可期', - '帝君', '拒收病婿'], - 10000031: ['菲谢尔', 'Fischl', 'fischl', '皇女', '小艾米', '小艾咪', '奥兹', '断罪皇女', '中二病', '中二少女', - '中二皇女', '奥兹发射器'], - 10000032: ['班尼特', 'Bennett', 'bennett', '点赞哥', '点赞', '倒霉少年', '倒霉蛋', '霹雳闪雷真君', '班神', '班爷', - '倒霉', '火神', '六星真神'], - 10000033: ['达达利亚', 'Tartaglia', 'tartaglia', 'Childe', 'childe', 'Ajax', 'ajax', '达达鸭', '达达利鸭', '公子', - '玩具销售员', '玩具推销员', '钱包', '鸭鸭', '愚人众末席'], - 10000034: ['诺艾尔', 'Noelle', 'noelle', '女仆', '高达', '岩王帝姬'], - 10000035: ['七七', 'Qiqi', 'qiqi', '僵尸', '肚饿真君', '度厄真君', '77'], - 10000036: ['重云', 'Chongyun', 'chongyun', '纯阳之体', '冰棍'], - 10000037: ['甘雨', 'Ganyu', 'ganyu', '椰羊', '椰奶', '王小美'], - 10000038: ['阿贝多', 'Albedo', 'albedo', '可莉哥哥', '升降机', '升降台', '电梯', '白垩之子', '贝爷', '白垩', - '阿贝少', '花呗多', '阿贝夕', 'abd', '阿师傅'], - 10000039: ['迪奥娜', 'Diona', 'diona', '迪欧娜', 'dio', 'dio娜', '冰猫', '猫猫', '猫娘', '喵喵', '调酒师'], - 10000041: ['莫娜', 'Mona', 'mona', '穷鬼', '穷光蛋', '穷', '莫纳', '占星术士', '占星师', '讨龙真君', '半部讨龙真君', - '阿斯托洛吉斯·莫娜·梅姬斯图斯', '梅姬斯图斯', '梅姬斯图斯卿'], - 10000042: ['刻晴', 'Keqing', 'keqing', '刻情', '氪晴', '刻师傅', '刻师父', '牛杂', '牛杂师傅', '斩尽牛杂', '免疫', - '免疫免疫', '屁斜剑法', '玉衡星', '阿晴', ' 啊晴'], - 10000043: ['砂糖', 'Sucrose', 'sucrose', '雷莹术士', '雷萤术士', '雷荧术士'], - 10000044: ['辛焱', 'Xinyan', 'xinyan', '辛炎', '黑妹', '摇滚'], - 10000045: ['罗莎莉亚', 'Rosaria', 'rosaria', '罗莎莉娅', '白色史莱姆', '白史莱姆', '修女', '罗莎利亚', '罗莎利娅', - '罗沙莉亚', '罗沙莉娅', '罗沙利亚', '罗沙利娅', '萝莎莉亚', '萝莎莉娅', '萝莎利亚', '萝莎利娅', - '萝沙莉亚', '萝沙莉娅', '萝沙利亚', '萝沙利娅'], - 10000046: ['胡桃', 'HuTao', 'hutao', 'Hu Tao', 'hu tao', 'Hutao', '胡 淘', '往生堂堂主', '火化', '抬棺的', '蝴蝶', - '核桃', '堂主', '胡堂主', '雪霁梅香'], - 10000047: ['枫原万叶', 'Kazuha', 'kazuha', 'Kaedehara Kazuha', '万叶', '叶天帝', '天帝', '叶师傅'], - 10000048: ['烟绯', 'Yanfei', 'yanfei', '烟老师', '律师', '罗翔'], - 10000049: ['宵宫', 'Yoimiya', 'yoimiya', '霄宫', '烟花', '肖宫', '肖工', '绷带女孩'], - 10000050: ['托马', 'Thoma', 'thoma', '家政官', '太郎丸', '地头蛇', '男仆', '拖马'], - 10000051: ['优菈', 'Eula', 'eula', '优拉', '尤拉', '尤菈', '浪花骑士', '记仇', '劳伦斯'], - 10000052: ['雷电将军', 'Raiden Shogun', 'Raiden', 'raiden', '雷神', '将军', '雷军', '巴尔', '阿影', '影', - '巴尔泽布', '煮饭婆', '奶香一刀', '无想一刀', '宅女'], - 10000053: ['早柚', 'Sayu', 'sayu', '小狸猫', '狸 猫', '忍者'], - 10000054: ['珊瑚宫心海', 'Kokomi', 'kokomi', 'Sangonomiya Kokomi', '心海', '军师', '珊瑚宫', '书记', '观赏鱼', - '水母', '鱼', '美人鱼'], - 10000055: ['五郎', 'Gorou', 'gorou', '柴犬', '土狗', '希娜', '希娜小姐'], - 10000056: ['九条裟罗', 'Sara', 'sara', 'Kujou Sara', '九条', '九条沙罗', '裟罗', '沙罗', '天狗'], - 10000057: ['荒泷一斗', 'Itto', 'itto', 'Arataki Itto', '荒龙一斗', '荒泷天下第一斗', '一斗', '一抖', '荒泷', '1斗', - '牛牛', '斗子哥', '牛子哥', '牛子', '孩子 王', '斗虫', '巧乐兹', '放牛的'], - 10000058: ['八重神子', 'Miko', 'miko', 'Yae Miko', '八重', '神子', '狐狸', '想得美哦', '巫女', '屑狐狸', '骚狐狸', - '八重宫司', '婶子', '小八'], - 10000059: ['鹿野院平藏', 'Heizou', 'heizou', 'shikanoin heizou', 'heizo', '鹿野苑', '鹿野院', '平藏', '鹿野苑平藏', - '鹿野', '小鹿'], - 10000060: ['夜兰', 'Yelan', 'yelan', '夜阑', '叶 澜', '腋兰', '夜天后'], - 10000062: ['埃洛伊', 'Aloy', 'aloy'], - 10000063: ['申鹤', 'Shenhe', 'shenhe', '神鹤', '小姨', '小姨子', '审鹤'], - 10000064: ['云堇', 'YunJin', 'yunjin', 'Yun Jin', 'yun jin', '云瑾', '云先生', '云锦', '神女劈观'], - 10000065: ['久岐忍', 'Kuki', 'kuki', 'Kuki Shinobu', 'Shinobu', 'shinobu', '97忍', '小忍', '久歧忍', '97', '茄忍', - '阿忍', '忍姐'], - 10000066: ['神里绫人', 'Ayato', 'ayato', 'Kamisato Ayato', '绫人', '神里凌人', '凌人', '0人', '神人', '零人', - '大舅哥'], - 10000067: ['柯莱', 'Collei', 'collei', '柯来', '科莱', '科来', '小天使', '须弥安柏', '须弥飞行冠军', '见习巡林员', - '克莱', '草安伯'], - 10000068: ['多莉', 'Dori', 'dori', '多利', '多力', '多丽', '奸商'], - 10000069: ['提纳里', 'Tighnari', 'tighnari', '小提', '提那里', '缇娜里', '提哪里', '驴', '柯莱老师', '柯莱师傅', - '巡林官', - '提那里'], - 10000070: ['妮露', 'Nilou', 'nilou', '尼露', '尼禄'], - 10000071: ['赛诺', 'Cyno', 'cyno', '赛洛'], - 10000072: ['坎蒂丝', 'Candace', 'candace', '坎迪斯'], - 10000073: ['纳西妲', 'Nahida', 'nahida', '草王', '草神', '小吉祥草王', '草萝莉', '纳西坦'], - 10000074: ['莱依拉', 'Layla', 'layla', '拉一拉'], + 10000020: ["雷泽", "Razor", "razor", "狼少年", "狼崽子", "狼崽", "卢皮卡", "小狼", "小狼狗"], + 10000021: ["安柏", "Amber", "amber", "安伯", "兔兔伯爵", "飞行冠军", "侦查骑士", "点火姬", "点火机", "打火机", "打火姬"], + 10000022: [ + "温迪", + "Venti", + "venti", + "温蒂", + "风神", + "卖唱的", + "巴巴托斯", + "巴巴脱丝", + "芭芭托斯", + "芭芭脱丝", + "干点正事", + "不干正事", + "吟游诗人", + "诶嘿", + "唉嘿", + "摸鱼", + ], + 10000023: ["香菱", "Xiangling", "xiangling", "香玲", "锅巴", "厨师", "万民堂厨师", "香师傅"], + 10000024: ["北斗", "Beidou", "beidou", "大姐头", "大姐", "无冕的龙王", "龙王"], + 10000025: ["行秋", "Xingqiu", "xingqiu", "秋秋人", "秋妹妹", "书呆子", "水神", "飞云商会二少爷"], + 10000026: [ + "魈", + "Xiao", + "xiao", + "杏仁豆腐", + "打桩机", + "插秧", + "三眼五显仙人", + "三眼五显真人", + "降魔大圣", + "护法夜叉", + "快乐风男", + "无聊", + "靖妖傩舞", + "矮子仙人", + "三点五尺仙人", + "跳跳虎", + ], + 10000027: ["凝光", "Ningguang", "ningguang", "富婆", "天权星"], + 10000029: [ + "可莉", + "Klee", + "klee", + "嘟嘟可", + "火花骑士", + "蹦蹦炸弹", + "炸鱼", + "放火烧山", + "放火烧山真君", + "蒙德最强战力", + "逃跑的太阳", + "啦啦啦", + "哒哒哒", + "炸弹人", + "禁闭室", + ], + 10000030: ["钟离", "Zhongli", "zhongli", "摩拉克斯", "岩王爷", "岩神", "钟师傅", "天动万象", "岩王帝君", "未来可期", "帝君", "拒收病婿"], + 10000031: ["菲谢尔", "Fischl", "fischl", "皇女", "小艾米", "小艾咪", "奥兹", "断罪皇女", "中二病", "中二少女", "中二皇女", "奥兹发射器"], + 10000032: ["班尼特", "Bennett", "bennett", "点赞哥", "点赞", "倒霉少年", "倒霉蛋", "霹雳闪雷真君", "班神", "班爷", "倒霉", "火神", "六星真神"], + 10000033: [ + "达达利亚", + "Tartaglia", + "tartaglia", + "Childe", + "childe", + "Ajax", + "ajax", + "达达鸭", + "达达利鸭", + "公子", + "玩具销售员", + "玩具推销员", + "钱包", + "鸭鸭", + "愚人众末席", + ], + 10000034: ["诺艾尔", "Noelle", "noelle", "女仆", "高达", "岩王帝姬"], + 10000035: ["七七", "Qiqi", "qiqi", "僵尸", "肚饿真君", "度厄真君", "77"], + 10000036: ["重云", "Chongyun", "chongyun", "纯阳之体", "冰棍"], + 10000037: ["甘雨", "Ganyu", "ganyu", "椰羊", "椰奶", "王小美"], + 10000038: [ + "阿贝多", + "Albedo", + "albedo", + "可莉哥哥", + "升降机", + "升降台", + "电梯", + "白垩之子", + "贝爷", + "白垩", + "阿贝少", + "花呗多", + "阿贝夕", + "abd", + "阿师傅", + ], + 10000039: ["迪奥娜", "Diona", "diona", "迪欧娜", "dio", "dio娜", "冰猫", "猫猫", "猫娘", "喵喵", "调酒师"], + 10000041: [ + "莫娜", + "Mona", + "mona", + "穷鬼", + "穷光蛋", + "穷", + "莫纳", + "占星术士", + "占星师", + "讨龙真君", + "半部讨龙真君", + "阿斯托洛吉斯·莫娜·梅姬斯图斯", + "梅姬斯图斯", + "梅姬斯图斯卿", + ], + 10000042: [ + "刻晴", + "Keqing", + "keqing", + "刻情", + "氪晴", + "刻师傅", + "刻师父", + "牛杂", + "牛杂师傅", + "斩尽牛杂", + "免疫", + "免疫免疫", + "屁斜剑法", + "玉衡星", + "阿晴", + " 啊晴", + ], + 10000043: ["砂糖", "Sucrose", "sucrose", "雷莹术士", "雷萤术士", "雷荧术士"], + 10000044: ["辛焱", "Xinyan", "xinyan", "辛炎", "黑妹", "摇滚"], + 10000045: [ + "罗莎莉亚", + "Rosaria", + "rosaria", + "罗莎莉娅", + "白色史莱姆", + "白史莱姆", + "修女", + "罗莎利亚", + "罗莎利娅", + "罗沙莉亚", + "罗沙莉娅", + "罗沙利亚", + "罗沙利娅", + "萝莎莉亚", + "萝莎莉娅", + "萝莎利亚", + "萝莎利娅", + "萝沙莉亚", + "萝沙莉娅", + "萝沙利亚", + "萝沙利娅", + ], + 10000046: [ + "胡桃", + "HuTao", + "hutao", + "Hu Tao", + "hu tao", + "Hutao", + "胡 淘", + "往生堂堂主", + "火化", + "抬棺的", + "蝴蝶", + "核桃", + "堂主", + "胡堂主", + "雪霁梅香", + ], + 10000047: ["枫原万叶", "Kazuha", "kazuha", "Kaedehara Kazuha", "万叶", "叶天帝", "天帝", "叶师傅"], + 10000048: ["烟绯", "Yanfei", "yanfei", "烟老师", "律师", "罗翔"], + 10000049: ["宵宫", "Yoimiya", "yoimiya", "霄宫", "烟花", "肖宫", "肖工", "绷带女孩"], + 10000050: ["托马", "Thoma", "thoma", "家政官", "太郎丸", "地头蛇", "男仆", "拖马"], + 10000051: ["优菈", "Eula", "eula", "优拉", "尤拉", "尤菈", "浪花骑士", "记仇", "劳伦斯"], + 10000052: [ + "雷电将军", + "Raiden Shogun", + "Raiden", + "raiden", + "雷神", + "将军", + "雷军", + "巴尔", + "阿影", + "影", + "巴尔泽布", + "煮饭婆", + "奶香一刀", + "无想一刀", + "宅女", + ], + 10000053: ["早柚", "Sayu", "sayu", "小狸猫", "狸 猫", "忍者"], + 10000054: ["珊瑚宫心海", "Kokomi", "kokomi", "Sangonomiya Kokomi", "心海", "军师", "珊瑚宫", "书记", "观赏鱼", "水母", "鱼", "美人鱼"], + 10000055: ["五郎", "Gorou", "gorou", "柴犬", "土狗", "希娜", "希娜小姐"], + 10000056: ["九条裟罗", "Sara", "sara", "Kujou Sara", "九条", "九条沙罗", "裟罗", "沙罗", "天狗"], + 10000057: [ + "荒泷一斗", + "Itto", + "itto", + "Arataki Itto", + "荒龙一斗", + "荒泷天下第一斗", + "一斗", + "一抖", + "荒泷", + "1斗", + "牛牛", + "斗子哥", + "牛子哥", + "牛子", + "孩子 王", + "斗虫", + "巧乐兹", + "放牛的", + ], + 10000058: ["八重神子", "Miko", "miko", "Yae Miko", "八重", "神子", "狐狸", "想得美哦", "巫女", "屑狐狸", "骚狐狸", "八重宫司", "婶子", "小八"], + 10000059: ["鹿野院平藏", "Heizou", "heizou", "shikanoin heizou", "heizo", "鹿野苑", "鹿野院", "平藏", "鹿野苑平藏", "鹿野", "小鹿"], + 10000060: ["夜兰", "Yelan", "yelan", "夜阑", "叶 澜", "腋兰", "夜天后"], + 10000062: ["埃洛伊", "Aloy", "aloy"], + 10000063: ["申鹤", "Shenhe", "shenhe", "神鹤", "小姨", "小姨子", "审鹤"], + 10000064: ["云堇", "YunJin", "yunjin", "Yun Jin", "yun jin", "云瑾", "云先生", "云锦", "神女劈观"], + 10000065: ["久岐忍", "Kuki", "kuki", "Kuki Shinobu", "Shinobu", "shinobu", "97忍", "小忍", "久歧忍", "97", "茄忍", "阿忍", "忍姐"], + 10000066: ["神里绫人", "Ayato", "ayato", "Kamisato Ayato", "绫人", "神里凌人", "凌人", "0人", "神人", "零人", "大舅哥"], + 10000067: ["柯莱", "Collei", "collei", "柯来", "科莱", "科来", "小天使", "须弥安柏", "须弥飞行冠军", "见习巡林员", "克莱", "草安伯"], + 10000068: ["多莉", "Dori", "dori", "多利", "多力", "多丽", "奸商"], + 10000069: ["提纳里", "Tighnari", "tighnari", "小提", "提那里", "缇娜里", "提哪里", "驴", "柯莱老师", "柯莱师傅", "巡林官", "提那里"], + 10000070: ["妮露", "Nilou", "nilou", "尼露", "尼禄"], + 10000071: ["赛诺", "Cyno", "cyno", "赛洛"], + 10000072: ["坎蒂丝", "Candace", "candace", "坎迪斯"], + 10000073: ["纳西妲", "Nahida", "nahida", "草王", "草神", "小吉祥草王", "草萝莉", "纳西坦"], + 10000074: ["莱依拉", "Layla", "layla", "拉一拉"], } not_real_roles = [10000073, 10000074] weapons = { @@ -110,37 +281,29 @@ weapons = { "贯虹之槊": ["贯虹", "岩枪", "盾枪"], "赤角石溃杵": ["赤角", "石溃杵"], "尘世之锁": ["尘世锁", "尘世", "盾书", "锁"], - "终末嗟叹之诗": ["终末", "终末弓", "叹气弓", "乐团弓"], "松籁响起之时": ["松籁", "乐团大剑", "松剑"], "苍古自由之誓": ["苍古", "乐团剑"], - "「渔获」": ["鱼叉", "渔叉"], "衔珠海皇": ["海皇", "咸鱼剑", "咸鱼大剑"], - "匣里日月": ["日月"], "匣里灭辰": ["灭辰"], "匣里龙吟": ["龙吟"], - "天空之翼": ["天空弓"], "天空之刃": ["天空剑"], "天空之卷": ["天空书", "厕纸"], "天空之脊": ["天空枪", "薄荷枪"], "天空之傲": ["天空大剑"], "四风原典": ["四风"], - "试作斩岩": ["斩岩"], "试作星镰": ["星镰"], "试作金珀": ["金珀"], "试作古华": ["古华"], "试作澹月": ["澹月"], - "千岩长枪": ["千岩枪"], "千岩古剑": ["千岩剑", "千岩大剑"], - "暗巷闪光": ["暗巷剑"], "暗巷猎手": ["暗巷弓"], - "阿莫斯之弓": ["阿莫斯", "ams", "痛苦弓"], "雾切之回光": ["雾切"], "飞雷之弦振": ["飞雷", "飞雷弓"], @@ -154,7 +317,6 @@ weapons = { "不灭月华": ["月华"], "波乱月白经津": ["波乱", "月白", "波乱月白", "经津", "波波津"], "若水": ["麒麟弓", "Aqua", "aqua"], - "昭心": ["糟心"], "幽夜华尔兹": ["幽夜", "幽夜弓", "华尔兹", "皇女弓"], "雪葬的星银": ["雪葬", "星银", "雪葬星银", "雪山大剑"], @@ -172,15 +334,13 @@ weapons = { "嘟嘟可故事集": ["嘟嘟可"], "辰砂之纺锤": ["辰砂", "辰砂纺锤", "纺锤"], "白辰之环": ["白辰", "白辰环"], - "决斗之枪": ["决斗枪", "决斗", "月卡枪"], "螭骨剑": ["螭骨", "丈育剑", "离骨剑", "月卡大剑"], "黑剑": ["月卡剑"], "苍翠猎弓": ["绿弓", "月卡弓"], - "讨龙英杰谭": ["讨龙"], "神射手之誓": ["脚气弓", "神射手"], - "黑缨枪": ["史莱姆枪"] + "黑缨枪": ["史莱姆枪"], } @@ -209,4 +369,4 @@ def weaponToName(shortname: str) -> str: @functools.lru_cache() def weaponToId(name: str) -> int | None: """获取武器ID""" - return next((int(key) for key, value in WEAPON_DATA.items() if weaponToName(name) in value['name']), None) + return next((int(key) for key, value in WEAPON_DATA.items() if weaponToName(name) in value["name"]), None) diff --git a/modules/apihelper/artifact.py b/modules/apihelper/artifact.py index b3949b2c..66564392 100644 --- a/modules/apihelper/artifact.py +++ b/modules/apihelper/artifact.py @@ -9,20 +9,54 @@ def get_format_sub_item(artifact_attr: dict): def get_comment(get_rate_num): - data = {"1": ["破玩意谁能用啊,谁都用不了吧", "喂了吧,这东西做狗粮还能有点用", "抽卡有保底,圣遗物没有下限", - "未来可期呢(笑)", "你出门一定很安全", "你是不是得罪米哈游了?", "……宁就是班尼特本特?", - "丢人!你给我退出提瓦特(", "不能说很糟糕,只能说特别不好"], - "2": ["淡如清泉镇的圣水,莫得提升", "你怎么不强化啊?", "嗯嗯嗯好好好可以可以可以挺好挺好(敷衍)", - "这就是日常,下一个", "洗洗还能吃(bushi)", "下次一定行……?", "派蒙平静地点了个赞", - "不知道该说什么,就当留个纪念吧"], - "3": ["不能说有质变,只能说有提升", "过渡用的话没啥问题,大概", "再努努力吧", "嗯,差不多能用", - "这很合理", "达成成就“合格圣遗物”", "嗯,及格了,过渡用挺好的", "中规中矩,有待提升"], - "4": ["以普遍理性而论,很好", "算是个很不戳的圣遗物了!", "很好,很有精神!", "再努努力,超越一下自己", - "感觉可以戴着它大杀四方了", "这就是大佬背包里的平均水平吧", "先锁上呗,这波不亏", "达成成就“高分圣遗物”", - "这波对输出有很大提升啊(认真)", "我也想拥有这种分数的圣遗物(切实)"], - "5": ["多吃点好的,出门注意安全", "晒吧,欧不可耻,只是可恨", "没啥好说的,让我自闭一会", "达成成就“高分圣遗物”", - "怕不是以后开宝箱只能开出卷心菜", "吃了吗?没吃的话,吃我一拳", "我觉得这个游戏有问题", "这合理吗", - "这东西没啥用,给我吧(柠檬)", "??? ????"]} + data = { + "1": [ + "破玩意谁能用啊,谁都用不了吧", + "喂了吧,这东西做狗粮还能有点用", + "抽卡有保底,圣遗物没有下限", + "未来可期呢(笑)", + "你出门一定很安全", + "你是不是得罪米哈游了?", + "……宁就是班尼特本特?", + "丢人!你给我退出提瓦特(", + "不能说很糟糕,只能说特别不好", + ], + "2": [ + "淡如清泉镇的圣水,莫得提升", + "你怎么不强化啊?", + "嗯嗯嗯好好好可以可以可以挺好挺好(敷衍)", + "这就是日常,下一个", + "洗洗还能吃(bushi)", + "下次一定行……?", + "派蒙平静地点了个赞", + "不知道该说什么,就当留个纪念吧", + ], + "3": ["不能说有质变,只能说有提升", "过渡用的话没啥问题,大概", "再努努力吧", "嗯,差不多能用", "这很合理", "达成成就“合格圣遗物”", "嗯,及格了,过渡用挺好的", "中规中矩,有待提升"], + "4": [ + "以普遍理性而论,很好", + "算是个很不戳的圣遗物了!", + "很好,很有精神!", + "再努努力,超越一下自己", + "感觉可以戴着它大杀四方了", + "这就是大佬背包里的平均水平吧", + "先锁上呗,这波不亏", + "达成成就“高分圣遗物”", + "这波对输出有很大提升啊(认真)", + "我也想拥有这种分数的圣遗物(切实)", + ], + "5": [ + "多吃点好的,出门注意安全", + "晒吧,欧不可耻,只是可恨", + "没啥好说的,让我自闭一会", + "达成成就“高分圣遗物”", + "怕不是以后开宝箱只能开出卷心菜", + "吃了吗?没吃的话,吃我一拳", + "我觉得这个游戏有问题", + "这合理吗", + "这东西没啥用,给我吧(柠檬)", + "??? ????", + ], + } try: data_ = int(float(get_rate_num)) except ValueError: @@ -36,22 +70,22 @@ class ArtifactOcrRate: OCR_URL = "https://api.genshin.pub/api/v1/app/ocr" RATE_URL = "https://api.genshin.pub/api/v1/relic/rate" HEADERS = { - 'authority': 'api.genshin.pub', - 'accept': 'application/json, text/plain, */*', - 'accept-language': 'zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4', - 'content-type': 'application/json;charset=UTF-8', - 'dnt': '1', - 'origin': 'https://genshin.pub', - 'referer': 'https://genshin.pub/', - 'sec-ch-ua': '"Chromium";v="104", " Not A;Brand";v="99"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-site', - 'sec-gpc': '1', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/104.0.5112.115 Safari/537.36', + "authority": "api.genshin.pub", + "accept": "application/json, text/plain, */*", + "accept-language": "zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4", + "content-type": "application/json;charset=UTF-8", + "dnt": "1", + "origin": "https://genshin.pub", + "referer": "https://genshin.pub/", + "sec-ch-ua": '"Chromium";v="104", " Not A;Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-site", + "sec-gpc": "1", + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/104.0.5112.115 Safari/537.36", } def __init__(self): diff --git a/modules/apihelper/base.py b/modules/apihelper/base.py index 63930c1f..e3ef79e8 100644 --- a/modules/apihelper/base.py +++ b/modules/apihelper/base.py @@ -43,8 +43,14 @@ class PostInfo(BaseModel): created_at = post["created_at"] user = _data_post["user"] # 用户数据 user_uid = user["uid"] # 用户ID - return PostInfo(_data=data, post_id=post_id, user_uid=user_uid, subject=subject, image_urls=image_urls, - created_at=created_at) + return PostInfo( + _data=data, + post_id=post_id, + user_uid=user_uid, + subject=subject, + image_urls=image_urls, + created_at=created_at, + ) def __getitem__(self, item): return self._data[item] diff --git a/modules/apihelper/helpers.py b/modules/apihelper/helpers.py index 97bf71ba..90f9a4c4 100644 --- a/modules/apihelper/helpers.py +++ b/modules/apihelper/helpers.py @@ -16,7 +16,7 @@ RECOGNIZE_SERVER = { def get_device_id(name: str) -> str: - return str(uuid.uuid3(uuid.NAMESPACE_URL, name)).replace('-', '').upper() + return str(uuid.uuid3(uuid.NAMESPACE_URL, name)).replace("-", "").upper() def md5(text: str) -> str: @@ -26,7 +26,7 @@ def md5(text: str) -> str: def random_text(num: int) -> str: - return ''.join(random.sample(string.ascii_lowercase + string.digits, num)) + return "".join(random.sample(string.ascii_lowercase + string.digits, num)) def timestamp() -> int: @@ -58,6 +58,6 @@ def get_recognize_server(uid: int) -> str: def get_headers(): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36", + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36", } return headers diff --git a/modules/apihelper/hyperion.py b/modules/apihelper/hyperion.py index 93004905..4e8d0952 100644 --- a/modules/apihelper/hyperion.py +++ b/modules/apihelper/hyperion.py @@ -20,8 +20,10 @@ class Hyperion: POST_FULL_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFull" POST_FULL_IN_COLLECTION_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFullInCollection" GET_NEW_LIST_URL = "https://bbs-api.mihoyo.com/post/wapi/getNewsList" - USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \ - "Chrome/90.0.4430.72 Safari/537.36" + USER_AGENT = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/90.0.4430.72 Safari/537.36" + ) def __init__(self): self.client = HOYORequest(headers=self.get_headers()) @@ -42,34 +44,31 @@ class Hyperion: if entries is None: return -1 try: - art_id = int(entries.get('article_id')) + art_id = int(entries.get("article_id")) except (IndexError, ValueError, TypeError): return -1 return art_id def get_headers(self, referer: str = "https://bbs.mihoyo.com/"): - return { - "User-Agent": self.USER_AGENT, - "Referer": referer - } + return {"User-Agent": self.USER_AGENT, "Referer": referer} @staticmethod - def get_list_url_params(forum_id: int, is_good: bool = False, is_hot: bool = False, - page_size: int = 20) -> dict: + def get_list_url_params(forum_id: int, is_good: bool = False, is_hot: bool = False, page_size: int = 20) -> dict: params = { "forum_id": forum_id, "gids": 2, "is_good": is_good, "is_hot": is_hot, "page_size": page_size, - "sort_type": 1 + "sort_type": 1, } return params @staticmethod - def get_images_params(resize: int = 600, quality: int = 80, auto_orient: int = 0, interlace: int = 1, - images_format: str = "jpg"): + def get_images_params( + resize: int = 600, quality: int = 80, auto_orient: int = 0, interlace: int = 1, images_format: str = "jpg" + ): """ image/resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,jpg :param resize: 图片大小 @@ -79,25 +78,19 @@ class Hyperion: :param images_format: 图片格式 :return: """ - params = f"image/resize,s_{resize}/quality,q_{quality}/auto-orient," \ - f"{auto_orient}/interlace,{interlace}/format,{images_format}" + params = ( + f"image/resize,s_{resize}/quality,q_{quality}/auto-orient," + f"{auto_orient}/interlace,{interlace}/format,{images_format}" + ) return {"x-oss-process": params} async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> JSONDict: - params = { - "collection_id": collection_id, - "gids": gids, - "order_type": order_type - } + params = {"collection_id": collection_id, "gids": gids, "order_type": order_type} response = await self.client.get(url=self.POST_FULL_IN_COLLECTION_URL, params=params) return response async def get_post_info(self, gids: int, post_id: int, read: int = 1) -> PostInfo: - params = { - "gids": gids, - "post_id": post_id, - "read": read - } + params = {"gids": gids, "post_id": post_id, "read": read} response = await self.client.get(self.POST_FULL_URL, params=params) return PostInfo.paste_data(response) @@ -128,11 +121,7 @@ class Hyperion: ?gids=2&page_size=20&type=3 :return: """ - params = { - "gids": gids, - "page_size": page_size, - "type": type_id - } + params = {"gids": gids, "page_size": page_size, "type": type_id} response = await self.client.get(url=self.GET_NEW_LIST_URL, params=params) return response @@ -144,12 +133,14 @@ class GachaInfo: GACHA_LIST_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/gacha/list.json" GACHA_INFO_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/%s/zh-cn.json" - USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \ - "Chrome/90.0.4430.72 Safari/537.36" + USER_AGENT = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/90.0.4430.72 Safari/537.36" + ) def __init__(self): self.headers = { - 'User-Agent': self.USER_AGENT, + "User-Agent": self.USER_AGENT, } self.client = HOYORequest(headers=self.headers) self.cache = {} @@ -177,32 +168,35 @@ class GachaInfo: class SignIn: LOGIN_URL = "https://webapi.account.mihoyo.com/Api/login_by_mobilecaptcha" - S_TOKEN_URL = "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?" \ - "login_ticket={0}&token_types=3&uid={1}" + S_TOKEN_URL = ( + "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?" "login_ticket={0}&token_types=3&uid={1}" + ) BBS_URL = "https://api-takumi.mihoyo.com/account/auth/api/webLoginByMobile" - USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " \ - "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15" + USER_AGENT = ( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15" + ) HEADERS = { "Host": "webapi.account.mihoyo.com", "Connection": "keep-alive", - "sec-ch-ua": "\".Not/A)Brand\";v=\"99\", \"Microsoft Edge\";v=\"103\", \"Chromium\";v=\"103\"", + "sec-ch-ua": '".Not/A)Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103"', "DNT": "1", "x-rpc-device_model": "OS X 10.15.7", "sec-ch-ua-mobile": "?0", "User-Agent": USER_AGENT, - 'x-rpc-device_id': get_device_id(USER_AGENT), + "x-rpc-device_id": get_device_id(USER_AGENT), "Accept": "application/json, text/plain, */*", "x-rpc-device_name": "Microsoft Edge 103.0.1264.62", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "x-rpc-client_type": "4", - "sec-ch-ua-platform": "\"macOS\"", + "sec-ch-ua-platform": '"macOS"', "Origin": "https://user.mihoyo.com", "Sec-Fetch-Site": "same-site", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Referer": "https://user.mihoyo.com/", "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", } BBS_HEADERS = { "Host": "api-takumi.mihoyo.com", @@ -213,7 +207,7 @@ class SignIn: "Accept": "application/json, text/plain, */*", "User-Agent": USER_AGENT, "Referer": "https://bbs.mihoyo.com/", - "Accept-Language": "zh-CN,zh-Hans;q=0.9" + "Accept-Language": "zh-CN,zh-Hans;q=0.9", } def __init__(self, phone: int): @@ -251,7 +245,7 @@ class SignIn: data = await self.client.post( self.LOGIN_URL, data={"mobile": str(self.phone), "mobile_captcha": str(captcha), "source": "user.mihoyo.com"}, - headers=self.HEADERS + headers=self.HEADERS, ) res_json = data.json() if self.check_error(res_json): @@ -265,8 +259,7 @@ class SignIn: async def get_s_token(self): data = await self.client.get( - self.S_TOKEN_URL.format(self.cookie["login_ticket"], self.uid), - headers={"User-Agent": self.USER_AGENT} + self.S_TOKEN_URL.format(self.cookie["login_ticket"], self.uid), headers={"User-Agent": self.USER_AGENT} ) res_json = data.json() res_data = res_json.get("data", {}).get("list", []) @@ -283,8 +276,8 @@ class SignIn: "mobile": str(self.phone), "captcha": str(captcha), "action_type": "login", - "token_type": 6 - } + "token_type": 6, + }, ) res_json = data.json() if self.check_error(res_json): diff --git a/modules/apihelper/request/hoyorequest.py b/modules/apihelper/request/hoyorequest.py index 17125f10..d0178ada 100644 --- a/modules/apihelper/request/hoyorequest.py +++ b/modules/apihelper/request/hoyorequest.py @@ -8,9 +8,9 @@ from modules.apihelper.typedefs import POST_DATA, JSON_DATA class HOYORequest(HTTPXRequest): - - async def get(self, url: str, *args, de_json: bool = True, re_json_data: bool = False, **kwargs) \ - -> Union[POST_DATA, JSON_DATA, bytes]: + async def get( + self, url: str, *args, de_json: bool = True, re_json_data: bool = False, **kwargs + ) -> Union[POST_DATA, JSON_DATA, bytes]: try: response = await self._client.get(url=url, *args, **kwargs) except httpx.TimeoutException as err: diff --git a/modules/apihelper/request/httpxrequest.py b/modules/apihelper/request/httpxrequest.py index e61a2a74..052dc5c8 100644 --- a/modules/apihelper/request/httpxrequest.py +++ b/modules/apihelper/request/httpxrequest.py @@ -6,7 +6,6 @@ import httpx class HTTPXRequest(AbstractAsyncContextManager): - def __init__(self, *args, headers=None, **kwargs): self._client = httpx.AsyncClient(headers=headers, *args, **kwargs) @@ -18,8 +17,9 @@ class HTTPXRequest(AbstractAsyncContextManager): await self.shutdown() raise exc - async def __aexit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType]) -> None: + async def __aexit__( + self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType] + ) -> None: await self.initialize() diff --git a/modules/playercards/helpers.py b/modules/playercards/helpers.py index 3ee0622a..a4a1a31b 100644 --- a/modules/playercards/helpers.py +++ b/modules/playercards/helpers.py @@ -15,29 +15,33 @@ with open(_fix_skills_level_file, "r", encoding="utf-8") as f: class ArtifactStatsTheory: - def __init__(self, character_name: str): self.character_name = character_name fight_prop_rule_list = fight_prop_rule_data.get(self.character_name, []) self.main_prop = [FightProp(fight_prop_rule) for fight_prop_rule in fight_prop_rule_list] if not self.main_prop: - self.main_prop = [FightProp.FIGHT_PROP_CRITICAL, FightProp.FIGHT_PROP_CRITICAL_HURT, - FightProp.FIGHT_PROP_ATTACK_PERCENT] + self.main_prop = [ + FightProp.FIGHT_PROP_CRITICAL, + FightProp.FIGHT_PROP_CRITICAL_HURT, + FightProp.FIGHT_PROP_ATTACK_PERCENT, + ] # 修正要评分的数值词条 if FightProp.FIGHT_PROP_ATTACK_PERCENT in self.main_prop and FightProp.FIGHT_PROP_ATTACK not in self.main_prop: self.main_prop.append(FightProp.FIGHT_PROP_ATTACK) if FightProp.FIGHT_PROP_HP_PERCENT in self.main_prop and FightProp.FIGHT_PROP_HP not in self.main_prop: self.main_prop.append(FightProp.FIGHT_PROP_HP) - if FightProp.FIGHT_PROP_DEFENSE_PERCENT in self.main_prop and \ - FightProp.FIGHT_PROP_DEFENSE not in self.main_prop: + if ( + FightProp.FIGHT_PROP_DEFENSE_PERCENT in self.main_prop + and FightProp.FIGHT_PROP_DEFENSE not in self.main_prop + ): self.main_prop.append(FightProp.FIGHT_PROP_DEFENSE) def theory(self, sub_stats: EquipmentsStats) -> float: """圣遗物副词条评分 - Args: - sub_stats: 圣遗物对象 - Returns: - 返回得分 + Args: + sub_stats: 圣遗物对象 + Returns: + 返回得分 """ score: float = 0 if sub_stats.prop_id in map(lambda x: x.name, self.main_prop): diff --git a/modules/wiki/base.py b/modules/wiki/base.py index cf5dc36b..d05da0b9 100644 --- a/modules/wiki/base.py +++ b/modules/wiki/base.py @@ -16,7 +16,7 @@ from pydantic import ( ) from typing_extensions import Self -__all__ = ['Model', 'WikiModel', 'HONEY_HOST'] +__all__ = ["Model", "WikiModel", "HONEY_HOST"] HONEY_HOST = URL("https://genshin.honeyhunterworld.com/") @@ -39,13 +39,13 @@ class WikiModel(Model): # noinspection PyUnresolvedReferences """wiki所用到的基类 - Attributes: - id (:obj:`int`): ID - name (:obj:`str`): 名称 - rarity (:obj:`int`): 星级 + Attributes: + id (:obj:`int`): ID + name (:obj:`str`): 名称 + rarity (:obj:`int`): 星级 - _client (:class:`httpx.AsyncClient`): 发起 http 请求的 client - """ + _client (:class:`httpx.AsyncClient`): 发起 http 请求的 client + """ _client: ClassVar[AsyncClient] = AsyncClient() id: str @@ -107,7 +107,7 @@ class WikiModel(Model): 返回对应的 WikiModel """ response = await cls._client_get(url) - return await cls._parse_soup(BeautifulSoup(response.text, 'lxml')) + return await cls._parse_soup(BeautifulSoup(response.text, "lxml")) @classmethod async def get_by_id(cls, id_: str) -> Self: @@ -152,7 +152,7 @@ class WikiModel(Model): 返回能爬到的所有的 WikiModel 所组成的 List """ queue: Queue[Self] = Queue() # 存放 Model 的队列 - signal = Value('i', 0) # 一个用于异步任务同步的信号 + signal = Value("i", 0) # 一个用于异步任务同步的信号 async def task(u): # 包装的爬虫任务 @@ -196,18 +196,18 @@ class WikiModel(Model): """ urls = cls.scrape_urls() queue: Queue[Union[str, Tuple[str, URL]]] = Queue() # 存放 Model 的队列 - signal = Value('i', len(urls)) # 一个用于异步任务同步的信号,初始值为存放所需要爬取的页面数 + signal = Value("i", len(urls)) # 一个用于异步任务同步的信号,初始值为存放所需要爬取的页面数 async def task(page: URL): """包装的爬虫任务""" response = await cls._client_get(page) # 从页面中获取对应的 chaos data (未处理的json格式字符串) - chaos_data = re.findall(r'sortable_data\.push\((.*)\);\s*sortable_cur_page', response.text)[0] + chaos_data = re.findall(r"sortable_data\.push\((.*)\);\s*sortable_cur_page", response.text)[0] json_data = json.loads(chaos_data) # 转为 json for data in json_data: # 遍历 json - data_name = re.findall(r'>(.*)<', data[1])[0] # 获取 Model 的名称 + data_name = re.findall(r">(.*)<", data[1])[0] # 获取 Model 的名称 if with_url: # 如果需要返回对应的 url - data_url = HONEY_HOST.join(re.findall(r'\"(.*?)\"', data[0])[0]) + data_url = HONEY_HOST.join(re.findall(r"\"(.*?)\"", data[0])[0]) await queue.put((data_name, data_url)) else: await queue.put(data_name) diff --git a/modules/wiki/character.py b/modules/wiki/character.py index e380301b..e38dc008 100644 --- a/modules/wiki/character.py +++ b/modules/wiki/character.py @@ -15,6 +15,7 @@ class Birth(Model): day: 天 month: 月 """ + day: int month: int @@ -26,6 +27,7 @@ class CharacterAscension(Model): level: 等级突破材料 skill: 技能/天赋培养材料 """ + level: List[str] = [] skill: List[str] = [] @@ -42,6 +44,7 @@ class CharacterState(Model): CD: 暴击伤害 bonus: 突破属性 """ + level: str HP: int ATK: float @@ -97,23 +100,23 @@ class Character(WikiModel): return [HONEY_HOST.join("fam_chars/?lang=CHS")] @classmethod - async def _parse_soup(cls, soup: BeautifulSoup) -> 'Character': + async def _parse_soup(cls, soup: BeautifulSoup) -> "Character": """解析角色页""" - soup = soup.select('.wp-block-post-content')[0] - tables = soup.find_all('table') - table_rows = tables[0].find_all('tr') + soup = soup.select(".wp-block-post-content")[0] + tables = soup.find_all("table") + table_rows = tables[0].find_all("tr") def get_table_text(row_num: int) -> str: """一个快捷函数,用于返回表格对应行的最后一个单元格中的文本""" - return table_rows[row_num].find_all('td')[-1].text.replace('\xa0', '') + return table_rows[row_num].find_all("td")[-1].text.replace("\xa0", "") - id_ = re.findall(r'img/(.*?_\d+)_.*', table_rows[0].find('img').attrs['src'])[0] + id_ = re.findall(r"img/(.*?_\d+)_.*", table_rows[0].find("img").attrs["src"])[0] name = get_table_text(0) - if name != '旅行者': # 如果角色名不是 旅行者 + if name != "旅行者": # 如果角色名不是 旅行者 title = get_table_text(1) occupation = get_table_text(2) association = Association.convert(get_table_text(3).lower().title()) - rarity = len(table_rows[4].find_all('img')) + rarity = len(table_rows[4].find_all("img")) weapon_type = WeaponType[get_table_text(5)] element = Element[get_table_text(6)] birth = Birth(day=int(get_table_text(7)), month=int(get_table_text(8))) @@ -123,11 +126,11 @@ class Character(WikiModel): en_cv = get_table_text(13) kr_cv = get_table_text(14) else: - name = '空' if id_.endswith('5') else '荧' + name = "空" if id_.endswith("5") else "荧" title = get_table_text(0) occupation = get_table_text(1) association = Association.convert(get_table_text(2).lower().title()) - rarity = len(table_rows[3].find_all('img')) + rarity = len(table_rows[3].find_all("img")) weapon_type = WeaponType[get_table_text(4)] element = Element[get_table_text(5)] birth = None @@ -139,30 +142,50 @@ class Character(WikiModel): description = get_table_text(-3) ascension = CharacterAscension( level=[ - target[0] for i in table_rows[-2].find_all('a') - if (target := re.findall(r'/(.*)/', i.attrs['href'])) # 过滤掉错误的材料(honey网页的bug) + target[0] + for i in table_rows[-2].find_all("a") + if (target := re.findall(r"/(.*)/", i.attrs["href"])) # 过滤掉错误的材料(honey网页的bug) ], - skill=[re.findall(r'/(.*)/', i.attrs['href'])[0] for i in table_rows[-1].find_all('a')] + skill=[re.findall(r"/(.*)/", i.attrs["href"])[0] for i in table_rows[-1].find_all("a")], ) stats = [] - for row in tables[2].find_all('tr')[1:]: - cells = row.find_all('td') + for row in tables[2].find_all("tr")[1:]: + cells = row.find_all("td") stats.append( CharacterState( - level=cells[0].text, HP=cells[1].text, ATK=cells[2].text, DEF=cells[3].text, - CR=cells[4].text, CD=cells[5].text, bonus=cells[6].text + level=cells[0].text, + HP=cells[1].text, + ATK=cells[2].text, + DEF=cells[3].text, + CR=cells[4].text, + CD=cells[5].text, + bonus=cells[6].text, ) ) return Character( - id=id_, name=name, title=title, occupation=occupation, association=association, weapon_type=weapon_type, - element=element, birth=birth, constellation=constellation, cn_cv=cn_cv, jp_cv=jp_cv, rarity=rarity, - en_cv=en_cv, kr_cv=kr_cv, description=description, ascension=ascension, stats=stats + id=id_, + name=name, + title=title, + occupation=occupation, + association=association, + weapon_type=weapon_type, + element=element, + birth=birth, + constellation=constellation, + cn_cv=cn_cv, + jp_cv=jp_cv, + rarity=rarity, + en_cv=en_cv, + kr_cv=kr_cv, + description=description, + ascension=ascension, + stats=stats, ) @classmethod async def get_url_by_name(cls, name: str) -> Optional[URL]: # 重写此函数的目的是处理主角名字的 ID - _map = {'荧': "playergirl_007", '空': "playerboy_005"} + _map = {"荧": "playergirl_007", "空": "playerboy_005"} if (id_ := _map.get(name)) is not None: return await cls.get_url_by_id(id_) return await super(Character, cls).get_url_by_name(name) @@ -170,8 +193,8 @@ class Character(WikiModel): @property def icon(self) -> CharacterIcon: return CharacterIcon( - icon=str(HONEY_HOST.join(f'/img/{self.id}_icon.webp')), - side=str(HONEY_HOST.join(f'/img/{self.id}_side_icon.webp')), - gacha=str(HONEY_HOST.join(f'/img/{self.id}_gacha_card.webp')), - splash=str(HONEY_HOST.join(f'/img/{self.id}_gacha_splash.webp')) + icon=str(HONEY_HOST.join(f"/img/{self.id}_icon.webp")), + side=str(HONEY_HOST.join(f"/img/{self.id}_side_icon.webp")), + gacha=str(HONEY_HOST.join(f"/img/{self.id}_gacha_card.webp")), + splash=str(HONEY_HOST.join(f"/img/{self.id}_gacha_splash.webp")), ) diff --git a/modules/wiki/material.py b/modules/wiki/material.py index 44294f0f..cfdbdcd8 100644 --- a/modules/wiki/material.py +++ b/modules/wiki/material.py @@ -6,21 +6,21 @@ from httpx import URL from modules.wiki.base import HONEY_HOST, WikiModel -__all__ = ['Material'] +__all__ = ["Material"] -WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] +WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] class Material(WikiModel): # noinspection PyUnresolvedReferences """武器、角色培养素材 - Attributes: - type: 类型 - weekdays: 每周开放的时间 - source: 获取方式 - description: 描述 - """ + Attributes: + type: 类型 + weekdays: 每周开放的时间 + source: 获取方式 + description: 描述 + """ type: str source: Optional[List[str]] = None weekdays: Optional[List[int]] = None @@ -28,8 +28,8 @@ class Material(WikiModel): @staticmethod def scrape_urls() -> List[URL]: - weapon = [HONEY_HOST.join(f'fam_wep_{i}/?lang=CHS') for i in ['primary', 'secondary', 'common']] - talent = [HONEY_HOST.join(f'fam_talent_{i}/?lang=CHS') for i in ['book', 'boss', 'common', 'reward']] + weapon = [HONEY_HOST.join(f"fam_wep_{i}/?lang=CHS") for i in ["primary", "secondary", "common"]] + talent = [HONEY_HOST.join(f"fam_talent_{i}/?lang=CHS") for i in ["book", "boss", "common", "reward"]] return weapon + talent @classmethod @@ -39,37 +39,37 @@ class Material(WikiModel): @classmethod async def _parse_soup(cls, soup: BeautifulSoup) -> "Material": """解析突破素材页""" - soup = soup.select('.wp-block-post-content')[0] - tables = soup.find_all('table') - table_rows = tables[0].find_all('tr') + soup = soup.select(".wp-block-post-content")[0] + tables = soup.find_all("table") + table_rows = tables[0].find_all("tr") def get_table_row(target: str): """一个便捷函数,用于返回对应表格头的对应行的最后一个单元格中的文本""" for row in table_rows: - if target in row.find('td').text: - return row.find_all('td')[-1] + if target in row.find("td").text: + return row.find_all("td")[-1] def get_table_text(row_num: int) -> str: """一个便捷函数,用于返回表格对应行的最后一个单元格中的文本""" - return table_rows[row_num].find_all('td')[-1].text.replace('\xa0', '') + return table_rows[row_num].find_all("td")[-1].text.replace("\xa0", "") - id_ = re.findall(r'/img/(.*?)\.webp', str(table_rows[0]))[0] + id_ = re.findall(r"/img/(.*?)\.webp", str(table_rows[0]))[0] name = get_table_text(0) - rarity = len(table_rows[3].find_all('img')) + rarity = len(table_rows[3].find_all("img")) type_ = get_table_text(1) - if (item_source := get_table_row('Item Source')) is not None: + if (item_source := get_table_row("Item Source")) is not None: item_source = list( # filter 在这里的作用是过滤掉为空的数据 - filter(lambda x: x, item_source.encode_contents().decode().split('
')) + filter(lambda x: x, item_source.encode_contents().decode().split("
")) ) - if (alter_source := get_table_row('Alternative Item')) is not None: + if (alter_source := get_table_row("Alternative Item")) is not None: alter_source = list( # filter 在这里的作用是过滤掉为空的数据 - filter(lambda x: x, alter_source.encode_contents().decode().split('
')) + filter(lambda x: x, alter_source.encode_contents().decode().split("
")) ) source = list(sorted(set((item_source or []) + (alter_source or [])))) - if (weekdays := get_table_row('Weekday')) is not None: - weekdays = [*(WEEKDAYS.index(weekdays.text.replace('\xa0', '').split(',')[0]) + 3 * i for i in range(2)), 6] + if (weekdays := get_table_row("Weekday")) is not None: + weekdays = [*(WEEKDAYS.index(weekdays.text.replace("\xa0", "").split(",")[0]) + 3 * i for i in range(2)), 6] description = get_table_text(-1) return Material( id=id_, name=name, rarity=rarity, type=type_, description=description, source=source, weekdays=weekdays @@ -77,4 +77,4 @@ class Material(WikiModel): @property def icon(self) -> str: - return str(HONEY_HOST.join(f'/img/{self.id}.webp')) + return str(HONEY_HOST.join(f"/img/{self.id}.webp")) diff --git a/modules/wiki/other.py b/modules/wiki/other.py index 41357fdc..4677f048 100644 --- a/modules/wiki/other.py +++ b/modules/wiki/other.py @@ -6,41 +6,43 @@ from typing_extensions import Self from modules.wiki.base import HONEY_HOST __all__ = [ - 'Element', - 'WeaponType', - 'AttributeType', - 'Association', + "Element", + "WeaponType", + "AttributeType", + "Association", ] class Element(Enum): """元素""" - Pyro = '火' - Hydro = '水' - Electro = '雷' - Cryo = '冰' - Dendro = '草' - Anemo = '风' - Geo = '岩' - Multi = '无' # 主角 + + Pyro = "火" + Hydro = "水" + Electro = "雷" + Cryo = "冰" + Dendro = "草" + Anemo = "风" + Geo = "岩" + Multi = "无" # 主角 _WEAPON_ICON_MAP = { - 'Sword': HONEY_HOST.join('img/s_23101.png'), - 'Claymore': HONEY_HOST.join('img/s_163101.png'), - 'Polearm': HONEY_HOST.join('img/s_233101.png'), - 'Catalyst': HONEY_HOST.join('img/s_43101.png'), - 'Bow': HONEY_HOST.join('img/s_213101.png'), + "Sword": HONEY_HOST.join("img/s_23101.png"), + "Claymore": HONEY_HOST.join("img/s_163101.png"), + "Polearm": HONEY_HOST.join("img/s_233101.png"), + "Catalyst": HONEY_HOST.join("img/s_43101.png"), + "Bow": HONEY_HOST.join("img/s_213101.png"), } class WeaponType(Enum): """武器类型""" - Sword = '单手剑' - Claymore = '双手剑' - Polearm = '长柄武器' - Catalyst = '法器' - Bow = '弓' + + Sword = "单手剑" + Claymore = "双手剑" + Polearm = "长柄武器" + Catalyst = "法器" + Bow = "弓" def icon_url(self) -> str: return str(_WEAPON_ICON_MAP.get(self.name)) @@ -49,17 +51,17 @@ class WeaponType(Enum): _ATTR_TYPE_MAP = { # 这个字典用于将 Honey 页面中遇到的 属性的缩写的字符 转为 AttributeType 的字符 # 例如 Honey 页面上写的 HP% 则对应 HP_p - "HP": ['Health'], - "HP_p": ['HP%', 'Health %'], - "ATK": ['Attack'], - "ATK_p": ['Atk%', 'Attack %'], - "DEF": ['Defense'], - "DEF_p": ['Def%', 'Defense %'], - "EM": ['Elemental Mastery'], - "ER": ['ER%', 'Energy Recharge %'], - "CR": ['CrR%', 'Critical Rate %', 'CritRate%'], - "CD": ['Crd%', 'Critical Damage %', 'CritDMG%'], - "PD": ['Phys%', 'Physical Damage %'], + "HP": ["Health"], + "HP_p": ["HP%", "Health %"], + "ATK": ["Attack"], + "ATK_p": ["Atk%", "Attack %"], + "DEF": ["Defense"], + "DEF_p": ["Def%", "Defense %"], + "EM": ["Elemental Mastery"], + "ER": ["ER%", "Energy Recharge %"], + "CR": ["CrR%", "Critical Rate %", "CritRate%"], + "CD": ["Crd%", "Critical Damage %", "CritDMG%"], + "PD": ["Phys%", "Physical Damage %"], "HB": [], "Pyro": [], "Hydro": [], @@ -73,6 +75,7 @@ _ATTR_TYPE_MAP = { class AttributeType(Enum): """属性枚举类。包含了武器和圣遗物的属性。""" + HP = "生命" HP_p = "生命%" ATK = "攻击力" @@ -85,13 +88,13 @@ class AttributeType(Enum): CD = "暴击伤害" PD = "物理伤害加成" HB = "治疗加成" - Pyro = '火元素伤害加成' - Hydro = '水元素伤害加成' - Electro = '雷元素伤害加成' - Cryo = '冰元素伤害加成' - Dendro = '草元素伤害加成' - Anemo = '风元素伤害加成' - Geo = '岩元素伤害加成' + Pyro = "火元素伤害加成" + Hydro = "水元素伤害加成" + Electro = "雷元素伤害加成" + Cryo = "冰元素伤害加成" + Dendro = "草元素伤害加成" + Anemo = "风元素伤害加成" + Geo = "岩元素伤害加成" @classmethod def convert(cls, string: str) -> Optional[Self]: @@ -102,23 +105,24 @@ class AttributeType(Enum): _ASSOCIATION_MAP = { - 'Other': ['Mainactor', 'Ranger', 'Fatui'], - 'Snezhnaya': [], - 'Sumeru': [], - 'Inazuma': [], - 'Liyue': [], - 'Mondstadt': [], + "Other": ["Mainactor", "Ranger", "Fatui"], + "Snezhnaya": [], + "Sumeru": [], + "Inazuma": [], + "Liyue": [], + "Mondstadt": [], } class Association(Enum): """角色所属地区""" - Other = '其它' - Snezhnaya = '至冬' - Sumeru = '须弥' - Inazuma = '稻妻' - Liyue = '璃月' - Mondstadt = '蒙德' + + Other = "其它" + Snezhnaya = "至冬" + Sumeru = "须弥" + Inazuma = "稻妻" + Liyue = "璃月" + Mondstadt = "蒙德" @classmethod def convert(cls, string: str) -> Optional[Self]: diff --git a/modules/wiki/weapon.py b/modules/wiki/weapon.py index ff4afa38..65d6feac 100644 --- a/modules/wiki/weapon.py +++ b/modules/wiki/weapon.py @@ -8,11 +8,12 @@ from httpx import URL from modules.wiki.base import Model, HONEY_HOST, WikiModel from modules.wiki.other import AttributeType, WeaponType -__all__ = ['Weapon', 'WeaponAffix', 'WeaponAttribute'] +__all__ = ["Weapon", "WeaponAffix", "WeaponAttribute"] class WeaponAttribute(Model): """武器词条""" + type: AttributeType value: str @@ -25,6 +26,7 @@ class WeaponAffix(Model): description: 技能描述 """ + name: str description: List[str] @@ -69,38 +71,36 @@ class Weapon(WikiModel): return [HONEY_HOST.join(f"fam_{i.lower()}/?lang=CHS") for i in WeaponType.__members__] @classmethod - async def _parse_soup(cls, soup: BeautifulSoup) -> 'Weapon': + async def _parse_soup(cls, soup: BeautifulSoup) -> "Weapon": """解析武器页""" - soup = soup.select('.wp-block-post-content')[0] - tables = soup.find_all('table') - table_rows = tables[0].find_all('tr') + soup = soup.select(".wp-block-post-content")[0] + tables = soup.find_all("table") + table_rows = tables[0].find_all("tr") def get_table_text(row_num: int) -> str: """一个快捷函数,用于返回表格对应行的最后一个单元格中的文本""" - return table_rows[row_num].find_all('td')[-1].text.replace('\xa0', '') + return table_rows[row_num].find_all("td")[-1].text.replace("\xa0", "") def find_table(select: str): """一个快捷函数,用于寻找对应表格头的表格""" - return list(filter(lambda x: select in ' '.join(x.attrs['class']), tables)) + return list(filter(lambda x: select in " ".join(x.attrs["class"]), tables)) - id_ = re.findall(r'/img/(.*?)_gacha', str(table_rows[0]))[0] - weapon_type = WeaponType[get_table_text(1).split(',')[-1].strip()] + id_ = re.findall(r"/img/(.*?)_gacha", str(table_rows[0]))[0] + weapon_type = WeaponType[get_table_text(1).split(",")[-1].strip()] name = get_table_text(0) - rarity = len(table_rows[2].find_all('img')) + rarity = len(table_rows[2].find_all("img")) attack = float(get_table_text(4)) - ascension = [re.findall(r'/(.*)/', tag.attrs['href'])[0] for tag in table_rows[-1].find_all('a')] + ascension = [re.findall(r"/(.*)/", tag.attrs["href"])[0] for tag in table_rows[-1].find_all("a")] if rarity > 2: # 如果是 3 星及其以上的武器 attribute = WeaponAttribute( - type=AttributeType.convert( - tables[2].find('thead').find('tr').find_all('td')[2].text.split(' ')[1] - ), - value=get_table_text(6) + type=AttributeType.convert(tables[2].find("thead").find("tr").find_all("td")[2].text.split(" ")[1]), + value=get_table_text(6), + ) + affix = WeaponAffix( + name=get_table_text(7), description=[i.find_all("td")[1].text for i in tables[3].find_all("tr")[1:]] ) - affix = WeaponAffix(name=get_table_text(7), description=[ - i.find_all('td')[1].text for i in tables[3].find_all('tr')[1:] - ]) description = get_table_text(-1) if len(tables) < 11 else get_table_text(9) - if story_table := find_table('quotes'): + if story_table := find_table("quotes"): story = story_table[0].text.strip() else: story = None @@ -109,15 +109,24 @@ class Weapon(WikiModel): description = get_table_text(5) story = tables[-1].text.strip() stats = [] - for row in tables[2].find_all('tr')[1:]: - cells = row.find_all('td') + for row in tables[2].find_all("tr")[1:]: + cells = row.find_all("td") if rarity > 2: stats.append(WeaponState(level=cells[0].text, ATK=cells[1].text, bonus=cells[2].text)) else: stats.append(WeaponState(level=cells[0].text, ATK=cells[1].text)) return Weapon( - id=id_, name=name, rarity=rarity, attack=attack, attribute=attribute, affix=affix, weapon_type=weapon_type, - story=story, stats=stats, description=description, ascension=ascension + id=id_, + name=name, + rarity=rarity, + attack=attack, + attribute=attribute, + affix=affix, + weapon_type=weapon_type, + story=story, + stats=stats, + description=description, + ascension=ascension, ) @classmethod @@ -125,16 +134,14 @@ class Weapon(WikiModel): # 重写此函数的目的是名字去重,例如单手剑页面中有三个 “「一心传」名刀” name_list = [i async for i in cls._name_list_generator(with_url=with_url)] if with_url: - return [ - (i[0], list(i[1])[0][1]) for i in itertools.groupby(name_list, lambda x: x[0]) - ] + return [(i[0], list(i[1])[0][1]) for i in itertools.groupby(name_list, lambda x: x[0])] else: return [i[0] for i in itertools.groupby(name_list, lambda x: x)] @property def icon(self) -> WeaponIcon: return WeaponIcon( - icon=str(HONEY_HOST.join(f'/img/{self.id}.webp')), - awakened=str(HONEY_HOST.join(f'/img/{self.id}_awaken_icon.webp')), - gacha=str(HONEY_HOST.join(f'/img/{self.id}_gacha_icon.webp')), + icon=str(HONEY_HOST.join(f"/img/{self.id}.webp")), + awakened=str(HONEY_HOST.join(f"/img/{self.id}_awaken_icon.webp")), + gacha=str(HONEY_HOST.join(f"/img/{self.id}_gacha_icon.webp")), ) diff --git a/plugins/genshin/abyss.py b/plugins/genshin/abyss.py index cbbf7671..3963ab55 100644 --- a/plugins/genshin/abyss.py +++ b/plugins/genshin/abyss.py @@ -21,11 +21,13 @@ from utils.log import logger class AbyssUnlocked(Exception): """根本没动""" + pass class NoMostKills(Exception): """挑战了但是数据没刷新""" + pass @@ -33,11 +35,11 @@ class Abyss(Plugin, BasePlugin): """深渊数据查询""" def __init__( - self, - user_service: UserService = None, - cookies_service: CookiesService = None, - template_service: TemplateService = None, - assets_service: AssetsService = None + self, + user_service: UserService = None, + cookies_service: CookiesService = None, + template_service: TemplateService = None, + assets_service: AssetsService = None, ): self.template_service = template_service self.cookies_service = cookies_service @@ -77,20 +79,20 @@ class Abyss(Plugin, BasePlugin): }, "strongest_strike": { "icon": await self.assets_service.avatar(ranks.strongest_strike[0].id).side(), - "value": ranks.strongest_strike[0].value + "value": ranks.strongest_strike[0].value, }, "most_damage_taken": { "icon": await self.assets_service.avatar(ranks.most_damage_taken[0].id).side(), - "value": ranks.most_damage_taken[0].value + "value": ranks.most_damage_taken[0].value, }, "most_bursts_used": { "icon": await self.assets_service.avatar(ranks.most_bursts_used[0].id).side(), - "value": ranks.most_bursts_used[0].value + "value": ranks.most_bursts_used[0].value, }, "most_skills_used": { "icon": await self.assets_service.avatar(ranks.most_skills_used[0].id).side(), - "value": ranks.most_skills_used[0].value - } + "value": ranks.most_skills_used[0].value, + }, } # most_kills most_played_list = ranks.most_played @@ -98,7 +100,7 @@ class Abyss(Plugin, BasePlugin): temp = { "icon": await self.assets_service.avatar(most_played.id).icon(), "value": most_played.value, - "background": self._get_role_star_bg(most_played.rarity) + "background": self._get_role_star_bg(most_played.rarity), } abyss_data["most_played_list"].append(temp) return abyss_data @@ -132,7 +134,7 @@ class Abyss(Plugin, BasePlugin): await message.reply_text("本次深渊旅行者还没挑战呢,咕咕咕~~~") return await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) - png_data = await self.template_service.render('genshin/abyss', "abyss.html", abyss_data, - {"width": 865, "height": 504}, full_page=False) - await message.reply_photo(png_data, filename=f"abyss_{user.id}.png", - allow_sending_without_reply=True) + png_data = await self.template_service.render( + "genshin/abyss", "abyss.html", abyss_data, {"width": 865, "height": 504}, full_page=False + ) + await message.reply_photo(png_data, filename=f"abyss_{user.id}.png", allow_sending_without_reply=True) diff --git a/plugins/genshin/artifact_rate.py b/plugins/genshin/artifact_rate.py index 77d63e02..37c4360d 100644 --- a/plugins/genshin/artifact_rate.py +++ b/plugins/genshin/artifact_rate.py @@ -18,15 +18,17 @@ COMMAND_RESULT = 1 class ArtifactRate(Plugin.Conversation, BasePlugin.Conversation): """圣遗物评分""" - STAR_KEYBOARD = [[ - InlineKeyboardButton( - f"{i}", callback_data=f"artifact_ocr_rate_data|star|{i}") for i in range(1, 6) - ]] + STAR_KEYBOARD = [ + [InlineKeyboardButton(f"{i}", callback_data=f"artifact_ocr_rate_data|star|{i}") for i in range(1, 6)] + ] - LEVEL_KEYBOARD = [[ - InlineKeyboardButton( - f"{i * 5 + j}", callback_data=f"artifact_ocr_rate_data|level|{i * 5 + j}") for j in range(1, 6) - ] for i in range(0, 4)] + LEVEL_KEYBOARD = [ + [ + InlineKeyboardButton(f"{i * 5 + j}", callback_data=f"artifact_ocr_rate_data|level|{i * 5 + j}") + for j in range(1, 6) + ] + for i in range(0, 4) + ] def __init__(self): self.artifact_rate = ArtifactOcrRate() @@ -39,19 +41,21 @@ class ArtifactRate(Plugin.Conversation, BasePlugin.Conversation): return artifact_attr.get("message", "API请求错误") return "API请求错误" rate_result = rate_result_req.json() - return "*圣遗物评分结果*\n" \ - f"主属性:{escape_markdown(artifact_attr['main_item']['name'], version=2)}\n" \ - f"{escape_markdown(get_format_sub_item(artifact_attr), version=2)}" \ - f'`--------------------`\n' \ - f"总分:{escape_markdown(rate_result['total_percent'], version=2)}\n" \ - f"主词条:{escape_markdown(rate_result['main_percent'], version=2)}\n" \ - f"副词条:{escape_markdown(rate_result['sub_percent'], version=2)}\n" \ - f'`--------------------`\n' \ - f"{escape_markdown(get_comment(rate_result['total_percent']), version=2)}\n" \ - "_评分、识图均来自 genshin\\.pub_" + return ( + "*圣遗物评分结果*\n" + f"主属性:{escape_markdown(artifact_attr['main_item']['name'], version=2)}\n" + f"{escape_markdown(get_format_sub_item(artifact_attr), version=2)}" + f"`--------------------`\n" + f"总分:{escape_markdown(rate_result['total_percent'], version=2)}\n" + f"主词条:{escape_markdown(rate_result['main_percent'], version=2)}\n" + f"副词条:{escape_markdown(rate_result['sub_percent'], version=2)}\n" + f"`--------------------`\n" + f"{escape_markdown(get_comment(rate_result['total_percent']), version=2)}\n" + "_评分、识图均来自 genshin\\.pub_" + ) @conversation.entry_point - @handler.command(command='artifact_rate', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="artifact_rate", filters=filters.ChatType.PRIVATE, block=True) @handler.message(filters=filters.Regex(r"^圣遗物评分(.*)"), block=True) @handler.message(filters=filters.CaptionRegex(r"^圣遗物评分(.*)"), block=True) @error_callable @@ -95,15 +99,12 @@ class ArtifactRate(Plugin.Conversation, BasePlugin.Conversation): artifact_attr = artifact_attr_req.json() context.user_data["artifact_attr"] = artifact_attr if artifact_attr.get("star") is None: - await message.reply_text("无法识别圣遗物星级,请选择圣遗物星级", - reply_markup=InlineKeyboardMarkup(self.STAR_KEYBOARD)) + await message.reply_text("无法识别圣遗物星级,请选择圣遗物星级", reply_markup=InlineKeyboardMarkup(self.STAR_KEYBOARD)) return COMMAND_RESULT if artifact_attr.get("level") is None: - await message.reply_text("无法识别圣遗物等级,请选择圣遗物等级", - reply_markup=InlineKeyboardMarkup(self.LEVEL_KEYBOARD)) + await message.reply_text("无法识别圣遗物等级,请选择圣遗物等级", reply_markup=InlineKeyboardMarkup(self.LEVEL_KEYBOARD)) return COMMAND_RESULT - reply_message = await message.reply_text("识图成功!\n" - "正在评分中...") + reply_message = await message.reply_text("识图成功!\n" "正在评分中...") rate_text = await self.get_rate(artifact_attr) await reply_message.edit_text(rate_text, parse_mode=ParseMode.MARKDOWN_V2) return ConversationHandler.END @@ -138,12 +139,10 @@ class ArtifactRate(Plugin.Conversation, BasePlugin.Conversation): await query.edit_message_text("数据错误") return ConversationHandler.END if artifact_attr.get("level") is None: - await query.edit_message_text("无法识别圣遗物等级,请选择圣遗物等级", - reply_markup=InlineKeyboardMarkup(self.LEVEL_KEYBOARD)) + await query.edit_message_text("无法识别圣遗物等级,请选择圣遗物等级", reply_markup=InlineKeyboardMarkup(self.LEVEL_KEYBOARD)) return COMMAND_RESULT if artifact_attr.get("star") is None: - await query.edit_message_text("无法识别圣遗物星级,请选择圣遗物星级", - reply_markup=InlineKeyboardMarkup(self.STAR_KEYBOARD)) + await query.edit_message_text("无法识别圣遗物星级,请选择圣遗物星级", reply_markup=InlineKeyboardMarkup(self.STAR_KEYBOARD)) return COMMAND_RESULT await query.edit_message_text("正在评分中...") rate_text = await self.get_rate(artifact_attr) diff --git a/plugins/genshin/cookies.py b/plugins/genshin/cookies.py index 4bf27ddc..97ecc9ac 100644 --- a/plugins/genshin/cookies.py +++ b/plugins/genshin/cookies.py @@ -43,7 +43,7 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): self.user_service = user_service @conversation.entry_point - @handler.command(command='setcookies', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="setcookies", filters=filters.ChatType.PRIVATE, block=True) @restricts() @error_callable async def command_start(self, update: Update, context: CallbackContext) -> int: @@ -56,12 +56,12 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): context.chat_data["add_user_command_data"] = cookies_command_data text = f'你好 {user.mention_markdown_v2()} {escape_markdown("!请选择要绑定的服务器!或回复退出取消操作")}' - reply_keyboard = [['米游社', 'HoYoLab'], ["退出"]] + reply_keyboard = [["米游社", "HoYoLab"], ["退出"]] await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) return CHECK_SERVER @conversation.entry_point - @handler.command(command='mlogin', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="mlogin", filters=filters.ChatType.PRIVATE, block=True) @error_callable async def choose_method(self, update: Update, context: CallbackContext) -> int: user = update.effective_user @@ -112,21 +112,25 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): add_user_command_data.user = user_info add_user_command_data.region = region await message.reply_text(f"请输入{bbs_name}的Cookies!或回复退出取消操作", reply_markup=ReplyKeyboardRemove()) - javascript = "javascript:(()=>{_=(n)=>{for(i in(r=document.cookie.split(';'))){var a=r[i].split('=');if(a[" \ - "0].trim()==n)return a[1]}};c=_('account_id')||alert('无效的Cookie,请重新登录!');c&&confirm(" \ - "'将Cookie复制到剪贴板?')&©(document.cookie)})(); " + javascript = ( + "javascript:(()=>{_=(n)=>{for(i in(r=document.cookie.split(';'))){var a=r[i].split('=');if(a[" + "0].trim()==n)return a[1]}};c=_('account_id')||alert('无效的Cookie,请重新登录!');c&&confirm(" + "'将Cookie复制到剪贴板?')&©(document.cookie)})(); " + ) javascript_android = "javascript:(()=>{prompt('',document.cookie)})();" - help_message = f"*关于如何获取Cookies*\n\n" \ - f"PC:\n" \ - f"[1、打开{bbs_name}并登录]({bbs_url})\n" \ - f"2、按F12打开开发者工具\n" \ - f"3、{escape_markdown('将开发者工具切换至控制台(Console)页签', version=2)}\n" \ - f"4、复制下方的代码,并将其粘贴在控制台中,按下回车\n" \ - f"`{escape_markdown(javascript, version=2, entity_type='code')}`\n\n" \ - f"Android:\n" \ - f"[1、通过 Via 浏览器打开{bbs_name}并登录]({bbs_url})\n" \ - f"2、复制下方的代码,并将其粘贴在地址栏中,点击右侧箭头\n" \ - f"`{escape_markdown(javascript_android, version=2, entity_type='code')}`" + help_message = ( + f"*关于如何获取Cookies*\n\n" + f"PC:\n" + f"[1、打开{bbs_name}并登录]({bbs_url})\n" + f"2、按F12打开开发者工具\n" + f"3、{escape_markdown('将开发者工具切换至控制台(Console)页签', version=2)}\n" + f"4、复制下方的代码,并将其粘贴在控制台中,按下回车\n" + f"`{escape_markdown(javascript, version=2, entity_type='code')}`\n\n" + f"Android:\n" + f"[1、通过 Via 浏览器打开{bbs_name}并登录]({bbs_url})\n" + f"2、复制下方的代码,并将其粘贴在地址栏中,点击右侧箭头\n" + f"`{escape_markdown(javascript_android, version=2, entity_type='code')}`" + ) await message.reply_markdown_v2(help_message, disable_web_page_preview=True) return INPUT_COOKIES @@ -149,8 +153,9 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): return CHECK_PHONE add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data") add_user_command_data.phone = phone - await message.reply_text("请打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码," - "然后将收到的验证码发送给我(请不要在网页上进行登录)") + await message.reply_text( + "请打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码," "然后将收到的验证码发送给我(请不要在网页上进行登录)" + ) return CHECK_CAPTCHA @conversation.state(state=CHECK_CAPTCHA) @@ -175,8 +180,7 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): try: success = await client.login(captcha) if not success: - await message.reply_text( - "登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!") + await message.reply_text("登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!") return ConversationHandler.END await client.get_s_token() except Exception: @@ -184,16 +188,15 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): return ConversationHandler.END add_user_command_data.sign_in_client = client await message.reply_text( - "请再次打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码(需要等待一分钟)," - "然后将收到的验证码发送给我(请不要在网页上进行登录)") + "请再次打开 https://user.mihoyo.com/#/login/captcha ,输入手机号并获取验证码(需要等待一分钟)," "然后将收到的验证码发送给我(请不要在网页上进行登录)" + ) return CHECK_CAPTCHA else: client = add_user_command_data.sign_in_client try: success = await client.get_token(captcha) if not success: - await message.reply_text( - "登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!") + await message.reply_text("登录失败:可能是验证码错误,注意不要在登录页面使用掉验证码,如果验证码已经使用,请重新获取验证码!") return ConversationHandler.END except Exception: await message.reply_text("登录失败:米游社返回了错误的数据,请稍后再试!") @@ -249,21 +252,24 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): await message.reply_text("Cookies已经过期,请检查是否正确", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END except GenshinException as exc: - await message.reply_text(f"获取账号信息发生错误,错误信息为 {str(exc)},请检查Cookie或者账号是否正常", - reply_markup=ReplyKeyboardRemove()) + await message.reply_text( + f"获取账号信息发生错误,错误信息为 {str(exc)},请检查Cookie或者账号是否正常", reply_markup=ReplyKeyboardRemove() + ) return ConversationHandler.END except (AttributeError, ValueError): await message.reply_text("Cookies错误,请检查是否正确", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END add_user_command_data.game_uid = user_info.uid - reply_keyboard = [['确认', '退出']] + reply_keyboard = [["确认", "退出"]] await message.reply_text("获取角色基础信息成功,请检查是否正确!") logger.info(f"用户 {user.full_name}[{user.id}] 获取账号 {user_info.nickname}[{user_info.uid}] 信息成功") - text = f"*角色信息*\n" \ - f"角色名称:{escape_markdown(user_info.nickname, version=2)}\n" \ - f"角色等级:{user_info.level}\n" \ - f"UID:`{user_info.uid}`\n" \ - f"服务器名称:`{user_info.server_name}`\n" + text = ( + f"*角色信息*\n" + f"角色名称:{escape_markdown(user_info.nickname, version=2)}\n" + f"角色等级:{user_info.level}\n" + f"UID:`{user_info.uid}`\n" + f"服务器名称:`{user_info.server_name}`\n" + ) await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) return COMMAND_RESULT @@ -280,11 +286,15 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): elif message.text == "确认": if add_user_command_data.user is None: if add_user_command_data.region == RegionEnum.HYPERION: - user_db = User(user_id=user.id, yuanshen_uid=add_user_command_data.game_uid, - region=add_user_command_data.region) + user_db = User( + user_id=user.id, + yuanshen_uid=add_user_command_data.game_uid, + region=add_user_command_data.region, + ) elif add_user_command_data.region == RegionEnum.HOYOLAB: - user_db = User(user_id=user.id, genshin_uid=add_user_command_data.game_uid, - region=add_user_command_data.region) + user_db = User( + user_id=user.id, genshin_uid=add_user_command_data.game_uid, region=add_user_command_data.region + ) else: await message.reply_text("数据错误") return ConversationHandler.END @@ -301,11 +311,13 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation): return ConversationHandler.END await self.user_service.update_user(user_db) if add_user_command_data.cookies_database_data is None: - await self.cookies_service.add_cookies(user.id, add_user_command_data.cookies, - add_user_command_data.region) + await self.cookies_service.add_cookies( + user.id, add_user_command_data.cookies, add_user_command_data.region + ) else: - await self.cookies_service.update_cookies(user.id, add_user_command_data.cookies, - add_user_command_data.region) + await self.cookies_service.update_cookies( + user.id, add_user_command_data.cookies, add_user_command_data.region + ) logger.info(f"用户 {user.full_name}[{user.id}] 绑定账号成功") await message.reply_text("保存成功", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END diff --git a/plugins/genshin/daily/material.py b/plugins/genshin/daily/material.py index 6dc0eb05..31c2544e 100644 --- a/plugins/genshin/daily/material.py +++ b/plugins/genshin/daily/material.py @@ -39,14 +39,14 @@ from utils.log import logger INTERVAL = 1 DATA_TYPE = Dict[str, List[List[str]]] -DATA_FILE_PATH = Path(__file__).joinpath('../daily.json').resolve() -DOMAINS = ['忘却之峡', '太山府', '菫色之庭', '昏识塔', '塞西莉亚苗圃', '震雷连山密宫', '砂流之庭', '有顶塔'] -DOMAIN_AREA_MAP = dict(zip(DOMAINS, ['蒙德', '璃月', '稻妻', '须弥'] * 2)) +DATA_FILE_PATH = Path(__file__).joinpath("../daily.json").resolve() +DOMAINS = ["忘却之峡", "太山府", "菫色之庭", "昏识塔", "塞西莉亚苗圃", "震雷连山密宫", "砂流之庭", "有顶塔"] +DOMAIN_AREA_MAP = dict(zip(DOMAINS, ["蒙德", "璃月", "稻妻", "须弥"] * 2)) -WEEK_MAP = ['一', '二', '三', '四', '五', '六', '日'] +WEEK_MAP = ["一", "二", "三", "四", "五", "六", "日"] -def sort_item(items: List['ItemData']) -> Iterable['ItemData']: +def sort_item(items: List["ItemData"]) -> Iterable["ItemData"]: """对武器和角色进行排序 排序规则:持有(星级 > 等级 > 命座/精炼) > 未持有(星级 > 等级 > 命座/精炼) @@ -60,14 +60,17 @@ def sort_item(items: List['ItemData']) -> Iterable['ItemData']: ArkoWrapper(x[1]) .sort(lambda y: y.rarity, reverse=True) .groupby(lambda y: y.rarity) # 根据星级分组并排序 - .map(lambda y: ( - ArkoWrapper(y[1]) - .sort(lambda z: z.refinement or z.constellation or -1, reverse=True) - .groupby(lambda z: z.refinement or z.constellation or -1) # 根据命座/精炼进行分组并排序 - .map(lambda i: ArkoWrapper(i[1]).sort(lambda j: j.id)) - )) + .map( + lambda y: ( + ArkoWrapper(y[1]) + .sort(lambda z: z.refinement or z.constellation or -1, reverse=True) + .groupby(lambda z: z.refinement or z.constellation or -1) # 根据命座/精炼进行分组并排序 + .map(lambda i: ArkoWrapper(i[1]).sort(lambda j: j.id)) + ) + ) ) - ).flat(3) + ) + .flat(3) ) @@ -87,7 +90,7 @@ def get_material_serial_name(names: Iterable[str]) -> str: if sub_string in ArkoWrapper(all_substrings(name_b)): result.append(sub_string) result = ArkoWrapper(result).sort(len, reverse=True)[0] - chars = {'的': 0, '之': 0} + chars = {"的": 0, "之": 0} for char, k in chars.items(): result = result.split(char)[k] return result @@ -95,6 +98,7 @@ def get_material_serial_name(names: Iterable[str]) -> str: class DailyMaterial(Plugin, BasePlugin): """每日素材表""" + data: DATA_TYPE locks: Tuple[Lock] = (Lock(), Lock()) @@ -122,39 +126,47 @@ class DailyMaterial(Plugin, BasePlugin): async def _get_data_from_user(self, user: User) -> Tuple[Optional[Client], Dict[str, List[Any]]]: """获取已经绑定的账号的角色、武器信息""" client = None - user_data = {'avatar': [], 'weapon': []} + user_data = {"avatar": [], "weapon": []} try: logger.debug("尝试获取已绑定的原神账号") client = await get_genshin_client(user.id) logger.debug(f"获取账号数据成功: UID={client.uid}") characters = await client.get_genshin_characters(client.uid) for character in characters: - if character.name == '旅行者': # 跳过主角 + if character.name == "旅行者": # 跳过主角 continue - cid = AVATAR_DATA[str(character.id)]['id'] + cid = AVATAR_DATA[str(character.id)]["id"] weapon = character.weapon - user_data['avatar'].append( + user_data["avatar"].append( ItemData( - id=cid, name=character.name, rarity=character.rarity, level=character.level, + id=cid, + name=character.name, + rarity=character.rarity, + level=character.level, constellation=character.constellation, - icon=(await self.assets_service.avatar(cid).icon()).as_uri() + icon=(await self.assets_service.avatar(cid).icon()).as_uri(), ) ) - user_data['weapon'].append( + user_data["weapon"].append( ItemData( - id=str(weapon.id), name=weapon.name, level=weapon.level, rarity=weapon.rarity, + id=str(weapon.id), + name=weapon.name, + level=weapon.level, + rarity=weapon.rarity, refinement=weapon.refinement, - icon=(await getattr( # 判定武器的突破次数是否大于 2 ;若是, 则将图标替换为 awakened (觉醒) 的图标 - self.assets_service.weapon(weapon.id), 'icon' if weapon.ascension < 2 else 'awaken' - )()).as_uri(), - c_path=(await self.assets_service.avatar(cid).side()).as_uri() + icon=( + await getattr( # 判定武器的突破次数是否大于 2 ;若是, 则将图标替换为 awakened (觉醒) 的图标 + self.assets_service.weapon(weapon.id), "icon" if weapon.ascension < 2 else "awaken" + )() + ).as_uri(), + c_path=(await self.assets_service.avatar(cid).side()).as_uri(), ) ) except (UserNotFoundError, CookiesNotFoundError): logger.info(f"未查询到用户({user.full_name} {user.id}) 所绑定的账号信息") return client, user_data - @handler.command('daily_material', block=False) + @handler.command("daily_material", block=False) @restricts(restricts_time_of_groups=20, without_overlapping=True) @error_callable async def daily_material(self, update: Update, context: CallbackContext): @@ -171,15 +183,13 @@ class DailyMaterial(Plugin, BasePlugin): weekday = now.weekday() - (1 if now.hour < 4 else 0) weekday = 6 if weekday < 0 else weekday time = now.strftime("%m-%d %H:%M") + " 星期" + WEEK_MAP[weekday] - full = bool(args and args[-1] == 'full') # 判定最后一个参数是不是 full + full = bool(args and args[-1] == "full") # 判定最后一个参数是不是 full - logger.info( - f"用户 {user.full_name}[{user.id}] 每日素材命令请求 || 参数 weekday=\"{WEEK_MAP[weekday]}\" full={full}") + logger.info(f'用户 {user.full_name}[{user.id}] 每日素材命令请求 || 参数 weekday="{WEEK_MAP[weekday]}" full={full}') if weekday == 6: await update.message.reply_text( - ("今天" if title == '今日' else '这天') + "是星期天, 全部素材都可以刷哦~", - parse_mode=ParseMode.HTML + ("今天" if title == "今日" else "这天") + "是星期天, 全部素材都可以刷哦~", parse_mode=ParseMode.HTML ) return @@ -196,26 +206,26 @@ class DailyMaterial(Plugin, BasePlugin): await update.message.reply_chat_action(ChatAction.TYPING) # 获取已经缓存的秘境素材信息 - local_data = {'avatar': [], 'weapon': []} + local_data = {"avatar": [], "weapon": []} if not self.data: # 若没有缓存每日素材表的数据 logger.info("正在获取每日素材缓存") self.data = await self._refresh_data() for domain, sche in self.data.items(): area = DOMAIN_AREA_MAP[domain] # 获取秘境所在的区域 - type_ = 'avatar' if DOMAINS.index(domain) < 4 else 'weapon' # 获取秘境的培养素材的类型:是天赋书还是武器突破材料 + type_ = "avatar" if DOMAINS.index(domain) < 4 else "weapon" # 获取秘境的培养素材的类型:是天赋书还是武器突破材料 # 将读取到的数据存入 local_data 中 - local_data[type_].append({'name': area, 'materials': sche[weekday][0], 'items': sche[weekday][1]}) + local_data[type_].append({"name": area, "materials": sche[weekday][0], "items": sche[weekday][1]}) # 尝试获取用户已绑定的原神账号信息 client, user_data = await self._get_data_from_user(user) await update.message.reply_chat_action(ChatAction.TYPING) render_data = RenderData(title=title, time=time, uid=client.uid if client else client) - for type_ in ['avatar', 'weapon']: + for type_ in ["avatar", "weapon"]: areas = [] for area_data in local_data[type_]: # 遍历每个区域的信息:蒙德、璃月、稻妻、须弥 items = [] - for id_ in area_data['items']: # 遍历所有该区域下,当天(weekday)可以培养的角色、武器 + for id_ in area_data["items"]: # 遍历所有该区域下,当天(weekday)可以培养的角色、武器 added = False for i in user_data[type_]: # 从已经获取的角色数据中查找对应角色、武器 if id_ == str(i.id): @@ -231,33 +241,42 @@ class DailyMaterial(Plugin, BasePlugin): continue if item[2] < 4: # 跳过 3 星及以下的武器 continue - items.append(ItemData( # 添加角色数据中未找到的 - id=id_, name=item[1], rarity=item[2], - icon=(await getattr(self.assets_service, type_)(id_).icon()).as_uri() - )) + items.append( + ItemData( # 添加角色数据中未找到的 + id=id_, + name=item[1], + rarity=item[2], + icon=(await getattr(self.assets_service, type_)(id_).icon()).as_uri(), + ) + ) materials = [] - for mid in area_data['materials']: # 添加这个区域当天(weekday)的培养素材 + for mid in area_data["materials"]: # 添加这个区域当天(weekday)的培养素材 path = (await self.assets_service.material(mid).icon()).as_uri() - material = HONEY_DATA['material'][mid] + material = HONEY_DATA["material"][mid] materials.append(ItemData(id=mid, icon=path, name=material[1], rarity=material[2])) - areas.append(AreaData( - name=area_data['name'], materials=materials, items=sort_item(items), - material_name=get_material_serial_name(map(lambda x: x.name, materials)) - )) - setattr(render_data, {'avatar': 'character'}.get(type_, type_), areas) + areas.append( + AreaData( + name=area_data["name"], + materials=materials, + items=sort_item(items), + material_name=get_material_serial_name(map(lambda x: x.name, materials)), + ) + ) + setattr(render_data, {"avatar": "character"}.get(type_, type_), areas) await update.message.reply_chat_action(ChatAction.TYPING) render_tasks = [ asyncio.create_task( self.template_service.render( # 渲染角色素材页 - 'genshin/daily_material', 'character.html', {'data': render_data}, {'width': 1164, 'height': 500} + "genshin/daily_material", "character.html", {"data": render_data}, {"width": 1164, "height": 500} ) ), asyncio.create_task( self.template_service.render( # 渲染武器素材页 - 'genshin/daily_material', 'weapon.html', {'data': render_data}, {'width': 1164, 'height': 500} + "genshin/daily_material", "weapon.html", {"data": render_data}, {"width": 1164, "height": 500} ) - )] + ), + ] while not all(map(lambda x: x.done(), render_tasks)): await asyncio.sleep(0) @@ -267,25 +286,25 @@ class DailyMaterial(Plugin, BasePlugin): self._add_delete_message_job(context, notice.chat_id, notice.message_id, 5) await update.message.reply_chat_action(ChatAction.UPLOAD_PHOTO) if full: # 是否发送原图 - await update.message.reply_media_group([ - InputMediaDocument(character_img_data, filename="可培养角色.png"), - InputMediaDocument(weapon_img_data, filename="可培养武器.png") - ]) + await update.message.reply_media_group( + [ + InputMediaDocument(character_img_data, filename="可培养角色.png"), + InputMediaDocument(weapon_img_data, filename="可培养武器.png"), + ] + ) else: await update.message.reply_media_group( [InputMediaPhoto(character_img_data), InputMediaPhoto(weapon_img_data)] ) logger.debug("角色、武器培养素材图发送成功") - @handler.command('refresh_daily_material', block=False) + @handler.command("refresh_daily_material", block=False) @bot_admins_rights_check async def refresh(self, update: Update, context: CallbackContext): user = update.effective_user message = update.effective_message - logger.info( - f"用户 {user.full_name}[{user.id}] 刷新[bold]每日素材[/]缓存命令", extra={'markup': True} - ) + logger.info(f"用户 {user.full_name}[{user.id}] 刷新[bold]每日素材[/]缓存命令", extra={"markup": True}) if self.locks[0].locked(): notice = await message.reply_text("派蒙还在抄每日素材表呢,我有在好好工作哦~") self._add_delete_message_job(context, notice.chat_id, notice.message_id, 10) @@ -299,68 +318,61 @@ class DailyMaterial(Plugin, BasePlugin): async with self.locks[0]: # 锁住第一把锁 data = await self._refresh_data() notice = await notice.edit_text( - "每日素材表" + - ("摘抄完成!" if data else "坏掉了!等会它再长好了之后我再抄。。。") + - '\n正搬运每日素材的图标中。。。', - parse_mode=ParseMode.HTML + "每日素材表" + ("摘抄完成!" if data else "坏掉了!等会它再长好了之后我再抄。。。") + "\n正搬运每日素材的图标中。。。", + parse_mode=ParseMode.HTML, ) self.data = data or self.data time = await self._download_icon(notice) async def job(_, n): - await n.edit_text( - n.text_html.split('\n')[0] + "\n每日素材图标搬运完成!", - parse_mode=ParseMode.HTML - ) + await n.edit_text(n.text_html.split("\n")[0] + "\n每日素材图标搬运完成!", parse_mode=ParseMode.HTML) await asyncio.sleep(INTERVAL) await notice.delete() context.application.job_queue.run_once( - partial(job, n=notice), when=time + INTERVAL, name='notice_msg_final_job' + partial(job, n=notice), when=time + INTERVAL, name="notice_msg_final_job" ) async def _refresh_data(self, retry: int = 5) -> DATA_TYPE: """刷新来自 honey impact 的每日素材表""" from bs4 import Tag + result = {} for i in range(retry): # 重复尝试 retry 次 try: response = await self.client.get("https://genshin.honeyhunterworld.com/?lang=CHS") - soup = BeautifulSoup(response.text, 'lxml') + soup = BeautifulSoup(response.text, "lxml") calendar = soup.select(".calendar_day_wrap")[0] - key: str = '' + key: str = "" for tag in calendar: tag: Tag - if tag.name == 'span': # 如果是秘境 - key = tag.find('a').text + if tag.name == "span": # 如果是秘境 + key = tag.find("a").text result[key] = [[[], []] for _ in range(7)] - for day, div in enumerate(tag.find_all('div')): + for day, div in enumerate(tag.find_all("div")): result[key][day][0] = [] - for a in div.find_all('a'): - honey_id = re.findall(r"/(.*)?/", a['href'])[0] - mid: str = [ - i[0] - for i in HONEY_DATA['material'].items() - if i[1][0] == honey_id - ][0] + for a in div.find_all("a"): + honey_id = re.findall(r"/(.*)?/", a["href"])[0] + mid: str = [i[0] for i in HONEY_DATA["material"].items() if i[1][0] == honey_id][0] result[key][day][0].append(mid) else: # 如果是角色或武器 - id_ = re.findall(r"/(.*)?/", tag['href'])[0] - if tag.text.strip() == '旅行者': # 忽略主角 + id_ = re.findall(r"/(.*)?/", tag["href"])[0] + if tag.text.strip() == "旅行者": # 忽略主角 continue - id_ = ("" if id_.startswith('i_n') else "10000") + re.findall(r'\d+', id_)[0] - for day in map(int, tag.find('div')['data-days']): # 获取该角色/武器的可培养天 + id_ = ("" if id_.startswith("i_n") else "10000") + re.findall(r"\d+", id_)[0] + for day in map(int, tag.find("div")["data-days"]): # 获取该角色/武器的可培养天 result[key][day][1].append(id_) for stage, schedules in result.items(): for day, _ in enumerate(schedules): # noinspection PyUnresolvedReferences result[stage][day][1] = list(set(result[stage][day][1])) # 去重 - async with async_open(DATA_FILE_PATH, 'w', encoding='utf-8') as file: + async with async_open(DATA_FILE_PATH, "w", encoding="utf-8") as file: await file.write(json.dumps(result)) # pylint: disable=PY-W0079 logger.info("每日素材刷新成功") break except (HTTPError, SSLZeroReturnError): from asyncio import sleep + await sleep(1) if i <= retry - 1: logger.warning("每日素材刷新失败, 正在重试") @@ -375,6 +387,7 @@ class DailyMaterial(Plugin, BasePlugin): asset_list = [] from time import time as time_ + lock = asyncio.Lock() the_time = Value(c_double, time_() - INTERVAL) @@ -382,20 +395,15 @@ class DailyMaterial(Plugin, BasePlugin): async def edit_message(text): """修改提示消息""" async with lock: - if ( - message is not None - and - time_() >= (the_time.value + INTERVAL) - ): + if message is not None and time_() >= (the_time.value + INTERVAL): with contextlib.suppress(TimedOut, RetryAfter): await message.edit_text( - '\n'.join(message.text_html.split('\n')[:2] + [text]), - parse_mode=ParseMode.HTML + "\n".join(message.text_html.split("\n")[:2] + [text]), parse_mode=ParseMode.HTML ) the_time.value = time_() async def task(item_id, name, item_type): - logger.debug(f"正在开始下载 \"{name}\" 的图标素材") + logger.debug(f'正在开始下载 "{name}" 的图标素材') await edit_message(f"正在搬运 {name} 的图标素材。。。") asset: AssetsServiceType = getattr(self.assets_service, item_type)(item_id) # 获取素材对象 asset_list.append(asset.honey_id) @@ -403,7 +411,7 @@ class DailyMaterial(Plugin, BasePlugin): # 并根据图标类型找到下载对应图标的函数 for icon_type in asset.icon_types: await getattr(asset, icon_type)(True) # 执行下载函数 - logger.debug(f"\"{name}\" 的图标素材下载成功") + logger.debug(f'"{name}" 的图标素材下载成功') await edit_message(f"正在搬运 {name} 的图标素材。。。成功!") for TYPE, ITEMS in HONEY_DATA.items(): # 遍历每个对象 @@ -431,7 +439,7 @@ class ItemData(BaseModel): class AreaData(BaseModel): - name: Literal['蒙德', '璃月', '稻妻', '须弥'] # 区域名 + name: Literal["蒙德", "璃月", "稻妻", "须弥"] # 区域名 material_name: str # 区域的材料系列名 materials: List[ItemData] = [] # 区域材料 items: Iterable[ItemData] = [] # 可培养的角色或武器 diff --git a/plugins/genshin/daily_note.py b/plugins/genshin/daily_note.py index eabb1c78..f458d241 100644 --- a/plugins/genshin/daily_note.py +++ b/plugins/genshin/daily_note.py @@ -5,8 +5,7 @@ from typing import Optional from genshin import DataNotPublic from telegram import Update from telegram.constants import ChatAction -from telegram.ext import CommandHandler, MessageHandler, ConversationHandler, filters, \ - CallbackContext +from telegram.ext import CommandHandler, MessageHandler, ConversationHandler, filters, CallbackContext from core.baseplugin import BasePlugin from core.cookies.error import CookiesNotFoundError @@ -24,8 +23,12 @@ from utils.log import logger class DailyNote(Plugin, BasePlugin): """每日便签""" - def __init__(self, user_service: UserService = None, cookies_service: CookiesService = None, - template_service: TemplateService = None): + def __init__( + self, + user_service: UserService = None, + cookies_service: CookiesService = None, + template_service: TemplateService = None, + ): self.template_service = template_service self.cookies_service = cookies_service self.user_service = user_service @@ -34,11 +37,18 @@ class DailyNote(Plugin, BasePlugin): async def _get_daily_note(self, client) -> bytes: daily_info = await client.get_genshin_notes(client.uid) day = datetime.datetime.now().strftime("%m-%d %H:%M") + " 星期" + "一二三四五六日"[datetime.datetime.now().weekday()] - resin_recovery_time = daily_info.resin_recovery_time.strftime("%m-%d %H:%M") if \ - daily_info.max_resin - daily_info.current_resin else None - realm_recovery_time = (datetime.datetime.now().astimezone() + - daily_info.remaining_realm_currency_recovery_time).strftime("%m-%d %H:%M") if \ - daily_info.max_realm_currency - daily_info.current_realm_currency else None + resin_recovery_time = ( + daily_info.resin_recovery_time.strftime("%m-%d %H:%M") + if daily_info.max_resin - daily_info.current_resin + else None + ) + realm_recovery_time = ( + (datetime.datetime.now().astimezone() + daily_info.remaining_realm_currency_recovery_time).strftime( + "%m-%d %H:%M" + ) + if daily_info.max_realm_currency - daily_info.current_realm_currency + else None + ) remained_time = None for i in daily_info.expeditions: if remained_time: @@ -73,10 +83,11 @@ class DailyNote(Plugin, BasePlugin): "max_resin_discounts": daily_info.max_resin_discounts, "transformer": transformer, "transformer_ready": transformer_ready, - "transformer_recovery_time": transformer_recovery_time + "transformer_recovery_time": transformer_recovery_time, } - png_data = await self.template_service.render('genshin/daily_note', "daily_note.html", daily_data, - {"width": 600, "height": 548}, full_page=False) + png_data = await self.template_service.render( + "genshin/daily_note", "daily_note.html", daily_data, {"width": 600, "height": 548}, full_page=False + ) return png_data @handler(CommandHandler, command="dailynote", block=False) diff --git a/plugins/genshin/gacha/gacha.py b/plugins/genshin/gacha/gacha.py index 4e112b81..42d18b99 100644 --- a/plugins/genshin/gacha/gacha.py +++ b/plugins/genshin/gacha/gacha.py @@ -125,8 +125,9 @@ class Gacha(Plugin, BasePlugin): data["items"].sort(key=take_rang, reverse=True) await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) # 因为 gacha_info["title"] 返回的是 HTML 标签 尝试关闭自动转义 - png_data = await self.template_service.render('genshin/gacha', "gacha.html", data, - {"width": 1157, "height": 603}, False) + png_data = await self.template_service.render( + "genshin/gacha", "gacha.html", data, {"width": 1157, "height": 603}, False + ) reply_message = await message.reply_photo(png_data) if filters.ChatType.GROUPS.filter(message): diff --git a/plugins/genshin/ledger.py b/plugins/genshin/ledger.py index 8c613b34..9786c504 100644 --- a/plugins/genshin/ledger.py +++ b/plugins/genshin/ledger.py @@ -38,8 +38,7 @@ def check_ledger_month(context: CallbackContext) -> int: elif re_data := re.findall(r"\d+", str(month)): month = int(re_data[0]) else: - num_dict = {"一": 1, "二": 2, "三": 3, "四": 4, "五": 5, - "六": 6, "七": 7, "八": 8, "九": 9, "十": 10} + num_dict = {"一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10} month = sum(num_dict.get(i, 0) for i in str(month)) # check right allow_month = [now_time.month] @@ -58,8 +57,12 @@ def check_ledger_month(context: CallbackContext) -> int: class Ledger(Plugin, BasePlugin): """旅行札记""" - def __init__(self, user_service: UserService = None, cookies_service: CookiesService = None, - template_service: TemplateService = None): + def __init__( + self, + user_service: UserService = None, + cookies_service: CookiesService = None, + template_service: TemplateService = None, + ): self.template_service = template_service self.cookies_service = cookies_service self.user_service = user_service @@ -71,18 +74,26 @@ class Ledger(Plugin, BasePlugin): except GenshinException as error: raise error color = ["#73a9c6", "#d56565", "#70b2b4", "#bd9a5a", "#739970", "#7a6da7", "#597ea0"] - categories = [{"id": i.id, - "name": i.name, - "color": color[i.id % len(color)], - "amount": i.amount, - "percentage": i.percentage} for i in diary_info.month_data.categories] + categories = [ + { + "id": i.id, + "name": i.name, + "color": color[i.id % len(color)], + "amount": i.amount, + "percentage": i.percentage, + } + for i in diary_info.month_data.categories + ] color = [i["color"] for i in categories] def format_amount(amount: int) -> str: return f"{round(amount / 10000, 2)}w" if amount >= 10000 else amount - evaluate = """const { Pie } = G2Plot; - const data = JSON.parse(`""" + json.dumps(categories) + """`); + evaluate = ( + """const { Pie } = G2Plot; + const data = JSON.parse(`""" + + json.dumps(categories) + + """`); const piePlot = new Pie("chartContainer", { renderer: "svg", animation: false, @@ -92,7 +103,9 @@ class Ledger(Plugin, BasePlugin): colorField: "name", radius: 1, innerRadius: 0.7, - color: JSON.parse(`""" + json.dumps(color) + """`), + color: JSON.parse(`""" + + json.dumps(color) + + """`), meta: {}, label: { type: "inner", @@ -121,6 +134,7 @@ class Ledger(Plugin, BasePlugin): legend:false, }); piePlot.render();""" + ) ledger_data = { "uid": client.uid, "day": diary_info.month, @@ -132,9 +146,9 @@ class Ledger(Plugin, BasePlugin): "last_mora": format_amount(diary_info.month_data.last_mora), "categories": categories, } - png_data = await self.template_service.render('genshin/ledger', "ledger.html", ledger_data, - {"width": 580, "height": 610}, - evaluate=evaluate) + png_data = await self.template_service.render( + "genshin/ledger", "ledger.html", ledger_data, {"width": 580, "height": 610}, evaluate=evaluate + ) return png_data @handler(CommandHandler, command="ledger", block=False) diff --git a/plugins/genshin/map/map.py b/plugins/genshin/map/map.py index 2c0f81ba..f15e0094 100644 --- a/plugins/genshin/map/map.py +++ b/plugins/genshin/map/map.py @@ -60,6 +60,6 @@ class Map(Plugin, BasePlugin): return img = Image.open(f"cache{sep}map.jpg") if img.size[0] > 2048 or img.size[1] > 2048: - await message.reply_document(open(f"cache{sep}map.jpg", mode='rb+'), caption=text) + await message.reply_document(open(f"cache{sep}map.jpg", mode="rb+"), caption=text) else: - await message.reply_photo(open(f"cache{sep}map.jpg", mode='rb+'), caption=text) + await message.reply_photo(open(f"cache{sep}map.jpg", mode="rb+"), caption=text) diff --git a/plugins/genshin/map/model.py b/plugins/genshin/map/model.py index db714b2a..02f48445 100644 --- a/plugins/genshin/map/model.py +++ b/plugins/genshin/map/model.py @@ -16,9 +16,9 @@ RESOURCE_ICON_OFFSET = (-int(150 * 0.5 * ZOOM), -int(150 * ZOOM)) class MapHelper: - LABEL_URL = 'https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc' - POINT_LIST_URL = 'https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list?map_id=2&app_sn=ys_obc' - MAP_URL = 'https://api-static.mihoyo.com/common/map_user/ys_obc/v1/map/info?map_id=2&app_sn=ys_obc&lang=zh-cn' + LABEL_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc" + POINT_LIST_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list?map_id=2&app_sn=ys_obc" + MAP_URL = "https://api-static.mihoyo.com/common/map_user/ys_obc/v1/map/info?map_id=2&app_sn=ys_obc&lang=zh-cn" def __init__(self, cache_dir_name: str = "cache"): self._current_dir = os.getcwd() @@ -109,11 +109,11 @@ class MapHelper: map_info = map_info["data"]["info"]["detail"] map_info = ujson.loads(map_info) - map_url_list = map_info['slices'][0] + map_url_list = map_info["slices"][0] origin = map_info["origin"] - x_start = map_info['total_size'][1] - y_start = map_info['total_size'][1] + x_start = map_info["total_size"][1] + y_start = map_info["total_size"][1] x_end = 0 y_end = 0 for resource_point in self.all_resource_point_list: @@ -223,7 +223,6 @@ class MapHelper: class ResourceMap: - def __init__(self, all_resource_point_list: List[dict], map_icon: Image, center: List[float], resource_id: int): self.all_resource_point_list = all_resource_point_list self.resource_id = resource_id @@ -263,8 +262,9 @@ class ResourceMap: # 这时地图已经裁切过了,要以裁切后的地图左上角为中心再转换一次坐标 x -= self.x_start y -= self.y_start - self.map_image.paste(self.resource_icon, (x + RESOURCE_ICON_OFFSET[0], y + RESOURCE_ICON_OFFSET[1]), - self.resource_icon) + self.map_image.paste( + self.resource_icon, (x + RESOURCE_ICON_OFFSET[0], y + RESOURCE_ICON_OFFSET[1]), self.resource_icon + ) def crop(self): # 把大地图裁切到只保留资源图标位置 @@ -291,8 +291,7 @@ class ResourceMap: self.y_start = center - 500 self.y_end = center + 500 - self.map_image = self.map_image.crop((self.x_start, self.y_start, - self.x_end, self.y_end)) + self.map_image = self.map_image.crop((self.x_start, self.y_start, self.x_end, self.y_end)) def gen_jpg(self): if not self.resource_xy_list: @@ -301,7 +300,7 @@ class ResourceMap: os.mkdir("cache") # 查找 cache 目录 (缓存目录) 是否存在,如果不存在则创建 self.crop() self.paste() - self.map_image.save(f'cache{os.sep}map.jpg', format='JPEG') + self.map_image.save(f"cache{os.sep}map.jpg", format="JPEG") def get_resource_count(self): return len(self.resource_xy_list) diff --git a/plugins/genshin/material.py b/plugins/genshin/material.py index b605e07b..f534e67e 100644 --- a/plugins/genshin/material.py +++ b/plugins/genshin/material.py @@ -16,9 +16,7 @@ from utils.log import logger class Material(Plugin, BasePlugin): """角色培养素材查询""" - KEYBOARD = [[InlineKeyboardButton( - text="查看角色培养素材列表并查询", - switch_inline_query_current_chat="查看角色培养素材列表并查询")]] + KEYBOARD = [[InlineKeyboardButton(text="查看角色培养素材列表并查询", switch_inline_query_current_chat="查看角色培养素材列表并查询")]] def __init__(self, game_material_service: GameMaterialService = None): self.game_material_service = game_material_service @@ -34,8 +32,9 @@ class Material(Plugin, BasePlugin): if len(args) >= 1: character_name = args[0] else: - reply_message = await message.reply_text("请回复你要查询的培养素材的角色名", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text( + "请回复你要查询的培养素材的角色名", reply_markup=InlineKeyboardMarkup(self.KEYBOARD) + ) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -43,8 +42,9 @@ class Material(Plugin, BasePlugin): character_name = roleToName(character_name) url = await self.game_material_service.get_material(character_name) if not url: - reply_message = await message.reply_text(f"没有找到 {character_name} 的培养素材", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text( + f"没有找到 {character_name} 的培养素材", reply_markup=InlineKeyboardMarkup(self.KEYBOARD) + ) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -52,7 +52,11 @@ class Material(Plugin, BasePlugin): logger.info(f"用户 {user.full_name}[{user.id}] 查询角色培养素材命令请求 || 参数 {character_name}") await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) file_path = await url_to_file(url, return_path=True) - caption = "From 米游社 " \ - f"查看 [原图]({url})" - await message.reply_photo(photo=open(file_path, "rb"), caption=caption, filename=f"{character_name}.png", - allow_sending_without_reply=True, parse_mode=ParseMode.MARKDOWN_V2) + caption = "From 米游社 " f"查看 [原图]({url})" + await message.reply_photo( + photo=open(file_path, "rb"), + caption=caption, + filename=f"{character_name}.png", + allow_sending_without_reply=True, + parse_mode=ParseMode.MARKDOWN_V2, + ) diff --git a/plugins/genshin/quiz.py b/plugins/genshin/quiz.py index 05ba1f01..6e4694cc 100644 --- a/plugins/genshin/quiz.py +++ b/plugins/genshin/quiz.py @@ -50,8 +50,13 @@ class QuizPlugin(Plugin, BasePlugin): return None random.shuffle(_options) index = _options.index(correct_option) - poll_message = await update.effective_message.reply_poll(question.text, _options, - correct_option_id=index, is_anonymous=False, - open_period=self.time_out, type=Poll.QUIZ) + poll_message = await update.effective_message.reply_poll( + question.text, + _options, + correct_option_id=index, + is_anonymous=False, + open_period=self.time_out, + type=Poll.QUIZ, + ) self._add_delete_message_job(context, update.message.chat_id, update.message.message_id, 300) self._add_delete_message_job(context, poll_message.chat_id, poll_message.message_id, 300) diff --git a/plugins/genshin/strategy.py b/plugins/genshin/strategy.py index 57732e9a..c88c48f3 100644 --- a/plugins/genshin/strategy.py +++ b/plugins/genshin/strategy.py @@ -35,8 +35,7 @@ class StrategyPlugin(Plugin, BasePlugin): if len(args) >= 1: character_name = args[0] else: - reply_message = await message.reply_text("请回复你要查询的攻略的角色名", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text("请回复你要查询的攻略的角色名", reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -44,8 +43,9 @@ class StrategyPlugin(Plugin, BasePlugin): character_name = roleToName(character_name) url = await self.game_strategy_service.get_strategy(character_name) if url == "": - reply_message = await message.reply_text(f"没有找到 {character_name} 的攻略", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text( + f"没有找到 {character_name} 的攻略", reply_markup=InlineKeyboardMarkup(self.KEYBOARD) + ) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -53,7 +53,11 @@ class StrategyPlugin(Plugin, BasePlugin): logger.info(f"用户 {user.full_name}[{user.id}] 查询角色攻略命令请求 || 参数 {character_name}") await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) file_path = await url_to_file(url, return_path=True) - caption = "From 米游社 西风驿站 " \ - f"查看 [原图]({url})" - await message.reply_photo(photo=open(file_path, "rb"), caption=caption, filename=f"{character_name}.png", - allow_sending_without_reply=True, parse_mode=ParseMode.MARKDOWN_V2) + caption = "From 米游社 西风驿站 " f"查看 [原图]({url})" + await message.reply_photo( + photo=open(file_path, "rb"), + caption=caption, + filename=f"{character_name}.png", + allow_sending_without_reply=True, + parse_mode=ParseMode.MARKDOWN_V2, + ) diff --git a/plugins/genshin/uid.py b/plugins/genshin/uid.py index ea16fe54..03dbf4f7 100644 --- a/plugins/genshin/uid.py +++ b/plugins/genshin/uid.py @@ -31,14 +31,18 @@ CHECK_SERVER, CHECK_UID, COMMAND_RESULT = range(10100, 10103) class SetUserUid(Plugin.Conversation, BasePlugin.Conversation): """UID用户绑定""" - def __init__(self, user_service: UserService = None, cookies_service: CookiesService = None, - public_cookies_service: PublicCookiesService = None): + def __init__( + self, + user_service: UserService = None, + cookies_service: CookiesService = None, + public_cookies_service: PublicCookiesService = None, + ): self.public_cookies_service = public_cookies_service self.cookies_service = cookies_service self.user_service = user_service @conversation.entry_point - @handler.command(command='setuid', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="setuid", filters=filters.ChatType.PRIVATE, block=True) @restricts() @error_callable async def command_start(self, update: Update, context: CallbackContext) -> int: @@ -49,9 +53,11 @@ class SetUserUid(Plugin.Conversation, BasePlugin.Conversation): if add_user_command_data is None: cookies_command_data = AddUserCommandData() context.chat_data["add_uid_command_data"] = cookies_command_data - text = f'你好 {user.mention_markdown_v2()} ' \ - f'{escape_markdown("!请输入通行证UID(非游戏UID),BOT将会通过通行证UID查找游戏UID。请选择要绑定的服务器!或回复退出取消操作")}' - reply_keyboard = [['米游社', 'HoYoLab'], ["退出"]] + text = ( + f"你好 {user.mention_markdown_v2()} " + f'{escape_markdown("!请输入通行证UID(非游戏UID),BOT将会通过通行证UID查找游戏UID。请选择要绑定的服务器!或回复退出取消操作")}' + ) + reply_keyboard = [["米游社", "HoYoLab"], ["退出"]] await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) return CHECK_SERVER @@ -112,8 +118,9 @@ class SetUserUid(Plugin.Conversation, BasePlugin.Conversation): if region == RegionEnum.HYPERION: client = genshin.Client(cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.CHINESE) elif region == RegionEnum.HOYOLAB: - client = genshin.Client(cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, - lang="zh-cn") + client = genshin.Client( + cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn" + ) else: return ConversationHandler.END try: @@ -128,22 +135,20 @@ class SetUserUid(Plugin.Conversation, BasePlugin.Conversation): logger.exception(exc) return ConversationHandler.END if user_info.game != types.Game.GENSHIN: - await message.reply_text("角色信息查询返回非原神游戏信息," - "请设置展示主界面为原神", reply_markup=ReplyKeyboardRemove()) + await message.reply_text("角色信息查询返回非原神游戏信息," "请设置展示主界面为原神", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END - reply_keyboard = [['确认', '退出']] + reply_keyboard = [["确认", "退出"]] await message.reply_text("获取角色基础信息成功,请检查是否正确!") logger.info(f"用户 {user.full_name}[{user.id}] 获取账号 {user_info.nickname}[{user_info.uid}] 信息成功") - text = f"*角色信息*\n" \ - f"角色名称:{escape_markdown(user_info.nickname, version=2)}\n" \ - f"角色等级:{user_info.level}\n" \ - f"UID:`{user_info.uid}`\n" \ - f"服务器名称:`{user_info.server_name}`\n" - add_user_command_data.game_uid = user_info.uid - await message.reply_markdown_v2( - text, - reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) + text = ( + f"*角色信息*\n" + f"角色名称:{escape_markdown(user_info.nickname, version=2)}\n" + f"角色等级:{user_info.level}\n" + f"UID:`{user_info.uid}`\n" + f"服务器名称:`{user_info.server_name}`\n" ) + add_user_command_data.game_uid = user_info.uid + await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) return COMMAND_RESULT @conversation.state(state=COMMAND_RESULT) @@ -159,11 +164,15 @@ class SetUserUid(Plugin.Conversation, BasePlugin.Conversation): elif message.text == "确认": if add_user_command_data.user is None: if add_user_command_data.region == RegionEnum.HYPERION: - user_db = User(user_id=user.id, yuanshen_uid=add_user_command_data.game_uid, - region=add_user_command_data.region) + user_db = User( + user_id=user.id, + yuanshen_uid=add_user_command_data.game_uid, + region=add_user_command_data.region, + ) elif add_user_command_data.region == RegionEnum.HOYOLAB: - user_db = User(user_id=user.id, genshin_uid=add_user_command_data.game_uid, - region=add_user_command_data.region) + user_db = User( + user_id=user.id, genshin_uid=add_user_command_data.game_uid, region=add_user_command_data.region + ) else: await message.reply_text("数据错误") return ConversationHandler.END diff --git a/plugins/genshin/userstats.py b/plugins/genshin/userstats.py index 5eb571fb..3c7d6ff1 100644 --- a/plugins/genshin/userstats.py +++ b/plugins/genshin/userstats.py @@ -55,13 +55,9 @@ class UserStatsPlugins(Plugin, BasePlugin): except UserNotFoundError: reply_message = await message.reply_text("未查询到账号信息,请先私聊派蒙绑定账号") if filters.ChatType.GROUPS.filter(message): - self._add_delete_message_job( - context, reply_message.chat_id, reply_message.message_id, 30 - ) + self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id, 30) - self._add_delete_message_job( - context, message.chat_id, message.message_id, 30 - ) + self._add_delete_message_job(context, message.chat_id, message.message_id, 30) return except TooManyRequestPublicCookies: await message.reply_text("用户查询次数过多 请稍后重试") @@ -72,9 +68,7 @@ class UserStatsPlugins(Plugin, BasePlugin): await message.reply_text("角色数据有误 估计是派蒙晕了") return await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) - await message.reply_photo( - png_data, filename=f"{client.uid}.png", allow_sending_without_reply=True - ) + await message.reply_photo(png_data, filename=f"{client.uid}.png", allow_sending_without_reply=True) async def render(self, client: Client, uid: Optional[int] = None) -> bytes: if uid is None: @@ -109,7 +103,7 @@ class UserStatsPlugins(Plugin, BasePlugin): ("雷神瞳", "electroculi"), ("草神瞳", "dendroculi"), ], - "style": secrets.choice(["mondstadt", "liyue"]) + "style": secrets.choice(["mondstadt", "liyue"]), } # html = await self.template_service.render_async( diff --git a/plugins/genshin/weapon.py b/plugins/genshin/weapon.py index b6e8d64a..7a846bd0 100644 --- a/plugins/genshin/weapon.py +++ b/plugins/genshin/weapon.py @@ -20,15 +20,13 @@ from utils.log import logger class WeaponPlugin(Plugin, BasePlugin): """武器查询""" - KEYBOARD = [[ - InlineKeyboardButton(text="查看武器列表并查询", switch_inline_query_current_chat="查看武器列表并查询") - ]] + KEYBOARD = [[InlineKeyboardButton(text="查看武器列表并查询", switch_inline_query_current_chat="查看武器列表并查询")]] def __init__( - self, - template_service: TemplateService = None, - wiki_service: WikiService = None, - assets_service: AssetsService = None + self, + template_service: TemplateService = None, + wiki_service: WikiService = None, + assets_service: AssetsService = None, ): self.wiki_service = wiki_service self.template_service = template_service @@ -45,8 +43,7 @@ class WeaponPlugin(Plugin, BasePlugin): if len(args) >= 1: weapon_name = args[0] else: - reply_message = await message.reply_text("请回复你要查询的武器", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text("请回复你要查询的武器", reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -59,8 +56,9 @@ class WeaponPlugin(Plugin, BasePlugin): weapon_data = weapon break else: - reply_message = await message.reply_text(f"没有找到 {weapon_name}", - reply_markup=InlineKeyboardMarkup(self.KEYBOARD)) + reply_message = await message.reply_text( + f"没有找到 {weapon_name}", reply_markup=InlineKeyboardMarkup(self.KEYBOARD) + ) if filters.ChatType.GROUPS.filter(reply_message): self._add_delete_message_job(context, message.chat_id, message.message_id) self._add_delete_message_job(context, reply_message.chat_id, reply_message.message_id) @@ -70,8 +68,8 @@ class WeaponPlugin(Plugin, BasePlugin): async def input_template_data(_weapon_data: Weapon): if weapon.rarity > 2: bonus = _weapon_data.stats[-1].bonus - if '%' in bonus: - bonus = str(round(float(bonus.rstrip('%')))) + '%' + if "%" in bonus: + bonus = str(round(float(bonus.rstrip("%")))) + "%" else: bonus = str(round(float(bonus))) _template_data = { @@ -80,12 +78,12 @@ class WeaponPlugin(Plugin, BasePlugin): "progression_secondary_stat_value": bonus, "progression_secondary_stat_name": _weapon_data.attribute.type.value, "weapon_info_source_img": ( - await self.assets_service.weapon(honey_id_to_game_id(_weapon_data.id, 'weapon')).icon() + await self.assets_service.weapon(honey_id_to_game_id(_weapon_data.id, "weapon")).icon() ).as_uri(), "weapon_info_max_level": _weapon_data.stats[-1].level, "progression_base_atk": round(_weapon_data.stats[-1].ATK), "weapon_info_source_list": [ - (await self.assets_service.material(honey_id_to_game_id(mid, 'material')).icon()).as_uri() + (await self.assets_service.material(honey_id_to_game_id(mid, "material")).icon()).as_uri() for mid in _weapon_data.ascension[-3:] ], "special_ability_name": _weapon_data.affix.name, @@ -95,25 +93,27 @@ class WeaponPlugin(Plugin, BasePlugin): _template_data = { "weapon_name": _weapon_data.name, "weapon_info_type_img": await url_to_file(_weapon_data.weapon_type.icon_url()), - "progression_secondary_stat_value": ' ', - "progression_secondary_stat_name": '无其它属性加成', + "progression_secondary_stat_value": " ", + "progression_secondary_stat_name": "无其它属性加成", "weapon_info_source_img": ( - await self.assets_service.weapon(honey_id_to_game_id(_weapon_data.id, 'weapon')).icon() + await self.assets_service.weapon(honey_id_to_game_id(_weapon_data.id, "weapon")).icon() ).as_uri(), "weapon_info_max_level": _weapon_data.stats[-1].level, "progression_base_atk": round(_weapon_data.stats[-1].ATK), "weapon_info_source_list": [ - (await self.assets_service.material(honey_id_to_game_id(mid, 'material')).icon()).as_uri() + (await self.assets_service.material(honey_id_to_game_id(mid, "material")).icon()).as_uri() for mid in _weapon_data.ascension[-3:] ], - "special_ability_name": '', + "special_ability_name": "", "special_ability_info": _weapon_data.description, } return _template_data template_data = await input_template_data(weapon_data) - png_data = await self.template_service.render('genshin/weapon', "weapon.html", template_data, - {"width": 540, "height": 540}) + png_data = await self.template_service.render( + "genshin/weapon", "weapon.html", template_data, {"width": 540, "height": 540} + ) await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) - await message.reply_photo(png_data, filename=f"{template_data['weapon_name']}.png", - allow_sending_without_reply=True) + await message.reply_photo( + png_data, filename=f"{template_data['weapon_name']}.png", allow_sending_without_reply=True + ) diff --git a/plugins/jobs/public_cookies.py b/plugins/jobs/public_cookies.py index 83264341..df223e7d 100644 --- a/plugins/jobs/public_cookies.py +++ b/plugins/jobs/public_cookies.py @@ -8,7 +8,6 @@ from utils.log import logger class PublicCookies(Plugin): - def __init__(self, public_cookies_service: PublicCookiesService = None): self.public_cookies_service = public_cookies_service diff --git a/plugins/other/post.py b/plugins/other/post.py index 2ebe10a0..9667d21c 100644 --- a/plugins/other/post.py +++ b/plugins/other/post.py @@ -19,7 +19,6 @@ from utils.log import logger class PostHandlerData: - def __init__(self): self.post_text: str = "" self.post_images: Optional[List[ArtworkImage]] = None @@ -41,7 +40,7 @@ class Post(Plugin.Conversation, BasePlugin): self.bbs = Hyperion() @conversation.entry_point - @handler.command(command='post', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="post", filters=filters.ChatType.PRIVATE, block=True) @restricts() @bot_admins_rights_check @error_callable @@ -53,10 +52,8 @@ class Post(Plugin.Conversation, BasePlugin): if post_handler_data is None: post_handler_data = PostHandlerData() context.chat_data["post_handler_data"] = post_handler_data - text = f"✿✿ヽ(°▽°)ノ✿ 你好! {user.username} ,\n" \ - "只需复制URL回复即可 \n" \ - "退出投稿只需回复退出" - reply_keyboard = [['退出']] + text = f"✿✿ヽ(°▽°)ノ✿ 你好! {user.username} ,\n" "只需复制URL回复即可 \n" "退出投稿只需回复退出" + reply_keyboard = [["退出"]] await message.reply_text(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True)) return CHECK_POST @@ -77,17 +74,16 @@ class Post(Plugin.Conversation, BasePlugin): post_info = await self.bbs.get_post_info(2, post_id) post_images = await self.bbs.get_images_by_post_id(2, post_id) post_data = post_info["post"]["post"] - post_subject = post_data['subject'] + post_subject = post_data["subject"] post_soup = BeautifulSoup(post_data["content"], features="html.parser") - post_p = post_soup.find_all('p') - post_text = f"*{escape_markdown(post_subject, version=2)}*\n" \ - f"\n" + post_p = post_soup.find_all("p") + post_text = f"*{escape_markdown(post_subject, version=2)}*\n" f"\n" for p in post_p: post_text += f"{escape_markdown(p.get_text(), version=2)}\n" post_text += f"[source](https://bbs.mihoyo.com/ys/article/{post_id})" if len(post_text) >= MessageLimit.CAPTION_LENGTH: await message.reply_markdown_v2(post_text) - post_text = post_text[0:MessageLimit.CAPTION_LENGTH] + post_text = post_text[0 : MessageLimit.CAPTION_LENGTH] await message.reply_text(f"警告!图片字符描述已经超过 {MessageLimit.CAPTION_LENGTH} 个字,已经切割并发送原文本") try: if len(post_images) > 1: @@ -138,8 +134,7 @@ class Post(Plugin.Conversation, BasePlugin): post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data") photo_len = len(post_handler_data.post_images) message = update.effective_message - await message.reply_text("请回复你要删除的图片的序列,从1开始,如果删除多张图片回复的序列请以空格作为分隔符," - f"当前一共有 {photo_len} 张图片") + await message.reply_text("请回复你要删除的图片的序列,从1开始,如果删除多张图片回复的序列请以空格作为分隔符," f"当前一共有 {photo_len} 张图片") return GTE_DELETE_PHOTO @conversation.state(state=GTE_DELETE_PHOTO) @@ -175,8 +170,7 @@ class Post(Plugin.Conversation, BasePlugin): logger.error("从配置文件获取频道信息发生错误,退出任务", error) await message.reply_text("从配置文件获取频道信息发生错误,退出任务", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END - await message.reply_text("请选择你要推送的频道", - reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True)) + await message.reply_text("请选择你要推送的频道", reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True)) return GET_POST_CHANNEL @conversation.state(state=GET_POST_CHANNEL) @@ -200,8 +194,7 @@ class Post(Plugin.Conversation, BasePlugin): return ConversationHandler.END post_handler_data.channel_id = channel_id reply_keyboard = [["确认", "退出"]] - await message.reply_text("请核对你修改的信息", - reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True)) + await message.reply_text("请核对你修改的信息", reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True)) return SEND_POST async def add_tags(self, update: Update, _: CallbackContext) -> int: @@ -273,8 +266,9 @@ class Post(Plugin.Conversation, BasePlugin): await context.bot.send_media_group(channel_id, media=media) elif len(post_images) == 1: image = post_images[0] - await context.bot.send_photo(channel_id, photo=image.data, caption=post_text, - parse_mode=ParseMode.MARKDOWN_V2) + await context.bot.send_photo( + channel_id, photo=image.data, caption=post_text, parse_mode=ParseMode.MARKDOWN_V2 + ) elif len(post_images) == 0: await context.bot.send_message(channel_id, post_text, parse_mode=ParseMode.MARKDOWN_V2) else: @@ -287,4 +281,3 @@ class Post(Plugin.Conversation, BasePlugin): return ConversationHandler.END await message.reply_text("推送成功", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END - diff --git a/plugins/system/auth.py b/plugins/system/auth.py index db6d8a79..6d472f36 100644 --- a/plugins/system/auth.py +++ b/plugins/system/auth.py @@ -72,8 +72,9 @@ class GroupJoiningVerification(Plugin): job = context.job logger.info(f"踢出用户 user_id[{job.user_id}] 在 chat_id[{job.chat_id}]") try: - await context.bot.ban_chat_member(chat_id=job.chat_id, user_id=job.user_id, - until_date=int(time.time()) + self.kick_time) + await context.bot.ban_chat_member( + chat_id=job.chat_id, user_id=job.user_id, until_date=int(time.time()) + self.kick_time + ) except BadRequest as exc: logger.error(f"Auth模块在 chat_id[{job.chat_id}] user_id[{job.user_id}] 执行kick失败") logger.exception(exc) @@ -88,8 +89,7 @@ class GroupJoiningVerification(Plugin): if "not found" in str(exc): logger.warning(f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息不存在") elif "Message can't be deleted" in str(exc): - logger.warning( - f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息无法删除 可能是没有授权") + logger.warning(f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息无法删除 可能是没有授权") else: logger.error(f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败") logger.exception(exc) @@ -106,7 +106,6 @@ class GroupJoiningVerification(Plugin): @handler(CallbackQueryHandler, pattern=r"^auth_admin\|", block=False) @restricts(without_overlapping=True) async def admin(self, update: Update, context: CallbackContext) -> None: - async def admin_callback(callback_query_data: str) -> Tuple[str, int]: _data = callback_query_data.split("|") _result = _data[1] @@ -122,8 +121,7 @@ class GroupJoiningVerification(Plugin): chat_administrators = await self.get_chat_administrators(context, chat_id=chat.id) if not self.is_admin(chat_administrators, user.id): logger.debug(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 非群管理") - await callback_query.answer(text="你不是管理!\n" - "再乱点我叫西风骑士团、千岩军和天领奉行了!", show_alert=True) + await callback_query.answer(text="你不是管理!\n" "再乱点我叫西风骑士团、千岩军和天领奉行了!", show_alert=True) return result, user_id = await admin_callback(callback_query.data) try: @@ -139,22 +137,21 @@ class GroupJoiningVerification(Plugin): await self.restore_member(context, chat.id, user_id) if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user_id}|auth_kick"): schedule.remove() - await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 放行", - parse_mode=ParseMode.MARKDOWN_V2) + await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 放行", parse_mode=ParseMode.MARKDOWN_V2) logger.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理放行") elif result == "kick": await callback_query.answer(text="驱离", show_alert=False) await context.bot.ban_chat_member(chat.id, user_id) - await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 驱离", - parse_mode=ParseMode.MARKDOWN_V2) + await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 驱离", parse_mode=ParseMode.MARKDOWN_V2) logger.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理踢出") elif result == "unban": await callback_query.answer(text="解除驱离", show_alert=False) await self.restore_member(context, chat.id, user_id) if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user_id}|auth_kick"): schedule.remove() - await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 解除驱离", - parse_mode=ParseMode.MARKDOWN_V2) + await message.edit_text( + f"{user_info} 被 {user.mention_markdown_v2()} 解除驱离", parse_mode=ParseMode.MARKDOWN_V2 + ) logger.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理解除封禁") else: logger.warning(f"auth 模块 admin 函数 发现未知命令 result[{result}]") @@ -165,7 +162,6 @@ class GroupJoiningVerification(Plugin): @handler(CallbackQueryHandler, pattern=r"^auth_challenge\|", block=False) @restricts(without_overlapping=True) async def query(self, update: Update, context: CallbackContext) -> None: - async def query_callback(callback_query_data: str) -> Tuple[int, bool, str, str]: _data = callback_query_data.split("|") _user_id = int(_data[1]) @@ -176,8 +172,10 @@ class GroupJoiningVerification(Plugin): _result = _answer.is_correct _answer_encode = _answer.text _question_encode = _question.text - logger.debug(f"query_callback函数返回 user_id[{_user_id}] result[{_result}] \n" - f"question_encode[{_question_encode}] answer_encode[{_answer_encode}]") + logger.debug( + f"query_callback函数返回 user_id[{_user_id}] result[{_result}] \n" + f"question_encode[{_question_encode}] answer_encode[{_answer_encode}]" + ) return _user_id, _result, _question_encode, _answer_encode callback_query = update.callback_query @@ -187,36 +185,43 @@ class GroupJoiningVerification(Plugin): user_id, result, question, answer = await query_callback(callback_query.data) logger.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 点击Auth认证命令 ") if user.id != user_id: - await callback_query.answer(text="这不是你的验证!\n" - "再乱点再按我叫西风骑士团、千岩军和天领奉行了!", show_alert=True) + await callback_query.answer(text="这不是你的验证!\n" "再乱点再按我叫西风骑士团、千岩军和天领奉行了!", show_alert=True) return - logger.info( - f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 认证结果为 {'通过' if result else '失败'}") + logger.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 认证结果为 {'通过' if result else '失败'}") if result: buttons = [[InlineKeyboardButton("驱离", callback_data=f"auth_admin|kick|{user.id}")]] await callback_query.answer(text="验证成功", show_alert=False) await self.restore_member(context, chat.id, user_id) if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user.id}|auth_kick"): schedule.remove() - text = f"{user.mention_markdown_v2()} 验证成功,向着星辰与深渊!\n" \ - f"问题:{escape_markdown(question, version=2)} \n" \ - f"回答:{escape_markdown(answer, version=2)}" + text = ( + f"{user.mention_markdown_v2()} 验证成功,向着星辰与深渊!\n" + f"问题:{escape_markdown(question, version=2)} \n" + f"回答:{escape_markdown(answer, version=2)}" + ) logger.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 验证成功") else: - buttons = [[InlineKeyboardButton("驱离", callback_data=f"auth_admin|kick|{user.id}"), - InlineKeyboardButton("撤回驱离", callback_data=f"auth_admin|unban|{user.id}")]] + buttons = [ + [ + InlineKeyboardButton("驱离", callback_data=f"auth_admin|kick|{user.id}"), + InlineKeyboardButton("撤回驱离", callback_data=f"auth_admin|unban|{user.id}"), + ] + ] await callback_query.answer(text=f"验证失败,请在 {self.time_out} 秒后重试", show_alert=True) await asyncio.sleep(3) - await context.bot.ban_chat_member(chat_id=chat.id, user_id=user_id, - until_date=int(time.time()) + self.kick_time) - text = f"{user.mention_markdown_v2()} 验证失败,已经赶出提瓦特大陆!\n" \ - f"问题:{escape_markdown(question, version=2)} \n" \ - f"回答:{escape_markdown(answer, version=2)}" + await context.bot.ban_chat_member( + chat_id=chat.id, user_id=user_id, until_date=int(time.time()) + self.kick_time + ) + text = ( + f"{user.mention_markdown_v2()} 验证失败,已经赶出提瓦特大陆!\n" + f"问题:{escape_markdown(question, version=2)} \n" + f"回答:{escape_markdown(answer, version=2)}" + ) logger.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 验证失败") try: await message.edit_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode=ParseMode.MARKDOWN_V2) except BadRequest as exc: - if 'are exactly the same as ' in str(exc): + if "are exactly the same as " in str(exc): logger.warning("编辑消息发生异常,可能为用户点按多次键盘导致") else: raise exc @@ -254,8 +259,9 @@ class GroupJoiningVerification(Plugin): await message.reply_text("旅行者!!!派蒙的问题清单你还没给我!!快去私聊我给我问题!") return try: - await context.bot.restrict_chat_member(chat_id=message.chat.id, user_id=user.id, - permissions=ChatPermissions(can_send_messages=False)) + await context.bot.restrict_chat_member( + chat_id=message.chat.id, user_id=user.id, permissions=ChatPermissions(can_send_messages=False) + ) except BadRequest as err: if "Not enough rights" in str(err): logger.warning(f"权限不够 chat_id[{message.chat_id}]") @@ -290,45 +296,59 @@ class GroupJoiningVerification(Plugin): ), ] ) - reply_message = f"*欢迎来到「提瓦特」世界!* \n" \ - f"问题: {escape_markdown(question.text, version=2)} \n" \ - f"请在 {self.time_out}S 内回答问题" - logger.debug(f"发送入群验证问题 question_id[{question.question_id}] question[{question.text}] \n" - f"给{user.full_name}[{user.id}] 在 {chat.title}[{chat.id}]") + reply_message = ( + f"*欢迎来到「提瓦特」世界!* \n" f"问题: {escape_markdown(question.text, version=2)} \n" f"请在 {self.time_out}S 内回答问题" + ) + logger.debug( + f"发送入群验证问题 question_id[{question.question_id}] question[{question.text}] \n" + f"给{user.full_name}[{user.id}] 在 {chat.title}[{chat.id}]" + ) try: - question_message = await message.reply_markdown_v2(reply_message, - reply_markup=InlineKeyboardMarkup(buttons)) + question_message = await message.reply_markdown_v2( + reply_message, reply_markup=InlineKeyboardMarkup(buttons) + ) except BadRequest as exc: await message.reply_text("派蒙分心了一下,不小心忘记你了,你只能先退出群再重新进来吧。") raise exc - context.job_queue.run_once(callback=self.kick_member_job, when=self.time_out, - name=f"{chat.id}|{user.id}|auth_kick", chat_id=chat.id, user_id=user.id, - job_kwargs={"replace_existing": True, "id": f"{chat.id}|{user.id}|auth_kick"}) - context.job_queue.run_once(callback=self.clean_message_job, when=self.time_out, data=message.message_id, - name=f"{chat.id}|{user.id}|auth_clean_join_message", - chat_id=chat.id, user_id=user.id, - job_kwargs={"replace_existing": True, - "id": f"{chat.id}|{user.id}|auth_clean_join_message"}) - context.job_queue.run_once(callback=self.clean_message_job, when=self.time_out, - data=question_message.message_id, - name=f"{chat.id}|{user.id}|auth_clean_question_message", - chat_id=chat.id, user_id=user.id, - job_kwargs={"replace_existing": True, - "id": f"{chat.id}|{user.id}|auth_clean_question_message"}) + context.job_queue.run_once( + callback=self.kick_member_job, + when=self.time_out, + name=f"{chat.id}|{user.id}|auth_kick", + chat_id=chat.id, + user_id=user.id, + job_kwargs={"replace_existing": True, "id": f"{chat.id}|{user.id}|auth_kick"}, + ) + context.job_queue.run_once( + callback=self.clean_message_job, + when=self.time_out, + data=message.message_id, + name=f"{chat.id}|{user.id}|auth_clean_join_message", + chat_id=chat.id, + user_id=user.id, + job_kwargs={"replace_existing": True, "id": f"{chat.id}|{user.id}|auth_clean_join_message"}, + ) + context.job_queue.run_once( + callback=self.clean_message_job, + when=self.time_out, + data=question_message.message_id, + name=f"{chat.id}|{user.id}|auth_clean_question_message", + chat_id=chat.id, + user_id=user.id, + job_kwargs={"replace_existing": True, "id": f"{chat.id}|{user.id}|auth_clean_question_message"}, + ) if self.mtp and (question_message.id - message.id - 1): from pyrogram.errors import BadRequest as MTPBadRequest, FloodWait as MTPFloodWait + try: messages_list = await self.mtp.get_messages( - chat.id, - message_ids=list(range(message.id + 1, question_message.id)) + chat.id, message_ids=list(range(message.id + 1, question_message.id)) ) for find_message in messages_list: if find_message.empty: continue if find_message.from_user and find_message.from_user.id == user.id: await self.mtp.delete_messages(chat_id=chat.id, message_ids=find_message.id) - logger.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 验证缝隙间发送消息" - "现已删除") + logger.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 验证缝隙间发送消息" "现已删除") except MTPFloodWait: logger.warning("调用 mtp 触发洪水限制") continue diff --git a/plugins/system/errorhandler.py b/plugins/system/errorhandler.py index 3c143a3d..70467530 100644 --- a/plugins/system/errorhandler.py +++ b/plugins/system/errorhandler.py @@ -24,7 +24,6 @@ if not os.path.exists(report_dir): class ErrorHandler(Plugin): - @error_handler(block=False) # pylint: disable=E1123, E1120 async def error_handler(self, update: object, context: CallbackContext) -> None: """记录错误并发送消息通知开发人员。 logger the error and send a telegram message to notify the developer.""" @@ -52,20 +51,23 @@ class ErrorHandler(Plugin): file_name = f"error_{update.update_id if isinstance(update, Update) else int(time.time())}.txt" log_file = os.path.join(report_dir, file_name) try: - async with aiofiles.open(log_file, mode='w+', encoding='utf-8') as f: + async with aiofiles.open(log_file, mode="w+", encoding="utf-8") as f: await f.write(error_text) except Exception as exc: logger.error("保存日记失败") logger.exception(exc) try: - if 'make sure that only one bot instance is running' in tb_string: + if "make sure that only one bot instance is running" in tb_string: logger.error("其他机器人在运行,请停止!") return - if 'Message is not modified' in tb_string: + if "Message is not modified" in tb_string: logger.error("消息未修改") return - await context.bot.send_document(chat_id=notice_chat_id, document=open(log_file, "rb"), - caption=f"Error: \"{context.error.__class__.__name__}\"") + await context.bot.send_document( + chat_id=notice_chat_id, + document=open(log_file, "rb"), + caption=f'Error: "{context.error.__class__.__name__}"', + ) except (BadRequest, Forbidden) as exc: logger.error("发送日记失败") logger.exception(exc) @@ -76,12 +78,15 @@ class ErrorHandler(Plugin): try: if effective_message is not None: chat = effective_message.chat - logger.info(f"尝试通知用户 {effective_user.full_name}[{effective_user.id}] " - f"在 {chat.full_name}[{chat.id}]" - f"的 update_id[{update.update_id}] 错误信息") + logger.info( + f"尝试通知用户 {effective_user.full_name}[{effective_user.id}] " + f"在 {chat.full_name}[{chat.id}]" + f"的 update_id[{update.update_id}] 错误信息" + ) text = "出错了呜呜呜 ~ 派蒙这边发生了点问题无法处理!" - await context.bot.send_message(effective_message.chat_id, text, reply_markup=ReplyKeyboardRemove(), - parse_mode=ParseMode.HTML) + await context.bot.send_message( + effective_message.chat_id, text, reply_markup=ReplyKeyboardRemove(), parse_mode=ParseMode.HTML + ) except (BadRequest, Forbidden) as exc: logger.error(f"发送 update_id[{update.update_id}] 错误信息失败 错误信息为") logger.exception(exc) diff --git a/plugins/system/get_chat.py b/plugins/system/get_chat.py index ecd9497f..6b83f230 100644 --- a/plugins/system/get_chat.py +++ b/plugins/system/get_chat.py @@ -25,15 +25,14 @@ class GetChat(Plugin): @staticmethod def parse_group_chat(chat: Chat, admins: List[ChatMember]) -> str: - text = f"群 ID:{chat.id}\n" \ - f"群名称:{chat.title}\n" + text = f"群 ID:{chat.id}\n" f"群名称:{chat.title}\n" if chat.username: text += f"群用户名:{chat.username}\n" if chat.description: text += f"群简介:{chat.description}\n" if admins: for admin in admins: - text += f"{admin.user.full_name} " + text += f'{admin.user.full_name} ' if isinstance(admin, ChatMemberAdministrator): text += "C" if admin.can_change_info else "_" text += "D" if admin.can_delete_messages else "_" @@ -49,9 +48,11 @@ class GetChat(Plugin): return text async def parse_private_chat(self, chat: Chat) -> str: - text = f"MENTION\n" \ - f"用户 ID:{chat.id}\n" \ - f"用户名称:{chat.full_name}\n" + text = ( + f'MENTION\n' + f"用户 ID:{chat.id}\n" + f"用户名称:{chat.full_name}\n" + ) if chat.username: text += f"用户名:@{chat.username}\n" try: @@ -70,8 +71,7 @@ class GetChat(Plugin): await get_genshin_client(chat.id) except CookiesNotFoundError: temp = "UID 绑定" - text += f"{temp}\n" \ - f"游戏 ID:{uid}" + text += f"{temp}\n" f"游戏 ID:{uid}" with contextlib.suppress(Exception): gacha_log, status = await GachaLog.load_history_info(str(chat.id), str(uid)) if status: diff --git a/plugins/system/inline.py b/plugins/system/inline.py index 504a2425..df7e88c4 100644 --- a/plugins/system/inline.py +++ b/plugins/system/inline.py @@ -36,9 +36,11 @@ class Inline(Plugin): id=str(uuid4()), title=weapons_name, description=f"查看武器列表并查询 {weapons_name}", - input_message_content=InputTextMessageContent(f"武器查询{weapons_name}", - parse_mode=ParseMode.MARKDOWN_V2) - )) + input_message_content=InputTextMessageContent( + f"武器查询{weapons_name}", parse_mode=ParseMode.MARKDOWN_V2 + ), + ) + ) elif "查看角色攻略列表并查询" == args[0]: characters_list = await self.wiki_service.get_characters_name_list() for role_name in characters_list: @@ -47,9 +49,11 @@ class Inline(Plugin): id=str(uuid4()), title=role_name, description=f"查看角色攻略列表并查询 {role_name}", - input_message_content=InputTextMessageContent(f"角色攻略查询{role_name}", - parse_mode=ParseMode.MARKDOWN_V2) - )) + input_message_content=InputTextMessageContent( + f"角色攻略查询{role_name}", parse_mode=ParseMode.MARKDOWN_V2 + ), + ) + ) elif "查看角色培养素材列表并查询" == args[0]: characters_list = await self.wiki_service.get_characters_name_list() for role_name in characters_list: @@ -58,9 +62,11 @@ class Inline(Plugin): id=str(uuid4()), title=role_name, description=f"查看角色培养素材列表并查询 {role_name}", - input_message_content=InputTextMessageContent(f"角色培养素材查询{role_name}", - parse_mode=ParseMode.MARKDOWN_V2) - )) + input_message_content=InputTextMessageContent( + f"角色培养素材查询{role_name}", parse_mode=ParseMode.MARKDOWN_V2 + ), + ) + ) if not results_list: results_list.append( @@ -69,7 +75,8 @@ class Inline(Plugin): title="好像找不到问题呢", description="这个问题我也不知道,因为我就是个应急食品。", input_message_content=InputTextMessageContent("这个问题我也不知道,因为我就是个应急食品。"), - )) + ) + ) try: await ilq.answer( results=results_list, diff --git a/plugins/system/log.py b/plugins/system/log.py index 22c0a2df..2dcc0b0f 100644 --- a/plugins/system/log.py +++ b/plugins/system/log.py @@ -13,7 +13,6 @@ debug_log = os.path.join(current_dir, "logs", "debug", "debug.log") class Log(Plugin): - @handler(CommandHandler, command="send_log", block=False) @bot_admins_rights_check async def send_log(self, update: Update, _: CallbackContext): @@ -21,10 +20,10 @@ class Log(Plugin): logger.info(f"用户 {user.full_name}[{user.id}] send_log 命令请求") message = update.effective_message if os.path.exists(error_log) and os.path.getsize(error_log) > 0: - await message.reply_document(open(error_log, mode='rb+'), caption="Error Log") + await message.reply_document(open(error_log, mode="rb+"), caption="Error Log") else: await message.reply_text("错误日记未找到") if os.path.exists(debug_log) and os.path.getsize(debug_log) > 0: - await message.reply_document(open(debug_log, mode='rb+'), caption="Debug Log") + await message.reply_document(open(debug_log, mode="rb+"), caption="Debug Log") else: await message.reply_text("调试日记未找到") diff --git a/plugins/system/new_member.py b/plugins/system/new_member.py index 5caa37c9..49f4b7c1 100644 --- a/plugins/system/new_member.py +++ b/plugins/system/new_member.py @@ -7,7 +7,6 @@ from utils.log import logger class BotJoiningGroupsVerification(Plugin): - def __init__(self, bot_admin_service: BotAdminService = None): self.bot_admin_service = bot_admin_service @@ -25,8 +24,7 @@ class BotJoiningGroupsVerification(Plugin): logger.info(f"用户 {from_user.full_name}[{from_user.id}] 在群 {chat.title}[{chat.id}] 邀请BOT") admin_list = await self.bot_admin_service.get_admin_list() if from_user.id in admin_list: - await context.bot.send_message(message.chat_id, - '感谢邀请小派蒙到本群!请使用 /help 查看咱已经学会的功能。') + await context.bot.send_message(message.chat_id, "感谢邀请小派蒙到本群!请使用 /help 查看咱已经学会的功能。") quit_status = False else: logger.info(f"未知用户 在群 {chat.title}[{chat.id}] 邀请BOT") diff --git a/plugins/system/refresh_metadata.py b/plugins/system/refresh_metadata.py index 978d61c6..eaf02702 100644 --- a/plugins/system/refresh_metadata.py +++ b/plugins/system/refresh_metadata.py @@ -8,16 +8,13 @@ from utils.log import logger class MetadataPlugin(Plugin): - - @handler.command('refresh_metadata') + @handler.command("refresh_metadata") @bot_admins_rights_check async def refresh(self, update: Update, _) -> None: user = update.effective_user message = update.effective_message - logger.info( - f"用户 {user.full_name}[{user.id}] 刷新[bold]metadata[/]缓存命令", extra={'markup': True} - ) + logger.info(f"用户 {user.full_name}[{user.id}] 刷新[bold]metadata[/]缓存命令", extra={"markup": True}) msg = await message.reply_text("正在刷新元数据,请耐心等待...") logger.info("正在从 github 上获取元数据") diff --git a/plugins/system/set_quiz.py b/plugins/system/set_quiz.py index 40cfd770..6bb073bf 100644 --- a/plugins/system/set_quiz.py +++ b/plugins/system/set_quiz.py @@ -23,7 +23,7 @@ from utils.log import logger GET_NEW_CORRECT_ANSWER, GET_NEW_WRONG_ANSWER, QUESTION_EDIT, - SAVE_QUESTION + SAVE_QUESTION, ) = range(10300, 10308) @@ -43,7 +43,7 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): self.time_out = 120 @conversation.entry_point - @handler.command(command='set_quiz', filters=filters.ChatType.PRIVATE, block=True) + @handler.command(command="set_quiz", filters=filters.ChatType.PRIVATE, block=True) @restricts() @bot_admins_rights_check @error_callable @@ -56,32 +56,20 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): quiz_command_data = QuizCommandData() context.chat_data["quiz_command_data"] = quiz_command_data text = f'你好 {user.mention_markdown_v2()} {escape_markdown("!请选择你的操作!")}' - reply_keyboard = [ - ["查看问题", "添加问题"], - ["重载问题"], - ["退出"] - ] + reply_keyboard = [["查看问题", "添加问题"], ["重载问题"], ["退出"]] await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) return CHECK_COMMAND async def view_command(self, update: Update, _: CallbackContext) -> int: _ = self - keyboard = [ - [ - InlineKeyboardButton(text="选择问题", switch_inline_query_current_chat="查看问题 ") - ] - ] - await update.message.reply_text("请回复你要查看的问题", - reply_markup=InlineKeyboardMarkup(keyboard)) + keyboard = [[InlineKeyboardButton(text="选择问题", switch_inline_query_current_chat="查看问题 ")]] + await update.message.reply_text("请回复你要查看的问题", reply_markup=InlineKeyboardMarkup(keyboard)) return CHECK_COMMAND @conversation.state(state=CHECK_QUESTION) @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True) async def check_question(self, update: Update, _: CallbackContext) -> int: - reply_keyboard = [ - ["删除问题"], - ["退出"] - ] + reply_keyboard = [["删除问题"], ["退出"]] await update.message.reply_text("请选择你的操作", reply_markup=ReplyKeyboardMarkup(reply_keyboard)) return CHECK_COMMAND @@ -124,8 +112,7 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): return ConversationHandler.END except ResponseError as error: logger.error("重载问题失败", error) - await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", - reply_markup=ReplyKeyboardRemove()) + await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END await update.message.reply_text("重载成功", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END @@ -137,8 +124,7 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): quiz_command_data.new_question = "" quiz_command_data.new_correct_answer = "" quiz_command_data.status = 1 - await update.message.reply_text("请回复你要添加的问题,或发送 /cancel 取消操作", - reply_markup=ReplyKeyboardRemove()) + await update.message.reply_text("请回复你要添加的问题,或发送 /cancel 取消操作", reply_markup=ReplyKeyboardRemove()) return GET_NEW_QUESTION @conversation.state(state=GET_NEW_QUESTION) @@ -146,8 +132,7 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): async def get_new_question(self, update: Update, context: CallbackContext) -> int: message = update.effective_message quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data") - reply_text = f"问题:`{escape_markdown(update.message.text, version=2)}`\n" \ - f"请填写正确答案:" + reply_text = f"问题:`{escape_markdown(update.message.text, version=2)}`\n" f"请填写正确答案:" quiz_command_data.new_question = message.text await update.message.reply_markdown_v2(reply_text) return GET_NEW_CORRECT_ANSWER @@ -156,19 +141,20 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True) async def get_new_correct_answer(self, update: Update, context: CallbackContext) -> int: quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data") - reply_text = f"正确答案:`{escape_markdown(update.message.text, version=2)}`\n" \ - f"请填写错误答案:" + reply_text = f"正确答案:`{escape_markdown(update.message.text, version=2)}`\n" f"请填写错误答案:" await update.message.reply_markdown_v2(reply_text) quiz_command_data.new_correct_answer = update.message.text return GET_NEW_WRONG_ANSWER @conversation.state(state=GET_NEW_WRONG_ANSWER) @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=True) - @handler.command(command='finish_edit', block=True) + @handler.command(command="finish_edit", block=True) async def get_new_wrong_answer(self, update: Update, context: CallbackContext) -> int: quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data") - reply_text = f"错误答案:`{escape_markdown(update.message.text, version=2)}`\n" \ - f"可继续填写,并使用 {escape_markdown('/finish', version=2)} 结束。" + reply_text = ( + f"错误答案:`{escape_markdown(update.message.text, version=2)}`\n" + f"可继续填写,并使用 {escape_markdown('/finish', version=2)} 结束。" + ) await update.message.reply_markdown_v2(reply_text) quiz_command_data.new_wrong_answer.append(update.message.text) return GET_NEW_WRONG_ANSWER @@ -176,13 +162,14 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): async def finish_edit(self, update: Update, context: CallbackContext): _ = self quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data") - reply_text = f"问题:`{escape_markdown(quiz_command_data.new_question, version=2)}`\n" \ - f"正确答案:`{escape_markdown(quiz_command_data.new_correct_answer, version=2)}`\n" \ - f"错误答案:`{escape_markdown(' '.join(quiz_command_data.new_wrong_answer), version=2)}`" + reply_text = ( + f"问题:`{escape_markdown(quiz_command_data.new_question, version=2)}`\n" + f"正确答案:`{escape_markdown(quiz_command_data.new_correct_answer, version=2)}`\n" + f"错误答案:`{escape_markdown(' '.join(quiz_command_data.new_wrong_answer), version=2)}`" + ) await update.message.reply_markdown_v2(reply_text) reply_keyboard = [["保存并重载配置", "抛弃修改并退出"]] - await update.message.reply_text("请核对问题,并选择下一步操作。", - reply_markup=ReplyKeyboardMarkup(reply_keyboard)) + await update.message.reply_text("请核对问题,并选择下一步操作。", reply_markup=ReplyKeyboardMarkup(reply_keyboard)) return SAVE_QUESTION @conversation.state(state=SAVE_QUESTION) @@ -195,19 +182,18 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): elif update.message.text == "保存并重载配置": if quiz_command_data.status == 1: answer = [ - Answer(text=wrong_answer, is_correct=False) for wrong_answer in - quiz_command_data.new_wrong_answer + Answer(text=wrong_answer, is_correct=False) for wrong_answer in quiz_command_data.new_wrong_answer ] answer.append(Answer(text=quiz_command_data.new_correct_answer, is_correct=True)) - await self.quiz_service.save_quiz( - Question(text=quiz_command_data.new_question)) + await self.quiz_service.save_quiz(Question(text=quiz_command_data.new_question)) await update.message.reply_text("保存成功", reply_markup=ReplyKeyboardRemove()) try: await self.quiz_service.refresh_quiz() except ResponseError as error: logger.error("重载问题失败", error) - await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", - reply_markup=ReplyKeyboardRemove()) + await update.message.reply_text( + "重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", reply_markup=ReplyKeyboardRemove() + ) return ConversationHandler.END await update.message.reply_text("重载配置成功", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END @@ -239,8 +225,7 @@ class SetQuizPlugin(Plugin.Conversation, BasePlugin.Conversation): await self.quiz_service.refresh_quiz() except ResponseError as error: logger.error("重载问题失败", error) - await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", - reply_markup=ReplyKeyboardRemove()) + await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END await update.message.reply_text("重载配置成功", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END diff --git a/plugins/system/start.py b/plugins/system/start.py index 1fe08b96..f04db487 100644 --- a/plugins/system/start.py +++ b/plugins/system/start.py @@ -7,7 +7,6 @@ from utils.decorators.restricts import restricts class StartPlugin(Plugin): - @handler(CommandHandler, command="start", block=False) @restricts() async def start(self, update: Update, context: CallbackContext) -> None: @@ -15,8 +14,10 @@ class StartPlugin(Plugin): message = update.effective_message args = context.args if args is not None and len(args) >= 1 and args[0] == "inline_message": - await message.reply_markdown_v2(f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 !')}\n" - f"{escape_markdown('发送 /help 命令即可查看命令帮助')}") + await message.reply_markdown_v2( + f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 !')}\n" + f"{escape_markdown('发送 /help 命令即可查看命令帮助')}" + ) return await message.reply_markdown_v2(f"你好 {user.mention_markdown_v2()} {escape_markdown('!我是派蒙 !')}") diff --git a/plugins/system/update.py b/plugins/system/update.py index f5c4fa9f..40ff430d 100644 --- a/plugins/system/update.py +++ b/plugins/system/update.py @@ -21,7 +21,6 @@ UPDATE_DATA = os.path.join(current_dir, "data", "update.json") class UpdatePlugin(Plugin): - def __init__(self): self._lock = asyncio.Lock() @@ -59,6 +58,6 @@ class UpdatePlugin(Plugin): await execute(f"{executable} -m poetry install --extras all") logger.info(f"更新成功 正在重启") await reply_text.edit_text("更新成功 正在重启") - async with async_open(UPDATE_DATA, mode='w', encoding='utf-8') as file: + async with async_open(UPDATE_DATA, mode="w", encoding="utf-8") as file: await file.write(reply_text.to_json()) raise SystemExit diff --git a/resources/genshin/abyss_team/example2.html b/resources/genshin/abyss_team/example2.html new file mode 100644 index 00000000..9e0e5cc3 --- /dev/null +++ b/resources/genshin/abyss_team/example2.html @@ -0,0 +1,477 @@ + + + + + abyss + + + + + + +
+
+ 深境螺旋 - 推荐配队 +
+ +
+
UID 12345678
+
版本 3.1
+
+ +
+
推荐配队 1
+ +
+
+
+ +
+
迪奥娜
+
+ +
+
+ +
+
莫娜
+
+ +
+
+ +
+
神里绫华
+
+ +
+
+ +
+
枫原万叶
+
+ +
+
上半
+
+ 使用率 + 5.99% +
+
+
+ +
+
+
+ +
+
香菱
+
+ +
+
+ +
+
雷电将军
+
+ +
+
+ +
+
行秋
+
+ +
+
+ +
+
班尼特
+
+ +
+
上半
+
使用率: 22.34%
+
+
+
+ +
+
推荐配队 2
+ +
+
+
+ +
+
神里绫华
+
+ +
+
+ +
+
甘雨
+
+ +
+
+ +
+
珊瑚宫心海
+
+ +
+
+ +
+
枫原万叶
+
+ +
+
上半
+
使用率: 5.42%
+
+
+ +
+
+
+ +
+
香菱
+
+ +
+
+ +
+
雷电将军
+
+ +
+
+ +
+
行秋
+
+ +
+
+ +
+
班尼特
+
+ +
+
上半
+
使用率: 22.34%
+
+
+
+ +
+
推荐配队 3
+ +
+
+
+ +
+
罗莎莉亚
+
+ +
+
+ +
+
神里绫华
+
+ +
+
+ +
+
珊瑚宫心海
+
+ +
+
+ +
+
枫原万叶
+
+ +
+
上半
+
使用率: 5.13%
+
+
+ +
+
+
+ +
+
香菱
+
+ +
+
+ +
+
雷电将军
+
+ +
+
+ +
+
行秋
+
+ +
+
+ +
+
班尼特
+
+ +
+
上半
+
使用率: 22.34%
+
+
+
+ +
+ 数据来源:游创工坊 +
+
+ + diff --git a/run.py b/run.py index 8d0f4a55..c135eb1e 100644 --- a/run.py +++ b/run.py @@ -5,5 +5,5 @@ def main(): bot.launch() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/test_artifact.py b/tests/test_artifact.py index ff5c0cc6..f60adbe9 100644 --- a/tests/test_artifact.py +++ b/tests/test_artifact.py @@ -20,17 +20,22 @@ async def artifact_rate(): # noinspection PyShadowingNames @pytest.mark.asyncio class TestArtifactOcrRate: - @staticmethod @flaky(3, 1) async def test_rate_artifact(artifact_rate): artifact_attr = { - 'name': '翠绿的猎人之冠', 'pos': '理之冠', 'star': 5, 'level': 20, - 'main_item': {'type': 'cr', 'name': '暴击率', 'value': '31.1%'}, - 'sub_item': [{'type': 'hp', 'name': '生命值', 'value': '9.3%'}, - {'type': 'df', 'name': '防御力', 'value': '46'}, - {'type': 'atk', 'name': '攻击力', 'value': '49'}, - {'type': 'cd', 'name': '暴击伤害', 'value': '10.9%'}]} + "name": "翠绿的猎人之冠", + "pos": "理之冠", + "star": 5, + "level": 20, + "main_item": {"type": "cr", "name": "暴击率", "value": "31.1%"}, + "sub_item": [ + {"type": "hp", "name": "生命值", "value": "9.3%"}, + {"type": "df", "name": "防御力", "value": "46"}, + {"type": "atk", "name": "攻击力", "value": "49"}, + {"type": "cd", "name": "暴击伤害", "value": "10.9%"}, + ], + } assert await artifact_rate.rate_artifact(artifact_attr) @staticmethod diff --git a/tests/test_hyperion_bbs.py b/tests/test_hyperion_bbs.py index c88275a9..966de237 100644 --- a/tests/test_hyperion_bbs.py +++ b/tests/test_hyperion_bbs.py @@ -28,13 +28,13 @@ async def test_get_post_info(hyperion): post_info = await hyperion.get_post_info(2, 29023709) assert post_info assert isinstance(post_info, PostInfo) - assert post_info["post"]["post"]["post_id"] == '29023709' + assert post_info["post"]["post"]["post_id"] == "29023709" assert post_info.post_id == 29023709 assert post_info["post"]["post"]["subject"] == "《原神》长期项目启动·概念PV" assert post_info.subject == "《原神》长期项目启动·概念PV" assert len(post_info["post"]["post"]["images"]) == 1 post_soup = BeautifulSoup(post_info["post"]["post"]["content"], features="html.parser") - assert post_soup.find_all('p') + assert post_soup.find_all("p") # noinspection PyShadowingNames diff --git a/tests/test_wiki.py b/tests/test_wiki.py index 834c5dd0..c2aed798 100644 --- a/tests/test_wiki.py +++ b/tests/test_wiki.py @@ -24,32 +24,32 @@ def event_loop(): @pytest.mark.asyncio class TestWeapon: - @staticmethod @flaky(3, 1) async def test_get_by_id(): - weapon = await Weapon.get_by_id('i_n11417') - assert weapon.name == '原木刀' + weapon = await Weapon.get_by_id("i_n11417") + assert weapon.name == "原木刀" assert weapon.rarity == 4 assert weapon.attack == 43.73 - assert weapon.attribute.type.value == '元素充能效率' - assert weapon.affix.name == '森林的瑞佑' + assert weapon.attribute.type.value == "元素充能效率" + assert weapon.affix.name == "森林的瑞佑" @staticmethod @flaky(3, 1) async def test_get_by_name(): - weapon = await Weapon.get_by_name('风鹰剑') - assert weapon.id == 'i_n11501' + weapon = await Weapon.get_by_name("风鹰剑") + assert weapon.id == "i_n11501" assert weapon.rarity == 5 assert weapon.attack == 47.54 - assert weapon.attribute.type.value == '物理伤害加成' - assert weapon.affix.name == '西风之鹰的抗争' - assert '听凭风引,便是正义与自由之风' in weapon.story + assert weapon.attribute.type.value == "物理伤害加成" + assert weapon.affix.name == "西风之鹰的抗争" + assert "听凭风引,便是正义与自由之风" in weapon.story @staticmethod @flaky(3, 1) async def test_name_list(): from httpx import URL + async for name in Weapon._name_list_generator(with_url=True): assert isinstance(name[0], str) assert isinstance(name[1], URL) @@ -57,34 +57,34 @@ class TestWeapon: @pytest.mark.asyncio class TestCharacter: - @staticmethod @flaky(3, 1) async def test_get_by_id(): - character = await Character.get_by_id('ayaka_002') - assert character.name == '神里绫华' - assert character.title == '白鹭霜华' - assert character.occupation == '社奉行' - assert character.association.value == '稻妻' - assert character.cn_cv == '小N' + character = await Character.get_by_id("ayaka_002") + assert character.name == "神里绫华" + assert character.title == "白鹭霜华" + assert character.occupation == "社奉行" + assert character.association.value == "稻妻" + assert character.cn_cv == "小N" @staticmethod @flaky(3, 1) async def test_get_by_name(): - character = await Character.get_by_name('神里绫华') - assert character.name == '神里绫华' - assert character.title == '白鹭霜华' - assert character.occupation == '社奉行' - assert character.association.value == '稻妻' - assert character.cn_cv == '小N' - main_character = await Character.get_by_name('荧') - assert main_character.constellation == '旅人座' - assert main_character.cn_cv == '宴宁&多多poi' + character = await Character.get_by_name("神里绫华") + assert character.name == "神里绫华" + assert character.title == "白鹭霜华" + assert character.occupation == "社奉行" + assert character.association.value == "稻妻" + assert character.cn_cv == "小N" + main_character = await Character.get_by_name("荧") + assert main_character.constellation == "旅人座" + assert main_character.cn_cv == "宴宁&多多poi" @staticmethod @flaky(3, 1) async def test_name_list(): from httpx import URL + async for name in Character._name_list_generator(with_url=True): assert isinstance(name[0], str) assert isinstance(name[1], URL) @@ -92,41 +92,41 @@ class TestCharacter: @pytest.mark.asyncio class TestMaterial: - @staticmethod @flaky(3, 1) async def test_get_by_id(): - material = await Material.get_by_id('i_504') - assert material.name == '高塔孤王的碎梦' - assert material.type == '武器突破素材' - assert '合成获得' in material.source - assert '巴巴托斯' in material.description + material = await Material.get_by_id("i_504") + assert material.name == "高塔孤王的碎梦" + assert material.type == "武器突破素材" + assert "合成获得" in material.source + assert "巴巴托斯" in material.description - material = await Material.get_by_id('i_483') - assert material.name == '凶将之手眼' - assert material.type == '角色培养素材' - assert '70级以上永恒的守护者挑战奖励' in material.source - assert '所见即所为' in material.description + material = await Material.get_by_id("i_483") + assert material.name == "凶将之手眼" + assert material.type == "角色培养素材" + assert "70级以上永恒的守护者挑战奖励" in material.source + assert "所见即所为" in material.description @staticmethod @flaky(3, 1) async def test_get_by_name(): - material = await Material.get_by_name('地脉的新芽') - assert material.id == 'i_73' - assert material.type == '角色培养素材' - assert '60级以上深渊法师掉落' in material.source - assert '勃发' in material.description + material = await Material.get_by_name("地脉的新芽") + assert material.id == "i_73" + assert material.type == "角色培养素材" + assert "60级以上深渊法师掉落" in material.source + assert "勃发" in material.description - material = await Material.get_by_name('「黄金」的教导') - assert material.id == 'i_431' - assert material.type == '天赋培养素材' + material = await Material.get_by_name("「黄金」的教导") + assert material.id == "i_431" + assert material.type == "天赋培养素材" assert 2 in material.weekdays - assert '土的象' in material.description + assert "土的象" in material.description @staticmethod @flaky(3, 1) async def test_name_list(): from httpx import URL + async for name in Material._name_list_generator(with_url=True): assert isinstance(name[0], str) assert isinstance(name[1], URL) @@ -134,11 +134,11 @@ class TestMaterial: @pytest.mark.asyncio class TestAll: - @staticmethod @flaky(3, 1) async def make_test(target: Type[WikiModel]): from httpx import URL + name_list = await target.get_name_list(with_url=True) name_len = len(name_list) assert name_len != 0 diff --git a/utils/baseobject.py b/utils/baseobject.py index 6343f5a5..617b7e5d 100644 --- a/utils/baseobject.py +++ b/utils/baseobject.py @@ -22,8 +22,7 @@ class BaseObject: return getattr(self, item) except AttributeError as exc: raise KeyError( - f"Objects of type {self.__class__.__name__} don't have an attribute called " - f"`{item}`." + f"Objects of type {self.__class__.__name__} don't have an attribute called " f"`{item}`." ) from exc def __getstate__(self) -> Dict[str, Union[str, object]]: @@ -47,7 +46,11 @@ class BaseObject: # 添加插槽可减少内存使用,并允许更快的属性访问 __slots__ = () - def _get_attrs(self, include_private: bool = False, recursive: bool = False, ) -> Dict[str, Union[str, object]]: + def _get_attrs( + self, + include_private: bool = False, + recursive: bool = False, + ) -> Dict[str, Union[str, object]]: data = {} if not recursive: try: diff --git a/utils/const.py b/utils/const.py index 626a9f5a..7c089fbf 100644 --- a/utils/const.py +++ b/utils/const.py @@ -4,19 +4,24 @@ from pathlib import Path from httpx import URL __all__ = [ - 'PROJECT_ROOT', 'PLUGIN_DIR', 'RESOURCE_DIR', - 'NOT_SET', - 'HONEY_HOST', 'ENKA_HOST', 'AMBR_HOST', 'CELESTIA_HOST', + "PROJECT_ROOT", + "PLUGIN_DIR", + "RESOURCE_DIR", + "NOT_SET", + "HONEY_HOST", + "ENKA_HOST", + "AMBR_HOST", + "CELESTIA_HOST", ] # 项目根目录 -PROJECT_ROOT = Path(__file__).joinpath('../..').resolve() +PROJECT_ROOT = Path(__file__).joinpath("../..").resolve() # Core 目录 -CORE_DIR = PROJECT_ROOT / 'core' +CORE_DIR = PROJECT_ROOT / "core" # 插件目录 -PLUGIN_DIR = PROJECT_ROOT / 'plugins' +PLUGIN_DIR = PROJECT_ROOT / "plugins" # 资源目录 -RESOURCE_DIR = PROJECT_ROOT / 'resources' +RESOURCE_DIR = PROJECT_ROOT / "resources" NOT_SET = object() diff --git a/utils/decorators/error.py b/utils/decorators/error.py index 9e8d1599..93569fcd 100644 --- a/utils/decorators/error.py +++ b/utils/decorators/error.py @@ -22,9 +22,9 @@ async def send_user_notification(update: Update, _: CallbackContext, text: str): logger.warning("错误的消息类型\n" + json.dumps(update_str, indent=2, ensure_ascii=False)) return chat = message.chat - logger.info(f"尝试通知用户 {effective_user.full_name}[{effective_user.id}] " - f"在 {chat.full_name}[{chat.id}]" - f"的 错误信息[{text}]") + logger.info( + f"尝试通知用户 {effective_user.full_name}[{effective_user.id}] " f"在 {chat.full_name}[{chat.id}]" f"的 错误信息[{text}]" + ) try: await message.reply_text(text, reply_markup=ReplyKeyboardRemove(), allow_sending_without_reply=True) except BadRequest as exc: @@ -81,8 +81,7 @@ def error_callable(func: Callable) -> Callable: if exc.retcode in (10001, -100): await send_user_notification(update, context, "出错了呜呜呜 ~ Cookies无效,请尝试重新绑定账户") elif exc.retcode == 10103: - await send_user_notification(update, context, "出错了呜呜呜 ~ Cookie有效,但没有绑定到游戏帐户," - "请尝试重新绑定邮游戏账户") + await send_user_notification(update, context, "出错了呜呜呜 ~ Cookie有效,但没有绑定到游戏帐户," "请尝试重新绑定邮游戏账户") else: logger.warning("Cookie错误") logger.exception(exc) diff --git a/utils/decorators/restricts.py b/utils/decorators/restricts.py index 9fccfce3..cef3020c 100644 --- a/utils/decorators/restricts.py +++ b/utils/decorators/restricts.py @@ -11,8 +11,12 @@ from utils.log import logger _lock = asyncio.Lock() -def restricts(restricts_time: int = 9, restricts_time_of_groups: Optional[int] = None, return_data: Any = None, - without_overlapping: bool = False): +def restricts( + restricts_time: int = 9, + restricts_time_of_groups: Optional[int] = None, + return_data: Any = None, + without_overlapping: bool = False, +): """用于装饰在指定函数预防洪水攻击的装饰器 被修饰的函数生声明必须为 diff --git a/utils/helpers.py b/utils/helpers.py index f7ef9dbf..ae8d04c2 100644 --- a/utils/helpers.py +++ b/utils/helpers.py @@ -20,12 +20,14 @@ from utils.error import UrlResourcesNotFoundError from utils.log import logger from utils.models.base import RegionEnum -T = TypeVar('T') -P = ParamSpec('P') +T = TypeVar("T") +P = ParamSpec("P") -USER_AGENT: str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \ - "Chrome/90.0.4430.72 Safari/537.36" -REQUEST_HEADERS: dict = {'User-Agent': USER_AGENT} +USER_AGENT: str = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/90.0.4430.72 Safari/537.36" +) +REQUEST_HEADERS: dict = {"User-Agent": USER_AGENT} current_dir = os.getcwd() cache_dir = os.path.join(current_dir, "cache") if not os.path.exists(cache_dir): @@ -74,7 +76,7 @@ async def url_to_file(url: str, return_path: bool = False) -> str: if data.status_code != 200: logger.error(f"url_to_file 获取url[{url}] 错误 status_code[f{data.status_code}]") raise UrlResourcesNotFoundError(url) - async with aiofiles.open(file_dir, mode='wb') as f: + async with aiofiles.open(file_dir, mode="wb") as f: await f.write(data.content) logger.debug(f"url_to_file 获取url[{url}] 并下载到 file_dir[{file_dir}]") @@ -101,8 +103,9 @@ async def get_genshin_client(user_id: int, region: Optional[RegionEnum] = None, client = genshin.Client(cookies=cookies, game=types.Game.GENSHIN, region=types.Region.CHINESE, uid=uid) elif region == RegionEnum.HOYOLAB: uid = user.genshin_uid - client = genshin.Client(cookies=cookies, - game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn", uid=uid) + client = genshin.Client( + cookies=cookies, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn", uid=uid + ) else: raise TypeError("region is not RegionEnum.NULL") return client @@ -121,8 +124,9 @@ async def get_public_genshin_client(user_id: int) -> Tuple[Client, Optional[int] client = genshin.Client(cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.CHINESE) elif region == RegionEnum.HOYOLAB: uid = user.genshin_uid - client = genshin.Client(cookies=cookies.cookies, - game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn") + client = genshin.Client( + cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn" + ) else: raise TypeError("region is not RegionEnum.NULL") return client, uid @@ -142,25 +146,18 @@ def region_server(uid: Union[int, str]) -> RegionEnum: async def execute(command, pass_error=True): - """ Executes command and returns output, with the option of enabling stderr. """ - executor = await create_subprocess_shell( - command, - stdout=PIPE, - stderr=PIPE, - stdin=PIPE - ) + """Executes command and returns output, with the option of enabling stderr.""" + executor = await create_subprocess_shell(command, stdout=PIPE, stderr=PIPE, stdin=PIPE) stdout, stderr = await executor.communicate() if pass_error: try: - result = str(stdout.decode().strip()) \ - + str(stderr.decode().strip()) + result = str(stdout.decode().strip()) + str(stderr.decode().strip()) except UnicodeDecodeError: - result = str(stdout.decode('gbk').strip()) \ - + str(stderr.decode('gbk').strip()) + result = str(stdout.decode("gbk").strip()) + str(stderr.decode("gbk").strip()) else: try: result = str(stdout.decode().strip()) except UnicodeDecodeError: - result = str(stdout.decode('gbk').strip()) + result = str(stdout.decode("gbk").strip()) return result diff --git a/utils/log/_file.py b/utils/log/_file.py index 68a7ea60..7083fe43 100644 --- a/utils/log/_file.py +++ b/utils/log/_file.py @@ -4,7 +4,7 @@ from pathlib import Path from types import TracebackType from typing import AnyStr, IO, Iterable, Iterator, List, Optional, Type -__all__ = ['FileIO'] +__all__ = ["FileIO"] # noinspection SpellCheckingInspection @@ -18,12 +18,12 @@ class FileIO(IO[str]): today = date.today() if self.file.exists(): if not self.file.is_file(): - raise RuntimeError(f'日志文件冲突, 请删除文件夹 \"{str(self.file.resolve())}\"') + raise RuntimeError(f'日志文件冲突, 请删除文件夹 "{str(self.file.resolve())}"') if self.file_stream is None or self.file_stream.closed: - self.file_stream = self.file.open(mode='a+', encoding='utf-8') + self.file_stream = self.file.open(mode="a+", encoding="utf-8") modify_date = date.fromtimestamp(os.stat(self.file).st_mtime) else: - self.file_stream = self.file.open(mode='a+', encoding='utf-8') + self.file_stream = self.file.open(mode="a+", encoding="utf-8") modify_date = today if modify_date < today: if self.file_stream is not None and not self.file_stream.closed: @@ -31,13 +31,13 @@ class FileIO(IO[str]): log_path = self.path.joinpath(f'{modify_date.strftime("%Y-%m-%d")}.log') if log_path.exists(): # 转存日志 - with open(log_path, mode='a+', encoding='utf-8') as file: - file.write('\n') - with open(self.file, mode='r+', encoding='utf-8') as f: + with open(log_path, mode="a+", encoding="utf-8") as file: + file.write("\n") + with open(self.file, mode="r+", encoding="utf-8") as f: file.writelines(f.readlines()) else: self.file.rename(self.path.joinpath(f'{modify_date.strftime("%Y-%m-%d")}.log')) - self.file_stream = self.file.open(mode='a+', encoding='utf-8') + self.file_stream = self.file.open(mode="a+", encoding="utf-8") return self.file_stream def close(self) -> None: @@ -94,6 +94,7 @@ class FileIO(IO[str]): def __enter__(self) -> IO[AnyStr]: return self._get_file().__enter__() - def __exit__(self, __t: Optional[Type[BaseException]], __value: Optional[BaseException], - __traceback: Optional[TracebackType]) -> None: + def __exit__( + self, __t: Optional[Type[BaseException]], __value: Optional[BaseException], __traceback: Optional[TracebackType] + ) -> None: return self._get_file().__exit__(__t, __value, __traceback) diff --git a/utils/log/_logger.py b/utils/log/_logger.py index d0355017..44b5cbb4 100644 --- a/utils/log/_logger.py +++ b/utils/log/_logger.py @@ -7,7 +7,7 @@ import traceback as traceback_ from datetime import datetime from multiprocessing import RLock as Lock from pathlib import Path -from typing import (Any, Callable, Dict, Iterable, List, Literal, Mapping, Optional, TYPE_CHECKING, Tuple, Union) +from typing import Any, Callable, Dict, Iterable, List, Literal, Mapping, Optional, TYPE_CHECKING, Tuple, Union import ujson as json from rich.columns import Columns @@ -65,22 +65,20 @@ __initialized__ = False FormatTimeCallable = Callable[[datetime], Text] config = BotConfig() -logging.addLevelName(5, 'TRACE') -logging.addLevelName(25, 'SUCCESS') -color_system: Literal['windows', 'truecolor'] -if sys.platform == 'win32': - color_system = 'windows' +logging.addLevelName(5, "TRACE") +logging.addLevelName(25, "SUCCESS") +color_system: Literal["windows", "truecolor"] +if sys.platform == "win32": + color_system = "windows" else: - color_system = 'truecolor' + color_system = "truecolor" # noinspection SpellCheckingInspection -log_console = Console( - color_system=color_system, theme=Theme(DEFAULT_STYLE), width=config.logger.width -) +log_console = Console(color_system=color_system, theme=Theme(DEFAULT_STYLE), width=config.logger.width) class Traceback(BaseTraceback): def __init__(self, *args, **kwargs): - kwargs.update({'show_locals': True, 'max_frames': config.logger.traceback_max_frames}) + kwargs.update({"show_locals": True, "max_frames": config.logger.traceback_max_frames}) super(Traceback, self).__init__(*args, **kwargs) self.theme = PygmentsSyntaxTheme(MonokaiProStyle) @@ -99,9 +97,7 @@ class Traceback(BaseTraceback): def read_code(filename: str) -> str: code = code_cache.get(filename) if code is None: - with open( - filename, "rt", encoding="utf-8", errors="replace" - ) as code_file: + with open(filename, "rt", encoding="utf-8", errors="replace") as code_file: code = code_file.read() code_cache[filename] = code return code @@ -110,6 +106,7 @@ class Traceback(BaseTraceback): def render_locals(frame: Frame) -> Iterable["ConsoleRenderable"]: if frame.locals: from rich.scope import render_scope + yield render_scope( frame.locals, title="locals", @@ -144,8 +141,7 @@ class Traceback(BaseTraceback): first = frame_index == 0 frame_filename = frame.filename - suppressed = any( - frame_filename.startswith(path) for path in self.suppress) + suppressed = any(frame_filename.startswith(path) for path in self.suppress) text = Text.assemble( path_highlighter(Text(frame.filename, style="pygments.string")), @@ -218,15 +214,15 @@ class LogRender(DefaultLogRender): self.time_format = config.logger.time_format def __call__( - self, - console: "Console", - renderables: Iterable["ConsoleRenderable"], - log_time: Optional[datetime] = None, - time_format: Optional[Union[str, FormatTimeCallable]] = None, - level: TextType = "", - path: Optional[str] = None, - line_no: Optional[int] = None, - link_path: Optional[str] = None, + self, + console: "Console", + renderables: Iterable["ConsoleRenderable"], + log_time: Optional[datetime] = None, + time_format: Optional[Union[str, FormatTimeCallable]] = None, + level: TextType = "", + path: Optional[str] = None, + line_no: Optional[int] = None, + link_path: Optional[str] = None, ) -> "Table": from rich.containers import Renderables from rich.table import Table @@ -239,7 +235,7 @@ class LogRender(DefaultLogRender): if path: output.add_column(style="log.path") if line_no: - output.add_column(style='log.line_no', width=4) + output.add_column(style="log.line_no", width=4) row: List["RenderableType"] = [] if self.show_time: log_time = log_time or log_console.get_datetime() @@ -259,15 +255,11 @@ class LogRender(DefaultLogRender): row.append(Renderables(renderables)) if path: path_text = Text() - path_text.append( - path, style=f"link file://{link_path}" if link_path else "" - ) + path_text.append(path, style=f"link file://{link_path}" if link_path else "") row.append(path_text) line_no_text = Text() - line_no_text.append( - str(line_no), style=f"link file://{link_path}#{line_no}" if link_path else "" - ) + line_no_text.append(str(line_no), style=f"link file://{link_path}#{line_no}" if link_path else "") row.append(line_no_text) output.add_row(*row) @@ -284,18 +276,19 @@ class Handler(DefaultRichHandler): self.keywords = self.KEYWORDS + config.logger.render_keywords def render( - self, - *, - record: "LogRecord", - traceback: Optional[Traceback], - message_renderable: Optional["ConsoleRenderable"], + self, + *, + record: "LogRecord", + traceback: Optional[Traceback], + message_renderable: Optional["ConsoleRenderable"], ) -> "ConsoleRenderable": - if record.pathname != '': + if record.pathname != "": try: path = str(Path(record.pathname).relative_to(PROJECT_ROOT)) - path = path.split('.')[0].replace(os.sep, '.') + path = path.split(".")[0].replace(os.sep, ".") except ValueError: import site + path = None for s in site.getsitepackages(): try: @@ -306,10 +299,10 @@ class Handler(DefaultRichHandler): if path is None: path = "" else: - path = path.split('.')[0].replace(os.sep, '.') + path = path.split(".")[0].replace(os.sep, ".") else: - path = '' - path = path.replace('lib.site-packages.', '') + path = "" + path = path.replace("lib.site-packages.", "") _level = self.get_level_text(record) time_format = None if self.formatter is None else self.formatter.datefmt log_time = datetime.fromtimestamp(record.created) @@ -318,12 +311,8 @@ class Handler(DefaultRichHandler): self.console, ( [message_renderable] - if not traceback else - ( - [message_renderable, traceback] - if message_renderable is not None else - [traceback] - ) + if not traceback + else ([message_renderable, traceback] if message_renderable is not None else [traceback]) ), log_time=log_time, time_format=time_format, @@ -334,18 +323,15 @@ class Handler(DefaultRichHandler): ) return log_renderable - def render_message( - self, record: "LogRecord", message: Any - ) -> "ConsoleRenderable": + def render_message(self, record: "LogRecord", message: Any) -> "ConsoleRenderable": use_markup = getattr(record, "markup", self.markup) if isinstance(message, str): - message_text = ( - Text.from_markup(message) if use_markup else Text(message) - ) + message_text = Text.from_markup(message) if use_markup else Text(message) highlighter = getattr(record, "highlighter", self.highlighter) else: from rich.highlighter import JSONHighlighter from rich.json import JSON + highlighter = JSONHighlighter() message_text = JSON.from_data(message, indent=4).text @@ -364,11 +350,7 @@ class Handler(DefaultRichHandler): def emit(self, record: "LogRecord") -> None: message = self.format(record) _traceback = None - if ( - self.rich_tracebacks - and record.exc_info - and record.exc_info != (None, None, None) - ): + if self.rich_tracebacks and record.exc_info and record.exc_info != (None, None, None): exc_type, exc_value, exc_traceback = record.exc_info if exc_type is None or exc_value is None: raise ValueError(record) @@ -430,28 +412,33 @@ class FileHandler(Handler): class Logger(logging.Logger): def success( - self, - msg: Any, *args: Any, - exc_info: Optional[ExceptionInfoType] = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Optional[Mapping[str, Any]] = None + self, + msg: Any, + *args: Any, + exc_info: Optional[ExceptionInfoType] = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Optional[Mapping[str, Any]] = None, ) -> None: return self.log(25, msg, *args, exc_info=exc_info, stack_info=stack_info, stacklevel=stacklevel, extra=extra) def exception( - self, - msg: Any = NOT_SET, - *args: Any, - exc_info: Optional[ExceptionInfoType] = True, - stack_info: bool = False, - stacklevel: int = 1, - extra: Optional[Mapping[str, Any]] = None, - **kwargs + self, + msg: Any = NOT_SET, + *args: Any, + exc_info: Optional[ExceptionInfoType] = True, + stack_info: bool = False, + stacklevel: int = 1, + extra: Optional[Mapping[str, Any]] = None, + **kwargs, ) -> None: super(Logger, self).exception( - "" if msg is NOT_SET else msg, *args, - exc_info=exc_info, stack_info=stack_info, stacklevel=stacklevel, extra=extra + "" if msg is NOT_SET else msg, + *args, + exc_info=exc_info, + stack_info=stack_info, + stacklevel=stacklevel, + extra=extra, ) def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> Tuple[str, int, str, Optional[str]]: @@ -470,17 +457,17 @@ class Logger(logging.Logger): filename = os.path.normcase(code.co_filename) if filename in [ os.path.normcase(Path(__file__).resolve()), - os.path.normcase(logging.addLevelName.__code__.co_filename) + os.path.normcase(logging.addLevelName.__code__.co_filename), ]: frame = frame.f_back continue sinfo = None if stack_info: sio = io.StringIO() - sio.write('Stack (most recent call last):\n') + sio.write("Stack (most recent call last):\n") traceback_.print_stack(frame, file=sio) sinfo = sio.getvalue() - if sinfo[-1] == '\n': + if sinfo[-1] == "\n": sinfo = sinfo[:-1] sio.close() rv = (code.co_filename, frame.f_lineno, code.co_name, sinfo) @@ -496,10 +483,10 @@ with _lock: handler, debug_handler, error_handler = ( Handler(locals_max_length=4), FileHandler(level=10, path=config.logger.path.joinpath("debug/debug.log")), - FileHandler(level=40, path=config.logger.path.joinpath("error/error.log")) + FileHandler(level=40, path=config.logger.path.joinpath("error/error.log")), ) - log_filter = logging.Filter('TGPaimon') + log_filter = logging.Filter("TGPaimon") handler.addFilter(log_filter) debug_handler.addFilter(log_filter) @@ -514,7 +501,7 @@ with _lock: warnings_logger.addHandler(handler) warnings_logger.addHandler(debug_handler) - logger = Logger('TGPaimon', level_) + logger = Logger("TGPaimon", level_) logger.addHandler(handler) logger.addHandler(debug_handler) logger.addHandler(error_handler) diff --git a/utils/log/_style.py b/utils/log/_style.py index 05e3a755..45475fe3 100644 --- a/utils/log/_style.py +++ b/utils/log/_style.py @@ -17,10 +17,23 @@ from pygments.token import ( from rich.style import Style __all__ = [ - 'MonokaiProStyle', 'DEFAULT_STYLE', - 'BACKGROUND', 'FOREGROUND', - 'BLACK', 'DARK_GREY', 'LIGHT_GREY', 'GREY', 'RED', 'MAGENTA', 'GREEN', - 'YELLOW', 'ORANGE', 'PURPLE', 'BLUE', 'CYAN', 'WHITE' + "MonokaiProStyle", + "DEFAULT_STYLE", + "BACKGROUND", + "FOREGROUND", + "BLACK", + "DARK_GREY", + "LIGHT_GREY", + "GREY", + "RED", + "MAGENTA", + "GREEN", + "YELLOW", + "ORANGE", + "PURPLE", + "BLUE", + "CYAN", + "WHITE", ] BACKGROUND = "#272822" @@ -49,17 +62,12 @@ class MonokaiProStyle(PyStyle): # No corresponding class for the following: Text: WHITE, # class: '' Error: "#fc618d bg:#1e0010", # class: 'err' - Comment: LIGHT_GREY, # class: 'c' Comment.Multiline: YELLOW, # class: 'cm' - Keyword: RED, # class: 'k' Keyword.Namespace: GREEN, # class: 'kn' - Operator: RED, # class: 'o' - Punctuation: WHITE, # class: 'p' - Name: WHITE, # class: 'n' Name.Attribute: GREEN, # class: 'na' - to be revised Name.Builtin: CYAN, # class: 'nb' @@ -69,15 +77,11 @@ class MonokaiProStyle(PyStyle): Name.Exception: GREEN, # class: 'ne' Name.Function: GREEN, # class: 'nf' Name.Property: ORANGE, # class: 'py' - Number: PURPLE, # class: 'm' - Literal: PURPLE, # class: 'l' Literal.Date: ORANGE, # class: 'ld' - String: YELLOW, # class: 's' String.Regex: ORANGE, # class: 'sr' - Generic.Deleted: YELLOW, # class: 'gd', Generic.Emph: "italic", # class: 'ge' Generic.Inserted: GREEN, # class: 'gi' @@ -122,7 +126,6 @@ DEFAULT_STYLE: Dict[str, Style] = { "blue": Style(color=BLUE), "cyan": Style(color=CYAN), "white": Style(color=WHITE), - # inspect "inspect.attr": Style(color=YELLOW, italic=True), "inspect.attr.dunder": Style(color=YELLOW, italic=True, dim=True), @@ -134,31 +137,27 @@ DEFAULT_STYLE: Dict[str, Style] = { "inspect.help": Style(color=CYAN), "inspect.doc": Style(dim=True), "inspect.value.border": Style(color=GREEN), - # live "live.ellipsis": Style(bold=True, color=RED), - # layout "layout.tree.row": Style(dim=False, color=RED), "layout.tree.column": Style(dim=False, color=BLUE), - # log "logging.keyword": Style(bold=True, color=ORANGE), "logging.level.notset": Style(color=DARK_GREY, dim=True), "logging.level.trace": Style(color=GREY), "logging.level.debug": Style(color=LIGHT_GREY, bold=True), - "logging.level.info": Style(color='white'), - "logging.level.plugin": Style(color='cyan'), - "logging.level.success": Style(color='green'), - "logging.level.warning": Style(color='yellow'), - "logging.level.error": Style(color='red'), - "logging.level.critical": Style(color='red', bgcolor='#1e0010', bold=True), + "logging.level.info": Style(color="white"), + "logging.level.plugin": Style(color="cyan"), + "logging.level.success": Style(color="green"), + "logging.level.warning": Style(color="yellow"), + "logging.level.error": Style(color="red"), + "logging.level.critical": Style(color="red", bgcolor="#1e0010", bold=True), "log.level": Style.null(), "log.time": Style(color=CYAN, dim=True), "log.message": Style.null(), "log.path": Style(dim=True), "log.line_no": Style(color=CYAN, bold=True, italic=False, dim=True), - # repr "repr.ellipsis": Style(color=YELLOW), "repr.indent": Style(color=GREEN, dim=True), @@ -182,16 +181,13 @@ DEFAULT_STYLE: Dict[str, Style] = { "repr.bool_true": Style(color="bright_green", italic=True), "repr.bool_false": Style(color="bright_red", italic=True), "repr.none": Style(color=MAGENTA, italic=True), - "repr.url": Style( - underline=True, color="bright_blue", italic=False, bold=False - ), + "repr.url": Style(underline=True, color="bright_blue", italic=False, bold=False), "repr.uuid": Style(color="bright_yellow", bold=False), "repr.call": Style(color=MAGENTA, bold=True), "repr.path": Style(color=MAGENTA), "repr.filename": Style(color="bright_magenta"), "rule.line": Style(color="bright_green"), "rule.text": Style.null(), - # json "json.brace": Style(bold=True), "json.bool_true": Style(color="bright_green", italic=True), @@ -200,30 +196,25 @@ DEFAULT_STYLE: Dict[str, Style] = { "json.number": Style(color=CYAN, bold=True, italic=False), "json.str": Style(color=GREEN, italic=False, bold=False), "json.key": Style(color=BLUE, bold=True), - # prompt "prompt": Style.null(), "prompt.choices": Style(color=MAGENTA, bold=True), "prompt.default": Style(color=CYAN, bold=True), "prompt.invalid": Style(color=RED), "prompt.invalid.choice": Style(color=RED), - # pretty "pretty": Style.null(), - # scope "scope.border": Style(color=BLUE), "scope.key": Style(color=YELLOW, italic=True), "scope.key.special": Style(color=YELLOW, italic=True, dim=True), "scope.equals": Style(color=RED), - # table "table.header": Style(bold=True), "table.footer": Style(bold=True), "table.cell": Style.null(), "table.title": Style(italic=True), "table.caption": Style(italic=True, dim=True), - # traceback "traceback.error": Style(color=RED, italic=True), "traceback.border.syntax_error": Style(color="bright_red"), @@ -233,13 +224,11 @@ DEFAULT_STYLE: Dict[str, Style] = { "traceback.exc_type": Style(color="bright_red", bold=True), "traceback.exc_value": Style.null(), "traceback.offset": Style(color="bright_red", bold=True), - # bar "bar.back": Style(color="grey23"), "bar.complete": Style(color="rgb(249,38,114)"), "bar.finished": Style(color="rgb(114,156,31)"), "bar.pulse": Style(color="rgb(249,38,114)"), - # progress "progress.description": Style.null(), "progress.filesize": Style(color=GREEN), @@ -251,11 +240,9 @@ DEFAULT_STYLE: Dict[str, Style] = { "progress.data.speed": Style(color=RED), "progress.spinner": Style(color=GREEN), "status.spinner": Style(color=GREEN), - # tree "tree": Style(), "tree.line": Style(), - # markdown "markdown.paragraph": Style(), "markdown.text": Style(), diff --git a/utils/models/base.py b/utils/models/base.py index 413a6a41..2fd352c0 100644 --- a/utils/models/base.py +++ b/utils/models/base.py @@ -7,8 +7,9 @@ from utils.baseobject import BaseObject class Stat: - def __init__(self, view_num: int = 0, reply_num: int = 0, like_num: int = 0, bookmark_num: int = 0, - forward_num: int = 0): + def __init__( + self, view_num: int = 0, reply_num: int = 0, like_num: int = 0, bookmark_num: int = 0, forward_num: int = 0 + ): self.forward_num = forward_num # 关注数 self.bookmark_num = bookmark_num # 收藏数 self.like_num = like_num # 喜欢数 @@ -17,7 +18,6 @@ class Stat: class ArtworkInfo: - def __init__(self): self.user_id: int = 0 self.artwork_id: int = 0 # 作品ID @@ -32,7 +32,6 @@ class ArtworkInfo: class ArtworkImage: - def __init__(self, art_id: int, page: int = 0, is_error: bool = False, data: bytes = b""): self.art_id = art_id self.data = data @@ -50,14 +49,20 @@ class RegionEnum(Enum): 查了一下确实有点意思 考虑到大部分重要的功能确实是在移动端实现了 干脆用这个还好听 )""" + NULL = None HYPERION = 1 # 米忽悠国服 hyperion HOYOLAB = 2 # 米忽悠国际服 hoyolab class GameItem(BaseObject): - def __init__(self, item_id: int = 0, name: str = "", item_type: Union[Enum, str, int] = "", - value: Union[Enum, str, int, bool, float] = 0): + def __init__( + self, + item_id: int = 0, + name: str = "", + item_type: Union[Enum, str, int] = "", + value: Union[Enum, str, int, bool, float] = 0, + ): self.item_id = item_id self.name = name # 名称 self.type = item_type # 类型 @@ -67,9 +72,9 @@ class GameItem(BaseObject): class ModuleInfo: - - def __init__(self, file_name: Optional[str] = None, plugin_name: Optional[str] = None, - relative_path: Optional[str] = None): + def __init__( + self, file_name: Optional[str] = None, plugin_name: Optional[str] = None, relative_path: Optional[str] = None + ): self.relative_path = relative_path self.module_name = plugin_name self.file_name = file_name diff --git a/utils/patch/methods.py b/utils/patch/methods.py index c8c28574..8f667182 100644 --- a/utils/patch/methods.py +++ b/utils/patch/methods.py @@ -1,11 +1,11 @@ def patch(obj): def is_patchable(item): - return getattr(item[1], 'patchable', False) + return getattr(item[1], "patchable", False) def wrapper(container): for name, func in filter(is_patchable, container.__dict__.items()): old = getattr(obj, name, None) - setattr(obj, f'old_{name}', old) + setattr(obj, f"old_{name}", old) setattr(obj, name, func) return container diff --git a/utils/typedefs.py b/utils/typedefs.py index f47dbe15..30f81fb6 100644 --- a/utils/typedefs.py +++ b/utils/typedefs.py @@ -4,20 +4,13 @@ from typing import Any, Dict, Optional, Tuple, Type, Union from httpx import URL -__all__ = [ - 'StrOrPath', 'StrOrURL', 'StrOrInt', - 'SysExcInfoType', 'ExceptionInfoType', - 'JSONDict', 'JSONType' -] +__all__ = ["StrOrPath", "StrOrURL", "StrOrInt", "SysExcInfoType", "ExceptionInfoType", "JSONDict", "JSONType"] StrOrPath = Union[str, Path] StrOrURL = Union[str, URL] StrOrInt = Union[str, int] -SysExcInfoType = Union[ - Tuple[Type[BaseException], BaseException, Optional[TracebackType]], - Tuple[None, None, None] -] +SysExcInfoType = Union[Tuple[Type[BaseException], BaseException, Optional[TracebackType]], Tuple[None, None, None]] ExceptionInfoType = Union[bool, SysExcInfoType, BaseException] JSONDict = Dict[str, Any] JSONType = Union[JSONDict, list]