From b37e55648b19a1046153ed2cfc0b24cf11ac6c9f Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Tue, 13 Aug 2024 00:47:39 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20hook=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E9=99=A4poke=E5=A4=96=E7=9A=84notice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builtin_plugins/hooks/_auth_checker.py | 28 ++++++++++++------- zhenxun/builtin_plugins/hooks/auth_hook.py | 12 ++++++-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/zhenxun/builtin_plugins/hooks/_auth_checker.py b/zhenxun/builtin_plugins/hooks/_auth_checker.py index e103cf95..03c0bd90 100644 --- a/zhenxun/builtin_plugins/hooks/_auth_checker.py +++ b/zhenxun/builtin_plugins/hooks/_auth_checker.py @@ -1,4 +1,5 @@ -from nonebot.adapters import Bot +from nonebot.adapters import Bot, Event +from nonebot.adapters.onebot.v11 import PokeNotifyEvent from nonebot.exception import IgnoredException from nonebot.matcher import Matcher from nonebot_plugin_alconna import At, UniMsg @@ -183,6 +184,7 @@ class AuthChecker: async def auth( self, matcher: Matcher, + event: Event, bot: Bot, session: EventSession, message: UniMsg, @@ -203,6 +205,9 @@ class AuthChecker: if not group_id: group_id = channel_id channel_id = None + if matcher.type == "notice" and not isinstance(event, PokeNotifyEvent): + """过滤除poke外的notice""" + return if user_id and matcher.plugin and (module_path := matcher.plugin.module_name): try: user = await UserConsole.get_user(user_id, session.platform) @@ -225,7 +230,7 @@ class AuthChecker: raise IsSuperuserException() await self.auth_group(plugin, session, message) await self.auth_admin(plugin, session) - await self.auth_plugin(plugin, session) + await self.auth_plugin(plugin, session, event) await self.auth_limit(plugin, session) except IsSuperuserException: logger.debug( @@ -278,7 +283,9 @@ class AuthChecker: plugin.module, user_id, group_id, channel_id, session ) - async def auth_plugin(self, plugin: PluginInfo, session: EventSession): + async def auth_plugin( + self, plugin: PluginInfo, session: EventSession, event: Event + ): """插件状态 参数: @@ -288,6 +295,7 @@ class AuthChecker: user_id = session.id1 group_id = session.id3 channel_id = session.id2 + is_poke = isinstance(event, PokeNotifyEvent) if not group_id: group_id = channel_id channel_id = None @@ -297,7 +305,7 @@ class AuthChecker: group_id, plugin.module, channel_id ): """超级用户群组插件状态""" - if self._flmt_s.check(group_id or user_id): + if self._flmt_s.check(group_id or user_id) and not is_poke: self._flmt_s.start_cd(group_id or user_id) await MessageUtils.build_message( "超级管理员禁用了该群此功能..." @@ -312,7 +320,7 @@ class AuthChecker: group_id, plugin.module, channel_id ): """群组插件状态""" - if self._flmt_s.check(group_id or user_id): + if self._flmt_s.check(group_id or user_id) and not is_poke: self._flmt_s.start_cd(group_id or user_id) await MessageUtils.build_message("该群未开启此功能...").send( reply_to=True @@ -326,7 +334,7 @@ class AuthChecker: if not plugin.status and plugin.block_type == BlockType.GROUP: """全局群组禁用""" try: - if self._flmt_c.check(group_id): + if self._flmt_c.check(group_id) and not is_poke: self._flmt_c.start_cd(group_id) await MessageUtils.build_message( "该功能在群组中已被禁用..." @@ -345,7 +353,7 @@ class AuthChecker: if not plugin.status and plugin.block_type == BlockType.PRIVATE: """全局私聊禁用""" try: - if self._flmt_c.check(user_id): + if self._flmt_c.check(user_id) and not is_poke: self._flmt_c.start_cd(user_id) await MessageUtils.build_message( "该功能在私聊中已被禁用..." @@ -365,14 +373,14 @@ class AuthChecker: if group_id: if await GroupConsole.is_super_group(group_id, channel_id): raise IsSuperuserException() - if self._flmt_s.check(group_id or user_id): - self._flmt_s.start_cd(group_id or user_id) - await MessageUtils.build_message("全局未开启此功能...").send() logger.debug( f"{plugin.name}({plugin.module}) 全局未开启此功能...", "HOOK", session=session, ) + if self._flmt_s.check(group_id or user_id) and not is_poke: + self._flmt_s.start_cd(group_id or user_id) + await MessageUtils.build_message("全局未开启此功能...").send() raise IgnoredException("全局未开启此功能...") async def auth_admin(self, plugin: PluginInfo, session: EventSession): diff --git a/zhenxun/builtin_plugins/hooks/auth_hook.py b/zhenxun/builtin_plugins/hooks/auth_hook.py index 938f8222..4f030d8c 100644 --- a/zhenxun/builtin_plugins/hooks/auth_hook.py +++ b/zhenxun/builtin_plugins/hooks/auth_hook.py @@ -11,8 +11,16 @@ from ._auth_checker import LimitManage, checker # # 权限检测 @run_preprocessor -async def _(matcher: Matcher, bot: Bot, session: EventSession, message: UniMsg): - await checker.auth(matcher, bot, session, message) +async def _( + matcher: Matcher, event: Event, bot: Bot, session: EventSession, message: UniMsg +): + await checker.auth( + matcher, + event, + bot, + session, + message, + ) # 解除命令block阻塞 From 2d0cee80c255b9faf60ed9aacf5e145c9c6651cb Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Tue, 13 Aug 2024 22:37:44 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8E=A8=20webui=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/plugins/statistics/statistics_hook.py | 14 ++- zhenxun/plugins/web_ui/__init__.py | 6 +- .../web_ui/api/tabs/manage/__init__.py | 111 ------------------ .../plugins/web_ui/api/tabs/manage/chat.py | 111 ++++++++++++++++++ 4 files changed, 123 insertions(+), 119 deletions(-) create mode 100644 zhenxun/plugins/web_ui/api/tabs/manage/chat.py diff --git a/zhenxun/plugins/statistics/statistics_hook.py b/zhenxun/plugins/statistics/statistics_hook.py index cb1f4b1f..d1fc4635 100644 --- a/zhenxun/plugins/statistics/statistics_hook.py +++ b/zhenxun/plugins/statistics/statistics_hook.py @@ -1,6 +1,7 @@ from datetime import datetime -from nonebot.adapters import Bot +from nonebot.adapters import Bot, Event +from nonebot.adapters.onebot.v11 import PokeNotifyEvent from nonebot.matcher import Matcher from nonebot.message import run_postprocessor from nonebot.plugin import PluginMetadata @@ -23,10 +24,17 @@ __plugin_meta__ = PluginMetadata( @run_postprocessor async def _( - matcher: Matcher, exception: Exception | None, bot: Bot, session: EventSession + matcher: Matcher, + exception: Exception | None, + bot: Bot, + session: EventSession, + event: Event, ): + if matcher.type == "notice" and not isinstance(event, PokeNotifyEvent): + """过滤除poke外的notice""" + return if session.id1: - plugin = await PluginInfo.get_or_none(module=matcher.plugin_name) + plugin = await PluginInfo.get_or_none(module=matcher.module_name) plugin_type = plugin.plugin_type if plugin else None if plugin_type == PluginType.NORMAL and matcher.plugin_name not in [ "update_info", diff --git a/zhenxun/plugins/web_ui/__init__.py b/zhenxun/plugins/web_ui/__init__.py index 37684372..8df59903 100644 --- a/zhenxun/plugins/web_ui/__init__.py +++ b/zhenxun/plugins/web_ui/__init__.py @@ -2,11 +2,7 @@ import asyncio import nonebot from fastapi import APIRouter, FastAPI -from nonebot.adapters.onebot.v11 import Bot, MessageEvent from nonebot.log import default_filter, default_format -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor -from nonebot.typing import T_State from zhenxun.configs.config import Config as gConfig from zhenxun.services.log import logger, logger_ @@ -17,7 +13,7 @@ from .api.tabs.database import router as database_router from .api.tabs.main import router as main_router from .api.tabs.main import ws_router as status_routes from .api.tabs.manage import router as manage_router -from .api.tabs.manage import ws_router as chat_routes +from .api.tabs.manage.chat import ws_router as chat_routes from .api.tabs.plugin_manage import router as plugin_router from .api.tabs.system import router as system_router from .auth import router as auth_router diff --git a/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py b/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py index 683b3a71..4f545200 100644 --- a/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py +++ b/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py @@ -1,19 +1,13 @@ -import re -from typing import Literal - import nonebot from fastapi import APIRouter from nonebot.adapters.onebot.v11 import ActionFailed -from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState from tortoise.functions import Count from zhenxun.configs.config import NICKNAME from zhenxun.models.ban_console import BanConsole from zhenxun.models.chat_history import ChatHistory from zhenxun.models.fg_request import FgRequest -from zhenxun.models.friend_user import FriendUser from zhenxun.models.group_console import GroupConsole -from zhenxun.models.group_member_info import GroupInfoUser from zhenxun.models.plugin_info import PluginInfo from zhenxun.models.statistics import Statistics from zhenxun.models.task_info import TaskInfo @@ -25,7 +19,6 @@ from zhenxun.utils.platform import PlatformUtils from ....base_model import Result from ....config import AVA_URL, GROUP_AVA_URL from ....utils import authentication -from ...logs.log_manager import LOG_STORAGE from .model import ( ClearRequest, DeleteFriend, @@ -36,8 +29,6 @@ from .model import ( GroupResult, HandleRequest, LeaveGroup, - Message, - MessageItem, Plugin, ReqResult, SendMessage, @@ -46,19 +37,8 @@ from .model import ( UserDetail, ) -ws_router = APIRouter() router = APIRouter(prefix="/manage") -SUB_PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))" - -GROUP_PATTERN = r".*?Message (-?\d*) from (\d*)@\[群:(\d*)] '(.*)'" - -PRIVATE_PATTERN = r".*?Message (-?\d*) from (\d*) '(.*)'" - -AT_PATTERN = r"\[CQ:at,qq=(.*)\]" - -IMAGE_PATTERN = r"\[image:file=.*,url=(.*);.*?\]" - @router.get( "/get_group_list", dependencies=[authentication()], description="获取群组列表" @@ -72,7 +52,6 @@ async def _(bot_id: str) -> Result: return Result.warning_("指定Bot未连接...") group_list_result = [] try: - group_info = {} group_list = await bots[bot_id].get_group_list() for g in group_list: gid = g["group_id"] @@ -459,93 +438,3 @@ async def _(param: SendMessage) -> Result: return Result.ok("发送成功!") return Result.warning_("指定Bot未连接...") return Result.warning_("无Bot连接...") - - -MSG_LIST = [] - -ID2NAME = {} - - -async def message_handle( - sub_log: str, type: Literal["private", "group"] -) -> Message | None: - global MSG_LIST, ID2NAME - pattern = PRIVATE_PATTERN if type == "private" else GROUP_PATTERN - msg_id = None - uid = None - gid = None - msg = None - img_list = re.findall(IMAGE_PATTERN, sub_log) - if r := re.search(pattern, sub_log): - if type == "private": - msg_id = r.group(1) - uid = r.group(2) - msg = r.group(3) - if uid not in ID2NAME: - if user := await FriendUser.get_or_none(user_id=uid): - ID2NAME[uid] = user.user_name or user.nickname - else: - msg_id = r.group(1) - uid = r.group(2) - gid = r.group(3) - msg = r.group(4) - if gid not in ID2NAME: - if user := await GroupInfoUser.get_or_none(user_id=uid, group_id=gid): - ID2NAME[uid] = user.user_name or user.nickname - if at_list := re.findall(AT_PATTERN, msg): - user_list = await GroupInfoUser.filter( - user_id__in=at_list, group_id=gid - ).all() - id2name = {u.user_id: (u.user_name or u.nickname) for u in user_list} - for qq in at_list: - msg = re.sub(rf"\[CQ:at,qq={qq}\]", f"@{id2name[qq] or ''}", msg) - if msg_id in MSG_LIST: - return - MSG_LIST.append(msg_id) - messages = [] - if msg and uid: - rep = re.split(r"\[CQ:image.*\]", msg) - if img_list: - for i in range(len(rep)): - messages.append(MessageItem(type="text", msg=rep[i])) - if i < len(img_list): - messages.append(MessageItem(type="img", msg=img_list[i])) - else: - messages = [MessageItem(type="text", msg=x) for x in rep] - return Message( - object_id=uid if type == "private" else gid, # type: ignore - user_id=uid, - group_id=gid, - message=messages, - name=ID2NAME.get(uid) or "", - ava_url=AVA_URL.format(uid), - ) - return None - - -@ws_router.websocket("/chat") -async def _(websocket: WebSocket): - await websocket.accept() - - async def log_listener(log: str): - global MSG_LIST, ID2NAME - sub_log = re.sub(SUB_PATTERN, "", log) - if "message.private.friend" in log: - if message := await message_handle(sub_log, "private"): - await websocket.send_json(message.dict()) - else: - if r := re.search(GROUP_PATTERN, sub_log): - if message := await message_handle(sub_log, "group"): - await websocket.send_json(message.dict()) - if len(MSG_LIST) > 30: - MSG_LIST = MSG_LIST[-1:] - - LOG_STORAGE.listeners.add(log_listener) - try: - while websocket.client_state == WebSocketState.CONNECTED: - recv = await websocket.receive() - except WebSocketDisconnect: - pass - finally: - LOG_STORAGE.listeners.remove(log_listener) - return diff --git a/zhenxun/plugins/web_ui/api/tabs/manage/chat.py b/zhenxun/plugins/web_ui/api/tabs/manage/chat.py new file mode 100644 index 00000000..8ee12274 --- /dev/null +++ b/zhenxun/plugins/web_ui/api/tabs/manage/chat.py @@ -0,0 +1,111 @@ +import re +from typing import Literal + +import nonebot +from fastapi import APIRouter +from nonebot import on_message +from nonebot.adapters.onebot.v11 import MessageEvent +from nonebot_plugin_alconna import At, Emoji, Hyper, Image, Text, UniMessage, UniMsg +from nonebot_plugin_session import EventSession +from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState + +from zhenxun.models.friend_user import FriendUser +from zhenxun.models.group_console import GroupConsole +from zhenxun.models.group_member_info import GroupInfoUser +from zhenxun.utils.depends import UserName + +from ....config import AVA_URL, GROUP_AVA_URL +from .model import Message, MessageItem + +driver = nonebot.get_driver() + +ws_conn: WebSocket | None = None + +ID2NAME = {} + +ID_LIST = [] + +ws_router = APIRouter() + +matcher = on_message(block=False, priority=1) + + +@driver.on_shutdown +async def _(): + if ws_conn: + await ws_conn.close() + + +@ws_router.websocket("/chat") +async def _(websocket: WebSocket): + global ws_conn + await websocket.accept() + if not ws_conn: + ws_conn = websocket + try: + while websocket.client_state == WebSocketState.CONNECTED: + recv = await websocket.receive() + except WebSocketDisconnect: + ws_conn = None + + +async def message_handle( + message: UniMsg, + group_id: str | None, +): + messages = [] + for m in message: + if isinstance(m, (Text, str)): + messages.append(MessageItem(type="text", msg=str(m))) + elif isinstance(m, Image): + if m.url: + messages.append(MessageItem(type="img", msg=m.url)) + elif isinstance(m, At): + if group_id: + if m.target == "0": + uname = "全体成员" + else: + uname = m.target + if group_id not in ID2NAME: + ID2NAME[group_id] = {} + if m.target in ID2NAME[group_id]: + uname = ID2NAME[group_id][m.target] + else: + if group_user := await GroupInfoUser.get_or_none( + user_id=m.target, group_id=group_id + ): + uname = group_user.user_name + if m.target not in ID2NAME[group_id]: + ID2NAME[group_id][m.target] = uname + messages.append(MessageItem(type="at", msg=f"@{uname}")) + # elif isinstance(m, Emoji): + # messages.append(MessageItem(type="text", msg=f"[emoji]")) + elif isinstance(m, Hyper): + messages.append(MessageItem(type="text", msg=f"[分享消息]")) + return messages + + +@matcher.handle() +async def _( + message: UniMsg, event: MessageEvent, session: EventSession, uname: str = UserName() +): + global ws_conn, ID2NAME, ID_LIST + uid = session.id1 + gid = session.id3 or session.id2 + if ws_conn and uid: + msg_id = event.message_id + if msg_id in ID_LIST: + return + ID_LIST.append(msg_id) + if len(ID_LIST) > 50: + ID_LIST = ID_LIST[40:] + messages = await message_handle(message, gid) + data = Message( + object_id=gid or uid, + user_id=uid, + group_id=gid, + message=messages, + name=uname, + ava_url=AVA_URL.format(uid), + ) + await ws_conn.send_json(data.dict()) From 53f4007292bec9ffee9a68bbf74b54789e0760a1 Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Tue, 13 Aug 2024 22:39:35 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BF=AE=E6=94=B9md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index abd2f589..d97ebe70 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可 ``` # 获取代码 -git clone https://github.com/HibiKier/zhenxun.git +git clone https://github.com/HibiKier/zhenxun_bot.git # 进入目录 cd zhenxun_bot @@ -135,6 +135,9 @@ poetry install # 安装依赖 poetry shell # 进入虚拟环境 python bot.py +# 在Linux系统,你可能还需要运行此命令安装playwright依赖 +playwright install-deps + # 首次后会在data目录下生成database.json和config.yaml文件 # database.json用户配置数据库信息 # config.yaml用户配置插件 @@ -387,9 +390,9 @@ python bot.py +* 重构webui -