feat:初版

This commit is contained in:
2025-12-03 15:48:44 +08:00
commit b4df26f61d
199 changed files with 23434 additions and 0 deletions

View File

@@ -0,0 +1 @@
# GroupLeave Plugin

264
plugins/GroupLeave/main.py Normal file
View File

@@ -0,0 +1,264 @@
"""
退群提醒插件
当成员退出群聊时,发送提醒卡片
支持两种方式:
1. type=11099 群成员删除事件(如果 API 支持)
2. type=11058 系统消息解析(通用方式)
"""
import tomllib
from pathlib import Path
from loguru import logger
from utils.plugin_base import PluginBase
from utils.decorators import on_text_message
# 定义事件装饰器
def on_chatroom_member_remove(priority=50):
"""群成员删除装饰器"""
def decorator(func):
setattr(func, '_event_type', 'chatroom_member_remove')
setattr(func, '_priority', min(max(priority, 0), 99))
return func
return decorator
def on_system_message(priority=50):
"""系统消息装饰器"""
def decorator(func):
setattr(func, '_event_type', 'system_message')
setattr(func, '_priority', min(max(priority, 0), 99))
return func
return decorator
def on_chatroom_info_change(priority=50):
"""群信息变化装饰器"""
def decorator(func):
setattr(func, '_event_type', 'chatroom_info_change')
setattr(func, '_priority', min(max(priority, 0), 99))
return func
return decorator
class GroupLeave(PluginBase):
"""退群提醒插件"""
# 插件元数据
description = "成员退群时发送提醒卡片"
author = "ShiHao"
version = "1.0.0"
def __init__(self):
super().__init__()
self.config = None
# 群成员缓存:{room_wxid: {wxid: nickname}}
self.member_cache = {}
async def async_init(self):
"""插件异步初始化"""
# 读取配置
config_path = Path(__file__).parent / "config.toml"
with open(config_path, "rb") as f:
self.config = tomllib.load(f)
logger.info("退群提醒插件已加载")
@on_chatroom_member_remove(priority=50)
async def on_chatroom_member_remove(self, bot, message: dict):
"""处理群成员删除事件type=11099"""
logger.info(f"[GroupLeave] 收到群成员删除事件,原始消息: {message}")
# 检查是否启用
if not self.config["behavior"]["enabled"]:
logger.warning("[GroupLeave] 插件未启用,跳过处理")
return
room_wxid = message.get("RoomWxid", "")
member_list = message.get("MemberList", [])
logger.info(f"[GroupLeave] 解析结果 - 群ID: {room_wxid}, 成员列表: {member_list}")
# 检查群聊过滤
if not self._should_notify(room_wxid):
logger.info(f"[GroupLeave] 群 {room_wxid} 不在提醒列表中,跳过")
return
logger.success(f"[GroupLeave] 群 {room_wxid} 有成员退出: {len(member_list)}")
await self._process_leave_members(bot, room_wxid, member_list)
@on_system_message(priority=50)
async def on_system_message(self, bot, message: dict):
"""处理系统消息type=11058解析退群通知"""
# 检查是否启用
if not self.config["behavior"]["enabled"]:
return
raw_msg = message.get("Content", "")
room_wxid = message.get("FromWxid", "")
# 只处理群聊系统消息
if not room_wxid.endswith("@chatroom"):
return
logger.info(f"[GroupLeave] 收到系统消息: room={room_wxid}, msg={raw_msg}")
# 解析退群消息
# 格式1: "你将xxxx移出了群聊"
# 格式2: "xxxx将xxxx移出了群聊"
# 格式3: "xxxx退出了群聊"
import re
# 匹配退群消息
patterns = [
r"(.+?)退出了群聊", # 主动退群
r"(?:你|.+?)将(.+?)移出了群聊", # 被踢出群
]
for pattern in patterns:
match = re.search(pattern, raw_msg)
if match:
nickname = match.group(1).strip()
logger.success(f"[GroupLeave] 从系统消息解析到退群成员: {nickname}")
# 检查群聊过滤
if not self._should_notify(room_wxid):
logger.info(f"[GroupLeave] 群 {room_wxid} 不在提醒列表中,跳过")
return
# 构造成员列表(只有昵称,没有 wxid
member_list = [{"wxid": "", "nickname": nickname}]
await self._process_leave_members(bot, room_wxid, member_list)
break
@on_chatroom_info_change(priority=50)
async def on_chatroom_info_change(self, bot, message: dict):
"""处理群信息变化事件type=11100检测退群"""
if not self.config["behavior"]["enabled"]:
return
room_wxid = message.get("RoomWxid", "")
if not self._should_notify(room_wxid):
return
try:
# 获取当前群成员列表
current_members = await bot.get_chatroom_members(room_wxid)
if not current_members:
return
# 构建当前成员字典 {wxid: nickname}
current_member_dict = {m.get("wxid"): m.get("nickname", "未知成员") for m in current_members if m.get("wxid")}
# 首次遇到该群,初始化缓存
if room_wxid not in self.member_cache:
logger.info(f"[GroupLeave] 首次记录群成员: {room_wxid}, 成员数: {len(current_member_dict)}")
self.member_cache[room_wxid] = current_member_dict
return
# 对比找出退群成员
cached_member_dict = self.member_cache[room_wxid]
left_wxids = set(cached_member_dict.keys()) - set(current_member_dict.keys())
if left_wxids:
logger.success(f"[GroupLeave] 检测到退群成员: {len(left_wxids)}")
# 构造退群成员列表(使用缓存中的昵称)
left_members = [{"wxid": wxid, "nickname": cached_member_dict.get(wxid, "未知成员")} for wxid in left_wxids]
# 更新缓存
self.member_cache[room_wxid] = current_member_dict
# 处理退群成员
await self._process_leave_members(bot, room_wxid, left_members)
else:
# 更新缓存
self.member_cache[room_wxid] = current_member_dict
except Exception as e:
logger.error(f"[GroupLeave] 处理群信息变化失败: {e}")
async def _process_leave_members(self, bot, room_wxid: str, member_list: list):
"""处理退群成员列表"""
from datetime import datetime
# 为每个退出的成员发送提醒
for member in member_list:
nickname = member.get("nickname", "某成员")
try:
# 获取当前时间
leave_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 发送文字提醒
await self._send_leave_message(bot, room_wxid, nickname, leave_time)
except Exception as e:
logger.error(f"处理退群成员 {nickname} 提醒失败: {e}")
async def _send_leave_message(self, bot, room_wxid: str, nickname: str, leave_time: str):
"""发送退群提醒文字消息"""
try:
message = f"【退群提醒】\n成员:{nickname}\n时间:{leave_time}"
await bot.send_text(room_wxid, message)
logger.success(f"已发送退群提醒: {nickname}")
except Exception as e:
logger.error(f"发送退群提醒失败: {e}")
def _should_notify(self, room_wxid: str) -> bool:
"""判断是否应该发送提醒"""
enabled_groups = self.config["behavior"]["enabled_groups"]
disabled_groups = self.config["behavior"]["disabled_groups"]
# 如果在禁用列表中,不提醒
if room_wxid in disabled_groups:
return False
# 如果启用列表为空,对所有群生效
if not enabled_groups:
return True
# 否则只对启用列表中的群生效
return room_wxid in enabled_groups
@on_text_message(priority=90)
async def handle_test_command(self, bot, message: dict):
"""处理测试命令"""
content = message.get("Content", "").strip()
from_wxid = message.get("FromWxid", "")
is_group = message.get("IsGroup", False)
# 只处理群聊消息
if not is_group:
return
# 检查是否是测试退群命令
if content.startswith("/测试退群"):
parts = content.split()
if len(parts) < 2:
await bot.send_text(from_wxid, "用法: /测试退群 wxid")
return False
test_wxid = parts[1].strip()
logger.info(f"收到测试退群命令: wxid={test_wxid}")
try:
# 模拟群成员删除事件
test_message = {
"RoomWxid": from_wxid,
"MemberList": [{"wxid": test_wxid, "nickname": "测试用户"}],
}
await self.on_chatroom_member_remove(bot, test_message)
await bot.send_text(from_wxid, f"已触发退群提醒测试: {test_wxid}")
except Exception as e:
logger.error(f"测试退群提醒失败: {e}")
await bot.send_text(from_wxid, f"测试失败: {e}")
return False # 阻止后续处理