Skip to content

Commit 793d334

Browse files
authored
Merge pull request #75 from MuRainBot/dev
合并Dev:实现会话等待功能,允许通过yield的方式等待自定义内容,发生后再恢复执行的功能,实现TimerManager,统一简单的定时任务,减少开销
2 parents 3cb9075 + 02244d3 commit 793d334

File tree

16 files changed

+741
-182
lines changed

16 files changed

+741
-182
lines changed

.gitignore

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,61 @@
1-
# 默认忽略的文件
2-
/shelf/
3-
/workspace.xml
4-
# 基于编辑器的 HTTP 客户端请求
5-
/httpRequests/
6-
# Datasource local storage ignored files
7-
/dataSources/
8-
/dataSources.local.xml
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
95

6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# Unit test / coverage reports
30+
htmlcov/
31+
.tox/
32+
.nox/
33+
.coverage
34+
.coverage.*
35+
.cache
36+
nosetests.xml
37+
coverage.xml
38+
*.cover
39+
*.py,cover
40+
.hypothesis/
41+
.pytest_cache/
42+
43+
# Environments
44+
.env
45+
.venv
46+
venv/
47+
ENV/
48+
env/
49+
env.bak/
50+
venv.bak/
51+
52+
# IDE / Editor specific
53+
.idea/
54+
.vscode/
55+
*.swp
56+
*~
57+
58+
# MuRainBot
1059
*.log
11-
/.idea
12-
*.pyc
1360
*.dump
14-
test.py
15-
*.zip
16-
/MURainBot2
61+
/test.py

main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""
22
MuRainBot2
33
"""
4+
import os
45

56
from murainbot.main import start
67

7-
start()
8+
start(os.path.dirname(os.path.abspath(__file__)))

murainbot/__init__.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
11
"""
2-
MRB2 Lib 工具模块
2+
MRB2 Lib
33
"""
44
from typing import TYPE_CHECKING
55
import importlib
66

77
if TYPE_CHECKING:
88
from .utils import *
99
from . import common
10+
else:
11+
def __getattr__(name: str):
12+
"""当访问 murainbot.name 时,按需导入并返回。"""
13+
if name in __all__:
14+
module_name = f".utils.{name}"
15+
if name == "common":
16+
module_name = ".common"
17+
module = importlib.import_module(module_name, __name__)
18+
return module
19+
20+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
21+
22+
23+
def __dir__():
24+
"""让 dir(murainbot) 和 tab 自动补全能正常工作。"""
25+
return list(__all__)
26+
1027

1128
__all__ = [
1229
"Logger",
@@ -19,22 +36,6 @@
1936
"PluginConfig",
2037
"QQDataCacher",
2138
"StateManager",
39+
"TimerManager",
2240
"common"
2341
]
24-
25-
26-
def __getattr__(name: str):
27-
"""当访问 murainbot.name 时,按需导入并返回。"""
28-
if name in __all__:
29-
module_name = f".utils.{name}"
30-
if name == "common":
31-
module_name = ".common"
32-
module = importlib.import_module(module_name, __name__)
33-
return module
34-
35-
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
36-
37-
38-
def __dir__():
39-
"""让 dir(murainbot) 和 tab 自动补全能正常工作。"""
40-
return list(__all__)

murainbot/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
.Python
2626
env/
2727
venv/
28+
.venv/
2829
.env
2930
3031
# MuRainBot
31-
/data/
32+
/data/cache/
3233
/logs/
3334
/exc_dumps/
3435
*.db

murainbot/core/EventManager.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from dataclasses import dataclass, field
77
from typing import Any, TypeVar
88

9-
from murainbot.core.ThreadPool import async_task
9+
from murainbot.common import save_exc_dump
1010
from murainbot.core import ConfigManager
11+
from murainbot.core.ThreadPool import async_task
1112
from murainbot.utils import Logger
12-
from murainbot.common import save_exc_dump
1313

1414
logger = Logger.get_logger()
1515

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

29-
def __init__(self, event, listener):
29+
def __init__(self, event: "Event", listener: "EventListener"):
3030
self.event = event
3131
self.listener = listener
3232

@@ -136,7 +136,7 @@ class Event(_Event):
136136
基事件类,所有自定义事件均继承自此类,继承自此类以创建自定义事件
137137
"""
138138

139-
def _call_hook(self, listener):
139+
def _call_hook(self, listener: EventListener):
140140
return Hook(self, listener).call()
141141

142142
def call(self):
@@ -167,7 +167,7 @@ def call_async(self):
167167
"""
168168
无需等待的异步按优先级顺序触发所有监听器
169169
"""
170-
self.call()
170+
return self.call()
171171

172172

173173
if __name__ == "__main__":

murainbot/main.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ def get_gradient(start_color: tuple[int, int, int], end_color: tuple[int, int, i
4141
)
4242

4343

44-
def start(work_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))):
44+
def start(work_path=None):
4545
"""
4646
启动MRB2
4747
Args:
48-
work_path: MRB2实例的工作目录,默认为安装目录,谨慎填写
48+
work_path: MRB2实例的工作目录,默认为os.getcwd(),谨慎填写
4949
"""
50+
if work_path is None:
51+
work_path = os.getcwd()
5052
paths.init_paths(work_path)
5153
paths.paths.ensure_all_dirs_exist()
5254

