Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions Lib/common.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
工具
"""

import os.path
import shutil
import sys
import time
Expand Down Expand Up @@ -241,9 +241,12 @@ def save_exc_dump(description: str = None, path: str = None):
break
i += 1

for _ in ['?', '*', '"', '<', '>']:
path_ = path_.replace(_, "")

kwargs = {
"frame": frame,
"path": path_
"path": os.path.normpath(path_)
}
if description:
kwargs["description"] = description
Expand Down
6 changes: 3 additions & 3 deletions Lib/core/OnebotAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@ def get_msg(self, message_id: int):
}
return self.get("/get_msg", data)

def get_forward_msg(self, message_id: int):
def get_forward_msg(self, id: int):
"""
获取合并转发消息
Args:
message_id: 消息id
id: 合并转发id
"""
data = {
"message_id": message_id
"id": id
}
return self.get("/get_forward_msg", data)

Expand Down
29 changes: 29 additions & 0 deletions Lib/core/PluginManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import dataclasses
import importlib
import inspect
import sys
import traceback

Expand Down Expand Up @@ -221,3 +222,31 @@ def run_plugin_main_wrapper(event):
event: 事件
"""
run_plugin_main(event.event_data)


def get_caller_plugin_data():
"""
获取调用者的插件数据
:return:
plugin_data: dict | None
"""

stack = inspect.stack()[1:]
for frame_info in stack:
filename = frame_info.filename

normalized_filename = os.path.normpath(filename)
normalized_plugins_path = os.path.normpath(PLUGINS_PATH)

if normalized_filename.startswith(normalized_plugins_path):
for plugin in found_plugins:
normalized_plugin_file_path = os.path.normpath(plugin["file_path"])
plugin_dir, plugin_file = os.path.split(normalized_plugin_file_path)

if plugin_dir == normalized_plugins_path:
if normalized_plugin_file_path == normalized_filename:
return plugin
else:
if normalized_filename.startswith(plugin_dir):
return plugin
return None
10 changes: 5 additions & 5 deletions Lib/utils/Actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,16 +335,16 @@ class GetForwardMsg(Action):

call_func = api.get_forward_msg

def __init__(self, message_id: int, callback: Callable[[Result], ...] = None):
def __init__(self, id: int, callback: Callable[[Result], ...] = None):
"""
Args:
message_id (int): 合并转发 ID
id (int): 合并转发 ID
callback (Callable[[Result], ...], optional): 回调函数. Defaults to None.
"""
super().__init__(message_id=message_id, callback=callback)
super().__init__(id=id, callback=callback)

def logger(self, result, message_id: int):
logger.debug(f"获取合并转发消息 {message_id}")
def logger(self, result, id: int):
logger.debug(f"获取合并转发消息 {id}")


class SendLike(Action):
Expand Down
56 changes: 45 additions & 11 deletions Lib/utils/EventHandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import traceback

from Lib.common import save_exc_dump
from Lib.core import EventManager, ConfigManager
from Lib.utils import EventClassifier, Logger, QQRichText
from Lib.core import EventManager, ConfigManager, PluginManager
from Lib.utils import EventClassifier, Logger, QQRichText, StateManager

import inspect
from typing import Literal, Callable, Any, Type
Expand Down Expand Up @@ -313,18 +313,51 @@ def wrapper(func):

return wrapper

def match(self, event_data: EventClassifier.Event):
def match(self, event_data: EventClassifier.Event, plugin_data: dict):
"""
匹配事件处理器
Args:
event_data: 事件数据
plugin_data: 插件数据
"""
for priority, rules, handler, args, kwargs in sorted(self.handlers, key=lambda x: x[0], reverse=True):
try:
if all(rule.match(event_data) for rule in rules):
if handler(event_data, *args, **kwargs) is True:
logger.debug(f"处理器 {handler.__name__} 阻断了事件 {event_data} 的传播")
return
if not all(rule(event_data) for rule in rules):
continue

# 检测依赖注入
handler_kwargs = kwargs.copy() # 复制静态 kwargs

sig = inspect.signature(handler)

for name, param in sig.parameters.items():
if name == "state":
if (isinstance(event_data, EventClassifier.MessageEvent) or
isinstance(event_data, EventClassifier.PrivateMessageEvent)):
state_id = f"u{event_data.user_id}"
elif isinstance(event_data, EventClassifier.GroupMessageEvent):
state_id = f"g{event_data.group_id}_u{event_data.user_id}"
else:
raise TypeError("event_data must be a MessageEvent")
handler_kwargs[name] = StateManager.get_state(state_id, plugin_data)
elif name == "user_state":
if isinstance(event_data, EventClassifier.MessageEvent):
state_id = f"u{event_data.user_id}"
else:
raise TypeError("event_data must be a MessageEvent")
handler_kwargs[name] = StateManager.get_state(state_id, plugin_data)
elif name == "group_state":
if isinstance(event_data, EventClassifier.GroupMessageEvent):
state_id = f"g{event_data.group_id}"
else:
raise TypeError("event_data must be a MessageEvent")
handler_kwargs[name] = StateManager.get_state(state_id, plugin_data)

result = handler(event_data, *args, **handler_kwargs)

