mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 06:12:53 +08:00
✨ 新增插件商店api (#1659)
* ✨ 新增插件商店api
* chore(version): Update version to v0.2.2-7e15f20
---------
Co-authored-by: HibiKier <HibiKier@users.noreply.github.com>
This commit is contained in:
parent
e8425f371c
commit
d68a4099ba
@ -1 +1 @@
|
|||||||
__version__: v0.2.2-e08b388
|
__version__: v0.2.2-7e15f20
|
||||||
|
|||||||
@ -70,7 +70,7 @@ def install_requirement(plugin_path: Path):
|
|||||||
class ShopManage:
|
class ShopManage:
|
||||||
@classmethod
|
@classmethod
|
||||||
@cached(60)
|
@cached(60)
|
||||||
async def __get_data(cls) -> dict[str, StorePluginInfo]:
|
async def get_data(cls) -> dict[str, StorePluginInfo]:
|
||||||
"""获取插件信息数据
|
"""获取插件信息数据
|
||||||
|
|
||||||
异常:
|
异常:
|
||||||
@ -150,7 +150,7 @@ class ShopManage:
|
|||||||
返回:
|
返回:
|
||||||
BuildImage | str: 返回消息
|
BuildImage | str: 返回消息
|
||||||
"""
|
"""
|
||||||
data: dict[str, StorePluginInfo] = await cls.__get_data()
|
data: dict[str, StorePluginInfo] = await cls.get_data()
|
||||||
column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"]
|
column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"]
|
||||||
plugin_list = await cls.get_loaded_plugins("module", "version")
|
plugin_list = await cls.get_loaded_plugins("module", "version")
|
||||||
suc_plugin = {p[0]: (p[1] or "0.1") for p in plugin_list}
|
suc_plugin = {p[0]: (p[1] or "0.1") for p in plugin_list}
|
||||||
@ -184,7 +184,7 @@ class ShopManage:
|
|||||||
返回:
|
返回:
|
||||||
str: 返回消息
|
str: 返回消息
|
||||||
"""
|
"""
|
||||||
data: dict[str, StorePluginInfo] = await cls.__get_data()
|
data: dict[str, StorePluginInfo] = await cls.get_data()
|
||||||
if plugin_id < 0 or plugin_id >= len(data):
|
if plugin_id < 0 or plugin_id >= len(data):
|
||||||
return "插件ID不存在..."
|
return "插件ID不存在..."
|
||||||
plugin_key = list(data.keys())[plugin_id]
|
plugin_key = list(data.keys())[plugin_id]
|
||||||
@ -274,7 +274,7 @@ class ShopManage:
|
|||||||
返回:
|
返回:
|
||||||
str: 返回消息
|
str: 返回消息
|
||||||
"""
|
"""
|
||||||
data: dict[str, StorePluginInfo] = await cls.__get_data()
|
data: dict[str, StorePluginInfo] = await cls.get_data()
|
||||||
if plugin_id < 0 or plugin_id >= len(data):
|
if plugin_id < 0 or plugin_id >= len(data):
|
||||||
return "插件ID不存在..."
|
return "插件ID不存在..."
|
||||||
plugin_key = list(data.keys())[plugin_id]
|
plugin_key = list(data.keys())[plugin_id]
|
||||||
@ -306,7 +306,7 @@ class ShopManage:
|
|||||||
返回:
|
返回:
|
||||||
BuildImage | str: 返回消息
|
BuildImage | str: 返回消息
|
||||||
"""
|
"""
|
||||||
data: dict[str, StorePluginInfo] = await cls.__get_data()
|
data: dict[str, StorePluginInfo] = await cls.get_data()
|
||||||
plugin_list = await cls.get_loaded_plugins("module", "version")
|
plugin_list = await cls.get_loaded_plugins("module", "version")
|
||||||
suc_plugin = {p[0]: (p[1] or "Unknown") for p in plugin_list}
|
suc_plugin = {p[0]: (p[1] or "Unknown") for p in plugin_list}
|
||||||
filtered_data = [
|
filtered_data = [
|
||||||
@ -349,7 +349,7 @@ class ShopManage:
|
|||||||
返回:
|
返回:
|
||||||
str: 返回消息
|
str: 返回消息
|
||||||
"""
|
"""
|
||||||
data: dict[str, StorePluginInfo] = await cls.__get_data()
|
data: dict[str, StorePluginInfo] = await cls.get_data()
|
||||||
if plugin_id < 0 or plugin_id >= len(data):
|
if plugin_id < 0 or plugin_id >= len(data):
|
||||||
return "插件ID不存在..."
|
return "插件ID不存在..."
|
||||||
plugin_key = list(data.keys())[plugin_id]
|
plugin_key = list(data.keys())[plugin_id]
|
||||||
|
|||||||
@ -16,14 +16,23 @@ class StorePluginInfo(BaseModel):
|
|||||||
"""插件信息"""
|
"""插件信息"""
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
|
"""模块名"""
|
||||||
module_path: str
|
module_path: str
|
||||||
|
"""模块路径"""
|
||||||
description: str
|
description: str
|
||||||
|
"""简介"""
|
||||||
usage: str
|
usage: str
|
||||||
|
"""用法"""
|
||||||
author: str
|
author: str
|
||||||
|
"""作者"""
|
||||||
version: str
|
version: str
|
||||||
|
"""版本"""
|
||||||
plugin_type: PluginType
|
plugin_type: PluginType
|
||||||
|
"""插件类型"""
|
||||||
is_dir: bool
|
is_dir: bool
|
||||||
|
"""是否为文件夹插件"""
|
||||||
github_url: str | None
|
github_url: str | None
|
||||||
|
"""github链接"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def plugin_type_name(self):
|
def plugin_type_name(self):
|
||||||
|
|||||||
@ -20,8 +20,10 @@ from .api.tabs.manage import router as manage_router
|
|||||||
from .api.tabs.system import router as system_router
|
from .api.tabs.system import router as system_router
|
||||||
from .api.tabs.main import ws_router as status_routes
|
from .api.tabs.main import ws_router as status_routes
|
||||||
from .api.tabs.database import router as database_router
|
from .api.tabs.database import router as database_router
|
||||||
|
from .api.tabs.dashboard import router as dashboard_router
|
||||||
from .api.tabs.manage.chat import ws_router as chat_routes
|
from .api.tabs.manage.chat import ws_router as chat_routes
|
||||||
from .api.tabs.plugin_manage import router as plugin_router
|
from .api.tabs.plugin_manage import router as plugin_router
|
||||||
|
from .api.tabs.plugin_manage.store import router as store_router
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="WebUi",
|
name="WebUi",
|
||||||
@ -71,6 +73,8 @@ BaseApiRouter = APIRouter(prefix="/zhenxun/api")
|
|||||||
|
|
||||||
|
|
||||||
BaseApiRouter.include_router(auth_router)
|
BaseApiRouter.include_router(auth_router)
|
||||||
|
BaseApiRouter.include_router(store_router)
|
||||||
|
BaseApiRouter.include_router(dashboard_router)
|
||||||
BaseApiRouter.include_router(main_router)
|
BaseApiRouter.include_router(main_router)
|
||||||
BaseApiRouter.include_router(manage_router)
|
BaseApiRouter.include_router(manage_router)
|
||||||
BaseApiRouter.include_router(database_router)
|
BaseApiRouter.include_router(database_router)
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
from .tabs import *
|
from .tabs import * # noqa: F403
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
from .logs import *
|
from .logs import * # noqa: F403
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Awaitable, Callable, Generic, TypeVar
|
from typing import Generic, TypeVar
|
||||||
|
from collections.abc import Callable, Awaitable
|
||||||
PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))"
|
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
LogListener = Callable[[_T], Awaitable[None]]
|
LogListener = Callable[[_T], Awaitable[None]]
|
||||||
@ -22,14 +21,13 @@ class LogStorage(Generic[_T]):
|
|||||||
self.logs[seq] = log
|
self.logs[seq] = log
|
||||||
asyncio.get_running_loop().call_later(self.rotation, self.remove, seq)
|
asyncio.get_running_loop().call_later(self.rotation, self.remove, seq)
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*map(lambda listener: listener(log), self.listeners),
|
*(listener(log) for listener in self.listeners),
|
||||||
return_exceptions=True,
|
return_exceptions=True,
|
||||||
)
|
)
|
||||||
return seq
|
return seq
|
||||||
|
|
||||||
def remove(self, seq: int):
|
def remove(self, seq: int):
|
||||||
del self.logs[seq]
|
del self.logs[seq]
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
LOG_STORAGE: LogStorage[str] = LogStorage[str]()
|
LOG_STORAGE: LogStorage[str] = LogStorage[str]()
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from fastapi import APIRouter, WebSocket
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from fastapi import APIRouter
|
||||||
from nonebot.utils import escape_tag
|
from nonebot.utils import escape_tag
|
||||||
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
|
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
|
||||||
|
|
||||||
from .log_manager import LOG_STORAGE
|
from .log_manager import LOG_STORAGE
|
||||||
|
|
||||||
@ -27,4 +27,3 @@ async def system_logs_realtime(websocket: WebSocket):
|
|||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
LOG_STORAGE.listeners.remove(log_listener)
|
LOG_STORAGE.listeners.remove(log_listener)
|
||||||
return
|
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
from nonebot import require
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from ....base_model import Result
|
||||||
|
from .data_source import BotManage
|
||||||
|
from ....utils import authentication
|
||||||
|
|
||||||
|
require("plugin_store")
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/dashboard")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/get_bot_list",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="获取bot列表", # type: ignore
|
||||||
|
)
|
||||||
|
async def _() -> Result:
|
||||||
|
try:
|
||||||
|
return Result.ok(await BotManage.get_bot_list(), "拿到信息啦!")
|
||||||
|
except Exception as e:
|
||||||
|
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import nonebot
|
||||||
|
from nonebot.adapters import Bot
|
||||||
|
from nonebot.drivers import Driver
|
||||||
|
|
||||||
|
from zhenxun.models.statistics import Statistics
|
||||||
|
from zhenxun.utils.platform import PlatformUtils
|
||||||
|
from zhenxun.models.chat_history import ChatHistory
|
||||||
|
|
||||||
|
from .model import BotInfo
|
||||||
|
from ..main.data_source import bot_live
|
||||||
|
|
||||||
|
driver: Driver = nonebot.get_driver()
|
||||||
|
|
||||||
|
|
||||||
|
CONNECT_TIME = 0
|
||||||
|
|
||||||
|
|
||||||
|
@driver.on_startup
|
||||||
|
async def _():
|
||||||
|
global CONNECT_TIME
|
||||||
|
CONNECT_TIME = int(time.time())
|
||||||
|
|
||||||
|
|
||||||
|
class BotManage:
|
||||||
|
@classmethod
|
||||||
|
async def __build_bot_info(cls, bot: Bot) -> BotInfo:
|
||||||
|
"""构建Bot信息
|
||||||
|
|
||||||
|
参数:
|
||||||
|
bot: Bot
|
||||||
|
|
||||||
|
返回:
|
||||||
|
BotInfo: Bot信息
|
||||||
|
"""
|
||||||
|
now = datetime.now()
|
||||||
|
platform = PlatformUtils.get_platform(bot) or ""
|
||||||
|
if platform == "qq":
|
||||||
|
login_info = await bot.get_login_info()
|
||||||
|
nickname = login_info["nickname"]
|
||||||
|
ava_url = PlatformUtils.get_user_avatar_url(bot.self_id, "qq") or ""
|
||||||
|
else:
|
||||||
|
nickname = bot.self_id
|
||||||
|
ava_url = ""
|
||||||
|
bot_info = BotInfo(
|
||||||
|
self_id=bot.self_id, nickname=nickname, ava_url=ava_url, platform=platform
|
||||||
|
)
|
||||||
|
group_list, _ = await PlatformUtils.get_group_list(bot)
|
||||||
|
group_list = [g for g in group_list if g.channel_id is None]
|
||||||
|
friend_list = await PlatformUtils.get_friend_list(bot)
|
||||||
|
bot_info.group_count = len(group_list)
|
||||||
|
bot_info.friend_count = len(friend_list)
|
||||||
|
bot_info.day_call = await Statistics.filter(
|
||||||
|
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||||
|
).count()
|
||||||
|
bot_info.received_messages = await ChatHistory.filter(
|
||||||
|
bot_id=bot_info.self_id,
|
||||||
|
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute),
|
||||||
|
).count()
|
||||||
|
bot_info.connect_time = bot_live.get(bot.self_id) or 0
|
||||||
|
if bot_info.connect_time:
|
||||||
|
connect_date = datetime.fromtimestamp(CONNECT_TIME)
|
||||||
|
connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
bot_info.connect_date = datetime.strptime(
|
||||||
|
connect_date_str, "%Y-%m-%d %H:%M:%S"
|
||||||
|
)
|
||||||
|
return bot_info
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_bot_list(cls) -> list[BotInfo]:
|
||||||
|
"""获取bot列表
|
||||||
|
|
||||||
|
返回:
|
||||||
|
list[BotInfo]: Bot列表
|
||||||
|
"""
|
||||||
|
bot_list: list[BotInfo] = []
|
||||||
|
for _, bot in nonebot.get_bots().items():
|
||||||
|
bot_list.append(await cls.__build_bot_info(bot))
|
||||||
|
return bot_list
|
||||||
26
zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py
Normal file
26
zhenxun/builtin_plugins/web_ui/api/tabs/dashboard/model.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class BotInfo(BaseModel):
|
||||||
|
self_id: str
|
||||||
|
"""SELF ID"""
|
||||||
|
nickname: str
|
||||||
|
"""昵称"""
|
||||||
|
ava_url: str
|
||||||
|
"""头像url"""
|
||||||
|
platform: str
|
||||||
|
"""平台"""
|
||||||
|
friend_count: int = 0
|
||||||
|
"""好友数量"""
|
||||||
|
group_count: int = 0
|
||||||
|
"""群聊数量"""
|
||||||
|
received_messages: int = 0
|
||||||
|
"""今日消息接收"""
|
||||||
|
day_call: int = 0
|
||||||
|
"""今日调用插件次数"""
|
||||||
|
connect_time: int = 0
|
||||||
|
"""连接时间"""
|
||||||
|
connect_date: datetime | None = None
|
||||||
|
"""连接日期"""
|
||||||
@ -4,7 +4,6 @@ from zhenxun.services.db_context import Model
|
|||||||
|
|
||||||
|
|
||||||
class SqlLog(Model):
|
class SqlLog(Model):
|
||||||
|
|
||||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||||
"""自增id"""
|
"""自增id"""
|
||||||
ip = fields.CharField(255)
|
ip = fields.CharField(255)
|
||||||
@ -18,7 +17,7 @@ class SqlLog(Model):
|
|||||||
create_time = fields.DatetimeField(auto_now_add=True)
|
create_time = fields.DatetimeField(auto_now_add=True)
|
||||||
"""创建时间"""
|
"""创建时间"""
|
||||||
|
|
||||||
class Meta:
|
class Meta: # type: ignore
|
||||||
table = "sql_log"
|
table = "sql_log"
|
||||||
table_description = "sql执行日志"
|
table_description = "sql执行日志"
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +1,27 @@
|
|||||||
import asyncio
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta
|
import asyncio
|
||||||
|
import contextlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
from fastapi import APIRouter, WebSocket
|
from fastapi import APIRouter
|
||||||
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
|
|
||||||
from tortoise.functions import Count
|
from tortoise.functions import Count
|
||||||
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
|
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
||||||
|
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
|
||||||
|
|
||||||
from zhenxun.models.chat_history import ChatHistory
|
|
||||||
from zhenxun.models.group_info import GroupInfo
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
|
||||||
from zhenxun.models.statistics import Statistics
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
from zhenxun.models.group_info import GroupInfo
|
||||||
|
from zhenxun.models.statistics import Statistics
|
||||||
from zhenxun.utils.platform import PlatformUtils
|
from zhenxun.utils.platform import PlatformUtils
|
||||||
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.models.chat_history import ChatHistory
|
||||||
|
|
||||||
from ....base_model import Result
|
from ....base_model import Result
|
||||||
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
|
|
||||||
from ....utils import authentication, get_system_status
|
|
||||||
from .data_source import bot_live
|
from .data_source import bot_live
|
||||||
from .model import ActiveGroup, BaseInfo, ChatHistoryCount, HotPlugin
|
from ....utils import authentication, get_system_status
|
||||||
|
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
|
||||||
|
from .model import BaseInfo, HotPlugin, ActiveGroup, ChatHistoryCount
|
||||||
|
|
||||||
run_time = time.time()
|
run_time = time.time()
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ async def _(bot_id: str) -> Result:
|
|||||||
"/get_ch_count", dependencies=[authentication()], description="获取接收消息数量"
|
"/get_ch_count", dependencies=[authentication()], description="获取接收消息数量"
|
||||||
)
|
)
|
||||||
async def _(bot_id: str, query_type: QueryDateType | None = None) -> Result:
|
async def _(bot_id: str, query_type: QueryDateType | None = None) -> Result:
|
||||||
if bots := nonebot.get_bots():
|
if nonebot.get_bot(bot_id):
|
||||||
if not query_type:
|
if not query_type:
|
||||||
return Result.ok(await ChatHistory.filter(bot_id=bot_id).count())
|
return Result.ok(await ChatHistory.filter(bot_id=bot_id).count())
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
@ -210,7 +211,6 @@ async def _(date_type: QueryDateType | None = None) -> Result:
|
|||||||
.limit(5)
|
.limit(5)
|
||||||
.values_list("group_id", "count")
|
.values_list("group_id", "count")
|
||||||
)
|
)
|
||||||
active_group_list = []
|
|
||||||
id2name = {}
|
id2name = {}
|
||||||
if data_list:
|
if data_list:
|
||||||
if info_list := await GroupInfo.filter(
|
if info_list := await GroupInfo.filter(
|
||||||
@ -218,15 +218,15 @@ async def _(date_type: QueryDateType | None = None) -> Result:
|
|||||||
).all():
|
).all():
|
||||||
for group_info in info_list:
|
for group_info in info_list:
|
||||||
id2name[group_info.group_id] = group_info.group_name
|
id2name[group_info.group_id] = group_info.group_name
|
||||||
for data in data_list:
|
active_group_list = [
|
||||||
active_group_list.append(
|
ActiveGroup(
|
||||||
ActiveGroup(
|
group_id=data[0],
|
||||||
group_id=data[0],
|
name=id2name.get(data[0]) or data[0],
|
||||||
name=id2name.get(data[0]) or data[0],
|
chat_num=data[1],
|
||||||
chat_num=data[1],
|
ava_img=GROUP_AVA_URL.format(data[0], data[0]),
|
||||||
ava_img=GROUP_AVA_URL.format(data[0], data[0]),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
for data in data_list
|
||||||
|
]
|
||||||
active_group_list = sorted(
|
active_group_list = sorted(
|
||||||
active_group_list, key=lambda x: x.chat_num, reverse=True
|
active_group_list, key=lambda x: x.chat_num, reverse=True
|
||||||
)
|
)
|
||||||
@ -263,13 +263,7 @@ async def _(date_type: QueryDateType | None = None) -> Result:
|
|||||||
for data in data_list:
|
for data in data_list:
|
||||||
module = data[0]
|
module = data[0]
|
||||||
name = module2name.get(module) or module
|
name = module2name.get(module) or module
|
||||||
hot_plugin_list.append(
|
hot_plugin_list.append(HotPlugin(module=module, name=name, count=data[1]))
|
||||||
HotPlugin(
|
|
||||||
module=data[0],
|
|
||||||
name=name,
|
|
||||||
count=data[1],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
hot_plugin_list = sorted(hot_plugin_list, key=lambda x: x.count, reverse=True)
|
hot_plugin_list = sorted(hot_plugin_list, key=lambda x: x.count, reverse=True)
|
||||||
if len(hot_plugin_list) > 5:
|
if len(hot_plugin_list) > 5:
|
||||||
hot_plugin_list = hot_plugin_list[:5]
|
hot_plugin_list = hot_plugin_list[:5]
|
||||||
@ -280,11 +274,11 @@ async def _(date_type: QueryDateType | None = None) -> Result:
|
|||||||
async def system_logs_realtime(websocket: WebSocket, sleep: int = 5):
|
async def system_logs_realtime(websocket: WebSocket, sleep: int = 5):
|
||||||
await websocket.accept()
|
await websocket.accept()
|
||||||
logger.debug("ws system_status is connect")
|
logger.debug("ws system_status is connect")
|
||||||
try:
|
with contextlib.suppress(
|
||||||
|
WebSocketDisconnect, ConnectionClosedError, ConnectionClosedOK
|
||||||
|
):
|
||||||
while websocket.client_state == WebSocketState.CONNECTED:
|
while websocket.client_state == WebSocketState.CONNECTED:
|
||||||
system_status = await get_system_status()
|
system_status = await get_system_status()
|
||||||
await websocket.send_text(system_status.json())
|
await websocket.send_text(system_status.json())
|
||||||
await asyncio.sleep(sleep)
|
await asyncio.sleep(sleep)
|
||||||
except (WebSocketDisconnect, ConnectionClosedError, ConnectionClosedOK):
|
|
||||||
pass
|
|
||||||
return
|
return
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
|
||||||
from tortoise.functions import Count
|
from tortoise.functions import Count
|
||||||
|
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||||
|
|
||||||
from zhenxun.configs.config import BotConfig
|
|
||||||
from zhenxun.models.ban_console import BanConsole
|
|
||||||
from zhenxun.models.chat_history import ChatHistory
|
|
||||||
from zhenxun.models.fg_request import FgRequest
|
|
||||||
from zhenxun.models.group_console import GroupConsole
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo
|
|
||||||
from zhenxun.models.statistics import Statistics
|
|
||||||
from zhenxun.models.task_info import TaskInfo
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.enum import RequestHandleType, RequestType
|
from zhenxun.configs.config import BotConfig
|
||||||
from zhenxun.utils.exception import NotFoundError
|
from zhenxun.models.task_info import TaskInfo
|
||||||
|
from zhenxun.models.fg_request import FgRequest
|
||||||
|
from zhenxun.models.statistics import Statistics
|
||||||
from zhenxun.utils.platform import PlatformUtils
|
from zhenxun.utils.platform import PlatformUtils
|
||||||
|
from zhenxun.models.ban_console import BanConsole
|
||||||
|
from zhenxun.models.plugin_info import PluginInfo
|
||||||
|
from zhenxun.utils.exception import NotFoundError
|
||||||
|
from zhenxun.models.chat_history import ChatHistory
|
||||||
|
from zhenxun.models.group_console import GroupConsole
|
||||||
|
from zhenxun.utils.enum import RequestType, RequestHandleType
|
||||||
|
|
||||||
from ....base_model import Result
|
from ....base_model import Result
|
||||||
from ....config import AVA_URL, GROUP_AVA_URL
|
|
||||||
from ....utils import authentication
|
from ....utils import authentication
|
||||||
|
from ....config import AVA_URL, GROUP_AVA_URL
|
||||||
from .model import (
|
from .model import (
|
||||||
ClearRequest,
|
Task,
|
||||||
DeleteFriend,
|
|
||||||
Friend,
|
Friend,
|
||||||
FriendRequestResult,
|
|
||||||
GroupDetail,
|
|
||||||
GroupRequestResult,
|
|
||||||
GroupResult,
|
|
||||||
HandleRequest,
|
|
||||||
LeaveGroup,
|
|
||||||
Plugin,
|
Plugin,
|
||||||
ReqResult,
|
ReqResult,
|
||||||
SendMessage,
|
LeaveGroup,
|
||||||
Task,
|
|
||||||
UpdateGroup,
|
|
||||||
UserDetail,
|
UserDetail,
|
||||||
|
GroupDetail,
|
||||||
|
GroupResult,
|
||||||
|
SendMessage,
|
||||||
|
UpdateGroup,
|
||||||
|
ClearRequest,
|
||||||
|
DeleteFriend,
|
||||||
|
HandleRequest,
|
||||||
|
GroupRequestResult,
|
||||||
|
FriendRequestResult,
|
||||||
)
|
)
|
||||||
|
|
||||||
router = APIRouter(prefix="/manage")
|
router = APIRouter(prefix="/manage")
|
||||||
@ -47,21 +47,21 @@ async def _(bot_id: str) -> Result:
|
|||||||
"""
|
"""
|
||||||
获取群信息
|
获取群信息
|
||||||
"""
|
"""
|
||||||
if bots := nonebot.get_bots():
|
if not (bots := nonebot.get_bots()):
|
||||||
if bot_id not in bots:
|
return Result.warning_("无Bot连接...")
|
||||||
return Result.warning_("指定Bot未连接...")
|
if bot_id not in bots:
|
||||||
group_list_result = []
|
return Result.warning_("指定Bot未连接...")
|
||||||
try:
|
group_list_result = []
|
||||||
group_list = await bots[bot_id].get_group_list()
|
try:
|
||||||
for g in group_list:
|
group_list = await bots[bot_id].get_group_list()
|
||||||
gid = g["group_id"]
|
for g in group_list:
|
||||||
g["ava_url"] = GROUP_AVA_URL.format(gid, gid)
|
gid = g["group_id"]
|
||||||
group_list_result.append(GroupResult(**g))
|
g["ava_url"] = GROUP_AVA_URL.format(gid, gid)
|
||||||
except Exception as e:
|
group_list_result.append(GroupResult(**g))
|
||||||
logger.error("调用API错误", "/get_group_list", e=e)
|
except Exception as e:
|
||||||
return Result.fail(f"{type(e)}: {e}")
|
logger.error("调用API错误", "/get_group_list", e=e)
|
||||||
return Result.ok(group_list_result, "拿到了新鲜出炉的数据!")
|
return Result.fail(f"{type(e)}: {e}")
|
||||||
return Result.warning_("无Bot连接...")
|
return Result.ok(group_list_result, "拿到了新鲜出炉的数据!")
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
@ -77,12 +77,8 @@ async def _(group: UpdateGroup) -> Result:
|
|||||||
if group.close_plugins:
|
if group.close_plugins:
|
||||||
db_group.block_plugin = ",".join(group.close_plugins) + ","
|
db_group.block_plugin = ",".join(group.close_plugins) + ","
|
||||||
if group.task:
|
if group.task:
|
||||||
block_task = []
|
if block_task := [t for t in task_list if t not in group.task]:
|
||||||
for t in task_list:
|
db_group.block_task = ",".join(block_task) + "," # type: ignore
|
||||||
if t not in group.task:
|
|
||||||
block_task.append(t)
|
|
||||||
if block_task:
|
|
||||||
db_group.block_task = ",".join(block_task) + ","
|
|
||||||
await db_group.save(
|
await db_group.save(
|
||||||
update_fields=["level", "status", "block_plugin", "block_task"]
|
update_fields=["level", "status", "block_plugin", "block_task"]
|
||||||
)
|
)
|
||||||
@ -199,7 +195,7 @@ async def _(parma: HandleRequest) -> Result:
|
|||||||
return Result.warning_("指定Bot未连接...")
|
return Result.warning_("指定Bot未连接...")
|
||||||
try:
|
try:
|
||||||
await FgRequest.refused(bots[bot_id], parma.id)
|
await FgRequest.refused(bots[bot_id], parma.id)
|
||||||
except ActionFailed as e:
|
except ActionFailed:
|
||||||
await FgRequest.expire(parma.id)
|
await FgRequest.expire(parma.id)
|
||||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
@ -226,22 +222,21 @@ async def _(parma: HandleRequest) -> Result:
|
|||||||
bot_id = parma.bot_id
|
bot_id = parma.bot_id
|
||||||
if bot_id not in nonebot.get_bots():
|
if bot_id not in nonebot.get_bots():
|
||||||
return Result.warning_("指定Bot未连接...")
|
return Result.warning_("指定Bot未连接...")
|
||||||
if req := await FgRequest.get_or_none(id=parma.id):
|
if not (req := await FgRequest.get_or_none(id=parma.id)):
|
||||||
if req.request_type == RequestType.GROUP:
|
|
||||||
if group := await GroupConsole.get_group(group_id=req.group_id):
|
|
||||||
group.group_flag = 1
|
|
||||||
await group.save(update_fields=["group_flag"])
|
|
||||||
else:
|
|
||||||
await GroupConsole.update_or_create(
|
|
||||||
group_id=req.group_id,
|
|
||||||
defaults={"group_flag": 1},
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return Result.warning_("未找到此Id请求...")
|
return Result.warning_("未找到此Id请求...")
|
||||||
|
if req.request_type == RequestType.GROUP:
|
||||||
|
if group := await GroupConsole.get_group(group_id=req.group_id):
|
||||||
|
group.group_flag = 1
|
||||||
|
await group.save(update_fields=["group_flag"])
|
||||||
|
else:
|
||||||
|
await GroupConsole.update_or_create(
|
||||||
|
group_id=req.group_id,
|
||||||
|
defaults={"group_flag": 1},
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
await FgRequest.approve(bots[bot_id], parma.id)
|
await FgRequest.approve(bots[bot_id], parma.id)
|
||||||
return Result.ok(info="成功处理了请求!")
|
return Result.ok(info="成功处理了请求!")
|
||||||
except ActionFailed as e:
|
except ActionFailed:
|
||||||
await FgRequest.expire(parma.id)
|
await FgRequest.expire(parma.id)
|
||||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||||
return Result.warning_("无Bot连接...")
|
return Result.warning_("无Bot连接...")
|
||||||
@ -335,99 +330,98 @@ async def _(bot_id: str, user_id: str) -> Result:
|
|||||||
"/get_group_detail", dependencies=[authentication()], description="获取群组详情"
|
"/get_group_detail", dependencies=[authentication()], description="获取群组详情"
|
||||||
)
|
)
|
||||||
async def _(bot_id: str, group_id: str) -> Result:
|
async def _(bot_id: str, group_id: str) -> Result:
|
||||||
if bots := nonebot.get_bots():
|
if not (bots := nonebot.get_bots()):
|
||||||
if bot_id in bots:
|
return Result.warning_("无Bot连接...")
|
||||||
group = await GroupConsole.get_or_none(group_id=group_id)
|
if bot_id not in bots:
|
||||||
if not group:
|
return Result.warning_("未添加指定群组...")
|
||||||
return Result.warning_("指定群组未被收录...")
|
group = await GroupConsole.get_or_none(group_id=group_id)
|
||||||
like_plugin_list = (
|
if not group:
|
||||||
await Statistics.filter(group_id=group_id)
|
return Result.warning_("指定群组未被收录...")
|
||||||
.annotate(count=Count("id"))
|
like_plugin_list = (
|
||||||
.group_by("plugin_name")
|
await Statistics.filter(group_id=group_id)
|
||||||
.order_by("-count")
|
.annotate(count=Count("id"))
|
||||||
.limit(5)
|
.group_by("plugin_name")
|
||||||
.values_list("plugin_name", "count")
|
.order_by("-count")
|
||||||
|
.limit(5)
|
||||||
|
.values_list("plugin_name", "count")
|
||||||
|
)
|
||||||
|
like_plugin = {}
|
||||||
|
plugins = await PluginInfo.all()
|
||||||
|
module2name = {p.module: p.name for p in plugins}
|
||||||
|
for data in like_plugin_list:
|
||||||
|
name = module2name.get(data[0]) or data[0]
|
||||||
|
like_plugin[name] = data[1]
|
||||||
|
close_plugins = []
|
||||||
|
if group.block_plugin:
|
||||||
|
for module in group.block_plugin.split(","):
|
||||||
|
module_ = module.replace(":super", "")
|
||||||
|
is_super_block = module.endswith(":super")
|
||||||
|
plugin = Plugin(
|
||||||
|
module=module_,
|
||||||
|
plugin_name=module,
|
||||||
|
is_super_block=is_super_block,
|
||||||
)
|
)
|
||||||
like_plugin = {}
|
plugin.plugin_name = module2name.get(module) or module
|
||||||
plugins = await PluginInfo.all()
|
close_plugins.append(plugin)
|
||||||
module2name = {p.module: p.name for p in plugins}
|
all_task = await TaskInfo.annotate().values_list("module", "name")
|
||||||
for data in like_plugin_list:
|
task_module2name = {x[0]: x[1] for x in all_task}
|
||||||
name = module2name.get(data[0]) or data[0]
|
task_list = []
|
||||||
like_plugin[name] = data[1]
|
if group.block_task:
|
||||||
close_plugins = []
|
split_task = group.block_task.split(",")
|
||||||
if group.block_plugin:
|
for task in all_task:
|
||||||
for module in group.block_plugin.split(","):
|
task_list.append(
|
||||||
module_ = module.replace(":super", "")
|
Task(
|
||||||
is_super_block = module.endswith(":super")
|
name=task[0],
|
||||||
plugin = Plugin(
|
zh_name=task_module2name.get(task[0]) or task[0],
|
||||||
module=module_,
|
status=task[0] not in split_task,
|
||||||
plugin_name=module,
|
)
|
||||||
is_super_block=is_super_block,
|
|
||||||
)
|
|
||||||
plugin.plugin_name = module2name.get(module) or module
|
|
||||||
close_plugins.append(plugin)
|
|
||||||
all_task = await TaskInfo.annotate().values_list("module", "name")
|
|
||||||
task_module2name = {x[0]: x[1] for x in all_task}
|
|
||||||
task_list = []
|
|
||||||
if group.block_task:
|
|
||||||
split_task = group.block_task.split(",")
|
|
||||||
for task in all_task:
|
|
||||||
task_list.append(
|
|
||||||
Task(
|
|
||||||
name=task[0],
|
|
||||||
zh_name=task_module2name.get(task[0]) or task[0],
|
|
||||||
status=task[0] not in split_task,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for task in all_task:
|
|
||||||
task_list.append(
|
|
||||||
Task(
|
|
||||||
name=task[0],
|
|
||||||
zh_name=task_module2name.get(task[0]) or task[0],
|
|
||||||
status=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
group_detail = GroupDetail(
|
|
||||||
group_id=group_id,
|
|
||||||
ava_url=GROUP_AVA_URL.format(group_id, group_id),
|
|
||||||
name=group.group_name,
|
|
||||||
member_count=group.member_count,
|
|
||||||
max_member_count=group.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=group.level,
|
|
||||||
status=group.status,
|
|
||||||
close_plugins=close_plugins,
|
|
||||||
task=task_list,
|
|
||||||
)
|
)
|
||||||
return Result.ok(group_detail)
|
else:
|
||||||
else:
|
for task in all_task:
|
||||||
return Result.warning_("未添加指定群组...")
|
task_list.append(
|
||||||
return Result.warning_("无Bot连接...")
|
Task(
|
||||||
|
name=task[0],
|
||||||
|
zh_name=task_module2name.get(task[0]) or task[0],
|
||||||
|
status=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
group_detail = GroupDetail(
|
||||||
|
group_id=group_id,
|
||||||
|
ava_url=GROUP_AVA_URL.format(group_id, group_id),
|
||||||
|
name=group.group_name,
|
||||||
|
member_count=group.member_count,
|
||||||
|
max_member_count=group.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=group.level,
|
||||||
|
status=group.status,
|
||||||
|
close_plugins=close_plugins,
|
||||||
|
task=task_list,
|
||||||
|
)
|
||||||
|
return Result.ok(group_detail)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/send_message", dependencies=[authentication()], description="获取群组详情"
|
"/send_message", dependencies=[authentication()], description="获取群组详情"
|
||||||
)
|
)
|
||||||
async def _(param: SendMessage) -> Result:
|
async def _(param: SendMessage) -> Result:
|
||||||
if bots := nonebot.get_bots():
|
if not (bots := nonebot.get_bots()):
|
||||||
if param.bot_id in bots:
|
return Result.warning_("无Bot连接...")
|
||||||
platform = PlatformUtils.get_platform(bots[param.bot_id])
|
if param.bot_id in bots:
|
||||||
if platform != "qq":
|
platform = PlatformUtils.get_platform(bots[param.bot_id])
|
||||||
return Result.warning_("暂不支持该平台...")
|
if platform != "qq":
|
||||||
try:
|
return Result.warning_("暂不支持该平台...")
|
||||||
if param.user_id:
|
try:
|
||||||
await bots[param.bot_id].send_private_msg(
|
if param.user_id:
|
||||||
user_id=str(param.user_id), message=param.message
|
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(
|
else:
|
||||||
group_id=str(param.group_id), message=param.message
|
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))
|
except Exception as e:
|
||||||
return Result.ok("发送成功!")
|
return Result.fail(str(e))
|
||||||
return Result.warning_("指定Bot未连接...")
|
return Result.ok("发送成功!")
|
||||||
return Result.warning_("无Bot连接...")
|
return Result.warning_("指定Bot未连接...")
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
from typing import Literal
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from zhenxun.utils.enum import RequestType
|
from zhenxun.utils.enum import RequestType
|
||||||
@ -232,7 +230,6 @@ class GroupDetail(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class MessageItem(BaseModel):
|
class MessageItem(BaseModel):
|
||||||
|
|
||||||
type: str
|
type: str
|
||||||
"""消息类型"""
|
"""消息类型"""
|
||||||
msg: str
|
msg: str
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import cattrs
|
import cattrs
|
||||||
from fastapi import APIRouter, Query
|
from fastapi import Query, APIRouter
|
||||||
|
|
||||||
from zhenxun.configs.config import Config
|
|
||||||
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
from zhenxun.configs.config import Config
|
||||||
from zhenxun.utils.enum import BlockType, PluginType
|
from zhenxun.utils.enum import BlockType, PluginType
|
||||||
|
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
||||||
|
|
||||||
from ....base_model import Result
|
from ....base_model import Result
|
||||||
from ....utils import authentication
|
from ....utils import authentication
|
||||||
from .model import (
|
from .model import (
|
||||||
PluginConfig,
|
|
||||||
PluginCount,
|
|
||||||
PluginDetail,
|
|
||||||
PluginInfo,
|
PluginInfo,
|
||||||
|
PluginCount,
|
||||||
|
PluginConfig,
|
||||||
|
PluginDetail,
|
||||||
PluginSwitch,
|
PluginSwitch,
|
||||||
UpdatePlugin,
|
UpdatePlugin,
|
||||||
)
|
)
|
||||||
@ -23,7 +23,9 @@ router = APIRouter(prefix="/plugin")
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/get_plugin_list", dependencies=[authentication()], deprecated="获取插件列表" # type: ignore
|
"/get_plugin_list",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="获取插件列表", # type: ignore
|
||||||
)
|
)
|
||||||
async def _(
|
async def _(
|
||||||
plugin_type: list[PluginType] = Query(None), menu_type: str | None = None
|
plugin_type: list[PluginType] = Query(None), menu_type: str | None = None
|
||||||
@ -57,7 +59,9 @@ async def _(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/get_plugin_count", dependencies=[authentication()], deprecated="获取插件数量" # type: ignore
|
"/get_plugin_count",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="获取插件数量", # type: ignore
|
||||||
)
|
)
|
||||||
async def _() -> Result:
|
async def _() -> Result:
|
||||||
plugin_count = PluginCount()
|
plugin_count = PluginCount()
|
||||||
@ -93,10 +97,7 @@ async def _(plugin: UpdatePlugin) -> Result:
|
|||||||
db_plugin.level = plugin.level
|
db_plugin.level = plugin.level
|
||||||
db_plugin.menu_type = plugin.menu_type
|
db_plugin.menu_type = plugin.menu_type
|
||||||
db_plugin.block_type = plugin.block_type
|
db_plugin.block_type = plugin.block_type
|
||||||
if plugin.block_type == BlockType.ALL:
|
db_plugin.status = plugin.block_type != BlockType.ALL
|
||||||
db_plugin.status = False
|
|
||||||
else:
|
|
||||||
db_plugin.status = True
|
|
||||||
await db_plugin.save()
|
await db_plugin.save()
|
||||||
# 配置项
|
# 配置项
|
||||||
if plugin.configs and (configs := Config.get(plugin.module)):
|
if plugin.configs and (configs := Config.get(plugin.module)):
|
||||||
@ -149,19 +150,15 @@ async def _(module: str) -> Result:
|
|||||||
for cfg in config.configs:
|
for cfg in config.configs:
|
||||||
type_str = ""
|
type_str = ""
|
||||||
type_inner = None
|
type_inner = None
|
||||||
x = str(config.configs[cfg].type)
|
if r := re.search(r"<class '(.*)'>", str(config.configs[cfg].type)):
|
||||||
r = re.search(r"<class '(.*)'>", str(config.configs[cfg].type))
|
type_str = r[1]
|
||||||
if r:
|
elif r := re.search(r"typing\.(.*)\[(.*)\]", str(config.configs[cfg].type)):
|
||||||
type_str = r.group(1)
|
type_str = r[1]
|
||||||
else:
|
if type_str:
|
||||||
r = re.search(r"typing\.(.*)\[(.*)\]", str(config.configs[cfg].type))
|
type_str = type_str.lower()
|
||||||
if r:
|
type_inner = r[2]
|
||||||
type_str = r.group(1)
|
if type_inner:
|
||||||
if type_str:
|
type_inner = [x.strip() for x in type_inner.split(",")]
|
||||||
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(
|
config_list.append(
|
||||||
PluginConfig(
|
PluginConfig(
|
||||||
module=module,
|
module=module,
|
||||||
|
|||||||
@ -123,3 +123,8 @@ class PluginDetail(PluginInfo):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
config_list: list[PluginConfig]
|
config_list: list[PluginConfig]
|
||||||
|
|
||||||
|
|
||||||
|
class PluginIr(BaseModel):
|
||||||
|
id: int
|
||||||
|
"""插件id"""
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
from nonebot import require
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from .model import PluginIr
|
||||||
|
from ....base_model import Result
|
||||||
|
from ....utils import authentication
|
||||||
|
|
||||||
|
require("plugin_store")
|
||||||
|
from zhenxun.builtin_plugins.plugin_store import ShopManage
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/store")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/get_plugin_store",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="获取插件商店插件信息", # type: ignore
|
||||||
|
)
|
||||||
|
async def _() -> Result:
|
||||||
|
try:
|
||||||
|
data = await ShopManage.get_data()
|
||||||
|
return Result.ok(data)
|
||||||
|
except Exception as e:
|
||||||
|
return Result.fail(f"获取插件商店插件信息失败: {type(e)}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/install_plugin",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="安装插件", # type: ignore
|
||||||
|
)
|
||||||
|
async def _(param: PluginIr) -> Result:
|
||||||
|
try:
|
||||||
|
result = await ShopManage.add_plugin(param.id) # type: ignore
|
||||||
|
return Result.ok(result)
|
||||||
|
except Exception as e:
|
||||||
|
return Result.fail(f"安装插件失败: {type(e)}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/remove_plugin",
|
||||||
|
dependencies=[authentication()],
|
||||||
|
deprecated="移除插件", # type: ignore
|
||||||
|
)
|
||||||
|
async def _(param: PluginIr) -> Result:
|
||||||
|
try:
|
||||||
|
result = await ShopManage.remove_plugin(param.id) # type: ignore
|
||||||
|
return Result.ok(result)
|
||||||
|
except Exception as e:
|
||||||
|
return Result.fail(f"移除插件失败: {type(e)}: {e}")
|
||||||
@ -1,15 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
|
import aiofiles
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from zhenxun.utils._build_image import BuildImage
|
from zhenxun.utils._build_image import BuildImage
|
||||||
|
|
||||||
from ....base_model import Result
|
from ....base_model import Result
|
||||||
from ....utils import authentication, get_system_disk
|
from ....utils import authentication, get_system_disk
|
||||||
from .model import AddFile, DeleteFile, DirFile, RenameFile, SaveFile
|
from .model import AddFile, DirFile, SaveFile, DeleteFile, RenameFile
|
||||||
|
|
||||||
router = APIRouter(prefix="/system")
|
router = APIRouter(prefix="/system")
|
||||||
|
|
||||||
@ -19,16 +19,12 @@ IMAGE_TYPE = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"]
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/get_dir_list", dependencies=[authentication()], description="获取文件列表"
|
"/get_dir_list", dependencies=[authentication()], description="获取文件列表"
|
||||||
)
|
)
|
||||||
async def _(path: Optional[str] = None) -> Result:
|
async def _(path: str | None = None) -> Result:
|
||||||
base_path = Path(path) if path else Path()
|
base_path = Path(path) if path else Path()
|
||||||
data_list = []
|
data_list = []
|
||||||
for file in os.listdir(base_path):
|
for file in os.listdir(base_path):
|
||||||
file_path = base_path / file
|
file_path = base_path / file
|
||||||
is_image = False
|
is_image = any(file.endswith(f".{t}") for t in IMAGE_TYPE)
|
||||||
for t in IMAGE_TYPE:
|
|
||||||
if file.endswith(f".{t}"):
|
|
||||||
is_image = True
|
|
||||||
break
|
|
||||||
data_list.append(
|
data_list.append(
|
||||||
DirFile(
|
DirFile(
|
||||||
is_file=not file_path.is_dir(),
|
is_file=not file_path.is_dir(),
|
||||||
@ -43,7 +39,7 @@ async def _(path: Optional[str] = None) -> Result:
|
|||||||
@router.get(
|
@router.get(
|
||||||
"/get_resources_size", dependencies=[authentication()], description="获取文件列表"
|
"/get_resources_size", dependencies=[authentication()], description="获取文件列表"
|
||||||
)
|
)
|
||||||
async def _(full_path: Optional[str] = None) -> Result:
|
async def _(full_path: str | None = None) -> Result:
|
||||||
return Result.ok(await get_system_disk(full_path))
|
return Result.ok(await get_system_disk(full_path))
|
||||||
|
|
||||||
|
|
||||||
@ -56,7 +52,7 @@ async def _(param: DeleteFile) -> Result:
|
|||||||
path.unlink()
|
path.unlink()
|
||||||
return Result.ok("删除成功!")
|
return Result.ok("删除成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("删除失败: " + str(e))
|
return Result.warning_(f"删除失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
@ -70,7 +66,7 @@ async def _(param: DeleteFile) -> Result:
|
|||||||
shutil.rmtree(path.absolute())
|
shutil.rmtree(path.absolute())
|
||||||
return Result.ok("删除成功!")
|
return Result.ok("删除成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("删除失败: " + str(e))
|
return Result.warning_(f"删除失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/rename_file", dependencies=[authentication()], description="重命名文件")
|
@router.post("/rename_file", dependencies=[authentication()], description="重命名文件")
|
||||||
@ -84,7 +80,7 @@ async def _(param: RenameFile) -> Result:
|
|||||||
path.rename(path.parent / param.name)
|
path.rename(path.parent / param.name)
|
||||||
return Result.ok("重命名成功!")
|
return Result.ok("重命名成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("重命名失败: " + str(e))
|
return Result.warning_(f"重命名失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
@ -101,7 +97,7 @@ async def _(param: RenameFile) -> Result:
|
|||||||
shutil.move(path.absolute(), new_path.absolute())
|
shutil.move(path.absolute(), new_path.absolute())
|
||||||
return Result.ok("重命名成功!")
|
return Result.ok("重命名成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("重命名失败: " + str(e))
|
return Result.warning_(f"重命名失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/add_file", dependencies=[authentication()], description="新建文件")
|
@router.post("/add_file", dependencies=[authentication()], description="新建文件")
|
||||||
@ -113,7 +109,7 @@ async def _(param: AddFile) -> Result:
|
|||||||
path.open("w")
|
path.open("w")
|
||||||
return Result.ok("新建文件成功!")
|
return Result.ok("新建文件成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("新建文件失败: " + str(e))
|
return Result.warning_(f"新建文件失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/add_folder", dependencies=[authentication()], description="新建文件夹")
|
@router.post("/add_folder", dependencies=[authentication()], description="新建文件夹")
|
||||||
@ -125,7 +121,7 @@ async def _(param: AddFile) -> Result:
|
|||||||
path.mkdir()
|
path.mkdir()
|
||||||
return Result.ok("新建文件夹成功!")
|
return Result.ok("新建文件夹成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("新建文件夹失败: " + str(e))
|
return Result.warning_(f"新建文件夹失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/read_file", dependencies=[authentication()], description="读取文件")
|
@router.get("/read_file", dependencies=[authentication()], description="读取文件")
|
||||||
@ -137,18 +133,18 @@ async def _(full_path: str) -> Result:
|
|||||||
text = path.read_text(encoding="utf-8")
|
text = path.read_text(encoding="utf-8")
|
||||||
return Result.ok(text)
|
return Result.ok(text)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("读取文件失败: " + str(e))
|
return Result.warning_(f"读取文件失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/save_file", dependencies=[authentication()], description="读取文件")
|
@router.post("/save_file", dependencies=[authentication()], description="读取文件")
|
||||||
async def _(param: SaveFile) -> Result:
|
async def _(param: SaveFile) -> Result:
|
||||||
path = Path(param.full_path)
|
path = Path(param.full_path)
|
||||||
try:
|
try:
|
||||||
with path.open("w") as f:
|
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
||||||
f.write(param.content)
|
await f.write(param.content)
|
||||||
return Result.ok("更新成功!")
|
return Result.ok("更新成功!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("保存文件失败: " + str(e))
|
return Result.warning_(f"保存文件失败: {e!s}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/get_image", dependencies=[authentication()], description="读取图片base64")
|
@router.get("/get_image", dependencies=[authentication()], description="读取图片base64")
|
||||||
@ -159,4 +155,4 @@ async def _(full_path: str) -> Result:
|
|||||||
try:
|
try:
|
||||||
return Result.ok(BuildImage.open(path).pic2bs4())
|
return Result.ok(BuildImage.open(path).pic2bs4())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Result.warning_("获取图片失败: " + str(e))
|
return Result.warning_(f"获取图片失败: {e!s}")
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Generic, Optional, TypeVar
|
from typing_extensions import Self
|
||||||
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ class Result(BaseModel):
|
|||||||
"""code"""
|
"""code"""
|
||||||
info: str = "操作成功"
|
info: str = "操作成功"
|
||||||
"""info"""
|
"""info"""
|
||||||
warning: Optional[str] = None
|
warning: str | None = None
|
||||||
"""警告信息"""
|
"""警告信息"""
|
||||||
data: Any = None
|
data: Any = None
|
||||||
"""返回数据"""
|
"""返回数据"""
|
||||||
@ -102,7 +102,7 @@ class SystemFolderSize(BaseModel):
|
|||||||
"""名称"""
|
"""名称"""
|
||||||
size: float
|
size: float
|
||||||
"""大小"""
|
"""大小"""
|
||||||
full_path: Optional[str]
|
full_path: str | None
|
||||||
"""完整路径"""
|
"""完整路径"""
|
||||||
is_dir: bool
|
is_dir: bool
|
||||||
"""是否为文件夹"""
|
"""是否为文件夹"""
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user