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