🔀 (Close #11)Merge pull request #13

This commit is contained in:
Ju4tCode 2020-09-27 18:29:52 +08:00 committed by GitHub
commit 1952d93324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 233 additions and 37 deletions

View File

@ -213,5 +213,5 @@ async def _start_scheduler():
from nonebot.plugin import on_message, on_notice, on_request, on_metaevent from nonebot.plugin import on_message, on_notice, on_request, on_metaevent
from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex from nonebot.plugin import on_startswith, on_endswith, on_command, on_regex, CommandGroup
from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins from nonebot.plugin import load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins

View File

@ -278,3 +278,172 @@ class Matcher(metaclass=MatcherMeta):
logger.info(f"Matcher {self} running complete") logger.info(f"Matcher {self} running complete")
current_bot.reset(b_t) current_bot.reset(b_t)
current_event.reset(e_t) current_event.reset(e_t)
class MatcherGroup:
def __init__(self,
type_: str = "",
rule: Rule = Rule(),
permission: Permission = Permission(),
handlers: Optional[list] = None,
temp: bool = False,
priority: int = 1,
block: bool = False,
*,
module: Optional[str] = None,
default_state: Optional[dict] = None,
expire_time: Optional[datetime] = None):
self.matchers: List[Type[Matcher]] = []
self.type = type_
self.rule = rule
self.permission = permission
self.handlers = handlers
self.temp = temp
self.priority = priority
self.block = block
self.module = module
self.default_state = default_state
self.expire_time = expire_time
def __repr__(self) -> str:
return (
f"<MatcherGroup from {self.module or 'unknow'}, type={self.type}, "
f"priority={self.priority}, temp={self.temp}>")
def __str__(self) -> str:
return self.__repr__()
def new(self,
type_: str = "",
rule: Rule = Rule(),
permission: Permission = Permission(),
handlers: Optional[list] = None,
temp: bool = False,
priority: int = 1,
block: bool = False,
*,
module: Optional[str] = None,
default_state: Optional[dict] = None,
expire_time: Optional[datetime] = None) -> Type[Matcher]:
matcher = Matcher.new(type_=type_ or self.type,
rule=self.rule & rule,
permission=permission or self.permission,
handlers=handlers or self.handlers,
temp=temp or self.temp,
priority=priority or self.priority,
block=block or self.block,
module=module or self.module,
default_state=default_state or self.default_state,
expire_time=expire_time or self.expire_time)
self.matchers.append(matcher)
return matcher
def args_parser(self, func: ArgsParser) -> ArgsParser:
for matcher in self.matchers:
matcher.args_parser(func)
return func
def handle(self) -> Callable[[Handler], Handler]:
"""直接处理消息事件"""
def _decorator(func: Handler) -> Handler:
self.handlers.append(func)
return func
return _decorator
def receive(self) -> Callable[[Handler], Handler]:
"""接收一条新消息并处理"""
async def _receive(bot: Bot, event: Event, state: dict) -> NoReturn:
raise PausedException
if self.handlers:
# 已有前置handlers则接受一条新的消息否则视为接收初始消息
self.handlers.append(_receive)
def _decorator(func: Handler) -> Handler:
if not self.handlers or self.handlers[-1] is not func:
self.handlers.append(func)
return func
return _decorator
def got(
self,
key: str,
prompt: Optional[str] = None,
args_parser: Optional[ArgsParser] = None
) -> Callable[[Handler], Handler]:
async def _key_getter(bot: Bot, event: Event, state: dict):
state["_current_key"] = key
if key not in state:
if prompt:
await bot.send(event=event, message=prompt)
raise PausedException
else:
state["_skip_key"] = True
async def _key_parser(bot: Bot, event: Event, state: dict):
if key in state and state.get("_skip_key"):
del state["_skip_key"]
return
parser = args_parser or self._default_parser
if parser:
await parser(bot, event, state)
else:
state[state["_current_key"]] = str(event.message)
self.handlers.append(_key_getter)
self.handlers.append(_key_parser)
def _decorator(func: Handler) -> Handler:
if not hasattr(self.handlers[-1], "__wrapped__"):
parser = self.handlers.pop()
@wraps(func)
async def wrapper(bot: Bot, event: Event, state: dict):
await parser(bot, event, state)
await func(bot, event, state)
if "_current_key" in state:
del state["_current_key"]
self.handlers.append(wrapper)
return func
return _decorator
async def finish(
self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
raise FinishedException
async def pause(
self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
raise PausedException
async def reject(
self,
prompt: Optional[Union[str, Message,
MessageSegment]] = None) -> NoReturn:
bot: Bot = current_bot.get()
event: Event = current_event.get()
if prompt:
await bot.send(event=event, message=prompt)
raise RejectedException

View File

@ -14,7 +14,7 @@
import asyncio import asyncio
from nonebot.utils import run_sync from nonebot.utils import run_sync
from nonebot.typing import Bot, Event, Union, NoReturn, Callable, Awaitable, PermissionChecker from nonebot.typing import Bot, Event, Union, NoReturn, Optional, Callable, Awaitable, PermissionChecker
class Permission: class Permission:
@ -53,10 +53,13 @@ class Permission:
def __and__(self, other) -> NoReturn: def __and__(self, other) -> NoReturn:
raise RuntimeError("And operation between Permissions is not allowed.") raise RuntimeError("And operation between Permissions is not allowed.")
def __or__(self, other: Union["Permission", def __or__(
PermissionChecker]) -> "Permission": self, other: Optional[Union["Permission",
PermissionChecker]]) -> "Permission":
checkers = self.checkers.copy() checkers = self.checkers.copy()
if isinstance(other, Permission): if other is None:
return self
elif isinstance(other, Permission):
checkers |= other.checkers checkers |= other.checkers
elif asyncio.iscoroutinefunction(other): elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore checkers.add(other) # type: ignore

View File

@ -9,9 +9,9 @@ from dataclasses import dataclass
from importlib._bootstrap import _load from importlib._bootstrap import _load
from nonebot.log import logger from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.permission import Permission from nonebot.permission import Permission
from nonebot.typing import Handler, RuleChecker from nonebot.typing import Handler, RuleChecker
from nonebot.matcher import Matcher, MatcherGroup
from nonebot.rule import Rule, startswith, endswith, command, regex from nonebot.rule import Rule, startswith, endswith, command, regex
from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
@ -27,8 +27,8 @@ class Plugin(object):
matcher: Set[Type[Matcher]] matcher: Set[Type[Matcher]]
def on(rule: Union[Rule, RuleChecker] = Rule(), def on(rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(), permission: Optional[Permission] = None,
*, *,
handlers: Optional[List[Handler]] = None, handlers: Optional[List[Handler]] = None,
temp: bool = False, temp: bool = False,
@ -37,7 +37,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]: state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("", matcher = Matcher.new("",
Rule() & rule, Rule() & rule,
permission, permission or Permission(),
temp=temp, temp=temp,
priority=priority, priority=priority,
block=block, block=block,
@ -47,7 +47,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
return matcher return matcher
def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(), def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None,
*, *,
handlers: Optional[List[Handler]] = None, handlers: Optional[List[Handler]] = None,
temp: bool = False, temp: bool = False,
@ -66,8 +66,8 @@ def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
return matcher return matcher
def on_message(rule: Union[Rule, RuleChecker] = Rule(), def on_message(rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(), permission: Optional[Permission] = None,
*, *,
handlers: Optional[List[Handler]] = None, handlers: Optional[List[Handler]] = None,
temp: bool = False, temp: bool = False,
@ -76,7 +76,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]: state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("message", matcher = Matcher.new("message",
Rule() & rule, Rule() & rule,
permission, permission or Permission(),
temp=temp, temp=temp,
priority=priority, priority=priority,
block=block, block=block,
@ -86,7 +86,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
return matcher return matcher
def on_notice(rule: Union[Rule, RuleChecker] = Rule(), def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None,
*, *,
handlers: Optional[List[Handler]] = None, handlers: Optional[List[Handler]] = None,
temp: bool = False, temp: bool = False,
@ -105,7 +105,7 @@ def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
return matcher return matcher
def on_request(rule: Union[Rule, RuleChecker] = Rule(), def on_request(rule: Optional[Union[Rule, RuleChecker]] = None,
*, *,
handlers: Optional[List[Handler]] = None, handlers: Optional[List[Handler]] = None,
temp: bool = False, temp: bool = False,
@ -125,27 +125,23 @@ def on_request(rule: Union[Rule, RuleChecker] = Rule(),
def on_startswith(msg: str, def on_startswith(msg: str,
rule: Optional[Union[Rule, RuleChecker]] = None, rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
permission: Permission = Permission(),
**kwargs) -> Type[Matcher]: **kwargs) -> Type[Matcher]:
return on_message(startswith(msg) & return on_message(startswith(msg) & rule, **kwargs) if rule else on_message(
rule, permission, **kwargs) if rule else on_message( startswith(msg), **kwargs)
startswith(msg), permission, **kwargs)
def on_endswith(msg: str, def on_endswith(msg: str,
rule: Optional[Union[Rule, RuleChecker]] = None, rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
permission: Permission = Permission(),
**kwargs) -> Type[Matcher]: **kwargs) -> Type[Matcher]:
return on_message(endswith(msg) & return on_message(endswith(msg) & rule, **kwargs) if rule else on_message(
rule, permission, **kwargs) if rule else on_message( startswith(msg), **kwargs)
startswith(msg), permission, **kwargs)
def on_command(cmd: Union[str, Tuple[str, ...]], def on_command(cmd: Union[str, Tuple[str, ...]],
alias: Set[Union[str, Tuple[str, ...]]] = None,
rule: Optional[Union[Rule, RuleChecker]] = None, rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(), **kwargs) -> Union[Type[Matcher], MatcherGroup]:
**kwargs) -> Type[Matcher]:
if isinstance(cmd, str): if isinstance(cmd, str):
cmd = (cmd,) cmd = (cmd,)
@ -157,20 +153,28 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
handlers = kwargs.pop("handlers", []) handlers = kwargs.pop("handlers", [])
handlers.insert(0, _strip_cmd) handlers.insert(0, _strip_cmd)
return on_message( if alias:
command(cmd) & alias = set(map(lambda x: (x,) if isinstance(x, str) else x, alias))
rule, permission, handlers=handlers, **kwargs) if rule else on_message( group = MatcherGroup("message",
command(cmd), permission, handlers=handlers, **kwargs) Rule() & rule,
handlers=handlers,
**kwargs)
for cmd_ in [cmd, *alias]:
group.new(rule=command(cmd_))
return group
else:
return on_message(command(cmd) & rule, handlers=handlers, **
kwargs) if rule else on_message(
command(cmd), handlers=handlers, **kwargs)
def on_regex(pattern: str, def on_regex(pattern: str,
flags: Union[int, re.RegexFlag] = 0, flags: Union[int, re.RegexFlag] = 0,
rule: Optional[Rule] = None, rule: Optional[Rule] = None,
permission: Permission = Permission(),
**kwargs) -> Type[Matcher]: **kwargs) -> Type[Matcher]:
return on_message(regex(pattern, flags) & return on_message(regex(pattern, flags) &
rule, permission, **kwargs) if rule else on_message( rule, **kwargs) if rule else on_message(
regex(pattern, flags), permission, **kwargs) regex(pattern, flags), **kwargs)
def load_plugin(module_path: str) -> Optional[Plugin]: def load_plugin(module_path: str) -> Optional[Plugin]:
@ -234,3 +238,21 @@ def load_builtin_plugins():
def get_loaded_plugins() -> Set[Plugin]: def get_loaded_plugins() -> Set[Plugin]:
return set(plugins.values()) return set(plugins.values())
class CommandGroup:
def __init__(self, name: Union[str, Tuple[str, ...]], **kwargs):
self.basename = (name,) if isinstance(name, str) else name
if "aliases" in kwargs:
del kwargs["aliases"]
self.base_kwargs = kwargs
def command(self, name: Union[str, Tuple[str, ...]],
**kwargs) -> Union[Type[Matcher], MatcherGroup]:
sub_name = (name,) if isinstance(name, str) else name
name = self.basename + sub_name
final_kwargs = self.base_kwargs.copy()
final_kwargs.update(kwargs)
return on_command(name, **final_kwargs)

View File

@ -20,7 +20,7 @@ from pygtrie import CharTrie
from nonebot import get_driver from nonebot import get_driver
from nonebot.log import logger from nonebot.log import logger
from nonebot.utils import run_sync from nonebot.utils import run_sync
from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Callable, Awaitable, RuleChecker from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Optional, Callable, Awaitable, RuleChecker
class Rule: class Rule:
@ -68,9 +68,11 @@ class Rule:
*map(lambda c: c(bot, event, state), self.checkers)) *map(lambda c: c(bot, event, state), self.checkers))
return all(results) return all(results)
def __and__(self, other: Union["Rule", RuleChecker]) -> "Rule": def __and__(self, other: Optional[Union["Rule", RuleChecker]]) -> "Rule":
checkers = self.checkers.copy() checkers = self.checkers.copy()
if isinstance(other, Rule): if other is None:
return self
elif isinstance(other, Rule):
checkers |= other.checkers checkers |= other.checkers
elif asyncio.iscoroutinefunction(other): elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore checkers.add(other) # type: ignore