if result is True:
logger.debug(f"处理器 {handler.__name__} 阻断了事件 {event_data} 的传播")
return # 阻断同一 Matcher 内的传播
except Exception as e:
if ConfigManager.GlobalConfig().debug.save_dump:
dump_path = save_exc_dump(f"执行匹配事件或执行处理器时出错 {event_data}")
Expand All @@ -340,12 +373,12 @@ def match(self, event_data: EventClassifier.Event):
events_matchers: dict[str, dict[Type[EventClassifier.Event], list[tuple[int, list[Rule], Matcher]]]] = {}


def _on_event(event_data, path, event_type):
def _on_event(event_data, path, event_type, plugin_data):
matchers = events_matchers[path][event_type]
for priority, rules, matcher in sorted(matchers, key=lambda x: x[0], reverse=True):
matcher_event_data = event_data.__class__(event_data.event_data)
if all(rule.match(matcher_event_data) for rule in rules):
matcher.match(matcher_event_data)
matcher.match(matcher_event_data, plugin_data)


def on_event(event: Type[EventClassifier.Event], priority: int = 0, rules: list[Rule] = None):
Expand All @@ -364,12 +397,13 @@ def on_event(event: Type[EventClassifier.Event], priority: int = 0, rules: list[
raise TypeError("rules must be a list of Rule")
if not issubclass(event, EventClassifier.Event):
raise TypeError("event must be an instance of EventClassifier.Event")
path = inspect.stack()[1].filename
plugin_data = PluginManager.get_caller_plugin_data()
path = plugin_data["path"]
if path not in events_matchers:
events_matchers[path] = {}
if event not in events_matchers[path]:
events_matchers[path][event] = []
EventManager.event_listener(event, path=path, event_type=event)(_on_event)
EventManager.event_listener(event, path=path, event_type=event, plugin_data=plugin_data)(_on_event)
events_matcher = Matcher()
events_matchers[path][event].append((priority, rules, events_matcher))
return events_matcher
20 changes: 10 additions & 10 deletions Lib/utils/QQRichText.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def cq_2_array(cq: str) -> list[dict[str, dict[str, Any]]]:
segment_data[now_key] += c # 继续拼接参数值

if "text" in segment_data and len(segment_data["text"]): # 处理末尾可能存在的文本内容
cq_array.append({"type": "text", "data": {"text": segment_data["text"]}})
cq_array.append({"type": "text", "data": {"text": cq_decode(segment_data["text"])}})

return cq_array

Expand Down Expand Up @@ -961,17 +961,17 @@ class XML(Segment):
segment_type = "xml"

def __init__(self, data):
self.data = data
super().__init__({"type": "xml", "data": {"data": str(self.data)}})
self.xml_data = data
super().__init__({"type": "xml", "data": {"data": str(self.xml_data)}})

def set_xml_data(self, data):
"""
设置xml数据
Args:
data: xml数据
"""
self.data = data
self.array["data"]["data"] = str(self.data)
self.xml_data = data
self.array["data"]["data"] = str(self.xml_data)


class JSON(Segment):
Expand All @@ -985,25 +985,25 @@ def __init__(self, data):
Args:
data: JSON 内容
"""
self.data = data
super().__init__({"type": "json", "data": {"data": str(self.data)}})
self.json_data = data
super().__init__({"type": "json", "data": {"data": str(self.json_data)}})

def set_json(self, data):
"""
设置json数据
Args:
data: json 内容
"""
self.data = data
self.array["data"]["data"] = str(self.data)
self.json_data = data
self.array["data"]["data"] = str(self.json_data)

def get_json(self):
"""
获取json数据(自动序列化)
Returns:
json: json数据
"""
return json.loads(self.data)
return json.loads(self.json_data)


class QQRichText:
Expand Down
68 changes: 68 additions & 0 deletions Lib/utils/StateManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
状态管理器
注意,数据存储于内存中,重启丢失,需要持久化保存的数据请勿放在里面
"""

from Lib.core import PluginManager

states = {}


def get_state(state_id: str, plugin_data: dict = None):
"""
获取状态数据
Args:
state_id: 状态ID
plugin_data: 插件数据

Returns:
状态数据,结构类似
{
"state_id": state_id,
"data": {
k1: v1,
k2: v2
},
"other_plugin_data": {
plugin1_path: {
"data": {
k1: v1,
k2: v2
},
"meta": {
"plugin_data": plugin_data
}
},
plugin2_path: {
"data": {
k1: v1,
k2: v2
},
"meta": {
"plugin_data": plugin_data
}
}
}
}
"""
if plugin_data is None:
plugin_data = PluginManager.get_caller_plugin_data()

if state_id not in states:
states[state_id] = {}

if plugin_data["path"] not in states[state_id]:
states[state_id][plugin_data["path"]] = {
"data": {},
"meta": {
"plugin_data": plugin_data
}
}

return {
"state_id": state_id,
"data": states[state_id][plugin_data["path"]]["data"],
"other_plugin_data": {
k: v for k, v in states[state_id].items() if k != plugin_data["path"]
}
}
1 change: 1 addition & 0 deletions Lib/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
from . import Logger
from . import EventClassifier
from . import StateManager
from . import QQRichText
from . import QQDataCacher
from . import Actions
Expand Down
Loading