Files
zhenxun_bot/zhenxun/models/ban_console.py
T
ce94f63d9a ✨ feat(hook): 增强认证钩子和运行时缓存管理 (#2106)
* ✨ feat(hook): 增强认证钩子和运行时缓存管理
```
♻ refactor(hook): 移除未使用的配置项并优化缓存设置

移除 AUTH_HOOKS_CONCURRENCY_LIMIT 配置项,该配置项不再使用

✨feat(auth_ban): 简化缓存配置并添加实体参数支持

将 BAN_CACHE_TTL 相关配置从动态配置改为常量定义,
移除复杂的 TTL 值转换逻辑,并为 auth_ban 函数添加可选的
entity 参数以支持外部传入实体信息

♻ refactor(auth_limit): 移除未使用的配置依赖

移除 AUTH_LIMIT_NOTICE_CD 配置项,直接使用常量值 2
作为限制通知冷却时间

📦 依赖更新: update playwright dependency to version 1.57.0 in pyproject.toml and requirements.txt
```

* ✨ feat(auth_checker): 增强插件模块处理和预过滤逻辑,支持用户插件兼容性

* ✨ feat(http_utils): 添加内容缓存机制以优化HTTP响应处理

* ✨ feat(cache): 添加群组插件设置视图缓存类型并更新相关逻辑

* ✨ feat(renderer): 优化渲染引擎,增加内存缓存管理和HTML文档处理逻辑

* ✨ feat(sign_in): 添加好感度排行和好感度总排行命令

* ✨ feat(renderer): 增强浏览器实例管理和模板预处理,支持历史 include 语法兼容

* ✨ feat(renderer): 优化 Playwright 环境检查逻辑,增加结果缓存以提高性能

* ✨ feat(renderer): 增强模板文件渲染策略,优化资产加载路径处理

* 🚨 auto fix by pre-commit hooks

* ✨ feat(log): 增加日志内容安全序列化,避免超长 base64 等污染日志
✨ feat(log_sanitizer): 添加对嵌入超长 base64/data URI 的清理功能
✨ feat(auth_checker): 添加 Alconna 快捷方式缓存检查,优化路由匹配逻辑

* feat(renderer): 添加渲染结果内存缓存功能以优化性能
feat(help): 实现帮助菜单图像缓存机制
feat(sign_in): 更新HTML卡片生成以支持动画禁用和剪裁
feat(superuser): 在启动时预热超级用户帮助缓存
feat(theme): 优化主题管理器的资源解析缓存

* 🚨 auto fix by pre-commit hooks

* feat(renderer): 增加全页面视口最大宽度限制并优化内容尺寸计算

---------

Co-authored-by: ATTomatoo <1126160939@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com>
2026-03-03 15:54:47 +08:00

237 lines
7.2 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 time
from typing import ClassVar
from typing_extensions import Self
from tortoise import fields
from zhenxun.services.cache import CacheException, CacheRegistry, CacheRoot
from zhenxun.services.cache.runtime_cache import BanMemoryCache
from zhenxun.services.data_access import DataAccess
from zhenxun.services.db_context import Model
from zhenxun.services.log import logger
from zhenxun.utils.enum import CacheType, DbLockType
from zhenxun.utils.exception import UserAndGroupIsNone
class BanConsole(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255, null=True)
"""用户id"""
group_id = fields.CharField(255, null=True)
"""群组id"""
ban_level = fields.IntField()
"""使用ban命令的用户等级"""
ban_time = fields.BigIntField()
"""ban开始的时间"""
ban_reason = fields.TextField(null=True, default=None)
"""ban的理由"""
duration = fields.BigIntField()
"""ban时长"""
operator = fields.CharField(255)
"""使用Ban命令的用户"""
class Meta: # pyright: ignore [reportIncompatibleVariableOverride]
table = "ban_console"
table_description = "封禁人员/群组数据表"
unique_together = ("user_id", "group_id")
indexes = [("user_id",), ("group_id",)] # noqa: RUF012
cache_type = CacheType.BAN
"""缓存类型"""
cache_key_field = ("user_id", "group_id")
"""缓存键字段"""
enable_lock: ClassVar[list[DbLockType]] = [DbLockType.CREATE, DbLockType.UPSERT]
"""开启锁"""
_cache_checked: ClassVar[bool] = False
@classmethod
def _ensure_cache_registered(cls):
"""兜底注册 BAN 缓存,避免启动时序导致的未注册问题。"""
if cls._cache_checked:
return
try:
CacheRoot.get_model(CacheType.BAN)
except CacheException:
CacheRegistry.register(
CacheType.BAN,
cls,
key_format="{user_id}_{group_id}",
)
cls._cache_checked = True
@classmethod
async def create(cls, *args, **kwargs) -> Self:
result = await super().create(*args, **kwargs)
await BanMemoryCache.upsert_from_model(result)
return result
async def delete(self, *args, **kwargs):
user_id = self.user_id
group_id = self.group_id
await super().delete(*args, **kwargs)
await BanMemoryCache.remove(user_id, group_id)
@classmethod
async def _get_data(cls, user_id: str | None, group_id: str | None) -> Self | None:
"""获取数据
参数:
user_id: 用户id
group_id: 群组id
异常:
UserAndGroupIsNone: 用户id和群组id都为空
返回:
Self | None: Self
"""
cls._ensure_cache_registered()
if not user_id and not group_id:
raise UserAndGroupIsNone()
dao = DataAccess(cls)
if user_id:
return (
await dao.safe_get_or_none(user_id=user_id, group_id=group_id)
if group_id
else await dao.safe_get_or_none(user_id=user_id, group_id__isnull=True)
)
else:
return await dao.safe_get_or_none(user_id="", group_id=group_id)
@classmethod
async def check_ban_level(
cls, user_id: str | None, group_id: str | None, level: int
) -> bool:
"""检测ban掉目标的用户与unban用户的权限等级大小
参数:
user_id: 用户id
group_id: 群组id
level: 权限等级
返回:
bool: 权限判断,能否unban
"""
logger.debug("检测用户被ban等级", target=f"{group_id}:{user_id}")
if not BanMemoryCache.is_loaded():
return False
return BanMemoryCache.check_ban_level(user_id, group_id, level)
@classmethod
async def check_ban_time(
cls, user_id: str | None, group_id: str | None = None
) -> int:
"""检测用户被ban时长
参数:
user_id: 用户id
返回:
int: ban剩余时长,-1时为永久ban,0表示未被ban
"""
logger.debug("获取用户ban时长", target=f"{group_id}:{user_id}")
if not BanMemoryCache.is_loaded():
return 0
return BanMemoryCache.remaining_time(user_id, group_id)
@classmethod
async def is_ban(cls, user_id: str | None, group_id: str | None = None) -> bool:
"""判断用户是否被ban
参数:
user_id: 用户id
返回:
bool: 是否被ban
"""
logger.debug("检测是否被ban", target=f"{group_id}:{user_id}")
return (await cls.check_ban_time(user_id, group_id)) != 0
@classmethod
async def ban(
cls,
user_id: str | None,
group_id: str | None,
ban_level: int,
reason: str | None,
duration: int,
operator: str | None = None,
):
"""ban掉目标用户
参数:
user_id: 用户id
group_id: 群组id
ban_level: 使用命令者的权限等级
duration: 时长,分钟,-1时为永久
operator: 操作者id
"""
logger.debug(
f"封禁用户/群组,等级:{ban_level},时长: {duration}",
target=f"{group_id}:{user_id}",
)
if not user_id and not group_id:
raise UserAndGroupIsNone()
cls._ensure_cache_registered()
target, _ = await cls.update_or_create(
user_id=user_id,
group_id=group_id,
defaults={
"ban_level": ban_level,
"ban_time": int(time.time()),
"ban_reason": reason,
"duration": duration,
"operator": operator or 0,
},
)
await BanMemoryCache.upsert_from_model(target)
@classmethod
async def unban(cls, user_id: str | None, group_id: str | None = None) -> bool:
"""unban用户
参数:
user_id: 用户id
group_id: 群组id
返回:
bool: 是否被ban
"""
user = await cls._get_data(user_id, group_id)
if user:
logger.debug("解除封禁", target=f"{group_id}:{user_id}")
await user.delete()
return True
return False
@classmethod
async def get_ban(
cls,
*,
id: int | None = None,
user_id: str | None = None,
group_id: str | None = None,
) -> Self | None:
"""安全地获取ban记录
参数:
id: 记录id
user_id: 用户id
group_id: 群组id
返回:
Self | None: ban记录
"""
if id is not None:
return await cls.safe_get_or_none(id=id)
return await cls._get_data(user_id, group_id)
@classmethod
async def _run_script(cls):
return [
"CREATE INDEX idx_ban_console_user_id ON ban_console(user_id);",
"CREATE INDEX idx_ban_console_group_id ON ban_console(group_id);",
"ALTER TABLE ban_console ADD COLUMN ban_reason TEXT DEFAULT NULL;",
]