mirror of
https://github.com/nonebot/nonebot2.git
synced 2026-02-26 20:54:44 +00:00
💥 Remove: 移除 Python 3.9 支持 (#3860)
This commit is contained in:
@@ -41,7 +41,7 @@ FrontMatter:
|
||||
from contextvars import ContextVar
|
||||
from itertools import chain
|
||||
from types import ModuleType
|
||||
from typing import Optional, TypeVar
|
||||
from typing import TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -53,7 +53,7 @@ C = TypeVar("C", bound=BaseModel)
|
||||
|
||||
_plugins: dict[str, "Plugin"] = {}
|
||||
_managers: list["PluginManager"] = []
|
||||
_current_plugin: ContextVar[Optional["Plugin"]] = ContextVar(
|
||||
_current_plugin: ContextVar["Plugin | None"] = ContextVar(
|
||||
"_current_plugin", default=None
|
||||
)
|
||||
|
||||
@@ -71,8 +71,8 @@ def _controlled_modules() -> dict[str, str]:
|
||||
|
||||
|
||||
def _find_parent_plugin_id(
|
||||
module_name: str, controlled_modules: Optional[dict[str, str]] = None
|
||||
) -> Optional[str]:
|
||||
module_name: str, controlled_modules: dict[str, str] | None = None
|
||||
) -> str | None:
|
||||
if controlled_modules is None:
|
||||
controlled_modules = _controlled_modules()
|
||||
available = {
|
||||
@@ -85,7 +85,7 @@ def _find_parent_plugin_id(
|
||||
|
||||
|
||||
def _module_name_to_plugin_id(
|
||||
module_name: str, controlled_modules: Optional[dict[str, str]] = None
|
||||
module_name: str, controlled_modules: dict[str, str] | None = None
|
||||
) -> str:
|
||||
plugin_name = _module_name_to_plugin_name(module_name)
|
||||
if parent_plugin_id := _find_parent_plugin_id(module_name, controlled_modules):
|
||||
@@ -132,7 +132,7 @@ def _revert_plugin(plugin: "Plugin") -> None:
|
||||
parent_plugin.sub_plugins.discard(plugin)
|
||||
|
||||
|
||||
def get_plugin(plugin_id: str) -> Optional["Plugin"]:
|
||||
def get_plugin(plugin_id: str) -> "Plugin | None":
|
||||
"""获取已经导入的某个插件。
|
||||
|
||||
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
|
||||
@@ -145,7 +145,7 @@ def get_plugin(plugin_id: str) -> Optional["Plugin"]:
|
||||
return _plugins.get(plugin_id)
|
||||
|
||||
|
||||
def get_plugin_by_module_name(module_name: str) -> Optional["Plugin"]:
|
||||
def get_plugin_by_module_name(module_name: str) -> "Plugin | None":
|
||||
"""通过模块名获取已经导入的某个插件。
|
||||
|
||||
如果提供的模块名为某个插件的子模块,同样会返回该插件。
|
||||
|
||||
@@ -12,7 +12,6 @@ from itertools import chain
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Optional, Union
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.utils import path_to_module_name
|
||||
@@ -27,7 +26,7 @@ except ModuleNotFoundError: # pragma: py-lt-311
|
||||
import tomli as tomllib # pyright: ignore[reportMissingImports]
|
||||
|
||||
|
||||
def load_plugin(module_path: Union[str, Path]) -> Optional[Plugin]:
|
||||
def load_plugin(module_path: str | Path) -> Plugin | None:
|
||||
"""加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
||||
|
||||
参数:
|
||||
@@ -159,7 +158,7 @@ def load_from_toml(file_path: str, encoding: str = "utf-8") -> set[Plugin]:
|
||||
)
|
||||
|
||||
|
||||
def load_builtin_plugin(name: str) -> Optional[Plugin]:
|
||||
def load_builtin_plugin(name: str) -> Plugin | None:
|
||||
"""导入 NoneBot 内置插件。
|
||||
|
||||
参数:
|
||||
@@ -177,7 +176,7 @@ def load_builtin_plugins(*plugins: str) -> set[Plugin]:
|
||||
return load_all_plugins([f"nonebot.plugins.{p}" for p in plugins], [])
|
||||
|
||||
|
||||
def _find_manager_by_name(name: str) -> Optional[PluginManager]:
|
||||
def _find_manager_by_name(name: str) -> PluginManager | None:
|
||||
for manager in reversed(_managers):
|
||||
if (
|
||||
name in manager.controlled_modules
|
||||
@@ -217,7 +216,7 @@ def require(name: str) -> ModuleType:
|
||||
return plugin.module
|
||||
|
||||
|
||||
def inherit_supported_adapters(*names: str) -> Optional[set[str]]:
|
||||
def inherit_supported_adapters(*names: str) -> set[str] | None:
|
||||
"""获取已加载插件的适配器支持状态集合。
|
||||
|
||||
如果传入了多个插件名称,返回值会自动取交集。
|
||||
@@ -229,7 +228,7 @@ def inherit_supported_adapters(*names: str) -> Optional[set[str]]:
|
||||
RuntimeError: 插件未加载
|
||||
ValueError: 插件缺少元数据
|
||||
"""
|
||||
final_supported: Optional[set[str]] = None
|
||||
final_supported: set[str] | None = None
|
||||
|
||||
for name in names:
|
||||
plugin = get_plugin(_module_name_to_plugin_id(name))
|
||||
|
||||
@@ -18,7 +18,6 @@ from pathlib import Path
|
||||
import pkgutil
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import Optional
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.utils import escape_tag, path_to_module_name
|
||||
@@ -43,8 +42,8 @@ class PluginManager:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
plugins: Optional[Iterable[str]] = None,
|
||||
search_path: Optional[Iterable[str]] = None,
|
||||
plugins: Iterable[str] | None = None,
|
||||
search_path: Iterable[str] | None = None,
|
||||
):
|
||||
# simple plugin not in search path
|
||||
self.plugins: set[str] = set(plugins or [])
|
||||
@@ -154,7 +153,7 @@ class PluginManager:
|
||||
|
||||
return self.available_plugins
|
||||
|
||||
def load_plugin(self, name: str) -> Optional[Plugin]:
|
||||
def load_plugin(self, name: str) -> Plugin | None:
|
||||
"""加载指定插件。
|
||||
|
||||
可以使用完整插件模块名或者插件标识符加载。
|
||||
@@ -211,8 +210,8 @@ class PluginFinder(MetaPathFinder):
|
||||
def find_spec(
|
||||
self,
|
||||
fullname: str,
|
||||
path: Optional[Sequence[str]],
|
||||
target: Optional[ModuleType] = None,
|
||||
path: Sequence[str] | None,
|
||||
target: ModuleType | None = None,
|
||||
):
|
||||
if _managers:
|
||||
module_spec = PathFinder.find_spec(fullname, path, target)
|
||||
@@ -235,7 +234,7 @@ class PluginLoader(SourceFileLoader):
|
||||
self.loaded = False
|
||||
super().__init__(fullname, path)
|
||||
|
||||
def create_module(self, spec) -> Optional[ModuleType]:
|
||||
def create_module(self, spec) -> ModuleType | None:
|
||||
if self.name in sys.modules:
|
||||
self.loaded = True
|
||||
return sys.modules[self.name]
|
||||
@@ -263,7 +262,7 @@ class PluginLoader(SourceFileLoader):
|
||||
_current_plugin.reset(_plugin_token)
|
||||
|
||||
# get plugin metadata
|
||||
metadata: Optional[PluginMetadata] = getattr(module, "__plugin_meta__", None)
|
||||
metadata: PluginMetadata | None = getattr(module, "__plugin_meta__", None)
|
||||
plugin.metadata = metadata
|
||||
|
||||
return
|
||||
|
||||
@@ -10,7 +10,7 @@ FrontMatter:
|
||||
import contextlib
|
||||
from dataclasses import dataclass, field
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING, Any, Optional, Type # noqa: UP035
|
||||
from typing import TYPE_CHECKING, Any, Type # noqa: UP035
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -33,13 +33,13 @@ class PluginMetadata:
|
||||
"""插件功能介绍"""
|
||||
usage: str
|
||||
"""插件使用方法"""
|
||||
type: Optional[str] = None
|
||||
type: str | None = None
|
||||
"""插件类型,用于商店分类"""
|
||||
homepage: Optional[str] = None
|
||||
homepage: str | None = None
|
||||
"""插件主页"""
|
||||
config: Optional[Type[BaseModel]] = None # noqa: UP006
|
||||
config: Type[BaseModel] | None = None # noqa: UP006
|
||||
"""插件配置项"""
|
||||
supported_adapters: Optional[set[str]] = None
|
||||
supported_adapters: set[str] | None = None
|
||||
"""插件支持的适配器模块路径
|
||||
|
||||
格式为 `<module>[:<Adapter>]`,`~` 为 `nonebot.adapters.` 的缩写。
|
||||
@@ -49,7 +49,7 @@ class PluginMetadata:
|
||||
extra: dict[Any, Any] = field(default_factory=dict)
|
||||
"""插件额外信息,可由插件编写者自由扩展定义"""
|
||||
|
||||
def get_supported_adapters(self) -> Optional[set[Type["Adapter"]]]: # noqa: UP006
|
||||
def get_supported_adapters(self) -> set[Type["Adapter"]] | None: # noqa: UP006
|
||||
"""获取当前已安装的插件支持适配器类列表"""
|
||||
if self.supported_adapters is None:
|
||||
return None
|
||||
@@ -77,11 +77,11 @@ class Plugin:
|
||||
"""导入该插件的插件管理器"""
|
||||
matcher: set[type[Matcher]] = field(default_factory=set)
|
||||
"""插件加载时定义的 `Matcher`"""
|
||||
parent_plugin: Optional["Plugin"] = None
|
||||
parent_plugin: "Plugin | None" = None
|
||||
"""父插件"""
|
||||
sub_plugins: set["Plugin"] = field(default_factory=set)
|
||||
"""子插件集合"""
|
||||
metadata: Optional[PluginMetadata] = None
|
||||
metadata: PluginMetadata | None = None
|
||||
"""插件元信息"""
|
||||
|
||||
@property
|
||||
|
||||
@@ -11,7 +11,7 @@ from datetime import datetime, timedelta
|
||||
import inspect
|
||||
import re
|
||||
from types import ModuleType
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any
|
||||
import warnings
|
||||
|
||||
from nonebot.adapters import Event
|
||||
@@ -48,7 +48,7 @@ def store_matcher(matcher: type[Matcher]) -> None:
|
||||
plugin.matcher.add(matcher)
|
||||
|
||||
|
||||
def get_matcher_plugin(depth: int = 1) -> Optional[Plugin]: # pragma: no cover
|
||||
def get_matcher_plugin(depth: int = 1) -> Plugin | None: # pragma: no cover
|
||||
"""获取事件响应器定义所在插件。
|
||||
|
||||
**Deprecated**, 请使用 {ref}`nonebot.plugin.on.get_matcher_source` 获取信息。
|
||||
@@ -63,7 +63,7 @@ def get_matcher_plugin(depth: int = 1) -> Optional[Plugin]: # pragma: no cover
|
||||
return (source := get_matcher_source(depth + 1)) and source.plugin
|
||||
|
||||
|
||||
def get_matcher_module(depth: int = 1) -> Optional[ModuleType]: # pragma: no cover
|
||||
def get_matcher_module(depth: int = 1) -> ModuleType | None: # pragma: no cover
|
||||
"""获取事件响应器定义所在模块。
|
||||
|
||||
**Deprecated**, 请使用 {ref}`nonebot.plugin.on.get_matcher_source` 获取信息。
|
||||
@@ -78,7 +78,7 @@ def get_matcher_module(depth: int = 1) -> Optional[ModuleType]: # pragma: no co
|
||||
return (source := get_matcher_source(depth + 1)) and source.module
|
||||
|
||||
|
||||
def get_matcher_source(depth: int = 0) -> Optional[MatcherSource]:
|
||||
def get_matcher_source(depth: int = 0) -> MatcherSource | None:
|
||||
"""获取事件响应器定义所在源码信息。
|
||||
|
||||
参数:
|
||||
@@ -99,7 +99,7 @@ def get_matcher_source(depth: int = 0) -> Optional[MatcherSource]:
|
||||
module_name = (module := inspect.getmodule(frame)) and module.__name__
|
||||
|
||||
# matcher defined when plugin loading
|
||||
plugin: Optional["Plugin"] = _current_plugin.get()
|
||||
plugin: Plugin | None = _current_plugin.get()
|
||||
# matcher defined when plugin running
|
||||
if plugin is None and module_name:
|
||||
plugin = get_plugin_by_module_name(module_name)
|
||||
@@ -113,15 +113,15 @@ def get_matcher_source(depth: int = 0) -> Optional[MatcherSource]:
|
||||
|
||||
def on(
|
||||
type: str = "",
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
permission: Optional[Union[Permission, T_PermissionChecker]] = None,
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
permission: Permission | T_PermissionChecker | None = None,
|
||||
*,
|
||||
handlers: Optional[list[Union[T_Handler, Dependent[Any]]]] = None,
|
||||
handlers: list[T_Handler | Dependent[Any]] | None = None,
|
||||
temp: bool = False,
|
||||
expire_time: Optional[Union[datetime, timedelta]] = None,
|
||||
expire_time: datetime | timedelta | None = None,
|
||||
priority: int = 1,
|
||||
block: bool = False,
|
||||
state: Optional[T_State] = None,
|
||||
state: T_State | None = None,
|
||||
_depth: int = 0,
|
||||
) -> type[Matcher]:
|
||||
"""注册一个基础事件响应器,可自定义类型。
|
||||
@@ -219,8 +219,8 @@ def on_request(*args, _depth: int = 0, **kwargs) -> type[Matcher]:
|
||||
|
||||
|
||||
def on_startswith(
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
msg: str | tuple[str, ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
ignorecase: bool = False,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
@@ -243,8 +243,8 @@ def on_startswith(
|
||||
|
||||
|
||||
def on_endswith(
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
msg: str | tuple[str, ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
ignorecase: bool = False,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
@@ -267,8 +267,8 @@ def on_endswith(
|
||||
|
||||
|
||||
def on_fullmatch(
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
msg: str | tuple[str, ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
ignorecase: bool = False,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
@@ -292,7 +292,7 @@ def on_fullmatch(
|
||||
|
||||
def on_keyword(
|
||||
keywords: set[str],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
@@ -313,10 +313,10 @@ def on_keyword(
|
||||
|
||||
|
||||
def on_command(
|
||||
cmd: Union[str, tuple[str, ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
|
||||
force_whitespace: Optional[Union[str, bool]] = None,
|
||||
cmd: str | tuple[str, ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
aliases: set[str | tuple[str, ...]] | None = None,
|
||||
force_whitespace: str | bool | None = None,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
@@ -348,10 +348,10 @@ def on_command(
|
||||
|
||||
|
||||
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,
|
||||
cmd: str | tuple[str, ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
aliases: set[str | tuple[str, ...]] | None = None,
|
||||
parser: ArgumentParser | None = None,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
@@ -386,8 +386,8 @@ def on_shell_command(
|
||||
|
||||
def on_regex(
|
||||
pattern: str,
|
||||
flags: Union[int, re.RegexFlag] = 0,
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
flags: int | re.RegexFlag = 0,
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
@@ -411,8 +411,8 @@ def on_regex(
|
||||
|
||||
|
||||
def on_type(
|
||||
types: Union[type[Event], tuple[type[Event], ...]],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
types: type[Event] | tuple[type[Event], ...],
|
||||
rule: Rule | T_RuleChecker | None = None,
|
||||
*,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
@@ -443,7 +443,7 @@ class _Group:
|
||||
"""其他传递给 `on` 的参数默认值"""
|
||||
|
||||
def _get_final_kwargs(
|
||||
self, update: dict[str, Any], *, exclude: Optional[set[str]] = None
|
||||
self, update: dict[str, Any], *, exclude: set[str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""获取最终传递给 `on` 的参数
|
||||
|
||||
@@ -477,7 +477,7 @@ class CommandGroup(_Group):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, cmd: Union[str, tuple[str, ...]], prefix_aliases: bool = False, **kwargs
|
||||
self, cmd: str | tuple[str, ...], prefix_aliases: bool = False, **kwargs
|
||||
):
|
||||
"""命令前缀"""
|
||||
super().__init__(**kwargs)
|
||||
@@ -488,7 +488,7 @@ class CommandGroup(_Group):
|
||||
def __repr__(self) -> str:
|
||||
return f"CommandGroup(cmd={self.basecmd}, matchers={len(self.matchers)})"
|
||||
|
||||
def command(self, cmd: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
|
||||
def command(self, cmd: str | tuple[str, ...], **kwargs) -> type[Matcher]:
|
||||
"""注册一个新的命令。新参数将会覆盖命令组默认值
|
||||
|
||||
参数:
|
||||
@@ -515,9 +515,7 @@ class CommandGroup(_Group):
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def shell_command(
|
||||
self, cmd: Union[str, tuple[str, ...]], **kwargs
|
||||
) -> type[Matcher]:
|
||||
def shell_command(self, cmd: str | tuple[str, ...], **kwargs) -> type[Matcher]:
|
||||
"""注册一个新的 `shell_like` 命令。新参数将会覆盖命令组默认值
|
||||
|
||||
参数:
|
||||
@@ -641,9 +639,7 @@ class MatcherGroup(_Group):
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_startswith(
|
||||
self, msg: Union[str, tuple[str, ...]], **kwargs
|
||||
) -> type[Matcher]:
|
||||
def on_startswith(self, msg: str | tuple[str, ...], **kwargs) -> type[Matcher]:
|
||||
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
|
||||
|
||||
参数:
|
||||
@@ -663,7 +659,7 @@ class MatcherGroup(_Group):
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_endswith(self, msg: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
|
||||
def on_endswith(self, msg: str | tuple[str, ...], **kwargs) -> type[Matcher]:
|
||||
"""注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
|
||||
|
||||
参数:
|
||||
@@ -683,7 +679,7 @@ class MatcherGroup(_Group):
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_fullmatch(self, msg: Union[str, tuple[str, ...]], **kwargs) -> type[Matcher]:
|
||||
def on_fullmatch(self, msg: str | tuple[str, ...], **kwargs) -> type[Matcher]:
|
||||
"""注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
|
||||
|
||||
参数:
|
||||
@@ -724,9 +720,9 @@ class MatcherGroup(_Group):
|
||||
|
||||
def on_command(
|
||||
self,
|
||||
cmd: Union[str, tuple[str, ...]],
|
||||
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
|
||||
force_whitespace: Optional[Union[str, bool]] = None,
|
||||
cmd: str | tuple[str, ...],
|
||||
aliases: set[str | tuple[str, ...]] | None = None,
|
||||
force_whitespace: str | bool | None = None,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
"""注册一个消息事件响应器,并且当消息以指定命令开头时响应。
|
||||
@@ -755,9 +751,9 @@ class MatcherGroup(_Group):
|
||||
|
||||
def on_shell_command(
|
||||
self,
|
||||
cmd: Union[str, tuple[str, ...]],
|
||||
aliases: Optional[set[Union[str, tuple[str, ...]]]] = None,
|
||||
parser: Optional[ArgumentParser] = None,
|
||||
cmd: str | tuple[str, ...],
|
||||
aliases: set[str | tuple[str, ...]] | None = None,
|
||||
parser: ArgumentParser | None = None,
|
||||
**kwargs,
|
||||
) -> type[Matcher]:
|
||||
"""注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
|
||||
@@ -786,7 +782,7 @@ class MatcherGroup(_Group):
|
||||
return matcher
|
||||
|
||||
def on_regex(
|
||||
self, pattern: str, flags: Union[int, re.RegexFlag] = 0, **kwargs
|
||||
self, pattern: str, flags: int | re.RegexFlag = 0, **kwargs
|
||||
) -> type[Matcher]:
|
||||
"""注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
|
||||
|
||||
@@ -810,7 +806,7 @@ class MatcherGroup(_Group):
|
||||
return matcher
|
||||
|
||||
def on_type(
|
||||
self, types: Union[type[Event], tuple[type[Event]]], **kwargs
|
||||
self, types: type[Event] | tuple[type[Event]], **kwargs
|
||||
) -> type[Matcher]:
|
||||
"""注册一个事件响应器,并且当事件为指定类型时响应。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user