mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-04 19:29:59 +08:00
bugfix:更换数据库初始化超时路径以修复连接超时问题 (#2137)
* bugfix:更换数据库初始化超时路径以修复连接超时问题 * 移除部分观测链路 * 细节修改 * 权限检查细节修改2 * bugfix:修复金币懒加载造成插件金币消耗不了的问题 * bugfix:整理鉴权逻辑 * 完善缓存系统 * 优化官端使用 * bugfix:增加官机和频道的前导统一剥离,新增获取群列表自愈 * bugfix:修复导入问题
This commit is contained in:
@@ -5,12 +5,12 @@ from nonebot_plugin_uninfo import Uninfo
|
||||
|
||||
from zhenxun.models.level_user import LevelUser
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.services.cache.runtime_cache import LevelUserMemoryCache, LevelUserSnapshot
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.utils import EntityIDs, get_entity_ids
|
||||
|
||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||
from .context import PermissionContext
|
||||
from .data_provider import DEFAULT_PERMISSION_DATA_PROVIDER, LevelUserSnapshot
|
||||
from .exception import SkipPluginException
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ async def auth_admin(
|
||||
if cached_levels is not None:
|
||||
global_user, group_users = cached_levels
|
||||
else:
|
||||
global_user, group_users = await LevelUserMemoryCache.get_levels(
|
||||
(
|
||||
global_user,
|
||||
group_users,
|
||||
) = await DEFAULT_PERMISSION_DATA_PROVIDER.get_admin_levels(
|
||||
entity.user_id, entity.group_id
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ from nonebot_plugin_uninfo import Uninfo
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.models.ban_console import BanConsole
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.services.cache.runtime_cache import BanMemoryCache
|
||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.enum import PluginType
|
||||
@@ -15,6 +14,7 @@ from zhenxun.utils.utils import EntityIDs, get_entity_ids
|
||||
|
||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||
from .context import PermissionContext
|
||||
from .data_provider import DEFAULT_PERMISSION_DATA_PROVIDER
|
||||
from .exception import SkipPluginException
|
||||
from .utils import freq
|
||||
|
||||
@@ -60,9 +60,10 @@ async def is_ban(user_id: str | None, group_id: str | None) -> int:
|
||||
"""
|
||||
if not user_id and not group_id:
|
||||
return 0
|
||||
if not BanMemoryCache.is_loaded():
|
||||
provider = DEFAULT_PERMISSION_DATA_PROVIDER
|
||||
if not provider.ban_cache_loaded():
|
||||
return 0
|
||||
return BanMemoryCache.remaining_time(user_id, group_id)
|
||||
return provider.get_ban_remaining_time(user_id, group_id)
|
||||
|
||||
|
||||
def check_plugin_type(matcher: Matcher) -> bool:
|
||||
|
||||
@@ -2,12 +2,12 @@ import time
|
||||
|
||||
from zhenxun.models.bot_console import BotConsole
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.services.cache.runtime_cache import BotMemoryCache, BotSnapshot
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.common_utils import CommonUtils
|
||||
|
||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||
from .context import PermissionContext
|
||||
from .data_provider import DEFAULT_PERMISSION_DATA_PROVIDER, BotSnapshot
|
||||
from .exception import SkipPluginException
|
||||
|
||||
|
||||
@@ -33,12 +33,13 @@ async def auth_bot(
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
provider = DEFAULT_PERMISSION_DATA_PROVIDER
|
||||
if context is not None:
|
||||
bot_id = context.event.bot_id
|
||||
bot_data = context.bot_data
|
||||
bot: BotConsole | BotSnapshot | None = bot_data
|
||||
if bot is None and not skip_fetch:
|
||||
bot = await BotMemoryCache.get(bot_id)
|
||||
bot = await provider.get_bot(bot_id)
|
||||
|
||||
if bot is None:
|
||||
raise SkipPluginException("Bot不存在,阻断权限检测...")
|
||||
|
||||
@@ -10,10 +10,6 @@ from pydantic import BaseModel
|
||||
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.models.plugin_limit import PluginLimit
|
||||
from zhenxun.services.cache.runtime_cache import (
|
||||
PluginLimitMemoryCache,
|
||||
PluginLimitSnapshot,
|
||||
)
|
||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.enum import LimitWatchType, PluginLimitType
|
||||
@@ -25,6 +21,10 @@ from zhenxun.utils.utils import EntityIDs, get_entity_ids
|
||||
|
||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||
from .context import PermissionContext
|
||||
from .data_provider import (
|
||||
DEFAULT_PERMISSION_DATA_PROVIDER,
|
||||
PluginLimitSnapshot,
|
||||
)
|
||||
from .exception import SkipPluginException
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
@@ -106,11 +106,10 @@ class LimitManager:
|
||||
block_limit: ClassVar[dict[str, Limit]] = {}
|
||||
count_limit: ClassVar[dict[str, Limit]] = {}
|
||||
|
||||
# 模块限制缓存,避免频繁查询数据库
|
||||
module_limit_cache: ClassVar[
|
||||
dict[str, tuple[float, list[PluginLimitSnapshot], bool]]
|
||||
# 只缓存异常短路结果;正常 limit 列表统一从 PluginLimitMemoryCache 读取。
|
||||
module_limit_error_cache: ClassVar[
|
||||
dict[str, tuple[float, list[PluginLimitSnapshot]]]
|
||||
] = {}
|
||||
module_cache_ttl: ClassVar[float] = 60 # 模块缓存有效期(秒)
|
||||
module_cache_error_ttl: ClassVar[float] = 5 # 超时缓存有效期(秒)
|
||||
|
||||
@classmethod
|
||||
@@ -132,15 +131,16 @@ class LimitManager:
|
||||
cls.is_updating = True
|
||||
try:
|
||||
start_time = time.time()
|
||||
await PluginLimitMemoryCache.ensure_loaded()
|
||||
limit_list = PluginLimitMemoryCache.get_all_limits()
|
||||
provider = DEFAULT_PERMISSION_DATA_PROVIDER
|
||||
await provider.ensure_module_limits_loaded()
|
||||
limit_list = await provider.get_all_module_limits()
|
||||
|
||||
# 清空旧数据
|
||||
cls.add_module = []
|
||||
cls.cd_limit = {}
|
||||
cls.block_limit = {}
|
||||
cls.count_limit = {}
|
||||
cls.module_limit_cache.clear()
|
||||
cls.module_limit_error_cache.clear()
|
||||
# 添加新数据
|
||||
for limit in limit_list:
|
||||
cls.add_limit(limit)
|
||||
@@ -216,22 +216,21 @@ class LimitManager:
|
||||
"""
|
||||
current_time = time.time()
|
||||
|
||||
# 检查缓存
|
||||
if module in cls.module_limit_cache:
|
||||
cache_time, limits, is_error = cls.module_limit_cache[module]
|
||||
ttl = cls.module_cache_error_ttl if is_error else cls.module_cache_ttl
|
||||
if current_time - cache_time < ttl:
|
||||
# 正常路径不再二次缓存列表,避免与 PluginLimitMemoryCache 形成双真源。
|
||||
if module in cls.module_limit_error_cache:
|
||||
cache_time, limits = cls.module_limit_error_cache[module]
|
||||
if current_time - cache_time < cls.module_cache_error_ttl:
|
||||
return limits
|
||||
cls.module_limit_error_cache.pop(module, None)
|
||||
|
||||
# 缓存不存在或已过期,从内存缓存获取
|
||||
try:
|
||||
await PluginLimitMemoryCache.ensure_loaded()
|
||||
limits = await PluginLimitMemoryCache.get_limits(module)
|
||||
cls.module_limit_cache[module] = (current_time, limits, False)
|
||||
return limits
|
||||
provider = DEFAULT_PERMISSION_DATA_PROVIDER
|
||||
await provider.ensure_module_limits_loaded()
|
||||
return await provider.get_module_limits(module)
|
||||
except Exception as exc:
|
||||
logger.error(f"get module limits failed: {module}", LOGGER_COMMAND, e=exc)
|
||||
cls.module_limit_cache[module] = (current_time, [], True)
|
||||
cls.module_limit_error_cache[module] = (current_time, [])
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -39,6 +39,7 @@ if TYPE_CHECKING:
|
||||
class EventContext:
|
||||
bot_id: str
|
||||
platform: str
|
||||
platform_scope: str
|
||||
event_type: str
|
||||
message_id: str | int | None
|
||||
entity: EntityIDs
|
||||
@@ -170,6 +171,7 @@ def event_cache_key(
|
||||
*,
|
||||
bot_id: str,
|
||||
platform: str,
|
||||
platform_scope: str | None = None,
|
||||
entity: EntityIDs,
|
||||
) -> str:
|
||||
msg_id = _event_message_id(event)
|
||||
@@ -177,7 +179,11 @@ def event_cache_key(
|
||||
msg_id = id(event)
|
||||
group_id = entity.group_id or ""
|
||||
channel_id = entity.channel_id or ""
|
||||
return f"{platform}:{bot_id}:{entity.user_id}:{group_id}:{channel_id}:{msg_id}"
|
||||
scope = platform_scope or platform
|
||||
return (
|
||||
f"{scope}:{platform}:{bot_id}:{entity.user_id}:"
|
||||
f"{group_id}:{channel_id}:{msg_id}"
|
||||
)
|
||||
|
||||
|
||||
def get_event_cache(
|
||||
@@ -185,11 +191,18 @@ def get_event_cache(
|
||||
*,
|
||||
bot_id: str,
|
||||
platform: str,
|
||||
platform_scope: str | None = None,
|
||||
entity: EntityIDs,
|
||||
) -> dict[str, Any] | None:
|
||||
if not EVENT_CACHE:
|
||||
return None
|
||||
key = event_cache_key(event, bot_id=bot_id, platform=platform, entity=entity)
|
||||
key = event_cache_key(
|
||||
event,
|
||||
bot_id=bot_id,
|
||||
platform=platform,
|
||||
platform_scope=platform_scope,
|
||||
entity=entity,
|
||||
)
|
||||
try:
|
||||
return EVENT_CACHE[key]
|
||||
except KeyError:
|
||||
@@ -255,6 +268,7 @@ def get_or_create_event_context(
|
||||
entity = resolve_entity_ids(event, session)
|
||||
|
||||
platform = PlatformUtils.get_platform(session)
|
||||
platform_scope = PlatformUtils.get_platform_scope(session)
|
||||
bot_id = str(bot.self_id)
|
||||
event_cache = state.get(STATE_EVENT_CACHE)
|
||||
if not isinstance(event_cache, dict):
|
||||
@@ -262,6 +276,7 @@ def get_or_create_event_context(
|
||||
event,
|
||||
bot_id=bot_id,
|
||||
platform=platform,
|
||||
platform_scope=platform_scope,
|
||||
entity=entity,
|
||||
)
|
||||
|
||||
@@ -292,6 +307,7 @@ def get_or_create_event_context(
|
||||
context = EventContext(
|
||||
bot_id=bot_id,
|
||||
platform=platform,
|
||||
platform_scope=platform_scope,
|
||||
event_type=event.get_type(),
|
||||
message_id=_event_message_id(event),
|
||||
entity=entity,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from zhenxun.services.cache.runtime_cache import (
|
||||
BanMemoryCache,
|
||||
BotMemoryCache,
|
||||
BotSnapshot,
|
||||
GroupMemoryCache,
|
||||
GroupSnapshot,
|
||||
LevelUserMemoryCache,
|
||||
LevelUserSnapshot,
|
||||
PluginLimitMemoryCache,
|
||||
PluginLimitSnapshot,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
|
||||
|
||||
AdminLevels = tuple[LevelUserSnapshot | None, LevelUserSnapshot | None]
|
||||
|
||||
|
||||
class PermissionDataProvider:
|
||||
"""Auth data facade over runtime caches.
|
||||
|
||||
Permission checks should read stable runtime snapshots through this provider
|
||||
instead of reaching into individual cache classes from multiple auth modules.
|
||||
The provider does not own policy semantics and does not query the database
|
||||
directly.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def plugin_cache_loaded() -> bool:
|
||||
from zhenxun.services.cache.runtime_cache import PluginInfoMemoryCache
|
||||
|
||||
return PluginInfoMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_if_ready(module: str) -> "PluginInfo | None":
|
||||
from zhenxun.services.cache.runtime_cache import PluginInfoMemoryCache
|
||||
|
||||
return PluginInfoMemoryCache.get_by_module_if_ready(module)
|
||||
|
||||
@staticmethod
|
||||
async def get_plugin(module: str) -> "PluginInfo | None":
|
||||
from zhenxun.services.cache.runtime_cache import PluginInfoMemoryCache
|
||||
|
||||
return await PluginInfoMemoryCache.get_by_module(module)
|
||||
|
||||
@staticmethod
|
||||
def module_limit_cache_loaded() -> bool:
|
||||
return PluginLimitMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
async def ensure_module_limits_loaded() -> None:
|
||||
await PluginLimitMemoryCache.ensure_loaded()
|
||||
|
||||
@staticmethod
|
||||
def get_module_limits_if_ready(
|
||||
module: str,
|
||||
) -> list[PluginLimitSnapshot] | None:
|
||||
return PluginLimitMemoryCache.get_limits_if_ready(module)
|
||||
|
||||
@staticmethod
|
||||
async def get_module_limits(module: str) -> list[PluginLimitSnapshot]:
|
||||
return await PluginLimitMemoryCache.get_limits(module)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_module_limits() -> list[PluginLimitSnapshot]:
|
||||
if not PluginLimitMemoryCache.is_loaded():
|
||||
await PluginLimitMemoryCache.ensure_loaded()
|
||||
return PluginLimitMemoryCache.get_all_limits()
|
||||
|
||||
@staticmethod
|
||||
def bot_cache_loaded() -> bool:
|
||||
return BotMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
def get_bot_if_ready(bot_id: str | None) -> BotSnapshot | None:
|
||||
return BotMemoryCache.get_if_ready(bot_id)
|
||||
|
||||
@staticmethod
|
||||
async def get_bot(bot_id: str | None) -> BotSnapshot | None:
|
||||
return await BotMemoryCache.get(bot_id)
|
||||
|
||||
@staticmethod
|
||||
def group_cache_loaded() -> bool:
|
||||
return GroupMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
def get_group_if_ready(
|
||||
group_id: str | None,
|
||||
channel_id: str | None = None,
|
||||
) -> GroupSnapshot | None:
|
||||
return GroupMemoryCache.get_if_ready(group_id, channel_id)
|
||||
|
||||
@staticmethod
|
||||
async def get_group(
|
||||
group_id: str | None,
|
||||
channel_id: str | None = None,
|
||||
) -> GroupSnapshot | None:
|
||||
return await GroupMemoryCache.get(group_id, channel_id)
|
||||
|
||||
@staticmethod
|
||||
def admin_cache_loaded() -> bool:
|
||||
return LevelUserMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
def get_admin_levels_if_ready(
|
||||
user_id: str | None,
|
||||
group_id: str | None,
|
||||
) -> AdminLevels | None:
|
||||
return LevelUserMemoryCache.get_levels_if_ready(user_id, group_id)
|
||||
|
||||
@staticmethod
|
||||
async def get_admin_levels(
|
||||
user_id: str | None,
|
||||
group_id: str | None,
|
||||
) -> AdminLevels:
|
||||
return await LevelUserMemoryCache.get_levels(user_id, group_id)
|
||||
|
||||
@staticmethod
|
||||
def ban_cache_loaded() -> bool:
|
||||
return BanMemoryCache.is_loaded()
|
||||
|
||||
@staticmethod
|
||||
async def ensure_ban_loaded() -> None:
|
||||
await BanMemoryCache.ensure_loaded()
|
||||
|
||||
@staticmethod
|
||||
def is_banned(user_id: str | None, group_id: str | None) -> bool:
|
||||
return BanMemoryCache.is_banned(user_id, group_id)
|
||||
|
||||
@staticmethod
|
||||
def get_ban_remaining_time(user_id: str | None, group_id: str | None) -> int:
|
||||
return BanMemoryCache.remaining_time(user_id, group_id)
|
||||
|
||||
|
||||
DEFAULT_PERMISSION_DATA_PROVIDER = PermissionDataProvider()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DEFAULT_PERMISSION_DATA_PROVIDER",
|
||||
"AdminLevels",
|
||||
"BotSnapshot",
|
||||
"GroupSnapshot",
|
||||
"LevelUserSnapshot",
|
||||
"PermissionDataProvider",
|
||||
"PluginLimitSnapshot",
|
||||
]
|
||||
Reference in New Issue
Block a user