diff --git a/plugins/fun_command_play/main.py b/plugins/fun_command_play/main.py
index cfbac17..01ff4f7 100644
--- a/plugins/fun_command_play/main.py
+++ b/plugins/fun_command_play/main.py
@@ -13,6 +13,7 @@ import threading
from collections import OrderedDict
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
+import xml.etree.ElementTree as ET
from loguru import logger
@@ -234,16 +235,70 @@ class FunCommandPlayPlugin(MessagePluginInterface):
return "private", sender, sender
@staticmethod
- def _extract_event_key(message: Dict[str, Any]) -> str:
- """提取事件触发键。
+ def _parse_pat_event_meta(message: Dict[str, Any]) -> Dict[str, str]:
+ """解析拍一拍事件元数据。
- 当前内置:
- - PAT:拍一拍事件
+ 兼容格式示例(用户实测):
+
+
+ Jyunere
+ 56594698995@chatroom
+ wxid_xxx
+
+
+
- 检测策略:
- 1. 系统消息文案包含“拍了拍”。
- 2. XML 内容包含 patMsg 结构。
+ 返回字段:
+ - event_key: PAT
+ - pat_from_username / pat_chatusername / pat_pattedusername
+ - pat_suffix / pat_suffix_version / pat_template
"""
+ content = str(message.get("content", "") or "")
+ if not content:
+ return {}
+
+ normalized = content.strip()
+ if " str:
+ node = pat_node.find(tag_name)
+ if node is None or node.text is None:
+ return ""
+ return str(node.text or "").strip()
+
+ return {
+ "event_key": "PAT",
+ "pat_from_username": _read_text("fromusername"),
+ "pat_chatusername": _read_text("chatusername"),
+ "pat_pattedusername": _read_text("pattedusername"),
+ "pat_suffix": _read_text("patsuffix"),
+ "pat_suffix_version": _read_text("patsuffixversion"),
+ "pat_template": _read_text("template"),
+ }
+
+ @staticmethod
+ def _extract_event_key(message: Dict[str, Any]) -> str:
+ """提取事件触发键。"""
+ # 优先走结构化解析:sysmsg type="pat" 命中即判定为 PAT。
+ pat_meta = FunCommandPlayPlugin._parse_pat_event_meta(message)
+ if pat_meta.get("event_key"):
+ return pat_meta["event_key"]
+
content = str(message.get("content", "") or "")
full_msg = message.get("full_wx_msg")
@@ -261,15 +316,20 @@ class FunCommandPlayPlugin(MessagePluginInterface):
return ""
@staticmethod
- def _build_message_context(message: Dict[str, Any], event_key: str) -> Dict[str, str]:
+ def _build_message_context(message: Dict[str, Any], event_key: str, event_meta: Optional[Dict[str, str]] = None) -> Dict[str, str]:
"""构建模板变量上下文。"""
room_id = str(message.get("roomid", "") or "")
sender = str(message.get("sender", "") or "")
- return {
+ context = {
"sender": sender,
"roomid": room_id,
"event": event_key or "",
}
+ # 拍一拍扩展变量,便于在规则文案中使用更精细的占位符。
+ # 例如:{pat_from_username}、{pat_pattedusername}、{pat_template}
+ for key, value in (event_meta or {}).items():
+ context[str(key)] = str(value or "")
+ return context
@staticmethod
def _render_template(text: str, context: Dict[str, str]) -> str:
@@ -331,8 +391,10 @@ class FunCommandPlayPlugin(MessagePluginInterface):
# 先做一次匹配并塞入 message,process_message 阶段直接复用,减少重复计算。
matched_rule = self._find_match_rule(message)
if matched_rule:
+ event_meta = self._parse_pat_event_meta(message)
message["_fun_rule_match"] = matched_rule
- message["_fun_event_key"] = self._extract_event_key(message)
+ message["_fun_event_key"] = str(event_meta.get("event_key") or self._extract_event_key(message))
+ message["_fun_event_meta"] = event_meta
return True
return False
@@ -417,7 +479,10 @@ class FunCommandPlayPlugin(MessagePluginInterface):
return False, "规则无响应动作"
event_key = str(message.get("_fun_event_key", "") or "")
- context = self._build_message_context(message, event_key=event_key)
+ event_meta = message.get("_fun_event_meta")
+ if not isinstance(event_meta, dict):
+ event_meta = self._parse_pat_event_meta(message)
+ context = self._build_message_context(message, event_key=event_key, event_meta=event_meta)
try:
for action in responses: