feat(multi-qq-platforms): 尝试适配多个适配器实例,支持填入多个QQ bot账号 (#42)

This commit is contained in:
SXP-Simon
2025-11-18 00:17:04 +08:00
committed by Helian Nuits
parent 65f86e7429
commit eb16f78b15
8 changed files with 70 additions and 40 deletions
+7 -4
View File
@@ -33,10 +33,13 @@
"hint": "是否在指定时间自动进行群聊分析,启用需要填写下面的机器人QQ号,并且确保群聊号在 enabled_groups 配置中,否则获取不到实例,不会自动分析" "hint": "是否在指定时间自动进行群聊分析,启用需要填写下面的机器人QQ号,并且确保群聊号在 enabled_groups 配置中,否则获取不到实例,不会自动分析"
}, },
"bot_qq_id": { "bot_qq_id": {
"type": "string", "type": "list",
"description": "机器人QQ号", "description": "机器人QQ号列表",
"default": "", "default": [],
"hint": "用于自动分析的机器人QQ号,填写后可启用自动分析功能" "hint": "用于自动分析的机器人QQ号,填写后可启用自动分析功能。支持配置多个QQ号,用于多机器人场景",
"items": {
"type": "string"
}
}, },
"enable_user_card": { "enable_user_card": {
"type": "bool", "type": "bool",
@@ -119,7 +119,7 @@ class UserTitleAnalyzer(BaseAnalyzer):
logger.warning(f"用户称号数据格式不完整,跳过: {title_data}") logger.warning(f"用户称号数据格式不完整,跳过: {title_data}")
continue continue
# 验证QQ号格式 # 验证QQ号格式(单个用户QQ号)
try: try:
qq = int(qq) qq = int(qq)
except (ValueError, TypeError): except (ValueError, TypeError):
@@ -151,8 +151,8 @@ class UserTitleAnalyzer(BaseAnalyzer):
准备好的用户数据字典 准备好的用户数据字典
""" """
try: try:
# 获取机器人QQ号用于过滤 # 获取机器人QQ号列表用于过滤
bot_qq_id = self.config_manager.get_bot_qq_id() bot_qq_ids = self.config_manager.get_bot_qq_id()
user_summaries = [] user_summaries = []
@@ -173,7 +173,7 @@ class UserTitleAnalyzer(BaseAnalyzer):
for user_id, stats in user_analysis.items(): for user_id, stats in user_analysis.items():
# 过滤机器人自己的消息 # 过滤机器人自己的消息
if bot_qq_id and str(user_id) == str(bot_qq_id): if bot_qq_ids and str(user_id) in [str(qq) for qq in bot_qq_ids]:
logger.debug(f"过滤掉机器人QQ号: {user_id}") logger.debug(f"过滤掉机器人QQ号: {user_id}")
continue continue
+6 -6
View File
@@ -17,8 +17,8 @@ class UserAnalyzer:
def analyze_users(self, messages: List[Dict]) -> Dict[str, Dict]: def analyze_users(self, messages: List[Dict]) -> Dict[str, Dict]:
"""分析用户活跃度""" """分析用户活跃度"""
# 获取机器人QQ号用于过滤 # 获取机器人QQ号列表用于过滤
bot_qq_id = self.config_manager.get_bot_qq_id() bot_qq_ids = self.config_manager.get_bot_qq_id()
user_stats = defaultdict( user_stats = defaultdict(
lambda: { lambda: {
@@ -36,7 +36,7 @@ class UserAnalyzer:
user_id = str(sender.get("user_id", "")) user_id = str(sender.get("user_id", ""))
# 跳过机器人自己的消息,避免进入统计 # 跳过机器人自己的消息,避免进入统计
if bot_qq_id and user_id == str(bot_qq_id): if bot_qq_ids and user_id in [str(qq) for qq in bot_qq_ids]:
continue continue
nickname = InfoUtils.get_user_nickname(self.config_manager, sender) nickname = InfoUtils.get_user_nickname(self.config_manager, sender)
@@ -81,13 +81,13 @@ class UserAnalyzer:
self, user_analysis: Dict[str, Dict], limit: int = 10 self, user_analysis: Dict[str, Dict], limit: int = 10
) -> List[Dict]: ) -> List[Dict]:
"""获取最活跃的用户""" """获取最活跃的用户"""
# 获取机器人QQ号用于过滤 # 获取机器人QQ号列表用于过滤
bot_qq_id = self.config_manager.get_bot_qq_id() bot_qq_ids = self.config_manager.get_bot_qq_id()
users = [] users = []
for user_id, stats in user_analysis.items(): for user_id, stats in user_analysis.items():
# 过滤机器人自己 # 过滤机器人自己
if bot_qq_id and str(user_id) == str(bot_qq_id): if bot_qq_ids and str(user_id) in [str(qq) for qq in bot_qq_ids]:
continue continue
users.append( users.append(
+26 -12
View File
@@ -13,6 +13,7 @@ class BotManager:
self.config_manager = config_manager self.config_manager = config_manager
self._bot_instance = None self._bot_instance = None
self._bot_qq_id = None self._bot_qq_id = None
self._bot_qq_ids = [] # 支持多个QQ号
self._context = None self._context = None
self._is_initialized = False self._is_initialized = False
@@ -30,10 +31,15 @@ class BotManager:
if bot_qq_id: if bot_qq_id:
self._bot_qq_id = str(bot_qq_id) self._bot_qq_id = str(bot_qq_id)
def set_bot_qq_id(self, bot_qq_id: str): def set_bot_qq_id(self, bot_qq_id):
"""设置bot QQ号""" """设置bot QQ号(支持单个或列表)"""
if bot_qq_id: if isinstance(bot_qq_id, list):
self._bot_qq_ids = [str(qq) for qq in bot_qq_id if qq]
if self._bot_qq_ids:
self._bot_qq_id = self._bot_qq_ids[0] # 保持向后兼容
elif bot_qq_id:
self._bot_qq_id = str(bot_qq_id) self._bot_qq_id = str(bot_qq_id)
self._bot_qq_ids = [str(bot_qq_id)]
def get_bot_instance(self): def get_bot_instance(self):
"""获取当前bot实例""" """获取当前bot实例"""
@@ -45,7 +51,7 @@ class BotManager:
def has_bot_qq_id(self) -> bool: def has_bot_qq_id(self) -> bool:
"""检查是否有配置的bot QQ号""" """检查是否有配置的bot QQ号"""
return self._bot_qq_id is not None return bool(self._bot_qq_ids) or self._bot_qq_id is not None
def is_ready_for_auto_analysis(self) -> bool: def is_ready_for_auto_analysis(self) -> bool:
"""检查是否准备好进行自动分析""" """检查是否准备好进行自动分析"""
@@ -72,10 +78,10 @@ class BotManager:
async def initialize_from_config(self): async def initialize_from_config(self):
"""从配置初始化bot管理器""" """从配置初始化bot管理器"""
# 设置配置的bot QQ号 # 设置配置的bot QQ号列表
bot_qq_id = self.config_manager.get_bot_qq_id() bot_qq_ids = self.config_manager.get_bot_qq_id()
if bot_qq_id: if bot_qq_ids:
self.set_bot_qq_id(bot_qq_id) self.set_bot_qq_id(bot_qq_ids)
# 自动发现bot实例 # 自动发现bot实例
await self.auto_discover_bot_instance() await self.auto_discover_bot_instance()
@@ -110,7 +116,7 @@ class BotManager:
return False return False
def _extract_bot_qq_id(self, bot_instance): def _extract_bot_qq_id(self, bot_instance):
"""从bot实例中提取QQ号""" """从bot实例中提取QQ号(单个)"""
# 尝试多种方式获取bot QQ号 # 尝试多种方式获取bot QQ号
if hasattr(bot_instance, "self_id") and bot_instance.self_id: if hasattr(bot_instance, "self_id") and bot_instance.self_id:
return str(bot_instance.self_id) return str(bot_instance.self_id)
@@ -125,7 +131,15 @@ class BotManager:
return self.has_bot_instance() and bool(group_id) return self.has_bot_instance() and bool(group_id)
def should_filter_bot_message(self, sender_id: str) -> bool: def should_filter_bot_message(self, sender_id: str) -> bool:
"""判断是否应该过滤bot自己的消息""" """判断是否应该过滤bot自己的消息(支持多个QQ号)"""
if not self._bot_qq_id: if not self._bot_qq_ids and not self._bot_qq_id:
return False return False
return str(sender_id) == self._bot_qq_id
sender_id_str = str(sender_id)
# 检查是否在QQ号列表中
if self._bot_qq_ids and sender_id_str in self._bot_qq_ids:
return True
# 向后兼容:检查单个QQ号
if self._bot_qq_id and sender_id_str == self._bot_qq_id:
return True
return False
+3 -3
View File
@@ -115,9 +115,9 @@ class ConfigManager:
"pdf_output_dir", "data/plugins/astrbot-qq-group-daily-analysis/reports" "pdf_output_dir", "data/plugins/astrbot-qq-group-daily-analysis/reports"
) )
def get_bot_qq_id(self) -> str: def get_bot_qq_id(self) -> list:
"""获取bot QQ号""" """获取bot QQ号列表"""
return str(self.config.get("bot_qq_id", "")) return self.config.get("bot_qq_id", [])
def get_pdf_filename_format(self) -> str: def get_pdf_filename_format(self) -> str:
"""获取PDF文件名格式""" """获取PDF文件名格式"""
+12 -7
View File
@@ -19,11 +19,15 @@ class MessageHandler:
self.activity_visualizer = ActivityVisualizer() self.activity_visualizer = ActivityVisualizer()
self.bot_manager = bot_manager self.bot_manager = bot_manager
async def set_bot_qq_id(self, bot_qq_id: str): async def set_bot_qq_id(self, bot_qq_id):
"""设置机器人QQ号(保持向后兼容""" """设置机器人QQ号(支持单个QQ号或QQ号列表"""
try: try:
if self.bot_manager: if self.bot_manager:
self.bot_manager.set_bot_qq_id(bot_qq_id) # 确保传入的是列表,保持统一处理
if isinstance(bot_qq_id, list):
self.bot_manager.set_bot_qq_id(bot_qq_id)
elif bot_qq_id:
self.bot_manager.set_bot_qq_id([bot_qq_id])
logger.info(f"设置机器人QQ号: {bot_qq_id}") logger.info(f"设置机器人QQ号: {bot_qq_id}")
except Exception as e: except Exception as e:
logger.error(f"设置机器人QQ号失败: {e}") logger.error(f"设置机器人QQ号失败: {e}")
@@ -33,7 +37,7 @@ class MessageHandler:
self.bot_manager = bot_manager self.bot_manager = bot_manager
def _extract_bot_qq_id_from_instance(self, bot_instance): def _extract_bot_qq_id_from_instance(self, bot_instance):
"""从bot实例中提取QQ号""" """从bot实例中提取QQ号(单个)"""
if hasattr(bot_instance, "self_id") and bot_instance.self_id: if hasattr(bot_instance, "self_id") and bot_instance.self_id:
return str(bot_instance.self_id) return str(bot_instance.self_id)
elif hasattr(bot_instance, "qq") and bot_instance.qq: elif hasattr(bot_instance, "qq") and bot_instance.qq:
@@ -52,12 +56,13 @@ class MessageHandler:
logger.error(f"{group_id} 参数无效") logger.error(f"{group_id} 参数无效")
return [] return []
# 确保bot_manager有QQ号用于过滤 # 确保bot_manager有QQ号列表用于过滤
if self.bot_manager and not self.bot_manager.has_bot_qq_id(): if self.bot_manager and not self.bot_manager.has_bot_qq_id():
# 尝试从bot_instance提取QQ号 # 尝试从bot_instance提取QQ号并设置为列表
bot_qq_id = self._extract_bot_qq_id_from_instance(bot_instance) bot_qq_id = self._extract_bot_qq_id_from_instance(bot_instance)
if bot_qq_id: if bot_qq_id:
self.bot_manager.set_bot_qq_id(bot_qq_id) # 将单个QQ号转换为列表,保持统一处理
self.bot_manager.set_bot_qq_id([bot_qq_id])
# 计算时间范围 # 计算时间范围
end_time = datetime.now() end_time = datetime.now()
+7 -3
View File
@@ -33,9 +33,13 @@ class AutoScheduler:
"""设置bot实例(保持向后兼容)""" """设置bot实例(保持向后兼容)"""
self.bot_manager.set_bot_instance(bot_instance) self.bot_manager.set_bot_instance(bot_instance)
def set_bot_qq_id(self, bot_qq_id: str): def set_bot_qq_id(self, bot_qq_id):
"""设置bot QQ号(保持向后兼容""" """设置bot QQ号(支持单个QQ号或QQ号列表"""
self.bot_manager.set_bot_qq_id(bot_qq_id) # 确保传入的是列表,保持统一处理
if isinstance(bot_qq_id, list):
self.bot_manager.set_bot_qq_id(bot_qq_id)
elif bot_qq_id:
self.bot_manager.set_bot_qq_id([bot_qq_id])
def _get_platform_id(self): def _get_platform_id(self):
"""获取平台ID""" """获取平台ID"""
+5 -1
View File
@@ -27,7 +27,11 @@ class MessageAnalyzer:
if self.bot_manager: if self.bot_manager:
self.bot_manager.set_bot_instance(bot_instance) self.bot_manager.set_bot_instance(bot_instance)
else: else:
await self.message_handler.set_bot_qq_id(bot_instance) # 从bot实例提取QQ号并设置为列表
bot_qq_id = self._extract_bot_qq_id_from_instance(bot_instance)
if bot_qq_id:
# 将单个QQ号转换为列表,保持统一处理
await self.message_handler.set_bot_qq_id([bot_qq_id])
async def analyze_messages( async def analyze_messages(
self, messages: List[Dict], group_id: str, unified_msg_origin: str = None self, messages: List[Dict], group_id: str, unified_msg_origin: str = None