""" 管理员管理器模块 该模块负责管理机器人的管理员列表。 它现在以 Redis 作为主要数据源,文件仅用作备份。 """ import json import os from typing import Set from ..utils.logger import logger from ..utils.singleton import Singleton from .redis_manager import redis_manager class AdminManager(Singleton): """ 管理员管理器类 以 Redis Set 作为管理员列表的唯一真实来源,提供高速的读写能力。 文件 (admin.json) 仅用于首次启动时的数据迁移和作为灾备。 """ _REDIS_KEY = "neobot:admins" # 用于存储管理员集合的 Redis 键 def __init__(self): """ 初始化 AdminManager """ if hasattr(self, '_initialized') and self._initialized: return # 管理员数据文件路径,主要用于备份和首次迁移 self.data_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "..", "data", "admin.json" ) os.makedirs(os.path.dirname(self.data_file), exist_ok=True) logger.info("管理员管理器初始化完成") super().__init__() async def initialize(self): """ 异步初始化,检查 Redis 数据,如果为空则尝试从文件迁移 """ try: # 检查 Redis 中是否已存在数据 if await redis_manager.redis.exists(self._REDIS_KEY): admin_count = await redis_manager.redis.scard(self._REDIS_KEY) logger.info(f"Redis 中已存在管理员数据,共 {admin_count} 位。") else: # Redis 为空,尝试从文件迁移 logger.info("Redis 中未找到管理员数据,尝试从 admin.json 文件迁移...") await self._migrate_from_file_to_redis() except Exception as e: logger.error(f"初始化管理员数据时发生错误: {e}") async def _migrate_from_file_to_redis(self): """ 从 admin.json 加载管理员列表并存入 Redis 这通常只在首次启动或 Redis 数据丢失时执行一次 """ admins_to_migrate = set() try: if os.path.exists(self.data_file): with open(self.data_file, "r", encoding="utf-8") as f: data = json.load(f) admins = data.get("admins", []) admins_to_migrate = set(int(admin_id) for admin_id in admins) if admins_to_migrate: await redis_manager.redis.sadd(self._REDIS_KEY, *admins_to_migrate) logger.success(f"成功从文件迁移 {len(admins_to_migrate)} 位管理员到 Redis。") else: logger.info("admin.json 文件为空或不存在,无需迁移。") except (json.JSONDecodeError, ValueError) as e: logger.error(f"解析 admin.json 失败,无法迁移: {e}") except Exception as e: logger.error(f"迁移管理员数据到 Redis 失败: {e}") async def _save_to_file_backup(self): """ 将 Redis 中的管理员列表备份到 admin.json """ try: admins = await self.get_all_admins() admin_list = [str(admin_id) for admin_id in admins] with open(self.data_file, "w", encoding="utf-8") as f: json.dump({"admins": admin_list}, f, indent=2, ensure_ascii=False) logger.debug(f"管理员列表已备份到 {self.data_file}") except Exception as e: logger.error(f"备份管理员列表到 admin.json 失败: {e}") async def is_admin(self, user_id: int) -> bool: """ 检查用户是否为管理员(直接从 Redis 读取) """ try: return await redis_manager.redis.sismember(self._REDIS_KEY, user_id) except Exception as e: logger.error(f"从 Redis 检查管理员权限失败: {e}") return False async def add_admin(self, user_id: int) -> bool: """ 添加管理员到 Redis,并更新文件备份 """ try: # sadd 返回成功添加的成员数量,1 表示成功,0 表示已存在 if await redis_manager.redis.sadd(self._REDIS_KEY, user_id) == 1: logger.info(f"已添加新管理员 {user_id} 到 Redis") await self._save_to_file_backup() # 更新备份 return True return False # 用户已经是管理员 except Exception as e: logger.error(f"添加管理员 {user_id} 到 Redis 失败: {e}") return False async def remove_admin(self, user_id: int) -> bool: """ 从 Redis 移除管理员,并更新文件备份 """ try: # srem 返回成功移除的成员数量,1 表示成功,0 表示不存在 if await redis_manager.redis.srem(self._REDIS_KEY, user_id) == 1: logger.info(f"已从 Redis 移除管理员 {user_id}") await self._save_to_file_backup() # 更新备份 return True return False # 用户不是管理员 except Exception as e: logger.error(f"从 Redis 移除管理员 {user_id} 失败: {e}") return False async def get_all_admins(self) -> Set[int]: """ 从 Redis 获取所有管理员的集合 """ try: admins = await redis_manager.redis.smembers(self._REDIS_KEY) return {int(admin_id) for admin_id in admins} except Exception as e: logger.error(f"从 Redis 获取所有管理员失败: {e}") return set() # 全局 AdminManager 实例 admin_manager = AdminManager()