first commit

This commit is contained in:
Prometheus-first
2026-10-15 18:27:46 +08:00
commit 68eb19ea35
88 changed files with 10319 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
"""纯 Python 的文本会话解析;不依赖 Windows 或原生 DLL。"""
from __future__ import annotations
import re
WXID = re.compile(r"^[A-Za-z0-9_.@-]{1,127}$")
SYSTEM_PEERS = frozenset({"weixin", "fmessage", "newsapp", "floatbottle"})
GROUP_CONTROL_COMMANDS = frozenset({"/help", "成语接龙", "合并接龙", "退出接龙"})
FOUR_CHINESE = re.compile(r"^[\u3400-\u4dbf\u4e00-\u9fff]{4}$")
def own_group_command(text):
value = text.strip()
return bool(value in GROUP_CONTROL_COMMANDS or FOUR_CHINESE.fullmatch(value) or
value.split(maxsplit=1)[:1] in (["/绑定"], ["/总结"], ["/总结群聊"], ["/切换人格"], ["/清空"]))
def validate_target(target):
if not isinstance(target, str) or not WXID.fullmatch(target):
raise ValueError("目标应为微信 ID 或群 ID,长度最多 127 个 ASCII 字符")
return target
def validate_mentions(room, members):
validate_target(room)
if not room.endswith("@chatroom"):
raise ValueError("艾特消息的目标必须是群 ID")
if not isinstance(members, (list, tuple)) or not 1 <= len(members) <= 8:
raise ValueError("at_list 应包含 1–8 个成员微信 ID")
from .account import validate_account
result = tuple(validate_account(member) for member in members)
if len(set(result)) != len(result):
raise ValueError("at_list 不能重复")
return result
class TextRouter:
"""先识别本账号,再区分会话与作者,避免回复自己同步过来的消息。"""
def __init__(self, *, include_self_groups=False):
self.include_self_groups = include_self_groups
self.self_wxid = ""
self.unidentified = 0
self.unparsed_groups = 0
self.ignored_self = 0
def set_account(self, account):
from .account import validate_account
validate_account(account)
if self.self_wxid and self.self_wxid != account:
raise RuntimeError("当前登录账号发生变化,需要关闭旧连接后重新识别")
self.self_wxid = account
def convert(self, sender, receiver, text, *, sequence=0, thread_id=0):
validate_target(sender)
validate_target(receiver)
filehelper = "filehelper" in (sender, receiver)
incoming_group = sender.endswith("@chatroom")
# 文件传输助手的另一端,以及收到群消息的接收方,都是本账号。
identified = (receiver if sender == "filehelper" else sender) if filehelper else (
receiver if incoming_group else "")
if identified:
validate_target(identified)
if identified == "filehelper" or identified.endswith("@chatroom"):
return None
self.set_account(identified)
if not self.self_wxid:
self.unidentified += 1
return None
is_self = sender == self.self_wxid
if filehelper:
chat, author, room, body = "filehelper", sender, "", text
elif incoming_group:
if receiver != self.self_wxid:
return None
author, separator, body = text.partition(":\n")
# Sync AddMsg 的群文本包含“成员 ID:\n正文”;不猜测缺失的作者。
if not separator or not WXID.fullmatch(author) or author.endswith("@chatroom"):
self.unparsed_groups += 1
return None
chat = room = sender
is_self = author == self.self_wxid
elif receiver.endswith("@chatroom"):
if not is_self:
return None
chat = room = receiver
author, body = sender, text
elif receiver == self.self_wxid:
chat, author, room, body = sender, sender, "", text
elif is_self:
chat, author, room, body = receiver, sender, "", text
else:
return None
own_group_input = bool(room and own_group_command(body))
if not filehelper and is_self and not own_group_input and not (room and self.include_self_groups):
self.ignored_self += 1
return None
if chat.startswith("gh_") or chat in SYSTEM_PEERS:
return None
return {"msg": body, "content": body, "from_wxid": author if room else chat,
"room_wxid": room, "to_wxid": receiver, "wx_type": 1,
"chat_id": chat, "sender_id": author, "is_self": is_self,
"self_wxid": self.self_wxid, "native_sender": sender,
"native_receiver": receiver, "sequence": sequence,
"dispatch_to_plugins": not (room and is_self and not own_group_input),
"native_thread_id": thread_id}