🔀 (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_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

View File

@ -278,3 +278,172 @@ class Matcher(metaclass=MatcherMeta):
logger.info(f"Matcher {self} running complete")
current_bot.reset(b_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
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:
@ -53,10 +53,13 @@ class Permission:
def __and__(self, other) -> NoReturn:
raise RuntimeError("And operation between Permissions is not allowed.")
def __or__(self, other: Union["Permission",
PermissionChecker]) -> "Permission":
def __or__(
self, other: Optional[Union["Permission",
PermissionChecker]]) -> "Permission":
checkers = self.checkers.copy()
if isinstance(other, Permission):
if other is None:
return self
elif isinstance(other, Permission):
checkers |= other.checkers
elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore

View File

@ -9,9 +9,9 @@ from dataclasses import dataclass
from importlib._bootstrap import _load
from nonebot.log import logger
from nonebot.matcher import Matcher
from nonebot.permission import Permission
from nonebot.typing import Handler, RuleChecker
from nonebot.matcher import Matcher, MatcherGroup
from nonebot.rule import Rule, startswith, endswith, command, regex
from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
@ -27,8 +27,8 @@ class Plugin(object):
matcher: Set[Type[Matcher]]
def on(rule: Union[Rule, RuleChecker] = Rule(),
permission: Permission = Permission(),
def on(rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Optional[Permission] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@ -37,7 +37,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("",
Rule() & rule,
permission,
permission or Permission(),
temp=temp,
priority=priority,
block=block,
@ -47,7 +47,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@ -66,8 +66,8 @@ def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
def on_message(rule: Union[Rule, RuleChecker] = Rule(),
permission: Permission = Permission(),
def on_message(rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Optional[Permission] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@ -76,7 +76,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
state: Optional[dict] = None) -> Type[Matcher]:
matcher = Matcher.new("message",
Rule() & rule,
permission,
permission or Permission(),
temp=temp,
priority=priority,
block=block,
@ -86,7 +86,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@ -105,7 +105,7 @@ def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
return matcher
def on_request(rule: Union[Rule, RuleChecker] = Rule(),
def on_request(rule: Optional[Union[Rule, RuleChecker]] = None,
*,
handlers: Optional[List[Handler]] = None,
temp: bool = False,
@ -125,27 +125,23 @@ def on_request(rule: Union[Rule, RuleChecker] = Rule(),
def on_startswith(msg: str,
rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(),
rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
return on_message(startswith(msg) &
rule, permission, **kwargs) if rule else on_message(
startswith(msg), permission, **kwargs)
return on_message(startswith(msg) & rule, **kwargs) if rule else on_message(
startswith(msg), **kwargs)
def on_endswith(msg: str,
rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(),
rule: Optional[Optional[Union[Rule, RuleChecker]]] = None,
**kwargs) -> Type[Matcher]:
return on_message(endswith(msg) &
rule, permission, **kwargs) if rule else on_message(
startswith(msg), permission, **kwargs)
return on_message(endswith(msg) & rule, **kwargs) if rule else on_message(
startswith(msg), **kwargs)
def on_command(cmd: Union[str, Tuple[str, ...]],
alias: Set[Union[str, Tuple[str, ...]]] = None,
rule: Optional[Union[Rule, RuleChecker]] = None,
permission: Permission = Permission(),
**kwargs) -> Type[Matcher]:
**kwargs) -> Union[Type[Matcher], MatcherGroup]:
if isinstance(cmd, str):
cmd = (cmd,)
@ -157,20 +153,28 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
handlers = kwargs.pop("handlers", [])
handlers.insert(0, _strip_cmd)
return on_message(
command(cmd) &
rule, permission, handlers=handlers, **kwargs) if rule else on_message(
command(cmd), permission, handlers=handlers, **kwargs)
if alias:
alias = set(map(lambda x: (x,) if isinstance(x, str) else x, alias))
group = MatcherGroup("message",
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,
flags: Union[int, re.RegexFlag] = 0,
rule: Optional[Rule] = None,
permission: Permission = Permission(),
**kwargs) -> Type[Matcher]:
return on_message(regex(pattern, flags) &
rule, permission, **kwargs) if rule else on_message(
regex(pattern, flags), permission, **kwargs)
rule, **kwargs) if rule else on_message(
regex(pattern, flags), **kwargs)
def load_plugin(module_path: str) -> Optional[Plugin]:
@ -234,3 +238,21 @@ def load_builtin_plugins():
def get_loaded_plugins() -> Set[Plugin]:
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.log import logger
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:
@ -68,9 +68,11 @@ class Rule:
*map(lambda c: c(bot, event, state), self.checkers))
return all(results)
def __and__(self, other: Union["Rule", RuleChecker]) -> "Rule":
def __and__(self, other: Optional[Union["Rule", RuleChecker]]) -> "Rule":
checkers = self.checkers.copy()
if isinstance(other, Rule):
if other is None:
return self
elif isinstance(other, Rule):
checkers |= other.checkers
elif asyncio.iscoroutinefunction(other):
checkers.add(other) # type: ignore