mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-11-04 08:56:42 +00:00 
			
		
		
		
	🔀 Merge pull request #8 from nonebot/dev
This commit is contained in:
		@@ -7,9 +7,9 @@ sidebarDepth: 0
 | 
			
		||||
 | 
			
		||||
## 日志
 | 
			
		||||
 | 
			
		||||
NoneBot 使用标准库 [logging](https://docs.python.org/3/library/logging.html) 来记录日志信息。
 | 
			
		||||
NoneBot 使用 [loguru](https://github.com/Delgan/loguru) 来记录日志信息。
 | 
			
		||||
 | 
			
		||||
自定义 logger 请参考 [logging](https://docs.python.org/3/library/logging.html) 文档。
 | 
			
		||||
自定义 logger 请参考 [loguru](https://github.com/Delgan/loguru) 文档。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## `logger`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import importlib
 | 
			
		||||
from nonebot.typing import Bot, Dict, Type, Union, Driver, Optional, NoReturn
 | 
			
		||||
 | 
			
		||||
@@ -84,7 +83,7 @@ def get_asgi():
 | 
			
		||||
    return driver.asgi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_bots() -> Dict[str, Bot]:
 | 
			
		||||
def get_bots() -> Union[NoReturn, Dict[str, Bot]]:
 | 
			
		||||
    """
 | 
			
		||||
    :说明:
 | 
			
		||||
 | 
			
		||||
@@ -109,8 +108,8 @@ def get_bots() -> Dict[str, Bot]:
 | 
			
		||||
    return driver.bots
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from nonebot.log import logger
 | 
			
		||||
from nonebot.config import Env, Config
 | 
			
		||||
from nonebot.log import logger, default_filter
 | 
			
		||||
from nonebot.adapters.cqhttp import Bot as CQBot
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
@@ -146,15 +145,20 @@ def init(*, _env_file: Optional[str] = None, **kwargs):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    global _driver
 | 
			
		||||
    if not _driver:
 | 
			
		||||
        logger.debug("NoneBot is initializing...")
 | 
			
		||||
        env = Env()
 | 
			
		||||
    logger.debug(f"Current Env: {env.environment}")
 | 
			
		||||
    config = Config(**kwargs, _env_file=_env_file or f".env.{env.environment}")
 | 
			
		||||
        logger.opt(
 | 
			
		||||
            colors=True).debug(f"Current <y><b>Env: {env.environment}</b></y>")
 | 
			
		||||
        config = Config(**kwargs,
 | 
			
		||||
                        _env_file=_env_file or f".env.{env.environment}")
 | 
			
		||||
 | 
			
		||||
    logger.setLevel(logging.DEBUG if config.debug else logging.INFO)
 | 
			
		||||
    logger.debug(f"Loaded config: {config.dict()}")
 | 
			
		||||
        default_filter.level = "DEBUG" if config.debug else "INFO"
 | 
			
		||||
        logger.opt(
 | 
			
		||||
            colors=True).debug(f"Loaded <y><b>Config</b></y>: {config.dict()}")
 | 
			
		||||
 | 
			
		||||
    DriverClass: Type[Driver] = getattr(importlib.import_module(config.driver),
 | 
			
		||||
                                        "Driver")
 | 
			
		||||
        DriverClass: Type[Driver] = getattr(
 | 
			
		||||
            importlib.import_module(config.driver), "Driver")
 | 
			
		||||
        _driver = DriverClass(env, config)
 | 
			
		||||
 | 
			
		||||
        # register build-in adapters
 | 
			
		||||
@@ -193,7 +197,10 @@ def run(host: Optional[str] = None,
 | 
			
		||||
        nonebot.run(host="127.0.0.1", port=8080)
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    logger.info("Running NoneBot...")
 | 
			
		||||
    get_driver().run(host, port, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from nonebot.plugin import load_plugins, get_loaded_plugins
 | 
			
		||||
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 load_plugin, load_plugins, load_builtin_plugins, get_loaded_plugins
 | 
			
		||||
 
 | 
			
		||||
@@ -150,6 +150,16 @@ class BaseEvent(abc.ABC):
 | 
			
		||||
    def message(self, value) -> None:
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def reply(self) -> Optional[dict]:
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    @reply.setter
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def reply(self, value) -> None:
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def raw_message(self) -> Optional[str]:
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,10 @@ from nonebot.typing import overrides, Driver, WebSocket, NoReturn
 | 
			
		||||
from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def log(level: str, message: str):
 | 
			
		||||
    return logger.opt(colors=True).log(level, "<m>CQHTTP</m> | " + message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def escape(s: str, *, escape_comma: bool = True) -> str:
 | 
			
		||||
    """
 | 
			
		||||
    对字符串进行 CQ 码转义。
 | 
			
		||||
@@ -53,6 +57,19 @@ def _b2s(b: Optional[bool]) -> Optional[str]:
 | 
			
		||||
    return b if b is None else str(b).lower()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _check_reply(bot: "Bot", event: "Event"):
 | 
			
		||||
    if event.type != "message":
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    first_msg_seg = event.message[0]
 | 
			
		||||
    if first_msg_seg.type == "reply":
 | 
			
		||||
        msg_id = first_msg_seg.data["id"]
 | 
			
		||||
        event.reply = await bot.get_msg(message_id=msg_id)
 | 
			
		||||
        if event.reply["sender"]["user_id"] == event.self_id:
 | 
			
		||||
            event.to_me = True
 | 
			
		||||
        del event.message[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _check_at_me(bot: "Bot", event: "Event"):
 | 
			
		||||
    if event.type != "message":
 | 
			
		||||
        return
 | 
			
		||||
@@ -60,7 +77,6 @@ def _check_at_me(bot: "Bot", event: "Event"):
 | 
			
		||||
    if event.detail_type == "private":
 | 
			
		||||
        event.to_me = True
 | 
			
		||||
    else:
 | 
			
		||||
        event.to_me = False
 | 
			
		||||
        at_me_seg = MessageSegment.at(event.self_id)
 | 
			
		||||
 | 
			
		||||
        # check the first segment
 | 
			
		||||
@@ -109,7 +125,7 @@ def _check_nickname(bot: "Bot", event: "Event"):
 | 
			
		||||
                      re.IGNORECASE)
 | 
			
		||||
        if m:
 | 
			
		||||
            nickname = m.group(1)
 | 
			
		||||
            logger.debug(f"User is calling me {nickname}")
 | 
			
		||||
            log("DEBUG", f"User is calling me {nickname}")
 | 
			
		||||
            event.to_me = True
 | 
			
		||||
            first_msg_seg.data["text"] = first_text[m.end():]
 | 
			
		||||
 | 
			
		||||
@@ -146,7 +162,7 @@ class ResultStore:
 | 
			
		||||
        try:
 | 
			
		||||
            return await asyncio.wait_for(future, timeout)
 | 
			
		||||
        except asyncio.TimeoutError:
 | 
			
		||||
            raise NetworkError("WebSocket API call timeout")
 | 
			
		||||
            raise NetworkError("WebSocket API call timeout") from None
 | 
			
		||||
        finally:
 | 
			
		||||
            del cls._futures[seq]
 | 
			
		||||
 | 
			
		||||
@@ -186,7 +202,7 @@ class Bot(BaseBot):
 | 
			
		||||
        event = Event(message)
 | 
			
		||||
 | 
			
		||||
        # Check whether user is calling me
 | 
			
		||||
        # TODO: Check reply
 | 
			
		||||
        await _check_reply(self, event)
 | 
			
		||||
        _check_at_me(self, event)
 | 
			
		||||
        _check_nickname(self, event)
 | 
			
		||||
 | 
			
		||||
@@ -200,7 +216,8 @@ class Bot(BaseBot):
 | 
			
		||||
                bot = self.driver.bots[str(self_id)]
 | 
			
		||||
                return await bot.call_api(api, **data)
 | 
			
		||||
 | 
			
		||||
        if self.type == "websocket":
 | 
			
		||||
        log("DEBUG", f"Calling API <y>{api}</y>")
 | 
			
		||||
        if self.connection_type == "websocket":
 | 
			
		||||
            seq = ResultStore.get_seq()
 | 
			
		||||
            await self.websocket.send({
 | 
			
		||||
                "action": api,
 | 
			
		||||
@@ -212,7 +229,7 @@ class Bot(BaseBot):
 | 
			
		||||
            return _handle_api_result(await ResultStore.fetch(
 | 
			
		||||
                seq, self.config.api_timeout))
 | 
			
		||||
 | 
			
		||||
        elif self.type == "http":
 | 
			
		||||
        elif self.connection_type == "http":
 | 
			
		||||
            api_root = self.config.api_root.get(self.self_id)
 | 
			
		||||
            if not api_root:
 | 
			
		||||
                raise ApiNotAvailable
 | 
			
		||||
@@ -372,6 +389,16 @@ class Event(BaseEvent):
 | 
			
		||||
    def message(self, value) -> None:
 | 
			
		||||
        self._raw_event["message"] = value
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @overrides(BaseEvent)
 | 
			
		||||
    def reply(self) -> Optional[dict]:
 | 
			
		||||
        return self._raw_event.get("reply")
 | 
			
		||||
 | 
			
		||||
    @reply.setter
 | 
			
		||||
    @overrides(BaseEvent)
 | 
			
		||||
    def reply(self, value) -> None:
 | 
			
		||||
        self._raw_event["reply"] = value
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @overrides(BaseEvent)
 | 
			
		||||
    def raw_message(self) -> Optional[str]:
 | 
			
		||||
@@ -403,7 +430,7 @@ class MessageSegment(BaseMessageSegment):
 | 
			
		||||
    @overrides(BaseMessageSegment)
 | 
			
		||||
    def __init__(self, type: str, data: Dict[str, Union[str, list]]) -> None:
 | 
			
		||||
        if type == "text":
 | 
			
		||||
            data["text"] = unescape(data["text"])
 | 
			
		||||
            data["text"] = unescape(data["text"])  # type: ignore
 | 
			
		||||
        super().__init__(type=type, data=data)
 | 
			
		||||
 | 
			
		||||
    @overrides(BaseMessageSegment)
 | 
			
		||||
@@ -413,7 +440,9 @@ class MessageSegment(BaseMessageSegment):
 | 
			
		||||
 | 
			
		||||
        # process special types
 | 
			
		||||
        if type_ == "text":
 | 
			
		||||
            return escape(data.get("text", ""), escape_comma=False)
 | 
			
		||||
            return escape(
 | 
			
		||||
                data.get("text", ""),  # type: ignore
 | 
			
		||||
                escape_comma=False)
 | 
			
		||||
 | 
			
		||||
        params = ",".join(
 | 
			
		||||
            [f"{k}={escape(str(v))}" for k, v in data.items() if v is not None])
 | 
			
		||||
@@ -449,7 +478,7 @@ class MessageSegment(BaseMessageSegment):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def forward(id_: str) -> "MessageSegment":
 | 
			
		||||
        logger.warning("Forward Message only can be received!")
 | 
			
		||||
        log("WARNING", "Forward Message only can be received!")
 | 
			
		||||
        return MessageSegment("forward", {"id": id_})
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
 
 | 
			
		||||
@@ -174,10 +174,10 @@ class Config(BaseConfig):
 | 
			
		||||
 | 
			
		||||
        API_ROOT={"123456": "http://127.0.0.1:5700"}
 | 
			
		||||
    """
 | 
			
		||||
    api_timeout: Optional[float] = 60.
 | 
			
		||||
    api_timeout: Optional[float] = 30.
 | 
			
		||||
    """
 | 
			
		||||
    - 类型: ``Optional[float]``
 | 
			
		||||
    - 默认值: ``60.``
 | 
			
		||||
    - 默认值: ``30.``
 | 
			
		||||
    - 说明:
 | 
			
		||||
      API 请求超时时间,单位: 秒。
 | 
			
		||||
    """
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,8 @@ class BaseDriver(abc.ABC):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def register_adapter(cls, name: str, adapter: Type[Bot]):
 | 
			
		||||
        cls._adapters[name] = adapter
 | 
			
		||||
        logger.debug(f'Succeeded to load adapter "{name}"')
 | 
			
		||||
        logger.opt(
 | 
			
		||||
            colors=True).debug(f'Succeeded to load adapter "<y>{name}</y>"')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
 
 | 
			
		||||
@@ -84,17 +84,9 @@ class Driver(BaseDriver):
 | 
			
		||||
        LOGGING_CONFIG = {
 | 
			
		||||
            "version": 1,
 | 
			
		||||
            "disable_existing_loggers": False,
 | 
			
		||||
            "formatters": {
 | 
			
		||||
                "default": {
 | 
			
		||||
                    "()": "logging.Formatter",
 | 
			
		||||
                    "fmt": "[%(asctime)s %(name)s] %(levelname)s: %(message)s",
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            "handlers": {
 | 
			
		||||
                "default": {
 | 
			
		||||
                    "formatter": "default",
 | 
			
		||||
                    "class": "logging.StreamHandler",
 | 
			
		||||
                    "stream": "ext://sys.stdout",
 | 
			
		||||
                    "class": "nonebot.log.LoguruHandler",
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            "loggers": {
 | 
			
		||||
@@ -200,6 +192,9 @@ class Driver(BaseDriver):
 | 
			
		||||
 | 
			
		||||
        await ws.accept()
 | 
			
		||||
        self._clients[x_self_id] = bot
 | 
			
		||||
        logger.opt(colors=True).info(
 | 
			
		||||
            f"WebSocket Connection from <y>{adapter.upper()} "
 | 
			
		||||
            f"Bot {x_self_id}</y> Accepted!")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            while not ws.closed:
 | 
			
		||||
 
 | 
			
		||||
@@ -4,18 +4,21 @@
 | 
			
		||||
日志
 | 
			
		||||
====
 | 
			
		||||
 | 
			
		||||
NoneBot 使用标准库 `logging`_ 来记录日志信息。
 | 
			
		||||
NoneBot 使用 `loguru`_ 来记录日志信息。
 | 
			
		||||
 | 
			
		||||
自定义 logger 请参考 `logging`_ 文档。
 | 
			
		||||
自定义 logger 请参考 `loguru`_ 文档。
 | 
			
		||||
 | 
			
		||||
.. _logging:
 | 
			
		||||
    https://docs.python.org/3/library/logging.html
 | 
			
		||||
.. _loguru:
 | 
			
		||||
    https://github.com/Delgan/loguru
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger("nonebot")
 | 
			
		||||
from loguru import logger as logger_
 | 
			
		||||
 | 
			
		||||
# logger = logging.getLogger("nonebot")
 | 
			
		||||
logger = logger_
 | 
			
		||||
"""
 | 
			
		||||
:说明:
 | 
			
		||||
 | 
			
		||||
@@ -32,13 +35,52 @@ logger = logging.getLogger("nonebot")
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    from nonebot.log import logger
 | 
			
		||||
 | 
			
		||||
    # 也可以这样
 | 
			
		||||
    import logging
 | 
			
		||||
    logger = logging.getLogger("nonebot")
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
default_handler = logging.StreamHandler(sys.stdout)
 | 
			
		||||
default_handler.setFormatter(
 | 
			
		||||
    logging.Formatter("[%(asctime)s %(name)s] %(levelname)s: %(message)s"))
 | 
			
		||||
logger.addHandler(default_handler)
 | 
			
		||||
# default_handler = logging.StreamHandler(sys.stdout)
 | 
			
		||||
# default_handler.setFormatter(
 | 
			
		||||
#     logging.Formatter("[%(asctime)s %(name)s] %(levelname)s: %(message)s"))
 | 
			
		||||
# logger.addHandler(default_handler)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Filter:
 | 
			
		||||
 | 
			
		||||
    def __init__(self) -> None:
 | 
			
		||||
        self.level = "DEBUG"
 | 
			
		||||
 | 
			
		||||
    def __call__(self, record):
 | 
			
		||||
        record["name"] = record["name"].split(".")[0]
 | 
			
		||||
        levelno = logger.level(self.level).no
 | 
			
		||||
        return record["level"].no >= levelno
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LoguruHandler(logging.Handler):
 | 
			
		||||
 | 
			
		||||
    def emit(self, record):
 | 
			
		||||
        try:
 | 
			
		||||
            level = logger.level(record.levelname).name
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            level = record.levelno
 | 
			
		||||
 | 
			
		||||
        frame, depth = logging.currentframe(), 2
 | 
			
		||||
        while frame.f_code.co_filename == logging.__file__:
 | 
			
		||||
            frame = frame.f_back
 | 
			
		||||
            depth += 1
 | 
			
		||||
 | 
			
		||||
        logger.opt(depth=depth,
 | 
			
		||||
                   exception=record.exc_info).log(level, record.getMessage())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logger.remove()
 | 
			
		||||
default_filter = Filter()
 | 
			
		||||
default_format = (
 | 
			
		||||
    "<g>{time:MM-DD HH:mm:ss}</g> "
 | 
			
		||||
    "[<lvl>{level}</lvl>] "
 | 
			
		||||
    "<c><u>{name}</u></c> | "
 | 
			
		||||
    # "<c>{function}:{line}</c>| "
 | 
			
		||||
    "{message}")
 | 
			
		||||
logger.add(sys.stdout,
 | 
			
		||||
           colorize=True,
 | 
			
		||||
           diagnose=False,
 | 
			
		||||
           filter=default_filter,
 | 
			
		||||
           format=default_format)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from nonebot.log import logger
 | 
			
		||||
import typing
 | 
			
		||||
import inspect
 | 
			
		||||
from functools import wraps
 | 
			
		||||
@@ -19,9 +20,21 @@ current_bot: ContextVar = ContextVar("current_bot")
 | 
			
		||||
current_event: ContextVar = ContextVar("current_event")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Matcher:
 | 
			
		||||
class MatcherMeta(type):
 | 
			
		||||
 | 
			
		||||
    def __repr__(self) -> str:
 | 
			
		||||
        return (f"<Matcher from {self.module or 'unknow'}, "  # type: ignore
 | 
			
		||||
                f"type={self.type}, priority={self.priority}, "  # type: ignore
 | 
			
		||||
                f"temp={self.temp}>")  # type: ignore
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        return repr(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Matcher(metaclass=MatcherMeta):
 | 
			
		||||
    """`Matcher`类
 | 
			
		||||
    """
 | 
			
		||||
    module: Optional[str] = None
 | 
			
		||||
 | 
			
		||||
    type: str = ""
 | 
			
		||||
    rule: Rule = Rule()
 | 
			
		||||
@@ -43,8 +56,8 @@ class Matcher:
 | 
			
		||||
        self.state = self._default_state.copy()
 | 
			
		||||
 | 
			
		||||
    def __repr__(self) -> str:
 | 
			
		||||
        return (f"<Matcher {self.type}, priority={self.priority},"
 | 
			
		||||
                f" temp={self.temp}, expire={self.expire_time}>")
 | 
			
		||||
        return (f"<Matcher from {self.module or 'unknow'}, type={self.type}, "
 | 
			
		||||
                f"priority={self.priority}, temp={self.temp}>")
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        return self.__repr__()
 | 
			
		||||
@@ -59,6 +72,7 @@ class Matcher:
 | 
			
		||||
            priority: int = 1,
 | 
			
		||||
            block: bool = False,
 | 
			
		||||
            *,
 | 
			
		||||
            module: Optional[str] = None,
 | 
			
		||||
            default_state: Optional[dict] = None,
 | 
			
		||||
            expire_time: Optional[datetime] = None) -> Type["Matcher"]:
 | 
			
		||||
        """创建新的 Matcher
 | 
			
		||||
@@ -69,6 +83,7 @@ class Matcher:
 | 
			
		||||
 | 
			
		||||
        NewMatcher = type(
 | 
			
		||||
            "Matcher", (Matcher,), {
 | 
			
		||||
                "module": module,
 | 
			
		||||
                "type": type_,
 | 
			
		||||
                "rule": rule,
 | 
			
		||||
                "permission": permission,
 | 
			
		||||
@@ -253,5 +268,6 @@ class Matcher:
 | 
			
		||||
        except FinishedException:
 | 
			
		||||
            pass
 | 
			
		||||
        finally:
 | 
			
		||||
            logger.info(f"Matcher {self} running complete")
 | 
			
		||||
            current_bot.reset(b_t)
 | 
			
		||||
            current_event.reset(e_t)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,11 +30,10 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
 | 
			
		||||
                bot, event) or not await Matcher.check_rule(bot, event, state):
 | 
			
		||||
            return
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error(f"Rule check failed for matcher {Matcher}. Ignored.")
 | 
			
		||||
        logger.exception(e)
 | 
			
		||||
        logger.opt(colors=True, exception=e).error(
 | 
			
		||||
            f"<r><bg #f8bbd0>Rule check failed for {Matcher}.</bg #f8bbd0></r>")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # TODO: log matcher
 | 
			
		||||
    logger.info(f"Event will be handled by {Matcher}")
 | 
			
		||||
 | 
			
		||||
    matcher = Matcher()
 | 
			
		||||
@@ -43,8 +42,9 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
 | 
			
		||||
        logger.debug(f"Running matcher {matcher}")
 | 
			
		||||
        await matcher.run(bot, event, state)
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error(f"Running matcher {matcher} failed.")
 | 
			
		||||
        logger.exception(e)
 | 
			
		||||
        logger.opt(colors=True, exception=e).error(
 | 
			
		||||
            f"<r><bg #f8bbd0>Running matcher {matcher} failed.</bg #f8bbd0></r>"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    exceptions = []
 | 
			
		||||
    if Matcher.temp:
 | 
			
		||||
@@ -56,20 +56,23 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def handle_event(bot: Bot, event: Event):
 | 
			
		||||
    log_msg = f"{bot.type.upper()} Bot {event.self_id} [{event.name}]: "
 | 
			
		||||
    log_msg = f"<m>{bot.type.upper()} </m>| {event.self_id} [{event.name}]: "
 | 
			
		||||
    if event.type == "message":
 | 
			
		||||
        log_msg += f"Message {event.id} from "
 | 
			
		||||
        log_msg += str(event.user_id)
 | 
			
		||||
        if event.detail_type == "group":
 | 
			
		||||
            log_msg += f"@[群:{event.group_id}]:"
 | 
			
		||||
        log_msg += repr(str(event.message))
 | 
			
		||||
 | 
			
		||||
        log_msg += ' "' + "".join(
 | 
			
		||||
            map(lambda x: str(x) if x.type == "text" else f"<le>{x!s}</le>",
 | 
			
		||||
                event.message)) + '"'  # type: ignore
 | 
			
		||||
    elif event.type == "notice":
 | 
			
		||||
        log_msg += f"Notice {event.raw_event}"
 | 
			
		||||
    elif event.type == "request":
 | 
			
		||||
        log_msg += f"Request {event.raw_event}"
 | 
			
		||||
    elif event.type == "meta_event":
 | 
			
		||||
        log_msg += f"MetaEvent {event.raw_event}"
 | 
			
		||||
    logger.info(log_msg)
 | 
			
		||||
    logger.opt(colors=True).info(log_msg)
 | 
			
		||||
 | 
			
		||||
    coros = []
 | 
			
		||||
    state = {}
 | 
			
		||||
@@ -80,7 +83,8 @@ async def handle_event(bot: Bot, event: Event):
 | 
			
		||||
            logger.debug("Running PreProcessors...")
 | 
			
		||||
            await asyncio.gather(*coros)
 | 
			
		||||
        except IgnoredException:
 | 
			
		||||
            logger.info(f"Event {event.name} is ignored")
 | 
			
		||||
            logger.opt(
 | 
			
		||||
                colors=True).info(f"Event {event.name} is <b>ignored</b>")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    # Trie Match
 | 
			
		||||
@@ -96,7 +100,7 @@ async def handle_event(bot: Bot, event: Event):
 | 
			
		||||
            for matcher in matchers[priority]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        logger.debug(f"Checking for all matchers in priority {priority}...")
 | 
			
		||||
        logger.debug(f"Checking for matchers in priority {priority}...")
 | 
			
		||||
        results = await asyncio.gather(*pending_tasks, return_exceptions=True)
 | 
			
		||||
 | 
			
		||||
        i = 0
 | 
			
		||||
@@ -104,8 +108,12 @@ async def handle_event(bot: Bot, event: Event):
 | 
			
		||||
            if isinstance(result, _ExceptionContainer):
 | 
			
		||||
                e_list = result.exceptions
 | 
			
		||||
                if StopPropagation in e_list:
 | 
			
		||||
                    if not break_flag:
 | 
			
		||||
                        break_flag = True
 | 
			
		||||
                        logger.debug("Stop event propagation")
 | 
			
		||||
                if ExpiredException in e_list:
 | 
			
		||||
                    logger.debug(
 | 
			
		||||
                        f"Matcher {matchers[priority][index - i]} will be removed."
 | 
			
		||||
                    )
 | 
			
		||||
                    del matchers[priority][index - i]
 | 
			
		||||
                    i += 1
 | 
			
		||||
 
 | 
			
		||||
@@ -5,33 +5,32 @@ import re
 | 
			
		||||
import sys
 | 
			
		||||
import pkgutil
 | 
			
		||||
import importlib
 | 
			
		||||
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.rule import Rule, startswith, endswith, command, regex
 | 
			
		||||
from nonebot.typing import Set, Dict, Type, Tuple, Union, Optional, ModuleType, RuleChecker
 | 
			
		||||
from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType
 | 
			
		||||
 | 
			
		||||
plugins: Dict[str, "Plugin"] = {}
 | 
			
		||||
 | 
			
		||||
_tmp_matchers: Set[Type[Matcher]] = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(eq=False)
 | 
			
		||||
class Plugin(object):
 | 
			
		||||
 | 
			
		||||
    # TODO: store plugin informations
 | 
			
		||||
    def __init__(self, module_path: str, module: ModuleType,
 | 
			
		||||
                 matchers: Set[Type[Matcher]]):
 | 
			
		||||
        self.module_path = module_path
 | 
			
		||||
        self.module = module
 | 
			
		||||
        self.matchers = matchers
 | 
			
		||||
    name: str
 | 
			
		||||
    module: ModuleType
 | 
			
		||||
    matcher: Set[Type[Matcher]]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def on(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
       permission: Permission = Permission(),
 | 
			
		||||
       *,
 | 
			
		||||
       handlers: Optional[list] = None,
 | 
			
		||||
       handlers: Optional[List[Handler]] = None,
 | 
			
		||||
       temp: bool = False,
 | 
			
		||||
       priority: int = 1,
 | 
			
		||||
       block: bool = False,
 | 
			
		||||
@@ -50,7 +49,7 @@ def on(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
 | 
			
		||||
def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
                 *,
 | 
			
		||||
                 handlers: Optional[list] = None,
 | 
			
		||||
                 handlers: Optional[List[Handler]] = None,
 | 
			
		||||
                 temp: bool = False,
 | 
			
		||||
                 priority: int = 1,
 | 
			
		||||
                 block: bool = False,
 | 
			
		||||
@@ -70,7 +69,7 @@ def on_metaevent(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
def on_message(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
               permission: Permission = Permission(),
 | 
			
		||||
               *,
 | 
			
		||||
               handlers: Optional[list] = None,
 | 
			
		||||
               handlers: Optional[List[Handler]] = None,
 | 
			
		||||
               temp: bool = False,
 | 
			
		||||
               priority: int = 1,
 | 
			
		||||
               block: bool = True,
 | 
			
		||||
@@ -89,7 +88,7 @@ def on_message(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
 | 
			
		||||
def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
              *,
 | 
			
		||||
              handlers: Optional[list] = None,
 | 
			
		||||
              handlers: Optional[List[Handler]] = None,
 | 
			
		||||
              temp: bool = False,
 | 
			
		||||
              priority: int = 1,
 | 
			
		||||
              block: bool = False,
 | 
			
		||||
@@ -108,7 +107,7 @@ def on_notice(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
 | 
			
		||||
def on_request(rule: Union[Rule, RuleChecker] = Rule(),
 | 
			
		||||
               *,
 | 
			
		||||
               handlers: Optional[list] = None,
 | 
			
		||||
               handlers: Optional[List[Handler]] = None,
 | 
			
		||||
               temp: bool = False,
 | 
			
		||||
               priority: int = 1,
 | 
			
		||||
               block: bool = False,
 | 
			
		||||
@@ -149,9 +148,19 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
 | 
			
		||||
               **kwargs) -> Type[Matcher]:
 | 
			
		||||
    if isinstance(cmd, str):
 | 
			
		||||
        cmd = (cmd,)
 | 
			
		||||
    return on_message(command(cmd) &
 | 
			
		||||
                      rule, permission, **kwargs) if rule else on_message(
 | 
			
		||||
                          command(cmd), permission, **kwargs)
 | 
			
		||||
 | 
			
		||||
    async def _strip_cmd(bot, event, state: dict):
 | 
			
		||||
        message = event.message
 | 
			
		||||
        event.message = message.__class__(
 | 
			
		||||
            str(message)[len(state["_prefix"]["raw_command"]):].strip())
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def on_regex(pattern: str,
 | 
			
		||||
@@ -167,14 +176,24 @@ def on_regex(pattern: str,
 | 
			
		||||
def load_plugin(module_path: str) -> Optional[Plugin]:
 | 
			
		||||
    try:
 | 
			
		||||
        _tmp_matchers.clear()
 | 
			
		||||
        if module_path in plugins:
 | 
			
		||||
            return plugins[module_path]
 | 
			
		||||
        elif module_path in sys.modules:
 | 
			
		||||
            logger.warning(
 | 
			
		||||
                f"Module {module_path} has been loaded by other plugins! Ignored"
 | 
			
		||||
            )
 | 
			
		||||
            return
 | 
			
		||||
        module = importlib.import_module(module_path)
 | 
			
		||||
        for m in _tmp_matchers:
 | 
			
		||||
            m.module = module_path
 | 
			
		||||
        plugin = Plugin(module_path, module, _tmp_matchers.copy())
 | 
			
		||||
        plugins[module_path] = plugin
 | 
			
		||||
        logger.info(f"Succeeded to import \"{module_path}\"")
 | 
			
		||||
        logger.opt(
 | 
			
		||||
            colors=True).info(f'Succeeded to import "<y>{module_path}</y>"')
 | 
			
		||||
        return plugin
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        logger.error(f"Failed to import \"{module_path}\", error: {e}")
 | 
			
		||||
        logger.exception(e)
 | 
			
		||||
        logger.opt(colors=True, exception=e).error(
 | 
			
		||||
            f'<r><bg #f8bbd0>Failed to import "{module_path}"</bg #f8bbd0></r>')
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -187,21 +206,31 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        spec = module_info.module_finder.find_spec(name)
 | 
			
		||||
        if spec.name in sys.modules:
 | 
			
		||||
        if spec.name in plugins:
 | 
			
		||||
            continue
 | 
			
		||||
        elif spec.name in sys.modules:
 | 
			
		||||
            logger.warning(
 | 
			
		||||
                f"Module {spec.name} has been loaded by other plugin! Ignored")
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            module = _load(spec)
 | 
			
		||||
 | 
			
		||||
            for m in _tmp_matchers:
 | 
			
		||||
                m.module = name
 | 
			
		||||
            plugin = Plugin(name, module, _tmp_matchers.copy())
 | 
			
		||||
            plugins[name] = plugin
 | 
			
		||||
            loaded_plugins.add(plugin)
 | 
			
		||||
            logger.info(f"Succeeded to import \"{name}\"")
 | 
			
		||||
            logger.opt(colors=True).info(f'Succeeded to import "<y>{name}</y>"')
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(f"Failed to import \"{name}\", error: {e}")
 | 
			
		||||
            logger.exception(e)
 | 
			
		||||
            logger.opt(colors=True, exception=e).error(
 | 
			
		||||
                f'<r><bg #f8bbd0>Failed to import "{name}"</bg #f8bbd0></r>')
 | 
			
		||||
    return loaded_plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_builtin_plugins():
 | 
			
		||||
    return load_plugin("nonebot.plugins.base")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_loaded_plugins() -> Set[Plugin]:
 | 
			
		||||
    return set(plugins.values())
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								nonebot/plugins/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								nonebot/plugins/base.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
from nonebot.rule import to_me
 | 
			
		||||
from nonebot.plugin import on_command
 | 
			
		||||
from nonebot.typing import Bot, Event
 | 
			
		||||
 | 
			
		||||
say = on_command("say", to_me())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@say.handle()
 | 
			
		||||
async def repeat(bot: Bot, event: Event, state: dict):
 | 
			
		||||
    await bot.send(message=event.message, event=event)
 | 
			
		||||
@@ -74,13 +74,21 @@ class TrieRule:
 | 
			
		||||
            suffix = cls.suffix.longest_prefix(
 | 
			
		||||
                message_r.data["text"].rstrip()[::-1])
 | 
			
		||||
 | 
			
		||||
        state["_prefix"] = {prefix.key: prefix.value} if prefix else {}
 | 
			
		||||
        state["_suffix"] = {suffix.key: suffix.value} if suffix else {}
 | 
			
		||||
        state["_prefix"] = {
 | 
			
		||||
            "raw_command": prefix.key,
 | 
			
		||||
            "command": prefix.value
 | 
			
		||||
        } if prefix else {}
 | 
			
		||||
        state["_suffix"] = {
 | 
			
		||||
            "raw_command": suffix.key,
 | 
			
		||||
            "command": suffix.value
 | 
			
		||||
        } if suffix else {}
 | 
			
		||||
 | 
			
		||||
        return ({
 | 
			
		||||
            prefix.key: prefix.value
 | 
			
		||||
            "raw_command": prefix.key,
 | 
			
		||||
            "command": prefix.value
 | 
			
		||||
        } if prefix else {}, {
 | 
			
		||||
            suffix.key: suffix.value
 | 
			
		||||
            "raw_command": suffix.key,
 | 
			
		||||
            "command": suffix.value
 | 
			
		||||
        } if suffix else {})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +130,7 @@ def command(command: Tuple[str, ...]) -> Rule:
 | 
			
		||||
            TrieRule.add_prefix(f"{start}{sep.join(command)}", command)
 | 
			
		||||
 | 
			
		||||
    async def _command(bot: Bot, event: Event, state: dict) -> bool:
 | 
			
		||||
        return command in state["_prefix"].values()
 | 
			
		||||
        return command == state["_prefix"]["command"]
 | 
			
		||||
 | 
			
		||||
    return Rule(_command)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										51
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@@ -99,7 +99,7 @@ type = "legacy"
 | 
			
		||||
url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "dev"
 | 
			
		||||
category = "main"
 | 
			
		||||
description = "Cross-platform colored terminal text."
 | 
			
		||||
marker = "sys_platform == \"win32\""
 | 
			
		||||
name = "colorama"
 | 
			
		||||
@@ -332,6 +332,26 @@ reference = "aliyun"
 | 
			
		||||
type = "legacy"
 | 
			
		||||
url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "main"
 | 
			
		||||
description = "Python logging made (stupidly) simple"
 | 
			
		||||
name = "loguru"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.5"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
 | 
			
		||||
[package.dependencies]
 | 
			
		||||
colorama = ">=0.3.4"
 | 
			
		||||
win32-setctime = ">=1.0.0"
 | 
			
		||||
 | 
			
		||||
[package.extras]
 | 
			
		||||
dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=4.3.20)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.3b0)"]
 | 
			
		||||
 | 
			
		||||
[package.source]
 | 
			
		||||
reference = "aliyun"
 | 
			
		||||
type = "legacy"
 | 
			
		||||
url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "dev"
 | 
			
		||||
description = "Safely add untrusted strings to HTML/XML markup."
 | 
			
		||||
@@ -603,6 +623,7 @@ yapf = "*"
 | 
			
		||||
reference = "1438d33cbeaab0230c9f7e33bd059eb9f57c86d6"
 | 
			
		||||
type = "git"
 | 
			
		||||
url = "https://github.com/nonebot/sphinx-markdown-builder.git"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "dev"
 | 
			
		||||
description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
 | 
			
		||||
@@ -833,6 +854,23 @@ reference = "aliyun"
 | 
			
		||||
type = "legacy"
 | 
			
		||||
url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "main"
 | 
			
		||||
description = "A small Python utility to set file creation time on Windows"
 | 
			
		||||
marker = "sys_platform == \"win32\""
 | 
			
		||||
name = "win32-setctime"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.5"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
 | 
			
		||||
[package.extras]
 | 
			
		||||
dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
 | 
			
		||||
 | 
			
		||||
[package.source]
 | 
			
		||||
reference = "aliyun"
 | 
			
		||||
type = "legacy"
 | 
			
		||||
url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
category = "dev"
 | 
			
		||||
description = "A formatter for Python code."
 | 
			
		||||
@@ -848,10 +886,9 @@ url = "https://mirrors.aliyun.com/pypi/simple"
 | 
			
		||||
 | 
			
		||||
[extras]
 | 
			
		||||
scheduler = ["apscheduler"]
 | 
			
		||||
test = []
 | 
			
		||||
 | 
			
		||||
[metadata]
 | 
			
		||||
content-hash = "4d16d7ad0930bc9851802bc149f843c4e990a987e89414d765579ea8dccc8d6e"
 | 
			
		||||
content-hash = "2e8f1fc9fcb89a528ecbebbf0f2315abf39e3de8eb40c133b91085a784e49173"
 | 
			
		||||
python-versions = "^3.7"
 | 
			
		||||
 | 
			
		||||
[metadata.files]
 | 
			
		||||
@@ -949,6 +986,10 @@ jinja2 = [
 | 
			
		||||
    {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
 | 
			
		||||
    {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
 | 
			
		||||
]
 | 
			
		||||
loguru = [
 | 
			
		||||
    {file = "loguru-0.5.1-py3-none-any.whl", hash = "sha256:e5d362a43cd2fc2da63551d79a6830619c4d5b3a8b976515748026f92f351b61"},
 | 
			
		||||
    {file = "loguru-0.5.1.tar.gz", hash = "sha256:70201d5fce26da89b7a5f168caa2bb674e06b969829f56737db1d6472e53e7c3"},
 | 
			
		||||
]
 | 
			
		||||
markupsafe = [
 | 
			
		||||
    {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
 | 
			
		||||
    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
 | 
			
		||||
@@ -1136,6 +1177,10 @@ websockets = [
 | 
			
		||||
    {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"},
 | 
			
		||||
    {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
 | 
			
		||||
]
 | 
			
		||||
win32-setctime = [
 | 
			
		||||
    {file = "win32_setctime-1.0.1-py3-none-any.whl", hash = "sha256:568fd636c68350bcc54755213fe01966fe0a6c90b386c0776425944a0382abef"},
 | 
			
		||||
    {file = "win32_setctime-1.0.1.tar.gz", hash = "sha256:b47e5023ec7f0b4962950902b15bc56464a380d869f59d27dbf9ab423b23e8f9"},
 | 
			
		||||
]
 | 
			
		||||
yapf = [
 | 
			
		||||
    {file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
 | 
			
		||||
    {file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
 | 
			
		||||
 
 | 
			
		||||
@@ -27,15 +27,16 @@ fastapi = "^0.58.1"
 | 
			
		||||
uvicorn = "^0.11.5"
 | 
			
		||||
pydantic = { extras = ["dotenv"], version = "^1.6.1" }
 | 
			
		||||
apscheduler = { version = "^3.6.3", optional = true }
 | 
			
		||||
nonebot-test = { version = "^0.1.0", optional = true }
 | 
			
		||||
# nonebot-test = { version = "^0.1.0", optional = true }
 | 
			
		||||
 | 
			
		||||
loguru = "^0.5.1"
 | 
			
		||||
[tool.poetry.dev-dependencies]
 | 
			
		||||
yapf = "^0.30.0"
 | 
			
		||||
sphinx = "^3.1.1"
 | 
			
		||||
sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-builder.git" }
 | 
			
		||||
 | 
			
		||||
[tool.poetry.extras]
 | 
			
		||||
test = ["nonebot-test"]
 | 
			
		||||
# test = ["nonebot-test"]
 | 
			
		||||
scheduler = ["apscheduler"]
 | 
			
		||||
 | 
			
		||||
[[tool.poetry.source]]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								tests/bot.py
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tests/bot.py
									
									
									
									
									
								
							@@ -7,10 +7,22 @@ import sys
 | 
			
		||||
sys.path.insert(0, os.path.abspath(".."))
 | 
			
		||||
 | 
			
		||||
import nonebot
 | 
			
		||||
from nonebot.log import logger, default_format
 | 
			
		||||
 | 
			
		||||
# test custom log
 | 
			
		||||
logger.add("error.log",
 | 
			
		||||
           rotation="00:00",
 | 
			
		||||
           diagnose=False,
 | 
			
		||||
           level="ERROR",
 | 
			
		||||
           format=default_format)
 | 
			
		||||
 | 
			
		||||
nonebot.init()
 | 
			
		||||
app = nonebot.get_asgi()
 | 
			
		||||
 | 
			
		||||
# load builtin plugin
 | 
			
		||||
nonebot.load_plugin("nonebot.plugins.base")
 | 
			
		||||
 | 
			
		||||
# load local plugins
 | 
			
		||||
nonebot.load_plugins("test_plugins")
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ test_command = on_command("帮助", to_me())
 | 
			
		||||
 | 
			
		||||
@test_command.handle()
 | 
			
		||||
async def test_handler(bot: Bot, event: Event, state: dict):
 | 
			
		||||
    args = str(event.message)[len(list(state["_prefix"].keys())[0]):].strip()
 | 
			
		||||
    args = str(event.message).strip()
 | 
			
		||||
    print("[!] Command:", state["_prefix"], "Args:", args)
 | 
			
		||||
    if args:
 | 
			
		||||
        state["help"] = args
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user