Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3ff06e0
fix: 修改state的state_id生成逻辑
xiaosuyyds Apr 19, 2025
7f2e813
refactor(Logger): 重构日志记录器并优化异常日志记录
xiaosuyyds Apr 23, 2025
1ca3ef8
fix(Logger): 修复除了murainbot本身以外的全部日志都不会写入日志文件的问题
xiaosuyyds Apr 24, 2025
7d1908d
fix(Logger): 修复插件日志命名
xiaosuyyds Apr 24, 2025
2f1ad2a
feat(EventManager): 优化事件管理器并添加监听器注销功能
xiaosuyyds Apr 24, 2025
6b7f508
style: 统一unregister_listener与event_listener的event_class类型检查
xiaosuyyds Apr 24, 2025
bc82efe
去掉banner打印时的time.sleep,加快启动速度
xiaosuyyds Apr 25, 2025
5196289
fix(utils): 修复to_me rule检测逻辑
xiaosuyyds Apr 25, 2025
ef59787
feat(QQRichText): 为array_2_cq与cq_2_array添加更为详细的异常数据的检查和处理,并优化报错返回内容
xiaosuyyds Apr 26, 2025
af78b85
fix(utils): 修复 QQRichText 中的值类型问题并优化错误提示
xiaosuyyds Apr 27, 2025
774fcd9
fix(Helper): 修复帮助插件无法正确使用的问题,并支持私聊使用
xiaosuyyds May 1, 2025
5e30f63
refactor(QQRichText): 重构消息段解析,统一id和type字段的写法,修改类型注解
xiaosuyyds May 11, 2025
cb42c8e
feat(utils): 为 Rule 类添加逻辑运算符重载
xiaosuyyds May 11, 2025
fe09521
fix:修复_at_事件中的QQ号类型错误
xiaosuyyds May 19, 2025
347065c
feat(EventManager): 在EventManager也保存dump文件
xiaosuyyds May 19, 2025
ccd79f3
docs(README): 更新项目描述、问题反馈和术语
xiaosuyyds May 19, 2025
750ebb1
fix(QQRichText): 修复在QQRichText输入QQRichText会导致报错的问题,并整理导入逻辑,移除不必要的判断
xiaosuyyds May 21, 2025
a090ca7
fix(QQRichText): 修复在QQRichText输入QQRichText会导致报错的问题(再一次)
xiaosuyyds May 21, 2025
940b356
feat(common): 增加异常堆栈自动归档功能并优化时间格式
xiaosuyyds May 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions Lib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import sys
import threading
import time
import traceback
import uuid
from collections import OrderedDict
from io import BytesIO
Expand Down Expand Up @@ -222,14 +221,32 @@ def save_exc_dump(description: str = None, path: str = None):
description: 保存的dump描述,为空则默认
path: 保存的路径,为空则自动根据错误生成
"""
# 扫描是否存在非当前日期且为归档的exc_dump
exc_dump_files = [
file for file in os.listdir(DUMPS_PATH) if file.startswith("coredumpy_") and file.endswith(".dump")
]

today_date = time.strftime("%Y%m%d")
date_flags = []

for file in exc_dump_files:
file_date = file.split("coredumpy_", 1)[1].split("_", 1)[0][:len("YYYYMMDD")]
if file_date != today_date:
os.makedirs(os.path.join(DUMPS_PATH, f"coredumpy_archive_{file_date}"), exist_ok=True)
os.rename(os.path.join(DUMPS_PATH, file), os.path.join(DUMPS_PATH, f"coredumpy_archive_{file_date}", file))
if file_date not in date_flags:
logger.info(f"已自动归档 {file_date} 的异常堆栈到 coredumpy_archive_{file_date}")
date_flags.append(file_date)

# 保存dump文件
try:
import coredumpy
except ImportError:
logger.warning("coredumpy未安装,无法保存异常堆栈")
return
return None

try:
exc_type, exc_value, exc_traceback = sys.exc_info()
_, _, exc_traceback = sys.exc_info()
if not exc_traceback:
raise Exception("No traceback found")

Expand All @@ -245,12 +262,12 @@ def save_exc_dump(description: str = None, path: str = None):
if i > 0:
path_ = os.path.join(DUMPS_PATH,
f"coredumpy_"
f"{time.strftime('%Y%m%d%H%M%S')}_"
f"{time.strftime('%Y%m%d%-H%M%S')}_"
f"{frame.f_code.co_name}_{i}.dump")
else:
path_ = os.path.join(DUMPS_PATH,
f"coredumpy_"
f"{time.strftime('%Y%m%d%H%M%S')}_"
f"{time.strftime('%Y%m%d-%H%M%S')}_"
f"{frame.f_code.co_name}.dump")
if not os.path.exists(path_):
break
Expand All @@ -270,8 +287,7 @@ def save_exc_dump(description: str = None, path: str = None):

coredumpy.dump(**kwargs)
except Exception as e:
logger.error(f"保存异常堆栈时发生错误: {repr(e)}\n"
f"{traceback.format_exc()}")
logger.error(f"保存异常堆栈时发生错误: {repr(e)}", exc_info=True)
return None

return kwargs["path"]
Expand Down
84 changes: 63 additions & 21 deletions Lib/core/EventManager.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""
事件管理器,用于管理事件与事件监听器
"""
import traceback
from typing import Any, TypeVar

