Skip to content

Commit f0cf191

Browse files
authored
Merge pull request #78 from MuRainBot/dev
合并Dev: 重构QQRichText,CommandManager新增自定义异常处理,新增SessionManager,优化一些东西,修复一些bug
2 parents 8d7c83c + b58235a commit f0cf191

24 files changed

+2462
-2118
lines changed

config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ qq_data_cache: # QQ数据缓存设置
2626

2727
debug: # 调试模式,若启用框架的日志等级将被设置为debug,不建议在生产环境开启
2828
enable: false # 是否启用调试模式
29-
save_dump: true # 是否在发生异常的同时保存一个dump错误文件(不受debug.enable约束,独立开关,若要使用请先安装coredumpy库,不使用可不安装)
29+
save_dump: false # 是否在发生异常的同时保存一个dump错误文件(不受debug.enable约束,独立开关,若要使用请先安装coredumpy库,不使用可不安装)
3030

3131
auto_restart_onebot: # 在Onebot实现端状态异常时自动重启Onebot实现端(需开启心跳包)
3232
enable: true # 是否启用自动重启

murainbot/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
if TYPE_CHECKING:
88
from .utils import *
99
from . import common
10+
from . import core
1011
else:
1112
def __getattr__(name: str):
1213
"""当访问 murainbot.name 时,按需导入并返回。"""
1314
if name in __all__:
1415
module_name = f".utils.{name}"
1516
if name == "common":
1617
module_name = ".common"
18+
elif name.startswith("core."):
19+
module_name = f".{name}"
20+
# print(module_name)
1721
module = importlib.import_module(module_name, __name__)
1822
return module
1923

@@ -37,5 +41,7 @@ def __dir__():
3741
"QQDataCacher",
3842
"StateManager",
3943
"TimerManager",
40-
"common"
44+
"SessionManager",
45+
"common",
46+
"core"
4147
]

murainbot/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import shutil
22
from pathlib import Path
33
import typer
4-
from typing_extensions import Annotated
4+
from typing import Annotated
55
from rich import print
66

77
app = typer.Typer(

murainbot/common.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import requests
1818

19+
from .core import ConfigManager
1920
from .paths import paths
2021
from .utils import Logger
2122

@@ -333,6 +334,27 @@ def save_exc_dump(description: str = None, path: str = None):
333334
return kwargs["path"]
334335

335336

337+
def exc_logger(e: Exception, message: str = None):
338+
"""
339+
记录异常日志并保存dump
340+
Args:
341+
e: 异常
342+
message: 描述
343+
344+
Returns:
345+
None
346+
"""
347+
if ConfigManager.GlobalConfig().debug.save_dump:
348+
dump_path = save_exc_dump(message)
349+
else:
350+
dump_path = None
351+
logger.error(
352+
f"{message}: {repr(e)}"
353+
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
354+
exc_info=True
355+
)
356+
357+
336358
def bytes_io_to_file(
337359
io_bytes: BytesIO,
338360
file_name: str | None = None,

murainbot/core/ConfigManager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def init(self):
218218
)
219219
self.debug = self.Debug(
220220
enable=self.get("debug", {}).get("enable", False),
221-
save_dump=self.get("debug", {}).get("save_dump", True)
221+
save_dump=self.get("debug", {}).get("save_dump", False)
222222
)
223223
self.auto_restart_onebot = self.AutoRestartOnebot(
224224
enable=self.get("auto_restart_onebot", {}).get("enable", True)

murainbot/core/EventManager.py

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
事件管理器,用于管理事件与事件监听器
33
"""
44
import inspect
5+
import threading
56
from collections.abc import Callable
67
from dataclasses import dataclass, field
78
from typing import Any, TypeVar
89

9-
from murainbot.common import save_exc_dump
10-
from murainbot.core import ConfigManager
10+
from murainbot.common import exc_logger
1111
from murainbot.core.ThreadPool import async_task
1212
from murainbot.utils import Logger
1313

1414
logger = Logger.get_logger()
1515

16+
_event_lock = threading.Lock()
17+
1618

1719
class _Event:
1820
"""
@@ -34,30 +36,28 @@ def call(self):
3436
"""
3537
按优先级顺序同步触发所有监听器
3638
"""
37-
if self.__class__ in event_listeners:
38-
for listener in sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True):
39-
try:
40-
res = listener.func(self, **listener.kwargs)
41-
except Exception as e:
42-
if ConfigManager.GlobalConfig().debug.save_dump:
43-
dump_path = save_exc_dump(f"监听器中发生错误")
44-
else:
45-
dump_path = None
46-
logger.error(f"监听器中发生错误: {repr(e)}"
47-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
48-
exc_info=True)
49-
continue
50-
if res is True:
51-
return True
52-
return False
53-
return None
39+
with _event_lock:
40+
if self.__class__ not in event_listeners:
41+
return None
42+
43+
listeners_list = sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True)
44+
45+
for listener in listeners_list:
46+
try:
47+
res = listener.func(self, **listener.kwargs)
48+
except Exception as e:
49+
exc_logger(e, "监听器中发生错误")
50+
continue
51+
if res is True:
52+
return True
53+
return False
5454

