mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
重构webui适配 (#1801)
* ♻️ 使用Uninfo重构PlatformUtils基础方法 * 🩹 优化插件加载与模块格式转换逻辑 * 🚑 修复商店道具无法使用 * 🚑 修复道具无法正常使用 * 🔧 增加Bot状态管理及模块禁用功能 * 🎨 优化Web UI代码结构,修改target方法 * 🚨 auto fix by pre-commit hooks * 🎨 添加菜单API及优化异常处理 * 🐛 优化菜单API及模型结构,修复WebUi插件列表Api * 📝 更新仓库readme * 🚨 add mdlint file * 📝 Add help chapter. * 🐛 修复优化AuthChecker逻辑 * 🐛 优化数据库API,移除冗余导入及修正SQL_DICT引用 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: BalconyJH <balconyjh@gmail.com>
This commit is contained in:
co-authored by
pre-commit-ci[bot]
BalconyJH
parent
ebf05fd884
commit
35014e4048
@@ -2,20 +2,12 @@ from datetime import datetime
|
||||
import uuid
|
||||
|
||||
import nonebot
|
||||
from nonebot import require
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.drivers import Driver
|
||||
from tortoise import Tortoise
|
||||
from tortoise.exceptions import OperationalError
|
||||
import ujson as json
|
||||
|
||||
require("nonebot_plugin_apscheduler")
|
||||
require("nonebot_plugin_alconna")
|
||||
require("nonebot_plugin_session")
|
||||
require("nonebot_plugin_userinfo")
|
||||
require("nonebot_plugin_htmlrender")
|
||||
# require("nonebot_plugin_uninfo")
|
||||
|
||||
from zhenxun.models.bot_connect_log import BotConnectLog
|
||||
from zhenxun.models.bot_console import BotConsole
|
||||
from zhenxun.models.goods_info import GoodsInfo
|
||||
|
||||
@@ -387,7 +387,7 @@ class AuthChecker:
|
||||
session=session,
|
||||
)
|
||||
raise IgnoredException("该群未开启此功能...")
|
||||
if not plugin.status and plugin.block_type == BlockType.GROUP:
|
||||
if plugin.block_type == BlockType.GROUP:
|
||||
"""全局群组禁用"""
|
||||
try:
|
||||
if self.is_send_limit_message(plugin, sid) and not is_poke:
|
||||
@@ -410,7 +410,7 @@ class AuthChecker:
|
||||
raise IgnoredException("该插件在群组中已被禁用...")
|
||||
else:
|
||||
sid = user_id
|
||||
if not plugin.status and plugin.block_type == BlockType.PRIVATE:
|
||||
if plugin.block_type == BlockType.PRIVATE:
|
||||
"""全局私聊禁用"""
|
||||
try:
|
||||
if self.is_send_limit_message(plugin, sid) and not is_poke:
|
||||
|
||||
@@ -120,7 +120,7 @@ async def _():
|
||||
if module_list := await PluginInfo.all().values("id", "module_path"):
|
||||
module2id = {m["module_path"]: m["id"] for m in module_list}
|
||||
for plugin in get_loaded_plugins():
|
||||
load_plugin.append(plugin.name)
|
||||
load_plugin.append(plugin.module_name)
|
||||
await _handle_setting(plugin, plugin_list, limit_list, task_list)
|
||||
create_list = []
|
||||
update_list = []
|
||||
@@ -198,8 +198,8 @@ async def _():
|
||||
10,
|
||||
)
|
||||
await data_migration()
|
||||
await PluginInfo.filter(module__in=load_plugin).update(load_status=True)
|
||||
await PluginInfo.filter(module__not_in=load_plugin).update(load_status=False)
|
||||
await PluginInfo.filter(module_path__in=load_plugin).update(load_status=True)
|
||||
await PluginInfo.filter(module_path__not_in=load_plugin).update(load_status=False)
|
||||
manager.init()
|
||||
if limit_list:
|
||||
for limit in limit_list:
|
||||
|
||||
@@ -89,7 +89,7 @@ async def _(bot: Bot):
|
||||
async with aiofiles.open(RESTART_MARK, encoding="utf8") as f:
|
||||
bot_id, user_id = (await f.read()).split()
|
||||
if bot := nonebot.get_bot(bot_id):
|
||||
if target := PlatformUtils.get_target(bot, user_id):
|
||||
if target := PlatformUtils.get_target(user_id=user_id):
|
||||
await MessageUtils.build_message(
|
||||
f"{BotConfig.self_nickname}已成功重启!"
|
||||
).send(target, bot=bot)
|
||||
|
||||
@@ -37,8 +37,7 @@ async def _():
|
||||
update_list = []
|
||||
if modules := await TaskInfo.annotate().values_list("module", flat=True):
|
||||
for bot in nonebot.get_bots().values():
|
||||
group_list, _ = await PlatformUtils.get_group_list(bot)
|
||||
group_list = [g for g in group_list if g.channel_id is None]
|
||||
group_list, _ = await PlatformUtils.get_group_list(bot, True)
|
||||
for group in group_list:
|
||||
try:
|
||||
last_message = (
|
||||
|
||||
@@ -126,7 +126,9 @@ async def _(session: Uninfo, arparma: Arparma):
|
||||
async def _(session: Uninfo, arparma: Arparma, nickname: str = UserName()):
|
||||
logger.info("查看道具", arparma.header_result, session=session)
|
||||
if image := await ShopManage.my_props(
|
||||
session.user.id, nickname, PlatformUtils.get_platform(session)
|
||||
session.user.id,
|
||||
nickname,
|
||||
PlatformUtils.get_platform(session),
|
||||
):
|
||||
await MessageUtils.build_message(image.pic2bytes()).finish(reply_to=True)
|
||||
return await MessageUtils.build_message("你的道具为空捏...").send(reply_to=True)
|
||||
|
||||
@@ -47,7 +47,7 @@ class BroadcastManage:
|
||||
group.group_id,
|
||||
):
|
||||
target = PlatformUtils.get_target(
|
||||
bot, None, group.channel_id or group.group_id
|
||||
group_id=group.group_id, channel_id=group.channel_id
|
||||
)
|
||||
if target:
|
||||
await MessageUtils.build_message(message_list).send(
|
||||
|
||||
@@ -13,6 +13,7 @@ from zhenxun.utils.enum import PluginType
|
||||
|
||||
from .api.logs import router as ws_log_routes
|
||||
from .api.logs.log_manager import LOG_STORAGE
|
||||
from .api.menu import router as menu_router
|
||||
from .api.tabs.dashboard import router as dashboard_router
|
||||
from .api.tabs.database import router as database_router
|
||||
from .api.tabs.main import router as main_router
|
||||
@@ -80,6 +81,7 @@ BaseApiRouter.include_router(manage_router)
|
||||
BaseApiRouter.include_router(database_router)
|
||||
BaseApiRouter.include_router(plugin_router)
|
||||
BaseApiRouter.include_router(system_router)
|
||||
BaseApiRouter.include_router(menu_router)
|
||||
|
||||
|
||||
WsApiRouter = APIRouter(prefix="/zhenxun/socket")
|
||||
@@ -112,6 +114,6 @@ async def _():
|
||||
app.include_router(BaseApiRouter)
|
||||
app.include_router(WsApiRouter)
|
||||
await init_public(app)
|
||||
logger.info("<g>API启动成功</g>", "Web UI")
|
||||
logger.info("<g>API启动成功</g>", "WebUi")
|
||||
except Exception as e:
|
||||
logger.error("<g>API启动失败</g>", "Web UI", e=e)
|
||||
logger.error("<g>API启动失败</g>", "WebUi", e=e)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from .tabs import * # noqa: F403
|
||||
from .menu import * # noqa: F403f
|
||||
from .tabs import * # noqa: F403f
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from ...base_model import Result
|
||||
from ...utils import authentication
|
||||
from .data_source import menu_manage
|
||||
from .model import MenuData
|
||||
|
||||
router = APIRouter(prefix="/menu")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/get_menus",
|
||||
dependencies=[authentication()],
|
||||
response_model=Result[list[MenuData]],
|
||||
response_class=JSONResponse,
|
||||
deprecated="获取菜单列表", # type: ignore
|
||||
)
|
||||
async def _() -> Result[list[MenuData]]:
|
||||
try:
|
||||
return Result.ok(menu_manage.get_menus(), "拿到菜单了哦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_menus 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
@@ -0,0 +1,64 @@
|
||||
import ujson as json
|
||||
|
||||
from zhenxun.configs.path_config import DATA_PATH
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from .model import MenuData, MenuItem
|
||||
|
||||
|
||||
class MenuManage:
|
||||
def __init__(self) -> None:
|
||||
self.file = DATA_PATH / "web_ui" / "menu.json"
|
||||
self.menu = []
|
||||
if self.file.exists():
|
||||
try:
|
||||
self.menu = json.load(self.file.open(encoding="utf8"))
|
||||
except Exception as e:
|
||||
logger.warning("菜单文件损坏,已重新生成...", "WebUi", e=e)
|
||||
if not self.menu:
|
||||
self.menu = [
|
||||
MenuItem(
|
||||
name="仪表盘",
|
||||
module="dashboard",
|
||||
router="/dashboard",
|
||||
icon="dashboard",
|
||||
default=True,
|
||||
),
|
||||
MenuItem(
|
||||
name="真寻控制台",
|
||||
module="command",
|
||||
router="/command",
|
||||
icon="command",
|
||||
),
|
||||
MenuItem(
|
||||
name="插件列表", module="plugin", router="/plugin", icon="plugin"
|
||||
),
|
||||
MenuItem(
|
||||
name="插件商店", module="store", router="/store", icon="store"
|
||||
),
|
||||
MenuItem(
|
||||
name="好友/群组", module="manage", router="/manage", icon="user"
|
||||
),
|
||||
MenuItem(
|
||||
name="数据库管理",
|
||||
module="database",
|
||||
router="/database",
|
||||
icon="database",
|
||||
),
|
||||
MenuItem(
|
||||
name="系统信息", module="system", router="/system", icon="system"
|
||||
),
|
||||
]
|
||||
self.save()
|
||||
|
||||
def get_menus(self):
|
||||
return MenuData(menus=self.menu)
|
||||
|
||||
def save(self):
|
||||
self.file.parent.mkdir(parents=True, exist_ok=True)
|
||||
temp = [menu.dict() for menu in self.menu]
|
||||
with self.file.open("w", encoding="utf8") as f:
|
||||
json.dump(temp, f, ensure_ascii=False, indent=4)
|
||||
|
||||
|
||||
menu_manage = MenuManage()
|
||||
@@ -0,0 +1,21 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MenuItem(BaseModel):
|
||||
module: str
|
||||
"""模块名称"""
|
||||
name: str
|
||||
"""菜单名称"""
|
||||
router: str
|
||||
"""路由"""
|
||||
icon: str
|
||||
"""图标"""
|
||||
default: bool = False
|
||||
"""默认选中"""
|
||||
|
||||
|
||||
class MenuData(BaseModel):
|
||||
bot_type: str = "zhenxun"
|
||||
"""bot类型"""
|
||||
menus: list[MenuItem]
|
||||
"""菜单列表"""
|
||||
@@ -1,20 +1,14 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import nonebot
|
||||
from nonebot import require
|
||||
from nonebot.config import Config
|
||||
from tortoise.expressions import RawSQL
|
||||
from tortoise.functions import Count
|
||||
|
||||
from zhenxun.models.bot_connect_log import BotConnectLog
|
||||
from zhenxun.models.chat_history import ChatHistory
|
||||
from zhenxun.models.statistics import Statistics
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from ....base_model import BaseResultModel, QueryModel, Result
|
||||
from ....utils import authentication
|
||||
from .data_source import BotManage
|
||||
from .data_source import ApiDataSource
|
||||
from .model import AllChatAndCallCount, BotInfo, ChatCallMonthCount, QueryChatCallCount
|
||||
|
||||
require("plugin_store")
|
||||
@@ -33,8 +27,9 @@ driver = nonebot.get_driver()
|
||||
)
|
||||
async def _() -> Result[list[BotInfo]]:
|
||||
try:
|
||||
return Result.ok(await BotManage.get_bot_list(), "拿到信息啦!")
|
||||
return Result.ok(await ApiDataSource.get_bot_list(), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_bot_list 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@@ -46,29 +41,13 @@ async def _() -> Result[list[BotInfo]]:
|
||||
description="获取聊天/调用记录的全部和今日数量",
|
||||
)
|
||||
async def _(bot_id: str | None = None) -> Result[QueryChatCallCount]:
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
chat_all_count = await query.annotate().count()
|
||||
chat_day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
call_all_count = await query.annotate().count()
|
||||
call_day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return Result.ok(
|
||||
QueryChatCallCount(
|
||||
chat_num=chat_all_count,
|
||||
chat_day=chat_day_count,
|
||||
call_num=call_all_count,
|
||||
call_day=call_day_count,
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_chat_and_call_count(bot_id), "拿到信息啦!"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_chat_and_call_count 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -79,41 +58,15 @@ async def _(bot_id: str | None = None) -> Result[QueryChatCallCount]:
|
||||
description="获取聊天/调用记录的全部数据次数",
|
||||
)
|
||||
async def _(bot_id: str | None = None) -> Result[AllChatAndCallCount]:
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
chat_week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
chat_month_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
chat_year_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
call_week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
call_month_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
call_year_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return Result.ok(
|
||||
AllChatAndCallCount(
|
||||
chat_week=chat_week_count,
|
||||
chat_month=chat_month_count,
|
||||
chat_year=chat_year_count,
|
||||
call_week=call_week_count,
|
||||
call_month=call_month_count,
|
||||
call_year=call_year_count,
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_all_chat_and_call_count(bot_id), "拿到信息啦!"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"{router.prefix}/get_all_chat_and_call_count 调用错误", "WebUi", e=e
|
||||
)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -124,48 +77,13 @@ async def _(bot_id: str | None = None) -> Result[AllChatAndCallCount]:
|
||||
deprecated="获取聊天/调用记录的一个月数量", # type: ignore
|
||||
)
|
||||
async def _(bot_id: str | None = None) -> Result[ChatCallMonthCount]:
|
||||
now = datetime.now()
|
||||
filter_date = now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
chat_query = ChatHistory
|
||||
call_query = Statistics
|
||||
if bot_id:
|
||||
chat_query = chat_query.filter(bot_id=bot_id)
|
||||
call_query = call_query.filter(bot_id=bot_id)
|
||||
chat_date_list = (
|
||||
await chat_query.filter(create_time__gte=filter_date)
|
||||
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
|
||||
.group_by("date")
|
||||
.values("date", "count")
|
||||
)
|
||||
call_date_list = (
|
||||
await call_query.filter(create_time__gte=filter_date)
|
||||
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
|
||||
.group_by("date")
|
||||
.values("date", "count")
|
||||
)
|
||||
date_list = []
|
||||
chat_count_list = []
|
||||
call_count_list = []
|
||||
chat_date2cnt = {str(date["date"]): date["count"] for date in chat_date_list}
|
||||
call_date2cnt = {str(date["date"]): date["count"] for date in call_date_list}
|
||||
date = now.date()
|
||||
for _ in range(30):
|
||||
if str(date) in chat_date2cnt:
|
||||
chat_count_list.append(chat_date2cnt[str(date)])
|
||||
else:
|
||||
chat_count_list.append(0)
|
||||
if str(date) in call_date2cnt:
|
||||
call_count_list.append(call_date2cnt[str(date)])
|
||||
else:
|
||||
call_count_list.append(0)
|
||||
date_list.append(str(date)[5:])
|
||||
date -= timedelta(days=1)
|
||||
chat_count_list.reverse()
|
||||
call_count_list.reverse()
|
||||
date_list.reverse()
|
||||
return Result.ok(
|
||||
ChatCallMonthCount(chat=chat_count_list, call=call_count_list, date=date_list)
|
||||
)
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_chat_and_call_month(bot_id), "拿到信息啦!"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_chat_and_call_month 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -176,18 +94,11 @@ async def _(bot_id: str | None = None) -> Result[ChatCallMonthCount]:
|
||||
deprecated="获取Bot连接记录", # type: ignore
|
||||
)
|
||||
async def _(query: QueryModel) -> Result[BaseResultModel]:
|
||||
total = await BotConnectLog.all().count()
|
||||
if total % query.size:
|
||||
total += 1
|
||||
data = (
|
||||
await BotConnectLog.all()
|
||||
.order_by("-id")
|
||||
.offset((query.index - 1) * query.size)
|
||||
.limit(query.size)
|
||||
)
|
||||
for v in data:
|
||||
v.connect_time = v.connect_time.replace(tzinfo=None).replace(microsecond=0)
|
||||
return Result.ok(BaseResultModel(total=total, data=data))
|
||||
try:
|
||||
return Result.ok(await ApiDataSource.get_connect_log(query), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_connect_log 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
|
||||
@@ -4,13 +4,17 @@ import time
|
||||
import nonebot
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.drivers import Driver
|
||||
from tortoise.expressions import RawSQL
|
||||
from tortoise.functions import Count
|
||||
|
||||
from zhenxun.models.bot_connect_log import BotConnectLog
|
||||
from zhenxun.models.chat_history import ChatHistory
|
||||
from zhenxun.models.statistics import Statistics
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ....base_model import BaseResultModel, QueryModel
|
||||
from ..main.data_source import bot_live
|
||||
from .model import BotInfo
|
||||
from .model import AllChatAndCallCount, BotInfo, ChatCallMonthCount, QueryChatCallCount
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
@@ -24,7 +28,7 @@ async def _():
|
||||
CONNECT_TIME = int(time.time())
|
||||
|
||||
|
||||
class BotManage:
|
||||
class ApiDataSource:
|
||||
@classmethod
|
||||
async def __build_bot_info(cls, bot: Bot) -> BotInfo:
|
||||
"""构建Bot信息
|
||||
@@ -47,8 +51,7 @@ class BotManage:
|
||||
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]
|
||||
group_list, _ = await PlatformUtils.get_group_list(bot, True)
|
||||
friend_list, _ = await PlatformUtils.get_friend_list(bot)
|
||||
bot_info.group_count = len(group_list)
|
||||
bot_info.friend_count = len(friend_list)
|
||||
@@ -77,3 +80,161 @@ class BotManage:
|
||||
for _, bot in nonebot.get_bots().items():
|
||||
bot_list.append(await cls.__build_bot_info(bot))
|
||||
return bot_list
|
||||
|
||||
@classmethod
|
||||
async def get_chat_and_call_count(cls, bot_id: str | None) -> QueryChatCallCount:
|
||||
"""获取今日聊天和调用次数
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
QueryChatCallCount: 数据内容
|
||||
"""
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
chat_all_count = await query.annotate().count()
|
||||
chat_day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
call_all_count = await query.annotate().count()
|
||||
call_day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return QueryChatCallCount(
|
||||
chat_num=chat_all_count,
|
||||
chat_day=chat_day_count,
|
||||
call_num=call_all_count,
|
||||
call_day=call_day_count,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_all_chat_and_call_count(
|
||||
cls, bot_id: str | None
|
||||
) -> AllChatAndCallCount:
|
||||
"""获取全部聊天和调用记录
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
AllChatAndCallCount: 数据内容
|
||||
"""
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
chat_week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
chat_month_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
chat_year_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
call_week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
call_month_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
call_year_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return AllChatAndCallCount(
|
||||
chat_week=chat_week_count,
|
||||
chat_month=chat_month_count,
|
||||
chat_year=chat_year_count,
|
||||
call_week=call_week_count,
|
||||
call_month=call_month_count,
|
||||
call_year=call_year_count,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_chat_and_call_month(cls, bot_id: str | None) -> ChatCallMonthCount:
|
||||
"""获取一个月内的调用/消息记录次数,并根据日期对数据填充0
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
ChatCallMonthCount: 数据内容
|
||||
"""
|
||||
now = datetime.now()
|
||||
filter_date = now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
chat_query = ChatHistory
|
||||
call_query = Statistics
|
||||
if bot_id:
|
||||
chat_query = chat_query.filter(bot_id=bot_id)
|
||||
call_query = call_query.filter(bot_id=bot_id)
|
||||
chat_date_list = (
|
||||
await chat_query.filter(create_time__gte=filter_date)
|
||||
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
|
||||
.group_by("date")
|
||||
.values("date", "count")
|
||||
)
|
||||
call_date_list = (
|
||||
await call_query.filter(create_time__gte=filter_date)
|
||||
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
|
||||
.group_by("date")
|
||||
.values("date", "count")
|
||||
)
|
||||
date_list = []
|
||||
chat_count_list = []
|
||||
call_count_list = []
|
||||
chat_date2cnt = {str(date["date"]): date["count"] for date in chat_date_list}
|
||||
call_date2cnt = {str(date["date"]): date["count"] for date in call_date_list}
|
||||
date = now.date()
|
||||
for _ in range(30):
|
||||
if str(date) in chat_date2cnt:
|
||||
chat_count_list.append(chat_date2cnt[str(date)])
|
||||
else:
|
||||
chat_count_list.append(0)
|
||||
if str(date) in call_date2cnt:
|
||||
call_count_list.append(call_date2cnt[str(date)])
|
||||
else:
|
||||
call_count_list.append(0)
|
||||
date_list.append(str(date)[5:])
|
||||
date -= timedelta(days=1)
|
||||
chat_count_list.reverse()
|
||||
call_count_list.reverse()
|
||||
date_list.reverse()
|
||||
return ChatCallMonthCount(
|
||||
chat=chat_count_list, call=call_count_list, date=date_list
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_connect_log(cls, query: QueryModel) -> BaseResultModel:
|
||||
"""获取bot连接日志
|
||||
|
||||
参数:
|
||||
query: 查询模型
|
||||
|
||||
返回:
|
||||
BaseResultModel: 数据内容
|
||||
"""
|
||||
total = await BotConnectLog.all().count()
|
||||
if total % query.size:
|
||||
total += 1
|
||||
data = (
|
||||
await BotConnectLog.all()
|
||||
.order_by("-id")
|
||||
.offset((query.index - 1) * query.size)
|
||||
.limit(query.size)
|
||||
)
|
||||
for v in data:
|
||||
v.connect_time = v.connect_time.replace(tzinfo=None).replace(microsecond=0)
|
||||
return BaseResultModel(total=total, data=data)
|
||||
|
||||
@@ -3,14 +3,15 @@ from fastapi.responses import JSONResponse
|
||||
import nonebot
|
||||
from nonebot.drivers import Driver
|
||||
from tortoise import Tortoise
|
||||
from tortoise.exceptions import OperationalError
|
||||
|
||||
from zhenxun.configs.config import BotConfig
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.models.task_info import TaskInfo
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
from ....base_model import BaseResultModel, QueryModel, Result
|
||||
from ....utils import authentication
|
||||
from .data_source import ApiDataSource, type2sql
|
||||
from .models.model import Column, SqlModel, SqlText
|
||||
from .models.sql_log import SqlLog
|
||||
|
||||
@@ -20,52 +21,6 @@ router = APIRouter(prefix="/database")
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
SQL_DICT = {}
|
||||
|
||||
|
||||
SELECT_TABLE_MYSQL_SQL = """
|
||||
SELECT table_name AS name, table_comment AS `desc`
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE();
|
||||
"""
|
||||
|
||||
SELECT_TABLE_SQLITE_SQL = """
|
||||
SELECT name FROM sqlite_master WHERE type='table';
|
||||
"""
|
||||
|
||||
SELECT_TABLE_PSQL_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_PSQL_SQL = """
|
||||
SELECT column_name, data_type, character_maximum_length as max_length, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = '{}';
|
||||
"""
|
||||
|
||||
SELECT_TABLE_COLUMN_MYSQL_SQL = """
|
||||
SHOW COLUMNS FROM {};
|
||||
"""
|
||||
|
||||
SELECT_TABLE_COLUMN_SQLITE_SQL = """
|
||||
PRAGMA table_info({});
|
||||
"""
|
||||
|
||||
type2sql = {
|
||||
"mysql": SELECT_TABLE_MYSQL_SQL,
|
||||
"sqlite": SELECT_TABLE_SQLITE_SQL,
|
||||
"postgres": SELECT_TABLE_PSQL_SQL,
|
||||
}
|
||||
|
||||
type2sql_column = {
|
||||
"mysql": SELECT_TABLE_COLUMN_MYSQL_SQL,
|
||||
"sqlite": SELECT_TABLE_COLUMN_SQLITE_SQL,
|
||||
"postgres": SELECT_TABLE_COLUMN_PSQL_SQL,
|
||||
}
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def _():
|
||||
for plugin in nonebot.get_loaded_plugins():
|
||||
@@ -73,7 +28,7 @@ async def _():
|
||||
sql_list = []
|
||||
if plugin.metadata and plugin.metadata.extra:
|
||||
sql_list = plugin.metadata.extra.get("sql_list")
|
||||
if module in SQL_DICT:
|
||||
if module in ApiDataSource.SQL_DICT:
|
||||
raise ValueError(f"{module} 常用SQL module 重复")
|
||||
if sql_list:
|
||||
SqlModel(
|
||||
@@ -81,15 +36,15 @@ async def _():
|
||||
module=module,
|
||||
sql_list=sql_list,
|
||||
)
|
||||
SQL_DICT[module] = SqlModel
|
||||
if SQL_DICT:
|
||||
result = await PluginInfo.filter(module__in=SQL_DICT.keys()).values_list(
|
||||
"module", "name"
|
||||
)
|
||||
ApiDataSource.SQL_DICT[module] = SqlModel
|
||||
if ApiDataSource.SQL_DICT:
|
||||
result = await PluginInfo.filter(
|
||||
module__in=ApiDataSource.SQL_DICT.keys()
|
||||
).values_list("module", "name")
|
||||
module2name = {r[0]: r[1] for r in result}
|
||||
for s in SQL_DICT:
|
||||
module = SQL_DICT[s].module
|
||||
SQL_DICT[s].name = module2name.get(module, module)
|
||||
for s in ApiDataSource.SQL_DICT:
|
||||
module = ApiDataSource.SQL_DICT[s].module
|
||||
ApiDataSource.SQL_DICT[s].name = module2name.get(module, module)
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -100,10 +55,14 @@ async def _():
|
||||
description="获取数据库表",
|
||||
)
|
||||
async def _() -> Result[list[dict]]:
|
||||
db = Tortoise.get_connection("default")
|
||||
sql_type = BotConfig.get_sql_type()
|
||||
query = await db.execute_query_dict(type2sql[sql_type])
|
||||
return Result.ok(query)
|
||||
try:
|
||||
db = Tortoise.get_connection("default")
|
||||
sql_type = BotConfig.get_sql_type()
|
||||
query = await db.execute_query_dict(type2sql[sql_type])
|
||||
return Result.ok(query)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_table_list 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -114,34 +73,13 @@ async def _() -> Result[list[dict]]:
|
||||
description="获取表字段",
|
||||
)
|
||||
async def _(table_name: str) -> Result[list[Column]]:
|
||||
db = Tortoise.get_connection("default")
|
||||
sql_type = BotConfig.get_sql_type()
|
||||
sql = type2sql_column[sql_type]
|
||||
query = await db.execute_query_dict(sql.format(table_name))
|
||||
result_list = []
|
||||
if sql_type == "sqlite":
|
||||
result_list.extend(
|
||||
Column(
|
||||
column_name=result["name"],
|
||||
data_type=result["type"],
|
||||
max_length=-1,
|
||||
is_nullable="YES" if result["notnull"] == 1 else "NO",
|
||||
)
|
||||
for result in query
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_table_column(table_name), "拿到信息啦!"
|
||||
)
|
||||
elif sql_type == "mysql":
|
||||
result_list.extend(
|
||||
Column(
|
||||
column_name=result["Field"],
|
||||
data_type=result["Type"],
|
||||
max_length=-1,
|
||||
is_nullable=result["Null"],
|
||||
)
|
||||
for result in query
|
||||
)
|
||||
else:
|
||||
result_list.extend(Column(**result) for result in query)
|
||||
return Result.ok(result_list)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_table_column 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -163,7 +101,8 @@ async def _(sql: SqlText, request: Request) -> Result[list[dict]]:
|
||||
result = await TaskInfo.raw(sql.sql)
|
||||
await SqlLog.add(ip or "0.0.0.0", sql.sql, str(result))
|
||||
return Result.ok(info="执行成功啦!")
|
||||
except OperationalError as e:
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/exec_sql 调用错误", "WebUi", e=e)
|
||||
await SqlLog.add(ip or "0.0.0.0", sql.sql, str(e), False)
|
||||
return Result.warning_(f"sql执行错误: {e}")
|
||||
|
||||
@@ -176,16 +115,20 @@ async def _(sql: SqlText, request: Request) -> Result[list[dict]]:
|
||||
description="sql日志列表",
|
||||
)
|
||||
async def _(query: QueryModel) -> Result[BaseResultModel]:
|
||||
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))
|
||||
try:
|
||||
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))
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_sql_log 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -197,5 +140,5 @@ async def _(query: QueryModel) -> Result[BaseResultModel]:
|
||||
)
|
||||
async def _(plugin_name: str | None = None) -> Result[dict]:
|
||||
if plugin_name:
|
||||
return Result.ok(SQL_DICT.get(plugin_name))
|
||||
return Result.ok(str(SQL_DICT))
|
||||
return Result.ok(ApiDataSource.SQL_DICT.get(plugin_name))
|
||||
return Result.ok(str(ApiDataSource.SQL_DICT))
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
from tortoise import Tortoise
|
||||
|
||||
from zhenxun.configs.config import BotConfig
|
||||
|
||||
from .models.model import Column
|
||||
|
||||
SELECT_TABLE_MYSQL_SQL = """
|
||||
SELECT table_name AS name, table_comment AS `desc`
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE();
|
||||
"""
|
||||
|
||||
SELECT_TABLE_SQLITE_SQL = """
|
||||
SELECT name FROM sqlite_master WHERE type='table';
|
||||
"""
|
||||
|
||||
SELECT_TABLE_PSQL_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_PSQL_SQL = """
|
||||
SELECT column_name, data_type, character_maximum_length as max_length, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = '{}';
|
||||
"""
|
||||
|
||||
SELECT_TABLE_COLUMN_MYSQL_SQL = """
|
||||
SHOW COLUMNS FROM {};
|
||||
"""
|
||||
|
||||
SELECT_TABLE_COLUMN_SQLITE_SQL = """
|
||||
PRAGMA table_info({});
|
||||
"""
|
||||
|
||||
type2sql = {
|
||||
"mysql": SELECT_TABLE_MYSQL_SQL,
|
||||
"sqlite": SELECT_TABLE_SQLITE_SQL,
|
||||
"postgres": SELECT_TABLE_PSQL_SQL,
|
||||
}
|
||||
|
||||
type2sql_column = {
|
||||
"mysql": SELECT_TABLE_COLUMN_MYSQL_SQL,
|
||||
"sqlite": SELECT_TABLE_COLUMN_SQLITE_SQL,
|
||||
"postgres": SELECT_TABLE_COLUMN_PSQL_SQL,
|
||||
}
|
||||
|
||||
|
||||
class ApiDataSource:
|
||||
SQL_DICT = {} # noqa: RUF012
|
||||
|
||||
@classmethod
|
||||
async def get_table_column(cls, table_name: str) -> list[Column]:
|
||||
"""获取表字段信息
|
||||
|
||||
参数:
|
||||
table_name: 表名
|
||||
|
||||
返回:
|
||||
list[Column]: 字段数据
|
||||
"""
|
||||
db = Tortoise.get_connection("default")
|
||||
sql_type = BotConfig.get_sql_type()
|
||||
sql = type2sql_column[sql_type]
|
||||
query = await db.execute_query_dict(sql.format(table_name))
|
||||
result_list = []
|
||||
if sql_type == "sqlite":
|
||||
result_list.extend(
|
||||
Column(
|
||||
column_name=result["name"],
|
||||
data_type=result["type"],
|
||||
max_length=-1,
|
||||
is_nullable="YES" if result["notnull"] == 1 else "NO",
|
||||
)
|
||||
for result in query
|
||||
)
|
||||
elif sql_type == "mysql":
|
||||
result_list.extend(
|
||||
Column(
|
||||
column_name=result["Field"],
|
||||
data_type=result["Type"],
|
||||
max_length=-1,
|
||||
is_nullable=result["Null"],
|
||||
)
|
||||
for result in query
|
||||
)
|
||||
else:
|
||||
result_list.extend(Column(**result) for result in query)
|
||||
return result_list
|
||||
@@ -1,7 +1,5 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter
|
||||
@@ -9,28 +7,26 @@ from fastapi.responses import JSONResponse
|
||||
import nonebot
|
||||
from nonebot.config import Config
|
||||
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
|
||||
from tortoise.functions import Count
|
||||
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
|
||||
|
||||
from zhenxun.models.bot_connect_log import BotConnectLog
|
||||
from zhenxun.models.chat_history import ChatHistory
|
||||
from zhenxun.models.group_console import GroupConsole
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.models.statistics import Statistics
|
||||
from zhenxun.models.bot_console import BotConsole
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.common_utils import CommonUtils
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ....base_model import Result
|
||||
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
|
||||
from ....config import QueryDateType
|
||||
from ....utils import authentication, get_system_status
|
||||
from .data_source import bot_live
|
||||
from .data_source import ApiDataSource
|
||||
from .model import (
|
||||
ActiveGroup,
|
||||
BaseInfo,
|
||||
BotBlockModule,
|
||||
BotManageUpdateParam,
|
||||
BotStatusParam,
|
||||
HotPlugin,
|
||||
NonebotData,
|
||||
QueryCount,
|
||||
TemplateBaseInfo,
|
||||
)
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
@@ -56,64 +52,14 @@ async def _(bot_id: str | None = None) -> Result[list[BaseInfo]]:
|
||||
返回:
|
||||
Result: 获取指定bot信息与bot列表
|
||||
"""
|
||||
global run_time
|
||||
bot_list: list[TemplateBaseInfo] = []
|
||||
if bots := nonebot.get_bots():
|
||||
select_bot: BaseInfo
|
||||
for _, bot in bots.items():
|
||||
login_info = await bot.get_login_info()
|
||||
bot_list.append(
|
||||
TemplateBaseInfo(
|
||||
bot=bot, # type: ignore
|
||||
self_id=bot.self_id,
|
||||
nickname=login_info["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 = await PluginInfo.all().count()
|
||||
fail_count = await PluginInfo.filter(load_status=False).count()
|
||||
select_bot.fail_plugin_count = fail_count
|
||||
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
|
||||
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
|
||||
select_bot.connect_count = await BotConnectLog.filter(
|
||||
bot_id=select_bot.self_id
|
||||
).count()
|
||||
return Result.ok([BaseInfo(**e.dict()) for e in bot_list], "拿到信息啦!")
|
||||
return Result.warning_("无Bot连接...")
|
||||
try:
|
||||
result = await ApiDataSource.get_base_info(bot_id)
|
||||
if not result:
|
||||
Result.warning_("无Bot连接...")
|
||||
return Result.ok(result, "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_base_info 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -124,32 +70,11 @@ async def _(bot_id: str | None = None) -> Result[list[BaseInfo]]:
|
||||
description="获取接收消息数量",
|
||||
)
|
||||
async def _(bot_id: str | None = None) -> Result[QueryCount]:
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
all_count = await query.annotate().count()
|
||||
day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
month_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
year_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return Result.ok(
|
||||
QueryCount(
|
||||
num=all_count,
|
||||
day=day_count,
|
||||
week=week_count,
|
||||
month=month_count,
|
||||
year=year_count,
|
||||
)
|
||||
)
|
||||
try:
|
||||
return Result.ok(await ApiDataSource.get_all_chat_count(bot_id), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_all_chat_count 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -160,32 +85,11 @@ async def _(bot_id: str | None = None) -> Result[QueryCount]:
|
||||
description="获取调用次数",
|
||||
)
|
||||
async def _(bot_id: str | None = None) -> Result[QueryCount]:
|
||||
now = datetime.now()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
all_count = await query.annotate().count()
|
||||
day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
month_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
year_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return Result.ok(
|
||||
QueryCount(
|
||||
num=all_count,
|
||||
day=day_count,
|
||||
week=week_count,
|
||||
month=month_count,
|
||||
year=year_count,
|
||||
)
|
||||
)
|
||||
try:
|
||||
return Result.ok(await ApiDataSource.get_all_call_count(bot_id), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_all_call_count 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -196,19 +100,18 @@ async def _(bot_id: str | None = None) -> Result[QueryCount]:
|
||||
description="好友/群组数量",
|
||||
)
|
||||
async def _(bot_id: str) -> Result[dict[str, int]]:
|
||||
if bots := nonebot.get_bots():
|
||||
if bot_id not in bots:
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
bot = bots[bot_id]
|
||||
platform = PlatformUtils.get_platform(bot)
|
||||
if platform == "qq":
|
||||
data = {
|
||||
"friend_count": len(await bot.get_friend_list()),
|
||||
"group_count": len(await bot.get_group_list()),
|
||||
}
|
||||
return Result.ok(data)
|
||||
return Result.warning_("暂不支持该平台...")
|
||||
return Result.warning_("无Bot连接...")
|
||||
try:
|
||||
bot = nonebot.get_bot(bot_id)
|
||||
data = {
|
||||
"friend_count": len(await PlatformUtils.get_friend_list(bot)),
|
||||
"group_count": len(await PlatformUtils.get_group_list(bot)),
|
||||
}
|
||||
return Result.ok(data, "拿到信息啦!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_fg_count 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -219,6 +122,7 @@ async def _(bot_id: str) -> Result[dict[str, int]]:
|
||||
description="获取nb数据",
|
||||
)
|
||||
async def _() -> Result[NonebotData]:
|
||||
global run_time
|
||||
return Result.ok(NonebotData(config=driver.config, run_time=int(run_time)))
|
||||
|
||||
|
||||
@@ -241,6 +145,7 @@ async def _() -> Result[Config]:
|
||||
description="获取nb运行时间",
|
||||
)
|
||||
async def _() -> Result[int]:
|
||||
global run_time
|
||||
return Result.ok(int(run_time))
|
||||
|
||||
|
||||
@@ -254,48 +159,13 @@ async def _() -> Result[int]:
|
||||
async def _(
|
||||
date_type: QueryDateType | None = None, bot_id: str | None = None
|
||||
) -> Result[list[ActiveGroup]]:
|
||||
query = ChatHistory
|
||||
now = datetime.now()
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
if date_type == QueryDateType.DAY:
|
||||
query = query.filter(create_time__gte=now - timedelta(hours=now.hour))
|
||||
if date_type == QueryDateType.WEEK:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=7))
|
||||
if date_type == QueryDateType.MONTH:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=30))
|
||||
if date_type == QueryDateType.YEAR:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=365))
|
||||
data_list = (
|
||||
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")
|
||||
)
|
||||
id2name = {}
|
||||
if data_list:
|
||||
if info_list := await GroupConsole.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
|
||||
active_group_list = [
|
||||
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]),
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_active_group(date_type, bot_id), "拿到信息啦!"
|
||||
)
|
||||
for data in data_list
|
||||
]
|
||||
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)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_active_group 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -308,37 +178,66 @@ async def _(
|
||||
async def _(
|
||||
date_type: QueryDateType | None = None, bot_id: str | None = None
|
||||
) -> Result[list[HotPlugin]]:
|
||||
query = Statistics
|
||||
now = datetime.now()
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
if date_type == QueryDateType.DAY:
|
||||
query = query.filter(create_time__gte=now - timedelta(hours=now.hour))
|
||||
if date_type == QueryDateType.WEEK:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=7))
|
||||
if date_type == QueryDateType.MONTH:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=30))
|
||||
if date_type == QueryDateType.YEAR:
|
||||
query = query.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 = []
|
||||
module_list = [x[0] for x in data_list]
|
||||
plugins = await PluginInfo.filter(module__in=module_list).all()
|
||||
module2name = {p.module: p.name for p in plugins}
|
||||
for data in data_list:
|
||||
module = data[0]
|
||||
name = module2name.get(module) or module
|
||||
hot_plugin_list.append(HotPlugin(module=module, 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)
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_hot_plugin(date_type, bot_id), "拿到信息啦!"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_hot_plugin 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/change_bot_status",
|
||||
dependencies=[authentication()],
|
||||
response_model=Result,
|
||||
response_class=JSONResponse,
|
||||
description="修改bot全局开关",
|
||||
)
|
||||
async def _(param: BotStatusParam):
|
||||
try:
|
||||
await BotConsole.set_bot_status(param.status, param.bot_id)
|
||||
return Result.ok(info="修改bot全局开关成功!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.fail("Bot未初始化...")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/get_bot_block_module",
|
||||
dependencies=[authentication()],
|
||||
response_model=Result[BotBlockModule],
|
||||
response_class=JSONResponse,
|
||||
description="获取bot层面的禁用模块",
|
||||
)
|
||||
async def _(bot_id: str) -> Result[BotBlockModule]:
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_bot_block_module(bot_id), "拿到信息啦!"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_bot_block_module 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/update_bot_manage",
|
||||
dependencies=[authentication()],
|
||||
response_model=Result,
|
||||
response_class=JSONResponse,
|
||||
description="修改bot全局开关",
|
||||
)
|
||||
async def _(param: BotManageUpdateParam):
|
||||
try:
|
||||
bot_data = await BotConsole.get_or_none(bot_id=param.bot_id)
|
||||
if not bot_data:
|
||||
return Result.fail("Bot数据不存在...")
|
||||
bot_data.block_plugins = CommonUtils.convert_module_format(param.block_plugins)
|
||||
bot_data.block_tasks = CommonUtils.convert_module_format(param.block_tasks)
|
||||
await bot_data.save(update_fields=["block_plugins", "block_tasks"])
|
||||
return Result.ok()
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/update_bot_manage 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@ws_router.websocket("/system_status")
|
||||
|
||||
@@ -1,8 +1,33 @@
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.drivers import Driver
|
||||
from tortoise.functions import Count
|
||||
|
||||
from zhenxun.models.bot_connect_log import BotConnectLog
|
||||
from zhenxun.models.bot_console import BotConsole
|
||||
from zhenxun.models.chat_history import ChatHistory
|
||||
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.utils.common_utils import CommonUtils
|
||||
from zhenxun.utils.enum import PluginType
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
|
||||
from .model import (
|
||||
ActiveGroup,
|
||||
BaseInfo,
|
||||
BotBlockModule,
|
||||
HotPlugin,
|
||||
QueryCount,
|
||||
TemplateBaseInfo,
|
||||
)
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
@@ -33,3 +58,313 @@ async def _(bot: Bot):
|
||||
@driver.on_bot_disconnect
|
||||
async def _(bot: Bot):
|
||||
bot_live.remove(bot.self_id)
|
||||
|
||||
|
||||
class ApiDataSource:
|
||||
@classmethod
|
||||
async def __build_bot_info(cls, bot: Bot) -> TemplateBaseInfo:
|
||||
"""构建bot信息
|
||||
|
||||
参数:
|
||||
bot: bot实例
|
||||
|
||||
返回:
|
||||
TemplateBaseInfo: bot信息
|
||||
"""
|
||||
login_info = None
|
||||
try:
|
||||
login_info = await bot.get_login_info()
|
||||
except Exception as e:
|
||||
logger.warning("调用接口get_login_info失败", "WebUi", e=e)
|
||||
return TemplateBaseInfo(
|
||||
bot=bot,
|
||||
self_id=bot.self_id,
|
||||
nickname=login_info["nickname"] if login_info else bot.self_id,
|
||||
ava_url=AVA_URL.format(bot.self_id),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def __get_bot_version(cls) -> str:
|
||||
"""获取bot版本
|
||||
|
||||
返回:
|
||||
str | None: 版本
|
||||
"""
|
||||
version_file = Path() / "__version__"
|
||||
if version_file.exists():
|
||||
if text := version_file.open().read():
|
||||
return text.replace("__version__: ", "").strip()
|
||||
return "unknown"
|
||||
|
||||
@classmethod
|
||||
async def __init_bot_base_data(cls, select_bot: TemplateBaseInfo):
|
||||
"""初始化bot的基础数据
|
||||
|
||||
参数:
|
||||
select_bot: bot
|
||||
"""
|
||||
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 PlatformUtils.get_group_list(select_bot.bot))
|
||||
# 好友数量
|
||||
select_bot.friend_count = len(
|
||||
await PlatformUtils.get_friend_list(select_bot.bot)
|
||||
)
|
||||
select_bot.status = await BotConsole.get_bot_status(select_bot.self_id)
|
||||
# 连接时间
|
||||
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")
|
||||
select_bot.version = cls.__get_bot_version()
|
||||
day_call = await Statistics.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour)
|
||||
).count()
|
||||
select_bot.day_call = day_call
|
||||
select_bot.connect_count = await BotConnectLog.filter(
|
||||
bot_id=select_bot.self_id
|
||||
).count()
|
||||
|
||||
@classmethod
|
||||
async def get_base_info(cls, bot_id: str | None) -> list[BaseInfo] | None:
|
||||
"""获取bot信息
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
list[BaseInfo] | None: bot列表
|
||||
"""
|
||||
bots = nonebot.get_bots()
|
||||
if not bots:
|
||||
return None
|
||||
select_bot: BaseInfo
|
||||
bot_list = [await cls.__build_bot_info(bot) for _, bot in bots.items()]
|
||||
# 获取指定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]
|
||||
await cls.__init_bot_base_data(select_bot)
|
||||
for bot in bot_list:
|
||||
bot.bot = None # type: ignore
|
||||
select_bot.is_select = True
|
||||
return [BaseInfo(**e.dict()) for e in bot_list]
|
||||
|
||||
@classmethod
|
||||
async def get_all_chat_count(cls, bot_id: str | None) -> QueryCount:
|
||||
"""获取年/月/周/日聊天次数
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
QueryCount: 数据内容
|
||||
"""
|
||||
now = datetime.now()
|
||||
query = ChatHistory
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
all_count = await query.annotate().count()
|
||||
day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
month_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
year_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return QueryCount(
|
||||
num=all_count,
|
||||
day=day_count,
|
||||
week=week_count,
|
||||
month=month_count,
|
||||
year=year_count,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_all_call_count(cls, bot_id: str | None) -> QueryCount:
|
||||
"""获取年/月/周/日调用次数
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
QueryCount: 数据内容
|
||||
"""
|
||||
now = datetime.now()
|
||||
query = Statistics
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
all_count = await query.annotate().count()
|
||||
day_count = await query.filter(
|
||||
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
week_count = await query.filter(
|
||||
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
month_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=30, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
year_count = await query.filter(
|
||||
create_time__gte=now
|
||||
- timedelta(days=365, hours=now.hour, minutes=now.minute)
|
||||
).count()
|
||||
return QueryCount(
|
||||
num=all_count,
|
||||
day=day_count,
|
||||
week=week_count,
|
||||
month=month_count,
|
||||
year=year_count,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def __get_query(
|
||||
cls,
|
||||
base_query: type[ChatHistory | Statistics],
|
||||
date_type: QueryDateType | None = None,
|
||||
bot_id: str | None = None,
|
||||
):
|
||||
"""构建日期查询条件
|
||||
|
||||
参数:
|
||||
date_type: 日期类型.
|
||||
bot_id: bot id.
|
||||
"""
|
||||
query = base_query
|
||||
now = datetime.now()
|
||||
if bot_id:
|
||||
query = query.filter(bot_id=bot_id)
|
||||
if date_type == QueryDateType.DAY:
|
||||
query = query.filter(create_time__gte=now - timedelta(hours=now.hour))
|
||||
if date_type == QueryDateType.WEEK:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=7))
|
||||
if date_type == QueryDateType.MONTH:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=30))
|
||||
if date_type == QueryDateType.YEAR:
|
||||
query = query.filter(create_time__gte=now - timedelta(days=365))
|
||||
return query
|
||||
|
||||
@classmethod
|
||||
async def get_active_group(
|
||||
cls, date_type: QueryDateType | None = None, bot_id: str | None = None
|
||||
) -> list[ActiveGroup]:
|
||||
"""获取活跃群组
|
||||
|
||||
参数:
|
||||
date_type: 日期类型.
|
||||
bot_id: bot id.
|
||||
|
||||
返回:
|
||||
list[ActiveGroup]: 活跃群组列表
|
||||
"""
|
||||
query = cls.__get_query(ChatHistory, date_type, bot_id)
|
||||
data_list = (
|
||||
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")
|
||||
)
|
||||
id2name = {}
|
||||
if data_list:
|
||||
if info_list := await GroupConsole.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
|
||||
active_group_list = [
|
||||
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]),
|
||||
)
|
||||
for data in data_list
|
||||
]
|
||||
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 active_group_list
|
||||
|
||||
@classmethod
|
||||
async def get_hot_plugin(
|
||||
cls, date_type: QueryDateType | None = None, bot_id: str | None = None
|
||||
) -> list[HotPlugin]:
|
||||
"""获取热门插件
|
||||
|
||||
参数:
|
||||
date_type: 日期类型.
|
||||
bot_id: bot id.
|
||||
|
||||
返回:
|
||||
list[HotPlugin]: 热门插件列表
|
||||
"""
|
||||
query = cls.__get_query(Statistics, date_type, bot_id)
|
||||
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 = []
|
||||
module_list = [x[0] for x in data_list]
|
||||
plugins = await PluginInfo.filter(module__in=module_list).all()
|
||||
module2name = {p.module: p.name for p in plugins}
|
||||
for data in data_list:
|
||||
module = data[0]
|
||||
name = module2name.get(module) or module
|
||||
hot_plugin_list.append(HotPlugin(module=module, 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 hot_plugin_list
|
||||
|
||||
@classmethod
|
||||
async def get_bot_block_module(cls, bot_id: str) -> BotBlockModule | None:
|
||||
"""获取bot层面的禁用模块
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
|
||||
返回:
|
||||
BotBlockModule | None: 数据内容
|
||||
"""
|
||||
bot_data = await BotConsole.get_or_none(bot_id=bot_id)
|
||||
if not bot_data:
|
||||
return None
|
||||
block_tasks = []
|
||||
block_plugins = []
|
||||
all_plugins = await PluginInfo.filter(
|
||||
load_status=True, plugin_type=PluginType.NORMAL
|
||||
).values("module", "name")
|
||||
all_task = await TaskInfo.annotate().values("module", "name")
|
||||
if bot_data.block_tasks:
|
||||
tasks = CommonUtils.convert_module_format(bot_data.block_tasks)
|
||||
block_tasks = [t["module"] for t in all_task if t["module"] in tasks]
|
||||
if bot_data.block_plugins:
|
||||
plugins = CommonUtils.convert_module_format(bot_data.block_plugins)
|
||||
block_plugins = [t["module"] for t in all_plugins if t["module"] in plugins]
|
||||
return BotBlockModule(
|
||||
bot_id=bot_id,
|
||||
block_tasks=block_tasks,
|
||||
block_plugins=block_plugins,
|
||||
all_plugins=all_plugins,
|
||||
all_tasks=all_task,
|
||||
)
|
||||
|
||||
@@ -1,8 +1,45 @@
|
||||
from typing import Any
|
||||
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.config import Config
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BotManageUpdateParam(BaseModel):
|
||||
"""bot更新参数"""
|
||||
|
||||
bot_id: str
|
||||
"""bot id"""
|
||||
block_plugins: list[str]
|
||||
"""禁用插件"""
|
||||
block_tasks: list[str]
|
||||
"""禁用被动"""
|
||||
|
||||
|
||||
class BotStatusParam(BaseModel):
|
||||
"""bot状态参数"""
|
||||
|
||||
bot_id: str
|
||||
"""bot id"""
|
||||
status: bool
|
||||
"""状态"""
|
||||
|
||||
|
||||
class BotBlockModule(BaseModel):
|
||||
"""bot禁用模块参数"""
|
||||
|
||||
bot_id: str
|
||||
"""bot id"""
|
||||
block_plugins: list[str]
|
||||
"""禁用插件"""
|
||||
block_tasks: list[str]
|
||||
"""禁用被动"""
|
||||
all_plugins: list[dict[str, Any]]
|
||||
"""所有插件"""
|
||||
all_tasks: list[dict[str, Any]]
|
||||
"""所有被动"""
|
||||
|
||||
|
||||
class SystemStatus(BaseModel):
|
||||
"""
|
||||
系统状态
|
||||
@@ -36,13 +73,8 @@ class BaseInfo(BaseModel):
|
||||
"""连接日期"""
|
||||
connect_count: int = 0
|
||||
"""连接次数"""
|
||||
|
||||
plugin_count: int = 0
|
||||
"""加载插件数量"""
|
||||
success_plugin_count: int = 0
|
||||
"""加载成功插件数量"""
|
||||
fail_plugin_count: int = 0
|
||||
"""加载失败插件数量"""
|
||||
status: bool = False
|
||||
"""全局状态"""
|
||||
|
||||
is_select: bool = False
|
||||
"""当前选择"""
|
||||
|
||||
@@ -2,16 +2,9 @@ from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
from tortoise.functions import Count
|
||||
|
||||
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.utils.enum import RequestHandleType, RequestType
|
||||
from zhenxun.utils.exception import NotFoundError
|
||||
@@ -20,20 +13,17 @@ from zhenxun.utils.platform import PlatformUtils
|
||||
from ....base_model import Result
|
||||
from ....config import AVA_URL, GROUP_AVA_URL
|
||||
from ....utils import authentication
|
||||
from .data_source import ApiDataSource
|
||||
from .model import (
|
||||
ClearRequest,
|
||||
DeleteFriend,
|
||||
Friend,
|
||||
FriendRequestResult,
|
||||
GroupDetail,
|
||||
GroupRequestResult,
|
||||
GroupResult,
|
||||
HandleRequest,
|
||||
LeaveGroup,
|
||||
Plugin,
|
||||
ReqResult,
|
||||
SendMessage,
|
||||
Task,
|
||||
SendMessageParam,
|
||||
UpdateGroup,
|
||||
UserDetail,
|
||||
)
|
||||
@@ -52,19 +42,21 @@ async def _(bot_id: str) -> Result:
|
||||
"""
|
||||
获取群信息
|
||||
"""
|
||||
if not (bots := nonebot.get_bots()):
|
||||
return Result.warning_("无Bot连接...")
|
||||
if bot_id not in bots:
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
group_list_result = []
|
||||
try:
|
||||
group_list = await bots[bot_id].get_group_list()
|
||||
bot = nonebot.get_bot(bot_id)
|
||||
group_list, _ = await PlatformUtils.get_group_list(bot)
|
||||
for g in group_list:
|
||||
gid = g["group_id"]
|
||||
g["ava_url"] = GROUP_AVA_URL.format(gid, gid)
|
||||
group_list_result.append(GroupResult(**g))
|
||||
ava_url = GROUP_AVA_URL.format(g.group_id, g.group_id)
|
||||
group_list_result.append(
|
||||
GroupResult(
|
||||
group_id=g.group_id, group_name=g.group_name, ava_url=ava_url
|
||||
)
|
||||
)
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_group_list", e=e)
|
||||
logger.error(f"{router.prefix}/get_group_list 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(group_list_result, "拿到了新鲜出炉的数据!")
|
||||
|
||||
@@ -78,25 +70,11 @@ async def _(bot_id: str) -> Result:
|
||||
)
|
||||
async def _(group: UpdateGroup) -> Result[str]:
|
||||
try:
|
||||
group_id = group.group_id
|
||||
if db_group := await GroupConsole.get_group(group_id):
|
||||
task_list = await TaskInfo.all().values_list("module", flat=True)
|
||||
db_group.level = group.level
|
||||
db_group.status = group.status
|
||||
if group.close_plugins:
|
||||
group.close_plugins = [f"<{module}" for module in group.close_plugins]
|
||||
db_group.block_plugin = ",".join(group.close_plugins) + ","
|
||||
if group.task:
|
||||
if block_task := [t for t in task_list if t not in group.task]:
|
||||
block_task = [f"<{module}" for module in block_task]
|
||||
db_group.block_task = ",".join(block_task) + "," # type: ignore
|
||||
await db_group.save(
|
||||
update_fields=["level", "status", "block_plugin", "block_task"]
|
||||
)
|
||||
await ApiDataSource.update_group(group)
|
||||
return Result.ok(info="已完成记录!")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_group", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(info="已完成记录!")
|
||||
logger.error(f"{router.prefix}/update_group 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -110,24 +88,24 @@ async def _(bot_id: str) -> Result[list[Friend]]:
|
||||
"""
|
||||
获取群信息
|
||||
"""
|
||||
if bots := nonebot.get_bots():
|
||||
if bot_id not in bots:
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
try:
|
||||
platform = PlatformUtils.get_platform(bots[bot_id])
|
||||
if platform != "qq":
|
||||
return Result.warning_("该平台暂不支持该功能...")
|
||||
friend_list = await bots[bot_id].get_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],
|
||||
"拿到了新鲜出炉的数据!",
|
||||
try:
|
||||
bot = nonebot.get_bot(bot_id)
|
||||
friend_list, _ = await PlatformUtils.get_friend_list(bot)
|
||||
result_list = []
|
||||
for f in friend_list:
|
||||
ava_url = AVA_URL.format(f.user_id)
|
||||
result_list.append(
|
||||
Friend(user_id=f.user_id, nickname=f.nickname, ava_url=ava_url)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_group_list", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.warning_("无Bot连接...")
|
||||
return Result.ok(
|
||||
result_list,
|
||||
"拿到了新鲜出炉的数据!",
|
||||
)
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_group_list", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -138,17 +116,21 @@ async def _(bot_id: str) -> Result[list[Friend]]:
|
||||
description="获取请求数量",
|
||||
)
|
||||
async def _() -> Result[dict[str, int]]:
|
||||
f_count = await FgRequest.filter(
|
||||
request_type=RequestType.FRIEND, handle_type__isnull=True
|
||||
).count()
|
||||
g_count = await FgRequest.filter(
|
||||
request_type=RequestType.GROUP, handle_type__isnull=True
|
||||
).count()
|
||||
data = {
|
||||
"friend_count": f_count,
|
||||
"group_count": g_count,
|
||||
}
|
||||
return Result.ok(data, f"{BotConfig.self_nickname}带来了最新的数据!")
|
||||
try:
|
||||
f_count = await FgRequest.filter(
|
||||
request_type=RequestType.FRIEND, handle_type__isnull=True
|
||||
).count()
|
||||
g_count = await FgRequest.filter(
|
||||
request_type=RequestType.GROUP, handle_type__isnull=True
|
||||
).count()
|
||||
data = {
|
||||
"friend_count": f_count,
|
||||
"group_count": g_count,
|
||||
}
|
||||
return Result.ok(data, "拿到了新鲜出炉的数据!")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_request_count", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -160,43 +142,10 @@ async def _() -> Result[dict[str, int]]:
|
||||
)
|
||||
async def _() -> Result[ReqResult]:
|
||||
try:
|
||||
req_result = ReqResult()
|
||||
data_list = await FgRequest.filter(handle_type__isnull=True).all()
|
||||
for req in data_list:
|
||||
if req.request_type == RequestType.FRIEND:
|
||||
req_result.friend.append(
|
||||
FriendRequestResult(
|
||||
oid=req.id,
|
||||
bot_id=req.bot_id,
|
||||
id=req.user_id,
|
||||
flag=req.flag,
|
||||
nickname=req.nickname,
|
||||
comment=req.comment,
|
||||
ava_url=AVA_URL.format(req.user_id),
|
||||
type=str(req.request_type).lower(),
|
||||
)
|
||||
)
|
||||
else:
|
||||
req_result.group.append(
|
||||
GroupRequestResult(
|
||||
oid=req.id,
|
||||
bot_id=req.bot_id,
|
||||
id=req.user_id,
|
||||
flag=req.flag,
|
||||
nickname=req.nickname,
|
||||
comment=req.comment,
|
||||
ava_url=GROUP_AVA_URL.format(req.group_id, req.group_id),
|
||||
type=str(req.request_type).lower(),
|
||||
invite_group=req.group_id,
|
||||
group_name=None,
|
||||
)
|
||||
)
|
||||
req_result.friend.reverse()
|
||||
req_result.group.reverse()
|
||||
return Result.ok(await ApiDataSource.get_request_list(), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_request", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(req_result, f"{BotConfig.self_nickname}带来了最新的数据!")
|
||||
logger.error(f"{router.prefix}/get_request_list 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -220,23 +169,21 @@ async def _(cr: ClearRequest) -> Result:
|
||||
response_class=JSONResponse,
|
||||
description="拒绝请求",
|
||||
)
|
||||
async def _(parma: HandleRequest) -> Result:
|
||||
async def _(param: HandleRequest) -> Result:
|
||||
try:
|
||||
if bots := nonebot.get_bots():
|
||||
bot_id = parma.bot_id
|
||||
if bot_id not in nonebot.get_bots():
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
try:
|
||||
await FgRequest.refused(bots[bot_id], parma.id)
|
||||
except ActionFailed:
|
||||
await FgRequest.expire(parma.id)
|
||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||
except NotFoundError:
|
||||
return Result.warning_("未找到此Id请求...")
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
return Result.warning_("无Bot连接...")
|
||||
bot = nonebot.get_bot(param.bot_id)
|
||||
try:
|
||||
await FgRequest.refused(bot, param.id)
|
||||
except ActionFailed:
|
||||
await FgRequest.expire(param.id)
|
||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||
except NotFoundError:
|
||||
return Result.warning_("未找到此Id请求...")
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/refuse_request", e=e)
|
||||
logger.error(f"{router.prefix}/refuse_request 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@@ -247,8 +194,8 @@ async def _(parma: HandleRequest) -> Result:
|
||||
response_class=JSONResponse,
|
||||
description="忽略请求",
|
||||
)
|
||||
async def _(parma: HandleRequest) -> Result:
|
||||
await FgRequest.ignore(parma.id)
|
||||
async def _(param: HandleRequest) -> Result:
|
||||
await FgRequest.ignore(param.id)
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
|
||||
|
||||
@@ -259,32 +206,30 @@ async def _(parma: HandleRequest) -> Result:
|
||||
response_class=JSONResponse,
|
||||
description="同意请求",
|
||||
)
|
||||
async def _(parma: HandleRequest) -> Result:
|
||||
async def _(param: HandleRequest) -> Result:
|
||||
try:
|
||||
if bots := nonebot.get_bots():
|
||||
bot_id = parma.bot_id
|
||||
if bot_id not in nonebot.get_bots():
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
if not (req := await FgRequest.get_or_none(id=parma.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:
|
||||
await FgRequest.approve(bots[bot_id], parma.id)
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
except ActionFailed:
|
||||
await FgRequest.expire(parma.id)
|
||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||
return Result.warning_("无Bot连接...")
|
||||
bot = nonebot.get_bot(param.bot_id)
|
||||
if not (req := await FgRequest.get_or_none(id=param.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:
|
||||
await FgRequest.approve(bot, param.id)
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
except ActionFailed:
|
||||
await FgRequest.expire(param.id)
|
||||
return Result.warning_("请求失败,可能该请求已失效或请求数据错误...")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/approve_request", e=e)
|
||||
logger.error(f"{router.prefix}/approve_request 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@@ -297,19 +242,19 @@ async def _(parma: HandleRequest) -> Result:
|
||||
)
|
||||
async def _(param: LeaveGroup) -> Result:
|
||||
try:
|
||||
if bots := nonebot.get_bots():
|
||||
bot_id = param.bot_id
|
||||
platform = PlatformUtils.get_platform(bots[bot_id])
|
||||
if platform != "qq":
|
||||
return Result.warning_("该平台不支持退群操作...")
|
||||
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连接...")
|
||||
bot = nonebot.get_bot(param.bot_id)
|
||||
platform = PlatformUtils.get_platform(bot)
|
||||
if platform != "qq":
|
||||
return Result.warning_("该平台不支持退群操作...")
|
||||
group_list, _ = await PlatformUtils.get_group_list(bot)
|
||||
if param.group_id not in [g.group_id for g in group_list]:
|
||||
return Result.warning_("Bot未在该群聊中...")
|
||||
await bot.set_group_leave(group_id=param.group_id)
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/leave_group", e=e)
|
||||
logger.error(f"{router.prefix}/leave_group 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@@ -322,19 +267,19 @@ async def _(param: LeaveGroup) -> Result:
|
||||
)
|
||||
async def _(param: DeleteFriend) -> Result:
|
||||
try:
|
||||
if bots := nonebot.get_bots():
|
||||
bot_id = param.bot_id
|
||||
platform = PlatformUtils.get_platform(bots[bot_id])
|
||||
if platform != "qq":
|
||||
return Result.warning_("该平台不支持删除好友操作...")
|
||||
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未连接...")
|
||||
bot = nonebot.get_bot(param.bot_id)
|
||||
platform = PlatformUtils.get_platform(bot)
|
||||
if platform != "qq":
|
||||
return Result.warning_("该平台不支持删除好友操作...")
|
||||
friend_list, _ = await PlatformUtils.get_friend_list(bot)
|
||||
if param.user_id not in [f.user_id for f in friend_list]:
|
||||
return Result.warning_("Bot未有其好友...")
|
||||
await bot.delete_friend(user_id=param.user_id)
|
||||
return Result.ok(info="成功处理了请求!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/delete_friend", e=e)
|
||||
logger.error(f"{router.prefix}/delete_friend 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@@ -346,43 +291,18 @@ async def _(param: DeleteFriend) -> Result:
|
||||
description="获取好友详情",
|
||||
)
|
||||
async def _(bot_id: str, user_id: str) -> Result[UserDetail]:
|
||||
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 = {}
|
||||
module_list = [x[0] for x in like_plugin_list]
|
||||
plugins = await PluginInfo.filter(module__in=module_list).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]
|
||||
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 BanConsole.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连接...")
|
||||
try:
|
||||
result = await ApiDataSource.get_friend_detail(bot_id, user_id)
|
||||
return (
|
||||
Result.ok(result, "拿到信息啦!")
|
||||
if result
|
||||
else Result.warning_("未找到该好友...")
|
||||
)
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_friend_detail 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -392,90 +312,12 @@ async def _(bot_id: str, user_id: str) -> Result[UserDetail]:
|
||||
response_class=JSONResponse,
|
||||
description="获取群组详情",
|
||||
)
|
||||
async def _(bot_id: str, group_id: str) -> Result[GroupDetail]:
|
||||
if not (bots := nonebot.get_bots()):
|
||||
return Result.warning_("无Bot连接...")
|
||||
if bot_id not in bots:
|
||||
return Result.warning_("未添加指定群组...")
|
||||
group = await GroupConsole.get_or_none(group_id=group_id)
|
||||
if not group:
|
||||
return Result.warning_("指定群组未被收录...")
|
||||
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 = {}
|
||||
plugins = await PluginInfo.get_plugins()
|
||||
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: list[Plugin] = []
|
||||
if group.block_plugin:
|
||||
for module in group.block_plugin.replace("<", "").split(","):
|
||||
if module:
|
||||
plugin = Plugin(
|
||||
module=module,
|
||||
plugin_name=module,
|
||||
is_super_block=False,
|
||||
)
|
||||
plugin.plugin_name = module2name.get(module) or module
|
||||
close_plugins.append(plugin)
|
||||
exists_modules = [p.module for p in close_plugins]
|
||||
if group.superuser_block_plugin:
|
||||
for module in group.superuser_block_plugin.replace("<", "").split(","):
|
||||
if module and module not in exists_modules:
|
||||
plugin = Plugin(
|
||||
module=module,
|
||||
plugin_name=module,
|
||||
is_super_block=True,
|
||||
)
|
||||
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 or group.superuser_block_plugin:
|
||||
sbp = group.superuser_block_plugin.replace("<", "").split(",")
|
||||
split_task = group.block_task.replace("<", "").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 and task[0] not in sbp,
|
||||
is_super_block=task[0] in sbp,
|
||||
)
|
||||
)
|
||||
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,
|
||||
is_super_block=False,
|
||||
)
|
||||
)
|
||||
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)
|
||||
async def _(group_id: str) -> Result[GroupDetail]:
|
||||
try:
|
||||
return Result.ok(await ApiDataSource.get_group_detail(group_id), "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_group_detail 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -483,25 +325,17 @@ async def _(bot_id: str, group_id: str) -> Result[GroupDetail]:
|
||||
dependencies=[authentication()],
|
||||
response_model=Result,
|
||||
response_class=JSONResponse,
|
||||
description="获取群组详情",
|
||||
description="发送消息",
|
||||
)
|
||||
async def _(param: SendMessage) -> Result:
|
||||
if not (bots := nonebot.get_bots()):
|
||||
return Result.warning_("无Bot连接...")
|
||||
if param.bot_id in bots:
|
||||
platform = PlatformUtils.get_platform(bots[param.bot_id])
|
||||
if platform != "qq":
|
||||
return Result.warning_("暂不支持该平台...")
|
||||
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))
|
||||
async def _(param: SendMessageParam) -> Result:
|
||||
try:
|
||||
bot = nonebot.get_bot(param.bot_id)
|
||||
await PlatformUtils.send_message(
|
||||
bot, param.user_id, param.group_id, param.message
|
||||
)
|
||||
return Result.ok("发送成功!")
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except (ValueError, KeyError):
|
||||
return Result.warning_("指定Bot未连接...")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/send_message 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
@@ -3,7 +3,7 @@ import nonebot
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot_plugin_alconna import At, Hyper, Image, Text, UniMsg
|
||||
from nonebot_plugin_session import EventSession
|
||||
from nonebot_plugin_uninfo import Uninfo
|
||||
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
|
||||
|
||||
from zhenxun.models.group_member_info import GroupInfoUser
|
||||
@@ -28,7 +28,7 @@ matcher = on_message(block=False, priority=1, rule=lambda: bool(ws_conn))
|
||||
|
||||
@driver.on_shutdown
|
||||
async def _():
|
||||
if ws_conn:
|
||||
if ws_conn and ws_conn.client_state == WebSocketState.CONNECTED:
|
||||
await ws_conn.close()
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ async def _():
|
||||
async def _(websocket: WebSocket):
|
||||
global ws_conn
|
||||
await websocket.accept()
|
||||
if not ws_conn:
|
||||
if not ws_conn or ws_conn.client_state != WebSocketState.CONNECTED:
|
||||
ws_conn = websocket
|
||||
try:
|
||||
while websocket.client_state == WebSocketState.CONNECTED:
|
||||
@@ -80,25 +80,24 @@ async def message_handle(
|
||||
|
||||
@matcher.handle()
|
||||
async def _(
|
||||
message: UniMsg, event: MessageEvent, session: EventSession, uname: str = UserName()
|
||||
message: UniMsg, event: MessageEvent, session: Uninfo, uname: str = UserName()
|
||||
):
|
||||
global ws_conn, ID2NAME, ID_LIST
|
||||
uid = session.id1
|
||||
if ws_conn and ws_conn.client_state == WebSocketState.CONNECTED and uid:
|
||||
if ws_conn and ws_conn.client_state == WebSocketState.CONNECTED:
|
||||
msg_id = event.message_id
|
||||
if msg_id in ID_LIST:
|
||||
return
|
||||
ID_LIST.append(msg_id)
|
||||
if len(ID_LIST) > 50:
|
||||
ID_LIST = ID_LIST[40:]
|
||||
gid = session.id3 or session.id2
|
||||
gid = session.group.id if session.group else None
|
||||
messages = await message_handle(message, gid)
|
||||
data = Message(
|
||||
object_id=gid or uid,
|
||||
user_id=uid,
|
||||
object_id=gid or session.user.id,
|
||||
user_id=session.user.id,
|
||||
group_id=gid,
|
||||
message=messages,
|
||||
name=uname,
|
||||
ava_url=AVA_URL.format(uid),
|
||||
ava_url=AVA_URL.format(session.user.id),
|
||||
)
|
||||
await ws_conn.send_json(data.dict())
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
import nonebot
|
||||
from tortoise.functions import Count
|
||||
|
||||
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.utils.common_utils import CommonUtils
|
||||
from zhenxun.utils.enum import RequestType
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
|
||||
from ....config import AVA_URL, GROUP_AVA_URL
|
||||
from .model import (
|
||||
FriendRequestResult,
|
||||
GroupDetail,
|
||||
GroupRequestResult,
|
||||
Plugin,
|
||||
ReqResult,
|
||||
Task,
|
||||
UpdateGroup,
|
||||
UserDetail,
|
||||
)
|
||||
|
||||
|
||||
class ApiDataSource:
|
||||
@classmethod
|
||||
async def update_group(cls, group: UpdateGroup):
|
||||
"""更新群组数据
|
||||
|
||||
参数:
|
||||
group: UpdateGroup
|
||||
"""
|
||||
db_group = await GroupConsole.get_group(group.group_id) or GroupConsole(
|
||||
group_id=group.group_id
|
||||
)
|
||||
task_list = await TaskInfo.all().values_list("module", flat=True)
|
||||
db_group.level = group.level
|
||||
db_group.status = group.status
|
||||
if group.close_plugins:
|
||||
db_group.block_plugin = CommonUtils.convert_module_format(
|
||||
group.close_plugins
|
||||
)
|
||||
else:
|
||||
db_group.block_plugin = ""
|
||||
if group.task:
|
||||
if block_task := [t for t in task_list if t not in group.task]:
|
||||
db_group.block_task = CommonUtils.convert_module_format(block_task) # type: ignore
|
||||
else:
|
||||
db_group.block_task = CommonUtils.convert_module_format(task_list) # type: ignore
|
||||
await db_group.save()
|
||||
|
||||
@classmethod
|
||||
async def get_request_list(cls) -> ReqResult:
|
||||
"""获取好友与群组请求列表
|
||||
|
||||
返回:
|
||||
ReqResult: 数据内容
|
||||
"""
|
||||
req_result = ReqResult()
|
||||
data_list = await FgRequest.filter(handle_type__isnull=True).all()
|
||||
for req in data_list:
|
||||
if req.request_type == RequestType.FRIEND:
|
||||
req_result.friend.append(
|
||||
FriendRequestResult(
|
||||
oid=req.id,
|
||||
bot_id=req.bot_id,
|
||||
id=req.user_id,
|
||||
flag=req.flag,
|
||||
nickname=req.nickname,
|
||||
comment=req.comment,
|
||||
ava_url=AVA_URL.format(req.user_id),
|
||||
type=str(req.request_type).lower(),
|
||||
)
|
||||
)
|
||||
else:
|
||||
req_result.group.append(
|
||||
GroupRequestResult(
|
||||
oid=req.id,
|
||||
bot_id=req.bot_id,
|
||||
id=req.user_id,
|
||||
flag=req.flag,
|
||||
nickname=req.nickname,
|
||||
comment=req.comment,
|
||||
ava_url=GROUP_AVA_URL.format(req.group_id, req.group_id),
|
||||
type=str(req.request_type).lower(),
|
||||
invite_group=req.group_id,
|
||||
group_name=None,
|
||||
)
|
||||
)
|
||||
req_result.friend.reverse()
|
||||
req_result.group.reverse()
|
||||
return req_result
|
||||
|
||||
@classmethod
|
||||
async def get_friend_detail(cls, bot_id: str, user_id: str) -> UserDetail | None:
|
||||
"""获取好友详情
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
user_id: 用户id
|
||||
|
||||
返回:
|
||||
UserDetail | None: 详情数据
|
||||
"""
|
||||
bot = nonebot.get_bot(bot_id)
|
||||
friend_list, _ = await PlatformUtils.get_friend_list(bot)
|
||||
fd = [x for x in friend_list if x == user_id]
|
||||
if not fd:
|
||||
return None
|
||||
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 = {}
|
||||
module_list = [x[0] for x in like_plugin_list]
|
||||
plugins = await PluginInfo.filter(module__in=module_list).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]
|
||||
user = fd[0]
|
||||
return UserDetail(
|
||||
user_id=user_id,
|
||||
ava_url=AVA_URL.format(user_id),
|
||||
nickname=user.user_name,
|
||||
remark="",
|
||||
is_ban=await BanConsole.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,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def __get_group_detail_like_plugin(cls, group_id: str) -> dict[str, int]:
|
||||
"""获取群组喜爱的插件
|
||||
|
||||
参数:
|
||||
group_id: 群组id
|
||||
|
||||
返回:
|
||||
dict[str, int]: 插件与调用次数
|
||||
"""
|
||||
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 = {}
|
||||
plugins = await PluginInfo.get_plugins()
|
||||
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]
|
||||
return like_plugin
|
||||
|
||||
@classmethod
|
||||
async def __get_group_detail_disable_plugin(
|
||||
cls, group: GroupConsole
|
||||
) -> list[Plugin]:
|
||||
"""获取群组禁用插件
|
||||
|
||||
参数:
|
||||
group: GroupConsole
|
||||
|
||||
返回:
|
||||
list[Plugin]: 禁用插件数据列表
|
||||
"""
|
||||
disable_plugins: list[Plugin] = []
|
||||
plugins = await PluginInfo.get_plugins()
|
||||
module2name = {p.module: p.name for p in plugins}
|
||||
if group.block_plugin:
|
||||
for module in CommonUtils.convert_module_format(group.block_plugin):
|
||||
if module:
|
||||
plugin = Plugin(
|
||||
module=module,
|
||||
plugin_name=module,
|
||||
is_super_block=False,
|
||||
)
|
||||
plugin.plugin_name = module2name.get(module) or module
|
||||
disable_plugins.append(plugin)
|
||||
exists_modules = [p.module for p in disable_plugins]
|
||||
if group.superuser_block_plugin:
|
||||
for module in CommonUtils.convert_module_format(
|
||||
group.superuser_block_plugin
|
||||
):
|
||||
if module and module not in exists_modules:
|
||||
plugin = Plugin(
|
||||
module=module,
|
||||
plugin_name=module,
|
||||
is_super_block=True,
|
||||
)
|
||||
plugin.plugin_name = module2name.get(module) or module
|
||||
disable_plugins.append(plugin)
|
||||
return disable_plugins
|
||||
|
||||
@classmethod
|
||||
async def __get_group_detail_task(cls, group: GroupConsole) -> list[Task]:
|
||||
"""获取群组被动技能状态
|
||||
|
||||
参数:
|
||||
group: GroupConsole
|
||||
|
||||
返回:
|
||||
list[Task]: 群组被动列表
|
||||
"""
|
||||
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 or group.superuser_block_plugin:
|
||||
sbp = CommonUtils.convert_module_format(group.superuser_block_task)
|
||||
tasks = CommonUtils.convert_module_format(group.block_task)
|
||||
task_list.extend(
|
||||
Task(
|
||||
name=task[0],
|
||||
zh_name=task_module2name.get(task[0]) or task[0],
|
||||
status=task[0] not in tasks and task[0] not in sbp,
|
||||
is_super_block=task[0] in sbp,
|
||||
)
|
||||
for task in all_task
|
||||
)
|
||||
else:
|
||||
task_list.extend(
|
||||
Task(
|
||||
name=task[0],
|
||||
zh_name=task_module2name.get(task[0]) or task[0],
|
||||
status=True,
|
||||
is_super_block=False,
|
||||
)
|
||||
for task in all_task
|
||||
)
|
||||
return task_list
|
||||
|
||||
@classmethod
|
||||
async def get_group_detail(cls, group_id: str) -> GroupDetail | None:
|
||||
"""获取群组详情
|
||||
|
||||
参数:
|
||||
group_id: 群组id
|
||||
|
||||
返回:
|
||||
GroupDetail | None: 群组详情数据
|
||||
"""
|
||||
group = await GroupConsole.get_or_none(group_id=group_id)
|
||||
if not group:
|
||||
return None
|
||||
like_plugin = await cls.__get_group_detail_like_plugin(group_id)
|
||||
disable_plugins: list[Plugin] = await cls.__get_group_detail_disable_plugin(
|
||||
group
|
||||
)
|
||||
task_list = await cls.__get_group_detail_task(group)
|
||||
return 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=disable_plugins,
|
||||
task=task_list,
|
||||
)
|
||||
@@ -257,7 +257,7 @@ class Message(BaseModel):
|
||||
"""用户头像"""
|
||||
|
||||
|
||||
class SendMessage(BaseModel):
|
||||
class SendMessageParam(BaseModel):
|
||||
"""
|
||||
发送消息
|
||||
"""
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import re
|
||||
|
||||
import cattrs
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.enum import BlockType, PluginType
|
||||
|
||||
from ....base_model import Result
|
||||
from ....utils import authentication
|
||||
from .data_source import ApiDataSource
|
||||
from .model import (
|
||||
PluginConfig,
|
||||
PluginCount,
|
||||
PluginDetail,
|
||||
PluginInfo,
|
||||
@@ -34,31 +30,12 @@ async def _(
|
||||
plugin_type: list[PluginType] = Query(None), menu_type: str | None = None
|
||||
) -> Result[list[PluginInfo]]:
|
||||
try:
|
||||
plugin_list: list[PluginInfo] = []
|
||||
query = DbPluginInfo
|
||||
if plugin_type:
|
||||
query = query.filter(plugin_type__in=plugin_type, load_status=True)
|
||||
if menu_type:
|
||||
query = query.filter(menu_type=menu_type)
|
||||
plugins = await query.all()
|
||||
for plugin in plugins:
|
||||
plugin_info = PluginInfo(
|
||||
module=plugin.module,
|
||||
plugin_name=plugin.name,
|
||||
default_status=plugin.default_status,
|
||||
limit_superuser=plugin.limit_superuser,
|
||||
cost_gold=plugin.cost_gold,
|
||||
menu_type=plugin.menu_type,
|
||||
version=plugin.version or "0",
|
||||
level=plugin.level,
|
||||
status=plugin.status,
|
||||
author=plugin.author,
|
||||
)
|
||||
plugin_list.append(plugin_info)
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_plugin_list(plugin_type, menu_type), "拿到信息啦!"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_plugins", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(plugin_list, "拿到了新鲜出炉的数据!")
|
||||
logger.error(f"{router.prefix}/get_plugin_list 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -69,21 +46,26 @@ async def _(
|
||||
deprecated="获取插件数量", # type: ignore
|
||||
)
|
||||
async def _() -> Result[int]:
|
||||
plugin_count = PluginCount()
|
||||
plugin_count.normal = await DbPluginInfo.filter(
|
||||
plugin_type=PluginType.NORMAL, load_status=True
|
||||
).count()
|
||||
plugin_count.admin = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.ADMIN, PluginType.SUPER_AND_ADMIN], load_status=True
|
||||
).count()
|
||||
plugin_count.superuser = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.SUPERUSER, PluginType.SUPER_AND_ADMIN],
|
||||
load_status=True,
|
||||
).count()
|
||||
plugin_count.other = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.HIDDEN, PluginType.DEPENDANT], load_status=True
|
||||
).count()
|
||||
return Result.ok(plugin_count)
|
||||
try:
|
||||
plugin_count = PluginCount()
|
||||
plugin_count.normal = await DbPluginInfo.filter(
|
||||
plugin_type=PluginType.NORMAL, load_status=True
|
||||
).count()
|
||||
plugin_count.admin = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.ADMIN, PluginType.SUPER_AND_ADMIN],
|
||||
load_status=True,
|
||||
).count()
|
||||
plugin_count.superuser = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.SUPERUSER, PluginType.SUPER_AND_ADMIN],
|
||||
load_status=True,
|
||||
).count()
|
||||
plugin_count.other = await DbPluginInfo.filter(
|
||||
plugin_type__in=[PluginType.HIDDEN, PluginType.DEPENDANT], load_status=True
|
||||
).count()
|
||||
return Result.ok(plugin_count, "拿到信息啦!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_plugin_count 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -93,33 +75,15 @@ async def _() -> Result[int]:
|
||||
response_class=JSONResponse,
|
||||
description="更新插件参数",
|
||||
)
|
||||
async def _(plugin: UpdatePlugin) -> Result:
|
||||
async def _(param: UpdatePlugin) -> Result:
|
||||
try:
|
||||
db_plugin = await DbPluginInfo.get_or_none(
|
||||
module=plugin.module, load_status=True
|
||||
)
|
||||
if not db_plugin:
|
||||
return Result.fail("插件不存在...")
|
||||
db_plugin.default_status = plugin.default_status
|
||||
db_plugin.limit_superuser = plugin.limit_superuser
|
||||
db_plugin.cost_gold = plugin.cost_gold
|
||||
db_plugin.level = plugin.level
|
||||
db_plugin.menu_type = plugin.menu_type
|
||||
db_plugin.block_type = plugin.block_type
|
||||
db_plugin.status = plugin.block_type != BlockType.ALL
|
||||
await db_plugin.save()
|
||||
# 配置项
|
||||
if plugin.configs and (configs := Config.get(plugin.module)):
|
||||
for key in plugin.configs:
|
||||
if c := configs.configs.get(key):
|
||||
value = plugin.configs[key]
|
||||
if c.type and value is not None:
|
||||
value = cattrs.structure(value, c.type)
|
||||
Config.set_config(plugin.module, key, value)
|
||||
await ApiDataSource.update_plugin(param)
|
||||
return Result.ok(info="已经帮你写好啦!")
|
||||
except (ValueError, KeyError):
|
||||
return Result.fail("插件数据不存在...")
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/update_plugins", e=e)
|
||||
logger.error(f"{router.prefix}/update_plugin 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(info="已经帮你写好啦!")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -130,17 +94,21 @@ async def _(plugin: UpdatePlugin) -> Result:
|
||||
description="开关插件",
|
||||
)
|
||||
async def _(param: PluginSwitch) -> Result:
|
||||
db_plugin = await DbPluginInfo.get_or_none(module=param.module, load_status=True)
|
||||
if not db_plugin:
|
||||
return Result.fail("插件不存在...")
|
||||
if not param.status:
|
||||
db_plugin.block_type = BlockType.ALL
|
||||
db_plugin.status = False
|
||||
else:
|
||||
db_plugin.block_type = None
|
||||
db_plugin.status = True
|
||||
await db_plugin.save()
|
||||
return Result.ok(info="成功改变了开关状态!")
|
||||
try:
|
||||
db_plugin = await DbPluginInfo.get_plugin(module=param.module)
|
||||
if not db_plugin:
|
||||
return Result.fail("插件不存在...")
|
||||
if not param.status:
|
||||
db_plugin.block_type = BlockType.ALL
|
||||
db_plugin.status = False
|
||||
else:
|
||||
db_plugin.block_type = None
|
||||
db_plugin.status = True
|
||||
await db_plugin.save()
|
||||
return Result.ok(info="成功改变了开关状态!")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/change_switch 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -151,12 +119,20 @@ async def _(param: PluginSwitch) -> Result:
|
||||
description="获取插件类型",
|
||||
)
|
||||
async def _() -> Result[list[str]]:
|
||||
menu_type_list = []
|
||||
result = await DbPluginInfo.annotate().values_list("menu_type", flat=True)
|
||||
for r in result:
|
||||
if r not in menu_type_list and r:
|
||||
menu_type_list.append(r)
|
||||
return Result.ok(menu_type_list)
|
||||
try:
|
||||
menu_type_list = []
|
||||
result = (
|
||||
await DbPluginInfo.filter(load_status=True)
|
||||
.annotate()
|
||||
.values_list("menu_type", flat=True)
|
||||
)
|
||||
for r in result:
|
||||
if r not in menu_type_list and r:
|
||||
menu_type_list.append(r)
|
||||
return Result.ok(menu_type_list)
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_plugin_menu_type 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -167,46 +143,12 @@ async def _() -> Result[list[str]]:
|
||||
description="获取插件详情",
|
||||
)
|
||||
async def _(module: str) -> Result[PluginDetail]:
|
||||
db_plugin = await DbPluginInfo.get_or_none(module=module, load_status=True)
|
||||
if not db_plugin:
|
||||
return Result.fail("插件不存在...")
|
||||
config_list = []
|
||||
if config := Config.get(module):
|
||||
for cfg in config.configs:
|
||||
type_str = ""
|
||||
type_inner = None
|
||||
if r := re.search(r"<class '(.*)'>", str(config.configs[cfg].type)):
|
||||
type_str = r[1]
|
||||
elif r := re.search(r"typing\.(.*)\[(.*)\]", str(config.configs[cfg].type)):
|
||||
type_str = r[1]
|
||||
if type_str:
|
||||
type_str = type_str.lower()
|
||||
type_inner = r[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, # type: ignore
|
||||
)
|
||||
)
|
||||
plugin_info = PluginDetail(
|
||||
module=module,
|
||||
plugin_name=db_plugin.name,
|
||||
default_status=db_plugin.default_status,
|
||||
limit_superuser=db_plugin.limit_superuser,
|
||||
cost_gold=db_plugin.cost_gold,
|
||||
menu_type=db_plugin.menu_type,
|
||||
version=db_plugin.version or "0",
|
||||
level=db_plugin.level,
|
||||
status=db_plugin.status,
|
||||
author=db_plugin.author,
|
||||
config_list=config_list,
|
||||
block_type=db_plugin.block_type,
|
||||
)
|
||||
return Result.ok(plugin_info)
|
||||
try:
|
||||
return Result.ok(
|
||||
await ApiDataSource.get_plugin_detail(module), "已经帮你写好啦!"
|
||||
)
|
||||
except (ValueError, KeyError):
|
||||
return Result.fail("插件数据不存在...")
|
||||
except Exception as e:
|
||||
logger.error(f"{router.prefix}/get_plugin 调用错误", "WebUi", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import re
|
||||
|
||||
import cattrs
|
||||
from fastapi import Query
|
||||
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.configs.utils import ConfigGroup
|
||||
from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo
|
||||
from zhenxun.utils.enum import BlockType, PluginType
|
||||
|
||||
from .model import PluginConfig, PluginDetail, PluginInfo, UpdatePlugin
|
||||
|
||||
|
||||
class ApiDataSource:
|
||||
@classmethod
|
||||
async def get_plugin_list(
|
||||
cls, plugin_type: list[PluginType] = Query(None), menu_type: str | None = None
|
||||
) -> list[PluginInfo]:
|
||||
"""获取插件列表
|
||||
|
||||
参数:
|
||||
plugin_type: 插件类型.
|
||||
menu_type: 菜单类型.
|
||||
|
||||
返回:
|
||||
list[PluginInfo]: 插件数据列表
|
||||
"""
|
||||
plugin_list: list[PluginInfo] = []
|
||||
query = DbPluginInfo
|
||||
if plugin_type:
|
||||
query = query.filter(plugin_type__in=plugin_type, load_status=True)
|
||||
if menu_type:
|
||||
query = query.filter(menu_type=menu_type, load_status=True)
|
||||
plugins = await query.all()
|
||||
for plugin in plugins:
|
||||
plugin_info = PluginInfo(
|
||||
module=plugin.module,
|
||||
plugin_name=plugin.name,
|
||||
default_status=plugin.default_status,
|
||||
limit_superuser=plugin.limit_superuser,
|
||||
cost_gold=plugin.cost_gold,
|
||||
menu_type=plugin.menu_type,
|
||||
version=plugin.version or "0",
|
||||
level=plugin.level,
|
||||
status=plugin.status,
|
||||
author=plugin.author,
|
||||
)
|
||||
plugin_list.append(plugin_info)
|
||||
return plugin_list
|
||||
|
||||
@classmethod
|
||||
async def update_plugin(cls, param: UpdatePlugin) -> DbPluginInfo:
|
||||
"""更新插件数据
|
||||
|
||||
参数:
|
||||
param: UpdatePlugin
|
||||
|
||||
返回:
|
||||
DbPluginInfo | None: 插件数据
|
||||
"""
|
||||
db_plugin = await DbPluginInfo.get_plugin(module=param.module)
|
||||
if not db_plugin:
|
||||
raise ValueError("插件不存在")
|
||||
db_plugin.default_status = param.default_status
|
||||
db_plugin.limit_superuser = param.limit_superuser
|
||||
db_plugin.cost_gold = param.cost_gold
|
||||
db_plugin.level = param.level
|
||||
db_plugin.menu_type = param.menu_type
|
||||
db_plugin.block_type = param.block_type
|
||||
db_plugin.status = param.block_type != BlockType.ALL
|
||||
await db_plugin.save()
|
||||
# 配置项
|
||||
if param.configs and (configs := Config.get(param.module)):
|
||||
for key in param.configs:
|
||||
if c := configs.configs.get(key):
|
||||
value = param.configs[key]
|
||||
if c.type and value is not None:
|
||||
value = cattrs.structure(value, c.type)
|
||||
Config.set_config(param.module, key, value)
|
||||
Config.save(save_simple_data=True)
|
||||
return db_plugin
|
||||
|
||||
@classmethod
|
||||
def __build_plugin_config(
|
||||
cls, module: str, cfg: str, config: ConfigGroup
|
||||
) -> PluginConfig:
|
||||
"""获取插件配置项
|
||||
|
||||
参数:
|
||||
module: 模块名
|
||||
cfg: cfg
|
||||
config: ConfigGroup
|
||||
|
||||
返回:
|
||||
lPluginConfig: 配置数据
|
||||
"""
|
||||
type_str = ""
|
||||
type_inner = None
|
||||
if r := re.search(r"<class '(.*)'>", str(config.configs[cfg].type)):
|
||||
type_str = r[1]
|
||||
elif r := re.search(r"typing\.(.*)\[(.*)\]", str(config.configs[cfg].type)):
|
||||
type_str = r[1]
|
||||
if type_str:
|
||||
type_str = type_str.lower()
|
||||
type_inner = r[2]
|
||||
if type_inner:
|
||||
type_inner = [x.strip() for x in type_inner.split(",")]
|
||||
return 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, # type: ignore
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_plugin_detail(cls, module: str) -> PluginDetail:
|
||||
"""获取插件详情
|
||||
|
||||
参数:
|
||||
module: 模块名
|
||||
|
||||
异常:
|
||||
ValueError: 插件不存在
|
||||
|
||||
返回:
|
||||
PluginDetail: 插件详情数据
|
||||
"""
|
||||
db_plugin = await DbPluginInfo.get_plugin(module=module)
|
||||
if not db_plugin:
|
||||
raise ValueError("插件不存在")
|
||||
config_list = []
|
||||
if config := Config.get(module):
|
||||
config_list.extend(
|
||||
cls.__build_plugin_config(module, cfg, config) for cfg in config.configs
|
||||
)
|
||||
return PluginDetail(
|
||||
module=module,
|
||||
plugin_name=db_plugin.name,
|
||||
default_status=db_plugin.default_status,
|
||||
limit_superuser=db_plugin.limit_superuser,
|
||||
cost_gold=db_plugin.cost_gold,
|
||||
menu_type=db_plugin.menu_type,
|
||||
version=db_plugin.version or "0",
|
||||
level=db_plugin.level,
|
||||
status=db_plugin.status,
|
||||
author=db_plugin.author,
|
||||
config_list=config_list,
|
||||
block_type=db_plugin.block_type,
|
||||
)
|
||||
@@ -31,7 +31,7 @@ class Result(Generic[RT], BaseModel):
|
||||
"""info"""
|
||||
warning: str | None = None
|
||||
"""警告信息"""
|
||||
data: RT = None
|
||||
data: RT | None = None
|
||||
"""返回数据"""
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user