diff --git a/docs/api/exception.md b/docs/api/exception.md
index bf281b11..984aa80c 100644
--- a/docs/api/exception.md
+++ b/docs/api/exception.md
@@ -43,11 +43,22 @@ sidebarDepth: 0
-## _exception_ `IgnoredException`
+## _exception_ `ProcessException`
基类:`nonebot.exception.NoneBotException`
+* **说明**
+
+ 事件处理过程中发生的异常基类。
+
+
+
+## _exception_ `IgnoredException`
+
+基类:`nonebot.exception.ProcessException`
+
+
* **说明**
指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。
@@ -61,9 +72,27 @@ sidebarDepth: 0
+## _exception_ `MockApiException`
+
+基类:`nonebot.exception.ProcessException`
+
+
+* **说明**
+
+ 指示 NoneBot 阻止本次 API 调用或修改本次调用返回值,并返回自定义内容。可由 api hook 抛出。
+
+
+
+* **参数**
+
+
+ * `result`: 返回的内容
+
+
+
## _exception_ `StopPropagation`
-基类:`nonebot.exception.NoneBotException`
+基类:`nonebot.exception.ProcessException`
* **说明**
diff --git a/nonebot/adapters/_bot.py b/nonebot/adapters/_bot.py
index 6ed7b2b2..5e3aec7f 100644
--- a/nonebot/adapters/_bot.py
+++ b/nonebot/adapters/_bot.py
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Set, Tuple, Union, Optional
from nonebot.log import logger
from nonebot.config import Config
+from nonebot.exception import MockApiException
from nonebot.typing import T_CalledAPIHook, T_CallingAPIHook
from nonebot.drivers import Driver, HTTPResponse, HTTPConnection
@@ -137,24 +138,33 @@ class Bot(abc.ABC):
await bot.call_api("send_msg", message="hello world")
await bot.send_msg(message="hello world")
"""
+
+ result: Any = None
+ skip_calling_api: bool = False
+ exception: Optional[Exception] = None
+
coros = list(map(lambda x: x(self, api, data), self._calling_api_hook))
if coros:
try:
logger.debug("Running CallingAPI hooks...")
await asyncio.gather(*coros)
+ except MockApiException as e:
+ skip_calling_api = True
+ result = e.result
+ logger.debug(
+ f"Calling API {api} is cancelled. Return {result} instead."
+ )
except Exception as e:
logger.opt(colors=True, exception=e).error(
"Error when running CallingAPI hook. "
"Running cancelled!"
)
- exception = None
- result = None
-
- try:
- result = await self._call_api(api, **data)
- except Exception as e:
- exception = e
+ if not skip_calling_api:
+ try:
+ result = await self._call_api(api, **data)
+ except Exception as e:
+ exception = e
coros = list(
map(lambda x: x(self, exception, api, data, result), self._called_api_hook)
@@ -163,6 +173,11 @@ class Bot(abc.ABC):
try:
logger.debug("Running CalledAPI hooks...")
await asyncio.gather(*coros)
+ except MockApiException as e:
+ result = e.result
+ logger.debug(
+ f"Calling API {api} result is mocked. Return {result} instead."
+ )
except Exception as e:
logger.opt(colors=True, exception=e).error(
"Error when running CalledAPI hook. "
diff --git a/nonebot/exception.py b/nonebot/exception.py
index e602ee93..193023b7 100644
--- a/nonebot/exception.py
+++ b/nonebot/exception.py
@@ -6,7 +6,7 @@
这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。
"""
-from typing import Optional
+from typing import Any, Optional
class NoneBotException(Exception):
@@ -42,7 +42,15 @@ class ParserExit(NoneBotException):
# Processor Exception
-class IgnoredException(NoneBotException):
+class ProcessException(NoneBotException):
+ """
+ :说明:
+
+ 事件处理过程中发生的异常基类。
+ """
+
+
+class IgnoredException(ProcessException):
"""
:说明:
@@ -63,7 +71,28 @@ class IgnoredException(NoneBotException):
return self.__repr__()
-class StopPropagation(NoneBotException):
+class MockApiException(ProcessException):
+ """
+ :说明:
+
+ 指示 NoneBot 阻止本次 API 调用或修改本次调用返回值,并返回自定义内容。可由 api hook 抛出。
+
+ :参数:
+
+ * ``result``: 返回的内容
+ """
+
+ def __init__(self, result: Any):
+ self.result = result
+
+ def __repr__(self):
+ return f""
+
+ def __str__(self):
+ return self.__repr__()
+
+
+class StopPropagation(ProcessException):
"""
:说明: