Files
NeoBot/core/managers/admin_manager.py
K2cr2O1 3cbf5328bb refactor(core): 优化权限管理和事件模型
- 重构 AdminManager 和 PermissionManager 以 Redis 为主要数据源
- 为所有事件模型添加 slots=True 提升性能
- 更新文档说明 Mypyc 编译注意事项
- 清理测试和调试文件
- 移动静态资源到 web_static 目录
2026-01-13 08:35:54 +08:00

151 lines
5.7 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.
"""
管理员管理器模块
该模块负责管理机器人的管理员列表。
它现在以 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()