Files
abot/plugins/group_member_change/main.py

248 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
import threading
import time
from datetime import datetime
from typing import Dict, Any, List, Optional, Set, Tuple
from wcferry import Wcf
from plugin_common.message_plugin_interface import MessagePluginInterface
from plugin_common.plugin_interface import PluginStatus
from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
class GroupMemberChangePlugin(MessagePluginInterface):
"""群成员变更监控插件"""
@property
def name(self) -> str:
return "群成员变更监控"
@property
def version(self) -> str:
return "1.0.0"
@property
def description(self) -> str:
return "监控群成员变动并发送通知"
@property
def author(self) -> str:
return "Trae AI"
def __init__(self):
super().__init__()
self.status = PluginStatus.LOADED
self.wcf = None
self.gbm = None
self.LOG = logging.getLogger(f"Plugin.{self.name}")
# 初始化本地缓存字典,使用 group_id 作为键
self.local_members = {}
# 监控线程
self.monitor_thread = None
self.stop_flag = False
# 检查间隔时间(秒)
self.check_interval = 30
def initialize(self, context: Dict[str, Any]) -> bool:
"""初始化插件"""
self.LOG.info(f"正在初始化 {self.name} 插件...")
# 保存上下文对象
self.wcf: Wcf = context.get("wcf")
if not self.wcf:
self.LOG.error("无法获取wcf对象插件初始化失败")
return False
# 获取群管理器
self.gbm = context.get("gbm")
if not self.gbm:
self.LOG.error("无法获取群管理器对象,插件初始化失败")
return False
# 从配置中获取启用状态和检查间隔
plugin_config = self._config.get("GroupMemberChange", {})
self.check_interval = plugin_config.get("check_interval", 30)
self.LOG.info(f"{self.name} 插件初始化完成,检查间隔: {self.check_interval}")
return True
def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
"""处理接收到的消息"""
# 此插件主要通过定时任务工作,不处理消息
return False, "无需执行"
def start(self) -> bool:
"""启动插件"""
if self.status == PluginStatus.RUNNING:
self.LOG.warning(f"{self.name} 插件已经在运行中")
return True
self.stop_flag = False
# 启动监控线程
self.monitor_thread = threading.Thread(target=self._monitor_groups, daemon=True)
self.monitor_thread.start()
self.status = PluginStatus.RUNNING
self.LOG.info(f"[{self.name}] 插件已启动")
return True
def stop(self) -> bool:
"""停止插件"""
if self.status != PluginStatus.RUNNING:
self.LOG.warning(f"{self.name} 插件未在运行中")
return True
self.stop_flag = True
if self.monitor_thread and self.monitor_thread.is_alive():
self.monitor_thread.join(timeout=5)
self.status = PluginStatus.STOPPED
self.LOG.info(f"[{self.name}] 插件已停止")
return True
def _monitor_groups(self):
"""监控群成员变化的线程函数"""
self.LOG.info("群成员监控线程已启动")
while not self.stop_flag:
try:
# 获取所有启用了机器人的群组
group_list = self.gbm.get_group_list()
for group_id in group_list:
# 检查群是否启用了成员变更提醒功能
if self.gbm.get_group_permission(group_id, Feature.GROUP_MEMBER_CHANGE) == PermissionStatus.ENABLED:
self._check_group_members(group_id)
# 等待指定的时间间隔
for _ in range(self.check_interval):
if self.stop_flag:
break
time.sleep(1)
except Exception as e:
self.LOG.error(f"监控群成员变化时发生错误: {e}", exc_info=True)
time.sleep(5) # 发生错误时短暂休眠
self.LOG.info("群成员监控线程已停止")
def _check_group_members(self, group_id: str):
"""检查指定群的成员变化"""
try:
# 获取当前群成员
current_members = self.wcf.get_chatroom_members(group_id)
# 如果是首次检查该群
if group_id not in self.local_members:
self.LOG.info(f"首次检查群 {group_id},记录当前成员")
self.local_members[group_id] = current_members
return
# 获取上次记录的成员
previous_members = self.local_members[group_id]
# 比较成员变化
current_member_ids = set(current_members.keys())
previous_member_ids = set(previous_members.keys())
# 找出退群的成员
left_members = previous_member_ids - current_member_ids
# 找出新加入的成员
joined_members = current_member_ids - previous_member_ids
# 如果有成员变化
if left_members or joined_members:
self.LOG.info(f"{group_id} 成员发生变化: {len(joined_members)}人加入, {len(left_members)}人退出")
# 处理退群成员
for wxid in left_members:
nickname = previous_members[wxid]
member_info = self._get_member_info(wxid, nickname)
self._send_leave_notification(group_id, member_info)
# 处理新加入成员
for wxid in joined_members:
nickname = current_members[wxid]
# 可以在这里添加欢迎新成员的代码
# 更新本地缓存
self.local_members[group_id] = current_members
except Exception as e:
self.LOG.error(f"检查群 {group_id} 成员变化时发生错误: {e}", exc_info=True)
def _get_member_info(self, wxid: str, nickname: str) -> Dict[str, str]:
"""获取成员详细信息"""
try:
# 尝试获取成员详细信息
member_info = {
"wxid": wxid,
"nickname": nickname,
"remark": "",
"gender": "未知",
"region": "未知"
}
# 尝试获取用户详情
user_info = self.wcf.get_user_info(wxid)
if user_info:
member_info["remark"] = user_info.get("remark", "")
# 性别转换
gender_code = user_info.get("gender", 0)
if gender_code == 1:
member_info["gender"] = ""
elif gender_code == 2:
member_info["gender"] = ""
# 地区信息
country = user_info.get("country", "")
province = user_info.get("province", "")
city = user_info.get("city", "")
if country or province or city:
member_info["region"] = f"{country} {province} {city}".strip()
return member_info
except Exception as e:
self.LOG.error(f"获取成员 {wxid} 信息时发生错误: {e}")
return {
"wxid": wxid,
"nickname": nickname,
"remark": "",
"gender": "未知",
"region": "未知"
}
def _send_leave_notification(self, group_id: str, member_info: Dict[str, str]):
"""发送成员退群通知"""
now_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
message = f"""【退群提醒】
用户: {member_info['nickname']} ({member_info['gender']})
群备注: {member_info['remark']}
微信号: {member_info['wxid']}
地区: {member_info['region']}
退群时间: {now_time}
"""
self.wcf.send_text(message, group_id)
self.LOG.info(f"已发送退群通知: {member_info['nickname']} 退出群 {group_id}")
@property
def commands(self) -> List[str]:
"""插件支持的命令列表"""
return []
def get_help(self) -> str:
"""获取插件帮助信息"""
return "群成员变更监控插件:自动监控群成员变动并发送通知。"