5555

5656
T = TypeVar('T', bound='_Event')
5757

5858

59-
# 定义事件监听器的数据类
60-
@dataclass(order=True)
59+
# 定义事件监听器的数据类,使用frozen是为了保证数据是只读的来避免可能的线程安全问题
60+
@dataclass(order=True, frozen=True)
6161
class EventListener:
6262
"""
6363
事件监听器数据类
@@ -91,7 +91,8 @@ def event_listener(event_class: type[T], priority: int = 0, **kwargs):
9191
def wrapper(func: Callable[[T, ...], Any]):
9292
# 注册事件监听器
9393
listener = EventListener(priority=priority, func=func, kwargs=kwargs)
94-
event_listeners.setdefault(event_class, []).append(listener)
94+
with _event_lock:
95+
event_listeners.setdefault(event_class, []).append(listener)
9596
return func
9697

9798
return wrapper
@@ -109,26 +110,25 @@ def unregister_listener(event_class: type[T], func: Callable[[T, ...], Any]):
109110
if not issubclass(event_class, _Event):
110111
raise TypeError("event_class 类必须是 _Event 的子类")
111112

112-
listeners_list = event_listeners.get(event_class)
113+
with _event_lock:
114+
listeners_list = event_listeners.get(event_class)
113115

114-
if not listeners_list:
115-
raise ValueError(f"事件类型 {event_class.__name__} 没有已注册的监听器。")
116+
if not listeners_list:
117+
raise ValueError(f"事件类型 {event_class.__name__} 没有已注册的监听器。")
116118

117-
# 查找所有与给定函数匹配的监听器对象
118-
listeners_to_remove = [listener for listener in listeners_list if listener.func == func]
119+
# 查找所有与给定函数匹配的监听器对象
120+
listeners_to_remove = [listener for listener in listeners_list if listener.func == func]
119121

120-
if not listeners_to_remove:
121-
# 如果没有找到匹配的函数
122-
raise ValueError(f"未找到函数 {func.__name__} 对应的监听器,无法为事件 {event_class.__name__} 注销。")
122+
if not listeners_to_remove:
123+
# 如果没有找到匹配的函数
124+
raise ValueError(f"未找到函数 {func.__name__} 对应的监听器,无法为事件 {event_class.__name__} 注销。")
123125

124-
# 移除所有找到的监听器
125-
removed_count = 0
126-
for listener_obj in listeners_to_remove:
127-
listeners_list.remove(listener_obj)
128-
removed_count += 1
126+
# 移除所有找到的监听器
127+
for listener_obj in listeners_to_remove:
128+
listeners_list.remove(listener_obj)
129129

130-
if not listeners_list:
131-
del event_listeners[event_class]
130+
if not listeners_list:
131+
del event_listeners[event_class]
132132

133133

134134
class Event(_Event):
@@ -139,28 +139,29 @@ class Event(_Event):
139139
def _call_hook(self, listener: EventListener):
140140
return Hook(self, listener).call()
141141

142-
def call(self):
142+
def call(self) -> list[tuple[EventListener, Any]] | None:
143143
"""
144144
按优先级顺序同步触发所有监听器
145145
"""
146-
if self.__class__ in event_listeners:
147-
res_list = []
148-
for listener in sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True):
149-
if self._call_hook(listener):
150-
logger.debug(f"由 Hook 跳过监听器: {listener.func.__name__}")
151-
continue
152-
try:
153-
res = listener.func(self, **listener.kwargs)
154-
except Exception as e:
155-
if ConfigManager.GlobalConfig().debug.save_dump:
156-
dump_path = save_exc_dump(f"监听器中发生错误")
157-
else:
158-
dump_path = None
159-
logger.error(f"监听器中发生错误: {repr(e)}"
160-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
161-
exc_info=True)
162-
continue
163-
res_list.append(res)
146+
with _event_lock:
147+
if self.__class__ not in event_listeners:
148+
return None
149+
150+
listeners_list = sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True)
151+
152+
results = []
153+
for listener in listeners_list:
154+
if self._call_hook(listener):
155+
logger.debug(f"由 Hook 跳过监听器: {listener.func.__name__}")
156+
continue
157+
try:
158+
res = listener.func(self, **listener.kwargs)
159+
except Exception as e:
160+
exc_logger(e, "监听器中发生错误")
161+
continue
162+
results.append((listener, res))
163+
164+
return results
164165

165166
@async_task
166167
def call_async(self):

murainbot/core/OnebotAPI.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
import json
1313
import traceback
1414
import urllib.parse
15+
1516
import requests
1617

1718
from . import EventManager, ConfigManager
18-
from ..common import save_exc_dump
19+
from ..common import exc_logger
1920
from ..utils import Logger
2021

2122
logger = Logger.get_logger()
@@ -123,15 +124,7 @@ def get(self, node, data: dict = None, original: bool = None):
123124
else:
124125
return response.json()['data']
125126
except Exception as e:
126-
if ConfigManager.GlobalConfig().debug.save_dump and creat_dump:
127-
dump_path = save_exc_dump(f"调用 API: {node} data: {data} 异常")
128-
else:
129-
dump_path = None
130-
logger.error(
131-
f"调用 API: {node} data: {data} 异常: {repr(e)}"
132-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
133-
exc_info=True
134-
)
127+
exc_logger(e, f"调用 API: {node} data: {data} 异常")
135128
raise e
136129

137130
def send_private_msg(self, user_id: int, message: str | list[dict]):

murainbot/core/PluginManager.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from types import ModuleType
1111
from typing import TypedDict
1212

13-
from murainbot.common import save_exc_dump
13+
from murainbot.common import exc_logger
1414
from murainbot.paths import paths
1515
from murainbot.core import ConfigManager
1616
from murainbot.utils.Logger import get_logger
@@ -158,13 +158,7 @@ def load_plugins():
158158
logger.warning(f"插件 {name}({full_path}) 已被禁用,将不会被加载")
159159
continue
160160
except Exception as e:
161-
if ConfigManager.GlobalConfig().debug.save_dump:
162-
dump_path = save_exc_dump(f"尝试加载插件 {full_path} 时失败")
163-
else:
164-
dump_path = None
165-
logger.error(f"尝试加载插件 {full_path} 时失败! 原因:{repr(e)}"
166-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
167-
exc_info=True)
161+
exc_logger(e, f"尝试加载插件 {full_path} 时失败")
168162
continue
169163

170164
logger.debug(f"插件 {name}({full_path}) 加载成功!")
@@ -188,17 +182,12 @@ def requirement_plugin(plugin_name: str):
188182
plugin["info"] = plugin_info
189183
plugin["plugin"] = module
190184
plugins.append(plugin)
191-
except NotEnabledPluginException:
192-
logger.error(f"被依赖的插件 {plugin_name} 已被禁用,无法加载依赖")
193-
raise Exception(f"被依赖的插件 {plugin_name} 已被禁用,无法加载依赖")
185+
except NotEnabledPluginException as e:
186+
msg = f"被依赖的插件 {plugin_name} 已被禁用,无法加载依赖"
187+
logger.error(msg)
188+
raise Exception(msg) from e
194189
except Exception as e:
195-
if ConfigManager.GlobalConfig().debug.save_dump:
196-
dump_path = save_exc_dump(f"尝试加载被依赖的插件 {plugin_name} 时失败!")
197-
else:
198-
dump_path = None
199-
logger.error(f"尝试加载被依赖的插件 {plugin_name} 时失败! 原因:{repr(e)}"
200-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
201-
exc_info=True)
190+
exc_logger(e, f"尝试加载被依赖的插件 {plugin_name} 时失败")
202191
raise e
203192
logger.debug(f"由于插件依赖,插件 {plugin_name} 加载成功!")
204193
else:

murainbot/core/ThreadPool.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
import atexit
77
from concurrent.futures import ThreadPoolExecutor
88

9-
from murainbot.common import save_exc_dump
10-
from murainbot.core import ConfigManager
9+
from murainbot.common import exc_logger
1110
from murainbot.core.ConfigManager import GlobalConfig
1211
from murainbot.utils.Logger import get_logger
1312

@@ -41,16 +40,7 @@ def _wrapper(func, *args, **kwargs):
4140
try:
4241
return func(*args, **kwargs)
4342
except Exception as e:
44-
if ConfigManager.GlobalConfig().debug.save_dump:
45-
dump_path = save_exc_dump(f"Error in async task({func.__module__}.{func.__name__})")
46-
else:
47-
dump_path = None
48-
# 打印到日志中
49-
logger.error(
50-
f"Error in async task({func.__module__}.{func.__name__}): {repr(e)}"
51-
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
52-
exc_info=True
53-
)
43+
exc_logger(e, f"Error in async task({func.__module__}.{func.__name__})")
5444
return None
5545

5646

0 commit comments

Comments
 (0)