mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-18 10:40:03 +08:00
fix: use AUTH_SNAPSHOT and PLUGIN_SNAPSHOT cache types instead of TEMP/PLUGINS
This commit is contained in:
@@ -6,13 +6,13 @@ from nonebot_plugin_uninfo import Uninfo
|
|||||||
|
|
||||||
from zhenxun.models.level_user import LevelUser
|
from zhenxun.models.level_user import LevelUser
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.data_access import DataAccess
|
from zhenxun.services.data_access import DataAccess
|
||||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.utils import get_entity_ids
|
from zhenxun.utils.utils import get_entity_ids
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import SkipPluginException
|
|
||||||
from .utils import send_message
|
from .utils import send_message
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ from nonebot_plugin_uninfo import Uninfo
|
|||||||
from zhenxun.configs.config import Config
|
from zhenxun.configs.config import Config
|
||||||
from zhenxun.models.ban_console import BanConsole
|
from zhenxun.models.ban_console import BanConsole
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.enum import PluginType
|
from zhenxun.utils.enum import PluginType
|
||||||
from zhenxun.utils.utils import EntityIDs, get_entity_ids
|
from zhenxun.utils.utils import EntityIDs, get_entity_ids
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import SkipPluginException
|
|
||||||
from .utils import freq, send_message
|
from .utils import freq, send_message
|
||||||
|
|
||||||
Config.add_plugin_config(
|
Config.add_plugin_config(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import time
|
|||||||
|
|
||||||
from zhenxun.models.bot_console import BotConsole
|
from zhenxun.models.bot_console import BotConsole
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.data_access import DataAccess
|
from zhenxun.services.data_access import DataAccess
|
||||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.common_utils import CommonUtils
|
from zhenxun.utils.common_utils import CommonUtils
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import SkipPluginException
|
|
||||||
|
|
||||||
|
|
||||||
async def auth_bot(plugin: PluginInfo, bot_id: str):
|
async def auth_bot(plugin: PluginInfo, bot_id: str):
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ from nonebot_plugin_uninfo import Uninfo
|
|||||||
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
from zhenxun.models.user_console import UserConsole
|
from zhenxun.models.user_console import UserConsole
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import SkipPluginException
|
|
||||||
from .utils import send_message
|
from .utils import send_message
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ from nonebot_plugin_alconna import UniMsg
|
|||||||
|
|
||||||
from zhenxun.models.group_console import GroupConsole
|
from zhenxun.models.group_console import GroupConsole
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD, SwitchEnum
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD, SwitchEnum
|
||||||
from .exception import SkipPluginException
|
|
||||||
|
|
||||||
|
|
||||||
async def auth_group(
|
async def auth_group(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
from zhenxun.models.plugin_limit import PluginLimit
|
from zhenxun.models.plugin_limit import PluginLimit
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.enum import LimitWatchType, PluginLimitType
|
from zhenxun.utils.enum import LimitWatchType, PluginLimitType
|
||||||
@@ -18,7 +19,6 @@ from zhenxun.utils.time_utils import TimeUtils
|
|||||||
from zhenxun.utils.utils import get_entity_ids
|
from zhenxun.utils.utils import get_entity_ids
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import SkipPluginException
|
|
||||||
|
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,16 @@ from nonebot_plugin_uninfo import Uninfo
|
|||||||
|
|
||||||
from zhenxun.models.group_console import GroupConsole
|
from zhenxun.models.group_console import GroupConsole
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.services.auth_snapshot.exception import (
|
||||||
|
IsSuperuserException,
|
||||||
|
SkipPluginException,
|
||||||
|
)
|
||||||
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
from zhenxun.services.db_context import DB_TIMEOUT_SECONDS
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.common_utils import CommonUtils
|
from zhenxun.utils.common_utils import CommonUtils
|
||||||
from zhenxun.utils.enum import BlockType
|
from zhenxun.utils.enum import BlockType
|
||||||
|
|
||||||
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
from .config import LOGGER_COMMAND, WARNING_THRESHOLD
|
||||||
from .exception import IsSuperuserException, SkipPluginException
|
|
||||||
from .utils import freq, is_poke, send_message
|
from .utils import freq, is_poke, send_message
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import nonebot
|
|||||||
from nonebot_plugin_uninfo import Uninfo
|
from nonebot_plugin_uninfo import Uninfo
|
||||||
|
|
||||||
from zhenxun.configs.config import Config
|
from zhenxun.configs.config import Config
|
||||||
|
from zhenxun.services.auth_snapshot.exception import SkipPluginException
|
||||||
from .exception import SkipPluginException
|
|
||||||
|
|
||||||
Config.add_plugin_config(
|
Config.add_plugin_config(
|
||||||
"hook",
|
"hook",
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from nonebot.adapters import Bot, Event
|
from nonebot.adapters import Bot, Event
|
||||||
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.message import run_postprocessor, run_preprocessor
|
from nonebot.message import run_postprocessor, run_preprocessor
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
from nonebot_plugin_uninfo import Uninfo
|
from nonebot_plugin_uninfo import Uninfo
|
||||||
|
|
||||||
from zhenxun.services.auth_snapshot.checker import optimized_auth_checker
|
from zhenxun.services.auth_snapshot.checker import optimized_auth_checker
|
||||||
|
from zhenxun.services.auth_snapshot.exception import (
|
||||||
|
PermissionExemption,
|
||||||
|
SkipPluginException,
|
||||||
|
)
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
from .auth.auth_limit import LimitManager
|
||||||
from .auth.config import LOGGER_COMMAND
|
from .auth.config import LOGGER_COMMAND
|
||||||
from .auth_checker import LimitManager
|
|
||||||
|
|
||||||
|
|
||||||
# # 权限检测
|
# # 权限检测
|
||||||
@@ -24,7 +29,19 @@ async def _(matcher: Matcher, event: Event, bot: Bot, session: Uninfo, message:
|
|||||||
# session,
|
# session,
|
||||||
# message,
|
# message,
|
||||||
# )
|
# )
|
||||||
await optimized_auth_checker.check(matcher, event, bot, session, message)
|
try:
|
||||||
|
await optimized_auth_checker.check(matcher, event, bot, session, message)
|
||||||
|
except SkipPluginException as e:
|
||||||
|
logger.info(str(e), LOGGER_COMMAND, session=session)
|
||||||
|
raise IgnoredException(str(e))
|
||||||
|
except PermissionExemption as e:
|
||||||
|
logger.info(
|
||||||
|
str(e) or "超级用户跳过权限检测...", LOGGER_COMMAND, session=session
|
||||||
|
)
|
||||||
|
raise IgnoredException(str(e))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"权限检测异常: {e}", LOGGER_COMMAND, session=session, e=e)
|
||||||
|
raise SkipPluginException("权限检测异常") from e
|
||||||
logger.debug(f"权限检测耗时:{time.time() - start_time}秒", LOGGER_COMMAND)
|
logger.debug(f"权限检测耗时:{time.time() - start_time}秒", LOGGER_COMMAND)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from zhenxun.models.group_plugin_setting import GroupPluginSetting
|
|||||||
from zhenxun.models.level_user import LevelUser
|
from zhenxun.models.level_user import LevelUser
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
from zhenxun.models.user_console import UserConsole
|
from zhenxun.models.user_console import UserConsole
|
||||||
|
from zhenxun.services.auth_snapshot import AuthSnapshot, PluginSnapshot
|
||||||
from zhenxun.services.cache import CacheRegistry, cache_config
|
from zhenxun.services.cache import CacheRegistry, cache_config
|
||||||
from zhenxun.services.cache.config import CacheMode
|
from zhenxun.services.cache.config import CacheMode
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
@@ -34,6 +35,9 @@ def register_cache_types():
|
|||||||
)
|
)
|
||||||
CacheRegistry.register(CacheType.BAN, BanConsole, key_format="{user_id}_{group_id}")
|
CacheRegistry.register(CacheType.BAN, BanConsole, key_format="{user_id}_{group_id}")
|
||||||
CacheRegistry.register(CacheType.TEMP, None, 3600)
|
CacheRegistry.register(CacheType.TEMP, None, 3600)
|
||||||
|
CacheRegistry.register(CacheType.AUTH_SNAPSHOT, AuthSnapshot)
|
||||||
|
CacheRegistry.register(CacheType.PLUGIN_SNAPSHOT, PluginSnapshot)
|
||||||
|
|
||||||
if cache_config.cache_mode == CacheMode.NONE:
|
if cache_config.cache_mode == CacheMode.NONE:
|
||||||
logger.info("缓存功能已禁用,将直接从数据库获取数据")
|
logger.info("缓存功能已禁用,将直接从数据库获取数据")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ class SnapshotBuilder:
|
|||||||
queries.append(f"""
|
queries.append(f"""
|
||||||
SELECT 'level_global' as query_type, NULL as gold, user_level,
|
SELECT 'level_global' as query_type, NULL as gold, user_level,
|
||||||
NULL as ban_time, NULL as duration
|
NULL as ban_time, NULL as duration
|
||||||
FROM level_user WHERE user_id = {ph()} AND group_id IS NULL
|
FROM level_users WHERE user_id = {ph()} AND group_id IS NULL
|
||||||
""")
|
""")
|
||||||
params.append(user_id)
|
params.append(user_id)
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ class SnapshotBuilder:
|
|||||||
queries.append(f"""
|
queries.append(f"""
|
||||||
SELECT 'level_group' as query_type, NULL as gold, user_level,
|
SELECT 'level_group' as query_type, NULL as gold, user_level,
|
||||||
NULL as ban_time, NULL as duration
|
NULL as ban_time, NULL as duration
|
||||||
FROM level_user
|
FROM level_users
|
||||||
WHERE user_id = {ph()} AND group_id = {ph()}
|
WHERE user_id = {ph()} AND group_id = {ph()}
|
||||||
""")
|
""")
|
||||||
params.extend([user_id, group_id])
|
params.extend([user_id, group_id])
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import asyncio
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from nonebot.adapters import Bot, Event
|
from nonebot.adapters import Bot, Event
|
||||||
from nonebot.exception import IgnoredException
|
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
from nonebot_plugin_uninfo import Uninfo
|
from nonebot_plugin_uninfo import Uninfo
|
||||||
@@ -19,10 +18,11 @@ from zhenxun.utils.enum import BlockType, GoldHandle
|
|||||||
from zhenxun.utils.platform import PlatformUtils
|
from zhenxun.utils.platform import PlatformUtils
|
||||||
from zhenxun.utils.utils import get_entity_ids
|
from zhenxun.utils.utils import get_entity_ids
|
||||||
|
|
||||||
|
from .exception import IsSuperuserException, SkipPluginException
|
||||||
from .models import AuthSnapshot, PluginSnapshot
|
from .models import AuthSnapshot, PluginSnapshot
|
||||||
from .service import AuthSnapshotService, PluginSnapshotService
|
from .service import AuthSnapshotService, PluginSnapshotService
|
||||||
|
|
||||||
LOG_COMMAND = "auth_checker_v2"
|
LOG_COMMAND = "AuthSnapshotChecker"
|
||||||
WARNING_THRESHOLD = 0.5 # 警告阈值(秒)
|
WARNING_THRESHOLD = 0.5 # 警告阈值(秒)
|
||||||
|
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ class OptimizedAuthChecker:
|
|||||||
|
|
||||||
if not module:
|
if not module:
|
||||||
result.fail("Matcher插件名称不存在...")
|
result.fail("Matcher插件名称不存在...")
|
||||||
raise IgnoredException(result.skip_reason)
|
raise SkipPluginException(result.skip_reason)
|
||||||
|
|
||||||
# 2. 获取权限快照(第一次查询)
|
# 2. 获取权限快照(第一次查询)
|
||||||
snapshot_start = time.time()
|
snapshot_start = time.time()
|
||||||
@@ -96,12 +96,12 @@ class OptimizedAuthChecker:
|
|||||||
|
|
||||||
if not plugin_snapshot:
|
if not plugin_snapshot:
|
||||||
result.fail(f"插件:{module} 数据不存在...")
|
result.fail(f"插件:{module} 数据不存在...")
|
||||||
raise IgnoredException(result.skip_reason)
|
raise SkipPluginException(result.skip_reason)
|
||||||
|
|
||||||
# 4. 检查是否为隐藏插件
|
# 4. 检查是否为隐藏插件
|
||||||
if plugin_snapshot.is_hidden():
|
if plugin_snapshot.is_hidden():
|
||||||
result.fail(f"插件: {plugin_snapshot.name}:{module} 为HIDDEN...")
|
result.fail(f"插件: {plugin_snapshot.name}:{module} 为HIDDEN...")
|
||||||
raise IgnoredException(result.skip_reason)
|
return
|
||||||
|
|
||||||
# 5. 检查超级用户
|
# 5. 检查超级用户
|
||||||
is_superuser = session.user.id in bot.config.superusers
|
is_superuser = session.user.id in bot.config.superusers
|
||||||
@@ -122,7 +122,7 @@ class OptimizedAuthChecker:
|
|||||||
# 7. 处理检查结果
|
# 7. 处理检查结果
|
||||||
if not result.passed:
|
if not result.passed:
|
||||||
logger.info(result.skip_reason, LOG_COMMAND, session=session)
|
logger.info(result.skip_reason, LOG_COMMAND, session=session)
|
||||||
raise IgnoredException(result.skip_reason)
|
raise SkipPluginException(result.skip_reason)
|
||||||
|
|
||||||
# 8. 超级用户跳过后续限制
|
# 8. 超级用户跳过后续限制
|
||||||
if is_superuser:
|
if is_superuser:
|
||||||
@@ -162,12 +162,13 @@ class OptimizedAuthChecker:
|
|||||||
logger.error(
|
logger.error(
|
||||||
f"扣除金币超时,模块: {module}", LOG_COMMAND, session=session
|
f"扣除金币超时,模块: {module}", LOG_COMMAND, session=session
|
||||||
)
|
)
|
||||||
|
except IsSuperuserException:
|
||||||
except IgnoredException:
|
raise
|
||||||
|
except SkipPluginException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"权限检查异常: {e}", LOG_COMMAND, session=session, e=e)
|
logger.error(f"权限检查异常: {e}", LOG_COMMAND, session=session, e=e)
|
||||||
raise IgnoredException("权限检查异常") from e
|
raise SkipPluginException("权限检查异常") from e
|
||||||
finally:
|
finally:
|
||||||
# 记录总执行时间
|
# 记录总执行时间
|
||||||
total_time = time.time() - start_time
|
total_time = time.time() - start_time
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class AuthSnapshotService:
|
|||||||
# 2. 尝试从Redis获取
|
# 2. 尝试从Redis获取
|
||||||
if not force_refresh and cache_config.cache_mode != CacheMode.NONE:
|
if not force_refresh and cache_config.cache_mode != CacheMode.NONE:
|
||||||
try:
|
try:
|
||||||
cached = await CacheRoot.get(CacheType.TEMP, cache_key)
|
cached = await CacheRoot.get(CacheType.AUTH_SNAPSHOT, cache_key)
|
||||||
if cached and isinstance(cached, dict):
|
if cached and isinstance(cached, dict):
|
||||||
snapshot = AuthSnapshot.model_validate(cached)
|
snapshot = AuthSnapshot.model_validate(cached)
|
||||||
if not snapshot.is_expired(AUTH_REDIS_TTL):
|
if not snapshot.is_expired(AUTH_REDIS_TTL):
|
||||||
@@ -201,7 +201,7 @@ class AuthSnapshotService:
|
|||||||
"""异步存入Redis"""
|
"""异步存入Redis"""
|
||||||
try:
|
try:
|
||||||
await CacheRoot.set(
|
await CacheRoot.set(
|
||||||
CacheType.TEMP,
|
CacheType.AUTH_SNAPSHOT,
|
||||||
cache_key,
|
cache_key,
|
||||||
snapshot.model_dump(),
|
snapshot.model_dump(),
|
||||||
expire=AUTH_REDIS_TTL,
|
expire=AUTH_REDIS_TTL,
|
||||||
@@ -329,7 +329,7 @@ class PluginSnapshotService:
|
|||||||
# 2. 尝试从Redis获取
|
# 2. 尝试从Redis获取
|
||||||
if not force_refresh and cache_config.cache_mode != CacheMode.NONE:
|
if not force_refresh and cache_config.cache_mode != CacheMode.NONE:
|
||||||
try:
|
try:
|
||||||
cached = await CacheRoot.get(CacheType.PLUGINS, cache_key)
|
cached = await CacheRoot.get(CacheType.PLUGIN_SNAPSHOT, cache_key)
|
||||||
if cached and isinstance(cached, dict):
|
if cached and isinstance(cached, dict):
|
||||||
snapshot = PluginSnapshot.model_validate(cached)
|
snapshot = PluginSnapshot.model_validate(cached)
|
||||||
if not snapshot.is_expired(PLUGIN_REDIS_TTL):
|
if not snapshot.is_expired(PLUGIN_REDIS_TTL):
|
||||||
@@ -395,7 +395,7 @@ class PluginSnapshotService:
|
|||||||
"""异步存入Redis"""
|
"""异步存入Redis"""
|
||||||
try:
|
try:
|
||||||
await CacheRoot.set(
|
await CacheRoot.set(
|
||||||
CacheType.PLUGINS,
|
CacheType.PLUGIN_SNAPSHOT,
|
||||||
cache_key,
|
cache_key,
|
||||||
snapshot.model_dump(),
|
snapshot.model_dump(),
|
||||||
expire=PLUGIN_REDIS_TTL,
|
expire=PLUGIN_REDIS_TTL,
|
||||||
@@ -420,7 +420,7 @@ class PluginSnapshotService:
|
|||||||
# 清理Redis缓存
|
# 清理Redis缓存
|
||||||
if cache_config.cache_mode != CacheMode.NONE:
|
if cache_config.cache_mode != CacheMode.NONE:
|
||||||
try:
|
try:
|
||||||
await CacheRoot.delete(CacheType.PLUGINS, cache_key)
|
await CacheRoot.delete(CacheType.PLUGIN_SNAPSHOT, cache_key)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user