mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-28 16:20:56 +08:00
* bugfix:更换数据库初始化超时路径以修复连接超时问题 * 移除部分观测链路 * 细节修改 * 权限检查细节修改2 * bugfix:修复金币懒加载造成插件金币消耗不了的问题 * bugfix:整理鉴权逻辑 * 完善缓存系统 * 优化官端使用 * bugfix:增加官机和频道的前导统一剥离,新增获取群列表自愈 * bugfix:修复导入问题
291 lines
11 KiB
Python
291 lines
11 KiB
Python
import asyncio
|
|
from collections.abc import Awaitable, Callable
|
|
import contextlib
|
|
import importlib
|
|
from typing import Any, cast
|
|
|
|
from nonebot.adapters import Bot, Event
|
|
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
|
from nonebot.log import logger
|
|
|
|
_PATCHED = False
|
|
_ORIGINAL_FETCH: Callable[..., Awaitable[Any]] | None = None
|
|
_ORIGINAL_ONEBOT11_GROUP_MESSAGE: Callable[..., Awaitable[dict[str, Any]]] | None = None
|
|
_ORIGINAL_QQ_C2C_MESSAGE: Callable[..., Awaitable[dict[str, Any]]] | None = None
|
|
_ORIGINAL_QQ_GROUP_AT_MESSAGE: Callable[..., Awaitable[dict[str, Any]]] | None = None
|
|
_ORIGINAL_QQ_GUILD_MESSAGE: Callable[..., Awaitable[dict[str, Any]]] | None = None
|
|
|
|
|
|
def _sender_value(sender: Any, key: str, default: Any = None) -> Any:
|
|
value = getattr(sender, key, default)
|
|
return default if value is None else value
|
|
|
|
|
|
def _event_value(event: Event, key: str, default: Any = None) -> Any:
|
|
value = getattr(event, key, default)
|
|
return default if value is None else value
|
|
|
|
|
|
def _event_group_name(event: Event) -> str | None:
|
|
group_name = _event_value(event, "group_name")
|
|
if isinstance(group_name, str) and group_name:
|
|
return group_name
|
|
group = _event_value(event, "group")
|
|
if group is not None:
|
|
name = _sender_value(group, "name") or _sender_value(group, "group_name")
|
|
if isinstance(name, str) and name:
|
|
return name
|
|
return None
|
|
|
|
|
|
def _has_compatible_onebot11_sender(event: Event) -> bool:
|
|
if getattr(event, "_zx_uninfo_full_fetch", False):
|
|
return False
|
|
sender = _event_value(event, "sender")
|
|
if sender is None:
|
|
return False
|
|
return (
|
|
_event_value(event, "user_id") is not None
|
|
and _event_value(event, "group_id") is not None
|
|
and _sender_value(sender, "nickname") is not None
|
|
and _sender_value(sender, "role") is not None
|
|
)
|
|
|
|
|
|
async def _fast_onebot11_group_message(bot: Bot, event: Event) -> dict[str, Any]:
|
|
"""Build Uninfo session data from OneBot v11 group message event fields.
|
|
|
|
nonebot-plugin-uninfo's default OneBot v11 fetcher always calls
|
|
get_group_info and get_group_member_info for group messages. For normal
|
|
matcher rule checks, event-provided sender fields are enough and avoid
|
|
multiplying protocol API calls by the number of candidate matchers.
|
|
"""
|
|
|
|
original = _ORIGINAL_ONEBOT11_GROUP_MESSAGE
|
|
if not _has_compatible_onebot11_sender(event):
|
|
if original is not None:
|
|
return await original(bot, event)
|
|
logger.debug("Uninfo OneBot11 fast fetch fallback unavailable")
|
|
|
|
sender = _event_value(event, "sender")
|
|
user_id = str(_event_value(event, "user_id", ""))
|
|
group_id = str(_event_value(event, "group_id", ""))
|
|
nickname = _sender_value(sender, "nickname", "")
|
|
card = _sender_value(sender, "card", "") or nickname
|
|
return {
|
|
"group_id": group_id,
|
|
"group_name": _event_group_name(event),
|
|
"user_id": user_id,
|
|
"name": nickname,
|
|
"nickname": card,
|
|
"card": card,
|
|
"role": _sender_value(sender, "role", "member"),
|
|
"join_time": _event_value(event, "join_time"),
|
|
"gender": _sender_value(sender, "sex", "unknown") or "unknown",
|
|
}
|
|
|
|
|
|
def _qq_bot_app_id(bot: Bot) -> str:
|
|
bot_info = getattr(bot, "bot_info", None)
|
|
app_id = getattr(bot_info, "id", None)
|
|
return str(app_id or getattr(bot, "self_id", ""))
|
|
|
|
|
|
async def _fast_qq_c2c_message(bot: Bot, event: Event) -> dict[str, Any]:
|
|
"""Build Uninfo session for QQ official C2C messages from event fields."""
|
|
|
|
author = _event_value(event, "author")
|
|
user_id = str(
|
|
_sender_value(author, "user_openid")
|
|
or _sender_value(author, "id")
|
|
or _event_value(event, "user_id", "")
|
|
)
|
|
username = str(_sender_value(author, "username", "") or "")
|
|
return {
|
|
"user_id": user_id,
|
|
"name": username,
|
|
"nickname": username,
|
|
"avatar": f"https://q.qlogo.cn/qqapp/{_qq_bot_app_id(bot)}/{user_id}/100",
|
|
}
|
|
|
|
|
|
async def _fast_qq_group_at_message(bot: Bot, event: Event) -> dict[str, Any]:
|
|
"""Build Uninfo session for QQ official group-at messages from event fields."""
|
|
|
|
author = _event_value(event, "author")
|
|
user_id = str(
|
|
_sender_value(author, "member_openid")
|
|
or _sender_value(author, "id")
|
|
or _event_value(event, "user_id", "")
|
|
)
|
|
username = str(_sender_value(author, "username", "") or "")
|
|
group_id = str(
|
|
_event_value(event, "group_openid") or _event_value(event, "group_id") or ""
|
|
)
|
|
return {
|
|
"user_id": user_id,
|
|
"name": username,
|
|
"nickname": username,
|
|
"avatar": f"https://q.qlogo.cn/qqapp/{_qq_bot_app_id(bot)}/{user_id}/100",
|
|
"group_id": group_id,
|
|
}
|
|
|
|
|
|
async def _fast_qq_guild_message(bot: Bot, event: Event) -> dict[str, Any]:
|
|
"""Build Uninfo session for QQ official guild/channel messages locally.
|
|
|
|
nonebot-plugin-uninfo enriches guild messages through remote guild/channel
|
|
APIs. Runtime auth only needs stable scene/user ids, so avoid remote calls
|
|
during matcher fanout.
|
|
"""
|
|
|
|
author = _event_value(event, "author")
|
|
member = _event_value(event, "member")
|
|
guild_id = str(_event_value(event, "guild_id", "") or "")
|
|
channel_id = str(_event_value(event, "channel_id", "") or "")
|
|
user_id = str(_sender_value(author, "id", "") or "")
|
|
nickname = str(_sender_value(member, "nick", "") or "")
|
|
username = str(_sender_value(author, "username", "") or "")
|
|
base: dict[str, Any] = {
|
|
"user_id": user_id,
|
|
"name": username,
|
|
"nickname": nickname or username,
|
|
"avatar": _sender_value(author, "avatar"),
|
|
"guild_id": guild_id,
|
|
"channel_id": channel_id,
|
|
"guild_name": "",
|
|
"guild_avatar": None,
|
|
"channel_name": "",
|
|
"channel_type": -1,
|
|
}
|
|
roles = _sender_value(member, "roles")
|
|
if roles is not None:
|
|
base["roles"] = roles
|
|
joined_at = _sender_value(member, "joined_at")
|
|
if joined_at is not None:
|
|
base["joined_at"] = joined_at
|
|
return base
|
|
|
|
|
|
async def _singleflight_fetch(self: Any, bot: Bot, event: Event) -> Any:
|
|
original = _ORIGINAL_FETCH
|
|
if original is None:
|
|
return None
|
|
|
|
try:
|
|
sess_id = self.get_session_id(event)
|
|
except ValueError:
|
|
return await original(self, bot, event)
|
|
|
|
session_cache = getattr(self, "session_cache", None)
|
|
if isinstance(session_cache, dict) and sess_id in session_cache:
|
|
return session_cache[sess_id]
|
|
|
|
inflight = getattr(self, "_zx_fetch_inflight", None)
|
|
if not isinstance(inflight, dict):
|
|
inflight = {}
|
|
setattr(self, "_zx_fetch_inflight", inflight)
|
|
|
|
key = (str(getattr(bot, "self_id", "")), event.__class__, sess_id)
|
|
task = inflight.get(key)
|
|
if task is None or task.done():
|
|
task = asyncio.ensure_future(original(self, bot, event))
|
|
inflight[key] = task
|
|
try:
|
|
return await task
|
|
finally:
|
|
if inflight.get(key) is task and task.done():
|
|
inflight.pop(key, None)
|
|
|
|
|
|
def apply_uninfo_onebot11_patch() -> None:
|
|
global _ORIGINAL_FETCH, _ORIGINAL_ONEBOT11_GROUP_MESSAGE, _PATCHED
|
|
global _ORIGINAL_QQ_C2C_MESSAGE, _ORIGINAL_QQ_GROUP_AT_MESSAGE
|
|
global _ORIGINAL_QQ_GUILD_MESSAGE
|
|
if _PATCHED:
|
|
return
|
|
|
|
with contextlib.suppress(Exception):
|
|
from nonebot_plugin_uninfo.adapters.onebot11.main import fetcher
|
|
|
|
original_endpoint = fetcher.endpoint.get(GroupMessageEvent)
|
|
if not getattr(original_endpoint, "__zhenxun_fast_onebot11__", False):
|
|
_ORIGINAL_ONEBOT11_GROUP_MESSAGE = cast(
|
|
Callable[..., Awaitable[dict[str, Any]]] | None,
|
|
original_endpoint,
|
|
)
|
|
setattr(_fast_onebot11_group_message, "__zhenxun_fast_onebot11__", True)
|
|
fetcher.endpoint[GroupMessageEvent] = _fast_onebot11_group_message
|
|
|
|
with contextlib.suppress(Exception):
|
|
qq_event_module = importlib.import_module("nonebot.adapters.qq.event")
|
|
AtMessageCreateEvent = getattr(qq_event_module, "AtMessageCreateEvent")
|
|
C2CMessageCreateEvent = getattr(qq_event_module, "C2CMessageCreateEvent")
|
|
DirectMessageCreateEvent = getattr(qq_event_module, "DirectMessageCreateEvent")
|
|
GroupAtMessageCreateEvent = getattr(
|
|
qq_event_module,
|
|
"GroupAtMessageCreateEvent",
|
|
)
|
|
GroupMessageCreateEvent = getattr(
|
|
qq_event_module,
|
|
"GroupMessageCreateEvent",
|
|
)
|
|
MessageCreateEvent = getattr(qq_event_module, "MessageCreateEvent")
|
|
from nonebot_plugin_uninfo.adapters.qq.main import fetcher as qq_fetcher
|
|
|
|
original_c2c = qq_fetcher.endpoint.get(C2CMessageCreateEvent)
|
|
if not getattr(original_c2c, "__zhenxun_fast_qq__", False):
|
|
_ORIGINAL_QQ_C2C_MESSAGE = cast(
|
|
Callable[..., Awaitable[dict[str, Any]]] | None,
|
|
original_c2c,
|
|
)
|
|
setattr(_fast_qq_c2c_message, "__zhenxun_fast_qq__", True)
|
|
qq_fetcher.endpoint[C2CMessageCreateEvent] = _fast_qq_c2c_message
|
|
|
|
for event_type in (GroupMessageCreateEvent, GroupAtMessageCreateEvent):
|
|
original_group_at = qq_fetcher.endpoint.get(event_type)
|
|
if getattr(original_group_at, "__zhenxun_fast_qq__", False):
|
|
continue
|
|
if _ORIGINAL_QQ_GROUP_AT_MESSAGE is None and original_group_at is not None:
|
|
_ORIGINAL_QQ_GROUP_AT_MESSAGE = cast(
|
|
Callable[..., Awaitable[dict[str, Any]]],
|
|
original_group_at,
|
|
)
|
|
setattr(_fast_qq_group_at_message, "__zhenxun_fast_qq__", True)
|
|
qq_fetcher.endpoint[event_type] = _fast_qq_group_at_message
|
|
|
|
for event_type in (
|
|
MessageCreateEvent,
|
|
AtMessageCreateEvent,
|
|
DirectMessageCreateEvent,
|
|
):
|
|
original_guild = qq_fetcher.endpoint.get(event_type)
|
|
if getattr(original_guild, "__zhenxun_fast_qq__", False):
|
|
continue
|
|
if _ORIGINAL_QQ_GUILD_MESSAGE is None and original_guild is not None:
|
|
_ORIGINAL_QQ_GUILD_MESSAGE = cast(
|
|
Callable[..., Awaitable[dict[str, Any]]],
|
|
original_guild,
|
|
)
|
|
setattr(_fast_qq_guild_message, "__zhenxun_fast_qq__", True)
|
|
qq_fetcher.endpoint[event_type] = _fast_qq_guild_message
|
|
|
|
try:
|
|
from nonebot_plugin_uninfo.fetch import InfoFetcher
|
|
except Exception as e:
|
|
logger.warning("Uninfo patch skipped", e=e)
|
|
return
|
|
|
|
original_fetch = getattr(InfoFetcher, "fetch", None)
|
|
if getattr(original_fetch, "__zhenxun_singleflight__", False):
|
|
_PATCHED = True
|
|
return
|
|
if original_fetch is None:
|
|
return
|
|
|
|
_ORIGINAL_FETCH = cast(Callable[..., Awaitable[Any]], original_fetch)
|
|
setattr(_singleflight_fetch, "__zhenxun_singleflight__", True)
|
|
setattr(InfoFetcher, "fetch", _singleflight_fetch)
|
|
_PATCHED = True
|
|
logger.debug("Uninfo fast fetch and singleflight patch applied")
|