diff --git a/README.md b/README.md
index dd6fe61..53c9302 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/main/docs/badge/v2.json)](https://pydantic.dev)
[![PyPI Version](https://img.shields.io/pypi/v/nonebot-plugin-all4one.svg?style=flat-square)](https://pypi.python.org/pypi/nonebot-plugin-all4one)
[![OneBot 4A](https://img.shields.io/badge/OneBot-4A-black?style=flat-square)](https://onebot4all.vercel.app/)
-[![NoneBot Version](https://img.shields.io/badge/nonebot-2.3.0+-red.svg?style=flat-square)](https://v2.nonebot.dev/)
+[![NoneBot Version](https://img.shields.io/badge/nonebot-2.3.0+-red.svg?style=flat-square)](https://v2.nonebot.dev/)
[![NoneBot Registry](https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin%2Fnonebot-plugin-all4one)](https://registry.nonebot.dev/plugin/nonebot-plugin-all4one:nonebot_plugin_all4one)
[![Supported Adapters](https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin-adapters%2Fnonebot-plugin-all4one)](https://registry.nonebot.dev/plugin/nonebot-plugin-all4one:nonebot_plugin_all4one)
diff --git a/nonebot_plugin_all4one/__version__.py b/nonebot_plugin_all4one/__version__.py
new file mode 100644
index 0000000..bf2e442
--- /dev/null
+++ b/nonebot_plugin_all4one/__version__.py
@@ -0,0 +1,3 @@
+import importlib.metadata as importlib_metadata
+
+__version__ = importlib_metadata.version(__package__ or "nonebot-plugin-all4one")
diff --git a/nonebot_plugin_all4one/logger.py b/nonebot_plugin_all4one/logger.py
new file mode 100644
index 0000000..133e5d4
--- /dev/null
+++ b/nonebot_plugin_all4one/logger.py
@@ -0,0 +1,3 @@
+from nonebot.utils import logger_wrapper
+
+log = logger_wrapper("041")
diff --git a/nonebot_plugin_all4one/middlewares/__init__.py b/nonebot_plugin_all4one/middlewares/__init__.py
index 12e6402..afcd614 100644
--- a/nonebot_plugin_all4one/middlewares/__init__.py
+++ b/nonebot_plugin_all4one/middlewares/__init__.py
@@ -2,8 +2,7 @@
from pathlib import Path
from typing import Optional, cast
-from nonebot.log import logger
-
+from ..logger import log
from .base import Middleware
MIDDLEWARE_MAP = {}
@@ -23,4 +22,4 @@
):
MIDDLEWARE_MAP[middleware.get_name()] = middleware
except Exception:
- logger.warning(f"Failed to import {module_name}")
+ log("WARNING", f"Failed to import {module_name}")
diff --git a/nonebot_plugin_all4one/onebotimpl/__init__.py b/nonebot_plugin_all4one/onebotimpl/__init__.py
index c6766aa..9265b38 100644
--- a/nonebot_plugin_all4one/onebotimpl/__init__.py
+++ b/nonebot_plugin_all4one/onebotimpl/__init__.py
@@ -4,11 +4,10 @@
from functools import partial
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
-from typing import Any, Union, Literal, Optional, cast
+from typing import Any, Union, Literal, ClassVar, Optional, cast
from asyncio import Task, Queue, sleep, gather, wait_for, create_task
import msgpack
-from nonebot.log import logger
from nonebot.adapters import Bot
from nonebot.utils import escape_tag
from nonebot.exception import WebSocketClosed
@@ -38,7 +37,9 @@
WebSocketServerSetup,
)
+from ..logger import log
from .utils import encode_data
+from ..__version__ import __version__
from ..middlewares import MIDDLEWARE_MAP, Middleware
from .config import (
Config,
@@ -50,6 +51,10 @@
class OneBotImplementation:
+ USER_AGENT: ClassVar[str] = f"OneBot4All NoneBot-Plugin-All4One/{__version__}"
+ ONEBOT_VERSION: ClassVar[str] = "A"
+ IMPL_NAME: ClassVar[str] = "nonebot-plugin-all4one"
+
def __init__(self, driver: Driver):
self.driver = driver
self.config = Config(**self.driver.config.model_dump())
@@ -89,14 +94,10 @@ def register_middleware(self, middleware: type[Middleware]):
"""注册一个中间件"""
name = middleware.get_name()
if name in self._middlewares:
- logger.opt(colors=True).warning(
- f'Middleware "{escape_tag(name)}" already exists'
- )
+ log("WARNING", f'Middleware "{escape_tag(name)}" already exists')
return
self._middlewares[name] = middleware
- logger.opt(colors=True).info(
- f'Succeeded to load middleware "{escape_tag(name)}"'
- )
+ log("INFO", f'Succeeded to load middleware "{escape_tag(name)}"')
async def _call_api(self, data: dict[str, Any]) -> Any:
try:
@@ -190,9 +191,9 @@ async def get_version(
kwargs: 扩展字段
"""
return {
- "impl": "nonebot-plugin-all4one",
- "version": "0.1.0",
- "onebot_version": "12",
+ "impl": self.IMPL_NAME,
+ "version": __version__,
+ "onebot_version": self.ONEBOT_VERSION,
}
def _check_access_token(
@@ -215,20 +216,20 @@ async def _ws_send(
websocket: WebSocket,
conn: Union[WebsocketConfig, WebsocketReverseConfig],
) -> None:
- queue = Queue()
+ queue = Queue[Event]()
self.queues.append(queue)
try:
while True:
event = await queue.get()
- await websocket.send(encode_data(event.dict(), conn.use_msgpack))
+ await websocket.send(encode_data(event.model_dump(), conn.use_msgpack))
except WebSocketClosed:
- logger.opt(colors=True).warning(
- "WebSocket Closed"
- )
- except Exception:
- logger.opt(colors=True).exception(
- "Error while process data from websocket"
- ". Trying to reconnect..."
+ log("WARNING", "WebSocket Closed")
+ except Exception as e:
+ log(
+ "ERROR",
+ "Error while process data from websocket. "
+ "Trying to reconnect...",
+ e,
)
finally:
self.queues.remove(queue)
@@ -247,7 +248,7 @@ async def _ws_recv(self, websocket: WebSocket) -> None:
if "echo" in data:
echo = data["echo"]
resp = await self._call_api(data)
- # 格式错误(包括实现不支持 MessagePack 的情况)、必要字段缺失或字段类型错误
+ # 格式错误(包括实现不支持 MessagePack 的情况)、必要字段缺失或类型错误
except (json.JSONDecodeError, msgpack.UnpackException):
resp = {
"status": "failed",
@@ -267,11 +268,13 @@ async def _ws_recv(self, websocket: WebSocket) -> None:
resp["echo"] = echo
await websocket.send(encode_data(resp, isinstance(raw_data, bytes)))
except WebSocketClosed:
- logger.opt(colors=True).warning("WebSocket closed by peer")
+ log("WARNING", "WebSocket closed by peer")
# 与 WebSocket 服务器的连接发生了意料之外的错误
- except Exception:
- logger.opt(colors=True).exception(
- "Error while process data from websocket"
+ except Exception as e:
+ log(
+ "ERROR",
+ "Error while process data from websocket",
+ e,
)
async def _handle_http(
@@ -283,7 +286,7 @@ async def _handle_http(
if response := self._check_access_token(request, conn.access_token):
return response
- # 如果收到不支持的 Content-Type 请求头,必须返回 HTTP 状态码 415 Unsupported Media Type
+ # 如果收到不支持的 Content-Type 请求头,必须返回 HTTP 状态码 415
content_type = request.headers.get("Content-Type")
if content_type not in ("application/json", "application/msgpack"):
return Response(415, content="Invalid Content-Type")
@@ -341,6 +344,19 @@ async def _handle_ws(self, conn: WebsocketConfig, websocket: WebSocket) -> None:
conn.use_msgpack,
)
)
+ await websocket.send(
+ encode_data(
+ StatusUpdateMetaEvent(
+ id=uuid.uuid4().hex,
+ time=datetime.now(),
+ type="meta",
+ detail_type="status_update",
+ sub_type="",
+ status=await self.get_status(),
+ ).model_dump(),
+ conn.use_msgpack,
+ )
+ )
t1 = create_task(self._ws_send(websocket, conn))
t2 = create_task(self._ws_recv(websocket))
await t2
@@ -351,14 +367,24 @@ async def _http_webhook(self, conn: HTTPWebhookConfig):
"Content-Type": (
"application/msgpack" if conn.use_msgpack else "application/json"
),
- "User-Agent": "OneBot/12 NoneBot Plugin All4One/0.1.0",
- "X-OneBot-Version": "12",
- "X-Impl": "nonebot-plugin-all4one",
+ "User-Agent": self.USER_AGENT,
+ "X-OneBot-Version": self.ONEBOT_VERSION,
+ "X-Impl": self.IMPL_NAME,
}
if conn.access_token:
headers["Authorization"] = f"Bearer {conn.access_token}"
- queue = Queue()
+ queue = Queue[Event]()
self.queues.append(queue)
+ await queue.put(
+ StatusUpdateMetaEvent(
+ id=uuid.uuid4().hex,
+ time=datetime.now(),
+ type="meta",
+ detail_type="status_update",
+ sub_type="",
+ status=await self.get_status(),
+ )
+ )
while True:
try:
event = await queue.get()
@@ -366,7 +392,7 @@ async def _http_webhook(self, conn: HTTPWebhookConfig):
"POST",
str(conn.url),
headers=headers,
- content=encode_data(event.dict(), conn.use_msgpack),
+ content=encode_data(event.model_dump(), conn.use_msgpack),
)
resp = await self.request(request)
if resp.status_code == 200:
@@ -380,32 +406,33 @@ async def _http_webhook(self, conn: HTTPWebhookConfig):
elif content_type == "application/json":
data = json.loads(resp.content)
else:
- logger.error("Invalid Content-Type")
+ log("ERROR", "Invalid Content-Type")
continue
for action in data:
await self._call_api(action)
# 动作请求执行出错
- except Exception:
- logger.exception("HTTP Webhook Response action failed")
+ except Exception as e:
+ log("ERROR", "HTTP Webhook Response action failed", e)
# 事件推送成功,并不做更多处理
elif resp.status_code == 204:
pass
# 事件推送失败
else:
- logger.error(f"HTTP Webhook event push failed: {resp}")
+ log("ERROR", f"HTTP Webhook event push failed: {resp}")
except (NotImplementedError, TypeError):
- logger.error(
- f"Current driver {self.driver.type} does not support http client"
+ log(
+ "ERROR",
+ f"Current driver {self.driver.type} does not support http client",
)
self.queues.remove(queue)
break
- except Exception:
- logger.exception("HTTP Webhook event push failed")
+ except Exception as e:
+ log("ERROR", "HTTP Webhook event push failed", e)
async def _websocket_rev(self, conn: WebsocketReverseConfig) -> None:
headers = {
- "User-Agent": "OneBot/12 NoneBot Plugin All4One/0.1.0",
- "Sec-WebSocket-Protocol": "12.nonebot-plugin-all4one",
+ "User-Agent": self.USER_AGENT,
+ "Sec-WebSocket-Protocol": f"{self.ONEBOT_VERSION}.{self.IMPL_NAME}",
}
if conn.access_token:
headers["Authorization"] = f"Bearer {conn.access_token}"
@@ -432,28 +459,44 @@ async def _websocket_rev(self, conn: WebsocketReverseConfig) -> None:
conn.use_msgpack,
)
)
+ await ws.send(
+ encode_data(
+ StatusUpdateMetaEvent(
+ id=uuid.uuid4().hex,
+ time=datetime.now(),
+ type="meta",
+ detail_type="status_update",
+ sub_type="",
+ status=await self.get_status(),
+ ).model_dump(),
+ conn.use_msgpack,
+ )
+ )
t1 = create_task(self._ws_send(ws, conn))
t2 = create_task(self._ws_recv(ws))
await t2
t1.cancel()
except WebSocketClosed:
- logger.opt(colors=True).warning(
- "WebSocket Closed"
- )
- except Exception:
- logger.opt(colors=True).exception(
- "Error while process data from websocket"
- f"{escape_tag(str(conn.url))}. Trying to reconnect...",
+ log("WARNING", "WebSocket Closed")
+ except Exception as e:
+ log(
+ "ERROR",
+ "Error while process data from websocket"
+ f"{escape_tag(str(conn.url))}. Trying to reconnect...",
+ e,
)
except (NotImplementedError, TypeError):
- logger.error(
- f"Current driver {self.driver.type} does not support websocket server"
+ log(
+ "ERROR",
+ f"Current driver {self.driver.type} "
+ "does not support websocket server",
)
break
except Exception:
- logger.opt(colors=True).warning(
- "Error while setup websocket to "
- f"{escape_tag(str(conn.url))}. Trying to reconnect...",
+ log(
+ "WARNING",
+ "Error while setup websocket to "
+ f"{escape_tag(str(conn.url))}. Trying to reconnect...",
)
await sleep(conn.reconnect_interval)
@@ -502,7 +545,7 @@ def _register_middlewares(self, middlewares: Optional[set[str]] = None):
if middleware in MIDDLEWARE_MAP:
self.register_middleware(MIDDLEWARE_MAP[middleware])
else:
- logger.error(f"Can not find middleware for Adapter {middleware}")
+ log("ERROR", f"Can not find middleware for Adapter {middleware}")
def setup(self):
@self.driver.on_startup
@@ -512,7 +555,7 @@ async def _():
if isinstance(conn, HTTPConfig):
queue = None
if conn.event_enabled:
- queue = Queue(conn.event_buffer_size)
+ queue = Queue[Event](conn.event_buffer_size)
self.setup_http_server(
HTTPServerSetup(
URL("/all4one/"),