ErisPulse 2.2.0.dev1__py3-none-any.whl → 2.2.1__py3-none-any.whl

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.
@@ -2,29 +2,33 @@
2
2
  ErisPulse 事件处理模块
3
3
 
4
4
  提供统一的事件处理接口,支持命令、消息、通知、请求和元事件处理
5
+
6
+ {!--< tips >!--}
7
+ 1. 所有事件处理都基于OneBot12标准事件格式
8
+ 2. 通过装饰器方式注册事件处理器
9
+ 3. 支持优先级和条件过滤
10
+ {!--< /tips >!--}
5
11
  """
6
12
 
7
- from .cmd import command
13
+ from .command import command
8
14
  from .message import message
9
15
  from .notice import notice
10
16
  from .request import request
11
17
  from .meta import meta
12
- from .manager import event_manager
13
18
  from . import exceptions
14
19
  from .. import config
15
20
 
16
21
  # 初始化默认配置
17
22
  def _setup_default_config():
18
23
  """
19
- 设置默认配置
20
-
21
24
  {!--< internal-use >!--}
22
- 内部使用的方法,用于初始化默认配置
25
+ 设置默认配置
23
26
  """
24
27
  default_config = {
25
28
  "command": {
26
29
  "prefix": "/",
27
- "case_sensitive": True
30
+ "case_sensitive": True,
31
+ "allow_space_prefix": False
28
32
  },
29
33
  "message": {
30
34
  "ignore_self": True
@@ -43,6 +47,5 @@ __all__ = [
43
47
  "notice",
44
48
  "request",
45
49
  "meta",
46
- "event_manager",
47
50
  "exceptions"
48
51
  ]
@@ -1,18 +1,23 @@
1
1
  """
2
2
  ErisPulse 事件处理基础模块
3
3
 
4
- 提供事件处理的核心功能,包括事件注册、处理和中间件支持
4
+ 提供事件处理的核心功能,包括事件注册和处理
5
+
6
+ {!--< tips >!--}
7
+ 1. 所有事件处理都基于OneBot12标准事件格式
8
+ 2. 通过适配器系统进行事件分发和接收
9
+ {!--< /tips >!--}
5
10
  """
6
11
 
7
12
  from .. import adapter, logger
8
- from typing import Callable, Any, Dict, List, Optional, Union
13
+ from typing import Callable, Any, Dict, List, Optional
9
14
  import asyncio
10
15
 
11
16
  class BaseEventHandler:
12
17
  """
13
18
  基础事件处理器
14
19
 
15
- 提供事件处理的基本功能,包括处理器注册、中间件支持等
20
+ 提供事件处理的基本功能,包括处理器注册和注销
16
21
  """
17
22
 
18
23
  def __init__(self, event_type: str, module_name: str = None):
@@ -25,17 +30,8 @@ class BaseEventHandler:
25
30
  self.event_type = event_type
26
31
  self.module_name = module_name
27
32
  self.handlers: List[Dict] = []
28
- self.middlewares: List[Callable] = []
29
-
30
- def middleware(self, func: Callable) -> Callable:
31
- """
32
- 添加中间件
33
-
34
- :param func: 中间件函数
35
- :return: 中间件函数
36
- """
37
- self.middlewares.append(func)
38
- return func
33
+ self._handler_map = {} # 用于快速查找处理器
34
+ self._adapter_handler_registered = False # 是否已注册到适配器
39
35
 
40
36
  def register(self, handler: Callable, priority: int = 0, condition: Callable = None):
41
37
  """
@@ -52,12 +48,28 @@ class BaseEventHandler:
52
48
  "module": self.module_name
53
49
  }
54
50
  self.handlers.append(handler_info)
51
+ self._handler_map[id(handler)] = handler_info
55
52
  # 按优先级排序
56
53
  self.handlers.sort(key=lambda x: x["priority"])
57
54
 
58
55
  # 注册到适配器
