From 081c86aba0df526b7d118ce251e697c928598508 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Thu, 28 Dec 2023 01:52:06 +0800
Subject: [PATCH 01/20] test
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index ad1eaaac..4b74dee2 100644
--- a/README.md
+++ b/README.md
@@ -338,6 +338,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
### 2023/12/28
* 修复B站动态获取失败的时候,会发送空消息
+* test
### 2023/9/6
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 02/20] =?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
From c146df3d25293d4909aab787d892bcf2cc5f4d5b Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 31 Dec 2023 01:58:26 +0800
Subject: [PATCH 03/20] =?UTF-8?q?feat=E2=9C=A8:=20webui=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/__init__.py | 11 ++-
plugins/web_ui/api/tabs/__init__.py | 1 +
plugins/web_ui/api/tabs/database/__init__.py | 76 +++++++++++++++++++
.../web_ui/api/tabs/database/models/model.py | 23 ++++++
.../api/tabs/database/models/sql_log.py | 40 ++++++++++
plugins/web_ui/base_model.py | 31 +++++++-
6 files changed, 176 insertions(+), 6 deletions(-)
create mode 100644 plugins/web_ui/api/tabs/database/__init__.py
create mode 100644 plugins/web_ui/api/tabs/database/models/model.py
create mode 100644 plugins/web_ui/api/tabs/database/models/sql_log.py
diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py
index c9c8a8ca..4e03feda 100644
--- a/plugins/web_ui/__init__.py
+++ b/plugins/web_ui/__init__.py
@@ -16,9 +16,7 @@ from utils.manager import plugins2settings_manager
# from .api.group import router as group_routes
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.tabs.database import router as database_router
# from .api.system import router as system_routes
from .api.tabs.main import router as main_router
@@ -28,6 +26,10 @@ from .api.tabs.manage import router as manage_router
# from .api.g import *
from .auth import router as auth_router
+# from .api.plugins import router as plugin_routes
+# from .api.request import router as request_routes
+
+
driver = nonebot.get_driver()
gConfig.add_plugin_config("web-ui", "username", "admin", name="web-ui", help_="前端管理用户名")
@@ -40,10 +42,11 @@ BaseApiRouter = APIRouter(prefix="/zhenxun/api")
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(main_router)
BaseApiRouter.include_router(manage_router)
+BaseApiRouter.include_router(database_router)
@driver.on_startup
diff --git a/plugins/web_ui/api/tabs/__init__.py b/plugins/web_ui/api/tabs/__init__.py
index f750065f..97abef9c 100644
--- a/plugins/web_ui/api/tabs/__init__.py
+++ b/plugins/web_ui/api/tabs/__init__.py
@@ -1,2 +1,3 @@
+from .database import *
from .main import *
from .manage import *
diff --git a/plugins/web_ui/api/tabs/database/__init__.py b/plugins/web_ui/api/tabs/database/__init__.py
new file mode 100644
index 00000000..3df9d212
--- /dev/null
+++ b/plugins/web_ui/api/tabs/database/__init__.py
@@ -0,0 +1,76 @@
+from os import name
+from typing import Optional
+
+import nonebot
+from fastapi import APIRouter, Request
+from nonebot.drivers import Driver
+from tortoise import Tortoise
+from tortoise.exceptions import OperationalError
+
+from configs.config import NICKNAME
+from services.db_context import TestSQL
+from utils.utils import get_matchers
+
+from ....base_model import QueryModel, Result
+from ....config import QueryDateType
+from ....utils import authentication
+from .models.model import SqlModel, SqlText
+from .models.sql_log import SqlLog
+
+router = APIRouter()
+
+
+driver: Driver = nonebot.get_driver()
+
+
+SQL_DICT = {}
+
+
+@driver.on_startup
+async def _():
+ for matcher in get_matchers(True):
+ if _plugin := matcher.plugin:
+ try:
+ _module = _plugin.module
+ except AttributeError:
+ pass
+ else:
+ plugin_name = matcher.plugin_name
+ if plugin_name in SQL_DICT:
+ raise ValueError(f"{plugin_name} 常用SQL plugin_name 重复")
+ SqlModel(
+ name=getattr(_module, "__plugin_name__", None) or plugin_name or "",
+ plugin_name=plugin_name or "",
+ sql_list=getattr(_module, "sql_list", []),
+ )
+ SQL_DICT[plugin_name] = SqlModel
+
+
+@router.post("/exec_sql", dependencies=[authentication()], description="执行sql")
+async def _(sql: SqlText, request: Request) -> Result:
+ ip = request.client.host if request.client else "unknown"
+ try:
+ if sql.sql.lower().startswith("select"):
+ db = Tortoise.get_connection("default")
+ res = await db.execute_query_dict(sql.sql)
+ return Result.ok(res, "执行成功啦!")
+ else:
+ result = await TestSQL.raw(sql.sql)
+ await SqlLog.add(ip or "0.0.0.0", sql.sql, str(result))
+ return Result.ok(info="执行成功啦!")
+ except OperationalError as e:
+ await SqlLog.add(ip or "0.0.0.0", sql.sql, str(e), False)
+ return Result.warning_(f"sql执行错误: {e}")
+
+
+@router.post("/get_sql_log", dependencies=[authentication()], description="sql日志列表")
+async def _(query: QueryModel) -> Result:
+ data = await SqlLog.all().offset((query.index - 1) * query.size).limit(query.size)
+ return Result.ok(data)
+
+
+@router.get("/get_sql", dependencies=[authentication()], description="常用sql")
+async def _(plugin_name: Optional[str] = None) -> Result:
+ if plugin_name:
+ return Result.ok(SQL_DICT.get(plugin_name))
+ return Result.ok(SQL_DICT)
diff --git a/plugins/web_ui/api/tabs/database/models/model.py b/plugins/web_ui/api/tabs/database/models/model.py
new file mode 100644
index 00000000..ed78c405
--- /dev/null
+++ b/plugins/web_ui/api/tabs/database/models/model.py
@@ -0,0 +1,23 @@
+from typing import List
+
+from pydantic import BaseModel
+
+from utils.models import CommonSql
+
+
+class SqlText(BaseModel):
+ """
+ sql语句
+ """
+
+ sql: str
+
+
+class SqlModel(BaseModel):
+
+ name: str
+ """插件中文名称"""
+ plugin_name: str
+ """插件名称"""
+ sql_list: List[CommonSql]
+ """插件列表"""
diff --git a/plugins/web_ui/api/tabs/database/models/sql_log.py b/plugins/web_ui/api/tabs/database/models/sql_log.py
new file mode 100644
index 00000000..d58ddd34
--- /dev/null
+++ b/plugins/web_ui/api/tabs/database/models/sql_log.py
@@ -0,0 +1,40 @@
+from typing import Optional, Union
+
+from tortoise import fields
+
+from services.db_context import Model
+
+
+class SqlLog(Model):
+
+ id = fields.IntField(pk=True, generated=True, auto_increment=True)
+ """自增id"""
+ ip = fields.CharField(255)
+ """ip"""
+ sql = fields.CharField(255)
+ """sql"""
+ result = fields.CharField(255, null=True)
+ """结果"""
+ is_suc = fields.BooleanField(default=True)
+ """是否成功"""
+ create_time = fields.DatetimeField(auto_now_add=True)
+ """创建时间"""
+
+ class Meta:
+ table = "sql_log"
+ table_description = "sql执行日志"
+
+ @classmethod
+ async def add(
+ cls, ip: str, sql: str, result: Optional[str] = None, is_suc: bool = True
+ ):
+ """
+ 说明:
+ 获取用户在群内的等级
+ 参数:
+ :param ip: ip
+ :param sql: sql
+ :param result: 返回结果
+ :param is_suc: 是否成功
+ """
+ await cls.create(ip=ip, sql=sql, result=result, is_suc=is_suc)
diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py
index 3f2e562f..d9f76531 100644
--- a/plugins/web_ui/base_model.py
+++ b/plugins/web_ui/base_model.py
@@ -1,14 +1,16 @@
from datetime import datetime
from logging import warning
-from typing import Any, Dict, List, Optional, TypeVar, Union
+from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
from nonebot.adapters.onebot.v11 import Bot
-from pydantic import BaseModel
+from pydantic import BaseModel, validator
from configs.utils import Config
from utils.manager.models import Plugin as PluginManager
from utils.manager.models import PluginBlock, PluginCd, PluginCount, PluginSetting
+T = TypeVar("T")
+
class User(BaseModel):
username: str
@@ -49,6 +51,31 @@ class Result(BaseModel):
return cls(suc=True, info=info, code=code, data=data)
+class QueryModel(BaseModel, Generic[T]):
+ """
+ 基本查询条件
+ """
+
+ index: int
+ """页数"""
+ size: int
+ """每页数量"""
+ data: T
+ """携带数据"""
+
+ @validator("index")
+ def index_validator(cls, index):
+ if index < 1:
+ raise ValueError("查询下标小于1...")
+ return index
+
+ @validator("size")
+ def size_validator(cls, size):
+ if size < 1:
+ raise ValueError("每页数量小于1...")
+ return size
+
+
# class PluginConfig(BaseModel):
# """
# 插件配置项
From a5bb1f768ea4296dfc57a7ce8c0f5c3c5fed99cf Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sat, 6 Jan 2024 17:36:22 +0800
Subject: [PATCH 04/20] =?UTF-8?q?webui=E4=B8=BB=E9=A1=B5=E5=92=8C=E6=8F=92?=
=?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86api=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../_model.py => models/statistics.py | 0
plugins/statistics/_config.py | 26 ++
plugins/statistics/statistics_handle.py | 6 +-
plugins/statistics/statistics_hook.py | 325 +++++++++---------
plugins/statistics/utils.py | 16 +
plugins/web_ui/__init__.py | 16 +-
plugins/web_ui/api/__init__.py | 4 -
plugins/web_ui/api/base_info.py | 75 ----
plugins/web_ui/api/group.py | 69 ----
plugins/web_ui/api/logs/log_manager.py | 24 +-
plugins/web_ui/api/plugins.py | 115 -------
plugins/web_ui/api/request.py | 87 -----
plugins/web_ui/api/tabs/__init__.py | 1 +
plugins/web_ui/api/tabs/main/__init__.py | 130 ++++++-
plugins/web_ui/api/tabs/main/data_source.py | 2 +-
plugins/web_ui/api/tabs/main/model.py | 54 +++
.../web_ui/api/tabs/plugin_manage/__init__.py | 149 ++++++++
.../web_ui/api/tabs/plugin_manage/model.py | 135 ++++++++
plugins/web_ui/base_model.py | 56 +--
19 files changed, 711 insertions(+), 579 deletions(-)
rename plugins/statistics/_model.py => models/statistics.py (100%)
create mode 100644 plugins/statistics/_config.py
create mode 100644 plugins/statistics/utils.py
delete mode 100644 plugins/web_ui/api/base_info.py
delete mode 100644 plugins/web_ui/api/group.py
delete mode 100644 plugins/web_ui/api/plugins.py
delete mode 100644 plugins/web_ui/api/request.py
create mode 100644 plugins/web_ui/api/tabs/plugin_manage/__init__.py
create mode 100644 plugins/web_ui/api/tabs/plugin_manage/model.py
diff --git a/plugins/statistics/_model.py b/models/statistics.py
similarity index 100%
rename from plugins/statistics/_model.py
rename to models/statistics.py
diff --git a/plugins/statistics/_config.py b/plugins/statistics/_config.py
new file mode 100644
index 00000000..edee1069
--- /dev/null
+++ b/plugins/statistics/_config.py
@@ -0,0 +1,26 @@
+from enum import Enum
+from typing import NamedTuple
+
+
+class SearchType(Enum):
+
+ """
+ 查询类型
+ """
+
+ DAY = "day_statistics"
+ """天"""
+ WEEK = "week_statistics"
+ """周"""
+ MONTH = "month_statistics"
+ """月"""
+ TOTAL = "total_statistics"
+ """总数"""
+
+
+class ParseData(NamedTuple):
+
+ global_search: bool
+ """是否全局搜索"""
+ search_type: SearchType
+ """搜索类型"""
diff --git a/plugins/statistics/statistics_handle.py b/plugins/statistics/statistics_handle.py
index 706ff10c..f0515a4b 100755
--- a/plugins/statistics/statistics_handle.py
+++ b/plugins/statistics/statistics_handle.py
@@ -1,13 +1,13 @@
import asyncio
import os
-from typing import Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
-from nonebot.params import Command, CommandArg
+from nonebot.params import CommandArg
from configs.path_config import DATA_PATH, IMAGE_PATH
from models.group_info import GroupInfo
+from utils.depends import OneCommand
from utils.image_utils import BuildMat
from utils.manager import plugins2settings_manager
from utils.message_builder import image
@@ -91,7 +91,7 @@ statistics_user_file = DATA_PATH / "statistics" / "_prefix_user_count.json"
@statistics.handle()
-async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
+async def _(bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if cmd[0][:2] == "全局":
if str(event.user_id) in bot.config.superusers:
diff --git a/plugins/statistics/statistics_hook.py b/plugins/statistics/statistics_hook.py
index 2de3a1f1..9fea8b2c 100755
--- a/plugins/statistics/statistics_hook.py
+++ b/plugins/statistics/statistics_hook.py
@@ -6,11 +6,10 @@ from nonebot.message import run_postprocessor
from nonebot.typing import Optional, T_State
from configs.path_config import DATA_PATH
+from models.statistics import Statistics
from utils.manager import plugins2settings_manager
from utils.utils import scheduler
-from ._model import Statistics
-
try:
import ujson as json
except ModuleNotFoundError:
@@ -21,69 +20,69 @@ __zx_plugin_name__ = "功能调用统计 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
-statistics_group_file = DATA_PATH / "statistics" / "_prefix_count.json"
-statistics_user_file = DATA_PATH / "statistics" / "_prefix_user_count.json"
+# statistics_group_file = DATA_PATH / "statistics" / "_prefix_count.json"
+# statistics_user_file = DATA_PATH / "statistics" / "_prefix_user_count.json"
-try:
- with open(statistics_group_file, "r", encoding="utf8") as f:
- _prefix_count_dict = json.load(f)
-except (FileNotFoundError, ValueError):
- _prefix_count_dict = {
- "total_statistics": {
- "total": {},
- },
- "day_statistics": {
- "total": {},
- },
- "week_statistics": {
- "total": {},
- },
- "month_statistics": {
- "total": {},
- },
- "start_time": str(datetime.now().date()),
- "day_index": 0,
- }
+# try:
+# with open(statistics_group_file, "r", encoding="utf8") as f:
+# _prefix_count_dict = json.load(f)
+# except (FileNotFoundError, ValueError):
+# _prefix_count_dict = {
+# "total_statistics": {
+# "total": {},
+# },
+# "day_statistics": {
+# "total": {},
+# },
+# "week_statistics": {
+# "total": {},
+# },
+# "month_statistics": {
+# "total": {},
+# },
+# "start_time": str(datetime.now().date()),
+# "day_index": 0,
+# }
-try:
- with open(statistics_user_file, "r", encoding="utf8") as f:
- _prefix_user_count_dict = json.load(f)
-except (FileNotFoundError, ValueError):
- _prefix_user_count_dict = {
- "total_statistics": {
- "total": {},
- },
- "day_statistics": {
- "total": {},
- },
- "week_statistics": {
- "total": {},
- },
- "month_statistics": {
- "total": {},
- },
- "start_time": str(datetime.now().date()),
- "day_index": 0,
- }
+# try:
+# with open(statistics_user_file, "r", encoding="utf8") as f:
+# _prefix_user_count_dict = json.load(f)
+# except (FileNotFoundError, ValueError):
+# _prefix_user_count_dict = {
+# "total_statistics": {
+# "total": {},
+# },
+# "day_statistics": {
+# "total": {},
+# },
+# "week_statistics": {
+# "total": {},
+# },
+# "month_statistics": {
+# "total": {},
+# },
+# "start_time": str(datetime.now().date()),
+# "day_index": 0,
+# }
-# 以前版本转换
-if _prefix_count_dict.get("day_index") is None:
- tmp = _prefix_count_dict.copy()
- _prefix_count_dict = {
- "total_statistics": tmp["total_statistics"],
- "day_statistics": {
- "total": {},
- },
- "week_statistics": {
- "total": {},
- },
- "month_statistics": {
- "total": {},
- },
- "start_time": tmp["start_time"],
- "day_index": 0,
- }
+# # 以前版本转换
+# if _prefix_count_dict.get("day_index") is None:
+# tmp = _prefix_count_dict.copy()
+# _prefix_count_dict = {
+# "total_statistics": tmp["total_statistics"],
+# "day_statistics": {
+# "total": {},
+# },
+# "week_statistics": {
+# "total": {},
+# },
+# "month_statistics": {
+# "total": {},
+# },
+# "start_time": tmp["start_time"],
+# "day_index": 0,
+# }
# 添加命令次数
@@ -95,7 +94,7 @@ async def _(
event: MessageEvent,
state: T_State,
):
- global _prefix_count_dict
+ # global _prefix_count_dict
if (
matcher.type == "message"
and matcher.priority not in [1, 999]
@@ -107,112 +106,112 @@ async def _(
plugin_name=matcher.plugin_name,
create_time=datetime.now(),
)
- module = matcher.plugin_name
- day_index = _prefix_count_dict["day_index"]
- try:
- group_id = str(event.group_id)
- except AttributeError:
- group_id = "total"
- user_id = str(event.user_id)
- plugin_name = plugins2settings_manager.get_plugin_data(module)
- if plugin_name and plugin_name.cmd:
- plugin_name = plugin_name.cmd[0]
- check_exists_key(group_id, user_id, plugin_name)
- for data in [_prefix_count_dict, _prefix_user_count_dict]:
- data["total_statistics"]["total"][plugin_name] += 1
- data["day_statistics"]["total"][plugin_name] += 1
- data["week_statistics"]["total"][plugin_name] += 1
- data["month_statistics"]["total"][plugin_name] += 1
- # print(_prefix_count_dict)
- if group_id != "total":
- for data in [_prefix_count_dict, _prefix_user_count_dict]:
- if data == _prefix_count_dict:
- key = group_id
- else:
- key = user_id
- data["total_statistics"][key][plugin_name] += 1
- data["day_statistics"][key][plugin_name] += 1
- data["week_statistics"][key][str(day_index % 7)][plugin_name] += 1
- data["month_statistics"][key][str(day_index % 30)][plugin_name] += 1
- with open(statistics_group_file, "w", encoding="utf8") as f:
- json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False)
- with open(statistics_user_file, "w", encoding="utf8") as f:
- json.dump(_prefix_user_count_dict, f, ensure_ascii=False, indent=4)
+ # module = matcher.plugin_name
+ # day_index = _prefix_count_dict["day_index"]
+ # try:
+ # group_id = str(event.group_id)
+ # except AttributeError:
+ # group_id = "total"
+ # user_id = str(event.user_id)
+ # plugin_name = plugins2settings_manager.get_plugin_data(module)
+ # if plugin_name and plugin_name.cmd:
+ # plugin_name = plugin_name.cmd[0]
+ # check_exists_key(group_id, user_id, plugin_name)
+ # for data in [_prefix_count_dict, _prefix_user_count_dict]:
+ # data["total_statistics"]["total"][plugin_name] += 1
+ # data["day_statistics"]["total"][plugin_name] += 1
+ # data["week_statistics"]["total"][plugin_name] += 1
+ # data["month_statistics"]["total"][plugin_name] += 1
+ # # print(_prefix_count_dict)
+ # if group_id != "total":
+ # for data in [_prefix_count_dict, _prefix_user_count_dict]:
+ # if data == _prefix_count_dict:
+ # key = group_id
+ # else:
+ # key = user_id
+ # data["total_statistics"][key][plugin_name] += 1
+ # data["day_statistics"][key][plugin_name] += 1
+ # data["week_statistics"][key][str(day_index % 7)][plugin_name] += 1
+ # data["month_statistics"][key][str(day_index % 30)][plugin_name] += 1
+ # with open(statistics_group_file, "w", encoding="utf8") as f:
+ # json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False)
+ # with open(statistics_user_file, "w", encoding="utf8") as f:
+ # json.dump(_prefix_user_count_dict, f, ensure_ascii=False, indent=4)
-def check_exists_key(group_id: str, user_id: str, plugin_name: str):
- global _prefix_count_dict, _prefix_user_count_dict
- for data in [_prefix_count_dict, _prefix_user_count_dict]:
- if data == _prefix_count_dict:
- key = group_id
- else:
- key = user_id
- if not data["total_statistics"]["total"].get(plugin_name):
- data["total_statistics"]["total"][plugin_name] = 0
- if not data["day_statistics"]["total"].get(plugin_name):
- data["day_statistics"]["total"][plugin_name] = 0
- if not data["week_statistics"]["total"].get(plugin_name):
- data["week_statistics"]["total"][plugin_name] = 0
- if not data["month_statistics"]["total"].get(plugin_name):
- data["month_statistics"]["total"][plugin_name] = 0
+# def check_exists_key(group_id: str, user_id: str, plugin_name: str):
+# global _prefix_count_dict, _prefix_user_count_dict
+# for data in [_prefix_count_dict, _prefix_user_count_dict]:
+# if data == _prefix_count_dict:
+# key = group_id
+# else:
+# key = user_id
+# if not data["total_statistics"]["total"].get(plugin_name):
+# data["total_statistics"]["total"][plugin_name] = 0
+# if not data["day_statistics"]["total"].get(plugin_name):
+# data["day_statistics"]["total"][plugin_name] = 0
+# if not data["week_statistics"]["total"].get(plugin_name):
+# data["week_statistics"]["total"][plugin_name] = 0
+# if not data["month_statistics"]["total"].get(plugin_name):
+# data["month_statistics"]["total"][plugin_name] = 0
- if not data["total_statistics"].get(key):
- data["total_statistics"][key] = {}
- if not data["total_statistics"][key].get(plugin_name):
- data["total_statistics"][key][plugin_name] = 0
- if not data["day_statistics"].get(key):
- data["day_statistics"][key] = {}
- if not data["day_statistics"][key].get(plugin_name):
- data["day_statistics"][key][plugin_name] = 0
+# if not data["total_statistics"].get(key):
+# data["total_statistics"][key] = {}
+# if not data["total_statistics"][key].get(plugin_name):
+# data["total_statistics"][key][plugin_name] = 0
+# if not data["day_statistics"].get(key):
+# data["day_statistics"][key] = {}
+# if not data["day_statistics"][key].get(plugin_name):
+# data["day_statistics"][key][plugin_name] = 0
- if key != "total":
- if not data["week_statistics"].get(key):
- data["week_statistics"][key] = {}
- if data["week_statistics"][key].get("0") is None:
- for i in range(7):
- data["week_statistics"][key][str(i)] = {}
- if data["week_statistics"][key]["0"].get(plugin_name) is None:
- for i in range(7):
- data["week_statistics"][key][str(i)][plugin_name] = 0
+# if key != "total":
+# if not data["week_statistics"].get(key):
+# data["week_statistics"][key] = {}
+# if data["week_statistics"][key].get("0") is None:
+# for i in range(7):
+# data["week_statistics"][key][str(i)] = {}
+# if data["week_statistics"][key]["0"].get(plugin_name) is None:
+# for i in range(7):
+# data["week_statistics"][key][str(i)][plugin_name] = 0
- if not data["month_statistics"].get(key):
- data["month_statistics"][key] = {}
- if data["month_statistics"][key].get("0") is None:
- for i in range(30):
- data["month_statistics"][key][str(i)] = {}
- if data["month_statistics"][key]["0"].get(plugin_name) is None:
- for i in range(30):
- data["month_statistics"][key][str(i)][plugin_name] = 0
+# if not data["month_statistics"].get(key):
+# data["month_statistics"][key] = {}
+# if data["month_statistics"][key].get("0") is None:
+# for i in range(30):
+# data["month_statistics"][key][str(i)] = {}
+# if data["month_statistics"][key]["0"].get(plugin_name) is None:
+# for i in range(30):
+# data["month_statistics"][key][str(i)][plugin_name] = 0
# 天
-@scheduler.scheduled_job(
- "cron",
- hour=0,
- minute=1,
-)
-async def _():
- for data in [_prefix_count_dict, _prefix_user_count_dict]:
- data["day_index"] += 1
- for x in data["day_statistics"].keys():
- for key in data["day_statistics"][x].keys():
- try:
- data["day_statistics"][x][key] = 0
- except KeyError:
- pass
- for type_ in ["week_statistics", "month_statistics"]:
- index = str(
- data["day_index"] % 7
- if type_ == "week_statistics"
- else data["day_index"] % 30
- )
- for x in data[type_].keys():
- try:
- for key in data[type_][x][index].keys():
- data[type_][x][index][key] = 0
- except KeyError:
- pass
- with open(statistics_group_file, "w", encoding="utf8") as f:
- json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False)
- with open(statistics_user_file, "w", encoding="utf8") as f:
- json.dump(_prefix_user_count_dict, f, indent=4, ensure_ascii=False)
+# @scheduler.scheduled_job(
+# "cron",
+# hour=0,
+# minute=1,
+# )
+# async def _():
+# for data in [_prefix_count_dict, _prefix_user_count_dict]:
+# data["day_index"] += 1
+# for x in data["day_statistics"].keys():
+# for key in data["day_statistics"][x].keys():
+# try:
+# data["day_statistics"][x][key] = 0
+# except KeyError:
+# pass
+# for type_ in ["week_statistics", "month_statistics"]:
+# index = str(
+# data["day_index"] % 7
+# if type_ == "week_statistics"
+# else data["day_index"] % 30
+# )
+# for x in data[type_].keys():
+# try:
+# for key in data[type_][x][index].keys():
+# data[type_][x][index][key] = 0
+# except KeyError:
+# pass
+# with open(statistics_group_file, "w", encoding="utf8") as f:
+# json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False)
+# with open(statistics_user_file, "w", encoding="utf8") as f:
+# json.dump(_prefix_user_count_dict, f, indent=4, ensure_ascii=False)
diff --git a/plugins/statistics/utils.py b/plugins/statistics/utils.py
new file mode 100644
index 00000000..c35b5672
--- /dev/null
+++ b/plugins/statistics/utils.py
@@ -0,0 +1,16 @@
+from typing import List
+from nonebot.adapters.onebot.v11 import MessageEvent
+from ._config import SearchType
+
+
+def parse_data(cmd: str, event: MessageEvent, superusers: List[str]):
+ search_type = SearchType.TOTAL
+ if cmd[:2] == "全局":
+ if str(event.user_id) in superusers:
+ if cmd[2] == '日':
+ search_type = SearchType.DAY
+ elif cmd[2] == '周':
+ _type = SearchType.WEEK
+ elif cmd[2] == '月':
+ _type = SearchType.MONTH
+
diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py
index 4e03feda..fddfe793 100644
--- a/plugins/web_ui/__init__.py
+++ b/plugins/web_ui/__init__.py
@@ -12,24 +12,15 @@ 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_log_routes
from .api.logs.log_manager import LOG_STORAGE
from .api.tabs.database import router as database_router
-
-# 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 .api.tabs.plugin_manage import router as plugin_router
from .auth import router as auth_router
-# from .api.plugins import router as plugin_routes
-# from .api.request import router as request_routes
-
-
driver = nonebot.get_driver()
gConfig.add_plugin_config("web-ui", "username", "admin", name="web-ui", help_="前端管理用户名")
@@ -40,13 +31,10 @@ gConfig.add_plugin_config("web-ui", "password", None, name="web-ui", help_="前
BaseApiRouter = APIRouter(prefix="/zhenxun/api")
BaseApiRouter.include_router(auth_router)
-# BaseApiRouter.include_router(plugin_routes)
-# BaseApiRouter.include_router(group_routes)
-# BaseApiRouter.include_router(request_routes)
-# BaseApiRouter.include_router(system_routes)
BaseApiRouter.include_router(main_router)
BaseApiRouter.include_router(manage_router)
BaseApiRouter.include_router(database_router)
+BaseApiRouter.include_router(plugin_router)
@driver.on_startup
diff --git a/plugins/web_ui/api/__init__.py b/plugins/web_ui/api/__init__.py
index 92af2280..32d31b27 100644
--- a/plugins/web_ui/api/__init__.py
+++ b/plugins/web_ui/api/__init__.py
@@ -1,5 +1 @@
-# 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
deleted file mode 100644
index 3b9a7db3..00000000
--- a/plugins/web_ui/api/base_info.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# from datetime import datetime, timedelta
-# from typing import List, Optional
-
-# 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 ..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"
-
-# router = APIRouter()
-
-
-# @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.
-
-# 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连接")
diff --git a/plugins/web_ui/api/group.py b/plugins/web_ui/api/group.py
deleted file mode 100644
index 76f1a4d0..00000000
--- a/plugins/web_ui/api/group.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# 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 ..base_model import Group, GroupResult, Result, Task
-# from ..models.params import UpdateGroup
-# from ..utils import authentication
-
-# 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.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 0257f9cf..c7c8140e 100644
--- a/plugins/web_ui/api/logs/log_manager.py
+++ b/plugins/web_ui/api/logs/log_manager.py
@@ -21,18 +21,18 @@ class LogStorage(Generic[_T]):
self.listeners: Set[LogListener[str]] = set()
async def add(self, log: str):
- log = re.sub(PATTERN, "", log)
- log_split = log.split()
- time = log_split[0] + " " + log_split[1]
- level = log_split[2]
- main = log_split[3]
- type_ = None
- log_ = " ".join(log_split[3:])
- if "Calling API" in log_:
- sp = log_.split("|")
- type_ = sp[1]
- log_ = "|".join(log_[1:])
- data = {"time": time, "level": level, "main": main, "type": type_, "log": log_}
+ # log = re.sub(PATTERN, "", log)
+ # log_split = log.split()
+ # time = log_split[0] + " " + log_split[1]
+ # level = log_split[2]
+ # main = log_split[3]
+ # type_ = None
+ # log_ = " ".join(log_split[3:])
+ # if "Calling API" in log_:
+ # sp = log_.split("|")
+ # type_ = sp[1]
+ # log_ = "|".join(log_[1:])
+ # data = {"time": time, "level": level, "main": main, "type": type_, "log": log_}
seq = self.count = self.count + 1
self.logs[seq] = log
asyncio.get_running_loop().call_later(self.rotation, self.remove, seq)
diff --git a/plugins/web_ui/api/plugins.py b/plugins/web_ui/api/plugins.py
deleted file mode 100644
index 8262a0bd..00000000
--- a/plugins/web_ui/api/plugins.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# from typing import Optional
-
-# 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 ..config import *
-# from ..base_model import Plugin, PluginConfig, Result
-# from ..models.params import UpdateConfig, UpdatePlugin
-# from ..utils import authentication
-
-# 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.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="写入配置项了哦!")
diff --git a/plugins/web_ui/api/request.py b/plugins/web_ui/api/request.py
deleted file mode 100644
index 83a878dd..00000000
--- a/plugins/web_ui/api/request.py
+++ /dev/null
@@ -1,87 +0,0 @@
-from typing import Optional
-
-from fastapi import APIRouter
-
-from configs.config import NICKNAME
-from models.group_info import GroupInfo
-from services.log import logger
-from utils.manager import requests_manager
-from utils.utils import get_bot
-
-from ..base_model import RequestResult, Result
-from ..models.params import HandleRequest
-from ..utils import authentication
-
-router = APIRouter()
-
-
-@router.get("/get_request", dependencies=[authentication()])
-def _(request_type: Optional[str]) -> Result:
- try:
- req_data = requests_manager.get_data()
- req_list = []
- if request_type in ["group", "private"]:
- req_data = req_data[request_type]
- for x in req_data:
- req_data[x]["oid"] = x
- req_list.append(RequestResult(**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()])
-def _(request_type: Optional[str]) -> Result:
- """
- 清空请求
- :param type_: 类型
- """
- requests_manager.clear(request_type)
- 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}")
diff --git a/plugins/web_ui/api/tabs/__init__.py b/plugins/web_ui/api/tabs/__init__.py
index 97abef9c..c2758a6a 100644
--- a/plugins/web_ui/api/tabs/__init__.py
+++ b/plugins/web_ui/api/tabs/__init__.py
@@ -1,3 +1,4 @@
from .database import *
from .main import *
from .manage import *
+from .plugin_manage import *
diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py
index 7daa0ddc..125cb587 100644
--- a/plugins/web_ui/api/tabs/main/__init__.py
+++ b/plugins/web_ui/api/tabs/main/__init__.py
@@ -7,21 +7,26 @@ import nonebot
from fastapi import APIRouter, WebSocket
from nonebot.utils import escape_tag
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
+from tortoise.functions import Count
+from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
from configs.config import NICKNAME
from models.chat_history import ChatHistory
+from models.group_info import GroupInfo
+from models.statistics import Statistics
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 ....config import QueryDateType
from ....utils import authentication, get_system_status
from .data_source import bot_live
-from .model import BaseInfo
+from .model import ActiveGroup, BaseInfo, ChatHistoryCount, HotPlugin
AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160"
+GROUP_AVA_URL = "http://p.qlogo.cn/gh/{}/{}/640/"
+
run_time = time.time()
ws_router = APIRouter()
@@ -34,7 +39,7 @@ async def _(bot_id: Optional[str] = None) -> Result:
获取Bot基础信息
Args:
- qq (Optional[str], optional): qq号. Defaults to None.
+ bot_id (Optional[str], optional): bot_id. Defaults to None.
Returns:
Result: 获取指定bot信息与bot列表
@@ -43,20 +48,22 @@ async def _(bot_id: Optional[str] = None) -> Result:
if bots := nonebot.get_bots():
select_bot: BaseInfo
for key, bot in bots.items():
+ login_info = await bot.get_login_info()
bot_list.append(
BaseInfo(
bot=bot, # type: ignore
self_id=bot.self_id,
- nickname=NICKNAME,
+ nickname=login_info["nickname"],
ava_url=AVA_URL.format(bot.self_id),
)
)
- # 获取指定qq号的bot信息,若无指定则获取第一个
+ # 获取指定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
+ select_bot.config = select_bot.bot.config
now = datetime.now()
# 今日累计接收消息
select_bot.received_messages = await ChatHistory.filter(
@@ -78,11 +85,43 @@ async def _(bot_id: Optional[str] = None) -> Result:
)
# 连接时间
select_bot.connect_time = bot_live.get(select_bot.self_id) or 0
+ if select_bot.connect_time:
+ connect_date = datetime.fromtimestamp(select_bot.connect_time)
+ select_bot.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
- return Result.ok(bot_list, "已获取操作列表")
+ return Result.ok(bot_list, "拿到信息啦!")
return Result.warning_("无Bot连接...")
+@router.get(
+ "/get_all_ch_count", dependencies=[authentication()], description="获取接收消息数量"
+)
+async def _(bot_id: str) -> Result:
+ now = datetime.now()
+ all_count = await ChatHistory.filter(bot_id=bot_id).count()
+ day_count = await ChatHistory.filter(
+ bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour)
+ ).count()
+ week_count = await ChatHistory.filter(
+ bot_id=bot_id, create_time__gte=now - timedelta(days=7)
+ ).count()
+ month_count = await ChatHistory.filter(
+ bot_id=bot_id, create_time__gte=now - timedelta(days=30)
+ ).count()
+ year_count = await ChatHistory.filter(
+ bot_id=bot_id, create_time__gte=now - timedelta(days=365)
+ ).count()
+ return Result.ok(
+ ChatHistoryCount(
+ num=all_count,
+ day=day_count,
+ week=week_count,
+ month=month_count,
+ year=year_count,
+ )
+ )
+
+
@router.get("/get_ch_count", dependencies=[authentication()], description="获取接收消息数量")
async def _(bot_id: str, query_type: Optional[QueryDateType] = None) -> Result:
if bots := nonebot.get_bots():
@@ -135,6 +174,81 @@ async def _() -> Result:
return Result.ok(int(time.time() - run_time))
+@router.get("/get_active_group", dependencies=[authentication()], description="获取活跃群聊")
+async def _(date_type: Optional[QueryDateType] = None) -> Result:
+ query = ChatHistory
+ now = datetime.now()
+ if date_type == QueryDateType.DAY:
+ query = ChatHistory.filter(create_time__gte=now - timedelta(hours=now.hour))
+ if date_type == QueryDateType.WEEK:
+ query = ChatHistory.filter(create_time__gte=now - timedelta(days=7))
+ if date_type == QueryDateType.MONTH:
+ query = ChatHistory.filter(create_time__gte=now - timedelta(days=30))
+ if date_type == QueryDateType.YEAR:
+ query = ChatHistory.filter(create_time__gte=now - timedelta(days=365))
+ data_list = (
+ await query.annotate(count=Count("id"))
+ .group_by("group_id").order_by("-count").limit(5)
+ .values_list("group_id", "count")
+ )
+ active_group_list = []
+ id2name = {}
+ if data_list:
+ if info_list := await GroupInfo.filter(group_id__in=[x[0] for x in data_list]).all():
+ for group_info in info_list:
+ id2name[group_info.group_id] = group_info.group_name
+ for data in data_list:
+ active_group_list.append(
+ ActiveGroup(
+ group_id=data[0],
+ name=id2name.get(data[0]) or data[0],
+ chat_num=data[1],
+ ava_img=GROUP_AVA_URL.format(data[0], data[0]),
+ )
+ )
+ active_group_list = sorted(
+ active_group_list, key=lambda x: x.chat_num, reverse=True
+ )
+ if len(active_group_list) > 5:
+ active_group_list = active_group_list[:5]
+ return Result.ok(active_group_list)
+
+
+@router.get("/get_hot_plugin", dependencies=[authentication()], description="获取热门插件")
+async def _(date_type: Optional[QueryDateType] = None) -> Result:
+ query = Statistics
+ now = datetime.now()
+ if date_type == QueryDateType.DAY:
+ query = Statistics.filter(create_time__gte=now - timedelta(hours=now.hour))
+ if date_type == QueryDateType.WEEK:
+ query = Statistics.filter(create_time__gte=now - timedelta(days=7))
+ if date_type == QueryDateType.MONTH:
+ query = Statistics.filter(create_time__gte=now - timedelta(days=30))
+ if date_type == QueryDateType.YEAR:
+ query = Statistics.filter(create_time__gte=now - timedelta(days=365))
+ data_list = (
+ await query.annotate(count=Count("id"))
+ .group_by("plugin_name").order_by("-count").limit(5)
+ .values_list("plugin_name", "count")
+ )
+ hot_plugin_list = []
+ for data in data_list:
+ name = data[0]
+ if plugin_data := plugin_data_manager.get(data[0]):
+ name = plugin_data.name
+ hot_plugin_list.append(
+ HotPlugin(
+ module=data[0],
+ name=name,
+ count=data[1],
+ )
+ )
+ hot_plugin_list = sorted(hot_plugin_list, key=lambda x: x.count, reverse=True)
+ if len(hot_plugin_list) > 5:
+ hot_plugin_list = hot_plugin_list[:5]
+ return Result.ok(hot_plugin_list)
+
+
@ws_router.websocket("/system_status")
async def system_logs_realtime(websocket: WebSocket):
await websocket.accept()
@@ -144,6 +258,6 @@ async def system_logs_realtime(websocket: WebSocket):
system_status = await get_system_status()
await websocket.send_text(system_status.json())
await asyncio.sleep(5)
- except WebSocketDisconnect:
+ except (WebSocketDisconnect, ConnectionClosedError, ConnectionClosedOK):
pass
return
diff --git a/plugins/web_ui/api/tabs/main/data_source.py b/plugins/web_ui/api/tabs/main/data_source.py
index e164383c..42a3df42 100644
--- a/plugins/web_ui/api/tabs/main/data_source.py
+++ b/plugins/web_ui/api/tabs/main/data_source.py
@@ -3,7 +3,7 @@ from typing import Optional
import nonebot
from nonebot import Driver
-from nonebot.adapters.onebot.v12 import Bot
+from nonebot.adapters.onebot.v11 import Bot
driver: Driver = nonebot.get_driver()
diff --git a/plugins/web_ui/api/tabs/main/model.py b/plugins/web_ui/api/tabs/main/model.py
index decb2aea..bced1817 100644
--- a/plugins/web_ui/api/tabs/main/model.py
+++ b/plugins/web_ui/api/tabs/main/model.py
@@ -1,4 +1,8 @@
+from datetime import datetime
+from typing import Optional, Union
+
from nonebot.adapters.onebot.v11 import Bot
+from nonebot.config import Config
from pydantic import BaseModel
@@ -41,6 +45,8 @@ class BaseInfo(BaseModel):
# """一年内累计接受消息"""
connect_time: int = 0
"""连接时间"""
+ connect_date: Optional[datetime] = None
+ """连接日期"""
plugin_count: int = 0
"""加载插件数量"""
@@ -52,5 +58,53 @@ class BaseInfo(BaseModel):
is_select: bool = False
"""当前选择"""
+ config: Optional[Config] = None
+ """nb配置"""
+
class Config:
arbitrary_types_allowed = True
+
+
+class ChatHistoryCount(BaseModel):
+ """
+ 聊天记录数量
+ """
+
+ num: int
+ """总数"""
+ day: int
+ """一天内"""
+ week: int
+ """一周内"""
+ month: int
+ """一月内"""
+ year: int
+ """一年内"""
+
+
+class ActiveGroup(BaseModel):
+ """
+ 活跃群聊数据
+ """
+
+ group_id: Union[str, int]
+ """群组id"""
+ name: str
+ """群组名称"""
+ chat_num: int
+ """发言数量"""
+ ava_img: str
+ """群组头像"""
+
+
+class HotPlugin(BaseModel):
+ """
+ 热门插件
+ """
+
+ module: str
+ """模块名"""
+ name: str
+ """插件名称"""
+ count: int
+ """调用次数"""
diff --git a/plugins/web_ui/api/tabs/plugin_manage/__init__.py b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
new file mode 100644
index 00000000..4201a528
--- /dev/null
+++ b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
@@ -0,0 +1,149 @@
+from typing import List, Optional
+
+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, PluginSetting, PluginType
+
+from ....base_model import Result
+from ....utils import authentication
+from .model import PluginCount, PluginInfo, PluginSwitch, UpdateConfig, UpdatePlugin
+
+router = APIRouter()
+
+
+@router.get("/get_plugin_list", dependencies=[authentication()], deprecated="获取插件列表")
+def _(
+ plugin_type: PluginType, menu_type: Optional[str] = None
+) -> Result:
+ """
+ 获取插件列表
+ :param plugin_type: 类型 normal, superuser, hidden, admin
+ """
+ try:
+ plugin_list: List[PluginInfo] = []
+ 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:
+ setting = plugin_data.plugin_setting or PluginSetting()
+ plugin = plugin_data.plugin_status
+ menu_type_ = getattr(setting, "plugin_type", ["无"])[0]
+ if menu_type and menu_type != menu_type_:
+ continue
+ plugin_info = PluginInfo(
+ module=module,
+ plugin_name=plugin_data.name,
+ default_switch=getattr(setting, "default_status", False),
+ limit_superuser=getattr(setting, "limit_superuser", False),
+ cost_gold=getattr(setting, "cost_gold", 0),
+ menu_type=menu_type_,
+ version=(plugin.version or 0) if plugin else 0,
+ level=getattr(setting, "level", 5),
+ status=plugin.status if plugin else False,
+ author=plugin.author if plugin else None
+ )
+ plugin_info.version = (plugin.version or 0) if plugin else 0
+ plugin_list.append(plugin_info)
+ 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_plugin_count", dependencies=[authentication()], deprecated="获取插件数量")
+def _() -> Result:
+ plugin_count = PluginCount()
+ for module in plugin_data_manager.keys():
+ plugin_data: Optional[PluginData] = plugin_data_manager[module]
+ if plugin_data and plugin_data.plugin_type == PluginType.NORMAL:
+ plugin_count.normal += 1
+ elif plugin_data and plugin_data.plugin_type == PluginType.ADMIN:
+ plugin_count.admin += 1
+ elif plugin_data and plugin_data.plugin_type == PluginType.SUPERUSER:
+ plugin_count.superuser += 1
+ else:
+ plugin_count.other += 1
+ return Result.ok(plugin_count)
+
+@router.post("/update_plugins", dependencies=[authentication()], description="更新插件参数")
+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.split(",") if plugin.cmd else []
+ p2s.level = plugin.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()], description="更新配置")
+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("/change_switch", dependencies=[authentication()], description="开关插件")
+def _(param: PluginSwitch) -> Result:
+ if pm := plugins_manager.get(param.module):
+ pm.block_type = None if param.status else 'all'
+ pm.status = param.status
+ plugins_manager.save()
+ return Result.ok(info="成功改变了开关状态!")
+ return Result.warning_("未获取该插件的配置!")
+
+
+@router.get("/get_plugin_menu_type", dependencies=[authentication()], description="获取插件类型")
+def _() -> Result:
+ menu_type_list = []
+ for module in plugin_data_manager.keys():
+ plugin_data: Optional[PluginData] = plugin_data_manager[module]
+ if plugin_data:
+ setting = plugin_data.plugin_setting or PluginSetting()
+ menu_type = getattr(setting, "plugin_type", ["无"])[0]
+ if menu_type not in menu_type_list:
+ menu_type_list.append(menu_type)
+ return Result.ok(menu_type_list)
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/plugin_manage/model.py b/plugins/web_ui/api/tabs/plugin_manage/model.py
new file mode 100644
index 00000000..869bd594
--- /dev/null
+++ b/plugins/web_ui/api/tabs/plugin_manage/model.py
@@ -0,0 +1,135 @@
+from typing import Any, Dict, List, Optional, Union
+
+from pydantic import BaseModel
+
+from utils.manager.models import Plugin as PluginManager
+from utils.manager.models import (
+ PluginBlock,
+ PluginCd,
+ PluginCount,
+ PluginSetting,
+ PluginType,
+)
+from utils.typing import BLOCK_TYPE
+
+
+class PluginSwitch(BaseModel):
+ """
+ 插件开关
+ """
+
+ module: str
+ """模块"""
+ status: bool
+ """开关状态"""
+
+
+class UpdateConfig(BaseModel):
+ """
+ 配置项修改参数
+ """
+
+ module: str
+ """模块"""
+ key: str
+ """配置项key"""
+ value: Any
+ """配置项值"""
+
+
+class UpdatePlugin(BaseModel):
+ """
+ 插件修改参数
+ """
+
+ module: str
+ """模块"""
+ default_status: bool
+ """默认开关"""
+ limit_superuser: bool
+ """限制超级用户"""
+ cost_gold: int
+ """金币花费"""
+ cmd: str
+ """插件别名"""
+ menu_type: str
+ """插件菜单类型"""
+ level: int
+ """插件所需群权限"""
+ block_type: BLOCK_TYPE
+ """禁用类型"""
+
+
+class PluginInfo(BaseModel):
+ """
+ 基本插件信息
+ """
+
+ module: str
+ """插件名称"""
+ plugin_name: str
+ """插件中文名称"""
+ default_switch: bool
+ """默认开关"""
+ limit_superuser: bool
+ """限制超级用户"""
+ cost_gold: int
+ """花费金币"""
+ menu_type: str
+ """插件菜单类型"""
+ version: Union[int, str, float]
+ """插件版本"""
+ level: int
+ """群权限"""
+ status: bool
+ """当前状态"""
+ author: Optional[str] = None
+ """作者"""
+
+
+class PluginConfig(BaseModel):
+ """
+ 插件配置项
+ """
+
+ module: str
+ key: str
+ value: Any
+ help: Optional[str]
+ default_value: Any
+ has_type: bool
+
+
+class Plugin(BaseModel):
+ """
+ 插件
+ """
+
+ module: 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 PluginCount(BaseModel):
+ """
+ 插件数量
+ """
+
+ normal: int = 0
+ """普通插件"""
+ admin: int = 0
+ """管理员插件"""
+ superuser: int = 0
+ """超级用户插件"""
+ other: int = 0
+ """其他插件"""
\ No newline at end of file
diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py
index d9f76531..2ce1a82f 100644
--- a/plugins/web_ui/base_model.py
+++ b/plugins/web_ui/base_model.py
@@ -76,38 +76,38 @@ class QueryModel(BaseModel, Generic[T]):
return size
-# class PluginConfig(BaseModel):
-# """
-# 插件配置项
-# """
+class PluginConfig(BaseModel):
+ """
+ 插件配置项
+ """
-# module: str
-# key: str
-# value: Optional[Any]
-# help: Optional[str]
-# default_value: Optional[Any]
-# has_type: bool
+ module: str
+ key: str
+ value: Optional[Any]
+ help: Optional[str]
+ default_value: Optional[Any]
+ has_type: bool
-# class Plugin(BaseModel):
-# """
-# 插件
-# """
+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]
-# """次数限制"""
+ 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):
From 87a39bf033981378c4f9be723781a66040b9d417 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sat, 6 Jan 2024 22:09:44 +0800
Subject: [PATCH 05/20] =?UTF-8?q?refactor=F0=9F=8E=A8:=20=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0webui=20model?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
utils/models/__init__.py | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/utils/models/__init__.py b/utils/models/__init__.py
index 754d7df7..0b217fcf 100644
--- a/utils/models/__init__.py
+++ b/utils/models/__init__.py
@@ -1,16 +1,37 @@
+from typing import Any
+
from nonebot.adapters.onebot.v11 import Message, MessageEvent
from pydantic import BaseModel
-from typing import Any
class ShopParam(BaseModel):
+
+
goods_name: str
+ """商品名称"""
user_id: int
+ """用户id"""
group_id: int
+ """群聊id"""
bot: Any
+ """bot"""
event: MessageEvent
- num: int # 道具单次使用数量
+ """event"""
+ num: int
+ """道具单次使用数量"""
message: Message
+ """message"""
text: str
- send_success_msg: bool = True # 是否发送使用成功信息
- max_num_limit: int = 1 # 单次使用最大次数
+ """text"""
+ send_success_msg: bool = True
+ """是否发送使用成功信息"""
+ max_num_limit: int = 1
+ """单次使用最大次数"""
+
+
+class CommonSql(BaseModel):
+
+ sql: str
+ """sql语句"""
+ remark: str
+ """备注"""
\ No newline at end of file
From 8acbe0bb1cef5a34d48e437e16afb6e25a3732af Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 8 Jan 2024 05:39:44 +0800
Subject: [PATCH 06/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E5=AE=8C=E5=96=84?=
=?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/__init__.py | 22 +++-
.../web_ui/api/tabs/plugin_manage/__init__.py | 113 +++++++++++++-----
.../web_ui/api/tabs/plugin_manage/model.py | 41 ++++---
utils/manager/plugin_data_manager.py | 3 +
utils/typing.py | 1 +
5 files changed, 129 insertions(+), 51 deletions(-)
diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py
index fddfe793..cb05b979 100644
--- a/plugins/web_ui/__init__.py
+++ b/plugins/web_ui/__init__.py
@@ -30,6 +30,7 @@ gConfig.add_plugin_config("web-ui", "password", None, name="web-ui", help_="前
BaseApiRouter = APIRouter(prefix="/zhenxun/api")
+
BaseApiRouter.include_router(auth_router)
BaseApiRouter.include_router(main_router)
BaseApiRouter.include_router(manage_router)
@@ -37,12 +38,24 @@ BaseApiRouter.include_router(database_router)
BaseApiRouter.include_router(plugin_router)
+WsApiRouter = APIRouter(prefix="/zhenxun/socket")
+
+WsApiRouter.include_router(ws_log_routes)
+WsApiRouter.include_router(status_routes)
+
+
@driver.on_startup
def _():
try:
- loop = asyncio.get_running_loop()
-
- def log_sink(message: str):
+ async def log_sink(message: str):
+ loop = None
+ if not loop:
+ try:
+ loop = asyncio.get_running_loop()
+ except Exception as e:
+ logger.warning('Web Ui log_sink', e=e)
+ if not loop:
+ loop = asyncio.new_event_loop()
loop.create_task(LOG_STORAGE.add(message.rstrip("\n")))
logger_.add(
@@ -51,8 +64,7 @@ def _():
app: FastAPI = nonebot.get_app()
app.include_router(BaseApiRouter)
- app.include_router(ws_log_routes)
- app.include_router(status_routes)
+ app.include_router(WsApiRouter)
logger.info("API启动成功", "Web UI")
except Exception as e:
logger.error("API启动失败", "Web UI", e=e)
diff --git a/plugins/web_ui/api/tabs/plugin_manage/__init__.py b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
index 4201a528..d53cb03d 100644
--- a/plugins/web_ui/api/tabs/plugin_manage/__init__.py
+++ b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
@@ -1,3 +1,4 @@
+import re
from typing import List, Optional
import cattrs
@@ -10,7 +11,15 @@ from utils.manager.models import PluginData, PluginSetting, PluginType
from ....base_model import Result
from ....utils import authentication
-from .model import PluginCount, PluginInfo, PluginSwitch, UpdateConfig, UpdatePlugin
+from .model import (
+ PluginConfig,
+ PluginCount,
+ PluginDetail,
+ PluginInfo,
+ PluginSwitch,
+ UpdateConfig,
+ UpdatePlugin,
+)
router = APIRouter()
@@ -36,7 +45,7 @@ def _(
plugin_info = PluginInfo(
module=module,
plugin_name=plugin_data.name,
- default_switch=getattr(setting, "default_status", False),
+ default_status=getattr(setting, "default_status", False),
limit_superuser=getattr(setting, "limit_superuser", False),
cost_gold=getattr(setting, "cost_gold", 0),
menu_type=menu_type_,
@@ -67,7 +76,7 @@ def _() -> Result:
plugin_count.other += 1
return Result.ok(plugin_count)
-@router.post("/update_plugins", dependencies=[authentication()], description="更新插件参数")
+@router.post("/update_plugin", dependencies=[authentication()], description="更新插件参数")
def _(plugin: UpdatePlugin) -> Result:
"""
修改插件信息
@@ -79,16 +88,15 @@ def _(plugin: UpdatePlugin) -> Result:
p2s.default_status = plugin.default_status
p2s.limit_superuser = plugin.limit_superuser
p2s.cost_gold = plugin.cost_gold
- p2s.cmd = plugin.cmd.split(",") if plugin.cmd else []
+ # p2s.cmd = plugin.cmd.split(",") if plugin.cmd else []
p2s.level = plugin.level
- if pd := plugin_data_manager.get(module):
menu_lin = None
- if len(pd.menu_type) > 1:
- menu_lin = pd.menu_type[1]
+ if len(p2s.plugin_type) > 1:
+ menu_lin = p2s.menu_type[1]
if menu_lin is not None:
- pd.menu_type = (plugin.menu_type, menu_lin)
+ p2s.plugin_type = (plugin.menu_type, menu_lin)
else:
- pd.menu_type = (plugin.menu_type,)
+ p2s.plugin_type = (plugin.menu_type,)
if pm := plugins_manager.get(module):
if plugin.block_type:
pm.block_type = plugin.block_type
@@ -98,34 +106,25 @@ def _(plugin: UpdatePlugin) -> Result:
pm.status = True
plugins2settings_manager.save()
plugins_manager.save()
+ # 配置项
+ if plugin.configs and (configs := Config.get(module)):
+ for key in plugin.configs:
+ if c := configs.configs.get(key):
+ value = plugin.configs[key]
+ if isinstance(c.value, (list, tuple)) or isinstance(
+ c.default_value, (list, tuple)
+ ):
+ value = value.split(",")
+ if c.type and value is not None:
+ value = cattrs.structure(value, c.type)
+ Config.set_config(module, key, value)
+ plugin_data_manager.reload()
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()], description="更新配置")
-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("/change_switch", dependencies=[authentication()], description="开关插件")
def _(param: PluginSwitch) -> Result:
if pm := plugins_manager.get(param.module):
@@ -146,4 +145,54 @@ def _() -> Result:
menu_type = getattr(setting, "plugin_type", ["无"])[0]
if menu_type not in menu_type_list:
menu_type_list.append(menu_type)
- return Result.ok(menu_type_list)
\ No newline at end of file
+ return Result.ok(menu_type_list)
+
+
+@router.get("/get_plugin", dependencies=[authentication()], description="获取插件详情")
+def _(module: str) -> Result:
+ if plugin_data := plugin_data_manager.get(module):
+ setting = plugin_data.plugin_setting or PluginSetting()
+ plugin = plugin_data.plugin_status
+ config_list = []
+ if config := Config.get(module):
+ for cfg in config.configs:
+ type_str = ""
+ type_inner = None
+ x = str(config.configs[cfg].type)
+ r = re.search(r"",str(config.configs[cfg].type))
+ if r:
+ type_str = r.group(1)
+ else:
+ r = re.search(r"typing\.(.*)\[(.*)\]",str(config.configs[cfg].type))
+ if r:
+ type_str = r.group(1)
+ if type_str:
+ type_str = type_str.lower()
+ type_inner = r.group(2)
+ if type_inner:
+ type_inner = [x.strip() for x in type_inner.split(",")]
+ config_list.append(PluginConfig(
+ module=module,
+ key=cfg,
+ value=config.configs[cfg].value,
+ help=config.configs[cfg].help,
+ default_value=config.configs[cfg].default_value,
+ type=type_str,
+ type_inner=type_inner
+ ))
+ plugin_info = PluginDetail(
+ module=module,
+ plugin_name=plugin_data.name,
+ default_status=getattr(setting, "default_status", False),
+ limit_superuser=getattr(setting, "limit_superuser", False),
+ cost_gold=getattr(setting, "cost_gold", 0),
+ menu_type=getattr(setting, "plugin_type", ["无"])[0],
+ version=(plugin.version or 0) if plugin else 0,
+ level=getattr(setting, "level", 5),
+ status=plugin.status if plugin else False,
+ author=plugin.author if plugin else None,
+ config_list=config_list,
+ block_type=getattr(plugin, "block_type", None)
+ )
+ return Result.ok(plugin_info)
+ return Result.warning_("未获取到插件详情...")
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/plugin_manage/model.py b/plugins/web_ui/api/tabs/plugin_manage/model.py
index 869bd594..31c1ae9a 100644
--- a/plugins/web_ui/api/tabs/plugin_manage/model.py
+++ b/plugins/web_ui/api/tabs/plugin_manage/model.py
@@ -3,13 +3,7 @@ from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel
from utils.manager.models import Plugin as PluginManager
-from utils.manager.models import (
- PluginBlock,
- PluginCd,
- PluginCount,
- PluginSetting,
- PluginType,
-)
+from utils.manager.models import PluginBlock, PluginCd, PluginCount, PluginSetting
from utils.typing import BLOCK_TYPE
@@ -50,14 +44,14 @@ class UpdatePlugin(BaseModel):
"""限制超级用户"""
cost_gold: int
"""金币花费"""
- cmd: str
- """插件别名"""
menu_type: str
"""插件菜单类型"""
level: int
"""插件所需群权限"""
- block_type: BLOCK_TYPE
+ block_type: Optional[BLOCK_TYPE] = None
"""禁用类型"""
+ configs: Optional[Dict[str, Any]] = None
+ """配置项"""
class PluginInfo(BaseModel):
@@ -69,7 +63,7 @@ class PluginInfo(BaseModel):
"""插件名称"""
plugin_name: str
"""插件中文名称"""
- default_switch: bool
+ default_status: bool
"""默认开关"""
limit_superuser: bool
"""限制超级用户"""
@@ -85,6 +79,8 @@ class PluginInfo(BaseModel):
"""当前状态"""
author: Optional[str] = None
"""作者"""
+ block_type: BLOCK_TYPE = None
+ """禁用类型"""
class PluginConfig(BaseModel):
@@ -93,11 +89,20 @@ class PluginConfig(BaseModel):
"""
module: str
+ """模块"""
key: str
+ """键"""
value: Any
- help: Optional[str]
+ """值"""
+ help: Optional[str] = None
+ """帮助"""
default_value: Any
- has_type: bool
+ """默认值"""
+ type: Optional[Any] = None
+ """值类型"""
+ type_inner: Optional[List[str]] = None
+ """List Tuple等内部类型检验"""
+
class Plugin(BaseModel):
@@ -132,4 +137,12 @@ class PluginCount(BaseModel):
superuser: int = 0
"""超级用户插件"""
other: int = 0
- """其他插件"""
\ No newline at end of file
+ """其他插件"""
+
+
+class PluginDetail(PluginInfo):
+ """
+ 插件详情
+ """
+
+ config_list: List[PluginConfig]
\ No newline at end of file
diff --git a/utils/manager/plugin_data_manager.py b/utils/manager/plugin_data_manager.py
index bd9c4aae..f94307dc 100644
--- a/utils/manager/plugin_data_manager.py
+++ b/utils/manager/plugin_data_manager.py
@@ -29,3 +29,6 @@ class PluginDataManager(StaticData[PluginData]):
def __getitem__(self, item) -> Optional[PluginData]:
return self._data.get(item)
+
+ def reload(self):
+ pass
diff --git a/utils/typing.py b/utils/typing.py
index 5d8fa815..cac8ca09 100644
--- a/utils/typing.py
+++ b/utils/typing.py
@@ -1,3 +1,4 @@
from typing import Literal
BLOCK_TYPE = Literal["all", "private", "group"]
+"""禁用类型"""
From 3bfb3620f1dcfe18ddebc816f23a272e9f55f345 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 8 Jan 2024 05:41:13 +0800
Subject: [PATCH 07/20] modified:
basic_plugins/init_plugin_config/init_plugins_settings.py
---
.../init_plugins_settings.py | 24 ++++++++++++++-----
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/basic_plugins/init_plugin_config/init_plugins_settings.py b/basic_plugins/init_plugin_config/init_plugins_settings.py
index 52864ef2..cdf7b372 100755
--- a/basic_plugins/init_plugin_config/init_plugins_settings.py
+++ b/basic_plugins/init_plugin_config/init_plugins_settings.py
@@ -1,8 +1,9 @@
-from utils.manager import plugins2settings_manager, admin_manager, plugin_data_manager
+import nonebot
+
from services.log import logger
+from utils.manager import admin_manager, plugin_data_manager, plugins2settings_manager
from utils.manager.models import PluginType
from utils.utils import get_matchers
-import nonebot
def init_plugins_settings():
@@ -18,7 +19,10 @@ def init_plugins_settings():
# logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
for matcher in get_matchers(True):
try:
- if matcher.plugin_name not in plugins2settings_manager.keys():
+ if (
+ matcher.plugin_name
+ and matcher.plugin_name not in plugins2settings_manager.keys()
+ ):
if _plugin := matcher.plugin:
try:
_module = _plugin.module
@@ -27,18 +31,26 @@ def init_plugins_settings():
else:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
if plugin_settings := plugin_data.plugin_setting:
- if (name := _module.__getattribute__("__zx_plugin_name__")) not in plugin_settings.cmd:
+ if (
+ name := _module.__getattribute__(
+ "__zx_plugin_name__"
+ )
+ ) not in plugin_settings.cmd:
plugin_settings.cmd.append(name)
# 管理员命令
if plugin_data.plugin_type == PluginType.ADMIN:
admin_manager.add_admin_plugin_settings(
- matcher.plugin_name, plugin_settings.cmd, plugin_settings.level
+ matcher.plugin_name,
+ plugin_settings.cmd,
+ plugin_settings.level,
)
else:
plugins2settings_manager.add_plugin_settings(
matcher.plugin_name, plugin_settings
)
except Exception as e:
- logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}')
+ logger.error(
+ f"{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}"
+ )
plugins2settings_manager.save()
logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.")
From 9a1510fe7ef382df3c3a010457a37dfd73b5532a Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 8 Jan 2024 15:21:07 +0800
Subject: [PATCH 08/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E5=AE=8C=E5=96=84?=
=?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/plugin_manage/__init__.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/plugins/web_ui/api/tabs/plugin_manage/__init__.py b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
index d53cb03d..6179d9c5 100644
--- a/plugins/web_ui/api/tabs/plugin_manage/__init__.py
+++ b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
@@ -111,10 +111,10 @@ def _(plugin: UpdatePlugin) -> Result:
for key in plugin.configs:
if c := configs.configs.get(key):
value = plugin.configs[key]
- if isinstance(c.value, (list, tuple)) or isinstance(
- c.default_value, (list, tuple)
- ):
- value = value.split(",")
+ # if isinstance(c.value, (list, tuple)) or isinstance(
+ # c.default_value, (list, tuple)
+ # ):
+ # value = value.split(",")
if c.type and value is not None:
value = cattrs.structure(value, c.type)
Config.set_config(module, key, value)
From bf55a20241fb23d6076af25b230bfcf93715e508 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Tue, 9 Jan 2024 13:47:24 +0800
Subject: [PATCH 09/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E4=BC=98=E5=8C=96webui=E7=BE=A4=E7=BB=84/=E5=A5=BD=E5=8F=8B?=
=?UTF-8?q?=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/__init__.py | 2 +
plugins/web_ui/api/logs/log_manager.py | 13 +-
plugins/web_ui/api/tabs/main/__init__.py | 8 +-
plugins/web_ui/api/tabs/manage/__init__.py | 280 +++++++++++++++++----
plugins/web_ui/api/tabs/manage/model.py | 139 ++++++++--
plugins/web_ui/config.py | 5 +
utils/manager/requests_manager.py | 91 ++++++-
7 files changed, 443 insertions(+), 95 deletions(-)
diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py
index cb05b979..3de51bfe 100644
--- a/plugins/web_ui/__init__.py
+++ b/plugins/web_ui/__init__.py
@@ -18,6 +18,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.plugin_manage import router as plugin_router
from .auth import router as auth_router
@@ -42,6 +43,7 @@ WsApiRouter = APIRouter(prefix="/zhenxun/socket")
WsApiRouter.include_router(ws_log_routes)
WsApiRouter.include_router(status_routes)
+WsApiRouter.include_router(chat_routes)
@driver.on_startup
diff --git a/plugins/web_ui/api/logs/log_manager.py b/plugins/web_ui/api/logs/log_manager.py
index c7c8140e..f375313d 100644
--- a/plugins/web_ui/api/logs/log_manager.py
+++ b/plugins/web_ui/api/logs/log_manager.py
@@ -21,18 +21,6 @@ class LogStorage(Generic[_T]):
self.listeners: Set[LogListener[str]] = set()
async def add(self, log: str):
- # log = re.sub(PATTERN, "", log)
- # log_split = log.split()
- # time = log_split[0] + " " + log_split[1]
- # level = log_split[2]
- # main = log_split[3]
- # type_ = None
- # log_ = " ".join(log_split[3:])
- # if "Calling API" in log_:
- # sp = log_.split("|")
- # type_ = sp[1]
- # log_ = "|".join(log_[1:])
- # data = {"time": time, "level": level, "main": main, "type": type_, "log": log_}
seq = self.count = self.count + 1
self.logs[seq] = log
asyncio.get_running_loop().call_later(self.rotation, self.remove, seq)
@@ -48,3 +36,4 @@ class LogStorage(Generic[_T]):
LOG_STORAGE: LogStorage[str] = LogStorage[str]()
+
diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py
index 125cb587..6fec031c 100644
--- a/plugins/web_ui/api/tabs/main/__init__.py
+++ b/plugins/web_ui/api/tabs/main/__init__.py
@@ -5,7 +5,6 @@ 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 tortoise.functions import Count
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
@@ -18,21 +17,18 @@ from services.log import logger
from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager
from ....base_model import Result
-from ....config import QueryDateType
+from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
from ....utils import authentication, get_system_status
from .data_source import bot_live
from .model import ActiveGroup, BaseInfo, ChatHistoryCount, HotPlugin
-AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160"
-
-GROUP_AVA_URL = "http://p.qlogo.cn/gh/{}/{}/640/"
-
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:
"""
diff --git a/plugins/web_ui/api/tabs/manage/__init__.py b/plugins/web_ui/api/tabs/manage/__init__.py
index a764f1bd..96e70ccb 100644
--- a/plugins/web_ui/api/tabs/manage/__init__.py
+++ b/plugins/web_ui/api/tabs/manage/__init__.py
@@ -1,32 +1,54 @@
-from typing import Literal
+import re
+from typing import Literal, Optional
import nonebot
from fastapi import APIRouter
-from pydantic.error_wrappers import ValidationError
+from nonebot.adapters.onebot.v11.exception import ActionFailed
+from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
+from tortoise.functions import Count
+from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
from configs.config import NICKNAME
+from models.ban_user import BanUser
+from models.chat_history import ChatHistory
+from models.friend_user import FriendUser
from models.group_info import GroupInfo
+from models.group_member_info import GroupInfoUser
+from models.statistics import Statistics
from services.log import logger
-from utils.manager import group_manager, requests_manager
+from utils.manager import group_manager, plugin_data_manager, requests_manager
from utils.utils import get_bot
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 (
DeleteFriend,
Friend,
FriendRequestResult,
- Group,
+ GroupDetail,
GroupRequestResult,
GroupResult,
HandleRequest,
LeaveGroup,
+ Message,
+ Plugin,
+ ReqResult,
+ SendMessage,
Task,
UpdateGroup,
+ UserDetail,
)
+ws_router = APIRouter()
router = APIRouter()
+SUB_PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))"
+
+GROUP_PATTERN = r'.*?Message (-?\d*) from (\d*)@\[群:(\d*)] "(.*)"'
+
+PRIVATE_PATTERN = r'.*?Message (-?\d*) from (\d*) "(.*)"'
@router.get("/get_group_list", dependencies=[authentication()], description="获取群组列表")
async def _(bot_id: str) -> Result:
@@ -41,27 +63,9 @@ async def _(bot_id: str) -> Result:
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))
+ gid = g['group_id']
+ g['ava_url'] = GROUP_AVA_URL.format(gid, gid)
+ group_list_result.append(GroupResult(**g))
except Exception as e:
logger.error("调用API错误", "/get_group_list", e=e)
return Result.fail(f"{type(e)}: {e}")
@@ -78,12 +82,14 @@ async def _(group: UpdateGroup) -> Result:
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]:
+ all_task = group_manager.get_task_data().keys()
+ if group.task:
+ for task in all_task:
+ if task in group.task:
group_manager.open_group_task(group_id, task)
else:
group_manager.close_group_task(group_id, task)
+ group_manager[group_id].close_plugins = group.close_plugins
group_manager.save()
except Exception as e:
logger.error("调用API错误", "/get_group", e=e)
@@ -101,7 +107,9 @@ async def _(bot_id: str) -> Result:
return Result.warning_("指定Bot未连接...")
try:
friend_list = await bots[bot_id].get_friend_list()
- return Result.ok([Friend(**f) for f in friend_list], "拿到了新鲜出炉的数据!")
+ for f in friend_list:
+ f['ava_url'] = AVA_URL.format(f['user_id'])
+ return Result.ok([Friend(**f) for f in friend_list if str(f['user_id']) != bot_id], "拿到了新鲜出炉的数据!")
except Exception as e:
logger.error("调用API错误", "/get_group_list", e=e)
return Result.fail(f"{type(e)}: {e}")
@@ -118,21 +126,27 @@ def _() -> Result:
@router.get("/get_request_list", dependencies=[authentication()], description="获取请求列表")
-def _(request_type: Literal["private", "group"]) -> Result:
+def _() -> 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()
+ req_result = ReqResult()
+ data = requests_manager.get_data()
+ for type_ in requests_manager.get_data():
+ for x in data[type_]:
+ data[type_][x]["oid"] = x
+ data[type_][x]['type'] = type_
+ if type_ == "private":
+ data[type_][x]['ava_url'] = AVA_URL.format(data[type_][x]['id'])
+ req_result.friend.append(FriendRequestResult(**data[type_][x]))
+ else:
+ gid = data[type_][x]['id']
+ data[type_][x]['ava_url'] = GROUP_AVA_URL.format(gid, gid)
+ req_result.group.append(GroupRequestResult(**data[type_][x]))
+ req_result.friend.reverse()
+ req_result.group.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}带来了最新的数据!")
+ return Result.ok(req_result, f"{NICKNAME}带来了最新的数据!")
@router.delete("/clear_request", dependencies=[authentication()], description="清空请求列表")
@@ -156,14 +170,18 @@ async def _(parma: HandleRequest) -> Result:
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
+ try:
+ flag = await requests_manager.refused(bots[bot_id], parma.flag, parma.request_type) # type: ignore
+ except ActionFailed as e:
+ requests_manager.delete_request(parma.flag, parma.request_type)
+ return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
if flag == 1:
- requests_manager.delete_request(parma.id, parma.request_type)
+ requests_manager.delete_request(parma.flag, parma.request_type)
return Result.warning_("该请求已失效...")
elif flag == 2:
return Result.warning_("未找到此Id请求...")
return Result.ok(info="成功处理了请求!")
- return Result.warning_("Bot未连接...")
+ return Result.warning_("无Bot连接...")
except Exception as e:
logger.error("调用API错误", "/refuse_request", e=e)
return Result.fail(f"{type(e)}: {e}")
@@ -175,7 +193,7 @@ async def _(parma: HandleRequest) -> Result:
操作请求
:param parma: 参数
"""
- requests_manager.delete_request(parma.id, parma.request_type)
+ requests_manager.delete_request(parma.flag, parma.request_type)
return Result.ok(info="成功处理了请求!")
@@ -191,7 +209,7 @@ async def _(parma: HandleRequest) -> Result:
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 rid := requests_manager.get_group_id(parma.flag):
if group := await GroupInfo.get_or_none(group_id=str(rid)):
await group.update_or_create(group_flag=1)
else:
@@ -205,9 +223,13 @@ async def _(parma: HandleRequest) -> Result:
"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未连接...")
+ try:
+ await requests_manager.approve(bots[bot_id], parma.flag, parma.request_type) # type: ignore
+ return Result.ok(info="成功处理了请求!")
+ except ActionFailed as e:
+ requests_manager.delete_request(parma.flag, parma.request_type)
+ return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
+ return Result.warning_("无Bot连接...")
except Exception as e:
logger.error("调用API错误", "/approve_request", e=e)
return Result.fail(f"{type(e)}: {e}")
@@ -223,7 +245,7 @@ async def _(param: LeaveGroup) -> Result:
return Result.warning_("Bot未在该群聊中...")
await bots[bot_id].set_group_leave(group_id=param.group_id)
return Result.ok(info="成功处理了请求!")
- return Result.warning_("Bot未连接...")
+ return Result.warning_("无Bot连接...")
except Exception as e:
logger.error("调用API错误", "/leave_group", e=e)
return Result.fail(f"{type(e)}: {e}")
@@ -243,3 +265,163 @@ async def _(param: DeleteFriend) -> Result:
except Exception as e:
logger.error("调用API错误", "/delete_friend", e=e)
return Result.fail(f"{type(e)}: {e}")
+
+
+
+@router.get("/get_friend_detail", dependencies=[authentication()], description="获取好友详情")
+async def _(bot_id: str, user_id: str) -> Result:
+ if bots := nonebot.get_bots():
+ if bot_id in bots:
+ if fd := [x for x in await bots[bot_id].get_friend_list() if str(x['user_id']) == user_id]:
+ like_plugin_list = (
+ await Statistics.filter(user_id=user_id).annotate(count=Count("id"))
+ .group_by("plugin_name").order_by("-count").limit(5)
+ .values_list("plugin_name", "count")
+ )
+ like_plugin = {}
+ for data in like_plugin_list:
+ name = data[0]
+ if plugin_data := plugin_data_manager.get(data[0]):
+ name = plugin_data.name
+ like_plugin[name] = data[1]
+ user = fd[0]
+ user_detail = UserDetail(
+ user_id=user_id,
+ ava_url=AVA_URL.format(user_id),
+ nickname=user['nickname'],
+ remark=user['remark'],
+ is_ban=await BanUser.is_ban(user_id),
+ chat_count=await ChatHistory.filter(user_id=user_id).count(),
+ call_count=await Statistics.filter(user_id=user_id).count(),
+ like_plugin=like_plugin,
+ )
+ return Result.ok(user_detail)
+ else:
+ return Result.warning_("未添加指定好友...")
+ return Result.warning_("无Bot连接...")
+
+
+@router.get("/get_group_detail", dependencies=[authentication()], description="获取群组详情")
+async def _(bot_id: str, group_id: str) -> Result:
+ if bots := nonebot.get_bots():
+ if bot_id in bots:
+ group_info = await bots[bot_id].get_group_info(group_id=int(group_id))
+ g = group_manager[group_id]
+ if not g:
+ return Result.warning_("指定群组未被收录...")
+ if group_info:
+ like_plugin_list = (
+ await Statistics.filter(group_id=group_id).annotate(count=Count("id"))
+ .group_by("plugin_name").order_by("-count").limit(5)
+ .values_list("plugin_name", "count")
+ )
+ like_plugin = {}
+ for data in like_plugin_list:
+ name = data[0]
+ if plugin_data := plugin_data_manager.get(data[0]):
+ name = plugin_data.name
+ like_plugin[name] = data[1]
+ close_plugins = []
+ for module in g.close_plugins:
+ plugin = Plugin(module=module, plugin_name=module)
+ if plugin_data := plugin_data_manager.get(module):
+ plugin.plugin_name = plugin_data.name
+ close_plugins.append(plugin)
+ task_list = []
+ task_data = group_manager.get_task_data()
+ for tn, status in g.group_task_status.items():
+ task_list.append(
+ Task(
+ name=tn,
+ zh_name=task_data.get(tn) or tn,
+ status=status
+ )
+ )
+ group_detail = GroupDetail(
+ group_id=group_id,
+ ava_url=GROUP_AVA_URL.format(group_id, group_id),
+ name=group_info['group_name'],
+ member_count=group_info['member_count'],
+ max_member_count=group_info['max_member_count'],
+ chat_count=await ChatHistory.filter(group_id=group_id).count(),
+ call_count=await Statistics.filter(group_id=group_id).count(),
+ like_plugin=like_plugin,
+ level=g.level,
+ status=g.status,
+ close_plugins=close_plugins,
+ task=task_list
+ )
+ return Result.ok(group_detail)
+ else:
+ return Result.warning_("未添加指定群组...")
+ return Result.warning_("无Bot连接...")
+
+
+@router.post("/send_message", dependencies=[authentication()], description="获取群组详情")
+async def _(param: SendMessage) -> Result:
+ if bots := nonebot.get_bots():
+ if param.bot_id in bots:
+ try:
+ if param.user_id:
+ await bots[param.bot_id].send_private_msg(user_id=str(param.user_id), message=param.message)
+ else:
+ await bots[param.bot_id].send_group_msg(group_id=str(param.group_id), message=param.message)
+ except Exception as e:
+ return Result.fail(str(e))
+ return Result.ok("发送成功!")
+ return Result.warning_("指定Bot未连接...")
+ return Result.warning_("无Bot连接...")
+
+MSG_LIST = []
+
+@ws_router.websocket("/chat")
+async def _(websocket: WebSocket, group_id: Optional[str] = None, user_id: Optional[str] = None):
+ global MSG_LIST
+ await websocket.accept()
+
+ async def log_listener(log: str):
+ sub_log = re.sub(SUB_PATTERN, "", log)
+ if "message.private.friend" in log:
+ if r := re.search(PRIVATE_PATTERN, sub_log):
+ msg_id = r.group(1)
+ uid = r.group(2)
+ msg = r.group(3)
+ user = await FriendUser.filter(user_id=user_id).first()
+ name = user.user_name
+ if uid and uid == user_id and msg_id not in MSG_LIST:
+ MSG_LIST.append(msg_id)
+ message = Message(
+ user_id=uid,
+ message=msg,
+ name=name,
+ ava_url=AVA_URL.format(uid)
+ )
+ await websocket.send_json(message.dict())
+ else:
+ if r := re.search(GROUP_PATTERN, sub_log):
+ msg_id = r.group(1)
+ uid = r.group(2)
+ gid = r.group(3)
+ msg = r.group(4)
+ user = await GroupInfoUser.filter(user_id=uid, group_id=gid).first()
+ name = user.user_name or user.nickname
+ if gid and gid == group_id and msg_id not in MSG_LIST:
+ MSG_LIST.append(msg_id)
+ message = Message(
+ user_id=uid,
+ group_id=gid,
+ message=msg,
+ name=name,
+ ava_url=AVA_URL.format(uid)
+ )
+ await websocket.send_json(message.dict())
+
+ 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
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/manage/model.py b/plugins/web_ui/api/tabs/manage/model.py
index 089da80d..bc8df37f 100644
--- a/plugins/web_ui/api/tabs/manage/model.py
+++ b/plugins/web_ui/api/tabs/manage/model.py
@@ -27,27 +27,33 @@ class Task(BaseModel):
name: str
"""被动名称"""
- nameZh: str
+ zh_name: str
"""被动中文名称"""
status: bool
"""状态"""
+class Plugin(BaseModel):
+ """
+ 插件
+ """
+
+ module: str
+ """模块名"""
+ plugin_name: str
+ """中文名"""
+
class GroupResult(BaseModel):
"""
群组返回数据
"""
- group: Group
- """Group"""
- level: int
- """群等级"""
- status: bool
- """状态"""
- close_plugins: List[str]
- """关闭的插件"""
- task: List[Task]
- """被动列表"""
+ group_id: Union[str, int]
+ """群组id"""
+ group_name: str
+ """群组名称"""
+ ava_url: str
+ """群组头像"""
class Friend(BaseModel):
@@ -61,7 +67,8 @@ class Friend(BaseModel):
"""昵称"""
remark: str = ""
"""备注"""
-
+ ava_url: str = ""
+ """头像url"""
class UpdateGroup(BaseModel):
"""
@@ -74,8 +81,10 @@ class UpdateGroup(BaseModel):
"""状态"""
level: int
"""群权限"""
- task_status: Dict[str, bool]
+ task: List[str]
"""被动状态"""
+ close_plugins: List[str]
+ """关闭插件"""
class FriendRequestResult(BaseModel):
@@ -103,6 +112,10 @@ class FriendRequestResult(BaseModel):
"""来自"""
comment: Optional[str]
"""备注信息"""
+ ava_url: str
+ """头像"""
+ type: str
+ """类型 private group"""
class GroupRequestResult(FriendRequestResult):
@@ -121,10 +134,10 @@ class HandleRequest(BaseModel):
操作请求接收数据
"""
- bot_id: str
+ bot_id: Optional[str] = None
"""bot_id"""
- id: int
- """id"""
+ flag: str
+ """flag"""
request_type: Literal["private", "group"]
"""类型"""
@@ -149,3 +162,97 @@ class DeleteFriend(BaseModel):
"""bot_id"""
user_id: str
"""用户id"""
+
+class ReqResult(BaseModel):
+ """
+ 好友/群组请求列表
+ """
+
+ friend: List[FriendRequestResult] = []
+ """好友请求列表"""
+ group: List[GroupRequestResult] = []
+ """群组请求列表"""
+
+
+class UserDetail(BaseModel):
+ """
+ 用户详情
+ """
+
+ user_id: str
+ """用户id"""
+ ava_url: str
+ """头像url"""
+ nickname: str
+ """昵称"""
+ remark: str
+ """备注"""
+ is_ban: bool
+ """是否被ban"""
+ chat_count: int
+ """发言次数"""
+ call_count: int
+ """功能调用次数"""
+ like_plugin: Dict[str, int]
+ """最喜爱的功能"""
+
+
+class GroupDetail(BaseModel):
+ """
+ 用户详情
+ """
+
+ group_id: str
+ """群组id"""
+ ava_url: str
+ """头像url"""
+ name: str
+ """名称"""
+ member_count: int
+ """成员数"""
+ max_member_count: int
+ """最大成员数"""
+ chat_count: int
+ """发言次数"""
+ call_count: int
+ """功能调用次数"""
+ like_plugin: Dict[str, int]
+ """最喜爱的功能"""
+ level: int
+ """群权限"""
+ status: bool
+ """状态(睡眠)"""
+ close_plugins: List[Plugin]
+ """关闭的插件"""
+ task: List[Task]
+ """被动列表"""
+
+
+class Message(BaseModel):
+ """
+ 消息
+ """
+
+ user_id: str
+ """用户id"""
+ group_id: Optional[str] = None
+ """群组id"""
+ message: str
+ """消息"""
+ name: str
+ """用户名称"""
+ ava_url: str
+ """用户头像"""
+
+class SendMessage(BaseModel):
+ """
+ 发送消息
+ """
+ bot_id: str
+ """bot id"""
+ user_id: Optional[str] = None
+ """用户id"""
+ group_id: Optional[str] = None
+ """群组id"""
+ message: str
+ """消息"""
diff --git a/plugins/web_ui/config.py b/plugins/web_ui/config.py
index a85d9126..b6e6fde0 100644
--- a/plugins/web_ui/config.py
+++ b/plugins/web_ui/config.py
@@ -20,6 +20,11 @@ app.add_middleware(
)
+AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160"
+
+GROUP_AVA_URL = "http://p.qlogo.cn/gh/{}/{}/640/"
+
+
class QueryDateType(StrEnum):
"""
diff --git a/utils/manager/requests_manager.py b/utils/manager/requests_manager.py
index c97f2000..d9b5175c 100644
--- a/utils/manager/requests_manager.py
+++ b/utils/manager/requests_manager.py
@@ -1,6 +1,6 @@
from io import BytesIO
from pathlib import Path
-from typing import Optional
+from typing import Optional, Union, overload
from nonebot.adapters.onebot.v11 import ActionFailed, Bot
@@ -67,14 +67,24 @@ class RequestManager(StaticData):
}
self.save()
+ @overload
+ def remove_request(self, type_: str, flag: str):
+ ...
+
+ @overload
def remove_request(self, type_: str, id_: int):
+ ...
+
+ def remove_request(self, type_: str, id_: Union[int, str]):
"""
删除一个请求数据
:param type_: 类型
:param id_: id,user_id 或 group_id
"""
for x in self._data[type_].keys():
- if self._data[type_][x].get("id") == id_:
+ a_id = self._data[type_][x].get("id")
+ a_flag = self._data[type_][x].get("flag")
+ if a_id == id_ or a_flag == id_:
del self._data[type_][x]
break
self.save()
@@ -88,8 +98,17 @@ class RequestManager(StaticData):
if data:
return data["invite_group"]
return None
-
+
+ @overload
async def approve(self, bot: Bot, id_: int, type_: str) -> int:
+ ...
+
+ @overload
+ async def approve(self, bot: Bot, flag: str, type_: str) -> int:
+ ...
+
+
+ async def approve(self, bot: Bot, id_: Union[int, str], type_: str) -> int:
"""
同意请求
:param bot: Bot
@@ -97,8 +116,16 @@ class RequestManager(StaticData):
:param type_: 类型,private 或 group
"""
return await self._set_add_request(bot, id_, type_, True)
+
+ @overload
+ async def refused(self, bot: Bot, id_: int, type_: str) -> int:
+ ...
- async def refused(self, bot: Bot, id_: int, type_: str) -> Optional[int]:
+ @overload
+ async def refused(self, bot: Bot, flag: str, type_: str) -> int:
+ ...
+
+ async def refused(self, bot: Bot, id_: Union[int, str], type_: str) -> Optional[int]:
"""
拒绝请求
:param bot: Bot
@@ -120,18 +147,32 @@ class RequestManager(StaticData):
self._data = {"private": {}, "group": {}}
self.save()
+ @overload
+ async def delete_request(self, id_: int, type_: str) -> int:
+ ...
+
+ @overload
+ async def delete_request(self, flag: str, type_: str) -> int:
+ ...
+
def delete_request(
- self, id_: int, type_: str
+ self, id_: Union[str, int], type_: str
): # type_: Literal["group", "private"]
"""
删除请求
:param id_: id
:param type_: 类型
"""
- id_ = str(id_)
- if self._data[type_].get(id_):
- del self._data[type_][id_]
- self.save()
+ if type(id_) == int:
+ if self._data[type_].get(id_):
+ del self._data[type_][id_]
+ self.save()
+ else:
+ for k, item in self._data[type_].items():
+ if item['flag'] == id_:
+ del self._data[type_][k]
+ self.save()
+ break
def set_group_name(self, group_name: str, group_id: int):
"""
@@ -239,7 +280,7 @@ class RequestManager(StaticData):
return bk.pic2bs4()
async def _set_add_request(
- self, bot: Bot, idx: int, type_: str, approve: bool
+ self, bot: Bot, idx: Union[str, int], type_: str, approve: bool
) -> int:
"""
处理请求
@@ -248,8 +289,13 @@ class RequestManager(StaticData):
:param type_: 类型,private 或 group
:param approve: 是否同意
"""
- id_ = str(idx)
- if id_ in self._data[type_].keys():
+ flag = None
+ id_ = None
+ if type(idx) == str:
+ flag = idx
+ else:
+ id_ = str(idx)
+ if id_ and id_ in self._data[type_].keys():
try:
if type_ == "private":
await bot.set_friend_add_request(
@@ -277,4 +323,25 @@ class RequestManager(StaticData):
del self._data[type_][id_]
self.save()
return rid
+ if flag:
+ rm_id = None
+ for k, item in self._data[type_].items():
+ if item['flag'] == flag:
+ rm_id = k
+ if type_ == 'private':
+ await bot.set_friend_add_request(
+ flag=item['flag'], approve=approve
+ )
+ rid = item["id"]
+ else:
+ await bot.set_group_add_request(
+ flag=item['flag'],
+ sub_type="invite",
+ approve=approve,
+ )
+ rid = item["invite_group"]
+ if rm_id is not None:
+ del self._data[type_][rm_id]
+ self.save()
+ return rid
return 2 # 未找到id
From 59cb2bee8b61691765d39ee07f1b85cc77367473 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 14 Jan 2024 05:20:21 +0800
Subject: [PATCH 10/20] =?UTF-8?q?perf=F0=9F=91=8C:=20webui=E5=AE=8C?=
=?UTF-8?q?=E5=96=84=E5=A5=BD=E5=8F=8B=E7=BE=A4=E7=BB=84=E7=AE=A1=E7=90=86?=
=?UTF-8?q?api=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=83=A8=E5=88=86=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E5=BA=93=E7=AE=A1=E7=90=86=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/database/__init__.py | 26 ++++-
plugins/web_ui/api/tabs/main/__init__.py | 4 +-
plugins/web_ui/api/tabs/manage/__init__.py | 100 +++++++++++-------
plugins/web_ui/api/tabs/manage/model.py | 14 ++-
.../web_ui/api/tabs/plugin_manage/__init__.py | 10 +-
5 files changed, 109 insertions(+), 45 deletions(-)
diff --git a/plugins/web_ui/api/tabs/database/__init__.py b/plugins/web_ui/api/tabs/database/__init__.py
index 3df9d212..0f3eb985 100644
--- a/plugins/web_ui/api/tabs/database/__init__.py
+++ b/plugins/web_ui/api/tabs/database/__init__.py
@@ -17,7 +17,7 @@ from ....utils import authentication
from .models.model import SqlModel, SqlText
from .models.sql_log import SqlLog
-router = APIRouter()
+router = APIRouter(prefix="/database")
driver: Driver = nonebot.get_driver()
@@ -26,6 +26,18 @@ driver: Driver = nonebot.get_driver()
SQL_DICT = {}
+SELECT_TABLE_SQL = """
+select a.tablename as name,d.description as desc from pg_tables a
+ left join pg_class c on relname=tablename
+ left join pg_description d on oid=objoid and objsubid=0 where a.schemaname = 'public'
+"""
+
+SELECT_TABLE_COLUMN_SQL = """
+SELECT column_name, data_type, character_maximum_length as max_length, is_nullable
+FROM information_schema.columns
+WHERE table_name = '{}';
+"""
+
@driver.on_startup
async def _():
for matcher in get_matchers(True):
@@ -45,6 +57,18 @@ async def _():
)
SQL_DICT[plugin_name] = SqlModel
+@router.get("/get_table_list", dependencies=[authentication()], description="获取数据库表")
+async def _() -> Result:
+ db = Tortoise.get_connection("default")
+ query = await db.execute_query_dict(SELECT_TABLE_SQL)
+ return Result.ok(query)
+
+@router.get("/get_table_column", dependencies=[authentication()], description="获取表字段")
+async def _(table_name: str) -> Result:
+ db = Tortoise.get_connection("default")
+ print(SELECT_TABLE_COLUMN_SQL.format(table_name))
+ query = await db.execute_query_dict(SELECT_TABLE_COLUMN_SQL.format(table_name))
+ return Result.ok(query)
@router.post("/exec_sql", dependencies=[authentication()], description="执行sql")
async def _(sql: SqlText, request: Request) -> Result:
diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py
index 6fec031c..b5d6e63d 100644
--- a/plugins/web_ui/api/tabs/main/__init__.py
+++ b/plugins/web_ui/api/tabs/main/__init__.py
@@ -25,7 +25,7 @@ from .model import ActiveGroup, BaseInfo, ChatHistoryCount, HotPlugin
run_time = time.time()
ws_router = APIRouter()
-router = APIRouter()
+router = APIRouter(prefix="/main")
@@ -183,7 +183,7 @@ async def _(date_type: Optional[QueryDateType] = None) -> Result:
if date_type == QueryDateType.YEAR:
query = ChatHistory.filter(create_time__gte=now - timedelta(days=365))
data_list = (
- await query.annotate(count=Count("id"))
+ await query.annotate(count=Count("id")).filter(group_id__not_isnull=True)
.group_by("group_id").order_by("-count").limit(5)
.values_list("group_id", "count")
)
diff --git a/plugins/web_ui/api/tabs/manage/__init__.py b/plugins/web_ui/api/tabs/manage/__init__.py
index 96e70ccb..e6ce8e96 100644
--- a/plugins/web_ui/api/tabs/manage/__init__.py
+++ b/plugins/web_ui/api/tabs/manage/__init__.py
@@ -33,6 +33,7 @@ from .model import (
HandleRequest,
LeaveGroup,
Message,
+ MessageItem,
Plugin,
ReqResult,
SendMessage,
@@ -42,7 +43,7 @@ from .model import (
)
ws_router = APIRouter()
-router = APIRouter()
+router = APIRouter(prefix="/manage")
SUB_PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))"
@@ -50,6 +51,8 @@ GROUP_PATTERN = r'.*?Message (-?\d*) from (\d*)@\[群:(\d*)] "(.*)"'
PRIVATE_PATTERN = r'.*?Message (-?\d*) from (\d*) "(.*)"'
+IMAGE_PATTERN = r'\[CQ:image,.*,url=(.*);.*?\]'
+
@router.get("/get_group_list", dependencies=[authentication()], description="获取群组列表")
async def _(bot_id: str) -> Result:
"""
@@ -323,8 +326,10 @@ async def _(bot_id: str, group_id: str) -> Result:
like_plugin[name] = data[1]
close_plugins = []
for module in g.close_plugins:
- plugin = Plugin(module=module, plugin_name=module)
- if plugin_data := plugin_data_manager.get(module):
+ module_ = module.replace(":super", "")
+ is_super_block = module.endswith(":super")
+ plugin = Plugin(module=module_, plugin_name=module, is_super_block=is_super_block)
+ if plugin_data := plugin_data_manager.get(module_):
plugin.plugin_name = plugin_data.name
close_plugins.append(plugin)
task_list = []
@@ -374,48 +379,71 @@ async def _(param: SendMessage) -> Result:
MSG_LIST = []
+ID2NAME = {}
+
+
+async def message_handle(sub_log: str, type: Literal["private", "group"]):
+ 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:
+ user = await FriendUser.filter(user_id=uid).first()
+ 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:
+ user = await GroupInfoUser.filter(user_id=uid, group_id=gid).first()
+ ID2NAME[gid] = user.user_name or user.nickname
+ if msg_id in MSG_LIST:
+ return
+ MSG_LIST.append(msg_id)
+ messages = []
+ 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,
+ user_id=uid,
+ group_id=gid,
+ message=messages,
+ name=ID2NAME[uid],
+ ava_url=AVA_URL.format(uid),
+ )
+
@ws_router.websocket("/chat")
-async def _(websocket: WebSocket, group_id: Optional[str] = None, user_id: Optional[str] = None):
- global MSG_LIST
+async def _(websocket: WebSocket):
await websocket.accept()
async def log_listener(log: str):
+ global MSG_LIST, ID2NAME
sub_log = re.sub(SUB_PATTERN, "", log)
+ img_list = re.findall(IMAGE_PATTERN, sub_log)
if "message.private.friend" in log:
- if r := re.search(PRIVATE_PATTERN, sub_log):
- msg_id = r.group(1)
- uid = r.group(2)
- msg = r.group(3)
- user = await FriendUser.filter(user_id=user_id).first()
- name = user.user_name
- if uid and uid == user_id and msg_id not in MSG_LIST:
- MSG_LIST.append(msg_id)
- message = Message(
- user_id=uid,
- message=msg,
- name=name,
- ava_url=AVA_URL.format(uid)
- )
- await websocket.send_json(message.dict())
+ if message := await message_handle(sub_log, 'private'):
+ await websocket.send_json(message.dict())
else:
if r := re.search(GROUP_PATTERN, sub_log):
- msg_id = r.group(1)
- uid = r.group(2)
- gid = r.group(3)
- msg = r.group(4)
- user = await GroupInfoUser.filter(user_id=uid, group_id=gid).first()
- name = user.user_name or user.nickname
- if gid and gid == group_id and msg_id not in MSG_LIST:
- MSG_LIST.append(msg_id)
- message = Message(
- user_id=uid,
- group_id=gid,
- message=msg,
- name=name,
- ava_url=AVA_URL.format(uid)
- )
+ 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:
diff --git a/plugins/web_ui/api/tabs/manage/model.py b/plugins/web_ui/api/tabs/manage/model.py
index bc8df37f..ab8b83f1 100644
--- a/plugins/web_ui/api/tabs/manage/model.py
+++ b/plugins/web_ui/api/tabs/manage/model.py
@@ -41,6 +41,8 @@ class Plugin(BaseModel):
"""模块名"""
plugin_name: str
"""中文名"""
+ is_super_block: bool
+ """是否超级用户禁用"""
class GroupResult(BaseModel):
@@ -227,23 +229,33 @@ class GroupDetail(BaseModel):
task: List[Task]
"""被动列表"""
+class MessageItem(BaseModel):
+
+ type: str
+ """消息类型"""
+ msg: str
+ """内容"""
class Message(BaseModel):
"""
消息
"""
+ object_id: str
+ """主体id user_id 或 group_id"""
user_id: str
"""用户id"""
group_id: Optional[str] = None
"""群组id"""
- message: str
+ message: List[MessageItem]
"""消息"""
name: str
"""用户名称"""
ava_url: str
"""用户头像"""
+
+
class SendMessage(BaseModel):
"""
发送消息
diff --git a/plugins/web_ui/api/tabs/plugin_manage/__init__.py b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
index 6179d9c5..20e5fd97 100644
--- a/plugins/web_ui/api/tabs/plugin_manage/__init__.py
+++ b/plugins/web_ui/api/tabs/plugin_manage/__init__.py
@@ -2,7 +2,7 @@ import re
from typing import List, Optional
import cattrs
-from fastapi import APIRouter
+from fastapi import APIRouter, Query
from configs.config import Config
from services.log import logger
@@ -17,26 +17,26 @@ from .model import (
PluginDetail,
PluginInfo,
PluginSwitch,
- UpdateConfig,
UpdatePlugin,
)
-router = APIRouter()
+router = APIRouter(prefix="/plugin")
@router.get("/get_plugin_list", dependencies=[authentication()], deprecated="获取插件列表")
def _(
- plugin_type: PluginType, menu_type: Optional[str] = None
+ plugin_type: List[PluginType] = Query(None), menu_type: Optional[str] = None
) -> Result:
"""
获取插件列表
:param plugin_type: 类型 normal, superuser, hidden, admin
+ :param menu_type: 菜单类型
"""
try:
plugin_list: List[PluginInfo] = []
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:
+ if plugin_data and plugin_data.plugin_type in plugin_type:
setting = plugin_data.plugin_setting or PluginSetting()
plugin = plugin_data.plugin_status
menu_type_ = getattr(setting, "plugin_type", ["无"])[0]
From 45acf4a0941eaee340d31c95ff971618ffc945bc Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 14 Jan 2024 17:48:15 +0800
Subject: [PATCH 11/20] =?UTF-8?q?perf=F0=9F=91=8C:=20webui=E5=AE=8C?=
=?UTF-8?q?=E5=96=84=E6=95=B0=E6=8D=AE=E5=BA=93=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/database/__init__.py | 12 ++++---
.../web_ui/api/tabs/database/models/model.py | 3 ++
plugins/web_ui/api/tabs/manage/__init__.py | 11 +++++--
plugins/web_ui/base_model.py | 31 +++++++------------
4 files changed, 31 insertions(+), 26 deletions(-)
diff --git a/plugins/web_ui/api/tabs/database/__init__.py b/plugins/web_ui/api/tabs/database/__init__.py
index 0f3eb985..da8c74e0 100644
--- a/plugins/web_ui/api/tabs/database/__init__.py
+++ b/plugins/web_ui/api/tabs/database/__init__.py
@@ -11,7 +11,7 @@ from configs.config import NICKNAME
from services.db_context import TestSQL
from utils.utils import get_matchers
-from ....base_model import QueryModel, Result
+from ....base_model import BaseResultModel, QueryModel, Result
from ....config import QueryDateType
from ....utils import authentication
from .models.model import SqlModel, SqlText
@@ -77,6 +77,7 @@ async def _(sql: SqlText, request: Request) -> Result:
if sql.sql.lower().startswith("select"):
db = Tortoise.get_connection("default")
res = await db.execute_query_dict(sql.sql)
+ await SqlLog.add(ip or "0.0.0.0", sql.sql, "")
return Result.ok(res, "执行成功啦!")
else:
result = await TestSQL.raw(sql.sql)
@@ -89,11 +90,14 @@ async def _(sql: SqlText, request: Request) -> Result:
@router.post("/get_sql_log", dependencies=[authentication()], description="sql日志列表")
async def _(query: QueryModel) -> Result:
- data = await SqlLog.all().offset((query.index - 1) * query.size).limit(query.size)
- return Result.ok(data)
+ total = await SqlLog.all().count()
+ if (total % query.size):
+ total += 1
+ data = await SqlLog.all().order_by("-id").offset((query.index - 1) * query.size).limit(query.size)
+ return Result.ok(BaseResultModel(total=total, data=data))
-@router.get("/get_sql", dependencies=[authentication()], description="常用sql")
+@router.get("/get_common_sql", dependencies=[authentication()], description="常用sql")
async def _(plugin_name: Optional[str] = None) -> Result:
if plugin_name:
return Result.ok(SQL_DICT.get(plugin_name))
diff --git a/plugins/web_ui/api/tabs/database/models/model.py b/plugins/web_ui/api/tabs/database/models/model.py
index ed78c405..37c682a6 100644
--- a/plugins/web_ui/api/tabs/database/models/model.py
+++ b/plugins/web_ui/api/tabs/database/models/model.py
@@ -14,6 +14,9 @@ class SqlText(BaseModel):
class SqlModel(BaseModel):
+ """
+ 常用sql
+ """
name: str
"""插件中文名称"""
diff --git a/plugins/web_ui/api/tabs/manage/__init__.py b/plugins/web_ui/api/tabs/manage/__init__.py
index e6ce8e96..3bfdf9d5 100644
--- a/plugins/web_ui/api/tabs/manage/__init__.py
+++ b/plugins/web_ui/api/tabs/manage/__init__.py
@@ -51,6 +51,8 @@ GROUP_PATTERN = r'.*?Message (-?\d*) from (\d*)@\[群:(\d*)] "(.*)"'
PRIVATE_PATTERN = r'.*?Message (-?\d*) from (\d*) "(.*)"'
+AT_PATTERN = r'\[CQ:at,qq=(.*)\]'
+
IMAGE_PATTERN = r'\[CQ:image,.*,url=(.*);.*?\]'
@router.get("/get_group_list", dependencies=[authentication()], description="获取群组列表")
@@ -405,7 +407,12 @@ async def message_handle(sub_log: str, type: Literal["private", "group"]):
msg = r.group(4)
if gid not in ID2NAME:
user = await GroupInfoUser.filter(user_id=uid, group_id=gid).first()
- ID2NAME[gid] = user.user_name or user.nickname
+ 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)
@@ -423,7 +430,7 @@ async def message_handle(sub_log: str, type: Literal["private", "group"]):
user_id=uid,
group_id=gid,
message=messages,
- name=ID2NAME[uid],
+ name=ID2NAME.get(uid) or "",
ava_url=AVA_URL.format(uid),
)
diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py
index 2ce1a82f..384d1a7c 100644
--- a/plugins/web_ui/base_model.py
+++ b/plugins/web_ui/base_model.py
@@ -74,6 +74,17 @@ class QueryModel(BaseModel, Generic[T]):
if size < 1:
raise ValueError("每页数量小于1...")
return size
+
+
+class BaseResultModel(BaseModel):
+ """
+ 基础返回
+ """
+
+ total: int
+ """总页数"""
+ data: Any
+ """数据"""
class PluginConfig(BaseModel):
@@ -89,26 +100,6 @@ class PluginConfig(BaseModel):
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):
"""
From b656f89c8ea815301a471749819a5724590cadb1 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 15 Jan 2024 01:19:37 +0800
Subject: [PATCH 12/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E5=AE=8C=E5=96=84?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/__init__.py | 2 +
plugins/web_ui/api/system.py | 228 -------------------
plugins/web_ui/api/tabs/__init__.py | 1 +
plugins/web_ui/api/tabs/database/__init__.py | 2 +-
plugins/web_ui/api/tabs/main/__init__.py | 4 +-
plugins/web_ui/api/tabs/system/__init__.py | 26 +++
plugins/web_ui/api/tabs/system/model.py | 35 +++
plugins/web_ui/base_model.py | 40 ----
plugins/web_ui/utils.py | 10 +-
9 files changed, 68 insertions(+), 280 deletions(-)
delete mode 100644 plugins/web_ui/api/system.py
create mode 100644 plugins/web_ui/api/tabs/system/__init__.py
create mode 100644 plugins/web_ui/api/tabs/system/model.py
diff --git a/plugins/web_ui/__init__.py b/plugins/web_ui/__init__.py
index 3de51bfe..00bcd2b5 100644
--- a/plugins/web_ui/__init__.py
+++ b/plugins/web_ui/__init__.py
@@ -20,6 +20,7 @@ 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.plugin_manage import router as plugin_router
+from .api.tabs.system import router as system_router
from .auth import router as auth_router
driver = nonebot.get_driver()
@@ -37,6 +38,7 @@ BaseApiRouter.include_router(main_router)
BaseApiRouter.include_router(manage_router)
BaseApiRouter.include_router(database_router)
BaseApiRouter.include_router(plugin_router)
+BaseApiRouter.include_router(system_router)
WsApiRouter = APIRouter(prefix="/zhenxun/socket")
diff --git a/plugins/web_ui/api/system.py b/plugins/web_ui/api/system.py
deleted file mode 100644
index dacc1d9f..00000000
--- a/plugins/web_ui/api/system.py
+++ /dev/null
@@ -1,228 +0,0 @@
-# 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
-
-# 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 ..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": []}
-
-# router = APIRouter()
-
-
-# @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("/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,
-# ),
-# )
-
-
-# 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_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 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
index c2758a6a..99ed6ea1 100644
--- a/plugins/web_ui/api/tabs/__init__.py
+++ b/plugins/web_ui/api/tabs/__init__.py
@@ -2,3 +2,4 @@ from .database import *
from .main import *
from .manage import *
from .plugin_manage import *
+from .system import *
diff --git a/plugins/web_ui/api/tabs/database/__init__.py b/plugins/web_ui/api/tabs/database/__init__.py
index da8c74e0..f047d711 100644
--- a/plugins/web_ui/api/tabs/database/__init__.py
+++ b/plugins/web_ui/api/tabs/database/__init__.py
@@ -101,4 +101,4 @@ async def _(query: QueryModel) -> Result:
async def _(plugin_name: Optional[str] = None) -> Result:
if plugin_name:
return Result.ok(SQL_DICT.get(plugin_name))
- return Result.ok(SQL_DICT)
+ return Result.ok(str(SQL_DICT))
diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py
index b5d6e63d..fdbe8888 100644
--- a/plugins/web_ui/api/tabs/main/__init__.py
+++ b/plugins/web_ui/api/tabs/main/__init__.py
@@ -246,14 +246,14 @@ async def _(date_type: Optional[QueryDateType] = None) -> Result:
@ws_router.websocket("/system_status")
-async def system_logs_realtime(websocket: WebSocket):
+async def system_logs_realtime(websocket: WebSocket, sleep: Optional[int] = 5):
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)
+ await asyncio.sleep(sleep)
except (WebSocketDisconnect, ConnectionClosedError, ConnectionClosedOK):
pass
return
diff --git a/plugins/web_ui/api/tabs/system/__init__.py b/plugins/web_ui/api/tabs/system/__init__.py
new file mode 100644
index 00000000..cc696e11
--- /dev/null
+++ b/plugins/web_ui/api/tabs/system/__init__.py
@@ -0,0 +1,26 @@
+import os
+from pathlib import Path
+from typing import List, Optional
+
+from fastapi import APIRouter
+
+from ....base_model import Result
+from ....utils import authentication, get_system_disk
+from .model import DirFile
+
+router = APIRouter(prefix="/system")
+
+
+
+@router.get("/get_dir_list", dependencies=[authentication()], description="获取文件列表")
+async def _(path: Optional[str] = None) -> Result:
+ base_path = Path(path) if path else Path()
+ data_list = []
+ for file in os.listdir(base_path):
+ data_list.append(DirFile(is_file=not (base_path / file).is_dir(), name=file, parent=path))
+ return Result.ok(data_list)
+
+
+@router.get("/get_resources_size", dependencies=[authentication()], description="获取文件列表")
+async def _(type: Optional[str] = None) -> Result:
+ return Result.ok(await get_system_disk(type))
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/system/model.py b/plugins/web_ui/api/tabs/system/model.py
new file mode 100644
index 00000000..b00a582c
--- /dev/null
+++ b/plugins/web_ui/api/tabs/system/model.py
@@ -0,0 +1,35 @@
+
+
+
+from datetime import datetime
+from typing import Literal, Optional
+
+from pydantic import BaseModel
+
+
+class DirFile(BaseModel):
+
+ """
+ 文件或文件夹
+ """
+
+ is_file: bool
+ """是否为文件"""
+ name: str
+ """文件夹或文件名称"""
+ parent: Optional[str] = None
+ """父级"""
+
+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
\ No newline at end of file
diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py
index 384d1a7c..f05a685e 100644
--- a/plugins/web_ui/base_model.py
+++ b/plugins/web_ui/base_model.py
@@ -87,20 +87,6 @@ class BaseResultModel(BaseModel):
"""数据"""
-class PluginConfig(BaseModel):
- """
- 插件配置项
- """
-
- module: str
- key: str
- value: Optional[Any]
- help: Optional[str]
- default_value: Optional[Any]
- has_type: bool
-
-
-
class SystemStatus(BaseModel):
"""
系统状态
@@ -112,13 +98,6 @@ class SystemStatus(BaseModel):
check_time: datetime
-class SystemNetwork(BaseModel):
- """
- 系统网络状态
- """
-
- baidu: int
- google: int
class SystemFolderSize(BaseModel):
@@ -136,22 +115,3 @@ class SystemFolderSize(BaseModel):
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/utils.py b/plugins/web_ui/utils.py
index cce1c692..a4b7d6d2 100644
--- a/plugins/web_ui/utils.py
+++ b/plugins/web_ui/utils.py
@@ -21,15 +21,7 @@ from configs.path_config import (
TEXT_PATH,
)
-from .base_model import (
- Result,
- SystemFolderSize,
- SystemNetwork,
- SystemResult,
- SystemStatus,
- SystemStatusList,
- User,
-)
+from .base_model import SystemFolderSize, SystemStatus, User
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
From 72b0b011ee5e03a47235637c07c775012c78dc93 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 15 Jan 2024 13:00:17 +0800
Subject: [PATCH 13/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E5=AE=8C=E5=96=84?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=AE=A1=E7=90=86api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/system/__init__.py | 4 +-
plugins/web_ui/api/tabs/system/model.py | 14 ----
plugins/web_ui/base_model.py | 16 ++---
plugins/web_ui/utils.py | 81 +++++++++++-----------
4 files changed, 51 insertions(+), 64 deletions(-)
diff --git a/plugins/web_ui/api/tabs/system/__init__.py b/plugins/web_ui/api/tabs/system/__init__.py
index cc696e11..ef80d1bc 100644
--- a/plugins/web_ui/api/tabs/system/__init__.py
+++ b/plugins/web_ui/api/tabs/system/__init__.py
@@ -22,5 +22,5 @@ async def _(path: Optional[str] = None) -> Result:
@router.get("/get_resources_size", dependencies=[authentication()], description="获取文件列表")
-async def _(type: Optional[str] = None) -> Result:
- return Result.ok(await get_system_disk(type))
\ No newline at end of file
+async def _(full_path: Optional[str] = None) -> Result:
+ return Result.ok(await get_system_disk(full_path))
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/system/model.py b/plugins/web_ui/api/tabs/system/model.py
index b00a582c..01d42218 100644
--- a/plugins/web_ui/api/tabs/system/model.py
+++ b/plugins/web_ui/api/tabs/system/model.py
@@ -19,17 +19,3 @@ class DirFile(BaseModel):
"""文件夹或文件名称"""
parent: Optional[str] = None
"""父级"""
-
-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
\ No newline at end of file
diff --git a/plugins/web_ui/base_model.py b/plugins/web_ui/base_model.py
index f05a685e..ee7a93e4 100644
--- a/plugins/web_ui/base_model.py
+++ b/plugins/web_ui/base_model.py
@@ -105,13 +105,13 @@ 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
+ name: str
+ """名称"""
+ size: float
+ """大小"""
+ full_path: Optional[str]
+ """完整路径"""
+ is_dir: bool
+ """是否为文件夹"""
diff --git a/plugins/web_ui/utils.py b/plugins/web_ui/utils.py
index a4b7d6d2..eba64b63 100644
--- a/plugins/web_ui/utils.py
+++ b/plugins/web_ui/utils.py
@@ -1,7 +1,7 @@
import os
from datetime import datetime, timedelta
from pathlib import Path
-from typing import Any, Dict, Optional, Union
+from typing import Any, Dict, List, Optional, Union
import psutil
import ujson as json
@@ -123,47 +123,48 @@ def get_system_status() -> SystemStatus:
@run_sync
def get_system_disk(
- type_: Optional[str],
-) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]:
+ full_path: Optional[str],
+) -> List[SystemFolderSize]:
"""
说明:
获取资源文件大小等
"""
- 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
+ base_path = Path(full_path) if full_path else Path()
+ other_size = 0
+ data_list = []
+ for file in os.listdir(base_path):
+ f = base_path / file
+ if f.is_dir():
+ size = _get_dir_size(f) / 1024 / 1024
+ data_list.append(SystemFolderSize(name=file, size=size, full_path=str(f), is_dir=True))
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
+ other_size += f.stat().st_size / 1024 / 1024
+ if other_size:
+ data_list.append(SystemFolderSize(name='other_file', size=other_size, full_path=full_path, is_dir=False))
+ return data_list
+ # 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
From 1a9a19e1cb858c78a8932a044581e8487cb226a0 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 21 Jan 2024 06:34:39 +0800
Subject: [PATCH 14/20] =?UTF-8?q?feat=E2=9C=A8:=20webui=E7=BB=93=E6=9E=84?=
=?UTF-8?q?=E6=A0=91=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4/=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E/=E9=87=8D=E5=91=BD=E5=90=8D=E6=96=87=E4=BB=B6/?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/system/__init__.py | 65 +++++++++++++++++++++-
plugins/web_ui/api/tabs/system/model.py | 32 +++++++++++
2 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/plugins/web_ui/api/tabs/system/__init__.py b/plugins/web_ui/api/tabs/system/__init__.py
index ef80d1bc..36755ff4 100644
--- a/plugins/web_ui/api/tabs/system/__init__.py
+++ b/plugins/web_ui/api/tabs/system/__init__.py
@@ -1,4 +1,5 @@
import os
+import shutil
from pathlib import Path
from typing import List, Optional
@@ -6,7 +7,7 @@ from fastapi import APIRouter
from ....base_model import Result
from ....utils import authentication, get_system_disk
-from .model import DirFile
+from .model import AddFile, DeleteFile, DirFile, RenameFile
router = APIRouter(prefix="/system")
@@ -23,4 +24,64 @@ async def _(path: Optional[str] = None) -> Result:
@router.get("/get_resources_size", dependencies=[authentication()], description="获取文件列表")
async def _(full_path: Optional[str] = None) -> Result:
- return Result.ok(await get_system_disk(full_path))
\ No newline at end of file
+ return Result.ok(await get_system_disk(full_path))
+
+
+@router.post("/delete_file", dependencies=[authentication()], description="删除文件")
+async def _(param: DeleteFile) -> Result:
+ path = Path(param.full_path)
+ if not path or not path.exists():
+ return Result.warning_("文件不存在...")
+ try:
+ path.unlink()
+ return Result.ok('删除成功!')
+ except Exception as e:
+ return Result.warning_('删除失败: ' + str(e))
+
+@router.post("/delete_folder", dependencies=[authentication()], description="删除文件夹")
+async def _(param: DeleteFile) -> Result:
+ path = Path(param.full_path)
+ if not path or not path.exists() or path.is_file():
+ return Result.warning_("文件夹不存在...")
+ try:
+ shutil.rmtree(path.absolute())
+ return Result.ok('删除成功!')
+ except Exception as e:
+ return Result.warning_('删除失败: ' + str(e))
+
+
+@router.post("/rename_file", dependencies=[authentication()], description="重命名文件")
+async def _(param: RenameFile) -> Result:
+ path = (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name)
+ if not path or not path.exists():
+ return Result.warning_("文件不存在...")
+ try:
+ path.rename(path.parent / param.name)
+ return Result.ok('重命名成功!')
+ except Exception as e:
+ return Result.warning_('重命名失败: ' + str(e))
+
+
+@router.post("/rename_folder", dependencies=[authentication()], description="重命名文件夹")
+async def _(param: RenameFile) -> Result:
+ path = (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name)
+ if not path or not path.exists() or path.is_file():
+ return Result.warning_("文件夹不存在...")
+ try:
+ new_path = path.parent / param.name
+ shutil.move(path.absolute(), new_path.absolute())
+ return Result.ok('重命名成功!')
+ except Exception as e:
+ return Result.warning_('重命名失败: ' + str(e))
+
+
+@router.post("/add_file", dependencies=[authentication()], description="新建文件")
+async def _(param: AddFile) -> Result:
+ path = (Path(param.parent) / param.name) if param.parent else Path(param.name)
+ if path.exists():
+ return Result.warning_("文件已存在...")
+ try:
+ path.open('w')
+ return Result.ok('新建文件成功!')
+ except Exception as e:
+ return Result.warning_('新建文件失败: ' + str(e))
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/system/model.py b/plugins/web_ui/api/tabs/system/model.py
index 01d42218..af4f6209 100644
--- a/plugins/web_ui/api/tabs/system/model.py
+++ b/plugins/web_ui/api/tabs/system/model.py
@@ -19,3 +19,35 @@ class DirFile(BaseModel):
"""文件夹或文件名称"""
parent: Optional[str] = None
"""父级"""
+
+class DeleteFile(BaseModel):
+
+ """
+ 删除文件
+ """
+
+ full_path: str
+ """文件全路径"""
+
+class RenameFile(BaseModel):
+
+ """
+ 删除文件
+ """
+ parent: Optional[str]
+ """父路径"""
+ old_name: str
+ """旧名称"""
+ name: str
+ """新名称"""
+
+
+class AddFile(BaseModel):
+
+ """
+ 新建文件
+ """
+ parent: Optional[str]
+ """父路径"""
+ name: str
+ """新名称"""
From 45d19d1cdd96827aa4a5daad1d60f05b16e74be0 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 21 Jan 2024 22:27:03 +0800
Subject: [PATCH 15/20] =?UTF-8?q?feat=E2=9C=A8:=20=20=E7=9B=AE=E5=BD=95?=
=?UTF-8?q?=E7=BB=93=E6=9E=84=E6=A0=91=E7=BC=96=E8=BE=91=E6=9F=A5=E7=9C=8B?=
=?UTF-8?q?api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/system/__init__.py | 26 +++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/plugins/web_ui/api/tabs/system/__init__.py b/plugins/web_ui/api/tabs/system/__init__.py
index 36755ff4..ec41df26 100644
--- a/plugins/web_ui/api/tabs/system/__init__.py
+++ b/plugins/web_ui/api/tabs/system/__init__.py
@@ -84,4 +84,28 @@ async def _(param: AddFile) -> Result:
path.open('w')
return Result.ok('新建文件成功!')
except Exception as e:
- return Result.warning_('新建文件失败: ' + str(e))
\ No newline at end of file
+ return Result.warning_('新建文件失败: ' + str(e))
+
+
+@router.post("/add_folder", dependencies=[authentication()], description="新建文件夹")
+async def _(param: AddFile) -> Result:
+ path = (Path(param.parent) / param.name) if param.parent else Path(param.name)
+ if path.exists():
+ return Result.warning_("文件夹已存在...")
+ try:
+ path.mkdir()
+ return Result.ok('新建文件夹成功!')
+ except Exception as e:
+ return Result.warning_('新建文件夹失败: ' + str(e))
+
+
+@router.get("/read_file", dependencies=[authentication()], description="读取文件")
+async def _(full_path: str) -> Result:
+ path = Path(full_path)
+ if not path.exists():
+ return Result.warning_("文件不存在...")
+ try:
+ text = path.read_text(encoding='utf-8')
+ return Result.ok(text)
+ except Exception as e:
+ return Result.warning_('新建文件夹失败: ' + str(e))
\ No newline at end of file
From 428f374a8c6418279b67757e66312304896f4d01 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Sun, 21 Jan 2024 22:34:05 +0800
Subject: [PATCH 16/20] =?UTF-8?q?feat=E2=9C=A8:=20=E7=9B=AE=E5=BD=95?=
=?UTF-8?q?=E7=BB=93=E6=9E=84=E6=A0=91=E7=BC=96=E8=BE=91=E6=96=87=E4=BB=B6?=
=?UTF-8?q?api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/system/__init__.py | 12 +++++++++++-
plugins/web_ui/api/tabs/system/model.py | 11 +++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/plugins/web_ui/api/tabs/system/__init__.py b/plugins/web_ui/api/tabs/system/__init__.py
index ec41df26..61430144 100644
--- a/plugins/web_ui/api/tabs/system/__init__.py
+++ b/plugins/web_ui/api/tabs/system/__init__.py
@@ -7,7 +7,7 @@ from fastapi import APIRouter
from ....base_model import Result
from ....utils import authentication, get_system_disk
-from .model import AddFile, DeleteFile, DirFile, RenameFile
+from .model import AddFile, DeleteFile, DirFile, RenameFile, SaveFile
router = APIRouter(prefix="/system")
@@ -107,5 +107,15 @@ async def _(full_path: str) -> Result:
try:
text = path.read_text(encoding='utf-8')
return Result.ok(text)
+ except Exception as e:
+ return Result.warning_('新建文件夹失败: ' + str(e))
+
+@router.post("/save_file", dependencies=[authentication()], description="读取文件")
+async def _(param: SaveFile) -> Result:
+ path = Path(param.full_path)
+ try:
+ with path.open('w') as f:
+ f.write(param.content)
+ return Result.ok("更新成功!")
except Exception as e:
return Result.warning_('新建文件夹失败: ' + str(e))
\ No newline at end of file
diff --git a/plugins/web_ui/api/tabs/system/model.py b/plugins/web_ui/api/tabs/system/model.py
index af4f6209..b3b5a45f 100644
--- a/plugins/web_ui/api/tabs/system/model.py
+++ b/plugins/web_ui/api/tabs/system/model.py
@@ -51,3 +51,14 @@ class AddFile(BaseModel):
"""父路径"""
name: str
"""新名称"""
+
+
+class SaveFile(BaseModel):
+
+ """
+ 保存文件
+ """
+ full_path: str
+ """全路径"""
+ content: str
+ """内容"""
From 66c9b8045da7c75574d7ac5b9a68ca0fea2227ce Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Mon, 22 Jan 2024 05:33:41 +0800
Subject: [PATCH 17/20] =?UTF-8?q?perf=F0=9F=91=8C:=20webui=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=E7=BB=86=E8=8A=82=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/auth/__init__.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/plugins/web_ui/auth/__init__.py b/plugins/web_ui/auth/__init__.py
index 0040be85..d927977f 100644
--- a/plugins/web_ui/auth/__init__.py
+++ b/plugins/web_ui/auth/__init__.py
@@ -5,6 +5,8 @@ import nonebot
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm
+from configs.config import Config
+
from ..base_model import Result
from ..utils import (
ACCESS_TOKEN_EXPIRE_MINUTES,
@@ -22,13 +24,14 @@ router = APIRouter()
@router.post("/login")
async def login_get_token(form_data: OAuth2PasswordRequestForm = Depends()):
- if user := get_user(form_data.username):
- if user.password != form_data.password:
- return Result.fail("真笨, 密码都能记错!", 999)
- else:
+ username = Config.get_config("web-ui", "username")
+ password = Config.get_config("web-ui", "password")
+ if not username or not password:
return Result.fail("你滴配置文件里用户名密码配置项为空", 998)
+ if username != form_data.username or password != form_data.password:
+ return Result.fail("真笨, 账号密码都能记错!", 999)
access_token = create_token(
- user=user, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+ user=get_user(form_data.username), expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
token_data["token"].append(access_token)
if len(token_data["token"]) > 3:
From 38c79a81e3d46de23170e786f02df83b222454e4 Mon Sep 17 00:00:00 2001
From: HibiKier <775757368@qq.com>
Date: Thu, 25 Jan 2024 04:08:12 +0800
Subject: [PATCH 18/20] =?UTF-8?q?perf=F0=9F=91=8C:=20=E7=BB=86=E8=8A=82?=
=?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
plugins/web_ui/api/tabs/main/__init__.py | 9 ++++++++-
plugins/web_ui/api/tabs/main/model.py | 13 +++++--------
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/plugins/web_ui/api/tabs/main/__init__.py b/plugins/web_ui/api/tabs/main/__init__.py
index fdbe8888..ac892e65 100644
--- a/plugins/web_ui/api/tabs/main/__init__.py
+++ b/plugins/web_ui/api/tabs/main/__init__.py
@@ -1,6 +1,7 @@
import asyncio
import time
from datetime import datetime, timedelta
+from pathlib import Path
from typing import List, Optional
import nonebot
@@ -84,7 +85,13 @@ async def _(bot_id: Optional[str] = None) -> Result:
if select_bot.connect_time:
connect_date = datetime.fromtimestamp(select_bot.connect_time)
select_bot.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
-
+ version_file = Path() / "__version__"
+ if version_file.exists():
+ if text := version_file.open().read():
+ if ver := text.replace("__version__: ", "").strip():
+ select_bot.version = ver
+ day_call = await Statistics.filter(create_time__gte=now - timedelta(hours=now.hour)).count()
+ select_bot.day_call = day_call
return Result.ok(bot_list, "拿到信息啦!")
return Result.warning_("无Bot连接...")
diff --git a/plugins/web_ui/api/tabs/main/model.py b/plugins/web_ui/api/tabs/main/model.py
index bced1817..7dd770c6 100644
--- a/plugins/web_ui/api/tabs/main/model.py
+++ b/plugins/web_ui/api/tabs/main/model.py
@@ -35,14 +35,6 @@ class BaseInfo(BaseModel):
"""群聊数量"""
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
"""连接时间"""
connect_date: Optional[datetime] = None
@@ -60,6 +52,11 @@ class BaseInfo(BaseModel):
config: Optional[Config] = None
"""nb配置"""
+ day_call: int = 0
+ """今日调用插件次数"""
+ version: str = "unknown"
+ """真寻版本"""
+
class Config:
arbitrary_types_allowed = True
From 3c9b56f35f80e7402aff4c5d4eb1aca151206bf3 Mon Sep 17 00:00:00 2001
From: HibiKier <45528451+HibiKier@users.noreply.github.com>
Date: Thu, 25 Jan 2024 18:32:51 +0800
Subject: [PATCH 19/20] Update README.md
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 4b74dee2..ad1eaaac 100644
--- a/README.md
+++ b/README.md
@@ -338,7 +338,6 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
### 2023/12/28
* 修复B站动态获取失败的时候,会发送空消息
-* test
### 2023/9/6
From a3004438a9260631ec6deb39f99f96f63e32ac36 Mon Sep 17 00:00:00 2001
From: HibiKier <45528451+HibiKier@users.noreply.github.com>
Date: Thu, 25 Jan 2024 18:35:48 +0800
Subject: [PATCH 20/20] Update README.md
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index ad1eaaac..476b3780 100644
--- a/README.md
+++ b/README.md
@@ -335,6 +335,10 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
## 更新
+### 2024/1/25
+
+* 重构webui
+
### 2023/12/28
* 修复B站动态获取失败的时候,会发送空消息