virid-std 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- virid_std-0.1.0/.gitignore +4 -0
- virid_std-0.1.0/PKG-INFO +5 -0
- virid_std-0.1.0/pyproject.toml +18 -0
- virid_std-0.1.0/src/virid/std/__init__.py +8 -0
- virid_std-0.1.0/src/virid/std/plugin.py +23 -0
- virid_std-0.1.0/src/virid/std/utils/__init__.py +14 -0
- virid_std-0.1.0/src/virid/std/utils/execute_group.py +136 -0
- virid_std-0.1.0/src/virid/std/utils/nexttick.py +30 -0
virid_std-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "virid-std"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.10"
|
|
5
|
+
dependencies = [
|
|
6
|
+
"core",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
[tool.uv.sources]
|
|
10
|
+
core = { workspace = true }
|
|
11
|
+
|
|
12
|
+
[tool.hatch.build.targets.wheel]
|
|
13
|
+
packages = ["src/virid"]
|
|
14
|
+
layout = "src"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026-present Ailrid.
|
|
3
|
+
Licensed under the Apache License, Version 2.0.
|
|
4
|
+
Project: Virid
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from virid.core import ViridApp
|
|
8
|
+
from .utils import activate_utils
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def activate_plugin(app: ViridApp):
|
|
12
|
+
activate_utils(app)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Plugin:
|
|
16
|
+
name = "std"
|
|
17
|
+
|
|
18
|
+
# 将返回值改为 None,以契合 Protocol 的声明
|
|
19
|
+
def install(self, app: ViridApp, options: None) -> None:
|
|
20
|
+
activate_plugin(app)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
StdPlugin = Plugin()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026-present Ailrid.
|
|
3
|
+
Licensed under the Apache License, Version 2.0.
|
|
4
|
+
Project: Virid
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .execute_group import activate_group_messages, execute_group, execute_block
|
|
8
|
+
from .nexttick import activate_next_tick, next_tick
|
|
9
|
+
from virid.core import ViridApp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def activate_utils(app: ViridApp):
|
|
13
|
+
activate_group_messages(app)
|
|
14
|
+
activate_next_tick(app)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026-present Ailrid.
|
|
3
|
+
Licensed under the Apache License, Version 2.0.
|
|
4
|
+
Project: Virid
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Callable
|
|
8
|
+
from virid.core import EventMessage, ViridApp, MessageWriter, ExecuteHookContext
|
|
9
|
+
from contextlib import contextmanager
|
|
10
|
+
|
|
11
|
+
# 每个 key 中缓存的执行组队列及状态映射
|
|
12
|
+
execute_group_map: dict[str, list[dict[str, Any]]] = {}
|
|
13
|
+
message_key_map: dict[int, str] = {} # 键类型改为 int,用于存放 id(message)
|
|
14
|
+
key_message_map: dict[str, list[EventMessage]] = {}
|
|
15
|
+
call_back_map: dict[str, Callable[[bool], None]] = {}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def clear_group(group_id: str) -> None:
|
|
19
|
+
for message in key_message_map[group_id]:
|
|
20
|
+
message_key_map.pop(id(message), None) # 使用 id(message) 释放
|
|
21
|
+
key_message_map.pop(group_id, None)
|
|
22
|
+
execute_group_map.pop(group_id, None)
|
|
23
|
+
call_back_map.pop(group_id, None)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def after_execute_hook(
|
|
27
|
+
message: EventMessage,
|
|
28
|
+
_hook_context: ExecuteHookContext,
|
|
29
|
+
success: bool,
|
|
30
|
+
) -> None:
|
|
31
|
+
key = message_key_map.get(id(message)) # 使用 id(message) 获取
|
|
32
|
+
if key:
|
|
33
|
+
execute_group = execute_group_map.get(key)
|
|
34
|
+
if execute_group is None:
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if success:
|
|
38
|
+
# 只有当前任务执行成功,才继续下一个
|
|
39
|
+
if len(execute_group) > 0:
|
|
40
|
+
context = execute_group.pop(0)
|
|
41
|
+
resolve = context["resolve"]
|
|
42
|
+
resolve()
|
|
43
|
+
# 全部执行完成
|
|
44
|
+
if len(execute_group) == 0:
|
|
45
|
+
callback = call_back_map.get(key)
|
|
46
|
+
if callback:
|
|
47
|
+
callback(True)
|
|
48
|
+
clear_group(key)
|
|
49
|
+
else:
|
|
50
|
+
callback = call_back_map.get(key)
|
|
51
|
+
if callback:
|
|
52
|
+
callback(False)
|
|
53
|
+
clear_group(key)
|
|
54
|
+
# 如果出错了,直接取消执行队列
|
|
55
|
+
MessageWriter.error(
|
|
56
|
+
Exception(
|
|
57
|
+
f"[ExecuteGroup] Queue Execution Failed: Due to an error in the System execution triggered by {message.__class__.__name__}, the message group '{key}' has been cancelled"
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def execute_group(
|
|
63
|
+
messages: list[EventMessage],
|
|
64
|
+
group_id: str = "default",
|
|
65
|
+
callback: Callable[[bool], None] | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
# 修复:原代码为 id in execute_group_map,会导致判定错误
|
|
68
|
+
if group_id in execute_group_map:
|
|
69
|
+
MessageWriter.error(
|
|
70
|
+
Exception(
|
|
71
|
+
f"[ExecuteGroup] Unavailable ID: The id '{group_id}' not yet executed"
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
if callback:
|
|
75
|
+
callback(False)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# 注册这一个执行组
|
|
79
|
+
execute_chain: list[dict[str, Any]] = []
|
|
80
|
+
key_message_map[group_id] = messages
|
|
81
|
+
|
|
82
|
+
for index, message in enumerate(messages):
|
|
83
|
+
if index == len(messages) - 1:
|
|
84
|
+
resolve = lambda: None
|
|
85
|
+
else:
|
|
86
|
+
# 使用默认参数绑定 next_msg,防止 Python 闭包延迟绑定导致总是拿到最后一条消息
|
|
87
|
+
next_msg = messages[index + 1]
|
|
88
|
+
resolve = lambda m=next_msg: MessageWriter.write(m)
|
|
89
|
+
|
|
90
|
+
execute_chain.append({"message": message, "resolve": resolve})
|
|
91
|
+
message_key_map[id(message)] = group_id # 使用 id(message) 作为键
|
|
92
|
+
|
|
93
|
+
if callback:
|
|
94
|
+
call_back_map[group_id] = callback
|
|
95
|
+
|
|
96
|
+
execute_group_map[group_id] = execute_chain
|
|
97
|
+
|
|
98
|
+
# 立刻触发第一个
|
|
99
|
+
MessageWriter.write(messages[0])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@contextmanager
|
|
103
|
+
def execute_block(
|
|
104
|
+
group_id: str = "default", callback: Callable[[bool], None] | None = None
|
|
105
|
+
):
|
|
106
|
+
# 创建一个临时篮子,用来装 with 期间产生的所有消息实例
|
|
107
|
+
captured_messages: list[EventMessage] = []
|
|
108
|
+
|
|
109
|
+
# 备份原本的全局 MessageWriter.write 方法
|
|
110
|
+
original_write = MessageWriter.write
|
|
111
|
+
|
|
112
|
+
# 定义一个拦截函数
|
|
113
|
+
def mock_write(message: Any) -> None:
|
|
114
|
+
if isinstance(message, EventMessage):
|
|
115
|
+
# 如果是事件消息,拦截下来,存进篮子,先不发送
|
|
116
|
+
captured_messages.append(message)
|
|
117
|
+
else:
|
|
118
|
+
# 如果是其他不归群组管的消息(比如 InfoMessage 等),让它走原渠道正常发
|
|
119
|
+
original_write(message)
|
|
120
|
+
|
|
121
|
+
# 把原本的 write 换成拦截器
|
|
122
|
+
MessageWriter.write = mock_write # type: ignore
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
yield # 此时执行 with 块内部的代码
|
|
126
|
+
finally:
|
|
127
|
+
# 离开 with 无论里面是否报错,雷打不动地把原本的 write 恢复回去
|
|
128
|
+
# 这确保了黑魔法不会污染全局
|
|
129
|
+
MessageWriter.write = original_write
|
|
130
|
+
|
|
131
|
+
if captured_messages:
|
|
132
|
+
execute_group(captured_messages, group_id=group_id, callback=callback)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def activate_group_messages(app: ViridApp) -> None:
|
|
136
|
+
app.on_after_execute(EventMessage, after_execute_hook)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2026-present Ailrid.
|
|
3
|
+
Licensed under the Apache License, Version 2.0.
|
|
4
|
+
Project: Virid
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Callable, Any
|
|
8
|
+
from virid.core import ViridApp
|
|
9
|
+
|
|
10
|
+
# 一个简单的 nextTick 任务队列
|
|
11
|
+
tick_task_queue: list[Callable[[], None]] = []
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def after_tick_hook(context: Any) -> None:
|
|
15
|
+
global tick_task_queue
|
|
16
|
+
if len(tick_task_queue) == 0:
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
# 严格遵循你 TS 原版的双缓冲/清空逻辑
|
|
20
|
+
for task in tick_task_queue:
|
|
21
|
+
task()
|
|
22
|
+
tick_task_queue = []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def next_tick(task: Callable[[], None]) -> None:
|
|
26
|
+
tick_task_queue.append(task)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def activate_next_tick(app: ViridApp) -> None:
|
|
30
|
+
app.on_after_tick(after_tick_hook)
|