59
- if self.event_type:
56
+ if self.event_type and not self._adapter_handler_registered:
60
57
  adapter.on(self.event_type)(self._process_event)
58
+ self._adapter_handler_registered = True
59
+
60
+ def unregister(self, handler: Callable) -> bool:
61
+ """
62
+ 注销事件处理器
63
+
64
+ :param handler: 要注销的事件处理器
65
+ :return: 是否成功注销
66
+ """
67
+ handler_id = id(handler)
68
+ if handler_id in self._handler_map:
69
+ self.handlers = [h for h in self.handlers if h["func"] != handler]
70
+ del self._handler_map[handler_id]
71
+ return True
72
+ return False
61
73
 
62
74
  def __call__(self, priority: int = 0, condition: Callable = None):
63
75
  """
@@ -81,37 +93,18 @@ class BaseEventHandler:
81
93
 
82
94
  :param event: 事件数据
83
95
  """
84
- # 执行中间件
85
- processed_event = event
86
- for middleware in self.middlewares:
87
- try:
88
- if asyncio.iscoroutinefunction(middleware):
89
- processed_event = await middleware(processed_event)
90
- else:
91
- processed_event = middleware(processed_event)
92
- except Exception as e:
93
- logger.error(f"中间件执行错误: {e}")
94
-
95
96
  # 执行处理器
96
97
  for handler_info in self.handlers:
97
98
  condition = handler_info.get("condition")
98
99
  # 检查条件
99
- if condition and not condition(processed_event):
100
+ if condition and not condition(event):
100
101
  continue
101
102
 
102
103
  handler = handler_info["func"]
103
104
  try:
104
105
  if asyncio.iscoroutinefunction(handler):
105
- await handler(processed_event)
106
+ await handler(event)
106
107
  else:
107
- handler(processed_event)
108
+ handler(event)
108
109
  except Exception as e:
109
110
  logger.error(f"事件处理器执行错误: {e}")
