Skip to content

Commit 0afb434

Browse files
he0119j1g5awi
andauthored
🚧 优化异常处理以符合 OneBot 标准 (#15)
* 🔊 删除 WARNING 日志的 traceback * 🚧 优化异常处理以符合 OneBot 标准 * 💡 添加 OneBot 标准中的报错理由 * 🔊 统一 logger 用法 * 🐛 Fix logger.excepgion without Exception --------- Co-authored-by: jigsaw <[email protected]>
1 parent 7196ed1 commit 0afb434

File tree

1 file changed

+103
-72
lines changed

1 file changed

+103
-72
lines changed

nonebot_plugin_all4one/onebotimpl/__init__.py

Lines changed: 103 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ def register_middleware(self, middleware: Type[Middleware]):
9393
"""注册一个中间件"""
9494
name = middleware.get_name()
9595
if name in self._middlewares:
96-
logger.opt(colors=True).debug(
96+
logger.opt(colors=True).warning(
9797
f'Middleware "<y>{escape_tag(name)}</y>" already exists'
9898
)
9999
return
100100
self._middlewares[name] = middleware
101-
logger.opt(colors=True).debug(
101+
logger.opt(colors=True).info(
102102
f'Succeeded to load middleware "<y>{escape_tag(name)}</y>"'
103103
)
104104

@@ -121,8 +121,6 @@ async def _call_api(self, middleware: Middleware, api: str, **kwargs: Any) -> An
121121
"data": e.data,
122122
"message": e.message,
123123
}
124-
except Exception as e:
125-
logger.debug(e)
126124

127125
async def get_latest_events(
128126
self,
@@ -217,17 +215,13 @@ async def _ws_send(
217215
event = await queue.get()
218216
await websocket.send(encode_data(event.dict(), conn.use_msgpack))
219217
except WebSocketClosed as e:
220-
logger.opt(colors=True, exception=e).log(
221-
"WARNING",
222-
"<y><bg #f8bbd0>WebSocket Closed</bg #f8bbd0></y>",
223-
e,
218+
logger.opt(colors=True).warning(
219+
"<y><bg #f8bbd0>WebSocket Closed</bg #f8bbd0></y>"
224220
)
225221
except Exception as e:
226-
logger.opt(colors=True, exception=e).log(
227-
"ERROR",
222+
logger.opt(colors=True).exception(
228223
"<r><bg #f8bbd0>Error while process data from websocket"
229-
". Trying to reconnect...</bg #f8bbd0></r>",
230-
e,
224+
". Trying to reconnect...</bg #f8bbd0></r>"
231225
)
232226
finally:
233227
middleware.queues.remove(queue)
@@ -236,28 +230,43 @@ async def _ws_recv(self, middleware: Middleware, websocket: WebSocket) -> None:
236230
try:
237231
while True:
238232
raw_data = await websocket.receive()
239-
data = (
240-
json.loads(raw_data)
241-
if isinstance(raw_data, str)
242-
else msgpack.unpackb(raw_data)
243-
)
244-
resp = await self._call_api(
245-
middleware, data["action"], **data["params"]
246-
)
247-
if "echo" in data:
248-
resp["echo"] = data["echo"]
233+
try:
234+
data = (
235+
json.loads(raw_data)
236+
if isinstance(raw_data, str)
237+
else msgpack.unpackb(raw_data)
238+
)
239+
resp = await self._call_api(
240+
middleware, data["action"], **data["params"]
241+
)
242+
if "echo" in data:
243+
resp["echo"] = data["echo"]
244+
# 格式错误(包括实现不支持 MessagePack 的情况)、必要字段缺失或字段类型错误
245+
except (json.JSONDecodeError, msgpack.UnpackException):
246+
resp = {
247+
"status": "failed",
248+
"retcode": 10001,
249+
"data": None,
250+
"message": "Invalid data format",
251+
}
252+
# OneBot 实现内部发生了未捕获的意料之外的异常
253+
except Exception as e:
254+
resp = {
255+
"status": "failed",
256+
"retcode": 20002,
257+
"data": None,
258+
"message": str(e),
259+
}
249260
await websocket.send(encode_data(resp, isinstance(raw_data, str)))
250261
except WebSocketClosed as e:
251-
logger.opt(colors=True, exception=e).log(
252-
"WARNING",
253-
f"WebSocket for Bot {escape_tag(middleware.self_id)} closed by peer",
262+
logger.opt(colors=True).warning(
263+
f"WebSocket for Bot {escape_tag(middleware.self_id)} closed by peer"
254264
)
265+
# 与 WebSocket 服务器的连接发生了意料之外的错误
255266
except Exception as e:
256-
logger.opt(colors=True, exception=e).log(
257-
"ERROR",
267+
logger.opt(colors=True).exception(
258268
"<r><bg #f8bbd0>Error while process data from websocket "
259-
f"for bot {escape_tag(middleware.self_id)}.</bg #f8bbd0></r>",
260-
e,
269+
f"for bot {escape_tag(middleware.self_id)}.</bg #f8bbd0></r>"
261270
)
262271

263272
async def _handle_http(
@@ -269,32 +278,47 @@ async def _handle_http(
269278
) -> Response:
270279
if response := self._check_access_token(request, conn.access_token):
271280
return response
281+
282+
# 如果收到不支持的 Content-Type 请求头,必须返回 HTTP 状态码 415 Unsupported Media Type
283+
content_type = request.headers.get("Content-Type")
284+
if content_type not in ("application/json", "application/msgpack"):
285+
return Response(415, content="Invalid Content-Type")
286+
272287
try:
273-
if request.content:
274-
if (
275-
content_type := request.headers.get("Content-Type")
276-
) == "application/msgpack":
277-
data = msgpack.unpackb(request.content)
278-
elif content_type == "application/json":
279-
data = json.loads(request.content)
280-
else:
281-
return Response(415, content="Invalid Content-Type")
282-
resp = await self._call_api(
283-
middleware,
284-
data["action"],
285-
queue=queue,
286-
**data["params"],
287-
)
288-
if "echo" in data:
289-
resp["echo"] = data["echo"]
290-
return Response(
291-
200,
292-
headers={"Content-Type": content_type},
293-
content=encode_data(resp, content_type != "application/json"),
294-
)
288+
if request.content is None:
289+
raise ValueError("Empty request body")
290+
if content_type == "application/msgpack":
291+
data = msgpack.unpackb(request.content)
292+
else:
293+
data = json.loads(request.content)
294+
295+
resp = await self._call_api(
296+
middleware,
297+
data["action"],
298+
queue=queue,
299+
**data["params"],
300+
)
301+
if "echo" in data:
302+
resp["echo"] = data["echo"]
303+
except (json.JSONDecodeError, msgpack.UnpackException, ValueError):
304+
resp = {
305+
"status": "failed",
306+
"retcode": 10001,
307+
"data": None,
308+
"message": "Invalid data format",
309+
}
295310
except Exception as e:
296-
logger.debug(e)
297-
return Response(204)
311+
resp = {
312+
"status": "failed",
313+
"retcode": 20002,
314+
"data": None,
315+
"message": str(e),
316+
}
317+
return Response(
318+
200,
319+
headers={"Content-Type": content_type},
320+
content=encode_data(resp, content_type != "application/json"),
321+
)
298322

299323
async def _handle_ws(
300324
self, middleware: Middleware, conn: WebsocketConfig, websocket: WebSocket
@@ -382,20 +406,33 @@ async def _http_webhook(self, middleware: Middleware, conn: HTTPWebhookConfig):
382406
)
383407
resp = await self.request(request)
384408
if resp.status_code == 200:
385-
if resp.content:
386-
if resp.headers.get("Content-Type") == "application/msgpack":
409+
try:
410+
if resp.content is None:
411+
raise ValueError("Empty response body")
412+
if (
413+
content_type := resp.headers.get("Content-Type")
414+
) == "application/msgpack":
387415
data = msgpack.unpackb(resp.content)
388-
elif resp.headers.get("Content-Type") == "application/json":
416+
elif content_type == "application/json":
389417
data = json.loads(resp.content)
390418
else:
391-
logger.exception("Invalid Content-Type")
419+
logger.error("Invalid Content-Type")
392420
continue
393421
for action in data:
394422
await self._call_api(
395423
middleware, action["action"], **action["params"]
396424
)
397-
except Exception as e:
398-
logger.debug(e)
425+
# 动作请求执行出错
426+
except Exception:
427+
logger.exception("HTTP Webhook Response action failed")
428+
# 事件推送成功,并不做更多处理
429+
elif resp.status_code == 204:
430+
pass
431+
# 事件推送失败
432+
else:
433+
logger.error(f"HTTP Webhook event push failed: {resp}")
434+
except Exception:
435+
logger.exception("HTTP Webhook event push failed")
399436

400437
async def _websocket_rev(
401438
self, middleware: Middleware, conn: WebsocketReverseConfig
@@ -434,24 +471,18 @@ async def _websocket_rev(
434471
await t2
435472
t1.cancel()
436473
except WebSocketClosed as e:
437-
logger.opt(colors=True, exception=e).log(
438-
"WARNING",
439-
"<y><bg #f8bbd0>WebSocket Closed</bg #f8bbd0></y>",
440-
e,
474+
logger.opt(colors=True).warning(
475+
"<y><bg #f8bbd0>WebSocket Closed</bg #f8bbd0></y>"
441476
)
442477
except Exception as e:
443-
logger.opt(colors=True, exception=e).log(
444-
"ERROR",
478+
logger.opt(colors=True).exception(
445479
"<r><bg #f8bbd0>Error while process data from websocket"
446480
f"{escape_tag(str(conn.url))}. Trying to reconnect...</bg #f8bbd0></r>",
447-
e,
448481
)
449482
except Exception as e:
450-
logger.opt(colors=True).log(
451-
"WARNING",
483+
logger.opt(colors=True).warning(
452484
"<y><bg #f8bbd0>Error while setup websocket to "
453485
f"{escape_tag(str(conn.url))}. Trying to reconnect...</bg #f8bbd0></y>",
454-
e,
455486
)
456487
await asyncio.sleep(conn.reconnect_interval)
457488

@@ -473,9 +504,9 @@ def import_middlewares(self, middlewares: Optional[Set[str]] = None):
473504
)
474505
self.register_middleware(getattr(module, "Middleware"))
475506
else:
476-
logger.warning(f"Can not find middleware for Adapter {middleware}")
477-
except Exception as e:
478-
logger.warning(f"Can not load middleware for Adapter {middleware}: {e}")
507+
logger.error(f"Can not find middleware for Adapter {middleware}")
508+
except Exception:
509+
logger.exception(f"Can not load middleware for Adapter {middleware}:")
479510

480511
def setup(self):
481512
@self.driver.on_startup

0 commit comments

Comments
 (0)