import inspect
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import Any, TypeVar

from Lib.core.ThreadPool import async_task
from Lib.core import ConfigManager
from Lib.utils import Logger
import inspect
from Lib.common import save_exc_dump

logger = Logger.get_logger()

Expand All @@ -26,6 +25,7 @@ class Hook(_Event):
"""
钩子事件,用于在事件处理过程中跳过某些监听器
"""

def __init__(self, event, listener):
self.event = event
self.listener = listener
Expand All @@ -36,17 +36,21 @@ def call(self):
"""
if self.__class__ in event_listeners:
for listener in sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True):
if not ConfigManager.GlobalConfig().debug.enable:
try:
res = listener.func(self, **listener.kwargs)
except Exception as e:
logger.error(f"Error occurred in listener: {repr(e)}\n{traceback.format_exc()}")
continue
else:
try:
res = listener.func(self, **listener.kwargs)
except Exception as e:
if ConfigManager.GlobalConfig().debug.save_dump:
dump_path = save_exc_dump(f"监听器中发生错误")
else:
dump_path = None
logger.error(f"监听器中发生错误: {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
continue
if res is True:
return True
return False
return None


T = TypeVar('T', bound='_Event')
Expand All @@ -64,7 +68,7 @@ class EventListener:

def __post_init__(self):
# 确保监听器函数至少有一个参数
assert len(inspect.signature(self.func).parameters) >= 1, "The listener takes at least 1 parameter"
assert len(inspect.signature(self.func).parameters) >= 1, "监听器至少接受 1 个参数"


# 定义监听器的类型和存储
Expand All @@ -81,7 +85,8 @@ def event_listener(event_class: type[T], priority: int = 0, **kwargs):
priority: 优先级,默认为0
**kwargs: 附加参数
"""
assert issubclass(event_class, _Event), "Event class must be a subclass of Event"
if not issubclass(event_class, _Event):
raise TypeError("event_class 类必须是 _Event 的子类")

def wrapper(func: Callable[[T, ...], Any]):
# 注册事件监听器
Expand All @@ -92,6 +97,40 @@ def wrapper(func: Callable[[T, ...], Any]):
return wrapper


def unregister_listener(event_class: type[T], func: Callable[[T, ...], Any]):
"""
用于取消注册监听器
注意,会删除所有与给定函数匹配的监听器。

Args:
event_class: 事件类型
func: 监听器函数
"""
if not issubclass(event_class, _Event):
raise TypeError("event_class 类必须是 _Event 的子类")

listeners_list = event_listeners.get(event_class)

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

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

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

# 移除所有找到的监听器
removed_count = 0
for listener_obj in listeners_to_remove:
listeners_list.remove(listener_obj)
removed_count += 1

if not listeners_list:
del event_listeners[event_class]


class Event(_Event):
"""
基事件类,所有自定义事件均继承自此类,继承自此类以创建自定义事件
Expand All @@ -108,16 +147,19 @@ def call(self):
res_list = []
for listener in sorted(event_listeners[self.__class__], key=lambda i: i.priority, reverse=True):
if self._call_hook(listener):
logger.debug(f"Skipped listener: {listener.func.__name__}")
logger.debug(f"由 Hook 跳过监听器: {listener.func.__name__}")
continue
if not ConfigManager.GlobalConfig().debug.enable:
try:
res = listener.func(self, **listener.kwargs)
except Exception as e:
logger.error(f"Error occurred in listener: {repr(e)}\n{traceback.format_exc()}")
continue
else:
try:
res = listener.func(self, **listener.kwargs)
except Exception as e:
if ConfigManager.GlobalConfig().debug.save_dump:
dump_path = save_exc_dump(f"监听器中发生错误")
else:
dump_path = None
logger.error(f"监听器中发生错误: {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
continue
res_list.append(res)

@async_task
Expand Down
6 changes: 3 additions & 3 deletions Lib/core/OnebotAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ def get(self, node, data: dict = None, original: bool = None):
else:
dump_path = None
logger.error(
f"调用 API: {node} data: {data} 异常: {repr(e)}\n"
f"{traceback.format_exc()}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}"
f"调用 API: {node} data: {data} 异常: {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True
)
raise e

Expand Down
16 changes: 7 additions & 9 deletions Lib/core/PluginManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import importlib
import inspect
import sys
import traceback

from Lib.common import save_exc_dump
from Lib.constants import *
Expand Down Expand Up @@ -61,8 +60,7 @@ def load_plugin(plugin):
logger.debug(f"尝试加载: {import_path}")
module = importlib.import_module(import_path)
except ImportError as e:
logger.error(f"加载 {import_path} 失败: {repr(e)}\n"
f"{traceback.format_exc()}")
logger.error(f"加载 {import_path} 失败: {repr(e)}", exc_info=True)
raise

plugin_info = None
Expand Down Expand Up @@ -133,9 +131,9 @@ def load_plugins():
dump_path = save_exc_dump(f"尝试加载插件 {full_path} 时失败")
else:
dump_path = None
logger.error(f"尝试加载插件 {full_path} 时失败! 原因:{repr(e)}\n"
f"{"".join(traceback.format_exc())}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}")
logger.error(f"尝试加载插件 {full_path} 时失败! 原因:{repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
continue

logger.debug(f"插件 {name}({full_path}) 加载成功!")
Expand Down Expand Up @@ -188,9 +186,9 @@ def requirement_plugin(plugin_name: str):
dump_path = save_exc_dump(f"尝试加载被依赖的插件 {plugin_name} 时失败!")
else:
dump_path = None
logger.error(f"尝试加载被依赖的插件 {plugin_name} 时失败! 原因:{repr(e)}\n"
f"{"".join(traceback.format_exc())}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}")
logger.error(f"尝试加载被依赖的插件 {plugin_name} 时失败! 原因:{repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
raise e
logger.debug(f"由于插件依赖,插件 {plugin_name} 加载成功!")
else:
Expand Down
14 changes: 6 additions & 8 deletions Lib/core/ThreadPool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
Created by BigCookie233
"""

