mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-30 17:20:03 +08:00
webui主页和插件管理api更新
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from .database import *
|
||||
from .main import *
|
||||
from .manage import *
|
||||
from .plugin_manage import *
|
||||
|
||||
@@ -7,21 +7,26 @@ import nonebot
|
||||
from fastapi import APIRouter, WebSocket
|
||||
from nonebot.utils import escape_tag
|
||||
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
|
||||
from tortoise.functions import Count
|
||||
from websockets.exceptions import ConnectionClosedError, ConnectionClosedOK
|
||||
|
||||
from configs.config import NICKNAME
|
||||
from models.chat_history import ChatHistory
|
||||
from models.group_info import GroupInfo
|
||||
from models.statistics import Statistics
|
||||
from services.log import logger
|
||||
from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager
|
||||
from utils.manager.models import PluginData, PluginType
|
||||
|
||||
from ....config import QueryDateType
|
||||
from ....base_model import Result
|
||||
from ....config import QueryDateType
|
||||
from ....utils import authentication, get_system_status
|
||||
from .data_source import bot_live
|
||||
from .model import BaseInfo
|
||||
from .model import ActiveGroup, BaseInfo, ChatHistoryCount, HotPlugin
|
||||
|
||||
AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160"
|
||||
|
||||
GROUP_AVA_URL = "http://p.qlogo.cn/gh/{}/{}/640/"
|
||||
|
||||
run_time = time.time()
|
||||
|
||||
ws_router = APIRouter()
|
||||
@@ -34,7 +39,7 @@ async def _(bot_id: Optional[str] = None) -> Result:
|
||||
获取Bot基础信息
|
||||
|
||||
Args:
|
||||
qq (Optional[str], optional): qq号. Defaults to None.
|
||||
bot_id (Optional[str], optional): bot_id. Defaults to None.
|
||||
|
||||
Returns:
|
||||
Result: 获取指定bot信息与bot列表
|
||||
@@ -43,20 +48,22 @@ async def _(bot_id: Optional[str] = None) -> Result:
|
||||
if bots := nonebot.get_bots():
|
||||
select_bot: BaseInfo
|
||||
for key, bot in bots.items():
|
||||
login_info = await bot.get_login_info()
|
||||
bot_list.append(
|
||||
BaseInfo(
|
||||
bot=bot, # type: ignore
|
||||
self_id=bot.self_id,
|
||||
nickname=NICKNAME,
|
||||
nickname=login_info["nickname"],
|
||||
ava_url=AVA_URL.format(bot.self_id),
|
||||
)
|
||||
)
|
||||
# 获取指定qq号的bot信息,若无指定则获取第一个
|
||||
# 获取指定qq号的bot信息,若无指定 则获取第一个
|
||||
if _bl := [b for b in bot_list if b.self_id == bot_id]:
|
||||
select_bot = _bl[0]
|
||||
else:
|
||||
select_bot = bot_list[0]
|
||||
select_bot.is_select = True
|
||||
select_bot.config = select_bot.bot.config
|
||||
now = datetime.now()
|
||||
# 今日累计接收消息
|
||||
select_bot.received_messages = await ChatHistory.filter(
|
||||
@@ -78,11 +85,43 @@ async def _(bot_id: Optional[str] = None) -> Result:
|
||||
)
|
||||
# 连接时间
|
||||
select_bot.connect_time = bot_live.get(select_bot.self_id) or 0
|
||||
if select_bot.connect_time:
|
||||
connect_date = datetime.fromtimestamp(select_bot.connect_time)
|
||||
select_bot.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
return Result.ok(bot_list, "已获取操作列表")
|
||||
return Result.ok(bot_list, "拿到信息啦!")
|
||||
return Result.warning_("无Bot连接...")
|
||||
|
||||
|
||||
@router.get(
|
||||
"/get_all_ch_count", dependencies=[authentication()], description="获取接收消息数量"
|
||||
)
|
||||
async def _(bot_id: str) -> Result:
|
||||
now = datetime.now()
|
||||
all_count = await ChatHistory.filter(bot_id=bot_id).count()
|
||||
day_count = await ChatHistory.filter(
|
||||
bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour)
|
||||
).count()
|
||||
week_count = await ChatHistory.filter(
|
||||
bot_id=bot_id, create_time__gte=now - timedelta(days=7)
|
||||
).count()
|
||||
month_count = await ChatHistory.filter(
|
||||
bot_id=bot_id, create_time__gte=now - timedelta(days=30)
|
||||
).count()
|
||||
year_count = await ChatHistory.filter(
|
||||
bot_id=bot_id, create_time__gte=now - timedelta(days=365)
|
||||
).count()
|
||||
return Result.ok(
|
||||
ChatHistoryCount(
|
||||
num=all_count,
|
||||
day=day_count,
|
||||
week=week_count,
|
||||
month=month_count,
|
||||
year=year_count,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.get("/get_ch_count", dependencies=[authentication()], description="获取接收消息数量")
|
||||
async def _(bot_id: str, query_type: Optional[QueryDateType] = None) -> Result:
|
||||
if bots := nonebot.get_bots():
|
||||
@@ -135,6 +174,81 @@ async def _() -> Result:
|
||||
return Result.ok(int(time.time() - run_time))
|
||||
|
||||
|
||||
@router.get("/get_active_group", dependencies=[authentication()], description="获取活跃群聊")
|
||||
async def _(date_type: Optional[QueryDateType] = None) -> Result:
|
||||
query = ChatHistory
|
||||
now = datetime.now()
|
||||
if date_type == QueryDateType.DAY:
|
||||
query = ChatHistory.filter(create_time__gte=now - timedelta(hours=now.hour))
|
||||
if date_type == QueryDateType.WEEK:
|
||||
query = ChatHistory.filter(create_time__gte=now - timedelta(days=7))
|
||||
if date_type == QueryDateType.MONTH:
|
||||
query = ChatHistory.filter(create_time__gte=now - timedelta(days=30))
|
||||
if date_type == QueryDateType.YEAR:
|
||||
query = ChatHistory.filter(create_time__gte=now - timedelta(days=365))
|
||||
data_list = (
|
||||
await query.annotate(count=Count("id"))
|
||||
.group_by("group_id").order_by("-count").limit(5)
|
||||
.values_list("group_id", "count")
|
||||
)
|
||||
active_group_list = []
|
||||
id2name = {}
|
||||
if data_list:
|
||||
if info_list := await GroupInfo.filter(group_id__in=[x[0] for x in data_list]).all():
|
||||
for group_info in info_list:
|
||||
id2name[group_info.group_id] = group_info.group_name
|
||||
for data in data_list:
|
||||
active_group_list.append(
|
||||
ActiveGroup(
|
||||
group_id=data[0],
|
||||
name=id2name.get(data[0]) or data[0],
|
||||
chat_num=data[1],
|
||||
ava_img=GROUP_AVA_URL.format(data[0], data[0]),
|
||||
)
|
||||
)
|
||||
active_group_list = sorted(
|
||||
active_group_list, key=lambda x: x.chat_num, reverse=True
|
||||
)
|
||||
if len(active_group_list) > 5:
|
||||
active_group_list = active_group_list[:5]
|
||||
return Result.ok(active_group_list)
|
||||
|
||||
|
||||
@router.get("/get_hot_plugin", dependencies=[authentication()], description="获取热门插件")
|
||||
async def _(date_type: Optional[QueryDateType] = None) -> Result:
|
||||
query = Statistics
|
||||
now = datetime.now()
|
||||
if date_type == QueryDateType.DAY:
|
||||
query = Statistics.filter(create_time__gte=now - timedelta(hours=now.hour))
|
||||
if date_type == QueryDateType.WEEK:
|
||||
query = Statistics.filter(create_time__gte=now - timedelta(days=7))
|
||||
if date_type == QueryDateType.MONTH:
|
||||
query = Statistics.filter(create_time__gte=now - timedelta(days=30))
|
||||
if date_type == QueryDateType.YEAR:
|
||||
query = Statistics.filter(create_time__gte=now - timedelta(days=365))
|
||||
data_list = (
|
||||
await query.annotate(count=Count("id"))
|
||||
.group_by("plugin_name").order_by("-count").limit(5)
|
||||
.values_list("plugin_name", "count")
|
||||
)
|
||||
hot_plugin_list = []
|
||||
for data in data_list:
|
||||
name = data[0]
|
||||
if plugin_data := plugin_data_manager.get(data[0]):
|
||||
name = plugin_data.name
|
||||
hot_plugin_list.append(
|
||||
HotPlugin(
|
||||
module=data[0],
|
||||
name=name,
|
||||
count=data[1],
|
||||
)
|
||||
)
|
||||
hot_plugin_list = sorted(hot_plugin_list, key=lambda x: x.count, reverse=True)
|
||||
if len(hot_plugin_list) > 5:
|
||||
hot_plugin_list = hot_plugin_list[:5]
|
||||
return Result.ok(hot_plugin_list)
|
||||
|
||||
|
||||
@ws_router.websocket("/system_status")
|
||||
async def system_logs_realtime(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
@@ -144,6 +258,6 @@ async def system_logs_realtime(websocket: WebSocket):
|
||||
system_status = await get_system_status()
|
||||
await websocket.send_text(system_status.json())
|
||||
await asyncio.sleep(5)
|
||||
except WebSocketDisconnect:
|
||||
except (WebSocketDisconnect, ConnectionClosedError, ConnectionClosedOK):
|
||||
pass
|
||||
return
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Optional
|
||||
|
||||
import nonebot
|
||||
from nonebot import Driver
|
||||
from nonebot.adapters.onebot.v12 import Bot
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional, Union
|
||||
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.config import Config
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -41,6 +45,8 @@ class BaseInfo(BaseModel):
|
||||
# """一年内累计接受消息"""
|
||||
connect_time: int = 0
|
||||
"""连接时间"""
|
||||
connect_date: Optional[datetime] = None
|
||||
"""连接日期"""
|
||||
|
||||
plugin_count: int = 0
|
||||
"""加载插件数量"""
|
||||
@@ -52,5 +58,53 @@ class BaseInfo(BaseModel):
|
||||
is_select: bool = False
|
||||
"""当前选择"""
|
||||
|
||||
config: Optional[Config] = None
|
||||
"""nb配置"""
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class ChatHistoryCount(BaseModel):
|
||||
"""
|
||||
聊天记录数量
|
||||
"""
|
||||
|
||||
num: int
|
||||
"""总数"""
|
||||
day: int
|
||||
"""一天内"""
|
||||
week: int
|
||||
"""一周内"""
|
||||
month: int
|
||||
"""一月内"""
|
||||
year: int
|
||||
"""一年内"""
|
||||
|
||||
|
||||
class ActiveGroup(BaseModel):
|
||||
"""
|
||||
活跃群聊数据
|
||||
"""
|
||||
|
||||
group_id: Union[str, int]
|
||||
"""群组id"""
|
||||
name: str
|
||||
"""群组名称"""
|
||||
chat_num: int
|
||||
"""发言数量"""
|
||||
ava_img: str
|
||||
"""群组头像"""
|
||||
|
||||
|
||||
class HotPlugin(BaseModel):
|
||||
"""
|
||||
热门插件
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块名"""
|
||||
name: str
|
||||
"""插件名称"""
|
||||
count: int
|
||||
"""调用次数"""
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
from typing import List, Optional
|
||||
|
||||
import cattrs
|
||||
from fastapi import APIRouter
|
||||
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager
|
||||
from utils.manager.models import PluginData, PluginSetting, PluginType
|
||||
|
||||
from ....base_model import Result
|
||||
from ....utils import authentication
|
||||
from .model import PluginCount, PluginInfo, PluginSwitch, UpdateConfig, UpdatePlugin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/get_plugin_list", dependencies=[authentication()], deprecated="获取插件列表")
|
||||
def _(
|
||||
plugin_type: PluginType, menu_type: Optional[str] = None
|
||||
) -> Result:
|
||||
"""
|
||||
获取插件列表
|
||||
:param plugin_type: 类型 normal, superuser, hidden, admin
|
||||
"""
|
||||
try:
|
||||
plugin_list: List[PluginInfo] = []
|
||||
for module in plugin_data_manager.keys():
|
||||
plugin_data: Optional[PluginData] = plugin_data_manager[module]
|
||||
if plugin_data and plugin_data.plugin_type == plugin_type:
|
||||
setting = plugin_data.plugin_setting or PluginSetting()
|
||||
plugin = plugin_data.plugin_status
|
||||
menu_type_ = getattr(setting, "plugin_type", ["无"])[0]
|
||||
if menu_type and menu_type != menu_type_:
|
||||
continue
|
||||
plugin_info = PluginInfo(
|
||||
module=module,
|
||||
plugin_name=plugin_data.name,
|
||||
default_switch=getattr(setting, "default_status", False),
|
||||
limit_superuser=getattr(setting, "limit_superuser", False),
|
||||
cost_gold=getattr(setting, "cost_gold", 0),
|
||||
menu_type=menu_type_,
|
||||
version=(plugin.version or 0) if plugin else 0,
|
||||
level=getattr(setting, "level", 5),
|
||||
status=plugin.status if plugin else False,
|
||||
author=plugin.author if plugin else None
|
||||
)
|
||||
plugin_info.version = (plugin.version or 0) if plugin else 0
|
||||
plugin_list.append(plugin_info)
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/get_plugins", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(plugin_list, "拿到了新鲜出炉的数据!")
|
||||
|
||||
@router.get("/get_plugin_count", dependencies=[authentication()], deprecated="获取插件数量")
|
||||
def _() -> Result:
|
||||
plugin_count = PluginCount()
|
||||
for module in plugin_data_manager.keys():
|
||||
plugin_data: Optional[PluginData] = plugin_data_manager[module]
|
||||
if plugin_data and plugin_data.plugin_type == PluginType.NORMAL:
|
||||
plugin_count.normal += 1
|
||||
elif plugin_data and plugin_data.plugin_type == PluginType.ADMIN:
|
||||
plugin_count.admin += 1
|
||||
elif plugin_data and plugin_data.plugin_type == PluginType.SUPERUSER:
|
||||
plugin_count.superuser += 1
|
||||
else:
|
||||
plugin_count.other += 1
|
||||
return Result.ok(plugin_count)
|
||||
|
||||
@router.post("/update_plugins", dependencies=[authentication()], description="更新插件参数")
|
||||
def _(plugin: UpdatePlugin) -> Result:
|
||||
"""
|
||||
修改插件信息
|
||||
:param plugin: 插件内容
|
||||
"""
|
||||
try:
|
||||
module = plugin.module
|
||||
if p2s := plugins2settings_manager.get(module):
|
||||
p2s.default_status = plugin.default_status
|
||||
p2s.limit_superuser = plugin.limit_superuser
|
||||
p2s.cost_gold = plugin.cost_gold
|
||||
p2s.cmd = plugin.cmd.split(",") if plugin.cmd else []
|
||||
p2s.level = plugin.level
|
||||
if pd := plugin_data_manager.get(module):
|
||||
menu_lin = None
|
||||
if len(pd.menu_type) > 1:
|
||||
menu_lin = pd.menu_type[1]
|
||||
if menu_lin is not None:
|
||||
pd.menu_type = (plugin.menu_type, menu_lin)
|
||||
else:
|
||||
pd.menu_type = (plugin.menu_type,)
|
||||
if pm := plugins_manager.get(module):
|
||||
if plugin.block_type:
|
||||
pm.block_type = plugin.block_type
|
||||
pm.status = False
|
||||
else:
|
||||
pm.block_type = None
|
||||
pm.status = True
|
||||
plugins2settings_manager.save()
|
||||
plugins_manager.save()
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/update_plugins", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
return Result.ok(info="已经帮你写好啦!")
|
||||
|
||||
|
||||
@router.post("/update_config", dependencies=[authentication()], description="更新配置")
|
||||
def _(config_list: List[UpdateConfig]) -> Result:
|
||||
try:
|
||||
for config in config_list:
|
||||
if cg := Config.get(config.module):
|
||||
if c := cg.configs.get(config.key):
|
||||
if isinstance(c.value, (list, tuple)) or isinstance(
|
||||
c.default_value, (list, tuple)
|
||||
):
|
||||
value = config.value.split(",")
|
||||
else:
|
||||
value = config.value
|
||||
if c.type and value is not None:
|
||||
value = cattrs.structure(value, c.type)
|
||||
Config.set_config(config.module, config.key, value)
|
||||
except Exception as e:
|
||||
logger.error("调用API错误", "/update_config", e=e)
|
||||
return Result.fail(f"{type(e)}: {e}")
|
||||
Config.save(save_simple_data=True)
|
||||
return Result.ok(info="写入配置项了哦!")
|
||||
|
||||
|
||||
@router.post("/change_switch", dependencies=[authentication()], description="开关插件")
|
||||
def _(param: PluginSwitch) -> Result:
|
||||
if pm := plugins_manager.get(param.module):
|
||||
pm.block_type = None if param.status else 'all'
|
||||
pm.status = param.status
|
||||
plugins_manager.save()
|
||||
return Result.ok(info="成功改变了开关状态!")
|
||||
return Result.warning_("未获取该插件的配置!")
|
||||
|
||||
|
||||
@router.get("/get_plugin_menu_type", dependencies=[authentication()], description="获取插件类型")
|
||||
def _() -> Result:
|
||||
menu_type_list = []
|
||||
for module in plugin_data_manager.keys():
|
||||
plugin_data: Optional[PluginData] = plugin_data_manager[module]
|
||||
if plugin_data:
|
||||
setting = plugin_data.plugin_setting or PluginSetting()
|
||||
menu_type = getattr(setting, "plugin_type", ["无"])[0]
|
||||
if menu_type not in menu_type_list:
|
||||
menu_type_list.append(menu_type)
|
||||
return Result.ok(menu_type_list)
|
||||
@@ -0,0 +1,135 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from utils.manager.models import Plugin as PluginManager
|
||||
from utils.manager.models import (
|
||||
PluginBlock,
|
||||
PluginCd,
|
||||
PluginCount,
|
||||
PluginSetting,
|
||||
PluginType,
|
||||
)
|
||||
from utils.typing import BLOCK_TYPE
|
||||
|
||||
|
||||
class PluginSwitch(BaseModel):
|
||||
"""
|
||||
插件开关
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块"""
|
||||
status: bool
|
||||
"""开关状态"""
|
||||
|
||||
|
||||
class UpdateConfig(BaseModel):
|
||||
"""
|
||||
配置项修改参数
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块"""
|
||||
key: str
|
||||
"""配置项key"""
|
||||
value: Any
|
||||
"""配置项值"""
|
||||
|
||||
|
||||
class UpdatePlugin(BaseModel):
|
||||
"""
|
||||
插件修改参数
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块"""
|
||||
default_status: bool
|
||||
"""默认开关"""
|
||||
limit_superuser: bool
|
||||
"""限制超级用户"""
|
||||
cost_gold: int
|
||||
"""金币花费"""
|
||||
cmd: str
|
||||
"""插件别名"""
|
||||
menu_type: str
|
||||
"""插件菜单类型"""
|
||||
level: int
|
||||
"""插件所需群权限"""
|
||||
block_type: BLOCK_TYPE
|
||||
"""禁用类型"""
|
||||
|
||||
|
||||
class PluginInfo(BaseModel):
|
||||
"""
|
||||
基本插件信息
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""插件名称"""
|
||||
plugin_name: str
|
||||
"""插件中文名称"""
|
||||
default_switch: bool
|
||||
"""默认开关"""
|
||||
limit_superuser: bool
|
||||
"""限制超级用户"""
|
||||
cost_gold: int
|
||||
"""花费金币"""
|
||||
menu_type: str
|
||||
"""插件菜单类型"""
|
||||
version: Union[int, str, float]
|
||||
"""插件版本"""
|
||||
level: int
|
||||
"""群权限"""
|
||||
status: bool
|
||||
"""当前状态"""
|
||||
author: Optional[str] = None
|
||||
"""作者"""
|
||||
|
||||
|
||||
class PluginConfig(BaseModel):
|
||||
"""
|
||||
插件配置项
|
||||
"""
|
||||
|
||||
module: str
|
||||
key: str
|
||||
value: Any
|
||||
help: Optional[str]
|
||||
default_value: Any
|
||||
has_type: bool
|
||||
|
||||
|
||||
class Plugin(BaseModel):
|
||||
"""
|
||||
插件
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块名称"""
|
||||
plugin_settings: Optional[PluginSetting]
|
||||
"""settings"""
|
||||
plugin_manager: Optional[PluginManager]
|
||||
"""manager"""
|
||||
plugin_config: Optional[Dict[str, PluginConfig]]
|
||||
"""配置项"""
|
||||
cd_limit: Optional[PluginCd]
|
||||
"""cd限制"""
|
||||
block_limit: Optional[PluginBlock]
|
||||
"""阻断限制"""
|
||||
count_limit: Optional[PluginCount]
|
||||
"""次数限制"""
|
||||
|
||||
class PluginCount(BaseModel):
|
||||
"""
|
||||
插件数量
|
||||
"""
|
||||
|
||||
normal: int = 0
|
||||
"""普通插件"""
|
||||
admin: int = 0
|
||||
"""管理员插件"""
|
||||
superuser: int = 0
|
||||
"""超级用户插件"""
|
||||
other: int = 0
|
||||
"""其他插件"""
|
||||
Reference in New Issue
Block a user