mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-27 16:21:28 +00:00
add fastapi driver, config
This commit is contained in:
@ -2,8 +2,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from .log import logger
|
||||
import importlib
|
||||
from typing import Optional
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
from .log import logger
|
||||
from .config import Env, Config
|
||||
from .drivers import BaseDriver
|
||||
|
||||
_driver: Optional[BaseDriver] = None
|
||||
|
||||
|
||||
def get_driver() -> BaseDriver:
|
||||
if _driver is None:
|
||||
raise ValueError("NoneBot has not been initialized.")
|
||||
return _driver
|
||||
|
||||
|
||||
def get_app():
|
||||
driver = get_driver()
|
||||
return driver.server_app
|
||||
|
||||
|
||||
def get_asgi():
|
||||
driver = get_driver()
|
||||
return driver.asgi
|
||||
|
||||
|
||||
def init(*, _env_file=None, **kwargs):
|
||||
global _driver
|
||||
env = Env()
|
||||
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()}")
|
||||
|
||||
Driver = getattr(importlib.import_module(config.driver), "Driver")
|
||||
_driver = Driver(config)
|
||||
|
||||
|
||||
def run(host: Optional[IPv4Address] = None,
|
||||
port: Optional[int] = None,
|
||||
*args,
|
||||
**kwargs):
|
||||
get_driver().run(host, port, *args, **kwargs)
|
||||
|
||||
logger.setLevel(level=logging.DEBUG)
|
||||
|
||||
from .plugin import load_plugins, get_loaded_plugins
|
||||
|
29
nonebot/config.py
Normal file
29
nonebot/config.py
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Set, Union
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
from pydantic import BaseSettings
|
||||
|
||||
|
||||
class Env(BaseSettings):
|
||||
environment: str = "prod"
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
|
||||
|
||||
class Config(BaseSettings):
|
||||
driver: str = "nonebot.drivers.fastapi"
|
||||
host: IPv4Address = IPv4Address("127.0.0.1")
|
||||
port: int = 8080
|
||||
debug: bool = False
|
||||
|
||||
superusers: Set[int] = set()
|
||||
nickname: Union[str, Set[str]] = ""
|
||||
|
||||
custom_config: dict = {}
|
||||
|
||||
class Config:
|
||||
env_file = ".env.prod"
|
27
nonebot/drivers/__init__.py
Normal file
27
nonebot/drivers/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
|
||||
class BaseDriver(object):
|
||||
|
||||
@property
|
||||
def server_app(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def asgi(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def run(self,
|
||||
host: Optional[IPv4Address] = None,
|
||||
port: Optional[int] = None,
|
||||
*args,
|
||||
**kwargs):
|
||||
raise NotImplementedError
|
78
nonebot/drivers/fastapi.py
Normal file
78
nonebot/drivers/fastapi.py
Normal file
@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from typing import Optional
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
|
||||
from . import BaseDriver
|
||||
|
||||
|
||||
class Driver(BaseDriver):
|
||||
|
||||
def __init__(self, config):
|
||||
self._server_app = FastAPI(
|
||||
debug=config.debug,
|
||||
openapi_url=None,
|
||||
docs_url=None,
|
||||
redoc_url=None,
|
||||
)
|
||||
|
||||
self.config = config
|
||||
|
||||
@property
|
||||
def server_app(self):
|
||||
return self._server_app
|
||||
|
||||
@property
|
||||
def asgi(self):
|
||||
return self._server_app
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
return logging.getLogger("fastapi")
|
||||
|
||||
def run(self,
|
||||
host: Optional[IPv4Address] = None,
|
||||
port: Optional[int] = None,
|
||||
*,
|
||||
app: Optional[str] = None,
|
||||
**kwargs):
|
||||
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",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn.error": {
|
||||
"handlers": ["default"],
|
||||
"level": "INFO"
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["default"],
|
||||
"level": "INFO",
|
||||
},
|
||||
},
|
||||
}
|
||||
uvicorn.run(app or self.server_app,
|
||||
host=host or str(self.config.host),
|
||||
port=port or self.config.port,
|
||||
reload=app and self.config.debug,
|
||||
debug=self.config.debug,
|
||||
log_config=LOGGING_CONFIG,
|
||||
**kwargs)
|
@ -5,6 +5,8 @@ import sys
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("nonebot")
|
||||
"""nonebot logger"""
|
||||
|
||||
default_handler = logging.StreamHandler(sys.stdout)
|
||||
default_handler.setFormatter(
|
||||
logging.Formatter("[%(asctime)s %(name)s] %(levelname)s: %(message)s"))
|
||||
|
@ -14,6 +14,8 @@ matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list)
|
||||
|
||||
|
||||
class Matcher:
|
||||
"""`Matcher`类
|
||||
"""
|
||||
|
||||
rule: Rule = Rule()
|
||||
handlers: List[Handler] = []
|
||||
@ -26,6 +28,8 @@ class Matcher:
|
||||
# _args_parser: Optional[Callable[[Event, dict], None]] = None
|
||||
|
||||
def __init__(self):
|
||||
"""实例化 Matcher 以便运行
|
||||
"""
|
||||
self.handlers = self.handlers.copy()
|
||||
self.state = self._default_state.copy()
|
||||
# self.parser = self._args_parser or self._default_parser
|
||||
@ -38,6 +42,11 @@ class Matcher:
|
||||
priority: int = 1,
|
||||
*,
|
||||
default_state: dict = {}) -> Type["Matcher"]:
|
||||
"""创建新的 Matcher
|
||||
|
||||
Returns:
|
||||
Type["Matcher"]: 新的 Matcher 类
|
||||
"""
|
||||
|
||||
NewMatcher = type(
|
||||
"Matcher", (Matcher,), {
|
||||
@ -52,6 +61,18 @@ class Matcher:
|
||||
|
||||
return NewMatcher
|
||||
|
||||
@classmethod
|
||||
def check_rule(cls, event: Event) -> bool:
|
||||
"""检查 Matcher 的 Rule 是否成立
|
||||
|
||||
Args:
|
||||
event (Event): 消息事件
|
||||
|
||||
Returns:
|
||||
bool: 条件成立与否
|
||||
"""
|
||||
return cls.rule(event)
|
||||
|
||||
# @classmethod
|
||||
# def args_parser(cls, func: Callable[[Event, dict], None]):
|
||||
# cls._default_parser = func
|
||||
@ -59,6 +80,7 @@ class Matcher:
|
||||
|
||||
@classmethod
|
||||
def handle(cls):
|
||||
"""直接处理消息事件"""
|
||||
|
||||
def _decorator(func: Handler) -> Handler:
|
||||
cls.handlers.append(func)
|
||||
@ -68,6 +90,7 @@ class Matcher:
|
||||
|
||||
@classmethod
|
||||
def receive(cls):
|
||||
"""接收一条新消息并处理"""
|
||||
|
||||
def _decorator(func: Handler) -> Handler:
|
||||
|
||||
@ -117,6 +140,7 @@ class Matcher:
|
||||
# def reject(cls, prompt: Optional[str] = None):
|
||||
# raise RejectedException
|
||||
|
||||
# 运行handlers
|
||||
async def run(self, bot, event):
|
||||
if not self.rule(event):
|
||||
return
|
||||
|
26
nonebot/message.py
Normal file
26
nonebot/message.py
Normal file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .log import logger
|
||||
from .event import Event
|
||||
from .matcher import matchers
|
||||
|
||||
|
||||
async def handle_message(bot, event: Event):
|
||||
# TODO: PreProcess
|
||||
|
||||
for priority in sorted(matchers.keys()):
|
||||
for index in range(len(matchers[priority])):
|
||||
Matcher = matchers[priority][index]
|
||||
if not Matcher.check_rule(event):
|
||||
continue
|
||||
|
||||
matcher = Matcher()
|
||||
if Matcher.temp:
|
||||
del matchers[priority][index]
|
||||
|
||||
try:
|
||||
await matcher.run(bot, event)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return
|
@ -12,16 +12,16 @@ class Rule:
|
||||
def __init__(self, checker: Optional[Callable[[Event], bool]] = None):
|
||||
self.checker = checker or (lambda event: True)
|
||||
|
||||
def __call__(self, event):
|
||||
def __call__(self, event: Event) -> bool:
|
||||
return self.checker(event)
|
||||
|
||||
def __and__(self, other):
|
||||
def __and__(self, other: "Rule") -> "Rule":
|
||||
return Rule(lambda event: self.checker(event) and other.checker(event))
|
||||
|
||||
def __or__(self, other):
|
||||
def __or__(self, other: "Rule") -> "Rule":
|
||||
return Rule(lambda event: self.checker(event) or other.checker(event))
|
||||
|
||||
def __neg__(self):
|
||||
def __neg__(self) -> "Rule":
|
||||
return Rule(lambda event: not self.checker(event))
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user