110
-
111
- def unregister(self, handler: Callable):
112
- """
113
- 注销事件处理器
114
-
115
- :param handler: 要注销的事件处理器
116
- """
117
- self.handlers = [h for h in self.handlers if h["func"] != handler]
@@ -0,0 +1,470 @@
1
+ """
2
+ ErisPulse 命令处理模块
3
+
4
+ 提供基于装饰器的命令注册和处理功能
5
+
6
+ {!--< tips >!--}
7
+ 1. 支持命令别名和命令组
8
+ 2. 支持命令权限控制
9
+ 3. 支持命令帮助系统
10
+ 4. 支持等待用户回复交互
11
+ {!--< /tips >!--}
12
+ """
13
+
14
+ from .base import BaseEventHandler
15
+ from .. import adapter, config, logger
16
+ from typing import Callable, Union, List, Dict, Any, Optional, Awaitable
17
+ import asyncio
18
+ import re
19
+ from collections import defaultdict
20
+
21
+ class CommandHandler:
22
+ def __init__(self):
23
+ self.commands: Dict[str, Dict] = {}
24
+ self.aliases: Dict[str, str] = {} # 别名映射
25
+ self.groups: Dict[str, List[str]] = {} # 命令组
26
+ self.permissions: Dict[str, Callable] = {} # 权限检查函数
27
+ self.prefix = config.getConfig("ErisPulse.event.command.prefix", "/")
28
+ self.case_sensitive = config.getConfig("ErisPulse.event.command.case_sensitive", True)
29
+ self.allow_space_prefix = config.getConfig("ErisPulse.event.command.allow_space_prefix", False)
30
+
31
+ # 等待回复相关
32
+ self._waiting_replies = {} # 存储等待回复的用户信息
33
+
34
+ # 创建消息事件处理器
35
+ self.handler = BaseEventHandler("message", "command")
36
+
37
+ # 注册消息处理器
38
+ if not hasattr(self.handler, '_command_handler_registered') or not self.handler._command_handler_registered:
39
+ self.handler.register(self._handle_message)
40
+ self.handler._command_handler_registered = True
41
+
42
+ def __call__(self,
43
+ name: Union[str, List[str]] = None,
44
+ aliases: List[str] = None,
45
+ group: str = None,
46
+ priority: int = 0,
47
+ permission: Callable = None,
48
+ help: str = None,
49
+ usage: str = None,
50
+ hidden: bool = False):
51
+ """
52
+ 命令装饰器
53
+
54
+ :param name: 命令名称,可以是字符串或字符串列表
55
+ :param aliases: 命令别名列表
56
+ :param group: 命令组名称
57
+ :param priority: 处理器优先级
58
+ :param permission: 权限检查函数,返回True时允许执行命令
59
+ :param help: 命令帮助信息
60
+ :param usage: 命令使用方法
61
+ :param hidden: 是否在帮助中隐藏命令
62
+ :return: 装饰器函数
63
+ """
64
+ def decorator(func: Callable):
65
+ cmd_names = []
66
+ if isinstance(name, str):
67
+ cmd_names = [name]
68
+ elif isinstance(name, list):
69
+ cmd_names = name
70
+ else:
71
+ # 使用函数名作为命令名
72
+ cmd_names = [func.__name__]
73
+
74
+ main_name = cmd_names[0]
75
+
76
+ # 添加别名
77
+ alias_list = aliases or []
78
+ if len(cmd_names) > 1:
79
+ alias_list.extend(cmd_names[1:])
80
+
81
+ # 注册命令
82
+ for cmd_name in cmd_names:
83
+ self.commands[cmd_name] = {
84
+ "func": func,
85
+ "help": help,
86
+ "usage": usage,
87
+ "group": group,
88
+ "permission": permission,
89
+ "hidden": hidden,
90
+ "main_name": main_name
91
+ }
92
+
93
+ # 注册别名映射
94
+ if cmd_name != main_name:
95
+ self.aliases[cmd_name] = main_name
96
+
97
+ # 注册权限检查函数
98
+ if permission and cmd_name not in self.permissions:
99
+ self.permissions[cmd_name] = permission
100
+
101
+ # 添加到命令组
102
+ if group:
103
+ if group not in self.groups:
104
+ self.groups[group] = []
105
+ for cmd_name in cmd_names:
106
+ if cmd_name not in self.groups[group]:
107
+ self.groups[group].append(cmd_name)
108
+
109
+ return func
110
+ return decorator
111
+
112
+ def unregister(self, handler: Callable) -> bool:
113
+ """
114
+ 注销命令处理器
115
+
116
+ :param handler: 要注销的命令处理器
117
+ :return: 是否成功注销
118
+ """
119
+ # 从基础处理器中移除
120
+ result = self.handler.unregister(handler)
121
+
122
+ # 从命令映射中移除
123
+ commands_to_remove = []
124
+ for cmd_name, cmd_info in self.commands.items():
125
+ if cmd_info["func"] == handler:
126
+ commands_to_remove.append(cmd_name)
127
+
128
+ for cmd_name in commands_to_remove:
129
+ # 移除命令别名映射
130
+ main_name = self.commands[cmd_name]["main_name"]
131
+ aliases_to_remove = [alias for alias, name in self.aliases.items() if name == main_name]
132
+ for alias in aliases_to_remove:
133
+ del self.aliases[alias]
134
+
135
+ # 从命令组中移除
136
+ for group_name, group_commands in self.groups.items():
137
+ if cmd_name in group_commands:
138
+ group_commands.remove(cmd_name)
139
+
140
+ # 移除权限检查函数
141
+ if cmd_name in self.permissions:
142
+ del self.permissions[cmd_name]
143
+
144
+ # 最后移除命令本身
145
+ del self.commands[cmd_name]
146
+
147
+ return result
148
+
149
+ async def wait_reply(self,
150
+ event: Dict[str, Any],
151
+ prompt: str = None,
152
+ timeout: float = 60.0,
153
+ callback: Callable[[Dict[str, Any]], Awaitable[Any]] = None,
154
+ validator: Callable[[Dict[str, Any]], bool] = None) -> Optional[Dict[str, Any]]:
155
+ """
156
+ 等待用户回复
157
+
158
+ :param event: 原始事件数据
159
+ :param prompt: 提示消息,如果提供会发送给用户
160
+ :param timeout: 等待超时时间(秒)
161
+ :param callback: 回调函数,当收到回复时执行
162
+ :param validator: 验证函数,用于验证回复是否有效
163
+ :return: 用户回复的事件数据,如果超时则返回None
164
+ """
165
+ platform = event.get("platform")
166
+ user_id = event.get("user_id")
167
+ group_id = event.get("group_id")
168
+ detail_type = "group" if group_id else "private"
169
+ target_id = group_id or user_id
170
+
171
+ # 发送提示消息(如果提供)
172
+ if prompt and platform:
173
+ try:
174
+ adapter_instance = getattr(adapter, platform)
175
+ await adapter_instance.Send.To(detail_type, target_id).Text(prompt)
176
+ except Exception as e:
177
+ logger.warning(f"发送提示消息失败: {e}")
178
+
179
+ # 创建等待 future
180
+ future = asyncio.get_event_loop().create_future()
181
+
182
+ # 存储等待信息
183
+ wait_key = f"{platform}:{user_id}:{target_id}"
184
+ self._waiting_replies[wait_key] = {
185
+ "future": future,
186
+ "callback": callback,
187
+ "validator": validator,
188
+ "timestamp": asyncio.get_event_loop().time()
189
+ }
190
+
191
+ try:
192
+ # 等待回复或超时
193
+ result = await asyncio.wait_for(future, timeout=timeout)
194
+
195
+ # 如果提供了回调函数,则执行
196
+ if callback:
197
+ if asyncio.iscoroutinefunction(callback):
198
+ await callback(result)
199
+ else:
200
+ callback(result)
201
+
202
+ return result
203
+ except asyncio.TimeoutError:
204
+ # 清理超时的等待
205
+ if wait_key in self._waiting_replies:
206
+ del self._waiting_replies[wait_key]
207
+ return None
208
+ except Exception as e:
209
+ # 清理异常情况
210
+ if wait_key in self._waiting_replies:
211
+ del self._waiting_replies[wait_key]
212
+ logger.error(f"等待回复时发生错误: {e}")
213
+ return None
214
+
215
+ async def _handle_message(self, event: Dict[str, Any]):
216
+ """
217
+ 处理消息事件中的命令
218
+
219
+ {!--< internal-use >!--}
220
+ 内部使用的方法,用于从消息中解析并执行命令
221
+
222
+ :param event: 消息事件数据
223
+ """
224
+ # 检查是否是系统消息或自身消息
225
+ if event.get("self", {}).get("user_id") == event.get("user_id"):
226
+ # 根据配置决定是否忽略自身消息
227
+ ignore_self = config.getConfig("ErisPulse.event.message.ignore_self", True)
228
+ if ignore_self:
229
+ return
230
+
231
+ # 检查是否已经被其他处理器标记为已处理
232
+ if event.get("_processed"):
233
+ return
234
+
235
+ # 检查是否为文本消息
236
+ if event.get("type") != "message":
237
+ return
238
+
239
+ message_segments = event.get("message", [])
240
+ text_content = ""
241
+ for segment in message_segments:
242
+ if segment.get("type") == "text":
243
+ text_content = segment.get("data", {}).get("text", "")
244
+ break
245
+
246
+ if not text_content:
247
+ return
248
+
249
+ # 处理大小写敏感性
250
+ check_text = text_content if self.case_sensitive else text_content.lower()
251
+ prefix = self.prefix if self.case_sensitive else self.prefix.lower()
252
+
253
+ # 检查前缀
254
+ if not check_text.startswith(prefix):
255
+ # 检查是否允许空格前缀 (例如: "/ command")
256
+ if self.allow_space_prefix and check_text.startswith(prefix + " "):
257
+ pass
258
+ else:
259
+ # 检查是否是等待回复的消息
260
+ await self._check_pending_reply(event)
261
+ return
262
+
263
+ # 解析命令和参数
264
+ command_text = check_text[len(prefix):].strip()
265
+ parts = command_text.split()
266
+ if not parts:
267
+ return
268
+
269
+ cmd_name = parts[0]
270
+ args = parts[1:] if len(parts) > 1 else []
271
+
272
+ # 处理大小写敏感性
273
+ if not self.case_sensitive:
274
+ cmd_name = cmd_name.lower()
275
+
276
+ # 处理别名
277
+ actual_cmd_name = self.aliases.get(cmd_name, cmd_name)
278
+
279
+ # 查找命令处理器
280
+ if actual_cmd_name in self.commands:
281
+ cmd_info = self.commands[actual_cmd_name]
282
+ handler = cmd_info["func"]
283
+
284
+ # 检查权限
285
+ permission_func = cmd_info.get("permission") or self.permissions.get(actual_cmd_name)
286
+ if permission_func:
287
+ try:
288
+ has_permission = permission_func(event) if not asyncio.iscoroutinefunction(permission_func) \
289
+ else await permission_func(event)
290
+ if not has_permission:
291
+ await self._send_permission_denied(event)
292
+ return
293
+ except Exception as e:
294
+ logger.error(f"权限检查错误: {e}")
295
+ await self._send_permission_denied(event)
296
+ return
297
+
298
+ # 添加命令相关信息到事件
299
+ command_info = {
300
+ "name": actual_cmd_name,
301
+ "main_name": cmd_info["main_name"],
302
+ "args": args,
303
+ "raw": command_text,
304
+ "help": cmd_info["help"],
305
+ "usage": cmd_info["usage"],
306
+ "group": cmd_info["group"]
307
+ }
308
+
309
+ event["command"] = command_info
310
+
311
+ # 标记事件已被处理
312
+ event["_processed"] = True
313
+
314
+ try:
315
+ if asyncio.iscoroutinefunction(handler):
316
+ await handler(event)
317
+ else:
318
+ handler(event)
319
+ except Exception as e:
320
+ logger.error(f"命令执行错误: {e}")
321
+ await self._send_command_error(event, str(e))
322
+
323
+ async def _check_pending_reply(self, event: Dict[str, Any]):
324
+ """
325
+ 检查是否是等待回复的消息
326
+
327
+ :param event: 消息事件数据
328
+ """
329
+ platform = event.get("platform")
330
+ user_id = event.get("user_id")
331
+ group_id = event.get("group_id")
332
+ target_id = group_id or user_id
333
+
334
+ wait_key = f"{platform}:{user_id}:{target_id}"
335
+
336
+ # 检查是否有等待的处理器
337
+ if wait_key in self._waiting_replies:
338
+ wait_info = self._waiting_replies[wait_key]
339
+ validator = wait_info.get("validator")
340
+
341
+ # 如果有验证器,验证回复是否有效
342
+ if validator:
343
+ if not validator(event):
344
+ # 验证失败,不处理此回复,继续等待
345
+ return
346
+
347
+ # 设置 future 结果
348
+ if not wait_info["future"].done():
349
+ wait_info["future"].set_result(event)
350
+
351
+ # 清理等待信息
352
+ del self._waiting_replies[wait_key]
353
+
354
+ # 标记事件已被处理
355
+ event["_processed"] = True
356
+
357
+ async def _send_permission_denied(self, event: Dict[str, Any]):
358
+ """
359
+ 发送权限拒绝消息
360
+
361
+ {!--< internal-use >!--}
362
+ 内部使用的方法
363
+
364
+ :param event: 事件数据
365
+ """
366
+ try:
367
+ platform = event.get("platform")
368
+ user_id = event.get("user_id")
369
+ group_id = event.get("group_id")
370
+ detail_type = "group" if group_id else "private"
371
+ target_id = group_id or user_id
372
+
373
+ if platform and hasattr(adapter, platform):
374
+ adapter_instance = getattr(adapter, platform)
375
+ await adapter_instance.Send.To(detail_type, target_id).Text("权限不足,无法执行该命令")
376
+ except Exception as e:
377
+ logger.error(f"发送权限拒绝消息失败: {e}")
378
+
379
+ async def _send_command_error(self, event: Dict[str, Any], error: str):
380
+ """
381
+ 发送命令错误消息
382
+
383
+ {!--< internal-use >!--}
384
+ 内部使用的方法
385
+
386
+ :param event: 事件数据
387
+ :param error: 错误信息
388
+ """
389
+ try:
390
+ platform = event.get("platform")
391
+ user_id = event.get("user_id")
392
+ group_id = event.get("group_id")
393
+ detail_type = "group" if group_id else "private"
394
+ target_id = group_id or user_id
395
+
396
+ if platform and hasattr(adapter, platform):
397
+ adapter_instance = getattr(adapter, platform)
398
+ await adapter_instance.Send.To(detail_type, target_id).Text(f"命令执行出错: {error}")
399
+ except Exception as e:
400
+ logger.error(f"发送命令错误消息失败: {e}")
401
+
402
+ def get_command(self, name: str) -> Optional[Dict]:
403
+ """
404
+ 获取命令信息
405
+
406
+ :param name: 命令名称
407
+ :return: 命令信息字典,如果不存在则返回None
408
+ """
409
+ actual_name = self.aliases.get(name, name)
410
+ return self.commands.get(actual_name)
411
+
412
+ def get_commands(self) -> Dict[str, Dict]:
413
+ """
414
+ 获取所有命令
415
+
416
+ :return: 命令信息字典
417
+ """
418
+ return self.commands
419
+
420
+ def get_group_commands(self, group: str) -> List[str]:
421
+ """
422
+ 获取命令组中的命令
423
+
424
+ :param group: 命令组名称
425
+ :return: 命令名称列表
426
+ """
427
+ return self.groups.get(group, [])
428
+
429
+ def get_visible_commands(self) -> Dict[str, Dict]:
430
+ """
431
+ 获取所有可见命令(非隐藏命令)
432
+
433
+ :return: 可见命令信息字典
434
+ """
435
+ return {name: info for name, info in self.commands.items()
436
+ if not info.get("hidden", False) and name == info["main_name"]}
437
+
438
+ def help(self, command_name: str = None, show_hidden: bool = False) -> str:
439
+ """
440
+ 生成帮助信息
441
+
442
+ :param command_name: 命令名称,如果为None则生成所有命令的帮助
443
+ :param show_hidden: 是否显示隐藏命令
444
+ :return: 帮助信息字符串
445
+ """
446
+ if command_name:
447
+ cmd_info = self.get_command(command_name)
448
+ if cmd_info:
449
+ help_text = cmd_info.get("help", "无帮助信息")
450
+ usage = cmd_info.get("usage", f"{self.prefix}{command_name}")
451
+ return f"命令: {command_name}\n用法: {usage}\n说明: {help_text}"
452
+ else:
453
+ return f"未找到命令: {command_name}"
454
+ else:
455
+ # 生成所有命令的帮助
456
+ commands_to_show = self.get_visible_commands() if not show_hidden else {
457
+ name: info for name, info in self.commands.items()
458
+ if name == info["main_name"]
459
+ }
460
+
461
+ if not commands_to_show:
462
+ return "暂无可用命令"
463
+
464
+ help_lines = ["可用命令:"]
465
+ for cmd_name, cmd_info in commands_to_show.items():
466
+ help_text = cmd_info.get("help", "无说明")
467
+ help_lines.append(f" {self.prefix}{cmd_name} - {help_text}")
468
+ return "\n".join(help_lines)
469
+
470
+ command = CommandHandler()