From a98d98cd1279ca040decbae8a12646db9e43aa90 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 22 Nov 2021 23:21:26 +0800 Subject: [PATCH] :art: format code using black and isort --- .style.yapf | 2 - nonebot/__init__.py | 36 +- nonebot/adapters/__init__.py | 6 +- nonebot/adapters/_bot.py | 17 +- nonebot/adapters/_event.py | 3 +- nonebot/adapters/_message.py | 31 +- nonebot/adapters/_template.py | 74 +- nonebot/config.py | 50 +- nonebot/dependencies/__init__.py | 137 ++-- nonebot/dependencies/models.py | 28 +- nonebot/dependencies/utils.py | 8 +- nonebot/drivers/__init__.py | 42 +- nonebot/drivers/aiohttp.py | 112 +-- nonebot/drivers/fastapi.py | 165 +++-- nonebot/drivers/quart.py | 96 ++- nonebot/exception.py | 4 + nonebot/handler.py | 48 +- nonebot/log.py | 27 +- nonebot/matcher.py | 271 ++++---- nonebot/message.py | 154 +++-- nonebot/params.py | 40 +- nonebot/permission.py | 46 +- nonebot/plugin/__init__.py | 5 +- nonebot/plugin/export.py | 3 +- nonebot/plugin/load.py | 16 +- nonebot/plugin/manager.py | 47 +- nonebot/plugin/on.py | 397 ++++++----- nonebot/plugin/on.pyi | 648 +++++++++--------- nonebot/plugin/plugin.py | 1 + nonebot/plugins/echo.py | 9 +- nonebot/plugins/single_session.py | 7 +- nonebot/rule.py | 101 +-- nonebot/typing.py | 30 +- nonebot/utils.py | 34 +- .../nonebot/adapters/cqhttp/bot.py | 146 ++-- .../nonebot/adapters/cqhttp/bot.pyi | 1 - .../nonebot/adapters/cqhttp/config.py | 9 +- .../nonebot/adapters/cqhttp/event.py | 125 +++- .../nonebot/adapters/cqhttp/exception.py | 8 +- .../nonebot/adapters/cqhttp/message.py | 186 ++--- .../nonebot/adapters/cqhttp/permission.py | 14 +- .../nonebot/adapters/cqhttp/utils.py | 12 +- .../nonebot-adapter-cqhttp/pyproject.toml | 15 + .../nonebot/adapters/ding/bot.py | 99 +-- .../nonebot/adapters/ding/config.py | 1 + .../nonebot/adapters/ding/event.py | 10 +- .../nonebot/adapters/ding/exception.py | 14 +- .../nonebot/adapters/ding/message.py | 43 +- .../nonebot/adapters/ding/utils.py | 12 +- packages/nonebot-adapter-ding/pyproject.toml | 15 + .../nonebot/adapters/feishu/__init__.py | 2 +- .../nonebot/adapters/feishu/bot.py | 155 +++-- .../nonebot/adapters/feishu/config.py | 9 +- .../nonebot/adapters/feishu/event.py | 8 +- .../nonebot/adapters/feishu/exception.py | 12 +- .../nonebot/adapters/feishu/message.py | 97 +-- .../nonebot/adapters/feishu/utils.py | 13 +- .../nonebot-adapter-feishu/pyproject.toml | 15 + .../nonebot/adapters/mirai/bot.py | 313 +++++---- .../nonebot/adapters/mirai/config.py | 11 +- .../nonebot/adapters/mirai/event/__init__.py | 65 +- .../nonebot/adapters/mirai/event/base.py | 36 +- .../nonebot/adapters/mirai/event/message.py | 14 +- .../nonebot/adapters/mirai/event/meta.py | 8 +- .../nonebot/adapters/mirai/event/notice.py | 37 +- .../nonebot/adapters/mirai/event/request.py | 144 ++-- .../nonebot/adapters/mirai/message.py | 132 ++-- .../nonebot/adapters/mirai/utils.py | 56 +- packages/nonebot-adapter-mirai/pyproject.toml | 15 + .../nonebot_plugin_docs/__init__.py | 8 +- .../nonebot_plugin_docs/drivers/fastapi.py | 4 +- packages/nonebot-plugin-docs/pyproject.toml | 15 + poetry.lock | 217 +++++- pyproject.toml | 15 +- tests/bot.py | 8 +- tests/test_plugins/test_ding.py | 144 ++-- tests/test_plugins/test_feishu.py | 5 +- tests/test_plugins/test_get_export.py | 1 - tests/test_plugins/test_group/__init__.py | 2 +- tests/test_plugins/test_group/commands.py | 3 +- tests/test_plugins/test_group/matches.py | 3 +- tests/test_plugins/test_metaevent.py | 2 +- tests/test_plugins/test_mirai.py | 8 +- tests/test_plugins/test_overload.py | 2 +- tests/test_plugins/test_permission.py | 2 +- tests/test_plugins/test_shell.py | 2 +- 86 files changed, 2893 insertions(+), 2095 deletions(-) delete mode 100644 .style.yapf diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index 0e9640c2..00000000 --- a/.style.yapf +++ /dev/null @@ -1,2 +0,0 @@ -[style] -based_on_style = google diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 809b696c..9d90ac19 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -40,8 +40,7 @@ from nonebot.log import logger, default_filter from nonebot.drivers import Driver, ReverseDriver try: - _dist: pkg_resources.Distribution = pkg_resources.get_distribution( - "nonebot2") + _dist: pkg_resources.Distribution = pkg_resources.get_distribution("nonebot2") __version__ = _dist.version VERSION = _dist.parsed_version except pkg_resources.DistributionNotFound: @@ -100,8 +99,8 @@ def get_app() -> Any: """ driver = get_driver() assert isinstance( - driver, - ReverseDriver), "app object is only available for reverse driver" + driver, ReverseDriver + ), "app object is only available for reverse driver" return driver.server_app @@ -128,8 +127,8 @@ def get_asgi() -> Any: """ driver = get_driver() assert isinstance( - driver, - ReverseDriver), "asgi object is only available for reverse driver" + driver, ReverseDriver + ), "asgi object is only available for reverse driver" return driver.asgi @@ -226,17 +225,23 @@ def init(*, _env_file: Optional[str] = None, **kwargs): if not _driver: logger.success("NoneBot is initializing...") env = Env() - config = Config(**kwargs, - _common_config=env.dict(), - _env_file=_env_file or f".env.{env.environment}") + config = Config( + **kwargs, + _common_config=env.dict(), + _env_file=_env_file or f".env.{env.environment}", + ) default_filter.level = ( - "DEBUG" if config.debug else - "INFO") if config.log_level is None else config.log_level + ("DEBUG" if config.debug else "INFO") + if config.log_level is None + else config.log_level + ) logger.opt(colors=True).info( - f"Current Env: {escape_tag(env.environment)}") + f"Current Env: {escape_tag(env.environment)}" + ) logger.opt(colors=True).debug( - f"Loaded Config: {escape_tag(str(config.dict()))}") + f"Loaded Config: {escape_tag(str(config.dict()))}" + ) modulename, _, cls = config.driver.partition(":") module = importlib.import_module(modulename) @@ -247,10 +252,7 @@ def init(*, _env_file: Optional[str] = None, **kwargs): _driver = DriverClass(env, config) -def run(host: Optional[str] = None, - port: Optional[int] = None, - *args, - **kwargs): +def run(host: Optional[str] = None, port: Optional[int] = None, *args, **kwargs): """ :说明: diff --git a/nonebot/adapters/__init__.py b/nonebot/adapters/__init__.py index 74d26f69..a3080971 100644 --- a/nonebot/adapters/__init__.py +++ b/nonebot/adapters/__init__.py @@ -9,13 +9,13 @@ from typing import Iterable try: import pkg_resources + pkg_resources.declare_namespace(__name__) del pkg_resources except ImportError: import pkgutil - __path__: Iterable[str] = pkgutil.extend_path( - __path__, # type: ignore - __name__) + + __path__: Iterable[str] = pkgutil.extend_path(__path__, __name__) # type: ignore del pkgutil except Exception: pass diff --git a/nonebot/adapters/_bot.py b/nonebot/adapters/_bot.py index b96e31ae..6ed7b2b2 100644 --- a/nonebot/adapters/_bot.py +++ b/nonebot/adapters/_bot.py @@ -15,7 +15,6 @@ if TYPE_CHECKING: class _ApiCall(Protocol): - async def __call__(self, **kwargs: Any) -> Any: ... @@ -146,7 +145,8 @@ class Bot(abc.ABC): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running CallingAPI hook. " - "Running cancelled!") + "Running cancelled!" + ) exception = None result = None @@ -157,8 +157,8 @@ class Bot(abc.ABC): exception = e coros = list( - map(lambda x: x(self, exception, api, data, result), - self._called_api_hook)) + map(lambda x: x(self, exception, api, data, result), self._called_api_hook) + ) if coros: try: logger.debug("Running CalledAPI hooks...") @@ -166,16 +166,17 @@ class Bot(abc.ABC): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running CalledAPI hook. " - "Running cancelled!") + "Running cancelled!" + ) if exception: raise exception return result @abc.abstractmethod - async def send(self, event: "Event", message: Union[str, "Message", - "MessageSegment"], - **kwargs) -> Any: + async def send( + self, event: "Event", message: Union[str, "Message", "MessageSegment"], **kwargs + ) -> Any: """ :说明: diff --git a/nonebot/adapters/_event.py b/nonebot/adapters/_event.py index 669b4cf2..933a7085 100644 --- a/nonebot/adapters/_event.py +++ b/nonebot/adapters/_event.py @@ -2,9 +2,8 @@ import abc from pydantic import BaseModel -from nonebot.utils import DataclassEncoder - from ._message import Message +from nonebot.utils import DataclassEncoder class Event(abc.ABC, BaseModel): diff --git a/nonebot/adapters/_message.py b/nonebot/adapters/_message.py index b05a35a6..3175bed8 100644 --- a/nonebot/adapters/_message.py +++ b/nonebot/adapters/_message.py @@ -1,8 +1,17 @@ import abc from copy import deepcopy from dataclasses import field, asdict, dataclass -from typing import (Any, Dict, List, Type, Union, Generic, Mapping, TypeVar, - Iterable) +from typing import ( + Any, + Dict, + List, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, +) from ._template import MessageTemplate @@ -14,6 +23,7 @@ TM = TypeVar("TM", bound="Message") @dataclass class MessageSegment(Mapping, abc.ABC, Generic[TM]): """消息段基类""" + type: str """ - 类型: ``str`` @@ -82,11 +92,12 @@ class MessageSegment(Mapping, abc.ABC, Generic[TM]): class Message(List[TMS], abc.ABC): """消息数组""" - def __init__(self: TM, - message: Union[str, None, Mapping, Iterable[Mapping], TMS, TM, - Any] = None, - *args, - **kwargs): + def __init__( + self: TM, + message: Union[str, None, Mapping, Iterable[Mapping], TMS, TM, Any] = None, + *args, + **kwargs, + ): """ :参数: @@ -103,8 +114,7 @@ class Message(List[TMS], abc.ABC): self.extend(self._construct(message)) @classmethod - def template(cls: Type[TM], - format_string: Union[str, TM]) -> MessageTemplate[TM]: + def template(cls: Type[TM], format_string: Union[str, TM]) -> MessageTemplate[TM]: """ :说明: @@ -156,8 +166,7 @@ class Message(List[TMS], abc.ABC): @staticmethod @abc.abstractmethod - def _construct( - msg: Union[str, Mapping, Iterable[Mapping], Any]) -> Iterable[TMS]: + def _construct(msg: Union[str, Mapping, Iterable[Mapping], Any]) -> Iterable[TMS]: raise NotImplementedError def __add__(self: TM, other: Union[str, Mapping, Iterable[Mapping]]) -> TM: diff --git a/nonebot/adapters/_template.py b/nonebot/adapters/_template.py index 94472018..fad17d84 100644 --- a/nonebot/adapters/_template.py +++ b/nonebot/adapters/_template.py @@ -1,8 +1,21 @@ import inspect import functools from string import Formatter -from typing import (TYPE_CHECKING, Any, Set, List, Type, Tuple, Union, Generic, - Mapping, TypeVar, Sequence, cast, overload) +from typing import ( + TYPE_CHECKING, + Any, + Set, + List, + Type, + Tuple, + Union, + Generic, + Mapping, + TypeVar, + Sequence, + cast, + overload, +) if TYPE_CHECKING: from . import Message, MessageSegment @@ -15,14 +28,15 @@ class MessageTemplate(Formatter, Generic[TF]): """消息模板格式化实现类""" @overload - def __init__(self: "MessageTemplate[str]", - template: str, - factory: Type[str] = str) -> None: + def __init__( + self: "MessageTemplate[str]", template: str, factory: Type[str] = str + ) -> None: ... @overload - def __init__(self: "MessageTemplate[TM]", template: Union[str, TM], - factory: Type[TM]) -> None: + def __init__( + self: "MessageTemplate[TM]", template: Union[str, TM], factory: Type[TM] + ) -> None: ... def __init__(self, template, factory=str) -> None: @@ -51,15 +65,15 @@ class MessageTemplate(Formatter, Generic[TF]): elif isinstance(self.template, self.factory): template = cast("Message[MessageSegment]", self.template) for seg in template: - msg += self.vformat(str(seg), args, - kwargs) if seg.is_text() else seg + msg += self.vformat(str(seg), args, kwargs) if seg.is_text() else seg else: - raise TypeError('template must be a string or instance of Message!') + raise TypeError("template must be a string or instance of Message!") return msg - def vformat(self, format_string: str, args: Sequence[Any], - kwargs: Mapping[str, Any]) -> TF: + def vformat( + self, format_string: str, args: Sequence[Any], kwargs: Mapping[str, Any] + ) -> TF: used_args = set() result, _ = self._vformat(format_string, args, kwargs, used_args, 2) self.check_unused_args(list(used_args), args, kwargs) @@ -79,8 +93,9 @@ class MessageTemplate(Formatter, Generic[TF]): results: List[Any] = [] - for (literal_text, field_name, format_spec, - conversion) in self.parse(format_string): + for (literal_text, field_name, format_spec, conversion) in self.parse( + format_string + ): # output the literal text if literal_text: @@ -96,14 +111,16 @@ class MessageTemplate(Formatter, Generic[TF]): if auto_arg_index is False: raise ValueError( "cannot switch from manual field specification to " - "automatic field numbering") + "automatic field numbering" + ) field_name = str(auto_arg_index) auto_arg_index += 1 elif field_name.isdigit(): if auto_arg_index: raise ValueError( "cannot switch from manual field specification to " - "automatic field numbering") + "automatic field numbering" + ) # disable auto arg incrementing, if it gets # used later on, then an exception will be raised auto_arg_index = False @@ -132,8 +149,10 @@ class MessageTemplate(Formatter, Generic[TF]): formatted_text = self.format_field(obj, str(format_control)) results.append(formatted_text) - return self.factory(functools.reduce(self._add, results or - [""])), auto_arg_index + return ( + self.factory(functools.reduce(self._add, results or [""])), + auto_arg_index, + ) def format_field(self, value: Any, format_spec: str) -> Any: if issubclass(self.factory, str): @@ -142,11 +161,20 @@ class MessageTemplate(Formatter, Generic[TF]): segment_class: Type[MessageSegment] = self.factory.get_segment_class() method = getattr(segment_class, format_spec, None) method_type = inspect.getattr_static(segment_class, format_spec, None) - return (super().format_field(value, format_spec) if - ((method is None) or - (not isinstance(method_type, (classmethod, staticmethod)) - ) # Only Call staticmethod or classmethod - ) else method(value)) if format_spec else value + return ( + ( + super().format_field(value, format_spec) + if ( + (method is None) + or ( + not isinstance(method_type, (classmethod, staticmethod)) + ) # Only Call staticmethod or classmethod + ) + else method(value) + ) + if format_spec + else value + ) def _add(self, a: Any, b: Any) -> Any: try: diff --git a/nonebot/config.py b/nonebot/config.py index 82fca0d3..4bbbf1f9 100644 --- a/nonebot/config.py +++ b/nonebot/config.py @@ -20,13 +20,17 @@ from ipaddress import IPv4Address from typing import Any, Set, Dict, Tuple, Union, Mapping, Optional from pydantic import BaseSettings, IPvAnyAddress -from pydantic.env_settings import (SettingsError, EnvSettingsSource, - InitSettingsSource, SettingsSourceCallable, - read_env_file, env_file_sentinel) +from pydantic.env_settings import ( + SettingsError, + EnvSettingsSource, + InitSettingsSource, + SettingsSourceCallable, + read_env_file, + env_file_sentinel, +) class CustomEnvSettings(EnvSettingsSource): - def __call__(self, settings: BaseSettings) -> Dict[str, Any]: """ Build environment variables suitable for passing to the Model. @@ -39,15 +43,24 @@ class CustomEnvSettings(EnvSettingsSource): env_vars = {k.lower(): v for k, v in os.environ.items()} env_file_vars: Dict[str, Optional[str]] = {} - env_file = self.env_file if self.env_file != env_file_sentinel else settings.__config__.env_file - env_file_encoding = self.env_file_encoding if self.env_file_encoding is not None else settings.__config__.env_file_encoding + env_file = ( + self.env_file + if self.env_file != env_file_sentinel + else settings.__config__.env_file + ) + env_file_encoding = ( + self.env_file_encoding + if self.env_file_encoding is not None + else settings.__config__.env_file_encoding + ) if env_file is not None: env_path = Path(env_file) if env_path.is_file(): env_file_vars = read_env_file( env_path, encoding=env_file_encoding, - case_sensitive=settings.__config__.case_sensitive) + case_sensitive=settings.__config__.case_sensitive, + ) env_vars = {**env_file_vars, **env_vars} for field in settings.__fields__.values(): @@ -66,14 +79,12 @@ class CustomEnvSettings(EnvSettingsSource): try: env_val = settings.__config__.json_loads(env_val) except ValueError as e: - raise SettingsError( - f'error parsing JSON for "{env_name}"') from e + raise SettingsError(f'error parsing JSON for "{env_name}"') from e d[field.alias] = env_val if env_file_vars: for env_name, env_val in env_file_vars.items(): - if (env_val is None or - len(env_val) == 0) and env_name in env_vars: + if (env_val is None or len(env_val) == 0) and env_name in env_vars: env_val = env_vars[env_name] try: if env_val: @@ -87,12 +98,10 @@ class CustomEnvSettings(EnvSettingsSource): class BaseConfig(BaseSettings): - def __getattr__(self, name: str) -> Any: return self.__dict__.get(name) class Config: - @classmethod def customise_sources( cls, @@ -101,10 +110,14 @@ class BaseConfig(BaseSettings): file_secret_settings: SettingsSourceCallable, ) -> Tuple[SettingsSourceCallable, ...]: common_config = init_settings.init_kwargs.pop("_common_config", {}) - return (init_settings, - CustomEnvSettings(env_settings.env_file, - env_settings.env_file_encoding), - InitSettingsSource(common_config), file_secret_settings) + return ( + init_settings, + CustomEnvSettings( + env_settings.env_file, env_settings.env_file_encoding + ), + InitSettingsSource(common_config), + file_secret_settings, + ) class Env(BaseConfig): @@ -135,6 +148,7 @@ class Config(BaseConfig): 除了 NoneBot 的配置项外,还可以自行添加配置项到 ``.env.{environment}`` 文件中。 这些配置将会在 json 反序列化后一起带入 ``Config`` 类中。 """ + # nonebot configs driver: str = "nonebot.drivers.fastapi" """ @@ -210,7 +224,7 @@ class Config(BaseConfig): API_ROOT={"123456": "http://127.0.0.1:5700"} """ - api_timeout: Optional[float] = 30. + api_timeout: Optional[float] = 30.0 """ - **类型**: ``Optional[float]`` - **默认值**: ``30.`` diff --git a/nonebot/dependencies/__init__.py b/nonebot/dependencies/__init__.py index 44f568f7..c32a9ffe 100644 --- a/nonebot/dependencies/__init__.py +++ b/nonebot/dependencies/__init__.py @@ -21,9 +21,14 @@ from .models import Dependent as Dependent from nonebot.exception import SkippedException from .models import DependsWrapper as DependsWrapper from nonebot.typing import T_Handler, T_DependencyCache -from nonebot.utils import (CacheLock, run_sync, is_gen_callable, - run_sync_ctx_manager, is_async_gen_callable, - is_coroutine_callable) +from nonebot.utils import ( + CacheLock, + run_sync, + is_gen_callable, + run_sync_ctx_manager, + is_async_gen_callable, + is_coroutine_callable, +) cache_lock = CacheLock() @@ -33,60 +38,59 @@ class CustomConfig(BaseConfig): def get_param_sub_dependent( - *, - param: inspect.Parameter, - allow_types: Optional[List[Type[Param]]] = None) -> Dependent: + *, param: inspect.Parameter, allow_types: Optional[List[Type[Param]]] = None +) -> Dependent: depends: DependsWrapper = param.default if depends.dependency: dependency = depends.dependency else: dependency = param.annotation - return get_sub_dependant(depends=depends, - dependency=dependency, - name=param.name, - allow_types=allow_types) + return get_sub_dependant( + depends=depends, dependency=dependency, name=param.name, allow_types=allow_types + ) def get_parameterless_sub_dependant( - *, - depends: DependsWrapper, - allow_types: Optional[List[Type[Param]]] = None) -> Dependent: + *, depends: DependsWrapper, allow_types: Optional[List[Type[Param]]] = None +) -> Dependent: assert callable( depends.dependency ), "A parameter-less dependency must have a callable dependency" - return get_sub_dependant(depends=depends, - dependency=depends.dependency, - allow_types=allow_types) + return get_sub_dependant( + depends=depends, dependency=depends.dependency, allow_types=allow_types + ) def get_sub_dependant( - *, - depends: DependsWrapper, - dependency: T_Handler, - name: Optional[str] = None, - allow_types: Optional[List[Type[Param]]] = None) -> Dependent: - sub_dependant = get_dependent(func=dependency, - name=name, - use_cache=depends.use_cache, - allow_types=allow_types) + *, + depends: DependsWrapper, + dependency: T_Handler, + name: Optional[str] = None, + allow_types: Optional[List[Type[Param]]] = None, +) -> Dependent: + sub_dependant = get_dependent( + func=dependency, name=name, use_cache=depends.use_cache, allow_types=allow_types + ) return sub_dependant -def get_dependent(*, - func: T_Handler, - name: Optional[str] = None, - use_cache: bool = True, - allow_types: Optional[List[Type[Param]]] = None) -> Dependent: +def get_dependent( + *, + func: T_Handler, + name: Optional[str] = None, + use_cache: bool = True, + allow_types: Optional[List[Type[Param]]] = None, +) -> Dependent: signature = get_typed_signature(func) params = signature.parameters - dependent = Dependent(func=func, - name=name, - allow_types=allow_types, - use_cache=use_cache) + dependent = Dependent( + func=func, name=name, allow_types=allow_types, use_cache=use_cache + ) for param_name, param in params.items(): if isinstance(param.default, DependsWrapper): - sub_dependent = get_param_sub_dependent(param=param, - allow_types=allow_types) + sub_dependent = get_param_sub_dependent( + param=param, allow_types=allow_types + ) dependent.dependencies.append(sub_dependent) continue @@ -111,44 +115,44 @@ def get_dependent(*, required = default_value == Required if param.annotation != param.empty: annotation = param.annotation - annotation = get_annotation_from_field_info(annotation, field_info, - param_name) + annotation = get_annotation_from_field_info(annotation, field_info, param_name) dependent.params.append( - ModelField(name=param_name, - type_=annotation, - class_validators=None, - model_config=CustomConfig, - default=None if required else default_value, - required=required, - field_info=field_info)) + ModelField( + name=param_name, + type_=annotation, + class_validators=None, + model_config=CustomConfig, + default=None if required else default_value, + required=required, + field_info=field_info, + ) + ) return dependent async def solve_dependencies( - *, - _dependent: Dependent, - _stack: Optional[AsyncExitStack] = None, - _sub_dependents: Optional[List[Dependent]] = None, - _dependency_cache: Optional[T_DependencyCache] = None, - **params: Any) -> Tuple[Dict[str, Any], T_DependencyCache]: + *, + _dependent: Dependent, + _stack: Optional[AsyncExitStack] = None, + _sub_dependents: Optional[List[Dependent]] = None, + _dependency_cache: Optional[T_DependencyCache] = None, + **params: Any, +) -> Tuple[Dict[str, Any], T_DependencyCache]: values: Dict[str, Any] = {} dependency_cache = {} if _dependency_cache is None else _dependency_cache # solve sub dependencies sub_dependent: Dependent - for sub_dependent in chain(_sub_dependents or tuple(), - _dependent.dependencies): + for sub_dependent in chain(_sub_dependents or tuple(), _dependent.dependencies): sub_dependent.func = cast(Callable[..., Any], sub_dependent.func) - sub_dependent.cache_key = cast(Callable[..., Any], - sub_dependent.cache_key) + sub_dependent.cache_key = cast(Callable[..., Any], sub_dependent.cache_key) func = sub_dependent.func # solve sub dependency with current cache solved_result = await solve_dependencies( - _dependent=sub_dependent, - _dependency_cache=dependency_cache, - **params) + _dependent=sub_dependent, _dependency_cache=dependency_cache, **params + ) sub_values, sub_dependency_cache = solved_result # update cache? # dependency_cache.update(sub_dependency_cache) @@ -162,8 +166,7 @@ async def solve_dependencies( _stack, AsyncExitStack ), "Generator dependency should be called in context" if is_gen_callable(func): - cm = run_sync_ctx_manager( - contextmanager(func)(**sub_values)) + cm = run_sync_ctx_manager(contextmanager(func)(**sub_values)) else: cm = asynccontextmanager(func)(**sub_values) solved = await _stack.enter_async_context(cm) @@ -182,19 +185,17 @@ async def solve_dependencies( # usual dependency for field in _dependent.params: field_info = field.field_info - assert isinstance(field_info, - Param), "Params must be subclasses of Param" + assert isinstance(field_info, Param), "Params must be subclasses of Param" value = field_info._solve(**params) if value == Undefined: value = field.get_default() - _, errs_ = field.validate(value, - values, - loc=(str(field_info), field.alias)) + _, errs_ = field.validate(value, values, loc=(str(field_info), field.alias)) if errs_: logger.debug( f"{field_info} " f"type {type(value)} not match depends {_dependent.func} " - f"annotation {field._type_display()}, ignored") + f"annotation {field._type_display()}, ignored" + ) raise SkippedException else: values[field.name] = value @@ -202,9 +203,7 @@ async def solve_dependencies( return values, dependency_cache -def Depends(dependency: Optional[T_Handler] = None, - *, - use_cache: bool = True) -> Any: +def Depends(dependency: Optional[T_Handler] = None, *, use_cache: bool = True) -> Any: """ :说明: diff --git a/nonebot/dependencies/models.py b/nonebot/dependencies/models.py index 9acba4db..3fdb8e81 100644 --- a/nonebot/dependencies/models.py +++ b/nonebot/dependencies/models.py @@ -9,7 +9,6 @@ from nonebot.typing import T_Handler class Param(abc.ABC, FieldInfo): - @classmethod @abc.abstractmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: @@ -21,11 +20,9 @@ class Param(abc.ABC, FieldInfo): class DependsWrapper: - - def __init__(self, - dependency: Optional[T_Handler] = None, - *, - use_cache: bool = True) -> None: + def __init__( + self, dependency: Optional[T_Handler] = None, *, use_cache: bool = True + ) -> None: self.dependency = dependency self.use_cache = use_cache @@ -36,15 +33,16 @@ class DependsWrapper: class Dependent: - - def __init__(self, - *, - func: Optional[T_Handler] = None, - name: Optional[str] = None, - params: Optional[List[ModelField]] = None, - allow_types: Optional[List[Type[Param]]] = None, - dependencies: Optional[List["Dependent"]] = None, - use_cache: bool = True) -> None: + def __init__( + self, + *, + func: Optional[T_Handler] = None, + name: Optional[str] = None, + params: Optional[List[ModelField]] = None, + allow_types: Optional[List[Type[Param]]] = None, + dependencies: Optional[List["Dependent"]] = None, + use_cache: bool = True, + ) -> None: self.func = func self.name = name self.params = params or [] diff --git a/nonebot/dependencies/utils.py b/nonebot/dependencies/utils.py index ade7fd97..44a6b4c9 100644 --- a/nonebot/dependencies/utils.py +++ b/nonebot/dependencies/utils.py @@ -16,14 +16,14 @@ def get_typed_signature(func: T_Handler) -> inspect.Signature: kind=param.kind, default=param.default, annotation=get_typed_annotation(param, globalns), - ) for param in signature.parameters.values() + ) + for param in signature.parameters.values() ] typed_signature = inspect.Signature(typed_params) return typed_signature -def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, - Any]) -> Any: +def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any: annotation = param.annotation if isinstance(annotation, str): annotation = ForwardRef(annotation) @@ -31,7 +31,7 @@ def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, annotation = evaluate_forwardref(annotation, globalns, globalns) except Exception as e: logger.opt(colors=True, exception=e).warning( - f"Unknown ForwardRef[\"{param.annotation}\"] for parameter {param.name}" + f'Unknown ForwardRef["{param.annotation}"] for parameter {param.name}' ) return inspect.Parameter.empty return annotation diff --git a/nonebot/drivers/__init__.py b/nonebot/drivers/__init__.py index d08f6b0c..4add8a19 100644 --- a/nonebot/drivers/__init__.py +++ b/nonebot/drivers/__init__.py @@ -8,8 +8,17 @@ import abc import asyncio from dataclasses import field, dataclass -from typing import (TYPE_CHECKING, Any, Set, Dict, Type, Union, Callable, - Optional, Awaitable) +from typing import ( + TYPE_CHECKING, + Any, + Set, + Dict, + Type, + Union, + Callable, + Optional, + Awaitable, +) from nonebot.log import logger from nonebot.utils import escape_tag @@ -90,12 +99,14 @@ class Driver(abc.ABC): """ if name in self._adapters: logger.opt(colors=True).debug( - f'Adapter "{escape_tag(name)}" already exists') + f'Adapter "{escape_tag(name)}" already exists' + ) return self._adapters[name] = adapter adapter.register(self, self.config, **kwargs) logger.opt(colors=True).debug( - f'Succeeded to load adapter "{escape_tag(name)}"') + f'Succeeded to load adapter "{escape_tag(name)}"' + ) @property @abc.abstractmethod @@ -121,7 +132,8 @@ class Driver(abc.ABC): * ``**kwargs`` """ logger.opt(colors=True).debug( - f"Loaded adapters: {escape_tag(', '.join(self._adapters))}") + f"Loaded adapters: {escape_tag(', '.join(self._adapters))}" + ) @abc.abstractmethod def on_startup(self, func: Callable) -> Callable: @@ -146,8 +158,7 @@ class Driver(abc.ABC): self._bot_connection_hook.add(func) return func - def on_bot_disconnect( - self, func: T_BotDisconnectionHook) -> T_BotDisconnectionHook: + def on_bot_disconnect(self, func: T_BotDisconnectionHook) -> T_BotDisconnectionHook: """ :说明: @@ -172,7 +183,8 @@ class Driver(abc.ABC): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running WebSocketConnection hook. " - "Running cancelled!") + "Running cancelled!" + ) asyncio.create_task(_run_hook(bot)) @@ -189,7 +201,8 @@ class Driver(abc.ABC): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running WebSocketDisConnection hook. " - "Running cancelled!") + "Running cancelled!" + ) asyncio.create_task(_run_hook(bot)) @@ -201,8 +214,8 @@ class ForwardDriver(Driver): @abc.abstractmethod def setup_http_polling( - self, setup: Union["HTTPPollingSetup", - Callable[[], Awaitable["HTTPPollingSetup"]]] + self, + setup: Union["HTTPPollingSetup", Callable[[], Awaitable["HTTPPollingSetup"]]], ) -> None: """ :说明: @@ -217,8 +230,7 @@ class ForwardDriver(Driver): @abc.abstractmethod def setup_websocket( - self, setup: Union["WebSocketSetup", - Callable[[], Awaitable["WebSocketSetup"]]] + self, setup: Union["WebSocketSetup", Callable[[], Awaitable["WebSocketSetup"]]] ) -> None: """ :说明: @@ -288,6 +300,7 @@ class HTTPRequest(HTTPConnection): .. _asgi http scope: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope """ + method: str = "GET" """The HTTP method name, uppercased.""" body: bytes = b"" @@ -309,6 +322,7 @@ class HTTPResponse: .. _asgi http scope: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope """ + status: int """HTTP status code.""" body: Optional[bytes] = None @@ -416,5 +430,5 @@ class WebSocketSetup: """URL""" headers: Dict[str, str] = field(default_factory=dict) """HTTP headers""" - reconnect_interval: float = 3. + reconnect_interval: float = 3.0 """WebSocket 重连间隔""" diff --git a/nonebot/drivers/aiohttp.py b/nonebot/drivers/aiohttp.py index 3670c414..40879d62 100644 --- a/nonebot/drivers/aiohttp.py +++ b/nonebot/drivers/aiohttp.py @@ -20,13 +20,16 @@ from nonebot.typing import overrides from nonebot.utils import escape_tag from nonebot.config import Env, Config from nonebot.drivers import WebSocket as BaseWebSocket -from nonebot.drivers import (HTTPRequest, ForwardDriver, WebSocketSetup, - HTTPPollingSetup) +from nonebot.drivers import ( + HTTPRequest, + ForwardDriver, + WebSocketSetup, + HTTPPollingSetup, +) STARTUP_FUNC = Callable[[], Awaitable[None]] SHUTDOWN_FUNC = Callable[[], Awaitable[None]] -HTTPPOLLING_SETUP = Union[HTTPPollingSetup, - Callable[[], Awaitable[HTTPPollingSetup]]] +HTTPPOLLING_SETUP = Union[HTTPPollingSetup, Callable[[], Awaitable[HTTPPollingSetup]]] WEBSOCKET_SETUP = Union[WebSocketSetup, Callable[[], Awaitable[WebSocketSetup]]] HANDLED_SIGNALS = ( signal.SIGINT, # Unix signal 2. Sent by Ctrl+C. @@ -146,7 +149,8 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running startup function. " - "Ignored!") + "Ignored!" + ) async def main_loop(self): await self.should_exit.wait() @@ -160,24 +164,20 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running shutdown function. " - "Ignored!") + "Ignored!" + ) for task in self.connections: if not task.done(): task.cancel() await asyncio.sleep(0.1) - tasks = [ - t for t in asyncio.all_tasks() if t is not asyncio.current_task() - ] + tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] if tasks and not self.force_exit: logger.info("Waiting for tasks to finish. (CTRL+C to force quit)") while tasks and not self.force_exit: await asyncio.sleep(0.1) - tasks = [ - t for t in asyncio.all_tasks() - if t is not asyncio.current_task() - ] + tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] for task in tasks: task.cancel() @@ -209,9 +209,7 @@ class Driver(ForwardDriver): self.should_exit.set() async def _http_loop(self, setup: HTTPPOLLING_SETUP): - - async def _build_request( - setup: HTTPPollingSetup) -> Optional[HTTPRequest]: + async def _build_request(setup: HTTPPollingSetup) -> Optional[HTTPRequest]: url = URL(setup.url) if not url.is_absolute() or not url.host: logger.opt(colors=True).error( @@ -219,10 +217,15 @@ class Driver(ForwardDriver): ) return host = f"{url.host}:{url.port}" if url.port else url.host - return HTTPRequest(setup.http_version, url.scheme, url.path, - url.raw_query_string.encode("latin-1"), { - **setup.headers, "host": host - }, setup.method, setup.body) + return HTTPRequest( + setup.http_version, + url.scheme, + url.path, + url.raw_query_string.encode("latin-1"), + {**setup.headers, "host": host}, + setup.method, + setup.body, + ) bot: Optional[Bot] = None request: Optional[HTTPRequest] = None @@ -230,7 +233,8 @@ class Driver(ForwardDriver): logger.opt(colors=True).info( f"Start http polling for {escape_tag(setup.adapter.upper())} " - f"Bot {escape_tag(setup.self_id)}") + f"Bot {escape_tag(setup.self_id)}" + ) try: async with aiohttp.ClientSession() as session: @@ -244,7 +248,8 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error while parsing setup " - f"{escape_tag(repr(setup))}.") + f"{escape_tag(repr(setup))}." + ) await asyncio.sleep(3) continue @@ -286,19 +291,22 @@ class Driver(ForwardDriver): ) try: - async with session.request(request.method, - setup_.url, - data=request.body, - headers=headers, - timeout=timeout, - version=version) as response: + async with session.request( + request.method, + setup_.url, + data=request.body, + headers=headers, + timeout=timeout, + version=version, + ) as response: response.raise_for_status() data = await response.read() asyncio.create_task(bot.handle_message(data)) except aiohttp.ClientResponseError as e: logger.opt(colors=True, exception=e).error( f"Error occurred while requesting {escape_tag(setup_.url)}. " - "Try to reconnect...") + "Try to reconnect..." + ) await asyncio.sleep(setup_.poll_interval) @@ -307,7 +315,8 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Unexpected exception occurred " - "while http polling") + "while http polling" + ) finally: if bot: self._bot_disconnect(bot) @@ -327,7 +336,8 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error while parsing setup " - f"{escape_tag(repr(setup))}.") + f"{escape_tag(repr(setup))}." + ) await asyncio.sleep(3) continue @@ -346,17 +356,21 @@ class Driver(ForwardDriver): f"Bot {setup_.self_id} from adapter {setup_.adapter} connecting to {url}" ) try: - async with session.ws_connect(url, - headers=headers, - timeout=30.) as ws: + async with session.ws_connect( + url, headers=headers, timeout=30.0 + ) as ws: logger.opt(colors=True).info( f"WebSocket Connection to {escape_tag(setup_.adapter.upper())} " f"Bot {escape_tag(setup_.self_id)} succeeded!" ) request = WebSocket( - "1.1", url.scheme, url.path, - url.raw_query_string.encode("latin-1"), headers, - ws) + "1.1", + url.scheme, + url.path, + url.raw_query_string.encode("latin-1"), + headers, + ws, + ) BotClass = self._adapters[setup_.adapter] bot = BotClass(setup_.self_id, request) @@ -365,25 +379,30 @@ class Driver(ForwardDriver): msg = await ws.receive() if msg.type == aiohttp.WSMsgType.text: asyncio.create_task( - bot.handle_message(msg.data.encode())) + bot.handle_message(msg.data.encode()) + ) elif msg.type == aiohttp.WSMsgType.binary: - asyncio.create_task( - bot.handle_message(msg.data)) + asyncio.create_task(bot.handle_message(msg.data)) elif msg.type == aiohttp.WSMsgType.error: logger.opt(colors=True).error( "Error while handling websocket frame. " - "Try to reconnect...") + "Try to reconnect..." + ) break else: logger.opt(colors=True).error( "WebSocket connection closed by peer. " - "Try to reconnect...") + "Try to reconnect..." + ) break - except (aiohttp.ClientResponseError, - aiohttp.ClientConnectionError) as e: + except ( + aiohttp.ClientResponseError, + aiohttp.ClientConnectionError, + ) as e: logger.opt(colors=True, exception=e).error( f"Error while connecting to {escape_tag(str(url))}. " - "Try to reconnect...") + "Try to reconnect..." + ) finally: if bot: self._bot_disconnect(bot) @@ -395,7 +414,8 @@ class Driver(ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Unexpected exception occurred " - "while websocket loop") + "while websocket loop" + ) @dataclass diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py index e0d47d79..db9abf41 100644 --- a/nonebot/drivers/fastapi.py +++ b/nonebot/drivers/fastapi.py @@ -32,11 +32,15 @@ from nonebot.typing import overrides from nonebot.utils import escape_tag from nonebot.config import Config as NoneBotConfig from nonebot.drivers import WebSocket as BaseWebSocket -from nonebot.drivers import (HTTPRequest, ForwardDriver, ReverseDriver, - WebSocketSetup, HTTPPollingSetup) +from nonebot.drivers import ( + HTTPRequest, + ForwardDriver, + ReverseDriver, + WebSocketSetup, + HTTPPollingSetup, +) -HTTPPOLLING_SETUP = Union[HTTPPollingSetup, - Callable[[], Awaitable[HTTPPollingSetup]]] +HTTPPOLLING_SETUP = Union[HTTPPollingSetup, Callable[[], Awaitable[HTTPPollingSetup]]] WEBSOCKET_SETUP = Union[WebSocketSetup, Callable[[], Awaitable[WebSocketSetup]]] @@ -44,6 +48,7 @@ class Config(BaseSettings): """ FastAPI 驱动框架设置,详情参考 FastAPI 文档 """ + fastapi_openapi_url: Optional[str] = None """ :类型: @@ -226,12 +231,14 @@ class Driver(ReverseDriver, ForwardDriver): self.websockets.append(setup) @overrides(ReverseDriver) - def run(self, - host: Optional[str] = None, - port: Optional[int] = None, - *, - app: Optional[str] = None, - **kwargs): + def run( + self, + host: Optional[str] = None, + port: Optional[int] = None, + *, + app: Optional[str] = None, + **kwargs, + ): """使用 ``uvicorn`` 启动 FastAPI""" super().run(host, port, app, **kwargs) LOGGING_CONFIG = { @@ -243,10 +250,7 @@ class Driver(ReverseDriver, ForwardDriver): }, }, "loggers": { - "uvicorn.error": { - "handlers": ["default"], - "level": "INFO" - }, + "uvicorn.error": {"handlers": ["default"], "level": "INFO"}, "uvicorn.access": { "handlers": ["default"], "level": "INFO", @@ -258,15 +262,16 @@ class Driver(ReverseDriver, ForwardDriver): host=host or str(self.config.host), port=port or self.config.port, reload=self.fastapi_config.fastapi_reload - if self.fastapi_config.fastapi_reload is not None else - (bool(app) and self.config.debug), + if self.fastapi_config.fastapi_reload is not None + else (bool(app) and self.config.debug), reload_dirs=self.fastapi_config.fastapi_reload_dirs, reload_delay=self.fastapi_config.fastapi_reload_delay, reload_includes=self.fastapi_config.fastapi_reload_includes, reload_excludes=self.fastapi_config.fastapi_reload_excludes, debug=self.config.debug, log_config=LOGGING_CONFIG, - **kwargs) + **kwargs, + ) def _run_forward(self): for setup in self.http_pollings: @@ -287,39 +292,49 @@ class Driver(ReverseDriver, ForwardDriver): logger.warning( f"Unknown adapter {adapter}. Please register the adapter before use." ) - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, - detail="adapter not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="adapter not found" + ) # 创建 Bot 对象 BotClass = self._adapters[adapter] - http_request = HTTPRequest(request.scope["http_version"], - request.url.scheme, request.url.path, - request.scope["query_string"], - dict(request.headers), request.method, data) - x_self_id, response = await BotClass.check_permission( - self, http_request) + http_request = HTTPRequest( + request.scope["http_version"], + request.url.scheme, + request.url.path, + request.scope["query_string"], + dict(request.headers), + request.method, + data, + ) + x_self_id, response = await BotClass.check_permission(self, http_request) if not x_self_id: raise HTTPException( - response and response.status or 401, response and - response.body and response.body.decode("utf-8")) + response and response.status or 401, + response and response.body and response.body.decode("utf-8"), + ) if x_self_id in self._clients: - logger.warning("There's already a reverse websocket connection," - "so the event may be handled twice.") + logger.warning( + "There's already a reverse websocket connection," + "so the event may be handled twice." + ) bot = BotClass(x_self_id, http_request) asyncio.create_task(bot.handle_message(data)) - return Response(response and response.body, - response and response.status or 200) + return Response(response and response.body, response and response.status or 200) - async def _handle_ws_reverse(self, adapter: str, - websocket: FastAPIWebSocket): - ws = WebSocket(websocket.scope.get("http_version", - "1.1"), websocket.url.scheme, - websocket.url.path, websocket.scope["query_string"], - dict(websocket.headers), websocket) + async def _handle_ws_reverse(self, adapter: str, websocket: FastAPIWebSocket): + ws = WebSocket( + websocket.scope.get("http_version", "1.1"), + websocket.url.scheme, + websocket.url.path, + websocket.scope["query_string"], + dict(websocket.headers), + websocket, + ) if adapter not in self._adapters: logger.warning( @@ -349,7 +364,8 @@ class Driver(ReverseDriver, ForwardDriver): await ws.accept() logger.opt(colors=True).info( f"WebSocket Connection from {escape_tag(adapter.upper())} " - f"Bot {escape_tag(self_id)} Accepted!") + f"Bot {escape_tag(self_id)} Accepted!" + ) self._bot_connect(bot) @@ -362,7 +378,8 @@ class Driver(ReverseDriver, ForwardDriver): break except Exception as e: logger.opt(exception=e).error( - "Error when receiving data from websocket.") + "Error when receiving data from websocket." + ) break asyncio.create_task(bot.handle_message(data.encode())) @@ -370,9 +387,7 @@ class Driver(ReverseDriver, ForwardDriver): self._bot_disconnect(bot) async def _http_loop(self, setup: HTTPPOLLING_SETUP): - - async def _build_request( - setup: HTTPPollingSetup) -> Optional[HTTPRequest]: + async def _build_request(setup: HTTPPollingSetup) -> Optional[HTTPRequest]: url = httpx.URL(setup.url) if not url.netloc: logger.opt(colors=True).error( @@ -380,9 +395,14 @@ class Driver(ReverseDriver, ForwardDriver): ) return return HTTPRequest( - setup.http_version, url.scheme, url.path, url.query, { - **setup.headers, "host": url.netloc.decode("ascii") - }, setup.method, setup.body) + setup.http_version, + url.scheme, + url.path, + url.query, + {**setup.headers, "host": url.netloc.decode("ascii")}, + setup.method, + setup.body, + ) bot: Optional[Bot] = None request: Optional[HTTPRequest] = None @@ -390,11 +410,11 @@ class Driver(ReverseDriver, ForwardDriver): logger.opt(colors=True).info( f"Start http polling for {escape_tag(setup.adapter.upper())} " - f"Bot {escape_tag(setup.self_id)}") + f"Bot {escape_tag(setup.self_id)}" + ) try: - async with httpx.AsyncClient(http2=True, - follow_redirects=True) as session: + async with httpx.AsyncClient(http2=True, follow_redirects=True) as session: while not self.shutdown.is_set(): try: @@ -405,7 +425,8 @@ class Driver(ReverseDriver, ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error while parsing setup " - f"{escape_tag(repr(setup))}.") + f"{escape_tag(repr(setup))}." + ) await asyncio.sleep(3) continue @@ -432,18 +453,21 @@ class Driver(ReverseDriver, ForwardDriver): f"Bot {setup_.self_id} from adapter {setup_.adapter} request {setup_.url}" ) try: - response = await session.request(request.method, - setup_.url, - content=request.body, - headers=headers, - timeout=30.) + response = await session.request( + request.method, + setup_.url, + content=request.body, + headers=headers, + timeout=30.0, + ) response.raise_for_status() data = response.read() asyncio.create_task(bot.handle_message(data)) except httpx.HTTPError as e: logger.opt(colors=True, exception=e).error( f"Error occurred while requesting {escape_tag(setup_.url)}. " - "Try to reconnect...") + "Try to reconnect..." + ) await asyncio.sleep(setup_.poll_interval) @@ -452,7 +476,8 @@ class Driver(ReverseDriver, ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Unexpected exception occurred " - "while http polling") + "while http polling" + ) finally: if bot: self._bot_disconnect(bot) @@ -471,7 +496,8 @@ class Driver(ReverseDriver, ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Error while parsing setup " - f"{escape_tag(repr(setup))}.") + f"{escape_tag(repr(setup))}." + ) await asyncio.sleep(3) continue @@ -491,9 +517,11 @@ class Driver(ReverseDriver, ForwardDriver): async with connection as ws: logger.opt(colors=True).info( f"WebSocket Connection to {escape_tag(setup_.adapter.upper())} " - f"Bot {escape_tag(setup_.self_id)} succeeded!") - request = WebSocket("1.1", url.scheme, url.path, - url.query, headers, ws) + f"Bot {escape_tag(setup_.self_id)} succeeded!" + ) + request = WebSocket( + "1.1", url.scheme, url.path, url.query, headers, ws + ) BotClass = self._adapters[setup_.adapter] bot = BotClass(setup_.self_id, request) @@ -506,12 +534,14 @@ class Driver(ReverseDriver, ForwardDriver): except ConnectionClosed: logger.opt(colors=True).error( "WebSocket connection closed by peer. " - "Try to reconnect...") + "Try to reconnect..." + ) break except Exception as e: logger.opt(colors=True, exception=e).error( f"Error while connecting to {url}. " - "Try to reconnect...") + "Try to reconnect..." + ) finally: if bot: self._bot_disconnect(bot) @@ -523,21 +553,22 @@ class Driver(ReverseDriver, ForwardDriver): except Exception as e: logger.opt(colors=True, exception=e).error( "Unexpected exception occurred " - "while websocket loop") + "while websocket loop" + ) @dataclass class WebSocket(BaseWebSocket): - websocket: Union[FastAPIWebSocket, - WebSocketClientProtocol] = None # type: ignore + websocket: Union[FastAPIWebSocket, WebSocketClientProtocol] = None # type: ignore @property @overrides(BaseWebSocket) def closed(self) -> bool: if isinstance(self.websocket, FastAPIWebSocket): return ( - self.websocket.client_state == WebSocketState.DISCONNECTED or - self.websocket.application_state == WebSocketState.DISCONNECTED) + self.websocket.client_state == WebSocketState.DISCONNECTED + or self.websocket.application_state == WebSocketState.DISCONNECTED + ) else: return self.websocket.closed diff --git a/nonebot/drivers/quart.py b/nonebot/drivers/quart.py index 5359c270..79055eb9 100644 --- a/nonebot/drivers/quart.py +++ b/nonebot/drivers/quart.py @@ -30,8 +30,7 @@ try: from quart import Quart, Request, Response from quart import Websocket as QuartWebSocket except ImportError: - raise ValueError( - 'Please install Quart by using `pip install nonebot2[quart]`') + raise ValueError("Please install Quart by using `pip install nonebot2[quart]`") _AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine]) @@ -40,6 +39,7 @@ class Config(BaseSettings): """ Quart 驱动框架设置 """ + quart_reload: Optional[bool] = None """ :类型: @@ -111,11 +111,12 @@ class Driver(ReverseDriver): self.quart_config = Config(**config.dict()) self._server_app = Quart(self.__class__.__qualname__) - self._server_app.add_url_rule("//http", - methods=["POST"], - view_func=self._handle_http) - self._server_app.add_websocket("//ws", - view_func=self._handle_ws_reverse) + self._server_app.add_url_rule( + "//http", methods=["POST"], view_func=self._handle_http + ) + self._server_app.add_websocket( + "//ws", view_func=self._handle_ws_reverse + ) @property @overrides(ReverseDriver) @@ -156,12 +157,14 @@ class Driver(ReverseDriver): return self.server_app.after_serving(func) # type: ignore @overrides(ReverseDriver) - def run(self, - host: Optional[str] = None, - port: Optional[int] = None, - *, - app: Optional[str] = None, - **kwargs): + def run( + self, + host: Optional[str] = None, + port: Optional[int] = None, + *, + app: Optional[str] = None, + **kwargs, + ): """使用 ``uvicorn`` 启动 Quart""" super().run(host, port, app, **kwargs) LOGGING_CONFIG = { @@ -173,10 +176,7 @@ class Driver(ReverseDriver): }, }, "loggers": { - "uvicorn.error": { - "handlers": ["default"], - "level": "INFO" - }, + "uvicorn.error": {"handlers": ["default"], "level": "INFO"}, "uvicorn.access": { "handlers": ["default"], "level": "INFO", @@ -188,52 +188,69 @@ class Driver(ReverseDriver): host=host or str(self.config.host), port=port or self.config.port, reload=self.quart_config.quart_reload - if self.quart_config.quart_reload is not None else - (bool(app) and self.config.debug), + if self.quart_config.quart_reload is not None + else (bool(app) and self.config.debug), reload_dirs=self.quart_config.quart_reload_dirs, reload_delay=self.quart_config.quart_reload_delay, reload_includes=self.quart_config.quart_reload_includes, reload_excludes=self.quart_config.quart_reload_excludes, debug=self.config.debug, log_config=LOGGING_CONFIG, - **kwargs) + **kwargs, + ) async def _handle_http(self, adapter: str): request: Request = _request data: bytes = await request.get_data() # type: ignore if adapter not in self._adapters: - logger.warning(f'Unknown adapter {adapter}. ' - 'Please register the adapter before use.') + logger.warning( + f"Unknown adapter {adapter}. " "Please register the adapter before use." + ) raise exceptions.NotFound() BotClass = self._adapters[adapter] - http_request = HTTPRequest(request.http_version, request.scheme, - request.path, request.query_string, - dict(request.headers), request.method, data) + http_request = HTTPRequest( + request.http_version, + request.scheme, + request.path, + request.query_string, + dict(request.headers), + request.method, + data, + ) self_id, response = await BotClass.check_permission(self, http_request) if not self_id: raise exceptions.Unauthorized( - description=(response and response.body or b"").decode()) + description=(response and response.body or b"").decode() + ) if self_id in self._clients: - logger.warning("There's already a reverse websocket connection," - "so the event may be handled twice.") + logger.warning( + "There's already a reverse websocket connection," + "so the event may be handled twice." + ) bot = BotClass(self_id, http_request) asyncio.create_task(bot.handle_message(data)) - return Response(response and response.body or "", - response and response.status or 200) + return Response( + response and response.body or "", response and response.status or 200 + ) async def _handle_ws_reverse(self, adapter: str): websocket: QuartWebSocket = _websocket - ws = WebSocket(websocket.http_version, websocket.scheme, - websocket.path, websocket.query_string, - dict(websocket.headers), websocket) + ws = WebSocket( + websocket.http_version, + websocket.scheme, + websocket.path, + websocket.query_string, + dict(websocket.headers), + websocket, + ) if adapter not in self._adapters: logger.warning( - f'Unknown adapter {adapter}. Please register the adapter before use.' + f"Unknown adapter {adapter}. Please register the adapter before use." ) raise exceptions.NotFound() @@ -242,20 +259,22 @@ class Driver(ReverseDriver): if not self_id: raise exceptions.Unauthorized( - description=(response and response.body or b"").decode()) + description=(response and response.body or b"").decode() + ) if self_id in self._clients: logger.opt(colors=True).warning( "There's already a websocket connection, " f"{escape_tag(adapter.upper())} Bot {escape_tag(self_id)} ignored." ) - raise exceptions.Forbidden(description='Client already exists.') + raise exceptions.Forbidden(description="Client already exists.") bot = BotClass(self_id, ws) await ws.accept() logger.opt(colors=True).info( f"WebSocket Connection from {escape_tag(adapter.upper())} " - f"Bot {escape_tag(self_id)} Accepted!") + f"Bot {escape_tag(self_id)} Accepted!" + ) self._bot_connect(bot) try: @@ -267,7 +286,8 @@ class Driver(ReverseDriver): break except Exception as e: logger.opt(exception=e).error( - "Error when receiving data from websocket.") + "Error when receiving data from websocket." + ) break asyncio.create_task(bot.handle_message(data.encode())) diff --git a/nonebot/exception.py b/nonebot/exception.py index 227d8e10..e602ee93 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -157,6 +157,7 @@ class NoLogException(AdapterException): 指示 NoneBot 对当前 ``Event`` 进行处理但不显示 Log 信息,可在 ``get_log_string`` 时抛出 """ + pass @@ -166,6 +167,7 @@ class ApiNotAvailable(AdapterException): 在 API 连接不可用时抛出。 """ + pass @@ -175,6 +177,7 @@ class NetworkError(AdapterException): 在网络出现问题时抛出,如: API 请求地址不正确, API 请求无返回或返回状态非正常等。 """ + pass @@ -184,4 +187,5 @@ class ActionFailed(AdapterException): API 请求成功返回数据,但 API 操作失败。 """ + pass diff --git a/nonebot/handler.py b/nonebot/handler.py index 6203bebd..78ec21b9 100644 --- a/nonebot/handler.py +++ b/nonebot/handler.py @@ -10,20 +10,27 @@ from contextlib import AsyncExitStack from typing import Any, Dict, List, Type, Callable, Optional from nonebot.utils import get_name, run_sync -from nonebot.dependencies import (Param, Dependent, DependsWrapper, - get_dependent, solve_dependencies, - get_parameterless_sub_dependant) +from nonebot.dependencies import ( + Param, + Dependent, + DependsWrapper, + get_dependent, + solve_dependencies, + get_parameterless_sub_dependant, +) class Handler: """事件处理器类。支持依赖注入。""" - def __init__(self, - func: Callable[..., Any], - *, - name: Optional[str] = None, - dependencies: Optional[List[DependsWrapper]] = None, - allow_types: Optional[List[Type[Param]]] = None): + def __init__( + self, + func: Callable[..., Any], + *, + name: Optional[str] = None, + dependencies: Optional[List[DependsWrapper]] = None, + allow_types: Optional[List[Type[Param]]] = None, + ): """ :说明: @@ -64,19 +71,18 @@ class Handler: self.dependent = get_dependent(func=func, allow_types=self.allow_types) def __repr__(self) -> str: - return ( - f"" - ) + return f"" def __str__(self) -> str: return repr(self) - async def __call__(self, - *, - _stack: Optional[AsyncExitStack] = None, - _dependency_cache: Optional[Dict[Callable[..., Any], - Any]] = None, - **params) -> Any: + async def __call__( + self, + *, + _stack: Optional[AsyncExitStack] = None, + _dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, + **params, + ) -> Any: values, _ = await solve_dependencies( _dependent=self.dependent, _stack=_stack, @@ -85,7 +91,8 @@ class Handler: for dependency in self.dependencies ], _dependency_cache=_dependency_cache, - **params) + **params, + ) if asyncio.iscoroutinefunction(self.func): return await self.func(**values) @@ -98,7 +105,8 @@ class Handler: if dependency.dependency in self.sub_dependents: raise ValueError(f"{dependency} is already in dependencies") sub_dependant = get_parameterless_sub_dependant( - depends=dependency, allow_types=self.allow_types) + depends=dependency, allow_types=self.allow_types + ) self.sub_dependents[dependency.dependency] = sub_dependant def prepend_dependency(self, dependency: DependsWrapper): diff --git a/nonebot/log.py b/nonebot/log.py index 8b1cc38c..8541f6b8 100644 --- a/nonebot/log.py +++ b/nonebot/log.py @@ -48,7 +48,6 @@ logger: "Logger" = loguru.logger class Filter: - def __init__(self) -> None: self.level: Union[int, str] = "DEBUG" @@ -58,13 +57,13 @@ class Filter: if module: module_name = getattr(module, "__module_name__", module_name) record["name"] = module_name.split(".")[0] - levelno = logger.level(self.level).no if isinstance(self.level, - str) else self.level + levelno = ( + logger.level(self.level).no if isinstance(self.level, str) else self.level + ) return record["level"].no >= levelno class LoguruHandler(logging.Handler): - def emit(self, record): try: level = logger.level(record.levelname).name @@ -76,8 +75,9 @@ class LoguruHandler(logging.Handler): frame = frame.f_back depth += 1 - logger.opt(depth=depth, - exception=record.exc_info).log(level, record.getMessage()) + logger.opt(depth=depth, exception=record.exc_info).log( + level, record.getMessage() + ) logger.remove() @@ -87,9 +87,12 @@ default_format = ( "[{level}] " "{name} | " # "{function}:{line}| " - "{message}") -logger_id = logger.add(sys.stdout, - colorize=True, - diagnose=False, - filter=default_filter, - format=default_format) + "{message}" +) +logger_id = logger.add( + sys.stdout, + colorize=True, + diagnose=False, + filter=default_filter, + format=default_format, +) diff --git a/nonebot/matcher.py b/nonebot/matcher.py index cbaab224..6f42dcf2 100644 --- a/nonebot/matcher.py +++ b/nonebot/matcher.py @@ -10,8 +10,17 @@ from datetime import datetime from contextvars import ContextVar from collections import defaultdict from contextlib import AsyncExitStack -from typing import (TYPE_CHECKING, Any, Dict, List, Type, Union, Callable, - NoReturn, Optional) +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Union, + Callable, + NoReturn, + Optional, +) from nonebot import params from nonebot.rule import Rule @@ -19,14 +28,29 @@ from nonebot.log import logger from nonebot.handler import Handler from nonebot.dependencies import DependsWrapper from nonebot.permission import USER, Permission -from nonebot.adapters import (Bot, Event, Message, MessageSegment, - MessageTemplate) -from nonebot.exception import (PausedException, StopPropagation, - SkippedException, FinishedException, - RejectedException) -from nonebot.typing import (T_State, T_Handler, T_ArgsParser, T_TypeUpdater, - T_StateFactory, T_DependencyCache, - T_PermissionUpdater) +from nonebot.adapters import ( + Bot, + Event, + Message, + MessageSegment, + MessageTemplate, +) +from nonebot.exception import ( + PausedException, + StopPropagation, + SkippedException, + FinishedException, + RejectedException, +) +from nonebot.typing import ( + T_State, + T_Handler, + T_ArgsParser, + T_TypeUpdater, + T_StateFactory, + T_DependencyCache, + T_PermissionUpdater, +) if TYPE_CHECKING: from nonebot.plugin import Plugin @@ -57,9 +81,11 @@ class MatcherMeta(type): expire_time: Optional[datetime] def __repr__(self) -> str: - return (f"") + return ( + f"" + ) def __str__(self) -> str: return repr(self) @@ -67,6 +93,7 @@ class MatcherMeta(type): class Matcher(metaclass=MatcherMeta): """事件响应器类""" + plugin: Optional["Plugin"] = None """ :类型: ``Optional[Plugin]`` @@ -157,8 +184,11 @@ class Matcher(metaclass=MatcherMeta): """ HANDLER_PARAM_TYPES = [ - params.BotParam, params.EventParam, params.StateParam, - params.MatcherParam, params.DefaultParam + params.BotParam, + params.EventParam, + params.StateParam, + params.MatcherParam, + params.DefaultParam, ] def __init__(self): @@ -169,7 +199,8 @@ class Matcher(metaclass=MatcherMeta): def __repr__(self) -> str: return ( f"") + f"priority={self.priority}, temp={self.temp}>" + ) def __str__(self) -> str: return repr(self) @@ -180,8 +211,9 @@ class Matcher(metaclass=MatcherMeta): type_: str = "", rule: Optional[Rule] = None, permission: Optional[Permission] = None, - handlers: Optional[Union[List[T_Handler], List[Handler], - List[Union[T_Handler, Handler]]]] = None, + handlers: Optional[ + Union[List[T_Handler], List[Handler], List[Union[T_Handler, Handler]]] + ] = None, temp: bool = False, priority: int = 1, block: bool = False, @@ -193,7 +225,7 @@ class Matcher(metaclass=MatcherMeta): default_state_factory: Optional[T_StateFactory] = None, default_parser: Optional[T_ArgsParser] = None, default_type_updater: Optional[T_TypeUpdater] = None, - default_permission_updater: Optional[T_PermissionUpdater] = None + default_permission_updater: Optional[T_PermissionUpdater] = None, ) -> Type["Matcher"]: """ :说明: @@ -221,46 +253,37 @@ class Matcher(metaclass=MatcherMeta): """ NewMatcher = type( - "Matcher", (Matcher,), { - "plugin": - plugin, - "module": - module, - "plugin_name": - plugin and plugin.name, - "module_name": - module and module.__name__, - "type": - type_, - "rule": - rule or Rule(), - "permission": - permission or Permission(), + "Matcher", + (Matcher,), + { + "plugin": plugin, + "module": module, + "plugin_name": plugin and plugin.name, + "module_name": module and module.__name__, + "type": type_, + "rule": rule or Rule(), + "permission": permission or Permission(), "handlers": [ - handler if isinstance(handler, Handler) else Handler( - handler, allow_types=cls.HANDLER_PARAM_TYPES) + handler + if isinstance(handler, Handler) + else Handler(handler, allow_types=cls.HANDLER_PARAM_TYPES) for handler in handlers - ] if handlers else [], - "temp": - temp, - "expire_time": - expire_time, - "priority": - priority, - "block": - block, - "_default_state": - default_state or {}, - "_default_state_factory": - staticmethod(default_state_factory) - if default_state_factory else None, - "_default_parser": - default_parser, - "_default_type_updater": - default_type_updater, - "_default_permission_updater": - default_permission_updater - }) + ] + if handlers + else [], + "temp": temp, + "expire_time": expire_time, + "priority": priority, + "block": block, + "_default_state": default_state or {}, + "_default_state_factory": staticmethod(default_state_factory) + if default_state_factory + else None, + "_default_parser": default_parser, + "_default_type_updater": default_type_updater, + "_default_permission_updater": default_permission_updater, + }, + ) matchers[priority].append(NewMatcher) @@ -272,8 +295,8 @@ class Matcher(metaclass=MatcherMeta): bot: Bot, event: Event, stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[Dict[Callable[..., Any], - Any]] = None) -> bool: + dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, + ) -> bool: """ :说明: @@ -289,8 +312,9 @@ class Matcher(metaclass=MatcherMeta): - ``bool``: 是否满足权限 """ event_type = event.get_type() - return (event_type == (cls.type or event_type) and - await cls.permission(bot, event, stack, dependency_cache)) + return event_type == (cls.type or event_type) and await cls.permission( + bot, event, stack, dependency_cache + ) @classmethod async def check_rule( @@ -299,8 +323,8 @@ class Matcher(metaclass=MatcherMeta): event: Event, state: T_State, stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[Dict[Callable[..., Any], - Any]] = None) -> bool: + dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, + ) -> bool: """ :说明: @@ -317,8 +341,9 @@ class Matcher(metaclass=MatcherMeta): - ``bool``: 是否满足匹配规则 """ event_type = event.get_type() - return (event_type == (cls.type or event_type) and - await cls.rule(bot, event, state, stack, dependency_cache)) + return event_type == (cls.type or event_type) and await cls.rule( + bot, event, state, stack, dependency_cache + ) @classmethod def args_parser(cls, func: T_ArgsParser) -> T_ArgsParser: @@ -349,8 +374,7 @@ class Matcher(metaclass=MatcherMeta): return func @classmethod - def permission_updater(cls, - func: T_PermissionUpdater) -> T_PermissionUpdater: + def permission_updater(cls, func: T_PermissionUpdater) -> T_PermissionUpdater: """ :说明: @@ -365,12 +389,11 @@ class Matcher(metaclass=MatcherMeta): @classmethod def append_handler( - cls, - handler: T_Handler, - dependencies: Optional[List[DependsWrapper]] = None) -> Handler: - handler_ = Handler(handler, - dependencies=dependencies, - allow_types=cls.HANDLER_PARAM_TYPES) + cls, handler: T_Handler, dependencies: Optional[List[DependsWrapper]] = None + ) -> Handler: + handler_ = Handler( + handler, dependencies=dependencies, allow_types=cls.HANDLER_PARAM_TYPES + ) cls.handlers.append(handler_) return handler_ @@ -418,8 +441,7 @@ class Matcher(metaclass=MatcherMeta): func_handler = cls.handlers[-1] func_handler.prepend_dependency(depend) else: - cls.append_handler( - func, dependencies=[depend] if cls.handlers else []) + cls.append_handler(func, dependencies=[depend] if cls.handlers else []) return func @@ -429,9 +451,8 @@ class Matcher(metaclass=MatcherMeta): def got( cls, key: str, - prompt: Optional[Union[str, Message, MessageSegment, - MessageTemplate]] = None, - args_parser: Optional[T_ArgsParser] = None + prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, + args_parser: Optional[T_ArgsParser] = None, ) -> Callable[[T_Handler], T_Handler]: """ :说明: @@ -483,16 +504,16 @@ class Matcher(metaclass=MatcherMeta): func_handler.prepend_dependency(parser_depend) func_handler.prepend_dependency(get_depend) else: - cls.append_handler(func, - dependencies=[get_depend, parser_depend]) + cls.append_handler(func, dependencies=[get_depend, parser_depend]) return func return _decorator @classmethod - async def send(cls, message: Union[str, Message, MessageSegment, - MessageTemplate], **kwargs) -> Any: + async def send( + cls, message: Union[str, Message, MessageSegment, MessageTemplate], **kwargs + ) -> Any: """ :说明: @@ -513,10 +534,11 @@ class Matcher(metaclass=MatcherMeta): return await bot.send(event=event, message=_message, **kwargs) @classmethod - async def finish(cls, - message: Optional[Union[str, Message, MessageSegment, - MessageTemplate]] = None, - **kwargs) -> NoReturn: + async def finish( + cls, + message: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, + **kwargs, + ) -> NoReturn: """ :说明: @@ -539,10 +561,11 @@ class Matcher(metaclass=MatcherMeta): raise FinishedException @classmethod - async def pause(cls, - prompt: Optional[Union[str, Message, MessageSegment, - MessageTemplate]] = None, - **kwargs) -> NoReturn: + async def pause( + cls, + prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, + **kwargs, + ) -> NoReturn: """ :说明: @@ -565,10 +588,9 @@ class Matcher(metaclass=MatcherMeta): raise PausedException @classmethod - async def reject(cls, - prompt: Optional[Union[str, Message, - MessageSegment]] = None, - **kwargs) -> NoReturn: + async def reject( + cls, prompt: Optional[Union[str, Message, MessageSegment]] = None, **kwargs + ) -> NoReturn: """ :说明: @@ -601,31 +623,38 @@ class Matcher(metaclass=MatcherMeta): self.block = True # 运行handlers - async def run(self, - bot: Bot, - event: Event, - state: T_State, - stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[T_DependencyCache] = None): + async def run( + self, + bot: Bot, + event: Event, + state: T_State, + stack: Optional[AsyncExitStack] = None, + dependency_cache: Optional[T_DependencyCache] = None, + ): b_t = current_bot.set(bot) e_t = current_event.set(event) s_t = current_state.set(self.state) try: # Refresh preprocess state - self.state = await self._default_state_factory( - bot, event) if self._default_state_factory else self.state + self.state = ( + await self._default_state_factory(bot, event) + if self._default_state_factory + else self.state + ) self.state.update(state) while self.handlers: handler = self.handlers.pop(0) logger.debug(f"Running handler {handler}") try: - await handler(matcher=self, - bot=bot, - event=event, - state=self.state, - _stack=stack, - _dependency_cache=dependency_cache) + await handler( + matcher=self, + bot=bot, + event=event, + state=self.state, + _stack=stack, + _dependency_cache=dependency_cache, + ) except SkippedException: pass @@ -633,18 +662,13 @@ class Matcher(metaclass=MatcherMeta): self.handlers.insert(0, handler) # type: ignore updater = self.__class__._default_type_updater if updater: - type_ = await updater( - bot, - event, - self.state, # type: ignore - self.type) + type_ = await updater(bot, event, self.state, self.type) # type: ignore else: type_ = "message" updater = self.__class__._default_permission_updater if updater: - permission = await updater(bot, event, self.state, - self.permission) + permission = await updater(bot, event, self.state, self.permission) else: permission = USER(event.get_session_id(), perm=self.permission) @@ -662,23 +686,18 @@ class Matcher(metaclass=MatcherMeta): default_state=self.state, default_parser=self.__class__._default_parser, default_type_updater=self.__class__._default_type_updater, - default_permission_updater=self.__class__. - _default_permission_updater) + default_permission_updater=self.__class__._default_permission_updater, + ) except PausedException: updater = self.__class__._default_type_updater if updater: - type_ = await updater( - bot, - event, - self.state, # type: ignore - self.type) + type_ = await updater(bot, event, self.state, self.type) # type: ignore else: type_ = "message" updater = self.__class__._default_permission_updater if updater: - permission = await updater(bot, event, self.state, - self.permission) + permission = await updater(bot, event, self.state, self.permission) else: permission = USER(event.get_session_id(), perm=self.permission) @@ -696,8 +715,8 @@ class Matcher(metaclass=MatcherMeta): default_state=self.state, default_parser=self.__class__._default_parser, default_type_updater=self.__class__._default_type_updater, - default_permission_updater=self.__class__. - _default_permission_updater) + default_permission_updater=self.__class__._default_permission_updater, + ) except FinishedException: pass except StopPropagation: diff --git a/nonebot/message.py b/nonebot/message.py index 87184362..f2ff0ca9 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -17,9 +17,14 @@ from nonebot.handler import Handler from nonebot.utils import escape_tag from nonebot.matcher import Matcher, matchers from nonebot.exception import NoLogException, StopPropagation, IgnoredException -from nonebot.typing import (T_State, T_DependencyCache, T_RunPreProcessor, - T_RunPostProcessor, T_EventPreProcessor, - T_EventPostProcessor) +from nonebot.typing import ( + T_State, + T_DependencyCache, + T_RunPreProcessor, + T_RunPostProcessor, + T_EventPreProcessor, + T_EventPostProcessor, +) if TYPE_CHECKING: from nonebot.adapters import Bot, Event @@ -30,15 +35,25 @@ _run_preprocessors: Set[Handler] = set() _run_postprocessors: Set[Handler] = set() EVENT_PCS_PARAMS = [ - params.BotParam, params.EventParam, params.StateParam, params.DefaultParam + params.BotParam, + params.EventParam, + params.StateParam, + params.DefaultParam, ] RUN_PREPCS_PARAMS = [ - params.MatcherParam, params.BotParam, params.EventParam, params.StateParam, - params.DefaultParam + params.MatcherParam, + params.BotParam, + params.EventParam, + params.StateParam, + params.DefaultParam, ] RUN_POSTPCS_PARAMS = [ - params.MatcherParam, params.ExceptionParam, params.BotParam, - params.EventParam, params.StateParam, params.DefaultParam + params.MatcherParam, + params.ExceptionParam, + params.BotParam, + params.EventParam, + params.StateParam, + params.DefaultParam, ] @@ -83,13 +98,14 @@ def run_postprocessor(func: T_RunPostProcessor) -> T_RunPostProcessor: async def _check_matcher( - priority: int, - Matcher: Type[Matcher], - bot: "Bot", - event: "Event", - state: T_State, - stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[T_DependencyCache] = None) -> None: + priority: int, + Matcher: Type[Matcher], + bot: "Bot", + event: "Event", + state: T_State, + stack: Optional[AsyncExitStack] = None, + dependency_cache: Optional[T_DependencyCache] = None, +) -> None: if Matcher.expire_time and datetime.now() > Matcher.expire_time: try: matchers[priority].remove(Matcher) @@ -99,13 +115,13 @@ async def _check_matcher( try: if not await Matcher.check_perm( - bot, event, stack, - dependency_cache) or not await Matcher.check_rule( - bot, event, state, stack, dependency_cache): + bot, event, stack, dependency_cache + ) or not await Matcher.check_rule(bot, event, state, stack, dependency_cache): return except Exception as e: logger.opt(colors=True, exception=e).error( - f"Rule check failed for {Matcher}.") + f"Rule check failed for {Matcher}." + ) return if Matcher.temp: @@ -118,36 +134,43 @@ async def _check_matcher( async def _run_matcher( - Matcher: Type[Matcher], - bot: "Bot", - event: "Event", - state: T_State, - stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[T_DependencyCache] = None) -> None: + Matcher: Type[Matcher], + bot: "Bot", + event: "Event", + state: T_State, + stack: Optional[AsyncExitStack] = None, + dependency_cache: Optional[T_DependencyCache] = None, +) -> None: logger.info(f"Event will be handled by {Matcher}") matcher = Matcher() coros = list( map( - lambda x: x(matcher=matcher, - bot=bot, - event=event, - state=state, - _stack=stack, - _dependency_cache=dependency_cache), - _run_preprocessors)) + lambda x: x( + matcher=matcher, + bot=bot, + event=event, + state=state, + _stack=stack, + _dependency_cache=dependency_cache, + ), + _run_preprocessors, + ) + ) if coros: try: await asyncio.gather(*coros) except IgnoredException: logger.opt(colors=True).info( - f"Matcher {matcher} running is cancelled") + f"Matcher {matcher} running is cancelled" + ) return except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running RunPreProcessors. " - "Running cancelled!") + "Running cancelled!" + ) return exception = None @@ -163,14 +186,18 @@ async def _run_matcher( coros = list( map( - lambda x: x(matcher=matcher, - exception=exception, - bot=bot, - event=event, - state=state, - _stack=stack, - _dependency_cache=dependency_cache), - _run_postprocessors)) + lambda x: x( + matcher=matcher, + exception=exception, + bot=bot, + event=event, + state=state, + _stack=stack, + _dependency_cache=dependency_cache, + ), + _run_postprocessors, + ) + ) if coros: try: await asyncio.gather(*coros) @@ -217,12 +244,16 @@ async def handle_event(bot: "Bot", event: "Event") -> None: async with AsyncExitStack() as stack: coros = list( map( - lambda x: x(bot=bot, - event=event, - state=state, - _stack=stack, - _dependency_cache=dependency_cache), - _event_preprocessors)) + lambda x: x( + bot=bot, + event=event, + state=state, + _stack=stack, + _dependency_cache=dependency_cache, + ), + _event_preprocessors, + ) + ) if coros: try: if show_log: @@ -236,7 +267,8 @@ async def handle_event(bot: "Bot", event: "Event") -> None: except Exception as e: logger.opt(colors=True, exception=e).error( "Error when running EventPreProcessors. " - "Event ignored!") + "Event ignored!" + ) return # Trie Match @@ -251,13 +283,13 @@ async def handle_event(bot: "Bot", event: "Event") -> None: logger.debug(f"Checking for matchers in priority {priority}...") pending_tasks = [ - _check_matcher(priority, matcher, bot, event, state.copy(), - stack, dependency_cache) + _check_matcher( + priority, matcher, bot, event, state.copy(), stack, dependency_cache + ) for matcher in matchers[priority] ] - results = await asyncio.gather(*pending_tasks, - return_exceptions=True) + results = await asyncio.gather(*pending_tasks, return_exceptions=True) for result in results: if not isinstance(result, Exception): @@ -272,12 +304,16 @@ async def handle_event(bot: "Bot", event: "Event") -> None: coros = list( map( - lambda x: x(bot=bot, - event=event, - state=state, - _stack=stack, - _dependency_cache=dependency_cache), - _event_postprocessors)) + lambda x: x( + bot=bot, + event=event, + state=state, + _stack=stack, + _dependency_cache=dependency_cache, + ), + _event_postprocessors, + ) + ) if coros: try: if show_log: diff --git a/nonebot/params.py b/nonebot/params.py index 06fb6987..484c4d21 100644 --- a/nonebot/params.py +++ b/nonebot/params.py @@ -10,69 +10,61 @@ from nonebot.utils import generic_check_issubclass class BotParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: - return generic_check_issubclass( - param.annotation, Bot) or (param.annotation == param.empty and - name == "bot") + return generic_check_issubclass(param.annotation, Bot) or ( + param.annotation == param.empty and name == "bot" + ) def _solve(self, bot: Bot, **kwargs: Any) -> Any: return bot class EventParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: - return generic_check_issubclass( - param.annotation, Event) or (param.annotation == param.empty and - name == "event") + return generic_check_issubclass(param.annotation, Event) or ( + param.annotation == param.empty and name == "event" + ) def _solve(self, event: Event, **kwargs: Any) -> Any: return event class StateParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: - return generic_check_issubclass( - param.annotation, Dict) or (param.annotation == param.empty and - name == "state") + return generic_check_issubclass(param.annotation, Dict) or ( + param.annotation == param.empty and name == "state" + ) def _solve(self, state: T_State, **kwargs: Any) -> Any: return state class MatcherParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: - return generic_check_issubclass( - param.annotation, Matcher) or (param.annotation == param.empty and - name == "matcher") + return generic_check_issubclass(param.annotation, Matcher) or ( + param.annotation == param.empty and name == "matcher" + ) def _solve(self, matcher: Optional["Matcher"] = None, **kwargs: Any) -> Any: return matcher class ExceptionParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: - return generic_check_issubclass( - param.annotation, Exception) or (param.annotation == param.empty and - name == "exception") + return generic_check_issubclass(param.annotation, Exception) or ( + param.annotation == param.empty and name == "exception" + ) - def _solve(self, - exception: Optional[Exception] = None, - **kwargs: Any) -> Any: + def _solve(self, exception: Optional[Exception] = None, **kwargs: Any) -> Any: return exception class DefaultParam(Param): - @classmethod def _check(cls, name: str, param: inspect.Parameter) -> bool: return param.default != param.empty diff --git a/nonebot/permission.py b/nonebot/permission.py index 4e7e89da..7724d1dd 100644 --- a/nonebot/permission.py +++ b/nonebot/permission.py @@ -34,11 +34,10 @@ class Permission: from nonebot.utils import run_sync Permission(async_function, run_sync(sync_function)) """ + __slots__ = ("checkers",) - HANDLER_PARAM_TYPES = [ - params.BotParam, params.EventParam, params.DefaultParam - ] + HANDLER_PARAM_TYPES = [params.BotParam, params.EventParam, params.DefaultParam] def __init__(self, *checkers: Union[T_PermissionChecker, Handler]) -> None: """ @@ -48,9 +47,11 @@ class Permission: """ self.checkers = set( - checker if isinstance(checker, Handler) else Handler( - checker, allow_types=self.HANDLER_PARAM_TYPES) - for checker in checkers) + checker + if isinstance(checker, Handler) + else Handler(checker, allow_types=self.HANDLER_PARAM_TYPES) + for checker in checkers + ) """ :说明: @@ -66,8 +67,8 @@ class Permission: bot: Bot, event: Event, stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[Dict[Callable[..., Any], - Any]] = None) -> bool: + dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, + ) -> bool: """ :说明: @@ -87,19 +88,24 @@ class Permission: if not self.checkers: return True results = await asyncio.gather( - *(checker(bot=bot, - event=event, - _stack=stack, - _dependency_cache=dependency_cache) - for checker in self.checkers)) + *( + checker( + bot=bot, + event=event, + _stack=stack, + _dependency_cache=dependency_cache, + ) + for checker in self.checkers + ) + ) return any(results) def __and__(self, other) -> NoReturn: raise RuntimeError("And operation between Permissions is not allowed.") def __or__( - self, other: Optional[Union["Permission", - T_PermissionChecker]]) -> "Permission": + self, other: Optional[Union["Permission", T_PermissionChecker]] + ) -> "Permission": if other is None: return self elif isinstance(other, Permission): @@ -155,15 +161,17 @@ def USER(*user: str, perm: Optional[Permission] = None): """ async def _user(bot: Bot, event: Event) -> bool: - return bool(event.get_session_id() in user and - (perm is None or await perm(bot, event))) + return bool( + event.get_session_id() in user and (perm is None or await perm(bot, event)) + ) return Permission(_user) async def _superuser(bot: Bot, event: Event) -> bool: - return (event.get_type() == "message" and - event.get_user_id() in bot.config.superusers) + return ( + event.get_type() == "message" and event.get_user_id() in bot.config.superusers + ) SUPERUSER = Permission(_superuser) diff --git a/nonebot/plugin/__init__.py b/nonebot/plugin/__init__.py index 69a34e63..e55e1180 100644 --- a/nonebot/plugin/__init__.py +++ b/nonebot/plugin/__init__.py @@ -9,8 +9,9 @@ from typing import List, Optional from contextvars import ContextVar _managers: List["PluginManager"] = [] -_current_plugin: ContextVar[Optional["Plugin"]] = ContextVar("_current_plugin", - default=None) +_current_plugin: ContextVar[Optional["Plugin"]] = ContextVar( + "_current_plugin", default=None +) from .on import on as on from .manager import PluginManager diff --git a/nonebot/plugin/export.py b/nonebot/plugin/export.py index 9c7c3f57..2a792a01 100644 --- a/nonebot/plugin/export.py +++ b/nonebot/plugin/export.py @@ -33,8 +33,7 @@ class Export(dict): return func def __setitem__(self, key, value): - super().__setitem__(key, - Export(value) if isinstance(value, dict) else value) + super().__setitem__(key, Export(value) if isinstance(value, dict) else value) def __setattr__(self, name, value): self[name] = Export(value) if isinstance(value, dict) else value diff --git a/nonebot/plugin/load.py b/nonebot/plugin/load.py index 75850f91..bfd08918 100644 --- a/nonebot/plugin/load.py +++ b/nonebot/plugin/load.py @@ -49,8 +49,9 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]: return manager.load_all_plugins() -def load_all_plugins(module_path: Iterable[str], - plugin_dir: Iterable[str]) -> Set[Plugin]: +def load_all_plugins( + module_path: Iterable[str], plugin_dir: Iterable[str] +) -> Set[Plugin]: """ :说明: @@ -90,8 +91,7 @@ def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]: plugins = data.get("plugins") plugin_dirs = data.get("plugin_dirs") assert isinstance(plugins, list), "plugins must be a list of plugin name" - assert isinstance(plugin_dirs, - list), "plugin_dirs must be a list of directories" + assert isinstance(plugin_dirs, list), "plugin_dirs must be a list of directories" return load_all_plugins(set(plugins), set(plugin_dirs)) @@ -120,14 +120,14 @@ def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]: if nonebot_data: warnings.warn( "[nonebot.plugins] table are now deprecated. Use [tool.nonebot] instead.", - DeprecationWarning) + DeprecationWarning, + ) else: raise ValueError("Cannot find '[tool.nonebot]' in given toml file!") plugins = nonebot_data.get("plugins", []) plugin_dirs = nonebot_data.get("plugin_dirs", []) assert isinstance(plugins, list), "plugins must be a list of plugin name" - assert isinstance(plugin_dirs, - list), "plugin_dirs must be a list of directories" + assert isinstance(plugin_dirs, list), "plugin_dirs must be a list of directories" return load_all_plugins(plugins, plugin_dirs) @@ -163,5 +163,5 @@ def require(name: str) -> Export: """ plugin = get_plugin(name) or load_plugin(name) if not plugin: - raise RuntimeError(f"Cannot load plugin \"{name}\"!") + raise RuntimeError(f'Cannot load plugin "{name}"!') return plugin.export diff --git a/nonebot/plugin/manager.py b/nonebot/plugin/manager.py index e98495d0..a4b375ab 100644 --- a/nonebot/plugin/manager.py +++ b/nonebot/plugin/manager.py @@ -15,7 +15,6 @@ from . import _managers, _current_plugin class PluginManager: - def __init__( self, plugins: Optional[Iterable[str]] = None, @@ -39,14 +38,15 @@ class PluginManager: def _previous_plugins(self) -> List[str]: _pre_managers: List[PluginManager] if self in _managers: - _pre_managers = _managers[:_managers.index(self)] + _pre_managers = _managers[: _managers.index(self)] else: _pre_managers = _managers[:] return [ *chain.from_iterable( [*manager.plugins, *manager.searched_plugins.keys()] - for manager in _pre_managers) + for manager in _pre_managers + ) ] def list_plugins(self) -> Set[str]: @@ -57,13 +57,14 @@ class PluginManager: for module_info in pkgutil.iter_modules(self.search_path): if module_info.name.startswith("_"): continue - if module_info.name in searched_plugins.keys( - ) or module_info.name in previous_plugins: + if ( + module_info.name in searched_plugins.keys() + or module_info.name in previous_plugins + ): raise RuntimeError( f"Plugin already exists: {module_info.name}! Check your plugin name" ) - module_spec = module_info.module_finder.find_spec( - module_info.name, None) + module_spec = module_info.module_finder.find_spec(module_info.name, None) if not module_spec: continue module_path = module_spec.origin @@ -80,14 +81,15 @@ class PluginManager: if name in self.plugins: module = importlib.import_module(name) elif name not in self.searched_plugins: - raise RuntimeError( - f"Plugin not found: {name}! Check your plugin name") + raise RuntimeError(f"Plugin not found: {name}! Check your plugin name") else: module = importlib.import_module( - self._path_to_module_name(self.searched_plugins[name])) + self._path_to_module_name(self.searched_plugins[name]) + ) logger.opt(colors=True).success( - f'Succeeded to import "{escape_tag(name)}"') + f'Succeeded to import "{escape_tag(name)}"' + ) return getattr(module, "__plugin__", None) except Exception as e: logger.opt(colors=True, exception=e).error( @@ -96,16 +98,17 @@ class PluginManager: def load_all_plugins(self) -> Set[Plugin]: return set( - filter(None, - (self.load_plugin(name) for name in self.list_plugins()))) + filter(None, (self.load_plugin(name) for name in self.list_plugins())) + ) class PluginFinder(MetaPathFinder): - - def find_spec(self, - fullname: str, - path: Optional[Sequence[Union[bytes, str]]], - target: Optional[ModuleType] = None): + def find_spec( + self, + fullname: str, + path: Optional[Sequence[Union[bytes, str]]], + target: Optional[ModuleType] = None, + ): if _managers: index = -1 module_spec = PathFinder.find_spec(fullname, path, target) @@ -119,10 +122,11 @@ class PluginFinder(MetaPathFinder): while -index <= len(_managers): manager = _managers[index] - if fullname in manager.plugins or module_path in manager.searched_plugins.values( + if ( + fullname in manager.plugins + or module_path in manager.searched_plugins.values() ): - module_spec.loader = PluginLoader(manager, fullname, - module_origin) + module_spec.loader = PluginLoader(manager, fullname, module_origin) return module_spec index -= 1 @@ -130,7 +134,6 @@ class PluginFinder(MetaPathFinder): class PluginLoader(SourceFileLoader): - def __init__(self, manager: PluginManager, fullname: str, path) -> None: self.manager = manager self.loaded = False diff --git a/nonebot/plugin/on.py b/nonebot/plugin/on.py index 5b0f2979..a60d45d5 100644 --- a/nonebot/plugin/on.py +++ b/nonebot/plugin/on.py @@ -10,8 +10,18 @@ from nonebot.matcher import Matcher from .manager import _current_plugin from nonebot.permission import Permission from nonebot.typing import T_State, T_Handler, T_RuleChecker, T_StateFactory -from nonebot.rule import (PREFIX_KEY, RAW_CMD_KEY, Rule, ArgumentParser, regex, - command, keyword, endswith, startswith, shell_command) +from nonebot.rule import ( + PREFIX_KEY, + RAW_CMD_KEY, + Rule, + ArgumentParser, + regex, + command, + keyword, + endswith, + startswith, + shell_command, +) def _store_matcher(matcher: Type[Matcher]) -> None: @@ -30,17 +40,19 @@ def _get_matcher_module(depth: int = 1) -> Optional[ModuleType]: return sys.modules.get(module_name) -def on(type: str = "", - rule: Optional[Union[Rule, T_RuleChecker]] = None, - permission: Optional[Permission] = None, - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = None, - temp: bool = False, - priority: int = 1, - block: bool = False, - state: Optional[T_State] = None, - state_factory: Optional[T_StateFactory] = None, - _depth: int = 0) -> Type[Matcher]: +def on( + type: str = "", + rule: Optional[Union[Rule, T_RuleChecker]] = None, + permission: Optional[Permission] = None, + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = None, + temp: bool = False, + priority: int = 1, + block: bool = False, + state: Optional[T_State] = None, + state_factory: Optional[T_StateFactory] = None, + _depth: int = 0, +) -> Type[Matcher]: """ :说明: @@ -62,30 +74,34 @@ def on(type: str = "", - ``Type[Matcher]`` """ - matcher = Matcher.new(type, - Rule() & rule, - permission or Permission(), - temp=temp, - priority=priority, - block=block, - handlers=handlers, - plugin=_current_plugin.get(), - module=_get_matcher_module(_depth + 1), - default_state=state, - default_state_factory=state_factory) + matcher = Matcher.new( + type, + Rule() & rule, + permission or Permission(), + temp=temp, + priority=priority, + block=block, + handlers=handlers, + plugin=_current_plugin.get(), + module=_get_matcher_module(_depth + 1), + default_state=state, + default_state_factory=state_factory, + ) _store_matcher(matcher) return matcher -def on_metaevent(rule: Optional[Union[Rule, T_RuleChecker]] = None, - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = None, - temp: bool = False, - priority: int = 1, - block: bool = False, - state: Optional[T_State] = None, - state_factory: Optional[T_StateFactory] = None, - _depth: int = 0) -> Type[Matcher]: +def on_metaevent( + rule: Optional[Union[Rule, T_RuleChecker]] = None, + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = None, + temp: bool = False, + priority: int = 1, + block: bool = False, + state: Optional[T_State] = None, + state_factory: Optional[T_StateFactory] = None, + _depth: int = 0, +) -> Type[Matcher]: """ :说明: @@ -105,31 +121,35 @@ def on_metaevent(rule: Optional[Union[Rule, T_RuleChecker]] = None, - ``Type[Matcher]`` """ - matcher = Matcher.new("meta_event", - Rule() & rule, - Permission(), - temp=temp, - priority=priority, - block=block, - handlers=handlers, - plugin=_current_plugin.get(), - module=_get_matcher_module(_depth + 1), - default_state=state, - default_state_factory=state_factory) + matcher = Matcher.new( + "meta_event", + Rule() & rule, + Permission(), + temp=temp, + priority=priority, + block=block, + handlers=handlers, + plugin=_current_plugin.get(), + module=_get_matcher_module(_depth + 1), + default_state=state, + default_state_factory=state_factory, + ) _store_matcher(matcher) return matcher -def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None, - permission: Optional[Permission] = None, - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = None, - temp: bool = False, - priority: int = 1, - block: bool = True, - state: Optional[T_State] = None, - state_factory: Optional[T_StateFactory] = None, - _depth: int = 0) -> Type[Matcher]: +def on_message( + rule: Optional[Union[Rule, T_RuleChecker]] = None, + permission: Optional[Permission] = None, + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = None, + temp: bool = False, + priority: int = 1, + block: bool = True, + state: Optional[T_State] = None, + state_factory: Optional[T_StateFactory] = None, + _depth: int = 0, +) -> Type[Matcher]: """ :说明: @@ -150,30 +170,34 @@ def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None, - ``Type[Matcher]`` """ - matcher = Matcher.new("message", - Rule() & rule, - permission or Permission(), - temp=temp, - priority=priority, - block=block, - handlers=handlers, - plugin=_current_plugin.get(), - module=_get_matcher_module(_depth + 1), - default_state=state, - default_state_factory=state_factory) + matcher = Matcher.new( + "message", + Rule() & rule, + permission or Permission(), + temp=temp, + priority=priority, + block=block, + handlers=handlers, + plugin=_current_plugin.get(), + module=_get_matcher_module(_depth + 1), + default_state=state, + default_state_factory=state_factory, + ) _store_matcher(matcher) return matcher -def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None, - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = None, - temp: bool = False, - priority: int = 1, - block: bool = False, - state: Optional[T_State] = None, - state_factory: Optional[T_StateFactory] = None, - _depth: int = 0) -> Type[Matcher]: +def on_notice( + rule: Optional[Union[Rule, T_RuleChecker]] = None, + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = None, + temp: bool = False, + priority: int = 1, + block: bool = False, + state: Optional[T_State] = None, + state_factory: Optional[T_StateFactory] = None, + _depth: int = 0, +) -> Type[Matcher]: """ :说明: @@ -193,30 +217,34 @@ def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None, - ``Type[Matcher]`` """ - matcher = Matcher.new("notice", - Rule() & rule, - Permission(), - temp=temp, - priority=priority, - block=block, - handlers=handlers, - plugin=_current_plugin.get(), - module=_get_matcher_module(_depth + 1), - default_state=state, - default_state_factory=state_factory) + matcher = Matcher.new( + "notice", + Rule() & rule, + Permission(), + temp=temp, + priority=priority, + block=block, + handlers=handlers, + plugin=_current_plugin.get(), + module=_get_matcher_module(_depth + 1), + default_state=state, + default_state_factory=state_factory, + ) _store_matcher(matcher) return matcher -def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None, - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = None, - temp: bool = False, - priority: int = 1, - block: bool = False, - state: Optional[T_State] = None, - state_factory: Optional[T_StateFactory] = None, - _depth: int = 0) -> Type[Matcher]: +def on_request( + rule: Optional[Union[Rule, T_RuleChecker]] = None, + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = None, + temp: bool = False, + priority: int = 1, + block: bool = False, + state: Optional[T_State] = None, + state_factory: Optional[T_StateFactory] = None, + _depth: int = 0, +) -> Type[Matcher]: """ :说明: @@ -236,26 +264,30 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None, - ``Type[Matcher]`` """ - matcher = Matcher.new("request", - Rule() & rule, - Permission(), - temp=temp, - priority=priority, - block=block, - handlers=handlers, - plugin=_current_plugin.get(), - module=_get_matcher_module(_depth + 1), - default_state=state, - default_state_factory=state_factory) + matcher = Matcher.new( + "request", + Rule() & rule, + Permission(), + temp=temp, + priority=priority, + block=block, + handlers=handlers, + plugin=_current_plugin.get(), + module=_get_matcher_module(_depth + 1), + default_state=state, + default_state_factory=state_factory, + ) _store_matcher(matcher) return matcher -def on_startswith(msg: Union[str, Tuple[str, ...]], - rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None, - ignorecase: bool = False, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_startswith( + msg: Union[str, Tuple[str, ...]], + rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None, + ignorecase: bool = False, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -278,16 +310,16 @@ def on_startswith(msg: Union[str, Tuple[str, ...]], - ``Type[Matcher]`` """ - return on_message(startswith(msg, ignorecase) & rule, - **kwargs, - _depth=_depth + 1) + return on_message(startswith(msg, ignorecase) & rule, **kwargs, _depth=_depth + 1) -def on_endswith(msg: Union[str, Tuple[str, ...]], - rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None, - ignorecase: bool = False, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_endswith( + msg: Union[str, Tuple[str, ...]], + rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None, + ignorecase: bool = False, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -310,15 +342,15 @@ def on_endswith(msg: Union[str, Tuple[str, ...]], - ``Type[Matcher]`` """ - return on_message(endswith(msg, ignorecase) & rule, - **kwargs, - _depth=_depth + 1) + return on_message(endswith(msg, ignorecase) & rule, **kwargs, _depth=_depth + 1) -def on_keyword(keywords: Set[str], - rule: Optional[Union[Rule, T_RuleChecker]] = None, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_keyword( + keywords: Set[str], + rule: Optional[Union[Rule, T_RuleChecker]] = None, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -343,11 +375,13 @@ def on_keyword(keywords: Set[str], return on_message(keyword(*keywords) & rule, **kwargs, _depth=_depth + 1) -def on_command(cmd: Union[str, Tuple[str, ...]], - rule: Optional[Union[Rule, T_RuleChecker]] = None, - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_command( + cmd: Union[str, Tuple[str, ...]], + rule: Optional[Union[Rule, T_RuleChecker]] = None, + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -382,7 +416,8 @@ def on_command(cmd: Union[str, Tuple[str, ...]], if not segment_text.startswith(state[PREFIX_KEY][RAW_CMD_KEY]): return new_message = message.__class__( - segment_text[len(state[PREFIX_KEY][RAW_CMD_KEY]):].lstrip()) + segment_text[len(state[PREFIX_KEY][RAW_CMD_KEY]) :].lstrip() + ) for new_segment in reversed(new_message): message.insert(0, new_segment) @@ -390,18 +425,19 @@ def on_command(cmd: Union[str, Tuple[str, ...]], handlers.insert(0, _strip_cmd) commands = set([cmd]) | (aliases or set()) - return on_message(command(*commands) & rule, - handlers=handlers, - **kwargs, - _depth=_depth + 1) + return on_message( + command(*commands) & rule, handlers=handlers, **kwargs, _depth=_depth + 1 + ) -def on_shell_command(cmd: Union[str, Tuple[str, ...]], - rule: Optional[Union[Rule, T_RuleChecker]] = None, - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, - parser: Optional[ArgumentParser] = None, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_shell_command( + cmd: Union[str, Tuple[str, ...]], + rule: Optional[Union[Rule, T_RuleChecker]] = None, + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, + parser: Optional[ArgumentParser] = None, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -434,7 +470,8 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]], message = event.get_message() segment = message.pop(0) new_message = message.__class__( - str(segment)[len(state[PREFIX_KEY][RAW_CMD_KEY]):].strip()) + str(segment)[len(state[PREFIX_KEY][RAW_CMD_KEY]) :].strip() + ) for new_segment in reversed(new_message): message.insert(0, new_segment) @@ -442,17 +479,21 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]], handlers.insert(0, _strip_cmd) commands = set([cmd]) | (aliases or set()) - return on_message(shell_command(*commands, parser=parser) & rule, - handlers=handlers, - **kwargs, - _depth=_depth + 1) + return on_message( + shell_command(*commands, parser=parser) & rule, + handlers=handlers, + **kwargs, + _depth=_depth + 1, + ) -def on_regex(pattern: str, - flags: Union[int, re.RegexFlag] = 0, - rule: Optional[Union[Rule, T_RuleChecker]] = None, - _depth: int = 0, - **kwargs) -> Type[Matcher]: +def on_regex( + pattern: str, + flags: Union[int, re.RegexFlag] = 0, + rule: Optional[Union[Rule, T_RuleChecker]] = None, + _depth: int = 0, + **kwargs, +) -> Type[Matcher]: """ :说明: @@ -503,8 +544,7 @@ class CommandGroup: - **说明**: 其他传递给 ``on_command`` 的参数默认值 """ - def command(self, cmd: Union[str, Tuple[str, ...]], - **kwargs) -> Type[Matcher]: + def command(self, cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: """ :说明: @@ -526,8 +566,9 @@ class CommandGroup: final_kwargs.update(kwargs) return on_command(cmd, **final_kwargs, _depth=1) - def shell_command(self, cmd: Union[str, Tuple[str, ...]], - **kwargs) -> Type[Matcher]: + def shell_command( + self, cmd: Union[str, Tuple[str, ...]], **kwargs + ) -> Type[Matcher]: """ :说明: @@ -708,8 +749,9 @@ class MatcherGroup: self.matchers.append(matcher) return matcher - def on_startswith(self, msg: Union[str, Tuple[str, ...]], - **kwargs) -> Type[Matcher]: + def on_startswith( + self, msg: Union[str, Tuple[str, ...]], **kwargs + ) -> Type[Matcher]: """ :说明: @@ -739,8 +781,7 @@ class MatcherGroup: self.matchers.append(matcher) return matcher - def on_endswith(self, msg: Union[str, Tuple[str, ...]], - **kwargs) -> Type[Matcher]: + def on_endswith(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: """ :说明: @@ -799,10 +840,12 @@ class MatcherGroup: self.matchers.append(matcher) return matcher - def on_command(self, - cmd: Union[str, Tuple[str, ...]], - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, - **kwargs) -> Type[Matcher]: + def on_command( + self, + cmd: Union[str, Tuple[str, ...]], + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, + **kwargs, + ) -> Type[Matcher]: """ :说明: @@ -834,12 +877,13 @@ class MatcherGroup: self.matchers.append(matcher) return matcher - def on_shell_command(self, - cmd: Union[str, Tuple[str, ...]], - aliases: Optional[Set[Union[str, Tuple[str, - ...]]]] = None, - parser: Optional[ArgumentParser] = None, - **kwargs) -> Type[Matcher]: + def on_shell_command( + self, + cmd: Union[str, Tuple[str, ...]], + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, + parser: Optional[ArgumentParser] = None, + **kwargs, + ) -> Type[Matcher]: """ :说明: @@ -870,18 +914,15 @@ class MatcherGroup: final_kwargs = self.base_kwargs.copy() final_kwargs.update(kwargs) final_kwargs.pop("type", None) - matcher = on_shell_command(cmd, - aliases=aliases, - parser=parser, - **final_kwargs, - _depth=1) + matcher = on_shell_command( + cmd, aliases=aliases, parser=parser, **final_kwargs, _depth=1 + ) self.matchers.append(matcher) return matcher - def on_regex(self, - pattern: str, - flags: Union[int, re.RegexFlag] = 0, - **kwargs) -> Type[Matcher]: + def on_regex( + self, pattern: str, flags: Union[int, re.RegexFlag] = 0, **kwargs + ) -> Type[Matcher]: """ :说明: diff --git a/nonebot/plugin/on.pyi b/nonebot/plugin/on.pyi index 68e8ad62..affd285b 100644 --- a/nonebot/plugin/on.pyi +++ b/nonebot/plugin/on.pyi @@ -7,361 +7,335 @@ from nonebot.permission import Permission from nonebot.rule import Rule, ArgumentParser from nonebot.typing import T_State, T_Handler, T_RuleChecker, T_StateFactory - -def on(type: str = "", - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - +def on( + type: str = "", + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... def on_metaevent( - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = ..., - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = ..., - *, - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_message( + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_notice( + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_request( + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + *, + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... def on_startswith( - msg: Union[str, Tuple[str, ...]], - rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ..., - ignorecase: bool = ..., + msg: Union[str, Tuple[str, ...]], + rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ..., + ignorecase: bool = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_endswith( + msg: Union[str, Tuple[str, ...]], + rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ..., + ignorecase: bool = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_keyword( + keywords: Set[str], + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_command( + cmd: Union[str, Tuple[str, ...]], + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_shell_command( + cmd: Union[str, Tuple[str, ...]], + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., + parser: Optional[ArgumentParser] = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... +def on_regex( + pattern: str, + flags: Union[int, re.RegexFlag] = ..., + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + *, + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., +) -> Type[Matcher]: ... + +class CommandGroup: + def __init__( + self, + cmd: Union[str, Tuple[str, ...]], *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., permission: Optional[Permission] = ..., handlers: Optional[List[Union[T_Handler, Handler]]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_endswith(msg: Union[str, Tuple[str, ...]], - rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ..., - ignorecase: bool = ..., - *, - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_keyword(keywords: Set[str], - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - *, - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_command(cmd: Union[str, Tuple[str, ...]], - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., - *, - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_shell_command( + state_factory: Optional[T_StateFactory] = ..., + ): ... + def command( + self, cmd: Union[str, Tuple[str, ...]], + *, + aliases: Optional[Set[Union[str, Tuple[str, ...]]]], rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def shell_command( + self, + cmd: Union[str, Tuple[str, ...]], + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + aliases: Optional[Set[Union[str, Tuple[str, ...]]]], + parser: Optional[ArgumentParser] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + +class MatcherGroup: + def __init__( + self, + *, + type: str = ..., + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ): ... + def on( + self, + *, + type: str = ..., + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_metaevent( + self, + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_message( + self, + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_notice( + self, + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_request( + self, + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_startswith( + self, + msg: Union[str, Tuple[str, ...]], + *, + ignorecase: bool = ..., + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_endswith( + self, + msg: Union[str, Tuple[str, ...]], + *, + ignorecase: bool = ..., + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_keyword( + self, + keywords: Set[str], + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_command( + self, + cmd: Union[str, Tuple[str, ...]], + aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... + def on_shell_command( + self, + cmd: Union[str, Tuple[str, ...]], aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., parser: Optional[ArgumentParser] = ..., *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., permission: Optional[Permission] = ..., handlers: Optional[List[Union[T_Handler, Handler]]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -def on_regex(pattern: str, - flags: Union[int, re.RegexFlag] = ..., - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - *, - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -class CommandGroup: - - def __init__(self, - cmd: Union[str, Tuple[str, ...]], - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...): - ... - - def command(self, - cmd: Union[str, Tuple[str, ...]], - *, - aliases: Optional[Set[Union[str, Tuple[str, ...]]]], - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def shell_command( - self, - cmd: Union[str, Tuple[str, ...]], - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - aliases: Optional[Set[Union[str, Tuple[str, ...]]]], - parser: Optional[ArgumentParser] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - -class MatcherGroup: - - def __init__(self, - *, - type: str = ..., - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...): - ... - - def on(self, - *, - type: str = ..., - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_metaevent( - self, - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_message( - self, - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_notice( - self, - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_request( - self, - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_startswith( - self, - msg: Union[str, Tuple[str, ...]], - *, - ignorecase: bool = ..., - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_endswith( - self, - msg: Union[str, Tuple[str, ...]], - *, - ignorecase: bool = ..., - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_keyword( - self, - keywords: Set[str], - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_command( - self, - cmd: Union[str, Tuple[str, ...]], - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - - def on_shell_command( - self, - cmd: Union[str, Tuple[str, ...]], - aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ..., - parser: Optional[ArgumentParser] = ..., - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... - + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... def on_regex( - self, - pattern: str, - flags: Union[int, re.RegexFlag] = ..., - *, - rule: Optional[Union[Rule, T_RuleChecker]] = ..., - permission: Optional[Permission] = ..., - handlers: Optional[List[Union[T_Handler, Handler]]] = ..., - temp: bool = ..., - priority: int = ..., - block: bool = ..., - state: Optional[T_State] = ..., - state_factory: Optional[T_StateFactory] = ...) -> Type[Matcher]: - ... + self, + pattern: str, + flags: Union[int, re.RegexFlag] = ..., + *, + rule: Optional[Union[Rule, T_RuleChecker]] = ..., + permission: Optional[Permission] = ..., + handlers: Optional[List[Union[T_Handler, Handler]]] = ..., + temp: bool = ..., + priority: int = ..., + block: bool = ..., + state: Optional[T_State] = ..., + state_factory: Optional[T_StateFactory] = ..., + ) -> Type[Matcher]: ... diff --git a/nonebot/plugin/plugin.py b/nonebot/plugin/plugin.py index ee5a7c51..49e95dc2 100644 --- a/nonebot/plugin/plugin.py +++ b/nonebot/plugin/plugin.py @@ -15,6 +15,7 @@ plugins: Dict[str, "Plugin"] = {} @dataclass(eq=False) class Plugin(object): """存储插件信息""" + name: str """ - **类型**: ``str`` diff --git a/nonebot/plugins/echo.py b/nonebot/plugins/echo.py index 763fd715..42e99cc3 100644 --- a/nonebot/plugins/echo.py +++ b/nonebot/plugins/echo.py @@ -3,15 +3,18 @@ from functools import reduce from nonebot.rule import to_me from nonebot.plugin import on_command from nonebot.permission import SUPERUSER -from nonebot.adapters.cqhttp import (Message, MessageEvent, MessageSegment, - unescape) +from nonebot.adapters.cqhttp import ( + Message, + MessageEvent, + MessageSegment, + unescape, +) say = on_command("say", to_me(), permission=SUPERUSER) @say.handle() async def say_unescape(event: MessageEvent): - def _unescape(message: Message, segment: MessageSegment): if segment.is_text(): return message.append(unescape(str(segment))) diff --git a/nonebot/plugins/single_session.py b/nonebot/plugins/single_session.py index 7bb4d52e..e76e1a1b 100644 --- a/nonebot/plugins/single_session.py +++ b/nonebot/plugins/single_session.py @@ -1,8 +1,11 @@ from typing import Dict from nonebot.adapters import Event -from nonebot.message import (IgnoredException, run_preprocessor, - run_postprocessor) +from nonebot.message import ( + IgnoredException, + run_preprocessor, + run_postprocessor, +) _running_matcher: Dict[str, int] = {} diff --git a/nonebot/rule.py b/nonebot/rule.py index 2059ceac..e72cc372 100644 --- a/nonebot/rule.py +++ b/nonebot/rule.py @@ -17,8 +17,18 @@ from argparse import Namespace from contextlib import AsyncExitStack from typing_extensions import TypedDict from argparse import ArgumentParser as ArgParser -from typing import (Any, Dict, List, Type, Tuple, Union, Callable, NoReturn, - Optional, Sequence) +from typing import ( + Any, + Dict, + List, + Type, + Tuple, + Union, + Callable, + NoReturn, + Optional, + Sequence, +) from pygtrie import CharTrie @@ -33,10 +43,9 @@ PREFIX_KEY = "_prefix" SUFFIX_KEY = "_suffix" CMD_KEY = "command" RAW_CMD_KEY = "raw_command" -CMD_RESULT = TypedDict("CMD_RESULT", { - "command": Optional[Tuple[str, ...]], - "raw_command": Optional[str] -}) +CMD_RESULT = TypedDict( + "CMD_RESULT", {"command": Optional[Tuple[str, ...]], "raw_command": Optional[str]} +) SHELL_ARGS = "_args" SHELL_ARGV = "_argv" @@ -61,11 +70,14 @@ class Rule: from nonebot.utils import run_sync Rule(async_function, run_sync(sync_function)) """ + __slots__ = ("checkers",) HANDLER_PARAM_TYPES = [ - params.BotParam, params.EventParam, params.StateParam, - params.DefaultParam + params.BotParam, + params.EventParam, + params.StateParam, + params.DefaultParam, ] def __init__(self, *checkers: Union[T_RuleChecker, Handler]) -> None: @@ -76,9 +88,11 @@ class Rule: """ self.checkers = set( - checker if isinstance(checker, Handler) else Handler( - checker, allow_types=self.HANDLER_PARAM_TYPES) - for checker in checkers) + checker + if isinstance(checker, Handler) + else Handler(checker, allow_types=self.HANDLER_PARAM_TYPES) + for checker in checkers + ) """ :说明: @@ -95,8 +109,8 @@ class Rule: event: Event, state: T_State, stack: Optional[AsyncExitStack] = None, - dependency_cache: Optional[Dict[Callable[..., Any], - Any]] = None) -> bool: + dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, + ) -> bool: """ :说明: @@ -117,12 +131,17 @@ class Rule: if not self.checkers: return True results = await asyncio.gather( - *(checker(bot=bot, - event=event, - state=state, - _stack=stack, - _dependency_cache=dependency_cache) - for checker in self.checkers)) + *( + checker( + bot=bot, + event=event, + state=state, + _stack=stack, + _dependency_cache=dependency_cache, + ) + for checker in self.checkers + ) + ) return all(results) def __and__(self, other: Optional[Union["Rule", T_RuleChecker]]) -> "Rule": @@ -156,8 +175,9 @@ class TrieRule: cls.suffix[suffix[::-1]] = value @classmethod - def get_value(cls, bot: Bot, event: Event, - state: T_State) -> Tuple[CMD_RESULT, CMD_RESULT]: + def get_value( + cls, bot: Bot, event: Event, state: T_State + ) -> Tuple[CMD_RESULT, CMD_RESULT]: prefix = CMD_RESULT(command=None, raw_command=None) suffix = CMD_RESULT(command=None, raw_command=None) state[PREFIX_KEY] = prefix @@ -180,8 +200,7 @@ class TrieRule: return prefix, suffix -def startswith(msg: Union[str, Tuple[str, ...]], - ignorecase: bool = False) -> Rule: +def startswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule: """ :说明: @@ -196,7 +215,8 @@ def startswith(msg: Union[str, Tuple[str, ...]], pattern = re.compile( f"^(?:{'|'.join(re.escape(prefix) for prefix in msg)})", - re.IGNORECASE if ignorecase else 0) + re.IGNORECASE if ignorecase else 0, + ) async def _startswith(bot: Bot, event: Event, state: T_State) -> bool: if event.get_type() != "message": @@ -207,8 +227,7 @@ def startswith(msg: Union[str, Tuple[str, ...]], return Rule(_startswith) -def endswith(msg: Union[str, Tuple[str, ...]], - ignorecase: bool = False) -> Rule: +def endswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule: """ :说明: @@ -223,7 +242,8 @@ def endswith(msg: Union[str, Tuple[str, ...]], pattern = re.compile( f"(?:{'|'.join(re.escape(prefix) for prefix in msg)})$", - re.IGNORECASE if ignorecase else 0) + re.IGNORECASE if ignorecase else 0, + ) async def _endswith(bot: Bot, event: Event, state: T_State) -> bool: if event.get_type() != "message": @@ -314,19 +334,22 @@ class ArgumentParser(ArgParser): setattr(self, "message", old_message) def exit(self, status: int = 0, message: Optional[str] = None): - raise ParserExit(status=status, - message=message or getattr(self, "message", None)) + raise ParserExit( + status=status, message=message or getattr(self, "message", None) + ) - def parse_args(self, - args: Optional[Sequence[str]] = None, - namespace: Optional[Namespace] = None) -> Namespace: + def parse_args( + self, + args: Optional[Sequence[str]] = None, + namespace: Optional[Namespace] = None, + ) -> Namespace: setattr(self, "message", "") - return super().parse_args(args=args, - namespace=namespace) # type: ignore + return super().parse_args(args=args, namespace=namespace) # type: ignore -def shell_command(*cmds: Union[str, Tuple[str, ...]], - parser: Optional[ArgumentParser] = None) -> Rule: +def shell_command( + *cmds: Union[str, Tuple[str, ...]], parser: Optional[ArgumentParser] = None +) -> Rule: r""" :说明: @@ -361,8 +384,7 @@ def shell_command(*cmds: Union[str, Tuple[str, ...]], \:\:\: """ if not isinstance(parser, ArgumentParser): - raise TypeError( - "`parser` must be an instance of nonebot.rule.ArgumentParser") + raise TypeError("`parser` must be an instance of nonebot.rule.ArgumentParser") config = get_driver().config command_start = config.command_start @@ -382,8 +404,7 @@ def shell_command(*cmds: Union[str, Tuple[str, ...]], async def _shell_command(event: Event, state: T_State) -> bool: if state[PREFIX_KEY][CMD_KEY] in commands: message = str(event.get_message()) - strip_message = message[len(state[PREFIX_KEY][RAW_CMD_KEY] - ):].lstrip() + strip_message = message[len(state[PREFIX_KEY][RAW_CMD_KEY]) :].lstrip() state[SHELL_ARGV] = shlex.split(strip_message) if parser: try: diff --git a/nonebot/typing.py b/nonebot/typing.py index 3394cd6f..04632c72 100644 --- a/nonebot/typing.py +++ b/nonebot/typing.py @@ -18,8 +18,17 @@ https://docs.python.org/3/library/typing.html """ -from typing import (TYPE_CHECKING, Any, Dict, Union, TypeVar, Callable, - NoReturn, Optional, Awaitable) +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Union, + TypeVar, + Callable, + NoReturn, + Optional, + Awaitable, +) if TYPE_CHECKING: from nonebot.adapters import Bot, Event @@ -29,10 +38,8 @@ T_Wrapped = TypeVar("T_Wrapped", bound=Callable) def overrides(InterfaceClass: object): - def overrider(func: T_Wrapped) -> T_Wrapped: - assert func.__name__ in dir( - InterfaceClass), f"Error method: {func.__name__}" + assert func.__name__ in dir(InterfaceClass), f"Error method: {func.__name__}" return func return overrider @@ -80,7 +87,8 @@ T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[None]] ``bot.call_api`` 时执行的函数 """ T_CalledAPIHook = Callable[ - ["Bot", Optional[Exception], str, Dict[str, Any], Any], Awaitable[None]] + ["Bot", Optional[Exception], str, Dict[str, Any], Any], Awaitable[None] +] """ :类型: ``Callable[[Bot, Optional[Exception], str, Dict[str, Any], Any], Awaitable[None]]`` @@ -193,8 +201,9 @@ T_DependencyCache = Dict[T_Handler, Any] 依赖缓存, 用于存储依赖函数的返回值 """ -T_ArgsParser = Callable[["Bot", "Event", T_State], Union[Awaitable[None], - Awaitable[NoReturn]]] +T_ArgsParser = Callable[ + ["Bot", "Event", T_State], Union[Awaitable[None], Awaitable[NoReturn]] +] """ :类型: ``Callable[[Bot, Event, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`` @@ -210,8 +219,9 @@ T_TypeUpdater = Callable[["Bot", "Event", T_State, str], Awaitable[str]] TypeUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新响应的事件类型。默认会更新为 ``message``。 """ -T_PermissionUpdater = Callable[["Bot", "Event", T_State, "Permission"], - Awaitable["Permission"]] +T_PermissionUpdater = Callable[ + ["Bot", "Event", T_State, "Permission"], Awaitable["Permission"] +] """ :类型: ``Callable[[Bot, Event, T_State, Permission], Awaitable[Permission]]`` diff --git a/nonebot/utils.py b/nonebot/utils.py index 32eca0db..001ee43e 100644 --- a/nonebot/utils.py +++ b/nonebot/utils.py @@ -8,8 +8,19 @@ from functools import wraps, partial from contextlib import asynccontextmanager from typing_extensions import GenericAlias # type: ignore from typing_extensions import ParamSpec, get_args, get_origin -from typing import (Any, Type, Deque, Tuple, Union, TypeVar, Callable, Optional, - Awaitable, AsyncGenerator, ContextManager) +from typing import ( + Any, + Type, + Deque, + Tuple, + Union, + TypeVar, + Callable, + Optional, + Awaitable, + AsyncGenerator, + ContextManager, +) from nonebot.log import logger from nonebot.typing import overrides @@ -37,15 +48,16 @@ def escape_tag(s: str) -> str: def generic_check_issubclass( - cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], - ...]]) -> bool: + cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]] +) -> bool: try: return issubclass(cls, class_or_tuple) except TypeError: if get_origin(cls) is Union: for type_ in get_args(cls): if type_ is not type(None) and not generic_check_issubclass( - type_, class_or_tuple): + type_, class_or_tuple + ): return False return True elif isinstance(cls, GenericAlias): @@ -104,7 +116,8 @@ def run_sync(func: Callable[P, R]) -> Callable[P, Awaitable[R]]: @asynccontextmanager async def run_sync_ctx_manager( - cm: ContextManager[T],) -> AsyncGenerator[T, None]: + cm: ContextManager[T], +) -> AsyncGenerator[T, None]: try: yield await run_sync(cm.__enter__)() except Exception as e: @@ -122,7 +135,6 @@ def get_name(obj: Any) -> str: class CacheLock: - def __init__(self): self._waiters: Optional[Deque[asyncio.Future]] = None self._locked = False @@ -144,8 +156,9 @@ class CacheLock: return self._locked async def acquire(self): - if (not self._locked and (self._waiters is None or - all(w.cancelled() for w in self._waiters))): + if not self._locked and ( + self._waiters is None or all(w.cancelled() for w in self._waiters) + ): self._locked = True return True @@ -223,6 +236,7 @@ def logger_wrapper(logger_name: str): def log(level: str, message: str, exception: Optional[Exception] = None): return logger.opt(colors=True, exception=exception).log( - level, f"{escape_tag(logger_name)} | " + message) + level, f"{escape_tag(logger_name)} | " + message + ) return log diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.py index 554e1209..be88edce 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.py @@ -12,8 +12,15 @@ from nonebot.typing import overrides from nonebot.message import handle_event from nonebot.adapters import Bot as BaseBot from nonebot.utils import DataclassEncoder, escape_tag -from nonebot.drivers import (Driver, WebSocket, HTTPRequest, HTTPResponse, - ForwardDriver, HTTPConnection, WebSocketSetup) +from nonebot.drivers import ( + Driver, + WebSocket, + HTTPRequest, + HTTPResponse, + ForwardDriver, + HTTPConnection, + WebSocketSetup, +) from .utils import log, escape from .config import Config as CQHTTPConfig @@ -49,15 +56,12 @@ async def _check_reply(bot: "Bot", event: "Event"): return try: - index = list(map(lambda x: x.type == "reply", - event.message)).index(True) + index = list(map(lambda x: x.type == "reply", event.message)).index(True) except ValueError: return msg_seg = event.message[index] try: - event.reply = Reply.parse_obj(await - bot.get_msg(message_id=msg_seg.data["id"] - )) + event.reply = Reply.parse_obj(await bot.get_msg(message_id=msg_seg.data["id"])) except Exception as e: log("WARNING", f"Error when getting message reply info: {repr(e)}", e) return @@ -68,8 +72,7 @@ async def _check_reply(bot: "Bot", event: "Event"): if len(event.message) > index and event.message[index].type == "at": del event.message[index] if len(event.message) > index and event.message[index].type == "text": - event.message[index].data["text"] = event.message[index].data[ - "text"].lstrip() + event.message[index].data["text"] = event.message[index].data["text"].lstrip() if not event.message[index].data["text"]: del event.message[index] if not event.message: @@ -99,23 +102,24 @@ def _check_at_me(bot: "Bot", event: "Event"): else: def _is_at_me_seg(segment: MessageSegment): - return segment.type == "at" and str(segment.data.get( - "qq", "")) == str(event.self_id) + return segment.type == "at" and str(segment.data.get("qq", "")) == str( + event.self_id + ) # check the first segment if _is_at_me_seg(event.message[0]): event.to_me = True event.message.pop(0) if event.message and event.message[0].type == "text": - event.message[0].data["text"] = event.message[0].data[ - "text"].lstrip() + event.message[0].data["text"] = event.message[0].data["text"].lstrip() if not event.message[0].data["text"]: del event.message[0] if event.message and _is_at_me_seg(event.message[0]): event.message.pop(0) if event.message and event.message[0].type == "text": - event.message[0].data["text"] = event.message[0].data[ - "text"].lstrip() + event.message[0].data["text"] = ( + event.message[0].data["text"].lstrip() + ) if not event.message[0].data["text"]: del event.message[0] @@ -123,9 +127,11 @@ def _check_at_me(bot: "Bot", event: "Event"): # check the last segment i = -1 last_msg_seg = event.message[i] - if last_msg_seg.type == "text" and \ - not last_msg_seg.data["text"].strip() and \ - len(event.message) >= 2: + if ( + last_msg_seg.type == "text" + and not last_msg_seg.data["text"].strip() + and len(event.message) >= 2 + ): i -= 1 last_msg_seg = event.message[i] @@ -161,13 +167,12 @@ def _check_nickname(bot: "Bot", event: "Event"): if nicknames: # check if the user is calling me with my nickname nickname_regex = "|".join(nicknames) - m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, - re.IGNORECASE) + m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, re.IGNORECASE) if m: nickname = m.group(1) log("DEBUG", f"User is calling me {nickname}") event.to_me = True - first_msg_seg.data["text"] = first_text[m.end():] + first_msg_seg.data["text"] = first_text[m.end() :] def _handle_api_result(result: Optional[Dict[str, Any]]) -> Any: @@ -206,8 +211,9 @@ class ResultStore: @classmethod def add_result(cls, result: Dict[str, Any]): - if isinstance(result.get("echo"), dict) and \ - isinstance(result["echo"].get("seq"), int): + if isinstance(result.get("echo"), dict) and isinstance( + result["echo"].get("seq"), int + ): future = cls._futures.get(result["echo"]["seq"]) if future: future.set_result(result) @@ -228,6 +234,7 @@ class Bot(BaseBot): """ CQHTTP 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 """ + cqhttp_config: CQHTTPConfig @property @@ -249,22 +256,25 @@ class Bot(BaseBot): elif isinstance(driver, ForwardDriver) and cls.cqhttp_config.ws_urls: for self_id, url in cls.cqhttp_config.ws_urls.items(): try: - headers = { - "authorization": - f"Bearer {cls.cqhttp_config.access_token}" - } if cls.cqhttp_config.access_token else {} + headers = ( + {"authorization": f"Bearer {cls.cqhttp_config.access_token}"} + if cls.cqhttp_config.access_token + else {} + ) driver.setup_websocket( - WebSocketSetup("cqhttp", self_id, url, headers=headers)) + WebSocketSetup("cqhttp", self_id, url, headers=headers) + ) except Exception as e: logger.opt(colors=True, exception=e).error( f"Bad url {escape_tag(url)} for bot {escape_tag(self_id)} " - "in cqhttp forward websocket") + "in cqhttp forward websocket" + ) @classmethod @overrides(BaseBot) async def check_permission( - cls, driver: Driver, - request: HTTPConnection) -> Tuple[Optional[str], HTTPResponse]: + cls, driver: Driver, request: HTTPConnection + ) -> Tuple[Optional[str], HTTPResponse]: """ :说明: @@ -286,22 +296,26 @@ class Bot(BaseBot): if not x_signature: log("WARNING", "Missing Signature Header") return None, HTTPResponse(401, b"Missing Signature") - sig = hmac.new(secret.encode("utf-8"), request.body, - "sha1").hexdigest() + sig = hmac.new(secret.encode("utf-8"), request.body, "sha1").hexdigest() if x_signature != "sha1=" + sig: log("WARNING", "Signature Header is invalid") return None, HTTPResponse(403, b"Signature is invalid") access_token = cqhttp_config.access_token - if access_token and access_token != token and isinstance( - request, WebSocket): + if access_token and access_token != token and isinstance(request, WebSocket): log( - "WARNING", "Authorization Header is invalid" - if token else "Missing Authorization Header") + "WARNING", + "Authorization Header is invalid" + if token + else "Missing Authorization Header", + ) return None, HTTPResponse( - 403, b"Authorization Header is invalid" - if token else b"Missing Authorization Header") - return str(x_self_id), HTTPResponse(204, b'') + 403, + b"Authorization Header is invalid" + if token + else b"Missing Authorization Header", + ) + return str(x_self_id), HTTPResponse(204, b"") @overrides(BaseBot) async def handle_message(self, message: bytes): @@ -320,7 +334,7 @@ class Bot(BaseBot): return try: - post_type = data['post_type'] + post_type = data["post_type"] detail_type = data.get(f"{post_type}_type") detail_type = f".{detail_type}" if detail_type else "" sub_type = data.get("sub_type") @@ -352,17 +366,13 @@ class Bot(BaseBot): if isinstance(self.request, WebSocket): seq = ResultStore.get_seq() json_data = json.dumps( - { - "action": api, - "params": data, - "echo": { - "seq": seq - } - }, - cls=DataclassEncoder) + {"action": api, "params": data, "echo": {"seq": seq}}, + cls=DataclassEncoder, + ) await self.request.send(json_data) - return _handle_api_result(await ResultStore.fetch( - seq, self.config.api_timeout)) + return _handle_api_result( + await ResultStore.fetch(seq, self.config.api_timeout) + ) elif isinstance(self.request, HTTPRequest): api_root = self.config.api_root.get(self.self_id) @@ -373,22 +383,25 @@ class Bot(BaseBot): headers = {"Content-Type": "application/json"} if self.cqhttp_config.access_token is not None: - headers[ - "Authorization"] = "Bearer " + self.cqhttp_config.access_token + headers["Authorization"] = "Bearer " + self.cqhttp_config.access_token try: - async with httpx.AsyncClient(headers=headers, - follow_redirects=True) as client: + async with httpx.AsyncClient( + headers=headers, follow_redirects=True + ) as client: response = await client.post( api_root + api, content=json.dumps(data, cls=DataclassEncoder), - timeout=self.config.api_timeout) + timeout=self.config.api_timeout, + ) if 200 <= response.status_code < 300: result = response.json() return _handle_api_result(result) - raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code}") + raise NetworkError( + f"HTTP request received unexpected " + f"status code: {response.status_code}" + ) except httpx.InvalidURL: raise NetworkError("API root url invalid") except httpx.HTTPError: @@ -418,11 +431,13 @@ class Bot(BaseBot): return await super().call_api(api, **data) @overrides(BaseBot) - async def send(self, - event: Event, - message: Union[str, Message, MessageSegment], - at_sender: bool = False, - **kwargs) -> Any: + async def send( + self, + event: Event, + message: Union[str, Message, MessageSegment], + at_sender: bool = False, + **kwargs, + ) -> Any: """ :说明: @@ -445,8 +460,9 @@ class Bot(BaseBot): - ``NetworkError``: 网络错误 - ``ActionFailed``: API 调用失败 """ - message = escape(message, escape_comma=False) if isinstance( - message, str) else message + message = ( + escape(message, escape_comma=False) if isinstance(message, str) else message + ) msg = message if isinstance(message, Message) else Message(message) at_sender = at_sender and bool(getattr(event, "user_id", None)) diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.pyi b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.pyi index 8152bb6f..39ad4122 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.pyi +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/bot.pyi @@ -8,7 +8,6 @@ from nonebot.drivers import Driver, WebSocket from .event import Event from .message import Message, MessageSegment - def get_auth_bearer(access_token: Optional[str] = ...) -> Optional[str]: ... diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/config.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/config.py index ee894893..12908073 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/config.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/config.py @@ -1,6 +1,6 @@ from typing import Dict, Optional -from pydantic import Field, BaseModel, AnyUrl +from pydantic import Field, AnyUrl, BaseModel # priority: alias > origin @@ -14,11 +14,10 @@ class Config(BaseModel): - ``secret`` / ``cqhttp_secret``: CQHTTP HTTP 上报数据签名口令 - ``ws_urls`` / ``cqhttp_ws_urls``: CQHTTP 正向 Websocket 连接 Bot ID、目标 URL 字典 """ - access_token: Optional[str] = Field(default=None, - alias="cqhttp_access_token") + + access_token: Optional[str] = Field(default=None, alias="cqhttp_access_token") secret: Optional[str] = Field(default=None, alias="cqhttp_secret") - ws_urls: Dict[str, AnyUrl] = Field(default_factory=set, - alias="cqhttp_ws_urls") + ws_urls: Dict[str, AnyUrl] = Field(default_factory=set, alias="cqhttp_ws_urls") class Config: extra = "ignore" diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/event.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/event.py index 690a8437..21c7b34c 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/event.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/event.py @@ -5,12 +5,13 @@ from typing import TYPE_CHECKING, List, Type, Optional from pydantic import BaseModel from pygtrie import StringTrie -from .message import Message from nonebot.typing import overrides from nonebot.utils import escape_tag -from .exception import NoLogException from nonebot.adapters import Event as BaseEvent +from .message import Message +from .exception import NoLogException + if TYPE_CHECKING: from .bot import Bot @@ -22,6 +23,7 @@ class Event(BaseEvent): .. _CQHTTP 文档: https://github.com/howmanybots/onebot/blob/master/README.md """ + __event__ = "" time: int self_id: int @@ -118,6 +120,7 @@ class Status(BaseModel): # Message Events class MessageEvent(Event): """消息事件""" + __event__ = "message" post_type: Literal["message"] sub_type: str @@ -144,8 +147,9 @@ class MessageEvent(Event): @overrides(Event) def get_event_name(self) -> str: sub_type = getattr(self, "sub_type", None) - return f"{self.post_type}.{self.message_type}" + (f".{sub_type}" - if sub_type else "") + return f"{self.post_type}.{self.message_type}" + ( + f".{sub_type}" if sub_type else "" + ) @overrides(Event) def get_message(self) -> Message: @@ -170,20 +174,29 @@ class MessageEvent(Event): class PrivateMessageEvent(MessageEvent): """私聊消息""" + __event__ = "message.private" message_type: Literal["private"] @overrides(Event) def get_event_description(self) -> str: - return (f'Message {self.message_id} from {self.user_id} "' + "".join( - map( - lambda x: escape_tag(str(x)) - if x.is_text() else f"{escape_tag(str(x))}", - self.message)) + '"') + return ( + f'Message {self.message_id} from {self.user_id} "' + + "".join( + map( + lambda x: escape_tag(str(x)) + if x.is_text() + else f"{escape_tag(str(x))}", + self.message, + ) + ) + + '"' + ) class GroupMessageEvent(MessageEvent): """群消息""" + __event__ = "message.group" message_type: Literal["group"] group_id: int @@ -196,8 +209,13 @@ class GroupMessageEvent(MessageEvent): + "".join( map( lambda x: escape_tag(str(x)) - if x.is_text() else f"{escape_tag(str(x))}", - self.message)) + '"') + if x.is_text() + else f"{escape_tag(str(x))}", + self.message, + ) + ) + + '"' + ) @overrides(MessageEvent) def get_session_id(self) -> str: @@ -207,6 +225,7 @@ class GroupMessageEvent(MessageEvent): # Notice Events class NoticeEvent(Event): """通知事件""" + __event__ = "notice" post_type: Literal["notice"] notice_type: str @@ -214,12 +233,14 @@ class NoticeEvent(Event): @overrides(Event) def get_event_name(self) -> str: sub_type = getattr(self, "sub_type", None) - return f"{self.post_type}.{self.notice_type}" + (f".{sub_type}" - if sub_type else "") + return f"{self.post_type}.{self.notice_type}" + ( + f".{sub_type}" if sub_type else "" + ) class GroupUploadNoticeEvent(NoticeEvent): """群文件上传事件""" + __event__ = "notice.group_upload" notice_type: Literal["group_upload"] user_id: int @@ -237,6 +258,7 @@ class GroupUploadNoticeEvent(NoticeEvent): class GroupAdminNoticeEvent(NoticeEvent): """群管理员变动""" + __event__ = "notice.group_admin" notice_type: Literal["group_admin"] sub_type: str @@ -258,6 +280,7 @@ class GroupAdminNoticeEvent(NoticeEvent): class GroupDecreaseNoticeEvent(NoticeEvent): """群成员减少事件""" + __event__ = "notice.group_decrease" notice_type: Literal["group_decrease"] sub_type: str @@ -280,6 +303,7 @@ class GroupDecreaseNoticeEvent(NoticeEvent): class GroupIncreaseNoticeEvent(NoticeEvent): """群成员增加事件""" + __event__ = "notice.group_increase" notice_type: Literal["group_increase"] sub_type: str @@ -302,6 +326,7 @@ class GroupIncreaseNoticeEvent(NoticeEvent): class GroupBanNoticeEvent(NoticeEvent): """群禁言事件""" + __event__ = "notice.group_ban" notice_type: Literal["group_ban"] sub_type: str @@ -325,6 +350,7 @@ class GroupBanNoticeEvent(NoticeEvent): class FriendAddNoticeEvent(NoticeEvent): """好友添加事件""" + __event__ = "notice.friend_add" notice_type: Literal["friend_add"] user_id: int @@ -340,6 +366,7 @@ class FriendAddNoticeEvent(NoticeEvent): class GroupRecallNoticeEvent(NoticeEvent): """群消息撤回事件""" + __event__ = "notice.group_recall" notice_type: Literal["group_recall"] user_id: int @@ -362,6 +389,7 @@ class GroupRecallNoticeEvent(NoticeEvent): class FriendRecallNoticeEvent(NoticeEvent): """好友消息撤回事件""" + __event__ = "notice.friend_recall" notice_type: Literal["friend_recall"] user_id: int @@ -378,6 +406,7 @@ class FriendRecallNoticeEvent(NoticeEvent): class NotifyEvent(NoticeEvent): """提醒事件""" + __event__ = "notice.notify" notice_type: Literal["notify"] sub_type: str @@ -395,6 +424,7 @@ class NotifyEvent(NoticeEvent): class PokeNotifyEvent(NotifyEvent): """戳一戳提醒事件""" + __event__ = "notice.notify.poke" sub_type: Literal["poke"] target_id: int @@ -413,6 +443,7 @@ class PokeNotifyEvent(NotifyEvent): class LuckyKingNotifyEvent(NotifyEvent): """群红包运气王提醒事件""" + __event__ = "notice.notify.lucky_king" sub_type: Literal["lucky_king"] target_id: int @@ -432,6 +463,7 @@ class LuckyKingNotifyEvent(NotifyEvent): class HonorNotifyEvent(NotifyEvent): """群荣誉变更提醒事件""" + __event__ = "notice.notify.honor" sub_type: Literal["honor"] honor_type: str @@ -444,6 +476,7 @@ class HonorNotifyEvent(NotifyEvent): # Request Events class RequestEvent(Event): """请求事件""" + __event__ = "request" post_type: Literal["request"] request_type: str @@ -451,12 +484,14 @@ class RequestEvent(Event): @overrides(Event) def get_event_name(self) -> str: sub_type = getattr(self, "sub_type", None) - return f"{self.post_type}.{self.request_type}" + (f".{sub_type}" - if sub_type else "") + return f"{self.post_type}.{self.request_type}" + ( + f".{sub_type}" if sub_type else "" + ) class FriendRequestEvent(RequestEvent): """加好友请求事件""" + __event__ = "request.friend" request_type: Literal["friend"] user_id: int @@ -472,9 +507,9 @@ class FriendRequestEvent(RequestEvent): return str(self.user_id) async def approve(self, bot: "Bot", remark: str = ""): - return await bot.set_friend_add_request(flag=self.flag, - approve=True, - remark=remark) + return await bot.set_friend_add_request( + flag=self.flag, approve=True, remark=remark + ) async def reject(self, bot: "Bot"): return await bot.set_friend_add_request(flag=self.flag, approve=False) @@ -482,6 +517,7 @@ class FriendRequestEvent(RequestEvent): class GroupRequestEvent(RequestEvent): """加群请求/邀请事件""" + __event__ = "request.group" request_type: Literal["group"] sub_type: str @@ -499,20 +535,20 @@ class GroupRequestEvent(RequestEvent): return f"group_{self.group_id}_{self.user_id}" async def approve(self, bot: "Bot"): - return await bot.set_group_add_request(flag=self.flag, - sub_type=self.sub_type, - approve=True) + return await bot.set_group_add_request( + flag=self.flag, sub_type=self.sub_type, approve=True + ) async def reject(self, bot: "Bot", reason: str = ""): - return await bot.set_group_add_request(flag=self.flag, - sub_type=self.sub_type, - approve=False, - reason=reason) + return await bot.set_group_add_request( + flag=self.flag, sub_type=self.sub_type, approve=False, reason=reason + ) # Meta Events class MetaEvent(Event): """元事件""" + __event__ = "meta_event" post_type: Literal["meta_event"] meta_event_type: str @@ -520,8 +556,9 @@ class MetaEvent(Event): @overrides(Event) def get_event_name(self) -> str: sub_type = getattr(self, "sub_type", None) - return f"{self.post_type}.{self.meta_event_type}" + (f".{sub_type}" if - sub_type else "") + return f"{self.post_type}.{self.meta_event_type}" + ( + f".{sub_type}" if sub_type else "" + ) @overrides(Event) def get_log_string(self) -> str: @@ -530,6 +567,7 @@ class MetaEvent(Event): class LifecycleMetaEvent(MetaEvent): """生命周期元事件""" + __event__ = "meta_event.lifecycle" meta_event_type: Literal["lifecycle"] sub_type: str @@ -537,6 +575,7 @@ class LifecycleMetaEvent(MetaEvent): class HeartbeatMetaEvent(MetaEvent): """心跳元事件""" + __event__ = "meta_event.heartbeat" meta_event_type: Literal["heartbeat"] status: Status @@ -567,12 +606,28 @@ def get_event_model(event_name) -> List[Type[Event]]: __all__ = [ - "Event", "MessageEvent", "PrivateMessageEvent", "GroupMessageEvent", - "NoticeEvent", "GroupUploadNoticeEvent", "GroupAdminNoticeEvent", - "GroupDecreaseNoticeEvent", "GroupIncreaseNoticeEvent", - "GroupBanNoticeEvent", "FriendAddNoticeEvent", "GroupRecallNoticeEvent", - "FriendRecallNoticeEvent", "NotifyEvent", "PokeNotifyEvent", - "LuckyKingNotifyEvent", "HonorNotifyEvent", "RequestEvent", - "FriendRequestEvent", "GroupRequestEvent", "MetaEvent", - "LifecycleMetaEvent", "HeartbeatMetaEvent", "get_event_model" + "Event", + "MessageEvent", + "PrivateMessageEvent", + "GroupMessageEvent", + "NoticeEvent", + "GroupUploadNoticeEvent", + "GroupAdminNoticeEvent", + "GroupDecreaseNoticeEvent", + "GroupIncreaseNoticeEvent", + "GroupBanNoticeEvent", + "FriendAddNoticeEvent", + "GroupRecallNoticeEvent", + "FriendRecallNoticeEvent", + "NotifyEvent", + "PokeNotifyEvent", + "LuckyKingNotifyEvent", + "HonorNotifyEvent", + "RequestEvent", + "FriendRequestEvent", + "GroupRequestEvent", + "MetaEvent", + "LifecycleMetaEvent", + "HeartbeatMetaEvent", + "get_event_model", ] diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/exception.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/exception.py index 4d3fd1e9..1ea01f80 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/exception.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/exception.py @@ -8,7 +8,6 @@ from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable class CQHTTPAdapterException(AdapterException): - def __init__(self): super().__init__("cqhttp") @@ -33,8 +32,11 @@ class ActionFailed(BaseActionFailed, CQHTTPAdapterException): self.info = kwargs def __repr__(self): - return f"" + return ( + f"" + ) def __str__(self): return self.__repr__() diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/message.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/message.py index 37db4e88..8ca6378a 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/message.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/message.py @@ -5,10 +5,11 @@ from base64 import b64encode from typing import Any, Type, Tuple, Union, Mapping, Iterable, Optional, cast from nonebot.typing import overrides -from .utils import log, _b2s, escape, unescape from nonebot.adapters import Message as BaseMessage from nonebot.adapters import MessageSegment as BaseMessageSegment +from .utils import log, _b2s, escape, unescape + class MessageSegment(BaseMessageSegment["Message"]): """ @@ -27,23 +28,24 @@ class MessageSegment(BaseMessageSegment["Message"]): # process special types if type_ == "text": - return escape( - data.get("text", ""), # type: ignore - escape_comma=False) + return escape(data.get("text", ""), escape_comma=False) # type: ignore params = ",".join( - [f"{k}={escape(str(v))}" for k, v in data.items() if v is not None]) + [f"{k}={escape(str(v))}" for k, v in data.items() if v is not None] + ) return f"[CQ:{type_}{',' if params else ''}{params}]" @overrides(BaseMessageSegment) def __add__(self, other) -> "Message": - return Message(self) + (MessageSegment.text(other) if isinstance( - other, str) else other) + return Message(self) + ( + MessageSegment.text(other) if isinstance(other, str) else other + ) @overrides(BaseMessageSegment) def __radd__(self, other) -> "Message": - return (MessageSegment.text(other) - if isinstance(other, str) else Message(other)) + self + return ( + MessageSegment.text(other) if isinstance(other, str) else Message(other) + ) + self @overrides(BaseMessageSegment) def is_text(self) -> bool: @@ -83,11 +85,13 @@ class MessageSegment(BaseMessageSegment["Message"]): return MessageSegment("forward", {"id": id_}) @staticmethod - def image(file: Union[str, bytes, BytesIO, Path], - type_: Optional[str] = None, - cache: bool = True, - proxy: bool = True, - timeout: Optional[int] = None) -> "MessageSegment": + def image( + file: Union[str, bytes, BytesIO, Path], + type_: Optional[str] = None, + cache: bool = True, + proxy: bool = True, + timeout: Optional[int] = None, + ) -> "MessageSegment": if isinstance(file, BytesIO): file = file.getvalue() if isinstance(file, bytes): @@ -95,74 +99,85 @@ class MessageSegment(BaseMessageSegment["Message"]): elif isinstance(file, Path): file = f"file:///{file.resolve()}" return MessageSegment( - "image", { + "image", + { "file": file, "type": type_, "cache": _b2s(cache), "proxy": _b2s(proxy), - "timeout": timeout - }) + "timeout": timeout, + }, + ) @staticmethod def json(data: str) -> "MessageSegment": return MessageSegment("json", {"data": data}) @staticmethod - def location(latitude: float, - longitude: float, - title: Optional[str] = None, - content: Optional[str] = None) -> "MessageSegment": + def location( + latitude: float, + longitude: float, + title: Optional[str] = None, + content: Optional[str] = None, + ) -> "MessageSegment": return MessageSegment( - "location", { + "location", + { "lat": str(latitude), "lon": str(longitude), "title": title, - "content": content - }) + "content": content, + }, + ) @staticmethod def music(type_: str, id_: int) -> "MessageSegment": return MessageSegment("music", {"type": type_, "id": id_}) @staticmethod - def music_custom(url: str, - audio: str, - title: str, - content: Optional[str] = None, - img_url: Optional[str] = None) -> "MessageSegment": + def music_custom( + url: str, + audio: str, + title: str, + content: Optional[str] = None, + img_url: Optional[str] = None, + ) -> "MessageSegment": return MessageSegment( - "music", { + "music", + { "type": "custom", "url": url, "audio": audio, "title": title, "content": content, - "image": img_url - }) + "image": img_url, + }, + ) @staticmethod def node(id_: int) -> "MessageSegment": return MessageSegment("node", {"id": str(id_)}) @staticmethod - def node_custom(user_id: int, nickname: str, - content: Union[str, "Message"]) -> "MessageSegment": - return MessageSegment("node", { - "user_id": str(user_id), - "nickname": nickname, - "content": content - }) + def node_custom( + user_id: int, nickname: str, content: Union[str, "Message"] + ) -> "MessageSegment": + return MessageSegment( + "node", {"user_id": str(user_id), "nickname": nickname, "content": content} + ) @staticmethod def poke(type_: str, id_: str) -> "MessageSegment": return MessageSegment("poke", {"type": type_, "id": id_}) @staticmethod - def record(file: Union[str, bytes, BytesIO, Path], - magic: Optional[bool] = None, - cache: Optional[bool] = None, - proxy: Optional[bool] = None, - timeout: Optional[int] = None) -> "MessageSegment": + def record( + file: Union[str, bytes, BytesIO, Path], + magic: Optional[bool] = None, + cache: Optional[bool] = None, + proxy: Optional[bool] = None, + timeout: Optional[int] = None, + ) -> "MessageSegment": if isinstance(file, BytesIO): file = file.getvalue() if isinstance(file, bytes): @@ -170,13 +185,15 @@ class MessageSegment(BaseMessageSegment["Message"]): elif isinstance(file, Path): file = f"file:///{file.resolve()}" return MessageSegment( - "record", { + "record", + { "file": file, "magic": _b2s(magic), "cache": _b2s(cache), "proxy": _b2s(proxy), - "timeout": timeout - }) + "timeout": timeout, + }, + ) @staticmethod def reply(id_: int) -> "MessageSegment": @@ -191,26 +208,27 @@ class MessageSegment(BaseMessageSegment["Message"]): return MessageSegment("shake", {}) @staticmethod - def share(url: str = "", - title: str = "", - content: Optional[str] = None, - image: Optional[str] = None) -> "MessageSegment": - return MessageSegment("share", { - "url": url, - "title": title, - "content": content, - "image": image - }) + def share( + url: str = "", + title: str = "", + content: Optional[str] = None, + image: Optional[str] = None, + ) -> "MessageSegment": + return MessageSegment( + "share", {"url": url, "title": title, "content": content, "image": image} + ) @staticmethod def text(text: str) -> "MessageSegment": return MessageSegment("text", {"text": text}) @staticmethod - def video(file: Union[str, bytes, BytesIO, Path], - cache: Optional[bool] = None, - proxy: Optional[bool] = None, - timeout: Optional[int] = None) -> "MessageSegment": + def video( + file: Union[str, bytes, BytesIO, Path], + cache: Optional[bool] = None, + proxy: Optional[bool] = None, + timeout: Optional[int] = None, + ) -> "MessageSegment": if isinstance(file, BytesIO): file = file.getvalue() if isinstance(file, bytes): @@ -218,12 +236,14 @@ class MessageSegment(BaseMessageSegment["Message"]): elif isinstance(file, Path): file = f"file:///{file.resolve()}" return MessageSegment( - "video", { + "video", + { "file": file, "cache": _b2s(cache), "proxy": _b2s(proxy), - "timeout": timeout - }) + "timeout": timeout, + }, + ) @staticmethod def xml(data: str) -> "MessageSegment": @@ -241,22 +261,22 @@ class Message(BaseMessage[MessageSegment]): return MessageSegment @overrides(BaseMessage) - def __add__(self, other: Union[str, Mapping, - Iterable[Mapping]]) -> "Message": + def __add__(self, other: Union[str, Mapping, Iterable[Mapping]]) -> "Message": return super(Message, self).__add__( - MessageSegment.text(other) if isinstance(other, str) else other) + MessageSegment.text(other) if isinstance(other, str) else other + ) @overrides(BaseMessage) - def __radd__(self, other: Union[str, Mapping, - Iterable[Mapping]]) -> "Message": + def __radd__(self, other: Union[str, Mapping, Iterable[Mapping]]) -> "Message": return super(Message, self).__radd__( - MessageSegment.text(other) if isinstance(other, str) else other) + MessageSegment.text(other) if isinstance(other, str) else other + ) @staticmethod @overrides(BaseMessage) def _construct( - msg: Union[str, Mapping, - Iterable[Mapping]]) -> Iterable[MessageSegment]: + msg: Union[str, Mapping, Iterable[Mapping]] + ) -> Iterable[MessageSegment]: if isinstance(msg, Mapping): msg = cast(Mapping[str, Any], msg) yield MessageSegment(msg["type"], msg.get("data") or {}) @@ -270,14 +290,15 @@ class Message(BaseMessage[MessageSegment]): def _iter_message(msg: str) -> Iterable[Tuple[str, str]]: text_begin = 0 for cqcode in re.finditer( - r"\[CQ:(?P[a-zA-Z0-9-_.]+)" - r"(?P" - r"(?:,[a-zA-Z0-9-_.]+=[^,\]]+)*" - r"),?\]", msg): - yield "text", msg[text_begin:cqcode.pos + cqcode.start()] + r"\[CQ:(?P[a-zA-Z0-9-_.]+)" + r"(?P" + r"(?:,[a-zA-Z0-9-_.]+=[^,\]]+)*" + r"),?\]", + msg, + ): + yield "text", msg[text_begin : cqcode.pos + cqcode.start()] text_begin = cqcode.pos + cqcode.end() - yield cqcode.group("type"), cqcode.group("params").lstrip( - ",") + yield cqcode.group("type"), cqcode.group("params").lstrip(",") yield "text", msg[text_begin:] for type_, data in _iter_message(msg): @@ -287,10 +308,11 @@ class Message(BaseMessage[MessageSegment]): yield MessageSegment(type_, {"text": unescape(data)}) else: data = { - k: unescape(v) for k, v in map( + k: unescape(v) + for k, v in map( lambda x: x.split("=", maxsplit=1), - filter(lambda x: x, ( - x.lstrip() for x in data.split(",")))) + filter(lambda x: x, (x.lstrip() for x in data.split(","))), + ) } yield MessageSegment(type_, data) diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/permission.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/permission.py index 09ea7b7a..2a85d6f1 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/permission.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/permission.py @@ -1,5 +1,6 @@ from nonebot.adapters import Event from nonebot.permission import Permission + from .event import GroupMessageEvent, PrivateMessageEvent @@ -42,8 +43,7 @@ async def _group(event: Event) -> bool: async def _group_member(event: Event) -> bool: - return isinstance(event, - GroupMessageEvent) and event.sender.role == "member" + return isinstance(event, GroupMessageEvent) and event.sender.role == "member" async def _group_admin(event: Event) -> bool: @@ -76,6 +76,12 @@ GROUP_OWNER = Permission(_group_owner) """ __all__ = [ - "PRIVATE", "PRIVATE_FRIEND", "PRIVATE_GROUP", "PRIVATE_OTHER", "GROUP", - "GROUP_MEMBER", "GROUP_ADMIN", "GROUP_OWNER" + "PRIVATE", + "PRIVATE_FRIEND", + "PRIVATE_GROUP", + "PRIVATE_OTHER", + "GROUP", + "GROUP_MEMBER", + "GROUP_ADMIN", + "GROUP_OWNER", ] diff --git a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/utils.py b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/utils.py index 747b964f..a3d2d0e4 100644 --- a/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/utils.py +++ b/packages/nonebot-adapter-cqhttp/nonebot/adapters/cqhttp/utils.py @@ -16,9 +16,7 @@ def escape(s: str, *, escape_comma: bool = True) -> str: * ``s: str``: 需要转义的字符串 * ``escape_comma: bool``: 是否转义逗号(``,``)。 """ - s = s.replace("&", "&") \ - .replace("[", "[") \ - .replace("]", "]") + s = s.replace("&", "&").replace("[", "[").replace("]", "]") if escape_comma: s = s.replace(",", ",") return s @@ -34,10 +32,12 @@ def unescape(s: str) -> str: * ``s: str``: 需要转义的字符串 """ - return s.replace(",", ",") \ - .replace("[", "[") \ - .replace("]", "]") \ + return ( + s.replace(",", ",") + .replace("[", "[") + .replace("]", "]") .replace("&", "&") + ) def _b2s(b: Optional[bool]) -> Optional[str]: diff --git a/packages/nonebot-adapter-cqhttp/pyproject.toml b/packages/nonebot-adapter-cqhttp/pyproject.toml index a500f04a..35386f9f 100644 --- a/packages/nonebot-adapter-cqhttp/pyproject.toml +++ b/packages/nonebot-adapter-cqhttp/pyproject.toml @@ -34,6 +34,21 @@ nonebot2 = { path = "../../", develop = true } # url = "https://mirrors.aliyun.com/pypi/simple/" # default = true +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39"] +include = '\.pyi?$' +extend-exclude = ''' +''' + +[tool.isort] +profile = "black" +line_length = 80 +length_sort = true +skip_gitignore = true +force_sort_within_sections = true +extra_standard_library = ["typing_extensions"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/bot.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/bot.py index dd8663f5..df5af674 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/bot.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/bot.py @@ -16,10 +16,18 @@ from nonebot.drivers import Driver, HTTPRequest, HTTPResponse, HTTPConnection from .config import Config as DingConfig from .utils import log, calc_hmac_base64 from .message import Message, MessageSegment -from .exception import (ActionFailed, NetworkError, SessionExpired, - ApiNotAvailable) -from .event import (MessageEvent, ConversationType, GroupMessageEvent, - PrivateMessageEvent) +from .exception import ( + ActionFailed, + NetworkError, + SessionExpired, + ApiNotAvailable, +) +from .event import ( + MessageEvent, + ConversationType, + GroupMessageEvent, + PrivateMessageEvent, +) if TYPE_CHECKING: from nonebot.config import Config @@ -31,6 +39,7 @@ class Bot(BaseBot): """ 钉钉 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 """ + ding_config: DingConfig @property @@ -48,8 +57,8 @@ class Bot(BaseBot): @classmethod @overrides(BaseBot) async def check_permission( - cls, driver: Driver, - request: HTTPConnection) -> Tuple[Optional[str], HTTPResponse]: + cls, driver: Driver, request: HTTPConnection + ) -> Tuple[Optional[str], HTTPResponse]: """ :说明: @@ -61,7 +70,8 @@ class Bot(BaseBot): # 检查连接方式 if not isinstance(request, HTTPRequest): return None, HTTPResponse( - 405, b"Unsupported connection type, available type: `http`") + 405, b"Unsupported connection type, available type: `http`" + ) # 检查 timestamp if not timestamp: @@ -74,13 +84,15 @@ class Bot(BaseBot): log("WARNING", "Missing Signature Header") return None, HTTPResponse(400, b"Missing `sign` Header") sign_base64 = calc_hmac_base64(str(timestamp), secret) - if sign != sign_base64.decode('utf-8'): + if sign != sign_base64.decode("utf-8"): log("WARNING", "Signature Header is invalid") return None, HTTPResponse(403, b"Signature is invalid") else: log("WARNING", "Ding signature check ignored!") - return (json.loads(request.body.decode())["chatbotUserId"], - HTTPResponse(204, b'')) + return ( + json.loads(request.body.decode())["chatbotUserId"], + HTTPResponse(204, b""), + ) @overrides(BaseBot) async def handle_message(self, message: bytes): @@ -111,10 +123,9 @@ class Bot(BaseBot): return @overrides(BaseBot) - async def _call_api(self, - api: str, - event: Optional[MessageEvent] = None, - **data) -> Any: + async def _call_api( + self, api: str, event: Optional[MessageEvent] = None, **data + ) -> Any: if not isinstance(self.request, HTTPRequest): log("ERROR", "Only support http connection.") return @@ -138,7 +149,8 @@ class Bot(BaseBot): if event: # 确保 sessionWebhook 没有过期 if int(datetime.now().timestamp()) > int( - event.sessionWebhookExpiredTime / 1000): + event.sessionWebhookExpiredTime / 1000 + ): raise SessionExpired webhook = event.sessionWebhook @@ -150,32 +162,37 @@ class Bot(BaseBot): if not message: raise ValueError("Message not found") try: - async with httpx.AsyncClient(headers=headers, - follow_redirects=True) as client: - response = await client.post(webhook, - params=params, - json=message._produce(), - timeout=self.config.api_timeout) + async with httpx.AsyncClient( + headers=headers, follow_redirects=True + ) as client: + response = await client.post( + webhook, + params=params, + json=message._produce(), + timeout=self.config.api_timeout, + ) if 200 <= response.status_code < 300: result = response.json() if isinstance(result, dict): if result.get("errcode") != 0: - raise ActionFailed(errcode=result.get("errcode"), - errmsg=result.get("errmsg")) + raise ActionFailed( + errcode=result.get("errcode"), errmsg=result.get("errmsg") + ) return result - raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code}") + raise NetworkError( + f"HTTP request received unexpected " + f"status code: {response.status_code}" + ) except httpx.InvalidURL: raise NetworkError("API root url invalid") except httpx.HTTPError: raise NetworkError("HTTP request failed") @overrides(BaseBot) - async def call_api(self, - api: str, - event: Optional[MessageEvent] = None, - **data) -> Any: + async def call_api( + self, api: str, event: Optional[MessageEvent] = None, **data + ) -> Any: """ :说明: @@ -199,13 +216,15 @@ class Bot(BaseBot): return await super().call_api(api, event=event, **data) @overrides(BaseBot) - async def send(self, - event: MessageEvent, - message: Union[str, "Message", "MessageSegment"], - at_sender: bool = False, - webhook: Optional[str] = None, - secret: Optional[str] = None, - **kwargs) -> Any: + async def send( + self, + event: MessageEvent, + message: Union[str, "Message", "MessageSegment"], + at_sender: bool = False, + webhook: Optional[str] = None, + secret: Optional[str] = None, + **kwargs, + ) -> Any: """ :说明: @@ -241,9 +260,11 @@ class Bot(BaseBot): params.update(kwargs) if at_sender and event.conversationType != ConversationType.private: - params[ - "message"] = f"@{event.senderId} " + msg + MessageSegment.atDingtalkIds( - event.senderId) + params["message"] = ( + f"@{event.senderId} " + + msg + + MessageSegment.atDingtalkIds(event.senderId) + ) else: params["message"] = msg diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/config.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/config.py index 405fb2c5..9c88b1b7 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/config.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/config.py @@ -12,6 +12,7 @@ class Config(BaseModel): - ``access_token`` / ``ding_access_token``: 钉钉令牌 - ``secret`` / ``ding_secret``: 钉钉 HTTP 上报数据签名口令 """ + secret: Optional[str] = Field(default=None, alias="ding_secret") access_token: Optional[str] = Field(default=None, alias="ding_access_token") diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/event.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/event.py index e01588a6..ad9eb230 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/event.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/event.py @@ -69,6 +69,7 @@ class ConversationType(str, Enum): class MessageEvent(Event): """消息事件""" + msgtype: str text: TextMessage msgId: str @@ -88,11 +89,10 @@ class MessageEvent(Event): def gen_message(cls, values: dict): assert "msgtype" in values, "msgtype must be specified" # 其实目前钉钉机器人只能接收到 text 类型的消息 - assert values[ - "msgtype"] in values, f"{values['msgtype']} must be specified" - content = values[values['msgtype']]['content'] + assert values["msgtype"] in values, f"{values['msgtype']} must be specified" + content = values[values["msgtype"]]["content"] # 如果是被 @,第一个字符将会为空格,移除特殊情况 - if content[0] == ' ': + if content[0] == " ": content = content[1:] values["message"] = content return values @@ -128,6 +128,7 @@ class MessageEvent(Event): class PrivateMessageEvent(MessageEvent): """私聊消息事件""" + chatbotCorpId: str senderStaffId: Optional[str] conversationType: ConversationType = ConversationType.private @@ -135,6 +136,7 @@ class PrivateMessageEvent(MessageEvent): class GroupMessageEvent(MessageEvent): """群消息事件""" + atUsers: List[AtUsersItem] conversationType: ConversationType = ConversationType.group conversationTitle: str diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/exception.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/exception.py index df416932..73b08b43 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/exception.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/exception.py @@ -1,9 +1,9 @@ from typing import Optional -from nonebot.exception import (AdapterException, ActionFailed as - BaseActionFailed, ApiNotAvailable as - BaseApiNotAvailable, NetworkError as - BaseNetworkError) +from nonebot.exception import AdapterException +from nonebot.exception import ActionFailed as BaseActionFailed +from nonebot.exception import NetworkError as BaseNetworkError +from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable class DingAdapterException(AdapterException): @@ -29,15 +29,13 @@ class ActionFailed(BaseActionFailed, DingAdapterException): * ``errmsg: Optional[str]``: 错误信息 """ - def __init__(self, - errcode: Optional[int] = None, - errmsg: Optional[str] = None): + def __init__(self, errcode: Optional[int] = None, errmsg: Optional[str] = None): super().__init__() self.errcode = errcode self.errmsg = errmsg def __repr__(self): - return f"" + return f'' def __str__(self): return self.__repr__() diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/message.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/message.py index 98d13f7d..ef092739 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/message.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/message.py @@ -77,10 +77,9 @@ class MessageSegment(BaseMessageSegment["Message"]): def code(code_language: str, code: str) -> "Message": """发送 code 消息段""" message = MessageSegment.text(code) - message += MessageSegment.extension({ - "text_type": "code_snippet", - "code_language": code_language - }) + message += MessageSegment.extension( + {"text_type": "code_snippet", "code_language": code_language} + ) return message @staticmethod @@ -95,16 +94,19 @@ class MessageSegment(BaseMessageSegment["Message"]): ) @staticmethod - def actionCardSingleBtn(title: str, text: str, singleTitle: str, - singleURL) -> "MessageSegment": + def actionCardSingleBtn( + title: str, text: str, singleTitle: str, singleURL + ) -> "MessageSegment": """发送 ``actionCardSingleBtn`` 类型消息""" return MessageSegment( - "actionCard", { + "actionCard", + { "title": title, "text": text, "singleTitle": singleTitle, - "singleURL": singleURL - }) + "singleURL": singleURL, + }, + ) @staticmethod def actionCardMultiBtns( @@ -112,7 +114,7 @@ class MessageSegment(BaseMessageSegment["Message"]): text: str, btns: list, hideAvatar: bool = False, - btnOrientation: str = '1', + btnOrientation: str = "1", ) -> "MessageSegment": """ 发送 ``actionCardMultiBtn`` 类型消息 @@ -123,13 +125,15 @@ class MessageSegment(BaseMessageSegment["Message"]): * ``btns``: ``[{ "title": title, "actionURL": actionURL }, ...]`` """ return MessageSegment( - "actionCard", { + "actionCard", + { "title": title, "text": text, "hideAvatar": "1" if hideAvatar else "0", "btnOrientation": btnOrientation, - "btns": btns - }) + "btns": btns, + }, + ) @staticmethod def feedCard(links: list) -> "MessageSegment": @@ -144,7 +148,7 @@ class MessageSegment(BaseMessageSegment["Message"]): @staticmethod def raw(data) -> "MessageSegment": - return MessageSegment('raw', data) + return MessageSegment("raw", data) def to_dict(self) -> Dict[str, Any]: # 让用户可以直接发送原始的消息格式 @@ -171,8 +175,8 @@ class Message(BaseMessage[MessageSegment]): @staticmethod @overrides(BaseMessage) def _construct( - msg: Union[str, Mapping, - Iterable[Mapping]]) -> Iterable[MessageSegment]: + msg: Union[str, Mapping, Iterable[Mapping]] + ) -> Iterable[MessageSegment]: if isinstance(msg, Mapping): msg = cast(Mapping[str, Any], msg) yield MessageSegment(msg["type"], msg.get("data") or {}) @@ -187,10 +191,11 @@ class Message(BaseMessage[MessageSegment]): segment: MessageSegment for segment in self: # text 可以和 text 合并 - if segment.type == "text" and data.get("msgtype") == 'text': + if segment.type == "text" and data.get("msgtype") == "text": data.setdefault("text", {}) - data["text"]["content"] = data["text"].setdefault( - "content", "") + segment.data["content"] + data["text"]["content"] = ( + data["text"].setdefault("content", "") + segment.data["content"] + ) else: data.update(segment.to_dict()) return data diff --git a/packages/nonebot-adapter-ding/nonebot/adapters/ding/utils.py b/packages/nonebot-adapter-ding/nonebot/adapters/ding/utils.py index 5529d42b..cf77a552 100644 --- a/packages/nonebot-adapter-ding/nonebot/adapters/ding/utils.py +++ b/packages/nonebot-adapter-ding/nonebot/adapters/ding/utils.py @@ -8,10 +8,10 @@ log = logger_wrapper("DING") def calc_hmac_base64(timestamp: str, secret: str): - secret_enc = secret.encode('utf-8') - string_to_sign = '{}\n{}'.format(timestamp, secret) - string_to_sign_enc = string_to_sign.encode('utf-8') - hmac_code = hmac.new(secret_enc, - string_to_sign_enc, - digestmod=hashlib.sha256).digest() + secret_enc = secret.encode("utf-8") + string_to_sign = "{}\n{}".format(timestamp, secret) + string_to_sign_enc = string_to_sign.encode("utf-8") + hmac_code = hmac.new( + secret_enc, string_to_sign_enc, digestmod=hashlib.sha256 + ).digest() return base64.b64encode(hmac_code) diff --git a/packages/nonebot-adapter-ding/pyproject.toml b/packages/nonebot-adapter-ding/pyproject.toml index 755499ec..2ee57d3d 100644 --- a/packages/nonebot-adapter-ding/pyproject.toml +++ b/packages/nonebot-adapter-ding/pyproject.toml @@ -34,6 +34,21 @@ nonebot2 = { path = "../../", develop = true } # url = "https://mirrors.aliyun.com/pypi/simple/" # default = true +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39"] +include = '\.pyi?$' +extend-exclude = ''' +''' + +[tool.isort] +profile = "black" +line_length = 80 +length_sort = true +skip_gitignore = true +force_sort_within_sections = true +extra_standard_library = ["typing_extensions"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py index 49f6e956..d9e2cdfb 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py @@ -7,7 +7,7 @@ aiocache_logger.setLevel(logging.DEBUG) aiocache_logger.handlers.clear() aiocache_logger.addHandler(LoguruHandler()) -from .bot import Bot as Bot from .event import * +from .bot import Bot as Bot from .message import Message as Message from .message import MessageSegment as MessageSegment diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index c3ab6cc8..6fa3756c 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -1,24 +1,39 @@ import re import json -from typing import (TYPE_CHECKING, Any, Dict, Tuple, Union, Iterable, Optional, - AsyncIterable, cast) +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Tuple, + Union, + Iterable, + Optional, + AsyncIterable, + cast, +) import httpx from aiocache import Cache, cached from aiocache.serializers import PickleSerializer from nonebot.log import logger -from .utils import AESCipher, log from nonebot.typing import overrides from nonebot.utils import escape_tag from nonebot.message import handle_event -from .config import Config as FeishuConfig from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse + +from .utils import AESCipher, log +from .config import Config as FeishuConfig from .message import Message, MessageSegment, MessageSerializer from .exception import ActionFailed, NetworkError, ApiNotAvailable -from .event import (Event, MessageEvent, GroupMessageEvent, PrivateMessageEvent, - get_event_model) +from .event import ( + Event, + MessageEvent, + GroupMessageEvent, + PrivateMessageEvent, + get_event_model, +) if TYPE_CHECKING: from nonebot.config import Config @@ -47,8 +62,10 @@ def _check_at_me(bot: "Bot", event: "Event"): event.to_me = True for index, segment in enumerate(message): - if segment.type == "at" and segment.data.get( - "user_name") in bot.config.nickname: + if ( + segment.type == "at" + and segment.data.get("user_name") in bot.config.nickname + ): event.to_me = True del event.event.message.content[index] return @@ -57,7 +74,8 @@ def _check_at_me(bot: "Bot", event: "Event"): if mention["name"] in bot.config.nickname: event.to_me = True segment.data["text"] = segment.data["text"].replace( - f"@{mention['name']}", "") + f"@{mention['name']}", "" + ) segment.data["text"] = segment.data["text"].lstrip() break else: @@ -92,18 +110,18 @@ def _check_nickname(bot: "Bot", event: "Event"): if nicknames: # check if the user is calling me with my nickname nickname_regex = "|".join(nicknames) - m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, - re.IGNORECASE) + m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, re.IGNORECASE) if m: nickname = m.group(1) log("DEBUG", f"User is calling me {nickname}") event.to_me = True - first_msg_seg.data["text"] = first_text[m.end():] + first_msg_seg.data["text"] = first_text[m.end() :] def _handle_api_result( - result: Union[Optional[Dict[str, Any]], str, bytes, Iterable[bytes], - AsyncIterable[bytes]] + result: Union[ + Optional[Dict[str, Any]], str, bytes, Iterable[bytes], AsyncIterable[bytes] + ] ) -> Any: """ :说明: @@ -155,13 +173,13 @@ class Bot(BaseBot): @classmethod @overrides(BaseBot) async def check_permission( - cls, driver: Driver, request: HTTPRequest + cls, driver: Driver, request: HTTPRequest ) -> Tuple[Optional[str], Optional[HTTPResponse]]: if not isinstance(request, HTTPRequest): - log("WARNING", - "Unsupported connection type, available type: `http`") + log("WARNING", "Unsupported connection type, available type: `http`") return None, HTTPResponse( - 405, b"Unsupported connection type, available type: `http`") + 405, b"Unsupported connection type, available type: `http`" + ) encrypt_key = cls.feishu_config.encrypt_key if encrypt_key: @@ -174,16 +192,13 @@ class Bot(BaseBot): challenge = data.get("challenge") if challenge: return data.get("token"), HTTPResponse( - 200, - json.dumps({ - "challenge": challenge - }).encode()) + 200, json.dumps({"challenge": challenge}).encode() + ) schema = data.get("schema") if not schema: return None, HTTPResponse( - 400, - b"Missing `schema` in POST body, only accept event of version 2.0" + 400, b"Missing `schema` in POST body, only accept event of version 2.0" ) headers = data.get("header") @@ -196,15 +211,13 @@ class Bot(BaseBot): if not token: log("WARNING", "Missing `verification token` in POST body") - return None, HTTPResponse( - 400, b"Missing `verification token` in POST body") + return None, HTTPResponse(400, b"Missing `verification token` in POST body") else: if token != cls.feishu_config.verification_token: log("WARNING", "Verification token check failed") - return None, HTTPResponse(403, - b"Verification token check failed") + return None, HTTPResponse(403, b"Verification token check failed") - return app_id, HTTPResponse(200, b'') + return app_id, HTTPResponse(200, b"") async def handle_message(self, message: bytes): """ @@ -245,28 +258,32 @@ class Bot(BaseBot): def _construct_url(self, path: str) -> str: return self.api_root + path - @cached(ttl=60 * 60, - cache=Cache.MEMORY, - key="_feishu_tenant_access_token", - serializer=PickleSerializer()) + @cached( + ttl=60 * 60, + cache=Cache.MEMORY, + key="_feishu_tenant_access_token", + serializer=PickleSerializer(), + ) async def _fetch_tenant_access_token(self) -> str: try: async with httpx.AsyncClient(follow_redirects=True) as client: response = await client.post( - self._construct_url( - "auth/v3/tenant_access_token/internal/"), + self._construct_url("auth/v3/tenant_access_token/internal/"), json={ "app_id": self.feishu_config.app_id, - "app_secret": self.feishu_config.app_secret + "app_secret": self.feishu_config.app_secret, }, - timeout=self.config.api_timeout) + timeout=self.config.api_timeout, + ) if 200 <= response.status_code < 300: result = response.json() return result["tenant_access_token"] else: - raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code}") + raise NetworkError( + f"HTTP request received unexpected " + f"status code: {response.status_code}" + ) except httpx.InvalidURL: raise NetworkError("API root url invalid") except httpx.HTTPError: @@ -280,30 +297,37 @@ class Bot(BaseBot): raise ApiNotAvailable headers = {} - self.feishu_config.tenant_access_token = await self._fetch_tenant_access_token( + self.feishu_config.tenant_access_token = ( + await self._fetch_tenant_access_token() + ) + headers["Authorization"] = ( + "Bearer " + self.feishu_config.tenant_access_token ) - headers[ - "Authorization"] = "Bearer " + self.feishu_config.tenant_access_token try: - async with httpx.AsyncClient(timeout=self.config.api_timeout, - follow_redirects=True) as client: + async with httpx.AsyncClient( + timeout=self.config.api_timeout, follow_redirects=True + ) as client: response = await client.send( - httpx.Request(data["method"], - self.api_root + api, - json=data.get("body", {}), - params=data.get("query", {}), - headers=headers)) + httpx.Request( + data["method"], + self.api_root + api, + json=data.get("body", {}), + params=data.get("query", {}), + headers=headers, + ) + ) if 200 <= response.status_code < 300: - if response.headers["content-type"].startswith( - "application/json"): + if response.headers["content-type"].startswith("application/json"): result = response.json() else: result = response.content return _handle_api_result(result) - raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code} " - f"response body: {response.text}") + raise NetworkError( + f"HTTP request received unexpected " + f"status code: {response.status_code} " + f"response body: {response.text}" + ) except httpx.InvalidURL: raise NetworkError("API root url invalid") except httpx.HTTPError: @@ -333,11 +357,13 @@ class Bot(BaseBot): return await super().call_api(api, **data) @overrides(BaseBot) - async def send(self, - event: Event, - message: Union[str, Message, MessageSegment], - at_sender: bool = False, - **kwargs) -> Any: + async def send( + self, + event: Event, + message: Union[str, Message, MessageSegment], + at_sender: bool = False, + **kwargs, + ) -> Any: msg = message if isinstance(message, Message) else Message(message) if isinstance(event, GroupMessageEvent): @@ -346,7 +372,8 @@ class Bot(BaseBot): receive_id, receive_id_type = event.get_user_id(), "open_id" else: raise ValueError( - "Cannot guess `receive_id` and `receive_id_type` to reply!") + "Cannot guess `receive_id` and `receive_id_type` to reply!" + ) at_sender = at_sender and bool(event.get_user_id()) @@ -357,14 +384,12 @@ class Bot(BaseBot): params = { "method": "POST", - "query": { - "receive_id_type": receive_id_type - }, + "query": {"receive_id_type": receive_id_type}, "body": { "receive_id": receive_id, "content": content, - "msg_type": msg_type - } + "msg_type": msg_type, + }, } return await self.call_api(f"im/v1/messages", **params) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py index 4d74a1de..4bc70c12 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py @@ -17,13 +17,16 @@ class Config(BaseModel): - ``is_lark`` / ``feishu_is_lark``: 是否使用Lark(飞书海外版),默认为 false """ + app_id: Optional[str] = Field(default=None, alias="feishu_app_id") app_secret: Optional[str] = Field(default=None, alias="feishu_app_secret") encrypt_key: Optional[str] = Field(default=None, alias="feishu_encrypt_key") - verification_token: Optional[str] = Field(default=None, - alias="feishu_verification_token") + verification_token: Optional[str] = Field( + default=None, alias="feishu_verification_token" + ) tenant_access_token: Optional[str] = Field( - default=None, alias="feishu_tenant_access_token") + default=None, alias="feishu_tenant_access_token" + ) is_lark: Optional[str] = Field(default=False, alias="feishu_is_lark") class Config: diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 9c35ea9e..620bbe36 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,12 +1,12 @@ -import inspect import json -from typing import Any, Dict, List, Literal, Optional, Type +import inspect +from typing import Any, Dict, List, Type, Literal, Optional -from pydantic import BaseModel, Field, root_validator from pygtrie import StringTrie +from pydantic import Field, BaseModel, root_validator -from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides +from nonebot.adapters import Event as BaseEvent from .message import Message, MessageDeserializer diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py index 38336e53..99fb562c 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py @@ -1,13 +1,12 @@ from typing import Optional -from nonebot.exception import ActionFailed as BaseActionFailed from nonebot.exception import AdapterException -from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable +from nonebot.exception import ActionFailed as BaseActionFailed from nonebot.exception import NetworkError as BaseNetworkError +from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable class FeishuAdapterException(AdapterException): - def __init__(self): super().__init__("feishu") @@ -28,8 +27,11 @@ class ActionFailed(BaseActionFailed, FeishuAdapterException): self.info = kwargs def __repr__(self): - return f"" + return ( + f"" + ) def __str__(self): return self.__repr__() diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index d9a7e2a1..7e7eabd9 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,8 +1,18 @@ import json import itertools from dataclasses import dataclass -from typing import (Any, Dict, List, Type, Tuple, Union, Mapping, Iterable, - Optional, cast) +from typing import ( + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + Iterable, + Optional, + cast, +) from nonebot.typing import overrides from nonebot.adapters import Message as BaseMessage @@ -34,7 +44,7 @@ class MessageSegment(BaseMessageSegment["Message"]): "share_user": "[个人名片]", "system": "[系统消息]", "location": "[位置]", - "video_chat": "[视频通话]" + "video_chat": "[视频通话]", } def __str__(self) -> str: @@ -47,24 +57,26 @@ class MessageSegment(BaseMessageSegment["Message"]): @overrides(BaseMessageSegment) def __add__(self, other) -> "Message": - return Message(self) + (MessageSegment.text(other) if isinstance( - other, str) else other) + return Message(self) + ( + MessageSegment.text(other) if isinstance(other, str) else other + ) @overrides(BaseMessageSegment) def __radd__(self, other) -> "Message": - return (MessageSegment.text(other) - if isinstance(other, str) else Message(other)) + self + return ( + MessageSegment.text(other) if isinstance(other, str) else Message(other) + ) + self @overrides(BaseMessageSegment) def is_text(self) -> bool: return self.type == "text" - #接收消息 + # 接收消息 @staticmethod def at(user_id: str) -> "MessageSegment": return MessageSegment("at", {"user_id": user_id}) - #发送消息 + # 发送消息 @staticmethod def text(text: str) -> "MessageSegment": return MessageSegment("text", {"text": text}) @@ -79,10 +91,7 @@ class MessageSegment(BaseMessageSegment["Message"]): @staticmethod def interactive(title: str, elements: list) -> "MessageSegment": - return MessageSegment("interactive", { - "title": title, - "elements": elements - }) + return MessageSegment("interactive", {"title": title, "elements": elements}) @staticmethod def share_chat(chat_id: str) -> "MessageSegment": @@ -94,28 +103,25 @@ class MessageSegment(BaseMessageSegment["Message"]): @staticmethod def audio(file_key: str, duration: int) -> "MessageSegment": - return MessageSegment("audio", { - "file_key": file_key, - "duration": duration - }) + return MessageSegment("audio", {"file_key": file_key, "duration": duration}) @staticmethod - def media(file_key: str, image_key: str, file_name: str, - duration: int) -> "MessageSegment": + def media( + file_key: str, image_key: str, file_name: str, duration: int + ) -> "MessageSegment": return MessageSegment( - "media", { + "media", + { "file_key": file_key, "image_key": image_key, "file_name": file_name, - "duration": duration - }) + "duration": duration, + }, + ) @staticmethod def file(file_key: str, file_name: str) -> "MessageSegment": - return MessageSegment("file", { - "file_key": file_key, - "file_name": file_name - }) + return MessageSegment("file", {"file_key": file_key, "file_name": file_name}) @staticmethod def sticker(file_key) -> "MessageSegment": @@ -133,22 +139,22 @@ class Message(BaseMessage[MessageSegment]): return MessageSegment @overrides(BaseMessage) - def __add__(self, other: Union[str, Mapping, - Iterable[Mapping]]) -> "Message": + def __add__(self, other: Union[str, Mapping, Iterable[Mapping]]) -> "Message": return super(Message, self).__add__( - MessageSegment.text(other) if isinstance(other, str) else other) + MessageSegment.text(other) if isinstance(other, str) else other + ) @overrides(BaseMessage) - def __radd__(self, other: Union[str, Mapping, - Iterable[Mapping]]) -> "Message": + def __radd__(self, other: Union[str, Mapping, Iterable[Mapping]]) -> "Message": return super(Message, self).__radd__( - MessageSegment.text(other) if isinstance(other, str) else other) + MessageSegment.text(other) if isinstance(other, str) else other + ) @staticmethod @overrides(BaseMessage) def _construct( - msg: Union[str, Mapping, - Iterable[Mapping]]) -> Iterable[MessageSegment]: + msg: Union[str, Mapping, Iterable[Mapping]] + ) -> Iterable[MessageSegment]: if isinstance(msg, Mapping): msg = cast(Mapping[str, Any], msg) yield MessageSegment(msg["type"], msg.get("data") or {}) @@ -169,7 +175,8 @@ class Message(BaseMessage[MessageSegment]): for i, seg in enumerate(self): if seg.type == "text" and i != 0 and msg[-1].type == "text": msg[-1] = MessageSegment( - "text", {"text": msg[-1].data["text"] + seg.data["text"]}) + "text", {"text": msg[-1].data["text"] + seg.data["text"]} + ) else: msg.append(seg) return Message(msg) @@ -184,6 +191,7 @@ class MessageSerializer: """ 飞书 协议 Message 序列化器。 """ + message: Message def serialize(self) -> Tuple[str, str]: @@ -198,10 +206,12 @@ class MessageSerializer: else: if last_segment_type == "image": msg["content"].append([]) - msg["content"][-1].append({ - "tag": segment.type if segment.type != "image" else "img", - **segment.data - }) + msg["content"][-1].append( + { + "tag": segment.type if segment.type != "image" else "img", + **segment.data, + } + ) last_segment_type = segment.type return "post", json.dumps({"zh_cn": {**msg}}) @@ -214,6 +224,7 @@ class MessageDeserializer: """ 飞书 协议 Message 反序列化器。 """ + type: str data: Dict[str, Any] mentions: Optional[List[dict]] @@ -227,14 +238,13 @@ class MessageDeserializer: if self.type == "post": msg = Message() if self.data["title"] != "": - msg += MessageSegment("text", {'text': self.data["title"]}) + msg += MessageSegment("text", {"text": self.data["title"]}) for seg in itertools.chain(*self.data["content"]): tag = seg.pop("tag") if tag == "at": seg["user_name"] = dict_mention[seg["user_id"]]["name"] - seg["user_id"] = dict_mention[ - seg["user_id"]]["id"]["open_id"] + seg["user_id"] = dict_mention[seg["user_id"]]["id"]["open_id"] msg += MessageSegment(tag if tag != "img" else "image", seg) @@ -242,7 +252,8 @@ class MessageDeserializer: elif self.type == "text": for key, mention in dict_mention.items(): self.data["text"] = self.data["text"].replace( - key, f"@{mention['name']}") + key, f"@{mention['name']}" + ) self.data["mentions"] = dict_mention return Message(MessageSegment(self.type, self.data)) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py index ba418356..8848e563 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py @@ -9,27 +9,26 @@ log = logger_wrapper("FEISHU") class AESCipher(object): - def __init__(self, key): self.block_size = AES.block_size self.key = hashlib.sha256(AESCipher.str_to_bytes(key)).digest() @staticmethod def str_to_bytes(data): - u_type = type(b"".decode('utf8')) + u_type = type(b"".decode("utf8")) if isinstance(data, u_type): - return data.encode('utf8') + return data.encode("utf8") return data @staticmethod def _unpad(s): - return s[:-ord(s[len(s) - 1:])] + return s[: -ord(s[len(s) - 1 :])] def decrypt(self, enc): - iv = enc[:AES.block_size] + iv = enc[: AES.block_size] cipher = AES.new(self.key, AES.MODE_CBC, iv) - return self._unpad(cipher.decrypt(enc[AES.block_size:])) + return self._unpad(cipher.decrypt(enc[AES.block_size :])) def decrypt_string(self, enc): enc = base64.b64decode(enc) - return self.decrypt(enc).decode('utf8') + return self.decrypt(enc).decode("utf8") diff --git a/packages/nonebot-adapter-feishu/pyproject.toml b/packages/nonebot-adapter-feishu/pyproject.toml index 030852e9..c3dfd427 100644 --- a/packages/nonebot-adapter-feishu/pyproject.toml +++ b/packages/nonebot-adapter-feishu/pyproject.toml @@ -36,6 +36,21 @@ nonebot2 = { path = "../../", develop = true } # url = "https://mirrors.aliyun.com/pypi/simple/" # default = true +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39"] +include = '\.pyi?$' +extend-exclude = ''' +''' + +[tool.isort] +profile = "black" +line_length = 80 +length_sort = true +skip_gitignore = true +force_sort_within_sections = true +extra_standard_library = ["typing_extensions"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py index 9e197079..8af7ad8d 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py @@ -12,8 +12,15 @@ from nonebot.config import Config from nonebot.typing import overrides from nonebot.adapters import Bot as BaseBot from nonebot.exception import ApiNotAvailable -from nonebot.drivers import (Driver, WebSocket, HTTPResponse, ForwardDriver, - ReverseDriver, HTTPConnection, WebSocketSetup) +from nonebot.drivers import ( + Driver, + WebSocket, + HTTPResponse, + ForwardDriver, + ReverseDriver, + HTTPConnection, + WebSocketSetup, +) from .config import Config as MiraiConfig from .message import MessageChain, MessageSegment @@ -23,16 +30,14 @@ from .utils import Log, process_event, argument_validation, catch_network_error class SessionManager: """Bot会话管理器, 提供API主动调用接口""" + sessions: Dict[int, Tuple[str, httpx.AsyncClient]] = {} def __init__(self, session_key: str, client: httpx.AsyncClient): self.session_key, self.client = session_key, client @catch_network_error - async def post(self, - path: str, - *, - params: Optional[Dict[str, Any]] = None) -> Any: + async def post(self, path: str, *, params: Optional[Dict[str, Any]] = None) -> Any: """ :说明: @@ -51,7 +56,7 @@ class SessionManager: path, json={ **(params or {}), - 'sessionKey': self.session_key, + "sessionKey": self.session_key, }, timeout=3, ) @@ -59,10 +64,9 @@ class SessionManager: return response.json() @catch_network_error - async def request(self, - path: str, - *, - params: Optional[Dict[str, Any]] = None) -> Any: + async def request( + self, path: str, *, params: Optional[Dict[str, Any]] = None + ) -> Any: """ :说明: @@ -77,7 +81,7 @@ class SessionManager: path, params={ **(params or {}), - 'sessionKey': self.session_key, + "sessionKey": self.session_key, }, timeout=3, ) @@ -98,7 +102,7 @@ class SessionManager: """ files = {k: v for k, v in params.items() if isinstance(v, BytesIO)} form = {k: v for k, v in params.items() if k not in files} - form['sessionKey'] = self.session_key + form["sessionKey"] = self.session_key response = await self.client.post( path, data=form, @@ -109,25 +113,25 @@ class SessionManager: return response.json() @classmethod - async def new(cls, self_id: int, *, host: IPv4Address, port: int, - auth_key: str) -> "SessionManager": + async def new( + cls, self_id: int, *, host: IPv4Address, port: int, auth_key: str + ) -> "SessionManager": session = cls.get(self_id) if session is not None: return session - client = httpx.AsyncClient(base_url=f'http://{host}:{port}', - follow_redirects=True) - response = await client.post('/auth', json={'authKey': auth_key}) + client = httpx.AsyncClient( + base_url=f"http://{host}:{port}", follow_redirects=True + ) + response = await client.post("/auth", json={"authKey": auth_key}) response.raise_for_status() auth = response.json() - assert auth['code'] == 0 - session_key = auth['session'] - response = await client.post('/verify', - json={ - 'sessionKey': session_key, - 'qq': self_id - }) - assert response.json()['code'] == 0 + assert auth["code"] == 0 + session_key = auth["session"] + response = await client.post( + "/verify", json={"sessionKey": session_key, "qq": self_id} + ) + assert response.json()["code"] == 0 cls.sessions[self_id] = session_key, client return cls(session_key, client) @@ -152,7 +156,7 @@ class Bot(BaseBot): """ - _type = 'mirai' + _type = "mirai" @property @overrides(BaseBot) @@ -166,37 +170,42 @@ class Bot(BaseBot): if api is None: if isinstance(self.request, WebSocket): asyncio.create_task(self.request.close(1000)) - assert api is not None, 'SessionManager has not been initialized' + assert api is not None, "SessionManager has not been initialized" return api @classmethod @overrides(BaseBot) async def check_permission( - cls, driver: Driver, - request: HTTPConnection) -> Tuple[Optional[str], HTTPResponse]: + cls, driver: Driver, request: HTTPConnection + ) -> Tuple[Optional[str], HTTPResponse]: if isinstance(request, WebSocket): - return None, HTTPResponse( - 501, b'Websocket connection is not implemented') - self_id: Optional[str] = request.headers.get('bot') + return None, HTTPResponse(501, b"Websocket connection is not implemented") + self_id: Optional[str] = request.headers.get("bot") if self_id is None: - return None, HTTPResponse(400, b'Header `Bot` is required.') + return None, HTTPResponse(400, b"Header `Bot` is required.") self_id = str(self_id).strip() await SessionManager.new( int(self_id), host=cls.mirai_config.host, # type: ignore - port=cls.mirai_config.port, #type: ignore - auth_key=cls.mirai_config.auth_key) # type: ignore - return self_id, HTTPResponse(204, b'') + port=cls.mirai_config.port, # type: ignore + auth_key=cls.mirai_config.auth_key, # type: ignore + ) + return self_id, HTTPResponse(204, b"") @classmethod @overrides(BaseBot) - def register(cls, - driver: Driver, - config: "Config", - qq: Optional[Union[int, List[int]]] = None): + def register( + cls, + driver: Driver, + config: "Config", + qq: Optional[Union[int, List[int]]] = None, + ): cls.mirai_config = MiraiConfig(**config.dict()) - if (cls.mirai_config.auth_key and cls.mirai_config.host and - cls.mirai_config.port) is None: + if ( + cls.mirai_config.auth_key + and cls.mirai_config.host + and cls.mirai_config.port + ) is None: raise ApiNotAvailable(cls._type) super().register(driver, config) @@ -209,17 +218,25 @@ class Bot(BaseBot): self_ids = [qq] if isinstance(qq, int) else qq async def url_factory(qq: int): - assert cls.mirai_config.host and cls.mirai_config.port and cls.mirai_config.auth_key + assert ( + cls.mirai_config.host + and cls.mirai_config.port + and cls.mirai_config.auth_key + ) session = await SessionManager.new( qq, host=cls.mirai_config.host, port=cls.mirai_config.port, - auth_key=cls.mirai_config.auth_key) + auth_key=cls.mirai_config.auth_key, + ) return WebSocketSetup( adapter=cls._type, self_id=str(qq), - url=(f'ws://{cls.mirai_config.host}:{cls.mirai_config.port}' - f'/all?sessionKey={session.session_key}')) + url=( + f"ws://{cls.mirai_config.host}:{cls.mirai_config.port}" + f"/all?sessionKey={session.session_key}" + ), + ) for self_id in self_ids: driver.setup_websocket(partial(url_factory, qq=self_id)) @@ -234,13 +251,15 @@ class Bot(BaseBot): try: await process_event( bot=self, - event=Event.new({ - **json.loads(message), - 'self_id': self.self_id, - }), + event=Event.new( + { + **json.loads(message), + "self_id": self.self_id, + } + ), ) except Exception as e: - Log.error(f'Failed to handle message: {message}', e) + Log.error(f"Failed to handle message: {message}", e) @overrides(BaseBot) async def _call_api(self, api: str, **data) -> NoReturn: @@ -266,10 +285,12 @@ class Bot(BaseBot): @overrides(BaseBot) @argument_validation - async def send(self, - event: Event, - message: Union[MessageChain, MessageSegment, str], - at_sender: bool = False): + async def send( + self, + event: Event, + message: Union[MessageChain, MessageSegment, str], + at_sender: bool = False, + ): """ :说明: @@ -284,23 +305,24 @@ class Bot(BaseBot): if not isinstance(message, MessageChain): message = MessageChain(message) if isinstance(event, FriendMessage): - return await self.send_friend_message(target=event.sender.id, - message_chain=message) + return await self.send_friend_message( + target=event.sender.id, message_chain=message + ) elif isinstance(event, GroupMessage): if at_sender: message = MessageSegment.at(event.sender.id) + message - return await self.send_group_message(group=event.sender.group.id, - message_chain=message) + return await self.send_group_message( + group=event.sender.group.id, message_chain=message + ) elif isinstance(event, TempMessage): - return await self.send_temp_message(qq=event.sender.id, - group=event.sender.group.id, - message_chain=message) + return await self.send_temp_message( + qq=event.sender.id, group=event.sender.group.id, message_chain=message + ) else: - raise ValueError(f'Unsupported event type {event!r}.') + raise ValueError(f"Unsupported event type {event!r}.") @argument_validation - async def send_friend_message(self, target: int, - message_chain: MessageChain): + async def send_friend_message(self, target: int, message_chain: MessageChain): """ :说明: @@ -311,15 +333,13 @@ class Bot(BaseBot): * ``target: int``: 发送消息目标好友的 QQ 号 * ``message_chain: MessageChain``: 消息链,是一个消息对象构成的数组 """ - return await self.api.post('sendFriendMessage', - params={ - 'target': target, - 'messageChain': message_chain.export() - }) + return await self.api.post( + "sendFriendMessage", + params={"target": target, "messageChain": message_chain.export()}, + ) @argument_validation - async def send_temp_message(self, qq: int, group: int, - message_chain: MessageChain): + async def send_temp_message(self, qq: int, group: int, message_chain: MessageChain): """ :说明: @@ -331,18 +351,15 @@ class Bot(BaseBot): * ``group: int``: 临时会话群号 * ``message_chain: MessageChain``: 消息链,是一个消息对象构成的数组 """ - return await self.api.post('sendTempMessage', - params={ - 'qq': qq, - 'group': group, - 'messageChain': message_chain.export() - }) + return await self.api.post( + "sendTempMessage", + params={"qq": qq, "group": group, "messageChain": message_chain.export()}, + ) @argument_validation - async def send_group_message(self, - group: int, - message_chain: MessageChain, - quote: Optional[int] = None): + async def send_group_message( + self, group: int, message_chain: MessageChain, quote: Optional[int] = None + ): """ :说明: @@ -354,12 +371,14 @@ class Bot(BaseBot): * ``message_chain: MessageChain``: 消息链,是一个消息对象构成的数组 * ``quote: Optional[int]``: 引用一条消息的 message_id 进行回复 """ - return await self.api.post('sendGroupMessage', - params={ - 'group': group, - 'messageChain': message_chain.export(), - 'quote': quote - }) + return await self.api.post( + "sendGroupMessage", + params={ + "group": group, + "messageChain": message_chain.export(), + "quote": quote, + }, + ) @argument_validation async def recall(self, target: int): @@ -372,11 +391,12 @@ class Bot(BaseBot): * ``target: int``: 需要撤回的消息的message_id """ - return await self.api.post('recall', params={'target': target}) + return await self.api.post("recall", params={"target": target}) @argument_validation - async def send_image_message(self, target: int, qq: int, group: int, - urls: List[str]) -> List[str]: + async def send_image_message( + self, target: int, qq: int, group: int, urls: List[str] + ) -> List[str]: """ :说明: @@ -396,13 +416,10 @@ class Bot(BaseBot): - ``List[str]``: 一个包含图片imageId的数组 """ - return await self.api.post('sendImageMessage', - params={ - 'target': target, - 'qq': qq, - 'group': group, - 'urls': urls - }) + return await self.api.post( + "sendImageMessage", + params={"target": target, "qq": qq, "group": group, "urls": urls}, + ) @argument_validation async def upload_image(self, type: str, img: BytesIO): @@ -416,11 +433,7 @@ class Bot(BaseBot): * ``type: str``: "friend" 或 "group" 或 "temp" * ``img: BytesIO``: 图片的BytesIO对象 """ - return await self.api.upload('uploadImage', - params={ - 'type': type, - 'img': img - }) + return await self.api.upload("uploadImage", params={"type": type, "img": img}) @argument_validation async def upload_voice(self, type: str, voice: BytesIO): @@ -434,11 +447,9 @@ class Bot(BaseBot): * ``type: str``: 当前仅支持 "group" * ``voice: BytesIO``: 语音的BytesIO对象 """ - return await self.api.upload('uploadVoice', - params={ - 'type': type, - 'voice': voice - }) + return await self.api.upload( + "uploadVoice", params={"type": type, "voice": voice} + ) @argument_validation async def fetch_message(self, count: int = 10): @@ -452,7 +463,7 @@ class Bot(BaseBot): * ``count: int``: 获取消息和事件的数量 """ - return await self.api.request('fetchMessage', params={'count': count}) + return await self.api.request("fetchMessage", params={"count": count}) @argument_validation async def fetch_latest_message(self, count: int = 10): @@ -466,8 +477,7 @@ class Bot(BaseBot): * ``count: int``: 获取消息和事件的数量 """ - return await self.api.request('fetchLatestMessage', - params={'count': count}) + return await self.api.request("fetchLatestMessage", params={"count": count}) @argument_validation async def peek_message(self, count: int = 10): @@ -481,7 +491,7 @@ class Bot(BaseBot): * ``count: int``: 获取消息和事件的数量 """ - return await self.api.request('peekMessage', params={'count': count}) + return await self.api.request("peekMessage", params={"count": count}) @argument_validation async def peek_latest_message(self, count: int = 10): @@ -495,8 +505,7 @@ class Bot(BaseBot): * ``count: int``: 获取消息和事件的数量 """ - return await self.api.request('peekLatestMessage', - params={'count': count}) + return await self.api.request("peekLatestMessage", params={"count": count}) @argument_validation async def messsage_from_id(self, id: int): @@ -510,7 +519,7 @@ class Bot(BaseBot): * ``id: int``: 获取消息的message_id """ - return await self.api.request('messageFromId', params={'id': id}) + return await self.api.request("messageFromId", params={"id": id}) @argument_validation async def count_message(self): @@ -519,7 +528,7 @@ class Bot(BaseBot): 使用此方法获取bot接收并缓存的消息总数,注意不包含被删除的 """ - return await self.api.request('countMessage') + return await self.api.request("countMessage") @argument_validation async def friend_list(self) -> List[Dict[str, Any]]: @@ -532,7 +541,7 @@ class Bot(BaseBot): - ``List[Dict[str, Any]]``: 返回的好友列表数据 """ - return await self.api.request('friendList') + return await self.api.request("friendList") @argument_validation async def group_list(self) -> List[Dict[str, Any]]: @@ -545,7 +554,7 @@ class Bot(BaseBot): - ``List[Dict[str, Any]]``: 返回的群列表数据 """ - return await self.api.request('groupList') + return await self.api.request("groupList") @argument_validation async def member_list(self, target: int) -> List[Dict[str, Any]]: @@ -562,7 +571,7 @@ class Bot(BaseBot): - ``List[Dict[str, Any]]``: 返回的群成员列表数据 """ - return await self.api.request('memberList', params={'target': target}) + return await self.api.request("memberList", params={"target": target}) @argument_validation async def mute(self, target: int, member_id: int, time: int): @@ -577,12 +586,9 @@ class Bot(BaseBot): * ``member_id: int``: 指定群员QQ号 * ``time: int``: 禁言时长,单位为秒,最多30天 """ - return await self.api.post('mute', - params={ - 'target': target, - 'memberId': member_id, - 'time': time - }) + return await self.api.post( + "mute", params={"target": target, "memberId": member_id, "time": time} + ) @argument_validation async def unmute(self, target: int, member_id: int): @@ -596,11 +602,9 @@ class Bot(BaseBot): * ``target: int``: 指定群的群号 * ``member_id: int``: 指定群员QQ号 """ - return await self.api.post('unmute', - params={ - 'target': target, - 'memberId': member_id - }) + return await self.api.post( + "unmute", params={"target": target, "memberId": member_id} + ) @argument_validation async def kick(self, target: int, member_id: int, msg: str): @@ -615,12 +619,9 @@ class Bot(BaseBot): * ``member_id: int``: 指定群员QQ号 * ``msg: str``: 信息 """ - return await self.api.post('kick', - params={ - 'target': target, - 'memberId': member_id, - 'msg': msg - }) + return await self.api.post( + "kick", params={"target": target, "memberId": member_id, "msg": msg} + ) @argument_validation async def quit(self, target: int): @@ -633,7 +634,7 @@ class Bot(BaseBot): * ``target: int``: 退出的群号 """ - return await self.api.post('quit', params={'target': target}) + return await self.api.post("quit", params={"target": target}) @argument_validation async def mute_all(self, target: int): @@ -646,7 +647,7 @@ class Bot(BaseBot): * ``target: int``: 指定群的群号 """ - return await self.api.post('muteAll', params={'target': target}) + return await self.api.post("muteAll", params={"target": target}) @argument_validation async def unmute_all(self, target: int): @@ -659,7 +660,7 @@ class Bot(BaseBot): * ``target: int``: 指定群的群号 """ - return await self.api.post('unmuteAll', params={'target': target}) + return await self.api.post("unmuteAll", params={"target": target}) @argument_validation async def group_config(self, target: int): @@ -685,7 +686,7 @@ class Bot(BaseBot): "anonymousChat": true } """ - return await self.api.request('groupConfig', params={'target': target}) + return await self.api.request("groupConfig", params={"target": target}) @argument_validation async def modify_group_config(self, target: int, config: Dict[str, Any]): @@ -699,11 +700,9 @@ class Bot(BaseBot): * ``target: int``: 指定群的群号 * ``config: Dict[str, Any]``: 群设置, 格式见 ``group_config`` 的返回值 """ - return await self.api.post('groupConfig', - params={ - 'target': target, - 'config': config - }) + return await self.api.post( + "groupConfig", params={"target": target, "config": config} + ) @argument_validation async def member_info(self, target: int, member_id: int): @@ -726,15 +725,14 @@ class Bot(BaseBot): "specialTitle": "群头衔" } """ - return await self.api.request('memberInfo', - params={ - 'target': target, - 'memberId': member_id - }) + return await self.api.request( + "memberInfo", params={"target": target, "memberId": member_id} + ) @argument_validation - async def modify_member_info(self, target: int, member_id: int, - info: Dict[str, Any]): + async def modify_member_info( + self, target: int, member_id: int, info: Dict[str, Any] + ): """ :说明: @@ -746,9 +744,6 @@ class Bot(BaseBot): * ``member_id: int``: 群员QQ号 * ``info: Dict[str, Any]``: 群员资料, 格式见 ``member_info`` 的返回值 """ - return await self.api.post('memberInfo', - params={ - 'target': target, - 'memberId': member_id, - 'info': info - }) + return await self.api.post( + "memberInfo", params={"target": target, "memberId": member_id, "info": info} + ) diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/config.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/config.py index 6783259f..241af2a3 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/config.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/config.py @@ -1,7 +1,7 @@ -from ipaddress import IPv4Address from typing import Optional +from ipaddress import IPv4Address -from pydantic import BaseModel, Extra, Field +from pydantic import Extra, Field, BaseModel class Config(BaseModel): @@ -14,9 +14,10 @@ class Config(BaseModel): - ``mirai_host``: mirai-api-http 的地址 - ``mirai_port``: mirai-api-http 的端口 """ - auth_key: Optional[str] = Field(None, alias='mirai_auth_key') - host: Optional[IPv4Address] = Field(None, alias='mirai_host') - port: Optional[int] = Field(None, alias='mirai_port') + + auth_key: Optional[str] = Field(None, alias="mirai_auth_key") + host: Optional[IPv4Address] = Field(None, alias="mirai_host") + port: Optional[int] = Field(None, alias="mirai_port") class Config: extra = Extra.ignore diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/__init__.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/__init__.py index 78e5cba4..14e21c74 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/__init__.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/__init__.py @@ -5,25 +5,56 @@ r""" 部分字段可能与文档在符号上不一致 \:\:\: """ -from .base import (Event, GroupChatInfo, GroupInfo, PrivateChatInfo, - UserPermission) -from .message import * from .notice import * +from .message import * from .request import * +from .base import ( + Event, + GroupInfo, + GroupChatInfo, + UserPermission, + PrivateChatInfo, +) __all__ = [ - 'Event', 'GroupChatInfo', 'GroupInfo', 'PrivateChatInfo', 'UserPermission', - 'MessageSource', 'MessageEvent', 'GroupMessage', 'FriendMessage', - 'TempMessage', 'NoticeEvent', 'MuteEvent', 'BotMuteEvent', 'BotUnmuteEvent', - 'MemberMuteEvent', 'MemberUnmuteEvent', 'BotJoinGroupEvent', - 'BotLeaveEventActive', 'BotLeaveEventKick', 'MemberJoinEvent', - 'MemberLeaveEventKick', 'MemberLeaveEventQuit', 'FriendRecallEvent', - 'GroupRecallEvent', 'GroupStateChangeEvent', 'GroupNameChangeEvent', - 'GroupEntranceAnnouncementChangeEvent', 'GroupMuteAllEvent', - 'GroupAllowAnonymousChatEvent', 'GroupAllowConfessTalkEvent', - 'GroupAllowMemberInviteEvent', 'MemberStateChangeEvent', - 'MemberCardChangeEvent', 'MemberSpecialTitleChangeEvent', - 'BotGroupPermissionChangeEvent', 'MemberPermissionChangeEvent', - 'RequestEvent', 'NewFriendRequestEvent', 'MemberJoinRequestEvent', - 'BotInvitedJoinGroupRequestEvent' + "Event", + "GroupChatInfo", + "GroupInfo", + "PrivateChatInfo", + "UserPermission", + "MessageSource", + "MessageEvent", + "GroupMessage", + "FriendMessage", + "TempMessage", + "NoticeEvent", + "MuteEvent", + "BotMuteEvent", + "BotUnmuteEvent", + "MemberMuteEvent", + "MemberUnmuteEvent", + "BotJoinGroupEvent", + "BotLeaveEventActive", + "BotLeaveEventKick", + "MemberJoinEvent", + "MemberLeaveEventKick", + "MemberLeaveEventQuit", + "FriendRecallEvent", + "GroupRecallEvent", + "GroupStateChangeEvent", + "GroupNameChangeEvent", + "GroupEntranceAnnouncementChangeEvent", + "GroupMuteAllEvent", + "GroupAllowAnonymousChatEvent", + "GroupAllowConfessTalkEvent", + "GroupAllowMemberInviteEvent", + "MemberStateChangeEvent", + "MemberCardChangeEvent", + "MemberSpecialTitleChangeEvent", + "BotGroupPermissionChangeEvent", + "MemberPermissionChangeEvent", + "RequestEvent", + "NewFriendRequestEvent", + "MemberJoinRequestEvent", + "BotInvitedJoinGroupRequestEvent", ] diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/base.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/base.py index fd95f4e8..93b7863b 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/base.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/base.py @@ -22,9 +22,10 @@ class UserPermission(str, Enum): * ``ADMINISTRATOR``: 群管理 * ``MEMBER``: 普通群成员 """ - OWNER = 'OWNER' - ADMINISTRATOR = 'ADMINISTRATOR' - MEMBER = 'MEMBER' + + OWNER = "OWNER" + ADMINISTRATOR = "ADMINISTRATOR" + MEMBER = "MEMBER" class NudgeSubjectKind(str, Enum): @@ -36,8 +37,9 @@ class NudgeSubjectKind(str, Enum): * ``Group``: 群 * ``Friend``: 好友 """ - Group = 'Group' - Friend = 'Friend' + + Group = "Group" + Friend = "Friend" class GroupInfo(BaseModel): @@ -48,7 +50,7 @@ class GroupInfo(BaseModel): class GroupChatInfo(BaseModel): id: int - name: str = Field(alias='memberName') + name: str = Field(alias="memberName") permission: UserPermission group: GroupInfo @@ -71,6 +73,7 @@ class Event(BaseEvent): .. _mirai-api-http 事件类型: https://github.com/project-mirai/mirai-api-http/blob/master/docs/EventType.md """ + self_id: int type: str @@ -79,11 +82,12 @@ class Event(BaseEvent): """ 此事件类的工厂函数, 能够通过事件数据选择合适的子类进行序列化 """ - type = data['type'] + type = data["type"] def all_subclasses(cls: Type[Event]): return set(cls.__subclasses__()).union( - [s for c in cls.__subclasses__() for s in all_subclasses(c)]) + [s for c in cls.__subclasses__() for s in all_subclasses(c)] + ) event_class: Optional[Type[Event]] = None for subclass in all_subclasses(cls): @@ -99,23 +103,25 @@ class Event(BaseEvent): return event_class.parse_obj(data) except ValidationError as e: logger.info( - f'Failed to parse {data} to class {event_class.__name__}: ' - f'{e.errors()!r}. Fallback to parent class.') + f"Failed to parse {data} to class {event_class.__name__}: " + f"{e.errors()!r}. Fallback to parent class." + ) event_class = event_class.__base__ # type: ignore - raise ValueError(f'Failed to serialize {data}.') + raise ValueError(f"Failed to serialize {data}.") @overrides(BaseEvent) def get_type(self) -> Literal["message", "notice", "request", "meta_event"]: from . import meta, notice, message, request + if isinstance(self, message.MessageEvent): - return 'message' + return "message" elif isinstance(self, notice.NoticeEvent): - return 'notice' + return "notice" elif isinstance(self, request.RequestEvent): - return 'request' + return "request" else: - return 'meta_event' + return "meta_event" @overrides(BaseEvent) def get_event_name(self) -> str: diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/message.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/message.py index 5dda0857..74896eee 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/message.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/message.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Any, Optional -from pydantic import BaseModel, Field +from pydantic import Field, BaseModel from nonebot.typing import overrides @@ -16,7 +16,8 @@ class MessageSource(BaseModel): class MessageEvent(Event): """消息事件基类""" - message_chain: MessageChain = Field(alias='messageChain') + + message_chain: MessageChain = Field(alias="messageChain") source: Optional[MessageSource] = None sender: Any @@ -39,12 +40,13 @@ class MessageEvent(Event): class GroupMessage(MessageEvent): """群消息事件""" + sender: GroupChatInfo to_me: bool = False @overrides(MessageEvent) def get_session_id(self) -> str: - return f'group_{self.sender.group.id}_' + self.get_user_id() + return f"group_{self.sender.group.id}_" + self.get_user_id() @overrides(MessageEvent) def get_user_id(self) -> str: @@ -57,6 +59,7 @@ class GroupMessage(MessageEvent): class FriendMessage(MessageEvent): """好友消息事件""" + sender: PrivateChatInfo @overrides(MessageEvent) @@ -65,7 +68,7 @@ class FriendMessage(MessageEvent): @overrides(MessageEvent) def get_session_id(self) -> str: - return 'friend_' + self.get_user_id() + return "friend_" + self.get_user_id() @overrides(MessageEvent) def is_tome(self) -> bool: @@ -74,11 +77,12 @@ class FriendMessage(MessageEvent): class TempMessage(MessageEvent): """临时会话消息事件""" + sender: GroupChatInfo @overrides(MessageEvent) def get_session_id(self) -> str: - return f'temp_{self.sender.group.id}_' + self.get_user_id() + return f"temp_{self.sender.group.id}_" + self.get_user_id() @overrides(MessageEvent) def is_tome(self) -> bool: diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/meta.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/meta.py index e42baf72..c0fa7a29 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/meta.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/meta.py @@ -3,29 +3,35 @@ from .base import Event class MetaEvent(Event): """元事件基类""" + qq: int class BotOnlineEvent(MetaEvent): """Bot登录成功""" + pass class BotOfflineEventActive(MetaEvent): """Bot主动离线""" + pass class BotOfflineEventForce(MetaEvent): """Bot被挤下线""" + pass class BotOfflineEventDropped(MetaEvent): """Bot被服务器断开或因网络问题而掉线""" + pass class BotReloginEvent(MetaEvent): """Bot主动重新登录""" - pass \ No newline at end of file + + pass diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/notice.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/notice.py index 8726d58e..0fa30dda 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/notice.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/notice.py @@ -2,88 +2,103 @@ from typing import Any, Optional from pydantic import Field -from .base import Event, GroupChatInfo, GroupInfo, NudgeSubject, UserPermission +from .base import Event, GroupInfo, NudgeSubject, GroupChatInfo, UserPermission class NoticeEvent(Event): """通知事件基类""" + pass class MuteEvent(NoticeEvent): """禁言类事件基类""" + operator: GroupChatInfo class BotMuteEvent(MuteEvent): """Bot被禁言""" + pass class BotUnmuteEvent(MuteEvent): """Bot被取消禁言""" + pass class MemberMuteEvent(MuteEvent): """群成员被禁言事件(该成员不是Bot)""" - duration_seconds: int = Field(alias='durationSeconds') + + duration_seconds: int = Field(alias="durationSeconds") member: GroupChatInfo operator: Optional[GroupChatInfo] = None class MemberUnmuteEvent(MuteEvent): """群成员被取消禁言事件(该成员不是Bot)""" + member: GroupChatInfo operator: Optional[GroupChatInfo] = None class BotJoinGroupEvent(NoticeEvent): """Bot加入了一个新群""" + group: GroupInfo class BotLeaveEventActive(BotJoinGroupEvent): """Bot主动退出一个群""" + pass class BotLeaveEventKick(BotJoinGroupEvent): """Bot被踢出一个群""" + pass class MemberJoinEvent(NoticeEvent): """新人入群的事件""" + member: GroupChatInfo class MemberLeaveEventKick(MemberJoinEvent): """成员被踢出群(该成员不是Bot)""" + operator: Optional[GroupChatInfo] = None class MemberLeaveEventQuit(MemberJoinEvent): """成员主动离群(该成员不是Bot)""" + pass class FriendRecallEvent(NoticeEvent): """好友消息撤回""" - author_id: int = Field(alias='authorId') - message_id: int = Field(alias='messageId') + + author_id: int = Field(alias="authorId") + message_id: int = Field(alias="messageId") time: int operator: int class GroupRecallEvent(FriendRecallEvent): """群消息撤回""" + group: GroupInfo operator: Optional[GroupChatInfo] = None class GroupStateChangeEvent(NoticeEvent): """群变化事件基类""" + origin: Any current: Any group: GroupInfo @@ -92,73 +107,85 @@ class GroupStateChangeEvent(NoticeEvent): class GroupNameChangeEvent(GroupStateChangeEvent): """某个群名改变""" + origin: str current: str class GroupEntranceAnnouncementChangeEvent(GroupStateChangeEvent): """某群入群公告改变""" + origin: str current: str class GroupMuteAllEvent(GroupStateChangeEvent): """全员禁言""" + origin: bool current: bool class GroupAllowAnonymousChatEvent(GroupStateChangeEvent): """匿名聊天""" + origin: bool current: bool class GroupAllowConfessTalkEvent(GroupStateChangeEvent): """坦白说""" + origin: bool current: bool class GroupAllowMemberInviteEvent(GroupStateChangeEvent): """允许群员邀请好友加群""" + origin: bool current: bool class MemberStateChangeEvent(NoticeEvent): """群成员变化事件基类""" + member: GroupChatInfo operator: Optional[GroupChatInfo] = None class MemberCardChangeEvent(MemberStateChangeEvent): """群名片改动""" + origin: str current: str class MemberSpecialTitleChangeEvent(MemberStateChangeEvent): """群头衔改动(只有群主有操作限权)""" + origin: str current: str class BotGroupPermissionChangeEvent(MemberStateChangeEvent): """Bot在群里的权限被改变""" + origin: UserPermission current: UserPermission class MemberPermissionChangeEvent(MemberStateChangeEvent): """成员权限改变的事件(该成员不是Bot)""" + origin: UserPermission current: UserPermission class NudgeEvent(NoticeEvent): """戳一戳触发事件""" - from_id: int = Field(alias='fromId') + + from_id: int = Field(alias="fromId") target: int subject: NudgeSubject action: str diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/request.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/request.py index d57af767..42f33e97 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/request.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/event/request.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING +from typing_extensions import Literal from pydantic import Field -from typing_extensions import Literal from .base import Event @@ -11,15 +11,17 @@ if TYPE_CHECKING: class RequestEvent(Event): """请求事件基类""" - event_id: int = Field(alias='eventId') + + event_id: int = Field(alias="eventId") message: str nick: str class NewFriendRequestEvent(RequestEvent): """添加好友申请""" - from_id: int = Field(alias='fromId') - group_id: int = Field(0, alias='groupId') + + from_id: int = Field(alias="fromId") + group_id: int = Field(0, alias="groupId") async def approve(self, bot: "Bot"): """ @@ -31,19 +33,18 @@ class NewFriendRequestEvent(RequestEvent): * ``bot: Bot``: 当前的 ``Bot`` 对象 """ - return await bot.api.post('/resp/newFriendRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': 0, - 'message': '' - }) + return await bot.api.post( + "/resp/newFriendRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": 0, + "message": "", + }, + ) - async def reject(self, - bot: "Bot", - operate: Literal[1, 2] = 1, - message: str = ''): + async def reject(self, bot: "Bot", operate: Literal[1, 2] = 1, message: str = ""): """ :说明: @@ -60,21 +61,24 @@ class NewFriendRequestEvent(RequestEvent): * ``message: str``: 回复的信息 """ assert operate > 0 - return await bot.api.post('/resp/newFriendRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': operate, - 'message': message - }) + return await bot.api.post( + "/resp/newFriendRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": operate, + "message": message, + }, + ) class MemberJoinRequestEvent(RequestEvent): """用户入群申请(Bot需要有管理员权限)""" - from_id: int = Field(alias='fromId') - group_id: int = Field(alias='groupId') - group_name: str = Field(alias='groupName') + + from_id: int = Field(alias="fromId") + group_id: int = Field(alias="groupId") + group_name: str = Field(alias="groupName") async def approve(self, bot: "Bot"): """ @@ -86,19 +90,20 @@ class MemberJoinRequestEvent(RequestEvent): * ``bot: Bot``: 当前的 ``Bot`` 对象 """ - return await bot.api.post('/resp/memberJoinRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': 0, - 'message': '' - }) + return await bot.api.post( + "/resp/memberJoinRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": 0, + "message": "", + }, + ) - async def reject(self, - bot: "Bot", - operate: Literal[1, 2, 3, 4] = 1, - message: str = ''): + async def reject( + self, bot: "Bot", operate: Literal[1, 2, 3, 4] = 1, message: str = "" + ): """ :说明: @@ -117,21 +122,24 @@ class MemberJoinRequestEvent(RequestEvent): * ``message: str``: 回复的信息 """ assert operate > 0 - return await bot.api.post('/resp/memberJoinRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': operate, - 'message': message - }) + return await bot.api.post( + "/resp/memberJoinRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": operate, + "message": message, + }, + ) class BotInvitedJoinGroupRequestEvent(RequestEvent): """Bot被邀请入群申请""" - from_id: int = Field(alias='fromId') - group_id: int = Field(alias='groupId') - group_name: str = Field(alias='groupName') + + from_id: int = Field(alias="fromId") + group_id: int = Field(alias="groupId") + group_name: str = Field(alias="groupName") async def approve(self, bot: "Bot"): """ @@ -143,14 +151,16 @@ class BotInvitedJoinGroupRequestEvent(RequestEvent): * ``bot: Bot``: 当前的 ``Bot`` 对象 """ - return await bot.api.post('/resp/botInvitedJoinGroupRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': 0, - 'message': '' - }) + return await bot.api.post( + "/resp/botInvitedJoinGroupRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": 0, + "message": "", + }, + ) async def reject(self, bot: "Bot", message: str = ""): """ @@ -163,11 +173,13 @@ class BotInvitedJoinGroupRequestEvent(RequestEvent): * ``bot: Bot``: 当前的 ``Bot`` 对象 * ``message: str``: 邀请消息 """ - return await bot.api.post('/resp/botInvitedJoinGroupRequestEvent', - params={ - 'eventId': self.event_id, - 'groupId': self.group_id, - 'fromId': self.from_id, - 'operate': 1, - 'message': message - }) + return await bot.api.post( + "/resp/botInvitedJoinGroupRequestEvent", + params={ + "eventId": self.event_id, + "groupId": self.group_id, + "fromId": self.from_id, + "operate": 1, + "message": message, + }, + ) diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/message.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/message.py index 14d6f9e8..2f74b197 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/message.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/message.py @@ -1,28 +1,29 @@ from enum import Enum -from typing import Any, List, Dict, Type, Iterable, Optional, Union +from typing import Any, Dict, List, Type, Union, Iterable, Optional from pydantic import validate_arguments +from nonebot.typing import overrides from nonebot.adapters import Message as BaseMessage from nonebot.adapters import MessageSegment as BaseMessageSegment -from nonebot.typing import overrides class MessageType(str, Enum): """消息类型枚举类""" - SOURCE = 'Source' - QUOTE = 'Quote' - AT = 'At' - AT_ALL = 'AtAll' - FACE = 'Face' - PLAIN = 'Plain' - IMAGE = 'Image' - FLASH_IMAGE = 'FlashImage' - VOICE = 'Voice' - XML = 'Xml' - JSON = 'Json' - APP = 'App' - POKE = 'Poke' + + SOURCE = "Source" + QUOTE = "Quote" + AT = "At" + AT_ALL = "AtAll" + FACE = "Face" + PLAIN = "Plain" + IMAGE = "Image" + FLASH_IMAGE = "FlashImage" + VOICE = "Voice" + XML = "Xml" + JSON = "Json" + APP = "App" + POKE = "Poke" class MessageSegment(BaseMessageSegment["MessageChain"]): @@ -43,21 +44,24 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): @validate_arguments @overrides(BaseMessageSegment) def __init__(self, type: MessageType, **data: Any): - super().__init__(type=type, - data={k: v for k, v in data.items() if v is not None}) + super().__init__( + type=type, data={k: v for k, v in data.items() if v is not None} + ) @overrides(BaseMessageSegment) def __str__(self) -> str: - return self.data['text'] if self.is_text() else repr(self) + return self.data["text"] if self.is_text() else repr(self) def __repr__(self) -> str: - return '[mirai:%s]' % ','.join([ - self.type.value, - *map( - lambda s: '%s=%r' % s, - self.data.items(), - ), - ]) + return "[mirai:%s]" % ",".join( + [ + self.type.value, + *map( + lambda s: "%s=%r" % s, + self.data.items(), + ), + ] + ) @overrides(BaseMessageSegment) def is_text(self) -> bool: @@ -65,15 +69,21 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): def as_dict(self) -> Dict[str, Any]: """导出可以被正常json序列化的结构体""" - return {'type': self.type.value, **self.data} + return {"type": self.type.value, **self.data} @classmethod def source(cls, id: int, time: int): return cls(type=MessageType.SOURCE, id=id, time=time) @classmethod - def quote(cls, id: int, group_id: int, sender_id: int, target_id: int, - origin: "MessageChain"): + def quote( + cls, + id: int, + group_id: int, + sender_id: int, + target_id: int, + origin: "MessageChain", + ): """ :说明: @@ -87,12 +97,14 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): * ``target_id: int``: 被引用回复的原消息的接收者者的QQ号(或群号) * ``origin: MessageChain``: 被引用回复的原消息的消息链对象 """ - return cls(type=MessageType.QUOTE, - id=id, - groupId=group_id, - senderId=sender_id, - targetId=target_id, - origin=origin.export()) + return cls( + type=MessageType.QUOTE, + id=id, + groupId=group_id, + senderId=sender_id, + targetId=target_id, + origin=origin.export(), + ) @classmethod def at(cls, target: int): @@ -144,10 +156,12 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): return cls(type=MessageType.PLAIN, text=text) @classmethod - def image(cls, - image_id: Optional[str] = None, - url: Optional[str] = None, - path: Optional[str] = None): + def image( + cls, + image_id: Optional[str] = None, + url: Optional[str] = None, + path: Optional[str] = None, + ): """ :说明: @@ -162,10 +176,12 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): return cls(type=MessageType.IMAGE, imageId=image_id, url=url, path=path) @classmethod - def flash_image(cls, - image_id: Optional[str] = None, - url: Optional[str] = None, - path: Optional[str] = None): + def flash_image( + cls, + image_id: Optional[str] = None, + url: Optional[str] = None, + path: Optional[str] = None, + ): """ :说明: @@ -175,16 +191,15 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): 同 ``image`` """ - return cls(type=MessageType.FLASH_IMAGE, - imageId=image_id, - url=url, - path=path) + return cls(type=MessageType.FLASH_IMAGE, imageId=image_id, url=url, path=path) @classmethod - def voice(cls, - voice_id: Optional[str] = None, - url: Optional[str] = None, - path: Optional[str] = None): + def voice( + cls, + voice_id: Optional[str] = None, + url: Optional[str] = None, + path: Optional[str] = None, + ): """ :说明: @@ -196,10 +211,7 @@ class MessageSegment(BaseMessageSegment["MessageChain"]): * ``url: Optional[str]``: 语音的URL,发送时可作网络语音的链接 * ``path: Optional[str]``: 语音的路径,发送本地语音 """ - return cls(type=MessageType.FLASH_IMAGE, - imageId=voice_id, - url=url, - path=path) + return cls(type=MessageType.FLASH_IMAGE, imageId=voice_id, url=url, path=path) @classmethod def xml(cls, xml: str): @@ -282,16 +294,14 @@ class MessageChain(BaseMessage[MessageSegment]): return [MessageSegment.plain(text=message)] return [ *map( - lambda x: x - if isinstance(x, MessageSegment) else MessageSegment(**x), - message) + lambda x: x if isinstance(x, MessageSegment) else MessageSegment(**x), + message, + ) ] def export(self) -> List[Dict[str, Any]]: """导出为可以被正常json序列化的数组""" - return [ - *map(lambda segment: segment.as_dict(), self.copy()) # type: ignore - ] + return [*map(lambda segment: segment.as_dict(), self.copy())] # type: ignore def extract_first(self, *type: MessageType) -> Optional[MessageSegment]: """ @@ -311,4 +321,4 @@ class MessageChain(BaseMessage[MessageSegment]): return None def __repr__(self) -> str: - return f'<{self.__class__.__name__} {[*self.copy()]}>' + return f"<{self.__class__.__name__} {[*self.copy()]}>" diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/utils.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/utils.py index 200f0197..6a403990 100644 --- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/utils.py +++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/utils.py @@ -1,17 +1,17 @@ import re from functools import wraps -from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, Callable, Optional, Coroutine import httpx from pydantic import Extra, ValidationError, validate_arguments -import nonebot.exception as exception from nonebot.log import logger +import nonebot.exception as exception from nonebot.message import handle_event from nonebot.utils import escape_tag, logger_wrapper -from .event import Event, GroupMessage, MessageEvent, MessageSource from .message import MessageType, MessageSegment +from .event import Event, GroupMessage, MessageEvent, MessageSource if TYPE_CHECKING: from .bot import Bot @@ -21,28 +21,27 @@ _AnyCallable = TypeVar("_AnyCallable", bound=Callable) class Log: - @staticmethod def log(level: str, message: str, exception: Optional[Exception] = None): - logger = logger_wrapper('MIRAI') - message = '' + escape_tag(message) + '' + logger = logger_wrapper("MIRAI") + message = "" + escape_tag(message) + "" logger(level=level.upper(), message=message, exception=exception) @classmethod def info(cls, message: Any): - cls.log('INFO', str(message)) + cls.log("INFO", str(message)) @classmethod def debug(cls, message: Any): - cls.log('DEBUG', str(message)) + cls.log("DEBUG", str(message)) @classmethod def warn(cls, message: Any): - cls.log('WARNING', str(message)) + cls.log("WARNING", str(message)) @classmethod def error(cls, message: Any, exception: Optional[Exception] = None): - cls.log('ERROR', str(message), exception=exception) + cls.log("ERROR", str(message), exception=exception) class ActionFailed(exception.ActionFailed): @@ -53,12 +52,13 @@ class ActionFailed(exception.ActionFailed): """ def __init__(self, **kwargs): - super().__init__('mirai') + super().__init__("mirai") self.data = kwargs.copy() def __repr__(self): - return self.__class__.__name__ + '(%s)' % ', '.join( - map(lambda m: '%s=%r' % m, self.data.items())) + return self.__class__.__name__ + "(%s)" % ", ".join( + map(lambda m: "%s=%r" % m, self.data.items()) + ) class InvalidArgument(exception.AdapterException): @@ -69,7 +69,7 @@ class InvalidArgument(exception.AdapterException): """ def __init__(self, **kwargs): - super().__init__('mirai') + super().__init__("mirai") def catch_network_error(function: _AsyncCallable) -> _AsyncCallable: @@ -90,11 +90,12 @@ def catch_network_error(function: _AsyncCallable) -> _AsyncCallable: try: data = await function(*args, **kwargs) except httpx.HTTPError: - raise exception.NetworkError('mirai') - logger.opt(colors=True).debug('Mirai API returned data: ' - f'{escape_tag(str(data))}') + raise exception.NetworkError("mirai") + logger.opt(colors=True).debug( + "Mirai API returned data: " f"{escape_tag(str(data))}" + ) if isinstance(data, dict): - if data.get('code', 0) != 0: + if data.get("code", 0) != 0: raise ActionFailed(**data) return data @@ -109,10 +110,9 @@ def argument_validation(function: _AnyCallable) -> _AnyCallable: 会在参数出错时释放 ``InvalidArgument`` 异常 """ - function = validate_arguments(config={ - 'arbitrary_types_allowed': True, - 'extra': Extra.forbid - })(function) + function = validate_arguments( + config={"arbitrary_types_allowed": True, "extra": Extra.forbid} + )(function) @wraps(function) def wrapper(*args, **kwargs): @@ -134,12 +134,12 @@ def process_source(bot: "Bot", event: MessageEvent) -> MessageEvent: def process_at(bot: "Bot", event: GroupMessage) -> GroupMessage: at = event.message_chain.extract_first(MessageType.AT) if at is not None: - if at.data['target'] == event.self_id: + if at.data["target"] == event.self_id: event.to_me = True else: event.message_chain.insert(0, at) if not event.message_chain: - event.message_chain.append(MessageSegment.plain('')) + event.message_chain.append(MessageSegment.plain("")) return event @@ -147,13 +147,13 @@ def process_nick(bot: "Bot", event: GroupMessage) -> GroupMessage: plain = event.message_chain.extract_first(MessageType.PLAIN) if plain is not None: text = str(plain) - nick_regex = '|'.join(filter(lambda x: x, bot.config.nickname)) + nick_regex = "|".join(filter(lambda x: x, bot.config.nickname)) matched = re.search(rf"^({nick_regex})([\s,,]*|$)", text, re.IGNORECASE) if matched is not None: event.to_me = True nickname = matched.group(1) - Log.info(f'User is calling me {nickname}') - plain.data['text'] = text[matched.end():] + Log.info(f"User is calling me {nickname}") + plain.data["text"] = text[matched.end() :] event.message_chain.insert(0, plain) return event @@ -161,7 +161,7 @@ def process_nick(bot: "Bot", event: GroupMessage) -> GroupMessage: def process_reply(bot: "Bot", event: GroupMessage) -> GroupMessage: reply = event.message_chain.extract_first(MessageType.QUOTE) if reply is not None: - if reply.data['senderId'] == event.self_id: + if reply.data["senderId"] == event.self_id: event.to_me = True else: event.message_chain.insert(0, reply) diff --git a/packages/nonebot-adapter-mirai/pyproject.toml b/packages/nonebot-adapter-mirai/pyproject.toml index 7340cd75..7f3c5380 100644 --- a/packages/nonebot-adapter-mirai/pyproject.toml +++ b/packages/nonebot-adapter-mirai/pyproject.toml @@ -34,6 +34,21 @@ nonebot2 = { path = "../../", develop = true } # url = "https://mirrors.aliyun.com/pypi/simple/" # default = true +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39"] +include = '\.pyi?$' +extend-exclude = ''' +''' + +[tool.isort] +profile = "black" +line_length = 80 +length_sort = true +skip_gitignore = true +force_sort_within_sections = true +extra_standard_library = ["typing_extensions"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/packages/nonebot-plugin-docs/nonebot_plugin_docs/__init__.py b/packages/nonebot-plugin-docs/nonebot_plugin_docs/__init__.py index 161cd626..98a40f8c 100644 --- a/packages/nonebot-plugin-docs/nonebot_plugin_docs/__init__.py +++ b/packages/nonebot-plugin-docs/nonebot_plugin_docs/__init__.py @@ -7,8 +7,7 @@ from nonebot.log import logger def init(): driver = nonebot.get_driver() try: - _module = importlib.import_module( - f"nonebot_plugin_docs.drivers.{driver.type}") + _module = importlib.import_module(f"nonebot_plugin_docs.drivers.{driver.type}") except ImportError: logger.warning(f"Driver {driver.type} not supported") return @@ -18,8 +17,9 @@ def init(): port = driver.config.port if host in ["0.0.0.0", "127.0.0.1"]: host = "localhost" - logger.opt(colors=True).info(f"Nonebot docs will be running at: " - f"http://{host}:{port}/docs/") + logger.opt(colors=True).info( + f"Nonebot docs will be running at: " f"http://{host}:{port}/docs/" + ) init() diff --git a/packages/nonebot-plugin-docs/nonebot_plugin_docs/drivers/fastapi.py b/packages/nonebot-plugin-docs/nonebot_plugin_docs/drivers/fastapi.py index da598d19..c01cbd09 100644 --- a/packages/nonebot-plugin-docs/nonebot_plugin_docs/drivers/fastapi.py +++ b/packages/nonebot-plugin-docs/nonebot_plugin_docs/drivers/fastapi.py @@ -9,6 +9,4 @@ def register_route(driver: Driver): static_path = str((Path(__file__).parent / ".." / "dist").resolve()) - app.mount("/docs", - StaticFiles(directory=static_path, html=True), - name="docs") + app.mount("/docs", StaticFiles(directory=static_path, html=True), name="docs") diff --git a/packages/nonebot-plugin-docs/pyproject.toml b/packages/nonebot-plugin-docs/pyproject.toml index ce5c9d18..8ee81dc6 100644 --- a/packages/nonebot-plugin-docs/pyproject.toml +++ b/packages/nonebot-plugin-docs/pyproject.toml @@ -18,6 +18,21 @@ nonebot2 = "^2.0.0-alpha.1" [tool.poetry.dev-dependencies] +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39"] +include = '\.pyi?$' +extend-exclude = ''' +''' + +[tool.isort] +profile = "black" +line_length = 80 +length_sort = true +skip_gitignore = true +force_sort_within_sections = true +extra_standard_library = ["typing_extensions"] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/poetry.lock b/poetry.lock index b3568e16..56535b06 100644 --- a/poetry.lock +++ b/poetry.lock @@ -151,6 +151,34 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pytz = ">=2015.7" +[[package]] +name = "black" +version = "21.11b1" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +regex = ">=2021.4.4" +tomli = ">=0.2.6,<2.0.0" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = [ + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, + {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, +] + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "blinker" version = "1.4" @@ -297,7 +325,7 @@ python-versions = ">=3.5" [[package]] name = "httpcore" -version = "0.14.2" +version = "0.14.3" description = "A minimal low-level HTTP client." category = "main" optional = false @@ -406,6 +434,20 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] perf = ["ipython"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + [[package]] name = "itsdangerous" version = "2.0.1" @@ -459,6 +501,14 @@ category = "main" optional = true python-versions = ">=3.6" +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "nonebot-adapter-cqhttp" version = "2.0.0-alpha.16" @@ -544,14 +594,34 @@ python-socketio = ">=4.6.1,<5.0.0" [[package]] name = "packaging" -version = "21.2" +version = "21.3" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2,<3" +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "priority" @@ -636,11 +706,14 @@ python-versions = "*" [[package]] name = "pyparsing" -version = "2.4.7" +version = "3.0.6" description = "Python parsing module" category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "python-dotenv" @@ -722,6 +795,14 @@ werkzeug = ">=2.0.0" [package.extras] dotenv = ["python-dotenv"] +[[package]] +name = "regex" +version = "2021.11.10" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "requests" version = "2.26.0" @@ -925,6 +1006,14 @@ category = "main" optional = true python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "tomlkit" version = "0.7.2" @@ -933,6 +1022,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "typed-ast" +version = "1.5.0" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "typing-extensions" version = "4.0.0" @@ -1100,7 +1197,7 @@ quart = ["Quart"] [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "537c91f98fd6598dbce8c2942530f18dee0858a896b6f393a684252a77dc76c6" +content-hash = "9a64b2ba25ea3367e1636545241122d5b4d454ab3042865416193f84bf358fc3" [metadata.files] aiocache = [ @@ -1221,6 +1318,10 @@ babel = [ {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"}, ] +black = [ + {file = "black-21.11b1-py3-none-any.whl", hash = "sha256:802c6c30b637b28645b7fde282ed2569c0cd777dbe493a41b6a03c1d903f99ac"}, + {file = "black-21.11b1.tar.gz", hash = "sha256:a042adbb18b3262faad5aff4e834ff186bb893f95ba3a8013f09de1e5569def2"}, +] blinker = [ {file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"}, ] @@ -1471,8 +1572,8 @@ html2text = [ {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, ] httpcore = [ - {file = "httpcore-0.14.2-py3-none-any.whl", hash = "sha256:47d7c8f755719d4a57be0b6e022897e9e963bf9ce4b15b9cc006a38a1cfa2932"}, - {file = "httpcore-0.14.2.tar.gz", hash = "sha256:ff8f8b9434ec4823f95a30596fbe78039913e706d3e598b0b8955b1e1828e093"}, + {file = "httpcore-0.14.3-py3-none-any.whl", hash = "sha256:9a98d2416b78976fc5396ff1f6b26ae9885efbb3105d24eed490f20ab4c95ec1"}, + {file = "httpcore-0.14.3.tar.gz", hash = "sha256:d10162a63265a0228d5807964bd964478cbdb5178f9a2eedfebb2faba27eef5d"}, ] httptools = [ {file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"}, @@ -1515,6 +1616,10 @@ importlib-metadata = [ {file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"}, {file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"}, ] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] itsdangerous = [ {file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"}, {file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"}, @@ -1672,6 +1777,10 @@ multidict = [ {file = "multidict-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c9631c642e08b9fff1c6255487e62971d8b8e821808ddd013d8ac058087591ac"}, {file = "multidict-5.2.0.tar.gz", hash = "sha256:0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"}, ] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] nonebot-adapter-cqhttp = [] nonebot-adapter-ding = [] nonebot-adapter-feishu = [] @@ -1681,8 +1790,16 @@ nonebot-plugin-test = [ {file = "nonebot_plugin_test-0.3.0-py3-none-any.whl", hash = "sha256:edb880340436323ccd0a13b31d48975136b6bdc71daa178601c4b05b068cc73e"}, ] packaging = [ - {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, - {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] priority = [ {file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"}, @@ -1793,8 +1910,8 @@ pygtrie = [ {file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"}, ] pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, ] python-dotenv = [ {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, @@ -1851,6 +1968,57 @@ quart = [ {file = "Quart-0.15.1-py3-none-any.whl", hash = "sha256:f35134fb1d81af61624e6d89bca33cd611dcedce2dc4e291f527ab04395f4e1a"}, {file = "Quart-0.15.1.tar.gz", hash = "sha256:f80c91d1e0588662483e22dd9c368a5778886b62e128c5399d2cc1b1898482cf"}, ] +regex = [ + {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, + {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, + {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, + {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, + {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, + {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, + {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, + {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, + {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, + {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, + {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, + {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, + {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, + {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, + {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, +] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, @@ -1908,10 +2076,35 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, +] tomlkit = [ {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, ] +typed-ast = [ + {file = "typed_ast-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b310a207ee9fde3f46ba327989e6cba4195bc0c8c70a158456e7b10233e6bed"}, + {file = "typed_ast-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52ca2b2b524d770bed7a393371a38e91943f9160a190141e0df911586066ecda"}, + {file = "typed_ast-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14fed8820114a389a2b7e91624db5f85f3f6682fda09fe0268a59aabd28fe5f5"}, + {file = "typed_ast-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:65c81abbabda7d760df7304d843cc9dbe7ef5d485504ca59a46ae2d1731d2428"}, + {file = "typed_ast-1.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:37ba2ab65a0028b1a4f2b61a8fe77f12d242731977d274a03d68ebb751271508"}, + {file = "typed_ast-1.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:49af5b8f6f03ed1eb89ee06c1d7c2e7c8e743d720c3746a5857609a1abc94c94"}, + {file = "typed_ast-1.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e4374a76e61399a173137e7984a1d7e356038cf844f24fd8aea46c8029a2f712"}, + {file = "typed_ast-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ea517c2bb11c5e4ba7a83a91482a2837041181d57d3ed0749a6c382a2b6b7086"}, + {file = "typed_ast-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:51040bf45aacefa44fa67fb9ebcd1f2bec73182b99a532c2394eea7dabd18e24"}, + {file = "typed_ast-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:806e0c7346b9b4af8c62d9a29053f484599921a4448c37fbbcbbf15c25138570"}, + {file = "typed_ast-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a67fd5914603e2165e075f1b12f5a8356bfb9557e8bfb74511108cfbab0f51ed"}, + {file = "typed_ast-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:224afecb8b39739f5c9562794a7c98325cb9d972712e1a98b6989a4720219541"}, + {file = "typed_ast-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:155b74b078be842d2eb630dd30a280025eca0a5383c7d45853c27afee65f278f"}, + {file = "typed_ast-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:361b9e5d27bd8e3ccb6ea6ad6c4f3c0be322a1a0f8177db6d56264fa0ae40410"}, + {file = "typed_ast-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:618912cbc7e17b4aeba86ffe071698c6e2d292acbd6d1d5ec1ee724b8c4ae450"}, + {file = "typed_ast-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7e6731044f748340ef68dcadb5172a4b1f40847a2983fe3983b2a66445fbc8e6"}, + {file = "typed_ast-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8a9b9c87801cecaad3b4c2b8876387115d1a14caa602c1618cedbb0cb2a14e6"}, + {file = "typed_ast-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec184dfb5d3d11e82841dbb973e7092b75f306b625fad7b2e665b64c5d60ab3f"}, + {file = "typed_ast-1.5.0.tar.gz", hash = "sha256:ff4ad88271aa7a55f19b6a161ed44e088c393846d954729549e3cde8257747bb"}, +] typing-extensions = [ {file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"}, {file = "typing_extensions-4.0.0.tar.gz", hash = "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed"}, diff --git a/pyproject.toml b/pyproject.toml index 404eb0ac..1b00e887 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,8 +36,9 @@ uvicorn = { version = "^0.15.0", extras = ["standard"] } aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true } [tool.poetry.dev-dependencies] -yapf = "^0.31.0" sphinx = "^4.1.1" +isort = "^5.10.1" +black = "^21.11b1" nonebot-plugin-test = "^0.3.0" nonebot-adapter-cqhttp = { path = "./packages/nonebot-adapter-cqhttp", develop = true } nonebot-adapter-ding = { path = "./packages/nonebot-adapter-ding", develop = true } @@ -55,13 +56,21 @@ all = ["quart", "aiohttp"] # url = "https://mirrors.aliyun.com/pypi/simple/" # default = true +[tool.black] +line-length = 88 +target-version = ["py37", "py38", "py39", "py310"] +include = '\.pyi?$' +extend-exclude = ''' +''' + [tool.isort] +profile = "black" line_length = 80 length_sort = true skip_gitignore = true force_sort_within_sections = true -known_local_folder = "nonebot" -extra_standard_library = "typing_extensions" +known_local_folder = ["nonebot"] +extra_standard_library = ["typing_extensions"] [build-system] requires = ["poetry_core>=1.0.0"] diff --git a/tests/bot.py b/tests/bot.py index ef719db0..739078b2 100644 --- a/tests/bot.py +++ b/tests/bot.py @@ -11,11 +11,9 @@ from nonebot.adapters.mirai import Bot as MiraiBot from nonebot.adapters.feishu import Bot as FeishuBot # test custom log -logger.add("error.log", - rotation="00:00", - diagnose=False, - level="ERROR", - format=default_format) +logger.add( + "error.log", rotation="00:00", diagnose=False, level="ERROR", format=default_format +) nonebot.init(custom_config2="config on init") app = nonebot.get_asgi() diff --git a/tests/test_plugins/test_ding.py b/tests/test_plugins/test_ding.py index ad9b8d4c..234d07fe 100644 --- a/tests/test_plugins/test_ding.py +++ b/tests/test_plugins/test_ding.py @@ -1,7 +1,8 @@ -from nonebot.adapters.ding.event import GroupMessageEvent, PrivateMessageEvent from nonebot.rule import to_me from nonebot.plugin import on_command -from nonebot.adapters.ding import Bot as DingBot, MessageSegment, MessageEvent +from nonebot.adapters.ding import Bot as DingBot +from nonebot.adapters.ding import MessageEvent, MessageSegment +from nonebot.adapters.ding.event import GroupMessageEvent, PrivateMessageEvent helper = on_command("ding_helper", to_me()) @@ -34,7 +35,7 @@ markdown = on_command("markdown", to_me()) async def markdown_handler(bot: DingBot): message = MessageSegment.markdown( "Hello, This is NoneBot", - "#### NoneBot \n> Nonebot 是一款高性能的 Python 机器人框架\n> ![screenshot](https://v2.nonebot.dev/logo.png)\n> [GitHub 仓库地址](https://github.com/nonebot/nonebot2) \n" + "#### NoneBot \n> Nonebot 是一款高性能的 Python 机器人框架\n> ![screenshot](https://v2.nonebot.dev/logo.png)\n> [GitHub 仓库地址](https://github.com/nonebot/nonebot2) \n", ) await markdown.finish(message) @@ -46,10 +47,10 @@ actionCardSingleBtn = on_command("actionCardSingleBtn", to_me()) async def actionCardSingleBtn_handler(bot: DingBot): message = MessageSegment.actionCardSingleBtn( title="打造一间咖啡厅", - text= - "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", + text="![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", singleTitle="阅读全文", - singleURL="https://www.dingtalk.com/") + singleURL="https://www.dingtalk.com/", + ) await actionCardSingleBtn.finish(message) @@ -58,26 +59,21 @@ actionCard = on_command("actionCard", to_me()) @actionCard.handle() async def actionCard_handler(bot: DingBot): - message = MessageSegment.raw({ - "msgtype": "actionCard", - "actionCard": { - "title": - "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", - "text": - "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", - "hideAvatar": - "0", - "btnOrientation": - "0", - "btns": [{ - "title": "内容不错", - "actionURL": "https://www.dingtalk.com/" - }, { - "title": "不感兴趣", - "actionURL": "https://www.dingtalk.com/" - }] + message = MessageSegment.raw( + { + "msgtype": "actionCard", + "actionCard": { + "title": "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", + "text": "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", + "hideAvatar": "0", + "btnOrientation": "0", + "btns": [ + {"title": "内容不错", "actionURL": "https://www.dingtalk.com/"}, + {"title": "不感兴趣", "actionURL": "https://www.dingtalk.com/"}, + ], + }, } - }) + ) await actionCard.finish(message, at_sender=True) @@ -86,26 +82,25 @@ feedCard = on_command("feedCard", to_me()) @feedCard.handle() async def feedCard_handler(bot: DingBot): - message = MessageSegment.raw({ - "msgtype": "feedCard", - "feedCard": { - "links": [{ - "title": - "时代的火车向前开1", - "messageURL": - "https://www.dingtalk.com/", - "picURL": - "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" - }, { - "title": - "时代的火车向前开2", - "messageURL": - "https://www.dingtalk.com/", - "picURL": - "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" - }] + message = MessageSegment.raw( + { + "msgtype": "feedCard", + "feedCard": { + "links": [ + { + "title": "时代的火车向前开1", + "messageURL": "https://www.dingtalk.com/", + "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png", + }, + { + "title": "时代的火车向前开2", + "messageURL": "https://www.dingtalk.com/", + "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png", + }, + ] + }, } - }) + ) await feedCard.finish(message) @@ -115,7 +110,8 @@ atme = on_command("atme", to_me()) @atme.handle() async def atme_handler(bot: DingBot, event: MessageEvent): message = f"@{event.senderId} manually at you" + MessageSegment.atDingtalkIds( - event.senderId) + event.senderId + ) await atme.send("matcher send auto at you", at_sender=True) await bot.send(event, "bot send auto at you", at_sender=True) await atme.finish(message) @@ -143,12 +139,12 @@ async def textAdd_handler(bot: DingBot, event: MessageEvent): message = message + MessageSegment.text("第二段消息\n") await textAdd.send(message) - message = message + MessageSegment.text( - "\n第三段消息\n") + "adfkasfkhsdkfahskdjasdashdkjasdf" - message = message + MessageSegment.extension({ - "text_type": "code_snippet", - "code_language": "C#" - }) + message = ( + message + MessageSegment.text("\n第三段消息\n") + "adfkasfkhsdkfahskdjasdashdkjasdf" + ) + message = message + MessageSegment.extension( + {"text_type": "code_snippet", "code_language": "C#"} + ) await textAdd.send(message) @@ -159,7 +155,8 @@ code = on_command("code", to_me()) async def code_handler(bot: DingBot, event: MessageEvent): raw = MessageSegment.code("Python", 'print("hello world")') await code.send(raw) - message = MessageSegment.text("""using System; + message = MessageSegment.text( + """using System; namespace HelloWorld { @@ -170,11 +167,11 @@ namespace HelloWorld Console.WriteLine("Hello World!"); } } -}""") - message += MessageSegment.extension({ - "text_type": "code_snippet", - "code_language": "C#" - }) +}""" + ) + message += MessageSegment.extension( + {"text_type": "code_snippet", "code_language": "C#"} + ) await code.finish(message) @@ -196,12 +193,12 @@ hello = on_command("hello", to_me()) @hello.handle() async def hello_handler(bot: DingBot, event: MessageEvent): - message = MessageSegment.raw({ - "msgtype": "text", - "text": { - "content": 'hello ' - }, - }) + message = MessageSegment.raw( + { + "msgtype": "text", + "text": {"content": "hello "}, + } + ) message += MessageSegment.atDingtalkIds(event.senderId) await hello.send(message) @@ -216,22 +213,21 @@ hello = on_command("webhook", to_me()) @hello.handle() async def webhook_handler(bot: DingBot, event: MessageEvent): print(event) - message = MessageSegment.raw({ - "msgtype": "text", - "text": { - "content": 'hello from webhook,一定要注意安全方式的鉴权哦,否则可能发送失败的' - }, - }) + message = MessageSegment.raw( + { + "msgtype": "text", + "text": {"content": "hello from webhook,一定要注意安全方式的鉴权哦,否则可能发送失败的"}, + } + ) message += MessageSegment.atDingtalkIds(event.senderId) await hello.send( message, - webhook= - "https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX", - secret="SECXXXXXXXXXXXXXXXXXXXXXXXXX") + webhook="https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX", + secret="SECXXXXXXXXXXXXXXXXXXXXXXXXX", + ) message = MessageSegment.text("TEST 123123 S") await hello.send( message, - webhook= - "https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX", + webhook="https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXX", ) diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py index 960ae377..5d8726eb 100644 --- a/tests/test_plugins/test_feishu.py +++ b/tests/test_plugins/test_feishu.py @@ -1,6 +1,7 @@ -from nonebot.plugin import on_command from nonebot.typing import T_State -from nonebot.adapters.feishu import Bot as FeishuBot, MessageEvent +from nonebot.plugin import on_command +from nonebot.adapters.feishu import MessageEvent +from nonebot.adapters.feishu import Bot as FeishuBot helper = on_command("say") diff --git a/tests/test_plugins/test_get_export.py b/tests/test_plugins/test_get_export.py index ec4437d1..1bf06602 100644 --- a/tests/test_plugins/test_get_export.py +++ b/tests/test_plugins/test_get_export.py @@ -1,5 +1,4 @@ import nonebot - from .test_export import export print(export, nonebot.require("test_export")) diff --git a/tests/test_plugins/test_group/__init__.py b/tests/test_plugins/test_group/__init__.py index 9d1d04a7..cd081381 100644 --- a/tests/test_plugins/test_group/__init__.py +++ b/tests/test_plugins/test_group/__init__.py @@ -4,4 +4,4 @@ from nonebot import CommandGroup, MatcherGroup cmd = CommandGroup("test", rule=to_me()) match = MatcherGroup(priority=2) -from . import commands, matches +from . import matches, commands diff --git a/tests/test_plugins/test_group/commands.py b/tests/test_plugins/test_group/commands.py index 4d0898b9..71968351 100644 --- a/tests/test_plugins/test_group/commands.py +++ b/tests/test_plugins/test_group/commands.py @@ -1,6 +1,5 @@ -from nonebot.adapters import Bot, Event - from . import cmd +from nonebot.adapters import Bot, Event test_1 = cmd.command("1", aliases={"test"}) diff --git a/tests/test_plugins/test_group/matches.py b/tests/test_plugins/test_group/matches.py index d33d8dc7..595fd6a4 100644 --- a/tests/test_plugins/test_group/matches.py +++ b/tests/test_plugins/test_group/matches.py @@ -1,9 +1,8 @@ +from . import match from nonebot.typing import T_State from nonebot.adapters import Bot, Event from nonebot.adapters.cqhttp import HeartbeatMetaEvent -from . import match - async def heartbeat(bot: Bot, event: Event, state: T_State) -> bool: return isinstance(event, HeartbeatMetaEvent) diff --git a/tests/test_plugins/test_metaevent.py b/tests/test_plugins/test_metaevent.py index e2a160e2..98f76f78 100644 --- a/tests/test_plugins/test_metaevent.py +++ b/tests/test_plugins/test_metaevent.py @@ -1,6 +1,6 @@ from nonebot.typing import T_State -from nonebot.plugin import on_metaevent from nonebot.adapters import Bot, Event +from nonebot.plugin import on_metaevent from nonebot.adapters.cqhttp import HeartbeatMetaEvent diff --git a/tests/test_plugins/test_mirai.py b/tests/test_plugins/test_mirai.py index c518290a..08b23e34 100644 --- a/tests/test_plugins/test_mirai.py +++ b/tests/test_plugins/test_mirai.py @@ -1,8 +1,8 @@ -from nonebot.plugin import on_keyword, on_command from nonebot.rule import to_me +from nonebot.plugin import on_command, on_keyword from nonebot.adapters.mirai import Bot, MessageEvent -message_test = on_keyword({'reply'}, rule=to_me()) +message_test = on_keyword({"reply"}, rule=to_me()) @message_test.handle() @@ -11,10 +11,10 @@ async def _message(bot: Bot, event: MessageEvent): await bot.send(event, text, at_sender=True) -command_test = on_command('miecho') +command_test = on_command("miecho") @command_test.handle() async def _echo(bot: Bot, event: MessageEvent): text = event.get_plaintext() - await bot.send(event, text, at_sender=True) \ No newline at end of file + await bot.send(event, text, at_sender=True) diff --git a/tests/test_plugins/test_overload.py b/tests/test_plugins/test_overload.py index 06712779..4e9074e5 100644 --- a/tests/test_plugins/test_overload.py +++ b/tests/test_plugins/test_overload.py @@ -1,5 +1,5 @@ from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, PrivateMessageEvent, GroupMessageEvent +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent overload = on_command("overload") diff --git a/tests/test_plugins/test_permission.py b/tests/test_plugins/test_permission.py index 1f8b71d6..51e2f7cd 100644 --- a/tests/test_plugins/test_permission.py +++ b/tests/test_plugins/test_permission.py @@ -1,7 +1,7 @@ from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot.plugin import on_startswith from nonebot.permission import SUPERUSER +from nonebot.plugin import on_startswith from nonebot.adapters.ding import Bot as DingBot from nonebot.adapters.cqhttp import Bot as CQHTTPBot diff --git a/tests/test_plugins/test_shell.py b/tests/test_plugins/test_shell.py index 63719b42..3428406c 100644 --- a/tests/test_plugins/test_shell.py +++ b/tests/test_plugins/test_shell.py @@ -1,7 +1,7 @@ from nonebot.adapters import Bot from nonebot.typing import T_State from nonebot import on_shell_command -from nonebot.rule import to_me, ArgumentParser +from nonebot.rule import ArgumentParser, to_me parser = ArgumentParser() parser.add_argument("-a", action="store_true")