From 25af1802afb5cc1bc738bb6031b2dac7a00a43d0 Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Sat, 30 Dec 2023 05:27:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84webui=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E4=B8=8E=E5=A5=BD=E5=8F=8B/=E7=BE=A4=E7=BB=84api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- basic_plugins/chat_history/chat_message.py | 5 +- basic_plugins/invite_manager/__init__.py | 6 +- models/chat_history.py | 8 +- plugins/web_ui/__init__.py | 79 ++-- plugins/web_ui/api/__init__.py | 9 +- plugins/web_ui/api/base_info.py | 130 +++---- plugins/web_ui/api/group.py | 124 +++--- plugins/web_ui/api/logs/log_manager.py | 9 +- plugins/web_ui/api/logs/logs.py | 9 +- plugins/web_ui/api/plugins.py | 210 +++++----- plugins/web_ui/api/request.py | 88 ++--- plugins/web_ui/api/system.py | 410 ++++++++++---------- plugins/web_ui/api/tabs/__init__.py | 2 + plugins/web_ui/api/tabs/main/__init__.py | 149 +++++++ plugins/web_ui/api/tabs/main/data_source.py | 36 ++ plugins/web_ui/api/tabs/main/model.py | 56 +++ plugins/web_ui/api/tabs/manage/__init__.py | 245 ++++++++++++ plugins/web_ui/api/tabs/manage/model.py | 151 +++++++ plugins/web_ui/auth/__init__.py | 2 +- plugins/web_ui/base_model.py | 139 +++++++ plugins/web_ui/config.py | 112 +++--- plugins/web_ui/models/model.py | 223 ----------- plugins/web_ui/models/params.py | 59 --- plugins/web_ui/utils.py | 126 +++++- utils/browser.py | 3 +- utils/manager/data_class.py | 2 +- utils/manager/requests_manager.py | 35 +- 27 files changed, 1530 insertions(+), 897 deletions(-) create mode 100644 plugins/web_ui/api/tabs/__init__.py create mode 100644 plugins/web_ui/api/tabs/main/__init__.py create mode 100644 plugins/web_ui/api/tabs/main/data_source.py create mode 100644 plugins/web_ui/api/tabs/main/model.py create mode 100644 plugins/web_ui/api/tabs/manage/__init__.py create mode 100644 plugins/web_ui/api/tabs/manage/model.py create mode 100644 plugins/web_ui/base_model.py delete mode 100644 plugins/web_ui/models/model.py delete mode 100644 plugins/web_ui/models/params.py diff --git a/basic_plugins/chat_history/chat_message.py b/basic_plugins/chat_history/chat_message.py index bca98e22..2cefb2d7 100644 --- a/basic_plugins/chat_history/chat_message.py +++ b/basic_plugins/chat_history/chat_message.py @@ -1,5 +1,5 @@ from nonebot import on_message -from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent +from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent from configs.config import Config from models.chat_history import ChatHistory @@ -32,7 +32,7 @@ TEMP_LIST = [] @chat_history.handle() -async def _(event: MessageEvent, msg: str = PlaintText()): +async def _(bot: Bot, event: MessageEvent, msg: str = PlaintText()): group_id = None if isinstance(event, GroupMessageEvent): group_id = str(event.group_id) @@ -42,6 +42,7 @@ async def _(event: MessageEvent, msg: str = PlaintText()): group_id=group_id, text=str(event.get_message()), plain_text=msg, + bot_id=str(bot.self_id), ) ) diff --git a/basic_plugins/invite_manager/__init__.py b/basic_plugins/invite_manager/__init__.py index ba289d00..0317713b 100755 --- a/basic_plugins/invite_manager/__init__.py +++ b/basic_plugins/invite_manager/__init__.py @@ -58,9 +58,12 @@ async def _(bot: Bot, event: FriendRequestEvent): if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"): logger.debug(f"已开启好友请求自动同意,成功通过该请求", "好友请求", target=event.user_id) await bot.set_friend_add_request(flag=event.flag, approve=True) - await FriendUser.create(user_id=str(user["user_id"]), user_name=user["nickname"]) + await FriendUser.create( + user_id=str(user["user_id"]), user_name=user["nickname"] + ) else: requests_manager.add_request( + str(bot.self_id), event.user_id, "private", event.flag, @@ -126,6 +129,7 @@ async def _(bot: Bot, event: GroupRequestEvent): "等待管理员处理吧!", ) requests_manager.add_request( + str(bot.self_id), event.user_id, "group", event.flag, diff --git a/models/chat_history.py b/models/chat_history.py index dcdaf570..60afd130 100644 --- a/models/chat_history.py +++ b/models/chat_history.py @@ -21,6 +21,8 @@ class ChatHistory(Model): """纯文本""" create_time = fields.DatetimeField(auto_now_add=True) """创建时间""" + bot_id = fields.CharField(255, null=True) + """bot记录id""" class Meta: table = "chat_history" @@ -56,7 +58,9 @@ class ChatHistory(Model): ) # type: ignore @classmethod - async def get_group_first_msg_datetime(cls, group_id: Union[int, str]) -> Optional[datetime]: + async def get_group_first_msg_datetime( + cls, group_id: Union[int, str] + ) -> Optional[datetime]: """ 说明: 获取群第一条记录消息时间 @@ -117,4 +121,6 @@ class ChatHistory(Model): "ALTER TABLE chat_history RENAME COLUMN user_qq TO user_id;", # 将user_id改为user_id "ALTER TABLE chat_history ALTER COLUMN user_id TYPE character varying(255);", "ALTER TABLE chat_history ALTER COLUMN group_id TYPE character varying(255);", + "ALTER TABLE chat_history ADD bot_id VARCHAR(255);", # 添加bot_id字段 + "ALTER TABLE chat_history ALTER COLUMN bot_id TYPE character varying(255);", ] diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py index 9b2791cc..c9c8a8ca 100644 --- a/plugins/web_ui/__init__.py +++ b/plugins/web_ui/__init__.py @@ -1,56 +1,67 @@ -# import asyncio +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 +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 configs.config import Config as gConfig -# from services.log import logger, logger_ -# from utils.manager import plugins2settings_manager +from configs.config import Config as gConfig +from services.log import logger, logger_ +from utils.manager import plugins2settings_manager # from .api.base_info import router as base_info_routes # from .api.group import router as group_routes -# from .api.logs import router as ws_routes -# from .api.logs.log_manager import LOG_STORAGE +from .api.logs import router as ws_log_routes +from .api.logs.log_manager import LOG_STORAGE + # from .api.plugins import router as plugin_routes -# from .api.request import router as request_routes +from .api.request import router as request_routes + # from .api.system import router as system_routes +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.g import * -# from .auth import router as auth_router +# from .api.g import * +from .auth import router as auth_router -# driver = nonebot.get_driver() +driver = nonebot.get_driver() -# gConfig.add_plugin_config("web-ui", "username", "admin", name="web-ui", help_="前端管理用户名") +gConfig.add_plugin_config("web-ui", "username", "admin", name="web-ui", help_="前端管理用户名") -# gConfig.add_plugin_config("web-ui", "password", None, name="web-ui", help_="前端管理密码") +gConfig.add_plugin_config("web-ui", "password", None, name="web-ui", help_="前端管理密码") -# BaseApiRouter = APIRouter(prefix="/zhenxun/api") +BaseApiRouter = APIRouter(prefix="/zhenxun/api") -# BaseApiRouter.include_router(auth_router) +BaseApiRouter.include_router(auth_router) # BaseApiRouter.include_router(plugin_routes) # BaseApiRouter.include_router(group_routes) -# BaseApiRouter.include_router(request_routes) +BaseApiRouter.include_router(request_routes) # BaseApiRouter.include_router(system_routes) -# BaseApiRouter.include_router(base_info_routes) +BaseApiRouter.include_router(main_router) +BaseApiRouter.include_router(manage_router) -# @driver.on_startup -# def _(): +@driver.on_startup +def _(): + try: + loop = asyncio.get_running_loop() -# loop = asyncio.get_running_loop() + def log_sink(message: str): + loop.create_task(LOG_STORAGE.add(message.rstrip("\n"))) -# def log_sink(message: str): -# loop.create_task(LOG_STORAGE.add(message.rstrip("\n"))) + logger_.add( + log_sink, colorize=True, filter=default_filter, format=default_format + ) -# logger_.add(log_sink, colorize=True, filter=default_filter, format=default_format) - -# app: FastAPI = nonebot.get_app() -# app.include_router(BaseApiRouter) -# app.include_router(ws_routes) -# logger.info("API启动成功", "Web UI") + app: FastAPI = nonebot.get_app() + app.include_router(BaseApiRouter) + app.include_router(ws_log_routes) + app.include_router(status_routes) + logger.info("API启动成功", "Web UI") + except Exception as e: + logger.error("API启动失败", "Web UI", e=e) diff --git a/plugins/web_ui/api/__init__.py b/plugins/web_ui/api/__init__.py index 0b61b3a0..92af2280 100644 --- a/plugins/web_ui/api/__init__.py +++ b/plugins/web_ui/api/__init__.py @@ -1,4 +1,5 @@ -from .group import * -from .plugins import * -from .request import * -from .system import * +# from .group import * +# from .plugins import * +# from .request import * +# from .system import * +from .tabs import * diff --git a/plugins/web_ui/api/base_info.py b/plugins/web_ui/api/base_info.py index 1bfbb2f7..3b9a7db3 100644 --- a/plugins/web_ui/api/base_info.py +++ b/plugins/web_ui/api/base_info.py @@ -1,75 +1,75 @@ -from datetime import datetime, timedelta -from typing import List, Optional +# from datetime import datetime, timedelta +# from typing import List, Optional -import nonebot -from fastapi import APIRouter +# import nonebot +# from fastapi import APIRouter -from configs.config import Config -from models.chat_history import ChatHistory -from services.log import logger -from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager -from utils.manager.models import PluginData, PluginType +# from configs.config import Config +# from models.chat_history import ChatHistory +# from services.log import logger +# from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager +# from utils.manager.models import PluginData, PluginType -from ..models.model import BotInfo, Result -from ..models.params import UpdateConfig, UpdatePlugin -from ..utils import authentication +# from ..base_model import BotInfo, Result +# from ..models.params import UpdateConfig, UpdatePlugin +# from ..utils import authentication -AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160" +# AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160" -router = APIRouter() +# router = APIRouter() -@router.get("/get_bot_info", dependencies=[authentication()]) -async def _(self_id: Optional[str] = None) -> Result: - """ - 获取Bot基础信息 +# @router.get("/get_bot_info", dependencies=[authentication()]) +# async def _(self_id: Optional[str] = None) -> Result: +# """ +# 获取Bot基础信息 - Args: - qq (Optional[str], optional): qq号. Defaults to None. +# Args: +# qq (Optional[str], optional): qq号. Defaults to None. - Returns: - Result: 获取指定bot信息与bot列表 - """ - bot_list: List[BotInfo] = [] - if bots := nonebot.get_bots(): - select_bot: BotInfo - for key, bot in bots.items(): - bot_list.append( - BotInfo( - bot=bot, # type: ignore - self_id=bot.self_id, - nickname="可爱的小真寻", - ava_url=AVA_URL.format(bot.self_id), - ) - ) - if _bl := [b for b in bot_list if b.self_id == self_id]: - select_bot = _bl[0] - else: - select_bot = bot_list[0] - select_bot.is_select = True - now = datetime.now() - select_bot.received_messages = await ChatHistory.filter( - bot_id=int(select_bot.self_id) - ).count() - select_bot.received_messages_day = await ChatHistory.filter( - bot_id=int(select_bot.self_id), - create_time__gte=now - timedelta(hours=now.hour), - ).count() - select_bot.received_messages_week = await ChatHistory.filter( - bot_id=int(select_bot.self_id), - create_time__gte=now - timedelta(days=7), - ).count() - select_bot.group_count = len(await select_bot.bot.get_group_list()) - select_bot.friend_count = len(await select_bot.bot.get_friend_list()) - for bot in bot_list: - bot.bot = None # type: ignore - # 插件加载数量 - select_bot.plugin_count = len(plugins2settings_manager) - pm_data = plugins_manager.get_data() - select_bot.fail_plugin_count = len([pd for pd in pm_data if pm_data[pd].error]) - select_bot.success_plugin_count = ( - select_bot.plugin_count - select_bot.fail_plugin_count - ) +# Returns: +# Result: 获取指定bot信息与bot列表 +# """ +# bot_list: List[BotInfo] = [] +# if bots := nonebot.get_bots(): +# select_bot: BotInfo +# for key, bot in bots.items(): +# bot_list.append( +# BotInfo( +# bot=bot, # type: ignore +# self_id=bot.self_id, +# nickname="可爱的小真寻", +# ava_url=AVA_URL.format(bot.self_id), +# ) +# ) +# if _bl := [b for b in bot_list if b.self_id == self_id]: +# select_bot = _bl[0] +# else: +# select_bot = bot_list[0] +# select_bot.is_select = True +# now = datetime.now() +# select_bot.received_messages = await ChatHistory.filter( +# bot_id=int(select_bot.self_id) +# ).count() +# select_bot.received_messages_day = await ChatHistory.filter( +# bot_id=int(select_bot.self_id), +# create_time__gte=now - timedelta(hours=now.hour), +# ).count() +# select_bot.received_messages_week = await ChatHistory.filter( +# bot_id=int(select_bot.self_id), +# create_time__gte=now - timedelta(days=7), +# ).count() +# select_bot.group_count = len(await select_bot.bot.get_group_list()) +# select_bot.friend_count = len(await select_bot.bot.get_friend_list()) +# for bot in bot_list: +# bot.bot = None # type: ignore +# # 插件加载数量 +# select_bot.plugin_count = len(plugins2settings_manager) +# pm_data = plugins_manager.get_data() +# select_bot.fail_plugin_count = len([pd for pd in pm_data if pm_data[pd].error]) +# select_bot.success_plugin_count = ( +# select_bot.plugin_count - select_bot.fail_plugin_count +# ) - return Result.ok(bot_list, "已获取操作列表") - return Result.fail("无Bot连接") +# return Result.ok(bot_list, "已获取操作列表") +# return Result.fail("无Bot连接") diff --git a/plugins/web_ui/api/group.py b/plugins/web_ui/api/group.py index 664d9a0e..76f1a4d0 100644 --- a/plugins/web_ui/api/group.py +++ b/plugins/web_ui/api/group.py @@ -1,69 +1,69 @@ -from fastapi import APIRouter -from pydantic.error_wrappers import ValidationError +# from fastapi import APIRouter +# from pydantic.error_wrappers import ValidationError -from services.log import logger -from utils.manager import group_manager -from utils.utils import get_bot +# from services.log import logger +# from utils.manager import group_manager +# from utils.utils import get_bot -from ..models.model import Group, GroupResult, Result, Task -from ..models.params import UpdateGroup -from ..utils import authentication +# from ..base_model import Group, GroupResult, Result, Task +# from ..models.params import UpdateGroup +# from ..utils import authentication -router = APIRouter() +# router = APIRouter() -@router.get("/get_group", dependencies=[authentication()]) -async def _() -> Result: - """ - 获取群信息 - """ - group_list_result = [] - try: - group_info = {} - if bot := get_bot(): - group_list = await bot.get_group_list() - for g in group_list: - group_info[g["group_id"]] = Group(**g) - group_data = group_manager.get_data() - for group_id in group_data.group_manager: - task_list = [] - data = group_manager[group_id].dict() - for tn, status in data["group_task_status"].items(): - task_list.append( - Task( - **{ - "name": tn, - "nameZh": group_manager.get_task_data().get(tn) or tn, - "status": status, - } - ) - ) - data["task"] = task_list - if x := group_info.get(int(group_id)): - data["group"] = x - else: - continue - group_list_result.append(GroupResult(**data)) - except Exception as e: - logger.error("调用API错误", "/get_group", e=e) - return Result.fail(f"{type(e)}: {e}") - return Result.ok(group_list_result, "拿到了新鲜出炉的数据!") +# @router.get("/get_group", dependencies=[authentication()]) +# async def _() -> Result: +# """ +# 获取群信息 +# """ +# group_list_result = [] +# try: +# group_info = {} +# if bot := get_bot(): +# group_list = await bot.get_group_list() +# for g in group_list: +# group_info[g["group_id"]] = Group(**g) +# group_data = group_manager.get_data() +# for group_id in group_data.group_manager: +# task_list = [] +# data = group_manager[group_id].dict() +# for tn, status in data["group_task_status"].items(): +# task_list.append( +# Task( +# **{ +# "name": tn, +# "nameZh": group_manager.get_task_data().get(tn) or tn, +# "status": status, +# } +# ) +# ) +# data["task"] = task_list +# if x := group_info.get(int(group_id)): +# data["group"] = x +# else: +# continue +# group_list_result.append(GroupResult(**data)) +# except Exception as e: +# logger.error("调用API错误", "/get_group", e=e) +# return Result.fail(f"{type(e)}: {e}") +# return Result.ok(group_list_result, "拿到了新鲜出炉的数据!") -@router.post("/update_group", dependencies=[authentication()]) -async def _(group: UpdateGroup) -> Result: - """ - 修改群信息 - """ - try: - group_id = group.group_id - group_manager.set_group_level(group_id, group.level) - if group.status: - group_manager.turn_on_group_bot_status(group_id) - else: - group_manager.shutdown_group_bot_status(group_id) - group_manager.save() - except Exception as e: - logger.error("调用API错误", "/get_group", e=e) - return Result.fail(f"{type(e)}: {e}") - return Result.ok(info="已完成记录!") +# @router.post("/update_group", dependencies=[authentication()]) +# async def _(group: UpdateGroup) -> Result: +# """ +# 修改群信息 +# """ +# try: +# group_id = group.group_id +# group_manager.set_group_level(group_id, group.level) +# if group.status: +# group_manager.turn_on_group_bot_status(group_id) +# else: +# group_manager.shutdown_group_bot_status(group_id) +# group_manager.save() +# except Exception as e: +# logger.error("调用API错误", "/get_group", e=e) +# return Result.fail(f"{type(e)}: {e}") +# return Result.ok(info="已完成记录!") diff --git a/plugins/web_ui/api/logs/log_manager.py b/plugins/web_ui/api/logs/log_manager.py index ed773766..0257f9cf 100644 --- a/plugins/web_ui/api/logs/log_manager.py +++ b/plugins/web_ui/api/logs/log_manager.py @@ -1,6 +1,6 @@ import asyncio import re -from typing import Awaitable, Callable, ClassVar, Dict, Generic, List, Set, TypeVar +from typing import Awaitable, Callable, Dict, Generic, List, Set, TypeVar from urllib.parse import urlparse PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))" @@ -10,6 +10,11 @@ LogListener = Callable[[_T], Awaitable[None]] class LogStorage(Generic[_T]): + + """ + 日志存储 + """ + def __init__(self, rotation: float = 5 * 60): self.count, self.rotation = 0, rotation self.logs: Dict[int, str] = {} @@ -42,4 +47,4 @@ class LogStorage(Generic[_T]): return -LOG_STORAGE = LogStorage[str]() +LOG_STORAGE: LogStorage[str] = LogStorage[str]() diff --git a/plugins/web_ui/api/logs/logs.py b/plugins/web_ui/api/logs/logs.py index 4b5b1138..e6abebf3 100644 --- a/plugins/web_ui/api/logs/logs.py +++ b/plugins/web_ui/api/logs/logs.py @@ -2,7 +2,7 @@ from typing import List from fastapi import APIRouter, WebSocket from loguru import logger -from nonebot.utils import escape_tag, run_sync +from nonebot.utils import escape_tag from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState from .log_manager import LOG_STORAGE @@ -12,7 +12,12 @@ router = APIRouter() @router.get("/logs", response_model=List[str]) async def system_logs_history(reverse: bool = False): - return LOG_STORAGE.list(reverse=reverse) + """历史日志 + + 参数: + reverse: 反转顺序. + """ + return LOG_STORAGE.list(reverse=reverse) # type: ignore @router.websocket("/logs") diff --git a/plugins/web_ui/api/plugins.py b/plugins/web_ui/api/plugins.py index 1140f060..8262a0bd 100644 --- a/plugins/web_ui/api/plugins.py +++ b/plugins/web_ui/api/plugins.py @@ -1,115 +1,115 @@ -from typing import Optional +# from typing import Optional -import cattrs -from fastapi import APIRouter +# import cattrs +# from fastapi import APIRouter -from configs.config import Config -from services.log import logger -from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager -from utils.manager.models import PluginData, PluginType +# from configs.config import Config +# from services.log import logger +# from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager +# from utils.manager.models import PluginData, PluginType -from ..config import * -from ..models.model import Plugin, PluginConfig, Result -from ..models.params import UpdateConfig, UpdatePlugin -from ..utils import authentication +# from ..config import * +# from ..base_model import Plugin, PluginConfig, Result +# from ..models.params import UpdateConfig, UpdatePlugin +# from ..utils import authentication -router = APIRouter() +# router = APIRouter() -@router.get("/get_plugins", dependencies=[authentication()]) -def _( - plugin_type: PluginType, -) -> Result: - """ - 获取插件列表 - :param plugin_type: 类型 normal, superuser, hidden, admin - """ - try: - plugin_list = [] - for module in plugin_data_manager.keys(): - plugin_data: Optional[PluginData] = plugin_data_manager[module] - if plugin_data and plugin_data.plugin_type == plugin_type: - plugin_config = None - if plugin_data.plugin_configs: - plugin_config = {} - for key in plugin_data.plugin_configs: - plugin_config[key] = PluginConfig( - key=key, - module=module, - has_type=bool(plugin_data.plugin_configs[key].type), - **plugin_data.plugin_configs[key].dict(), - ) - plugin_list.append( - Plugin( - model=module, - plugin_settings=plugin_data.plugin_setting, - plugin_manager=plugin_data.plugin_status, - plugin_config=plugin_config, - cd_limit=plugin_data.plugin_cd, - block_limit=plugin_data.plugin_block, - count_limit=plugin_data.plugin_count, - ) - ) - except Exception as e: - logger.error("调用API错误", "/get_plugins", e=e) - return Result.fail(f"{type(e)}: {e}") - return Result.ok(plugin_list, "拿到了新鲜出炉的数据!") +# @router.get("/get_plugins", dependencies=[authentication()]) +# def _( +# plugin_type: PluginType, +# ) -> Result: +# """ +# 获取插件列表 +# :param plugin_type: 类型 normal, superuser, hidden, admin +# """ +# try: +# plugin_list = [] +# for module in plugin_data_manager.keys(): +# plugin_data: Optional[PluginData] = plugin_data_manager[module] +# if plugin_data and plugin_data.plugin_type == plugin_type: +# plugin_config = None +# if plugin_data.plugin_configs: +# plugin_config = {} +# for key in plugin_data.plugin_configs: +# plugin_config[key] = PluginConfig( +# key=key, +# module=module, +# has_type=bool(plugin_data.plugin_configs[key].type), +# **plugin_data.plugin_configs[key].dict(), +# ) +# plugin_list.append( +# Plugin( +# model=module, +# plugin_settings=plugin_data.plugin_setting, +# plugin_manager=plugin_data.plugin_status, +# plugin_config=plugin_config, +# cd_limit=plugin_data.plugin_cd, +# block_limit=plugin_data.plugin_block, +# count_limit=plugin_data.plugin_count, +# ) +# ) +# except Exception as e: +# logger.error("调用API错误", "/get_plugins", e=e) +# return Result.fail(f"{type(e)}: {e}") +# return Result.ok(plugin_list, "拿到了新鲜出炉的数据!") -@router.post("/update_plugins", dependencies=[authentication()]) -def _(plugin: UpdatePlugin) -> Result: - """ - 修改插件信息 - :param plugin: 插件内容 - """ - try: - module = plugin.module - if p2s := plugins2settings_manager.get(module): - p2s.default_status = plugin.default_status - p2s.limit_superuser = plugin.limit_superuser - p2s.cost_gold = plugin.cost_gold - p2s.cmd = plugin.cmd - p2s.level = plugin.group_level - if pd := plugin_data_manager.get(module): - menu_lin = None - if len(pd.menu_type) > 1: - menu_lin = pd.menu_type[1] - if menu_lin is not None: - pd.menu_type = (plugin.menu_type, menu_lin) - else: - pd.menu_type = (plugin.menu_type,) - if pm := plugins_manager.get(module): - if plugin.block_type: - pm.block_type = plugin.block_type - pm.status = False - else: - pm.block_type = None - pm.status = True - plugins2settings_manager.save() - plugins_manager.save() - except Exception as e: - logger.error("调用API错误", "/update_plugins", e=e) - return Result.fail(f"{type(e)}: {e}") - return Result.ok(info="已经帮你写好啦!") +# @router.post("/update_plugins", dependencies=[authentication()]) +# def _(plugin: UpdatePlugin) -> Result: +# """ +# 修改插件信息 +# :param plugin: 插件内容 +# """ +# try: +# module = plugin.module +# if p2s := plugins2settings_manager.get(module): +# p2s.default_status = plugin.default_status +# p2s.limit_superuser = plugin.limit_superuser +# p2s.cost_gold = plugin.cost_gold +# p2s.cmd = plugin.cmd +# p2s.level = plugin.group_level +# if pd := plugin_data_manager.get(module): +# menu_lin = None +# if len(pd.menu_type) > 1: +# menu_lin = pd.menu_type[1] +# if menu_lin is not None: +# pd.menu_type = (plugin.menu_type, menu_lin) +# else: +# pd.menu_type = (plugin.menu_type,) +# if pm := plugins_manager.get(module): +# if plugin.block_type: +# pm.block_type = plugin.block_type +# pm.status = False +# else: +# pm.block_type = None +# pm.status = True +# plugins2settings_manager.save() +# plugins_manager.save() +# except Exception as e: +# logger.error("调用API错误", "/update_plugins", e=e) +# return Result.fail(f"{type(e)}: {e}") +# return Result.ok(info="已经帮你写好啦!") -@router.post("/update_config", dependencies=[authentication()]) -def _(config_list: List[UpdateConfig]) -> Result: - try: - for config in config_list: - if cg := Config.get(config.module): - if c := cg.configs.get(config.key): - if isinstance(c.value, (list, tuple)) or isinstance( - c.default_value, (list, tuple) - ): - value = config.value.split(",") - else: - value = config.value - if c.type and value is not None: - value = cattrs.structure(value, c.type) - Config.set_config(config.module, config.key, value) - except Exception as e: - logger.error("调用API错误", "/update_config", e=e) - return Result.fail(f"{type(e)}: {e}") - Config.save(save_simple_data=True) - return Result.ok(info="写入配置项了哦!") +# @router.post("/update_config", dependencies=[authentication()]) +# def _(config_list: List[UpdateConfig]) -> Result: +# try: +# for config in config_list: +# if cg := Config.get(config.module): +# if c := cg.configs.get(config.key): +# if isinstance(c.value, (list, tuple)) or isinstance( +# c.default_value, (list, tuple) +# ): +# value = config.value.split(",") +# else: +# value = config.value +# if c.type and value is not None: +# value = cattrs.structure(value, c.type) +# Config.set_config(config.module, config.key, value) +# except Exception as e: +# logger.error("调用API错误", "/update_config", e=e) +# return Result.fail(f"{type(e)}: {e}") +# Config.save(save_simple_data=True) +# return Result.ok(info="写入配置项了哦!") diff --git a/plugins/web_ui/api/request.py b/plugins/web_ui/api/request.py index d68583ed..83a878dd 100644 --- a/plugins/web_ui/api/request.py +++ b/plugins/web_ui/api/request.py @@ -8,7 +8,7 @@ from services.log import logger from utils.manager import requests_manager from utils.utils import get_bot -from ..models.model import RequestResult, Result +from ..base_model import RequestResult, Result from ..models.params import HandleRequest from ..utils import authentication @@ -42,46 +42,46 @@ def _(request_type: Optional[str]) -> Result: return Result.ok(info="成功清除了数据") -@router.post("/handle_request", dependencies=[authentication()]) -async def _(parma: HandleRequest) -> Result: - """ - 操作请求 - :param parma: 参数 - """ - try: - result = "操作成功!" - flag = 3 - if bot := get_bot(): - if parma.handle == "approve": - if parma.type == "group": - if rid := requests_manager.get_group_id(parma.id): - # await GroupInfo.update_or_create(defaults={"group_flag": 1}, ) - if group := await GroupInfo.get_or_none(group_id=str(rid)): - await group.update_or_create(group_flag=1) - else: - group_info = await bot.get_group_info(group_id=rid) - await GroupInfo.update_or_create( - group_id=str(group_info["group_id"]), - defaults={ - "group_name": group_info["group_name"], - "max_member_count": group_info["max_member_count"], - "member_count": group_info["member_count"], - "group_flag": 1, - }, - ) - flag = await requests_manager.approve(bot, parma.id, parma.type) - elif parma.handle == "refuse": - flag = await requests_manager.refused(bot, parma.id, parma.type) - elif parma.handle == "delete": - requests_manager.delete_request(parma.id, parma.type) - if parma.handle != "delete": - if flag == 1: - result = "该请求已失效" - requests_manager.delete_request(parma.id, parma.type) - elif flag == 2: - result = "未找到此Id" - return Result.ok(result, "成功处理了请求!") - return Result.fail("Bot未连接") - except Exception as e: - logger.error("调用API错误", "/get_group", e=e) - return Result.fail(f"{type(e)}: {e}") +# @router.post("/handle_request", dependencies=[authentication()]) +# async def _(parma: HandleRequest) -> Result: +# """ +# 操作请求 +# :param parma: 参数 +# """ +# try: +# result = "操作成功!" +# flag = 3 +# if bot := get_bot(): +# if parma.handle == "approve": +# if parma.type == "group": +# if rid := requests_manager.get_group_id(parma.id): +# # await GroupInfo.update_or_create(defaults={"group_flag": 1}, ) +# if group := await GroupInfo.get_or_none(group_id=str(rid)): +# await group.update_or_create(group_flag=1) +# else: +# group_info = await bot.get_group_info(group_id=rid) +# await GroupInfo.update_or_create( +# group_id=str(group_info["group_id"]), +# defaults={ +# "group_name": group_info["group_name"], +# "max_member_count": group_info["max_member_count"], +# "member_count": group_info["member_count"], +# "group_flag": 1, +# }, +# ) +# flag = await requests_manager.approve(bot, parma.id, parma.type) +# elif parma.handle == "refuse": +# flag = await requests_manager.refused(bot, parma.id, parma.type) +# elif parma.handle == "delete": +# requests_manager.delete_request(parma.id, parma.type) +# if parma.handle != "delete": +# if flag == 1: +# result = "该请求已失效" +# requests_manager.delete_request(parma.id, parma.type) +# elif flag == 2: +# result = "未找到此Id" +# return Result.ok(result, "成功处理了请求!") +# return Result.fail("Bot未连接") +# except Exception as e: +# logger.error("调用API错误", "/get_group", e=e) +# return Result.fail(f"{type(e)}: {e}") diff --git a/plugins/web_ui/api/system.py b/plugins/web_ui/api/system.py index 6e2d6fb8..dacc1d9f 100644 --- a/plugins/web_ui/api/system.py +++ b/plugins/web_ui/api/system.py @@ -1,228 +1,228 @@ -import asyncio -import os -from datetime import datetime -from pathlib import Path -from typing import Dict, Optional, Union +# import asyncio +# import os +# from datetime import datetime +# from pathlib import Path +# from typing import Dict, Optional, Union -import psutil -import ujson as json -from fastapi import APIRouter +# import psutil +# import ujson as json +# from fastapi import APIRouter -from configs.path_config import ( - DATA_PATH, - FONT_PATH, - IMAGE_PATH, - LOG_PATH, - RECORD_PATH, - TEMP_PATH, - TEXT_PATH, -) -from services.log import logger -from utils.http_utils import AsyncHttpx +# from configs.path_config import ( +# DATA_PATH, +# FONT_PATH, +# IMAGE_PATH, +# LOG_PATH, +# RECORD_PATH, +# TEMP_PATH, +# TEXT_PATH, +# ) +# from services.log import logger +# from utils.http_utils import AsyncHttpx -from ..models.model import ( - Result, - SystemFolderSize, - SystemNetwork, - SystemResult, - SystemStatus, - SystemStatusList, -) -from ..utils import authentication +# from ..base_model import ( +# Result, +# SystemFolderSize, +# SystemNetwork, +# SystemResult, +# SystemStatus, +# SystemStatusList, +# ) +# from ..utils import authentication -CPU_DATA_PATH = DATA_PATH / "system" / "cpu.json" -MEMORY_DATA_PATH = DATA_PATH / "system" / "memory.json" -DISK_DATA_PATH = DATA_PATH / "system" / "disk.json" -CPU_DATA_PATH.parent.mkdir(exist_ok=True, parents=True) -cpu_data = {"data": []} -memory_data = {"data": []} -disk_data = {"data": []} +# CPU_DATA_PATH = DATA_PATH / "system" / "cpu.json" +# MEMORY_DATA_PATH = DATA_PATH / "system" / "memory.json" +# DISK_DATA_PATH = DATA_PATH / "system" / "disk.json" +# CPU_DATA_PATH.parent.mkdir(exist_ok=True, parents=True) +# cpu_data = {"data": []} +# memory_data = {"data": []} +# disk_data = {"data": []} -router = APIRouter() +# router = APIRouter() -@router.get("/system", dependencies=[authentication()]) -async def _() -> Result: - return await get_system_data() +# @router.get("/system", dependencies=[authentication()]) +# async def _() -> Result: +# return await get_system_data() -@router.get("/status", dependencies=[authentication()]) -async def _() -> Result: - return Result.ok( - await asyncio.get_event_loop().run_in_executor(None, _get_system_status), - ) +# @router.get("/status", dependencies=[authentication()]) +# async def _() -> Result: +# return Result.ok( +# await asyncio.get_event_loop().run_in_executor(None, _get_system_status), +# ) -@router.get("/system/disk", dependencies=[authentication()]) -async def _(type_: Optional[str] = None) -> Result: - return Result.ok( - data=await asyncio.get_event_loop().run_in_executor( - None, _get_system_disk, type_ - ), - ) +# @router.get("/system/disk", dependencies=[authentication()]) +# async def _(type_: Optional[str] = None) -> Result: +# return Result.ok( +# data=await asyncio.get_event_loop().run_in_executor( +# None, _get_system_disk, type_ +# ), +# ) -@router.get("/system/statusList", dependencies=[authentication()]) -async def _() -> Result: - global cpu_data, memory_data, disk_data - await asyncio.get_event_loop().run_in_executor(None, _get_system_status) - cpu_rst = cpu_data["data"][-10:] if len(cpu_data["data"]) > 10 else cpu_data["data"] - memory_rst = ( - memory_data["data"][-10:] - if len(memory_data["data"]) > 10 - else memory_data["data"] - ) - disk_rst = ( - disk_data["data"][-10:] if len(disk_data["data"]) > 10 else disk_data["data"] - ) - return Result.ok( - SystemStatusList( - cpu_data=cpu_rst, - memory_data=memory_rst, - disk_data=disk_rst, - ), - ) +# @router.get("/system/statusList", dependencies=[authentication()]) +# async def _() -> Result: +# global cpu_data, memory_data, disk_data +# await asyncio.get_event_loop().run_in_executor(None, _get_system_status) +# cpu_rst = cpu_data["data"][-10:] if len(cpu_data["data"]) > 10 else cpu_data["data"] +# memory_rst = ( +# memory_data["data"][-10:] +# if len(memory_data["data"]) > 10 +# else memory_data["data"] +# ) +# disk_rst = ( +# disk_data["data"][-10:] if len(disk_data["data"]) > 10 else disk_data["data"] +# ) +# return Result.ok( +# SystemStatusList( +# cpu_data=cpu_rst, +# memory_data=memory_rst, +# disk_data=disk_rst, +# ), +# ) -async def get_system_data(): - """ - 说明: - 获取系统信息,资源文件大小,网络状态等 - """ - baidu = 200 - google = 200 - try: - await AsyncHttpx.get("https://www.baidu.com/", timeout=5) - except Exception as e: - logger.warning(f"访问BaiDu失败... {type(e)}: {e}") - baidu = 404 - try: - await AsyncHttpx.get("https://www.google.com/", timeout=5) - except Exception as e: - logger.warning(f"访问Google失败... {type(e)}: {e}") - google = 404 - network = SystemNetwork(baidu=baidu, google=google) - disk = await asyncio.get_event_loop().run_in_executor(None, _get_system_disk, None) - status = await asyncio.get_event_loop().run_in_executor(None, _get_system_status) - return Result.ok( - SystemResult( - status=status, - network=network, - disk=disk, # type: ignore - check_time=datetime.now().replace(microsecond=0), - ), - ) +# async def get_system_data(): +# """ +# 说明: +# 获取系统信息,资源文件大小,网络状态等 +# """ +# baidu = 200 +# google = 200 +# try: +# await AsyncHttpx.get("https://www.baidu.com/", timeout=5) +# except Exception as e: +# logger.warning(f"访问BaiDu失败... {type(e)}: {e}") +# baidu = 404 +# try: +# await AsyncHttpx.get("https://www.google.com/", timeout=5) +# except Exception as e: +# logger.warning(f"访问Google失败... {type(e)}: {e}") +# google = 404 +# network = SystemNetwork(baidu=baidu, google=google) +# disk = await asyncio.get_event_loop().run_in_executor(None, _get_system_disk, None) +# status = await asyncio.get_event_loop().run_in_executor(None, _get_system_status) +# return Result.ok( +# SystemResult( +# status=status, +# network=network, +# disk=disk, # type: ignore +# check_time=datetime.now().replace(microsecond=0), +# ), +# ) -def _get_system_status() -> SystemStatus: - """ - 说明: - 获取系统信息等 - """ - cpu = psutil.cpu_percent() - memory = psutil.virtual_memory().percent - disk = psutil.disk_usage("/").percent - save_system_data(cpu, memory, disk) - return SystemStatus( - cpu=cpu, - memory=memory, - disk=disk, - check_time=datetime.now().replace(microsecond=0), - ) +# def _get_system_status() -> SystemStatus: +# """ +# 说明: +# 获取系统信息等 +# """ +# cpu = psutil.cpu_percent() +# memory = psutil.virtual_memory().percent +# disk = psutil.disk_usage("/").percent +# save_system_data(cpu, memory, disk) +# return SystemStatus( +# cpu=cpu, +# memory=memory, +# disk=disk, +# check_time=datetime.now().replace(microsecond=0), +# ) -def _get_system_disk( - type_: Optional[str], -) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]: - """ - 说明: - 获取资源文件大小等 - """ - if not type_: - disk = SystemFolderSize( - font_dir_size=_get_dir_size(FONT_PATH) / 1024 / 1024, - image_dir_size=_get_dir_size(IMAGE_PATH) / 1024 / 1024, - text_dir_size=_get_dir_size(TEXT_PATH) / 1024 / 1024, - record_dir_size=_get_dir_size(RECORD_PATH) / 1024 / 1024, - temp_dir_size=_get_dir_size(TEMP_PATH) / 1024 / 102, - data_dir_size=_get_dir_size(DATA_PATH) / 1024 / 1024, - log_dir_size=_get_dir_size(LOG_PATH) / 1024 / 1024, - check_time=datetime.now().replace(microsecond=0), - ) - return disk - else: - if type_ == "image": - dir_path = IMAGE_PATH - elif type_ == "font": - dir_path = FONT_PATH - elif type_ == "text": - dir_path = TEXT_PATH - elif type_ == "record": - dir_path = RECORD_PATH - elif type_ == "data": - dir_path = DATA_PATH - elif type_ == "temp": - dir_path = TEMP_PATH - else: - dir_path = LOG_PATH - dir_map = {} - other_file_size = 0 - for file in os.listdir(dir_path): - file = Path(dir_path / file) - if file.is_dir(): - dir_map[file.name] = _get_dir_size(file) / 1024 / 1024 - else: - other_file_size += os.path.getsize(file) / 1024 / 1024 - dir_map["其他文件"] = other_file_size - dir_map["check_time"] = datetime.now().replace(microsecond=0) - return dir_map +# def _get_system_disk( +# type_: Optional[str], +# ) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]: +# """ +# 说明: +# 获取资源文件大小等 +# """ +# if not type_: +# disk = SystemFolderSize( +# font_dir_size=_get_dir_size(FONT_PATH) / 1024 / 1024, +# image_dir_size=_get_dir_size(IMAGE_PATH) / 1024 / 1024, +# text_dir_size=_get_dir_size(TEXT_PATH) / 1024 / 1024, +# record_dir_size=_get_dir_size(RECORD_PATH) / 1024 / 1024, +# temp_dir_size=_get_dir_size(TEMP_PATH) / 1024 / 102, +# data_dir_size=_get_dir_size(DATA_PATH) / 1024 / 1024, +# log_dir_size=_get_dir_size(LOG_PATH) / 1024 / 1024, +# check_time=datetime.now().replace(microsecond=0), +# ) +# return disk +# else: +# if type_ == "image": +# dir_path = IMAGE_PATH +# elif type_ == "font": +# dir_path = FONT_PATH +# elif type_ == "text": +# dir_path = TEXT_PATH +# elif type_ == "record": +# dir_path = RECORD_PATH +# elif type_ == "data": +# dir_path = DATA_PATH +# elif type_ == "temp": +# dir_path = TEMP_PATH +# else: +# dir_path = LOG_PATH +# dir_map = {} +# other_file_size = 0 +# for file in os.listdir(dir_path): +# file = Path(dir_path / file) +# if file.is_dir(): +# dir_map[file.name] = _get_dir_size(file) / 1024 / 1024 +# else: +# other_file_size += os.path.getsize(file) / 1024 / 1024 +# dir_map["其他文件"] = other_file_size +# dir_map["check_time"] = datetime.now().replace(microsecond=0) +# return dir_map -def _get_dir_size(dir_path: Path) -> float: - """ - 说明: - 获取文件夹大小 - 参数: - :param dir_path: 文件夹路径 - """ - size = 0 - for root, dirs, files in os.walk(dir_path): - size += sum([os.path.getsize(os.path.join(root, name)) for name in files]) - return size +# def _get_dir_size(dir_path: Path) -> float: +# """ +# 说明: +# 获取文件夹大小 +# 参数: +# :param dir_path: 文件夹路径 +# """ +# size = 0 +# for root, dirs, files in os.walk(dir_path): +# size += sum([os.path.getsize(os.path.join(root, name)) for name in files]) +# return size -def save_system_data(cpu: float, memory: float, disk: float): - """ - 说明: - 保存一些系统信息 - 参数: - :param cpu: cpu - :param memory: memory - :param disk: disk - """ - global cpu_data, memory_data, disk_data - if CPU_DATA_PATH.exists() and not cpu_data["data"]: - with open(CPU_DATA_PATH, "r") as f: - cpu_data = json.load(f) - if MEMORY_DATA_PATH.exists() and not memory_data["data"]: - with open(MEMORY_DATA_PATH, "r") as f: - memory_data = json.load(f) - if DISK_DATA_PATH.exists() and not disk_data["data"]: - with open(DISK_DATA_PATH, "r") as f: - disk_data = json.load(f) - now = str(datetime.now().time().replace(microsecond=0)) - cpu_data["data"].append({"time": now, "data": cpu}) - memory_data["data"].append({"time": now, "data": memory}) - disk_data["data"].append({"time": now, "data": disk}) - if len(cpu_data["data"]) > 50: - cpu_data["data"] = cpu_data["data"][-50:] - if len(memory_data["data"]) > 50: - memory_data["data"] = memory_data["data"][-50:] - if len(disk_data["data"]) > 50: - disk_data["data"] = disk_data["data"][-50:] - with open(CPU_DATA_PATH, "w") as f: - json.dump(cpu_data, f, indent=4, ensure_ascii=False) - with open(MEMORY_DATA_PATH, "w") as f: - json.dump(memory_data, f, indent=4, ensure_ascii=False) - with open(DISK_DATA_PATH, "w") as f: - json.dump(disk_data, f, indent=4, ensure_ascii=False) +# def save_system_data(cpu: float, memory: float, disk: float): +# """ +# 说明: +# 保存一些系统信息 +# 参数: +# :param cpu: cpu +# :param memory: memory +# :param disk: disk +# """ +# global cpu_data, memory_data, disk_data +# if CPU_DATA_PATH.exists() and not cpu_data["data"]: +# with open(CPU_DATA_PATH, "r") as f: +# cpu_data = json.load(f) +# if MEMORY_DATA_PATH.exists() and not memory_data["data"]: +# with open(MEMORY_DATA_PATH, "r") as f: +# memory_data = json.load(f) +# if DISK_DATA_PATH.exists() and not disk_data["data"]: +# with open(DISK_DATA_PATH, "r") as f: +# disk_data = json.load(f) +# now = str(datetime.now().time().replace(microsecond=0)) +# cpu_data["data"].append({"time": now, "data": cpu}) +# memory_data["data"].append({"time": now, "data": memory}) +# disk_data["data"].append({"time": now, "data": disk}) +# if len(cpu_data["data"]) > 50: +# cpu_data["data"] = cpu_data["data"][-50:] +# if len(memory_data["data"]) > 50: +# memory_data["data"] = memory_data["data"][-50:] +# if len(disk_data["data"]) > 50: +# disk_data["data"] = disk_data["data"][-50:] +# with open(CPU_DATA_PATH, "w") as f: +# json.dump(cpu_data, f, indent=4, ensure_ascii=False) +# with open(MEMORY_DATA_PATH, "w") as f: +# json.dump(memory_data, f, indent=4, ensure_ascii=False) +# with open(DISK_DATA_PATH, "w") as f: +# json.dump(disk_data, f, indent=4, ensure_ascii=False) diff --git a/plugins/web_ui/api/tabs/__init__.py b/plugins/web_ui/api/tabs/__init__.py new file mode 100644 index 00000000..f750065f --- /dev/null +++ b/plugins/web_ui/api/tabs/__init__.py @@ -0,0 +1,2 @@ +from .main import * +from .manage import * diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py new file mode 100644 index 00000000..7daa0ddc --- /dev/null +++ b/plugins/web_ui/api/tabs/main/__init__.py @@ -0,0 +1,149 @@ +import asyncio +import time +from datetime import datetime, timedelta +from typing import List, Optional + +import nonebot +from fastapi import APIRouter, WebSocket +from nonebot.utils import escape_tag +from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState + +from configs.config import NICKNAME +from models.chat_history import ChatHistory +from services.log import logger +from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager +from utils.manager.models import PluginData, PluginType + +from ....config import QueryDateType +from ....base_model import Result +from ....utils import authentication, get_system_status +from .data_source import bot_live +from .model import BaseInfo + +AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160" + +run_time = time.time() + +ws_router = APIRouter() +router = APIRouter() + + +@router.get("/get_base_info", dependencies=[authentication()], description="基础信息") +async def _(bot_id: Optional[str] = None) -> Result: + """ + 获取Bot基础信息 + + Args: + qq (Optional[str], optional): qq号. Defaults to None. + + Returns: + Result: 获取指定bot信息与bot列表 + """ + bot_list: List[BaseInfo] = [] + if bots := nonebot.get_bots(): + select_bot: BaseInfo + for key, bot in bots.items(): + bot_list.append( + BaseInfo( + bot=bot, # type: ignore + self_id=bot.self_id, + nickname=NICKNAME, + ava_url=AVA_URL.format(bot.self_id), + ) + ) + # 获取指定qq号的bot信息,若无指定则获取第一个 + if _bl := [b for b in bot_list if b.self_id == bot_id]: + select_bot = _bl[0] + else: + select_bot = bot_list[0] + select_bot.is_select = True + now = datetime.now() + # 今日累计接收消息 + select_bot.received_messages = await ChatHistory.filter( + bot_id=select_bot.self_id, + create_time__gte=now - timedelta(hours=now.hour), + ).count() + # 群聊数量 + select_bot.group_count = len(await select_bot.bot.get_group_list()) + # 好友数量 + select_bot.friend_count = len(await select_bot.bot.get_friend_list()) + for bot in bot_list: + bot.bot = None # type: ignore + # 插件加载数量 + select_bot.plugin_count = len(plugins2settings_manager) + pm_data = plugins_manager.get_data() + select_bot.fail_plugin_count = len([pd for pd in pm_data if pm_data[pd].error]) + select_bot.success_plugin_count = ( + select_bot.plugin_count - select_bot.fail_plugin_count + ) + # 连接时间 + select_bot.connect_time = bot_live.get(select_bot.self_id) or 0 + + return Result.ok(bot_list, "已获取操作列表") + return Result.warning_("无Bot连接...") + + +@router.get("/get_ch_count", dependencies=[authentication()], description="获取接收消息数量") +async def _(bot_id: str, query_type: Optional[QueryDateType] = None) -> Result: + if bots := nonebot.get_bots(): + if not query_type: + return Result.ok(await ChatHistory.filter(bot_id=bot_id).count()) + now = datetime.now() + if query_type == QueryDateType.DAY: + return Result.ok( + await ChatHistory.filter( + bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour) + ).count() + ) + if query_type == QueryDateType.WEEK: + return Result.ok( + await ChatHistory.filter( + bot_id=bot_id, create_time__gte=now - timedelta(days=7) + ).count() + ) + if query_type == QueryDateType.MONTH: + return Result.ok( + await ChatHistory.filter( + bot_id=bot_id, create_time__gte=now - timedelta(days=30) + ).count() + ) + if query_type == QueryDateType.YEAR: + return Result.ok( + await ChatHistory.filter( + bot_id=bot_id, create_time__gte=now - timedelta(days=365) + ).count() + ) + return Result.warning_("无Bot连接...") + + +@router.get("get_fg_count", dependencies=[authentication()], description="好友/群组数量") +async def _(bot_id: str) -> Result: + if bots := nonebot.get_bots(): + if bot_id not in bots: + return Result.warning_("指定Bot未连接...") + bot = bots[bot_id] + data = { + "friend_count": len(await bot.get_friend_list()), + "group_count": len(await bot.get_group_list()), + } + return Result.ok(data) + return Result.warning_("无Bot连接...") + + +@router.get("/get_run_time", dependencies=[authentication()], description="获取nb运行时间") +async def _() -> Result: + return Result.ok(int(time.time() - run_time)) + + +@ws_router.websocket("/system_status") +async def system_logs_realtime(websocket: WebSocket): + await websocket.accept() + logger.debug("ws system_status is connect") + try: + while websocket.client_state == WebSocketState.CONNECTED: + system_status = await get_system_status() + await websocket.send_text(system_status.json()) + await asyncio.sleep(5) + except WebSocketDisconnect: + pass + return diff --git a/plugins/web_ui/api/tabs/main/data_source.py b/plugins/web_ui/api/tabs/main/data_source.py new file mode 100644 index 00000000..e164383c --- /dev/null +++ b/plugins/web_ui/api/tabs/main/data_source.py @@ -0,0 +1,36 @@ +import time +from typing import Optional + +import nonebot +from nonebot import Driver +from nonebot.adapters.onebot.v12 import Bot + +driver: Driver = nonebot.get_driver() + + +class BotLive: + def __init__(self): + self._data = {} + + def add(self, bot_id: str): + self._data[bot_id] = time.time() + + def get(self, bot_id: str) -> Optional[int]: + return self._data.get(bot_id) + + def remove(self, bot_id: str): + if bot_id in self._data: + del self._data[bot_id] + + +bot_live = BotLive() + + +@driver.on_bot_connect +async def _(bot: Bot): + bot_live.add(bot.self_id) + + +@driver.on_bot_disconnect +async def _(bot: Bot): + bot_live.remove(bot.self_id) diff --git a/plugins/web_ui/api/tabs/main/model.py b/plugins/web_ui/api/tabs/main/model.py new file mode 100644 index 00000000..decb2aea --- /dev/null +++ b/plugins/web_ui/api/tabs/main/model.py @@ -0,0 +1,56 @@ +from nonebot.adapters.onebot.v11 import Bot +from pydantic import BaseModel + + +class SystemStatus(BaseModel): + """ + 系统状态 + """ + + cpu: float + memory: float + disk: float + + +class BaseInfo(BaseModel): + """ + 基础信息 + """ + + bot: Bot + """Bot""" + self_id: str + """SELF ID""" + nickname: str + """昵称""" + ava_url: str + """头像url""" + friend_count: int = 0 + """好友数量""" + group_count: int = 0 + """群聊数量""" + received_messages: int = 0 + """今日 累计接收消息""" + # received_messages_day: int = 0 + # """今日累计接收消息""" + # received_messages_week: int = 0 + # """一周内累计接收消息""" + # received_messages_month: int = 0 + # """一月内累计接收消息""" + # received_messages_year: int = 0 + # """一年内累计接受消息""" + connect_time: int = 0 + """连接时间""" + + plugin_count: int = 0 + """加载插件数量""" + success_plugin_count: int = 0 + """加载成功插件数量""" + fail_plugin_count: int = 0 + """加载失败插件数量""" + + is_select: bool = False + """当前选择""" + + class Config: + arbitrary_types_allowed = True diff --git a/plugins/web_ui/api/tabs/manage/__init__.py b/plugins/web_ui/api/tabs/manage/__init__.py new file mode 100644 index 00000000..a764f1bd --- /dev/null +++ b/plugins/web_ui/api/tabs/manage/__init__.py @@ -0,0 +1,245 @@ +from typing import Literal + +import nonebot +from fastapi import APIRouter +from pydantic.error_wrappers import ValidationError + +from configs.config import NICKNAME +from models.group_info import GroupInfo +from services.log import logger +from utils.manager import group_manager, requests_manager +from utils.utils import get_bot + +from ....base_model import Result +from ....utils import authentication +from .model import ( + DeleteFriend, + Friend, + FriendRequestResult, + Group, + GroupRequestResult, + GroupResult, + HandleRequest, + LeaveGroup, + Task, + UpdateGroup, +) + +router = APIRouter() + + +@router.get("/get_group_list", dependencies=[authentication()], description="获取群组列表") +async def _(bot_id: str) -> Result: + """ + 获取群信息 + """ + if bots := nonebot.get_bots(): + if bot_id not in bots: + return Result.warning_("指定Bot未连接...") + group_list_result = [] + try: + group_info = {} + group_list = await bots[bot_id].get_group_list() + for g in group_list: + group_info[g["group_id"]] = Group(**g) + group_data = group_manager.get_data() + for group_id in group_data.group_manager: + task_list = [] + data = group_manager[group_id].dict() + for tn, status in data["group_task_status"].items(): + task_list.append( + Task( + **{ + "name": tn, + "nameZh": group_manager.get_task_data().get(tn) or tn, + "status": status, + } + ) + ) + data["task"] = task_list + if x := group_info.get(int(group_id)): + data["group"] = x + else: + continue + group_list_result.append(GroupResult(**data)) + except Exception as e: + logger.error("调用API错误", "/get_group_list", e=e) + return Result.fail(f"{type(e)}: {e}") + return Result.ok(group_list_result, "拿到了新鲜出炉的数据!") + return Result.warning_("无Bot连接...") + + +@router.post("/update_group", dependencies=[authentication()], description="修改群组信息") +async def _(group: UpdateGroup) -> Result: + try: + group_id = group.group_id + group_manager.set_group_level(group_id, group.level) + if group.status: + group_manager.turn_on_group_bot_status(group_id) + else: + group_manager.shutdown_group_bot_status(group_id) + if group.task_status: + for task in group.task_status: + if group.task_status[task]: + group_manager.open_group_task(group_id, task) + else: + group_manager.close_group_task(group_id, task) + group_manager.save() + except Exception as e: + logger.error("调用API错误", "/get_group", e=e) + return Result.fail(f"{type(e)}: {e}") + return Result.ok(info="已完成记录!") + + +@router.get("/get_friend_list", dependencies=[authentication()], description="获取好友列表") +async def _(bot_id: str) -> Result: + """ + 获取群信息 + """ + if bots := nonebot.get_bots(): + if bot_id not in bots: + return Result.warning_("指定Bot未连接...") + try: + friend_list = await bots[bot_id].get_friend_list() + return Result.ok([Friend(**f) for f in friend_list], "拿到了新鲜出炉的数据!") + except Exception as e: + logger.error("调用API错误", "/get_group_list", e=e) + return Result.fail(f"{type(e)}: {e}") + return Result.warning_("无Bot连接...") + + +@router.get("/get_request_count", dependencies=[authentication()], description="获取请求数量") +def _() -> Result: + data = { + "friend_count": len(requests_manager.get_data().get("private") or []), + "group_count": len(requests_manager.get_data().get("group") or []), + } + return Result.ok(data, f"{NICKNAME}带来了最新的数据!") + + +@router.get("/get_request_list", dependencies=[authentication()], description="获取请求列表") +def _(request_type: Literal["private", "group"]) -> Result: + try: + req_data = requests_manager.get_data().get(request_type) or [] + req_list = [] + for x in req_data: + req_data[x]["oid"] = x + if request_type == "private": + req_list.append(FriendRequestResult(**req_data[x])) + else: + req_list.append(GroupRequestResult(**req_data[x])) + req_list.reverse() + except Exception as e: + logger.error("调用API错误", "/get_request", e=e) + return Result.fail(f"{type(e)}: {e}") + return Result.ok(req_list, f"{NICKNAME}带来了最新的数据!") + + +@router.delete("/clear_request", dependencies=[authentication()], description="清空请求列表") +def _(request_type: Literal["private", "group"]) -> Result: + """ + 清空请求 + :param type_: 类型 + """ + requests_manager.clear(request_type) + return Result.ok(info="成功清除了数据!") + + +@router.post("/refuse_request", dependencies=[authentication()], description="拒绝请求") +async def _(parma: HandleRequest) -> Result: + """ + 操作请求 + :param parma: 参数 + """ + try: + if bots := nonebot.get_bots(): + bot_id = parma.bot_id + if bot_id not in nonebot.get_bots(): + return Result.warning_("指定Bot未连接...") + flag = await requests_manager.refused(bots[bot_id], parma.id, parma.request_type) # type: ignore + if flag == 1: + requests_manager.delete_request(parma.id, parma.request_type) + return Result.warning_("该请求已失效...") + elif flag == 2: + return Result.warning_("未找到此Id请求...") + return Result.ok(info="成功处理了请求!") + return Result.warning_("Bot未连接...") + except Exception as e: + logger.error("调用API错误", "/refuse_request", e=e) + return Result.fail(f"{type(e)}: {e}") + + +@router.post("/delete_request", dependencies=[authentication()], description="忽略请求") +async def _(parma: HandleRequest) -> Result: + """ + 操作请求 + :param parma: 参数 + """ + requests_manager.delete_request(parma.id, parma.request_type) + return Result.ok(info="成功处理了请求!") + + +@router.post("/approve_request", dependencies=[authentication()], description="同意请求") +async def _(parma: HandleRequest) -> Result: + """ + 操作请求 + :param parma: 参数 + """ + try: + if bots := nonebot.get_bots(): + bot_id = parma.bot_id + if bot_id not in nonebot.get_bots(): + return Result.warning_("指定Bot未连接...") + if parma.request_type == "group": + if rid := requests_manager.get_group_id(parma.id): + if group := await GroupInfo.get_or_none(group_id=str(rid)): + await group.update_or_create(group_flag=1) + else: + group_info = await bots[bot_id].get_group_info(group_id=rid) + await GroupInfo.update_or_create( + group_id=str(group_info["group_id"]), + defaults={ + "group_name": group_info["group_name"], + "max_member_count": group_info["max_member_count"], + "member_count": group_info["member_count"], + "group_flag": 1, + }, + ) + await requests_manager.approve(bots[bot_id], parma.id, parma.request_type) # type: ignore + return Result.ok(info="成功处理了请求!") + return Result.warning_("Bot未连接...") + except Exception as e: + logger.error("调用API错误", "/approve_request", e=e) + return Result.fail(f"{type(e)}: {e}") + + +@router.post("/leave_group", dependencies=[authentication()], description="退群") +async def _(param: LeaveGroup) -> Result: + try: + if bots := nonebot.get_bots(): + bot_id = param.bot_id + group_list = await bots[bot_id].get_group_list() + if param.group_id not in [str(g["group_id"]) for g in group_list]: + return Result.warning_("Bot未在该群聊中...") + await bots[bot_id].set_group_leave(group_id=param.group_id) + return Result.ok(info="成功处理了请求!") + return Result.warning_("Bot未连接...") + except Exception as e: + logger.error("调用API错误", "/leave_group", e=e) + return Result.fail(f"{type(e)}: {e}") + + +@router.post("/delete_friend", dependencies=[authentication()], description="删除好友") +async def _(param: DeleteFriend) -> Result: + try: + if bots := nonebot.get_bots(): + bot_id = param.bot_id + friend_list = await bots[bot_id].get_friend_list() + if param.user_id not in [str(g["user_id"]) for g in friend_list]: + return Result.warning_("Bot未有其好友...") + await bots[bot_id].delete_friend(user_id=param.user_id) + return Result.ok(info="成功处理了请求!") + return Result.warning_("Bot未连接...") + except Exception as e: + logger.error("调用API错误", "/delete_friend", e=e) + return Result.fail(f"{type(e)}: {e}") diff --git a/plugins/web_ui/api/tabs/manage/model.py b/plugins/web_ui/api/tabs/manage/model.py new file mode 100644 index 00000000..089da80d --- /dev/null +++ b/plugins/web_ui/api/tabs/manage/model.py @@ -0,0 +1,151 @@ +from typing import Dict, List, Literal, Optional, Union + +from matplotlib.dates import FR +from nonebot.adapters.onebot.v11 import Bot +from pydantic import BaseModel + + +class Group(BaseModel): + """ + 群组信息 + """ + + group_id: Union[str, int] + """群组id""" + group_name: str + """群组名称""" + member_count: int + """成员人数""" + max_member_count: int + """群组最大人数""" + + +class Task(BaseModel): + """ + 被动技能 + """ + + name: str + """被动名称""" + nameZh: str + """被动中文名称""" + status: bool + """状态""" + + +class GroupResult(BaseModel): + """ + 群组返回数据 + """ + + group: Group + """Group""" + level: int + """群等级""" + status: bool + """状态""" + close_plugins: List[str] + """关闭的插件""" + task: List[Task] + """被动列表""" + + +class Friend(BaseModel): + """ + 好友数据 + """ + + user_id: Union[str, int] + """用户id""" + nickname: str = "" + """昵称""" + remark: str = "" + """备注""" + + +class UpdateGroup(BaseModel): + """ + 更新群组信息 + """ + + group_id: str + """群号""" + status: bool + """状态""" + level: int + """群权限""" + task_status: Dict[str, bool] + """被动状态""" + + +class FriendRequestResult(BaseModel): + """ + 好友/群组请求管理 + """ + + bot_id: Union[str, int] + """bot_id""" + oid: str + """排序""" + id: int + """id""" + flag: str + """flag""" + nickname: Optional[str] + """昵称""" + level: Optional[int] + """等级""" + sex: Optional[str] + """性别""" + age: Optional[int] + """年龄""" + from_: Optional[str] + """来自""" + comment: Optional[str] + """备注信息""" + + +class GroupRequestResult(FriendRequestResult): + """ + 群聊邀请请求 + """ + + invite_group: Union[int, str] + """邀请群聊""" + group_name: Optional[str] + """群聊名称""" + + +class HandleRequest(BaseModel): + """ + 操作请求接收数据 + """ + + bot_id: str + """bot_id""" + id: int + """id""" + request_type: Literal["private", "group"] + """类型""" + + +class LeaveGroup(BaseModel): + """ + 退出群聊 + """ + + bot_id: str + """bot_id""" + group_id: str + """群聊id""" + + +class DeleteFriend(BaseModel): + """ + 删除好友 + """ + + bot_id: str + """bot_id""" + user_id: str + """用户id""" diff --git a/plugins/web_ui/auth/__init__.py b/plugins/web_ui/auth/__init__.py index 88a726d3..0040be85 100644 --- a/plugins/web_ui/auth/__init__.py +++ b/plugins/web_ui/auth/__init__.py @@ -5,7 +5,7 @@ import nonebot from fastapi import APIRouter, Depends from fastapi.security import OAuth2PasswordRequestForm -from ..models.model import Result +from ..base_model import Result from ..utils import ( ACCESS_TOKEN_EXPIRE_MINUTES, create_token, diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py new file mode 100644 index 00000000..3f2e562f --- /dev/null +++ b/plugins/web_ui/base_model.py @@ -0,0 +1,139 @@ +from datetime import datetime +from logging import warning +from typing import Any, Dict, List, Optional, TypeVar, Union + +from nonebot.adapters.onebot.v11 import Bot +from pydantic import BaseModel + +from configs.utils import Config +from utils.manager.models import Plugin as PluginManager +from utils.manager.models import PluginBlock, PluginCd, PluginCount, PluginSetting + + +class User(BaseModel): + username: str + password: str + + +class Token(BaseModel): + access_token: str + token_type: str + + +class Result(BaseModel): + """ + 总体返回 + """ + + suc: bool + """调用状态""" + code: int = 200 + """code""" + info: str = "操作成功" + """info""" + warning: Optional[str] = None + """警告信息""" + data: Any = None + """返回数据""" + + @classmethod + def warning_(cls, info: str, code: int = 200) -> "Result": + return cls(suc=True, warning=info, code=code) + + @classmethod + def fail(cls, info: str = "异常错误", code: int = 500) -> "Result": + return cls(suc=False, info=info, code=code) + + @classmethod + def ok(cls, data: Any = None, info: str = "操作成功", code: int = 200) -> "Result": + return cls(suc=True, info=info, code=code, data=data) + + +# class PluginConfig(BaseModel): +# """ +# 插件配置项 +# """ + +# module: str +# key: str +# value: Optional[Any] +# help: Optional[str] +# default_value: Optional[Any] +# has_type: bool + + +# class Plugin(BaseModel): +# """ +# 插件 +# """ + +# model: str +# """模块名称""" +# plugin_settings: Optional[PluginSetting] +# """settings""" +# plugin_manager: Optional[PluginManager] +# """manager""" +# plugin_config: Optional[Dict[str, PluginConfig]] +# """配置项""" +# cd_limit: Optional[PluginCd] +# """cd限制""" +# block_limit: Optional[PluginBlock] +# """阻断限制""" +# count_limit: Optional[PluginCount] +# """次数限制""" + + +class SystemStatus(BaseModel): + """ + 系统状态 + """ + + cpu: float + memory: float + disk: float + check_time: datetime + + +class SystemNetwork(BaseModel): + """ + 系统网络状态 + """ + + baidu: int + google: int + + +class SystemFolderSize(BaseModel): + """ + 资源文件占比 + """ + + font_dir_size: float + image_dir_size: float + text_dir_size: float + record_dir_size: float + temp_dir_size: float + data_dir_size: float + log_dir_size: float + check_time: datetime + + +class SystemStatusList(BaseModel): + """ + 状态记录 + """ + + cpu_data: List[Dict[str, Union[float, str]]] + memory_data: List[Dict[str, Union[float, str]]] + disk_data: List[Dict[str, Union[float, str]]] + + +class SystemResult(BaseModel): + """ + 系统api返回 + """ + + status: SystemStatus + network: SystemNetwork + disk: SystemFolderSize + check_time: datetime diff --git a/plugins/web_ui/config.py b/plugins/web_ui/config.py index b2cf0145..a85d9126 100644 --- a/plugins/web_ui/config.py +++ b/plugins/web_ui/config.py @@ -5,6 +5,7 @@ import nonebot from fastapi import APIRouter from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel +from strenum import StrEnum app = nonebot.get_app() @@ -19,85 +20,62 @@ app.add_middleware( ) -class RequestResult(BaseModel): +class QueryDateType(StrEnum): + """ - 好友/群组请求管理 + 查询日期类型 """ - oid: str - id: int - flag: str - nickname: Optional[str] - level: Optional[int] - sex: Optional[str] - age: Optional[int] - from_: Optional[str] - comment: Optional[str] - invite_group: Optional[int] - group_name: Optional[str] + DAY = "day" + """日""" + WEEK = "week" + """周""" + MONTH = "month" + """月""" + YEAR = "year" + """年""" -class RequestParma(BaseModel): - """ - 操作请求接收数据 - """ +# class SystemNetwork(BaseModel): +# """ +# 系统网络状态 +# """ - id: int - handle: str - type: str +# baidu: int +# google: int -class SystemStatus(BaseModel): - """ - 系统状态 - """ +# class SystemFolderSize(BaseModel): +# """ +# 资源文件占比 +# """ - cpu: int - memory: int - disk: int - check_time: datetime +# font_dir_size: float +# image_dir_size: float +# text_dir_size: float +# record_dir_size: float +# temp_dir_size: float +# data_dir_size: float +# log_dir_size: float +# check_time: datetime -class SystemNetwork(BaseModel): - """ - 系统网络状态 - """ +# class SystemStatusList(BaseModel): +# """ +# 状态记录 +# """ - baidu: int - google: int +# cpu_data: List[Dict[str, Union[float, str]]] +# memory_data: List[Dict[str, Union[float, str]]] +# disk_data: List[Dict[str, Union[float, str]]] -class SystemFolderSize(BaseModel): - """ - 资源文件占比 - """ +# class SystemResult(BaseModel): +# """ +# 系统api返回 +# """ - font_dir_size: float - image_dir_size: float - text_dir_size: float - record_dir_size: float - temp_dir_size: float - data_dir_size: float - log_dir_size: float - check_time: datetime - - -class SystemStatusList(BaseModel): - """ - 状态记录 - """ - - cpu_data: List[Dict[str, Union[float, str]]] - memory_data: List[Dict[str, Union[float, str]]] - disk_data: List[Dict[str, Union[float, str]]] - - -class SystemResult(BaseModel): - """ - 系统api返回 - """ - - status: SystemStatus - network: SystemNetwork - disk: SystemFolderSize - check_time: datetime +# status: SystemStatus +# network: SystemNetwork +# disk: SystemFolderSize +# check_time: datetime diff --git a/plugins/web_ui/models/model.py b/plugins/web_ui/models/model.py deleted file mode 100644 index 257678b0..00000000 --- a/plugins/web_ui/models/model.py +++ /dev/null @@ -1,223 +0,0 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional, TypeVar, Union - -from nonebot.adapters.onebot.v11 import Bot -from pydantic import BaseModel - -from configs.utils import Config -from utils.manager.models import Plugin as PluginManager -from utils.manager.models import PluginBlock, PluginCd, PluginCount, PluginSetting - - -class User(BaseModel): - username: str - password: str - - -class Token(BaseModel): - access_token: str - token_type: str - - -class Result(BaseModel): - """ - 总体返回 - """ - - suc: bool - """调用状态""" - code: int = 200 - """code""" - info: str = "操作成功" - """info""" - data: Any = None - """返回数据""" - - @classmethod - def fail(cls, info: str = "异常错误", code: int = 500) -> "Result": - return cls(suc=False, info=info, code=code) - - @classmethod - def ok(cls, data: Any = None, info: str = "操作成功", code: int = 200) -> "Result": - return cls(suc=True, info=info, code=code, data=data) - - -class PluginConfig(BaseModel): - """ - 插件配置项 - """ - - module: str - key: str - value: Optional[Any] - help: Optional[str] - default_value: Optional[Any] - has_type: bool - - -class Plugin(BaseModel): - """ - 插件 - """ - - model: str - """模块名称""" - plugin_settings: Optional[PluginSetting] - """settings""" - plugin_manager: Optional[PluginManager] - """manager""" - plugin_config: Optional[Dict[str, PluginConfig]] - """配置项""" - cd_limit: Optional[PluginCd] - """cd限制""" - block_limit: Optional[PluginBlock] - """阻断限制""" - count_limit: Optional[PluginCount] - """次数限制""" - - -class Group(BaseModel): - """ - 群组信息 - """ - - group_id: int - group_name: str - member_count: int - max_member_count: int - - -class Task(BaseModel): - """ - 被动技能 - """ - - name: str - nameZh: str - status: bool - - -class GroupResult(BaseModel): - """ - 群组返回数据 - """ - - group: Group - level: int - status: bool - close_plugins: List[str] - task: List[Task] - - -class RequestResult(BaseModel): - """ - 好友/群组请求管理 - """ - - oid: str - id: int - flag: str - nickname: Optional[str] - level: Optional[int] - sex: Optional[str] - age: Optional[int] - from_: Optional[str] - comment: Optional[str] - invite_group: Optional[int] - group_name: Optional[str] - - -class SystemStatus(BaseModel): - """ - 系统状态 - """ - - cpu: float - memory: float - disk: float - check_time: datetime - - -class SystemNetwork(BaseModel): - """ - 系统网络状态 - """ - - baidu: int - google: int - - -class SystemFolderSize(BaseModel): - """ - 资源文件占比 - """ - - font_dir_size: float - image_dir_size: float - text_dir_size: float - record_dir_size: float - temp_dir_size: float - data_dir_size: float - log_dir_size: float - check_time: datetime - - -class SystemStatusList(BaseModel): - """ - 状态记录 - """ - - cpu_data: List[Dict[str, Union[float, str]]] - memory_data: List[Dict[str, Union[float, str]]] - disk_data: List[Dict[str, Union[float, str]]] - - -class SystemResult(BaseModel): - """ - 系统api返回 - """ - - status: SystemStatus - network: SystemNetwork - disk: SystemFolderSize - check_time: datetime - - -class BotInfo(BaseModel): - """ - Bot基础信息 - """ - - bot: Bot - """Bot""" - self_id: str - """SELF ID""" - nickname: str - """昵称""" - ava_url: str - """头像url""" - friend_count: int = 0 - """好友数量""" - group_count: int = 0 - """群聊数量""" - received_messages: int = 0 - """累计接收消息""" - received_messages_day: int = 0 - """今日累计接收消息""" - received_messages_week: int = 0 - """一周内累计接收消息""" - received_messages_month: int = 0 - """一月内累计接收消息""" - - plugin_count: int = 0 - """加载插件数量""" - success_plugin_count: int = 0 - """加载成功插件数量""" - fail_plugin_count: int = 0 - """加载失败插件数量""" - - is_select: bool = False - """当前选择""" - - class Config: - arbitrary_types_allowed = True diff --git a/plugins/web_ui/models/params.py b/plugins/web_ui/models/params.py deleted file mode 100644 index 86a30401..00000000 --- a/plugins/web_ui/models/params.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Any, List - -from pydantic import BaseModel - - -class UpdatePlugin(BaseModel): - """ - 插件修改参数 - """ - - module: str - """模块""" - default_status: bool - """默认开关""" - limit_superuser: bool - """限制超级用户""" - cost_gold: int - """金币花费""" - cmd: List[str] - """插件别名""" - menu_type: str - """插件菜单类型""" - group_level: int - """插件所需群权限""" - block_type: str - """禁用类型""" - - -class UpdateConfig(BaseModel): - """ - 配置项修改参数 - """ - - module: str - """模块""" - key: str - """配置项key""" - value: Any - """配置项值""" - - -class UpdateGroup(BaseModel): - - group_id: str - """群号""" - status: bool - """状态""" - level: int - """群权限""" - - -class HandleRequest(BaseModel): - """ - 操作请求接收数据 - """ - - id: int - handle: str - type: str diff --git a/plugins/web_ui/utils.py b/plugins/web_ui/utils.py index 4531f3e7..cce1c692 100644 --- a/plugins/web_ui/utils.py +++ b/plugins/web_ui/utils.py @@ -1,16 +1,35 @@ +import os from datetime import datetime, timedelta -from typing import Any, Optional +from pathlib import Path +from typing import Any, Dict, Optional, Union -import nonebot +import psutil import ujson as json from fastapi import Depends, HTTPException from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt +from nonebot.utils import run_sync from configs.config import Config -from configs.path_config import DATA_PATH +from configs.path_config import ( + DATA_PATH, + FONT_PATH, + IMAGE_PATH, + LOG_PATH, + RECORD_PATH, + TEMP_PATH, + TEXT_PATH, +) -from .models.model import Result, User +from .base_model import ( + Result, + SystemFolderSize, + SystemNetwork, + SystemResult, + SystemStatus, + SystemStatusList, + User, +) SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" ALGORITHM = "HS256" @@ -29,6 +48,14 @@ if token_file.exists(): def get_user(uname: str) -> Optional[User]: + """获取账号密码 + + 参数: + uname: uname + + 返回: + Optional[User]: 用户信息 + """ username = Config.get_config("web-ui", "username") password = Config.get_config("web-ui", "password") if username and password and uname == username: @@ -36,6 +63,12 @@ def get_user(uname: str) -> Optional[User]: def create_token(user: User, expires_delta: Optional[timedelta] = None): + """创建token + + 参数: + user: 用户信息 + expires_delta: 过期时间. + """ expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15)) return jwt.encode( claims={"sub": user.username, "exp": expire}, @@ -45,6 +78,13 @@ def create_token(user: User, expires_delta: Optional[timedelta] = None): def authentication(): + """权限验证 + + + 异常: + JWTError: JWTError + HTTPException: HTTPException + """ # if token not in token_data["token"]: def inner(token: str = Depends(oauth2_scheme)): try: @@ -57,3 +97,81 @@ def authentication(): raise HTTPException(status_code=400, detail="登录验证失败或已失效, 踢出房间!") return Depends(inner) + + +def _get_dir_size(dir_path: Path) -> float: + """ + 说明: + 获取文件夹大小 + 参数: + :param dir_path: 文件夹路径 + """ + size = 0 + for root, dirs, files in os.walk(dir_path): + size += sum([os.path.getsize(os.path.join(root, name)) for name in files]) + return size + + +@run_sync +def get_system_status() -> SystemStatus: + """ + 说明: + 获取系统信息等 + """ + cpu = psutil.cpu_percent() + memory = psutil.virtual_memory().percent + disk = psutil.disk_usage("/").percent + return SystemStatus( + cpu=cpu, + memory=memory, + disk=disk, + check_time=datetime.now().replace(microsecond=0), + ) + + +@run_sync +def get_system_disk( + type_: Optional[str], +) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]: + """ + 说明: + 获取资源文件大小等 + """ + if not type_: + disk = SystemFolderSize( + font_dir_size=_get_dir_size(FONT_PATH) / 1024 / 1024, + image_dir_size=_get_dir_size(IMAGE_PATH) / 1024 / 1024, + text_dir_size=_get_dir_size(TEXT_PATH) / 1024 / 1024, + record_dir_size=_get_dir_size(RECORD_PATH) / 1024 / 1024, + temp_dir_size=_get_dir_size(TEMP_PATH) / 1024 / 102, + data_dir_size=_get_dir_size(DATA_PATH) / 1024 / 1024, + log_dir_size=_get_dir_size(LOG_PATH) / 1024 / 1024, + check_time=datetime.now().replace(microsecond=0), + ) + return disk + else: + if type_ == "image": + dir_path = IMAGE_PATH + elif type_ == "font": + dir_path = FONT_PATH + elif type_ == "text": + dir_path = TEXT_PATH + elif type_ == "record": + dir_path = RECORD_PATH + elif type_ == "data": + dir_path = DATA_PATH + elif type_ == "temp": + dir_path = TEMP_PATH + else: + dir_path = LOG_PATH + dir_map = {} + other_file_size = 0 + for file in os.listdir(dir_path): + file = Path(dir_path / file) + if file.is_dir(): + dir_map[file.name] = _get_dir_size(file) / 1024 / 1024 + else: + other_file_size += os.path.getsize(file) / 1024 / 1024 + dir_map["其他文件"] = other_file_size + dir_map["check_time"] = datetime.now().replace(microsecond=0) + return dir_map diff --git a/utils/browser.py b/utils/browser.py index c7a15587..66530008 100755 --- a/utils/browser.py +++ b/utils/browser.py @@ -2,7 +2,6 @@ import asyncio from typing import Optional from nonebot import get_driver -from nonebot.log import logger from playwright.async_api import Browser, Playwright, async_playwright from services.log import logger @@ -26,7 +25,7 @@ async def shutdown_browser(): if _browser: await _browser.close() if _playwright: - _playwright.stop() + await _playwright.stop() # type: ignore def get_browser() -> Browser: diff --git a/utils/manager/data_class.py b/utils/manager/data_class.py index cdfcd64d..b93a0ef5 100755 --- a/utils/manager/data_class.py +++ b/utils/manager/data_class.py @@ -30,7 +30,7 @@ class StaticData(Generic[T]): try: self._data: dict = json.load(f) except ValueError: - if f.read().strip(): + if not f.read().strip(): raise ValueError(f"{file} 文件加载错误,请检查文件内容格式.") elif file.name.endswith("yaml"): self._data = _yaml.load(f) diff --git a/utils/manager/requests_manager.py b/utils/manager/requests_manager.py index 41bef067..c97f2000 100644 --- a/utils/manager/requests_manager.py +++ b/utils/manager/requests_manager.py @@ -1,11 +1,13 @@ -from utils.manager.data_class import StaticData -from nonebot.adapters.onebot.v11 import Bot, ActionFailed -from services.log import logger -from typing import Optional -from utils.image_utils import BuildImage -from utils.utils import get_user_avatar -from pathlib import Path from io import BytesIO +from pathlib import Path +from typing import Optional + +from nonebot.adapters.onebot.v11 import ActionFailed, Bot + +from services.log import logger +from utils.image_utils import BuildImage +from utils.manager.data_class import StaticData +from utils.utils import get_user_avatar class RequestManager(StaticData): @@ -21,6 +23,7 @@ class RequestManager(StaticData): def add_request( self, + bot_id: str, id_: int, type_: str, flag: str, @@ -36,6 +39,7 @@ class RequestManager(StaticData): ): """ 添加一个请求 + :param bot_id: bot_id :param id_: id,用户id或群id :param type_: 类型,private 或 group :param flag: event.flag @@ -49,6 +53,7 @@ class RequestManager(StaticData): :param group_name: 群聊名称 """ self._data[type_][str(len(self._data[type_].keys()))] = { + "bot_id": bot_id, "id": id_, "flag": flag, "nickname": nickname, @@ -102,7 +107,9 @@ class RequestManager(StaticData): """ return await self._set_add_request(bot, id_, type_, False) - def clear(self, type_: Optional[str] = None): # type_: Optional[Literal["group", "private"]] = None + def clear( + self, type_: Optional[str] = None + ): # type_: Optional[Literal["group", "private"]] = None """ 清空所有请求信息,无视请求 :param type_: 类型 @@ -113,7 +120,9 @@ class RequestManager(StaticData): self._data = {"private": {}, "group": {}} self.save() - def delete_request(self, id_: int, type_: str): # type_: Literal["group", "private"] + def delete_request( + self, id_: int, type_: str + ): # type_: Literal["group", "private"] """ 删除请求 :param id_: id @@ -230,7 +239,7 @@ class RequestManager(StaticData): return bk.pic2bs4() async def _set_add_request( - self, bot: Bot, id_: int, type_: str, approve: bool + self, bot: Bot, idx: int, type_: str, approve: bool ) -> int: """ 处理请求 @@ -239,7 +248,7 @@ class RequestManager(StaticData): :param type_: 类型,private 或 group :param approve: 是否同意 """ - id_ = str(id_) + id_ = str(idx) if id_ in self._data[type_].keys(): try: if type_ == "private": @@ -259,7 +268,7 @@ class RequestManager(StaticData): f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" f"的{'好友' if type_ == 'private' else '入群'}请求失败了..." ) - return 1 # flag失效 + return 1 # flag失效 else: logger.info( f"{'同意' if approve else '拒绝'}{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" @@ -268,4 +277,4 @@ class RequestManager(StaticData): del self._data[type_][id_] self.save() return rid - return 2 # 未找到id + return 2 # 未找到id