import sys
import traceback
import atexit
from concurrent.futures import ThreadPoolExecutor

from Lib.common import save_exc_dump
from Lib.core import ConfigManager
from Lib.core.ConfigManager import GlobalConfig
from Lib.utils.Logger import get_logger
from Lib.common import save_exc_dump

import atexit

thread_pool = None
logger = get_logger()
Expand Down Expand Up @@ -50,10 +47,11 @@ def _wrapper(func, *args, **kwargs):
dump_path = None
# 打印到日志中
logger.error(
f"Error in async task({func.__module__}.{func.__name__}): {repr(e)}\n"
f"{"".join(traceback.format_exc())}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}"
f"Error in async task({func.__module__}.{func.__name__}): {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True
)
return None


def async_task(func):
Expand Down
18 changes: 8 additions & 10 deletions Lib/utils/Actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
操作
"""

import traceback

from Lib.common import save_exc_dump
from Lib.core import OnebotAPI, ThreadPool, ConfigManager
from Lib.utils import QQRichText, Logger, QQDataCacher
Expand Down Expand Up @@ -110,8 +108,8 @@ def set_callback(self, callback: Callable[[Result], ...]):
else:
dump_path = None
logger.warning(f"执行回调函数异常: {repr(e)}\n"
f"{traceback.format_exc()}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}")
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
return self

def call(self):
Expand All @@ -133,9 +131,9 @@ def call(self):
dump_path = save_exc_dump(f"调用日志记录函数异常")
else:
dump_path = None
logger.warning(f"调用日志记录函数异常: {repr(e)}\n"
f"{traceback.format_exc()}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}")
logger.warning(f"调用日志记录函数异常: {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
if self.callback is not None:
try:
self.callback(self._result)
Expand All @@ -144,9 +142,9 @@ def call(self):
dump_path = save_exc_dump(f"执行回调函数异常")
else:
dump_path = None
logger.warning(f"回调函数异常: {repr(e)}\n"
f"{traceback.format_exc()}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}")
logger.warning(f"回调函数异常: {repr(e)}"
f"{f"\n已保存异常到 {dump_path}" if dump_path else ""}",
exc_info=True)
return self

def logger(self, *args, **kwargs):
Expand Down
6 changes: 6 additions & 0 deletions Lib/utils/EventClassifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def logger(self):
f"{self.message.render()}"
f"({self.message_id})"
)
return None

elif self.sub_type == "group":
logger.info(
Expand All @@ -182,6 +183,7 @@ def logger(self):
f"{self.message.render()}"
f"({self.message_id})"
)
return None

elif self.sub_type == "other":
logger.info(
Expand All @@ -195,6 +197,7 @@ def logger(self):
f"{self.message.render()}"
f"({self.message_id})"
)
return None

else:
return super().logger()
Expand Down Expand Up @@ -227,6 +230,7 @@ def logger(self):
f"{self.message.render(group_id=self.group_id)}"
f"({self.message_id})"
)
return None

elif self.sub_type == "anonymous":
anonymous_data = self.get('anonymous', {})
Expand All @@ -242,6 +246,7 @@ def logger(self):
f"{self.message.render(group_id=self.group_id)}"
f"({self.message_id})"
)
return None

elif self.sub_type == "notice":
logger.info(
Expand All @@ -252,6 +257,7 @@ def logger(self):
f"{self.message.render(group_id=self.group_id)}"
f"({self.message_id})"
)
return None

else:
return super().logger()
Expand Down
Loading