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] =?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())