@@ -87,7 +89,7 @@ def start(work_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8789
from . import common
8890
atexit.register(common.finalize_and_cleanup)
8991

90-
from .utils import AutoRestartOnebot
92+
from .utils import AutoRestartOnebot, TimerManager
9193

9294
Logger.set_logger_level(logging.DEBUG if ConfigManager.GlobalConfig().debug.enable else logging.INFO)
9395
live.stop()
@@ -130,7 +132,8 @@ def start(work_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
130132
]
131133
)}")
132134

133-
threading.Thread(target=AutoRestartOnebot.check_heartbeat, daemon=True).start()
135+
threading.Thread(target=TimerManager.run_timer, daemon=True).start()
136+
TimerManager.delay(0, AutoRestartOnebot.check_heartbeat)
134137

135138
logger.info(f"启动监听服务器: {ConfigManager.GlobalConfig().server.server}")
136139

murainbot/paths.py

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,80 @@ def ensure_all_dirs_exist(self):
3131
self.CACHE_PATH.mkdir(exist_ok=True)
3232

3333

34-
# 全局变量,在CLI启动时被赋值
35-
paths: PathManager = None
34+
class PathManagerProxy:
35+
"""
36+
路径代理类,会在首次操作时把自己替换掉。
37+
"""
38+
39+
def __init__(self):
40+
"""
41+
初始化代理对象,存储初始化 PathManager 所需的 work_path。
42+
"""
43+
object.__setattr__(self, '_initialized', False) # 标记是否已初始化真实对象
44+
45+
def _initialize_and_transform(self):
46+
"""
47+
这个函数负责初始化真正的 PathManager 实例,并使代理对象“替换掉自己”。
48+
它通过改变自身的 __class__ 和更新 __dict__ 来实现。
49+
"""
50+
if object.__getattribute__(self, '_initialized'):
51+
return # 已经初始化过了,直接返回
52+
53+
work_path = os.getcwd()
54+
55+
print(f"由于Path在未被正常初始化的时候调用,临时初始化,工作目录: {work_path}")
56+
57+
init_paths(work_path)
58+
real_instance = paths
59+
60+
self.__dict__.update(real_instance.__dict__)
61+
62+
self.__class__ = PathManager
63+
64+
object.__setattr__(self, '_initialized', True)
65+
66+
def __getattr__(self, name):
67+
"""
68+
当访问代理对象上不存在的属性时(包括方法),此方法会被调用。
69+
它会触发真实 PathManager 对象的初始化。
70+
"""
71+
# 如果当前对象尚未初始化为 PathManager,则进行初始化和转换
72+
if not object.__getattribute__(self, '_initialized'):
73+
self._initialize_and_transform()
74+
75+
# 此时,self 已经变成了 PathManager 实例,其属性已在 __dict__ 或 PathManager 的方法中
76+
# 再次尝试从自身获取属性,这次会成功(如果 PathManager 有该属性/方法)
77+
return object.__getattribute__(self, name)
78+
79+
def __setattr__(self, name, value):
80+
"""
81+
当设置代理对象的属性时,此方法会被调用。
82+
它也会触发真实 PathManager 对象的初始化。
83+
"""
84+
# 如果正在设置代理自身内部的延迟参数或初始化标记,直接通过基类设置,避免递归
85+
if name in ['_initialized']:
86+
object.__setattr__(self, name, value)
87+
else:
88+
# 否则,表示用户正在操作一个业务属性,触发初始化
89+
if not object.__getattribute__(self, '_initialized'):
90+
self._initialize_and_transform()
91+
# 初始化完成后,通过基类设置属性,因为现在 self 已经是 PathManager
92+
object.__setattr__(self, name, value)
93+
94+
def __delattr__(self, name):
95+
"""
96+
当删除代理对象的属性时,此方法会被调用。
97+
它也会触发真实 PathManager 对象的初始化。
98+
"""
99+
if name in ['_initialized']:
100+
object.__delattr__(self, name)
101+
else:
102+
if not object.__getattribute__(self, '_initialized'):
103+
self._initialize_and_transform()
104+
object.__delattr__(self, name)
105+
106+
107+
paths: PathManager = PathManagerProxy()
36108

37109

38110
def init_paths(work_path_str: str):
@@ -42,8 +114,10 @@ def init_paths(work_path_str: str):
42114
work_path_str: 工作目录
43115
"""
44116
global paths
45-
paths = PathManager(Path(work_path_str))
117+
if isinstance(paths, PathManagerProxy):
118+
paths = PathManager(Path(work_path_str))
46119

47120

48-
if os.path.isdir(os.path.join(os.getcwd(), "murainbot")):
121+
# 如果是 MRB2 开发环境,自动初始化
122+
if os.path.isdir(os.path.join(os.getcwd(), "murainbot")) and os.path.isdir(os.path.join(os.getcwd(), "plugins")):
49123
init_paths(os.getcwd())

0 commit comments

Comments
 (0)