mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-08 05:10:01 +08:00
bugfix:修复notice事件扩散问题 (#2132)
* bugfix:修复notice事件扩散问题 * 优化并发调度 * bugfix:修复签到样式 * bugfix:功能调用统计修复 * bugfix:修复私聊时功能调用统计显示已退群问题 * 提高插件适配兼容性 * 优化发送队列 * 修改权限检查设计 * 继续修改权限检查设计 * 完善权限检查设计 * 优化sqlite配置 * 优化数据库初始化 * 代码整理,无用代码清理 * bugfix:修复启动时数据库校验问题 * bugfix:修复预算裁剪过于激进问题
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from dataclasses import dataclass, field
|
||||
import time
|
||||
from typing import Any, Protocol
|
||||
|
||||
from nonebot_plugin_uninfo import Uninfo
|
||||
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.utils import EntityIDs
|
||||
|
||||
from .auth.config import LOGGER_COMMAND
|
||||
from .auth.utils import send_message
|
||||
|
||||
AsyncAction = Callable[[], Awaitable[None]]
|
||||
|
||||
|
||||
class SyncReservation(Protocol):
|
||||
def commit(self) -> None: ...
|
||||
|
||||
def release(self) -> None: ...
|
||||
|
||||
|
||||
class AsyncReservation(Protocol):
|
||||
async def commit(self) -> None: ...
|
||||
|
||||
async def release(self) -> None: ...
|
||||
|
||||
|
||||
ReservationLike = AsyncAction | SyncReservation | AsyncReservation
|
||||
SideEffectKind = str
|
||||
SideEffectState = str
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class SideEffectReservation:
|
||||
kind: SideEffectKind
|
||||
reservation: ReservationLike
|
||||
amount: int = 0
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
state: SideEffectState = "reserved"
|
||||
reserved_at: float = field(default_factory=time.monotonic)
|
||||
committed_at: float | None = None
|
||||
released_at: float | None = None
|
||||
reason: str | None = None
|
||||
|
||||
@property
|
||||
def should_auto_unblock(self) -> bool:
|
||||
return bool(getattr(self.reservation, "should_auto_unblock", False))
|
||||
|
||||
|
||||
async def _maybe_await(value: Any) -> None:
|
||||
if hasattr(value, "__await__"):
|
||||
await value
|
||||
|
||||
|
||||
async def _commit_reservation(reservation: ReservationLike) -> None:
|
||||
commit = getattr(reservation, "commit", None)
|
||||
if callable(commit):
|
||||
await _maybe_await(commit())
|
||||
return
|
||||
if callable(reservation):
|
||||
await reservation()
|
||||
|
||||
|
||||
async def _release_reservation(reservation: ReservationLike) -> None:
|
||||
release = getattr(reservation, "release", None)
|
||||
if callable(release):
|
||||
await _maybe_await(release())
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class SideEffectCommit:
|
||||
"""权限链副作用提交器。
|
||||
|
||||
第一阶段只封装既有调用点,不改变扣金币、限流提交、权限提示发送时机。
|
||||
"""
|
||||
|
||||
session: Uninfo
|
||||
module: str
|
||||
owner_matcher_id: int | None = None
|
||||
limit_entity: EntityIDs | None = None
|
||||
_reservations: dict[SideEffectKind, SideEffectReservation] = field(
|
||||
default_factory=dict
|
||||
)
|
||||
committed: bool = False
|
||||
|
||||
@property
|
||||
def limit_should_auto_unblock(self) -> bool:
|
||||
record = self._reservations.get("limit")
|
||||
return bool(record and record.should_auto_unblock)
|
||||
|
||||
@property
|
||||
def has_pending(self) -> bool:
|
||||
return any(record.state == "reserved" for record in self._reservations.values())
|
||||
|
||||
@property
|
||||
def pending_kinds(self) -> tuple[str, ...]:
|
||||
return tuple(
|
||||
kind
|
||||
for kind, record in self._reservations.items()
|
||||
if record.state == "reserved"
|
||||
)
|
||||
|
||||
def snapshot(self) -> dict[str, Any]:
|
||||
return {
|
||||
"module": self.module,
|
||||
"committed": self.committed,
|
||||
"pending": list(self.pending_kinds),
|
||||
"reservations": {
|
||||
kind: {
|
||||
"state": record.state,
|
||||
"amount": record.amount,
|
||||
"metadata": record.metadata,
|
||||
"reason": record.reason,
|
||||
}
|
||||
for kind, record in self._reservations.items()
|
||||
},
|
||||
}
|
||||
|
||||
async def send_permission_tip(
|
||||
self,
|
||||
message: list | str,
|
||||
check_tag: str | None = None,
|
||||
*,
|
||||
background: bool = False,
|
||||
timeout: float | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
tip_coro = send_message(
|
||||
self.session,
|
||||
message,
|
||||
check_tag,
|
||||
background=background,
|
||||
)
|
||||
if timeout and not background:
|
||||
await asyncio.wait_for(tip_coro, timeout=timeout)
|
||||
else:
|
||||
await tip_coro
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("发送权限提示超时", LOGGER_COMMAND, session=self.session)
|
||||
|
||||
async def reduce_gold(
|
||||
self,
|
||||
func: ReservationLike,
|
||||
) -> None:
|
||||
await self.reserve_gold(func)
|
||||
await self.commit_gold()
|
||||
|
||||
async def reserve(
|
||||
self,
|
||||
kind: SideEffectKind,
|
||||
reservation: ReservationLike,
|
||||
*,
|
||||
amount: int = 0,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
await self.release(kind, f"replace_{kind}_reservation")
|
||||
self._reservations[kind] = SideEffectReservation(
|
||||
kind=kind,
|
||||
reservation=reservation,
|
||||
amount=amount,
|
||||
metadata=metadata or {},
|
||||
)
|
||||
|
||||
async def commit(self, kind: SideEffectKind) -> None:
|
||||
record = self._reservations.get(kind)
|
||||
if record is None or record.state != "reserved":
|
||||
return
|
||||
try:
|
||||
await _commit_reservation(record.reservation)
|
||||
except Exception:
|
||||
record.reason = "commit_failed"
|
||||
raise
|
||||
record.state = "committed"
|
||||
record.committed_at = time.monotonic()
|
||||
|
||||
async def release(
|
||||
self,
|
||||
kind: SideEffectKind,
|
||||
reason: str | None = None,
|
||||
) -> None:
|
||||
record = self._reservations.get(kind)
|
||||
if record is None or record.state != "reserved":
|
||||
return
|
||||
try:
|
||||
await _release_reservation(record.reservation)
|
||||
finally:
|
||||
record.state = "released"
|
||||
record.released_at = time.monotonic()
|
||||
record.reason = reason
|
||||
|
||||
async def reserve_limit(self, reservation: ReservationLike) -> None:
|
||||
await self.reserve("limit", reservation)
|
||||
|
||||
async def commit_limit(
|
||||
self,
|
||||
reservation: ReservationLike | None = None,
|
||||
) -> None:
|
||||
if reservation is not None:
|
||||
await self.reserve_limit(reservation)
|
||||
await self.commit("limit")
|
||||
|
||||
async def release_limit(self, reason: str | None = None) -> None:
|
||||
await self.release("limit", reason)
|
||||
|
||||
async def reserve_gold(
|
||||
self,
|
||||
reservation: ReservationLike,
|
||||
*,
|
||||
amount: int = 0,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
await self.reserve(
|
||||
"gold",
|
||||
reservation,
|
||||
amount=amount,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
async def commit_gold(self) -> None:
|
||||
await self.commit("gold")
|
||||
|
||||
async def rollback_gold(self, reason: str | None = None) -> None:
|
||||
await self.release("gold", reason)
|
||||
|
||||
async def rollback_all(self, reason: str | None = None) -> None:
|
||||
for kind in list(self._reservations):
|
||||
await self.release(kind, reason)
|
||||
|
||||
async def commit_all(self, *, order: Sequence[str] = ("gold", "limit")) -> None:
|
||||
for name in order:
|
||||
await self.commit(name)
|
||||
self.committed = True
|
||||
Reference in New Issue
Block a user